tosanoob commited on
Commit
e00c0e4
·
1 Parent(s): 44f095f

feat: update email send using sendgrid

Browse files
Files changed (4) hide show
  1. .gitignore +2 -1
  2. modules/email_sender.py +51 -35
  3. requirements.txt +4 -0
  4. test.py +0 -235
.gitignore CHANGED
@@ -1,4 +1,5 @@
1
  .env
2
  PLAN.md
3
  PLAN_UPDATE.md
4
- __pycache__/
 
 
1
  .env
2
  PLAN.md
3
  PLAN_UPDATE.md
4
+ __pycache__/
5
+ test.py
modules/email_sender.py CHANGED
@@ -1,5 +1,4 @@
1
  import os
2
- import smtplib
3
  import tempfile
4
  from email.mime.multipart import MIMEMultipart
5
  from email.mime.text import MIMEText
@@ -9,6 +8,12 @@ from markdown_it import MarkdownIt
9
  from datetime import datetime
10
  import weasyprint
11
 
 
 
 
 
 
 
12
  # Tải biến môi trường
13
  load_dotenv()
14
 
@@ -184,43 +189,54 @@ def _generate_pdf_from_markdown(markdown_string):
184
  os.unlink(temp_html_path)
185
  raise e
186
 
187
- def send_report_via_email(report_markdown, recipient_email):
188
- """Send market report via email"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  try:
190
- # Generate PDF from markdown
191
  pdf_data = _generate_pdf_from_markdown(report_markdown)
192
 
193
- # Create message
194
- message = MIMEMultipart()
195
- message["From"] = SENDER_EMAIL
196
- message["To"] = recipient_email
197
- message["Subject"] = f"AI Financial Dashboard - Daily Market Report {datetime.now().strftime('%d/%m/%Y')}"
198
-
199
- # Add content with UTF-8 encoding
200
- body = """
201
- Dear User,
202
-
203
- Attached is today's financial market report, automatically generated by AI Financial Dashboard.
204
-
205
- Best regards,
206
- AI Financial Dashboard Team
207
- """
208
- message.attach(MIMEText(body, "plain", "utf-8"))
209
-
210
- # Attach PDF file
211
- attachment = MIMEApplication(pdf_data, _subtype="pdf")
212
- attachment.add_header(
213
- "Content-Disposition", "attachment",
214
- filename=f"Market_Report_{datetime.now().strftime('%Y%m%d')}.pdf"
215
  )
216
- message.attach(attachment)
217
-
218
- # Connect to SMTP server and send email
219
- with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
220
- server.login(SENDER_EMAIL, SENDER_APP_PASSWORD)
221
- server.send_message(message)
222
 
223
- return True, "Email sent successfully!"
224
-
 
 
 
 
 
 
225
  except Exception as e:
226
- return False, f"Error sending email: {str(e)}"
 
 
 
 
1
  import os
 
2
  import tempfile
3
  from email.mime.multipart import MIMEMultipart
4
  from email.mime.text import MIMEText
 
8
  from datetime import datetime
9
  import weasyprint
10
 
11
+ import base64
12
+ from sendgrid import SendGridAPIClient
13
+ from sendgrid.helpers.mail import (
14
+ Mail, Attachment, FileContent, FileName,
15
+ FileType, Disposition)
16
+
17
  # Tải biến môi trường
18
  load_dotenv()
19
 
 
189
  os.unlink(temp_html_path)
190
  raise e
191
 
192
+ def send_report_via_email(report_markdown: str, recipient_email: str):
193
+ """
194
+ Tạo báo cáo PDF từ markdown và gửi qua email bằng API của SendGrid.
195
+ """
196
+ sendgrid_api_key = os.getenv("SENDGRID_API_KEY")
197
+ sender_email = os.getenv("SENDER_EMAIL")
198
+
199
+ if not sendgrid_api_key or not sender_email:
200
+ raise ValueError("Thiếu thông tin SENDGRID_API_KEY hoặc SENDER_EMAIL trong biến môi trường.")
201
+
202
+ # Tạo đối tượng Mail của SendGrid
203
+ message = Mail(
204
+ from_email=sender_email,
205
+ to_emails=recipient_email,
206
+ subject=f"Bản tin Thị trường Tài chính ngày {datetime.now().strftime('%d-%m-%Y')}",
207
+ 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>"
208
+ )
209
+
210
  try:
211
+ # Tạo file PDF trong bộ nhớ
212
  pdf_data = _generate_pdf_from_markdown(report_markdown)
213
 
214
+ # hóa base64 cho file đính kèm
215
+ encoded_file = base64.b64encode(pdf_data).decode()
216
+
217
+ # Tạo đối tượng Attachment
218
+ attached_file = Attachment(
219
+ FileContent(encoded_file),
220
+ FileName(f'Ban_tin_Thi_truong_{datetime.now().strftime("%Y%m%d")}.pdf'),
221
+ FileType('application/pdf'),
222
+ Disposition('attachment')
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  )
224
+ message.attachment = attached_file
225
+
226
+ # Gửi email qua API
227
+ sg = SendGridAPIClient(sendgrid_api_key)
228
+ response = sg.send(message)
 
229
 
230
+ # Kiểm tra response từ SendGrid
231
+ if 200 <= response.status_code < 300:
232
+ print(f"Email đã được gửi thành công đến {recipient_email} qua SendGrid.")
233
+ return True, "Email đã được gửi thành công!"
234
+ else:
235
+ print(f"Lỗi khi gửi email qua SendGrid: {response.body}")
236
+ return False, f"Không thể gửi email. Mã lỗi: {response.status_code}"
237
+
238
  except Exception as e:
239
+ import traceback
240
+ traceback.print_exc()
241
+ print(f"Lỗi khi gửi email: {e}")
242
+ return False, f"Đã xảy ra lỗi: {e}"
requirements.txt CHANGED
@@ -19,6 +19,7 @@ decorator==5.2.1
19
  defusedxml==0.7.1
20
  distro==1.9.0
21
  dnspython==2.7.0
 
22
  email_validator==2.2.0
23
  executing==2.2.0
24
  fonttools==4.59.0
@@ -85,12 +86,14 @@ pyphen==0.17.2
85
  PySocks==1.7.1
86
  python-dateutil==2.9.0.post0
87
  python-dotenv==1.1.1
 
88
  pytz==2025.2
89
  referencing==0.36.2
90
  requests==2.32.4
91
  rpds-py==0.26.0
92
  rsa==4.9.1
93
  selenium==4.34.2
 
94
  setuptools==78.1.1
95
  six==1.17.0
96
  smmap==5.0.2
@@ -122,6 +125,7 @@ weasyprint==66.0
122
  webencodings==0.5.1
123
  websocket-client==1.8.0
124
  websockets==15.0.1
 
125
  wheel==0.45.1
126
  widgetsnbextension==4.0.14
127
  wsproto==1.2.0
 
19
  defusedxml==0.7.1
20
  distro==1.9.0
21
  dnspython==2.7.0
22
+ ecdsa==0.19.1
23
  email_validator==2.2.0
24
  executing==2.2.0
25
  fonttools==4.59.0
 
86
  PySocks==1.7.1
87
  python-dateutil==2.9.0.post0
88
  python-dotenv==1.1.1
89
+ python-http-client==3.3.7
90
  pytz==2025.2
91
  referencing==0.36.2
92
  requests==2.32.4
93
  rpds-py==0.26.0
94
  rsa==4.9.1
95
  selenium==4.34.2
96
+ sendgrid==6.12.4
97
  setuptools==78.1.1
98
  six==1.17.0
99
  smmap==5.0.2
 
125
  webencodings==0.5.1
126
  websocket-client==1.8.0
127
  websockets==15.0.1
128
+ Werkzeug==3.1.3
129
  wheel==0.45.1
130
  widgetsnbextension==4.0.14
131
  wsproto==1.2.0
test.py DELETED
@@ -1,235 +0,0 @@
1
- import os
2
- import smtplib
3
- import tempfile
4
- from email.mime.multipart import MIMEMultipart
5
- from email.mime.text import MIMEText
6
- from email.mime.application import MIMEApplication
7
- from dotenv import load_dotenv
8
- from markdown_it import MarkdownIt
9
- from datetime import datetime
10
- import weasyprint
11
-
12
- # Tải biến môi trường
13
- load_dotenv()
14
-
15
- # Thông tin email từ biến môi trường
16
- SENDER_EMAIL = os.getenv("SENDER_EMAIL")
17
- SENDER_APP_PASSWORD = os.getenv("SENDER_APP_PASSWORD")
18
-
19
- def _markdown_to_html(markdown_string):
20
- """Chuyển đổi Markdown thành HTML"""
21
- md = MarkdownIt()
22
- html_content = md.render(markdown_string)
23
-
24
- # Định dạng ngày hiện tại
25
- current_date = datetime.now().strftime("%d/%m/%Y")
26
-
27
- # Tạo HTML hoàn chỉnh với CSS để định dạng đẹp
28
- full_html = f"""
29
- <!DOCTYPE html>
30
- <html>
31
- <head>
32
- <meta charset="UTF-8">
33
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
34
- <title>Bản tin Thị trường Ngày {current_date}</title>
35
- <style>
36
- @page {{
37
- size: A4;
38
- margin: 2cm;
39
- }}
40
- body {{
41
- font-family: Arial, Helvetica, sans-serif;
42
- line-height: 1.5;
43
- color: #333;
44
- max-width: 800px;
45
- margin: 0 auto;
46
- }}
47
- .report-header {{
48
- text-align: center;
49
- margin-bottom: 30px;
50
- }}
51
- .report-date {{
52
- font-style: italic;
53
- color: #666;
54
- margin-bottom: 10px;
55
- }}
56
- .report-title {{
57
- font-size: 24pt;
58
- margin-bottom: 5px;
59
- color: #2c3e50;
60
- }}
61
- .report-subtitle {{
62
- font-size: 14pt;
63
- color: #7f8c8d;
64
- margin-top: 0;
65
- }}
66
- .report-body {{
67
- text-align: justify;
68
- }}
69
- h1, h2, h3, h4, h5, h6 {{
70
- color: #2c3e50;
71
- margin-top: 20px;
72
- }}
73
- h1 {{ font-size: 20pt; }}
74
- h2 {{ font-size: 18pt; }}
75
- h3 {{ font-size: 16pt; }}
76
- h4 {{ font-size: 14pt; }}
77
- h5 {{ font-size: 12pt; }}
78
- h6 {{ font-size: 10pt; }}
79
-
80
- p {{
81
- margin-bottom: 10px;
82
- }}
83
-
84
- a {{
85
- color: #3498db;
86
- text-decoration: none;
87
- }}
88
-
89
- a:hover {{
90
- text-decoration: underline;
91
- }}
92
-
93
- ul, ol {{
94
- margin: 10px 0 10px 20px;
95
- }}
96
-
97
- li {{
98
- margin-bottom: 5px;
99
- }}
100
-
101
- blockquote {{
102
- border-left: 4px solid #eee;
103
- padding-left: 10px;
104
- margin-left: 0;
105
- color: #777;
106
- }}
107
-
108
- .section {{
109
- margin-bottom: 30px;
110
- }}
111
-
112
- .footer {{
113
- text-align: center;
114
- margin-top: 40px;
115
- padding-top: 20px;
116
- font-size: 12px;
117
- color: #777;
118
- border-top: 1px solid #eee;
119
- }}
120
-
121
- /* Custom styling for bullet points */
122
- ul {{
123
- list-style-type: disc;
124
- }}
125
- ul ul {{
126
- list-style-type: circle;
127
- }}
128
- ul ul ul {{
129
- list-style-type: square;
130
- }}
131
- </style>
132
- </head>
133
- <body>
134
- <div class="report-header">
135
- <div class="report-date">Ngày: {current_date}</div>
136
- <h1 class="report-title">Bản tin Thị trường</h1>
137
- <h2 class="report-subtitle">AI Financial Dashboard</h2>
138
- </div>
139
-
140
- <div class="report-body">
141
- {html_content}
142
- </div>
143
-
144
- <div class="footer">
145
- 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.
146
- </div>
147
- </body>
148
- </html>
149
- """
150
-
151
- return full_html
152
-
153
- def _generate_pdf_from_markdown(markdown_string):
154
- """Tạo PDF từ Markdown sử dụng WeasyPrint"""
155
- # Chuyển đổi markdown sang HTML
156
- html_content = _markdown_to_html(markdown_string)
157
-
158
- # Tạo file HTML tạm thời
159
- with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as temp_html:
160
- temp_html_path = temp_html.name
161
- temp_html.write(html_content.encode('utf-8'))
162
-
163
- # Tạo file PDF từ HTML
164
- try:
165
- # Tạo tên file PDF tạm thời
166
- with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as temp_pdf:
167
- temp_pdf_path = temp_pdf.name
168
-
169
- # Tạo PDF
170
- weasyprint.HTML(filename=temp_html_path).write_pdf(temp_pdf_path)
171
-
172
- # Đọc nội dung PDF
173
- with open(temp_pdf_path, 'rb') as f:
174
- pdf_data = f.read()
175
-
176
- # Xóa các file tạm
177
- os.unlink(temp_html_path)
178
- os.unlink(temp_pdf_path)
179
-
180
- return pdf_data
181
- except Exception as e:
182
- # Xử lý lỗi và đảm bảo xóa file tạm
183
- if os.path.exists(temp_html_path):
184
- os.unlink(temp_html_path)
185
- raise e
186
-
187
- def send_report_via_email(report_markdown, recipient_email):
188
- """Gửi báo cáo thị trường qua email"""
189
- try:
190
- # Tạo PDF từ markdown
191
- pdf_data = _generate_pdf_from_markdown(report_markdown)
192
-
193
- # Tạo message
194
- message = MIMEMultipart()
195
- message["From"] = SENDER_EMAIL
196
- message["To"] = recipient_email
197
- message["Subject"] = f"AI Financial Dashboard - Bản tin Thị trường {datetime.now().strftime('%d/%m/%Y')}"
198
-
199
- # Thêm phần nội dung với encoding UTF-8
200
- body = """
201
- Kính gửi Quý khách,
202
-
203
- Đí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.
204
-
205
- Trân trọng,
206
- AI Financial Dashboard Team
207
- """
208
- message.attach(MIMEText(body, "plain", "utf-8"))
209
-
210
- # Đính kèm file PDF
211
- attachment = MIMEApplication(pdf_data, _subtype="pdf")
212
- attachment.add_header(
213
- "Content-Disposition", "attachment",
214
- filename=f"Market_Report_{datetime.now().strftime('%Y%m%d')}.pdf"
215
- )
216
- message.attach(attachment)
217
-
218
- # Kết nối đến server SMTP và gửi email
219
- with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
220
- server.login(SENDER_EMAIL, SENDER_APP_PASSWORD)
221
- server.send_message(message)
222
-
223
- return True, "Email đã được gửi thành công!"
224
-
225
- except Exception as e:
226
- return False, f"Lỗi khi gửi email: {str(e)}"
227
-
228
- if __name__ == "__main__":
229
- report_markdown = """
230
- # Bản tin Thị trường
231
- ## Ngày 29/07/2025
232
- - Cổ phiếu A tăng 10%
233
- - Cổ phiếu B giảm 5%
234
- """
235
- send_report_via_email(report_markdown, "[email protected]")