diff --git a/infomentor/__init__.py b/infomentor/__init__.py index 38c8aa7..e04f80f 100644 --- a/infomentor/__init__.py +++ b/infomentor/__init__.py @@ -1 +1 @@ -#from infomentor.__main__ import * +# from infomentor.__main__ import * diff --git a/infomentor/__main__.py b/infomentor/__main__.py index 546ee46..ba09a49 100644 --- a/infomentor/__main__.py +++ b/infomentor/__main__.py @@ -8,13 +8,15 @@ import requests from infomentor import db, model, connector, informer, config -logformat = "{asctime} - {name:25s}[{filename:20s}:{lineno:3d}] - {levelname:8s} - {message}" +logformat = ( + "{asctime} - {name:25s}[{filename:20s}:{lineno:3d}] - {levelname:8s} - {message}" +) def logtofile(): from logging.handlers import RotatingFileHandler - handler = RotatingFileHandler("log.txt", maxBytes=1024*1024, backupCount=10) + handler = RotatingFileHandler("log.txt", maxBytes=1024 * 1024, backupCount=10) logging.basicConfig( level=logging.INFO, format=logformat, handlers=[handler], style="{" ) @@ -29,109 +31,78 @@ def parse_args(arglist): parser.add_argument( "--nolog", action="store_true", help="print log instead of logging to file" ) - parser.add_argument("--adduser", action='store_true', help="add user") - parser.add_argument("--addfake", action='store_true', help="add fake") - parser.add_argument("--addpushover", action='store_true', help="add pushover") - parser.add_argument("--addmail", action='store_true', help="add mail") - parser.add_argument("--addcalendar", action='store_true', help="add icloud calendar") - parser.add_argument("--addinvitation", action='store_true', help="add calendar invitation") - parser.add_argument("--test", action="store_true", help="test") - parser.add_argument("--username", type=str, nargs='?', help="username") + parser.add_argument("--username", type=str, nargs="?", help="infomentor username") + parser.add_argument("--password", type=str, nargs="?", help="infomentor password") + parser.add_argument("--fake", action="store_true", help="add fake") + parser.add_argument("--pushover", type=str, nargs="?", help="pushover user id") + parser.add_argument("--mail", type=str, nargs="?", help="e-mail for notification") + parser.add_argument( + "--iclouduser", type=str, nargs="?", help="icloud calendar user" + ) + parser.add_argument( + "--icloudpwd", type=str, nargs="?", help="icloud calendar password" + ) + parser.add_argument( + "--icloudcalendar", type=str, nargs="?", help="icloud calendar calendar" + ) + parser.add_argument( + "--invitationmail", type=str, nargs="?", help="e-mail for notification" + ) args = parser.parse_args(arglist) return args -def add_user(username): +def perform_user_update(args): + username = args.username session = db.get_db() existing_user = ( session.query(model.User).filter(model.User.name == username).one_or_none() ) if existing_user is not None: - print("user exists, change pw") + print("user exists, changing pw") else: print(f"Adding user: {username}") - import getpass + if args.password is None: + import getpass + + password = getpass.getpass(prompt="Password: ") + else: + password = args.password - password = getpass.getpass(prompt="Password: ") if existing_user is not None: existing_user.password = password else: user = model.User(name=username, password=password) session.add(user) - session.commit() + if args.pushover is not None: + user.notification = model.Notification( + ntype=model.Notification.Types.PUSHOVER, info=args.pushover + ) + elif args.mail: + user.notification = model.Notification( + ntype=model.Notification.Types.EMAIL, info=args.mail + ) + elif args.fake: + user.notification = model.Notification( + ntype=model.Notification.Types.FAKE, info="" + ) -def add_pushover(username): - session = db.get_db() - user = session.query(model.User).filter(model.User.name == username).one_or_none() - if user is None: - print("user does not exist") - return - else: - print(f"Adding PUSHOVER for user: {username}") - id = input("PUSHOVER ID: ") - user.notification = model.Notification( - ntype=model.Notification.Types.PUSHOVER, info=id - ) - session.commit() + if ( + args.iclouduser is not None + and args.icloudpwd is not None + and args.icloudcalendar is not None + ): + user.icalendar = model.ICloudCalendar( + icloud_user=args.iclouduser, + password=args.icloudpwd, + calendarname=args.icloudcalendar, + ) + if args.invitationmail: + user.invitation = model.Invitation(email=mail) -def add_fake(username): - session = db.get_db() - user = session.query(model.User).filter(model.User.name == username).one_or_none() - if user is None: - print("user does not exist") - return - else: - print(f"Adding FAKE for user: {username}") - user.notification = model.Notification(ntype=model.Notification.Types.FAKE, info="") - session.commit() - - -def add_calendar(username): - session = db.get_db() - user = session.query(model.User).filter(model.User.name == username).one_or_none() - if user is None: - print("user does not exist") - return - else: - print(f"Adding icloud calendar for user: {username}") - id = input("Apple ID: ") - import getpass - - password = getpass.getpass(prompt="iCloud Password: ") - calendar = input("Calendar: ") - user.icalendar = model.ICloudCalendar( - icloud_user=id, password=password, calendarname=calendar - ) - session.commit() - -def add_invitation(username): - session = db.get_db() - user = session.query(model.User).filter(model.User.name == username).one_or_none() - if user is None: - print("user does not exist") - return - else: - print(f"Adding Mail for calendar invitation for user: {username}") - mail = input("Mail: ") - user.invitation = model.Invitation(email=mail) - session.commit() - - -def add_mail(username): - session = db.get_db() - user = session.query(model.User).filter(model.User.name == username).one_or_none() - if user is None: - print("user does not exist") - return - else: - print(f"Adding MAIL for user: {username}") - address = input("MAIL ADDRESS: ") - user.notification = model.Notification( - ntype=model.Notification.Types.EMAIL, info=address - ) session.commit() @@ -140,7 +111,7 @@ def notify_users(): session = db.get_db() cfg = config.load() if cfg["healthchecks"]["url"] != "": - requests.get(cfg["healthchecks"]["url"] + '/start') + requests.get(cfg["healthchecks"]["url"] + "/start") for user in session.query(model.User): logger.info("==== USER: %s =====", user.name) @@ -195,8 +166,6 @@ def notify_users(): def main(): args = parse_args(sys.argv[1:]) - if args.test: - return if args.nolog: logtoconsole() else: @@ -206,20 +175,10 @@ def main(): try: lock = flock.flock() if not lock.aquire(): - logger.info("EXITING - PREVIOUS IS RUNNING") + logger.info("EXITING - PREVIOUS IS STILL RUNNING") raise Exception() - if args.addfake: - add_fake(args.username) - elif args.adduser: - add_user(args.username) - elif args.addpushover: - add_pushover(args.username) - elif args.addmail: - add_mail(args.username) - elif args.addcalendar: - add_calendar(args.username) - elif args.addinvitation: - add_invitation(args.username) + if args.username: + perform_user_update(args) else: notify_users() except Exception as e: @@ -228,64 +187,6 @@ def main(): finally: logger.info("EXITING--------------------- %s", os.getpid()) -def run_notify(): - run_without_args(notify_users) - -def run_adduser(): - run_with_args(add_user) - -def run_addfake(): - run_with_args(add_fake) - -def run_addpushover(): - run_with_args(add_pushover) - -def run_addmail(): - run_with_args(add_mail) - -def run_addcalendar(): - run_with_args(add_calendar) - -def run_addinvitation(): - run_with_args(add_invitation) - - -def run_with_args(fct): - args = parse_args(sys.argv[1:]) - logtofile() - logger = logging.getLogger("Infomentor Notifier") - logger.info("STARTING-------------------- %s", os.getpid()) - lock = flock.flock() - try: - if not lock.aquire(): - logger.info("EXITING - PREVIOUS IS RUNNING") - raise Exception() - if args.username is None: - print('Provide Username using --username') - raise Exception('No username provided') - fct(args.username) - except Exception as e: - logger.info("Exceptional exit") - logger.exception("Info") - finally: - logger.info("EXITING--------------------- %s", os.getpid()) - -def run_without_args(fct): - args = parse_args(sys.argv[1:]) - logtofile() - logger = logging.getLogger("Infomentor Notifier") - logger.info("STARTING-------------------- %s", os.getpid()) - try: - lock = flock.flock() - if not lock.aquire(): - logger.info("EXITING - PREVIOUS IS RUNNING") - raise Exception() - fct() - except Exception as e: - logger.info("Exceptional exit") - logger.exception("Info") - finally: - logger.info("EXITING--------------------- %s", os.getpid()) if __name__ == "__main__": main() diff --git a/infomentor/config.py b/infomentor/config.py index 052495a..51e88ad 100644 --- a/infomentor/config.py +++ b/infomentor/config.py @@ -4,9 +4,7 @@ import os _config = None _defaults = { - "pushover": { - "apikey": "", - }, + "pushover": {"apikey": ""}, "general": { "secretkey": "", "baseurl": "", @@ -14,16 +12,11 @@ _defaults = { "im1url": "https://im1.infomentor.de/Germany/Germany/Production", "mimrul": "https://mein.infomentor.de", }, - "smtp": { - "server": "", - "username": "", - "password": "", - }, - "healthchecks": { - "url": "", - }, + "smtp": {"server": "", "username": "", "password": ""}, + "healthchecks": {"url": ""}, } + def _set_defaults(config): config = _defaults diff --git a/infomentor/connector.py b/infomentor/connector.py index 310dbdc..ffa5bce 100644 --- a/infomentor/connector.py +++ b/infomentor/connector.py @@ -18,11 +18,13 @@ from infomentor import model, config class InfomentorFile(object): """Represent a file which is downloaded""" - def __init__(self, directory, filename, seed=''): + def __init__(self, directory, filename, seed=""): if directory is None: raise Exception("directory is required") self.filename = filename - self.randomid = hashlib.sha1('{}{}'.format(filename, seed).encode('utf-8')).hexdigest() + self.randomid = hashlib.sha1( + "{}{}".format(filename, seed).encode("utf-8") + ).hexdigest() self.directory = directory @property @@ -266,7 +268,7 @@ class Infomentor(object): self.logger.info("fetching news") self._do_post(self._mim_url("Communication/News/GetNewsList")) news_json = self.get_json_return() - return news_json['items'] + return news_json["items"] def get_news_article(self, news_entry): """Receive all the article information""" diff --git a/infomentor/icloudcalendar.py b/infomentor/icloudcalendar.py index b746be3..16495d2 100644 --- a/infomentor/icloudcalendar.py +++ b/infomentor/icloudcalendar.py @@ -20,7 +20,6 @@ class iCloudConnector(object): password = None propfind_principal = '' propfind_calendar_home_set = " " - def __init__(self, username, password, **kwargs): self.username = username @@ -30,7 +29,6 @@ class iCloudConnector(object): self.discover() self.get_calendars() - # discover: connect to icloud using the provided credentials and discover # # 1. The principal URL @@ -45,10 +43,7 @@ class iCloudConnector(object): headers = {"Depth": "1"} auth = HTTPBasicAuth(self.username, self.password) principal_response = self.repeated_request( - "PROPFIND", - self.icloud_url, - auth=auth, - data=self.propfind_principal + "PROPFIND", self.icloud_url, auth=auth, data=self.propfind_principal ) # Parse the resulting XML response soup = BeautifulSoup(principal_response.content, "lxml") @@ -60,15 +55,20 @@ class iCloudConnector(object): # Next use the discovery URL to get more detailed properties - such as # the calendar-home-set home_set_response = self.repeated_request( - "PROPFIND", - discovery_url, - auth=auth, - data=self.propfind_calendar_home_set, + "PROPFIND", discovery_url, auth=auth, data=self.propfind_calendar_home_set ) _logger.debug("Result code: {}".format(home_set_response.status_code)) if home_set_response.status_code != 207: - _logger.error("Failed to retrieve calendar-home-set {}".format(home_set_response.status_code)) - raise Exception("failed to retrieve calender home set {}".format(home_set_response.content)) + _logger.error( + "Failed to retrieve calendar-home-set {}".format( + home_set_response.status_code + ) + ) + raise Exception( + "failed to retrieve calender home set {}".format( + home_set_response.content + ) + ) # And then extract the calendar-home-set URL soup = BeautifulSoup(home_set_response.content, "lxml") self.calendar_home_set_url = soup.find( @@ -77,18 +77,20 @@ class iCloudConnector(object): def repeated_request(self, *args, **kwargs): for _ in range(0, 5): - response = requests.request( - *args, **kwargs - ) + response = requests.request(*args, **kwargs) _logger.debug("Request result code: {}".format(response.status_code)) if response.status_code != 207: - _logger.error("Failed to retrieve response: {}".format(response.status_code)) + _logger.error( + "Failed to retrieve response: {}".format(response.status_code) + ) _logger.error("Retry") time.sleep(0.25) if response.status_code == 207: break else: - raise Exception("failed to retrieve {} {}".format(response.content, response.headers)) + raise Exception( + "failed to retrieve {} {}".format(response.content, response.headers) + ) return response # get_calendars @@ -135,4 +137,3 @@ class iCloudConnector(object): ): # to do pass - diff --git a/infomentor/informer.py b/infomentor/informer.py index 28c91c7..d138a46 100755 --- a/infomentor/informer.py +++ b/infomentor/informer.py @@ -56,13 +56,13 @@ class Informer(object): for news_entry in newslist: news = ( session.query(model.News) - .filter(model.News.news_id == news_entry['id']) - .filter(model.News.date == news_entry['publishedDate']) + .filter(model.News.news_id == news_entry["id"]) + .filter(model.News.date == news_entry["publishedDate"]) .with_parent(self.user, "news") .one_or_none() ) if news is not None: - self.logger.debug('Skipping news %s', news_entry['id']) + self.logger.debug("Skipping news %s", news_entry["id"]) continue news = self.im.get_news_article(news_entry) self._notify_news(news) @@ -71,7 +71,7 @@ class Informer(object): def _notify_news(self, news): if self.user.notification is None: - self.logger.debug('Warn: no notification for user') + self.logger.debug("Warn: no notification for user") return if self.user.notification.ntype == model.Notification.Types.PUSHOVER: self._notify_news_pushover(news) @@ -93,7 +93,9 @@ class Informer(object): for attachment in news.attachments: fid, fname = attachment.localpath.split("/") text += """
Attachment {0}: {2}/{1}
""".format( - fname, urllib.parse.quote(attachment.localpath), cfg["general"]["baseurl"] + fname, + urllib.parse.quote(attachment.localpath), + cfg["general"]["baseurl"], ) parsed_date = dateparser.parse(news.date) now = datetime.datetime.now() @@ -177,7 +179,9 @@ class Informer(object): for attachment in hw.attachments: fid, fname = attachment.localpath.split("/") text += """
Attachment {0}: {2}/{1}
""".format( - fname, urllib.parse.quote(attachment.localpath), cfg["general"]["baseurl"] + fname, + urllib.parse.quote(attachment.localpath), + cfg["general"]["baseurl"], ) if len(text) > 900: url = self._make_site(text) @@ -240,37 +244,38 @@ class Informer(object): def _send_invitation(self, calobj, to, fr="infomentor@09a.de"): event = calobj.subcomponents[0] - eml_body = event['description'] - eml_body_bin = event['description'] - msg = MIMEMultipart('mixed') - msg['Reply-To']=fr - msg['Subject'] = event['summary'] - msg['From'] = fr - msg['To'] = to + eml_body = event["description"] + eml_body_bin = event["description"] + msg = MIMEMultipart("mixed") + msg["Reply-To"] = fr + msg["Subject"] = event["summary"] + msg["From"] = fr + msg["To"] = to - part_email = MIMEText(eml_body,"html") - part_email_text = MIMEText(eml_body,"plain") - part_cal = MIMEText(calobj.to_ical().decode('utf-8'),'calendar;method=REQUEST') + part_email = MIMEText(eml_body, "html") + part_email_text = MIMEText(eml_body, "plain") + part_cal = MIMEText(calobj.to_ical().decode("utf-8"), "calendar;method=REQUEST") - msgAlternative = MIMEMultipart('alternative') + msgAlternative = MIMEMultipart("alternative") msg.attach(msgAlternative) - ical_atch = MIMEBase('application/ics',' ;name="%s"'%("invite.ics")) - ical_atch.set_payload(calobj.to_ical().decode('utf-8')) + ical_atch = MIMEBase("application/ics", ' ;name="%s"' % ("invite.ics")) + ical_atch.set_payload(calobj.to_ical().decode("utf-8")) encoders.encode_base64(ical_atch) - ical_atch.add_header('Content-Disposition', 'attachment; filename="%s"'%("invite.ics")) + ical_atch.add_header( + "Content-Disposition", 'attachment; filename="%s"' % ("invite.ics") + ) - eml_atch = MIMEBase('text/plain','') - eml_atch.set_payload('') + eml_atch = MIMEBase("text/plain", "") + eml_atch.set_payload("") encoders.encode_base64(eml_atch) - eml_atch.add_header('Content-Transfer-Encoding', "") + eml_atch.add_header("Content-Transfer-Encoding", "") msg.attach(part_email) msg.attach(part_email_text) msg.attach(part_cal) self._send_mail(msg) - def _setup_icloudconnector(self): if self.cal is None: try: @@ -284,17 +289,19 @@ class Informer(object): self.logger.warn("using icloud") except Exception as e: self.logger.exception("using icloud dummy connector") + class Dummy(object): def add_event(self, *args, **kwargs): pass + self.cal = Dummy() - + def _write_icalendar(self, calend): try: self._setup_icloudconnector() self.cal.add_event(calend.to_ical()) except Exception as e: - self.logger.exception('Calendar failed') + self.logger.exception("Calendar failed") def update_calendar(self): session = db.get_db() @@ -304,7 +311,9 @@ class Informer(object): calentries = self.im.get_calendar() for entry in calentries: self.logger.debug(entry) - uid = str(uuid.uuid5(uuid.NAMESPACE_URL, "infomentor_{}".format(entry["id"]))) + uid = str( + uuid.uuid5(uuid.NAMESPACE_URL, "infomentor_{}".format(entry["id"])) + ) event_details = self.im.get_event(entry["id"]) calend = Calendar() event = Event() @@ -319,13 +328,13 @@ class Informer(object): event.add("dtend", dateparser.parse(entry["end"]).date()) description = event_details["notes"] - eventinfo = event_details['info'] + eventinfo = event_details["info"] new_cal_entry = calend.to_ical().replace(b"\r", b"") new_cal_hash = hashlib.sha1(new_cal_entry).hexdigest() - for res in eventinfo['resources']: + for res in eventinfo["resources"]: f = self.im.download_file(res["url"], directory="files") description += """\nAttachment {0}: {2}/{1}""".format( - res['title'], urllib.parse.quote(f), cfg["general"]["baseurl"] + res["title"], urllib.parse.quote(f), cfg["general"]["baseurl"] ) event.add("description", description) @@ -364,4 +373,4 @@ class Informer(object): self.user.calendarentries.append(calendarentry) session.commit() except Exception as e: - self.logger.exception('Calendar failed') + self.logger.exception("Calendar failed") diff --git a/infomentor/model.py b/infomentor/model.py index 98c150d..c87ce94 100755 --- a/infomentor/model.py +++ b/infomentor/model.py @@ -252,8 +252,5 @@ class Invitation(ModelBase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - def __repr__(self): - return "" % ( - self.email, - ) + return "" % (self.email,)