Skip to content

Commit

Permalink
feat: Polls (Pycord-Development#2408)
Browse files Browse the repository at this point in the history
Signed-off-by: UK <41271523+NeloBlivion@users.noreply.github.com>
Signed-off-by: Lala Sabathil <aiko@aitsys.dev>
Signed-off-by: Dorukyum <53639936+Dorukyum@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: plun1331 <plun1331@gmail.com>
Co-authored-by: JustaSqu1d <89910983+JustaSqu1d@users.noreply.github.com>
Co-authored-by: Dorukyum <53639936+Dorukyum@users.noreply.github.com>
Co-authored-by: Lala Sabathil <aiko@aitsys.dev>
  • Loading branch information
6 people authored Jun 15, 2024
1 parent 58a329f commit cd9ca50
Show file tree
Hide file tree
Showing 21 changed files with 1,136 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ These changes are available on the `master` branch, but have not yet been releas
([#2421](https://github.com/Pycord-Development/pycord/pull/2421))
- Added `member` data to the `raw_reaction_remove` event.
([#2412](https://github.com/Pycord-Development/pycord/pull/2412))
- Added `Poll` and all related features.
([#2408](https://github.com/Pycord-Development/pycord/pull/2408))
- Added `stacklevel` param to `utils.warn_deprecated` and `utils.deprecated`.
([#2450](https://github.com/Pycord-Development/pycord/pull/2450))

Expand Down
1 change: 1 addition & 0 deletions discord/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
from .partial_emoji import *
from .permissions import *
from .player import *
from .poll import *
from .raw_models import *
from .reaction import *
from .role import *
Expand Down
16 changes: 16 additions & 0 deletions discord/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
from .guild import Guild
from .member import Member
from .message import Message, MessageReference, PartialMessage
from .poll import Poll
from .state import ConnectionState
from .threads import Thread
from .types.channel import Channel as ChannelPayload
Expand Down Expand Up @@ -1351,6 +1352,7 @@ async def send(
reference: Message | MessageReference | PartialMessage = ...,
mention_author: bool = ...,
view: View = ...,
poll: Poll = ...,
suppress: bool = ...,
silent: bool = ...,
) -> Message: ...
Expand All @@ -1371,6 +1373,7 @@ async def send(
reference: Message | MessageReference | PartialMessage = ...,
mention_author: bool = ...,
view: View = ...,
poll: Poll = ...,
suppress: bool = ...,
silent: bool = ...,
) -> Message: ...
Expand All @@ -1391,6 +1394,7 @@ async def send(
reference: Message | MessageReference | PartialMessage = ...,
mention_author: bool = ...,
view: View = ...,
poll: Poll = ...,
suppress: bool = ...,
silent: bool = ...,
) -> Message: ...
Expand All @@ -1411,6 +1415,7 @@ async def send(
reference: Message | MessageReference | PartialMessage = ...,
mention_author: bool = ...,
view: View = ...,
poll: Poll = ...,
suppress: bool = ...,
silent: bool = ...,
) -> Message: ...
Expand All @@ -1432,6 +1437,7 @@ async def send(
reference=None,
mention_author=None,
view=None,
poll=None,
suppress=None,
silent=None,
):
Expand Down Expand Up @@ -1515,6 +1521,10 @@ async def send(
Whether to suppress push and desktop notifications for the message.
.. versionadded:: 2.4
poll: :class:`Poll`
The poll to send.
.. versionadded:: 2.6
Returns
-------
Expand Down Expand Up @@ -1594,6 +1604,9 @@ async def send(
else:
components = None

if poll:
poll = poll.to_dict()

if file is not None and files is not None:
raise InvalidArgument("cannot pass both file and files parameter to send()")

Expand All @@ -1616,6 +1629,7 @@ async def send(
stickers=stickers,
components=components,
flags=flags,
poll=poll,
)
finally:
file.close()
Expand Down Expand Up @@ -1643,6 +1657,7 @@ async def send(
stickers=stickers,
components=components,
flags=flags,
poll=poll,
)
finally:
for f in files:
Expand All @@ -1661,6 +1676,7 @@ async def send(
stickers=stickers,
components=components,
flags=flags,
poll=poll,
)

ret = state.create_message(channel=channel, data=data)
Expand Down
24 changes: 24 additions & 0 deletions discord/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
from .channel import DMChannel
from .member import Member
from .message import Message
from .poll import Poll
from .voice_client import VoiceProtocol

__all__ = ("Client",)
Expand Down Expand Up @@ -338,6 +339,14 @@ def stickers(self) -> list[GuildSticker]:
"""
return self._connection.stickers

@property
def polls(self) -> list[Poll]:
"""The polls that the connected client has.
.. versionadded:: 2.6
"""
return self._connection.polls

@property
def cached_messages(self) -> Sequence[Message]:
"""Read-only list of messages the connected client has cached.
Expand Down Expand Up @@ -1010,6 +1019,21 @@ def get_sticker(self, id: int, /) -> GuildSticker | None:
"""
return self._connection.get_sticker(id)

def get_poll(self, id: int, /) -> Poll | None:
"""Returns a poll attached to the given message ID.
Parameters
----------
id: :class:`int`
The message ID of the poll to search for.
Returns
-------
Optional[:class:`.Poll`]
The poll or ``None`` if not found.
"""
return self._connection.get_poll(id)

def get_all_channels(self) -> Generator[GuildChannel, None, None]:
"""A generator that retrieves every :class:`.abc.GuildChannel` the client can 'access'.
Expand Down
6 changes: 6 additions & 0 deletions discord/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,12 @@ class EntitlementOwnerType(Enum):
user = 2


class PollLayoutType(Enum):
"""The poll's layout type."""

default = 1


T = TypeVar("T")


Expand Down
68 changes: 68 additions & 0 deletions discord/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,8 @@ def messages(self):
- :class:`Message`
- :attr:`Client.cached_messages`
- :meth:`Client.get_message`
- :attr:`Client.polls`
- :meth:`Client.get_poll`
Note that due to an implicit relationship this also corresponds to the following events:
Expand Down Expand Up @@ -917,6 +919,8 @@ def guild_messages(self):
- :class:`Message`
- :attr:`Client.cached_messages` (only for guilds)
- :meth:`Client.get_message` (only for guilds)
- :attr:`Client.polls` (only for guilds)
- :meth:`Client.get_poll` (only for guilds)
Note that due to an implicit relationship this also corresponds to the following events:
Expand All @@ -931,6 +935,7 @@ def guild_messages(self):
- :attr:`Message.embeds`
- :attr:`Message.attachments`
- :attr:`Message.components`
- :attr:`Message.poll`
For more information go to the :ref:`message content intent documentation <need_message_content_intent>`.
"""
Expand All @@ -955,6 +960,8 @@ def dm_messages(self):
- :class:`Message`
- :attr:`Client.cached_messages` (only for DMs)
- :meth:`Client.get_message` (only for DMs)
- :attr:`Client.polls` (only for DMs)
- :meth:`Client.get_poll` (only for DMs)
Note that due to an implicit relationship this also corresponds to the following events:
Expand Down Expand Up @@ -1079,6 +1086,7 @@ def message_content(self):
- :attr:`Message.embeds`
- :attr:`Message.attachments`
- :attr:`Message.components`
- :attr:`Message.poll`
These attributes will still be available for messages received from interactions,
the bot's own messages, messages the bot was mentioned in, and DMs.
Expand Down Expand Up @@ -1137,6 +1145,66 @@ def auto_moderation_execution(self):
"""
return 1 << 21

@flag_value
def guild_polls(self):
""":class:`bool`: Whether poll-related events in guilds are enabled.
See also :attr:`dm_polls` for DMs or :attr:`polls` for both.
This corresponds to the following events:
- :func:`on_poll_vote_add` (only for guilds)
- :func:`on_poll_vote_remove` (only for guilds)
- :func:`on_raw_poll_vote_add` (only for guilds)
- :func:`on_raw_poll_vote_remove` (only for guilds)
This also corresponds to the following attributes and classes in terms of cache:
- :attr:`PollAnswer.count` (only for guild polls)
- :attr:`PollResults.answer_counts` (only for guild polls)
"""
return 1 << 24

@flag_value
def dm_polls(self):
""":class:`bool`: Whether poll-related events in direct messages are enabled.
See also :attr:`guild_polls` for guilds or :attr:`polls` for both.
This corresponds to the following events:
- :func:`on_poll_vote_add` (only for DMs)
- :func:`on_poll_vote_remove` (only for DMs)
- :func:`on_raw_poll_vote_add` (only for DMs)
- :func:`on_raw_poll_vote_remove` (only for DMs)
This also corresponds to the following attributes and classes in terms of cache:
- :attr:`PollAnswer.count` (only for DM polls)
- :attr:`PollResults.answer_counts` (only for DM polls)
"""
return 1 << 25

@alias_flag_value
def polls(self):
""":class:`bool`: Whether poll-related events in guilds and direct messages are enabled.
This is a shortcut to set or get both :attr:`guild_polls` and :attr:`dm_polls`.
This corresponds to the following events:
- :func:`on_poll_vote_add` (both guilds and DMs)
- :func:`on_poll_vote_remove` (both guilds and DMs)
- :func:`on_raw_poll_vote_add` (both guilds and DMs)
- :func:`on_raw_poll_vote_remove` (both guilds and DMs)
This also corresponds to the following attributes and classes in terms of cache:
- :attr:`PollAnswer.count` (both guild and DM polls)
- :attr:`PollResults.answer_counts` (both guild and DM polls)
"""
return (1 << 24) | (1 << 25)


@fill_with_flags()
class MemberCacheFlags(BaseFlags):
Expand Down
47 changes: 47 additions & 0 deletions discord/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
message,
monetization,
onboarding,
poll,
role,
scheduled_events,
sticker,
Expand Down Expand Up @@ -471,6 +472,7 @@ def send_message(
stickers: list[sticker.StickerItem] | None = None,
components: list[components.Component] | None = None,
flags: int | None = None,
poll: poll.Poll | None = None,
) -> Response[message.Message]:
r = Route("POST", "/channels/{channel_id}/messages", channel_id=channel_id)
payload = {}
Expand Down Expand Up @@ -508,6 +510,9 @@ def send_message(
if flags:
payload["flags"] = flags

if poll:
payload["poll"] = poll

return self.request(r, json=payload)

def send_typing(self, channel_id: Snowflake) -> Response[None]:
Expand All @@ -531,6 +536,7 @@ def send_multipart_helper(
stickers: list[sticker.StickerItem] | None = None,
components: list[components.Component] | None = None,
flags: int | None = None,
poll: poll.Poll | None = None,
) -> Response[message.Message]:
form = []

Expand All @@ -555,6 +561,8 @@ def send_multipart_helper(
payload["sticker_ids"] = stickers
if flags:
payload["flags"] = flags
if poll:
payload["poll"] = poll

attachments = []
form.append({"name": "payload_json"})
Expand Down Expand Up @@ -594,6 +602,7 @@ def send_files(
stickers: list[sticker.StickerItem] | None = None,
components: list[components.Component] | None = None,
flags: int | None = None,
poll: poll.Poll | None = None,
) -> Response[message.Message]:
r = Route("POST", "/channels/{channel_id}/messages", channel_id=channel_id)
return self.send_multipart_helper(
Expand All @@ -610,6 +619,7 @@ def send_files(
stickers=stickers,
components=components,
flags=flags,
poll=poll,
)

def edit_multipart_helper(
Expand Down Expand Up @@ -3003,6 +3013,43 @@ def edit_onboarding(
reason=reason,
)

# Polls

def expire_poll(
self, channel_id: Snowflake, message_id: Snowflake
) -> Response[message.Message]:
return self.request(
Route(
"POST",
"/channels/{channel_id}/polls/{message_id}/expire",
channel_id=channel_id,
message_id=message_id,
)
)

def get_answer_voters(
self,
channel_id: Snowflake,
message_id: Snowflake,
answer_id: int,
limit: int,
after: Snowflake | None = None,
) -> Response[list[user.User]]:
r = Route(
"GET",
"/channels/{channel_id}/polls/{message_id}/answers/{answer_id}",
channel_id=channel_id,
message_id=message_id,
answer_id=answer_id,
)

params: dict[str, Any] = {
"limit": limit,
}
if after:
params["after"] = after
return self.request(r, params=params)

# Misc

def application_info(self) -> Response[appinfo.AppInfo]:
Expand Down
Loading

0 comments on commit cd9ca50

Please sign in to comment.