docs(alerts): document webhook/notification system in USAGE.md

fix(db): use gevent-safe local storage for DB connections under gunicorn+gevent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
James Smith
2026-04-15 12:45:58 +01:00
parent 6dce911172
commit f12f4145ef
2 changed files with 985 additions and 784 deletions
+144
View File
@@ -539,6 +539,150 @@ Enable "Show All Agents" to aggregate data from all registered agents simultaneo
For complete documentation, see [Distributed Agents Guide](DISTRIBUTED_AGENTS.md).
## Webhooks & Notifications
INTERCEPT has a built-in alert engine that fires webhooks when decoded events match configurable rules. This lets you forward pager messages (or events from any other mode) to Discord, Slack, n8n, Home Assistant, or any HTTP endpoint.
### How it works
1. You configure **alert rules** via the Alerts UI — each rule defines which mode and event type to watch, optional match criteria, and a severity level.
2. When an incoming event matches a rule, INTERCEPT stores it in the alert log and POSTs a JSON payload to your configured webhook URL.
3. All modes are supported: pager, sensor, ADS-B, AIS, ACARS, WiFi, Bluetooth, and more.
### Enable the webhook
Set these environment variables in your `.env` file or `docker-compose.yml`:
| Variable | Default | Description |
|----------|---------|-------------|
| `ALERT_WEBHOOK_URL` | _(empty)_ | URL to POST alert payloads to |
| `ALERT_WEBHOOK_SECRET` | _(empty)_ | Optional token sent as `X-Alert-Token` header |
| `ALERT_WEBHOOK_TIMEOUT` | `5` | HTTP timeout in seconds |
**Local install (`.env`):**
```env
ALERT_WEBHOOK_URL=https://your-endpoint.example.com/intercept-alerts
ALERT_WEBHOOK_SECRET=mysecrettoken
```
**Docker (`.env` or `docker-compose.yml` environment block):**
```env
ALERT_WEBHOOK_URL=https://your-endpoint.example.com/intercept-alerts
ALERT_WEBHOOK_SECRET=mysecrettoken
```
### Create an alert rule
1. Open the **Alerts** panel in INTERCEPT
2. Click **New Rule**
3. Configure:
- **Mode**: `pager` (or any other mode, or leave blank to match all)
- **Event type**: `message` for pager decodes (or blank to match all event types)
- **Match criteria**: leave empty to forward everything, or add filters (e.g. capcode equals `1234567`, or message contains `FIRE`)
- **Severity**: `low`, `medium`, or `high`
4. Save and enable the rule
### Webhook payload format
INTERCEPT sends a POST request with `Content-Type: application/json`:
```json
{
"id": 42,
"rule_id": 1,
"mode": "pager",
"event_type": "message",
"severity": "medium",
"title": "My Pager Rule",
"message": "message | 1234567",
"created_at": "2026-04-13T10:00:00+00:00",
"payload": {
"mode": "pager",
"event_type": "message",
"event": {
"capcode": "1234567",
"message": "UNIT 4 RESPOND TO 123 MAIN ST",
"type": "POCSAG1200"
},
"rule": { "id": 1, "name": "My Pager Rule" }
}
}
```
### Sending to Discord
Discord webhooks expect a specific JSON format (`content`, `embeds`), so you need a small relay between INTERCEPT and Discord. Two options:
**Option A — No-code relay (recommended)**
Use [n8n](https://n8n.io), [Make](https://make.com), or [Pipedream](https://pipedream.com) to receive INTERCEPT's webhook and forward it to Discord with a custom message template. Point `ALERT_WEBHOOK_URL` at your workflow's ingest URL.
**Option B — Self-hosted Python relay**
Save this as `discord_relay.py` and run it alongside INTERCEPT:
```python
from flask import Flask, request
import urllib.request, json
app = Flask(__name__)
DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN"
@app.post("/relay")
def relay():
data = request.get_json(force=True)
mode = data.get("mode", "unknown").upper()
title = data.get("title", "Alert")
message = data.get("message", "")
event = data.get("payload", {}).get("event", {})
# Build a readable Discord message
lines = [f"**[{mode}]** {title}", message]
if event.get("capcode"):
lines.append(f"Capcode: `{event['capcode']}`")
if event.get("type"):
lines.append(f"Protocol: {event['type']}")
payload = json.dumps({"content": "\n".join(lines)}).encode()
req = urllib.request.Request(
DISCORD_WEBHOOK_URL,
data=payload,
headers={"Content-Type": "application/json"},
method="POST",
)
urllib.request.urlopen(req, timeout=5)
return "", 204
app.run(host="0.0.0.0", port=5051)
```
Then set:
```env
ALERT_WEBHOOK_URL=http://localhost:5051/relay
```
Run the relay: `python3 discord_relay.py`
The relay formats pager decodes as Discord messages like:
```
[PAGER] My Pager Rule
message | 1234567
Capcode: `1234567`
Protocol: POCSAG1200
```
### Filtering specific capcodes
To only forward decodes from a specific capcode, set the rule's **Match criteria**:
| Field | Operator | Value |
|-------|----------|-------|
| `capcode` | equals | `1234567` |
Multiple rules can coexist — e.g. one rule for all pager traffic to a general Discord channel, and a second rule for emergency capcodes with `high` severity to a separate channel (using a second relay instance on a different port).
## Configuration
INTERCEPT can be configured via environment variables:
+841 -784
View File
File diff suppressed because it is too large Load Diff