From fc450f633e23736159b77b91778004413817db1e Mon Sep 17 00:00:00 2001 From: dymik739 Date: Tue, 5 Sep 2023 19:22:49 +0300 Subject: [PATCH 01/12] update .gitignore to not track __pycache__ folders --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e7d28ec..4d75c18 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ config/* modules/irc-bridge/error.log +__pycache__/ From 2b9ac41ced7c503e2801810827342a0d9ac8ad08 Mon Sep 17 00:00:00 2001 From: dymik739 Date: Tue, 5 Sep 2023 20:40:57 +0300 Subject: [PATCH 02/12] improve error reporting system, switch to schedule-v2 format in auto-schedule-pro-v2 --- main.py | 4 ++ module-testing.py | 4 ++ modules/auto-schedule-pro-v2/main.py | 83 ++++++++++++++++++++++------ 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/main.py b/main.py index 382d0f8..a210ad9 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,7 @@ import sys import os import threading import importlib +import traceback # global variables STOP_REQUESTED = False @@ -57,6 +58,7 @@ class ModuleV1: return self.RESPONSE except Exception as e: print(f"[ERROR] module v1: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"") + print(f"[ERROR] module v1: traceback:\ntraceback.format_exc()") return "" @@ -75,6 +77,7 @@ class ModuleV2: return self.obj.process(msg, self.path) except Exception as e: print(f"[ERROR] module v2: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"") + print(f"[ERROR] module v2: traceback:\ntraceback.format_exc()") return None @@ -213,6 +216,7 @@ def queue_processor(): time.sleep(1) except Exception as e: print(f"[ERROR] queue_processor: current message queue: {message_queue}") + print(f"[ERROR] Traceback:\n{traceback.format_exc()}") print(f"[ERROR] queue_processor: error while processing message ({e}), trying to delete it...") try: del message_queue[0] diff --git a/module-testing.py b/module-testing.py index 1db62da..af6981f 100644 --- a/module-testing.py +++ b/module-testing.py @@ -6,6 +6,7 @@ import sys import os import threading import importlib +import traceback from telegram import Message, Chat @@ -58,6 +59,7 @@ class ModuleV1: return self.RESPONSE except Exception as e: print(f"[ERROR] module v1: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"") + print(f"[ERROR] module v1: traceback:\ntraceback.format_exc()") return "" @@ -77,6 +79,7 @@ class ModuleV2: return responce except Exception as e: print(f"[ERROR] module v2: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"") + print(f"[ERROR] module v2: traceback:\n{traceback.format_exc()}") return None @@ -203,6 +206,7 @@ def queue_processor(): except Exception: print(f"[ERROR] queue_processor: current message queue: {message_queue}") + print(f"[ERROR] Traceback:\n{traceback.format_exc()}") print("[ERROR] queue_processor: error while processing message, trying to delete it...") try: del message_queue[0] diff --git a/modules/auto-schedule-pro-v2/main.py b/modules/auto-schedule-pro-v2/main.py index d2f1634..3014ae7 100644 --- a/modules/auto-schedule-pro-v2/main.py +++ b/modules/auto-schedule-pro-v2/main.py @@ -10,6 +10,7 @@ def readfile(filename): # global constants # Accusative - znahidnyj WEEKDAYS_ACCUSATIVE = ["понеділок", "вівторок", "середу", "четвер", "п'ятницю", "суботу", "неділю"] + # Genitive - rodovyj WEEKDAYS_GENITIVE_NEXT = ["наступного понеділка", "наступного вівторка", "наступної середи", "наступного четверга", "наступної п'ятниці", "наступної суботи", "наступної неділі"] @@ -17,9 +18,25 @@ WEEKDAYS_GENITIVE_NEXT = ["наступного понеділка", "насту WEEKDAYS_GENITIVE_THIS = ["цього понеділка", "цього вівторка", "цієї середи", "цього четверга", "цієї п'ятниці", "цієї суботи", "цієї неділі"] +lesson_types_to_strings = { + "lec": "лекція", + "prac": "практика", + "lab": "лабораторна" +} + + # global variables module_path = "" +def escaped_string(input_string): + result_string = input_string + + symbols_to_escape = ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'] + + for symbol in symbols_to_escape: + result_string = result_string.replace(symbol, f"\\{symbol}") + + return result_string def get_human_readable_date(start_datetime, end_datetime, current_day, current_week): @@ -43,25 +60,34 @@ def get_human_readable_date(start_datetime, end_datetime, return human_readable_date -def generate_lesson_description(lesson, start_datetime, end_datetime, current_day, current_week, overrides={}): - output_settings = {"name": True, "date": True, "teacher": True, "link": True} +def get_name_of_lesson_type(lesson_type): + if lesson_type in lesson_types_to_strings: + return lesson_types_to_strings[lesson_type] + + +def generate_lesson_description(lesson, start_datetime, end_datetime, current_day, current_week, overrides={}, + custom_name_prefix="*Назва*"): + output_settings = {"name": True, "date": True, "teacher": True, "link": True, "comment": True} output_settings.update(overrides) result = "" if output_settings['name']: - result += f"{lesson['name']}\n" + result += f"{custom_name_prefix}: {escaped_string(lesson['name'])} ({escaped_string(get_name_of_lesson_type(lesson['type']))})\n" if output_settings['date']: human_readable_date = get_human_readable_date(start_datetime, end_datetime, current_day, current_week) - result += f"*Дата*: {human_readable_date}\n" + result += f"*Дата*: {escaped_string(human_readable_date)}\n" if output_settings['teacher']: - result += f"*Викладач*: {lesson['teacher']}\n" + result += f"*Викладач*: {escaped_string(lesson['teacher'])}\n" if output_settings['link']: - result += f"*Посилання*: {lesson['link']}" + result += f"*Посилання*: {escaped_string(lesson['link'])}\n" + + if output_settings['comment'] and 'comment' in lesson: + result += f"*Примітка*: {escaped_string(lesson['comment'])}" return result @@ -76,8 +102,15 @@ def get_schedule_data_from(filename): timestamp = day_number * 86400 + int(lesson_time.split(":")[0]) * 3600 \ + int(lesson_time.split(":")[1]) * 60 - new_record = dict(raw_schedule[day_number][lesson_time]) - new_record["source"] = filename.split(".json")[0] + new_record = raw_schedule[day_number][lesson_time] + item_source = filename.split(".json")[0] + + if new_record.__class__ == list: + for item in new_record: + item["source"] = item_source + else: + new_record["source"] = item_source + baked_schedule[timestamp] = new_record return baked_schedule @@ -105,14 +138,30 @@ def process_arguments(args, base_day): return preferences, selected_day -def get_lesson_description(schedule, reference_time, lesson_time, current_day, current_week, overrides={}): +def get_lesson_description(schedule, reference_time, lesson_time, current_day, current_week, overrides={}, + custom_name_prefix="*Назва*"): lesson_record = schedule[lesson_time] lesson_start_datetime = datetime.fromtimestamp(reference_time + lesson_time) lesson_end_datetime = datetime.fromtimestamp(reference_time + lesson_time + 5400) - return generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, - current_week, overrides=overrides) + if lesson_record.__class__ == dict: + return generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, + current_week, overrides=overrides) + elif lesson_record.__class__ == list: + user_defined_overrides = dict(overrides) + internal_overrides = dict(overrides) + internal_overrides['date'] = False + + descriptions = [generate_lesson_description(i, lesson_start_datetime, lesson_end_datetime, current_day, + current_week, overrides=internal_overrides, custom_name_prefix=custom_name_prefix) for i in lesson_record] + + if 'date' in user_defined_overrides and not user_defined_overrides['date']: + return "\n\n".join(descriptions) + else: + human_readable_date = get_human_readable_date(lesson_start_datetime, lesson_end_datetime, + current_day, current_week) + return f"__{human_readable_date.capitalize()}__:\n" + "\n\n".join(descriptions) def process(message, path): @@ -130,8 +179,8 @@ def process(message, path): global module_path module_path = path - schedule = get_schedule_data_from("schedule.json") - schedule.update(get_schedule_data_from("additions.json")) + schedule = get_schedule_data_from("schedule-v2.json") + schedule.update(get_schedule_data_from("additions-v2.json")) current_time = datetime.now() @@ -161,8 +210,8 @@ def process(message, path): else: closest_lesson_time = min(schedule) - return "*Актуальна пара*: " + get_lesson_description(schedule, reference_time, closest_lesson_time, current_day, - current_week) + return get_lesson_description(schedule, reference_time, closest_lesson_time, current_day, + current_week, custom_name_prefix="*Актуальна пара*") elif base_command == "!пари": base_day = current_week * 7 + current_day @@ -176,8 +225,8 @@ def process(message, path): lesson_list = [i for i in schedule if selected_day * 86400 <= i < (selected_day + 1) * 86400] - lesson_descriptions_list = ["*Назва*: " + get_lesson_description(schedule, reference_time, lesson_time, - current_day, current_week, overrides=preferences) + lesson_descriptions_list = [get_lesson_description(schedule, reference_time, lesson_time, current_day, + current_week, overrides=preferences, custom_name_prefix="*Назва*") for lesson_time in lesson_list] return f"__Пари у {WEEKDAYS_ACCUSATIVE[selected_day % 7]}__:\n" + "\n\n".join(lesson_descriptions_list) From 0497cbf9b7ab4db1fc77d32e7ec5d8b0eac12c93 Mon Sep 17 00:00:00 2001 From: dymik739 Date: Wed, 6 Sep 2023 17:22:26 +0300 Subject: [PATCH 03/12] add support for parse_mode selection in all module APIs and update modules to respect these changes --- main.py | 32 ++++++------ module-testing.py | 23 +++++---- modules/auto-schedule-pro-v2/main.py | 66 +++++++++++++++++------- modules/module-v2-example/index.py | 4 +- modules/transliteration-decoder/index.py | 11 +++- 5 files changed, 91 insertions(+), 45 deletions(-) diff --git a/main.py b/main.py index a210ad9..cdbeb92 100644 --- a/main.py +++ b/main.py @@ -40,6 +40,7 @@ class ModuleV1: # set environmental variables def set_env(self): self.RESPONSE = "" + self.FORMAT = "" def set_predefine(self): try: @@ -55,11 +56,11 @@ class ModuleV1: self.MESSAGE = msg try: exec(self.code) - return self.RESPONSE + return self.RESPONSE, self.FORMAT except Exception as e: print(f"[ERROR] module v1: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"") print(f"[ERROR] module v1: traceback:\ntraceback.format_exc()") - return "" + return "", None class ModuleV2: @@ -78,7 +79,7 @@ class ModuleV2: except Exception as e: print(f"[ERROR] module v2: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"") print(f"[ERROR] module v2: traceback:\ntraceback.format_exc()") - return None + return None, None # module control unit @@ -190,21 +191,22 @@ def queue_processor(): # modules are used in here for mod in mcu.modules: if mod.enabled: - if mod.version == 1 or mod.version == 2: - response = mod.process(msg) + if mod.version in [1, 2]: + response, formatting = mod.process(msg) if response: - ''' - # protecting output - symbols_to_escape = ['[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'] - for symbol in symbols_to_escape: - response = response.replace(symbol, f"\\{symbol}") - ''' + if formatting == None: + updater.bot.send_message(chat_id=msg.chat.id, text=response, + disable_web_page_preview=True) + print(f"Responded using module {mod.path} ({mod.alias}) with text: {response}") + break - updater.bot.send_message(chat_id=msg.chat.id, text=response, - disable_web_page_preview=True) - print(f"Responded using module {mod.path} ({mod.alias}) with text: {response}") - break + elif formatting in ["Markdown", "MarkdownV2", "HTML"]: + updater.bot.send_message(chat_id=msg.chat.id, text=response, + disable_web_page_preview=True, + parse_mode=formatting) + print(f"Responded using module {mod.path} ({mod.alias}) with text (using {formatting}): {response}") + break del message_queue[0] diff --git a/module-testing.py b/module-testing.py index af6981f..ef88846 100644 --- a/module-testing.py +++ b/module-testing.py @@ -41,6 +41,7 @@ class ModuleV1: # set environmental variables def set_env(self): self.RESPONSE = "" + self.FORMAT = "" def set_predefine(self): try: @@ -56,11 +57,11 @@ class ModuleV1: self.MESSAGE = msg try: exec(self.code) - return self.RESPONSE + return self.RESPONSE, self.FORMAT except Exception as e: print(f"[ERROR] module v1: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"") print(f"[ERROR] module v1: traceback:\ntraceback.format_exc()") - return "" + return "", None class ModuleV2: @@ -75,12 +76,11 @@ class ModuleV2: # running the module def process(self, msg): try: - responce = self.obj.process(msg, self.path) - return responce + return self.obj.process(msg, self.path) except Exception as e: print(f"[ERROR] module v2: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"") print(f"[ERROR] module v2: traceback:\n{traceback.format_exc()}") - return None + return None, None # module control unit @@ -191,11 +191,16 @@ def queue_processor(): # modules are used in here for mod in mcu.modules: if mod.enabled: - if mod.version == 1 or mod.version == 2: - response = mod.process(msg) + if mod.version in [1, 2]: + response, formatting = mod.process(msg) + if response: - print(f"Responded using module {mod.path} ({mod.alias}) with text: {response}") - break + if formatting == None: + print(f"Responded using module {mod.path} ({mod.alias}) with text: {response}") + break + elif formatting in ["Markdown", "MarkdownV2", "HTML"]: + print(f"Responded using module {mod.path} ({mod.alias}) with text (using {formatting}): {response}") + break del message_queue[0] else: diff --git a/modules/auto-schedule-pro-v2/main.py b/modules/auto-schedule-pro-v2/main.py index 3014ae7..38b8749 100644 --- a/modules/auto-schedule-pro-v2/main.py +++ b/modules/auto-schedule-pro-v2/main.py @@ -28,7 +28,7 @@ lesson_types_to_strings = { # global variables module_path = "" -def escaped_string(input_string): +def escaped_string_markdownV2(input_string): result_string = input_string symbols_to_escape = ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'] @@ -38,6 +38,18 @@ def escaped_string(input_string): return result_string + +def escaped_string_html(input_string): + result_string = input_string + + symbols_to_escape = ['<', '>', '/'] + + for symbol in symbols_to_escape: + result_string = result_string.replace(symbol, f"\\{symbol}") + + return result_string + + def get_human_readable_date(start_datetime, end_datetime, current_day, current_week): human_readable_date = "" @@ -66,28 +78,28 @@ def get_name_of_lesson_type(lesson_type): def generate_lesson_description(lesson, start_datetime, end_datetime, current_day, current_week, overrides={}, - custom_name_prefix="*Назва*"): + custom_name_prefix="Назва"): output_settings = {"name": True, "date": True, "teacher": True, "link": True, "comment": True} output_settings.update(overrides) result = "" if output_settings['name']: - result += f"{custom_name_prefix}: {escaped_string(lesson['name'])} ({escaped_string(get_name_of_lesson_type(lesson['type']))})\n" + result += f"{custom_name_prefix}: {escaped_string_html(lesson['name'])} ({escaped_string_html(get_name_of_lesson_type(lesson['type']))})\n" if output_settings['date']: human_readable_date = get_human_readable_date(start_datetime, end_datetime, current_day, current_week) - result += f"*Дата*: {escaped_string(human_readable_date)}\n" + result += f"Дата: {escaped_string_html(human_readable_date)}\n" if output_settings['teacher']: - result += f"*Викладач*: {escaped_string(lesson['teacher'])}\n" + result += f"Викладач: {escaped_string_html(lesson['teacher'])}\n" if output_settings['link']: - result += f"*Посилання*: {escaped_string(lesson['link'])}\n" + result += f"Посилання: {escaped_string_html(lesson['link'])}\n" if output_settings['comment'] and 'comment' in lesson: - result += f"*Примітка*: {escaped_string(lesson['comment'])}" + result += f"Примітка: {escaped_string_html(lesson['comment'])}\n" return result @@ -139,15 +151,31 @@ def process_arguments(args, base_day): def get_lesson_description(schedule, reference_time, lesson_time, current_day, current_week, overrides={}, - custom_name_prefix="*Назва*"): + custom_name_prefix="Назва", force_date_at_top=False): lesson_record = schedule[lesson_time] lesson_start_datetime = datetime.fromtimestamp(reference_time + lesson_time) lesson_end_datetime = datetime.fromtimestamp(reference_time + lesson_time + 5400) if lesson_record.__class__ == dict: - return generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, - current_week, overrides=overrides) + if force_date_at_top: + user_defined_overrides = dict(overrides) + internal_overrides = dict(overrides) + internal_overrides['date'] = False + + description = generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, + current_week, overrides=overrides) + + if 'date' in user_defined_overrides and not user_defined_overrides['date']: + return description + else: + human_readable_date = get_human_readable_date(lesson_start_datetime, lesson_end_datetime, + current_day, current_week) + return f"{human_readable_date.capitalize()}:\n" + description + else: + return generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, + current_week, overrides=overrides) + elif lesson_record.__class__ == list: user_defined_overrides = dict(overrides) internal_overrides = dict(overrides) @@ -157,11 +185,11 @@ def get_lesson_description(schedule, reference_time, lesson_time, current_day, c current_week, overrides=internal_overrides, custom_name_prefix=custom_name_prefix) for i in lesson_record] if 'date' in user_defined_overrides and not user_defined_overrides['date']: - return "\n\n".join(descriptions) + return "\n".join(descriptions) else: human_readable_date = get_human_readable_date(lesson_start_datetime, lesson_end_datetime, current_day, current_week) - return f"__{human_readable_date.capitalize()}__:\n" + "\n\n".join(descriptions) + return f"{human_readable_date.capitalize()}:\n" + "\n".join(descriptions) def process(message, path): @@ -174,7 +202,7 @@ def process(message, path): base_command = full_command[0].lower() if base_command not in ["!пара", "!пари"]: - return "" + return None, None global module_path module_path = path @@ -197,11 +225,11 @@ def process(message, path): current_ts = int(datetime.now().strftime("%s")) if -3600*4 < study_begin_ts - current_ts < 0: - return "Навчання незабаром розпочнеться!" + return "Навчання незабаром розпочнеться!", None elif 0 <= study_begin_ts - current_ts < 1209600: - return f"До навчання залишилося {study_begin_ts - current_ts} секунд..." + return f"До навчання залишилося {study_begin_ts - current_ts} секунд...", None elif study_begin_ts - current_ts >= 1209600: - return "Ви маєте законне право відпочити, пари почнуться не скоро" + return "Ви маєте законне право відпочити, пари почнуться не скоро", None upcoming_lessons = [timestamp for timestamp in schedule if timestamp > current_seconds - 5400] @@ -211,7 +239,7 @@ def process(message, path): closest_lesson_time = min(schedule) return get_lesson_description(schedule, reference_time, closest_lesson_time, current_day, - current_week, custom_name_prefix="*Актуальна пара*") + current_week, custom_name_prefix="Актуальна пара"), "HTML" elif base_command == "!пари": base_day = current_week * 7 + current_day @@ -226,7 +254,7 @@ def process(message, path): lesson_list = [i for i in schedule if selected_day * 86400 <= i < (selected_day + 1) * 86400] lesson_descriptions_list = [get_lesson_description(schedule, reference_time, lesson_time, current_day, - current_week, overrides=preferences, custom_name_prefix="*Назва*") + current_week, overrides=preferences, custom_name_prefix="Назва", force_date_at_top=True) for lesson_time in lesson_list] - return f"__Пари у {WEEKDAYS_ACCUSATIVE[selected_day % 7]}__:\n" + "\n\n".join(lesson_descriptions_list) + return f"Пари у {WEEKDAYS_ACCUSATIVE[selected_day % 7]}:\n\n" + "\n".join(lesson_descriptions_list), "HTML" diff --git a/modules/module-v2-example/index.py b/modules/module-v2-example/index.py index c48f583..dee9fbb 100644 --- a/modules/module-v2-example/index.py +++ b/modules/module-v2-example/index.py @@ -13,4 +13,6 @@ def get_num(): def process(message, path): if message.text == "!v2-testing": - return f"Testing successful - call number {get_num()}" + return f"Testing successful - call number {get_num()}", None + else: + return None, None diff --git a/modules/transliteration-decoder/index.py b/modules/transliteration-decoder/index.py index dcbf772..9415be2 100644 --- a/modules/transliteration-decoder/index.py +++ b/modules/transliteration-decoder/index.py @@ -1,6 +1,14 @@ command = self.MESSAGE['text'].split(" ", 2) command_length = len(command) +def escaped_string(unescaped_string): + result_string = str(unescaped_string) + + for i in ["/", "<", ">"]: + result_string = result_string.replace(i, f"\\{i}") + + return result_string + if (command[0] in self.aliases) and (1 <= command_length <= 3): try: models = json.loads(readfile(self.path + "translate_models.json")) @@ -25,7 +33,8 @@ if (command[0] in self.aliases) and (1 <= command_length <= 3): decoded_text = decoded_text.replace(i[0].capitalize(), i[1].capitalize()) decoded_text = decoded_text.replace(i[0].upper(), i[1].upper()) - self.RESPONSE = f"__Результат__\n{decoded_text}" + self.RESPONSE = f"Результат\n{escaped_string(decoded_text)}" + self.FORMAT = "HTML" except Exception as e: print(f"[translit-decoder] Got exception: {e}") From 67b3c4278bca52cbd6134ff9fc5d6f85cb39436b Mon Sep 17 00:00:00 2001 From: dymik739 Date: Wed, 6 Sep 2023 17:33:12 +0300 Subject: [PATCH 04/12] auto-schedule-pro-v2: fix date showing up twice during schedule lookup --- modules/auto-schedule-pro-v2/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auto-schedule-pro-v2/main.py b/modules/auto-schedule-pro-v2/main.py index 38b8749..48ea3af 100644 --- a/modules/auto-schedule-pro-v2/main.py +++ b/modules/auto-schedule-pro-v2/main.py @@ -164,7 +164,7 @@ def get_lesson_description(schedule, reference_time, lesson_time, current_day, c internal_overrides['date'] = False description = generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, - current_week, overrides=overrides) + current_week, overrides=internal_overrides) if 'date' in user_defined_overrides and not user_defined_overrides['date']: return description From 5dc78b8cb0efb6374ccf7236ba3660c2bd0bd36b Mon Sep 17 00:00:00 2001 From: dymik739 Date: Wed, 6 Sep 2023 17:36:18 +0300 Subject: [PATCH 05/12] auto-schedule-pro-v2: fix HTML escaping --- modules/auto-schedule-pro-v2/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auto-schedule-pro-v2/main.py b/modules/auto-schedule-pro-v2/main.py index 48ea3af..be2d394 100644 --- a/modules/auto-schedule-pro-v2/main.py +++ b/modules/auto-schedule-pro-v2/main.py @@ -42,7 +42,7 @@ def escaped_string_markdownV2(input_string): def escaped_string_html(input_string): result_string = input_string - symbols_to_escape = ['<', '>', '/'] + symbols_to_escape = ['<', '>'] for symbol in symbols_to_escape: result_string = result_string.replace(symbol, f"\\{symbol}") From e9bec0159f77557fa6aa1edb254a567b060004f8 Mon Sep 17 00:00:00 2001 From: dymik739 Date: Wed, 6 Sep 2023 17:39:58 +0300 Subject: [PATCH 06/12] auto-schedule-pro-v2: change spacing between schedule lookup parts --- modules/auto-schedule-pro-v2/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auto-schedule-pro-v2/main.py b/modules/auto-schedule-pro-v2/main.py index be2d394..ecd43f1 100644 --- a/modules/auto-schedule-pro-v2/main.py +++ b/modules/auto-schedule-pro-v2/main.py @@ -257,4 +257,4 @@ def process(message, path): current_week, overrides=preferences, custom_name_prefix="Назва", force_date_at_top=True) for lesson_time in lesson_list] - return f"Пари у {WEEKDAYS_ACCUSATIVE[selected_day % 7]}:\n\n" + "\n".join(lesson_descriptions_list), "HTML" + return f"Пари у {WEEKDAYS_ACCUSATIVE[selected_day % 7]}:\n\n\n" + "\n\n".join(lesson_descriptions_list), "HTML" From 1381ddb007d8b19a77e7dc0a7aacec494d96b685 Mon Sep 17 00:00:00 2001 From: dymik739 Date: Mon, 11 Sep 2023 21:36:24 +0300 Subject: [PATCH 07/12] auto-schedule-pro-v2: implement templates support for custom output theming --- modules/auto-schedule-pro-v2/main.py | 149 +++++++++++++++++- .../templates/legacy-vibrant/date.msg | 1 + .../templates/legacy-vibrant/multiple.msg | 3 + .../templates/legacy-vibrant/single.msg | 4 + .../templates/legacy/date.msg | 1 + .../templates/legacy/multiple.msg | 3 + .../templates/legacy/single.msg | 4 + .../templates/modern/date.msg | 1 + .../templates/modern/multiple.msg | 3 + .../templates/modern/single.msg | 4 + 10 files changed, 165 insertions(+), 8 deletions(-) create mode 100644 modules/auto-schedule-pro-v2/templates/legacy-vibrant/date.msg create mode 100644 modules/auto-schedule-pro-v2/templates/legacy-vibrant/multiple.msg create mode 100644 modules/auto-schedule-pro-v2/templates/legacy-vibrant/single.msg create mode 100644 modules/auto-schedule-pro-v2/templates/legacy/date.msg create mode 100644 modules/auto-schedule-pro-v2/templates/legacy/multiple.msg create mode 100644 modules/auto-schedule-pro-v2/templates/legacy/single.msg create mode 100644 modules/auto-schedule-pro-v2/templates/modern/date.msg create mode 100644 modules/auto-schedule-pro-v2/templates/modern/multiple.msg create mode 100644 modules/auto-schedule-pro-v2/templates/modern/single.msg diff --git a/modules/auto-schedule-pro-v2/main.py b/modules/auto-schedule-pro-v2/main.py index ecd43f1..98c63f4 100644 --- a/modules/auto-schedule-pro-v2/main.py +++ b/modules/auto-schedule-pro-v2/main.py @@ -6,6 +6,9 @@ def readfile(filename): with open(module_path + filename) as f: return f.read() +def writefile(filename, data): + with open(module_path + filename, 'w') as f: + f.write(data) # global constants # Accusative - znahidnyj @@ -28,6 +31,56 @@ lesson_types_to_strings = { # global variables module_path = "" +def get_preference_by_id(user_id, name): + if not os.path.exists(module_path + f"preference-db/{user_id}.json"): + return None + + raw_prefs = readfile(f"preference-db/{user_id}.json") + try: + preferences = json.loads(raw_prefs) + except Exception as e: + return None + + if not name in preferences: + return None + + return preferences['name'] + + +def get_all_preferences_by_id(user_id): + user_preferences = {"output-style": "legacy-vibrant"} + + for i in default_preferences: + override = get_preferences_by_id(i) + if override != None: + user_preferences[i] = override + + return user_preferences + + +def set_preference_by_id(user_id, name, value): + if not os.path.exists(module_path + "preference-db/"): + os.mkdir(module_path + "preference-db/") + + if os.path.exists(module_path + f"preference-db/{user_id}.json"): + try: + raw_prefs = readfile(f"preference-db/{user_id}.json") + preferences = json.loads(raw_prefs) + except Exception as e: + preferences = {} + else: + preferences = {} + + preferences[name] = value + + final_data = json.dumps(preferences) + writefile(final_data) + + +def load_template(template, part): + return readfile(f"templates/{template}/{part}.msg") + + def escaped_string_markdownV2(input_string): result_string = input_string @@ -76,7 +129,7 @@ def get_name_of_lesson_type(lesson_type): if lesson_type in lesson_types_to_strings: return lesson_types_to_strings[lesson_type] - +''' def generate_lesson_description(lesson, start_datetime, end_datetime, current_day, current_week, overrides={}, custom_name_prefix="Назва"): output_settings = {"name": True, "date": True, "teacher": True, "link": True, "comment": True} @@ -102,7 +155,49 @@ def generate_lesson_description(lesson, start_datetime, end_datetime, current_da result += f"Примітка: {escaped_string_html(lesson['comment'])}\n" return result +''' +def generate_lesson_description(lesson, start_datetime, end_datetime, current_day, current_week, overrides={}, + custom_name_prefix="Назва", template="legacy-vibrant"): + # temporarily not supported + #output_settings = {"name": True, "date": True, "teacher": True, "link": True, "comment": True} + #output_settings.update(overrides) + + if lesson.__class__ == dict: + active_template = load_template(template, "single") + + for i in ['name', 'teacher', 'link', 'comment']: + active_template = active_template.replace(f"%{i.upper()}%", lesson[i]) + + + human_readable_date = get_human_readable_date(start_datetime, end_datetime, + current_day, current_week) + + active_template = active_template.replace("%DATE%", human_readable_date) + active_template = active_template.replace("%TYPE%", get_name_of_lesson_type(lesson['type'])) + active_template = active_template.replace("%NAME_PREFIX%", custom_name_prefix) + + return active_template + + elif lesson.__class__ == list: + total_result = load_template(template, "date") + human_readable_date = get_human_readable_date(start_datetime, end_datetime, + current_day, current_week) + total_result = total_result.replace("%DATE%", human_readable_date) + + for l in lesson: + active_template = load_template(template, "multiple") + + for i in ['name', 'teacher', 'link', 'comment']: + active_template = active_template.replace(f"%{i.upper()}%", escaped_string(lesson[i])) + + active_template = active_template.replace("%DATE%", human_readable_date) + active_template = active_template.replace("%TYPE%", get_name_of_lesson_type(lesson['type'])) + active_template = active_template.replace("%NAME_PREFIX%", custom_name_prefix) + + total_result += active_template + "\n" + + return total_result def get_schedule_data_from(filename): raw_schedule = json.loads(readfile(filename)) @@ -151,12 +246,12 @@ def process_arguments(args, base_day): def get_lesson_description(schedule, reference_time, lesson_time, current_day, current_week, overrides={}, - custom_name_prefix="Назва", force_date_at_top=False): + custom_name_prefix="Назва", template="legacy-vibrant"): lesson_record = schedule[lesson_time] lesson_start_datetime = datetime.fromtimestamp(reference_time + lesson_time) lesson_end_datetime = datetime.fromtimestamp(reference_time + lesson_time + 5400) - + ''' if lesson_record.__class__ == dict: if force_date_at_top: user_defined_overrides = dict(overrides) @@ -190,6 +285,10 @@ def get_lesson_description(schedule, reference_time, lesson_time, current_day, c human_readable_date = get_human_readable_date(lesson_start_datetime, lesson_end_datetime, current_day, current_week) return f"{human_readable_date.capitalize()}:\n" + "\n".join(descriptions) + ''' + + return generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, + current_week, overrides=overrides, custom_name_prefix=custom_name_prefix) def process(message, path): @@ -201,7 +300,7 @@ def process(message, path): # one printable symbol, so this is already protected base_command = full_command[0].lower() - if base_command not in ["!пара", "!пари"]: + if base_command not in ["!пара", "!пари", "!schedule-ctl"]: return None, None global module_path @@ -220,17 +319,21 @@ def process(message, path): reference_time = int(current_time.strftime("%s")) - current_seconds + output_style_preference = get_preference_by_id(message['from']['id'], "output-style") + if base_command == "!пара": + # easter egg study_begin_ts = int(datetime(year=2023, month=9, day=4).strftime("%s")) current_ts = int(datetime.now().strftime("%s")) if -3600*4 < study_begin_ts - current_ts < 0: - return "Навчання незабаром розпочнеться!", None + return "Навчання от-от розпочнеться!", None elif 0 <= study_begin_ts - current_ts < 1209600: return f"До навчання залишилося {study_begin_ts - current_ts} секунд...", None elif study_begin_ts - current_ts >= 1209600: return "Ви маєте законне право відпочити, пари почнуться не скоро", None - + + # actual lesson finding code upcoming_lessons = [timestamp for timestamp in schedule if timestamp > current_seconds - 5400] if len(upcoming_lessons) > 0: @@ -239,7 +342,7 @@ def process(message, path): closest_lesson_time = min(schedule) return get_lesson_description(schedule, reference_time, closest_lesson_time, current_day, - current_week, custom_name_prefix="Актуальна пара"), "HTML" + current_week, custom_name_prefix="Актуальна пара", template=output_style_preference), "HTML" elif base_command == "!пари": base_day = current_week * 7 + current_day @@ -254,7 +357,37 @@ def process(message, path): lesson_list = [i for i in schedule if selected_day * 86400 <= i < (selected_day + 1) * 86400] lesson_descriptions_list = [get_lesson_description(schedule, reference_time, lesson_time, current_day, - current_week, overrides=preferences, custom_name_prefix="Назва", force_date_at_top=True) + current_week, overrides=preferences, custom_name_prefix="Назва", template=output_style_preference) for lesson_time in lesson_list] return f"Пари у {WEEKDAYS_ACCUSATIVE[selected_day % 7]}:\n\n\n" + "\n\n".join(lesson_descriptions_list), "HTML" + + elif base_command == "!schedule-ctl": + if base_command[1] == "list": + prefs = get_all_preferences_by_id(message['from']['id']) + return "Ваші персональні налаштування:\n" + [f"- {k} = {v}" for k, v in prefs.items()] + + elif base_command[1] == "set": + prefs = get_all_preferences_by_id(message['from']['id']) + + if base_command[2] in prefs: + if base_command[2] == "output-style": + if base_command[3] not in os.listdir(module_path + "templates/"): + return f"Стилю {base_command[3]} не існує; доступні варіанти: " \ + + ', '.join(os.listdir(module_path + "templates/")), "HTML" + + previous_value = prefs[base_command[2]] + prefs[base_command[2]] = base_command[3] + + set_preference_by_id(message['from']['id'], base_command[2], base_command[3]) + + return f"Змінено значення {base_command[2]}: {previous_value} -> {base_command[3]}", "HTML" + else: + return f"Такого налаштування не існує; переглянути наявні налаштування можна за допомогою команди !schedule-ctl list", "HTML" + + elif base_command[1] == "get": + requested_preference = get_preference_by_id(message['from']['id'], base_command[2]) + return f"Налаштування {base_command[2]} має значення {requested_preference}", "HTML" + + else: + return "Такої команди не існує", "HTML" diff --git a/modules/auto-schedule-pro-v2/templates/legacy-vibrant/date.msg b/modules/auto-schedule-pro-v2/templates/legacy-vibrant/date.msg new file mode 100644 index 0000000..5cdc8e1 --- /dev/null +++ b/modules/auto-schedule-pro-v2/templates/legacy-vibrant/date.msg @@ -0,0 +1 @@ +%DATE%: diff --git a/modules/auto-schedule-pro-v2/templates/legacy-vibrant/multiple.msg b/modules/auto-schedule-pro-v2/templates/legacy-vibrant/multiple.msg new file mode 100644 index 0000000..fd3369a --- /dev/null +++ b/modules/auto-schedule-pro-v2/templates/legacy-vibrant/multiple.msg @@ -0,0 +1,3 @@ +%NAME_PREFIX%: %NAME% (%TYPE%) +Викладач: %TEACHER% +Посилання: %LINK% diff --git a/modules/auto-schedule-pro-v2/templates/legacy-vibrant/single.msg b/modules/auto-schedule-pro-v2/templates/legacy-vibrant/single.msg new file mode 100644 index 0000000..da66381 --- /dev/null +++ b/modules/auto-schedule-pro-v2/templates/legacy-vibrant/single.msg @@ -0,0 +1,4 @@ +%NAME_PREFIX%: %NAME% (%TYPE%) +Дата: %DATE% +Викладач: %TEACHER% +Посилання: %LINK% diff --git a/modules/auto-schedule-pro-v2/templates/legacy/date.msg b/modules/auto-schedule-pro-v2/templates/legacy/date.msg new file mode 100644 index 0000000..0d6ea22 --- /dev/null +++ b/modules/auto-schedule-pro-v2/templates/legacy/date.msg @@ -0,0 +1 @@ +%DATE%: diff --git a/modules/auto-schedule-pro-v2/templates/legacy/multiple.msg b/modules/auto-schedule-pro-v2/templates/legacy/multiple.msg new file mode 100644 index 0000000..cfcdc79 --- /dev/null +++ b/modules/auto-schedule-pro-v2/templates/legacy/multiple.msg @@ -0,0 +1,3 @@ +%NAME_PREFIX%: %NAME% (%TYPE%) +Викладач: %TEACHER% +Посилання: %LINK% diff --git a/modules/auto-schedule-pro-v2/templates/legacy/single.msg b/modules/auto-schedule-pro-v2/templates/legacy/single.msg new file mode 100644 index 0000000..53f9f70 --- /dev/null +++ b/modules/auto-schedule-pro-v2/templates/legacy/single.msg @@ -0,0 +1,4 @@ +%NAME_PREFIX%: %NAME% (%TYPE%) +Дата: %DATE% +Викладач: %TEACHER% +Посилання: %LINK% diff --git a/modules/auto-schedule-pro-v2/templates/modern/date.msg b/modules/auto-schedule-pro-v2/templates/modern/date.msg new file mode 100644 index 0000000..5cdc8e1 --- /dev/null +++ b/modules/auto-schedule-pro-v2/templates/modern/date.msg @@ -0,0 +1 @@ +%DATE%: diff --git a/modules/auto-schedule-pro-v2/templates/modern/multiple.msg b/modules/auto-schedule-pro-v2/templates/modern/multiple.msg new file mode 100644 index 0000000..7eb14cd --- /dev/null +++ b/modules/auto-schedule-pro-v2/templates/modern/multiple.msg @@ -0,0 +1,3 @@ +%NAME% (%TYPE%) +Викладач: %TEACHER% +Посилання: %LINK% diff --git a/modules/auto-schedule-pro-v2/templates/modern/single.msg b/modules/auto-schedule-pro-v2/templates/modern/single.msg new file mode 100644 index 0000000..8b20b2f --- /dev/null +++ b/modules/auto-schedule-pro-v2/templates/modern/single.msg @@ -0,0 +1,4 @@ +%NAME% (%TYPE%) +Дата: %DATE% +Викладач: %TEACHER% +Посилання: %LINK% From a22fb2b4b139354d722f9d448ff8b33069e92dbe Mon Sep 17 00:00:00 2001 From: dymik739 Date: Mon, 11 Sep 2023 22:29:52 +0300 Subject: [PATCH 08/12] auto-schedule-pro-v2: hotfixing many issues at once to make the module work properly --- module-testing.py | 2 +- modules/auto-schedule-pro-v2/main.py | 64 ++++++++++--------- .../templates/legacy/date.msg | 2 +- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/module-testing.py b/module-testing.py index ef88846..820b976 100644 --- a/module-testing.py +++ b/module-testing.py @@ -60,7 +60,7 @@ class ModuleV1: return self.RESPONSE, self.FORMAT except Exception as e: print(f"[ERROR] module v1: module \"{self.path}\" ({self.alias}) raised exception \"{e}\"") - print(f"[ERROR] module v1: traceback:\ntraceback.format_exc()") + print(f"[ERROR] module v1: traceback:\n{traceback.format_exc()}") return "", None diff --git a/modules/auto-schedule-pro-v2/main.py b/modules/auto-schedule-pro-v2/main.py index 98c63f4..e5561f9 100644 --- a/modules/auto-schedule-pro-v2/main.py +++ b/modules/auto-schedule-pro-v2/main.py @@ -1,6 +1,6 @@ from datetime import datetime import json - +import os def readfile(filename): with open(module_path + filename) as f: @@ -43,15 +43,15 @@ def get_preference_by_id(user_id, name): if not name in preferences: return None - - return preferences['name'] + + return preferences[name] def get_all_preferences_by_id(user_id): user_preferences = {"output-style": "legacy-vibrant"} - for i in default_preferences: - override = get_preferences_by_id(i) + for i in user_preferences: + override = get_preference_by_id(user_id, i) if override != None: user_preferences[i] = override @@ -62,6 +62,8 @@ def set_preference_by_id(user_id, name, value): if not os.path.exists(module_path + "preference-db/"): os.mkdir(module_path + "preference-db/") + preferences = {} + if os.path.exists(module_path + f"preference-db/{user_id}.json"): try: raw_prefs = readfile(f"preference-db/{user_id}.json") @@ -74,7 +76,7 @@ def set_preference_by_id(user_id, name, value): preferences[name] = value final_data = json.dumps(preferences) - writefile(final_data) + writefile(f"preference-db/{user_id}.json", final_data) def load_template(template, part): @@ -162,11 +164,11 @@ def generate_lesson_description(lesson, start_datetime, end_datetime, current_da # temporarily not supported #output_settings = {"name": True, "date": True, "teacher": True, "link": True, "comment": True} #output_settings.update(overrides) - + if lesson.__class__ == dict: active_template = load_template(template, "single") - for i in ['name', 'teacher', 'link', 'comment']: + for i in ['name', 'teacher', 'link']: active_template = active_template.replace(f"%{i.upper()}%", lesson[i]) @@ -188,11 +190,11 @@ def generate_lesson_description(lesson, start_datetime, end_datetime, current_da for l in lesson: active_template = load_template(template, "multiple") - for i in ['name', 'teacher', 'link', 'comment']: - active_template = active_template.replace(f"%{i.upper()}%", escaped_string(lesson[i])) + for i in ['name', 'teacher', 'link']: + active_template = active_template.replace(f"%{i.upper()}%", l[i]) active_template = active_template.replace("%DATE%", human_readable_date) - active_template = active_template.replace("%TYPE%", get_name_of_lesson_type(lesson['type'])) + active_template = active_template.replace("%TYPE%", get_name_of_lesson_type(l['type'])) active_template = active_template.replace("%NAME_PREFIX%", custom_name_prefix) total_result += active_template + "\n" @@ -288,7 +290,7 @@ def get_lesson_description(schedule, reference_time, lesson_time, current_day, c ''' return generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, - current_week, overrides=overrides, custom_name_prefix=custom_name_prefix) + current_week, overrides=overrides, custom_name_prefix=custom_name_prefix, template=template) def process(message, path): @@ -319,7 +321,9 @@ def process(message, path): reference_time = int(current_time.strftime("%s")) - current_seconds - output_style_preference = get_preference_by_id(message['from']['id'], "output-style") + output_style_preference = get_preference_by_id(message.from_user.id, "output-style") + if output_style_preference == None: + output_style_preference = "legacy-vibrant" if base_command == "!пара": # easter egg @@ -360,34 +364,34 @@ def process(message, path): current_week, overrides=preferences, custom_name_prefix="Назва", template=output_style_preference) for lesson_time in lesson_list] - return f"Пари у {WEEKDAYS_ACCUSATIVE[selected_day % 7]}:\n\n\n" + "\n\n".join(lesson_descriptions_list), "HTML" + return f"Пари у {WEEKDAYS_ACCUSATIVE[selected_day % 7]}:\n\n\n" + "\n".join(lesson_descriptions_list), "HTML" elif base_command == "!schedule-ctl": - if base_command[1] == "list": - prefs = get_all_preferences_by_id(message['from']['id']) - return "Ваші персональні налаштування:\n" + [f"- {k} = {v}" for k, v in prefs.items()] + if full_command[1] == "list": + prefs = get_all_preferences_by_id(message.from_user.id) + return "Ваші персональні налаштування:\n" + '\n'.join([f"- {k} = {v}" for k, v in prefs.items()]), "HTML" - elif base_command[1] == "set": - prefs = get_all_preferences_by_id(message['from']['id']) + elif full_command[1] == "set": + prefs = get_all_preferences_by_id(message.from_user.id) - if base_command[2] in prefs: - if base_command[2] == "output-style": - if base_command[3] not in os.listdir(module_path + "templates/"): - return f"Стилю {base_command[3]} не існує; доступні варіанти: " \ + if full_command[2] in prefs: + if full_command[2] == "output-style": + if full_command[3] not in os.listdir(module_path + "templates/"): + return f"Стилю {full_command[3]} не існує; доступні варіанти: " \ + ', '.join(os.listdir(module_path + "templates/")), "HTML" - previous_value = prefs[base_command[2]] - prefs[base_command[2]] = base_command[3] + previous_value = prefs[full_command[2]] + prefs[full_command[2]] = full_command[3] - set_preference_by_id(message['from']['id'], base_command[2], base_command[3]) + set_preference_by_id(message.from_user.id, full_command[2], full_command[3]) - return f"Змінено значення {base_command[2]}: {previous_value} -> {base_command[3]}", "HTML" + return f"Змінено значення {full_command[2]}: {previous_value} -> {full_command[3]}", "HTML" else: return f"Такого налаштування не існує; переглянути наявні налаштування можна за допомогою команди !schedule-ctl list", "HTML" - elif base_command[1] == "get": - requested_preference = get_preference_by_id(message['from']['id'], base_command[2]) - return f"Налаштування {base_command[2]} має значення {requested_preference}", "HTML" + elif full_command[1] == "get": + requested_preference = get_preference_by_id(message.from_user.id, full_command[2]) + return f"Налаштування {full_command[2]} має значення {requested_preference}", "HTML" else: return "Такої команди не існує", "HTML" diff --git a/modules/auto-schedule-pro-v2/templates/legacy/date.msg b/modules/auto-schedule-pro-v2/templates/legacy/date.msg index 0d6ea22..5cdc8e1 100644 --- a/modules/auto-schedule-pro-v2/templates/legacy/date.msg +++ b/modules/auto-schedule-pro-v2/templates/legacy/date.msg @@ -1 +1 @@ -%DATE%: +%DATE%: From 05a8039b1884d3f42edd35ee7a56745284a6c3a9 Mon Sep 17 00:00:00 2001 From: dymik739 Date: Tue, 12 Sep 2023 09:52:38 +0300 Subject: [PATCH 09/12] auto-schedule-pro-v2: add clear option to remove custom setting value and clean up old code remnants --- .gitignore | 1 + modules/auto-schedule-pro-v2/main.py | 97 +++++++++------------------- 2 files changed, 32 insertions(+), 66 deletions(-) diff --git a/.gitignore b/.gitignore index 4d75c18..b250f05 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ config/* modules/irc-bridge/error.log __pycache__/ +modules/auto-schedule-pro-v2/preference-db diff --git a/modules/auto-schedule-pro-v2/main.py b/modules/auto-schedule-pro-v2/main.py index e5561f9..22dcbac 100644 --- a/modules/auto-schedule-pro-v2/main.py +++ b/modules/auto-schedule-pro-v2/main.py @@ -79,6 +79,28 @@ def set_preference_by_id(user_id, name, value): writefile(f"preference-db/{user_id}.json", final_data) +def clear_preference_by_id(user_id, name, value): + if not os.path.exists(module_path + "preference-db/"): + os.mkdir(module_path + "preference-db/") + + preferences = {} + + if os.path.exists(module_path + f"preference-db/{user_id}.json"): + try: + raw_prefs = readfile(f"preference-db/{user_id}.json") + preferences = json.loads(raw_prefs) + except Exception as e: + preferences = {} + else: + preferences = {} + + if name in preferences: + del preferences[name] + + final_data = json.dumps(preferences) + writefile(f"preference-db/{user_id}.json", final_data) + + def load_template(template, part): return readfile(f"templates/{template}/{part}.msg") @@ -131,33 +153,6 @@ def get_name_of_lesson_type(lesson_type): if lesson_type in lesson_types_to_strings: return lesson_types_to_strings[lesson_type] -''' -def generate_lesson_description(lesson, start_datetime, end_datetime, current_day, current_week, overrides={}, - custom_name_prefix="Назва"): - output_settings = {"name": True, "date": True, "teacher": True, "link": True, "comment": True} - output_settings.update(overrides) - - result = "" - - if output_settings['name']: - result += f"{custom_name_prefix}: {escaped_string_html(lesson['name'])} ({escaped_string_html(get_name_of_lesson_type(lesson['type']))})\n" - - if output_settings['date']: - human_readable_date = get_human_readable_date(start_datetime, end_datetime, - current_day, current_week) - result += f"Дата: {escaped_string_html(human_readable_date)}\n" - - if output_settings['teacher']: - result += f"Викладач: {escaped_string_html(lesson['teacher'])}\n" - - if output_settings['link']: - result += f"Посилання: {escaped_string_html(lesson['link'])}\n" - - if output_settings['comment'] and 'comment' in lesson: - result += f"Примітка: {escaped_string_html(lesson['comment'])}\n" - - return result -''' def generate_lesson_description(lesson, start_datetime, end_datetime, current_day, current_week, overrides={}, custom_name_prefix="Назва", template="legacy-vibrant"): @@ -201,6 +196,7 @@ def generate_lesson_description(lesson, start_datetime, end_datetime, current_da return total_result + def get_schedule_data_from(filename): raw_schedule = json.loads(readfile(filename)) @@ -253,41 +249,6 @@ def get_lesson_description(schedule, reference_time, lesson_time, current_day, c lesson_start_datetime = datetime.fromtimestamp(reference_time + lesson_time) lesson_end_datetime = datetime.fromtimestamp(reference_time + lesson_time + 5400) - ''' - if lesson_record.__class__ == dict: - if force_date_at_top: - user_defined_overrides = dict(overrides) - internal_overrides = dict(overrides) - internal_overrides['date'] = False - - description = generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, - current_week, overrides=internal_overrides) - - if 'date' in user_defined_overrides and not user_defined_overrides['date']: - return description - else: - human_readable_date = get_human_readable_date(lesson_start_datetime, lesson_end_datetime, - current_day, current_week) - return f"{human_readable_date.capitalize()}:\n" + description - else: - return generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, - current_week, overrides=overrides) - - elif lesson_record.__class__ == list: - user_defined_overrides = dict(overrides) - internal_overrides = dict(overrides) - internal_overrides['date'] = False - - descriptions = [generate_lesson_description(i, lesson_start_datetime, lesson_end_datetime, current_day, - current_week, overrides=internal_overrides, custom_name_prefix=custom_name_prefix) for i in lesson_record] - - if 'date' in user_defined_overrides and not user_defined_overrides['date']: - return "\n".join(descriptions) - else: - human_readable_date = get_human_readable_date(lesson_start_datetime, lesson_end_datetime, - current_day, current_week) - return f"{human_readable_date.capitalize()}:\n" + "\n".join(descriptions) - ''' return generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, current_week, overrides=overrides, custom_name_prefix=custom_name_prefix, template=template) @@ -366,12 +327,12 @@ def process(message, path): return f"Пари у {WEEKDAYS_ACCUSATIVE[selected_day % 7]}:\n\n\n" + "\n".join(lesson_descriptions_list), "HTML" - elif base_command == "!schedule-ctl": + elif base_command == "!schedule-ctl" and len(full_command) >= 2: if full_command[1] == "list": prefs = get_all_preferences_by_id(message.from_user.id) return "Ваші персональні налаштування:\n" + '\n'.join([f"- {k} = {v}" for k, v in prefs.items()]), "HTML" - elif full_command[1] == "set": + elif full_command[1] == "set" and len(full_command) == 4: prefs = get_all_preferences_by_id(message.from_user.id) if full_command[2] in prefs: @@ -389,9 +350,13 @@ def process(message, path): else: return f"Такого налаштування не існує; переглянути наявні налаштування можна за допомогою команди !schedule-ctl list", "HTML" - elif full_command[1] == "get": + elif full_command[1] == "get" and len(full_command) == 3: requested_preference = get_preference_by_id(message.from_user.id, full_command[2]) return f"Налаштування {full_command[2]} має значення {requested_preference}", "HTML" + elif full_command[1] == "clear" and len(full_command) == 3: + reset_preference_by_id(message.from_user.id, full_command[2]) + return f"Очищено значення для налаштування {full_command[2]}, надалі для нього використовуватиметься стандартне значення", "HTML" + else: - return "Такої команди не існує", "HTML" + return "Такої команди не існує (або був використаний помилковий синтаксис)", "HTML" From e57773ccbb532fa006e5f001e47ee337bfa9ce28 Mon Sep 17 00:00:00 2001 From: dymik739 Date: Tue, 12 Sep 2023 10:04:00 +0300 Subject: [PATCH 10/12] auto-schedule-pro-v2: hotfixes and add "default" label next to auto-generated values --- modules/auto-schedule-pro-v2/main.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/auto-schedule-pro-v2/main.py b/modules/auto-schedule-pro-v2/main.py index 22dcbac..a3840a8 100644 --- a/modules/auto-schedule-pro-v2/main.py +++ b/modules/auto-schedule-pro-v2/main.py @@ -22,9 +22,9 @@ WEEKDAYS_GENITIVE_THIS = ["цього понеділка", "цього вівт "цієї суботи", "цієї неділі"] lesson_types_to_strings = { - "lec": "лекція", - "prac": "практика", - "lab": "лабораторна" + "lec": "лекція", + "prac": "практика", + "lab": "лабораторна" } @@ -50,6 +50,9 @@ def get_preference_by_id(user_id, name): def get_all_preferences_by_id(user_id): user_preferences = {"output-style": "legacy-vibrant"} + for i in user_preferences: + user_preferences[i] += " (default)" + for i in user_preferences: override = get_preference_by_id(user_id, i) if override != None: @@ -79,7 +82,7 @@ def set_preference_by_id(user_id, name, value): writefile(f"preference-db/{user_id}.json", final_data) -def clear_preference_by_id(user_id, name, value): +def clear_preference_by_id(user_id, name): if not os.path.exists(module_path + "preference-db/"): os.mkdir(module_path + "preference-db/") @@ -355,7 +358,7 @@ def process(message, path): return f"Налаштування {full_command[2]} має значення {requested_preference}", "HTML" elif full_command[1] == "clear" and len(full_command) == 3: - reset_preference_by_id(message.from_user.id, full_command[2]) + clear_preference_by_id(message.from_user.id, full_command[2]) return f"Очищено значення для налаштування {full_command[2]}, надалі для нього використовуватиметься стандартне значення", "HTML" else: From d6bd06109024bff06dd09e2e5ecfa406eac91987 Mon Sep 17 00:00:00 2001 From: dymik739 Date: Tue, 12 Sep 2023 11:44:03 +0300 Subject: [PATCH 11/12] auto-schedule-pro-v2: in schedule lookup set date to be always on top even if the lesson is single --- modules/auto-schedule-pro-v2/main.py | 42 +++++++++++++------ .../templates/legacy/date.msg | 2 +- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/modules/auto-schedule-pro-v2/main.py b/modules/auto-schedule-pro-v2/main.py index a3840a8..777daa6 100644 --- a/modules/auto-schedule-pro-v2/main.py +++ b/modules/auto-schedule-pro-v2/main.py @@ -158,26 +158,43 @@ def get_name_of_lesson_type(lesson_type): def generate_lesson_description(lesson, start_datetime, end_datetime, current_day, current_week, overrides={}, - custom_name_prefix="Назва", template="legacy-vibrant"): + custom_name_prefix="Назва", template="legacy-vibrant", force_date_at_top=False): # temporarily not supported #output_settings = {"name": True, "date": True, "teacher": True, "link": True, "comment": True} #output_settings.update(overrides) if lesson.__class__ == dict: - active_template = load_template(template, "single") + if force_date_at_top: + total_result = load_template(template, "date") + human_readable_date = get_human_readable_date(start_datetime, end_datetime, + current_day, current_week) + total_result = total_result.replace("%DATE%", human_readable_date) - for i in ['name', 'teacher', 'link']: - active_template = active_template.replace(f"%{i.upper()}%", lesson[i]) + total_result += load_template(template, "multiple") + for i in ['name', 'teacher', 'link']: + total_result = total_result.replace(f"%{i.upper()}%", lesson[i]) + + total_result = total_result.replace("%DATE%", human_readable_date) + total_result = total_result.replace("%TYPE%", get_name_of_lesson_type(lesson['type'])) + total_result = total_result.replace("%NAME_PREFIX%", custom_name_prefix) + + return total_result + "\n" + + else: + active_template = load_template(template, "single") + + for i in ['name', 'teacher', 'link']: + active_template = active_template.replace(f"%{i.upper()}%", lesson[i]) - human_readable_date = get_human_readable_date(start_datetime, end_datetime, + human_readable_date = get_human_readable_date(start_datetime, end_datetime, current_day, current_week) - active_template = active_template.replace("%DATE%", human_readable_date) - active_template = active_template.replace("%TYPE%", get_name_of_lesson_type(lesson['type'])) - active_template = active_template.replace("%NAME_PREFIX%", custom_name_prefix) + active_template = active_template.replace("%DATE%", human_readable_date) + active_template = active_template.replace("%TYPE%", get_name_of_lesson_type(lesson['type'])) + active_template = active_template.replace("%NAME_PREFIX%", custom_name_prefix) - return active_template + return active_template elif lesson.__class__ == list: total_result = load_template(template, "date") @@ -247,14 +264,15 @@ def process_arguments(args, base_day): def get_lesson_description(schedule, reference_time, lesson_time, current_day, current_week, overrides={}, - custom_name_prefix="Назва", template="legacy-vibrant"): + custom_name_prefix="Назва", template="legacy-vibrant", force_date_at_top=False): lesson_record = schedule[lesson_time] lesson_start_datetime = datetime.fromtimestamp(reference_time + lesson_time) lesson_end_datetime = datetime.fromtimestamp(reference_time + lesson_time + 5400) return generate_lesson_description(lesson_record, lesson_start_datetime, lesson_end_datetime, current_day, - current_week, overrides=overrides, custom_name_prefix=custom_name_prefix, template=template) + current_week, overrides=overrides, custom_name_prefix=custom_name_prefix, template=template, + force_date_at_top=force_date_at_top) def process(message, path): @@ -325,7 +343,7 @@ def process(message, path): lesson_list = [i for i in schedule if selected_day * 86400 <= i < (selected_day + 1) * 86400] lesson_descriptions_list = [get_lesson_description(schedule, reference_time, lesson_time, current_day, - current_week, overrides=preferences, custom_name_prefix="Назва", template=output_style_preference) + current_week, overrides=preferences, custom_name_prefix="Назва", template=output_style_preference, force_date_at_top=True) for lesson_time in lesson_list] return f"Пари у {WEEKDAYS_ACCUSATIVE[selected_day % 7]}:\n\n\n" + "\n".join(lesson_descriptions_list), "HTML" diff --git a/modules/auto-schedule-pro-v2/templates/legacy/date.msg b/modules/auto-schedule-pro-v2/templates/legacy/date.msg index 5cdc8e1..dfd8af3 100644 --- a/modules/auto-schedule-pro-v2/templates/legacy/date.msg +++ b/modules/auto-schedule-pro-v2/templates/legacy/date.msg @@ -1 +1 @@ -%DATE%: +%DATE%: From 604054af9ea115275912e2a882d88c857d742bf0 Mon Sep 17 00:00:00 2001 From: dymik739 Date: Tue, 12 Sep 2023 12:24:33 +0300 Subject: [PATCH 12/12] auto-schedule-pro-v2: add lookup capability to current lesson requests and make styling more flexible --- modules/auto-schedule-pro-v2/main.py | 49 ++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/modules/auto-schedule-pro-v2/main.py b/modules/auto-schedule-pro-v2/main.py index 777daa6..fa4e166 100644 --- a/modules/auto-schedule-pro-v2/main.py +++ b/modules/auto-schedule-pro-v2/main.py @@ -48,8 +48,13 @@ def get_preference_by_id(user_id, name): def get_all_preferences_by_id(user_id): - user_preferences = {"output-style": "legacy-vibrant"} + user_preferences = { + "output-style": "legacy-vibrant", + "output-style-lesson": "None", + "output-style-lookup": "None" + } + # label defaults as defaults and let custom settings override these labels for i in user_preferences: user_preferences[i] += " (default)" @@ -303,10 +308,6 @@ def process(message, path): reference_time = int(current_time.strftime("%s")) - current_seconds - output_style_preference = get_preference_by_id(message.from_user.id, "output-style") - if output_style_preference == None: - output_style_preference = "legacy-vibrant" - if base_command == "!пара": # easter egg study_begin_ts = int(datetime(year=2023, month=9, day=4).strftime("%s")) @@ -327,6 +328,32 @@ def process(message, path): else: closest_lesson_time = min(schedule) + # shifting lesson pointer if requested to do so + if len(full_command) >= 2: + possible_times = list(schedule.keys()) + current_pointer_position = possible_times.index(closest_lesson_time) + total_list_length = len(possible_times) + + if len(full_command[1]) > 1 and full_command[1][1:].isdigit(): + if full_command[1][0] == "+": + current_pointer_position = (current_pointer_position + int(full_command[1][1:])) % total_list_length + else: + current_pointer_position = (current_pointer_position - int(full_command[1][1:])) % total_list_length + + closest_lesson_time = possible_times[current_pointer_position] + + # getting corrent style + output_style_preference = "legacy-vibrant" + + general_output_style_preference = get_preference_by_id(message.from_user.id, "output-style") + if general_output_style_preference != None: + output_style_preference = general_output_style_preference + + specific_output_style_preference = get_preference_by_id(message.from_user.id, "output-style-lesson") + if specific_output_style_preference != None: + output_style_preference = specific_output_style_preference + + # returning generated pair description return get_lesson_description(schedule, reference_time, closest_lesson_time, current_day, current_week, custom_name_prefix="Актуальна пара", template=output_style_preference), "HTML" @@ -342,6 +369,16 @@ def process(message, path): lesson_list = [i for i in schedule if selected_day * 86400 <= i < (selected_day + 1) * 86400] + output_style_preference = "legacy-vibrant" + + general_output_style_preference = get_preference_by_id(message.from_user.id, "output-style") + if general_output_style_preference != None: + output_style_preference = general_output_style_preference + + specific_output_style_preference = get_preference_by_id(message.from_user.id, "output-style-lookup") + if specific_output_style_preference != None: + output_style_preference = specific_output_style_preference + lesson_descriptions_list = [get_lesson_description(schedule, reference_time, lesson_time, current_day, current_week, overrides=preferences, custom_name_prefix="Назва", template=output_style_preference, force_date_at_top=True) for lesson_time in lesson_list] @@ -357,7 +394,7 @@ def process(message, path): prefs = get_all_preferences_by_id(message.from_user.id) if full_command[2] in prefs: - if full_command[2] == "output-style": + if full_command[2] in ["output-style", "output-style-lesson", "output-style-lookup"]: if full_command[3] not in os.listdir(module_path + "templates/"): return f"Стилю {full_command[3]} не існує; доступні варіанти: " \ + ', '.join(os.listdir(module_path + "templates/")), "HTML"