diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b1af417 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,86 @@ +# sp0rkle Agent Guide: Instructions for Developers and AI Agents + +Welcome to the sp0rkle development guide. This document is designed to help you navigate the codebase, understand its unique architecture, and follow the established coding conventions. + +## 1. Architecture Overview + +sp0rkle is a modular IRC bot with a driver-based architecture. + +- **`bot/`**: The core logic. Manages the IRC connection, command dispatching, rewriters, and pollers. +- **`db/`**: The database abstraction layer. Currently handles the dual-writing logic for the MongoDB to BoltDB migration. +- **`drivers/`**: Modular features (e.g., `factdriver`, `quotedriver`). Each driver is self-contained and registers its functionality with the core bot. +- **`collections/`**: Data access objects for specific features (e.g., `collections/karma`). These sit between the drivers and the `db/` package. +- **`util/`**: General-purpose utilities, including complex lexing (`util/datetime`, `util/lexer.go`). +- **`main.go`**: The entry point. It initializes databases and manually registers drivers. + +## 2. Coding Style & "sp0rkle-isms" + +This project uses Go 1.22 and follows some non-standard patterns that you must respect: + +- **The Global `bot` Singleton**: The `bot` package uses a global singleton to manage state. While not "modern Go," it is the established pattern here. Use `bot.Command`, `bot.Handle`, etc., to register functionality. +- **Manual Registration**: Drivers must be imported in `main.go` and their `Init()` function called explicitly. There is no auto-discovery. +- **Driver Globals**: It is common for drivers to maintain their own package-level state (e.g., collection handles, rate limit maps). +- **Concurrency**: IRC handlers are executed in concurrent goroutines. Shared state in drivers **must** be protected by `sync.Mutex` or `sync.RWMutex`. +- **Go 1.22 Conventions**: Use `interface{}` (not `any`), `math/rand` (seeded in `main.go`), and `io/ioutil` (though `os` is preferred where applicable). +- **Tabs for Indentation**: Use standard `gofmt` with tabs. + +## 3. Events and Handlers + +sp0rkle uses an event-based system built on top of `goirc`. + +- **Handler Signature**: `type HandlerFunc func(*bot.Context)` +- **`bot.Handle(fn, events...)`**: Registers a `HandlerFunc` for specific IRC events (e.g., `client.PRIVMSG`, `client.JOIN`). +- **`bot.HandleBG(fn, events...)`**: Same as `Handle`, but runs the handler in its own goroutine. Useful for long-running tasks like migrations. +- **`bot.Context`**: The primary object passed to handlers. It encapsulates the IRC line, the connection, and provides helper methods: + - `ctx.Text()`: Message body with bot name/command prefix stripped. + - `ctx.ReplyN(format, args...)`: Reply with "Nick: " prefix. + - `ctx.Storable()`: Returns sender nick and channel. + +## 4. Plugins + +The Factoid driver supports "plugins" which allow other drivers to perform transformations on factoid values. +- **Implementation**: A driver provides a `RegisterPlugins` method (if using the older pattern) or simply registers functions that the factoid driver calls. +- **Factoid Syntax**: Triggered via `` in a factoid value. +- **Identifier Replacement**: Common identifiers like `$nick`, `$chan`, `$date`, and `$time` are handled by a standard replacer in `factdriver/plugins.go`. + +## 5. The "Long Slog" Migration (Mongo to BoltDB) + +We are in the middle of a migration from MongoDB to BoltDB. + +- **`db.Both`**: Most collections use `db.Both`, which writes to both databases simultaneously and compares reads. +- **`db.K`**: Keys are constructed using a custom key builder: + ```go + key := db.K{db.S{"nick", "fluffle"}, db.I{"count", 42}} + ``` +- **BoltDB Structure**: Successive key elements create nested BoltDB buckets, with the final element as the key. + +## 6. Extending the Bot + +### Drivers +To add a new feature, create a new package in `drivers/`. It must have an `Init()` function: +```go +func Init() { + // Register a command: !myfeat + bot.Command(myHandler, "myfeat", "myfeat -- descriptive help") + + // Register a raw IRC handler + bot.Handle(rawHandler, client.PRIVMSG) +} +``` + +### Pollers +For periodic tasks, implement `bot.Poller` (`Start`, `Stop`, `Poll`, `Tick`) and register with `bot.Poll(myPoller)`. + +## 7. Testing + +- Tests live in `_test.go` files. +- Use `bot.Context` mocking for handler tests. +- Reference `collections/` tests for database interaction testing. + +## 8. Tips for AI Agents + +- **Trace to Source**: Many files in `util/` (like `datetime/y.go`) are generated from `.y` or `.rl` files. +- **Check `main.go`**: Manual registration is required for all drivers. +- **Logging**: Use `github.com/fluffle/golog/logging`. + +Happy Hacking! diff --git a/docs/drivers/decision.md b/docs/drivers/decision.md new file mode 100644 index 0000000..a107734 --- /dev/null +++ b/docs/drivers/decision.md @@ -0,0 +1,8 @@ +# Decision Driver + +Make choices based on random numbers. + +## Usage +- `!decide or or ...` - The bot will pick one for you. +- `!choose or or ...` - Same as `!decide`. +- `!rand ` - Pick a random number in the given range. diff --git a/docs/drivers/factoid.md b/docs/drivers/factoid.md new file mode 100644 index 0000000..f8ed9ce --- /dev/null +++ b/docs/drivers/factoid.md @@ -0,0 +1,28 @@ +# Factoid Driver + +Factoids allow the bot to remember and recall information. + +## Adding Factoids +There are two ways to teach the bot something (must address the bot by nick): +- `botNick: := ` - Stores `` for ``. +- `botNick: :is ` - Stores ` is ` for ``. + +## Retrieving Factoids +- Simply type the `` in the channel, and the bot will respond with a random value associated with that key. +- `botNick: literal ` - Prints all known values for a key. If there are many, the bot will ask you to do this in a private message. + +## Managing Factoids +These commands operate on the **last factoid triggered** in the channel: +- `botNick: forget that` or `delete that` - Deletes the last triggered value. +- `botNick: replace that with ` - Replaces the last value. +- `botNick: that =~ /regex/replacement/` - Edit the last factoid value using regex. +- `botNick: chance of that is %` - Sets the probability (0-100%) that the bot will respond to this key. +- `botNick: fact info` - Displays metadata about the last factoid (creator, time, etc.). +- `botNick: fact search ` - Search for factoid keys matching a regex. + +## Factoid Variables +You can use variables in factoid values: +- `$nick`: The nick of the person who triggered the factoid. +- `$chan`: The channel it was triggered in. +- `$date` / `$time`: Current date or time. +- `$user` / `$host`: Ident or host of the triggerer. diff --git a/docs/drivers/github.md b/docs/drivers/github.md new file mode 100644 index 0000000..ed5c899 --- /dev/null +++ b/docs/drivers/github.md @@ -0,0 +1,8 @@ +# GitHub Integration + +File and update issues directly from IRC. + +## Usage +- `!file bug: ` - Create a new issue. +- `!report bug <title>` - Same as `!file bug:`. +- `!update bug #<number> <comment>` - Add a comment to an existing issue. diff --git a/docs/drivers/karma.md b/docs/drivers/karma.md new file mode 100644 index 0000000..72bd3d4 --- /dev/null +++ b/docs/drivers/karma.md @@ -0,0 +1,9 @@ +# Karma Driver + +Karma tracks the "score" of things or people. + +## Usage +- `<thing>++` - Increases the karma of `<thing>`. +- `<thing>--` - Decreases the karma of `<thing>`. +- `(a thing with spaces)++` - Use parentheses for things with spaces. +- `!karma <thing>` - Retrieve the karma score of `<thing>`. diff --git a/docs/drivers/management.md b/docs/drivers/management.md new file mode 100644 index 0000000..7a0e628 --- /dev/null +++ b/docs/drivers/management.md @@ -0,0 +1,10 @@ +# Bot Management + +Commands for bot administrators. + +## Usage +- `!ignore <nick>` - Make the bot ignore a user. +- `!unignore <nick>` - Make the bot stop ignoring a user. +- `!rebuild` - Trigger a rebuild and restart (authorized users only). +- `!shutdown` - Shutdown the bot (authorized users only). +- `!migrate <state>` - Change database migration state. diff --git a/docs/drivers/markov.md b/docs/drivers/markov.md new file mode 100644 index 0000000..12e2ed8 --- /dev/null +++ b/docs/drivers/markov.md @@ -0,0 +1,10 @@ +# Markov and Insults + +Random sentence generation and friendly insults. + +## Usage +- `!markov [nick]` - Generate a random sentence. +- `!markov me` - Opt-in to Markov learning. +- `!don't markov me` - Opt-out of Markov learning. +- `!insult <nick>` - Insult a user at random. +- `!learn <tag> <sentence>` - Teach the bot a new sentence for a tag. diff --git a/docs/drivers/minecraft.md b/docs/drivers/minecraft.md new file mode 100644 index 0000000..325f88d --- /dev/null +++ b/docs/drivers/minecraft.md @@ -0,0 +1,6 @@ +# Minecraft Integration + +Minecraft server integration. + +## Usage +- `!mc set <key> <value>` - Set configuration for Minecraft integration. diff --git a/docs/drivers/push.md b/docs/drivers/push.md new file mode 100644 index 0000000..1c069bf --- /dev/null +++ b/docs/drivers/push.md @@ -0,0 +1,10 @@ +# Push Notifications + +Integration with Pushbullet. + +## Usage +- `!push enable` - Enable push notifications. +- `!push disable` - Disable push notifications. +- `!push auth <pin>` - Authenticate with Pushbullet. +- `!push add alias` - Add an alias for push notifications. +- `!push del alias` - Delete an alias. diff --git a/docs/drivers/quote.md b/docs/drivers/quote.md new file mode 100644 index 0000000..41f994a --- /dev/null +++ b/docs/drivers/quote.md @@ -0,0 +1,30 @@ +# Quote Driver + +The quote driver implements quote storage and retrieval. + +## Adding quotes +Quotes can be added in three ways (must address the bot by nick): +- `botNick: qadd <quote>` +- `botNick: quote add <quote>` +- `botNick: add quote <quote>` + +## Removing quotes +Quotes are deleted by quote ID (must address the bot by nick): +- `botNick: qdel <qid>` +- `botNick: quote del <qid>` +- `botNick: del quote <qid>` + +## Retrieving quotes by ID +A specific quote may be looked up by its ID: +- `botNick: quote #<qid>` + +## Retrieving quotes +Quotes may also be retrieved at random, with an optional case-insensitive regular expression: +- `botNick: quote <regex>` +- `botNick: quote` + +## Plugin Syntax +The quote driver provides plugin functionality for the factoid driver, allowing you to embed quotes in factoid results. Add a factoid containing a `<plugin=quote>` directive: +- `botNick: quote fact := <plugin=quote>` +- `botNick: quote fact := <plugin=quote #quote ID>` +- `botNick: quote fact := <plugin=quote regex>` diff --git a/docs/drivers/reminders.md b/docs/drivers/reminders.md new file mode 100644 index 0000000..acde114 --- /dev/null +++ b/docs/drivers/reminders.md @@ -0,0 +1,18 @@ +# Reminders and Tells + +sp0rkle can remind you of things later or leave messages for absent users. + +## Reminders +- `!remind <nick> <message> in <duration>` (e.g., `!remind me coffee in 10m`). +- `!remind <nick> <message> at <time>` (e.g., `!remind me meeting at 2pm`). +- `!remind list` - Lists reminders set by or for your nick. +- `!remind del <N>` - Deletes (previously listed) reminder N. +- `!snooze [duration]` - Resets the previously-triggered reminder. + +## Tells +- `!tell <nick> <msg>` - Stores a message for the (absent) nick. +- `!ask <nick> <msg>` - Same as `!tell`. + +## Timezones +- `!my timezone is <zone>` - Sets a local timezone for your nick. +- `!forget my timezone` - Unsets your local timezone. diff --git a/docs/drivers/utils.md b/docs/drivers/utils.md new file mode 100644 index 0000000..cab2bdc --- /dev/null +++ b/docs/drivers/utils.md @@ -0,0 +1,26 @@ +# Utilities + +Various small utility commands. + +## Calculator and Date +- `!calc <expression>` - Does maths for you. +- `!date <time/date> [in <zone>]` - Parse and format dates/times. + +## Conversions +- `!base <from>to<to> <num>` - Base conversion (e.g., `!base 10to16 255`). +- `!length <string>` - Returns the length of a string. +- `!chr <int>` - Get character from integer. +- `!ord <char>` - Get integer from character. +- `!netmask <ip/cidr>|<ip> <mask>` - Netmask calculator. + +## Web Tools +- `!ud <term>` - Urban Dictionary lookup. +- `!urlfind <regex>` - Search for URLs previously seen in the channel. +- `!randurl` / `!random url` - Show a random URL. +- `!shorten <url>` - Shortens a URL. +- `!cache <url>` / `!save <url>` - Caches a URL. + +## Stats and Seen +- `!seen <nick>` - Check when a user was last seen. +- `!stats [nick]` / `!lines [nick]` - Line count statistics. +- `!topten` / `!top10` - Show top 10 most active users. diff --git a/docs/guide.md b/docs/guide.md new file mode 100644 index 0000000..51f5657 --- /dev/null +++ b/docs/guide.md @@ -0,0 +1,23 @@ +# sp0rkle User Guide: How to use the Bot + +This guide provides an overview of the commands and features available to users of the sp0rkle IRC bot. + +## Core Features + +- [Factoids](drivers/factoid.md) - Teach the bot new things. +- [Karma](drivers/karma.md) - Track the "score" of things or people. +- [Quotes](drivers/quote.md) - Store and retrieve funny or memorable lines. +- [Reminders and Tells](drivers/reminders.md) - Set reminders for yourself or leave messages for others. +- [Decisions](drivers/decision.md) - Let the bot help you make choices. + +## Utilities and Integrations + +- [Utilities](drivers/utils.md) - Calculator, date parsing, base conversion, etc. +- [GitHub](drivers/github.md) - File and update issues directly from IRC. +- [Push Notifications](drivers/push.md) - Integration with Pushbullet. +- [Minecraft](drivers/minecraft.md) - Minecraft server integration. +- [Markov and Insults](drivers/markov.md) - Random sentence generation and friendly insults. + +## Bot Management + +- [Bot Management](drivers/management.md) - Commands for bot administrators.