From 99bb9607e58c4ece1c37a54fa765481ed7ff816c Mon Sep 17 00:00:00 2001 From: "Arthur K." Date: Wed, 14 May 2025 15:27:39 +0300 Subject: [PATCH 1/8] refactor: simplify deadline filtering and message generation logic --- main.py | 127 +++++++++++++++++++++----------------------------------- 1 file changed, 48 insertions(+), 79 deletions(-) diff --git a/main.py b/main.py index 54b798d..f625cf7 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,7 @@ from aiogram import Bot import datetime as dt import locale import urllib.parse +import re # Modify the links and data below: DEADLINES_URL = "https://m3104.nawinds.dev/DEADLINES.json" @@ -15,8 +16,8 @@ BOT_NAME = "Дединсайдер M3104" BOT_USERNAME = "m3104_deadliner_bot" # Environment variables that should be available: -TOKEN = os.getenv("TOKEN") -MAIN_GROUP_ID = int(os.getenv("MAIN_GROUP_ID")) +TOKEN: str = os.getenv("TOKEN") # type: ignore +MAIN_GROUP_ID = int(os.getenv("MAIN_GROUP_ID")) # type: ignore logging.basicConfig(level=logging.INFO) @@ -24,7 +25,7 @@ bot = Bot(TOKEN) NUMBER_EMOJIS = ['0.', '1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟'] -async def get_current_time() -> str: +def get_current_time() -> str: current_time = dt.datetime.now() current_time_hour = current_time.hour if current_time.hour >= 10 else "0" + str(current_time.hour) current_time_minute = current_time.minute if current_time.minute >= 10 else "0" + str(current_time.minute) @@ -35,7 +36,7 @@ def get_dt_obj_from_string(time: str) -> dt.datetime: locale.setlocale(locale.LC_TIME, 'en_US.UTF-8') return dt.datetime.strptime(time, "%d %b %Y %H:%M:%S %z") -async def generate_link(event_name: str, event_time: str) -> str: +def generate_link(event_name: str, event_time: str) -> str: dt_obj = get_dt_obj_from_string(event_time) formatted_time = dt_obj.strftime("%Y%m%d T%H%M%S%z") description = f"Дедлайн добавлен ботом {BOT_NAME} (https://t.me/{BOT_USERNAME})" @@ -45,7 +46,7 @@ async def generate_link(event_name: str, event_time: str) -> str: f"color=6" return link -async def get_human_timedelta(time: str) -> str: +def get_human_timedelta(time: str) -> str: dt_obj = get_dt_obj_from_string(time) dt_now = dt.datetime.now(dt_obj.tzinfo) # Ensure timezones are consistent delta = dt_obj - dt_now @@ -64,7 +65,7 @@ async def get_human_timedelta(time: str) -> str: else: return f"{hours}ч {minutes}м" -async def get_human_time(time: str) -> str: +def get_human_time(time: str) -> str: dt_obj = get_dt_obj_from_string(time) locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8') formatted_date = dt_obj.strftime("%a, %d %B в %H:%M") @@ -78,20 +79,15 @@ def timestamp_func(a: dict) -> float: def relevant_filter_func(d: dict) -> bool: dt_obj = get_dt_obj_from_string(d["time"]) - if dt_obj < dt.datetime.now(dt_obj.tzinfo): - return False - return True + return not dt_obj < dt.datetime.now(dt_obj.tzinfo) -def deadline_type_filter_func(d: dict, dtype: str) -> bool: - if f"[{dtype.lower()}]" in d["name"].lower(): - return True - return False +def deadline_type_filter_func(d: dict, dtype: str = '') -> bool: + if not dtype: + return not re.match(r'^\[.*\]', d['name']) -def deadlines_filter_func(d: dict) -> bool: - return (not deadline_type_filter_func(d, "тест") and - not deadline_type_filter_func(d, "лекция")) + return f"[{dtype.lower()}]" in d["name"].lower() -async def get_message_text() -> str: +def get_message_text() -> str: try: response = requests.get(DEADLINES_URL).json() except Exception as e: @@ -99,89 +95,62 @@ async def get_message_text() -> str: return "" all_deadlines = response["deadlines"] - deadlines = list(filter(lambda d: deadlines_filter_func(d) and relevant_filter_func(d), all_deadlines)) - tests = list(filter(lambda t: deadline_type_filter_func(t, "тест") and relevant_filter_func(t), all_deadlines)) - lectures = list(filter(lambda t: deadline_type_filter_func(t, "лекция") and relevant_filter_func(t), all_deadlines)) + types = [ + ('', ''), # deadlines + ('🧑‍💻 Тесты', 'тест'), + ('🎓 Лекции', 'лекция'), + ('🛡 Защиты', 'защита') + ] - text = f"🔥️️ Дедлайны (Обновлено в {await get_current_time()} 🔄):\n\n" + assignments = [(sorted(filter(lambda t: deadline_type_filter_func(t, x[1]) and relevant_filter_func(t), all_deadlines), + key=lambda z: timestamp_func(z)), x[0], x[1]) for x in types] - deadlines = sorted(deadlines, key=lambda x: timestamp_func(x)) - tests = sorted(tests, key=lambda x: timestamp_func(x)) - lectures = sorted(lectures, key=lambda x: timestamp_func(x)) + text = f"🔥️️ Дедлайны (Обновлено в {get_current_time()} 🔄):\n\n" - if len(deadlines) == 0: + if len(assignments[0]) == 0: text += "Дедлайнов нет)\n\n" - for i in range(len(deadlines)): - no = i + 1 - if no < 11: - no = NUMBER_EMOJIS[no] + " " - else: - no += ". " - text += str(no) + "" + def add_items(items: list, category_name: str = '', replace_name: str = ''): + if len(items) == 0: + return - if deadlines[i].get("url"): - text += f"{deadlines[i]['name']}" - else: - text += deadlines[i]["name"] + nonlocal text + REPLACE_PATTERN = re.compile(rf'^\[{replace_name}\]', flags=re.IGNORECASE) - text += " — " - text += await get_human_timedelta(deadlines[i]["time"]) - text += f"\n(" - text += await get_human_time(deadlines[i]["time"]) + ")\n\n" + if category_name: + text += f"\n{category_name}:\n\n" - if len(tests) > 0: - text += f"\n🧑‍💻 Тесты:\n\n" - - for i in range(len(tests)): - test_name = tests[i]["name"].replace("[Тест] ", "").replace("[тест]", "") - test_url = tests[i].get("url") + for i, item in enumerate(items): no = i + 1 - if no < 11: + if no <= 10: no = NUMBER_EMOJIS[no] + " " else: - no += ". " - text += str(no) + "" + no = str(no) + ". " - if test_url: - text += f"{test_name}" + text += no + "" + + name = re.sub(REPLACE_PATTERN, '', item['name']) + url = item.get('url') + + if url: + text += f"{name}" else: - text += test_name + text += name text += " — " - text += await get_human_timedelta(tests[i]["time"]) - text += f"\n(" - text += await get_human_time(tests[i]["time"]) + ")\n\n" + text += get_human_timedelta(item["time"]) + text += f"\n(" + text += get_human_time(item["time"]) + ")\n\n" - if len(lectures) > 0: - text += f"\n👨‍🏫 Лекции:\n\n" - - for i in range(len(lectures)): - lecture_name = lectures[i]["name"].replace("[Лекция] ", "").replace("[лекция]", "") - lecture_url = lectures[i].get("url") - no = i + 1 - if no < 11: - no = NUMBER_EMOJIS[no] + " " - else: - no += ". " - text += str(no) + "" - - if lecture_url: - text += f"{lecture_name}" - else: - text += lecture_name - - text += " — " - text += await get_human_timedelta(lectures[i]["time"]) - text += f"\n(" - text += await get_human_time(lectures[i]["time"]) + ")\n\n" + for assignment_type in assignments: + add_items(*assignment_type) text += f"\n🆕 " \ f"Добавить дедлайн" return text async def send_deadlines(chat_id: int) -> None: - text = await get_message_text() + text = get_message_text() if text == "Дедлайнов нет)\n\n": return @@ -192,7 +161,7 @@ async def send_deadlines(chat_id: int) -> None: while dt.datetime.now() - started_updating < dt.timedelta(days=1): await asyncio.sleep(60) try: - new_text = await get_message_text() + new_text = get_message_text() if text != new_text and new_text != "": await msg.edit_text(new_text, parse_mode="HTML", disable_web_page_preview=True) text = new_text From 486e58d3f484cc18272ddd4626c87bb64e494888 Mon Sep 17 00:00:00 2001 From: Nikita Aksenov Date: Wed, 14 May 2025 15:45:58 +0300 Subject: [PATCH 2/8] double space fixed --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index f625cf7..574cdd7 100644 --- a/main.py +++ b/main.py @@ -115,7 +115,7 @@ def get_message_text() -> str: return nonlocal text - REPLACE_PATTERN = re.compile(rf'^\[{replace_name}\]', flags=re.IGNORECASE) + REPLACE_PATTERN = re.compile(rf'^\[{replace_name}\] ', flags=re.IGNORECASE) if category_name: text += f"\n{category_name}:\n\n" From 07c609de8740824b76f7a8faa3ce1de884dd6ec7 Mon Sep 17 00:00:00 2001 From: "Arthur K." Date: Wed, 21 May 2025 15:04:06 +0300 Subject: [PATCH 3/8] chore: add Dockerfile and compose.yml --- .env.sample | 2 ++ .gitignore | 1 + Dockerfile | 15 +++++++++++++++ compose.yml | 7 +++++++ 4 files changed, 25 insertions(+) create mode 100644 .env.sample create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 compose.yml diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..4f2a5c2 --- /dev/null +++ b/.env.sample @@ -0,0 +1,2 @@ +TOKEN= +MAIN_GROUP_ID= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ed3af3e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.12-bookworm +STOPSIGNAL SIGINT +WORKDIR /app + +# make ru_RU.UTF-8 locale available +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install \ + --no-install-recommends -y locales && rm -r /var/lib/apt/lists/* && \ + printf "en_US.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8" >/etc/locale.gen && \ + dpkg-reconfigure --frontend=noninteractive locales + +COPY requirements.txt . +RUN pip install -r requirements.txt + +COPY main.py . +CMD ["python3", "-u", "main.py"] diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..c854143 --- /dev/null +++ b/compose.yml @@ -0,0 +1,7 @@ +services: + deadline_bot: + build: . + env_file: .env + volumes: + - /etc/localtime:/etc/localtime:ro + restart: unless-stopped From 331008aa14d5cbe8addb61f3d214cc29da6ae88f Mon Sep 17 00:00:00 2001 From: "Arthur K." Date: Wed, 21 May 2025 15:41:27 +0300 Subject: [PATCH 4/8] feat: add multiple env options to simplify personal deployments * ADD_CALENDAR_LINK: bool (defaults to True): do not send "Add to calendar" links * EDIT_MESSAGE_ID: int (default to None): do not send initial message, instead edit the given one. --- .env.sample | 2 ++ main.py | 70 ++++++++++++++++++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/.env.sample b/.env.sample index 4f2a5c2..c52b606 100644 --- a/.env.sample +++ b/.env.sample @@ -1,2 +1,4 @@ TOKEN= MAIN_GROUP_ID= +EDIT_MESSAGE_ID= +ADD_CALENDAR_LINK= diff --git a/main.py b/main.py index 574cdd7..9361307 100644 --- a/main.py +++ b/main.py @@ -1,13 +1,15 @@ -import datetime -import logging -import os -import requests import asyncio -from aiogram import Bot import datetime as dt import locale -import urllib.parse +import logging +import os import re +import urllib.parse + +import requests +from aiogram import Bot +from aiogram.client.default import DefaultBotProperties +from aiogram.types import LinkPreviewOptions # Modify the links and data below: DEADLINES_URL = "https://m3104.nawinds.dev/DEADLINES.json" @@ -16,12 +18,17 @@ BOT_NAME = "Дединсайдер M3104" BOT_USERNAME = "m3104_deadliner_bot" # Environment variables that should be available: -TOKEN: str = os.getenv("TOKEN") # type: ignore -MAIN_GROUP_ID = int(os.getenv("MAIN_GROUP_ID")) # type: ignore +TOKEN = os.getenv("TOKEN") +MAIN_GROUP_ID = int(os.getenv("MAIN_GROUP_ID") or '0') +EDIT_MESSAGE_ID = int(os.getenv("EDIT_MESSAGE_ID") or '0') +ADD_CALENDAR_LINK = os.getenv("ADD_CALENDAR_LINK") != 'false' + +assert TOKEN, "Missing token!" +assert MAIN_GROUP_ID, "Missing group ID!" logging.basicConfig(level=logging.INFO) -bot = Bot(TOKEN) +bot = Bot(TOKEN, default=DefaultBotProperties(parse_mode='HTML', link_preview=LinkPreviewOptions(is_disabled=True))) NUMBER_EMOJIS = ['0.', '1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟'] @@ -91,7 +98,7 @@ def get_message_text() -> str: try: response = requests.get(DEADLINES_URL).json() except Exception as e: - print(f"{datetime.datetime.now()} Failed to fetch deadlines: {e}") + print(f"{dt.datetime.now()} Failed to fetch deadlines: {e}") return "" all_deadlines = response["deadlines"] @@ -139,45 +146,58 @@ def get_message_text() -> str: text += " — " text += get_human_timedelta(item["time"]) - text += f"\n(" - text += get_human_time(item["time"]) + ")\n\n" + if ADD_CALENDAR_LINK: + text += f"\n(" + text += get_human_time(item["time"]) + ")\n\n" + else: + text += f'\n({get_human_time(item["time"])})\n\n' for assignment_type in assignments: add_items(*assignment_type) - text += f"\n🆕 " \ - f"Добавить дедлайн" + text += ( + f"\n🆕 " + f"Добавить дедлайн" + ) + return text async def send_deadlines(chat_id: int) -> None: text = get_message_text() - if text == "Дедлайнов нет)\n\n": - return - msg = await bot.send_message(chat_id, text, parse_mode="HTML", disable_web_page_preview=True) + if EDIT_MESSAGE_ID: + msg = await bot.edit_message_text(text, chat_id=chat_id, message_id=EDIT_MESSAGE_ID) + assert not isinstance(msg, bool), 'EDIT_MESSAGE_ID is not a message!' + else: + msg = await bot.send_message(chat_id, text, parse_mode="HTML", disable_web_page_preview=True) started_updating = dt.datetime.now() - print(datetime.datetime.now(), "Message sent. Msg id:", msg.message_id) + print(dt.datetime.now(), "Message sent. Msg id:", msg.message_id) - while dt.datetime.now() - started_updating < dt.timedelta(days=1): + condition = (lambda: True) if EDIT_MESSAGE_ID else (lambda: dt.datetime.now() - started_updating < dt.timedelta(days=1)) + while condition(): await asyncio.sleep(60) try: new_text = get_message_text() if text != new_text and new_text != "": await msg.edit_text(new_text, parse_mode="HTML", disable_web_page_preview=True) text = new_text - print(datetime.datetime.now(), "Message updated. Msg id:", msg.message_id) + print(dt.datetime.now(), "Message updated. Msg id:", msg.message_id) else: - print(datetime.datetime.now(), "Message update skipped. Msg id:", msg.message_id) + print(dt.datetime.now(), "Message update skipped. Msg id:", msg.message_id) except Exception as e: - logging.warning(datetime.datetime.now(),f"{datetime.datetime.now()} Error updating message: {e}") + logging.warning(dt.datetime.now(),f"{dt.datetime.now()} Error updating message: {e}") continue - await msg.delete() + + if not EDIT_MESSAGE_ID: + await msg.delete() async def main(): - await send_deadlines(MAIN_GROUP_ID) - await bot.session.close() + try: + await send_deadlines(MAIN_GROUP_ID) + finally: + await bot.session.close() if __name__ == '__main__': From bbcfa2a60117da21d2375d58ae637f6155995bba Mon Sep 17 00:00:00 2001 From: "Arthur K." Date: Wed, 21 May 2025 16:36:31 +0300 Subject: [PATCH 5/8] chore: remove aiogram as a dependency --- main.py | 90 +++++++++++++++++++++++++++++++++++------------- requirements.txt | 22 ++---------- 2 files changed, 69 insertions(+), 43 deletions(-) diff --git a/main.py b/main.py index 9361307..95e021f 100644 --- a/main.py +++ b/main.py @@ -1,15 +1,12 @@ -import asyncio import datetime as dt import locale import logging import os import re +import time import urllib.parse import requests -from aiogram import Bot -from aiogram.client.default import DefaultBotProperties -from aiogram.types import LinkPreviewOptions # Modify the links and data below: DEADLINES_URL = "https://m3104.nawinds.dev/DEADLINES.json" @@ -18,6 +15,7 @@ BOT_NAME = "Дединсайдер M3104" BOT_USERNAME = "m3104_deadliner_bot" # Environment variables that should be available: +API_URL = 'https://api.telegram.org/bot' TOKEN = os.getenv("TOKEN") MAIN_GROUP_ID = int(os.getenv("MAIN_GROUP_ID") or '0') EDIT_MESSAGE_ID = int(os.getenv("EDIT_MESSAGE_ID") or '0') @@ -28,21 +26,66 @@ assert MAIN_GROUP_ID, "Missing group ID!" logging.basicConfig(level=logging.INFO) -bot = Bot(TOKEN, default=DefaultBotProperties(parse_mode='HTML', link_preview=LinkPreviewOptions(is_disabled=True))) - NUMBER_EMOJIS = ['0.', '1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟'] + +class TelegramException(Exception): + def __init__(self, *, error_code: int, description: str, **_): + super().__init__(f'Error {error_code}: {description}') + self.error_code = error_code + self.description = description + + +def telegram_request(method: str, args: dict): + data = requests.post(API_URL + f'{TOKEN}/{method}', json=args).json() + if not data['ok']: + raise TelegramException(**data) + return data + + +def send_message(text: str) -> int: + return telegram_request('sendMessage', { + 'chat_id': MAIN_GROUP_ID, + 'parse_mode': 'HTML', + 'text': text, + 'link_preview_options': { + 'is_disabled': True + } + })['result']['message_id'] + + +def edit_message(message_id: int, text: str) -> int: + return telegram_request('editMessageText', { + 'chat_id': MAIN_GROUP_ID, + 'parse_mode': 'HTML', + 'message_id': message_id, + 'text': text, + 'link_preview_options': { + 'is_disabled': True + } + })['result']['message_id'] + + +def delete_message(message_id: int) -> bool: + return telegram_request('deleteMessage', { + 'chat_id': MAIN_GROUP_ID, + 'message_id': message_id + })['result'] + + def get_current_time() -> str: current_time = dt.datetime.now() current_time_hour = current_time.hour if current_time.hour >= 10 else "0" + str(current_time.hour) current_time_minute = current_time.minute if current_time.minute >= 10 else "0" + str(current_time.minute) return f"{current_time_hour}:{current_time_minute}" + def get_dt_obj_from_string(time: str) -> dt.datetime: time = time.replace('GMT+3', '+0300') locale.setlocale(locale.LC_TIME, 'en_US.UTF-8') return dt.datetime.strptime(time, "%d %b %Y %H:%M:%S %z") + def generate_link(event_name: str, event_time: str) -> str: dt_obj = get_dt_obj_from_string(event_time) formatted_time = dt_obj.strftime("%Y%m%d T%H%M%S%z") @@ -53,6 +96,7 @@ def generate_link(event_name: str, event_time: str) -> str: f"color=6" return link + def get_human_timedelta(time: str) -> str: dt_obj = get_dt_obj_from_string(time) dt_now = dt.datetime.now(dt_obj.tzinfo) # Ensure timezones are consistent @@ -72,28 +116,33 @@ def get_human_timedelta(time: str) -> str: else: return f"{hours}ч {minutes}м" + def get_human_time(time: str) -> str: dt_obj = get_dt_obj_from_string(time) locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8') formatted_date = dt_obj.strftime("%a, %d %B в %H:%M") return formatted_date + def timestamp_func(a: dict) -> float: time = a["time"].replace('GMT+3', '+0300') locale.setlocale(locale.LC_TIME, 'en_US.UTF-8') a_timestamp = dt.datetime.strptime(time, "%d %b %Y %H:%M:%S %z").timestamp() return a_timestamp # 29 Oct 2024 23:59:59 GMT+3 + def relevant_filter_func(d: dict) -> bool: dt_obj = get_dt_obj_from_string(d["time"]) return not dt_obj < dt.datetime.now(dt_obj.tzinfo) + def deadline_type_filter_func(d: dict, dtype: str = '') -> bool: if not dtype: return not re.match(r'^\[.*\]', d['name']) return f"[{dtype.lower()}]" in d["name"].lower() + def get_message_text() -> str: try: response = requests.get(DEADLINES_URL).json() @@ -162,43 +211,36 @@ def get_message_text() -> str: return text -async def send_deadlines(chat_id: int) -> None: + +def main() -> None: text = get_message_text() if EDIT_MESSAGE_ID: - msg = await bot.edit_message_text(text, chat_id=chat_id, message_id=EDIT_MESSAGE_ID) - assert not isinstance(msg, bool), 'EDIT_MESSAGE_ID is not a message!' + msg_id = edit_message(EDIT_MESSAGE_ID, text) else: - msg = await bot.send_message(chat_id, text, parse_mode="HTML", disable_web_page_preview=True) + msg_id = send_message(text) started_updating = dt.datetime.now() - print(dt.datetime.now(), "Message sent. Msg id:", msg.message_id) + print(dt.datetime.now(), "Message sent. Msg id:", msg_id) condition = (lambda: True) if EDIT_MESSAGE_ID else (lambda: dt.datetime.now() - started_updating < dt.timedelta(days=1)) while condition(): - await asyncio.sleep(60) + time.sleep(60) try: new_text = get_message_text() if text != new_text and new_text != "": - await msg.edit_text(new_text, parse_mode="HTML", disable_web_page_preview=True) + edit_message(msg_id, new_text) text = new_text - print(dt.datetime.now(), "Message updated. Msg id:", msg.message_id) + print(dt.datetime.now(), "Message updated. Msg id:", msg_id) else: - print(dt.datetime.now(), "Message update skipped. Msg id:", msg.message_id) + print(dt.datetime.now(), "Message update skipped. Msg id:", msg_id) except Exception as e: logging.warning(dt.datetime.now(),f"{dt.datetime.now()} Error updating message: {e}") continue if not EDIT_MESSAGE_ID: - await msg.delete() - - -async def main(): - try: - await send_deadlines(MAIN_GROUP_ID) - finally: - await bot.session.close() + delete_message(msg_id) if __name__ == '__main__': - asyncio.run(main()) + main() diff --git a/requirements.txt b/requirements.txt index 1d84bca..13b630a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,21 +1,5 @@ -aiofiles==24.1.0 -aiogram==3.13.1 -aiohappyeyeballs==2.4.3 -aiohttp==3.10.10 -aiosignal==1.3.1 -annotated-types==0.7.0 -async-timeout==4.0.3 -attrs==24.2.0 -certifi==2024.8.30 -charset-normalizer==3.4.0 -frozenlist==1.4.1 +certifi==2025.4.26 +charset-normalizer==3.4.2 idna==3.10 -magic-filter==1.0.12 -multidict==6.1.0 -propcache==0.2.0 -pydantic==2.9.2 -pydantic_core==2.23.4 requests==2.32.3 -typing_extensions==4.12.2 -urllib3==2.2.3 -yarl==1.15.5 +urllib3==2.4.0 From 3190f84591320d79d37a4ad163d2b3c2eda82a7e Mon Sep 17 00:00:00 2001 From: "Arthur K." Date: Tue, 27 May 2025 15:29:13 +0300 Subject: [PATCH 6/8] feat: add exams --- main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 95e021f..6491f6e 100644 --- a/main.py +++ b/main.py @@ -155,7 +155,9 @@ def get_message_text() -> str: ('', ''), # deadlines ('🧑‍💻 Тесты', 'тест'), ('🎓 Лекции', 'лекция'), - ('🛡 Защиты', 'защита') + ('🛡 Защиты', 'защита'), + ('🤓 Экзамены', 'экзамен'), + ('👞 Консультации', 'консультация'), ] assignments = [(sorted(filter(lambda t: deadline_type_filter_func(t, x[1]) and relevant_filter_func(t), all_deadlines), From d11681710ba30cb3bf887973267fe36d84762f56 Mon Sep 17 00:00:00 2001 From: Nikita Aksenov Date: Tue, 27 May 2025 17:14:17 +0300 Subject: [PATCH 7/8] ENTITIES_TOO_LONG fixed --- main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main.py b/main.py index 6491f6e..c14ca98 100644 --- a/main.py +++ b/main.py @@ -92,8 +92,7 @@ def generate_link(event_name: str, event_time: str) -> str: description = f"Дедлайн добавлен ботом {BOT_NAME} (https://t.me/{BOT_USERNAME})" link = f"https://calendar.google.com/calendar/u/0/r/eventedit?" \ f"text={urllib.parse.quote(event_name)}&" \ - f"dates={formatted_time}/{formatted_time}&details={urllib.parse.quote(description)}&" \ - f"color=6" + f"dates={formatted_time}/{formatted_time}" return link From f330e3dff3134380d45a32f4974c15a430d5ff2a Mon Sep 17 00:00:00 2001 From: Nikita Aksenov Date: Tue, 27 May 2025 17:21:03 +0300 Subject: [PATCH 8/8] =?UTF-8?q?=D0=B7=D0=B0=D1=89=D0=B8=D1=82=D1=8B=20?= =?UTF-8?q?=D0=B8=20=D0=BB=D0=B5=D0=BA=D1=86=D0=B8=D0=B8=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=8F=D0=BB=20=D0=BC=D0=B5=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index c14ca98..c82caff 100644 --- a/main.py +++ b/main.py @@ -153,8 +153,8 @@ def get_message_text() -> str: types = [ ('', ''), # deadlines ('🧑‍💻 Тесты', 'тест'), - ('🎓 Лекции', 'лекция'), ('🛡 Защиты', 'защита'), + ('🎓 Лекции', 'лекция'), ('🤓 Экзамены', 'экзамен'), ('👞 Консультации', 'консультация'), ]