Files
derp/docs/USAGE.md
user 77f9a364e6 feat: add hot-reload, shorthand commands, and plugin help
- Plugin registry: add unload_plugin(), reload_plugin(), path tracking
- Bot: add load_plugin(), reload_plugin(), unload_plugin() public API
- Core plugin: add !load, !reload, !unload, !plugins commands
- Command dispatch: support unambiguous prefix matching (!h -> !help)
- Help: support !help <plugin> to show plugin description and commands
- Tests: 17 new tests covering hot-reload, prefix matching

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 01:15:59 +01:00

4.3 KiB

Usage Guide

Running

# From project directory
derp

# With options
derp --config /path/to/derp.toml --verbose

CLI Flags

Flag Description
-c, --config PATH Config file path
-v, --verbose Debug logging
-V, --version Print version
-h, --help Show help

Configuration

All settings in config/derp.toml:

[server]
host = "irc.libera.chat"    # IRC server hostname
port = 6697                   # Port (6697 = TLS, 6667 = plain)
tls = true                    # Enable TLS encryption
nick = "derp"                 # Bot nickname
user = "derp"                 # Username (ident)
realname = "derp IRC bot"     # Real name field
password = ""                 # Server password (optional)

[bot]
prefix = "!"                  # Command prefix character
channels = ["#test"]          # Channels to join on connect
plugins_dir = "plugins"       # Plugin directory path

[logging]
level = "info"                # Logging level: debug, info, warning, error

Built-in Commands

Command Description
!ping Bot responds with "pong"
!help List all available commands
!help <cmd> Show help for a specific command
!help <plugin> Show plugin description and its commands
!version Show bot version
!echo <text> Echo back text (example plugin)
!cert <domain> [...] Lookup CT logs for up to 5 domains
!load <plugin> Hot-load a plugin from the plugins directory
!reload <plugin> Reload a plugin, picking up file changes
!unload <plugin> Unload a plugin, removing its handlers
!plugins List loaded plugins with handler counts

Command Shorthand

Commands can be abbreviated to any unambiguous prefix:

!h           -> !help (unique match)
!pi          -> !ping (unique match)
!p           -> error: ambiguous (ping, plugins)

Exact matches always take priority over prefix matches.

!cert -- Certificate Transparency Lookup

Query crt.sh CT logs to enumerate SSL certificates for domains. Reports totals (expired/valid) and flags domains still serving expired certs.

!cert example.com
!cert example.com badsite.com another.org

Output format:

example.com -- 127 certs (23 expired, 104 valid)
badsite.com -- 45 certs (8 expired, 37 valid) | live cert EXPIRED
broken.test -- error: timeout
  • Max 5 domains per invocation
  • crt.sh can be slow; the bot confirms receipt before querying
  • Live cert check runs only when expired CT entries exist

Plugin Management

Plugins can be loaded, unloaded, and reloaded at runtime without restarting the bot.

!load crtsh          # Hot-load a new plugin from plugins/
!reload crtsh        # Reload a changed plugin
!unload crtsh        # Remove a plugin and all its handlers
!plugins             # List loaded plugins with handler counts

The core plugin cannot be unloaded (prevents losing !load/!reload), but it can be reloaded.

Writing Plugins

Create a .py file in the plugins/ directory:

from derp.plugin import command, event

@command("hello", help="Greet the user")
async def cmd_hello(bot, message):
    """Handler receives bot instance and parsed Message."""
    await bot.reply(message, f"Hello, {message.nick}!")

@event("JOIN")
async def on_join(bot, message):
    """Event handlers fire on IRC events (JOIN, PART, QUIT, etc.)."""
    if message.nick != bot.nick:
        await bot.send(message.target, f"Welcome, {message.nick}")

Plugin API

The bot object provides:

Method Description
bot.send(target, text) Send message to channel or nick
bot.reply(msg, text) Reply to source (channel or PM)
bot.action(target, text) Send /me action
bot.join(channel) Join a channel
bot.part(channel [, reason]) Leave a channel
bot.quit([reason]) Disconnect from server

The message object provides:

Attribute Description
message.nick Sender's nickname
message.prefix Full nick!user@host prefix
message.command IRC command (PRIVMSG, JOIN, etc.)
message.target First param (channel or nick)
message.text Trailing text content
message.is_channel Whether target is a channel
message.params All message parameters