Skip to main content

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.

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, 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.

The last event id is kept in bot.last_event_id, so a bot that restarts can pick up where it left off. 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.

License

MIT

Metadata

Release files for yapper-bot 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for yapper-bot 0.1.0
File Size Uploaded Provenance Actions
yapper_bot-0.1.0.tar.gz 48.6 kB Without provenance Details for yapper_bot-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for yapper-bot 0.1.0
File Interpreter ABI Platform Actions
yapper_bot-0.1.0-py3-none-any.whl Python 3 none any Details for yapper_bot-0.1.0-py3-none-any.whl

Total release size: 93.5 kB

Release files / yapper_bot-0.1.0.tar.gz

Download URL yapper_bot-0.1.0.tar.gz
Size 48.6 kB
Tags Source
SHA-256 checksum
How to use checksums
04d821b3a43c6de70994ebc31919ad2388266693670971750264b7a42d2fc805
BLAKE2b-256 checksum
How to use checksums
21a53a24ad487858854b2de586da3daf069afa97c6c045b88b077dcdc2004a49
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.1.0-py3-none-any.whl

Download URL yapper_bot-0.1.0-py3-none-any.whl
Size 44.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e6536f0852852a6fa3f830857763a7a8955863cd03ce6fd4c58e76bfbc5ae4db
BLAKE2b-256 checksum
How to use checksums
6a3958ed69355390ab1b3d252ae063453fe19fd2e4f68c62e8472eb116b1e775
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.1.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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page