Merge pull request #50 from Vladless/new1

Полная реструктуризация. Тексты все в texts.py
This commit is contained in:
Vladislav Lisitsyn
2024-10-27 03:17:40 +03:00
committed by GitHub
19 changed files with 95 additions and 71 deletions
+2 -1
View File
@@ -8,4 +8,5 @@
/database.db
/backup_pg.sh
/config copy.py
/docker-compose.yml
/docker-compose.yml
__pycache__
+4 -3
View File
@@ -8,8 +8,10 @@ storage = MemoryStorage()
dp = Dispatcher(bot=bot, storage=storage)
router = Router()
from handlers import (backup_handler, key_management, keys, notifications, pay,
profile, start, admin, commands)
from handlers.admin import admin
from handlers.keys import key_management, keys
from handlers import (notifications, pay,
profile, start, commands)
dp.include_router(commands.router)
dp.include_router(start.router)
@@ -18,5 +20,4 @@ dp.include_router(keys.router)
dp.include_router(key_management.router)
dp.include_router(pay.router)
dp.include_router(notifications.router)
dp.include_router(backup_handler.router)
dp.include_router(admin.router)
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"