From 16716ec44221d65d5edb0550dc2c7c9192cd201b Mon Sep 17 00:00:00 2001 From: Filip Hein <34664324+f-hein@users.noreply.github.com> Date: Fri, 6 Mar 2020 23:16:21 +0100 Subject: [PATCH] fixed not sending confirmation mail after subscription/unsubscription issue --- README.md | 1 - emails/__init__.py | 3 +- emails/mailer.py | 71 +++------------------------------- emails/subscription_checker.py | 66 +++++++++++++++++++++++++++++++ logic/sites.py | 4 +- scrapers/FacebookPage.py | 2 +- scrapers/KameMenu.py | 3 +- scrapers/all_scrapers.py | 31 --------------- 8 files changed, 79 insertions(+), 102 deletions(-) create mode 100644 emails/subscription_checker.py delete mode 100644 scrapers/all_scrapers.py diff --git a/README.md b/README.md index 66b7fd3..5568539 100644 --- a/README.md +++ b/README.md @@ -22,5 +22,4 @@ Used variables' names are: `WL_USERNAME, WL_PASSWORD, WG_USERNAME, WG_PASSWORD`. - [ ] Add unit tests - [ ] Send mail to a newly subscribed person IF foodletter was sent that day - [ ] Add Meet&Eat Wroclaw to GTA/GTB -- [ ] Repair L87/mailer - [ ] Add backup of subscribers lists to cloud diff --git a/emails/__init__.py b/emails/__init__.py index fb91d19..c97edfc 100644 --- a/emails/__init__.py +++ b/emails/__init__.py @@ -1,4 +1,5 @@ from .email_parts import footer, get_default_subject -from .mailer import MailCreator, MailSender, SubscriptionChecker +from .mailer import MailCreator, MailSender from .mailing_list import MailingList from .state import State +from .subscription_checker import SubscriptionChecker diff --git a/emails/mailer.py b/emails/mailer.py index e6db29b..7da8306 100644 --- a/emails/mailer.py +++ b/emails/mailer.py @@ -1,23 +1,24 @@ # -*- coding: utf-8 -*- -import imaplib import logging -import re import smtplib from datetime import date from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from emails.email_parts import footer, get_default_subject -from emails.mailing_list import MailingList class MailCreator: - def create_email(self, recipient: str, sender: str, subject=None, msg_body=None) -> MIMEMultipart: + def create_email(self, recipient: str, sender: str, subject=None, msg_body=None, + use_mail_template=True) -> MIMEMultipart: msg = MIMEMultipart() msg['From'] = f"FoodBot <{sender}>" msg['To'] = recipient msg['Subject'] = get_default_subject() if not subject else subject - msg.attach(MIMEText(self._get_body_with_added_date_and_footer(msg_body), 'plain')) + if use_mail_template: + msg.attach(MIMEText(self._get_body_with_added_date_and_footer(msg_body), 'plain')) + else: + msg.attach(MIMEText(msg_body, 'plain')) return msg @staticmethod @@ -48,63 +49,3 @@ def _send_email(self, email_object: MIMEMultipart) -> None: server.login(self.email, self.password) server.send_message(email_object) server.close() - - -class SubscriptionChecker: - def __init__(self, email, password, site, send_confirmation_mails=True): - self.email = email - self.password = password - self.site = site - self.send_confirmation_mails = send_confirmation_mails - self.imap = None - - def check(self) -> None: - self._create_imap_connection_and_select_inbox() - unread_mails_ids = self._get_unread_mails_ids() - for email_id in unread_mails_ids: - sender_email, subject, body = self._get_core_data_by_email_id(email_id) - self._check_subscription_requests(sender_email, subject, body) - self._delete_imap_connection() - - def _create_imap_connection_and_select_inbox(self): - self.imap = imaplib.IMAP4_SSL("imap.gmail.com", 993) - self.imap.login(self.email, self.password) - self.imap.select('INBOX') - - def _delete_imap_connection(self): - self.imap.close() - del self.imap - - @staticmethod - def _get_sender_mail(body) -> str: - return re.findall('<(.*)>', str(body[1][1]))[0] - - @staticmethod - def _get_subject(body) -> str: - return re.findall('(?<=Subject: )[a-zA-Z ]*', str(body[1][1]))[0].upper() - - def _check_subscription_requests(self, sender_email: str, subject: str, body: str) -> None: - key_words = ['SUBSCRIBE', 'UNSUBSCRIBE'] - for key_word in key_words: - if key_word in subject or key_word in body: - MailingList(self.site).add(sender_email) if key_word == 'SUBSCRIBE' \ - else MailingList(self.site).delete(sender_email) - if self.send_confirmation_mails: - self._send_confirmation_email(key_word, sender_email) - - def _send_confirmation_email(self, subscribe_state, recipient) -> None: - subject = f"{subscribe_state.lower()}d" - body = f"You've been successfully {subscribe_state.lower()}d!" - email_object = MailCreator().create_email(self.email, recipient, subject=subject, msg_body=body) - MailSender(self.email, self.password).send_mail_to_one_recipient(recipient, email_object) - - def _get_unread_mails_ids(self): - _, response = self.imap.search(None, '(UNSEEN)') - return response[0].split() - - def _get_core_data_by_email_id(self, email_id) -> tuple: - _, body = self.imap.fetch(email_id, '(BODY[TEXT] BODY[HEADER.FIELDS (FROM SUBJECT)])') - sender_email = self._get_sender_mail(body) - subject = self._get_subject(body) - body = str(body[0][1]).upper() - return sender_email, subject, body diff --git a/emails/subscription_checker.py b/emails/subscription_checker.py new file mode 100644 index 0000000..76a9fce --- /dev/null +++ b/emails/subscription_checker.py @@ -0,0 +1,66 @@ +import imaplib +import re + +from emails import MailingList, MailCreator, MailSender + + +class SubscriptionChecker: + def __init__(self, email, password, site, send_confirmation_mails=True): + self.email = email + self.password = password + self.site = site + self.send_confirmation_mails = send_confirmation_mails + self.imap = None + + def check(self) -> None: + self._create_imap_connection_and_select_inbox() + unread_mails_ids = self._get_unread_mails_ids() + for email_id in unread_mails_ids: + sender_email, subject, body = self._get_core_data_by_email_id(email_id) + self._check_subscription_requests(sender_email, subject, body) + self._delete_imap_connection() + + def _create_imap_connection_and_select_inbox(self): + self.imap = imaplib.IMAP4_SSL("imap.gmail.com", 993) + self.imap.login(self.email, self.password) + self.imap.select('INBOX') + + def _delete_imap_connection(self): + self.imap.close() + del self.imap + + @staticmethod + def _get_sender_mail(body) -> str: + return re.findall('<(.*)>', str(body[1][1]))[0] + + @staticmethod + def _get_subject(body) -> str: + subject = re.findall('(?<=Subject: )[a-zA-Z ]*', str(body[1][1])) + return subject[0].upper() if subject else '' + + def _check_subscription_requests(self, sender_email: str, subject: str, body: str) -> None: + key_words = ['UNSUBSCRIBE', 'SUBSCRIBE'] + for key_word in key_words: + if key_word in subject or key_word in body: + MailingList(self.site).add(sender_email) if key_word == 'SUBSCRIBE' \ + else MailingList(self.site).delete(sender_email) + if self.send_confirmation_mails: + self._send_confirmation_email(key_word, sender_email) + + def _send_confirmation_email(self, subscribe_state, recipient) -> None: + subject = f"{subscribe_state.lower()}d".capitalize() + body = f"You've been successfully {subscribe_state.lower()}d!" + email_object = MailCreator().create_email(recipient, self.email, subject=subject, msg_body=body, + use_mail_template=False) + MailSender(self.email, self.password).send_mail_to_one_recipient(recipient, email_object) + + def _get_unread_mails_ids(self): + _, response = self.imap.search(None, '(UNSEEN)') + return response[0].split() + + def _get_core_data_by_email_id(self, email_id) -> tuple: + _, body = self.imap.fetch(email_id, '(BODY[TEXT] BODY[HEADER.FIELDS (FROM SUBJECT)])') + sender_email = self._get_sender_mail(body) + subject = self._get_subject(body) + body = str(body[0][1]).upper() + return sender_email, subject, body diff --git a/logic/sites.py b/logic/sites.py index 6adfbc5..23e9be9 100644 --- a/logic/sites.py +++ b/logic/sites.py @@ -1,10 +1,10 @@ import logging from abc import ABC -from emails import SubscriptionChecker, MailSender, MailingList, MailCreator +from bot_credentials import WL_USERNAME, WL_PASSWORD, GT_USERNAME, GT_PASSWORD from emails import State +from emails import SubscriptionChecker, MailSender, MailingList, MailCreator from scrapers import AstraMenu, CockpeatMenu, ObiadeoMenu, KameMenu, GreenTowersBistroMenu -from bot_credentials import WL_USERNAME, WL_PASSWORD, GT_USERNAME, GT_PASSWORD class Site(ABC): diff --git a/scrapers/FacebookPage.py b/scrapers/FacebookPage.py index ee5e56e..e3e2f87 100644 --- a/scrapers/FacebookPage.py +++ b/scrapers/FacebookPage.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import re -from abc import ABC, abstractmethod +from abc import ABC from datetime import datetime import requests diff --git a/scrapers/KameMenu.py b/scrapers/KameMenu.py index dd0c363..7e6b374 100644 --- a/scrapers/KameMenu.py +++ b/scrapers/KameMenu.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -import re import datetime +import re + from scrapers.FacebookPage import FacebookPage from scrapers.IMenu import IMenu from tools.levenshtein_distance import calc_levenshtein diff --git a/scrapers/all_scrapers.py b/scrapers/all_scrapers.py deleted file mode 100644 index 1ba093f..0000000 --- a/scrapers/all_scrapers.py +++ /dev/null @@ -1,31 +0,0 @@ -# # -*- coding: utf-8 -*- -# import logging -# -# from scrapers.AstraMenu import AstraMenu -# from scrapers.CockpeatMenu import CockpeatMenu -# from scrapers.ObiadeoMenu import ObiadeoMenu -# -# -# def get_all_menus_wl() -> dict: -# list_of_classes = [AstraMenu, CockpeatMenu, ObiadeoMenu] -# all_menus = dict() -# for single_menu in map(lambda cls: cls().get_todays_menu(), list_of_classes): -# all_menus.update(single_menu) -# return all_menus -# -# -# def get_all_menus_gt() -> dict: -# list_of_classes = [] -# all_menus = dict() -# for single_menu in map(lambda cls: cls().get_todays_menu(), list_of_classes): -# all_menus.update(single_menu) -# return all_menus - - -# def all_menus_exist() -> bool: -# all_menus = get_all_menus() -# if '' in all_menus.values(): -# for place_name, menu in all_menus.items(): -# if not menu: -# logging.info(f"{place_name} menu is still empty.") -# return '' not in all_menus.values()