uni-notify: Outlook + Teams → Telegram с LLM-фильтром

- Playwright держит открытыми веб-версии Outlook и Teams (постоянный профиль, Xvfb)
- watcher.js перехватывает уведомления клиента и изменения карточек в реальном времени
- pipeline: дедуп, baseline, VIP/MUTE, оценка важности через Haiku (OpenRouter)
- aiogram-бот: уведомления, дайджест, вопросы по журналу, /login с 2FA-кодом, ручное управление браузером
- Docker/compose, systemd-юнит, e2e-тест на поддельных страницах

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0194buWJjpDjU52JPX7PjsMs
This commit is contained in:
Claude
2026-11-13 14:20:14 +01:00
commit da28d0ac1c
22 changed files with 1844 additions and 0 deletions

94
README.md Normal file
View File

@@ -0,0 +1,94 @@
# uni-notify
Читает университетскую почту (Outlook) и Microsoft Teams через их веб-версии и присылает важное в Telegram. Ему можно задавать вопросы вроде «что препод писал про курсач?».
## Как это работает
```
Chromium (Playwright, постоянный профиль, Xvfb)
├─ вкладка Outlook ─┐ watcher.js ловит:
└─ вкладка Teams ──┤ 1) уведомления самого клиента (Notification / showNotification) — мгновенно
│ 2) изменения карточек в списках писем/чатов (MutationObserver) — страховка
▼
pipeline: дедуп → baseline → VIP/MUTE → LLM (Haiku через OpenRouter) → уровень 0–3
▼
Telegram-бот (aiogram): 🔴 срочно / 🟡 важно / дайджест / ответы на вопросы
```
- **Реалтайм.** Веб-клиенты Microsoft сами получают пуши. Бот держит их открытыми и перехватывает уведомления прямо в странице, поэтому задержка составляет секунды. Токены из браузера никуда не выносятся.
- **Сессия.** Профиль браузера хранится в `data/browser-profile`. Вход нужен один раз, дальше клиент сам обновляет токены. Если сессия всё-таки слетит, бот напишет «нужен /login».
- **2FA.** `/login` заполняет обычную форму входа Microsoft, а 6-значный код спрашивает у тебя в Telegram. Если включено сопоставление чисел, бот присылает число, которое нужно выбрать в Authenticator.
- **Нестандартный SSO университета.** Бот пришлёт скриншот. Дальше управляешь браузером через `/click X Y`, `/type`, `/key Enter`, `/screen`.
- **Хранилище.** SQLite с полнотекстовым поиском (`data/uni.db`). По нему бот отвечает на вопросы.
## Установка (Docker)
```bash
git clone https://git.azimkin.dev/Azimkin/uni-notify.git && cd uni-notify
cp .env.example .env && nano .env # токен бота, OWNER_ID, ключ OpenRouter, UNI_EMAIL
docker compose up -d --build
docker compose logs -f
```
Потом в Telegram: `/start`, затем `/login`, затем введи код из приложения.
Нужно примерно 1,5–2 ГБ RAM: Teams в браузере прожорлив. Вкладки перезагружаются раз в `RELOAD_HOURS`.
### Без Docker
```bash
sudo apt install -y xvfb
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt && playwright install --with-deps chromium
xvfb-run -a python -m app.main
```
systemd-юнит лежит в `deploy/uni-notify.service`.
## Первый запуск: что важно знать
1. **Baseline.** При самом первом старте всё, что уже лежит в списках, молча запоминается в течение 90 секунд. Старые письма не придут.
2. **Включи уведомления в веб-клиентах** (настройки хранятся в аккаунте, можно сделать со своего браузера):
- Outlook: Settings → General → Notifications → Desktop notifications → включить;
- Teams: Settings → Notifications → включить уведомления для чатов, упоминаний и активности.
Без этого бот всё равно работает через карточки списков, просто чуть менее точно.
3. **Проверка парсера:** `/debug mail` и `/debug teams` показывают, какие карточки видит бот. Если пусто, у Microsoft поменялась вёрстка: поправь `rowSelector` в `config/selectors.json` и перезапусти.
## Команды бота
| Команда | Что делает |
|---|---|
| любой текст | вопрос по журналу событий («какие дедлайны на неделе?») |
| `/status` | состояние сессий, статистика за сутки |
| `/recent [N]` | последние важные события |
| `/digest` | дайджест неважного прямо сейчас |
| `/login` | войти заново |
| `/vip слово`, `/mute слово`, `/lists` | всегда срочно / никогда / показать списки |
| `/screen [mail\|teams]` | скриншот вкладки |
| `/use`, `/click X Y`, `/type`, `/key`, `/goto` | ручное управление браузером |
| `/reload`, `/debug` | перезагрузка вкладки / что видит парсер |
## Настройка важности
- `config/rules.md`: правила на человеческом языке. LLM читает файл при каждой оценке, перезапуск не нужен.
- `.env`: `VIP`, `MUTE`, `NOTIFY_MIN_LEVEL`, `QUIET_HOURS`, `DIGEST_TIME`.
Уровни: **3** срочно (со звуком даже в тихие часы), **2** важно, **1** в дайджест, **0** игнор. Если LLM недоступна, бот присылает всё подряд: лучше лишнее, чем пропустить.
## Стоимость
Haiku через OpenRouter: одна пачка событий занимает примерно 1–2 тыс. токенов. При сотне событий в день выходит порядка $1–3 в месяц.
## Тест
```bash
CHROMIUM_PATH=/usr/bin/google-chrome python -m tests.test_e2e
```
Тест поднимает поддельные страницы Outlook и Teams и проверяет перехват уведомлений, дедуп, baseline, VIP/MUTE и устойчивость к «тикающему» времени в карточках.
## Риски
- Автоматизация веб-клиента может противоречить правилам ИТ университета. Худший реалистичный исход: блокировка сессии или вопросы от админов.
- Пароль, если указан в `.env`, хранится на сервере открытым текстом. Можно его не указывать: бот спросит пароль в Telegram при `/login` и сразу удалит сообщение. Пароль держится только в памяти.