Introduction to Python Applications 2023 - Python Telegram Bot === ###### tags: `Python` `Python and Its Application 2023` `telegram` ## 簡介 對 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://hackmd.io/_uploads/BJatXcJVh.png) 2. 輸入 Bot 名稱。 ![](https://hackmd.io/_uploads/SkfomqkEn.png) 3. 輸入 Bot 的帳號,名稱一定要以 `bot` 結尾。 ![](https://hackmd.io/_uploads/Sk2jX5JVh.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.10/Install\ Certificates.command ``` ## Hello world ```python= from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters # Define a few command handlers. These usually take the two arguments update and # context. async def start(update, context): """Send a message when the command /start is issued.""" user = update.effective_user await update.message.reply_text(f"Hi {user.name}!") async def echo(update, context): """Echo the user message.""" await update.message.reply_text(update.message.text) def main(): """Start the bot.""" # Create the Application and pass it your bot's token. application = Application.builder().token("TOKEN").build() # on different commands - answer in Telegram application.add_handler(CommandHandler("start", start)) # on non command i.e message - echo the message on Telegram application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo)) # Run the bot until the user presses Ctrl-C application.run_polling() if __name__ == "__main__": main() ``` ### 建立 [Application](https://docs.python-telegram-bot.org/en/latest/telegram.ext.application.html#telegram.ext.Application.builder) token 參數需要給他剛剛申請的 Token,建立完後會產生 application。 ```python= application = Application.builder().token("TOKEN").build() ``` ### 建立及註冊 Handler 這邊建立兩個 Function 分別對應不同 Handler 回應,使用 [update.message.reply_text(text)](https://docs.python-telegram-bot.org/en/latest/telegram.message.html#telegram.Message.reply_text)回應使用者訊息,`update.effective_user` 來取得使用者資訊 ```python= async def start(update, context): """Send a message when the command /start is issued.""" user = update.effective_user await update.message.reply_text(f"Hi {user.name}!") async def echo(update, context): """Echo the user message.""" await update.message.reply_text(update.message.text) ``` 接著分別建立 handler 並註冊進去 application: ```python= # on different commands - answer in Telegram application.add_handler(CommandHandler("start", start)) # on non command i.e message - echo the message on Telegram application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo)) ``` 這邊示範兩種 Handler,分別為 CommandHalder() 與 MessageHandler(): - [CommandHalder](https://docs.python-telegram-bot.org/en/latest/telegram.ext.commandhandler.html) ```python= start_handler = CommandHandler("start", start) ``` 輸入 Command 時執行對應動作,例如這邊 command 參數為 'start',callback 參數為 start,因此當對 Bot 輸入 /start 時會執行 start() Function - [MessageHandler](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.messagehandler.html) ``` repeat_handler = MessageHandler(filters.TEXT & ~filters.COMMAND, echo) ``` 輸入一般訊息時回應的動作,例如這邊 filters 參數為 ilters.TEXT & ~filters.COMMAND 條件為是文字且不是 command,callback 參數為 echo,因此當對 Bot 輸入不為 command 的訊息時會執行 echo() Function。 ### [start_polling()](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.updater.html#telegram.ext.Updater.start_polling) ```python= application.run_polling() ``` ### 執行結果 ![](https://hackmd.io/_uploads/By3wjq1Vn.png) ## 可獲取的訊息資訊 我們可以從 Application 中取得使用者傳送過來 Message 的基本資訊,可參考 [Message Class](https://python-telegram-bot.readthedocs.io/en/stable/telegram.message.html),其中常用的有: - message_id - from_user ([User Clas](https://docs.python-telegram-bot.org/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 Application, CommandHandler, ContextTypes, MessageHandler, filters async def start(update, context): await update.message.reply_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.html')]]) ) def main(): """Start the bot.""" # Create the Application and pass it your bot's token. application = Application.builder().token("TOKEN").build() # on different commands - answer in Telegram application.add_handler(CommandHandler("start", start)) # Run the bot until the user presses Ctrl-C application.run_polling() if __name__ == "__main__": main() ``` 在 InlineKeyboardMarkup() 內放的結構為 [[課程 E3, 課程 Github], Python Telegram Bot 文件] 外層括號代表每個 row,內層括號代表每個 column,因此排列方式如下圖: ![](https://hackmd.io/_uploads/HJ-EZsyNh.png) ## 傳送資料 上述示範了如何產生互動按鈕以及擺放位置,接下來我們來接收按鈕傳回的資料,以不同資料觸發不同 Function,並示範三種回應方式。 ```python= from telegram import InlineKeyboardMarkup, InlineKeyboardButton from telegram.ext import Application, CommandHandler, CommandHandler, CallbackQueryHandler # Define a few command handlers. These usually take the two arguments update and # context. async def func(update, context): if update.callback_query.data == 'a': await context.bot.answer_callback_query(update.callback_query.id, '你按的是功能 A') elif update.callback_query.data == 'b': await context.bot.edit_message_text('你按的是功能 B', chat_id=update.callback_query.message.chat_id, message_id=update.callback_query.message.message_id) else: await context.bot.send_message(chat_id=update.effective_chat.id, text="你按的是功能 C") async def start(update, context): await update.message.reply_text('參考資料', reply_markup=InlineKeyboardMarkup([[ InlineKeyboardButton('功能 A', callback_data='a'), InlineKeyboardButton('功能 B', callback_data='b') ], [InlineKeyboardButton('功能 C', callback_data='c')]]) ) def main(): """Start the bot.""" # Create the Application and pass it your bot's token. application = Application.builder().token("TOKEN").build() # on different commands - answer in Telegram application.add_handler(CommandHandler("start", start)) application.add_handler(CallbackQueryHandler(func)) # Run the bot until the user presses Ctrl-C application.run_polling() if __name__ == "__main__": main() ``` ### [CallbackQueryHandler](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.callbackqueryhandler.html) 我們使用 CallbackQueryHandler 接取回傳資料,可以從 update.callback_query.data 收取我們從按鈕傳送的 callback_data 資料。 ```python= application.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://hackmd.io/_uploads/rJfD4iJNh.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://hackmd.io/_uploads/SkF-BjJEh.png) #### [bot.send_message](https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html#telegram.Bot.send_message) ```python= context.bot.send_message(chat_id=update.effective_chat.id, text="你按的是功能 C") ``` 此種方式上面有出現過,正常回應訊息。 ![](https://hackmd.io/_uploads/BJtyLikVn.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) ## 參考資料 - [Telegram Bot API](https://core.telegram.org/bots/api)