Part 6 in a series of articles on implementing a notification system using Gmail and Line Bot
Greetings. In this article I will be going over the thought process of how I handled multiple emails in a single script.
In most cases there will only be a single email that needs to be processed at any given time. But to allow for more flexibility and to have a single place of control, and unfortunately complexity, I have decided to use a single script.
Some background
The services I receive email from have some well defined static attributes.
- Sending Address
- Subject Line
About once a month they will also send billing information using the same Sending Address which can be ignored by the script.
Because of this; the logic will be something along the lines of.
- Check the subject line. If it exist in the list of known subject lines process the email.
- If 1 true. Double check that the sending address is an approved/known sending address.
- If 1 is false. Log an issue (This should not occur due to the filtering which is being done within Gmail.)
- If 1 is true, but the sending address is false. Log the message.
- If both 1 and 2 are true. This is a valid notification email.
- Determine which notification is being handled using the Sender's Address.
- Apply the correct regex expression(s) to get the information
- Send the notification and log its successful handling.
- Attach the notified label to the email.
Organization
constants.py
SUB_1 = "something" SUB_2 = "something" SUBJECTS = [SUB_1, SUB_2] FROM_BUS = "<email address of sender>" FROM_KIDZDUO = "<email address of sender>"
gmail.py
I have intentionally used a somewhat defensive coding style here.
The code look something like this
def handle_each_email(service, message_id, logger) -> tuple: data = notifier = None single_email = get_message(service, message_id, logger) # Check the subject is an expected notification subject line subject = single_email.get("subject") if subject in constants.SUBJECTS: # workout which notification we are dealing with and use the correct # regular expression string sender = single_email.get("from") email_body = single_email.get_content() if sender == constants.FROM_BUS: data = patterns.findMatches(email_body, patterns.BUS_DATA) datetime = patterns.findMatches(email_body, patterns.BUS_DATE_TIME) # Merge data and datetime into a single dictionary data.update(datetime) notifier = "BUS" elif sender == constants.FROM_KIDZDUO: data = patterns.findMatches(email_body, patterns.KIDZDUO_ENTEREXIT) notifier = "KIDZDUO" else: # This needs to be logged. Means failed to match sender. # if notifier is None: logger.warning(f"Failed to process message_id: {message_id} " f"Matched Subject: {subject} " f"Sender not matched: {sender}") pass return notifier, data else: # Not an expected subject line. Ignore this email logger.info("This is not a notifcation.") logger.info(f"sender: {sender}\n\t") logger.info(f"subject: {subject}") return notifier, data
The break down
- First we set data and notifier to None
- Get the full email
- Get the subject line
- Check that the subject line is one of the expect subject strings
- Get the sending address and email body
- Check who the sender is:
- If it's the bus company. set data using the bus regex and set notifier to "BUS"
- If it's Kidsduo. Set the data using the kidzduo regex and set nofifer to "KIDZDUO"
- Otherwise log the warning and return notifier and data
- If item 4 above is false, log an info message and return notifier and data both of which should be None
Main loop
Let's see how we will process this in a batch form if needed.
for message_id in list_of_message_ids: processed = False notifier, data = handle_each_email(service, message_id, logger) # Notifier tells us how the data dict is structured if notifier == "BUS": logger.debug("Bus") # Your notification / bot code here processed = True elif notifier == "KIDZDUO": logger.debug("KidsDuo") # Your notification / bot code here processed = True elif notifier is None and data is not None: logger.warning(f"Subject matched but From was not matched") elif notifier is None and data is None: logger.info(f"Non-Notification email from expected sender") else: # We should not get here. But log it. logger.warning( f"Something went wrong. Unexpected match." f"Don't know how to handle data." ) if processed: # Mail was processed. Add label so it's not processed again # Gmail only allows for the shortest time interval of one day. logger.debug("adding label") add_label_to_message(service, message_id, secrets.LABEL_ID) # End of the program logger.info("Ending cleanly")
Note that there are several checks that should probably be made. The return of add_label_to_message()
for example.
I will be making the code available in GitHub later. Along with the systemd configuration I used to have the script run at select times of the day and week. Watch for the future post on this.
Top comments (0)