Skip to content

Push Alerts to Telegram via RPC

1. Overview

ThinkLink (TKL) RPC can call external HTTP endpoints (see RPC Model §1.3.2 Type 4) and forward device alerts to Telegram in real time. This note shows how to create a Telegram Bot and send messages to a chat, channel, or group from an RPC.

2. Prerequisites

2.1 Create a Telegram Bot

  1. In Telegram, search for @BotFather and send /newbot
  2. Follow the prompts to set the bot's display name and username (must end with bot)
  3. BotFather returns a Bot Token that looks like:
    123456789:AAEhBPxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

2.2 Get the Chat ID

  1. Add the bot to the target group/channel (for channels, set the bot as admin)
  2. Send any message in the target chat
  3. Open https://api.telegram.org/bot<TOKEN>/getUpdates in a browser and find the chat.id field in the JSON response
  4. Group IDs are usually negative (e.g. -1001234567890); personal chat IDs are positive

2.3 Protect the Bot Token (Required Reading)

⚠️ Security notice: The Bot Token is equivalent to full control of the bot — anyone holding it can forge every message the bot sends, read all messages in the bot's groups (if Privacy Mode is off), and remove the bot.

Follow these rules:

  • Do not hard-code the Bot Token into scripts, config files, or screenshots, and never commit it to public repositories (GitHub's secret scanner automatically detects leaked Telegram tokens)
  • Recommended: Store the token in the device's (or asset's) server_attrs and reference it via device.server_attrs.tg_token
  • If you suspect a leak, immediately send /revoke to @BotFather to invalidate the old token and generate a new one
  • Keep the bot's Privacy Mode enabled (default) so it cannot read unrelated group messages
  • Use different bots for different environments (prod / staging / internal) to limit blast radius
  • If the bot is push-only, keep Group Privacy enabled in BotFather → Bot Settings

3. Calling Telegram from an RPC

3.1 Store Credentials in server_attrs

In Operations → Device Management → Device Detail → Attributes, add to server_attrs:

json
{
    "tg_token": "123456789:AAEhBPxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "tg_chat_id": "-1001234567890"
}

3.2 RPC Script: Markdown Message

Telegram supports Markdown and MarkdownV2. The example below uses MarkdownV2 (more robust, but requires escaping special characters):

javascript
function rpc_script({ device, params, alarms, logger }) {
    let token   = device.server_attrs?.tg_token;
    let chatId  = device.server_attrs?.tg_chat_id;
    if (!token || !chatId) {
        logger.error("telegram credentials not configured", { eui: device.eui });
        return null;
    }

    // MarkdownV2 reserved characters: _ * [ ] ( ) ~ ` > # + - = | { } . !
    let esc = (s) => String(s ?? "").replace(/([_*\[\]()~`>#+\-=|{}.!])/g, "\\$1");

    let text = [
        `*🚨 Device Alert*`,
        ``,
        `*Device:* ${esc(device.name)}`,
        `*EUI:* \`${esc(device.eui)}\``,
        `*Level:* ${esc((params.level || "").toUpperCase())}`,
        `*Description:* ${esc(params.desc || "n/a")}`,
        `*Time:* ${esc(new Date().toISOString())}`
    ].join("\n");

    return [
        {
            sleepTimeMs: 0,
            type: "axios",
            dnMsg: {
                method: "POST",
                url: `https://api.telegram.org/bot${token}/sendMessage`,
                headers: { "Content-Type": "application/json" },
                data: {
                    chat_id: chatId,
                    text: text,
                    parse_mode: "MarkdownV2",
                    disable_web_page_preview: true
                },
                timeout: 10000
            }
        }
    ];
}

💡 The TKL server must be able to reach api.telegram.org. If direct access is not available in your network environment, configure a proxy to forward the requests.

3.3 RPC Script: Plain Text Message

javascript
function rpc_script({ device, params, alarms, logger }) {
    let token  = device.server_attrs?.tg_token;
    let chatId = device.server_attrs?.tg_chat_id;
    if (!token || !chatId) return null;

    return [
        {
            sleepTimeMs: 0,
            type: "axios",
            dnMsg: {
                method: "POST",
                url: `https://api.telegram.org/bot${token}/sendMessage`,
                headers: { "Content-Type": "application/json" },
                data: {
                    chat_id: chatId,
                    text: params.content || `${device.name} alert`
                },
                timeout: 10000
            }
        }
    ];
}

4. RPC Parameter Configuration

Attr NameTypeAliasDescription
contentstringMessage contentUsed in plain-text mode
levelstringAlert levellow / mid / high / urgent
descstringAlert descriptionMarkdown-mode body text

5. Wiring into the Alert Flow

See WeCom AN §5 for the trigger script example — just change method to the name of this RPC.

6. Troubleshooting

SymptomLikely Cause
UnauthorizedWrong or revoked Bot Token
chat not foundWrong Chat ID; bot not yet in the target group; channel did not promote the bot to admin
Bad Request: can't parse entitiesUnescaped special characters in MarkdownV2
Request timeoutTKL server cannot reach api.telegram.org; increase timeout to 10000+ ms
Rate limitMax 1 message per second per chat; max 30 messages per second across all chats

7. Notes

  • Group chat IDs are negative — do not convert them to positive, or messages will go to a private chat with someone else
  • Do not put the Bot Token itself in messages (e.g. don't dump full request bodies or stack traces)
  • With MarkdownV2, escape every piece of dynamic content (device names, descriptions, etc.)
  • For critical alerts, configure multiple notification channels (e.g. Telegram + email) so network issues do not silence alerts