Merge pull request #9 from Vladless/new

New
This commit is contained in:
Vladislav Lisitsyn
2024-09-23 16:42:31 +03:00
committed by GitHub
16 changed files with 170 additions and 152 deletions
+6 -4
View File
@@ -1,13 +1,15 @@
import requests
from config import ADMIN_USERNAME, ADMIN_PASSWORD, GET_INBOUNDS_URL
import json
import requests
from config import API_URL, GET_INBOUNDS_URL
session = None
def login_with_credentials(username, password):
global session
session = requests.Session()
auth_url = "https://solonet.pocomacho.ru:62553/solonet/login/"
auth_url = f"{API_URL}/login/"
data = {
"username": username,
"password": password
@@ -39,7 +41,7 @@ def link(session, client_id: str, email: str):
if 'obj' not in response or len(response['obj']) == 0:
raise Exception("Не удалось получить данные клиентов.")
inbounds = response['obj'][0] # Убедитесь, что это правильный индекс
inbounds = response['obj'][0]
settings = json.loads(inbounds['settings'])
# Найти клиентский ID в настройках
+3 -3
View File
@@ -1,17 +1,17 @@
from aiogram import Bot, Dispatcher, Router
from aiogram.fsm.storage.memory import MemoryStorage
from config import API_TOKEN
from auth import login_with_credentials
from config import API_TOKEN
bot = Bot(token=API_TOKEN)
storage = MemoryStorage()
dp = Dispatcher(bot=bot, storage=storage)
router = Router()
from handlers import start, profile, keys, balance, pay
from handlers import balance, keys, pay, profile, start
from key_management import router as key_management_router
# Регистрация обработчиков
dp.include_router(start.router)
dp.include_router(profile.router)
+7 -4
View File
@@ -1,7 +1,10 @@
import json
from config import API_URL
def add_client(session, client_id: str, email: str, tg_id: str, limit_ip: int, total_gb: int, expiry_time: int, enable: bool, flow: str):
url = 'https://solonet.pocomacho.ru:62553/solonet/panel/api/inbounds/addClient'
url = f'{API_URL}/panel/api/inbounds/addClient'
email = email.lower()
@@ -41,7 +44,7 @@ def add_client(session, client_id: str, email: str, tg_id: str, limit_ip: int, t
print(f"Ошибка при добавлении клиента: {response.status_code}, {response.text}")
def extend_client_key(session, tg_id, client_id, email: str, new_expiry_time: int) -> bool:
response = session.get(f"https://solonet.pocomacho.ru:62553/solonet/panel/api/inbounds/getClientTraffics/{email}")
response = session.get(f"{API_URL}/panel/api/inbounds/getClientTraffics/{email}")
print(f"GET {response.url} Status: {response.status_code}")
print(f"GET Response: {response.text}")
@@ -89,7 +92,7 @@ def extend_client_key(session, tg_id, client_id, email: str, new_expiry_time: in
}
try:
response = session.post(f"https://solonet.pocomacho.ru:62553/solonet/panel/api/inbounds/updateClient/{client_id}", json=payload, headers=headers)
response = session.post(f"{API_URL}/panel/api/inbounds/updateClient/{client_id}", json=payload, headers=headers)
print(f"POST {response.url} Status: {response.status_code}")
print(f"POST Request Data: {json.dumps(payload, indent=2)}")
print(f"POST Response: {response.text}")
@@ -104,7 +107,7 @@ def extend_client_key(session, tg_id, client_id, email: str, new_expiry_time: in
return False
def delete_client(session, client_id: str) -> bool:
url = f"https://solonet.pocomacho.ru:62553/solonet/panel/api/inbounds/1/delClient/{client_id}"
url = f"{API_URL}/panel/api/inbounds/1/delClient/{client_id}"
headers = {
'Accept': 'application/json'
}
+5 -2
View File
@@ -1,6 +1,9 @@
import asyncpg
from datetime import datetime
from config import DATABASE_URL
import asyncpg
from config import DATABASE_URL
async def init_db():
conn = await asyncpg.connect(DATABASE_URL)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
from aiogram import types, Router
import aiosqlite
from aiogram import Router, types
router = Router()
+8 -7
View File
@@ -1,12 +1,13 @@
import asyncpg
from aiogram import types, Router
from bot import bot
from datetime import datetime, timedelta
from database import get_balance, update_balance
from client import extend_client_key
from config import ADMIN_USERNAME, DATABASE_URL, ADMIN_PASSWORD
import asyncpg
from aiogram import Router, types
from auth import login_with_credentials
from client import extend_client_key, delete_client
from bot import bot
from client import delete_client, extend_client_key
from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL
from database import get_balance, update_balance
router = Router()
+6 -5
View File
@@ -1,12 +1,13 @@
from aiogram import types, Router
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
import asyncpg
from aiogram import Router, types
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from config import ADMIN_ID, DATABASE_URL
from database import get_balance, update_balance, get_key_count
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from bot import bot
from config import ADMIN_ID, DATABASE_URL
from database import get_balance, get_key_count, update_balance
from handlers.profile import process_callback_view_profile
import asyncpg
router = Router()
+23 -14
View File
@@ -1,9 +1,12 @@
from aiogram import types, Router
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from datetime import datetime
from aiogram import Router, types
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from datetime import datetime
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from database import get_balance, get_key_count
from bot import bot # Импортируем объект bot
router = Router()
@@ -13,6 +16,7 @@ class ReplenishBalanceState(StatesGroup):
async def process_callback_view_profile(callback_query: types.CallbackQuery, state: FSMContext):
tg_id = callback_query.from_user.id
username = callback_query.from_user.full_name # Получаем имя пользователя
try:
# Получаем количество ключей
@@ -24,38 +28,43 @@ async def process_callback_view_profile(callback_query: types.CallbackQuery, sta
balance = 0 # По умолчанию 0, если баланс неизвестен
profile_message = (
f"<b>Профиль пользователя</b>\n\n"
f"<b>Профиль: {username}</b>\n\n"
f"🔹 <b>ID:</b> {tg_id}\n"
f"🔹 <b>Количество ключей:</b> {key_count}\n"
f"🔹 <b>Баланс:</b> {balance} RUB\n"
f"🔹 <b>Дата регистрации:</b> {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}\n"
f"🔹 <b>Количество ключей:</b> {key_count}\n"
)
# Кнопки для действий в профиле
button_create_key = InlineKeyboardButton(text='Создать ключ', callback_data='create_key')
button_view_keys = InlineKeyboardButton(text='Мои ключи', callback_data='view_keys')
button_replenish_balance = InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance')
# Кнопки для действий в профиле с добавленными смайликами
button_create_key = InlineKeyboardButton(text='🔑 Создать ключ', callback_data='create_key')
button_view_keys = InlineKeyboardButton(text='🔐 Мои ключи', callback_data='view_keys')
button_replenish_balance = InlineKeyboardButton(text='💳 Пополнить баланс', callback_data='replenish_balance')
button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_menu') # Добавляем кнопку "Назад"
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[button_create_key], # Добавляем кнопку "Создать ключ"
[button_create_key],
[button_view_keys],
[button_replenish_balance],
[button_back] # Добавляем кнопку "Назад" в клавиатуру
[button_back]
])
except Exception as e:
profile_message = f"❗️ Ошибка при получении данных профиля: {e}"
keyboard = None
# Редактируем существующее сообщение, а не отправляем новое
await callback_query.message.edit_text(
# Удаляем текущее сообщение
await callback_query.message.delete()
# Отправляем новое сообщение с профилем через bot
await bot.send_message(
chat_id=tg_id, # ID чата, куда отправляем сообщение
text=profile_message,
parse_mode='HTML',
reply_markup=keyboard
)
await callback_query.answer()
@router.callback_query(lambda c: c.data == 'view_profile')
async def view_profile_handler(callback_query: types.CallbackQuery, state: FSMContext):
await process_callback_view_profile(callback_query, state)
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

+58 -74
View File
@@ -1,47 +1,66 @@
from aiogram import Router, types
from aiogram.filters import Command
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message, ReplyKeyboardMarkup, KeyboardButton
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.types import (CallbackQuery, InlineKeyboardButton,
InlineKeyboardMarkup, Message)
from aiogram.types import BufferedInputFile
import os
from bot import bot
from config import ADMIN_ID, CHANNEL_URL
from bot import bot # Убедитесь, что bot и dp импортируются из правильного места
router = Router()
class FeedbackState(StatesGroup):
waiting_for_feedback = State()
async def start_command(message: types.Message):
async def send_welcome_message(chat_id: int):
# Новый текст приветствия
welcome_text = (
"*Добро пожаловать в наш сервис!*\n\n"
"Вы можете воспользоваться следующими функциями:\n\n"
"🔑 *Создать ключ для подключения VPN* - Получите уникальный ключ для доступа к VPN.\n"
"📅 *Узнать дату окончания ключа* - Проверьте, когда истекает срок действия вашего ключа.\n"
"👤 *Просмотреть ваш профиль* - Получите информацию о вашем аккаунте и балансе.\n\n"
"Нажмите на соответствующую кнопку ниже, чтобы начать!"
"*SoloNet -- ваш провайдер в бесцензурный интернет!*\n\n"
"Получите высокую скорость и самый безопасный протокол VPN, "
"который работает даже в Китае."
)
button_view_profile = InlineKeyboardButton(text='👤 Мой профиль', callback_data='view_profile')
button_about_vpn = InlineKeyboardButton(text='🔒 О VPN', callback_data='about_vpn')
button_feedback = InlineKeyboardButton(text='📝 Обратная связь', callback_data='feedback')
button_channel = InlineKeyboardButton(text='📢 Наш канал', url=CHANNEL_URL)
# Путь к изображению
image_path = os.path.join(os.path.dirname(__file__), 'solo_pic.png')
# Проверка существования файла
if not os.path.isfile(image_path):
await bot.send_message(chat_id, "Файл изображения не найден.")
return
# Создаем клавиатуру
inline_keyboard = InlineKeyboardMarkup(inline_keyboard=[
[button_view_profile],
[button_about_vpn],
[button_feedback],
[button_channel]
[InlineKeyboardButton(text='👤 Мой профиль', callback_data='view_profile')],
[InlineKeyboardButton(text='🔒 О VPN', callback_data='about_vpn')],
[InlineKeyboardButton(text='📝 Обратная связь', callback_data='feedback')],
[InlineKeyboardButton(text='📢 Наш канал', url=CHANNEL_URL)]
])
await message.bot.send_message(
chat_id=message.chat.id,
text=welcome_text,
parse_mode='Markdown',
reply_markup=inline_keyboard
)
# Используем BufferedInputFile для отправки изображения
with open(image_path, 'rb') as image_from_buffer:
await bot.send_photo(
chat_id,
BufferedInputFile(
image_from_buffer.read(),
filename="solo_pic.png"
),
caption=welcome_text,
parse_mode='Markdown',
reply_markup=inline_keyboard
)
@router.message(Command('start'))
async def start_command(message: Message):
await send_welcome_message(message.chat.id)
@router.callback_query(lambda c: c.data == 'about_vpn')
async def handle_about_vpn(callback_query: CallbackQuery):
# Удаляем сообщение главного меню
await callback_query.message.delete()
info_message = (
"*О VPN*\n\n"
"Мы используем VLESS для обеспечения безопасного и надежного подключения к интернету. "
@@ -49,69 +68,39 @@ async def handle_about_vpn(callback_query: CallbackQuery):
"Этот ключ необходим для использования нашего VPN-сервиса."
)
# Кнопка "Назад"
button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_menu')
inline_keyboard_back = InlineKeyboardMarkup(inline_keyboard=[[button_back]])
# Обновлённая клавиатура с кнопкой "Назад"
inline_keyboard_back = InlineKeyboardMarkup(inline_keyboard=[
[button_back]
])
await callback_query.message.edit_text(
await callback_query.message.answer(
info_message,
parse_mode='Markdown',
reply_markup=inline_keyboard_back # Добавление клавиатуры с кнопкой "Назад"
reply_markup=inline_keyboard_back
)
await callback_query.answer()
@router.callback_query(lambda c: c.data == 'back_to_menu')
async def handle_back_to_menu(callback_query: CallbackQuery):
welcome_text = (
"*Добро пожаловать в наш сервис!*\n\n"
"Вы можете воспользоваться следующими функциями:\n\n"
"🔑 *Создать ключ для подключения VPN* - Получите уникальный ключ для доступа к VPN.\n"
"📅 *Узнать дату окончания ключа* - Проверьте, когда истекает срок действия вашего ключа.\n"
"👤 *Просмотреть ваш профиль* - Получите информацию о вашем аккаунте и балансе.\n\n"
"Нажмите на соответствующую кнопку ниже, чтобы начать!"
)
button_view_profile = InlineKeyboardButton(text='👤 Мой профиль', callback_data='view_profile')
button_about_vpn = InlineKeyboardButton(text='🔒 О VPN', callback_data='about_vpn')
button_feedback = InlineKeyboardButton(text='📝 Обратная связь', callback_data='feedback')
button_channel = InlineKeyboardButton(text='📢 Наш канал', url=CHANNEL_URL)
inline_keyboard = InlineKeyboardMarkup(inline_keyboard=[
[button_view_profile],
[button_about_vpn],
[button_feedback],
[button_channel]
])
# Редактирование сообщения на главное меню
await callback_query.message.edit_text(
welcome_text,
parse_mode='Markdown',
reply_markup=inline_keyboard
)
# Удаляем текущее сообщение
await callback_query.message.delete()
# Отправляем приветственное сообщение
await send_welcome_message(callback_query.from_user.id)
await callback_query.answer()
@router.callback_query(lambda c: c.data == 'feedback')
async def handle_feedback(callback_query: CallbackQuery, state: FSMContext):
# Удаляем сообщение главного меню
await callback_query.message.delete()
feedback_text = "Напишите нам, если у вас возникли трудности с подключением, есть отзыв или предложение. @pocomacho"
# Кнопка "Назад"
button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_menu')
inline_keyboard_back = InlineKeyboardMarkup(inline_keyboard=[[button_back]])
# Обновлённая клавиатура с кнопкой "Назад"
inline_keyboard_back = InlineKeyboardMarkup(inline_keyboard=[
[button_back]
])
# Редактирование сообщения с кнопкой "Назад"
await callback_query.message.edit_text(
await callback_query.message.answer(
feedback_text,
parse_mode='Markdown',
reply_markup=inline_keyboard_back # Клавиатура с кнопкой "Назад"
reply_markup=inline_keyboard_back
)
await state.set_state(FeedbackState.waiting_for_feedback)
@@ -127,17 +116,12 @@ async def receive_feedback(message: types.Message, state: FSMContext):
f"Обратная связь от пользователя {user_id}:\n\n"
f"{message.text}"
)
try:
await bot.send_message(ADMIN_ID, feedback_message)
await message.answer("Спасибо за ваше сообщение! Мы свяжемся с вами, если это будет необходимо.")
except Exception as e:
await message.answer("Произошла ошибка при отправке вашего сообщения.")
print(f"Ошибка при отправке обратной связи: {e}") # Логирование ошибок
await state.clear()
# Пример обработки команды старт
@router.message(Command('start'))
async def cmd_start(message: Message):
await start_command(message)
await state.clear()
+53 -38
View File
@@ -1,21 +1,24 @@
import asyncio
import re
import uuid
import asyncio
from datetime import datetime, timedelta
from aiogram import Router, types, F
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from auth import login_with_credentials, link
from client import add_client
from config import API_TOKEN, ADMIN_PASSWORD, ADMIN_USERNAME, ADMIN_CHAT_ID, DATABASE_URL
from database import add_connection, has_active_key, get_balance, store_key, update_balance
from bot import dp, bot
from handlers.profile import process_callback_view_profile
from handlers.start import start_command
import asyncpg
from aiogram import F, Router, types
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.types import (CallbackQuery, InlineKeyboardButton,
InlineKeyboardMarkup, Message)
from auth import link, login_with_credentials
from bot import bot, dp
from client import add_client
from config import (ADMIN_CHAT_ID, ADMIN_PASSWORD, ADMIN_USERNAME, API_TOKEN,
DATABASE_URL)
from database import (add_connection, get_balance, has_active_key, store_key,
update_balance)
from handlers.profile import process_callback_view_profile
from handlers.start import start_command
router = Router()
@@ -42,7 +45,7 @@ async def process_callback_create_key(callback_query: CallbackQuery, state: FSMC
if trial_status == 1:
await callback_query.message.edit_text(
"У вас уже был пробный ключ. Вы можете создать новый за 100 рублей. "
"У вас уже был пробный ключ. Новый стоит 100 рублей и сразу на месяц. \n\n"
"Хотите продолжить?",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text='Да, создать новый ключ', callback_data='confirm_create_new_key')],
@@ -65,11 +68,15 @@ async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContex
if balance < 100:
replenish_button = InlineKeyboardButton(text='Перейти в профиль', callback_data='view_profile')
keyboard = InlineKeyboardMarkup(inline_keyboard=[[replenish_button]])
await callback_query.message.edit_text("Недостаточно средств на балансе для создания нового ключа.", reply_markup=keyboard)
await callback_query.message.edit_text(
"❗️ Недостаточно средств на балансе для создания нового ключа. "
"Пожалуйста, пополните баланс.",
reply_markup=keyboard
)
await state.clear()
return
await callback_query.message.edit_text("Пожалуйста, выберите имя для вашего нового ключа:")
await callback_query.message.edit_text("🔑 Пожалуйста, выберите имя для вашего нового ключа:")
await state.set_state(Form.waiting_for_key_name)
await state.update_data(creating_new_key=True)
@@ -109,8 +116,8 @@ async def handle_key_name_input(message: Message, state: FSMContext):
key_name = sanitize_key_name(message.text)
if not key_name:
await message.bot.send_message(tg_id, "Назовите профиль на английском языке.")
return # Прерываем выполнение функции
await message.bot.send_message(tg_id, "📝 Пожалуйста, назовите профиль на английском языке.")
return
data = await state.get_data()
creating_new_key = data.get('creating_new_key', False)
@@ -119,28 +126,32 @@ async def handle_key_name_input(message: Message, state: FSMContext):
client_id = str(uuid.uuid4())
email = key_name.lower()
current_time = datetime.utcnow()
expiry_time = int((current_time + timedelta(days=1)).timestamp() * 1000)
expiry_time = None
if creating_new_key:
# Проверяем, занято ли имя ключа
conn = await asyncpg.connect(DATABASE_URL)
try:
existing_key = await conn.fetchrow('SELECT * FROM keys WHERE email = $1', email)
if existing_key:
await message.bot.send_message(tg_id, "Это имя уже занято. Пожалуйста, выберите другое имя.")
return # Прерываем выполнение функции
finally:
await conn.close()
# Получаем статус пробного ключа из базы данных
conn = await asyncpg.connect(DATABASE_URL)
try:
existing_connection = await conn.fetchrow('SELECT trial FROM connections WHERE tg_id = $1', tg_id)
finally:
await conn.close()
trial_status = existing_connection['trial'] if existing_connection else 0
if trial_status == 0:
# Создаем пробный ключ на 1 день
expiry_time = int((current_time + timedelta(days=1)).timestamp() * 1000)
else:
# Проверяем баланс перед созданием нового ключа
balance = await get_balance(tg_id)
if balance < 100:
replenish_button = InlineKeyboardButton(text='Перейти в профиль', callback_data='view_profile')
keyboard = InlineKeyboardMarkup(inline_keyboard=[[replenish_button]])
await message.bot.send_message(tg_id, "Недостаточно средств на балансе для создания нового ключа.", reply_markup=keyboard)
await message.bot.send_message(tg_id, "❗️ Недостаточно средств на балансе для создания нового ключа.", reply_markup=keyboard)
await state.clear()
return
await update_balance(tg_id, -100)
expiry_time = int((current_time + timedelta(days=30)).timestamp() * 1000)
try:
add_client(session, client_id, email, tg_id, limit_ip=1, total_gb=0, expiry_time=expiry_time, enable=True, flow="xtls-rprx-vision")
@@ -162,32 +173,36 @@ async def handle_key_name_input(message: Message, state: FSMContext):
await store_key(tg_id, client_id, email, expiry_time, connection_link)
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text='Инструкции по использованию', callback_data='instructions')],
[InlineKeyboardButton(text='Перейти в профиль', callback_data='view_profile')]
[InlineKeyboardButton(text='📖 Инструкции по использованию', callback_data='instructions')],
[InlineKeyboardButton(text='🔙 Перейти в профиль', callback_data='view_profile')]
])
key_message = f"Ключ создан:\n<pre>{connection_link}</pre>"
key_message = (
"✅ Ключ успешно создан:\n"
f"<pre>{connection_link}</pre>"
)
await message.bot.send_message(tg_id, key_message, parse_mode="HTML", reply_markup=keyboard)
except Exception as e:
await message.bot.send_message(tg_id, f"Ошибка при создании ключа: {e}")
await message.bot.send_message(tg_id, f"Ошибка при создании ключа: {e}")
await state.clear()
@dp.callback_query(F.data == 'instructions')
async def handle_instructions(callback_query: CallbackQuery):
instructions_message = (
"*Инструкции по использованию вашего ключа:*\n\n"
"*📋 Инструкции по использованию вашего ключа:*\n\n"
"1. Скачайте приложение для вашего устройства:\n"
" - Для Android: [V2Ray](https://play.google.com/store/apps/details?id=com.v2ray.ang&hl=ru&pli=1)\n"
" - Для iPhone: [Streisand](https://apps.apple.com/ru/app/streisand/id6450534064)\n\n"
"2. Скопируйте предоставленный ключ, который вы получили ранее.\n"
"3. Откройте приложение и нажмите на плюсик сверху справа.\n"
"4. Выберите 'Вставить из буфера обмена' для добавления ключа.\n\n"
"Если у вас возникнут вопросы, не стесняйтесь обращаться в поддержку."
"💬 Если у вас возникнут вопросы, не стесняйтесь обращаться в поддержку."
)
back_button = InlineKeyboardButton(text='Назад', callback_data='back_to_main')
back_button = InlineKeyboardButton(text='🔙 Назад', callback_data='back_to_main')
keyboard = InlineKeyboardMarkup(inline_keyboard=[[back_button]])
await callback_query.message.edit_text(instructions_message, parse_mode='Markdown', reply_markup=keyboard)