Receber por email
← Voltar para o catálogo

add-telegram

Add Telegram as a channel. Can replace WhatsApp entirely or run alongside it. Also configurable as a control-only channel (triggers actions) or passive channel (receives notifications only).

10estrelas
Atualizado há 4 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add crypdick/pynchy

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/add-telegram-crypdick/svg)](https://www.skillteca.com.br/skills/add-telegram-crypdick)
#ai

Skills relacionadas

Ver todas de Segurança →

Alerta por categoria

Receba novas skills de Segurança toda segunda

1 email curto, só com as skills novas de Segurança. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Add Telegram Channel

This skill adds Telegram support to NanoClaw. Users can choose to:

  1. Replace WhatsApp - Use Telegram as the only messaging channel
  2. Add alongside WhatsApp - Both channels active
  3. Control channel - Telegram triggers agent but doesn't receive all outputs
  4. Notification channel - Receives outputs but limited triggering

Prerequisites

1. Install Grammy

npm install grammy

Grammy is a modern, TypeScript-first Telegram bot framework.

2. Create Telegram Bot

Tell the user:

I need you to create a Telegram bot:
1. Open Telegram and search for @BotFather
2. Send /newbot and follow prompts:
- Bot name: Something friendly (e.g., "Andy Assistant")
- Bot username: Must end with "bot" (e.g., "andy_ai_bot")
3. Copy the bot token (looks like 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11)

Wait for user to provide the token.

3. Get Chat ID

Tell the user:

To register a chat, you need its Chat ID. Here's how:
For Private Chat (DM with bot):
1. Search for your bot in Telegram
2. Start a chat and send any message
3. I'll add a /chatid command to help you get the ID
For Group Chat:
1. Add your bot to the group
2. Send any message
3. Use the /chatid command in the group

4. Disable Group Privacy (for group chats)

Tell the user:

Important for group chats: By default, Telegram bots in groups only receive messages that @mention the bot or are commands. To let the bot see all messages (needed for requiresTrigger: false or trigger-word detection):
1. Open Telegram and search for @BotFather
2. Send /mybots and select your bot
3. Go to Bot Settings > Group Privacy
4. Select Turn off
Without this, the bot will only see messages that directly @mention it.

This step is optional if the user only wants trigger-based responses via @mentioning the bot.

Questions to Ask

Before making changes, ask:

  1. Mode: Replace WhatsApp or add alongside it?
  2. If replace: Set TELEGRAM_ONLY=true
  3. If alongside: Both will run
  1. Chat behavior: Should this chat respond to all messages or only when @mentioned?
  2. Main chat: Responds to all (set requiresTrigger: false)
  3. Other chats: Default requires trigger (requiresTrigger: true)

Architecture

NanoClaw uses a Channel abstraction (Channel interface in src/types.ts). Each messaging platform implements this interface. Key files:

| File | Purpose | |------|---------| | src/types.ts | Channel interface definition | | src/channels/whatsapp.ts | WhatsAppChannel class (reference implementation) | | src/router.ts | findChannel(), routeOutbound(), formatOutbound() | | src/index.ts | Orchestrator: creates channels, wires callbacks, starts subsystems | | src/ipc.ts | IPC watcher (uses sendMessage dep for outbound) |

The Telegram channel follows the same pattern as WhatsApp:

  • Implements Channel interface (connect, sendMessage, ownsJid, disconnect, setTyping)
  • Delivers inbound messages via onMessage / onChatMetadata callbacks
  • The existing message loop in src/index.ts picks up stored messages automatically

Implementation

Step 1: Update Configuration

Read src/config.ts and add Telegram config exports:

export const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN || "";
export const TELEGRAM_ONLY = process.env.TELEGRAM_ONLY === "true";

These should be added near the top with other configuration exports.

Step 2: Create Telegram Channel

Create src/channels/telegram.ts implementing the Channel interface. Use src/channels/whatsapp.ts as a reference for the pattern.

import { Bot } from "grammy";

import {
  ASSISTANT_NAME,
  TRIGGER_PATTERN,
} from "../config.js";
import { logger } from "../logger.js";
import { Channel, OnInboundMessage, OnChatMetadata, RegisteredGroup } from "../types.js";

export interface TelegramChannelOpts {
  onMessage: OnInboundMessage;
  onChatMetadata: OnChatMetadata;
  registeredGroups: () => Record<string, RegisteredGroup>;
}

export class TelegramChannel implements Channel {
  name = "telegram";
  prefixAssistantName = false; // Telegram bots already display their name

  private bot: Bot | null = null;
  private opts: TelegramChannelOpts;
  private botToken: string;

  constructor(botToken: string, opts: TelegramChannelOpts) {
    this.botToken = botToken;
    this.opts = opts;
  }

  async connect(): Promise<void> {
    this.bot = new Bot(this.botToken);

    // Command to get chat ID (useful for registration)
    this.bot.command("chatid", (ctx) => {
      const chatId = ctx.chat.id;
      const chatType = ctx.chat.type;
      const chatName =
        chatType === "private"
          ? ctx.from?.first_name || "Private"
          : (ctx.chat as any).title || "Unknown";

      ctx.reply(
        `Chat ID: \`tg:${chatId}\`\nName: ${chatName}\nType: ${chatType}`,
        { parse_mode: "Markdown" },
      );
    });

    // Command to check bot status
    this.bot.command("ping", (ctx) => {
      ctx.reply(`${ASSISTANT_NAME} is online.`);
    });

    this.bot.on("message:text", async (ctx) => {
      // Skip commands
      if (ctx.message.text.startsWith("/")) return;

      const chatJid = `tg:${ctx.chat.id}`;
      let content = ctx.message.text;
      const timestamp = new Date(ctx.message.date * 1000).toISOString();
      const senderName =
        ctx.from?.first_name ||
        ctx.from?.username ||
        ctx.from?.id.toString() ||
        "Unknown";
      const sender = ctx.from?.id.toString() || "";
      const msgId = ctx.message.message_id.toString();

      // Determine chat name
      const chatName =
        ctx.chat.type === "private"
          ? senderName
          : (ctx.chat as any).title || chatJid;

      // Translate Telegram @bot_username mentions into TRIGGER_PATTERN format.
      // Telegram @mentions (e.g., @andy_ai_bot) won't match TRIGGER_PATTERN
      // (e.g., ^@Andy\b), so we prepend the trigger when the bot is @mentioned.
      const botUsername = ctx.me?.username?.toLowerCase();
      if (botUsername) {
        const entities = ctx.message.entities || [];
        const isBotMentioned = entities.some((entity) => {
          if (entity.type === "mention") {
            const mentionText = content
              .substring(entity.offset, entity.offset + entity.length)
              .toLowerCase();
            return mentionText === `@${botUsername}`;
          }
          return false;
        });
        if (isBotMentioned && !TRIGGER_PATTERN.test(content)) {
          content = `@${ASSISTANT_NAME} ${content}`;
        }
      }

      // Store chat metadata for discovery
      this.opts.onChatMetadata(chatJid, timestamp, chatName);

      // Only deliver full message for registered groups
      const group = this.opts.registeredGroups()[chatJid];
      if (!group) {
        logger.debug(
          { chatJid, chatName },
          "Message from unregistered Telegram chat",
        );
        return;
      }

      // Deliver message — startMessageLoop() will pick it up
      this.opts.onMessage(chatJid, {
        id: msgId,
        chat_jid: chatJid,
        sender,
        sender_name: senderName,
        content,
        timestamp,
        is_from_me: false,
      });

      logger.info(
        { chatJid, chatName, sender: senderName },
        "Telegram message stored",
      );
    });

    // Handle non-text messages with placeholders so the agent knows something was sent
    const storeNonText = (ctx: any, placeholder: string) => {
      const chatJid = `tg:${ctx.chat.id}`;
      const group = this.opts.registeredGroups()[chatJid];
      if (!group) return;

      const timestamp = new Date(ctx.message.date * 1000).toISOString();
      const senderName =
        ctx.from?.first_name || ctx.from?.username || ctx.from?.id?.toString() || "Unknown";
      const caption = ctx.message.capt
Compartilhar X LinkedIn