avtopost.kultmost.ru/email_task.py
2026-08-17 03:33:47 -04:00

148 lines
4.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# `email_task.py` generate from `email_task.ipynb` by `build.ipynb`
#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)