scurrypy 0.1.0__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.

Files changed (52) hide show
  1. discord/__init__.py +9 -0
  2. discord/client.py +312 -0
  3. discord/dispatch/__init__.py +1 -0
  4. discord/dispatch/command_dispatcher.py +156 -0
  5. discord/dispatch/event_dispatcher.py +85 -0
  6. discord/dispatch/prefix_dispatcher.py +53 -0
  7. discord/error.py +63 -0
  8. discord/events/__init__.py +33 -0
  9. discord/events/channel_events.py +52 -0
  10. discord/events/guild_events.py +38 -0
  11. discord/events/hello_event.py +9 -0
  12. discord/events/interaction_events.py +145 -0
  13. discord/events/message_events.py +43 -0
  14. discord/events/reaction_events.py +99 -0
  15. discord/events/ready_event.py +30 -0
  16. discord/gateway.py +175 -0
  17. discord/http.py +292 -0
  18. discord/intents.py +87 -0
  19. discord/logger.py +147 -0
  20. discord/model.py +88 -0
  21. discord/models/__init__.py +8 -0
  22. discord/models/application.py +37 -0
  23. discord/models/emoji.py +34 -0
  24. discord/models/guild.py +35 -0
  25. discord/models/integration.py +23 -0
  26. discord/models/member.py +27 -0
  27. discord/models/role.py +53 -0
  28. discord/models/user.py +15 -0
  29. discord/parts/__init__.py +28 -0
  30. discord/parts/action_row.py +258 -0
  31. discord/parts/attachment.py +18 -0
  32. discord/parts/channel.py +20 -0
  33. discord/parts/command.py +102 -0
  34. discord/parts/component_types.py +5 -0
  35. discord/parts/components_v2.py +270 -0
  36. discord/parts/embed.py +154 -0
  37. discord/parts/message.py +179 -0
  38. discord/parts/modal.py +21 -0
  39. discord/parts/role.py +39 -0
  40. discord/resources/__init__.py +10 -0
  41. discord/resources/application.py +94 -0
  42. discord/resources/bot_emojis.py +49 -0
  43. discord/resources/channel.py +192 -0
  44. discord/resources/guild.py +265 -0
  45. discord/resources/interaction.py +155 -0
  46. discord/resources/message.py +223 -0
  47. discord/resources/user.py +111 -0
  48. scurrypy-0.1.0.dist-info/METADATA +8 -0
  49. scurrypy-0.1.0.dist-info/RECORD +52 -0
  50. scurrypy-0.1.0.dist-info/WHEEL +5 -0
  51. scurrypy-0.1.0.dist-info/licenses/LICENSE +5 -0
  52. scurrypy-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,270 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Literal, Optional
3
+ from ..model import DataModel
4
+
5
+ from .component_types import *
6
+ from .action_row import StringSelect, ActionRow
7
+
8
+ class _TextInputStyles:
9
+ """Represents the types of Text Inputs."""
10
+ SHORT = 1 # one line
11
+ PARAGRAPH = 2 # multiple lines
12
+
13
+ @dataclass
14
+ class _TextInput(DataModel, LabelChild):
15
+ """Represents the Text Input component."""
16
+ type: Literal[4] = field(init=False, default=4)
17
+ style: _TextInputStyles = _TextInputStyles.SHORT # refer to _TextInputStyles for details
18
+ custom_id: str = None
19
+ required: Optional[bool] = False
20
+ min_length: Optional[int] = None
21
+ max_length: Optional[int] = None
22
+ value: Optional[str] = None
23
+ placeholder: Optional[str] = None
24
+
25
+ @dataclass
26
+ class Section(DataModel, ContainerChild):
27
+ """Represents the Section component."""
28
+ type: Literal[9] = field(init=False, default=9)
29
+ accessory: Optional[SectionAccessory] = None
30
+ components: list[SectionChild] = field(default_factory=list)
31
+
32
+ @dataclass
33
+ class _TextDisplay(DataModel, ContainerChild, SectionChild):
34
+ """Represents the Text Display component."""
35
+ type: Literal[10] = field(init=False, default=10)
36
+ content: str
37
+
38
+
39
+ @dataclass
40
+ class _Thumbnail(DataModel, SectionAccessory):
41
+ """Represents the _Thumbnail component."""
42
+ type: Literal[11] = field(init=False, default=11)
43
+ media: str # http or attachment://<filename>
44
+ description: Optional[str] = None
45
+ spoiler: Optional[bool] = False
46
+
47
+
48
+ @dataclass
49
+ class MediaGalleryItem(DataModel):
50
+ """Represents the Media Gallery Item component."""
51
+
52
+ media: str
53
+ """Image data. http or attachment://<filename> scheme."""
54
+
55
+ description: Optional[str] = None
56
+ """Alt text for the media."""
57
+
58
+ spoiler: Optional[bool] = False
59
+ """If the media should be blurred out."""
60
+
61
+ @dataclass
62
+ class _MediaGallery(DataModel, ContainerChild):
63
+ """Represents the Media Gallery component."""
64
+ type: Literal[12] = field(init=False, default=12)
65
+ items: list[MediaGalleryItem] = field(default_factory=list)
66
+
67
+
68
+ @dataclass
69
+ class _File(DataModel, ContainerChild):
70
+ """Represents the File component."""
71
+ type: Literal[13] = field(init=False, default=13)
72
+ file: str # http or attachment://<filename>
73
+ spoiler: Optional[bool] = False
74
+
75
+ class _SeparatorTypes:
76
+ """Represents separator types constants."""
77
+ SMALL_PADDING = 1
78
+ LARGE_PADDING = 2
79
+
80
+ @dataclass
81
+ class _Separator(DataModel, ContainerChild):
82
+ """Represents the Separator component."""
83
+ type: Literal[14] = field(init=False, default=14)
84
+ divider: bool = True
85
+ spacing: Optional[int] = _SeparatorTypes.SMALL_PADDING # refer to _SeparatorTypes
86
+
87
+ @dataclass
88
+ class Label(DataModel):
89
+ """Represents the Discord Label component."""
90
+ label: str
91
+ """Label text."""
92
+
93
+ component: LabelChild = None
94
+ """A component within the label."""
95
+
96
+ description: Optional[str] = None
97
+ """An optional description text for the label."""
98
+
99
+ type: Literal[18] = field(init=False, default=18)
100
+
101
+ def string_select(self, select: StringSelect):
102
+ """Set this label to be a string select component.
103
+
104
+ Args:
105
+ select (StringSelect): the string select component
106
+
107
+ Returns:
108
+ (Label): self
109
+ """
110
+ self.component = select
111
+ return self
112
+
113
+ def text_input(self,
114
+ *,
115
+ custom_id: str,
116
+ min_length: int,
117
+ max_length: int,
118
+ value: str = None,
119
+ style: Literal['Short', 'Paragraph'] = 'Short',
120
+ placeholder: str = None,
121
+ require: bool = False
122
+ ):
123
+ """Set this label to be a text input component.
124
+
125
+ Args:
126
+ custom_id (str): developer-defined component ID
127
+ min_length (int): minimum number of characters required
128
+ max_length (int): maximum number of characters required
129
+ value (str, optional): component value
130
+ style (Literal['Short', 'Paragraph'], optional):
131
+ text format. Defaults to 'Short'.
132
+ placeholder (str, optional): custom placeholder text if empty
133
+ require (bool, optional): if input is required. Defaults to False.
134
+
135
+ Returns:
136
+ (Label): self
137
+ """
138
+ _styles = {
139
+ 'SHORT': _TextInputStyles.SHORT,
140
+ 'PARAGRAPH': _TextInputStyles.PARAGRAPH
141
+ }
142
+
143
+ self.component = _TextInput(
144
+ style = _styles.get(style.upper()),
145
+ placeholder=placeholder,
146
+ custom_id=custom_id,
147
+ min_length=min_length,
148
+ max_length=max_length,
149
+ value=value,
150
+ required=require
151
+ )
152
+ return self
153
+
154
+ @dataclass
155
+ class Container(DataModel):
156
+ """Represents a container of display and interactable components."""
157
+ type: Literal[17] = field(init=False, default=17)
158
+
159
+ components: list[ContainerChild] = field(default_factory=list)
160
+ """Child components that are encapsulated within the Container."""
161
+
162
+ accent_color: Optional[int] = None
163
+ """Color for the accent as an integer."""
164
+
165
+ spoiler: Optional[bool] = False
166
+ """If the container should be blurred out."""
167
+
168
+ def set_color(self, hex: str):
169
+ """Set this container's color with a hex. (format: #FFFFFF)
170
+
171
+ Args:
172
+ hex (str): color as a hex code
173
+
174
+ Returns:
175
+ (Container): self
176
+ """
177
+ self.accent_color = int(hex.strip('#'), 16)
178
+ return self
179
+
180
+ def add_row(self, row: ActionRow):
181
+ """Add an action row to this container.
182
+
183
+ Args:
184
+ row (ActionRow): the ActionRow object
185
+
186
+ Returns:
187
+ (Container): self
188
+ """
189
+ self.components.append(row)
190
+ return self
191
+
192
+ def add_section(self, section: Section):
193
+ """Add a section to this container.
194
+
195
+ Args:
196
+ section (Section): the Section object
197
+
198
+ Returns:
199
+ (Container): self
200
+ """
201
+ self.components.append(section)
202
+ return self
203
+
204
+ def add_text_display(self, content: str):
205
+ """Add a text display to this container.
206
+
207
+ Args:
208
+ content (str): the content to display
209
+
210
+ Returns:
211
+ (Container): self
212
+ """
213
+ self.components.append(_TextDisplay(content))
214
+ return self
215
+
216
+ def set_thumbnail(self, media: str, description: str = None, has_spoiler: bool = False):
217
+ """Set the thumbnail for this container
218
+
219
+ Args:
220
+ media (str): Image data. http or attachment://<filename> scheme.
221
+ description (str, optional): Alt text for the media
222
+ has_spoiler (bool, optional): If the media should be blurred out. Defaults to False.
223
+
224
+ Returns:
225
+ (Container): self
226
+ """
227
+ self.components.append(_Thumbnail(media, description, has_spoiler))
228
+ return self
229
+
230
+ def set_media_gallery(self, items: list[MediaGalleryItem]):
231
+ """Add a media gallery to this container.
232
+
233
+ Args:
234
+ items (list[MediaGalleryItem]): list of media gallery images
235
+
236
+ Returns:
237
+ (Container): self
238
+ """
239
+ self.components.append(_MediaGallery(items))
240
+ return self
241
+
242
+ def add_attachment(self, file: str, has_spoiler: bool = False):
243
+ """Add a single attachment to this container.
244
+
245
+ Args:
246
+ file (str): Image data. http or attachment://<filename> scheme
247
+ has_spoiler (bool, optional): If the media should be blurred out. Defaults to False.
248
+
249
+ Returns:
250
+ (Container): self
251
+ """
252
+ self.components.append(_File(file, has_spoiler))
253
+ return self
254
+
255
+ def add_separator(self, spacing: Literal['Small', 'Large'] = 'Small', has_divider: bool = True):
256
+ """Add a separator to this container. Positionally accurate.
257
+
258
+ Args:
259
+ spacing (Literal['Small', 'Large'], optional): size of separator padding. Defaults to 'Small'.
260
+ has_divider (bool, optional): if a visual divider should be shown. Defaults to True.
261
+
262
+ Returns:
263
+ (Container): self
264
+ """
265
+ _spacing_types = {
266
+ 'SMALL': _SeparatorTypes.SMALL_PADDING,
267
+ 'LARGE': _SeparatorTypes.LARGE_PADDING
268
+ }
269
+ self.components.append(_Separator(divider=has_divider, spacing=_spacing_types.get(spacing.upper())))
270
+ return self
discord/parts/embed.py ADDED
@@ -0,0 +1,154 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+ from discord.model import DataModel
4
+ from datetime import datetime, timezone
5
+ from ..models.user import UserModel
6
+
7
+ @dataclass
8
+ class _EmbedAuthor(DataModel):
9
+ """Embed author parameters."""
10
+ name: str
11
+ url: Optional[str] = None
12
+ icon_url: Optional[str] = None
13
+
14
+ @dataclass
15
+ class _EmbedThumbnail(DataModel):
16
+ """Embed thumbnail."""
17
+ url: str
18
+
19
+ @dataclass
20
+ class _EmbedImage(DataModel):
21
+ """Embed image."""
22
+ url: str
23
+
24
+ @dataclass
25
+ class _EmbedFooter(DataModel):
26
+ """Embed footer."""
27
+ text: str
28
+ url: Optional[str] = None
29
+ icon_url: Optional[str] = None
30
+
31
+ @dataclass
32
+ class _EmbedField(DataModel):
33
+ """Embed field."""
34
+ name: str
35
+ value: str
36
+ inline: Optional[bool] = None
37
+
38
+ @dataclass
39
+ class EmbedBuilder(DataModel):
40
+ """Represents the Embed portion of a message."""
41
+
42
+ title: Optional[str] = None
43
+ """This embed's title."""
44
+
45
+ description: Optional[str] = None
46
+ """This embed's description."""
47
+
48
+ timestamp: Optional[str] = None
49
+ """Timestamp of when the embed was sent."""
50
+
51
+ color: Optional[int] = None
52
+ """Embed's accent color."""
53
+
54
+ author: Optional[_EmbedAuthor] = None
55
+ """Embed's author."""
56
+
57
+ thumbnail: Optional[_EmbedThumbnail] = None
58
+ """Embed's thumbnail attachment."""
59
+
60
+ image: Optional[_EmbedImage] = None
61
+ """Embed's image attachment."""
62
+
63
+ fields: Optional[list[_EmbedField]] = None
64
+ """List of embed's fields."""
65
+
66
+ footer: Optional[_EmbedFooter] = None
67
+ """Embed's footer."""
68
+
69
+ def set_color(self, hex: str):
70
+ """Set this embed's color with a hex.
71
+
72
+ Args:
73
+ hex (str): color as a hex code (format: #FFFFFF)
74
+
75
+ Returns:
76
+ (EmbedBuilder): self
77
+ """
78
+ self.color=int(hex.strip('#'), 16)
79
+ return self
80
+
81
+ def set_user_author(self, user: UserModel):
82
+ """Set this embed's author.
83
+
84
+ Args:
85
+ user (UserModel): the user model
86
+
87
+ Returns:
88
+ (EmbedBuilder): self
89
+ """
90
+ self.author = _EmbedAuthor(
91
+ name=user.username,
92
+ icon_url=f"https://cdn.discordapp.com/avatars/{user.id}/{user.avatar}.png"
93
+ )
94
+ return self
95
+
96
+ def set_image(self, url: str):
97
+ """Set this embed's image.
98
+
99
+ Args:
100
+ url (str): attachment://<file> scheme or http(s) URL
101
+
102
+ Returns:
103
+ (EmbedBuilder): self
104
+ """
105
+ self.image = _EmbedImage(url=url)
106
+ return self
107
+
108
+ def set_thumbnail(self, url: str):
109
+ """Set this embed's thumbnail.
110
+
111
+ Args:
112
+ url (str): attachment://<file> scheme or http(s) URL
113
+
114
+ Returns:
115
+ (EmbedBuilder): self
116
+ """
117
+ self.thumbnail = _EmbedThumbnail(url=url)
118
+ return self
119
+
120
+ def set_footer(self, text: str, icon_url: str = None):
121
+ """Set this embed's footer.
122
+
123
+ Args:
124
+ text (str): footer's text
125
+ icon_url (str, optional): attachment://<file> scheme or http(s) URL.
126
+
127
+ Returns:
128
+ (EmbedBuilder): self
129
+ """
130
+ self.footer = _EmbedFooter(text=text, icon_url=icon_url)
131
+ return self
132
+
133
+ def add_field(self, name: str, value: str, is_inline: bool = False):
134
+ """Add a field to this embed.
135
+
136
+ Args:
137
+ name (str): field's title
138
+ value (str): field's text
139
+ is_inline (bool): if this field should be inlined
140
+
141
+ Returns:
142
+ (EmbedBuilder): self
143
+ """
144
+ self.fields.append(_EmbedField(name=name, value=value, inline=is_inline))
145
+ return self
146
+
147
+ def set_timestamp(self):
148
+ """Set this embed's timestamp.
149
+
150
+ Returns:
151
+ (EmbedBuilder): self
152
+ """
153
+ self.timestamp = datetime.now(timezone.utc).isoformat()
154
+ return self
@@ -0,0 +1,179 @@
1
+ import os
2
+ from dataclasses import dataclass, field
3
+ from typing import Optional, TypedDict, Unpack, Literal
4
+ from discord.model import DataModel
5
+ from .embed import EmbedBuilder
6
+ from .action_row import ActionRow
7
+ from .components_v2 import Container
8
+ from .attachment import _Attachment
9
+
10
+ class MessageFlags:
11
+ """Flags that can be applied to a message."""
12
+ CROSSPOSTED = 1 << 0
13
+ """Message has been published."""
14
+
15
+ IS_CROSSPOST = 1 << 1
16
+ """Message originated from another channel."""
17
+
18
+ SUPPRESS_EMBEDS = 1 << 2
19
+ """Hide embeds (if any)."""
20
+
21
+ EPHEMERAL = 1 << 6
22
+ """Only visible to the invoking user."""
23
+
24
+ LOADING = 1 << 7
25
+ """Thinking response."""
26
+
27
+ IS_COMPONENTS_V2 = 1 << 15
28
+ """This message includes Discord's V2 Components."""
29
+
30
+ class MessageFlagParams(TypedDict, total=False):
31
+ """Parameters for setting message flags."""
32
+ crossposted: bool
33
+ is_crosspost: bool
34
+ suppress_embeds: bool
35
+ ephemeral: bool
36
+ loading: bool
37
+ is_components_v2: bool
38
+
39
+ @dataclass
40
+ class _MessageReference(DataModel):
41
+ message_id: int
42
+ channel_id: int
43
+ type: int = 0
44
+
45
+ @dataclass
46
+ class MessageBuilder(DataModel):
47
+ """Describes expected params when editing/creating a message."""
48
+
49
+ content: Optional[str] = None
50
+ """Message text content."""
51
+
52
+ flags: Optional[int] = 0
53
+ """Message flags. See [`MessageFlags`][discord.parts.message.MessageFlags] for details."""
54
+
55
+ components: Optional[list[ActionRow | Container]] = field(default_factory=list)
56
+ """Components to be attached to this message."""
57
+
58
+ attachments: Optional[list[_Attachment]] = field(default_factory=list)
59
+ """Attachments to be attached to this message."""
60
+
61
+ embeds: Optional[list[EmbedBuilder]] = field(default_factory=list)
62
+ """Embeds to be attached to this message."""
63
+
64
+ message_reference: Optional[_MessageReference] = None
65
+ """Message reference if reply."""
66
+
67
+ def add_row(self, row: ActionRow):
68
+ """Add an action row to this message.
69
+
70
+ Args:
71
+ row (ActionRow): the ActionRow object
72
+
73
+ Returns:
74
+ (MessageBuilder): self
75
+ """
76
+ self.components.append(row)
77
+ return self
78
+
79
+ def add_container(self, container: Container, *, has_container_boarder: bool = False):
80
+ """Add a container to this message.
81
+
82
+ Args:
83
+ container (Container): the Container object.
84
+ has_container_boarder (bool, optional): If message should be contained in an Embed-like container. Defaults to False.
85
+
86
+ Returns:
87
+ (MessageBuilder): self
88
+ """
89
+ if has_container_boarder:
90
+ self.components.append(container)
91
+ else:
92
+ self.components.extend(container.components)
93
+ return self
94
+
95
+ def add_embed(self, embed: EmbedBuilder):
96
+ """Add an embed to this message.
97
+
98
+ Args:
99
+ embed (EmbedBuilder): The EmbedBuilder object.
100
+
101
+ Returns:
102
+ (MessageBuilder): self
103
+ """
104
+ self.embeds.append(embed)
105
+ return self
106
+
107
+ def add_attachment(self, file_path: str, description: str = None):
108
+ """Add an attachment to this message
109
+
110
+ Args:
111
+ file_path (str): full qualifying path to file
112
+ description (str, optional): file descriptor. Defaults to None.
113
+
114
+ Returns:
115
+ (MessageBuilder): self
116
+ """
117
+ self.attachments.append(
118
+ _Attachment(
119
+ id=len(self.attachments),
120
+ filename=os.path.basename(file_path),
121
+ path=file_path,
122
+ description=description
123
+ )
124
+ )
125
+ return self
126
+
127
+ def set_flags(self, **flags: Unpack[MessageFlagParams]):
128
+ """Set this message's flags using MessageFlagParams.
129
+
130
+ Args:
131
+ flags (Unpack[MessageFlagParams]): message flags to set. (set respective flag to True to toggle.)
132
+
133
+ Raises:
134
+ (ValueError): invalid flag
135
+
136
+ Returns:
137
+ (MessageBuilder): self
138
+ """
139
+ _flag_map = {
140
+ 'crossposted': MessageFlags.CROSSPOSTED,
141
+ 'is_crosspost': MessageFlags.IS_CROSSPOST,
142
+ 'suppress_embeds': MessageFlags.SUPPRESS_EMBEDS,
143
+ 'ephemeral': MessageFlags.EPHEMERAL,
144
+ 'loading': MessageFlags.LOADING,
145
+ 'is_components_v2': MessageFlags.IS_COMPONENTS_V2,
146
+ }
147
+
148
+ # each flag maps to a specific combined bit!
149
+ for name, value in flags.items():
150
+ if name not in _flag_map:
151
+ raise ValueError(f"Invalid flag: {name}")
152
+ if value:
153
+ self.flags |= _flag_map[name]
154
+
155
+ return self
156
+
157
+ def _set_reference(self,
158
+ message_id: int,
159
+ channel_id: int,
160
+ ref_type: Literal['Default', 'Forward'] = 'Default'
161
+ ):
162
+ """Internal helper for setting this message's reference message. Used in replies.
163
+
164
+ Args:
165
+ message_id (int): message to reference
166
+
167
+ Returns:
168
+ (MessageBuilder): self
169
+ """
170
+ _ref_types = {
171
+ 'DEFAULT': 0,
172
+ 'FORWARD': 1
173
+ }
174
+ self.message_reference = _MessageReference(
175
+ type=_ref_types.get(ref_type.upper()),
176
+ channel_id=channel_id,
177
+ message_id=message_id
178
+ )
179
+ return self
discord/parts/modal.py ADDED
@@ -0,0 +1,21 @@
1
+ from dataclasses import dataclass, field
2
+ from discord.model import DataModel
3
+ from .components_v2 import Label
4
+
5
+ @dataclass
6
+ class ModalBuilder(DataModel):
7
+ title: str
8
+ custom_id: str = None
9
+ components: list[Label] = field(default_factory=list)
10
+
11
+ def add_label(self, component: Label):
12
+ """Add a label component to this modal.
13
+
14
+ Args:
15
+ component (Label): the label component
16
+
17
+ Returns:
18
+ ModalBuilder: self
19
+ """
20
+ self.components.append(component)
21
+ return self
discord/parts/role.py ADDED
@@ -0,0 +1,39 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+ from discord.model import DataModel
4
+
5
+ from ..models.role import RoleColors
6
+
7
+ @dataclass
8
+ class Role(DataModel):
9
+ """Parameters for creating/editing a role."""
10
+
11
+ colors: RoleColors
12
+ """Colors of the role."""
13
+
14
+ name: str = None
15
+ """Name of the role."""
16
+
17
+ permissions: int = 0
18
+ """Permission bit set."""
19
+
20
+ hoist: bool = False
21
+ """If the role is pinned in the user listing."""
22
+
23
+ mentionable: bool = False
24
+ """If the role is mentionable."""
25
+
26
+ unicode_emoji: Optional[str] = None
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
@@ -0,0 +1,10 @@
1
+ # discord/resources
2
+
3
+ from .guild import Guild
4
+ from .channel import Channel
5
+ from .message import Message
6
+ from .bot_emojis import BotEmojis
7
+ from .user import User
8
+ from .interaction import Interaction
9
+ from .application import Application
10
+ from .interaction import Interaction