Skip to content

Commit 87f814f

Browse files
authored
Merge pull request #1690 from interactions-py/unstable
5.13
2 parents a8f6fcf + cf395d1 commit 87f814f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1522
-359
lines changed

.pre-commit-config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ repos:
3434
hooks:
3535
- id: ruff
3636
args: [--fix, --exit-non-zero-on-fix]
37+
language: python
3738
- repo: https://github.com/psf/black
3839
rev: 24.2.0
3940
hooks:
@@ -48,6 +49,9 @@ repos:
4849
# name: isort Formatting
4950
# language: python
5051
# types: [file, python]
52+
53+
default_language_version:
54+
python: python3.10
5155
ci:
5256
autoupdate_branch: "unstable"
5357
autofix_prs: true

docs/src/API Reference/API Reference/models/Discord/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ search:
2222
- [Invite](invite)
2323
- [Message](message)
2424
- [Modals](modals)
25+
- [Poll](poll)
2526
- [Reaction](reaction)
2627
- [Role](role)
2728
- [Scheduled event](scheduled_event)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
::: interactions.models.discord.poll

docs/src/Guides/01 Getting Started.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ async def on_ready():
7676
@listen()
7777
async def on_message_create(event):
7878
# This event is called when a message is sent in a channel the bot can see
79-
print(f"message received: {event.message.content}")
79+
print(f"message received: {event.message.jump_url}")
8080

8181

8282
bot.start("Put your token here")

docs/src/Guides/03 Creating Commands.md

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -423,18 +423,69 @@ There are two ways to define permissions.
423423

424424
Multiple permissions are defined with the bitwise OR operator `|`.
425425

426-
### Blocking Commands in DMs
426+
## Usable Contexts
427427

428-
You can also block commands in DMs. To do that, just set `dm_permission` to false.
428+
You can control where slash commands (and other application commands) can be used using - in guilds, in DMs, and/or other private channels. By default, commands can be used in all contexts.
429429

430-
```py
431-
@slash_command(
432-
name="my_guild_only_command",
433-
dm_permission=False,
434-
)
435-
async def my_command_function(ctx: SlashContext):
436-
...
437-
```
430+
As with permissions, there are two ways to define the context.
431+
432+
=== ":one: Decorators"
433+
434+
```python
435+
from interactions import contexts
436+
437+
@slash_command(name="my_guild_only_command")
438+
@contexts(guild=True, bot_dm=False, private_channel=False)
439+
async def my_command_function(ctx: SlashContext):
440+
...
441+
```
442+
443+
=== ":two: Function Definition"
444+
445+
```python
446+
from interactions import ContextType
447+
448+
@slash_command(
449+
name="my_command",
450+
contexts=[ContextType.GUILD],
451+
)
452+
async def my_command_function(ctx: SlashContext):
453+
...
454+
```
455+
456+
## Integration Types
457+
458+
Applications can be installed/integrated in different ways:
459+
- The one you are familiar with is the *guild* integration, where the application is installed in a specific guild, and so the entire guild can use the application.
460+
- You can also install the application to a *user*, where the application can then be used by the user anywhere they desire.
461+
462+
By default, commands can only be used in guild integrations. Like many other properties, this can be changed.
463+
464+
There are two ways to define this:
465+
466+
=== ":one: Decorators"
467+
468+
```python
469+
from interactions import integration_types
470+
471+
@slash_command(name="my_command")
472+
@integration_types(guild=True, user=True)
473+
async def my_command_function(ctx: SlashContext):
474+
...
475+
```
476+
477+
=== ":two: Function Definition"
478+
479+
```python
480+
from interactions import IntegrationType
481+
482+
@slash_command(
483+
name="my_command",
484+
integration_types=[IntegrationType.GUILD_INSTALL, IntegrationType.USER_INSTALL],
485+
)
486+
async def my_command_function(ctx: SlashContext):
487+
...
488+
```
438489

439490
## Checks
440491

interactions/__init__.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
MentionPrefix,
2626
Missing,
2727
MISSING,
28+
POLL_MAX_ANSWERS,
29+
POLL_MAX_DURATION_HOURS,
2830
PREMIUM_GUILD_LIMITS,
2931
SELECT_MAX_NAME_LENGTH,
3032
SELECTS_MAX_OPTIONS,
@@ -37,6 +39,7 @@
3739
smart_cache,
3840
T,
3941
T_co,
42+
ClientT,
4043
utils,
4144
)
4245
from .client import const
@@ -83,6 +86,7 @@
8386
BrandColors,
8487
BrandColours,
8588
Buckets,
89+
BulkBanResponse,
8690
Button,
8791
ButtonStyle,
8892
CallbackObject,
@@ -104,9 +108,11 @@
104108
ComponentContext,
105109
ComponentType,
106110
ConsumeRest,
111+
contexts,
107112
context_menu,
108113
ContextMenu,
109114
ContextMenuContext,
115+
ContextType,
110116
Converter,
111117
cooldown,
112118
Cooldown,
@@ -179,6 +185,8 @@
179185
IDConverter,
180186
InputText,
181187
IntegrationExpireBehaviour,
188+
IntegrationType,
189+
integration_types,
182190
Intents,
183191
InteractionCommand,
184192
InteractionContext,
@@ -214,6 +222,7 @@
214222
MessageConverter,
215223
MessageFlags,
216224
MessageInteraction,
225+
MessageInteractionMetadata,
217226
MessageReference,
218227
MessageType,
219228
MFALevel,
@@ -238,6 +247,12 @@
238247
PartialEmojiConverter,
239248
PermissionOverwrite,
240249
Permissions,
250+
Poll,
251+
PollAnswer,
252+
PollAnswerCount,
253+
PollLayoutType,
254+
PollMedia,
255+
PollResults,
241256
PremiumTier,
242257
PremiumType,
243258
process_allowed_mentions,
@@ -403,6 +418,7 @@
403418
"BrandColors",
404419
"BrandColours",
405420
"Buckets",
421+
"BulkBanResponse",
406422
"Button",
407423
"ButtonStyle",
408424
"CallbackObject",
@@ -415,6 +431,7 @@
415431
"ChannelType",
416432
"check",
417433
"Client",
434+
"ClientT",
418435
"ClientUser",
419436
"Color",
420437
"COLOR_TYPES",
@@ -426,10 +443,12 @@
426443
"ComponentType",
427444
"ConsumeRest",
428445
"const",
446+
"contexts",
429447
"context_menu",
430448
"CONTEXT_MENU_NAME_LENGTH",
431449
"ContextMenu",
432450
"ContextMenuContext",
451+
"ContextType",
433452
"Converter",
434453
"cooldown",
435454
"Cooldown",
@@ -519,6 +538,8 @@
519538
"IDConverter",
520539
"InputText",
521540
"IntegrationExpireBehaviour",
541+
"IntegrationType",
542+
"integration_types",
522543
"Intents",
523544
"InteractionCommand",
524545
"InteractionContext",
@@ -558,6 +579,7 @@
558579
"MessageConverter",
559580
"MessageFlags",
560581
"MessageInteraction",
582+
"MessageInteractionMetadata",
561583
"MessageReference",
562584
"MessageType",
563585
"MFALevel",
@@ -584,6 +606,14 @@
584606
"PartialEmojiConverter",
585607
"PermissionOverwrite",
586608
"Permissions",
609+
"Poll",
610+
"PollAnswer",
611+
"PollAnswerCount",
612+
"PollLayoutType",
613+
"POLL_MAX_ANSWERS",
614+
"POLL_MAX_DURATION_HOURS",
615+
"PollMedia",
616+
"PollResults",
587617
"PREMIUM_GUILD_LIMITS",
588618
"PremiumTier",
589619
"PremiumType",

interactions/api/events/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
MessageCreate,
4242
MessageDelete,
4343
MessageDeleteBulk,
44+
MessagePollVoteAdd,
45+
MessagePollVoteRemove,
4446
MessageReactionAdd,
4547
MessageReactionRemove,
4648
MessageReactionRemoveAll,
@@ -159,6 +161,8 @@
159161
"MessageCreate",
160162
"MessageDelete",
161163
"MessageDeleteBulk",
164+
"MessagePollVoteAdd",
165+
"MessagePollVoteRemove",
162166
"MessageReactionAdd",
163167
"MessageReactionRemove",
164168
"MessageReactionRemoveAll",

interactions/api/events/discord.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ async def an_event_handler(event: ChannelCreate):
7272
"MessageCreate",
7373
"MessageDelete",
7474
"MessageDeleteBulk",
75+
"MessagePollVoteAdd",
76+
"MessagePollVoteRemove",
7577
"MessageReactionAdd",
7678
"MessageReactionRemove",
7779
"MessageReactionRemoveAll",
@@ -115,6 +117,7 @@ async def an_event_handler(event: ChannelCreate):
115117
from interactions.models.discord.entitlement import Entitlement
116118
from interactions.models.discord.guild import Guild, GuildIntegration
117119
from interactions.models.discord.message import Message
120+
from interactions.models.discord.poll import Poll
118121
from interactions.models.discord.reaction import Reaction
119122
from interactions.models.discord.role import Role
120123
from interactions.models.discord.scheduled_event import ScheduledEvent
@@ -588,6 +591,72 @@ class MessageReactionRemoveEmoji(MessageReactionRemoveAll):
588591
"""The emoji that was removed"""
589592

590593

594+
@attrs.define(eq=False, order=False, hash=False, kw_only=False)
595+
class BaseMessagePollEvent(BaseEvent):
596+
user_id: "Snowflake_Type" = attrs.field(repr=False)
597+
"""The ID of the user that voted"""
598+
channel_id: "Snowflake_Type" = attrs.field(repr=False)
599+
"""The ID of the channel the poll is in"""
600+
message_id: "Snowflake_Type" = attrs.field(repr=False)
601+
"""The ID of the message the poll is in"""
602+
answer_id: int = attrs.field(repr=False)
603+
"""The ID of the answer the user voted for"""
604+
guild_id: "Optional[Snowflake_Type]" = attrs.field(repr=False, default=None)
605+
"""The ID of the guild the poll is in"""
606+
607+
def get_message(self) -> "Optional[Message]":
608+
"""Get the message object if it is cached"""
609+
return self.client.cache.get_message(self.channel_id, self.message_id)
610+
611+
def get_user(self) -> "Optional[User]":
612+
"""Get the user object if it is cached"""
613+
return self.client.get_user(self.user_id)
614+
615+
def get_channel(self) -> "Optional[TYPE_ALL_CHANNEL]":
616+
"""Get the channel object if it is cached"""
617+
return self.client.get_channel(self.channel_id)
618+
619+
def get_guild(self) -> "Optional[Guild]":
620+
"""Get the guild object if it is cached"""
621+
return self.client.get_guild(self.guild_id) if self.guild_id is not None else None
622+
623+
def get_poll(self) -> "Optional[Poll]":
624+
"""Get the poll object if it is cached"""
625+
message = self.get_message()
626+
return message.poll if message is not None else None
627+
628+
async def fetch_message(self) -> "Message":
629+
"""Fetch the message the poll is in"""
630+
return await self.client.cache.fetch_message(self.channel_id, self.message_id)
631+
632+
async def fetch_user(self) -> "User":
633+
"""Fetch the user that voted"""
634+
return await self.client.fetch_user(self.user_id)
635+
636+
async def fetch_channel(self) -> "TYPE_ALL_CHANNEL":
637+
"""Fetch the channel the poll is in"""
638+
return await self.client.fetch_channel(self.channel_id)
639+
640+
async def fetch_guild(self) -> "Optional[Guild]":
641+
"""Fetch the guild the poll is in"""
642+
return await self.client.fetch_guild(self.guild_id) if self.guild_id is not None else None
643+
644+
async def fetch_poll(self) -> "Poll":
645+
"""Fetch the poll object"""
646+
message = await self.fetch_message()
647+
return message.poll
648+
649+
650+
@attrs.define(eq=False, order=False, hash=False, kw_only=False)
651+
class MessagePollVoteAdd(BaseMessagePollEvent):
652+
"""Dispatched when a user votes in a poll"""
653+
654+
655+
@attrs.define(eq=False, order=False, hash=False, kw_only=False)
656+
class MessagePollVoteRemove(BaseMessagePollEvent):
657+
"""Dispatched when a user remotes a votes in a poll"""
658+
659+
591660
@attrs.define(eq=False, order=False, hash=False, kw_only=False)
592661
class PresenceUpdate(BaseEvent):
593662
"""A user's presence has changed."""

interactions/api/events/processors/message_events.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,41 @@ async def _on_raw_message_delete_bulk(self, event: "RawGatewayEvent") -> None:
8383
event.data.get("ids"),
8484
)
8585
)
86+
87+
@Processor.define()
88+
async def _on_raw_message_poll_vote_add(self, event: "RawGatewayEvent") -> None:
89+
"""
90+
Process raw message poll vote add event and dispatch a processed poll vote add event.
91+
92+
Args:
93+
event: raw poll vote add event
94+
95+
"""
96+
self.dispatch(
97+
events.MessagePollVoteAdd(
98+
event.data.get("guild_id", None),
99+
event.data["channel_id"],
100+
event.data["message_id"],
101+
event.data["user_id"],
102+
event.data["option"],
103+
)
104+
)
105+
106+
@Processor.define()
107+
async def _on_raw_message_poll_vote_remove(self, event: "RawGatewayEvent") -> None:
108+
"""
109+
Process raw message poll vote remove event and dispatch a processed poll vote remove event.
110+
111+
Args:
112+
event: raw poll vote remove event
113+
114+
"""
115+
self.dispatch(
116+
events.MessagePollVoteRemove(
117+
event.data.get("guild_id", None),
118+
event.data["channel_id"],
119+
event.data["message_id"],
120+
event.data["user_id"],
121+
event.data["option"],
122+
)
123+
)

0 commit comments

Comments
 (0)