22import time
33import os
44from typing import Optional , List
5- from datetime import datetime
5+ from datetime import datetime , timedelta
6+ import logging
67
78import discord
89from discord .ext import commands
910from discord import ButtonStyle
1011
1112from draft_database import DraftDatabase
1213
14+ # Set up logger
15+ logger = logging .getLogger (__name__ )
16+
1317
1418def log_vote (message : str ) -> None :
1519 """Log vote events to a file."""
@@ -21,9 +25,28 @@ def log_vote(message: str) -> None:
2125 file .write (f"[{ timestamp } ] { message } \n " )
2226
2327
24- class VoteView (discord .ui .View ):
25- """A view containing the voting buttons and result handling."""
28+ class ReviewModal (discord .Modal ):
29+ def __init__ (self , is_approval : bool ) -> None :
30+ title = "Draft Review" if is_approval else "Draft Rejection"
31+ super ().__init__ (title = title )
32+
33+ self .input = discord .TextInput (
34+ label = "Categories" if is_approval else "Rejection Reason" ,
35+ placeholder = ("Enter categories separated by commas" if is_approval
36+ else "Enter reason for rejection" ),
37+ style = discord .TextStyle .paragraph ,
38+ required = True ,
39+ row = 0
40+ )
41+ self .add_item (self .input )
42+ self .result = None
2643
44+ async def callback (self , interaction : discord .Interaction ):
45+ self .result = self .input .value
46+ await interaction .response .defer ()
47+
48+
49+ class VoteView (discord .ui .View ):
2750 def __init__ (self ,
2851 author : str ,
2952 draft_name : str ,
@@ -33,15 +56,33 @@ def __init__(self,
3356 self .author = author
3457 self .draft_name = draft_name
3558 self .required_votes = required_votes
36- self .approve_votes : List [int ] = [] # Store user IDs
59+ self .approve_votes : List [int ] = []
3760 self .reject_votes : List [int ] = []
38- self .result : Optional [bool ] = None # True for approve, False for reject
61+ self .result : Optional [bool ] = None
62+ self .end_time = int ((datetime .now () + timedelta (seconds = timeout )).timestamp ())
63+ self .message = None
3964
40- @discord .ui .button (label = "Approve (0)" , style = ButtonStyle .green , emoji = "✅" )
65+ def _create_status_embed (self ) -> discord .Embed :
66+ """Create an embed showing the current voting status."""
67+ embed = discord .Embed (
68+ title = f"Vote: { self .draft_name } " ,
69+ description = (
70+ f"Draft by { self .author } \n \n "
71+ f"Required votes: { self .required_votes } \n "
72+ f"Ends: <t:{ self .end_time } :R>\n \n "
73+ f"Current status:\n "
74+ f"✅ Approve: { len (self .approve_votes )} \n "
75+ f"❌ Reject: { len (self .reject_votes )} "
76+ ),
77+ color = discord .Color .blue ()
78+ )
79+ return embed
80+
81+ @discord .ui .button (label = "Approve (0)" , style = discord .ButtonStyle .green , emoji = "✅" )
4182 async def approve (self , button : discord .ui .Button , interaction : discord .Interaction ):
4283 await self ._handle_vote (interaction , True )
4384
44- @discord .ui .button (label = "Reject (0)" , style = ButtonStyle .red , emoji = "❌" )
85+ @discord .ui .button (label = "Reject (0)" , style = discord . ButtonStyle .red , emoji = "❌" )
4586 async def reject (self , button : discord .ui .Button , interaction : discord .Interaction ):
4687 await self ._handle_vote (interaction , False )
4788
@@ -77,46 +118,18 @@ async def _handle_vote(self, interaction: discord.Interaction, is_approve: bool)
77118 embed = self ._create_status_embed ()
78119 await interaction .response .edit_message (embed = embed , view = self )
79120
80- def _create_status_embed (self ) -> discord .Embed :
81- """Create an embed showing the current voting status."""
82- embed = discord .Embed (
83- title = f"Vote: { self .draft_name } " ,
84- description = f"Draft by { self .author } \n \n "
85- f"Required votes: { self .required_votes } \n "
86- f"Current status:\n "
87- f"✅ Approve: { len (self .approve_votes )} \n "
88- f"❌ Reject: { len (self .reject_votes )} " ,
89- color = discord .Color .blue ()
90- )
91- return embed
92-
93-
94- class ReviewModal (discord .ui .Modal ):
95- """Modal for entering review comments."""
96-
97- def __init__ (self , is_approval : bool ) -> None :
98- title = "Draft Review" if is_approval else "Draft Rejection"
99- super ().__init__ (title = title )
100-
101- self .input = discord .ui .TextInput (
102- label = "Categories" if is_approval else "Rejection Reason" ,
103- placeholder = ("Enter categories separated by commas" if is_approval
104- else "Enter reason for rejection" ),
105- style = discord .TextStyle .paragraph ,
106- required = True
107- )
108- self .add_item (self .input )
109- self .result = None
110-
111- async def callback (self , interaction : discord .Interaction ):
112- self .result = self .input .value
113- await interaction .response .defer ()
121+ async def on_timeout (self ) -> None :
122+ """Handle view timeout"""
123+ for item in self .children :
124+ item .disabled = True
125+ if self .message :
126+ await self .message .edit (view = self )
114127
115128
116129class DraftVote (commands .Cog ):
117130 def __init__ (self , bot : commands .Bot ):
118131 self .bot = bot
119- db_path = os .getenv ('DATABASE_PATH ' , 'drafts.db' )
132+ db_path = os .getenv ('DRAFT_DB_PATH ' , 'drafts.db' )
120133 self .db = DraftDatabase (db_path )
121134
122135 @discord .slash_command (
@@ -147,12 +160,13 @@ async def vote(
147160 Duration of vote in hours (default: 24)
148161 """
149162 await ctx .defer ()
163+
150164 try :
151165 # Verify draft exists
152166 draft_title = f"User:{ author } /Drafts/{ draft_name } "
153167 draft = self .db .get_draft (draft_title )
154168 if not draft :
155- await ctx .respond (
169+ await ctx .followup . send (
156170 f"Error: Draft '{ draft_name } ' by { author } not found." ,
157171 ephemeral = True
158172 )
@@ -168,44 +182,49 @@ async def vote(
168182
169183 embed = view ._create_status_embed ()
170184 message = await ctx .followup .send (embed = embed , view = view )
185+ view .message = message
171186
172187 log_vote (f"Vote started for { draft_name } by { author } " )
173188
174- # Wait for voting to complete
175189 try :
176190 await view .wait ()
191+
192+ if view .result is None : # Timeout
193+ await ctx .followup .send ("Vote has timed out." )
194+ return
195+
196+ # Handle vote result
197+ modal = ReviewModal (is_approval = view .result )
198+ await ctx .interaction .response .send_modal (modal )
199+ await modal .wait ()
200+
201+ if view .result : # Approved
202+ await self .bot .get_cog ('DraftBot' ).approve (author , draft_name , modal .result )
203+ result_embed = discord .Embed (
204+ title = "Draft Approved" ,
205+ description = f"{ draft_name } by { author } has been approved." ,
206+ color = discord .Color .green ()
207+ )
208+ else : # Rejected
209+ await self .bot .get_cog ('DraftBot' ).reject (author , draft_name , modal .result )
210+ result_embed = discord .Embed (
211+ title = "Draft Rejected" ,
212+ description = f"{ draft_name } by { author } has been rejected." ,
213+ color = discord .Color .red ()
214+ )
215+
216+ await ctx .followup .send (embed = result_embed )
217+ log_vote (f"Vote completed for { draft_name } by { author } : { 'Approved' if view .result else 'Rejected' } " )
218+
177219 except asyncio .TimeoutError :
178220 log_vote (f"Vote timed out for { draft_name } by { author } " )
179221 await message .edit (content = "Vote has timed out." , view = None )
180222 return
181223
182- # Handle vote result
183- modal = ReviewModal (is_approval = view .result )
184- await ctx .interaction .response .send_modal (modal )
185- await modal .wait ()
186-
187- if view .result : # Approved
188- await self .bot .get_cog ('DraftBot' ).approve (author , draft_name , modal .result )
189- result_embed = discord .Embed (
190- title = "Draft Approved" ,
191- description = f"{ draft_name } by { author } has been approved." ,
192- color = discord .Color .green ()
193- )
194- else : # Rejected
195- await self .bot .get_cog ('DraftBot' ).reject (author , draft_name , modal .result )
196- result_embed = discord .Embed (
197- title = "Draft Rejected" ,
198- description = f"{ draft_name } by { author } has been rejected." ,
199- color = discord .Color .red ()
200- )
201-
202- await ctx .send (embed = result_embed )
203- log_vote (f"Vote completed for { draft_name } by { author } : { 'Approved' if view .result else 'Rejected' } " )
204-
205224 except Exception as e :
206- await ctx .followup .send (f"An error occurred: { str (e )} " , ephemeral = True )
207225 logger .error (f"Error in vote command: { str (e )} " , exc_info = True )
226+ await ctx .followup .send (f"An error occurred: { str (e )} " , ephemeral = True )
227+
208228
209- def setup (bot ):
210- print ("Setting up DraftVote cog" ) # Debug print
211- bot .add_cog (DraftVote (bot ))
229+ def setup (bot : commands .Bot ):
230+ bot .add_cog (DraftVote (bot ))
0 commit comments