# Python Telegram Bot ###### tags: `Introduction to Python Applications` ## 簡介 對 Telegram Bot 的操作可以使用 HTTP 方式直接操作 [Telegram Bot API](https://core.telegram.org/bots/api),但直接操作步驟繁瑣,因此有許多 [Library](https://core.telegram.org/bots/samples) 針對 API 進行包裝,讓各個程式語言使用起來更簡單,這邊我們主要介紹 `python-telegram-bot` 套件。 ## 申請 Bot 找 [@BotFather](https://t.me/botfather) 申請一個 Bot。 1. 首先輸入 /newbot 申請 Bot。 ![](https://i.imgur.com/9cToPlF.png) 2. 輸入 Bot 名稱。 ![](https://i.imgur.com/lre4XUK.png) 3. 輸入 Bot 的帳號,名稱一定要以 `bot` 結尾。 ![](https://i.imgur.com/cAvqLey.png) 4. 最後會取得 Bot 的 ID 以及 API 的 Token,以範例來說至 `https://t.me/yuching_test_bot` 即可加 Bot 好友,`1780604788:AAElXw3Kv0mATlJCtmu3_N3-jn9tYDrH0pQ` 為 API 的 Token,Token 為往後對 API 操作驗證的 Key,請妥善保存。 ## 安裝 python-telegram-bot 使用 pip 進行安裝 ``` pip3 install python-telegram-bot ``` macOS 使用者會有 CERTIFICATE_VERIFY_FAILED 錯誤的問題,須執行: ``` /Applications/Python\ 3.9/Install\ Certificates.command ``` ## Hello world 大致參考官方 [tutorial](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-Your-first-Bot#your-first-bot-step-by-step): ```python= from telegram.ext import Filters, Updater, CommandHandler, MessageHandler updater = Updater(token='TOKEN', use_context=True) dispatcher = updater.dispatcher def init(update, context): context.bot.send_message( chat_id=update.effective_chat.id, text="Hello {0}!".format(update.message.from_user.full_name)) def repeat(update, context): context.bot.send_message( chat_id=update.effective_chat.id, text=update.message.text * 2) start_handler = CommandHandler('start', init) dispatcher.add_handler(start_handler) repeat_handler = MessageHandler(Filters.text & (~Filters.command), repeat) dispatcher.add_handler(repeat_handler) updater.start_polling() ``` ### 建立 [Updater](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.updater.html#telegram.ext.updater.Updater) `token` 參數需要給他剛剛申請的 Token,建立完後會產生 Dispatcher。 ``` updater = Updater(token='TOKEN', use_context=True) dispatcher = updater.dispatcher ``` ### 建立及註冊 Handler 這邊建立兩個 Function 分別對應不同 Handler 回應,使用 [`context.bot.send_message(chat_id, text)`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html#telegram.Bot.send_message) 回應使用者訊息,也可以使用 `update.message.reply_text(text)` 省略輸入 `chat_id` 替代。 `init()` 為傳送 `Hello 使用者全名` 字串,`repeat()` 為傳送重複兩次使用者輸入字串。 ```python= def init(update, context): context.bot.send_message( chat_id=update.effective_chat.id, text="Hello {0}!".format(update.message.from_user.full_name)) def repeat(update, context): context.bot.send_message( chat_id=update.effective_chat.id, text=update.message.text * 2) ``` 接著分別建立 Handler 並註冊進去 Dispatcher: ```python= start_handler = CommandHandler('start', init) dispatcher.add_handler(start_handler) repeat_handler = MessageHandler(Filters.text & (~Filters.command), repeat) dispatcher.add_handler(repeat_handler) ``` 這邊示範兩種 Handler,分別為 `CommandHalder()` 與 `MessageHandler()`: * [CommandHalder](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.commandhandler.html) ```python= start_handler = CommandHandler('start', init) ``` 輸入 Command 時執行對應動作,例如這邊 command 參數為 `'start'`,callback 參數為 `init`,因此當對 Bot 輸入 `/start` 時會執行 `init()` Function 回傳 `Hello 使用者全名` 字串。 * [MessageHandler](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.messagehandler.html) ```python= repeat_handler = MessageHandler(Filters.text & (~Filters.command), repeat) ``` 輸入一般訊息時回應的動作,例如這邊 [filters](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.filters.html) 參數為 `Filters.text & (~Filters.command)` 條件為是文字且不是 command,callback 參數為 `repeat`,因此當對 Bot 輸入不為 command 的訊息時會執行 `repeat()` Function 回傳對應的輸入文字。 ### [start_polling()](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.updater.html#telegram.ext.Updater.start_polling) 最後執行 start_polling() 啟動 Bot。 ```python= updater.start_polling() ``` ### 執行結果 ![](https://i.imgur.com/z9BX0Uy.png) ## 可獲取的訊息資訊 我們可以從 Updater 中取得使用者傳送過來 Message 的基本資訊,可參考 [Message Class](https://python-telegram-bot.readthedocs.io/en/stable/telegram.message.html),其中常用的有: * message_id * from_user ([User Class](https://python-telegram-bot.readthedocs.io/en/stable/telegram.user.html#telegram.User)) * id * first_name * last_name * full_name * username * date * chat ([Chat Class](https://python-telegram-bot.readthedocs.io/en/stable/telegram.chat.html#telegram.Chat)) * id * type * title * text 例如我們要拿取使用者的全名,我們可以使用 `update.message.from_user.full_name` 以及對話的文字內容我們可以使用 `update.message.text`。 ## 互動按鈕 ### 建立按鈕 前面介紹了收 Command 和過濾特定訊息的的互動方式,接著我們來看互動按鈕的部分。 在 `bot.send_message()` 設定 `reply_markup` 為 [ReplayMarkup Class](https://python-telegram-bot.readthedocs.io/en/stable/telegram.replymarkup.html#telegram.ReplyMarkup),裡面再依 row 和 column 分別放置 `(List[List[InlineKeyboardButton]])` 的 [InlineKeyboardButton](https://python-telegram-bot.readthedocs.io/en/stable/telegram.inlinekeyboardmarkup.html#telegram.InlineKeyboardMarkup.inline_keyboard) 按鈕,例如我們新增兩個簡單的連結按鈕: ```python= from telegram import InlineKeyboardMarkup, InlineKeyboardButton from telegram.ext import Updater, CommandHandler updater = Updater(token='TOKEN', use_context=True) dispatcher = updater.dispatcher def init(update, context): context.bot.send_message( chat_id=update.effective_chat.id, text='參考資料', reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton( '課程 E3', url='https://e3.nycu.edu.tw/course/view.php?id=23863'), InlineKeyboardButton( '課程 Github', url='https://github.com/mzshieh/pa21spring') ], [InlineKeyboardButton( 'Python Telegram Bot 文件', url='https://python-telegram-bot.readthedocs.io/en/stable/index.htmlF')]]) ) start_handler = CommandHandler('start', init) dispatcher.add_handler(start_handler) updater.start_polling() ``` 在 `InlineKeyboardMarkup()` 內放的結構為 `[[課程 E3, 課程 Github], Python Telegram Bot 文件]` 外層括號代表每個 row,內層括號代表每個 column,因此排列方式如下圖: ![](https://i.imgur.com/hFo2Sh0.png) ### 傳送資料 上述示範了如何產生互動按鈕以及擺放位置,接下來我們來接收按鈕傳回的資料,以不同資料觸發不同 Function,並示範三種回應方式。 ```python= from telegram import InlineKeyboardMarkup, InlineKeyboardButton from telegram.ext import Updater, CommandHandler, CallbackQueryHandler updater = Updater( token='TOKEN', use_context=True) dispatcher = updater.dispatcher def func(update, context): if update.callback_query.data == 'a': context.bot.answer_callback_query(update.callback_query.id, '你按的是功能 A') elif update.callback_query.data == 'b': context.bot.edit_message_text('你按的是功能 B', chat_id=update.callback_query.message.chat_id, message_id=update.callback_query.message.message_id) else: context.bot.send_message(chat_id=update.effective_chat.id, text="你按的是功能 C") def init(update, context): context.bot.send_message( chat_id=update.effective_chat.id, text='參考資料', reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton('功能 A', callback_data='a'), InlineKeyboardButton('功能 B', callback_data='b') ], [InlineKeyboardButton('功能 C', callback_data='c')]]) ) start_handler = CommandHandler('start', init) dispatcher.add_handler(start_handler) dispatcher.add_handler(CallbackQueryHandler(func)) updater.start_polling() ``` ### [CallbackQueryHandler](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.callbackqueryhandler.html) 我們使用 CallbackQueryHandler 接取回傳資料,可以從 `update.callback_query.data` 收取我們從按鈕傳送的 `callback_data` 資料。 ```python= dispatcher.add_handler(CallbackQueryHandler(func)) ``` ### 回應 CallbackQuery 接著示範三種回應方式,分別對應到功能 A、B、C 按鈕上,分別是 * [bot.answer_callback_query](https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html#telegram.Bot.answer_callback_query) ```python= context.bot.answer_callback_query( update.callback_query.id, '你按的是功能 A') ``` 此種方式為在上方顯示。 ![](https://i.imgur.com/gngUnLS.png) * [bot.edit_message_text](https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html#telegram.Bot.edit_message_text) ```python= context.bot.edit_message_text('你按的是功能 B', chat_id=update.callback_query.message.chat_id, message_id=update.callback_query.message.message_id) ``` 此種方式為修改為新訊息。 ![](https://i.imgur.com/BHRYjoT.png) * [bot.send_message](https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html#telegram.Bot.send_message) ```python= context.bot.answer_callback_query( update.callback_query.id, '你按的是功能 C') ``` 此種方式上面有出現過,正常回應訊息。 ![](https://i.imgur.com/XRVQdJx.png) * 其他還有 [`sendPhoto`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html#telegram.Bot.send_photo) 、 [`sendAudio`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html#telegram.Bot.send_audio) 等等回應方式,詳細資訊請參考 [Document](https://python-telegram-bot.readthedocs.io/en/stable/telegram.callbackquery.html)。 ## 參考資料 * [Python Telegram Bot Document](https://python-telegram-bot.readthedocs.io/en/stable/index.html) * [Python Telegram Bot 教學 (by 陳達仁) ](https://hackmd.io/@truckski/HkgaMUc24?type=view) * [Telegram Bot API](https://core.telegram.org/bots/api)