diff --git a/bot.py b/bot.py index 6c3dc20..4cffa1a 100644 --- a/bot.py +++ b/bot.py @@ -13,12 +13,13 @@ from threading import Thread import telebot import config_reader as cr import db_classes +import timeout as tmo # TODO more backends (redis at least) db = db_classes.PickleDB(".db") db.load() -CURRENT_VERSION = "v1.0rc8" +CURRENT_VERSION = "v1.0rc9" VERSION = db.read("about.version", CURRENT_VERSION) db.write("about.author", "electromagneticcyclone") db.write("about.tester", "angelbeautifull") @@ -38,7 +39,7 @@ bot = telebot.TeleBot(cr.read(f"tokens.{MODE}"), parse_mode="MarkdownV2") def get_time(forum: int) -> dt.datetime: "Get datetime.now in forum's timezone. Default timezone is UTC+3." return dt.datetime.now(dt.UTC) + dt.timedelta( - hours=db.read(str(forum) + ".settings.timezone", 3) + hours=db.read(f"{forum}.settings.timezone", 3) ) @@ -46,9 +47,9 @@ def change_phase(forum: int, date: dt.datetime = None) -> None: "Changes forum's current phase." if date is None: date = get_time(forum).date() - phase = db.read(str(forum) + ".schedule.phase") - phases = db.read(str(forum) + ".schedule.phases") - last_changed = db.read(str(forum) + ".schedule.last_phase_changing_date") + phase = db.read(f"{forum}.schedule.phase") + phases = db.read(f"{forum}.schedule.phases") + last_changed = db.read(f"{forum}.schedule.last_phase_changing_date") if None in (phase, last_changed, phases): return now = date @@ -56,8 +57,8 @@ def change_phase(forum: int, date: dt.datetime = None) -> None: if not (now > last_changed and weekday == 0): return phase = (phase + 1) % phases - db.write(str(forum) + ".schedule.phase", phase) - db.write(str(forum) + ".schedule.last_phase_changing_date", now) + db.write(f"{forum}.schedule.phase", phase) + db.write(f"{forum}.schedule.last_phase_changing_date", now) def get_status(forum: int, now: dt.datetime = None) -> bool: @@ -67,11 +68,11 @@ def get_status(forum: int, now: dt.datetime = None) -> bool: weekday = now.weekday() if weekday == 6: return False - phase = db.read(str(forum) + ".schedule.phase", 0) - work_days = db.read(str(forum) + ".schedule.work_days", []) - skip_days = db.read(str(forum) + ".schedule.skip_days", []) - days = db.read(str(forum) + ".schedule.days", ([0] * 6, [0] * 6)) - is_active = db.read(str(forum) + ".is_active", False) + phase = db.read(f"{forum}.schedule.phase", 0) + work_days = db.read(f"{forum}.schedule.work_days", []) + skip_days = db.read(f"{forum}.schedule.skip_days", []) + days = db.read(f"{forum}.schedule.days", ([0] * 6, [0] * 6)) + is_active = db.read(f"{forum}.is_active", False) if is_active: if now in work_days: return True @@ -94,7 +95,7 @@ def get_chat( forum = message.chat.id if (db.read(str(forum)) is None or not is_forum) and not start: return None - chat = db.read(str(forum) + ".settings.chat") + chat = db.read(f"{forum}.settings.chat") if isinstance(message, int): return chat if (chat is None) or (chat.id == message.reply_to_message.id): @@ -105,7 +106,7 @@ def get_chat( def check_if_admin(message: telebot.types.Message) -> bool | None: "Checks if the message is sent by the forum's admin." forum = message.chat.id - admin = db.read(str(forum) + ".settings.admin") + admin = db.read(f"{forum}.settings.admin") if admin is None: return True if admin["id"] is None: @@ -116,29 +117,24 @@ def check_if_admin(message: telebot.types.Message) -> bool | None: def mention(forum: int, uid: int) -> str | None: "Returns markdown formatted string with user's mention." uid = str(uid) - if db.read(str(forum) + ".people." + uid) is None: - stderr.write("Пользователя с ID " + uid + " нет в базе.\n") + if db.read(f"{forum}.people.{uid}") is None: + stderr.write(f"Пользователя с ID {uid} нет в базе.\n") return None return ( - "[" - + db.read(str(forum) + ".people." + uid + ".name") - + " " - + db.read(str(forum) + ".people." + uid + ".surname") - + "](tg://user?id=" - + str(uid) - + ")" + f"[{db.read(f"{forum}.people.{uid}.name")} " + + f"{db.read(f"{forum}.people.{uid}.surname")}](tg://user?id={uid})" ) def find_uids(forum: int, s: str) -> list | None: "Find user's id by nickname, name or surname." - people = db.read(str(forum) + ".people") + people = db.read(f"{forum}.people") if people is None: return None if len(s) > 0: if s[0] == "@": s = s[1:] - f = [i for i in people.keys() if len([j for j in people[i].values() if s in j]) > 0] + f = list(filter(lambda x: s in people[x].values(), people.keys())) else: f = list(people.keys()) if len(f) == 0: @@ -149,47 +145,48 @@ def find_uids(forum: int, s: str) -> list | None: def format_user_info(forum: int, uid: int) -> str: "Returns markdown formatted string with all user's info by their id." uid = str(uid) - person = db.read(str(forum) + ".people." + uid) + person = db.read(f"{forum}.people.{uid}") if person is None: return "" r = "" - r += "\\#" + uid + "\n" + r += f"\\#{uid}\n" for k, i in person.items(): - r += "‖" + " " + k + " \\= " + telebot.formatting.escape_markdown(i) + "\n" + r += f"‖ {k} \\= {telebot.formatting.escape_markdown(i)}\n" return r def prepend_user(forum: int, ulist_s: str, uid: int) -> None: "Inserts user id at the start of provided db list in forum's context." uid = str(uid) - ulist = db.read(str(forum) + "." + ulist_s, []) + ulist = db.read(f"{forum}.{ulist_s}", []) ulist = list(set([uid] + ulist)) - db.write(str(forum) + "." + ulist_s, ulist) + db.write(f"{forum}.{ulist_s}", ulist) def append_user(forum: int, ulist_s: str, uid: int) -> None: "Inserts user id at the end of provided db list in forum's context." uid = str(uid) - ulist = db.read(str(forum) + "." + ulist_s, []) + ulist = db.read(f"{forum}.{ulist_s}", []) ulist = list(set(ulist + [uid])) - db.write(str(forum) + "." + ulist_s, ulist) + db.write(f"{forum}.{ulist_s}", ulist) def pop_user(forum: int, ulist_s: str) -> dict | None: "Removes user id from the start of provided db list in forum's context. Returns user id." - ulist = db.read(str(forum) + "." + ulist_s, []) + ulist = db.read(f"{forum}.{ulist_s}", []) r = None if len(ulist) > 0: r = ulist.pop(0) - db.write(str(forum) + "." + ulist_s, ulist) + db.write(f"{forum}.{ulist_s}", ulist) return r def insert_user_in_current_order(forum: int, uid: int) -> bool: + "Inserts user id into current order list." uid = str(uid) - order = db.read(str(forum) + ".rookies.order", []) - people = db.read(str(forum) + ".people", {}) - current = db.read(str(forum) + ".rookies.current") + order = db.read(f"{forum}.rookies.order", []) + people = db.read(f"{forum}.people", {}) + current = db.read(f"{forum}.rookies.current") if uid not in people: return False order = dict(map(lambda x: (x, people[x]), order)) @@ -209,30 +206,28 @@ def insert_user_in_current_order(forum: int, uid: int) -> bool: pos = list(order.keys()).index(current) if pos == 0: return False - db.write(str(forum) + ".rookies.order", list(order.keys())[1:]) + db.write(f"{forum}.rookies.order", list(order.keys())[1:]) return True def parse_dates(forum: int, args: typing.Iterable) -> list | str: + """ + Translates strings into dates in forum's context. + Returns problematic string if it couldn't be parsed. + """ dates = [] cur_date = get_time(forum).date() - dt.timedelta(days=1) cur_year = cur_date.year for a in args: - if a.lower() == "сегодня": - dates.append(get_time(forum).date()) - continue - if a.lower() == "завтра": - dates.append(get_time(forum).date() + dt.timedelta(days=1)) - continue - if a.lower() == "послезавтра": - dates.append(get_time(forum).date() + dt.timedelta(days=2)) - continue - if a.lower() == "вчера": - dates.append(get_time(forum).date() - dt.timedelta(days=1)) - continue - if a.lower() == "позавчера": - dates.append(get_time(forum).date() - dt.timedelta(days=2)) - continue + human_relative = { + "сегодня": get_time(forum).date(), + "завтра": get_time(forum).date() + dt.timedelta(days=1), + "послезавтра": get_time(forum).date() + dt.timedelta(days=2), + "вчера": get_time(forum).date() - dt.timedelta(days=1), + "позавчера": get_time(forum).date() - dt.timedelta(days=2), + }.get(a.lower()) + if human_relative is not None: + dates.append(human_relative) d = a.split(".") a_dates = [] if len(d) in (2, 3): @@ -264,6 +259,7 @@ def parse_dates(forum: int, args: typing.Iterable) -> list | str: def mod_days(message: telebot.types.Message, target: str, neighbour: str) -> None: + "Helper function to add skip and work days." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -293,7 +289,7 @@ def mod_days(message: telebot.types.Message, target: str, neighbour: str) -> Non n = [] db.write(neighbour, list(filter(lambda x: x not in dates, n))) db.write(target, list(sorted(set(t + dates)))) - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, @@ -306,35 +302,7 @@ def mod_days(message: telebot.types.Message, target: str, neighbour: str) -> Non ) -class Timeout: - __timeout = None - __caution = True - - def __init__(self, period: int): - self.period = period - - @property - def period(self) -> int: - return self.__period - - @period.setter - def period(self, period: int): - self.__period = period - - def check(self, once_func: typing.Callable, always_func: typing.Callable) -> bool: - if self.__timeout is not None: - if time.time() - self.__timeout < self.period: - if self.__caution: - once_func() - self.__caution = False - always_func() - return True - self.__caution = True - self.__timeout = time.time() - return False - - -tmo = Timeout(600) +antispam_tmo = tmo.Timeout(10 * 60) def antispam( @@ -343,8 +311,8 @@ def antispam( "Removes frequent non admin's commands." if check_if_admin(message): return False - tmo.period = db.read(str(forum) + ".settings.antispam.period", 600) - return tmo.check( + antispam_tmo.period = db.read(f"{forum}.settings.antispam.period", 600) + return antispam_tmo.check( lambda: bot.reply_to(chat, "*Хватит спамить\\!\\!\\!*"), lambda: bot.delete_message(forum, message.id), ) @@ -352,6 +320,7 @@ def antispam( @bot.message_handler(commands=["start"]) def start_bot(message: telebot.types.Message): + "Command to print kickstart info." forum = message.chat.id chat = get_chat(message, True) if chat is not None: @@ -360,26 +329,27 @@ def start_bot(message: telebot.types.Message): if message.chat.is_forum: bot.reply_to( chat, - "Привет\\! Я бот для управления дежурствами и напоминания о них\\. " - + "Напиши /link, чтобы привязать комнату\\.", + "Привет\\! Я бот для управления дежурствами и напоминания о них\\." + + " Напиши /link, чтобы привязать комнату\\.", ) else: bot.reply_to( chat, - "Я работаю только на форумах \\(супергруппах с комнатами\\)\\. " - + "Пригласи меня в один из них и напиши /start", + "Я работаю только на форумах \\(супергруппах с комнатами\\)\\." + + " Пригласи меня в один из них и напиши /start", ) @bot.message_handler(commands=["help"]) def get_help(message: telebot.types.Message): + "Command to print info about all of the available commands." forum = message.chat.id chat = get_chat(message) if chat is not None: if antispam(message, chat, forum): return if check_if_admin(message): - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, @@ -396,9 +366,12 @@ def get_help(message: telebot.types.Message): + "/list \\[@/\\#/Имя\\] — получить информацию по имени/фамиили\n" + "/purge — удалить форум из базы *\\!\\!\\!ОПАСНО\\!\\!\\!*\n" + "/timezone ±n — установить часовой пояс по UTC\n" - + "/days \\*\\*\\*\\*\\*\\* \\*\\*\\*\\*\\*\\* \\(\\* \\= \\[\\0/1\\]\\) — задать дни недели \\(ПН\\-СБ\\), когда необходимо дежурство, отдельно для числителей и знаменателей\n" + + "/days \\*\\*\\*\\*\\*\\* \\*\\*\\*\\*\\*\\*" + + " \\(\\* \\= \\[\\0/1\\]\\) — задать дни недели \\(ПН\\-СБ\\)," + + " когда необходимо дежурство, отдельно для числителей и знаменателей\n" + "/calendar \\[Числитель/знаменатель\\] — показать календарь дежурств\n" - + "/phase \\[Числитель/знаменатель\\] — узнать или скорректировать фазу текущей недели\n" + + "/phase \\[Числитель/знаменатель\\] — узнать или скорректировать" + + " фазу текущей недели\n" + "/skip \\[00\\.00\\] — пропустить сегодняшний или заданный день\n" + "/work \\[00\\.00\\] — поработать в сегодняшнем или заданном дне\n" + "/honor \\[\\-\\]@ — пропуск следующего дежурства, так как студент молодец\n" @@ -413,15 +386,17 @@ def get_help(message: telebot.types.Message): if __debug__: def pretty(d, indent=0): + "Print pretty dict." for key, value in d.items(): - print(" " * indent + str(key)) + stderr.write(" " * indent + f"{key}\n") if isinstance(value, dict): pretty(value, indent + 1) else: - print(" " * (indent + 1) + str(value)) + stderr.write(" " * (indent + 1) + f"{value}\n") @bot.message_handler(commands=["info"]) def info(message: telebot.types.Message): + "Command to print db." forum = message.chat.id chat = get_chat(message, True) if chat is not None: @@ -431,6 +406,7 @@ if __debug__: @bot.message_handler(commands=["exec"]) def exec_bot(message: telebot.types.Message): + "Command to eval python code." forum = message.chat.id chat = get_chat(message, True) if chat is not None: @@ -439,7 +415,7 @@ if __debug__: result = ast.literal_eval(" ".join(message.text.split(" ")[1:])) # Disabling W0718 because everything can be excepted with eval except Exception as e: # pylint: disable=broad-exception-caught - bot.reply_to(chat, "Ошибка выполнения: " + str(e)) + bot.reply_to(chat, f"Ошибка выполнения: {e}") return bot.reply_to( chat, @@ -451,6 +427,7 @@ if __debug__: @bot.message_handler(commands=["backup"]) def backup_db(message: telebot.types.Message): + "Command to backup database." forum = message.chat.id chat = get_chat(message, True) if chat is not None: @@ -459,11 +436,12 @@ def backup_db(message: telebot.types.Message): args = message.text.split()[1:] if len(args) == 0: args.append("") - db.save(args[0] + ".backup.db") + db.save(f"{args[0]}.backup.db") @bot.message_handler(commands=["restore"]) def restore_db(message: telebot.types.Message): + "Command to restore from backup" forum = message.chat.id chat = get_chat(message, True) if chat is not None: @@ -472,31 +450,33 @@ def restore_db(message: telebot.types.Message): args = message.text.split()[1:] if len(args) == 0: args.append("") - db.load(args[0] + ".backup.db") + db.load(f"{args[0]}.backup.db") @bot.message_handler(commands=["link"]) def link(message: telebot.types.Message): + "Command to link forum's room." forum = message.chat.id chat = get_chat(message, True) if chat is not None: if antispam(message, chat, forum): return if check_if_admin(message): - db.write(str(forum) + ".settings.chat", message.reply_to_message) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.settings.chat", message.reply_to_message) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( message.reply_to_message, 'Комната "' - + str(message.reply_to_message.forum_topic_created.name) + + f"{message.reply_to_message.forum_topic_created.name}" + '" привязана\\. ' + "Чек-лист того, что нужно написать в первую очередь:\n" + "/admin \\[@\\], чтобы задать админа\n" + "/help, чтобы узнать, что я умею\n" + "/tz, чтобы задать часовой пояс\n" + "/phase \\[Числитель/знаменатель\\], чтобы задать недельную фазу\n" - + "/days \\*\\*\\*\\*\\*\\* \\*\\*\\*\\*\\*\\* \\(\\* \\= \\[\\0/1\\]\\), чтобы задать дни дежурств\n" + + "/days \\*\\*\\*\\*\\*\\* \\*\\*\\*\\*\\*\\* \\(\\* \\= \\[\\0/1\\]\\)," + + " чтобы задать дни дежурств\n" + "/new @ Имя Фамилия, чтобы добавить ~салаг~ студетов\n" + "/begin \\[@\\], чтобы начать дежурство", ) @@ -504,20 +484,22 @@ def link(message: telebot.types.Message): @bot.message_handler(commands=["unlink"]) def unlink(message: telebot.types.Message): + "Command to unlink room." forum = message.chat.id chat = get_chat(message) if chat is not None: if antispam(message, chat, forum): return if check_if_admin(message): - db.write(str(forum) + ".settings.chat", None) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.settings.chat", None) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to(chat, "Комната отвязана") @bot.message_handler(commands=["purge"]) def purge_db_people(message: telebot.types.Message): + "Command to clear info about people from database." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -526,11 +508,11 @@ def purge_db_people(message: telebot.types.Message): if check_if_admin(message): if message.text == "/purge ДА УДАЛЯЙ ДАВАЙ": db.write(str(forum), None) - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to(chat, "База студентов удалена") else: - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, @@ -541,15 +523,16 @@ def purge_db_people(message: telebot.types.Message): @bot.message_handler(commands=["forget"]) def forget_db_pending(message: telebot.types.Message): + "Command to forget unregistered newbies." forum = message.chat.id chat = get_chat(message) if chat is not None: if antispam(message, chat, forum): return if check_if_admin(message): - empty = db.read(str(forum) + ".pending") is None - db.write(str(forum) + ".pending", None) - if db.read(str(forum) + ".settings.delete_messages"): + empty = db.read(f"{forum}.pending") is None + db.write(f"{forum}.pending", None) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, @@ -560,6 +543,7 @@ def forget_db_pending(message: telebot.types.Message): @bot.message_handler(commands=["admin"]) def set_admin(message: telebot.types.Message): + "Command to register admin." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -568,28 +552,26 @@ def set_admin(message: telebot.types.Message): if check_if_admin(message): admin = [i for i in message.text.split() if i[0] == "@"] if len(admin) == 0: - uid = db.read(str(forum) + ".settings.admin.id") + uid = db.read(f"{forum}.settings.admin.id") if uid == message.from_user.id: bot.reply_to(chat, "Ты уже тут главный") return - db.write(str(forum) + ".settings.admin.id", message.from_user.id) - db.write( - str(forum) + ".settings.admin.username", message.from_user.username - ) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.settings.admin.id", message.from_user.id) + db.write(f"{forum}.settings.admin.username", message.from_user.username) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( - chat, "Рад познакомиться, " + message.from_user.first_name + "\\!" + chat, f"Рад познакомиться, {message.from_user.first_name}\\!" ) else: admin = admin[0][1:] - uadmin = db.read(str(forum) + ".settings.admin.username") + uadmin = db.read(f"{forum}.settings.admin.username") if uadmin == admin: bot.reply_to(chat, "Ты уже тут главный") return - db.write(str(forum) + ".settings.admin.id", None) - db.write(str(forum) + ".settings.admin.username", admin) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.settings.admin.id", None) + db.write(f"{forum}.settings.admin.username", admin) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, @@ -602,6 +584,7 @@ def set_admin(message: telebot.types.Message): @bot.message_handler(commands=["list"]) def list_users(message: telebot.types.Message): + "Command to list users." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -619,7 +602,7 @@ def list_users(message: telebot.types.Message): if len(r) == 0: bot.reply_to(chat, "Никого не нашёл") return - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) res = [] for i in r: @@ -628,45 +611,40 @@ def list_users(message: telebot.types.Message): @bot.message_handler(commands=["remind"]) -def remind_users(message: telebot.types.Message): - auto = False - if isinstance(message, int): - auto = True - forum = message - else: - forum = message.chat.id +def remind_users(message: telebot.types.Message | int): + "Command to remind about duty or about registration. Can be called automatically." + auto, forum = ( + (True, message) if isinstance(message, int) else (False, message.chat.id) + ) chat = get_chat(message) if chat is not None: if not auto and antispam(message, chat, forum): return if auto or check_if_admin(message): - pending = db.read(str(forum) + ".pending", {}) + pending = db.read(f"{forum}.pending", {}) r = "" - if len(pending.keys()) == 0: - if not auto and db.read(str(forum) + ".settings.delete_messages"): - bot.delete_message(forum, message.id) - else: + if len(pending.keys()) != 0: for i in pending.keys(): - r += "@" + telebot.formatting.escape_markdown(i) + " " - r += "нужно нажать /new\n" + r += f"@{telebot.formatting.escape_markdown(i)}\n" + r += "нужно нажать /new\n\n" status = get_status(forum) if status: - current = db.read(str(forum) + ".rookies.current") + current = db.read(f"{forum}.rookies.current") if current is not None: - rookie = db.read(str(forum) + ".people." + current) + rookie = db.read(f"{forum}.people.{current}") if rookie is not None: - r += mention(forum, current) + " сегодня дежурит" - if not auto and db.read(str(forum) + ".settings.delete_messages"): + r += f"{mention(forum, current)} сегодня дежурит" + if not auto and db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) - if r == "": - if not auto: - bot.reply_to(chat, "Дежурств сегодня нет") + if not auto and r == "": + bot.reply_to(chat, "Дежурств сегодня нет") else: bot.reply_to(chat, r) @bot.message_handler(commands=["del"]) def del_person(message: telebot.types.Message): + "Command to remove person from forum." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -682,7 +660,7 @@ def del_person(message: telebot.types.Message): return user = args[0] if user[1:] == bot.get_me().username: - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to(chat, "Ты сейчас быканул\\?") return @@ -695,14 +673,15 @@ def del_person(message: telebot.types.Message): list_users(message) bot.reply_to(chat, "Конкретезируй плиз") return - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) - bot.reply_to(chat, "Скатертью дорога, " + mention(forum, f[0]) + "\\!") - db.pop(str(forum) + ".people." + f[0]) + bot.reply_to(chat, f"Скатертью дорога, {mention(forum, f[0])}\\!") + db.pop(f"{forum}.people.{f[0]}") @bot.message_handler(commands=["cleaner"]) def cleaner(message: telebot.types.Message): + "Command to set commands cleaner status." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -717,7 +696,7 @@ def cleaner(message: telebot.types.Message): bot.reply_to(chat, "Нужно указать `0` или `1`") return state = args[0] == "1" - db.write(str(forum) + ".settings.delete_messages", state) + db.write(f"{forum}.settings.delete_messages", state) if state: bot.delete_message(forum, message.id) bot.reply_to( @@ -734,12 +713,13 @@ def cleaner(message: telebot.types.Message): @bot.message_handler(commands=["new"]) def add_new(message: telebot.types.Message): + "Command to add new person to forum." forum = message.chat.id chat = get_chat(message) if chat is not None: args = message.text.split()[1:] if len(args) == 0: - pending = db.read(str(forum) + ".pending") + pending = db.read(f"{forum}.pending") if pending is None: antispam(message, chat, forum) return @@ -748,15 +728,13 @@ def add_new(message: telebot.types.Message): antispam(message, chat, forum) return uid = str(message.from_user.id) - db.write(str(forum) + ".people." + uid + ".username", user) - db.write(str(forum) + ".people." + uid + ".name", pending[user]["name"]) - db.write( - str(forum) + ".people." + uid + ".surname", pending[user]["surname"] - ) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.people.{uid}.username", user) + db.write(f"{forum}.people.{uid}.name", pending[user]["name"]) + db.write(f"{forum}.people.{uid}.surname", pending[user]["surname"]) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) - bot.reply_to(chat, "Рад познакомиться, " + mention(forum, uid) + "\\!") - db.pop(str(forum) + ".pending." + user) + bot.reply_to(chat, f"Рад познакомиться, {mention(forum, uid)}\\!") + db.pop(f"{forum}.pending.{user}") elif check_if_admin(message): if len(args) != 3: bot.reply_to( @@ -769,12 +747,12 @@ def add_new(message: telebot.types.Message): return user = user[1:] if user == bot.get_me().username: - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to(chat, "Я тебе не салага\\!") return - people = db.read(str(forum) + ".people", []) - pending = db.read(str(forum) + ".pending", {}) + people = db.read(f"{forum}.people", []) + pending = db.read(f"{forum}.pending", {}) if any( [people[i]["username"] == user for i in people] + [user in pending.keys()] @@ -783,25 +761,22 @@ def add_new(message: telebot.types.Message): return name = args[1] surname = args[2] - db.write(str(forum) + ".pending." + user + ".name", name) - db.write(str(forum) + ".pending." + user + ".surname", surname) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.pending.{user}.name", name) + db.write(f"{forum}.pending.{user}.surname", surname) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, "Добро пожаловать, @" + telebot.formatting.escape_markdown(user) - + " \\(" - + name - + " " - + surname - + "\\)\\!\n" + + f" \\({name} {surname}\\)\\!\n" + "Первое твоё обязательство — написать /new", ) @bot.message_handler(commands=["readd"]) def readd(message: telebot.types.Message): + "Command to change person's nickname." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -820,7 +795,7 @@ def readd(message: telebot.types.Message): chat, "ID — это число\nНа буквах пока считать не научились" ) return - if db.read(str(forum) + ".people." + uid) is None: + if db.read(f"{forum}.people.{uid}") is None: bot.reply_to(chat, "Такого пользователя нет в базе") return user = args[1] @@ -831,19 +806,19 @@ def readd(message: telebot.types.Message): if user == bot.get_me().username: bot.reply_to(chat, "Я тебе не салага\\!") return - db.write(str(forum) + ".people." + uid + ".username", user) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.people.{uid}.username", user) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, - "@" - + telebot.formatting.escape_markdown(user) + f"@{telebot.formatting.escape_markdown(user)}" + " от меня не скроется\\!", ) @bot.message_handler(commands=["days"]) def set_days(message: telebot.types.Message): + "Command to define duty days." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -862,17 +837,17 @@ def set_days(message: telebot.types.Message): if len(num2) != 6: bot.reply_to(chat, "Во втором числе не 6 цифр") return - if not all([i == "0" or i == "1" for i in num1]): + if not all(i in ("0", "1") for i in num1): bot.reply_to(chat, "Цифры двоичные должны быть в первом") return - if not all([i == "0" or i == "1" for i in num2]): + if not all(i in ("0", "1") for i in num2): bot.reply_to(chat, "Цифры двоичные должны быть во втором") return db.write( - str(forum) + ".schedule.days", + f"{forum}.schedule.days", ( - [True if num1[i] == "1" else False for i in range(6)], - [True if num2[i] == "1" else False for i in range(6)], + [num1[i] == "1" for i in range(6)], + [num2[i] == "1" for i in range(6)], ), ) message.text = "" @@ -881,6 +856,7 @@ def set_days(message: telebot.types.Message): @bot.message_handler(commands=["calendar"]) def calendar(message: telebot.types.Message): + "Command to display duty calendar." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -896,10 +872,10 @@ def calendar(message: telebot.types.Message): en2 = args[0].lower() in "знаменатель" if en1 == en2 == False: en1, en2 = True, True - days = db.read(str(forum) + ".schedule.days", ([False] * 6, [False] * 6)) - skip = db.read(str(forum) + ".schedule.skip_days", []) - work = db.read(str(forum) + ".schedule.work_days", []) - if db.read(str(forum) + ".settings.delete_messages"): + days = db.read(f"{forum}.schedule.days", ([False] * 6, [False] * 6)) + skip = db.read(f"{forum}.schedule.skip_days", []) + work = db.read(f"{forum}.schedule.work_days", []) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, @@ -933,43 +909,43 @@ def calendar(message: telebot.types.Message): @bot.message_handler(commands=["skip"]) def set_skip_days(message: telebot.types.Message): + "Command to set skipped days." forum = message.chat.id - mod_days( - message, str(forum) + ".schedule.skip_days", str(forum) + ".schedule.work_days" - ) + mod_days(message, f"{forum}.schedule.skip_days", f"{forum}.schedule.work_days") @bot.message_handler(commands=["work"]) def set_work_days(message: telebot.types.Message): + "Command to set work days." forum = message.chat.id - mod_days( - message, str(forum) + ".schedule.work_days", str(forum) + ".schedule.skip_days" - ) + mod_days(message, f"{forum}.schedule.work_days", f"{forum}.schedule.skip_days") @bot.message_handler(commands=["stop"]) def stop_queue(message: telebot.types.Message): + "Command to stop duty." forum = message.chat.id chat = get_chat(message) if chat is not None: if antispam(message, chat, forum): return if check_if_admin(message): - status = db.read(str(forum) + ".is_active") + status = db.read(f"{forum}.is_active") if status is None or not status: - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to(chat, "Держурство уже не идёт") return status = False - db.write(str(forum) + ".is_active", status) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.is_active", status) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to(chat, "Держурство остановлено") @bot.message_handler(commands=["honor"]) def add_honor(message: telebot.types.Message): + "Command to add honored person." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -994,29 +970,30 @@ def add_honor(message: telebot.types.Message): bot.reply_to(chat, "Конкретезируй плиз") return user = f[0] - force = db.read(str(forum) + ".rookies.force_order", []) - honor = db.read(str(forum) + ".rookies.honor_order", []) - people = db.read(str(forum) + ".people") + force = db.read(f"{forum}.rookies.force_order", []) + honor = db.read(f"{forum}.rookies.honor_order", []) + people = db.read(f"{forum}.people") honor.append(user) for i in people: if (i in force) and (i in honor): force.remove(i) honor.remove(i) - db.write(str(forum) + ".rookies.force_order", force) - db.write(str(forum) + ".rookies.honor_order", honor) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.rookies.force_order", force) + db.write(f"{forum}.rookies.honor_order", honor) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, "Вы в почёте, " + telebot.formatting.escape_markdown( - people[user]["name"] + " " + people[user]["surname"] - ), + f"{people[user]["name"]} {people[user]["surname"]}" + ) ) @bot.message_handler(commands=["force"]) def add_force(message: telebot.types.Message): + "Command to add guilty person." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -1041,29 +1018,30 @@ def add_force(message: telebot.types.Message): bot.reply_to(chat, "Конкретезируй плиз") return user = f[0] - force = db.read(str(forum) + ".rookies.force_order", []) - honor = db.read(str(forum) + ".rookies.honor_order", []) - people = db.read(str(forum) + ".people") + force = db.read(f"{forum}.rookies.force_order", []) + honor = db.read(f"{forum}.rookies.honor_order", []) + people = db.read(f"{forum}.people") force.append(user) for i in people: if (i in force) and (i in honor): force.remove(i) honor.remove(i) - db.write(str(forum) + ".rookies.force_order", force) - db.write(str(forum) + ".rookies.honor_order", honor) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.rookies.force_order", force) + db.write(f"{forum}.rookies.honor_order", honor) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, "Отрабатывай, " + telebot.formatting.escape_markdown( - people[user]["name"] + " " + people[user]["surname"] - ), + f"{people[user]["name"]} {people[user]["surname"]}" + ) ) @bot.message_handler(commands=["sick"]) def add_sick(message: telebot.types.Message): + "Command to add sick person." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -1076,7 +1054,7 @@ def add_sick(message: telebot.types.Message): return user = args[0] if user[1:] == bot.get_me().username: - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to(chat, "Ты сейчас быканул\\?") return @@ -1105,16 +1083,16 @@ def add_sick(message: telebot.types.Message): date = dates[0] else: date = get_time(forum).date() + dt.timedelta(days=30) - sicks = db.read(str(forum) + ".rookies.sick_order", {}) - people = db.read(str(forum) + ".people") + sicks = db.read(f"{forum}.rookies.sick_order", {}) + people = db.read(f"{forum}.people") sicks[user] = (date, False) - db.write(str(forum) + ".rookies.sick_order", sicks) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.rookies.sick_order", sicks) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, - telebot.formatting.escape_markdown( - people[user]["name"] + " " + people[user]["surname"] + + telebot.formatting.escape_markdown( + f"{people[user]["name"]} {people[user]["surname"]}" ) + " болеет до " + telebot.formatting.escape_markdown( @@ -1125,6 +1103,7 @@ def add_sick(message: telebot.types.Message): @bot.message_handler(commands=["order"]) def view_order(message: telebot.types.Message): + "Command to display duty order, honored, guilty and sick people." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -1134,33 +1113,33 @@ def view_order(message: telebot.types.Message): if len(args) > 0: bot.reply_to(chat, "Многа букав") return - order = db.read(str(forum) + ".rookies.order", []) - force = db.read(str(forum) + ".rookies.force_order", []) - honor = db.read(str(forum) + ".rookies.honor_order", []) - sicks = db.read(str(forum) + ".rookies.sick_order", {}) - people = db.read(str(forum) + ".people") - if db.read(str(forum) + ".settings.delete_messages"): + order = db.read(f"{forum}.rookies.order", []) + force = db.read(f"{forum}.rookies.force_order", []) + honor = db.read(f"{forum}.rookies.honor_order", []) + sicks = db.read(f"{forum}.rookies.sick_order", {}) + people = db.read(f"{forum}.people") + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, "*Очередь:*\n" + telebot.formatting.escape_markdown( "\n".join( - [people[u]["surname"] + " " + people[u]["name"] for u in order] + [f"{people[u]["name"]} {people[u]["surname"]}" for u in order] ) ) + "\n\n" + "*Почитаемые:*\n" + telebot.formatting.escape_markdown( "\n".join( - [people[u]["surname"] + " " + people[u]["name"] for u in honor] + [f"{people[u]["name"]} {people[u]["surname"]}" for u in honor] ) ) + "\n" + "*Виноватые:*\n" + telebot.formatting.escape_markdown( "\n".join( - [people[u]["surname"] + " " + people[u]["name"] for u in force] + [f"{people[u]["name"]} {people[u]["surname"]}" for u in force] ) ) + "\n" @@ -1168,10 +1147,7 @@ def view_order(message: telebot.types.Message): + telebot.formatting.escape_markdown( "\n".join( [ - people[u]["surname"] - + " " - + people[u]["name"] - + " до " + f"{people[u]["name"]} {people[u]["surname"]} до " + ".".join( map( str, @@ -1187,6 +1163,7 @@ def view_order(message: telebot.types.Message): @bot.message_handler(commands=["begin"]) def begin_queue(message: telebot.types.Message): + "Command to begin queue." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -1202,7 +1179,7 @@ def begin_queue(message: telebot.types.Message): if len(args) == 1: user = args[0] if user[1:] == bot.get_me().username: - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to(chat, "Ты сейчас быканул\\?") return @@ -1218,40 +1195,39 @@ def begin_queue(message: telebot.types.Message): start_with = f[0] else: start_with = None - phase = db.read(str(forum) + ".schedule.phase") + phase = db.read(f"{forum}.schedule.phase") if phase is None: bot.reply_to(chat, "Задай фазу с помощью /phase") return - status = db.read(str(forum) + ".is_active") + status = db.read(f"{forum}.is_active") if status: - if db.read(str(forum) + ".settings.delete_messages"): + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to(chat, "Держурство уже идёт") return status = True - db.write(str(forum) + ".is_active", status) + db.write(f"{forum}.is_active", status) stack_update(forum, start_with) now_date = get_time(forum).date() - start_with = db.read(str(forum) + ".rookies.order", [None])[0] + start_with = db.read(f"{forum}.rookies.order", [None])[0] if start_with is None: bot.reply_to(chat, "Людей нет") return - db.write(str(forum) + ".schedule.last_stack_update_date", now_date) - db.write(str(forum) + ".schedule.last_notification_date", now_date) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.schedule.last_stack_update_date", now_date) + db.write(f"{forum}.schedule.last_notification_date", now_date) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, "Держурство начнёт " - + db.read(str(forum) + ".people." + start_with + ".name") - + " " - + db.read(str(forum) + ".people." + start_with + ".surname") - + " ", + + f"{db.read(f"{forum}.people.{start_with}.name")} " + + f"{db.read(f"{forum}.people.{start_with}.surname")}" ) @bot.message_handler(commands=["phase"]) def set_phase(message: telebot.types.Message): + "Command to set start phase." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -1272,12 +1248,12 @@ def set_phase(message: telebot.types.Message): now = get_time(forum).date() phase = int(bool(en2)) phases = 2 - db.write(str(forum) + ".schedule.phase", phase) - db.write(str(forum) + ".schedule.phases", phases) - db.write(str(forum) + ".schedule.last_phase_changing_date", now) - phase = db.read(str(forum) + ".schedule.phase", 0) - phases = db.read(str(forum) + ".schedule.phase", 2) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.schedule.phase", phase) + db.write(f"{forum}.schedule.phases", phases) + db.write(f"{forum}.schedule.last_phase_changing_date", now) + phase = db.read(f"{forum}.schedule.phase", 0) + phases = db.read(f"{forum}.schedule.phase", 2) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, "Текущая неделя: " + ("знаменатель" if phase else "числитель") @@ -1286,6 +1262,7 @@ def set_phase(message: telebot.types.Message): @bot.message_handler(commands=["timezone", "tz"]) def set_timezone(message: telebot.types.Message): + "Command to set forum's timezone." forum = message.chat.id chat = get_chat(message) if chat is not None: @@ -1316,9 +1293,9 @@ def set_timezone(message: telebot.types.Message): + "0%B5%D0%BC%D1%8F)", ) return - db.write(str(forum) + ".settings.timezone", tz) - tz = db.read(str(forum) + ".settings.timezone", 3) - if db.read(str(forum) + ".settings.delete_messages"): + db.write(f"{forum}.settings.timezone", tz) + tz = db.read(f"{forum}.settings.timezone", 3) + if db.read(f"{forum}.settings.delete_messages"): bot.delete_message(forum, message.id) bot.reply_to( chat, @@ -1329,30 +1306,32 @@ def set_timezone(message: telebot.types.Message): def get_hours() -> tuple: + "Returns forum's work hours." # TODO command to set range return (8, 20) def stack_update(forum: int, force_reset: bool = False) -> None: + "Updates forum's stacks." now = get_time(forum) now_date = now.date() - order = db.read(str(forum) + ".rookies.order", []) - force = db.read(str(forum) + ".rookies.force_order", []) - honor = db.read(str(forum) + ".rookies.honor_order", []) - sicks = db.read(str(forum) + ".rookies.sick_order", {}) - people = db.read(str(forum) + ".people", {}) + order = db.read(f"{forum}.rookies.order", []) + force = db.read(f"{forum}.rookies.force_order", []) + honor = db.read(f"{forum}.rookies.honor_order", []) + sicks = db.read(f"{forum}.rookies.sick_order", {}) + people = db.read(f"{forum}.people", {}) for i in sicks: if now_date >= sicks[i][0]: if sicks[i][1]: prepend_user(forum, ".rookies.force_order", i) sicks.pop(i) - db.write(str(forum) + ".rookies.sick_order", sicks) + db.write(f"{forum}.rookies.sick_order", sicks) for i in people: if (i in force) and (i in honor): force.remove(i) honor.remove(i) - db.write(str(forum) + ".rookies.force_order", force) - db.write(str(forum) + ".rookies.honor_order", honor) + db.write(f"{forum}.rookies.force_order", force) + db.write(f"{forum}.rookies.honor_order", honor) for i in order: if i not in people.keys(): order.remove(i) @@ -1361,38 +1340,34 @@ def stack_update(forum: int, force_reset: bool = False) -> None: dict(sorted(people.items(), key=lambda item: item[1]["surname"])).keys() ) if force_reset is not False: - db.write(str(forum) + ".rookies.force_order", []) - db.write(str(forum) + ".rookies.honor_order", []) - db.write(str(forum) + ".rookies.sick_order", {}) + db.write(f"{forum}.rookies.force_order", []) + db.write(f"{forum}.rookies.honor_order", []) + db.write(f"{forum}.rookies.sick_order", {}) if force_reset is not None: try: order = order[order.index(force_reset) :] except ValueError: pass - db.write(str(forum) + ".rookies.order", order) + db.write(f"{forum}.rookies.order", order) if len(order) == 0: return if force_reset is False: if len(force) > 0: - db.write( - str(forum) + ".rookies.current", pop_user(forum, "rookies.force_order") - ) + db.write(f"{forum}.rookies.current", pop_user(forum, "rookies.force_order")) else: current = pop_user(forum, "rookies.order") if current in honor: honor.remove(current) - db.write(str(forum) + ".rookies.honor_order", honor) + db.write(f"{forum}.rookies.honor_order", honor) stack_update(forum) elif any([(sicks[i][1] is False) and (i == current) for i in sicks]): skipped = list(sicks[current]) skipped[1] = True sicks[current] = tuple(skipped) - db.write(str(forum) + ".rookies.sick_order", sicks) + db.write(f"{forum}.rookies.sick_order", sicks) stack_update(forum) else: - db.write( - str(forum) + ".rookies.current", pop_user(forum, "rookies.order") - ) + db.write(f"{forum}.rookies.current", pop_user(forum, "rookies.order")) def clean_old_dates(date: dt.datetime, array: str) -> None: @@ -1403,32 +1378,40 @@ def clean_old_dates(date: dt.datetime, array: str) -> None: def update(forum: int) -> None: + """ + Updates forum's state: + · Cleans old skip/work dates. + · Updates order stack. + · Notifies about upcoming duty. + """ now = get_time(forum) now_date = now.date() now_time = now.time() - last_notif = db.read(str(forum) + ".schedule.last_notification_date") - last_upd_stack = db.read(str(forum) + ".schedule.last_stack_update_date") + last_notif = db.read(f"{forum}.schedule.last_notification_date") + last_upd_stack = db.read(f"{forum}.schedule.last_stack_update_date") is_active = get_status(forum, now_date) hours_range = get_hours() change_phase(forum, now_date) - clean_old_dates(now_date, str(forum) + ".schedule.work_days") - clean_old_dates(now_date, str(forum) + ".schedule.skip_days") + clean_old_dates(now_date, f"{forum}.schedule.work_days") + clean_old_dates(now_date, f"{forum}.schedule.skip_days") if is_active and (last_upd_stack is None or now_date > last_upd_stack): - db.write(str(forum) + ".schedule.last_stack_update_date", now_date) + db.write(f"{forum}.schedule.last_stack_update_date", now_date) stack_update(forum) - print(now_time.hour, hours_range) if now_time.hour in hours_range: if last_notif is None or now_date > last_notif: - print("Notified") - db.write(str(forum) + ".schedule.last_notification_date", now_date) + stdout.write("Notified\n") + db.write(f"{forum}.schedule.last_notification_date", now_date) remind_users(forum) + if db.read("about.updatedfrom") != db.read("about.version"): + db.write("about.updatenotified", True) + update_notify(forum) def update_notify(forum: int) -> None: "Notifies the forum about bot's new version." bot.reply_to( get_chat(forum), - "Обновился до версии " + telebot.formatting.escape_markdown(CURRENT_VERSION), + f"Обновился до версии {telebot.formatting.escape_markdown(CURRENT_VERSION)}", ) @@ -1437,20 +1420,26 @@ def process1(): bot.infinity_polling(none_stop=True) +p2_tmo = tmo.Timeout(120) + + def process2(): "The process updates duty order for every forum once a `period` seconds." - period = int(cr.read("settings.notify_period")) - prev_time = time.time() + + def p2(): + "Helper function." + stdout.write("Process 2 update\n") + if db.read("about.updatenotified", True): + db.write("about.updatedfrom", db.read("about.version")) + for f in db.read("").keys(): + try: + update(int(f)) + except ValueError: + pass + + p2_tmo.period = int(cr.read("settings.notify_period")) while True: - cur_time = time.time() - if cur_time - prev_time >= period: - prev_time = cur_time - stdout.write("Process 2 update\n") - for i in db.read("").keys(): - try: - update(int(i)) - except ValueError: - pass + p2_tmo.check(p2, lambda: None) funcs = [process1, process2] @@ -1459,15 +1448,9 @@ for thread in threads: thread.daemon = True thread.start() +db.write("about.updatenotified", False) db.write("about.updatedfrom", VERSION) db.write("about.version", CURRENT_VERSION) -if VERSION != CURRENT_VERSION: - for FORUM in db.read("").keys(): - try: - update_notify(int(FORUM)) - print("New version notification", FORUM) - except ValueError: - pass while True: time.sleep(1) diff --git a/main.py b/main.py index ddb3555..c3a0537 100644 --- a/main.py +++ b/main.py @@ -8,6 +8,7 @@ import os import signal import time import subprocess +from sys import stderr class Runner: @@ -28,10 +29,9 @@ bot_runner = Runner() if __name__ == "__main__": branch = os.popen("git rev-parse --abbrev-ref HEAD").read()[:-1] - print("Starting") bot_runner.start() while True: - print("Updater") + stderr.write("main.py: Updater\n") os.system("git fetch --quiet") if ( os.popen("git log " + branch + "..origin/" + branch + " --oneline") @@ -39,7 +39,7 @@ if __name__ == "__main__": .strip() != "" ): - print("Updating") + stderr.write("main.py: Updating from git\n") bot_runner.stop() os.system("git pull --quiet") os.system("git gc --quiet --aggressive --prune=all") diff --git a/timeout.py b/timeout.py new file mode 100644 index 0000000..0596a42 --- /dev/null +++ b/timeout.py @@ -0,0 +1,43 @@ +# SPDX-FileCopyrightText: 2023 Egor Guslyancev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +"The module provides class to check timeouts." + +import time +import typing + + +class Timeout: + "Timeout checking. Period in seconds." + __timeout = None + __caution = True + + def __init__(self, period: int): + self.period = period + + @property + def period(self) -> int: + "Period property." + return self.__period + + @period.setter + def period(self, period: int): + self.__period = period + + def check(self, once_func: typing.Callable, always_func: typing.Callable) -> bool: + """ + Check if time is out. + `once_func` called once if time is not out, but `check` called twice. + `always_func` called when `check` called twice or more. + """ + if self.__timeout is not None: + if time.time() - self.__timeout < self.period: + if self.__caution: + once_func() + self.__caution = False + always_func() + return True + self.__caution = True + self.__timeout = time.time() + return False