From 6bcac057d166f2362016260db10d223f01f69495 Mon Sep 17 00:00:00 2001 From: zosimovaa Date: Sun, 7 Dec 2025 16:44:23 +0300 Subject: [PATCH] no message --- src/mail_order_bot/context.py | 7 +- .../email_client/{email_magic.py => utils.py} | 65 ++++++++++--------- .../attachment_handler/attachment_handler.py | 11 ++++ .../email_processor/processor.py | 29 ++++++--- src/mail_order_bot/main.py | 7 +- 5 files changed, 76 insertions(+), 43 deletions(-) rename src/mail_order_bot/email_client/{email_magic.py => utils.py} (72%) create mode 100644 src/mail_order_bot/email_processor/handlers/attachment_handler/attachment_handler.py diff --git a/src/mail_order_bot/context.py b/src/mail_order_bot/context.py index 9d6c8dd..bc00bfe 100644 --- a/src/mail_order_bot/context.py +++ b/src/mail_order_bot/context.py @@ -1,5 +1,8 @@ import threading from typing import Any, Dict +import logging + +logger = logging.getLogger() class _SingletonMeta(type): _instances = {} @@ -23,10 +26,10 @@ class Context(metaclass=_SingletonMeta): """Очищает self.context, устанавливая его в None или пустой словарь""" with self._lock: # потокобезопасная очистка self.context = {} - print("Context очищен") # опциональный лог + logger.debug("Context очищен") # опциональный лог def set_context(self, new_context: Dict[str, Any]): """Устанавливает новый контекст (бонусный метод)""" with self._lock: self.context = new_context - print("Новый контекст установлен") \ No newline at end of file + logger.debug("Новый контекст установлен") \ No newline at end of file diff --git a/src/mail_order_bot/email_client/email_magic.py b/src/mail_order_bot/email_client/utils.py similarity index 72% rename from src/mail_order_bot/email_client/email_magic.py rename to src/mail_order_bot/email_client/utils.py index c8e6370..bbb0672 100644 --- a/src/mail_order_bot/email_client/email_magic.py +++ b/src/mail_order_bot/email_client/utils.py @@ -1,3 +1,4 @@ +from email.header import decode_header, make_header import re from datetime import datetime from typing import List, Optional @@ -11,33 +12,28 @@ from email.header import decode_header import imaplib import smtplib +from .objects import EmailMessage, EmailAttachment -class EmailMagic: - def __init__(self, email): - self.email = email - - def _decode_header(self, header_value: str) -> str: +class EmailUtils: + @staticmethod + def extract_header(msg, header_name) -> str: """Декодировать заголовок письма.""" - if header_value is None: + header = msg.get(header_name, "") + if header is None: return "" + decoded = decode_header(header) + return str(make_header(decoded)) - decoded_parts = [] - for part, encoding in decode_header(header_value): - if isinstance(part, bytes): - if encoding: - try: - decoded_parts.append(part.decode(encoding)) - except: - decoded_parts.append(part.decode('utf-8', errors='ignore')) - else: - decoded_parts.append(part.decode('utf-8', errors='ignore')) - else: - decoded_parts.append(str(part)) + @staticmethod + def extract_email(text) -> str: + match = re.search(r'<([^<>]+)>', text) + if match: + return match.group(1) + return None - return ''.join(decoded_parts) - - def _extract_body(self, msg: email.message.Message) -> str: + @staticmethod + def extract_body(msg: email.message.Message) -> str: """Извлечь текст письма из любого типа содержимого, кроме вложений""" body = "" if msg.is_multipart(): @@ -65,13 +61,8 @@ class EmailMagic: return body - def __extract_email(self, text: str) -> str: - match = re.search(r'<([^<>]+)>', text) - if match: - return match.group(1) - return None - - def _extract_first_sender(self, body: str): + @staticmethod + def extract_first_sender(body: str): """Извлекает адреса отправителей из пересылаемого сообщения. Нужно для отладки""" # Ищем email внутри скобок после строки "Пересылаемое сообщение" pattern = r"Пересылаемое сообщение.*?\((.*?)\)" @@ -80,7 +71,8 @@ class EmailMagic: return match.group(1) return None - def _extract_attachments(self, msg: email.message.Message) -> List[EmailAttachment]: + @staticmethod + def _extract_attachments(msg: email.message.Message) -> List[EmailAttachment]: """Извлечь вложения из письма.""" attachments = [] @@ -91,9 +83,22 @@ class EmailMagic: filename = part.get_filename() if filename: # Декодируем имя файла - filename = self._decode_header(filename) + filename = decode_header(filename)[0] # Получаем содержимое content = part.get_payload(decode=True) if content: attachments.append(EmailAttachment(filename=filename, content=content)) return attachments + + @staticmethod + def extract_domain(email: str) -> str | None: + """Вернуть домен из email либо None, если формат странный.""" + if "@" not in email: + return None + # убираем пробелы по краям и берём часть после '@' + return email.strip().split("@", 1)[1] + + + + + diff --git a/src/mail_order_bot/email_processor/handlers/attachment_handler/attachment_handler.py b/src/mail_order_bot/email_processor/handlers/attachment_handler/attachment_handler.py new file mode 100644 index 0000000..0834a5c --- /dev/null +++ b/src/mail_order_bot/email_processor/handlers/attachment_handler/attachment_handler.py @@ -0,0 +1,11 @@ +import logging + +from mail_order_bot.email_processor.handlers.abstract_task import AbstractTask + +logger = logging.getLogger(__name__) + +class AttachmentHandler(AbstractTask): + def do(self) -> None: + + email = self.context["email"] + diff --git a/src/mail_order_bot/email_processor/processor.py b/src/mail_order_bot/email_processor/processor.py index a843925..3daab70 100644 --- a/src/mail_order_bot/email_processor/processor.py +++ b/src/mail_order_bot/email_processor/processor.py @@ -7,6 +7,7 @@ from pathlib import Path logger = logging.getLogger(__name__) from mail_order_bot.context import Context +from mail_order_bot.email_client.utils import EmailUtils from enum import Enum @@ -20,7 +21,7 @@ class RequestStatus(Enum): class EmailProcessor(Context): - def __init__(self, configs_path: Path): + def __init__(self, configs_path: str): super().__init__() self.configs_path = configs_path self.status = RequestStatus.NEW @@ -33,20 +34,30 @@ class EmailProcessor(Context): self.context["email"] = email # Определить клиента + email_body = EmailUtils.extract_body(email) + email_from = EmailUtils.extract_first_sender(email_body) + client = EmailUtils.extract_domain(email_from) + try: + # Определить конфиг для пайплайна + config = self._load_config(client) - # Определить конфиг для пайплайна - config = {} + # Запустить обработку пайплайна + for stage in config["pipeline"]: + handler_name = stage["handler"] + logger.info(f"Processing handler: {handler_name}") + task = globals()[handler_name](stage.get("config", None), self.context) + task.do() + + except FileNotFoundError: + logger.error(f"Конфиг для клиента {client} не найден") + #except Exception as e: + # logger.error(f"Произошла другая ошибка: {e}") - # Запустить обработку пайплайна - for stage in config["pipeline"]: - handler_name = stage["handler"] - logger.info(f"Processing handler: {handler_name}") - task = globals()[handler_name](stage.get("config", None), self.context) - task.do() def _load_config(self, client) -> Dict[str, Any]: """Загружает конфигурацию из YAML или JSON""" + path = os.path.join(self.configs_path, client + '.yml') with open(path, 'r', encoding='utf-8') as f: return yaml.safe_load(f) \ No newline at end of file diff --git a/src/mail_order_bot/main.py b/src/mail_order_bot/main.py index e6a3eef..e065e36 100644 --- a/src/mail_order_bot/main.py +++ b/src/mail_order_bot/main.py @@ -34,10 +34,12 @@ class MailOrderBot(ConfigManager): self.context.email_client = self.email_client # Обработчик писем - self.email_processor = EmailProcessor() + self.email_processor = EmailProcessor("./configs") + logger.warning("MailOrderBot инициализирован") + def execute(self): - logger.debug(f"Check emails for new orders") + logger.info(f"Проверяем почту на наличие новых писем") # Получить список айдишников письма unread_email_ids = self.email_client.get_emails_id(folder="spareparts") @@ -46,6 +48,7 @@ class MailOrderBot(ConfigManager): # Обработать каждое письмо по идентификатору for email_id in unread_email_ids: + logger.debug(f"==================================================") logger.debug(f"Обработка письма с идентификатором {email_id}") # Получить письмо по идентификатору и запустить его обработку email = self.email_client.get_email(email_id)