From ebddf4506aa19f58266afb73cab4cb3a859f0734 Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Tue, 2 Jun 2026 00:25:49 +0530 Subject: [PATCH] feat(messaging): introduce comprehensive setup docs for Telegram, WhatsApp, Slack, and Discord messaging channels --- docker/.env.example | 32 +++---- .../docker-installation/docker-compose.mdx | 21 ++++- surfsense_web/content/docs/index.mdx | 8 +- .../content/docs/manual-installation.mdx | 42 ++-------- .../docs/messaging-channels/discord.mdx | 76 +++++++++++++++++ .../docs/messaging-channels/docker.mdx | 60 +++++++++++++ .../content/docs/messaging-channels/index.mdx | 42 ++++++++++ .../content/docs/messaging-channels/meta.json | 13 +++ .../content/docs/messaging-channels/slack.mdx | 84 +++++++++++++++++++ .../docs/messaging-channels/telegram.mdx | 62 ++++++++++++++ .../messaging-channels/troubleshooting.mdx | 66 +++++++++++++++ .../docs/messaging-channels/whatsapp.mdx | 75 +++++++++++++++++ surfsense_web/content/docs/meta.json | 1 + surfsense_web/lib/source.ts | 2 + 14 files changed, 530 insertions(+), 54 deletions(-) create mode 100644 surfsense_web/content/docs/messaging-channels/discord.mdx create mode 100644 surfsense_web/content/docs/messaging-channels/docker.mdx create mode 100644 surfsense_web/content/docs/messaging-channels/index.mdx create mode 100644 surfsense_web/content/docs/messaging-channels/meta.json create mode 100644 surfsense_web/content/docs/messaging-channels/slack.mdx create mode 100644 surfsense_web/content/docs/messaging-channels/telegram.mdx create mode 100644 surfsense_web/content/docs/messaging-channels/troubleshooting.mdx create mode 100644 surfsense_web/content/docs/messaging-channels/whatsapp.mdx diff --git a/docker/.env.example b/docker/.env.example index 96152c129..12c5dcc55 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -70,7 +70,7 @@ EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2 # ------------------------------------------------------------------------------ # ONLY set these if you are serving SurfSense on a real domain via a reverse # proxy (e.g. Caddy, Nginx, Cloudflare Tunnel). -# For standard localhost deployments, leave all of these commented out — +# For standard localhost deployments, leave all of these commented out. # they are automatically derived from the port settings above. # # NEXT_FRONTEND_URL=https://app.yourdomain.com @@ -92,7 +92,7 @@ EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2 # Only change this if you manage publications manually. # ZERO_APP_PUBLICATIONS=zero_publication -# Sync worker tuning — zero-cache defaults ZERO_NUM_SYNC_WORKERS to the number +# Sync worker tuning. zero-cache defaults ZERO_NUM_SYNC_WORKERS to the number # of CPU cores, which can exceed the connection pool limits on high-core machines. # Each sync worker needs at least 1 connection from both the UPSTREAM and CVR # pools, so these constraints must hold: @@ -137,7 +137,7 @@ EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2 # SSL mode for database connections: disable, require, verify-ca, verify-full # DB_SSLMODE=disable -# Full DATABASE_URL override — when set, takes precedence over the individual +# Full DATABASE_URL override. When set, this takes precedence over the individual # DB_USER / DB_PASSWORD / DB_NAME / DB_HOST / DB_PORT settings above. # Use this for managed databases (AWS RDS, GCP Cloud SQL, Supabase, etc.) # DATABASE_URL=postgresql+asyncpg://user:password@your-rds-host:5432/surfsense?sslmode=require @@ -152,7 +152,7 @@ EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2 # REDIS_URL=redis://redis:6379/0 # ------------------------------------------------------------------------------ -# Stripe (pay-as-you-go page packs — disabled by default) +# Stripe (pay-as-you-go page packs, disabled by default) # ------------------------------------------------------------------------------ # Set TRUE to allow users to buy additional page packs via Stripe Checkout @@ -171,7 +171,7 @@ STRIPE_PAGE_BUYING_ENABLED=FALSE # STRIPE_TOKEN_BUYING_ENABLED=FALSE # STRIPE_PREMIUM_TOKEN_PRICE_ID=price_... # STRIPE_CREDIT_MICROS_PER_UNIT=1000000 -# DEPRECATED — STRIPE_TOKENS_PER_UNIT=1000000 +# DEPRECATED: STRIPE_TOKENS_PER_UNIT=1000000 # ------------------------------------------------------------------------------ # TTS & STT (Text-to-Speech / Speech-to-Text) @@ -266,18 +266,18 @@ STT_SERVICE=local/base # COMPOSIO_REDIRECT_URI=http://localhost:8000/api/v1/auth/composio/connector/callback # ------------------------------------------------------------------------------ -# Messaging Gateways (optional) +# Messaging Channels (optional) # ------------------------------------------------------------------------------ -# Configure only the gateways you want to use. +# Configure only the external chat channels you want to use. -# -- Telegram Gateway -- +# -- Telegram -- # TELEGRAM_SHARED_BOT_TOKEN= # TELEGRAM_SHARED_BOT_USERNAME= # TELEGRAM_WEBHOOK_SECRET= # GATEWAY_BASE_URL=http://localhost:8929 # GATEWAY_TELEGRAM_INTAKE_MODE=webhook -# -- WhatsApp Gateway -- +# -- WhatsApp -- # GATEWAY_WHATSAPP_INTAKE_MODE=disabled # WHATSAPP_SHARED_BUSINESS_TOKEN= # WHATSAPP_SHARED_PHONE_NUMBER_ID= @@ -288,14 +288,14 @@ STT_SERVICE=local/base # WHATSAPP_WEBHOOK_APP_SECRET= # WHATSAPP_BRIDGE_URL=http://whatsapp-bridge:9929 -# -- Slack Gateway -- +# -- Slack -- # Uses SLACK_CLIENT_ID and SLACK_CLIENT_SECRET from the Slack connector section. # # GATEWAY_SLACK_ENABLED=FALSE # GATEWAY_SLACK_SIGNING_SECRET= # GATEWAY_SLACK_REDIRECT_URI=http://localhost:8929/api/v1/gateway/slack/callback -# -- Discord Gateway -- +# -- Discord -- # Uses DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, and DISCORD_BOT_TOKEN from the # Discord connector section. # @@ -303,7 +303,7 @@ STT_SERVICE=local/base # GATEWAY_DISCORD_REDIRECT_URI=http://localhost:8929/api/v1/gateway/discord/callback # ------------------------------------------------------------------------------ -# SearXNG (bundled web search — works out of the box, no config needed) +# SearXNG (bundled web search, works out of the box with no config needed) # ------------------------------------------------------------------------------ # SearXNG provides web search to all search spaces automatically. # To access the SearXNG UI directly: http://localhost:8888 @@ -313,7 +313,7 @@ STT_SERVICE=local/base # SEARXNG_SECRET=surfsense-searxng-secret # ------------------------------------------------------------------------------ -# Daytona Sandbox (optional — cloud code execution for the deep agent) +# Daytona Sandbox (optional cloud code execution for the deep agent) # ------------------------------------------------------------------------------ # Set DAYTONA_SANDBOX_ENABLED=TRUE and provide credentials to give the agent # an isolated code execution environment via the Daytona cloud API. @@ -404,7 +404,7 @@ SURFSENSE_ENABLE_DOOM_LOOP=true # Premium turns are debited at the actual per-call provider cost reported # by LiteLLM. Only applies to models with billing_tier=premium. # PREMIUM_CREDIT_MICROS_LIMIT=5000000 -# DEPRECATED — PREMIUM_TOKEN_LIMIT=5000000 +# DEPRECATED: PREMIUM_TOKEN_LIMIT=5000000 # Safety ceiling on per-call premium reservation, in micro-USD ($1.00 default). # QUOTA_MAX_RESERVE_MICROS=1000000 @@ -416,10 +416,10 @@ SURFSENSE_ENABLE_DOOM_LOOP=true # QUOTA_DEFAULT_PODCAST_RESERVE_MICROS=200000 # Per-video-presentation reservation for the video Celery task ($1.00 default). -# Override path bypasses QUOTA_MAX_RESERVE_MICROS clamp — raise with care. +# Override path bypasses QUOTA_MAX_RESERVE_MICROS clamp. Raise with care. # QUOTA_DEFAULT_VIDEO_PRESENTATION_RESERVE_MICROS=1000000 -# No-login (anonymous) mode — public users can chat without an account +# No-login (anonymous) mode. Public users can chat without an account # Set TRUE to enable /free pages and anonymous chat API NOLOGIN_MODE_ENABLED=FALSE # ANON_TOKEN_LIMIT=1000000 diff --git a/surfsense_web/content/docs/docker-installation/docker-compose.mdx b/surfsense_web/content/docs/docker-installation/docker-compose.mdx index 0155969cd..488f4e24a 100644 --- a/surfsense_web/content/docs/docker-installation/docker-compose.mdx +++ b/surfsense_web/content/docs/docker-installation/docker-compose.mdx @@ -124,6 +124,19 @@ Uncomment the connectors you want to use. Redirect URIs follow the pattern `http | Microsoft (Teams & OneDrive) | `MICROSOFT_CLIENT_ID`, `MICROSOFT_CLIENT_SECRET`, `TEAMS_REDIRECT_URI`, `ONEDRIVE_REDIRECT_URI` | | Dropbox | `DROPBOX_APP_KEY`, `DROPBOX_APP_SECRET`, `DROPBOX_REDIRECT_URI` | +### Messaging Channels + +Configure these in the same `docker/.env` file when you want users to chat with +SurfSense from external apps. See [Messaging Channels](/docs/messaging-channels) +for full setup. + +| Channel | Variables | +|---------|-----------| +| Telegram | `TELEGRAM_SHARED_BOT_TOKEN`, `TELEGRAM_SHARED_BOT_USERNAME`, `TELEGRAM_WEBHOOK_SECRET`, `GATEWAY_BASE_URL`, `GATEWAY_TELEGRAM_INTAKE_MODE` | +| WhatsApp | `GATEWAY_WHATSAPP_INTAKE_MODE`, `WHATSAPP_SHARED_BUSINESS_TOKEN`, `WHATSAPP_SHARED_PHONE_NUMBER_ID`, `WHATSAPP_SHARED_DISPLAY_PHONE_NUMBER`, `WHATSAPP_SHARED_WABA_ID`, `WHATSAPP_WEBHOOK_VERIFY_TOKEN`, `WHATSAPP_WEBHOOK_APP_SECRET` | +| Slack | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET`, `GATEWAY_SLACK_ENABLED`, `GATEWAY_SLACK_SIGNING_SECRET`, `GATEWAY_SLACK_REDIRECT_URI` | +| Discord | `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET`, `DISCORD_BOT_TOKEN`, `GATEWAY_DISCORD_ENABLED`, `GATEWAY_DISCORD_REDIRECT_URI` | + ### Observability (optional) | Variable | Description | @@ -187,9 +200,9 @@ Postgres. Before this design, a silent migration failure would leave The backend exposes two endpoints: -- `GET /health` — lightweight liveness probe (always returns 200 if the +- `GET /health`: lightweight liveness probe (always returns 200 if the process is up). -- `GET /ready` — readiness probe that confirms `zero_publication` exists. +- `GET /ready`: readiness probe that confirms `zero_publication` exists. Returns 503 if not. The compose `backend.healthcheck` uses `/ready` so the container only reports `healthy` once the schema is actually usable by zero-cache. @@ -247,7 +260,7 @@ docker compose exec db psql -U surfsense -d surfsense \ ``` The default migration timeout is 900 seconds. Slow disks (Windows / WSL2) -may need more — set `MIGRATION_TIMEOUT` in `.env` to increase it. +may need more. Set `MIGRATION_TIMEOUT` in `.env` to increase it. ### Zero-cache stuck on `Unknown or invalid publications` @@ -258,7 +271,7 @@ Error: Unknown or invalid publications. Specified: [zero_publication]. Found: [] ``` This means `zero-cache` started before `zero_publication` was created. With -the current compose files this should be impossible — the `migrations` +the current compose files this should be impossible. The `migrations` service blocks `zero-cache` from starting. If you see it, your stack predates the fix or you brought up `zero-cache` manually with `docker compose up zero-cache` before the migrations service ran. diff --git a/surfsense_web/content/docs/index.mdx b/surfsense_web/content/docs/index.mdx index 2204e4e34..4a321b376 100644 --- a/surfsense_web/content/docs/index.mdx +++ b/surfsense_web/content/docs/index.mdx @@ -5,7 +5,7 @@ icon: BookOpen --- import { Card, Cards } from 'fumadocs-ui/components/card'; -import { ClipboardCheck, Download, Container, Wrench, Cable, BookOpen, FlaskConical, Heart } from 'lucide-react'; +import { ClipboardCheck, Download, Container, Wrench, Cable, BookOpen, FlaskConical, Heart, MessageCircle } from 'lucide-react'; Welcome to **SurfSense's Documentation!** Here, you'll find everything you need to get the most out of SurfSense. Dive in to explore how SurfSense can be your AI-powered research companion. @@ -40,6 +40,12 @@ Welcome to **SurfSense's Documentation!** Here, you'll find everything you need description="Integrate with third-party services" href="/docs/connectors" /> + } + title="Messaging Channels" + description="Chat with SurfSense from Telegram, WhatsApp, Slack, and Discord" + href="/docs/messaging-channels" + /> } title="How-To Guides" diff --git a/surfsense_web/content/docs/manual-installation.mdx b/surfsense_web/content/docs/manual-installation.mdx index 602c9a30b..203c244c0 100644 --- a/surfsense_web/content/docs/manual-installation.mdx +++ b/surfsense_web/content/docs/manual-installation.mdx @@ -39,38 +39,14 @@ Complete all the [setup steps](/docs), including: The backend is the core of SurfSense. Follow these steps to set it up: -### Optional: Telegram External Chat Surface +### Optional: Messaging Channels -SurfSense can expose the same canonical chat agent through Telegram. The `external_chat_*` tables store adapter identity, delivery configuration, and durable inbox rows. The actual chat thread and messages remain in `new_chat_threads` and `new_chat_messages`, with first-party web/desktop chats marked as `source="surfsense"` and external surfaces marked by platform, such as `source="telegram"`. All chat-message sources are eligible for Zero replication so a future SurfSense UI layer can render external chat surfaces. The web app initially shows pairing, health, revoke, and resume controls under **User Settings > Messaging Channels**. +SurfSense can expose the same backend agent through Telegram, WhatsApp, Slack, +and Discord. For manual installs, configure the relevant channel variables in +`surfsense_backend/.env`. -Add these variables to `surfsense_backend/.env` when enabling the Telegram surface: - -```bash -TELEGRAM_SHARED_BOT_TOKEN=123456:bot-token-from-botfather -TELEGRAM_SHARED_BOT_USERNAME=your_bot_username -TELEGRAM_WEBHOOK_SECRET=generate-a-long-random-secret -GATEWAY_BASE_URL=https://api.example.com -GATEWAY_TELEGRAM_INTAKE_MODE=webhook -``` - -`GATEWAY_TELEGRAM_INTAKE_MODE` must be `webhook`, `longpoll`, or `disabled`. Use `webhook` for production/SaaS deployments, `longpoll` only for single-replica self-host installs that cannot expose a public HTTPS webhook, and `disabled` to skip Telegram intake. `TELEGRAM_WEBHOOK_SECRET` must use only `A-Z`, `a-z`, `0-9`, `_`, and `-` characters. `REDIS_APP_URL` is reused for external chat rate limits and per-thread locks. The webhook URL shape is: - -```text -${GATEWAY_BASE_URL}/api/v1/gateway/webhooks/telegram/{account_id} -``` - -After deploying the backend, register the webhook: - -```bash -cd surfsense_backend -uv run python scripts/register_webhook.py -``` - -Keep the FastAPI backend, Celery worker, and Celery beat running. Telegram webhooks write inbound updates into `external_chat_inbound_events`. The FastAPI process owns external chat inbox processing and runs the same SurfSense agent used by web UI chats, then replies back to Telegram. Celery remains maintenance-only for external chat reconciliation, health checks, and retention sweeps. There is no separate gateway service, `SERVICE_ROLE=gateway` process, or Celery agent-processing path. - -For self-hosted BYO Telegram bots without a public HTTPS URL, set `GATEWAY_TELEGRAM_INTAKE_MODE=longpoll`. The FastAPI process starts one lifespan long-poll supervisor per non-system Telegram account and writes updates into the same durable inbox. The FastAPI inbox worker then processes those rows in-process through the canonical `new_chat_*` surface. This fallback is intended for single-replica self-hosted installs. For SaaS-style multi-replica deployments, prefer public webhooks and keep `GATEWAY_TELEGRAM_INTAKE_MODE=webhook` so API replicas skip BYO polling entirely. Telegram does not allow `get_updates()` while a webhook is active, so delete any existing webhook for a BYO bot before relying on long polling. - -When upgrading from an older gateway-runner deployment, apply the rewritten migration 144 external chat schema, deploy the new backend, worker, and beat images, then stop the old `gateway` service. Wait about 30 seconds for any old Telegram `getUpdates` long-poll request to release its advisory lock before starting the new API process. Register each webhook again with the account-id URL above and the per-account `webhook_secret`. If you roll back before using the migration in production, restore the old image and downgrade the schema first. +See [Messaging Channels](/docs/messaging-channels) for the channel-specific +setup guides. ### 1. Environment Configuration @@ -490,7 +466,7 @@ If everything is set up correctly, you should see output indicating the server i ## Zero-Cache Setup -**zero-cache** is the Rocicorp Zero server that sits between PostgreSQL and the browser. It streams real-time updates (notifications, document indexing status, chat comments, collaboration indicators) to all connected clients via WebSocket. The frontend connects to it on startup — without zero-cache running, you will not see live updates and many parts of the UI will sit on stale data. +**zero-cache** is the Rocicorp Zero server that sits between PostgreSQL and the browser. It streams real-time updates (notifications, document indexing status, chat comments, collaboration indicators) to all connected clients via WebSocket. The frontend connects to it on startup. Without zero-cache running, you will not see live updates and many parts of the UI will sit on stale data. For an overview of how Zero works and the list of synced tables, see the [Real-Time Sync with Zero](/docs/how-to/zero-sync) guide. @@ -572,7 +548,7 @@ cd ../docker docker compose -f docker-compose.deps-only.yml up -d ``` -The deps-only stack exposes zero-cache on port `4848` (default) — keep `NEXT_PUBLIC_ZERO_CACHE_URL=http://localhost:4848` in your `surfsense_web/.env`. +The deps-only stack exposes zero-cache on port `4848` by default. Keep `NEXT_PUBLIC_ZERO_CACHE_URL=http://localhost:4848` in your `surfsense_web/.env`. ## Frontend Setup @@ -708,7 +684,7 @@ To verify your installation: 1. Open your browser and navigate to `http://localhost:3000` 2. Sign in with your Google account (or local credentials if `AUTH_TYPE=LOCAL`) 3. Create a search space and try uploading a document -4. Watch the upload status update live without refreshing — this confirms zero-cache is wired up correctly +4. Watch the upload status update live without refreshing. This confirms zero-cache is wired up correctly 5. Test the chat functionality with your uploaded content ## Troubleshooting diff --git a/surfsense_web/content/docs/messaging-channels/discord.mdx b/surfsense_web/content/docs/messaging-channels/discord.mdx new file mode 100644 index 000000000..c0874dfe3 --- /dev/null +++ b/surfsense_web/content/docs/messaging-channels/discord.mdx @@ -0,0 +1,76 @@ +--- +title: Discord +description: Enable the SurfSense bot for in-Discord agent chat +--- + +# Discord Messaging Channel + +The Discord messaging channel lets users mention the SurfSense bot in Discord +and chat with the SurfSense backend agent from a Discord channel. + +This is separate from the Discord connector. The messaging channel handles bot +mentions and replies; the connector gives the agent Discord channel/message read +tools. + +## Discord Application Settings + +Create or reuse a Discord application in the +[Discord Developer Portal](https://discord.com/developers/applications). + +In **OAuth2 > Redirects**, add both callback URLs if the same application powers +the connector and messaging channel: + +```bash +https://your-backend.example.com/api/v1/auth/discord/connector/callback +https://your-backend.example.com/api/v1/gateway/discord/callback +``` + +For local OAuth testing, replace the host with your local or public tunnel URL, +and make sure `DISCORD_REDIRECT_URI` and `GATEWAY_DISCORD_REDIRECT_URI` match +the Discord dashboard exactly. + +## Bot Permissions And Intents + +In **Bot > Privileged Gateway Intents**, enable: + +- **Message Content Intent** so SurfSense can read text after a bot mention. + +When installing the bot, grant: + +- View Channels +- Send Messages +- Send Messages in Threads +- Read Message History + +## Environment Variables + +For Docker installs, add these to `docker/.env`. For manual installs, add them to +`surfsense_backend/.env`. + +```bash +DISCORD_CLIENT_ID=your_discord_client_id +DISCORD_CLIENT_SECRET=your_discord_client_secret +DISCORD_BOT_TOKEN=your_discord_bot_token +GATEWAY_DISCORD_ENABLED=TRUE +GATEWAY_DISCORD_REDIRECT_URI=https://your-backend.example.com/api/v1/gateway/discord/callback +``` + +The messaging channel uses the same Discord app credentials as the Discord +connector. `DISCORD_REDIRECT_URI` remains the connector callback; +`GATEWAY_DISCORD_REDIRECT_URI` is the separate messaging channel install +callback. + +## Runtime Behavior + +1. Discord sends a `MESSAGE_CREATE` event over its WebSocket API. +2. SurfSense stores the event in the durable gateway inbox. +3. SurfSense resolves the Discord user binding to a SurfSense user and search space. +4. SurfSense runs the backend agent with that user's permissions. +5. The agent reply is posted back to the same Discord channel. + +## Deployment Note + +Only one running backend process should connect to Discord with a +given bot token. For multi-replica deployments, enable +`GATEWAY_DISCORD_ENABLED=TRUE` in a single backend process and leave it disabled +in other API replicas. diff --git a/surfsense_web/content/docs/messaging-channels/docker.mdx b/surfsense_web/content/docs/messaging-channels/docker.mdx new file mode 100644 index 000000000..3a4d4177f --- /dev/null +++ b/surfsense_web/content/docs/messaging-channels/docker.mdx @@ -0,0 +1,60 @@ +--- +title: Docker Setup +description: Configure messaging channels for Docker and one-line installs +--- + +# Docker Setup + +For Docker and one-line installs, configure messaging channels in the generated +`docker/.env` file. You do not need to edit `surfsense_backend/.env.example`. + +The Compose stack passes `docker/.env` into the backend, worker, and beat +containers. Database, Redis, SearXNG, and internal Docker networking are already +wired by Compose. + +## Public URLs + +For localhost-only testing, the defaults are enough for the SurfSense UI, but +public webhooks from Telegram, WhatsApp, and Slack require a public HTTPS backend +URL. Use your deployed backend URL or a tunnel such as Cloudflare Tunnel or +ngrok. + +When using a custom domain or tunnel, set: + +```bash +BACKEND_URL=https://api.example.com +GATEWAY_BASE_URL=https://api.example.com +NEXT_FRONTEND_URL=https://app.example.com +NEXT_PUBLIC_FASTAPI_BACKEND_URL=https://api.example.com +``` + +## Environment Variables + +Uncomment only the channel you are enabling in `docker/.env`. + +| Channel | Main variables | +| --- | --- | +| Telegram | `TELEGRAM_SHARED_BOT_TOKEN`, `TELEGRAM_SHARED_BOT_USERNAME`, `TELEGRAM_WEBHOOK_SECRET`, `GATEWAY_BASE_URL`, `GATEWAY_TELEGRAM_INTAKE_MODE` | +| WhatsApp Cloud API | `GATEWAY_WHATSAPP_INTAKE_MODE`, `WHATSAPP_SHARED_BUSINESS_TOKEN`, `WHATSAPP_SHARED_PHONE_NUMBER_ID`, `WHATSAPP_SHARED_DISPLAY_PHONE_NUMBER`, `WHATSAPP_SHARED_WABA_ID`, `WHATSAPP_WEBHOOK_VERIFY_TOKEN`, `WHATSAPP_WEBHOOK_APP_SECRET` | +| WhatsApp Baileys | `GATEWAY_WHATSAPP_INTAKE_MODE`, `WHATSAPP_BRIDGE_URL`, `WHATSAPP_MODE` | +| Slack | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET`, `GATEWAY_SLACK_ENABLED`, `GATEWAY_SLACK_SIGNING_SECRET`, `GATEWAY_SLACK_REDIRECT_URI` | +| Discord | `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET`, `DISCORD_BOT_TOKEN`, `GATEWAY_DISCORD_ENABLED`, `GATEWAY_DISCORD_REDIRECT_URI` | + +After editing `docker/.env`, restart the stack: + +```bash +docker compose up -d +``` + +For WhatsApp Baileys, start the Compose profile: + +```bash +docker compose --profile whatsapp up -d +``` + +Then follow the per-channel setup pages: + +- [Telegram](/docs/messaging-channels/telegram) +- [WhatsApp](/docs/messaging-channels/whatsapp) +- [Slack](/docs/messaging-channels/slack) +- [Discord](/docs/messaging-channels/discord) diff --git a/surfsense_web/content/docs/messaging-channels/index.mdx b/surfsense_web/content/docs/messaging-channels/index.mdx new file mode 100644 index 000000000..d15dc0e6e --- /dev/null +++ b/surfsense_web/content/docs/messaging-channels/index.mdx @@ -0,0 +1,42 @@ +--- +title: Messaging Channels +description: Chat with SurfSense from Telegram, WhatsApp, Slack, and Discord +--- + +import { Card, Cards } from 'fumadocs-ui/components/card'; + +Choose the external chat app you want to connect to SurfSense. Each guide shows +the required app setup, environment variables, and pairing flow. + + + + + + + + + diff --git a/surfsense_web/content/docs/messaging-channels/meta.json b/surfsense_web/content/docs/messaging-channels/meta.json new file mode 100644 index 000000000..00647bdb0 --- /dev/null +++ b/surfsense_web/content/docs/messaging-channels/meta.json @@ -0,0 +1,13 @@ +{ + "title": "Messaging Channels", + "icon": "MessageCircle", + "pages": [ + "telegram", + "whatsapp", + "slack", + "discord", + "docker", + "troubleshooting" + ], + "defaultOpen": false +} diff --git a/surfsense_web/content/docs/messaging-channels/slack.mdx b/surfsense_web/content/docs/messaging-channels/slack.mdx new file mode 100644 index 000000000..4e001d13a --- /dev/null +++ b/surfsense_web/content/docs/messaging-channels/slack.mdx @@ -0,0 +1,84 @@ +--- +title: Slack +description: Enable the SurfSense bot for in-Slack agent chat +--- + +# Slack Messaging Channel + +The Slack messaging channel lets users mention the SurfSense bot in Slack and +chat with the SurfSense backend agent from a Slack thread. + +This is separate from the Slack connector. The messaging channel handles bot +mentions and replies; the connector gives the agent Slack search/read tools. + +## Required Slack App Scopes + +Add these **Bot Token Scopes** in Slack OAuth & Permissions: + +| Scope | Purpose | +| --- | --- | +| `app_mentions:read` | Receive bot mention events | +| `chat:write` | Reply in Slack threads | +| `channels:read` | Read public channel metadata | +| `groups:read` | Read private channel metadata where the bot is present | +| `im:write` | Send onboarding or direct replies | +| `users:read` | Resolve Slack users | +| `team:read` | Resolve workspace metadata | + +Optional scopes: + +- `im:history` if you support direct message chat with the bot. +- `commands` if you add slash commands. + +Avoid `channels:history` and `groups:history` for the messaging channel unless +you specifically need gateway-side context reads. Slack workspace search should +stay with the Slack connector. + +## Event Subscriptions + +Enable Slack Events API and subscribe to: + +- `app_mention` + +Set the request URL to: + +```bash +https://your-backend.example.com/api/v1/gateway/webhooks/slack +``` + +Slack must be able to reach this URL. Do not use `localhost` for event +subscriptions. + +## OAuth Redirect URLs + +If the same Slack app powers both the connector and messaging channel, add both +redirect URLs in **OAuth & Permissions**: + +```bash +https://your-backend.example.com/api/v1/auth/slack/connector/callback +https://your-backend.example.com/api/v1/gateway/slack/callback +``` + +## Environment Variables + +For Docker installs, add these to `docker/.env`. For manual installs, add them to +`surfsense_backend/.env`. + +```bash +SLACK_CLIENT_ID=your_slack_client_id +SLACK_CLIENT_SECRET=your_slack_client_secret +GATEWAY_SLACK_ENABLED=TRUE +GATEWAY_SLACK_SIGNING_SECRET=your_slack_signing_secret +GATEWAY_SLACK_REDIRECT_URI=https://your-backend.example.com/api/v1/gateway/slack/callback +``` + +After changing Slack scopes, redirect URLs, or event subscriptions, reinstall +the Slack app to your workspace so Slack grants the updated permissions. + +## Runtime Behavior + +1. Slack sends an `app_mention` event to SurfSense. +2. SurfSense verifies the Slack signature and stores the event in the gateway inbox. +3. SurfSense resolves the Slack user binding to a SurfSense user and search space. +4. SurfSense runs the backend agent with that user's permissions. +5. The agent reply is posted back in the same Slack thread. diff --git a/surfsense_web/content/docs/messaging-channels/telegram.mdx b/surfsense_web/content/docs/messaging-channels/telegram.mdx new file mode 100644 index 000000000..3487da864 --- /dev/null +++ b/surfsense_web/content/docs/messaging-channels/telegram.mdx @@ -0,0 +1,62 @@ +--- +title: Telegram +description: Enable SurfSense chat from Telegram +--- + +# Telegram Messaging Channel + +Telegram lets users chat with the SurfSense agent from a Telegram bot. Users pair +their Telegram chat from **User Settings > Messaging Channels**. + +## Environment Variables + +For Docker installs, add these to `docker/.env`. For manual installs, add them to +`surfsense_backend/.env`. + +```bash +TELEGRAM_SHARED_BOT_TOKEN=123456:bot-token-from-botfather +TELEGRAM_SHARED_BOT_USERNAME=your_bot_username +TELEGRAM_WEBHOOK_SECRET=generate-a-long-random-secret +GATEWAY_BASE_URL=https://api.example.com +GATEWAY_TELEGRAM_INTAKE_MODE=webhook +``` + +`TELEGRAM_WEBHOOK_SECRET` must be 1-256 characters and contain only `A-Z`, `a-z`, +`0-9`, `_`, or `-`. + +## Intake Modes + +| Mode | Use when | +| --- | --- | +| `webhook` | Production or any deployment with a public HTTPS backend URL | +| `longpoll` | Single-replica self-host installs that cannot expose a public HTTPS webhook | +| `disabled` | You do not want Telegram intake enabled | + +For SaaS-style or multi-replica deployments, use `webhook`. Long polling should +only run in a single backend process. + +## Webhook URL + +Telegram webhooks use this shape: + +```text +${GATEWAY_BASE_URL}/api/v1/gateway/webhooks/telegram/{account_id} +``` + +After deploying the backend, register the webhook: + +```bash +cd surfsense_backend +uv run python scripts/register_webhook.py +``` + +If switching a bot from long polling to webhooks, delete any existing Telegram +webhook or pending `getUpdates` session before relying on the new mode. + +## Pairing Flow + +1. The user opens **User Settings > Messaging Channels**. +2. The user starts Telegram pairing. +3. SurfSense provides a pairing code or bot link. +4. The user sends the pairing command to the Telegram bot. +5. SurfSense binds that Telegram chat to the selected search space. diff --git a/surfsense_web/content/docs/messaging-channels/troubleshooting.mdx b/surfsense_web/content/docs/messaging-channels/troubleshooting.mdx new file mode 100644 index 000000000..bdd385e28 --- /dev/null +++ b/surfsense_web/content/docs/messaging-channels/troubleshooting.mdx @@ -0,0 +1,66 @@ +--- +title: Troubleshooting +description: Common messaging channel pairing, webhook, and bot reply issues +--- + +# Messaging Channels Troubleshooting + +## The Bot Does Not Reply + +Check that: + +- The channel is enabled in the backend environment. +- The backend restarted after the environment change. +- The external platform can reach your public HTTPS backend URL. +- The user paired the channel from **User Settings > Messaging Channels**. +- Redis is running, because gateway inbox processing uses backend coordination + and rate-limit state. + +## Telegram + +Check that: + +- `TELEGRAM_SHARED_BOT_TOKEN` and `TELEGRAM_SHARED_BOT_USERNAME` are correct. +- `GATEWAY_TELEGRAM_INTAKE_MODE` is one of `webhook`, `longpoll`, or `disabled`. +- `TELEGRAM_WEBHOOK_SECRET` contains only `A-Z`, `a-z`, `0-9`, `_`, or `-`. +- Webhook mode uses a public HTTPS `GATEWAY_BASE_URL`. +- Long polling runs in only one backend process. + +## WhatsApp + +For Meta Cloud API, check that: + +- `GATEWAY_WHATSAPP_INTAKE_MODE=cloud`. +- The Meta webhook URL is `${GATEWAY_BASE_URL}/api/v1/gateway/webhooks/whatsapp`. +- The Meta verify token matches `WHATSAPP_WEBHOOK_VERIFY_TOKEN`. +- `WHATSAPP_SHARED_DISPLAY_PHONE_NUMBER` contains the public WhatsApp number + users should message. + +For Baileys, check that: + +- `GATEWAY_WHATSAPP_INTAKE_MODE=baileys`. +- The `whatsapp` Compose profile is running. +- The bridge is paired and healthy. +- You are messaging the account's Message Yourself chat. + +## Slack + +Check that: + +- `GATEWAY_SLACK_ENABLED=TRUE`. +- The Slack signing secret matches `GATEWAY_SLACK_SIGNING_SECRET`. +- Slack Events API is enabled and subscribed to `app_mention`. +- The Slack event request URL is public HTTPS and points to + `/api/v1/gateway/webhooks/slack`. +- The Slack app was reinstalled after scope or redirect URL changes. + +## Discord + +Check that: + +- `GATEWAY_DISCORD_ENABLED=TRUE`. +- The bot token is valid. +- Message Content Intent is enabled. +- The bot can view and send messages in the channel. +- Exactly one backend process is running the Discord listener. +- The Discord user is paired to a SurfSense user and search space. diff --git a/surfsense_web/content/docs/messaging-channels/whatsapp.mdx b/surfsense_web/content/docs/messaging-channels/whatsapp.mdx new file mode 100644 index 000000000..56015fd20 --- /dev/null +++ b/surfsense_web/content/docs/messaging-channels/whatsapp.mdx @@ -0,0 +1,75 @@ +--- +title: WhatsApp +description: Enable SurfSense chat from WhatsApp +--- + +# WhatsApp Messaging Channel + +WhatsApp supports two intake modes: + +- `cloud` uses the official Meta WhatsApp Cloud API with a SurfSense-owned system + WhatsApp Business Account. +- `baileys` uses the unofficial Baileys WebSocket bridge for self-hosted, + one-tenant Message Yourself installs. + +Use `cloud` for production and shared deployments. + +## Meta Cloud API + +Create a Meta app, provision a WhatsApp Business Account and phone number, and +create a long-lived system user token with WhatsApp permissions. + +Point the Meta app webhook to: + +```text +${GATEWAY_BASE_URL}/api/v1/gateway/webhooks/whatsapp +``` + +Set the webhook verify token in Meta to the same value as +`WHATSAPP_WEBHOOK_VERIFY_TOKEN`. + +For Docker installs, add these to `docker/.env`. For manual installs, add them to +`surfsense_backend/.env`. + +```bash +GATEWAY_WHATSAPP_INTAKE_MODE=cloud +WHATSAPP_SHARED_BUSINESS_TOKEN=your-system-user-token +WHATSAPP_SHARED_PHONE_NUMBER_ID=your-meta-phone-number-id +WHATSAPP_SHARED_DISPLAY_PHONE_NUMBER=15551234567 +WHATSAPP_SHARED_WABA_ID=your-waba-id +WHATSAPP_GRAPH_API_VERSION=v25.0 +WHATSAPP_WEBHOOK_VERIFY_TOKEN=generate-a-long-random-secret +WHATSAPP_WEBHOOK_APP_SECRET=your-meta-app-secret +``` + +Users open **User Settings > Messaging Channels**, click **Pair WhatsApp**, and +open the returned `wa.me` link. WhatsApp pre-fills `/start CODE`; the user must +press send to bind the chat. + +## Baileys Self-Hosted Mode + +Baileys is unofficial. Use it only for single-tenant self-hosted installs where +the operator accepts the risk of a personal WhatsApp session bridge. + +```bash +GATEWAY_WHATSAPP_INTAKE_MODE=baileys +WHATSAPP_BRIDGE_URL=http://whatsapp-bridge:9929 +WHATSAPP_MODE=self-chat +docker compose --profile whatsapp up -d +``` + +After pairing, use WhatsApp's Message Yourself chat. The bridge only forwards +messages from your own self-chat and ignores groups, other chats, and other +people. + +The `whatsapp-bridge` container stores Baileys auth state in the +`surfsense-whatsapp-sessions` Docker volume. That volume contains account +takeover material. Treat it like a secret. + +To intentionally reset pairing: + +```bash +docker compose --profile whatsapp down +docker volume rm surfsense-whatsapp-sessions +docker compose --profile whatsapp up -d +``` diff --git a/surfsense_web/content/docs/meta.json b/surfsense_web/content/docs/meta.json index 13b599118..74be10600 100644 --- a/surfsense_web/content/docs/meta.json +++ b/surfsense_web/content/docs/meta.json @@ -9,6 +9,7 @@ "installation", "manual-installation", "docker-installation", + "messaging-channels", "connectors", "how-to", "---Developers---", diff --git a/surfsense_web/lib/source.ts b/surfsense_web/lib/source.ts index 62fbb362b..f71e8b688 100644 --- a/surfsense_web/lib/source.ts +++ b/surfsense_web/lib/source.ts @@ -7,6 +7,7 @@ import { Download, FlaskConical, Heart, + MessageCircle, Radar, Unplug, Wrench, @@ -27,6 +28,7 @@ const DOCS_ICONS: Record = { Download, FlaskConical, Heart, + MessageCircle, Radar, Unplug, Wrench,