Skip to content

Commit

Permalink
Train Notification Support
Browse files Browse the repository at this point in the history
Add support for train notifications and code clean up

Code clean up

Adding documentation

Documentation
  • Loading branch information
Bas-Man committed Sep 21, 2021
1 parent 2dc2bec commit 69f8d82
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 38 deletions.
23 changes: 6 additions & 17 deletions gmail-sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,12 @@
'https://www.googleapis.com/auth/gmail.labels']


def setupLogging():
def setup_logging():
# supress google discovery_cache logging
# https://github.com/googleapis/google-api-python-client/issues/299
logging.getLogger('googleapiclient.discovery_cache').setLevel(
logging.ERROR)
logger = logging.getLogger("gmail-notifier")
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# warning_handler = logging.FileHandler("gnotifier.log")
# warning_handler.setLevel(logging.WARNING)
# warning_handler.setFormatter(formatter)
# info_handler = logging.FileHandler("gnotifier.log")
# info_handler.setLevel(logging.INFO)
# info_handler.setFormatter(formatter)
# logger.addHandler(warning_handler)
# logger.addHandler(info_handler)
logging.basicConfig(
handlers=[RotatingFileHandler(LOG_FILE,
maxBytes=100000,
Expand Down Expand Up @@ -224,7 +213,7 @@ def handle_each_email(service, message_id, logger) -> tuple:


def main():
logger = setupLogging()
logger = setup_logging()
logger.info("Looking for email for notification")
service = get_service()
# Stage 1 Check Connection
Expand Down Expand Up @@ -267,13 +256,13 @@ def main():
# Your custom code goes here. Bot / Line
processed = True
elif notifier is None and data is not None:
logger.warning(f"Subject matched but From was not matched")
logger.warning("Subject matched but From was not matched")
elif notifier is None and data is None:
logger.info(f"Non-Notification email from expected sender")
logger.info("Non-Notification email from expected sender")
else:
# We should not get here. But log it.
logger.warning(f"Something went wrong. Unexpected match. "
f"Dont know how to handle data."
logger.warning("Something went wrong. Unexpected match. "
"Dont know how to handle data."
)
if processed:
# Mail was processed. Add label so its not processed again
Expand Down
98 changes: 77 additions & 21 deletions gmail.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
""" A program to send notification through LINE based on Email received."""
import pickle
import os.path
import sys
Expand All @@ -8,12 +9,12 @@
from email import parser
from email import policy
import secrets
import patterns
import constants
from line_notify import LineNotify
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from line_notify import LineNotify
import constants
import patterns

ACCESS_TOKEN = secrets.LINE_TOKEN
LOG_FILE = "gnotifier.log"
Expand All @@ -26,7 +27,7 @@

def setup_logging():
""" Setup logging for applciation."""
# supress google discovery_cache logging
# suopress google discovery_cache logging
# https://github.com/googleapis/google-api-python-client/issues/299
logging.getLogger('googleapiclient.discovery_cache').setLevel(
logging.ERROR)
Expand All @@ -45,14 +46,18 @@ def send_notification(message):
"""
Send LINE notification
:param message: The message to be sent via LINE
:type: str:
:type str:
"""
notice = LineNotify(ACCESS_TOKEN)
notice.send(message)


def get_service():
"""Connect to the Gmail API."""
"""
Connect to the Gmail API.
:return: Return a connection to the Google API Service.
:rtype object:
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
Expand Down Expand Up @@ -83,7 +88,9 @@ def get_labels(service) -> list:
"""
Get all gmail labels.
:param service: Gmail service connection object
:type list:
:type objectt:
:return: List of Gmail Labels
:rtype: list
"""
list_of_labels = service.users().labels().list(userId='me').execute()
return list_of_labels.get('labels')
Expand Down Expand Up @@ -127,20 +134,38 @@ def define_label(name, mlv="show", llv="labelShow") -> dict:
return label


def add_label_to_gmail(service, label) -> dict:
""" Add new label to gmail system. """
def register_label_with_gmail(service, label) -> dict:
"""
Register the provide label with the gmail system.
:param service: The gmail service
:type service: object
:param label: Label to be registered
:type label: dict
:return: The label and associated details from gmail.
:rtype: dict
"""

created_label = service.users().labels().create(userId='me',
body=label).execute()
return created_label


def get_new_label_id(new_label) -> str:
""" obtain new label using id. """
return new_label.get('id')
def get_label_id(label) -> str:
"""
obtain new label using id.
:param label: Gmail label
:type label: dict
:return: The id of the label passed in to the function
:rtype: str
"""
return label.get('id')


def get_folders(service, logger):
""" Get a list of Gmail folders. """
def get_folders(service, logger) -> None:
"""
Get a list of Gmail folders.
Writes information to the log file.
"""
# Call the Gmail API
try:
results = service.users().labels().list(userId='me').execute()
Expand All @@ -159,10 +184,18 @@ def get_folders(service, logger):


def get_message_ids(service, search_string) -> dict:

search = service.users().messages().list(userId='me',
q=search_string).execute()
return search
"""
Searchs Gmail for any messages that match the search string provided.
:param service: The Gmail API connection.
:type service: object
:param search_string: The Gmail search string to use.
:type search_string: str
:return: A dictionary of messages that match the search string.
:rytpe: dict
"""
message_ids = service.users().messages().list(userId='me',
q=search_string).execute()
return message_ids


def found_messages(message_ids) -> bool:
Expand All @@ -182,22 +215,45 @@ def get_only_message_ids(message_ids) -> list:


def get_message(service, msg_id, logger) -> email.message.EmailMessage:
"""
Retrive the email message assicated with the given msg_id
:param service: Gmail API connection
:type service: object
:param msg_id: The id for the requested message.
:type msg_id: str
:param logger: Logger to pass information.
:type logger: object
:return: The Email message referenced by mss_id
:rtype: email.message.EmailMessage
"""
try:
msg = service.users().messages().get(userId='me',
id=msg_id,
format='raw').execute()
msg_in_bytes = base64.urlsafe_b64decode(msg['raw'].encode('ASCII'))
email_tmp = email.message_from_bytes(msg_in_bytes,
policy=policy.default)
emailParser = parser.Parser(policy=policy.default)
resulting_email = emailParser.parsestr(email_tmp.as_string())
email_parser = parser.Parser(policy=policy.default)
resulting_email = email_parser.parsestr(email_tmp.as_string())
return resulting_email
except (errors.HttpError, error):
logger.error(f"Unable to get message for {msg_id}")
return None


def handle_each_email(service, message_id, logger) -> tuple:
"""
Process each message and extract who the notifier is as well as the data
in the body of the email
:param service: Gmail API connection
:type service: object
:param message_id: The id of the message to be processed.
:type message_id: str
:param logger: The logger object
:type logger: object
:return: The data extracted from the email. Notifier and data.
:rtype: tuple
"""
data = notifier = None
single_email = get_message(service, message_id, logger)
# Check the subject is an expected notification subject line
Expand Down Expand Up @@ -284,7 +340,7 @@ def main():
status = "Exited"

message = (f"{secrets.NAME} Train Notification.\n"
f"{data['date']}"
f"{data['date']} at {data['time']}\n"
f"{status} {data['station']}"
)
send_notification(message)
Expand Down

0 comments on commit 69f8d82

Please sign in to comment.