mirror of
https://github.com/ada-dmitry/vol_bot.git
synced 2026-09-24 00:30:19 +00:00
Изменение БД, добавление регистрации, добавление сложной рассылки, кнопки для СР
This commit is contained in:
Generated
+1
@@ -2,6 +2,7 @@
|
|||||||
<module type="PYTHON_MODULE" version="4">
|
<module type="PYTHON_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.idea/dataSources" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.13 (vc_bot)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.13 (vc_bot)" jdkType="Python SDK" />
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
from aiogram import Router, F, Bot
|
||||||
|
from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
from aiogram.fsm.context import FSMContext
|
||||||
|
from aiogram.fsm.state import StatesGroup, State
|
||||||
|
from sqlalchemy import select
|
||||||
|
from database import async_session
|
||||||
|
from models import User
|
||||||
|
from uuid import uuid4
|
||||||
|
import os
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
admin_ids = list(map(int, os.getenv("ADMINS", "").split(",")))
|
||||||
|
|
||||||
|
# Храним рассылки
|
||||||
|
adv_memory = {} # {adv_id: {"text": str, "is_active": bool}}
|
||||||
|
current_adv_id = None
|
||||||
|
|
||||||
|
# Состояния для FSM
|
||||||
|
class AdvForm(StatesGroup):
|
||||||
|
short = State()
|
||||||
|
full = State()
|
||||||
|
|
||||||
|
@router.message(F.text == "/send_adv")
|
||||||
|
async def start_adv(message: Message, state: FSMContext):
|
||||||
|
if message.from_user.id not in admin_ids:
|
||||||
|
return await message.answer("⛔ Доступ запрещён.")
|
||||||
|
await state.set_state(AdvForm.short)
|
||||||
|
await message.answer("Введите краткую форму сообщения:")
|
||||||
|
|
||||||
|
@router.message(AdvForm.short)
|
||||||
|
async def get_short_text(message: Message, state: FSMContext):
|
||||||
|
await state.update_data(short=message.text)
|
||||||
|
await state.set_state(AdvForm.full)
|
||||||
|
await message.answer("Теперь введите полную форму сообщения:")
|
||||||
|
|
||||||
|
@router.message(AdvForm.full)
|
||||||
|
async def get_full_text_and_send(message: Message, state: FSMContext, bot: Bot):
|
||||||
|
global current_adv_id, adv_memory
|
||||||
|
|
||||||
|
data = await state.get_data()
|
||||||
|
short_text = data["short"]
|
||||||
|
full_text = message.text
|
||||||
|
|
||||||
|
# Генерируем уникальный ID рассылки
|
||||||
|
adv_id = str(uuid4())
|
||||||
|
|
||||||
|
# Деактивируем старые рассылки
|
||||||
|
for adv in adv_memory.values():
|
||||||
|
adv["is_active"] = False
|
||||||
|
|
||||||
|
# Сохраняем новую рассылку
|
||||||
|
adv_memory[adv_id] = {
|
||||||
|
"text": full_text,
|
||||||
|
"is_active": True
|
||||||
|
}
|
||||||
|
current_adv_id = adv_id
|
||||||
|
|
||||||
|
# Клавиатура
|
||||||
|
kb = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(text="👍 Интересно", callback_data=f"adv:{adv_id}"),
|
||||||
|
InlineKeyboardButton(text="👎 Не интересно", callback_data="adv:no")
|
||||||
|
]
|
||||||
|
])
|
||||||
|
|
||||||
|
# Рассылка краткой формы
|
||||||
|
async with async_session() as session:
|
||||||
|
result = await session.execute(select(User.user_id).where(User.is_subscribed == True))
|
||||||
|
users = result.scalars().all()
|
||||||
|
|
||||||
|
sent = 0
|
||||||
|
for uid in users:
|
||||||
|
try:
|
||||||
|
await bot.send_message(uid, short_text, reply_markup=kb)
|
||||||
|
sent += 1
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
await message.answer(f"📬 Рассылка завершена. Отправлено: {sent}")
|
||||||
|
await state.clear()
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
from aiogram import Router, F
|
||||||
|
from aiogram.types import CallbackQuery
|
||||||
|
from handlers.admin_adv import adv_memory
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
@router.callback_query(F.data.startswith("adv:"))
|
||||||
|
async def handle_adv_click(callback: CallbackQuery):
|
||||||
|
adv_id = callback.data.split(":")[1]
|
||||||
|
|
||||||
|
adv = adv_memory.get(adv_id)
|
||||||
|
if not adv:
|
||||||
|
await callback.answer("❌ Сообщение не найдено", show_alert=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not adv["is_active"]:
|
||||||
|
await callback.answer("⏰ Срок действия сообщения истёк", show_alert=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
await callback.message.answer(adv["text"])
|
||||||
|
await callback.answer("Вот подробности 👇")
|
||||||
|
|
||||||
|
@router.callback_query(F.data == "adv:no")
|
||||||
|
async def handle_not_interested(callback: CallbackQuery):
|
||||||
|
await callback.answer("Спасибо за обратную связь!")
|
||||||
+44
-18
@@ -1,5 +1,7 @@
|
|||||||
from aiogram import Router, F
|
from aiogram import Router, F
|
||||||
|
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 sqlalchemy import select, insert, update
|
from sqlalchemy import select, insert, update
|
||||||
from models import User
|
from models import User
|
||||||
from database import async_session
|
from database import async_session
|
||||||
@@ -7,27 +9,51 @@ from keyboards import get_subscription_keyboard
|
|||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|
||||||
|
# Машина состояний
|
||||||
|
class Register(StatesGroup):
|
||||||
|
waiting_for_fullname = State()
|
||||||
|
waiting_for_study_group = State()
|
||||||
|
|
||||||
@router.message(F.text == "/start")
|
@router.message(F.text == "/start")
|
||||||
async def start_handler(message: Message):
|
async def start_handler(message: Message, state: FSMContext):
|
||||||
async with async_session() as session:
|
user_id = message.from_user.id
|
||||||
|
|
||||||
result = await session.execute(select(User).where(User.user_id == message.from_user.id))
|
async with async_session() as session:
|
||||||
|
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 not user:
|
|
||||||
await session.execute(insert(User).values(user_id=message.from_user.id))
|
|
||||||
await session.commit()
|
|
||||||
text = "Добро пожаловать! Управляйте подпиской:"
|
|
||||||
await message.answer(text, reply_markup=get_subscription_keyboard(user.is_subscribed if user else True))
|
|
||||||
|
|
||||||
@router.callback_query(F.data.in_(["subscribe", "unsubscribe"]))
|
if user:
|
||||||
async def toggle_subscription(callback: CallbackQuery):
|
# Приветствие при повторном запуске
|
||||||
subscribe = callback.data == "subscribe"
|
text = f"👋 Привет, {user.full_name or 'друг'}!\n" \
|
||||||
|
f"Группа: {user.study_group or 'не указана'}\n" \
|
||||||
|
"Используй кнопки ниже, чтобы управлять подпиской:"
|
||||||
|
await message.answer(text, reply_markup=get_subscription_keyboard(user.is_subscribed))
|
||||||
|
else:
|
||||||
|
# Первый запуск — регистрация
|
||||||
|
await state.set_state(Register.waiting_for_fullname)
|
||||||
|
await message.answer("👋 Привет! Пожалуйста, напиши свои ФИО одной строкой:")
|
||||||
|
|
||||||
|
@router.message(Register.waiting_for_fullname)
|
||||||
|
async def get_fullname(message: Message, state: FSMContext):
|
||||||
|
await state.update_data(full_name=message.text)
|
||||||
|
await state.set_state(Register.waiting_for_study_group)
|
||||||
|
await message.answer("Теперь укажи номер своей группы (например, Б11-111):")
|
||||||
|
|
||||||
|
@router.message(Register.waiting_for_study_group)
|
||||||
|
async def get_study_group(message: Message, state: FSMContext):
|
||||||
|
data = await state.get_data()
|
||||||
|
full_name = data["full_name"]
|
||||||
|
study_group = message.text
|
||||||
|
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
await session.execute(
|
await session.execute(insert(User).values(
|
||||||
update(User)
|
user_id=message.from_user.id,
|
||||||
.where(User.user_id == callback.from_user.id)
|
full_name=full_name,
|
||||||
.values(is_subscribed=subscribe)
|
study_group=study_group,
|
||||||
)
|
is_subscribed=True
|
||||||
|
))
|
||||||
await session.commit()
|
await session.commit()
|
||||||
await callback.message.edit_reply_markup(reply_markup=get_subscription_keyboard(subscribe))
|
|
||||||
await callback.answer("Вы подписались!" if subscribe else "Вы отписались.")
|
await message.answer(f"✅ Регистрация завершена!\nФИО: {full_name}\nГруппа: {study_group}")
|
||||||
|
await message.answer("Теперь ты можешь управлять подпиской ниже 👇", reply_markup=get_subscription_keyboard(True))
|
||||||
|
await state.clear()
|
||||||
|
|||||||
@@ -1,23 +1,33 @@
|
|||||||
from aiogram import Bot, Dispatcher
|
from aiogram import Bot, Dispatcher
|
||||||
from aiogram.fsm.storage.memory import MemoryStorage
|
from aiogram.fsm.storage.memory import MemoryStorage
|
||||||
from handlers import user, admin
|
|
||||||
from database import engine, Base
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
from handlers import user, admin, admin_adv, adv_callbacks
|
||||||
|
from database import engine, Base
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def on_startup():
|
async def on_startup():
|
||||||
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.create_all)
|
await conn.run_sync(Base.metadata.create_all)
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
bot = Bot(token=os.getenv("BOT_TOKEN"))
|
bot = Bot(token=os.getenv("BOT_TOKEN"))
|
||||||
dp = Dispatcher(storage=MemoryStorage())
|
dp = Dispatcher(storage=MemoryStorage())
|
||||||
dp.include_routers(user.router, admin.router)
|
dp.include_routers(
|
||||||
|
user.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)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
from sqlalchemy import Column, BigInteger, Boolean
|
from sqlalchemy import Column, BigInteger, Boolean, String
|
||||||
from database import Base
|
from database import Base
|
||||||
|
|
||||||
class User(Base):
|
class User(Base):
|
||||||
__tablename__ = "users"
|
__tablename__ = "users"
|
||||||
|
|
||||||
user_id = Column(BigInteger, primary_key=True)
|
user_id = Column(BigInteger, primary_key=True)
|
||||||
|
full_name = Column(String, nullable=False)
|
||||||
|
study_group = Column(String, nullable=False)
|
||||||
is_subscribed = Column(Boolean, default=True)
|
is_subscribed = Column(Boolean, default=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user