Processing Email – Single Script / Multiple Emails

Google API Gmail LINE Notifications (7 Part Series)

1 Connecting to Gmail API with Python
2 Managing your Labels in Gmail
3 more parts…
3 Searching for and Getting Emails
4 Processing Email Contents
5 Processing Email – Being Selective
6 Processing Email – Single Script / Multiple Emails
7 Automation with Systemd & Code

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.

  1. Check the subject line. If it exist in the list of known subject lines process the email.
  2. If 1 true. Double check that the sending address is an approved/known sending address.
  3. If 1 is false. Log an issue (This should not occur due to the filtering which is being done within Gmail.)
  4. If 1 is true, but the sending address is false. Log the message.
  5. If both 1 and 2 are true. This is a valid notification email.
    1. Determine which notification is being handled using the Sender’s Address.
    2. Apply the correct regex expression(s) to get the information
    3. Send the notification and log its successful handling.
    4. Attach the notified label to the email.

Organization

constants.py

<span>SUB_1</span> <span>=</span> <span>"something"</span>
<span>SUB_2</span> <span>=</span> <span>"something"</span>
<span>SUBJECTS</span> <span>=</span> <span>[</span><span>SUB_1</span><span>,</span> <span>SUB_2</span><span>]</span>
<span>FROM_BUS</span> <span>=</span> <span>"<email address of sender>"</span>
<span>FROM_KIDZDUO</span> <span>=</span> <span>"<email address of sender>"</span>
<span>SUB_1</span> <span>=</span> <span>"something"</span>
<span>SUB_2</span> <span>=</span> <span>"something"</span>

<span>SUBJECTS</span> <span>=</span> <span>[</span><span>SUB_1</span><span>,</span> <span>SUB_2</span><span>]</span>

<span>FROM_BUS</span> <span>=</span> <span>"<email address of sender>"</span>
<span>FROM_KIDZDUO</span> <span>=</span> <span>"<email address of sender>"</span>
SUB_1 = "something" SUB_2 = "something" SUBJECTS = [SUB_1, SUB_2] FROM_BUS = "<email address of sender>" FROM_KIDZDUO = "<email address of sender>"

Enter fullscreen mode Exit fullscreen mode

gmail.py

I have intentionally used a somewhat defensive coding style here.

The code look something like this

<span>def</span> <span>handle_each_email</span><span>(</span><span>service</span><span>,</span> <span>message_id</span><span>,</span> <span>logger</span><span>)</span> <span>-></span> <span>tuple</span><span>:</span>
<span>data</span> <span>=</span> <span>notifier</span> <span>=</span> <span>None</span>
<span>single_email</span> <span>=</span> <span>get_message</span><span>(</span><span>service</span><span>,</span> <span>message_id</span><span>,</span> <span>logger</span><span>)</span>
<span># Check the subject is an expected notification subject line </span> <span>subject</span> <span>=</span> <span>single_email</span><span>.</span><span>get</span><span>(</span><span>"subject"</span><span>)</span>
<span>if</span> <span>subject</span> <span>in</span> <span>constants</span><span>.</span><span>SUBJECTS</span><span>:</span>
<span># workout which notification we are dealing with and use the correct </span> <span># regular expression string </span> <span>sender</span> <span>=</span> <span>single_email</span><span>.</span><span>get</span><span>(</span><span>"from"</span><span>)</span>
<span>email_body</span> <span>=</span> <span>single_email</span><span>.</span><span>get_content</span><span>()</span>
<span>if</span> <span>sender</span> <span>==</span> <span>constants</span><span>.</span><span>FROM_BUS</span><span>:</span>
<span>data</span> <span>=</span> <span>patterns</span><span>.</span><span>findMatches</span><span>(</span><span>email_body</span><span>,</span>
<span>patterns</span><span>.</span><span>BUS_DATA</span><span>)</span>
<span>datetime</span> <span>=</span> <span>patterns</span><span>.</span><span>findMatches</span><span>(</span><span>email_body</span><span>,</span>
<span>patterns</span><span>.</span><span>BUS_DATE_TIME</span><span>)</span>
<span># Merge data and datetime into a single dictionary </span> <span>data</span><span>.</span><span>update</span><span>(</span><span>datetime</span><span>)</span>
<span>notifier</span> <span>=</span> <span>"BUS"</span>
<span>elif</span> <span>sender</span> <span>==</span> <span>constants</span><span>.</span><span>FROM_KIDZDUO</span><span>:</span>
<span>data</span> <span>=</span> <span>patterns</span><span>.</span><span>findMatches</span><span>(</span><span>email_body</span><span>,</span>
<span>patterns</span><span>.</span><span>KIDZDUO_ENTEREXIT</span><span>)</span>
<span>notifier</span> <span>=</span> <span>"KIDZDUO"</span>
<span>else</span><span>:</span>
<span># This needs to be logged. Means failed to match sender. </span> <span># if notifier is None: </span> <span>logger</span><span>.</span><span>warning</span><span>(</span><span>f</span><span>"Failed to process message_id: </span><span>{</span><span>message_id</span><span>}</span><span> "</span>
<span>f</span><span>"Matched Subject: </span><span>{</span><span>subject</span><span>}</span><span> "</span>
<span>f</span><span>"Sender not matched: </span><span>{</span><span>sender</span><span>}</span><span>"</span><span>)</span>
<span>pass</span>
<span>return</span> <span>notifier</span><span>,</span> <span>data</span>
<span>else</span><span>:</span>
<span># Not an expected subject line. Ignore this email </span> <span>logger</span><span>.</span><span>info</span><span>(</span><span>"This is not a notifcation."</span><span>)</span>
<span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"sender: </span><span>{</span><span>sender</span><span>}</span><span>\n\t</span><span>"</span><span>)</span>
<span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"subject: </span><span>{</span><span>subject</span><span>}</span><span>"</span><span>)</span>
<span>return</span> <span>notifier</span><span>,</span> <span>data</span>
<span>def</span> <span>handle_each_email</span><span>(</span><span>service</span><span>,</span> <span>message_id</span><span>,</span> <span>logger</span><span>)</span> <span>-></span> <span>tuple</span><span>:</span>
    <span>data</span> <span>=</span> <span>notifier</span> <span>=</span> <span>None</span>
    <span>single_email</span> <span>=</span> <span>get_message</span><span>(</span><span>service</span><span>,</span> <span>message_id</span><span>,</span> <span>logger</span><span>)</span>
    <span># Check the subject is an expected notification subject line </span>    <span>subject</span> <span>=</span> <span>single_email</span><span>.</span><span>get</span><span>(</span><span>"subject"</span><span>)</span>
    <span>if</span> <span>subject</span> <span>in</span> <span>constants</span><span>.</span><span>SUBJECTS</span><span>:</span>
        <span># workout which notification we are dealing with and use the correct </span>        <span># regular expression string </span>        <span>sender</span> <span>=</span> <span>single_email</span><span>.</span><span>get</span><span>(</span><span>"from"</span><span>)</span>
        <span>email_body</span> <span>=</span> <span>single_email</span><span>.</span><span>get_content</span><span>()</span>
        <span>if</span> <span>sender</span> <span>==</span> <span>constants</span><span>.</span><span>FROM_BUS</span><span>:</span>
            <span>data</span> <span>=</span> <span>patterns</span><span>.</span><span>findMatches</span><span>(</span><span>email_body</span><span>,</span>
                                        <span>patterns</span><span>.</span><span>BUS_DATA</span><span>)</span>
            <span>datetime</span> <span>=</span> <span>patterns</span><span>.</span><span>findMatches</span><span>(</span><span>email_body</span><span>,</span>
                                            <span>patterns</span><span>.</span><span>BUS_DATE_TIME</span><span>)</span>
            <span># Merge data and datetime into a single dictionary </span>            <span>data</span><span>.</span><span>update</span><span>(</span><span>datetime</span><span>)</span>
            <span>notifier</span> <span>=</span> <span>"BUS"</span>
        <span>elif</span> <span>sender</span> <span>==</span> <span>constants</span><span>.</span><span>FROM_KIDZDUO</span><span>:</span>
            <span>data</span> <span>=</span> <span>patterns</span><span>.</span><span>findMatches</span><span>(</span><span>email_body</span><span>,</span>
                                        <span>patterns</span><span>.</span><span>KIDZDUO_ENTEREXIT</span><span>)</span>
            <span>notifier</span> <span>=</span> <span>"KIDZDUO"</span>
        <span>else</span><span>:</span>
            <span># This needs to be logged. Means failed to match sender. </span>            <span># if notifier is None: </span>            <span>logger</span><span>.</span><span>warning</span><span>(</span><span>f</span><span>"Failed to process message_id: </span><span>{</span><span>message_id</span><span>}</span><span> "</span>
                           <span>f</span><span>"Matched Subject: </span><span>{</span><span>subject</span><span>}</span><span> "</span>
                           <span>f</span><span>"Sender not matched: </span><span>{</span><span>sender</span><span>}</span><span>"</span><span>)</span>
            <span>pass</span>
        <span>return</span> <span>notifier</span><span>,</span> <span>data</span>
    <span>else</span><span>:</span>
        <span># Not an expected subject line. Ignore this email </span>        <span>logger</span><span>.</span><span>info</span><span>(</span><span>"This is not a notifcation."</span><span>)</span>
        <span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"sender: </span><span>{</span><span>sender</span><span>}</span><span>\n\t</span><span>"</span><span>)</span>
        <span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"subject: </span><span>{</span><span>subject</span><span>}</span><span>"</span><span>)</span>
        <span>return</span> <span>notifier</span><span>,</span> <span>data</span>
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

Enter fullscreen mode Exit fullscreen mode

The break down

  1. First we set data and notifier to None
  2. Get the full email
  3. Get the subject line
  4. Check that the subject line is one of the expect subject strings
  5. Get the sending address and email body
  6. Check who the sender is:
    1. If it’s the bus company. set data using the bus regex and set notifier to “BUS”
    2. If it’s Kidsduo. Set the data using the kidzduo regex and set nofifer to “KIDZDUO”
    3. Otherwise log the warning and return notifier and data
  7. 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.

<span>for</span> <span>message_id</span> <span>in</span> <span>list_of_message_ids</span><span>:</span>
<span>processed</span> <span>=</span> <span>False</span>
<span>notifier</span><span>,</span> <span>data</span> <span>=</span> <span>handle_each_email</span><span>(</span><span>service</span><span>,</span> <span>message_id</span><span>,</span> <span>logger</span><span>)</span>
<span># Notifier tells us how the data dict is structured </span> <span>if</span> <span>notifier</span> <span>==</span> <span>"BUS"</span><span>:</span>
<span>logger</span><span>.</span><span>debug</span><span>(</span><span>"Bus"</span><span>)</span>
<span># Your notification / bot code here </span> <span>processed</span> <span>=</span> <span>True</span>
<span>elif</span> <span>notifier</span> <span>==</span> <span>"KIDZDUO"</span><span>:</span>
<span>logger</span><span>.</span><span>debug</span><span>(</span><span>"KidsDuo"</span><span>)</span>
<span># Your notification / bot code here </span> <span>processed</span> <span>=</span> <span>True</span>
<span>elif</span> <span>notifier</span> <span>is</span> <span>None</span> <span>and</span> <span>data</span> <span>is</span> <span>not</span> <span>None</span><span>:</span>
<span>logger</span><span>.</span><span>warning</span><span>(</span><span>f</span><span>"Subject matched but From was not matched"</span><span>)</span>
<span>elif</span> <span>notifier</span> <span>is</span> <span>None</span> <span>and</span> <span>data</span> <span>is</span> <span>None</span><span>:</span>
<span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"Non-Notification email from expected sender"</span><span>)</span>
<span>else</span><span>:</span>
<span># We should not get here. But log it. </span> <span>logger</span><span>.</span><span>warning</span><span>(</span>
<span>f</span><span>"Something went wrong. Unexpected match."</span>
<span>f</span><span>"Don't know how to handle data."</span>
<span>)</span>
<span>if</span> <span>processed</span><span>:</span>
<span># Mail was processed. Add label so it's not processed again </span> <span># Gmail only allows for the shortest time interval of one day. </span> <span>logger</span><span>.</span><span>debug</span><span>(</span><span>"adding label"</span><span>)</span>
<span>add_label_to_message</span><span>(</span><span>service</span><span>,</span> <span>message_id</span><span>,</span> <span>secrets</span><span>.</span><span>LABEL_ID</span><span>)</span>
<span># End of the program </span><span>logger</span><span>.</span><span>info</span><span>(</span><span>"Ending cleanly"</span><span>)</span>
<span>for</span> <span>message_id</span> <span>in</span> <span>list_of_message_ids</span><span>:</span>
    <span>processed</span> <span>=</span> <span>False</span>
    <span>notifier</span><span>,</span> <span>data</span> <span>=</span> <span>handle_each_email</span><span>(</span><span>service</span><span>,</span> <span>message_id</span><span>,</span> <span>logger</span><span>)</span>
    <span># Notifier tells us how the data dict is structured </span>    <span>if</span> <span>notifier</span> <span>==</span> <span>"BUS"</span><span>:</span>
        <span>logger</span><span>.</span><span>debug</span><span>(</span><span>"Bus"</span><span>)</span>
        <span># Your notification / bot code here </span>        <span>processed</span> <span>=</span> <span>True</span>
    <span>elif</span> <span>notifier</span> <span>==</span> <span>"KIDZDUO"</span><span>:</span>
        <span>logger</span><span>.</span><span>debug</span><span>(</span><span>"KidsDuo"</span><span>)</span>
        <span># Your notification / bot code here </span>        <span>processed</span> <span>=</span> <span>True</span>
    <span>elif</span> <span>notifier</span> <span>is</span> <span>None</span> <span>and</span> <span>data</span> <span>is</span> <span>not</span> <span>None</span><span>:</span>
        <span>logger</span><span>.</span><span>warning</span><span>(</span><span>f</span><span>"Subject matched but From was not matched"</span><span>)</span>
    <span>elif</span> <span>notifier</span> <span>is</span> <span>None</span> <span>and</span> <span>data</span> <span>is</span> <span>None</span><span>:</span>
        <span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"Non-Notification email from expected sender"</span><span>)</span>
    <span>else</span><span>:</span>
        <span># We should not get here. But log it. </span>        <span>logger</span><span>.</span><span>warning</span><span>(</span>
                       <span>f</span><span>"Something went wrong. Unexpected match."</span>
                       <span>f</span><span>"Don't know how to handle data."</span>
                      <span>)</span>
    <span>if</span> <span>processed</span><span>:</span>
        <span># Mail was processed. Add label so it's not processed again </span>        <span># Gmail only allows for the shortest time interval of one day. </span>        <span>logger</span><span>.</span><span>debug</span><span>(</span><span>"adding label"</span><span>)</span>
        <span>add_label_to_message</span><span>(</span><span>service</span><span>,</span> <span>message_id</span><span>,</span> <span>secrets</span><span>.</span><span>LABEL_ID</span><span>)</span>
<span># End of the program </span><span>logger</span><span>.</span><span>info</span><span>(</span><span>"Ending cleanly"</span><span>)</span>
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")

Enter fullscreen mode Exit fullscreen mode

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.

Google API Gmail LINE Notifications (7 Part Series)

1 Connecting to Gmail API with Python
2 Managing your Labels in Gmail
3 more parts…
3 Searching for and Getting Emails
4 Processing Email Contents
5 Processing Email – Being Selective
6 Processing Email – Single Script / Multiple Emails
7 Automation with Systemd & Code

原文链接:Processing Email – Single Script / Multiple Emails

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
Happiness isn't about getting what you want all the time, it's about loving what you have.
幸福并不是一味得到自己想要的,而是珍爱自己拥有的
评论 抢沙发

请登录后发表评论

    暂无评论内容