Полная реструктуризация. Тексты все в texts.py

This commit is contained in:
Vladless
2024-10-27 03:16:58 +03:00
parent 374965ad3c
commit 1c19bf6205
19 changed files with 95 additions and 71 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
from aiogram import Router, types
from aiogram.filters import Command
from database import add_balance_to_client, get_balance, check_connection_exists # Импорт необходимых функций
from database import add_balance_to_client, get_balance, check_connection_exists
from config import ADMIN_ID
router = Router()
-17
View File
@@ -1,17 +0,0 @@
from aiogram import Router
from aiogram.filters import Command
from aiogram.types import Message
from config import ADMIN_ID
router = Router()
@router.message(Command('backup'))
async def backup_command(message: Message):
if message.from_user.id != ADMIN_ID:
await message.answer("У вас нет прав для выполнения этой команды.")
return
from backup import backup_database
await message.answer("Запускаю бэкап базы данных...")
await backup_database()
await message.answer("Бэкап завершен и отправлен админу.")
+14 -3
View File
@@ -6,13 +6,13 @@ import asyncpg
from bot import bot
from config import ADMIN_ID, DATABASE_URL
from handlers.backup_handler import backup_command
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.texts import TRIAL
from handlers.admin import cmd_add_balance
from handlers.key_management import handle_key_name_input
from handlers.admin.admin import cmd_add_balance
from handlers.keys.key_management import handle_key_name_input
from aiogram.types import Message
router = Router()
@@ -22,6 +22,17 @@ class Form(StatesGroup):
viewing_profile = State()
waiting_for_message = State()
@router.message(Command('backup'))
async def backup_command(message: Message):
if message.from_user.id != ADMIN_ID:
await message.answer("У вас нет прав для выполнения этой команды.")
return
from backup import backup_database
await message.answer("Запускаю бэкап базы данных...")
await backup_database()
await message.answer("Бэкап завершен и отправлен админу.")
@router.message(Command('start'))
async def handle_start(message: types.Message, state: FSMContext):
await start_command(message)

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

@@ -1,6 +1,7 @@
import uuid
from datetime import datetime, timedelta
from bot import dp
import asyncpg
from aiogram import F, Router
from aiogram.fsm.context import FSMContext
@@ -9,12 +10,11 @@ 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_PASSWORD, ADMIN_USERNAME, DATABASE_URL,
SERVERS)
from database import add_connection, get_balance, store_key, update_balance
from handlers.instructions import send_instructions
from handlers.instructions.instructions import send_instructions
from handlers.profile import process_callback_view_profile
from handlers.texts import KEY, KEY_TRIAL, NULL_BALANCE, key_message_success
from handlers.utils import sanitize_key_name
@@ -121,7 +121,7 @@ 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, "📝 Пожалуйста, назовите профиль на английском языке.")
await message.bot.send_message(tg_id, "📝 Пожалуйста, назовите ключ устройства на английском языке.")
return
data = await state.get_data()
@@ -164,7 +164,7 @@ async def handle_key_name_input(message: Message, state: FSMContext):
if not response.get("success", True):
error_msg = response.get("msg", "Неизвестная ошибка.")
if "Duplicate email" in error_msg:
await message.bot.send_message(tg_id, "❌ Этот email уже используется. Пожалуйста, выберите другое имя для ключа.")
await message.bot.send_message(tg_id, "❌ Это имя уже используется. Пожалуйста, выберите другое имя для ключа.")
await state.set_state(Form.waiting_for_key_name)
return
else:
+14 -21
View File
@@ -11,6 +11,7 @@ from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, SERVERS
from database import get_balance, update_balance
from handlers.texts import NO_KEYS
from handlers.texts import key_message, key_relocated
from handlers.texts import RENEWAL_PLANS, INSUFFICIENT_FUNDS_MSG, KEY_NOT_FOUND_MSG, SUCCESS_RENEWAL_MSG, ERROR_RENEWAL_MSG, PLAN_SELECTION_MSG
locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8')
@@ -159,17 +160,15 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery):
expiry_time = record['expiry_time']
current_time = datetime.utcnow().timestamp() * 1000
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[
[types.InlineKeyboardButton(text='📅 1 месяц (100 руб.)', callback_data=f'renew_plan|1|{client_id}')],
[types.InlineKeyboardButton(text='📅 3 месяца (285 руб.)', callback_data=f'renew_plan|3|{client_id}')],
[types.InlineKeyboardButton(text='📅 6 месяцев (540 руб.)', callback_data=f'renew_plan|6|{client_id}')],
[types.InlineKeyboardButton(text='📅 12 месяцев (1000 руб.)', callback_data=f'renew_plan|12|{client_id}')],
[types.InlineKeyboardButton(text=f'📅 1 месяц ({RENEWAL_PLANS["1"]["price"]} руб.)', callback_data=f'renew_plan|1|{client_id}')],
[types.InlineKeyboardButton(text=f'📅 3 месяца ({RENEWAL_PLANS["3"]["price"]} руб.)', callback_data=f'renew_plan|3|{client_id}')],
[types.InlineKeyboardButton(text=f'📅 6 месяцев ({RENEWAL_PLANS["6"]["price"]} руб.)', callback_data=f'renew_plan|6|{client_id}')],
[types.InlineKeyboardButton(text=f'📅 12 месяцев ({RENEWAL_PLANS["12"]["price"]} руб.)', callback_data=f'renew_plan|12|{client_id}')],
[types.InlineKeyboardButton(text='🔙 Назад', callback_data='view_profile')]
])
balance = await get_balance(tg_id)
response_message = (f"<b>Выберите план продления:</b>\n\n"
f"💰 <b>Баланс:</b> {balance} руб.\n\n"
f"📅 <b>Текущая дата истечения ключа:</b> {datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S')}")
response_message = PLAN_SELECTION_MSG.format(balance=balance, expiry_date=datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S'))
await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard, parse_mode="HTML")
@@ -181,6 +180,7 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery):
await callback_query.answer()
@router.callback_query(lambda c: c.data.startswith('confirm_delete|'))
async def process_callback_confirm_delete(callback_query: types.CallbackQuery):
tg_id = callback_query.from_user.id
@@ -222,7 +222,7 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery):
@router.callback_query(lambda c: c.data.startswith('renew_plan|'))
async def process_callback_renew_plan(callback_query: types.CallbackQuery):
tg_id = callback_query.from_user.id
plan, client_id = callback_query.data.split('|')[1], callback_query.data.split('|')[2]
plan, client_id = callback_query.data.split('|')[1], callback_query.data.split('|')[2]
days_to_extend = 30 * int(plan)
try:
@@ -241,14 +241,7 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery):
else:
new_expiry_time = int(expiry_time + timedelta(days=days_to_extend).total_seconds() * 1000)
if plan == '1':
cost = 100
elif plan == '3':
cost = 285
elif plan == '6':
cost = 540
elif plan == '12':
cost = 1000
cost = RENEWAL_PLANS[plan]['price']
balance = await get_balance(tg_id)
if balance < cost:
@@ -256,7 +249,7 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery):
back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_profile')
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[replenish_button], [back_button]])
await bot.edit_message_text("Недостаточно средств для продления ключа.", chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard)
await bot.edit_message_text(INSUFFICIENT_FUNDS_MSG, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard)
return
session = await login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD)
@@ -265,14 +258,14 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery):
if success:
await update_balance(tg_id, -cost)
await conn.execute('UPDATE keys SET expiry_time = $1 WHERE client_id = $2', new_expiry_time, client_id)
response_message = f"Ваш ключ был успешно продлен на {days_to_extend // 30} месяц(-а)."
response_message = SUCCESS_RENEWAL_MSG.format(months=RENEWAL_PLANS[plan]['months'])
back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_profile')
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]])
await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard)
else:
await bot.edit_message_text("Ошибка при продлении ключа.", chat_id=tg_id, message_id=callback_query.message.message_id)
await bot.edit_message_text(ERROR_RENEWAL_MSG, chat_id=tg_id, message_id=callback_query.message.message_id)
else:
await bot.edit_message_text("Ключ не найден.", chat_id=tg_id, message_id=callback_query.message.message_id)
await bot.edit_message_text(KEY_NOT_FOUND_MSG, chat_id=tg_id, message_id=callback_query.message.message_id)
finally:
await conn.close()
@@ -294,7 +287,7 @@ async def process_callback_change_location(callback_query: types.CallbackQuery):
try:
for server_id, server in SERVERS.items():
count = await conn.fetchval('SELECT COUNT(*) FROM keys WHERE server_id = $1', server_id)
percent_full = (count / 100) * 100
percent_full = (count / 60) * 100 if count <= 60 else 100
server_name = f"{server['name']} ({percent_full:.1f}%)"
server_buttons.append([types.InlineKeyboardButton(text=server_name, callback_data=f'select_server&{server_id}&{client_id}')])
finally:
@@ -1,4 +1,3 @@
import random
import asyncpg
import uuid
from config import DATABASE_URL, SERVERS, ADMIN_USERNAME, ADMIN_PASSWORD
+20 -9
View File
@@ -13,6 +13,7 @@ from config import YOOKASSA_SECRET_KEY, YOOKASSA_SHOP_ID
from database import (add_connection, check_connection_exists, get_key_count,
update_balance)
from handlers.profile import process_callback_view_profile
from handlers.texts import PAYMENT_OPTIONS
router = Router()
@@ -59,10 +60,20 @@ async def process_callback_replenish_balance(callback_query: types.CallbackQuery
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')],
[InlineKeyboardButton(text='💰 Ввести свою сумму', callback_data='enter_custom_amount')],
[InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_profile')]
[
InlineKeyboardButton(text=PAYMENT_OPTIONS[0]['text'], callback_data=PAYMENT_OPTIONS[0]['callback_data']),
InlineKeyboardButton(text=PAYMENT_OPTIONS[1]['text'], callback_data=PAYMENT_OPTIONS[1]['callback_data'])
],
[
InlineKeyboardButton(text=PAYMENT_OPTIONS[2]['text'], callback_data=PAYMENT_OPTIONS[2]['callback_data']),
InlineKeyboardButton(text=PAYMENT_OPTIONS[3]['text'], callback_data=PAYMENT_OPTIONS[3]['callback_data'])
],
[
InlineKeyboardButton(text=PAYMENT_OPTIONS[4]['text'], callback_data=PAYMENT_OPTIONS[4]['callback_data'])
],
[
InlineKeyboardButton(text=PAYMENT_OPTIONS[5]['text'], callback_data=PAYMENT_OPTIONS[5]['callback_data'])
]
])
await callback_query.message.edit_text(
@@ -72,6 +83,7 @@ async def process_callback_replenish_balance(callback_query: types.CallbackQuery
await state.set_state(ReplenishBalanceState.choosing_amount)
await callback_query.answer()
@router.callback_query(lambda c: c.data == 'back_to_profile')
async def back_to_profile_handler(callback_query: types.CallbackQuery, state: FSMContext):
await process_callback_view_profile(callback_query, state)
@@ -126,9 +138,6 @@ async def process_amount_selection(callback_query: types.CallbackQuery, state: F
"currency": "RUB"
},
"vat_code": 6
## Раскоментируйте следующие строки, если у вас регистрация как ИП, а не самозанятость
## "payment_subject": "payment",
## "payment_mode": "full_payment",
}
]
},
@@ -260,7 +269,9 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext)
)
else:
await message.answer("Ошибка при создании платежа.")
except Exception as e:
await message.answer(f"Произошла ошибка при обработке платежа: {str(e)}")
logging.error(f"Ошибка при создании платежа: {e}")
await message.answer("Произошла ошибка при создании платежа.")
else:
await message.answer("Некорректный ввод. Пожалуйста, введите сумму числом:")
await message.answer("Некорректная сумма. Пожалуйста, введите сумму еще раз:")
+5 -4
View File
@@ -5,7 +5,7 @@ from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from bot import bot
from database import get_balance, get_key_count, get_referral_stats
from handlers.texts import profile_message_send, invite_message_send
from handlers.texts import profile_message_send, invite_message_send, CHANNEL_LINK, get_referral_link
class ReplenishBalanceState(StatesGroup):
@@ -13,6 +13,7 @@ class ReplenishBalanceState(StatesGroup):
waiting_for_admin_confirmation = State()
router = Router()
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
@@ -28,7 +29,7 @@ async def process_callback_view_profile(callback_query: types.CallbackQuery, sta
)
profile_message += (
f"<b>Обязательно подпишитесь на канал</b> <a href='https://t.me/solonet_vpn'>здесь</a>\n"
f"<b>Обязательно подпишитесь на канал</b> <a href='{CHANNEL_LINK}'>здесь</a>\n"
)
if key_count == 0:
@@ -66,7 +67,7 @@ async def process_callback_view_profile(callback_query: types.CallbackQuery, sta
@router.callback_query(lambda c: c.data == 'invite')
async def invite_handler(callback_query: types.CallbackQuery):
tg_id = callback_query.from_user.id
referral_link = f"https://t.me/SoloNetVPN_bot?start=referral_{tg_id}"
referral_link = get_referral_link(tg_id)
referral_stats = await get_referral_stats(tg_id)
@@ -90,4 +91,4 @@ async def invite_handler(callback_query: types.CallbackQuery):
@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)
await process_callback_view_profile(callback_query, state)
+1 -5
View File
@@ -8,7 +8,7 @@ from handlers.texts import ABOUT_VPN, WELCOME_TEXT
from bot import bot
from config import CHANNEL_URL, SUPPORT_CHAT_URL
from database import add_connection, add_referral, check_connection_exists, get_trial
from handlers.trial_key import create_trial_key
from handlers.keys.trial_key import create_trial_key
from handlers.texts import INSTRUCTIONS_TRIAL
router = Router()
@@ -65,23 +65,19 @@ async def handle_connect_vpn(callback_query: CallbackQuery):
await callback_query.message.delete()
user_id = callback_query.from_user.id
# Создаём триальный ключ
trial_key_info = await create_trial_key(user_id)
if 'error' in trial_key_info:
await callback_query.message.answer(trial_key_info['error'])
else:
# Формируем сообщение с ключом и инструкциями
key_message = (
f"<b>Ваш ключ доступа:</b>\n<pre>{trial_key_info['key']}</pre>\n\n"
f"<b>Инструкции:</b>\n{INSTRUCTIONS_TRIAL}"
)
# Кнопка "В профиль"
button_profile = InlineKeyboardButton(text='👤 Мой профиль', callback_data='view_profile')
inline_keyboard_profile = InlineKeyboardMarkup(inline_keyboard=[[button_profile]])
# Отправляем текст с ключом и инструкциями в виде цитаты
await callback_query.message.answer(
key_message,
parse_mode='HTML',
+30 -2
View File
@@ -1,5 +1,29 @@
from config import BOT_VERSION
BOT_VERSION = '1.3.2'
### Функции образования цен и кнопок продления ключа
PAYMENT_OPTIONS = [
{'text': '100 RUB', 'callback_data': 'amount_100'},
{'text': '300 RUB', 'callback_data': 'amount_300'},
{'text': '600 RUB', 'callback_data': 'amount_600'},
{'text': '1000 RUB', 'callback_data': 'amount_1000'},
{'text': '💰 Ввести свою сумму', 'callback_data': 'enter_custom_amount'},
{'text': '⬅️ Назад', 'callback_data': 'back_to_profile'},
]
RENEWAL_PLANS = {
'1': {'months': 1, 'price': 100},
'3': {'months': 3, 'price': 285},
'6': {'months': 6, 'price': 540},
'12': {'months': 12, 'price': 1000},
}
INSUFFICIENT_FUNDS_MSG = "Недостаточно средств для продления ключа."
KEY_NOT_FOUND_MSG = "Ключ не найден."
SUCCESS_RENEWAL_MSG = "Ваш ключ был успешно продлен на {months} месяц(-а)."
ERROR_RENEWAL_MSG = "Ошибка при продлении ключа."
PLAN_SELECTION_MSG = "<b>Выберите план продления:</b>\n\n💰 <b>Баланс:</b> {balance} руб.\n\n📅 <b>Текущая дата истечения ключа:</b> {expiry_date}"
### Текст главного меню
WELCOME_TEXT = (
"<b>🎉 SoloNet — твой доступ в свободный интернет! 🌐✨</b>\n\n"
"<b>Наши преимущества:</b>\n"
@@ -80,7 +104,6 @@ INSTRUCTIONS_TRIAL = (
"💬 Если у вас возникнут вопросы, не стесняйтесь обращаться в <a href='https://t.me/solonet_sup'>поддержку</a>."
)
KEY_EXPIRY_10H = "🔔 Уведомление: Ваш ключ {email} для сервера {server_id} истекает через 10 часов.\n" \
"Дата истечения: {expiry_date}"\
"Перейдите в профиль и пополните баланс, всего 100 рублей на целый месяц"\
@@ -96,6 +119,11 @@ KEY_RENEWAL_FAILED = "Не удалось продлить ключ на пан
KEY_DELETED = "Ваш ключ был удален из-за недостаточного баланса."
KEY_DELETION_FAILED = "Не удалось удалить ключ с панели, обратитесь в поддержку."
CHANNEL_LINK = "https://t.me/solonet_vpn"
def get_referral_link(user_id):
return f"https://t.me/SoloNetVPN_bot?start=referral_{user_id}"
def key_message_success(connection_link, remaining_time_message):
key_message = (
"✅ Ключ успешно создан:\n"