mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
* chore: remove dormant proactive-intelligence agent (superseded by digest) PR #2889 merged a Phase 4 "Proactive Intelligence Agent" in 2026-04 with 588 lines of code and a PR body explicitly requiring a 6h Railway cron service. That service was never provisioned — no Dockerfile, no Railway entry, no health-registry key, all 7 test-plan checkboxes unchecked. In the meantime the daily Intelligence Brief shipped via scripts/seed-digest-notifications.mjs (PR #3321 and earlier), covering the same "personalized editorial brief across all channels" use-case at a different cadence (30m rather than 6h). The proactive agent's landscape-diff trigger was speculative; the digest is the shipped equivalent. This PR retires the dormant code and scrubs the aspirational "post-launch classifier" references that docs + comments have been quietly carrying: - Deleted scripts/proactive-intelligence.mjs (588 lines). - scripts/_energy-disruption-registry.mjs, scripts/seed-fuel-shortages.mjs, scripts/_fuel-shortage-registry.mjs, src/shared/shortage-evidence.ts: dropped "proactive-intelligence.mjs will extend this registry / classifier output" comments. Registries are curated-only; no classifier exists. - docs/methodology/disruptions.mdx: replaced "post-launch classifier" prose with the accurate "curated-only" description of how the event log is maintained. - docs/api-notifications.mdx: envelope version is shared across **two** producers now (notification-relay, seed-digest-notifications), not three. - scripts/notification-relay.cjs: one cross-producer comment updated. - proto/worldmonitor/supply_chain/v1/list_energy_disruptions.proto + list_fuel_shortages.proto: same aspirational wording scrubbed. - docs/api/SupplyChainService.openapi.{yaml,json} auto-regenerated via `make generate` — text-only description updates, no schema changes. Net: -626 lines, +36 lines. No runtime behavior change. 6573/6573 unit tests pass locally. * fix(proto): scrub stale ListFuelShortages RPC comment (PR #3325 review) Reviewer caught a stale "classifier-extended post-launch" comment on the ListFuelShortages RPC method in service.proto that this PR's initial pass missed — I fixed the message-definition comment in list_fuel_shortages.proto but not the RPC-method comment in service.proto, which propagates into the published OpenAPI operation description. - proto/worldmonitor/supply_chain/v1/service.proto: rewrite the ListFuelShortages RPC comment to match the curated-only framing used elsewhere in this PR. - docs/api/SupplyChainService.openapi.{yaml,json}: auto-regenerated via `make generate`. Text-only operation-description update; no schema / contract changes. No runtime impact. Other `classifier` references remaining in the OpenAPI are legitimate schema field names (classifierVersion, classifierConfidence) and an unrelated auto-revision-log trigger enum value, both of which describe real on-row fields that existed before this cleanup.
116 lines
4.7 KiB
Plaintext
116 lines
4.7 KiB
Plaintext
---
|
|
title: "Notifications & Integrations"
|
|
description: "Notification channels, webhook delivery, and Telegram/Slack/Discord/YouTube integration endpoints."
|
|
---
|
|
|
|
## Notification channels
|
|
|
|
Users can register multiple delivery channels (webhook, Telegram, Slack, Discord, email) and bind alert rules to them.
|
|
|
|
### `GET /api/notification-channels`
|
|
|
|
Lists the caller's registered channels and alert rules.
|
|
|
|
```json
|
|
{
|
|
"channels": [
|
|
{ "id": "chn_01", "type": "webhook", "url": "https://hooks.example.com/...", "active": true },
|
|
{ "id": "chn_02", "type": "telegram", "chatId": "@alerts_xyz", "active": true }
|
|
],
|
|
"alertRules": [
|
|
{ "id": "rul_01", "channelId": "chn_01", "trigger": "brief_ready", "filter": null }
|
|
]
|
|
}
|
|
```
|
|
|
|
### `POST /api/notification-channels`
|
|
|
|
Action-dispatched writer. The body's `action` field selects the mutation:
|
|
|
|
| action | Purpose |
|
|
|--------|---------|
|
|
| `create-pairing-token` | Mint a one-time pairing token (optional `variant`) for the mobile / Tauri client to bind a push channel. |
|
|
| `set-channel` | Register or update a channel. For `webhook` channels the `webhookEnvelope` URL is validated HTTPS-only, must not resolve to a private/loopback address, and is AES-256-GCM encrypted before storage. Optional `email`, `webhookLabel` (truncated to 100 chars). |
|
|
| `set-web-push` | Register a browser Web Push subscription for the signed-in user. |
|
|
| `delete-channel` | Remove a channel by type (`email`, `webhook`, `telegram`, `web-push`, etc.). |
|
|
| `set-alert-rules` | Replace the caller's alert-rules set in one shot. |
|
|
| `set-quiet-hours` | Set do-not-disturb windows. |
|
|
| `set-digest-settings` | Configure digest cadence and channel routing. |
|
|
|
|
All actions require Clerk bearer + PRO (`tier >= 1`). Invalid actions return `400 Unknown action`. Requests are forwarded to Convex via `RELAY_SHARED_SECRET`.
|
|
|
|
## Webhook delivery contract
|
|
|
|
When an alert fires, registered webhook URLs receive:
|
|
|
|
- **Method**: `POST`
|
|
- **Headers**:
|
|
- `Content-Type: application/json`
|
|
- `X-WM-Signature: sha256=<HMAC-SHA256(body, channelSecret)>`
|
|
- `X-WM-Delivery-Id: <ulid>`
|
|
- `X-WM-Event: <event-name>`
|
|
- **Body** (envelope v1):
|
|
```json
|
|
{
|
|
"envelope": 1,
|
|
"event": "brief_ready",
|
|
"deliveryId": "01HX...",
|
|
"occurredAt": "2026-04-19T06:00:00Z",
|
|
"data": { "issueDate": "2026-04-19", "magazineUrl": "..." }
|
|
}
|
|
```
|
|
|
|
Signature verification: `hmac_sha256(rawBody, channelSecret) == X-WM-Signature[7:]`.
|
|
|
|
<Warning>
|
|
The envelope version is **shared across two producers** (`notification-relay`, `seed-digest-notifications`). Bumping it requires coordinated updates.
|
|
</Warning>
|
|
|
|
### `POST /api/notify`
|
|
|
|
Internal ingestion endpoint called by Railway producers to enqueue a notification. Requires `RELAY_SHARED_SECRET`. Not a public API.
|
|
|
|
## Telegram
|
|
|
|
### `GET /api/telegram-feed?userId=...`
|
|
|
|
Returns the pre-rendered brief feed for a given Telegram-linked user. Used by the Telegram mini-app.
|
|
|
|
## YouTube
|
|
|
|
### `GET /api/youtube/embed?videoId=...`
|
|
|
|
SSR'd YouTube embed iframe with CSP-compatible wrapping. Used to bypass WKWebView autoplay restrictions on the desktop app.
|
|
|
|
### `GET /api/youtube/live?channel=<handle>` or `?videoId=<11-char-id>`
|
|
|
|
Returns live-stream metadata for a YouTube channel (`channel` — handle with or without `@` prefix) or a specific video (`videoId` — 11-char YouTube id). At least one of the two params is required; returns `400 Missing channel or videoId parameter` otherwise. Response cached 10 min for channel lookups, 1 hour for videoId lookups.
|
|
|
|
Proxies to the Railway relay first (residential proxy for YouTube scraping). On relay failure, falls back to YouTube oEmbed (for `videoId`) or direct channel scraping — both are unreliable from datacenter IPs.
|
|
|
|
## Slack integration
|
|
|
|
### `POST /api/slack/oauth/start`
|
|
|
|
Authenticated (Clerk JWT + PRO). Body is empty. Server generates a one-time CSRF state token, stores the caller's userId in Upstash keyed by that state (10-min TTL), and returns the Slack authorize URL for the frontend to open in a popup.
|
|
|
|
```json
|
|
{ "oauthUrl": "https://slack.com/oauth/v2/authorize?client_id=...&scope=incoming-webhook&..." }
|
|
```
|
|
|
|
Errors: 401 (missing/invalid JWT), 403 `pro_required`, 503 (OAuth not configured or Upstash unavailable).
|
|
|
|
### `GET /api/slack/oauth/callback`
|
|
|
|
Unauthenticated — the popup lands here after Slack redirects. Validates the state token, exchanges `code` for an incoming-webhook URL, AES-256-GCM encrypts the webhook, and stores it in Convex. Returns a tiny HTML page that `postMessage`s the opener and closes.
|
|
|
|
## Discord integration
|
|
|
|
### `POST /api/discord/oauth/start`
|
|
|
|
Authenticated (Clerk JWT + PRO). Same shape as the Slack start route — returns `{ oauthUrl }` for a popup.
|
|
|
|
### `GET /api/discord/oauth/callback`
|
|
|
|
Unauthenticated. Exchanges `code`, stores the guild webhook, and `postMessage`s the opener.
|