{"id":"add-telegram","name":"add-telegram","summary":"Chat SDKを通じてTelegramチャネル連携を追加しましょう。","body":"# Add Telegram Channel\n\nAdds Telegram bot support via the Chat SDK bridge. NanoClaw doesn't ship\nchannels in trunk — this skill copies the Telegram adapter, its pairing helper,\nand their tests in from the `channels` branch. The\n`pair-telegram` setup step is maintained in trunk, so it is not copied here.\n\nThe mechanical steps under **Apply** carry `nc:` directive fences: an agent\nreads the prose and applies them, and a parser can apply them deterministically\nfrom the same document. Every directive is idempotent, so the whole skill is\nsafe to re-run; anything a parser can't apply falls back to the prose beside it.\n\n## Apply\n\n### 1. Copy the adapter, helpers, and tests\n\nFetch the `channels` branch and copy the Telegram adapter, its pairing helper\n(with its test), and the focused adapter tests into place (overwrite — the\nbranch is canonical):\n\n```nc:copy from-branch:channels\nsrc/channels/telegram.ts\nsrc/channels/telegram-pairing.ts\nsrc/channels/telegram-pairing.test.ts\nsrc/channels/telegram-registration.test.ts\nsrc/channels/telegram-connect-group.test.ts\n```\n\n### 2. Register the adapter\n\nAppend the self-registration import to the channel barrel (skipped if the line\nis already present). This one line is the skill's only reach-in into core:\n\n```nc:append to:src/channels/index.ts\nimport './telegram.js';\n```\n\n### 3. Register the pairing setup step\n\nAdd the `pair-telegram` loader to the `STEPS` map in `setup/index.ts`, inside the\ndormant marker region (skipped if already present — `pair-telegram` ships in core,\nso this idempotent-skips on a normal install, but is expressed for a\nclean-upstream rebuild). The pairing handshake below spawns this step:\n\n```nc:append to:setup/index.ts at:nanoclaw:setup-steps\n'pair-telegram': () => import('./pair-telegram.js'),\n```\n\n### 4. Install the adapter package\n\nPinned to an exact version — the supply-chain policy rejects ranges and `latest`:\n\n```nc:dep\n@chat-adapter/telegram@4.29.0\n```\n\n### 5. Build and validate\n\nBuild first: it guards the typed `createChatSdkBridge(...)` core call and proves\nthe dependency is installed. Then run the focused tests.\n\n```nc:run effect:build\npnpm run build\n```\n```nc:run effect:test\npnpm exec vitest run src/channels/telegram-registration.test.ts src/channels/telegram-connect-group.test.ts\n```\n\n`telegram-registration.test.ts` imports the real channel barrel and asserts the\nregistry contains `telegram`. It goes red if the import line is deleted or drifts,\nif the barrel fails to evaluate, or if `@chat-adapter/telegram` isn't installed\n(the import throws) — so it also covers the dependency from step 4. End-to-end\ndelivery against a real bot is verified manually once the service runs.\n\n## Credentials\n\nBot creation in Telegram is human and interactive — no parser can click through\nBotFather. The adapter is installed and registered, but it can't receive a\nmessage until the bot exists. Tell the user:\n\n```nc:operator\nCreate the Telegram bot:\n1. Open Telegram and message @BotFather — Telegram's official bot for creating bots.\n2. Send /newbot and follow the prompts: a friendly name, then a username that must end in \"bot\".\n3. Copy the bot token it gives you (looks like 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11a).\n4. Planning to use the bot in group chats? Send /mybots → your bot → Bot Settings → Group Privacy → Turn off, so the bot can see all messages and not just @mentions.\n```\n\nCollect the bot token and store it — the bridge reads it from `.env` (set-if-absent,\nso a value you've already filled in is never overwritten) and syncs it to the\ncontainer:\n\n```nc:prompt bot_token secret validate:^[0-9]+:[A-Za-z0-9_-]{35,}$\nPaste the bot token from BotFather (looks like `123456:ABC-DEF...`).\n```\n```nc:env-set\nTELEGRAM_BOT_TOKEN={{bot_token}}\n```\nConfirm the token works and capture the bot's handle — `getMe` returns the bot\naccount and fails here if the token is bad. You'll use the handle to open the\nright chat just before pairing:\n\n```nc:run capture:bot_username effect:fetch\ncurl -sf https://api.telegram.org/bot{{bot_token}}/getMe | jq -er '.result.username'\n```\n\n## Restart\n\nRestart the service so it loads the Telegram adapter and the token you just\nstored, and wait for its CLI socket. The adapter must be live and polling before\npairing — it's the thing that observes the code you send:\n\n```nc:run effect:restart\nbash setup/lib/restart.sh\n```\n\n## Pair your chat\n\nTelegram tokens carry no user binding, so the agent proves you own the chat with\na one-time pairing handshake: it issues a 4-digit code, you send those exact 4\ndigits to the bot from the chat you want to register, and the live adapter\nmatches them. Open the bot first so you're on the right screen when the code\nappears. Tell the user:\n\n```nc:operator\nOpen @{{bot_username}} (https://telegram.me/{{bot_username}}) in Telegram now and keep it on screen — a 4-digit pairing code is about to appear in this terminal. When it does, send just those 4 digits to the bot as a message (in a group chat with Group Privacy on, prefix them with @{{bot_username}}). A wrong guess is rejected and a fresh code is issued automatically.\n```\n\nRun the pairing handshake. It prints the code, streams \"waiting…\" and wrong-code\nfeedback while it watches for your message, and resolves your chat address\n`telegram:<chatId>` plus your Telegram user id once the code matches:\n\n```nc:run effect:step capture:platform_id=PLATFORM_ID,owner_handle=ADMIN_USER_ID\npnpm exec tsx setup/index.ts --step pair-telegram -- --intent main\n```\n\n`owner_handle` (your Telegram user id) and `platform_id` (`telegram:<chatId>`)\nare what the owner-wiring step needs. The greeting goes out over the same chat as\nsoon as pairing completes.\n\n## Next Steps\n\nIf you're in the middle of `/setup`, return to the setup flow now. Otherwise wire\nthis channel with `/init-first-agent` (or `/manage-channels`).\n\n## Connect a group\n\nAfter the first DM is paired, an owner or global admin can send `/connect_group` in\ntheir bot DM. The bot replies with Telegram's native group picker. Choosing a\ngroup adds the bot and posts an addressed start command there; NanoClaw then\nsends its existing channel-registration approval card to an eligible\nowner/admin DM. Nothing is wired until that card is approved.\n\nThe picker link is navigation, not authorization: it carries no secret and\ncreates no role, member, messaging-group, or wiring row. The existing approval\nflow remains the authority. If the picker is unavailable, add the bot to the\ngroup manually and post `/start@{{bot_username}} connect` there to reach the\nsame approval card.\n\nIf the negative Telegram chat ID is already known, the fully manual `ncl`\nequivalent is:\n\n```bash\nncl messaging-groups create --channel-type telegram --platform-id \"telegram:<chat-id>\" --name \"<group-name>\" --is-group 1\nncl wirings create --channel-type telegram --platform-id \"telegram:<chat-id>\" --agent-group \"<folder>\" --session-mode shared\n```\n\n`wirings create` applies Telegram's group defaults and creates the companion\ndestination row. Prefer `/connect_group` when the ID is unknown; it discovers\nthe group and keeps the approval card in the loop.\n\n## Channel Info\n\n- **type**: `telegram`\n- **terminology**: Telegram calls them \"groups\" and \"chats.\" A \"group\" has multiple members; a \"chat\" is a 1:1 conversation with the bot.\n- **platform-id-format**: `telegram:{chatId}` (e.g. `telegram:123456789` for a DM, `telegram:-1001234567890` for a group — negative chat IDs are groups/channels).\n- **how-to-find-id**: Do NOT ask the user for a chat ID. Pair the first DM with `pnpm exec tsx setup/index.ts --step pair-telegram -- --intent main`. For another group, have an owner/global admin send `/connect_group` in that paired DM, choose the group, and approve the resulting channel-registration card. The service must be running — the polling adapter observes both flows.\n- **supports-threads**: no\n- **typical-use**: Interactive chat — direct messages or small groups\n- **default-isolation**: Same agent group if you're the only participant across multiple chats. Separate agent group if different people are in different groups.\n\n## Troubleshooting\n\n**The bot token paste is rejected.** A BotFather token is `<numeric bot id>:<35+ character secret>` — e.g. `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11a`. Pasting only the part after the colon, or the bot's @username, won't pass. Recover the full token any time by sending `/token` to @BotFather.\n\n**`getMe` fails.** The token was revoked (a `/revoke` or a fresh `/token` invalidates the old value) or picked up whitespace in the paste. Get the current token from BotFather and re-paste it.\n\n**Pairing never completes.** The live adapter is what observes the code, so the service must be running — the restart step comes before pairing for exactly this reason. Send *just* the 4 digits from the exact chat you want registered; in a group with Group Privacy on, prefix them with `@<botname>`. Wrong guesses are fine (a fresh code is issued, up to 5 times), but a dead adapter waits forever.\n\n**The bot ignores group messages.** Group Privacy is on, so the bot only sees addressed commands and replies, not ordinary `@bot` text. BotFather → `/mybots` → your bot → Bot Settings → Group Privacy → Turn off — then remove and re-add the bot to the group so the change takes effect.\n\n**`/connect_group` is denied.** The Telegram sender is not a NanoClaw owner or global admin. The command never grants privileges; inspect them with `ncl roles list` and grant the intended role explicitly if appropriate.\n\n**The group was chosen but no approval card arrived.** Confirm the service is running and post `/start@{{bot_username}} connect` in the group. The card is delivered to an eligible owner/admin DM, not to the group.\n\n**Everything green but no replies.** Run `pnpm exec vitest run src/channels/telegram-registration.test.ts` — red means the barrel import or the `@chat-adapter/telegram` install drifted, so re-run the Apply steps. If green, restart again (`bash setup/lib/restart.sh`) and check `logs/nanoclaw.error.log` for token errors.","author":"@nanocoai","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/nanocoai/nanoclaw/tree/main/.claude/skills/add-telegram","license":"MIT","category":"writing","lang":"en","tokens":2455,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"apply-fixtures.json","size":665,"sha256":"efba471e7a8051316bfe501f34855ace8df7f8d3b30e962d81a30b88c1f17cec"},{"path":"REMOVE.md","size":1121,"sha256":"10be522ebcfddf80aa8d94fb67b503f9a7bfb98881c02452cef060a0b5a048b7"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["api.telegram.org","telegram.me"]}}