scurrypy 0.2.2__tar.gz → 0.3.2__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.
Files changed (60) hide show
  1. {scurrypy-0.2.2/scurrypy.egg-info → scurrypy-0.3.2}/PKG-INFO +5 -5
  2. {scurrypy-0.2.2 → scurrypy-0.3.2}/README.md +4 -4
  3. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/client.py +18 -11
  4. scurrypy-0.3.2/discord/client_like.py +19 -0
  5. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/dispatch/command_dispatcher.py +30 -26
  6. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/dispatch/event_dispatcher.py +10 -9
  7. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/dispatch/prefix_dispatcher.py +10 -9
  8. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/channel_events.py +2 -3
  9. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/guild_events.py +1 -2
  10. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/interaction_events.py +0 -4
  11. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/message_events.py +3 -4
  12. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/reaction_events.py +4 -5
  13. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/ready_event.py +0 -4
  14. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/model.py +2 -0
  15. scurrypy-0.3.2/discord/models/interaction.py +26 -0
  16. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/action_row.py +0 -1
  17. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/channel.py +30 -1
  18. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/interaction.py +11 -3
  19. {scurrypy-0.2.2 → scurrypy-0.3.2}/pyproject.toml +1 -1
  20. {scurrypy-0.2.2 → scurrypy-0.3.2/scurrypy.egg-info}/PKG-INFO +5 -5
  21. {scurrypy-0.2.2 → scurrypy-0.3.2}/scurrypy.egg-info/SOURCES.txt +2 -1
  22. scurrypy-0.2.2/discord/events/event_model.py +0 -10
  23. {scurrypy-0.2.2 → scurrypy-0.3.2}/LICENSE +0 -0
  24. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/__init__.py +0 -0
  25. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/config.py +0 -0
  26. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/dispatch/__init__.py +0 -0
  27. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/error.py +0 -0
  28. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/__init__.py +0 -0
  29. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/events/hello_event.py +0 -0
  30. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/gateway.py +0 -0
  31. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/http.py +0 -0
  32. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/intents.py +0 -0
  33. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/logger.py +0 -0
  34. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/__init__.py +0 -0
  35. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/application.py +0 -0
  36. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/emoji.py +0 -0
  37. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/guild.py +0 -0
  38. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/integration.py +0 -0
  39. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/member.py +0 -0
  40. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/role.py +0 -0
  41. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/models/user.py +0 -0
  42. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/__init__.py +0 -0
  43. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/attachment.py +0 -0
  44. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/channel.py +0 -0
  45. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/command.py +0 -0
  46. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/component_types.py +0 -0
  47. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/components_v2.py +0 -0
  48. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/embed.py +0 -0
  49. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/message.py +0 -0
  50. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/modal.py +0 -0
  51. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/parts/role.py +0 -0
  52. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/__init__.py +0 -0
  53. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/application.py +0 -0
  54. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/bot_emojis.py +0 -0
  55. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/guild.py +0 -0
  56. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/message.py +0 -0
  57. {scurrypy-0.2.2 → scurrypy-0.3.2}/discord/resources/user.py +0 -0
  58. {scurrypy-0.2.2 → scurrypy-0.3.2}/scurrypy.egg-info/dependency_links.txt +0 -0
  59. {scurrypy-0.2.2 → scurrypy-0.3.2}/scurrypy.egg-info/top_level.txt +0 -0
  60. {scurrypy-0.2.2 → scurrypy-0.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scurrypy
3
- Version: 0.2.2
3
+ Version: 0.3.2
4
4
  Summary: Discord API Wrapper in Python
5
5
  Author: Furmissile
6
6
  Requires-Python: >=3.10
@@ -46,14 +46,14 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
46
46
 
47
47
  bot = discord.Client(
48
48
  token=os.getenv("DISCORD_TOKEN"),
49
- application_id=1234567890 # replace with your bot's user ID
49
+ application_id=APPLICATION_ID # replace with your bot's user ID
50
50
  )
51
51
 
52
52
  @bot.command(
53
53
  command=discord.SlashCommand(name='example', description='Demonstrate the minimal slash command!'),
54
54
  guild_id=GUILD_ID # must be a guild ID your bot is in!
55
55
  )
56
- async def example(event: discord.InteractionEvent):
56
+ async def example(bot: discord.Client, event: discord.InteractionEvent):
57
57
  await event.interaction.respond(f'Hello, {event.interaction.member.user.username}!')
58
58
 
59
59
  bot.run()
@@ -69,13 +69,13 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
69
69
 
70
70
  bot = discord.Client(
71
71
  token=os.getenv("DISCORD_TOKEN"),
72
- application_id=1234567890, # replace with your bot's user ID
72
+ application_id=APPLICATION_ID, # replace with your bot's user ID
73
73
  intents=discord.set_intents(message_content=True),
74
74
  prefix='!' # your custom prefix
75
75
  )
76
76
 
77
77
  @bot.prefix_command
78
- async def ping(event: discord.MessageCreateEvent): # the function name is the name of the command!
78
+ async def ping(bot: discord.Client, event: discord.MessageCreateEvent): # the function name is the name of the command!
79
79
  await event.message.send(f"Pong!")
80
80
 
81
81
  bot.run()
@@ -36,14 +36,14 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
36
36
 
37
37
  bot = discord.Client(
38
38
  token=os.getenv("DISCORD_TOKEN"),
39
- application_id=1234567890 # replace with your bot's user ID
39
+ application_id=APPLICATION_ID # replace with your bot's user ID
40
40
  )
41
41
 
42
42
  @bot.command(
43
43
  command=discord.SlashCommand(name='example', description='Demonstrate the minimal slash command!'),
44
44
  guild_id=GUILD_ID # must be a guild ID your bot is in!
45
45
  )
46
- async def example(event: discord.InteractionEvent):
46
+ async def example(bot: discord.Client, event: discord.InteractionEvent):
47
47
  await event.interaction.respond(f'Hello, {event.interaction.member.user.username}!')
48
48
 
49
49
  bot.run()
@@ -59,13 +59,13 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
59
59
 
60
60
  bot = discord.Client(
61
61
  token=os.getenv("DISCORD_TOKEN"),
62
- application_id=1234567890, # replace with your bot's user ID
62
+ application_id=APPLICATION_ID, # replace with your bot's user ID
63
63
  intents=discord.set_intents(message_content=True),
64
64
  prefix='!' # your custom prefix
65
65
  )
66
66
 
67
67
  @bot.prefix_command
68
- async def ping(event: discord.MessageCreateEvent): # the function name is the name of the command!
68
+ async def ping(bot: discord.Client, event: discord.MessageCreateEvent): # the function name is the name of the command!
69
69
  await event.message.send(f"Pong!")
70
70
 
71
71
  bot.run()
@@ -6,6 +6,7 @@ from .http import HTTPClient
6
6
  from .intents import Intents
7
7
  from .error import DiscordError
8
8
  from .config import BaseConfig
9
+ from .client_like import ClientLike
9
10
 
10
11
  from .resources.guild import Guild
11
12
  from .resources.channel import Channel
@@ -20,7 +21,7 @@ from .dispatch.event_dispatcher import EventDispatcher
20
21
  from .dispatch.prefix_dispatcher import PrefixDispatcher
21
22
  from .dispatch.command_dispatcher import CommandDispatcher
22
23
 
23
- class Client:
24
+ class Client(ClientLike):
24
25
  """Main entry point for Discord bots.
25
26
  Ties together the moving parts: gateway, HTTP, event dispatching, command handling, and resource managers.
26
27
  """
@@ -39,6 +40,7 @@ class Client:
39
40
  token (str): the bot's token
40
41
  application_id (int): the bot's user ID
41
42
  intents (int, optional): gateway intents. Defaults to Intents.DEFAULT.
43
+ config (BaseConfig, optional): user-defined config data
42
44
  debug_mode (bool, optional): toggle debug messages. Defaults to False.
43
45
  prefix (str, optional): set message prefix if using command prefixes
44
46
  quiet (bool, optional): if INFO, DEBUG, and WARN should be logged
@@ -54,9 +56,9 @@ class Client:
54
56
  if prefix and (intents & Intents.MESSAGE_CONTENT == 0):
55
57
  self._logger.log_warn('Prefix set without message content enabled.')
56
58
 
57
- self.dispatcher = EventDispatcher(self.application_id, self._http, self._logger, config)
58
- self.prefix_dispatcher = PrefixDispatcher(self._http, self._logger, prefix, config)
59
- self.command_dispatcher = CommandDispatcher(self.application_id, self._http, self._logger, config)
59
+ self.dispatcher = EventDispatcher(self)
60
+ self.prefix_dispatcher = PrefixDispatcher(self, prefix)
61
+ self.command_dispatcher = CommandDispatcher(self)
60
62
 
61
63
  self._global_commands = [] # SlashCommand
62
64
  self._guild_commands = {} # {guild_id : [commands], ...}
@@ -75,13 +77,18 @@ class Client:
75
77
  """
76
78
  self.prefix_dispatcher.register(func.__name__, func)
77
79
 
78
- def component(self, func):
80
+ def component(self, custom_id: str):
79
81
  """Decorator registers a function for a component handler.
80
82
 
81
83
  Args:
82
- func (callable): callback handle for component response
84
+ custom_id (str): Identifier of the component
85
+ !!! warning "Important"
86
+ Must match the `custom_id` set where the component was created.
83
87
  """
84
- self.command_dispatcher.component(func)
88
+ def decorator(func):
89
+ self.command_dispatcher.component(func, custom_id)
90
+ return func
91
+ return decorator
85
92
 
86
93
  def command(self, command: SlashCommand | MessageCommand | UserCommand, guild_id: int = 0):
87
94
  """Decorator registers a function for a command handler.
@@ -93,13 +100,13 @@ class Client:
93
100
  def decorator(func):
94
101
  # hash out command type
95
102
  if isinstance(command, MessageCommand):
96
- self.command_dispatcher.message_command(func)
103
+ self.command_dispatcher.message_command(command.name, func)
97
104
  elif isinstance(command, UserCommand):
98
- self.command_dispatcher.user_command(func)
105
+ self.command_dispatcher.user_command(command.name, func)
99
106
  elif isinstance(command, SlashCommand):
100
- self.command_dispatcher.command(func)
107
+ self.command_dispatcher.command(command.name, func)
101
108
  else:
102
- raise ValueError(f'Command {func.__name__} expected to be of type SlashCommand, UserCommand, MessageCommand; \
109
+ raise ValueError(f'Command {command.name} expected to be of type SlashCommand, UserCommand, MessageCommand; \
103
110
  got {type(command).__name__}.')
104
111
 
105
112
  # then hash out if this command should be guild or global level
@@ -0,0 +1,19 @@
1
+ from typing import Protocol
2
+
3
+ from .config import BaseConfig
4
+ from .http import HTTPClient
5
+ from .logger import Logger
6
+
7
+ class ClientLike(Protocol):
8
+ """Exposes a common interface for [`Client`][discord.client.Client]."""
9
+ application_id: int
10
+ """Bot's application ID."""
11
+
12
+ config: BaseConfig
13
+ """User-defined config."""
14
+
15
+ _http: HTTPClient
16
+ """HTTP session for requests."""
17
+
18
+ _logger: Logger
19
+ """Logger instance to log events."""
@@ -1,8 +1,4 @@
1
- import asyncio
2
-
3
- from ..http import HTTPClient
4
- from ..logger import Logger
5
- from ..config import BaseConfig
1
+ from ..client_like import ClientLike
6
2
 
7
3
  from ..events.interaction_events import ApplicationCommandData, MessageComponentData, ModalData, InteractionEvent
8
4
  from ..resources.interaction import Interaction, InteractionDataTypes
@@ -29,17 +25,20 @@ class CommandDispatcher:
29
25
  }
30
26
  """Maps [`InteractionTypes`][discord.dispatch.command_dispatcher.InteractionTypes] to their respective dataclass."""
31
27
 
32
- def __init__(self, application_id: int, http: HTTPClient, logger: Logger, config: BaseConfig):
33
- self.application_id = application_id
28
+ def __init__(self, client: ClientLike):
29
+ self.application_id = client.application_id
34
30
  """Bot's application ID."""
35
31
 
36
- self._http = http
32
+ self.bot = client
33
+ """Bot session for user access to bot."""
34
+
35
+ self._http = client._http
37
36
  """HTTP session for requests."""
38
37
 
39
- self._logger = logger
38
+ self._logger = client._logger
40
39
  """Logger instance to log events."""
41
40
 
42
- self.config = config
41
+ self.config = client.config
43
42
 
44
43
  self._component_handlers = {}
45
44
  """Mapping of component custom IDs to handler."""
@@ -79,37 +78,42 @@ class CommandDispatcher:
79
78
 
80
79
  await self._http.request('PUT', f"applications/{self.application_id}/commands", global_commands)
81
80
 
82
- def command(self, handler):
81
+ def command(self, name: str, handler):
83
82
  """Decorator to register slash commands.
84
83
 
85
84
  Args:
85
+ name (str): name of the command to register
86
86
  handler (callable): callback handle for command response
87
87
  """
88
- self._handlers[handler.__name__] = handler
89
-
90
- def component(self, handler):
91
- """Decorator to register component interactions.
92
-
93
- Args:
94
- handler (callable): callback handle for component response
95
- """
96
- self._component_handlers[handler.__name__] = handler
88
+ self._handlers[name] = handler
97
89
 
98
- def user_command(self, handler):
90
+ def user_command(self, name: str, handler):
99
91
  """Decorator to register user commands.
100
92
 
101
93
  Args:
94
+ name (str): name of the command to register
102
95
  handler (callable): callback handle for user command response
103
96
  """
104
- self._user_handlers[handler.__name__] = handler
97
+ self._user_handlers[name] = handler
105
98
 
106
- def message_command(self, handler):
99
+ def message_command(self, name: str, handler):
107
100
  """Decorator to register message commands.
108
101
 
109
102
  Args:
103
+ name (str): name of the command to register
110
104
  handler (callable): callback handle for message command response
111
105
  """
112
- self._message_handlers[handler.__name__] = handler
106
+ self._message_handlers[name] = handler
107
+
108
+ def component(self, func, custom_id: str):
109
+ """Decorator to register component interactions.
110
+
111
+ Args:
112
+ custom_id (str): Identifier of the component
113
+ !!! warning "Important"
114
+ Must match the `custom_id` set where the component was created.
115
+ """
116
+ self._component_handlers[custom_id] = func
113
117
 
114
118
  async def dispatch(self, data: dict):
115
119
  """Dispatch a response to an `INTERACTION_CREATE` event
@@ -117,7 +121,7 @@ class CommandDispatcher:
117
121
  Args:
118
122
  data (dict): interaction data
119
123
  """
120
- event = InteractionEvent(config=self.config, interaction=Interaction.from_dict(data, self._http))
124
+ event = InteractionEvent(interaction=Interaction.from_dict(data, self._http))
121
125
 
122
126
  event_data_obj = self.RESOURCE_MAP.get(event.interaction.type)
123
127
 
@@ -153,7 +157,7 @@ class CommandDispatcher:
153
157
  return
154
158
 
155
159
  try:
156
- await handler(event) # NOTE: treat command options as args!
160
+ await handler(self.bot, event) # NOTE: treat command options as args!
157
161
  self._logger.log_info(f"Interaction Event '{name}' Acknowledged.")
158
162
  except Exception as e:
159
163
  self._logger.log_error(f"Error in interaction '{name}': {e}")
@@ -1,6 +1,4 @@
1
- from ..http import HTTPClient
2
- from ..logger import Logger
3
- from ..config import BaseConfig
1
+ from ..client_like import ClientLike
4
2
 
5
3
  from ..events.ready_event import *
6
4
  from ..events.reaction_events import *
@@ -41,17 +39,20 @@ class EventDispatcher:
41
39
  }
42
40
  """Mapping of event names to respective dataclass."""
43
41
 
44
- def __init__(self, application_id: int, http: HTTPClient, logger: Logger, config: BaseConfig):
45
- self.application_id = application_id
42
+ def __init__(self, client: ClientLike):
43
+ self.application_id = client.application_id
46
44
  """Bot's ID."""
47
45
 
48
- self._http = http
46
+ self.bot = client
47
+ """Top-level discord client."""
48
+
49
+ self._http = client._http
49
50
  """HTTP session for requests."""
50
51
 
51
- self._logger = logger
52
+ self._logger = client._logger
52
53
  """HTTP session for requests"""
53
54
 
54
- self.config = config
55
+ self.config = client.config
55
56
  """User-defined bot config for persistent data."""
56
57
 
57
58
  self._handlers = {}
@@ -86,5 +87,5 @@ class EventDispatcher:
86
87
  if handler:
87
88
  obj = cls.from_dict(data, self._http)
88
89
  obj.config = self.config
89
- await handler(obj)
90
+ await handler(self.bot, obj)
90
91
  self._logger.log_info(f"Event {event_name} Acknowledged.")
@@ -1,6 +1,4 @@
1
- from ..http import HTTPClient
2
- from ..logger import Logger
3
- from ..config import BaseConfig
1
+ from ..client_like import ClientLike
4
2
 
5
3
  from ..events.message_events import MessageCreateEvent
6
4
 
@@ -9,17 +7,21 @@ from ..models.member import MemberModel
9
7
 
10
8
  class PrefixDispatcher:
11
9
  """Handles text-based command messages that start with a specific prefix."""
12
- def __init__(self, http: HTTPClient, logger: Logger, prefix: str, config: BaseConfig):
13
- self._http = http
10
+ def __init__(self, client: ClientLike, prefix: str):
11
+
12
+ self.bot = client
13
+ """Bot session for user access to bot."""
14
+
15
+ self._http = client._http
14
16
  """HTTP session for requests."""
15
17
 
16
- self._logger = logger
18
+ self._logger = client._logger
17
19
  """Logger instance to log events."""
18
20
 
19
21
  self.prefix = prefix
20
22
  """User-defined command prefix."""
21
23
 
22
- self.config = config
24
+ self.config = client.config
23
25
  """User-defined bot config for persistent data."""
24
26
 
25
27
  self._handlers = {}
@@ -41,7 +43,6 @@ class PrefixDispatcher:
41
43
  data (dict): Discord's raw event payload
42
44
  """
43
45
  event = MessageCreateEvent(
44
- config=self.config,
45
46
  guild_id=data.get('guild_id'),
46
47
  message=Message.from_dict(data, self._http),
47
48
  member=MemberModel.from_dict(data.get('member'))
@@ -52,7 +53,7 @@ class PrefixDispatcher:
52
53
  handler = self._handlers.get(command)
53
54
  if handler:
54
55
  try:
55
- await handler(event, *args)
56
+ await handler(self.bot, event, *args)
56
57
  self._logger.log_info(f"Prefix Event '{command}' Acknowledged.")
57
58
  except Exception as e:
58
59
  self._logger.log_error(f"Error in prefix command '{command}': {e}")
@@ -1,9 +1,8 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import Optional
3
- from .event_model import EventModel
4
3
 
5
4
  @dataclass
6
- class GuildChannelEvent(EventModel):
5
+ class GuildChannelEvent:
7
6
  """Base guild channel event."""
8
7
 
9
8
  id: int
@@ -46,7 +45,7 @@ class GuildChannelDeleteEvent(GuildChannelEvent):
46
45
  pass
47
46
 
48
47
  @dataclass
49
- class ChannelPinsUpdateEvent(EventModel):
48
+ class ChannelPinsUpdateEvent:
50
49
  """Pin update event."""
51
50
  channel_id: int
52
51
  guild_id: Optional[int]
@@ -1,10 +1,9 @@
1
1
  from dataclasses import dataclass
2
- from .event_model import EventModel
3
2
  from ..models import MemberModel
4
3
  from ..resources.channel import Channel
5
4
 
6
5
  @dataclass
7
- class GuildEvent(EventModel):
6
+ class GuildEvent:
8
7
  """Base guild event."""
9
8
  joined_at: str
10
9
  """ISO8601 timestamp of when app joined the guild."""
@@ -1,7 +1,6 @@
1
1
  from dataclasses import dataclass, field
2
2
  from typing import Optional
3
3
  from ..model import DataModel
4
- from ..config import BaseConfig
5
4
 
6
5
  from ..resources.interaction import Interaction
7
6
 
@@ -142,8 +141,5 @@ class InteractionEvent(DataModel):
142
141
  interaction: Interaction
143
142
  """Interaction resource object. See [`Interaction`][discord.resources.interaction.Interaction]."""
144
143
 
145
- config: BaseConfig
146
- """User-defined bot config for persistent data."""
147
-
148
144
  data: Optional[ApplicationCommandData | MessageComponentData | ModalData] = None
149
145
  """Interaction response data."""
@@ -1,12 +1,11 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import Optional
3
- from .event_model import EventModel
4
3
 
5
4
  from ..resources.message import Message
6
5
  from ..models.member import MemberModel
7
6
 
8
7
  @dataclass
9
- class MessageCreateEvent(EventModel):
8
+ class MessageCreateEvent:
10
9
  """Received when a message is created."""
11
10
  message: Message
12
11
  """Message resource object. See [`Resource.Message`][discord.resources.message.Message]."""
@@ -18,7 +17,7 @@ class MessageCreateEvent(EventModel):
18
17
  """Partial Member object of the author of the message. See [`MemberModel`][discord.models.member.MemberModel]."""
19
18
 
20
19
  @dataclass
21
- class MessageUpdateEvent(EventModel):
20
+ class MessageUpdateEvent:
22
21
  """Received when a message is updated."""
23
22
  message: Message
24
23
  """Message resource object. See [`Resource.Message`][discord.resources.message.Message]."""
@@ -30,7 +29,7 @@ class MessageUpdateEvent(EventModel):
30
29
  """Partial Member object of the author of the message. See [`MemberModel`][discord.models.member.MemberModel]."""
31
30
 
32
31
  @dataclass
33
- class MessageDeleteEvent(EventModel):
32
+ class MessageDeleteEvent:
34
33
  """Received when a message is deleted."""
35
34
 
36
35
  id: int
@@ -1,6 +1,5 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import Optional
3
- from .event_model import EventModel
4
3
 
5
4
  from ..models.member import MemberModel
6
5
  from ..models.emoji import EmojiModel
@@ -15,7 +14,7 @@ class ReactionType:
15
14
  """A super emoji."""
16
15
 
17
16
  @dataclass
18
- class ReactionAddEvent(EventModel):
17
+ class ReactionAddEvent:
19
18
  """Reaction added event."""
20
19
 
21
20
  type: int
@@ -46,7 +45,7 @@ class ReactionAddEvent(EventModel):
46
45
  """ID of the user who sent the message where the reaction was added."""
47
46
 
48
47
  @dataclass
49
- class ReactionRemoveEvent(EventModel):
48
+ class ReactionRemoveEvent:
50
49
  """Reaction removed event."""
51
50
 
52
51
  type: int
@@ -70,7 +69,7 @@ class ReactionRemoveEvent(EventModel):
70
69
  burst: bool
71
70
  """If the emoji of the removed reaction is super."""
72
71
 
73
- class ReactionRemoveAllEvent(EventModel):
72
+ class ReactionRemoveAllEvent:
74
73
  """Remove all reactions event."""
75
74
 
76
75
  channel_id: int
@@ -83,7 +82,7 @@ class ReactionRemoveAllEvent(EventModel):
83
82
  """ID of the guild where all reaction were removed (if in a guild)."""
84
83
 
85
84
  @dataclass
86
- class ReactionRemoveEmojiEvent(EventModel):
85
+ class ReactionRemoveEmojiEvent:
87
86
  """All reactions of a specific emoji removed."""
88
87
 
89
88
  emoji: EmojiModel
@@ -1,6 +1,5 @@
1
1
  from dataclasses import dataclass
2
2
  from ..model import DataModel
3
- from ..config import BaseConfig
4
3
 
5
4
  from ..models.user import UserModel
6
5
  from ..models.guild import ReadyGuildModel
@@ -30,6 +29,3 @@ class ReadyEvent(DataModel):
30
29
 
31
30
  application: ApplicationModel
32
31
  """Partial application object. Contains ID and flags."""
33
-
34
- config: BaseConfig
35
- """User-defined bot config for persistent data."""
@@ -81,6 +81,8 @@ class DataModel:
81
81
 
82
82
  result = {}
83
83
  for f in fields(self):
84
+ if f.name.startswith('_'): # ignore private fields
85
+ continue
84
86
  value = getattr(self, f.name)
85
87
  if value not in (None, [], {}, "", 0):
86
88
  result[f.name] = serialize(value)
@@ -0,0 +1,26 @@
1
+ from dataclasses import dataclass
2
+ from ..model import DataModel
3
+
4
+ @dataclass
5
+ class InteractionCallbackDataModel(DataModel):
6
+ id: int
7
+ """ID of the interaction."""
8
+
9
+ type: int
10
+ """Type of interaction."""
11
+
12
+ activity_instance_id: str
13
+ """Instance ID of activity if an activity was launched or joined."""
14
+
15
+ response_message_id: int
16
+ """ID of the message created by the interaction."""
17
+
18
+ response_message_loading: bool
19
+ """If the interaction is in a loading state."""
20
+
21
+ response_message_ephemeral: bool
22
+ """If the interaction is ephemeral."""
23
+
24
+ @dataclass
25
+ class InteractionCallbackModel(DataModel):
26
+ interaction: InteractionCallbackDataModel
@@ -28,7 +28,6 @@ class _Button(DataModel, ActionRowChild, SectionAccessory):
28
28
  @dataclass
29
29
  class _SelectOption(DataModel):
30
30
  """Represents the Select Option component"""
31
-
32
31
  label: str
33
32
  value: str
34
33
  description: Optional[str] = None # appears below the label
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from typing import TypedDict, Unpack, Optional
2
+ from typing import TypedDict, Unpack, Optional, Literal
3
3
 
4
4
  from ..http import HTTPClient
5
5
  from ..model import DataModel
@@ -32,6 +32,15 @@ class PinsFetchParams(TypedDict, total=False):
32
32
  limit: int
33
33
  """Max number of pinned messages to return. Range 1 - 50. Default 50."""
34
34
 
35
+ class ThreadFromMessageParams(TypedDict, total=False):
36
+ """Params when attaching a thread to a message."""
37
+
38
+ rate_limit_per_user: Literal[60, 1440, 4320, 10080]
39
+ """time (minutes) of inactivity before thread is archived."""
40
+
41
+ rate_limit_per_user: int
42
+ """time (seconds) user waits before sending another message."""
43
+
35
44
  @dataclass
36
45
  class PinnedMessage(DataModel):
37
46
  """Pinned message data."""
@@ -160,6 +169,26 @@ class Channel(DataModel):
160
169
 
161
170
  return self
162
171
 
172
+ async def create_thread_from_message(self, message_id: int, name: str, **kwargs: Unpack[ThreadFromMessageParams]):
173
+ """Create a thread from this message
174
+
175
+ Args:
176
+ message_id: ID of message to attach thread
177
+ name (str): thread name
178
+
179
+ Returns:
180
+ (Channel): The updated channel object
181
+ """
182
+
183
+ content = {
184
+ 'name': name,
185
+ **kwargs
186
+ }
187
+
188
+ data = await self._http.request('POST', f"channels/{self.id}/messages/{message_id}/threads", data=content)
189
+
190
+ return Channel.from_dict(data, self._http)
191
+
163
192
  async def fetch_pins(self, **kwargs: Unpack[PinsFetchParams]):
164
193
  """Get this channel's pinned messages.
165
194
 
@@ -11,6 +11,7 @@ from ..parts.component_types import *
11
11
 
12
12
  from ..models.guild import GuildModel
13
13
  from ..models.member import MemberModel
14
+ from ..models.interaction import InteractionCallbackModel
14
15
 
15
16
  from .channel import Channel
16
17
 
@@ -98,11 +99,12 @@ class Interaction(DataModel):
98
99
  channel: Optional[Channel] = None
99
100
  """Partial channel object the interaction was invoked."""
100
101
 
101
- async def respond(self, message: str | MessageBuilder, **flags: Unpack[MessageFlagParams]):
102
+ async def respond(self, message: str | MessageBuilder, with_response: bool = False, **flags: Unpack[MessageFlagParams]):
102
103
  """Create a message in response to an interaction.
103
104
 
104
105
  Args:
105
106
  message (str | MessageBuilder): content as a string or from MessageBuilder
107
+ with_response (bool, optional): if the interaction data should be returned. Defaults to False.
106
108
  """
107
109
  if isinstance(message, str):
108
110
  message = MessageBuilder(content=message).set_flags(**flags)
@@ -112,12 +114,18 @@ class Interaction(DataModel):
112
114
  'data': message._to_dict()
113
115
  }
114
116
 
115
- await self._http.request(
117
+ params = {'with_response': with_response}
118
+
119
+ data = await self._http.request(
116
120
  'POST',
117
121
  f'/interactions/{self.id}/{self.token}/callback',
118
122
  content,
119
- files=[fp.path for fp in message.attachments])
123
+ files=[fp.path for fp in message.attachments],
124
+ params=params)
120
125
 
126
+ if with_response:
127
+ return InteractionCallbackModel.from_dict(data, self._http)
128
+
121
129
  async def update(self, message: str | MessageBuilder, **flags: Unpack[MessageFlagParams]):
122
130
  """Update a message in response to an interaction.
123
131
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "scurrypy"
7
- version = "0.2.2"
7
+ version = "0.3.2"
8
8
 
9
9
  description = "Discord API Wrapper in Python"
10
10
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scurrypy
3
- Version: 0.2.2
3
+ Version: 0.3.2
4
4
  Summary: Discord API Wrapper in Python
5
5
  Author: Furmissile
6
6
  Requires-Python: >=3.10
@@ -46,14 +46,14 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
46
46
 
47
47
  bot = discord.Client(
48
48
  token=os.getenv("DISCORD_TOKEN"),
49
- application_id=1234567890 # replace with your bot's user ID
49
+ application_id=APPLICATION_ID # replace with your bot's user ID
50
50
  )
51
51
 
52
52
  @bot.command(
53
53
  command=discord.SlashCommand(name='example', description='Demonstrate the minimal slash command!'),
54
54
  guild_id=GUILD_ID # must be a guild ID your bot is in!
55
55
  )
56
- async def example(event: discord.InteractionEvent):
56
+ async def example(bot: discord.Client, event: discord.InteractionEvent):
57
57
  await event.interaction.respond(f'Hello, {event.interaction.member.user.username}!')
58
58
 
59
59
  bot.run()
@@ -69,13 +69,13 @@ load_dotenv(dotenv_path='./path/to/env') # omit argument if your env file is on
69
69
 
70
70
  bot = discord.Client(
71
71
  token=os.getenv("DISCORD_TOKEN"),
72
- application_id=1234567890, # replace with your bot's user ID
72
+ application_id=APPLICATION_ID, # replace with your bot's user ID
73
73
  intents=discord.set_intents(message_content=True),
74
74
  prefix='!' # your custom prefix
75
75
  )
76
76
 
77
77
  @bot.prefix_command
78
- async def ping(event: discord.MessageCreateEvent): # the function name is the name of the command!
78
+ async def ping(bot: discord.Client, event: discord.MessageCreateEvent): # the function name is the name of the command!
79
79
  await event.message.send(f"Pong!")
80
80
 
81
81
  bot.run()
@@ -3,6 +3,7 @@ README.md
3
3
  pyproject.toml
4
4
  discord/__init__.py
5
5
  discord/client.py
6
+ discord/client_like.py
6
7
  discord/config.py
7
8
  discord/error.py
8
9
  discord/gateway.py
@@ -16,7 +17,6 @@ discord/dispatch/event_dispatcher.py
16
17
  discord/dispatch/prefix_dispatcher.py
17
18
  discord/events/__init__.py
18
19
  discord/events/channel_events.py
19
- discord/events/event_model.py
20
20
  discord/events/guild_events.py
21
21
  discord/events/hello_event.py
22
22
  discord/events/interaction_events.py
@@ -28,6 +28,7 @@ discord/models/application.py
28
28
  discord/models/emoji.py
29
29
  discord/models/guild.py
30
30
  discord/models/integration.py
31
+ discord/models/interaction.py
31
32
  discord/models/member.py
32
33
  discord/models/role.py
33
34
  discord/models/user.py
@@ -1,10 +0,0 @@
1
- from dataclasses import dataclass
2
- from ..model import DataModel
3
- from ..config import BaseConfig
4
-
5
- @dataclass
6
- class EventModel(DataModel):
7
- """Event Model for event-driven fields common among all events."""
8
-
9
- config: BaseConfig
10
- """User-defined bot config for persistent data."""
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