#ignore import os def load_env(file_path): """Загружает переменные окружения из файла .env с поддержкой булевых значений.""" with open(file_path) as f: for line in f: line = line.strip() if not line or line.startswith('#'): continue try: key, value = line.split('=', 1) key = key.strip() value = value.strip('"\' ') # Преобразование строковых значений в булевые if value.lower() in ['true', '1', 'yes']: value = True elif value.lower() in ['false', '0', 'no']: value = False # Проверка, что ключ не пустой if key: os.environ[key] = str(value) # Храним как строку except ValueError: print(f"Ошибка: неверный формат в строке '{line}'") try: load_env('env') except: load_env('env_example') import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from celery import Celery from config import * from db import * from templates import templates import logging from typing import Optional import asyncio from email.utils import formatdate, make_msgid def build_unsubscribe_url(email: str, token: str, base_url: str) -> str: return f"{base_url}/api/v1/email/unsubscribe?token={token}&email={email}" async def create_email_message( to_email: str, subject: str, html_content: str, user_id: int = None, ): msg = MIMEMultipart("alternative") msg['From'] = f"{FROM_NAME} <{FROM_EMAIL}>" msg['To'] = to_email msg['Subject'] = subject msg['Message-ID'] = make_msgid(domain=FROM_EMAIL.split('@')[1]) msg['Date'] = formatdate(localtime=True) msg['X-Priority'] = "3" msg['Importance'] = "normal" msg['X-Auto-Response-Suppress'] = "All" # token = await create_unsubscribe_token(to_email, user_id) unsubscribe_url = build_unsubscribe_url(to_email, None, FULL_DOMAIN) if to_email != FROM_EMAIL: msg['List-Unsubscribe'] = f"<{unsubscribe_url}>" msg['List-Unsubscribe-Post'] = "List-Unsubscribe=One-Click" msg.attach(MIMEText(html_content, 'html')) return msg def connect(email_server, email_port): server = smtplib.SMTP(email_server, email_port) server.starttls(context=ctx) return server def send_email( to_email: str, html_content: str, subject: str, user_id: Optional[str] = None, ) -> bool: msg =asyncio.run(create_email_message( to_email=to_email, subject=subject, html_content=html_content, user_id=user_id, )) try: with connect(EMAIL_SMTP_SERVER, EMAIL_SMTP_PORT) as server: server.login(FROM_EMAIL, EMAIL_PASS) server.send_message(msg) # server.close() logging.info(f"Email sent successfully to: {to_email} | Subject: {subject}") return True except Exception as e: logging.error( f"Error sending email to: {to_email}, " f"Subject: {subject}, Error: {str(e)}" ) return False celery_app_email = Celery('emails', broker=f'{redis_uri}2', backend=f'{redis_uri}2', task_track_started=True) @celery_app_email.task( bind=True, rate_limit='30/m', name='email_task.send_email', retry_backoff=True, retry_backoff_max=600, retry_jitter=True, max_retries=3, soft_time_limit=30, time_limit=45, ) def notify(self, to_email: str, data: dict, subject: str = EMAIL_SUBJECT, template: str = 'main.html'): logging.info(f"Отправка email на {to_email} (попытка {self.request.retries + 1})") try: html_content = templates.get_template(f"email/{template}").render({ "subject": subject, "data": data }) ok = send_email(to_email, html_content, subject=subject) if ok: return ok else: raise self.retry(exc=exc) except Exception as exc: logging.warning( f"Ошибка при отправке email на {to_email} (попытка {self.request.retries + 1}): {exc}" ) raise self.retry(exc=exc)