I am processing emails using imapclient.IMAPClient in Python and want to flag the successfully processed emails as "Seen" at the end. I have tried different ways but they either do not flag all the successful email or do not show as "read" in my Gmail inbox. I am looking for a way to update Gmail inbox flags of successfully processed emails using IMAP.
Here is the summary of code that I've tried:
imapObj = imapclient.IMAPClient(self.server_host, ssl=True)
imapObj.login(self.user_name, self.password)
imapObj.select_folder(email_folder, readonly=False)
#searches unseen emails with subject containing document
self.emails = imapObj.search(
[search_criterion1, search_value1,
search_criterion2, search_value2,
search_criterion3, search_value3,
search_flag])
#using PEEK to not mark all the emails as [Seen]
self.raw_messages = imapObj.fetch(self.emails, 'BODY.PEEK[]')
self.successful_emails = []
for i, self.email_id in enumerate(self.emails):
...do some process and for each successful process, append the email_id to list...
self.successful_emails.append(self.email_id)
#flag the successfully processed email as [SEEN]
imapObj.set_flags(self.successful_emails, '+FLAGS', '\SEEN')
Related
I have implemented a platform using rails, and the goal is to send thousands of emails to customers with one click. The concept is that an email array runs each loop and inside each loop runs send email functionality like below.
#emails = ['abc#gmai.com', 'abc#example.com'] # More than 3 thousands
#emails.each do |email|
aws_email_sender(email, #email_subject, #email_body_html)
end
And the email function is like below:
def aws_email_sender(recipient, subject, htmlbody)
sender = "hello#example.com"
awsregion = "ap-west-1"
# The HTML body of the email.
htmlbodycontent = "#{htmlbody}"
# The email body for recipients with non-HTML email clients.
textbody = "This email was sent with Amazon SES using the AWS SDK for Ruby."
# Specify the text encoding scheme.
encoding = "UTF-8"
# Create a new SES resource and specify a region
ses = Aws::SES::Client.new(region: awsregion)
# Try to send the email.
begin
# Provide the contents of the email.
resp = ses.send_email({
destination: {
to_addresses: [recipient]
},
message: {
body: {
html: {
charset: encoding,
data: htmlbodycontent
},
text: {
charset: encoding,
data: textbody,
},
},
subject: {
charset: encoding,
data: subject,
},
},
source: sender,
});
# If something goes wrong, display an error message.
rescue Aws::SES::Errors::ServiceError => error
puts "Email not sent. Error message: #{error}"
end
end
The email is sending well by AWS but my rails application has gone down like
A timeout occurred, error code 524
I couldn't get the breaking point, why has my application gone down every time?
Thanks in Advance
If 524 is an HTTP status code then it means...
Cloudflare was able to make a TCP connection to the website behind them, but it did not reply with an HTTP response before the connection timed out.
Meaning your Rails app is behind a Cloudflare proxy. Cloudflare received an HTTP request, forwarded it to your app, waited around for your app to respond, but your app never did. A more detailed explanation can be found here.
Probably because it's trying to send emails to 3000 people one-by-one.
There's two strategies to fix this.
Use Bulk Email
Since the content of the email is the same for everyone, use an email template to send bulk email using the #send_bulk_templated_email method.
You can send to up to 50 addresses at a time, so use #each_slice to loop through emails in slices of 50.
This will be more efficient, but your app will still be waiting around for 3000/50 = 60 AWS API calls. At worst it will still time out. At best the user will be waiting around for a form submission.
Use A Background Job
Anytime your app needs to do something that might take a lot of time, like using a service or a large database query, consider putting it into a background job. The Rails app queues up a job to send the emails, and then it can respond to the web request while the mailing is handled in the background. This has other advantages: errors calling the service won't cause an error for the user, and failed jobs due to a temporary service outage can automatically be retried.
In Rails this is done with ActiveJob and you could write a job class to send your mail.
Use ActionMailer
However, Rails also offers a class specifically for sending email in the background: ActionMailer. You can have ActionMailer use AWS with the aws-sdk-rails gem.
config.action_mailer.delivery_method = :ses
Currently I'm sending an envelope with a pdf document to multiple recipients but only the first one receives it(which is incidentally the impersonated user).
The recipient list is as follows:
recipients = DocuSign_eSign::Recipients.new(
'carbonCopies' => [cc1, candidate],
'signers' => [signer4],
'agents' => [signer1],
'editors' => [signer3],
'witnesses' => [signer5])
envelope_definition.recipients = recipients
# Request that the envelope be sent by setting status to "sent".
# To request that the envelope be created as a draft, set status to "created"
envelope_definition.status = 'created'
envelope_definition
Do I need bulk sending? Can I push the envelope to everyones Draft inboxes?
An envelope in created (draft) status is not sent to anyone.
Instead, it is waiting until its status is changed to sent.
A draft envelope is only available to the creator (the sender).
You may wish to create a new question in StackOverflow where you detail your use case: what are you trying to accomplish?
This is a similar question to this link, but slightly different:
Skipping blank emails in Google Apps Script Mail Merge
I have a form/sheet set up that, when the users fills out the form, it generates a receipt email (does not contain the form contents as some receipts do) that is then sent to the recipient. However when the user leaves this field blank I get a form trigger error, which is understandable why the script didn't finish. I am trying to figure out how to keep the script from attempting to send an email when the recipient/email field is blank. Unfortunately, making the email field required on the form is not an option since, oddly, not everyone would have an email address (if this were an option I would certainly just require an email address to be entered).
I have tried the following code snippet based on the link provided above.
// Send Email to recipient(s) declared above in #var sendEmail
if (e.values[11] != null) {
var sendEmail = e.values[11]; //email field column
var subject = "subject message";
var body = "body message";
MailApp.sendEmail(sendEmail, subject, body, {
name: "Community Home Health Care",
body: body,
noReply: true,
})
I have also tried instead of
(e.values[11] != null)
using
(e.values[11] != "")
The remaining code I have omitted (goes above what I have shown) simply takes the form field responses and generates a document converted to a PDF which works as expected. The email section also works, just trying to eliminate the failed script emails I get occasionally.
Thanks
First is an assumption that 12 items are passed (0 through 11) and the email is the last item to be passed. If that is the case, then test that the item is defined with:
if(typeof e.values[11] !== 'undefined')
If you are using the Mail Merge Tutorial linked to in the post you linked to, and using the getRowsData() function to get your form responses, you should be able to use the Header of the column containing the email address such as e.values.emailAddress and get:
if(typeof e.values.emailAddress !== 'undefined')
This may vary based on how your data is defined.
I'm trying to read email from GMail using gmail-xoauth Gem. I want to read the email and leave its unread status.
First, I tried reading just the header. Works.
imap = Net::IMAP.new('imap.gmail.com', 993, usessl = true, certs = nil, verify = false)
imap.authenticate('XOAUTH2', email, access_token)
imap.select('INBOX')
imap.search(["SINCE", since]).each do |message_id|
msg = imap.fetch(message_id,'RFC822.HEADER')[0].attr['RFC822.HEADER']
mail = Mail.read_from_string msg
puts mail.subject
end
Now, I want to read the body/text of the Email without marking it read.
This maybe be very late but I will leave it here for anyone else that stumbles onto this. If, for what ever reason you want to read the email and leave the flags intake, use:
imap.examine('INBOX')
instead of:
imap.select('INBOX')
From the Net::IMAP doc
Sends a EXAMINE command to select a mailbox so that messages in the mailbox can be accessed. Behaves the same as select(), except that the selected mailbox is identified as read-only.
Based on the documentation you need to use the store method. The documentation mentions:
store(set, attr, flags)
Sends a STORE command to alter data associated with messages in the mailbox, in particular their flags. The set parameter is a number or an array of numbers or a Range object. Each number is a message sequence number. attr is the name of a data item to store: ‘FLAGS’ means to replace the message’s flag list with the provided one; ‘+FLAGS’ means to add the provided flags; and ‘-FLAGS’ means to remove them. flags is a list of flags.
The return value is an array of Net::IMAP::FetchData. For example:
p imap.store(6..8, "+FLAGS", [:Deleted])
#=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
#<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
#<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
So you have to remove the :Seen flag
imap.store(message_id, "-FLAGS", [:Seen])
I have a rails application which processes incoming emails via IMAP. Currently a method is used that searches the parts of a TMail object for a given content_type:
def self.search_parts_for_content_type(parts, content_type = 'text/html')
parts.each do |part|
if part.content_type == content_type
return part.body
else
if part.multipart?
if body = self.search_parts_for_content_type(part.parts, content_type)
return body
end
end
end
end
return false
end
These emails are generally in response to a html email it sent out in the first place. (The original outbound email is never the same.) The body text the method above returns contains the full history of the email and I would like to just parse out the reply text.
I'm wondering whether it's reasonable to place some '---please reply above this line---' text at the top of the mail as I have seen in a 37 signals application.
Is there another way to ignore the client specific additions to the email, other than write a multitude of regular expressions (which I haven't yet attempted) for each and every mail client? They all seem to tack on their own bit at the top of any replies.
I have to do email reply parsing on a project I'm working on right now. I ended up using pattern matching to identify the response part, so users wouldn't have to worry about where to insert their reply.
The good news is that the implementation really isn't too difficult. The hard part is just testing all the different email clients and services you want to support and figuring out how to identify each one. Generally, you can use either the message ID or the X-Mailer or Return-Path header to determine where an incoming email came from.
Here's a method that takes a TMail object and extracts the response part of the message and returns that along with the email client/service it was sent from. It assumes you have the original message's From: name and address in the constants FROM_NAME and FROM_ADDRESS.
def find_reply(email)
message_id = email.message_id('')
x_mailer = email.header_string('x-mailer')
# For optimization, this list could be sorted from most popular to least popular email client/service
rules = [
[ 'Gmail', lambda { message_id =~ /.+gmail\.com>\z/}, /^.*#{FROM_NAME}\s+<#{FROM_ADDRESS}>\s*wrote:.*$/ ],
[ 'Yahoo! Mail', lambda { message_id =~ /.+yahoo\.com>\z/}, /^_+\nFrom: #{FROM_NAME} <#{FROM_ADDRESS}>$/ ],
[ 'Microsoft Live Mail/Hotmail', lambda { email.header_string('return-path') =~ /<.+#(hotmail|live).com>/}, /^Date:.+\nSubject:.+\nFrom: #{FROM_ADDRESS}$/ ],
[ 'Outlook Express', lambda { x_mailer =~ /Microsoft Outlook Express/ }, /^----- Original Message -----$/ ],
[ 'Outlook', lambda { x_mailer =~ /Microsoft Office Outlook/ }, /^\s*_+\s*\nFrom: #{FROM_NAME}.*$/ ],
# TODO: other email clients/services
# Generic fallback
[ nil, lambda { true }, /^.*#{FROM_ADDRESS}.*$/ ]
]
# Default to using the whole body as the reply (maybe the user deleted the original message when they replied?)
notes = email.body
source = nil
# Try to detect which email service/client sent this message
rules.find do |r|
if r[1].call
# Try to extract the reply. If we find it, save it and cancel the search.
reply_match = email.body.match(r[2])
if reply_match
notes = email.body[0, reply_match.begin(0)]
source = r[0]
next true
end
end
end
[notes.strip, source]
end
I think you will be stuck on this one. I have been doing some stuff with emails myself in TMail recently, and what you will generally find is that an email that has an HTML part is generally structured like:
part 1 - multipart/mixed
sub part 1 - text/plain
sub part 2 - text/html
end
The email clients I have played with Outlook and Gmail both generate replies in this format, and they just generally quote the original email inline in the reply. At first I though that the 'old' parts of the original email would be separate parts, but they are actually not - the old part is just merged into the reply part.
You could search the part for a line that begins 'From: ' (as most clients generally place a header at the top of the original email text detailing who sent it etc), but its probably not guaranteed.
I don't really see anything wrong with a --- please reply above this line --- generally, its not that invasive, and could make things a lot simpler.