mirror of
https://github.com/ada-dmitry/vol_bot.git
synced 2026-09-24 00:30:19 +00:00
Добавление логгирования
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
BOT_TOKEN=
|
||||||
|
ADMINS=
|
||||||
|
DATABASE_URL=
|
||||||
|
|
||||||
Generated
-8
@@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
Generated
-19
@@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
|
||||||
<data-source source="LOCAL" name="postgres@localhost" uuid="64a7c47f-1c6d-4f9b-bb6d-5b6fe66fba41">
|
|
||||||
<driver-ref>postgresql</driver-ref>
|
|
||||||
<synchronize>true</synchronize>
|
|
||||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
|
||||||
<jdbc-url>jdbc:postgresql://localhost:5432/postgres</jdbc-url>
|
|
||||||
<working-dir>$ProjectFileDir$</working-dir>
|
|
||||||
</data-source>
|
|
||||||
<data-source source="LOCAL" name="vc_database" uuid="bdaa7723-af4b-4080-b3fe-20d0cf595d3c">
|
|
||||||
<driver-ref>postgresql</driver-ref>
|
|
||||||
<synchronize>true</synchronize>
|
|
||||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
|
||||||
<jdbc-url>jdbc:postgresql://localhost:5432/postgres</jdbc-url>
|
|
||||||
<working-dir>$ProjectFileDir$</working-dir>
|
|
||||||
</data-source>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
-6
@@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
Generated
-7
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Black">
|
|
||||||
<option name="sdkName" value="Python 3.12 (vol_bot)" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (vc_bot)" project-jdk-type="Python SDK" />
|
|
||||||
</project>
|
|
||||||
Generated
-8
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/vol_bot.iml" filepath="$PROJECT_DIR$/.idea/vol_bot.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
-6
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
-14
@@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="PYTHON_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.idea/dataSources" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="jdk" jdkName="Python 3.13 (vc_bot)" jdkType="Python SDK" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
<component name="PackageRequirementsSettings">
|
|
||||||
<option name="keepMatchingSpecifier" value="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
@@ -1,3 +1,93 @@
|
|||||||
## Бот для Волонтерского Центра НИЯУ МИФИ
|
# Volunteer Center Bot (Волонтерский Центр НИЯУ МИФИ Бот)
|
||||||
|
|
||||||
-
|
## Описание
|
||||||
|
|
||||||
|
Этот проект представляет собой Telegram-бота, разработанного для Волонтерского Центра НИЯУ МИФИ. Бот предоставляет функционал для регистрации пользователей, управления рассылками и взаимодействия с администраторами.
|
||||||
|
|
||||||
|
## Основной функционал:
|
||||||
|
|
||||||
|
1. **Регистрация пользователей:**
|
||||||
|
* Позволяет новым пользователям зарегистрироваться в системе бота.
|
||||||
|
* Данные пользователей сохраняются в базе данных.
|
||||||
|
|
||||||
|
2. **Подписка/Отписка от рассылки:**
|
||||||
|
* Пользователи могут подписываться на рассылку сообщений от администраторов.
|
||||||
|
* Пользователи могут отписываться от рассылки в любое время.
|
||||||
|
|
||||||
|
3. **Рассылка сообщений:**
|
||||||
|
* Администраторы могут отправлять сообщения пользователям, подписанным на рассылку.
|
||||||
|
* Поддерживаются два формата сообщений: короткий и длинный.
|
||||||
|
|
||||||
|
4. **Статистика рассылки:**
|
||||||
|
* Предоставляет администраторам информацию об объеме рассылки (например, количество получателей).
|
||||||
|
|
||||||
|
## Технологии:
|
||||||
|
|
||||||
|
* **Python:** Основной язык программирования.
|
||||||
|
* **aiogram:** Библиотека для создания Telegram-ботов.
|
||||||
|
* **SQLAlchemy:** ORM для работы с базой данных.
|
||||||
|
* **PostgreSQL:** Используется в качестве базы данных.
|
||||||
|
* **dotenv:** Для управления переменными окружения.
|
||||||
|
* **FSM (Finite State Machine):** Для управления состояниями бота.
|
||||||
|
* **MemoryStorage:** Хранилище состояний в оперативной памяти.
|
||||||
|
|
||||||
|
## Структура проекта:
|
||||||
|
|
||||||
|
* **`main.py`:** Основной файл, запускающий бота.
|
||||||
|
* **`handlers/`:** Директория, содержащая обработчики событий (хендлеры) для различных типов пользователей (обычные пользователи, администраторы) и callback-запросов.
|
||||||
|
* `user.py`: Обработчики для обычных пользователей.
|
||||||
|
* `admin.py`: Обработчики для администраторов.
|
||||||
|
* `admin_adv.py`: Обработчики для рассылки сообщений администраторами.
|
||||||
|
* `adv_callbacks.py`: Обработчики для callback-запросов, связанных с рассылкой.
|
||||||
|
* **`database/`:** Директория, содержащая код для работы с базой данных.
|
||||||
|
* `__init__.py`: Инициализация модуля.
|
||||||
|
* **`.env`:** Файл с переменными окружения (например, токен бота).
|
||||||
|
* **`README.md`:** Этот файл.
|
||||||
|
|
||||||
|
## Установка и запуск:
|
||||||
|
|
||||||
|
1. **Клонируйте репозиторий:**
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/ada-dmitry/vol_bot
|
||||||
|
cd vol_bot
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Создайте виртуальное окружение (рекомендуется):**
|
||||||
|
```bash
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate # Linux/macOS
|
||||||
|
venv\Scripts\activate # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Установите зависимости:**
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
(Если у вас нет файла `requirements.txt`, то установите зависимости вручную: `pip install aiogram python-dotenv sqlalchemy`)
|
||||||
|
|
||||||
|
4. **Создайте файл `.env`:**
|
||||||
|
* Скопируйте содержимое файла `.env.example` в новый файл `.env`.
|
||||||
|
* Замените `BOT_TOKEN` на токен вашего бота, полученный от BotFather.
|
||||||
|
|
||||||
|
5. **Запустите бота:**
|
||||||
|
```bash
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Разработка:
|
||||||
|
|
||||||
|
* Для добавления нового функционала создавайте новые обработчики в соответствующих файлах директории `handlers/`.
|
||||||
|
* Для работы с базой данных используйте SQLAlchemy.
|
||||||
|
* Для управления состояниями бота используйте FSM.
|
||||||
|
|
||||||
|
## Планы по развитию:
|
||||||
|
|
||||||
|
* Добавление новых команд и функций.
|
||||||
|
* Улучшение интерфейса взаимодействия с пользователем.
|
||||||
|
* Расширение возможностей администраторов.
|
||||||
|
* Добавление логирования.
|
||||||
|
* Добавление тестов.
|
||||||
|
|
||||||
|
## Контакты
|
||||||
|
Другие проекты: [GitHub](https://github.com/ada-dmitry)
|
||||||
|
По вопросам и предложениям обращайтесь: [TG - Дмитрий](https://t.me/ada_dmitry)
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,51 @@
|
|||||||
|
2025-05-02 20:48:11,017 - INFO - __main__ - Запуск работы бота.
|
||||||
|
2025-05-02 20:48:11,070 - INFO - aiogram.dispatcher - Start polling
|
||||||
|
2025-05-02 20:48:11,331 - INFO - aiogram.dispatcher - Run polling for bot @volunteer_mephi_bot id=7827792861 - 'VC_Bot'
|
||||||
|
2025-05-02 20:48:38,874 - WARNING - aiogram.dispatcher - Received SIGINT signal
|
||||||
|
2025-05-02 20:48:38,875 - INFO - aiogram.dispatcher - Polling stopped for bot @volunteer_mephi_bot id=7827792861 - 'VC_Bot'
|
||||||
|
2025-05-02 20:48:38,875 - INFO - aiogram.dispatcher - Polling stopped
|
||||||
|
2025-05-02 20:52:05,968 - INFO - __main__ - Запуск работы бота.
|
||||||
|
2025-05-02 20:52:06,013 - INFO - aiogram.dispatcher - Start polling
|
||||||
|
2025-05-02 20:52:06,277 - INFO - aiogram.dispatcher - Run polling for bot @volunteer_mephi_bot id=7827792861 - 'VC_Bot'
|
||||||
|
2025-05-02 20:52:13,305 - WARNING - aiogram.dispatcher - Received SIGINT signal
|
||||||
|
2025-05-02 20:52:13,306 - INFO - aiogram.dispatcher - Polling stopped for bot @volunteer_mephi_bot id=7827792861 - 'VC_Bot'
|
||||||
|
2025-05-02 20:52:13,306 - INFO - aiogram.dispatcher - Polling stopped
|
||||||
|
2025-05-02 20:52:17,747 - INFO - __main__ - Запуск работы бота.
|
||||||
|
2025-05-02 20:52:17,780 - INFO - aiogram.dispatcher - Start polling
|
||||||
|
2025-05-02 20:52:18,048 - INFO - aiogram.dispatcher - Run polling for bot @volunteer_mephi_bot id=7827792861 - 'VC_Bot'
|
||||||
|
2025-05-02 20:52:32,713 - INFO - aiogram.event - Update id=328315775 is handled. Duration 226 ms by bot id=7827792861
|
||||||
|
2025-05-02 20:52:44,275 - INFO - aiogram.event - Update id=328315776 is not handled. Duration 1 ms by bot id=7827792861
|
||||||
|
2025-05-02 20:53:04,829 - INFO - aiogram.event - Update id=328315777 is not handled. Duration 2 ms by bot id=7827792861
|
||||||
|
2025-05-02 20:53:17,894 - WARNING - aiogram.dispatcher - Received SIGINT signal
|
||||||
|
2025-05-02 20:53:17,895 - INFO - aiogram.dispatcher - Polling stopped for bot @volunteer_mephi_bot id=7827792861 - 'VC_Bot'
|
||||||
|
2025-05-02 20:53:17,896 - INFO - aiogram.dispatcher - Polling stopped
|
||||||
|
2025-05-02 20:55:10,923 - INFO - __main__ - Запуск работы бота.
|
||||||
|
2025-05-02 20:55:10,971 - INFO - aiogram.dispatcher - Start polling
|
||||||
|
2025-05-02 20:55:11,246 - INFO - aiogram.dispatcher - Run polling for bot @volunteer_mephi_bot id=7827792861 - 'VC_Bot'
|
||||||
|
2025-05-02 20:55:19,000 - INFO - aiogram.event - Update id=328315778 is not handled. Duration 1 ms by bot id=7827792861
|
||||||
|
2025-05-02 20:55:20,367 - INFO - main - User 451218808 started the bot
|
||||||
|
2025-05-02 20:55:20,838 - INFO - aiogram.event - Update id=328315779 is handled. Duration 472 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:00:28,136 - INFO - aiogram.event - Update id=328315780 is not handled. Duration 1 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:00:28,840 - INFO - main - User 451218808 started the bot
|
||||||
|
2025-05-02 21:00:28,963 - INFO - aiogram.event - Update id=328315781 is handled. Duration 123 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:00:31,620 - INFO - aiogram.event - Update id=328315782 is not handled. Duration 2 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:00:33,258 - INFO - aiogram.event - Update id=328315783 is not handled. Duration 1 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:00:33,973 - INFO - aiogram.event - Update id=328315784 is not handled. Duration 1 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:00:34,039 - INFO - aiogram.event - Update id=328315785 is not handled. Duration 1 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:00:34,178 - INFO - aiogram.event - Update id=328315786 is not handled. Duration 2 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:00:34,389 - INFO - aiogram.event - Update id=328315787 is not handled. Duration 1 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:08:22,430 - WARNING - aiogram.dispatcher - Received SIGINT signal
|
||||||
|
2025-05-02 21:08:22,431 - INFO - aiogram.dispatcher - Polling stopped for bot @volunteer_mephi_bot id=7827792861 - 'VC_Bot'
|
||||||
|
2025-05-02 21:08:22,431 - INFO - aiogram.dispatcher - Polling stopped
|
||||||
|
2025-05-02 21:16:06,283 - INFO - __main__ - Запуск работы бота.
|
||||||
|
2025-05-02 21:16:06,343 - INFO - aiogram.dispatcher - Start polling
|
||||||
|
2025-05-02 21:16:06,584 - INFO - aiogram.dispatcher - Run polling for bot @volunteer_mephi_bot id=7827792861 - 'VC_Bot'
|
||||||
|
2025-05-02 21:16:23,053 - INFO - handlers.user - User 451218808 started the bot
|
||||||
|
2025-05-02 21:16:23,296 - INFO - aiogram.event - Update id=328315793 is handled. Duration 243 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:16:24,462 - INFO - aiogram.event - Update id=328315794 is handled. Duration 220 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:16:25,683 - INFO - aiogram.event - Update id=328315795 is handled. Duration 234 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:16:27,117 - INFO - aiogram.event - Update id=328315796 is handled. Duration 212 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:16:28,052 - INFO - aiogram.event - Update id=328315797 is handled. Duration 381 ms by bot id=7827792861
|
||||||
|
2025-05-02 21:16:30,962 - INFO - aiogram.event - Update id=328315798 is handled. Duration 200 ms by bot id=7827792861
|
||||||
|
2025-05-03 09:36:21,305 - INFO - __main__ - Запуск работы бота.
|
||||||
|
2025-05-03 09:37:58,037 - INFO - __main__ - Запуск работы бота.
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+8
-2
@@ -5,24 +5,30 @@ from models import User
|
|||||||
from database import async_session
|
from database import async_session
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
admin_ids = list(map(int, os.getenv("ADMINS").split(",")))
|
admin_ids = list(map(int, os.getenv("ADMINS").split(",")))
|
||||||
|
|
||||||
|
|
||||||
@router.message(F.text == "/admin")
|
@router.message(F.text == "/admin")
|
||||||
async def admin_panel(message: Message):
|
async def admin_panel(message: Message):
|
||||||
if message.from_user.id not in admin_ids:
|
if message.from_user.id not in admin_ids:
|
||||||
return await message.answer("Доступ запрещён.")
|
return await message.answer("Доступ запрещён.")
|
||||||
await message.answer("Команды:\n/stat — статистика\n/broadcast <текст> — рассылка")
|
await message.answer("Команды:\n/stat — статистика\n/broadcast <текст> — рассылка")
|
||||||
|
|
||||||
|
|
||||||
@router.message(F.text.startswith("/stat"))
|
@router.message(F.text.startswith("/stat"))
|
||||||
async def stat_handler(message: Message):
|
async def stat_handler(message: Message):
|
||||||
if message.from_user.id not in admin_ids:
|
if message.from_user.id not in admin_ids:
|
||||||
return
|
return
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
total = await session.scalar(select(User).count())
|
total = await session.scalar(select(User).count())
|
||||||
subscribed = await session.scalar(select(User).where(User.is_subscribed).count())
|
subscribed = await session.scalar(
|
||||||
|
select(User).where(User.is_subscribed).count()
|
||||||
|
)
|
||||||
await message.answer(f"Всего пользователей: {total}\nПодписаны: {subscribed}")
|
await message.answer(f"Всего пользователей: {total}\nПодписаны: {subscribed}")
|
||||||
|
|
||||||
|
|
||||||
@router.message(F.text.startswith("/broadcast "))
|
@router.message(F.text.startswith("/broadcast "))
|
||||||
async def broadcast_handler(message: Message, bot: Bot):
|
async def broadcast_handler(message: Message, bot: Bot):
|
||||||
if message.from_user.id not in admin_ids:
|
if message.from_user.id not in admin_ids:
|
||||||
@@ -48,4 +54,4 @@ async def broadcast_handler(message: Message, bot: Bot):
|
|||||||
except Exception:
|
except Exception:
|
||||||
failed += 1
|
failed += 1
|
||||||
|
|
||||||
await message.answer(f"📬 Рассылка завершена:\nУспешно: {sent}\nОшибок: {failed}")
|
await message.answer(f"📬 Рассылка завершена:\nУспешно: {sent}\nОшибок: {failed}")
|
||||||
|
|||||||
@@ -11,11 +11,10 @@ import os
|
|||||||
router = Router()
|
router = Router()
|
||||||
admin_ids = list(map(int, os.getenv("ADMINS", "").split(",")))
|
admin_ids = list(map(int, os.getenv("ADMINS", "").split(",")))
|
||||||
|
|
||||||
# Храним рассылки
|
|
||||||
adv_memory = {} # {adv_id: {"text": str, "is_active": bool}}
|
adv_memory = {} # {adv_id: {"text": str, "is_active": bool}}
|
||||||
current_adv_id = None
|
current_adv_id = None
|
||||||
|
|
||||||
# Состояния для FSM
|
|
||||||
class AdvForm(StatesGroup):
|
class AdvForm(StatesGroup):
|
||||||
short = State()
|
short = State()
|
||||||
full = State()
|
full = State()
|
||||||
@@ -44,18 +43,15 @@ async def get_full_text_and_send(message: Message, state: FSMContext, bot: Bot):
|
|||||||
# Генерируем уникальный ID рассылки
|
# Генерируем уникальный ID рассылки
|
||||||
adv_id = str(uuid4())
|
adv_id = str(uuid4())
|
||||||
|
|
||||||
# Деактивируем старые рассылки
|
|
||||||
for adv in adv_memory.values():
|
for adv in adv_memory.values():
|
||||||
adv["is_active"] = False
|
adv["is_active"] = False
|
||||||
|
|
||||||
# Сохраняем новую рассылку
|
|
||||||
adv_memory[adv_id] = {
|
adv_memory[adv_id] = {
|
||||||
"text": full_text,
|
"text": full_text,
|
||||||
"is_active": True
|
"is_active": True
|
||||||
}
|
}
|
||||||
current_adv_id = adv_id
|
current_adv_id = adv_id
|
||||||
|
|
||||||
# Клавиатура
|
|
||||||
kb = InlineKeyboardMarkup(inline_keyboard=[
|
kb = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
[
|
[
|
||||||
InlineKeyboardButton(text="👍 Интересно", callback_data=f"adv:{adv_id}"),
|
InlineKeyboardButton(text="👍 Интересно", callback_data=f"adv:{adv_id}"),
|
||||||
|
|||||||
+64
-14
@@ -3,35 +3,47 @@ from aiogram.fsm.context import FSMContext
|
|||||||
from aiogram.types import Message, CallbackQuery
|
from aiogram.types import Message, CallbackQuery
|
||||||
from aiogram.fsm.state import State, StatesGroup
|
from aiogram.fsm.state import State, StatesGroup
|
||||||
from sqlalchemy import select, insert, update
|
from sqlalchemy import select, insert, update
|
||||||
|
import logging
|
||||||
|
|
||||||
from models import User
|
from models import User
|
||||||
from database import async_session
|
from database import async_session
|
||||||
from keyboards import get_subscription_keyboard
|
from keyboards import get_subscription_keyboard
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
# Машина состояний
|
# Машина состояний
|
||||||
class Register(StatesGroup):
|
class Register(StatesGroup):
|
||||||
waiting_for_fullname = State()
|
waiting_for_fullname = State()
|
||||||
waiting_for_study_group = State()
|
waiting_for_study_group = State()
|
||||||
|
|
||||||
|
|
||||||
@router.message(F.text == "/start")
|
@router.message(F.text == "/start")
|
||||||
async def start_handler(message: Message, state: FSMContext):
|
async def start_handler(message: Message, state: FSMContext):
|
||||||
user_id = message.from_user.id
|
user_id = message.from_user.id
|
||||||
|
logger.info(f"User {message.from_user.id} started the bot")
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
result = await session.execute(select(User).where(User.user_id == user_id))
|
result = await session.execute(select(User).where(User.user_id == user_id))
|
||||||
user = result.scalar_one_or_none()
|
user = result.scalar_one_or_none()
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
# Приветствие при повторном запуске
|
# Приветствие при повторном запуске
|
||||||
text = f"👋 Привет, {user.full_name or 'друг'}!\n" \
|
text = (
|
||||||
f"Группа: {user.study_group or 'не указана'}\n" \
|
f"👋 Привет, {user.full_name or 'друг'}!\n"
|
||||||
"Используй кнопки ниже, чтобы управлять подпиской:"
|
f"Группа: {user.study_group or 'не указана'}\n"
|
||||||
await message.answer(text, reply_markup=get_subscription_keyboard(user.is_subscribed))
|
"Используй кнопки ниже, чтобы управлять подпиской:"
|
||||||
|
)
|
||||||
|
await message.answer(
|
||||||
|
text, reply_markup=get_subscription_keyboard(user.is_subscribed)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# Первый запуск — регистрация
|
# Первый запуск — регистрация
|
||||||
await state.set_state(Register.waiting_for_fullname)
|
await state.set_state(Register.waiting_for_fullname)
|
||||||
await message.answer("👋 Привет! Пожалуйста, напиши свои ФИО одной строкой:")
|
await message.answer(
|
||||||
|
"👋 Привет! Пожалуйста, напиши свои ФИО одной строкой:"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.message(Register.waiting_for_fullname)
|
@router.message(Register.waiting_for_fullname)
|
||||||
async def get_fullname(message: Message, state: FSMContext):
|
async def get_fullname(message: Message, state: FSMContext):
|
||||||
@@ -39,6 +51,7 @@ async def get_fullname(message: Message, state: FSMContext):
|
|||||||
await state.set_state(Register.waiting_for_study_group)
|
await state.set_state(Register.waiting_for_study_group)
|
||||||
await message.answer("Теперь укажи номер своей группы (например, Б11-111):")
|
await message.answer("Теперь укажи номер своей группы (например, Б11-111):")
|
||||||
|
|
||||||
|
|
||||||
@router.message(Register.waiting_for_study_group)
|
@router.message(Register.waiting_for_study_group)
|
||||||
async def get_study_group(message: Message, state: FSMContext):
|
async def get_study_group(message: Message, state: FSMContext):
|
||||||
data = await state.get_data()
|
data = await state.get_data()
|
||||||
@@ -46,14 +59,51 @@ async def get_study_group(message: Message, state: FSMContext):
|
|||||||
study_group = message.text
|
study_group = message.text
|
||||||
|
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
await session.execute(insert(User).values(
|
await session.execute(
|
||||||
user_id=message.from_user.id,
|
insert(User).values(
|
||||||
full_name=full_name,
|
user_id=message.from_user.id,
|
||||||
study_group=study_group,
|
full_name=full_name,
|
||||||
is_subscribed=True
|
study_group=study_group,
|
||||||
))
|
is_subscribed=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
await message.answer(f"✅ Регистрация завершена!\nФИО: {full_name}\nГруппа: {study_group}")
|
await message.answer(
|
||||||
await message.answer("Теперь ты можешь управлять подпиской ниже 👇", reply_markup=get_subscription_keyboard(True))
|
f"✅ Регистрация завершена!\nФИО: {full_name}\nГруппа: {study_group}"
|
||||||
|
)
|
||||||
|
await message.answer(
|
||||||
|
"Теперь ты можешь управлять подпиской ниже 👇",
|
||||||
|
reply_markup=get_subscription_keyboard(True),
|
||||||
|
)
|
||||||
await state.clear()
|
await state.clear()
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == "subscribe")
|
||||||
|
async def subscribe(callback: CallbackQuery):
|
||||||
|
async with async_session() as session:
|
||||||
|
await session.execute(
|
||||||
|
update(User)
|
||||||
|
.where(User.user_id == callback.from_user.id)
|
||||||
|
.values(is_subscribed=True)
|
||||||
|
)
|
||||||
|
await session.commit()
|
||||||
|
await callback.message.edit_reply_markup(
|
||||||
|
reply_markup=get_subscription_keyboard(True)
|
||||||
|
)
|
||||||
|
await callback.answer("✅ Вы подписались на рассылку!")
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data == "unsubscribe")
|
||||||
|
async def unsubscribe(callback: CallbackQuery):
|
||||||
|
async with async_session() as session:
|
||||||
|
await session.execute(
|
||||||
|
update(User)
|
||||||
|
.where(User.user_id == callback.from_user.id)
|
||||||
|
.values(is_subscribed=False)
|
||||||
|
)
|
||||||
|
await session.commit()
|
||||||
|
await callback.message.edit_reply_markup(
|
||||||
|
reply_markup=get_subscription_keyboard(False)
|
||||||
|
)
|
||||||
|
await callback.answer("❌ Вы отписались от рассылки.")
|
||||||
|
|||||||
@@ -4,30 +4,47 @@ from aiogram.fsm.storage.memory import MemoryStorage
|
|||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
from handlers import user, admin, admin_adv, adv_callbacks
|
from handlers import user, admin, admin_adv, adv_callbacks
|
||||||
from database import engine, Base
|
from database import engine, Base
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO, # Уровень логирования (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||||
|
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
|
||||||
|
handlers=[
|
||||||
|
logging.FileHandler("bot.log"), # Логирование в файл bot.log
|
||||||
|
# logging.StreamHandler(), # Логирование в консоль
|
||||||
|
],
|
||||||
|
)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def on_startup():
|
async def on_startup():
|
||||||
|
"""
|
||||||
|
Основная процедура для запуска работы с БД чреез SQLAlchemy.
|
||||||
|
"""
|
||||||
async with engine.begin() as conn:
|
async with engine.begin() as conn:
|
||||||
# await conn.run_sync(Base.metadata.drop_all) # Сброс БД
|
# await conn.run_sync(Base.metadata.drop_all) # Сброс БД
|
||||||
await conn.run_sync(Base.metadata.create_all)
|
await conn.run_sync(Base.metadata.create_all)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
bot = Bot(token=os.getenv("BOT_TOKEN"))
|
"""
|
||||||
|
Функция запуска бота, подключение к TG_API, создание диспетчера, подключение роутеров и запуск бота в режиме пуллинг.
|
||||||
|
"""
|
||||||
|
bot = Bot(token=os.getenv("BOT_TOKEN")) # type: ignore
|
||||||
dp = Dispatcher(storage=MemoryStorage())
|
dp = Dispatcher(storage=MemoryStorage())
|
||||||
|
logger.info("Запуск работы бота.")
|
||||||
dp.include_routers(
|
dp.include_routers(
|
||||||
user.router,
|
user.router, admin.router, admin_adv.router, adv_callbacks.router
|
||||||
admin.router,
|
|
||||||
admin_adv.router,
|
|
||||||
adv_callbacks.router
|
|
||||||
)
|
)
|
||||||
await on_startup()
|
await on_startup()
|
||||||
await dp.start_polling(bot)
|
await dp.start_polling(bot) # Бот в пуллинг режиме - "почтальон для API"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
aiogram~=3.20.0
|
aiogram~=3.20.0
|
||||||
dotenv~=0.9.9
|
|
||||||
python-dotenv~=1.1.0
|
python-dotenv~=1.1.0
|
||||||
sqlalchemy~=2.0.40
|
sqlalchemy~=2.0.40
|
||||||
asyncpg~=0.30.0
|
asyncpg~=0.30.0
|
||||||
|
|||||||
Reference in New Issue
Block a user