114 lines
3.3 KiB
Python
114 lines
3.3 KiB
Python
import smtplib
|
|
from email.mime.multipart import MIMEMultipart
|
|
from email.mime.text import MIMEText
|
|
from celery import Celery
|
|
from config 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'amqps://admin:{RABBITMQ_PASS}@{RABBITMQ_HOST}/email', backend=f'redis://{REDIS_HOST}:{6379 if PROD else 30003}/0', 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) |