Перенос части логики на бекенд rest api и изменение логики работы бота так же из за бекенда

This commit is contained in:
Disledg
2024-12-24 16:43:55 +03:00
parent 4151e2a546
commit 379da6cbba
14 changed files with 245 additions and 699 deletions

View File

@@ -1,65 +0,0 @@
import os
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from motor.motor_asyncio import AsyncIOMotorClient
from databases.model import Base
# Настройки PostgreSQL из переменных окружения
POSTGRES_DSN = os.getenv("POSTGRES_URL")
# Создание движка для PostgreSQL
postgres_engine = create_async_engine(POSTGRES_DSN, echo=False)
AsyncSessionLocal = sessionmaker(bind=postgres_engine, class_=AsyncSession, expire_on_commit=False)
# Настройки MongoDB из переменных окружения
MONGO_URI = os.getenv("MONGO_URL")
DATABASE_NAME = os.getenv("DB_NAME")
# Создание клиента MongoDB
mongo_client = AsyncIOMotorClient(MONGO_URI)
mongo_db = mongo_client[DATABASE_NAME]
# Инициализация PostgreSQL
async def init_postgresql():
"""
Инициализация подключения к PostgreSQL.
"""
try:
async with postgres_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
print("PostgreSQL connected.")
except Exception as e:
print(f"Failed to connect to PostgreSQL: {e}")
# Инициализация MongoDB
async def init_mongodb():
"""
Проверка подключения к MongoDB.
"""
try:
# Проверяем подключение к MongoDB
await mongo_client.admin.command("ping")
print("MongoDB connected.")
except Exception as e:
print(f"Failed to connect to MongoDB: {e}")
# Получение сессии PostgreSQL
async def get_postgres_session():
"""
Асинхронный генератор сессий PostgreSQL.
"""
async with AsyncSessionLocal() as session:
yield session
# Закрытие соединений
async def close_connections():
"""
Закрытие всех соединений с базами данных.
"""
# Закрытие PostgreSQL
await postgres_engine.dispose()
print("PostgreSQL connection closed.")
# Закрытие MongoDB
mongo_client.close()
print("MongoDB connection closed.")

View File

@@ -1,103 +0,0 @@
import os
from motor.motor_asyncio import AsyncIOMotorClient
import logging
class MongoDBRepository:
def __init__(self):
# Настройки MongoDB из переменных окружения
mongo_uri = os.getenv("MONGO_URL")
database_name = os.getenv("DB_NAME")
server_collection = os.getenv("SERVER_COLLECTION", "servers")
plan_collection = os.getenv("PLAN_COLLECTION", "plans")
# Подключение к базе данных и коллекциям
self.client = AsyncIOMotorClient(mongo_uri)
self.db = self.client[database_name]
self.collection = self.db[server_collection] # Коллекция серверов
self.plans_collection = self.db[plan_collection] # Коллекция тарифных планов
self.logger = logging.getLogger(__name__)
async def add_subscription_plan(self, plan_data):
"""Добавляет новый тарифный план в коллекцию."""
result = await self.plans_collection.insert_one(plan_data)
self.logger.debug(f"Тарифный план добавлен с ID: {result.inserted_id}")
return result.inserted_id
async def get_subscription_plan(self, plan_name):
"""Получает тарифный план по его имени."""
plan = await self.plans_collection.find_one({"name": plan_name})
if plan:
self.logger.debug(f"Найден тарифный план: {plan}")
else:
self.logger.error(f"Тарифный план {plan_name} не найден.")
return plan
async def add_server(self, server_data):
"""Добавляет новый VPN сервер в коллекцию."""
result = await self.collection.insert_one(server_data)
self.logger.debug(f"VPN сервер добавлен с ID: {result.inserted_id}")
return result.inserted_id
async def get_server(self, server_name: str):
"""Получает сервер VPN по его ID."""
server = await self.collection.find_one({"server.name": server_name})
if server:
self.logger.debug(f"Найден VPN сервер: {server}")
else:
self.logger.debug(f"VPN сервер с ID {server_name} не найден.")
return server
async def get_server_with_least_clients(self):
"""Возвращает сервер с наименьшим количеством подключенных клиентов."""
pipeline = [
{
"$addFields": {
"current_clients": {"$size": {"$ifNull": ["$clients", []]}}
}
},
{
"$sort": {"current_clients": 1}
},
{
"$limit": 1
}
]
result = await self.collection.aggregate(pipeline).to_list(length=1)
if result:
server = result[0]
self.logger.debug(f"Найден сервер с наименьшим количеством клиентов: {server}")
return server
else:
self.logger.debug("Не найдено серверов.")
return None
async def update_server(self, server_id, update_data):
"""Обновляет данные VPN сервера."""
result = await self.collection.update_one({"_id": server_id}, {"$set": update_data})
if result.matched_count > 0:
self.logger.debug(f"VPN сервер с ID {server_id} обновлен.")
else:
self.logger.debug(f"VPN сервер с ID {server_id} не найден.")
return result.matched_count > 0
async def delete_server(self, server_id):
"""Удаляет VPN сервер по его ID."""
result = await self.collection.delete_one({"_id": server_id})
if result.deleted_count > 0:
self.logger.debug(f"VPN сервер с ID {server_id} удален.")
else:
self.logger.debug(f"VPN сервер с ID {server_id} не найден.")
return result.deleted_count > 0
async def list_servers(self):
"""Возвращает список всех VPN серверов."""
servers = await self.collection.find().to_list(length=1000) # Получить до 1000 серверов (можно настроить)
self.logger.debug(f"Найдено {len(servers)} VPN серверов.")
return servers
async def close_connection(self):
"""Закрывает подключение к базе данных MongoDB."""
self.client.close()
self.logger.debug("Подключение к MongoDB закрыто.")

View File

@@ -1,211 +0,0 @@
from databases.model import User, Subscription, Transaction, Administrators
from sqlalchemy.future import select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy import desc
from dateutil.relativedelta import relativedelta
from datetime import datetime
from utils.panel import PanelInteraction
from databases.mongodb import MongoDBRepository
import random
import string
import logging
import asyncio
class DatabaseManager:
def __init__(self, session_generator):
"""
Инициализация с асинхронным генератором сессий (например, get_postgres_session).
"""
self.session_generator = session_generator
self.logger = logging.getLogger(__name__)
self.mongo_repo = MongoDBRepository()
async def create_user(self, telegram_id: int):
"""
Создаёт нового пользователя, если его нет.
"""
async for session in self.session_generator():
try:
username = self.generate_string(6)
result = await session.execute(select(User).where(User.telegram_id == int(telegram_id)))
user = result.scalars().first()
if not user:
new_user = User(telegram_id=int(telegram_id), username=username)
session.add(new_user)
await session.commit()
return new_user
return user
except SQLAlchemyError as e:
self.logger.error(f"Ошибка при создании пользователя {telegram_id}: {e}")
await session.rollback()
return "ERROR"
async def get_user_by_telegram_id(self, telegram_id: int):
"""
Возвращает пользователя по Telegram ID.
"""
async for session in self.session_generator():
try:
result = await session.execute(select(User).where(User.telegram_id == telegram_id))
return result.scalars().first()
except SQLAlchemyError as e:
self.logger.error(f"Ошибка при получении пользователя {telegram_id}: {e}")
return None
async def add_transaction(self, user_id: int, amount: float):
"""
Добавляет транзакцию для пользователя.
"""
async for session in self.session_generator():
try:
transaction = Transaction(user_id=user_id, amount=amount)
session.add(transaction)
await session.commit()
except SQLAlchemyError as e:
self.logger.error(f"Ошибка добавления транзакции для пользователя {user_id}: {e}")
await session.rollback()
async def update_balance(self, telegram_id: int, amount: float):
"""
Обновляет баланс пользователя и добавляет транзакцию.
"""
async for session in self.session_generator():
try:
result = await session.execute(select(User).where(User.telegram_id == telegram_id))
user = result.scalars().first()
if user:
user.balance += int(amount)
await self.add_transaction(user.id, amount)
await session.commit()
else:
self.logger.warning(f"Пользователь с Telegram ID {telegram_id} не найден.")
return "ERROR"
except SQLAlchemyError as e:
self.logger.error(f"Ошибка при обновлении баланса: {e}")
await session.rollback()
return "ERROR"
async def last_subscription(self, user_id: int):
"""
Возвращает список подписок пользователя.
"""
async for session in self.session_generator():
try:
result = await session.execute(
select(Subscription)
.where(Subscription.user_id == user_id)
.order_by(desc(Subscription.created_at))
)
return result.scalars().all()
except SQLAlchemyError as e:
self.logger.error(f"Ошибка при получении последней подписки пользователя {user_id}: {e}")
return "ERROR"
async def last_transaction(self, user_id: int):
"""
Возвращает список транзакций пользователя.
"""
async for session in self.session_generator():
try:
result = await session.execute(
select(Transaction)
.where(Transaction.user_id == user_id)
.order_by(desc(Transaction.created_at))
)
transactions = result.scalars().all()
return transactions
except SQLAlchemyError as e:
self.logger.error(f"Ошибка при получении транзакций пользователя {user_id}: {e}")
return "ERROR"
async def buy_sub(self, telegram_id: str, plan_id: str):
async for session in self.session_generator():
try:
result = await self.create_user(telegram_id)
if not result:
self.logger.error(f"Пользователь с Telegram ID {telegram_id} не найден.")
return "ERROR"
# Получение тарифного плана из MongoDB
plan = await self.mongo_repo.get_subscription_plan(plan_id)
if not plan:
self.logger.error(f"Тарифный план {plan_id} не найден.")
return "ERROR"
# Проверка достаточности средств
cost = int(plan["price"])
if result.balance < cost:
self.logger.error(f"Недостаточно средств у пользователя {telegram_id} для покупки плана {plan_id}.")
return "INSUFFICIENT_FUNDS"
# Списываем средства
result.balance -= cost
# Создаем подписку
expiry_date = datetime.utcnow() + relativedelta(months=plan["duration_months"])
server = await self.mongo_repo.get_server_with_least_clients()
self.logger.info(f"Выбран сервер для подписки: {server}")
new_subscription = Subscription(
user_id=result.id,
vpn_server_id=str(server['server']["name"]),
plan=plan_id,
expiry_date=expiry_date
)
session.add(new_subscription)
# Попытка добавить пользователя на сервер
# Получаем информацию о пользователе
user = result # так как result уже содержит пользователя
if not user:
self.logger.error(f"Не удалось найти пользователя для добавления на сервер.")
await session.rollback()
return "ERROR"
# Получаем сервер из MongoDB
server_data = await self.mongo_repo.get_server(new_subscription.vpn_server_id)
if not server_data:
self.logger.error(f"Не удалось найти сервер с ID {new_subscription.vpn_server_id}.")
await session.rollback()
return "ERROR"
server_info = server_data['server']
url_base = f"https://{server_info['ip']}:{server_info['port']}/{server_info['secretKey']}"
login_data = {
'username': server_info['login'],
'password': server_info['password'],
}
panel = PanelInteraction(url_base, login_data, self.logger,server_info['certificate']['data'])
expiry_date_iso = new_subscription.expiry_date.isoformat()
# Добавляем на сервер
response = await panel.add_client(user.id, expiry_date_iso, user.username)
if response != "OK":
self.logger.error(f"Ошибка при добавлении клиента {telegram_id} на сервер: {response}")
# Если не получилось добавить на сервер, откатываем транзакцию
await session.rollback()
return "ERROR"
# Если мы здесь - значит и подписка, и добавление на сервер успешны
await session.commit()
self.logger.info(f"Подписка успешно оформлена для пользователя {telegram_id} на план {plan_id} и клиент добавлен на сервер.")
return "OK"
except SQLAlchemyError as e:
self.logger.error(f"Ошибка при покупке подписки {plan_id} для пользователя {telegram_id}: {e}")
await session.rollback()
return "ERROR"
except Exception as e:
self.logger.error(f"Непредвиденная ошибка: {e}")
await session.rollback()
return "ERROR"
@staticmethod
def generate_string(length):
"""
Генерирует случайную строку заданной длины.
"""
characters = string.ascii_lowercase + string.digits
return ''.join(random.choices(characters, k=length))

76
docker-compose.yml Normal file
View File

@@ -0,0 +1,76 @@
networks:
bot_network:
driver: bridge
volumes:
mongo_data:
postgres_data:
logs_data:
services:
# MongoDB
mongodb:
networks:
- bot_network
image: mongo:latest
container_name: mongodb
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: itOj4CE2miKR
volumes:
- mongo_data:/data/db
# PostgreSQL
postgres:
networks:
- bot_network
image: postgres:latest
container_name: postgres
ports:
- "5432:5432"
environment:
POSTGRES_USER: AH3J9GSPBYOP
POSTGRES_PASSWORD: uPS9?y~mcu2
POSTGRES_DB: bot_db
volumes:
- postgres_data:/var/lib/postgresql/data
# Бэкенд (FastAPI)
backend:
networks:
- bot_network
build:
context: ./Flask-Backend-All
dockerfile: Dockerfile
container_name: backend
environment:
POSTGRES_URL: "postgresql+asyncpg://AH3J9GSPBYOP:uPS9?y~mcu2@postgres:5432/bot_db"
MONGO_URL: "mongodb://root:itOj4CE2miKR@mongodb:27017"
DB_NAME: "MongoDBSub&Ser"
SERVER_COLLECTION: "servers"
PLAN_COLLECTION: "plans"
BASE_URL: "http://backend:8000"
ports:
- "8000:8000"
depends_on:
- postgres
- mongodb
# Telegram Bot
bot:
networks:
- bot_network
build:
context: ./Lark_VPN_Bot
dockerfile: Dockerfile
container_name: telegram_bot
environment:
BASE_URL: "http://backend:8000"
TOKEN: "8104061818:AAGJ6H1PTFmfJm-_mZqGv7EnHxGl4dZndnU"
volumes:
- logs_data:/app/logs
depends_on:
- backend

View File

@@ -1,37 +0,0 @@
version: '3.8'
networks:
bot_network:
driver: bridge
volumes:
mongo_data:
postgres_data:
services:
mongodb:
networks:
- bot_network
image: mongo:latest
container_name: mongodb
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: itOj4CE2miKR
volumes:
- mongo_data:/data/db
postgres:
networks:
- bot_network
image: postgres:latest
container_name: postgres
ports:
- "5432:5432"
environment:
POSTGRES_USER: AH3J9GSPBYOP
POSTGRES_PASSWORD: uPS9?y~mcu2
POSTGRES_DB: bot_db
volumes:
- postgres_data:/var/lib/postgresql/data

View File

@@ -1,27 +0,0 @@
version: '3.8'
networks:
bot_network:
external: true
volumes:
logs_data:
services:
bot:
networks:
- bot_network
build:
context: ..
dockerfile: docker/Dockerfile
container_name: telegram_bot
environment:
TOKEN: "8104061818:AAGJ6H1PTFmfJm-_mZqGv7EnHxGl4dZndnU"
POSTGRES_URL: "postgresql+asyncpg://AH3J9GSPBYOP:uPS9?y~mcu2@postgres:5432/bot_db"
MONGO_URL: "mongodb://root:itOj4CE2miKR@mongodb:27017"
DB_NAME: "MongoDBSub&Ser"
SERVER_COLLECTION: "servers"
PLAN_COLLECTION: "plans"
volumes:
- logs_data:/app/logs
command: ["python", "main.py"]

View File

@@ -1,12 +1,17 @@
from aiogram import types, Dispatcher
from aiogram.filters import Command
from databases.postgresql import DatabaseManager
from databases.model import User, Subscription, Transaction, Administrators
from databases.db_config import get_postgres_session
import aiohttp
from instences.config import BASE_URL_FASTAPI
from keyboard.keyboards import subhist_keyboard,confirm_popup_keyboard,tarif_confirm_keyboard, popup_keyboard, main_keyboard,faq_keyboard, account_keyboard, buy_keyboard,balance_keyboard,guide_keyboard,tarif_Lark_keyboard,tarif_Lark_pro_keyboard,tranhist_keyboard
# Инициализируем менеджер базы данных
db_manager = DatabaseManager(get_postgres_session)
async def call_api(method, endpoint, data=None):
async with aiohttp.ClientSession() as session:
url = f"{BASE_URL_FASTAPI}{endpoint}"
async with session.request(method, url, json=data) as response:
if response.status in {200, 201}:
return await response.json()
return "ERROR"
async def popup_command(message: types.Message):
"""
@@ -48,46 +53,27 @@ async def start_callback_handler(callback: types.CallbackQuery):
)
async def profile_callback_handler(callback: types.CallbackQuery):
"""
Обработчик callback_query с data="profile".
"""
user = await db_manager.create_user(telegram_id=callback.from_user.id)
if user == "ERROR":
await callback.message.answer(
"Произошла ошибка, попробуйте позже или свяжитесь с администрацией."
)
user_data = await call_api("POST", "/user/create", {"telegram_id": callback.from_user.id})
if not user_data:
await callback.message.answer("Произошла ошибка, попробуйте позже.")
await callback.answer()
return
if user:
text = f"""Ваш профиль:\nID: {user.username}\nБаланс: {user.balance}"""
await callback.message.edit_text(
text,
reply_markup=account_keyboard()
)
else:
await callback.message.edit_text("Вы еще не зарегистрированы.")
text = f"Ваш профиль:\nID: {user_data['username']}\nБаланс: {user_data['balance']}"
await callback.message.edit_text(text, reply_markup=account_keyboard())
await callback.answer()
async def balance_callback_handler(callback: types.CallbackQuery):
"""
Обработчик callback_query с data="balance".
"""
user = await db_manager.create_user(telegram_id=callback.from_user.id)
if user == "ERROR":
await callback.message.answer(
"Произошла ошибка, попробуйте позже или свяжитесь с администрацией."
)
user_data = await call_api("GET", f"/user/{callback.from_user.id}")
if not user_data:
await callback.message.answer("Вы еще не зарегистрированы.")
await callback.answer()
return
if user:
await callback.message.edit_text(
f"Ваш баланс: {user.balance} ₽. Выберите сумму для пополнения 🐥",
reply_markup=balance_keyboard()
)
else:
await callback.message.edit_text("Вы еще не зарегистрированы.")
await callback.message.edit_text(
f"Ваш баланс: {user_data['balance']} ₽. Выберите сумму для пополнения 🐥",
reply_markup=balance_keyboard()
)
await callback.answer()
@@ -95,7 +81,7 @@ async def popup_callback_handler(callback: types.CallbackQuery):
"""
Обработчик callback_query с data="popup".
"""
user = await db_manager.create_user(telegram_id=callback.from_user.id)
user = await call_api("POST", "/user/create", {"telegram_id": callback.from_user.id})
if user == "ERROR":
await callback.message.answer(
"Произошла ошибка, попробуйте позже или свяжитесь с администрацией."
@@ -113,68 +99,41 @@ async def popup_callback_handler(callback: types.CallbackQuery):
await callback.answer()
async def tranhist_callback_handler(callback: types.CallbackQuery):
"""
Обработчик callback_query с data="tranhist".
"""
user = await db_manager.create_user(callback.from_user.id)
trans = await db_manager.last_transaction(user.id)
if trans == "ERROR":
await callback.message.answer(
"Произошла ошибка, попробуйте позже или свяжитесь с администрацией."
)
user_data = await call_api("GET", f"/user/{callback.from_user.id}")
if not user_data:
await callback.message.edit_text("Вы еще не зарегистрированы.")
await callback.answer()
return
if not trans:
await callback.message.edit_text(
"У вас нет транзакций. Пожалуйста, пополните баланс.",
reply_markup=tranhist_keyboard()
)
transactions = await call_api("GET", f"/user/{user_data['id']}/transactions")
if not transactions:
await callback.message.edit_text("У вас нет транзакций.", reply_markup=tranhist_keyboard())
await callback.answer()
return
result = "Ваши транзакции:\n"
for count, tran in enumerate(trans, start=1):
result += f"{count}. Сумма: {tran.amount}, Дата: {tran.created_at}\n"
await callback.message.edit_text(
result,
reply_markup=tranhist_keyboard()
)
for count, tran in enumerate(transactions, start=1):
result += f"{count}. Сумма: {tran['amount']}, Дата: {tran['created_at']}\n"
await callback.message.edit_text(result, reply_markup=tranhist_keyboard())
await callback.answer()
async def subhist_callback_handler(callback: types.CallbackQuery):
"""
Обработчик callback_query с data="subhist".
"""
user = await db_manager.create_user(callback.from_user.id)
subs = await db_manager.last_subscription(user.id)
if subs == "ERROR":
await callback.message.answer(
"Произошла ошибка, попробуйте позже или свяжитесь с администрацией."
)
await callback.answer()
return
if subs is None:
await callback.message.edit_text(
f"Ты хули тут забыл, ты ж не покупаешь нихуя",
reply_markup=account_keyboard()
)
await callback.answer()
return
result = ""
count = 0
for sub in subs:
if count > 0:
result += f"Последняя подписка истекает: {sub.expiry_date}\n"
count += 1
result += f"{count}. Истекла {sub.expiry_date}"
count += 1
if subs:
await callback.message.edit_text(
result,
reply_markup=account_keyboard()
)
else:
user_data = await call_api("GET", f"/user/{callback.from_user.id}")
if not user_data:
await callback.message.edit_text("Вы еще не зарегистрированы.")
await callback.answer()
return
subscriptions = await call_api("GET", f"/subscription/{user_data['id']}/last")
if not subscriptions:
await callback.message.edit_text("У вас нет активных подписок.", reply_markup=account_keyboard())
await callback.answer()
return
result = "Ваши подписки:\n"
for count, sub in enumerate(subscriptions, start=1):
result += f"{count}. Тариф: {sub['plan']}, Истекает: {sub['expiry_date']}\n"
await callback.message.edit_text(result, reply_markup=account_keyboard())
await callback.answer()
async def buy_subscription_callback_handler(callback: types.CallbackQuery):
@@ -260,7 +219,7 @@ async def popup_confirm_callback_handler(callback: types.CallbackQuery):
"""
data = callback.data.split(":")
popup_info = data[1]
result = await db_manager.update_balance(callback.from_user.id,popup_info)
result = await call_api("POST", f"/user/{callback.from_user.id}/balance", {"telegram_id": callback.from_user.id, "amount": popup_info})
if result == "ERROR":
await callback.message.answer(
"Произошла ошибка, попробуйте позже или свяжитесь с администрацией."
@@ -271,40 +230,16 @@ async def popup_confirm_callback_handler(callback: types.CallbackQuery):
await callback.message.edit_text(text=text, reply_markup=confirm_popup_keyboard())
async def confirm_callback_handler(callback: types.CallbackQuery):
"""
Обработчик подтверждения покупки тарифа.
"""
tariff_info = callback.data.split(":")[1].split("_")
tariff_name = tariff_info[0]
tariff_class = tariff_info[1]
tariff_amount = int(tariff_info[2])
data = callback.data.split(":")[1]
tariff_info = data.split("_")
plan_id = f"{tariff_info[0]}_{tariff_info[1]}_{tariff_info[2]}"
result = await call_api("POST", "/subscription/buy", {"telegram_id": callback.from_user.id, "plan_id": plan_id})
sub = await db_manager.buy_sub(callback.from_user.id, f"{tariff_name}_{tariff_class}_{tariff_amount}")
if sub == "ERROR":
await callback.message.answer(
"Произошла ошибка, попробуйте позже или свяжитесь с администрацией."
)
await callback.answer()
return
elif sub == "INSUFFICIENT_FUNDS":
await callback.message.answer(
"Произошла ошибка, не достаточно средств на балансе."
)
await callback.answer()
return
add_to_server = await db_manager.add_to_server(callback.from_user.id)
if add_to_server == "ERROR":
await callback.message.answer(
"Произошла ошибка, попробуйте позже или свяжитесь с администрацией."
)
await callback.answer()
return
# Текст подтверждения на основе тарифа
months_text = f"{tariff_amount} месяцев" if tariff_amount > 1 else f"{tariff_amount} месяц"
text = f"Вы успешно оформили тариф {tariff_name} на {months_text}. Спасибо за покупку!"
await callback.message.edit_text(text=text)
if result and result.get("message"):
await callback.message.edit_text(f"Подписка успешно оформлена!")
else:
await callback.message.edit_text("Произошла ошибка при оформлении подписки.")
await callback.answer()
def register_handlers(dp: Dispatcher):
"""

4
instences/config.py Normal file
View File

@@ -0,0 +1,4 @@
import os
# Настройки PostgreSQL из переменных окружения
BASE_URL_FASTAPI = os.getenv("BASE_URL")

View File

@@ -1,140 +1,123 @@
from aiogram.utils.keyboard import InlineKeyboardBuilder
from aiogram.types import InlineKeyboardButton
from aiogram import types, Dispatcher
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery
from aiogram.filters import Command
# Главное меню клавиатура
def main_menu_keyboard():
keyboard = InlineKeyboardMarkup()
keyboard.add(InlineKeyboardButton("Личный кабинет", callback_data="main:personal"))
keyboard.add(InlineKeyboardButton("FAQ", callback_data="main:faq"))
keyboard.add(InlineKeyboardButton("О нас", callback_data="main:about"))
return keyboard
def main_keyboard():
"""
База
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Профиль", callback_data="profile"))
builder.row(InlineKeyboardButton(text="FAQ", callback_data="faq"))
builder.row(InlineKeyboardButton(text="О нас", url="https://www.youtube.com/watch?v=Zirn-CKck-c"))
return builder.as_markup()
# Личный кабинет клавиатура
def personal_menu_keyboard():
keyboard = InlineKeyboardMarkup()
keyboard.add(InlineKeyboardButton("Баланс", callback_data="personal:balance"))
keyboard.add(InlineKeyboardButton("Приобрести подписку", callback_data="personal:subscribe"))
keyboard.add(InlineKeyboardButton("Руководство по использованию", callback_data="personal:guide"))
keyboard.add(InlineKeyboardButton("Назад", callback_data="back:main"))
return keyboard
def account_keyboard():
"""
Аккаунт
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Баланс", callback_data="balance"))
builder.row(InlineKeyboardButton(text="Приобрести подписку", callback_data="buy_subscription"))
builder.row(InlineKeyboardButton(text="Руководство по подключению", callback_data="guide"))
builder.row(InlineKeyboardButton(text="Назад", callback_data="base"))
return builder.as_markup()
# Подписка меню клавиатура
def subscribe_menu_keyboard():
keyboard = InlineKeyboardMarkup()
keyboard.add(InlineKeyboardButton("Lark", callback_data="subscribe:lark"))
keyboard.add(InlineKeyboardButton("Lark Pro", callback_data="subscribe:lark_pro"))
keyboard.add(InlineKeyboardButton("О тарифах", callback_data="subscribe:about_tariffs"))
keyboard.add(InlineKeyboardButton("Назад", callback_data="back:personal"))
return keyboard
# Тарифы Lark клавиатура
def lark_tariffs_keyboard():
keyboard = InlineKeyboardMarkup()
keyboard.add(InlineKeyboardButton("Lark 1 месяц", callback_data="lark:1"))
keyboard.add(InlineKeyboardButton("Lark 6 месяцев", callback_data="lark:6"))
keyboard.add(InlineKeyboardButton("Lark 12 месяцев", callback_data="lark:12"))
keyboard.add(InlineKeyboardButton("Назад", callback_data="back:subscribe"))
return keyboard
def buy_keyboard():
"""
Приобрести подписку
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Тариф Lark", callback_data="subs"))
builder.row(InlineKeyboardButton(text="Тариф Lark Pro", callback_data="subs_pro"))
builder.row(InlineKeyboardButton(text="О тарифах", url="https://t.me/proxylark/19"))
builder.row(InlineKeyboardButton(text="Назад", callback_data="profile"))
return builder.as_markup()
# Тарифы Lark Pro клавиатура
def lark_pro_tariffs_keyboard():
keyboard = InlineKeyboardMarkup()
keyboard.add(InlineKeyboardButton("Lark Pro 1 месяц", callback_data="lark_pro:1"))
keyboard.add(InlineKeyboardButton("Lark Pro 6 месяцев", callback_data="lark_pro:6"))
keyboard.add(InlineKeyboardButton("Lark Pro 12 месяцев", callback_data="lark_pro:12"))
keyboard.add(InlineKeyboardButton("Назад", callback_data="back:subscribe"))
return keyboard
def subhist_keyboard():
"""
Подписки
"""
builder = InlineKeyboardBuilder()
builder.button(text="Назад", callback_data="profile")
return builder.as_markup()
# Руководство меню клавиатура
def guide_menu_keyboard():
keyboard = InlineKeyboardMarkup()
keyboard.add(InlineKeyboardButton("iOS, Android", callback_data="guide:ios_android"))
keyboard.add(InlineKeyboardButton("Windows, Macintosh", callback_data="guide:windows_mac"))
keyboard.add(InlineKeyboardButton("Назад", callback_data="back:personal"))
return keyboard
def popup_keyboard():
"""
Пополнение
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="200₽", callback_data="popup:200"),InlineKeyboardButton(text="500₽", callback_data="popup:500"))
builder.row(InlineKeyboardButton(text="1000₽", callback_data="popup:1000"),InlineKeyboardButton(text="2000₽", callback_data="popup:2000"))
builder.row(InlineKeyboardButton(text="3000₽", callback_data="popup:3000"),InlineKeyboardButton(text="5000₽", callback_data="popup:5000"))
builder.row(InlineKeyboardButton(text="Назад", callback_data="balance"))
return builder.as_markup()
async def start_command(message: types.Message):
"""Обработчик команды /start."""
await message.answer("Главное меню", reply_markup=main_menu_keyboard())
def balance_keyboard():
"""
Баланс
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Пополнение", callback_data="popup"))
builder.row(InlineKeyboardButton(text="История транзакций", callback_data="tranhist"))
builder.row(InlineKeyboardButton(text="Назад", callback_data="profile"))
return builder.as_markup()
async def callback_handler(callback: CallbackQuery):
"""Обработчик callback."""
data = callback.data.split(":")
action = data[0]
sub_action = data[1] if len(data) > 1 else None
def tarif_Lark_keyboard():
"""
Тариф Lark
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Тариф Lark 1 Месяц", callback_data="Lark:Standart:1"))
builder.row(InlineKeyboardButton(text="Тариф Lark 6 Месяц", callback_data="Lark:Standart:6"))
builder.row(InlineKeyboardButton(text="Тариф Lark 12 Месяц", callback_data="Lark:Standart:12"))
builder.row(InlineKeyboardButton(text="Назад", callback_data="buy_subscription"))
return builder.as_markup()
if action == "main":
if sub_action == "personal":
await callback.message.edit_text("Личный кабинет", reply_markup=personal_menu_keyboard())
elif sub_action == "faq":
await callback.message.edit_text("FAQ", reply_markup=InlineKeyboardMarkup().add(
InlineKeyboardButton("Назад", callback_data="back:main")))
elif sub_action == "about":
await callback.message.edit_text("Наш сайт: {URL}", reply_markup=InlineKeyboardMarkup().add(
InlineKeyboardButton("Назад", callback_data="back:main")))
def tarif_Lark_pro_keyboard():
"""
Тариф Lark Pro
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Тариф Lark Pro 1 Месяц", callback_data="Lark:Pro:1"))
builder.row(InlineKeyboardButton(text="Тариф Lark Pro 6 Месяц", callback_data="Lark:Pro:6"))
builder.row(InlineKeyboardButton(text="Тариф Lark Pro 12 Месяц", callback_data="Lark:Pro:12"))
builder.row(InlineKeyboardButton(text="Назад", callback_data="buy_subscription"))
return builder.as_markup()
elif action == "personal":
if sub_action == "balance":
await callback.message.edit_text("Баланс:", reply_markup=InlineKeyboardMarkup().add(
InlineKeyboardButton("Пополнение", callback_data="balance:topup"),
InlineKeyboardButton("История транзакций", callback_data="balance:history"),
InlineKeyboardButton("Назад", callback_data="back:personal")
))
elif sub_action == "subscribe":
await callback.message.edit_text("Выберите подписку:", reply_markup=subscribe_menu_keyboard())
elif sub_action == "guide":
await callback.message.edit_text("Руководство по использованию", reply_markup=guide_menu_keyboard())
def guide_keyboard():
"""
Руководство по подключению
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="IOS,Android", callback_data="mob"))
builder.row(InlineKeyboardButton(text="Windows,MacOS", callback_data="pc"))
builder.row(InlineKeyboardButton(text="Назад", callback_data="profile"))
return builder.as_markup()
elif action == "subscribe":
if sub_action == "lark":
await callback.message.edit_text("Тарифы Lark", reply_markup=lark_tariffs_keyboard())
elif sub_action == "lark_pro":
await callback.message.edit_text("Тарифы Lark Pro", reply_markup=lark_pro_tariffs_keyboard())
elif sub_action == "about_tariffs":
await callback.message.edit_text("О тарифах", reply_markup=InlineKeyboardMarkup().add(
InlineKeyboardButton("Назад", callback_data="back:subscribe")
))
elif action == "lark":
await callback.message.edit_text(f"Вы выбрали тариф Lark {sub_action} месяцев.", reply_markup=InlineKeyboardMarkup().add(
InlineKeyboardButton("Подтвердить", callback_data=f"confirm:lark:{sub_action}"),
InlineKeyboardButton("Отменить", callback_data="back:subscribe")
))
# def about_tarifs_keyboard():
# """
# О тарифах
# """
# builder = InlineKeyboardBuilder()
# builder.row(InlineKeyboardButton(text="Назад", callback_data="buy_subscription"))
# return builder.as_markup()
elif action == "lark_pro":
await callback.message.edit_text(f"Вы выбрали тариф Lark Pro {sub_action} месяцев.", reply_markup=InlineKeyboardMarkup().add(
InlineKeyboardButton("Подтвердить", callback_data=f"confirm:lark_pro:{sub_action}"),
InlineKeyboardButton("Отменить", callback_data="back:subscribe")
))
elif action == "back":
if sub_action == "main":
await callback.message.edit_text("Главное меню", reply_markup=main_menu_keyboard())
elif sub_action == "personal":
await callback.message.edit_text("Личный кабинет", reply_markup=personal_menu_keyboard())
elif sub_action == "subscribe":
await callback.message.edit_text("Выберите подписку:", reply_markup=subscribe_menu_keyboard())
def faq_keyboard():
"""
FAQ
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Назад", callback_data="base"))
return builder.as_markup()
await callback.answer()
def tranhist_keyboard():
"""
История транзакций
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Назад",callback_data="balance"))
return builder.as_markup()
def tarif_confirm_keyboard(name,amount,classif):
"""
Подтверждение покупки тарифа
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Подтвердить", callback_data=f"confirm:{name}_{classif}_{amount}"))
builder.row(InlineKeyboardButton(text="Отменить",callback_data="buy_subscription"))
return builder.as_markup()
def confirm_popup_keyboard():
"""
Подтверждение пополнения
"""
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Теперь иди нахуй", callback_data="balance"))
return builder.as_markup()
def register_handlers(dp: Dispatcher):
dp.message.register(start_command, Command("start"))
dp.callback_query.register(callback_handler)

View File

@@ -1,7 +1,6 @@
import os
import asyncio
from aiogram import Bot, Dispatcher
from databases.db_config import init_postgresql, init_mongodb, close_connections
from aiogram.types import BotCommand
from Middleware.anti_spam_middleware import AntiSpamMiddleware
import logging
@@ -30,14 +29,9 @@ async def set_commands():
async def on_startup():
"""Действия при запуске бота."""
# Инициализация баз данных
await init_mongodb()
await init_postgresql()
# Установка команд бота
await set_commands()
# Настройка логирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info("Бот запущен!")
@@ -45,10 +39,7 @@ async def on_startup():
async def on_shutdown():
"""Действия при остановке бота."""
# Закрытие подключений к базам данных
await close_connections()
# Закрытие сессии бота
await bot.session.close()
print("Бот остановлен.")