Receive replies from Gmail with smtplib - Python - python

Ok, I am working on a type of system so that I can start operations on my computer with sms messages. I can get it to send the initial message:
import smtplib
fromAdd = 'GmailFrom'
toAdd = 'SMSTo'
msg = 'Options \nH - Help \nT - Terminal'
username = 'GMail'
password = 'Pass'
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username , password)
server.sendmail(fromAdd , toAdd , msg)
server.quit()
I just need to know how to wait for the reply or pull the reply from Gmail itself, then store it in a variable for later functions.

Instead of SMTP which is used for sending emails, you should use either POP3 or IMAP (the latter is preferable).
Example of using SMTP (the code is not mine, see the url below for more info):
import imaplib
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('myusername#gmail.com', 'mypassword')
mail.list()
# Out: list of "folders" aka labels in gmail.
mail.select("inbox") # connect to inbox.
result, data = mail.search(None, "ALL")
ids = data[0] # data is a list.
id_list = ids.split() # ids is a space separated string
latest_email_id = id_list[-1] # get the latest
result, data = mail.fetch(latest_email_id, "(RFC822)") # fetch the email body (RFC822) for the given ID
raw_email = data[0][1] # here's the body, which is raw text of the whole email
# including headers and alternate payloads
Shamelessly stolen from here

Uku's answer looks reasonable. However, as a pragmatist, I'm going to answer a question you didn't ask, and suggest a nicer IMAP and SMTP library.
I haven't used these myself in anything other then side projects so you'll need to do your own evaluation, but both are much nicer to use.
IMAP
https://github.com/martinrusev/imbox
SMTP:
http://tomekwojcik.github.io/envelopes/

I can suggest you to use this new lib https://github.com/charlierguo/gmail
A Pythonic interface to Google's GMail, with all the tools you'll
need. Search, read and send multipart emails, archive, mark as
read/unread, delete emails, and manage labels.
Usage
from gmail import Gmail
g = Gmail()
g.login(username, password)
#get all emails
mails = g.inbox().mail()
# or if you just want your unread mails
mails = g.inbox().mail(unread=True, from="youradress#gmail.com")
g.logout()

Related

Python sends bulk mail from [BCC] insted of [TO], why?

I am writing a python script that sends every day a bulk email (connected to an outlook.com account) with the body of a news website as an ebook to multiple kindle accounts (that have already whitelisted this outlook account).
This was working so far, but since yesterday the kindle devices couldn't get the ebooks. I've tried a lot and I found out, that the problem is because all the sent emails have the receiver in the BCC part of the email. For some reason since yesterday if the receiver is on BCC, kindle doesn't receive the email (or process it).
So I am asking, what should I change on my code so I could have the receiver addresses on the [TO] part of the email instead of the [BCC]. I don't mind if it's a single email with multiple addresses or multiple single emails, as far as the receiver is not on the BCC.
receiver_email = ["email1#mail.com", "email2#mail.com", "email3#mail.com", "email4#mail.com", "email5#mail.com"]
subj = "Issue - "+fullDate
msg = MIMEMultipart('alternative')
msg['Subject'] = subj
msg['From'] = sender_email
msg.attach(MIMEText('Forward this email to your kindle account'))
# Attach the .mobi file
print("Attaching "+epubname)
fp = open(epubname, "rb")
epub_file = MIMEApplication(fp.read())
fp.close()
encoders.encode_base64(epub_file)
epub_file.add_header('Content-Disposition', 'attachment', filename=epubname)
msg.attach(epub_file)
debug = False
if debug:
print(msg.as_string())
else:
server = smtplib.SMTP('smtp.office365.com',587)
server.ehlo()
server.starttls()
server.login("##EMAIL##", "##PASSWORD##")
text = msg.as_string()+ "\r\n" + message_text
for x in range(len(receiver_email)):
email_to = receiver_email[x]
msg['To'] = email_to #msg['To'] = ", ".join(email_to)
server.sendmail(sender_email, email_to, text.encode('utf-8'))
server.quit()
I've found this question, but with no specific answer unfortunately.
Your code seems to be written for Python 3.5 or earlier. The email library was overhauled in 3.6 and is now quite a bit more versatile and logical. Probably throw away what you have and start over with the examples from the email documentation.
The simple and obvious solution is to put all the recipients in msg["to"] instead of msg["bcc"].
Here is a refactoring for Python 3.3+ (the new API was informally introduced already in 3.3).
from email.message import EmailMessage
receiver_email = ["email1#mail.com", "email2#mail.com", "email3#mail.com", "email4#mail.com", "email5#mail.com"]
subj = "Issue - "+fullDate
msg = EmailMessage()
msg['Subject'] = subj
msg['From'] = sender_email
msg['To'] = ", ".join(receiver_email)
msg.set_content('Forward this email to your Kindle account')
with open(epubname, "rb") as fp:
msg.add_attachment(fp.read()) # XXX TODO: add proper MIME type?
debug = False
if debug:
print(msg.as_string())
else:
with smtplib.SMTP('smtp.office365.com',587) as server:
server.ehlo()
server.starttls()
server.login("##EMAIL##", "##PASSWORD##")
server.send_message(msg)
This sends a single message to all the recipients; this means they can see each others' email addresses if they view the raw message. You can avoid that by going back to looping over the recipients and sending one message for each, but you really want to avoid that if you can.
As an aside, you don't need range if you don't care where you are in the list. The idiomatic way to loop over a list in Python is simply
for email_to in receiver_email:
...

Reading Gmail Email in Python

I am attempting to create a simple script to check my Gmail for emails with a certain title. When I run this program on Python 3.7.3 I receive this data: ('OK', [b'17']).
I need to access the body of the email within python. I am just not sure what to do with the data that I have.
Here is my current code:
import imaplib
import credentials
imap_ssl_host = 'imap.gmail.com'
imap_ssl_port = 993
username = credentials.email
password = credentials.passwd
server = imaplib.IMAP4_SSL(imap_ssl_host, imap_ssl_port)
server.login(username, password)
server.select('INBOX')
data = server.uid('search',None, '(SUBJECT "MY QUERY HERE!")')
print(data)
The result from running the code:
('OK', [b'17'])
I know it is a little rough, but I am still learning, so any advice you have to help me improve would be greatly appreciated!
You need to first list the contents of the selected mailbox and fetch one of the items. You could do something like this (also check the link suggested by #asynts)
imap.select('Inbox')
status, data = imap.search(None, 'ALL')
for num in data[0].split():
status, data = imap.fetch(num, '(RFC822)')
email_msg = data[0][1]
If you only want specific fields in the body, instead of parsing the RFC you could filter fields like:
status, data = imap.fetch(num, '(BODY[HEADER.FIELDS (SUBJECT DATE FROM TO)])')

How to send email to users using python?

I have to send email to users with their respective servers down.
Code:
import smtplib
import time
import re
from string import Template
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
#SMTP Credential
user = "username"
password = "pass"
server = smtplib.SMTP('smtp server')
def get_contacts(filename):
emails = []
hostname = []
ip = []
with open(filename,'r') as contacts_file:
for a_contact in contacts_file:
match = re.match('^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$', a_contact.split()[0])
if match == None:
emails.append('no_email')
hostname.append(a_contact.split()[0])
ip.append(a_contact.split()[1])
else:
emails.append(a_contact.split()[0])
hostname.append(a_contact.split()[1])
ip.append(a_contact.split()[2])
return emails, hostname, ip
def main():
emails, hostname, ip = get_contacts('provider.txt') # read contacts
# Send email to l2#abc.com if provider's support email address is not available:
for email, hostname, ip in zip(emails, hostname, ip):
match = re.match('^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$', email)
if match == None: #If provider does not have support email send email to l2#abc.com
l2_email = 'l2#abc.com'
msg = MIMEText("Hello server with main IP %s is currently down." %ip,'html')
msg['Subject'] = 'Server down %s: %s' % (hostname,ip)
msg['From'] = 'username#abc.com'
msg['To'] = l2_email
server.starttls()
server.login(user,password)
server.sendmail(msg['From'], msg['To'], msg.as_string())
##Send email to provider if support email address is found##
else:
msg = MIMEText("Hello server with main IP %s is currently down." %ip,'html')
msg['Subject'] = 'Server down %s: %s' % (hostname,ip)
msg['From'] = 'username#abc.com'
msg['To'] = email
server.starttls()
server.login(user,password)
server.sendmail(msg['From'], msg['To'], msg.as_string())
server.quit()
if __name__ == '__main__':
main()
Input:
host1 192.168.100.24
user1#abc.com host2 192.168.100.45 host7 192.168.100.40 host3 192.168.100.34 host4 192.168.100.20
user2#xyz.com host8 192.168.100.48 host6 192.168.100.43 host10 192.168.100.37
host5 192.168.100.24 host9 192.168.100.33
I want to send email to user1#abc.com with message like below:
Hello your following servers are down:
host2 192.168.100.45
host7 192.168.100.40
host3 192.168.100.34
host4 192.168.100.20
If the line does not contain email address then to specific email address like l2#abc.com with message like:
Hello your following servers are down:
host1 192.168.100.24
host5 192.168.100.24
host9 192.168.100.33
Currently script is working for only single host. Can any one please help how to implement this? Thanks in advance.
In your get_contacts() method, you only add one host and ip per line of contact information processed.
If we reduce your function to something very basic, you'll see what's going on:
contact = 'user1#abc.com host2 192.168.100.45 host7 192.168.100.40 host3 192.168.100.34 host4 192.168.100.20'
# This has an email in it, so we won't bother with the regex check.
print(contact.split()[0])
print(contact.split()[1])
print(contact.split()[2])
If you run that code snippet above, you'll get this output:
user1#abc.com
host2
192.168.100.45
It's because split() will break the entire line into a list of elements, so you're only ever going to access at most the first host and ip combination.
Or more concretely, here's print(contact.split()) in my Python shell:
>> print(contact.split())
['user1#abc.com', 'host2', '192.168.100.45', 'host7', '192.168.100.40', 'host3', '192.168.100.34', 'host4', '192.168.100.20']
For your problem, you have many ways of solving it. A basic one is:
Split the entire string
Determine if the first element is an email address
If email address is found, add that email to a dictionary, then process the rest of the list for the host-ip combinations that will be sent in the email
If no email address is found, add a default email to the dictionary, then process the rest of the list for the host-ip combinations that will be sent in the email
Here is a modified version of your code which generates a single dictionary with all the hosts bound to whatever email things should be sent to.
from collections import defaultdict
def get_contacts(filename):
contacts = defaultdict(list)
with open(filename, 'r') as contacts_file:
for line in contacts_file:
split_data = line.split()
if re.match('^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$', split_data[0]):
email = split_data[0]
# Merge every two elements in the list
# together to form a host-ip pair
hosts = [' '.join(i) for i in zip(split_data[1::2], split_data[2::2])]
else:
# Some default email here
email = 'l2#abc.com'
hosts = [' '.join(i) for i in zip(split_data[0::2], split_data[1::2])]
contacts[email].extend(hosts)
return contacts
Run this against your data, and your data will look like this:
{'l2#abc.com': ['host1 192.168.100.24',
'host5 192.168.100.24',
'host9 192.168.100.33'],
'user1#abc.com': ['host2 192.168.100.45',
'host7 192.168.100.40',
'host3 192.168.100.34',
'host4 192.168.100.20'],
'user2#xyz.com': ['host8 192.168.100.48',
'host6 192.168.100.43',
'host10 192.168.100.37']})
Then you just iterate over the structure and send emails as needed:
for email, hosts in get_contacts('provider.txt'):
# ... do what you need to do with the email address
# and list of hosts
Notice some things I've done, and some stuff that I've noticed in your code:
You kept calling split() on the same line of text every time you wanted to do a comparison in your get_contacts method. This is unnecessary - split it once and assign to a variable if you're going to keep using it.
You don't create a body for your email in your code that sends emails.
Your email regex is probably excessive, since you can't actually guarantee it's a real email. You just want to know that what you're parsing is in an email-like format, so something like [^#]+#[^#]+\.[^#]+ would probably be just as effective.

Handling Bounce in Sendgrid smtp API in Python

I am sending marketing emails through sendgrid smtp api via python EmailMultiAlternatives. I want to know how can I handle bounces directly from there to mark particular emails as undeliverable.
The code snippet is:
def send1():
text_content = 'Hi this is the text version'
connection = get_connection(host=EMAIL_HOST,
port=EMAIL_PORT,
username=EMAIL_HOST_USER,
password=EMAIL_HOST_PASSWORD,
use_tls=EMAIL_USE_TLS)
connection.open()
subject = 'Inviting {0} to join the Business Network of SMEs'.format('surya')
html_content = template.format('Surya')
from_email = 'sp#abc.com'
to = 'abc#gmail.com'
msg = EmailMultiAlternatives(subject, text_content, from_email, [to], connection=connection)
msg.attach_alternative(html_content, "text/html")
msg.send()
connection.close()
Is it possible to get the response here only after msg.send() or is there some other way.
The best way to respond to events like blocks and bounces is to implement the event webhook.
You can also poll for the data via the bounces endpoint.
So, For those who might be looking for a solution:
I am using imaplib python package to scrape the inbox (of the mail id through which i am sending the emails) everyday for bounced emails and complaints to get those unwanted emails.
def bounce():
M = imaplib.IMAP4_SSL('imap.zoho.com')
M.login('email#emailcom', password)
M.select()
line = '(HEADER Subject "Bounce")'
typ, data = M.uid('search', line)
if typ != 'OK':
return
print(len(data[0].split()))
for i in data[0].split():
result, data = M.uid('fetch', i, '(RFC822)')
raw_email = data[0][1].decode('utf-8', 'ignore')
emg = email.message_from_string(raw_email)
w = get_first_text_block(emg)
emails = re.findall(r"[a-z0-9\.\-+_]+#[a-z0-9\.\-+_]+\.[a-z]+", str(w), re.I)
So Executing the code everyday or every hour will help you.
You can also use sendgrid. The below was written using version 6.4.1.
import sendgrid
sg = sendgrid.SendGridAPIClient(api_key="<your_key>")
payload = {
"limit": 100,
"offset": 0,
}
response = sg.client.suppression.bounces.get(query_params=payload)
query_params can be omitted to accept the defaults, there's also start_time and end_time available (need to be integer Unix times).

Forwarding an email with python smtplib

I'm trying to put together a script that automatically forwards certain emails that match a specific criteria to another email.
I've got the downloading and parsing of messages using imaplib and email working, but I can't figure out how to forward an entire email to another address. Do I need to build a new message from scratch, or can I somehow modify the old one and re-send it?
Here's what I have so far (client is an imaplib.IMAP4 connection, and id is a message ID):
import smtplib, imaplib
smtp = smtplib.SMTP(host, smtp_port)
smtp.login(user, passw)
client = imaplib.IMAP4(host)
client.login(user, passw)
client.select('INBOX')
status, data = client.fetch(id, '(RFC822)')
email_body = data[0][1]
mail = email.message_from_string(email_body)
# ...Process message...
# This doesn't work
forward = email.message.Message()
forward.set_payload(mail.get_payload())
forward['From'] = 'source.email.address#domain.com'
forward['To'] = 'my.email.address#gmail.com'
smtp.sendmail(user, ['my.email.address#gmail.com'], forward.as_string())
I'm sure there's something slightly more complicated I need to be doing with regard to the MIME content of the message. Surely there's some simple way of just forwarding the entire message though?
# This doesn't work either, it just freezes...?
mail['From'] = 'source.email.address#domain.com'
mail['To'] = 'my.email.address#gmail.com'
smtp.sendmail(user, ['my.email.address#gmail.com'], mail.as_string())
I think the part you had wrong was how to replace the headers in the message, and the fact that you don't need to make a copy of the message, you can just operate directly on it after creating it from the raw data you fetched from the IMAP server.
You did omit some detail so here's my complete solution with all details spelled out. Note that I'm putting the SMTP connection in STARTTLS mode since I need that and note that I've separated the IMAP phase and the SMTP phase from each other. Maybe you thought that altering the message would somehow alter it on the IMAP server? If you did, this should show you clearly that that doesn't happen.
import smtplib, imaplib, email
imap_host = "mail.example.com"
smtp_host = "mail.example.com"
smtp_port = 587
user = "xyz"
passwd = "xyz"
msgid = 7
from_addr = "from.me#example.com"
to_addr = "to.you#example.com"
# open IMAP connection and fetch message with id msgid
# store message data in email_data
client = imaplib.IMAP4(imap_host)
client.login(user, passwd)
client.select('INBOX')
status, data = client.fetch(msgid, "(RFC822)")
email_data = data[0][1]
client.close()
client.logout()
# create a Message instance from the email data
message = email.message_from_string(email_data)
# replace headers (could do other processing here)
message.replace_header("From", from_addr)
message.replace_header("To", to_addr)
# open authenticated SMTP connection and send message with
# specified envelope from and to addresses
smtp = smtplib.SMTP(smtp_host, smtp_port)
smtp.starttls()
smtp.login(user, passwd)
smtp.sendmail(from_addr, to_addr, message.as_string())
smtp.quit()
Hope this helps even if this answer comes quite late.

Categories

Resources