Боты GasApp
Бот — отдельный аккаунт, который ведёт ваша программа. Ему пишут, он отвечает; его можно назначить администратором канала, и тогда он пишет в канал, одобряет заявки в закрытый канал и удаляет подписчиков. Что именно бот делает, решает его код.
Чем это отличается от ботов Telegram
В Telegram бот ходит на сервер по токену, и сервер действует за него — сервер видит всё, что видит бот. В GasApp сервер содержимого не видит, поэтому и за бота он ничего делать не может: у бота свои ключи, и всё шифрование происходит на его стороне.
Отсюда устройство:
ваш код (любой язык) ⇄ строки JSON ⇄ gasapp-bot ⇄ TLS ⇄ сервер GasApp
gasapp-bot — движок: он хранит ключи бота, шифрует и расшифровывает. Ваш код запускает его как дочерний процесс, пишет команды в его стандартный ввод и читает ответы и события из стандартного вывода.
Что есть: переписка, кнопки под сообщениями, каналы (включая закрытые: заявки, подписчики), команды по HTTP и веб-хуки.
Чего нет:
- платежей — их нет и в приложении;
- групп — групповых чатов в GasApp пока нет вообще, поэтому и боту в них быть негде. Каналы есть.
Как завести бота
- Нужен аккаунт с премиумом (его выдаёт оператор сервера). Ботов у аккаунта — не больше 20 разом, токенов — не больше 20 в сутки. 🔐 Боты живут, пока у владельца есть премиум: когда оператор его снимает, боты аккаунта удаляются.
- В приложении: Каналы → Создать → Мои боты → Создать бота. Имя бота кончается на
bot. Приложение покажет строку видаgasapp-bot:имя_bot:…— это одноразовый токен, он действует час. На машине, где бот будет работать:
cargo build --release -p gasapp-bot # из каталога crates/ gasapp-bot init --server https://ваш.сервер --data ./mybot --token 'gasapp-bot:имя_bot:…'initрегистрирует бота и кладёт его ключи в каталог./mybot.- Запуск:
gasapp-bot run --data ./mybot— обычно его запускает ваш код (см. примеры вcrates/gasapp-bot/bots/).
Удалить бота — там же, в приложении. У одного владельца не больше 20 ботов; удалён владелец — удалены и его боты.
Как людям начать разговор с ботом
Написать боту по имени можно, только когда его ключ простоял в журнале ключей сутки. Раньше — по ссылке-контакту бота: её отдаёт команда getMe (contact_link), автор публикует её там, где людям видно, что это он, а человек вставляет её в приложении в «Новый чат» вместо имени. Ссылка несёт ключ бота, поэтому верить ей стоит ровно настолько, насколько месту, где она опубликована.
Протокол
Одна строка — один объект JSON.
Команда и ответ:
→ {"id": 1, "method": "sendMessage", "params": {"to": "alice", "text": "привет"}}
← {"id": 1, "ok": true, "result": {"message_id": "…"}}
← {"id": 2, "ok": false, "error": "peer_unconfirmed"}
id — любое значение; оно возвращается в ответе. Событие приходит без id, с полем event. Движок обходит сервер раз в несколько секунд (--poll, по умолчанию 5, не чаще 2).
Три способа разговаривать с движком
| Способ | Запуск | Команды | События |
|---|---|---|---|
| Строки JSON | gasapp-bot run --data ./mybot | в стандартный ввод | из стандартного вывода |
| HTTP | … --listen 127.0.0.1:8081 | POST /sendMessage, параметры — JSON в теле | GET /getUpdates |
| HTTP + веб-хук | … --listen 127.0.0.1:8081 --webhook https://ваш.бот/hook | как выше | движок сам шлёт POST на ваш адрес |
Ответ на команду по HTTP — тот же JSON ({"ok": true, "result": …}). getUpdates отдаёт накопившиеся события разом и не ждёт: опрашивайте его раз в секунду-две. Веб-хук получает по одному событию в запросе; ответ 2xx — «принято», иначе движок повторит на следующем обходе.
curl -X POST http://127.0.0.1:8081/sendMessage \
-H 'Content-Type: application/json' \
-d '{"to": "alice", "text": "привет"}'
🔐 Кто шлёт движку команды, тот управляет ботом. Без --secret движок слушает только адрес своей машины. С --secret S каждая команда обязана нести заголовок Authorization: Bearer S, а веб-хук получает X-GasApp-Bot-Secret: S — так ваш код отличает движок от посторонних. Сам интерфейс — открытый HTTP: наружу его выставляют только за своим обратным прокси с TLS.
🔐 Секрет и токен — переменными окружения. Командную строку процесса видят другие пользователи той же машины (ps), поэтому вместо --secret и --token задавайте GASAPP_BOT_SECRET и GASAPP_BOT_TOKEN. Каталог бота движок создаёт доступным только владельцу: в нём база и её ключ.
🔐 Запросы из браузера движок не принимает — даже с верным секретом. Иначе страница, открытая на машине бота, управляла бы им через 127.0.0.1. Отсюда три правила для вашего кода: команда — POST с Content-Type: application/json (либо без тела); в Host — тот адрес, на котором движок слушает; без заголовков Origin и Sec-Fetch-*. Обычные HTTP-библиотеки и curl так и делают.
⚠️ Это не веб-хук Telegram: событие отправляет не сервер GasApp (он ничего не видит), а движок на вашей машине. Он должен работать.
Кнопки
К сообщению бота можно приложить ряды кнопок — до 8 рядов по 4 кнопки:
{"method": "sendMessage", "params": {
"to": "alice", "text": "Одобрить заявку?",
"buttons": [[{"text": "Да", "data": "approve:42"}, {"text": "Нет", "data": "decline:42"}],
[{"text": "Правила", "url": "https://example.org/rules"}]]}}
data(до 64 байт) — при нажатии боту приходит событиеcallbackс этими данными. В переписке нажатие не показывается.url(толькоhttps://) — приложение показывает человеку ссылку целиком и открывает её, если он согласен. Боту ничего не приходит.setButtonsзаменяет кнопки под уже отправленным сообщением; пустой список их убирает.
🔐 Кнопки и нажатия едут внутри сквозного шифрования, сервер их не видит. Движок принимает нажатие, только если бот сам предложил кнопку с такими данными под этим сообщением: прислать «нажатие» с выдуманными данными собеседник не может.
События
event | Поля | Когда |
|---|---|---|
message | from, text, chat_id, message_id, at | Боту написали. from — имя, если оно известно |
callback | from, chat_id, message_id, data | Нажата кнопка с данными под сообщением бота |
post | channel_id, post | В канале, который бот читает, новая запись (не его собственная) |
join_request | channel_id, account_id, username, known_contact, in_key_log | Заявка в закрытый канал, который бот ведёт |
joined | channel | Заявку самого бота одобрили: канал добавлен |
Команды
О себе и переписка
| Метод | Параметры | Что делает |
|---|---|---|
getMe | — | Имя, устройство и ссылка-контакт бота |
sendMessage | to, text, buttons | Сообщение человеку по имени; buttons — необязательные кнопки |
setButtons | to, message_id, buttons | Заменить либо убрать ([]) кнопки под своим сообщением |
addContact | link | Добавить контакт по его ссылке (подтверждает ключ) |
trustContact | username | ⚠️ Поверить серверу, что это ключ этого имени |
acceptNewKey | username | ⚠️ Принять новый ключ собеседника после identity_changed (он переустановил приложение или восстановился из копии — либо ключ подменили); затем trustContact |
contacts | — | Контакты бота |
Каналы: читать
| Метод | Параметры | Что делает |
|---|---|---|
channels | — | Каналы бота: роль, закрытый ли |
joinChannel | link | Войти в открытый канал по ссылке |
requestJoin | link | Подать заявку в закрытый канал |
pendingJoins | — | Заявки бота, ждущие ответа |
syncChannel | channel_id | Забрать новое прямо сейчас |
posts | channel_id, limit | Последние записи |
leaveChannel | channel_id | Уйти из канала |
Каналы: вести (бот — администратор)
| Метод | Параметры | Что делает |
|---|---|---|
adminRequest | channel_id | Заявка на администрирование — её вставляет владелец канала |
publishPost | channel_id, text | Запись в канал |
editPost | channel_id, seq, text | Исправить запись |
deletePost | channel_id, seq | Удалить запись |
pinPost | channel_id, seq | Закрепить (0 — снять). Только владелец канала |
createInvite | channel_id | Новая ссылка-приглашение закрытого канала |
invites | channel_id | Свои ссылки-приглашения |
revokeInvite | channel_id, link | Отозвать ссылку |
joinRequests | channel_id | Заявки, ждущие ответа |
approveJoinRequest | channel_id, account_id | Одобрить: заявитель получает ключ чтения |
declineJoinRequest | channel_id, account_id | Отклонить |
subscribers | channel_id, after | Подписчики, страницами по 100 |
removeSubscriber | channel_id, account_id | Удалить из канала |
banned | channel_id | Удалённые |
restoreSubscriber | channel_id, account_id | Разрешить удалённому снова подать заявку |
Ошибки
network — сервер недоступен, повторите позже · forbidden — нет права · not_found · conflict · bad_request — негодная команда · peer_unconfirmed — ключ собеседника не подтверждён · identity_changed — у собеседника сменился ключ · error — прочее.
Как сделать бота администратором канала
- Бот входит в канал:
joinChannel(открытый) либоrequestJoin(закрытый — и вы одобряете его заявку, как любую другую). - Бот вызывает
adminRequestи отдаёт вам строкуgasapp://channel-admin/…. - Вы в приложении: канал → ⋯ → Администраторы → Добавить и вставляете эту строку.
- После следующего обхода бот видит роль
adminвchannels.
🔐 Что нужно знать автору бота
- Ключи бота лежат в каталоге
--data. База зашифрована, но ключ базы — в файлеbot.keyрядом. Защищают бота права на этот каталог и сама машина. Кто получил каталог — получил бота. - Tor нет. Движок ходит на сервер напрямую по TLS: сервер видит адрес машины бота. Сертификат проверяется по обычным корневым центрам; закрепления сертификата, как в приложении, нет.
- Ответить незнакомому человеку можно, только когда его ключ подтверждён. Это происходит само для имён, которые стоят в журнале ключей дольше суток. Для остальных —
addContactпо ссылке человека либоtrustContact. ⚠️trustContactзначит «верю серверу, что это ключ этого имени»: сервер, который лжёт, прочитает ответ бота. - Одобрить заявку — значит отдать ключ чтения канала, и забрать его нельзя. В событии два признака:
known_contact— заявка подписана ключом подтверждённого контакта бота;in_key_log— ключ заявителя стоит под его именем в журнале ключей дольше суток. Обаfalse— о заявителе известно только со слов сервера. Бот, который одобряет всех подряд, делает закрытый канал открытым для любого, у кого есть ссылка. - Удаление подписчика меняет ключ канала. Движок делает это сам при
removeSubscriberи раздаёт новый ключ оставшимся; если сети не было — доводит при следующем обходе (rekey_pendingвchannels). Записи, вышедшие до удаления, удалённый прочесть может. - Тем, кто пишет боту: сообщения читает программа бота и тот, кто её запустил. Имя на
botзначит только то, что аккаунт заведён как бот.