├── .gitignore
├── config.py.sample
├── README.md
├── fwd_zoom.py
└── outlook.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | config.py
3 |
4 |
--------------------------------------------------------------------------------
/config.py.sample:
--------------------------------------------------------------------------------
1 | # for outlook.com
2 | imap_server = "imap-mail.outlook.com"
3 | imap_port = 993
4 | smtp_server = "smtp-mail.outlook.com"
5 | smtp_port = 587
6 | # if you choose this lib for office365.com, please comment above and uncomment below
7 | #imap_server = "outlook.office365.com"
8 | #imap_port = 993
9 | #smtp_server = "smtp.office365.com"
10 | #smtp_port = 587
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Python Outlook (Microsoft email service) Library
2 | Python Library to read email from live, hotmail, outlook or any microsoft email service, just dowload to yout python script folder. This library using Imaplib python to read email with IMAP protocol.
3 | ## Prerequisite Libraries
4 | Please make sure you have these libraries installed on your system first before running this code:
5 | * email
6 | * imaplib
7 | * smtplib
8 | * datetime
9 |
10 | then rename config.py.sample to config.py and edit comment in config.py file
11 |
12 | ## Examples
13 | ### To get latest Unread Message in inbox:
14 | ```py
15 | import outlook
16 | mail = outlook.Outlook()
17 | mail.login('emailaccount@live.com','yourpassword')
18 | mail.inbox()
19 | print mail.unread()
20 | ```
21 |
22 | ### To get latest Unread Message in Junk:
23 | ```py
24 | import outlook
25 | mail = outlook.Outlook()
26 | mail.login('emailaccount@live.com','yourpassword')
27 | mail.junk()
28 | print mail.unread()
29 | ```
30 |
31 | Use `mail.select(folder)` to switch to folders other than `inbox, `junk`.
32 | ### Retrive email element:
33 | ```py
34 | print mail.mailbody()
35 | print mail.mailsubject()
36 | print mail.mailfrom()
37 | print mail.mailto()
38 | ```
39 |
40 | ### To send Message:
41 | ```py
42 | import outlook
43 | mail = outlook.Outlook()
44 | mail.login('emailaccount@live.com','yourpassword')
45 | mail.sendEmail('recipient@email.com','subject','message body')
46 | ```
47 |
48 | ### To check Credentials:
49 | ```py
50 | import outlook
51 | mail = outlook.Outlook()
52 | mail.checkLogin()
53 | ```
54 |
55 | ### Reading e-mails from Outlook with Python through MAPI and get email with word 'skype id':
56 | ```py
57 | import Skype4Py
58 | import outlook
59 | import time
60 | import config
61 | import parser
62 |
63 | skype = Skype4Py.Skype()
64 | skype.Attach()
65 |
66 | def checkingFolder(folder):
67 | mail = outlook.Outlook()
68 | mail.login(config.outlook_email,config.outlook_password)
69 | mail.readOnly(folder)
70 | print " Looking Up "+folder
71 | try:
72 | unread_ids_today = mail.unreadIdsToday()
73 | print " unread email ids today : "
74 | print unread_ids_today
75 | unread_ids_with_word = mail.getIdswithWord(unread_ids_today,'skype id')
76 | print " unread email ids with word Skype ID today : "
77 | print unread_ids_with_word
78 | except:
79 | print config.nomail
80 | #fetch Inbox folder
81 | mail = outlook.Outlook()
82 | mail.login(config.outlook_email,config.outlook_password)
83 | mail.select(folder)
84 | try:
85 | for id_w_word in unread_ids_with_word:
86 | mail.getEmail(id_w_word)
87 | subject = mail.mailsubject()
88 | message = mail.mailbody()
89 | skypeidarr = parser.getSkype(message)
90 | print subject
91 | print skypeidarr
92 | i = 0
93 | while i < len(skypeidarr):
94 | skype.SendMessage(skypeidarr[i],config.intromsg+subject+"\r\n with Content : \r\n"+message)
95 | i += 1
96 | config.success()
97 | print " sending reply message..."
98 | print " to :"+mail.mailfrom().split('>')[0].split('<')[1]
99 | print " subject : "+subject
100 | print " content : "+config.replymessage
101 | mail.sendEmail(mail.mailfrom().split('>')[0].split('<')[1],"Re : "+subject,config.replymessage)
102 | time.sleep(10)
103 | except:
104 | print config.noword
105 | time.sleep(10)
106 |
107 | while True:
108 | #checking ids in Inbox Folder
109 | print config.checkinbox
110 | checkingFolder('Inbox')
111 | #checking Junk Folder
112 | print config.checkjunk
113 | checkingFolder('Junk')
114 |
115 | ```
116 |
117 | ### Forward zoom recording email to recipients a mailing list:
118 | Use `fwd_zoom.py`. The class `OutlookMailForwarder` defined there has the capability to filter messages based on the
119 | received time (a time window from now -- in hours), and to do string search in email subject and body. Additionaly,
120 | a filter can be defined to change the body before sending the email. For example, in `fwd_zoom.py`:
121 |
122 | ```py
123 | def filter_zoom_mailbody(mailbody):
124 | ''' Returns the link to share. This filters out other info in the email such as the host-only link'''
125 | m = re.search(r'Share recording with viewers:
\s*(.*)\b', mailbody)
126 | return m.group(1)
127 | ```
128 |
129 | To run, you can either define your `email[space]password` in `.cred` or giveemail/password in stdin upon prompt.
130 | NOTE: Do not forget to `chmod 400 ./.cred` if the former method is used.
131 |
132 | Example 1: With existing `.cred` with contents `test@example.com mypassword` and a small time window:
133 | ```
134 | ./fwd_zoom.py
135 | How many hours to llok back?1
136 | (' > Signed in as test@example.com', ['LOGIN completed.'])
137 | looking up pattern in 3/3 most recent emails in folder zoom
138 | 1 items match subject_pattern
139 | 1 items match subject_pattern and body_pattern
140 | skipping email_id 213 because its timedelta 6:31:25.556269 is greater than 1 hours
141 | ```
142 |
143 | Example 2: without a `.cred` file and a large-enough time window:
144 | ```
145 | ./fwd_zoom.py
146 | Outlook email:test@example.com
147 | Outlook Password:
148 | How many hours to look back?10
149 | (' > Signed in as test@example.com', ['LOGIN completed.'])
150 | looking up pattern in 3/3 most recent emails in folder zoom
151 | 1 items match subject_pattern
152 | 1 items match subject_pattern and body_pattern
153 | email_id 213 is within range (6:45:22.572372 < 10:00:00)
154 | maillsubject to send: Cloud Recording - Zoom Meeting is now available Tue, 12 Nov 2019 16:51:48 +0000
155 | mailbody to send: https://test.zoom.us/recording/share/4abdcsefkHergre45grdgDdafdefMWd
156 | Sending email...
157 | email sent.
158 | ```
159 |
--------------------------------------------------------------------------------
/fwd_zoom.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python2
2 |
3 | import re
4 | import getpass
5 | import outlook
6 | import datetime
7 | from pytz import timezone
8 |
9 |
10 | class OutlookMailForwarder:
11 | def __init__(self, email_addr, email_passwd, window_hours=24, folder_list=None,
12 | mailing_list=None, subject_pattern='', body_pattern='', filter_body=None):
13 | self.mail = outlook.Outlook()
14 | self.mail.login(email_addr, email_passwd)
15 | self.window_hours = window_hours
16 | self.window_days = (self.window_hours + self.window_hours % 24) / 24
17 | self.folder_list = folder_list
18 | self.mailing_list = mailing_list
19 | self.subject_pattern = subject_pattern
20 | self.body_pattern = body_pattern
21 | self.filter_body = filter_body
22 |
23 | def send_email(self, mailsubject, mailbody):
24 | if self.mailing_list is None:
25 | return
26 |
27 | if mailsubject is None:
28 | return
29 |
30 | for recipient in self.mailing_list:
31 | try:
32 | self.mail.sendEmail(recipient, mailsubject, mailbody)
33 | print('sent mail to %s' % recipient)
34 | except Exception as err:
35 | print('error sending mail to %s: %s' % (recipient, str(err)))
36 |
37 | def prepare_email(self, email_id):
38 | self.mail.getEmail(email_id)
39 | mailsubject = self.mail.mailsubject()
40 | maildate = self.mail.maildate()
41 | # filter out any date below now - window_hour
42 | # because outlook searche is limited to the beginning of the day of the now - window_days
43 | maildatetime = datetime.datetime.strptime(maildate[:-6], '%a, %d %b %Y %H:%M:%S')
44 | maildatetime.replace(tzinfo=timezone('UTC'))
45 | timedelta = datetime.datetime.utcnow() - maildatetime
46 | if timedelta > datetime.timedelta(0, 0, 0, 0, 0, self.window_hours):
47 | raise ValueError('skipping email_id %s because its timedelta %s is greater than %d hours' %
48 | (email_id, str(timedelta), self.window_hours))
49 | else:
50 | print('email_id %s is within range (%s < %s)' % (email_id, str(timedelta),
51 | str(datetime.timedelta(0, 0, 0, 0, 0,
52 | self.window_hours))))
53 | mailsubject = mailsubject + ' ' + self.mail.maildate()
54 | mailbody = self.mail.mailbody()
55 | if self.filter_body is not None:
56 | mailbody = self.filter_body(mailbody)
57 | print('maillsubject to send: %s' % mailsubject)
58 | print('mailbody to send: %s' % mailbody)
59 | return (mailsubject, mailbody)
60 |
61 | def lookup_pattern(self):
62 | if self.folder_list is None:
63 | return
64 |
65 | for folder in self.folder_list:
66 | try:
67 | self.mail.select(folder)
68 | all_ids = self.mail.allIdsSince(self.window_days)
69 | max_num = 100 if len(all_ids) > 100 else len(all_ids)
70 | print('looking up pattern in %d/%d most recent emails in folder %s' % (max_num, len(all_ids), folder))
71 | emails_with_subject_pattern = self.mail.getIdswithWord(all_ids[:max_num], self.subject_pattern)
72 | print('%d items match subject_pattern' % len(emails_with_subject_pattern))
73 | emails_match = self.mail.getIdswithWord(emails_with_subject_pattern, self.body_pattern)
74 | print('%d items match subject_pattern and body_pattern' % len(emails_match))
75 | except Exception as err:
76 | print('error looking up pattern in folder %s: %s' % (folder, str(err)))
77 | continue
78 |
79 | try:
80 | for email_id in emails_match:
81 | try:
82 | (mailsubject, mailbody) = self.prepare_email(email_id)
83 | self.send_email(mailsubject, mailbody)
84 | except ValueError as err:
85 | print('%s' % str(err))
86 | continue
87 | except Exception as err:
88 | print('error processing matched emails in folder %s: %s' % (folder, str(err)))
89 | continue
90 |
91 |
92 | def filter_zoom_mailbody(mailbody):
93 | ''' Returns the link to share. This filters out other info in the email such as the host-only link'''
94 | m = re.search(r'Share recording with viewers:
\s*(.*)\b', mailbody)
95 | return m.group(1)
96 |
97 |
98 | def main(_user, _pass, win_hours):
99 | zoom_forwarder = OutlookMailForwarder(_user, _pass, win_hours, folder_list=['zoom'],
100 | mailing_list=['test@example.com'],
101 | subject_pattern='cloud recording',
102 | body_pattern='share recording with viewers:',
103 | filter_body=filter_zoom_mailbody)
104 | zoom_forwarder.lookup_pattern()
105 |
106 |
107 | if __name__ == '__main__':
108 | try:
109 | with open('.cred', 'r') as f:
110 | userpass = f.readline()
111 | (_user, _pass) = userpass.split()
112 | except IOError:
113 | _user = raw_input('Outlook email:')
114 | _pass = getpass.getpass('Outlook Password:')
115 |
116 | win_hours = int(raw_input('How many hours to look back?'))
117 | main(_user, _pass, win_hours)
118 |
--------------------------------------------------------------------------------
/outlook.py:
--------------------------------------------------------------------------------
1 | import email
2 | import imaplib
3 | import smtplib
4 | import datetime
5 | import email.mime.multipart
6 | import config
7 | import base64
8 |
9 |
10 | class Outlook():
11 | def __init__(self):
12 | pass
13 | # self.imap = imaplib.IMAP4_SSL('imap-mail.outlook.com')
14 | # self.smtp = smtplib.SMTP('smtp-mail.outlook.com')
15 |
16 | def login(self, username, password):
17 | self.username = username
18 | self.password = password
19 | login_attempts = 0
20 | while True:
21 | try:
22 | self.imap = imaplib.IMAP4_SSL(config.imap_server,config.imap_port)
23 | r, d = self.imap.login(username, password)
24 | assert r == 'OK', 'login failed: %s' % str (r)
25 | print(" > Signed in as %s" % self.username, d)
26 | return
27 | except Exception as err:
28 | print(" > Sign in error: %s" % str(err))
29 | login_attempts = login_attempts + 1
30 | if login_attempts < 3:
31 | continue
32 | assert False, 'login failed'
33 |
34 | def sendEmailMIME(self, recipient, subject, message):
35 | msg = email.mime.multipart.MIMEMultipart()
36 | msg['to'] = recipient
37 | msg['from'] = self.username
38 | msg['subject'] = subject
39 | msg.add_header('reply-to', self.username)
40 | # headers = "\r\n".join(["from: " + "sms@kitaklik.com","subject: " + subject,"to: " + recipient,"mime-version: 1.0","content-type: text/html"])
41 | # content = headers + "\r\n\r\n" + message
42 | try:
43 | self.smtp = smtplib.SMTP(config.smtp_server, config.smtp_port)
44 | self.smtp.ehlo()
45 | self.smtp.starttls()
46 | self.smtp.login(self.username, self.password)
47 | self.smtp.sendmail(msg['from'], [msg['to']], msg.as_string())
48 | print(" email replied")
49 | except smtplib.SMTPException:
50 | print("Error: unable to send email")
51 |
52 | def sendEmail(self, recipient, subject, message):
53 | headers = "\r\n".join([
54 | "from: " + self.username,
55 | "subject: " + subject,
56 | "to: " + recipient,
57 | "mime-version: 1.0",
58 | "content-type: text/html"
59 | ])
60 | content = headers + "\r\n\r\n" + message
61 | attempts = 0
62 | while True:
63 | try:
64 | self.smtp = smtplib.SMTP(config.smtp_server, config.smtp_port)
65 | self.smtp.ehlo()
66 | self.smtp.starttls()
67 | self.smtp.login(self.username, self.password)
68 | self.smtp.sendmail(self.username, recipient, content)
69 | print(" email sent.")
70 | return
71 | except Exception as err:
72 | print(" Sending email failed: %s" % str(err))
73 | attempts = attempts + 1
74 | if attempts < 3:
75 | continue
76 | raise Exception("Send failed. Check the recipient email address")
77 |
78 | def list(self):
79 | # self.login()
80 | return self.imap.list()
81 |
82 | def select(self, str):
83 | return self.imap.select(str)
84 |
85 | def inbox(self):
86 | return self.imap.select("Inbox")
87 |
88 | def junk(self):
89 | return self.imap.select("Junk")
90 |
91 | def logout(self):
92 | return self.imap.logout()
93 |
94 | def since_date(self, days):
95 | mydate = datetime.datetime.now() - datetime.timedelta(days=days)
96 | return mydate.strftime("%d-%b-%Y")
97 |
98 | def allIdsSince(self, days):
99 | r, d = self.imap.search(None, '(SINCE "'+self.since_date(days)+'")', 'ALL')
100 | list = d[0].split(' ')
101 | return list
102 |
103 | def allIdsToday(self):
104 | return self.allIdsSince(1)
105 |
106 | def readIdsSince(self, days):
107 | r, d = self.imap.search(None, '(SINCE "'+self.date_since(days)+'")', 'SEEN')
108 | list = d[0].split(' ')
109 | return list
110 |
111 | def readIdsToday(self):
112 | return self.readIdsSince(1)
113 |
114 | def unreadIdsSince(self, days):
115 | r, d = self.imap.search(None, '(SINCE "'+self.since_date(days)+'")', 'UNSEEN')
116 | list = d[0].split(' ')
117 | return list
118 |
119 | def unreadIdsToday(self):
120 | return self.unreadIdsSince(1)
121 |
122 | def allIds(self):
123 | r, d = self.imap.search(None, "ALL")
124 | list = d[0].split(' ')
125 | return list
126 |
127 | def readIds(self):
128 | r, d = self.imap.search(None, "SEEN")
129 | list = d[0].split(' ')
130 | return list
131 |
132 | def unreadIds(self):
133 | r, d = self.imap.search(None, "UNSEEN")
134 | list = d[0].split(' ')
135 | return list
136 |
137 | def hasUnread(self):
138 | list = self.unreadIds()
139 | return list != ['']
140 |
141 | def getIdswithWord(self, ids, word):
142 | stack = []
143 | for id in ids:
144 | self.getEmail(id)
145 | if word in self.mailbody().lower():
146 | stack.append(id)
147 | return stack
148 |
149 | def getEmail(self, id):
150 | r, d = self.imap.fetch(id, "(RFC822)")
151 | self.raw_email = d[0][1]
152 | self.email_message = email.message_from_string(self.raw_email)
153 | return self.email_message
154 |
155 | def unread(self):
156 | list = self.unreadIds()
157 | latest_id = list[-1]
158 | return self.getEmail(latest_id)
159 |
160 | def read(self):
161 | list = self.readIds()
162 | latest_id = list[-1]
163 | return self.getEmail(latest_id)
164 |
165 | def readToday(self):
166 | list = self.readIdsToday()
167 | latest_id = list[-1]
168 | return self.getEmail(latest_id)
169 |
170 | def unreadToday(self):
171 | list = self.unreadIdsToday()
172 | latest_id = list[-1]
173 | return self.getEmail(latest_id)
174 |
175 | def readOnly(self, folder):
176 | return self.imap.select(folder, readonly=True)
177 |
178 | def writeEnable(self, folder):
179 | return self.imap.select(folder, readonly=False)
180 |
181 | def rawRead(self):
182 | list = self.readIds()
183 | latest_id = list[-1]
184 | r, d = self.imap.fetch(latest_id, "(RFC822)")
185 | self.raw_email = d[0][1]
186 | return self.raw_email
187 |
188 | def mailbody(self):
189 | if self.email_message.is_multipart():
190 | for payload in self.email_message.get_payload():
191 | # if payload.is_multipart(): ...
192 | body = (
193 | payload.get_payload()
194 | .split(self.email_message['from'])[0]
195 | .split('\r\n\r\n2015')[0]
196 | )
197 | return body
198 | else:
199 | body = (
200 | self.email_message.get_payload()
201 | .split(self.email_message['from'])[0]
202 | .split('\r\n\r\n2015')[0]
203 | )
204 | return body
205 |
206 | def mailsubject(self):
207 | return self.email_message['Subject']
208 |
209 | def mailfrom(self):
210 | return self.email_message['from']
211 |
212 | def mailto(self):
213 | return self.email_message['to']
214 |
215 | def maildate(self):
216 | return self.email_message['date']
217 |
218 | def mailreturnpath(self):
219 | return self.email_message['Return-Path']
220 |
221 | def mailreplyto(self):
222 | return self.email_message['Reply-To']
223 |
224 | def mailall(self):
225 | return self.email_message
226 |
227 | def mailbodydecoded(self):
228 | return base64.urlsafe_b64decode(self.mailbody())
229 |
--------------------------------------------------------------------------------