scurrypy 0.4.2__py3-none-any.whl → 0.5.3__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.
- scurrypy/__init__.py +376 -0
- {discord → scurrypy}/client_like.py +1 -1
- {discord → scurrypy}/dispatch/command_dispatcher.py +13 -5
- {discord → scurrypy}/dispatch/event_dispatcher.py +15 -15
- {discord → scurrypy}/events/channel_events.py +1 -1
- {discord → scurrypy}/events/interaction_events.py +21 -9
- {discord → scurrypy}/events/message_events.py +4 -4
- {discord → scurrypy}/http.py +1 -1
- {discord → scurrypy}/intents.py +1 -1
- scurrypy/model.py +71 -0
- {discord → scurrypy}/models/emoji.py +17 -1
- {discord → scurrypy}/models/interaction.py +5 -0
- scurrypy/parts/channel.py +42 -0
- scurrypy/parts/command.py +90 -0
- scurrypy/parts/components.py +224 -0
- scurrypy/parts/components_v2.py +144 -0
- scurrypy/parts/embed.py +83 -0
- scurrypy/parts/message.py +137 -0
- scurrypy/parts/modal.py +16 -0
- {discord → scurrypy}/parts/role.py +1 -13
- {discord → scurrypy}/resources/channel.py +6 -6
- {discord → scurrypy}/resources/guild.py +3 -4
- {discord → scurrypy}/resources/interaction.py +23 -22
- {discord → scurrypy}/resources/message.py +13 -13
- {scurrypy-0.4.2.dist-info → scurrypy-0.5.3.dist-info}/METADATA +19 -25
- scurrypy-0.5.3.dist-info/RECORD +54 -0
- scurrypy-0.5.3.dist-info/top_level.txt +1 -0
- discord/__init__.py +0 -223
- discord/model.py +0 -90
- discord/parts/action_row.py +0 -208
- discord/parts/channel.py +0 -20
- discord/parts/command.py +0 -102
- discord/parts/components_v2.py +0 -353
- discord/parts/embed.py +0 -154
- discord/parts/message.py +0 -194
- discord/parts/modal.py +0 -21
- scurrypy-0.4.2.dist-info/RECORD +0 -54
- scurrypy-0.4.2.dist-info/top_level.txt +0 -1
- {discord → scurrypy}/client.py +0 -0
- {discord → scurrypy}/config.py +0 -0
- {discord → scurrypy}/dispatch/__init__.py +0 -0
- {discord → scurrypy}/dispatch/prefix_dispatcher.py +0 -0
- {discord → scurrypy}/error.py +0 -0
- {discord → scurrypy}/events/__init__.py +0 -0
- {discord → scurrypy}/events/guild_events.py +0 -0
- {discord → scurrypy}/events/hello_event.py +0 -0
- {discord → scurrypy}/events/reaction_events.py +0 -0
- {discord → scurrypy}/events/ready_event.py +0 -0
- {discord → scurrypy}/gateway.py +0 -0
- {discord → scurrypy}/logger.py +0 -0
- {discord → scurrypy}/models/__init__.py +0 -0
- {discord → scurrypy}/models/application.py +0 -0
- {discord → scurrypy}/models/guild.py +0 -0
- {discord → scurrypy}/models/integration.py +0 -0
- {discord → scurrypy}/models/member.py +0 -0
- {discord → scurrypy}/models/role.py +0 -0
- {discord → scurrypy}/models/user.py +0 -0
- {discord → scurrypy}/parts/__init__.py +0 -0
- {discord → scurrypy}/parts/component_types.py +0 -0
- {discord → scurrypy}/resources/__init__.py +0 -0
- {discord → scurrypy}/resources/application.py +0 -0
- {discord → scurrypy}/resources/bot_emojis.py +0 -0
- {discord → scurrypy}/resources/user.py +0 -0
- {scurrypy-0.4.2.dist-info → scurrypy-0.5.3.dist-info}/WHEEL +0 -0
- {scurrypy-0.4.2.dist-info → scurrypy-0.5.3.dist-info}/licenses/LICENSE +0 -0
scurrypy/parts/embed.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from ..model import DataModel
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class EmbedAuthor(DataModel):
|
|
7
|
+
"""Embed author parameters."""
|
|
8
|
+
|
|
9
|
+
name: str
|
|
10
|
+
"""Name of the author."""
|
|
11
|
+
|
|
12
|
+
url: Optional[str] = None
|
|
13
|
+
"""URL of the author. http or attachment://<filename> scheme."""
|
|
14
|
+
|
|
15
|
+
icon_url: Optional[str] = None
|
|
16
|
+
"""URL of author's icon. http or attachment://<filename> scheme."""
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class EmbedThumbnail(DataModel):
|
|
20
|
+
"""Embed thumbnail."""
|
|
21
|
+
|
|
22
|
+
url: str
|
|
23
|
+
"""Thumbnail content. http or attachment://<filename> scheme."""
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class EmbedField(DataModel):
|
|
27
|
+
"""Embed field."""
|
|
28
|
+
|
|
29
|
+
name: str
|
|
30
|
+
"""Name of the field."""
|
|
31
|
+
|
|
32
|
+
value: str
|
|
33
|
+
"""Value of the field."""
|
|
34
|
+
|
|
35
|
+
inline: Optional[bool] = None
|
|
36
|
+
"""Whether or not this field should display inline."""
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class EmbedImage(DataModel):
|
|
40
|
+
"""Embed image."""
|
|
41
|
+
|
|
42
|
+
url: str
|
|
43
|
+
"""Image content. http or attachment://<filename> scheme."""
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class EmbedFooter(DataModel):
|
|
47
|
+
"""Embed footer."""
|
|
48
|
+
text: str
|
|
49
|
+
"""Footer text."""
|
|
50
|
+
|
|
51
|
+
icon_url: Optional[str] = None
|
|
52
|
+
"""URL of the footer icon. http or attachment://<filename> scheme."""
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class EmbedPart(DataModel):
|
|
56
|
+
"""Represents the Embed portion of a message."""
|
|
57
|
+
|
|
58
|
+
title: Optional[str] = None
|
|
59
|
+
"""This embed's title."""
|
|
60
|
+
|
|
61
|
+
description: Optional[str] = None
|
|
62
|
+
"""This embed's description."""
|
|
63
|
+
|
|
64
|
+
timestamp: Optional[str] = None
|
|
65
|
+
"""Timestamp of when the embed was sent."""
|
|
66
|
+
|
|
67
|
+
color: Optional[int] = None
|
|
68
|
+
"""Embed's accent color."""
|
|
69
|
+
|
|
70
|
+
author: Optional[EmbedAuthor] = None
|
|
71
|
+
"""Embed's author."""
|
|
72
|
+
|
|
73
|
+
thumbnail: Optional[EmbedThumbnail] = None
|
|
74
|
+
"""Embed's thumbnail attachment."""
|
|
75
|
+
|
|
76
|
+
image: Optional[EmbedImage] = None
|
|
77
|
+
"""Embed's image attachment."""
|
|
78
|
+
|
|
79
|
+
fields: Optional[list[EmbedField]] = field(default_factory=list)
|
|
80
|
+
"""List of embed's fields."""
|
|
81
|
+
|
|
82
|
+
footer: Optional[EmbedFooter] = None
|
|
83
|
+
"""Embed's footer."""
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional, TypedDict, Unpack
|
|
3
|
+
from ..model import DataModel
|
|
4
|
+
from .embed import EmbedPart
|
|
5
|
+
from .components import ActionRowPart
|
|
6
|
+
from .components_v2 import ContainerPart
|
|
7
|
+
|
|
8
|
+
class MessageFlags:
|
|
9
|
+
"""Flags that can be applied to a message."""
|
|
10
|
+
|
|
11
|
+
CROSSPOSTED = 1 << 0
|
|
12
|
+
"""Message has been published."""
|
|
13
|
+
|
|
14
|
+
IS_CROSSPOST = 1 << 1
|
|
15
|
+
"""Message originated from another channel."""
|
|
16
|
+
|
|
17
|
+
SUPPRESS_EMBEDS = 1 << 2
|
|
18
|
+
"""Hide embeds (if any)."""
|
|
19
|
+
|
|
20
|
+
EPHEMERAL = 1 << 6
|
|
21
|
+
"""Only visible to the invoking user."""
|
|
22
|
+
|
|
23
|
+
LOADING = 1 << 7
|
|
24
|
+
"""Thinking response."""
|
|
25
|
+
|
|
26
|
+
IS_COMPONENTS_V2 = 1 << 15
|
|
27
|
+
"""This message includes Discord's V2 Components."""
|
|
28
|
+
|
|
29
|
+
class MessageFlagParams(TypedDict, total=False):
|
|
30
|
+
"""Parameters for setting message flags. See [`MessageFlags`][scurrypy.parts.message.MessageFlags]."""
|
|
31
|
+
crossposted: bool
|
|
32
|
+
is_crosspost: bool
|
|
33
|
+
suppress_embeds: bool
|
|
34
|
+
ephemeral: bool
|
|
35
|
+
loading: bool
|
|
36
|
+
is_components_v2: bool
|
|
37
|
+
|
|
38
|
+
class MessageReferenceTypes:
|
|
39
|
+
"""Constants associated with how reference data is populated."""
|
|
40
|
+
|
|
41
|
+
DEFAULT = 0
|
|
42
|
+
"""Standard reference used by replies."""
|
|
43
|
+
|
|
44
|
+
FORWARD = 1
|
|
45
|
+
"""Reference used to point to a message at a point in time."""
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class MessageReference(DataModel):
|
|
49
|
+
"""Represents the Message Reference object."""
|
|
50
|
+
|
|
51
|
+
message_id: int
|
|
52
|
+
"""ID of the originating message."""
|
|
53
|
+
|
|
54
|
+
channel_id: int
|
|
55
|
+
"""
|
|
56
|
+
Channel ID of the originating message.
|
|
57
|
+
!!! note
|
|
58
|
+
Optional for default type, but REQUIRED for forwards.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
type: int = MessageReferenceTypes.DEFAULT
|
|
62
|
+
"""Type of reference. Defaults to `DEFAULT`. See [`MessageReferenceTypes`][scurrypy.parts.message.MessageReferenceTypes]."""
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class Attachment(DataModel):
|
|
66
|
+
"""Represents an attachment."""
|
|
67
|
+
|
|
68
|
+
id: int
|
|
69
|
+
"""User-defined ID for the attachment."""
|
|
70
|
+
|
|
71
|
+
path: str
|
|
72
|
+
"""Relative path to the file."""
|
|
73
|
+
|
|
74
|
+
filename: str
|
|
75
|
+
"""Name of the file."""
|
|
76
|
+
|
|
77
|
+
description: str
|
|
78
|
+
"""Description of the file."""
|
|
79
|
+
|
|
80
|
+
def to_dict(self):
|
|
81
|
+
return {
|
|
82
|
+
'id': self.id,
|
|
83
|
+
'filename': self.filename,
|
|
84
|
+
'description': self.description
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@dataclass
|
|
88
|
+
class MessagePart(DataModel):
|
|
89
|
+
"""Describes expected params when editing/creating a message."""
|
|
90
|
+
|
|
91
|
+
content: Optional[str] = None
|
|
92
|
+
"""Message text content."""
|
|
93
|
+
|
|
94
|
+
flags: Optional[int] = 0
|
|
95
|
+
"""Message flags. See [`MessageFlags`][scurrypy.parts.message.MessageFlags]."""
|
|
96
|
+
|
|
97
|
+
components: Optional[list[ActionRowPart | ContainerPart]] = field(default_factory=list)
|
|
98
|
+
"""Components to be attached to this message."""
|
|
99
|
+
|
|
100
|
+
attachments: Optional[list[Attachment]] = field(default_factory=list)
|
|
101
|
+
"""Attachments to be attached to this message."""
|
|
102
|
+
|
|
103
|
+
embeds: Optional[list[EmbedPart]] = field(default_factory=list)
|
|
104
|
+
"""Embeds to be attached to this message."""
|
|
105
|
+
|
|
106
|
+
message_reference: Optional[MessageReference] = None
|
|
107
|
+
"""Message reference if reply."""
|
|
108
|
+
|
|
109
|
+
def set_flags(self, **flags: Unpack[MessageFlagParams]):
|
|
110
|
+
"""Set this message's flags using MessageFlagParams.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
flags (Unpack[MessageFlagParams]): message flags to set. (set respective flag to True to toggle.)
|
|
114
|
+
|
|
115
|
+
Raises:
|
|
116
|
+
(ValueError): invalid flag
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
(MessagePart): self
|
|
120
|
+
"""
|
|
121
|
+
_flag_map = {
|
|
122
|
+
'crossposted': MessageFlags.CROSSPOSTED,
|
|
123
|
+
'is_crosspost': MessageFlags.IS_CROSSPOST,
|
|
124
|
+
'suppress_embeds': MessageFlags.SUPPRESS_EMBEDS,
|
|
125
|
+
'ephemeral': MessageFlags.EPHEMERAL,
|
|
126
|
+
'loading': MessageFlags.LOADING,
|
|
127
|
+
'is_components_v2': MessageFlags.IS_COMPONENTS_V2,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
# each flag maps to a specific combined bit!
|
|
131
|
+
for name, value in flags.items():
|
|
132
|
+
if name not in _flag_map:
|
|
133
|
+
raise ValueError(f"Invalid flag: {name}")
|
|
134
|
+
if value:
|
|
135
|
+
self.flags |= _flag_map[name]
|
|
136
|
+
|
|
137
|
+
return self
|
scurrypy/parts/modal.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from ..model import DataModel
|
|
3
|
+
from .components_v2 import Label
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class ModalPart(DataModel):
|
|
7
|
+
"""Represents the Modal object."""
|
|
8
|
+
|
|
9
|
+
title: str
|
|
10
|
+
"""Title of the popup modal."""
|
|
11
|
+
|
|
12
|
+
custom_id: str = None
|
|
13
|
+
"""ID for the modal."""
|
|
14
|
+
|
|
15
|
+
components: list[Label] = field(default_factory=list)
|
|
16
|
+
"""1 to 5 components that make up the modal."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Optional
|
|
3
|
-
from
|
|
3
|
+
from ..model import DataModel
|
|
4
4
|
|
|
5
5
|
from ..models.role import RoleColors
|
|
6
6
|
|
|
@@ -25,15 +25,3 @@ class Role(DataModel):
|
|
|
25
25
|
|
|
26
26
|
unicode_emoji: Optional[str] = None
|
|
27
27
|
"""Unicode emoji of the role."""
|
|
28
|
-
|
|
29
|
-
def set_color(self, hex: str):
|
|
30
|
-
"""Set this role's color with a hex. (format: #FFFFFF)
|
|
31
|
-
|
|
32
|
-
Args:
|
|
33
|
-
hex (str): color as a hex code
|
|
34
|
-
|
|
35
|
-
Returns:
|
|
36
|
-
(Role): self
|
|
37
|
-
"""
|
|
38
|
-
self.color=int(hex.strip('#'), 16)
|
|
39
|
-
return self
|
|
@@ -6,7 +6,7 @@ from ..model import DataModel
|
|
|
6
6
|
from .message import Message
|
|
7
7
|
|
|
8
8
|
from ..parts.channel import GuildChannel
|
|
9
|
-
from ..parts.message import
|
|
9
|
+
from ..parts.message import MessagePart
|
|
10
10
|
|
|
11
11
|
class MessagesFetchParams(TypedDict, total=False):
|
|
12
12
|
"""Params when fetching guild channel messages."""
|
|
@@ -132,7 +132,7 @@ class Channel(DataModel):
|
|
|
132
132
|
|
|
133
133
|
return [Message.from_dict(msg, self._http) for msg in data]
|
|
134
134
|
|
|
135
|
-
async def send(self, message: str |
|
|
135
|
+
async def send(self, message: str | MessagePart):
|
|
136
136
|
"""
|
|
137
137
|
Send a message to this channel.
|
|
138
138
|
|
|
@@ -140,15 +140,15 @@ class Channel(DataModel):
|
|
|
140
140
|
* SEND_MESSAGES → required to create a message in this channel
|
|
141
141
|
|
|
142
142
|
Args:
|
|
143
|
-
message (str |
|
|
143
|
+
message (str | MessagePart): can be just text or the MessagePart for dynamic messages
|
|
144
144
|
|
|
145
145
|
Returns:
|
|
146
146
|
(Message): The created Message object
|
|
147
147
|
"""
|
|
148
148
|
if isinstance(message, str):
|
|
149
|
-
message =
|
|
149
|
+
message = MessagePart(content=message)
|
|
150
150
|
|
|
151
|
-
data = await self._http.request("POST", f"/channels/{self.id}/messages", data=message.
|
|
151
|
+
data = await self._http.request("POST", f"/channels/{self.id}/messages", data=message.to_dict())
|
|
152
152
|
|
|
153
153
|
return Message.from_dict(data, self._http)
|
|
154
154
|
|
|
@@ -164,7 +164,7 @@ class Channel(DataModel):
|
|
|
164
164
|
Returns:
|
|
165
165
|
(Channel): The updated channel object
|
|
166
166
|
"""
|
|
167
|
-
data = await self._http.request("PATCH", f"/channels/{self.id}", data=channel.
|
|
167
|
+
data = await self._http.request("PATCH", f"/channels/{self.id}", data=channel.to_dict())
|
|
168
168
|
self._update(data)
|
|
169
169
|
|
|
170
170
|
return self
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Optional, TypedDict, Unpack
|
|
3
|
-
from urllib.parse import urlencode
|
|
4
3
|
|
|
5
4
|
from ..http import HTTPClient
|
|
6
5
|
from ..model import DataModel
|
|
@@ -137,7 +136,7 @@ class Guild(DataModel):
|
|
|
137
136
|
Returns:
|
|
138
137
|
(Channel): the created channel
|
|
139
138
|
"""
|
|
140
|
-
data = await self._http.request('POST', f'/guilds/{self.id}/channels', data=channel.
|
|
139
|
+
data = await self._http.request('POST', f'/guilds/{self.id}/channels', data=channel.to_dict())
|
|
141
140
|
|
|
142
141
|
return Channel.from_dict(data, self._http)
|
|
143
142
|
|
|
@@ -234,7 +233,7 @@ class Guild(DataModel):
|
|
|
234
233
|
Returns:
|
|
235
234
|
(RoleModel): new role data
|
|
236
235
|
"""
|
|
237
|
-
data = await self._http.request('POST', f'/guilds/{self.id}/roles', data=role.
|
|
236
|
+
data = await self._http.request('POST', f'/guilds/{self.id}/roles', data=role.to_dict())
|
|
238
237
|
|
|
239
238
|
return RoleModel.from_dict(data)
|
|
240
239
|
|
|
@@ -250,7 +249,7 @@ class Guild(DataModel):
|
|
|
250
249
|
Returns:
|
|
251
250
|
(RoleModel): role with changes
|
|
252
251
|
"""
|
|
253
|
-
data = await self._http.request('PATCH', f'/guilds/{self.id}/roles/{role_id}', data=role.
|
|
252
|
+
data = await self._http.request('PATCH', f'/guilds/{self.id}/roles/{role_id}', data=role.to_dict())
|
|
254
253
|
|
|
255
254
|
return RoleModel.from_dict(data)
|
|
256
255
|
|
|
@@ -4,8 +4,8 @@ from typing import Optional, Unpack
|
|
|
4
4
|
from ..http import HTTPClient
|
|
5
5
|
from ..model import DataModel
|
|
6
6
|
|
|
7
|
-
from ..parts.modal import
|
|
8
|
-
from ..parts.message import
|
|
7
|
+
from ..parts.modal import ModalPart
|
|
8
|
+
from ..parts.message import MessagePart, MessageFlagParams
|
|
9
9
|
|
|
10
10
|
from ..models.guild import GuildModel
|
|
11
11
|
from ..models.member import MemberModel
|
|
@@ -38,8 +38,9 @@ class InteractionCallbackTypes:
|
|
|
38
38
|
"""Acknowledge an interaction and edit a response later. User sees a loading state."""
|
|
39
39
|
|
|
40
40
|
DEFERRED_UPDATE_MESSAGE = 6
|
|
41
|
-
"""
|
|
42
|
-
|
|
41
|
+
"""
|
|
42
|
+
Acknowledge an interaction and edit the original message later.
|
|
43
|
+
The user does NOT see a loading state. (Components only)
|
|
43
44
|
"""
|
|
44
45
|
|
|
45
46
|
UPDATE_MESSAGE = 7
|
|
@@ -68,7 +69,7 @@ class Interaction(DataModel):
|
|
|
68
69
|
"""HTTP session for requests."""
|
|
69
70
|
|
|
70
71
|
type: int
|
|
71
|
-
"""Type of interaction."""
|
|
72
|
+
"""Type of interaction. See [`InteractionTypes`][scurrypy.dispatch.command_dispatcher.InteractionTypes]."""
|
|
72
73
|
|
|
73
74
|
channel_id: int
|
|
74
75
|
"""ID of the channel where the interaction was sent."""
|
|
@@ -97,24 +98,24 @@ class Interaction(DataModel):
|
|
|
97
98
|
channel: Optional[Channel] = None
|
|
98
99
|
"""Partial channel object the interaction was invoked."""
|
|
99
100
|
|
|
100
|
-
async def respond(self, message: str |
|
|
101
|
+
async def respond(self, message: str | MessagePart, with_response: bool = False, **flags: Unpack[MessageFlagParams]):
|
|
101
102
|
"""Create a message in response to an interaction.
|
|
102
103
|
|
|
103
104
|
Args:
|
|
104
|
-
message (str |
|
|
105
|
+
message (str | MessagePart): content as a string or from MessagePart
|
|
105
106
|
with_response (bool, optional): if the interaction data should be returned. Defaults to False.
|
|
106
107
|
|
|
107
108
|
Raises:
|
|
108
109
|
TypeError: invalid type
|
|
109
110
|
"""
|
|
110
111
|
if isinstance(message, str):
|
|
111
|
-
message =
|
|
112
|
-
elif not isinstance(message,
|
|
113
|
-
raise TypeError(f"Interaction.respond expects type str or
|
|
112
|
+
message = MessagePart(content=message).set_flags(**flags)
|
|
113
|
+
elif not isinstance(message, MessagePart):
|
|
114
|
+
raise TypeError(f"Interaction.respond expects type str or MessagePart, got {type(message).__name__}")
|
|
114
115
|
|
|
115
116
|
content = {
|
|
116
117
|
'type': InteractionCallbackTypes.CHANNEL_MESSAGE_WITH_SOURCE,
|
|
117
|
-
'data': message.
|
|
118
|
+
'data': message.to_dict()
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
params = {'with_response': with_response}
|
|
@@ -129,23 +130,23 @@ class Interaction(DataModel):
|
|
|
129
130
|
if with_response:
|
|
130
131
|
return InteractionCallbackModel.from_dict(data, self._http)
|
|
131
132
|
|
|
132
|
-
async def update(self, message: str |
|
|
133
|
+
async def update(self, message: str | MessagePart, **flags: Unpack[MessageFlagParams]):
|
|
133
134
|
"""Update a message in response to an interaction.
|
|
134
135
|
|
|
135
136
|
Args:
|
|
136
|
-
message (str |
|
|
137
|
+
message (str | MessagePart): content as a string or from MessagePart
|
|
137
138
|
|
|
138
139
|
Raises:
|
|
139
140
|
TypeError: invalid type
|
|
140
141
|
"""
|
|
141
142
|
if isinstance(message, str):
|
|
142
|
-
message =
|
|
143
|
-
elif not isinstance(message,
|
|
144
|
-
raise TypeError(f"Interaction.update expects type str or
|
|
143
|
+
message = MessagePart(content=message).set_flags(**flags)
|
|
144
|
+
elif not isinstance(message, MessagePart):
|
|
145
|
+
raise TypeError(f"Interaction.update expects type str or MessagePart, got {type(message).__name__}")
|
|
145
146
|
|
|
146
147
|
content = {
|
|
147
148
|
'type': InteractionCallbackTypes.UPDATE_MESSAGE,
|
|
148
|
-
'data': message.
|
|
149
|
+
'data': message.to_dict()
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
await self._http.request(
|
|
@@ -154,21 +155,21 @@ class Interaction(DataModel):
|
|
|
154
155
|
data=content,
|
|
155
156
|
files=[fp.path for fp in message.attachments])
|
|
156
157
|
|
|
157
|
-
async def respond_modal(self, modal:
|
|
158
|
+
async def respond_modal(self, modal: ModalPart):
|
|
158
159
|
"""Create a modal in response to an interaction.
|
|
159
160
|
|
|
160
161
|
Args:
|
|
161
|
-
modal (
|
|
162
|
+
modal (ModalPart): modal data
|
|
162
163
|
|
|
163
164
|
Raises:
|
|
164
165
|
TypeError: invalid type
|
|
165
166
|
"""
|
|
166
|
-
if not isinstance(modal,
|
|
167
|
-
raise TypeError(f"Interaction.respond_modal expects type
|
|
167
|
+
if not isinstance(modal, ModalPart):
|
|
168
|
+
raise TypeError(f"Interaction.respond_modal expects type ModalPart, got {type(modal).__name__}")
|
|
168
169
|
|
|
169
170
|
content = {
|
|
170
171
|
'type': InteractionCallbackTypes.MODAL,
|
|
171
|
-
'data': modal.
|
|
172
|
+
'data': modal.to_dict()
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
await self._http.request(
|
|
@@ -6,7 +6,7 @@ from ..model import DataModel
|
|
|
6
6
|
|
|
7
7
|
from ..models.user import UserModel
|
|
8
8
|
from ..models.emoji import EmojiModel
|
|
9
|
-
from ..parts.message import
|
|
9
|
+
from ..parts.message import MessagePart
|
|
10
10
|
|
|
11
11
|
@dataclass
|
|
12
12
|
class Message(DataModel):
|
|
@@ -50,67 +50,67 @@ class Message(DataModel):
|
|
|
50
50
|
|
|
51
51
|
return Message.from_dict(data, self._http)
|
|
52
52
|
|
|
53
|
-
async def send(self, message: str |
|
|
53
|
+
async def send(self, message: str | MessagePart):
|
|
54
54
|
"""Sends a new message to the current channel.
|
|
55
55
|
|
|
56
56
|
Permissions:
|
|
57
57
|
* SEND_MESSAGES → required to senf your own messages
|
|
58
58
|
|
|
59
59
|
Args:
|
|
60
|
-
message (str |
|
|
60
|
+
message (str | MessagePart): can be just text or the MessagePart for dynamic messages
|
|
61
61
|
|
|
62
62
|
Returns:
|
|
63
63
|
(Message): the new Message object with all fields populated
|
|
64
64
|
"""
|
|
65
65
|
if isinstance(message, str):
|
|
66
|
-
message =
|
|
66
|
+
message = MessagePart(content=message)
|
|
67
67
|
|
|
68
68
|
data = await self._http.request(
|
|
69
69
|
"POST",
|
|
70
70
|
f"/channels/{self.channel_id}/messages",
|
|
71
|
-
data=message.
|
|
71
|
+
data=message.to_dict(),
|
|
72
72
|
files=[fp.path for fp in message.attachments] if message.attachments else None
|
|
73
73
|
)
|
|
74
74
|
return Message.from_dict(data, self._http)
|
|
75
75
|
|
|
76
|
-
async def edit(self, message: str |
|
|
76
|
+
async def edit(self, message: str | MessagePart):
|
|
77
77
|
"""Edits this message.
|
|
78
78
|
|
|
79
79
|
Permissions:
|
|
80
80
|
* MANAGE_MESSAGES → ONLY if editing another user's message
|
|
81
81
|
|
|
82
82
|
Args:
|
|
83
|
-
message (str |
|
|
83
|
+
message (str | MessagePart): can be just text or the MessagePart for dynamic messages
|
|
84
84
|
"""
|
|
85
85
|
if isinstance(message, str):
|
|
86
|
-
message =
|
|
86
|
+
message = MessagePart(content=message)
|
|
87
87
|
|
|
88
88
|
data = await self._http.request(
|
|
89
89
|
"PATCH",
|
|
90
90
|
f"/channels/{self.channel_id}/messages/{self.id}",
|
|
91
|
-
data=message.
|
|
91
|
+
data=message.to_dict(),
|
|
92
92
|
files=[fp.path for fp in message.attachments] if message.attachments else None)
|
|
93
93
|
|
|
94
94
|
self._update(data)
|
|
95
95
|
|
|
96
|
-
async def reply(self, message: str |
|
|
96
|
+
async def reply(self, message: str | MessagePart):
|
|
97
97
|
"""Reply to this message with a new message.
|
|
98
98
|
|
|
99
99
|
Permissions:
|
|
100
100
|
* SEND_MESSAGES → required to send the message
|
|
101
101
|
|
|
102
102
|
Args:
|
|
103
|
-
message (str |
|
|
103
|
+
message (str | MessagePart): the new message
|
|
104
104
|
"""
|
|
105
105
|
if isinstance(message, str):
|
|
106
|
-
message =
|
|
106
|
+
message = MessagePart(content=message)
|
|
107
107
|
|
|
108
108
|
message = message._set_reference(self.id, self.channel_id)
|
|
109
109
|
|
|
110
110
|
await self._http.request(
|
|
111
111
|
'POST',
|
|
112
112
|
f"/channels/{self.channel_id}/messages",
|
|
113
|
-
data=message.
|
|
113
|
+
data=message.to_dict(),
|
|
114
114
|
files=[fp.path for fp in message.attachments] if message.attachments else None)
|
|
115
115
|
|
|
116
116
|
async def crosspost(self):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scurrypy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.3
|
|
4
4
|
Summary: Dataclass-driven Discord API Wrapper in Python
|
|
5
5
|
Author: Furmissile
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -8,7 +8,7 @@ Description-Content-Type: text/markdown
|
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Dynamic: license-file
|
|
10
10
|
|
|
11
|
-
#
|
|
11
|
+
# __ScurryPy__
|
|
12
12
|
|
|
13
13
|
[](https://badge.fury.io/py/scurrypy)
|
|
14
14
|
|
|
@@ -20,22 +20,17 @@ A dataclass-driven Discord API wrapper in Python!
|
|
|
20
20
|
|
|
21
21
|
While this wrapper is mainly used for various squirrel-related shenanigans, it can also be used for more generic bot purposes.
|
|
22
22
|
|
|
23
|
-
---
|
|
24
|
-
|
|
25
23
|
## Features
|
|
26
|
-
* Command and event handling
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
|
|
31
|
-
---
|
|
24
|
+
* Command, and event handling
|
|
25
|
+
* Unix shell-style wildcards for component routing
|
|
26
|
+
* Declarative style using decorators
|
|
27
|
+
* Supports both legacy and new features
|
|
28
|
+
* Respects Discord’s rate limits
|
|
32
29
|
|
|
33
|
-
##
|
|
30
|
+
## Notes & Early Status
|
|
34
31
|
* 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.
|
|
35
32
|
* Certain features are not yet supported, while others are intentionally omitted. See the [docs](https://furmissile.github.io/scurrypy) for full details.
|
|
36
33
|
|
|
37
|
-
---
|
|
38
|
-
|
|
39
34
|
## Getting Started
|
|
40
35
|
*Note: This section also appears in the documentation, but here are complete examples ready to use with your bot credentials.*
|
|
41
36
|
|
|
@@ -51,24 +46,24 @@ The following demonstrates building and responding to a slash command.
|
|
|
51
46
|
*Note: Adjust `dotenv_path` if your `.env` file is not in the same directory as this script.*
|
|
52
47
|
|
|
53
48
|
```py
|
|
54
|
-
import
|
|
49
|
+
import scurrypy, os
|
|
55
50
|
from dotenv import load_dotenv
|
|
56
51
|
|
|
57
52
|
load_dotenv(dotenv_path='./path/to/env')
|
|
58
53
|
|
|
59
|
-
client =
|
|
54
|
+
client = scurrypy.Client(
|
|
60
55
|
token=os.getenv("DISCORD_TOKEN"),
|
|
61
|
-
application_id=APPLICATION_ID #
|
|
56
|
+
application_id=APPLICATION_ID # your bot’s application ID
|
|
62
57
|
)
|
|
63
58
|
|
|
64
59
|
@client.command(
|
|
65
|
-
command=
|
|
60
|
+
command=scurrypy.SlashCommand(
|
|
66
61
|
name='example',
|
|
67
62
|
description='Demonstrate the minimal slash command!'
|
|
68
63
|
),
|
|
69
64
|
guild_ids=GUILD_ID # must be a guild ID your bot is in
|
|
70
65
|
)
|
|
71
|
-
async def example(bot:
|
|
66
|
+
async def example(bot: scurrypy.Client, event: scurrypy.InteractionEvent):
|
|
72
67
|
await event.interaction.respond(f'Hello, {event.interaction.member.user.username}!')
|
|
73
68
|
|
|
74
69
|
client.run()
|
|
@@ -77,20 +72,20 @@ client.run()
|
|
|
77
72
|
## Minimal Prefix Command (Legacy)
|
|
78
73
|
The following demonstrates building and responding to a message prefix command.
|
|
79
74
|
```py
|
|
80
|
-
import
|
|
75
|
+
import scurrypy, os
|
|
81
76
|
from dotenv import load_dotenv
|
|
82
77
|
|
|
83
78
|
load_dotenv(dotenv_path='./path/to/env')
|
|
84
79
|
|
|
85
|
-
client =
|
|
80
|
+
client = scurrypy.Client(
|
|
86
81
|
token=os.getenv("DISCORD_TOKEN"),
|
|
87
|
-
application_id=APPLICATION_ID
|
|
88
|
-
intents=
|
|
82
|
+
application_id=APPLICATION_ID # your bot’s application ID
|
|
83
|
+
intents=scurrypy.set_intents(message_content=True),
|
|
89
84
|
prefix='!' # your custom prefix
|
|
90
85
|
)
|
|
91
86
|
|
|
92
87
|
@client.prefix_command
|
|
93
|
-
async def ping(bot:
|
|
88
|
+
async def ping(bot: scurrypy.Client, event: scurrypy.MessageCreateEvent):
|
|
94
89
|
# The function name is the name of the command
|
|
95
90
|
await event.message.send("Pong!")
|
|
96
91
|
|
|
@@ -126,5 +121,4 @@ If you plan to make substantial changes or release your own variant:
|
|
|
126
121
|
See the [License](./LICENSE) for details.
|
|
127
122
|
|
|
128
123
|
## Like What You See?
|
|
129
|
-
|
|
130
|
-
for more examples, guides, and API reference!
|
|
124
|
+
Explore the full [documentation](https://furmissile.github.io/scurrypy) for more examples, guides, and API reference.
|