scurrypy 0.3.2__py3-none-any.whl → 0.3.4.1__py3-none-any.whl
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.
- discord/__init__.py +221 -8
- discord/client.py +129 -115
- discord/dispatch/event_dispatcher.py +32 -27
- discord/events/__init__.py +0 -32
- discord/events/channel_events.py +7 -1
- discord/events/guild_events.py +4 -2
- discord/events/message_events.py +4 -3
- discord/events/reaction_events.py +5 -4
- discord/gateway.py +37 -57
- discord/http.py +4 -16
- discord/logger.py +4 -3
- discord/models/__init__.py +0 -7
- discord/parts/__init__.py +0 -26
- discord/parts/message.py +2 -1
- discord/resources/__init__.py +0 -9
- discord/resources/interaction.py +0 -2
- scurrypy-0.3.4.1.dist-info/METADATA +92 -0
- {scurrypy-0.3.2.dist-info → scurrypy-0.3.4.1.dist-info}/RECORD +21 -21
- scurrypy-0.3.2.dist-info/METADATA +0 -85
- {scurrypy-0.3.2.dist-info → scurrypy-0.3.4.1.dist-info}/WHEEL +0 -0
- {scurrypy-0.3.2.dist-info → scurrypy-0.3.4.1.dist-info}/licenses/LICENSE +0 -0
- {scurrypy-0.3.2.dist-info → scurrypy-0.3.4.1.dist-info}/top_level.txt +0 -0
|
@@ -1,42 +1,35 @@
|
|
|
1
|
+
import importlib
|
|
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
8
|
|
|
26
|
-
|
|
27
|
-
'
|
|
28
|
-
'CHANNEL_DELETE': GuildChannelDeleteEvent,
|
|
9
|
+
RESOURCE_MAP = { # maps discord events to their respective dataclass (lazy loading)
|
|
10
|
+
'READY': ('discord.events.ready_event', 'ReadyEvent'),
|
|
29
11
|
|
|
30
|
-
'
|
|
12
|
+
'GUILD_CREATE': ('discord.events.guild_events', 'GuildCreateEvent'),
|
|
13
|
+
'GUILD_UPDATE': ('discord.events.guild_events', 'GuildUpdateEvent'),
|
|
14
|
+
'GUILD_DELETE': ('discord.events.guild_events', 'GuildDeleteEvent'),
|
|
31
15
|
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
16
|
+
'CHANNEL_CREATE': ('discord.events.channel_events', 'GuildChannelCreateEvent'),
|
|
17
|
+
'CHANNEL_UPDATE': ('discord.events.channel_events', 'GuildChannelUpdateEvent'),
|
|
18
|
+
'CHANNEL_DELETE': ('discord.events.channel_events', 'GuildChannelDeleteEvent'),
|
|
19
|
+
'CHANNEL_PINS_UPDATE': ('discord.events.channel_events', 'ChannelPinsUpdateEvent'),
|
|
35
20
|
|
|
36
|
-
'
|
|
21
|
+
'MESSAGE_CREATE': ('discord.events.message_events', 'MessageCreateEvent'),
|
|
22
|
+
'MESSAGE_UPDATE': ('discord.events.message_events', 'MessageUpdateEvent'),
|
|
23
|
+
'MESSAGE_DELETE': ('discord.events.message_events', 'MessageDeleteEvent'),
|
|
24
|
+
|
|
25
|
+
'MESSAGE_REACTION_ADD': ('discord.events.reaction_events', 'ReactionAddEvent'),
|
|
26
|
+
'MESSAGE_REACTION_REMOVE': ('discord.events.reaction_events', 'ReactionRemoveEvent'),
|
|
27
|
+
'MESSAGE_REACTION_REMOVE_ALL': ('discord.events.reaction_events', 'ReactionRemoveAllEvent'),
|
|
28
|
+
'MESSAGE_REACTION_REMOVE_EMOJI': ('discord.events.reaction_events', 'ReactionRemoveEmojiEvent'),
|
|
37
29
|
|
|
38
30
|
# and other events...
|
|
39
31
|
}
|
|
32
|
+
|
|
40
33
|
"""Mapping of event names to respective dataclass."""
|
|
41
34
|
|
|
42
35
|
def __init__(self, client: ClientLike):
|
|
@@ -75,9 +68,21 @@ class EventDispatcher:
|
|
|
75
68
|
event_name (str): name of the event
|
|
76
69
|
data (dict): Discord's raw event payload
|
|
77
70
|
"""
|
|
78
|
-
|
|
71
|
+
module_info = self.RESOURCE_MAP.get(event_name)
|
|
79
72
|
|
|
73
|
+
if not module_info:
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
module_name, class_name = module_info
|
|
77
|
+
|
|
78
|
+
module = importlib.import_module(module_name)
|
|
79
|
+
if not module:
|
|
80
|
+
print(f"Cannot find module '{module_name}'!")
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
cls = getattr(module, class_name)
|
|
80
84
|
if not cls:
|
|
85
|
+
print(f"Cannot find class '{class_name}'!")
|
|
81
86
|
return
|
|
82
87
|
|
|
83
88
|
if isinstance(cls, Message) and cls.author.id == self.application_id:
|
discord/events/__init__.py
CHANGED
|
@@ -1,33 +1 @@
|
|
|
1
1
|
# discord/events
|
|
2
|
-
|
|
3
|
-
from .ready_event import ReadyEvent
|
|
4
|
-
|
|
5
|
-
from .reaction_events import (
|
|
6
|
-
ReactionAddEvent,
|
|
7
|
-
ReactionRemoveEvent,
|
|
8
|
-
ReactionRemoveEmojiEvent,
|
|
9
|
-
ReactionRemoveAllEvent
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
from .guild_events import (
|
|
13
|
-
GuildCreateEvent,
|
|
14
|
-
GuildUpdateEvent,
|
|
15
|
-
GuildDeleteEvent
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
from .message_events import (
|
|
19
|
-
MessageCreateEvent,
|
|
20
|
-
MessageUpdateEvent,
|
|
21
|
-
MessageDeleteEvent
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
from .channel_events import (
|
|
25
|
-
GuildChannelCreateEvent,
|
|
26
|
-
GuildChannelUpdateEvent,
|
|
27
|
-
GuildChannelDeleteEvent,
|
|
28
|
-
ChannelPinsUpdateEvent
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
from .interaction_events import (
|
|
32
|
-
InteractionEvent
|
|
33
|
-
)
|
discord/events/channel_events.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
+
from ..model import DataModel
|
|
2
3
|
from typing import Optional
|
|
3
4
|
|
|
4
5
|
@dataclass
|
|
5
|
-
class GuildChannelEvent:
|
|
6
|
+
class GuildChannelEvent(DataModel):
|
|
6
7
|
"""Base guild channel event."""
|
|
7
8
|
|
|
8
9
|
id: int
|
|
@@ -48,5 +49,10 @@ class GuildChannelDeleteEvent(GuildChannelEvent):
|
|
|
48
49
|
class ChannelPinsUpdateEvent:
|
|
49
50
|
"""Pin update event."""
|
|
50
51
|
channel_id: int
|
|
52
|
+
"""ID of channel where the pins were updated."""
|
|
53
|
+
|
|
51
54
|
guild_id: Optional[int]
|
|
55
|
+
"""ID of the guild where the pins were updated."""
|
|
56
|
+
|
|
52
57
|
last_pin_timestamp: Optional[str] # ISO8601 timestamp of last pinned message
|
|
58
|
+
"""ISO8601 formatted timestamp of the last pinned message in the channel."""
|
discord/events/guild_events.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from ..
|
|
2
|
+
from ..model import DataModel
|
|
3
|
+
|
|
4
|
+
from ..models.member import MemberModel
|
|
3
5
|
from ..resources.channel import Channel
|
|
4
6
|
|
|
5
7
|
@dataclass
|
|
6
|
-
class GuildEvent:
|
|
8
|
+
class GuildEvent(DataModel):
|
|
7
9
|
"""Base guild event."""
|
|
8
10
|
joined_at: str
|
|
9
11
|
"""ISO8601 timestamp of when app joined the guild."""
|
discord/events/message_events.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Optional
|
|
3
|
+
from ..model import DataModel
|
|
3
4
|
|
|
4
5
|
from ..resources.message import Message
|
|
5
6
|
from ..models.member import MemberModel
|
|
6
7
|
|
|
7
8
|
@dataclass
|
|
8
|
-
class MessageCreateEvent:
|
|
9
|
+
class MessageCreateEvent(DataModel):
|
|
9
10
|
"""Received when a message is created."""
|
|
10
11
|
message: Message
|
|
11
12
|
"""Message resource object. See [`Resource.Message`][discord.resources.message.Message]."""
|
|
@@ -17,7 +18,7 @@ class MessageCreateEvent:
|
|
|
17
18
|
"""Partial Member object of the author of the message. See [`MemberModel`][discord.models.member.MemberModel]."""
|
|
18
19
|
|
|
19
20
|
@dataclass
|
|
20
|
-
class MessageUpdateEvent:
|
|
21
|
+
class MessageUpdateEvent(DataModel):
|
|
21
22
|
"""Received when a message is updated."""
|
|
22
23
|
message: Message
|
|
23
24
|
"""Message resource object. See [`Resource.Message`][discord.resources.message.Message]."""
|
|
@@ -29,7 +30,7 @@ class MessageUpdateEvent:
|
|
|
29
30
|
"""Partial Member object of the author of the message. See [`MemberModel`][discord.models.member.MemberModel]."""
|
|
30
31
|
|
|
31
32
|
@dataclass
|
|
32
|
-
class MessageDeleteEvent:
|
|
33
|
+
class MessageDeleteEvent(DataModel):
|
|
33
34
|
"""Received when a message is deleted."""
|
|
34
35
|
|
|
35
36
|
id: int
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Optional
|
|
3
|
+
from ..model import DataModel
|
|
3
4
|
|
|
4
5
|
from ..models.member import MemberModel
|
|
5
6
|
from ..models.emoji import EmojiModel
|
|
@@ -14,7 +15,7 @@ class ReactionType:
|
|
|
14
15
|
"""A super emoji."""
|
|
15
16
|
|
|
16
17
|
@dataclass
|
|
17
|
-
class ReactionAddEvent:
|
|
18
|
+
class ReactionAddEvent(DataModel):
|
|
18
19
|
"""Reaction added event."""
|
|
19
20
|
|
|
20
21
|
type: int
|
|
@@ -45,7 +46,7 @@ class ReactionAddEvent:
|
|
|
45
46
|
"""ID of the user who sent the message where the reaction was added."""
|
|
46
47
|
|
|
47
48
|
@dataclass
|
|
48
|
-
class ReactionRemoveEvent:
|
|
49
|
+
class ReactionRemoveEvent(DataModel):
|
|
49
50
|
"""Reaction removed event."""
|
|
50
51
|
|
|
51
52
|
type: int
|
|
@@ -69,7 +70,7 @@ class ReactionRemoveEvent:
|
|
|
69
70
|
burst: bool
|
|
70
71
|
"""If the emoji of the removed reaction is super."""
|
|
71
72
|
|
|
72
|
-
class ReactionRemoveAllEvent:
|
|
73
|
+
class ReactionRemoveAllEvent(DataModel):
|
|
73
74
|
"""Remove all reactions event."""
|
|
74
75
|
|
|
75
76
|
channel_id: int
|
|
@@ -82,7 +83,7 @@ class ReactionRemoveAllEvent:
|
|
|
82
83
|
"""ID of the guild where all reaction were removed (if in a guild)."""
|
|
83
84
|
|
|
84
85
|
@dataclass
|
|
85
|
-
class ReactionRemoveEmojiEvent:
|
|
86
|
+
class ReactionRemoveEmojiEvent(DataModel):
|
|
86
87
|
"""All reactions of a specific emoji removed."""
|
|
87
88
|
|
|
88
89
|
emoji: EmojiModel
|
discord/gateway.py
CHANGED
|
@@ -53,57 +53,46 @@ class GatewayClient:
|
|
|
53
53
|
|
|
54
54
|
async def connect(self):
|
|
55
55
|
"""Established websocket connection to Discord."""
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
except Exception as e:
|
|
60
|
-
self._logger.log_error(f"Websocket Connection Error {type(e).__name__} - {e}")
|
|
61
|
-
|
|
56
|
+
self.ws = await websockets.connect(self.connect_url + self.url_params)
|
|
57
|
+
self._logger.log_high_priority("Connected to Discord.")
|
|
58
|
+
|
|
62
59
|
async def receive(self):
|
|
63
60
|
"""Receives and logs messages from the gateway.
|
|
64
61
|
|
|
65
62
|
Returns:
|
|
66
63
|
(dict): parsed JSON data
|
|
67
64
|
"""
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
65
|
+
|
|
66
|
+
message = await asyncio.wait_for(self.ws.recv(), timeout=60)
|
|
67
|
+
|
|
68
|
+
if message:
|
|
69
|
+
data: dict = json.loads(message)
|
|
70
|
+
self._logger.log_debug(f"Received: {DISCORD_OP_CODES.get(data.get('op'))} - {json.dumps(data, indent=4)}")
|
|
71
|
+
self._logger.log_info(f"Received: {DISCORD_OP_CODES.get(data.get('op'))}")
|
|
72
|
+
return data
|
|
73
|
+
|
|
74
|
+
return None
|
|
75
|
+
|
|
80
76
|
async def send(self, message: dict):
|
|
81
77
|
"""Sends a JSON-encoded message to the gateway.
|
|
82
78
|
|
|
83
79
|
Args:
|
|
84
80
|
message (dict): the message to send
|
|
85
81
|
"""
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
self._logger.log_error(f"Error on send: {type(e).__name__} - {e}")
|
|
90
|
-
|
|
82
|
+
self._logger.log_debug(f"Sending payload: {message}")
|
|
83
|
+
await self.ws.send(json.dumps(message))
|
|
84
|
+
|
|
91
85
|
async def send_heartbeat_loop(self):
|
|
92
86
|
"""Background task that sends heartbeat payloads in regular intervals.
|
|
93
87
|
Retries until cancelled.
|
|
94
88
|
"""
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
except asyncio.CancelledError:
|
|
103
|
-
self._logger.log_debug("Heartbeat task cancelled")
|
|
104
|
-
except Exception as e:
|
|
105
|
-
self._logger.log_error(f"Error on heartbeat send: {type(e).__name__} - {e}")
|
|
106
|
-
|
|
89
|
+
while self.ws:
|
|
90
|
+
await asyncio.sleep(self.heartbeat_interval / 1000)
|
|
91
|
+
hb_data = {"op": 1, "d": self.sequence}
|
|
92
|
+
await self.send(hb_data)
|
|
93
|
+
self._logger.log_debug(f"Sending: {hb_data}")
|
|
94
|
+
self._logger.log_info("Heartbeat sent.")
|
|
95
|
+
|
|
107
96
|
async def identify(self):
|
|
108
97
|
"""Sends the IDENIFY payload (token, intents, connection properties).
|
|
109
98
|
Must be sent after connecting to the WS.
|
|
@@ -127,15 +116,12 @@ class GatewayClient:
|
|
|
127
116
|
|
|
128
117
|
async def start_heartbeat(self):
|
|
129
118
|
"""Waits for initial HELLO event, hydrates the HelloEvent class, and begins the heartbeat."""
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
except Exception as e:
|
|
137
|
-
self._logger.log_error(f"Heartbeat Task Error: {type(e).__name__} - {e}")
|
|
138
|
-
|
|
119
|
+
data = await self.receive()
|
|
120
|
+
hello = HelloEvent.from_dict(data.get('d'))
|
|
121
|
+
self.heartbeat_interval = hello.heartbeat_interval
|
|
122
|
+
self.heartbeat = asyncio.create_task(self.send_heartbeat_loop())
|
|
123
|
+
self._logger.log_high_priority("Heartbeat started.")
|
|
124
|
+
|
|
139
125
|
async def reconnect(self):
|
|
140
126
|
"""Sends RESUME payload to reconnect with the same session ID and sequence number
|
|
141
127
|
as provided by Discord.
|
|
@@ -156,20 +142,14 @@ class GatewayClient:
|
|
|
156
142
|
if self.heartbeat:
|
|
157
143
|
self._logger.log_high_priority(f"Cancelling heartbeat...")
|
|
158
144
|
self.heartbeat.cancel()
|
|
159
|
-
|
|
160
|
-
await asyncio.wait_for(self.heartbeat, timeout=3) # Add timeout to avoid hanging
|
|
161
|
-
except asyncio.CancelledError:
|
|
162
|
-
self._logger.log_debug("Heartbeat cancelled by CancelledError.")
|
|
163
|
-
except asyncio.TimeoutError:
|
|
164
|
-
self._logger.log_error("Heartbeat cancel timed out.")
|
|
165
|
-
except Exception as e:
|
|
166
|
-
self._logger.log_error(f"Unexpected error cancelling heartbeat: {type(e).__name__} - {e}")
|
|
145
|
+
await asyncio.wait_for(self.heartbeat, timeout=3) # Add timeout to avoid hanging
|
|
167
146
|
self.heartbeat = None
|
|
168
147
|
|
|
169
148
|
if self.ws:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
self._logger.log_high_priority("WebSocket closed.")
|
|
173
|
-
except Exception as e:
|
|
174
|
-
self._logger.log_error(f"Error while closing websocket: {type(e).__name__} - {e}")
|
|
149
|
+
await self.ws.close()
|
|
150
|
+
self._logger.log_high_priority("WebSocket closed.")
|
|
175
151
|
self.ws = None
|
|
152
|
+
|
|
153
|
+
def is_connected(self):
|
|
154
|
+
"""Helper function to tell whether the websocket is still active."""
|
|
155
|
+
return self.ws is not None
|
discord/http.py
CHANGED
|
@@ -269,24 +269,12 @@ class HTTPClient:
|
|
|
269
269
|
if now < self.global_reset:
|
|
270
270
|
await asyncio.sleep(self.global_reset - now)
|
|
271
271
|
|
|
272
|
-
async def close_session(self):
|
|
273
|
-
"""Gracefully shuts down all workes and closes aiohttp session."""
|
|
274
|
-
# Stop
|
|
272
|
+
async def close_session(self):
|
|
273
|
+
"""Gracefully shuts down all workes and closes aiohttp session."""
|
|
274
|
+
# Stop workers
|
|
275
275
|
for q in self.bucket_queues.values():
|
|
276
276
|
await q.queue.put(self._sentinel)
|
|
277
|
-
|
|
278
|
-
# Stop pending worker
|
|
279
277
|
await self.pending_queue.put(self._sentinel)
|
|
280
|
-
|
|
281
|
-
# Cancel all tasks except the current one
|
|
282
|
-
tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
|
|
283
|
-
for task in tasks:
|
|
284
|
-
task.cancel()
|
|
285
|
-
|
|
286
|
-
# Wait for tasks to acknowledge cancellation
|
|
287
|
-
await asyncio.gather(*tasks, return_exceptions=True)
|
|
288
|
-
|
|
289
|
-
# Close the aiohttp session
|
|
278
|
+
|
|
290
279
|
if self.session and not self.session.closed:
|
|
291
280
|
await self.session.close()
|
|
292
|
-
self._logger.log_debug("Session closed successfully.")
|
discord/logger.py
CHANGED
|
@@ -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):
|
discord/models/__init__.py
CHANGED
discord/parts/__init__.py
CHANGED
|
@@ -1,28 +1,2 @@
|
|
|
1
1
|
# discord/parts
|
|
2
2
|
|
|
3
|
-
from .channel import GuildChannel
|
|
4
|
-
from .role import Role
|
|
5
|
-
from .message import MessageBuilder
|
|
6
|
-
from .modal import ModalBuilder
|
|
7
|
-
from .embed import EmbedBuilder
|
|
8
|
-
from .action_row import (
|
|
9
|
-
ActionRow,
|
|
10
|
-
StringSelect,
|
|
11
|
-
UserSelect,
|
|
12
|
-
RoleSelect,
|
|
13
|
-
ChannelSelect,
|
|
14
|
-
MentionableSelect
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
from .command import (
|
|
18
|
-
SlashCommand,
|
|
19
|
-
MessageCommand,
|
|
20
|
-
UserCommand
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
from .components_v2 import (
|
|
24
|
-
Container,
|
|
25
|
-
Section,
|
|
26
|
-
MediaGalleryItem,
|
|
27
|
-
Label
|
|
28
|
-
)
|
discord/parts/message.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import os
|
|
2
1
|
from dataclasses import dataclass, field
|
|
3
2
|
from typing import Optional, TypedDict, Unpack, Literal
|
|
4
3
|
from discord.model import DataModel
|
|
@@ -114,6 +113,8 @@ class MessageBuilder(DataModel):
|
|
|
114
113
|
Returns:
|
|
115
114
|
(MessageBuilder): self
|
|
116
115
|
"""
|
|
116
|
+
import os
|
|
117
|
+
|
|
117
118
|
self.attachments.append(
|
|
118
119
|
_Attachment(
|
|
119
120
|
id=len(self.attachments),
|
discord/resources/__init__.py
CHANGED
|
@@ -1,10 +1 @@
|
|
|
1
1
|
# discord/resources
|
|
2
|
-
|
|
3
|
-
from .guild import Guild
|
|
4
|
-
from .channel import Channel
|
|
5
|
-
from .message import Message
|
|
6
|
-
from .bot_emojis import BotEmojis
|
|
7
|
-
from .user import User
|
|
8
|
-
from .interaction import Interaction
|
|
9
|
-
from .application import Application
|
|
10
|
-
from .interaction import Interaction
|
discord/resources/interaction.py
CHANGED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scurrypy
|
|
3
|
+
Version: 0.3.4.1
|
|
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
|
+
[](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!
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
discord/__init__.py,sha256=
|
|
2
|
-
discord/client.py,sha256=
|
|
1
|
+
discord/__init__.py,sha256=cETkxHmm0s9YkSJgn-1daQhnbL96fuD7L9SIg2t5vBg,6823
|
|
2
|
+
discord/client.py,sha256=LCK6dzQxt6YEy_JiMNpvaJ0WQPRODS_YVvy1SEPeXt8,13582
|
|
3
3
|
discord/client_like.py,sha256=JyJq0XBq0vKuPBJ_ZnYf5yAAuX1zz_2B1TZBQE-BYbQ,473
|
|
4
4
|
discord/config.py,sha256=OH1A2mNKhDlGvQYASEsVUx2pNxP1YQ2a7a7z-IM5xFg,200
|
|
5
5
|
discord/error.py,sha256=AlislRTna554cM6KC0KrwKugzYDYtx_9C8_3QFe4XDc,2070
|
|
6
|
-
discord/gateway.py,sha256=
|
|
7
|
-
discord/http.py,sha256=
|
|
6
|
+
discord/gateway.py,sha256=1TVUKrd3JovoM4df5-GlMZ0kz15Xls5V48ShXDSlK3Q,5334
|
|
7
|
+
discord/http.py,sha256=BI7bCjedh8zuu5pdOLhI-XwMZfdVj5qP9ZZY6WJqtgo,10511
|
|
8
8
|
discord/intents.py,sha256=Lf2fogFFDqilZeKJv7tcUgKmMW3D7ykK4bBNi-zDzYA,2866
|
|
9
|
-
discord/logger.py,sha256=
|
|
9
|
+
discord/logger.py,sha256=qAmOc3geCcCCqPhdi61SVWzMDewmM8Q_KWhTcjO46j8,4726
|
|
10
10
|
discord/model.py,sha256=CmuxyoWLWokE_UvCQ9M7U9Cr7JH9R7ULMv9KMwzXjDQ,3105
|
|
11
11
|
discord/dispatch/__init__.py,sha256=m7ixrbNhOV9QRORXPw6LSwxofQMAvLmPFBweBZu9ACc,20
|
|
12
12
|
discord/dispatch/command_dispatcher.py,sha256=pyJOQaZLZYrHUEs6HEWp8XMKTMZX4SBrwTizGKIeUG8,5904
|
|
13
|
-
discord/dispatch/event_dispatcher.py,sha256=
|
|
13
|
+
discord/dispatch/event_dispatcher.py,sha256=1A7Qof_IzTi5_14IMPxIQDpvo3-Sj-X0KZWOuVGH53k,3764
|
|
14
14
|
discord/dispatch/prefix_dispatcher.py,sha256=4mkn3cuXTjdEChbewkbZQqd_sMKm4jePSFKKOPbt12g,2065
|
|
15
|
-
discord/events/__init__.py,sha256=
|
|
16
|
-
discord/events/channel_events.py,sha256=
|
|
17
|
-
discord/events/guild_events.py,sha256=
|
|
15
|
+
discord/events/__init__.py,sha256=xE8YtJ7NKZkm7MLnohDQIbezh3ColmLR-3BMiZabt3k,18
|
|
16
|
+
discord/events/channel_events.py,sha256=t9UL4JjDqulAP_XepQ8MRMW54pNRqCbIK3M8tauzf9I,1556
|
|
17
|
+
discord/events/guild_events.py,sha256=Ok9tW3tjcwtbiqJgbe-42d9-R3-2RzqmIgBHEP-2Pcc,896
|
|
18
18
|
discord/events/hello_event.py,sha256=O8Ketu_N943cnGaFkGsAHfWhgKXFQCYCqSD3EqdsXjA,225
|
|
19
19
|
discord/events/interaction_events.py,sha256=5hlYOsV1ROiRXISAGCKcZo8vfk6oqiZcNzzZjSteiag,4361
|
|
20
|
-
discord/events/message_events.py,sha256=
|
|
21
|
-
discord/events/reaction_events.py,sha256=
|
|
20
|
+
discord/events/message_events.py,sha256=M5xdaJH1zRzdZk0oN0Jykaeu9k09EjgZjeiIT_EkL1A,1475
|
|
21
|
+
discord/events/reaction_events.py,sha256=xx7GD-fqakhJmS-X-HbuAUg9pg6Gqo_KRtLTdPJu7UE,2643
|
|
22
22
|
discord/events/ready_event.py,sha256=c3Pf4ndNYV2byuliADi8pUxpuvKXa9FLKNz_uzIWGso,794
|
|
23
|
-
discord/models/__init__.py,sha256=
|
|
23
|
+
discord/models/__init__.py,sha256=ZKhFO5eX4GbTRdvi4CU4z2hO-HQU29WZw2x4DujvARY,18
|
|
24
24
|
discord/models/application.py,sha256=2sXtRysUc2TJ40FjdcrWgosmwMrp_h3ybddubQMixKM,924
|
|
25
25
|
discord/models/emoji.py,sha256=6iz1DhWj_eTUj2KHmwMewjB3AdEBm68EmIZp2WFFCQg,932
|
|
26
26
|
discord/models/guild.py,sha256=aXUByOUUIGt9d2qIGC6_X_vh0Nyib8Iqj5ZElBeNV_I,819
|
|
@@ -29,7 +29,7 @@ discord/models/interaction.py,sha256=VPbf49C1RmQpDSODk3e1voW8EnbVsH_w1qpmiq4hVRM
|
|
|
29
29
|
discord/models/member.py,sha256=pkI-NVRMb3hUBkxI26FSYZxzx2mRNGXOeWWCw3BGGsY,705
|
|
30
30
|
discord/models/role.py,sha256=erlERmK-IZz4YzSNY-XLNvCc-Z5PoVlClxPOX67dQJg,1169
|
|
31
31
|
discord/models/user.py,sha256=lgG6GoU_7L68oHt6PGTzTkU1vrbsclRQzGjKzsLBeKA,298
|
|
32
|
-
discord/parts/__init__.py,sha256=
|
|
32
|
+
discord/parts/__init__.py,sha256=yROb-BqEw-FKXqq_-0WbP33U-Arm_9NpJuEamXpvjeA,19
|
|
33
33
|
discord/parts/action_row.py,sha256=PQ24Rr02QR4Sz1mypjM0C2gxYLH4Tsx7GvTorF4wsfQ,8264
|
|
34
34
|
discord/parts/attachment.py,sha256=fhWpb5e0JKfbibcEc0EOiNrzqSSAUEL87NEhRmB1H34,388
|
|
35
35
|
discord/parts/channel.py,sha256=2wmEjmRqUpORzL3CFp2rugMxrpSm_LxxvlcrmWIH4r4,584
|
|
@@ -37,19 +37,19 @@ discord/parts/command.py,sha256=CPyPO_T5ULp7j7syF9z2LztP3SF6KyX89sodz2c40Aw,2924
|
|
|
37
37
|
discord/parts/component_types.py,sha256=qr1R0jzXpE_h9Xv4P5DyYRSuhxS0Qnm9aag-JKrJvBA,131
|
|
38
38
|
discord/parts/components_v2.py,sha256=53cQm4FmxXHAA7PmDcDIO4bergX-BaUB3bZVWej-U9s,8660
|
|
39
39
|
discord/parts/embed.py,sha256=_PV-lEAKn-MiXyyLa2s8JKHEplA8J9dDO80NPdZtmLk,3986
|
|
40
|
-
discord/parts/message.py,sha256=
|
|
40
|
+
discord/parts/message.py,sha256=XSRGYQjmbVyS9YG6VVw19chU1CUUljrFpUy6NY2HRJ0,5517
|
|
41
41
|
discord/parts/modal.py,sha256=EX6J9Mh5dAQBOZqYKzSE7SFsKLfM_B1BhcJamjBNkZw,554
|
|
42
42
|
discord/parts/role.py,sha256=cK96UdgT-kU0gY5C_1LZXPrYg144x2RDmGjT28so57A,920
|
|
43
|
-
discord/resources/__init__.py,sha256=
|
|
43
|
+
discord/resources/__init__.py,sha256=EdzYKftSLqqr3Bpzc0_90kfozJXOtp9jNTIHhCTt_-0,21
|
|
44
44
|
discord/resources/application.py,sha256=vYMTli_FSbC7venMepsJ9bkzdEQVkKYpnxCJ9K2XDho,2765
|
|
45
45
|
discord/resources/bot_emojis.py,sha256=RvGCSOBkjS39P2aab0FzYUOTzBOiHX99RLrJZzAYNiU,1701
|
|
46
46
|
discord/resources/channel.py,sha256=fe2JUp943VnXa-BKyRMtNP-JyNd_Mp516sWBKHKn_GI,6915
|
|
47
47
|
discord/resources/guild.py,sha256=Unld1lWY3XynmRHU2FCi3-LA9VNp2thMI2BlILUTTxk,8183
|
|
48
|
-
discord/resources/interaction.py,sha256=
|
|
48
|
+
discord/resources/interaction.py,sha256=mr4kLecQpl3AtgnNeqnj3eZIwQ73Clutdi-gnoSMWVU,5237
|
|
49
49
|
discord/resources/message.py,sha256=RtvcCRx0lwW-mHPl3aNYoEvGffrvtpLsQ2fVWckywVI,7527
|
|
50
50
|
discord/resources/user.py,sha256=vk89TnCVi-6ZgbDs_TZTCXrx_NfFS5Q9Wi_itYoaoyg,3085
|
|
51
|
-
scurrypy-0.3.
|
|
52
|
-
scurrypy-0.3.
|
|
53
|
-
scurrypy-0.3.
|
|
54
|
-
scurrypy-0.3.
|
|
55
|
-
scurrypy-0.3.
|
|
51
|
+
scurrypy-0.3.4.1.dist-info/licenses/LICENSE,sha256=NtspfRMAlryd1Eev4BYi9EFbKhvdmlCJJ2-ADUoEBoI,426
|
|
52
|
+
scurrypy-0.3.4.1.dist-info/METADATA,sha256=n5LZHS1N0dlKDDo6DIGXih1tIty503u8UnmTIqTRQ6I,2956
|
|
53
|
+
scurrypy-0.3.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
54
|
+
scurrypy-0.3.4.1.dist-info/top_level.txt,sha256=fJkrNbR-_8ubMBUcDEJBcfkpECrvSEmMrNKgvLlQFoM,8
|
|
55
|
+
scurrypy-0.3.4.1.dist-info/RECORD,,
|