scurrypy 0.5__tar.gz → 0.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of scurrypy might be problematic. Click here for more details.
- {scurrypy-0.5 → scurrypy-0.5.3}/PKG-INFO +14 -13
- {scurrypy-0.5 → scurrypy-0.5.3}/README.md +13 -12
- {scurrypy-0.5 → scurrypy-0.5.3}/pyproject.toml +7 -7
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/__init__.py +92 -92
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/client_like.py +1 -1
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/dispatch/command_dispatcher.py +3 -3
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/dispatch/event_dispatcher.py +15 -15
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/events/interaction_events.py +2 -2
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/events/message_events.py +4 -4
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/intents.py +1 -1
- scurrypy-0.5.3/scurrypy/model.py +71 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/models/emoji.py +16 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/parts/channel.py +1 -1
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/parts/command.py +4 -4
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/parts/components.py +12 -12
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/parts/components_v2.py +10 -10
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/parts/message.py +4 -4
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/resources/channel.py +2 -2
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/resources/guild.py +3 -3
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/resources/interaction.py +4 -4
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/resources/message.py +3 -3
- {scurrypy-0.5 → scurrypy-0.5.3}/scurrypy.egg-info/PKG-INFO +14 -13
- scurrypy-0.5.3/scurrypy.egg-info/SOURCES.txt +56 -0
- scurrypy-0.5.3/scurrypy.egg-info/top_level.txt +1 -0
- scurrypy-0.5/discord/model.py +0 -90
- scurrypy-0.5/scurrypy.egg-info/SOURCES.txt +0 -56
- scurrypy-0.5/scurrypy.egg-info/top_level.txt +0 -1
- {scurrypy-0.5 → scurrypy-0.5.3}/LICENSE +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/client.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/config.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/dispatch/__init__.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/dispatch/prefix_dispatcher.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/error.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/events/__init__.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/events/channel_events.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/events/guild_events.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/events/hello_event.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/events/reaction_events.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/events/ready_event.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/gateway.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/http.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/logger.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/models/__init__.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/models/application.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/models/guild.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/models/integration.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/models/interaction.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/models/member.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/models/role.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/models/user.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/parts/__init__.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/parts/component_types.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/parts/embed.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/parts/modal.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/parts/role.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/resources/__init__.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/resources/application.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/resources/bot_emojis.py +0 -0
- {scurrypy-0.5/discord → scurrypy-0.5.3/scurrypy}/resources/user.py +0 -0
- {scurrypy-0.5 → scurrypy-0.5.3}/scurrypy.egg-info/dependency_links.txt +0 -0
- {scurrypy-0.5 → scurrypy-0.5.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scurrypy
|
|
3
|
-
Version: 0.5
|
|
3
|
+
Version: 0.5.3
|
|
4
4
|
Summary: Dataclass-driven Discord API Wrapper in Python
|
|
5
5
|
Author: Furmissile
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -21,10 +21,11 @@ A dataclass-driven Discord API wrapper in Python!
|
|
|
21
21
|
While this wrapper is mainly used for various squirrel-related shenanigans, it can also be used for more generic bot purposes.
|
|
22
22
|
|
|
23
23
|
## Features
|
|
24
|
-
* Command and event handling
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* Command, and event handling
|
|
25
|
+
* Unix shell-style wildcards for component routing
|
|
26
|
+
* Declarative style using decorators
|
|
27
|
+
* Supports both legacy and new features
|
|
28
|
+
* Respects Discord’s rate limits
|
|
28
29
|
|
|
29
30
|
## Notes & Early Status
|
|
30
31
|
* This is an early version — feedback, ideas, and contributions are very welcome! That said, there may be bumps along the way, so expect occasional bugs and quirks.
|
|
@@ -45,24 +46,24 @@ The following demonstrates building and responding to a slash command.
|
|
|
45
46
|
*Note: Adjust `dotenv_path` if your `.env` file is not in the same directory as this script.*
|
|
46
47
|
|
|
47
48
|
```py
|
|
48
|
-
import
|
|
49
|
+
import scurrypy, os
|
|
49
50
|
from dotenv import load_dotenv
|
|
50
51
|
|
|
51
52
|
load_dotenv(dotenv_path='./path/to/env')
|
|
52
53
|
|
|
53
|
-
client =
|
|
54
|
+
client = scurrypy.Client(
|
|
54
55
|
token=os.getenv("DISCORD_TOKEN"),
|
|
55
56
|
application_id=APPLICATION_ID # your bot’s application ID
|
|
56
57
|
)
|
|
57
58
|
|
|
58
59
|
@client.command(
|
|
59
|
-
command=
|
|
60
|
+
command=scurrypy.SlashCommand(
|
|
60
61
|
name='example',
|
|
61
62
|
description='Demonstrate the minimal slash command!'
|
|
62
63
|
),
|
|
63
64
|
guild_ids=GUILD_ID # must be a guild ID your bot is in
|
|
64
65
|
)
|
|
65
|
-
async def example(bot:
|
|
66
|
+
async def example(bot: scurrypy.Client, event: scurrypy.InteractionEvent):
|
|
66
67
|
await event.interaction.respond(f'Hello, {event.interaction.member.user.username}!')
|
|
67
68
|
|
|
68
69
|
client.run()
|
|
@@ -71,20 +72,20 @@ client.run()
|
|
|
71
72
|
## Minimal Prefix Command (Legacy)
|
|
72
73
|
The following demonstrates building and responding to a message prefix command.
|
|
73
74
|
```py
|
|
74
|
-
import
|
|
75
|
+
import scurrypy, os
|
|
75
76
|
from dotenv import load_dotenv
|
|
76
77
|
|
|
77
78
|
load_dotenv(dotenv_path='./path/to/env')
|
|
78
79
|
|
|
79
|
-
client =
|
|
80
|
+
client = scurrypy.Client(
|
|
80
81
|
token=os.getenv("DISCORD_TOKEN"),
|
|
81
82
|
application_id=APPLICATION_ID # your bot’s application ID
|
|
82
|
-
intents=
|
|
83
|
+
intents=scurrypy.set_intents(message_content=True),
|
|
83
84
|
prefix='!' # your custom prefix
|
|
84
85
|
)
|
|
85
86
|
|
|
86
87
|
@client.prefix_command
|
|
87
|
-
async def ping(bot:
|
|
88
|
+
async def ping(bot: scurrypy.Client, event: scurrypy.MessageCreateEvent):
|
|
88
89
|
# The function name is the name of the command
|
|
89
90
|
await event.message.send("Pong!")
|
|
90
91
|
|
|
@@ -11,10 +11,11 @@ A dataclass-driven Discord API wrapper in Python!
|
|
|
11
11
|
While this wrapper is mainly used for various squirrel-related shenanigans, it can also be used for more generic bot purposes.
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
|
-
* Command and event handling
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* Command, and event handling
|
|
15
|
+
* Unix shell-style wildcards for component routing
|
|
16
|
+
* Declarative style using decorators
|
|
17
|
+
* Supports both legacy and new features
|
|
18
|
+
* Respects Discord’s rate limits
|
|
18
19
|
|
|
19
20
|
## Notes & Early Status
|
|
20
21
|
* This is an early version — feedback, ideas, and contributions are very welcome! That said, there may be bumps along the way, so expect occasional bugs and quirks.
|
|
@@ -35,24 +36,24 @@ The following demonstrates building and responding to a slash command.
|
|
|
35
36
|
*Note: Adjust `dotenv_path` if your `.env` file is not in the same directory as this script.*
|
|
36
37
|
|
|
37
38
|
```py
|
|
38
|
-
import
|
|
39
|
+
import scurrypy, os
|
|
39
40
|
from dotenv import load_dotenv
|
|
40
41
|
|
|
41
42
|
load_dotenv(dotenv_path='./path/to/env')
|
|
42
43
|
|
|
43
|
-
client =
|
|
44
|
+
client = scurrypy.Client(
|
|
44
45
|
token=os.getenv("DISCORD_TOKEN"),
|
|
45
46
|
application_id=APPLICATION_ID # your bot’s application ID
|
|
46
47
|
)
|
|
47
48
|
|
|
48
49
|
@client.command(
|
|
49
|
-
command=
|
|
50
|
+
command=scurrypy.SlashCommand(
|
|
50
51
|
name='example',
|
|
51
52
|
description='Demonstrate the minimal slash command!'
|
|
52
53
|
),
|
|
53
54
|
guild_ids=GUILD_ID # must be a guild ID your bot is in
|
|
54
55
|
)
|
|
55
|
-
async def example(bot:
|
|
56
|
+
async def example(bot: scurrypy.Client, event: scurrypy.InteractionEvent):
|
|
56
57
|
await event.interaction.respond(f'Hello, {event.interaction.member.user.username}!')
|
|
57
58
|
|
|
58
59
|
client.run()
|
|
@@ -61,20 +62,20 @@ client.run()
|
|
|
61
62
|
## Minimal Prefix Command (Legacy)
|
|
62
63
|
The following demonstrates building and responding to a message prefix command.
|
|
63
64
|
```py
|
|
64
|
-
import
|
|
65
|
+
import scurrypy, os
|
|
65
66
|
from dotenv import load_dotenv
|
|
66
67
|
|
|
67
68
|
load_dotenv(dotenv_path='./path/to/env')
|
|
68
69
|
|
|
69
|
-
client =
|
|
70
|
+
client = scurrypy.Client(
|
|
70
71
|
token=os.getenv("DISCORD_TOKEN"),
|
|
71
72
|
application_id=APPLICATION_ID # your bot’s application ID
|
|
72
|
-
intents=
|
|
73
|
+
intents=scurrypy.set_intents(message_content=True),
|
|
73
74
|
prefix='!' # your custom prefix
|
|
74
75
|
)
|
|
75
76
|
|
|
76
77
|
@client.prefix_command
|
|
77
|
-
async def ping(bot:
|
|
78
|
+
async def ping(bot: scurrypy.Client, event: scurrypy.MessageCreateEvent):
|
|
78
79
|
# The function name is the name of the command
|
|
79
80
|
await event.message.send("Pong!")
|
|
80
81
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "scurrypy"
|
|
7
|
-
version = "0.5"
|
|
7
|
+
version = "0.5.3"
|
|
8
8
|
|
|
9
9
|
description = "Dataclass-driven Discord API Wrapper in Python"
|
|
10
10
|
readme = "README.md"
|
|
@@ -14,10 +14,10 @@ dependencies = []
|
|
|
14
14
|
|
|
15
15
|
[tool.setuptools]
|
|
16
16
|
packages = [
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
17
|
+
"scurrypy",
|
|
18
|
+
"scurrypy.dispatch",
|
|
19
|
+
"scurrypy.events",
|
|
20
|
+
"scurrypy.models",
|
|
21
|
+
"scurrypy.resources",
|
|
22
|
+
"scurrypy.parts"
|
|
23
23
|
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# scurrypy
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
@@ -261,110 +261,110 @@ def __getattr__(name: str):
|
|
|
261
261
|
|
|
262
262
|
mapping = {
|
|
263
263
|
# top-level
|
|
264
|
-
"Logger": "
|
|
265
|
-
"Client": "
|
|
266
|
-
"Intents": "
|
|
267
|
-
"set_intents": "
|
|
268
|
-
"BaseConfig": "
|
|
264
|
+
"Logger": "scurrypy.logger",
|
|
265
|
+
"Client": "scurrypy.client",
|
|
266
|
+
"Intents": "scurrypy.intents",
|
|
267
|
+
"set_intents": "scurrypy.intents",
|
|
268
|
+
"BaseConfig": "scurrypy.config",
|
|
269
269
|
|
|
270
|
-
'InteractionTypes': "
|
|
270
|
+
'InteractionTypes': "scurrypy.dispatch.command_dispatcher",
|
|
271
271
|
|
|
272
|
-
"ReadyEvent": "
|
|
272
|
+
"ReadyEvent": "scurrypy.events.ready_event",
|
|
273
273
|
|
|
274
|
-
"ReactionAddEvent": "
|
|
275
|
-
"ReactionRemoveEvent": "
|
|
276
|
-
"ReactionRemoveEmojiEvent": "
|
|
277
|
-
"ReactionRemoveAllEvent": "
|
|
278
|
-
|
|
279
|
-
"GuildCreateEvent": "
|
|
280
|
-
"GuildUpdateEvent": "
|
|
281
|
-
"GuildDeleteEvent": "
|
|
282
|
-
|
|
283
|
-
"MessageCreateEvent": "
|
|
284
|
-
"MessageUpdateEvent": "
|
|
285
|
-
"MessageDeleteEvent": "
|
|
286
|
-
|
|
287
|
-
"GuildChannelCreateEvent": "
|
|
288
|
-
"GuildChannelUpdateEvent": "
|
|
289
|
-
"GuildChannelDeleteEvent": "
|
|
290
|
-
"ChannelPinsUpdateEvent": "
|
|
291
|
-
|
|
292
|
-
"InteractionEvent": "
|
|
293
|
-
|
|
294
|
-
'ApplicationModel': "
|
|
295
|
-
'EmojiModel': "
|
|
296
|
-
'GuildModel': "
|
|
297
|
-
'MemberModel': "
|
|
298
|
-
'UserModel': "
|
|
299
|
-
'RoleModel': "
|
|
300
|
-
|
|
301
|
-
'ChannelTypes': "
|
|
302
|
-
'GuildChannel': "
|
|
303
|
-
|
|
304
|
-
'CommandTypes': "
|
|
305
|
-
'CommandOptionTypes': "
|
|
306
|
-
'SlashCommand': "
|
|
307
|
-
'UserCommand': "
|
|
308
|
-
'MessageCommand': "
|
|
309
|
-
|
|
310
|
-
'ComponentV2Types': "
|
|
311
|
-
'SectionPart': "
|
|
312
|
-
'TextDisplay': "
|
|
313
|
-
'Thumbnail': "
|
|
314
|
-
'MediaGalleryItem': "
|
|
315
|
-
'MediaGallery': "
|
|
316
|
-
'File': "
|
|
317
|
-
'SeparatorTypes': "
|
|
318
|
-
'Separator': "
|
|
319
|
-
'ContainerPart': "
|
|
320
|
-
'Label': "
|
|
321
|
-
|
|
322
|
-
'ComponentTypes': "
|
|
323
|
-
'ActionRowPart': "
|
|
324
|
-
'ButtonStyles': "
|
|
325
|
-
'Button': "
|
|
326
|
-
'SelectOption': "
|
|
327
|
-
'StringSelect': "
|
|
328
|
-
'TextInputStyles': '
|
|
329
|
-
'TextInput': "
|
|
330
|
-
'DefaultValue': "
|
|
331
|
-
'UserSelect': "
|
|
332
|
-
'RoleSelect': "
|
|
333
|
-
'MentionableSelect': "
|
|
334
|
-
'ChannelSelect': "
|
|
274
|
+
"ReactionAddEvent": "scurrypy.events.reaction_events",
|
|
275
|
+
"ReactionRemoveEvent": "scurrypy.events.reaction_events",
|
|
276
|
+
"ReactionRemoveEmojiEvent": "scurrypy.events.reaction_events",
|
|
277
|
+
"ReactionRemoveAllEvent": "scurrypy.events.reaction_events",
|
|
278
|
+
|
|
279
|
+
"GuildCreateEvent": "scurrypy.events.guild_events",
|
|
280
|
+
"GuildUpdateEvent": "scurrypy.events.guild_events",
|
|
281
|
+
"GuildDeleteEvent": "scurrypy.events.guild_events",
|
|
282
|
+
|
|
283
|
+
"MessageCreateEvent": "scurrypy.events.message_events",
|
|
284
|
+
"MessageUpdateEvent": "scurrypy.events.message_events",
|
|
285
|
+
"MessageDeleteEvent": "scurrypy.events.message_events",
|
|
286
|
+
|
|
287
|
+
"GuildChannelCreateEvent": "scurrypy.events.channel_events",
|
|
288
|
+
"GuildChannelUpdateEvent": "scurrypy.events.channel_events",
|
|
289
|
+
"GuildChannelDeleteEvent": "scurrypy.events.channel_events",
|
|
290
|
+
"ChannelPinsUpdateEvent": "scurrypy.events.channel_events",
|
|
291
|
+
|
|
292
|
+
"InteractionEvent": "scurrypy.events.interaction_events",
|
|
293
|
+
|
|
294
|
+
'ApplicationModel': "scurrypy.models.application",
|
|
295
|
+
'EmojiModel': "scurrypy.models.emoji",
|
|
296
|
+
'GuildModel': "scurrypy.models.guild",
|
|
297
|
+
'MemberModel': "scurrypy.models.member",
|
|
298
|
+
'UserModel': "scurrypy.models.user",
|
|
299
|
+
'RoleModel': "scurrypy.models.role",
|
|
300
|
+
|
|
301
|
+
'ChannelTypes': "scurrypy.parts.channel",
|
|
302
|
+
'GuildChannel': "scurrypy.parts.channel",
|
|
303
|
+
|
|
304
|
+
'CommandTypes': "scurrypy.parts.command",
|
|
305
|
+
'CommandOptionTypes': "scurrypy.parts.command",
|
|
306
|
+
'SlashCommand': "scurrypy.parts.command",
|
|
307
|
+
'UserCommand': "scurrypy.parts.command",
|
|
308
|
+
'MessageCommand': "scurrypy.parts.command",
|
|
309
|
+
|
|
310
|
+
'ComponentV2Types': "scurrypy.parts.components_v2",
|
|
311
|
+
'SectionPart': "scurrypy.parts.components_v2",
|
|
312
|
+
'TextDisplay': "scurrypy.parts.components_v2",
|
|
313
|
+
'Thumbnail': "scurrypy.parts.components_v2",
|
|
314
|
+
'MediaGalleryItem': "scurrypy.parts.components_v2",
|
|
315
|
+
'MediaGallery': "scurrypy.parts.components_v2",
|
|
316
|
+
'File': "scurrypy.parts.components_v2",
|
|
317
|
+
'SeparatorTypes': "scurrypy.parts.components_v2",
|
|
318
|
+
'Separator': "scurrypy.parts.components_v2",
|
|
319
|
+
'ContainerPart': "scurrypy.parts.components_v2",
|
|
320
|
+
'Label': "scurrypy.parts.components_v2",
|
|
321
|
+
|
|
322
|
+
'ComponentTypes': "scurrypy.parts.components",
|
|
323
|
+
'ActionRowPart': "scurrypy.parts.components",
|
|
324
|
+
'ButtonStyles': "scurrypy.parts.components",
|
|
325
|
+
'Button': "scurrypy.parts.components",
|
|
326
|
+
'SelectOption': "scurrypy.parts.components",
|
|
327
|
+
'StringSelect': "scurrypy.parts.components",
|
|
328
|
+
'TextInputStyles': 'scurrypy.parts.components',
|
|
329
|
+
'TextInput': "scurrypy.parts.components",
|
|
330
|
+
'DefaultValue': "scurrypy.parts.components",
|
|
331
|
+
'UserSelect': "scurrypy.parts.components",
|
|
332
|
+
'RoleSelect': "scurrypy.parts.components",
|
|
333
|
+
'MentionableSelect': "scurrypy.parts.components",
|
|
334
|
+
'ChannelSelect': "scurrypy.parts.components",
|
|
335
335
|
|
|
336
|
-
'EmbedAuthor': "
|
|
337
|
-
'EmbedThumbnail': "
|
|
338
|
-
'EmbedField': "
|
|
339
|
-
'EmbedImage': "
|
|
340
|
-
'EmbedFooter': "
|
|
341
|
-
'EmbedPart': "
|
|
336
|
+
'EmbedAuthor': "scurrypy.parts.embed",
|
|
337
|
+
'EmbedThumbnail': "scurrypy.parts.embed",
|
|
338
|
+
'EmbedField': "scurrypy.parts.embed",
|
|
339
|
+
'EmbedImage': "scurrypy.parts.embed",
|
|
340
|
+
'EmbedFooter': "scurrypy.parts.embed",
|
|
341
|
+
'EmbedPart': "scurrypy.parts.embed",
|
|
342
342
|
|
|
343
|
-
'MessageFlags': "
|
|
344
|
-
'MessageReferenceTypes': "
|
|
345
|
-
'MessageReference': "
|
|
346
|
-
'Attachment': "
|
|
347
|
-
'MessagePart': "
|
|
343
|
+
'MessageFlags': "scurrypy.parts.message",
|
|
344
|
+
'MessageReferenceTypes': "scurrypy.parts.message",
|
|
345
|
+
'MessageReference': "scurrypy.parts.message",
|
|
346
|
+
'Attachment': "scurrypy.parts.message",
|
|
347
|
+
'MessagePart': "scurrypy.parts.message",
|
|
348
348
|
|
|
349
|
-
'ModalPart': "
|
|
350
|
-
'Role': "
|
|
349
|
+
'ModalPart': "scurrypy.parts.modal",
|
|
350
|
+
'Role': "scurrypy.parts.role",
|
|
351
351
|
|
|
352
|
-
'ApplicationFlags': "
|
|
353
|
-
'Application': "
|
|
352
|
+
'ApplicationFlags': "scurrypy.resources.application",
|
|
353
|
+
'Application': "scurrypy.resources.application",
|
|
354
354
|
|
|
355
|
-
'BotEmojis': "
|
|
355
|
+
'BotEmojis': "scurrypy.resources.bot_emojis",
|
|
356
356
|
|
|
357
|
-
'PinnedMessage': "
|
|
358
|
-
'Channel': "
|
|
357
|
+
'PinnedMessage': "scurrypy.resources.channel",
|
|
358
|
+
'Channel': "scurrypy.resources.channel",
|
|
359
359
|
|
|
360
|
-
'Guild': "
|
|
360
|
+
'Guild': "scurrypy.resources.guild",
|
|
361
361
|
|
|
362
|
-
'InteractionCallbackTypes': "
|
|
363
|
-
'Interaction': "
|
|
362
|
+
'InteractionCallbackTypes': "scurrypy.resources.interaction",
|
|
363
|
+
'Interaction': "scurrypy.resources.interaction",
|
|
364
364
|
|
|
365
|
-
'Message': "
|
|
365
|
+
'Message': "scurrypy.resources.message",
|
|
366
366
|
|
|
367
|
-
'User': "
|
|
367
|
+
'User': "scurrypy.resources.user"
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
module = importlib.import_module(mapping[name])
|
|
@@ -5,7 +5,7 @@ from .http import HTTPClient
|
|
|
5
5
|
from .logger import Logger
|
|
6
6
|
|
|
7
7
|
class ClientLike(Protocol):
|
|
8
|
-
"""Exposes a common interface for [`Client`][
|
|
8
|
+
"""Exposes a common interface for [`Client`][scurrypy.client.Client]."""
|
|
9
9
|
application_id: int
|
|
10
10
|
"""Bot's application ID."""
|
|
11
11
|
|
|
@@ -25,7 +25,7 @@ class CommandDispatcher:
|
|
|
25
25
|
InteractionTypes.MESSAGE_COMPONENT: MessageComponentData,
|
|
26
26
|
InteractionTypes.MODAL_SUBMIT: ModalData
|
|
27
27
|
}
|
|
28
|
-
"""Maps [`InteractionTypes`][
|
|
28
|
+
"""Maps [`InteractionTypes`][scurrypy.dispatch.command_dispatcher.InteractionTypes] to their respective dataclass."""
|
|
29
29
|
|
|
30
30
|
def __init__(self, client: ClientLike):
|
|
31
31
|
self.application_id = client.application_id
|
|
@@ -66,7 +66,7 @@ class CommandDispatcher:
|
|
|
66
66
|
await self._http.request(
|
|
67
67
|
'PUT',
|
|
68
68
|
f"applications/{self.application_id}/guilds/{guild_id}/commands",
|
|
69
|
-
data=[command.
|
|
69
|
+
data=[command.to_dict() for command in cmds]
|
|
70
70
|
)
|
|
71
71
|
|
|
72
72
|
async def _register_global_commands(self, commands: list):
|
|
@@ -76,7 +76,7 @@ class CommandDispatcher:
|
|
|
76
76
|
commands (list): list of serialized commands
|
|
77
77
|
"""
|
|
78
78
|
|
|
79
|
-
global_commands = [command.
|
|
79
|
+
global_commands = [command.to_dict() for command in commands]
|
|
80
80
|
|
|
81
81
|
await self._http.request('PUT', f"applications/{self.application_id}/commands", data=global_commands)
|
|
82
82
|
|
|
@@ -7,25 +7,25 @@ class EventDispatcher:
|
|
|
7
7
|
"""Central hub for handling Discord Gateway events."""
|
|
8
8
|
|
|
9
9
|
RESOURCE_MAP = { # maps discord events to their respective dataclass (lazy loading)
|
|
10
|
-
'READY': ('
|
|
10
|
+
'READY': ('scurrypy.events.ready_event', 'ReadyEvent'),
|
|
11
11
|
|
|
12
|
-
'GUILD_CREATE': ('
|
|
13
|
-
'GUILD_UPDATE': ('
|
|
14
|
-
'GUILD_DELETE': ('
|
|
12
|
+
'GUILD_CREATE': ('scurrypy.events.guild_events', 'GuildCreateEvent'),
|
|
13
|
+
'GUILD_UPDATE': ('scurrypy.events.guild_events', 'GuildUpdateEvent'),
|
|
14
|
+
'GUILD_DELETE': ('scurrypy.events.guild_events', 'GuildDeleteEvent'),
|
|
15
15
|
|
|
16
|
-
'CHANNEL_CREATE': ('
|
|
17
|
-
'CHANNEL_UPDATE': ('
|
|
18
|
-
'CHANNEL_DELETE': ('
|
|
19
|
-
'CHANNEL_PINS_UPDATE': ('
|
|
16
|
+
'CHANNEL_CREATE': ('scurrypy.events.channel_events', 'GuildChannelCreateEvent'),
|
|
17
|
+
'CHANNEL_UPDATE': ('scurrypy.events.channel_events', 'GuildChannelUpdateEvent'),
|
|
18
|
+
'CHANNEL_DELETE': ('scurrypy.events.channel_events', 'GuildChannelDeleteEvent'),
|
|
19
|
+
'CHANNEL_PINS_UPDATE': ('scurrypy.events.channel_events', 'ChannelPinsUpdateEvent'),
|
|
20
20
|
|
|
21
|
-
'MESSAGE_CREATE': ('
|
|
22
|
-
'MESSAGE_UPDATE': ('
|
|
23
|
-
'MESSAGE_DELETE': ('
|
|
21
|
+
'MESSAGE_CREATE': ('scurrypy.events.message_events', 'MessageCreateEvent'),
|
|
22
|
+
'MESSAGE_UPDATE': ('scurrypy.events.message_events', 'MessageUpdateEvent'),
|
|
23
|
+
'MESSAGE_DELETE': ('scurrypy.events.message_events', 'MessageDeleteEvent'),
|
|
24
24
|
|
|
25
|
-
'MESSAGE_REACTION_ADD': ('
|
|
26
|
-
'MESSAGE_REACTION_REMOVE': ('
|
|
27
|
-
'MESSAGE_REACTION_REMOVE_ALL': ('
|
|
28
|
-
'MESSAGE_REACTION_REMOVE_EMOJI': ('
|
|
25
|
+
'MESSAGE_REACTION_ADD': ('scurrypy.events.reaction_events', 'ReactionAddEvent'),
|
|
26
|
+
'MESSAGE_REACTION_REMOVE': ('scurrypy.events.reaction_events', 'ReactionRemoveEvent'),
|
|
27
|
+
'MESSAGE_REACTION_REMOVE_ALL': ('scurrypy.events.reaction_events', 'ReactionRemoveAllEvent'),
|
|
28
|
+
'MESSAGE_REACTION_REMOVE_EMOJI': ('scurrypy.events.reaction_events', 'ReactionRemoveEmojiEvent'),
|
|
29
29
|
|
|
30
30
|
# and other events...
|
|
31
31
|
}
|
|
@@ -15,7 +15,7 @@ class ApplicationCommandOptionData(DataModel):
|
|
|
15
15
|
"""Name of the command option."""
|
|
16
16
|
|
|
17
17
|
type: int
|
|
18
|
-
"""Type of command option. See [`CommandOptionTypes`][
|
|
18
|
+
"""Type of command option. See [`CommandOptionTypes`][scurrypy.parts.command.CommandOptionTypes]."""
|
|
19
19
|
|
|
20
20
|
value: str | int | float | bool
|
|
21
21
|
"""Input value for option."""
|
|
@@ -153,7 +153,7 @@ class InteractionEvent(DataModel):
|
|
|
153
153
|
"""Represents the interaction response."""
|
|
154
154
|
|
|
155
155
|
interaction: Interaction
|
|
156
|
-
"""Interaction resource object. See [`Interaction`][
|
|
156
|
+
"""Interaction resource object. See [`Interaction`][scurrypy.resources.interaction.Interaction]."""
|
|
157
157
|
|
|
158
158
|
data: Optional[ApplicationCommandData | MessageComponentData | ModalData] = None
|
|
159
159
|
"""Interaction response data."""
|
|
@@ -9,25 +9,25 @@ from ..models.member import MemberModel
|
|
|
9
9
|
class MessageCreateEvent(DataModel):
|
|
10
10
|
"""Received when a message is created."""
|
|
11
11
|
message: Message
|
|
12
|
-
"""Message resource object. See [`Resource.Message`][
|
|
12
|
+
"""Message resource object. See [`Resource.Message`][scurrypy.resources.message.Message]."""
|
|
13
13
|
|
|
14
14
|
guild_id: Optional[int]
|
|
15
15
|
"""Guild ID of the updated message (if in a guild channel)."""
|
|
16
16
|
|
|
17
17
|
member: Optional[MemberModel] # guild-only author info
|
|
18
|
-
"""Partial Member object of the author of the message. See [`MemberModel`][
|
|
18
|
+
"""Partial Member object of the author of the message. See [`MemberModel`][scurrypy.models.member.MemberModel]."""
|
|
19
19
|
|
|
20
20
|
@dataclass
|
|
21
21
|
class MessageUpdateEvent(DataModel):
|
|
22
22
|
"""Received when a message is updated."""
|
|
23
23
|
message: Message
|
|
24
|
-
"""Message resource object. See [`Resource.Message`][
|
|
24
|
+
"""Message resource object. See [`Resource.Message`][scurrypy.resources.message.Message]."""
|
|
25
25
|
|
|
26
26
|
guild_id: Optional[int]
|
|
27
27
|
"""Guild ID of the updated message (if in a guild channel)."""
|
|
28
28
|
|
|
29
29
|
member: Optional[MemberModel]
|
|
30
|
-
"""Partial Member object of the author of the message. See [`MemberModel`][
|
|
30
|
+
"""Partial Member object of the author of the message. See [`MemberModel`][scurrypy.models.member.MemberModel]."""
|
|
31
31
|
|
|
32
32
|
@dataclass
|
|
33
33
|
class MessageDeleteEvent(DataModel):
|
|
@@ -54,7 +54,7 @@ class IntentFlagParams(TypedDict, total=False):
|
|
|
54
54
|
message_content: bool
|
|
55
55
|
|
|
56
56
|
def set_intents(**flags: Unpack[IntentFlagParams]):
|
|
57
|
-
"""Set bot intents using [`IntentFlagParams`][
|
|
57
|
+
"""Set bot intents using [`IntentFlagParams`][scurrypy.intents.IntentFlagParams].
|
|
58
58
|
`Intents.DEFAULT` = (GUILDS | GUILD_MESSAGES) will also be set.
|
|
59
59
|
|
|
60
60
|
Args:
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from dataclasses import dataclass, fields, is_dataclass
|
|
2
|
+
from typing import get_args, get_origin, Union
|
|
3
|
+
|
|
4
|
+
from .http import HTTPClient
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class DataModel:
|
|
8
|
+
"""DataModel is a base class for Discord JSONs that provides hydration from raw dicts,
|
|
9
|
+
optional field defaults, and access to HTTP-bound methods.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def from_dict(cls, data: dict, http: 'HTTPClient' = None):
|
|
14
|
+
"""Hydrates the given data into the dataclass child.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
data (dict): JSON data
|
|
18
|
+
http (HTTPClient, optional): HTTP session for requests
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
(dataclass): hydrated dataclass
|
|
22
|
+
"""
|
|
23
|
+
def unwrap_optional(t):
|
|
24
|
+
if get_origin(t) is Union:
|
|
25
|
+
args = tuple(a for a in get_args(t) if a is not type(None))
|
|
26
|
+
return args[0] if len(args) == 1 else Union[args]
|
|
27
|
+
return t
|
|
28
|
+
|
|
29
|
+
kwargs = {}
|
|
30
|
+
for f in fields(cls):
|
|
31
|
+
v = data.get(f.name)
|
|
32
|
+
t = unwrap_optional(f.type)
|
|
33
|
+
|
|
34
|
+
if v is None:
|
|
35
|
+
kwargs[f.name] = None
|
|
36
|
+
elif is_dataclass(t):
|
|
37
|
+
kwargs[f.name] = t.from_dict(v, http)
|
|
38
|
+
elif get_origin(t) is list:
|
|
39
|
+
lt = get_args(t)[0]
|
|
40
|
+
kwargs[f.name] = [lt.from_dict(x, http) if is_dataclass(lt) else x for x in v]
|
|
41
|
+
else:
|
|
42
|
+
try:
|
|
43
|
+
kwargs[f.name] = t(v)
|
|
44
|
+
except Exception:
|
|
45
|
+
kwargs[f.name] = v # fallback to raw
|
|
46
|
+
|
|
47
|
+
inst = cls(**kwargs)
|
|
48
|
+
if http: inst._http = http
|
|
49
|
+
return inst
|
|
50
|
+
|
|
51
|
+
def to_dict(self):
|
|
52
|
+
"""Recursively turns the dataclass into a dictionary and drops empty fields.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
(dict): serialized dataclasss
|
|
56
|
+
"""
|
|
57
|
+
def serialize(val):
|
|
58
|
+
if isinstance(val, list):
|
|
59
|
+
return [serialize(v) for v in val if v is not None]
|
|
60
|
+
if isinstance(val, DataModel):
|
|
61
|
+
return val.to_dict()
|
|
62
|
+
return val
|
|
63
|
+
|
|
64
|
+
result = {}
|
|
65
|
+
for f in fields(self):
|
|
66
|
+
if f.name.startswith('_'):
|
|
67
|
+
continue
|
|
68
|
+
val = getattr(self, f.name)
|
|
69
|
+
# if val not in (None, [], {}, "", 0):
|
|
70
|
+
result[f.name] = serialize(val)
|
|
71
|
+
return result
|
|
@@ -32,3 +32,19 @@ class EmojiModel(DataModel):
|
|
|
32
32
|
return quote(f"a:{self.name}:{self.id}")
|
|
33
33
|
|
|
34
34
|
return quote(f"{self.name}:{self.id}")
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def url(self) -> str:
|
|
38
|
+
"""
|
|
39
|
+
Return the full qualifying link for this emoji.
|
|
40
|
+
|
|
41
|
+
!!! important
|
|
42
|
+
This only works for custom Discord emojis (those with an ID).
|
|
43
|
+
Unicode emojis will return `None`.
|
|
44
|
+
"""
|
|
45
|
+
if not self.id:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
ext = 'gif' if self.animated else 'png'
|
|
49
|
+
|
|
50
|
+
return f"https://cdn.discordapp.com/emojis/{self.id}.{ext}"
|
|
@@ -27,7 +27,7 @@ class GuildChannel(DataModel):
|
|
|
27
27
|
"""Name of the channel."""
|
|
28
28
|
|
|
29
29
|
type: Optional[int] = None
|
|
30
|
-
"""Type of channel. See [`ChannelTypes`][
|
|
30
|
+
"""Type of channel. See [`ChannelTypes`][scurrypy.parts.channel.ChannelTypes]."""
|
|
31
31
|
|
|
32
32
|
topic: Optional[str] = None
|
|
33
33
|
"""Topic of channel."""
|