Skip to content

romkravets/blago-bot

Repository files navigation

Charity Raffle Bot 🎟

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).

При активації попередній активний розіграш автоматично закривається.

Змінити пул (✏️ Змінити пул)

Дозволяє збільшити пул номерків у поточному розіграші.
Показує поточний розмір та кількість вже зайнятих. Нове значення має бути більшим за зайняті.


Функції Super Admin

Новий розіграш (🎯 Новий розіграш)

Wizard у 3 кроки:

  1. Назва розіграшу (3–100 символів)
  2. Кількість номерків (швидкі варіанти: 100 / 200 / 300 / 500 / 1000, або своє)
  3. Підтвердження → попередній активний закривається, новий стартує

Налаштування (⚙️ Налаштування)

/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 — бот має відповісти головним меню.


Деплой на VPS

Вимоги до сервера

  • 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-bot

Перевірка статусу

pm2 status blago-bot          # має бути "online"
pm2 logs blago-bot --lines 50 # логи в реальному часі
pm2 monit                     # інтерактивний монітор

ARM-сервери (Hetzner CAX та ін.)

На 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 generate

Бекап бази

SQLite — це один файл. Бекап:

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 скрипти

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

About

Telegram-бот для благодійних розіграшів. Учасники роблять донат, надсилають скріншот, адміни підтверджують — учасник отримує номерок. Переможця обирає організатор вручну (wheelofnames.com).

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors