{"id":51973172,"url":"https://github.com/mrnko/velotor-ride","last_synced_at":"2026-07-30T01:30:32.596Z","repository":{"id":371174674,"uuid":"689752354","full_name":"mrnko/velotor-ride","owner":"mrnko","description":"Telegram bot for tracking cycling club statistics, ride distances, and weekly leaderboards. Built with pure PHP and Telegram Bot API for the «VeloTor» community.","archived":false,"fork":false,"pushed_at":"2026-07-13T19:25:20.000Z","size":1900,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-07-13T21:09:59.508Z","etag":null,"topics":["php","telegram","telegrambot"],"latest_commit_sha":null,"homepage":"https://ride.velotor.com.ua","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mrnko.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-09-10T19:39:43.000Z","updated_at":"2026-07-13T19:25:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mrnko/velotor-ride","commit_stats":null,"previous_names":["mrnko/velotor-ride"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/mrnko/velotor-ride","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrnko%2Fvelotor-ride","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrnko%2Fvelotor-ride/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrnko%2Fvelotor-ride/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrnko%2Fvelotor-ride/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mrnko","download_url":"https://codeload.github.com/mrnko/velotor-ride/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrnko%2Fvelotor-ride/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36056276,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-29T02:00:04.910Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["php","telegram","telegrambot"],"created_at":"2026-07-30T01:30:27.748Z","updated_at":"2026-07-30T01:30:32.541Z","avatar_url":"https://github.com/mrnko.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Velotor Ride\n\nTelegram-бот + сайт статистики велоклубу: учасники надсилають кілометраж у\nчат, бот рахує рейтинги, тижневі та річні підсумки й внутрішню валюту\nTorcoins; сайт показує поточний тиждень, архів, річні рейтинги та профілі\nучасників.\n\nЦе новий проєкт з чистою архітектурою. Старий PHP-проєкт з тією ж назвою\nвикористовувався лише як референс бізнес-логіки під час проєктування - код з\nнього не переносився.\n\n## Стек\n\nLaravel 11 (PHP) + Vue 3 + Inertia.js + Tailwind CSS v4 + MySQL. Повне\nобґрунтування — [STACK_DECISION.md](STACK_DECISION.md). Опис архітектури —\n[PROJECT_PLAN.md](PROJECT_PLAN.md). Інструкції по деплою на VPS —\n[DEPLOYMENT.md](DEPLOYMENT.md).\n\n## Встановлення (локально)\n\nВимоги: PHP 8.2+, Composer, Node.js 18+/npm, MySQL.\n\n```bash\ncomposer install\nnpm install\n\ncp .env.example .env\nphp artisan key:generate\n```\n\n### Налаштування `.env`\n\nВідредагувати:\n\n```\nDB_DATABASE=velotor_ride\nDB_USERNAME=root\nDB_PASSWORD=\n\nVELOTOR_TIMEZONE=Europe/Kyiv\n\nTELEGRAM_BOT_TOKEN=       # токен від @BotFather\nTELEGRAM_WEBHOOK_SECRET=  # будь-який довгий випадковий рядок\nTELEGRAM_CHAT_ID=         # id чату клубу для тижневих звітів\n```\n\nСтворити базу даних (MySQL має вже бути запущений):\n\n```bash\nmysql -u root -e \"CREATE DATABASE velotor_ride CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\"\n```\n\n### Міграції та демо-дані\n\n```bash\nphp artisan migrate --seed\n```\n\nСидер створює: адмін-акаунт (`admin@velotor.ride` / `password` — **обов'язково\nзмінити пароль на проді**), базові налаштування, ~14 учасників і суцільний\nланцюжок тижнів за останні ~60 тижнів (з переходом через Новий рік) із\nвипадковими результатами — щоб інтерфейс одразу був наповнений даними.\n\n### Запуск локально\n\n```bash\ncomposer run dev\n```\n\nЦя команда одночасно піднімає `php artisan serve`, чергу (`queue:listen` —\nне використовується логікою проєкту, але йде в комплекті зі стандартним\nскриптом Laravel), логи (`pail`) і Vite dev-сервер. Сайт буде доступний на\n`http://localhost:8000`.\n\nЩоб локально перевірити закриття тижня без очікування понеділка, просто\nвикликайте команду вручну: `php artisan week:close`.\n\nЯкщо потрібен лише сайт без hot-reload:\n\n```bash\nnpm run build\nphp artisan serve\n```\n\n## Логіка тижнів і років\n\nТиждень клубу — окрема сутність `weekly_periods`, а не голий номер: у неї є\n`start_date`/`end_date` (понеділок 00:00 → наступний понеділок 00:00 за\n`VELOTOR_TIMEZONE`), і саме діапазон дат визначає \"поточний тиждень\", а не\nлічильник. Пара `(year, week_number)` унікальна; при переході через Новий рік\n`week_number` автоматично скидається на 1, а `year` — оновлюється, без\nручного втручання. Усі результати завжди прив'язані до `weekly_period_id`\n(FK) — це структурно виключає змішування даних різних років на стику тижня 1.\n\nЩопонеділка о 00:00 (`php artisan week:close`, через Laravel Scheduler)\nсистема: рахує підсумки активного тижня, формує топ-5, надсилає звіт у\nTelegram, закриває тиждень і відкриває наступний. Дія ідемпотентна: якщо\nscheduler випадково запуститься двічі поспіль, повторний виклик нічого не\nзробить (звіт не продублюється).\n\n## Torcoins\n\nTorcoins нараховуються пропорційно дистанції: 100 км = 1 Torcoin. Перший\nучасник із результатом у кожному новому тижні додатково отримує 0.1 Torcoin.\nБаланс рахується окремо за весь час і за поточний рік.\n\n## Telegram-бот\n\nРозпізнає повідомлення виду `результат 10`, `результат 10 км`, `результат\n10.5`, `результат 10,5`, `result 10`, `+10 км` (крапка або кома як\nрозділювач, одиниця виміру необов'язкова після ключового слова). Усі інші\nповідомлення в чаті ігноруються. Команди: `/start`, `/help`, `/me`, `/top`,\n`/week`, `/year`, `/alltime`.\n\n### Налаштування вебхука\n\nЛокально Telegram не може достукатись до `localhost` напряму — для\nрозробки використовуйте тунель (ngrok/Cloudflare Tunnel) і вкажіть публічну\nURL:\n\n```bash\ncurl -X POST \"https://api.telegram.org/bot\u003cTOKEN\u003e/setWebhook\" \\\n  -d \"url=https://\u003cваш-домен-або-тунель\u003e/telegram/webhook\" \\\n  -d \"secret_token=\u003cTELEGRAM_WEBHOOK_SECRET\u003e\"\n```\n\nДля продакшену — див. [DEPLOYMENT.md](DEPLOYMENT.md).\n\n### Налаштування cron\n\n```bash\n* * * * * cd /path/to/project \u0026\u0026 php artisan schedule:run \u003e\u003e /dev/null 2\u003e\u00261\n```\n\nПеревірити, що команда закриття тижня запланована:\n\n```bash\nphp artisan schedule:list\n```\n\n## Адмінка\n\n`/admin/login` (сидер створює `admin@velotor.ride` / `password`). Дозволяє:\nпереглядати учасників і результати, редагувати/видаляти помилковий\nрезультат (з автоматичним перерахунком статистики тижня), запускати повний\nперерахунок статистики, дивитись логи Telegram-бота, дивитись статус\nпоточного тижня і закривати його вручну, змінювати `telegram_chat_id` та\nчасовий пояс.\n\n## Тести\n\n```bash\nphp artisan test\n```\n\n83 тести (unit + feature) покривають: розпізнавання всіх форматів\nрезультату, захист від дублів, розрахунок Torcoins, логіку тижнів/років\n(включно з переходом через Новий рік), ідемпотентність закриття тижня,\nTelegram-вебхук (усі команди, некоректні payload, перевірку секретного\nтокена), публічні сторінки сайту та адмінку.\n\n## Як перевірити, що бот працює\n\n1. Написати боту `/start` у чаті — має відповісти коротким описом.\n2. Написати `результат 15 км` — бот має відповісти підсумком (км за\n   тиждень/усього/Torcoins) і зберегти результат.\n3. Написати щось не по темі — бот не повинен відповідати взагалі.\n4. `/me`, `/top`, `/week`, `/year`, `/alltime` — мають відповідати коротким\n   рейтингом/статистикою.\n5. `/admin/bot-logs` — кожне повідомлення має з'явитись у логах зі\n   статусом `ok`/`ignored`/`error`.\n\n## Як перевірити закриття тижня\n\n```bash\nphp artisan week:close\n```\n\nПерший запуск закриває активний тиждень (якщо він уже закінчився, або\nзавжди — через адмінку з примусовим закриттям), надсилає звіт у\n`TELEGRAM_CHAT_ID` і створює наступний тиждень. Повторний виклик одразу\nпісля цього нічого не робить — це і є ідемпотентність, покрита тестом\n`WeeklyCloseActionTest`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrnko%2Fvelotor-ride","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmrnko%2Fvelotor-ride","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrnko%2Fvelotor-ride/lists"}