181 lines
9.0 KiB
Markdown
181 lines
9.0 KiB
Markdown
> [!IMPORTANT]
|
|
> This project was developed entirely with AI coding assistance (Claude Opus 4.6 via Cursor IDE) and has not undergone rigorous review. It is provided as-is and may require adjustments for other environments.
|
|
|
|
# SongRequest — Limnoria IRC Song Request Plugin
|
|
|
|
A Limnoria (Supybot) plugin that watches IRC channels for song requests, validates them against the iTunes Search API, and streams them in real time to an HTMX web dashboard via WebSocket.
|
|
|
|
## Features
|
|
|
|
- **Passive detection** — recognizes `Artist - Title` patterns in chat and validates against Apple Music
|
|
- **Explicit command** — `!request Artist - Title` for direct requests
|
|
- **Disambiguation** — presents top matches when multiple results found; user picks by number
|
|
- **Feeling Lucky mode** — auto-select the first match, store the rest as alternates (per-channel)
|
|
- **Explicit/clean filtering** — prefer explicit tracks, filter out clean duplicates, or vice versa (configurable per-channel)
|
|
- **Last.fm spell correction** — optionally correct misspelled artist/track names before searching iTunes
|
|
- **Smart search** — dual-strategy iTunes queries with attribute-targeted searches and increased result limits
|
|
- **Web dashboard** — HTMX-powered UI with album art, Apple Music links, and moderation controls
|
|
- **Real-time updates** — WebSocket pushes new requests and status changes to all connected dashboards
|
|
- **Moderation** — approve, reject, or mark requests as played from the web UI
|
|
- **Bulk actions** — select multiple requests and approve, reject, or mark played in one action
|
|
- **Alternate matches** — when disambiguating, unchosen tracks appear as collapsible sub-cards with approve and mark-played buttons
|
|
- **Clickable cards** — the entire request card links to Apple Music
|
|
- **Session management** — start/stop named sessions, archive them, rename/clear/delete archived sessions
|
|
- **Channel grouping** — requests grouped by channel with tab filtering; URL-based channel routing
|
|
- **Auth system** — per-admin login via IRC-managed accounts (`addSongAdmin`), admin presence indicator
|
|
- **Theme support** — dark/light/system theme toggle
|
|
- **Rate limiting** — configurable per-user request limits
|
|
- **Ignore list** — block specific users from making requests
|
|
- **Auto-approve** — skip the pending queue and auto-approve requests (per-channel)
|
|
- **Persistence** — SQLite-backed; survives bot restarts
|
|
- **IRC announcements** — configurable delivery: channel, private message, or NOTICE
|
|
- **Quiet mode** — suppress the "Queued" IRC confirmation per-channel
|
|
- **Export history** — download request history as a Markdown file
|
|
- **Open/close requests** — global toggle (with per-channel override) from IRC or the web panel
|
|
- **Clear history** — wipe played/rejected entries from IRC or the web panel
|
|
- **Mobile responsive** — optimized layout for phones and tablets
|
|
|
|
## Dependencies
|
|
|
|
- `aiohttp` — standalone async web server for the dashboard
|
|
|
|
```bash
|
|
pip install aiohttp
|
|
```
|
|
|
|
## Installation
|
|
|
|
1. Copy the `SongRequest/` directory into your Limnoria plugins directory (or add its parent to `config directories.plugins`).
|
|
|
|
2. Load the plugin:
|
|
```
|
|
@load SongRequest
|
|
```
|
|
|
|
3. Configure enabled channels:
|
|
```
|
|
@config plugins.SongRequest.enabledChannels #music #requests
|
|
```
|
|
|
|
4. Add a web dashboard admin (from IRC, requires bot admin):
|
|
```
|
|
@addsongadmin alice s3cretK3y
|
|
```
|
|
|
|
5. (Optional) Set a Last.fm API key for spell correction:
|
|
```
|
|
@config plugins.SongRequest.lastfmApiKey YOUR_LASTFM_KEY
|
|
```
|
|
|
|
6. Access the dashboard at `http://<bot-host>:8888/` (default port, configurable via `webPort`).
|
|
|
|
## Configuration
|
|
|
|
### Global Settings
|
|
|
|
| Setting | Type | Default | Description |
|
|
|---------|------|---------|-------------|
|
|
| `enabledChannels` | Space-separated list | (empty) | Channels for passive detection |
|
|
| `ignoredUsers` | Space-separated list | (empty) | Nicks/hostmasks to ignore |
|
|
| `maxRequestsPerUser` | Integer | 10 | Max requests per rate limit window (0 = unlimited) |
|
|
| `rateLimitWindow` | Integer | 3600 | Rate limit window in seconds |
|
|
| `webAuthToken` | String (private) | (empty) | (Deprecated) Legacy shared auth token; prefer per-admin accounts |
|
|
| `lastfmApiKey` | String (private) | (empty) | Last.fm API key for spell correction; empty to disable |
|
|
| `announceStatus` | Boolean | True | Master switch for all IRC status announcements |
|
|
| `maxChoices` | Integer | 3 | Disambiguation choices shown |
|
|
| `webPort` | Integer | 8888 | Port for the web dashboard server |
|
|
| `webHost` | String | 0.0.0.0 | Bind address for the web dashboard server |
|
|
| `requestsOpen` | Boolean | True | Global toggle — accept or reject new requests |
|
|
|
|
### Per-Channel Settings
|
|
|
|
| Setting | Type | Default | Description |
|
|
|---------|------|---------|-------------|
|
|
| `requestsOpenOverride` | String | (empty) | `open`, `closed`, or empty for global default |
|
|
| `quietQueued` | Boolean | False | Suppress the "Queued: ..." IRC confirmation |
|
|
| `queuedReplyMode` | String | `channel` | Queued confirmation delivery: `channel`, `private`, or `notice` |
|
|
| `autoApprove` | Boolean | False | Auto-approve requests (skip pending queue) |
|
|
| `feelingLucky` | Boolean | False | Auto-select first match; store rest as alternates |
|
|
| `explicitMode` | String | `filter` | `off`, `prefer`, `filter`, or `clean` (see below) |
|
|
| `announceApproved` | Boolean | True | Announce approved requests in IRC |
|
|
| `announceRejected` | Boolean | True | Announce rejected requests in IRC |
|
|
| `announceNowPlaying` | Boolean | True | Announce now-playing requests in IRC |
|
|
| `announceReplyMode` | String | `channel` | Status announcement delivery: `channel`, `private`, or `notice` |
|
|
| `passiveDetection` | Boolean | True | Enable passive pattern matching |
|
|
| `requestCommand` | Boolean | True | Enable the `!request` command |
|
|
|
|
### Explicit Mode
|
|
|
|
Controls how explicit/clean track versions are handled in search results:
|
|
|
|
- **`off`** — return results as-is from iTunes
|
|
- **`prefer`** — sort explicit tracks first, keep all results
|
|
- **`filter`** (default) — drop cleaned versions when an explicit version of the same track exists, sort explicit first
|
|
- **`clean`** — drop explicit versions when a clean version exists, sort clean first
|
|
|
|
## Commands
|
|
|
|
| Command | Description | Permission |
|
|
|---------|-------------|------------|
|
|
| `request <text>` | Request a song by artist/title | Anyone |
|
|
| `pick <number>` | Pick from disambiguation choices | Anyone |
|
|
| `ignore <nick>` | Add a nick to the ignore list | Admin |
|
|
| `unignore <nick>` | Remove a nick from the ignore list | Admin |
|
|
| `requeststats` | Show queue statistics | Anyone |
|
|
| `openrequests [channel]` | Open requests globally or per-channel | Admin |
|
|
| `closerequests [channel]` | Close requests globally or per-channel | Admin |
|
|
| `clearhistory` | Clear all played/rejected requests | Admin |
|
|
| `startsession [name]` | Start a new request session | Admin |
|
|
| `stopsession` | Stop and archive the active session | Admin |
|
|
| `addsongadmin <user> <key>` | Add a web dashboard admin account | Admin |
|
|
| `removesongadmin <user>` | Remove a web dashboard admin account | Admin |
|
|
| `listsongadmins` | List all web dashboard admin accounts | Admin |
|
|
|
|
## Web Dashboard
|
|
|
|
The dashboard runs on a standalone aiohttp server at the configured `webPort` (default `8888`).
|
|
|
|
### Authentication
|
|
|
|
Admins are managed via IRC commands (`addsongadmin`/`removesongadmin`). The dashboard has a login page at `/login`. Non-admins can view the queue and history read-only; admin controls (moderation buttons, session management, export, etc.) are only visible to logged-in admins.
|
|
|
|
A floating presence indicator shows which admins are currently online.
|
|
|
|
### Queue View
|
|
|
|
- **Pending** requests awaiting moderation
|
|
- **Approved** requests ready to play
|
|
- **Bulk select** mode for mass approve/reject/mark-played
|
|
- Action buttons (approve/reject/mark played) positioned in the top-right corner of each card
|
|
|
|
### History View
|
|
|
|
- History of played/rejected requests with **Export .md** and **Clear History** buttons
|
|
- **Session archives** — previous sessions listed with expand-to-view, plus rename/clear/delete actions
|
|
|
|
### Other Features
|
|
|
|
- Channel filter tabs with URL-based routing (`/#channelName` or `/channelName`)
|
|
- Dark/light/system theme toggle
|
|
- Toast notifications for new requests when viewing history
|
|
- Custom themed modals (no native browser prompts)
|
|
- Real-time WebSocket connection with auto-reconnect
|
|
|
|
## How Passive Detection Works
|
|
|
|
1. The bot watches messages in `enabledChannels` for lines matching `Something - Something`
|
|
2. Lines starting with bot command prefixes (`!`, `.`, `@`, etc.) or URLs are skipped
|
|
3. If a Last.fm API key is configured, artist/track names are spell-corrected
|
|
4. The extracted text is searched against the iTunes Search API (dual-strategy: combined + attribute-targeted)
|
|
5. Results are filtered/sorted based on `explicitMode`
|
|
6. If no song matches, the message is silently ignored
|
|
7. If one match (or `feelingLucky` is on), it's queued automatically
|
|
8. If multiple matches, the user is presented with choices
|
|
|
|
## Running Tests
|
|
|
|
```bash
|
|
pip install limnoria aiohttp pytest
|
|
python -m pytest SongRequest/test.py -v
|
|
```
|