Spaces:
Sleeping
Sleeping
import os | |
import smtplib | |
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 | |
# 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): | |
"""Chuyển đổi Markdown thành HTML""" | |
md = MarkdownIt() | |
html_content = md.render(markdown_string) | |
# Định dạng ngày hiện tại | |
current_date = datetime.now().strftime("%d/%m/%Y") | |
# Tạo HTML hoàn chỉnh với CSS để định dạng đẹp | |
full_html = f""" | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Bản tin Thị trường Ngày {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">Ngày: {current_date}</div> | |
<h1 class="report-title">Bản tin Thị trường</h1> | |
<h2 class="report-subtitle">AI Financial Dashboard</h2> | |
</div> | |
<div class="report-body"> | |
{html_content} | |
</div> | |
<div class="footer"> | |
Bản tin được tạo tự động bởi AI Financial Dashboard. Thông tin chỉ mang tính chất tham khảo. | |
</div> | |
</body> | |
</html> | |
""" | |
return full_html | |
def _generate_pdf_from_markdown(markdown_string): | |
"""Tạo PDF từ Markdown sử dụng WeasyPrint""" | |
# Chuyển đổi markdown sang HTML | |
html_content = _markdown_to_html(markdown_string) | |
# Tạo file HTML tạm thời | |
with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as temp_html: | |
temp_html_path = temp_html.name | |
temp_html.write(html_content.encode('utf-8')) | |
# Tạo file PDF từ HTML | |
try: | |
# Tạo tên file PDF tạm thời | |
with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as temp_pdf: | |
temp_pdf_path = temp_pdf.name | |
# Tạo PDF | |
weasyprint.HTML(filename=temp_html_path).write_pdf(temp_pdf_path) | |
# Đọc nội dung PDF | |
with open(temp_pdf_path, 'rb') as f: | |
pdf_data = f.read() | |
# Xóa các file tạm | |
os.unlink(temp_html_path) | |
os.unlink(temp_pdf_path) | |
return pdf_data | |
except Exception as e: | |
# Xử lý lỗi và đảm bảo xóa file tạm | |
if os.path.exists(temp_html_path): | |
os.unlink(temp_html_path) | |
raise e | |
def send_report_via_email(report_markdown, recipient_email): | |
"""Gửi báo cáo thị trường qua email""" | |
try: | |
# Tạo PDF từ markdown | |
pdf_data = _generate_pdf_from_markdown(report_markdown) | |
# Tạo message | |
message = MIMEMultipart() | |
message["From"] = SENDER_EMAIL | |
message["To"] = recipient_email | |
message["Subject"] = f"AI Financial Dashboard - Bản tin Thị trường {datetime.now().strftime('%d/%m/%Y')}" | |
# Thêm phần nội dung với encoding UTF-8 | |
body = """ | |
Kính gửi Quý khách, | |
Đính kèm là bản tin thị trường tài chính hôm nay, được tổng hợp tự động bởi AI Financial Dashboard. | |
Trân trọng, | |
AI Financial Dashboard Team | |
""" | |
message.attach(MIMEText(body, "plain", "utf-8")) | |
# Đính kèm file PDF | |
attachment = MIMEApplication(pdf_data, _subtype="pdf") | |
attachment.add_header( | |
"Content-Disposition", "attachment", | |
filename=f"Market_Report_{datetime.now().strftime('%Y%m%d')}.pdf" | |
) | |
message.attach(attachment) | |
# Kết nối đến server SMTP và gửi email | |
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server: | |
server.login(SENDER_EMAIL, SENDER_APP_PASSWORD) | |
server.send_message(message) | |
return True, "Email đã được gửi thành công!" | |
except Exception as e: | |
return False, f"Lỗi khi gửi email: {str(e)}" | |
if __name__ == "__main__": | |
report_markdown = """ | |
# Bản tin Thị trường | |
## Ngày 29/07/2025 | |
- Cổ phiếu A tăng 10% | |
- Cổ phiếu B giảm 5% | |
""" | |
send_report_via_email(report_markdown, "[email protected]") |