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: int

HTTPResponse.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 не означает, что ядро принимает произвольные поля. Известные операции имеют закрытый набор параметров. Их исходные значения также сверяются с кодом в маршрутах.

Параметры **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] = None

edit_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' = None

send_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] = None

edit_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' = None

send_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_deniedAccessDeniedПраво, ограничение ресурсов или полномочия события не позволяют действие
conflictConflictДанные или ревизия изменились
invalid_input, invalidInvalidInputНеверные параметры
invalid_stateInvalidStateНедопустимое текущее состояние
quota_exceededQuotaExceededПревышен лимит
unavailableUnavailableСлужба или участник недоступны
floodFloodWaitTelegram ограничил частоту; 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 модуля. Изменение расположения внутреннего файла не должно требовать правки пользовательского модуля.