Create project
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
*.json
|
||||||
|
pyvenv.cfg
|
||||||
|
bin/
|
||||||
|
include/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
lib64
|
||||||
107
bot.py
Normal file
107
bot.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
|
||||||
|
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
|
||||||
|
from telegram.ext import Application, CallbackQueryHandler, CommandHandler, ContextTypes
|
||||||
|
from db import User
|
||||||
|
from db import Subscription
|
||||||
|
from db import Transaction
|
||||||
|
from db import VPNServer
|
||||||
|
from sqlalchemy import desc
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
from db import get_db_session
|
||||||
|
from db import init_db, SessionLocal
|
||||||
|
from logger_config import setup_logger
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("Личный кабинет", callback_data="account"),
|
||||||
|
InlineKeyboardButton("О нac ;)", callback_data='about'),
|
||||||
|
],
|
||||||
|
[InlineKeyboardButton("Поддержка", callback_data='support')],
|
||||||
|
]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
await update.message.reply_text(f'Добро пожаловать в ...! Здесь вы можете приобрести VPN. И нечего более',reply_markup=reply_markup)
|
||||||
|
|
||||||
|
async def personal_account(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("Пополнить баланс", callback_data="pop_up"),
|
||||||
|
InlineKeyboardButton("Приобрести подписку", callback_data='buy_tarif'),
|
||||||
|
],
|
||||||
|
[InlineKeyboardButton("FAQ", callback_data='faq')],
|
||||||
|
[InlineKeyboardButton("История платежей", callback_data='payment_history')]
|
||||||
|
]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
|
session = next(get_db_session())
|
||||||
|
user = session.query(User).filter(User.telegram_id == update.chat_member.from_user.id).first()
|
||||||
|
check = last_subscription(session=session,user=user)
|
||||||
|
|
||||||
|
if not check:
|
||||||
|
await update.message.reply_text(f'Профиль {user.username}\nВы не приобретали ещё у нас подписку, но это явно стоит сделать:)',reply_markup=reply_markup)
|
||||||
|
# проверяем, истекла ли подписка
|
||||||
|
if check.expiry_date < datetime.now():
|
||||||
|
await update.message.reply_text(f'Ваш профиль {user.username},Ваша подписка действует до - {check.expiry_date}',reply_markup=reply_markup)
|
||||||
|
else:
|
||||||
|
await update.message.reply_text(f'Ваш профиль {user.username},\nВаша подписка истекла - {check.expiry_date}',reply_markup=reply_markup)
|
||||||
|
|
||||||
|
async def about(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("Главное меню", callback_data="account")
|
||||||
|
]]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
await update.message.reply_text(f'Игорь чё нить напишет, я продублирую',reply_markup=reply_markup)
|
||||||
|
|
||||||
|
async def support(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("Главное меню", callback_data="account"),
|
||||||
|
InlineKeyboardButton("Написать", callback_data="sup") # Нужно через каллбек доделать
|
||||||
|
]]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
await update.message.reply_text(f'Что бы отправить сообщение поддержке выберите в меню кнопку "Написать", а далее изложите в одном сообщении свою ошибку.',reply_markup=reply_markup)
|
||||||
|
|
||||||
|
async def pop_up(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("Главное меню", callback_data="account"),
|
||||||
|
]]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
await update.message.reply_text(f'Когда нибудь эта штука заработает',reply_markup=reply_markup)
|
||||||
|
|
||||||
|
#async def buy_subscription(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
logger = setup_logger()
|
||||||
|
init_db()
|
||||||
|
db = SessionLocal()
|
||||||
|
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.run_polling(allowed_updates=Update.ALL_TYPES)
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
84
db.py
Normal file
84
db.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
from sqlalchemy import create_engine, Column, String, Integer, Numeric, DateTime, ForeignKey, Text
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker, relationship
|
||||||
|
from datetime import datetime
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
def generate_uuid():
|
||||||
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
#Пользователи
|
||||||
|
class User(Base):
|
||||||
|
__tablename__ = 'users'
|
||||||
|
|
||||||
|
id = Column(String, primary_key=True, default=generate_uuid)
|
||||||
|
telegram_id = Column(String, unique=True, nullable=False)
|
||||||
|
username = Column(String) # email 3x-ui
|
||||||
|
balance = Column(Numeric(10, 2), default=0.0)
|
||||||
|
created_at = Column(DateTime, default=datetime.now)
|
||||||
|
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||||
|
|
||||||
|
subscriptions = relationship("Subscription", back_populates="user")
|
||||||
|
transactions = relationship("Transaction", back_populates="user")
|
||||||
|
|
||||||
|
#Подписки
|
||||||
|
class Subscription(Base):
|
||||||
|
__tablename__ = 'subscriptions'
|
||||||
|
|
||||||
|
id = Column(String, primary_key=True, default=generate_uuid)
|
||||||
|
user_id = Column(String, ForeignKey('users.id'))
|
||||||
|
vpn_server_id = Column(String, ForeignKey('vpn_servers.id'))
|
||||||
|
plan = Column(String)
|
||||||
|
expiry_date = Column(DateTime)
|
||||||
|
created_at = Column(DateTime, default=datetime.now)
|
||||||
|
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||||
|
|
||||||
|
user = relationship("User", back_populates="subscriptions")
|
||||||
|
vpn_server = relationship("VPNServer", back_populates="subscriptions")
|
||||||
|
|
||||||
|
#Транзакции
|
||||||
|
class Transaction(Base):
|
||||||
|
__tablename__ = 'transactions'
|
||||||
|
|
||||||
|
id = Column(String, primary_key=True, default=generate_uuid)
|
||||||
|
user_id = Column(String, ForeignKey('users.id'))
|
||||||
|
amount = Column(Numeric(10, 2))
|
||||||
|
transaction_type = Column(String)
|
||||||
|
created_at = Column(DateTime, default=datetime.now)
|
||||||
|
|
||||||
|
user = relationship("User", back_populates="transactions")
|
||||||
|
|
||||||
|
# VPN-серверы
|
||||||
|
class VPNServer(Base):
|
||||||
|
__tablename__ = 'vpn_servers'
|
||||||
|
|
||||||
|
id = Column(String, primary_key=True, default=generate_uuid)
|
||||||
|
server_name = Column(String)
|
||||||
|
ip_address = Column(String)
|
||||||
|
port = Column(Integer)
|
||||||
|
login_data = Column(Text)
|
||||||
|
inbound = Column(Text)
|
||||||
|
config = Column(Text)
|
||||||
|
current_users = Column(Integer, default=0)
|
||||||
|
max_users = Column(Integer, default=4)
|
||||||
|
|
||||||
|
subscriptions = relationship("Subscription", back_populates="vpn_server")
|
||||||
|
|
||||||
|
# Настройка подключения к базе данных
|
||||||
|
DATABASE_URL = "postgresql://vpn_bot_user:yourpassword@localhost/vpn_bot_db"
|
||||||
|
|
||||||
|
engine = create_engine(DATABASE_URL)
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
def init_db():
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
def get_db_session():
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
21
logger_config.py
Normal file
21
logger_config.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import logging
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
|
def setup_logger():
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
handler = TimedRotatingFileHandler(
|
||||||
|
"app.log",
|
||||||
|
when="midnight",
|
||||||
|
interval=1,
|
||||||
|
backupCount=7,
|
||||||
|
encoding='utf-8'
|
||||||
|
)
|
||||||
|
|
||||||
|
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
logger.addHandler(handler)
|
||||||
|
|
||||||
|
return logger
|
||||||
133
panel.py
Normal file
133
panel.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import requests
|
||||||
|
import uuid
|
||||||
|
import string
|
||||||
|
import secrets
|
||||||
|
import json
|
||||||
|
from logger_config import setup_logger
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
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():
|
||||||
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
|
class PanelInteraction:
|
||||||
|
def __init__(self, base_url, login_data, logger_):
|
||||||
|
self.base_url = base_url
|
||||||
|
self.login_data = login_data
|
||||||
|
self.logger = logger_
|
||||||
|
self.session_id = self.login()
|
||||||
|
if self.session_id:
|
||||||
|
self.headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Cookie': f'3x-ui={self.session_id}',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
raise ValueError("Login failed, session_id is None")
|
||||||
|
|
||||||
|
def login(self):
|
||||||
|
login_url = self.base_url + "/login"
|
||||||
|
response = requests.post(login_url, data=self.login_data)
|
||||||
|
if response.status_code == 200:
|
||||||
|
session_id = response.cookies.get("3x-ui")
|
||||||
|
return session_id
|
||||||
|
else:
|
||||||
|
self.logger.error(f"Login failed: {response.status_code}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getInboundInfo(self,inboundId):
|
||||||
|
url = f"{self.base_url}/panel/api/inbounds/get/{inboundId}"
|
||||||
|
response = requests.get(url, headers=self.headers)
|
||||||
|
if response.status_code == 200:
|
||||||
|
return response.json()
|
||||||
|
else:
|
||||||
|
self.logger.error(f"Failed to get inbound info: {response.status_code}")
|
||||||
|
self.logger.debug("Response:", response.text)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_client_traffic(self, email):
|
||||||
|
url = f"{self.base_url}/panel/api/inbounds/getClientTraffics/{email}"
|
||||||
|
response = requests.get(url, headers=self.headers)
|
||||||
|
if response.status_code == 200:
|
||||||
|
return response.json()
|
||||||
|
else:
|
||||||
|
self.logger.error(f"Failed to get client traffic: {response.status_code}")
|
||||||
|
self.logger.debug("Response:", response.text)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update_client_expiry(self, client_uuid, new_expiry_time, client_email):
|
||||||
|
url = f"{self.base_url}/panel/api/inbounds/updateClient"
|
||||||
|
update_data = {
|
||||||
|
"id": 1,
|
||||||
|
"settings": json.dumps({
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": client_uuid,
|
||||||
|
"alterId": 0,
|
||||||
|
"email": client_email,
|
||||||
|
"limitIp": 2,
|
||||||
|
"totalGB": 0,
|
||||||
|
"expiryTime": new_expiry_time,
|
||||||
|
"enable": True,
|
||||||
|
"tgId": "",
|
||||||
|
"subId": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
response = requests.post(url, headers=self.headers, json=update_data)
|
||||||
|
if response.status_code == 200:
|
||||||
|
self.logger.debug("Client expiry time updated successfully.")
|
||||||
|
else:
|
||||||
|
self.logger.error(f"Failed to update client: {response.status_code} {response.text}")
|
||||||
|
|
||||||
|
def add_client(self, inbound_id, months):
|
||||||
|
url = f"{self.base_url}/panel/api/inbounds/addClient"
|
||||||
|
client_info = {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": generate_uuid(),
|
||||||
|
"alterId": 0,
|
||||||
|
"email": generate_random_string(),
|
||||||
|
"limitIp": 2,
|
||||||
|
"totalGB": 0,
|
||||||
|
"expiryTime": generate_date(months),
|
||||||
|
"enable": True,
|
||||||
|
"tgId": "",
|
||||||
|
"subId": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
"id": inbound_id,
|
||||||
|
"settings": json.dumps(client_info)
|
||||||
|
}
|
||||||
|
response = requests.post(url, headers=self.headers, json=payload)
|
||||||
|
if response.status_code == 200:
|
||||||
|
self.logger.debug("Client added successfully!")
|
||||||
|
return response.json()
|
||||||
|
else:
|
||||||
|
self.logger.error(f"Failed to add client: {response.status_code}")
|
||||||
|
self.logger.debug("Response:", response.text)
|
||||||
|
return None
|
||||||
145
service.py
Normal file
145
service.py
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
from db import User
|
||||||
|
from db import Subscription
|
||||||
|
from db import Transaction
|
||||||
|
from db import VPNServer
|
||||||
|
from datetime import datetime,timedelta
|
||||||
|
from db import get_db_session
|
||||||
|
import json
|
||||||
|
from panel import PanelInteraction
|
||||||
|
|
||||||
|
with open('config.json', 'r') as file : config = json.load(file)
|
||||||
|
|
||||||
|
class UserService:
|
||||||
|
def __init__(self,logger):
|
||||||
|
self.logger = logger
|
||||||
|
|
||||||
|
def add_user(self,telegram_id: int, username: str):
|
||||||
|
session = next(get_db_session())
|
||||||
|
try:
|
||||||
|
new_user = User(telegram_id=telegram_id, username=username)
|
||||||
|
session.add(new_user)
|
||||||
|
session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
session.rollback()
|
||||||
|
self.logger.error(f"Ошибка при добавлении пользователя: {e}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
def get_user_by_telegram_id(self,telegram_id: int):
|
||||||
|
session = next(get_db_session())
|
||||||
|
try:
|
||||||
|
return session.query(User).filter(User.telegram_id == telegram_id).first()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Ошибка при получении пользователя: {e}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
def add_transaction(self,user_id: int,amount: float):
|
||||||
|
session = next(get_db_session())
|
||||||
|
try:
|
||||||
|
transaction = Transaction(user_id = user_id,amount = amount)
|
||||||
|
session.add(transaction)
|
||||||
|
session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Ошибка добавления транзакции:{e}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
def pop_up_balance(self,telegram_id: int,amount: float):
|
||||||
|
session = next(get_db_session())
|
||||||
|
try:
|
||||||
|
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
||||||
|
if user:
|
||||||
|
user.balance = amount
|
||||||
|
self.add_transaction(user.id,amount)
|
||||||
|
session.commit()
|
||||||
|
else:
|
||||||
|
self.logger.warning(f"Пользователь с Telegram ID {telegram_id} не найден.")
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Ошибка при обновлении баланса:{e}")
|
||||||
|
self.logger.error(f"Сумма: {amount}, Пользователь: {telegram_id}")
|
||||||
|
session.rollback()
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
def tariff_setting(self,telegram_id: int,plan: str):
|
||||||
|
session = next(get_db_session())
|
||||||
|
try:
|
||||||
|
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
||||||
|
if user:
|
||||||
|
server = (
|
||||||
|
session.query(VPNServer)
|
||||||
|
.filter(VPNServer.current_users < VPNServer.max_users)
|
||||||
|
.order_by(VPNServer.current_users.asc())
|
||||||
|
.first())
|
||||||
|
current_plan = config['subscription_templates'].get(plan)
|
||||||
|
expiry_ = datetime.now() + timedelta(days=current_plan['expiry_duration'])
|
||||||
|
new_subscription = Subscription(user_id = user.id,vpn_server_id = server.id,plan = plan,expiry_date = expiry_)
|
||||||
|
session.add(new_subscription)
|
||||||
|
session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Чё то ошибка в установке тарифа: {e}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
|
||||||
|
def create_uri(self,telegram_id,):
|
||||||
|
session = next(get_db_session())
|
||||||
|
try:
|
||||||
|
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
||||||
|
if user:
|
||||||
|
subscription = user.subscriptions
|
||||||
|
if not subscription:
|
||||||
|
return None
|
||||||
|
vpn_server = session.query(VPNServer).filter_by(id=subscription.vpn_server_id).first()
|
||||||
|
baseURL ="http://" + vpn_server.ip_address + ":" + vpn_server.port
|
||||||
|
PI = PanelInteraction(baseURL,vpn_server.login_data,self.logger)
|
||||||
|
CIF3 = PI.get_client_traffic(user.username) # Client Info From 3x-ui
|
||||||
|
URI = self.generate_uri(vpn_config=vpn_server.config,CIF3=CIF3)
|
||||||
|
session.commit()
|
||||||
|
return URI
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Чё то ошибка в создании uri: {e}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_uri(self, vpn_config, CIF3):
|
||||||
|
try:
|
||||||
|
# Извлечение необходимых данных из конфигурации
|
||||||
|
config_data = json.loads(vpn_config)
|
||||||
|
obj = config_data["obj"]
|
||||||
|
port = obj["port"]
|
||||||
|
clients = json.loads(obj["settings"])["clients"]
|
||||||
|
|
||||||
|
# Поиск клиента по email (CIF3)
|
||||||
|
for client in clients:
|
||||||
|
if client["email"] == CIF3:
|
||||||
|
uuid = client["id"]
|
||||||
|
flow = client["flow"]
|
||||||
|
|
||||||
|
# Извлечение параметров из streamSettings
|
||||||
|
stream_settings = json.loads(obj["streamSettings"])
|
||||||
|
dest = stream_settings["realitySettings"]["dest"]
|
||||||
|
server_names = stream_settings["realitySettings"]["serverNames"]
|
||||||
|
public_key = stream_settings["realitySettings"]["settings"]["publicKey"]
|
||||||
|
fingerprint = stream_settings["realitySettings"]["settings"]["fingerprint"]
|
||||||
|
short_id = stream_settings["realitySettings"]["shortIds"][0] # Первый короткий ID
|
||||||
|
|
||||||
|
# Сборка строки VLess
|
||||||
|
URI = (
|
||||||
|
f"vless://{uuid}@{dest}:{port}?type=tcp&security=reality"
|
||||||
|
f"&pbk={public_key}&fp={fingerprint}&sni={server_names[0]}"
|
||||||
|
f"&sid={short_id}&spx=%2F&flow={flow}#user-{CIF3}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return URI
|
||||||
|
|
||||||
|
# Если клиент с указанным email не найден
|
||||||
|
self.logger.warning(f"Клиент с email {CIF3} не найден.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Ошибка в методе создания uri: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
Reference in New Issue
Block a user