2025年4月16日 星期三

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

本篇紀錄如何用 Telegram Bot API 透過 HTTP 傳送一般檔案.  

本系列之前的文章參考 :


我將 Telegram Bot API 權杖與聊天室識別碼都存放在環境變數檔 .env 裡面, 利用 dotenv 套件載入後再用 os 模組讀取出來放在 token 與 chat_id 變數裡 : 

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

# load_secrets_py
from dotenv import load_dotenv  
import os  

load_dotenv()    
token=os.environ.get('TELEGRAM_TOKEN')   
chat_id=os.environ.get('TELEGRAM_ID') 

參考 :



12. 利用 HTTP API 傳送一般檔案 :     

Telegram Bot API 傳送一般檔案的 API 是 sendDocument, 使用 Bot 物件的話則是呼叫其 send_document() 方法. 與之前傳送多媒體 (圖片, 影片, 音訊) 可用 sendMediaGroup 類別打包後一次傳送不同的是, sendDoucumetn 或 send_document() 一次只能傳送一個文件, 如果要傳送多個文件可先壓縮成 zip 檔或使用迴圈傳送. 

其次, 傳送網路檔案因為 Telegram 要去網站抓取檔案, 這時主機可能會檢查來源 IP, User-Agent 與 TLS 握手細節等原因導致下載失敗 (收到 403 或 TLS 錯誤), 所以以下測試以傳送本機為主.


(1). 使用 HTTP API 傳送檔案 :       

匯入 requests 模組來處理 HTTP 請求與回應 :

>>> import requests    

Telegram 傳送檔案的 HTTP API 網址格式如下 :

https://api.telegram.org/bot{TOKEN}/sendDocument 

製作請求網址 :

>>> url=f'https://api.telegram.org/bot{token}/sendDocument'   

製作要傳給 data 參數的字典, 必須包含 chat_id 鍵, 或可選用的 caption 與 parse_mode 鍵 :

>>> data={'chat_id': chat_id,
      'caption': '*出國要帶的東西*',
      'parse_mode': 'Markdown'}   

然後用 open() 開啟要傳送的檔案, 我準備了一個 WORD 檔案 travellers.docx (注意, 不可以用中文檔名, 會被強制斷線), 將檔案參考傳給 files 字典的 document 鍵, 然後在呼叫 requests.post() 時將 files 字典傳給 files 參數, 這樣 requests 就會用 multipart/form-data 方式傳送檔案 : 

>>> with open(file_path, 'rb') as f:
    files={'document': f}
    r=requests.post(url, data=data, files=files)
  
檢視傳回值 :

>>> r.json()   
{'ok': True, 'result': {'message_id': 294, 'from': {'id': 7938146214, 'is_bot': True, 'first_name': 'twstock', 'username': 'twstock168_bot'}, 'chat': {'id': <聊天室ID>, 'first_name': '<我的名字>', 'username': '<我的 username>', 'type': 'private'}, 'date': 1744790747, 'document': {'file_name': 'travellers.docx', 'mime_type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'file_id': 'BQACAgUAAxkDAAIBJmf_ZNtPGDpz2YNbvWBJUz92Y35pAALZFgACTYwAAVQb3M1fKs1GWzYE', 'file_unique_id': 'AgAD2RYAAk2MAAFU', 'file_size': 17276}, 'caption': '出國要帶的東西', 'caption_entities': [{'offset': 0, 'length': 7, 'type': 'bold'}]}}

查看 Telegram App 有收到此 docx 檔案 :




以上測試的完整程式碼如下 :

# telegram_send_document.py
import requests

def telegram_document(token, chat_id, file_path, caption=None, parse_mode='Markdown'):
    data={
        'chat_id': chat_id,
        'caption': caption,
        'parse_mode': parse_mode
        }
    try:
        with open(file_path, 'rb') as f:
            files={'document': f}        
            with requests.post(url, data=data, files=files) as r:
                r.raise_for_status()
                result=r.json()
                if result.get('ok'):
                    print('Message sent successfully.')
                    return True
                else:
                    print(f'Telegram API Error: {result}')
                    return False
    except Exception as e:
        print(f'Request error: {e}')
        return False
    
if __name__ == '__main__':
    token='YOUR TOKEN'
    chat_id='YOUR CHAT ID'
    file_path='FILE PATH'
    caption='THE CAPTION'
    telegram_document(token, chat_id, file_path, caption)    


(2). 使用 Bot 物件傳送檔案 :      

上面程式可以改用 Bot 物件以非同步方式傳送, 程式碼如下 :

# telegram_send_document_bot.py
import asyncio
from telegram import Bot
from telegram.error import TelegramError

async def telegram_document(token, chat_id, file_path, caption=None, parse_mode='Markdown'):
    bot=Bot(token=token)
    try:
        with open(file_path, 'rb') as f:
            msg=await bot.send_document(
                chat_id=chat_id,
                document=f,
                caption=caption,
                parse_mode=parse_mode
                )
        print('Message sent successfully.')
        return True
    except TelegramError as e:
        print(f'Telegram API Error: {e}')
        return False
    except Exception as e:
        print(f'Request error: {e}')
        return False

if __name__ == '__main__':
    token='YOUR TOKEN'
    chat_id='YOUR CHAT ID'
    file_path='FILE PATH'
    caption='THE CAPTION'
    asyncio.run(telegram_document(token, chat_id, file_path, caption))

>>> async def telegram_document(token, chat_id, file_path, caption=None, parse_mode='Markdown'):
    bot=Bot(token=token)
    try:
        with open(file_path, 'rb') as f:
            msg=await bot.send_document(
                chat_id=chat_id,
                document=f,
                caption=caption,
                parse_mode=parse_mode
                )
        print('Message sent successfully.')
        return True
    except TelegramError as e:
        print(f'Telegram API Error: {e}')
        return False
    except Exception as e:
        print(f'Request error: {e}')
        return False

我將之前測試圖片時的 cat1.jpg, cat2.jpg, 與 cat3.jpg 壓縮為 cats.zip : 測試如下 : 

>>> file_path='cats.zip'   
>>> caption='*可愛的三小貓*'     
>>> asyncio.run(telegram_document(token, chat_id, file_path, caption))   
Message sent successfully.
True

檢查 App 果然有收到該 zip 檔 : 



沒有留言 :