Telegram Mini App + bot for collecting participants, calculating package cost, and coordinating the final purchase group.
Raspil iFLY 2.0 turns a Telegram group into a lightweight buying pool for a shared iFLY time package. Users join from a Telegram Mini App, choose how many hours they can buy, see price and savings immediately, and the bot keeps the current split visible at the bottom of the chat.
When enough time is collected, the bot finalizes the split, sends private instructions, helps create a separate discussion group, and shows purchase details only inside that private group.
- Production Mini App: miniapp-lemon-beta.vercel.app
- Bot username:
@raspiliflybot - Interface language: Russian
- Primary runtime: Vercel Functions + Supabase Postgres
Public docs intentionally do not include bot tokens, group IDs, webhook secrets, Supabase project refs, or direct webhook URLs.
| Problem | What Raspil iFLY does |
|---|---|
| People coordinate iFLY package purchases manually in chat | Keeps one current split message at the bottom of the Telegram group |
| Users need to know price before joining | Calculates package price instantly in the Mini App |
| People can buy exact time or only a range | Supports Π’ΠΎΡΠ½ΠΎΠ΅ and ΠΠΈΠ°ΠΏΠ°Π·ΠΎΠ½ slider modes |
| Package closes when enough hours are collected | Finalizes automatically at 10+ total hours |
| Final purchase needs private account data | Shows iFLY account data only in the split discussion group |
| Multiple users may try to create separate discussion groups | First group claims the split; later attempts get the existing link |
| Rule | Value |
|---|---|
| Package size | 10 hours minimum |
| Participants limit | 4 people maximum |
| Package price | 10 500 BYN / 10h |
| Base hourly price | 1 050 BYN/h |
| Standard comparison price | 1 200 BYN/h |
| Helmet option | +50 BYN/h |
| Time validity | Purchased time can be flown within 6 months |
Savings are calculated only from package time price: (1 200 - 1 050) * hours. Helmet cost does not affect savings.
Users can create either:
- exact request:
4h; - range request:
1-2.5h.
The split finalizes when total max hours reaches at least 10h. Final allocation then converts all ranges into exact hours:
- Sum all minimum hours.
- Set target hours to
max(10, total minimum hours). - Add extra hours to range participants until target is reached.
- Store final exact hours on matched requests.
Example:
| Participant | Request | Final |
|---|---|---|
| User A | 1h |
1h |
| User B | 1-9h |
9h |
The package closes as 10h; final price is calculated for 10h, not 2h.
The Mini App is mobile-first and follows Telegram system theme.
| Screen | Purpose |
|---|---|
Π‘ΠΎΠ·Π΄Π°ΡΡ |
Create, edit, or leave a split request |
ΠΠΎΠΈ Π·Π°ΡΠ²ΠΊΠΈ |
Active requests and split history with pull-to-refresh |
ΠΡΠΎΡΠΈΠ»Ρ |
Total savings, split stats, bought hours, and private iFLY account data |
UX details:
- Telegram/system theme only; no visible theme toggle.
- Bottom tab bar with mobile-app navigation.
- Sticky CTA with always-visible price summary.
- Radix UI slider for exact/range hour selection.
- Telegram vertical swipe protection to avoid closing the Mini App while scrolling.
- Haptic feedback and lightweight toast states for Telegram clients that support them.
The bot keeps a single active split message at the bottom of the chat:
- deletes the old message;
- sends the updated message after user messages;
- shows participants and remaining hours;
- changes inline button by state:
- no requests:
Π‘ΠΎΠ·Π΄Π°ΡΡ ΡΠ°ΡΠΏΠΈΠ»; - active requests:
ΠΡΠΊΡΡΡΡ / ΠΠΏΠΈΡΠ°ΡΡΡΡ!.
- no requests:
When the package is collected:
- Pending requests become matched.
- A short split code is generated.
- The main group gets a compact success message with participant nicknames and hours.
- Participants receive private instructions.
- A separate message asks them to create a discussion group and add the bot.
The first user who adds the bot to a new group and makes it an admin claims the group for the split.
After admin rights are granted, the bot:
- finds the newest fresh split for that user within 24 hours;
- creates or reuses an invite link;
- stores the discussion group link on the matched split;
- renames the group to
Π Π°ΡΠΏΠΈΠ» iFLY (#CODE) Β· 10Ρ; - sends invites to participants who are not already in that group;
- posts the private purchase summary inside the group.
When a participant joins the discussion group, the bot reposts the summary at the bottom and deletes the old copy so the latest info stays visible.
If a different participant tries to create another group for the same latest split, the bot does not rebind the split. It sends the already claimed group link instead.
| Command | Context | Action |
|---|---|---|
/start |
private chat | Allows the bot to send private group links later |
/fake N |
main group | Creates fake pending test requests; /fake 0 clears fake requests |
/add CODE |
discussion group | Manually attaches a group to a collected split |
Custom emoji helper: send a custom emoji or sticker to the bot in private chat. It replies with emoji = custom_emoji_id pairs for reuse in bot messages.
flowchart LR
tgUser["Telegram user"] --> mainChat["Main Telegram group"]
mainChat --> botWebhook["Bot webhook"]
tgUser --> miniApp["Telegram Mini App"]
miniApp --> nextApi["Next.js API routes"]
botWebhook --> nextApi
nextApi --> supabase["Supabase Postgres"]
nextApi --> telegramApi["Telegram Bot API"]
telegramApi --> mainChat
telegramApi --> privateDm["Private DMs"]
telegramApi --> discussionGroup["Split discussion group"]
.
βββ miniapp/ # Production runtime: Next.js UI + API + Telegram webhook
β βββ app/ # App Router pages and API routes
β βββ lib/server/ifly.ts # Domain logic, Telegram API calls, Supabase access
β βββ public/ # Mini App icon and instruction images
β βββ vercel.json # Region and cron configuration
βββ supabase/migrations/ # Postgres schema migrations
βββ assets/ # Source images and repo branding
βββ README.md
Production source of truth is miniapp/.
| Table | Purpose |
|---|---|
ifly_chats |
Telegram chat state, sticky message id, discussion setup metadata |
ifly_launch_contexts |
Short-lived Mini App launch tokens bound to Telegram chats |
ifly_requests |
Pending, matched, and cancelled user requests |
ifly_user_profiles |
Private iFLY account data keyed by Telegram user id |
ifly_bot_config |
Optional bot-level configuration |
Discussion group state is stored in ifly_chats.sticky_text as compact JSON metadata:
pending_split_token;discussion_split_token;discussion_message_id;discussion_setup_claim_token;discussion_setup_claimed_at;discussion_setup_completed_at.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/request |
Full Mini App state |
POST |
/api/request |
Create or replace a request |
DELETE |
/api/request |
Cancel the current pending request |
POST |
/api/profile |
Save private iFLY profile data |
POST |
/api/telegram/webhook |
Telegram Bot API webhook receiver |
GET |
/api/cron/expire-requests |
Scheduled cleanup for expired requests |
GET |
/api/health |
Health check |
Mini App API calls use Telegram WebApp init data:
Authorization: tma <telegram initData>Webhook and cron routes are protected by secrets stored outside the repository.
Requirements:
- Node.js 20+
- npm
- Supabase project with migrations applied
- Telegram bot token
- Vercel project or compatible Node.js hosting
Install and run locally:
cd miniapp
npm install
npm run devProduction build:
cd miniapp
npm run buildRuntime configuration lives in Vercel environment variables. Local .env files are for development only and must not be committed.
| Variable | Required | Notes |
|---|---|---|
SUPABASE_URL |
yes | Supabase API URL |
SUPABASE_ANON_KEY |
yes | Supabase anon key used with backend secret policy |
IFLY_APP_SECRET |
yes | Private backend secret checked by RLS |
TELEGRAM_BOT_TOKEN |
yes | Telegram Bot API token |
ALLOWED_GROUP_ID |
yes | Main Telegram group id |
WEBAPP_BASE_URL |
local | Public Mini App base URL; Vercel can infer it in production |
WEBHOOK_SECRET |
yes | Telegram webhook secret token |
CRON_SECRET |
yes | Bearer token for cron route |
BOT_USERNAME |
optional | Defaults to raspiliflybot |
MINIAPP_SHORT_NAME |
optional | Telegram Mini App short name |
APP_TIMEZONE |
optional | Defaults to Europe/Minsk |
NEXT_PUBLIC_API_BASE_URL |
optional | Defaults to /api |
NEXT_PUBLIC_BOT_USERNAME |
optional | Defaults to raspiliflybot |
Do not publish concrete values for tokens, group IDs, webhook secrets, project refs, or database credentials.
Production deploy uses Vercel:
cd miniapp
npx vercel deploy . --prod -yHealth check:
curl -sL https://miniapp-lemon-beta.vercel.app/api/healthExpected response:
{"ok":true}- No bot tokens or database secrets are stored in the repository.
- Telegram Mini App requests verify
initData.hash. - Telegram
auth_dateolder than 24 hours is rejected. - Telegram webhook requests require
WEBHOOK_SECRET. - Cron requests require
CRON_SECRET. - Supabase tables use RLS and a backend secret check.
- iFLY account data is shown only in the discussion group for the collected split.
- Main group messages never show phone, email, or full iFLY profile data.
- Public docs omit direct webhook URLs, Supabase project refs, and private deployment identifiers.
| Symptom | Fix |
|---|---|
| Mini App says bot cannot message the user | Open @raspiliflybot in private chat and press Start |
| Bot was added to a discussion group but does not create a link | Make the bot an admin with invite/manage permissions |
| Telegram redirects to private bot chat during admin assignment | This is Telegram behavior; return to the group after confirming rights |
| Another user tries to create a second discussion group | The bot should return the already claimed group link |
| Sticky message is not at the bottom | Send any message in the main group |
| Need test participants | Run /fake 3 in the main group; /fake 0 clears them |
No. It is designed as a Telegram Mini App. Opening the production URL in a browser is useful for health and hosting checks, but real auth and chat context come from Telegram.
Yes. 10h is the minimum package size. If users request more time or ranges allocate above 10 hours, the final split can exceed 10 hours.
No. A split is capped at four participants.
No. Helmet adds cost, but savings are based only on package time price compared with the standard hourly price.
Only inside the discussion group for the collected split. The main work group only receives nicknames and hours.
- Keep all production app and bot logic in
miniapp/. - Put database changes in
supabase/migrations/. - Keep secrets and deployment-specific IDs outside public docs.
- Escape user-facing Telegram HTML.
- Treat Telegram updates as retryable and duplicate-prone.
- Do not rebind discussion groups after a split already has
discussion_link. - Run
npm run buildbefore deploying production changes.