django sendgrid send unique arguments - django

Sendgrid allows to specify unique arguments when sending emails. These can be used for the event webhook integration to identify emails doc.
I have an existing code piece in django that uses django.core.mail.EmailMultiAlternatives to send emails via SendGrid. I'd like to specify the above mentioned unique arguments if possible. So far I was trying to use the custom_args field
email.custom_args = {'test_arg': 'value'}
but that didn't seem to work.
I saw that there's a django-sendgrid module, but if possible I'd prefer not having to re-write the existing code base.

i don't use the SendGrid, but looks like the Unique Arguments is email headers, and by the doc: emailmessage, you can add headers for example:
from django.core.mail import EmailMultiAlternatives
subject, from_email, to = 'hello', 'EXAMPLE#FROM.com', 'EXAMPLE#TO.NET'
text_content = 'This is an important message.'
msg = EmailMultiAlternatives(
subject,
text_content,
from_email,
[to], headers={"customerAccountNumber": "55555", },
)
msg.send()

Have you tried using the SendGrid Python Library?
The term custom_args is specific to Web API v3 Mail Send, so adding it to your SMTP message won't work. The Web v3 API call is faster and more full-featured than the SMTP transaction, but is a newer generation of call, and has some updated vocabulary.
If you need to send via SMTP, you'll need to use the term unique_args within the X-SMTPAPI header specifically. That will allow those key:values to be attached to all Events related to the messages generated in that send.

I'm using Python and Django to configure sending messages through SendGrid: https://docs.sendgrid.com/for-developers/sending-email/django
As others have mentioned, the best way to match event webhooks with previously sent emails is to add a unique identifier when sending the email. To do so, you need to use the X-SMTPAPI header with a unique_args key/value. It's important to configure this header correctly, otherwise the email will be rejected. Two helpful links:
Building an X-SMTPAPI Header
Unique Arguments
Based on this documentation, here's what my implementation looks like:
MESSAGE_ID_KEY = 'jv_message_id'
message = EmailMultiAlternatives(
subject=subject,
body=plain_content,
from_email=from_email or EMAIL_ADDRESS_SEND,
to=to_emails,
cc=cc_email,
headers={
'X-SMTPAPI': json.dumps({'unique_args': {MESSAGE_ID_KEY: '<internal message id>'}})
}
)
Then when the webhook is sent, I get the message ID like this:
def post(self, request):
event_data = request.data
if not event_data:
logger.error('Sendgrid webhook delivered an unparseable request')
return Response(status=status.HTTP_200_OK)
for event in event_data:
event_type = event['event']
if not (message_id := event.get(MESSAGE_ID_KEY)):
logger.warning('Sendgrid webhook delivered an event without a unique message ID')
continue
...

Related

Send mail *without* mail queue in Django

Im using django_yubin to send mails via its mail queue.
However, there are a few instances where I want to send the mail immediately without putting it on the queue.
For example, when a user registers or resets their password. Or certain admin emails.
I tried using Django's built in email system by
from django.core.mail import send_mail as send_mail_core
and then using the send_mail_core() function.
This didnt work - looks like the send_mail in django.core.mail gets overridden by yubin
Thanks for your help
Why would you even try to use send_mail_core if you can select the mail backend inside of send_mail function
def send_mail(subject, message, from_email, recipient_list,
fail_silently=False, auth_user=None, auth_password=None,
connection=None, html_message=None):
connection: The optional email backend to use to send the mail. If
unspecified, an instance of the default backend will be used. See the
documentation on Email backends for more details.

django oscar: How to send an email to store admin for order placement notification [duplicate]

This question already has an answer here:
Django Oscar - email admin on order placed
(1 answer)
Closed 3 years ago.
I want to get a notification
email
to myself (As Admin) when there is new order placed on my django-oscar store.
There is a way that I am following to send emails to the admins, what I did was forked the order app and the went to the utils.py, went to the place_order function under OrderCreator() class, oscar sends signals for order placement the code from oscar's github is here, so what I did was put my send email code after this.
SO I created a communication event type with code ADMIN_ORDER_MAIL in my admin side and used that here to send mails.
My code is here:
# Send an email to admin on placing of order
ctx = {'order': order, 'user': order.user}
commtype_code = 'ADMIN_ORDER_MAIL'
try:
event_type = CommunicationEventType.objects.get(code=commtype_code)
# print("Event type typr: ", type(event_type))
# self.create_communication_event(order, event_type)
except Exception as e:
messages = CommunicationEventType.objects.get_and_render(code=commtype_code, context=ctx)
# print(e)
else:
messages = event_type.get_messages(ctx)
send_mail(
subject=messages['subject'],
message='',
html_message=messages['html'],
from_email='from email address',
recipient_list=['to email address'],
fail_silently=False,
)
I think that you can use django signals.
Django-Oscar has some signals defined for multiple actions, and one of them is associated to the action of order placed.
This signal provide information about the order made and the user who made the order. You can use this information to send the email.
I hope it will be useful for you!

Sending Bulk email in Django

I have to send bulk email in django, the email template will be will be customized and the some data in the template will be coming from db. i twas using django notification but it can only send email to the registered users. I have to send emails to the non-registered users. there will be five email template the user can select any one and the email has to be sent.
For ex. An invitation to the event to the group of non-registered users. user will enter email ids, and will do a bulk send. which django package can i use to achieve the same.
You can use django's default sending multiple email system. From here: https://docs.djangoproject.com/en/dev/topics/email/#sending-multiple-emails
You can try like this:
from django.core import mail
connection = mail.get_connection()
connection.open()
reciever_list= ['aa#bb.cc', 'dd#ee.ff'] #extend this list according to your requirement
email1 = mail.EmailMessage('Hello', 'Body goes here', 'from#example.com',
reciever_list, connection=connection)
email1.send()
connection.close()
For bulk email reference, you can check this so answer: How does one send an email to 10,000 users in Django?
Edit
From this stackoverflow answer, you can send emails with template. If you use django 1.7, html_message can be added as perameter of send_mail(). Details here.
By the way, for mass email handling, django has send_mass_mail() method.

Control a function (E.g send mail) from a users profile page

I have a profile page like so: http://i.stack.imgur.com/Rx4kg.png . In management I would like a option "Notify by mail" that would control my send_email functions in every application I want. As example I'm using django-messages and it sends private messages aswell as emails when you send a message. I would like for the user to be able to specify if he wants emails aswell when he gets a message.
messages/utils.py
def new_message_email(sender, instance, signal,
subject_prefix=_(u'New Message: %(subject)s'),
template_name="messages/new_message.html",
default_protocol=None,
*args, **kwargs):
"""
This function sends an email and is called via Django's signal framework.
Optional arguments:
``template_name``: the template to use
``subject_prefix``: prefix for the email subject.
``default_protocol``: default protocol in site URL passed to template
"""
if default_protocol is None:
default_protocol = getattr(settings, 'DEFAULT_HTTP_PROTOCOL', 'http')
if 'created' in kwargs and kwargs['created']:
try:
current_domain = Site.objects.get_current().domain
subject = subject_prefix % {'subject': instance.subject}
message = render_to_string(template_name, {
'site_url': '%s://%s' % (default_protocol, current_domain),
'message': instance,
})
if instance.recipient.email != "":
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[instance.recipient.email,])
except Exception, e:
#print e
pass #fail silently
Apparently instance.recipient.email is the email for the recipient user. So my questions are: How do I go about creating an option in my profile management that can be used in my new_message_email to check if the user wants emails or not? My own thoughts are that I need to save a value in the database for the user and then check for that value in new_message_email function. How I do that isn't clear though. Do I create a new function in my userprofile/views.py and class in userprofile/forms.py? And have my userprofile/overview.html template change them? Some specifics and thoughts if this is the right approach would help alot!
You probably want to start off by creating a user profile so that you have a good way to store weather or not the user wants these emails sent to them. This is done using the AUTH_PROFILE_MODULE setting in your settings.py.
Once you have the data stored, you should be able to access it from instance.recipient (assuming that instance.recipient is a User object). So you could change your code to:
if instance.recipient.get_profile().wants_emails and instance.recipient.email != "":
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[instance.recipient.email,])
Done and done.

Emails, a different 'reply to' address than sender address

I have a contact form on a website (a general form: name, email, subject, message) in which mails are sent using google apps smtp to the admins.
Currently if an administrator wants to reply to the mail directly selecting the reply option, the person's reply's To field will be filled by the sender's address automatically.
What I wan't to ask is, Is there any standardized way to pass on some additional info with the mail which would define any reply to the mail should go to this address instead of the sender's?
It does seems that there is a little chance for this option as it may lead to some problems due to spammers (They may define a custom reply field in their mail and a general user might not look where they are replying).
So as an alternative what I thought is to find a way to create a filter with sender's account which figures out the reply email address from the format and forwards the mail (Doesn't seems like a good solution and I have no idea how to achieve this).
I have tagged django, though this is not directly related with this, as I will finally implement this through django.
There are in fact standardized headers to specify response headers: http://cr.yp.to/immhf/response.html.
As far as implementing this in Django is concerned, the documentation contains an example:
from django.core.mail import EmailMessage
email = EmailMessage(
'Hello', # email subject
'Body goes here', # email body
'from#example.com', # sender address
['to1#example.com', 'to2#example.com'],
['bcc#example.com'],
headers={'Reply-To': 'another#example.com'},
)
This solved my problem.
Reply-To is a standard SMTP header.
I can't find a good reference for it at the moment, but it is mentioned in the Wikipedia article on Email.
Edit: Found it: RFC 5322, section 3.6.2
The RFC says you can specify multiple emails and that is what I was looking for. Came up with this:
from django.core.mail import EmailMessage
headers = {'Reply-To': 'email#one.com;email#two.com'}
msg = EmailMessage(subject, html_content, EMAIL_HOST_USER, email_list, headers=headers)
msg.content_subtype = "html"
msg.send()
Works like a charm. Note: EMAIL_HOST_USER is imported from your settings file as per Django doc email setup. More on this here, search for 'reply-to': https://docs.djangoproject.com/en/dev/topics/email/
Here is also how reply-to can be used
from django.core.mail import EmailMessage
email = EmailMessage(
'Hello',
'Body goes here',
'from#example.com',
['to1#example.com', 'to2#example.com'],
['bcc#example.com'],
reply_to=['another#example.com'],
headers={'Message-ID': 'foo'},
)
Read more at docs docs.djangoproject