{"id":50168105,"url":"https://github.com/slashgear/group-ride","last_synced_at":"2026-05-24T22:02:00.538Z","repository":{"id":360061613,"uuid":"1243913054","full_name":"Slashgear/group-ride","owner":"Slashgear","description":"Telegram \u0026 Discord bot to organise group cycling rides — forum threads per ride, Komoot/Strava/Garmin import, Bun, SQLite","archived":false,"fork":false,"pushed_at":"2026-05-24T20:31:41.000Z","size":233,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T21:23:21.091Z","etag":null,"topics":["bot","bun","cycling","discord","sqlite","telegram","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/Slashgear.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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":"2026-05-19T19:38:19.000Z","updated_at":"2026-05-24T20:28:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Slashgear/group-ride","commit_stats":null,"previous_names":["slashgear/group-ride"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/Slashgear/group-ride","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Slashgear%2Fgroup-ride","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Slashgear%2Fgroup-ride/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Slashgear%2Fgroup-ride/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Slashgear%2Fgroup-ride/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Slashgear","download_url":"https://codeload.github.com/Slashgear/group-ride/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Slashgear%2Fgroup-ride/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33452033,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-24T19:21:36.376Z","status":"ssl_error","status_checked_at":"2026-05-24T19:21:10.562Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["bot","bun","cycling","discord","sqlite","telegram","typescript"],"created_at":"2026-05-24T22:01:59.496Z","updated_at":"2026-05-24T22:02:00.525Z","avatar_url":"https://github.com/Slashgear.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Group Ride — Discord bot to organise group cycling rides\n\n## Context and problem\n\nA group of cycling friends communicates via a shared channel (WhatsApp, Signal…). When someone proposes a ride, everyone gets notified — including those who are not interested. The discussion that follows spams the entire group, and the relevant information ends up buried in the feed.\n\n**Group Ride** is a Discord bot that solves this problem:\n- a single notification in the announcement channel for each proposed ride\n- an isolated thread per ride in a Forum channel, visible to registered participants\n- the thread closes automatically 24 hours after the ride\n\n---\n\n## Platform: Discord\n\n| Criteria | Discord | Telegram | WhatsApp |\n|---|---|---|---|\n| Official bot API | ✅ Free | ✅ Free | ⚠️ Paid + business badge |\n| Threads per ride | ✅ Native (Forum Channels) | ✅ Native (Forum Topics) | ❌ |\n| Native form (modal) | ✅ Yes | ❌ Conversation only | ❌ |\n| Participant limit | ✅ None | ✅ None | ⚠️ 8 via API |\n| Integration effort | Low | Low | High |\n\nDiscord offers native **modals** for structured input, which gives a much cleaner ride creation experience than a step-by-step conversation.\n\n---\n\n## Discord architecture\n\n```\nDiscord Server\n│\n├── 📢 #announcements   → ride announcements only, no discussion\n├── 🗂️ #rides (Forum)   → one thread per ride\n│   ├── 💬 Ride May 25  → isolated discussion for registered members\n│   ├── 💬 Ride Jun 1   → isolated discussion for registered members\n│   └── ...\n└── ...\n```\n\nThe bot automatically creates a forum thread per ride and manages its full lifecycle.\n\n---\n\n## Roles\n\n| Role | Permissions |\n|---|---|\n| **Admin** | Manage server members (add, remove, promote) |\n| **Member** | Propose a ride, join/leave a ride, cancel a ride |\n\nAny member can propose a ride. The organiser is not a fixed role — it is simply the member who proposes.\n\n---\n\n## Ride form\n\nEach ride is described by:\n\n- 📅 Date\n- 🕐 Meeting time (optional)\n- 📍 Meeting point\n- 📏 Distance / D+ / D- (optional, set manually or auto-filled from import)\n- 💪 Estimated level (auto-filled from Komoot import)\n- 🔗 Link to the source platform (Komoot, Strava, Garmin)\n- 📝 Free notes\n\nThe form is displayed as the **starter message** of the ride thread and updated automatically on modification.\n\n---\n\n## Import from an external platform\n\nThe ride creation modal accepts an optional URL that pre-fills the form automatically:\n\n- **Komoot** — public access; extracts name, distance, D+/D-, level, and link\n- **Strava** — requires OAuth; only the link is saved, a warning is shown\n- **Garmin Connect** — courses are not publicly accessible; only the link is saved, a warning is shown\n\nIf extraction fails (private activity or unavailable), the bot warns the proposer and falls back to the data entered manually in the modal.\n\n---\n\n## Sequence diagram\n\n```mermaid\nsequenceDiagram\n    actor Admin\n    actor Proposer\n    participant Bot as Discord Bot\n    participant Channel as #announcements\n    participant Ride as Ride thread (Forum)\n    actor Member\n\n    Note over Admin,Member: Discord Server\n\n    Note over Admin,Channel: Server management — admins only\n\n    alt Add a member\n        Admin-\u003e\u003eChannel: Adds a member to the server\n        Channel-\u003e\u003eBot: Event - guildMemberAdd\n        Bot--\u003e\u003eChannel: Welcome message\n    else Remove a member\n        Admin-\u003e\u003eChannel: Removes a member from the server\n        Channel-\u003e\u003eBot: Event - guildMemberRemove\n        Bot--\u003e\u003eRide: Removes from all active rides\n    end\n\n    Note over Proposer,Member: Proposal — any member can propose\n\n    Proposer-\u003e\u003eBot: /newride\n    Bot--\u003e\u003eProposer: Opens a modal (date, meeting point, import URL, distance, notes)\n    Proposer-\u003e\u003eBot: Submits the modal\n\n    alt Import URL provided\n        Bot-\u003e\u003eBot: Extracts ride info from URL\n        alt Extraction succeeded\n            Bot--\u003e\u003eProposer: Summary with imported data (ephemeral)\n        else Extraction failed\n            Bot--\u003e\u003eProposer: Warning + summary with manual data (ephemeral)\n        end\n    else No import URL\n        Bot--\u003e\u003eProposer: Summary with manual data (ephemeral)\n    end\n\n    Proposer-\u003e\u003eBot: ✅ Create ride\n    Bot-\u003e\u003eChannel: Single notification in #announcements\n    Note over Channel: Announcement channel is for ride announcements only\n\n    Bot-\u003e\u003eRide: Creates a dedicated thread in the Forum channel\n    Note over Ride: Title, date, meeting point, level, distance, D+, GPX\n\n    Member-\u003e\u003eChannel: Clicks \"Join this ride 🚴\"\n    Bot-\u003e\u003eRide: Adds member to thread\n    Bot--\u003e\u003eMember: Confirmation (ephemeral)\n\n    alt Member joins from thread\n        Member-\u003e\u003eRide: Clicks 🚴 Join\n        Bot--\u003e\u003eMember: Confirmation (ephemeral)\n    else Member leaves\n        Member-\u003e\u003eRide: Clicks 🚪 Leave\n        Bot-\u003e\u003eRide: Removes member, notifies thread\n    else Edit ride\n        Proposer-\u003e\u003eRide: Clicks ✏️ Edit\n        Bot--\u003e\u003eProposer: Pre-filled modal\n        Proposer-\u003e\u003eBot: Submits changes\n        Bot-\u003e\u003eRide: Updates pinned message\n    else Cancel\n        Proposer-\u003e\u003eRide: Clicks ❌ Cancel\n        Bot-\u003e\u003eChannel: Cancellation notice\n        Bot-\u003e\u003eRide: Closes thread\n    else View participants\n        Member-\u003e\u003eRide: Clicks 👥 Participants\n        Bot--\u003e\u003eMember: List of members (ephemeral)\n    end\n\n    Note over Proposer,Ride: Reminders (if meeting time is set)\n    Bot-\u003e\u003eRide: Day-before reminder notification\n    Bot-\u003e\u003eRide: 1-hour-before reminder notification\n\n    Note over Proposer,Ride: After the ride (if not cancelled)\n    Bot-\u003e\u003eRide: Closes (archives) the thread\n```\n\n---\n\n## Business rules\n\n### Announcement channel\n- Reserved for ride announcements. No discussion takes place here.\n- On cancellation, the bot posts a notification there (justified exception: non-registered members also need to know).\n\n### Ride thread\n- Created automatically by the bot for each proposal.\n- The starter message is the source of truth for the ride details.\n- Any registered member can join, leave, edit, or cancel the ride via the action buttons.\n- If a meeting time is set, the bot sends a reminder the day before and 1 hour before.\n- The thread closes immediately on cancellation.\n- The thread becomes read-only 24 hours after the ride.\n\n### Member management\n- Only admins can add or remove members from the server.\n- If a member leaves or is removed, the bot automatically removes them from all active rides.\n\n---\n\n## Stack\n\n- **Runtime**: [Bun](https://bun.sh)\n- **Language**: TypeScript\n- **Bot framework**: [discord.js](https://discord.js.org) v14\n- **Database**: SQLite via `bun:sqlite` (default) or PostgreSQL via Bun's native SQL — set `DATABASE_URL` to use PostgreSQL\n- **Architecture**: Ports \u0026 Adapters — see [ARCHITECTURE.md](ARCHITECTURE.md) for diagrams and file map\n\n---\n\n## Open questions\n\n- **Member removal** — are other members registered for that member's active rides notified?\n- **OAuth for Strava / Garmin** — should an authentication flow be modelled to enable full data extraction for private activities?\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslashgear%2Fgroup-ride","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslashgear%2Fgroup-ride","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslashgear%2Fgroup-ride/lists"}