Implement message queue management and automatic sync on bot startup

- Added initialization of the message queue manager during bot startup, enhancing message handling capabilities.
- Implemented automatic synchronization of the admin panel on startup, providing real-time updates and improved reliability.
- Updated admin handlers to utilize the message queue for broadcasting messages, improving efficiency and error handling.
- Introduced a new command for admins to check the status of message queues, enhancing monitoring and management capabilities.
- Enhanced localization for new features and messages related to queue management and synchronization.
This commit is contained in:
machka-pasla
2025-08-06 16:39:35 +03:00
parent 5c8099168d
commit 00ffec13d8
13 changed files with 493 additions and 35 deletions
+39 -7
View File
@@ -2,12 +2,14 @@ import logging
import asyncio
from aiogram import Bot
from aiogram.utils.text_decorations import html_decoration as hd
from aiogram.exceptions import TelegramRetryAfter
from datetime import datetime, timezone
from typing import Optional, Union, Dict, Any
from config.settings import Settings
from sqlalchemy.orm import sessionmaker
from bot.middlewares.i18n import JsonI18n
from bot.utils.message_queue import get_queue_manager
class NotificationService:
@@ -19,16 +21,30 @@ class NotificationService:
self.i18n = i18n
async def _send_to_log_channel(self, message: str, thread_id: Optional[int] = None):
"""Send message to configured log channel/group"""
"""Send message to configured log channel/group using message queue"""
if not self.settings.LOG_CHAT_ID:
return
queue_manager = get_queue_manager()
if not queue_manager:
logging.warning("Message queue manager not available, falling back to direct send")
try:
await self.bot.send_message(
chat_id=self.settings.LOG_CHAT_ID,
text=message,
parse_mode="HTML",
disable_web_page_preview=True,
message_thread_id=thread_id or self.settings.LOG_THREAD_ID
)
except Exception as e:
logging.error(f"Failed to send notification to log channel {self.settings.LOG_CHAT_ID}: {e}")
return
try:
# Use thread_id if provided, otherwise use from settings
final_thread_id = thread_id or self.settings.LOG_THREAD_ID
kwargs = {
"chat_id": self.settings.LOG_CHAT_ID,
"text": message,
"parse_mode": "HTML",
"disable_web_page_preview": True
@@ -38,26 +54,42 @@ class NotificationService:
if final_thread_id:
kwargs["message_thread_id"] = final_thread_id
await self.bot.send_message(**kwargs)
# Queue message for sending (groups are rate limited to 15/minute)
await queue_manager.send_message(self.settings.LOG_CHAT_ID, **kwargs)
except Exception as e:
logging.error(f"Failed to send notification to log channel {self.settings.LOG_CHAT_ID}: {e}")
logging.error(f"Failed to queue notification to log channel {self.settings.LOG_CHAT_ID}: {e}")
async def _send_to_admins(self, message: str):
"""Send message to all admin users"""
"""Send message to all admin users using message queue"""
if not self.settings.ADMIN_IDS:
return
queue_manager = get_queue_manager()
if not queue_manager:
logging.warning("Message queue manager not available, falling back to direct send")
for admin_id in self.settings.ADMIN_IDS:
try:
await self.bot.send_message(
chat_id=admin_id,
text=message,
parse_mode="HTML",
disable_web_page_preview=True
)
except Exception as e:
logging.error(f"Failed to send notification to admin {admin_id}: {e}")
return
for admin_id in self.settings.ADMIN_IDS:
try:
await self.bot.send_message(
await queue_manager.send_message(
chat_id=admin_id,
text=message,
parse_mode="HTML",
disable_web_page_preview=True
)
except Exception as e:
logging.error(f"Failed to send notification to admin {admin_id}: {e}")
logging.error(f"Failed to queue notification to admin {admin_id}: {e}")
async def notify_new_user_registration(self, user_id: int, username: Optional[str] = None,
first_name: Optional[str] = None,
+19 -6
View File
@@ -563,6 +563,10 @@ class SubscriptionService:
)
start_date = datetime.now(timezone.utc)
new_end_date_obj = start_date + timedelta(days=bonus_days)
# For promo code activations, use the configured user traffic limit
traffic_limit = self.settings.user_traffic_limit_bytes if "promo code" in reason.lower() else self.settings.trial_traffic_limit_bytes
bonus_sub_payload = {
"user_id": user_id,
"panel_user_uuid": panel_uuid,
@@ -572,7 +576,7 @@ class SubscriptionService:
"duration_months": 0,
"is_active": True,
"status_from_panel": "ACTIVE_BONUS",
"traffic_limit_bytes": self.settings.user_traffic_limit_bytes,
"traffic_limit_bytes": traffic_limit,
}
await subscription_dal.deactivate_other_active_subscriptions(
session, panel_uuid, panel_sub_uuid
@@ -593,14 +597,23 @@ class SubscriptionService:
)
if updated_sub_model:
# Prepare panel update payload
panel_update_payload = {
"expireAt": new_end_date_obj.isoformat(
timespec="milliseconds"
).replace("+00:00", "Z")
}
# For promo code activations, remove traffic limit
if "promo code" in reason.lower():
panel_update_payload["trafficLimitBytes"] = self.settings.user_traffic_limit_bytes
panel_update_payload["trafficLimitStrategy"] = self.settings.USER_TRAFFIC_STRATEGY
logging.info(f"Updating traffic limit for user {user_id} to {self.settings.user_traffic_limit_bytes} bytes due to promo code activation")
panel_update_success = (
await self.panel_service.update_user_details_on_panel(
panel_uuid,
{
"expireAt": new_end_date_obj.isoformat(
timespec="milliseconds"
).replace("+00:00", "Z")
},
panel_update_payload,
)
)
if not panel_update_success: