英文:
Python Telegram bot send message with buttons
问题
I'd like to send a message through telegram bot with buttons. Once button is pressed i need to know which button was that and then change the text that came with the button.
I've almost found out how to do things separately but i can't unite them. To send a message with buttons i need either to write /start or have to press a menu button. I need those buttons to appear after the message without user having to press anything.
This is a script that i've found in the official description with added functions to send a message
#!/usr/bin/env python
# pylint: disable=unused-argument, wrong-import-position
# This program is dedicated to the public domain under the CC0 license.
"""
Basic example for a bot that uses inline keyboards. For an in-depth explanation, check out
https://github.com/python-telegram-bot/python-telegram-bot/wiki/InlineKeyboard-Example.
"""
import logging
import asyncio
from telegram import __version__ as TG_VER
try:
from telegram import __version_info__
except ImportError:
__version_info__ = (0, 0, 0, 0, 0) # type: ignore[assignment]
if __version_info__ < (20, 0, 0, "alpha", 1):
raise RuntimeError(
f"This example is not compatible with your current PTB version {TG_VER}. To view the "
f"{TG_VER} version of this example, "
f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
)
from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ApplicationBuilder, Application, CallbackQueryHandler, CommandHandler, ContextTypes
# Enable logging
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Sends a message with three inline buttons attached."""
keyboard = [
[
InlineKeyboardButton("Option 1", callback_data="1"),
InlineKeyboardButton("Option 2", callback_data="2"),
],
[InlineKeyboardButton("Option 3", callback_data="3")],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text("Please choose:", reply_markup=reply_markup)
async def button(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Parses the CallbackQuery and updates the message text."""
query = update.callback_query
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
await query.answer()
await query.edit_message_text(text=f"Selected option: {query.data}")
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Displays info on how to use the bot."""
await update.message.reply_text("Use /start to test this bot.")
# using telegram.Bot
async def send(chat, msg):
await Bot('<TOKEN>').sendMessage(chat_id=chat, text=msg)
# using ApplicationBuilder
async def send_more(chat, msg):
application = ApplicationBuilder().token('<TOKEN>').build()
await application.bot.sendMessage(chat_id=chat, text=msg)
def main() -> None:
"""Run the bot."""
# Create the Application and pass it your bot's token.
application = Application.builder().token("<TOKEN>").build()
# asyncio.run(send_more('<CHAT_ID>', 'Hello there!'))
application.add_handler(CommandHandler("start", start))
application.add_handler(CallbackQueryHandler(button))
application.add_handler(CommandHandler("help", help_command))
asyncio.run(send('<CHAT_ID>', 'Hello there!'))
# Run the bot until the user presses Ctrl-C
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
main()
The new code:
#!/usr/bin/env python
# pylint: disable=unused-argument, wrong-import-position
# This program is dedicated to the public domain under the CC0 license.
"""
Basic example for a bot that uses inline keyboards. For an in-depth explanation, check out
https://github.com/python-telegram-bot/python-telegram-bot/wiki/InlineKeyboard-Example.
"""
import logging
from telegram import __version__ as TG_VER
try:
from telegram import __version_info__
except ImportError:
__version_info__ = (0, 0, 0, 0, 0) # type: ignore[assignment]
if __version_info__ < (20, 0, 0, "alpha", 1):
raise RuntimeError(
f"This example is not compatible with your current PTB version {TG_VER}. To view the "
f"{TG_VER} version of this example, "
f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
)
from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ApplicationBuilder, Application, CallbackQueryHandler, CommandHandler, ContextTypes
# Enable logging
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
prolong_1y = 0
prolong_2y = 0
noprolong = 0
async def button(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Parses the CallbackQuery and updates the message text."""
query = update.callback_query
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
await query.answer()
global prolong_1y, prolong_2y, noprolong
if "prolong_1y" in query:
prolong_1y = 1
prolong_2y = 0
noprolong = 0
elif "prolong_2y" in query:
prolong_1y = 0
prolong_2y = 1
noprolong = 0
elif "noprolong" in query:
prolong_1y = 0
prolong_2y = 0
noprolong = 1
await query.edit_message_text(text=f"Selected option: {query.data}")
print(f"prolong_1y => {prolong_1y}, prolong_2y => {prolong_2y} and noprolong => {noprolong}")
# using telegram.Bot
async def send(chat, msg, reply_markup):
await Bot('<TOKEN>').sendMessage(chat_id=chat, text=msg, reply_markup=reply_markup)
async def post_init(application:
<details>
<summary>英文:</summary>
i'd like to send a message through telegram bot with buttons. Once button is pressed i need to know which button was that and then change the text that came with the button.
I've almost found out how to do things separately but i can't unite them. To send a message with buttons i need either to write /start or have to press a menu button. I need those buttons to appear after the message without user having to press anything.
This is a script that i've found in the official description with added functions to send a message
#!/usr/bin/env python
# pylint: disable=unused-argument, wrong-import-position
# This program is dedicated to the public domain under the CC0 license.
"""
Basic example for a bot that uses inline keyboards. For an in-depth explanation, check out
https://github.com/python-telegram-bot/python-telegram-bot/wiki/InlineKeyboard-Example.
"""
import logging
import asyncio
from telegram import __version__ as TG_VER
try:
from telegram import __version_info__
except ImportError:
__version_info__ = (0, 0, 0, 0, 0) # type: ignore[assignment]
if __version_info__ < (20, 0, 0, "alpha", 1):
raise RuntimeError(
f"This example is not compatible with your current PTB version {TG_VER}. To view the "
f"{TG_VER} version of this example, "
f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
)
from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ApplicationBuilder, Application, CallbackQueryHandler, CommandHandler, ContextTypes
# Enable logging
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Sends a message with three inline buttons attached."""
keyboard = [
[
InlineKeyboardButton("Option 1", callback_data="1"),
InlineKeyboardButton("Option 2", callback_data="2"),
],
[InlineKeyboardButton("Option 3", callback_data="3")],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text("Please choose:", reply_markup=reply_markup)
async def button(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Parses the CallbackQuery and updates the message text."""
query = update.callback_query
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
await query.answer()
await query.edit_message_text(text=f"Selected option: {query.data}")
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Displays info on how to use the bot."""
await update.message.reply_text("Use /start to test this bot.")
# using telegram.Bot
async def send(chat, msg):
await Bot('<TOKEN>').sendMessage(chat_id=chat, text=msg)
# using ApplicationBuilder
async def send_more(chat, msg):
application = ApplicationBuilder().token('<TOKEN>').build()
await application.bot.sendMessage(chat_id=chat, text=msg)
def main() -> None:
"""Run the bot."""
# Create the Application and pass it your bot's token.
application = Application.builder().token("<TOKEN>").build()
# asyncio.run(send_more('<CHAT_ID>', 'Hello there!'))
application.add_handler(CommandHandler("start", start))
application.add_handler(CallbackQueryHandler(button))
application.add_handler(CommandHandler("help", help_command))
asyncio.run(send('<CHAT_ID>', 'Hello there!'))
# Run the bot until the user presses Ctrl-C
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
main()
<<< EDIT >>>
The new code:
#!/usr/bin/env python
# pylint: disable=unused-argument, wrong-import-position
# This program is dedicated to the public domain under the CC0 license.
"""
Basic example for a bot that uses inline keyboards. For an in-depth explanation, check out
https://github.com/python-telegram-bot/python-telegram-bot/wiki/InlineKeyboard-Example.
"""
import logging
from telegram import __version__ as TG_VER
try:
from telegram import __version_info__
except ImportError:
__version_info__ = (0, 0, 0, 0, 0) # type: ignore[assignment]
if __version_info__ < (20, 0, 0, "alpha", 1):
raise RuntimeError(
f"This example is not compatible with your current PTB version {TG_VER}. To view the "
f"{TG_VER} version of this example, "
f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
)
from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ApplicationBuilder, Application, CallbackQueryHandler, CommandHandler, ContextTypes
# Enable logging
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
prolong_1y = 0
prolong_2y = 0
noprolong = 0
async def button(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Parses the CallbackQuery and updates the message text."""
query = update.callback_query
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
await query.answer()
global prolong_1y, prolong_2y, noprolong
if "prolong_1y" in query:
prolong_1y = 1
prolong_2y = 0
noprolong = 0
elif "prolong_2y" in query:
prolong_1y = 0
prolong_2y = 1
noprolong = 0
elif "noprolong" in query:
prolong_1y = 0
prolong_2y = 0
noprolong = 1
await query.edit_message_text(text=f"Selected option: {query.data}")
print(f"prolong_1y => {prolong_1y}, prolong_2y => {prolong_2y} and noprolong => {noprolong}")
# using telegram.Bot
async def send(chat, msg, reply_markup):
await Bot('<TOKEN>').sendMessage(chat_id=chat, text=msg, reply_markup=reply_markup)
async def post_init(application: Application) -> None:
"""Sends a message with three inline buttons attached."""
keyboard = [
[
InlineKeyboardButton("Option 1", callback_data="prolong_1y"),
InlineKeyboardButton("Option 2", callback_data="prolong_2y"),
],
[InlineKeyboardButton("Option 3", callback_data="noprolong")],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await send('<CHAT_ID>', 'Hello there!', reply_markup)
def main() -> None:
"""Run the bot."""
# Create the Application and pass it your bot's token.
application = Application.builder().token("<TOKEN>").post_init(post_init).build()
application.add_handler(CallbackQueryHandler(button))
# Run the bot until the user presses Ctrl-C
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
main()
The error that i receive after pressing on either Choice button:
2023-07-19 13:50:13,278 - apscheduler.scheduler - INFO - Scheduler started
2023-07-19 13:50:13,278 - telegram.ext.Application - INFO - Application started
2023-07-19 13:50:16,557 - telegram.ext.Application - ERROR - No error handlers are registered, logging exception.
Traceback (most recent call last):
File "/home/admin2/.local/lib/python3.11/site-packages/telegram/ext/_application.py", line 1173, in process_update
await coroutine
File "/home/admin2/.local/lib/python3.11/site-packages/telegram/ext/_basehandler.py", line 141, in handle_update
return await self.callback(update, context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/media/smb_general/Адміністрування/Source/ElReports/elreports/inlinekeyboard.py", line 50, in button
if "prolong_1y" in query:
^^^^^^^^^^^^^^^^^^^^^
File "/home/admin2/.local/lib/python3.11/site-packages/telegram/_telegramobject.py", line 247, in __getitem__
return getattr(self, item)
^^^^^^^^^^^^^^^^^^^
TypeError: attribute name must be string, not 'int'
<<< EDIT 2 >>>
Silly me, changed query to query.data now everything works!
</details>
# 答案1
**得分**: 2
如果你想在`send`中发送的消息中附加按钮,只需使用`send_message`的[相应参数](https://docs.python-telegram-bot.org/en/stable/telegram.bot.html#telegram.Bot.send_message.params.reply_markup)即可。请注意,`start`中使用的`message.reply_text`仅是该方法的快捷方式。
此外,你无需在`send`中手动初始化一个机器人,也无需通过`asyncio.run`手动运行该方法。我建议改为使用`post_init`,它允许作为`application.run_polling`的启动逻辑的一部分运行自定义逻辑。
免责声明:我目前是`python-telegram-bot`的维护者。
<details>
<summary>英文:</summary>
If you want to attach buttons to the message sent in `send`, you can just use the [corresponding parameter of `send_message`](https://docs.python-telegram-bot.org/en/stable/telegram.bot.html#telegram.Bot.send_message.params.reply_markup) for that. Not that [`message.reply_text`](https://docs.python-telegram-bot.org/en/stable/telegram.message.html#telegram.Message.reply_text) as used in `start` is just a shortcut for that method.
Moreover, you don't need to manually initialize a bot in `send` and to manually run that method via `asyncio.run`. I recommend to instead make use of [`post_init`](https://docs.python-telegram-bot.org/en/stable/telegram.ext.applicationbuilder.html#telegram.ext.ApplicationBuilder.post_init) which allows to run a custom logic as part of the startup logic of `application.run_polling`.
---
Disclaimer: I'm currently the maintainer of `python-telegram-bot`.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论