Create a Telegram EchoBot

Category Tech

最近當助教要出一個 Telegram Bot 相關的 Project
先來寫一篇簡單的教學,減少之後的問題 XDD

如果對 Chat Bot 的基本運作概念不太熟
可以參考[Bot] Introduction to Chatbot

What is Telegram

在台灣,好像還沒有那麼多人用 Telegram
簡單來說就跟 Facebook Messenger 或 Line 這類的 IM 差不多

Why Telegram

至於這次為什麼要選用 Telegram 麻
是因為上次有聽其他開發者說 Telegram Bot 提供相當多的功能
就想說來試試看

Web Framework

上次寫 Line EchoBot 的教學是用 django
這次來試試 Flask

Source Code 一樣放在 GitHub 上
這篇文章會用minimal-flask branch 當範例
只有 31 行 Code,比較容易理解

master 上也是用 Flask
只是架構比較複雜,有試一下 Flask 的 blueprint,之後可能還會多加一些奇怪的功能 xd

Apply a telegram bot

首先當然必須要有Telegram 的帳號
再來要加BotFather 為好友

跟他說 /newbot
接著他會問你,Bot 的 name 跟 username

  • name 是 Bot 在聯絡人資訊顯示的名稱
  • username 則比較像 id 的概念,而且一定要用 Bot 結尾

之後就會得到剛申請 Bot 的 API Token
然後你就可以從 https://telegram.me/<bot_username> 找到剛申請的 bot

Telegram Bot API Wrapper

開發上,我使用的是python-telegram-bot

pip install python-telegram-bot

它其中一點設計的很不錯的是
Telegram API 的命名是 CamelCase (e.g. sendMessage)
但 Python 的命名 convention 卻是 lowercase_separated_by_underscores (e.g. send_message)
而它是兩種都支援

Star Programming

先附上全部的 code,後面再慢慢解釋

import sys

import telegram
from flask import Flask, request


app = Flask(__name__)
bot = telegram.Bot(token="Your API Token")


def _set_webhook():
    status = bot.set_webhook("https://Your URL/hook")
    if not status:
        print("Webhook setup failed")
        sys.exit(1)


@app.route("/hook", methods=["POST"])
def webhook_handler():
    if request.method == "POST":
        update = telegram.Update.de_json(request.get_json(force=True), bot)

        text = update.message.text

        update.message.reply_text(text)
    return "ok"


if __name__ == "__main__":
    _set_webhook()
    app.run()

Setup

其中有兩個地方,必須要改成自己的設定

1. API Token

bot = telegram.Bot(token="Your API Token")

Your API Token 要改成剛剛取得的 API Token

2. Webhook URL

_set_webhook 中的 Your URL

statue = bot.set_webhook("https://Your URL/hook")

這裡的 URL 就是設定成你這個 Bot Server 的 URL
不過 Telegram 一樣要求必須要是 https
最簡單的方式就是使用 ngrok
(ngrok 的使用在[Bot] Line Echo Bot on Django 最後面有介紹 )
平常測試使用 ngrok 就很足夠了
之後要 production 的時候,在 deploy 到適當的 server 就好了

_set_webhook

Telegram 有兩種接收訊息的方式
隨時去監聽的 webhook,和主動去要求更新的 get_updates
這裡使用的是 webhook

這個 function 先設定 Bot 的 webhook URL,如果設定失敗就直接終止程式
也就是告訴 Telegram 要把 Bot 收到的訊息傳到哪
set_webhook 前面要有個 _的原因是我不希望它被其他的 code 使用
但 Python 本身並不支援 Private 的概念,而是慣例上在前面加一個底線

def _set_webhook():
    status = bot.set_webhook("https://Your URL/hook")
    if not status:
        print("Webhook setup failed")
        sys.exit(1)

在最後 run Flask app 前,要先把 webhook URL 設定好

if __name__ == "__main__":
    _set_webhook()
    app.run()

wehook_handler

這裡就是 bot 收到訊息要怎麼處理

@app.route("/hook", methods=["POST"])
def webhook_handler():
    if request.method == "POST":
        update = telegram.Update.de_json(request.get_json(force=True), bot)

        text = update.message.text

        update.message.reply_text(text)
    return "ok"

app.route 這個 decorator 是 Flask 的語法
表示 https:/Your URL/hook 會導到這個 function,而它只能接受 POST
這裡設定的 /hook 也就是為什麼在 _set_webhook 中的 URL 最後面必須有 /hook

另外還可以發現 webhook_handler 是不帶任何參數的
跟 django 不同的是
Flask 把 request 這種幾乎所有 view function 都會用到的參數直接變成全域可讀取的變數
也就是最一開始的

from flask import Flask, request

接下來 webhook_handler 內做的就只是把收到的訊息轉成 update
再從裡面讀到對方傳來的 text
最後用 reply_text 回傳同樣的 text 回去


這是最簡單的 Telegram Bot
不過我覺得開始學一個東西,還是會希望能在最短時間看到點東西,再慢慢專研
接下來可以從python-telegram-botwiki,試更多 Telegram Bot 的功能

Reference