finance-bot / modules /email_sender.py
tosanoob's picture
feat: update email send using sendgrid
e00c0e4
import os
import tempfile
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from dotenv import load_dotenv
from markdown_it import MarkdownIt
from datetime import datetime
import weasyprint
import base64
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import (
Mail, Attachment, FileContent, FileName,
FileType, Disposition)
# Tải biến môi trường
load_dotenv()
# Thông tin email từ biến môi trường
SENDER_EMAIL = os.getenv("SENDER_EMAIL")
SENDER_APP_PASSWORD = os.getenv("SENDER_APP_PASSWORD")
def _markdown_to_html(markdown_string):
"""Convert Markdown to HTML"""
md = MarkdownIt()
html_content = md.render(markdown_string)
# Format current date
current_date = datetime.now().strftime("%d/%m/%Y")
# Create complete HTML with CSS for nice formatting
full_html = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Daily Market Report - {current_date}</title>
<style>
@page {{
size: A4;
margin: 2cm;
}}
body {{
font-family: Arial, Helvetica, sans-serif;
line-height: 1.5;
color: #333;
max-width: 800px;
margin: 0 auto;
}}
.report-header {{
text-align: center;
margin-bottom: 30px;
}}
.report-date {{
font-style: italic;
color: #666;
margin-bottom: 10px;
}}
.report-title {{
font-size: 24pt;
margin-bottom: 5px;
color: #2c3e50;
}}
.report-subtitle {{
font-size: 14pt;
color: #7f8c8d;
margin-top: 0;
}}
.report-body {{
text-align: justify;
}}
h1, h2, h3, h4, h5, h6 {{
color: #2c3e50;
margin-top: 20px;
}}
h1 {{ font-size: 20pt; }}
h2 {{ font-size: 18pt; }}
h3 {{ font-size: 16pt; }}
h4 {{ font-size: 14pt; }}
h5 {{ font-size: 12pt; }}
h6 {{ font-size: 10pt; }}
p {{
margin-bottom: 10px;
}}
a {{
color: #3498db;
text-decoration: none;
}}
a:hover {{
text-decoration: underline;
}}
ul, ol {{
margin: 10px 0 10px 20px;
}}
li {{
margin-bottom: 5px;
}}
blockquote {{
border-left: 4px solid #eee;
padding-left: 10px;
margin-left: 0;
color: #777;
}}
.section {{
margin-bottom: 30px;
}}
.footer {{
text-align: center;
margin-top: 40px;
padding-top: 20px;
font-size: 12px;
color: #777;
border-top: 1px solid #eee;
}}
/* Custom styling for bullet points */
ul {{
list-style-type: disc;
}}
ul ul {{
list-style-type: circle;
}}
ul ul ul {{
list-style-type: square;
}}
</style>
</head>
<body>
<div class="report-header">
<div class="report-date">Date: {current_date}</div>
<h1 class="report-title">Daily Market Report</h1>
<h2 class="report-subtitle">AI Financial Dashboard</h2>
</div>
<div class="report-body">
{html_content}
</div>
<div class="footer">
This report was automatically generated by AI Financial Dashboard. Information is for reference only.
</div>
</body>
</html>
"""
return full_html
def _generate_pdf_from_markdown(markdown_string):
"""Generate PDF from Markdown using WeasyPrint"""
# Convert markdown to HTML
html_content = _markdown_to_html(markdown_string)
# Create temporary HTML file
with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as temp_html:
temp_html_path = temp_html.name
temp_html.write(html_content.encode('utf-8'))
# Create PDF from HTML
try:
# Create temporary PDF filename
with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as temp_pdf:
temp_pdf_path = temp_pdf.name
# Generate PDF
weasyprint.HTML(filename=temp_html_path).write_pdf(temp_pdf_path)
# Read PDF content
with open(temp_pdf_path, 'rb') as f:
pdf_data = f.read()
# Delete temporary files
os.unlink(temp_html_path)
os.unlink(temp_pdf_path)
return pdf_data
except Exception as e:
# Handle errors and ensure temporary files are deleted
if os.path.exists(temp_html_path):
os.unlink(temp_html_path)
raise e
def send_report_via_email(report_markdown: str, recipient_email: str):
"""
Tạo báo cáo PDF từ markdown và gửi qua email bằng API của SendGrid.
"""
sendgrid_api_key = os.getenv("SENDGRID_API_KEY")
sender_email = os.getenv("SENDER_EMAIL")
if not sendgrid_api_key or not sender_email:
raise ValueError("Thiếu thông tin SENDGRID_API_KEY hoặc SENDER_EMAIL trong biến môi trường.")
# Tạo đối tượng Mail của SendGrid
message = Mail(
from_email=sender_email,
to_emails=recipient_email,
subject=f"Bản tin Thị trường Tài chính ngày {datetime.now().strftime('%d-%m-%Y')}",
html_content="<p>Xin chào,</p><p>Đây là bản tin thị trường được tạo tự động bởi AI Financial Dashboard.</p><p>Vui lòng xem chi tiết trong file PDF đính kèm.</p><p>Trân trọng,</p>"
)
try:
# Tạo file PDF trong bộ nhớ
pdf_data = _generate_pdf_from_markdown(report_markdown)
# Mã hóa base64 cho file đính kèm
encoded_file = base64.b64encode(pdf_data).decode()
# Tạo đối tượng Attachment
attached_file = Attachment(
FileContent(encoded_file),
FileName(f'Ban_tin_Thi_truong_{datetime.now().strftime("%Y%m%d")}.pdf'),
FileType('application/pdf'),
Disposition('attachment')
)
message.attachment = attached_file
# Gửi email qua API
sg = SendGridAPIClient(sendgrid_api_key)
response = sg.send(message)
# Kiểm tra response từ SendGrid
if 200 <= response.status_code < 300:
print(f"Email đã được gửi thành công đến {recipient_email} qua SendGrid.")
return True, "Email đã được gửi thành công!"
else:
print(f"Lỗi khi gửi email qua SendGrid: {response.body}")
return False, f"Không thể gửi email. Mã lỗi: {response.status_code}"
except Exception as e:
import traceback
traceback.print_exc()
print(f"Lỗi khi gửi email: {e}")
return False, f"Đã xảy ra lỗi: {e}"