2025年4月13日 星期日

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') 

參考 :


Telegram 支援 mp3 與 m4a 音訊檔案傳送, 可在 Telegram App 上直接播放. 我在 GitHub 上準備了下面兩個 mp3 檔案做為測試用的標本 : 


前者取自 Beyond 的 "光輝歲月" 前 11 秒片段; 後者則是取自黃霄雲 "你的答案" 前 22 秒片段. 可以下載後做為測試傳送本機 mp3 檔案之用. 以下測試將直接使用函式, 不再一步一步地在命令列測試. 


10. 利用 HTTP API 傳送音訊 :     

Telegram Bot API 傳送音訊的 API 是 sendAudio, 作法與傳送影片類似, 但多了 title (音樂標題) 與 performer (演唱者) 這兩個參數, 但其實並沒有效果 (因為 Telegram 預設會使用音訊檔的 metadata), 故以下測試不使用這兩個參數. 


(1). 傳送網路音訊 :       

傳送單一個 mp3 音訊的完整程式碼如下 :

# telegram_send_web_audio.py
import requests

def telegram_web_audio(token, chat_id, audio_url, caption=None, parse_mode='Markdown'):
    url=f'https://api.telegram.org/bot{token}/sendAudio'
    data={
        'chat_id': chat_id,
        'audio': audio_url,
        'caption': caption,
        'parse_mode': parse_mode,
        }
    try:
        with requests.post(url, data=data) as r:
            r.raise_for_status()
            result=r.json()
            if result.get('ok'):
                print('Audio 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

if __name__ == '__main__':
    token='YOUR TOKEN'
    chat_id='YOUR CHAT ID'
    audio_url='https://yaohuang1966.github.io/media/glorious_days_clip.mp3'  
    caption='🎵 一起聽聽這首歌吧'
    telegram_web_audio(token, chat_id, audio_url, caption, title, performer)

首先用 "光輝歲月" 的前奏 mp3 測試 : 

>>> audio_url='https://yaohuang1966.github.io/media/glorious_days_clip.mp3'   
>>> caption='🎵 一起聽聽這首歌吧'    
>>> telegram_web_audio(token, chat_id, audio_url, caption, title, performer)    
Audio message sent successfully.
True

查看 App 有收到這首歌, 但表演者顯示的卻是 'Unknown artist', 歌曲標題顯示的是 mp3 檔案名稱, 這是因為光輝歲月這首歌 mp3 檔的 metadata (ID3 標籤) 裡面的 Title 與 Artist 欄位空白所致 :




改用另一個 mp3 黃霄雲的 "你的答案" 測試 :

>>> audio_url='https://yaohuang1966.github.io/media/your_answer_clip.mp3'   
>>> telegram_web_audio(token, chat_id, audio_url, caption, title, performer)   
Audio message sent successfully.
True

結果曲名與歌手顯示正常, 因為這首歌的 ID3 標籤的 Title 與 Artist 欄位有設定 :




可以用 MP3tag 這個軟體去修改 ID3 標籤 : 




如果要傳送多個音訊, API 就要改用 sendMediaGroup, 每個音訊要先與標題打包成字典串列, 轉成 JSON 字串後傳給 data 字典的 media 參數, 完整程式碼如下 :

# telegram_send_web_audios.py 
import requests

def telegram_web_audios(token, chat_id, media_list, caption_list, parse_mode='Markdown'):
    url=f'https://api.telegram.org/bot{token}/sendMediaGroup'
    media=[{
        'type': 'audio',
        'media': media,
        'caption': caption,
        'parse_mode': parse_mode
        } for media, caption in zip(media_list, caption_list)]
    data={
        'chat_id': chat_id,
        'media': str(media).replace("'", '"')
        }
    try:
        with requests.post(url, data=data) 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 Exception as e:
        print(f'Request error: {e}')
        return False

if __name__ == '__main__':
    token='YOUR TOKEN'
    chat_id='YOUR CHAT ID'
    audio_urls=[
        'https://yaohuang1966.github.io/media/glorious_days_clip.mp3',
        'https://yaohuang1966.github.io/media/your_answer_clip.mp3'
        ]
    captions=['光輝歲月', '你的答案']
    telegram_web_audios(token, chat_id, audio_urls, captions)

測試結果如下 :

>>> audio_urls=[
        'https://yaohuang1966.github.io/media/glorious_days_clip.mp3',
        'https://yaohuang1966.github.io/media/your_answer_clip.mp3'
        ] 
>>> captions=['光輝歲月', '你的答案']    
>>> telegram_web_audios(token, chat_id, audio_urls, captions)     
Message sent successfully.
True

查看 App 有收到這兩首歌 : 




(2). 傳送本機音訊 :       

傳送本機音訊所用的 API 與網路音訊的一樣, 傳送一個 mp3 檔使用 sendAudio; 傳送多個 mp3 檔使用 sendMidiaGroup, 傳送本機音訊就是多了開檔與關檔動作而已. 

傳送一個音訊檔的程式碼如下 :

# telegram_send_local_audio.py 
import requests

def telegram_local_audio(token, chat_id, audio_path, caption=None, parse_mode='Markdown'):
    url=f'https://api.telegram.org/bot{token}/sendAudio'
    data={
        'chat_id': chat_id,
        'caption': caption,
        'parse_mode': parse_mode,
        }
    try:
        with open(audio_path, 'rb') as f:
            files={'audio': 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'
    audio_path='your_answer_clip.mp3'
    caption='🎵 一起來聽你的答案'
    telegram_local_audio(token, chat_id, audio_path, caption)

測試結果如下 :

>>> audio_path='your_answer_clip.mp3'   
>>> caption='🎵 一起來聽你的答案'   
>>> telegram_local_audio(token, chat_id, audio_path, caption)    
Message sent successfully.
True

查看 App 果然有收到這首歌 :




傳送多個本機檔案使用的 API 是 sendMediaGroup, 完整程式碼如下 : 

# telegram_send_local_audios.py
import requests

def telegram_local_audios(token, chat_id, audio_paths, captions):
    url=f'https://api.telegram.org/bot{token}/sendMediaGroup'
    files={}  # 儲存檔案物件
    media=[]  # 準備 media payload

    for i, audio_path in enumerate(audio_paths):
        key=f'media{i+1}'
        f=open(audio_path, 'rb')
        files[key]=f
        item={
            'type': 'audio',
            'media': f'attach://{key}'
            }
        if i < len(captions):
            item['caption']=captions[i]
        media.append(item)
    data={
        'chat_id': chat_id,
        'media': str(media).replace("'", '"')  # 轉成 JSON 字串格式
        }
    multipart={
        k: (f.name, f, 'audio/mpeg') for k, f in files.items()
        }
    try:
        with requests.post(url, data=data, files=multipart) 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
    finally:
        for f in files.values():
            f.close()

if __name__ == '__main__':
    token='YOUR TOKEN'
    chat_id='YOUR CHAT ID'
    audio_paths=['glorious_days_clip.mp3', 'your_answer_clip.mp3']
    captions=['🎵 光輝歲月', '🎶 你的答案']
    telegram_local_audios(token, chat_id, audio_paths, captions)

請注意每個 mp3 音訊檔打包時 type 鍵要設為 audio, 而 multipart 的編碼格式要設為 audio/mpeg, 測試結果如下 : 

>>> audio_paths=['glorious_days_clip.mp3', 'your_answer_clip.mp3']   
>>> captions=['🎵 光輝歲月', '🎶 你的答案']     
>>> telegram_local_audios(token, chat_id, audio_paths, captions)       
Message sent successfully.
True

查看 App 有收到這兩首歌 :



沒有留言 :