hikari-arc 0.4.0__py3-none-any.whl → 0.6.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.
- arc/__init__.py +55 -5
- arc/abc/__init__.py +32 -1
- arc/abc/client.py +207 -67
- arc/abc/command.py +245 -34
- arc/abc/error_handler.py +33 -2
- arc/abc/hookable.py +24 -0
- arc/abc/limiter.py +61 -0
- arc/abc/option.py +73 -5
- arc/abc/plugin.py +185 -29
- arc/client.py +103 -33
- arc/command/message.py +21 -18
- arc/command/option/attachment.py +9 -5
- arc/command/option/bool.py +9 -6
- arc/command/option/channel.py +9 -5
- arc/command/option/float.py +11 -7
- arc/command/option/int.py +11 -7
- arc/command/option/mentionable.py +9 -5
- arc/command/option/role.py +9 -5
- arc/command/option/str.py +11 -7
- arc/command/option/user.py +9 -5
- arc/command/slash.py +222 -197
- arc/command/user.py +20 -17
- arc/context/autocomplete.py +1 -0
- arc/context/base.py +216 -105
- arc/errors.py +52 -10
- arc/events.py +5 -1
- arc/extension.py +23 -0
- arc/internal/about.py +1 -1
- arc/internal/deprecation.py +3 -4
- arc/internal/options.py +106 -0
- arc/internal/sigparse.py +19 -1
- arc/internal/sync.py +13 -10
- arc/internal/types.py +34 -15
- arc/locale.py +28 -0
- arc/plugin.py +56 -5
- arc/utils/__init__.py +53 -2
- arc/utils/hooks/__init__.py +25 -0
- arc/utils/{hooks.py → hooks/basic.py} +28 -1
- arc/utils/hooks/limiters.py +217 -0
- arc/utils/ratelimiter.py +243 -0
- {hikari_arc-0.4.0.dist-info → hikari_arc-0.6.0.dist-info}/METADATA +13 -8
- hikari_arc-0.6.0.dist-info/RECORD +52 -0
- hikari_arc-0.4.0.dist-info/RECORD +0 -47
- {hikari_arc-0.4.0.dist-info → hikari_arc-0.6.0.dist-info}/LICENSE +0 -0
- {hikari_arc-0.4.0.dist-info → hikari_arc-0.6.0.dist-info}/WHEEL +0 -0
- {hikari_arc-0.4.0.dist-info → hikari_arc-0.6.0.dist-info}/top_level.txt +0 -0
arc/abc/option.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import abc
|
|
4
|
+
import enum
|
|
4
5
|
import typing as t
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
@@ -16,7 +17,15 @@ if t.TYPE_CHECKING:
|
|
|
16
17
|
from arc.abc.client import Client
|
|
17
18
|
from arc.abc.command import CommandProto
|
|
18
19
|
|
|
19
|
-
__all__ = (
|
|
20
|
+
__all__ = (
|
|
21
|
+
"Option",
|
|
22
|
+
"OptionParams",
|
|
23
|
+
"OptionWithChoices",
|
|
24
|
+
"OptionWithChoicesParams",
|
|
25
|
+
"OptionBase",
|
|
26
|
+
"CommandOptionBase",
|
|
27
|
+
"OptionType",
|
|
28
|
+
)
|
|
20
29
|
|
|
21
30
|
T = t.TypeVar("T")
|
|
22
31
|
|
|
@@ -37,6 +46,65 @@ arc.Option[int, arc.IntParams(...)]
|
|
|
37
46
|
"""
|
|
38
47
|
|
|
39
48
|
|
|
49
|
+
class OptionType(enum.IntEnum):
|
|
50
|
+
"""The type of a command option.
|
|
51
|
+
|
|
52
|
+
This is practically identical to `hikari.OptionType` at the moment.
|
|
53
|
+
It may however be used in the future to define custom option types.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
SUB_COMMAND = 1
|
|
57
|
+
"""Denotes a command option where the value will be a sub command."""
|
|
58
|
+
|
|
59
|
+
SUB_COMMAND_GROUP = 2
|
|
60
|
+
"""Denotes a command option where the value will be a sub command group."""
|
|
61
|
+
|
|
62
|
+
STRING = 3
|
|
63
|
+
"""Denotes a command option where the value will be a string."""
|
|
64
|
+
|
|
65
|
+
INTEGER = 4
|
|
66
|
+
"""Denotes a command option where the value will be a int.
|
|
67
|
+
|
|
68
|
+
This is range limited between -2^53 and 2^53.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
BOOLEAN = 5
|
|
72
|
+
"""Denotes a command option where the value will be a bool."""
|
|
73
|
+
|
|
74
|
+
USER = 6
|
|
75
|
+
"""Denotes a command option where the value will be resolved to a user."""
|
|
76
|
+
|
|
77
|
+
CHANNEL = 7
|
|
78
|
+
"""Denotes a command option where the value will be resolved to a channel."""
|
|
79
|
+
|
|
80
|
+
ROLE = 8
|
|
81
|
+
"""Denotes a command option where the value will be resolved to a role."""
|
|
82
|
+
|
|
83
|
+
MENTIONABLE = 9
|
|
84
|
+
"""Denotes a command option where the value will be a snowflake ID."""
|
|
85
|
+
|
|
86
|
+
FLOAT = 10
|
|
87
|
+
"""Denotes a command option where the value will be a float.
|
|
88
|
+
|
|
89
|
+
This is range limited between -2^53 and 2^53.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
ATTACHMENT = 11
|
|
93
|
+
"""Denotes a command option where the value will be an attachment."""
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def from_hikari(cls, option_type: hikari.OptionType) -> OptionType:
|
|
97
|
+
"""Convert a hikari.OptionType to an OptionType."""
|
|
98
|
+
return cls(option_type.value)
|
|
99
|
+
|
|
100
|
+
def to_hikari(self) -> hikari.OptionType:
|
|
101
|
+
"""Convert an OptionType to a hikari.OptionType."""
|
|
102
|
+
# TODO: Map custom option types to their respective hikari.OptionType
|
|
103
|
+
return hikari.OptionType(self.value)
|
|
104
|
+
|
|
105
|
+
# TODO: When adding custom convertible option types, add them with an offset of 1000 or so
|
|
106
|
+
|
|
107
|
+
|
|
40
108
|
class OptionParams(t.Generic[T]):
|
|
41
109
|
"""The base class for all option parameters objects.
|
|
42
110
|
|
|
@@ -54,9 +122,9 @@ class OptionParams(t.Generic[T]):
|
|
|
54
122
|
|
|
55
123
|
def __init__(
|
|
56
124
|
self,
|
|
57
|
-
name: str | None = None,
|
|
58
125
|
description: str = "No description provided.",
|
|
59
126
|
*,
|
|
127
|
+
name: str | None = None,
|
|
60
128
|
name_localizations: t.Mapping[hikari.Locale, str] | None = None,
|
|
61
129
|
description_localizations: t.Mapping[hikari.Locale, str] | None = None,
|
|
62
130
|
) -> None:
|
|
@@ -110,9 +178,9 @@ class OptionWithChoicesParams(OptionParams[ChoiceT], t.Generic[ChoiceT, ClientT]
|
|
|
110
178
|
|
|
111
179
|
def __init__(
|
|
112
180
|
self,
|
|
113
|
-
name: str | None = None,
|
|
114
181
|
description: str = "No description provided.",
|
|
115
182
|
*,
|
|
183
|
+
name: str | None = None,
|
|
116
184
|
name_localizations: t.Mapping[hikari.Locale, str] | None = None,
|
|
117
185
|
description_localizations: t.Mapping[hikari.Locale, str] | None = None,
|
|
118
186
|
choices: t.Sequence[ChoiceT | hikari.CommandChoice] | t.Mapping[str, ChoiceT] | None = None,
|
|
@@ -153,13 +221,13 @@ class OptionBase(abc.ABC, t.Generic[T]):
|
|
|
153
221
|
|
|
154
222
|
@property
|
|
155
223
|
@abc.abstractmethod
|
|
156
|
-
def option_type(self) ->
|
|
224
|
+
def option_type(self) -> OptionType:
|
|
157
225
|
"""The type of the option. Used to register the command."""
|
|
158
226
|
|
|
159
227
|
def _to_dict(self) -> dict[str, t.Any]:
|
|
160
228
|
"""Convert the option to a dictionary of kwargs that can be passed to hikari.CommandOption."""
|
|
161
229
|
return {
|
|
162
|
-
"type": self.option_type,
|
|
230
|
+
"type": self.option_type.to_hikari(),
|
|
163
231
|
"name": self.name,
|
|
164
232
|
"description": self.description,
|
|
165
233
|
"autocomplete": False,
|
arc/abc/plugin.py
CHANGED
|
@@ -8,15 +8,16 @@ import typing as t
|
|
|
8
8
|
|
|
9
9
|
import hikari
|
|
10
10
|
|
|
11
|
+
from arc.abc.command import _CommandSettings
|
|
11
12
|
from arc.abc.error_handler import HasErrorHandler
|
|
12
13
|
from arc.abc.hookable import Hookable
|
|
13
14
|
from arc.command import MessageCommand, SlashCommand, SlashGroup, UserCommand
|
|
15
|
+
from arc.command.slash import SlashSubCommand, SlashSubGroup
|
|
14
16
|
from arc.context import AutodeferMode, Context
|
|
15
17
|
from arc.internal.types import BuilderT, ClientT, ErrorHandlerCallbackT, HookT, PostHookT, SlashCommandLike
|
|
16
18
|
|
|
17
19
|
if t.TYPE_CHECKING:
|
|
18
20
|
from arc.abc.command import CommandBase
|
|
19
|
-
from arc.command import SlashSubCommand, SlashSubGroup
|
|
20
21
|
|
|
21
22
|
__all__ = ("PluginBase",)
|
|
22
23
|
|
|
@@ -31,17 +32,52 @@ class PluginBase(HasErrorHandler[ClientT], Hookable[ClientT]):
|
|
|
31
32
|
----------
|
|
32
33
|
name : str
|
|
33
34
|
The name of this plugin. This must be unique across all plugins.
|
|
35
|
+
default_enabled_guilds : t.Sequence[hikari.Snowflake] | hikari.UndefinedType
|
|
36
|
+
The default guilds to enable commands in
|
|
37
|
+
autodefer : bool | AutodeferMode
|
|
38
|
+
If True, all commands in this plugin will automatically defer if it is taking longer than 2 seconds to respond.
|
|
39
|
+
This can be overridden on a per-command basis.
|
|
40
|
+
is_dm_enabled : bool | hikari.UndefinedType
|
|
41
|
+
Whether commands in this plugin are enabled in DMs
|
|
42
|
+
This can be overridden on a per-command basis.
|
|
43
|
+
default_permissions : hikari.Permissions | hikari.UndefinedType
|
|
44
|
+
The default permissions for this plugin
|
|
45
|
+
This can be overridden on a per-command basis.
|
|
46
|
+
is_nsfw : bool | hikari.UndefinedType
|
|
47
|
+
Whether this plugin is only usable in NSFW channels
|
|
48
|
+
|
|
49
|
+
!!! note
|
|
50
|
+
Parameters left as `hikari.UNDEFINED` will be inherited from the parent client.
|
|
34
51
|
"""
|
|
35
52
|
|
|
36
53
|
def __init__(
|
|
37
|
-
self,
|
|
54
|
+
self,
|
|
55
|
+
name: str,
|
|
56
|
+
*,
|
|
57
|
+
default_enabled_guilds: t.Sequence[hikari.Snowflakeish | hikari.PartialGuild]
|
|
58
|
+
| hikari.UndefinedType = hikari.UNDEFINED,
|
|
59
|
+
autodefer: bool | AutodeferMode | hikari.UndefinedType = hikari.UNDEFINED,
|
|
60
|
+
is_dm_enabled: bool | hikari.UndefinedType = hikari.UNDEFINED,
|
|
61
|
+
default_permissions: hikari.Permissions | hikari.UndefinedType = hikari.UNDEFINED,
|
|
62
|
+
is_nsfw: bool | hikari.UndefinedType = hikari.UNDEFINED,
|
|
38
63
|
) -> None:
|
|
39
64
|
self._client: ClientT | None = None
|
|
40
65
|
self._name = name
|
|
66
|
+
self._default_enabled_guilds = (
|
|
67
|
+
tuple(hikari.Snowflake(i) for i in default_enabled_guilds)
|
|
68
|
+
if default_enabled_guilds is not hikari.UNDEFINED
|
|
69
|
+
else hikari.UNDEFINED
|
|
70
|
+
)
|
|
71
|
+
self._cmd_settings = _CommandSettings(
|
|
72
|
+
autodefer=AutodeferMode(autodefer) if isinstance(autodefer, bool) else autodefer,
|
|
73
|
+
is_dm_enabled=is_dm_enabled,
|
|
74
|
+
default_permissions=default_permissions,
|
|
75
|
+
is_nsfw=is_nsfw,
|
|
76
|
+
)
|
|
77
|
+
|
|
41
78
|
self._slash_commands: dict[str, SlashCommandLike[ClientT]] = {}
|
|
42
79
|
self._user_commands: dict[str, UserCommand[ClientT]] = {}
|
|
43
80
|
self._message_commands: dict[str, MessageCommand[ClientT]] = {}
|
|
44
|
-
self._default_enabled_guilds = default_enabled_guilds
|
|
45
81
|
self._error_handler: ErrorHandlerCallbackT[ClientT] | None = None
|
|
46
82
|
self._hooks: list[HookT[ClientT]] = []
|
|
47
83
|
self._post_hooks: list[PostHookT[ClientT]] = []
|
|
@@ -86,7 +122,7 @@ class PluginBase(HasErrorHandler[ClientT], Hookable[ClientT]):
|
|
|
86
122
|
return self._client
|
|
87
123
|
|
|
88
124
|
@property
|
|
89
|
-
def default_enabled_guilds(self) ->
|
|
125
|
+
def default_enabled_guilds(self) -> t.Sequence[hikari.Snowflake] | hikari.UndefinedType:
|
|
90
126
|
"""The default guilds to enable commands in."""
|
|
91
127
|
return self._default_enabled_guilds
|
|
92
128
|
|
|
@@ -99,6 +135,10 @@ class PluginBase(HasErrorHandler[ClientT], Hookable[ClientT]):
|
|
|
99
135
|
except Exception as exc:
|
|
100
136
|
await self.client._on_error(ctx, exc)
|
|
101
137
|
|
|
138
|
+
def _resolve_settings(self) -> _CommandSettings:
|
|
139
|
+
settings = self._client._cmd_settings if self._client is not None else _CommandSettings.default()
|
|
140
|
+
return settings.apply(self._cmd_settings)
|
|
141
|
+
|
|
102
142
|
def _resolve_hooks(self) -> list[HookT[ClientT]]:
|
|
103
143
|
return self._hooks
|
|
104
144
|
|
|
@@ -183,13 +223,13 @@ class PluginBase(HasErrorHandler[ClientT], Hookable[ClientT]):
|
|
|
183
223
|
name: str,
|
|
184
224
|
description: str = "No description provided.",
|
|
185
225
|
*,
|
|
186
|
-
guilds:
|
|
187
|
-
autodefer: bool | AutodeferMode =
|
|
188
|
-
is_dm_enabled: bool =
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
226
|
+
guilds: t.Sequence[hikari.Snowflakeish | hikari.PartialGuild] | hikari.UndefinedType = hikari.UNDEFINED,
|
|
227
|
+
autodefer: bool | AutodeferMode | hikari.UndefinedType = hikari.UNDEFINED,
|
|
228
|
+
is_dm_enabled: bool | hikari.UndefinedType = hikari.UNDEFINED,
|
|
229
|
+
is_nsfw: bool | hikari.UndefinedType = hikari.UNDEFINED,
|
|
230
|
+
default_permissions: hikari.Permissions | hikari.UndefinedType = hikari.UNDEFINED,
|
|
231
|
+
name_localizations: t.Mapping[hikari.Locale, str] | None = None,
|
|
232
|
+
description_localizations: t.Mapping[hikari.Locale, str] | None = None,
|
|
193
233
|
) -> SlashGroup[ClientT]:
|
|
194
234
|
"""Add a new slash command group to this client.
|
|
195
235
|
|
|
@@ -199,27 +239,30 @@ class PluginBase(HasErrorHandler[ClientT], Hookable[ClientT]):
|
|
|
199
239
|
The name of the slash command group.
|
|
200
240
|
description : str
|
|
201
241
|
The description of the slash command group.
|
|
202
|
-
guilds :
|
|
203
|
-
The guilds to register the slash command group in
|
|
204
|
-
autodefer : bool | AutodeferMode
|
|
242
|
+
guilds : t.Sequence[hikari.Snowflake] | hikari.UndefinedType
|
|
243
|
+
The guilds to register the slash command group in
|
|
244
|
+
autodefer : bool | AutodeferMode
|
|
205
245
|
If True, all commands in this group will automatically defer if it is taking longer than 2 seconds to respond.
|
|
206
246
|
This can be overridden on a per-subcommand basis.
|
|
207
|
-
is_dm_enabled : bool
|
|
208
|
-
Whether the slash command group is enabled in DMs
|
|
209
|
-
default_permissions : hikari.
|
|
210
|
-
The default permissions for the slash command group
|
|
211
|
-
name_localizations : dict[hikari.Locale, str]
|
|
212
|
-
The name of the slash command group in different locales
|
|
213
|
-
description_localizations : dict[hikari.Locale, str]
|
|
214
|
-
The description of the slash command group in different locales
|
|
215
|
-
is_nsfw : bool
|
|
216
|
-
Whether the slash command group is only usable in NSFW channels
|
|
247
|
+
is_dm_enabled : bool
|
|
248
|
+
Whether the slash command group is enabled in DMs
|
|
249
|
+
default_permissions : hikari.Permissions | hikari.UndefinedType
|
|
250
|
+
The default permissions for the slash command group
|
|
251
|
+
name_localizations : dict[hikari.Locale, str]
|
|
252
|
+
The name of the slash command group in different locales
|
|
253
|
+
description_localizations : dict[hikari.Locale, str]
|
|
254
|
+
The description of the slash command group in different locales
|
|
255
|
+
is_nsfw : bool
|
|
256
|
+
Whether the slash command group is only usable in NSFW channels
|
|
217
257
|
|
|
218
258
|
Returns
|
|
219
259
|
-------
|
|
220
260
|
SlashGroup[te.Self]
|
|
221
261
|
The slash command group that was created.
|
|
222
262
|
|
|
263
|
+
!!! note
|
|
264
|
+
Parameters left as `hikari.UNDEFINED` will be inherited from the parent plugin or client.
|
|
265
|
+
|
|
223
266
|
Usage
|
|
224
267
|
-----
|
|
225
268
|
```py
|
|
@@ -231,14 +274,13 @@ class PluginBase(HasErrorHandler[ClientT], Hookable[ClientT]):
|
|
|
231
274
|
await ctx.respond("Hello!")
|
|
232
275
|
```
|
|
233
276
|
"""
|
|
234
|
-
|
|
277
|
+
guild_ids = tuple(hikari.Snowflake(i) for i in guilds) if guilds is not hikari.UNDEFINED else hikari.UNDEFINED
|
|
235
278
|
|
|
236
|
-
group = SlashGroup(
|
|
279
|
+
group: SlashGroup[ClientT] = SlashGroup(
|
|
237
280
|
name=name,
|
|
238
281
|
description=description,
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
autodefer=AutodeferMode(autodefer),
|
|
282
|
+
guilds=guild_ids,
|
|
283
|
+
autodefer=AutodeferMode(autodefer) if isinstance(autodefer, bool) else autodefer,
|
|
242
284
|
is_dm_enabled=is_dm_enabled,
|
|
243
285
|
default_permissions=default_permissions,
|
|
244
286
|
name_localizations=name_localizations or {},
|
|
@@ -326,3 +368,117 @@ class PluginBase(HasErrorHandler[ClientT], Hookable[ClientT]):
|
|
|
326
368
|
return decorator(func)
|
|
327
369
|
|
|
328
370
|
return decorator
|
|
371
|
+
|
|
372
|
+
@t.overload
|
|
373
|
+
def walk_commands(
|
|
374
|
+
self, command_type: t.Literal[hikari.CommandType.USER], *, callable_only: bool = False
|
|
375
|
+
) -> t.Iterator[UserCommand[ClientT]]:
|
|
376
|
+
...
|
|
377
|
+
|
|
378
|
+
@t.overload
|
|
379
|
+
def walk_commands(
|
|
380
|
+
self, command_type: t.Literal[hikari.CommandType.MESSAGE], *, callable_only: bool = False
|
|
381
|
+
) -> t.Iterator[MessageCommand[ClientT]]:
|
|
382
|
+
...
|
|
383
|
+
|
|
384
|
+
@t.overload
|
|
385
|
+
def walk_commands(
|
|
386
|
+
self, command_type: t.Literal[hikari.CommandType.SLASH], *, callable_only: t.Literal[False]
|
|
387
|
+
) -> t.Iterator[SlashCommand[ClientT] | SlashSubCommand[ClientT] | SlashGroup[ClientT] | SlashSubGroup[ClientT]]:
|
|
388
|
+
...
|
|
389
|
+
|
|
390
|
+
@t.overload
|
|
391
|
+
def walk_commands(
|
|
392
|
+
self, command_type: t.Literal[hikari.CommandType.SLASH], *, callable_only: t.Literal[True]
|
|
393
|
+
) -> t.Iterator[SlashCommand[ClientT] | SlashSubCommand[ClientT]]:
|
|
394
|
+
...
|
|
395
|
+
|
|
396
|
+
def walk_commands( # noqa: C901
|
|
397
|
+
self, command_type: hikari.CommandType, *, callable_only: bool = False
|
|
398
|
+
) -> t.Iterator[t.Any]:
|
|
399
|
+
"""Iterate over all commands of a certain type added to this plugin.
|
|
400
|
+
|
|
401
|
+
Parameters
|
|
402
|
+
----------
|
|
403
|
+
command_type : hikari.CommandType
|
|
404
|
+
The type of commands to return.
|
|
405
|
+
callable_only : bool
|
|
406
|
+
Whether to only return commands that are directly callable.
|
|
407
|
+
If True, command groups and subgroups will be skipped.
|
|
408
|
+
|
|
409
|
+
Yields
|
|
410
|
+
------
|
|
411
|
+
CommandT[ClientT]
|
|
412
|
+
The next command that matches the given criteria.
|
|
413
|
+
|
|
414
|
+
Usage
|
|
415
|
+
-----
|
|
416
|
+
```py
|
|
417
|
+
for cmd in plugin.walk_commands(hikari.CommandType.SLASH):
|
|
418
|
+
print(cmd.name)
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
!!! tip
|
|
422
|
+
To iterate over all types of commands, you may use [`itertools.chain()`][itertools.chain]:
|
|
423
|
+
|
|
424
|
+
```py
|
|
425
|
+
import itertools
|
|
426
|
+
|
|
427
|
+
for cmd in itertools.chain(
|
|
428
|
+
plugin.walk_commands(hikari.CommandType.SLASH),
|
|
429
|
+
plugin.walk_commands(hikari.CommandType.MESSAGE),
|
|
430
|
+
plugin.walk_commands(hikari.CommandType.USER),
|
|
431
|
+
):
|
|
432
|
+
print(cmd.name)
|
|
433
|
+
```
|
|
434
|
+
"""
|
|
435
|
+
if hikari.CommandType.SLASH is command_type:
|
|
436
|
+
for command in self._slash_commands.values():
|
|
437
|
+
if isinstance(command, SlashCommand):
|
|
438
|
+
yield command
|
|
439
|
+
continue
|
|
440
|
+
|
|
441
|
+
if not callable_only:
|
|
442
|
+
yield command
|
|
443
|
+
|
|
444
|
+
for sub in command.children.values():
|
|
445
|
+
if isinstance(sub, SlashSubCommand):
|
|
446
|
+
yield sub
|
|
447
|
+
continue
|
|
448
|
+
|
|
449
|
+
if not callable_only:
|
|
450
|
+
yield sub
|
|
451
|
+
|
|
452
|
+
for subsub in sub.children.values():
|
|
453
|
+
yield subsub
|
|
454
|
+
|
|
455
|
+
elif hikari.CommandType.MESSAGE is command_type:
|
|
456
|
+
for command in self._message_commands.values():
|
|
457
|
+
yield command
|
|
458
|
+
|
|
459
|
+
elif hikari.CommandType.USER is command_type:
|
|
460
|
+
for command in self._user_commands.values():
|
|
461
|
+
yield command
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
# MIT License
|
|
465
|
+
#
|
|
466
|
+
# Copyright (c) 2023-present hypergonial
|
|
467
|
+
#
|
|
468
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
469
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
470
|
+
# in the Software without restriction, including without limitation the rights
|
|
471
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
472
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
473
|
+
# furnished to do so, subject to the following conditions:
|
|
474
|
+
#
|
|
475
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
476
|
+
# copies or substantial portions of the Software.
|
|
477
|
+
#
|
|
478
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
479
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
480
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
481
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
482
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
483
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
484
|
+
# SOFTWARE.
|
arc/client.py
CHANGED
|
@@ -10,14 +10,26 @@ from arc.context import Context
|
|
|
10
10
|
from arc.errors import NoResponseIssuedError
|
|
11
11
|
from arc.events import CommandErrorEvent
|
|
12
12
|
from arc.internal.sigparse import parse_event_signature
|
|
13
|
+
from arc.internal.types import GatewayBotT, RESTBotT
|
|
13
14
|
from arc.plugin import GatewayPluginBase, RESTPluginBase
|
|
14
15
|
|
|
15
16
|
if t.TYPE_CHECKING:
|
|
16
17
|
import typing_extensions as te
|
|
17
18
|
|
|
19
|
+
from arc import AutodeferMode
|
|
20
|
+
|
|
18
21
|
from .internal.types import EventCallbackT, EventT, ResponseBuilderT
|
|
19
22
|
|
|
20
|
-
__all__ = (
|
|
23
|
+
__all__ = (
|
|
24
|
+
"GatewayClientBase",
|
|
25
|
+
"RESTClientBase",
|
|
26
|
+
"GatewayClient",
|
|
27
|
+
"RESTClient",
|
|
28
|
+
"GatewayContext",
|
|
29
|
+
"RESTContext",
|
|
30
|
+
"RESTPlugin",
|
|
31
|
+
"GatewayPlugin",
|
|
32
|
+
)
|
|
21
33
|
|
|
22
34
|
|
|
23
35
|
T = t.TypeVar("T")
|
|
@@ -26,20 +38,33 @@ P = t.ParamSpec("P")
|
|
|
26
38
|
logger = logging.getLogger(__name__)
|
|
27
39
|
|
|
28
40
|
|
|
29
|
-
class
|
|
30
|
-
"""The
|
|
31
|
-
|
|
41
|
+
class GatewayClientBase(Client[GatewayBotT]):
|
|
42
|
+
"""The base class for an arc client with Gateway support.
|
|
43
|
+
|
|
44
|
+
If you want to use a RESTBot, use [`RESTClientBase`][arc.client.RESTClientBase] instead.
|
|
32
45
|
|
|
33
46
|
Parameters
|
|
34
47
|
----------
|
|
35
48
|
app : hikari.GatewayBotAware
|
|
36
49
|
The application this client is for.
|
|
37
|
-
default_enabled_guilds : t.Sequence[hikari.
|
|
38
|
-
The guilds that
|
|
39
|
-
autosync : bool
|
|
40
|
-
Whether to automatically sync commands on startup
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
default_enabled_guilds : t.Sequence[hikari.Snowflakeish] | None
|
|
51
|
+
The guilds that commands will be registered in by default
|
|
52
|
+
autosync : bool
|
|
53
|
+
Whether to automatically sync commands on startup
|
|
54
|
+
autodefer : bool | AutodeferMode
|
|
55
|
+
Whether to automatically defer responses
|
|
56
|
+
This applies to all commands, and can be overridden on a per-command basis.
|
|
57
|
+
default_permissions : hikari.Permissions | hikari.UndefinedType
|
|
58
|
+
The default permissions for commands
|
|
59
|
+
This applies to all commands, and can be overridden on a per-command basis.
|
|
60
|
+
is_nsfw : bool
|
|
61
|
+
Whether commands are NSFW
|
|
62
|
+
This applies to all commands, and can be overridden on a per-command basis.
|
|
63
|
+
is_dm_enabled : bool
|
|
64
|
+
Whether commands are enabled in DMs
|
|
65
|
+
This applies to all commands, and can be overridden on a per-command basis.
|
|
66
|
+
provided_locales : t.Sequence[hikari.Locale] | None
|
|
67
|
+
The locales that will be provided to the client by locale provider callbacks
|
|
43
68
|
|
|
44
69
|
Usage
|
|
45
70
|
-----
|
|
@@ -48,6 +73,7 @@ class GatewayClient(Client[hikari.GatewayBotAware]):
|
|
|
48
73
|
import arc
|
|
49
74
|
|
|
50
75
|
bot = hikari.GatewayBot("TOKEN")
|
|
76
|
+
# Default client implementation
|
|
51
77
|
client = arc.GatewayClient(bot)
|
|
52
78
|
|
|
53
79
|
...
|
|
@@ -58,14 +84,26 @@ class GatewayClient(Client[hikari.GatewayBotAware]):
|
|
|
58
84
|
|
|
59
85
|
def __init__(
|
|
60
86
|
self,
|
|
61
|
-
app:
|
|
87
|
+
app: GatewayBotT,
|
|
62
88
|
*,
|
|
63
|
-
default_enabled_guilds: t.Sequence[hikari.
|
|
89
|
+
default_enabled_guilds: t.Sequence[hikari.Snowflakeish | hikari.PartialGuild]
|
|
90
|
+
| hikari.UndefinedType = hikari.UNDEFINED,
|
|
64
91
|
autosync: bool = True,
|
|
92
|
+
autodefer: bool | AutodeferMode = True,
|
|
93
|
+
default_permissions: hikari.Permissions | hikari.UndefinedType = hikari.UNDEFINED,
|
|
94
|
+
is_nsfw: bool = False,
|
|
95
|
+
is_dm_enabled: bool = True,
|
|
65
96
|
provided_locales: t.Sequence[hikari.Locale] | None = None,
|
|
66
97
|
) -> None:
|
|
67
98
|
super().__init__(
|
|
68
|
-
app,
|
|
99
|
+
app,
|
|
100
|
+
default_enabled_guilds=default_enabled_guilds,
|
|
101
|
+
autosync=autosync,
|
|
102
|
+
autodefer=autodefer,
|
|
103
|
+
default_permissions=default_permissions,
|
|
104
|
+
is_nsfw=is_nsfw,
|
|
105
|
+
is_dm_enabled=is_dm_enabled,
|
|
106
|
+
provided_locales=provided_locales,
|
|
69
107
|
)
|
|
70
108
|
self.app.event_manager.subscribe(hikari.StartedEvent, self._on_gatewaybot_startup)
|
|
71
109
|
self.app.event_manager.subscribe(hikari.StoppingEvent, self._on_gatewaybot_shutdown)
|
|
@@ -108,7 +146,7 @@ class GatewayClient(Client[hikari.GatewayBotAware]):
|
|
|
108
146
|
The event type to subscribe to.
|
|
109
147
|
|
|
110
148
|
`EventT` must be a subclass of `hikari.events.base_events.Event`.
|
|
111
|
-
callback : t.Callable[
|
|
149
|
+
callback : t.Callable[EventT], t.Awaitable[None]]
|
|
112
150
|
The callback to call when the event is dispatched.
|
|
113
151
|
"""
|
|
114
152
|
self.app.event_manager.subscribe(event_type, callback) # pyright: ignore reportGeneralTypeIssues
|
|
@@ -120,7 +158,7 @@ class GatewayClient(Client[hikari.GatewayBotAware]):
|
|
|
120
158
|
----------
|
|
121
159
|
event_type : type[EventT]
|
|
122
160
|
The event type to unsubscribe from.
|
|
123
|
-
callback : t.Callable[
|
|
161
|
+
callback : t.Callable[[EventT], t.Awaitable[None]]
|
|
124
162
|
The callback to unsubscribe.
|
|
125
163
|
"""
|
|
126
164
|
self.app.event_manager.unsubscribe(event_type, callback) # pyright: ignore reportGeneralTypeIssues
|
|
@@ -140,7 +178,7 @@ class GatewayClient(Client[hikari.GatewayBotAware]):
|
|
|
140
178
|
|
|
141
179
|
Returns
|
|
142
180
|
-------
|
|
143
|
-
t.Callable[t.Callable[
|
|
181
|
+
t.Callable[t.Callable[[EventT], t.Awaitable[None]]], t.Callable[[EventT], t.Awaitable[None]]]
|
|
144
182
|
A decorator for a coroutine function that passes it to
|
|
145
183
|
`EventManager.subscribe` before returning the function
|
|
146
184
|
reference.
|
|
@@ -157,20 +195,33 @@ class GatewayClient(Client[hikari.GatewayBotAware]):
|
|
|
157
195
|
return decorator
|
|
158
196
|
|
|
159
197
|
|
|
160
|
-
class
|
|
161
|
-
"""The
|
|
162
|
-
|
|
198
|
+
class RESTClientBase(Client[RESTBotT]):
|
|
199
|
+
"""The base class for an arc client with REST support.
|
|
200
|
+
|
|
201
|
+
If you want to use GatewayBot, use [`GatewayClient`][arc.client.GatewayClientBase] instead.
|
|
163
202
|
|
|
164
203
|
Parameters
|
|
165
204
|
----------
|
|
166
|
-
app : hikari.
|
|
205
|
+
app : hikari.GatewayBotAware
|
|
167
206
|
The application this client is for.
|
|
168
|
-
default_enabled_guilds : t.Sequence[hikari.
|
|
169
|
-
The guilds that
|
|
170
|
-
autosync : bool
|
|
171
|
-
Whether to automatically sync commands on startup
|
|
172
|
-
|
|
173
|
-
|
|
207
|
+
default_enabled_guilds : t.Sequence[hikari.Snowflakeish | hikari.PartialGuild] | None
|
|
208
|
+
The guilds that commands will be registered in by default
|
|
209
|
+
autosync : bool
|
|
210
|
+
Whether to automatically sync commands on startup
|
|
211
|
+
autodefer : bool | AutodeferMode
|
|
212
|
+
Whether to automatically defer responses
|
|
213
|
+
This applies to all commands, and can be overridden on a per-command basis.
|
|
214
|
+
default_permissions : hikari.Permissions | hikari.UndefinedType
|
|
215
|
+
The default permissions for commands
|
|
216
|
+
This applies to all commands, and can be overridden on a per-command basis.
|
|
217
|
+
is_nsfw : bool
|
|
218
|
+
Whether commands are NSFW
|
|
219
|
+
This applies to all commands, and can be overridden on a per-command basis.
|
|
220
|
+
is_dm_enabled : bool
|
|
221
|
+
Whether commands are enabled in DMs
|
|
222
|
+
This applies to all commands, and can be overridden on a per-command basis.
|
|
223
|
+
provided_locales : t.Sequence[hikari.Locale] | None
|
|
224
|
+
The locales that will be provided to the client by locale provider callbacks
|
|
174
225
|
|
|
175
226
|
|
|
176
227
|
Usage
|
|
@@ -180,6 +231,7 @@ class RESTClient(Client[hikari.RESTBotAware]):
|
|
|
180
231
|
import arc
|
|
181
232
|
|
|
182
233
|
bot = hikari.RESTBot("TOKEN")
|
|
234
|
+
# Default client implementation
|
|
183
235
|
client = arc.RESTClient(bot)
|
|
184
236
|
|
|
185
237
|
...
|
|
@@ -193,14 +245,26 @@ class RESTClient(Client[hikari.RESTBotAware]):
|
|
|
193
245
|
|
|
194
246
|
def __init__(
|
|
195
247
|
self,
|
|
196
|
-
app:
|
|
248
|
+
app: RESTBotT,
|
|
197
249
|
*,
|
|
198
|
-
default_enabled_guilds: t.Sequence[hikari.
|
|
250
|
+
default_enabled_guilds: t.Sequence[hikari.Snowflakeish | hikari.PartialGuild]
|
|
251
|
+
| hikari.UndefinedType = hikari.UNDEFINED,
|
|
199
252
|
autosync: bool = True,
|
|
253
|
+
autodefer: bool | AutodeferMode = True,
|
|
254
|
+
default_permissions: hikari.Permissions | hikari.UndefinedType = hikari.UNDEFINED,
|
|
255
|
+
is_nsfw: bool = False,
|
|
256
|
+
is_dm_enabled: bool = True,
|
|
200
257
|
provided_locales: t.Sequence[hikari.Locale] | None = None,
|
|
201
258
|
) -> None:
|
|
202
259
|
super().__init__(
|
|
203
|
-
app,
|
|
260
|
+
app,
|
|
261
|
+
default_enabled_guilds=default_enabled_guilds,
|
|
262
|
+
autosync=autosync,
|
|
263
|
+
autodefer=autodefer,
|
|
264
|
+
default_permissions=default_permissions,
|
|
265
|
+
is_nsfw=is_nsfw,
|
|
266
|
+
is_dm_enabled=is_dm_enabled,
|
|
267
|
+
provided_locales=provided_locales,
|
|
204
268
|
)
|
|
205
269
|
self.app.add_startup_callback(self._on_restbot_startup)
|
|
206
270
|
self.app.add_shutdown_callback(self._on_restbot_shutdown)
|
|
@@ -261,16 +325,22 @@ class RESTClient(Client[hikari.RESTBotAware]):
|
|
|
261
325
|
return builder
|
|
262
326
|
|
|
263
327
|
|
|
264
|
-
|
|
328
|
+
GatewayClient = GatewayClientBase[hikari.GatewayBotAware]
|
|
329
|
+
"""The default gateway client implementation. An alias for [`arc.GatewayClientBase[hikari.GatewayBotAware]`][arc.client.GatewayClientBase]."""
|
|
330
|
+
|
|
331
|
+
RESTClient = RESTClientBase[hikari.RESTBotAware]
|
|
332
|
+
"""The default REST client implementation. An alias for [`arc.RESTClientBase[hikari.RESTBotAware]`][arc.client.RESTClientBase]."""
|
|
333
|
+
|
|
334
|
+
GatewayContext = Context[GatewayClientBase[hikari.GatewayBotAware]]
|
|
265
335
|
"""A context using the default gateway client implementation. An alias for [`arc.Context[arc.GatewayClient]`][arc.context.base.Context]."""
|
|
266
336
|
|
|
267
|
-
RESTContext = Context[
|
|
337
|
+
RESTContext = Context[RESTClientBase[hikari.RESTBotAware]]
|
|
268
338
|
"""A context using the default REST client implementation. An alias for [`arc.Context[arc.RESTClient]`][arc.context.base.Context]."""
|
|
269
339
|
|
|
270
|
-
RESTPlugin = RESTPluginBase[
|
|
340
|
+
RESTPlugin = RESTPluginBase[RESTClientBase[hikari.RESTBotAware]]
|
|
271
341
|
"""A plugin using the default REST client implementation. An alias for [`arc.RESTPluginBase[arc.RESTClient]`][arc.plugin.RESTPluginBase]."""
|
|
272
342
|
|
|
273
|
-
GatewayPlugin = GatewayPluginBase[
|
|
343
|
+
GatewayPlugin = GatewayPluginBase[GatewayClientBase[hikari.GatewayBotAware]]
|
|
274
344
|
"""An alias for [`arc.GatewayPluginBase[arc.GatewayClient]`][arc.plugin.GatewayPluginBase]."""
|
|
275
345
|
|
|
276
346
|
# MIT License
|