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

Every @bot.command is automatically a slash command — it registers with Nerimity's API and shows in the / menu. No separate handler needed.

@bot.command("ban", description="Ban a user")
async def ban(ctx):
    # works as !ban AND /ban
    ...

To keep a command prefix-only (not in the / menu):

@bot.command_private("debug")
async def debug(ctx):
    await ctx.reply("secret")

@bot.slash is just an alias for @bot.command.


Buttons

from nerimity_sdk import Button

@bot.command("confirm", description="Confirm an action")
async def confirm_cmd(ctx):
    await ctx.reply(
        "Are you sure?",
        buttons=[
            Button(id="yes_confirm", label="✅ Yes"),
            Button(id="no_confirm",  label="❌ No", alert=True),
        ]
    )

@bot.button("yes_{action}")
async def on_yes(bctx):
    await bctx.popup("Done!", f"Confirmed: {bctx.params['action']}")

@bot.button("no_{action}")
async def on_no(bctx):
    await bctx.popup("Cancelled", "Nothing was changed.")

Button IDs cannot contain colons. Use underscores as separators.


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://nerimitysdk.readthedocs.io/en/latest/

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-1.0.4.tar.gz (72.3 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-1.0.4-py3-none-any.whl (60.6 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for nerimity_sdk-1.0.4.tar.gz
Algorithm Hash digest
SHA256 e9c8f5193dce5637b072f59323ba44e84b6a0829505a4c20e3b0301ebcb889f9
MD5 0d65b5d398be6edda533dab65d671e72
BLAKE2b-256 e96237b408be429e171c070fa7286f47d50a62fe6b1ec04c12afa76f1e5b49b4

See more details on using hashes here.

File details

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

File metadata

  • Download URL: nerimity_sdk-1.0.4-py3-none-any.whl
  • Upload date:
  • Size: 60.6 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-1.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 16f3076b9aef0690728adf706dc882b5bacf875e774ab6653e2c140eba59a1b2
MD5 55718951aa2ba204043652e7d12bcdd7
BLAKE2b-256 c68f140e48fa2c43f6cdada291cd83dc521176a0f946db24e2243aa8412af81f

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