From 51b46bfe656473a9f24de1377c8148fd8ee64b60 Mon Sep 17 00:00:00 2001 From: Vlad Date: Sat, 21 Sep 2024 00:21:23 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D1=85=D0=BE=D0=B4=20?= =?UTF-8?q?=D0=BD=D0=B0=20postresql?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.py | 6 - database.py | 130 ++++----- handlers/__pycache__/keys.cpython-310.pyc | Bin 7777 -> 7272 bytes handlers/__pycache__/pay.cpython-310.pyc | Bin 8705 -> 8680 bytes handlers/__pycache__/profile.cpython-310.pyc | Bin 2420 -> 2355 bytes handlers/keys.py | 276 ++++++++++--------- handlers/pay.py | 55 ++-- handlers/profile.py | 2 - key_management.py | 48 ++-- 9 files changed, 266 insertions(+), 251 deletions(-) diff --git a/client.py b/client.py index a6ae8247..8522dc46 100644 --- a/client.py +++ b/client.py @@ -1,10 +1,4 @@ -import requests import json -from config import ADMIN_USERNAME, ADMIN_PASSWORD, GET_INBOUNDS_URL, DATABASE_PATH -import uuid -from auth import login_with_credentials -from datetime import datetime, timedelta -import aiosqlite def add_client(session, client_id: str, email: str, tg_id: str, limit_ip: int, total_gb: int, expiry_time: int, enable: bool, flow: str): url = 'https://solonet.pocomacho.ru:62553/solonet/panel/api/inbounds/addClient' diff --git a/database.py b/database.py index 4497275a..983762fe 100644 --- a/database.py +++ b/database.py @@ -1,84 +1,84 @@ -import aiosqlite +import asyncpg from datetime import datetime -from config import DATABASE_PATH +from config import DATABASE_URL # Example: "postgresql://user:password@localhost:5432/dbname" async def init_db(): - async with aiosqlite.connect(DATABASE_PATH) as db: - await db.execute(''' - CREATE TABLE IF NOT EXISTS connections ( - tg_id INTEGER PRIMARY KEY NOT NULL, - balance REAL NOT NULL DEFAULT 0.0, - trial INTEGER NOT NULL DEFAULT 0 - ) - ''') - await db.execute(''' - CREATE TABLE IF NOT EXISTS keys ( - tg_id INTEGER NOT NULL, - client_id TEXT NOT NULL, - email TEXT NOT NULL, - created_at INTEGER NOT NULL, - expiry_time INTEGER NOT NULL, - key TEXT NOT NULL, - PRIMARY KEY (tg_id, client_id) - ) - ''') - await db.commit() + conn = await asyncpg.connect(DATABASE_URL) + await conn.execute(''' + CREATE TABLE IF NOT EXISTS connections ( + tg_id BIGINT PRIMARY KEY NOT NULL, + balance REAL NOT NULL DEFAULT 0.0, + trial INTEGER NOT NULL DEFAULT 0 + ) + ''') + await conn.execute(''' + CREATE TABLE IF NOT EXISTS keys ( + tg_id BIGINT NOT NULL, + client_id TEXT NOT NULL, + email TEXT NOT NULL, + created_at BIGINT NOT NULL, + expiry_time BIGINT NOT NULL, + key TEXT NOT NULL, + PRIMARY KEY (tg_id, client_id) + ) + ''') + await conn.close() async def add_connection(tg_id: int, balance: float = 0.0, trial: int = 0): - async with aiosqlite.connect(DATABASE_PATH) as db: - await db.execute(''' - INSERT INTO connections (tg_id, balance, trial) - VALUES (?, ?, ?) - ''', (tg_id, balance, trial)) - await db.commit() + conn = await asyncpg.connect(DATABASE_URL) + await conn.execute(''' + INSERT INTO connections (tg_id, balance, trial) + VALUES ($1, $2, $3) + ''', tg_id, balance, trial) + await conn.close() async def store_key(tg_id: int, client_id: str, email: str, expiry_time: int, key: str): - async with aiosqlite.connect(DATABASE_PATH) as db: - await db.execute(''' - INSERT INTO keys (tg_id, client_id, email, created_at, expiry_time, key) - VALUES (?, ?, ?, ?, ?, ?) - ''', (tg_id, client_id, email, int(datetime.utcnow().timestamp() * 1000), expiry_time, key)) - await db.commit() - + conn = await asyncpg.connect(DATABASE_URL) + await conn.execute(''' + INSERT INTO keys (tg_id, client_id, email, created_at, expiry_time, key) + VALUES ($1, $2, $3, $4, $5, $6) + ''', tg_id, client_id, email, int(datetime.utcnow().timestamp() * 1000), expiry_time, key) + await conn.close() async def get_keys(tg_id: int): - async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute(''' - SELECT client_id, email, created_at, key - FROM keys - WHERE tg_id = ? - ''', (tg_id,)) as cursor: - return await cursor.fetchall() + conn = await asyncpg.connect(DATABASE_URL) + records = await conn.fetch(''' + SELECT client_id, email, created_at, key + FROM keys + WHERE tg_id = $1 + ''', tg_id) + await conn.close() + return records async def has_active_key(tg_id: int) -> bool: - async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute("SELECT COUNT(*) FROM keys WHERE tg_id = ?", (tg_id,)) as cursor: - count = await cursor.fetchone() - return count[0] > 0 + conn = await asyncpg.connect(DATABASE_URL) + count = await conn.fetchval("SELECT COUNT(*) FROM keys WHERE tg_id = $1", tg_id) + await conn.close() + return count > 0 async def get_balance(tg_id: int) -> float: - async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute("SELECT balance FROM connections WHERE tg_id = ?", (tg_id,)) as cursor: - record = await cursor.fetchone() - return record[0] if record else 0.0 + conn = await asyncpg.connect(DATABASE_URL) + balance = await conn.fetchval("SELECT balance FROM connections WHERE tg_id = $1", tg_id) + await conn.close() + return balance if balance is not None else 0.0 async def update_balance(tg_id: int, amount: float): - async with aiosqlite.connect(DATABASE_PATH) as db: - await db.execute(''' - UPDATE connections - SET balance = balance + ? - WHERE tg_id = ? - ''', (amount, tg_id)) - await db.commit() + conn = await asyncpg.connect(DATABASE_URL) + await conn.execute(''' + UPDATE connections + SET balance = balance + $1 + WHERE tg_id = $2 + ''', amount, tg_id) + await conn.close() async def get_trial(tg_id: int) -> int: - async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute("SELECT trial FROM connections WHERE tg_id = ?", (tg_id,)) as cursor: - record = await cursor.fetchone() - return record[0] if record else 0 + conn = await asyncpg.connect(DATABASE_URL) + trial = await conn.fetchval("SELECT trial FROM connections WHERE tg_id = $1", tg_id) + await conn.close() + return trial if trial is not None else 0 async def get_key_count(tg_id: int) -> int: - async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute('SELECT COUNT(*) FROM keys WHERE tg_id = ?', (tg_id,)) as cursor: - count = await cursor.fetchone() - return count[0] if count else 0 + conn = await asyncpg.connect(DATABASE_URL) + count = await conn.fetchval('SELECT COUNT(*) FROM keys WHERE tg_id = $1', tg_id) + await conn.close() + return count if count is not None else 0 diff --git a/handlers/__pycache__/keys.cpython-310.pyc b/handlers/__pycache__/keys.cpython-310.pyc index 458ddd96f3d32c6a69673f65cbff9c395013d75d..5b23c2fdb32b603107ef2bacfa09ff55692bc3ab 100644 GIT binary patch delta 3616 zcmZveU2q#$6@c&EU9DED)oLZJWl6SUCvj3#LK3H?*saoW=tHMwC>I7wni;tRJn_IxAK{7e!VJ?l7)H!6!%Jx%VCe9`6b8yUSJt12 zX7=o!d+r|Hz4tpme?I;3adR~i(Fy$i`O#l*UODu(c`%+Mj4)}Lu1Gm)Mb62TynXg$ zo#JS4IkIBr%#~;^x?<%lF{UlYR^quhB?WntCT@GveZ2#mtkp^xkqwCjLwnl z&_n#jnXJ?dxEpJQYEzwEz3mnpevMvui8f<}J8q%E@{7x*LdDH56*jVR)0~{TbYUic zZEkvYX7bW>)0mpPI{ED6+;slh?8UbA%H-VK{N>rHENv#2SBs@ee!b-0$}c(v2A)a_ z%T*qUrnxN@XoT0KL%bM`)Ssh-yiPq*CS~f=+8tMF$gZ+YFTft3C%#lGcv2(4s7JS@ zoiW*yor^`{E1o<>ZY2GH7hqDF_^PXxgPzJ{PrXkbNKDz1{Gg{aG%r}B+mw=5_rQ#h zr?9{l^);sYA-ArV!(Pb41sLUsr?KFc()t@pDx*w;Dc}?jQMl8E1HDL#R5%H{{qKi+ z-=8P2baR_@{AvCAI+-V9l|2e^qZ{`r@eN99ao57du&`&~H*Cs$Jhs=4b)7H*M`+B{ zrumHWRC0VmAM5!w1BI1^((?O1P@bV9!G-EZWpS<83@)x#DuqSYG2q;rftv+)@fN4* z_3%ESFD|cE3;bnuUn1h9#fV$XmsnH5*Z6()3v`VCMt%821Z>sgAf&sRBuN&Ps7fVC z|F5Dbl14RZQ9})bv~Y;8^{KuF?p&YKH{5_AO6t1mSe4CvD3+EGVAhya6Yw!1)L8WCaIWBLp0% zLlAd<(A66HHt~T1PfL+<*bjLj7F<%CX{LE0+~o>oAzx>@7r2i*A8Z)RsBcqHBAyNt zR6p!S$|f@zgeO2{2rb6qLW>A3!EB+KphZ2SNWBPLCXgbt0l+QlTIHA*7No_?2`|c$ zY;Y^kVZ%f_OSSE+o@D7Rihz>bu)7pgw0<2?5+n_5{2EAtYy>1F1xXptYLV3bt9kMw z#FlTpW>o^M`#N1G9%&4A{WB89Ofl1>`GWUKm7I;dTUsgbpXeXbVeW=&sZsFNRL)<$bTO+qdqHuKL1#Z#jpxm* z+%t5E=ZzN*A3*Ct6mbyEKy_`oJ$hBJr0PsP*(!_zm=dy`Dt^XiCm9U?XL7$ zTz0N=J$is%;76l->m_uR0@24euCv4UTm32To&QUDZ#**Pz2(Uw%|Y5@vcO#y*hAWD z1h+{;+a@efw+ZAt)eA0B=PV?(P(x>(7@|N4xDI#Ec^*iZBQB&qxJ(vg8VhYnMJhC% zg?lupTP+QMHZgy?QIBr6^K^(sF_u`}g4I|Md;tu;MvTQo9K|05eBF(Er2PUVZlY`h zpgK!H*0uXk?^`0E-lzAV{!2hTjHvH?=zteKg{-%827a$#J^Wf&unzYf)*&Ob<3VK9 zkG%4nznin;w3V$jYEgkVwd2Ys{AYHi zAL=6igZ42OjPL;d~uM56P&WL-7)bEOlnNn>ue{C^`!$EEJ>sx2dnG`{A+nMgDkt z&mh#P`Sa7W(_@`z7PadA8)=E2>jos&6=w!SQ!Z6@mzG`e>AZrIdn5CwnL{T`91Nqt z4#~l|a5NCjVBt<-5j$%IhZqVGy7o@!a(yWC)FWdTrxwsfv{G2l_mom>%Brq&ljnz$ zG{?U&bfu0FTFrsL5VxqYQ?DKo<_9u=vY;{3piMKOSxZV1wFmUSS+W9E&W3bg!j+=7 z6c>$~)h%!v;Jz;b;9=oceL(o-;ZyOw@F*&IR&8z1KK|}-T5atz|HW`-a1mzw9<-vi}xS#Emgc_(j*lHJv%IV0RHNT&EFh( za-^w3uX?joY#jp@TX7amW#P7ai~nxqv4QJ2Rz!i{;)ws7;ITcAAJhnLngttRh)b0W SIZlm3a28 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 delta 864 zcmZvaO-vI(6vyZ7lz!|NrL^0HZlR?h_sg`yNS%9H06+(JlWn8XbXjmn6$Qgf#DnKs;fkWsWQtQT1@uj@7yG80>DORr z2rBO5Q;KfWUGWgU3Pq3U$?qrt%PF<=E%o!tY_bw%PU$ uV4F@xVi2QNB-M~#U`^?6WSCxx&IkHf;zeE%S#LMnein)JQ&hu)3j76>Ce~H} diff --git a/handlers/__pycache__/profile.cpython-310.pyc b/handlers/__pycache__/profile.cpython-310.pyc index a59a06fb99098030a88b72ee01aff68155fbb456..a86006737c40724ac834555bf52e612ac99e00d0 100644 GIT binary patch delta 465 zcmZ8cF-yZh6u!I0OH7kCO;Zs;2SKoih;&e}c5x6o6pFe8p`_jwtErWjR1pV-W^ob9 z`2%!w?|*UV;4Y|>Kft?osUFgQiulDVAuBdWseV z(1-NILFv4p`ZCUlqJD%+;!dxwOx0w=$S8+Fr^$lKj#LqmU?SKO-}-K~Bt?hhq3ptE!G}%q=Jp$X3)QSZW8|_}A`a>zP@9h^bbB!e zPDXfhZa;wYf(gO#dvToWs{kd=&sw&CI?Rn z$;D%sKhXY$oC>{p=`ZMEPu@zwqlX?llU(A!d-HvK?|ttLGY%dCno{Z_ea1iTJ8VRs z!Ozj!(G~MJ;&x1vGAjep*oo&8pZQ6JRR9S*KrGlre2%+A#9iSI3arXY0?tD23Ak!K~rhHL-aJpA>Q8&%L#@(LCNjtshbU#b;1G&SaUet;*Axp;3 zET0pZ_IpA~^VFQai{iKywa<_GLS9O?Bme#)|cTwlPzvj diff --git a/handlers/keys.py b/handlers/keys.py index eddccbcc..f03a708e 100644 --- a/handlers/keys.py +++ b/handlers/keys.py @@ -1,10 +1,11 @@ +import asyncpg from aiogram import types, Router -import aiosqlite from bot import bot from datetime import datetime, timedelta from database import get_balance, update_balance, get_trial -from client import extend_client_key, login_with_credentials -from config import ADMIN_USERNAME, DATABASE_PATH, ADMIN_PASSWORD +from client import extend_client_key +from config import ADMIN_USERNAME, DATABASE_URL, ADMIN_PASSWORD +from auth import login_with_credentials router = Router() @@ -14,35 +15,38 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id try: - async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute(''' - SELECT email FROM keys WHERE tg_id = ? - ''', (tg_id,)) as cursor: - records = await cursor.fetchall() + conn = await asyncpg.connect(DATABASE_URL) + try: + records = await conn.fetch(''' + SELECT email FROM keys WHERE tg_id = $1 + ''', tg_id) - if records: - # Создаем кнопки для каждого ключа - buttons = [] - for record in records: - key_name = record[0] # Предполагается, что email - это название ключа - button = types.InlineKeyboardButton(text=key_name, callback_data=f'view_key_{key_name}') - buttons.append([button]) + if records: + # Создаем кнопки для каждого ключа + buttons = [] + for record in records: + key_name = record['email'] # Предполагается, что email - это название ключа + button = types.InlineKeyboardButton(text=key_name, callback_data=f'view_key_{key_name}') + buttons.append([button]) - # Создаем клавиатуру с кнопками - inline_keyboard = types.InlineKeyboardMarkup(inline_keyboard=buttons) - response_message = "Выберите ключ для просмотра информации:" - - # Редактируем сообщение с клавиатурой - await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=inline_keyboard) - else: - # Если нет ключей, добавляем кнопку "Создать ключ" - response_message = "У вас нет ключей." - - # Кнопка "Создать ключ" - create_key_button = types.InlineKeyboardButton(text='Создать ключ', callback_data='create_key') - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[create_key_button]]) - - await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard) + # Создаем клавиатуру с кнопками + inline_keyboard = types.InlineKeyboardMarkup(inline_keyboard=buttons) + response_message = "Выберите ключ для просмотра информации:" + + # Редактируем сообщение с клавиатурой + await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=inline_keyboard) + else: + # Если нет ключей, добавляем кнопку "Создать ключ" + response_message = "У вас нет ключей." + + # Кнопка "Создать ключ" + create_key_button = types.InlineKeyboardButton(text='Создать ключ', callback_data='create_key') + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[create_key_button]]) + + await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard) + + finally: + await conn.close() except Exception as e: await handle_error(tg_id, callback_query, f"Ошибка при получении ключей: {e}") @@ -57,146 +61,152 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): key_name = callback_query.data.split('_', 2)[2] # Получаем имя ключа try: - async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute(''' + conn = await asyncpg.connect(DATABASE_URL) + try: + record = await conn.fetchrow(''' SELECT k.key, k.expiry_time FROM keys k JOIN connections c ON k.tg_id = c.tg_id - WHERE k.tg_id = ? AND k.email = ? - ''', (tg_id, key_name)) as cursor: - record = await cursor.fetchone() + WHERE k.tg_id = $1 AND k.email = $2 + ''', tg_id, key_name) - if record: - key, expiry_time = record - expiry_date = datetime.utcfromtimestamp(expiry_time / 1000) - current_date = datetime.utcnow() - days_left = (expiry_date - current_date).days + if record: + key = record['key'] + expiry_time = record['expiry_time'] + expiry_date = datetime.utcfromtimestamp(expiry_time / 1000) + current_date = datetime.utcnow() + days_left = (expiry_date - current_date).days - days_left_message = f"Осталось дней: {days_left}" if days_left > 0 else "Ключ истек." - response_message = (f"Ваш ключ:\n
{key}
\n" - f"Дата окончания: {expiry_date.strftime('%Y-%m-%d %H:%M:%S')}\n" - f"{days_left_message}") + days_left_message = f"Осталось дней: {days_left}" if days_left > 0 else "Ключ истек." + response_message = (f"Ваш ключ:\n
{key}
\n" + f"Дата окончания: {expiry_date.strftime('%Y-%m-%d %H:%M:%S')}\n" + f"{days_left_message}") - # Кнопки для продления и инструкций - renew_button = types.InlineKeyboardButton(text='Продлить ключ', callback_data='renew_key') - instructions_button = types.InlineKeyboardButton(text='Инструкции по использованию', callback_data='instructions') - back_button = types.InlineKeyboardButton(text='Назад в профиль', callback_data='view_profile') - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[renew_button], [instructions_button], [back_button]]) + # Кнопки для продления и инструкций + renew_button = types.InlineKeyboardButton(text='Продлить ключ', callback_data='renew_key') + instructions_button = types.InlineKeyboardButton(text='Инструкции по использованию', callback_data='instructions') + back_button = types.InlineKeyboardButton(text='Назад в профиль', callback_data='view_profile') + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[renew_button], [instructions_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") - 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(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard, parse_mode="HTML") + else: + await bot.edit_message_text("Информация о ключе не найдена.", chat_id=tg_id, message_id=callback_query.message.message_id, parse_mode="HTML") + + finally: + await conn.close() except Exception as e: await handle_error(tg_id, callback_query, f"Ошибка при получении информации о ключе: {e}") 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, parse_mode="HTML") - # Обработка запроса на продление ключа @router.callback_query(lambda c: c.data == 'renew_key') async def process_callback_renew_key(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id - + try: - async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute('SELECT client_id, email, expiry_time FROM keys WHERE tg_id = ?', (tg_id,)) as cursor: - record = await cursor.fetchone() + conn = await asyncpg.connect(DATABASE_URL) + try: + record = await conn.fetchrow('SELECT client_id, email, expiry_time FROM keys WHERE tg_id = $1', tg_id) - if record: - client_id, email, expiry_time = record - current_time = datetime.utcnow().timestamp() * 1000 - - if expiry_time <= current_time: - await callback_query.message.answer("Ваш ключ уже истек и не может быть продлен.") - return - - # Создаем клавиатуру для выбора плана продления - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[ - [types.InlineKeyboardButton(text='Продлить на 1 месяц (100 руб.)', callback_data='renew_1_month')], - [types.InlineKeyboardButton(text='Продлить на 3 месяца (250 руб.)', callback_data='renew_3_months')], - [types.InlineKeyboardButton(text='Назад', callback_data='view_profile')] - ]) + if record: + client_id = record['client_id'] + email = record['email'] + expiry_time = record['expiry_time'] + current_time = datetime.utcnow().timestamp() * 1000 - balance = await get_balance(tg_id) - response_message = (f"Выберите план продления:\n" - f"Баланс: {balance} руб.\n" - f"Действующий ключ истекает {datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S')}") + if expiry_time <= current_time: + await callback_query.message.answer("Ваш ключ уже истек и не может быть продлен.") + return - await delete_previous_message(callback_query) - await bot.send_message(tg_id, response_message, parse_mode="HTML", reply_markup=keyboard) + # Создаем клавиатуру для выбора плана продления + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[ + [types.InlineKeyboardButton(text='Продлить на 1 месяц (100 руб.)', callback_data='renew_1_month')], + [types.InlineKeyboardButton(text='Продлить на 3 месяца (250 руб.)', callback_data='renew_3_months')], + [types.InlineKeyboardButton(text='Назад', callback_data='view_profile')] + ]) + + balance = await get_balance(tg_id) + response_message = (f"Выберите план продления:\n" + f"Баланс: {balance} руб.\n" + f"Действующий ключ истекает {datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S')}") + + await delete_previous_message(callback_query) + await bot.send_message(tg_id, response_message, parse_mode="HTML", reply_markup=keyboard) + + finally: + await conn.close() except Exception as e: await callback_query.message.answer(f"Ошибка при выборе плана: {e}") await callback_query.answer() + # Обработка выбора плана продления @router.callback_query(lambda c: c.data.startswith('renew_')) async def process_callback_renew_plan(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id plan = callback_query.data.split('_')[1] # '1' или '3' days_to_extend = 30 * int(plan) # 30 дней или 90 дней - + try: - async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute('SELECT client_id, email, expiry_time FROM keys WHERE tg_id = ?', (tg_id,)) as cursor: - record = await cursor.fetchone() - - if record: - client_id = record[0] - email = record[1] - expiry_time = record[2] - current_time = datetime.utcnow().timestamp() * 1000 - - if expiry_time <= current_time: - await callback_query.message.answer("Ваш ключ уже истек и не может быть продлен.") - return - - # Рассчитываем новый срок окончания, добавляя дни в зависимости от выбранного плана - new_expiry_time = int(expiry_time + timedelta(days=days_to_extend).total_seconds() * 1000) - - # Определяем стоимость продления - cost = 100 if plan == '1' else 250 - - # Проверка баланса - balance = await get_balance(tg_id) - if balance < cost: - replenish_button = types.InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance') - back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_keys') - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[replenish_button], [back_button]]) - - await callback_query.message.answer("Недостаточно средств для продления ключа.", reply_markup=keyboard) - return - - # Создаем сессию для API-запросов - session = login_with_credentials(ADMIN_USERNAME, ADMIN_PASSWORD) - - # Обновляем ключ через API - success = extend_client_key(session, tg_id, client_id, email, new_expiry_time) - - if success: - await update_balance(tg_id, -cost) # Списание средств с баланса - await db.execute('UPDATE keys SET expiry_time = ? WHERE client_id = ?', (new_expiry_time, client_id)) - await db.commit() - response_message = f"Ваш ключ был успешно продлен на {days_to_extend // 30} месяц(-)." - back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_keys') - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) - await bot.send_message(tg_id, response_message, reply_markup=keyboard) - else: - await bot.send_message(tg_id, "Ошибка при продлении ключа.") + conn = await asyncpg.connect(DATABASE_URL) + try: + record = await conn.fetchrow('SELECT client_id, email, expiry_time FROM keys WHERE tg_id = $1', tg_id) + + if record: + client_id = record['client_id'] + email = record['email'] + expiry_time = record['expiry_time'] + current_time = datetime.utcnow().timestamp() * 1000 + + if expiry_time <= current_time: + await callback_query.message.answer("Ваш ключ уже истек и не может быть продлен.") + return + + # Рассчитываем новый срок окончания + new_expiry_time = int(expiry_time + timedelta(days=days_to_extend).total_seconds() * 1000) + + # Определяем стоимость продления + cost = 100 if plan == '1' else 250 + + # Проверка баланса + balance = await get_balance(tg_id) + if balance < cost: + replenish_button = types.InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance') + back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_keys') + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[replenish_button], [back_button]]) + + await callback_query.message.answer("Недостаточно средств для продления ключа.", reply_markup=keyboard) + return + + # Создаем сессию для API-запросов + session = login_with_credentials(ADMIN_USERNAME, ADMIN_PASSWORD) + + # Обновляем ключ через API + success = extend_client_key(session, tg_id, client_id, email, new_expiry_time) + + 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} месяц(-)." + back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_keys') + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) + await bot.send_message(tg_id, response_message, reply_markup=keyboard) else: - await bot.send_message(tg_id, "У вас нет ключей для продления.") - + await bot.send_message(tg_id, "Ошибка при продлении ключа.") + else: + await bot.send_message(tg_id, "У вас нет ключей для продления.") + + finally: + await conn.close() + except Exception as e: await bot.send_message(tg_id, f"Ошибка при продлении ключа: {e}") - + await callback_query.answer() @@ -208,4 +218,4 @@ async def delete_previous_message(callback_query: types.CallbackQuery): # Обработка ошибок async def handle_error(tg_id, callback_query, message): await delete_previous_message(callback_query) - await bot.send_message(tg_id, message) \ No newline at end of file + await bot.send_message(tg_id, message) diff --git a/handlers/pay.py b/handlers/pay.py index 2ad6a0e9..5d7be13c 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -3,10 +3,11 @@ from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup import aiosqlite -from config import DATABASE_PATH, ADMIN_ID +from config import ADMIN_ID, DATABASE_URL from database import get_balance, update_balance, get_key_count from bot import bot from handlers.profile import process_callback_view_profile +import asyncpg router = Router() @@ -183,36 +184,41 @@ async def process_admin_confirmation(callback_query: types.CallbackQuery, state: state_data = await state.get_data() requisites_message_id = state_data.get('requisites_message_id') - if action == 'confirm': - async with aiosqlite.connect(DATABASE_PATH) as db: + # Подключаемся к базе данных PostgreSQL + conn = await asyncpg.connect(DATABASE_URL) + try: + # Обновляем баланс пользователя await update_balance(user_id, amount) - await db.commit() - balance = await get_balance(user_id) + # Получаем обновленный баланс + balance = await get_balance(user_id) - # Создаем клавиатуру с кнопкой "Профиль" - profile_button = InlineKeyboardButton(text='Профиль', callback_data='view_profile') - profile_keyboard = InlineKeyboardMarkup(inline_keyboard=[[profile_button]]) + # Создаем клавиатуру с кнопкой "Профиль" + profile_button = InlineKeyboardButton(text='Профиль', callback_data='view_profile') + 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( - user_id, - f"Ваш баланс был успешно пополнен на {amount} RUB.", - reply_markup=profile_keyboard - ) + # Отправляем уведомление пользователю с кнопкой "Профиль" + await bot.send_message( + user_id, + f"Ваш баланс был успешно пополнен на {amount} RUB.", + reply_markup=profile_keyboard + ) - # Удаляем сообщение с реквизитами - state_data = await state.get_data() - requisites_message_id = state_data.get('requisites_message_id') - if requisites_message_id: - try: - await bot.delete_message(chat_id=user_id, message_id=requisites_message_id) - except Exception as e: - print(f"Ошибка при удалении сообщения с реквизитами: {e}") + # Удаляем сообщение с реквизитами + requisites_message_id = state_data.get('requisites_message_id') + if requisites_message_id: + try: + await bot.delete_message(chat_id=user_id, message_id=requisites_message_id) + except Exception as e: + print(f"Ошибка при удалении сообщения с реквизитами: {e}") + + finally: + # Закрываем соединение с базой данных + await conn.close() elif action == 'decline': await send_message_with_deletion(callback_query.from_user.id, "Пополнение баланса отклонено.", state=state, message_key='admin_decline_message_id') @@ -226,7 +232,6 @@ async def process_admin_confirmation(callback_query: types.CallbackQuery, state: await callback_query.answer() - @router.message(lambda m: m.text and m.text.startswith('Недостаточно средств для продления')) async def handle_insufficient_funds(message: types.Message): user_id = message.from_user.id diff --git a/handlers/profile.py b/handlers/profile.py index c5fb2432..6089ff66 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -3,8 +3,6 @@ from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from datetime import datetime -import aiosqlite -from config import DATABASE_PATH from database import get_balance, get_key_count router = Router() diff --git a/key_management.py b/key_management.py index 417326e7..6a595d6c 100644 --- a/key_management.py +++ b/key_management.py @@ -1,8 +1,7 @@ -from datetime import datetime, timedelta -import asyncio import re import uuid -import aiosqlite +import asyncio +from datetime import datetime, timedelta from aiogram import Bot, Dispatcher, Router, types, F from aiogram.filters import Command from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message @@ -11,12 +10,13 @@ from aiogram.fsm.state import State, StatesGroup from auth import login_with_credentials, link from client import add_client -from config import API_TOKEN, ADMIN_PASSWORD, ADMIN_USERNAME, ADMIN_CHAT_ID, DATABASE_PATH +from config import API_TOKEN, ADMIN_PASSWORD, ADMIN_USERNAME, ADMIN_CHAT_ID, DATABASE_URL from database import add_connection, has_active_key, get_balance, store_key, update_balance -from bot import dp -from handlers.start import start_command +from bot import dp, bot from handlers.profile import process_callback_view_profile -from bot import bot +from handlers.start import start_command + +import asyncpg router = Router() @@ -118,15 +118,18 @@ async def handle_key_name_input(message: Message, state: FSMContext): connection_link = link(session, client_id, email) # Проверка существующей записи - async with aiosqlite.connect(DATABASE_PATH) as db: - cursor = await db.execute('SELECT * FROM connections WHERE tg_id = ?', (tg_id,)) - existing_connection = await cursor.fetchone() + conn = await asyncpg.connect(DATABASE_URL) + try: + existing_connection = await conn.fetchrow('SELECT * FROM connections WHERE tg_id = $1', tg_id) if existing_connection: - await db.execute('UPDATE connections SET trial = 1 WHERE tg_id = ?', (tg_id,)) + await conn.execute('UPDATE connections SET trial = 1 WHERE tg_id = $1', tg_id) else: await add_connection(tg_id, 0, 1) + finally: + await conn.close() + await store_key(tg_id, client_id, email, expiry_time, connection_link) keyboard = InlineKeyboardMarkup(inline_keyboard=[ @@ -168,20 +171,25 @@ async def handle_back_to_main(callback_query: CallbackQuery, state: FSMContext): async def renew_expired_keys(): while True: current_time = datetime.utcnow() - async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute('SELECT tg_id FROM connections WHERE trial > 0') as cursor: - active_keys = await cursor.fetchall() + conn = await asyncpg.connect(DATABASE_URL) + try: + active_keys = await conn.fetch('SELECT tg_id FROM connections WHERE trial > 0') - for tg_id, in active_keys: + finally: + await conn.close() + + for record in active_keys: + tg_id = record['tg_id'] balance = await get_balance(tg_id) if balance >= 100: new_expiry_time = int((current_time + timedelta(days=30)).timestamp() * 1000) - async with aiosqlite.connect(DATABASE_PATH) as db: - await db.execute('UPDATE keys SET expiry_time = ? WHERE tg_id = ?', (new_expiry_time, tg_id)) - await db.commit() - - await update_balance(tg_id, -100) + conn = await asyncpg.connect(DATABASE_URL) + try: + await conn.execute('UPDATE keys SET expiry_time = $1 WHERE tg_id = $2', new_expiry_time, tg_id) + await update_balance(tg_id, -100) + finally: + await conn.close() print(f"Ключ для пользователя {tg_id} продлен на месяц и списано 100 рублей.") else: