2929
3030import datetime
3131
32- from .enums import PollLayoutType , try_enum
32+ from .enums import PollLayoutType , try_enum , MessageType
3333from . import utils
3434from .emoji import PartialEmoji , Emoji
3535from .user import User
@@ -125,7 +125,16 @@ class PollAnswer:
125125 Whether the current user has voted to this answer or not.
126126 """
127127
128- __slots__ = ('media' , 'id' , '_state' , '_message' , '_vote_count' , 'self_voted' , '_poll' )
128+ __slots__ = (
129+ 'media' ,
130+ 'id' ,
131+ '_state' ,
132+ '_message' ,
133+ '_vote_count' ,
134+ 'self_voted' ,
135+ '_poll' ,
136+ '_victor' ,
137+ )
129138
130139 def __init__ (
131140 self ,
@@ -141,6 +150,7 @@ def __init__(
141150 self ._vote_count : int = 0
142151 self .self_voted : bool = False
143152 self ._poll : Poll = poll
153+ self ._victor : bool = False
144154
145155 def _handle_vote_event (self , added : bool , self_voted : bool ) -> None :
146156 if added :
@@ -210,6 +220,19 @@ def _to_dict(self) -> PollAnswerPayload:
210220 'poll_media' : self .media .to_dict (),
211221 }
212222
223+ @property
224+ def victor (self ) -> bool :
225+ """:class:`bool`: Whether the answer is the one that had the most
226+ votes when the poll ended.
227+
228+ .. versionadded:: 2.5
229+
230+ .. note::
231+
232+ If the poll has not ended, this will always return ``False``.
233+ """
234+ return self ._victor
235+
213236 async def voters (
214237 self , * , limit : Optional [int ] = None , after : Optional [Snowflake ] = None
215238 ) -> AsyncIterator [Union [User , Member ]]:
@@ -325,6 +348,8 @@ class Poll:
325348 '_expiry' ,
326349 '_finalized' ,
327350 '_state' ,
351+ '_total_votes' ,
352+ '_victor_answer_id' ,
328353 )
329354
330355 def __init__ (
@@ -348,6 +373,8 @@ def __init__(
348373 self ._state : Optional [ConnectionState ] = None
349374 self ._finalized : bool = False
350375 self ._expiry : Optional [datetime .datetime ] = None
376+ self ._total_votes : Optional [int ] = None
377+ self ._victor_answer_id : Optional [int ] = None
351378
352379 def _update (self , message : Message ) -> None :
353380 self ._state = message ._state
@@ -360,6 +387,33 @@ def _update(self, message: Message) -> None:
360387 self ._expiry = message .poll .expires_at
361388 self ._finalized = message .poll ._finalized
362389 self ._answers = message .poll ._answers
390+ self ._update_results_from_message (message )
391+
392+ def _update_results_from_message (self , message : Message ) -> None :
393+ if message .type != MessageType .poll_result or not message .embeds :
394+ return
395+
396+ result_embed = message .embeds [0 ] # Will always have 1 embed
397+ fields : Dict [str , str ] = {field .name : field .value for field in result_embed .fields } # type: ignore
398+
399+ total_votes = fields .get ('total_votes' )
400+
401+ if total_votes is not None :
402+ self ._total_votes = int (total_votes )
403+
404+ victor_answer = fields .get ('victor_answer_id' )
405+
406+ if victor_answer is None :
407+ return # Can't do anything else without the victor answer
408+
409+ self ._victor_answer_id = int (victor_answer )
410+
411+ victor_answer_votes = fields ['victor_answer_votes' ]
412+
413+ answer = self ._answers [self ._victor_answer_id ]
414+ answer ._victor = True
415+ answer ._vote_count = int (victor_answer_votes )
416+ self ._answers [answer .id ] = answer # Ensure update
363417
364418 def _update_results (self , data : PollResultPayload ) -> None :
365419 self ._finalized = data ['is_finalized' ]
@@ -432,6 +486,32 @@ def answers(self) -> List[PollAnswer]:
432486 """List[:class:`PollAnswer`]: Returns a read-only copy of the answers."""
433487 return list (self ._answers .values ())
434488
489+ @property
490+ def victor_answer_id (self ) -> Optional [int ]:
491+ """Optional[:class:`int`]: The victor answer ID.
492+
493+ .. versionadded:: 2.5
494+
495+ .. note::
496+
497+ This will **always** be ``None`` for polls that have not yet finished.
498+ """
499+ return self ._victor_answer_id
500+
501+ @property
502+ def victor_answer (self ) -> Optional [PollAnswer ]:
503+ """Optional[:class:`PollAnswer`]: The victor answer.
504+
505+ .. versionadded:: 2.5
506+
507+ .. note::
508+
509+ This will **always** be ``None`` for polls that have not yet finished.
510+ """
511+ if self .victor_answer_id is None :
512+ return None
513+ return self .get_answer (self .victor_answer_id )
514+
435515 @property
436516 def expires_at (self ) -> Optional [datetime .datetime ]:
437517 """Optional[:class:`datetime.datetime`]: A datetime object representing the poll expiry.
@@ -457,12 +537,20 @@ def created_at(self) -> Optional[datetime.datetime]:
457537
458538 @property
459539 def message (self ) -> Optional [Message ]:
460- """:class:`Message`: The message this poll is from."""
540+ """Optional[ :class:`Message`] : The message this poll is from."""
461541 return self ._message
462542
463543 @property
464544 def total_votes (self ) -> int :
465- """:class:`int`: Returns the sum of all the answer votes."""
545+ """:class:`int`: Returns the sum of all the answer votes.
546+
547+ If the poll has not yet finished, this is an approximate vote count.
548+
549+ .. versionchanged:: 2.5
550+ This now returns an exact vote count when updated from its poll results message.
551+ """
552+ if self ._total_votes is not None :
553+ return self ._total_votes
466554 return sum ([answer .vote_count for answer in self .answers ])
467555
468556 def is_finalised (self ) -> bool :
0 commit comments