Проблема: - Монолитный notification_signals.py (~310 строк) смешивал разные concerns - Сложная логика с упоминаниями, ответами и обычными сообщениями в одном файле - Множество вспомогательных функций без четкой структуры Решение: Разделен на модульную структуру communications/notifications/: - config.py - NotificationVerbs, MessageTemplates, ActionURLs, вспомогательные функции (extract_mentions, truncate_message, get_chat_name) - handlers.py - notify_new_message(), notify_chat_added(), get_users_with_notifications_enabled() - signals.py - Django signal receivers (@receiver) - __init__.py - публичный API модуля Изменения: - Создан: communications/notifications/ модуль с 4 файлами - Переименован: notification_signals.py → notification_signals.py.old - Изменен: apps.py - обновлен импорт на communications.notifications Логика уведомлений: 1. Упоминания (@email) - высокий приоритет 2. Ответы на сообщения - средний приоритет 3. Обычные сообщения - низкий приоритет Преимущества: - ✅ Единообразие с requests_app, procurement, feed - ✅ Четкое разделение ответственности (SRP) - ✅ ОПТИМИЗАЦИЯ: Настройки уведомлений загружаются одним запросом - ✅ Легче тестировать компоненты - ✅ Публичный API для переиспользования функций - ✅ Централизованные шаблоны сообщений Верификация: - python manage.py check - успешно
160 lines
5.0 KiB
Python
160 lines
5.0 KiB
Python
"""
|
||
Конфигурация уведомлений для модуля Communications.
|
||
|
||
Содержит:
|
||
- NotificationVerbs - глаголы для уведомлений
|
||
- MessageTemplates - шаблоны сообщений
|
||
- ActionURLs - URL-адреса действий
|
||
- Вспомогательные функции для обработки сообщений
|
||
"""
|
||
|
||
import re
|
||
|
||
|
||
class NotificationVerbs:
|
||
"""Глаголы (verbs) для уведомлений communications."""
|
||
|
||
NEW_MESSAGE = 'chat_new_message'
|
||
ANNOUNCEMENT = 'announcement_new_message'
|
||
MENTION = 'chat_mention'
|
||
REPLY = 'chat_reply'
|
||
ADDED_TO_CHAT = 'chat_added_to_chat'
|
||
|
||
|
||
class MessageTemplates:
|
||
"""Шаблоны сообщений для уведомлений communications."""
|
||
|
||
@staticmethod
|
||
def mention(author_name: str) -> str:
|
||
"""Шаблон для упоминания пользователя."""
|
||
return f'Вас упомянул {author_name}'
|
||
|
||
@staticmethod
|
||
def reply(author_name: str) -> str:
|
||
"""Шаблон для ответа на сообщение."""
|
||
return f'{author_name} ответил на ваше сообщение'
|
||
|
||
@staticmethod
|
||
def announcement(author_name: str) -> str:
|
||
"""Шаблон для нового объявления."""
|
||
return f'Новое объявление от {author_name}'
|
||
|
||
@staticmethod
|
||
def private_message(author_name: str) -> str:
|
||
"""Шаблон для приватного сообщения."""
|
||
return f'Новое сообщение от {author_name}'
|
||
|
||
@staticmethod
|
||
def group_message(author_name: str, chat_name: str) -> str:
|
||
"""Шаблон для сообщения в группе."""
|
||
return f'{author_name} в {chat_name}'
|
||
|
||
@staticmethod
|
||
def added_to_chat(chat_name: str) -> str:
|
||
"""Шаблон для добавления в чат."""
|
||
return f'Вы были добавлены в чат "{chat_name}"'
|
||
|
||
@staticmethod
|
||
def added_to_chat_title() -> str:
|
||
"""Заголовок для уведомления о добавлении в чат."""
|
||
return 'Вас добавили в чат'
|
||
|
||
|
||
class ActionURLs:
|
||
"""URL-адреса для действий с сообщениями."""
|
||
|
||
MESSAGES = '/messages'
|
||
|
||
@staticmethod
|
||
def chat_detail(chat_id: int) -> str:
|
||
"""
|
||
Возвращает URL-адрес чата.
|
||
|
||
TODO: реализовать прямые ссылки на конкретные чаты
|
||
"""
|
||
return f'/messages?chat={chat_id}'
|
||
|
||
@staticmethod
|
||
def message_detail(chat_id: int, message_id: int) -> str:
|
||
"""
|
||
Возвращает URL-адрес сообщения.
|
||
|
||
TODO: реализовать прямые ссылки на конкретные сообщения
|
||
"""
|
||
return f'/messages?chat={chat_id}&message={message_id}'
|
||
|
||
|
||
# ===== Вспомогательные функции =====
|
||
|
||
def extract_mentions(text: str) -> list[str]:
|
||
"""
|
||
Извлекает email'ы из упоминаний вида @email.
|
||
|
||
Args:
|
||
text: Текст сообщения
|
||
|
||
Returns:
|
||
list: Список email'ов без символа @
|
||
|
||
Examples:
|
||
>>> extract_mentions("Привет @user@example.com и @test@mail.com")
|
||
['user@example.com', 'test@mail.com']
|
||
"""
|
||
if not text:
|
||
return []
|
||
|
||
# Паттерн: @ + email формат
|
||
pattern = r'@([\w.+-]+@[\w.-]+\.[\w]+)'
|
||
mentions = re.findall(pattern, text)
|
||
|
||
return list(set(mentions)) # Уникальные значения
|
||
|
||
|
||
def truncate_message(text: str, max_length: int = 100) -> str:
|
||
"""
|
||
Обрезает текст сообщения до указанной длины.
|
||
|
||
Args:
|
||
text: Исходный текст
|
||
max_length: Максимальная длина (по умолчанию 100)
|
||
|
||
Returns:
|
||
Обрезанный текст с многоточием если нужно
|
||
"""
|
||
if not text:
|
||
return ''
|
||
|
||
if len(text) <= max_length:
|
||
return text
|
||
|
||
return text[:max_length - 3] + '...'
|
||
|
||
|
||
def get_chat_name(chat) -> str:
|
||
"""
|
||
Возвращает название чата в зависимости от типа.
|
||
|
||
Args:
|
||
chat: Объект Chat
|
||
|
||
Returns:
|
||
str: Название чата
|
||
"""
|
||
if chat.name:
|
||
return chat.name
|
||
|
||
if chat.type == 'global':
|
||
return 'Глобальный чат'
|
||
|
||
if chat.type == 'department' and chat.department:
|
||
return f'Чат отдела: {chat.department.name}'
|
||
|
||
if chat.type == 'private':
|
||
# Для приватного чата можно вернуть имена участников
|
||
participants = chat.participants.all()[:2]
|
||
if participants:
|
||
names = [p.get_full_name() or p.username for p in participants]
|
||
return ', '.join(names)
|
||
|
||
return 'Чат'
|