problems in sending email using sendgrid python django? - python

I am trying to send email using sendgrid using python django. For some reason, the email is being delivered but without the html content in it. What could I be doing wrong ?
def sendemail(sender,recipient,subject,content,url):
sg = sendgrid.SendGridAPIClient(apikey="")
from_email = Email(sender)
to_email = Email(recipient)
subject = subject
objects = {
"message":content,
"domain":url
}
html_content = get_template('sendemail.html').render(objects)
print ('htmlcontent',html_content)
content = Content("text/html",html_content)
mail = Mail(from_email, subject, to_email, content)
response = sg.client.mail.send.post(request_body=mail.get())
print(response.status_code)
print(response.body)
print(response.headers)

I think that you have to set text/plain content as well.
You can try something like this :
mail = Mail(from_email, subject, to_email)
mail.add_content(Content("text/plain", "plain message"))
mail.add_content(Content("text/html", html_content ))

Related

How to send python email.message.EmailMessage with Microsoft Graph API

Our web application sends nicely formatted messages (including some embedded images) through python's smtplib. The messages are generated with python's email.message.EmailMessage class, as shown in the code below.
At the start of October, Microsoft deprecated support for Basic Auth in their Office365 Email accounts. We had been using Basic Auth and now needed to find a new solution. After struggling with getting OAuth2 working for some time, we decided to refactor and use the Microsoft Graph API instead.
Oddly, even though most of the examples on the Graph API site includes multi-lingual (HTTP / C# / Javascript / PHP etc) examples, the one for sending an email with a MIME format (Examlpe 4) only has an HTTP example.
We would like to know if it's possible to send the email we had built using python.email.EmailMessage using the Graph API, and if yes, how to do it.
Below is some example code, showing what we did before and what we're trying to get right now.
when we run the code, we get the error
'{"error":{"code":"RequestBodyRead","message":"Requested value \'text/plain\' was not found."}}'
import smtplib
from email.utils import formatdate
from email.message import EmailMessage
import requests
server = 'smtp.office365.com' # for exampler
port = 587 # for example
from_mail = 'levrai#ninjane.er'
to_mail = 'desti#nati.on'
subject = 'Demo sending the old way!'
password = 'not_so_Secur3!'
message_parts = ['Hi sir', 'This is a demo message.', 'It could help others to help me, and possbily others too.']
# the below function builds up the nice message based on an html template
text_msg, html_msg, cids, locs = doc_mail_from_template(message_parts)
msg = EmailMessage()
msg.set_content(text_message)
msg.add_alternative(html_msg, subtype='html')
msg['From'] = from_mail
msg['To'] = to_mail
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
# now embed images to the email
for loc, cid in zip(locs, cids):
with open(loc, 'rb') as img:
maintype, subtype = guess_type(img.name)[0].split('/') # know the Content-Type of the image
msg.get_payload()[1].add_related(img.read(), maintype=maintype, subtype=subtype, cid=cid) # attach it
if date_now < '2022-10-01': # before, we could do this
with smtplib.SMTP(server, port) as smtp:
smtp.starttls(context=context)
smtp.login(from_mail, password)
smtp.sendmail(from_mail, [to_mail, ], msg.as_string())
else: # now we must do this
client_id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx8c5'
client_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxb96'
tenant_id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx973'
userId = "levrai#ninjane.er"
authority = f"https://login.microsoftonline.com/{tenant_id}"
scopes = ["https://graph.microsoft.com/.default"]
app = msal.ConfidentialClientApplication(client_id=client_id, client_credential=client_secret, authority=authority)
result = app.acquire_token_silent(scopes, account=None)
if not result:
result = app.acquire_token_for_client(scopes=scopes)
# setup message:
email_msg = {'Message': {'Subject': subject,
'Body': {
'ContentType': 'text/plain', 'Content': e_message.as_string() }, # what do i put here?
'ToRecipients': [{'EmailAddress': {'Address': to_mail}}]
},
'SaveToSentItems': 'true'}
endpoint = f'https://graph.microsoft.com/v1.0/users/{from_user}/sendMail'
r = requests.post(endpoint, json=email_msg,
headers={'Authorization': 'Bearer ' + result['access_token'], "Content-Type": "application/json"})
You seem to be mixing in your example sending as a JSON message using the Graph with sending as a MIME message. Sending as MIME has a few restrictions eg your email has to be under 4 mb(or because of MIME bloat 2.5-3 mb). You need to make sure you encode the message correctly but the following works okay for me in phython built from EmailMessage. (the parser on the Microsoft side can be a little restrictive so start simple with your formatting and see if that works then increase the complexity of the message).
eg
from email.message import EmailMessage
import base64
import sys
import json
import logging
import msal
import requests
config = {
"authority": "https://login.microsoftonline.com/eb8db77e-65e0-4fc3-b967-.......",
"client_id": "18bb3888-dad0-4997-96b1-.........",
"scope": ["https://graph.microsoft.com/.default"],
"secret": ".............",
"tenant-id": "eb8db77e-65e0-4fc3-b967-........"
}
app = msal.ConfidentialClientApplication(config['client_id'], authority=config['authority'],
client_credential=config['secret'])
result = app.acquire_token_silent(config["scope"], account=None)
if not result:
logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
result = app.acquire_token_for_client(scopes=config["scope"])
sender_email = "gscales#gbbbbb.onmicrosoft.com"
receiver_email = "gscales#ccccc.onmicrosoft.com"
def create_message(sender, to, subject, message_text):
message = EmailMessage()
message.set_content(message_text)
message['to'] = to
message['from'] = sender
message['subject'] = subject
raw = base64.urlsafe_b64encode(message.as_bytes())
return raw.decode()
messageToSend = create_message(sender_email,receiver_email,"test subject","test 123")
print(messageToSend)
endpoint = f'https://graph.microsoft.com/v1.0/users/{sender_email}/sendMail'
r = requests.post(endpoint, data=messageToSend,
headers={'Authorization': 'Bearer ' + result['access_token'], "Content-Type": "text/plain"})
print(r.status_code)
print(r.text)

SendGrid email displayed as plaintext instead of HTML

I am sending a dynamic email template through SendGrid using python. The correct HTML email template is displayed in inbox when I test the email through Sendgrid UI, but a plaintext version is displayed when the email is sent via python . Here's the code:
import sendgrid
import os
from sendgrid.helpers.mail import *
from sendgrid import SendGridAPIClient
sg = sendgrid.SendGridAPIClient('MY_KEY_HERE')
from_email = Email("example#email.com")
to_email = To("example#email.com")
subject = "Sending with SendGrid is Fun"
content = Content("text/plain", "and easy to do anywhere, even with Python")
mail = Mail(from_email, to_email, subject, content)
mail.template_id = "d-9e2812d070bb458e8d6cbd6c9b8922f6"
mail.dynamic_template_data = {
"first_name": "John",
"last_name": "Blacutt",
}
response = sg.client.mail.send.post(request_body=mail.get())
print(response.status_code)
print(response.body)
print(response.headers)
Here's the plaintext email:
If you are using a stored template and you want your dynamic content to render as HTML, you need to add an extra set of handlebars in the template itself else it will render as plain text.
EX:
<p>
{{{MailBody}}}
</p>
Notice the extra handlebars around {{MailBody}}

Flask Mail, Duplicate mails being sent

I am using Python and Flask Mail. I am trying to send a welcome email after the user confirms his account via clicking on a link sent as an email to the user earlier for confirming his account. After confirming his account by changing the value of the confirmed entry to true in the database, I send the welcome email. But it is sending 5 duplicate emails instead of one.
I used the below code-
class AccountConfirmation(Resource):
#jwt_required
def post(self):
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
user.confirmed = True
db.session.commit()
send_welcome_email(user)
return {'msg': 'Account confirmed successfully. You can now login to your account'}, 200
def send_welcome_email(user):
subject = '...'
body = '...'
sender = Config.ADMINS[0]
recipients = [user.email_id]
send_email(subject, body, sender, recipients)
from flask_mail import Message
def send_email(subject, body, sender, recipients):
msg = Message(subject = subject, body = body, sender=sender, recipients=recipients)
mail.send(msg)
I don't find any problem with the code. Except if there are multiple request to send the mail. To get over it, you can try sending Asynchronous Email. Please check this piece of code and don't forget to check the link for better explanation.
from threading import Thread
# ...
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
def send_email(subject, sender, recipients, text_body, html_body):
msg = Message(subject, sender=sender, recipients=recipients)
msg.body = text_body
msg.html = html_body
Thread(target=send_async_email, args=(app, msg)).start()

Applying css styles to sendgrid email python django?

I am learning to use the sendgrid api with django python. For now, I can successfully send emails but I would like to make the email more appealing and apply some css styles. I looked online for some help but couldn't find any. Can someone please suggest me how i should go about it ?
This is what i tried so far
def sendemail(sender,recipient,subject,content,url):
sg = sendgrid.SendGridAPIClient(apikey="*****")
from_email = Email(sender)
to_email = Email(recipient)
subject = subject
content = Content("text", content+url)
mail = Mail(from_email, subject, to_email, content)
response = sg.client.mail.send.post(request_body=mail.get())
print(response.status_code)
print(response.body)
print(response.headers)

SendGrid Multiple Recipients Python Json

I am trying to write a Python script using SendGrid to send personalized emails to approx 30 people. Instead of inputting each individual's first name and email address under a substitution (as I have below), isn't there a way to link to a JSON header file (let's call it names.json) that would have all the information and that python would pull from? How and where would I reference the JSon file that would pull each person's name and email address?
import sendgrid
import os
from sendgrid.helpers.mail import Email, Content, Substitution, Mail
try:
# Python 3
import urllib.request as urllib
except ImportError:
# Python 2
import urllib2 as urllib
sg = sendgrid.SendGridAPIClient([API_KEY])
from_email = Email("sender#gmail.com")
subject = "Your Monthly Updates!"
to_email = Email("john#gmail.com")
content = Content("text/plain", " ")
mail = Mail(from_email, subject, to_email, content)
mail.personalizations[0].add_to(Email("Jane#gmail.com"))
mail.personalizations[0].add_substitution(Substitution("-name-", "John"))
mail.personalizations[0].add_substitution(Substitution("-name-", "Jane"))
mail.set_template_id(TEMPLATE_ID)
try:
response = sg.client.mail.send.post(request_body=mail.get())
except urllib.HTTPError as e:
print e.read()
exit()
print(response.status_code)
print(response.body)
print(response.headers)

Categories