▞ jazzdocsblogpersonas
github

Creating a Telegram or Discord bot

A hands-on walkthrough for going from nothing to a working Jazz agent in your Telegram DMs or a Discord server. Both bridges are shipped, Docker-based services — this page is the account-creation and configuration steps; see Chat platforms for what they demonstrate architecturally, and each bridge’s own README for the full command/environment-variable reference: packages/telegram-bot/README.md, packages/discord-bot/README.md.

You need Docker + Docker Compose, and a model backend — an API key for a cloud provider (OpenAI by default), or a local Ollama with a tool-capable model pulled.


Telegram

1. Create the bot

Message @BotFather on Telegram:

  1. Send /newbot.
  2. Pick a display name, then a username ending in bot (e.g. my_jazz_bot).
  3. BotFather replies with a token that looks like 123456:ABC-DEF.... That’s TELEGRAM_BOT_TOKEN — treat it like a password.

2. Get your chat id

Message @userinfobot — it replies with your numeric id. That’s what goes on the allowlist so the bot only answers you (and anyone else you add).

3. Configure

cd packages/telegram-bot/src
cp .env.example .env

Edit .env and set at least:

  • TELEGRAM_BOT_TOKEN — from step 1.
  • TELEGRAM_ALLOWED_CHAT_IDS — your id from step 2 (comma-separated if more than one).
  • A model backend — OPENAI_API_KEY is set by default (JAZZ_TELEGRAM_PROVIDER=openai, JAZZ_TELEGRAM_MODEL=gpt-5.4). To run fully local instead, set JAZZ_TELEGRAM_PROVIDER=ollama and JAZZ_TELEGRAM_MODEL=<a model you've pulled>.

4. Run it

The image builds Jazz from the repo source, so the compose build context is the repo root (already wired — no extra setup needed):

docker compose up -d --build
docker compose logs -f          # expect "Polling Telegram for updates…"

5. Talk to it

Message your bot on Telegram. It shows a “typing…” indicator, then the agent’s reply.

If nothing happens: check your chat id is actually on TELEGRAM_ALLOWED_CHAT_IDS, and that docker compose logs doesn’t show an auth error from Telegram (a copy-pasted token with a trailing space is the usual culprit).


Discord

1. Create the application and bot

  1. Open the Discord developer portal and sign in.
  2. New Application → name it (e.g. Jazz) → Create.
  3. Left sidebar → BotReset Token → copy it. That’s DISCORD_BOT_TOKEN — treat it like a password.
  4. Still on the Bot page, under Privileged Gateway Intents, turn on Message Content Intent and Save. Without this the bot cannot read what people type in a server.
  5. Left sidebar → OAuth2 → copy the Client ID (also called Application ID) — you’ll need it for the invite URL in the next step.

2. Invite it to your server

You need permission to add bots on that server (owner, or Manage Server).

Open this URL, replacing YOUR_APP_ID with the Client ID from step 1:

https://discord.com/oauth2/authorize?client_id=YOUR_APP_ID&scope=bot%20applications.commands&permissions=311385246720

Pick your server → Authorize. The bot appears in the member list, offline until the bridge is running. Those permissions are: View Channel, Send Messages, Send Messages in Threads, Create Public Threads, Embed Links, Attach Files, Read Message History, Use Application Commands.

To restrict it to one channel: after inviting, edit that channel’s permissions to allow the bot role there, and deny (or don’t grant) View Channel everywhere else. Then put that channel’s id on the allowlist below instead of a server id.

3. Copy ids for the allowlist

In Discord: User Settings → Advanced → Developer Mode (on). Then right-click and Copy … ID:

You want…Right-click
Yourself (DMs + your @mentions)your avatar / username → Copy User ID
One channel onlythe channel → Copy Channel ID
The whole serverthe server name → Copy Server ID

For a private server, the usual choice is DISCORD_ALLOWED_GUILD_IDS=<server id> (anyone in the server can @mention the bot), or DISCORD_ALLOWED_USER_IDS=<your id> (only you, in DMs and in any server the bot is in).

4. Configure

cd packages/discord-bot/src
cp .env.example .env

Edit .env and set at least:

  • DISCORD_BOT_TOKEN — from step 1.
  • One allowlist: DISCORD_ALLOWED_USER_IDS, DISCORD_ALLOWED_CHANNEL_IDS, and/or DISCORD_ALLOWED_GUILD_IDS.
  • A model backend — OPENAI_API_KEY is set by default (JAZZ_DISCORD_PROVIDER=openai, JAZZ_DISCORD_MODEL=gpt-5.4). To run fully local instead, set JAZZ_DISCORD_PROVIDER=ollama and JAZZ_DISCORD_MODEL=<a model you've pulled>.

5. Run it

docker compose up -d --build
docker compose logs -f          # expect "Discord → Jazz bridge ready as @…"

6. Talk to it

  • In the server: @Jazz what's the weather in Lyon — it starts a thread and replies there. Follow-ups in that thread don’t need another mention.
  • DMs: only if your user id is on DISCORD_ALLOWED_USER_IDS.
  • Slash commands (/help, /status, /tz, …) show up in the server a few seconds after the bridge logs “ready”.

If it stays silent in the server: Message Content Intent is off, the channel isn’t allowlisted, or you didn’t @mention it (DISCORD_REQUIRE_MENTION=1 by default).


Adding more providers

Both bridges start on one provider (OPENAI_API_KEY/gpt-5.4 by default), but /model can switch a conversation to any of the ~18 providers Jazz supports — Anthropic, Gemini, xAI, OpenRouter, Groq, and more — without touching JAZZ_TELEGRAM_PROVIDER/ JAZZ_DISCORD_PROVIDER (those only set what a brand-new conversation starts on).

To enable a provider for /model, set its API key as an env var on the bot and restart the container — .env.example lists the full set (ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, OPENROUTER_API_KEY, XAI_API_KEY, GROQ_API_KEY, …). Then, as a normal message in the chat:

/model anthropic/claude-sonnet-5

Bare /model (no arguments) instead shows a picker of whatever the conversation’s current provider offers. Reasoning effort is set automatically either way.


Keeping it updated

Both bots ship an auto-update.sh that fast-forwards the checkout to origin/main, rebuilds only if something changed, and rolls back if the new build doesn’t come up healthy. Install it as an hourly cron job (adjust the path to where you cloned the repo):

(crontab -l 2>/dev/null; echo "30 * * * * $HOME/jazz/packages/telegram-bot/src/auto-update.sh >> $HOME/jazz-autoupdate.log 2>&1") | crontab -

Swap telegram-bot for discord-bot to update the other one. A sibling executable notify.sh — present in both directories — posts the outcome (success, rollback, or a build that needs a look) back to the bot’s own chat/channel, so a failed deploy doesn’t sit silently in a logfile.

machine-readable: /docs/start/chat-bots.md · /llms.txt · /llms-full.txt