Cluster · разработчикам
Типы, параметры и ошибки SDK
Справочник методов · Bot API и Telethon · Права
Обозначения
Типы здесь описывают контракт, а не новые обязательные импорты. Модуль получает готовые app, loader и контекст обработчика. Объекты берутся из фабрик app; прямые конструкторы обычно не нужны. Self означает тот же объект, AsyncIterator[T] перебирается через async for, Iterator[T] обычным for. У асинхронного генератора не нужно писать await перед созданием итератора.
Callable обозначает функцию или метод; её аргументы зависят от места использования. Для сортировки нужен синхронный ключ, для событий синхронный фильтр, для кнопки допускается асинхронная подпись. object используется только там, где значение преобразуется через str() либо передаётся стандартным протоколом Python.
from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Iterator
from datetime import datetime
from types import TracebackType
from typing import Literal, NotRequired, Self, TypedDict
type JSON = None | bool | int | float | str | list[JSON] | dict[str, JSON]
type Path = str | int | tuple[str | int, ...] | list[str | int]
type QueryValue = None | bool | int | float | str | list[str | int | float | bool] | tuple[str | int | float | bool, ...]
type SocketValue = str | bytes | None
type ButtonLabel = str | Callable[..., str | Awaitable[str]]JSON в локальных Document, записях и аргументах обработчиков копируется. Ключи словарей строковые, вложенность не больше 32, целые числа от -2**63 до 2**64 - 1, вещественные числа конечные. Кортеж не является JSON-массивом: для значения используй список. У TL нормализация отдельно преобразует tuple в list, bytes и datetime в JSON-оболочки.
missing в сигнатуре означает внутренний маркер пропущенного аргумента. Передавать или импортировать его не нужно. Например, record.save() сохраняет прежний срок, а record.save(ttl=None) снимает его.
Формы результатов
Ниже перечислены поля, на которые опирается SDK. Служебные ответы могут содержать дополнительные поля. revision базы, файлов и задач является непрозрачной строкой; ревизии настроек ядра и конфигурации являются целыми числами. Не заменяй одну ревизию другой.
class KeysPage(TypedDict):
keys: list[str]
after: str | None
class FileInfo(TypedDict):
id: str
unit: str
instance: str
filename: str
size: int
sha256: str | None
written: int
state: Literal["uploading", "ready"]
created: float
expires: float | None
ttl: int | None
class FilesPage(TypedDict):
files: list[FileInfo]
after: str | None
class HTTPResponse(TypedDict):
status: int
headers: dict[str, str]
body: str
binary: bool
class UploadResponse(TypedDict):
status: int
body: str
class SocketFrame(TypedDict):
data: str
binary: bool
class TelegramUser(TypedDict):
id: int
first_name: str
last_name: str
username: str | None
bot: bool
class BotUser(TypedDict):
id: int
username: str | None
first_name: str | None
is_bot: bool | None
class TelegramMessage(TypedDict):
id: int
chat: int
text: str
date: int
sender: NotRequired[int | None]
ephemeral_message_id: NotRequired[int]
receiver_user: NotRequired[dict[str, JSON]]
class FormMessage(TelegramMessage):
form: str
class CoreInfo(TypedDict):
version: str
name: str
prefix: str
uptime: int
modules: int
running: int
class ModuleInfo(TypedDict):
instance: str
name: str | dict[str, str]
state: str
class ModulesPage(TypedDict):
modules: list[ModuleInfo]
next: int | None
class RevisionResult(TypedDict):
revision: int
class SettingsSnapshot(TypedDict):
instance: str
fields: dict[str, dict[str, JSON]]
values: dict[str, JSON]
revision: int
prefix: str
commands: NotRequired[bool]
class TaskInfo(TypedDict):
id: str
instance: str
handler: str
values: dict[str, JSON]
at: float
interval: int | None
retry: int
attempt: int
state: str
result: JSON
error: JSON
class EndpointInfo(TypedDict):
path: str
token: str
class ExchangePage(TypedDict):
events: list[dict[str, JSON]]
after: intHTTPResponse.body содержит base64 только при binary=True. Response.content превращает его в bytes. Маршрут загрузки файла на внешний сайт возвращает UploadResponse: у созданного из него Response будут пустые заголовки и текстовое тело.
TaskInfo показывает основные поля. Состояния выполнения: pending, running, done, failed, cancelled. Повторяющаяся успешно выполненная задача может сразу вернуться в pending. Ожидание Task.wait() рассчитано на завершение задачи; для наблюдения за повторяющейся задачей используй load() и её данные.
AgreementInfo является dict[str, JSON] с ID, участниками, конкретным ресурсом, режимом, направлением, одобрением и состоянием договора. Точная структура и состояния описаны в обмене. Результат передачи содержит state, data и transfer; очистка событий возвращает {"removed": int}.
EventData является словарём события источника: chat, id, sender, text и данные соответствующего события. EventEnvelope является оболочкой диспетчера с data, actor, sender, ticket, group и handler; модуль обычно получает готовый Event, а не строит эту оболочку. Поля ctx.args и ctx.values описаны в модулях.
FieldDefinition, ElementDefinition и PageDefinitionData являются словарями объявлений конфигурации и страницы. Их полный формат, включая items, when, columns, actions и локализации, приведён в описании интерфейса модуля. LayerData и CanvasData являются словарями сцены, формат и пределы приведены в Image Generator.
LogEntry является словарём с id: int, time: float, level: str, group: str, instance: str и message: str.
LogWriter обозначает app.logs или связанный Journal. Остальные имена классов в сигнатурах ведут к отдельным страницам справочника.
Дополнительные параметры коротких методов
**kwargs в Python не означает, что ядро принимает произвольные поля. Известные операции имеют закрытый набор параметров. Их исходные значения также сверяются с кодом в маршрутах.
app.db.get/delete: module: str | None, slot: str | None.setдополнительно ttl: int | None.keysдополнительно after: str | None и limit: int = 100, от 1 до 1000. None выбирает текущий раздел или отсутствие курсора.app.web.request: method: str = "GET", body: str | None = None, headers: dict[str, str] | None = None, binary: bool = False. Методы GET, POST, PUT, PATCH, DELETE, HEAD. Изначальный предел ответа 256 KiB; конкретные лимиты определяет ядро.app.web.profile: proxy: str | None = None, cookies: bool = True, domain: str | None = None. Прокси и домен проходят проверку ядра; domain является областью проверки права, не заменой URL. Формат прокси описан в сети.app.web.download: maximum: int = 10485760.upload: method: str = "POST", headers: dict[str, str] | None = None.receive: wait: float = 10.app.files.list: after: str | None = None, limit: int = 100, от 1 до 100.File.edit: filename: str и/или ttl: int | None; неизвестные поля запрещены.app.tasks.create: interval: int | None = None, retry: int = 0, request: str | None = None, after: float | None = None. Интервал 1..315360000, retry 0..10, after 0..315360000. При after аргумент at должен быть None.Task.editдопускает at, after, interval, retry и values; at и after одновременно запрещены.app.exchange.request: fields: dict[str, JSON] | None = None, mode: str = "permanent", direction: str = "receive", group: str | None = None.list: after: str | None = None, limit: int = 100.transfer: value: JSON = None.app.inline.button: key: str | None, values: dict[str, JSON] | None, args: JSON, visible: bool = True, enabled: bool = True и параметры конкретного вида кнопки. URL, callback, switch_inline_query и другие действия должны соответствовать правилам кнопок.Form.form: chat: int, text: str | Text, keyboard: Keyboard или buttons со строками кнопок, media: Media | dict[str, str] | None, ttl: int = 3600, users: list[int] | None, receiver: int | None, replace: bool = False, method: str = "sendMessage", via: str = "bot", options: dict[str, JSON] | None. Прочие поля отправки объединяются с options. Поддержка via="inline" и адресатов описана в конструкторах.Form.edit: text, keyboard/buttons, media, options и поля редактирования. Идентификаторы чата, сообщения и адресата управляются ядром; переопределять их через options нельзя.app.config.field: label, default, required, min, max, choices, items, when по типу поля. Типы и ограничения одинаковы сFieldи схемой модуля.app.ui.element: поля выбранного вида элемента из схемы страницы.Table.rowпринимает только объявленные колонки и простые JSON-значения.
Параметры **query и Request.form(**values) являются именами параметров внешнего запроса, а не аргументами операции ядра. Они кодируются как query или form-urlencoded. Дополнительные поля журналов и аргументы функций модулей тоже являются данными, но должны проходить соответствующую JSON-схему.
Параметры Telegram-клиента
Ниже перечислены дополнительные параметры выделенных методов app.client. Параметры file и progress_callback из Telethon напрямую не передаются: файлы управляются ядром, а callback не сериализуется.
send_message
reply_to: 'typing.Union[int, types.Message]' = None,
attributes: 'typing.Sequence[types.TypeDocumentAttribute]' = None,
parse_mode: Optional[str] = (),
formatting_entities: Optional[List[Union[telethon.tl.types.MessageEntityUnknown, telethon.tl.types.MessageEntityMention, telethon.tl.types.MessageEntityHashtag, telethon.tl.types.MessageEntityBotCommand, telethon.tl.types.MessageEntityUrl, telethon.tl.types.MessageEntityEmail, telethon.tl.types.MessageEntityBold, telethon.tl.types.MessageEntityItalic, telethon.tl.types.MessageEntityCode, telethon.tl.types.MessageEntityPre, telethon.tl.types.MessageEntityTextUrl, telethon.tl.types.MessageEntityMentionName, telethon.tl.types.InputMessageEntityMentionName, telethon.tl.types.MessageEntityPhone, telethon.tl.types.MessageEntityCashtag, telethon.tl.types.MessageEntityUnderline, telethon.tl.types.MessageEntityStrike, telethon.tl.types.MessageEntityBankCard, telethon.tl.types.MessageEntitySpoiler, telethon.tl.types.MessageEntityCustomEmoji, telethon.tl.types.MessageEntityBlockquote, telethon.tl.types.MessageEntityFormattedDate, telethon.tl.types.MessageEntityDiffInsert, telethon.tl.types.MessageEntityDiffReplace, telethon.tl.types.MessageEntityDiffDelete]]] = None,
link_preview: bool = True,
thumb: 'hints.FileLike' = None,
force_document: bool = False,
clear_draft: bool = False,
buttons: Optional[ForwardRef('hints.MarkupLike')] = None,
silent: bool = None,
background: bool = None,
supports_streaming: bool = False,
schedule: 'hints.DateLike' = None,
comment_to: 'typing.Union[int, types.Message]' = None,
nosound_video: bool = None,
send_as: Optional[ForwardRef('hints.EntityLike')] = None,
message_effect_id: Optional[int] = Noneedit_message
parse_mode: Optional[str] = (),
attributes: 'typing.Sequence[types.TypeDocumentAttribute]' = None,
formatting_entities: Optional[List[Union[telethon.tl.types.MessageEntityUnknown, telethon.tl.types.MessageEntityMention, telethon.tl.types.MessageEntityHashtag, telethon.tl.types.MessageEntityBotCommand, telethon.tl.types.MessageEntityUrl, telethon.tl.types.MessageEntityEmail, telethon.tl.types.MessageEntityBold, telethon.tl.types.MessageEntityItalic, telethon.tl.types.MessageEntityCode, telethon.tl.types.MessageEntityPre, telethon.tl.types.MessageEntityTextUrl, telethon.tl.types.MessageEntityMentionName, telethon.tl.types.InputMessageEntityMentionName, telethon.tl.types.MessageEntityPhone, telethon.tl.types.MessageEntityCashtag, telethon.tl.types.MessageEntityUnderline, telethon.tl.types.MessageEntityStrike, telethon.tl.types.MessageEntityBankCard, telethon.tl.types.MessageEntitySpoiler, telethon.tl.types.MessageEntityCustomEmoji, telethon.tl.types.MessageEntityBlockquote, telethon.tl.types.MessageEntityFormattedDate, telethon.tl.types.MessageEntityDiffInsert, telethon.tl.types.MessageEntityDiffReplace, telethon.tl.types.MessageEntityDiffDelete]]] = None,
link_preview: bool = True,
thumb: 'hints.FileLike' = None,
force_document: bool = False,
buttons: Optional[ForwardRef('hints.MarkupLike')] = None,
supports_streaming: bool = False,
schedule: 'hints.DateLike' = Nonesend_file
caption: Union[str, Sequence[str]] = None,
force_document: bool = False,
mime_type: str = None,
file_size: int = None,
clear_draft: bool = False,
reply_to: 'hints.MessageIDLike' = None,
attributes: 'typing.Sequence[types.TypeDocumentAttribute]' = None,
thumb: 'hints.FileLike' = None,
allow_cache: bool = True,
parse_mode: Optional[str] = (),
formatting_entities: Union[List[Union[telethon.tl.types.MessageEntityUnknown, telethon.tl.types.MessageEntityMention, telethon.tl.types.MessageEntityHashtag, telethon.tl.types.MessageEntityBotCommand, telethon.tl.types.MessageEntityUrl, telethon.tl.types.MessageEntityEmail, telethon.tl.types.MessageEntityBold, telethon.tl.types.MessageEntityItalic, telethon.tl.types.MessageEntityCode, telethon.tl.types.MessageEntityPre, telethon.tl.types.MessageEntityTextUrl, telethon.tl.types.MessageEntityMentionName, telethon.tl.types.InputMessageEntityMentionName, telethon.tl.types.MessageEntityPhone, telethon.tl.types.MessageEntityCashtag, telethon.tl.types.MessageEntityUnderline, telethon.tl.types.MessageEntityStrike, telethon.tl.types.MessageEntityBankCard, telethon.tl.types.MessageEntitySpoiler, telethon.tl.types.MessageEntityCustomEmoji, telethon.tl.types.MessageEntityBlockquote, telethon.tl.types.MessageEntityFormattedDate, telethon.tl.types.MessageEntityDiffInsert, telethon.tl.types.MessageEntityDiffReplace, telethon.tl.types.MessageEntityDiffDelete]], List[List[Union[telethon.tl.types.MessageEntityUnknown, telethon.tl.types.MessageEntityMention, telethon.tl.types.MessageEntityHashtag, telethon.tl.types.MessageEntityBotCommand, telethon.tl.types.MessageEntityUrl, telethon.tl.types.MessageEntityEmail, telethon.tl.types.MessageEntityBold, telethon.tl.types.MessageEntityItalic, telethon.tl.types.MessageEntityCode, telethon.tl.types.MessageEntityPre, telethon.tl.types.MessageEntityTextUrl, telethon.tl.types.MessageEntityMentionName, telethon.tl.types.InputMessageEntityMentionName, telethon.tl.types.MessageEntityPhone, telethon.tl.types.MessageEntityCashtag, telethon.tl.types.MessageEntityUnderline, telethon.tl.types.MessageEntityStrike, telethon.tl.types.MessageEntityBankCard, telethon.tl.types.MessageEntitySpoiler, telethon.tl.types.MessageEntityCustomEmoji, telethon.tl.types.MessageEntityBlockquote, telethon.tl.types.MessageEntityFormattedDate, telethon.tl.types.MessageEntityDiffInsert, telethon.tl.types.MessageEntityDiffReplace, telethon.tl.types.MessageEntityDiffDelete]]], NoneType] = None,
voice_note: bool = False,
video_note: bool = False,
buttons: Optional[ForwardRef('hints.MarkupLike')] = None,
silent: bool = None,
background: bool = None,
supports_streaming: bool = False,
schedule: 'hints.DateLike' = None,
comment_to: 'typing.Union[int, types.Message]' = None,
ttl: int = None,
nosound_video: bool = None,
send_as: Optional[ForwardRef('hints.EntityLike')] = None,
message_effect_id: Optional[int] = Noneedit_file
parse_mode: Optional[str] = (),
attributes: 'typing.Sequence[types.TypeDocumentAttribute]' = None,
formatting_entities: Optional[List[Union[telethon.tl.types.MessageEntityUnknown, telethon.tl.types.MessageEntityMention, telethon.tl.types.MessageEntityHashtag, telethon.tl.types.MessageEntityBotCommand, telethon.tl.types.MessageEntityUrl, telethon.tl.types.MessageEntityEmail, telethon.tl.types.MessageEntityBold, telethon.tl.types.MessageEntityItalic, telethon.tl.types.MessageEntityCode, telethon.tl.types.MessageEntityPre, telethon.tl.types.MessageEntityTextUrl, telethon.tl.types.MessageEntityMentionName, telethon.tl.types.InputMessageEntityMentionName, telethon.tl.types.MessageEntityPhone, telethon.tl.types.MessageEntityCashtag, telethon.tl.types.MessageEntityUnderline, telethon.tl.types.MessageEntityStrike, telethon.tl.types.MessageEntityBankCard, telethon.tl.types.MessageEntitySpoiler, telethon.tl.types.MessageEntityCustomEmoji, telethon.tl.types.MessageEntityBlockquote, telethon.tl.types.MessageEntityFormattedDate, telethon.tl.types.MessageEntityDiffInsert, telethon.tl.types.MessageEntityDiffReplace, telethon.tl.types.MessageEntityDiffDelete]]] = None,
link_preview: bool = True,
thumb: 'hints.FileLike' = None,
force_document: bool = False,
buttons: Optional[ForwardRef('hints.MarkupLike')] = None,
supports_streaming: bool = False,
schedule: 'hints.DateLike' = Nonesend_album
caption: Union[str, Sequence[str]] = None,
force_document: bool = False,
mime_type: str = None,
file_size: int = None,
clear_draft: bool = False,
reply_to: 'hints.MessageIDLike' = None,
attributes: 'typing.Sequence[types.TypeDocumentAttribute]' = None,
thumb: 'hints.FileLike' = None,
allow_cache: bool = True,
parse_mode: Optional[str] = (),
formatting_entities: Union[List[Union[telethon.tl.types.MessageEntityUnknown, telethon.tl.types.MessageEntityMention, telethon.tl.types.MessageEntityHashtag, telethon.tl.types.MessageEntityBotCommand, telethon.tl.types.MessageEntityUrl, telethon.tl.types.MessageEntityEmail, telethon.tl.types.MessageEntityBold, telethon.tl.types.MessageEntityItalic, telethon.tl.types.MessageEntityCode, telethon.tl.types.MessageEntityPre, telethon.tl.types.MessageEntityTextUrl, telethon.tl.types.MessageEntityMentionName, telethon.tl.types.InputMessageEntityMentionName, telethon.tl.types.MessageEntityPhone, telethon.tl.types.MessageEntityCashtag, telethon.tl.types.MessageEntityUnderline, telethon.tl.types.MessageEntityStrike, telethon.tl.types.MessageEntityBankCard, telethon.tl.types.MessageEntitySpoiler, telethon.tl.types.MessageEntityCustomEmoji, telethon.tl.types.MessageEntityBlockquote, telethon.tl.types.MessageEntityFormattedDate, telethon.tl.types.MessageEntityDiffInsert, telethon.tl.types.MessageEntityDiffReplace, telethon.tl.types.MessageEntityDiffDelete]], List[List[Union[telethon.tl.types.MessageEntityUnknown, telethon.tl.types.MessageEntityMention, telethon.tl.types.MessageEntityHashtag, telethon.tl.types.MessageEntityBotCommand, telethon.tl.types.MessageEntityUrl, telethon.tl.types.MessageEntityEmail, telethon.tl.types.MessageEntityBold, telethon.tl.types.MessageEntityItalic, telethon.tl.types.MessageEntityCode, telethon.tl.types.MessageEntityPre, telethon.tl.types.MessageEntityTextUrl, telethon.tl.types.MessageEntityMentionName, telethon.tl.types.InputMessageEntityMentionName, telethon.tl.types.MessageEntityPhone, telethon.tl.types.MessageEntityCashtag, telethon.tl.types.MessageEntityUnderline, telethon.tl.types.MessageEntityStrike, telethon.tl.types.MessageEntityBankCard, telethon.tl.types.MessageEntitySpoiler, telethon.tl.types.MessageEntityCustomEmoji, telethon.tl.types.MessageEntityBlockquote, telethon.tl.types.MessageEntityFormattedDate, telethon.tl.types.MessageEntityDiffInsert, telethon.tl.types.MessageEntityDiffReplace, telethon.tl.types.MessageEntityDiffDelete]]], NoneType] = None,
voice_note: bool = False,
video_note: bool = False,
buttons: Optional[ForwardRef('hints.MarkupLike')] = None,
silent: bool = None,
background: bool = None,
supports_streaming: bool = False,
schedule: 'hints.DateLike' = None,
comment_to: 'typing.Union[int, types.Message]' = None,
ttl: int = None,
nosound_video: bool = None,
send_as: Optional[ForwardRef('hints.EntityLike')] = None,
message_effect_id: Optional[int] = NoneВ этих сигнатурах используются названия типов Telethon только для объяснения формы. В SDK вместо TL-объекта передавай описание из app.client.types, вместо callable только предусмотренные JSON-данные. thumb является ID готового файла Cluster и требует files.read. Общие альтернативные адресаты send_as и comment_to дополнительно требуют telegram.full без непроверяемых ограничений. parse_mode допускает только None, html, md или markdown в нижнем регистре. Имена параметров сверяются с закреплённой Telethon 1.45.0.
У app.client.update_profile допускаются first_name, last_name и about, каждое строкой до 256 символов. Должно быть хотя бы одно поле. Текст send_message/edit_message до 4096 символов; подпись файла и каждого элемента альбома до 1024. get_messages.limit от 1 до 100, download_media.maximum от 1 до 1073741824 байт. Все ID сообщений целые от 1 до 2**31 - 1; chat является ненулевым int с модулем не больше 2**53.
У app.bot поля берутся из закреплённого каталога Bot API. Для метода send_file верхние параметры kind, caption, reply_markup и options обрабатывает SDK, остальные объединяются с options. Тип вложения выбирает sendPhoto/sendDocument и аналогичный метод. Общий запрос не может обходить запреты на служебные операции бота.
Ошибки
app.errors содержит классы исключений и фабрику error(). Все специализированные ошибки наследуют SDKError, а тот наследует RuntimeError. code является машинным кодом, retry_after задержкой либо None. Строка исключения может содержать исходный словарь ошибки; для логики используй code.
| Код | Исключение | Причина |
|---|---|---|
| access_denied | AccessDenied | Право, ограничение ресурсов или полномочия события не позволяют действие |
| conflict | Conflict | Данные или ревизия изменились |
| invalid_input, invalid | InvalidInput | Неверные параметры |
| invalid_state | InvalidState | Недопустимое текущее состояние |
| quota_exceeded | QuotaExceeded | Превышен лимит |
| unavailable | Unavailable | Служба или участник недоступны |
| flood | FloodWait | Telegram ограничил частоту; retry_after задаёт задержку |
| другие | SDKError | В том числе authorization, forbidden, offline и unknown из внешних служб |
async def save_count(value: int) -> bool:
try:
await app.db.set("count", value)
except app.errors.AccessDenied:
return False
return TrueЛокальные конструкторы и редакторы также вызывают обычные ValueError, TypeError, KeyError, IndexError, UnicodeDecodeError, JSONDecodeError или ошибки пользовательского callable. Ошибка канала связи с ядром является RuntimeError. TimeoutError ограничивает ожидание; CancelledError означает отмену текущей корутины. Уже отправленное внешнее действие при этом могло выполниться. Не повторяй автоматически оплату, отправку сообщения, запрос договора или вызов зависимости после неизвестного результата.
Границы справочника
Включены службы app, Event, loader, конструкторы сообщений, объекты ресурсов, графические сцены и доступные пространства Bot API/TL. Конструкторы и протоколы контекстных менеджеров и итераторов тоже описаны. Python-методы представления __repr__ и стандартные методы BaseException не образуют отдельного контракта Cluster.
Router, загрузчик пакетов Packages, транспорт терминала, worker и вспомогательные функции сериализации являются внутренней реализацией. Не используй их как API модуля. Изменение расположения внутреннего файла не должно требовать правки пользовательского модуля.