mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-07-11 04:17:58 +00:00
Add support for poll result messages
This commit is contained in:
parent
ff2ad34be5
commit
afbbc07e98
@ -266,6 +266,7 @@ class MessageType(Enum):
|
|||||||
guild_incident_report_raid = 38
|
guild_incident_report_raid = 38
|
||||||
guild_incident_report_false_alarm = 39
|
guild_incident_report_false_alarm = 39
|
||||||
purchase_notification = 44
|
purchase_notification = 44
|
||||||
|
poll_result = 46
|
||||||
|
|
||||||
|
|
||||||
class SpeakingState(Enum):
|
class SpeakingState(Enum):
|
||||||
|
@ -2268,6 +2268,13 @@ class Message(PartialMessage, Hashable):
|
|||||||
# the channel will be the correct type here
|
# the channel will be the correct type here
|
||||||
ref.resolved = self.__class__(channel=chan, data=resolved, state=state) # type: ignore
|
ref.resolved = self.__class__(channel=chan, data=resolved, state=state) # type: ignore
|
||||||
|
|
||||||
|
if self.type is MessageType.poll_result:
|
||||||
|
if isinstance(self.reference.resolved, self.__class__):
|
||||||
|
self._state._update_poll_results(self, self.reference.resolved)
|
||||||
|
else:
|
||||||
|
if self.reference.message_id:
|
||||||
|
self._state._update_poll_results(self, self.reference.message_id)
|
||||||
|
|
||||||
self.application: Optional[MessageApplication] = None
|
self.application: Optional[MessageApplication] = None
|
||||||
try:
|
try:
|
||||||
application = data['application']
|
application = data['application']
|
||||||
@ -2634,6 +2641,7 @@ class Message(PartialMessage, Hashable):
|
|||||||
MessageType.chat_input_command,
|
MessageType.chat_input_command,
|
||||||
MessageType.context_menu_command,
|
MessageType.context_menu_command,
|
||||||
MessageType.thread_starter_message,
|
MessageType.thread_starter_message,
|
||||||
|
MessageType.poll_result,
|
||||||
)
|
)
|
||||||
|
|
||||||
@utils.cached_slot_property('_cs_system_content')
|
@utils.cached_slot_property('_cs_system_content')
|
||||||
@ -2810,6 +2818,14 @@ class Message(PartialMessage, Hashable):
|
|||||||
if guild_product_purchase is not None:
|
if guild_product_purchase is not None:
|
||||||
return f'{self.author.name} has purchased {guild_product_purchase.product_name}!'
|
return f'{self.author.name} has purchased {guild_product_purchase.product_name}!'
|
||||||
|
|
||||||
|
if self.type is MessageType.poll_result:
|
||||||
|
embed = self.embeds[0] # Will always have 1 embed
|
||||||
|
poll_title = utils.get(
|
||||||
|
embed.fields,
|
||||||
|
name='poll_question_text',
|
||||||
|
)
|
||||||
|
return f'{self.author.display_name}\'s poll {poll_title.value} has closed.' # type: ignore
|
||||||
|
|
||||||
# Fallback for unknown message types
|
# Fallback for unknown message types
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ from typing import Optional, List, TYPE_CHECKING, Union, AsyncIterator, Dict
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from .enums import PollLayoutType, try_enum
|
from .enums import PollLayoutType, try_enum, MessageType
|
||||||
from . import utils
|
from . import utils
|
||||||
from .emoji import PartialEmoji, Emoji
|
from .emoji import PartialEmoji, Emoji
|
||||||
from .user import User
|
from .user import User
|
||||||
@ -125,7 +125,16 @@ class PollAnswer:
|
|||||||
Whether the current user has voted to this answer or not.
|
Whether the current user has voted to this answer or not.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('media', 'id', '_state', '_message', '_vote_count', 'self_voted', '_poll')
|
__slots__ = (
|
||||||
|
'media',
|
||||||
|
'id',
|
||||||
|
'_state',
|
||||||
|
'_message',
|
||||||
|
'_vote_count',
|
||||||
|
'self_voted',
|
||||||
|
'_poll',
|
||||||
|
'_victor',
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -141,6 +150,7 @@ class PollAnswer:
|
|||||||
self._vote_count: int = 0
|
self._vote_count: int = 0
|
||||||
self.self_voted: bool = False
|
self.self_voted: bool = False
|
||||||
self._poll: Poll = poll
|
self._poll: Poll = poll
|
||||||
|
self._victor: bool = False
|
||||||
|
|
||||||
def _handle_vote_event(self, added: bool, self_voted: bool) -> None:
|
def _handle_vote_event(self, added: bool, self_voted: bool) -> None:
|
||||||
if added:
|
if added:
|
||||||
@ -210,6 +220,19 @@ class PollAnswer:
|
|||||||
'poll_media': self.media.to_dict(),
|
'poll_media': self.media.to_dict(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def victor(self) -> bool:
|
||||||
|
""":class:`bool`: Whether the answer is the one that had the most
|
||||||
|
votes when the poll ended.
|
||||||
|
|
||||||
|
.. versionadded:: 2.5
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If the poll has not ended, this will always return ``False``.
|
||||||
|
"""
|
||||||
|
return self._victor
|
||||||
|
|
||||||
async def voters(
|
async def voters(
|
||||||
self, *, limit: Optional[int] = None, after: Optional[Snowflake] = None
|
self, *, limit: Optional[int] = None, after: Optional[Snowflake] = None
|
||||||
) -> AsyncIterator[Union[User, Member]]:
|
) -> AsyncIterator[Union[User, Member]]:
|
||||||
@ -325,6 +348,8 @@ class Poll:
|
|||||||
'_expiry',
|
'_expiry',
|
||||||
'_finalized',
|
'_finalized',
|
||||||
'_state',
|
'_state',
|
||||||
|
'_total_votes',
|
||||||
|
'_victor_answer_id',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -348,6 +373,8 @@ class Poll:
|
|||||||
self._state: Optional[ConnectionState] = None
|
self._state: Optional[ConnectionState] = None
|
||||||
self._finalized: bool = False
|
self._finalized: bool = False
|
||||||
self._expiry: Optional[datetime.datetime] = None
|
self._expiry: Optional[datetime.datetime] = None
|
||||||
|
self._total_votes: Optional[int] = None
|
||||||
|
self._victor_answer_id: Optional[int] = None
|
||||||
|
|
||||||
def _update(self, message: Message) -> None:
|
def _update(self, message: Message) -> None:
|
||||||
self._state = message._state
|
self._state = message._state
|
||||||
@ -360,6 +387,33 @@ class Poll:
|
|||||||
self._expiry = message.poll.expires_at
|
self._expiry = message.poll.expires_at
|
||||||
self._finalized = message.poll._finalized
|
self._finalized = message.poll._finalized
|
||||||
self._answers = message.poll._answers
|
self._answers = message.poll._answers
|
||||||
|
self._update_results_from_message(message)
|
||||||
|
|
||||||
|
def _update_results_from_message(self, message: Message) -> None:
|
||||||
|
if message.type != MessageType.poll_result or not message.embeds:
|
||||||
|
return
|
||||||
|
|
||||||
|
result_embed = message.embeds[0] # Will always have 1 embed
|
||||||
|
fields: Dict[str, str] = {field.name: field.value for field in result_embed.fields} # type: ignore
|
||||||
|
|
||||||
|
total_votes = fields.get('total_votes')
|
||||||
|
|
||||||
|
if total_votes is not None:
|
||||||
|
self._total_votes = int(total_votes)
|
||||||
|
|
||||||
|
victor_answer = fields.get('victor_answer_id')
|
||||||
|
|
||||||
|
if victor_answer is None:
|
||||||
|
return # Can't do anything else without the victor answer
|
||||||
|
|
||||||
|
self._victor_answer_id = int(victor_answer)
|
||||||
|
|
||||||
|
victor_answer_votes = fields['victor_answer_votes']
|
||||||
|
|
||||||
|
answer = self._answers[self._victor_answer_id]
|
||||||
|
answer._victor = True
|
||||||
|
answer._vote_count = int(victor_answer_votes)
|
||||||
|
self._answers[answer.id] = answer # Ensure update
|
||||||
|
|
||||||
def _update_results(self, data: PollResultPayload) -> None:
|
def _update_results(self, data: PollResultPayload) -> None:
|
||||||
self._finalized = data['is_finalized']
|
self._finalized = data['is_finalized']
|
||||||
@ -432,6 +486,32 @@ class Poll:
|
|||||||
"""List[:class:`PollAnswer`]: Returns a read-only copy of the answers."""
|
"""List[:class:`PollAnswer`]: Returns a read-only copy of the answers."""
|
||||||
return list(self._answers.values())
|
return list(self._answers.values())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def victor_answer_id(self) -> Optional[int]:
|
||||||
|
"""Optional[:class:`int`]: The victor answer ID.
|
||||||
|
|
||||||
|
.. versionadded:: 2.5
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This will **always** be ``None`` for polls that have not yet finished.
|
||||||
|
"""
|
||||||
|
return self._victor_answer_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def victor_answer(self) -> Optional[PollAnswer]:
|
||||||
|
"""Optional[:class:`PollAnswer`]: The victor answer.
|
||||||
|
|
||||||
|
.. versionadded:: 2.5
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This will **always** be ``None`` for polls that have not yet finished.
|
||||||
|
"""
|
||||||
|
if self.victor_answer_id is None:
|
||||||
|
return None
|
||||||
|
return self.get_answer(self.victor_answer_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def expires_at(self) -> Optional[datetime.datetime]:
|
def expires_at(self) -> Optional[datetime.datetime]:
|
||||||
"""Optional[:class:`datetime.datetime`]: A datetime object representing the poll expiry.
|
"""Optional[:class:`datetime.datetime`]: A datetime object representing the poll expiry.
|
||||||
@ -457,12 +537,20 @@ class Poll:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def message(self) -> Optional[Message]:
|
def message(self) -> Optional[Message]:
|
||||||
""":class:`Message`: The message this poll is from."""
|
"""Optional[:class:`Message`]: The message this poll is from."""
|
||||||
return self._message
|
return self._message
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total_votes(self) -> int:
|
def total_votes(self) -> int:
|
||||||
""":class:`int`: Returns the sum of all the answer votes."""
|
""":class:`int`: Returns the sum of all the answer votes.
|
||||||
|
|
||||||
|
If the poll has not yet finished, this is an approximate vote count.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.5
|
||||||
|
This now returns an exact vote count when updated from its poll results message.
|
||||||
|
"""
|
||||||
|
if self._total_votes is not None:
|
||||||
|
return self._total_votes
|
||||||
return sum([answer.vote_count for answer in self.answers])
|
return sum([answer.vote_count for answer in self.answers])
|
||||||
|
|
||||||
def is_finalised(self) -> bool:
|
def is_finalised(self) -> bool:
|
||||||
|
@ -552,6 +552,27 @@ class ConnectionState(Generic[ClientT]):
|
|||||||
poll._handle_vote(answer_id, added, self_voted)
|
poll._handle_vote(answer_id, added, self_voted)
|
||||||
return poll
|
return poll
|
||||||
|
|
||||||
|
def _update_poll_results(self, from_: Message, to: Union[Message, int]) -> None:
|
||||||
|
if isinstance(to, Message):
|
||||||
|
cached = self._get_message(to.id)
|
||||||
|
elif isinstance(to, int):
|
||||||
|
cached = self._get_message(to)
|
||||||
|
|
||||||
|
if cached is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
to = cached
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
if to.poll is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
to.poll._update_results_from_message(from_)
|
||||||
|
|
||||||
|
if cached is not None and cached.poll:
|
||||||
|
cached.poll._update_results_from_message(from_)
|
||||||
|
|
||||||
async def chunker(
|
async def chunker(
|
||||||
self, guild_id: int, query: str = '', limit: int = 0, presences: bool = False, *, nonce: Optional[str] = None
|
self, guild_id: int, query: str = '', limit: int = 0, presences: bool = False, *, nonce: Optional[str] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -71,7 +71,7 @@ class EmbedAuthor(TypedDict, total=False):
|
|||||||
proxy_icon_url: str
|
proxy_icon_url: str
|
||||||
|
|
||||||
|
|
||||||
EmbedType = Literal['rich', 'image', 'video', 'gifv', 'article', 'link']
|
EmbedType = Literal['rich', 'image', 'video', 'gifv', 'article', 'link', 'poll_result']
|
||||||
|
|
||||||
|
|
||||||
class Embed(TypedDict, total=False):
|
class Embed(TypedDict, total=False):
|
||||||
|
@ -174,6 +174,7 @@ MessageType = Literal[
|
|||||||
38,
|
38,
|
||||||
39,
|
39,
|
||||||
44,
|
44,
|
||||||
|
46,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -1887,6 +1887,10 @@ of :class:`enum.Enum`.
|
|||||||
|
|
||||||
.. versionadded:: 2.5
|
.. versionadded:: 2.5
|
||||||
|
|
||||||
|
.. attribute:: poll_result
|
||||||
|
|
||||||
|
The system message sent when a poll has closed.
|
||||||
|
|
||||||
.. class:: UserFlags
|
.. class:: UserFlags
|
||||||
|
|
||||||
Represents Discord User flags.
|
Represents Discord User flags.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user