From 71588fda6332cd42d69f0b6071258d4f316bd569 Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 24 Sep 2025 07:25:44 +0300 Subject: [PATCH] Revert "Add automated admin reports" --- .env.example | 2 - README.md | 6 - app/bot.py | 2 - app/config.py | 23 -- app/handlers/admin/main.py | 25 +- app/handlers/admin/reports.py | 95 ------- app/keyboards/admin.py | 12 - app/services/admin_notification_service.py | 28 +- app/services/report_service.py | 284 --------------------- locales/en.json | 7 - locales/ru.json | 7 - main.py | 18 -- 12 files changed, 5 insertions(+), 504 deletions(-) delete mode 100644 app/handlers/admin/reports.py delete mode 100644 app/services/report_service.py diff --git a/.env.example b/.env.example index c78d8a82..2a801dfb 100644 --- a/.env.example +++ b/.env.example @@ -14,8 +14,6 @@ ADMIN_NOTIFICATIONS_ENABLED=true ADMIN_NOTIFICATIONS_CHAT_ID=-1001234567890 # Замени на ID твоего канала (-100) - ПРЕФИКС ЗАКРЫТОГО КАНАЛА! ВСТАВИТЬ СВОЙ ID СРАЗУ ПОСЛЕ (-100) БЕЗ ПРОБЕЛОВ! ADMIN_NOTIFICATIONS_TOPIC_ID=123 # Опционально: ID топика ADMIN_NOTIFICATIONS_TICKET_TOPIC_ID=126 # Опционально: ID топика для тикетов -ADMIN_REPORTS_TOPIC_ID=130 # Опционально: отдельный ID топика для отчетов -ADMIN_REPORTS_TIME_MOSCOW=09:00 # Время ежедневного отчета (МСК) # Обязательная подписка на канал CHANNEL_SUB_ID= # Опционально ID твоего канала (-100) CHANNEL_IS_REQUIRED_SUB=false # Обязательна ли подписка на канал diff --git a/README.md b/README.md index 87da0ea8..7f65c08a 100644 --- a/README.md +++ b/README.md @@ -254,8 +254,6 @@ ADMIN_NOTIFICATIONS_ENABLED=true ADMIN_NOTIFICATIONS_CHAT_ID=-1001234567890 # Замени на ID твоего канала (-100) - ПРЕФИКС ЗАКРЫТОГО КАНАЛА! ВСТАВИТЬ СВОЙ ID СРАЗУ ПОСЛЕ (-100) БЕЗ ПРОБЕЛОВ! ADMIN_NOTIFICATIONS_TOPIC_ID=123 # Опционально: ID топика ADMIN_NOTIFICATIONS_TICKET_TOPIC_ID=126 # Опционально: ID топика для тикетов -ADMIN_REPORTS_TOPIC_ID=130 # Опционально: отдельный ID топика для отчетов -ADMIN_REPORTS_TIME_MOSCOW=09:00 # Время ежедневного отчета (МСК) # Обязательная подписка на канал CHANNEL_SUB_ID= # Опционально ID твоего канала (-100) CHANNEL_IS_REQUIRED_SUB=false # Обязательна ли подписка на канал @@ -971,12 +969,8 @@ docker compose down -v --remove-orphans ADMIN_NOTIFICATIONS_ENABLED=true ADMIN_NOTIFICATIONS_CHAT_ID=-1001234567890 # ID канала/группы ADMIN_NOTIFICATIONS_TOPIC_ID=123 # ID топика (опционально) -ADMIN_REPORTS_TOPIC_ID=130 # ID топика для отчетов (опционально) -ADMIN_REPORTS_TIME_MOSCOW=09:00 # Время ежедневного отчета (МСК) ``` -> ⚙️ Бот автоматически отправит ежедневный отчет за предыдущие сутки в указанное время (по МСК). Если `ADMIN_REPORTS_TOPIC_ID` не задан, отчеты будут приходить в основной топик уведомлений. В админ-панели доступен раздел «Отчеты» для ручной отправки ежедневных, недельных и месячных сводок. - #### 2. Создание канала 1. **Создайте приватный канал** или группу для уведомлений diff --git a/app/bot.py b/app/bot.py index a13e2237..c23bf06d 100644 --- a/app/bot.py +++ b/app/bot.py @@ -38,7 +38,6 @@ from app.handlers.admin import ( backup as admin_backup, welcome_text as admin_welcome_text, tickets as admin_tickets, - reports as admin_reports, ) from app.handlers.stars_payments import register_stars_handlers @@ -140,7 +139,6 @@ async def setup_bot() -> tuple[Bot, Dispatcher]: admin_backup.register_handlers(dp) admin_welcome_text.register_welcome_text_handlers(dp) admin_tickets.register_handlers(dp) - admin_reports.register_handlers(dp) common.register_handlers(dp) register_stars_handlers(dp) logger.info("⭐ Зарегистрированы обработчики Telegram Stars платежей") diff --git a/app/config.py b/app/config.py index 811c9999..8c8e6f17 100644 --- a/app/config.py +++ b/app/config.py @@ -2,7 +2,6 @@ import os import re import html from collections import defaultdict -from datetime import time as dt_time from typing import List, Optional, Union, Dict from pydantic_settings import BaseSettings from pydantic import field_validator, Field @@ -27,8 +26,6 @@ class Settings(BaseSettings): ADMIN_NOTIFICATIONS_CHAT_ID: Optional[str] = None ADMIN_NOTIFICATIONS_TOPIC_ID: Optional[int] = None ADMIN_NOTIFICATIONS_TICKET_TOPIC_ID: Optional[int] = None - ADMIN_REPORTS_TOPIC_ID: Optional[int] = None - ADMIN_REPORTS_TIME_MOSCOW: str = "09:00" CHANNEL_SUB_ID: Optional[str] = None CHANNEL_LINK: Optional[str] = None @@ -640,26 +637,6 @@ class Settings(BaseSettings): return (self.ADMIN_NOTIFICATIONS_ENABLED and self.get_admin_notifications_chat_id() is not None) - def get_admin_reports_topic_id(self) -> Optional[int]: - if not self.ADMIN_REPORTS_TOPIC_ID: - return None - - try: - return int(self.ADMIN_REPORTS_TOPIC_ID) - except (ValueError, TypeError): - return None - - def get_admin_reports_time(self) -> dt_time: - raw_value = (self.ADMIN_REPORTS_TIME_MOSCOW or "09:00").strip() - - try: - hours_str, minutes_str = raw_value.split(":", maxsplit=1) - hours = max(0, min(23, int(hours_str))) - minutes = max(0, min(59, int(minutes_str))) - return dt_time(hour=hours, minute=minutes) - except (ValueError, AttributeError): - return dt_time(hour=9, minute=0) - def get_backup_send_chat_id(self) -> Optional[int]: if not self.BACKUP_SEND_CHAT_ID: return None diff --git a/app/handlers/admin/main.py b/app/handlers/admin/main.py index ac86a658..a26fd7b3 100644 --- a/app/handlers/admin/main.py +++ b/app/handlers/admin/main.py @@ -12,8 +12,7 @@ from app.keyboards.admin import ( get_admin_communications_submenu_keyboard, get_admin_support_submenu_keyboard, get_admin_settings_submenu_keyboard, - get_admin_system_submenu_keyboard, - get_admin_reports_keyboard, + get_admin_system_submenu_keyboard ) from app.localization.texts import get_texts from app.handlers.admin import support_settings as support_settings_handlers @@ -145,23 +144,6 @@ async def show_support_submenu( await callback.answer() -@admin_required -@error_handler -async def show_reports_submenu( - callback: types.CallbackQuery, - db_user: User, - db: AsyncSession -): - texts = get_texts(db_user.language) - - await callback.message.edit_text( - f"📈 **{texts.ADMIN_REPORTS}**\n\n" + texts.ADMIN_REPORTS_MENU_HINT, - reply_markup=get_admin_reports_keyboard(db_user.language), - parse_mode="Markdown" - ) - await callback.answer() - - # Moderator panel entry (from main menu quick button) async def show_moderator_panel( callback: types.CallbackQuery, @@ -424,11 +406,6 @@ def register_handlers(dp: Dispatcher): show_support_audit, F.data.in_(["admin_support_audit"]) | F.data.startswith("admin_support_audit_page_") ) - - dp.callback_query.register( - show_reports_submenu, - F.data == "admin_submenu_reports" - ) dp.callback_query.register( show_settings_submenu, diff --git a/app/handlers/admin/reports.py b/app/handlers/admin/reports.py deleted file mode 100644 index d6f795e6..00000000 --- a/app/handlers/admin/reports.py +++ /dev/null @@ -1,95 +0,0 @@ -import logging - -from aiogram import Dispatcher, types, F -from sqlalchemy.ext.asyncio import AsyncSession - -from app.database.models import User -from app.localization.texts import get_texts -from app.services.report_service import report_service, ReportPeriod -from app.utils.decorators import admin_required, error_handler - - -logger = logging.getLogger(__name__) - - -async def _send_report( - callback: types.CallbackQuery, - db_user: User, - period: ReportPeriod, - success_message: str, - error_message: str, -): - success, _ = await report_service.send_report(period) - - if success: - logger.info("Админ %s отправил отчет %s", db_user.id, period.value) - await callback.answer(success_message) - else: - logger.error("Не удалось отправить отчет %s по запросу админа %s", period.value, db_user.id) - await callback.answer(error_message, show_alert=True) - - -@admin_required -@error_handler -async def send_daily_report( - callback: types.CallbackQuery, - db_user: User, - db: AsyncSession, -): - texts = get_texts(db_user.language) - await _send_report( - callback, - db_user, - ReportPeriod.DAILY, - texts.ADMIN_REPORTS_SENT, - texts.ADMIN_REPORTS_ERROR, - ) - - -@admin_required -@error_handler -async def send_weekly_report( - callback: types.CallbackQuery, - db_user: User, - db: AsyncSession, -): - texts = get_texts(db_user.language) - await _send_report( - callback, - db_user, - ReportPeriod.WEEKLY, - texts.ADMIN_REPORTS_SENT, - texts.ADMIN_REPORTS_ERROR, - ) - - -@admin_required -@error_handler -async def send_monthly_report( - callback: types.CallbackQuery, - db_user: User, - db: AsyncSession, -): - texts = get_texts(db_user.language) - await _send_report( - callback, - db_user, - ReportPeriod.MONTHLY, - texts.ADMIN_REPORTS_SENT, - texts.ADMIN_REPORTS_ERROR, - ) - - -def register_handlers(dp: Dispatcher) -> None: - dp.callback_query.register( - send_daily_report, - F.data == "admin_report_daily", - ) - dp.callback_query.register( - send_weekly_report, - F.data == "admin_report_weekly", - ) - dp.callback_query.register( - send_monthly_report, - F.data == "admin_report_monthly", - ) diff --git a/app/keyboards/admin.py b/app/keyboards/admin.py index e3c41abf..7b5f1549 100644 --- a/app/keyboards/admin.py +++ b/app/keyboards/admin.py @@ -10,7 +10,6 @@ def get_admin_main_keyboard(language: str = "ru") -> InlineKeyboardMarkup: return InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text="👥 Юзеры/Подписки", callback_data="admin_submenu_users")], [InlineKeyboardButton(text="💰 Промокоды/Статистика", callback_data="admin_submenu_promo")], - [InlineKeyboardButton(text=texts.ADMIN_REPORTS, callback_data="admin_submenu_reports")], [InlineKeyboardButton(text="🛟 Поддержка", callback_data="admin_submenu_support")], [InlineKeyboardButton(text="📨 Сообщения", callback_data="admin_submenu_communications")], [InlineKeyboardButton(text="⚙️ Настройки", callback_data="admin_submenu_settings")], @@ -92,17 +91,6 @@ def get_admin_support_submenu_keyboard(language: str = "ru") -> InlineKeyboardMa ]) -def get_admin_reports_keyboard(language: str = "ru") -> InlineKeyboardMarkup: - texts = get_texts(language) - - return InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text=texts.ADMIN_REPORTS_DAILY, callback_data="admin_report_daily")], - [InlineKeyboardButton(text=texts.ADMIN_REPORTS_WEEKLY, callback_data="admin_report_weekly")], - [InlineKeyboardButton(text=texts.ADMIN_REPORTS_MONTHLY, callback_data="admin_report_monthly")], - [InlineKeyboardButton(text="⬅️ Назад", callback_data="admin_panel")] - ]) - - def get_admin_settings_submenu_keyboard(language: str = "ru") -> InlineKeyboardMarkup: texts = get_texts(language) diff --git a/app/services/admin_notification_service.py b/app/services/admin_notification_service.py index f817c40a..1882e35b 100644 --- a/app/services/admin_notification_service.py +++ b/app/services/admin_notification_service.py @@ -20,7 +20,6 @@ class AdminNotificationService: self.chat_id = getattr(settings, 'ADMIN_NOTIFICATIONS_CHAT_ID', None) self.topic_id = getattr(settings, 'ADMIN_NOTIFICATIONS_TOPIC_ID', None) self.ticket_topic_id = getattr(settings, 'ADMIN_NOTIFICATIONS_TICKET_TOPIC_ID', None) - self.reports_topic_id = settings.get_admin_reports_topic_id() self.enabled = getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) async def _get_referrer_info(self, db: AsyncSession, referred_by_id: Optional[int]) -> str: @@ -307,18 +306,11 @@ class AdminNotificationService: logger.error(f"Ошибка отправки уведомления о продлении: {e}") return False - async def _send_message( - self, - text: str, - reply_markup: types.InlineKeyboardMarkup | None = None, - *, - ticket_event: bool = False, - topic_id: Optional[int] = None - ) -> bool: + async def _send_message(self, text: str, reply_markup: types.InlineKeyboardMarkup | None = None, *, ticket_event: bool = False) -> bool: if not self.chat_id: logger.warning("ADMIN_NOTIFICATIONS_CHAT_ID не настроен") return False - + try: message_kwargs = { 'chat_id': self.chat_id, @@ -329,9 +321,7 @@ class AdminNotificationService: # route to ticket-specific topic if provided thread_id = None - if topic_id: - thread_id = topic_id - elif ticket_event and self.ticket_topic_id: + if ticket_event and self.ticket_topic_id: thread_id = self.ticket_topic_id elif self.topic_id: thread_id = self.topic_id @@ -339,7 +329,7 @@ class AdminNotificationService: message_kwargs['message_thread_id'] = thread_id if reply_markup is not None: message_kwargs['reply_markup'] = reply_markup - + await self.bot.send_message(**message_kwargs) logger.info(f"Уведомление отправлено в чат {self.chat_id}") return True @@ -356,16 +346,6 @@ class AdminNotificationService: def _is_enabled(self) -> bool: return self.enabled and bool(self.chat_id) - - def is_enabled(self) -> bool: - return self._is_enabled() - - async def send_report_message(self, text: str, *, topic_id: Optional[int] = None) -> bool: - if not self._is_enabled(): - return False - - effective_topic = topic_id or self.reports_topic_id or self.topic_id - return await self._send_message(text, topic_id=effective_topic) def _get_payment_method_display(self, payment_method: Optional[str]) -> str: method_names = { diff --git a/app/services/report_service.py b/app/services/report_service.py deleted file mode 100644 index 45735ff7..00000000 --- a/app/services/report_service.py +++ /dev/null @@ -1,284 +0,0 @@ -import asyncio -import logging -from dataclasses import dataclass -from datetime import datetime, timedelta -from enum import Enum -from typing import Optional, Tuple -from zoneinfo import ZoneInfo - -from sqlalchemy import select, func -from sqlalchemy.ext.asyncio import AsyncSession - -from app.config import settings -from app.database.database import AsyncSessionLocal -from app.database.models import ( - Subscription, - SubscriptionStatus, - Transaction, - TransactionType, -) -from app.services.admin_notification_service import AdminNotificationService - - -logger = logging.getLogger(__name__) - - -class ReportPeriod(Enum): - DAILY = "daily" - WEEKLY = "weekly" - MONTHLY = "monthly" - - -@dataclass -class ReportPeriodInfo: - start_msk: datetime - end_msk: datetime - title: str - caption: str - range_caption: str - emoji: str - - -class ReportService: - def __init__(self) -> None: - self.notification_service: Optional[AdminNotificationService] = None - self._task: Optional[asyncio.Task] = None - self._stop_event = asyncio.Event() - self._moscow_tz = ZoneInfo("Europe/Moscow") - self._utc_tz = ZoneInfo("UTC") - - def set_notification_service(self, service: AdminNotificationService) -> None: - self.notification_service = service - - async def start(self) -> Optional[asyncio.Task]: - if self._task and not self._task.done(): - return self._task - - if not self.notification_service or not self.notification_service.is_enabled(): - logger.info("Сервис отчетов не запущен: админ-уведомления отключены или не настроен чат") - return None - - self._stop_event.clear() - self._task = asyncio.create_task(self._scheduler_loop()) - logger.info("Сервис отчетов запущен") - return self._task - - async def stop(self) -> None: - if not self._task: - return - - self._stop_event.set() - try: - await self._task - finally: - self._task = None - self._stop_event.clear() - logger.info("Сервис отчетов остановлен") - - async def send_report(self, period: ReportPeriod) -> Tuple[bool, str]: - text, _ = await self.generate_report(period) - - if not text: - return False, text - - if not self.notification_service or not self.notification_service.is_enabled(): - logger.warning("Отчет не отправлен: сервис админ-уведомлений недоступен") - return False, text - - success = await self.notification_service.send_report_message(text) - if success: - logger.info("Отчет %s отправлен", period.value) - else: - logger.error("Не удалось отправить отчет %s", period.value) - return success, text - - async def generate_report(self, period: ReportPeriod) -> Tuple[str, dict]: - info = self._get_period_info(period) - if not info: - return "", {} - - start_utc = info.start_msk.astimezone(self._utc_tz).replace(tzinfo=None) - end_utc = info.end_msk.astimezone(self._utc_tz).replace(tzinfo=None) - - async with AsyncSessionLocal() as session: - stats = await self._collect_stats(session, start_utc, end_utc) - - text = self._format_report(info, stats) - return text, stats - - async def _scheduler_loop(self) -> None: - while not self._stop_event.is_set(): - next_run = self._get_next_run_datetime() - now_utc = datetime.now(self._utc_tz) - wait_seconds = max(0, (next_run - now_utc).total_seconds()) - - try: - await asyncio.wait_for(self._stop_event.wait(), timeout=wait_seconds) - break - except asyncio.TimeoutError: - pass - - if self._stop_event.is_set(): - break - - try: - await self.send_report(ReportPeriod.DAILY) - except Exception as error: # pragma: no cover - defensive logging - logger.error("Ошибка отправки ежедневного отчета: %s", error, exc_info=True) - - async def _collect_stats(self, session: AsyncSession, start: datetime, end: datetime) -> dict: - now_utc = datetime.utcnow() - - total_trials_query = select(func.count()).select_from(Subscription).where( - Subscription.is_trial.is_(True), - Subscription.end_date > now_utc, - Subscription.status.in_([ - SubscriptionStatus.ACTIVE.value, - SubscriptionStatus.TRIAL.value, - ]), - ) - total_trials = (await session.scalar(total_trials_query)) or 0 - - total_paid_query = select(func.count()).select_from(Subscription).where( - Subscription.is_trial.is_(False), - Subscription.end_date > now_utc, - Subscription.status == SubscriptionStatus.ACTIVE.value, - ) - total_paid = (await session.scalar(total_paid_query)) or 0 - - new_trials_query = select(func.count()).select_from(Subscription).where( - Subscription.is_trial.is_(True), - Subscription.start_date >= start, - Subscription.start_date < end, - ) - new_trials = (await session.scalar(new_trials_query)) or 0 - - new_paid_query = select(func.count()).select_from(Subscription).where( - Subscription.is_trial.is_(False), - Subscription.start_date >= start, - Subscription.start_date < end, - ) - new_paid = (await session.scalar(new_paid_query)) or 0 - - payments_query = select( - func.count(Transaction.id), - func.coalesce(func.sum(Transaction.amount_kopeks), 0), - ).where( - Transaction.type == TransactionType.DEPOSIT.value, - Transaction.is_completed.is_(True), - Transaction.created_at >= start, - Transaction.created_at < end, - ) - payments_count, payments_sum = (await session.execute(payments_query)).one() - - return { - "total_trials": int(total_trials), - "total_paid": int(total_paid), - "new_trials": int(new_trials), - "new_paid": int(new_paid), - "payments_count": int(payments_count or 0), - "payments_sum": int(payments_sum or 0), - "period_start": start, - "period_end": end, - } - - def _format_report(self, info: ReportPeriodInfo, stats: dict) -> str: - now_msk = datetime.now(self._moscow_tz) - end_display = info.end_msk - timedelta(seconds=1) - period_range = ( - f"{info.start_msk.strftime('%d.%m.%Y %H:%M')} — " - f"{end_display.strftime('%d.%m.%Y %H:%M')}" - ) - - lines = [ - f"{info.emoji} {info.title} ({info.caption})", - "", - "🎯 Триалы", - f"• Активных сейчас: {stats['total_trials']}", - f"• Новых за период: {stats['new_trials']}", - "", - "💎 Платные подписки", - f"• Активных сейчас: {stats['total_paid']}", - f"• Новых за период: {stats['new_paid']}", - "", - "💳 Пополнения", - f"• Количество платежей: {stats['payments_count']}", - f"• Сумма: {settings.format_price(stats['payments_sum'])}", - "", - f"🕒 Период (МСК): {period_range}", - f"📅 Сформировано: {now_msk.strftime('%d.%m.%Y %H:%M')}", - ] - - return "\n".join(lines) - - def _get_period_info(self, period: ReportPeriod) -> Optional[ReportPeriodInfo]: - now_msk = datetime.now(self._moscow_tz) - - if period is ReportPeriod.DAILY: - target_date = now_msk.date() - timedelta(days=1) - start_msk = datetime.combine(target_date, datetime.min.time(), tzinfo=self._moscow_tz) - end_msk = start_msk + timedelta(days=1) - caption = start_msk.strftime('%d.%m.%Y') - return ReportPeriodInfo( - start_msk=start_msk, - end_msk=end_msk, - title="Ежедневный отчет", - caption=caption, - range_caption=caption, - emoji="🗓️", - ) - - if period is ReportPeriod.WEEKLY: - end_msk = datetime.combine(now_msk.date(), datetime.min.time(), tzinfo=self._moscow_tz) - start_msk = end_msk - timedelta(days=7) - caption = ( - f"{start_msk.strftime('%d.%m.%Y')} — " - f"{(end_msk - timedelta(days=1)).strftime('%d.%m.%Y')}" - ) - return ReportPeriodInfo( - start_msk=start_msk, - end_msk=end_msk, - title="Еженедельный отчет", - caption=caption, - range_caption=caption, - emoji="🗓️", - ) - - if period is ReportPeriod.MONTHLY: - current_month_start = datetime(now_msk.year, now_msk.month, 1, tzinfo=self._moscow_tz) - end_msk = current_month_start - previous_month_last_day = current_month_start - timedelta(days=1) - start_msk = datetime( - previous_month_last_day.year, - previous_month_last_day.month, - 1, - tzinfo=self._moscow_tz, - ) - caption = ( - f"{start_msk.strftime('%d.%m.%Y')} — " - f"{previous_month_last_day.strftime('%d.%m.%Y')}" - ) - return ReportPeriodInfo( - start_msk=start_msk, - end_msk=end_msk, - title="Ежемесячный отчет", - caption=caption, - range_caption=caption, - emoji="📆", - ) - - logger.warning("Неизвестный период отчета: %s", period) - return None - - def _get_next_run_datetime(self) -> datetime: - dispatch_time = settings.get_admin_reports_time() - now_msk = datetime.now(self._moscow_tz) - - run_msk = datetime.combine(now_msk.date(), dispatch_time, tzinfo=self._moscow_tz) - if run_msk <= now_msk: - run_msk += timedelta(days=1) - - return run_msk.astimezone(self._utc_tz) - - -report_service = ReportService() diff --git a/locales/en.json b/locales/en.json index 3b108db8..40656662 100644 --- a/locales/en.json +++ b/locales/en.json @@ -130,7 +130,6 @@ "ADMIN_MONITORING": "🔍 Monitoring", "ADMIN_PANEL": "\n⚙️ Administration panel\n\nSelect a section to manage:\n", "ADMIN_PROMOCODES": "🎫 Promo codes", - "ADMIN_REPORTS": "📈 Reports", "ADMIN_REFERRALS": "🤝 Referral program", "ADMIN_REMNAWAVE": "🖥️ Remnawave", "ADMIN_RULES": "📋 Rules", @@ -258,12 +257,6 @@ "ADMIN_PROMO_GROUP_DELETED": "Promo group “{name}” deleted.", "ADMIN_SUBSCRIPTIONS": "📱 Subscriptions", "ADMIN_USERS": "👥 Users", - "ADMIN_REPORTS_MENU_HINT": "Choose which report to send to the admin topic.", - "ADMIN_REPORTS_DAILY": "📅 Daily report (yesterday)", - "ADMIN_REPORTS_WEEKLY": "🗓️ Weekly report", - "ADMIN_REPORTS_MONTHLY": "📆 Monthly report", - "ADMIN_REPORTS_SENT": "✅ Report sent to the admin topic.", - "ADMIN_REPORTS_ERROR": "❌ Failed to send the report. Check notification settings.", "AUTOPAY_DISABLED_TEXT": "Disabled — don't forget to renew manually!", "AUTOPAY_ENABLED_TEXT": "Enabled — the subscription will renew automatically", "AUTOPAY_FAILED": "\n❌ Autopay failed\n\nWe couldn't charge the renewal payment.\nBalance available: {balance}\nRequired: {required}\n\nPlease top up your balance and renew manually.\n", diff --git a/locales/ru.json b/locales/ru.json index a56fdd58..eb8d1ea4 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -7,7 +7,6 @@ "ADMIN_MONITORING": "🔍 Мониторинг", "ADMIN_PANEL": "\n⚙️ Административная панель\n\nВыберите раздел для управления:\n", "ADMIN_PROMOCODES": "🎫 Промокоды", - "ADMIN_REPORTS": "📈 Отчеты", "ADMIN_REFERRALS": "🤝 Партнерка", "ADMIN_REMNAWAVE": "🖥️ Remnawave", "ADMIN_RULES": "📋 Правила", @@ -135,12 +134,6 @@ "ADMIN_PROMO_GROUP_DELETED": "Промогруппа «{name}» удалена.", "ADMIN_SUBSCRIPTIONS": "📱 Подписки", "ADMIN_USERS": "👥 Пользователи", - "ADMIN_REPORTS_MENU_HINT": "Выберите период отчета для отправки в админ-топик.", - "ADMIN_REPORTS_DAILY": "📅 Отчет за вчера", - "ADMIN_REPORTS_WEEKLY": "🗓️ Отчет за неделю", - "ADMIN_REPORTS_MONTHLY": "📆 Отчет за месяц", - "ADMIN_REPORTS_SENT": "✅ Отчет отправлен в админ-топик.", - "ADMIN_REPORTS_ERROR": "❌ Не удалось отправить отчет. Проверьте настройки уведомлений.", "AUTOPAY_BUTTON": "💳 Автоплатёж", "AUTOPAY_DISABLED_TEXT": "Отключен - не забудьте продлить вручную!", "AUTOPAY_ENABLED_TEXT": "Включен - подписка продлится автоматически", diff --git a/main.py b/main.py index 17c3de48..4be13cde 100644 --- a/main.py +++ b/main.py @@ -20,7 +20,6 @@ from app.external.pal24_webhook import start_pal24_webhook_server, Pal24WebhookS from app.database.universal_migration import run_universal_migration from app.services.backup_service import backup_service from app.localization.loader import ensure_locale_templates -from app.services.report_service import report_service class GracefulExit: @@ -61,7 +60,6 @@ async def main(): monitoring_task = None maintenance_task = None version_check_task = None - reports_task = None polling_task = None try: @@ -98,9 +96,6 @@ async def main(): version_service.set_notification_service(admin_notification_service) logger.info(f"📄 Сервис версий настроен для репозитория: {version_service.repo}") logger.info(f"📦 Текущая версия: {version_service.current_version}") - - report_service.set_notification_service(admin_notification_service) - reports_task = await report_service.start() logger.info("🔗 Бот подключен к сервисам мониторинга и техработ") @@ -227,13 +222,6 @@ async def main(): if settings.is_version_check_enabled(): logger.info("🔄 Перезапуск сервиса проверки версий...") version_check_task = asyncio.create_task(version_service.start_periodic_check()) - - if reports_task and reports_task.done(): - exception = reports_task.exception() - if exception: - logger.error(f"Сервис отчетов завершился с ошибкой: {exception}") - new_task = await report_service.start() - reports_task = new_task if new_task else None if polling_task.done(): exception = polling_task.exception() @@ -289,12 +277,6 @@ async def main(): except asyncio.CancelledError: pass - logger.info("ℹ️ Остановка сервиса отчетов...") - try: - await report_service.stop() - except Exception as e: - logger.error(f"Ошибка остановки сервиса отчетов: {e}") - logger.info("ℹ️ Остановка сервиса бекапов...") try: await backup_service.stop_auto_backup()