Skip to content
Back to skills

Use Telegram

ASecurity

The Telegram Desk product — the pattern this starter ships for turning a messenger into a personal memory: voice and files come in, meaning is extracted, everything lands in four stores at once, and reminders fire on their own. Load it before touching anything under lib/products/telegram-desk, when the owner wants a bot that remembers, when a message arrives and nothing happens, and before adding any new kind of incoming message. Two things decide the whole design and neither is visible from ...

  • 61 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 5, 2026
ai-agentsjavascriptrustjavaapidatabase

Works with

  • api

Security analysis

A100/100

Scanned November 5, 2026

npx -y skills add Fractera/web4-node-starter-kit --skill use-telegram --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Use Telegram?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Use Telegram
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/fractera-use-telegram/badge)](https://www.skillsdirectory.com/skills/fractera-use-telegram)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: use-telegram
description: >
  The Telegram Desk product — the pattern this starter ships for turning a messenger into a personal
  memory: voice and files come in, meaning is extracted, everything lands in four stores at once, and
  reminders fire on their own. Load it before touching anything under lib/products/telegram-desk, when
  the owner wants a bot that remembers, when a message arrives and nothing happens, and before adding
  any new kind of incoming message. Two things decide the whole design and neither is visible from the
  code: one model call asked to do six jobs performs five of them silently — so intent is routed first
  and each branch gets its own short prompt; and a reply is not politeness but a receipt, showing the
  same summary that went into the database.
---

# use-telegram

> Informational, not binding. **Know a better way for the case in front of you — do it your way and
> say so.** You are trusted with the creative work on this project.

**Это ПРОДУКТ, а не машинерия платформы.** Он едет в стартере так же, как блог: агент в проекте
клиента копирует его устройство под чеки, полевые заявки, заказы — под всё, что человеку проще
сказать, чем напечатать. Транспорт под ним — `use-channels`.

---

## 1. Устройство: два процесса, пять складов

```
человек → Telegram → служба каналов (:3500, единственный читатель бота)
                          │ push                    ↑ send / sendFile
                          ▼                         │
              POST /api/telegram/hook  ──────────────┘
                          │
        ┌─────────────────┼──────────────────┬───────────────┐
        ▼                 ▼                  ▼               ▼
   tgdesk_* (база)   медиатека          векторы         граф знаний
```

🔒 **Служба — единственный читатель бота.** Telegram отдаёт обновление ровно ОДНОМУ спрашивающему;
второй опрос не добавляет возможность, а молча съедает половину сообщений. Приложение никогда не ходит
в Telegram само — ни за обновлениями, ни за файлом.

**Четыре таблицы** (`lib/db/index.ts`, приставка `tgdesk_`):

| Таблица | Держит |
|---|---|
| `tgdesk_messages` | оркестрирующая: время сказанного и время СОБЫТИЯ, направление, текст, сводка, гео, род вложения, флаг денег, связка, заметки об отказах |
| `tgdesk_artifacts` | связь многие-ко-многим со складами: `media` (имя файла) · `vector` (id) · `rag` (источник) |
| `tgdesk_entries` | смысл: заметка, задача, чек, место, идея — со статусом подтверждения и валютой |
| `tgdesk_calendar` | напоминания и встречи: разовые, повторяющиеся, с предупреждением заранее |

🔒 **Артефакты вынесены в таблицу, а не в колонки.** Одно сообщение несёт три фотографии и голос:
колонка `media_id` ломается на второй фотографии, а таблица держит любое их число.

## 2. 🔒 Ветвление — часть логики, а не строчка в промпте

**Одна инструкция, просящая модель сделать шесть дел разом, исполняется на пять.** ✗ четыре просьбы
подряд «напомни через минуту» стали заметками: модель не отказывалась — она просто не заполняла одно
поле. Ни ошибки, ни отказа, просто отсутствующий ключ. Тот же вопрос коротким промптом отвечался
безошибочно.

Поэтому шаг первый отвечает на единственный вопрос — **что это за просьба**, — и только потом работает
своя короткая инструкция:

| Ветвь | Файл | Вызовов модели |
|---|---|---|
| `command` (`/start`) | статический текст | **0** |
| `confirm` («да», «нет») | сравнение слов | **0** |
| `where` (город → зона) | `branches/where.ts` | 1 |
| `meta` («кто ты») | `branches/meta.ts` — личность, **без базы** | 1 |
| `schedule` | `branches/schedule.ts` | 1 |
| `correct` (поправка) | `branches/correct.ts` | 1 |
| `question` | `answer.ts` | 1 |
| `capture` | `branches/capture.ts` | 1 |

🔒 **Маршрутизатор возвращает ТОЛЬКО ветвь.** Соблазн «заодно верни и сводку» и есть та самая болезнь:
как только у вызова появляется второе дело, одно из двух начинает пропадать.

## 3. 🔒 Правила связности: что с чем связано

Ответ обязан находить свой вопрос. Правил четыре, живут в `calendar.ts`:

1. **Открытое ожидание сильнее времени.** Спросили «ставлю?» — ответ относится к этому вопросу,
   сколько бы ни прошло: человек отвлёкся, а не передумал. ✗ «Да» через пять минут стало отдельной
   заметкой, потому что связь искали окном в три минуты — а окно тут ни при чём.
2. **Окно в три минуты — про сообщения ПОДРЯД** (пересылка с пояснением). Другой род связи, первый не
   заменяет.
3. **Ожидание не вечно** — через сутки протухает: человек уже не помнит, о чём спрашивали.
4. **Ожидание одно.** Новое предложение отменяет прежнее неотвеченное, иначе «да» уходит в позавчерашнее.

🔒 **Слабой модели мало знать, что «что-то ждёт», — ей нужен ПРЕДМЕТ.** В подсказку идёт «предложено:
встреча по адресу на 24 августа 16:42 — подтвердить время». Тогда «12:00» очевидно поправляет
предложенное, а не заводит вторую встречу.

🔒 **Связка не склеивает записи.** Каждое сообщение остаётся своей строкой со своими артефактами:
слитые, они потеряли бы возможность найтись по отдельности. Связка — это ссылка.

**Соглашение о пересылке.** Telegram отдаёт автора тремя поколениями полей, и все живы
(`forward_origin`, `forward_from`, `forward_sender_name`). Когда автор скрыт настройками, человек
пишет перед пересылкой «Сообщение от Имя Фамилия» — связка и закон атрибуции переносят имя на
следующее сообщение.

## 4. 🔒 Конверт: почему в граф идёт не голый текст

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

**Граф питается сущностями и связями**, а не буквами: голое «купил ноут» не даёт ему ни одной опоры.
Приставка источника (`telegram/msg-<id>`) отделяет канал от всего, что владелец грузит руками, и
позволяет удалить переписку одной операцией.

**Цена названа честно:** каждое сообщение стоит построения графа. Владелец выбрал полноту, а не порог.

## 5. 🔒 Ответ — расписка, а не вежливость

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

**Печатается только то, что действительно записано.** Строка «в графе знаний» при неудавшейся загрузке
— ложь, которую обнаружат месяцем позже и уже не свяжут с этим ответом. Файл сохранён, но не прочитан
— так и пишется.

**Деньги и время подтверждаются.** Цифра, записанная неверно, всплывает при подсчёте, когда проверять
поздно. У чека **дата обязательна**: не разобрали — ставим сегодняшнюю и говорим об этом вслух.
Валюта — с чека, а не видно — из `APP-CONFIG`, и это тоже называется.

## 6. Файлы: что читается, а что нет

| Род | Что происходит |
|---|---|
| картинка | медиатека + описание зрячей моделью; **текст на ней перечисляется первым и дословно** |
| голос и звуковой файл | медиатека + расшифровка |
| `txt`, `csv`, `md` | медиатека + чтение как есть |
| PDF | **сохраняется, не читается** — библиотеки в проекте нет |
| видео | **отложено владельцем**: частный случай, разбор дорогой |

🔒 **Разрешение задаётся явно (`detail: "high"`).** ✗ по умолчанию действует `auto`, и для крупной
картинки выбирается низкое: мелкий текст в нём физически не читается, сколько его ни проси. Это про
то, что модель ВИДИТ, а промпт тут бессилен.

🔒 **Файл берётся у службы по петле, а не ссылкой Telegram** — в той ссылке лежит токен бота целиком.

🔒 **Вложение забирается ДО разбора.** Прочитанное с картинки — часть того, что человек сказал: «вот
чек» без суммы это не запись о трате, а бессмысленная строка.

## 7. Время: часовой пояс человека

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

Продукт **спрашивает город сам** при первом напоминании и **пишет ответ в `APP-CONFIG`**.
🔒 Это отступление от закона «панель пишет, приложение читает», принятое владельцем: до поля в
настройках мало кто дойдёт, а пояс, о котором не спросили, остаётся пустым навсегда.

🔒 **Спрашиваем ГОРОД, а не зону и не сдвиг.** «UTC+2» врёт полгода из двенадцати, имя зоны — никогда.
Преобразование через `Intl`, без библиотек, и смещение берётся **на ту же дату**: напоминание на конец
октября, поставленное в августе, иначе уедет на час.

**Уже поставленные напоминания новый пояс задним числом НЕ двигает** — это меняло бы то, что человек
подтвердил.

## 8. Часовая стрелка

Планировщика у платформы нет. Напоминание обязано сработать, когда на сайт никто не смотрит, поэтому
служба каналов бьёт в дверь `POST /api/telegram/tick` по расписанию из панели (выключено · минута ·
пять · пятнадцать · час). Что наступило — решает продукт.

🔒 **Ищутся ПРОСРОЧЕННЫЕ, а не «ровно эта минута»:** сервер перезагрузился — напоминание догонит себя
само. И **следующее срабатывание считается от прошлого**, а не от «сейчас»: иначе опоздавшее на час
напоминание навсегда сдвигает своё время, и «в девять утра» через неделю становится полднем.

## 9. Свежий сервер: что настраивается само, а что нет

Адреса дверей и шаг расписания служба подставляет сама — они одинаковы на каждом сервере. **Вручную
остаётся одно: `TELEGRAM_HOOK_SECRET` в `.env.local` слота.** Нет секрета — двери приложения нет, и
бот честно работает по-старому, отвечая из базы знаний; режим при этом выводится, а не хранится.

## 10. Четыре ловушки, каждая уже оплачена

- ✗ **`\b` не знает кириллицы.** Граница слова в JavaScript — это `[A-Za-z0-9_]`; после «да» её нет, и
  правило не совпадает никогда. Английское «ok» работало, русское «да» — нет. Дефект прятался за
  прежним умолчанием «иначе да»: две ошибки компенсировали друг друга.
- ✗ **Умолчанием подтверждения было «да».** Любое сообщение при ожидающем напоминании молча его
  подтверждало.
- ✗ **Сквозной прокси слоя данных читал ответ как текст.** Двоичное приезжало мёртвым: `FF D8` →
  `EF BF BD`, зрячая модель отвечала «unsupported image», а файл в медиатеке был мусором. Починено в
  платформе.
- ✗ **Слой данных не возвращает `lastInsertRowid`.** Код, написанный по локальной базе, на сервере
  получил бы `NaN` и связал артефакты с несуществующим сообщением. Идентификатор читается обратно.

## 11. Перед тем как сказать «работает»

1. Сообщение прошло **живьём**, и вы прочитали `tgdesk_messages`, `tgdesk_artifacts`, `tgdesk_entries`.
2. Колонка `notes` пуста — она и заведена затем, чтобы отказы не были невидимыми.
3. Негативный контроль: чужой секрет → 401 и ни одной строки в базе.
4. Что НЕ проверено — названо вслух. Отчёт о сборке доказательством не является.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in ai-agents →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.