Modern and fully asynchronous framework for Yandex Messenger Bot API
Project description
yandex-messenger-bot
Unofficial library. Not affiliated with or endorsed by Yandex.
Modern, fully asynchronous Python framework for the Yandex Messenger Bot API.
Features
- Full API coverage — all 14 endpoints (messages, files, images, galleries, polls, chats, webhooks)
- Pydantic v2 models for all API types with strict validation
- Router/Dispatcher system with depth-first propagation
- MagicFilter support (
F.text,F.chat.type == "group", etc.) - FSM (Finite State Machine) with pluggable storage
- Dependency injection via
Annotated[T, Inject()]with type checker support - Middleware chain (inner + outer)
- Polling and webhook transports with auto-retry and backoff
- Typed exception hierarchy mapped to HTTP status codes
py.typed— full inline type annotations
Installation
pip install yandex-messenger-bot
Or with uv:
uv add yandex-messenger-bot
Quick Start
import asyncio
from yandex_messenger_bot import Bot, Dispatcher, Router
from yandex_messenger_bot.filters.command import CommandFilter
from yandex_messenger_bot.types import Update
router = Router()
@router.on_message(CommandFilter("start"))
async def on_start(update: Update, bot: Bot) -> None:
await bot.send_text(chat_id=update.chat.id, text="Hello!")
async def main() -> None:
bot = Bot(token="your-oauth-token")
dp = Dispatcher()
dp.include_router(router)
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Sending Messages
# Text
await bot.send_text(chat_id="chat-id", text="Hello!")
# By user login
await bot.send_text(login="user@company.ru", text="Hi there")
# File
from yandex_messenger_bot.types import FSInputFile
doc = FSInputFile("report.pdf")
await bot.send_file(chat_id="chat-id", document=doc)
# Image
img = FSInputFile("photo.jpg")
await bot.send_image(chat_id="chat-id", image=img)
# Gallery (up to 10 images)
images = [FSInputFile(f"img_{i}.jpg") for i in range(5)]
await bot.send_gallery(chat_id="chat-id", images=images, text="Album caption")
Buttons (SuggestButtons)
from yandex_messenger_bot.types import Directive, InlineSuggestButton, SuggestButtons
buttons = SuggestButtons(
buttons=[
[
InlineSuggestButton(
title="Open site",
directives=[Directive(type="open_uri", url="https://example.com")],
),
InlineSuggestButton(
title="Confirm",
directives=[
Directive(type="server_action", name="confirm", payload={"id": 42})
],
),
]
],
persist=True,
)
await bot.send_text(chat_id="chat-id", text="Choose:", suggest_buttons=buttons)
Handling Button Callbacks
from yandex_messenger_bot.filters.callback import ServerActionFilter
from yandex_messenger_bot.types import ServerAction
@router.on_bot_request(ServerActionFilter("confirm"))
async def on_confirm(update: Update, bot: Bot, server_action: ServerAction) -> None:
item_id = server_action.payload["id"]
await bot.send_text(chat_id=update.chat.id, text=f"Confirmed item {item_id}")
FSM (Conversation State)
from yandex_messenger_bot import State, StatesGroup, FSMContext
from yandex_messenger_bot.filters.state import StateFilter
class Form(StatesGroup):
name = State()
email = State()
@router.on_message(CommandFilter("form"))
async def form_start(update: Update, bot: Bot, state: FSMContext) -> None:
await state.set_state(Form.name)
await bot.send_text(chat_id=update.chat.id, text="Enter your name:")
@router.on_message(StateFilter(Form.name))
async def process_name(update: Update, bot: Bot, state: FSMContext) -> None:
await state.update_data(name=update.text)
await state.set_state(Form.email)
await bot.send_text(chat_id=update.chat.id, text="Enter your email:")
@router.on_message(StateFilter(Form.email))
async def process_email(update: Update, bot: Bot, state: FSMContext) -> None:
data = await state.get_data()
await state.clear()
await bot.send_text(
chat_id=update.chat.id,
text=f"Saved: {data['name']}, {update.text}",
)
Middleware
from yandex_messenger_bot import BaseMiddleware
from yandex_messenger_bot.types import Update
class LoggingMiddleware(BaseMiddleware):
async def __call__(self, handler, update: Update, data: dict) -> None:
print(f"Update {update.update_id} from {update.from_user}")
return await handler(update, data)
router.message.outer_middleware(LoggingMiddleware())
Dependency Injection
from typing import Annotated
from yandex_messenger_bot import Inject
class Database:
async def get_user(self, login: str) -> dict: ...
async def get_database() -> Database:
return Database()
# Register globally
dp.dependency(Database, factory=get_database)
# Or inline via Annotated
@router.on_message(CommandFilter("profile"))
async def profile(
update: Update,
bot: Bot,
db: Annotated[Database, Inject(factory=get_database)],
) -> None:
user = await db.get_user(update.from_user.login)
await bot.send_text(chat_id=update.chat.id, text=str(user))
Webhook Mode
from aiohttp import web
from yandex_messenger_bot.webhook import WebhookHandler
async def main() -> None:
bot = Bot(token="your-token")
dp = Dispatcher()
dp.include_router(router)
# Set webhook URL in Yandex
await bot.self_update(webhook_url="https://your-server.com/webhook")
# Start aiohttp server
wh = WebhookHandler(dp, bot, secret_token="your-secret")
app = web.Application()
wh.setup(app, path="/webhook")
web.run_app(app, port=8080)
Polls
# Create a poll
await bot.create_poll(
chat_id="chat-id",
title="Lunch spot?",
answers=["Sushi", "Pizza", "Burgers"],
max_choices=1,
is_anonymous=False,
)
# Get results
results = await bot.get_poll_results(chat_id="chat-id", message_id=12345)
print(results.voted_count, results.answers)
Chat Management
# Create a group chat
result = await bot.create_chat(
name="Project Team",
description="Discussion",
members=["alice@company.ru", "bob@company.ru"],
admins=["alice@company.ru"],
)
# Add/remove members
await bot.update_members(
chat_id=result.chat_id,
members_add=["charlie@company.ru"],
members_remove=["bob@company.ru"],
)
Error Handling
from yandex_messenger_bot.exceptions import (
APIError,
TooManyRequestsError,
ForbiddenError,
)
try:
await bot.send_text(chat_id="chat-id", text="Hello")
except TooManyRequestsError as e:
print(f"Rate limited, retry after {e.retry_after}s")
except ForbiddenError:
print("Bot doesn't have access to this chat")
except APIError as e:
print(f"API error {e.status_code}: {e.description}")
Development
# Install dependencies
uv sync --all-groups
# Run tests
uv run pytest
# Lint
uv run ruff check .
# Format
uv run ruff format .
# Type check
uv run ty check yandex_messenger_bot/
Releasing
- Bump
__version__inyandex_messenger_bot/_meta.pyand commit. - Create and push a tag:
git tag vX.Y.Z && git push origin vX.Y.Z. - The Release to PyPI workflow runs tests, builds, and publishes via OIDC.
One-time PyPI setup (Trusted Publisher + GitHub pypi environment): see docs/pypi-publishing.md.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file yandex_messenger_bot-0.1.0.tar.gz.
File metadata
- Download URL: yandex_messenger_bot-0.1.0.tar.gz
- Upload date:
- Size: 33.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
88914320a408f925b84a79ea2cad2245e6f3d5db562e3e197dbf02d86036ba39
|
|
| MD5 |
104bf6ea3b2c6c7c40d199a0fa5bf0f1
|
|
| BLAKE2b-256 |
ce1e899d59a91481ad3d59341005f007c884754d408ab574ab00f740146181af
|
Provenance
The following attestation bundles were made for yandex_messenger_bot-0.1.0.tar.gz:
Publisher:
release.yml on arielen/yandex-messenger-bot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
yandex_messenger_bot-0.1.0.tar.gz -
Subject digest:
88914320a408f925b84a79ea2cad2245e6f3d5db562e3e197dbf02d86036ba39 - Sigstore transparency entry: 1579313184
- Sigstore integration time:
-
Permalink:
arielen/yandex-messenger-bot@1bc2df769424ac9ead01681f9b730cfef0ce4bd7 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/arielen
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1bc2df769424ac9ead01681f9b730cfef0ce4bd7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file yandex_messenger_bot-0.1.0-py3-none-any.whl.
File metadata
- Download URL: yandex_messenger_bot-0.1.0-py3-none-any.whl
- Upload date:
- Size: 56.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1ccf86a56011cb8ad18e0b36d81ab70d6193ffa33b083aa3ea5571f6641e32a
|
|
| MD5 |
d2de0adffd5b6390541c303efb66d0f0
|
|
| BLAKE2b-256 |
5debe1649e0a78f7d3aa4a40b482d22478a7a60030dd579a3f91a185bede8e83
|
Provenance
The following attestation bundles were made for yandex_messenger_bot-0.1.0-py3-none-any.whl:
Publisher:
release.yml on arielen/yandex-messenger-bot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
yandex_messenger_bot-0.1.0-py3-none-any.whl -
Subject digest:
a1ccf86a56011cb8ad18e0b36d81ab70d6193ffa33b083aa3ea5571f6641e32a - Sigstore transparency entry: 1579313553
- Sigstore integration time:
-
Permalink:
arielen/yandex-messenger-bot@1bc2df769424ac9ead01681f9b730cfef0ce4bd7 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/arielen
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1bc2df769424ac9ead01681f9b730cfef0ce4bd7 -
Trigger Event:
push
-
Statement type: