Python Telegram Bot 教學 (by 陳達仁)
===
## BotFather
找 [@BotFather](https://t.me/BotFather) 申請一個 Bot。
1. /newbot
2. 輸入名稱
3. 輸入 username
4. 記下 token
![New Bot](https://i.imgur.com/NqKAsLK.png)
## hello, world
執行這個程式,注意 `'YOUR TOKEN HERE'` 的地方請填入前面得到的 Token。
```python=
from telegram.ext import Updater, CommandHandler
def hello(bot, update):
update.message.reply_text(
'hello, {}'.format(update.message.from_user.first_name))
updater = Updater('YOUR TOKEN HERE')
updater.dispatcher.add_handler(CommandHandler('hello', hello))
updater.start_polling()
updater.idle()
```
用 Bot 的 username 或是 BotFather 給的連結可以找到前面建立的 Bot。
對它輸入 `/hello`。
![hello](https://i.imgur.com/eeRxLC3.png)
## Command Handler 可從 update 獲得的資訊
- update
- update_id
- message
- message_id
- from_user:發訊人
- id
- first_name
- last_name
- full_name
- username
- chat:訊息所在的聊天室
- id
- type
- text:訊息內容
[更多資訊](https://python-telegram-bot.readthedocs.io/en/stable/telegram.message.html)
## 傳訊息
- bot.send_message(chat_id, text)
- update.message.reply_text(text):Shortcut for `bot.send_message(update.message.chat_id, text)`
[更多功能](https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html)
## 範例 - 語錄 Bot
```python=
import random, os
from telegram.ext import Updater, CommandHandler
# 把語錄檔案載入
if os.path.exists('sentences.txt'):
with open('sentences.txt') as FILE:
sentences = [sentence.strip() for sentence in FILE]
else:
sentences = []
def add(bot, update):
print('from user:', update.message.from_user.id)
# 限制只有特定人才能新增語錄
# if update.message.from_user.id == YOUR_USER_ID_HERE:
if True:
sentence = update.message.text[5:].replace('\n', ' ')
sentences.append(sentence)
with open('sentences.txt', 'a') as FILE:
print(sentence, file=FILE)
update.message.reply_text('已加入:' + sentence)
def say(bot, update):
if sentences:
update.message.reply_text(random.choice(sentences))
else:
update.message.reply_text('I have no words.')
updater = Updater('YOUR TOKEN HERE')
updater.dispatcher.add_handler(CommandHandler('add', add))
updater.dispatcher.add_handler(CommandHandler('say', say))
updater.start_polling()
updater.idle()
```
![語錄](https://i.imgur.com/G0MnIp1.png)
## 互動按鈕
`send_message` 加上 `reply_markup = InlineKeyboardMarkup(...)` 就會在該訊息附上按鈕。
```python=
from telegram.ext import Updater, CommandHandler
from telegram import InlineKeyboardMarkup, InlineKeyboardButton
def start:
bot.send_message(chat_id, '參考資料',
reply_markup = InlineKeyboardMarkup([[
InlineKeyboardButton('課程網站', url = 'https://github.com/mzshieh/pa19spring'),
InlineKeyboardButton('Documentation', url = 'https://python-telegram-bot.readthedocs.io/en/stable/index.html')]]))
# ...
```
![](https://i.imgur.com/NCuLrN6.png)
除了 `url` 以外,也可以用 `callback_data` 來讓 Bot 知道哪個按鈕被按了。
```python=
from random import randint
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
from telegram import InlineKeyboardMarkup, InlineKeyboardButton
def start(bot, update):
a, b = randint(1, 100), randint(1, 100)
update.message.reply_text('{} + {} = ?'.format(a, b),
reply_markup = InlineKeyboardMarkup([[
InlineKeyboardButton(str(s), callback_data = '{} {} {}'.format(a, b, s)) for s in range(a + b - randint(1, 3), a + b + randint(1, 3))
]]))
def answer(bot, update):
a, b, s = [int(x) for x in update.callback_query.data.split()]
if a + b == s:
update.callback_query.edit_message_text('你答對了!')
else:
update.callback_query.edit_message_text('你答錯囉!')
updater = Updater('YOUR TOKEN HERE')
updater.dispatcher.add_handler(CommandHandler('start', start))
updater.dispatcher.add_handler(CallbackQueryHandler(answer))
updater.start_polling()
updater.idle()
```
![70 + 90 = ?](https://i.imgur.com/ht4CPIH.png)
![你答對了!](https://i.imgur.com/SXfdpqZ.png)
## Callback Query Handler 可從 update 獲得的資訊
- update
- update_id
- callback_query
- from_user
- 略
- message:按鈕依附的 message
- 略
- data:建立 InlineKeyboardButton 時傳入的 callback_data
[更多資訊](https://python-telegram-bot.readthedocs.io/en/stable/telegram.callbackquery.html)
## 回應 Callback Query
- bot
- answer_callback_query(callback_query_id, text):會顯示文字在畫面中間。
- edit_message_text(chat_id = string, message_id = string, text):修改文字,會同時清除按鈕。
- update.callback_query
- answer(text):Shortcut for `bot.answer_callback_query(update.callback_query.id, text)`
- edit_message_text(text):Shortcut for `bot.edit_message_text(chat_id=update.callback_query.message.chat_id, message_id=update.callback_query.message.message_id, text`
[更多功能](https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html)
## 範例 - 剪刀石頭布
```python=
import random
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
from telegram import InlineKeyboardMarkup, InlineKeyboardButton
hands = ['rock', 'paper', 'scissors']
emoji = {
'rock': '👊',
'paper': '✋',
'scissors': '✌️'
}
def start(bot, update):
update.message.reply_text('剪刀石頭布!',
reply_markup = InlineKeyboardMarkup([[
InlineKeyboardButton(emoji, callback_data = hand) for hand, emoji in emoji.items()
]]))
def judge(mine, yours):
if mine == yours:
return '平手'
elif (hands.index(mine) - hands.index(yours)) % 3 == 1:
return '我贏了'
else:
return '我輸了'
def play(bot, update):
try:
mine = random.choice(hands)
yours = update.callback_query.data
update.callback_query.edit_message_text('我出{},你出{},{}!'.format(emoji[mine], emoji[yours], judge(mine, yours)))
except Exception as e:
print(e)
updater = Updater('YOUR TOKEN HERE')
updater.dispatcher.add_handler(CommandHandler('start', start))
updater.dispatcher.add_handler(CallbackQueryHandler(play))
updater.start_polling()
updater.idle()
```
![剪刀石頭布!](https://i.imgur.com/WCI88EP.png)
![我輸了!](https://i.imgur.com/Y4cNeFq.png)
## Reference
https://python-telegram-bot.readthedocs.io/en/stable/index.html