Telegram-бот для благодійних розіграшів. Учасники роблять донат, надсилають скріншот, адміни підтверджують — учасник отримує номерок. Переможця обирає організатор вручну (wheelofnames.com).
Бот має три рівні доступу:
| Дія | Учасник | Admin | Super Admin |
|---|---|---|---|
| Реєстрація участі | ✅ | — | — |
| Перегляд свого номерка | ✅ | ✅ | ✅ |
| Отримувати сповіщення про нові заявки | — | ✅ | ✅ |
| Підтверджувати / відхиляти заявки | — | ✅ | ✅ |
| Видаляти PENDING-запити | — | ✅ | ✅ |
| Статистика поточного розіграшу | — | ✅ | ✅ |
| Список заявок в очікуванні | — | ✅ | ✅ |
| Додавати учасника вручну | — | ✅ | ✅ |
| Експорт CSV / імена / номерки | — | ✅ | ✅ |
| Переглядати всі розіграші та активувати | — | ✅ | ✅ |
| Змінити пул (кількість номерків) | — | ✅ | ✅ |
| Створити новий розіграш | — | — | ✅ |
| Додати / видалити адміна | — | — | ✅ |
| Змінити налаштування | — | — | ✅ |
Super Admin — задається через змінну середовища SUPER_ADMIN_ID. Не зберігається в базі, захищений від видалення.
Admin — додається Super Admin'ом через команду /add_admin <telegram_id>. Зберігається в таблиці Admin.
Постійна клавіатура (завжди видна внизу чату):
[ 🎟 Взяти участь ] [ 📋 Мій номерок ]
[ ℹ️ Допомога ]
- 🎟 Взяти участь — запускає wizard реєстрації (ім'я → телефон → скріншот)
- 📋 Мій номерок — статус заявки або поточний номерок
- ℹ️ Допомога — інструкція про участь, посилання на організатора
Постійна клавіатура для адмінів та Super Admin'а:
[ ⏳ Очікують ] [ 📊 Статистика ]
[ 👤 Додати донора ] [ 📤 Експорт ]
[ 🎯 Новий розіграш ] [ ✏️ Змінити пул ]
[ 📋 Розіграші ] [ ⚙️ Налаштування ]
| Кнопка | Функція | Роль |
|---|---|---|
| ⏳ Очікують | Список PENDING-заявок з іменем і телефоном | Admin |
| 📊 Статистика | Загальний / очікують / підтверджено / відхилено | Admin |
| 👤 Додати донора | Wizard ручного додавання учасника | Admin |
| 📤 Експорт | Вибір формату: CSV / список імен / список номерків | Admin |
| 📋 Розіграші | Всі розіграші, статус, кількість учасників, активація | Admin |
| ✏️ Змінити пул | Збільшити кількість номерків поточного розіграшу | Admin |
| 🎯 Новий розіграш | Wizard створення нового розіграшу | Super Admin |
| ⚙️ Налаштування | Перегляд і зміна параметрів бота | Super Admin |
🎟 Взяти участь
→ Крок 1: Ім'я та прізвище (текстом, мін 2 слова)
→ Крок 2: Номер телефону (кнопка авто-надсилання або вручну +380...)
→ Крок 3: Скріншот оплати (фото з банківського додатку)
→ ✅ Запит надіслано — очікуй підтвердження (до 2 годин)
На кожному кроці доступні кнопки ⬅️ Назад та ❌ Скасувати.
При скасуванні — підтвердження діалогом (щоб уникнути випадкового виходу).
Якщо учасник вже реєструвався, і ввімкнено rememberUserData, бот пропонує використати збережені дані — крок одразу переходить до скріншоту.
- Приймається тільки фото (не файл)
- Мінімальний розмір: 10 KB (менше — відхиляється як некоректне)
- Перевірка на дублікат за SHA-256 хешем — не можна надіслати один скріншот двічі
Адмін та Super Admin отримують фото скріншоту з підписом:
🔔 Новий запит
👤 Марія Іваненко
📞 +380501234567
📸 Скріншот нижче
[ ✅ Підтвердити ] [ ❌ Відхилити ]
[ 🗑️ Видалити запит ]
- ✅ Підтвердити — учасник отримує inline-клавіатуру з 5 вільними номерками на вибір
- ❌ Відхилити — відкривається wizard, адмін вводить причину → учасник отримує повідомлення з поясненням
- 🗑️ Видалити запит — видаляє PENDING-запит з бази, учасник отримує повідомлення і може подати заново
🎉 Ваш донат підтверджено!
🎟 Оберіть свій номерок:
[ #47 ] [ #88 ] [ #134 ] [ #267 ] [ #391 ]
При колізії (хтось встиг вибрати номерок першим) — автоматично показується нова добірка.
Після вибору учасник отримує підтвердження та запрошення до групи учасників.
📊 Статистика розіграшу: Розіграш травень 2026
🎟 Пул номерків: 500
👥 Всього запитів: 87
⏳ Очікують: 12
✅ Підтверджено: 71
🎟 Номерки обрано: 65
❌ Відхилено: 4
Показує список PENDING-заявок з іменем, прізвищем, телефоном та ID кожної.
- 📊 CSV файл — завантажує файл
raffle-export.csv(номерок, ім'я, прізвище, телефон) - 📋 Список імен — текстовий список "Ім'я Прізвище" для wheelofnames.com
- 🔢 Список номерків — нумерований список
#Nдля draw
Wizard для додавання учасника без скріншоту (наприклад, готівкова оплата): ім'я → телефон → автоматичне підтвердження → учасник обирає номерок.
Показує всі розіграші з їхнім статусом та кількістю учасників.
Для кожного неактивного — кнопка активації (доступна тільки Super Admin).
При активації попередній активний розіграш автоматично закривається.
Дозволяє збільшити пул номерків у поточному розіграші.
Показує поточний розмір та кількість вже зайнятих. Нове значення має бути більшим за зайняті.
Wizard у 3 кроки:
- Назва розіграшу (3–100 символів)
- Кількість номерків (швидкі варіанти: 100 / 200 / 300 / 500 / 1000, або своє)
- Підтвердження → попередній активний закривається, новий стартує
/settings — перегляд поточних налаштувань
/settings remember_users on — запам'ятовувати ім'я/телефон між розіграшами
/settings remember_users off — завжди вводити дані заново
/add_admin 987654321 — додати адміна за Telegram ID
/remove_admin 987654321 — видалити адміна
Telegram ID дізнатись через @userinfobot — надіслати
/start.
ID це число, не @username.
| Команда | Що робить |
|---|---|
/start |
Показати головне меню |
/my_ticket |
Статус заявки або поточний номерок |
/help |
Інструкція |
| Команда | Що робить | Роль |
|---|---|---|
/pending |
Список заявок в очікуванні | Admin |
/stats |
Статистика розіграшу | Admin |
/export |
Меню експорту | Admin |
/add_donor |
Ручне додавання учасника | Admin |
/raffles |
Список усіх розіграшів | Admin |
/new_raffle "Назва" <кількість> |
Створити новий розіграш (текстовий) | Super Admin |
/add_admin <telegram_id> |
Додати адміна | Super Admin |
/remove_admin <telegram_id> |
Видалити адміна | Super Admin |
/settings [параметр] [значення] |
Переглянути/змінити налаштування | Super Admin |
- Node.js 20+
- npm 10+
# 1. Клонуй репозиторій
git clone <repo-url>
cd blago-bot
# 2. Встанови залежності
npm install
# 3. Скопіюй та заповни .env
cp .env.example .envВідкрий .env і заповни:
BOT_TOKEN=<токен від BotFather>
DATABASE_URL=file:./prisma/blago.db
SUPER_ADMIN_ID=<твій Telegram ID (число)>
NODE_ENV=development# 4. Застосуй схему БД
npm run db:push
# 5. Заповни початковими даними (перший розіграш + Config)
npm run db:seed
# 6. Запусти в режимі розробки
npm run devНадішли /start в Telegram — бот має відповісти головним меню.
- Ubuntu 22.04 / Debian 12 або новіший
- Node.js 20+
- npm 10+
- PM2 (
npm install -g pm2)
Бот працює в polling-режимі — не потребує відкритих портів і не конфліктує з іншими сервісами (nginx, Docker тощо).
# 1. SSH на сервер
ssh root@<server-ip>
# 2. Встанови Node.js (якщо ще немає)
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs
# 3. Встанови PM2
npm install -g pm2
# 4. Клонуй репозиторій
git clone <repo-url> /opt/blago-bot
cd /opt/blago-bot
# 5. Встанови залежності
npm install
# 6. Створи .env
cp .env.example .env
nano .env
# Заповни BOT_TOKEN, SUPER_ADMIN_ID, NODE_ENV=production
# DATABASE_URL залиш за замовчуванням: file:./prisma/blago.db
# 7. Застосуй схему БД
npm run db:push
# 8. Заповни початковими даними
npm run db:seed
# 9. Збери TypeScript
npm run build
# 10. Запусти через PM2
pm2 start ecosystem.config.js
pm2 save
# 11. Налаштуй автозапуск при перезавантаженні сервера
pm2 startup
# Скопіюй і виконай команду яку видасть (починається з sudo env PATH...)cd /opt/blago-bot
git pull
npm install # якщо змінились залежності
npm run build
pm2 restart blago-botpm2 status blago-bot # має бути "online"
pm2 logs blago-bot --lines 50 # логи в реальному часі
pm2 monit # інтерактивний моніторНа ARM-архітектурі (linux-arm64) Prisma потребує відповідного бінарного таргету.
У prisma/schema.prisma вже прописано:
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "linux-arm64-openssl-3.0.x"]
}Після зміни схеми або оновлення Prisma — перегенерувати:
npm run build # включає prisma generateSQLite — це один файл. Бекап:
cp /opt/blago-bot/prisma/blago.db /opt/blago-bot/backups/$(date +%Y%m%d_%H%M).dbАвтоматичний щоденний бекап (cron):
crontab -e
# Додати рядок:
0 3 * * * cp /opt/blago-bot/prisma/blago.db /opt/blago-bot/backups/$(date +\%Y\%m\%d).db# GUI у браузері (localhost:5555)
npm run db:studio
# Активний розіграш
sqlite3 prisma/blago.db "SELECT * FROM Config;"
# Всі учасники з номерками
sqlite3 prisma/blago.db \
"SELECT u.firstName, u.lastName, u.phone, d.chosenTicket
FROM Donation d JOIN User u ON d.userId = u.id
WHERE d.status = 'APPROVED' AND d.chosenTicket IS NOT NULL
ORDER BY d.chosenTicket;"| Змінна | Обов'язкова | Опис |
|---|---|---|
BOT_TOKEN |
✅ | Токен бота від @BotFather |
SUPER_ADMIN_ID |
✅ | Telegram ID Super Admin (число) |
DATABASE_URL |
✅ | Шлях до SQLite: file:./prisma/blago.db |
NODE_ENV |
— | development або production |
SUPER_ADMIN_ID— це числовий Telegram ID, не токен бота.
Отримати через @userinfobot →/start.
npm run dev # Розробка з hot reload (tsx watch)
npm run build # TypeScript → dist/ + prisma generate
npm run start # Запуск dist/index.js (production)
npm test # Vitest — всі тести
npm run db:push # Застосувати схему (dev / перший деплой)
npm run db:studio # Prisma Studio GUI
npm run db:seed # Початкові дані (перший розіграш + Config)blago-bot/
├── src/
│ ├── index.ts # Точка входу, middleware chain, polling
│ ├── config.ts # Zod валідація .env, GROUP_LINK
│ ├── types.ts # MyContext, SessionData
│ ├── db/
│ │ ├── client.ts # Prisma singleton
│ │ ├── configStore.ts # getConfig / setConfig
│ │ └── tickets.ts # generateAvailableTickets, claimTicket
│ └── bot/
│ ├── keyboards.ts # Всі inline/reply клавіатури
│ ├── menu.ts # checkIsAdmin, sendMainMenu
│ ├── handlers/
│ │ ├── user.ts # /start, /my_ticket, ticket callback
│ │ └── admin.ts # Всі адмін-команди, approve/reject/delete
│ ├── conversations/
│ │ ├── register.ts # Wizard реєстрації (3 кроки + back)
│ │ ├── rejectDonation.ts # Wizard відхилення з причиною
│ │ ├── addDonor.ts # Ручне додавання учасника
│ │ ├── newRaffle.ts # Wizard створення нового розіграшу
│ │ └── setTickets.ts # Wizard зміни пулу номерків
│ └── middlewares/
│ ├── auth.ts # requireAdmin, requireSuperAdmin
│ ├── rateLimit.ts # 3 запити / 10 хв per user
│ ├── logger.ts # Pino логування
│ └── errorHandler.ts # Глобальний catch
├── prisma/
│ ├── schema.prisma # Схема БД (User, Donation, Event, Admin, Config, AuditLog)
│ └── seed.ts # Початкові дані
├── ecosystem.config.js # PM2 конфіг
└── .env.example # Шаблон змінних середовища
| Компонент | Технологія |
|---|---|
| Runtime | Node.js 20 |
| Мова | TypeScript 5 (strict mode) |
| Telegram | Grammy + conversations + session |
| БД | SQLite + Prisma ORM |
| Режим | Polling (без webhook — не потребує домену/SSL/відкритих портів) |
| Логування | Pino |
| Тести | Vitest |
| Деплой | PM2 на VPS |