Back to Home

obabot for Telegram and Max bots

obabot — asynchronous Python library for launching bots in Telegram and Max with a single aiogram-compatible API. Supports FSM, filters, keyboards. Migration is minimal, test mode without tokens.

Launching a bot in Telegram and Max with obabot
Advertisement 728x90

obabot: Universal Library for Telegram and Max Bots

The obabot library lets you deploy a single asynchronous Python bot across both Telegram and Max simultaneously. Its API is fully compatible with aiogram 3.x: it uses native aiogram for Telegram and an adapter for Max that converts events into compatible objects. Installation is a one-liner: pip install obabot aiogram>=3.0.0 umaxbot>=0.1.7.

The core create_bot() function takes platform tokens and returns a tuple (bot, dispatcher, router). Message handlers are written uniformly, regardless of the platform.

Initialization Examples

For Telegram:

Google AdInline article slot
from obabot import create_bot
from obabot.filters import Command

bot, dp, router = create_bot(tg_token="YOUR_TG_TOKEN")

@router.message(Command("start"))
async def start(message):
    await message.answer(f"Hi from {message.platform}!")

await dp.start_polling(bot)

For Max:

from obabot import create_bot
from obabot.filters import Command

bot, dp, router = create_bot(max_token="YOUR_MAX_TOKEN")

@router.message(Command("start"))
async def start(message):
    await message.answer(f"Hi from {message.platform}!")

await dp.start_polling(bot)

For both platforms in parallel:

from obabot import create_bot
from obabot.filters import Command

bot, dp, router = create_bot(
    tg_token="YOUR_TG_TOKEN",
    max_token="YOUR_MAX_TOKEN"
)

@router.message(Command("start"))
async def start(message):
    await message.answer(f"Hi from {message.platform}!")

await dp.start_polling(bot)

The message.platform attribute returns "telegram" or "max" for platform-specific logic.

Google AdInline article slot

Migration from aiogram

Switching from pure aiogram 3.x requires minimal changes: just swap imports and use create_bot() instead of separate Bot, Dispatcher, and Router.

Original aiogram code:

from aiogram import Bot, Dispatcher, Router
from aiogram.filters import Command
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.context import FSMContext

bot = Bot(token="TOKEN")
dp = Dispatcher()
router = Router()
dp.include_router(router)

@router.message(Command("start"))
async def start(message):
    await message.answer("Hi!")

await dp.start_polling(bot)

After migration:

Google AdInline article slot
from obabot import create_bot
from obabot.filters import Command
from obabot.fsm import State, StatesGroup, FSMContext

bot, dp, router = create_bot(tg_token="TOKEN")

@router.message(Command("start"))
async def start(message):
    await message.answer("Hi!")

await dp.start_polling(bot)

The rest of your code—including FSM, filters, and keyboards—stays exactly the same.

Message Interface and Basic Methods

All Message objects share a unified interface:

  • message.text — text
  • message.from_user — sender
  • message.chat — chat
  • message.message_id — ID
  • message.platform — platform

Methods:

  • await message.answer("Reply")
  • await message.reply("Reply to message")
  • await message.delete()
  • await message.edit_text("New text")

FSM: State Machine

FSM works just like aiogram:

from obabot.fsm import State, StatesGroup, FSMContext

class Form(StatesGroup):
    name = State()
    age = State()

@router.message(Command("start"))
async def start(message, state: FSMContext):
    await state.set_state(Form.name)
    await message.answer("What's your name?")

@router.message(Form.name)
async def process_name(message, state: FSMContext):
    await state.update_data(name=message.text)
    await state.set_state(Form.age)
    await message.answer("How old are you?")

Inline Keyboards and Filters

Creating a keyboard:

from obabot.types import InlineKeyboardMarkup, InlineKeyboardButton

keyboard = InlineKeyboardMarkup(inline_keyboard=[
    [
        InlineKeyboardButton(text="Button 1", callback_data="btn1"),
        InlineKeyboardButton(text="Button 2", callback_data="btn2"),
    ]
])

await message.answer("Pick one:", reply_markup=keyboard)

Filters:

  • @router.message(Command("start", "help")) — multiple commands
  • @router.message(F.text.startswith("!")) — prefix
  • @router.message(F.photo) — photos
  • @router.callback_query(F.data == "click") — callbacks

Testing Without Tokens

test_mode=True skips all network calls:

bot, dp, router = create_bot(test_mode=True)

Supports Python 3.10–3.14, aiogram 3.0.0–3.24. Full unit test coverage.

What's Important

  • Single codebase: One handler for Telegram and Max—no adapters needed.
  • Full compatibility: 100% aiogram 3.x API + platform attribute.
  • FSM and filters: Complete support for states, inline keyboards, magic filters F.
  • Easy migration: Swap 2 import lines and initialization.
  • Testing: Isolated mode with no API calls.

— Editorial Team

Advertisement 728x90

Read Next