HomeBot — Telegram bot for schedule and Steam activity
  • Python 96.7%
  • Shell 3.2%
  • Dockerfile 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
lordkam 09fd82cea6
All checks were successful
HomeBot CI / quality (push) Successful in 47s
HomeBot CI / deploy (push) Successful in 23s
Merge pull request 'Preserve full details in Rich status tables' (#3) from codex/rich-message-detail into main
2026-09-19 17:42:13 +00:00
.forgejo/workflows ci: keep deploy credential scoped to runner host 2026-09-19 13:35:09 +03:00
assets fix(banner): знак Steam — точный оригинальный контур (маска-ассет) 2026-07-17 18:23:32 +03:00
db Improve Steam announcement audit and rich status 2026-09-19 18:15:51 +03:00
deploy fix: keep SQLite path stable across releases 2026-09-19 13:41:55 +03:00
docs ci: add Forgejo CI and safe release deployment 2026-09-19 13:13:31 +03:00
handlers Preserve details in rich status tables 2026-09-19 20:39:09 +03:00
keyboards feat(calendar): durable drafts and private Telegram panels 2026-09-07 12:14:38 +03:00
middlewares refactor(state): типизированный app_state — db/state.py вместо сырых ключей 2026-07-13 13:55:43 +03:00
scripts fix(calendar): track explicit schedule coverage 2026-08-27 16:29:14 +03:00
services Improve Steam announcement audit and rich status 2026-09-19 18:15:51 +03:00
tests Preserve details in rich status tables 2026-09-19 20:39:09 +03:00
utils Improve Steam announcement audit and rich status 2026-09-19 18:15:51 +03:00
.dockerignore ci: add Forgejo CI and safe release deployment 2026-09-19 13:13:31 +03:00
.env.example feat(steam): отслеживание вишлиста — анонсы, «купил из вишлиста», кооп-зов 2026-07-13 11:32:45 +03:00
.gitignore ci: add Forgejo CI and safe release deployment 2026-09-19 13:13:31 +03:00
.gitlab-ci.yml feat(steam): надежный monotonic-трекинг и диагностика 2026-08-25 02:54:59 +03:00
bot.py ci: add Forgejo CI and safe release deployment 2026-09-19 13:13:31 +03:00
config.py feat(steam): надежный monotonic-трекинг и диагностика 2026-08-25 02:54:59 +03:00
docker-compose.yml HomeBot: график «кто дома» + Steam-трекер (первый релиз) 2026-07-11 22:07:25 +03:00
Dockerfile HomeBot: график «кто дома» + Steam-трекер (первый релиз) 2026-07-11 22:07:25 +03:00
LICENSE HomeBot: график «кто дома» + Steam-трекер (первый релиз) 2026-07-11 22:07:25 +03:00
pytest.ini HomeBot: график «кто дома» + Steam-трекер (первый релиз) 2026-07-11 22:07:25 +03:00
README.md Improve Steam announcement audit and rich status 2026-09-19 18:15:51 +03:00
requirements-dev.txt feat(steam): надежный monotonic-трекинг и диагностика 2026-08-25 02:54:59 +03:00
requirements.txt fix(steam): deliver empty reports and support Bot API 10.3 2026-08-28 14:30:48 +03:00
SECURITY.md ci: keep deploy credential scoped to runner host 2026-09-19 13:35:09 +03:00

HomeBot

Телеграм-бот (aiogram 3) для группы друзей: каждый отмечает свои рабочие дни в интерактивном календаре, а бот показывает, кто сегодня дома и когда лучше всего собраться. Опционально — модуль слежки за Steam-профилями (кто что купил и сколько наиграл), красивыми сводками.

Хранение — SQLite (после перезапуска ничего не теряется), лёгкий, без внешних сервисов. Работает на скромном железе (проверено на LXC с 256 МБ RAM).

Модульность: оба блока независимы и включаются флагами в .env (ENABLE_WORKCAL, ENABLE_STEAM). Нужен только график — выключи Steam; нужен только Steam-трекер — выключи график. По умолчанию включены оба.


Возможности

График / кто дома:

  • 📅 /my — личный интерактивный календарь. Клик по дате ставит отметку; кнопка «🔁 Отмечаю» переключает тип: 🔴 рабочий день или 🟩 отпуск. Черновик хранится в SQLite, поэтому не теряется при перезапуске; « Готово» публикует изменения в общий график.
  • 🏠 /home — кто сегодня дома, кто работает. Выводится картинкой: аватарки (или цветной круг с инициалом, если фото скрыто), статусы «дома/отпуск/на работе».
  • 🗓 /collect [N] — сводка на N дней (по умолчанию 7). Тоже картинкой: сетка «люди × дни» с подсветкой лучшего дня (при >12 участниках или >14 днях — автоматически текстом).
  • 👤 /who @ник — чужой график (просмотр).
  • 📊 /stats — статистика за месяц: кто чаще всех дома.
  • 🧩 /shift — расставить смены по шаблону (2/2, 1/3, будни…) на 90 дней вперёд.
  • ✏️ /rename, 🛑 /cancel, /help.

Каждое утро (по умолчанию 09:00, AUTOPOST_HOUR) бот автопостит в группу «кто сегодня дома» — той же картинкой, что и /home. В сводку попадают только те, чей график явно подтверждён на ближайшие 7 дней; остальным напоминает обновить график (пинги — в подписи под картинкой).

Для владельца бота (см. «Доступ» ниже — только владелец):

  • 🩺 /status — глобальный статус: какие модули включены, все настройки и интервалы, мини-сводка последних Steam-проверок.
  • ⚙️ /settings — тумблеры: утренний автопост, доступ к /stats, сводки картинками (выключил — /home//collect/автопост снова текстом).
  • 🗂 /backup — бэкап базы в личку (+ авто раз в сутки, BACKUP_HOUR).
  • 📥 /backupsub / /backupunsub / /backupwho — управление получателями авто-бэкапа (без поиска числовых id). Пусто → бэкап уходит владельцу.
  • 🚫 /forget @ник — убрать ушедшего из графиков.
  • 🆔 /id — узнать chat id и свой user id (напр. для .env).

База в бэкапе содержит Telegram id, имена, графики и Steam-данные. Получатели — только явные (BACKUP_ADMIN_IDS//backupsub) или владелец; «всем админам чата» бот её не рассылает.

Steam-модуль (опционально):

  • 🆕 Впервые обнаруженные ботом игры — карточкой с обложкой сразу («купил игру» / «добавил игру» — различает платные и free-to-play, показывает цену и теги Онлайн/Кооп/Одиночная).
  • 🎁 Вишлист — анонс, когда кто-то добавляет игру в желаемое («X добавил в вишлист …»); если её хочет ещё кто-то из списка — подсказывает, кто. На карточке покупки: метка « из вишлиста», у кого она в вишлисте, у кого уже куплена, и для не-одиночных — зов «🤝 может, вместе поиграете?». Игры 18+ помечаются 🔞 (по данным стора).
  • 📊 Наигранное — красивой картинкой-сводкой: каждый день в 21:00 и по воскресеньям за неделю (аватары, иконки игр, время по каждой игре).
  • Шлёт в один заданный чат (личка или группа) — куда включишь командой /steamhere.

Требования

  • Python 3.10+ (цветные кнопки Bot API 9.4 требуют свежий aiogram).
  • Токен бота от @BotFather.
  • (для Steam-модуля) ключ Steam Web API и SteamID64 отслеживаемых профилей.

Быстрый старт

git clone <URL-репозитория> workcal-bot && cd workcal-bot

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

cp .env.example .env      # заполни значения (см. ниже)
python bot.py

Разработка и выкладка

  • Проверки запускаются командой python -m pytest -q. Для CI дополнительно используются compileall, pip check, pip-audit, Ruff, Bandit и Mypy.
  • Рабочая production-ветка — main. Изменения попадают туда только через pull request с успешным CI. Экспериментальная Telegram Mini App остаётся в отдельной ветке miniapp_experiment и не разворачивается вместе с ботом.
  • Production работает нативно через systemd, не через Docker. Docker-файлы оставлены только для локальной упаковки; актуальная схема выкладки и отката — в docs/deployment.md.

Настройка (.env)

Все секреты и параметры — в .env (он в .gitignore, в репозиторий не попадает). Скопируй .env.example → .env и заполни:

Переменная Обяз. Описание / где взять
BOT_TOKEN Токен бота. @BotFather → /newbot.
ENABLE_WORKCAL Модуль графика/«кто дома» (true/false, по умолчанию true).
ENABLE_STEAM Модуль Steam-трекера (true/false, по умолчанию true).
TIMEZONE Часовой пояс, напр. Europe/Moscow. Влияет на «сегодня» и время автопостов.
DB_PATH Путь к SQLite-базе (создаётся сама). По умолчанию workcal.db.
BACKUP_HOUR Час авто-бэкапа (023).
AUTOPOST_HOUR Час утреннего автопоста «кто дома» (023).
OWNER_IDS Владелец(ы) бота — Telegram id через запятую. Только им админ/Steam-команды. Пусто → TOFU (см. ниже).
BACKUP_ADMIN_IDS Кому слать авто-бэкап — Telegram id через запятую. Пусто → /backupsub, иначе владельцу.
ALLOWED_CHAT_ID Рабочая группа (её chat id, обычно -100…). Задан → в других группах бот молчит. Пусто → закрепится, когда владелец добавит бота в группу.
STEAM_API_KEY для Steam Ключ Steam Web API: https://steamcommunity.com/dev/apikey
STEAM_ACCOUNTS для Steam SteamID64 через запятую (см. ниже).

Если STEAM_API_KEY и STEAM_ACCOUNTS пустые — Steam-модуль просто выключен, а бот-график работает как обычно.

Куда прописывать Steam-аккаунты и ключ

  1. Ключ API → в .env, переменная STEAM_API_KEY. Получить: https://steamcommunity.com/dev/apikey (нужен аккаунт Steam; в поле «домен» можно указать любой).

  2. Аккаунты → в .env, переменная STEAM_ACCOUNTS — список через запятую. Каждый элемент — один из форматов (можно смешивать):

    • SteamID64 — 17-значное число: 76561198000000001;
    • логин (vanity) — короткое имя из ссылки .../id/: gabelogannewell;
    • ссылка на профильhttps://steamcommunity.com/id/gabelogannewell или https://steamcommunity.com/profiles/76561198000000001.

    Логины и ссылки бот сам разрезолвит в SteamID64 на старте. Опционально метка через двоеточие (ник всё равно берётся из Steam):

    STEAM_ACCOUNTS=76561198000000001,gabelogannewell:Гейб
    

    ⚠️ У отслеживаемых профилей «Игровые данные» должны быть публичными (Настройки приватности Steam), иначе API не отдаёт список игр, время и вишлист. У кого приватно — просто не попадёт в сводки/анонсы (тихо).

  3. Включить доставку: напиши боту /steamhere в том чате (личка или группа), куда должны приходить Steam-сводки.

Команды Steam-модуля

Команда Что делает
/steamhere Слать Steam-сводки в этот чат (личка или группа).
/steamoff Выключить доставку сводок.
/steamsettings Тумблеры (новые игры / вишлист / ежедневная / недельная), период опроса и время сводок.
/steamstatus Статус: какие аккаунты отслеживаются, последние проверки (успех/ошибка), приватность.
/steamdiag Диагностика приватности, пропавших игр и последнего monotonic-снимка.
/steamnow Проверить новые игры прямо сейчас.
/steamreport Превью дневной сводки (накопленное с последнего сброса; счётчик не трогает).

Расписание (настраивается в /steamsettings, по умолчанию):

  • 🆕 анонс новых игр — проверка каждые 15 мин (период меняется: 5120 мин);
  • 🎁 анонс вишлиста — проверка каждые 30 мин (период меняется: 5120 мин);
  • 📊 ежедневная сводка — 21:00;
  • 📅 недельная сводка — воскресенье 21:00.

Каждый из четырёх пунктов можно независимо выключить; периоды опроса и время/день сводок меняются кнопками. Время — по TIMEZONE бота. Дополнительный переключатель «В день недельной — только недельная» убирает двойную отправку: если недельная отключена, ежедневная снова приходит обычно.

Меню команд строится по включённым модулям: выключишь ENABLE_WORKCAL — команд графика в меню нет; выключишь ENABLE_STEAM — нет steam-команд. Плюс steam-команды показываются только в личке с ботом, в групповом меню их нет (чтобы посторонние не жали /steamhere); при этом набрать их вручную в группе по-прежнему можно.

Как считается игровое время

Бот не полагается на видимый статус Online/Invisible. При каждом опросе он сравнивает абсолютный счётчик Steam playtime_forever с максимальным ранее подтверждённым значением и накапливает только положительную разницу отдельно для дня и недели.

  • уменьшившийся или временно устаревший счётчик не создаёт повторные минуты;
  • исчезнувшая из API Private Game не удаляется из истории;
  • вернувшаяся игра сравнивается с прежним максимумом, а не считается новой;
  • Steam API не раскрывает дату покупки: анонс означает первое обнаружение игры ботом в библиотеке, а не подтверждённую дату получения лицензии;
  • покупка с нулевым временем не попадает в отчёт «кто играл»;
  • накопление очищается только после успешной доставки плановой сводки;
  • /steamreport показывает превью в том чате, где вызвана команда, и ничего не сбрасывает; плановые сводки по-прежнему идут в чат из /steamhere.
  • резкое сокращение библиотеки помечается как частичный ответ и не выдаётся за достоверное «не играл»; ограниченный журнал наблюдений помогает разбирать сбои;
  • если хеш иконки из GetOwnedGames устарел, бот пробует изображения Steam Store, проверяет содержимое и использует ограниченный положительный/отрицательный кэш.
  • аватары профилей кэшируются в SQLite, перепроверяются раз в сутки и при временном сбое Steam берутся из последней валидной копии.
  • перед анонсом новой игры Store-метаданные запрашиваются до трёх раз; без них неполная карточка не отправляется, а анонс безопасно повторяется следующим опросом. Через 30 минут ожидания отправляется базовая карточка с явным предупреждением, поэтому закрытое приложение не блокирует алерт навсегда. Успех кэшируется на 3 часа, неудача — только на 5 минут.

Статус Invisible сам по себе не скрывает playtime_forever. Если /steamdiag показывает исчезнувшие игры, обычно причина — Steam Private Game, приватность Game Details или временно неполный ответ API. Offline Mode может задержать синхронизацию времени до следующего подключения Steam.


Доступ (одна группа, один владелец)

Бот рассчитан на одну доверенную группу и одного владельца. Настройка — только в личке; в группу добавляют уже готового бота, команд в ней вводить не нужно.

  • Владелец — тот, кому доступны админ- и Steam-команды. Задаётся OWNER_IDS в .env (Telegram id через запятую) или, если пусто, назначается по TOFU: первый, кто напишет боту /start в личку, становится владельцем (запоминается в базе). Свой id — команда /id в личке.
  • Рабочая группа — задаётся ALLOWED_CHAT_ID в .env или закрепляется автоматически: когда владелец добавляет бота в группу, тот тихо запоминает её как рабочую (подтверждение приходит владельцу в личку, в саму группу бот ничего не пишет). После этого в любой другой группе бот молчит — «увести» его нельзя.
  • Обычные команды (/my, /home, /collect, …) — в рабочей группе всем участникам; в личке — только тем, кто уже зарегистрирован из группы. Посторонним в личке бот не отвечает. Участники регистрируются молча при первой команде — отдельный /start не обязателен.
  • Админ/Steam-команды — только владельцу.

Типичный порядок: владелец пишет боту /start в личку (становится владельцем и настраивает Steam/автопост), затем добавляет бота в группу — она закрепляется сама. env всегда главнее: если OWNER_IDS/ALLOWED_CHAT_ID заданы, TOFU и авто-закрепление не нужны.

Только Steam-модуль (ENABLE_WORKCAL=false): участникам делать ничего не нужно — бот лишь публикует Steam-сводки в заданный чат. Владельца в этом режиме удобнее задать через OWNER_IDS в .env.

Работа в группе

  1. У ботов в группах включён privacy mode — бот видит только команды (/…) и ответы на свои сообщения. Боту этого хватает.
  2. Каждый участник должен один раз написать боту (/start или любую команду), чтобы попасть в список.
  3. Для удаления чужих команд и авто-деактивации ушедших боту нужны права администратора группы (разрешение «Удаление сообщений»).

Авто-уборка чата

Чтобы не засорять чат, бот сам удаляет свои сообщения: быстрые ответы — через 5 минут, календарь после «Готово» — через ~30 сек, план /collect — до конца суток. Очередь удаления хранится в БД и переживает перезапуск (лимит Telegram — сообщения не старше 48 часов).


Деплой

Есть три варианта. Все скрипты берут адрес сервера из переменных окружения REMOTE (напр. user@host) и REMOTE_DIR.

1. Нативно (systemd + venv) — легковесно, рекомендуется

REMOTE=user@your-server ./deploy/deploy-native.sh

Ставит python3-venv, rsync, шрифт для баннеров, разворачивает venv, создаёт непривилегированного пользователя workcal, ставит systemd-сервис workcal-bot (бежит от workcal, не от root; с базовым hardening — ProtectSystem=strict, NoNewPrivileges, PrivateTmp и т.д.) и запускает. Юнит — deploy/workcal-bot.service.

2. Деплой одной командой git push (после нативного)

REMOTE=user@your-server ./deploy/setup-git-deploy.sh   # один раз на сервере
git remote add deploy user@your-server:/opt/workcal-bot.git
git push deploy main   # выкладывает код, ставит зависимости, перезапускает сервис

.env и база на сервере при этом не трогаются (они untracked).

Пуш защищён гейтом: сначала тесты нового кода гоняются на сервере во временном каталоге — при падении деплой отменяется и прод не трогается; после рестарта хук проверяет здоровье сервиса и при краше автоматически откатывает на предыдущий коммит. Плюс .gitlab-ci.yml запускает те же тесты на каждый пуш в GitLab (нужен настроенный Runner).

3. Docker

docker compose up -d --build          # локально, БД в ./data
REMOTE=user@your-server ./deploy.sh   # на сервер по SSH

.env переносится на сервер только если его там ещё нет — прод-секреты не затираются. Токен и ключи держи в .env, не коммить их.


Тесты

pip install -r requirements-dev.txt
pytest

Структура

bot.py                  — точка входа, polling, меню команд, фоновые задачи
config.py               — настройки из .env (в т.ч. STEAM_ACCOUNTS)
db/                     — схема, миграции, доступ к данным (SQLite)
db/state.py             — типизированные ключи app_state (repo.state.get/set)
keyboards/              — интерактивный календарь и date-picker
handlers/               — start, calendar, reports, shifts, admin, steam, core, errors
services/scheduling.py  — логика «кто дома», сбор, шаблоны смен
services/backup.py      — снимок БД и рассылка бэкапа
services/digest.py      — утренний автопост «кто дома»
services/home_*.py      — баннеры «кто дома»/«сбор» (Pillow) и сборка их данных
services/avatars.py     — аватарки Telegram с суточным кешем
services/steam*.py      — Steam API, трекер, рендер сводок-картинок (Pillow)
assets/                 — маска знака Steam для баннеров
utils/                  — авто-удаление, планировщик, даты, доступ, форматирование
middlewares/            — регистрация пользователей, ограничение по группе
deploy/                 — native/git/docker деплой, systemd-юнит, git-хук с гейтом
tests/                  — pytest

Лицензия

MIT — см. LICENSE.