SDK для создания Telegram ботов на Cloudflare Workers
Project description
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
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 edgebot-0.3.0.tar.gz.
File metadata
- Download URL: edgebot-0.3.0.tar.gz
- Upload date:
- Size: 30.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7babd2457d8bd6e047827824b421aabc8fa01f335b53b8d3dfd38d304f233ed3
|
|
| MD5 |
c0120a675b6adfc77a1493c56bf50ef6
|
|
| BLAKE2b-256 |
76823ee7b4ede19015ee805dfd0c1666f9b0024e0036e7ba1c70d0772a00a3dc
|
File details
Details for the file edgebot-0.3.0-py3-none-any.whl.
File metadata
- Download URL: edgebot-0.3.0-py3-none-any.whl
- Upload date:
- Size: 42.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60e1ffff5fc916633b0598b0a639c22e26c75266e368d8608f821f4806a020d3
|
|
| MD5 |
d0862a187ec6197ace65fe51342a381c
|
|
| BLAKE2b-256 |
a4f383f08aef8c62e1cfa251f6e1c82452cdfd822aec0008da0e1b0bf44b9439
|