Skip to main content
Glama

tgx 1.0.0

Telegram из терминала: полноэкранный TUI + скриптовый CLI на Telethon, плюс коннектор MCP, через который тем же самым пользуется агент.

Что менялось — в CHANGELOG.md.

tgx ui              # полноэкранный клиент
tgx ui --demo       # то же самое на демо-данных, без входа в аккаунт
tgx                 # карта команд с анимированной заставкой
tgx dialogs --jsonl # машинный вывод для скриптов (как раньше)

Установка

python3 -m venv .venv && .venv/bin/pip install -r requirements.txt

bin/tgx — обёртка, запускающая bin/tgx.py из .venv. Ключи API берутся из data/config.json или переменных TG_API_ID / TG_API_HASH; сессия лежит в data/tgx.session. Если сессия разлогинена, tgx ui сам покажет экран входа (телефон → код → облачный пароль).

Related MCP server: tgmcp

TUI

Слева — список чатов с непрочитанными, превью и папками Telegram; справа — переписка баблами с разделителями дат, ответами и картинками прямо в бабле; снизу — поле ввода. Новые сообщения прилетают вживую (Telethon events) и появляются с плавным проявлением, чужие чаты дают всплывающее уведомление.

клавиша

действие

/

фильтр по списку чатов

ctrl+k

фокус на список чатов

↑ ↓ / j k

навигация по списку и по сообщениям

клик по сообщению

выделить его (дальше работают v, o, ctrl+r)

enter

открыть чат · отправить сообщение

p

редактор поста: разметка, живое превью, файл, отложенная публикация

l

чек-лист: создать, отмечать пункты, дописывать

t

темы форума: переключение, создание, закрытие, закрепление

shift+p

закрепить или открепить сообщение (тихо)

s

показать/скрыть спойлер в сообщении

ctrl+s

прикрепить файл: фото, видео, кружок, голосовое, документ, альбом

c

комментарии к посту канала — читать и писать

ctrl+f

поиск внутри чата

ctrl+r

ответить на выбранное сообщение

ctrl+y

скопировать текст сообщения

e / x

править · удалить своё сообщение (удаление спросит подтверждение)

f

переслать сообщение в другой чат

+ /

поставить · убрать реакцию

b

нажать инлайн-кнопку бота

v / o

картинка на весь экран · открыть в системном просмотрщике

ctrl+d

скачать вложение целиком в data/downloads

ctrl+e

многострочный редактор

ctrl+u

догрузить историю выше

ctrl+n

следующий непрочитанный чат

ctrl+b

свернуть боковую панель (режим фокуса)

shift+r

отметить чат прочитанным

ctrl+t

следующая тема · ctrl+p — палитра команд

f1 / ?

справка · f12 — SVG-скриншот

ctrl+q

выход

Чаты не помечаются прочитанными автоматически — только по shift+r или с флагом --mark-read.

Флаги: --theme tgx-night|tgx-day|nord|gruvbox|catppuccin-mocha, --demo, --mark-read, --no-notify, --no-splash, --effect <имя>, --limit N.

Список чатов и папки

Грузятся все чаты, а не первая страница: первые 200 показываются сразу, чтобы интерфейс не ждал, остальные догружаются в фоне и вливаются в список (в строке над полем ввода видно «догружаю остальные чаты…»). Ограничить можно флагом --limit N, 0 — без ограничения, это и есть значение по умолчанию.

Папки Telegram применяются целиком, со всеми правилами DialogFilter, а не только со списком явно добавленных чатов:

правило папки

учитывается

явно добавленные и закреплённые чаты

да

явно исключённые чаты

да, исключение сильнее всего

категории: контакты, не-контакты, группы, каналы, боты

да

«исключить прочитанные / без звука / архив»

да

Именно поэтому папка вида «все группы» раньше выглядела почти пустой — учитывался только явный список.

Чек-листы

Telegram умеет чек-листы прямо в сообщении, и tgx тоже. l на пустом месте создаёт новый (заголовок и по пункту на строку), а на сообщении с чек-листом — открывает его: enter отмечает или снимает пункт, поле снизу дописывает новые. В бабле список виден целиком, с / и счётчиком выполненного.

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

tgx todo @chat "План недели" "созвон" "макеты" "релиз"
tgx todo-check @chat 4321 --done 1,2 --undone 3
tgx todo-add @chat 4321 "ретро"

Расшифровка голосовых

tgx transcribe status                    # доступна ли и сколько бесплатных осталось
tgx transcribe get @чат 295429           # голосовое или кружок → текст
tgx transcribe rate @чат 295429 3874… good

В TUI — a на выбранном голосовом; расшифровка появляется в самом пузыре курсивом, а под голосовыми и кружками висит подсказка a — расшифровать.

Главная тонкость документации незаметна снаружи: ответ на messages.transcribeAudio почти всегда приходит с флагом pending и пустым текстом, а готовая расшифровка прилетает позже апдейтом updateTranscribedAudio. Подписка на него ставится до запроса — иначе быстрый ответ проскакивает мимо, и команда молча возвращает пустую строку. Если за --wait секунд текст не пришёл, отдаётся то, что успело, с пометкой pending.

Без Premium Telegram даёт transcribe_audio_trial_weekly_number расшифровок в неделю, каждая не длиннее transcribe_audio_trial_duration_max секунд; в супергруппе с бустом от group_transcribe_level_min они не тратятся. status показывает эти числа для вашего аккаунта, а остаток — free_left в ответе.

Звонки

tgx call start @группа --title "Планёрка"
tgx call participants @группа / info / join-as
tgx call invite @группа @кто / mute @группа @кто --volume 150
tgx call record @группа --video / title @группа "Новое имя"
tgx call settings @группа --join-muted on / say @группа "начинаем"
tgx call stream-url @группа        # адрес и ключ для внешней трансляции
tgx call watch @группа --open      # живая страница участников
tgx call end @группа --confirm-to me --as @бот

Звук идёт по WebRTC, и на чистом Python его не сыграть — зато всё вокруг звука делается обычными вызовами. Поэтому разделение честное: терминал распоряжается звонком, а подключиться со звуком можно в приложении по ссылке.

watch поднимает маленькую страницу на 127.0.0.1 и обновляет её сама: видно, кто говорит, кто заглушён и кто поднял руку. Терминалу такое перерисовывать нечем — таблица мигала бы всем экраном. Страница наружу не смотрит и ничего не сохраняет, а данные берёт у того же процесса, что их опрашивает: второго соединения с Telegram не появляется.

Ссылку на звонок выдают только публичные чаты — у приватного её не существует, и watch это переживает, показывая участников без неё.

Сессии и приватность

tgx security sessions / websites          # устройства и сайты
tgx security close-session HASH --confirm-to me --as @бот
tgx security privacy                      # кто что о вас видит
tgx security set-privacy last-seen contacts --deny @кто
tgx security global-privacy --hide-read on
tgx security session-ttl 7 / account-ttl 548
tgx security notify-exceptions

Сессии и сайты — две разные породы. Обычные входы и входы через «Войти в Telegram» на сайтах сбрасываются разными методами, и сброс одного не трогает другой; отсюда две команды, а не одна.

Приватность Telegram хранит парами правил: «только контактам» — это «разрешить контактам» плюс «запретить всем остальным». Напечатанные подряд, они читаются как противоречие — «контактам, никому», — поэтому сводка выбирает одну основу и дописывает к ней исключения: контактам · кроме 3 выбранных. По этой строке человек решает, что видно посторонним, и ошибка тут стоит дорого.

Основа (everyone/contacts/nobody) при изменении обязательна: правило без неё молча оставило бы прежнюю аудиторию.

Черновики, отложенные, заготовки, избранное

tgx pending drafts                    # все черновики; они видны только вам
tgx pending draft @чат "текст"        # пустой текст стирает черновик
tgx pending scheduled @чат            # что уйдёт само и когда
tgx pending send-now @чат 123 / cancel @чат 123
tgx pending shortcuts / shortcut 1    # быстрые ответы и их содержимое
tgx pending send-shortcut @чат 1
tgx pending saved / tags / name-tag 🔥 "Важное"
tgx pending fact-check @канал 123

Telegram держит порознь четыре разных способа отложить сообщение, и команды повторяют это различие:

  • черновик живёт в чате, виден только вам и не отправится сам никогда;

  • отложенное — уже полноценное сообщение с назначенным временем: оно уйдёт само, и «отменить» значит удалить его до срока;

  • быстрый ответ — заготовка под ярлыком, которую отправляют вручную; именно на неё ссылаются приветствие и автоответ бизнес-режима;

  • избранное — пересланное себе, разложенное по авторам.

Черновик перезаписывается целиком, поэтому «дописать» его нельзя: сперва читаем, потом сохраняем всё вместе. Время принимается и полное (2026-09-01T10:00), и относительное (+30m, +2h, +3d); прошедшее отвергается сразу, а не после отправки.

Стикеры и статистика

tgx stickers show HotCherry                    # что внутри набора
tgx stickers suggest "Мой набор" --as @бот     # подобрать свободное имя
tgx stickers create me "Мой набор" myset a.png=😀 b.png=😎 --as @бот
tgx stickers add myset c.png 🔥 --as @бот
tgx stickers move myset 3 0 / emoji myset 0 🎉 / thumb myset 0 --as @бот

tgx stats channel @канал / group @группа
tgx stats message @канал 123 / story @кто 490
tgx stats forwards @канал 123                  # кто публично переслал
tgx stats graph @канал views_graph             # догрузить один график

Наборы правит бот, владельцем остаётесь вы. Telegram требует указать владельца, а вызывает метод бот — поэтому у команд правки обязателен --as, и это не прихоть, а условие API.

Стикер адресуется документом, а не номером. Номер живёт до первой перестановки, поэтому команды принимают позицию, но переводят её в документ прямо перед вызовом — иначе после move следующая правка попала бы не в тот стикер.

Статистика приходит наполовину токенами. Числа читаются сразу, а графики — это токены, которые надо обменять отдельным вызовом. Поэтому channel печатает список доступных графиков, а graph догружает один по имени: иначе одна команда тянула бы десяток запросов.

Общие папки

tgx share-folder share 24 "Рабочее"      # выписать ссылку на набор чатов
tgx share-folder invites 24 / revoke 24 СЛАГ
tgx share-folder check СЛАГ              # что внутри чужой ссылки — до принятия
tgx share-folder join СЛАГ
tgx share-folder updates 24 / accept 24  # что автор добавил с прошлого раза
tgx share-folder leave 24 --confirm-to me --as @бот

Личные переписки в общую папку не отдаются — Telegram их не делит, поэтому share без списка чатов берёт только делимые, а если таких нет, говорит об этом вместо пустой ссылки. И поделиться можно лишь папкой, созданной как общая: обычная отвечает FILTER_NOT_SUPPORTED.

Принятая папка живёт дальше: автор добавляет чаты, а у вас появляются обновления — их принимают (accept) или прячут (hide-updates).

Истории

tgx stories feed                       # лента; --hidden — скрытые
tgx stories of @кто / pinned / archive
tgx stories publish фото.jpg --caption "…" --audience close --hours 24
tgx stories viewers 490                # кто смотрел и как отреагировал
tgx stories react @кто 332 ❤ / read @кто 332
tgx stories stealth                    # просмотры не засчитываются (Premium)
tgx stories albums / new-album "Лето" 464 465
tgx stories search хештег / link @кто 332 / hide @кто
tgx stories delete 490 --confirm-to me --as @бот

Приватность по умолчанию — близкие друзья, а не «всем». Аудитория задаётся списком правил, которые складываются: --audience contacts --deny @кто означает «контактам, кроме него». Промахнуться в сторону «всем» дороже, чем в сторону «слишком узко», поэтому умолчание выбрано самым узким.

Просмотр чужой истории виден автору. Поэтому чтение ленты и отметка о просмотре — разные команды: feed ничего не отмечает, read отмечает и прямо говорит об этом. Скрытный режим (stealth) существует ровно затем, чтобы просмотр не засчитался; он требует Premium.

Срок жизни — 6, 12, 24 или 48 часов; --pin оставляет историю в профиле после срока.

Контакты и чёрный список

tgx contacts list / search "имя" / by-phone +7…
tgx contacts add @кто --note "познакомились на конференции"
tgx contacts remove @кто
tgx contacts block @кто / unblock @кто
tgx contacts blocked --stories        # кому запрещены истории
tgx contacts close-friends @а @б      # список задаётся целиком
tgx contacts birthdays / top / import ССЫЛКА

Telegram различает три вещи, которые легко спутать, и tgx повторяет это различие вместо того, чтобы прятать:

  • Контакт — запись в адресной книге. remove убирает её, но писать вам человек по-прежнему сможет; ответ команды об этом прямо говорит.

  • Блокировка — запрет писать. У неё есть второй, независимый список: --stories управляет тем, кому не видны ваши истории.

  • Близкие друзья — отдельная отметка, влияющая только на истории; список задаётся целиком, а не по одному человеку.

Заметка к контакту (--note) видна только вам, но хранится у Telegram — это часть аккаунта, а не локальный файл. И свой номер при добавлении по умолчанию не раскрывается: для этого есть отдельный --share-phone.

Человек в контуре

Опасное не запрещено — оно спрашивает. Бот присылает карточку с описанием того, что сейчас произойдёт, и двумя кнопками; действие ждёт нажатия.

tgx confirm "Удалить 40 сообщений?" --details "Чат: Команда" \
    --danger "необратимо" --to me --as @мой_бот --timeout 300

Возвращает решение и завершается с кодом 2, если согласия не было, — сценарий останавливается сам. Тем же ключом закрыты необратимые команды:

tgx delete @чат 10 11 12 --yes --confirm-to me --as @мой_бот
tgx guard check @чат --confirm-to me --as @мой_бот
tgx pay send "https://t.me/$…" --as @мой_бот --confirm-to me

Две вещи, без которых подтверждение было бы декоративным:

  • Нажать может только тот, кого спросили. Кнопки видны всем в чате, поэтому автор нажатия сверяется с адресатом; чужое нажатие отклоняется, ему показывается пояснение, а сам факт попадает в ответ полем strangers.

  • Ключ одноразовый. У каждого запроса свой случайный ключ, живущий до первого решения или до истечения срока; повторное нажатие ничего не меняет.

Опрос идёт через getUpdates, поэтому у бота не должно быть вебхука и второго получателя обновлений — иначе нажатие уйдёт не сюда.

Платежи: звёзды, TON и счета

Покрыто 62 метода payments из 66. Не взяты четыре: покупки через App Store и Play Market (нужен чек от магазина, с настольного клиента их не сделать) и служебный TL.

tgx pay balance                 # баланс звёзд
tgx pay balance --ton           # он же в TON
tgx pay history --outbound      # история операций
tgx pay receipt @чат 123        # чек по оплаченному сообщению
tgx pay show "https://t.me/$…"  # что просит счёт — без оплаты
tgx pay invoice "Название" "Описание" --price "Строка=50" --as @бот
tgx pay gift-options @друг

Три валюты живут в одних и тех же вызовах и различаются флагом: getStarsStatus с ton=True отвечает про криптовалюту, и так же устроена история операций.

Три вещи, на которых легко ошибиться:

  • Звёзды целые. --price "Донат=50" — это пятьдесят звёзд. Обычные валюты Telegram принимает в сотых (12.5 USD → 1250), и если применить это правило к звёздам, счёт выписывается в сто раз больше. Заметно только у плательщика.

  • Счета выписывает бот. От лица пользователя payments.exportInvoice отвечает USER_BOT_REQUIRED, поэтому --as @бот обязателен. Звёздам (валюта XTR) платёжный провайдер не нужен — Telegram сам платёжная система; для остальных валют нужен токен провайдера.

  • Сумма операции лежит в поле amount, а не stars — на stars приходят сплошные нули, и история выглядит пустой.

Что ещё умеет pay:

tgx pay gifts                      # каталог подарков Telegram и цены
tgx pay my-gifts                   # полученные подарки
tgx pay subscriptions              # подписки за звёзды
tgx pay revenue @канал --ton       # сколько заработал канал
tgx pay check-code СЛАГ            # что даёт подарочный код — до применения
tgx pay giveaway @канал 123        # сведения о розыгрыше
tgx pay saved-info                 # что Telegram хранит из платёжных данных

Тратящее и необратимое — только через подтверждение человеком:

tgx pay convert-gift 185532 --confirm-to me --as @бот   # подарок → звёзды
tgx pay transfer-gift 185532 @друг --confirm-to me --as @бот
tgx pay react @канал 42 5 --confirm-to me --as @бот     # платная реакция
tgx pay apply-code СЛАГ --confirm-to me --as @бот
tgx pay cancel-subscription @канал ID --confirm-to me --as @бот
tgx pay clear-saved --confirm-to me --as @бот

Без --confirm-to такие команды отказываются работать — это не флаг вежливости, а условие запуска.

Подарки — от витрины до вторичного рынка:

tgx pay gift 185532                  # подробности своего подарка
tgx pay upgrade-preview ID           # что даст улучшение — до оплаты
tgx pay resale ID                    # кто что продаёт и почём
tgx pay unique СЛАГ                  # уникальный подарок по ссылке
tgx pay collections / new-collection # коллекции на витрине
tgx pay show-gift 185532 --hide      # спрятать из профиля
tgx pay pin-gift 185532              # закрепить наверху
tgx pay topup                        # пакеты звёзд и цены
tgx pay auctions / referrals / can-send-gift ID

С воротами подтверждения: upgrade-gift, sell-gift (выставить цену или снять), refund (вернуть звёзды за покупку).

Цена приходит списком. Одна и та же вещь стоит и звёзд, и TON: 460 ⭐ — это 5.07 TON. При этом у TON нет поля nanos: вся сумма записана в amount в нанотонах, и если поделить её как звёзды, 4.68 TON превращаются в 4 680 000 000.

Аукционы, крафт, партнёрские программы и прочее:

tgx pay auction-state ID / auction-won ID
tgx pay craftable ID                  # из чего собрать подарок
tgx pay suggested-referrals           # какие боты предлагают партнёрство
tgx pay referral @бот                 # условия и заработок по одной программе
tgx pay premium-options               # почём подарить Premium
tgx pay giveaway-options              # варианты розыгрышей звёзд
tgx pay unique-value СЛАГ             # во что оценивается уникальный подарок
tgx pay upgrade-attributes ID         # варианты оформления и их редкость
tgx pay transaction ID…               # операции по идентификаторам
tgx pay ads-account @канал
tgx pay validate-info ССЫЛКА --name … # проверить данные ДО списания

За воротами: craft, offer, answer-offer, connect-referral, revoke-referral, delete-collection, fulfil-subscription, gift-to-blockchain, pay-card.

Оплата картой — без ввода карты. Telegram хранит способ оплаты у себя; tgx pay pay-card берёт временный пароль в обмен на пароль от аккаунта (по SRP) и платит сохранённым способом. Номер карты не проходит ни через аргументы, ни через этот процесс. Первый платёж картой всё равно делается в приложении — там способ и сохраняется.

Секреты — скрытым вводом, не аргументом:

tgx pay withdraw @канал 100 --confirm-to me --as @бот
tgx pay card-bank

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

  • Вывод сперва спрашивает согласие кнопкой, потом пароль. Пароль на сервер не уходит: Telegram проверяет его по SRP, то есть по доказательству знания. Денег команда не переводит — возвращает ссылку на страницу вывода, где подтверждение происходит уже у человека.

  • Банк по карте требует полный номер: на первых шести цифрах сервер отвечает отказом. Справочник банков лежит в другом дата-центре, и запрос повторяется там, куда указал сервер.

Оплата счёта — тоже за воротами подтверждения. tgx pay send сначала перечитывает счёт, показывает сумму человеку карточкой с кнопками и списывает звёзды лишь после «Разрешить». Форма запрашивается заново перед списанием: между показом суммы и согласием проходит время, а платить надо ровно за то, что человек видел. Карты отсюда не оплачиваются — платёжные данные вводят на своём экране.

Эффекты и свёрнутые цитаты

tgx effects --search 🎉        # 697 эффектов: эмодзи и id
tgx send @друг "с праздником" --effect 5046509860389126442

Из Bot API 7.4. Эффект работает только в личной переписке — в группе и канале сервер отвечает EFFECT_CHAT_INVALID, поэтому tgx отказывает заранее и объясняет почему.

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

Копии и бусты

tgx copy @откуда @куда 123 124 --drop-captions --topic 2
tgx boosts status @канал     # уровень и сколько до следующего
tgx boosts who @канал        # кто бустил
tgx boosts mine              # свои слоты
tgx boosts give @канал

copy — это copyMessage из Bot API: тот же метод, что и пересылка, но с drop_author, поэтому получатель не видит источник и сообщение не тянет за собой ссылку на него.

Обложка и точка старта видео

tgx send @чат "подпись" --file ролик.mp4 --cover обложка.png --start-at 12

Из Bot API 8.3. Telethon не пробрасывает video_cover и video_timestamp через send_file, поэтому медиа собирается вручную, а обложка сперва загружается как фотография.

У ролика без звуковой дорожки этого не будет. Telegram добавляет такому файлу DocumentAttributeAnimated, обращается с ним как с гифкой, и оба поля молча пропадают — видно только на живом сообщении.

Дата и время в тексте

Сущность date_time (Bot API 9.5, в слое MTProto — MessageEntityFormattedDate) хранит момент времени, а не написанную кем-то строку: каждый клиент показывает его по-своему. tgx подставляет момент по флагам автора — день недели, короткая или длинная дата, время с секундами или без.

Опросы и викторины

tgx poll create @чат "Вопрос?" "А" "Б" --multiple --members-only --countries RU,DE
tgx poll create @чат "Вопрос?" "А" "Б" --quiz 0 --as @бот --explanation "почему А"
tgx poll vote @чат 36 0            # без вариантов — снять голос
tgx poll results @чат 36
tgx poll voters @чат 36 --option 0 # если опрос не анонимный
tgx poll close @чат 36

Из Bot API 10.0: одного варианта теперь достаточно (раньше требовалось два), --members-only ограничивает опрос подписчиками, --countries — списком стран. Из 9.6: правильных ответов в викторине может быть несколько (--quiz 0 2), появилось --allow-revoting, а срок автозакрытия вырос с суток до месяца.

Викторина требует --as @бот. В схеме inputMediaPoll.correct_answers — вектор байтовых строк, а Telethon 1.44 объявляет и пакует его как вектор чисел, из-за чего сервер отвергает любое значение. Через Bot API правильный ответ задаётся обычным номером, поэтому викторина уходит оттуда. Обычные опросы этим не задеты и идут по MTProto от вашего имени.

Кнопки, вложения и богатые сообщения

tgx rich @чат --file пост.md --topic 2 --media banner=баннер.png     # от своего имени
tgx bot rich @чат --as @мой_бот --file пост.md --topic 2 \
    --media banner=баннер.png --button "Сайт[primary]=https://…"      # от бота, с кнопками

Три вещи, которые стоят за этими флагами:

  • Кнопки вешает только бот. Обычному аккаунту сервер reply_markup принимает без ошибки и молча выбрасывает — ни у простого сообщения, ни у богатого кнопок не появится. Проверять надо чтением сообщения обратно, а не отсутствием исключения.

  • Стили кнопок появились в слое 227: Текст[primary]=…, [danger], [success], а [success:5312…] ставит эмодзи вместо галочки.

  • Блочная форма (10.2–10.3) — единственный способ положить кнопки и файлы внутрь документа: tgx bot rich --blocks файл.json --attach имя=путь. Сборщики блоков лежат в tgx_rich: heading, quote(expandable=True), table(compact=True), button_row, document_block. Отправка работает, а вот показать такое сообщение tgx пока не может: конструкторы блочной формы новее слоя MTProto, который знает Telethon, и оно приходит как «сообщение нового типа». Получатель с обновлённым клиентом видит документ полностью.

  • Картинка внутрь документа. Bot API берёт медиа либо по публичному URL, либо частью формы, поэтому файл с диска уходит как attach:// вместе с multipart — это единственный способ, когда репозиторий приватный и публичной ссылки нет. Если ссылки tg://photo?id=… в тексте нет, tgx ставит картинку в начало сам: иначе Telegram молча выбросит вложение.

Именные приглашения

tgx guard lock @чат                        # звать людей могут только администраторы
tgx guard invite @чат @кого --note "зачем" # ссылка на одного, одно использование
tgx guard check @чат                       # вошёл не тот — удаляем, ссылку отзываем
tgx guard journal

Ссылку легко переслать, поэтому сверка идёт по факту входа, а не по обещанию: журнал (права 600) помнит, кому она выписана, а check сравнивает это с тем, кто реально вошёл. Лишний удаляется баном с мгновенным разбаном — без вечного бана. Решение «выгнать» вынесено в отдельную функцию и покрыто тестами, чтобы не проверяться на живых людях.

Форумы и темы

Тема — это не папка, а тред служебного сообщения, которым её создали: id темы равен id этого сообщения. Отсюда все особенности, и tgx закрывает core.telegram.org/api/forum целиком:

tgx forum on @группа --tabs on      # включить форум (только владелец)
tgx forum topics @группа            # список; --search ищет по названию
tgx forum icons                     # иконки, доступные без Premium
tgx forum create @группа "Релизы" --color зелёный
tgx forum create @группа "Идеи" --emoji 5312536423851630001
tgx forum edit @группа 4 --title "Архив" --close
tgx forum pin @группа 2
tgx forum reorder @группа 3 2 --force
tgx forum show @группа 4            # так же узнают, что тему удалили
tgx forum delete @группа 4 --yes    # необратимо: уносит всю переписку
tgx forum as-messages @группа on    # показывать форум сплошной лентой

Правила Telegram зашиты в команды, а не оставлены на память:

  • «Общую» тему (id 1) нельзя удалить — только скрыть; и скрыть можно единственно её, остальные закрывают.

  • Цвет стандартной иконки выбирается один раз при создании — у editForumTopic такого поля просто нет.

  • Переименование и закрытие — разные запросы. На попытку сделать это одним сервер отвечает TOPIC_CLOSE_SEPARATELY; tgx forum edit разбивает вызов сам.

  • Иконка-эмодзи без Premium — только из стандартного набора, его печатает tgx forum icons.

  • Закрепить можно topics_pinned_limit тем (у аккаунта — 5); reorder проверяет это до отправки.

  • Об удалении темы отдельного апдейта нет — пропадает корневое сообщение, а tgx forum show возвращает по этому id {"deleted": true}.

В TUI на экране тем добавлено d — удаление с подтверждением вторым нажатием.

Оформление профиля

Аватар в Telegram — уже не «одна картинка»: тем же полем ставятся четыре разные вещи, и одинаково для себя, канала, бота и контакта.

tgx profile formats                      # шпаргалка по форматам
tgx profile photo фото.png               # статичный аватар
tgx profile photo клип.mp4 --start 2.5   # видеоаватар; --start выбирает обложку
tgx profile photo emoji:5366316836101038579 --colors "#229ED9,#17212B"
tgx profile photo sticker:набор:42       # стикер на градиенте

tgx profile photo лого.png --chat @канал         # аватар канала или группы
tgx profile photo лого.png --bot @my_bot         # аватар своего бота
tgx profile photo лого.png --contact @друг       # фотография контакта у вас
tgx profile photo лого.png --contact @друг --suggest   # предложить её ему
tgx profile photo лого.png --fallback            # публичный запасной аватар

tgx profile photos                       # что у вас стоит: фото, видео, эмодзи
tgx profile emojis --kind status         # эмодзи с их id, чтобы было что выбрать
tgx profile color --color 5 --emoji 5379748062124056162
tgx profile status --emoji 5413694143601842851 --until 1780000000
tgx profile birthday 14.03.1990
tgx profile personal-channel @мой_канал

Не квадратное или слишком длинное видео tgx замечает до отправки и предлагает --square и --trim — они обрезают через ffmpeg, а не оставляют это Telegram.

Заставка в аватар

Анимированный баннер tgx можно записать прямо из терминала и поставить аватаром. Кадры снимаются с настоящего псевдотерминала и разбираются эмулятором, так что в видео попадает ровно то, что видно в окне, — вместе с блочными глифами и градиентом:

tgx profile banner --bot @my_bot                 # записать и поставить
tgx profile banner --chat @канал --effect decrypt
tgx profile banner --save баннер.mp4 --effect matrix --speed 40

Обложка выставляется сама на тот момент, когда логотип уже собран, — иначе в профиле висел бы пустой первый кадр. Цель указывается явно (--bot, --chat, --me, --save): собственный аватар слишком легко сменить по ошибке.

Бот-секретарь (Telegram Business)

Бот, подключённый к личному аккаунту, читает и отвечает в ваших приватных чатах. Настраивается это со стороны аккаунта, поэтому живёт в tgx, а не в боте:

tgx bot secretary @my_bot on           # без этого Telegram ответит BOT_BUSINESS_MISSING
tgx business bots                      # кто уже подключён и с какими правами
tgx business connect @my_bot --rights reply,read_messages --chats all --exclude @boss
tgx business pause @chat               # приостановить бота в одном чате
tgx business disconnect @my_bot

tgx business quick-replies             # быстрые ответы и их id
tgx business greeting --shortcut 1 --after-days 7
tgx business away --shortcut 3 --schedule outside
tgx business hours "пн-пт 9:00-18:00; сб 10:00-14:00" --timezone Europe/Moscow
tgx business intro --title "Привет" --text "Отвечаю в течение дня"
tgx business link "Здравствуйте! Чем помочь?" --title "Поддержка"

Права выдаются поштучно: reply, read_messages, delete_sent_messages, delete_received_messages, edit_name, edit_bio, edit_profile_photo, edit_username, view_gifts, sell_gifts, change_gift_settings, transfer_and_upgrade_gifts, transfer_stars, manage_stories — или all/none. Область: all, contacts, non-contacts, existing, new, плюс --exclude.

Два подводных камня, оба проверены на живом аккаунте, ни один не описан в документации:

  • Бот должен сам разрешить это. В BotFather переключатель называется «Secretary Mode» (в API — бизнес-режим); пока он выключен, подключение падает с BOT_BUSINESS_MISSING. tgx bot secretary щёлкает его за вас.

  • Подключённый бот всегда один. account.getConnectedBots возвращает вектор, и ограничения в документации нет — но второй бот молча отключает первого. Поэтому connect отказывается затирать чужое подключение без --replace и называет того, кого собирается вытеснить.

Со стороны бота это business_connection_id в Bot API: он получает апдейты business_message и отвечает от вашего имени. Нужен Telegram Business (Premium).

Ничего из этого не отдано агентам — подключение бота к личной переписке решает человек, не модель.

Богатые сообщения (Bot API 10.1)

Отдельный вид сообщения из Bot API 10.1 (11 июня 2026), дополненный в 10.2: не текст с разметкой, а документ — заголовки шести уровней, таблицы, списки задач, сноски, раскрывающиеся блоки, формулы, галереи, блок «размышления».

Отправлять их можно двумя путями, и tgx умеет оба:

  • от своего аккаунта — по MTProto (messages.sendMessage(rich_message=…), слой 227, Telethon 1.44). Ограничение «только боты» относится к Bot API, а не к протоколу;

  • от имени бота — по HTTP Bot API, с кнопками под сообщением и потоковым черновиком sendRichMessageDraft.

Входящие богатые сообщения клиент отрисовывает в терминале: заголовки, списки, таблицы, цитаты, код, спойлеры и сноски. Внутри это дерево блоков Instant View (PageBlock*), поэтому рендер общий с обычной разметкой.

tgx bot rich-syntax                      # шпаргалка по разметке
tgx bot rich @channel --as @my_bot --file post.md \
  --button "Открыть=webapp:https://app.example.com"
tgx bot rich @channel --as @my_bot --file post.md --draft   # потоковый черновик
tgx bot rich @channel --as @my_bot --file post.md \
  --media cover=https://example.com/cover.jpg   # для ![](tg://photo?id=cover)

Разметка (markdown-поле InputRichMessage):

# Заголовок 1 … ###### Заголовок 6      **жирный** *курсив* ~~зачёркнутый~~
==выделенный==   ||спойлер||   `код`    ```python … ```   $x^2 + y^2$
- пункт   1. нумерованный   - [ ] задача   - [x] сделано
> цитата
| Заголовок | Ещё |    ![](https://…/photo.jpg "подпись")    Сноска[^1]
|:----------|:---:|    ![](tg://photo?id=cover)             [^1]: текст

Лимиты Telegram проверяются локально, до запроса, и объясняются по-человечески: 32768 символов, 500 блоков, 16 уровней вложенности, 50 вложений, 20 колонок в таблице. Ссылки вида tg://photo?id=… должны быть перечислены в --media, иначе tgx откажет заранее.

В TUI это переключатель «богатое» в редакторе поста (p) рядом с выбором бота: без бота редактор предупредит, потому что личный аккаунт такие сообщения слать не умеет. Агентам добавились rich_syntax (чтение) и bot_send_rich (запись).

Не путать с соседним разделом: article публикует страницу на telegra.ph и кидает ссылку, а bot rich отправляет само сообщение внутри Telegram.

Статьи на telegra.ph

Длинный текст в Telegram лучше живёт статьёй: ссылка на telegra.ph открывается через Instant View прямо в приложении. tgx делает статью из маркдауна:

tgx article account --name tgx --author "Alex"        # один раз: аккаунт и токен
tgx article new "Заголовок" --file post.md --publish @mychannel
tgx article new "Заголовок" --file post.md --publish @mychannel --as @my_bot
tgx article edit Zagolovok-08-28 "Новый заголовок" --file post.md
tgx article list

Поддерживается ровно то, что Telegraph умеет: # → h3, ## → h4, абзацы, списки - и 1., цитаты >, блоки кода, разделители ---, картинки, ссылки, **жирный**, *курсив*, `код`, ~~зачёркнутый~~. Токен telegra.ph даёт право править все ваши статьи, поэтому лежит в data/telegraph.json с правами 600 и показывается маской.

Поиск с фильтрами

ctrl+f открывает поиск с полями: где искать (этот чат / все чаты), тип сообщений, отправитель и границы дат. Тип можно задать и без текста запроса — например «все ссылки за последнюю неделю».

тип

что попадёт

photo · video · media

фото, видео, и то и другое

file · link · voice · music · gif · round

документы, ссылки, голосовые, музыка, гифки, кружки

mention · pinned

упоминания вас · закреплённые

geo · contact · poll

геометки, контакты, опросы

Даты понимают 2026-08-01, 01.08.2026 и относительное -7d / -12h.

tgx search "релиз" --peer @channel --since -30d
tgx search --kind link --peer @channel --since 2026-08-01 --until 2026-08-28
tgx search "договор" --kind file --global
tgx search "" --kind mention --global --limit 50
tgx search "спасибо" --peer @chat --sender @alex

Две честные границы, они идут от самого Telegram: у глобального поиска нет поля отправителя, поэтому --sender работает только вместе с --peer; и нижнюю границу дат сервер не принимает, поэтому --since применяется при чтении — результаты идут от новых к старым, и выборка просто останавливается на границе.

Темы форума и закрепления

В группе с включёнными темами над перепиской появляется полоса тем: переключение переносит и чтение, и отправку в выбранный тред. У обычных чатов полоса скрыта.

  • t — список тем: enter открыть, n создать (название берётся из поля внизу), r переименовать, c закрыть/открыть, p закрепить.

  • shift+p — закрепить или открепить выделенное сообщение. Закрепление тихое: участников оно не будит. Закреплённые помечены 📌 прямо в бабле.

tgx topics @forum                       # список тем
tgx topic-create @forum "Релизы"
tgx topic-edit @forum 12 --title "Релизы 2026" --close
tgx topic-pin @forum 12                 # или --unpin
tgx channel-edit @group --forum 1       # включить темы в существующей группе

tgx pin @chat 4321                      # тихо; --notify разбудит участников
tgx pin @chat 4321 --unpin
tgx pinned @chat --limit 20

Агентам доступны list_topics, list_pinned на чтение и create_topic, edit_topic, pin_message на запись — последний по умолчанию тоже тихий, а notify: true в его описании отдельно помечен как то, о чём стоит спросить пользователя.

Боты

API для создания бота у Telegram нет — это разговор с @BotFather. tgx ведёт его за вас и проверяет каждый шаг: если BotFather ответил не то, вы увидите его собственные слова, а не «что-то пошло не так». Интерфейс разговорный и формулировки в нём иногда меняются, поэтому это самая хрупкая часть — и самая подробная по диагностике.

А как же bots.createBot из слоя 227? Он существует, но заменой BotFather не является, и это стоило проверки. Вызов из своего аккаунта отвечает MANAGER_INVALID: управляющим обязан быть бот. Подставляешь своего бота — MANAGER_PERMISSION_MISSING: этому боту BotFather должен был выдать право «управлять ботами». То есть путь работает для тех, кто строит ботов ботами, а не для «завести бота из терминала». Он доступен как --manager, но по умолчанию bot create идёт через BotFather — он единственный отдаёт токен владельцу.

Так же устроены exportBotToken, getBotCommands, getBotMenuButton: это вызовы для бота, из пользовательского аккаунта приходит USER_BOT_REQUIRED. Поэтому bot commands, bot menu-get и bot group-rights принимают имя бота и работают под его сессией. А bot mine и bot previews спрашиваются от вашего имени — и mine теперь мгновенный, без переписки с BotFather.

tgx bot mine                              # ваши боты, одним вызовом
tgx bot commands tgx_ops_bot              # команды глазами пользователя
tgx bot menu-get tgx_ops_bot              # какая сейчас кнопка-меню
tgx bot previews tgx_ops_bot              # картинки до запуска
tgx bot group-rights tgx_ops_bot --pin --delete
tgx bot create "Имя" myhelperbot --manager tgx_ops_bot   # если право выдано
tgx bot create "Мой бот" my_bot           # создать и сохранить токен
tgx bot list                              # токены показаны маской
tgx bot token @my_bot --reveal            # забрать токен у BotFather
tgx bot revoke @my_bot                    # отозвать и получить новый
tgx bot setname @my_bot "Новое имя"       # setabout / setdescription / setcommands
tgx bot info @my_bot                      # как бота видит API
tgx bot mine                              # каких ботов знает BotFather
tgx bot me @my_bot                        # вход по токену: кто это
tgx bot forget @my_bot                    # убрать из локального реестра

Имя, «о боте», описание и список команд ставятся прямо через API (bots.setBotInfo, bots.setBotCommands) — там нечему сломаться. Через диалог с BotFather идёт только то, для чего API не существует: создание бота и получение или отзыв токена. Если всё же нужен старый путь — флаг --via-botfather.

Токены — учётные данные: data/bots.json пишется с правами 600, в выводе маскируется (123456789:AAE…abcd), целиком печатается только по --reveal и агентам не отдаётся никогда.

Посты от имени бота, кнопки и мини-приложения

Инлайн-кнопки под постом может вешать только бот — у личного аккаунта такой возможности в Telegram нет:

tgx bot post @channel "**Релиз 1.0**" --as @my_bot \
  --button "Скачать=https://example.com/dl, Открыть=webapp:https://app.example.com ; Отзыв=cb:feedback"

синтаксис

кнопка

Текст=https://…

ссылка

Текст=webapp:https://…

мини-приложение (Web App)

Текст=cb:данные

callback — отвечает работающий бот

Текст=switch:запрос

поделиться инлайн-запросом

Текст=copy:значение

копирование текста

Текст=user:12345

профиль пользователя

Ряды разделяются ;, кнопки в ряду — запятой; шпаргалка — tgx bot buttons. Мини-приложение вешается и на меню самого бота:

tgx bot menu @my_bot --text "Открыть" --url https://app.example.com
tgx bot menu @my_bot --reset

В TUI то же самое в редакторе поста (p): выпадающий список «от себя / @бот» и поле кнопок. Если выбрать кнопки от личного аккаунта, редактор предупредит и не отправит — Telegram такое не примет.

Агентам доступны list_bots (только имена, без токенов) и bot_post — публикация от имени бота со всеми типами кнопок.

Правка текста руками Telegram

У сервера есть то, чего нет в Bot API: он вычитывает текст, расставляет эмодзи, переводит и переписывает в заданном тоне. Возвращает не только результат, но и разметку правок — что заменено, что убрано, что добавлено. Мы её показываем: принимать чужую правку вслепую — плохая привычка.

tgx ai compose "превет колега завтро релиз"
tgx ai compose "релиз завтра, всё готово" --tone viking
tgx ai compose письмо.txt --translate en --apply @roman

Правки приходят как разметка поверх слитого текста, где куски старой и новой версии лежат рядом. Мы проходим его один раз и собираем одну строку:

[-превет-]{+Превет,+} колега{+. Завтра+} [-завтро -]релиз{+.+}

Глазами это разбирается быстрее списка, поэтому строка идёт первой, а список — под ключом подробно.

Тонов семь встроенных (formal, short, tribal, corp, biblical, viking, zen), и можно завести свой — эмодзи, название и указание, как писать. Свои тоны живут как наборы стикеров: их ставят, ими делятся, их удаляют.

tgx ai tones
tgx ai tone-example viking
tgx ai tone-new "Сухо" "Пиши коротко и без прилагательных" --credit
tgx ai tone-save чужой-тон
tgx ai tone-delete мой-тон --confirm-to @me      # исчезнет у всех, кто поставил

Пересказ и перевод

Помимо правки, сервер умеет пересказывать и переводить. В графическом клиенте это кнопки, о которых половина людей не знает; в терминале это команды.

tgx ai summarize durov 544 --lang ru      # длинный пост в три фразы
tgx ai translate en --text "Завтра релиз"
tgx ai translate ru --peer durov --id 544 # перевести чужое сообщение
tgx ai auto-translate чат on              # полоска «перевести» в чате

Отсюда получается то, ради чего терминал и нужен, — сводка по чату:

tgx ai digest durov --limit 20 --lang ru

Сервер пересказывает по одному сообщению, поэтому сводку tgx собирает сам: длинное отдаёт на пересказ, короткое берёт как есть. Пересказывать «ага» смысла нет, а лишний вызов стоит времени и попадает под ограничение частоты. Если сервер на каком-то сообщении отказался, это видно строкой в ленте — сводка не рушится целиком.

Что ждёт внимания

В графическом Telegram непрочитанное видно боковым зрением: значки, кружки, жирные точки. В терминале бокового зрения нет, поэтому нужны прямые вопросы.

tgx mentions чат              # где вас звали и вы ещё не видели
tgx my-reactions чат          # на что вам отреагировали
tgx triage-clear чат          # пометить и то и другое просмотренным
tgx read-by группа 42         # кто прочитал это сообщение
tgx read-at @roman 42         # когда прочитали ваше
tgx views канал 544 543       # просмотры и пересылки постов
tgx chat-counts чат           # чем набит: фото, видео, ссылки, файлы
tgx online чат                # сколько человек сейчас
tgx mark-unread чат           # вернуть жирную точку, чтобы не забыть
tgx pin-chat чат              # закрепить чат в списке
tgx no-forwards чат on        # запретить пересылку и копирование

read-at работает взаимно: пока вы прячете своё время прочтения, чужое вам не покажут. Мы так и пишем в ошибке — это не поломка, а устройство.

Уведомления, заявки и реакции

Три темы, которые в графическом клиенте лежат по разным углам, а на деле про одно: кто может вас разбудить и кого вы впускаете.

tgx notify show                       # как настроено для личных чатов
tgx notify mute 2h "AllCrew Cockpit"  # заглушить на два часа
tgx notify mute forever чат           # насовсем
tgx notify mute off чат               # снять
tgx notify mute 8h --scope channels   # все каналы разом
tgx notify reactions --from contacts  # реакциями будят только контакты
tgx notify new-contacts off           # молчать о регистрации знакомых

Тишина у Telegram считается не «часами молчания», а отметкой времени, до которой чат молчит. Отсюда две ловушки, обе видны в выводе, если их не обработать:

  • незаглушённый чат приходит нулём, и Telethon превращает ноль в 1 января 1970 года — «тихо до 1970 года» было бы враньём, поэтому и ноль, и любую дату в прошлом мы считаем отсутствием тишины;

  • «навсегда» — это дата на краю тридцатидвухбитного времени, а не отдельный признак; мы её так и называем словом.

Заявки на вступление — оборотная сторона именных приглашений:

tgx notify requests чат                    # кто из админов сколько ссылок наделал
tgx notify approve чат @roman              # принять
tgx notify decline чат @stranger           # отклонить
tgx notify decline-all чат --link https://t.me/+…   # всех по одной ссылке
tgx notify invite-edit чат ССЫЛКА --limit 1 --expires 2h --request-needed
tgx notify invite-purge чат                # выбросить отозванные — они копятся

invite-edit правит выпущенную ссылку, не выпуская новую: это важно, когда ссылка уже кому-то отправлена, а условия поменялись.

Реакции:

tgx notify emoji-list                 # какие вообще есть
tgx notify emoji-top --limit 8        # какие ставят чаще всего
tgx notify emoji-default 🔥           # ваша по умолчанию
tgx notify emoji-allow чат 👍 🔥 ❤    # что можно в этом чате
tgx notify emoji-allow чат all        # любые
tgx notify emoji-allow чат            # запретить все

Стикеры: не только собирать, но и пользоваться

Собрать набор tgx умел давно, а вот найти и отправить стикер — нет.

tgx stickers installed              # какие наборы у вас стоят
tgx stickers mine                   # какие вы сделали сами
tgx stickers find-sets "cat"        # найти набор
tgx stickers find --emoji 🔥        # найти отдельные стикеры
tgx stickers recent                 # недавние
tgx stickers faved                  # избранные
tgx stickers install LadyCat        # поставить набор
tgx stickers send @roman 4909…:-629…

Две вещи, которые выясняются только на практике:

  • Отправить стикер файлом нельзя. Загруженный заново .webp станет обычной картинкой: стикером его делает принадлежность набору, а её несёт только исходный документ. Поэтому find печатает ключ вида id:hash — это то, чем ссылаются на существующий стикер, и без второй половины сервер отвечает, что документа не существует.

  • Поиск по эмодзи возвращает не то, что вы искали. Сервер подбирает стикеры по связям, и собственное эмодзи стикера обычно другое: спросишь 🔥, получишь 😡. Поэтому поле называется «своё эмодзи», а не «эмодзи» — чтобы не выглядело ошибкой.

Часть наборов приходит без короткого имени — у них его попросту нет. Сослаться на такой можно только числами, поэтому set_ref принимает и id:hash.

Видео в богатом сообщении

Через разметку не выйдет: ссылка tg://photo?id= принимает только фотографию, а tg://video?id= не существует — сервер отвечает RICH_MESSAGE_PHOTO_INVALID. Раньше туда молча уезжал mp4 и превращался в стоп-кадр.

Работает блочная форма. Проверено на живом сервере: приходит PageBlockVideo с настоящим mp4 внутри.

[
  {"type": "heading", "size": 1, "text": "Заголовок"},
  {"type": "video", "video": {"type": "video", "media": "attach://banner"}},
  {"type": "paragraph", "text": "Подпись рядом"}
]
tgx bot rich чат --as мойбот --blocks blocks.json --attach banner=баннер.mp4

Ссылка attach:// лежит в поле, названном по типу блока: у документа в document, у видео в video, у фотографии в photo. Пока tgx искал её только в document, у видеоблока имя выходило пустым — и в форму уезжало поле без имени. Сервер на такое отвечает 400 с пустым телом, а пустое тело превращалось в Expecting value: line 1 column 1. Теперь пустой ответ называется пустым ответом, а имя поля берётся по типу блока.

Собирать дерево руками не обязательно: --as-blocks переводит вашу разметку в блоки у себя, а не на сервере, — и тогда ![](tg://video?id=имя) работает.

tgx bot rich чат --as мойбот --file пост.md --as-blocks --attach banner=баннер.mp4

Форма текста внутри блока выяснена опытом, в документации её нет:

что пишете

что получается

"строка"

TextPlain

["часть ", {…}]

TextConcat

{"type": "bold", "text": …}

TextBold; так же italic, code

{"type": "url", "text": …, "url": …}

TextUrl

Узлы вкладываются друг в друга. Привычная по обычным сообщениям пара {"text": …, "entities": […]} здесь не принимается вовсе — сервер отвечает Can't find field "type".

Помните про ограничение блочной формы: она новее слоя MTProto, который знает Telethon, поэтому отправить её можно, а прочесть обратно в tgx — только структуру, не содержимое документа.

Мини-приложение, запущенное из терминала

Подписанный адрес, открытый в обычной вкладке, даёт сломанное приложение. Мини-приложение ждёт вокруг себя хозяина: скрипт внутри страницы шлёт наружу «я готов», «дай тему», «покажи главную кнопку», «закрой меня» — и ждёт ответов. В браузере отвечать некому, поэтому приложение либо висит на заставке, либо считает, что запущено вне Telegram.

tgx inline run wallet          # своё окно вокруг приложения
tgx inline run мойбот --param ref_12345

tgx изображает хозяина: локальная страница держит приложение в рамке, разговаривает с ним по тому же протоколу и рисует его кнопки своими руками. Главную кнопку и кнопку «назад» видно в окне; данные, которые приложение отправляет, печатаются в терминал.

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

Приложение подаётся с нашего адреса. Иначе никак: чужая страница может запретить себя встраивать, а её содержимое в любом случае закрыто правилом одного происхождения — ни прочитать, ни нажать. Проводник превращает любой адрес в путь /x/<схема>/<хост>/<путь>, и приложение оказывается обычной страницей внутри нашей.

Что при этом пришлось выяснить на живых приложениях, по одной поломке за раз:

  • Код переписывать нельзя. В нём адрес текстом не отличить от чего угодно другого: /https?:\/\//gi — это регулярное выражение, и подмена внутри него ломает разбор всего файла. Приложение падало на «invalid regular expression flags», пока я правил код наравне с разметкой. Теперь за код отвечает перехват во время работы — он видит настоящие адреса, а не похожий на них текст.

  • Якорь обязателен. Telegram кладёт в него подписанные данные запуска, а на сервер якорь не уходит вовсе. Отбросив его, мы отдавали приложению адрес без пропуска, и оно останавливалось на «Telegram initData is missing».

  • Пути от корня ведут к нам, а не к приложению. Вместо скриптов ему отдавалась наша же разметка, и оно молча не запускалось: браузер ругался на «MIME type text/html» для модулей.

  • HTTP/1.0 не годится. Браузер отказывался подгружать куски приложения при том, что тот же адрес прекрасно отдавался curl.

  • Чужой заголовок доступа отвергает наши куски — сервер разрешал доступ своему домену, а мы теперь под другим.

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

  • Приложение надо подавать по его собственному пути. Почти всякое из них одностраничное, и маршрутизатор разбирает location.pathname сам. Под префиксом /x/https/хост/bot/… он видит путь, не начинающийся с ожидаемого /bot, и не рисует ничего — белый экран без единой ошибки. Префикс остался только для чужих доменов.

  • Происхождение надо закрепить за приложением. Виджет поддержки внутри него тоже отдаёт HTML, и после его загрузки запросы приложения уходили к нему: сервер отвечал «wrong verify token», приложение вставало белым экраном.

  • Приложение может склеить свой адрес с нашим хостом. Wallet строит адрес API, приклеивая поддомен к текущему хосту: дома выходит alectryon.walletbot.me, а у нас — alectryon.127.0.0.1:51805. Такое браузер не разбирает вовсе: fetch падает до сети, приложение уходит в init_failed и показывает «Some technical issue». Возвращаем службе её настоящий дом до разбора адреса.

  • Сообщение может быть адресовано web.telegram.org. Часть приложений не грузит telegram-web-app.js вовсе, а шлёт события на конкретный адрес. Мы не он — браузер такое молча выбрасывает, приложение не дожидается ответа и через 200 мс пишет «Environment Error · Please open this application directly through Telegram». Своё окно вправе принимать всё, что ему шлют, поэтому чужой адрес назначения доставке не мешает; в журнале это видно строкой.

  • Событиям, которые ждут ответа, надо отвечать. Полноэкранный режим, «добавить на домашний экран», свои методы бота — молчание вешает приложение на заставке. «Тюряга» так и висела, пока не ответили на запрос полного экрана.

  • За переадресацией должен идти браузер, а не мы. Библиотека следует за 3xx молча, и страница возвращается по исходному пути: esim-bot.mobile.ag/ уводит на bot.mobile.tg/bot/, а рамка остаётся на / — маршрутизатор приложения видит не тот путь и не рисует ничего. Теперь переадресацию отдаём браузеру, а первая же из них закрепляет настоящее происхождение.

  • Страница окна не может жить в корне. У многих приложений путь — просто /, и окно грузило в рамку само себя: шапка рисовалась дважды, а приложение не появлялось вовсе. Окно ушло на /__tgx/, корень отдан приложению.

  • Вебсокеты надо вернуть на настоящий сервер. Их адрес уже переписан на нас, а вебсокетов мы не подаём — приложение на ActionCable просто не запускалось.

  • iframe_ready требует ответа стилем. Скрипт Telegram здоровается им ещё до самого приложения, и без ответа его подготовка не заканчивается — приложение падает по своему таймауту, с виду беспричинно.

Что проверено, до конца загрузки. Восемь приложений, каждое своим входом:

приложение

что показывает

Wallet

баланс, Transfer / Deposit / Withdraw / Exchange

Antarctic Wallet

вкладки, предложение поставить PIN

TonMobile eSIM

тарифы, баланс, страны — после починки переадресации

Epic Gift

баланс, Rocket, PVP, Play Hub

Gorilla Case

кейсы, розыгрыши, задания, турнир

Random Games

«Earn Stars», разделы, витрина

Telegram Simulator

рулетка, апгрейд, вкладки

Тюряга

запускается за полторы минуты: 6 МБ кода и 70 файлов ресурсов

Если приложение всё же откажется, окно скажет об этом словами и различит два случая: «загрузилось и отказалось работать» и «не пустило нас в рамку» — по тому, доступно ли нам её содержимое.

Отдельно — приложение может вовсе запретить себя встраивать; тогда содержимое нам недоступно, и в окне это написано другими словами. Различаем по тому, видим ли мы страницу внутри.

Кнопки, открывающие приложение

Обычным нажатием они не работают. У Telethon в click() нет ветки для KeyboardButtonWebView: нажатие молча возвращает пустоту, и tgx бодро отвечал "ok": true, "click_result": null, ничего при этом не сделав. Со стороны — «жму кнопку, ничего не открывается».

tgx inline press-app чат 295606 --text "Open Wallet" --run
tgx inline menu-button "Antarctic Wallet" --run

press-app берёт из кнопки её адрес и просит у сервера подписанный — тот самый, который открыл бы настоящий клиент. Обычную кнопку он отвергает и отсылает к message-click, а ненайденную — перечисляет, какие есть.

Кнопка-меню — отдельная сущность: не кнопка под сообщением, а свойство самого бота, та, что слева от поля ввода. Её адрес лежит в полном профиле бота, а не в bots.getBotMenuButton: тот отвечает только самому боту, а нам нужна та кнопка, которую видит человек. Настоящий клиент берёт её оттуда же.

Истории в полноэкранном клиенте

ctrl+o   лента историй

Слева список — автор, подпись, счётчик просмотров; справа сама картинка, тем же способом, что и превью в переписке: Kitty, iTerm2, sixel, а где ничего нет — юникодом.

j/k   листать          o   открыть целиком в системе
h     скрытые          s   скрытный просмотр
r     обновить         escape закрыть

У видео показываем кадр, а не качаем ролик. Терминал видео не играет, и тянуть мегабайты ради превью незачем — берём то, которое Telegram и так отдаёт. А o скачивает историю целиком и отдаёт системному просмотрщику: тем же путём, что и вложения в переписке.

Скрытный просмотр (s) — возможность Telegram Premium: просмотры не засчитываются ни за прошедшие минуты, ни за ближайшие. Без Premium сервер откажет, и отказ показывается как есть.

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

Все команды в полноэкранном клиенте

Долгое время окно и командная строка расходились: команд у tgx под шесть сотен, а в окне их было два десятка — те, что повешены на клавиши. Остальное из окна было недоступно вовсе.

ctrl+p   палитра команд — поиск по названию и описанию
ctrl+w   мини-приложение бота из его чата

Список в палитре не написан руками, а выведен из дерева разбора командной строки — оттуда же, откуда его берёт коннектор для агентов. Новая команда появляется в окне сама. Это не экономия труда: два списка, которые надо помнить обновлять, однажды разойдутся, и заметить это можно будет только по жалобе.

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

Соединение при этом одно. Команда, открывшая своё, упёрлась бы в занятый файл сессии — тот самый «database is locked», — поэтому ей одалживают уже открытый клиент окна, а закрытие делают пустым действием. Тот же приём, что и в коннекторе.

ctrl+w вынесен отдельно, потому что кнопка-меню — свойство самого бота, а не сообщения: в графическом клиенте она слева от поля ввода, и из чата с ботом её жмут чаще всего.

Числа палитра приводит сама. Поля ввода отдают строки — иначе им неоткуда взяться, — а командная строка прогоняет их через type=int при разборе, которого мы не делаем. Без приведения ломается не в палитре, а глубоко внутри: то '>' not supported between int and str, то struct.error: required argument is not an integer. По такому сообщению до причины не добраться, поэтому приведение стоит до вызова, а негодное значение называет своё поле.

Ещё одна мелочь, стоившая отладки: ctrl+p занят встроенной палитрой Textual. Пока обе были включены, выигрывала её — наша просто не открывалась, и по нажатию понять это было нельзя.

Представиться телефоном

Многие мини-приложения рисуют себя по-разному на телефоне и на настольном клиенте, а иные показывают на настольном заглушку.

tgx inline run бот --as android --phone

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

Это то же, что выбор устройства в отладчике браузера, и по умолчанию всё выключено.

Про «Тюрягу» я сперва написал неверно, и это стоит запомнить как урок. Мне показалось, что игра просит альбомную ориентацию и не запускается. На деле её блок #rotate-overlay лежит в разметке всегда и по умолчанию скрыт:

const надо = (платформа === 'android' || платформа === 'ios' ||
              matchMedia('(pointer:coarse)').matches)
             && (innerWidth < innerHeight && innerWidth / innerHeight < 0.75);

Оба условия у нас ложны, и блок не показывался ни разу. Я читал его текст сырым textContent, который берёт и скрытое, — и пять раз подряд искал причину там, где её не было. Собственный инструмент page при этом отвечал правильно: он пропускает display: none и возвращал пустоту, потому что игра рисует себя на холсте.

А стояла игра просто потому, что грузилась: 6 МБ кода и семьдесят файлов ресурсов. Через полторы минуты запустилась сама.

Переключатель платформы всё равно полезен — Gorilla Case в режиме телефона показывает подсказку, которой на десктопе нет, — но «Тюрягу» он не чинил и чинить было нечего.

Навигация в окне приложения

У окна своя шапка: назад, вперёд, обновить, в начало — и адрес, на котором вы сейчас внутри приложения. Без них из приложения некуда деться: кнопку «назад» рисует оно само и далеко не всегда, а обычная история браузера внутри есть всегда.

tgx window do back      # назад по истории приложения
tgx window do forward
tgx window do reload
tgx window do home      # к экрану, с которого начали

Агент внутри приложения

Приложение подано с нашего адреса — значит, оно видно и нажимаемо целиком, со своей навигацией, списками и полями.

tgx window do page                        # весь видимый текст приложения
tgx window do elements                    # что можно нажать и заполнить
tgx window do click --args '{"ref":"e1"}'
tgx window do fill --args '{"ref":"e2","text":"привет"}'
tgx window do scroll --args '{"y":600}'
tgx window do go --args '{"url":"/settings"}'

Проверено на настоящем чужом приложении: агент прочитал его текст, нашёл кнопку Show Error, нажал её и прочитал то, что приложение показало в ответ.

Поле заполняется через нативный сеттер значения, а не присваиванием: иначе React и подобные изменения не замечают.

page и layout берут текст из textContent, а не innerText, а видимость проверяют обходом предков по вычисляемому стилю. Причина одна: innerText, offsetParent и координаты отражают отрисовку, и у фоновой вкладки они пусты, а textContent и стиль живут в дереве и есть всегда. При этом сам браузер усыпляет вкладку, когда она уходит в фон, — тогда останавливается и опрос поручений. Поэтому окно, с которым работает агент, должно быть активной вкладкой; это его нормальное состояние — агент его открыл и с ним работает.

Окна, которыми пользуется агент

Человек окно видит и жмёт кнопки. Агент — нет: окно живёт в браузере, MCP-сервер в другом процессе, и общего между ними ничего. Здесь общее появляется — по образцу WebMCP, где страница объявляет свои действия сама.

tgx window list                   # какие окна открыты
tgx window read                   # что в окне и что в нём можно сделать
tgx window do press_main          # нажать главную кнопку приложения
tgx window do send_event --args '{"type":"theme_changed","data":{…}}'

Устройство трёхслойное, и каждый слой нужен по своей причине:

  • Реестр на диске. Окно поднимает одна команда, а спрашивает про него другой процесс — без записи он не узнает даже адреса. Записи чистятся по живости процесса. Тут легко ошибиться: отказ в праве послать сигнал означает, что процесс есть, но чужой, — это «жив», а не «мёртв», и спутав, реестр вычистит рабочее окно.

  • Состояние, которое шлёт страница. Что происходит внутри мини-приложения, знает только браузер: приложение чужое, его пиксели нам не видны. Зато оно разговаривает с нашей страницей, и всё сказанное страница пересылает нам — надпись на главной кнопке, есть ли «назад», что приложение прислало.

  • Поручения. Агент просит нажать кнопку, но нажимает её страница. Поручение кладётся в очередь, страница забирает его, выполняет и приносит результат. Ждём именно результата: «нажал» без него — отчёт о намерении, по которому нельзя судить, что вышло.

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

Сервер окна обязан быть многопоточным, и это не мелочь: поручение агента ждёт ответа страницы, а страница за ответом ходит на тот же сервер. В один поток они заперли бы друг друга насмерть.

Действия объявляет сама страница — встроенные snapshot, press_main, press_back, send_event, close у мини-приложения и snapshot, refresh у окна звонка. Своя страница может объявить свои:

window.tgx.registerTool('имя', 'что делает', {параметр: {type: 'string'}},
                        (args) => ({вышло: true}));

Наружу окна не смотрят: адрес обязан быть на 127.0.0.1, иначе tgx откажется с ним разговаривать.

Кнопка-меню бота

Видов у неё три, а не два, и средний легко пропустить:

tgx bot menu мойбот --url https://app.example --text "Открыть"
tgx bot menu мойбот --commands            # показывать список команд
tgx bot menu мойбот --reset               # как по умолчанию
tgx bot menu мойбот --url … --for-user @roman   # своя кнопка одному человеку

--commands — ровно то, чего хотят от бота без приложения. А --for-user делает персональную кнопку: у одного человека своя, у остальных общая, — так вешают панель администратора.

Стикеры: библиотека

tgx stickers featured             # что Telegram рекомендует
tgx stickers archived             # убранные в архив: не видны, но не удалены
tgx stickers emoji-groups         # разделы панели эмодзи
tgx stickers archive Набор        # убрать; --restore вернуть; --remove снести
tgx stickers gifs
tgx stickers whose чат 42         # из какого набора стикеры на фотографии

Чужие боты из терминала

Инлайн-бот — это тот, кого вызывают через @бот запрос прямо в поле ввода: @gif, @pic, @vid и тысячи других. Обычно результаты выбирают пальцем из всплывающего списка; здесь список печатается, а выбор делается номером.

tgx inline ask gif "cat"                    # спросить, получить метку и список
tgx inline send me 2059… gif5797…           # отправить выбранное
tgx inline start @somebot ref_12345         # «Начать» с параметром из ссылки
tgx inline press чат 4821 "vote_yes"        # нажать кнопку и услышать ответ

Метка списка живёт недолго. Когда сервер отвечает INLINE_RESULT_EXPIRED, это не поломка, а срок годности — надо спросить заново; мы так и пишем.

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

Мини-приложения:

tgx inline attach-list                # боты в меню вложений и где они работают
tgx inline attach @bot on --allow-write
tgx inline web-app wallet --open      # подписанный адрес, сразу в браузере

У Telegram три разных вызова под три способа открыть одно и то же приложение: из бокового меню, из чата и по короткому имени. Какой подойдёт, зависит от того, как бот его завёл, и снаружи это неизвестно. Поэтому tgx пробует боковое меню, а на отказ переходит к чату: для человека это одно действие, а не выбор из трёх непонятных.

Полученный адрес подписан вашей сессией — по сути это ключ от аккаунта внутри приложения. Он не пишется в файлы и никуда не уходит, кроме браузера; предупреждение печатается рядом с адресом каждый раз.

Блокировки, жалобы, уборка

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

tgx safety blocked                          # кого вы заблокировали
tgx safety block @stranger --confirm-to @me
tgx safety block @author --stories-only     # закрыть только истории
tgx safety unblock @stranger
tgx safety peer-settings @кто-то            # что Telegram думает о собеседнике
tgx safety clear-history @кто-то --both-sides --confirm-to @me
tgx safety unpin-all чат --confirm-to @me
tgx safety sponsored off                    # скрыть рекламу (Premium)

peer-settings — это не украшение: полоска «заблокировать / пожаловаться» над новым чатом собирается из ответа сервера, и его можно спросить прямо. Там же видно, берёт ли человек звёзды за сообщения и из какой страны его номер.

block-replier существует отдельно потому, что в комментариях под каналом вы часто не знаете, кто это, — знаете только сообщение:

tgx safety block-replier 4821 --delete --spam --confirm-to @me

Жалоба идёт по меню сервера, а не по списку в коде. Причины у Telegram меняются, и зашивать их к себе — значит устареть к следующему обновлению. Поэтому первый вызов показывает то, что прислал сервер, а второй передаёт выбранное обратно. Варианты приходят байтами, для терминала мы кодируем их в base64 и разворачиваем ровно в те же байты:

tgx safety report чат 4821
# → шаг: «За что?»; варианты: Spam (ключ OQ), Copyright (ключ OA), …
tgx safety report чат 4821 --option OQ --confirm-to @me

Остаток по сообщениям

tgx msgx vote чат 42 2            # проголосовать за второй вариант
tgx msgx voters чат 42           # кто как проголосовал
tgx msgx link-preview "https://…"   # что покажется под ссылкой, не отправляя
tgx msgx chat-theme @roman 🎄     # тема отдельного чата
tgx msgx who-reacted чат 42 43    # сводка реакций
tgx msgx saved-dialogs           # под-чаты избранного по автору
tgx msgx fact-check чат 42 "проверено" --confirm-to @me

Голоса задаются номерами, а сервер ждёт байты вариантов — их нельзя угадать, tgx берёт их из самого опроса. Иначе легко проголосовать не за то.

Ещё оттуда же — реклама, вход по кнопке, календарь вложений, приглашения:

tgx msgx sponsored канал          # какая реклама показывается
tgx msgx url-auth чат 42 0        # что предлагает «войти через Telegram»
tgx msgx search-calendar чат --kind photo   # по каким дням есть фото
tgx msgx check-invite "https://t.me/+…"     # что за чат, не вступая
tgx msgx scheduled чат 5 6        # отложенные по номерам
tgx msgx personal-channel @кто-то # посты его личного канала

Ключ рекламы — байты; в командную строку он идёт шестнадцатеричным и разворачивается обратно при отметке просмотра. url-auth показывает, что именно сайт узнает о вас, до согласия — потому и разведён на два шага.

Перенос переписки и прочий остаток

tgx msgx import-target чат          # годится ли чат для переноса
tgx msgx import-start чат выгрузка.txt
tgx msgx import-media чат 42 фото.jpg
tgx msgx import-run чат 42
tgx msgx web-page "https://…"       # мгновенный просмотр, если он есть
tgx msgx future-owner чат           # кому достанется чат, если выйти
tgx msgx suggested-folders          # какие папки Telegram предлагает

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

Мелочь, стоившая правки: название предлагаемой папки лежит внутри самой папки, а не на обёртке с описанием. Читая с обёртки, получаешь null там, где на самом деле «Unread».

Настройки и оформление аккаунта

Большая часть здесь в терминале не рисуется — обои, темы, наборы эмодзи-статусов, — но их можно ставить, и это меняется на всех ваших клиентах.

tgx account sensitive on            # показывать деликатный контент
tgx account username второйадрес off   # выключить дополнительный адрес
tgx account main-tab gifts          # что показывать первым в профиле
tgx account wallpapers              # какие обои доступны
tgx account themes --chat           # готовые темы для чатов
tgx account autosave                # что скачивается само
tgx account set-autosave users --photos --videos --max-mb 50
tgx account paid-revenue @кто-то    # сколько принесли платные сообщения

Тонкое место — дополнительные адреса. У аккаунта их бывает несколько, включённые показываются в профиле ссылками. Список один, а управляют по имени: включить, выключить, переставить. Перепутать основной и дополнительный легко — тогда профиль остаётся без адреса вовсе.

Выгрузка аккаунта

tgx takeout — та самая «экспорт данных» из настроек, только из терминала. Telegram даёт под неё отдельный режим сессии: внутри него ограничения на частоту мягче, поэтому историю можно выкачивать сотнями сообщений. Пишем на диск по ходу дела: у людей бывают чаты, которые в память не влезают.

tgx takeout ~/выгрузка                                   # контакты и список чатов
tgx takeout ~/выгрузка --chat "AllCrew Cockpit" --files   # и история со вложениями
tgx takeout-finish                                        # закрыть висящую

Две ловушки, обе стоили отладки:

  1. Пауза — это защита, а не поломка. На первый запрос Telegram может ответить TAKEOUT_INIT_DELAY и прислать в другое ваше устройство подтверждение. Смысл в том, что если аккаунт угнали, у вас есть время заметить. Мы так и пишем в ошибке, вместо «ошибка 420».

  2. Пустой признак выгрузки в сессии. Хранилище кладёт takeout_id в поле-блоб, и незаполненное поле читается как b'', а не None. Telethon сравнивает с None — и решает, что выгрузка уже идёт; сервер же на b'' отвечает ошибкой упаковки, ещё до сети. Получается выгрузка, которую нельзя ни начать, ни закончить. tgx приводит пустое значение к None перед работой, а takeout-finish честно говорит, было ли что закрывать.

Обычные группы и «печатает…»

Обычная группа — не супергруппа: без истории для новичков, без адреса, без ролей сложнее «администратор». Telegram её не убрал, и часть переписки у людей живёт именно в таких.

tgx group new "Ремонт" @roman @kate     # в одиночку нельзя, Telegram требует людей
tgx group add чат @kate --history 100   # и показать ей сто прошлых сообщений
tgx group admin чат @roman              # права
tgx group rank чат @roman "техдиректор" # звание вместо слова «админ»
tgx group upgrade чат --confirm-to @me  # в супергруппу; обратно нельзя
tgx group hand-over чат @roman --confirm-to @me

Обычная группа адресуется голым числом — единственное место в схеме, где так: у канала InputChannel, у человека InputUser, а тут просто chat_id. Перепутать легко, и сервер отвечает невнятным CHAT_ID_INVALID, поэтому tgx проверяет сам и отсылает к командам channel-*.

Признак набора в графическом клиенте ставится сам, в терминале — нет: терминал не знает, что вы набираете именно сообщение. Собеседник всё это время видит тишину.

tgx group typing @roman              # «печатает…»
tgx group typing @roman voice        # «записывает голосовое…»
tgx group typing @roman file --progress 60
tgx group typing @roman cancel       # передумали

Ветки обсуждений

Пост живёт в канале, а комментарии — в связанной группе, и там у них свой номер. Без этого перехода отвечать в ветку не по чему.

tgx group thread канал 6992     # → в обсуждении id 6464, ответов 2
tgx group replies канал 6992
tgx group read-thread канал 6992

Своё имя важнее чужого

Название вашего чата может совпасть с чужим адресом. У меня так вышло с PLOMBIR: это и мой канал, и посторонний @Plombir, — и глобальный поиск отдавал постороннего. Написать не туда так проще простого.

Теперь точное совпадение с названием своего диалога решает спор. Обратный порядок для @имени и адреса в нижнем регистре: их пишут, когда имеют в виду именно адрес, — tgx send durov по-прежнему идёт к @durov, а не ищет чат с таким названием.

Каналы: остаток управления

То, что в графическом клиенте разбросано по подменю настроек.

tgx chan search-posts "telegram cli"   # поиск по постам всего Telegram
tgx chan search-quota                  # сколько поисков осталось бесплатно
tgx chan username канал второйадрес off
tgx chan autotranslate канал on        # кнопка перевода у читателей
tgx chan main-tab канал gifts          # что показывать первым в профиле
tgx chan stickers группа НаборИмя      # общий набор для участников
tgx chan paid-messages группа 5        # звёзд за сообщение
tgx chan author канал 6992             # кто из админов написал пост
tgx chan left                          # каналы, из которых вы вышли
tgx chan delete канал --confirm-to @me

Две ловушки, обе в ответах сервера:

  • GetLeftChannels работает только внутри выгрузки. Сервер считает этот список историей, а не текущим состоянием, и снаружи отвечает TAKEOUT_REQUIRED. tgx chan left открывает выгрузку сам и закрывает за собой: просить об этом человека странно, он спросил всего лишь список.

  • send-as отвечает PEER_ID_INVALID и когда чата нет, и когда писать от чужого имени тут просто нельзя. Выбор появляется у админов канала с привязанной группой и у анонимных админов; в обычной группе его не бывает. Мы так и пишем — иначе выглядит как «чат не найден».

Витрина бота и его настройки

tgx bot previews-info мойбот      # что человек видит до «Начать»
tgx bot preview-add мойбот https://…/demo.mp4
tgx bot popular                   # какие мини-приложения сейчас смотрят
tgx bot similar мойбот
tgx bot referrals мойбот 150      # партнёрам 15%
tgx bot can-write мойбот
tgx bot free-name новыйбот

Доля партнёра считается в тысячных, а не в процентах: 150 — это 15%. Легко перепутать и отдать вдесятеро больше, поэтому tgx печатает результат процентами.

Превью принимают только публичный адрес — локальный файл сначала надо куда-то выложить. Ссылаться на превью можно лишь тем же медиа, каким его положили: отдельного идентификатора у них нет.

Что отдают только внутри выгрузки

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

tgx chan left        # каналы, из которых вы вышли
tgx contacts saved   # что Telegram помнит из вашей телефонной книги
tgx contacts forget-saved   # заставить забыть

Контакты: остаток

tgx contacts add-phone "+79261234567:Иван:Петров"
tgx contacts statuses          # кто когда был в сети, одним запросом
tgx contacts invite-token      # ссылка «добавь меня» без раскрытия номера
tgx contacts nearby 55.75 37.61            # только смотрим
tgx contacts nearby 55.75 37.61 --show-me 60   # и показываем себя на час
tgx contacts forget-top @кто-то   # убрать из «часто пишете»

nearby без --show-me не публикует вас: раздать своё местоположение случайно проще, чем кажется, поэтому по умолчанию мы только смотрим.

Ссылку «добавь меня» сервер отдаёт готовой. Я сначала собирал её из токена сам — и получал нерабочую: в настоящем токене есть вторая половина после двоеточия.

Истории: остаток

tgx stories edit чат 42 --caption "новая подпись"
tgx stories views чат 42 43        # просмотры, реакции, пересылки
tgx stories reactions чат 42       # кто как отреагировал
tgx stories album чат 7            # что в альбоме
tgx stories pin-top чат 42         # наверх профиля
tgx stories hide-all on            # убрать чужие истории из ленты
tgx stories start-live чат         # прямой эфир; смотреть его нечем

У правки отсутствие поля значит «не трогать», а не «стереть»: не назвали подпись — она осталась прежней.

Мелочи по чатам

Каждая из них по отдельности не тянет на раздел, но искать их приходилось каждый раз заново.

tgx message-link "AllCrew Cockpit" 42        # постоянная ссылка на сообщение
tgx message-link чат 42 --album              # на весь альбом
tgx common-chats @roman                      # где вы состоите вместе
tgx who-is чат @roman                        # кто он здесь: владелец, админ, гость
tgx my-public                                # ваши занятые публичные адреса
tgx similar durov                            # похожие каналы
tgx inactive                                 # где давно тихо — кандидаты на уборку
tgx antispam мойчат on                       # жёсткий антиспам (нужны бусты)
tgx default-ttl 604800                       # сообщения в новых чатах живут неделю

Даты в этих ответах приходят то числом, то датой — inactive отдаёт секунды, остальное объекты. Приводим к одному виду, иначе в выводе оказывается 1786542005 там, где ждёшь дату.

Каналы и группы

Создание и администрирование — из интерфейса, из шелла и у агентов.

  • n — новый чат: канал, супергруппа или группа с темами; название, описание и публичный адрес сразу в одном окне. Созданный чат тут же открывается.

  • i — управление текущим чатом: название, описание, публичный адрес, права участников по умолчанию, слоу-мод, а также ctrl+l — ссылка-приглашение (копируется в буфер), ctrl+d — привязать группу обсуждения, ctrl+u — участники. Если вы не администратор, панель об этом честно пишет.

tgx channel-create "Мой канал" --kind channel --about "о чём" --username mychannel
tgx channel-create "Команда" --kind group          # или --kind forum, с темами
tgx channel-discussion @mychannel --group @myteam  # привязать обсуждение
tgx channel-permissions @myteam --allow send_messages,send_media,embed_links
tgx channel-slowmode @myteam 30
tgx chat-join https://t.me/+abcdef                 # или @username
tgx chat-leave @oldchat --yes

Уже были и остаются: channel-info, channel-edit, channel-photo-set, channel-participants, channel-admin-set/remove, channel-ban/unban, channel-invite-add, invite-export/list, admin-log.

Агентам доступны чтение (chat_details, list_members) и созидательная часть записи: create_chat, edit_chat, create_invite_link, set_slowmode, set_permissions. Назначение админов, баны, удаление канала и выход из чатов намеренно оставлены только человеку — в CLI и TUI.

Форматирование и посты

Входящие сообщения рендерятся со всем оформлением: жирный, курсив, подчёркнутый, зачёркнутый, код, блоки кода, цитаты с полосой , упоминания и хэштеги цветом. Ссылки — настоящие OSC-8 гиперлинки, в Ghostty/kitty/iTerm2 кликаются мышью. Спойлеры закрыты плашкой ░░░, открываются клавишей s на выделенном сообщении.

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

Разметка (markdown, режим по умолчанию):

**жирный**   __курсив__   --подчёркнутый--   ~~зачёркнутый~~
`код`   ```блок кода```   ||спойлер||   [текст](https://ссылка)
> цитата — в начале строки, соседние строки склеиваются

Есть и HTML-режим: <b> <i> <u> <s> <code> <pre> <blockquote> <tg-spoiler> <a href>. Отступление от Telethon: подчёркивание, спойлеры и цитаты его markdown не умеет — tgx разбирает их сам и отправляет готовыми entity, поэтому то, что показано в превью, ровно то и уходит.

Смещения считаются в UTF-16, как требует Telegram, — жирный текст после эмодзи не съезжает.

Из CLI и у агентов то же самое:

tgx format                                   # шпаргалка
tgx format "**жир** ||секрет||"              # посмотреть, какие entity получатся
tgx send @channel "**Заголовок**\n\n> цитата" --schedule 2026-08-29T10:00
tgx send @channel "текст" --parse-mode html --no-preview --silent

Вложения

ctrl+s — прикрепить файл. В диалоге: поле пути, дерево файлов для выбора мышью или клавишами, превью с именем и размером (картинка рисуется тут же), четыре переключателя и подпись.

переключатель

что делает

как файл

без сжатия, оригиналом (force_document)

голосовое

ogg/opus уйдёт голосовым сообщением

кружок

круглое видео (video note)

без звука

уведомление без звука

Тип Telegram определяет сам: картинка уйдёт фото, .mp4 — видео с возможностью стриминга, остальное — документом; переключатели это поведение перекрывают. Для видео обязателен пакет hachoir (он в requirements.txt): без него Telethon не может прочитать длительность и размер, подставляет заглушку 1×1, и Telegram показывает ролик обычным файлом. Если в системе есть ffmpeg, tgx ещё и вырежет кадр-обложку, чтобы у видео было превью.

  • несколько файлов — перечислите пути через запятую, уйдут альбомом;

  • подпись подставляется из поля ввода и правится прямо в диалоге;

  • ответ — если выбран пост через ctrl+r, файл уйдёт ответом на него;

  • комментарий — в канале, где вы не админ, файл уходит комментарием к посту;

  • прогресс отправки виден в строке над полем ввода.

Из командной строки то же самое:

tgx send @peer "подпись" --file ~/photo.jpg --file ~/photo2.jpg
tgx send @peer --file ~/voice.ogg --voice
tgx send @peer --file ~/clip.mp4 --video-note --silent
tgx send @channel "мой коммент" --comment-to 12345

Каналы и комментарии

В канале, где вы не админ, обычная отправка запрещена — но комментарии открыты. Клиент это понимает: в шапке чата пишется только комментарии, поле ввода меняет подпись, и текст из него уходит комментарием к посту, а не падает с ошибкой прав.

  • c — тред комментариев к посту: список с бабблами и своё поле ввода;

  • под постом видно счётчик — 💬 12 комментариев c — открыть;

  • к какому посту комментарий: к выделенному (стрелками или кликом), к тому, на который вы ответили через ctrl+r, иначе — к последнему.

Под капотом это messages.SendMessage с comment_to (Telethon сам находит привязанную группу обсуждения) и iter_messages(reply_to=…) для чтения треда. Если у поста комментарии выключены, будет понятное сообщение, а не трейсбек.

Вложения в ленте

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

[2026-08-28T22:49] Alex: [голосовое 0:03]
[2026-08-29T22:28] Alex: [видео 0:10]
[2026-08-29T22:35] Alex: [стикер 💯]
[2026-08-29T22:41] Роман: [файл отчёт.pdf 2.1 МБ]
[2026-08-29T22:44] Роман: [опрос: Идём?]

Подпись всегда важнее описания: если текст есть, показывается он, а вложение остаётся отдельным полем. Кружок отличается от видео, голосовое от музыки, у музыки виден исполнитель. Нулевой вес не печатается — это «неизвестно», а не «0 Б».

Прочитанное

Чат, в котором вы задержались дольше секунды, отмечается прочитанным — счётчик непрочитанных гаснет и в TUI, и во всех остальных клиентах Telegram. Пролистывание списка стрелками чаты не «съедает»: отметка ставится только если вы действительно остались в чате. Отключается флагом --no-mark-read, вручную — shift+r.

Медиа

Фото, видео, гифки и стикеры показываются картинкой прямо в бабле. Канал рендера выбирается по возможностям терминала — опрос делается на старте, до запуска интерфейса (позже Textual забирает stdin, и ответ терминала уже не прочитать):

терминал

канал

что видно

kitty, Ghostty, WezTerm

kitty graphics protocol

настоящее изображение

xterm, foot, Windows Terminal, iTerm2

sixel

настоящее изображение

Terminal.app, tmux и всё остальное

полублоки в truecolor

цветной пиксель-арт, 2 пикселя на строку

Что реально используется — видно в шапке: 250 чатов · медиа: kitty graphics. macOS Terminal.app графических протоколов не умеет вообще, там будут только полублоки; за настоящими картинками — Ghostty, kitty, WezTerm или iTerm2.

Клавиши на выделенном сообщении:

клавиша

действие

v

картинка на весь экран (у видео — кадр, крупнее чем в бабле)

клик / двойной клик

выделить сообщение · открыть картинку

o

открыть оригинал в системном просмотрщике

ctrl+d

скачать вложение в data/downloads

Видео в терминале не проигрывается — ни kitty-протокол, ни sixel этого не умеют: v покажет кадр, o скачает оригинал и отдаст его системному плееру (с прогрессом в строке над полем ввода). Файлы кэша и загрузок сохраняются с правильным расширением — иначе macOS не понимает тип и open молча отказывается работать.

Флаги: --media auto (по умолчанию), либо tgp / sixel / halfcell / unicode / off, либо TGX_MEDIA. Если картинки в терминале выглядят криво, --media halfcell — самый безопасный вариант: обычный цветной текст, который корректно скроллится.

В ленте качается миниатюра (~320 px, десяток килобайт), а не оригинал, и только для последних 24 медиасообщений открытого чата; полноразмерная версия подтягивается лениво при v. Кэш — data/media-cache, удаляется в любой момент. Голосовые, аудио и документы без миниатюр остаются чипами.

Анимации

Заставка сделана на terminaltexteffects — 37 эффектов:

tgx banner --effect matrix     # или beams, decrypt, slide, laseretch, rain…
tgx banner --list              # все доступные
TGX_EFFECT=blackhole tgx ui    # эффект по умолчанию для запуска

Внутри интерфейса анимации свои: проявление бабблов, подсветка нового сообщения, анимация «печатает…», плавное сворачивание панели, тосты. TEXTUAL_ANIMATIONS=none отключает их, TGX_NO_SPLASH=1 — заставку.

CLI

Вывод раскрашивается только когда stdout — терминал. В пайпе, под --plain, NO_COLOR или TGX_PLAIN=1 формат остаётся ровно прежним, так что старые скрипты не ломаются.

tgx dialogs --limit 30            # таблица
tgx history @channel --limit 50   # лента сообщений
tgx search "релиз" --per-dialog 3
tgx folders                       # папки Telegram
tgx folder-upsert AI --match-regex '(?i)\b(ai|llm)\b'
tgx send @user "привет" --file ./pic.png
tgx export @channel --output out.jsonl --format jsonl

Полный список — tgx без аргументов, детали по команде — tgx <команда> --help.

Коннектор для агентов (MCP)

bin/tgx-mcp — MCP-сервер поверх того же аккаунта. Агенту доступно всё, что доступно вам: 647 инструментов.

claude mcp add tgx -- ~/telegram-cli-tools/bin/tgx-mcp
claude mcp add tgx --env TGX_MCP_READ_ONLY=1 -- ~/telegram-cli-tools/bin/tgx-mcp
~/telegram-cli-tools/bin/tgx-mcp --check                        # проверить руками

Набор состоит из двух частей.

Именные инструменты (около полусотни) написаны под частые задачи: у них подробные описания, они делят одно соединение и отвечают быстрее.

cli_* — весь командный интерфейс целиком. Они не написаны, а выведены из самого argparse: дерево команд обходится при старте, и каждая листовая команда становится инструментом со своей сигнатурой, типами и подсказками из help=. Новая команда в CLI появляется в MCP сама — дописывать в сервере нечего.

tgx profile photo клип.mp4 --start 2.5   →   cli_profile_photo(source, start=…)
tgx business connect @bot --replace      →   cli_business_connect(bot, replace=…)

Пропускаются только команды, которым нужен человек за клавиатурой: вход по коду из SMS и запуск TUI — в переписке с агентом они бы просто зависли. Если одно имя дают вложенная команда и старый плоский псевдоним, побеждает вложенная.

Ограничений по составу нет: профиль, бизнес-режим, токены ботов, удаление — всё на месте. Опасное не запрещено, а помечено: инструменты удаления и выхода из чата несут пометку destructive, а инструкции сервера требуют показать пользователю, что именно сейчас произойдёт, и дождаться согласия. TGX_MCP_READ_ONLY=1 вешает замок, если он всё-таки нужен, а TGX_MCP_PEERS сужает список чатов.

инструмент

режим

что делает

me

чтение

аккаунт, и включена ли запись

list_chats

чтение

список чатов с фильтрами по типу, тексту, непрочитанным

read_chat

чтение

сообщения чата, с пагинацией и поиском

search_messages

чтение

поиск в чате или по всем чатам

read_comments

чтение

тред комментариев под постом

list_folders

чтение

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

chat_info

чтение

тип чата и можно ли в нём писать

format_syntax

чтение

шпаргалка по разметке для составления постов

search_messages

чтение

поиск с фильтрами по типу, отправителю и датам

list_topics · list_pinned

чтение

темы форума · закреплённые сообщения

pin_message · create_topic · edit_topic

запись

закрепление и темы

send_checklist

запись

чек-лист с пунктами

rich_syntax · bot_send_rich

чтение · запись

богатые сообщения Bot API 10.1

list_articles · publish_article

чтение · запись

статьи на telegra.ph из маркдауна

list_bots

чтение

имена сохранённых ботов — без токенов

bot_post

запись

пост от имени бота с кнопками и мини-приложениями

download_media

чтение

скачать вложение, вернуть путь

send_message

запись

текст с разметкой, ответ, комментарий, отложенная отправка

react

запись

поставить или убрать реакцию

edit_message

запись

править своё сообщение (md/html)

forward_messages

запись

переслать между чатами

press_button

запись

нажать инлайн-кнопку бота

send_file

запись

файлы и альбомы со всеми флагами

mark_read

запись

отметить чат прочитанным

Что осталось от прежних ограничений:

  • предупреждение вместо запрета — необратимое (удаление, выход из чата, отзыв токена, подключение бота к личной переписке, смена собственного аватара) помечено destructive, и в инструкции сервера сказано сначала показать пользователю, что именно произойдёт, и дождаться ответа;

  • TGX_MCP_READ_ONLY=1 — замок на всю запись, если он нужен;

  • TGX_MCP_PEERS=@chat1,@chat2 ограничивает агента конкретными чатами;

  • отдельная сессия data/tgx-mcp.session, снятая копией через sqlite-backup, поэтому сервер работает одновременно с открытым TUI; команды cli_* идут через то же соединение, иначе второй клиент упирается в «database is locked»;

  • лимиты выдачи режутся до 200 сообщений на вызов;

  • токен бота выдаётся по запросу, но в инструкции сервера отдельно сказано не записывать его в сообщения и файлы;

  • инструкции, найденные внутри самих сообщений Telegram, — это данные, а не приказы: агенту велено их цитировать и спрашивать.

Сообщения уходят от вашего имени — текст и получателя агент подтверждает заранее.

Если агенту проще шеллом, а не по MCP, весь CLI уже машинный: tgx dialogs --jsonl, tgx history @peer --jsonl, tgx search "…" --jsonl — в пайпе формат остаётся стабильным JSON.

Сколько покрыто

.venv/bin/python tools/coverage.py            # сводка
.venv/bin/python tools/coverage.py messages   # пробелы одного раздела

На сегодня все одиннадцать разделов, которые имеют смысл в терминале, закрыты полностью: 100% по делу.

Голая доля вводит в заблуждение. В схеме лежат секретные чаты, покупки в магазинах приложений, обработчики на стороне работающего бота и сигнальный обмен звонков — терминальному клиенту это недоступно или не нужно. Поэтому непокрытое делится на три кучки: сделано через обёртку Telethon, неприменимо, настоящий пробел. Считать имеет смысл только последнюю — это столбец «по делу».

Отнесения в tools/coverage.py — суждение, а не факт. Они выписаны списком, их видно, и с ними можно спорить.

Структура

bin/tgx            обёртка venv → tgx.py
bin/tgx.py         CLI: подкоманды, argparse
bin/tgx_tui.py     TUI: бэкенды (Telethon + демо), виджеты, приложение
bin/tgx_tui.tcss   стили TUI (темы tgx-night / tgx-day)
bin/tgx_article.py статьи telegra.ph: маркдаун → узлы, публикация
bin/tgx_business.py бизнес-режим: бот-секретарь, часы, автоответы, ссылки
bin/tgx_forum.py    форумы и темы целиком: правила сервера в одном месте
bin/tgx_guard.py    именные одноразовые приглашения и сверка вошедших
bin/tgx_poll.py     опросы и викторины, включая новинки Bot API 10.0
bin/tgx_pay.py      звёзды, TON и счета; оплата — за воротами подтверждения
bin/tgx_confirm.py  человек в контуре: карточка с кнопками и ожидание решения
bin/tgx_contacts.py адресная книга, чёрный список, поиск людей
bin/tgx_stories.py  истории: лента, публикация, просмотры, альбомы
bin/tgx_folders.py  общие папки: ссылки на набор чатов и их обновления
bin/tgx_stickers.py свои наборы стикеров
bin/tgx_stats.py    статистика каналов, групп, постов и историй
bin/tgx_pending.py  черновики, отложенные, заготовки, избранное
bin/tgx_security.py сессии, приватность, сроки
bin/tgx_calls.py    групповые звонки: управление из терминала
bin/tgx_callweb.py  живая страница участников звонка
bin/tgx_transcribe.py расшифровка голосовых: ожидание готового текста
bin/tgx_profile.py  оформление: аватары всех форматов, цвета, статус, дата
bin/tgx_banner.py   запись терминальной заставки в видео для аватара
bin/tgx_ai.py      руками Telegram: правка, пересказ, перевод, тоны письма
bin/tgx_takeout.py выгрузка аккаунта: контакты, чаты, история, вложения
bin/tgx_chatx.py   мелочи по чатам: ссылки, общие группы, антиспам, сроки
bin/tgx_triage.py  что ждёт внимания: упоминания, реакции, прочтения, счётчики
bin/tgx_notify.py  уведомления и тишина, заявки на вступление, набор реакций
bin/tgx_safety.py  блокировки, жалобы по меню сервера, уборка переписки
bin/tgx_groups.py  обычные группы, ветки обсуждений, признак набора
bin/tgx_chanadmin.py остаток управления каналами: адреса, вид, уборка
bin/tgx_webapp.py  окно вокруг мини-приложения: протокол хозяина
bin/tgx_windows.py реестр окон и мостик к ним для агентов
bin/tgx_palette.py все команды CLI внутри полноэкранного клиента
bin/tgx_proxy.py   мини-приложение через свой адрес: видимость целиком
bin/tgx_account.py настройки и оформление аккаунта: адреса, обои, темы
bin/tgx_msgextra.py остаток по сообщениям: голоса, факт-чек, предпросмотр
bin/tgx_inline.py  чужие боты: инлайн-запросы, кнопки, мини-приложения
bin/tgx_autotools.py вывод инструментов MCP из дерева команд CLI
bin/tgx_net.py     общий HTTPS: сертификаты и понятные ошибки сети
bin/tgx_rich.py    богатые сообщения: разметка, лимиты, отправка и отрисовка
bin/tgx_bots.py    боты: реестр токенов, диалог с BotFather, клиент бота
bin/tgx_format.py  разметка: разбор в entity, отрисовка, шпаргалка
bin/tgx_media.py   превью картинок: выбор протокола и размера в ячейках
bin/tgx_render.py  общий слой вывода: палитра, таблицы, JSON, лента сообщений
bin/tgx_splash.py  анимированная заставка
bin/tgx-mcp        обёртка venv → tgx_mcp.py
tools/coverage.py  что из API покрыто и почему остальное — нет
bin/tgx_mcp.py     MCP-коннектор для агентов (чтение по умолчанию)
bin/tgx_smoke.py   headless-самопроверка TUI + SVG-скриншоты

Проверка после правок:

.venv/bin/python bin/tgx_smoke.py /tmp/tgx-shots
A
license - permissive license
Not graded
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to interact with Telegram accounts through MCP, supporting messaging, contacts, groups, media, and admin functions.
    4
    Apache 2.0
  • F
    license
    Not graded
    quality
    C
    maintenance
    Connects AI agents to Telegram via the official TDLib library, enabling tools like getting user info, listing dialogs, and searching messages.
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to interact with a user's Telegram account: list chats, read history, search, and send messages through Telegram's MTProto API.
    1
    MIT

View all related MCP servers

Related MCP Connectors

  • Multi-tenant Telegram gateway for AI agents — HTTP+stdio, 8 tools, MTProto User API

  • Telegram bridge for your MCP-compatible agent. Bidirectional, no LLM in our stack.

  • Phone, SMS & email for AI agents — one remote MCP endpoint, OAuth login, zero install.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mal4i6ka/tgx'

If you have feedback or need assistance with the MCP directory API, please join our Discord server