Compare commits
5 Commits
b7433f0c99
...
e55e330c50
| Author | SHA1 | Date | |
|---|---|---|---|
| e55e330c50 | |||
| 8b34d79f4e | |||
| 60f77b39eb | |||
| 55510a4379 | |||
| 604957f1a7 |
@ -1,13 +1,13 @@
|
||||
from . import subscribe, active_triggers
|
||||
from . import unsubscribe
|
||||
from . import my_subscriptions
|
||||
from . import cancel_input
|
||||
from . import notification_switch_mode
|
||||
from . import debug
|
||||
from . import help
|
||||
from . import my_subscriptions
|
||||
from . import notification_switch_mode
|
||||
from . import registration
|
||||
from . import settings
|
||||
from . import start
|
||||
from . import debug
|
||||
from . import subscribe, active_triggers
|
||||
from . import unsubscribe
|
||||
from ..states import UserStateManager
|
||||
|
||||
state_manager = UserStateManager()
|
||||
@ -32,3 +32,5 @@ def register_callbacks(bot, app):
|
||||
notification_switch_mode.register_callback_notification(bot, app, state_manager)
|
||||
active_triggers.register_callbacks_active_triggers(bot, app, state_manager)
|
||||
cancel_input.register_callback_cancel_input(bot,state_manager)
|
||||
subscribe.register_callback_subscribe(bot, app, state_manager)
|
||||
unsubscribe.register_callback_unsubscribe(bot, app, state_manager)
|
||||
@ -1,8 +1,9 @@
|
||||
import telebot
|
||||
from telebot.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from app.bot.keyboards.main_menu import get_main_menu
|
||||
|
||||
from app import Subscriptions, db
|
||||
from app.bot.constants import UserStates
|
||||
from app.bot.keyboards.main_menu import get_main_menu
|
||||
from app.bot.keyboards.settings_menu import get_settings_menu
|
||||
from app.bot.utils.auth import auth
|
||||
from app.bot.utils.tg_audit import log_user_event
|
||||
@ -60,7 +61,7 @@ def register_callback_notification(bot, app, state_manager):
|
||||
|
||||
mode_text_emoji = "⛔️ Критические события" if disaster_only else "⚠️ Все события"
|
||||
mode_text = "Критические события" if disaster_only else "Все события"
|
||||
bot.send_message(chat_id, f"✅ Режим уведомлений успешно изменён на:\n {mode_text_emoji}",reply_markup=get_settings_menu())
|
||||
bot.send_message(chat_id, f"🔄 Режим уведомлений успешно изменён на:\n {mode_text_emoji}",reply_markup=get_settings_menu())
|
||||
log_user_event(chat_id, app, username, f"Режим уведомлений изменился на: {mode_text}")
|
||||
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU)
|
||||
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
|
||||
from telebot.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from telebot import TeleBot, logger
|
||||
from telebot.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
|
||||
|
||||
from app import Subscriptions
|
||||
from app.bot.constants import UserStates
|
||||
from app.bot.processors.subscribe_processor import process_subscription_button
|
||||
from app.bot.processors.subscribe_processor import process_subscription_button, process_subscribe_all_regions
|
||||
from app.bot.states import UserStateManager
|
||||
from app.bot.utils.auth import auth
|
||||
from app.bot.utils.regions import get_sorted_regions, format_regions_list, format_regions_list_marked
|
||||
from telebot import TeleBot, logger
|
||||
from app.bot.utils.regions import get_sorted_regions, format_regions_list_marked
|
||||
|
||||
|
||||
def register_handlers(bot: TeleBot, app, state_manager: UserStateManager):
|
||||
@ -36,6 +36,7 @@ def register_handlers(bot: TeleBot, app, state_manager: UserStateManager):
|
||||
regions_text = format_regions_list_marked(regions, subscribed)
|
||||
|
||||
markup = InlineKeyboardMarkup()
|
||||
markup.add(InlineKeyboardButton("Подписаться на все регионы", callback_data="subscribe_all"))
|
||||
markup.add(InlineKeyboardButton(text="Отмена", callback_data="cancel_input"))
|
||||
|
||||
bot_message = bot.send_message(chat_id,
|
||||
@ -44,3 +45,17 @@ def register_handlers(bot: TeleBot, app, state_manager: UserStateManager):
|
||||
|
||||
bot.register_next_step_handler(message, process_subscription_button, app, bot, chat_id, state_manager, bot_message.message_id)
|
||||
|
||||
|
||||
def register_callback_subscribe(bot: TeleBot, app, state_manager):
|
||||
@bot.callback_query_handler(func=lambda call: call.data == "subscribe_all")
|
||||
def handle_subscribe_all_button(call: CallbackQuery):
|
||||
chat_id = call.message.chat.id
|
||||
username = f"{call.from_user.username}" if call.from_user.username else "N/A"
|
||||
|
||||
with app.app_context():
|
||||
if not auth(chat_id, app):
|
||||
bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.")
|
||||
logger.warning(f"Неавторизованный пользователь {chat_id} @{username}")
|
||||
state_manager.set_state(chat_id, UserStates.REGISTRATION)
|
||||
return
|
||||
process_subscribe_all_regions(call, app, bot, chat_id, state_manager)
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
# app/bot/handlers/settings.py
|
||||
from telebot.types import Message
|
||||
from app.bot.keyboards.main_menu import get_main_menu
|
||||
from app.bot.keyboards.settings_menu import get_settings_menu
|
||||
|
||||
def register_handlers(bot,app):
|
||||
|
||||
|
||||
@bot.message_handler(func=lambda msg: msg.text == "Режим уведомлений")
|
||||
def handle_notify_mode(message: Message):
|
||||
bot.send_message(message.chat.id, "⚙️ Настройка режима уведомлений пока не реализована.")
|
||||
|
||||
@bot.message_handler(func=lambda msg: msg.text == "Назад")
|
||||
def handle_back(message: Message):
|
||||
bot.send_message(message.chat.id, "Возврат в главное меню", reply_markup=get_main_menu())
|
||||
@ -1,10 +1,12 @@
|
||||
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from telebot import logger
|
||||
from telebot import logger, TeleBot
|
||||
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
|
||||
|
||||
from app.bot.constants import UserStates
|
||||
from app.bot.keyboards.settings_menu import get_settings_menu
|
||||
from app.bot.processors.unsubscribe_processor import process_unsubscribe_button, process_unsubscribe_all_regions
|
||||
from app.bot.utils.auth import auth
|
||||
from app.bot.utils.helpers import get_user_subscribed_regions
|
||||
from app.bot.utils.regions import format_regions_list
|
||||
from app.bot.processors.unsubscribe_processor import process_unsubscribe_button
|
||||
|
||||
|
||||
def register_handlers(bot, app, state_manager):
|
||||
@ -14,22 +16,48 @@ def register_handlers(bot, app, state_manager):
|
||||
with app.app_context():
|
||||
chat_id = message.chat.id
|
||||
username = f"{message.from_user.username}" if message.from_user.username else "N/A"
|
||||
|
||||
if not auth(chat_id, app):
|
||||
bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.")
|
||||
logger.warning(f"Неавторизованный пользователь {chat_id} @{username}")
|
||||
state_manager.set_state(chat_id, UserStates.REGISTRATION)
|
||||
return
|
||||
else:
|
||||
state_manager.set_state(chat_id, UserStates.WAITING_INPUT)
|
||||
|
||||
user_subscriptions = get_user_subscribed_regions(chat_id)
|
||||
|
||||
# ✅ Предварительная проверка: есть ли подписки
|
||||
if not user_subscriptions:
|
||||
bot.send_message(chat_id, "ℹ️ У вас нет активных подписок для отписки.",reply_markup=get_settings_menu())
|
||||
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU)
|
||||
return
|
||||
|
||||
# Есть подписки — предлагаем меню отписки
|
||||
state_manager.set_state(chat_id, UserStates.WAITING_INPUT)
|
||||
formated_user_subscriptions = format_regions_list(user_subscriptions)
|
||||
|
||||
markup = InlineKeyboardMarkup()
|
||||
markup.add(InlineKeyboardButton("Отписаться от всех регионов", callback_data="unsubscribe_all"))
|
||||
markup.add(InlineKeyboardButton("Отмена", callback_data="cancel_input"))
|
||||
|
||||
bot.send_message(chat_id,
|
||||
f"Введите номер(а) региона(ов) через запятую подписки которых вы хотите удалить:\n\n{formated_user_subscriptions}",
|
||||
f"Введите номер(а) региона(ов) через запятую, от которых вы хотите отписаться:\n\n{formated_user_subscriptions}",
|
||||
reply_markup=markup)
|
||||
|
||||
bot.register_next_step_handler(message, process_unsubscribe_button, app, bot, chat_id, state_manager)
|
||||
|
||||
|
||||
|
||||
def register_callback_unsubscribe(bot: TeleBot, app, state_manager):
|
||||
@bot.callback_query_handler(func=lambda call: call.data == "unsubscribe_all")
|
||||
def handle_subscribe_all_button(call: CallbackQuery):
|
||||
chat_id = call.message.chat.id
|
||||
username = f"{call.from_user.username}" if call.from_user.username else "N/A"
|
||||
|
||||
with app.app_context():
|
||||
if not auth(chat_id, app):
|
||||
bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.")
|
||||
logger.warning(f"Неавторизованный пользователь {chat_id} @{username}")
|
||||
state_manager.set_state(chat_id, UserStates.REGISTRATION)
|
||||
return
|
||||
|
||||
process_unsubscribe_all_regions(call, app, bot, state_manager)
|
||||
|
||||
@ -75,3 +75,50 @@ def process_subscription_button(message: Message, app, bot, chat_id: int, state_
|
||||
# Показываем меню
|
||||
bot.send_message(chat_id, f"✅ Подписка на регионы: {', '.join(subbed_regions)} оформлена.", reply_markup=get_settings_menu())
|
||||
|
||||
|
||||
def process_subscribe_all_regions(call, app, bot, chat_id: int, state_manager):
|
||||
try:
|
||||
with app.app_context():
|
||||
username = f"{call.from_user.username}" if call.from_user.username else "N/A"
|
||||
|
||||
# Получаем все активные регионы
|
||||
active_regions = Regions.query.filter_by(active=True).all()
|
||||
active_region_ids = {r.region_id for r in active_regions}
|
||||
|
||||
# Получаем уже подписанные регионы
|
||||
existing_subs = Subscriptions.query.filter_by(chat_id=chat_id).all()
|
||||
existing_region_ids = {sub.region_id for sub in existing_subs if sub.active}
|
||||
|
||||
newly_added = []
|
||||
for region in active_regions:
|
||||
if region.region_id in existing_region_ids:
|
||||
continue
|
||||
|
||||
existing = next((sub for sub in existing_subs if sub.region_id == region.region_id), None)
|
||||
if existing:
|
||||
existing.active = True
|
||||
db.session.add(existing)
|
||||
else:
|
||||
new_sub = Subscriptions(chat_id=chat_id, region_id=region.region_id, active=True)
|
||||
db.session.add(new_sub)
|
||||
newly_added.append(str(region.region_id))
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Логирование действия
|
||||
if newly_added:
|
||||
log_user_event(chat_id, app, username, f"Подписался на все регионы: {', '.join(newly_added)}")
|
||||
|
||||
bot.answer_callback_query(call.id)
|
||||
bot.clear_step_handler_by_chat_id(chat_id)
|
||||
|
||||
bot.delete_message(chat_id, call.message.message_id)
|
||||
|
||||
bot.send_message(chat_id,
|
||||
"✅ Вы подписались на все доступные регионы.",
|
||||
reply_markup=get_settings_menu())
|
||||
|
||||
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU)
|
||||
|
||||
except Exception as e:
|
||||
bot.send_message(chat_id, "⚠️ Не удалось выполнить подписку. Попробуйте позже.")
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
from flask import Flask
|
||||
from telebot import TeleBot, logger
|
||||
from telebot.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from app.bot.keyboards.settings_menu import get_settings_menu
|
||||
from app.bot.utils.helpers import get_user_subscribed_regions
|
||||
from telebot.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
|
||||
|
||||
from app import Subscriptions
|
||||
from app.bot.constants import UserStates
|
||||
from app.bot.keyboards.settings_menu import get_settings_menu
|
||||
from app.bot.states import UserStateManager
|
||||
from app.bot.utils.helpers import get_user_subscribed_regions
|
||||
from app.bot.utils.tg_audit import log_user_event
|
||||
from app.extensions.db import db
|
||||
from app.bot.states import UserStateManager
|
||||
from app.bot.constants import UserStates
|
||||
|
||||
|
||||
def process_unsubscribe_button(message: Message, app: Flask, bot: TeleBot, chat_id: int, state_manager: UserStateManager):
|
||||
unsubbed_regions = []
|
||||
@ -63,3 +65,38 @@ def process_unsubscribe_button(message: Message, app: Flask, bot: TeleBot, chat_
|
||||
|
||||
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU)
|
||||
bot.send_message(chat_id, "⚙ Вернулись в меню настроек.", reply_markup=get_settings_menu())
|
||||
|
||||
|
||||
def process_unsubscribe_all_regions(call: CallbackQuery, app: Flask, bot: TeleBot, state_manager: UserStateManager):
|
||||
chat_id = call.message.chat.id
|
||||
username = f"{call.from_user.username}" if call.from_user.username else "N/A"
|
||||
|
||||
try:
|
||||
with app.app_context():
|
||||
subscriptions = Subscriptions.query.filter_by(chat_id=chat_id, active=True).all()
|
||||
if not subscriptions:
|
||||
bot.answer_callback_query(call.id, text="❗ У вас нет активных подписок.")
|
||||
return
|
||||
|
||||
for sub in subscriptions:
|
||||
sub.active = False
|
||||
db.session.add(sub)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
log_user_event(chat_id, app, username, "Отписался от всех регионов")
|
||||
|
||||
bot.answer_callback_query(call.id)
|
||||
bot.clear_step_handler_by_chat_id(chat_id)
|
||||
|
||||
bot.delete_message(chat_id, call.message.message_id)
|
||||
bot.send_message(chat_id,
|
||||
"✅ Вы успешно отписались от всех регионов.",
|
||||
reply_markup=get_settings_menu())
|
||||
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU)
|
||||
bot.send_message(chat_id, "⚠ Произошла ошибка при отписке. Попробуйте позже.",reply_markup=get_settings_menu())
|
||||
return
|
||||
|
||||
@ -1,17 +1,21 @@
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from telebot import apihelper
|
||||
from aio_pika import connect_robust, Message, DeliveryMode
|
||||
from aio_pika.abc import AbstractIncomingMessage
|
||||
from app.bot.services.mailing_service.parser import parse_region_id
|
||||
from telebot import apihelper
|
||||
|
||||
from app.bot.services.mailing_service.composer import compose_telegram_message
|
||||
from app.bot.services.mailing_service.db_utils import get_recipients_by_region
|
||||
from config import RABBITMQ_URL_FULL, RABBITMQ_QUEUE, RABBITMQ_NOTIFICATIONS_QUEUE
|
||||
from app.bot.services.mailing_service.parser import parse_region_id
|
||||
from app.bot.services.mailing_service.utils.link_button import create_link_button
|
||||
from app.bot.services.mailing_service.utils.tg_get_user import get_telegram_id_by_chat_id
|
||||
from config import RABBITMQ_URL_FULL, RABBITMQ_QUEUE, RABBITMQ_NOTIFICATIONS_QUEUE, MAILING_RATE_LIMIT
|
||||
|
||||
logger = logging.getLogger("TeleBot")
|
||||
rate_limit_semaphore = asyncio.Semaphore(25)
|
||||
rate_limit_semaphore = asyncio.Semaphore(MAILING_RATE_LIMIT)
|
||||
|
||||
class AsyncMailingService:
|
||||
def __init__(self, flask_app, bot):
|
||||
@ -28,7 +32,7 @@ class AsyncMailingService:
|
||||
async def consume_raw_messages(self):
|
||||
while True:
|
||||
try:
|
||||
logger.info("[MailingService] Подключение к RabbitMQ (сырые сообщения)...")
|
||||
logger.info("[MailingService][raw] Подключение к RabbitMQ (сырые сообщения)...")
|
||||
connection = await connect_robust(RABBITMQ_URL_FULL, loop=self.loop)
|
||||
async with connection:
|
||||
channel = await connection.channel()
|
||||
@ -36,14 +40,14 @@ class AsyncMailingService:
|
||||
raw_queue = await channel.declare_queue(RABBITMQ_QUEUE, durable=True)
|
||||
notifications_queue = await channel.declare_queue(RABBITMQ_NOTIFICATIONS_QUEUE, durable=True)
|
||||
|
||||
logger.info("[MailingService] Ожидание сообщений из очереди...")
|
||||
logger.info("[MailingService][raw] Ожидание сообщений из очереди...")
|
||||
|
||||
async with raw_queue.iterator() as queue_iter:
|
||||
async for message in queue_iter:
|
||||
await self.handle_raw_message(message, channel, notifications_queue)
|
||||
except Exception as e:
|
||||
logger.error(f"[MailingService] Ошибка подключения или обработки: {e}")
|
||||
logger.info("[MailingService] Повторное подключение через 5 секунд...")
|
||||
logger.error(f"[MailingService][raw] Ошибка подключения или обработки: {e}")
|
||||
logger.info("[MailingService][raw] Повторное подключение через 5 секунд...")
|
||||
await asyncio.sleep(5)
|
||||
|
||||
async def handle_raw_message(self, message: AbstractIncomingMessage, channel, notifications_queue):
|
||||
@ -51,9 +55,9 @@ class AsyncMailingService:
|
||||
with self.flask_app.app_context():
|
||||
try:
|
||||
data = json.loads(message.body.decode("utf-8"))
|
||||
logger.info(f"[MailingService] Получено сообщение: {json.dumps(data, ensure_ascii=False)}")
|
||||
logger.info(f"[MailingService][raw] Получены данные: {json.dumps(data, ensure_ascii=False)}")
|
||||
|
||||
region_id = parse_region_id(data.get("host"))
|
||||
region_id = int(parse_region_id(data.get("hostgroups")))
|
||||
severity = data.get("severity", "")
|
||||
|
||||
# Получатели и сообщение параллельно
|
||||
@ -61,12 +65,8 @@ class AsyncMailingService:
|
||||
message_task = asyncio.to_thread(compose_telegram_message, data)
|
||||
|
||||
recipients, (final_message, link) = await asyncio.gather(recipients_task, message_task)
|
||||
|
||||
logger.info(f"[MailingService] Получатели: {recipients}")
|
||||
if link:
|
||||
logger.info(f"[MailingService] Сообщение для Telegram: {final_message} {link}")
|
||||
else:
|
||||
logger.info(f"[MailingService] Сообщение для Telegram: {final_message}")
|
||||
logger.info(f"[MailingService][raw] Формирование списка получателей ({len(recipients)})")
|
||||
logger.debug(f"[MailingService][raw] Список получателей: {', '.join(map(str, recipients))}")
|
||||
|
||||
# Формируем и публикуем индивидуальные уведомления в очередь отправки
|
||||
for chat_id in recipients:
|
||||
@ -79,29 +79,29 @@ class AsyncMailingService:
|
||||
msg = Message(body, delivery_mode=DeliveryMode.PERSISTENT)
|
||||
await channel.default_exchange.publish(msg, routing_key=notifications_queue.name)
|
||||
|
||||
logger.info(f"[MailingService] Поставлено в очередь уведомлений: {len(recipients)} сообщений")
|
||||
logger.info(f"[MailingService][raw] Поставлено в очередь уведомлений: {len(recipients)} сообщений")
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"[MailingService] Ошибка обработки сообщения: {e}")
|
||||
logger.exception(f"[MailingService][raw] Ошибка обработки сообщения: {e}")
|
||||
|
||||
async def consume_notifications(self):
|
||||
while True:
|
||||
try:
|
||||
logger.info("[MailingService] Подключение к RabbitMQ (уведомления для отправки)...")
|
||||
logger.info("[MailingService][formated] Подключение к RabbitMQ (уведомления для отправки)...")
|
||||
connection = await connect_robust(RABBITMQ_URL_FULL, loop=self.loop)
|
||||
async with connection:
|
||||
channel = await connection.channel()
|
||||
await channel.set_qos(prefetch_count=5)
|
||||
notif_queue = await channel.declare_queue(RABBITMQ_NOTIFICATIONS_QUEUE, durable=True)
|
||||
|
||||
logger.info("[MailingService] Ожидание сообщений из очереди уведомлений...")
|
||||
logger.info("[MailingService][formated] Ожидание сообщений из очереди уведомлений...")
|
||||
|
||||
async with notif_queue.iterator() as queue_iter:
|
||||
async for message in queue_iter:
|
||||
await self.handle_notification_message(message)
|
||||
except Exception as e:
|
||||
logger.error(f"[MailingService] Ошибка подключения или обработки уведомлений: {e}")
|
||||
logger.info("[MailingService] Повторное подключение через 5 секунд...")
|
||||
logger.error(f"[MailingService][formated] Ошибка подключения или обработки уведомлений: {e}")
|
||||
logger.info("[MailingService][formated] Повторное подключение через 5 секунд...")
|
||||
await asyncio.sleep(5)
|
||||
|
||||
async def handle_notification_message(self, message: AbstractIncomingMessage):
|
||||
@ -111,41 +111,60 @@ class AsyncMailingService:
|
||||
chat_id = data.get("chat_id")
|
||||
message_text = data.get("message")
|
||||
link = data.get("link")
|
||||
if link:
|
||||
message_text = f"{message_text} {link}"
|
||||
# TODO: расширить логику отправки с кнопкой, если нужно
|
||||
|
||||
await self.send_message(chat_id, message_text)
|
||||
await self.send_message(chat_id, message_text, link)
|
||||
except Exception as e:
|
||||
logger.error(f"[MailingService] Ошибка отправки уведомления: {e}")
|
||||
logger.error(f"[MailingService][tg message] Ошибка отправки уведомления: {e}")
|
||||
# Можно реализовать message.nack() для повторной попытки
|
||||
|
||||
async def send_message(self, chat_id, message):
|
||||
async def send_message(self, chat_id, message, link: Optional[str] = None):
|
||||
telegram_id = "unknown"
|
||||
try:
|
||||
await rate_limit_semaphore.acquire()
|
||||
|
||||
def get_telegram_id():
|
||||
with self.flask_app.app_context():
|
||||
from app.models.users import Users
|
||||
user = Users.query.filter_by(chat_id=chat_id).first()
|
||||
return user.telegram_id if user else "unknown"
|
||||
# Получаем telegram_id в отдельном потоке
|
||||
telegram_id = await asyncio.to_thread(get_telegram_id_by_chat_id, self.flask_app, chat_id)
|
||||
|
||||
telegram_id = await asyncio.to_thread(get_telegram_id)
|
||||
# Формируем клавиатуру с кнопкой
|
||||
link_button = create_link_button(link)
|
||||
|
||||
await asyncio.to_thread(self.bot.send_message, chat_id, message, parse_mode="HTML")
|
||||
await asyncio.to_thread(
|
||||
self.bot.send_message,
|
||||
chat_id,
|
||||
message,
|
||||
parse_mode="HTML",
|
||||
reply_markup=link_button
|
||||
)
|
||||
|
||||
formatted_message = message.replace("\n", " ").replace("\r", "")
|
||||
logger.info(f"[MailingService] Отправлено уведомление {telegram_id} ({chat_id}): {formatted_message}")
|
||||
logger.info(f"[MailingService][tg message] Отправлено уведомление {telegram_id} ({chat_id}): {formatted_message}")
|
||||
|
||||
except apihelper.ApiTelegramException as e:
|
||||
if "429" in str(e):
|
||||
logger.warning(f"[MailingService] Rate limit для {telegram_id} ({chat_id}), ждем и повторяем...")
|
||||
# Попытка получить error_code и описание из исключения
|
||||
error_code = None
|
||||
description = None
|
||||
try:
|
||||
if hasattr(e, "result") and e.result:
|
||||
error_code = e.result.get("error_code")
|
||||
description = e.result.get("description")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if error_code == 429:
|
||||
logger.warning(f"[MailingService][tg message] Rate limit для {telegram_id} ({chat_id}), ждем и повторяем...")
|
||||
await asyncio.sleep(1)
|
||||
await self.send_message(chat_id, message)
|
||||
await self.send_message(chat_id, message, link)
|
||||
elif error_code in (400, 403) and description and (
|
||||
"user is deactivated" in description.lower() or
|
||||
"bot was blocked by the user" in description.lower() or
|
||||
"user not found" in description.lower()
|
||||
):
|
||||
logger.warning(f"[MailingService][tg message] Ошибка {error_code}: {description}. Пользователь {telegram_id} ({chat_id}) заблокировал бота или не существует. Отправка пропущена.")
|
||||
# Не повторяем отправку
|
||||
else:
|
||||
logger.error(f"[MailingService] Ошибка отправки сообщения {telegram_id} ({chat_id}): {e}")
|
||||
logger.error(f"[MailingService][tg message] Ошибка отправки сообщения {telegram_id} ({chat_id}): {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"[MailingService] Неожиданная ошибка отправки сообщения {telegram_id} ({chat_id}): {e}")
|
||||
logger.error(f"[MailingService][tg message] Неожиданная ошибка отправки сообщения {telegram_id} ({chat_id}): {e}")
|
||||
finally:
|
||||
rate_limit_semaphore.release()
|
||||
|
||||
|
||||
@ -1,19 +1,31 @@
|
||||
# parser.py
|
||||
import re
|
||||
|
||||
|
||||
def parse_region_id(host: str) -> int | None:
|
||||
"""
|
||||
Извлекает region_id из строки host.
|
||||
Формат: p<region><...>, например p18ecpapp01 → region_id = 18
|
||||
# def parse_region_id(host: str) -> int | None:
|
||||
# """
|
||||
# Извлекает region_id из строки host.
|
||||
# Формат: p<region><...>, например p18ecpapp01 → region_id = 18
|
||||
#
|
||||
# Returns:
|
||||
# int | None: номер региона или None
|
||||
# """
|
||||
# if not host or not host.startswith("p"):
|
||||
# return None
|
||||
#
|
||||
# match = re.match(r"^p(\d+)", host)
|
||||
# if match:
|
||||
# return int(match.group(1))
|
||||
# return None
|
||||
|
||||
Returns:
|
||||
int | None: номер региона или None
|
||||
"""
|
||||
if not host or not host.startswith("p"):
|
||||
return None
|
||||
def parse_region_id(hostgroups: str | None) -> str:
|
||||
if not hostgroups:
|
||||
return "0"
|
||||
|
||||
match = re.match(r"^p(\d+)", host)
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
return None
|
||||
if hostgroups == "p00rtmis":
|
||||
return "0"
|
||||
|
||||
parts = hostgroups.split("_")
|
||||
if len(parts) >= 2:
|
||||
return parts[1]
|
||||
|
||||
return "0"
|
||||
|
||||
17
app/bot/services/mailing_service/utils/link_button.py
Normal file
17
app/bot/services/mailing_service/utils/link_button.py
Normal file
@ -0,0 +1,17 @@
|
||||
from typing import Optional
|
||||
|
||||
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
|
||||
|
||||
def create_link_button(link: Optional[str]) -> Optional[InlineKeyboardMarkup]:
|
||||
"""
|
||||
Создаёт InlineKeyboardMarkup с кнопкой-ссылкой.
|
||||
Если ссылка не передана, возвращает None.
|
||||
"""
|
||||
if not link:
|
||||
return None
|
||||
|
||||
markup = InlineKeyboardMarkup()
|
||||
button = InlineKeyboardButton(text="График", url=link)
|
||||
markup.add(button)
|
||||
return markup
|
||||
6
app/bot/services/mailing_service/utils/tg_get_user.py
Normal file
6
app/bot/services/mailing_service/utils/tg_get_user.py
Normal file
@ -0,0 +1,6 @@
|
||||
def get_telegram_id_by_chat_id(flask_app, chat_id: int) -> str:
|
||||
from app.models.users import Users # импорт здесь, чтобы избежать циклических зависимостей
|
||||
|
||||
with flask_app.app_context():
|
||||
user = Users.query.filter_by(chat_id=chat_id).first()
|
||||
return user.telegram_id if user else "unknown"
|
||||
@ -27,14 +27,13 @@ RABBITMQ_QUEUE = os.environ.get("RABBITMQ_QUEUE", "telegram_notifications")
|
||||
RABBITMQ_NOTIFICATIONS_QUEUE = os.environ.get("RABBITMQ_NOTIFICATIONS_QUEUE", "notifications_queue")
|
||||
RABBITMQ_VHOST = os.getenv("RABBITMQ_VHOST", "/")
|
||||
RABBITMQ_VHOST_ENCODED = quote_plus(RABBITMQ_VHOST)
|
||||
|
||||
RABBITMQ_URL_FULL = (
|
||||
f"amqp://{RABBITMQ_LOGIN}:{RABBITMQ_PASS}@{RABBITMQ_HOST}:{RABBITMQ_PORT}/{RABBITMQ_VHOST_ENCODED}"
|
||||
)
|
||||
# Mailing settings
|
||||
MAILING_MAX_WORKERS = int(os.environ.get("MAILING_MAX_WORKERS", "16"))
|
||||
MAILING_RATE_LIMIT = int(os.environ.get("MAILING_RATE_LIMIT", "25"))
|
||||
|
||||
MAILING_MAX_WORKERS = int(os.getenv("MAILING_MAX_WORKERS", "16"))
|
||||
MAILING_RATE_LIMIT = int(os.getenv("MAILING_RATE_LIMIT", "25"))
|
||||
MAILING_MAX_RETRIES = int(os.getenv("MAILING_MAX_RETRIES", "5"))
|
||||
# Настройки Flask-LDAP3-login
|
||||
LDAP_HOST = os.getenv('LDAP_HOST', 'localhost')
|
||||
LDAP_PORT = int(os.getenv('LDAP_PORT', 389))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user