docs: update docs for ACL tiers and webhook
- USAGE.md: permission tiers section, webhook config/API/example - CHEATSHEET.md: ACL tiers and webhook quick-ref sections - ROADMAP.md: mark webhook and ACL items done - TODO.md: mark webhook and ACL items done - TASKS.md: new sprint for ACL + webhook work
This commit is contained in:
114
docs/USAGE.md
114
docs/USAGE.md
@@ -53,10 +53,18 @@ rate_burst = 5 # Burst capacity (default: 5)
|
||||
paste_threshold = 4 # Max lines before overflow to FlaskPaste (default: 4)
|
||||
admins = [] # Hostmask patterns (fnmatch), IRCOPs auto-detected
|
||||
timezone = "UTC" # Timezone for calendar reminders (IANA tz name)
|
||||
operators = [] # Hostmask patterns for "oper" tier (fnmatch)
|
||||
trusted = [] # Hostmask patterns for "trusted" tier (fnmatch)
|
||||
|
||||
[logging]
|
||||
level = "info" # Logging level: debug, info, warning, error
|
||||
format = "text" # Log format: "text" (default) or "json"
|
||||
|
||||
[webhook]
|
||||
enabled = false # Enable HTTP webhook listener
|
||||
host = "127.0.0.1" # Bind address
|
||||
port = 8080 # Bind port
|
||||
secret = "" # HMAC-SHA256 shared secret (empty = no auth)
|
||||
```
|
||||
|
||||
## Built-in Commands
|
||||
@@ -142,6 +150,7 @@ format = "text" # Log format: "text" (default) or "json"
|
||||
| `!paste <text>` | Create a paste via FlaskPaste |
|
||||
| `!pastemoni <add\|del\|list\|check>` | Paste site keyword monitoring |
|
||||
| `!cron <add\|del\|list>` | Scheduled command execution (admin) |
|
||||
| `!webhook` | Show webhook listener status (admin) |
|
||||
|
||||
### Command Shorthand
|
||||
|
||||
@@ -221,24 +230,31 @@ Each line contains:
|
||||
|
||||
Default format is `"text"` (human-readable, same as before).
|
||||
|
||||
## Admin System
|
||||
## Permission Tiers (ACL)
|
||||
|
||||
Commands marked as `admin` require elevated permissions. Admin access is
|
||||
granted via:
|
||||
The bot uses a 4-tier permission model. Each command has a required tier;
|
||||
users must meet or exceed it.
|
||||
|
||||
1. **IRC operator status** -- detected automatically via `WHO`
|
||||
2. **Hostmask patterns** -- configured in `[bot] admins`, fnmatch-style
|
||||
```
|
||||
user < trusted < oper < admin
|
||||
```
|
||||
|
||||
| Tier | Granted by |
|
||||
|------|------------|
|
||||
| `user` | Everyone (default) |
|
||||
| `trusted` | `[bot] trusted` hostmask patterns |
|
||||
| `oper` | `[bot] operators` hostmask patterns |
|
||||
| `admin` | `[bot] admins` hostmask patterns or IRC operator status |
|
||||
|
||||
```toml
|
||||
[bot]
|
||||
admins = [
|
||||
"*!~user@trusted.host",
|
||||
"ops!*@*.ops.net",
|
||||
]
|
||||
admins = ["*!~root@*.ops.net"]
|
||||
operators = ["*!~staff@trusted.host"]
|
||||
trusted = ["*!~user@known.host"]
|
||||
```
|
||||
|
||||
Empty by default -- only IRC operators get admin access unless patterns
|
||||
are configured.
|
||||
All lists are empty by default -- only IRC operators get admin access
|
||||
unless patterns are configured. Patterns use fnmatch-style matching.
|
||||
|
||||
### Oper Detection
|
||||
|
||||
@@ -256,18 +272,24 @@ set automatically.
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `!whoami` | Show your hostmask and admin status |
|
||||
| `!admins` | Show configured patterns and detected opers (admin) |
|
||||
| `!whoami` | Show your hostmask and permission tier |
|
||||
| `!admins` | Show configured tiers and detected opers (admin) |
|
||||
|
||||
Admin-restricted commands: `!load`, `!reload`, `!unload`, `!admins`, `!state`,
|
||||
`!kick`, `!ban`, `!unban`, `!topic`, `!mode`.
|
||||
`!kick`, `!ban`, `!unban`, `!topic`, `!mode`, `!webhook`.
|
||||
|
||||
### Writing Admin Commands
|
||||
### Writing Tiered Commands
|
||||
|
||||
```python
|
||||
# admin=True still works (maps to tier="admin")
|
||||
@command("dangerous", help="Admin-only action", admin=True)
|
||||
async def cmd_dangerous(bot, message):
|
||||
...
|
||||
|
||||
# Explicit tier for finer control
|
||||
@command("moderate", help="Trusted-only action", tier="trusted")
|
||||
async def cmd_moderate(bot, message):
|
||||
...
|
||||
```
|
||||
|
||||
## IRCv3 Capability Negotiation
|
||||
@@ -405,6 +427,68 @@ restarting the bot.
|
||||
The `core` plugin cannot be unloaded (prevents losing `!load`/`!reload`),
|
||||
but it can be reloaded.
|
||||
|
||||
## Webhook Listener
|
||||
|
||||
Receive HTTP POST requests from external services (CI, monitoring, GitHub,
|
||||
etc.) and relay messages to IRC channels.
|
||||
|
||||
### Configuration
|
||||
|
||||
```toml
|
||||
[webhook]
|
||||
enabled = true
|
||||
host = "127.0.0.1"
|
||||
port = 8080
|
||||
secret = "your-shared-secret"
|
||||
```
|
||||
|
||||
### HTTP API
|
||||
|
||||
Single endpoint: `POST /`
|
||||
|
||||
**Request body** (JSON):
|
||||
|
||||
```json
|
||||
{"channel": "#ops", "text": "Deploy v2.3.1 complete"}
|
||||
```
|
||||
|
||||
Optional `"action": true` sends as a `/me` action.
|
||||
|
||||
**Authentication**: HMAC-SHA256 via `X-Signature: sha256=<hex>` header.
|
||||
If `secret` is empty, no authentication is required.
|
||||
|
||||
**Response codes**:
|
||||
|
||||
| Status | When |
|
||||
|--------|------|
|
||||
| 200 OK | Message sent |
|
||||
| 400 Bad Request | Invalid JSON, missing/invalid channel, empty text |
|
||||
| 401 Unauthorized | Bad or missing HMAC signature |
|
||||
| 405 Method Not Allowed | Non-POST request |
|
||||
| 413 Payload Too Large | Body > 64 KB |
|
||||
|
||||
### Usage Example
|
||||
|
||||
```bash
|
||||
SECRET="your-shared-secret"
|
||||
BODY='{"channel":"#ops","text":"Deploy v2.3.1 complete"}'
|
||||
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
|
||||
curl -X POST http://127.0.0.1:8080/ \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Signature: sha256=$SIG" \
|
||||
-d "$BODY"
|
||||
```
|
||||
|
||||
### Status Command
|
||||
|
||||
```
|
||||
!webhook Show listener address, request count, uptime (admin)
|
||||
```
|
||||
|
||||
The server starts on IRC connect (event 001) and runs for the lifetime of
|
||||
the bot. If the server is already running (e.g. after reconnect), it is
|
||||
not restarted.
|
||||
|
||||
## Writing Plugins
|
||||
|
||||
Create a `.py` file in the `plugins/` directory:
|
||||
|
||||
Reference in New Issue
Block a user