yapperpy
A Python package for the Yapper Bot API: a full bot framework with a typed REST client, sync and async, and no runtime dependencies.
pip install yapper-bot
The distribution is yapper-bot; the import name is yapperpy.
Requires Python 3.10 or newer.
A bot in ten lines
import yapperpy
bot = yapperpy.Bot() # token from YAPPER_BOT_TOKEN
@bot.command("ping", "Is the bot alive?")
def ping(msg):
msg.reply("pong")
bot.run()
The same bot with asyncio:
import asyncio
import yapperpy
bot = yapperpy.AsyncBot()
@bot.command("ping", "Is the bot alive?")
async def ping(msg):
await msg.reply("pong")
asyncio.run(bot.run())
A callback may be a plain function or a coroutine function: the async runtime awaits whatever it is gets, so one cog works in both.
What a bot can do
| Call | What it does |
|---|---|
@bot.command(name, description) |
Registers a command. People type /name or !name. |
@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. |
@bot.button("vote:") |
Handles clicks on buttons whose custom_id starts with vote:. A regular expression works too. |
msg.reply(text) |
Answers the message, quoting it. msg.say(text) posts in the same channel without a quote. |
msg.args, msg.argv |
The rest of the command line, and the same split on spaces. |
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. |
bot.add_cog(Cog) |
Groups commands, listeners and buttons in one class. |
bot.load_extensions("cogs") |
Loads every .py file in a folder. bot.unload_extension and bot.reload_extension work while the bot runs. |
bot.send(channel_id, text) |
Posts a message. |
client.voice.join(server_id, channel_id) |
Gets a short-lived LiveKit ticket for joining a voice channel. |
client.voice.leave(server_id) |
Takes the bot out of voice in a server. |
Cogs
A cog is a class with commands, listeners and buttons in it:
# cogs/fun.py
import yapperpy
class Fun(yapperpy.Cog):
@yapperpy.command("joke", "Tell a joke")
async def joke(self, msg):
await msg.reply("I know a UDP joke, but you might not get it")
@yapperpy.listener("member_join")
async def hello(self, ev):
print(ev.username, "joined")
def check(self, msg): # optional: return False to skip this cog
return True
# bot.py
import yapperpy
bot = yapperpy.AsyncBot()
bot.load_extensions("cogs")
asyncio.run(bot.run())
The API client
The framework is built on a plain API client you can use on its own.
import yapperpy
with yapperpy.Yapper("yb_...") as client:
print(client.bot.get().username)
for server in client.servers.list():
print(server.name, server.member_count)
for channel in client.channels.list(type="text"):
print(channel.name)
message = client.messages.send(channel.id, "hello", embeds=[yapperpy.Embed().title("Hi")])
client.messages.react(channel.id, message.id, "👍")
Every call returns a typed model: BotProfile, User, Server, Channel, Category, Message,
Member, MemberDetail, Role, Ban, Reaction, Command, Event, VoiceChannel, Image,
VoiceTicket, Embed, ActionRow, Button, Ok.
Namespaces: client.bot, client.events, client.servers, client.channels, client.messages,
client.interactions, client.voice, client.users.
Anything the API does that this package does not cover yet is one call away:
client.api("GET", "/servers") # returns plain JSON
await async_client.api("GET", "/servers")
Events
The bot long-polls GET /events and hands each event to the right listeners. The payload of a
message event is a MessageContext, which has every field of the message plus reply, say and
author_mention. Other events give you a typed payload: MemberJoinPayload, MemberLeavePayload,
ReactionPayload, InteractionPayload, VoicePayload, TestPayload.
When the API routes a slash command to another bot, command_for prevents this bot from running
it. On first start, the bot reads the current event cursor before listening, so it does not replay
old events. Set bot.last_event_id before run() to resume from a previously saved cursor.
The current cursor is exposed as bot.last_event_id; persist and restore it to resume after a
restart. Events are kept by Yapper for one day.
Buttons and embeds
import yapperpy
@bot.command("vote", "Pizza or not")
def vote(msg):
msg.reply(
"Pizza tonight?",
components=[
yapperpy.row(
yapperpy.btn("Yes", "vote:yes", "success", emoji="🍕"),
yapperpy.btn("No", "vote:no", "danger"),
yapperpy.link_button("Menu", "https://example.com/menu"),
)
],
)
@bot.button("vote:")
def on_vote(interaction):
interaction.ephemeral("You voted " + interaction.custom_id[5:])
A Components V2 layout, where the components are the whole message:
import yapperpy
bot.send_layout(
channel_id,
[
yapperpy.container(
"## Game night",
yapperpy.section("**Minecraft**", accessory=yapperpy.btn("Vote", "poll:mc", "primary")),
yapperpy.separator(),
yapperpy.gallery("https://example.com/a.png"),
accent="blue",
)
],
)
Errors
YapperError
YapperValidationError 400
YapperAuthError 401
YapperPermissionError 403
YapperNotFoundError 404
YapperConflictError 409
YapperRateLimitError 429 .retry_after
YapperServiceError 503 .retry_after
YapperServerError other 5xx
YapperConnectionError the network failed
YapperTimeoutError the request timed out
A 429 or a 503 with a Retry-After header is waited out and retried automatically. A 429 without
one is raised right away, because waiting does not help. A failing command goes to the error
listeners, or is printed with a traceback when there are none.
Configuration
| Setting | How |
|---|---|
| Token | YAPPER_BOT_TOKEN, or Yapper("yb_...") |
| Base URL | Yapper(base_url=...), for a test server |
| Prefixes | Bot(prefixes=("/", "!")) |
| Bot messages | Bot(ignore_bots=False) to answer other bots too |
| Retries | Yapper(retry=RetryPolicy(attempts=5)) |
Development
python -m venv .venv
.venv\Scripts\python.exe -m pip install --editable ".[dev]"
.venv\Scripts\python.exe -m pytest
.venv\Scripts\python.exe -m mypy
.venv\Scripts\python.exe -m ruff check .
start.bat sets up the environment and runs examples/echo_bot.py.
Publishing
publish.bat builds the wheel and the source archive, checks the metadata and uploads them.
publish.bat :: upload to PyPI
publish.bat test :: upload to TestPyPI instead
publish.bat --dry-run :: build and check everything, do not upload
The token comes from a file called pypi.tkn in this folder. One line, the whole token:
pypi-AgEIcHlwaS5vcmMFAKEtoken1234567890abcdef
pypi.tkn is in .gitignore and is never uploaded. If you would rather not keep a file around,
set the PYPI_TOKEN environment variable instead.
The token needs the pypi scope for the project yapper-bot. The script refuses to upload a
version that is already on the index, so bump __version__ in src/yapperpy/_version.py first.
Links
- API reference: https://www.yapper.website/developers/api
- OpenAPI file: https://www.yapper.website/openapi.json
- Developer guide: https://www.yapper.website/developers
License
MIT
Metadata
Release files for yapper-bot 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| yapper_bot-0.2.0.tar.gz | 50.0 kB | Without provenance | Details for yapper_bot-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| yapper_bot-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for yapper_bot-0.2.0-py3-none-any.whl |
Total release size: 95.5 kB
Release files / yapper_bot-0.2.0.tar.gz
| Download URL | yapper_bot-0.2.0.tar.gz |
|---|---|
| Size | 50.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
88e7cee15db33c6d5f54c336ef3276e57583a89f0f1d24d7718dd5fe7e115a6a
|
|
BLAKE2b-256 checksum How to use checksums |
3d714bbc866ff56737d24814f0fa84b0d7e173c57cadb26c3014d4eba6f4fff1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / yapper_bot-0.2.0-py3-none-any.whl
| Download URL | yapper_bot-0.2.0-py3-none-any.whl |
|---|---|
| Size | 45.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0539b030f1b8d6e9020c3e3871d1016f7afaa0db7a82b129a001ec733fcdc7da
|
|
BLAKE2b-256 checksum How to use checksums |
96604af834bb775efb701a28931327c543053a91c0069b19d0e872532ace56ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Security for yapper-bot 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.