Сгенерил базовую модульную структуру спасибо Claude AI
All checks were successful
Build and Push Docker Image / build (push) Successful in 2m15s

This commit is contained in:
Udo Chudo 2025-06-25 00:28:00 +05:00
parent a7d24616d7
commit 755280d7de
15 changed files with 517 additions and 276 deletions

3
bot/__init__.py Normal file
View File

@ -0,0 +1,3 @@
"""
VPN Bot - модульный бот для управления VPN клиентами через XUI панель.
"""

12
bot/config.py Normal file
View File

@ -0,0 +1,12 @@
import os
XUI_FULL_ADDRESS = os.getenv("XUI_FULL_ADDRESS")
XUI_PANEL_NAME = os.getenv("XUI_PANEL_NAME")
XUI_USERNAME = os.getenv("XUI_USERNAME")
XUI_PASSWORD = os.getenv("XUI_PASSWORD")
INBOUND_VLESS_ID = int(os.getenv("INBOUND_VLESS_ID", 15))
INBOUND_SS_ID = int(os.getenv("INBOUND_SS_ID", 2))
SUBSCRIPTION_UUID = os.getenv("SUBSCRIPTION_UUID")
SUB_BASE_URL = f"https://udochudo.ru/{SUBSCRIPTION_UUID}/"
BOT_TOKEN = os.getenv("BOT_TOKEN")
ALLOWED_CHAT_IDS = set(map(int, os.getenv("ALLOWED_CHAT_IDS", "").split(",")))

3
bot/core/__init__.py Normal file
View File

@ -0,0 +1,3 @@
"""
Ядро приложения - создание бота и диспетчера.
"""

23
bot/core/bot.py Normal file
View File

@ -0,0 +1,23 @@
from aiogram import Bot, Dispatcher
from aiogram.enums import ParseMode
from aiogram.client.default import DefaultBotProperties
from bot.config import BOT_TOKEN
from bot.utils.logging import logger
def create_bot() -> Bot:
"""Создание экземпляра бота."""
bot = Bot(
token=BOT_TOKEN,
default=DefaultBotProperties(parse_mode=ParseMode.HTML)
)
logger.info("Бот создан")
return bot
def create_dispatcher() -> Dispatcher:
"""Создание диспетчера."""
dp = Dispatcher()
logger.info("Диспетчер создан")
return dp

3
bot/filters/__init__.py Normal file
View File

@ -0,0 +1,3 @@
"""
Фильтры для обработки сообщений.
"""

11
bot/filters/access.py Normal file
View File

@ -0,0 +1,11 @@
from aiogram.types import Message
from aiogram.filters import BaseFilter
from bot.config import ALLOWED_CHAT_IDS
class AllowedUsersFilter(BaseFilter):
"""Фильтр для проверки разрешенных пользователей."""
async def __call__(self, message: Message) -> bool:
return message.chat.id in ALLOWED_CHAT_IDS

3
bot/handlers/__init__.py Normal file
View File

@ -0,0 +1,3 @@
"""
Обработчики команд и сообщений.
"""

29
bot/handlers/base.py Normal file
View File

@ -0,0 +1,29 @@
from aiogram import types
from aiogram.filters import Command
from bot.utils.logging import logger
async def cmd_start(message: types.Message):
"""Обработчик команды /start."""
await message.answer(
"Привет! Чтобы создать профиль, отправь команду:\n"
"<code>/create Telegram id or username</code>\n\n"
"Например:\n"
"<code>/create udochudo</code>",
reply_markup=None
)
async def cmd_help(message: types.Message):
"""Обработчик команды /help."""
help_text = (
"Доступные команды:\n\n"
"/start или /help - показать это сообщение\n"
"/create <telegram_id> - создать профиль клиента\n"
"/info <telegram_id> - получить информацию о клиенте\n\n"
"Примеры:\n"
"<code>/create udochudo</code>\n"
"<code>/info udochudo</code>"
)
await message.answer(help_text)

74
bot/handlers/client.py Normal file
View File

@ -0,0 +1,74 @@
import json
from aiogram import types
from aiogram.enums import ParseMode
from pyxui.errors import BadLogin
from bot.services.client import ClientService
from bot.utils.logging import logger
class ClientHandlers:
"""Обработчики команд для работы с клиентами."""
def __init__(self, client_service: ClientService):
self.client_service = client_service
async def cmd_create(self, message: types.Message):
"""Обработчик команды /create."""
args = (message.text or "").strip().split(maxsplit=1)
if len(args) < 2:
await message.answer(
"❌ Укажи Telegram ID или username после команды.\n"
"Пример:\n<code>/create udochudo</code>"
)
return
telegram_id = args[1].lstrip("@").strip()
try:
success, result_message = await self.client_service.create_client_profile(telegram_id)
if success:
await message.answer(result_message, parse_mode=ParseMode.HTML)
else:
await message.answer(f"{result_message}")
except BadLogin:
await message.answer("❌ Ошибка: неверный логин или пароль XUI.")
except Exception as e:
logger.error(f"Неожиданная ошибка при создании профиля: {e}")
await message.answer("❌ Произошла ошибка при создании профиля. Попробуйте позже.")
async def cmd_info(self, message: types.Message):
"""Обработчик команды /info."""
args = (message.text or "").strip().split(maxsplit=1)
if len(args) < 2:
await message.answer(
"❌ Укажи Telegram ID или username после команды.\n"
"Пример:\n<code>/info udochudo</code>"
)
return
telegram_id = args[1].lstrip("@").strip()
try:
vless_client, ss_client = self.client_service.get_client_info(telegram_id)
def format_info(client, name):
if not client:
return f"❌ Клиент <b>{name}</b> не найден.\n"
json_info = json.dumps(client, ensure_ascii=False, indent=2)
return f"🔹 <b>{name}</b>:\n<pre>{json_info}</pre>"
response = (
format_info(vless_client, "VLESS") +
"\n\n" +
format_info(ss_client, "Shadowsocks")
)
await message.answer(response, parse_mode=ParseMode.HTML)
except BadLogin:
await message.answer("❌ Ошибка входа в панель XUI.")
except Exception as e:
logger.error(f"Ошибка получения информации: {e}")
await message.answer("❌ Ошибка при получении информации. Проверь лог.")

View File

@ -1,290 +1,61 @@
import json
import logging
import uuid
import asyncio
import aiohttp
import os
from aiogram.filters import Command
from bot.core.bot import create_bot, create_dispatcher
from bot.services.xui import XUIService
from bot.services.client import ClientService
from bot.handlers.base import cmd_start, cmd_help
from bot.handlers.client import ClientHandlers
from bot.filters.access import AllowedUsersFilter
from bot.utils.logging import logger
from aiogram.types import Message
from aiogram import Bot, Dispatcher, types
from aiogram.filters import Command, BaseFilter
from aiogram.enums import ParseMode
from aiogram.client.default import DefaultBotProperties
async def setup_handlers(dp, client_handlers: ClientHandlers):
"""Настройка обработчиков команд."""
# Базовые команды (доступны всем)
dp.message.register(cmd_start, Command(commands=["start"]))
dp.message.register(cmd_help, Command(commands=["help"]))
from pyxui import XUI # type: ignore
from pyxui.errors import BadLogin # type: ignore
# --- Конфигурация и константы ---
XUI_FULL_ADDRESS = os.getenv("XUI_FULL_ADDRESS")
XUI_PANEL_NAME = os.getenv("XUI_PANEL_NAME")
XUI_USERNAME = os.getenv("XUI_USERNAME")
XUI_PASSWORD = os.getenv("XUI_PASSWORD")
INBOUND_VLESS_ID = int(os.getenv("INBOUND_VLESS_ID", 15))
INBOUND_SS_ID = int(os.getenv("INBOUND_SS_ID", 2))
SUBSCRIPTION_UUID = os.getenv("SUBSCRIPTION_UUID")
SUB_BASE_URL = f"https://udochudo.ru/{SUBSCRIPTION_UUID}/"
BOT_TOKEN = os.getenv("BOT_TOKEN")
ALLOWED_CHAT_IDS = set(map(int, os.getenv("ALLOWED_CHAT_IDS", "").split(",")))
# --- Логирование ---
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# --- Инициализация бота и диспетчера ---
bot = Bot(token=BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
dp = Dispatcher()
# --- Экземпляр XUI ---
xui = XUI(full_address=XUI_FULL_ADDRESS, panel=XUI_PANEL_NAME, https=True)
def xui_login():
if xui.session_string:
logger.debug("Уже залогинен в XUI.")
return
try:
xui.login(XUI_USERNAME, XUI_PASSWORD)
logger.info("Успешный вход в XUI.")
except BadLogin:
logger.error("Неверный логин или пароль XUI.")
raise
except Exception as e:
logger.error(f"Ошибка при входе в XUI: {e}")
raise
async def create_shadowsocks_client_via_api(telegram_id: str, password: str) -> bool:
"""Создание Shadowsocks клиента через API XUI с поддержкой повторного логина."""
try:
xui_login()
except Exception as e:
logger.error(f"Ошибка авторизации в XUI: {e}")
return False
ss_email = f"{telegram_id}_ss"
settings_str = json.dumps({
"clients": [{
"method": "chacha20-ietf-poly1305",
"password": password,
"email": ss_email,
"totalGB": 0,
"expiryTime": 0,
"enable": True,
"tgId": telegram_id,
"subId": telegram_id,
"reset": 0
}]
}, separators=(',', ':'))
payload = {
"id": INBOUND_SS_ID,
"settings": settings_str
}
api_url = f"{XUI_FULL_ADDRESS}/xui/API/inbounds/addClient"
# Получаем куки из сессии XUI
cookies = {}
if hasattr(xui, 'session') and xui.session and hasattr(xui.session, 'cookies'):
# Пробуем достать куки в нужном формате
for cookie in xui.session.cookies:
if cookie.name == 'x-ui':
cookies['x-ui'] = cookie.value
break
# Если xui.session_string установлен, используем его как значение cookie x-ui
if xui.session_string:
cookies['x-ui'] = xui.session_string
async with aiohttp.ClientSession() as session:
async def do_request():
async with session.post(
api_url,
json=payload,
cookies=cookies,
headers={
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0"
},
ssl=True
) as resp:
text = await resp.text()
logger.debug(f"Response status: {resp.status}")
logger.debug(f"Response text: {text}")
return resp.status, text
status, text = await do_request()
# Если сессия истекла, пробуем повторно залогиниться
if status == 401 or (status == 200 and "login" in text.lower()):
logger.info("Сессия XUI истекла, повторный вход...")
try:
xui.session_string = None
xui_login()
if hasattr(xui, 'session') and xui.session and hasattr(xui.session, 'cookies'):
cookies = {cookie.name: cookie.value for cookie in xui.session.cookies}
if xui.session_string:
cookies['x-ui'] = xui.session_string
except Exception as e:
logger.error(f"Ошибка повторной авторизации в XUI: {e}")
return False
status, text = await do_request()
if status == 200:
if "successfully" in text.lower() or "success" in text.lower():
logger.info(f"Shadowsocks клиент успешно создан: {ss_email}")
return True
elif not text.strip():
logger.warning(f"Пустой ответ при статусе 200. Проверяем наличие клиента: {ss_email}")
return await verify_client_created(telegram_id, ss_email)
else:
logger.error(f"Неожиданный ответ при создании SS клиента: {text}")
return False
else:
logger.error(f"Ошибка создания Shadowsocks клиента: {status} {text}")
return False
async def verify_client_created(telegram_id: str, ss_email: str) -> bool:
"""Проверка, что клиент существует в inbound SS."""
try:
inbound_info = xui.get_inbound(INBOUND_SS_ID)
if not inbound_info:
logger.error(f"Inbound {INBOUND_SS_ID} не найден.")
return False
settings = json.loads(inbound_info.get('settings', '{}'))
clients = settings.get('clients', [])
for client in clients:
if client.get('email') == ss_email:
logger.info(f"Клиент {ss_email} найден.")
return True
logger.warning(f"Клиент {ss_email} не найден.")
return False
except Exception as e:
logger.error(f"Ошибка при проверке клиента: {e}")
return False
class AllowedUsersFilter(BaseFilter):
async def __call__(self, message: Message) -> bool:
return message.chat.id in ALLOWED_CHAT_IDS
@dp.message(Command(commands=["start","help"]))
async def cmd_start(message: types.Message):
await message.answer(
"Привет! Чтобы создать профиль, отправь команду:\n"
"<code>/create Telegram id or username</code>\n\n"
"Например:\n"
"<code>/create udochudo</code>"
,reply_markup=None)
@dp.message(Command(commands=["info"]))
async def cmd_info(message: types.Message):
args = (message.text or "").strip().split(maxsplit=1)
if len(args) < 2:
await message.answer(
"❌ Укажи Telegram ID или username после команды.\n"
"Пример:\n<code>/info udochudo</code>"
)
return
telegram_id = args[1].lstrip("@").strip()
vless_email = f"{telegram_id}_vl_ssl"
ss_email = f"{telegram_id}_ss"
try:
xui_login()
vless_client = xui.get_client(inbound_id=INBOUND_VLESS_ID, email=vless_email)
ss_client = xui.get_client(inbound_id=INBOUND_SS_ID, email=ss_email)
def format_info(client, name):
if not client:
return f"❌ Клиент <b>{name}</b> не найден.\n"
json_info = json.dumps(client, ensure_ascii=False, indent=2)
return f"🔹 <b>{name}</b>:\n<pre>{json_info}</pre>"
response = format_info(vless_client, "VLESS") + "\n\n" + format_info(ss_client, "Shadowsocks")
await message.answer(response, parse_mode=ParseMode.HTML)
except BadLogin:
await message.answer("❌ Ошибка входа в панель XUI.")
except Exception as e:
logger.error(f"Ошибка получения информации: {e}")
await message.answer("❌ Ошибка при получении информации. Проверь лог.")
@dp.message(Command(commands=["create"]))
async def cmd_create(message: types.Message):
args = (message.text or "").strip().split(maxsplit=1)
if len(args) < 2:
await message.answer(
"❌ Укажи Telegram ID или username после команды.\n"
"Пример:\n<code>/create udochudo</code>"
)
return
telegram_id = args[1].lstrip("@").strip()
try:
xui_login()
vless_email = f"{telegram_id}_vl_ssl"
vless_uuid = str(uuid.uuid4())
ss_password = str(uuid.uuid4())
logger.info(f"Создаём клиентов для telegram_id={telegram_id} VLESS email={vless_email}, uuid={vless_uuid}")
# Создаём VLESS клиента через pyxui
xui.add_client(
inbound_id=INBOUND_VLESS_ID,
email=vless_email,
uuid=vless_uuid,
enable=True,
flow="xtls-rprx-vision",
limit_ip=0,
total_gb=0,
expire_time=0,
telegram_id=telegram_id,
subscription_id=telegram_id
)
logger.info(f"VLESS клиент создан с email={vless_email} и uuid={vless_uuid}")
# Создаём Shadowsocks клиента через API
success_ss = await create_shadowsocks_client_via_api(telegram_id, ss_password)
if not success_ss:
await message.answer("❌ Ошибка при создании Shadowsocks клиента.")
return
subscription_link = f"{SUB_BASE_URL}{telegram_id}?name={telegram_id}"
text = f"✅ Профиль для {telegram_id} успешно создан!\n🔗 Подписочная ссылка:\n<code>{subscription_link}</code>"
await message.answer(text,parse_mode=ParseMode.HTML)
except BadLogin:
await message.answer("❌ Ошибка: неверный логин или пароль XUI.")
except Exception as e:
logger.error(f"Ошибка при создании профиля: {e}")
await message.answer("❌ Произошла ошибка при создании профиля. Попробуйте позже.")
# Команды для работы с клиентами (только для разрешенных пользователей)
dp.message.register(
client_handlers.cmd_create,
Command(commands=["create"]),
AllowedUsersFilter()
)
dp.message.register(
client_handlers.cmd_info,
Command(commands=["info"]),
AllowedUsersFilter()
)
async def main():
"""Главная функция приложения."""
logger.info("Запуск бота...")
# Создаем основные компоненты
bot = create_bot()
dp = create_dispatcher()
# Создаем сервисы
xui_service = XUIService()
client_service = ClientService(xui_service)
# Создаем обработчики
client_handlers = ClientHandlers(client_service)
# Настраиваем обработчики
await setup_handlers(dp, client_handlers)
try:
logger.info("Бот запущен и готов к работе")
await dp.start_polling(bot)
except Exception as e:
logger.error(f"Ошибка при запуске бота: {e}")
raise
finally:
await bot.session.close()
logger.info("Бот остановлен")
if __name__ == "__main__":

3
bot/services/__init__.py Normal file
View File

@ -0,0 +1,3 @@
"""
Сервисы для бизнес-логики приложения.
"""

178
bot/services/client.py Normal file
View File

@ -0,0 +1,178 @@
import json
import uuid
from typing import Tuple, Optional, Dict, Any
import aiohttp
from bot.config import (
XUI_FULL_ADDRESS,
INBOUND_VLESS_ID,
INBOUND_SS_ID,
SUB_BASE_URL
)
from bot.services.xui import XUIService
from bot.utils.logging import logger
class ClientService:
"""Сервис для работы с клиентами VPN."""
def __init__(self, xui_service: XUIService):
self.xui_service = xui_service
def generate_client_credentials(self, telegram_id: str) -> Tuple[str, str, str, str]:
"""Генерация учетных данных для клиента."""
vless_email = f"{telegram_id}_vl_ssl"
ss_email = f"{telegram_id}_ss"
vless_uuid = str(uuid.uuid4())
ss_password = str(uuid.uuid4())
return vless_email, ss_email, vless_uuid, ss_password
def get_subscription_link(self, telegram_id: str) -> str:
"""Получение ссылки на подписку."""
return f"{SUB_BASE_URL}{telegram_id}?name={telegram_id}"
async def create_shadowsocks_client(self, telegram_id: str, password: str) -> bool:
"""Создание Shadowsocks клиента через API."""
try:
self.xui_service.login()
except Exception as e:
logger.error(f"Ошибка авторизации в XUI: {e}")
return False
ss_email = f"{telegram_id}_ss"
settings_str = json.dumps({
"clients": [{
"method": "chacha20-ietf-poly1305",
"password": password,
"email": ss_email,
"totalGB": 0,
"expiryTime": 0,
"enable": True,
"tgId": telegram_id,
"subId": telegram_id,
"reset": 0
}]
}, separators=(',', ':'))
payload = {
"id": INBOUND_SS_ID,
"settings": settings_str
}
api_url = f"{XUI_FULL_ADDRESS}/xui/API/inbounds/addClient"
cookies = self.xui_service.get_cookies()
async with aiohttp.ClientSession() as session:
async def do_request():
async with session.post(
api_url,
json=payload,
cookies=cookies,
headers={
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0"
},
ssl=True
) as resp:
text = await resp.text()
logger.debug(f"Response status: {resp.status}")
logger.debug(f"Response text: {text}")
return resp.status, text
status, text = await do_request()
# Если сессия истекла, пробуем повторно залогиниться
if status == 401 or (status == 200 and "login" in text.lower()):
logger.info("Сессия XUI истекла, повторный вход...")
try:
self.xui_service.reset_session()
self.xui_service.login()
cookies = self.xui_service.get_cookies()
except Exception as e:
logger.error(f"Ошибка повторной авторизации в XUI: {e}")
return False
status, text = await do_request()
if status == 200:
if "successfully" in text.lower() or "success" in text.lower():
logger.info(f"Shadowsocks клиент успешно создан: {ss_email}")
return True
elif not text.strip():
logger.warning(f"Пустой ответ при статусе 200. Проверяем наличие клиента: {ss_email}")
return await self.verify_client_created(telegram_id, ss_email)
else:
logger.error(f"Неожиданный ответ при создании SS клиента: {text}")
return False
else:
logger.error(f"Ошибка создания Shadowsocks клиента: {status} {text}")
return False
async def verify_client_created(self, telegram_id: str, ss_email: str) -> bool:
"""Проверка, что клиент существует в inbound SS."""
try:
inbound_info = self.xui_service.get_inbound(INBOUND_SS_ID)
if not inbound_info:
logger.error(f"Inbound {INBOUND_SS_ID} не найден.")
return False
settings = json.loads(inbound_info.get('settings', '{}'))
clients = settings.get('clients', [])
for client in clients:
if client.get('email') == ss_email:
logger.info(f"Клиент {ss_email} найден.")
return True
logger.warning(f"Клиент {ss_email} не найден.")
return False
except Exception as e:
logger.error(f"Ошибка при проверке клиента: {e}")
return False
async def create_client_profile(self, telegram_id: str) -> Tuple[bool, str]:
"""Создание полного профиля клиента (VLESS + Shadowsocks)."""
try:
vless_email, ss_email, vless_uuid, ss_password = self.generate_client_credentials(telegram_id)
logger.info(f"Создаём клиентов для telegram_id={telegram_id}")
# Создаём VLESS клиента
vless_success = self.xui_service.add_vless_client(
inbound_id=INBOUND_VLESS_ID,
email=vless_email,
uuid=vless_uuid,
telegram_id=telegram_id
)
if not vless_success:
return False, "Ошибка при создании VLESS клиента"
# Создаём Shadowsocks клиента
ss_success = await self.create_shadowsocks_client(telegram_id, ss_password)
if not ss_success:
return False, "Ошибка при создании Shadowsocks клиента"
subscription_link = self.get_subscription_link(telegram_id)
success_message = (
f"✅ Профиль для {telegram_id} успешно создан!\n"
f"🔗 Подписочная ссылка:\n<code>{subscription_link}</code>"
)
return True, success_message
except Exception as e:
logger.error(f"Ошибка при создании профиля: {e}")
return False, "Произошла ошибка при создании профиля"
def get_client_info(self, telegram_id: str) -> Tuple[Optional[Dict[str, Any]], Optional[Dict[str, Any]]]:
"""Получение информации о клиентах."""
vless_email = f"{telegram_id}_vl_ssl"
ss_email = f"{telegram_id}_ss"
vless_client = self.xui_service.get_client(INBOUND_VLESS_ID, vless_email)
ss_client = self.xui_service.get_client(INBOUND_SS_ID, ss_email)
return vless_client, ss_client

110
bot/services/xui.py Normal file
View File

@ -0,0 +1,110 @@
import json
from typing import Optional, Dict, Any
from pyxui import XUI
from pyxui.errors import BadLogin
from bot.config import (
XUI_FULL_ADDRESS,
XUI_PANEL_NAME,
XUI_USERNAME,
XUI_PASSWORD
)
from bot.utils.logging import logger
class XUIService:
"""Сервис для работы с XUI панелью."""
def __init__(self):
self.xui = XUI(
full_address=XUI_FULL_ADDRESS,
panel=XUI_PANEL_NAME,
https=True
)
def login(self) -> None:
"""Авторизация в XUI панели."""
if self.xui.session_string:
logger.debug("Уже залогинен в XUI.")
return
try:
self.xui.login(XUI_USERNAME, XUI_PASSWORD)
logger.info("Успешный вход в XUI.")
except BadLogin:
logger.error("Неверный логин или пароль XUI.")
raise
except Exception as e:
logger.error(f"Ошибка при входе в XUI: {e}")
raise
def get_client(self, inbound_id: int, email: str) -> Optional[Dict[str, Any]]:
"""Получение информации о клиенте."""
try:
self.login()
return self.xui.get_client(inbound_id=inbound_id, email=email)
except Exception as e:
logger.error(f"Ошибка получения клиента {email}: {e}")
return None
def add_vless_client(
self,
inbound_id: int,
email: str,
uuid: str,
telegram_id: str,
enable: bool = True,
flow: str = "xtls-rprx-vision",
limit_ip: int = 0,
total_gb: int = 0,
expire_time: int = 0
) -> bool:
"""Добавление VLESS клиента."""
try:
self.login()
self.xui.add_client(
inbound_id=inbound_id,
email=email,
uuid=uuid,
enable=enable,
flow=flow,
limit_ip=limit_ip,
total_gb=total_gb,
expire_time=expire_time,
telegram_id=telegram_id,
subscription_id=telegram_id
)
logger.info(f"VLESS клиент создан: {email}")
return True
except Exception as e:
logger.error(f"Ошибка создания VLESS клиента {email}: {e}")
return False
def get_inbound(self, inbound_id: int) -> Optional[Dict[str, Any]]:
"""Получение информации об inbound."""
try:
self.login()
return self.xui.get_inbound(inbound_id)
except Exception as e:
logger.error(f"Ошибка получения inbound {inbound_id}: {e}")
return None
def get_cookies(self) -> Dict[str, str]:
"""Получение cookies для API запросов."""
cookies = {}
if hasattr(self.xui, 'session') and self.xui.session and hasattr(self.xui.session, 'cookies'):
for cookie in self.xui.session.cookies:
if cookie.name == 'x-ui':
cookies['x-ui'] = cookie.value
break
if self.xui.session_string:
cookies['x-ui'] = self.xui.session_string
return cookies
def reset_session(self) -> None:
"""Сброс сессии для повторной авторизации."""
self.xui.session_string = None

3
bot/utils/__init__.py Normal file
View File

@ -0,0 +1,3 @@
"""
Утилиты и вспомогательные функции.
"""

15
bot/utils/logging.py Normal file
View File

@ -0,0 +1,15 @@
import logging
def setup_logging(level: int = logging.INFO) -> logging.Logger:
"""Настройка логирования для приложения."""
logging.basicConfig(
level=level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
return logger
# Создаем общий логгер для всего приложения
logger = setup_logging()