From telegram
Set up the Telegram channel — save the bot token, choose voice transcription, and review access policy. Use when the user pastes a Telegram bot token, asks to configure Telegram, set up voice transcription, asks "how do I set this up" or "who can reach me," wants to check channel status, or asks to uninstall / remove / tear down the Telegram channel.
How this skill is triggered — by the user, by Claude, or both
Slash command
/telegram:configureThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Writes the bot token and voice-transcription settings to
Writes the bot token and voice-transcription settings to
~/.claude/channels/telegram/.env and orients the user on access policy.
Arguments passed: $ARGUMENTS
A user can run several independent bridges (different bots) on one machine, each in its own
state dir, identified by an instance id — a number or a name (e.g. 2, work). Resolve
which instance this invocation targets, in priority order:
$ARGUMENTS (e.g. /telegram:configure work <token>) → that
id. (The first argument is the id only if it isn't itself a bot token — a token contains a
colon :. So /telegram:configure 123456:ABC… has no id; /telegram:configure work 123456:ABC…
targets id work.)claude-tg <id>). Read it:
tmux display-message -p -t "$TMUX_PANE" '#{@tg_bridge}' 2>/dev/null. If non-empty (e.g. work),
use it — so a user who ran claude-tg work can just type /telegram:configure <token> here and
it targets the work bridge (the very session they're in).1 (the default).State dir for the resolved id: id 1 → ~/.claude/channels/telegram; any other id <id> →
~/.claude/channels/telegram-<id> (e.g. work → telegram-work, 2 → telegram-2). Substitute
that path for ~/.claude/channels/telegram everywhere in the steps below. The daemon derives the
id from the dir name (TELEGRAM_INSTANCE_ID is not needed). Each instance's token/allowlist/pairings
are fully isolated.
After writing a NEW slot's token, bring its daemon up now (it isn't covered by the already-running
hook until the next session start): run bun ~/.claude/channels/telegram/ensure-daemon.js — it
enumerates every configured instance and launches any that's down. The new daemon then auto-discovers
and adopts the tagged pane (e.g. the claude-tg <id> session the user is in). For panes elsewhere,
tell the user to launch them with claude-tg <id>.
Strip the leading instance id (if present) before parsing the rest of the arguments below.
Read both state files and give the user a complete picture:
Token — check ~/.claude/channels/telegram/.env for
TELEGRAM_BOT_TOKEN. Show set/not-set; if set, show first 10 chars masked
(123456789:...).
Transcription — check .env for TELEGRAM_TRANSCRIBE (default off).
Show the backend and model. If off, mention voice notes arrive as
placeholders and offer /telegram:configure transcribe.
Access — read ~/.claude/channels/telegram/access.json (missing file
= defaults: dmPolicy: "pairing", empty allowlist). Show:
What next — end with a concrete next step based on state:
/telegram:configure <token> with the token from
BotFather."/telegram:access pair <code>."Available actions — briefly list what this skill can do so the menu is
discoverable: <token> (save token), transcribe (voice), mcp (MCP server
on/off), bang (Telegram ! shell — RCE, off by default), clear (remove token),
uninstall (stop the bot and tear down the channel), plus /telegram:access for
the allowlist. Keep it to one compact line.
Push toward lockdown — always. The goal for every setup is allowlist
with a defined list. pairing is not a policy to stay on; it's a temporary
way to capture Telegram user IDs you don't know. Once the IDs are in, pairing
has done its job and should be turned off.
Drive the conversation this way:
pairing → "Good. Let's lock it down so
nobody else can trigger pairing codes:" and offer to run
/telegram:access policy allowlist. Do this proactively — don't wait to
be asked./telegram:access pair <code>. Run this skill again once
everyone's in and we'll lock it."allowlist → confirm this is the locked state.
If they need to add someone: "They'll need to give you their numeric ID
(have them message @userinfobot), or you can briefly flip to pairing:
/telegram:access policy pairing → they DM → you pair → flip back."Never frame pairing as the correct long-term choice. Don't skip the lockdown
offer.
<token> — save it$ARGUMENTS as the token (trim whitespace). BotFather tokens look
like 123456789:AAH... — numeric prefix, colon, long string.mkdir -p ~/.claude/channels/telegram.env if present; update/add the TELEGRAM_BOT_TOKEN= line,
preserve other keys. Write back, no quotes around the value.chmod 600 ~/.claude/channels/telegram/.env — the token is a credential.transcribe [off | local | groq | openai] — voice transcriptionVoice and audio notes can be transcribed to text before they reach the
session, so the user can talk to Claude. Transcription runs entirely outside
Claude (a local model or a hosted Whisper API), so it never consumes Claude
usage — only the resulting text enters the conversation. If a backend isn't
given in $ARGUMENTS, explain the options and ask the user to choose:
| Backend | What it is | Tradeoff |
|---|---|---|
local (recommended) | faster-whisper on this machine | Free, fully private. Same model weights as Groq → identical quality at the same model size. Fast on GPU; slower on CPU for large models. |
groq | Groq Whisper API | Free tier, very fast. Needs a GROQ_API_KEY; audio leaves the machine. |
openai | OpenAI Whisper API | ~$0.006/min. Needs an OPENAI_API_KEY; audio leaves the machine. |
off | disabled | Voice/audio arrive as placeholders. |
Then set up the chosen backend, writing keys to .env (preserve other keys,
no quotes, then chmod 600):
off — set TELEGRAM_TRANSCRIBE=off. Confirm.
local
python3 -c "import faster_whisper". If it fails, offer to install:
python3 -m pip install faster-whisper. On an externally-managed Python
(PEP 668 error), make a venv instead —
python3 -m venv ~/.claude/channels/telegram/whisper-venv then
~/.claude/channels/telegram/whisper-venv/bin/python -m pip install faster-whisper
— and set TELEGRAM_WHISPER_PYTHON to that venv's python. (No system
ffmpeg is needed; faster-whisper decodes audio via bundled PyAV.)nvidia-smi to check for a GPU:
large-v3-turbo; set
TELEGRAM_WHISPER_DEVICE=cuda and TELEGRAM_WHISPER_COMPUTE=float16.base or small for low latency, or
large-v3-turbo if the user accepts slower transcription. Default
compute int8.
Ask the user which model they want.TELEGRAM_TRANSCRIBE=local, TELEGRAM_TRANSCRIBE_MODEL=<model>, plus
any TELEGRAM_WHISPER_PYTHON / _DEVICE / _COMPUTE overrides.groq
GROQ_API_KEY=<key> and TELEGRAM_TRANSCRIBE=groq. Default model is
whisper-large-v3-turbo; set TELEGRAM_TRANSCRIBE_MODEL only to override.openai
OPENAI_API_KEY=<key> and TELEGRAM_TRANSCRIBE=openai. Default model
is whisper-1.Confirm the backend/model, and tell the user transcription applies on the next voice message — no restart needed (the daemon reads these settings live).
mcp [on | off] — MCP server (off by default)Controls whether the plugin loads its MCP server (shim.ts). Off is the default and
recommended: the bridge runs off-MCP (replies read from the transcript, actions via the tg
CLI), which costs zero per-request tokens but requires tmux. On restores the MCP
server so the bridge works without tmux, at the cost of ~700 tokens of tool schemas + an
instruction block injected on every request. Both modes expose identical features.
The switch is the presence of the plugin's .mcp.json. Find the plugin dir (newest version
under the cache) and rename:
DIR=$(ls -d ~/.claude/plugins/cache/claude-tg/telegram/*/ | sort -V | tail -1)
# on: mv "$DIR/mcp.json.disabled" "$DIR/.mcp.json"
# off: mv "$DIR/.mcp.json" "$DIR/mcp.json.disabled"
.mcp.json present = on, mcp.json.disabled = off) and
the tradeoff above.on/off → do the rename (skip if already in that state).Then tell the user it applies to sessions started afterward (Claude Code loads MCP servers
at launch) — and that off-MCP claude-tg sessions don't load the plugin's MCP server anyway
(it ships disabled), so this doesn't affect them. (You can also flip this from
Telegram with /settings.)
bang [on | off] — Telegram shell commands (off by default)Controls bang shell: whether an inbound Telegram message starting with ! runs as a shell
command on the host (in the focused pane's cwd) with output relayed back — like Claude Code's
terminal ! REPL (e.g. !git status). Off by default.
⚠️ This is remote code execution from a chat app. Any allowlisted sender — or anyone who
compromises the bot token or an allowlisted account — can run arbitrary commands. It stays gated by
the access allowlist, but enabling it widens trust significantly. Confirm the user really wants it.
Only ever flip it from this terminal skill — it is deliberately not a /settings toggle,
because a Telegram-tappable switch would let a chat-side actor enable RCE.
The switch is the TELEGRAM_BANG_SHELL line in <state-dir>/.env:
TELEGRAM_BANG_SHELL=1 present = on, else off) + the warning.on → ensure a TELEGRAM_BANG_SHELL=1 line in .env (add if absent); keep the file chmod 600.off → remove the TELEGRAM_BANG_SHELL line.Then restart that instance's daemon so it re-reads .env (the flag is read at startup):
kill "$(cat <state-dir>/daemon.pid)" then bun ~/.claude/channels/telegram/ensure-daemon.js
(enumerates every configured instance and relaunches any that's down). Tell the user it's now live
for new inbound ! messages (or disabled).
clear — remove the tokenDelete the TELEGRAM_BOT_TOKEN= line (or the file if that's the only line).
uninstall — tear down the channelA guided teardown. This skill stops the running bot, removes this channel's
local state, and cleans the install's settings/PATH footprint (the SessionStart
hook, the appended CLAUDE.md block, the tg CLI); it cannot remove the
plugin itself (that's a /plugin command, and skills can't invoke slash commands
or run host-shell uninstall steps), so it hands those off at the end.
Confirm before doing anything — this stops the running bot. Ask the user to confirm, and ask one branching question: do they want to keep the saved token + pairing/access config (so a later reinstall stays paired — the default, recommended when reinstalling/upgrading), or do a full reset that deletes it? Don't delete state unless they explicitly choose the full reset.
Stop the daemon. Read ~/.claude/channels/telegram/daemon.pid. If it
exists, kill <pid> — the daemon is long-lived and outlives the session, so
it must be stopped explicitly or the old bot keeps running. If there's no
pid file, it isn't running; say so and move on.
Remove the install footprint — things the setup added outside the
plugin system, which /plugin uninstall won't touch (do these every time, on
keep or full reset):
~/.claude/settings.json — read the JSON and delete the SessionStart
hook whose command references channels/telegram/ensure-daemon.js; write it
back. Drop a now-empty SessionStart array (and hooks object) if removing
it leaves them empty. Leave enabledPlugins / extraKnownMarketplaces —
the /plugin commands in the next step remove those.~/.claude/CLAUDE.md — remove the appended convention block, from the
# Reachable over Telegram (no MCP) heading through the end of its
## Live activity section. Preserve everything else in the file.tg CLI — rm -f ~/.bun/bin/tg ~/.local/bin/tg (the daemon
provisions it onto PATH; it's dead once the daemon is gone).Channel state — act on the user's choice from above:
~/.claude/channels/telegram/ untouched. The
bot token, access.json allowlist, and pairings survive for reinstall.rm -rf ~/.claude/channels/telegram to delete the token,
access.json, inbox, and sockets. Tell the user plainly that the bot
token and allowlist are now gone and they'll reconfigure from scratch.Remove the plugin — this skill can't run /plugin commands, so print
these for the user to run in their session:
/plugin uninstall telegram@claude-tg
/plugin marketplace remove claude-tg
The plugin is cached, so to guarantee a fresh fetch on any reinstall, from a shell:
rm -rf ~/.claude/plugins/marketplaces/claude-tg
rm -rf ~/.claude/plugins/cache/claude-tg
To reinstall later:
/plugin marketplace add casualsav/claude-tg
/plugin install telegram@claude-tg
Restart Claude Code to apply either removal or reinstall.
End with a short summary of what was done (daemon stopped; hook + CLAUDE.md
block + tg CLI removed; channel state kept or removed) and exactly what's left
for the user to run (the /plugin commands).
TELEGRAM_TRANSCRIBE, model, keys, whisper
overrides) are read live from .env on each voice message — changes apply
immediately, no restart./reload-plugins).
To apply a token change, restart your Claude session — the surest way — or
force a relaunch with
kill "$(cat ~/.claude/channels/telegram/daemon.pid)" (the daemon respawns
on the next Telegram activity)..env as secret: always chmod 600 after writing, never echo full
key values back to the user (mask them).access.json is re-read on every inbound message — policy changes via
/telegram:access take effect immediately, no restart.npx claudepluginhub casualsav/claude-tg --plugin telegramCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.