# 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。

2. 輸入 Bot 名稱。

3. 輸入 Bot 的帳號,名稱一定要以 `bot` 結尾。

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()
```
### 執行結果

## 可獲取的訊息資訊
我們可以從 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,因此排列方式如下圖:

### 傳送資料
上述示範了如何產生互動按鈕以及擺放位置,接下來我們來接收按鈕傳回的資料,以不同資料觸發不同 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')
```
此種方式為在上方顯示。

* [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)
```
此種方式為修改為新訊息。

* [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')
```
此種方式上面有出現過,正常回應訊息。

* 其他還有 [`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)