2026年2月21日 星期六

ESP32 Telegram 指令選單(Bot Commands)

ESP32 Telegram 指令選單(Bot Commands) 



/*******************************************************************
 *******************************************************************/
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>

// Wifi network station credentials
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
// Telegram BOT Token (Get from Botfather)
#define BOT_TOKEN "7738940254:AAHbrWu9ovb1BKPQyWsbNSjNxfCGCrEWU-o"
const unsigned long BOT_MTBS = 1000; // mean time between scan messages

unsigned long bot_lasttime; // last time messages' scan has been done
WiFiClientSecure secured_client;
UniversalTelegramBot bot(BOT_TOKEN, secured_client);

void handleNewMessages(int numNewMessages)
{
  Serial.print("handleNewMessages ");
  Serial.println(numNewMessages);
 
  String answer;
  for (int i = 0; i < numNewMessages; i++)
  {
    telegramMessage &msg = bot.messages[i];
    Serial.println("Received " + msg.text);
    if (msg.text == "/help")
      answer = "So you need _help_, uh? me too! use /start or /status";
    else if (msg.text == "/start")
      answer = "Welcome my new friend! You are the first *" + msg.from_name + "* I've ever met";
    else if (msg.text == "/status")
      answer = "All is good here, thanks for asking!";
    else
      answer = "Say what?";

    bot.sendMessage(msg.chat_id, answer, "Markdown");
  }
}

void bot_setup()
{
  const String commands = F("["
                            "{\"command\":\"help\",  \"description\":\"Get bot usage help\"},"
                            "{\"command\":\"start\", \"description\":\"Message sent when you open a chat with a bot\"},"
                            "{\"command\":\"status\",\"description\":\"Answer device current status\"}" // no comma on last command
                            "]");
  bot.setMyCommands(commands);
  //bot.sendMessage("25235518", "Hola amigo!", "Markdown");
}

void setup()
{
  Serial.begin(115200);
  Serial.println();

  // attempt to connect to Wifi network:
  Serial.print("Connecting to Wifi SSID ");
  Serial.print(WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  secured_client.setCACert(TELEGRAM_CERTIFICATE_ROOT); // Add root certificate for api.telegram.org
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    delay(500);
  }
  Serial.print("\nWiFi connected. IP address: ");
  Serial.println(WiFi.localIP());

  Serial.print("Retrieving time: ");
  configTime(0, 0, "pool.ntp.org"); // get UTC time via NTP
  time_t now = time(nullptr);
  while (now < 24 * 3600)
  {
    Serial.print(".");
    delay(100);
    now = time(nullptr);
  }
  Serial.println(now);

  bot_setup();
}

void loop()
{
  if (millis() - bot_lasttime > BOT_MTBS)
  {
    int numNewMessages = bot.getUpdates(bot.last_message_received + 1);

    while (numNewMessages)
    {
      Serial.println("got response");
      handleNewMessages(numNewMessages);
      numNewMessages = bot.getUpdates(bot.last_message_received + 1);
    }

    bot_lasttime = millis();
  }
}

這段程式碼展示了 Telegram 機器人一個非常實用的功能:「指令選單(Bot Commands)」 以及 「Markdown 字體格式化」。它不僅能根據指令回應,還能在 Telegram 的輸入欄位自動顯示指令列表。

以下是逐行詳細說明:


1. 指令選單設定:bot_setup

這是本範例的核心亮點。它定義了機器人在 Telegram 介面中顯示的「指令列表」。

C++
void bot_setup() {
  // 定義 JSON 格式的指令列表,包含 command (指令) 與 description (說明)
  const String commands = F("["
                            "{\"command\":\"help\",  \"description\":\"Get bot usage help\"},"
                            "{\"command\":\"start\", \"description\":\"Message sent when you open a chat with a bot\"},"
                            "{\"command\":\"status\",\"description\":\"Answer device current status\"}" 
                            "]");
  // 將這個列表同步到 Telegram 伺服器
  bot.setMyCommands(commands);
}
  • 功能:當你在 Telegram 輸入 / 時,會自動彈出 helpstartstatus 選單。

  • F() 巨集:將字串儲存在 Flash 記憶體中,節省 RAM。


2. 訊息處理邏輯:handleNewMessages

這裡使用了結構化的邏輯來根據指令產生不同的「Markdown」格式回應:

C++
void handleNewMessages(int numNewMessages) {
  String answer;
  for (int i = 0; i < numNewMessages; i++) {
    telegramMessage &msg = bot.messages[i]; // 取得訊息物件引用

    // 根據文字指令進行判斷
    if (msg.text == "/help")
      answer = "So you need _help_, uh? me too!"; // _文字_ 代表斜體
    else if (msg.text == "/start")
      answer = "Welcome my new friend! You are the first *" + msg.from_name + "*"; // *文字* 代表粗體
    else if (msg.text == "/status")
      answer = "All is good here, thanks for asking!";
    else
      answer = "Say what?";

    // 傳送訊息並指定使用 "Markdown" 模式解析文字格式
    bot.sendMessage(msg.chat_id, answer, "Markdown");
  }
}
  • Markdown 格式

    • *粗體文字* $\rightarrow$ 粗體文字

    • _斜體文字_ $\rightarrow$ 斜體文字


3. 初始化與網路連線:setup

負責環境準備,讓 ESP32 具備溝通能力:

  1. 連線 WiFi:使用 Wokwi 模擬器環境。

  2. 設定 SSL 憑證:Telegram API 是加密的 HTTPS,必須載入根憑證。

  3. 同步網路時間 (NTP):這是機器人運作的關鍵。如果 ESP32 的時間不準確,與 Telegram 伺服器建立 SSL 握手時會失敗。

  4. 呼叫 bot_setup():在開機時就將指令選單註冊到 Telegram。


4. 主迴圈輪詢:loop

C++
void loop() {
  if (millis() - bot_lasttime > BOT_MTBS) { // 每秒檢查一次
    // 獲取新訊息數量
    int numNewMessages = bot.getUpdates(bot.last_message_received + 1);

    while (numNewMessages) { 
      handleNewMessages(numNewMessages); // 處理並回傳訊息
      numNewMessages = bot.getUpdates(bot.last_message_received + 1);
    }
    bot_lasttime = millis();
  }
}

💡 程式亮點小結

  1. 專業感:透過 setMyCommands 讓使用者不需要背指令,點選選單即可操作。

  2. 可讀性:回應訊息中使用了 Markdown(粗體、斜體),讓資訊呈現更具層次感。

  3. 引用技術telegramMessage &msg = bot.messages[i] 使用引用 (&) 可以減少記憶體複製,提高程式效能。

沒有留言:

張貼留言

ESP32 Telegram 指令選單(Bot Commands)

ESP32 Telegram 指令選單(Bot Commands)  /*******************************************************************  ***********************************...