docs: update docs for Mumble integration
Add Mumble sections to USAGE.md, CHEATSHEET.md, API.md, README.md. Mark Mumble done in ROADMAP.md and TODO.md. Update TASKS.md sprint.
This commit is contained in:
@@ -26,6 +26,7 @@ make down # Stop
|
|||||||
- Async IRC over plain TCP or TLS (SASL PLAIN auth, IRCv3 CAP negotiation)
|
- Async IRC over plain TCP or TLS (SASL PLAIN auth, IRCv3 CAP negotiation)
|
||||||
- Microsoft Teams support via outgoing webhooks (no SDK dependency)
|
- Microsoft Teams support via outgoing webhooks (no SDK dependency)
|
||||||
- Telegram support via long-polling (no SDK dependency, SOCKS5 proxied)
|
- Telegram support via long-polling (no SDK dependency, SOCKS5 proxied)
|
||||||
|
- Mumble support via TCP/TLS protobuf control channel (text only, SOCKS5 proxied)
|
||||||
- Plugin system with `@command` and `@event` decorators
|
- Plugin system with `@command` and `@event` decorators
|
||||||
- Hot-reload: load, unload, reload plugins at runtime
|
- Hot-reload: load, unload, reload plugins at runtime
|
||||||
- Admin permission system (hostmask patterns + IRCOP detection + AAD IDs)
|
- Admin permission system (hostmask patterns + IRCOP detection + AAD IDs)
|
||||||
|
|||||||
10
ROADMAP.md
10
ROADMAP.md
@@ -149,3 +149,13 @@
|
|||||||
- [ ] Adaptive Cards for richer formatting (Teams)
|
- [ ] Adaptive Cards for richer formatting (Teams)
|
||||||
- [ ] Graph API integration for DMs and richer channel access (Teams)
|
- [ ] Graph API integration for DMs and richer channel access (Teams)
|
||||||
- [ ] Teams event handlers (member join/leave)
|
- [ ] Teams event handlers (member join/leave)
|
||||||
|
|
||||||
|
## v2.2.0 -- Protocol Expansion
|
||||||
|
|
||||||
|
- [x] Mumble adapter via TCP/TLS protobuf control channel (text chat only)
|
||||||
|
- [ ] Discord adapter via WebSocket gateway + REST API
|
||||||
|
- [ ] Matrix adapter via long-poll `/sync` endpoint
|
||||||
|
- [ ] XMPP adapter via persistent TCP + XML stanzas (MUC support)
|
||||||
|
- [ ] Slack adapter via Socket Mode WebSocket
|
||||||
|
- [ ] Mattermost adapter via WebSocket API
|
||||||
|
- [ ] Bluesky adapter via AT Protocol firehose + REST API
|
||||||
|
|||||||
16
TASKS.md
16
TASKS.md
@@ -1,6 +1,20 @@
|
|||||||
# derp - Tasks
|
# derp - Tasks
|
||||||
|
|
||||||
## Current Sprint -- v2.1.0 Telegram Integration (2026-02-21)
|
## Current Sprint -- v2.2.0 Mumble Adapter (2026-02-21)
|
||||||
|
|
||||||
|
| Pri | Status | Task |
|
||||||
|
|-----|--------|------|
|
||||||
|
| P0 | [x] | `src/derp/mumble.py` -- MumbleBot, MumbleMessage, protobuf codec |
|
||||||
|
| P0 | [x] | TCP/TLS connection through SOCKS5 proxy |
|
||||||
|
| P0 | [x] | Minimal protobuf encoder/decoder (no external protobuf dep) |
|
||||||
|
| P0 | [x] | Mumble protocol: Version, Authenticate, Ping, TextMessage |
|
||||||
|
| P0 | [x] | Channel/user state tracking from ChannelState/UserState messages |
|
||||||
|
| P0 | [x] | `src/derp/config.py` -- `[mumble]` defaults |
|
||||||
|
| P0 | [x] | `src/derp/cli.py` -- conditionally start MumbleBot |
|
||||||
|
| P1 | [x] | Tests: `test_mumble.py` (93 cases, 1470 total) |
|
||||||
|
| P2 | [x] | Documentation update (USAGE.md, CHEATSHEET.md, API.md, README.md, ROADMAP.md) |
|
||||||
|
|
||||||
|
## Previous Sprint -- v2.1.0 Telegram Integration (2026-02-21)
|
||||||
|
|
||||||
| Pri | Status | Task |
|
| Pri | Status | Task |
|
||||||
|-----|--------|------|
|
|-----|--------|------|
|
||||||
|
|||||||
55
TODO.md
55
TODO.md
@@ -105,6 +105,61 @@ is preserved in git history for reference.
|
|||||||
- [ ] Markdown/HTML formatting mode toggle
|
- [ ] Markdown/HTML formatting mode toggle
|
||||||
- [ ] Webhook mode (for setWebhook instead of getUpdates)
|
- [ ] Webhook mode (for setWebhook instead of getUpdates)
|
||||||
|
|
||||||
|
## Discord
|
||||||
|
|
||||||
|
- [ ] Discord adapter via WebSocket gateway + REST API (no SDK)
|
||||||
|
- [ ] DiscordBot + DiscordMessage (duck-typed with IRC Message)
|
||||||
|
- [ ] Gateway intents for message content
|
||||||
|
- [ ] Message splitting at 2000-char limit
|
||||||
|
- [ ] Permission tiers via user/role IDs
|
||||||
|
- [ ] Slash command registration (optional)
|
||||||
|
|
||||||
|
## Matrix
|
||||||
|
|
||||||
|
- [ ] Matrix adapter via long-poll `/sync` endpoint (no SDK)
|
||||||
|
- [ ] MatrixBot + MatrixMessage (duck-typed with IRC Message)
|
||||||
|
- [ ] Room-based messaging (rooms map to channels)
|
||||||
|
- [ ] Power levels map to permission tiers
|
||||||
|
- [ ] E2EE support (optional, requires libolm)
|
||||||
|
|
||||||
|
## XMPP
|
||||||
|
|
||||||
|
- [ ] XMPP adapter via persistent TCP + XML stanzas (no SDK)
|
||||||
|
- [ ] XMPPBot + XMPPMessage (duck-typed with IRC Message)
|
||||||
|
- [ ] MUC (Multi-User Chat) support -- rooms map to channels
|
||||||
|
- [ ] SASL authentication
|
||||||
|
- [ ] TLS/STARTTLS connection
|
||||||
|
|
||||||
|
## Mumble
|
||||||
|
|
||||||
|
- [x] Mumble adapter via TCP/TLS + protobuf control channel (no SDK)
|
||||||
|
- [x] MumbleBot + MumbleMessage (duck-typed with IRC Message)
|
||||||
|
- [x] Text chat only (no voice)
|
||||||
|
- [x] Channel-based messaging
|
||||||
|
- [x] Minimal protobuf encoder/decoder (no protobuf dep)
|
||||||
|
|
||||||
|
## Slack
|
||||||
|
|
||||||
|
- [ ] Slack adapter via Socket Mode WebSocket (no SDK)
|
||||||
|
- [ ] SlackBot + SlackMessage (duck-typed with IRC Message)
|
||||||
|
- [ ] OAuth token + WebSocket for events
|
||||||
|
- [ ] Channel/DM messaging
|
||||||
|
- [ ] Permission tiers via user IDs
|
||||||
|
|
||||||
|
## Mattermost
|
||||||
|
|
||||||
|
- [ ] Mattermost adapter via WebSocket API (no SDK)
|
||||||
|
- [ ] MattermostBot + MattermostMessage (duck-typed with IRC Message)
|
||||||
|
- [ ] Self-hosted Slack alternative
|
||||||
|
- [ ] Channel/DM messaging
|
||||||
|
|
||||||
|
## Bluesky
|
||||||
|
|
||||||
|
- [ ] Bluesky adapter via AT Protocol firehose + REST API (no SDK)
|
||||||
|
- [ ] BlueskyBot + BlueskyMessage (duck-typed with IRC Message)
|
||||||
|
- [ ] Mention-based command dispatch
|
||||||
|
- [ ] Post/reply via `com.atproto.repo.createRecord`
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
- [x] Plugin command unit tests (encode, hash, dns, cidr, defang)
|
- [x] Plugin command unit tests (encode, hash, dns, cidr, defang)
|
||||||
|
|||||||
81
docs/API.md
81
docs/API.md
@@ -357,6 +357,87 @@ Same stable attributes and methods as `Bot`:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## `derp.mumble` -- Mumble Adapter
|
||||||
|
|
||||||
|
Alternative bot adapter for Mumble via TCP/TLS protobuf control channel
|
||||||
|
(text chat only). All TCP routed through SOCKS5 proxy. Uses a minimal
|
||||||
|
built-in protobuf encoder/decoder (no external dependency). Exposes the
|
||||||
|
same plugin API as `derp.bot.Bot`.
|
||||||
|
|
||||||
|
### `MumbleMessage` dataclass
|
||||||
|
|
||||||
|
Duck-typed compatible with IRC `Message`:
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `raw` | `dict` | Decoded protobuf fields |
|
||||||
|
| `nick` | `str \| None` | Sender username (from session lookup) |
|
||||||
|
| `prefix` | `str \| None` | Sender username (for ACL) |
|
||||||
|
| `text` | `str \| None` | Message body (HTML stripped) |
|
||||||
|
| `target` | `str \| None` | channel_id as string (or `"dm"`) |
|
||||||
|
| `is_channel` | `bool` | `True` for channel msgs, `False` for DMs |
|
||||||
|
| `command` | `str` | Always `"PRIVMSG"` (compat shim) |
|
||||||
|
| `params` | `list[str]` | `[target, text]` |
|
||||||
|
| `tags` | `dict[str, str]` | Empty dict (no IRCv3 tags) |
|
||||||
|
|
||||||
|
### `MumbleBot`
|
||||||
|
|
||||||
|
Same stable attributes and methods as `Bot`:
|
||||||
|
|
||||||
|
| Attribute | Type | Description |
|
||||||
|
|-----------|------|-------------|
|
||||||
|
| `name` | `str` | Always `"mumble"` |
|
||||||
|
| `config` | `dict` | Merged TOML configuration |
|
||||||
|
| `nick` | `str` | Bot username (from config) |
|
||||||
|
| `prefix` | `str` | Command prefix (from `[mumble]` or `[bot]`) |
|
||||||
|
| `state` | `StateStore` | Persistent key-value storage |
|
||||||
|
| `registry` | `PluginRegistry` | Shared command and event registry |
|
||||||
|
|
||||||
|
**Sending messages** -- same signatures, Mumble protobuf transport:
|
||||||
|
|
||||||
|
| Method | Behaviour |
|
||||||
|
|--------|-----------|
|
||||||
|
| `send(target, text)` | TextMessage to channel (HTML-escaped) |
|
||||||
|
| `reply(msg, text)` | `send(msg.target, text)` |
|
||||||
|
| `long_reply(msg, lines, *, label="")` | Paste overflow, same logic as IRC |
|
||||||
|
| `action(target, text)` | Italic HTML text (`<i>...</i>`) |
|
||||||
|
| `shorten_url(url)` | Same FlaskPaste integration |
|
||||||
|
|
||||||
|
**IRC no-ops** (debug log, no error):
|
||||||
|
|
||||||
|
`join`, `part`, `kick`, `mode`, `set_topic`
|
||||||
|
|
||||||
|
**Plugin management** -- delegates to shared registry:
|
||||||
|
|
||||||
|
`load_plugins`, `load_plugin`, `reload_plugin`, `unload_plugin`
|
||||||
|
|
||||||
|
**Permission tiers** -- same model, exact username string matching:
|
||||||
|
|
||||||
|
`_get_tier(msg)`, `_is_admin(msg)`
|
||||||
|
|
||||||
|
### Protobuf Codec (internal)
|
||||||
|
|
||||||
|
Minimal protobuf wire format helpers -- not part of the stable API:
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `_encode_varint(value)` | Encode unsigned int as protobuf varint |
|
||||||
|
| `_decode_varint(data, offset)` | Decode varint, returns `(value, offset)` |
|
||||||
|
| `_encode_field(num, wire_type, value)` | Encode a single protobuf field |
|
||||||
|
| `_decode_fields(data)` | Decode payload into `{field_num: [values]}` |
|
||||||
|
| `_pack_msg(msg_type, payload)` | Wrap payload in 6-byte Mumble header |
|
||||||
|
| `_unpack_header(data)` | Unpack header into `(msg_type, length)` |
|
||||||
|
|
||||||
|
### Helper Functions
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `_strip_html` | `(text: str) -> str` | Strip HTML tags and unescape entities |
|
||||||
|
| `_escape_html` | `(text: str) -> str` | Escape text for Mumble HTML messages |
|
||||||
|
| `_build_mumble_message` | `(fields, users, our_session) -> MumbleMessage \| None` | Build message from decoded TextMessage fields |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Handler Signatures
|
## Handler Signatures
|
||||||
|
|
||||||
All command and event handlers are async functions receiving `bot` and
|
All command and event handlers are async functions receiving `bot` and
|
||||||
|
|||||||
@@ -521,6 +521,26 @@ Long-polling via `getUpdates` -- no public endpoint needed. All HTTP
|
|||||||
through SOCKS5 proxy. Strips `@botusername` suffix in groups. Messages
|
through SOCKS5 proxy. Strips `@botusername` suffix in groups. Messages
|
||||||
split at 4096 chars. IRC-only commands are no-ops. ~90% of plugins work.
|
split at 4096 chars. IRC-only commands are no-ops. ~90% of plugins work.
|
||||||
|
|
||||||
|
## Mumble Integration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# config/derp.toml
|
||||||
|
[mumble]
|
||||||
|
enabled = true
|
||||||
|
host = "mumble.example.com"
|
||||||
|
port = 64738
|
||||||
|
username = "derp"
|
||||||
|
password = ""
|
||||||
|
tls_verify = false # self-signed certs common
|
||||||
|
admins = ["admin_user"] # Mumble usernames
|
||||||
|
operators = []
|
||||||
|
trusted = []
|
||||||
|
```
|
||||||
|
|
||||||
|
TCP/TLS via SOCKS5 proxy. Text chat only (no voice). Minimal protobuf
|
||||||
|
codec (no external dep). HTML stripped on receive, escaped on send.
|
||||||
|
IRC-only commands are no-ops. ~90% of plugins work.
|
||||||
|
|
||||||
## Plugin Template
|
## Plugin Template
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|||||||
@@ -1474,3 +1474,83 @@ this automatically: `!help@mybot` becomes `!help`.
|
|||||||
All HTTP traffic (API calls, long-polling) routes through the SOCKS5
|
All HTTP traffic (API calls, long-polling) routes through the SOCKS5
|
||||||
proxy at `127.0.0.1:1080` via `derp.http.urlopen`. No direct outbound
|
proxy at `127.0.0.1:1080` via `derp.http.urlopen`. No direct outbound
|
||||||
connections are made.
|
connections are made.
|
||||||
|
|
||||||
|
## Mumble Integration
|
||||||
|
|
||||||
|
Connect derp to a Mumble server via TCP/TLS protobuf control channel.
|
||||||
|
Text chat only (no voice). All TCP is routed through the SOCKS5 proxy.
|
||||||
|
No protobuf library dependency -- uses a minimal built-in varint/field
|
||||||
|
encoder/decoder for the ~7 message types needed.
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
|
||||||
|
The bot connects to the Mumble server over TLS (via SOCKS5), sends
|
||||||
|
Version and Authenticate messages, then enters a read loop. It tracks
|
||||||
|
channels (ChannelState), users (UserState), and dispatches commands
|
||||||
|
from TextMessage messages through the shared plugin registry.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[mumble]
|
||||||
|
enabled = true
|
||||||
|
host = "mumble.example.com" # Mumble server hostname
|
||||||
|
port = 64738 # Default Mumble port
|
||||||
|
username = "derp" # Bot username
|
||||||
|
password = "" # Server password (optional)
|
||||||
|
tls_verify = false # Mumble commonly uses self-signed certs
|
||||||
|
admins = ["admin_user"] # Mumble usernames
|
||||||
|
operators = [] # Mumble usernames
|
||||||
|
trusted = [] # Mumble usernames
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mumble Setup
|
||||||
|
|
||||||
|
1. **Ensure a Mumble server** (Murmur/Mumble-server) is running and
|
||||||
|
accessible through the SOCKS5 proxy
|
||||||
|
|
||||||
|
2. **Configure the bot** with the server hostname, port, and credentials
|
||||||
|
|
||||||
|
3. **Configure permissions** using Mumble registered usernames. Use
|
||||||
|
`!whoami` to discover your username as the bot sees it.
|
||||||
|
|
||||||
|
### Permission Tiers
|
||||||
|
|
||||||
|
Same 4-tier model as IRC, but matches exact Mumble usernames instead of
|
||||||
|
fnmatch hostmask patterns:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[mumble]
|
||||||
|
admins = ["admin_user"]
|
||||||
|
operators = ["oper_user"]
|
||||||
|
trusted = ["trusted_user"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plugin Compatibility
|
||||||
|
|
||||||
|
Same compatibility as Teams/Telegram -- ~90% of plugins work without
|
||||||
|
modification.
|
||||||
|
|
||||||
|
| Feature | IRC | Mumble |
|
||||||
|
|---------|-----|--------|
|
||||||
|
| `bot.reply()` | Sends PRIVMSG | TextMessage to channel |
|
||||||
|
| `bot.send()` | Sends PRIVMSG | TextMessage to channel |
|
||||||
|
| `bot.action()` | CTCP ACTION | Italic HTML text (`<i>...</i>`) |
|
||||||
|
| `bot.long_reply()` | Paste overflow | Paste overflow (same logic) |
|
||||||
|
| `bot.state` | Per-server SQLite | Per-server SQLite |
|
||||||
|
| `bot.join/part/kick/mode` | IRC commands | No-op (logged at debug) |
|
||||||
|
| Event handlers (JOIN, etc.) | Fired on IRC events | Not triggered |
|
||||||
|
| Hostmask ACL | fnmatch patterns | Exact usernames |
|
||||||
|
|
||||||
|
### Text Encoding
|
||||||
|
|
||||||
|
Mumble uses HTML for text messages. On receive, the bot strips tags and
|
||||||
|
unescapes entities. On send, text is HTML-escaped. Action messages use
|
||||||
|
`<i>` tags for italic formatting.
|
||||||
|
|
||||||
|
### Transport
|
||||||
|
|
||||||
|
All TCP connections route through the SOCKS5 proxy at `127.0.0.1:1080`
|
||||||
|
via `derp.http.create_connection`. TLS is applied on top of the proxied
|
||||||
|
socket. Mumble commonly uses self-signed certificates, so `tls_verify`
|
||||||
|
defaults to `false`.
|
||||||
|
|||||||
Reference in New Issue
Block a user