from datetime import datetime import json def readfile(filename): with open(module_path + filename) as f: return f.read() # global constants # Accusative - znahidnyj WEEKDAYS_ACCUSATIVE = ["понеділок", "вівторок", "середу", "четвер", "п'ятницю", "суботу", "неділю"] # Genitive - rodovyj 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): human_readable_date = "" if ((current_day + 2) == int(start_datetime.strftime("%u"))) or \ ((current_day == 6) and (start_datetime.strftime("%u") == "1")): human_readable_date += "завтра " elif current_week != int(start_datetime.strftime("%W")) % 2: human_readable_date += f"{WEEKDAYS_GENITIVE_NEXT[int(start_datetime.strftime('%u')) - 1]} " elif current_day != (int(start_datetime.strftime("%u")) - 1): human_readable_date += f"{WEEKDAYS_GENITIVE_THIS[int(start_datetime.strftime('%u')) - 1]} " else: human_readable_date += "сьогодні " human_readable_date += "з " human_readable_date += start_datetime.strftime("%H:%M") human_readable_date += " до " human_readable_date += end_datetime.strftime("%H:%M") return human_readable_date 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(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"*Дата*: {escaped_string(human_readable_date)}\n" if output_settings['teacher']: result += f"*Викладач*: {escaped_string(lesson['teacher'])}\n" if output_settings['link']: result += f"*Посилання*: {escaped_string(lesson['link'])}\n" if output_settings['comment'] and 'comment' in lesson: result += f"*Примітка*: {escaped_string(lesson['comment'])}" return result def get_schedule_data_from(filename): raw_schedule = json.loads(readfile(filename)) baked_schedule = {} for day_number, lesson_times in enumerate(raw_schedule): for lesson_time in lesson_times: timestamp = day_number * 86400 + int(lesson_time.split(":")[0]) * 3600 \ + int(lesson_time.split(":")[1]) * 60 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 def process_arguments(args, base_day): selected_day = int(base_day) preferences = {} for arg in args: if arg[0] == "-": if arg[1:].isdigit(): selected_day -= int(arg[1:]) else: preferences[arg[1:]] = False elif arg[0] == "+": if arg[1:].isdigit(): selected_day += int(arg[1:]) else: preferences[arg[1:]] = True selected_day = selected_day % 14 return preferences, selected_day 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) 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): message_text = message["text"] full_command = message_text.split() # there is no need to check if the full_command list if empty as it # never will be - Telegram requires all messages to have at least # one printable symbol, so this is already protected base_command = full_command[0].lower() if base_command not in ["!пара", "!пари"]: return "" global module_path module_path = path schedule = get_schedule_data_from("schedule-v2.json") schedule.update(get_schedule_data_from("additions-v2.json")) current_time = datetime.now() current_week = current_time.isocalendar()[1] % 2 current_day = current_time.weekday() current_seconds = current_week * 604800 + current_day * 86400 + current_time.hour * 3600 + current_time.minute \ * 60 + current_time.second reference_time = int(current_time.strftime("%s")) - current_seconds if base_command == "!пара": 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 "Навчання незабаром розпочнеться!" elif 0 <= study_begin_ts - current_ts < 1209600: return f"До навчання залишилося {study_begin_ts - current_ts} секунд..." elif study_begin_ts - current_ts >= 1209600: return "Ви маєте законне право відпочити, пари почнуться не скоро" upcoming_lessons = [timestamp for timestamp in schedule if timestamp > current_seconds - 5400] if len(upcoming_lessons) > 0: closest_lesson_time = min(upcoming_lessons) else: closest_lesson_time = min(schedule) 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 if len(full_command) >= 2: args = [i for i in full_command[1:] if len(i) > 1] preferences, selected_day = process_arguments(args, base_day) else: preferences = {} selected_day = base_day 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="*Назва*") for lesson_time in lesson_list] return f"__Пари у {WEEKDAYS_ACCUSATIVE[selected_day % 7]}__:\n" + "\n\n".join(lesson_descriptions_list)