在前一篇測試中我們已能順利用 HTTP 請求呼叫 Telegram Bot API 傳送訊息到指定的 Telegram 聊天室, 本篇要改用一個第三方套件 python-telegram-bot 來簡化這個程序. 本系列之前的文章參考 :
以下測試要用到 Telegram Bot API 的權杖 (token) 與聊天室識別碼 (chat_id), 我們先用 dotenv 套件從 .env 中讀取 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')
參考 :
4. 安裝 python-telegram-bot 套件 :
首先用 pip 安裝 python-telegram-bot 套件 :
D:\python\test>pip install python-telegram-bot
Collecting python-telegram-bot
Downloading python_telegram_bot-22.0-py3-none-any.whl.metadata (17 kB)
Collecting httpx~=0.27 (from python-telegram-bot)
Downloading httpx-0.28.1-py3-none-any.whl.metadata (7.1 kB)
Requirement already satisfied: anyio in c:\users\tony1\appdata\roaming\python\python310\site-packages (from httpx~=0.27->python-telegram-bot) (3.7.1)
Requirement already satisfied: certifi in c:\users\tony1\appdata\roaming\python\python310\site-packages (from httpx~=0.27->python-telegram-bot) (2023.7.22)
Collecting httpcore==1.* (from httpx~=0.27->python-telegram-bot)
Downloading httpcore-1.0.7-py3-none-any.whl.metadata (21 kB)
Requirement already satisfied: idna in c:\users\tony1\appdata\roaming\python\python310\site-packages (from httpx~=0.27->python-telegram-bot) (3.4)
Requirement already satisfied: h11<0.15,>=0.13 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from httpcore==1.*->httpx~=0.27->python-telegram-bot) (0.14.0)
Requirement already satisfied: sniffio>=1.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from anyio->httpx~=0.27->python-telegram-bot) (1.3.0)
Requirement already satisfied: exceptiongroup in c:\users\tony1\appdata\roaming\python\python310\site-packages (from anyio->httpx~=0.27->python-telegram-bot) (1.1.3)
Downloading python_telegram_bot-22.0-py3-none-any.whl (673 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 673.5/673.5 kB 3.3 MB/s eta 0:00:00
Downloading httpx-0.28.1-py3-none-any.whl (73 kB)
Downloading httpcore-1.0.7-py3-none-any.whl (78 kB)
Installing collected packages: httpcore, httpx, python-telegram-bot
Attempting uninstall: httpcore
Found existing installation: httpcore 0.17.3
Uninstalling httpcore-0.17.3:
Successfully uninstalled httpcore-0.17.3
Attempting uninstall: httpx
Found existing installation: httpx 0.24.1
Uninstalling httpx-0.24.1:
Successfully uninstalled httpx-0.24.1
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
reflex 0.2.8 requires fastapi<0.97.0,>=0.96.0, but you have fastapi 0.115.5 which is incompatible.
reflex 0.2.8 requires httpx<0.25.0,>=0.24.0, but you have httpx 0.28.1 which is incompatible.
reflex 0.2.8 requires pydantic<2.0.0,>=1.10.2, but you have pydantic 2.5.3 which is incompatible.
reflex 0.2.8 requires python-multipart<0.0.6,>=0.0.5, but you have python-multipart 0.0.12 which is incompatible.
reflex 0.2.8 requires typer==0.4.2, but you have typer 0.13.1 which is incompatible.
Successfully installed httpcore-1.0.7 httpx-0.28.1 python-telegram-bot-22.0
這樣就完成安裝了, 版本為 v22.
5. 利用 Bot 物件的 send_message() 傳送訊息 :
雖然安裝時的套件名稱是 python-telegram-bot (這是 PyPi 上的 distribution name), 實際使用時則是要匯入 telegram 這個套件名稱 :
>>> import telegram
先用 dir() 檢視套件內容 :
>>> dir(telegram)
['AffiliateInfo', 'Animation', 'Audio', 'BackgroundFill', 'BackgroundFillFreeformGradient', 'BackgroundFillGradient', 'BackgroundFillSolid', 'BackgroundType', 'BackgroundTypeChatTheme', 'BackgroundTypeFill', 'BackgroundTypePattern', 'BackgroundTypeWallpaper', 'Birthdate', 'Bot', 'BotCommand', 'BotCommandScope', 'BotCommandScopeAllChatAdministrators', 'BotCommandScopeAllGroupChats', 'BotCommandScopeAllPrivateChats', 'BotCommandScopeChat', 'BotCommandScopeChatAdministrators', 'BotCommandScopeChatMember', 'BotCommandScopeDefault', 'BotDescription', 'BotName', 'BotShortDescription', 'BusinessConnection', 'BusinessIntro', 'BusinessLocation', 'BusinessMessagesDeleted', 'BusinessOpeningHours', 'BusinessOpeningHoursInterval', 'CallbackGame', 'CallbackQuery', 'Chat', 'ChatAdministratorRights', 'ChatBackground', 'ChatBoost', 'ChatBoostAdded', 'ChatBoostRemoved', 'ChatBoostSource', 'ChatBoostSourceGiftCode', 'ChatBoostSourceGiveaway', 'ChatBoostSourcePremium', 'ChatBoostUpdated', 'ChatFullInfo', 'ChatInviteLink', 'ChatJoinRequest', 'ChatLocation', 'ChatMember', 'ChatMemberAdministrator', 'ChatMemberBanned', 'ChatMemberLeft', 'ChatMemberMember', 'ChatMemberOwner', 'ChatMemberRestricted', 'ChatMemberUpdated', 'ChatPermissions', 'ChatPhoto', 'ChatShared', 'ChosenInlineResult', 'Contact', 'CopyTextButton', 'Credentials', 'DataCredentials', 'Dice', 'Document', 'EncryptedCredentials', 'EncryptedPassportElement', 'ExternalReplyInfo', 'File', 'FileCredentials', 'ForceReply', 'ForumTopic', 'ForumTopicClosed', 'ForumTopicCreated', 'ForumTopicEdited', 'ForumTopicReopened', 'Game', 'GameHighScore', 'GeneralForumTopicHidden', 'GeneralForumTopicUnhidden', 'Gift', 'Gifts', 'Giveaway', 'GiveawayCompleted', 'GiveawayCreated', 'GiveawayWinners', 'IdDocumentData', 'InaccessibleMessage', 'InlineKeyboardButton', 'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult', 'InlineQueryResultArticle', 'InlineQueryResultAudio', 'InlineQueryResultCachedAudio', 'InlineQueryResultCachedDocument', 'InlineQueryResultCachedGif', 'InlineQueryResultCachedMpeg4Gif', 'InlineQueryResultCachedPhoto', 'InlineQueryResultCachedSticker', 'InlineQueryResultCachedVideo', 'InlineQueryResultCachedVoice', 'InlineQueryResultContact', 'InlineQueryResultDocument', 'InlineQueryResultGame', 'InlineQueryResultGif', 'InlineQueryResultLocation', 'InlineQueryResultMpeg4Gif', 'InlineQueryResultPhoto', 'InlineQueryResultVenue', 'InlineQueryResultVideo', 'InlineQueryResultVoice', 'InlineQueryResultsButton', 'InputContactMessageContent', 'InputFile', 'InputInvoiceMessageContent', 'InputLocationMessageContent', 'InputMedia', 'InputMediaAnimation', 'InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo', 'InputMessageContent', 'InputPaidMedia', 'InputPaidMediaPhoto', 'InputPaidMediaVideo', 'InputPollOption', 'InputSticker', 'InputTextMessageContent', 'InputVenueMessageContent', 'Invoice', 'KeyboardButton', 'KeyboardButtonPollType', 'KeyboardButtonRequestChat', 'KeyboardButtonRequestUsers', 'LabeledPrice', 'LinkPreviewOptions', 'Location', 'LoginUrl', 'MaskPosition', 'MaybeInaccessibleMessage', 'MenuButton', 'MenuButtonCommands', 'MenuButtonDefault', 'MenuButtonWebApp', 'Message', 'MessageAutoDeleteTimerChanged', 'MessageEntity', 'MessageId', 'MessageOrigin', 'MessageOriginChannel', 'MessageOriginChat', 'MessageOriginHiddenUser', 'MessageOriginUser', 'MessageReactionCountUpdated', 'MessageReactionUpdated', 'OrderInfo', 'PaidMedia', 'PaidMediaInfo', 'PaidMediaPhoto', 'PaidMediaPreview', 'PaidMediaPurchased', 'PaidMediaVideo', 'PassportData', 'PassportElementError', 'PassportElementErrorDataField', 'PassportElementErrorFile', 'PassportElementErrorFiles', 'PassportElementErrorFrontSide', 'PassportElementErrorReverseSide', 'PassportElementErrorSelfie', 'PassportElementErrorTranslationFile', 'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified', 'PassportFile', 'PersonalDetails', 'PhotoSize', 'Poll', 'PollAnswer', 'PollOption', 'PreCheckoutQuery', 'PreparedInlineMessage', 'ProximityAlertTriggered', 'ReactionCount', 'ReactionType', 'ReactionTypeCustomEmoji', 'ReactionTypeEmoji', 'ReactionTypePaid', 'RefundedPayment', 'ReplyKeyboardMarkup', 'ReplyKeyboardRemove', 'ReplyParameters', 'ResidentialAddress', 'RevenueWithdrawalState', 'RevenueWithdrawalStateFailed', 'RevenueWithdrawalStatePending', 'RevenueWithdrawalStateSucceeded', 'SecureData', 'SecureValue', 'SentWebAppMessage', 'SharedUser', 'ShippingAddress', 'ShippingOption', 'ShippingQuery', 'StarTransaction', 'StarTransactions', 'Sticker', 'StickerSet', 'Story', 'SuccessfulPayment', 'SwitchInlineQueryChosenChat', 'TelegramObject', 'TextQuote', 'TransactionPartner', 'TransactionPartnerAffiliateProgram', 'TransactionPartnerChat', 'TransactionPartnerFragment', 'TransactionPartnerOther', 'TransactionPartnerTelegramAds', 'TransactionPartnerTelegramApi', 'TransactionPartnerUser', 'Update', 'User', 'UserChatBoosts', 'UserProfilePhotos', 'UsersShared', 'Venue', 'Video', 'VideoChatEnded', 'VideoChatParticipantsInvited', 'VideoChatScheduled', 'VideoChatStarted', 'VideoNote', 'Voice', 'WebAppData', 'WebAppInfo', 'WebhookInfo', 'WriteAccessAllowed', '__all__', '__annotations__', '__author__', '__bot_api_version__', '__bot_api_version_info__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', '__version_info__', '_birthdate', '_bot', '_botcommand', '_botcommandscope', '_botdescription', '_botname', '_business', '_callbackquery', '_chat', '_chatadministratorrights', '_chatbackground', '_chatboost', '_chatfullinfo', '_chatinvitelink', '_chatjoinrequest', '_chatlocation', '_chatmember', '_chatmemberupdated', '_chatpermissions', '_choseninlineresult', '_copytextbutton', '_dice', '_files', '_forcereply', '_forumtopic', '_games', '_gifts', '_giveaway', '_inline', '_keyboardbutton', '_keyboardbuttonpolltype', '_keyboardbuttonrequest', '_linkpreviewoptions', '_loginurl', '_menubutton', '_message', '_messageautodeletetimerchanged', '_messageentity', '_messageid', '_messageorigin', '_messagereactionupdated', '_paidmedia', '_passport', '_payment', '_poll', '_proximityalerttriggered', '_reaction', '_reply', '_replykeyboardmarkup', '_replykeyboardremove', '_sentwebappmessage', '_shared', '_story', '_switchinlinequerychosenchat', '_telegramobject', '_update', '_user', '_userprofilephotos', '_utils', '_version', '_videochat', '_webappdata', '_webappinfo', '_webhookinfo', '_writeaccessallowed', 'constants', 'error', 'helpers', 'request', 'warnings']
可見 telegram 套件定義了非常多類別與模組, 其中的 Bot 類別是這個套件的核心, 所以通常只要匯入這個類別即可 :
>>> type(telegram.Bot)
<class 'abc.ABCMeta'>
>>> from telegram import Bot
呼叫這個 Bot 類別的建構式並傳入 token 可以建立一個 Telegram Bot 物件 :
>>> bot=Bot(token)
>>> type(bot)
<class 'telegram._bot.Bot'>
Bot 物件提供豐富的方法可用來控制機器人做出各種動作, 例如發出訊息, 傳送照片, 編輯訊息等等, 常用的方法如下表 :
Bot 物件常用方法 |
說明 |
send_message(chat_id, text) |
傳送文字訊息給某個使用者或群組 |
send_photo(chat_id, photo, caption=None) |
傳送照片, 並可附加說明文字 |
send_document(chat_id, document) |
傳送文件 (例如 PDF, ZIP 等) |
send_video(chat_id, video) |
傳送影片 |
send_audio(chat_id, audio) |
傳送音訊檔案 |
get_me() |
取得目前 bot 的基本資訊 |
get_updates() |
拉取使用者互動 (polling 模式) |
set_webhook(url) |
設定 webhook URL 以接收事件 |
delete_message(chat_id, message_id) |
刪除指定訊息 |
edit_message_text(...) |
編輯已發送的文字訊息 |
注意, 這些方法都有駝峰式的別名, 例如 send_message() 的駝峰式別名為 sendMessage(), 呼叫哪一個都可以. 其次, v20 版以後 Bot 物件的所有方法都改用非同步的 async 函式實作, 必須用 await 來執行它, 如果直接呼叫 send_message() 或 sendMessage() 是不會傳送訊息的, 只會傳回一個 coroutine 物件而已 :
>>> bot.send_message(chat_id, text)
<coroutine object Bot.send_message at 0x00000252D5938190>
呼叫 Bot 物件的 send_message() 前要先建立一個 Application 物件, 此類別位於 telegram.ext 模組內, 呼叫 Application.builder().token(token).build() 函式即可建立一個 Application 物件 :
>>> from telegram.ext import Application
>>> app=Application.builder().token(token).build()
>>> type(app)
<class 'telegram.ext._application.Application'>
然後定義一個 async 函式利用 await 來呼叫 Bot 物件的方法, 並透過 asyncio.run() 來呼叫此函式才會真正執行 Bot 物件的方法 :
>>> async def telegram_text():
await app.bot.send_message(chat_id=chat_id, text=text)
然後匯入 asyncio 模組, 呼叫其 run() 函式來執行非同步函式 :
>>> import asyncio
>>> asyncio.run(telegram_text())
Message(channel_chat_created=False, chat=Chat(first_name='<MyName>', id=<MyChatID>, type=<ChatType.PRIVATE>, username='yhhuang1966'), date=datetime.datetime(2025, 4, 7, 7, 27, 47, tzinfo=datetime.timezone.utc), delete_chat_photo=False, from_user=User(first_name='twstock', id=7938146214, is_bot=True, username='twstock168_bot'), group_chat_created=False, message_id=9, supergroup_chat_created=False, text='川普解放日關稅來襲, 今日台股上演大逃殺')
這時 Telegram App 就會收到這則訊息了 :
以上測試之完整程式碼如下 :
# telegram_send_text_2.py
from telegram import Bot
from telegram.ext import Application
import asyncio
async def telegram_text(chat_id, text):
await app.bot.send_message(chat_id=chat_id, text=text)
token='我的 Telegram 權杖'
chat_id='我的聊天室識別碼'
text='2025-04-07 台股收盤 19232 點, 跌 2065 點, 跌幅 9.7%'
app=Application.builder().token(token).build()
asyncio.run(telegram_text(chat_id, text))
此例 token 與 chat_id 為全域變數, 其實不需要傳入 telegram_text() 內, 但為了讓其他函式呼叫還是傳入參數較為完整. 執行結果如下 :