I'm attempting to send mail using the EmailMessage class, which, when used normally, as in:
message = mail.EmailMessage()
message.sender = ...
message.to = ...
message.subject = ...
message.send()
Works just fine; I receive the email as expected.
However, I am trying to add this email.send() event to the push queue using the deferred library:
def email():
message = mail.EmailMessage()
message.sender = ...
message.to = ...
message.subject = ...
// elsewhere
def send_email(message):
deferred.defer(message.send, _countdown=10)
app = webapp2.WSGIApplication([
('/api/email', EmailHandler)
], debug=False)
I can see it is successfully added to the push queue on the admin interface, but I never actually receive the email or any kind of failure notification/bounce message.
I've seen the limitations of the deferred library but don't think I'm running into any of those here?
deferred.defer takes as arguments a function and arguments to be passed to that function. When you do this:
deferred.defer(message.send, _countdown=10)
You pass the function message.send but all the data from the message object gets lost so you no longer have any data!
Instead move all of the logic into your send_email function:
def send_email(from, to, subject, body):
message = mail.EmailMessage()
message.sender = ...
message.to = ...
message.subject = ...
message.send()
// elsewhere
deferred.defer(send_email, from, to, subject, body, _countdown=10)
Related
I have a task to create a REST API that will be responsible for handling messages. As a part of the functionality, I should be able to write a message. This message should contain the following fields:
id
sender
receiver
message itself
subject
creation date
So, as I expected to do this is to have a route that should handle the object that I will send as an argument. But I am not sure I can do so. What would you recommend in this case?
For now, I can handle it somehow like this:
#app.route('/new_message/<string:sender>/<string:receiver>/<string:message>/', methods=['POST'])
def get_message(sender, receiver, message):
sender = sender
receiver = receiver
message = message
# Code that will add the data or to the database or to the json file
# if I decide not to implement DB for this task
return 'Sent successfully'
Thanks for your advice!
I am suggesting you to use JSON request body instead of the path parameters for the POST method.
Here is the example,
from flask import request, Flask
app = Flask(__name__)
#app.route('/new_message', methods=['POST'])
def get_message():
payload = request.json()
sender = payload['sender']
receiver = payload['receiver']
message = payload['message']
return 'Sent successfully'
if __name__ == "__main__":
app.run()
Now, If you want to add message as object then you can add it in JSON body. Not only message object you can add any number of fields if required.
I have verified the following
AWS SES isn't in Sandbox. I can send email via console to non-verified email ids.
My Lambda function has a role attached with full access to SES and Lambda (since its initial basic testing gave full permissions)
The following below a basic code from AWS documentation, just hard coded my email id. Yet I can't receive any email. The lambda code runs successfully but I don't receive emails.
import json
import os
import boto3
from botocore.exceptions import ClientError
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
print('Loading function')
def lambda_handler(event, context):
print("Received event: " + json.dumps(event, indent=2))
#print("value1 = " + event['key1'])
#print("value2 = " + event['key2'])
#print("value3 = " + event['key3'])
#return event['key1'] # Echo back the first key value
#raise Exception('Something went wrong')
SENDER = "[redacted email]"
RECIPIENT = event['email']
CONFIGURATION_SET = "ConfigSet"
AWS_REGION = "us-east-2"
SUBJECT = "Contact Us Form Details"
# The email body for recipients with non-HTML email clients.
BODY_TEXT = "Hello,\r\nPlease see the attached file for a list of customers to contact."
# The HTML body of the email.
BODY_HTML = """\
<html>
<head></head>
<body>
<h1>Hello!</h1>
<p>Please see the attached file for a list of customers to contact.</p>
</body>
</html>
"""
# The character encoding for the email.
CHARSET = "utf-8"
# Create a new SES resource and specify a region.
client = boto3.client('ses',region_name='us-east-2')
# Create a multipart/mixed parent container.
msg = MIMEMultipart('mixed')
# Add subject, from and to lines.
msg['Subject'] = "Contact Us Form Details"
msg['From'] ="[redacted email]"
msg['To'] = "[redacted email]"
# Create a multipart/alternative child container.
msg_body = MIMEMultipart('alternative')
# Encode the text and HTML content and set the character encoding. This step is
# necessary if you're sending a message with characters outside the ASCII range.
textpart = MIMEText(BODY_TEXT.encode(CHARSET), 'plain', CHARSET)
htmlpart = MIMEText(BODY_HTML.encode(CHARSET), 'html', CHARSET)
# Add the text and HTML parts to the child container.
msg_body.attach(textpart)
msg_body.attach(htmlpart)
# Define the attachment part and encode it using MIMEApplication.
#att = MIMEApplication(open(ATTACHMENT, 'rb').read())
# Add a header to tell the email client to treat this part as an attachment,
# and to give the attachment a name.
#att.add_header('Content-Disposition','attachment',filename=os.path.basename(ATTACHMENT))
# Attach the multipart/alternative child container to the multipart/mixed
# parent container.
msg.attach(msg_body)
# Add the attachment to the parent container.
#msg.attach(att)
print(msg)
try:
#Provide the contents of the email.
response = client.send_raw_email(
Source="[redacted email]",
Destinations=[
"[redacted email]"
],
RawMessage={
'Data':msg.as_string(),
},
#ConfigurationSetName=CONFIGURATION_SET
)
# Display an error if something goes wrong.
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
Attaching my cloud watch logs for reference
If your code is really what you have displayed to us, then the reason that it is not sending the email is because half your code is not being executed.
def lambda_handler(event, context):
print("Received event: " + json.dumps(event, indent=2))
#print("value1 = " + event['key1'])
#print("value2 = " + event['key2'])
#print("value3 = " + event['key3'])
#return event['key1'] # Echo back the first key value
#raise Exception('Something went wrong')
SENDER = "[redacted email]"
RECIPIENT = event['email']
CONFIGURATION_SET = "ConfigSet"
AWS_REGION = "us-east-2"
SUBJECT = "Contact Us Form Details"
# The email body for recipients with non-HTML email clients.
BODY_TEXT = "Hello,\r\nPlease see the attached file for a list of customers to contact."
When AWS Lambda executes the function, it calls lambda_handler(). As per Python formatting, it will execute all indented lines since they form part of the function. This includes your print() statement.
However, starting with the BODY_TEXT = ... line, there is no indenting. This means that the code is part of the "main" program, and not part of the lambda_handler() function. It would be executed when the Lambda container is first instantiated, but not when the function is triggered.
Bottom line: If this is your actual code, you need to fix your indents.
If you are not getting any errors from when executing the lambda, then most probably you are not hitting the SES API. From what I see in your code, you are missing an Access Key Id and Secret Access Key. Try configuring your boto client like this:
client = boto3.client(
'ses',
region_name=region,
aws_access_key_id='aws_access_key_string',
aws_secret_access_key='aws_secret_key_string'
)
Also make sure that your lambda is deployed in the same region as your SES. I see you are using us-east-2.
Another discrepancy I see, which is also in the documentation is that in the official AWS documentation, Destinations is actually Destination. Try it without the 's'.
Can you also paste the cloudwatch logs for the lambda. I see it should print the message Id upon success. Does it?
I am using xmpppy http://xmpppy.sourceforge.net/ to send Jabber notifications, it is working well for single destinations using the following code:
# pip install https://github.com/rochacbruno/xmpppy/tarball/master
import xmpp
JABBER_SETTINGS = {"USERNAME": None, "PASSWORD": None, "DOMAIN": None, "RESOURCE": None}
def get_jabber_client():
client = xmpp.Client(JABBER_SETTINGS.get('DOMAIN'))
client.connect(server=(JABBER_SETTINGS.get('DOMAIN'), '5222'))
client.auth(
JABBER_SETTINGS.get('USERNAME'),
JABBER_SETTINGS.get('PASSWORD'),
JABBER_SETTINGS.get('RESOURCE')
)
client.sendInitPresence()
return client
def send_message(to, message):
client = get_jabber_client()
xmpp_message = xmpp.Message(to, message)
client.send(xmpp_message)
client.disconnect()
send_message("single.destination#domain.com", "Hello World!")
But now I need to send the message to multiple destinations, for now I am doing.
for users in list_of_users:
send_message(user, "Hello World!")
Which works fine, but every time I call it starts the process of authentication and takes a lot of time.
I've tried to create a single client and use the same client to send the message.
def send_message(to, message):
if isinstance(to, basestring):
to = [to]
assert isinstance(to, (list, tuple))
client = get_jabber_client()
for destination in to:
xmpp_message = xmpp.Message(destination, message)
client.send(xmpp_message)
client.disconnect()
send_message(['user1...', 'user2...'], "Hello World!")
The code above works, but only the first user in the list gets the message well formatted, the other users receives the message in pure XML.
I saw this code (in .net), mentioning XEP-0033: Extended Stanza Addressing http://forum.ag-software.net/thread/1482-Send-Message-To-all-users-in-contact-list
var addresses = new Addresses();
addresses.AddAddress(new Address
{
Type = Type.to,
Jid = "hildjj#jabber.org/Work",
Description = "Joe Hildebrand"
});
addresses.AddAddress(new Address
{
Type = Type.cc,
Jid = "jer#jabber.org/Home",
Description = "Jeremie Miller"
});
var msg = new Matrix.Xmpp.Client.Message();
msg.Add(addresses);
msg.To = "multicast.jabber.org";
msg.Body = "Hello, world!";
builds the following Xml:
<message to='multicast.jabber.org'>
<addresses xmlns='http://jabber.org/protocol/address'>
<address type='to' jid='hildjj#jabber.org/Work' desc='Joe Hildebrand'/>
<address type='cc' jid='jer#jabber.org/Home' desc='Jeremie Miller'/>
</addresses>
<body>Hello, world!</body>
</message>
But I did not found the way to do the same in Python using xmpppy, any idea on how to build the multicast stanza and send the message to multiple users using Python?
Thanks.
You could probably get this done without XEP-0033. In this loop, you overwrite the value of the variable message the first time, and subsequent messages will get garbled contents:
for destination in to:
message = xmpp.Message(destination, message)
client.send(message)
Try this instead:
for destination in to:
xmpp_message = xmpp.Message(destination, message)
client.send(xmpp_message)
I'm trying to save both messages that get sent out and received in a database, including message sid, message body, so that I can process them using some logic.
class OutgoingMessage(models.Model):
''' Table of SMS messages that are sent out to users by Twilio '''
outgoing_sid = models.CharField(max_length=40)
sent_date = models.DateTimeField()
sender = models.ForeignKey(TwilioNumber, related_name='outgoing_messages')
recipient = models.ForeignKey(Caller, related_name='outgoing_messages')
message_body = models.TextField(max_length=1600)
class IncomingMessage(models.Model):
''' Table of SMS messages received by Twilio from users '''
incoming_sid = models.CharField(max_length=40)
sent_date = models.DateTimeField()
sender = models.ForeignKey(Caller, related_name='incoming_messages')
recipient = models.ForeignKey(TwilioNumber, related_name='incoming_messages')
message_body = models.TextField(max_length=1600)
Is there a straightforward way of getting the message sid from a message Twilio sends out, immediately after it gets sent? Getting the sid from an incoming message is pretty easy but the other way around is not very clear.
I'm trying to look for an alternative to using cookies, as suggested in this post
I found the answer here
# Download the Python helper library from twilio.com/docs/python/install
from twilio.rest import TwilioRestClient
# Your Account Sid and Auth Token from twilio.com/user/account
account_sid = "AC1ab0bb368322688c3d6cbb1355aeef9b"
auth_token = "{{ auth_token }}"
client = TwilioRestClient(account_sid, auth_token)
message = client.messages.create(body="Jenny please?! I love you <3",
to="+15558675309",
from_="+14158141829",
media_url="http://www.example.com/hearts.png")
print message.sid
However, this doesn't seem to work very well with django_twilio views.
New to Pyramid here, and trying to set up pyramid_mailer to send an email to myself:
I have in development.ini:
mail.host = smtp.gmail.com
mail.username = EMAIL#gmail.com
mail.password = PASSWORD
mail.port = 587
mail.ssl = True
[handlers]
keys = console
in my project/__init__.py:
config.include('pyramid_mailer')
in my project/views.py
from pyramid_mailer.mailer import Mailer
from pyramid_mailer import get_mailer
from pyramid_mailer.message import Message
#view_config(renderer="templates/site_view.pt")
def site_view(self):
...
config.registry['mailer'] = Mailer.from_settings(settings)
mailer = request.registry['mailer']
message = Message(subject="It works!",
sender="EMAIL#gmail.cm",
recipients=["EMAIL#gmail.com"],
body="Hey there!")
mailer.send(message)
Am I missing something very fundamental here?
In fact you are missing something fundamental! :-)
.send() is a lazy-send which adds the message to the transaction manager. If you are not using pyramid_tm then the mail will not be sent at the end of the request. The transactional emailing is nice because if an exception is raised in your code after calling send(), the mail will not be sent.
Anyway, the way to make your code send is via .send_immediately().
You might want to check that and use:
mail.tls = True
Can't send emails with pyramid_mailer and gmail
also you can use .send_immediately(message, fail_silently=False)
You'd have something like that:
mail.host = smtp.gmail.com
mail.username = EMAIL#gmail.com
mail.password = PASSWORD
mail.port = 587
mail.tls = True
In your setup:
config.include('pyramid_mailer')
And then
mailer = get_mailer(request)
message = Message(subject="It works!",
sender="EMAIL#gmail.cm",
recipients=["EMAIL#gmail.com"],
body="Hey there!")
mailer.send_immediately(message, fail_silently=False)
If nothing still work, you can enable debugging using
mail.debug = True
It should dump in stdout the smtp session. If something didn't work. You'll know exactly why. If everything is fine.