ZNAKORA / DEVELOPERS

Автоматизация профиля
на Python

От первого запуска до собственного рабочего сценария. Авторизация, все методы API, примеры и точные границы доступа в одном руководстве.

API / SDK 0.1.3ПК и собственный VPSОбновлено 06.11.202630 операций RPC
Содержание руководства

Что такое юзербот

Юзербот — Python-программа, которая выполняет разрешённые вами действия от имени вашего профиля Знакоры. Скрипт работает на вашем компьютере или собственном VPS.

В руководстве описаны установка библиотеки, вход с кодом подтверждения, все методы API и примеры готовых сценариев. Версия API и SDK: 0.1.3. Список доступных методов можно получить через /api/userbots/v1/capabilities.

Перед запуском. Используйте только понятный вам код. Ввод кода запуска разрешает программе перечисленные действия с аккаунтом. Никому не передавайте пароль и коды подтверждения.

Доступны сообщения, ответы, пересылка, история, поиск, файлы, опросы, координаты, списки, реакции, закрепления, описание и имя профиля, действия модератора и отложенная отправка.

Быстрый старт на Windows

  1. Установите Python 3.10 или новее с официального сайта. Проверьте python --version.
  2. Скачайте исходный архив SDK 0.1.3, распакуйте его в отдельную папку.
  3. Откройте PowerShell в папке, содержащей pyproject.toml. Создайте виртуальное окружение и установите пакет.
  4. Установка добавляет cryptography 50.0.2–50.x для Ed25519 и пакет tzdata для часовых поясов, в том числе на Windows и минимальных VPS.
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install .
.\.venv\Scripts\python.exe examples/discover_chats.py

Первая программа показывает ID доступных обычных чатов. Она не получает право чтения сообщений. Введите email, пароль и код запуска из системного чата. После вывода списка программа завершит свой допуск. Выберите нужные ID и запустите основной пример.

$env:ZNAKORA_CHAT_IDS = "ID_ПЕРВОГО_ЧАТА,ID_ВТОРОГО_ЧАТА"
$env:ZNAKORA_EMAIL = "you@example.com"
.\.venv\Scripts\python.exe examples/autoreply.py

Пароль будет запрошен скрытым вводом. Переменная ZNAKORA_PASSWORD поддерживается примером, но хранение пароля в файле или переменных окружения увеличивает доступность секрета другим программам вашего пользователя. Не отправляйте конфигурацию, дампы памяти или файлы окружения другим людям.

Linux, macOS и собственный VPS

python3 -m venv .venv
.venv/bin/python -m pip install .
.venv/bin/python examples/discover_chats.py
export ZNAKORA_CHAT_IDS="ID_ЧАТА"
.venv/bin/python examples/autoreply.py

На VPS запускайте программу под отдельным непривилегированным пользователем. Установите корректное время и ограничьте доступ к папке состояния. Для первого входа нужен терминал, чтобы ввести коды. SDK 0.1.2 сохраняет подтверждённый вход и восстанавливает его при перезапуске; после истечения или отзыва допуска понадобятся новые коды. После окончания допуска автоматический рестарт службы не сможет обойти новое подтверждение.

Повторяющиеся локальные задачи выполняются, пока работает Python. Серверные одноразовые задания хранятся в Знакоре, но тоже зависят от действующего допуска, его срока, активности и прав.

При использовании systemd или другого менеджера процессов не передавайте пароль в аргументах командной строки. Не настраивайте бесконечный цикл попыток авторизации: лимиты действуют совместно для всех запусков аккаунта.

Вход и код подтверждения

  1. Запустите скрипт и укажите email и пароль своего аккаунта.
  2. Выберите разрешения и чаты, с которыми сможет работать программа.
  3. Откройте системный чат Знакоры в приложении или браузере. Проверьте название скрипта, названия чатов и запрошенные разрешения.
  4. Введите шестизначный код запуска в терминале скрипта.
  5. Если у аккаунта включена дополнительная защита, введите также запрошенный код из письма или аутентификатора. При запросе доверенного устройства подтвердите вход в разделе «Безопасность → Защита входа», сверив номер.
  6. После подтверждения скрипт начнёт работу. В системном чате появится уведомление с кнопкой остановки.

Код запуска действует 3 минуты и используется один раз. Допускается максимум 5 проверок. Если срок истёк, начните запуск заново. Ошибка дополнительного кода тоже расходует попытку; ожидание подтверждения устройства проверяется отдельно.

Для получения кода нужна возможность открыть системный чат в уже авторизованном приложении или браузере. Если действующего входа нет, сначала войдите в Знакору обычным способом.

Подтверждайте только собственный запуск. Если вы не запускали скрипт или не согласны с его разрешениями, не вводите код. Не отправляйте его собеседникам, в поддержку или на сторонние сайты.

Неверный код, повторный запуск и отмена входа

Для входа могут понадобиться разные коды: код запуска из системного чата, дополнительный код из последнего письма этого запуска и код приложения-аутентификатора. Не подставляйте один код вместо другого. Письмо от предыдущего входа не подходит к новому запросу.

В SDK 0.1.1 метод login() при неверном коде предлагает повторно ввести именно этот код в рамках того же challenge_id. Остальные введённые факторы сохраняются в памяти до завершения входа. Всего сервер разрешает не более пяти проверок за три минуты; каждый неверный дополнительный код тоже расходует попытку. Пробелы и переносы строк при копировании игнорируются, но код должен состоять из шести цифр.

EMAIL_CODE_INVALID означает ошибку кода из письма, TOTP_INVALID — кода аутентификатора, CODE_INVALID — кода из системного чата. В ответе может быть error.attempts_remaining. После исчерпания попыток или срока нужен новый запрос с новыми кодами. Включённые дополнительные факторы остаются обязательными.

Если нажать Ctrl+C или вход закончится ошибкой, SDK пытается отменить свой незавершённый запрос. Для этого сервер должен быть доступен и поддерживать API 0.1.1. При принудительном завершении процесса или потере сети отмена может не дойти. Тогда новый login() один раз ждёт оставшееся время CHALLENGE_PENDING — до 180 секунд — и запрашивает новые коды. В терминале показывается время ожидания. Параметр wait_pending=False отключает это ожидание для собственной обработки ошибки.

# Если вы управляете входом вручную:
started = bot.start_login(email, password, name="My script",
                          scopes=["profile.bio.write"], chat_ids=[])
# finish_login(...): неверный код можно исправить с тем же challenge_id.
# Если решили отказаться от входа:
bot.cancel_login(started["challenge_id"])
# bot.close() также отменяет незавершённый вход этого Client.

POST /auth/cancel: JSON {"challenge_id":"…"}, подпись тем же временным ключом, без токена. Успех: {"ok":true,"status":"cancelled"}. Отменённые коды больше не действуют. Другой скрипт, знающий только ID запроса, отменить его не может. Отмена входа не останавливает уже подключённого юзербота: для этого используйте logout() подключённого клиента или список сеансов. В сохраняемом режиме close() оставляет допуск для следующего запуска.

Сохранение входа между запусками

Начиная с SDK 0.1.2 обычный bot.login(...) по умолчанию сохраняет подтверждённый допуск. При первом входе нужны пароль, код запуска и все включённые дополнительные факторы. При следующем запуске с теми же email, адресом сервера, названием скрипта, разрешениями и чатами библиотека восстанавливает тот же сеанс и проверяет его на сервере. Повторного кода не будет, пока этот допуск действует.

Сеанс живёт не более 30 дней с момента выдачи и допускает не более 7 дней без подписанных обращений. Heartbeat поддерживает активность, но не продлевает общий срок. Отзыв, смена пароля или настроек защиты, блокировка и потеря прав продолжают закрывать доступ. После отзыва или истечения SDK запросит новый подтверждённый вход.

Сохраняются только данные допуска, ключ подписи и курсоры событий — в зашифрованном .session-файле. Пароль и одноразовые коды не записываются в этот файл. Для расшифрования следующему вызову login() нужен тот же пароль аккаунта. Если ваш собственный скрипт сам вызывает getpass() при старте, он продолжит спрашивать пароль: это отдельная логика вашего скрипта.

По умолчанию файлы находятся в каталоге текущего пользователя: Linux — ~/.local/share/znakora-userbot/sessions/ с учётом XDG_DATA_HOME, Windows — %LOCALAPPDATA%/znakora-userbot/sessions/. Файл выбирается автоматически отдельно для каждого набора доступа. Один файл может использовать один процесс. Не переносите и не публикуйте его; даже зашифрованная копия относится к данным входа.

with Client() as bot:
    bot.login(email, password, name="My script",
              scopes=["events.read", "messages.send"], chat_ids=[CHAT_ID])
    bot.run()
# Выход из with закрывает локальный клиент, сохраняя этот сеанс.

# Для полного выхода вместо закрытия вызывайте у подключённого клиента:
# bot.logout()

# Прежний режим без сохранения, с новым кодом при каждом входе:
bot.login(email, password, name="One-time script",
          scopes=["profile.bio.write"], chat_ids=[], persist_session=False)

Свой путь задаётся как session_file="/private/directory/my.session". В Linux каталог должен принадлежать пользователю с правами 700, файл — с правами 600. Ошибки SESSION_FILE_BUSY и SESSION_FILE_UNAVAILABLE означают занятый файл или невозможность безопасно читать/писать его. Не исправляйте это выдачей прав всем пользователям. При повреждении или смене пароля понадобится новый полноценный вход; действующий сеанс с потерянным файлом можно остановить в Знакоре.

Для собственного HTTP-клиента: persistent:true задаётся в подписанном JSON POST /auth/start. В запросе подтверждения этот режим менять нельзя. По умолчанию прямой HTTP API и ручной start_login() используют прежний одноразовый режим. В ответе запуска, подтверждения и get_usage есть признак persistent; idle_timeout указан в секундах. Режим сохранения показывается владельцу в системном сообщении перед вводом кода.

В цикле run() курсоры полностью обработанных страниц сохраняются вместе с сеансом. Если вы вызываете get_updates() самостоятельно, сохраняйте возвращённый cursor после своей обработки. После обычного перезапуска скрипт продолжает с этой позиции, а не с начала допуска. При аварии в середине страницы уже выполненное внешнее действие может повториться: используйте устойчивые ID событий и собственную идемпотентность.

Разрешения и выбор чатов

Разрешения показываются владельцу до ввода кода. Они неизменны в рамках допуска. Расширение возможностей или списка чатов требует остановить допуск и получить новый код. Не запрашивайте больше прав, чем требуется сценарию.

РазрешениеЧто открывает
chats.listСписок обычных чатов без чтения сообщений
events.readНовые сообщения и изменения в выбранных чатах
history.readИстория и поиск в выбранных чатах
messages.sendОтправка сообщений в выбранные чаты
messages.forwardПересылка между выбранными чатами
messages.editРедактирование своих сообщений
messages.deleteУдаление своих сообщений
reactions.writeУстановка и снятие реакций
pins.writeЗакрепление и открепление сообщений
read.writeОтметка сообщений прочитанными
files.readСкачивание доступных вложений из выбранных чатов
files.writeЗагрузка файлов до 8 МиБ и их отправка
profile.bio.writeИзменение описания профиля
profile.name.writeИзменение имени профиля
moderation.writeУдаление сообщений участников и обработка заявок

chat_ids — список ID чатов из 24 шестнадцатеричных символов, максимум 20. Это не numeric ID пользователя и не @handle. Символ «все чаты» не поддерживается. Право chats.list позволяет отдельно получить ID обычных чатов без чтения сообщений; после выбора нужен новый допуск на конкретные чаты.

Системные диалоги, поддержка, встроенный ИИ и диалоги с bot-аккаунтами исключены. После выхода из группы или потери прав доступ закрывается. Пересылка требует разрешения и на источник, и на назначение; для вложений также нужно files.read.

Никакое разрешение юзербота не делает профиль администратором. Запрет личной переписки, блокировки, медленный режим и действующие права сохраняются при работе юзербота.

Готовый автоответчик

Пример из архива SDK. Он отвечает не чаще раза в час каждому отправителю в каждом чате. Состояние хранится локально; сообщения автоматизации и свои исходящие сообщения по умолчанию игнорируются.

"""Run on your PC/VPS. Never share your environment file or password."""
import getpass
import os
from znakora_userbot import Client, AwayResponder, StateStore

chat_ids = [value.strip() for value in os.environ["ZNAKORA_CHAT_IDS"].split(",") if value.strip()]
email = os.environ.get("ZNAKORA_EMAIL") or input("Email: ")
password = os.environ.get("ZNAKORA_PASSWORD") or getpass.getpass("Пароль: ")

with Client() as bot:
    bot.login(email, password, name="Away responder", scopes=["events.read", "messages.send"], chat_ids=chat_ids)
    del password
    store = StateStore()
    bot.on("message")(AwayResponder(bot, "Сейчас занят. Отвечу позже.", cooldown=3600, store=store))
    try:
        bot.run()
    except KeyboardInterrupt:
        bot.stop()
    finally:
        store.close()

Минимальные права: events.read и messages.send. Для первого опыта используйте отдельный тестовый аккаунт и один тестовый чат. Обработчик не должен бесконтрольно отвечать на каждое сообщение в активной группе.

Пересылка по правилам

from znakora_userbot import ForwardRule

# Авторизация должна включать источник и назначение.
# Права: events.read, messages.send, messages.forward.
bot.on("message")(ForwardRule(bot, [SOURCE_CHAT_ID], TARGET_CHAT_ID,
                              contains="заказ"))
bot.run()

Для медиа добавьте files.read. Сервер сам загружает исходное сообщение: подменить его текст, автора или вложение параметрами пересылки нельзя. Скрытые, удалённые, одноразовые и самоуничтожающиеся сообщения исключены.

Методы reply и ForwardRule передают trigger_id. Сервер отклоняет автоматический ответ на автоматизацию и использует стабильный ключ результата на входящее сообщение и чат назначения. Это ограничивает повторы между перезапусками. Несколько разных автоответов на один и тот же триггер в тот же чат вызовут конфликт идемпотентности.

Команды владельца

import getpass
import os
from znakora_userbot import Client, Router

with Client() as bot:
    bot.login(input("Email: "), getpass.getpass("Пароль: "), name="Owner commands",
              scopes=["events.read", "profile.bio.write"], chat_ids=os.environ["ZNAKORA_CHAT_IDS"].split(","))
    router = Router(bot)

    @router.command(".bio")
    def description(message):
        parts = message["text"].split(maxsplit=1)
        if len(parts) == 2:
            bot.update_bio(parts[1])

    @router.command(".stop")
    def stop(message):
        bot.stop()

    bot.run(include_own=True)

Router.command принимает команды только от ID владельца, полученного после авторизации. Для команд из ваших обычных сообщений включите include_own=True. Сообщения самого юзербота всё равно игнорируются по серверной отметке автоматизации.

Команда в обычном чате остаётся обычным сообщением и может быть видна собеседникам. Для секретных действий используйте терминал. Не делайте команды выполнения shell, eval, передачи пароля или изменения защиты из произвольного текста.

Фильтры и рабочие сценарии

from znakora_userbot import Router
router = Router(bot)

@router.when(contains="доставка", chat_ids=[CHAT_ID], kind="text")
def delivery(message):
    bot.reply(message, "Напишите номер заказа — проверю статус.")

@router.when(sender_ids=[TRUSTED_USER_ID])
def trusted(message):
    # Здесь может быть ваша локальная логика.
    pass

Можно сочетать фильтры по чату, отправителю, типу и подстроке. Пользовательские функции выполняются на вашем компьютере. Дополнительные фильтры, языки, CRM и классификация ИИ пишутся поверх этих обработчиков. В этой версии нет встроенного облачного ИИ-автоответчика и готового коннектора к каждой CRM.

Если передаёте текст внешнему ИИ, явно определите разрешённые чаты и объём отправляемых данных. Ответ ИИ должен оставаться текстом, а не инструкцией к произвольному вызову API, shell или изменению прав.

Локальные заметки, теги и заявки

from znakora_userbot import StateStore
state = StateStore("work.sqlite3")
state.set("tickets", "order-42", {"status": "new", "priority": "high"})
state.set("tags", MESSAGE_ID, ["заказ", "нужен ответ"])
print(state.get("tickets", "order-42"))
print(state.list("tickets", limit=100))
state.delete("tags", MESSAGE_ID)
state.close()

StateStore — локальная база на вашем компьютере или VPS. Данные не появляются новыми полями в интерфейсе Знакоры. Это основа для собственных заявок, заметок, меток, счётчиков и запоминания уже обработанных задач. Готовая многопользовательская CRM в состав API не входит.

Ограничьте доступ к файлу базы, настройте резервное копирование и срок хранения. Не помещайте туда пароль, код запуска, токен или закрытый ключ. Тексты переписки сохраняйте только когда это нужно вашему сценарию.

Разовые и повторяющиеся задания

from datetime import datetime, timedelta, timezone
job = bot.schedule_message(CHAT_ID, "Напоминание", datetime.now(timezone.utc) + timedelta(minutes=5))
print(job["job_id"])
print(bot.call("list_scheduled"))
bot.call("cancel_scheduled", job_id=job["job_id"])

Серверная отправка: ISO 8601 со смещением часового пояса, минимум через 5 секунд и строго до окончания допуска. Время отправки может отличаться от запланированного примерно на 10 секунд. Перед отправкой заново проверяются допуск, права, блокировки и квоты.

Не более 100 постановок заданий на допуск и не более 100 ожидающих заданий. Отмена не возвращает общий бюджет постановок. Задания относятся к создавшему их допуску: новый допуск их не наследует, а перезапуск с тем же сохранённым сеансом продолжает видеть свои задания. Отозванный допуск не позволяет исполнить ожидающие задания. При штатном отзыве через SDK/API и фоновой уборке они помечаются отменёнными. При отзыве через существующий список сеансов задание может получить failed / SESSION_REVOKED на проверке перед отправкой; сообщение не отправляется.

Статусы: pending, running, sent, cancelled, failed, uncertain. Последний означает, что результат выполнения не удалось однозначно подтвердить; сервер не делает слепую повторную отправку. Перед ручным повтором проверьте историю и используйте прежний request_id.

from znakora_userbot import Scheduler
schedule = Scheduler()
schedule.every("description", 3600, lambda: bot.update_bio("Сегодня на связи"))
bot.run(scheduler=schedule)

Scheduler выполняет повторяющиеся задачи внутри работающего Python-процесса. Интервал от 60 секунд, до 100 задач. Локальные задачи теряются при перезапуске; их можно восстановить из собственной конфигурации. Для работы вне цикла событий вызывайте heartbeat() самостоятельно.

Файлы, изображения и медиа

Передача загружаемого файла должна завершиться за 30 секунд. При обрыве или превышении времени проверьте результат и повторите загрузку после паузы. Бинарное тело передаётся без HTTP-сжатия.

uploaded = bot.upload_file(CHAT_ID, "report.pdf")
bot.call("send_media", chat_id=CHAT_ID, file_id=uploaded["file_id"],
         kind="file", text="Отчёт", request_id="report-2026-11-02")

# Или загрузить и отправить одним удобным вызовом:
bot.send_file(CHAT_ID, "photo.jpg", kind="image", text="Фото")

# destination задаёт владелец; имя из чужого сообщения не используется как путь.
bot.download_file(message, "downloads/report-copy.pdf")

Загрузка: files.write, не более 8 МиБ на файл и 5 загрузок в час на аккаунт. Отправка дополнительно требует messages.send. Варианты kind: file, image, video, voice, video_note, sticker. Формат файла должен соответствовать выбранному типу; SDK передаёт файл на сервер; сервер может оптимизировать поддерживаемое медиа и вернуть новое имя/размер.

При загрузке сохраняются ограничения вашего тарифа. API возвращает file_id, для дальнейшей отправки. Дескриптор привязан к текущему допуску. Поле name отражает имя сохранённого файла, size — его фактический размер после оптимизации, original_size — размер принятого оригинала. Размеры SDK намеренно строже возможного обычного лимита тарифа.

Скачивание: files.read, доступный исходный чат и сообщение, до 16 МиБ на файл, до 10 скачиваний в минуту. Ответ — application/octet-stream, принудительное вложение, без хранения в общем кэше. Одноразовые и исчезающие вложения не выдаются. После отзыва новые запросы отклоняются; уже переданные байты невозможно отозвать с чужого компьютера.

Загруженный документ не считается безопасным исполняемым кодом. Сервер не запускает вложения. SDK не открывает полученные файлы автоматически и не перезаписывает существующий файл назначения.

Сжатие фото, видео и аудио

Новые загрузки через API по умолчанию оптимизируются на сервере. SDK сам не уменьшает качество и не меняет локальный исходник. Сервер сохраняет меньший результат только после проверки. Размеры изображения и видео не уменьшаются. Результат никогда не должен быть крупнее исходника; если выигрыш отсутствует, формат не поддержан или проверка не прошла, сохраняется оригинал.

Что загруженоЧто может сохранить сервер
Обычный PNG / статический lossless WebPМеньший WebP без потерь: проверяется совпадение декодированных пикселей, прозрачности и размеров.
JPEG-фотографияМеньший WebP высокого качества. Это сжатие с небольшими потерями, а не математически идентичный оригинал. Сервер проверяет отличия и сохраняет исходник, если они превышают допустимый порог.
Поддерживаемый MP4 / H.264MP4 высокого качества без уменьшения разрешения и числа кадров. Проверяется сходство кадров. Аудиодорожка копируется без повторного сжатия.
Поддерживаемый WAV / PCMFLAC без потерь: декодированные аудиосэмплы должны совпадать.
Документы, архивы, уже сжатое аудио, анимация, HDR и неподдерживаемые вариантыОригинальный файл. Сервер не должен разрушать документ, кадры анимации или цветовой диапазон ради меньшего размера.

Уменьшение JPEG и видео не обещает абсолютного отсутствия потерь. Если нужны неизменные байты файла — отключите оптимизацию. Для PNG/графики и поддерживаемого WAV применяется проверяемый режим без потерь. Процент уменьшения зависит от исходника: уже хорошо сжатое медиа часто остаётся без изменений.

# Обычная отправка: сервер выбирает допустимую оптимизацию.
bot.send_file(CHAT_ID, "photo.jpg", kind="image")

# SDK 0.1.3: отправить исходные байты без оптимизации.
bot.send_file(CHAT_ID, "original.jpg", kind="file", optimize=False)

# Посмотреть результат оптимизации перед отправкой:
file = bot.upload_file(CHAT_ID, "picture.png")
print(file["name"], file["original_size"], file["size"])
print(file["compression"]["optimized"], file["compression"]["lossless"])
bot.call("send_media", chat_id=CHAT_ID, file_id=file["file_id"],
         kind="image", text="", request_id="picture-1")

В ответе загрузки есть compression: optimized, lossless, original_size, stored_size, saved_bytes, mode. При optimized:false остаётся исходный файл, reason объясняет пропуск. lossless:true означает отсутствие дополнительных потерь при этом действии, включая сохранение оригинала. Не угадывайте расширение по исходному имени: JPEG/PNG может стать .webp, WAV — .flac; используйте возвращённое name.

Для HTTP-клиента параметр optimize=false добавляется к подписанному пути /files/upload?chat_id=…&filename=…&optimize=false. По умолчанию оптимизация включена. Подпись и SHA-256 проверяют именно переданные исходные байты; преобразование выполняется после успешной проверки. Загрузка/права/одноразовый ключ не обходятся сжатием.

Старые SDK продолжают отправлять файлы как прежде и получают серверную оптимизацию автоматически. Обновление до SDK 0.1.3 нужно для удобного аргумента optimize=False. Ранее сохранённые вложения и пересылаемые сообщения повторно не перекодируются.

Лимит загрузки 8 МиБ действует до сжатия: слишком большой исходник нельзя загрузить в надежде на последующее уменьшение. При MEDIA_BUSY соблюдайте Retry-After. Для части медиа оптимизация может быть недоступна; актуальные возможности показывает capabilities.media_optimization. В этом случае сохраняется оригинал, а не результат с худшим качеством.

Опросы, координаты и списки

bot.call("send_poll", chat_id=GROUP_ID, question="Когда встречаемся?",
         options=["Утром", "Вечером"], anonymous=True, multiple=False,
         request_id="meeting-poll-1")
bot.call("send_location", chat_id=CHAT_ID, latitude=40.18, longitude=44.51,
         address="Место встречи", request_id="meeting-point-1")
bot.call("send_checklist", chat_id=PRIVATE_CHAT_ID, title="Покупки",
         items=["Хлеб", "Молоко"], request_id="shopping-1")

Опрос: группа/канал, вопрос до 255 символов, 2–5 вариантов до 80 символов. Список: личный чат, заголовок до 80 символов, 1–10 пунктов до 20 символов. Координаты: конечные числа, широта −90…90, долгота −180…180. Действующие серверные правила могут дополнительно отклонить содержимое.

Модерация, реакции и закрепления

bot.call("set_reaction", chat_id=CHAT_ID, message_id=MESSAGE_ID, emoji="👍")
bot.call("remove_reaction", chat_id=CHAT_ID, message_id=MESSAGE_ID, emoji="👍")
bot.call("pin_message", chat_id=GROUP_ID, message_id=MESSAGE_ID)
bot.call("unpin_message", chat_id=GROUP_ID, message_id=MESSAGE_ID, mode="everyone")
bot.call("mark_read", chat_id=CHAT_ID)
requests = bot.call("list_join_requests", chat_id=GROUP_ID)
bot.call("decide_join_request", chat_id=GROUP_ID, user_id=APPLICANT_ID, approve=True)

set_reaction устанавливает реакцию; повтор того же вызова не снимает её. remove_reaction снимает указанную собственную реакцию. Emoji — до 10 символов в серверном представлении. Для unpin_message режим me по умолчанию скрывает закреп только у владельца; everyone открепляет для всех при наличии действующих прав.

messages.edit и messages.delete позволяют менять/удалять только свои доступные сообщения. Для чужого сообщения есть отдельная операция moderate_message с moderation.write; действующая роль администратора и его право удаления проверяются сервером.

Модератор не может повысить собственную роль, изменить пароль владельца, массово очистить историю или добавить произвольного пользователя под видом одобрения заявки. Одобрять можно только существующую заявку. Удаление ограничено по частоте; встроенного метода массового удаления в версии 0.1.3 нет.

Для антиспама сначала используйте режим наблюдения: записывайте ID предполагаемых нарушений в локальную базу без удаления. Затем включайте точечные действия для проверенных условий.

История, поиск и приватность

page = bot.get_history(CHAT_ID, limit=30)
older = bot.get_history(CHAT_ID, before=page["next_before"], limit=30) if page["next_before"] else None
found = bot.call("search_messages", chat_id=CHAT_ID, query="номер заказа", limit=20)
one = bot.call("get_message", chat_id=CHAT_ID, message_id=MESSAGE_ID)

Требуется history.read. Максимум 50 сообщений на страницу. Поиск буквальный, без исполнения присланного регулярного выражения; строка до 100 символов, до 10 поисков в минуту, применяются лимиты API. Чужой ID не отменяет проверку чата и владельца.

Без history.read новые события, ответы, скачивание и пересылка ограничены сообщениями, созданными после выдачи текущего допуска. История, очищенная владельцем, скрытые сообщения и служебные диалоги не открываются через альтернативные методы.

Ответ содержит только поля, описанные в разделе «Форматы ответов». К цитируемому сообщению возвращается только ID, а не вложенное содержимое из другого контекста.

События, курсоры и восстановление

@bot.on("message")
def incoming(message):
    print(message["id"], message["type"])

@bot.on("message_edited")
def changed(message):
    pass

@bot.on("message_deleted")
def removed(message):
    # unavailable=True; секретное старое содержимое не возвращается.
    pass

@bot.on("error")
def failed(error):
    print(error.code)  # без пароля, токена и содержимого сообщения

bot.run()

Обновления можно получать через SDK или метод /updates. Передавайте полученный cursor без изменений и используйте его только для того же чата и подключения.

Одна страница — до 50 изменений. Для следующей страницы передавайте cursor из предыдущего ответа. Граница страницы может повторяться; SDK отсеивает повторные версии в памяти.

Это синхронизация текущего состояния, а не неизменяемый журнал всех промежуточных действий. Несколько быстрых изменений могут объединиться. Новое сообщение, удалённое до первого опроса, может прийти как недоступное. Уже известное изменение реакций также поступает как обновление сообщения. Изменения старой истории до выдачи допуска в этот поток не входят. Для сверки используйте разрешённую историю.

SDK по умолчанию игнорирует собственные исходящие сообщения и серверную отметку автоматизации. Курсоры и ключ хранятся в памяти. После нового запуска требуется новый код и новый поток. Однократность внешнего платежа, письма или другой сторонней операции библиотека гарантировать не может: храните собственный устойчивый ключ события.

Ошибка в пользовательском обработчике останавливает цикл. SDK не перезапускает такой обработчик автоматически: запись во внешнюю систему могла уже завершиться. Исправьте ошибку и проверьте результат перед новым запуском.

Для одного чата задержка опроса обычно от 2 секунд плюс защитное отставание; при большем числе чатов SDK увеличивает интервал, соблюдая общий бюджет. Ошибка связи не вызывает бесконечную повторную отправку пользовательских действий.

HTTP API: маршруты

Базовый URL: https://znakora.ru/api/userbots/v1. JSON в UTF-8 с Content-Type: application/json. Данные HTML-форм и HTTP-сжатие тела не принимаются. Исключение формата — бинарная загрузка с application/octet-stream. Все рабочие запросы подписываются; обычная cookie браузера не заменяет подпись и токен юзербота.

Метод и маршрутНазначение
GET /capabilitiesВерсия, доступность, реестр операций, разрешения, лимиты; без авторизации
POST /auth/startПароль, ключ и запрашиваемые права; подпись временным ключом без токена
POST /auth/statusСостояние подтверждения устройства; тот же ключ и challenge_id
POST /auth/verifyКод запуска и включённые дополнительные факторы; тот же ключ
POST /auth/cancelОтмена собственного незавершённого входа; challenge_id и тот же ключ
POST /rpcВызов операции с токеном и подписью
POST /updatesОбновления одного разрешённого чата с курсором
POST /heartbeatПоддержание активности без расширения общего срока
DELETE /sessionОтозвать собственный допуск
POST /files/upload?chat_id=…&filename=…Подписанные бинарные данные; специальные ограничения размера
POST /files/downloadПроверенное вложение выбранного сообщения
{"operation":"send_message","args":{"chat_id":"0123456789abcdef01234567","text":"Привет","request_id":"hello-1"}}

Успех RPC: {"ok":true,"result":…}. Ошибка: {"ok":false,"error":{"code":"SCOPE_REQUIRED","message":"…"}}. При ограничении сервер возвращает HTTP 429, Retry-After и по возможности retry_after в секундах. Не пытайтесь обходить паузу изменением IP, ключа или соединения.

Неизвестные поля отклоняются. Query-параметры разрешены только для бинарной загрузки, где они входят в подпись. В остальных методах передавайте параметры в JSON.

Все операции RPC

Любую операцию можно вызвать через bot.call("имя", **аргументы). Удобные методы SDK не меняют серверные права. Ниже приведён точный реестр текущей версии.

ОперацияРазрешение / параметрыРезультат действия
get_meдействующий допуск
без параметров
Публичные поля своего профиля
list_chatsдействующий допуск
без параметров
Только разрешённые обычные чаты
discover_chatschats.list
before
Получить ID обычных чатов для следующего допуска
get_chatдействующий допуск
chat_id
Информация разрешённого чата
get_historyhistory.read
chat_id, before, limit
История с безопасной пагинацией
search_messageshistory.read
chat_id, query, before, limit
Буквальный поиск текста в разрешённом чате
get_messagehistory.read
chat_id, message_id
Одно доступное сообщение
send_messagemessages.send
chat_id, text, reply_to_id, request_id, trigger_id
Текст или ответ на сообщение
send_mediamessages.send
chat_id, file_id, text, kind, request_id
Отправка загруженного файла, изображения, видео или аудио
send_pollmessages.send
chat_id, question, options, anonymous, multiple, request_id
Опрос в группе или канале
send_locationmessages.send
chat_id, latitude, longitude, address, request_id
Отправка координат
send_checklistmessages.send
chat_id, title, items, request_id
Список в личном чате
forward_messagemessages.forward
chat_id, source_chat_id, message_id, request_id, trigger_id
Пересылка проверенного исходного сообщения
edit_messagemessages.edit
chat_id, message_id, text
Редактирование собственного сообщения
delete_messagemessages.delete
chat_id, message_id
Удаление собственного сообщения
moderate_messagemoderation.write
chat_id, message_id
Удаление сообщения с проверкой прав модератора
set_reactionreactions.write
chat_id, message_id, emoji
Установить реакцию
remove_reactionreactions.write
chat_id, message_id, emoji
Снять реакцию
pin_messagepins.write
chat_id, message_id
Закрепить сообщение при наличии прав
unpin_messagepins.write
chat_id, message_id, mode
Скрыть закреп для себя или открепить для всех при наличии прав
mark_readread.write
chat_id
Отметить чат прочитанным
update_bioprofile.bio.write
text
Описание профиля до 200 символов
update_nameprofile.name.write
text
Имя профиля до 30 символов
list_join_requestsmoderation.write
chat_id
Заявки вступления в управляемый чат
decide_join_requestmoderation.write
chat_id, user_id, approve
Одобрить/отклонить заявку вступления
schedule_messagemessages.send
chat_id, text, due_at, request_id
Отправка текста сервером в пределах жизни допуска
list_scheduledmessages.send
без параметров
Состояние собственных заданий этого допуска
cancel_scheduledmessages.send
job_id
Отменить ожидающее задание
get_usageдействующий допуск
без параметров
Лимиты, срок допуска и доступная статистика действий
get_auditдействующий допуск
без параметров
Последние собственные события без секретов и содержимого переписки

chat_id, source_chat_id, message_id, user_id — 24-символьные ID. request_id — непустая строка до 64 символов. job_id и file_id — дескрипторы, полученные от сервера. before, limit, reply_to_id, trigger_id необязательны там, где перечислены. approve, anonymous, multiple — JSON boolean, не строки.

Для отправки текста обязательны chat_id, text, request_id. Текст до 2000 символов; профильное описание до 200, имя до 30. Для пересылки дополнительно нужны messages.send и доступный источник. Для медиа-отправки дополнительно нужно files.write. get_chat и list_chats показывают только заранее согласованные чаты; discover_chats требует отдельного права списка.

Форматы ответов и локальное состояние

Методы RPC возвращают результат внутри {"ok":true,"result":…}. Поля сообщений намеренно ограничены. Отсутствующий необязательный объект обычно равен null; даты передаются в ISO 8601 / UTC.

{"id":"0123456789abcdef01234567","numeric_id":123456,"name":"Мой профиль","bio":"На связи"}

Чат: id, type, title, handle, participants (только ID, имя и numeric_id), slow_mode. В личном чате title может быть пустым: название собеседника доступно в participants.

{
  "id": "0123456789abcdef01234567",
  "chat_id": "1123456789abcdef01234567",
  "created_at": "2026-11-02T08:00:00.000Z",
  "updated_at": "2026-11-02T08:00:00.000Z",
  "unavailable": false,
  "sender": {"id": "2123456789abcdef01234567", "name": "Собеседник", "numeric_id": 654321},
  "type": "text", "text": "Привет", "file_name": "",
  "reply_to_id": null, "automated": false, "has_file": false,
  "reactions": [], "location": null, "checklist": null, "poll": null
}

Реакция содержит emoji и count без списка пользователей. Координаты: latitude, longitude, address. Список: title и items с text/checked. Опрос: question, options с id/text/count и closed. Сам файл не включается в JSON: используйте проверенный метод скачивания.

При удалении, истечении срока или исчезающем сообщении остаются только ID, chat_id, даты и unavailable:true. Текст, имя файла, отправитель и вложенные данные не выдаются.

get_usage возвращает session_id, scopes, chat_ids, limits, expires_at, last_active_at и pending_jobs. Названия полей объекта limits соответствуют реестру capabilities. get_audit возвращает до 50 событий текущего допуска; событие operation_requested означает попытку, а не доказанный успех записи.

SDK удерживает до 10 000 последних версий сообщений в памяти. В сохраняемом режиме токен, ключ и курсоры завершённых страниц записываются в зашифрованный файл сеанса. Кэш последних версий в памяти после перезапуска создаётся заново. При аварии посреди страницы часть событий может прийти повторно. Для собственных внешних действий используйте StateStore и устойчивый ID входящего события. Пароль и токен в StateStore не помещайте.

Подпись запросов: точный формат

Алгоритм Ed25519. Открытый ключ: SPKI DER, закодированный base64url без padding. Закрытый ключ остаётся в процессе SDK. Подпись — 64 байта, также base64url без padding.

Строка подписи состоит из семи строк, разделённых единственным LF (\n), без завершающего перевода строки:

ZNAKORA-USERBOT-V1
HTTP_METHOD_UPPERCASE
EXACT_PATH_WITH_QUERY_IF_UPLOAD
SHA256_HEX_OF_EXACT_BODY_BYTES
SHA256_HEX_OF_TOKEN_OR_EMPTY_STRING
UNIX_SECONDS
RANDOM_NONCE

Заголовки: X-Userbot-Time (10 цифр, секунды Unix), X-Userbot-Nonce (22–64 символа base64url), X-Userbot-Signature. Для рабочих методов дополнительно Authorization: Bearer ub1_…. Допустимое расхождение времени — 60 секунд. Для каждого HTTP-запроса создавайте новый nonce.

Подписываются точные переданные байты, поэтому после вычисления подписи нельзя изменить пробелы JSON, кодировку или порядок полей. При отправке нового HTTP-запроса создайте новый nonce; для повторной операции сохраните прежний request_id.

Для загрузки бинарного файла имя и chat_id находятся в подписанном URL. Дополнительно передаётся X-Userbot-Body-Sha256. Сервер сначала проверяет подписанный заявленный хеш и допуск, затем ограниченно принимает тело и сравнивает фактический хеш. Content-Length обязателен. Перенаправления SDK запрещает; сертификат HTTPS проверяется.

Если используете готовый SDK, он формирует подписи самостоятельно. Этот раздел нужен при разработке собственного клиента.

Повторы, конфликты и потеря ответа

Сохранение сообщения может завершиться до обрыва ответа. Поэтому для одной логической отправки используйте один и тот же request_id. Новый ID означает новую операцию.

request_id = "order-42-confirmation"
bot.send_message(CHAT_ID, "Заказ принят", request_id=request_id)
# Если ответ потерян, сначала проверьте состояние/историю.
# Повтор той же отправки использует прежние chat_id, text и request_id.

Один request_id должен соответствовать одной логической отправке. Использование того же ID с другим текстом, чатом или вложением отклоняется с IDEMPOTENCY_CONFLICT. Повтор всё равно проходит проверку действующего допуска и расходует бюджет запросов; это не бесплатный обход квот.

Если сервер подтвердил запуск, но ответ с токеном потерян, завершите созданный сеанс через обычный клиент и начните новый запуск. Повторно получить токен завершённого входа нельзя.

Редактирование, удаление, реакции и внешние интеграции не следует автоматически повторять без понимания их результата. SDK намеренно не повторяет произвольные записи после сетевой ошибки.

Лимиты использования

ДействиеЛимит
Код запуска3 минуты; до 5 проверок; один ожидающий запрос на аккаунт
ЗапускДо 8 запросов за 15 минут на аккаунт
Одновременная работаОдин активный юзербот на аккаунт
Сохраняемый вход SDK 0.1.2До 30 дней; до 7 дней без подписанных обращений
Одноразовый режимpersist_session=False: до 12 часов; до 10 минут без подписанных обращений
Выбранные чатыДо 20 чатов на подключение
Запросы APIДо 120 в минуту на аккаунт
Изменяющие действияДо 60 в минуту; для отдельных методов действуют более строгие ограничения
Отправка сообщенийДо 30 в минуту суммарно и до 3 в минуту в один чат
ПрофильОдна смена в минуту; до 20 смен в час
Удаление модераторомДо 10 действий в минуту при наличии прав
История и обновленияДо 50 записей на страницу
ПоискДо 10 запросов в минуту; строка до 100 символов
JSON-запросДо 32 КиБ
ФайлыЗагрузка до 8 МиБ, до 5 файлов в час; скачивание до 16 МиБ, до 10 в минуту
Отложенная отправкаДо 100 постановок заданий за подключение и до 100 ожидающих задач

Также сохраняются ограничения тарифа, антиспам, блокировки и медленный режим чата. При ответе RATE_LIMITED соблюдайте указанную задержку Retry-After. Не запускайте повторные запросы в обход паузы.

Остановка и полный выход

stop() останавливает локальный цикл обработки. В сохраняемом режиме close() и выход из with Client() записывают позицию чтения, освобождают файл и закрывают локальный клиент, оставляя допуск для следующего запуска.

Чтобы полностью отозвать доступ, вызовите logout() у подключённого клиента, нажмите «Остановить юзербота» в системном уведомлении или завершите его подключение в списке сеансов Знакоры. logout() также удаляет сохранённый файл этого клиента. Следующий вход после отзыва снова требует кодов.

При persist_session=False сохраняется прежнее поведение: close() завершает допуск на сервере. Закрытие незавершённого входа отменяет собственный запрос запуска.

Остановка процесса с сохраняемым входом оставляет действительными его серверные отложенные задания в пределах срока и прав допуска. Если их тоже нужно остановить, отмените задания или выполните полный выход. После отзыва дальнейшие запросы и ожидающие задания не допускаются; уже завершённые действия не отменяются.

Безопасность вашего аккаунта

  • Не запускайте неизвестные скрипты и плагины с паролем своего аккаунта. Программа, которой вы сообщили пароль, имеет доступ к нему.
  • Перед подтверждением проверяйте название программы, чаты и разрешения. Название скрипта само по себе не подтверждает его подлинность.
  • Выдавайте только необходимые права. Для автоответчика не нужны права изменения профиля или удаления сообщений.
  • Включите дополнительную защиту аккаунта и следите за списком активных сеансов.
  • Не публикуйте пароль, коды, токен подключения, закрытый ключ, файл .session и файлы окружения.
  • При передаче сообщений внешней программе или ИИ учитывайте, какие данные вы ей разрешаете обрабатывать.
  • Используйте официальный HTTPS-адрес API. Не отключайте проверку сертификата.

Подтверждение кода разрешает конкретный набор действий, но не делает чужую программу безопасной. Защищайте компьютер или VPS, на котором работает ваш скрипт.

При утечке пароля, кода или доступа к обычному сеансу завершите неизвестные подключения и смените пароль. Если вы уже выдали программе доступ, остановите её через список сеансов.

Ошибки и устранение проблем

КодЧто делать
INVALID_CREDENTIALSПроверьте email и пароль; не повторяйте бесконечно
CODE_INVALIDПроверьте свежий код именно запуска юзербота
CHALLENGE_INVALID / CODE_ATTEMPTS_EXHAUSTEDКод истёк, израсходован или уже использован; начните новый запуск после паузы
CHALLENGE_PENDINGЕсть незавершённый вход: SDK 0.1.1 один раз ждёт Retry-After; свой ручной запрос можно отменить через cancel_login
EMAIL_CODE_INVALID / TOTP_INVALIDПовторите соответствующий код с тем же challenge_id; соблюдайте attempts_remaining и срок
DEVICE_APPROVAL_REQUIREDПодтвердите номер в существующем разделе защиты на доверенном устройстве
REQUEST_REJECTEDПрочитайте сообщение: возможен неверный дополнительный фактор или отклонение устройства
PROOF_INVALID / PROOF_REPLAYEDПроверьте часы, точные байты, путь, ключ; для нового запроса создайте новый nonce
JSON_BODY_REQUIRED / BODY_ENCODING_NOT_ALLOWEDИспользуйте application/json без HTTP-сжатия; для файла — application/octet-stream
SESSION_ALREADY_ACTIVEЗавершите предыдущий юзербот через список сеансов
SESSION_EXPIRED / SESSION_REVOKEDДопуск истёк или отозван; при следующем login SDK запросит новые коды
SESSION_FILE_BUSY / SESSION_FILE_UNAVAILABLE / SESSION_FILE_INVALIDПроверьте занятый процесс, приватность каталога и доступность файла; не выдавайте доступ всем
PERSISTENCE_NOT_SUPPORTEDОбновите сервер до 0.1.2 или временно используйте persist_session=False
SCOPE_REQUIRED / CHAT_NOT_ALLOWEDТребуется другой согласованный набор прав или доступ к чату
ACCOUNT_UNAVAILABLE / ACCOUNT_RESTRICTEDПрофиль или его автоматизация ограничены; обычные проверки не обходятся
RATE_LIMITEDСоблюдайте Retry-After; уменьшите частоту и число обработчиков
IDEMPOTENCY_CONFLICTОдин request_id использован для разных операций; сверьте сохранённые параметры
AUTOMATION_LOOP_BLOCKEDАвтоматизация не отвечает на автоматизацию/собственные триггеры
MESSAGE_UNAVAILABLEСообщение скрыто, удалено, недоступно по возрасту/правам или является исчезающим
UPLOAD_SIZE_LIMIT / DOWNLOAD_SIZE_LIMITФайл превышает ограничение API
MEDIA_BUSYСоблюдайте Retry-After: оптимизация медиа временно занята
UPLOAD_BUSY / REQUEST_CLOSEDЗагрузка занята, прервана или превысила время; повторите после паузы
SECURITY_UNAVAILABLE / SERVICE_UNAVAILABLEОбязательная проверка временно недоступна; повторите позже
CONNECTION_FAILEDПроверьте связь. Перед повтором записи учтите возможное сохранение результата
OPERATION_TIMEOUTРезультат записи может быть неоднозначен; проверьте историю и прежний request_id

При сообщении об ошибке передавайте версию SDK, код ошибки и время. Не прикладывайте полный запрос авторизации, пароль, код запуска, cookie, дамп памяти или закрытый ключ.