def getEntries(self, dayRange=8, dayOffset=0, **kwargs): key = get_cache_key(self.login_data, self.cache_key, dayRange=dayRange, dayOffset=dayOffset, useDate=True, **kwargs) logger.debug(key) entries = self._loadCache(key) error = None if entries is None: (error, bookings) = self.getAllBookings() #logger.debug(bookings) if not bookings: return None Range = namedtuple('Range', ['start', 'end']) # week = Range(start=datetime.now(), end=datetime.now() + timedelta(days=8)) entries = [] for booking, rules, start, duration in bookings: # print(list(rules)) for rule in rules.between( datetime.datetime.combine(datetime.datetime.now().date() + datetime.timedelta(days=dayOffset), datetime.time(0, 0)), datetime.datetime.combine(datetime.datetime.now().date() + datetime.timedelta(days=dayOffset + dayRange), datetime.time(23, 59)), inc=True): rule_start = datetime.datetime.combine(rule, start.time()) r = Range(start=rule_start, end=rule_start + duration) entries.append(self._make_entry(r, booking)) entries = self.sortBookings(entries, **kwargs) if not error: redis.set(key, pickle.dumps(entries), ex=24 * 3600) return error, entries
def searchEntries(self, text): text = text.lower() key = get_cache_key(self.login_data, self.cache_key + ':search', text) entry_data = self._loadCache(key) if entry_data is None: entries = [] error, bookings = self.getEntries(dayRange=365) # (error, data) = self._ajaxResponse() # if error or not data: # return None toomany = False for booking in bookings: if 'status_id' in booking and int(booking['status_id']) == 99: continue if any(key in booking and booking[key] and text in booking[key].lower() for key in ['descr', 'room', 'place', 'note']): # ['text', 'bezeichnung', 'ort', 'notizen']): #entries += self._parseBookings(booking) entries.append(booking) if len(entries) >= 10: toomany = True break # entries = self.sortBookings(entries) toomanymsg = "Zu viele Ergebnisse, zeige die ersten 10." if not error: redis.set(key, pickle.dumps((toomanymsg if toomany else None, entries)), ex=12*3600) if toomany: error = error + toomanymsg if error else toomanymsg else: error, entries = entry_data return error, entries
def parseGeburtstage(login_data): key = get_cache_key(login_data, 'birthdays', useDate=True) msg = loadCache(key) if not msg: (error, data) = getAjaxResponse('home', 'getBlockData', login_data=login_data, timeout=1800) if not data: print(error) return error else: try: html = data['blocks']['birthday']['html'] # soup = BeautifulSoup(html, 'html.parser') # comments = soup.find_all(string=lambda text:isinstance(text, Comment)) # [comment.extract() for comment in comments] # row = str(soup.table.tr) split = re.split( "<td><a (data-person-id='[^']+')[^>]+><img[^>]*></a><td[^>]*><a class='tooltip-person'[^>]*>([^<]+)</a><td[^>]*>([0-9]+)</?[^>]+>", html) msg = "" p_id = None for line in split: if not line: continue m = re.search('<th colspan="3">([^<]+)<tr>', line) m2 = re.match('data-person-id=\'([^\']+)\'', line) if m: msg += "<i>%s</i>\n" % m.group(1) elif m2: p_id = m2.group(1) msg += getPersonLink(login_data, p_id) elif re.match('[0-9]+', line): if p_id: msg += f"{line} /P{p_id}\n" else: msg += f"{line}\n" p_id = None elif re.match('[^<>]+', line): msg += "%s</a>: " % line if error: msg += f"\n<i>{error}</i>" else: redis.set(key, pickle.dumps(msg), ex=3600 * 24) except Exception as e: return "Error while parsing: %s" % e return msg
def _getCategories(self): key = get_cache_key(self.login_data, self.cache_key + 'master_data') cat_data = self._loadCache(key) if not cat_data: (error, data) = super()._ajaxResponse(func='getMasterData', timeout=None) if not data: return error, data categories = data['category'] cat_params = {} ctr = 0 for c in categories: cat_params[f'category_ids[{ctr}]'] = c ctr += 1 cat_data = categories, cat_params redis.set(key, pickle.dumps(cat_data), ex=7 * 24 * 3600) return cat_data
def getAllBookings(self): key = get_cache_key(self.login_data, self.cache_key, useDate=True) entries = self._loadCache(key) if not entries: entries = [] (error, data) = self._ajaxResponse() if not data: return error, entries for b in data: booking = data[b] if int(booking['status_id']) == 99: continue rules, start, duration = self._parseBooking(booking) entries.append((booking, rules, start, duration)) if not error: redis.set(key, pickle.dumps(entries), ex=12 * 3600) return error, entries return None, entries
def getAllBookings(self): self.categories, cat_params = self._getCategories() key = get_cache_key(self.login_data, self.cache_key, useDate=True) entries = self._loadCache(key) if not entries: (error, data) = self._ajaxResponse(**cat_params) if not data: return error, data entries = [] for c in data: category = data[c] for b in category: booking = category[b] rules, start, duration = self._parseBooking(booking) entries.append((booking, rules, start, duration)) if not error: redis.set(key, pickle.dumps(entries), ex=3600 * 12) return error, entries return None, entries
def download_file(login_data, url): key = get_cache_key(login_data, 'song:download', url) res = loadCache(key) if not res: (success, res) = login(login_data) if not success: return False, res try: # path = 'temp_file' logger.info(f"Donwloading {url}") r = requests.get(url, cookies=res, stream=True, timeout=20) if r.status_code == 200: res = {} if url.endswith('.txt') or url.endswith('.sng'): if url.endswith('.txt'): msg = [r.text] else: # sng msg = [r.text] res.update({ 'type': 'msg', 'msg': msg, }) else: bio = BytesIO(r.content) res.update({ 'type': 'file', 'file': bio, }) else: logger.warning(r) res['msg'] = r.text[:50] return False, res except Exception as e: logger.warning(e) res['msg'] = e return False, res redis.set(key, pickle.dumps(res)) return True, res
def calendar(context, update, login_data, mode_key, text): try: if text in RAUM_ZEIT_MARKUP: if text == 'Heute': msgs = parseCalendarByTime(login_data, dayRange=0) elif text == 'Nächste 7 Tage': msgs = parseCalendarByTime(login_data, dayRange=7) elif text == 'Morgen': msgs = parseCalendarByTime(login_data, dayRange=0, dayOffset=1) for msg in msgs: send_message(context, update, msg, telegram.ParseMode.HTML, mainMarkup()) elif text == 'Suche': redis.set(mode_key, 'calendar_search') send_message( context, update, "Gib den Namen des Kalendereintrags (oder einen Teil davon ein):", None, EMPTY_MARKUP) except Exception as e: eMsg = ''.join(traceback.format_exception(type(e), e, e.__traceback__)) msg = f"Failed!\nException: {eMsg}" logger.error(msg) send_message(context, update, msg, None, mainMarkup())
def getAjaxResponse(*args, login_data, isAjax=True, timeout=3600, additionalCacheKey=None, **params): key = get_cache_key(login_data, *args, additionalCacheKey=additionalCacheKey, **params) resp_str = redis.get(key) resp = json.loads(resp_str.decode('utf-8')) if resp_str else None if not resp or not timeout: relogin = False while True: (success, cookies) = login(login_data, updateCache=relogin) if not success: return cookies, None try: if isAjax: resp = cc_func(*args, cookies=cookies, login_data=login_data, params=params) else: resp = cc_api(*args, cookies=cookies, login_data=login_data, params=params) except Exception as e: resp_str = redis.get(key + "_latest") if resp_str: resp_time = float(redis.get(key + "_latest:time")) resp = json.loads(resp_str.decode('utf-8')) msg = f'Server unavailable. Data is from {datetime.fromtimestamp(resp_time)}' return msg, resp['data'] else: return "Error: Server unavailable!", None if resp['status'] == 'success': break elif relogin: break else: # retry relogin = True if resp['status'] != 'success' or 'data' not in resp: if 'message' in resp: return resp['message'], None else: return str(resp), None else: resp_str = json.dumps(resp) redis.set(key, resp_str, ex=timeout) redis.set(key + "_latest", resp_str) redis.set(key + "_latest:time", datetime.now().timestamp()) return None, resp['data']
def findGroup(login_data, name): key = get_cache_key(login_data, 'group:find', name) res = loadCache(key) error = None if not res or True: (error, data) = getAjaxResponse("db", "getMasterData", login_data=login_data, timeout=None) if not data: # or 'groups': return { 'success': False, 'msg': error, } res = { 'success': False, 'msg': [f"No group found with the name {name} :("], } matches = [] groups = data['groups'] if re.match('/G([0-9]+)', name): g_id = name[2:] if g_id in groups: matches.append(groups[g_id]) else: name = name.lower() for g in data['groups']: group = groups[g] bez = group['bezeichnung'] if name in bez.lower(): matches.append(group) t = [] if len(matches) == 0: pass elif len(matches) < 10: (error, persons) = getAjaxResponse("db", "getAllPersonData", login_data=login_data, timeout=24 * 3600) if not persons: return {'success': False, 'msg': error} for g in matches: g_id = g['id'] if t: t[-1] += '\n\n' url = urljoin(login_data['url'], f'?q=churchdb#GroupView/searchEntry:#{g_id}') if len(matches) == 1: #t.append(f'<a href="{url}">{g["bezeichnung"]}</a>\n') t += printGroup(login_data=login_data, group=g, persons=persons, masterData=data, list=False, onlyName=False) img_id = g['groupimage_id'] if img_id: try: img_data = getAjaxResponse( f'files/{img_id}/metadata', login_data=login_data, isAjax=False, timeout=24 * 3600) res['photo'] = urljoin(login_data['url'], img_data[1]['url']) except: pass else: t.append( f'<a href="{url}">{g["bezeichnung"]}</a> /G{g_id}\n') res.update({'msg': t, 'success': True}) elif len(matches) <= 50: for g in matches: g_id = g['id'] url = urljoin(login_data['url'], f'?q=churchdb#GroupView/searchEntry:#{g_id}') t.append(f'<a href="{url}">{g["bezeichnung"]}</a> /G{g_id}\n') res.update({'msg': t, 'success': True}) else: res.update({ 'msg': ['Zu viele Gruppen gefunden! Bitte Suche verfeinern'], 'success': False }) if error: res['msg'].append(f'\n<i>{error}</i>') else: redis.set(key, pickle.dumps(res), ex=7 * 24 * 3600) return res
def message(update, context): bot = context.bot user_id = update.message.from_user.id login_key = get_user_login_key(user_id) login_data_str = redis.get(login_key) text = update.message.text if login_data_str: login_data = json.loads(login_data_str) else: # not logged in, check if login data sent and don't continue afterwards check_login(context, update, text) return bot.send_chat_action(chat_id=update.message.chat_id, action=telegram.ChatAction.TYPING) mode = redis.get(mode_key(update)) if mode: mode = mode.decode('UTF-8') # print(f"In mode {mode}") redis.delete(mode_key(update)) if mode == 'calendar': calendar(context, update, login_data, mode_key(update), text) elif mode == 'rooms': if text in RAUM_ZEIT_MARKUP: cur_time_markup = [[f'{text}: {r}'] for r in room_markup] send_message(context, update, "Welche Räume?", telegram.ParseMode.HTML, ReplyKeyboardMarkup(cur_time_markup)) elif text in RAUM_EXTENDED_MARKUP: redis.set(mode_key(update), 'room_search') send_message( context, update, "Gib den Namen der Raumbelegung (oder einen Teil ein):", None, EMPTY_MARKUP) elif mode == 'song': (success, res) = songs.search(login_data, text) if success: for msg in res: send_message(context, update, msg, telegram.ParseMode.HTML, mainMarkup()) else: send_message(context, update, res, None, mainMarkup()) elif mode == 'person': success = person(context, update, text, mainMarkup(), login_data) if success is False: redis.set(mode_key(update), mode) elif mode == 'group': group(context, update, text, mainMarkup(), login_data=login_data) elif mode == 'room_search': msgs = parseRaeumeByText(login_data, text) for msg in msgs: send_message(context, update, msg, telegram.ParseMode.HTML, mainMarkup()) elif mode == 'calendar_search': try: msgs = parseCalendarByText(login_data, text) for msg in msgs: send_message(context, update, msg, telegram.ParseMode.HTML, mainMarkup()) except Exception as e: eMsg = ''.join( traceback.format_exception(type(e), e, e.__traceback__)) msg = f"Failed!\nException: {eMsg}" logger.error(msg) send_message(context, update, msg, None, mainMarkup()) elif mode == 'signup': parse_signup(context, update, login_data, mainMarkup(), text) else: # no special mode m1 = re.match('([A-Za-z0-9äöü ]+): ([A-Za-zäöü]+)', text) m2 = re.match('/dl_([0-9]+)_([0-9]+)', text) mPerson = re.match('/P([0-9]+)', text) mPersonContact = re.match('/C([0-9]+)', text) mGroup = re.match('/G([0-9]+)', text) mEvent = re.match('/E([0-9]+)', text) mQR = re.match('/Q([0-9]+)', text) mAgenda = re.match('/A([0-9]+)', text) mSong1 = re.match('/S([0-9]+)$', text) mSong2 = re.match('/S([0-9]+)_([0-9]+)', text) if m1: zeit = m1.group(1) room = m1.group(2) if m1 and zeit in RAUM_ZEIT_MARKUP and room in room_markup: try: if zeit == 'Heute': msgs = parseRaeumeByTime(login_data, room, dayRange=0) elif zeit == 'Nächste 7 Tage': msgs = parseRaeumeByTime(login_data, room, dayRange=7) elif zeit == 'Morgen': msgs = parseRaeumeByTime(login_data, room, dayRange=0, dayOffset=1) for msg in msgs: send_message(context, update, msg, telegram.ParseMode.HTML, mainMarkup()) except Exception as e: eMsg = ''.join( traceback.format_exception(type(e), e, e.__traceback__)) msg = f"Failed!\nException: {eMsg}" logger.error(msg) send_message(context, update, msg, None, mainMarkup()) elif m2: song_id = m2.group(1) file_id = m2.group(2) song(context, update, file_id, login_data, mainMarkup(), song_id) elif mPerson: person(context, update, text, mainMarkup(), login_data) elif mPersonContact: person(context, update, text, mainMarkup(), login_data, contact=True) elif mGroup: group(context, update, text, mainMarkup(), login_data=login_data) elif mEvent: g_id = mEvent.group(1) print_event(context, update, g_id, login_data, mainMarkup()) elif mQR: g_id = mQR.group(1) qr = groups.get_qrcode(login_data, g_id) if qr: try: bot.send_photo(update.effective_chat.id, photo=qr, caption="QR-Code fürs Check-In", parse_mode=telegram.ParseMode.HTML, reply_markup=mainMarkup(), timeout=30) except Exception as e: send_message( context, update, "<i>Konnte QR-Code nicht senden :(</i>\n" + str(e), telegram.ParseMode.HTML, mainMarkup()) else: send_message(context, update, "<i>Konnte QR-Code nicht abrufen :(</i>\n", telegram.ParseMode.HTML, mainMarkup()) elif mSong1 or mSong2: try: arrId = None if mSong1: songid = mSong1.group(1) else: songid = mSong2.group(1) arrId = mSong2.group(2) (success, msg) = songs.byID(login_data, song_id=songid, arrangement_id=arrId) send_message(context, update, msg, telegram.ParseMode.HTML, mainMarkup()) except Exception as e: eMsg = ''.join( traceback.format_exception(type(e), e, e.__traceback__)) msg = f"Failed!\nException: {eMsg}" logger.error(msg) send_message(context, update, msg, None, mainMarkup()) elif mAgenda: a_id = mAgenda.group(1) agenda(context, update, login_data, a_id, mainMarkup()) elif text == MARKUP_ROOMS: redis.set(mode_key(update), 'rooms') send_message( context, update, "Welcher Zeitraum?", telegram.ParseMode.HTML, ReplyKeyboardMarkup([RAUM_ZEIT_MARKUP, RAUM_EXTENDED_MARKUP])) elif text == MARKUP_CALENDAR: redis.set(mode_key(update), 'calendar') send_message( context, update, "Welcher Zeitraum?", telegram.ParseMode.HTML, ReplyKeyboardMarkup([RAUM_ZEIT_MARKUP, RAUM_EXTENDED_MARKUP])) elif text == MARKUP_BIRTHDAYS: try: msg = parseGeburtstage(login_data=login_data) send_message(context, update, msg, telegram.ParseMode.HTML, mainMarkup()) except Exception as e: msg = f"Failed!\nException: {e}" logger.error(msg) send_message(context, update, msg, None, mainMarkup()) elif text == MARKUP_SONGS: redis.set(mode_key(update), 'song') send_message(context, update, "Gib den Namen/Author (oder einen Teil davon ein):", None, EMPTY_MARKUP) elif text == MARKUP_PEOPLE: redis.set(mode_key(update), 'person') send_message( context, update, "Gib den Namen (oder einen Teil ein) oder eine Telefonnumer ein:", None, EMPTY_MARKUP) elif text == MARKUP_GROUPS: redis.set(mode_key(update), 'group') send_message(context, update, "Gib den Namen (oder einen Teil ein):", None, EMPTY_MARKUP) elif text == MARKUP_EVENTS: list_events(context, login_data, mainMarkup(), update) else: send_message( context, update, "Unbekannter Befehl, du kannst einen der Buttons unten nutzen", None, mainMarkup())
def login(login_data=None, updateCache=False, login_token=False): key = get_cache_key(login_data, 'login_cookies', usePerson=True) cookies_pickle = redis.get(key) cookies = pickle.loads(cookies_pickle) if cookies_pickle else None # Check if session cookie still valid if cookies and not updateCache: data = cc_func('resource', 'pollForNews', cookies, login_data=login_data) if not data or 'data' not in data: cookies = None else: data = data['data'] userid = data['userid'] if not userid or userid == -1: cookies = None if not cookies or updateCache: # need to login using permanent login key logger.info(f"Cookie is invalid for {login_data['personid']}") key_token = get_cache_key(login_data, 'login_token', usePerson=True) login_key_pickle = redis.get(key_token) login_key = pickle.loads( login_key_pickle) if login_key_pickle else None resp1 = requests.head(login_data['url']) cookies = resp1.cookies if not login_key or login_token: # login key not valid, try login token logger.info( f"Getting new login token for {login_data['personid']}") login_url = urljoin( login_data['url'], f"?q=profile&loginstr={login_data['token']}&id={login_data['personid']}" ) resp = requests.get(login_url, cookies=cookies) if 'Der verwendete Login-Link ist nicht mehr aktuell und kann deshalb nicht mehr verwendet werden.' in resp.text: redis.delete(get_user_login_key(login_data['telegramid'])) return False, 'Login fehlgeschlagen, versuchs es mit einem neuen Link.' else: # get new login key & cookies using login token data = cc_api(f'persons/{login_data["personid"]}/logintoken', cookies=cookies, login_data=login_data, returnJson=True) if data['status'] == 'success': inner_data = data['data'] # cookies = resp.cookies.get_dict() redis.set(key_token, pickle.dumps(inner_data['data'])) redis.set(key, pickle.dumps(cookies.get_dict())) else: return False, 'Login fehlgeschlagen, bitte log dich neu ein.' else: # get new cookies using login key try: token_url = f'whoami?login_token={login_key}&user_id={login_data["personid"]}' data = cc_api(token_url, cookies, login_data=login_data, returnJson=True) if data['status'] == 'success': logger.info(data) redis.set(key, pickle.dumps(cookies.get_dict())) else: logger.warning(data) return False, f'Login fehlgeschlagen, bitte log dich neu ein.' except Exception as e: return False, f'Could not renew token:\n{str(e)}' # redis.delete(get_user_login_key(login_data['telegramid'])) # return False, 'Login fehlgeschlagen, versuchs es mit einem neuen Link.' return True, cookies