├── .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 | --------------------------------------------------------------------------------