https://github.com/mrnko/velotor-ride
Telegram bot for tracking cycling club statistics, ride distances, and weekly leaderboards. Built with pure PHP and Telegram Bot API for the «VeloTor» community.
https://github.com/mrnko/velotor-ride
php telegram telegrambot
Last synced: 8 days ago
JSON representation
Telegram bot for tracking cycling club statistics, ride distances, and weekly leaderboards. Built with pure PHP and Telegram Bot API for the «VeloTor» community.
- Host: GitHub
- URL: https://github.com/mrnko/velotor-ride
- Owner: mrnko
- Created: 2023-09-10T19:39:43.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2026-07-13T19:25:20.000Z (25 days ago)
- Last Synced: 2026-07-13T21:09:59.508Z (25 days ago)
- Topics: php, telegram, telegrambot
- Language: PHP
- Homepage: https://ride.velotor.com.ua
- Size: 1.81 MB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# Velotor Ride
Telegram-бот + сайт статистики велоклубу: учасники надсилають кілометраж у
чат, бот рахує рейтинги, тижневі та річні підсумки й внутрішню валюту
Torcoins; сайт показує поточний тиждень, архів, річні рейтинги та профілі
учасників.
Це новий проєкт з чистою архітектурою. Старий PHP-проєкт з тією ж назвою
використовувався лише як референс бізнес-логіки під час проєктування - код з
нього не переносився.
## Стек
Laravel 11 (PHP) + Vue 3 + Inertia.js + Tailwind CSS v4 + MySQL. Повне
обґрунтування — [STACK_DECISION.md](STACK_DECISION.md). Опис архітектури —
[PROJECT_PLAN.md](PROJECT_PLAN.md). Інструкції по деплою на VPS —
[DEPLOYMENT.md](DEPLOYMENT.md).
## Встановлення (локально)
Вимоги: PHP 8.2+, Composer, Node.js 18+/npm, MySQL.
```bash
composer install
npm install
cp .env.example .env
php artisan key:generate
```
### Налаштування `.env`
Відредагувати:
```
DB_DATABASE=velotor_ride
DB_USERNAME=root
DB_PASSWORD=
VELOTOR_TIMEZONE=Europe/Kyiv
TELEGRAM_BOT_TOKEN= # токен від @BotFather
TELEGRAM_WEBHOOK_SECRET= # будь-який довгий випадковий рядок
TELEGRAM_CHAT_ID= # id чату клубу для тижневих звітів
```
Створити базу даних (MySQL має вже бути запущений):
```bash
mysql -u root -e "CREATE DATABASE velotor_ride CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
```
### Міграції та демо-дані
```bash
php artisan migrate --seed
```
Сидер створює: адмін-акаунт (`admin@velotor.ride` / `password` — **обов'язково
змінити пароль на проді**), базові налаштування, ~14 учасників і суцільний
ланцюжок тижнів за останні ~60 тижнів (з переходом через Новий рік) із
випадковими результатами — щоб інтерфейс одразу був наповнений даними.
### Запуск локально
```bash
composer run dev
```
Ця команда одночасно піднімає `php artisan serve`, чергу (`queue:listen` —
не використовується логікою проєкту, але йде в комплекті зі стандартним
скриптом Laravel), логи (`pail`) і Vite dev-сервер. Сайт буде доступний на
`http://localhost:8000`.
Щоб локально перевірити закриття тижня без очікування понеділка, просто
викликайте команду вручну: `php artisan week:close`.
Якщо потрібен лише сайт без hot-reload:
```bash
npm run build
php artisan serve
```
## Логіка тижнів і років
Тиждень клубу — окрема сутність `weekly_periods`, а не голий номер: у неї є
`start_date`/`end_date` (понеділок 00:00 → наступний понеділок 00:00 за
`VELOTOR_TIMEZONE`), і саме діапазон дат визначає "поточний тиждень", а не
лічильник. Пара `(year, week_number)` унікальна; при переході через Новий рік
`week_number` автоматично скидається на 1, а `year` — оновлюється, без
ручного втручання. Усі результати завжди прив'язані до `weekly_period_id`
(FK) — це структурно виключає змішування даних різних років на стику тижня 1.
Щопонеділка о 00:00 (`php artisan week:close`, через Laravel Scheduler)
система: рахує підсумки активного тижня, формує топ-5, надсилає звіт у
Telegram, закриває тиждень і відкриває наступний. Дія ідемпотентна: якщо
scheduler випадково запуститься двічі поспіль, повторний виклик нічого не
зробить (звіт не продублюється).
## Torcoins
Torcoins нараховуються пропорційно дистанції: 100 км = 1 Torcoin. Перший
учасник із результатом у кожному новому тижні додатково отримує 0.1 Torcoin.
Баланс рахується окремо за весь час і за поточний рік.
## Telegram-бот
Розпізнає повідомлення виду `результат 10`, `результат 10 км`, `результат
10.5`, `результат 10,5`, `result 10`, `+10 км` (крапка або кома як
розділювач, одиниця виміру необов'язкова після ключового слова). Усі інші
повідомлення в чаті ігноруються. Команди: `/start`, `/help`, `/me`, `/top`,
`/week`, `/year`, `/alltime`.
### Налаштування вебхука
Локально Telegram не може достукатись до `localhost` напряму — для
розробки використовуйте тунель (ngrok/Cloudflare Tunnel) і вкажіть публічну
URL:
```bash
curl -X POST "https://api.telegram.org/bot/setWebhook" \
-d "url=https://<ваш-домен-або-тунель>/telegram/webhook" \
-d "secret_token="
```
Для продакшену — див. [DEPLOYMENT.md](DEPLOYMENT.md).
### Налаштування cron
```bash
* * * * * cd /path/to/project && php artisan schedule:run >> /dev/null 2>&1
```
Перевірити, що команда закриття тижня запланована:
```bash
php artisan schedule:list
```
## Адмінка
`/admin/login` (сидер створює `admin@velotor.ride` / `password`). Дозволяє:
переглядати учасників і результати, редагувати/видаляти помилковий
результат (з автоматичним перерахунком статистики тижня), запускати повний
перерахунок статистики, дивитись логи Telegram-бота, дивитись статус
поточного тижня і закривати його вручну, змінювати `telegram_chat_id` та
часовий пояс.
## Тести
```bash
php artisan test
```
83 тести (unit + feature) покривають: розпізнавання всіх форматів
результату, захист від дублів, розрахунок Torcoins, логіку тижнів/років
(включно з переходом через Новий рік), ідемпотентність закриття тижня,
Telegram-вебхук (усі команди, некоректні payload, перевірку секретного
токена), публічні сторінки сайту та адмінку.
## Як перевірити, що бот працює
1. Написати боту `/start` у чаті — має відповісти коротким описом.
2. Написати `результат 15 км` — бот має відповісти підсумком (км за
тиждень/усього/Torcoins) і зберегти результат.
3. Написати щось не по темі — бот не повинен відповідати взагалі.
4. `/me`, `/top`, `/week`, `/year`, `/alltime` — мають відповідати коротким
рейтингом/статистикою.
5. `/admin/bot-logs` — кожне повідомлення має з'явитись у логах зі
статусом `ok`/`ignored`/`error`.
## Як перевірити закриття тижня
```bash
php artisan week:close
```
Перший запуск закриває активний тиждень (якщо він уже закінчився, або
завжди — через адмінку з примусовим закриттям), надсилає звіт у
`TELEGRAM_CHAT_ID` і створює наступний тиждень. Повторний виклик одразу
після цього нічого не робить — це і є ідемпотентність, покрита тестом
`WeeklyCloseActionTest`.