disagreement 0.1.0rc1__tar.gz → 0.1.0rc3__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.
- disagreement-0.1.0rc3/MANIFEST.in +4 -0
- {disagreement-0.1.0rc1/disagreement.egg-info → disagreement-0.1.0rc3}/PKG-INFO +6 -5
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/README.md +1 -1
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/__init__.py +9 -5
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/client.py +41 -6
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/color.py +28 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/enums.py +5 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/app_commands/__init__.py +2 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/app_commands/commands.py +13 -99
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/app_commands/decorators.py +1 -1
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/app_commands/handler.py +25 -12
- disagreement-0.1.0rc3/disagreement/ext/app_commands/hybrid.py +61 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/commands/cog.py +15 -6
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/commands/core.py +28 -12
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/tasks.py +46 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/gateway.py +102 -63
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/http.py +134 -17
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/interactions.py +17 -14
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/models.py +124 -9
- disagreement-0.1.0rc3/disagreement/py.typed +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ui/modal.py +1 -1
- disagreement-0.1.0rc3/disagreement/utils.py +73 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3/disagreement.egg-info}/PKG-INFO +6 -5
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement.egg-info/SOURCES.txt +12 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement.egg-info/requires.txt +2 -2
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/caching.md +1 -1
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/converters.md +3 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/gateway.md +6 -0
- disagreement-0.1.0rc3/docs/message_history.md +16 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/task_loop.md +16 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/using_components.md +1 -1
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/webhooks.md +9 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/examples/basic_bot.py +5 -4
- disagreement-0.1.0rc3/examples/extension_management.py +45 -0
- disagreement-0.1.0rc3/examples/message_history.py +31 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/examples/modal_send.py +1 -0
- disagreement-0.1.0rc3/examples/sample_extension.py +16 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/examples/sharded_bot.py +3 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/pyproject.toml +5 -4
- disagreement-0.1.0rc3/tests/test_color_acceptance.py +29 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_command_checks.py +2 -2
- disagreement-0.1.0rc3/tests/test_gateway_intent.py +7 -0
- disagreement-0.1.0rc3/tests/test_http_rate_limit.py +70 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_http_reactions.py +51 -6
- disagreement-0.1.0rc3/tests/test_member.py +22 -0
- disagreement-0.1.0rc3/tests/test_message_pager.py +37 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_modals.py +2 -2
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_tasks_extension.py +24 -0
- disagreement-0.1.0rc3/tests/test_textchannel_history.py +24 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_webhooks.py +43 -0
- disagreement-0.1.0rc1/MANIFEST.in +0 -3
- disagreement-0.1.0rc1/disagreement/utils.py +0 -10
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/LICENSE +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/audio.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/cache.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/components.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/error_handler.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/errors.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/event_dispatcher.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/__init__.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/app_commands/context.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/app_commands/converters.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/commands/__init__.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/commands/converters.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/commands/decorators.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/commands/errors.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/commands/help.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/commands/view.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ext/loader.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/hybrid_context.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/i18n.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/logging_config.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/oauth.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/permissions.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/rate_limiter.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/shard_manager.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/typing.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ui/__init__.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ui/button.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ui/item.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ui/select.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/ui/view.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement/voice_client.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement.egg-info/dependency_links.txt +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/disagreement.egg-info/top_level.txt +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/commands.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/context_menus.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/events.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/extension_loader.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/i18n.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/oauth2.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/permissions.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/presence.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/reactions.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/sharding.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/slash_commands.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/typing_indicator.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/voice_client.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/docs/voice_features.md +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/examples/component_bot.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/examples/context_menus.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/examples/hybrid_bot.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/examples/modal_command.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/examples/task_loop.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/examples/voice_bot.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/setup.cfg +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_additional_converters.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_cache.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_channel_permissions.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_client_context_manager.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_color.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_components_factory.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_context.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_context_menus.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_converter_registration.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_converters.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_error_handler.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_errors.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_event_dispatcher.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_event_error_hook.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_extension_loader.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_gateway_backoff.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_help_command.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_hybrid_context.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_i18n.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_interaction.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_logging_config.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_modal_send.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_oauth.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_permissions.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_presence_and_typing.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_presence_update.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_rate_limiter.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_reactions.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_send_files.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_sharding.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_slash_contexts.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_textchannel_purge.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_typing_indicator.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_ui.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_utils.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_view_layout.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_voice_client.py +0 -0
- {disagreement-0.1.0rc1 → disagreement-0.1.0rc3}/tests/test_wait_for.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: disagreement
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.0rc3
|
4
4
|
Summary: A Python library for the Discord API.
|
5
5
|
Author-email: Slipstream <me@slipstreamm.dev>
|
6
6
|
License: BSD 3-Clause
|
@@ -12,22 +12,23 @@ Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: License :: OSI Approved :: BSD License
|
13
13
|
Classifier: Operating System :: OS Independent
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
15
16
|
Classifier: Programming Language :: Python :: 3.11
|
16
17
|
Classifier: Programming Language :: Python :: 3.12
|
17
18
|
Classifier: Programming Language :: Python :: 3.13
|
18
19
|
Classifier: Topic :: Software Development :: Libraries
|
19
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
20
21
|
Classifier: Topic :: Internet
|
21
|
-
Requires-Python: >=3.
|
22
|
+
Requires-Python: >=3.10
|
22
23
|
Description-Content-Type: text/markdown
|
23
24
|
License-File: LICENSE
|
24
25
|
Requires-Dist: aiohttp<4.0.0,>=3.9.0
|
25
26
|
Provides-Extra: test
|
26
27
|
Requires-Dist: pytest>=8.0.0; extra == "test"
|
27
28
|
Requires-Dist: pytest-asyncio>=1.0.0; extra == "test"
|
28
|
-
Requires-Dist: hypothesis>=6.
|
29
|
+
Requires-Dist: hypothesis>=6.132.0; extra == "test"
|
29
30
|
Provides-Extra: dev
|
30
|
-
Requires-Dist: dotenv>=0.0
|
31
|
+
Requires-Dist: python-dotenv>=1.0.0; extra == "dev"
|
31
32
|
Dynamic: license-file
|
32
33
|
|
33
34
|
# Disagreement
|
@@ -53,7 +54,7 @@ pip install disagreement
|
|
53
54
|
pip install -e .
|
54
55
|
```
|
55
56
|
|
56
|
-
Requires Python 3.
|
57
|
+
Requires Python 3.10 or newer.
|
57
58
|
|
58
59
|
## Basic Usage
|
59
60
|
|
@@ -14,7 +14,7 @@ __title__ = "disagreement"
|
|
14
14
|
__author__ = "Slipstream"
|
15
15
|
__license__ = "BSD 3-Clause License"
|
16
16
|
__copyright__ = "Copyright 2025 Slipstream"
|
17
|
-
__version__ = "0.1.
|
17
|
+
__version__ = "0.1.0rc3"
|
18
18
|
|
19
19
|
from .client import Client, AutoShardedClient
|
20
20
|
from .models import Message, User, Reaction
|
@@ -30,12 +30,16 @@ from .errors import (
|
|
30
30
|
NotFound,
|
31
31
|
)
|
32
32
|
from .color import Color
|
33
|
-
from .utils import utcnow
|
33
|
+
from .utils import utcnow, message_pager
|
34
34
|
from .enums import GatewayIntent, GatewayOpcode # Export enums
|
35
35
|
from .error_handler import setup_global_error_handler
|
36
36
|
from .hybrid_context import HybridContext
|
37
37
|
from .ext import tasks
|
38
|
+
from .logging_config import setup_logging
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
import logging
|
41
|
+
|
42
|
+
|
43
|
+
# Configure a default logger if none has been configured yet
|
44
|
+
if not logging.getLogger().hasHandlers():
|
45
|
+
setup_logging(logging.INFO)
|
@@ -16,6 +16,7 @@ from typing import (
|
|
16
16
|
List,
|
17
17
|
Dict,
|
18
18
|
)
|
19
|
+
from types import ModuleType
|
19
20
|
|
20
21
|
from .http import HTTPClient
|
21
22
|
from .gateway import GatewayClient
|
@@ -28,6 +29,7 @@ from .ext.commands.core import CommandHandler
|
|
28
29
|
from .ext.commands.cog import Cog
|
29
30
|
from .ext.app_commands.handler import AppCommandHandler
|
30
31
|
from .ext.app_commands.context import AppCommandContext
|
32
|
+
from .ext import loader as ext_loader
|
31
33
|
from .interactions import Interaction, Snowflake
|
32
34
|
from .error_handler import setup_global_error_handler
|
33
35
|
from .voice_client import VoiceClient
|
@@ -121,14 +123,10 @@ class Client:
|
|
121
123
|
|
122
124
|
self._closed: bool = False
|
123
125
|
self._ready_event: asyncio.Event = asyncio.Event()
|
124
|
-
self.application_id: Optional[Snowflake] = None # For Application Commands
|
125
126
|
self.user: Optional["User"] = (
|
126
127
|
None # The bot's own user object, populated on READY
|
127
128
|
)
|
128
129
|
|
129
|
-
# Initialize AppCommandHandler
|
130
|
-
self.app_command_handler: AppCommandHandler = AppCommandHandler(client=self)
|
131
|
-
|
132
130
|
# Internal Caches
|
133
131
|
self._guilds: Dict[Snowflake, "Guild"] = {}
|
134
132
|
self._channels: Dict[Snowflake, "Channel"] = (
|
@@ -638,6 +636,23 @@ class Client:
|
|
638
636
|
# import traceback
|
639
637
|
# traceback.print_exception(type(error.original), error.original, error.original.__traceback__)
|
640
638
|
|
639
|
+
# --- Extension Management Methods ---
|
640
|
+
|
641
|
+
def load_extension(self, name: str) -> ModuleType:
|
642
|
+
"""Load an extension by name using :mod:`disagreement.ext.loader`."""
|
643
|
+
|
644
|
+
return ext_loader.load_extension(name)
|
645
|
+
|
646
|
+
def unload_extension(self, name: str) -> None:
|
647
|
+
"""Unload a previously loaded extension."""
|
648
|
+
|
649
|
+
ext_loader.unload_extension(name)
|
650
|
+
|
651
|
+
def reload_extension(self, name: str) -> ModuleType:
|
652
|
+
"""Reload an extension by name."""
|
653
|
+
|
654
|
+
return ext_loader.reload_extension(name)
|
655
|
+
|
641
656
|
# --- Model Parsing and Fetching ---
|
642
657
|
|
643
658
|
def parse_user(self, data: Dict[str, Any]) -> "User":
|
@@ -920,12 +935,12 @@ class Client:
|
|
920
935
|
await view._start(self)
|
921
936
|
components_payload = view.to_components_payload()
|
922
937
|
elif components:
|
923
|
-
from .models import
|
938
|
+
from .models import Component as ComponentModel
|
924
939
|
|
925
940
|
components_payload = [
|
926
941
|
comp.to_dict()
|
927
942
|
for comp in components
|
928
|
-
if isinstance(comp,
|
943
|
+
if isinstance(comp, ComponentModel)
|
929
944
|
]
|
930
945
|
|
931
946
|
message_data = await self._http.send_message(
|
@@ -1016,6 +1031,26 @@ class Client:
|
|
1016
1031
|
self._voice_clients[guild_id] = voice
|
1017
1032
|
return voice
|
1018
1033
|
|
1034
|
+
async def add_reaction(self, channel_id: str, message_id: str, emoji: str) -> None:
|
1035
|
+
"""|coro| Add a reaction to a message."""
|
1036
|
+
|
1037
|
+
await self.create_reaction(channel_id, message_id, emoji)
|
1038
|
+
|
1039
|
+
async def remove_reaction(
|
1040
|
+
self, channel_id: str, message_id: str, emoji: str
|
1041
|
+
) -> None:
|
1042
|
+
"""|coro| Remove the bot's reaction from a message."""
|
1043
|
+
|
1044
|
+
await self.delete_reaction(channel_id, message_id, emoji)
|
1045
|
+
|
1046
|
+
async def clear_reactions(self, channel_id: str, message_id: str) -> None:
|
1047
|
+
"""|coro| Remove all reactions from a message."""
|
1048
|
+
|
1049
|
+
if self._closed:
|
1050
|
+
raise DisagreementException("Client is closed.")
|
1051
|
+
|
1052
|
+
await self._http.clear_reactions(channel_id, message_id)
|
1053
|
+
|
1019
1054
|
async def create_reaction(
|
1020
1055
|
self, channel_id: str, message_id: str, emoji: str
|
1021
1056
|
) -> None:
|
@@ -48,3 +48,31 @@ class Color:
|
|
48
48
|
|
49
49
|
def to_rgb(self) -> tuple[int, int, int]:
|
50
50
|
return ((self.value >> 16) & 0xFF, (self.value >> 8) & 0xFF, self.value & 0xFF)
|
51
|
+
|
52
|
+
@classmethod
|
53
|
+
def parse(cls, value: "Color | int | str | tuple[int, int, int] | None") -> "Color | None":
|
54
|
+
"""Convert ``value`` to a :class:`Color` instance.
|
55
|
+
|
56
|
+
Parameters
|
57
|
+
----------
|
58
|
+
value:
|
59
|
+
The value to convert. May be ``None``, an existing ``Color``, an
|
60
|
+
integer in the ``0xRRGGBB`` format, or a hex string like ``"#RRGGBB"``.
|
61
|
+
|
62
|
+
Returns
|
63
|
+
-------
|
64
|
+
Optional[Color]
|
65
|
+
A ``Color`` object if ``value`` is not ``None``.
|
66
|
+
"""
|
67
|
+
|
68
|
+
if value is None:
|
69
|
+
return None
|
70
|
+
if isinstance(value, cls):
|
71
|
+
return value
|
72
|
+
if isinstance(value, int):
|
73
|
+
return cls(value)
|
74
|
+
if isinstance(value, str):
|
75
|
+
return cls.from_hex(value)
|
76
|
+
if isinstance(value, tuple) and len(value) == 3:
|
77
|
+
return cls.from_rgb(*value)
|
78
|
+
raise TypeError("Color value must be Color, int, str, tuple, or None")
|
@@ -49,6 +49,11 @@ class GatewayIntent(IntEnum):
|
|
49
49
|
AUTO_MODERATION_CONFIGURATION = 1 << 20
|
50
50
|
AUTO_MODERATION_EXECUTION = 1 << 21
|
51
51
|
|
52
|
+
@classmethod
|
53
|
+
def none(cls) -> int:
|
54
|
+
"""Return a bitmask representing no intents."""
|
55
|
+
return 0
|
56
|
+
|
52
57
|
@classmethod
|
53
58
|
def default(cls) -> int:
|
54
59
|
"""Returns default intents (excluding privileged ones like members, presences, message content)."""
|
@@ -1,58 +1,25 @@
|
|
1
1
|
# disagreement/ext/app_commands/commands.py
|
2
2
|
|
3
3
|
import inspect
|
4
|
-
from typing import Callable,
|
4
|
+
from typing import Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING
|
5
5
|
|
6
|
+
from disagreement.enums import (
|
7
|
+
ApplicationCommandType,
|
8
|
+
ApplicationCommandOptionType,
|
9
|
+
IntegrationType,
|
10
|
+
InteractionContextType,
|
11
|
+
)
|
12
|
+
from disagreement.interactions import ApplicationCommandOption, Snowflake
|
6
13
|
|
7
14
|
if TYPE_CHECKING:
|
8
|
-
from disagreement.ext.commands.core import
|
9
|
-
|
10
|
-
|
11
|
-
from disagreement.interactions import ApplicationCommandOption, Snowflake
|
12
|
-
from disagreement.enums import (
|
13
|
-
ApplicationCommandType,
|
14
|
-
IntegrationType,
|
15
|
-
InteractionContextType,
|
16
|
-
ApplicationCommandOptionType, # Added
|
17
|
-
)
|
18
|
-
from disagreement.ext.commands.cog import Cog # Corrected import path
|
19
|
-
|
20
|
-
# Placeholder for Cog if not using the existing one or if it needs adaptation
|
15
|
+
from disagreement.ext.commands.core import Command as PrefixCommand
|
16
|
+
from disagreement.ext.commands.cog import Cog
|
17
|
+
|
21
18
|
if not TYPE_CHECKING:
|
22
|
-
# This dynamic Cog = Any might not be ideal if Cog is used in runtime type checks.
|
23
|
-
# However, for type hinting purposes when TYPE_CHECKING is false, it avoids import.
|
24
|
-
# If Cog is needed at runtime by this module (it is, for AppCommand.cog type hint),
|
25
|
-
# it should be imported directly.
|
26
|
-
# For now, the TYPE_CHECKING block handles the proper import for static analysis.
|
27
|
-
# Let's ensure Cog is available at runtime if AppCommand.cog is accessed.
|
28
|
-
# A simple way is to import it outside TYPE_CHECKING too, if it doesn't cause circularity.
|
29
|
-
# Given its usage, a forward reference string 'Cog' might be better in AppCommand.cog type hint.
|
30
|
-
# Let's try importing it directly for runtime, assuming no circularity with this specific module.
|
31
|
-
try:
|
32
|
-
from disagreement.ext.commands.cog import Cog
|
33
|
-
except ImportError:
|
34
|
-
Cog = Any # Fallback if direct import fails (e.g. during partial builds/tests)
|
35
|
-
# Import PrefixCommand at runtime for HybridCommand
|
36
19
|
try:
|
37
20
|
from disagreement.ext.commands.core import Command as PrefixCommand
|
38
|
-
except
|
39
|
-
PrefixCommand = Any
|
40
|
-
# Import enums used at runtime
|
41
|
-
try:
|
42
|
-
from disagreement.enums import (
|
43
|
-
ApplicationCommandType,
|
44
|
-
IntegrationType,
|
45
|
-
InteractionContextType,
|
46
|
-
ApplicationCommandOptionType,
|
47
|
-
)
|
48
|
-
from disagreement.interactions import ApplicationCommandOption, Snowflake
|
49
|
-
except Exception: # pragma: no cover
|
50
|
-
ApplicationCommandType = ApplicationCommandOptionType = IntegrationType = (
|
51
|
-
InteractionContextType
|
52
|
-
) = Any # type: ignore
|
53
|
-
ApplicationCommandOption = Snowflake = Any # type: ignore
|
54
|
-
else: # When TYPE_CHECKING is true, Cog and PrefixCommand are already imported above.
|
55
|
-
pass
|
21
|
+
except ImportError:
|
22
|
+
PrefixCommand = Any
|
56
23
|
|
57
24
|
|
58
25
|
class AppCommand:
|
@@ -235,59 +202,6 @@ class MessageCommand(AppCommand):
|
|
235
202
|
super().__init__(callback, type=ApplicationCommandType.MESSAGE, **kwargs)
|
236
203
|
|
237
204
|
|
238
|
-
class HybridCommand(SlashCommand, PrefixCommand): # Inherit from both
|
239
|
-
"""
|
240
|
-
Represents a command that can be invoked as both a slash command
|
241
|
-
and a traditional prefix-based command.
|
242
|
-
"""
|
243
|
-
|
244
|
-
def __init__(self, callback: Callable[..., Any], **kwargs: Any):
|
245
|
-
# Initialize SlashCommand part (which calls AppCommand.__init__)
|
246
|
-
# We need to ensure 'type' is correctly passed for AppCommand
|
247
|
-
# kwargs for SlashCommand: name, description, guild_ids, default_member_permissions, nsfw, parent, cog, etc.
|
248
|
-
# kwargs for PrefixCommand: name, aliases, brief, description, cog
|
249
|
-
|
250
|
-
# Pop prefix-specific args before passing to SlashCommand constructor
|
251
|
-
prefix_aliases = kwargs.pop("aliases", [])
|
252
|
-
prefix_brief = kwargs.pop("brief", None)
|
253
|
-
# Description is used by both, AppCommand's constructor will handle it.
|
254
|
-
# Name is used by both. Cog is used by both.
|
255
|
-
|
256
|
-
# Call SlashCommand's __init__
|
257
|
-
# This will set up name, description, callback, type=CHAT_INPUT, options, etc.
|
258
|
-
super().__init__(callback, **kwargs) # This is SlashCommand.__init__
|
259
|
-
|
260
|
-
# Now, explicitly initialize the PrefixCommand parts that SlashCommand didn't cover
|
261
|
-
# or that need specific values for the prefix version.
|
262
|
-
# PrefixCommand.__init__(self, callback, name=self.name, aliases=prefix_aliases, brief=prefix_brief, description=self.description, cog=self.cog)
|
263
|
-
# However, PrefixCommand.__init__ also sets self.params, which AppCommand already did.
|
264
|
-
# We need to be careful not to re-initialize things unnecessarily or incorrectly.
|
265
|
-
# Let's manually set the distinct attributes for the PrefixCommand aspect.
|
266
|
-
|
267
|
-
# Attributes from PrefixCommand:
|
268
|
-
# self.callback is already set by AppCommand
|
269
|
-
# self.name is already set by AppCommand
|
270
|
-
self.aliases: List[str] = (
|
271
|
-
prefix_aliases # This was specific to HybridCommand before, now aligns with PrefixCommand
|
272
|
-
)
|
273
|
-
self.brief: Optional[str] = prefix_brief
|
274
|
-
# self.description is already set by AppCommand (SlashCommand ensures it exists)
|
275
|
-
# self.cog is already set by AppCommand
|
276
|
-
# self.params is already set by AppCommand
|
277
|
-
|
278
|
-
# Ensure the MRO is handled correctly. Python's MRO (C3 linearization)
|
279
|
-
# should call SlashCommand's __init__ then AppCommand's __init__.
|
280
|
-
# PrefixCommand.__init__ won't be called automatically unless we explicitly call it.
|
281
|
-
# By setting attributes directly, we avoid potential issues with multiple __init__ calls
|
282
|
-
# if their logic overlaps too much (e.g., both trying to set self.params).
|
283
|
-
|
284
|
-
# We might need to override invoke if the context or argument passing differs significantly
|
285
|
-
# between app command invocation and prefix command invocation.
|
286
|
-
# For now, SlashCommand.invoke and PrefixCommand.invoke are separate.
|
287
|
-
# The correct one will be called depending on how the command is dispatched.
|
288
|
-
# The AppCommandHandler will use AppCommand.invoke (via SlashCommand).
|
289
|
-
# The prefix CommandHandler will use PrefixCommand.invoke.
|
290
|
-
# This seems acceptable.
|
291
205
|
|
292
206
|
|
293
207
|
class AppCommandGroup:
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# disagreement/ext/app_commands/handler.py
|
2
2
|
|
3
3
|
import inspect
|
4
|
+
import logging
|
4
5
|
from typing import (
|
5
6
|
TYPE_CHECKING,
|
6
7
|
Dict,
|
@@ -64,6 +65,9 @@ if not TYPE_CHECKING:
|
|
64
65
|
Message = Any
|
65
66
|
|
66
67
|
|
68
|
+
logger = logging.getLogger(__name__)
|
69
|
+
|
70
|
+
|
67
71
|
class AppCommandHandler:
|
68
72
|
"""
|
69
73
|
Manages application command registration, parsing, and dispatching.
|
@@ -544,7 +548,7 @@ class AppCommandHandler:
|
|
544
548
|
await command.invoke(ctx, *parsed_args, **parsed_kwargs)
|
545
549
|
|
546
550
|
except Exception as e:
|
547
|
-
|
551
|
+
logger.error("Error invoking app command '%s': %s", command.name, e)
|
548
552
|
await self.dispatch_app_command_error(ctx, e)
|
549
553
|
# else:
|
550
554
|
# # Default error reply if no handler on client
|
@@ -594,34 +598,43 @@ class AppCommandHandler:
|
|
594
598
|
payload = cmd_or_group.to_dict()
|
595
599
|
commands_to_sync.append(payload)
|
596
600
|
except AttributeError:
|
597
|
-
|
598
|
-
|
601
|
+
logger.warning(
|
602
|
+
"Command or group '%s' does not have a to_dict() method. Skipping.",
|
603
|
+
cmd_or_group.name,
|
599
604
|
)
|
600
605
|
except Exception as e:
|
601
|
-
|
602
|
-
|
606
|
+
logger.error(
|
607
|
+
"Error converting command/group '%s' to dict: %s. Skipping.",
|
608
|
+
cmd_or_group.name,
|
609
|
+
e,
|
603
610
|
)
|
604
611
|
|
605
612
|
if not commands_to_sync:
|
606
|
-
|
607
|
-
|
613
|
+
logger.info(
|
614
|
+
"No commands to sync for %s scope.",
|
615
|
+
f"guild {guild_id}" if guild_id else "global",
|
608
616
|
)
|
609
617
|
return
|
610
618
|
|
611
619
|
try:
|
612
620
|
if guild_id:
|
613
|
-
|
614
|
-
|
621
|
+
logger.info(
|
622
|
+
"Syncing %s commands for guild %s...",
|
623
|
+
len(commands_to_sync),
|
624
|
+
guild_id,
|
615
625
|
)
|
616
626
|
await self.client._http.bulk_overwrite_guild_application_commands(
|
617
627
|
application_id, guild_id, commands_to_sync
|
618
628
|
)
|
619
629
|
else:
|
620
|
-
|
630
|
+
logger.info(
|
631
|
+
"Syncing %s global commands...",
|
632
|
+
len(commands_to_sync),
|
633
|
+
)
|
621
634
|
await self.client._http.bulk_overwrite_global_application_commands(
|
622
635
|
application_id, commands_to_sync
|
623
636
|
)
|
624
|
-
|
637
|
+
logger.info("Command sync successful.")
|
625
638
|
except Exception as e:
|
626
|
-
|
639
|
+
logger.error("Error syncing application commands: %s", e)
|
627
640
|
# Consider re-raising or specific error handling
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# disagreement/ext/app_commands/hybrid.py
|
2
|
+
|
3
|
+
from typing import Any, Callable, List, Optional
|
4
|
+
|
5
|
+
from .commands import SlashCommand
|
6
|
+
from disagreement.ext.commands.core import PrefixCommand
|
7
|
+
|
8
|
+
|
9
|
+
class HybridCommand(SlashCommand, PrefixCommand): # Inherit from both
|
10
|
+
"""
|
11
|
+
Represents a command that can be invoked as both a slash command
|
12
|
+
and a traditional prefix-based command.
|
13
|
+
"""
|
14
|
+
|
15
|
+
def __init__(self, callback: Callable[..., Any], **kwargs: Any):
|
16
|
+
# Initialize SlashCommand part (which calls AppCommand.__init__)
|
17
|
+
# We need to ensure 'type' is correctly passed for AppCommand
|
18
|
+
# kwargs for SlashCommand: name, description, guild_ids, default_member_permissions, nsfw, parent, cog, etc.
|
19
|
+
# kwargs for PrefixCommand: name, aliases, brief, description, cog
|
20
|
+
|
21
|
+
# Pop prefix-specific args before passing to SlashCommand constructor
|
22
|
+
prefix_aliases = kwargs.pop("aliases", [])
|
23
|
+
prefix_brief = kwargs.pop("brief", None)
|
24
|
+
# Description is used by both, AppCommand's constructor will handle it.
|
25
|
+
# Name is used by both. Cog is used by both.
|
26
|
+
|
27
|
+
# Call SlashCommand's __init__
|
28
|
+
# This will set up name, description, callback, type=CHAT_INPUT, options, etc.
|
29
|
+
super().__init__(callback, **kwargs) # This is SlashCommand.__init__
|
30
|
+
|
31
|
+
# Now, explicitly initialize the PrefixCommand parts that SlashCommand didn't cover
|
32
|
+
# or that need specific values for the prefix version.
|
33
|
+
# PrefixCommand.__init__(self, callback, name=self.name, aliases=prefix_aliases, brief=prefix_brief, description=self.description, cog=self.cog)
|
34
|
+
# However, PrefixCommand.__init__ also sets self.params, which AppCommand already did.
|
35
|
+
# We need to be careful not to re-initialize things unnecessarily or incorrectly.
|
36
|
+
# Let's manually set the distinct attributes for the PrefixCommand aspect.
|
37
|
+
|
38
|
+
# Attributes from PrefixCommand:
|
39
|
+
# self.callback is already set by AppCommand
|
40
|
+
# self.name is already set by AppCommand
|
41
|
+
self.aliases: List[str] = (
|
42
|
+
prefix_aliases # This was specific to HybridCommand before, now aligns with PrefixCommand
|
43
|
+
)
|
44
|
+
self.brief: Optional[str] = prefix_brief
|
45
|
+
# self.description is already set by AppCommand (SlashCommand ensures it exists)
|
46
|
+
# self.cog is already set by AppCommand
|
47
|
+
# self.params is already set by AppCommand
|
48
|
+
|
49
|
+
# Ensure the MRO is handled correctly. Python's MRO (C3 linearization)
|
50
|
+
# should call SlashCommand's __init__ then AppCommand's __init__.
|
51
|
+
# PrefixCommand.__init__ won't be called automatically unless we explicitly call it.
|
52
|
+
# By setting attributes directly, we avoid potential issues with multiple __init__ calls
|
53
|
+
# if their logic overlaps too much (e.g., both trying to set self.params).
|
54
|
+
|
55
|
+
# We might need to override invoke if the context or argument passing differs significantly
|
56
|
+
# between app command invocation and prefix command invocation.
|
57
|
+
# For now, SlashCommand.invoke and PrefixCommand.invoke are separate.
|
58
|
+
# The correct one will be called depending on how the command is dispatched.
|
59
|
+
# The AppCommandHandler will use AppCommand.invoke (via SlashCommand).
|
60
|
+
# The prefix CommandHandler will use PrefixCommand.invoke.
|
61
|
+
# This seems acceptable.
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# disagreement/ext/commands/cog.py
|
2
2
|
|
3
3
|
import inspect
|
4
|
+
import logging
|
4
5
|
from typing import TYPE_CHECKING, List, Tuple, Callable, Awaitable, Any, Dict, Union
|
5
6
|
|
6
7
|
if TYPE_CHECKING:
|
@@ -16,6 +17,8 @@ else: # pragma: no cover - runtime imports for isinstance checks
|
|
16
17
|
# EventDispatcher might be needed if cogs register listeners directly
|
17
18
|
# from disagreement.event_dispatcher import EventDispatcher
|
18
19
|
|
20
|
+
logger = logging.getLogger(__name__)
|
21
|
+
|
19
22
|
|
20
23
|
class Cog:
|
21
24
|
"""
|
@@ -59,8 +62,10 @@ class Cog:
|
|
59
62
|
cmd.cog = self # Assign the cog instance to the command
|
60
63
|
if cmd.name in self._commands:
|
61
64
|
# This should ideally be caught earlier or handled by CommandHandler
|
62
|
-
|
63
|
-
|
65
|
+
logger.warning(
|
66
|
+
"Duplicate command name '%s' in cog '%s'. Overwriting.",
|
67
|
+
cmd.name,
|
68
|
+
self.cog_name,
|
64
69
|
)
|
65
70
|
self._commands[cmd.name.lower()] = cmd
|
66
71
|
# Also register aliases
|
@@ -79,8 +84,10 @@ class Cog:
|
|
79
84
|
# For AppCommandGroup, its commands will have cog set individually if they are AppCommands
|
80
85
|
self._app_commands_and_groups.append(app_cmd_obj)
|
81
86
|
else:
|
82
|
-
|
83
|
-
|
87
|
+
logger.warning(
|
88
|
+
"Member '%s' in cog '%s' has '__app_command_object__' but it's not an AppCommand or AppCommandGroup.",
|
89
|
+
member_name,
|
90
|
+
self.cog_name,
|
84
91
|
)
|
85
92
|
|
86
93
|
elif isinstance(member, (AppCommand, AppCommandGroup)):
|
@@ -92,8 +99,10 @@ class Cog:
|
|
92
99
|
# This is a method decorated with @commands.Cog.listener or @commands.listener
|
93
100
|
if not inspect.iscoroutinefunction(member):
|
94
101
|
# Decorator should have caught this, but double check
|
95
|
-
|
96
|
-
|
102
|
+
logger.warning(
|
103
|
+
"Listener '%s' in cog '%s' is not a coroutine. Skipping.",
|
104
|
+
member_name,
|
105
|
+
self.cog_name,
|
97
106
|
)
|
98
107
|
continue
|
99
108
|
|