diff --git a/bot.py b/bot.py
index 9c98ab3d..ee0ce957 100644
--- a/bot.py
+++ b/bot.py
@@ -12,7 +12,6 @@ from middlewares.admin import AdminMiddleware
from middlewares.database import DatabaseMiddleware
from middlewares.delete import DeleteMessageMiddleware
from middlewares.logging import LoggingMiddleware
-from middlewares.throttling import ThrottlingMiddleware
from middlewares.user import UserMiddleware
bot = Bot(token=API_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
@@ -30,9 +29,8 @@ dp.callback_query.middleware(UserMiddleware())
dp.message.middleware(DatabaseMiddleware())
dp.callback_query.middleware(DatabaseMiddleware())
-# Add throttling middleware
-dp.message.middleware(ThrottlingMiddleware(limit=1)) # 1 message per second
-dp.callback_query.middleware(ThrottlingMiddleware(limit=1))
+# dp.message.middleware(ThrottlingMiddleware(limit=1))
+# dp.callback_query.middleware(ThrottlingMiddleware(limit=1))
dp.message.outer_middleware(DeleteMessageMiddleware())
dp.callback_query.outer_middleware(DeleteMessageMiddleware())
diff --git a/handlers/admin/admin_panel.py b/handlers/admin/admin_panel.py
index 699bef1b..2b05b337 100644
--- a/handlers/admin/admin_panel.py
+++ b/handlers/admin/admin_panel.py
@@ -120,6 +120,10 @@ async def user_stats_menu(callback_query: CallbackQuery, session: Any):
"SELECT COUNT(*) FROM connections WHERE created_at >= date_trunc('month', CURRENT_DATE)"
)
+ users_updated_today = await session.fetchval(
+ "SELECT COUNT(*) FROM users WHERE updated_at >= CURRENT_DATE"
+ )
+
active_keys = await session.fetchval(
"SELECT COUNT(*) FROM keys WHERE expiry_time > $1",
int(datetime.utcnow().timestamp() * 1000),
@@ -133,6 +137,8 @@ async def user_stats_menu(callback_query: CallbackQuery, session: Any):
f" 📆 За неделю: {registrations_week}\n"
f" 📆 За месяц: {registrations_month}\n"
f" 🌐 За все время: {total_users}\n\n"
+ f"🌟 Активные пользователи:\n"
+ f" 🌟 Активных сегодня: {users_updated_today}\n\n"
f"👥 Рефералы:\n"
f" 🤝 Всего привлечено: {total_referrals}\n\n"
f"🔑 Ключи:\n"
diff --git a/handlers/buttons/profile.py b/handlers/buttons/profile.py
index dea0cdd0..3b6d80ab 100644
--- a/handlers/buttons/profile.py
+++ b/handlers/buttons/profile.py
@@ -1,5 +1,7 @@
ADD_SUB = "➕ Подписка"
MY_SUBS = "📱 Мои подписки"
+BALANCE = "💰 Баланс"
+BALANCE_HISTORY = "📊 История пополнения"
PAYMENT = "💳 Пополнить баланс"
INVITE = "👥 Пригласить"
GIFTS = "🎁 Подарить"
diff --git a/handlers/keys/subscriptions.py b/handlers/keys/subscriptions.py
index 38ae40cc..a8be7c15 100644
--- a/handlers/keys/subscriptions.py
+++ b/handlers/keys/subscriptions.py
@@ -13,7 +13,8 @@ from logger import logger
async def fetch_url_content(url, tg_id):
try:
logger.info(f"Получение URL: {url} для tg_id: {tg_id}")
- async with aiohttp.ClientSession() as session:
+ timeout = aiohttp.ClientTimeout(total=5)
+ async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.get(url, ssl=False) as response:
if response.status == 200:
content = await response.text()
@@ -24,6 +25,9 @@ async def fetch_url_content(url, tg_id):
f"Не удалось получить {url} для tg_id: {tg_id}, статус: {response.status}"
)
return []
+ except asyncio.TimeoutError:
+ logger.error(f"Таймаут при получении {url} для tg_id: {tg_id}")
+ return []
except Exception as e:
logger.error(f"Ошибка при получении {url} для tg_id: {tg_id}: {e}")
return []
diff --git a/handlers/notifications.py b/handlers/notifications.py
index 355eb199..13b761a1 100644
--- a/handlers/notifications.py
+++ b/handlers/notifications.py
@@ -32,6 +32,28 @@ from logger import logger
router = Router()
+async def check_users_and_update_blocked(bot: Bot):
+ conn = None
+ try:
+ conn = await asyncpg.connect(DATABASE_URL)
+ users = await conn.fetch("SELECT tg_id FROM users")
+
+ for user in users:
+ try:
+ await bot.send_chat_action(user['tg_id'], "typing")
+ except (TelegramForbiddenError,Exception):
+ await conn.execute(
+ "INSERT INTO blocked_users (tg_id) VALUES ($1) ON CONFLICT (tg_id) DO NOTHING",
+ user['tg_id']
+ )
+ logger.info(f"User {user['tg_id']} added to blocked_users")
+ except Exception as e:
+ logger.error(f"Error in check_users_and_update_blocked: {e}")
+ finally:
+ if conn:
+ await conn.close()
+
+
async def notify_expiring_keys(bot: Bot):
conn = None
diff --git a/handlers/profile.py b/handlers/profile.py
index 243f3794..1f5f2fd4 100644
--- a/handlers/profile.py
+++ b/handlers/profile.py
@@ -10,6 +10,8 @@ from config import DATABASE_URL, NEWS_MESSAGE, RENEWAL_PLANS
from database import get_balance, get_key_count, get_referral_stats, get_trial
from handlers.buttons.profile import (
ADD_SUB,
+ BALANCE,
+ BALANCE_HISTORY,
GIFTS,
INSTRUCTIONS,
INVITE,
@@ -66,8 +68,8 @@ async def process_callback_view_profile(
builder.row(
InlineKeyboardButton(
- text=PAYMENT,
- callback_data="pay",
+ text=BALANCE,
+ callback_data="balance",
)
)
builder.row(
@@ -112,6 +114,46 @@ async def process_callback_view_profile(
await conn.close()
+@router.callback_query(F.data == "balance")
+async def balance_handler(callback_query: types.CallbackQuery):
+ builder = InlineKeyboardBuilder()
+ builder.row(InlineKeyboardButton(text=PAYMENT, callback_data="pay"))
+ builder.row(InlineKeyboardButton(text=BALANCE_HISTORY, callback_data="balance_history"))
+ builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile"))
+
+ await callback_query.message.answer(
+ "💰 Управление балансом:",
+ reply_markup=builder.as_markup()
+ )
+
+@router.callback_query(F.data == "balance_history")
+async def balance_history_handler(callback_query: types.CallbackQuery, session: Any):
+ builder = InlineKeyboardBuilder()
+ builder.row(InlineKeyboardButton(text=PAYMENT, callback_data="pay"))
+ builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile"))
+
+ query = """
+ SELECT amount, payment_system, status, created_at
+ FROM payments
+ WHERE tg_id = $1
+ ORDER BY created_at DESC
+ """
+ records = await session.fetch(query, callback_query.from_user.id)
+
+ history_text = "📊 История операций с балансом:\n\n"
+ for record in records:
+ amount = record['amount']
+ payment_system = record['payment_system']
+ status = record['status']
+ date = record['created_at'].strftime('%Y-%m-%d %H:%M:%S')
+ history_text += f"Сумма: {amount}\nСпособ оплаты: {payment_system}\nСтатус: {status}\nДата: {date}\n\n"
+
+ await callback_query.message.answer(
+ history_text,
+ reply_markup=builder.as_markup()
+ )
+
+
@router.message(F.text == "/tariffs")
@router.callback_query(F.data == "view_tariffs")
async def view_tariffs_handler(callback_query: types.CallbackQuery):
diff --git a/middlewares/throttling.py b/middlewares/throttling.py
index be6e64de..2761920c 100644
--- a/middlewares/throttling.py
+++ b/middlewares/throttling.py
@@ -1,42 +1,19 @@
-import asyncio
+from collections.abc import Awaitable, Callable
+from typing import Any
-from aiogram import Dispatcher, types
-from aiogram.dispatcher import DEFAULT_RATE_LIMIT
-from aiogram.dispatcher.handler import CancelHandler, current_handler
-from aiogram.dispatcher.middlewares import BaseMiddleware
-from aiogram.utils.exceptions import Throttled
-from aiogram.utils.keyboard import InlineKeyboardBuilder
+from aiogram import BaseMiddleware
+from aiogram.types import TelegramObject
-class ThrottlingMiddleware(BaseMiddleware):
- def __init__(self, limit=DEFAULT_RATE_LIMIT, key_prefix="antiflood_"):
- self.rate_limit = limit
- self.prefix = key_prefix
- super(ThrottlingMiddleware, self).__init__()
+class ThrottleMiddleware(BaseMiddleware):
+ def __init__(self, limit: int):
+ self.limit = limit
- async def on_process_message(self, message: types.Message, data: dict):
- handler = current_handler.get()
- dispatcher = Dispatcher.get_current()
-
- if handler:
- limit = getattr(handler, "throttling_rate_limit", self.rate_limit)
- key = getattr(handler, "throttling_key", f"{self.prefix}_{handler.__name__}")
- else:
- limit = self.rate_limit
- key = f"{self.prefix}_message"
-
- try:
- await dispatcher.throttle(key, rate=limit)
- except Throttled as t:
- await self.message_throttled(message, t)
- raise CancelHandler()
-
- async def message_throttled(self, message: types.Message, throttled: Throttled):
- delta = throttled.rate - throttled.delta
-
- if throttled.exceeded_count <= 2:
- builder = InlineKeyboardBuilder()
- builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
- await message.reply("🚫 Слишком много запросов! Пожалуйста, не торопитесь!", reply_markup=builder.as_markup())
-
- await asyncio.sleep(delta)
+ async def __call__(
+ self,
+ handler: Callable[[TelegramObject, dict[str, Any]], Awaitable[Any]],
+ event: TelegramObject,
+ data: dict[str, Any],
+ ) -> Any:
+ #todo
+ return await handler(event, data)
diff --git a/pyproject.toml b/pyproject.toml
index 1ea6e345..e8e30a7a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,13 +5,25 @@ target-version = "py310"
[tool.ruff.lint]
select = ["E", "F", "W", "I", "N", "UP", "ANN", "ASYNC", "S", "BLE", "FBT", "B", "A", "C4", "DTZ", "T10", "ISC", "ICN", "G", "PIE"]
ignore = ["ANN101", "ANN102", "S101",'ANN201','ANN001','BLE001']
+exclude = [
+ ".git",
+ "venv",
+ "main.py",
+ "handlers/payments",
+]
[tool.ruff.format]
-quote-style = "single"
-indent-style = "space"
+quote-style = "double"
+indent-style = "tab"
[tool.darker]
src = ["."]
revision = "HEAD"
diff = false
-check = false
\ No newline at end of file
+check = false
+exclude = [
+ ".git",
+ "venv",
+ "main.py",
+ "handlers/payments",
+]
\ No newline at end of file