diff --git a/CHANGES/5780.doc.rst b/CHANGES/5780.doc.rst new file mode 100644 index 00000000..2b8af247 --- /dev/null +++ b/CHANGES/5780.doc.rst @@ -0,0 +1 @@ +Refactored examples code to use aiogram enumerations and enhanced chat messages with markdown beautifications for a more user-friendly display. diff --git a/examples/echo_bot.py b/examples/echo_bot.py index 8ac45e43..fc0bb0ff 100644 --- a/examples/echo_bot.py +++ b/examples/echo_bot.py @@ -1,19 +1,22 @@ import asyncio import logging +import sys +from os import getenv from aiogram import Bot, Dispatcher, Router, types from aiogram.enums import ParseMode -from aiogram.filters import Command +from aiogram.filters import CommandStart from aiogram.types import Message +from aiogram.utils.markdown import hbold # Bot token can be obtained via https://t.me/BotFather -TOKEN = "42:TOKEN" +TOKEN = getenv("BOT_TOKEN") # All handlers should be attached to the Router (or Dispatcher) router = Router() -@router.message(Command("start")) +@router.message(CommandStart()) async def command_start_handler(message: Message) -> None: """ This handler receives messages with `/start` command @@ -23,7 +26,7 @@ async def command_start_handler(message: Message) -> None: # and the target chat will be passed to :ref:`aiogram.methods.send_message.SendMessage` # method automatically or call API method directly via # Bot instance: `bot.send_message(chat_id=message.chat.id, ...)` - await message.answer(f"Hello, {message.from_user.full_name}!") + await message.answer(f"Hello, {hbold(message.from_user.full_name)}!") @router.message() @@ -54,5 +57,8 @@ async def main() -> None: if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) - asyncio.run(main()) + logging.basicConfig(level=logging.INFO, stream=sys.stdout) + try: + asyncio.run(main()) + except (KeyboardInterrupt, SystemExit): + logging.info("Bot stopped!") diff --git a/examples/echo_bot_webhook.py b/examples/echo_bot_webhook.py index d8ed41c5..1fa56c3d 100644 --- a/examples/echo_bot_webhook.py +++ b/examples/echo_bot_webhook.py @@ -2,17 +2,20 @@ This example shows how to use webhook on behind of any reverse proxy (nginx, traefik, ingress etc.) """ import logging +import sys +from os import getenv from aiohttp import web from aiogram import Bot, Dispatcher, Router, types from aiogram.enums import ParseMode -from aiogram.filters import Command +from aiogram.filters import CommandStart from aiogram.types import Message +from aiogram.utils.markdown import hbold from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application # Bot token can be obtained via https://t.me/BotFather -TOKEN = "42:TOKEN" +TOKEN = getenv("BOT_TOKEN") # Webserver settings # bind localhost only to prevent any external access @@ -32,7 +35,7 @@ BASE_WEBHOOK_URL = "https://aiogram.dev/" router = Router() -@router.message(Command(commands=["start"])) +@router.message(CommandStart()) async def command_start_handler(message: Message) -> None: """ This handler receives messages with `/start` command @@ -42,7 +45,7 @@ async def command_start_handler(message: Message) -> None: # and the target chat will be passed to :ref:`aiogram.methods.send_message.SendMessage` # method automatically or call API method directly via # Bot instance: `bot.send_message(chat_id=message.chat.id, ...)` - await message.answer(f"Hello, {message.from_user.full_name}!") + await message.answer(f"Hello, {hbold(message.from_user.full_name)}!") @router.message() @@ -63,7 +66,7 @@ async def echo_handler(message: types.Message) -> None: async def on_startup(bot: Bot) -> None: # If you have a self-signed SSL certificate, then you will need to send a public # certificate to Telegram - await bot.set_webhook(f"{BASE_WEBHOOK_URL}{WEBHOOK_PATH}") + await bot.set_webhook(f"{BASE_WEBHOOK_URL}{WEBHOOK_PATH}", secret_token=WEBHOOK_SECRET) def main() -> None: @@ -100,5 +103,5 @@ def main() -> None: if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) + logging.basicConfig(level=logging.INFO, stream=sys.stdout) main() diff --git a/examples/echo_bot_webhook_ssl.py b/examples/echo_bot_webhook_ssl.py index 463717f0..ad41bc4d 100644 --- a/examples/echo_bot_webhook_ssl.py +++ b/examples/echo_bot_webhook_ssl.py @@ -3,17 +3,20 @@ This example shows how to use webhook with SSL certificate. """ import logging import ssl +import sys +from os import getenv from aiohttp import web from aiogram import Bot, Dispatcher, Router, types from aiogram.enums import ParseMode -from aiogram.filters import Command +from aiogram.filters import CommandStart from aiogram.types import FSInputFile, Message +from aiogram.utils.markdown import hbold from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application # Bot token can be obtained via https://t.me/BotFather -TOKEN = "42:TOKEN" +TOKEN = getenv("BOT_TOKEN") # Webserver settings # bind localhost only to prevent any external access @@ -37,7 +40,7 @@ WEBHOOK_SSL_PRIV = "/path/to/private.key" router = Router() -@router.message(Command("start")) +@router.message(CommandStart()) async def command_start_handler(message: Message) -> None: """ This handler receives messages with `/start` command @@ -47,7 +50,7 @@ async def command_start_handler(message: Message) -> None: # and the target chat will be passed to :ref:`aiogram.methods.send_message.SendMessage` # method automatically or call API method directly via # Bot instance: `bot.send_message(chat_id=message.chat.id, ...)` - await message.answer(f"Hello, {message.from_user.full_name}!") + await message.answer(f"Hello, {hbold(message.from_user.full_name)}!") @router.message() @@ -73,6 +76,7 @@ async def on_startup(bot: Bot) -> None: await bot.set_webhook( f"{BASE_WEBHOOK_URL}{WEBHOOK_PATH}", certificate=FSInputFile(WEBHOOK_SSL_CERT), + secret_token=WEBHOOK_SECRET, ) @@ -114,5 +118,5 @@ def main() -> None: if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) + logging.basicConfig(level=logging.INFO, stream=sys.stdout) main() diff --git a/examples/finite_state_machine.py b/examples/finite_state_machine.py index 8539015c..260fdfdf 100644 --- a/examples/finite_state_machine.py +++ b/examples/finite_state_machine.py @@ -5,7 +5,8 @@ from os import getenv from typing import Any, Dict from aiogram import Bot, Dispatcher, F, Router, html -from aiogram.filters import Command +from aiogram.enums import ParseMode +from aiogram.filters import Command, CommandStart from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import ( @@ -15,6 +16,8 @@ from aiogram.types import ( ReplyKeyboardRemove, ) +TOKEN = getenv("BOT_TOKEN") + form_router = Router() @@ -24,7 +27,7 @@ class Form(StatesGroup): language = State() -@form_router.message(Command("start")) +@form_router.message(CommandStart()) async def command_start(message: Message, state: FSMContext) -> None: await state.set_state(Form.name) await message.answer( @@ -91,7 +94,7 @@ async def process_like_write_bots(message: Message, state: FSMContext) -> None: @form_router.message(Form.like_bots) -async def process_unknown_write_bots(message: Message, state: FSMContext) -> None: +async def process_unknown_write_bots(message: Message) -> None: await message.reply("I don't understand you :(") @@ -99,12 +102,12 @@ async def process_unknown_write_bots(message: Message, state: FSMContext) -> Non async def process_language(message: Message, state: FSMContext) -> None: data = await state.update_data(language=message.text) await state.clear() - text = ( - "Thank for all! Python is in my hearth!\nSee you soon." - if message.text.casefold() == "python" - else "Thank for information!\nSee you soon." - ) - await message.answer(text) + + if message.text.casefold() == "python": + await message.reply( + "Python, you say? That's the language that makes my circuits light up! 😉" + ) + await show_summary(message=message, data=data) @@ -121,7 +124,7 @@ async def show_summary(message: Message, data: Dict[str, Any], positive: bool = async def main(): - bot = Bot(token=getenv("TELEGRAM_TOKEN"), parse_mode="HTML") + bot = Bot(token=TOKEN, parse_mode=ParseMode.HTML) dp = Dispatcher() dp.include_router(form_router) @@ -130,4 +133,7 @@ async def main(): if __name__ == "__main__": logging.basicConfig(level=logging.INFO, stream=sys.stdout) - asyncio.run(main()) + try: + asyncio.run(main()) + except (KeyboardInterrupt, SystemExit): + logging.info("Bot stopped!") diff --git a/examples/multibot.py b/examples/multibot.py index 57e75f8e..82fac4a1 100644 --- a/examples/multibot.py +++ b/examples/multibot.py @@ -1,14 +1,16 @@ +import logging +import sys from os import getenv from typing import Any, Dict, Union from aiohttp import web -from finite_state_machine import form_router from aiogram import Bot, Dispatcher, F, Router from aiogram.client.session.aiohttp import AiohttpSession +from aiogram.enums import ParseMode from aiogram.exceptions import TelegramUnauthorizedError from aiogram.filters import Command, CommandObject -from aiogram.fsm.storage.redis import DefaultKeyBuilder, RedisStorage +from aiogram.fsm.storage.memory import MemoryStorage from aiogram.types import Message from aiogram.utils.token import TokenValidationError, validate_token from aiogram.webhook.aiohttp_server import ( @@ -16,11 +18,12 @@ from aiogram.webhook.aiohttp_server import ( TokenBasedRequestHandler, setup_application, ) +from finite_state_machine import form_router main_router = Router() BASE_URL = getenv("BASE_URL", "https://example.com") -MAIN_BOT_TOKEN = getenv("TELEGRAM_TOKEN") +MAIN_BOT_TOKEN = getenv("BOT_TOKEN") WEB_SERVER_HOST = "127.0.0.1" WEB_SERVER_PORT = 8080 @@ -56,10 +59,13 @@ async def on_startup(dispatcher: Dispatcher, bot: Bot): def main(): + logging.basicConfig(level=logging.INFO, stream=sys.stdout) session = AiohttpSession() - bot_settings = {"session": session, "parse_mode": "HTML"} + bot_settings = {"session": session, "parse_mode": ParseMode.HTML} bot = Bot(token=MAIN_BOT_TOKEN, **bot_settings) - storage = RedisStorage.from_url(REDIS_DSN, key_builder=DefaultKeyBuilder(with_bot_id=True)) + storage = MemoryStorage() + # In order to use RedisStorage you need to use Key Builder with bot ID: + # storage = RedisStorage.from_url(REDIS_DSN, key_builder=DefaultKeyBuilder(with_bot_id=True)) main_dispatcher = Dispatcher(storage=storage) main_dispatcher.include_router(main_router) diff --git a/examples/specify_updates.py b/examples/specify_updates.py index b5d22afc..b3c26786 100644 --- a/examples/specify_updates.py +++ b/examples/specify_updates.py @@ -1,8 +1,11 @@ import asyncio import logging +import sys +from os import getenv from aiogram import Bot, Dispatcher, Router -from aiogram.filters import Command +from aiogram.enums import ParseMode +from aiogram.filters import LEAVE_TRANSITION, ChatMemberUpdatedFilter, CommandStart from aiogram.types import ( CallbackQuery, ChatMemberUpdated, @@ -10,8 +13,9 @@ from aiogram.types import ( InlineKeyboardMarkup, Message, ) +from aiogram.utils.markdown import hbold, hcode -TOKEN = "6wo" +TOKEN = getenv("BOT_TOKEN") logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) @@ -19,14 +23,14 @@ logging.basicConfig(level=logging.INFO) router = Router() -@router.message(Command("start")) +@router.message(CommandStart()) async def command_start_handler(message: Message) -> None: """ This handler receives messages with `/start` command """ await message.answer( - f"Hello, {message.from_user.full_name}!", + f"Hello, {hbold(message.from_user.full_name)}!", reply_markup=InlineKeyboardMarkup( inline_keyboard=[[InlineKeyboardButton(text="Tap me, bro", callback_data="*")]] ), @@ -37,7 +41,7 @@ async def command_start_handler(message: Message) -> None: async def chat_member_update(chat_member: ChatMemberUpdated, bot: Bot) -> None: await bot.send_message( chat_member.chat.id, - "Member {chat_member.from_user.id} was changed " + f"Member {hcode(chat_member.from_user.id)} was changed " + f"from {chat_member.old_chat_member.status} to {chat_member.new_chat_member.status}", ) @@ -48,7 +52,7 @@ sub_router = Router() @sub_router.callback_query() async def callback_tap_me(callback_query: CallbackQuery) -> None: - await callback_query.answer("Yeah good, now i'm fine") + await callback_query.answer("Yeah good, now I'm fine") # this router will use only edited_message updates @@ -57,38 +61,39 @@ sub_sub_router = Router() @sub_sub_router.edited_message() async def edited_message_handler(edited_message: Message) -> None: - await edited_message.reply("Message was edited, big brother watch you") + await edited_message.reply("Message was edited, Big Brother watches you") # this router will use only my_chat_member updates deep_dark_router = Router() -@deep_dark_router.my_chat_member() +@deep_dark_router.my_chat_member(~ChatMemberUpdatedFilter(~LEAVE_TRANSITION)) async def my_chat_member_change(chat_member: ChatMemberUpdated, bot: Bot) -> None: await bot.send_message( chat_member.chat.id, - "Member was changed from " - + f"{chat_member.old_chat_member.status} to {chat_member.new_chat_member.status}", + "This Bot`s status was changed from " + + f"{hbold(chat_member.old_chat_member.status)} to {hbold(chat_member.new_chat_member.status)}", ) async def main() -> None: # Initialize Bot instance with a default parse mode which will be passed to all API calls - bot = Bot(TOKEN, parse_mode="HTML") + bot = Bot(TOKEN, parse_mode=ParseMode.HTML) dp = Dispatcher() - dp.include_router(router) + sub_router.include_router(deep_dark_router) - router.include_router(sub_router) - router.include_router(sub_sub_router) + router.include_routers(sub_router, sub_sub_router) + dp.include_router(router) - useful_updates = dp.resolve_used_update_types() - - # And the run events dispatching - await dp.start_polling(bot, allowed_updates=useful_updates) + # Start event dispatching + await dp.start_polling(bot) if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) - asyncio.run(main()) + logging.basicConfig(level=logging.INFO, stream=sys.stdout) + try: + asyncio.run(main()) + except (KeyboardInterrupt, SystemExit): + logger.info("Bot stopped!") diff --git a/examples/web_app/main.py b/examples/web_app/main.py index 0d58042c..db148153 100644 --- a/examples/web_app/main.py +++ b/examples/web_app/main.py @@ -1,4 +1,5 @@ import logging +import sys from os import getenv from aiohttp.web import run_app @@ -10,7 +11,8 @@ from aiogram import Bot, Dispatcher from aiogram.types import MenuButtonWebApp, WebAppInfo from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application -TELEGRAM_TOKEN = getenv("TELEGRAM_TOKEN") +TOKEN = getenv("BOT_TOKEN") + APP_BASE_URL = getenv("APP_BASE_URL") @@ -22,7 +24,7 @@ async def on_startup(bot: Bot, base_url: str): def main(): - bot = Bot(token=TELEGRAM_TOKEN, parse_mode="HTML") + bot = Bot(token=TOKEN, parse_mode="HTML") dispatcher = Dispatcher() dispatcher["base_url"] = APP_BASE_URL dispatcher.startup.register(on_startup) @@ -45,5 +47,5 @@ def main(): if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) + logging.basicConfig(level=logging.INFO, stream=sys.stdout) main()