scurrypy 0.3.0__tar.gz → 0.3.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.

Files changed (67) hide show
  1. scurrypy-0.3.3/PKG-INFO +92 -0
  2. scurrypy-0.3.3/README.md +82 -0
  3. scurrypy-0.3.3/discord/__init__.py +223 -0
  4. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/client.py +29 -21
  5. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/dispatch/command_dispatcher.py +19 -16
  6. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/dispatch/event_dispatcher.py +19 -30
  7. scurrypy-0.3.3/discord/events/__init__.py +1 -0
  8. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/events/guild_events.py +1 -1
  9. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/logger.py +4 -3
  10. scurrypy-0.3.3/discord/models/__init__.py +1 -0
  11. scurrypy-0.3.3/discord/models/interaction.py +26 -0
  12. scurrypy-0.3.3/discord/parts/__init__.py +2 -0
  13. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/parts/message.py +2 -1
  14. scurrypy-0.3.3/discord/resources/__init__.py +1 -0
  15. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/resources/channel.py +30 -1
  16. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/resources/interaction.py +11 -5
  17. {scurrypy-0.3.0 → scurrypy-0.3.3}/pyproject.toml +1 -1
  18. scurrypy-0.3.3/scurrypy.egg-info/PKG-INFO +92 -0
  19. {scurrypy-0.3.0 → scurrypy-0.3.3}/scurrypy.egg-info/SOURCES.txt +1 -0
  20. scurrypy-0.3.0/PKG-INFO +0 -85
  21. scurrypy-0.3.0/README.md +0 -75
  22. scurrypy-0.3.0/discord/__init__.py +0 -10
  23. scurrypy-0.3.0/discord/events/__init__.py +0 -33
  24. scurrypy-0.3.0/discord/models/__init__.py +0 -8
  25. scurrypy-0.3.0/discord/parts/__init__.py +0 -28
  26. scurrypy-0.3.0/discord/resources/__init__.py +0 -10
  27. scurrypy-0.3.0/scurrypy.egg-info/PKG-INFO +0 -85
  28. {scurrypy-0.3.0 → scurrypy-0.3.3}/LICENSE +0 -0
  29. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/client_like.py +0 -0
  30. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/config.py +0 -0
  31. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/dispatch/__init__.py +0 -0
  32. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/dispatch/prefix_dispatcher.py +0 -0
  33. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/error.py +0 -0
  34. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/events/channel_events.py +0 -0
  35. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/events/hello_event.py +0 -0
  36. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/events/interaction_events.py +0 -0
  37. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/events/message_events.py +0 -0
  38. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/events/reaction_events.py +0 -0
  39. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/events/ready_event.py +0 -0
  40. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/gateway.py +0 -0
  41. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/http.py +0 -0
  42. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/intents.py +0 -0
  43. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/model.py +0 -0
  44. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/models/application.py +0 -0
  45. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/models/emoji.py +0 -0
  46. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/models/guild.py +0 -0
  47. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/models/integration.py +0 -0
  48. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/models/member.py +0 -0
  49. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/models/role.py +0 -0
  50. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/models/user.py +0 -0
  51. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/parts/action_row.py +0 -0
  52. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/parts/attachment.py +0 -0
  53. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/parts/channel.py +0 -0
  54. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/parts/command.py +0 -0
  55. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/parts/component_types.py +0 -0
  56. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/parts/components_v2.py +0 -0
  57. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/parts/embed.py +0 -0
  58. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/parts/modal.py +0 -0
  59. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/parts/role.py +0 -0
  60. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/resources/application.py +0 -0
  61. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/resources/bot_emojis.py +0 -0
  62. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/resources/guild.py +0 -0
  63. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/resources/message.py +0 -0
  64. {scurrypy-0.3.0 → scurrypy-0.3.3}/discord/resources/user.py +0 -0
  65. {scurrypy-0.3.0 → scurrypy-0.3.3}/scurrypy.egg-info/dependency_links.txt +0 -0
  66. {scurrypy-0.3.0 → scurrypy-0.3.3}/scurrypy.egg-info/top_level.txt +0 -0
  67. {scurrypy-0.3.0 → scurrypy-0.3.3}/setup.cfg +0 -0
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: scurrypy
3
+ Version: 0.3.3
4
+ Summary: Discord API Wrapper in Python
5
+ Author: Furmissile
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Dynamic: license-file
10
+
11
+ # __Welcome to ScurryPy__
12
+
13
+ [![PyPI version](https://badge.fury.io/py/scurrypy.svg)](https://badge.fury.io/py/scurrypy)
14
+
15
+ Yet another Discord API wrapper in Python!
16
+
17
+ While this wrapper is mainly used for various squirrel-related shenanigans, it can also be used for more generic bot purposes.
18
+
19
+ ## Features
20
+ * Command and event handling
21
+ * Declarative style using decorators
22
+ * Supports both legacy and new features
23
+ * Respects Discord's rate limits
24
+
25
+ ## Some things to consider...
26
+ * 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.
27
+ * Certain features are not yet supported, while others are intentionally omitted. See the [docs](https://furmissile.github.io/scurrypy) for full details.
28
+
29
+ ## Getting Started
30
+ *Note: This section also appears in the documentation, but here are complete examples ready to use with your bot credentials.*
31
+
32
+ ### Installation
33
+ To install the ScurryPy package, run:
34
+ ```bash
35
+ pip install scurrypy
36
+ ```
37
+
38
+ ### Minimal Slash Command
39
+ The following demonstrates building and responding to a slash command.
40
+
41
+ *Note: Adjust `dotenv_path` if your `.env` file is not in the same directory as this script.*
42
+
43
+ ```python
44
+ import discord, os
45
+ from dotenv import load_dotenv
46
+
47
+ load_dotenv(dotenv_path='./path/to/env')
48
+
49
+ client = discord.Client(
50
+ token=os.getenv("DISCORD_TOKEN"),
51
+ application_id=APPLICATION_ID # replace with your bot's user ID
52
+ )
53
+
54
+ @client.command(
55
+ command=discord.SlashCommand(
56
+ name='example',
57
+ description='Demonstrate the minimal slash command!'
58
+ ),
59
+ guild_id=GUILD_ID # must be a guild ID your bot is in
60
+ )
61
+ async def example(bot: discord.Client, event: discord.InteractionEvent):
62
+ await event.interaction.respond(f'Hello, {event.interaction.member.user.username}!')
63
+
64
+ client.run()
65
+ ```
66
+
67
+ ### Minimal Prefix Command (Legacy)
68
+ The following demonstrates building and responding to a message prefix command.
69
+
70
+ ```python
71
+ import discord, os
72
+ from dotenv import load_dotenv
73
+
74
+ load_dotenv(dotenv_path='./path/to/env')
75
+
76
+ client = discord.Client(
77
+ token=os.getenv("DISCORD_TOKEN"),
78
+ application_id=APPLICATION_ID, # replace with your bot's user ID
79
+ intents=discord.set_intents(message_content=True),
80
+ prefix='!' # your custom prefix
81
+ )
82
+
83
+ @client.prefix_command
84
+ async def ping(bot: discord.Client, event: discord.MessageCreateEvent):
85
+ # The function name is the name of the command
86
+ await event.message.send("Pong!")
87
+
88
+ client.run()
89
+ ```
90
+
91
+ ## Like what you see?
92
+ Check out the full [documentation](https://furmissile.github.io/scurrypy) for more examples, guides, and API reference!
@@ -0,0 +1,82 @@
1
+ # __Welcome to ScurryPy__
2
+
3
+ [![PyPI version](https://badge.fury.io/py/scurrypy.svg)](https://badge.fury.io/py/scurrypy)
4
+
5
+ Yet another Discord API wrapper in Python!
6
+
7
+ While this wrapper is mainly used for various squirrel-related shenanigans, it can also be used for more generic bot purposes.
8
+
9
+ ## Features
10
+ * Command and event handling
11
+ * Declarative style using decorators
12
+ * Supports both legacy and new features
13
+ * Respects Discord's rate limits
14
+
15
+ ## Some things to consider...
16
+ * 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.
17
+ * Certain features are not yet supported, while others are intentionally omitted. See the [docs](https://furmissile.github.io/scurrypy) for full details.
18
+
19
+ ## Getting Started
20
+ *Note: This section also appears in the documentation, but here are complete examples ready to use with your bot credentials.*
21
+
22
+ ### Installation
23
+ To install the ScurryPy package, run:
24
+ ```bash
25
+ pip install scurrypy
26
+ ```
27
+
28
+ ### Minimal Slash Command
29
+ The following demonstrates building and responding to a slash command.
30
+
31
+ *Note: Adjust `dotenv_path` if your `.env` file is not in the same directory as this script.*
32
+
33
+ ```python
34
+ import discord, os
35
+ from dotenv import load_dotenv
36
+
37
+ load_dotenv(dotenv_path='./path/to/env')
38
+
39
+ client = discord.Client(
40
+ token=os.getenv("DISCORD_TOKEN"),
41
+ application_id=APPLICATION_ID # replace with your bot's user ID
42
+ )
43
+
44
+ @client.command(
45
+ command=discord.SlashCommand(
46
+ name='example',
47
+ description='Demonstrate the minimal slash command!'
48
+ ),
49
+ guild_id=GUILD_ID # must be a guild ID your bot is in
50
+ )
51
+ async def example(bot: discord.Client, event: discord.InteractionEvent):
52
+ await event.interaction.respond(f'Hello, {event.interaction.member.user.username}!')
53
+
54
+ client.run()
55
+ ```
56
+
57
+ ### Minimal Prefix Command (Legacy)
58
+ The following demonstrates building and responding to a message prefix command.
59
+
60
+ ```python
61
+ import discord, os
62
+ from dotenv import load_dotenv
63
+
64
+ load_dotenv(dotenv_path='./path/to/env')
65
+
66
+ client = discord.Client(
67
+ token=os.getenv("DISCORD_TOKEN"),
68
+ application_id=APPLICATION_ID, # replace with your bot's user ID
69
+ intents=discord.set_intents(message_content=True),
70
+ prefix='!' # your custom prefix
71
+ )
72
+
73
+ @client.prefix_command
74
+ async def ping(bot: discord.Client, event: discord.MessageCreateEvent):
75
+ # The function name is the name of the command
76
+ await event.message.send("Pong!")
77
+
78
+ client.run()
79
+ ```
80
+
81
+ ## Like what you see?
82
+ Check out the full [documentation](https://furmissile.github.io/scurrypy) for more examples, guides, and API reference!
@@ -0,0 +1,223 @@
1
+ # discord
2
+
3
+ import importlib
4
+ from typing import TYPE_CHECKING
5
+
6
+ __all__ = [
7
+ # top-level
8
+ "Logger",
9
+ "Client",
10
+ "Intents",
11
+ "set_intents",
12
+ "BaseConfig",
13
+
14
+ # events
15
+ "ReadyEvent",
16
+ "ReactionAddEvent",
17
+ "ReactionRemoveEvent",
18
+ "ReactionRemoveEmojiEvent",
19
+ "ReactionRemoveAllEvent",
20
+ "GuildCreateEvent",
21
+ "GuildUpdateEvent",
22
+ "GuildDeleteEvent",
23
+ "MessageCreateEvent",
24
+ "MessageUpdateEvent",
25
+ "MessageDeleteEvent",
26
+ "GuildChannelCreateEvent",
27
+ "GuildChannelUpdateEvent",
28
+ "GuildChannelDeleteEvent",
29
+ "ChannelPinsUpdateEvent",
30
+ "InteractionEvent",
31
+
32
+ # models
33
+ "ApplicationModel",
34
+ "EmojiModel",
35
+ "GuildModel",
36
+ "MemberModel",
37
+ "UserModel",
38
+ "RoleModel",
39
+
40
+ # parts
41
+ "GuildChannel",
42
+ "Role",
43
+ "MessageBuilder",
44
+ "ModalBuilder",
45
+ "EmbedBuilder",
46
+ "ActionRow",
47
+ "StringSelect",
48
+ "UserSelect",
49
+ "RoleSelect",
50
+ "ChannelSelect",
51
+ "MentionableSelect",
52
+ "SlashCommand",
53
+ "MessageCommand",
54
+ "UserCommand",
55
+ "Container",
56
+ "Section",
57
+ "MediaGalleryItem",
58
+ "Label",
59
+
60
+ # resources
61
+ "Guild",
62
+ "Channel",
63
+ "Message",
64
+ "BotEmojis",
65
+ "User",
66
+ "Interaction",
67
+ "Application",
68
+ ]
69
+
70
+ # For editor support / autocomplete
71
+ if TYPE_CHECKING:
72
+ from .logger import Logger
73
+ from .client import Client
74
+ from .intents import Intents, set_intents
75
+ from .config import BaseConfig
76
+
77
+ # events
78
+ from .events.ready_event import ReadyEvent
79
+ from .events.reaction_events import (
80
+ ReactionAddEvent,
81
+ ReactionRemoveEvent,
82
+ ReactionRemoveEmojiEvent,
83
+ ReactionRemoveAllEvent,
84
+ )
85
+ from .events.guild_events import (
86
+ GuildCreateEvent,
87
+ GuildUpdateEvent,
88
+ GuildDeleteEvent,
89
+ )
90
+ from .events.message_events import (
91
+ MessageCreateEvent,
92
+ MessageUpdateEvent,
93
+ MessageDeleteEvent,
94
+ )
95
+ from .events.channel_events import (
96
+ GuildChannelCreateEvent,
97
+ GuildChannelUpdateEvent,
98
+ GuildChannelDeleteEvent,
99
+ ChannelPinsUpdateEvent,
100
+ )
101
+ from .events.interaction_events import InteractionEvent
102
+
103
+ # models
104
+ from .models.application import ApplicationModel
105
+ from .models.emoji import EmojiModel
106
+ from .models.guild import GuildModel
107
+ from .models.member import MemberModel
108
+ from .models.user import UserModel
109
+ from .models.role import RoleModel
110
+
111
+ # parts
112
+ from .parts.channel import GuildChannel
113
+ from .parts.role import Role
114
+ from .parts.message import MessageBuilder
115
+ from .parts.modal import ModalBuilder
116
+ from .parts.embed import EmbedBuilder
117
+ from .parts.action_row import (
118
+ ActionRow,
119
+ StringSelect,
120
+ UserSelect,
121
+ RoleSelect,
122
+ ChannelSelect,
123
+ MentionableSelect
124
+ )
125
+
126
+ from .parts.command import (
127
+ SlashCommand,
128
+ MessageCommand,
129
+ UserCommand
130
+ )
131
+
132
+ from .parts.components_v2 import (
133
+ Container,
134
+ Section,
135
+ MediaGalleryItem,
136
+ Label
137
+ )
138
+
139
+ # resources
140
+ from .resources.guild import Guild
141
+ from .resources.channel import Channel
142
+ from .resources.message import Message
143
+ from .resources.bot_emojis import BotEmojis
144
+ from .resources.user import User
145
+ from .resources.interaction import Interaction
146
+ from .resources.application import Application
147
+
148
+ # Lazy loader
149
+ def __getattr__(name: str):
150
+ if name not in __all__:
151
+ raise AttributeError(f"module {__name__} has no attribute {name}")
152
+
153
+ mapping = {
154
+ # top-level
155
+ "Logger": "discord.logger",
156
+ "Client": "discord.client",
157
+ "Intents": "discord.intents",
158
+ "set_intents": "discord.intents",
159
+ "BaseConfig": "discord.config",
160
+
161
+ # events
162
+ "ReadyEvent": "discord.events.ready_event",
163
+ "ReactionAddEvent": "discord.events.reaction_events",
164
+ "ReactionRemoveEvent": "discord.events.reaction_events",
165
+ "ReactionRemoveEmojiEvent": "discord.events.reaction_events",
166
+ "ReactionRemoveAllEvent": "discord.events.reaction_events",
167
+ "GuildCreateEvent": "discord.events.guild_events",
168
+ "GuildUpdateEvent": "discord.events.guild_events",
169
+ "GuildDeleteEvent": "discord.events.guild_events",
170
+ "MessageCreateEvent": "discord.events.message_events",
171
+ "MessageUpdateEvent": "discord.events.message_events",
172
+ "MessageDeleteEvent": "discord.events.message_events",
173
+ "GuildChannelCreateEvent": "discord.events.channel_events",
174
+ "GuildChannelUpdateEvent": "discord.events.channel_events",
175
+ "GuildChannelDeleteEvent": "discord.events.channel_events",
176
+ "ChannelPinsUpdateEvent": "discord.events.channel_events",
177
+ "InteractionEvent": "discord.events.interaction_events",
178
+
179
+ # models
180
+ 'ApplicationModel': "discord.models.application",
181
+ 'EmojiModel': "discord.models.emoji",
182
+ 'GuildModel': "discord.models.guild",
183
+ 'MemberModel': "discord.models.member",
184
+ 'UserModel': "discord.models.user",
185
+ 'RoleModel': "discord.models.role",
186
+
187
+ # parts
188
+ 'GuildChannel': "discord.parts.channel",
189
+ 'Role': "discord.parts.role",
190
+ 'MessageBuilder': "discord.parts.message",
191
+ 'ModalBuilder': "discord.parts.modal",
192
+ 'EmbedBuilder': "discord.parts.embed",
193
+ 'ActionRow': "discord.parts.action_row",
194
+ 'StringSelect': "discord.parts.action_row",
195
+ 'UserSelect': "discord.parts.action_row",
196
+ 'RoleSelect': "discord.parts.action_row",
197
+ 'ChannelSelect': "discord.parts.action_row",
198
+ 'MentionableSelect': "discord.parts.action_row",
199
+ 'SlashCommand': "discord.parts.command",
200
+ 'MessageCommand': "discord.parts.command",
201
+ 'UserCommand': "discord.parts.command",
202
+ 'Container': "discord.parts.components_v2",
203
+ 'Section': "discord.parts.components_v2",
204
+ 'MediaGalleryItem': "discord.parts.components_v2",
205
+ 'Label': "discord.parts.components_v2",
206
+
207
+ # resources
208
+ 'Guild': "discord.resources.guild",
209
+ 'Channel': "discord.resources.channel",
210
+ 'Message': "discord.resources.message",
211
+ 'BotEmojis': "discord.resources.bot_emojis",
212
+ 'User': "discord.resources.user",
213
+ 'Interaction': "discord.resources.interaction",
214
+ 'Application': "discord.resources.application"
215
+ }
216
+
217
+ module = importlib.import_module(mapping[name])
218
+ attr = getattr(module, name)
219
+ globals()[name] = attr # cache it for future lookups
220
+ return attr
221
+
222
+ def __dir__():
223
+ return sorted(list(globals().keys()) + __all__)
@@ -1,26 +1,10 @@
1
- import asyncio
2
-
3
- from .logger import Logger
4
- from .gateway import GatewayClient
5
- from .http import HTTPClient
1
+ from .config import BaseConfig
6
2
  from .intents import Intents
7
3
  from .error import DiscordError
8
- from .config import BaseConfig
9
4
  from .client_like import ClientLike
10
5
 
11
- from .resources.guild import Guild
12
- from .resources.channel import Channel
13
- from .resources.message import Message
14
- from .resources.bot_emojis import BotEmojis
15
- from .resources.user import User
16
- from .resources.application import Application
17
-
18
6
  from .parts.command import SlashCommand, MessageCommand, UserCommand
19
7
 
20
- from .dispatch.event_dispatcher import EventDispatcher
21
- from .dispatch.prefix_dispatcher import PrefixDispatcher
22
- from .dispatch.command_dispatcher import CommandDispatcher
23
-
24
8
  class Client(ClientLike):
25
9
  """Main entry point for Discord bots.
26
10
  Ties together the moving parts: gateway, HTTP, event dispatching, command handling, and resource managers.
@@ -45,6 +29,14 @@ class Client(ClientLike):
45
29
  prefix (str, optional): set message prefix if using command prefixes
46
30
  quiet (bool, optional): if INFO, DEBUG, and WARN should be logged
47
31
  """
32
+ from .logger import Logger
33
+ from .gateway import GatewayClient
34
+ from .http import HTTPClient
35
+ from .resources.bot_emojis import BotEmojis
36
+ from .dispatch.event_dispatcher import EventDispatcher
37
+ from .dispatch.prefix_dispatcher import PrefixDispatcher
38
+ from .dispatch.command_dispatcher import CommandDispatcher
39
+
48
40
  self.token = token
49
41
  self.application_id = application_id
50
42
  self.config = config
@@ -100,13 +92,13 @@ class Client(ClientLike):
100
92
  def decorator(func):
101
93
  # hash out command type
102
94
  if isinstance(command, MessageCommand):
103
- self.command_dispatcher.message_command(func)
95
+ self.command_dispatcher.message_command(command.name, func)
104
96
  elif isinstance(command, UserCommand):
105
- self.command_dispatcher.user_command(func)
97
+ self.command_dispatcher.user_command(command.name, func)
106
98
  elif isinstance(command, SlashCommand):
107
- self.command_dispatcher.command(func)
99
+ self.command_dispatcher.command(command.name, func)
108
100
  else:
109
- raise ValueError(f'Command {func.__name__} expected to be of type SlashCommand, UserCommand, MessageCommand; \
101
+ raise ValueError(f'Command {command.name} expected to be of type SlashCommand, UserCommand, MessageCommand; \
110
102
  got {type(command).__name__}.')
111
103
 
112
104
  # then hash out if this command should be guild or global level
@@ -154,6 +146,8 @@ class Client(ClientLike):
154
146
  Returns:
155
147
  (Application): the Application resource
156
148
  """
149
+ from .resources.application import Application
150
+
157
151
  return Application(application_id, self._http)
158
152
 
159
153
  def guild_from_id(self, guild_id: int):
@@ -165,6 +159,8 @@ class Client(ClientLike):
165
159
  Returns:
166
160
  (Guild): the Guild resource
167
161
  """
162
+ from .resources.guild import Guild
163
+
168
164
  return Guild(guild_id, self._http)
169
165
 
170
166
  def channel_from_id(self, channel_id: int):
@@ -176,6 +172,8 @@ class Client(ClientLike):
176
172
  Returns:
177
173
  (Channel): the Channel resource
178
174
  """
175
+ from .resources.channel import Channel
176
+
179
177
  return Channel(channel_id, self._http)
180
178
 
181
179
  def message_from_id(self, channel_id: int, message_id: int):
@@ -188,6 +186,8 @@ class Client(ClientLike):
188
186
  Returns:
189
187
  (Message): the Message resource
190
188
  """
189
+ from .resources.message import Message
190
+
191
191
  return Message(message_id, channel_id, self._http)
192
192
 
193
193
  def user_from_id(self, user_id: int):
@@ -199,6 +199,8 @@ class Client(ClientLike):
199
199
  Returns:
200
200
  (User): the User resource
201
201
  """
202
+ from .resources.user import User
203
+
202
204
  return User(user_id, self._http)
203
205
 
204
206
  async def clear_guild_commands(self, guild_id: int):
@@ -215,6 +217,8 @@ class Client(ClientLike):
215
217
 
216
218
  async def _listen(self):
217
219
  """Main event loop for incoming gateway requests."""
220
+ import asyncio
221
+
218
222
  while True:
219
223
  try:
220
224
  message = await self._ws.receive()
@@ -270,6 +274,8 @@ class Client(ClientLike):
270
274
  """Runs the main lifecycle of the bot.
271
275
  Handles connection setup, heartbeat management, event loop, and automatic reconnects.
272
276
  """
277
+ import asyncio
278
+
273
279
  while True:
274
280
  try:
275
281
  await self._http.start_session()
@@ -338,6 +344,8 @@ class Client(ClientLike):
338
344
  Handles starting the session, WS, and heartbeat, reconnection logic,
339
345
  setting up emojis and hooks, and then listens for gateway events.
340
346
  """
347
+ import asyncio
348
+
341
349
  try:
342
350
  asyncio.run(self.start())
343
351
  except KeyboardInterrupt:
@@ -78,39 +78,42 @@ class CommandDispatcher:
78
78
 
79
79
  await self._http.request('PUT', f"applications/{self.application_id}/commands", global_commands)
80
80
 
81
- def command(self, handler):
81
+ def command(self, name: str, handler):
82
82
  """Decorator to register slash commands.
83
83
 
84
84
  Args:
85
+ name (str): name of the command to register
85
86
  handler (callable): callback handle for command response
86
87
  """
87
- self._handlers[handler.__name__] = handler
88
-
89
- def component(self, func, custom_id: str):
90
- """Decorator to register component interactions.
91
-
92
- Args:
93
- custom_id (str): Identifier of the component
94
- !!! warning "Important"
95
- Must match the `custom_id` set where the component was created.
96
- """
97
- self._component_handlers[custom_id] = func
88
+ self._handlers[name] = handler
98
89
 
99
- def user_command(self, handler):
90
+ def user_command(self, name: str, handler):
100
91
  """Decorator to register user commands.
101
92
 
102
93
  Args:
94
+ name (str): name of the command to register
103
95
  handler (callable): callback handle for user command response
104
96
  """
105
- self._user_handlers[handler.__name__] = handler
97
+ self._user_handlers[name] = handler
106
98
 
107
- def message_command(self, handler):
99
+ def message_command(self, name: str, handler):
108
100
  """Decorator to register message commands.
109
101
 
110
102
  Args:
103
+ name (str): name of the command to register
111
104
  handler (callable): callback handle for message command response
112
105
  """
113
- self._message_handlers[handler.__name__] = handler
106
+ self._message_handlers[name] = handler
107
+
108
+ def component(self, func, custom_id: str):
109
+ """Decorator to register component interactions.
110
+
111
+ Args:
112
+ custom_id (str): Identifier of the component
113
+ !!! warning "Important"
114
+ Must match the `custom_id` set where the component was created.
115
+ """
116
+ self._component_handlers[custom_id] = func
114
117
 
115
118
  async def dispatch(self, data: dict):
116
119
  """Dispatch a response to an `INTERACTION_CREATE` event
@@ -1,42 +1,19 @@
1
+ from importlib import import_module
1
2
  from ..client_like import ClientLike
2
3
 
3
- from ..events.ready_event import *
4
- from ..events.reaction_events import *
5
- from ..events.guild_events import *
6
- from ..events.message_events import *
7
- from ..events.channel_events import *
8
- from ..events.interaction_events import *
9
-
10
4
  from ..resources.message import Message
11
5
 
12
6
  class EventDispatcher:
13
7
  """Central hub for handling Discord Gateway events."""
14
- RESOURCE_MAP = { # maps discord events to their respective dataclass
15
- 'READY': ReadyEvent,
16
-
17
- "MESSAGE_CREATE": MessageCreateEvent,
18
- "MESSAGE_UPDATE": MessageUpdateEvent,
19
- 'MESSAGE_DELETE': MessageDeleteEvent,
20
-
21
- 'MESSAGE_REACTION_ADD': ReactionAddEvent,
22
- 'MESSAGE_REACTION_REMOVE': ReactionRemoveEvent,
23
- 'MESSAGE_REACTION_REMOVE_ALL': ReactionRemoveAllEvent,
24
- 'MESSAGE_REACTION_REMOVE_EMOJI': ReactionRemoveEmojiEvent,
25
-
26
- 'CHANNEL_CREATE': GuildChannelCreateEvent,
27
- 'CHANNEL_UPDATE': GuildChannelUpdateEvent,
28
- 'CHANNEL_DELETE': GuildChannelDeleteEvent,
29
8
 
30
- 'CHANNEL_PINS_UPDATE': ChannelPinsUpdateEvent,
31
-
32
- 'GUILD_CREATE': GuildCreateEvent,
33
- 'GUILD_UPDATE': GuildUpdateEvent,
34
- 'GUILD_DELETE': GuildDeleteEvent,
35
-
36
- 'INTERACTION_CREATE': InteractionEvent
9
+ RESOURCE_MAP = { # maps discord events to their respective dataclass (lazy loading)
10
+ 'READY': ('discord.events.ready_event', 'ReadyEvent'),
37
11
 
12
+ 'MESSAGE_CREATE': ('discord.events.message_events', 'MessageCreateEvent')
13
+
38
14
  # and other events...
39
15
  }
16
+
40
17
  """Mapping of event names to respective dataclass."""
41
18
 
42
19
  def __init__(self, client: ClientLike):
@@ -75,9 +52,21 @@ class EventDispatcher:
75
52
  event_name (str): name of the event
76
53
  data (dict): Discord's raw event payload
77
54
  """
78
- cls = self.RESOURCE_MAP.get(event_name)
55
+ module_info = self.RESOURCE_MAP.get(event_name)
56
+
57
+ if not module_info:
58
+ return
79
59
 
60
+ module_name, class_name = module_info
61
+
62
+ module = import_module(module_name)
63
+ if not module:
64
+ print(f"Cannot find module '{module_name}'!")
65
+ return
66
+
67
+ cls = getattr(module, class_name)
80
68
  if not cls:
69
+ print(f"Cannot find class '{class_name}'!")
81
70
  return
82
71
 
83
72
  if isinstance(cls, Message) and cls.author.id == self.application_id:
@@ -0,0 +1 @@
1
+ # discord/events
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from ..models import MemberModel
2
+ from ..models.member import MemberModel
3
3
  from ..resources.channel import Channel
4
4
 
5
5
  @dataclass
@@ -1,6 +1,3 @@
1
- from datetime import datetime
2
- import copy
3
-
4
1
  class Logger:
5
2
  """A utility class for logging messages, supporting log levels, color-coded console output,
6
3
  optional file logging, and redaction of sensitive information.
@@ -51,6 +48,8 @@ class Logger:
51
48
  Returns:
52
49
  (str): timestamp formatted in YYYY-MM-DD HH:MM:SS
53
50
  """
51
+ from datetime import datetime
52
+
54
53
  return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
55
54
 
56
55
  def _log(self, level: str, color: str, message: str):
@@ -140,6 +139,8 @@ class Logger:
140
139
  return [_redact(item) for item in obj]
141
140
  return obj
142
141
 
142
+ import copy
143
+
143
144
  return _redact(copy.deepcopy(data))
144
145
 
145
146
  def close(self):