Skip to content

Naz1anmak/OurMate

Repository files navigation

OurMate Bot

Этот бот предназначен для студенческих чатов и личного использования. Он объединяет LLM-ассистента, автоматические поздравления и работу с расписанием занятий.


📖 Описание

OurMate — это умный Telegram‑бот на базе aiogram и внешней LLM.

Работает на aiogram 3.26.0 и Telegram Bot API 9.5.

Поддерживает работу через HTTP/SOCKS5‑прокси для Telegram Bot API (Для macOS рекомендуется использовать Python, установленный через Homebrew, чтобы избежать проблем с сертификатами).

Он умеет:

  • Отвечать на упоминания и личные сообщения с контекстом предыдущих диалогов (LLM)
  • Ежедневно в заданное время проверять JSON-файл с днями рождения и поздравлять в группе
  • При запуске бота уведомлять владельца в личку о ближайшем дне рождения
  • Автоматически тянуть расписание через JSON-API, нормализовать тип занятия (Лекция/Практика/Лаб./Экзамен/Зачёт) и отдавать пары на сегодня/завтра командами или рассылкой
  • Уведомлять чат об изменениях расписания (добавлено/убрано/изменено) и о появлении новой сессии
  • Отвечать на вопросы о расписании на естественном языке и искать в интернете — через инструменты LLM (function calling), см. ниже
  • Ставить напоминания на естественном языке («напомни в пятницу в 18 про созвон»): в беседе — с кнопкой «Подписаться / Отписаться» для подписчиков; в ЛС — персонально
  • Предоставлять владельцу расширенные административные команды для контроля сервера и бота

📺 Для работы с LLM используется прямой ключ DeepSeek (endpoint https://api.deepseek.com/v1/chat/completions).


⚙️ Главные фишки

1. Интерактивный чат

  • Обрабатывает упоминания и личные сообщения: chat.py роутит в chat_pm.py / chat_group.py, команды вынесены в chat_commands.py, стрим-логика в llm_flow.py
  • Запоминает три последние пары вопрос–ответ (в ЛС — бессрочно, в группе — 24 часа); контекст не путается между разными чатами, в группе — общий для всех участников
  • Использует внешнюю LLM для генерации ответов, с вежливым фолбэком и алертом владельцу при сбоях
  • Стримит ответы с плейсхолдером в группах всегда, с троттлингом редактирования (группы ~1.8s/140 символов, ЛС ~0.6s/50) чтобы не ловить flood control; при retry_after ждёт и догружает

2. Планировщик поздравлений

  • Читает data/birthdays.json и каждый день в заданное время шлёт поздравление в беседу (только если сегодня есть именинники); после поздравления пишет владельцу, кто следующий именинник
  • Исключает дубли в один день через кеш-файл
  • Поздравляет именинников по шаблону из .env
  • При запуске бота присылает владельцу уведомление о ближайшем дне рождения

3. Расписание пар (автообновление через JSON-API)

  • Тянет расписание из публичного JSON-API (/api/v1/ruz/scheduler/<group_id>?date=YYYY-MM-DD), сохраняет в data/<code>/schedule.json. Поддерживает несколько групп — по подпапке на каждую
  • Перед каждой утренней рассылкой и ночным обновлением закрепа бот тянет свежие данные на текущую + N будущих недель (по умолчанию 3); сетевые ошибки 5xx/таймауты — один retry, при полном провале остаётся прошлый снимок
  • Diff: после обновления, если расписание изменилось — бот шлёт в чат сообщение со списком 🆕 новых / ✅ заменивших слот / ❌ удалённых / ⏰ перенесённых по времени / ✏️ изменённых (место/тип) пар по датам, обёрнутое в цитату (с подписью «для <code>» когда групп >1)
  • В multi-group режиме одинаковые пары двух групп выводятся одним блоком, а различающиеся — двумя ❗️ ... для <имя группы> блоками подряд
  • Команды «пары» / «пары завтра» (в группе, при упоминании бота или ответе; в ЛС — для владельца и пользователей из списка дней рождения); перед ответом — lazy-refresh с TTL (по умолчанию 60 мин), чтобы не дёргать API на каждый запрос
  • «обнови расписание» — ручное принудительное обновление: показывает diff либо «расписание не изменилось», при недоступности API — гиперссылку на frontend и время прошлого снимка
  • Если на выбранный день пар нет, бот показывает ближайшие будущие пары
  • Ежедневная рассылка пар на сегодня в 08:00, только если пары есть (можно отключить флагом)
  • Закреп: при включении бот держит закреплённое сообщение со списком следующих PINNED_SCHEDULE_DAYS_AHEAD учебных дней (по умолчанию 7)

4. Расписание в закрепе

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

5. Инструменты LLM (function calling)

  • В обычном диалоге модель сама решает — ответить текстом или вызвать функцию (родной паттерн tool use поверх OpenAI-совместимого API). Каркас тулов — src/bot/services/llm_tools.py (ToolRegistry / run_tool_loop), стриминг с тул-вызовами — llm_service.py / llm_flow.py
  • Расписание на естественном языке: «что у нас в пятницу?», «во сколько последняя пара?» — tool get_schedule(date_from, date_to); поиск занятий по предмету (прошлые и будущие) — find_classes_by_subject(subject) (schedule_tools.py). Текущая дата/таймзона инжектятся system-сообщением, поэтому «завтра» / «в субботу» резолвятся в конкретные даты
  • Веб-поиск: tool web_search(query) ходит в интернет не сам, а через сторонний поисковый сервис Tavily — ключ в .env. Никакая LLM в сеть напрямую не ходит
  • Доступ к тулам расписания гейтится так же, как команды (в группе разрешён refresh+diff, в ЛС — нет)
  • LLM-движок — тоже внешний сервис: DeepSeek (endpoint https://api.deepseek.com/v1/chat/completions)

6. Напоминания

  • Создаются и управляются на естественном языке через LLM-тулы: «напомни в пятницу в 18 про созвон», «покажи мои напоминания», «отмени напоминание про созвон»
  • Четыре тула: create_reminder / list_reminders / update_reminder / cancel_reminder (src/bot/services/reminder_tools.py)
  • Команда напоминания — детерминированный компактный список активных напоминаний; доступна всем (как help/команды)
  • В беседе: бот постит карточку напоминания с единой кнопкой «Подписаться / Отписаться» — любой участник тогглит личную подписку, результат приходит персональным всплывающим уведомлением, а в карточке обновляется счётчик участников. Автор сразу подписан на своё напоминание (счётчик стартует с 1). В момент срабатывания все подписчики упоминаются в ответном сообщении
  • В личных сообщениях: персональное напоминание с шагом подтверждения; доступно всем пользователям, которые писали боту
  • Хранение в SQLite (data/reminders.db); база переживает рестарт контейнера
  • При старте бота пропущенные напоминания (просроченные не более чем на REMINDER_MISFIRE_HOURS часов) досылаются с пометкой «опоздало»; более старые молча помечаются выполненными
  • При старте же чистятся завершённые/отменённые/неподтверждённые записи старше REMINDER_RETENTION_DAYS дней (активные не трогаются)

Переменные окружения:

Переменная По умолчанию Описание
REMINDER_DB_PATH data/reminders.db Путь к SQLite-базе напоминаний
REMINDER_MISFIRE_HOURS 24 Окно (часов) для досылки просроченного напоминания при рестарте; старше — молча закрываются
REMINDER_RETENTION_DAYS 7 Срок хранения завершённых/отменённых/черновых записей; чистка при старте

7. Пинг-лист (список для уведомлений)

  • Opt-in список на беседу: люди сами вступают и могут позвать друг друга, когда в Telegram нет встроенного «уведомить подписавшихся»
  • Команда пинг (в беседе) постит панель со счётчиком участников и кнопками Вступить / Выйти (зелёная/красная через style); нажатие тоглит участие и обновляет счётчик персональным всплывающим уведомлением — никого не пингует
  • Триггер @all (обычным сообщением, без упоминания бота) зовёт всех из списка — упоминания через tg://user?id= (работает и без username), длинные списки бьются на батчи
  • Кулдаун на @all — 5 минут на беседу (в памяти), пустой список — без кулдауна
  • Выбывшие из беседы убираются из списка автоматически (апдейты chat_member + бэкап на сервисное сообщение о выходе)
  • Хранение в SQLite (data/ping.db), отдельный список на каждую беседу
  • Требует у бота выключенный Group Privacy (чтобы видеть @all без упоминания) и права админа беседы (чтобы получать апдейты участников)

Переменные окружения:

Переменная По умолчанию Описание
PING_DB_PATH data/ping.db Путь к SQLite-базе пинг-листа
PING_COOLDOWN_SECONDS 300 Кулдаун на @all в секундах (на беседу)

8. Гибкая конфигурация через .env

  • Все токены и ключи хранятся в одном файле
  • Промпты для чата и поздравлений можно менять без правки кода
  • Установка времени для уведомлений
  • Поддержка прокси для Telegram Bot API

🎯 Особенности

  • Логирование — единый формат %(asctime)s [%(levelname)-8s] %(name)s: %(message)s для всех строк (наш код + aiogram + httpx). Уровень настраивается переменной LOG_LEVEL (по умолчанию INFO).
  • Контекст — запоминает 3 пары диалога (в ЛС бессрочно, в группах 24 часа)
  • Премиум-эмодзи — описываются в src/core/emoji.py (класс E); PremiumEmojiMiddleware на исходящих сообщениях автоматически заменяет unicode на <tg-emoji> теги. В хэндлерах пишите обычный текст с эмодзи — middleware сам подставит ID.
  • Уведомления владельца — алерты о новых /start и о недоступности LLM
  • Безопасность — проверки прав доступа для команд владельца

🔧 Команды

Группы доступа:

  • 🌍 все;
  • 🗿 владелец;
  • 🎓 участники в беседе;
  • 🎂 пользователи из birthdays.json;
  • 🔒 только ЛС.
Команда Кто может воспользоваться Описание
help / команды 🌍 Справка по командам бота
отписаться 🔒 🎂 🗿 Отключить поздравления (в ЛС, для пользователей из списка)
др 🎓 🎂 🗿 Ближайший день рождения
др 12345 / др @username 🎓 🎂 🗿 Дата дня рождения по id или username
пары 🎓 🎂 🗿 Пары на сегодня
пары завтра 🎓 🎂 🗿 Пары на завтра
обнови расписание 🎓 🗿 Принудительное обновление расписания и закрепа (в беседе; в ЛС — только владелец)
пинг 🎓 🗿 Панель списка для уведомлений (вступить/выйти кнопками); только в беседе
@all 🎓 Позвать всех из списка (в беседе, без упоминания бота)
напоминания 🌍 Список активных напоминаний
logs 🗿 Краткие логи бота (только строки PM/GR/FP)
full logs 🗿 Последние 200 строк лога целиком
проверка ссылок 🗿 Диагностика ссылок и активации пользователей

Команды stop bot / status / system удалены после переезда на Docker — для остановки и статуса используйте make stop / make ps / make tail на сервере.

  • В беседе все команды (включая help/команды) работают только при упоминании бота или ответе на его сообщение — без этого бот молчит.
  • В ЛС команды, кроме help/команды, доступны только владельцу и пользователям из data/birthdays.json.
  • Чтобы получать поздравления, активируйте бота в ЛС (напишите любое сообщение).
  • Для повторной активации после отписки снова напишите боту любое сообщение.

📁 Структура проекта

OurMate_bot/
├── src/                                     # Основной код приложения
│   ├── bot/                                 # Логика Telegram бота
│   │   ├── setup.py                         # Сборка Bot/Dispatcher + middleware
│   │   ├── handlers/                        # Обработчики сообщений
│   │   │   ├── chat.py                      # Роутер: триггер-гейт + dispatch PM/GR
│   │   │   ├── access.py                    # Единый гейтинг доступа (classify/resolve)
│   │   │   ├── chat_pm.py / chat_group.py   # Обработка ЛС / групп (стрим + фолбэк)
│   │   │   ├── chat_commands.py             # Публичные команды (др/пары/обнови расписание)
│   │   │   ├── owner_commands.py            # Команды владельца (logs/проверка ссылок)
│   │   │   ├── llm_flow.py                  # Стриминг LLM-ответов + рендер (тул-флоу)
│   │   │   └── …                            # chat_context, commands, errors, placeholder_variants
│   │   ├── middlewares/                     # Middleware aiogram
│   │   │   └── emoji.py                     # PremiumEmojiMiddleware: unicode → <tg-emoji>
│   │   ├── services/                        # Бизнес-логика
│   │   │   ├── llm_service.py               # LLM API + стрим с тулами
│   │   │   ├── llm_tools.py                 # Каркас function calling (ToolRegistry/run_tool_loop)
│   │   │   ├── schedule_tools.py            # Тулы расписания (get_schedule, find_classes_by_subject)
│   │   │   ├── web_search_tool.py           # Тул web_search через сторонний Tavily
│   │   │   ├── reminder_tools.py            # Тулы напоминаний (create/list/update/cancel)
│   │   │   ├── reminder_service.py / reminder_store.py  # Бизнес-логика и SQLite-хранилище
│   │   │   ├── schedule_*.py                # Пайплайн расписания: schedule_client/schedule_parser/diff/refresher/service
│   │   │   └── *_service.py                 # birthday / context / system
│   │   └── scheduler/                       # Cron-задачи: поздравления, рассылка/закреп/автообновление расписания, напоминания
│   ├── core/                                # Доменное ядро (emoji.py — класс E: unicode + premium_id)
│   ├── models/                              # Модели данных (user.py)
│   ├── utils/                               # Хелперы: логи, даты, HTML-рендер, кеш get_me, текст
│   └── config/                              # settings.py — настройки из .env
├── data/                                    # Данные приложения (не в git)
│   ├── birthdays.json                       # Дни рождения
│   ├── reminders.db                         # SQLite-база напоминаний (путь задаётся REMINDER_DB_PATH)
│   ├── <CODE>/schedule.json                 # Снимок расписания из JSON-API (подпапка на группу)
│   └── cache/                               # Кеш: дедуп поздравлений, расписание, message_id закрепа
├── main.py                                  # Точка входа (тонкий entrypoint)
├── docker-compose.yml / Dockerfile          # Контейнер bot и сборка образа
├── Makefile                                 # Цели для разработки и эксплуатации
├── requirements.txt                         # Зависимости Python
├── .env.example                             # Шаблон переменных окружения (.env — реальный, не в git)
└── README.md                                # Документация

🚀 Установка и запуск

Бот запускается через Docker Compose + Makefile.

1. Клонировать репозиторий

git clone https://github.com/Naz1anmak/OurMate.git
cd OurMate

2. Подготовить .env

make env       # копирует .env.example → .env, если его нет

Затем открой .env и заполни обязательные значения: BOT_TOKEN, OWNER_CHAT_ID, CHAT_ID, LLM_API_KEY, прокси (если нужен), промпты PROMPT_TEMPLATE_*.

Список переменных с подсказками — в .env.example. Переменная LOG_LEVEL управляет уровнем логов (DEBUG/INFO/WARNING/ERROR, по умолчанию INFO).

3. Сборка и запуск через Docker Compose

make build     # собрать образ
make up        # запустить контейнер bot в фоне
make tail      # хвост логов с follow (Ctrl+C — выйти)

Все цели Make — make help:

Цель Действие
make help список целей
make env создать .env из .env.example
make up / make down запустить / остановить и удалить контейнеры
make stop остановить без удаления
make restart пересобрать и перезапустить
make build только пересобрать образ
make logs прицепиться ко всем логам
make tail последние 200 строк лога bot с follow
make ps список контейнеров compose
make sh войти в контейнер bot

4. Пример data/birthdays.json

Структура JSON-файла с пользователями:

{
  "users": [
     {
        "user_id": 123456789,
        "name": "Анастасия Ильинична",
        "last_name": "Алленова",
        "birthday": "30.12",
        "status": "active",
        "interacted_with_bot": false,
        "username": "example_user"
     },
     {
        "user_id": 987654321,
        "name": "Иван Петрович",
        "last_name": "Петров",
        "birthday": "10.7",
        "status": "-",
        "interacted_with_bot": false
     }
  ]
}

Поля:

  • user_id (int или null) — Telegram user_id для упоминаний и гиперссылок
  • name (str) — Имя и отчество пользователя
  • last_name (str) — Фамилия (используется для отчётности)
  • birthday (str) — Дата рождения в формате "D.M" или "DD.MM"
  • status (str) — Статус: "active" (действующий студент) или "-" (отчисленный)
  • interacted_with_bot (bool) — Активировал ли пользователь бота (по умолчанию false)
  • username (str, опционально) — Telegram username (обновляется автоматически при старте бота)

Важно об активации:

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

  1. Написать боту /start в личные сообщения
  2. После этого поле interacted_with_bot автоматически станет true
  3. Пользователь будет получать персональные поздравления от LLM в беседе

Если interacted_with_bot = false, бот не будет поздравлять этого пользователя в беседе.

Команда для отписки:

  • Пользователь может в любой момент отписаться от поздравлений, написав боту отписаться в ЛС
  • Для повторной активации нужно снова написать боту

5. Расписание (автообновление через JSON-API)

Бот сам тянет расписание из публичного JSON-API портала расписания — никаких ручных .ics не нужно. Подключение групп:

  1. В .env указать SCHEDULE_API_BASE_URL (домен портала расписания твоего вуза, со схемой https://) и SCHEDULE_API_FACULTY_ID (числовой ID факультета из URL).
  2. Для каждой группы добавить SCHEDULE_API_GROUP_<CODE>=<group_id>, где <CODE> — твой код подпапки (любой удобный, латиница/цифры), а <group_id> — числовой ID из URL страницы группы (между /groups/ и ?date=).
  3. Запустить бот — подпапка data/<CODE>/ создастся при первом обновлении и наполнится schedule.json.

Пример .env:

SCHEDULE_AUTO_UPDATE_ENABLED=true
SCHEDULE_API_BASE_URL=https://<домен-портала-расписания>
SCHEDULE_API_FACULTY_ID=125
SCHEDULE_API_GROUP_<CODE1>=<ID1>
SCHEDULE_API_GROUP_<CODE2>=<ID2>
SCHEDULE_API_WEEKS_AHEAD=3              # текущая + 3 будущих недели
SCHEDULE_API_HTTP_TIMEOUT=15            # секунд
SCHEDULE_API_LAZY_TTL_MIN=60            # TTL для lazy-refresh в командах

Когда обновляется.

  • Перед рассылкой (SCHEDULE_SEND_HOUR:MM) — force_refresh + при изменениях шлёт diff, затем пары на актуальный день: «Пары на сегодня», а если все сегодняшние пары уже прошли (напр. вечерняя рассылка) — «Пары на завтра» (как /пары и закреп). Если в актуальный день пар нет — рассылка молчит.
  • Перед обновлением закрепа (PINNED_SCHEDULE_UPDATE_HOUR:MM) — force_refresh + diff, затем рендер закрепа.
  • В командах «пары» / «пары завтра» — ensure_fresh с TTL: если последний снимок свежее SCHEDULE_API_LAZY_TTL_MIN, не дёргаем API.
  • По команде «обнови расписание» — принудительный force_refresh + обновление закрепа.

Что в diff. Сообщение «🗓️ Расписание обновилось» содержит блоки по датам (содержимое блока обёрнуто в <blockquote>, как в /пары и закрепе). Формат строки пары — <emoji> HH:MM–HH:MM · Тип, на следующей строке предмет жирным. Эмодзи:

  • 🆕 — новая пара (в этом слоте раньше ничего не было);
  • ✅ — добавленная пара, занявшая слот удалённой (замена предмета в то же время начала);
  • ❌ — удалённые;
  • ⏰ — изменилось только время (тот же предмет, тот же тип/место);
  • ✏️ — изменилось место и/или тип.

Если у нескольких групп на одну дату полностью совпадает состояние до и после, блок объединяется в один — и при полном покрытии всех известных групп суффикс «для …» опускается; иначе в заголовке появляется «для 40001 и 40002» (через _format_groups).

При появлении расписания «с нуля» (новая сессия после пустого периода) — короткий заголовок «🗓️ Появилось расписание!» без перечисления (содержимое всё равно придёт в утренней рассылке/закрепе).

Отображаемое имя группы. SCHEDULE_GROUP_NAME_PREFIX + код подпапки. Пример: при SCHEDULE_GROUP_NAME_PREFIX=prefix/ подпапка A отображается как prefix/A. Совпадающие пары двух групп показываются одним блоком; различающиеся — двумя блоками с заголовком ❗️ ... для <имя группы>.

Лимит закрепа. PINNED_SCHEDULE_DAYS_AHEAD (по умолчанию 7) ограничивает число ближайших учебных дней в закреплённом сообщении, чтобы оно не разрасталось при загрузке расписания на семестр.

Сеть недоступна? Если все недели для группы не загрузились — старый снимок остаётся (fetched_at не сдвигается). На команде «обнови расписание» бот ответит «⚠️ Расписание недоступно, остался прошлый снимок (от DD.MM HH:MM)» с гиперссылкой на frontend портала расписания. Утренняя рассылка и закреп продолжат работать с тем что есть.


📋 Logs

Маркеры в строках

Префиксы помогают быстро понимать источник события:

  • PM — Personal Message (личные сообщения)
  • GR — Group Reply (ответы в группе)
  • FP — First Ping (команды /start)

Формат и уровни

Формат задаётся src/utils/logging.py::configure_logging и одинаков для нашего кода и сторонних библиотек:

2026-05-14 12:34:56 [INFO    ] src.bot.handlers.commands: FP; От @user (Имя): /start

Уровень управляется переменной LOG_LEVEL (INFO по умолчанию). Для подробной диагностики стрим-логики удобно временно ставить LOG_LEVEL=DEBUG.

Куда пишутся

  • В файл data/logs/bot.log (RotatingFileHandler, до 10 МБ × 5 файлов). Это bind-volume, логи переживают рестарты контейнера.
  • Параллельно весь вывод идёт в stdout — make tail / make logs показывают ровно то же самое через docker-driver.

Команды владельца

  • logs — краткие строки с маркерами PM/GR/FP.
  • full logs — последние 200 строк лога целиком.

Обе обрезаются до 4000 символов Telegram-лимита.


🛡️ Безопасность

  • Команды владельца работают только для указанного OWNER_CHAT_ID
  • Команды владельца (logs, full logs, проверка ссылок) читают локальный файл логов / birthdays.json, не дёргают системные утилиты
  • Ограничение длины ответов (4000 символов)
  • Проверка типов чатов: команды владельца доступны в ЛС и группе только владельцу; публичные команды в ЛС — только для владельца и пользователей из birthdays.json

🔄 Развертывание на сервере

1. Установить Docker и Docker Compose (Ubuntu/Debian)

# Базовые пакеты и репозиторий Docker
sudo apt update && sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg git make

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

. /etc/os-release
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/$ID $VERSION_CODENAME stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Запустить и включить автостарт
sudo systemctl enable --now docker

# Проверка
docker --version
docker compose version

(Опционально) Разрешить запуск docker без sudo:

sudo usermod -aG docker $USER
newgrp docker        # или перелогиниться

2. Развернуть бота

git clone https://github.com/Naz1anmak/OurMate.git bot && cd bot
make env                 # создаст .env из .env.example
$EDITOR .env             # заполнить значения (BOT_TOKEN, OWNER_CHAT_ID, CHAT_ID, LLM_API_KEY, промпты)
mkdir -p data/logs && sudo chown -R 1000:1000 data   # ⚠️ см. ниже
make build && make up
make tail                # убедиться, что появилась строка «Бот запущен и готов к работе»

Права на data/. Внутри контейнера бот работает под пользователем app (UID 1000), а ./data монтируется как bind-volume с хоста. Если папка принадлежит root, бот не сможет создать data/logs/bot.log ([Errno 13] Permission denied: 'data/logs' в make tail) и не сможет обновлять data/birthdays.json. Один раз выполнить sudo chown -R 1000:1000 data решает проблему навсегда. Тот же фикс актуален, если ты впервые обновляешься со старой схемы (логи через systemd/journald) — папка data/logs/ появилась только в текущей версии.

Контейнер автоматически перезапускается при сбое (restart: unless-stopped в docker-compose.yml). Логи сервиса смотрите через make tail или make logs; они же дублируются в data/logs/bot.log (10 МБ × 5 файлов ротации), что переживает рестарт контейнера.

3. Обновление после изменений в репозитории

cd /root/bot
git pull
make restart             # пересборка образа + перезапуск
make tail

✅ Smoke-тест после деплоя

После make up запусти make tail и проверь, что появились строки:

… [INFO    ] __main__: Запуск бота...
… [INFO    ] src.bot.setup: Обработчики зарегистрированы
… [INFO    ] __main__: DeleteWebhook успешно выполнен
… [INFO    ] __main__: Планировщики запущены
… [INFO    ] __main__: Бот запущен и готов к работе

Затем напиши боту /start в ЛС — в логе должен появиться FP; От … /start, а в твоей личке владельца — уведомление о новом активации.

Если в make tail всплыло [WARNING ] root: Не удалось открыть data/logs/bot.log для логов: [Errno 13] Permission denied: 'data/logs' — папка data/ принадлежит не UID 1000. Выполните sudo chown -R 1000:1000 data и make restart.


About

Smart Telegram bot with LLM integration for student chats: birthdays, class schedules, and automated notifications

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages