mirror of
https://github.com/ada-dmitry/vol_bot.git
synced 2026-09-24 00:30:19 +00:00
init project
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
.env
|
||||||
|
.venv
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?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.10 (vol_bot)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?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
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<?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$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.10 (vol_bot)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
|
||||||
|
from sqlalchemy.orm import declarative_base
|
||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
engine = create_async_engine(os.getenv("DATABASE_URL"))
|
||||||
|
async_session = async_sessionmaker(engine, expire_on_commit=False)
|
||||||
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
from aiogram import Router, F, Bot
|
||||||
|
from aiogram.types import Message
|
||||||
|
from sqlalchemy import select
|
||||||
|
from models import User
|
||||||
|
from database import async_session
|
||||||
|
import os
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
admin_ids = list(map(int, os.getenv("ADMINS").split(",")))
|
||||||
|
|
||||||
|
@router.message(F.text == "/admin")
|
||||||
|
async def admin_panel(message: Message):
|
||||||
|
if message.from_user.id not in admin_ids:
|
||||||
|
return await message.answer("Доступ запрещён.")
|
||||||
|
await message.answer("Команды:\n/stat — статистика\n/broadcast <текст> — рассылка")
|
||||||
|
|
||||||
|
@router.message(F.text.startswith("/stat"))
|
||||||
|
async def stat_handler(message: Message):
|
||||||
|
if message.from_user.id not in admin_ids:
|
||||||
|
return
|
||||||
|
async with async_session() as session:
|
||||||
|
total = await session.scalar(select(User).count())
|
||||||
|
subscribed = await session.scalar(select(User).where(User.is_subscribed).count())
|
||||||
|
await message.answer(f"Всего пользователей: {total}\nПодписаны: {subscribed}")
|
||||||
|
|
||||||
|
@router.message(F.text.startswith("/broadcast "))
|
||||||
|
async def broadcast_handler(message: Message, bot: Bot):
|
||||||
|
if message.from_user.id not in admin_ids:
|
||||||
|
await message.answer("Доступ запрещён.")
|
||||||
|
return
|
||||||
|
|
||||||
|
text = message.text.removeprefix("/broadcast ").strip()
|
||||||
|
if not text:
|
||||||
|
await message.answer("Текст рассылки не найден.")
|
||||||
|
return
|
||||||
|
|
||||||
|
sent = 0
|
||||||
|
failed = 0
|
||||||
|
|
||||||
|
async with async_session() as session:
|
||||||
|
result = await session.execute(select(User.user_id).where(User.is_subscribed))
|
||||||
|
users = result.scalars().all()
|
||||||
|
|
||||||
|
for uid in users:
|
||||||
|
try:
|
||||||
|
await bot.send_message(uid, text)
|
||||||
|
sent += 1
|
||||||
|
except Exception:
|
||||||
|
failed += 1
|
||||||
|
|
||||||
|
await message.answer(f"📬 Рассылка завершена:\nУспешно: {sent}\nОшибок: {failed}")
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
from aiogram import Router, F
|
||||||
|
from aiogram.types import Message, CallbackQuery
|
||||||
|
from sqlalchemy import select, insert, update
|
||||||
|
from models import User
|
||||||
|
from database import async_session
|
||||||
|
from keyboards import get_subscription_keyboard
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
@router.message(F.text == "/start")
|
||||||
|
async def start_handler(message: Message):
|
||||||
|
async with async_session() as session:
|
||||||
|
|
||||||
|
result = await session.execute(select(User).where(User.user_id == message.from_user.id))
|
||||||
|
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"]))
|
||||||
|
async def toggle_subscription(callback: CallbackQuery):
|
||||||
|
subscribe = callback.data == "subscribe"
|
||||||
|
async with async_session() as session:
|
||||||
|
await session.execute(
|
||||||
|
update(User)
|
||||||
|
.where(User.user_id == callback.from_user.id)
|
||||||
|
.values(is_subscribed=subscribe)
|
||||||
|
)
|
||||||
|
await session.commit()
|
||||||
|
await callback.message.edit_reply_markup(reply_markup=get_subscription_keyboard(subscribe))
|
||||||
|
await callback.answer("Вы подписались!" if subscribe else "Вы отписались.")
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
|
||||||
|
def get_subscription_keyboard(subscribed: bool):
|
||||||
|
if subscribed:
|
||||||
|
return InlineKeyboardMarkup(inline_keyboard=[
|
||||||
|
[InlineKeyboardButton(text="❌ Отписаться", callback_data="unsubscribe")]
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
return InlineKeyboardMarkup(inline_keyboard=[
|
||||||
|
[InlineKeyboardButton(text="✅ Подписаться", callback_data="subscribe")]
|
||||||
|
])
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
from aiogram import Bot, Dispatcher
|
||||||
|
from aiogram.fsm.storage.memory import MemoryStorage
|
||||||
|
from handlers import user, admin
|
||||||
|
from database import engine, Base
|
||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
async def on_startup():
|
||||||
|
async with engine.begin() as conn:
|
||||||
|
await conn.run_sync(Base.metadata.create_all)
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
bot = Bot(token=os.getenv("BOT_TOKEN"))
|
||||||
|
dp = Dispatcher(storage=MemoryStorage())
|
||||||
|
dp.include_routers(user.router, admin.router)
|
||||||
|
await on_startup()
|
||||||
|
await dp.start_polling(bot)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from sqlalchemy import Column, BigInteger, Boolean
|
||||||
|
from database import Base
|
||||||
|
|
||||||
|
class User(Base):
|
||||||
|
__tablename__ = "users"
|
||||||
|
|
||||||
|
user_id = Column(BigInteger, primary_key=True)
|
||||||
|
is_subscribed = Column(Boolean, default=True)
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
aiogram~=3.19.0
|
||||||
|
SQLAlchemy~=2.0.40
|
||||||
|
# dotenv~=0.9.9
|
||||||
|
python-dotenv~=1.1.0
|
||||||
Reference in New Issue
Block a user