Español: see
README.es.mdfor the full Spanish version.
A multichannel Telegram + WhatsApp assistant that lets people operate the ResponseGrid humanitarian-aid platform in plain language — by text or voice note — from the chat apps they already use.
When a disaster hits, the bottleneck is rarely goodwill — it's coordination. ResponseGrid coordinates resources, collection points, inventory and needs per emergency. This bot is its conversational front door: a volunteer running a collection point at 2 a.m. won't open a dashboard, but they already have WhatsApp open. The bot lets them find aid nearby, register resources, update inventory and manage needs, all in natural language.
Try the live bots: Telegram · WhatsApp · Web app
- How it works
- Tech stack
- Quickstart
- Configuration
- Project structure
- Available tools
- Security model
- Deployment
- Contributing
- License
Telegram text/audio WhatsApp text/audio/location
│ │
Telegraf bot (long polling) HTTP webhook (HMAC signature + verify token)
│ │
└──────────────┬───────────────┘
│ (voice notes → transcription)
▼
OpenAI Agent ──► ResponseGrid tools ──► ResponseGrid API
│
▼
Reply on the originating channel
The codebase follows hexagonal architecture (DDD) with clear layers:
domain (entities, ports) → application (the ConversationService
orchestrator) → infrastructure (Telegram, WhatsApp, ResponseGrid HTTP client,
persistence, observability). Each channel implements the same
MessagingChannel port, so adding a new channel means writing one adapter.
Multi-account: several emergencies and channels run in the same process.
Each account (one channel + one emergency) is configured independently in
accounts.json.
- Runtime: Node.js ≥ 24 (ESM,
NodeNext), TypeScript (strict) - Agent:
@openai/agentsSDK - Telegram:
telegraf(long polling) - WhatsApp: Meta WhatsApp Cloud API (Graph API
v25.0) via a self-hosted webhook - Validation:
zod - Tests:
node:testviatsx(no test framework) - Voice: OpenAI transcription for voice notes
# Node 24+ required (see .nvmrc)
npm install
cp .env.example .env # fill in OPENAI_API_KEY, API_BASE_URL
cp accounts.example.json accounts.json # add at least one Telegram or WhatsApp account
npm run devThen message your Telegram bot, or (for WhatsApp) expose the webhook port through a tunnel so Meta can reach it. Try:
Hola, ¿qué puedes hacer en ResponseGrid?
List the active emergencies
Find water collection points in the default emergency
What resources do I manage?
You can also send any of these as a voice note.
Common scripts:
| Script | What it does |
|---|---|
npm run dev |
Run in watch mode (tsx) |
npm run build |
Compile TypeScript to dist/ |
npm start |
Run the compiled build |
npm test |
Run the test suite (node:test) |
npm run typecheck |
Type-check without emitting |
npm run mcp |
Run the MCP server (exposes the tools over MCP) |
Two files, both git-ignored (only their *.example.* counterparts are tracked):
.env — global config shared by every account:
OPENAI_API_KEY=sk-...
OPENAI_MODEL= # optional; empty = SDK default
API_BASE_URL=https://api.responsegrid.app
# Only if you have any channel="whatsapp" account:
WHATSAPP_APP_SECRET=
WHATSAPP_VERIFY_TOKEN=
WHATSAPP_WEBHOOK_PORT=8787accounts.json — one entry per account (one channel + one emergency):
| Field | Description |
|---|---|
id |
Unique account id (internal, for logs/sessions) |
channel |
"telegram" or "whatsapp" |
emergencySlug |
ResponseGrid emergency this account is bound to |
apiToken |
ResponseGrid service-account token for this account |
telegramBotToken |
Telegram only — token from @BotFather |
whatsappPhoneNumberId |
WhatsApp only — Meta Phone Number ID |
whatsappAccessToken |
WhatsApp only — Meta access token |
The process refuses to start until accounts.json has at least one account with
valid credentials. See README.es.md for step-by-step credential
setup (BotFather, Meta Cloud API).
src/
├── domain/ Entities & ports (Account, InboundMessage, MessagingChannel)
├── application/ ConversationService orchestrator, rate limiter
├── agent/ OpenAI agent, dynamic instructions, ResponseGrid tools
├── audio/ Voice-note transcription
├── config/ Env & accounts.json loading (zod-validated)
└── infrastructure/
├── telegram/ Telegraf adapter & bootstrap
├── whatsapp/ Cloud API webhook, HMAC verification, payload builders
├── responsegrid/ HTTP API client & trusted phone-login client
├── persistence/ File-based session & token stores
├── observability/ Structured conversation logging
└── mcp/ MCP tool mapper
The agent exposes safe, typed wrappers over the ResponseGrid API, grouped by
area (see src/agent/tools.ts for the full list):
- Identity & emergencies —
rg_get_api_identity,rg_list_emergencies,rg_get_emergency_by_slug - Public search —
rg_list_public_resources,rg_find_nearby_resources,rg_list_public_needs,rg_find_nearby_needs - Managed resources & inventory —
rg_list_my_managed_resources,rg_record_inventory_entry, status changes - Needs —
rg_create_need,rg_validate_need,rg_list_need_queue - Supply catalog —
rg_search_supplies(multi-language standardization) - Geocoding —
rg_geocode - Auth & UX —
rg_request_user_login(phone-based),rg_present_options(tappable buttons)
The security boundary is the ResponseGrid API, not the LLM. The API enforces JWT + role-based authorization on every call; the bot can never do more than the authenticated user is allowed to. Highlights:
- Phone-based login uses only the phone number verified by the messaging platform — a number typed into the chat is ignored, by design.
- WhatsApp webhooks verify the
X-Hub-Signature-256HMAC before parsing. - Rate limiting and input-size limits (text and audio) on both channels.
- No secrets in logs or model context. Session/token files are
chmod 600.
Found a vulnerability? See SECURITY.md — please report privately.
Deploys automatically on every merge to main via GitHub Actions (build + test
on the runner, artifacts copied over SSH, pm2 startOrReload on the server).
See section 10 of README.es.md for the full Plesk/PM2 setup and
the GitHub secrets it expects.
Contributions are very welcome — this is a humanitarian project. Start with
CONTRIBUTING.md and the
good first issues.
By participating you agree to our Code of Conduct.
GNU Affero General Public License v3.0 (AGPL-3.0). Built by Global Emergency.