-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Upgrade email functionality in Oppia to have email attachments #21380
Changes from 1 commit
5d63a5f
f2b3b0b
09026c5
47c7ac8
94ee84c
4e4e0eb
a05759b
3c0d86c
8f61992
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,8 @@ | |
|
||
import base64 | ||
import urllib | ||
import requests | ||
import logging | ||
|
||
from core import feconf | ||
from core import utils | ||
|
@@ -43,7 +45,8 @@ def send_email_to_recipients( | |
bcc: Optional[List[str]] = None, | ||
reply_to: Optional[str] = None, | ||
recipient_variables: Optional[ | ||
Dict[str, Dict[str, Union[str, float]]]] = None | ||
Dict[str, Dict[str, Union[str, float]]]] = None, | ||
attachments: Optional[List[str]] = None | ||
) -> bool: | ||
"""Send POST HTTP request to mailgun api. This method is adopted from | ||
the requests library's post method. | ||
|
@@ -74,6 +77,8 @@ def send_email_to_recipients( | |
More info about this format at: | ||
https://documentation.mailgun.com/en/ | ||
latest/user_manual.html#batch-sending. | ||
attachments: list(str)|None. Optional argument. List of file paths | ||
for attachments. | ||
|
||
Raises: | ||
Exception. The mailgun api key is not stored in | ||
|
@@ -95,16 +100,17 @@ def send_email_to_recipients( | |
# To send bulk emails we pass list of recipients in 'to' paarameter of | ||
# post data. Maximum limit of recipients per request is 1000. | ||
# For more detail check following link: | ||
# https://documentation.mailgun.com/user_manual.html#batch-sending | ||
# https://documentation.mailgun.com/docs/mailgun/user-manual/ | ||
# sending-messages/#batch-sending | ||
recipient_email_lists = [ | ||
recipient_emails[i:i + 1000] | ||
for i in range(0, len(recipient_emails), 1000)] | ||
for email_list in recipient_email_lists: | ||
data = { | ||
'from': sender_email, | ||
'subject': subject.encode('utf-8'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to encode manually because the |
||
'text': plaintext_body.encode('utf-8'), | ||
'html': html_body.encode('utf-8'), | ||
'subject': subject, | ||
'text': plaintext_body, | ||
'html': html_body, | ||
'to': email_list[0] if len(email_list) == 1 else email_list | ||
} | ||
|
||
|
@@ -118,28 +124,35 @@ def send_email_to_recipients( | |
# email to each recipient (This is intended to be a workaround for | ||
# sending individual emails). | ||
data['recipient_variables'] = recipient_variables or {} | ||
|
||
# The b64encode accepts and returns bytes, so we first need to encode | ||
# the MAILGUN_API_KEY to bytes, then decode the returned bytes back | ||
# to string. | ||
base64_mailgun_api_key = base64.b64encode( | ||
b'api:%b' % mailgun_api_key.encode('utf-8') | ||
).strip().decode('utf-8') | ||
auth_str = 'Basic %s' % base64_mailgun_api_key | ||
header = {'Authorization': auth_str} | ||
server = 'https://api.mailgun.net/v3/%s/messages' % ( | ||
feconf.MAILGUN_DOMAIN_NAME | ||
) | ||
# The 'ascii' is used here, because only ASCII char are allowed in url, | ||
# also the docs recommend this approach: | ||
# https://docs.python.org/3.7/library/urllib.request.html#urllib-examples | ||
encoded_url = urllib.parse.urlencode(data).encode('ascii') | ||
req = urllib.request.Request(server, encoded_url, header) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated the implementation from |
||
resp = utils.url_open(req) | ||
# The function url_open returns a file_like object that can be queried | ||
# for the status code of the url query. If it is not 200, the mail query | ||
# failed so we return False (this function did not complete | ||
# successfully). | ||
if resp.getcode() != 200: | ||
|
||
# Adding attachments to the email. | ||
files = [( | ||
"attachment", | ||
(attachment["filename"], open(attachment["path"], "rb"))) | ||
for attachment in attachments | ||
] if attachments else None | ||
|
||
try: | ||
response = requests.post( | ||
server, | ||
auth=("api", mailgun_api_key), | ||
data=data, | ||
files=files | ||
) | ||
# Close file objects after the request. | ||
if files: | ||
for _, (_, file_obj) in files: | ||
file_obj.close() | ||
|
||
if response.status_code != 200: | ||
logging.error( | ||
"Failed to send email: %s - %s." | ||
% (response.status_code, response.text)) | ||
return False | ||
except requests.RequestException as e: | ||
logging.error("Failed to send email: %s." %e) | ||
return False | ||
return True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated documentation link.