Async framework for Max messenger bots (aiogram-style)
Project description
AioScam
Async Python framework for building Max messenger bots, inspired by aiogram architecture.
Version
v0.2.1 — Latest (2026-06-21)
What's new in v0.2.1
- ✅
HomePage— generic landing page for the server root, so a plain visitor/scanner sees a normal-looking page with no hint that/api/*exists; mount the real Mini App frontend under its own path instead - ✅
WebAppMiddleware404/401 split — a request with noinitDataat all now gets a plain 404 (looks like the route doesn't exist); only a present-but-wrong signature gets 401 - ✅
api_prefix— move the API off the well-known/apipath;examples/webapp_bot.pywires it throughWEBAPP_API_PREFIXend to end, including server-side template rewriting for all 4 frontend pages - ✅
WebAppFailGuard— in-memory sliding-window per-IP ban for repeat failed-auth probing - ✅ 633/633 tests passing
What's new in v0.2.0
- ✅
aioscam.webapp— server-side module for Max WebApps (mini apps):validate_init_data()/validate_contact()(HMAC-SHA256),EventStreamManager(SSE push, Bot → WebApp),WebAppMiddleware(validatesinitDataon/api/*requests, static files stay public) - ✅
BotCapabilities—BotCapabilities.probe(bot)reports what the bot can actually do at startup, sinceGET /mecarries no permissions field;caps.log_report(logger)prints a banner - ✅ Hint-based exceptions everywhere — every framework exception now carries a
.hintwith a concrete cause/fix, appended automatically tostr(exc)(ApiError,NetworkError,RetryAfter,UnauthorizedError,BotTokenError,DispatcherError, and the newWebApp*exceptions) - ✅
examples/webapp_bot.py+examples/webapp/*.html— working bot with a REST+SSE API and 4 frontend pages (native Bridge SDK reference, Vue 3 demo, Chart.js, sortable table) - ✅ 604/604 tests passing
What's new in v0.1.8.1
- ✅
AIOSCAM_ENV/AIOSCAM_API_URL— исправлен регистр env-переменных (Aioscam_ENV→AIOSCAM_ENV), на Linux mixed-case переменные не работали - ✅ PolyForm Noncommercial License 1.0.0 — смена лицензии с MIT
Features
- 🚀 Fully async —
asyncio+aiohttp - 🎯 aiogram-style API — familiar decorators and patterns
- 🔄 Router system — modular, nestable
- 🎭 Magic Filters —
F.text,F.callback.payload,F.message.body.text - 🔧 Middleware — request/response pipeline
- 📦 FSM —
State,StatesGroup,MemoryStorage - 🛡️ StateGuard — blocks unauthorized commands during active FSM
- 🖼️ Media — upload/download images, video, audio, documents
- 📱 Contact & Location — inline buttons for phone and geolocation
- 📝 Formatting — Markdown and HTML
- 🔗 Deep links —
create_deep_link,parse_deep_link,StartCommandfilter - 🌍 I18n — JSON-based translations, auto locale from
user_locale - 📋 Bot Commands —
set_my_commands(),set_bot_info() - 🗑️ Message Management — delete, pin, edit
- 🌐 Webhook — aiohttp, FastAPI, Litestar
- 📡 Polling — long-polling with exponential backoff
- 🛡️ Rate Limiter — token bucket, 429 retry, exponential backoff
- 🔒 Security — webhook secret token, circular router detection
- 🪟 WebApp (Mini Apps) —
initData/contact validation, SSE push (Bot → WebApp),/api/*middleware - 📊
BotCapabilities— capability/permission report logged at startup - 💡 Hint-based exceptions — every error tells you what to actually do about it
- 📦 Python 3.9–3.12
Installation
git clone https://github.com/alex-di-96/aioscam.git
cd aioscam
pip install -e .
pip install aioscam[fastapi] # FastAPI webhook
pip install aioscam[litestar] # Litestar webhook
pip install aioscam[dev] # pytest, ruff, mypy
aioscam.webapp (WebApp/Mini App support — validate_init_data, EventStreamManager,
WebAppMiddleware) needs no extra install — it only uses aiohttp and pydantic, both
already required by the base package. A plain pip install aioscam is enough.
Quick Start
import asyncio
from aioscam import Bot, Dispatcher, Router
from aioscam.filters import Command, F
dp = Dispatcher()
router = Router()
@router.message_created(Command("start"))
async def cmd_start(event):
await event.answer("Hello! Send me anything.")
@router.message_created()
async def echo(event):
await event.answer(event.text)
dp.include_router(router)
async def main():
bot = Bot() # token from MAX_BOT_TOKEN env
await dp.start_polling(bot)
asyncio.run(main())
Media
from aioscam import InputMedia, InputMediaBuffer, UploadType
# Send from file path (type auto-detected from extension)
await bot.send_photo(chat_id=cid, user_id=uid, photo="photo.jpg")
await bot.send_video(chat_id=cid, user_id=uid, video="video.mp4", caption="Watch!")
await bot.send_document(chat_id=cid, user_id=uid, document="report.pdf")
await bot.send_audio(chat_id=cid, user_id=uid, audio="song.mp3")
# Auto-detect type
await bot.send_media(chat_id=cid, user_id=uid, media="anyfile.ext")
# From bytes buffer (no file on disk)
data = open("photo.jpg", "rb").read()
await bot.send_photo(chat_id=cid, user_id=uid, photo=data)
# Download incoming media into memory
image_bytes = await bot.download_file_bytes(url, token)
# Download to disk with unique temp name
path = Bot.make_temp_path(".jpg") # /tmp/aioscam_20260528_143022_847291.jpg
await bot.download_file(path, url, token)
FSM
from aioscam.fsm import State, StatesGroup
class Registration(StatesGroup):
waiting_name = State()
waiting_age = State()
waiting_email = State()
waiting_phone = State()
@router.message_created(Command("register"))
async def start(event, state):
await state.set_state(Registration.waiting_name)
await event.answer("Step 1/4: Enter your name:")
@router.message_created(StateFilter(Registration.waiting_name))
async def name(event, state):
await state.update_data(name=event.text)
await state.set_state(Registration.waiting_age)
await event.answer("Step 2/4: Enter your age:")
WebApp (Max Mini Apps)
Server-side helpers for two-way communication between your bot and a Max WebApp (mini app) running in the client's WebView:
from aioscam.webapp import validate_init_data, EventStreamManager
from aioscam.webapp.aiohttp import WebAppMiddleware
from aioscam.utils.capabilities import BotCapabilities
# Validate the signed initData a WebApp page sends you
data = validate_init_data(raw_init_data, bot_token) # -> WebAppInitData (HMAC-SHA256 checked)
# Push events from the bot to a connected WebApp over SSE
events = EventStreamManager()
await events.publish(user_id, {"type": "bot_message", "text": "hi from the bot"})
# Protect your /api/* routes (static files stay public)
app.middlewares.append(WebAppMiddleware(bot_token=bot.token))
# Log what this bot can actually do at startup
caps = await BotCapabilities.probe(bot, webapp_url="https://example.com/webapp")
caps.log_report(logger)
Serve the server's HTTP root with a generic landing page instead of a hand-written index.html —
works with no JS (plain "Open in Max" link) and gives a scanner/plain visitor no hint that /api/*
exists. Mount your actual Mini App frontend under its own path (e.g. /app) and register
that path in the Max bot dashboard, not the bare root:
from aioscam.webapp.aiohttp import HomePage
app.router.add_get("/", HomePage(bot).handler)
WebAppMiddleware already returns 404 (not 401) when a request carries no initData at all, so
blind probing of /api/* paths looks identical to a route that doesn't exist — only requests with
a (wrong) signature get a 401. For stronger masking, move the API off the well-known /api prefix
and add a WebAppFailGuard to flat-404 repeat offenders instead of letting them keep guessing:
from aioscam.webapp.aiohttp import WebAppFailGuard, WebAppMiddleware
guard = WebAppFailGuard(max_failures=20, window=60, ban_seconds=300)
app.middlewares.append(
WebAppMiddleware(bot_token=bot.token, api_prefix="/a8f3e1", fail_guard=guard)
)
Full working example with a REST+SSE backend and 4 frontend pages (native Bridge SDK reference,
Vue 3, Chart.js, sortable table): examples/webapp_bot.py + examples/webapp/*.html — run it with
WEBAPP_API_PREFIX=/your-secret to see api_prefix move the whole API and have the frontend pick
it up automatically (the server rewrites const API_PREFIX = "/api"; in each served page).
Rate Limiter
from aioscam import Bot
from aioscam.limiter import RateLimitConfig
bot = Bot(rate_limit=RateLimitConfig.strict()) # 5 req/s, burst 10
bot = Bot(rate_limit=RateLimitConfig.relaxed()) # 30 req/s, burst 50
API Coverage
| Category | Methods |
|---|---|
| Bot Info | get_me, get_me_from_chat, change_info, set_bot_info, set_my_commands |
| Messages | send_message, edit_message, delete_message, get_message, get_messages, pin_message, delete_pin_message, get_pin_message |
| Media | send_photo, send_video, send_audio, send_document, send_media, download_file, download_file_bytes, get_upload_url, get_video |
| Callbacks | send_callback, send_action |
| Chats | get_chats, get_chat_by_id, get_chat_by_link, edit_chat, delete_chat, add_chat_members, remove_member_chat, add_list_admin_chat, remove_admin, get_chat_members, get_chat_member, get_list_admin_chat, delete_me_from_chat |
| Updates | get_updates, get_last_marker |
| Webhooks | subscribe_webhook, unsubscribe_webhook, get_subscriptions |
14 event types: message_created, message_callback, message_edited, message_removed, bot_started, bot_stopped, bot_added, bot_removed, chat_title_changed, dialog_cleared, dialog_muted, dialog_unmuted, user_added, user_removed
8 button types: CallbackButton, LinkButton, ChatButton, MessageButton, ClipboardButton, OpenAppButton, RequestContactButton, RequestGeoLocationButton
Project Structure
aioscam/
├── bot/ # Bot client — all API methods
├── client/ # HTTP client (aiohttp, rate-limited, file upload/download)
├── dispatcher/ # Dispatcher, Router, EventContext, StateGuard
├── enums/ # 15 enum files
├── exceptions/ # 12 exception classes
├── filters/ # Command, Text, StartCommand, StateFilter, ContentType, F
├── fsm/ # State, StatesGroup, MemoryStorage
├── handler/ # MessageHandler, CallbackHandler, EventHandler
├── i18n/ # I18n — JSON translations, locale detection
├── limiter/ # RateLimiter, RateLimitConfig
├── methods/ # BaseMethod, GetMe, SendMessage, GetUpdates
├── middleware/ # BaseMiddleware, MiddlewareManager
├── types/ # Pydantic models — User, Chat, Message, Attachment, etc.
├── utils/ # KeyboardBuilder, formatting, deep_linking, media, BotCapabilities
└── webapp/ # validate_init_data, validate_contact, EventStreamManager, WebAppMiddleware
Configuration
MAX_BOT_TOKEN=your_token
AIOSCAM_ENV=prod # debug | test | prod
Testing
python -m pytest tests/ -v
633/633 tests passing (100%)
Example Bots
| File | Description |
|---|---|
echo_bot.py |
Simple echo bot |
fsm_bot.py |
FSM registration flow |
keyboard_bot.py |
Inline keyboard demo |
callback_bot.py |
send_callback demo |
middleware_bot.py |
Logging + timing middleware |
router_bot.py |
Multi-router architecture |
webhook_bot.py |
Webhook mode (aiohttp) |
deep_link_bot.py |
Deep links + referral |
deep_link_test_bot.py |
Deep link raw update debugger |
i18n_bot.py |
Multilingual bot (ru/en) |
media_bot.py |
Media upload/download demo |
rate_limited_bot.py |
Rate limiter demo |
methods_bot.py |
Methods API demo |
demo_bot.py |
Full-featured demo (FSM, media, i18n, deep links, SQLAlchemy) |
run_bot.py |
Minimal launcher |
webapp_bot.py |
WebApp REST+SSE backend — examples/webapp/*.html frontends |
License
PolyForm Noncommercial License 1.0.0 — aLex Di
Free for personal, educational, and other noncommercial use. Commercial use requires a separate license — contact the author.
☕ Support the Project
If you find AioScam useful and want to support its development, consider:
- 🍪 Buy me a cookie — Boosty
- ⭐ Star the repo — it helps with visibility
- 🐛 Report bugs — open issues for any problems you find
- 🤝 Contribute — PRs welcome!
Every cookie fuels more features, faster bug fixes, and better documentation! 🚀
Project details
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 aioscam-0.2.1.tar.gz.
File metadata
- Download URL: aioscam-0.2.1.tar.gz
- Upload date:
- Size: 126.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70526b45d00b55d1a1f600e7ccaadf5a922a4c5ae380502b6a85023638aebfb6
|
|
| MD5 |
75dc41875bfb3959dd82fc6eef905126
|
|
| BLAKE2b-256 |
15f82314ffa1d1fa69289cfcdf208ec928a9802300a4194793078f4c61498e31
|
File details
Details for the file aioscam-0.2.1-py3-none-any.whl.
File metadata
- Download URL: aioscam-0.2.1-py3-none-any.whl
- Upload date:
- Size: 99.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c620b9fe5a0683ea6bdfdd4cfde903200c2f6ece3529858167ef0b608e8b971a
|
|
| MD5 |
c2a8cd1e593cd099237158639d7b7e6b
|
|
| BLAKE2b-256 |
95cf8e424016e5164b7f2dec7008d328896782eebd6a75e45b66ea8bad725ceb
|