- TypeScript 99.5%
- Shell 0.3%
- Dockerfile 0.2%
|
|
||
|---|---|---|
| docs/specs | ||
| scripts | ||
| src | ||
| test | ||
| .env.example | ||
| .gitignore | ||
| bun.lock | ||
| bunfig.toml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
reminder-bot
Телеграм-интерфейс к личному трекеру Vikunja (@bysaw_remind_bot). Пишешь или наговариваешь в свободной форме — бот заводит задачу или напоминание в Vikunja, присылает напоминания, сводки изменений и просрочки.
- «через 20 минут снять чай» → «Напоминания», напомнит через 20 минут
- «сфера: смержить !858 завтра в 10» → проект «Сфера», срок и напоминание
- «каждый понедельник в 10 планёрка» → повтор (ведёт бот)
- «шлюз 404 ждёт devops», «срочно …» → метки и приоритет
- «сделал отчёт», «перенеси планёрку на 11», «перенеси блокеры в сферу», «пометь шлюз ждёт бэк», «коммент к шлюзу: всё ещё 404»
- пересланное сообщение (можно с подписью «завтра в 10») → задача с текстом и ссылкой на источник; фото, файлы, альбомы → вложения
- «что на сегодня», «что ждёт других», «что по сфере», «найди шлюз»
Архитектура
Telegram ⇄ bot (grammY, polling) ──REST v1──▶ Vikunja (http://vikunja-<uuid>:3456)
▲ │
└── :8080/vikunja ◀── webhooks ─────┘ (HMAC X-Vikunja-Signature)
src/vikunja.ts— клиент API v1 (ретраи чтений, таймаут 10 с, read-modify-write правок:POST /tasks/{id}в v1 — полная замена).src/store.ts— доменный слой: создание, done, перенос, snooze, проекты, метки, списки (сегодня/неделя/ждёт/мне/входящие/проект/поиск), поиск кандидатов.src/webhook.ts— приёмник вебхуков (Bun.serve,/vikunja,/health), проверка HMAC.src/changes.ts— эхо-фильтр своих правок (60 с), снимки задач для диффа, debounce-сводка «🔔 Изменения» (2 мин тишины, максимум 10 мин; «Напоминания» и перестановки в канбане не попадают).src/cards.ts— карточки, списки, клавиатуры (HTML).src/parse.ts,src/localparse.ts,src/domain.ts— интенты: локальный парсер первым, LLM (LiteLLM) фолбэком; проекты по ключевым словам, метки «ждёт: …», приоритет.src/migrate.ts— разовый перенос старых напоминаний SQLite → Vikunja («Напоминания»), metamigrated_vikunja.src/db.ts— SQLite: meta, parse_log,repeat_rules(повторы ведёт бот, в Vikunjarepeat_after = 0),task_snapshots,pending_ops(очередь создания, когда Vikunja лежит),cb_state(состояние кнопок). Таблицаreminders— только для отката.
Вебхуки Vikunja:
- пользовательский (id 1) →
task.reminder.fired,tasks.overdue(user-level принимает только такие события); - проектные на всех корневых проектах →
task.created/updated/deleted,task.comment.created. Их заводит и перепроверяет раз в час сам бот (WEBHOOK_TARGET_URL), подпроекты покрываются родителем.
Повторы: правило в repeat_rules; при срабатывании напоминания бот сразу переставляет срок на следующее вхождение; «Готово» у повторяющейся не закрывает её; закрыли в UI Vikunja — бот откроет со следующей датой; snooze повторяющейся — отдельное разовое напоминание.
Команды: /today /week /waiting /mine /inbox /overdue /p <проект> /find <текст> /list /app /health /unparsed, /t_<id> — карточка задачи. Кнопка меню чата открывает Mini App (TMA_URL).
Тесты: bun test (парсер, проекты/метки, клиент на моках fetch, повторы ↔ даты, карточки и клавиатуры, HMAC, debounce и эхо, миграция на SQLite-фикстуре), bun run typecheck.
E2E-прогон кнопок (scripts/e2e.ts)
Настоящий код бота (createApp()) против настоящей Vikunja, Telegram подменён: апдейты идут через bot.handleUpdate, все вызовы Bot API перехватываются bot.api.config.use и записываются (в Telegram ничего не уходит), скачивание файлов — фикстура. Все записи — во временное дерево проектов «E2E …» под «Напоминаниями» (прод-бот это поддерево в сводку не берёт), в конце удаляется. 19 сценариев: создание с проектом/меткой/сроком, 📁/🏷/✅/↩️/+1ч/Завтра/Отменить, ⏰ из подписанного вебхука (+10 мин, Готово, повтор), «сделал …» с выбором, /today /waiting /p /find с ◀ ▶ и ✅n, пересылка с подписью, альбом с вложениями, /app и меню, /t_.
Запуск на сервере рядом с прод-ботом (его образ и env, токен с правом создавать/удалять проекты — E2E_ADMIN_TOKEN, лежит в /root/.config/vikunja-hooks/e2e.env):
rsync -a src scripts hermes-server:/tmp/rb-e2e/ && ssh hermes-server 'sudo bash /tmp/rb-e2e/scripts/e2e-server.sh'
scripts/e2e-server.sh берёт образ и env прод-контейнера, добавляет e2e.env и запускает bun scripts/e2e.ts. Выход 0 — всё PASS.
Env
| Var | Что |
|---|---|
BOT_TOKEN |
токен от @BotFather |
ALLOWED_USER_IDS |
CSV user id (whitelist) |
ADMIN_CHAT_ID / OWNER_CHAT_ID |
алерты / куда слать напоминания и сводки (default: первый из whitelist) |
VIKUNJA_URL |
адрес API, в Coolify — http://vikunja-02adxvhzdteuziuuz7arzaml:3456 (внутренняя сеть coolify) |
VIKUNJA_TOKEN |
API-токен «reminder-bot» |
VIKUNJA_PUBLIC_URL |
для ссылок «🔗 Открыть» (default https://tasks.openbomber.com) |
VIKUNJA_WEBHOOK_SECRET |
HMAC-секрет вебхуков |
WEBHOOK_PORT |
порт приёмника (8080, наружу не публикуется) |
WEBHOOK_TARGET_URL |
как Vikunja достучится до бота: http://cgv2d141nfvy73p8kqbtaxgl:8080/vikunja; пусто — бот не трогает проектные вебхуки |
TMA_URL |
Mini App (https://tasks-app.openbomber.com) |
NO_PROXY |
должен включать хост Vikunja, иначе запросы уйдут в HTTP_PROXY |
LLM_BASE_URL / LLM_API_KEY / LLM_MODEL / LLM_FALLBACK_MODELS / LLM_TIMEOUT_MS |
парсер |
ASR_BASE_URL / ASR_API_KEY / ASR_MODEL |
голосовые |
WATCHDOG_MINUTES |
проверка LLM-цепочки (0 — выкл) |
TZ |
Europe/Moscow (как у пользователя в Vikunja) |
DB_PATH |
sqlite (/data/reminders.db) |
Деплой
Coolify (MS-02), app cgv2d141nfvy73p8kqbtaxgl: push в main на git.openbomber.com/bysawec/reminder-bot → Forgejo webhook → автодеплой. Контейнеру нужен HTTPS_PROXY (killswitch сервера режет прямой WAN) и NO_PROXY с хостом Vikunja.
Откат на версию до Vikunja
git revertмержаfeat/vikunja(или деплой коммита2d16df5) — старая версия снова читает таблицуreminders(она не тронута миграцией) и вTZвернутьAsia/Yekaterinburg,DIGEST_TIME=09:00.- Вебхук пользователя Vikunja id 1 вернуть на
http://10.0.1.1:8790со старым секретом (SQL в таблицеwebhooks, секрет — в/root/.config/vikunja-hooks/), проектные вебхуки бота удалить (target_url= адрес бота). sudo systemctl enable --now vikunja-tg-hook.