From b8894e7801f1a3f8a7006be095d4d766411fbc71 Mon Sep 17 00:00:00 2001 From: Nikita Aksenov Date: Wed, 19 Mar 2025 00:04:12 +0300 Subject: [PATCH] Bugfixes & refactoring --- main.py | 110 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/main.py b/main.py index 755fef2..92c0849 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,7 @@ import datetime as dt import locale from time import sleep import urllib.parse +import aiohttp # Modify the links and data below: DEADLINES_URL = "https://m3104.nawinds.dev/DEADLINES.json" @@ -19,27 +20,25 @@ TOKEN = os.getenv("TOKEN") MAIN_GROUP_ID = int(os.getenv("MAIN_GROUP_ID")) logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + bot = Bot(TOKEN) NUMBER_EMOJIS = ['0.', '1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟'] - -def get_current_time() -> str: +async 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) + 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: +async 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) +async def generate_link(event_name: str, event_time: str) -> str: + dt_obj = await 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})" link = f"https://calendar.google.com/calendar/u/0/r/eventedit?" \ @@ -48,9 +47,8 @@ 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) +async def get_human_timedelta(time: str) -> str: + dt_obj = await get_dt_obj_from_string(time) dt_now = dt.datetime.now(dt_obj.tzinfo) # Ensure timezones are consistent delta = dt_obj - dt_now @@ -68,47 +66,43 @@ 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) +async def get_human_time(time: str) -> str: + dt_obj = await 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: +async 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) -> float: - dt_obj = get_dt_obj_from_string(d["time"]) +async def relevant_filter_func(d: dict) -> bool: + dt_obj = await get_dt_obj_from_string(d["time"]) if dt_obj < dt.datetime.now(dt_obj.tzinfo): return False return True - -def deadlines_filter_func(d: dict) -> float: +async def deadlines_filter_func(d: dict) -> bool: if "[тест]" in d["name"].lower(): return False return True - -def get_message_text() -> str: +async def get_message_text() -> str: try: response = requests.get(DEADLINES_URL).json() - except Exception: + except Exception as e: + logger.error(f"Failed to fetch deadlines: {e}") return "" deadlines = response["deadlines"] - tests = list(filter(lambda t: not deadlines_filter_func(t) and relevant_filter_func(t), deadlines)) - deadlines = list(filter(lambda d: deadlines_filter_func(d) and relevant_filter_func(d), deadlines)) + tests = list(filter(lambda t: not await deadlines_filter_func(t) and await relevant_filter_func(t), deadlines)) + deadlines = list(filter(lambda d: await deadlines_filter_func(d) and await relevant_filter_func(d), deadlines)) - text = f"🔥️️ Дедлайны (Обновлено в {get_current_time()} 🔄):\n\n" - tests = sorted(tests, key=timestamp_func) - deadlines = sorted(deadlines, key=timestamp_func) + text = f"🔥️️ Дедлайны (Обновлено в {await get_current_time()} 🔄):\n\n" + tests = sorted(tests, key=lambda x: await timestamp_func(x)) + deadlines = sorted(deadlines, key=lambda x: await timestamp_func(x)) if len(deadlines) == 0: text += "Дедлайнов нет)\n\n" @@ -127,9 +121,9 @@ def get_message_text() -> str: text += deadlines[i]["name"] text += " — " - text += get_human_timedelta(deadlines[i]["time"]) - text += f"\n(" - text += get_human_time(deadlines[i]["time"]) + ")\n\n" + text += await get_human_timedelta(deadlines[i]["time"]) + text += f"\n(" + text += await get_human_time(deadlines[i]["time"]) + ")\n\n" if len(tests) > 0: text += f"\n🧑‍💻 Тесты:\n\n" @@ -143,33 +137,43 @@ def get_message_text() -> str: no += ". " text += str(no) + "" + test_name text += " — " - text += get_human_timedelta(tests[i]["time"]) - text += f"\n(" - text += get_human_time(tests[i]["time"]) + ")\n\n" + text += await get_human_timedelta(tests[i]["time"]) + text += f"\n(" + text += await get_human_time(tests[i]["time"]) + ")\n\n" text += f"\n🆕 " \ f"Добавить дедлайн/тест" return text - async def send_deadlines(chat_id: int) -> None: - text = get_message_text() - msg = await bot.send_message(chat_id, text, parse_mode="HTML", disable_web_page_preview=True) - started_updating = dt.datetime.now() - while dt.datetime.now() - started_updating < dt.timedelta(days=1): - sleep(60) - 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 - await msg.delete() - + retries = 3 + for attempt in range(retries): + try: + text = await get_message_text() + msg = await bot.send_message(chat_id, text, parse_mode="HTML", disable_web_page_preview=True) + started_updating = dt.datetime.now() + while dt.datetime.now() - started_updating < dt.timedelta(days=1): + await asyncio.sleep(60) + new_text = await 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 + await msg.delete() + break + except aiohttp.ClientConnectorError as e: + logger.error(f"Attempt {attempt + 1} failed: {e}") + if attempt < retries - 1: + await asyncio.sleep(2 ** attempt) # Exponential backoff + else: + raise async def main(): - await send_deadlines(MAIN_GROUP_ID) - - await bot.session.close() - + try: + await send_deadlines(MAIN_GROUP_ID) + except Exception as e: + logger.error(f"An error occurred: {e}") + finally: + await bot.session.close() if __name__ == '__main__': - asyncio.run(main()) + asyncio.run(main()) \ No newline at end of file