From acb558e5c0491f8b86e5e087d260d74e4e562b1c Mon Sep 17 00:00:00 2001 From: Matthias Bilger Date: Sun, 21 Oct 2018 07:31:07 +0200 Subject: [PATCH] deleted obsolete files --- login.py | 668 ------------------------------------------------------- 1 file changed, 668 deletions(-) delete mode 100755 login.py diff --git a/login.py b/login.py deleted file mode 100755 index bf3c669..0000000 --- a/login.py +++ /dev/null @@ -1,668 +0,0 @@ -import requests -import re -import os -import uuid -import copy -import dataset -import pushover -import http.cookiejar -import time -import math -import dateparser -import datetime -import contextlib -import logging -import hashlib -import urllib.parse - - -db = dataset.connect('sqlite:///infomentor.db') -pushover.init('***REMOVED***') -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)8s - %(message)s', - filename='log.txt', - filemode='a+' -) -logger = logging.getLogger('Infomentor Notifier') - -class NewsInformer(object): - def __init__(self, username, password, pushover, logger=None, **kwargs): - if logger is None: - self.logger = logging.getLogger(__name__) - else: - self.logger = logger - self.username = username - self.password = password - self.pushover = pushover - self._setup_db() - self.im = Infomentor(logger=logger) - res = self.im.login(self.username, self.password) - if not res: - self.logger.error('Login not successfull') - raise Exception('Login failed') - - def _setup_db(self): - self.db_news = db.create_table( - 'news', - primary_id='id', - primary_type=db.types.integer - ) - self.db_notification = db.create_table( - 'news_notification', - primary_id=False, - ) - self.db_homework = db.create_table( - 'homework', - primary_id='id', - primary_type=db.types.integer - ) - self.db_homework_notification = db.create_table( - 'homework_notification', - primary_id=False, - ) - self.db_news_attachments = db.create_table( - 'news_attachments', - primary_id=False, - ) - self.db_hw_attachments = db.create_table( - 'homework_attachments', - primary_id=False, - ) - self.db_attachments = db.create_table( - 'attachments', - primary_id='id', - primary_type=db.types.integer - ) - self.db_timetable = db.create_table( - 'timetable', - primary_id='id', - primary_type=db.types.string - ) - self.db_ttnotification = db.create_table( - 'timetable_notification', - primary_id=False, - ) - - def make_site(self, text): - filename = str(uuid.uuid4()) - fpath = os.path.join('files', filename+'.html') - urlfinder = re.compile("(https?://[^ \n\t]*)") - text = urlfinder.sub(r'\1', text) - text = ' {}'.format(text) - with open(fpath, 'w+') as f: - f.write(text) - return 'https://files.hyttioaoa.de/{}.html'.format(filename) - - def send_notification( - self, news_id, text, title, attachment=None, timestamp=True): - self.logger.info('sending notification: %s', title) - if len(text) > 900: - url = self.make_site(text) - shorttext = text[:900] - text = '{}...\n\nfulltext saved at: {}'.format(shorttext, url) - text = text.replace('
', '\n') - try: - self.logger.info(text) - self.logger.info(title) - pushover.Client(self.pushover).send_message( - text, - title=title, - attachment=attachment, - html=True, - timestamp=timestamp - ) - self.db_notification.insert( - {'id': news_id, 'username': self.username} - ) - except pushover.RequestError as e: - self.logger.error('Sending notification failed', exc_info=e) - - def _notification_sent(self, news_id): - entry = self.db_notification.find_one( - id=news_id, username=self.username) - return entry is not None - - def notify_timetable(self): - tt = self.im.get_timetable() - changed = [] - for item in tt: - parsed_item = copy.deepcopy(item) - start = datetime.datetime.strptime(parsed_item['start'], '%Y-%m-%dT%H:%M:%S') - parsed_item['wday'] = start.weekday() - key = '{wday}-{startTime}/{endTime}-{title}-{notes[roomInfo]}'.format(**parsed_item) - parsed_item['id'] = key - entry = self.db_timetable.find_one(id=key) - if entry is None: - changed.append(parsed_item) - - def appSetup(self): - print(self.im.appsetup()) - - def notify_homework(self): - homework = [] - homework.extend(self.im.get_homework()) - homework.extend(self.im.get_homework(1)) - self.logger.info('Parsing %d homework items', len(homework)) - for datehw in homework: - for hw in datehw['items']: - if hw['id'] == 0: - continue - storehw = self.db_homework.find_one(id=hw['id']) - if storehw is None: - self.logger.info('NEW homework found %s', hw['subject']) - storehw = { - k: hw[k] for k in ('id', 'subject', 'courseElement', 'homeworkText') - } - for attachment in hw['attachments']: - self.logger.info('found attachment %s', attachment['title']) - att_id = re.findall('Download/([0-9]+)?', attachment['url'])[0] - f = self.im.download(attachment['url'], directory='files', skip=True) - try: - fid, fname = f.split('/') - storehw['homeworkText'] += '''
Attachment {0}: https://files.hyttioaoa.de/{1}
'''.format(fname, f) - if self.db_attachments.find_one(id=int(att_id)): - continue - self.db_attachments.insert( - {'id': int(att_id), 'filename':f} - ); - self.db_hw_attachments.insert({'att_id': att_id, 'hw_id':hw['id']}) - except Exception as e: - self.logger.exception('failed to store attachment') - self.logger.info(storehw) - self.db_homework.insert(storehw) - - if not self._notification_sent(storehw['id']): - self.logger.info('Notify %s about HW %s', - self.username, storehw['subject']) - self.send_notification( - storehw['id'], - storehw['homeworkText'], - storehw['subject'], - timestamp=True - ) - - - - def notify_news(self): - im_news = self.im.get_news() - idlist = [str(i['id']) for i in im_news['items']] - self.logger.info('Parsing %d news (%s)', im_news['totalItems'], ', '.join(idlist)) - for news_item in reversed(im_news['items']): - storenewsdata = self.db_news.find_one(id=news_item['id']) - if storenewsdata is None: - self.logger.info('NEW article found %s', news_item['title']) - newsdata = self.im.get_article(news_item['id']) - storenewsdata = { - k: newsdata[k] for k in ('id', 'title', 'content', 'date') - } - for attachment in newsdata['attachments']: - self.logger.info('found attachment %s', news_item['title']) - att_id = re.findall('Download/([0-9]+)?', attachment['url'])[0] - f = self.im.download(attachment['url'], directory='files', skip=True) - try: - storenewsdata['content'] += '''
Attachment {0}: https://files.hyttioaoa.de/{0}
'''.format(f) - if self.db_attachments.find_one(id=int(att_id)): - continue - self.db_attachments.insert( - {'id': int(att_id), 'filename':f} - ); - self.db_news_attachments.insert({'att_id': att_id, 'news_id':newsdata['id']}) - except Exception as e: - self.logger.exception('failed to store attachment') - self.logger.info(storenewsdata) - self.db_news.insert(storenewsdata) - if not self._notification_sent(news_item['id']): - self.logger.info('Notify %s about %s', - self.username, news_item['title']) - image = None - image_filename = self.im.get_newsimage(news_item['id']) - if image_filename: - image = open(image_filename, 'rb') - - parsed_date = dateparser.parse(storenewsdata['date']) - now = datetime.datetime.now() - parsed_date += datetime.timedelta(hours=now.hour, minutes=now.minute) - timestamp = math.floor(parsed_date.timestamp()) - self.send_notification( - news_item['id'], - storenewsdata['content'], - storenewsdata['title'], - attachment=image, - timestamp=timestamp - ) - if image is not None: - image.close() - - -def get_filename_from_cd(cd): - if not cd: - return None - fname = re.match('.*(?:filename=(?P.+)|filename\*=(?P.+))(?:$|;.*)', cd) - filename = fname.group('native') - if filename is not None and len(filename) != 0: - return filename - filename = fname.group('extended') - if filename is not None and len(filename) != 0: - encoding, string = filename.split("''") - return urllib.parse.unquote(string, encoding) - filename = str(uuid.uuid4()) - logger.warning('no filename detected in %s: using random filename %s', cd, filename) - return filename - - -class Infomentor(object): - - BASE_IM1 = 'https://im1.infomentor.de/Germany/Germany/Production' - BASE_MIM = 'https://mein.infomentor.de' - - def __init__(self, logger=None): - if logger is None: - logger = logging.getLogger(__name__) - self.logger = logger - - self.session = requests.Session() - self.session.headers.update({'User-Agent': 'Mozilla/5.0'}) - self._last_result = None - - def login(self, user, password): - os.makedirs('cookiejars', exist_ok=True) - self.session.cookies = http.cookiejar.MozillaCookieJar( - filename='cookiejars/{}.cookies'.format(user) - ) - with contextlib.suppress(FileNotFoundError): - self.session.cookies.load(ignore_discard=True, ignore_expires=True) - if self.logged_in(user): - return True - self._do_login(user, password) - self._do_get(self._mim_url()) - return self.logged_in(user) - - def logged_in(self, username): - ts = math.floor(time.time()) - url = self._mim_url( - 'authentication/authentication/isauthenticated/?_={}000'.format(ts) - ) - r = self._do_post(url) - self.logger.info('%s loggedin: %s', username, r.text) - return r.text == 'true' - - def _get_auth_token(self): - rem = re.findall(r'name="oauth_token" value="([^"]*)"', - self._last_result.text) - if len(rem) != 1: - self.logger.error('OAUTH_TOKEN not found') - raise Exception('Invalid Count of tokens') - oauth_token = rem[0] - return oauth_token - - def _do_post(self, url, **kwargs): - self.logger.info('post to: %s', url) - self._last_result = self.session.post(url, **kwargs) - self.logger.info('result: %d', self._last_result.status_code) - self.session.cookies.save(ignore_discard=True, ignore_expires=True) - return self._last_result - - def _do_get(self, url, **kwargs): - self.logger.info('get: %s', url) - self._last_result = self.session.get(url, **kwargs) - self.logger.info('result: %d', self._last_result.status_code) - self.session.cookies.save(ignore_discard=True, ignore_expires=True) - return self._last_result - - def download(self, url, filename=None, directory=None, overwrite=False, skip=False): - self.logger.info('fetching download: %s', url) - if filename is not None: - self.logger.info('using given filename %s', filename) - os.makedirs(os.path.dirname(filename), exist_ok=True) - if os.path.isfile(filename) and not overwrite: - self.logger.info('file %s already downloaded', filename) - return filename - elif directory is not None: - self.logger.info('to directory %s', directory) - os.makedirs(directory, exist_ok=True) - else: - self.logger.error('fetching download requires filename or folder') - return False - - subid = str(uuid.uuid4()) - os.makedirs(os.path.join(directory, subid), exist_ok=True) - - url = self._mim_url(url) - r = self._do_get(url) - if r.status_code != 200: - return False - if filename is None: - self.logger.info('determine filename from headers') - filename = get_filename_from_cd(r.headers.get('content-disposition')) - self.logger.info('determined filename: %s', filename) - filename = os.path.join(subid, filename) - filepath = os.path.join(directory, filename) - self.logger.info('saveas: %s', filepath) - - with open(filepath, 'wb+') as f: - f.write(r.content) - return filename - - def _extract_hidden_fields(self): - hiddenfields = re.findall('', - self._last_result.text) - return hiddenfields - - def _build_url(self, path='', base=None): - if base is None: - base = self.BASE_IM1 - return '{}/{}'.format(base, path) - - def _mim_url(self, path=''): - return self._build_url(path, base=self.BASE_MIM) - - def _im1_url(self, path=''): - return self._build_url(path, base=self.BASE_IM1) - - def _get_hidden_fields(self): - hiddenfields = self._extract_hidden_fields() - field_values = {} - for f in hiddenfields: - names = re.findall('name="([^"]*)"', f) - if len(names) != 1: - self.logger.error('Could not parse hidden field (fieldname)') - continue - values = re.findall('value="([^"]*)"', f) - if len(values) != 1: - self.logger.error('Could not parse hidden field (value)') - continue - field_values[names[0]] = values[0] - return field_values - - def _do_request_initial_token(self): - # Get the initial oauth token - self._do_get(self._mim_url()) - self._oauth_token = self._get_auth_token() - # This request is performed by the browser, the reason is unclear - login_url = self._mim_url( - 'Authentication/Authentication/Login?ReturnUrl=%2F') - self._do_get(login_url) - - def _perform_login(self, user, password): - self._do_post( - self._im1_url('mentor/'), - data={'oauth_token': self._oauth_token} - ) - # Extract the hidden fields content - payload = self._get_hidden_fields() - # update with the missing and the login parameters - payload.update({ - 'login_ascx$txtNotandanafn': user, - 'login_ascx$txtLykilord': password, - '__EVENTTARGET': 'login_ascx$btnLogin', - '__EVENTARGUMENT': '' - }) - - # perform the login - self._do_post( - self._im1_url('mentor/'), - data=payload, - headers={ - 'Referer': self._im1_url('mentor/'), - 'Content-Type': 'application/x-www-form-urlencoded' - } - ) - - def _finalize_login(self): - # Read the oauth token which is the final token for the login - oauth_token = self._get_auth_token() - # autenticate - self._do_post( - self._im1_url('mentor/'), - data={'oauth_token': oauth_token} - ) - - def _do_login(self, user, password): - self._do_request_initial_token() - self._perform_login(user, password) - self._finalize_login() - - def get_news(self): - self.logger.info('fetching news') - r = self._do_post(self._mim_url('News/news/GetArticleList')) - return r.json() - - def get_article(self, id): - self.logger.info('fetching article: %s', id) - r = self._do_post(self._mim_url('News/news/GetArticle'), - data={'id': id}) - return r.json() - - - def appsetup(self): - self.logger.info('appsetup') - r = self._do_get(self._mim_url('account/PairedDevices/PairedDevices')) - return r.json() - - def get_newsimage(self, id): - self.logger.info('fetching article image: %s', id) - os.makedirs('images', exist_ok=True) - filename = 'images/{}.image'.format(id) - if os.path.isfile(filename): - self.logger.info('image %s already downloaded', filename) - return filename - url = self._mim_url('News/NewsImage/GetImage?id={}'.format(id)) - r = self._do_get(url) - if r.status_code != 200: - return False - from PIL import Image - from resizeimage import resizeimage - import io - si = io.BytesIO(r.content) - image = Image.open(si) - self.logger.info('image size: %d', image.size[0]) - - if image.size[0] > 800: - image = resizeimage.resize_width(image, 800) - image.save(filename, image.format) - return filename - - def get_calendar(self): - data = { - 'UTCOffset': '-120', - 'start': '2018-09-01', - 'end': '2019-09-01' - } - self.logger.info('fetching calendar') - r = self._do_post( - self._mim_url('Calendar/Calendar/getEntries'), - data=data - ) - return r.json() - - def get_homework(self, offset=0): - now = datetime.datetime.now() - dayofweek = now.weekday() - startofweek = now - datetime.timedelta(days=dayofweek) - startofweek -= datetime.timedelta(days=offset*7) - timestamp = startofweek.strftime('%Y-%m-%dT00:00:00.000Z') - data = { - 'date': timestamp, - 'isWeek': True, - } - r = self._do_post( - self._mim_url('Homework/homework/GetHomework'), - data=data - ) - return r.json() - - def get_timetable(self): - now = datetime.datetime.now() - dayofweek = now.weekday() - startofweek = now - datetime.timedelta(days=dayofweek) - endofweek = now + datetime.timedelta(days=(5-dayofweek)+7) - start = startofweek.strftime('%Y-%m-%d') - end = endofweek.strftime('%Y-%m-%d') - data = { - 'UTCOffset': '-120', - 'start': start, - 'end': end - } - self.logger.info('fetching timetable') - r = self._do_post( - self._mim_url('timetable/timetable/gettimetablelist'), - data=data - ) - return r.json() - - -def send_status_update(client, info): - pushover.Client(client).send_message( - info, - title='Statusinfo', - html=False, - timestamp=True - ) - - -class flock(object): - filename = '.im.lock' - - def __init__(self): - self.pid = os.getpid() - - def aquire(self): - if self.is_locked(): - return False - with open(self.filename, 'w+') as f: - f.write('{}'.format(self.pid)) - return True - - def release(self): - if self.own_lock(): - os.unlink(self.filename) - - def __del__(self): - self.release() - - def own_lock(self): - lockinfo = self._get_lockinfo() - return lockinfo == self.pid - - def is_locked(self): - lockinfo = self._get_lockinfo() - if not lockinfo: - return False - return self._is_process_active(lockinfo) - - def _is_process_active(self, pid): - try: - os.kill(pid, 0) - return pid != self.pid - except Exception as e: - return False - - def _get_lockinfo(self): - try: - lock = {} - with open(self.filename, 'r') as f: - pid = int(f.read().strip()) - return pid - except Exception as e: - return False - - - - -def main(): - logger.info('STARTING-------------------- {}'.format(os.getpid())) - lock = flock() - if not lock.aquire(): - logger.info('EXITING - PREVIOUS IS RUNNING') - logger.info('ENDING--------------------- {}'.format(os.getpid())) - return - - db_users = db.create_table( - 'user', - primary_id='username', - primary_type=db.types.string - ) - db_api_status = db.create_table( - 'api_status', - primary_id='username', - primary_type=db.types.string - ) - users = [ u['username'] for u in db_users ] - for user in users: - user = db_users.find_one(username=user) - logger.info('==== USER: {} ====='.format(user['username'])) - if user['password'] == '': - logger.warning('User %s not enabled', user['username']) - continue - now = datetime.datetime.now() - ni = NewsInformer(**user, logger=logger) - statusinfo = {'username': user['username'], - 'date': now, 'ok': False, 'info': '', 'degraded_count':0} - try: - ni.notify_news() - ni.notify_homework() - statusinfo['ok'] = True - statusinfo['degraded'] = False - except Exception as e: - inforstr = 'Exception occured:\n{}:{}\n'.format(type(e).__name__, e) - statusinfo['ok'] = False - statusinfo['info'] = inforstr - logger.exception("Something went wrong") - finally: - previous_status = db_api_status.find_one(username=user['username']) - if previous_status is not None: - if previous_status['ok'] == True and statusinfo['ok'] == False: - logger.error('Switching to degraded state %s', user['username']) - statusinfo['degraded'] = True - statusinfo['degraded_count'] = 1 - if previous_status['degraded'] == True and statusinfo['ok'] == False: - if statusinfo['degraded_count'] == 1 and user['wantstatus']: - send_status_update(user['pushover'], statusinfo['info']) - try: - statusinfo['degraded_count'] = previous_status['degraded_count'] + 1 - except KeyError as e: - statusinfo['degraded_count'] = 1 - if previous_status['degraded'] == True and statusinfo['ok'] == True: - statusinfo['info'] = 'Works as expected, failed {} times'.format(previous_status['degraded_count']) - statusinfo['degraded_count'] = 0 - if user['wantstatus']: - send_status_update(user['pushover'], statusinfo['info']) - - db_api_status.upsert(statusinfo, ['username']) - logger.info('ENDING--------------------- {}'.format(os.getpid())) - -def test(): - logger.info('STARTING-------------------- {}'.format(os.getpid())) - lock = flock() - if not lock.aquire(): - logger.info('EXITING - PREVIOUS IS RUNNING') - logger.info('ENDING--------------------- {}'.format(os.getpid())) - return - - db_users = db.create_table( - 'user', - primary_id='username', - primary_type=db.types.string - ) - db_api_status = db.create_table( - 'api_status', - primary_id='username', - primary_type=db.types.string - ) - for user in db_users: - logger.info('==== USER: {} ====='.format(user['username'])) - if user['password'] == '': - logger.warning('User %s not enabled', user['username']) - continue - now = datetime.datetime.now() - ni = NewsInformer(**user, logger=logger) - #ni.notify_timetable() - #ni.notify_news() - ni.appSetup() - - - -if __name__ == "__main__": - main() -