scurrypy 0.2.2__tar.gz → 0.3.2__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.
- {scurrypy-0.2.2/scurrypy.egg-info → scurrypy-0.3.2}/PKG-INFO +5 -5
- {scurrypy-0.2.2 → scurrypy-0.3.2}/README.md +4 -4
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/client.py +18 -11
- scurrypy-0.3.2/discord/client_like.py +19 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/dispatch/command_dispatcher.py +30 -26
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/dispatch/event_dispatcher.py +10 -9
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/dispatch/prefix_dispatcher.py +10 -9
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/channel_events.py +2 -3
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/guild_events.py +1 -2
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/interaction_events.py +0 -4
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/message_events.py +3 -4
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/reaction_events.py +4 -5
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/ready_event.py +0 -4
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/model.py +2 -0
- scurrypy-0.3.2/discord/models/interaction.py +26 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/action_row.py +0 -1
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/channel.py +30 -1
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/interaction.py +11 -3
- {scurrypy-0.2.2 → scurrypy-0.3.2}/pyproject.toml +1 -1
- {scurrypy-0.2.2 → scurrypy-0.3.2/scurrypy.egg-info}/PKG-INFO +5 -5
- {scurrypy-0.2.2 → scurrypy-0.3.2}/scurrypy.egg-info/SOURCES.txt +2 -1
- scurrypy-0.2.2/discord/events/event_model.py +0 -10
- {scurrypy-0.2.2 → scurrypy-0.3.2}/LICENSE +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/__init__.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/config.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/dispatch/__init__.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/error.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/__init__.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/hello_event.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/gateway.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/http.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/intents.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/logger.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/__init__.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/application.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/emoji.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/guild.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/integration.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/member.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/role.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/user.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/__init__.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/attachment.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/channel.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/command.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/component_types.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/components_v2.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/embed.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/message.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/modal.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/role.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/__init__.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/application.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/bot_emojis.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/guild.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/message.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/user.py +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/scurrypy.egg-info/dependency_links.txt +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/scurrypy.egg-info/top_level.txt +0 -0
- {scurrypy-0.2.2 → scurrypy-0.3.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scurrypy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Discord API Wrapper in Python
|
|
5
5
|
Author: Furmissile
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -46,14 +46,14 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
|
|
|
46
46
|
|
|
47
47
|
bot = discord.Client(
|
|
48
48
|
token=os.getenv("DISCORD_TOKEN"),
|
|
49
|
-
application_id=
|
|
49
|
+
application_id=APPLICATION_ID # replace with your bot's user ID
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
@bot.command(
|
|
53
53
|
command=discord.SlashCommand(name='example', description='Demonstrate the minimal slash command!'),
|
|
54
54
|
guild_id=GUILD_ID # must be a guild ID your bot is in!
|
|
55
55
|
)
|
|
56
|
-
async def example(event: discord.InteractionEvent):
|
|
56
|
+
async def example(bot: discord.Client, event: discord.InteractionEvent):
|
|
57
57
|
await event.interaction.respond(f'Hello, {event.interaction.member.user.username}!')
|
|
58
58
|
|
|
59
59
|
bot.run()
|
|
@@ -69,13 +69,13 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
|
|
|
69
69
|
|
|
70
70
|
bot = discord.Client(
|
|
71
71
|
token=os.getenv("DISCORD_TOKEN"),
|
|
72
|
-
application_id=
|
|
72
|
+
application_id=APPLICATION_ID, # replace with your bot's user ID
|
|
73
73
|
intents=discord.set_intents(message_content=True),
|
|
74
74
|
prefix='!' # your custom prefix
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
@bot.prefix_command
|
|
78
|
-
async def ping(event: discord.MessageCreateEvent): # the function name is the name of the command!
|
|
78
|
+
async def ping(bot: discord.Client, event: discord.MessageCreateEvent): # the function name is the name of the command!
|
|
79
79
|
await event.message.send(f"Pong!")
|
|
80
80
|
|
|
81
81
|
bot.run()
|
|
@@ -36,14 +36,14 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
|
|
|
36
36
|
|
|
37
37
|
bot = discord.Client(
|
|
38
38
|
token=os.getenv("DISCORD_TOKEN"),
|
|
39
|
-
application_id=
|
|
39
|
+
application_id=APPLICATION_ID # replace with your bot's user ID
|
|
40
40
|
)
|
|
41
41
|
|
|
42
42
|
@bot.command(
|
|
43
43
|
command=discord.SlashCommand(name='example', description='Demonstrate the minimal slash command!'),
|
|
44
44
|
guild_id=GUILD_ID # must be a guild ID your bot is in!
|
|
45
45
|
)
|
|
46
|
-
async def example(event: discord.InteractionEvent):
|
|
46
|
+
async def example(bot: discord.Client, event: discord.InteractionEvent):
|
|
47
47
|
await event.interaction.respond(f'Hello, {event.interaction.member.user.username}!')
|
|
48
48
|
|
|
49
49
|
bot.run()
|
|
@@ -59,13 +59,13 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
|
|
|
59
59
|
|
|
60
60
|
bot = discord.Client(
|
|
61
61
|
token=os.getenv("DISCORD_TOKEN"),
|
|
62
|
-
application_id=
|
|
62
|
+
application_id=APPLICATION_ID, # replace with your bot's user ID
|
|
63
63
|
intents=discord.set_intents(message_content=True),
|
|
64
64
|
prefix='!' # your custom prefix
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
@bot.prefix_command
|
|
68
|
-
async def ping(event: discord.MessageCreateEvent): # the function name is the name of the command!
|
|
68
|
+
async def ping(bot: discord.Client, event: discord.MessageCreateEvent): # the function name is the name of the command!
|
|
69
69
|
await event.message.send(f"Pong!")
|
|
70
70
|
|
|
71
71
|
bot.run()
|
|
@@ -6,6 +6,7 @@ from .http import HTTPClient
|
|
|
6
6
|
from .intents import Intents
|
|
7
7
|
from .error import DiscordError
|
|
8
8
|
from .config import BaseConfig
|
|
9
|
+
from .client_like import ClientLike
|
|
9
10
|
|
|
10
11
|
from .resources.guild import Guild
|
|
11
12
|
from .resources.channel import Channel
|
|
@@ -20,7 +21,7 @@ from .dispatch.event_dispatcher import EventDispatcher
|
|
|
20
21
|
from .dispatch.prefix_dispatcher import PrefixDispatcher
|
|
21
22
|
from .dispatch.command_dispatcher import CommandDispatcher
|
|
22
23
|
|
|
23
|
-
class Client:
|
|
24
|
+
class Client(ClientLike):
|
|
24
25
|
"""Main entry point for Discord bots.
|
|
25
26
|
Ties together the moving parts: gateway, HTTP, event dispatching, command handling, and resource managers.
|
|
26
27
|
"""
|
|
@@ -39,6 +40,7 @@ class Client:
|
|
|
39
40
|
token (str): the bot's token
|
|
40
41
|
application_id (int): the bot's user ID
|
|
41
42
|
intents (int, optional): gateway intents. Defaults to Intents.DEFAULT.
|
|
43
|
+
config (BaseConfig, optional): user-defined config data
|
|
42
44
|
debug_mode (bool, optional): toggle debug messages. Defaults to False.
|
|
43
45
|
prefix (str, optional): set message prefix if using command prefixes
|
|
44
46
|
quiet (bool, optional): if INFO, DEBUG, and WARN should be logged
|
|
@@ -54,9 +56,9 @@ class Client:
|
|
|
54
56
|
if prefix and (intents & Intents.MESSAGE_CONTENT == 0):
|
|
55
57
|
self._logger.log_warn('Prefix set without message content enabled.')
|
|
56
58
|
|
|
57
|
-
self.dispatcher = EventDispatcher(self
|
|
58
|
-
self.prefix_dispatcher = PrefixDispatcher(self
|
|
59
|
-
self.command_dispatcher = CommandDispatcher(self
|
|
59
|
+
self.dispatcher = EventDispatcher(self)
|
|
60
|
+
self.prefix_dispatcher = PrefixDispatcher(self, prefix)
|
|
61
|
+
self.command_dispatcher = CommandDispatcher(self)
|
|
60
62
|
|
|
61
63
|
self._global_commands = [] # SlashCommand
|
|
62
64
|
self._guild_commands = {} # {guild_id : [commands], ...}
|
|
@@ -75,13 +77,18 @@ class Client:
|
|
|
75
77
|
"""
|
|
76
78
|
self.prefix_dispatcher.register(func.__name__, func)
|
|
77
79
|
|
|
78
|
-
def component(self,
|
|
80
|
+
def component(self, custom_id: str):
|
|
79
81
|
"""Decorator registers a function for a component handler.
|
|
80
82
|
|
|
81
83
|
Args:
|
|
82
|
-
|
|
84
|
+
custom_id (str): Identifier of the component
|
|
85
|
+
!!! warning "Important"
|
|
86
|
+
Must match the `custom_id` set where the component was created.
|
|
83
87
|
"""
|
|
84
|
-
|
|
88
|
+
def decorator(func):
|
|
89
|
+
self.command_dispatcher.component(func, custom_id)
|
|
90
|
+
return func
|
|
91
|
+
return decorator
|
|
85
92
|
|
|
86
93
|
def command(self, command: SlashCommand | MessageCommand | UserCommand, guild_id: int = 0):
|
|
87
94
|
"""Decorator registers a function for a command handler.
|
|
@@ -93,13 +100,13 @@ class Client:
|
|
|
93
100
|
def decorator(func):
|
|
94
101
|
# hash out command type
|
|
95
102
|
if isinstance(command, MessageCommand):
|
|
96
|
-
self.command_dispatcher.message_command(func)
|
|
103
|
+
self.command_dispatcher.message_command(command.name, func)
|
|
97
104
|
elif isinstance(command, UserCommand):
|
|
98
|
-
self.command_dispatcher.user_command(func)
|
|
105
|
+
self.command_dispatcher.user_command(command.name, func)
|
|
99
106
|
elif isinstance(command, SlashCommand):
|
|
100
|
-
self.command_dispatcher.command(func)
|
|
107
|
+
self.command_dispatcher.command(command.name, func)
|
|
101
108
|
else:
|
|
102
|
-
raise ValueError(f'Command {
|
|
109
|
+
raise ValueError(f'Command {command.name} expected to be of type SlashCommand, UserCommand, MessageCommand; \
|
|
103
110
|
got {type(command).__name__}.')
|
|
104
111
|
|
|
105
112
|
# then hash out if this command should be guild or global level
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
from .config import BaseConfig
|
|
4
|
+
from .http import HTTPClient
|
|
5
|
+
from .logger import Logger
|
|
6
|
+
|
|
7
|
+
class ClientLike(Protocol):
|
|
8
|
+
"""Exposes a common interface for [`Client`][discord.client.Client]."""
|
|
9
|
+
application_id: int
|
|
10
|
+
"""Bot's application ID."""
|
|
11
|
+
|
|
12
|
+
config: BaseConfig
|
|
13
|
+
"""User-defined config."""
|
|
14
|
+
|
|
15
|
+
_http: HTTPClient
|
|
16
|
+
"""HTTP session for requests."""
|
|
17
|
+
|
|
18
|
+
_logger: Logger
|
|
19
|
+
"""Logger instance to log events."""
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
from ..http import HTTPClient
|
|
4
|
-
from ..logger import Logger
|
|
5
|
-
from ..config import BaseConfig
|
|
1
|
+
from ..client_like import ClientLike
|
|
6
2
|
|
|
7
3
|
from ..events.interaction_events import ApplicationCommandData, MessageComponentData, ModalData, InteractionEvent
|
|
8
4
|
from ..resources.interaction import Interaction, InteractionDataTypes
|
|
@@ -29,17 +25,20 @@ class CommandDispatcher:
|
|
|
29
25
|
}
|
|
30
26
|
"""Maps [`InteractionTypes`][discord.dispatch.command_dispatcher.InteractionTypes] to their respective dataclass."""
|
|
31
27
|
|
|
32
|
-
def __init__(self,
|
|
33
|
-
self.application_id = application_id
|
|
28
|
+
def __init__(self, client: ClientLike):
|
|
29
|
+
self.application_id = client.application_id
|
|
34
30
|
"""Bot's application ID."""
|
|
35
31
|
|
|
36
|
-
self.
|
|
32
|
+
self.bot = client
|
|
33
|
+
"""Bot session for user access to bot."""
|
|
34
|
+
|
|
35
|
+
self._http = client._http
|
|
37
36
|
"""HTTP session for requests."""
|
|
38
37
|
|
|
39
|
-
self._logger =
|
|
38
|
+
self._logger = client._logger
|
|
40
39
|
"""Logger instance to log events."""
|
|
41
40
|
|
|
42
|
-
self.config = config
|
|
41
|
+
self.config = client.config
|
|
43
42
|
|
|
44
43
|
self._component_handlers = {}
|
|
45
44
|
"""Mapping of component custom IDs to handler."""
|
|
@@ -79,37 +78,42 @@ class CommandDispatcher:
|
|
|
79
78
|
|
|
80
79
|
await self._http.request('PUT', f"applications/{self.application_id}/commands", global_commands)
|
|
81
80
|
|
|
82
|
-
def command(self, handler):
|
|
81
|
+
def command(self, name: str, handler):
|
|
83
82
|
"""Decorator to register slash commands.
|
|
84
83
|
|
|
85
84
|
Args:
|
|
85
|
+
name (str): name of the command to register
|
|
86
86
|
handler (callable): callback handle for command response
|
|
87
87
|
"""
|
|
88
|
-
self._handlers[
|
|
89
|
-
|
|
90
|
-
def component(self, handler):
|
|
91
|
-
"""Decorator to register component interactions.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
handler (callable): callback handle for component response
|
|
95
|
-
"""
|
|
96
|
-
self._component_handlers[handler.__name__] = handler
|
|
88
|
+
self._handlers[name] = handler
|
|
97
89
|
|
|
98
|
-
def user_command(self, handler):
|
|
90
|
+
def user_command(self, name: str, handler):
|
|
99
91
|
"""Decorator to register user commands.
|
|
100
92
|
|
|
101
93
|
Args:
|
|
94
|
+
name (str): name of the command to register
|
|
102
95
|
handler (callable): callback handle for user command response
|
|
103
96
|
"""
|
|
104
|
-
self._user_handlers[
|
|
97
|
+
self._user_handlers[name] = handler
|
|
105
98
|
|
|
106
|
-
def message_command(self, handler):
|
|
99
|
+
def message_command(self, name: str, handler):
|
|
107
100
|
"""Decorator to register message commands.
|
|
108
101
|
|
|
109
102
|
Args:
|
|
103
|
+
name (str): name of the command to register
|
|
110
104
|
handler (callable): callback handle for message command response
|
|
111
105
|
"""
|
|
112
|
-
self._message_handlers[
|
|
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
|
|
113
117
|
|
|
114
118
|
async def dispatch(self, data: dict):
|
|
115
119
|
"""Dispatch a response to an `INTERACTION_CREATE` event
|
|
@@ -117,7 +121,7 @@ class CommandDispatcher:
|
|
|
117
121
|
Args:
|
|
118
122
|
data (dict): interaction data
|
|
119
123
|
"""
|
|
120
|
-
event = InteractionEvent(
|
|
124
|
+
event = InteractionEvent(interaction=Interaction.from_dict(data, self._http))
|
|
121
125
|
|
|
122
126
|
event_data_obj = self.RESOURCE_MAP.get(event.interaction.type)
|
|
123
127
|
|
|
@@ -153,7 +157,7 @@ class CommandDispatcher:
|
|
|
153
157
|
return
|
|
154
158
|
|
|
155
159
|
try:
|
|
156
|
-
await handler(event) # NOTE: treat command options as args!
|
|
160
|
+
await handler(self.bot, event) # NOTE: treat command options as args!
|
|
157
161
|
self._logger.log_info(f"Interaction Event '{name}' Acknowledged.")
|
|
158
162
|
except Exception as e:
|
|
159
163
|
self._logger.log_error(f"Error in interaction '{name}': {e}")
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
from ..
|
|
2
|
-
from ..logger import Logger
|
|
3
|
-
from ..config import BaseConfig
|
|
1
|
+
from ..client_like import ClientLike
|
|
4
2
|
|
|
5
3
|
from ..events.ready_event import *
|
|
6
4
|
from ..events.reaction_events import *
|
|
@@ -41,17 +39,20 @@ class EventDispatcher:
|
|
|
41
39
|
}
|
|
42
40
|
"""Mapping of event names to respective dataclass."""
|
|
43
41
|
|
|
44
|
-
def __init__(self,
|
|
45
|
-
self.application_id = application_id
|
|
42
|
+
def __init__(self, client: ClientLike):
|
|
43
|
+
self.application_id = client.application_id
|
|
46
44
|
"""Bot's ID."""
|
|
47
45
|
|
|
48
|
-
self.
|
|
46
|
+
self.bot = client
|
|
47
|
+
"""Top-level discord client."""
|
|
48
|
+
|
|
49
|
+
self._http = client._http
|
|
49
50
|
"""HTTP session for requests."""
|
|
50
51
|
|
|
51
|
-
self._logger =
|
|
52
|
+
self._logger = client._logger
|
|
52
53
|
"""HTTP session for requests"""
|
|
53
54
|
|
|
54
|
-
self.config = config
|
|
55
|
+
self.config = client.config
|
|
55
56
|
"""User-defined bot config for persistent data."""
|
|
56
57
|
|
|
57
58
|
self._handlers = {}
|
|
@@ -86,5 +87,5 @@ class EventDispatcher:
|
|
|
86
87
|
if handler:
|
|
87
88
|
obj = cls.from_dict(data, self._http)
|
|
88
89
|
obj.config = self.config
|
|
89
|
-
await handler(obj)
|
|
90
|
+
await handler(self.bot, obj)
|
|
90
91
|
self._logger.log_info(f"Event {event_name} Acknowledged.")
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
from ..
|
|
2
|
-
from ..logger import Logger
|
|
3
|
-
from ..config import BaseConfig
|
|
1
|
+
from ..client_like import ClientLike
|
|
4
2
|
|
|
5
3
|
from ..events.message_events import MessageCreateEvent
|
|
6
4
|
|
|
@@ -9,17 +7,21 @@ from ..models.member import MemberModel
|
|
|
9
7
|
|
|
10
8
|
class PrefixDispatcher:
|
|
11
9
|
"""Handles text-based command messages that start with a specific prefix."""
|
|
12
|
-
def __init__(self,
|
|
13
|
-
|
|
10
|
+
def __init__(self, client: ClientLike, prefix: str):
|
|
11
|
+
|
|
12
|
+
self.bot = client
|
|
13
|
+
"""Bot session for user access to bot."""
|
|
14
|
+
|
|
15
|
+
self._http = client._http
|
|
14
16
|
"""HTTP session for requests."""
|
|
15
17
|
|
|
16
|
-
self._logger =
|
|
18
|
+
self._logger = client._logger
|
|
17
19
|
"""Logger instance to log events."""
|
|
18
20
|
|
|
19
21
|
self.prefix = prefix
|
|
20
22
|
"""User-defined command prefix."""
|
|
21
23
|
|
|
22
|
-
self.config = config
|
|
24
|
+
self.config = client.config
|
|
23
25
|
"""User-defined bot config for persistent data."""
|
|
24
26
|
|
|
25
27
|
self._handlers = {}
|
|
@@ -41,7 +43,6 @@ class PrefixDispatcher:
|
|
|
41
43
|
data (dict): Discord's raw event payload
|
|
42
44
|
"""
|
|
43
45
|
event = MessageCreateEvent(
|
|
44
|
-
config=self.config,
|
|
45
46
|
guild_id=data.get('guild_id'),
|
|
46
47
|
message=Message.from_dict(data, self._http),
|
|
47
48
|
member=MemberModel.from_dict(data.get('member'))
|
|
@@ -52,7 +53,7 @@ class PrefixDispatcher:
|
|
|
52
53
|
handler = self._handlers.get(command)
|
|
53
54
|
if handler:
|
|
54
55
|
try:
|
|
55
|
-
await handler(event, *args)
|
|
56
|
+
await handler(self.bot, event, *args)
|
|
56
57
|
self._logger.log_info(f"Prefix Event '{command}' Acknowledged.")
|
|
57
58
|
except Exception as e:
|
|
58
59
|
self._logger.log_error(f"Error in prefix command '{command}': {e}")
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Optional
|
|
3
|
-
from .event_model import EventModel
|
|
4
3
|
|
|
5
4
|
@dataclass
|
|
6
|
-
class GuildChannelEvent
|
|
5
|
+
class GuildChannelEvent:
|
|
7
6
|
"""Base guild channel event."""
|
|
8
7
|
|
|
9
8
|
id: int
|
|
@@ -46,7 +45,7 @@ class GuildChannelDeleteEvent(GuildChannelEvent):
|
|
|
46
45
|
pass
|
|
47
46
|
|
|
48
47
|
@dataclass
|
|
49
|
-
class ChannelPinsUpdateEvent
|
|
48
|
+
class ChannelPinsUpdateEvent:
|
|
50
49
|
"""Pin update event."""
|
|
51
50
|
channel_id: int
|
|
52
51
|
guild_id: Optional[int]
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from .event_model import EventModel
|
|
3
2
|
from ..models import MemberModel
|
|
4
3
|
from ..resources.channel import Channel
|
|
5
4
|
|
|
6
5
|
@dataclass
|
|
7
|
-
class GuildEvent
|
|
6
|
+
class GuildEvent:
|
|
8
7
|
"""Base guild event."""
|
|
9
8
|
joined_at: str
|
|
10
9
|
"""ISO8601 timestamp of when app joined the guild."""
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
2
|
from typing import Optional
|
|
3
3
|
from ..model import DataModel
|
|
4
|
-
from ..config import BaseConfig
|
|
5
4
|
|
|
6
5
|
from ..resources.interaction import Interaction
|
|
7
6
|
|
|
@@ -142,8 +141,5 @@ class InteractionEvent(DataModel):
|
|
|
142
141
|
interaction: Interaction
|
|
143
142
|
"""Interaction resource object. See [`Interaction`][discord.resources.interaction.Interaction]."""
|
|
144
143
|
|
|
145
|
-
config: BaseConfig
|
|
146
|
-
"""User-defined bot config for persistent data."""
|
|
147
|
-
|
|
148
144
|
data: Optional[ApplicationCommandData | MessageComponentData | ModalData] = None
|
|
149
145
|
"""Interaction response data."""
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Optional
|
|
3
|
-
from .event_model import EventModel
|
|
4
3
|
|
|
5
4
|
from ..resources.message import Message
|
|
6
5
|
from ..models.member import MemberModel
|
|
7
6
|
|
|
8
7
|
@dataclass
|
|
9
|
-
class MessageCreateEvent
|
|
8
|
+
class MessageCreateEvent:
|
|
10
9
|
"""Received when a message is created."""
|
|
11
10
|
message: Message
|
|
12
11
|
"""Message resource object. See [`Resource.Message`][discord.resources.message.Message]."""
|
|
@@ -18,7 +17,7 @@ class MessageCreateEvent(EventModel):
|
|
|
18
17
|
"""Partial Member object of the author of the message. See [`MemberModel`][discord.models.member.MemberModel]."""
|
|
19
18
|
|
|
20
19
|
@dataclass
|
|
21
|
-
class MessageUpdateEvent
|
|
20
|
+
class MessageUpdateEvent:
|
|
22
21
|
"""Received when a message is updated."""
|
|
23
22
|
message: Message
|
|
24
23
|
"""Message resource object. See [`Resource.Message`][discord.resources.message.Message]."""
|
|
@@ -30,7 +29,7 @@ class MessageUpdateEvent(EventModel):
|
|
|
30
29
|
"""Partial Member object of the author of the message. See [`MemberModel`][discord.models.member.MemberModel]."""
|
|
31
30
|
|
|
32
31
|
@dataclass
|
|
33
|
-
class MessageDeleteEvent
|
|
32
|
+
class MessageDeleteEvent:
|
|
34
33
|
"""Received when a message is deleted."""
|
|
35
34
|
|
|
36
35
|
id: int
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Optional
|
|
3
|
-
from .event_model import EventModel
|
|
4
3
|
|
|
5
4
|
from ..models.member import MemberModel
|
|
6
5
|
from ..models.emoji import EmojiModel
|
|
@@ -15,7 +14,7 @@ class ReactionType:
|
|
|
15
14
|
"""A super emoji."""
|
|
16
15
|
|
|
17
16
|
@dataclass
|
|
18
|
-
class ReactionAddEvent
|
|
17
|
+
class ReactionAddEvent:
|
|
19
18
|
"""Reaction added event."""
|
|
20
19
|
|
|
21
20
|
type: int
|
|
@@ -46,7 +45,7 @@ class ReactionAddEvent(EventModel):
|
|
|
46
45
|
"""ID of the user who sent the message where the reaction was added."""
|
|
47
46
|
|
|
48
47
|
@dataclass
|
|
49
|
-
class ReactionRemoveEvent
|
|
48
|
+
class ReactionRemoveEvent:
|
|
50
49
|
"""Reaction removed event."""
|
|
51
50
|
|
|
52
51
|
type: int
|
|
@@ -70,7 +69,7 @@ class ReactionRemoveEvent(EventModel):
|
|
|
70
69
|
burst: bool
|
|
71
70
|
"""If the emoji of the removed reaction is super."""
|
|
72
71
|
|
|
73
|
-
class ReactionRemoveAllEvent
|
|
72
|
+
class ReactionRemoveAllEvent:
|
|
74
73
|
"""Remove all reactions event."""
|
|
75
74
|
|
|
76
75
|
channel_id: int
|
|
@@ -83,7 +82,7 @@ class ReactionRemoveAllEvent(EventModel):
|
|
|
83
82
|
"""ID of the guild where all reaction were removed (if in a guild)."""
|
|
84
83
|
|
|
85
84
|
@dataclass
|
|
86
|
-
class ReactionRemoveEmojiEvent
|
|
85
|
+
class ReactionRemoveEmojiEvent:
|
|
87
86
|
"""All reactions of a specific emoji removed."""
|
|
88
87
|
|
|
89
88
|
emoji: EmojiModel
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from ..model import DataModel
|
|
3
|
-
from ..config import BaseConfig
|
|
4
3
|
|
|
5
4
|
from ..models.user import UserModel
|
|
6
5
|
from ..models.guild import ReadyGuildModel
|
|
@@ -30,6 +29,3 @@ class ReadyEvent(DataModel):
|
|
|
30
29
|
|
|
31
30
|
application: ApplicationModel
|
|
32
31
|
"""Partial application object. Contains ID and flags."""
|
|
33
|
-
|
|
34
|
-
config: BaseConfig
|
|
35
|
-
"""User-defined bot config for persistent data."""
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from ..model import DataModel
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class InteractionCallbackDataModel(DataModel):
|
|
6
|
+
id: int
|
|
7
|
+
"""ID of the interaction."""
|
|
8
|
+
|
|
9
|
+
type: int
|
|
10
|
+
"""Type of interaction."""
|
|
11
|
+
|
|
12
|
+
activity_instance_id: str
|
|
13
|
+
"""Instance ID of activity if an activity was launched or joined."""
|
|
14
|
+
|
|
15
|
+
response_message_id: int
|
|
16
|
+
"""ID of the message created by the interaction."""
|
|
17
|
+
|
|
18
|
+
response_message_loading: bool
|
|
19
|
+
"""If the interaction is in a loading state."""
|
|
20
|
+
|
|
21
|
+
response_message_ephemeral: bool
|
|
22
|
+
"""If the interaction is ephemeral."""
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class InteractionCallbackModel(DataModel):
|
|
26
|
+
interaction: InteractionCallbackDataModel
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import TypedDict, Unpack, Optional
|
|
2
|
+
from typing import TypedDict, Unpack, Optional, Literal
|
|
3
3
|
|
|
4
4
|
from ..http import HTTPClient
|
|
5
5
|
from ..model import DataModel
|
|
@@ -32,6 +32,15 @@ class PinsFetchParams(TypedDict, total=False):
|
|
|
32
32
|
limit: int
|
|
33
33
|
"""Max number of pinned messages to return. Range 1 - 50. Default 50."""
|
|
34
34
|
|
|
35
|
+
class ThreadFromMessageParams(TypedDict, total=False):
|
|
36
|
+
"""Params when attaching a thread to a message."""
|
|
37
|
+
|
|
38
|
+
rate_limit_per_user: Literal[60, 1440, 4320, 10080]
|
|
39
|
+
"""time (minutes) of inactivity before thread is archived."""
|
|
40
|
+
|
|
41
|
+
rate_limit_per_user: int
|
|
42
|
+
"""time (seconds) user waits before sending another message."""
|
|
43
|
+
|
|
35
44
|
@dataclass
|
|
36
45
|
class PinnedMessage(DataModel):
|
|
37
46
|
"""Pinned message data."""
|
|
@@ -160,6 +169,26 @@ class Channel(DataModel):
|
|
|
160
169
|
|
|
161
170
|
return self
|
|
162
171
|
|
|
172
|
+
async def create_thread_from_message(self, message_id: int, name: str, **kwargs: Unpack[ThreadFromMessageParams]):
|
|
173
|
+
"""Create a thread from this message
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
message_id: ID of message to attach thread
|
|
177
|
+
name (str): thread name
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
(Channel): The updated channel object
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
content = {
|
|
184
|
+
'name': name,
|
|
185
|
+
**kwargs
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
data = await self._http.request('POST', f"channels/{self.id}/messages/{message_id}/threads", data=content)
|
|
189
|
+
|
|
190
|
+
return Channel.from_dict(data, self._http)
|
|
191
|
+
|
|
163
192
|
async def fetch_pins(self, **kwargs: Unpack[PinsFetchParams]):
|
|
164
193
|
"""Get this channel's pinned messages.
|
|
165
194
|
|
|
@@ -11,6 +11,7 @@ from ..parts.component_types import *
|
|
|
11
11
|
|
|
12
12
|
from ..models.guild import GuildModel
|
|
13
13
|
from ..models.member import MemberModel
|
|
14
|
+
from ..models.interaction import InteractionCallbackModel
|
|
14
15
|
|
|
15
16
|
from .channel import Channel
|
|
16
17
|
|
|
@@ -98,11 +99,12 @@ class Interaction(DataModel):
|
|
|
98
99
|
channel: Optional[Channel] = None
|
|
99
100
|
"""Partial channel object the interaction was invoked."""
|
|
100
101
|
|
|
101
|
-
async def respond(self, message: str | MessageBuilder, **flags: Unpack[MessageFlagParams]):
|
|
102
|
+
async def respond(self, message: str | MessageBuilder, with_response: bool = False, **flags: Unpack[MessageFlagParams]):
|
|
102
103
|
"""Create a message in response to an interaction.
|
|
103
104
|
|
|
104
105
|
Args:
|
|
105
106
|
message (str | MessageBuilder): content as a string or from MessageBuilder
|
|
107
|
+
with_response (bool, optional): if the interaction data should be returned. Defaults to False.
|
|
106
108
|
"""
|
|
107
109
|
if isinstance(message, str):
|
|
108
110
|
message = MessageBuilder(content=message).set_flags(**flags)
|
|
@@ -112,12 +114,18 @@ class Interaction(DataModel):
|
|
|
112
114
|
'data': message._to_dict()
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
|
|
117
|
+
params = {'with_response': with_response}
|
|
118
|
+
|
|
119
|
+
data = await self._http.request(
|
|
116
120
|
'POST',
|
|
117
121
|
f'/interactions/{self.id}/{self.token}/callback',
|
|
118
122
|
content,
|
|
119
|
-
files=[fp.path for fp in message.attachments]
|
|
123
|
+
files=[fp.path for fp in message.attachments],
|
|
124
|
+
params=params)
|
|
120
125
|
|
|
126
|
+
if with_response:
|
|
127
|
+
return InteractionCallbackModel.from_dict(data, self._http)
|
|
128
|
+
|
|
121
129
|
async def update(self, message: str | MessageBuilder, **flags: Unpack[MessageFlagParams]):
|
|
122
130
|
"""Update a message in response to an interaction.
|
|
123
131
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scurrypy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Discord API Wrapper in Python
|
|
5
5
|
Author: Furmissile
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -46,14 +46,14 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
|
|
|
46
46
|
|
|
47
47
|
bot = discord.Client(
|
|
48
48
|
token=os.getenv("DISCORD_TOKEN"),
|
|
49
|
-
application_id=
|
|
49
|
+
application_id=APPLICATION_ID # replace with your bot's user ID
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
@bot.command(
|
|
53
53
|
command=discord.SlashCommand(name='example', description='Demonstrate the minimal slash command!'),
|
|
54
54
|
guild_id=GUILD_ID # must be a guild ID your bot is in!
|
|
55
55
|
)
|
|
56
|
-
async def example(event: discord.InteractionEvent):
|
|
56
|
+
async def example(bot: discord.Client, event: discord.InteractionEvent):
|
|
57
57
|
await event.interaction.respond(f'Hello, {event.interaction.member.user.username}!')
|
|
58
58
|
|
|
59
59
|
bot.run()
|
|
@@ -69,13 +69,13 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
|
|
|
69
69
|
|
|
70
70
|
bot = discord.Client(
|
|
71
71
|
token=os.getenv("DISCORD_TOKEN"),
|
|
72
|
-
application_id=
|
|
72
|
+
application_id=APPLICATION_ID, # replace with your bot's user ID
|
|
73
73
|
intents=discord.set_intents(message_content=True),
|
|
74
74
|
prefix='!' # your custom prefix
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
@bot.prefix_command
|
|
78
|
-
async def ping(event: discord.MessageCreateEvent): # the function name is the name of the command!
|
|
78
|
+
async def ping(bot: discord.Client, event: discord.MessageCreateEvent): # the function name is the name of the command!
|
|
79
79
|
await event.message.send(f"Pong!")
|
|
80
80
|
|
|
81
81
|
bot.run()
|
|
@@ -3,6 +3,7 @@ README.md
|
|
|
3
3
|
pyproject.toml
|
|
4
4
|
discord/__init__.py
|
|
5
5
|
discord/client.py
|
|
6
|
+
discord/client_like.py
|
|
6
7
|
discord/config.py
|
|
7
8
|
discord/error.py
|
|
8
9
|
discord/gateway.py
|
|
@@ -16,7 +17,6 @@ discord/dispatch/event_dispatcher.py
|
|
|
16
17
|
discord/dispatch/prefix_dispatcher.py
|
|
17
18
|
discord/events/__init__.py
|
|
18
19
|
discord/events/channel_events.py
|
|
19
|
-
discord/events/event_model.py
|
|
20
20
|
discord/events/guild_events.py
|
|
21
21
|
discord/events/hello_event.py
|
|
22
22
|
discord/events/interaction_events.py
|
|
@@ -28,6 +28,7 @@ discord/models/application.py
|
|
|
28
28
|
discord/models/emoji.py
|
|
29
29
|
discord/models/guild.py
|
|
30
30
|
discord/models/integration.py
|
|
31
|
+
discord/models/interaction.py
|
|
31
32
|
discord/models/member.py
|
|
32
33
|
discord/models/role.py
|
|
33
34
|
discord/models/user.py
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from ..model import DataModel
|
|
3
|
-
from ..config import BaseConfig
|
|
4
|
-
|
|
5
|
-
@dataclass
|
|
6
|
-
class EventModel(DataModel):
|
|
7
|
-
"""Event Model for event-driven fields common among all events."""
|
|
8
|
-
|
|
9
|
-
config: BaseConfig
|
|
10
|
-
"""User-defined bot config for persistent data."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|