nc://channel-telegram

v0.1.0Ships with Nodecode
Backnodecode-ai
pomterretools/nodecode-guard: a README -- the operator (2026-10-10), after Telegram's: "also for guard". Its page on nodecode.ai/cells had no About text. One file: what guard does and that it is a speed bump, not a boundary; turning it on (/setup's Recommended, `nodecode add guard`) and off (space in /cells, "enabled": false); the nine built-in rules as a table with an example each; how a call is read (the built-ins fire only when the same eval call starts a process, so a file that mentions sudo is still writable, and a call that both spawns and carries a risky command is refused); the refusal text the agent reads; the guard section's deny (id, pattern, reason, gated, ungated by default) and allow (exempts only what it covers), with an example; the keys; the tests. Every claim read off cell.lisp, the kernel's waist/textrules.lisp and the cell's tests at this commit; the header comment's "cells": ["nodecode-guard"] is no longer how a cell turns on, so the README does not repeat it.88bf4c7· 10 Oct2 commits
test3 filesadapter.lisp20.2 KBpoll.lisp10.7 KBprobe.lisp9.9 KBrest.lisp36.2 KB

Telegram

This cell puts Nodecode in Telegram as a bot. People you allow can message it in a private chat or a group, and it answers there with the same agent you run in the terminal: same tools, same models, same store.

Each ask runs on its own, so two asks in one chat run at the same time. Reply to one of the bot's messages to keep talking about that answer. While a longer turn runs you see a card of its steps with Stop and Details buttons, and it stays above the answer as the turn's record.

The shared machinery (rooms, lanes, admission, the turn's card, commands, voice) is the channel kit's, in ../nodecode-channel-kit/. This folder holds what is Telegram's own: the getUpdates long poll, the Bot API calls, the config section and the setup probe.

Channel messages run with your machine's full authority. The bot refuses to start until you name who may talk to it.

Quick start

  1. Message @BotFather, send /newbot, and pick a name. Save the token it answers with to a file only you can read, for example ~/.nodecode/secrets/telegram.token with mode 600, or put it in an environment variable.
  2. Turn the cell on: nodecode add channel-telegram, or /setup in the terminal. It ships with Nodecode, so nothing is downloaded.
  3. Send your new bot a message. Telegram doesn't let a bot list its chats, so a chat only shows up for setup once it has written to the bot.
  4. Add a channels.telegram section to ~/.nodecode/config.jsonc:

    {
      "channels": {
        "telegram": {
          "token_file": "/home/you/.nodecode/secrets/telegram.token",
          "allowed_users": ["<your user id>"],
          "owner": ["<your user id>"]
        }
      }
    }

    Or tell Nodecode "set up telegram; the token is in /home/you/.nodecode/secrets/telegram.token". It checks the token and lists the chats and people that have written to the bot, so you pick ids by name. /setup in the terminal offers the same panel.

  5. Restart the channels (the web page's Restart channels, or ask Nodecode to run (restart-cells)). /channels in the terminal should read telegram: running, connected.
  6. Message the bot hello.

In a group

Add the bot to the group and put the group's chat id (it is negative) in allowed_chats, or keep allowed_users as the gate. With require_mention on (the default) the bot answers when you @mention it or reply to one of its messages.

Telegram hides most group messages from bots. Mentions and replies to the bot still reach it, which is all require_mention needs. To let it read every message, send @BotFather /setprivacy, choose the bot, pick Disable, then remove the bot from the group and add it again.

In a forum group each topic is its own room. allowed_threads limits the bot to some topics; empty admits all of them.

What you see

  • The card. A turn that takes a while posts one message that grows as it works: a pill (blue working, green done, red failed), the task, what it is doing now, its steps ticked off as they land, and its numbers in the footer. Stop ends the turn; Details shows whoever pressed it what the turn did.
  • Words as they're written. With stream on (the default) the answer appears in a message that is edited as the model writes. When it is done the answer posts fresh as a reply to your ask, so it notifies you, and the draft is deleted.
  • Markdown. Answers are Telegram rich messages: bold, lists, code, quotes and tables read as they do anywhere else. A very long answer comes in parts of up to 16,384 characters.
  • Reactions. With reactions on, your ask carries 👀 while its turn is active, then 👍 when the answer lands or 👎 if the turn fails.
  • Commands. The bot's / menu is Nodecode's command list: /new starts the chat fresh, /stop ends a running turn, /models shows or changes the chat's model, and so on. When owner is set, every command but /help is the owners' alone.

Files and voice

  • Files in. The bot reads photos (the largest size sent), documents, videos, audio files, voice notes and round video notes. A caption counts as the ask's text. A file in the message you reply to comes along too.
  • Voice in. A voice note reaches the agent as its transcript. Locally by default, so the audio never leaves your machine; the transcription section can point at an OpenAI-compatible server instead.
  • Files out. The agent can send files back: a picture shows inline, anything else comes as a document.
  • Voice out. voice_replies adds a voice message under an answer: on answers a voice note with one, tts answers every ask with one, off (the default) never does. It speaks with the speech section's voice and needs ffmpeg.

The agent can also call any Bot API method as the bot from its eval tool (nct:request): pin a message, react, read a chat or a member, change the command menu. The token never appears in what it reads back.

Who may talk to it

  • allowed_users and allowed_chats are the gate. At least one of them must name someone or somewhere, or the bot won't start.
  • owner names whose word is standing policy in the chat. An owner may always talk to the bot.
  • With pairing on (the default), someone no list names who writes to the bot in a private chat gets a pairing code, and you are told. /channels pair CODE lets them in; /channels paired lists who is in and /channels unpair USER-ID takes someone out.

Configuration

Every key of channels.telegram. A present section is enabled; "enabled": false turns it off.

KeyDefaultWhat it does
token_envEnvironment variable holding the bot token. Use this or token_file.
token_fileFile holding the bot token. The token never goes in the config itself.
allowed_chatsChat ids the bot answers in. A group's id is negative; a private chat's is the user's id.
allowed_usersUser ids allowed to drive the bot.
ownerUser ids whose word is standing policy.
pairingtrueAnswer unknown private chats with a pairing code.
require_mentiontrueIn groups, answer only when @mentioned or replied to.
allowed_threadsallForum topic ids the bot answers in.
allow_private_chatstruefalse ignores private chats.
allow_group_chatstruefalse ignores groups.
allow_forum_topicstruefalse ignores forum topics.
bot_usernamefrom getMeThe bot's @handle, for the mention check.
reactionsfalseMark each ask with 👀, then 👍 or 👎.
streamtrueShow the answer as it is written.
voice_replies"off""on", "tts" or "off"; see Files and voice.
turn_budget_minutes0Minutes a turn may run before it must wrap up with what it has. 0 sets no limit.
room_tokens40000The most history a chat keeps, in estimated tokens. Past it the older half is dropped from the chat's record (recall can still find it). 0 keeps everything.
soul_fileA SOUL.md whose text is the chat's standing persona.
agentsNamed agents a chat's asks may run as: a folder to work in, a prompt, a model, skills.
routesWhich asks run as which agent, by chat, topic or user id.
allowed_updates["message"]Update kinds the poll asks Telegram for. Widen it for a layer that watches edits, reactions or membership changes.
api_basehttps://api.telegram.orgThe Bot API server, for a local one.
request_timeout_seconds60How long one Bot API call may take.
get_updates_timeout_seconds2The long poll's window. Short on purpose: it is also how long a stop waits.
poll_interval_ms1000Pause between polls.

config.example.jsonc is a commented section to copy.

Troubleshooting

  • unauthorized (401): the token is wrong or was revoked. Ask @BotFather for a new one (/token) and save it where the config points.
  • A 409 conflict in the log: another program is polling with the same token, for example a second Nodecode or an old bot script. Telegram lets only one poll at a time. Stop the other one.
  • Setup lists no chats: nobody has written to the bot yet, or the running lane is holding the update stream. Message the bot, or read the chat ids off /channels.
  • The bot ignores a group: check that the group's id is in allowed_chats (or that you are in allowed_users), and mention the bot or reply to it. If the log says it could not read the bot's username, set bot_username.
  • No reactions: some groups forbid them. Turn reactions off there.

Testing

  • From a Nodecode checkout, just channels-test runs the kit's and the adapters' tests in one image with no network.
  • From this repository, .github/test-cell.sh rooms/nodecode-channel-telegram runs this cell's tests alone against a pinned Nodecode tree, offline.

The tests drive the adapter through a scripted Bot API, so they need no Telegram account.

Add nc://channel-telegram

  1. 1

    Install Nodecode

    Once per machine.

    $ curl -fsSL https://nodecode.ai/install.sh | sh
  2. 2

    Add the cell

    It ships with Nodecode, pinned at 88bf4c7; this turns it on. It brings channel-kit, which ships with Nodecode.

    $ nodecode add nc://channel-telegram

    Or tell the agent add the channel telegram cell.