drekord 0.1.0__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.
@@ -0,0 +1,36 @@
1
+ # Bytecompiled / optimized
2
+ *__pycache__*
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ *.egg
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # IDE
18
+ .vscode/
19
+ .idea/
20
+ *.swp
21
+ *.swo
22
+
23
+ # Testing / coverage
24
+ .pytest_cache/
25
+ htmlcov/
26
+ .coverage
27
+ .coverage.*
28
+
29
+ # mypy / ruff
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+
33
+ # OS
34
+ .DS_Store
35
+ Thumbs.db
36
+ build.bat
drekord-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 drek124
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
drekord-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,287 @@
1
+ Metadata-Version: 2.5
2
+ Name: drekord
3
+ Version: 0.1.0
4
+ Summary: A lightweight, async Discord REST API wrapper for Python
5
+ Project-URL: Homepage, https://github.com/drek124/drekord
6
+ Project-URL: Repository, https://github.com/drek124/drekord
7
+ Project-URL: Issues, https://github.com/drek124/drekord/issues
8
+ Author-email: drek124 <drek.dev124@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: api,async,discord,rest,wrapper
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Communications :: Chat
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: aiohttp>=3.9.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: aioresponses>=0.7; extra == 'dev'
27
+ Requires-Dist: mypy>=1.0; extra == 'dev'
28
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
29
+ Requires-Dist: pytest>=7.0; extra == 'dev'
30
+ Requires-Dist: ruff>=0.4; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # Drekord Discord API Wrapper
34
+
35
+ A lightweight, async Discord REST API wrapper for Python.
36
+
37
+ BTW, Drekord is **not** a bot framework (like `discord.py`). It is a pure REST API client designed to be integrated into any async application that needs to interact with the Discord API, without maintaining a persistent WebSocket connection.
38
+
39
+ ## Features
40
+ - **Pure REST:** No WebSocket gateway; just API calls
41
+ - **Fully async:** Built on `aiohttp` for clean `async/await` usage
42
+ - **Object-oriented:** Making it ez to use x)
43
+ - **Rate-limit aware:** Automatic retry on rate limits and transient errors
44
+
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install drekord
50
+ ```
51
+
52
+ Or from source:
53
+
54
+ ```bash
55
+ git clone https://github.com/drek124/drekord.git
56
+ cd drekord
57
+ pip install -e .
58
+ ```
59
+
60
+ ## Quick Start
61
+
62
+ ```python
63
+ import asyncio
64
+ import drekord
65
+
66
+
67
+ async def main():
68
+ async with drekord.Client(token="YOUR_BOT_TOKEN") as client:
69
+ # Get the current bot user
70
+ me = await client.users.me()
71
+ print(f"Logged in as {me.username}")
72
+
73
+ # Send a message to a channel
74
+ msg = await client.messages.channel(CHANNEL_ID).send(
75
+ content="Hello from Drekord! 🎉"
76
+ )
77
+ print(f"Sent message {msg.id}")
78
+
79
+ # Read the last 10 messages
80
+ messages = await client.messages.channel(CHANNEL_ID).list(limit=10)
81
+ for m in messages:
82
+ print(f"{m.author.username}: {m.content}")
83
+
84
+
85
+ asyncio.run(main())
86
+ ```
87
+
88
+ ## API Overview
89
+
90
+ All API access is through resource classes on the `Client`:
91
+
92
+ ```python
93
+ client.users # UsersResource
94
+ client.guilds # GuildResource
95
+ client.messages # MessagesResource
96
+ client.channels # ChannelResource
97
+ client.webhooks # WebhooksResource
98
+ client.invites # InvitesResource
99
+ client.emojis # EmojiResource
100
+ ```
101
+
102
+ ### Users
103
+
104
+ ```python
105
+ # Current bot user
106
+ me = await client.users.me()
107
+
108
+ # Get any user
109
+ user = await client.users.get(user_id)
110
+
111
+ # Create a DM
112
+ dm_channel = await client.users.dm(user_id)
113
+
114
+ # Edit the bot's username/avatar
115
+ me = await client.users.edit({"username": "new_name"})
116
+ ```
117
+
118
+ ### Messages
119
+
120
+ ```python
121
+ # List messages in a channel
122
+ messages = await client.messages.channel(channel_id).list(limit=50)
123
+
124
+ # Send a message
125
+ msg = await client.messages.channel(channel_id).send(
126
+ content="Hello!",
127
+ embeds=[{"title": "My Embed", "description": "With an embed!"}],
128
+ )
129
+
130
+ # Edit a message
131
+ msg = await client.messages.channel(channel_id).edit(message_id, content="Edited!")
132
+
133
+ # Delete a message
134
+ await client.messages.channel(channel_id).delete(message_id)
135
+
136
+ # Pin / unpin
137
+ await client.messages.channel(channel_id).pin(message_id)
138
+ await client.messages.channel(channel_id).unpin(message_id)
139
+
140
+ # Bulk delete
141
+ await client.messages.bulk_delete(channel_id, [msg1_id, msg2_id, msg3_id])
142
+ ```
143
+
144
+ ### Guilds
145
+
146
+ ```python
147
+ # List bot's guilds
148
+ guilds = await client.guilds.list()
149
+
150
+ # Get a guild
151
+ guild = await client.guilds.get(guild_id)
152
+ print(f"{guild.name} has {guild.approximate_member_count} members")
153
+
154
+ # Edit guild settings
155
+ await client.guilds.edit(guild_id, {"name": "New Name"})
156
+
157
+ # Leave a guild
158
+ await client.guilds.leave(guild_id)
159
+ ```
160
+
161
+ ### Guild Sub-Resources
162
+
163
+ ```python
164
+ # Channels
165
+ channels = await client.guilds.channels(guild_id).list()
166
+ new_channel = await client.guilds.channels(guild_id).create({
167
+ "name": "new-channel",
168
+ "type": 0, # text channel
169
+ })
170
+
171
+ # Members
172
+ members = await client.guilds.members(guild_id).list(limit=100)
173
+ member = await client.guilds.members(guild_id).get(user_id)
174
+
175
+ # Roles
176
+ roles = await client.guilds.roles(guild_id).list()
177
+ new_role = await client.guilds.roles(guild_id).create({"name": "Moderator", "color": 0xFF0000})
178
+
179
+ # Emojis
180
+ emojis = await client.guilds.emojis(guild_id).list()
181
+ ```
182
+
183
+ ### Channels
184
+
185
+ ```python
186
+ channel = await client.channels.get(channel_id)
187
+
188
+ # Edit a channel
189
+ channel = await client.channels.edit(channel_id, {"name": "renamed"})
190
+
191
+ # Set permissions
192
+ await client.channels.set_permissions(channel_id, overwrite_id, allow="1024", deny="0")
193
+
194
+ # Trigger typing indicator
195
+ await client.channels.typing(channel_id)
196
+ ```
197
+
198
+ ### Webhooks
199
+
200
+ ```python
201
+ # List webhooks in a channel
202
+ webhooks = await client.webhooks.channel_webhooks(channel_id)
203
+
204
+ # Create a webhook
205
+ webhook = await client.webhooks.create(channel_id, name="My Webhook")
206
+
207
+ # Execute a webhook (send via webhook)
208
+ await client.webhooks.execute(
209
+ webhook.id,
210
+ token=webhook.token,
211
+ content="Posted via webhook!",
212
+ username="Drekord Bot",
213
+ )
214
+
215
+ # Delete a webhook
216
+ await client.webhooks.delete(webhook.id)
217
+ ```
218
+
219
+ ### Raw Requests (Escape Hatch)
220
+
221
+ For endpoints not yet covered:
222
+
223
+ ```python
224
+ data = await client.request("GET", "/gateway")
225
+ data = await client.request("POST", "/channels/123/threads", json={"name": "Thread"})
226
+ ```
227
+
228
+ ## Error Handling
229
+
230
+ Drekord raises typed exceptions for all error cases:
231
+
232
+ ```python
233
+ import drekord
234
+
235
+ try:
236
+ msg = await client.messages.channel(channel_id).send(content="Hello!")
237
+ except drekord.ForbiddenError:
238
+ print("I don't have permission to send messages here!")
239
+ except drekord.NotFoundError:
240
+ print("Channel not found!")
241
+ except drekord.RateLimitedError as e:
242
+ print(f"Rate limited! Retry after {e.retry_after}s")
243
+ except drekord.HTTPError as e:
244
+ print(f"HTTP error {e.status_code}: {e}")
245
+ ```
246
+
247
+ ### Exception Hierarchy
248
+
249
+ ```
250
+ DrekordError
251
+ └── HTTPError
252
+ ├── BadRequestError (400)
253
+ ├── UnauthorizedError (401)
254
+ ├── ForbiddenError (403)
255
+ ├── NotFoundError (404)
256
+ ├── RateLimitedError (429)
257
+ └── DiscordServerError (5xx)
258
+ ```
259
+
260
+ ## Models
261
+
262
+ All API responses are returned as model objects with typed properties:
263
+
264
+ ```python
265
+ user = await client.users.get(user_id)
266
+ print(user.id) # int
267
+ print(user.username) # str
268
+ print(user.global_name) # str | None
269
+ print(user.bot) # bool
270
+ print(user.avatar_url()) # str | None
271
+
272
+ guild = await client.guilds.get(guild_id)
273
+ print(guild.name) # str
274
+ print(guild.icon_url()) # str | None
275
+ print(guild.roles) # list[Role]
276
+ ```
277
+
278
+ Models also support dict-style access for unmapped fields:
279
+
280
+ ```python
281
+ user_data = user.raw # Get the raw dict
282
+ custom = user.get("custom_status") # Access any field
283
+ ```
284
+
285
+ ## License
286
+
287
+ MIT
@@ -0,0 +1,255 @@
1
+ # Drekord Discord API Wrapper
2
+
3
+ A lightweight, async Discord REST API wrapper for Python.
4
+
5
+ BTW, Drekord is **not** a bot framework (like `discord.py`). It is a pure REST API client designed to be integrated into any async application that needs to interact with the Discord API, without maintaining a persistent WebSocket connection.
6
+
7
+ ## Features
8
+ - **Pure REST:** No WebSocket gateway; just API calls
9
+ - **Fully async:** Built on `aiohttp` for clean `async/await` usage
10
+ - **Object-oriented:** Making it ez to use x)
11
+ - **Rate-limit aware:** Automatic retry on rate limits and transient errors
12
+
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pip install drekord
18
+ ```
19
+
20
+ Or from source:
21
+
22
+ ```bash
23
+ git clone https://github.com/drek124/drekord.git
24
+ cd drekord
25
+ pip install -e .
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ```python
31
+ import asyncio
32
+ import drekord
33
+
34
+
35
+ async def main():
36
+ async with drekord.Client(token="YOUR_BOT_TOKEN") as client:
37
+ # Get the current bot user
38
+ me = await client.users.me()
39
+ print(f"Logged in as {me.username}")
40
+
41
+ # Send a message to a channel
42
+ msg = await client.messages.channel(CHANNEL_ID).send(
43
+ content="Hello from Drekord! 🎉"
44
+ )
45
+ print(f"Sent message {msg.id}")
46
+
47
+ # Read the last 10 messages
48
+ messages = await client.messages.channel(CHANNEL_ID).list(limit=10)
49
+ for m in messages:
50
+ print(f"{m.author.username}: {m.content}")
51
+
52
+
53
+ asyncio.run(main())
54
+ ```
55
+
56
+ ## API Overview
57
+
58
+ All API access is through resource classes on the `Client`:
59
+
60
+ ```python
61
+ client.users # UsersResource
62
+ client.guilds # GuildResource
63
+ client.messages # MessagesResource
64
+ client.channels # ChannelResource
65
+ client.webhooks # WebhooksResource
66
+ client.invites # InvitesResource
67
+ client.emojis # EmojiResource
68
+ ```
69
+
70
+ ### Users
71
+
72
+ ```python
73
+ # Current bot user
74
+ me = await client.users.me()
75
+
76
+ # Get any user
77
+ user = await client.users.get(user_id)
78
+
79
+ # Create a DM
80
+ dm_channel = await client.users.dm(user_id)
81
+
82
+ # Edit the bot's username/avatar
83
+ me = await client.users.edit({"username": "new_name"})
84
+ ```
85
+
86
+ ### Messages
87
+
88
+ ```python
89
+ # List messages in a channel
90
+ messages = await client.messages.channel(channel_id).list(limit=50)
91
+
92
+ # Send a message
93
+ msg = await client.messages.channel(channel_id).send(
94
+ content="Hello!",
95
+ embeds=[{"title": "My Embed", "description": "With an embed!"}],
96
+ )
97
+
98
+ # Edit a message
99
+ msg = await client.messages.channel(channel_id).edit(message_id, content="Edited!")
100
+
101
+ # Delete a message
102
+ await client.messages.channel(channel_id).delete(message_id)
103
+
104
+ # Pin / unpin
105
+ await client.messages.channel(channel_id).pin(message_id)
106
+ await client.messages.channel(channel_id).unpin(message_id)
107
+
108
+ # Bulk delete
109
+ await client.messages.bulk_delete(channel_id, [msg1_id, msg2_id, msg3_id])
110
+ ```
111
+
112
+ ### Guilds
113
+
114
+ ```python
115
+ # List bot's guilds
116
+ guilds = await client.guilds.list()
117
+
118
+ # Get a guild
119
+ guild = await client.guilds.get(guild_id)
120
+ print(f"{guild.name} has {guild.approximate_member_count} members")
121
+
122
+ # Edit guild settings
123
+ await client.guilds.edit(guild_id, {"name": "New Name"})
124
+
125
+ # Leave a guild
126
+ await client.guilds.leave(guild_id)
127
+ ```
128
+
129
+ ### Guild Sub-Resources
130
+
131
+ ```python
132
+ # Channels
133
+ channels = await client.guilds.channels(guild_id).list()
134
+ new_channel = await client.guilds.channels(guild_id).create({
135
+ "name": "new-channel",
136
+ "type": 0, # text channel
137
+ })
138
+
139
+ # Members
140
+ members = await client.guilds.members(guild_id).list(limit=100)
141
+ member = await client.guilds.members(guild_id).get(user_id)
142
+
143
+ # Roles
144
+ roles = await client.guilds.roles(guild_id).list()
145
+ new_role = await client.guilds.roles(guild_id).create({"name": "Moderator", "color": 0xFF0000})
146
+
147
+ # Emojis
148
+ emojis = await client.guilds.emojis(guild_id).list()
149
+ ```
150
+
151
+ ### Channels
152
+
153
+ ```python
154
+ channel = await client.channels.get(channel_id)
155
+
156
+ # Edit a channel
157
+ channel = await client.channels.edit(channel_id, {"name": "renamed"})
158
+
159
+ # Set permissions
160
+ await client.channels.set_permissions(channel_id, overwrite_id, allow="1024", deny="0")
161
+
162
+ # Trigger typing indicator
163
+ await client.channels.typing(channel_id)
164
+ ```
165
+
166
+ ### Webhooks
167
+
168
+ ```python
169
+ # List webhooks in a channel
170
+ webhooks = await client.webhooks.channel_webhooks(channel_id)
171
+
172
+ # Create a webhook
173
+ webhook = await client.webhooks.create(channel_id, name="My Webhook")
174
+
175
+ # Execute a webhook (send via webhook)
176
+ await client.webhooks.execute(
177
+ webhook.id,
178
+ token=webhook.token,
179
+ content="Posted via webhook!",
180
+ username="Drekord Bot",
181
+ )
182
+
183
+ # Delete a webhook
184
+ await client.webhooks.delete(webhook.id)
185
+ ```
186
+
187
+ ### Raw Requests (Escape Hatch)
188
+
189
+ For endpoints not yet covered:
190
+
191
+ ```python
192
+ data = await client.request("GET", "/gateway")
193
+ data = await client.request("POST", "/channels/123/threads", json={"name": "Thread"})
194
+ ```
195
+
196
+ ## Error Handling
197
+
198
+ Drekord raises typed exceptions for all error cases:
199
+
200
+ ```python
201
+ import drekord
202
+
203
+ try:
204
+ msg = await client.messages.channel(channel_id).send(content="Hello!")
205
+ except drekord.ForbiddenError:
206
+ print("I don't have permission to send messages here!")
207
+ except drekord.NotFoundError:
208
+ print("Channel not found!")
209
+ except drekord.RateLimitedError as e:
210
+ print(f"Rate limited! Retry after {e.retry_after}s")
211
+ except drekord.HTTPError as e:
212
+ print(f"HTTP error {e.status_code}: {e}")
213
+ ```
214
+
215
+ ### Exception Hierarchy
216
+
217
+ ```
218
+ DrekordError
219
+ └── HTTPError
220
+ ├── BadRequestError (400)
221
+ ├── UnauthorizedError (401)
222
+ ├── ForbiddenError (403)
223
+ ├── NotFoundError (404)
224
+ ├── RateLimitedError (429)
225
+ └── DiscordServerError (5xx)
226
+ ```
227
+
228
+ ## Models
229
+
230
+ All API responses are returned as model objects with typed properties:
231
+
232
+ ```python
233
+ user = await client.users.get(user_id)
234
+ print(user.id) # int
235
+ print(user.username) # str
236
+ print(user.global_name) # str | None
237
+ print(user.bot) # bool
238
+ print(user.avatar_url()) # str | None
239
+
240
+ guild = await client.guilds.get(guild_id)
241
+ print(guild.name) # str
242
+ print(guild.icon_url()) # str | None
243
+ print(guild.roles) # list[Role]
244
+ ```
245
+
246
+ Models also support dict-style access for unmapped fields:
247
+
248
+ ```python
249
+ user_data = user.raw # Get the raw dict
250
+ custom = user.get("custom_status") # Access any field
251
+ ```
252
+
253
+ ## License
254
+
255
+ MIT
@@ -0,0 +1,95 @@
1
+ """
2
+ Drekord - A lightweight, async Discord REST API wrapper.
3
+
4
+ Drekord is not a bot framework. It is a pure API client designed to be
5
+ integrated into any async application that needs to interact with the
6
+ Discord REST API — read messages, send messages, manage channels, etc.
7
+
8
+ Usage::
9
+
10
+ import drekord
11
+
12
+ async with drekord.Client(token="your_token") as client:
13
+ # Basic message
14
+ await client.messages.channel(ch).send(content="Hello!")
15
+
16
+ # With an embed
17
+ embed = drekord.Embed(title="Hi", description="World")
18
+ await client.messages.channel(ch).send(embeds=[embed])
19
+
20
+ # With Components V2
21
+ view = drekord.ui.LayoutView()
22
+ view.add_item(drekord.ui.Container(
23
+ drekord.ui.TextDisplay("## Hello!"),
24
+ drekord.ui.Separator(),
25
+ accent_color="#5865F2",
26
+ ))
27
+ await client.messages.channel(ch).send(view=view)
28
+ """
29
+
30
+ __version__ = "0.1.0"
31
+ __author__ = "drek124"
32
+
33
+ from .client import Client
34
+ from .exceptions import (
35
+ DrekordError,
36
+ HTTPError,
37
+ RateLimitedError,
38
+ ForbiddenError,
39
+ NotFoundError,
40
+ BadRequestError,
41
+ UnauthorizedError,
42
+ DiscordServerError,
43
+ )
44
+ from .models import (
45
+ User,
46
+ Guild,
47
+ Channel,
48
+ Message,
49
+ Role,
50
+ Emoji,
51
+ Attachment,
52
+ Embed,
53
+ EmbedField,
54
+ EmbedFooter,
55
+ EmbedImage,
56
+ EmbedAuthor,
57
+ PermissionOverwrite,
58
+ GuildPreview,
59
+ VoiceRegion,
60
+ Integration,
61
+ AuditLogEntry,
62
+ Webhook,
63
+ Snowflake,
64
+ )
65
+
66
+ __all__ = [
67
+ "Client",
68
+ "DrekordError",
69
+ "HTTPError",
70
+ "RateLimitedError",
71
+ "ForbiddenError",
72
+ "NotFoundError",
73
+ "BadRequestError",
74
+ "UnauthorizedError",
75
+ "DiscordServerError",
76
+ "User",
77
+ "Guild",
78
+ "Channel",
79
+ "Message",
80
+ "Role",
81
+ "Emoji",
82
+ "Attachment",
83
+ "Embed",
84
+ "EmbedField",
85
+ "EmbedFooter",
86
+ "EmbedImage",
87
+ "EmbedAuthor",
88
+ "PermissionOverwrite",
89
+ "GuildPreview",
90
+ "VoiceRegion",
91
+ "Integration",
92
+ "AuditLogEntry",
93
+ "Webhook",
94
+ "Snowflake",
95
+ ]