I'm using SendGrid to send emails from my python-based Heroku app.I'm okay with it taking 10 or so minutes to get to my inbox, but I'm receiving three copies of the message and I can't figure out why. Here is the relevant code:
import sendgrid
from sendgrid import SendGridError, SendGridClientError, SendGridServerError
sg = sendgrid.SendGridClient('xxx#heroku.com', 'xxx')
message = sendgrid.Mail()
message.add_to('John Doe <xxx#xxx.com>')
message.set_subject('Example')
message.set_html('Body')
message.set_text('Body')
message.set_from('Dark Knight <xxx#xxx.com>')
message.add_attachment('image.jpg', './image.jpg')
status, msg = sg.send(message)
#app.route('/test2')
def test2():
sg.send(message)
return "sent"
When I go to the relevant route I get 'sent' returned and the email is sent, but again, it send three copies. I'm not sure why. Any help would be great.
Emails one and two:
status, msg = sg.send(message) would send two emails and then set status and msg to the response object.
Email three: after you load the route sg.send(message) sends the next email.
I suggest you to use sendgrid sendmail api to send email. its efficient, fast way to send emails.
You are calling sg.send(message) three times in your code.
It is called twice here: status, msg = sg.send(message) - this will send one mail for status and log it's response to that variable. It will then send again for msg and log it's response to that variable as well.
Then when the user hits the /test2 the function is called again, making it three messages in total.
Here's how you might change it to log responses but just send the one message out:
import sendgrid
from sendgrid import SendGridError, SendGridClientError, SendGridServerError
sg = sendgrid.SendGridClient('xxx#heroku.com', 'xxx')
def sendMessage(options):
message = sendgrid.Mail()
message.add_to('John Doe <xxx#xxx.com>')
message.set_subject('Example')
message.set_html('Body')
message.set_text('Body')
message.set_from('Dark Knight <xxx#xxx.com>')
message.add_attachment('image.jpg', './image.jpg')
// send the message and log the results to status
msg = sg.send(message)
return msg
#app.route('/test2')
def test2():
// send the message, pass any options like email address (not required)
status = sendMessage(options)
return status
I've added a new function above to send out the message and given it an optional options var, so you could use that to pass things to the message, like a different email address, or subject.
Related
I have a problem sending proactive messages using the Bot Framework with Python.
First what I need is to get the message body from Outlook, and then the bot must send that as a message to all the chats where it was added.
To do that, first I created a new file and called it Email.py.
To read every incoming message body I simply used while true: and time.sleep()
Here is my code example:
import imaplib, email, getpass
from email import policy
import json
import time
imap_host = 'outlook.office365.com'
imap_user = 'xx#xx.com'
# init imap connection
mail = imaplib.IMAP4_SSL(imap_host, 993)
rc, resp = mail.login(imap_user, 'xxxxxx')
while True:
# select only unread messages from inbox
mail.select('Inbox')
status, data = mail.search(None, '(UNSEEN)')
if not data[0].split():
time.sleep(120)
# Bot message variable
Message_for_bot = ''
# for each e-mail messages
for num in data[0].split():
# get a single message and parse it by policy.SMTP (RFC compliant)
status, data = mail.fetch(num, '(RFC822)')
email_msg = data[0][1]
email_msg = email.message_from_bytes(email_msg, policy=policy.SMTP)
# print only message parts that contain text data
for part in email_msg.walk():
if part.get_content_type() == "text/plain":
for line in part.get_content().splitlines():
Message_for_bot += '\n' + line
print(Message_for_bot)
After I successfully created a program to read and print all incoming messages, I tried to build my bot. I found a proactive message bot on the Internet and used it as an example.
First I thought to just run this file with os in the background, but then my bot wasn't running. So then I tried adding an async function in the bot file but it didn't work. My bot just ignores that function. (Then I found the async functions in activity_handler.py, but I didn't find any that could help me.)
Then I tried adding an on_message_activity function and thought maybe it will start working if I call the bot like "#bot hi" for example in Teams. For that idea I must always run the while cycle and never stop the bot, but then I just get a message, and if there's a new incoming message then the bot doesn't write it anymore, and it's not a solution because if the bot is used for multiple chats then it simply doesn't work this way.
Then I try include my code on on_members_added_activity it seems working on azure test in web chat perfectly, but in teams after 1-2 messages stopping to work.
my code
async def on_members_added_activity(
self, members_added: [ChannelAccount], turn_context: TurnContext
):
imap_host = 'outlook.office365.com'
imap_user = 'xxxxxx#xxxxxx.com'
# init imap connection
mail = imaplib.IMAP4_SSL(imap_host, 993)
rc, resp = mail.login(imap_user, 'xxxxxx')
while True:
# select only unread messages from inbox
mail.select('Inbox')
status, data = mail.search(None, '(UNSEEN)')
if not data[0].split():
time.sleep(5)
# Bot message variable
Message_for_bot = ''
# for each e-mail messages
for num in data[0].split():
# get a single message and parse it by policy.SMTP (RFC compliant)
status, data = mail.fetch(num, '(RFC822)')
email_msg = data[0][1]
email_msg = email.message_from_bytes(email_msg, policy=policy.SMTP)
# print only message parts that contain text data
for part in email_msg.walk():
if part.get_content_type() == "text/plain":
for line in part.get_content().splitlines():
Message_for_bot += '\n' + line
await turn_context.send_activity(f"{Message_for_bot}")
for member in members_added:
if member.id != turn_context.activity.recipient.id:
await turn_context.send_activity(
"bot starting work..."
)
So maybe it's possible to send a message to wherever the bot is added (it needs to get this information somehow, maybe it's kept in the bot memory) whenever Message_for_bot is not empty.
All help will be appreciated.
As we have discussed some logic has to change
Move your code out of the on_members_added_activity function
Use Proactive concept to send the message
-Vinoth
Hi am trying to defer sending mail via sendmail.
Am able to send mail no problem.
As soon as I try deferred.defer(send_invitation,recipient), it stops working.
I can see the tasks retrying to max allowed limit, and the logs show:
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/api/mail.py", line 1136, in send
raise ERROR_MAP[e.application_error](e.error_detail)
InvalidSenderError: Unauthorized sender
Does this mean that if a sendmail job is sent to the default queue (or any queue), it is no longer considered as being sent from the original sender, and that the new sender (i.e. the queue) is not authorized??
Without using deferred.defer the mail is sent, both with project owner's email, and default appengine service account. Both accounts have needed permissions/roles.
import webapp2
from google.appengine.ext import deferred
import jinja2
import os
import datetime
from google.appengine.api import users, mail
recipient ='xxxxxx#xxxx.com'
user = users.GetCurrentUser().email()
print "THIS IS THE CURRENT USER %s" %user
def send_invitation(recipient):
print "In the def user email is %s" %user
mail.send_mail(sender=user,to=recipient, subject='You\'re invited!',body='''You have been invited to join our community...''')
class SendInvitationHandler(webapp2.RequestHandler):
print "This is user within the class %s" %user
def get(self):
user = users.GetCurrentUser().email()
tempalte_env = jinja2.Environment(loader=jinja2.FileSystemLoader(os.getcwd()))
current_time = datetime.datetime.now()
template = tempalte_env.get_template('invitation.html')
message = "<p>%s, and an invitation to %s has been deferred from %s</p>" % (datetime.datetime.now(),recipient, user)
context = {'recipient': recipient,
'message': message,
'current_time': current_time,
'user': user
}
self.response.out.write(template.render(context))
deferred.defer(send_invitation,recipient)
app = webapp2.WSGIApplication([('/sendinvite', SendInvitationHandler)], debug=True)
Change line deferred.defer(send_invitation,recipient) to send_invitation(recipient) and it works ...
The deferred task executes on a different handler in response to a different request, it no longer has the context of the original request. So basically you no longer have the user set.
You need to collect all the info required to send the message in the original context and pass it as parameter(s) to the deferred task. In your particular case you need to pass user as well, in addition to recipient, something like this:
def send_invitation(recipient, sender):
mail.send_mail(sender=sender, to=recipient,
subject='You\'re invited!',
body='''You have been invited to join our community...''')
and defer it with:
deferred.defer(send_invitation, recipient, user)
I'm trying to get the payload from the quick replies that I'm using to create a chatbot on Messenger, but I keep getting this error: KeyError: 'quick_reply'. I am using python to create bot. I've tried everything imaginable such as:
#app.route('/', methods=['POST'])
def webhook():
data = request.get_json()
log(data)
if data["object"] == "page":
for entry in data["entry"]:
for messaging_event in entry["messaging"]:
if messaging_event.get("message"):
recieved(messaging_event)
message_text = messaging_event["message"]["quick_reply"]["payload"]
if messaging_event.get("delivery"):
pass
if messaging_event.get("optin"):
pass
if messaging_event.get("postback"):
pass
return "ok", 200
But each one keeps giving me the same problem. I've looked online and at docs but there seems to be no answer. Any insights?
When a Quick Reply is tapped, a text message will be sent to your webhook Message Received Callback. The text of the message will correspond to the title of the Quick Reply. The message object will also contain a field named quick_reply containing the payload data on the Quick Reply.
so quick reply payload will be like message.quick_reply.payload
We're currently using a super basic Python script that I found online a while ago to send an error report via HTML from out departmental email address. However, as it currently stands, it sends the email from our email address to our email address. I'm hoping there might be some way to send the email from the address of the current user. Here's what we've got:
def sendMail(sender, recipient, subject, html, text):
import MimeWriter, mimetools, cStringIO
import smtplib
out = cStringIO.StringIO()
htmlin = cStringIO.StringIO(html)
txtin = cStringIO.StringIO(text)
writer = MimeWriter.MimeWriter(out)
writer.addheader("From", sender)
writer.addheader("To", recipient)
writer.addheader("Subject", subject)
writer.addheader("MIME-Version", "1.0")
writer.startmultipartbody("alternative")
writer.flushheaders()
subpart = writer.nextpart()
subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
pout = subpart.startbody("text/plain", [("charset", 'us-ascii')])
mimetools.encode(txtin, pout, 'quoted-printable')
txtin.close()
subpart = writer.nextpart()
subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
pout = subpart.startbody("text/html", [("charset", 'us-ascii')])
mimetools.encode(htmlin, pout, 'quoted-printable')
htmlin.close()
writer.lastpart()
msg = out.getvalue()
server = smtplib.SMTP('smtp.office365.com',587)
server.ehlo()
server.starttls()
server.ehlo()
server.login("sample#email.com","sample")
server.sendmail(sender, recipient, msg)
server.quit()
Then we're using a simple try/except execfile script to run everything from:
try:
execfile('\\\\path.py')
except:
print 'ATTENTION: An error has been detected in a script process.'
traceback.print_exc(file=sys.stdout)
import sys, cgitb
recipient =
['sample#email.com','sample2#email.com','sample3#email.com','sample4#email.com']
for rec in recipient:
Utils.sendMail('sample#email.com',
rec,
'ATTENTION: An error has been detected in a script process...',
cgitb.html(sys.exc_info()),
cgitb.text(sys.exc_info()))
sys.exit()
It's here where we define the address we're sending to:
for rec in recipient:
Utils.sendMail('sample#email.com',
rec,
'ATTENTION: An error has been detected in a script process...',
cgitb.html(sys.exc_info()),
cgitb.text(sys.exc_info()))
sys.exit()
Is there some kind of AD script we can implement here? We've got about 30 people using our processes so whenever an error pops up all we get is an email with the error and no reference to who it belongs to.
It is not easy to work with AD in Python, and it's even tougher to do without installing any 3rd party modules.
Would adding the username that ran the script in the email body or subject suffice instead?:
import getpass
user = getpass.getuser()
Then in the execfile script:
Utils.sendMail(
'sample#email.com',
rec,
'ATTENTION: %s has run a script which has errored...' % user,
cgitb.html(sys.exc_info()),
cgitb.text(sys.exc_info())
)
It is important to note that this should not be used for any legitimate authentication purposes, as it does not protect against user-spoofing and the likes..
Sounds like you need to group you recipients i.e. you could have a group for engineers, quality assurance, business development, management, etc..
where each group is represented by a list of email addresses.
This way when an error occurs, you may send a human readable email describing the issue to the quality assurance group.
As for the engineers, you may want to send a more technical email that not only contains the human readable error descriptions, but also an additional stack trace. When an engineer is attempting to troubleshoot an error message more, is better.
As for the business development and management group, they probably shouldn't be receiving emails on failures.
hope that helps.
I have a python script that has to fetch unseen messages, process it, and mark as seen (or read)
I do this after login in:
typ, data = self.server.imap_server.search(None, '(UNSEEN)')
for num in data[0].split():
print "Mensage " + str(num) + " mark"
self.server.imap_server.store(num, '+FLAGS', '(SEEN)')
The first problem is that, the search returns ALL messages, and not only the UNSEEN.
The second problem is that messages are not marked as SEEN.
Can anybody give me a hand with this?
Thanks!
import imaplib
obj = imaplib.IMAP4_SSL('imap.gmail.com', '993')
obj.login('user', 'password')
obj.select('Inbox') <--- it will select inbox
typ ,data = obj.search(None,'UnSeen')
obj.store(data[0].replace(' ',','),'+FLAGS','\Seen')
I think the flag names need to start with a backslash, eg: \SEEN
I am not so familiar with the imaplib but I implement this well with the imapclient module
import imapclient,pyzmail,html2text
from backports import ssl
context=ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
iobj=imapclient.IMAPClient('outlook.office365.com', ssl=True, ssl_context=context)
iobj.login(uname,pwd)# provide your username and password
iobj.select_folder('INBOX',readonly=True)# Selecting Inbox.
unread=iobj.search('UNSEEN')# Selecting Unread messages, you can add more search criteria here to suit your purpose.'FROM', 'SINCE' etc.
print('There are: ',len(unread),' UNREAD emails')
for i in unread:
mail=iobj.fetch(i,['BODY[]'])#I'm fetching the body of the email here.
mcontent=pyzmail.PyzMessage.factory(mail[i][b'BODY[]'])#This returns the email content in HTML format
subject=mcontent.get_subject()# You might not need this
receiver_name,receiver_email=mcontent.get_address('from')
mail_body=html2text.html2text(mcontent.html_part.get_payload().decode(mcontent.html_part.charset))# This returns the email content as text that you can easily relate with.
Let's say I want to just go through the unread emails, reply the sender and mark the email as read. I'd call the smtp function from here to compose and send a reply.
import smtplib
smtpobj=smtplib.SMTP('smtp.office365.com',587)
smtpobj.starttls()
smtpobj.login(uname,pwd)# Your username and password goes here.
sub='Subject: '+str(subject)+'\n\n'# Subject of your reply
msg='Thanks for your email! You're qualified for the next round' #Some random reply :(
fullmsg=sub+new_result
smtpobj.sendmail(uname,test,fullmsg)# This sends the email.
iobj.set_flags(i,['\\Seen','\\Answered'])# This marks the email as read and adds the answered flag
iobj.append('Sent Items', fullmsg)# This puts a copy of your reply in your Sent Items.
iobj.logout()
smtpobj.logout()
I hope this helps