Skip to main content

A fully-featured Python SDK for building Nerimity bots

Project description

nerimity-sdk

A Python library for building bots on Nerimity.

pip install nerimity-sdk

How it works

You create a Bot, attach handlers to it with decorators, then call bot.run(). The bot connects to Nerimity over a WebSocket, receives events, and dispatches them to your handlers.

Nerimity server
      │
      │  WebSocket (Socket.IO)
      ▼
  GatewayClient          ← receives raw events, puts them in a queue
      │
      ▼
  EventEmitter           ← deserializes events into typed objects, calls your handlers
      │
      ├── @bot.on("message:created")   ← your event listeners
      ├── @bot.command("ping")         ← prefix commands  (!ping)
      ├── @bot.slash("info")           ← slash commands   (/info)
      └── @bot.button("confirm:*")     ← button clicks

Everything your handlers need is in the ctx (Context) object passed to them.


Quickstart

1. Get a bot token

Go to nerimity.com/app/settings/developer/applications, create an application, add a Bot, and copy the token.

2. Scaffold a project

nerimity create my-bot
cd my-bot
cp .env.example .env   # paste your token in here
python bot.py

3. Or start from scratch

# bot.py
import os
from dotenv import load_dotenv
from nerimity_sdk import Bot

load_dotenv()
bot = Bot(token=os.environ["NERIMITY_TOKEN"], prefix="!")

@bot.on("ready")
async def on_ready(me):
    print(f"Logged in as {me.username}#{me.tag}")

@bot.command("ping", description="Replies with Pong!")
async def ping(ctx):
    await ctx.reply("Pong! 🏓")

bot.run()

Core concepts

The Bot object

bot = Bot(
    token=os.environ["NERIMITY_TOKEN"],
    prefix="!",          # prefix for commands like !ping
)

bot.run() is blocking — it connects and stays connected until you Ctrl+C.


Event listeners

Listen to anything happening on Nerimity:

@bot.on("message:created")
async def on_message(event):
    # event is a typed MessageCreatedEvent — not a raw dict
    print(event.message.content)

@bot.on("server:member_joined")
async def on_join(event):
    print(f"{event.member.user.username} joined {event.server_id}")

@bot.on("*")  # wildcard — fires for every event
async def log_everything(event):
    print(event)

Every event is a typed dataclass — you get autocomplete, not event["data"]["user"]["id"] chains.


Prefix commands

Commands triggered by a message starting with your prefix (e.g. !ping):

@bot.command("ping", description="Replies with Pong!")
async def ping(ctx):
    await ctx.reply("Pong!")

With argument convertersctx.args are already the right types:

from nerimity_sdk import Int, MemberConverter

@bot.command("add", description="Add two numbers", args=[Int, Int])
async def add(ctx):
    a, b = ctx.args   # guaranteed ints, friendly error if user types garbage
    await ctx.reply(f"{a} + {b} = {a + b}")

@bot.command("kick", args=[MemberConverter], guild_only=True)
async def kick(ctx):
    member = ctx.args[0]   # a Member object, not a raw string
    if not await ctx.confirm(f"Kick {member.user.username}?"):
        return await ctx.reply("Cancelled.")
    await ctx.rest.kick_member(ctx.server_id, member.user.id)
    await ctx.reply("Done.")

The Context object (ctx)

Every command handler receives a ctx with everything you need:

ctx.author          # User who sent the message
ctx.server          # Server it was sent in (None in DMs)
ctx.channel_id      # Where to reply
ctx.args            # Parsed arguments
ctx.flags           # --flag=value flags
ctx.mentions        # [@:id] mentions resolved to User objects

await ctx.reply("hello")                    # send a message
await ctx.react("👍")                       # react to the message
await ctx.ask("What's your name?")          # wait for a follow-up
await ctx.confirm("Are you sure?")          # yes/no prompt → True/False/None
await ctx.author.send(bot.rest, "Hi!")      # send a DM

Slash commands

Registered with Nerimity's API and shown in the / menu:

@bot.slash("info", description="Show bot info")
async def info(sctx):
    await sctx.reply(f"Running nerimity-sdk")

@bot.slash("ban", description="Ban a user", args_hint="<user_id>")
async def ban(sctx):
    await sctx.defer()   # ack immediately if this will take a moment
    user_id = sctx.args[0] if sctx.args else None
    if not user_id:
        return await sctx.reply("Usage: /ban <user_id>")
    await sctx.rest.ban_member(sctx.server_id, user_id)
    await sctx.reply(f"Banned.")

Slash commands are automatically synced to Nerimity when the bot starts.


Buttons

from nerimity_sdk import Button, ComponentRow

@bot.command("poll")
async def poll(ctx):
    msg = await ctx.reply("Vote!")

    # Register handlers for the buttons on this specific message
    @bot.button(f"vote:yes:{msg.id}", ttl=300)   # expires after 5 min
    async def on_yes(bctx):
        await bctx.reply(f"{bctx.user.username} voted Yes!")

    @bot.button(f"vote:no:{msg.id}", ttl=300)
    async def on_no(bctx):
        await bctx.reply(f"{bctx.user.username} voted No!")

Use {name} segments to capture dynamic parts:

@bot.button("confirm:{action}:{target}")
async def on_confirm(bctx):
    action = bctx.params["action"]   # e.g. "delete"
    target = bctx.params["target"]   # e.g. "msg_123"
    await bctx.reply(f"Confirmed: {action} on {target}")

Error handling

Without error handlers, errors are logged but don't crash the bot. With them, you can send a friendly message to the user:

@bot.on_command_error
async def on_error(ctx, error):
    await ctx.reply(f"❌ {error}")

@bot.on_slash_error
async def on_slash_error(sctx, error):
    await sctx.reply(f"❌ {error}")

@bot.on_button_error
async def on_button_error(bctx, error):
    await bctx.reply(f"❌ {error}")

Plugins

Split your bot into reloadable modules:

# plugins/welcome.py
from nerimity_sdk import PluginBase, listener, mention

class WelcomePlugin(PluginBase):
    name = "welcome"

    @listener("server:member_joined")
    async def on_join(self, event):
        await self.bot.rest.create_message(
            "YOUR_CHANNEL_ID",
            f"👋 Welcome {mention(event.member.user.id)}!"
        )

async def setup(bot):
    await bot.plugins.load(WelcomePlugin())

Load it:

await bot.plugins.load_from_path("plugins/welcome.py")

# Hot-reload without restarting:
await bot.plugins.reload("welcome")

Persistent storage

Save data between restarts:

from nerimity_sdk import JsonStore

bot = Bot(token=..., store=JsonStore("data.json"))

# In any command:
await bot.store.set(f"guild:{ctx.server_id}:prefix", "?")
prefix = await bot.store.get(f"guild:{ctx.server_id}:prefix") or "!"

Swap JsonStore for SqliteStore or RedisStore with no other code changes.


Scheduled tasks

@bot.cron("0 9 * * 1")   # every Monday at 09:00 UTC
async def weekly():
    await bot.rest.create_message("CHANNEL_ID", "Good morning!")

Requires pip install "nerimity-sdk[cron]".


Waiting for events

# Wait for a specific member to join
event = await bot.wait_for(
    "server:member_joined",
    check=lambda e: e.member.user.id == "12345",
    timeout=60,
)

Paginator

For long responses like help menus or leaderboards:

from nerimity_sdk import Paginator

@bot.command("help")
async def help_cmd(ctx):
    pages = bot.router.help_text().split("\n\n")
    await Paginator(pages).send(ctx)

Mention helpers

from nerimity_sdk import mention

mention("123456")          # → "[@:123456]"  (use in messages to ping someone)
ctx.mentions               # list of User objects mentioned in the command message

CLI tools

nerimity create my-bot    # scaffold a new project
nerimity version          # show SDK version
nerimity lint             # check your bot code for common mistakes
nerimity-help             # quick reference in your terminal

Optional extras

pip install "nerimity-sdk[cron]"     # @bot.cron() scheduled tasks
pip install "nerimity-sdk[watch]"    # watch=True auto-reload plugins on save
pip install "nerimity-sdk[sqlite]"   # SqliteStore
pip install "nerimity-sdk[redis]"    # RedisStore

Full docs

https://joddabodscripts.github.io/Nerimity-SDK/


Built by @Lyney:SHOW on Nerimity · JoddabodScripts on GitHub

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nerimity_sdk-0.2.2.tar.gz (51.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nerimity_sdk-0.2.2-py3-none-any.whl (48.0 kB view details)

Uploaded Python 3

File details

Details for the file nerimity_sdk-0.2.2.tar.gz.

File metadata

  • Download URL: nerimity_sdk-0.2.2.tar.gz
  • Upload date:
  • Size: 51.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for nerimity_sdk-0.2.2.tar.gz
Algorithm Hash digest
SHA256 daad999b109a9946cbe50a2e5841f189f639fa9cf4a84d429abede863652577e
MD5 3d2b6dc6d07d5972ac2082450ae35e28
BLAKE2b-256 63c085d8ad643e38c0bab520d93ec4e1dea1a3a90f3de13da9311c79ffaaf169

See more details on using hashes here.

File details

Details for the file nerimity_sdk-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: nerimity_sdk-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 48.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for nerimity_sdk-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 7e3944504a53c8eae8655347eaf55a4a9e664f30873ce2338c3fd7e77b9643fe
MD5 43cd43c22fd6b7e1eab32bb1469f309c
BLAKE2b-256 e03badd7cafb92e206566ff633b0a5ac51756c2e6254d8e3443eed435825c069

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page