disagreement 0.0.1__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 (103) hide show
  1. disagreement-0.0.1/LICENSE +26 -0
  2. disagreement-0.0.1/MANIFEST.in +3 -0
  3. disagreement-0.0.1/PKG-INFO +163 -0
  4. disagreement-0.0.1/README.md +131 -0
  5. disagreement-0.0.1/disagreement/__init__.py +36 -0
  6. disagreement-0.0.1/disagreement/cache.py +55 -0
  7. disagreement-0.0.1/disagreement/client.py +1144 -0
  8. disagreement-0.0.1/disagreement/components.py +166 -0
  9. disagreement-0.0.1/disagreement/enums.py +357 -0
  10. disagreement-0.0.1/disagreement/error_handler.py +33 -0
  11. disagreement-0.0.1/disagreement/errors.py +112 -0
  12. disagreement-0.0.1/disagreement/event_dispatcher.py +243 -0
  13. disagreement-0.0.1/disagreement/gateway.py +490 -0
  14. disagreement-0.0.1/disagreement/http.py +657 -0
  15. disagreement-0.0.1/disagreement/hybrid_context.py +32 -0
  16. disagreement-0.0.1/disagreement/i18n.py +22 -0
  17. disagreement-0.0.1/disagreement/interactions.py +572 -0
  18. disagreement-0.0.1/disagreement/logging_config.py +26 -0
  19. disagreement-0.0.1/disagreement/models.py +1642 -0
  20. disagreement-0.0.1/disagreement/oauth.py +109 -0
  21. disagreement-0.0.1/disagreement/permissions.py +99 -0
  22. disagreement-0.0.1/disagreement/rate_limiter.py +75 -0
  23. disagreement-0.0.1/disagreement/shard_manager.py +65 -0
  24. disagreement-0.0.1/disagreement/typing.py +42 -0
  25. disagreement-0.0.1/disagreement/ui/__init__.py +17 -0
  26. disagreement-0.0.1/disagreement/ui/button.py +99 -0
  27. disagreement-0.0.1/disagreement/ui/item.py +38 -0
  28. disagreement-0.0.1/disagreement/ui/modal.py +132 -0
  29. disagreement-0.0.1/disagreement/ui/select.py +92 -0
  30. disagreement-0.0.1/disagreement/ui/view.py +165 -0
  31. disagreement-0.0.1/disagreement/voice_client.py +120 -0
  32. disagreement-0.0.1/disagreement.egg-info/PKG-INFO +163 -0
  33. disagreement-0.0.1/disagreement.egg-info/SOURCES.txt +102 -0
  34. disagreement-0.0.1/disagreement.egg-info/dependency_links.txt +1 -0
  35. disagreement-0.0.1/disagreement.egg-info/requires.txt +9 -0
  36. disagreement-0.0.1/disagreement.egg-info/top_level.txt +1 -0
  37. disagreement-0.0.1/docs/caching.md +18 -0
  38. disagreement-0.0.1/docs/commands.md +51 -0
  39. disagreement-0.0.1/docs/context_menus.md +21 -0
  40. disagreement-0.0.1/docs/converters.md +25 -0
  41. disagreement-0.0.1/docs/events.md +25 -0
  42. disagreement-0.0.1/docs/gateway.md +17 -0
  43. disagreement-0.0.1/docs/i18n.md +36 -0
  44. disagreement-0.0.1/docs/oauth2.md +48 -0
  45. disagreement-0.0.1/docs/permissions.md +62 -0
  46. disagreement-0.0.1/docs/presence.md +29 -0
  47. disagreement-0.0.1/docs/reactions.md +32 -0
  48. disagreement-0.0.1/docs/slash_commands.md +22 -0
  49. disagreement-0.0.1/docs/task_loop.md +15 -0
  50. disagreement-0.0.1/docs/typing_indicator.md +16 -0
  51. disagreement-0.0.1/docs/using_components.md +168 -0
  52. disagreement-0.0.1/docs/voice_client.md +29 -0
  53. disagreement-0.0.1/docs/voice_features.md +17 -0
  54. disagreement-0.0.1/docs/webhooks.md +34 -0
  55. disagreement-0.0.1/examples/basic_bot.py +218 -0
  56. disagreement-0.0.1/examples/component_bot.py +292 -0
  57. disagreement-0.0.1/examples/context_menus.py +71 -0
  58. disagreement-0.0.1/examples/hybrid_bot.py +315 -0
  59. disagreement-0.0.1/examples/modal_command.py +65 -0
  60. disagreement-0.0.1/examples/modal_send.py +63 -0
  61. disagreement-0.0.1/examples/sharded_bot.py +36 -0
  62. disagreement-0.0.1/examples/task_loop.py +30 -0
  63. disagreement-0.0.1/examples/voice_bot.py +61 -0
  64. disagreement-0.0.1/pyproject.toml +55 -0
  65. disagreement-0.0.1/setup.cfg +7 -0
  66. disagreement-0.0.1/tests/test_additional_converters.py +172 -0
  67. disagreement-0.0.1/tests/test_cache.py +17 -0
  68. disagreement-0.0.1/tests/test_client_context_manager.py +33 -0
  69. disagreement-0.0.1/tests/test_command_checks.py +51 -0
  70. disagreement-0.0.1/tests/test_components_factory.py +29 -0
  71. disagreement-0.0.1/tests/test_context.py +199 -0
  72. disagreement-0.0.1/tests/test_context_menus.py +80 -0
  73. disagreement-0.0.1/tests/test_converter_registration.py +30 -0
  74. disagreement-0.0.1/tests/test_converters.py +113 -0
  75. disagreement-0.0.1/tests/test_error_handler.py +38 -0
  76. disagreement-0.0.1/tests/test_errors.py +23 -0
  77. disagreement-0.0.1/tests/test_event_dispatcher.py +68 -0
  78. disagreement-0.0.1/tests/test_event_error_hook.py +42 -0
  79. disagreement-0.0.1/tests/test_extension_loader.py +44 -0
  80. disagreement-0.0.1/tests/test_gateway_backoff.py +67 -0
  81. disagreement-0.0.1/tests/test_help_command.py +57 -0
  82. disagreement-0.0.1/tests/test_http_reactions.py +57 -0
  83. disagreement-0.0.1/tests/test_hybrid_context.py +44 -0
  84. disagreement-0.0.1/tests/test_i18n.py +21 -0
  85. disagreement-0.0.1/tests/test_interaction.py +19 -0
  86. disagreement-0.0.1/tests/test_logging_config.py +30 -0
  87. disagreement-0.0.1/tests/test_modal_send.py +16 -0
  88. disagreement-0.0.1/tests/test_modals.py +32 -0
  89. disagreement-0.0.1/tests/test_oauth.py +114 -0
  90. disagreement-0.0.1/tests/test_permissions.py +34 -0
  91. disagreement-0.0.1/tests/test_presence_and_typing.py +39 -0
  92. disagreement-0.0.1/tests/test_presence_update.py +33 -0
  93. disagreement-0.0.1/tests/test_rate_limiter.py +30 -0
  94. disagreement-0.0.1/tests/test_reactions.py +38 -0
  95. disagreement-0.0.1/tests/test_sharding.py +52 -0
  96. disagreement-0.0.1/tests/test_slash_contexts.py +15 -0
  97. disagreement-0.0.1/tests/test_tasks_extension.py +24 -0
  98. disagreement-0.0.1/tests/test_typing_indicator.py +64 -0
  99. disagreement-0.0.1/tests/test_ui.py +65 -0
  100. disagreement-0.0.1/tests/test_view_layout.py +53 -0
  101. disagreement-0.0.1/tests/test_voice_client.py +75 -0
  102. disagreement-0.0.1/tests/test_wait_for.py +35 -0
  103. disagreement-0.0.1/tests/test_webhooks.py +44 -0
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2025, Slipstream
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this
7
+ list of conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+
13
+ 3. Neither the name of the copyright holder nor the names of its
14
+ contributors may be used to endorse or promote products derived from
15
+ this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,3 @@
1
+ graft docs
2
+ graft examples
3
+ include LICENSE
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.4
2
+ Name: disagreement
3
+ Version: 0.0.1
4
+ Summary: A Python library for the Discord API.
5
+ Author-email: Slipstream <me@slipstreamm.dev>
6
+ License: BSD 3-Clause
7
+ Project-URL: Homepage, https://github.com/Slipstreamm/disagreement
8
+ Project-URL: Issues, https://github.com/Slipstreamm/disagreement/issues
9
+ Keywords: discord,api,bot,async,aiohttp
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: BSD License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Topic :: Internet
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: aiohttp<4.0.0,>=3.9.0
25
+ Provides-Extra: test
26
+ Requires-Dist: pytest>=8.0.0; extra == "test"
27
+ Requires-Dist: pytest-asyncio>=1.0.0; extra == "test"
28
+ Requires-Dist: hypothesis>=6.89.0; extra == "test"
29
+ Provides-Extra: dev
30
+ Requires-Dist: dotenv>=0.0.5; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # Disagreement
34
+
35
+ A Python library for interacting with the Discord API, with a focus on bot development.
36
+
37
+ ## Features
38
+
39
+ - Asynchronous design using `aiohttp`
40
+ - Gateway and HTTP API clients
41
+ - Slash command framework
42
+ - Message component helpers
43
+ - Built-in caching layer
44
+ - Experimental voice support
45
+ - Helpful error handling utilities
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ python -m pip install -U pip
51
+ pip install disagreement
52
+ # or install from source for development
53
+ pip install -e .
54
+ ```
55
+
56
+ Requires Python 3.11 or newer.
57
+
58
+ ## Basic Usage
59
+
60
+ ```python
61
+ import asyncio
62
+ import os
63
+ import disagreement
64
+
65
+ # Ensure DISCORD_BOT_TOKEN is set in your environment
66
+ client = disagreement.Client(token=os.environ.get("DISCORD_BOT_TOKEN"))
67
+
68
+ @client.on_event('MESSAGE_CREATE')
69
+ async def on_message(message: disagreement.Message):
70
+ print(f"Received: {message.content} from {message.author.username}")
71
+ if message.content.lower() == '!ping':
72
+ await message.reply('Pong!')
73
+
74
+ async def main():
75
+ if not client.token:
76
+ print("Error: DISCORD_BOT_TOKEN environment variable not set.")
77
+ return
78
+ try:
79
+ async with client:
80
+ await asyncio.Future() # run until cancelled
81
+ except KeyboardInterrupt:
82
+ print("Bot shutting down...")
83
+ # Add any other specific exception handling from your library, e.g., disagreement.AuthenticationError
84
+
85
+ if __name__ == '__main__':
86
+ asyncio.run(main())
87
+ ```
88
+
89
+ ### Global Error Handling
90
+
91
+ To ensure unexpected errors don't crash your bot, you can enable the library's
92
+ global error handler:
93
+
94
+ ```python
95
+ import disagreement
96
+
97
+ disagreement.setup_global_error_handler()
98
+ ```
99
+
100
+ Call this early in your program to log unhandled exceptions instead of letting
101
+ them terminate the process.
102
+
103
+ ### Configuring Logging
104
+
105
+ Use :func:`disagreement.logging_config.setup_logging` to configure logging for
106
+ your bot. The helper accepts a logging level and an optional file path.
107
+
108
+ ```python
109
+ import logging
110
+ from disagreement.logging_config import setup_logging
111
+
112
+ setup_logging(logging.INFO)
113
+ # Or log to a file
114
+ setup_logging(logging.DEBUG, file="bot.log")
115
+ ```
116
+
117
+ ### Defining Subcommands with `AppCommandGroup`
118
+
119
+ ```python
120
+ from disagreement.ext.app_commands import AppCommandGroup
121
+
122
+ settings = AppCommandGroup("settings", "Manage settings")
123
+
124
+ @settings.command(name="show")
125
+ async def show(ctx):
126
+ """Displays a setting."""
127
+ ...
128
+
129
+ @settings.group("admin", description="Admin settings")
130
+ def admin_group():
131
+ pass
132
+
133
+ @admin_group.command(name="set")
134
+ async def set_setting(ctx, key: str, value: str):
135
+ ...
136
+ ## Fetching Guilds
137
+
138
+ Use `Client.fetch_guild` to retrieve a guild from the Discord API if it
139
+ isn't already cached. This is useful when working with guild IDs from
140
+ outside the gateway events.
141
+
142
+ ```python
143
+ guild = await client.fetch_guild("123456789012345678")
144
+ roles = await client.fetch_roles(guild.id)
145
+ ```
146
+
147
+ ## Sharding
148
+
149
+ To run your bot across multiple gateway shards, pass `shard_count` when creating
150
+ the client:
151
+
152
+ ```python
153
+ client = disagreement.Client(token=BOT_TOKEN, shard_count=2)
154
+ ```
155
+
156
+ See `examples/sharded_bot.py` for a full example.
157
+
158
+ ## Contributing
159
+
160
+ Contributions are welcome! Please open an issue or submit a pull request.
161
+
162
+ See the [docs](docs/) directory for detailed guides on components, slash commands, caching, and voice features.
163
+
@@ -0,0 +1,131 @@
1
+ # Disagreement
2
+
3
+ A Python library for interacting with the Discord API, with a focus on bot development.
4
+
5
+ ## Features
6
+
7
+ - Asynchronous design using `aiohttp`
8
+ - Gateway and HTTP API clients
9
+ - Slash command framework
10
+ - Message component helpers
11
+ - Built-in caching layer
12
+ - Experimental voice support
13
+ - Helpful error handling utilities
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ python -m pip install -U pip
19
+ pip install disagreement
20
+ # or install from source for development
21
+ pip install -e .
22
+ ```
23
+
24
+ Requires Python 3.11 or newer.
25
+
26
+ ## Basic Usage
27
+
28
+ ```python
29
+ import asyncio
30
+ import os
31
+ import disagreement
32
+
33
+ # Ensure DISCORD_BOT_TOKEN is set in your environment
34
+ client = disagreement.Client(token=os.environ.get("DISCORD_BOT_TOKEN"))
35
+
36
+ @client.on_event('MESSAGE_CREATE')
37
+ async def on_message(message: disagreement.Message):
38
+ print(f"Received: {message.content} from {message.author.username}")
39
+ if message.content.lower() == '!ping':
40
+ await message.reply('Pong!')
41
+
42
+ async def main():
43
+ if not client.token:
44
+ print("Error: DISCORD_BOT_TOKEN environment variable not set.")
45
+ return
46
+ try:
47
+ async with client:
48
+ await asyncio.Future() # run until cancelled
49
+ except KeyboardInterrupt:
50
+ print("Bot shutting down...")
51
+ # Add any other specific exception handling from your library, e.g., disagreement.AuthenticationError
52
+
53
+ if __name__ == '__main__':
54
+ asyncio.run(main())
55
+ ```
56
+
57
+ ### Global Error Handling
58
+
59
+ To ensure unexpected errors don't crash your bot, you can enable the library's
60
+ global error handler:
61
+
62
+ ```python
63
+ import disagreement
64
+
65
+ disagreement.setup_global_error_handler()
66
+ ```
67
+
68
+ Call this early in your program to log unhandled exceptions instead of letting
69
+ them terminate the process.
70
+
71
+ ### Configuring Logging
72
+
73
+ Use :func:`disagreement.logging_config.setup_logging` to configure logging for
74
+ your bot. The helper accepts a logging level and an optional file path.
75
+
76
+ ```python
77
+ import logging
78
+ from disagreement.logging_config import setup_logging
79
+
80
+ setup_logging(logging.INFO)
81
+ # Or log to a file
82
+ setup_logging(logging.DEBUG, file="bot.log")
83
+ ```
84
+
85
+ ### Defining Subcommands with `AppCommandGroup`
86
+
87
+ ```python
88
+ from disagreement.ext.app_commands import AppCommandGroup
89
+
90
+ settings = AppCommandGroup("settings", "Manage settings")
91
+
92
+ @settings.command(name="show")
93
+ async def show(ctx):
94
+ """Displays a setting."""
95
+ ...
96
+
97
+ @settings.group("admin", description="Admin settings")
98
+ def admin_group():
99
+ pass
100
+
101
+ @admin_group.command(name="set")
102
+ async def set_setting(ctx, key: str, value: str):
103
+ ...
104
+ ## Fetching Guilds
105
+
106
+ Use `Client.fetch_guild` to retrieve a guild from the Discord API if it
107
+ isn't already cached. This is useful when working with guild IDs from
108
+ outside the gateway events.
109
+
110
+ ```python
111
+ guild = await client.fetch_guild("123456789012345678")
112
+ roles = await client.fetch_roles(guild.id)
113
+ ```
114
+
115
+ ## Sharding
116
+
117
+ To run your bot across multiple gateway shards, pass `shard_count` when creating
118
+ the client:
119
+
120
+ ```python
121
+ client = disagreement.Client(token=BOT_TOKEN, shard_count=2)
122
+ ```
123
+
124
+ See `examples/sharded_bot.py` for a full example.
125
+
126
+ ## Contributing
127
+
128
+ Contributions are welcome! Please open an issue or submit a pull request.
129
+
130
+ See the [docs](docs/) directory for detailed guides on components, slash commands, caching, and voice features.
131
+
@@ -0,0 +1,36 @@
1
+ # disagreement/__init__.py
2
+
3
+ """
4
+ Disagreement
5
+ ~~~~~~~~~~~~
6
+
7
+ A Python library for interacting with the Discord API.
8
+
9
+ :copyright: (c) 2025 Slipstream
10
+ :license: BSD 3-Clause License, see LICENSE for more details.
11
+ """
12
+
13
+ __title__ = "disagreement"
14
+ __author__ = "Slipstream"
15
+ __license__ = "BSD 3-Clause License"
16
+ __copyright__ = "Copyright 2025 Slipstream"
17
+ __version__ = "0.0.1"
18
+
19
+ from .client import Client
20
+ from .models import Message, User
21
+ from .voice_client import VoiceClient
22
+ from .typing import Typing
23
+ from .errors import (
24
+ DisagreementException,
25
+ HTTPException,
26
+ GatewayException,
27
+ AuthenticationError,
28
+ )
29
+ from .enums import GatewayIntent, GatewayOpcode # Export enums
30
+ from .error_handler import setup_global_error_handler
31
+ from .hybrid_context import HybridContext
32
+ from .ext import tasks
33
+
34
+ # Set up logging if desired
35
+ # import logging
36
+ # logging.getLogger(__name__).addHandler(logging.NullHandler())
@@ -0,0 +1,55 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from typing import TYPE_CHECKING, Dict, Generic, Optional, TypeVar
5
+
6
+ if TYPE_CHECKING:
7
+ from .models import Channel, Guild
8
+
9
+ T = TypeVar("T")
10
+
11
+
12
+ class Cache(Generic[T]):
13
+ """Simple in-memory cache with optional TTL support."""
14
+
15
+ def __init__(self, ttl: Optional[float] = None) -> None:
16
+ self.ttl = ttl
17
+ self._data: Dict[str, tuple[T, Optional[float]]] = {}
18
+
19
+ def set(self, key: str, value: T) -> None:
20
+ expiry = time.monotonic() + self.ttl if self.ttl is not None else None
21
+ self._data[key] = (value, expiry)
22
+
23
+ def get(self, key: str) -> Optional[T]:
24
+ item = self._data.get(key)
25
+ if not item:
26
+ return None
27
+ value, expiry = item
28
+ if expiry is not None and expiry < time.monotonic():
29
+ self.invalidate(key)
30
+ return None
31
+ return value
32
+
33
+ def invalidate(self, key: str) -> None:
34
+ self._data.pop(key, None)
35
+
36
+ def clear(self) -> None:
37
+ self._data.clear()
38
+
39
+ def values(self) -> list[T]:
40
+ now = time.monotonic()
41
+ items = []
42
+ for key, (value, expiry) in list(self._data.items()):
43
+ if expiry is not None and expiry < now:
44
+ self.invalidate(key)
45
+ else:
46
+ items.append(value)
47
+ return items
48
+
49
+
50
+ class GuildCache(Cache["Guild"]):
51
+ """Cache specifically for :class:`Guild` objects."""
52
+
53
+
54
+ class ChannelCache(Cache["Channel"]):
55
+ """Cache specifically for :class:`Channel` objects."""