modifyself 0.1.9__tar.gz → 0.2.0__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.
- {modifyself-0.1.9 → modifyself-0.2.0}/PKG-INFO +1 -1
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/__init__.py +12 -12
- modifyself-0.2.0/modifyself/activity.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/client.py +240 -85
- modifyself-0.2.0/modifyself/components.py +0 -0
- modifyself-0.2.0/modifyself/interactions.py +0 -0
- modifyself-0.2.0/modifyself/models/billing.py +0 -0
- modifyself-0.2.0/modifyself/models/relationship.py +0 -0
- modifyself-0.2.0/modifyself/models/settings.py +0 -0
- modifyself-0.2.0/modifyself/models/webhook.py +0 -0
- modifyself-0.2.0/modifyself/types.py +162 -0
- modifyself-0.2.0/modifyself/voice.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself.egg-info/PKG-INFO +1 -1
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself.egg-info/SOURCES.txt +9 -1
- {modifyself-0.1.9 → modifyself-0.2.0}/pyproject.toml +29 -29
- modifyself-0.1.9/modifyself/types.py +0 -106
- {modifyself-0.1.9 → modifyself-0.2.0}/LICENSE +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/README.md +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/commands/__init__.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/commands/checks.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/commands/cog.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/commands/context.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/commands/converters.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/commands/core.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/core/bitfield.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/core/enums.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/core/mixins.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/core/snowflake.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/errors.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/gateway/__init__.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/gateway/dispatcher.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/gateway/heartbeat.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/gateway/websocket.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/headers.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/http/__init__.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/http/client.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/http/ratelimit.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/http/route.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/models/__init__.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/models/base.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/models/channel.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/models/guild.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/models/member.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/models/message.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/models/user.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/state.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself/utils.py +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself.egg-info/dependency_links.txt +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself.egg-info/requires.txt +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/modifyself.egg-info/top_level.txt +0 -0
- {modifyself-0.1.9 → modifyself-0.2.0}/setup.cfg +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
modifyself
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
__version__ = "0.1.4"
|
|
6
|
-
|
|
7
|
-
from .client import Client
|
|
8
|
-
from .commands.core import command
|
|
9
|
-
from .commands.cog import Cog
|
|
10
|
-
from .commands.context import Context
|
|
11
|
-
|
|
12
|
-
__all__ = ["Client", "command", "Cog", "Context"]
|
|
1
|
+
"""
|
|
2
|
+
modifyself ? a clean, pythonic Discord self-bot library.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__version__ = "0.1.4"
|
|
6
|
+
|
|
7
|
+
from .client import Client
|
|
8
|
+
from .commands.core import command
|
|
9
|
+
from .commands.cog import Cog
|
|
10
|
+
from .commands.context import Context
|
|
11
|
+
|
|
12
|
+
__all__ = ["Client", "command", "Cog", "Context"]
|
|
File without changes
|
|
@@ -19,8 +19,14 @@ from .commands.context import Context
|
|
|
19
19
|
from .commands.cog import Cog
|
|
20
20
|
from .errors import CommandError, ConversionError, CommandNotFound
|
|
21
21
|
from .models.message import Message
|
|
22
|
+
from .models.relationship import Relationship
|
|
23
|
+
from .models.billing import PaymentSource, Subscription
|
|
24
|
+
from .models.settings import GuildSettings, UserSettings
|
|
25
|
+
from .models.webhook import Webhook, WebhookMessage
|
|
22
26
|
from .headers import HeaderSpoofer, EMULATION
|
|
23
27
|
from .utils import send_notification, START_IMAGE, ERROR_IMAGE
|
|
28
|
+
from .interactions import Interaction, InteractionHandler, interaction_handler
|
|
29
|
+
from .voice import VoiceManager, VoiceClient, VoiceState
|
|
24
30
|
|
|
25
31
|
logger = logging.getLogger(__name__)
|
|
26
32
|
|
|
@@ -50,14 +56,13 @@ class Client:
|
|
|
50
56
|
command_prefix: Union[str, Callable[["Client", Message], str]] = "!",
|
|
51
57
|
owner_ids: Optional[List[int]] = None,
|
|
52
58
|
proxy: Optional[str] = None,
|
|
53
|
-
notifications: bool = True,
|
|
59
|
+
notifications: bool = True,
|
|
54
60
|
):
|
|
55
61
|
self.token = token
|
|
56
62
|
self.command_prefix = command_prefix
|
|
57
63
|
self.owner_ids = set(owner_ids) if owner_ids else set()
|
|
58
|
-
self._notifications = notifications
|
|
64
|
+
self._notifications = notifications
|
|
59
65
|
|
|
60
|
-
# Internal components
|
|
61
66
|
self._headers = HeaderSpoofer(token, EMULATION)
|
|
62
67
|
self._http = HTTPClient(
|
|
63
68
|
token,
|
|
@@ -72,26 +77,27 @@ class Client:
|
|
|
72
77
|
headers=self._headers,
|
|
73
78
|
)
|
|
74
79
|
|
|
75
|
-
# Event handlers
|
|
76
80
|
self._event_handlers: Dict[str, List[Callable]] = {}
|
|
77
81
|
self._once_handlers: Dict[str, List[Callable]] = {}
|
|
78
82
|
|
|
79
|
-
# Command registry
|
|
80
83
|
self._commands: Dict[str, Command] = {}
|
|
81
84
|
self._cogs: Dict[str, Cog] = {}
|
|
82
85
|
|
|
83
|
-
# Lifecycle
|
|
84
86
|
self._ready = asyncio.Event()
|
|
85
87
|
self._closed = False
|
|
86
88
|
self._task: Optional[asyncio.Task] = None
|
|
87
89
|
self._close_task: Optional[asyncio.Task] = None
|
|
88
|
-
self._error_handled = False
|
|
90
|
+
self._error_handled = False
|
|
91
|
+
|
|
92
|
+
self._interaction_handlers: Dict[str, Callable] = {}
|
|
93
|
+
|
|
94
|
+
self._voice = VoiceManager(self)
|
|
89
95
|
|
|
90
|
-
# Auto-register built-in event parsers
|
|
91
96
|
self._dispatcher.on("READY", self._on_ready_internal)
|
|
92
97
|
self._dispatcher.on("MESSAGE_CREATE", self._on_message_create_internal)
|
|
98
|
+
self._dispatcher.on("INTERACTION_CREATE", self._on_interaction_create)
|
|
99
|
+
self._dispatcher.on("VOICE_STATE_UPDATE", self._on_voice_state_update)
|
|
93
100
|
|
|
94
|
-
# Send startup notification
|
|
95
101
|
if self._notifications:
|
|
96
102
|
send_notification(
|
|
97
103
|
title="🚀 modifyself Initialized",
|
|
@@ -100,49 +106,40 @@ class Client:
|
|
|
100
106
|
timeout=3
|
|
101
107
|
)
|
|
102
108
|
|
|
103
|
-
# ------------------------------------------------------------------
|
|
104
|
-
# Properties
|
|
105
|
-
# ------------------------------------------------------------------
|
|
106
109
|
@property
|
|
107
110
|
def user(self):
|
|
108
|
-
"""The client's user object."""
|
|
109
111
|
return self._state.user
|
|
110
112
|
|
|
111
113
|
@property
|
|
112
114
|
def guilds(self):
|
|
113
|
-
"""A view of the guilds the client is in."""
|
|
114
115
|
return list(self._state._guilds.values())
|
|
115
116
|
|
|
116
117
|
@property
|
|
117
118
|
def users(self):
|
|
118
|
-
"""A view of the users the client has cached."""
|
|
119
119
|
return list(self._state._users.values())
|
|
120
120
|
|
|
121
121
|
@property
|
|
122
122
|
def latency(self) -> float:
|
|
123
|
-
"""The current gateway latency in seconds."""
|
|
124
123
|
return self._gateway.latency
|
|
125
124
|
|
|
126
125
|
@property
|
|
127
126
|
def listeners(self) -> Dict[str, List]:
|
|
128
|
-
"""A view of registered event listeners by event name."""
|
|
129
127
|
return dict(self._dispatcher._handlers)
|
|
130
128
|
|
|
131
129
|
@property
|
|
132
130
|
def commands(self) -> List[Command]:
|
|
133
|
-
"""Return a list of registered commands."""
|
|
134
131
|
return list(self._commands.values())
|
|
135
132
|
|
|
136
133
|
@property
|
|
137
134
|
def cogs(self) -> List[Cog]:
|
|
138
|
-
"""Return a list of loaded cogs."""
|
|
139
135
|
return list(self._cogs.values())
|
|
140
136
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
137
|
+
@property
|
|
138
|
+
def voice(self) -> VoiceManager:
|
|
139
|
+
"""Access voice manager."""
|
|
140
|
+
return self._voice
|
|
141
|
+
|
|
144
142
|
def event(self, coro: Callable):
|
|
145
|
-
"""Register an event handler."""
|
|
146
143
|
if not asyncio.iscoroutinefunction(coro):
|
|
147
144
|
raise TypeError("Event handlers must be coroutines")
|
|
148
145
|
name = coro.__name__.replace("on_", "", 1).upper()
|
|
@@ -152,7 +149,6 @@ class Client:
|
|
|
152
149
|
return coro
|
|
153
150
|
|
|
154
151
|
def listen(self, name: Optional[str] = None):
|
|
155
|
-
"""Decorator to register an event listener by explicit name."""
|
|
156
152
|
def decorator(coro: Callable):
|
|
157
153
|
if not asyncio.iscoroutinefunction(coro):
|
|
158
154
|
raise TypeError("Listeners must be coroutines")
|
|
@@ -162,20 +158,14 @@ class Client:
|
|
|
162
158
|
return decorator
|
|
163
159
|
|
|
164
160
|
def add_listener(self, event: str, handler: Callable):
|
|
165
|
-
"""Add a listener for an event."""
|
|
166
161
|
event = event.upper()
|
|
167
162
|
self._dispatcher.on(event, handler)
|
|
168
163
|
logger.info("[client] listener registered: %s -> %s", event, handler)
|
|
169
164
|
|
|
170
165
|
def remove_listener(self, event: str, handler: Callable):
|
|
171
|
-
"""Remove a listener for an event."""
|
|
172
166
|
self._dispatcher.off(event.upper(), handler)
|
|
173
167
|
|
|
174
|
-
# ------------------------------------------------------------------
|
|
175
|
-
# Command registration
|
|
176
|
-
# ------------------------------------------------------------------
|
|
177
168
|
def command(self, *, name: Optional[str] = None, aliases: Optional[List[str]] = None, **kwargs):
|
|
178
|
-
"""Decorator to register a command."""
|
|
179
169
|
def decorator(func: Callable):
|
|
180
170
|
cmd = Command(func, name=name, aliases=aliases, **kwargs)
|
|
181
171
|
self.add_command(cmd)
|
|
@@ -184,37 +174,28 @@ class Client:
|
|
|
184
174
|
return decorator
|
|
185
175
|
|
|
186
176
|
def add_command(self, cmd: Command):
|
|
187
|
-
"""Register a Command instance."""
|
|
188
177
|
if cmd.name in self._commands:
|
|
189
178
|
raise ValueError(f"Command {cmd.name} is already registered")
|
|
190
179
|
|
|
191
|
-
# Check aliases first before adding anything
|
|
192
180
|
for alias in cmd.aliases:
|
|
193
181
|
if alias in self._commands:
|
|
194
182
|
raise ValueError(f"Alias {alias} is already registered")
|
|
195
183
|
|
|
196
|
-
# Now safe to add
|
|
197
184
|
self._commands[cmd.name] = cmd
|
|
198
185
|
for alias in cmd.aliases:
|
|
199
186
|
self._commands[alias] = cmd
|
|
200
187
|
logger.debug("Registered command: %s", cmd.name)
|
|
201
188
|
|
|
202
189
|
def remove_command(self, name: str):
|
|
203
|
-
"""Remove a command by name or alias."""
|
|
204
190
|
cmd = self._commands.pop(name, None)
|
|
205
191
|
if cmd and cmd.name == name:
|
|
206
192
|
for alias in cmd.aliases:
|
|
207
193
|
self._commands.pop(alias, None)
|
|
208
194
|
|
|
209
195
|
def get_command(self, name: str) -> Optional[Command]:
|
|
210
|
-
"""Get a command by name or alias."""
|
|
211
196
|
return self._commands.get(name)
|
|
212
197
|
|
|
213
|
-
# ------------------------------------------------------------------
|
|
214
|
-
# Cog management
|
|
215
|
-
# ------------------------------------------------------------------
|
|
216
198
|
def add_cog(self, cog: Cog):
|
|
217
|
-
"""Add a cog to the bot."""
|
|
218
199
|
if cog.name in self._cogs:
|
|
219
200
|
raise ValueError(f"Cog {cog.name} is already loaded")
|
|
220
201
|
cog._inject(self)
|
|
@@ -223,7 +204,6 @@ class Client:
|
|
|
223
204
|
logger.info("Loaded cog: %s", cog.name)
|
|
224
205
|
|
|
225
206
|
def remove_cog(self, name: str):
|
|
226
|
-
"""Remove a cog from the bot."""
|
|
227
207
|
cog = self._cogs.pop(name, None)
|
|
228
208
|
if cog:
|
|
229
209
|
cog.cog_unload()
|
|
@@ -231,18 +211,19 @@ class Client:
|
|
|
231
211
|
logger.info("Unloaded cog: %s", name)
|
|
232
212
|
|
|
233
213
|
def get_cog(self, name: str) -> Optional[Cog]:
|
|
234
|
-
"""Get a loaded cog by name."""
|
|
235
214
|
return self._cogs.get(name)
|
|
236
215
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
216
|
+
def interaction_handler(self, custom_id: str):
|
|
217
|
+
"""Decorator to register an interaction handler."""
|
|
218
|
+
def decorator(func: Callable):
|
|
219
|
+
self._interaction_handlers[custom_id] = func
|
|
220
|
+
return func
|
|
221
|
+
return decorator
|
|
222
|
+
|
|
240
223
|
async def _on_ready_internal(self, user):
|
|
241
|
-
"""Internal READY handler."""
|
|
242
224
|
self._ready.set()
|
|
243
225
|
logger.debug("Ready event received internally")
|
|
244
226
|
|
|
245
|
-
# Send notification when bot is ready
|
|
246
227
|
if self._notifications:
|
|
247
228
|
username = user.name if user else "Unknown"
|
|
248
229
|
send_notification(
|
|
@@ -253,22 +234,38 @@ class Client:
|
|
|
253
234
|
)
|
|
254
235
|
|
|
255
236
|
async def _on_message_create_internal(self, message: Message):
|
|
256
|
-
"""Internal MESSAGE_CREATE handler for command processing."""
|
|
257
237
|
logger.debug(f"📩 MESSAGE_CREATE: {message.content[:50] if message.content else '(empty)'}")
|
|
258
238
|
await self._process_commands(message)
|
|
259
239
|
|
|
240
|
+
async def _on_interaction_create(self, data: dict):
|
|
241
|
+
"""Handle interaction_create events."""
|
|
242
|
+
try:
|
|
243
|
+
interaction = Interaction(self._state, data)
|
|
244
|
+
|
|
245
|
+
if interaction.custom_id and interaction.custom_id in self._interaction_handlers:
|
|
246
|
+
await self._interaction_handlers[interaction.custom_id](interaction)
|
|
247
|
+
else:
|
|
248
|
+
await interaction_handler.handle(interaction)
|
|
249
|
+
except Exception as e:
|
|
250
|
+
logger.exception(f"Error handling interaction: {e}")
|
|
251
|
+
|
|
252
|
+
async def _on_voice_state_update(self, data: dict):
|
|
253
|
+
"""Handle voice state updates."""
|
|
254
|
+
guild_id = int(data.get("guild_id", 0))
|
|
255
|
+
if not guild_id:
|
|
256
|
+
return
|
|
257
|
+
|
|
258
|
+
voice_state = data.get("voice_state", {})
|
|
259
|
+
self._voice.update_state(guild_id, voice_state)
|
|
260
|
+
|
|
260
261
|
async def _process_commands(self, message: Message):
|
|
261
|
-
"""Check if a message triggers a command and invoke it."""
|
|
262
|
-
# SELF-BOT: Only respond to our own messages
|
|
263
262
|
if not self.user or message.author.id != self.user.id:
|
|
264
263
|
return
|
|
265
264
|
|
|
266
|
-
# Don't process messages from bots
|
|
267
265
|
if message.author.bot:
|
|
268
266
|
logger.debug("Message from bot, ignoring")
|
|
269
267
|
return
|
|
270
268
|
|
|
271
|
-
# Resolve prefix
|
|
272
269
|
if callable(self.command_prefix):
|
|
273
270
|
prefix = self.command_prefix(self, message)
|
|
274
271
|
else:
|
|
@@ -283,9 +280,7 @@ class Client:
|
|
|
283
280
|
|
|
284
281
|
logger.debug(f"Checking command: content='{content}', prefix='{prefix}'")
|
|
285
282
|
|
|
286
|
-
# Check for prefix
|
|
287
283
|
if not content.startswith(prefix):
|
|
288
|
-
# Check for mention prefix (if user account)
|
|
289
284
|
if self.user:
|
|
290
285
|
mention = f"<@{self.user.id}>"
|
|
291
286
|
mention_nick = f"<@!{self.user.id}>"
|
|
@@ -300,7 +295,6 @@ class Client:
|
|
|
300
295
|
else:
|
|
301
296
|
return
|
|
302
297
|
else:
|
|
303
|
-
# Strip prefix
|
|
304
298
|
content = content[len(prefix):].strip()
|
|
305
299
|
logger.debug(f"Stripped prefix: '{content}'")
|
|
306
300
|
|
|
@@ -316,7 +310,6 @@ class Client:
|
|
|
316
310
|
command = self._commands.get(name)
|
|
317
311
|
if not command:
|
|
318
312
|
logger.debug(f"Command not found: {name}")
|
|
319
|
-
# Fire command not found event if registered
|
|
320
313
|
for handler in self._event_handlers.get("COMMAND_NOT_FOUND", []):
|
|
321
314
|
try:
|
|
322
315
|
await handler(message, name)
|
|
@@ -352,11 +345,7 @@ class Client:
|
|
|
352
345
|
except Exception:
|
|
353
346
|
pass
|
|
354
347
|
|
|
355
|
-
# ------------------------------------------------------------------
|
|
356
|
-
# Error handling
|
|
357
|
-
# ------------------------------------------------------------------
|
|
358
348
|
def _handle_error(self, error_message: str):
|
|
359
|
-
"""Send a notification when an error occurs."""
|
|
360
349
|
if self._notifications and not self._error_handled:
|
|
361
350
|
self._error_handled = True
|
|
362
351
|
send_notification(
|
|
@@ -365,14 +354,9 @@ class Client:
|
|
|
365
354
|
image_url=ERROR_IMAGE,
|
|
366
355
|
timeout=10
|
|
367
356
|
)
|
|
368
|
-
# Reset after a moment so new errors can trigger notifications
|
|
369
357
|
asyncio.get_event_loop().call_later(2, lambda: setattr(self, '_error_handled', False))
|
|
370
358
|
|
|
371
|
-
# ------------------------------------------------------------------
|
|
372
|
-
# Lifecycle
|
|
373
|
-
# ------------------------------------------------------------------
|
|
374
359
|
async def start(self):
|
|
375
|
-
"""Start the client (connects to gateway)."""
|
|
376
360
|
self._closed = False
|
|
377
361
|
self._error_handled = False
|
|
378
362
|
try:
|
|
@@ -383,47 +367,39 @@ class Client:
|
|
|
383
367
|
raise
|
|
384
368
|
|
|
385
369
|
async def close(self):
|
|
386
|
-
"""Close the client and cleanup."""
|
|
387
370
|
if self._closed:
|
|
388
371
|
return
|
|
389
372
|
|
|
390
373
|
self._closed = True
|
|
391
374
|
self._ready.clear()
|
|
392
375
|
|
|
393
|
-
|
|
376
|
+
await self._voice.disconnect_all()
|
|
394
377
|
await self._gateway.close()
|
|
395
|
-
|
|
396
|
-
# Close HTTP client
|
|
397
378
|
await self._http.close()
|
|
398
379
|
|
|
399
380
|
logger.info("Client closed")
|
|
400
381
|
|
|
401
382
|
def run(self):
|
|
402
|
-
"""Run the client with a new event loop."""
|
|
403
383
|
loop = asyncio.new_event_loop()
|
|
404
384
|
asyncio.set_event_loop(loop)
|
|
405
385
|
|
|
406
|
-
# Store close task reference
|
|
407
386
|
self._close_task = None
|
|
408
387
|
|
|
409
388
|
def signal_handler(sig):
|
|
410
389
|
logger.info("Received signal %s, shutting down...", sig)
|
|
411
390
|
if loop.is_running():
|
|
412
|
-
# Schedule close in the event loop
|
|
413
391
|
asyncio.run_coroutine_threadsafe(self.close(), loop)
|
|
414
392
|
|
|
415
393
|
try:
|
|
416
394
|
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
417
395
|
loop.add_signal_handler(sig, lambda s=sig: signal_handler(s))
|
|
418
396
|
except NotImplementedError:
|
|
419
|
-
# Windows doesn't support add_signal_handler
|
|
420
397
|
pass
|
|
421
398
|
|
|
422
399
|
async def runner():
|
|
423
400
|
try:
|
|
424
401
|
await self.start()
|
|
425
402
|
except Exception as exc:
|
|
426
|
-
# Catch any unhandled errors in the main loop
|
|
427
403
|
self._handle_error(f"Bot crashed: {exc}")
|
|
428
404
|
raise
|
|
429
405
|
finally:
|
|
@@ -434,7 +410,6 @@ class Client:
|
|
|
434
410
|
except KeyboardInterrupt:
|
|
435
411
|
logger.info("Keyboard interrupt received, shutting down...")
|
|
436
412
|
except Exception as exc:
|
|
437
|
-
# Any unhandled exception at the top level
|
|
438
413
|
self._handle_error(f"Bot crashed: {exc}")
|
|
439
414
|
raise
|
|
440
415
|
finally:
|
|
@@ -444,40 +419,220 @@ class Client:
|
|
|
444
419
|
loop.close()
|
|
445
420
|
|
|
446
421
|
async def wait_until_ready(self):
|
|
447
|
-
"""Wait until the client receives the READY event."""
|
|
448
422
|
await self._ready.wait()
|
|
449
423
|
|
|
450
|
-
#
|
|
451
|
-
#
|
|
452
|
-
#
|
|
424
|
+
# ============================================================
|
|
425
|
+
# FETCH METHODS
|
|
426
|
+
# ============================================================
|
|
453
427
|
async def fetch_user(self, user_id: int):
|
|
454
|
-
"""Fetch a user from the HTTP API."""
|
|
455
428
|
data = await self._http.request(Route.user(user_id))
|
|
456
429
|
return self._state._add_user(data)
|
|
457
430
|
|
|
458
431
|
async def fetch_guild(self, guild_id: int):
|
|
459
|
-
"""Fetch a guild from the HTTP API."""
|
|
460
432
|
data = await self._http.request(Route.guild(guild_id))
|
|
461
433
|
return self._state._add_guild(data)
|
|
462
434
|
|
|
463
435
|
async def fetch_channel(self, channel_id: int):
|
|
464
|
-
"""Fetch a channel from the HTTP API."""
|
|
465
436
|
data = await self._http.request(Route.channel(channel_id))
|
|
466
437
|
return self._state._add_channel(data)
|
|
467
438
|
|
|
468
439
|
async def fetch_message(self, channel_id: int, message_id: int):
|
|
469
|
-
"""Fetch a message from the HTTP API."""
|
|
470
440
|
data = await self._http.request(Route.channel_message(channel_id, message_id))
|
|
471
441
|
return self._state._store_message(data)
|
|
472
442
|
|
|
473
443
|
def get_guild(self, guild_id: int):
|
|
474
|
-
"""Get a cached guild by ID."""
|
|
475
444
|
return self._state._guilds.get(guild_id)
|
|
476
445
|
|
|
477
446
|
def get_channel(self, channel_id: int):
|
|
478
|
-
"""Get a cached channel by ID."""
|
|
479
447
|
return self._state._channels.get(channel_id)
|
|
480
448
|
|
|
481
449
|
def get_user(self, user_id: int):
|
|
482
|
-
"""Get a cached user by ID."""
|
|
483
450
|
return self._state._users.get(user_id)
|
|
451
|
+
|
|
452
|
+
# ============================================================
|
|
453
|
+
# RELATIONSHIPS
|
|
454
|
+
# ============================================================
|
|
455
|
+
async def get_relationships(self) -> List[Relationship]:
|
|
456
|
+
data = await self._http.request(Route("GET", "/users/@me/relationships"))
|
|
457
|
+
return [Relationship(state=self._state, data=r) for r in data]
|
|
458
|
+
|
|
459
|
+
async def add_friend(self, user_id: int, username: str = None) -> Dict[str, Any]:
|
|
460
|
+
payload = {"username": username} if username else {}
|
|
461
|
+
return await self._http.request(
|
|
462
|
+
Route("POST", f"/users/@me/relationships/{user_id}"),
|
|
463
|
+
json=payload
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
async def remove_friend(self, user_id: int) -> None:
|
|
467
|
+
await self._http.request(
|
|
468
|
+
Route("DELETE", f"/users/@me/relationships/{user_id}")
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
async def block_user(self, user_id: int) -> None:
|
|
472
|
+
await self._http.request(
|
|
473
|
+
Route("PUT", f"/users/@me/relationships/{user_id}"),
|
|
474
|
+
json={"type": 2}
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
async def unblock_user(self, user_id: int) -> None:
|
|
478
|
+
await self._http.request(
|
|
479
|
+
Route("DELETE", f"/users/@me/relationships/{user_id}")
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
# ============================================================
|
|
483
|
+
# BILLING
|
|
484
|
+
# ============================================================
|
|
485
|
+
async def get_payment_sources(self) -> List[PaymentSource]:
|
|
486
|
+
data = await self._http.request(Route("GET", "/users/@me/billing/payment-sources"))
|
|
487
|
+
return [PaymentSource(state=self._state, data=s) for s in data]
|
|
488
|
+
|
|
489
|
+
async def get_subscriptions(self) -> List[Subscription]:
|
|
490
|
+
data = await self._http.request(Route("GET", "/users/@me/billing/subscriptions"))
|
|
491
|
+
return [Subscription(state=self._state, data=s) for s in data]
|
|
492
|
+
|
|
493
|
+
async def get_entitlements(self) -> List[Dict[str, Any]]:
|
|
494
|
+
return await self._http.request(Route("GET", "/users/@me/entitlements"))
|
|
495
|
+
|
|
496
|
+
async def get_skus(self) -> List[Dict[str, Any]]:
|
|
497
|
+
return await self._http.request(Route("GET", "/users/@me/entitlements/skus"))
|
|
498
|
+
|
|
499
|
+
# ============================================================
|
|
500
|
+
# SETTINGS
|
|
501
|
+
# ============================================================
|
|
502
|
+
async def get_user_settings(self) -> UserSettings:
|
|
503
|
+
data = await self._http.request(Route("GET", "/users/@me/settings"))
|
|
504
|
+
return UserSettings(data)
|
|
505
|
+
|
|
506
|
+
async def update_user_settings(self, **kwargs) -> Dict[str, Any]:
|
|
507
|
+
return await self._http.request(
|
|
508
|
+
Route("PATCH", "/users/@me/settings"),
|
|
509
|
+
json=kwargs
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
async def set_status(self, status: str) -> Dict[str, Any]:
|
|
513
|
+
return await self.update_user_settings(status=status)
|
|
514
|
+
|
|
515
|
+
async def set_theme(self, theme: str) -> Dict[str, Any]:
|
|
516
|
+
return await self.update_user_settings(theme=theme)
|
|
517
|
+
|
|
518
|
+
async def set_language(self, locale: str) -> Dict[str, Any]:
|
|
519
|
+
return await self.update_user_settings(locale=locale)
|
|
520
|
+
|
|
521
|
+
async def set_dev_mode(self, enabled: bool) -> Dict[str, Any]:
|
|
522
|
+
return await self.update_user_settings(developer_mode=enabled)
|
|
523
|
+
|
|
524
|
+
async def get_guild_settings(self, guild_id: int) -> GuildSettings:
|
|
525
|
+
data = await self._http.request(
|
|
526
|
+
Route("GET", f"/users/@me/guilds/{guild_id}/settings")
|
|
527
|
+
)
|
|
528
|
+
return GuildSettings(state=self._state, data=data)
|
|
529
|
+
|
|
530
|
+
async def update_guild_settings(self, guild_id: int, **kwargs) -> Dict[str, Any]:
|
|
531
|
+
return await self._http.request(
|
|
532
|
+
Route("PATCH", f"/users/@me/guilds/{guild_id}/settings"),
|
|
533
|
+
json=kwargs
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
# ============================================================
|
|
537
|
+
# PROFILE
|
|
538
|
+
# ============================================================
|
|
539
|
+
async def get_profile(self, user_id: int = None) -> Dict[str, Any]:
|
|
540
|
+
endpoint = f"/users/{user_id}/profile" if user_id else "/users/@me/profile"
|
|
541
|
+
return await self._http.request(Route("GET", endpoint))
|
|
542
|
+
|
|
543
|
+
async def update_avatar(self, avatar_data: str) -> Dict[str, Any]:
|
|
544
|
+
return await self._http.request(
|
|
545
|
+
Route("PATCH", "/users/@me"),
|
|
546
|
+
json={"avatar": avatar_data}
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
async def update_banner(self, banner_data: str) -> Dict[str, Any]:
|
|
550
|
+
return await self._http.request(
|
|
551
|
+
Route("PATCH", "/users/@me"),
|
|
552
|
+
json={"banner": banner_data}
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
async def update_bio(self, bio: str) -> Dict[str, Any]:
|
|
556
|
+
return await self._http.request(
|
|
557
|
+
Route("PATCH", "/users/@me"),
|
|
558
|
+
json={"bio": bio}
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
async def update_display_name(self, name: str) -> Dict[str, Any]:
|
|
562
|
+
return await self._http.request(
|
|
563
|
+
Route("PATCH", "/users/@me"),
|
|
564
|
+
json={"global_name": name}
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
async def get_connections(self) -> List[Dict[str, Any]]:
|
|
568
|
+
return await self._http.request(Route("GET", "/users/@me/connections"))
|
|
569
|
+
|
|
570
|
+
# ============================================================
|
|
571
|
+
# WEBHOOKS
|
|
572
|
+
# ============================================================
|
|
573
|
+
async def get_webhook(self, webhook_id: int) -> Webhook:
|
|
574
|
+
"""Get a webhook by ID."""
|
|
575
|
+
data = await self._http.request(Route("GET", f"/webhooks/{webhook_id}"))
|
|
576
|
+
return Webhook(state=self._state, data=data)
|
|
577
|
+
|
|
578
|
+
async def get_webhook_with_token(self, webhook_id: int, token: str) -> Webhook:
|
|
579
|
+
"""Get a webhook by ID and token."""
|
|
580
|
+
data = await self._http.request(
|
|
581
|
+
method="GET",
|
|
582
|
+
url=f"/webhooks/{webhook_id}/{token}",
|
|
583
|
+
)
|
|
584
|
+
return Webhook(state=self._state, data=data)
|
|
585
|
+
|
|
586
|
+
async def get_channel_webhooks(self, channel_id: int) -> List[Webhook]:
|
|
587
|
+
"""Get all webhooks in a channel."""
|
|
588
|
+
data = await self._http.request(Route("GET", f"/channels/{channel_id}/webhooks"))
|
|
589
|
+
return [Webhook(state=self._state, data=w) for w in data]
|
|
590
|
+
|
|
591
|
+
async def get_guild_webhooks(self, guild_id: int) -> List[Webhook]:
|
|
592
|
+
"""Get all webhooks in a guild."""
|
|
593
|
+
data = await self._http.request(Route("GET", f"/guilds/{guild_id}/webhooks"))
|
|
594
|
+
return [Webhook(state=self._state, data=w) for w in data]
|
|
595
|
+
|
|
596
|
+
async def create_webhook(
|
|
597
|
+
self,
|
|
598
|
+
channel_id: int,
|
|
599
|
+
name: str,
|
|
600
|
+
avatar: Optional[str] = None,
|
|
601
|
+
) -> Webhook:
|
|
602
|
+
"""Create a webhook in a channel."""
|
|
603
|
+
payload = {"name": name}
|
|
604
|
+
if avatar:
|
|
605
|
+
payload["avatar"] = avatar
|
|
606
|
+
data = await self._http.request(
|
|
607
|
+
Route("POST", f"/channels/{channel_id}/webhooks"),
|
|
608
|
+
json=payload,
|
|
609
|
+
)
|
|
610
|
+
return Webhook(state=self._state, data=data)
|
|
611
|
+
|
|
612
|
+
# ============================================================
|
|
613
|
+
# VOICE
|
|
614
|
+
# ============================================================
|
|
615
|
+
async def create_voice_client(
|
|
616
|
+
self,
|
|
617
|
+
channel_id: int,
|
|
618
|
+
guild_id: int,
|
|
619
|
+
endpoint: str,
|
|
620
|
+
token: str,
|
|
621
|
+
session_id: str,
|
|
622
|
+
) -> VoiceClient:
|
|
623
|
+
"""Create and connect a voice client."""
|
|
624
|
+
return await self._voice.connect(
|
|
625
|
+
channel_id,
|
|
626
|
+
guild_id,
|
|
627
|
+
endpoint,
|
|
628
|
+
token,
|
|
629
|
+
session_id,
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
def get_voice_client(self, guild_id: int) -> Optional[VoiceClient]:
|
|
633
|
+
"""Get the voice client for a guild."""
|
|
634
|
+
return self._voice.get_client(guild_id)
|
|
635
|
+
|
|
636
|
+
async def disconnect_voice(self, guild_id: int) -> None:
|
|
637
|
+
"""Disconnect voice client for a guild."""
|
|
638
|
+
await self._voice.disconnect(guild_id)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Type definitions for modifyself.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, List, Optional, Union, Callable, Coroutine, TypeVar, Generic
|
|
6
|
+
from typing_extensions import Self
|
|
7
|
+
|
|
8
|
+
# ============================================
|
|
9
|
+
# Basic Types
|
|
10
|
+
# ============================================
|
|
11
|
+
|
|
12
|
+
Snowflake = int
|
|
13
|
+
Timestamp = str
|
|
14
|
+
Color = int
|
|
15
|
+
|
|
16
|
+
# ============================================
|
|
17
|
+
# Command Types
|
|
18
|
+
# ============================================
|
|
19
|
+
|
|
20
|
+
CommandCallback = Callable[..., Coroutine[Any, Any, Any]]
|
|
21
|
+
CommandName = str
|
|
22
|
+
CommandAliases = List[str]
|
|
23
|
+
CommandCooldown = int
|
|
24
|
+
|
|
25
|
+
# ============================================
|
|
26
|
+
# Event Types
|
|
27
|
+
# ============================================
|
|
28
|
+
|
|
29
|
+
EventHandler = Callable[..., Coroutine[Any, Any, Any]]
|
|
30
|
+
EventName = str
|
|
31
|
+
|
|
32
|
+
# ============================================
|
|
33
|
+
# HTTP Types
|
|
34
|
+
# ============================================
|
|
35
|
+
|
|
36
|
+
HTTPMethod = str
|
|
37
|
+
HTTPHeaders = Dict[str, str]
|
|
38
|
+
HTTPParams = Dict[str, Any]
|
|
39
|
+
HTTPData = Union[Dict[str, Any], str, None]
|
|
40
|
+
|
|
41
|
+
# ============================================
|
|
42
|
+
# Gateway Types
|
|
43
|
+
# ============================================
|
|
44
|
+
|
|
45
|
+
GatewayPayload = Dict[str, Any]
|
|
46
|
+
GatewayOPCode = int
|
|
47
|
+
GatewaySequence = int
|
|
48
|
+
|
|
49
|
+
# ============================================
|
|
50
|
+
# Model Types
|
|
51
|
+
# ============================================
|
|
52
|
+
|
|
53
|
+
T = TypeVar('T')
|
|
54
|
+
|
|
55
|
+
class CachedObject(Generic[T]):
|
|
56
|
+
"""Base class for cached objects."""
|
|
57
|
+
def __init__(self, data: T, state: Any) -> None:
|
|
58
|
+
self._data = data
|
|
59
|
+
self._state = state
|
|
60
|
+
|
|
61
|
+
def _update(self, data: T) -> None:
|
|
62
|
+
"""Update the object with new data."""
|
|
63
|
+
self._data = data
|
|
64
|
+
|
|
65
|
+
# ============================================
|
|
66
|
+
# Permission Types
|
|
67
|
+
# ============================================
|
|
68
|
+
|
|
69
|
+
PermissionValue = int
|
|
70
|
+
PermissionOverwrite = Dict[str, Any]
|
|
71
|
+
|
|
72
|
+
# ============================================
|
|
73
|
+
# Channel Types (using Python 3.12 union syntax)
|
|
74
|
+
# ============================================
|
|
75
|
+
|
|
76
|
+
ChannelID = int
|
|
77
|
+
GuildID = int
|
|
78
|
+
UserID = int
|
|
79
|
+
MessageID = int
|
|
80
|
+
RoleID = int
|
|
81
|
+
EmojiID = int
|
|
82
|
+
|
|
83
|
+
# ============================================
|
|
84
|
+
# Context Types
|
|
85
|
+
# ============================================
|
|
86
|
+
|
|
87
|
+
class ContextBase:
|
|
88
|
+
"""Base context class."""
|
|
89
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
90
|
+
for key, value in kwargs.items():
|
|
91
|
+
setattr(self, key, value)
|
|
92
|
+
|
|
93
|
+
# ============================================
|
|
94
|
+
# Option Types (Python 3.12 union syntax)
|
|
95
|
+
# ============================================
|
|
96
|
+
|
|
97
|
+
OptionalStr = str | None
|
|
98
|
+
OptionalInt = int | None
|
|
99
|
+
OptionalList = list | None
|
|
100
|
+
OptionalDict = dict | None
|
|
101
|
+
|
|
102
|
+
# ============================================
|
|
103
|
+
# Function Types
|
|
104
|
+
# ============================================
|
|
105
|
+
|
|
106
|
+
AsyncFunc = Callable[..., Coroutine[Any, Any, Any]]
|
|
107
|
+
SyncFunc = Callable[..., Any]
|
|
108
|
+
Predicate = Callable[[Any], bool]
|
|
109
|
+
|
|
110
|
+
# ============================================
|
|
111
|
+
# State Types
|
|
112
|
+
# ============================================
|
|
113
|
+
|
|
114
|
+
StateCache = Dict[Snowflake, Any]
|
|
115
|
+
StateUpdate = Dict[str, Any]
|
|
116
|
+
|
|
117
|
+
# ============================================
|
|
118
|
+
# Error Types
|
|
119
|
+
# ============================================
|
|
120
|
+
|
|
121
|
+
ErrorCode = int
|
|
122
|
+
ErrorMessage = str
|
|
123
|
+
|
|
124
|
+
# ============================================
|
|
125
|
+
# Rate Limit Types
|
|
126
|
+
# ============================================
|
|
127
|
+
|
|
128
|
+
RateLimitBucket = str
|
|
129
|
+
RateLimitRemaining = int
|
|
130
|
+
RateLimitReset = float
|
|
131
|
+
|
|
132
|
+
# ============================================
|
|
133
|
+
# WebSocket Types
|
|
134
|
+
# ============================================
|
|
135
|
+
|
|
136
|
+
WebSocketURL = str
|
|
137
|
+
WebSocketEvent = Dict[str, Any]
|
|
138
|
+
|
|
139
|
+
# ============================================
|
|
140
|
+
# Notification Types (Python 3.12 union syntax)
|
|
141
|
+
# ============================================
|
|
142
|
+
|
|
143
|
+
NotificationTitle = str
|
|
144
|
+
NotificationMessage = str
|
|
145
|
+
NotificationImage = str | None # Python 3.12 union syntax
|
|
146
|
+
NotificationTimeout = int
|
|
147
|
+
|
|
148
|
+
# ============================================
|
|
149
|
+
# Client Types
|
|
150
|
+
# ============================================
|
|
151
|
+
|
|
152
|
+
ClientToken = str
|
|
153
|
+
ClientPrefix = str | list[str] | Callable # Python 3.12 union syntax
|
|
154
|
+
ClientID = int
|
|
155
|
+
|
|
156
|
+
# ============================================
|
|
157
|
+
# JSON Types
|
|
158
|
+
# ============================================
|
|
159
|
+
|
|
160
|
+
JSONValue = str | int | float | bool | None | dict | list
|
|
161
|
+
JSONObject = dict[str, JSONValue]
|
|
162
|
+
JSONArray = list[JSONValue]
|
|
File without changes
|
|
@@ -2,12 +2,16 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
modifyself/__init__.py
|
|
5
|
+
modifyself/activity.py
|
|
5
6
|
modifyself/client.py
|
|
7
|
+
modifyself/components.py
|
|
6
8
|
modifyself/errors.py
|
|
7
9
|
modifyself/headers.py
|
|
10
|
+
modifyself/interactions.py
|
|
8
11
|
modifyself/state.py
|
|
9
12
|
modifyself/types.py
|
|
10
13
|
modifyself/utils.py
|
|
14
|
+
modifyself/voice.py
|
|
11
15
|
modifyself.egg-info/PKG-INFO
|
|
12
16
|
modifyself.egg-info/SOURCES.txt
|
|
13
17
|
modifyself.egg-info/dependency_links.txt
|
|
@@ -33,8 +37,12 @@ modifyself/http/ratelimit.py
|
|
|
33
37
|
modifyself/http/route.py
|
|
34
38
|
modifyself/models/__init__.py
|
|
35
39
|
modifyself/models/base.py
|
|
40
|
+
modifyself/models/billing.py
|
|
36
41
|
modifyself/models/channel.py
|
|
37
42
|
modifyself/models/guild.py
|
|
38
43
|
modifyself/models/member.py
|
|
39
44
|
modifyself/models/message.py
|
|
40
|
-
modifyself/models/
|
|
45
|
+
modifyself/models/relationship.py
|
|
46
|
+
modifyself/models/settings.py
|
|
47
|
+
modifyself/models/user.py
|
|
48
|
+
modifyself/models/webhook.py
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "modifyself"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "A clean, pythonic Discord self-bot library"
|
|
9
|
-
readme = "README.md"
|
|
10
|
-
license = "MIT"
|
|
11
|
-
authors = [
|
|
12
|
-
{name = "modifyself", email = "modifyself@example.com"}
|
|
13
|
-
]
|
|
14
|
-
requires-python = ">=3.9"
|
|
15
|
-
dependencies = [
|
|
16
|
-
"websockets>=10.0",
|
|
17
|
-
"wreq>=0.1.0",
|
|
18
|
-
"aiohttp>=3.8.0",
|
|
19
|
-
"plyer>=0.9",
|
|
20
|
-
"pillow>=10.0",
|
|
21
|
-
"requests>=2.28",
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
[project.urls]
|
|
25
|
-
Homepage = "https://github.com/clashings/modifyself"
|
|
26
|
-
Bug-Reports = "https://github.com/clashings/modifyself/issues"
|
|
27
|
-
Source = "https://github.com/clashings/modifyself"
|
|
28
|
-
|
|
29
|
-
[tool.setuptools.packages.find]
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "modifyself"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "A clean, pythonic Discord self-bot library"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "modifyself", email = "modifyself@example.com"}
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
dependencies = [
|
|
16
|
+
"websockets>=10.0",
|
|
17
|
+
"wreq>=0.1.0",
|
|
18
|
+
"aiohttp>=3.8.0",
|
|
19
|
+
"plyer>=0.9",
|
|
20
|
+
"pillow>=10.0",
|
|
21
|
+
"requests>=2.28",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/clashings/modifyself"
|
|
26
|
+
Bug-Reports = "https://github.com/clashings/modifyself/issues"
|
|
27
|
+
Source = "https://github.com/clashings/modifyself"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
30
|
include = ["modifyself*"]
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
TypedDict mirrors of Discord's JSON payloads.
|
|
3
|
-
Update this when Discord changes their API.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from typing import TypedDict, NotRequired
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class UserPayload(TypedDict):
|
|
10
|
-
id: str
|
|
11
|
-
username: str
|
|
12
|
-
discriminator: str
|
|
13
|
-
avatar: str | None
|
|
14
|
-
bot: NotRequired[bool]
|
|
15
|
-
system: NotRequired[bool]
|
|
16
|
-
public_flags: NotRequired[int]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class MemberPayload(TypedDict):
|
|
20
|
-
user: NotRequired[UserPayload]
|
|
21
|
-
nick: NotRequired[str | None]
|
|
22
|
-
roles: list[str]
|
|
23
|
-
joined_at: str
|
|
24
|
-
premium_since: NotRequired[str | None]
|
|
25
|
-
deaf: bool
|
|
26
|
-
mute: bool
|
|
27
|
-
pending: NotRequired[bool]
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class ChannelPayload(TypedDict):
|
|
31
|
-
id: str
|
|
32
|
-
type: int
|
|
33
|
-
guild_id: NotRequired[str]
|
|
34
|
-
name: NotRequired[str]
|
|
35
|
-
topic: NotRequired[str | None]
|
|
36
|
-
nsfw: NotRequired[bool]
|
|
37
|
-
last_message_id: NotRequired[str | None]
|
|
38
|
-
parent_id: NotRequired[str | None]
|
|
39
|
-
permission_overwrites: NotRequired[list[dict]]
|
|
40
|
-
position: NotRequired[int]
|
|
41
|
-
recipients: NotRequired[list[UserPayload]]
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class MessagePayload(TypedDict):
|
|
45
|
-
id: str
|
|
46
|
-
channel_id: str
|
|
47
|
-
author: UserPayload
|
|
48
|
-
content: str
|
|
49
|
-
timestamp: str
|
|
50
|
-
edited_timestamp: str | None
|
|
51
|
-
tts: bool
|
|
52
|
-
mention_everyone: bool
|
|
53
|
-
mentions: list[UserPayload]
|
|
54
|
-
mention_roles: list[str]
|
|
55
|
-
attachments: list[dict]
|
|
56
|
-
embeds: list[dict]
|
|
57
|
-
reactions: NotRequired[list[dict]]
|
|
58
|
-
pinned: bool
|
|
59
|
-
type: int
|
|
60
|
-
guild_id: NotRequired[str]
|
|
61
|
-
member: NotRequired[MemberPayload]
|
|
62
|
-
referenced_message: NotRequired["MessagePayload" | None]
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class GuildPayload(TypedDict):
|
|
66
|
-
id: str
|
|
67
|
-
name: str
|
|
68
|
-
icon: str | None
|
|
69
|
-
splash: str | None
|
|
70
|
-
owner_id: str
|
|
71
|
-
region: NotRequired[str]
|
|
72
|
-
afk_channel_id: str | None
|
|
73
|
-
afk_timeout: int
|
|
74
|
-
verification_level: int
|
|
75
|
-
default_message_notifications: int
|
|
76
|
-
explicit_content_filter: int
|
|
77
|
-
roles: list[dict]
|
|
78
|
-
emojis: list[dict]
|
|
79
|
-
features: list[str]
|
|
80
|
-
mfa_level: int
|
|
81
|
-
system_channel_id: str | None
|
|
82
|
-
system_channel_flags: int
|
|
83
|
-
max_presences: NotRequired[int | None]
|
|
84
|
-
max_members: NotRequired[int]
|
|
85
|
-
vanity_url_code: str | None
|
|
86
|
-
description: str | None
|
|
87
|
-
banner: str | None
|
|
88
|
-
premium_tier: int
|
|
89
|
-
premium_subscription_count: NotRequired[int]
|
|
90
|
-
preferred_locale: str
|
|
91
|
-
public_updates_channel_id: str | None
|
|
92
|
-
max_video_channel_users: NotRequired[int]
|
|
93
|
-
approximate_member_count: NotRequired[int]
|
|
94
|
-
approximate_presence_count: NotRequired[int]
|
|
95
|
-
nsfw_level: int
|
|
96
|
-
members: NotRequired[list[MemberPayload]]
|
|
97
|
-
channels: NotRequired[list[ChannelPayload]]
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
class ReadyPayload(TypedDict):
|
|
101
|
-
v: int
|
|
102
|
-
user: UserPayload
|
|
103
|
-
guilds: list[dict]
|
|
104
|
-
session_id: str
|
|
105
|
-
shard: NotRequired[list[int]]
|
|
106
|
-
application: NotRequired[dict]
|
|
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
|