Merge pull request #36 from Vladless/new1
основной текст в отдельном файле
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import subprocess
|
||||
import os
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
|
||||
from aiogram.types import BufferedInputFile
|
||||
|
||||
from bot import bot
|
||||
from config import ADMIN_ID, DB_USER, DB_NAME, DB_PASSWORD
|
||||
from config import ADMIN_ID, DB_NAME, DB_PASSWORD, DB_USER
|
||||
|
||||
|
||||
async def backup_database():
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ storage = MemoryStorage()
|
||||
dp = Dispatcher(bot=bot, storage=storage)
|
||||
router = Router()
|
||||
|
||||
from handlers import key_management, keys, notifications, pay, profile, start, backup_handler
|
||||
from handlers import (backup_handler, key_management, keys, notifications, pay,
|
||||
profile, start)
|
||||
|
||||
dp.include_router(start.router)
|
||||
dp.include_router(profile.router)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import json
|
||||
|
||||
import aiohttp
|
||||
|
||||
from config import SERVERS
|
||||
from config import SERVERS
|
||||
|
||||
|
||||
async def add_client(session, server_id: str, client_id: str, email: str, tg_id: str, limit_ip: int, total_gb: int, expiry_time: int, enable: bool, flow: str):
|
||||
|
||||
+1
-7
@@ -126,7 +126,6 @@ async def update_balance(tg_id: int, amount: float):
|
||||
WHERE tg_id = $2
|
||||
''', amount, tg_id)
|
||||
|
||||
# Проверка и начисление реферального бонуса
|
||||
await handle_referral_on_balance_update(tg_id, amount)
|
||||
|
||||
await conn.close()
|
||||
@@ -157,7 +156,6 @@ async def add_referral(referred_tg_id: int, referrer_tg_id: int):
|
||||
async def handle_referral_on_balance_update(tg_id: int, amount: float):
|
||||
conn = await asyncpg.connect(DATABASE_URL)
|
||||
|
||||
# Проверяем, есть ли реферал в таблице
|
||||
referral = await conn.fetchrow('''
|
||||
SELECT referrer_tg_id FROM referrals WHERE referred_tg_id = $1
|
||||
''', tg_id)
|
||||
@@ -165,17 +163,13 @@ async def handle_referral_on_balance_update(tg_id: int, amount: float):
|
||||
if referral:
|
||||
referrer_tg_id = referral['referrer_tg_id']
|
||||
|
||||
# Начисляем бонус (25% от платежа) пригласившему
|
||||
bonus = amount * 0.25 # 25% от платежа
|
||||
bonus = amount * 0.25
|
||||
|
||||
# Проверяем, чтобы бонус не был отрицательным
|
||||
if bonus < 0:
|
||||
bonus = 0
|
||||
|
||||
# Обновляем баланс пригласившего
|
||||
await update_balance(referrer_tg_id, bonus)
|
||||
|
||||
# Обновляем статус бонуса для пригласившего
|
||||
await conn.execute('''
|
||||
UPDATE referrals SET reward_issued = TRUE
|
||||
WHERE referrer_tg_id = $1 AND referred_tg_id = $2
|
||||
|
||||
@@ -9,6 +9,6 @@ router = Router()
|
||||
@router.message(Command('backup'))
|
||||
async def backup_command(message: Message):
|
||||
await message.answer("Запускаю бэкап базы данных...")
|
||||
await backup_database() # Запуск функции бэкапа
|
||||
await backup_database()
|
||||
await message.answer("Бэкап завершен и отправлен админу.")
|
||||
|
||||
|
||||
@@ -1,28 +1,21 @@
|
||||
import os
|
||||
|
||||
from aiogram import types
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from aiogram.types import BufferedInputFile
|
||||
from aiogram.types import (BufferedInputFile, InlineKeyboardButton,
|
||||
InlineKeyboardMarkup)
|
||||
|
||||
from handlers.texts import INSTRUCTIONS
|
||||
|
||||
|
||||
async def send_instructions(callback_query: types.CallbackQuery):
|
||||
# Удаляем предыдущее сообщение
|
||||
await callback_query.message.delete()
|
||||
|
||||
instructions_message = (
|
||||
"*📋 Инструкции по использованию вашего ключа:*\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"
|
||||
" - **Для Windows:** [Hiddify Next](https://github.com/hiddify/hiddify-next/releases/latest/download/Hiddify-Windows-Setup-x64.Msix)\n\n"
|
||||
"2. **Скопируйте предоставленный ключ**, который вы получили ранее.\n"
|
||||
"3. **Откройте приложение и нажмите на плюсик сверху справа.**\n"
|
||||
"4. **Выберите 'Вставить из буфера обмена' для добавления ключа.**\n\n"
|
||||
"💬 Если у вас возникнут вопросы, не стесняйтесь обращаться в [поддержку](https://t.me/solonet_sup)."
|
||||
INSTRUCTIONS
|
||||
)
|
||||
|
||||
# Путь к изображению
|
||||
image_path = os.path.join(os.path.dirname(__file__), 'instructions.jpg')
|
||||
|
||||
# Проверка существования файла изображения
|
||||
if not os.path.isfile(image_path):
|
||||
await callback_query.message.answer("Файл изображения не найден.")
|
||||
await callback_query.answer()
|
||||
@@ -31,7 +24,6 @@ async def send_instructions(callback_query: types.CallbackQuery):
|
||||
back_button = InlineKeyboardButton(text='🔙 Назад', callback_data='back_to_main')
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[[back_button]])
|
||||
|
||||
# Отправка изображения и сообщения
|
||||
with open(image_path, 'rb') as image_from_buffer:
|
||||
await callback_query.message.answer_photo(
|
||||
BufferedInputFile(image_from_buffer.read(), filename="instructions.jpg"),
|
||||
|
||||
+16
-24
@@ -1,25 +1,27 @@
|
||||
import re
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from aiogram.filters import Command
|
||||
|
||||
import asyncpg
|
||||
from aiogram import F, Router, types
|
||||
from aiogram.filters import Command
|
||||
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 dp, bot
|
||||
from bot import bot, dp
|
||||
from client import add_client
|
||||
from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, SERVERS, ADMIN_ID
|
||||
from config import (ADMIN_ID, ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL,
|
||||
SERVERS)
|
||||
from database import add_connection, get_balance, store_key, update_balance
|
||||
from handlers.backup_handler import backup_command
|
||||
from handlers.instructions import send_instructions
|
||||
from handlers.pay import ReplenishBalanceState, process_custom_amount_input
|
||||
from handlers.profile import process_callback_view_profile
|
||||
from handlers.start import start_command
|
||||
from handlers.pay import ReplenishBalanceState, process_custom_amount_input
|
||||
from handlers.backup_handler import backup_command
|
||||
from handlers.texts import KEY, KEY_TRIAL, NULL_BALANCE
|
||||
|
||||
router = Router()
|
||||
|
||||
@@ -41,16 +43,14 @@ async def process_callback_create_key(callback_query: CallbackQuery, state: FSMC
|
||||
try:
|
||||
for server_id, server in SERVERS.items():
|
||||
count = await conn.fetchval('SELECT COUNT(*) FROM keys WHERE server_id = $1', server_id)
|
||||
# Используем 60 как максимальное количество ключей
|
||||
percent_full = (count / 60) * 100 if count <= 60 else 100
|
||||
server_name = f"{server['name']} ({percent_full:.1f}%)"
|
||||
server_buttons.append([InlineKeyboardButton(text=server_name, callback_data=f'select_server|{server_id}')])
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
# Добавляем кнопку "Назад"
|
||||
button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='view_profile')
|
||||
server_buttons.append([button_back]) # Кнопка "Назад" внизу списка серверов
|
||||
server_buttons.append([button_back])
|
||||
|
||||
await callback_query.message.edit_text(
|
||||
"<b>⚙️ Выберите сервер для создания ключа:</b>",
|
||||
@@ -78,9 +78,7 @@ async def select_server(callback_query: CallbackQuery, state: FSMContext):
|
||||
|
||||
if trial_status == 1:
|
||||
await callback_query.message.edit_text(
|
||||
"<b>⚠️ У вас уже был пробный ключ.</b>\n\n"
|
||||
"Новый ключ будет выдан на <b>один месяц</b> и стоит <b>100 рублей</b>.\n\n"
|
||||
"<i>Хотите продолжить?</i>",
|
||||
KEY,
|
||||
parse_mode="HTML",
|
||||
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text='✅ Да, создать новый ключ', callback_data='confirm_create_new_key')],
|
||||
@@ -90,8 +88,7 @@ async def select_server(callback_query: CallbackQuery, state: FSMContext):
|
||||
await state.update_data(creating_new_key=True)
|
||||
else:
|
||||
await callback_query.message.edit_text(
|
||||
"<b>🎉 Вам будет выдан пробный ключ на 24 часа!</b>\n\n"
|
||||
"<i>Пожалуйста, введите название для вашего пробного ключа:</i>",
|
||||
KEY_TRIAL,
|
||||
parse_mode="HTML"
|
||||
)
|
||||
await state.set_state(Form.waiting_for_key_name)
|
||||
@@ -109,8 +106,7 @@ async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContex
|
||||
replenish_button = InlineKeyboardButton(text='Перейти в профиль', callback_data='view_profile')
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[[replenish_button]])
|
||||
await callback_query.message.edit_text(
|
||||
"❗️ Недостаточно средств на балансе для создания нового ключа. "
|
||||
"Пожалуйста, пополните баланс.",
|
||||
NULL_BALANCE,
|
||||
reply_markup=keyboard
|
||||
)
|
||||
await state.clear()
|
||||
@@ -140,7 +136,6 @@ async def handle_send_trial_command(message: types.Message, state: FSMContext):
|
||||
try:
|
||||
conn = await asyncpg.connect(DATABASE_URL)
|
||||
try:
|
||||
# Получаем всех пользователей с не использованным пробным ключом
|
||||
records = await conn.fetch('''
|
||||
SELECT tg_id FROM connections WHERE trial = 0
|
||||
''')
|
||||
@@ -153,10 +148,8 @@ async def handle_send_trial_command(message: types.Message, state: FSMContext):
|
||||
"Пожалуйста, воспользуйтесь им, чтобы протестировать наш сервис."
|
||||
)
|
||||
try:
|
||||
# Отправляем сообщение каждому пользователю
|
||||
await bot.send_message(chat_id=tg_id, text=trial_message)
|
||||
except Exception as e:
|
||||
# Если бот был заблокирован пользователем, просто пропускаем его
|
||||
if "Forbidden: bot was blocked by the user" in str(e):
|
||||
print(f"Бот заблокирован пользователем с tg_id: {tg_id}")
|
||||
else:
|
||||
@@ -179,11 +172,11 @@ async def send_message_to_all_clients(message: types.Message, state: FSMContext)
|
||||
return
|
||||
|
||||
await message.answer("Введите текст сообщения, который вы хотите отправить всем клиентам:")
|
||||
await state.set_state(Form.waiting_for_message) # Устанавливаем состояние ожидания сообщения
|
||||
await state.set_state(Form.waiting_for_message)
|
||||
|
||||
@router.message(Form.waiting_for_message)
|
||||
async def process_message_to_all(message: types.Message, state: FSMContext):
|
||||
text_message = message.text # Получаем текст сообщения
|
||||
text_message = message.text
|
||||
|
||||
try:
|
||||
conn = await asyncpg.connect(DATABASE_URL)
|
||||
@@ -203,14 +196,14 @@ async def process_message_to_all(message: types.Message, state: FSMContext):
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
await state.clear() # Очистка состояния после отправки сообщения
|
||||
await state.clear()
|
||||
|
||||
@router.message()
|
||||
async def handle_text(message: types.Message, state: FSMContext):
|
||||
current_state = await state.get_state()
|
||||
|
||||
if message.text in ["/send_to_all"]:
|
||||
await send_message_to_all_clients(message, state) # Передаем состояние
|
||||
await send_message_to_all_clients(message, state)
|
||||
return
|
||||
|
||||
if message.text == "Мой профиль":
|
||||
@@ -225,7 +218,6 @@ async def handle_text(message: types.Message, state: FSMContext):
|
||||
return
|
||||
|
||||
if current_state == ReplenishBalanceState.entering_custom_amount.state:
|
||||
# Здесь вызовите ваш обработчик ввода суммы
|
||||
await process_custom_amount_input(message, state)
|
||||
return
|
||||
|
||||
@@ -235,7 +227,7 @@ async def handle_text(message: types.Message, state: FSMContext):
|
||||
if message.text == "/backup":
|
||||
await backup_command(message)
|
||||
|
||||
elif current_state is None: # Если состояние не задано, обрабатываем обычные сообщения
|
||||
elif current_state is None:
|
||||
await start_command(message)
|
||||
|
||||
|
||||
|
||||
+3
-11
@@ -1,3 +1,4 @@
|
||||
import locale
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import asyncpg
|
||||
@@ -8,7 +9,7 @@ from bot import bot
|
||||
from client import add_client, delete_client, extend_client_key
|
||||
from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, SERVERS
|
||||
from database import get_balance, update_balance
|
||||
import locale
|
||||
from handlers.texts import NO_KEYS
|
||||
|
||||
locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8')
|
||||
|
||||
@@ -33,7 +34,6 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery):
|
||||
button = types.InlineKeyboardButton(text=f"🔑 {key_name}", callback_data=f'view_key|{key_name}|{client_id}')
|
||||
buttons.append([button])
|
||||
|
||||
# Добавляем кнопку "Назад" в конце списка
|
||||
back_button = types.InlineKeyboardButton(text='🔙 Назад', callback_data='view_profile')
|
||||
buttons.append([back_button])
|
||||
|
||||
@@ -46,9 +46,7 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery):
|
||||
await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=inline_keyboard, parse_mode="HTML")
|
||||
else:
|
||||
response_message = (
|
||||
"<b>У вас нет подключённых устройств</b> 😕\n\n"
|
||||
"➕ <b>Создайте первое устройство, чтобы подключиться к VPN</b>.\n"
|
||||
"Ваши устройства будут отображаться здесь."
|
||||
NO_KEYS
|
||||
)
|
||||
create_key_button = types.InlineKeyboardButton(text='➕ Создать ключ', callback_data='create_key')
|
||||
back_button = types.InlineKeyboardButton(text='🔙 Назад', callback_data='view_profile')
|
||||
@@ -99,7 +97,6 @@ async def process_callback_view_key(callback_query: types.CallbackQuery):
|
||||
hours_left = time_left.seconds // 3600
|
||||
days_left_message = f"Осталось часов: <b>{hours_left}</b>"
|
||||
|
||||
# Форматируем дату окончания в удобочитаемом виде
|
||||
formatted_expiry_date = expiry_date.strftime('%d %B %Y года')
|
||||
|
||||
response_message = (f"🔑 <b>Ваш ключ:</b>\n<pre>{key}</pre>\n"
|
||||
@@ -318,7 +315,6 @@ async def process_callback_select_server(callback_query: types.CallbackQuery):
|
||||
try:
|
||||
conn = await asyncpg.connect(DATABASE_URL)
|
||||
try:
|
||||
# Начинаем транзакцию с блокировкой строки по client_id, чтобы предотвратить параллельную миграцию
|
||||
async with conn.transaction():
|
||||
record = await conn.fetchrow(
|
||||
'SELECT email, expiry_time, server_id FROM keys WHERE client_id = $1 FOR UPDATE', client_id
|
||||
@@ -333,7 +329,6 @@ async def process_callback_select_server(callback_query: types.CallbackQuery):
|
||||
await callback_query.answer("Клиент уже на этом сервере.")
|
||||
return
|
||||
|
||||
# Проверка наличия клиента на новом сервере перед добавлением
|
||||
session_new = await login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD)
|
||||
new_client_data = await add_client(
|
||||
session_new, server_id, client_id, email, tg_id, limit_ip=1, total_gb=0,
|
||||
@@ -344,16 +339,13 @@ async def process_callback_select_server(callback_query: types.CallbackQuery):
|
||||
if not new_client_data:
|
||||
raise Exception("Ошибка при создании клиента на новом сервере.")
|
||||
|
||||
# Генерация нового ключа
|
||||
new_key = await link(session_new, server_id, client_id, email)
|
||||
|
||||
# Обновляем запись в БД
|
||||
await conn.execute(
|
||||
'UPDATE keys SET server_id = $1, key = $2 WHERE client_id = $3',
|
||||
server_id, new_key, client_id
|
||||
)
|
||||
|
||||
# Удаление клиента со старого сервера
|
||||
try:
|
||||
session_old = await login_with_credentials(current_server_id, ADMIN_USERNAME, ADMIN_PASSWORD)
|
||||
success_delete = await delete_client(session_old, current_server_id, client_id)
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import asyncpg
|
||||
from aiogram import Bot, Router, types
|
||||
from aiogram.filters import Command
|
||||
from aiogram import Bot, Router
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from auth import login_with_credentials
|
||||
from bot import bot
|
||||
from client import delete_client, extend_client_key
|
||||
from config import ADMIN_ID, ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL
|
||||
from database import get_balance, update_balance
|
||||
|
||||
from config import DATABASE_URL
|
||||
from database import get_balance
|
||||
|
||||
router = Router()
|
||||
|
||||
@@ -23,7 +20,6 @@ async def notify_expiring_keys(bot: Bot):
|
||||
threshold_time_10h = (datetime.utcnow() + timedelta(hours=10)).timestamp() * 1000
|
||||
threshold_time_24h = (datetime.utcnow() + timedelta(days=1)).timestamp() * 1000
|
||||
|
||||
# Уведомления за 10 часов
|
||||
records = await conn.fetch('''
|
||||
SELECT tg_id, email, expiry_time, client_id, server_id FROM keys
|
||||
WHERE expiry_time <= $1 AND expiry_time > $2 AND notified = FALSE
|
||||
@@ -35,17 +31,14 @@ async def notify_expiring_keys(bot: Bot):
|
||||
expiry_time = record['expiry_time']
|
||||
server_id = record['server_id']
|
||||
|
||||
# Логика формирования и отправки сообщения для уведомлений за 10 часов
|
||||
message = f"🔔 Уведомление: Ваш ключ для сервера {server_id} истекает через 10 часов.\n" \
|
||||
f"Email: {email}\n" \
|
||||
f"Дата истечения: {datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
|
||||
await bot.send_message(tg_id, message)
|
||||
|
||||
# Обновление статуса уведомления
|
||||
await conn.execute('UPDATE keys SET notified = TRUE WHERE client_id = $1', record['client_id'])
|
||||
|
||||
# Уведомления за 24 часа
|
||||
records_24h = await conn.fetch('''
|
||||
SELECT tg_id, email, expiry_time, client_id, server_id FROM keys
|
||||
WHERE expiry_time <= $1 AND expiry_time > $2 AND notified_24h = FALSE
|
||||
@@ -63,7 +56,6 @@ async def notify_expiring_keys(bot: Bot):
|
||||
expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S')
|
||||
balance = await get_balance(tg_id)
|
||||
|
||||
# Логика формирования и отправки сообщения для уведомлений за 24 часа
|
||||
message_24h = f"⏳ Уведомление: Ваш ключ для сервера {server_id} истекает через 24 часа.\n" \
|
||||
f"Email: {email}\n" \
|
||||
f"Осталось времени: {hours_left} часов\n" \
|
||||
@@ -72,7 +64,6 @@ async def notify_expiring_keys(bot: Bot):
|
||||
|
||||
await bot.send_message(tg_id, message_24h)
|
||||
|
||||
# Обновление статуса уведомления за 24 часа
|
||||
await conn.execute('UPDATE keys SET notified_24h = TRUE WHERE client_id = $1', record['client_id'])
|
||||
|
||||
finally:
|
||||
|
||||
+4
-8
@@ -6,7 +6,7 @@ from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from aiohttp import web
|
||||
from yookassa import Configuration, Payment
|
||||
from yookassa import Configuration, Payment
|
||||
|
||||
from bot import bot
|
||||
from config import YOOKASSA_SECRET_KEY, YOOKASSA_SHOP_ID
|
||||
@@ -58,7 +58,6 @@ async def process_callback_replenish_balance(callback_query: types.CallbackQuery
|
||||
if not exists:
|
||||
await add_connection(tg_id, balance=0.0, trial=0)
|
||||
|
||||
# Клавиатура с фиксированными суммами и новой кнопкой "Ввести сумму"
|
||||
amount_keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text='100 RUB', callback_data='amount_100'), InlineKeyboardButton(text='300 RUB', callback_data='amount_300')],
|
||||
[InlineKeyboardButton(text='600 RUB', callback_data='amount_600'), InlineKeyboardButton(text='1000 RUB', callback_data='amount_1000')],
|
||||
@@ -99,7 +98,6 @@ async def process_amount_selection(callback_query: types.CallbackQuery, state: F
|
||||
customer_name = callback_query.from_user.full_name
|
||||
customer_id = callback_query.from_user.id
|
||||
|
||||
# Используем tg_id для email
|
||||
customer_email = f"{customer_id}@solo.net"
|
||||
|
||||
payment = Payment.create({
|
||||
@@ -116,7 +114,7 @@ async def process_amount_selection(callback_query: types.CallbackQuery, state: F
|
||||
"receipt": {
|
||||
"customer": {
|
||||
"full_name": customer_name,
|
||||
"email": customer_email, # Используем email в формате "tg_id@solo.net"
|
||||
"email": customer_email,
|
||||
"phone": "79000000000"
|
||||
},
|
||||
"items": [
|
||||
@@ -201,7 +199,6 @@ async def process_enter_custom_amount(callback_query: types.CallbackQuery, state
|
||||
|
||||
@router.message(State(ReplenishBalanceState.entering_custom_amount))
|
||||
async def process_custom_amount_input(message: types.Message, state: FSMContext):
|
||||
# Проверяем, является ли введенное значение числом
|
||||
if message.text.isdigit():
|
||||
amount = int(message.text)
|
||||
if amount <= 0:
|
||||
@@ -211,7 +208,6 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext)
|
||||
await state.update_data(amount=amount)
|
||||
await state.set_state(ReplenishBalanceState.waiting_for_payment_confirmation)
|
||||
|
||||
# Создание платежа
|
||||
try:
|
||||
payment = Payment.create({
|
||||
"amount": {
|
||||
@@ -227,8 +223,8 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext)
|
||||
"receipt": {
|
||||
"customer": {
|
||||
"full_name": message.from_user.full_name,
|
||||
"email": f"{message.from_user.id}@solo.net", # Используем tg_id как email
|
||||
"phone": "79000000000" # Здесь можно использовать номер телефона клиента, если он доступен
|
||||
"email": f"{message.from_user.id}@solo.net",
|
||||
"phone": "79000000000"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
|
||||
+8
-7
@@ -1,10 +1,11 @@
|
||||
from aiogram import Router, types
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
|
||||
from bot import bot
|
||||
from database import get_balance, get_key_count, get_referral_stats
|
||||
|
||||
from bot import bot
|
||||
from database import get_balance, get_key_count, get_referral_stats, get_keys
|
||||
|
||||
class ReplenishBalanceState(StatesGroup):
|
||||
choosing_transfer_method = State()
|
||||
@@ -43,10 +44,10 @@ async def process_callback_view_profile(callback_query: types.CallbackQuery, sta
|
||||
button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_menu')
|
||||
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[button_create_key, button_view_keys], # В один ряд "➕ Устройство" и "📱 Мои устройства"
|
||||
[button_replenish_balance], # Отдельная кнопка для пополнения баланса
|
||||
[button_invite, button_instructions], # В один ряд "👥 Пригласить" и "📘 Инструкции"
|
||||
[button_back] # Отдельная кнопка "Назад"
|
||||
[button_create_key, button_view_keys],
|
||||
[button_replenish_balance],
|
||||
[button_invite, button_instructions],
|
||||
[button_back]
|
||||
])
|
||||
|
||||
except Exception as e:
|
||||
|
||||
+6
-29
@@ -1,13 +1,15 @@
|
||||
import os
|
||||
|
||||
from aiogram import Router
|
||||
from aiogram.filters import Command
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import (BufferedInputFile, CallbackQuery,
|
||||
InlineKeyboardButton, InlineKeyboardMarkup, Message)
|
||||
from texts import ABOUT_VPN, WELCOME_TEXT
|
||||
|
||||
from bot import bot
|
||||
from database import add_referral, check_connection_exists, add_connection
|
||||
from config import CHANNEL_URL, SUPPORT_CHAT_URL, BOT_VERSION # Импорт BOT_VERSION
|
||||
from config import CHANNEL_URL, SUPPORT_CHAT_URL
|
||||
from database import add_connection, add_referral, check_connection_exists
|
||||
|
||||
router = Router()
|
||||
|
||||
@@ -15,20 +17,7 @@ class FeedbackState(StatesGroup):
|
||||
waiting_for_feedback = State()
|
||||
|
||||
async def send_welcome_message(chat_id: int):
|
||||
welcome_text = (
|
||||
"<b>🎉 SoloNet — твой доступ в свободный интернет! 🌐✨</b>\n\n"
|
||||
"<b>Наши преимущества:</b>\n"
|
||||
"<blockquote>"
|
||||
"🚀 <b>Высокая скорость</b>\n"
|
||||
"🔄 <b>Стабильность</b>\n"
|
||||
"🌍 <b>Смена локаций</b>\n"
|
||||
"💬 <b>Отзывчивая поддержка</b>\n"
|
||||
"📱💻 <b>Для телефонов, компьютеров и планшетов</b>\n"
|
||||
"💰 <b>Реферальная программа: 25% от покупки</b>\n"
|
||||
"</blockquote>"
|
||||
"\n\n🎁 <b>1 день бесплатно!</b>\n\n" # Добавление версии бота
|
||||
"<i>Перейдите в профиль для продолжения 👇</i>"
|
||||
)
|
||||
welcome_text = WELCOME_TEXT
|
||||
|
||||
image_path = os.path.join(os.path.dirname(__file__), 'pic.jpg')
|
||||
|
||||
@@ -71,19 +60,7 @@ async def start_command(message: Message):
|
||||
@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"
|
||||
"<b>🚀 Высокоскоростные серверы</b>\n"
|
||||
"Мы используем высокоскоростные серверы в различных локациях для обеспечения стабильного и быстрого соединения.\n\n"
|
||||
|
||||
"<b>🔐 Безопасность данных</b>\n"
|
||||
"Для защиты ваших данных мы применяем новейшие протоколы шифрования, которые гарантируют вашу конфиденциальность.\n\n"
|
||||
|
||||
"<b>🔑 Ваш ключ — ваша безопасность!</b>\n"
|
||||
"Не передавайте своё шифрование сторонним лицам, чтобы избежать рисков.\n\n"
|
||||
|
||||
f"<i>Версия бота: {BOT_VERSION}</i>" # Добавление версии бота в информацию о VPN
|
||||
)
|
||||
info_message = ABOUT_VPN
|
||||
|
||||
button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_menu')
|
||||
inline_keyboard_back = InlineKeyboardMarkup(inline_keyboard=[[button_back]])
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
from config import BOT_VERSION
|
||||
|
||||
WELCOME_TEXT = (
|
||||
"<b>🎉 SoloNet — твой доступ в свободный интернет! 🌐✨</b>\n\n"
|
||||
"<b>Наши преимущества:</b>\n"
|
||||
"<blockquote>"
|
||||
"🚀 <b>Высокая скорость</b>\n"
|
||||
"🔄 <b>Стабильность</b>\n"
|
||||
"🌍 <b>Смена локаций</b>\n"
|
||||
"💬 <b>Отзывчивая поддержка</b>\n"
|
||||
"📱💻 <b>Для телефонов, компьютеров и планшетов</b>\n"
|
||||
"💰 <b>Реферальная программа: 25% от покупки</b>\n"
|
||||
"</blockquote>"
|
||||
"\n\n🎁 <b>1 день бесплатно!</b>\n\n"
|
||||
"<i>Перейдите в профиль для продолжения 👇</i>"
|
||||
)
|
||||
|
||||
ABOUT_VPN = (
|
||||
"*О VPN*\n\n"
|
||||
"<b>🚀 Высокоскоростные серверы</b>\n"
|
||||
"Мы используем высокоскоростные серверы в различных локациях для обеспечения стабильного и быстрого соединения.\n\n"
|
||||
|
||||
"<b>🔐 Безопасность данных</b>\n"
|
||||
"Для защиты ваших данных мы применяем новейшие протоколы шифрования, которые гарантируют вашу конфиденциальность.\n\n"
|
||||
|
||||
"<b>🔑 Ваш ключ — ваша безопасность!</b>\n"
|
||||
"Не передавайте своё шифрование сторонним лицам, чтобы избежать рисков.\n\n"
|
||||
|
||||
f"<i>Версия бота: {BOT_VERSION}</i>"
|
||||
)
|
||||
|
||||
KEY = (
|
||||
"<b>⚠️ У вас уже был пробный ключ.</b>\n\n"
|
||||
"Новый ключ будет выдан на <b>один месяц</b> и стоит <b>100 рублей</b>.\n\n"
|
||||
"<i>Хотите продолжить?</i>"
|
||||
)
|
||||
|
||||
KEY_TRIAL = (
|
||||
"<b>🎉 Вам будет выдан пробный ключ на 24 часа!</b>\n\n"
|
||||
"<i>Пожалуйста, введите название для вашего пробного ключа:</i>"
|
||||
)
|
||||
|
||||
NULL_BALANCE = (
|
||||
"❗️ Недостаточно средств на балансе для создания нового ключа. "
|
||||
"Пожалуйста, пополните баланс."
|
||||
)
|
||||
|
||||
NO_KEYS = (
|
||||
"<b>У вас нет подключённых устройств</b> 😕\n\n"
|
||||
"➕ <b>Создайте первое устройство, чтобы подключиться к VPN</b>.\n"
|
||||
"Ваши устройства будут отображаться здесь."
|
||||
)
|
||||
|
||||
INSTRUCTIONS = (
|
||||
"*📋 Инструкции по использованию вашего ключа:*\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"
|
||||
" - **Для Windows:** [Hiddify Next](https://github.com/hiddify/hiddify-next/releases/latest/download/Hiddify-Windows-Setup-x64.Msix)\n\n"
|
||||
"2. **Скопируйте предоставленный ключ**, который вы получили ранее.\n"
|
||||
"3. **Откройте приложение и нажмите на плюсик сверху справа.**\n"
|
||||
"4. **Выберите 'Вставить из буфера обмена' для добавления ключа.**\n\n"
|
||||
"💬 Если у вас возникнут вопросы, не стесняйтесь обращаться в [поддержку](https://t.me/solonet_sup)."
|
||||
)
|
||||
Reference in New Issue
Block a user