@@ -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())
|
||||
|
||||
@@ -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" 📆 За неделю: <b>{registrations_week}</b>\n"
|
||||
f" 📆 За месяц: <b>{registrations_month}</b>\n"
|
||||
f" 🌐 За все время: <b>{total_users}</b>\n\n"
|
||||
f"🌟 Активные пользователи:\n"
|
||||
f" 🌟 Активных сегодня: <b>{users_updated_today}</b>\n\n"
|
||||
f"👥 Рефералы:\n"
|
||||
f" 🤝 Всего привлечено: <b>{total_referrals}</b>\n\n"
|
||||
f"🔑 Ключи:\n"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
ADD_SUB = "➕ Подписка"
|
||||
MY_SUBS = "📱 Мои подписки"
|
||||
BALANCE = "💰 Баланс"
|
||||
BALANCE_HISTORY = "📊 История пополнения"
|
||||
PAYMENT = "💳 Пополнить баланс"
|
||||
INVITE = "👥 Пригласить"
|
||||
GIFTS = "🎁 Подарить"
|
||||
|
||||
@@ -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 []
|
||||
|
||||
@@ -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
|
||||
|
||||
+44
-2
@@ -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"<b>Сумма:</b> {amount}\n<b>Способ оплаты:</b> {payment_system}\n<b>Статус:</b> {status}\n<b>Дата:</b> {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):
|
||||
|
||||
+15
-38
@@ -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)
|
||||
|
||||
+15
-3
@@ -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
|
||||
check = false
|
||||
exclude = [
|
||||
".git",
|
||||
"venv",
|
||||
"main.py",
|
||||
"handlers/payments",
|
||||
]
|
||||
Reference in New Issue
Block a user