правки и доработки интерфейса
This commit is contained in:
+11
@@ -35,6 +35,14 @@ async def add_connection(tg_id: int, balance: float = 0.0, trial: int = 0):
|
|||||||
''', tg_id, balance, trial)
|
''', tg_id, balance, trial)
|
||||||
await conn.close()
|
await conn.close()
|
||||||
|
|
||||||
|
async def check_connection_exists(tg_id: int):
|
||||||
|
conn = await asyncpg.connect(DATABASE_URL)
|
||||||
|
exists = await conn.fetchval('''
|
||||||
|
SELECT EXISTS(SELECT 1 FROM connections WHERE tg_id = $1)
|
||||||
|
''', tg_id)
|
||||||
|
await conn.close()
|
||||||
|
return exists
|
||||||
|
|
||||||
async def store_key(tg_id: int, client_id: str, email: str, expiry_time: int, key: str):
|
async def store_key(tg_id: int, client_id: str, email: str, expiry_time: int, key: str):
|
||||||
conn = await asyncpg.connect(DATABASE_URL)
|
conn = await asyncpg.connect(DATABASE_URL)
|
||||||
await conn.execute('''
|
await conn.execute('''
|
||||||
@@ -85,3 +93,6 @@ async def get_key_count(tg_id: int) -> int:
|
|||||||
count = await conn.fetchval('SELECT COUNT(*) FROM keys WHERE tg_id = $1', tg_id)
|
count = await conn.fetchval('SELECT COUNT(*) FROM keys WHERE tg_id = $1', tg_id)
|
||||||
await conn.close()
|
await conn.close()
|
||||||
return count if count is not None else 0
|
return count if count is not None else 0
|
||||||
|
|
||||||
|
async def get_all_users(conn):
|
||||||
|
return await conn.fetch('SELECT tg_id FROM connections')
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+71
-20
@@ -11,6 +11,17 @@ from database import get_balance, update_balance
|
|||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import asyncpg
|
||||||
|
from aiogram import Router, types
|
||||||
|
from auth import login_with_credentials
|
||||||
|
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()
|
||||||
|
|
||||||
# Обработка запроса на просмотр ключей
|
# Обработка запроса на просмотр ключей
|
||||||
@router.callback_query(lambda c: c.data == 'view_keys')
|
@router.callback_query(lambda c: c.data == 'view_keys')
|
||||||
async def process_callback_view_keys(callback_query: types.CallbackQuery):
|
async def process_callback_view_keys(callback_query: types.CallbackQuery):
|
||||||
@@ -30,26 +41,26 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery):
|
|||||||
key_name = record['email']
|
key_name = record['email']
|
||||||
client_id = record['client_id']
|
client_id = record['client_id']
|
||||||
# Заменяем подчеркивание на вертикальную черту в callback_data
|
# Заменяем подчеркивание на вертикальную черту в callback_data
|
||||||
button = types.InlineKeyboardButton(text=key_name, callback_data=f'view_key|{key_name}|{client_id}')
|
button = types.InlineKeyboardButton(text=f"🔑 {key_name}", callback_data=f'view_key|{key_name}|{client_id}')
|
||||||
buttons.append([button])
|
buttons.append([button])
|
||||||
|
|
||||||
# Создаем клавиатуру с кнопками
|
# Создаем клавиатуру с кнопками
|
||||||
inline_keyboard = types.InlineKeyboardMarkup(inline_keyboard=buttons)
|
inline_keyboard = types.InlineKeyboardMarkup(inline_keyboard=buttons)
|
||||||
response_message = "Выберите ключ для просмотра информации:"
|
response_message = "<b>Выберите ключ для просмотра информации:</b>"
|
||||||
|
|
||||||
# Редактируем сообщение с клавиатурой
|
# Редактируем сообщение с клавиатурой
|
||||||
await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=inline_keyboard)
|
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:
|
else:
|
||||||
# Если нет ключей, добавляем кнопку "Создать ключ" и "Назад"
|
# Если нет ключей, добавляем кнопку "Создать ключ" и "Назад"
|
||||||
response_message = "У вас нет ключей."
|
response_message = "<b>У вас нет ключей.</b>"
|
||||||
|
|
||||||
# Кнопка "Создать ключ"
|
# Кнопка "Создать ключ"
|
||||||
create_key_button = types.InlineKeyboardButton(text='Создать ключ', callback_data='create_key')
|
create_key_button = types.InlineKeyboardButton(text='➕ Создать ключ', callback_data='create_key')
|
||||||
back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_profile') # Измените на правильное значение для кнопки "Назад"
|
back_button = types.InlineKeyboardButton(text='🔙 Назад', callback_data='view_profile') # Измените на правильное значение для кнопки "Назад"
|
||||||
|
|
||||||
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[create_key_button], [back_button]])
|
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[create_key_button], [back_button]])
|
||||||
|
|
||||||
await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard)
|
await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard, parse_mode="HTML")
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
await conn.close()
|
await conn.close()
|
||||||
@@ -82,27 +93,27 @@ async def process_callback_view_key(callback_query: types.CallbackQuery):
|
|||||||
time_left = expiry_date - current_date
|
time_left = expiry_date - current_date
|
||||||
|
|
||||||
if time_left.total_seconds() <= 0:
|
if time_left.total_seconds() <= 0:
|
||||||
days_left_message = "Ключ истек."
|
days_left_message = "<b>Ключ истек.</b>"
|
||||||
elif time_left.days > 0:
|
elif time_left.days > 0:
|
||||||
days_left_message = f"Осталось дней: {time_left.days}"
|
days_left_message = f"Осталось дней: <b>{time_left.days}</b>"
|
||||||
else:
|
else:
|
||||||
hours_left = time_left.seconds // 3600
|
hours_left = time_left.seconds // 3600
|
||||||
days_left_message = f"Осталось часов: {hours_left}"
|
days_left_message = f"Осталось часов: <b>{hours_left}</b>"
|
||||||
|
|
||||||
response_message = (f"Ваш ключ:\n<pre>{key}</pre>\n"
|
response_message = (f"🔑 <b>Ваш ключ:</b>\n<pre>{key}</pre>\n"
|
||||||
f"Дата окончания: <b>{expiry_date.strftime('%Y-%m-%d %H:%M:%S')}</b>\n"
|
f"📅 <b>Дата окончания:</b> {expiry_date.strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||||
f"{days_left_message}")
|
f"{days_left_message}")
|
||||||
|
|
||||||
# Кнопки для продления, инструкций и удаления
|
# Кнопки для продления, инструкций и удаления
|
||||||
renew_button = types.InlineKeyboardButton(text='Продлить ключ', callback_data=f'renew_key|{client_id}')
|
renew_button = types.InlineKeyboardButton(text='⏳ Продлить ключ', callback_data=f'renew_key|{client_id}')
|
||||||
instructions_button = types.InlineKeyboardButton(text='Инструкции по использованию', callback_data='instructions')
|
instructions_button = types.InlineKeyboardButton(text='📘 Инструкции', callback_data='instructions')
|
||||||
delete_button = types.InlineKeyboardButton(text='Удалить ключ', callback_data=f'delete_key|{client_id}')
|
delete_button = types.InlineKeyboardButton(text='❌ Удалить ключ', callback_data=f'delete_key|{client_id}')
|
||||||
back_button = types.InlineKeyboardButton(text='Назад в профиль', callback_data='view_profile')
|
back_button = types.InlineKeyboardButton(text='🔙 Назад в профиль', callback_data='view_profile')
|
||||||
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[renew_button], [instructions_button], [delete_button], [back_button]])
|
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[renew_button], [instructions_button], [delete_button], [back_button]])
|
||||||
|
|
||||||
await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard, parse_mode="HTML")
|
await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard, parse_mode="HTML")
|
||||||
else:
|
else:
|
||||||
await bot.edit_message_text("Информация о ключе не найдена.", chat_id=tg_id, message_id=callback_query.message.message_id, parse_mode="HTML")
|
await bot.edit_message_text("<b>Информация о ключе не найдена.</b>", chat_id=tg_id, message_id=callback_query.message.message_id, parse_mode="HTML")
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
await conn.close()
|
await conn.close()
|
||||||
@@ -119,13 +130,53 @@ async def process_callback_delete_key(callback_query: types.CallbackQuery):
|
|||||||
client_id = callback_query.data.split('|')[1] # Используем разделитель вертикальная черта
|
client_id = callback_query.data.split('|')[1] # Используем разделитель вертикальная черта
|
||||||
|
|
||||||
confirmation_keyboard = types.InlineKeyboardMarkup(inline_keyboard=[
|
confirmation_keyboard = types.InlineKeyboardMarkup(inline_keyboard=[
|
||||||
[types.InlineKeyboardButton(text='Да, удалить', callback_data=f'confirm_delete|{client_id}')],
|
[types.InlineKeyboardButton(text='✅ Да, удалить', callback_data=f'confirm_delete|{client_id}')],
|
||||||
[types.InlineKeyboardButton(text='Нет, отменить', callback_data='view_keys')]
|
[types.InlineKeyboardButton(text='❌ Нет, отменить', callback_data='view_keys')]
|
||||||
])
|
])
|
||||||
|
|
||||||
await bot.edit_message_text("Вы уверены, что хотите удалить ключ?", chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=confirmation_keyboard)
|
await bot.edit_message_text("<b>Вы уверены, что хотите удалить ключ?</b>", chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=confirmation_keyboard, parse_mode="HTML")
|
||||||
await callback_query.answer()
|
await callback_query.answer()
|
||||||
|
|
||||||
|
# Обработка выбора плана продления
|
||||||
|
@router.callback_query(lambda c: c.data.startswith('renew_key|'))
|
||||||
|
async def process_callback_renew_key(callback_query: types.CallbackQuery):
|
||||||
|
tg_id = callback_query.from_user.id
|
||||||
|
client_id = callback_query.data.split('|')[1] # Используем разделитель вертикальная черта
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = await asyncpg.connect(DATABASE_URL)
|
||||||
|
try:
|
||||||
|
record = await conn.fetchrow('SELECT email, expiry_time FROM keys WHERE client_id = $1', client_id)
|
||||||
|
|
||||||
|
if record:
|
||||||
|
email = record['email']
|
||||||
|
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 месяца (250 руб.)', callback_data=f'renew_plan|3|{client_id}')],
|
||||||
|
[types.InlineKeyboardButton(text='🔙 Назад', callback_data='view_profile')]
|
||||||
|
])
|
||||||
|
|
||||||
|
balance = await get_balance(tg_id)
|
||||||
|
response_message = (f"<b>Выберите план продления:</b>\n"
|
||||||
|
f"💰 <b>Баланс:</b> {balance} руб.\n"
|
||||||
|
f"📅 <b>Текущая дата истечения ключа:</b> {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")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
await conn.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await bot.edit_message_text(f"<b>Ошибка при выборе плана:</b> {e}", chat_id=tg_id, message_id=callback_query.message.message_id, parse_mode="HTML")
|
||||||
|
|
||||||
|
await callback_query.answer()
|
||||||
|
|
||||||
|
async def handle_error(tg_id, callback_query, message):
|
||||||
|
await bot.edit_message_text(message, chat_id=tg_id, message_id=callback_query.message.message_id)
|
||||||
|
|
||||||
# Подтверждение удаления
|
# Подтверждение удаления
|
||||||
@router.callback_query(lambda c: c.data.startswith('renew_key|'))
|
@router.callback_query(lambda c: c.data.startswith('renew_key|'))
|
||||||
|
|||||||
+21
-24
@@ -2,12 +2,18 @@ import asyncpg
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from aiogram import Bot
|
from aiogram import Bot
|
||||||
from aiogram import Router, types
|
from aiogram import Router, types
|
||||||
|
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup # Импортируем необходимые классы
|
||||||
from bot import bot
|
from bot import bot
|
||||||
|
from config import DATABASE_URL, ADMIN_ID
|
||||||
from aiogram.filters import Command
|
from aiogram.filters import Command
|
||||||
|
from database import get_all_users
|
||||||
|
from aiogram.fsm.state import StatesGroup, State
|
||||||
|
from aiogram.fsm.context import FSMContext
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|
||||||
from config import DATABASE_URL
|
class NotificationStates(StatesGroup):
|
||||||
|
waiting_for_notification_text = State()
|
||||||
|
|
||||||
async def notify_expiring_keys(bot: Bot):
|
async def notify_expiring_keys(bot: Bot):
|
||||||
try:
|
try:
|
||||||
@@ -25,8 +31,14 @@ async def notify_expiring_keys(bot: Bot):
|
|||||||
email = record['email']
|
email = record['email']
|
||||||
expiry_time = record['expiry_time']
|
expiry_time = record['expiry_time']
|
||||||
expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S')
|
expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
# Создаем клавиатуру с кнопкой "Пополнить баланс"
|
||||||
|
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
|
[InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance')]
|
||||||
|
])
|
||||||
|
|
||||||
message = f"Ваш ключ <b>{email}</b> истечет <b>{expiry_date}</b>. Пожалуйста, продлите его."
|
message = f"Ваш ключ <b>{email}</b> истечет <b>{expiry_date}</b>. Пожалуйста, продлите его."
|
||||||
await bot.send_message(chat_id=tg_id, text=message, parse_mode='HTML')
|
await bot.send_message(chat_id=tg_id, text=message, parse_mode='HTML', reply_markup=keyboard)
|
||||||
|
|
||||||
# Получаем все истекшие ключи
|
# Получаем все истекшие ключи
|
||||||
expired_records = await conn.fetch('''
|
expired_records = await conn.fetch('''
|
||||||
@@ -37,31 +49,16 @@ async def notify_expiring_keys(bot: Bot):
|
|||||||
for record in expired_records:
|
for record in expired_records:
|
||||||
tg_id = record['tg_id']
|
tg_id = record['tg_id']
|
||||||
email = record['email']
|
email = record['email']
|
||||||
|
|
||||||
|
# Создаем клавиатуру с кнопкой "Пополнить баланс"
|
||||||
|
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
|
[InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance')]
|
||||||
|
])
|
||||||
|
|
||||||
message = f"Ваш ключ <b>{email}</b> уже истек. Пожалуйста, продлите его."
|
message = f"Ваш ключ <b>{email}</b> уже истек. Пожалуйста, продлите его."
|
||||||
await bot.send_message(chat_id=tg_id, text=message, parse_mode='HTML')
|
await bot.send_message(chat_id=tg_id, text=message, parse_mode='HTML', reply_markup=keyboard)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
await conn.close()
|
await conn.close()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Ошибка при отправке уведомлений: {e}")
|
print(f"Ошибка при отправке уведомлений: {e}")
|
||||||
|
|
||||||
@router.message(Command(commands=['notify']))
|
|
||||||
async def notify_command(message: types.Message):
|
|
||||||
# Запрашиваем у администратора ID пользователя и текст уведомления
|
|
||||||
await message.answer("Введите ID пользователя и текст уведомления в формате:\n<code>/notify user_id текст</code>", parse_mode="HTML")
|
|
||||||
|
|
||||||
# Обработка команды уведомления
|
|
||||||
@router.message()
|
|
||||||
async def process_notification(message: types.Message):
|
|
||||||
if message.text.startswith("/notify"):
|
|
||||||
try:
|
|
||||||
# Парсим команду
|
|
||||||
command, user_id, *text = message.text.split()
|
|
||||||
text = ' '.join(text)
|
|
||||||
|
|
||||||
# Отправляем сообщение пользователю
|
|
||||||
await bot.send_message(chat_id=user_id, text=text)
|
|
||||||
await message.answer(f"Уведомление успешно отправлено пользователю {user_id}.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
await message.answer(f"Ошибка при отправке уведомления: {e}")
|
|
||||||
|
|||||||
+15
-27
@@ -6,7 +6,7 @@ from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
|||||||
|
|
||||||
from bot import bot
|
from bot import bot
|
||||||
from config import ADMIN_ID, DATABASE_URL
|
from config import ADMIN_ID, DATABASE_URL
|
||||||
from database import get_balance, get_key_count, update_balance
|
from database import get_balance, get_key_count, update_balance, add_connection, check_connection_exists
|
||||||
from handlers.profile import process_callback_view_profile
|
from handlers.profile import process_callback_view_profile
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
@@ -17,9 +17,6 @@ class ReplenishBalanceState(StatesGroup):
|
|||||||
waiting_for_admin_confirmation = State()
|
waiting_for_admin_confirmation = State()
|
||||||
|
|
||||||
async def send_message_with_deletion(chat_id, text, reply_markup=None, state=None, message_key='last_message_id'):
|
async def send_message_with_deletion(chat_id, text, reply_markup=None, state=None, message_key='last_message_id'):
|
||||||
"""
|
|
||||||
Отправляет новое сообщение и удаляет предыдущее сообщение (если оно существует), сохраненное в состоянии FSM.
|
|
||||||
"""
|
|
||||||
if state:
|
if state:
|
||||||
try:
|
try:
|
||||||
state_data = await state.get_data()
|
state_data = await state.get_data()
|
||||||
@@ -42,10 +39,20 @@ async def send_message_with_deletion(chat_id, text, reply_markup=None, state=Non
|
|||||||
async def process_callback_replenish_balance(callback_query: types.CallbackQuery, state: FSMContext):
|
async def process_callback_replenish_balance(callback_query: types.CallbackQuery, state: FSMContext):
|
||||||
tg_id = callback_query.from_user.id
|
tg_id = callback_query.from_user.id
|
||||||
|
|
||||||
|
# Проверяем, есть ли у пользователя ключи
|
||||||
|
key_count = await get_key_count(tg_id)
|
||||||
|
|
||||||
|
# Если ключей нет, проверяем, существует ли запись с таким tg_id
|
||||||
|
if key_count == 0:
|
||||||
|
exists = await check_connection_exists(tg_id)
|
||||||
|
# Если записи нет, создаем нового клиента в базе данных
|
||||||
|
if not exists:
|
||||||
|
await add_connection(tg_id, balance=0.0, trial=0)
|
||||||
|
|
||||||
await state.set_state(ReplenishBalanceState.choosing_transfer_method)
|
await state.set_state(ReplenishBalanceState.choosing_transfer_method)
|
||||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
[InlineKeyboardButton(text='По реквизитам', callback_data='transfer_method_requisites')],
|
[InlineKeyboardButton(text='По реквизитам', callback_data='transfer_method_requisites')],
|
||||||
[InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_profile')] # Кнопка "Назад"
|
[InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_profile')]
|
||||||
])
|
])
|
||||||
|
|
||||||
await callback_query.message.edit_text(
|
await callback_query.message.edit_text(
|
||||||
@@ -54,13 +61,10 @@ async def process_callback_replenish_balance(callback_query: types.CallbackQuery
|
|||||||
)
|
)
|
||||||
await callback_query.answer()
|
await callback_query.answer()
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(lambda c: c.data == 'back_to_profile')
|
@router.callback_query(lambda c: c.data == 'back_to_profile')
|
||||||
async def back_to_profile_handler(callback_query: types.CallbackQuery, state: FSMContext):
|
async def back_to_profile_handler(callback_query: types.CallbackQuery, state: FSMContext):
|
||||||
await process_callback_view_profile(callback_query, state)
|
await process_callback_view_profile(callback_query, state)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(lambda c: c.data.startswith('transfer_method_'))
|
@router.callback_query(lambda c: c.data.startswith('transfer_method_'))
|
||||||
async def process_transfer_method_selection(callback_query: types.CallbackQuery, state: FSMContext):
|
async def process_transfer_method_selection(callback_query: types.CallbackQuery, state: FSMContext):
|
||||||
data = callback_query.data.split('_', 2)
|
data = callback_query.data.split('_', 2)
|
||||||
@@ -76,7 +80,7 @@ async def process_transfer_method_selection(callback_query: types.CallbackQuery,
|
|||||||
[InlineKeyboardButton(text='100 RUB', callback_data='amount_100')],
|
[InlineKeyboardButton(text='100 RUB', callback_data='amount_100')],
|
||||||
[InlineKeyboardButton(text='300 RUB', callback_data='amount_300')],
|
[InlineKeyboardButton(text='300 RUB', callback_data='amount_300')],
|
||||||
[InlineKeyboardButton(text='500 RUB', callback_data='amount_500')],
|
[InlineKeyboardButton(text='500 RUB', callback_data='amount_500')],
|
||||||
[InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_profile')] # Кнопка "Назад"
|
[InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_profile')]
|
||||||
])
|
])
|
||||||
|
|
||||||
await callback_query.message.edit_text(
|
await callback_query.message.edit_text(
|
||||||
@@ -90,7 +94,6 @@ async def process_transfer_method_selection(callback_query: types.CallbackQuery,
|
|||||||
await send_message_with_deletion(callback_query.from_user.id, "Неверный метод перевода.", state=state, message_key='transfer_method_error_message_id')
|
await send_message_with_deletion(callback_query.from_user.id, "Неверный метод перевода.", state=state, message_key='transfer_method_error_message_id')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(lambda c: c.data.startswith('amount_'))
|
@router.callback_query(lambda c: c.data.startswith('amount_'))
|
||||||
async def process_amount_selection(callback_query: types.CallbackQuery, state: FSMContext):
|
async def process_amount_selection(callback_query: types.CallbackQuery, state: FSMContext):
|
||||||
data = callback_query.data.split('_', 1)
|
data = callback_query.data.split('_', 1)
|
||||||
@@ -102,18 +105,15 @@ async def process_amount_selection(callback_query: types.CallbackQuery, state: F
|
|||||||
amount_str = data[1]
|
amount_str = data[1]
|
||||||
amount = int(amount_str)
|
amount = int(amount_str)
|
||||||
|
|
||||||
# Получаем ID сообщения с кнопками выбора суммы
|
|
||||||
state_data = await state.get_data()
|
state_data = await state.get_data()
|
||||||
amount_selection_message_id = state_data.get('amount_selection_message_id')
|
amount_selection_message_id = state_data.get('amount_selection_message_id')
|
||||||
|
|
||||||
# Удаляем сообщение с кнопками выбора суммы
|
|
||||||
if amount_selection_message_id:
|
if amount_selection_message_id:
|
||||||
try:
|
try:
|
||||||
await bot.delete_message(chat_id=callback_query.from_user.id, message_id=amount_selection_message_id)
|
await bot.delete_message(chat_id=callback_query.from_user.id, message_id=amount_selection_message_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Ошибка при удалении сообщения: {e}")
|
print(f"Ошибка при удалении сообщения: {e}")
|
||||||
|
|
||||||
# Обновляем состояние и сохраняем выбранную сумму
|
|
||||||
await state.update_data(amount=amount)
|
await state.update_data(amount=amount)
|
||||||
await state.set_state(ReplenishBalanceState.waiting_for_admin_confirmation)
|
await state.set_state(ReplenishBalanceState.waiting_for_admin_confirmation)
|
||||||
|
|
||||||
@@ -126,10 +126,9 @@ async def process_amount_selection(callback_query: types.CallbackQuery, state: F
|
|||||||
"После перевода отправьте чек и дождитесь подтверждения."
|
"После перевода отправьте чек и дождитесь подтверждения."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Редактируем текущее сообщение
|
|
||||||
await callback_query.message.edit_text(
|
await callback_query.message.edit_text(
|
||||||
text=message,
|
text=message,
|
||||||
reply_markup=None # Или добавьте клавиатуру, если нужно
|
reply_markup=None
|
||||||
)
|
)
|
||||||
|
|
||||||
admin_keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
admin_keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
@@ -147,8 +146,6 @@ async def process_amount_selection(callback_query: types.CallbackQuery, state: F
|
|||||||
await send_message_with_deletion(ADMIN_ID, admin_message, reply_markup=admin_keyboard, state=state, message_key='admin_request_message_id')
|
await send_message_with_deletion(ADMIN_ID, admin_message, reply_markup=admin_keyboard, state=state, message_key='admin_request_message_id')
|
||||||
await callback_query.answer()
|
await callback_query.answer()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(lambda c: c.data.startswith('admin_'))
|
@router.callback_query(lambda c: c.data.startswith('admin_'))
|
||||||
async def process_admin_confirmation(callback_query: types.CallbackQuery, state: FSMContext):
|
async def process_admin_confirmation(callback_query: types.CallbackQuery, state: FSMContext):
|
||||||
if callback_query.from_user.id != ADMIN_ID:
|
if callback_query.from_user.id != ADMIN_ID:
|
||||||
@@ -169,35 +166,26 @@ async def process_admin_confirmation(callback_query: types.CallbackQuery, state:
|
|||||||
try:
|
try:
|
||||||
user_id = int(user_id_str)
|
user_id = int(user_id_str)
|
||||||
|
|
||||||
# Получаем данные из состояния для удаления сообщения с реквизитами
|
|
||||||
state_data = await state.get_data()
|
state_data = await state.get_data()
|
||||||
requisites_message_id = state_data.get('requisites_message_id')
|
requisites_message_id = state_data.get('requisites_message_id')
|
||||||
|
|
||||||
if action == 'confirm':
|
if action == 'confirm':
|
||||||
# Подключаемся к базе данных PostgreSQL
|
|
||||||
conn = await asyncpg.connect(DATABASE_URL)
|
conn = await asyncpg.connect(DATABASE_URL)
|
||||||
try:
|
try:
|
||||||
# Обновляем баланс пользователя
|
|
||||||
await update_balance(user_id, amount)
|
await update_balance(user_id, amount)
|
||||||
|
|
||||||
# Получаем обновленный баланс
|
|
||||||
balance = await get_balance(user_id)
|
balance = await get_balance(user_id)
|
||||||
|
|
||||||
# Создаем клавиатуру с кнопкой "Профиль"
|
|
||||||
profile_button = InlineKeyboardButton(text='Профиль', callback_data='view_profile')
|
profile_button = InlineKeyboardButton(text='Профиль', callback_data='view_profile')
|
||||||
profile_keyboard = InlineKeyboardMarkup(inline_keyboard=[[profile_button]])
|
profile_keyboard = InlineKeyboardMarkup(inline_keyboard=[[profile_button]])
|
||||||
|
|
||||||
# Отправляем уведомление с кнопкой "Профиль" администратору
|
|
||||||
await send_message_with_deletion(callback_query.from_user.id, f"Баланс пользователя успешно пополнен на {amount} RUB.\nТекущий баланс: {balance}", state=state, message_key='admin_confirm_message_id')
|
await send_message_with_deletion(callback_query.from_user.id, f"Баланс пользователя успешно пополнен на {amount} RUB.\nТекущий баланс: {balance}", state=state, message_key='admin_confirm_message_id')
|
||||||
|
|
||||||
# Отправляем уведомление пользователю с кнопкой "Профиль"
|
|
||||||
await bot.send_message(
|
await bot.send_message(
|
||||||
user_id,
|
user_id,
|
||||||
f"Ваш баланс был успешно пополнен на {amount} RUB.",
|
f"Ваш баланс был успешно пополнен на {amount} RUB.",
|
||||||
reply_markup=profile_keyboard
|
reply_markup=profile_keyboard
|
||||||
)
|
)
|
||||||
|
|
||||||
# Удаляем сообщение с реквизитами
|
|
||||||
requisites_message_id = state_data.get('requisites_message_id')
|
requisites_message_id = state_data.get('requisites_message_id')
|
||||||
if requisites_message_id:
|
if requisites_message_id:
|
||||||
try:
|
try:
|
||||||
@@ -218,4 +206,4 @@ async def process_admin_confirmation(callback_query: types.CallbackQuery, state:
|
|||||||
|
|
||||||
finally:
|
finally:
|
||||||
await state.clear()
|
await state.clear()
|
||||||
await callback_query.answer()
|
await callback_query.answer()
|
||||||
|
|||||||
+34
-21
@@ -9,7 +9,6 @@ from aiogram.fsm.context import FSMContext
|
|||||||
from aiogram.fsm.state import State, StatesGroup
|
from aiogram.fsm.state import State, StatesGroup
|
||||||
from aiogram.types import (CallbackQuery, InlineKeyboardButton,
|
from aiogram.types import (CallbackQuery, InlineKeyboardButton,
|
||||||
InlineKeyboardMarkup, Message)
|
InlineKeyboardMarkup, Message)
|
||||||
from pytz import timezone
|
|
||||||
|
|
||||||
from auth import link, login_with_credentials
|
from auth import link, login_with_credentials
|
||||||
from bot import bot, dp
|
from bot import bot, dp
|
||||||
@@ -20,6 +19,7 @@ from database import (add_connection, get_balance, has_active_key, store_key,
|
|||||||
update_balance)
|
update_balance)
|
||||||
from handlers.profile import process_callback_view_profile
|
from handlers.profile import process_callback_view_profile
|
||||||
from handlers.start import start_command
|
from handlers.start import start_command
|
||||||
|
from handlers.notifications import send_notification
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|
||||||
@@ -46,16 +46,22 @@ async def process_callback_create_key(callback_query: CallbackQuery, state: FSMC
|
|||||||
|
|
||||||
if trial_status == 1:
|
if trial_status == 1:
|
||||||
await callback_query.message.edit_text(
|
await callback_query.message.edit_text(
|
||||||
"У вас уже был пробный ключ. Новый стоит 100 рублей и сразу на месяц. \n\n"
|
"<b>⚠️ У вас уже был пробный ключ.</b>\n\n"
|
||||||
"Хотите продолжить?",
|
"Новый ключ будет выдан на <b>один месяц</b> и стоит <b>100 рублей</b>.\n\n"
|
||||||
|
"<i>Хотите продолжить?</i>",
|
||||||
|
parse_mode="HTML", # Добавляем параметр parse_mode
|
||||||
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
|
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
|
||||||
[InlineKeyboardButton(text='Да, создать новый ключ', callback_data='confirm_create_new_key')],
|
[InlineKeyboardButton(text='✅ Да, создать новый ключ', callback_data='confirm_create_new_key')],
|
||||||
[InlineKeyboardButton(text='Назад', callback_data='cancel_create_key')]
|
[InlineKeyboardButton(text='↩️ Назад', callback_data='cancel_create_key')]
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
await state.update_data(creating_new_key=True)
|
await state.update_data(creating_new_key=True)
|
||||||
else:
|
else:
|
||||||
await callback_query.message.edit_text("Вам будет выдан пробный ключ. Пожалуйста, выберите имя для вашего ключа:")
|
await callback_query.message.edit_text(
|
||||||
|
"<b>🎉 Вам будет выдан пробный ключ на 24 часа!</b>\n\n"
|
||||||
|
"<i>Пожалуйста, введите название для вашего пробного ключа:</i>",
|
||||||
|
parse_mode="HTML"
|
||||||
|
)
|
||||||
await state.set_state(Form.waiting_for_key_name)
|
await state.set_state(Form.waiting_for_key_name)
|
||||||
|
|
||||||
await callback_query.answer()
|
await callback_query.answer()
|
||||||
@@ -77,7 +83,7 @@ async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContex
|
|||||||
await state.clear()
|
await state.clear()
|
||||||
return
|
return
|
||||||
|
|
||||||
await callback_query.message.edit_text("🔑 Пожалуйста, выберите имя для вашего нового ключа:")
|
await callback_query.message.edit_text("🔑 Пожалуйста, введите имя нового ключа:")
|
||||||
await state.set_state(Form.waiting_for_key_name)
|
await state.set_state(Form.waiting_for_key_name)
|
||||||
await state.update_data(creating_new_key=True)
|
await state.update_data(creating_new_key=True)
|
||||||
|
|
||||||
@@ -113,7 +119,6 @@ async def handle_text(message: Message, state: FSMContext):
|
|||||||
|
|
||||||
async def handle_key_name_input(message: Message, state: FSMContext):
|
async def handle_key_name_input(message: Message, state: FSMContext):
|
||||||
tg_id = message.from_user.id
|
tg_id = message.from_user.id
|
||||||
|
|
||||||
key_name = sanitize_key_name(message.text)
|
key_name = sanitize_key_name(message.text)
|
||||||
|
|
||||||
if not key_name:
|
if not key_name:
|
||||||
@@ -140,7 +145,7 @@ async def handle_key_name_input(message: Message, state: FSMContext):
|
|||||||
|
|
||||||
if trial_status == 0:
|
if trial_status == 0:
|
||||||
# Создаем пробный ключ на 1 день
|
# Создаем пробный ключ на 1 день
|
||||||
expiry_time = int((current_time + timedelta(days=1, hours=3)).timestamp() * 1000)
|
expiry_time = current_time + timedelta(days=1, hours=3)
|
||||||
else:
|
else:
|
||||||
# Проверяем баланс перед созданием нового ключа
|
# Проверяем баланс перед созданием нового ключа
|
||||||
balance = await get_balance(tg_id)
|
balance = await get_balance(tg_id)
|
||||||
@@ -152,26 +157,25 @@ async def handle_key_name_input(message: Message, state: FSMContext):
|
|||||||
return
|
return
|
||||||
|
|
||||||
await update_balance(tg_id, -100)
|
await update_balance(tg_id, -100)
|
||||||
expiry_time = int((current_time + timedelta(days=30, hours=3)).timestamp() * 1000)
|
expiry_time = current_time + timedelta(days=30, hours=3)
|
||||||
|
|
||||||
|
expiry_timestamp = int(expiry_time.timestamp() * 1000)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Попробуем добавить клиента
|
# Попробуем добавить клиента
|
||||||
response = add_client(session, client_id, email, tg_id, limit_ip=1, total_gb=0, expiry_time=expiry_time, enable=True, flow="xtls-rprx-vision")
|
response = add_client(session, client_id, email, tg_id, limit_ip=1, total_gb=0, expiry_time=expiry_timestamp, enable=True, flow="xtls-rprx-vision")
|
||||||
|
|
||||||
# Проверяем статус ответа от сервера
|
|
||||||
if not response.get("success", True):
|
if not response.get("success", True):
|
||||||
error_msg = response.get("msg", "Неизвестная ошибка.")
|
error_msg = response.get("msg", "Неизвестная ошибка.")
|
||||||
if "Duplicate email" in error_msg:
|
if "Duplicate email" in error_msg:
|
||||||
await message.bot.send_message(tg_id, "❌ Этот email уже используется. Пожалуйста, выберите другое имя для ключа.")
|
await message.bot.send_message(tg_id, "❌ Этот email уже используется. Пожалуйста, выберите другое имя для ключа.")
|
||||||
await state.set_state(Form.waiting_for_key_name) # Возвращаем пользователя к вводу имени ключа
|
await state.set_state(Form.waiting_for_key_name)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
raise Exception(error_msg)
|
raise Exception(error_msg)
|
||||||
|
|
||||||
# Если добавление клиента прошло успешно, получаем ссылку
|
|
||||||
connection_link = link(session, client_id, email)
|
connection_link = link(session, client_id, email)
|
||||||
|
|
||||||
# Проверка существующей записи
|
|
||||||
conn = await asyncpg.connect(DATABASE_URL)
|
conn = await asyncpg.connect(DATABASE_URL)
|
||||||
try:
|
try:
|
||||||
existing_connection = await conn.fetchrow('SELECT * FROM connections WHERE tg_id = $1', tg_id)
|
existing_connection = await conn.fetchrow('SELECT * FROM connections WHERE tg_id = $1', tg_id)
|
||||||
@@ -180,11 +184,21 @@ async def handle_key_name_input(message: Message, state: FSMContext):
|
|||||||
await conn.execute('UPDATE connections SET trial = 1 WHERE tg_id = $1', tg_id)
|
await conn.execute('UPDATE connections SET trial = 1 WHERE tg_id = $1', tg_id)
|
||||||
else:
|
else:
|
||||||
await add_connection(tg_id, 0, 1)
|
await add_connection(tg_id, 0, 1)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
await conn.close()
|
await conn.close()
|
||||||
|
|
||||||
await store_key(tg_id, client_id, email, expiry_time, connection_link)
|
await store_key(tg_id, client_id, email, expiry_timestamp, connection_link)
|
||||||
|
|
||||||
|
# Рассчитываем оставшееся время до окончания действия ключа
|
||||||
|
remaining_time = expiry_time - current_time
|
||||||
|
days = remaining_time.days
|
||||||
|
hours, remainder = divmod(remaining_time.seconds, 3600)
|
||||||
|
minutes, _ = divmod(remainder, 60)
|
||||||
|
|
||||||
|
# Формируем сообщение с информацией о ключе
|
||||||
|
remaining_time_message = (
|
||||||
|
f"Оставшееся время ключа: {days} день"
|
||||||
|
)
|
||||||
|
|
||||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
[InlineKeyboardButton(text='📖 Инструкции по использованию', callback_data='instructions')],
|
[InlineKeyboardButton(text='📖 Инструкции по использованию', callback_data='instructions')],
|
||||||
@@ -193,8 +207,10 @@ async def handle_key_name_input(message: Message, state: FSMContext):
|
|||||||
|
|
||||||
key_message = (
|
key_message = (
|
||||||
"✅ Ключ успешно создан:\n"
|
"✅ Ключ успешно создан:\n"
|
||||||
f"<pre>{connection_link}</pre>"
|
f"<pre>{connection_link}</pre>\n\n"
|
||||||
|
f"{remaining_time_message}"
|
||||||
)
|
)
|
||||||
|
|
||||||
await message.bot.send_message(tg_id, key_message, parse_mode="HTML", reply_markup=keyboard)
|
await message.bot.send_message(tg_id, key_message, parse_mode="HTML", reply_markup=keyboard)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -202,9 +218,6 @@ async def handle_key_name_input(message: Message, state: FSMContext):
|
|||||||
|
|
||||||
await state.clear()
|
await state.clear()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@dp.callback_query(F.data == 'instructions')
|
@dp.callback_query(F.data == 'instructions')
|
||||||
async def handle_instructions(callback_query: CallbackQuery):
|
async def handle_instructions(callback_query: CallbackQuery):
|
||||||
instructions_message = (
|
instructions_message = (
|
||||||
|
|||||||
Reference in New Issue
Block a user