Skip to main content
4 of 5
added 83 characters in body
Graipher
  • 41.1k
  • 7
  • 69
  • 133

My main comment is that you should separate the different concerns of you code into descriptive functions. This will make it a lot more readable (and re-usable).

One comment before I get started: Your login function, which looks like it would log you in with the reddit API seems to be unused atm. I guess this is a copy&paste error from censoring?


Your first concern is to log-in with gmail to get the mail object. This can be pasted directly into a separate function:

def mail_login():
    mail = imaplib.IMAP4_SSL('imap.gmail.com')
    while True:
        try:
            emailpass = getpass.getpass(
                'Please enter the password for xxxxxxx@xxxxx: ')
            mail.login('[email protected]', emailpass)
            break
        except imaplib.IMAP4.error:
            print('Incorrect password')
    mail.select("inbox")
    return mail

The second task, which is repeated quite often, is to print a message with the current timestamp preceeding it:

def log(text):
    print('\t{:%Y-%m-%d %H:%M:%S} - {}'.format(datetime.now(), text))

Note that I used the custom format options of str.format here.


Another task is to post an alert to reddit, once it is found:

def post_alert(alert, r):
    alert_text = """
    {}

     ********

     *^^I ^^am ^^a ^^bot. ^^For ^^any ^^questions, ^^comments, ^^or ^^concerns, ^^please ^^email [^^xxxxxxx@xxxxx](mailto://xxxxxxx@xxxxx)*
     """
    while True:
        try:
            r.submit(subreddit='xxxxxxxxxxxx', title=alert,
                     text=alert_text.format(alert))
            print('\t{}'.format(alert), end=' ')
            break
        except praw.errors.ExceptionList as e:
            log('Reddit error! {}'.format(e))
            time.sleep(300)

I build the alert text first and filled it with str.format and used the log function.


The second to last task is to search in your emails for new messages and yield all alert texts:

class ShortTimeOut(Exception):
    pass


def search_for_alerts(mail):
    result, response = mail.uid('search', None, "(UNSEEN)")
    unread_msgs = response[0].split()

    if not unread_msgs:
        raise ShortTimeOut
    else:
        log('Something\'s wrong!')
        result, data = mail.uid('fetch', unread_msgs[-1], '(RFC822)')
        email_message = email.message_from_bytes(data[0][1])

        for part in email_message.walk():
            if part.get_content_type() == 'text/plain':
                yield part.get_payload()

I yield the email contents (to be iterated over in the outer scope). I also added a custom exception to allow handling the short timeout in main.

It seems to me like you did one request to many. After your first request you already have a list of all unseen emails, the last of which is the latest email. So there should be no need to do another request here.

Lastly, I re-ordered the logic, so that if there are no new messages, no further requests are made.


The last function is a main function, which calls all the other functions. It is executed in a if __name__ == "__main__": guard to allow importing your code from other scripts:

def main():
    while True:
        try:
            r = RUAlerts.login()
            mail = mail_login()
            try:
                for alert in search_for_alerts(mail):
                    post_alert(alert, r)
            except ShortTimeOut:
                log('All clear on the RU front')
                time.sleep(30)

        except Exception as e:
            log('{} Retrying in 5 minutes'.format(e))
            time.sleep(300)


if __name__ == "__main__":
    main()

Final code:

import praw
import imaplib
import email
import time
import getpass
import RUAlerts
from datetime import datetime

class ShortTimeOut(Exception):
    pass

app_id = 'xxxxxxxxxxxx'
app_secret = 'xxxxxxxxxxxx'
app_uri = 'xxxxxxxxxxxx'
app_scopes = 'account creddits edit flair history identity mysubreddits privatemessages read report save submit subscribe vote'
app_ua = 'xxxxxxxxxxxx'
app_account_code = 'xxxxxxxxxxxx'
app_refresh = 'xxxxxxxxxxxx'


def login():
    r = praw.Reddit(app_ua)
    r.set_oauth_app_info(app_id, app_secret, app_uri)
    r.refresh_access_information(app_refresh)
    return r


def log(text):
    print('\t{:%Y-%m-%d %H:%M:%S} - {}'.format(datetime.now(), text))


def mail_login():
    mail = imaplib.IMAP4_SSL('imap.gmail.com')
    while True:
        try:
            emailpass = getpass.getpass(
                'Please enter the password for xxxxxxx@xxxxx: ')
            mail.login('[email protected]', emailpass)
            break
        except imaplib.IMAP4.error:
            print('Incorrect password')
    mail.select("inbox")
    return mail


def post_alert(alert, r):
    alert_text = """
    {}

     ********

     *^^I ^^am ^^a ^^bot. ^^For ^^any ^^questions, ^^comments, ^^or ^^concerns, ^^please ^^email [^^xxxxxxx@xxxxx](mailto://xxxxxxx@xxxxx)*
     """
    while True:
        try:
            r.submit(subreddit='xxxxxxxxxxxx', title=alert,
                     text=alert_text.format(alert))
            print('\t{}'.format(alert), end=' ')
            break
        except praw.errors.ExceptionList as e:
            log('Reddit error! {}'.format(e))
            time.sleep(300)



def search_for_alerts(mail):
    result, response = mail.uid('search', None, "(UNSEEN)")
    unread_msgs = response[0].split()

    if not unread_msgs:
        raise ShortTimeOut
    else:
        log('Something\'s wrong!')
        result, data = mail.uid('fetch', unread_msgs[-1], '(RFC822)')
        email_message = email.message_from_bytes(data[0][1])

        for part in email_message.walk():
            if part.get_content_type() == 'text/plain':
                yield part.get_payload()

def main():
    while True:
        try:
            r = RUAlerts.login()
            mail = mail_login()
            try:
                for alert in search_for_alerts(mail):
                    post_alert(alert, r)
            except ShortTimeOut:
                log('All clear on the RU front')
                time.sleep(30)
    
        except Exception as e:
            log("{}! Retrying in 5 minutes".format(e))
            time.sleep(300)

if __name__ == "__main__":
    main()
Graipher
  • 41.1k
  • 7
  • 69
  • 133