modifyself 0.2.9__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {modifyself-0.2.9 → modifyself-0.3.3}/PKG-INFO +1 -1
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/__init__.py +22 -22
- modifyself-0.3.3/modifyself/activity.py +399 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/client.py +1 -73
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/commands/context.py +11 -8
- modifyself-0.3.3/modifyself/models/__init__.py +29 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself.egg-info/PKG-INFO +1 -1
- {modifyself-0.2.9 → modifyself-0.3.3}/pyproject.toml +1 -1
- modifyself-0.2.9/modifyself/activity.py +0 -0
- modifyself-0.2.9/modifyself/models/__init__.py +0 -23
- {modifyself-0.2.9 → modifyself-0.3.3}/LICENSE +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/README.md +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/commands/__init__.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/commands/checks.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/commands/cog.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/commands/converters.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/commands/core.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/components.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/core/bitfield.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/core/enums.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/core/mixins.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/core/snowflake.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/errors.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/gateway/__init__.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/gateway/dispatcher.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/gateway/heartbeat.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/gateway/websocket.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/headers.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/http/__init__.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/http/client.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/http/ratelimit.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/http/route.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/interactions.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/models/base.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/models/billing.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/models/channel.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/models/guild.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/models/member.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/models/message.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/models/relationship.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/models/settings.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/models/user.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/models/webhook.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/state.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/types.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/utils.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself/voice.py +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself.egg-info/SOURCES.txt +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself.egg-info/dependency_links.txt +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself.egg-info/requires.txt +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/modifyself.egg-info/top_level.txt +0 -0
- {modifyself-0.2.9 → modifyself-0.3.3}/setup.cfg +0 -0
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
modifyself
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
__version__ = "0.
|
|
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
|
-
from .models.relationship import Relationship
|
|
13
|
-
from .models.billing import PaymentSource, Subscription
|
|
14
|
-
from .models.settings import GuildSettings, UserSettings
|
|
15
|
-
from .models.webhook import Webhook, WebhookMessage
|
|
16
|
-
|
|
17
|
-
from .activity import Activity, ActivityType, ActivityFlags, spotify_activity, youtube_activity, xbox_activity, playstation_activity, crunchyroll_activity, custom_activity, listening_activity, streaming_activity, competing_activity, LOGO_MAP
|
|
18
|
-
|
|
19
|
-
from .components import ComponentType, ButtonStyle, TextInputStyle, Button, SelectOption, SelectMenu, ChannelSelect, RoleSelect, MentionableSelect, UserSelect, TextInput, ActionRow, Modal
|
|
20
|
-
|
|
21
|
-
from .interactions import Interaction, InteractionType, InteractionCallbackType, InteractionHandler, interaction_handler
|
|
22
|
-
|
|
1
|
+
"""
|
|
2
|
+
modifyself ? a clean, pythonic Discord self-bot library.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__version__ = "0.3.3"
|
|
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
|
+
from .models.relationship import Relationship
|
|
13
|
+
from .models.billing import PaymentSource, Subscription
|
|
14
|
+
from .models.settings import GuildSettings, UserSettings
|
|
15
|
+
from .models.webhook import Webhook, WebhookMessage
|
|
16
|
+
|
|
17
|
+
from .activity import Activity, ActivityType, ActivityFlags, spotify_activity, youtube_activity, xbox_activity, playstation_activity, crunchyroll_activity, custom_activity, listening_activity, streaming_activity, competing_activity, LOGO_MAP
|
|
18
|
+
|
|
19
|
+
from .components import ComponentType, ButtonStyle, TextInputStyle, Button, SelectOption, SelectMenu, ChannelSelect, RoleSelect, MentionableSelect, UserSelect, TextInput, ActionRow, Modal
|
|
20
|
+
|
|
21
|
+
from .interactions import Interaction, InteractionType, InteractionCallbackType, InteractionHandler, interaction_handler
|
|
22
|
+
|
|
23
23
|
__all__ = ["Client", "command", "Cog", "Context", "Relationship", "PaymentSource", "Subscription", "GuildSettings", "UserSettings", "Webhook", "WebhookMessage", "Activity", "ActivityType", "ActivityFlags", "spotify_activity", "youtube_activity", "xbox_activity", "playstation_activity", "crunchyroll_activity", "custom_activity", "listening_activity", "streaming_activity", "competing_activity", "LOGO_MAP", "ComponentType", "ButtonStyle", "TextInputStyle", "Button", "SelectOption", "SelectMenu", "ChannelSelect", "RoleSelect", "MentionableSelect", "UserSelect", "TextInput", "ActionRow", "Modal", "Interaction", "InteractionType", "InteractionCallbackType", "InteractionHandler", "interaction_handler"]
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Rich presence and activity types.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from typing import Optional, List, Dict, Any, Union
|
|
7
|
+
from enum import IntEnum
|
|
8
|
+
|
|
9
|
+
class ActivityType(IntEnum):
|
|
10
|
+
PLAYING = 0
|
|
11
|
+
STREAMING = 1
|
|
12
|
+
LISTENING = 2
|
|
13
|
+
WATCHING = 3
|
|
14
|
+
CUSTOM = 4
|
|
15
|
+
COMPETING = 5
|
|
16
|
+
|
|
17
|
+
class ActivityFlags(IntEnum):
|
|
18
|
+
INSTANCE = 1 << 0
|
|
19
|
+
JOIN = 1 << 1
|
|
20
|
+
SPECTATE = 1 << 2
|
|
21
|
+
JOIN_REQUEST = 1 << 3
|
|
22
|
+
SYNC = 1 << 4
|
|
23
|
+
PLAY = 1 << 5
|
|
24
|
+
|
|
25
|
+
class Activity:
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
type: Union[ActivityType, int] = ActivityType.PLAYING,
|
|
29
|
+
name: str = "",
|
|
30
|
+
url: Optional[str] = None,
|
|
31
|
+
details: Optional[str] = None,
|
|
32
|
+
state: Optional[str] = None,
|
|
33
|
+
timestamps: Optional[Dict[str, int]] = None,
|
|
34
|
+
assets: Optional[Dict[str, str]] = None,
|
|
35
|
+
party: Optional[Dict[str, Any]] = None,
|
|
36
|
+
secrets: Optional[Dict[str, str]] = None,
|
|
37
|
+
instance: bool = False,
|
|
38
|
+
flags: int = 0,
|
|
39
|
+
buttons: Optional[List[str]] = None,
|
|
40
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
41
|
+
session_id: Optional[str] = None,
|
|
42
|
+
sync_id: Optional[str] = None,
|
|
43
|
+
application_id: Optional[str] = None,
|
|
44
|
+
platform: Optional[str] = None,
|
|
45
|
+
):
|
|
46
|
+
self.type = type if isinstance(type, ActivityType) else ActivityType(type)
|
|
47
|
+
self.name = name
|
|
48
|
+
self.url = url
|
|
49
|
+
self.details = details
|
|
50
|
+
self.state = state
|
|
51
|
+
self.timestamps = timestamps
|
|
52
|
+
self.assets = assets
|
|
53
|
+
self.party = party
|
|
54
|
+
self.secrets = secrets
|
|
55
|
+
self.instance = instance
|
|
56
|
+
self.flags = flags
|
|
57
|
+
self.buttons = buttons
|
|
58
|
+
self.metadata = metadata
|
|
59
|
+
self.session_id = session_id
|
|
60
|
+
self.sync_id = sync_id
|
|
61
|
+
self.application_id = application_id
|
|
62
|
+
self.platform = platform
|
|
63
|
+
|
|
64
|
+
def to_dict(self) -> dict:
|
|
65
|
+
data = {
|
|
66
|
+
"type": int(self.type),
|
|
67
|
+
"name": self.name[:128],
|
|
68
|
+
}
|
|
69
|
+
if self.url:
|
|
70
|
+
data["url"] = self.url
|
|
71
|
+
if self.details:
|
|
72
|
+
data["details"] = self.details[:128]
|
|
73
|
+
if self.state:
|
|
74
|
+
data["state"] = self.state[:128]
|
|
75
|
+
if self.timestamps:
|
|
76
|
+
data["timestamps"] = self.timestamps
|
|
77
|
+
if self.assets:
|
|
78
|
+
data["assets"] = self.assets
|
|
79
|
+
if self.party:
|
|
80
|
+
data["party"] = self.party
|
|
81
|
+
if self.secrets:
|
|
82
|
+
data["secrets"] = self.secrets
|
|
83
|
+
if self.instance:
|
|
84
|
+
data["instance"] = self.instance
|
|
85
|
+
if self.flags:
|
|
86
|
+
data["flags"] = self.flags
|
|
87
|
+
if self.buttons:
|
|
88
|
+
data["buttons"] = self.buttons[:2]
|
|
89
|
+
if self.metadata:
|
|
90
|
+
data["metadata"] = self.metadata
|
|
91
|
+
if self.session_id:
|
|
92
|
+
data["session_id"] = self.session_id
|
|
93
|
+
if self.sync_id:
|
|
94
|
+
data["sync_id"] = self.sync_id
|
|
95
|
+
if self.application_id:
|
|
96
|
+
data["application_id"] = self.application_id
|
|
97
|
+
if self.platform:
|
|
98
|
+
data["platform"] = self.platform
|
|
99
|
+
return data
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def from_dict(cls, data: dict) -> "Activity":
|
|
103
|
+
return cls(
|
|
104
|
+
type=data.get("type", 0),
|
|
105
|
+
name=data.get("name", ""),
|
|
106
|
+
url=data.get("url"),
|
|
107
|
+
details=data.get("details"),
|
|
108
|
+
state=data.get("state"),
|
|
109
|
+
timestamps=data.get("timestamps"),
|
|
110
|
+
assets=data.get("assets"),
|
|
111
|
+
party=data.get("party"),
|
|
112
|
+
secrets=data.get("secrets"),
|
|
113
|
+
instance=data.get("instance", False),
|
|
114
|
+
flags=data.get("flags", 0),
|
|
115
|
+
buttons=data.get("buttons"),
|
|
116
|
+
metadata=data.get("metadata"),
|
|
117
|
+
session_id=data.get("session_id"),
|
|
118
|
+
sync_id=data.get("sync_id"),
|
|
119
|
+
application_id=data.get("application_id"),
|
|
120
|
+
platform=data.get("platform"),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def __repr__(self) -> str:
|
|
124
|
+
return f"<Activity type={self.type} name={self.name!r}>"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def spotify_activity(
|
|
128
|
+
song: str,
|
|
129
|
+
artist: str,
|
|
130
|
+
album: str = "",
|
|
131
|
+
duration_minutes: float = 3.5,
|
|
132
|
+
current_position: float = 0,
|
|
133
|
+
image_url: Optional[str] = None,
|
|
134
|
+
track_id: str = None,
|
|
135
|
+
album_id: str = None,
|
|
136
|
+
artist_id: str = None,
|
|
137
|
+
) -> Activity:
|
|
138
|
+
total_ms = int(duration_minutes * 60 * 1000)
|
|
139
|
+
start_time = int(time.time() * 1000)
|
|
140
|
+
end_time = start_time + total_ms
|
|
141
|
+
|
|
142
|
+
track_id = track_id or "0VjIjW4GlUZAMYd2vXMi3b"
|
|
143
|
+
album_id = album_id or "4yP0hdKOZPNshxUOjY0cZj"
|
|
144
|
+
artist_id = artist_id or "1Xyo4u8uXC1ZmMpatF05PJ"
|
|
145
|
+
|
|
146
|
+
activity = Activity(
|
|
147
|
+
type=ActivityType.LISTENING,
|
|
148
|
+
name="Spotify",
|
|
149
|
+
details=song[:128],
|
|
150
|
+
state=artist[:128],
|
|
151
|
+
timestamps={"start": start_time, "end": end_time},
|
|
152
|
+
application_id="3201606009684",
|
|
153
|
+
sync_id=track_id,
|
|
154
|
+
session_id=f"spotify:{track_id}",
|
|
155
|
+
party={"id": f"spotify:{track_id}", "size": [1, 1]},
|
|
156
|
+
secrets={"join": track_id, "spectate": track_id, "match": track_id},
|
|
157
|
+
instance=True,
|
|
158
|
+
flags=48,
|
|
159
|
+
metadata={
|
|
160
|
+
"context_uri": f"spotify:album:{album_id}",
|
|
161
|
+
"album_id": album_id,
|
|
162
|
+
"artist_ids": [artist_id],
|
|
163
|
+
"track_id": track_id,
|
|
164
|
+
},
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if image_url:
|
|
168
|
+
activity.assets = {
|
|
169
|
+
"large_image": image_url,
|
|
170
|
+
"large_text": f"{album} on Spotify" if album else "Spotify",
|
|
171
|
+
"small_image": image_url,
|
|
172
|
+
"small_text": album or "Spotify",
|
|
173
|
+
}
|
|
174
|
+
else:
|
|
175
|
+
activity.assets = {"large_image": "spotify", "large_text": "Spotify"}
|
|
176
|
+
|
|
177
|
+
return activity
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def youtube_activity(
|
|
181
|
+
video_title: str,
|
|
182
|
+
channel_name: str,
|
|
183
|
+
elapsed_minutes: float = 0,
|
|
184
|
+
duration_minutes: float = 10,
|
|
185
|
+
image_url: Optional[str] = None,
|
|
186
|
+
) -> Activity:
|
|
187
|
+
cur_ms = int(elapsed_minutes * 60 * 1000)
|
|
188
|
+
tot_ms = int(duration_minutes * 60 * 1000)
|
|
189
|
+
start_time = int(time.time() * 1000)
|
|
190
|
+
|
|
191
|
+
activity = Activity(
|
|
192
|
+
type=ActivityType.WATCHING,
|
|
193
|
+
name="YouTube",
|
|
194
|
+
details=video_title[:128],
|
|
195
|
+
state=channel_name[:128],
|
|
196
|
+
timestamps={"start": start_time - cur_ms, "end": start_time - cur_ms + tot_ms},
|
|
197
|
+
application_id="111299001912",
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if image_url:
|
|
201
|
+
activity.assets = {"large_image": image_url}
|
|
202
|
+
|
|
203
|
+
return activity
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def xbox_activity(
|
|
207
|
+
game_name: str,
|
|
208
|
+
details: Optional[str] = None,
|
|
209
|
+
state: Optional[str] = None,
|
|
210
|
+
image_url: Optional[str] = None,
|
|
211
|
+
party_cur: Optional[int] = None,
|
|
212
|
+
party_max: Optional[int] = None,
|
|
213
|
+
) -> Activity:
|
|
214
|
+
activity = Activity(
|
|
215
|
+
type=ActivityType.PLAYING,
|
|
216
|
+
name=game_name[:128],
|
|
217
|
+
application_id="622174530214821906",
|
|
218
|
+
platform="xbox",
|
|
219
|
+
timestamps={"start": int(time.time() * 1000)},
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
if details:
|
|
223
|
+
activity.details = details[:128]
|
|
224
|
+
if state:
|
|
225
|
+
activity.state = state[:128]
|
|
226
|
+
if image_url:
|
|
227
|
+
activity.assets = {"large_image": image_url}
|
|
228
|
+
if party_cur and party_max:
|
|
229
|
+
activity.party = {"id": "xbox-party", "size": [party_cur, party_max]}
|
|
230
|
+
|
|
231
|
+
return activity
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def playstation_activity(
|
|
235
|
+
game_name: str,
|
|
236
|
+
platform: str = "ps5",
|
|
237
|
+
details: Optional[str] = None,
|
|
238
|
+
state: Optional[str] = None,
|
|
239
|
+
image_url: Optional[str] = None,
|
|
240
|
+
party_cur: Optional[int] = None,
|
|
241
|
+
party_max: Optional[int] = None,
|
|
242
|
+
) -> Activity:
|
|
243
|
+
activity = Activity(
|
|
244
|
+
type=ActivityType.PLAYING,
|
|
245
|
+
name=game_name[:128],
|
|
246
|
+
application_id="1470539864909943067",
|
|
247
|
+
platform=platform,
|
|
248
|
+
timestamps={"start": int(time.time() * 1000)},
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if details:
|
|
252
|
+
activity.details = details[:128]
|
|
253
|
+
if state:
|
|
254
|
+
activity.state = state[:128]
|
|
255
|
+
if image_url:
|
|
256
|
+
activity.assets = {"large_image": image_url}
|
|
257
|
+
if party_cur and party_max:
|
|
258
|
+
activity.party = {"id": "ps-party", "size": [party_cur, party_max]}
|
|
259
|
+
|
|
260
|
+
return activity
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def crunchyroll_activity(
|
|
264
|
+
anime_title: str,
|
|
265
|
+
episode_title: Optional[str] = None,
|
|
266
|
+
elapsed_minutes: float = 0,
|
|
267
|
+
total_minutes: float = 24,
|
|
268
|
+
image_url: Optional[str] = None,
|
|
269
|
+
) -> Activity:
|
|
270
|
+
activity = Activity(
|
|
271
|
+
type=ActivityType.WATCHING,
|
|
272
|
+
name="Crunchyroll",
|
|
273
|
+
details=anime_title[:128],
|
|
274
|
+
application_id="981509069309354054",
|
|
275
|
+
timestamps={
|
|
276
|
+
"start": int(time.time() * 1000) - int(elapsed_minutes * 60 * 1000),
|
|
277
|
+
"end": int(time.time() * 1000) - int(elapsed_minutes * 60 * 1000) + int(total_minutes * 60 * 1000),
|
|
278
|
+
},
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
if episode_title:
|
|
282
|
+
activity.state = episode_title[:128]
|
|
283
|
+
if image_url:
|
|
284
|
+
activity.assets = {"large_image": image_url}
|
|
285
|
+
|
|
286
|
+
return activity
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def custom_activity(
|
|
290
|
+
name: str,
|
|
291
|
+
activity_type: Union[ActivityType, int] = ActivityType.PLAYING,
|
|
292
|
+
details: Optional[str] = None,
|
|
293
|
+
state: Optional[str] = None,
|
|
294
|
+
url: Optional[str] = None,
|
|
295
|
+
image_url: Optional[str] = None,
|
|
296
|
+
application_id: Optional[str] = None,
|
|
297
|
+
platform: Optional[str] = None,
|
|
298
|
+
) -> Activity:
|
|
299
|
+
return Activity(
|
|
300
|
+
type=activity_type,
|
|
301
|
+
name=name[:128],
|
|
302
|
+
details=details[:128] if details else None,
|
|
303
|
+
state=state[:128] if state else None,
|
|
304
|
+
url=url,
|
|
305
|
+
assets={"large_image": image_url} if image_url else None,
|
|
306
|
+
application_id=application_id or "1487942541696434337",
|
|
307
|
+
platform=platform,
|
|
308
|
+
timestamps={"start": int(time.time() * 1000)},
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def listening_activity(
|
|
313
|
+
name: str,
|
|
314
|
+
details: Optional[str] = None,
|
|
315
|
+
state: Optional[str] = None,
|
|
316
|
+
image_url: Optional[str] = None,
|
|
317
|
+
total_minutes: Optional[float] = None,
|
|
318
|
+
elapsed_minutes: float = 0,
|
|
319
|
+
) -> Activity:
|
|
320
|
+
activity = Activity(
|
|
321
|
+
type=ActivityType.LISTENING,
|
|
322
|
+
name=name[:128],
|
|
323
|
+
application_id="1487942541696434337",
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
if details:
|
|
327
|
+
activity.details = details[:128]
|
|
328
|
+
if state:
|
|
329
|
+
activity.state = state[:128]
|
|
330
|
+
if image_url:
|
|
331
|
+
activity.assets = {"large_image": image_url}
|
|
332
|
+
if total_minutes:
|
|
333
|
+
activity.timestamps = {
|
|
334
|
+
"start": int(time.time() * 1000) - int(elapsed_minutes * 60 * 1000),
|
|
335
|
+
"end": int(time.time() * 1000) - int(elapsed_minutes * 60 * 1000) + int(total_minutes * 60 * 1000),
|
|
336
|
+
}
|
|
337
|
+
else:
|
|
338
|
+
activity.timestamps = {"start": int(time.time() * 1000)}
|
|
339
|
+
|
|
340
|
+
return activity
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def streaming_activity(
|
|
344
|
+
name: str,
|
|
345
|
+
url: str = "https://twitch.tv/kaicenat",
|
|
346
|
+
details: Optional[str] = None,
|
|
347
|
+
state: Optional[str] = None,
|
|
348
|
+
image_url: Optional[str] = None,
|
|
349
|
+
) -> Activity:
|
|
350
|
+
activity = Activity(
|
|
351
|
+
type=ActivityType.STREAMING,
|
|
352
|
+
name=name[:128],
|
|
353
|
+
url=url,
|
|
354
|
+
application_id="1487942541696434337",
|
|
355
|
+
timestamps={"start": int(time.time() * 1000)},
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
if details:
|
|
359
|
+
activity.details = details[:128]
|
|
360
|
+
if state:
|
|
361
|
+
activity.state = state[:128]
|
|
362
|
+
if image_url:
|
|
363
|
+
activity.assets = {"large_image": image_url}
|
|
364
|
+
|
|
365
|
+
return activity
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def competing_activity(
|
|
369
|
+
name: str,
|
|
370
|
+
details: Optional[str] = None,
|
|
371
|
+
state: Optional[str] = None,
|
|
372
|
+
image_url: Optional[str] = None,
|
|
373
|
+
) -> Activity:
|
|
374
|
+
activity = Activity(
|
|
375
|
+
type=ActivityType.COMPETING,
|
|
376
|
+
name=name[:128],
|
|
377
|
+
application_id="1487942541696434337",
|
|
378
|
+
timestamps={"start": int(time.time() * 1000)},
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
if details:
|
|
382
|
+
activity.details = details[:128]
|
|
383
|
+
if state:
|
|
384
|
+
activity.state = state[:128]
|
|
385
|
+
if image_url:
|
|
386
|
+
activity.assets = {"large_image": image_url}
|
|
387
|
+
|
|
388
|
+
return activity
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
LOGO_MAP = {
|
|
392
|
+
"xbox": ("622174530214821906", "xbox"),
|
|
393
|
+
"playstation": ("1470539864909943067", "ps5"),
|
|
394
|
+
"ps5": ("1470539864909943067", "ps5"),
|
|
395
|
+
"ps4": ("1470539864909943067", "ps4"),
|
|
396
|
+
"crunchyroll": ("981509069309354054", None),
|
|
397
|
+
"spotify": ("3201606009684", None),
|
|
398
|
+
"youtube": ("111299001912", None),
|
|
399
|
+
}
|
|
@@ -26,7 +26,6 @@ from .models.webhook import Webhook, WebhookMessage
|
|
|
26
26
|
from .headers import HeaderSpoofer, EMULATION
|
|
27
27
|
from .utils import send_notification, START_IMAGE, ERROR_IMAGE
|
|
28
28
|
from .interactions import Interaction, InteractionHandler, interaction_handler
|
|
29
|
-
from .voice import VoiceManager, VoiceClient, VoiceState
|
|
30
29
|
|
|
31
30
|
logger = logging.getLogger(__name__)
|
|
32
31
|
|
|
@@ -91,12 +90,9 @@ class Client:
|
|
|
91
90
|
|
|
92
91
|
self._interaction_handlers: Dict[str, Callable] = {}
|
|
93
92
|
|
|
94
|
-
self._voice = VoiceManager(self)
|
|
95
|
-
|
|
96
93
|
self._dispatcher.on("READY", self._on_ready_internal)
|
|
97
94
|
self._dispatcher.on("MESSAGE_CREATE", self._on_message_create_internal)
|
|
98
95
|
self._dispatcher.on("INTERACTION_CREATE", self._on_interaction_create)
|
|
99
|
-
self._dispatcher.on("VOICE_STATE_UPDATE", self._on_voice_state_update)
|
|
100
96
|
|
|
101
97
|
if self._notifications:
|
|
102
98
|
send_notification(
|
|
@@ -134,11 +130,6 @@ class Client:
|
|
|
134
130
|
def cogs(self) -> List[Cog]:
|
|
135
131
|
return list(self._cogs.values())
|
|
136
132
|
|
|
137
|
-
@property
|
|
138
|
-
def voice(self) -> VoiceManager:
|
|
139
|
-
"""Access voice manager."""
|
|
140
|
-
return self._voice
|
|
141
|
-
|
|
142
133
|
def event(self, coro: Callable):
|
|
143
134
|
if not asyncio.iscoroutinefunction(coro):
|
|
144
135
|
raise TypeError("Event handlers must be coroutines")
|
|
@@ -214,7 +205,6 @@ class Client:
|
|
|
214
205
|
return self._cogs.get(name)
|
|
215
206
|
|
|
216
207
|
def interaction_handler(self, custom_id: str):
|
|
217
|
-
"""Decorator to register an interaction handler."""
|
|
218
208
|
def decorator(func: Callable):
|
|
219
209
|
self._interaction_handlers[custom_id] = func
|
|
220
210
|
return func
|
|
@@ -238,7 +228,6 @@ class Client:
|
|
|
238
228
|
await self._process_commands(message)
|
|
239
229
|
|
|
240
230
|
async def _on_interaction_create(self, data: dict):
|
|
241
|
-
"""Handle interaction_create events."""
|
|
242
231
|
try:
|
|
243
232
|
interaction = Interaction(self._state, data)
|
|
244
233
|
|
|
@@ -249,15 +238,6 @@ class Client:
|
|
|
249
238
|
except Exception as e:
|
|
250
239
|
logger.exception(f"Error handling interaction: {e}")
|
|
251
240
|
|
|
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
|
-
|
|
261
241
|
async def _process_commands(self, message: Message):
|
|
262
242
|
if not self.user or message.author.id != self.user.id:
|
|
263
243
|
return
|
|
@@ -373,7 +353,6 @@ class Client:
|
|
|
373
353
|
self._closed = True
|
|
374
354
|
self._ready.clear()
|
|
375
355
|
|
|
376
|
-
await self._voice.disconnect_all()
|
|
377
356
|
await self._gateway.close()
|
|
378
357
|
await self._http.close()
|
|
379
358
|
|
|
@@ -421,9 +400,6 @@ class Client:
|
|
|
421
400
|
async def wait_until_ready(self):
|
|
422
401
|
await self._ready.wait()
|
|
423
402
|
|
|
424
|
-
# ============================================================
|
|
425
|
-
# FETCH METHODS
|
|
426
|
-
# ============================================================
|
|
427
403
|
async def fetch_user(self, user_id: int):
|
|
428
404
|
data = await self._http.request(Route.user(user_id))
|
|
429
405
|
return self._state._add_user(data)
|
|
@@ -449,9 +425,6 @@ class Client:
|
|
|
449
425
|
def get_user(self, user_id: int):
|
|
450
426
|
return self._state._users.get(user_id)
|
|
451
427
|
|
|
452
|
-
# ============================================================
|
|
453
|
-
# RELATIONSHIPS
|
|
454
|
-
# ============================================================
|
|
455
428
|
async def get_relationships(self) -> List[Relationship]:
|
|
456
429
|
data = await self._http.request(Route("GET", "/users/@me/relationships"))
|
|
457
430
|
return [Relationship(state=self._state, data=r) for r in data]
|
|
@@ -479,9 +452,6 @@ class Client:
|
|
|
479
452
|
Route("DELETE", f"/users/@me/relationships/{user_id}")
|
|
480
453
|
)
|
|
481
454
|
|
|
482
|
-
# ============================================================
|
|
483
|
-
# BILLING
|
|
484
|
-
# ============================================================
|
|
485
455
|
async def get_payment_sources(self) -> List[PaymentSource]:
|
|
486
456
|
data = await self._http.request(Route("GET", "/users/@me/billing/payment-sources"))
|
|
487
457
|
return [PaymentSource(state=self._state, data=s) for s in data]
|
|
@@ -496,9 +466,6 @@ class Client:
|
|
|
496
466
|
async def get_skus(self) -> List[Dict[str, Any]]:
|
|
497
467
|
return await self._http.request(Route("GET", "/users/@me/entitlements/skus"))
|
|
498
468
|
|
|
499
|
-
# ============================================================
|
|
500
|
-
# SETTINGS
|
|
501
|
-
# ============================================================
|
|
502
469
|
async def get_user_settings(self) -> UserSettings:
|
|
503
470
|
data = await self._http.request(Route("GET", "/users/@me/settings"))
|
|
504
471
|
return UserSettings(data)
|
|
@@ -533,9 +500,6 @@ class Client:
|
|
|
533
500
|
json=kwargs
|
|
534
501
|
)
|
|
535
502
|
|
|
536
|
-
# ============================================================
|
|
537
|
-
# PROFILE
|
|
538
|
-
# ============================================================
|
|
539
503
|
async def get_profile(self, user_id: int = None) -> Dict[str, Any]:
|
|
540
504
|
endpoint = f"/users/{user_id}/profile" if user_id else "/users/@me/profile"
|
|
541
505
|
return await self._http.request(Route("GET", endpoint))
|
|
@@ -567,16 +531,11 @@ class Client:
|
|
|
567
531
|
async def get_connections(self) -> List[Dict[str, Any]]:
|
|
568
532
|
return await self._http.request(Route("GET", "/users/@me/connections"))
|
|
569
533
|
|
|
570
|
-
# ============================================================
|
|
571
|
-
# WEBHOOKS
|
|
572
|
-
# ============================================================
|
|
573
534
|
async def get_webhook(self, webhook_id: int) -> Webhook:
|
|
574
|
-
"""Get a webhook by ID."""
|
|
575
535
|
data = await self._http.request(Route("GET", f"/webhooks/{webhook_id}"))
|
|
576
536
|
return Webhook(state=self._state, data=data)
|
|
577
537
|
|
|
578
538
|
async def get_webhook_with_token(self, webhook_id: int, token: str) -> Webhook:
|
|
579
|
-
"""Get a webhook by ID and token."""
|
|
580
539
|
data = await self._http.request(
|
|
581
540
|
method="GET",
|
|
582
541
|
url=f"/webhooks/{webhook_id}/{token}",
|
|
@@ -584,12 +543,10 @@ class Client:
|
|
|
584
543
|
return Webhook(state=self._state, data=data)
|
|
585
544
|
|
|
586
545
|
async def get_channel_webhooks(self, channel_id: int) -> List[Webhook]:
|
|
587
|
-
"""Get all webhooks in a channel."""
|
|
588
546
|
data = await self._http.request(Route("GET", f"/channels/{channel_id}/webhooks"))
|
|
589
547
|
return [Webhook(state=self._state, data=w) for w in data]
|
|
590
548
|
|
|
591
549
|
async def get_guild_webhooks(self, guild_id: int) -> List[Webhook]:
|
|
592
|
-
"""Get all webhooks in a guild."""
|
|
593
550
|
data = await self._http.request(Route("GET", f"/guilds/{guild_id}/webhooks"))
|
|
594
551
|
return [Webhook(state=self._state, data=w) for w in data]
|
|
595
552
|
|
|
@@ -599,7 +556,6 @@ class Client:
|
|
|
599
556
|
name: str,
|
|
600
557
|
avatar: Optional[str] = None,
|
|
601
558
|
) -> Webhook:
|
|
602
|
-
"""Create a webhook in a channel."""
|
|
603
559
|
payload = {"name": name}
|
|
604
560
|
if avatar:
|
|
605
561
|
payload["avatar"] = avatar
|
|
@@ -607,32 +563,4 @@ class Client:
|
|
|
607
563
|
Route("POST", f"/channels/{channel_id}/webhooks"),
|
|
608
564
|
json=payload,
|
|
609
565
|
)
|
|
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)
|
|
566
|
+
return Webhook(state=self._state, data=data)
|
|
@@ -43,7 +43,7 @@ class Context:
|
|
|
43
43
|
@property
|
|
44
44
|
def channel(self) -> "Channel | None":
|
|
45
45
|
if self._cs_channel is None:
|
|
46
|
-
self._cs_channel = self.message.
|
|
46
|
+
self._cs_channel = self.bot.get_channel(self.message.channel_id)
|
|
47
47
|
return self._cs_channel
|
|
48
48
|
|
|
49
49
|
@property
|
|
@@ -72,8 +72,9 @@ class Context:
|
|
|
72
72
|
|
|
73
73
|
async def send(self, content: str | None = None, **kwargs):
|
|
74
74
|
"""Send a message to the current channel."""
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
ch = self.channel
|
|
76
|
+
if ch:
|
|
77
|
+
return await ch.send(content, **kwargs)
|
|
77
78
|
raise RuntimeError("Cannot send: no channel available")
|
|
78
79
|
|
|
79
80
|
async def reply(self, content: str | None = None, **kwargs):
|
|
@@ -82,16 +83,18 @@ class Context:
|
|
|
82
83
|
|
|
83
84
|
async def trigger_typing(self):
|
|
84
85
|
"""Trigger typing in the current channel."""
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
ch = self.channel
|
|
87
|
+
if ch:
|
|
88
|
+
await ch.typing()
|
|
87
89
|
|
|
88
90
|
async def fetch_message(self, message_id: int):
|
|
89
91
|
"""Fetch a message from the current channel."""
|
|
90
|
-
|
|
92
|
+
ch = self.channel
|
|
93
|
+
if ch:
|
|
91
94
|
data = await self.bot._state.http.get_messages(
|
|
92
|
-
|
|
95
|
+
ch.id, limit=1, before=message_id + 1
|
|
93
96
|
)
|
|
94
97
|
if data:
|
|
95
98
|
from ..models.message import Message
|
|
96
99
|
return Message(state=self.bot._state, data=data[0])
|
|
97
|
-
return None
|
|
100
|
+
return None
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .user import User
|
|
2
|
+
from .guild import Guild
|
|
3
|
+
from .channel import Channel, DMChannel, GroupChannel, channel_factory
|
|
4
|
+
from .message import Message
|
|
5
|
+
from .member import Member
|
|
6
|
+
from .base import DiscordObject
|
|
7
|
+
from .relationship import Relationship
|
|
8
|
+
from .billing import PaymentSource, Subscription
|
|
9
|
+
from .settings import GuildSettings, UserSettings
|
|
10
|
+
from .webhook import Webhook, WebhookMessage
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"User",
|
|
14
|
+
"Guild",
|
|
15
|
+
"Channel",
|
|
16
|
+
"DMChannel",
|
|
17
|
+
"GroupChannel",
|
|
18
|
+
"channel_factory",
|
|
19
|
+
"Message",
|
|
20
|
+
"Member",
|
|
21
|
+
"DiscordObject",
|
|
22
|
+
"Relationship",
|
|
23
|
+
"PaymentSource",
|
|
24
|
+
"Subscription",
|
|
25
|
+
"GuildSettings",
|
|
26
|
+
"UserSettings",
|
|
27
|
+
"Webhook",
|
|
28
|
+
"WebhookMessage",
|
|
29
|
+
]
|
|
File without changes
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Discord entity models.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from .base import DiscordObject
|
|
6
|
-
from .user import User
|
|
7
|
-
from .guild import Guild
|
|
8
|
-
from .channel import Channel, TextChannel, DMChannel, VoiceChannel, CategoryChannel
|
|
9
|
-
from .message import Message
|
|
10
|
-
from .member import Member
|
|
11
|
-
|
|
12
|
-
__all__ = [
|
|
13
|
-
"DiscordObject",
|
|
14
|
-
"User",
|
|
15
|
-
"Guild",
|
|
16
|
-
"Channel",
|
|
17
|
-
"TextChannel",
|
|
18
|
-
"DMChannel",
|
|
19
|
-
"VoiceChannel",
|
|
20
|
-
"CategoryChannel",
|
|
21
|
-
"Message",
|
|
22
|
-
"Member",
|
|
23
|
-
]
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|