mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-17 10:15:27 -04:00
feat(reminders): add generic webhook as a fourth reminder channel (#2952)
Replaces any Discord-specific reminder channel with a generic outbound
webhook channel. Users pick any saved Integration as the target and
supply a JSON payload template with {{title}} and {{message}}
placeholders — values are JSON-escaped before substitution. Works with
Discord, Slack, Teams, ntfy (JSON mode), or any service that accepts a
POST with a JSON body.
- `src/settings.py` — reminder_webhook_integration_id +
reminder_webhook_payload_template defaults
- `routes/note_routes.py` — webhook delivery block; Integration lookup,
template rendering, auth wiring; built-in preset defaults so
discord_webhook works out of the box without a configured template;
settings_override kwarg avoids test-button race condition
- `routes/auth_routes.py` — discord_webhook preset test handler
- `src/integrations.py` — discord_webhook preset with description +
example templates; hides auth/key fields in the Integration form
- `src/builtin_actions.py` — webhook_sent delivery check
- `src/tool_implementations.py` — webhook aliases + enum updated
- `static/index.html` — Webhook channel option; Integration picker +
payload template textarea
- `static/js/settings.js` — Integration list, populateWebhookIntegrations,
syncChannelRows, hints, load/save, auto-fill preset templates,
test-button override payload, hide auth/key for URL-auth presets
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -585,6 +585,27 @@ def setup_auth_routes(auth_manager: AuthManager) -> APIRouter:
|
||||
hint = " If this is Docker Compose ntfy, set NTFY_BIND to that host/Tailscale IP and NTFY_BASE_URL to the same server URL in .env, then recreate ntfy."
|
||||
return {"ok": False, "message": f"ntfy publish to {full_url} failed: {e}.{hint}"[:500]}
|
||||
|
||||
if preset == "discord_webhook":
|
||||
import httpx
|
||||
webhook_url = (integ.get("base_url") or "").strip()
|
||||
if not webhook_url:
|
||||
return {"ok": False, "message": "No webhook URL set — paste the full Discord webhook URL into the Base URL field."}
|
||||
payload = {
|
||||
"embeds": [{
|
||||
"title": "Odysseus connectivity test",
|
||||
"description": "If you see this, your Discord Webhook integration is wired up correctly.",
|
||||
"color": 5793266,
|
||||
}]
|
||||
}
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=8.0) as client:
|
||||
r = await client.post(webhook_url, json=payload)
|
||||
if r.is_success:
|
||||
return {"ok": True, "message": "Test embed sent — check your Discord channel to confirm it arrived."}
|
||||
return {"ok": False, "message": f"Discord returned HTTP {r.status_code}: {r.text[:200]}"}
|
||||
except Exception as e:
|
||||
return {"ok": False, "message": f"Request failed: {e}"[:400]}
|
||||
|
||||
# All other presets: GET against a known health endpoint.
|
||||
# Fall back to detecting from name if preset is missing.
|
||||
health_paths = {
|
||||
|
||||
Reference in New Issue
Block a user