2025年4月6日 星期日

Python 學習筆記 : 用 Telegram Bot 取代 LINE Notify (一)

由於 LINE Notify 服務已於 2025-04-01 終止, 必須為我的眾多爬蟲找尋替代方案, 比較之後覺得還是改用免費的 Telegram 好了, 實作方式也比 LINE 簡單得多, 作法參考 :


Telgram Bot 的優點摘要整理如下 :
  • 免費且無流量限制, 提供完整的通知服務
  • Telegram Bot 設定步驟簡單, 使用 HTTP 呼叫 API 即可傳遞訊息
  • 支援文字, 圖片, 影片, 文件等多媒體格式訊息
  • 支持自定義的訊息格式, 按鈕回應和群組通知, 可應用於即時提醒, 客服回應, 排程通知等
雖然 Telegram 是免費, 但為了防止濫用仍有如下用量之限制 :
  • 每個 Bot 每秒最多只能發出 30 個訊息
  • 每個 Bot 每分鐘最多只能發出 20 個訊息給每個聊天室 (包含群組和頻道)
  • 每個 Bot 每小時最多只能發出 1000 個訊息給每個聊天室 (包含群組和頻道)
  • 每個 Bot 每天最多只能發出 50000 個訊息
參考書籍 :


其他 Telegram Bot 教學 :



1. 安裝 Telegram App : 

建立 Telgram 機器人之前要先安裝 Telgram App, 它就像 LINE 一樣是接收/傳送/顯示即時訊息的 UI 介面. 在手機開啟 Google Play 或 App Store 搜尋 Telegram 後安裝 App (注意, 此 App 沒有中文介面, 用預設的英文介面就好) : 




開啟 Telegram App :




接下來會傳簡訊驗證手機號碼, 按 Continue 繼續 :




要求撥打電話給你, 按 "允許" : 




選擇國家 Taiwan, 輸入手機號碼 :




要求允許讀取通話紀錄以便自動讀取驗證簡訊, 按 Continue : 




輸入姓名按右下角的右箭頭 : 




允許 Telegram 傳送通知 :




這樣就完成 App 的安裝了 : 




按左上角的三條槓點選 Contacts :




按 Continue 允許 Telegram 讀取通訊錄 :




按允許 : 




這樣通訊錄上已有 Telegram 帳戶的人就會出現在 Telegram 聯絡人列表上了 :




可以按右下角的 + 鈕新增連絡人. 


2. 建立 Telegram Bot 取得 API token 與 Chat ID : 

建立 Telgram 機器人先安裝 Telgram App, 到 Google Play 或 App Store 搜尋 Telegram 後安裝 (此 App 沒有中文介面, 用預設的英文介面就好), 完成後執行此 App, 按右上角的搜尋鈕 :




輸入 BatFather 會出現一堆 BatFather 名稱的帳號, 找有藍勾勾且 users 數超過 275 萬的那個才是正宗機器人之父, 其他全部是假的不要點 (如果找不到可以只輸入部分例如 BotFa) :




點選 BatFather 後進入其聊天室, 按底下的 START 鈕 :




在最底下輸入 /newbot 指令 (Telegram 的指令都是用 / 開始) 後按右邊發送鈕建立機器人 :





這時他會問你這個機器人要取甚麼名字 (name), 在底下輸入名稱後按右邊發送鈕 :




接下來要為機器人取一個使用者名稱 (username), 在底下輸入名稱後按右邊發送鈕 :




結果 twstock_bot 這個 username 已經被別人用了, 所以我改用 twstock168_bot :




收到如下回應表示 username 已設定完成, 回應裡面的 HTTP API 就是呼叫 Telegram API 時必須提供的權杖 (token), 請將其複製到文字檔中儲存備用 : 




接下來還必須取得聊天室的識別碼 (ID), 先回到 App 首頁, 按右上方的搜尋鈕, 輸入 @userinfobot  搜尋 userinfobot 這個系統機器人, 注意, 必須是如下圖所示的 userinfobot@userinfobot 這個才是正宗, 其他都是假的 (不要亂點, 以免帳號被盜) :



進入 userinfobot 機器人聊天室後按底下的 START 鈕, 它就會傳回你的聊天室 ID : 





請將此 ID 複製貼到文字檔案中儲存備用, 例如我是將其與其他 API Key 或 Token 一起放在目前工作目錄下的純文字檔 .env 裡面的 TELEGRAM_TOKEN 與 TELEGRAM_ID 變數中 : 




然後用 dotenv 套件將其讀進環境變數中, 然後用 os 模組讀取環境變數 : 

>>> from dotenv import load_dotenv   
>>> load_dotenv()    
True 
>>> import os    
>>> token=os.environ.get('TELEGRAM_TOKEN')   
>>> chat_id=os.environ.get('TELEGRAM_ID')     

接下來只要在呼叫 Telegram API 時將 token 與 chat_id 傳給伺服器, 認證成功就可以將訊息傳出去了. 


3. 利用 HTTP 的 GET/POST 方法傳送訊息 : 

Telegram Bot API 是一個 RESTful API, 可以透過 HTTP 的 GET 或 POST 方法與 Telegram 的伺服器互動, 其介面簡潔易用, 非常適合專案開發, IoT 應用, 以及伺服器監控等用途. 

使用 GET 方法時變數是全部放在網址中傳遞, 網址格式如下 :

https://api.telegram.org/bot{token}/sendMessage?chat_id={chat_id}&text={text} 

可見 Telegram API 的參數有兩個 (網址中 ? 後面的就是攜帶的變數) : chat_id (聊天室識別碼) 與 text (要傳的訊息). 使用 requests 的 get() 函式時也可以將參數放在字典中, 然後將其傳給 params 變數. 

使用 POST 方法時變數 (chat_id 與 text 訊息) 是放在 body 中傳遞, 網址格式如下 :

https://api.telegram.org/bot{token}/sendMessage

變數則放在字典中 :

data={
    'chat_id': chat_id,
    'text': text
    }

然後在呼叫 post() 函式時將此字典傳給 data 參數即可 (注意是 data 不是 params). 

首先匯入 requests 模組 :呼叫 Telegram Bot API 來傳送即時訊息 :

>>> import requests

定義要傳送的訊息 text 並且將它與 token 以及 chat_id 一起嵌入 API 的網址變數 url 中 :

>>> text='Hello! 你好'  
>>> url=f'https://api.telegram.org/bot{token}/sendMessage?chat_id={chat_id}&text={text}'    

這裡用 f 字串嵌入上面用 dotenv 載入的環境變數 token (Telegram 權杖) 與聊天室識別碼 chat_id, 輸出 url 內容如下 :

>>> url   
'https://api.telegram.org/bot<權杖>/sendMessage?chat_id=<聊天室識別碼>&text=Hello! 你好'

然後呼叫 requests 的 get() 函式向 Telegram 伺服器提出 GET 請求就會將訊息 text 傳送到聊天室了, 它會傳回一個類 JSON 格式的 Response 物件, 呼叫其 json() 方法可以轉成字典 :

>>> r=requests.get(url)   
>>> type(r)   
<class 'requests.models.Response'>
>>> data=r.json()     
>>> data   
{'ok': True, 'result': {'message_id': 4, 'from': {'id': 7938146214, 'is_bot': True, 'first_name': 'twstock', 'username': 'twstock168_bot'}, 'chat': {'id': <MyChatID>, 'first_name': '77960', 'type': 'private'}, 'date': 1743950717, 'text': 'Hello! 你好'}}
>>> type(data)   
<class 'dict'>

可見傳回值就是與這次傳送相關的資訊, 其中 from 屬性的 id 就是權杖的編號. 這時去查看 Telegram 會發現收到機器人轉發的訊息 : 




也可以將變數放在一個字典中, 然後在呼叫 requests.get() 時將字典傳給 params 參數 :

>>> params={'chat_id': chat_id, 'text': text}     
>>> params    
{'chat_id': '<MyChatID>', 'text': 'Hello! 你好'}

這時網址就不用攜帶變數了 : 

>>> url=f'https://api.telegram.org/bot{token}/sendMessage'    
>>> r=requests.get(url, params=params)   
>>> r.json()   
{'ok': True, 'result': {'message_id': 5, 'from': {'id': 7938146214, 'is_bot': True, 'first_name': 'twstock', 'username': 'twstock168_bot'}, 'chat': {'id': <MyChatID>, 'first_name': '77960', 'type': 'private'}, 'date': 1743951580, 'text': 'Hello! 你好'}}

這樣就收到了一則相同的訊息 :




也可以用 POST 方法傳送訊息, 這時變數字典要用 data 傳送 (不是 params) :

>>> data={'chat_id': chat_id, 'text': text}  
>>> data  
{'chat_id': '<MyChatID>', 'text': 'Hello! 你好'}  

這個 data 變數字典與上面的 params 內容是完全相同的, 只是為了遷就 post() 的參數名稱改為 data 而已. 網址與上面無變數的一樣 : 

>>> url=f'https://api.telegram.org/bot{token}/sendMessage'   
>>> r=requests.post(url, data=data)    
>>> r.json()   
{'ok': True, 'result': {'message_id': 6, 'from': {'id': 7938146214, 'is_bot': True, 'first_name': 'twstock', 'username': 'twstock168_bot'}, 'chat': {'id': <MyChatID>, 'first_name': '77960', 'type': 'private'}, 'date': 1743952234, 'text': 'Hello! 你好'}}

這時 Telegram App 再次收到同樣的訊息 :




我將上面的程序寫成如下的函式 :

import requests

def telegram_msg(token, chat_id, text):
    url=f'https://api.telegram.org/bot{token}/sendMessage'
    data={'chat_id': chat_id, 'text': text}
    try:
        with requests.post(url, data=data, timeout=10) as r:
            r.raise_for_status()  # 若 status_code != 2xx 會拋出例外
            result=r.json()
            if result.get('ok'):
                print('Message sent successfully.')
                return True
            else:
                print(f'Telegram API Error: {result}')
                return False
    except requests.exceptions.RequestException as e:
        print(f'Request error: {e}')
        return False

此函式加入了例外捕捉並使用 with 語法, 這樣當 with 區塊結束時就會自動關閉 Response 物件了, 測試結果 OK :

>>> text='你是在說哈囉嗎?'   
>>> telegram_msg(token, chat_id, text)     
Message sent successfully.
True

檢視 App 確實有收到訊息 : 



沒有留言 :