Доделал наконец
- Покупку, но надо подправить пару моментов - И выдача конфигураций - Проверил работу panel.py наконец, исправил пару приколов
This commit is contained in:
261
bot.py
261
bot.py
@@ -1,243 +1,140 @@
|
|||||||
|
|
||||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
|
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
|
||||||
from telegram.ext import Application, CallbackQueryHandler, CommandHandler, ContextTypes,MessageHandler, filters, CommandHandler
|
from telegram.ext import Application, CallbackQueryHandler, ContextTypes, MessageHandler, filters
|
||||||
from db import User, VPNServer, Transaction, Subscription
|
from db import User, VPNServer, Transaction, Subscription, get_db_session, init_db, SessionLocal
|
||||||
from db import get_db_session
|
|
||||||
from db import init_db, SessionLocal
|
|
||||||
from db_operations import last_subscription, create_user , get_sub_list,buy_sub
|
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
|
from service import UserService
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from logger_config import setup_logger
|
from logger_config import setup_logger
|
||||||
|
|
||||||
|
# Чтение конфигурации и настройка логгера
|
||||||
with open('config.json', 'r') as file:
|
with open('config.json', 'r') as file:
|
||||||
config = json.load(file)
|
config = json.load(file)
|
||||||
|
|
||||||
logger = setup_logger()
|
logger = setup_logger()
|
||||||
|
|
||||||
|
# Общая функция для создания клавиатуры
|
||||||
|
def create_keyboard(buttons):
|
||||||
|
return InlineKeyboardMarkup([[InlineKeyboardButton(text, callback_data=data)] for text, data in buttons])
|
||||||
|
|
||||||
|
# Функция для отправки сообщений с загрузкой
|
||||||
|
async def send_loading_message(update, context, text, reply_markup=None):
|
||||||
|
loading_message = await context.bot.send_message(chat_id=update.effective_chat.id, text="Загрузка...")
|
||||||
|
await loading_message.edit_text(text, reply_markup=reply_markup)
|
||||||
|
|
||||||
|
# Функция для обработки главного меню
|
||||||
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
keyboard = [
|
buttons = [("Личный кабинет", "account"), ("О нас ;)", "about"), ("Поддержка", "support")]
|
||||||
[
|
await send_loading_message(update, context, 'Добро пожаловать в ...! Здесь вы можете приобрести VPN. И нечего более', create_keyboard(buttons))
|
||||||
InlineKeyboardButton("Личный кабинет", callback_data="account"),
|
|
||||||
InlineKeyboardButton("О нac ;)", callback_data='about'),
|
|
||||||
],
|
|
||||||
[InlineKeyboardButton("Поддержка", callback_data='support')],
|
|
||||||
]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
||||||
loading_message = await context.bot.send_message(chat_id=update.callback_query.message.chat.id, text="Загрузка...")
|
|
||||||
await loading_message.edit_text(f'Добро пожаловать в ...! Здесь вы можете приобрести VPN. И нечего более',reply_markup=reply_markup)
|
|
||||||
|
|
||||||
|
# Функция для обработки личного кабинета
|
||||||
async def personal_account(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def personal_account(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
keyboard = [
|
service = UserService(logger)
|
||||||
[
|
tgid = str(update.callback_query.from_user.id)
|
||||||
InlineKeyboardButton("Пополнить баланс", callback_data="pop_up"),
|
user = service.get_user_by_telegram_id(tgid) or service.add_user(tgid)
|
||||||
InlineKeyboardButton("Приобрести подписку", callback_data='buy_tarif'),
|
subscription = service.last_subscription(user)
|
||||||
],
|
|
||||||
[InlineKeyboardButton("❔FAQ❔", callback_data='faq')],
|
|
||||||
[InlineKeyboardButton("История платежей", callback_data='payment_history')]
|
|
||||||
]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
||||||
|
|
||||||
session = next(get_db_session())
|
|
||||||
|
|
||||||
if session.query(User).filter(User.telegram_id == str(update.callback_query.from_user.id)).first() == None:
|
|
||||||
create_user(str(update.callback_query.from_user.id),update.effective_user.username)
|
|
||||||
user = session.query(User).filter(User.telegram_id == str(update.callback_query.from_user.id)).first()
|
|
||||||
|
|
||||||
subscription = last_subscription(session=session,user=user)
|
|
||||||
|
|
||||||
loading_message = await context.bot.send_message(chat_id=update.callback_query.message.chat.id, text="Загрузка...")
|
|
||||||
|
|
||||||
if not subscription:
|
|
||||||
await loading_message.edit_text(
|
|
||||||
f'Профиль {user.username}, {user.telegram_id}\nВы не приобретали ещё у нас подписку, но это явно стоит сделать:)\nВаш счёт составляет: {user.balance}',reply_markup=reply_markup
|
|
||||||
)
|
|
||||||
# Проверяем, истекла ли подписка
|
|
||||||
elif subscription.expiry_date < datetime.now():
|
|
||||||
await loading_message.edit_text(
|
|
||||||
f'Ваш профиль {user.username}, {user.telegram_id}, Ваша подписка действует до - {subscription.expiry_date}',reply_markup=reply_markup
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
await loading_message.edit_text(
|
|
||||||
f'Ваш профиль {user.username}, {user.telegram_id},\nВаша подписка истекла - {subscription.expiry_date}',reply_markup=reply_markup
|
|
||||||
)
|
|
||||||
|
|
||||||
|
buttons = [("Пополнить баланс", "pop_up"), ("Приобрести подписку", "buy_tarif"), ("❔FAQ❔", "faq"), ("История платежей", "payment_history")]
|
||||||
|
text = (
|
||||||
|
f'Профиль {user.username}, {user.telegram_id}\n'
|
||||||
|
f'{"Вы не приобретали ещё у нас подписку, но это явно стоит сделать:)" if not subscription else f"Ваша подписка действует до - {subscription.expiry_date}" if subscription.expiry_date > datetime.now() else f"Ваша подписка истекла - {subscription.expiry_date}"}\n'
|
||||||
|
f'Ваш счёт составляет: {user.balance}'
|
||||||
|
)
|
||||||
|
await send_loading_message(update, context, text, create_keyboard(buttons))
|
||||||
|
|
||||||
|
# Функция для отображения информации "О нас"
|
||||||
async def about(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def about(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
keyboard = [
|
buttons = [("Главное меню", "start")]
|
||||||
[
|
await send_loading_message(update, context, 'Игорь чё нить напишет, я продублирую', create_keyboard(buttons))
|
||||||
InlineKeyboardButton("Главное меню", callback_data="start")
|
|
||||||
]]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
||||||
loading_message = await context.bot.send_message(chat_id=update.callback_query.message.chat.id, text="Загрузка...")
|
|
||||||
await loading_message.edit_text(f'Игорь чё нить напишет, я продублирую',reply_markup=reply_markup)
|
|
||||||
|
|
||||||
|
# Функция для отображения поддержки
|
||||||
async def support(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def support(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
keyboard = [
|
buttons = [("Главное меню", "start"), ("Написать", "sup")]
|
||||||
[
|
await send_loading_message(update, context, 'Для связи с поддержкой выберите "Написать" и изложите проблему в одном сообщении.', create_keyboard(buttons))
|
||||||
InlineKeyboardButton("Главное меню", callback_data="start"),
|
|
||||||
InlineKeyboardButton("Написать", callback_data="sup") # Нужно через каллбек доделать
|
|
||||||
]]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
||||||
loading_message = await context.bot.send_message(chat_id=update.callback_query.message.chat.id, text="Загрузка...")
|
|
||||||
await loading_message.edit_text(f'Что бы отправить сообщение поддержке выберите в меню кнопку "Написать", а далее изложите в одном сообщении свою ошибку.',reply_markup=reply_markup)
|
|
||||||
|
|
||||||
|
# Функция для пополнения баланса
|
||||||
async def pop_up(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def pop_up(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
keyboard = [
|
buttons = [("Главное меню", "start")]
|
||||||
[
|
await send_loading_message(update, context, 'Когда-нибудь эта функция заработает', create_keyboard(buttons))
|
||||||
InlineKeyboardButton("Главное меню", callback_data="start"),
|
|
||||||
]]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
||||||
loading_message = await context.bot.send_message(chat_id=update.callback_query.message.chat.id, text="Загрузка...")
|
|
||||||
await loading_message.edit_text(f'Когда нибудь эта штука заработает',reply_markup=reply_markup)
|
|
||||||
|
|
||||||
|
# Функция для покупки подписки
|
||||||
async def buy_subscription(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def buy_subscription(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
keyboard = [
|
service = UserService(logger)
|
||||||
[
|
tgid = str(update.callback_query.from_user.id)
|
||||||
InlineKeyboardButton("Главное меню", callback_data="start"),
|
user = service.get_user_by_telegram_id(tgid)
|
||||||
]]
|
subscription = service.last_subscription(user)
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
||||||
session = next(get_db_session())
|
|
||||||
user = session.query(User).filter(User.telegram_id == str(update.callback_query.from_user.id)).first()
|
|
||||||
check = last_subscription(session=session,user=user)
|
|
||||||
|
|
||||||
if not check:
|
if subscription is None:
|
||||||
keyboard = [
|
buttons = [("Тариф 1 \"Бимжик\"", "Бимжик"), ("Тариф 2 \"Бизнес хомячёк\"", "Бизнес_хомячёк"), ("Тариф 3 \"Продвинутый Акулёнок\"", "Продвинутый_Акулёнок"), ("Главное меню", "start")]
|
||||||
[
|
text = 'Какую подписку вы хотите приобрести?\n1. "Бимжик" - 200 руб. на 1 месяц\n2. "Бизнес хомячёк" - 500 руб. на 3 месяца\n3. "Продвинутый Акулёнок" - 888 руб. на 6 месяцев'
|
||||||
InlineKeyboardButton("Тариф 1 \"Бимжик\"", callback_data="Бимжик"),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
InlineKeyboardButton("Тариф 2 \"Бизнес хомячёк\"", callback_data="Бизнес_хомячёк"),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
InlineKeyboardButton("Тариф 3 \"Продвинутый Акулёнок\"", callback_data="Продвинутый_Акулёнок"),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
InlineKeyboardButton("Главное меню", callback_data="start"),
|
|
||||||
]]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
||||||
loading_message = await context.bot.send_message(chat_id=update.callback_query.message.chat.id, text="Загрузка...")
|
|
||||||
await loading_message.edit_text(f'Какую подписку вы хотели бы приобрести\nТариф 1 "Бимжик" - 200 рубликов - 1 месяцок\nТариф 2 "Бизнес хомячёк" - 500 рубликов - 3 месяцка\nТариф 3 "Продвинутый Акулёнок" - 888 рубликов - 6 месяцков\n',reply_markup=reply_markup)
|
|
||||||
# проверяем, истекла ли подписка
|
|
||||||
else:
|
else:
|
||||||
keyboard = [
|
buttons = [("Главное меню", "start")]
|
||||||
[
|
text = 'У вас уже приобретена подписка'
|
||||||
InlineKeyboardButton("Главное меню", callback_data="start"),
|
|
||||||
]]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
||||||
loading_message = await context.bot.send_message(chat_id=update.callback_query.message.chat.id, text="Загрузка...")
|
|
||||||
await loading_message.edit_text(f'У вас уже приобретена подписка',reply_markup=reply_markup)
|
|
||||||
|
|
||||||
|
await send_loading_message(update, context, text, create_keyboard(buttons))
|
||||||
|
|
||||||
|
# Функция для отображения FAQ
|
||||||
async def faq(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def faq(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
keyboard = [
|
buttons = [("Главное меню", "start")]
|
||||||
[
|
await send_loading_message(update, context, 'Когда-нибудь здесь появится полезная информация!', create_keyboard(buttons))
|
||||||
InlineKeyboardButton("Главное меню", callback_data="start"),
|
|
||||||
]]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
||||||
loading_message = await context.bot.send_message(chat_id=update.callback_query.message.chat.id, text="Загрузка...")
|
|
||||||
await loading_message.edit_text(f'Когда нибудь что нибудь здесь будет написано!!;)',reply_markup=reply_markup)
|
|
||||||
|
|
||||||
|
|
||||||
|
# Функция для обработки ввода пользователя в поддержку
|
||||||
async def sup(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def sup(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
if context.user_data.get('awaiting_input'):
|
if context.user_data.get('awaiting_input'):
|
||||||
user_input = update.message.text
|
user_input = update.message.text
|
||||||
await update.message.reply_text(f"Вы ввели: {user_input}")
|
await update.message.reply_text(f"Вы ввели: {user_input}")
|
||||||
|
|
||||||
# После получения текста сбрасываем ожидание
|
|
||||||
context.user_data['awaiting_input'] = False
|
context.user_data['awaiting_input'] = False
|
||||||
else:
|
else:
|
||||||
await update.message.reply_text("Выберите команду или нажмите кнопку для продолжения.")
|
await update.message.reply_text("Выберите команду или нажмите кнопку для продолжения.")
|
||||||
|
|
||||||
|
# Обработчик кнопок
|
||||||
async def button_handler(update: Update, context):
|
async def button_handler(update: Update, context):
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
|
|
||||||
await query.answer()
|
await query.answer()
|
||||||
|
data = query.data
|
||||||
|
|
||||||
|
service = UserService(logger)
|
||||||
|
tgid = str(query.from_user.id)
|
||||||
|
|
||||||
session = next(get_db_session())
|
|
||||||
try:
|
try:
|
||||||
if query.data == 'account':
|
if data == 'account':
|
||||||
await personal_account(update,context)
|
await personal_account(update, context)
|
||||||
elif query.data == 'start':
|
elif data == 'start':
|
||||||
await start(update,context)
|
await start(update, context)
|
||||||
elif query.data == 'about':
|
elif data == 'about':
|
||||||
await about(update, context)
|
await about(update, context)
|
||||||
elif query.data == 'support':
|
elif data == 'support':
|
||||||
await support(update, context)
|
await support(update, context)
|
||||||
elif query.data == 'sup':
|
elif data == 'sup':
|
||||||
context.user_data['awaiting_input'] = True
|
context.user_data['awaiting_input'] = True
|
||||||
elif query.data == 'pop_up':
|
elif data == 'pop_up':
|
||||||
await pop_up(update, context)
|
await pop_up(update, context)
|
||||||
elif query.data == 'buy_tarif':
|
elif data == 'buy_tarif':
|
||||||
await buy_subscription(update, context)
|
await buy_subscription(update, context)
|
||||||
elif query.data == 'faq':
|
elif data == 'faq':
|
||||||
await faq(update, context)
|
await faq(update, context)
|
||||||
elif query.data == 'payment_history':
|
elif data == 'payment_history':
|
||||||
await active_sub(update, context)
|
await active_sub(update, context)
|
||||||
|
elif data in ['Бимжик', 'Бизнес_хомячёк', 'Продвинутый_Акулёнок']:
|
||||||
if query.data in ['Бимжик', 'Бизнес_хомячёк', 'Продвинутый_Акулёнок']:
|
plan = data.replace('_', ' ')
|
||||||
loading_message = await query.message.reply_text("Загрузка...")
|
result = service.buy_sub(tgid, data)
|
||||||
plan = query.data.replace('_', ' ') if '_' in query.data else query.data
|
text = {
|
||||||
check = buy_sub(session, query.from_user.id, plan, logger)
|
"OK": "Ваша конфигурация готова!",
|
||||||
|
"100": "Недостаточно средств.",
|
||||||
|
"120": "Нет доступных серверов, подождите немного.",
|
||||||
if check != "OK":
|
}.get(result, "Неизвестный тариф.")
|
||||||
await loading_message.edit_text("Неизвестный тариф.")
|
await query.message.reply_text(text)
|
||||||
else:
|
|
||||||
await loading_message.edit_text("Тариф успешно установлен!")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при обработке запроса пользователя {query.from_user.id}: {e}")
|
logger.error(f"Ошибка при обработке запроса пользователя {tgid}: {e}")
|
||||||
await query.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте снова.")
|
await query.message.reply_text("Произошла ошибка. Пожалуйста, попробуйте снова.")
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def active_sub(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|
||||||
keyboard = [
|
|
||||||
[
|
|
||||||
InlineKeyboardButton("Главное меню", callback_data="start"),
|
|
||||||
]]
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
||||||
session = next(get_db_session())
|
|
||||||
list_sub = get_sub_list(session, 10, update.callback_query.from_user.id)
|
|
||||||
loading_message = await context.bot.send_message(chat_id=update.callback_query.message.chat.id, text="Загрузка...")
|
|
||||||
if list_sub:
|
|
||||||
message = "Ваши подписки:\n"
|
|
||||||
for cur_sub in list_sub:
|
|
||||||
if cur_sub.expiry_date > datetime.now():
|
|
||||||
message += f" Активная: {cur_sub.plan}, Дата покупки: {cur_sub.created_at}\n"
|
|
||||||
else:
|
|
||||||
message += f" Устаревшая: {cur_sub.plan}, Дата покупки: {cur_sub.created_at}\n"
|
|
||||||
else:
|
|
||||||
message = "Ты пидор, не приобрел у нас подписку?!"
|
|
||||||
|
|
||||||
await loading_message.edit_text(message,reply_markup=reply_markup)
|
|
||||||
|
|
||||||
|
# Запуск приложения
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
init_db()
|
init_db()
|
||||||
db = SessionLocal()
|
|
||||||
application = Application.builder().token(config['token']).build()
|
application = Application.builder().token(config['token']).build()
|
||||||
|
|
||||||
application.add_handler(CommandHandler("start", start))
|
|
||||||
application.add_handler(CommandHandler("account", personal_account))
|
|
||||||
application.add_handler(CommandHandler("about", about))
|
|
||||||
application.add_handler(CommandHandler("support", support))
|
|
||||||
application.add_handler(CommandHandler("popup", pop_up))
|
|
||||||
application.add_handler(CommandHandler("buy_subscription", buy_subscription))
|
|
||||||
application.add_handler(CommandHandler("faq", faq))
|
|
||||||
application.add_handler(CommandHandler("active_sub", active_sub))
|
|
||||||
application.add_handler(CallbackQueryHandler(button_handler))
|
application.add_handler(CallbackQueryHandler(button_handler))
|
||||||
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, sup))
|
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, sup))
|
||||||
|
|
||||||
application.run_polling(allowed_updates=Update.ALL_TYPES)
|
application.run_polling(allowed_updates=Update.ALL_TYPES)
|
||||||
db.close()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
27
db.py
27
db.py
@@ -1,4 +1,4 @@
|
|||||||
from sqlalchemy import create_engine, Column, String, Integer, Numeric, DateTime, ForeignKey, Text
|
from sqlalchemy import create_engine, Column, String, Integer, Numeric, DateTime, ForeignKey, Text, Boolean
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import sessionmaker, relationship
|
from sqlalchemy.orm import sessionmaker, relationship
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
@@ -19,13 +19,15 @@ class User(Base):
|
|||||||
|
|
||||||
id = Column(String, primary_key=True, default=generate_uuid)
|
id = Column(String, primary_key=True, default=generate_uuid)
|
||||||
telegram_id = Column(String, unique=True, nullable=False)
|
telegram_id = Column(String, unique=True, nullable=False)
|
||||||
username = Column(String) # email 3x-ui
|
username = Column(String) # email 3x-ui
|
||||||
balance = Column(Numeric(10, 2), default=0.0)
|
balance = Column(Numeric(10, 2), default=0.0)
|
||||||
created_at = Column(DateTime, default=datetime.now)
|
created_at = Column(DateTime, default=datetime.now)
|
||||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||||
|
|
||||||
subscriptions = relationship("Subscription", back_populates="user")
|
subscriptions = relationship("Subscription", back_populates="user")
|
||||||
transactions = relationship("Transaction", back_populates="user")
|
transactions = relationship("Transaction", back_populates="user")
|
||||||
|
requests = relationship("Requests", back_populates="user")
|
||||||
|
admins = relationship("Administrators", back_populates="user")
|
||||||
|
|
||||||
#Подписки
|
#Подписки
|
||||||
class Subscription(Base):
|
class Subscription(Base):
|
||||||
@@ -57,20 +59,22 @@ class Transaction(Base):
|
|||||||
class Requests(Base):
|
class Requests(Base):
|
||||||
__tablename__ = 'requests'
|
__tablename__ = 'requests'
|
||||||
|
|
||||||
id = Column(String,primary_key=True,default=generate_uuid)
|
id = Column(String, primary_key=True, default=generate_uuid)
|
||||||
user_id = Column(String,ForeignKey('users.id'))
|
user_id = Column(String, ForeignKey('users.id'))
|
||||||
username = Column(String)
|
username = Column(String)
|
||||||
created_at = Column(DateTime,default=datetime.now)
|
created_at = Column(DateTime, default=datetime.now)
|
||||||
content = Column(String)
|
content = Column(String)
|
||||||
status = Column(String,default='open')
|
status = Column(String, default='open')
|
||||||
user = relationship("User",back_populates="requests")
|
|
||||||
|
user = relationship("User", back_populates="requests")
|
||||||
|
|
||||||
|
|
||||||
class Administrators(Base):
|
class Administrators(Base):
|
||||||
__tablename__ = 'admins'
|
__tablename__ = 'admins'
|
||||||
|
|
||||||
id = Column(String,primary_key=True,default=generate_uuid)
|
id = Column(String,primary_key=True,default=generate_uuid)
|
||||||
user_id = Column(String,ForeignKey('users.id'))
|
user_id = Column(String,ForeignKey('users.id'))
|
||||||
admin = Column(bool,default=False)
|
admin = Column(Boolean,default=False)
|
||||||
user = relationship("User",back_populates="admins")
|
user = relationship("User",back_populates="admins")
|
||||||
# VPN-серверы
|
# VPN-серверы
|
||||||
class VPNServer(Base):
|
class VPNServer(Base):
|
||||||
@@ -80,9 +84,10 @@ class VPNServer(Base):
|
|||||||
server_name = Column(String)
|
server_name = Column(String)
|
||||||
ip_address = Column(String)
|
ip_address = Column(String)
|
||||||
port = Column(Integer)
|
port = Column(Integer)
|
||||||
login_data = Column(Text)
|
login = Column(String)
|
||||||
inbound = Column(Text)
|
password = Column(String)
|
||||||
config = Column(Text)
|
config = Column(String)
|
||||||
|
secret = Column(String)
|
||||||
current_users = Column(Integer, default=0)
|
current_users = Column(Integer, default=0)
|
||||||
max_users = Column(Integer, default=4)
|
max_users = Column(Integer, default=4)
|
||||||
|
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
|
|
||||||
from service import UserService
|
|
||||||
|
|
||||||
#DataBase
|
|
||||||
from sqlalchemy import desc
|
|
||||||
from db import get_db_session
|
|
||||||
from db import User
|
|
||||||
from db import Subscription
|
|
||||||
from db import Transaction
|
|
||||||
from db import VPNServer
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
with open('config.json', 'r') as file:
|
|
||||||
config = json.load(file)
|
|
||||||
|
|
||||||
|
|
||||||
def last_subscription(session,user):
|
|
||||||
last_subscription = (
|
|
||||||
session.query(Subscription)
|
|
||||||
.filter(Subscription.user_id == user.id)
|
|
||||||
.order_by(desc(Subscription.created_at))
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
return last_subscription
|
|
||||||
def get_sub_list(session,count,user_id):
|
|
||||||
subscriptions = (
|
|
||||||
session.query(Subscription)
|
|
||||||
.filter(Subscription.user_id == str(user_id))
|
|
||||||
.order_by(desc(Subscription.created_at))
|
|
||||||
.limit(count) # Ограничиваем результат 10 записями
|
|
||||||
.all() # Получаем все записи
|
|
||||||
)
|
|
||||||
return subscriptions
|
|
||||||
|
|
||||||
def create_user(telegram_id: str, username: str = None, balance: float = 0.0):
|
|
||||||
db = next(get_db_session())
|
|
||||||
try:
|
|
||||||
new_user = User(
|
|
||||||
telegram_id=telegram_id,
|
|
||||||
username=username,
|
|
||||||
balance=balance
|
|
||||||
)
|
|
||||||
db.add(new_user)
|
|
||||||
db.commit()
|
|
||||||
db.refresh(new_user)
|
|
||||||
return new_user
|
|
||||||
except Exception as e:
|
|
||||||
db.rollback()
|
|
||||||
raise e
|
|
||||||
finally:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
def buy_sub(session, telegram_id: str, plan: str, logger):
|
|
||||||
try:
|
|
||||||
user = session.query(User).filter(User.telegram_id == str(telegram_id)).first()
|
|
||||||
if user is None:
|
|
||||||
logger.error(f"Пользователь с Telegram ID {telegram_id} не найден.")
|
|
||||||
return "error"
|
|
||||||
|
|
||||||
current_plan = config['subscription_templates'].get(plan)
|
|
||||||
if current_plan is None:
|
|
||||||
logger.error(f"Тариф {plan} не найден в шаблонах.")
|
|
||||||
return "error"
|
|
||||||
|
|
||||||
if user.balance == current_plan['cost']:
|
|
||||||
service = UserService(logger)
|
|
||||||
service.tariff_setting(user, plan, current_plan['duration'])
|
|
||||||
return "OK"
|
|
||||||
else:
|
|
||||||
logger.error(f"Недостаточно средств на счету пользователя {telegram_id} для тарифа {plan}.")
|
|
||||||
return "error"
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Ошибка при покупке тарифа для пользователя {telegram_id}: {e}")
|
|
||||||
94
panel.py
94
panel.py
@@ -9,27 +9,11 @@ from dateutil.relativedelta import relativedelta
|
|||||||
|
|
||||||
with open('config.json', 'r') as file : config = json.load(file)
|
with open('config.json', 'r') as file : config = json.load(file)
|
||||||
|
|
||||||
def generate_date(months):
|
|
||||||
now = datetime.now()
|
|
||||||
|
|
||||||
# Преобразуем months в число
|
|
||||||
try:
|
|
||||||
months = int(months) # или float(months), если месяцы могут быть дробными
|
|
||||||
except ValueError:
|
|
||||||
raise TypeError("months должно быть числом")
|
|
||||||
|
|
||||||
future_date = now + timedelta(days=30 * months)
|
|
||||||
return future_date.isoformat()
|
|
||||||
|
|
||||||
|
|
||||||
def generate_random_string(length=8):
|
|
||||||
characters = string.ascii_letters + string.digits
|
|
||||||
return ''.join(secrets.choice(characters) for _ in range(length))
|
|
||||||
|
|
||||||
def generate_uuid():
|
def generate_uuid():
|
||||||
return str(uuid.uuid4())
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
class PanelInteraction:
|
class PanelInteraction:
|
||||||
def __init__(self, base_url, login_data, logger_):
|
def __init__(self, base_url, login_data, logger_):
|
||||||
self.base_url = base_url
|
self.base_url = base_url
|
||||||
@@ -47,7 +31,8 @@ class PanelInteraction:
|
|||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
login_url = self.base_url + "/login"
|
login_url = self.base_url + "/login"
|
||||||
response = requests.post(login_url, data=self.login_data)
|
self.logger.info(f"Login URL : {login_url}")
|
||||||
|
response = requests.post(login_url, data=self.login_data, verify=False)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
session_id = response.cookies.get("3x-ui")
|
session_id = response.cookies.get("3x-ui")
|
||||||
return session_id
|
return session_id
|
||||||
@@ -55,26 +40,31 @@ class PanelInteraction:
|
|||||||
self.logger.error(f"Login failed: {response.status_code}")
|
self.logger.error(f"Login failed: {response.status_code}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def getInboundInfo(self,inboundId):
|
def getInboundInfo(self, inboundId):
|
||||||
url = f"{self.base_url}/panel/api/inbounds/get/{inboundId}"
|
url = f"{self.base_url}/panel/api/inbounds/get/{inboundId}"
|
||||||
response = requests.get(url, headers=self.headers)
|
try:
|
||||||
if response.status_code == 200:
|
response = requests.get(url, headers=self.headers, verify=False)
|
||||||
return response.json()
|
if response.status_code == 200:
|
||||||
else:
|
return response.json()
|
||||||
self.logger.error(f"Failed to get inbound info: {response.status_code}")
|
else:
|
||||||
self.logger.debug("Response:", response.text)
|
self.logger.error(f"Failed to get inbound info: {response.status_code}")
|
||||||
return None
|
self.logger.debug("Response:", response.text)
|
||||||
|
return None
|
||||||
|
finally:
|
||||||
|
self.logger.info("Finished attempting to get inbound info.")
|
||||||
|
|
||||||
def get_client_traffic(self, email):
|
def get_client_traffic(self, email):
|
||||||
url = f"{self.base_url}/panel/api/inbounds/getClientTraffics/{email}"
|
url = f"{self.base_url}/panel/api/inbounds/getClientTraffics/{email}"
|
||||||
response = requests.get(url, headers=self.headers)
|
try:
|
||||||
if response.status_code == 200:
|
response = requests.get(url, headers=self.headers, verify=False)
|
||||||
return response.json()
|
if response.status_code == 200:
|
||||||
else:
|
return response.json()
|
||||||
self.logger.error(f"Failed to get client traffic: {response.status_code}")
|
else:
|
||||||
self.logger.debug("Response:", response.text)
|
self.logger.error(f"Failed to get client traffic: {response.status_code}")
|
||||||
return None
|
self.logger.debug("Response:", response.text)
|
||||||
|
return None
|
||||||
|
finally:
|
||||||
|
self.logger.info("Finished attempting to get client traffic.")
|
||||||
|
|
||||||
def update_client_expiry(self, client_uuid, new_expiry_time, client_email):
|
def update_client_expiry(self, client_uuid, new_expiry_time, client_email):
|
||||||
url = f"{self.base_url}/panel/api/inbounds/updateClient"
|
url = f"{self.base_url}/panel/api/inbounds/updateClient"
|
||||||
@@ -96,23 +86,27 @@ class PanelInteraction:
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
response = requests.post(url, headers=self.headers, json=update_data)
|
try:
|
||||||
if response.status_code == 200:
|
response = requests.post(url, headers=self.headers, json=update_data, verify=False)
|
||||||
self.logger.debug("Client expiry time updated successfully.")
|
if response.status_code == 200:
|
||||||
else:
|
self.logger.debug("Client expiry time updated successfully.")
|
||||||
self.logger.error(f"Failed to update client: {response.status_code} {response.text}")
|
else:
|
||||||
|
self.logger.error(f"Failed to update client: {response.status_code} {response.text}")
|
||||||
|
finally:
|
||||||
|
self.logger.info("Finished attempting to update client expiry.")
|
||||||
|
|
||||||
def add_client(self, inbound_id, months):
|
def add_client(self, inbound_id, expiry_date,email):
|
||||||
url = f"{self.base_url}/panel/api/inbounds/addClient"
|
url = f"{self.base_url}/panel/api/inbounds/addClient"
|
||||||
client_info = {
|
client_info = {
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"id": generate_uuid(),
|
"id": generate_uuid(),
|
||||||
"alterId": 0,
|
"alterId": 0,
|
||||||
"email": generate_random_string(),
|
"email": email,
|
||||||
"limitIp": 2,
|
"limitIp": 2,
|
||||||
"totalGB": 0,
|
"totalGB": 0,
|
||||||
"expiryTime": generate_date(months),
|
"flow":"xtls-rprx-vision",
|
||||||
|
"expiryTime": expiry_date,
|
||||||
"enable": True,
|
"enable": True,
|
||||||
"tgId": "",
|
"tgId": "",
|
||||||
"subId": ""
|
"subId": ""
|
||||||
@@ -123,11 +117,13 @@ class PanelInteraction:
|
|||||||
"id": inbound_id,
|
"id": inbound_id,
|
||||||
"settings": json.dumps(client_info)
|
"settings": json.dumps(client_info)
|
||||||
}
|
}
|
||||||
response = requests.post(url, headers=self.headers, json=payload)
|
try:
|
||||||
if response.status_code == 200:
|
response = requests.post(url, headers=self.headers, json=payload, verify=False)
|
||||||
self.logger.debug("Client added successfully!")
|
if response.status_code == 200:
|
||||||
return response.json()
|
return response.json()
|
||||||
else:
|
else:
|
||||||
self.logger.error(f"Failed to add client: {response.status_code}")
|
self.logger.error(f"Failed to add client: {response.status_code}")
|
||||||
self.logger.debug("Response:", response.text)
|
self.logger.debug("Response:", response.text)
|
||||||
return None
|
return None
|
||||||
|
finally:
|
||||||
|
self.logger.info("Finished attempting to add client.")
|
||||||
|
|||||||
219
service.py
219
service.py
@@ -1,23 +1,32 @@
|
|||||||
from db import User
|
from db import User, Subscription, Transaction, VPNServer
|
||||||
from db import Subscription
|
import string
|
||||||
from db import Transaction
|
import secrets
|
||||||
from db import VPNServer
|
|
||||||
from sqlalchemy import desc
|
|
||||||
from datetime import datetime,timedelta
|
|
||||||
from db import get_db_session
|
|
||||||
import json
|
import json
|
||||||
|
from sqlalchemy import desc
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
from datetime import datetime
|
||||||
|
from db import get_db_session
|
||||||
from panel import PanelInteraction
|
from panel import PanelInteraction
|
||||||
|
|
||||||
with open('config.json', 'r') as file : config = json.load(file)
|
|
||||||
|
def generate_random_string(length=8):
|
||||||
|
characters = string.ascii_letters + string.digits
|
||||||
|
return ''.join(secrets.choice(characters) for _ in range(length))
|
||||||
|
|
||||||
|
|
||||||
|
# Загрузка конфигурации один раз
|
||||||
|
with open('config.json', 'r') as file:
|
||||||
|
config = json.load(file)
|
||||||
|
|
||||||
|
|
||||||
class UserService:
|
class UserService:
|
||||||
def __init__(self,logger):
|
def __init__(self, logger):
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
|
||||||
def add_user(self,telegram_id: int, username: str):
|
def add_user(self, telegram_id: int):
|
||||||
session = next(get_db_session())
|
session = next(get_db_session())
|
||||||
try:
|
try:
|
||||||
new_user = User(telegram_id=telegram_id, username=username)
|
new_user = User(telegram_id=telegram_id, username=generate_random_string())
|
||||||
session.add(new_user)
|
session.add(new_user)
|
||||||
session.commit()
|
session.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -26,7 +35,7 @@ class UserService:
|
|||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
def get_user_by_telegram_id(self,telegram_id: int):
|
def get_user_by_telegram_id(self, telegram_id: int):
|
||||||
session = next(get_db_session())
|
session = next(get_db_session())
|
||||||
try:
|
try:
|
||||||
return session.query(User).filter(User.telegram_id == telegram_id).first()
|
return session.query(User).filter(User.telegram_id == telegram_id).first()
|
||||||
@@ -35,35 +44,48 @@ class UserService:
|
|||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
def add_transaction(self,user_id: int,amount: float):
|
def add_transaction(self, user_id: int, amount: float):
|
||||||
session = next(get_db_session())
|
session = next(get_db_session())
|
||||||
try:
|
try:
|
||||||
transaction = Transaction(user_id = user_id,amount = amount)
|
transaction = Transaction(user_id=user_id, amount=amount)
|
||||||
session.add(transaction)
|
session.add(transaction)
|
||||||
session.commit()
|
session.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Ошибка добавления транзакции:{e}")
|
self.logger.error(f"Ошибка добавления транзакции: {e}")
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
def pop_up_balance(self,telegram_id: int,amount: float):
|
def update_balance(self, telegram_id: int, amount: float):
|
||||||
session = next(get_db_session())
|
session = next(get_db_session())
|
||||||
try:
|
try:
|
||||||
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
||||||
if user:
|
if user:
|
||||||
user.balance = amount
|
user.balance = amount
|
||||||
self.add_transaction(user.id,amount)
|
self.add_transaction(user.id, amount)
|
||||||
session.commit()
|
session.commit()
|
||||||
else:
|
else:
|
||||||
self.logger.warning(f"Пользователь с Telegram ID {telegram_id} не найден.")
|
self.logger.warning(f"Пользователь с Telegram ID {telegram_id} не найден.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Ошибка при обновлении баланса:{e}")
|
|
||||||
self.logger.error(f"Сумма: {amount}, Пользователь: {telegram_id}")
|
|
||||||
session.rollback()
|
session.rollback()
|
||||||
|
self.logger.error(f"Ошибка при обновлении баланса: {e}")
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
def tariff_setting(self, user, plan: str, expiry_duration):
|
def last_subscription(self, user):
|
||||||
|
session = next(get_db_session())
|
||||||
|
try:
|
||||||
|
return (
|
||||||
|
session.query(Subscription)
|
||||||
|
.filter(Subscription.user_id == user.id)
|
||||||
|
.order_by(desc(Subscription.created_at))
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Ошибка при получении последней подписки: {e}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
def tariff_setting(self, user, plan: str, expiry_duration: int):
|
||||||
session = next(get_db_session())
|
session = next(get_db_session())
|
||||||
try:
|
try:
|
||||||
server = (
|
server = (
|
||||||
@@ -73,58 +95,159 @@ class UserService:
|
|||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
|
|
||||||
if server is None:
|
if not server:
|
||||||
self.logger.error("Нет доступных VPN серверов.")
|
self.logger.error("Нет доступных VPN серверов.")
|
||||||
return
|
return "120"
|
||||||
|
|
||||||
|
# Рассчитываем дату окончания подписки
|
||||||
|
expiry_ = datetime.now() + relativedelta(months=expiry_duration)
|
||||||
|
self.logger.info(f"Создание подписки для пользователя {user.id} на сервере {server.id} с планом {plan} до {expiry_}")
|
||||||
|
|
||||||
expiry_ = datetime.now() + timedelta(days=expiry_duration)
|
|
||||||
new_subscription = Subscription(user_id=user.id, vpn_server_id=server.id, plan=plan, expiry_date=expiry_)
|
new_subscription = Subscription(user_id=user.id, vpn_server_id=server.id, plan=plan, expiry_date=expiry_)
|
||||||
session.add(new_subscription)
|
session.add(new_subscription)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
self.logger.info(f"Подписка успешно создана для пользователя {user.id}")
|
||||||
|
return "OK"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Ошибка в установке тарифа: {e}")
|
self.logger.error(f"Ошибка в установке тарифа: {e}")
|
||||||
|
return "Ошибка"
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
|
def buy_sub(self, telegram_id: str, plan: str):
|
||||||
|
|
||||||
def create_uri(self,telegram_id,):
|
|
||||||
session = next(get_db_session())
|
session = next(get_db_session())
|
||||||
try:
|
try:
|
||||||
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
||||||
if user:
|
if not user:
|
||||||
subscription = user.subscriptions
|
self.logger.error(f"Пользователь с Telegram ID {telegram_id} не найден.")
|
||||||
if not subscription:
|
return "error"
|
||||||
return None
|
|
||||||
vpn_server = session.query(VPNServer).filter_by(id=subscription.vpn_server_id).first()
|
current_plan = config['subscription_templates'].get(plan)
|
||||||
baseURL ="http://" + vpn_server.ip_address + ":" + vpn_server.port
|
if not current_plan:
|
||||||
PI = PanelInteraction(baseURL,vpn_server.login_data,self.logger)
|
self.logger.error(f"Тариф {plan} не найден в шаблонах.")
|
||||||
CIF3 = PI.get_client_traffic(user.username) # Client Info From 3x-ui
|
return "error"
|
||||||
URI = self.generate_uri(vpn_config=vpn_server.config,CIF3=CIF3)
|
|
||||||
|
cost = current_plan['cost']
|
||||||
|
if user.balance >= cost:
|
||||||
|
user.balance -= cost
|
||||||
session.commit()
|
session.commit()
|
||||||
return URI
|
result = self.tariff_setting(user, plan, current_plan['duration'])
|
||||||
|
if result == "OK":
|
||||||
|
add_server_result = self.add_to_server(telegram_id)
|
||||||
|
if add_server_result == "OK":
|
||||||
|
return "OK"
|
||||||
|
else:
|
||||||
|
return "ERROR " + add_server_result
|
||||||
|
else:
|
||||||
|
return "ERROR " + result
|
||||||
|
|
||||||
|
self.logger.error(f"Недостаточно средств на счету пользователя {telegram_id} для тарифа {plan}.")
|
||||||
|
return 100
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Чё то ошибка в создании uri: {e}")
|
self.logger.error(f"Ошибка при покупке тарифа для пользователя {telegram_id}: {e}")
|
||||||
|
session.rollback()
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
|
def get_sub_list(self, count: int, user_id: int):
|
||||||
|
session = next(get_db_session())
|
||||||
|
try:
|
||||||
|
return (
|
||||||
|
session.query(Subscription)
|
||||||
|
.filter(Subscription.user_id == user_id)
|
||||||
|
.order_by(desc(Subscription.created_at))
|
||||||
|
.limit(count)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Ошибка при получении списка подписок для пользователя {user_id}: {e}")
|
||||||
|
|
||||||
|
def add_to_server(self, telegram_id: str):
|
||||||
|
session = next(get_db_session())
|
||||||
|
try:
|
||||||
|
user_sub = (
|
||||||
|
session.query(Subscription)
|
||||||
|
.join(User)
|
||||||
|
.filter(User.telegram_id == telegram_id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
||||||
|
server = session.query(VPNServer).filter(VPNServer.id == user_sub.vpn_server_id).first()
|
||||||
|
|
||||||
|
url_base = f"https://{server.ip_address}:{server.port}/{server.secret}"
|
||||||
|
login_data = {
|
||||||
|
'username': server.login,
|
||||||
|
'password': server.password,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Преобразование server.config из строки в словарь
|
||||||
|
try:
|
||||||
|
server_config_dict = json.loads(server.config)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
self.logger.error(f"Ошибка разбора JSON: {e}")
|
||||||
|
return "180"
|
||||||
|
|
||||||
|
client_id = server_config_dict['obj']['id']
|
||||||
|
panel = PanelInteraction(url_base, login_data, self.logger)
|
||||||
|
panel.add_client(client_id, user_sub.expiry_date.isoformat(), user.username)
|
||||||
|
return "OK"
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Ошибка при установке на сервер для пользователя {telegram_id}: {e}")
|
||||||
|
return "ERROR"
|
||||||
|
|
||||||
|
def create_uri(self, telegram_id: str):
|
||||||
|
session = next(get_db_session())
|
||||||
|
try:
|
||||||
|
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
||||||
|
if not user:
|
||||||
|
self.logger.error(f"Пользователь с Telegram ID {telegram_id} не найден.")
|
||||||
|
return "error"
|
||||||
|
|
||||||
|
sub = self.last_subscription(user)
|
||||||
|
if not sub:
|
||||||
|
self.logger.error("Подписка не найдена.")
|
||||||
|
return "error"
|
||||||
|
|
||||||
|
vpn_server = session.query(VPNServer).filter_by(id=sub.vpn_server_id).first()
|
||||||
|
base_url = f"https://{vpn_server.ip_address}:{vpn_server.port}/{vpn_server.secret}"
|
||||||
|
login_data = {
|
||||||
|
'username': vpn_server.login,
|
||||||
|
'password': vpn_server.password
|
||||||
|
}
|
||||||
|
|
||||||
|
server_config_dict = json.loads(vpn_server.config)
|
||||||
|
client_id = server_config_dict['obj']['id']
|
||||||
|
|
||||||
|
PI = PanelInteraction(base_url, login_data, self.logger)
|
||||||
|
CIF3 = PI.get_client_traffic(user.username) # Client Info From 3x-ui
|
||||||
|
VPNCIF3 = PI.getInboundInfo(client_id)
|
||||||
|
return self.generate_uri(vpn_config=VPNCIF3, CIF3=CIF3)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Ошибка в создании URI: {e}")
|
||||||
|
return "error"
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
def generate_uri(self, vpn_config, CIF3):
|
def generate_uri(self, vpn_config, CIF3):
|
||||||
try:
|
try:
|
||||||
# Извлечение необходимых данных из конфигурации
|
# Проверяем тип vpn_config и загружаем его, если это строка
|
||||||
config_data = json.loads(vpn_config)
|
config_data = json.loads(vpn_config) if isinstance(vpn_config, str) else vpn_config
|
||||||
|
|
||||||
obj = config_data["obj"]
|
obj = config_data["obj"]
|
||||||
port = obj["port"]
|
port = obj["port"]
|
||||||
clients = json.loads(obj["settings"])["clients"]
|
|
||||||
|
|
||||||
# Поиск клиента по email (CIF3)
|
# Обрабатываем настройки клиентов
|
||||||
|
clients = json.loads(obj["settings"])["clients"] if isinstance(obj["settings"], str) else obj["settings"]["clients"]
|
||||||
|
|
||||||
for client in clients:
|
for client in clients:
|
||||||
if client["email"] == CIF3:
|
if client["email"] == CIF3['obj']['email']:
|
||||||
uuid = client["id"]
|
uuid = client["id"]
|
||||||
flow = client["flow"]
|
flow = client["flow"]
|
||||||
|
|
||||||
# Извлечение параметров из streamSettings
|
# Извлечение параметров из streamSettings
|
||||||
stream_settings = json.loads(obj["streamSettings"])
|
stream_settings = json.loads(obj["streamSettings"]) if isinstance(obj["streamSettings"], str) else obj["streamSettings"]
|
||||||
dest = stream_settings["realitySettings"]["dest"]
|
dest = stream_settings["realitySettings"]["dest"]
|
||||||
server_names = stream_settings["realitySettings"]["serverNames"]
|
server_names = stream_settings["realitySettings"]["serverNames"]
|
||||||
public_key = stream_settings["realitySettings"]["settings"]["publicKey"]
|
public_key = stream_settings["realitySettings"]["settings"]["publicKey"]
|
||||||
@@ -132,19 +255,15 @@ class UserService:
|
|||||||
short_id = stream_settings["realitySettings"]["shortIds"][0] # Первый короткий ID
|
short_id = stream_settings["realitySettings"]["shortIds"][0] # Первый короткий ID
|
||||||
|
|
||||||
# Сборка строки VLess
|
# Сборка строки VLess
|
||||||
URI = (
|
return (
|
||||||
f"vless://{uuid}@{dest}:{port}?type=tcp&security=reality"
|
f"vless://{uuid}@{dest}:{port}?type=tcp&security=reality"
|
||||||
f"&pbk={public_key}&fp={fingerprint}&sni={server_names[0]}"
|
f"&pbk={public_key}&fp={fingerprint}&sni={server_names[0]}"
|
||||||
f"&sid={short_id}&spx=%2F&flow={flow}#user-{CIF3}"
|
f"&sid={short_id}&spx=%2F&flow={flow}#user-{CIF3}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return URI
|
self.logger.error(f"Клиент с email {CIF3} не найден.")
|
||||||
|
|
||||||
# Если клиент с указанным email не найден
|
|
||||||
self.logger.warning(f"Клиент с email {CIF3} не найден.")
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Ошибка в методе создания uri: {e}")
|
self.logger.error(f"Ошибка в методе создания URI: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user