yapper-bot 0.1.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.
- yapper_bot-0.1.0.dist-info/METADATA +280 -0
- yapper_bot-0.1.0.dist-info/RECORD +30 -0
- yapper_bot-0.1.0.dist-info/WHEEL +4 -0
- yapper_bot-0.1.0.dist-info/licenses/LICENSE +21 -0
- yapperpy/__init__.py +153 -0
- yapperpy/__main__.py +58 -0
- yapperpy/_core.py +58 -0
- yapperpy/_http/__init__.py +27 -0
- yapperpy/_http/aio.py +175 -0
- yapperpy/_http/base.py +97 -0
- yapperpy/_http/sync.py +83 -0
- yapperpy/_version.py +1 -0
- yapperpy/aio_bot.py +170 -0
- yapperpy/aio_client.py +423 -0
- yapperpy/bot.py +163 -0
- yapperpy/client.py +421 -0
- yapperpy/cogs.py +285 -0
- yapperpy/components.py +129 -0
- yapperpy/context.py +95 -0
- yapperpy/embeds.py +86 -0
- yapperpy/endpoints.py +730 -0
- yapperpy/enums.py +49 -0
- yapperpy/errors.py +79 -0
- yapperpy/event_models.py +257 -0
- yapperpy/events.py +80 -0
- yapperpy/framework.py +165 -0
- yapperpy/models.py +795 -0
- yapperpy/py.typed +0 -0
- yapperpy/registry.py +160 -0
- yapperpy/retry.py +96 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: yapper-bot
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python bot framework and typed client for the Yapper Bot API
|
|
5
|
+
Project-URL: Homepage, https://www.yapper.website/developers
|
|
6
|
+
Project-URL: Documentation, https://www.yapper.website/developers/api
|
|
7
|
+
Project-URL: Source, https://github.com/yapperpy/yapperpy
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: api,bot,chat,discord,sdk,yapper
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Communications :: Chat
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# yapperpy
|
|
30
|
+
|
|
31
|
+
A Python package for the [Yapper](https://www.yapper.website) Bot API: a full bot framework with a
|
|
32
|
+
typed REST client, sync and async, and **no runtime dependencies**.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install yapper-bot
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The distribution is `yapper-bot`; the import name is `yapperpy`.
|
|
39
|
+
|
|
40
|
+
Requires Python 3.10 or newer.
|
|
41
|
+
|
|
42
|
+
## A bot in ten lines
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import yapperpy
|
|
46
|
+
|
|
47
|
+
bot = yapperpy.Bot() # token from YAPPER_BOT_TOKEN
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@bot.command("ping", "Is the bot alive?")
|
|
51
|
+
def ping(msg):
|
|
52
|
+
msg.reply("pong")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
bot.run()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The same bot with asyncio:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
import asyncio
|
|
62
|
+
import yapperpy
|
|
63
|
+
|
|
64
|
+
bot = yapperpy.AsyncBot()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@bot.command("ping", "Is the bot alive?")
|
|
68
|
+
async def ping(msg):
|
|
69
|
+
await msg.reply("pong")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
asyncio.run(bot.run())
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
A callback may be a plain function or a coroutine function: the async runtime awaits whatever it is
|
|
76
|
+
gets, so one cog works in both.
|
|
77
|
+
|
|
78
|
+
## What a bot can do
|
|
79
|
+
|
|
80
|
+
| Call | What it does |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `@bot.command(name, description)` | Registers a command. People type `/name` or `!name`. |
|
|
83
|
+
| `@bot.on(event)` | Listens for an event: `message`, `command`, `member_join`, `member_leave`, `reaction_add`, `reaction_remove`, `voice_join`, `voice_leave`, `interaction`, `test`, `ready`, `error`. |
|
|
84
|
+
| `@bot.button("vote:")` | Handles clicks on buttons whose `custom_id` starts with `vote:`. A regular expression works too. |
|
|
85
|
+
| `msg.reply(text)` | Answers the message, quoting it. `msg.say(text)` posts in the same channel without a quote. |
|
|
86
|
+
| `msg.args`, `msg.argv` | The rest of the command line, and the same split on spaces. |
|
|
87
|
+
| `i.reply()`, `i.ephemeral()`, `i.update()`, `i.ack()` | Answers a button click: a new message, one only the clicker sees, an edit of the clicked message, or nothing visible. |
|
|
88
|
+
| `bot.add_cog(Cog)` | Groups commands, listeners and buttons in one class. |
|
|
89
|
+
| `bot.load_extensions("cogs")` | Loads every `.py` file in a folder. `bot.unload_extension` and `bot.reload_extension` work while the bot runs. |
|
|
90
|
+
| `bot.send(channel_id, text)` | Posts a message. |
|
|
91
|
+
|
|
92
|
+
## Cogs
|
|
93
|
+
|
|
94
|
+
A cog is a class with commands, listeners and buttons in it:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# cogs/fun.py
|
|
98
|
+
import yapperpy
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Fun(yapperpy.Cog):
|
|
102
|
+
@yapperpy.command("joke", "Tell a joke")
|
|
103
|
+
async def joke(self, msg):
|
|
104
|
+
await msg.reply("I know a UDP joke, but you might not get it")
|
|
105
|
+
|
|
106
|
+
@yapperpy.listener("member_join")
|
|
107
|
+
async def hello(self, ev):
|
|
108
|
+
print(ev.username, "joined")
|
|
109
|
+
|
|
110
|
+
def check(self, msg): # optional: return False to skip this cog
|
|
111
|
+
return True
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
# bot.py
|
|
116
|
+
import yapperpy
|
|
117
|
+
|
|
118
|
+
bot = yapperpy.AsyncBot()
|
|
119
|
+
bot.load_extensions("cogs")
|
|
120
|
+
asyncio.run(bot.run())
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## The API client
|
|
124
|
+
|
|
125
|
+
The framework is built on a plain API client you can use on its own.
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
import yapperpy
|
|
129
|
+
|
|
130
|
+
with yapperpy.Yapper("yb_...") as client:
|
|
131
|
+
print(client.bot.get().username)
|
|
132
|
+
for server in client.servers.list():
|
|
133
|
+
print(server.name, server.member_count)
|
|
134
|
+
for channel in client.channels.list(type="text"):
|
|
135
|
+
print(channel.name)
|
|
136
|
+
message = client.messages.send(channel.id, "hello", embeds=[yapperpy.Embed().title("Hi")])
|
|
137
|
+
client.messages.react(channel.id, message.id, "👍")
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Every call returns a typed model: `BotProfile`, `User`, `Server`, `Channel`, `Category`, `Message`,
|
|
141
|
+
`Member`, `MemberDetail`, `Role`, `Ban`, `Reaction`, `Command`, `Event`, `VoiceChannel`, `Image`,
|
|
142
|
+
`Embed`, `ActionRow`, `Button`, `Ok`.
|
|
143
|
+
|
|
144
|
+
Namespaces: `client.bot`, `client.events`, `client.servers`, `client.channels`, `client.messages`,
|
|
145
|
+
`client.interactions`, `client.voice`, `client.users`.
|
|
146
|
+
|
|
147
|
+
Anything the API does that this package does not cover yet is one call away:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
client.api("GET", "/servers") # returns plain JSON
|
|
151
|
+
await async_client.api("GET", "/servers")
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Events
|
|
155
|
+
|
|
156
|
+
The bot long-polls `GET /events` and hands each event to the right listeners. The payload of a
|
|
157
|
+
message event is a `MessageContext`, which has every field of the message plus `reply`, `say` and
|
|
158
|
+
`author_mention`. Other events give you a typed payload: `MemberJoinPayload`, `MemberLeavePayload`,
|
|
159
|
+
`ReactionPayload`, `InteractionPayload`, `VoicePayload`, `TestPayload`.
|
|
160
|
+
|
|
161
|
+
The last event id is kept in `bot.last_event_id`, so a bot that restarts can pick up where it left
|
|
162
|
+
off. Events are kept by Yapper for one day.
|
|
163
|
+
|
|
164
|
+
## Buttons and embeds
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
import yapperpy
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@bot.command("vote", "Pizza or not")
|
|
171
|
+
def vote(msg):
|
|
172
|
+
msg.reply(
|
|
173
|
+
"Pizza tonight?",
|
|
174
|
+
components=[
|
|
175
|
+
yapperpy.row(
|
|
176
|
+
yapperpy.btn("Yes", "vote:yes", "success", emoji="🍕"),
|
|
177
|
+
yapperpy.btn("No", "vote:no", "danger"),
|
|
178
|
+
yapperpy.link_button("Menu", "https://example.com/menu"),
|
|
179
|
+
)
|
|
180
|
+
],
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@bot.button("vote:")
|
|
185
|
+
def on_vote(interaction):
|
|
186
|
+
interaction.ephemeral("You voted " + interaction.custom_id[5:])
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
A Components V2 layout, where the components are the whole message:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
import yapperpy
|
|
193
|
+
|
|
194
|
+
bot.send_layout(
|
|
195
|
+
channel_id,
|
|
196
|
+
[
|
|
197
|
+
yapperpy.container(
|
|
198
|
+
"## Game night",
|
|
199
|
+
yapperpy.section("**Minecraft**", accessory=yapperpy.btn("Vote", "poll:mc", "primary")),
|
|
200
|
+
yapperpy.separator(),
|
|
201
|
+
yapperpy.gallery("https://example.com/a.png"),
|
|
202
|
+
accent="blue",
|
|
203
|
+
)
|
|
204
|
+
],
|
|
205
|
+
)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Errors
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
YapperError
|
|
212
|
+
YapperValidationError 400
|
|
213
|
+
YapperAuthError 401
|
|
214
|
+
YapperPermissionError 403
|
|
215
|
+
YapperNotFoundError 404
|
|
216
|
+
YapperConflictError 409
|
|
217
|
+
YapperRateLimitError 429 .retry_after
|
|
218
|
+
YapperServiceError 503 .retry_after
|
|
219
|
+
YapperServerError other 5xx
|
|
220
|
+
YapperConnectionError the network failed
|
|
221
|
+
YapperTimeoutError the request timed out
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
A 429 or a 503 with a `Retry-After` header is waited out and retried automatically. A 429 without
|
|
225
|
+
one is raised right away, because waiting does not help. A failing command goes to the `error`
|
|
226
|
+
listeners, or is printed with a traceback when there are none.
|
|
227
|
+
|
|
228
|
+
## Configuration
|
|
229
|
+
|
|
230
|
+
| Setting | How |
|
|
231
|
+
| --- | --- |
|
|
232
|
+
| Token | `YAPPER_BOT_TOKEN`, or `Yapper("yb_...")` |
|
|
233
|
+
| Base URL | `Yapper(base_url=...)`, for a test server |
|
|
234
|
+
| Prefixes | `Bot(prefixes=("/", "!"))` |
|
|
235
|
+
| Bot messages | `Bot(ignore_bots=False)` to answer other bots too |
|
|
236
|
+
| Retries | `Yapper(retry=RetryPolicy(attempts=5))` |
|
|
237
|
+
|
|
238
|
+
## Development
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
python -m venv .venv
|
|
242
|
+
.venv\Scripts\python.exe -m pip install --editable ".[dev]"
|
|
243
|
+
.venv\Scripts\python.exe -m pytest
|
|
244
|
+
.venv\Scripts\python.exe -m mypy
|
|
245
|
+
.venv\Scripts\python.exe -m ruff check .
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
`start.bat` sets up the environment and runs `examples/echo_bot.py`.
|
|
249
|
+
|
|
250
|
+
## Publishing
|
|
251
|
+
|
|
252
|
+
`publish.bat` builds the wheel and the source archive, checks the metadata and uploads them.
|
|
253
|
+
|
|
254
|
+
```bat
|
|
255
|
+
publish.bat :: upload to PyPI
|
|
256
|
+
publish.bat test :: upload to TestPyPI instead
|
|
257
|
+
publish.bat --dry-run :: build and check everything, do not upload
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
The token comes from a file called `pypi.tkn` in this folder. One line, the whole token:
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
pypi-AgEIcHlwaS5vcmMFAKEtoken1234567890abcdef
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
`pypi.tkn` is in `.gitignore` and is never uploaded. If you would rather not keep a file around,
|
|
267
|
+
set the `PYPI_TOKEN` environment variable instead.
|
|
268
|
+
|
|
269
|
+
The token needs the `pypi` scope for the project `yapper-bot`. The script refuses to upload a
|
|
270
|
+
version that is already on the index, so bump `__version__` in `src/yapperpy/_version.py` first.
|
|
271
|
+
|
|
272
|
+
## Links
|
|
273
|
+
|
|
274
|
+
- API reference: https://www.yapper.website/developers/api
|
|
275
|
+
- OpenAPI file: https://www.yapper.website/openapi.json
|
|
276
|
+
- Developer guide: https://www.yapper.website/developers
|
|
277
|
+
|
|
278
|
+
## License
|
|
279
|
+
|
|
280
|
+
MIT
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
yapperpy/__init__.py,sha256=ZcVzvvVt1tU39hM3oTtj2nrQxWy1tH_7aEjEm6M-BKk,2873
|
|
2
|
+
yapperpy/__main__.py,sha256=iqKmCOGMnFObr5EUI8DdsFqMPizuzyTeDWl-DtN5tzc,1585
|
|
3
|
+
yapperpy/_core.py,sha256=5fFD-bIq7iptmiUbqFKKqblGWmjELZNtapmPiU1NMbs,1736
|
|
4
|
+
yapperpy/_version.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
5
|
+
yapperpy/aio_bot.py,sha256=jlBGbyBjc6jpT7QE-xMinqWpWfvspIVOUSZKaYLEpEY,6121
|
|
6
|
+
yapperpy/aio_client.py,sha256=nrv2Eir_M5yjqDXYdC9nPbqXyFAo2xCqVKV3iDnFJBQ,15417
|
|
7
|
+
yapperpy/bot.py,sha256=ZIMjhuBOvCAaeiQBNcwCWI-lyERFC0ccLYu-ezi9_6Y,5634
|
|
8
|
+
yapperpy/client.py,sha256=f17xBVro6rGhN7LQRjbOqAJOzUgePG_CGEf22geXGTQ,14618
|
|
9
|
+
yapperpy/cogs.py,sha256=ovvdPrnp5v-LzRGt1AIG-HmxclklvhB_GDeait1S9Cc,10085
|
|
10
|
+
yapperpy/components.py,sha256=S3VnGDQWpZhlSf4z__8kJe3jXMuLI8xnQbQlQZoMrYs,3663
|
|
11
|
+
yapperpy/context.py,sha256=U4xnPEm3A_EqKbUjmTinZmABAM9kft7jCL8u1Vf_AHc,3070
|
|
12
|
+
yapperpy/embeds.py,sha256=QMT_dSEUEzzJ6aMOmqCxcCEX3kF0VPexYPTqurqiJv4,2754
|
|
13
|
+
yapperpy/endpoints.py,sha256=Iodrcot-hXie1nDkBgzevdV4LjuFhtBzuVY2vl0hiBk,21260
|
|
14
|
+
yapperpy/enums.py,sha256=gvuyNP7HkhOsoPuZpp5Q_05NBAqwYfKP0w_zcvTkuAk,973
|
|
15
|
+
yapperpy/errors.py,sha256=wjgt-z3Cm7Jw8KsPl3q1_DsrbjF79fnbx7I8upHuCdA,1973
|
|
16
|
+
yapperpy/event_models.py,sha256=pBoTmCJJOi_2qMiRoNurXEOhSIbrXtnzPEGmTOe8Y1U,7916
|
|
17
|
+
yapperpy/events.py,sha256=kufxwGlGwRQthMdXwX3nfbZC5kB45wcaTdScAMJ19UU,2224
|
|
18
|
+
yapperpy/framework.py,sha256=IdWK6J1-AxXJhW4gSlCcoBcD6jbgu-C7SkCVyyyq3is,5837
|
|
19
|
+
yapperpy/models.py,sha256=qPkj6qG1ignzV74SIIFvMrQMfRhEkn_pOf-JMr-xiXA,23734
|
|
20
|
+
yapperpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
yapperpy/registry.py,sha256=eff1BUxaBbJzNaLnuMhapYhnzXLrQFolPFebV4vEfN0,5844
|
|
22
|
+
yapperpy/retry.py,sha256=UtWBcrjcLMqV9wVOItUOlKr74JKI8xBOKExjqz8KRnM,3170
|
|
23
|
+
yapperpy/_http/__init__.py,sha256=nK0DOlL3nDZ2AB4Efy9r1X8EEFXqyi-8Nb99r4_v7yQ,461
|
|
24
|
+
yapperpy/_http/aio.py,sha256=hQUYjjNS0t4zx37K58_DiYNszYzS64oyGLTQvUjDoc4,6845
|
|
25
|
+
yapperpy/_http/base.py,sha256=PtH2xDh2_jIIxY8lyiVSy_QsnY-oTvK2kep58kUnh4s,2546
|
|
26
|
+
yapperpy/_http/sync.py,sha256=7qUr5U41L0wo0JW7XnrV5OasRrud3r9PEnki0NRoFZM,2994
|
|
27
|
+
yapper_bot-0.1.0.dist-info/METADATA,sha256=vj1MclNpjkRE8UsYtwtnILdksDKUibo7AMcuDYVVDCg,8661
|
|
28
|
+
yapper_bot-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
29
|
+
yapper_bot-0.1.0.dist-info/licenses/LICENSE,sha256=D-u-pnCVEDkVELns6oG0Z9q2RNgUbg0jUJM9oZbDLDY,1078
|
|
30
|
+
yapper_bot-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yapperpy contributors
|
|
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.
|
yapperpy/__init__.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from ._version import __version__
|
|
2
|
+
from .aio_bot import AsyncBot
|
|
3
|
+
from .aio_client import AsyncYapper
|
|
4
|
+
from .bot import Bot
|
|
5
|
+
from .client import Yapper
|
|
6
|
+
from .cogs import Cog, button, command, listener
|
|
7
|
+
from .components import (
|
|
8
|
+
IS_COMPONENTS_V2,
|
|
9
|
+
btn,
|
|
10
|
+
container,
|
|
11
|
+
gallery,
|
|
12
|
+
layout,
|
|
13
|
+
link_button,
|
|
14
|
+
row,
|
|
15
|
+
section,
|
|
16
|
+
separator,
|
|
17
|
+
text,
|
|
18
|
+
thumbnail,
|
|
19
|
+
)
|
|
20
|
+
from .context import InteractionContext, MessageContext
|
|
21
|
+
from .embeds import Embed
|
|
22
|
+
from .enums import BotStatus, ButtonStyle, CallbackType, ChannelType, EventType
|
|
23
|
+
from .errors import (
|
|
24
|
+
YapperAuthError,
|
|
25
|
+
YapperConflictError,
|
|
26
|
+
YapperConnectionError,
|
|
27
|
+
YapperError,
|
|
28
|
+
YapperNotFoundError,
|
|
29
|
+
YapperPermissionError,
|
|
30
|
+
YapperRateLimitError,
|
|
31
|
+
YapperServerError,
|
|
32
|
+
YapperServiceError,
|
|
33
|
+
YapperTimeoutError,
|
|
34
|
+
YapperValidationError,
|
|
35
|
+
)
|
|
36
|
+
from .event_models import (
|
|
37
|
+
EventPayload,
|
|
38
|
+
InteractionPayload,
|
|
39
|
+
MemberJoinPayload,
|
|
40
|
+
MemberLeavePayload,
|
|
41
|
+
MessagePayload,
|
|
42
|
+
ReactionPayload,
|
|
43
|
+
TestPayload,
|
|
44
|
+
VoicePayload,
|
|
45
|
+
)
|
|
46
|
+
from .models import (
|
|
47
|
+
ActionRow,
|
|
48
|
+
Ban,
|
|
49
|
+
BotProfile,
|
|
50
|
+
BulkDeleteResult,
|
|
51
|
+
Button,
|
|
52
|
+
Category,
|
|
53
|
+
Channel,
|
|
54
|
+
ChannelRow,
|
|
55
|
+
Command,
|
|
56
|
+
Event,
|
|
57
|
+
EventPage,
|
|
58
|
+
Image,
|
|
59
|
+
Member,
|
|
60
|
+
MemberDetail,
|
|
61
|
+
Message,
|
|
62
|
+
Ok,
|
|
63
|
+
PinResult,
|
|
64
|
+
Reaction,
|
|
65
|
+
Role,
|
|
66
|
+
Server,
|
|
67
|
+
StatusResult,
|
|
68
|
+
TimeoutResult,
|
|
69
|
+
User,
|
|
70
|
+
VoiceChannel,
|
|
71
|
+
VoiceDisconnectResult,
|
|
72
|
+
VoiceMoveResult,
|
|
73
|
+
)
|
|
74
|
+
from .models import (
|
|
75
|
+
Embed as EmbedModel,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
__all__ = [
|
|
79
|
+
"IS_COMPONENTS_V2",
|
|
80
|
+
"ActionRow",
|
|
81
|
+
"AsyncBot",
|
|
82
|
+
"AsyncYapper",
|
|
83
|
+
"Ban",
|
|
84
|
+
"Bot",
|
|
85
|
+
"BotProfile",
|
|
86
|
+
"BotStatus",
|
|
87
|
+
"BulkDeleteResult",
|
|
88
|
+
"Button",
|
|
89
|
+
"ButtonStyle",
|
|
90
|
+
"CallbackType",
|
|
91
|
+
"Category",
|
|
92
|
+
"Channel",
|
|
93
|
+
"ChannelRow",
|
|
94
|
+
"ChannelType",
|
|
95
|
+
"Cog",
|
|
96
|
+
"Command",
|
|
97
|
+
"Embed",
|
|
98
|
+
"EmbedModel",
|
|
99
|
+
"Event",
|
|
100
|
+
"EventPage",
|
|
101
|
+
"EventPayload",
|
|
102
|
+
"EventType",
|
|
103
|
+
"Image",
|
|
104
|
+
"InteractionContext",
|
|
105
|
+
"InteractionPayload",
|
|
106
|
+
"Member",
|
|
107
|
+
"MemberDetail",
|
|
108
|
+
"MemberJoinPayload",
|
|
109
|
+
"MemberLeavePayload",
|
|
110
|
+
"Message",
|
|
111
|
+
"MessageContext",
|
|
112
|
+
"MessagePayload",
|
|
113
|
+
"Ok",
|
|
114
|
+
"PinResult",
|
|
115
|
+
"Reaction",
|
|
116
|
+
"ReactionPayload",
|
|
117
|
+
"Role",
|
|
118
|
+
"Server",
|
|
119
|
+
"StatusResult",
|
|
120
|
+
"TestPayload",
|
|
121
|
+
"TimeoutResult",
|
|
122
|
+
"User",
|
|
123
|
+
"VoiceChannel",
|
|
124
|
+
"VoiceDisconnectResult",
|
|
125
|
+
"VoiceMoveResult",
|
|
126
|
+
"VoicePayload",
|
|
127
|
+
"Yapper",
|
|
128
|
+
"YapperAuthError",
|
|
129
|
+
"YapperConflictError",
|
|
130
|
+
"YapperConnectionError",
|
|
131
|
+
"YapperError",
|
|
132
|
+
"YapperNotFoundError",
|
|
133
|
+
"YapperPermissionError",
|
|
134
|
+
"YapperRateLimitError",
|
|
135
|
+
"YapperServerError",
|
|
136
|
+
"YapperServiceError",
|
|
137
|
+
"YapperTimeoutError",
|
|
138
|
+
"YapperValidationError",
|
|
139
|
+
"__version__",
|
|
140
|
+
"btn",
|
|
141
|
+
"button",
|
|
142
|
+
"command",
|
|
143
|
+
"container",
|
|
144
|
+
"gallery",
|
|
145
|
+
"layout",
|
|
146
|
+
"link_button",
|
|
147
|
+
"listener",
|
|
148
|
+
"row",
|
|
149
|
+
"section",
|
|
150
|
+
"separator",
|
|
151
|
+
"text",
|
|
152
|
+
"thumbnail",
|
|
153
|
+
]
|
yapperpy/__main__.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from .aio_client import AsyncYapper
|
|
7
|
+
from .client import Yapper
|
|
8
|
+
from .errors import YapperError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def show(client: Yapper) -> None:
|
|
12
|
+
profile = client.bot.get()
|
|
13
|
+
print(f"bot: {profile.username} (id {profile.id})")
|
|
14
|
+
if profile.display_name:
|
|
15
|
+
print(f"name: {profile.display_name}")
|
|
16
|
+
if profile.bio:
|
|
17
|
+
print(f"bio: {profile.bio}")
|
|
18
|
+
print(f"servers: {len(client.servers.list())}")
|
|
19
|
+
for server in client.servers.list():
|
|
20
|
+
print(f" - {server.name} ({server.id})")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def show_async(client: AsyncYapper) -> None:
|
|
24
|
+
profile = await client.bot.get()
|
|
25
|
+
print(f"bot: {profile.username} (id {profile.id})")
|
|
26
|
+
if profile.display_name:
|
|
27
|
+
print(f"name: {profile.display_name}")
|
|
28
|
+
if profile.bio:
|
|
29
|
+
print(f"bio: {profile.bio}")
|
|
30
|
+
servers = await client.servers.list()
|
|
31
|
+
print(f"servers: {len(servers)}")
|
|
32
|
+
for server in servers:
|
|
33
|
+
print(f" - {server.name} ({server.id})")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def main(argv: list[str]) -> int:
|
|
37
|
+
try:
|
|
38
|
+
if "--async" in argv:
|
|
39
|
+
|
|
40
|
+
async def runner() -> None:
|
|
41
|
+
async with AsyncYapper() as client:
|
|
42
|
+
await show_async(client)
|
|
43
|
+
|
|
44
|
+
asyncio.run(runner())
|
|
45
|
+
return 0
|
|
46
|
+
with Yapper() as client:
|
|
47
|
+
show(client)
|
|
48
|
+
except ValueError as exc:
|
|
49
|
+
print(exc)
|
|
50
|
+
return 1
|
|
51
|
+
except YapperError as exc:
|
|
52
|
+
print(f"{type(exc).__name__}: {exc}")
|
|
53
|
+
return 1
|
|
54
|
+
return 0
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
raise SystemExit(main(sys.argv[1:]))
|
yapperpy/_core.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Generic, TypeVar
|
|
5
|
+
|
|
6
|
+
from ._http.base import USER_AGENT, RawRequest, RawResponse, build_url
|
|
7
|
+
from .endpoints import Request
|
|
8
|
+
from .errors import YapperError, error_for_status
|
|
9
|
+
from .retry import retry_after_from
|
|
10
|
+
|
|
11
|
+
C = TypeVar("C")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Resource(Generic[C]):
|
|
15
|
+
def __init__(self, client: C) -> None:
|
|
16
|
+
self._client = client
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def prepare(
|
|
20
|
+
request: Request[Any],
|
|
21
|
+
*,
|
|
22
|
+
base_url: str,
|
|
23
|
+
token: str,
|
|
24
|
+
user_agent: str = USER_AGENT,
|
|
25
|
+
) -> RawRequest:
|
|
26
|
+
headers = {
|
|
27
|
+
"Authorization": f"Bot {token}",
|
|
28
|
+
"User-Agent": user_agent,
|
|
29
|
+
"Accept": "application/json",
|
|
30
|
+
}
|
|
31
|
+
body: bytes | None = None
|
|
32
|
+
if request.raw_body is not None:
|
|
33
|
+
body = request.raw_body
|
|
34
|
+
headers["Content-Type"] = request.content_type or "application/octet-stream"
|
|
35
|
+
elif request.json_body is not None:
|
|
36
|
+
body = json.dumps(request.json_body, separators=(",", ":")).encode("utf-8")
|
|
37
|
+
headers["Content-Type"] = "application/json"
|
|
38
|
+
return RawRequest(
|
|
39
|
+
method=request.method,
|
|
40
|
+
url=build_url(base_url, request.path, request.query),
|
|
41
|
+
headers=headers,
|
|
42
|
+
body=body,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def error_from_response(raw: RawResponse) -> YapperError:
|
|
47
|
+
body: Any
|
|
48
|
+
try:
|
|
49
|
+
body = raw.json()
|
|
50
|
+
except ValueError:
|
|
51
|
+
body = raw.body[:200].decode("utf-8", "replace")
|
|
52
|
+
message = f"the request failed with status {raw.status}"
|
|
53
|
+
if isinstance(body, dict):
|
|
54
|
+
detail = body.get("error") or body.get("message")
|
|
55
|
+
if isinstance(detail, str) and detail:
|
|
56
|
+
message = detail
|
|
57
|
+
wait = retry_after_from(raw.headers, body)
|
|
58
|
+
return error_for_status(raw.status, message, body=body, retry_after=wait)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .base import (
|
|
2
|
+
DEFAULT_BASE_URL,
|
|
3
|
+
DEFAULT_TIMEOUT,
|
|
4
|
+
USER_AGENT,
|
|
5
|
+
AsyncTransport,
|
|
6
|
+
RawRequest,
|
|
7
|
+
RawResponse,
|
|
8
|
+
SyncTransport,
|
|
9
|
+
TransportError,
|
|
10
|
+
TransportTimeout,
|
|
11
|
+
build_url,
|
|
12
|
+
segment,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"DEFAULT_BASE_URL",
|
|
17
|
+
"DEFAULT_TIMEOUT",
|
|
18
|
+
"USER_AGENT",
|
|
19
|
+
"AsyncTransport",
|
|
20
|
+
"RawRequest",
|
|
21
|
+
"RawResponse",
|
|
22
|
+
"SyncTransport",
|
|
23
|
+
"TransportError",
|
|
24
|
+
"TransportTimeout",
|
|
25
|
+
"build_url",
|
|
26
|
+
"segment",
|
|
27
|
+
]
|