Files

141 lines
5.6 KiB
Python

from __future__ import annotations
from datetime import datetime, timedelta
import pytz
from aiogram import Bot
from aiogram.types import InlineKeyboardButton, WebAppInfo
from aiogram.utils.keyboard import InlineKeyboardBuilder
from sqlalchemy import update
from sqlalchemy.ext.asyncio import AsyncSession
from config import NOTIFY_INACTIVE_TRAFFIC, REMNAWAVE_WEBAPP, REMNAWAVE_WEBAPP_OPEN_IN_BROWSER, SUPPORT_CHAT_URL
from core.bootstrap import MODES_CONFIG, NOTIFICATIONS_CONFIG
from database.models import Key
from database.tariffs import get_tariffs
from handlers.buttons import CONNECT_DEVICE, MAIN_MENU, SUPPORT
from handlers.keys.utils import build_key_callback
from handlers.notifications.sender import send_messages_with_limit
from handlers.texts import ZERO_TRAFFIC_MSG
from handlers.utils import is_full_remnawave_cluster
from hooks.hook_buttons import insert_hook_buttons
from hooks.hooks import run_hooks
from logger import logger
from panels.remnawave_runtime import fetch_all_remnawave_traffic
moscow_tz = pytz.timezone("Europe/Moscow")
async def process_zero_traffic(
bot: Bot,
session: AsyncSession,
current_time: int,
keys: list,
):
inactive_traffic_hours = int(NOTIFICATIONS_CONFIG.get("INACTIVE_TRAFFIC_ENABLED", NOTIFY_INACTIVE_TRAFFIC))
if inactive_traffic_hours <= 0:
return
trial_tariffs = await get_tariffs(session, group_code="trial")
trial_tariff_ids = {t["id"] for t in trial_tariffs} if trial_tariffs else set()
if not trial_tariff_ids:
return
current_dt = datetime.fromtimestamp(current_time / 1000, tz=moscow_tz)
remnawave_webapp_enabled = bool(MODES_CONFIG.get("REMNAWAVE_WEBAPP_ENABLED", REMNAWAVE_WEBAPP))
open_in_browser = bool(MODES_CONFIG.get("REMNAWAVE_WEBAPP_OPEN_IN_BROWSER", REMNAWAVE_WEBAPP_OPEN_IN_BROWSER))
candidate_keys = []
for key in keys:
if key.tariff_id not in trial_tariff_ids:
continue
if key.created_at is None or key.notified:
continue
created_at_dt = pytz.utc.localize(datetime.fromtimestamp(key.created_at / 1000)).astimezone(moscow_tz)
if current_dt < created_at_dt + timedelta(hours=inactive_traffic_hours):
continue
if key.expiry_time:
expiry_dt = pytz.utc.localize(datetime.fromtimestamp(key.expiry_time / 1000)).astimezone(moscow_tz)
if current_dt > expiry_dt:
continue
candidate_keys.append(key)
if not candidate_keys:
return
needed_uuids = {k.client_id for k in candidate_keys if k.client_id}
logger.info(f"[ZeroTraffic] Кандидатов: {len(candidate_keys)}, UUID: {len(needed_uuids)}")
try:
traffic_map = await fetch_all_remnawave_traffic(session, needed_uuids=needed_uuids)
except Exception as e:
logger.error(f"[ZeroTraffic] Ошибка получения трафика: {e}")
return
messages = []
keys_to_mark = [k.client_id for k in candidate_keys]
for key in candidate_keys:
tg_id = key.tg_id
email = key.email
client_id = key.client_id
used_bytes = traffic_map.get(client_id)
if used_bytes is None or used_bytes > 0:
continue
builder = InlineKeyboardBuilder()
server_id = key.server_id
try:
is_full_remnawave = await is_full_remnawave_cluster(server_id, session)
final_link = key.key or key.remnawave_link
if is_full_remnawave and final_link and remnawave_webapp_enabled:
if open_in_browser:
builder.row(InlineKeyboardButton(text=CONNECT_DEVICE, url=final_link))
else:
builder.row(InlineKeyboardButton(text=CONNECT_DEVICE, web_app=WebAppInfo(url=final_link)))
else:
builder.row(InlineKeyboardButton(
text=CONNECT_DEVICE,
callback_data=build_key_callback("connect_device", client_id, email),
))
except Exception as e:
logger.error(f"Ошибка типа панели для {email}: {e}")
builder.row(InlineKeyboardButton(
text=CONNECT_DEVICE,
callback_data=build_key_callback("connect_device", client_id, email),
))
builder.row(InlineKeyboardButton(text=SUPPORT, url=SUPPORT_CHAT_URL))
builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile"))
try:
hook_commands = await run_hooks(
"zero_traffic_notification", chat_id=tg_id, admin=False, session=session, email=email,
)
if hook_commands:
builder = insert_hook_buttons(builder, hook_commands)
except Exception as e:
logger.warning(f"[ZeroTraffic] Ошибка хуков: {e}")
messages.append({
"tg_id": tg_id,
"text": ZERO_TRAFFIC_MSG.format(email=email),
"keyboard": builder.as_markup(),
"client_id": client_id,
})
if keys_to_mark:
try:
await session.execute(update(Key).where(Key.client_id.in_(keys_to_mark)).values(notified=True))
logger.info(f"[ZeroTraffic] Отмечено {len(keys_to_mark)} ключей как notified")
except Exception as e:
logger.error(f"[ZeroTraffic] Ошибка обновления notified: {e}")
if messages:
results = await send_messages_with_limit(bot, messages, messages_per_second=25)
sent_count = sum(1 for r in results if r)
logger.info(f"[ZeroTraffic] Отправлено {sent_count} уведомлений")