EdgeBot SDK
Минималистичный SDK для Telegram-ботов на Cloudflare Workers (Pyodide).
Установка
pip install edgebot
Требования: Python 3.10+, Cloudflare Workers с флагом python_workers.
Структура пакета
Пакет импортируется как edgebot и разделён на подмодули, каждый из которых можно использовать независимо:
edgebot— ядро:Bot,Context,InlineKeyboard, исключения.edgebot.ui— UI-движок: атомарные экраны (UIComponent) и реестры клавиатур/промптов (KeyboardRegistry,PromptRegistry,Prompt).edgebot.storage— персистентность на Cloudflare KV:KVStore,UserRegistry.edgebot.utils— общие утилиты: HTTP-клиент с ретраями, структурированный логгер, таймстемпы.edgebot.ffi— граница Python ↔ JS: нормализацияnull/undefined, конвертация Python-структур в plain JS-объекты.
Наиболее используемые имена реэкспортированы из корня пакета: Bot, Context, InlineKeyboard, KVStore, UserRegistry, UIComponent, KeyboardRegistry, PromptRegistry, Prompt, sleep, to_py, to_js_object, EdgeBotError.
Возможности
Bot — маршрутизация обновлений Telegram
Декораторная регистрация хендлеров по типу события:
- Команды и текст:
@bot.on_command("/start"),@bot.on_message. - Inline-кнопки:
@bot.on_callback(фолбэк после UI-компонентов). - Медиа:
@bot.on_photo,on_video,on_audio,on_voice,on_document,on_sticker,on_animation. - Чеклисты:
@bot.on_checklist,on_checklist_tasks_done.
Привязка к Workers runtime и подключение реестров через чейнинг:
bot.env(env).ctx(ctx)— биндинги Cloudflare иExecutionContext;bot.with_users(UserRegistry(env.USERS))→ctx.users;bot.with_keyboards(KeyboardRegistry())→ctx.keyboards;bot.with_prompts(PromptRegistry())→ctx.prompts;bot.register_ui(ScreenA(), ScreenB(), ...)— UI-экраны, которые перехватывают callback по префиксу до классическихon_callback.
Context — данные апдейта и ответ
В каждом хендлере доступны разобранные поля: chat_id, message_id, text, command, callback_data, from_user, медиа-объекты (photo, video, voice и т.д.), а также ссылки на ctx.env, ctx.bot, ctx.users, ctx.keyboards, ctx.prompts.
Методы отправки: send, reply, edit_message, answer_callback, send_photo, send_video, send_audio, send_voice, send_document, send_sticker, send_animation.
InlineKeyboard — builder кнопок
Fluent API: button(text, callback_data=...|url=...), row(), extend(other), build(prefix=None).
callback_data может быть относительным ("toggle") или абсолютным ("/menu:home" — слэш срезается, префикс игнорируется). При вызове build(prefix="prof") все относительные пути префиксуются: "toggle" → "prof:toggle". Это позволяет UI-движку поздно связывать клавиатуру с префиксом конкретного компонента; build() идемпотентен и не мутирует исходные кнопки.
edgebot.ui — UI-движок (экраны и реестры)
Архитектура — MVC, адаптированная под stateless-Workers:
UIComponent— атомарный экран. Наследник задаёт уникальный__prefix__, опциональноprompt/markup(ключи в реестрах) или inline-text, реализуетopen(ctx, **kwargs)и методы, помеченные@UIComponent.callback("pattern"). Паттерн поддерживает плейсхолдеры{name}(превращаются в kwargs метода через regex) и спец-значение"*"— fallback в рамках префикса. Методыsend()/update()сами достают промпт и клавиатуру из реестров и отправляют сообщение;update()молча гасит ошибку Telegram «message is not modified».KeyboardRegistry— реестр фабрик клавиатур. Фабрика регистрируется через@registry.register("key")и обязана возвращатьInlineKeyboard(не сырой dict — финализацию делает движок).PromptRegistry+Prompt— реестр текстовых вьюх.Prompt— композитный билдер (append/prepend+render(**kwargs)черезstr.format_map).- Авто-фильтр kwargs. Реестры пропускают через фабрику только те ключи, что объявлены в её сигнатуре, чтобы один общий dict экрана можно было передавать и в клавиатуру, и в промпт без ловушек.
- Уникальность префиксов. Регистрация двух компонентов с одинаковым
__prefix__бросаетDuplicatePrefixError.
edgebot.storage — Cloudflare KV
KVStore— тонкая обёртка над KV-биндингом. Методы:get,put,delete,exists,list,iter_keys,get_with_metadata. Сам решает по типу значения, что кодировать как JSON, что оставить строкой, а что отправить какUint8Array.UserRegistry— реестр пользователей поверх KV. Методы:get,create,upsert,update,delete,iter_ids,items. Автоматически проставляетcreated_at/updated_at.
edgebot.utils
http.fetch— обёртка надjs.fetchс ретраями на 408/429/500/502/503/504, учётомRetry-After.http.sleep(ms)— async-задержка через JSsetTimeout.log— структурированный JSON-логгер:debug,info,warn,error. Уровни мапятся наjs.console.*, поэтому в Cloudflare Workers Logs корректно выставляется полеlevel.now()— ISO-8601 UTC-таймстемп.
edgebot.ffi
to_js_object(value)— Pythondict/list→ plain JSObject/Arrayдля API, которым нужен Object, а не Map (KV options, D1, R2, Queues, DO stub RPC).to_py(value)— JsProxy → Python, с нормализациейnull/undefinedвNone. Устойчив к разной модульной структуре Pyodide в разных сборках.
Исключения
Все ошибки SDK наследуются от EdgeBotError. Дочерние ветки:
StorageError→KVError,UsersError→UserAlreadyExists;UIError→KeyboardNotFound,PromptNotFound,DuplicatePrefixError.
Быстрый старт
from workers import Response, WorkerEntrypoint
from edgebot import Bot, Context, InlineKeyboard, UserRegistry
def build_bot(env) -> Bot:
users = UserRegistry(env.USERS)
bot = Bot(env.BOT_TOKEN, parse_mode="Markdown").with_users(users)
@bot.on_command("/start")
async def start(ctx: Context):
kb = InlineKeyboard()
kb.button("Нажми меня", callback_data="hello")
await ctx.send("Привет!", reply_markup=kb.build())
@bot.on_callback
async def cb(ctx: Context):
await ctx.answer_callback("Ок")
@bot.on_message
async def echo(ctx: Context):
await ctx.reply(ctx.text)
return bot
class Default(WorkerEntrypoint):
def __init__(self, ctx, env):
super().__init__(ctx, env)
self.bot = build_bot(env)
self.bot.env(env).ctx(ctx)
self.env = env
async def fetch(self, request):
secret = request.headers.get("X-Telegram-Bot-Api-Secret-Token")
if not secret or secret != self.env.WEBHOOK_TOKEN:
return Response("{}", status=401)
update = await request.json()
await self.bot.process_update(update)
return Response('{"ok": true}')
Пример UI-экрана
from edgebot import (
Bot, Context, InlineKeyboard,
UIComponent, KeyboardRegistry, PromptRegistry, Prompt,
)
keyboards = KeyboardRegistry()
prompts = PromptRegistry()
@prompts.register("profile_main")
def profile_prompt(username: str, is_active: bool) -> Prompt:
p = Prompt("*Профиль*: {username}")
p.append("Статус: активен" if is_active else "Статус: выключен")
return p
@keyboards.register("profile_main")
def profile_kb(is_active: bool) -> InlineKeyboard:
kb = InlineKeyboard()
kb.button("Выключить" if is_active else "Включить", callback_data="toggle")
return kb
class ProfileScreen(UIComponent):
__prefix__ = "prof"
prompt = "profile_main"
markup = "profile_main"
async def open(self, ctx: Context) -> None:
user = await ctx.users.get(ctx.from_user["id"])
kw = {"username": user["username"], "is_active": user["is_active"]}
await self.send(ctx, prompt_kwargs=kw, markup_kwargs=kw)
@UIComponent.callback("toggle")
async def on_toggle(self, ctx: Context) -> None:
user_id = ctx.from_user["id"]
user = await ctx.users.get(user_id)
await ctx.users.update(user_id, is_active=not user["is_active"])
user = await ctx.users.get(user_id)
kw = {"username": user["username"], "is_active": user["is_active"]}
await self.update(ctx, prompt_kwargs=kw, markup_kwargs=kw)
await ctx.answer_callback()
bot = (
Bot(token, parse_mode="Markdown")
.with_keyboards(keyboards)
.with_prompts(prompts)
.register_ui(ProfileScreen())
)
Полный пример с профилем в KV, inline-кнопками, UI-экранами, медиа-эхо и GitHub Actions-деплоем — в examples/echo/.
Деплой
Бот запускается как Cloudflare Worker (флаг python_workers). См. examples/echo/DEPLOY.md и examples/echo/wrangler.toml как референс.
Лицензия
MIT
Metadata
Release files for edgebot 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| edgebot-0.3.0.tar.gz | 30.6 kB | Without provenance | Details for edgebot-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| edgebot-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for edgebot-0.3.0-py3-none-any.whl |
Total release size: 72.8 kB
Release files / edgebot-0.3.0.tar.gz
| Download URL | edgebot-0.3.0.tar.gz |
|---|---|
| Size | 30.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7babd2457d8bd6e047827824b421aabc8fa01f335b53b8d3dfd38d304f233ed3
|
|
BLAKE2b-256 checksum How to use checksums |
76823ee7b4ede19015ee805dfd0c1666f9b0024e0036e7ba1c70d0772a00a3dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Release files / edgebot-0.3.0-py3-none-any.whl
| Download URL | edgebot-0.3.0-py3-none-any.whl |
|---|---|
| Size | 42.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
60e1ffff5fc916633b0598b0a639c22e26c75266e368d8608f821f4806a020d3
|
|
BLAKE2b-256 checksum How to use checksums |
a4f383f08aef8c62e1cfa251f6e1c82452cdfd822aec0008da0e1b0bf44b9439
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Security for edgebot 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.