import functools import gc import time import telebot from sqlalchemy.future import Engine from .. import cryptography, database Message = telebot.types.Message def _send_tmp_message( bot: telebot.TeleBot, chat_id: telebot.types.Message, text: str, timeout: int = 5 ) -> None: bot_mes = bot.send_message(chat_id, text, parse_mode="MarkdownV2") time.sleep(timeout) bot.delete_message(chat_id, bot_mes.id) def get_accounts( bot: telebot.TeleBot, engine: Engine, mes: telebot.types.Message ) -> None: accounts = database.get.get_accounts(engine, mes.from_user.id) bot.delete_message(mes.chat.id, mes.id) return _send_tmp_message( bot, mes.chat.id, "Ваши аккаунты:\n" + "\n".join(accounts) if accounts else "У вас нет аккаунтов", timeout=30, ) def _base(bot: telebot.TeleBot, mes: Message, prev_mes: Message | None = None) -> None: bot.delete_message(mes.chat.id, mes.id) if prev_mes is not None: bot.delete_message(prev_mes.chat.id, prev_mes.id) def delete_all( bot: telebot.TeleBot, engine: Engine, mes: telebot.types.Message ) -> None: _base(bot, mes) bot_mes = bot.send_message( mes.chat.id, "Вы действительно хотите удалить все ваши аккаунты? Это действие нельзя отменить. Отправьте YES для подтверждения", ) bot.register_next_step_handler( mes, functools.partial(_delete_all, bot, engine, bot_mes) ) def _delete_all( bot: telebot.TeleBot, engine: Engine, prev_mes: Message, mes: Message ) -> None: _base(bot, mes, prev_mes) text = mes.text.strip() if text == "YES": database.delete.purge_accounts(engine, mes.from_user.id) database.delete.delete_master_pass(engine, mes.from_user.id) _send_tmp_message(bot, mes.chat.id, "Всё успешно удалено", timeout=10) else: _send_tmp_message(bot, mes.chat.id, "Вы отправили не YES, ничего не удалено") def set_master_password(bot: telebot.TeleBot, engine: Engine, mes: Message) -> None: _base(bot, mes, None) if database.get.get_master_pass(engine, mes.from_user.id) is not None: return _send_tmp_message(bot, mes.chat.id, "Мастер пароль уже существует") bot_mes = bot.send_message(mes.chat.id, "Отправьте мастер пароль") bot.register_next_step_handler( mes, functools.partial(_set_master_pass2, bot, engine, bot_mes) ) def _set_master_pass2( bot: telebot.TeleBot, engine: Engine, prev_mes: Message, mes: Message ) -> None: _base(bot, mes, prev_mes) text = mes.text.strip() if text == "/cancel": return _send_tmp_message(bot, mes.chat.id, "Успешная отмена") hash_, salt = cryptography.master_pass.encrypt_master_pass(text) database.add.add_master_pass(engine, mes.from_user.id, salt, hash_) _send_tmp_message(bot, mes.chat.id, "Успех") del mes, text gc.collect() def reset_master_pass(bot: telebot.TeleBot, engine: Engine, mes: Message) -> None: _base(bot, mes) if database.get.get_master_pass(engine, mes.from_user.id) is None: return _send_tmp_message(bot, mes.chat.id, "Мастер пароль не задан") bot_mes = bot.send_message( mes.chat.id, "Отправьте новый мастер пароль, осторожно, все текущие аккаунты будут удалены навсегда", ) bot.register_next_step_handler( mes, functools.partial(_reset_master_pass2, bot, engine, bot_mes) ) def _reset_master_pass2( bot: telebot.TeleBot, engine: Engine, prev_mes: Message, mes: Message ) -> None: _base(bot, mes, prev_mes) text = mes.text.strip() if text == "/cancel": return _send_tmp_message(bot, mes.chat.id, "Успешная отмена") hash_, salt = cryptography.master_pass.encrypt_master_pass(text) database.delete.purge_accounts(engine, mes.from_user.id) database.change.change_master_pass(engine, mes.from_user.id, salt, hash_) _send_tmp_message( bot, mes.chat.id, "Все ваши аккаунты удалены, а мастер пароль изменён" ) del mes, text gc.collect() def add_account(bot: telebot.TeleBot, engine: Engine, mes: Message) -> None: _base(bot, mes) master_password_from_db = database.get.get_master_pass(engine, mes.from_user.id) if master_password_from_db is None: return _send_tmp_message(bot, mes.chat.id, "Нет мастер пароля") bot_mes = bot.send_message(mes.chat.id, "Отправьте название аккаунта") bot.register_next_step_handler( mes, functools.partial(_add_account2, bot, engine, bot_mes) ) def _add_account2( bot: telebot.TeleBot, engine: Engine, prev_mes: Message, mes: Message ) -> None: _base(bot, mes, prev_mes) text = mes.text.strip() if text == "/cancel": return _send_tmp_message(bot, mes.chat.id, "Успешная отмена") if text in database.get.get_accounts(engine, mes.from_user.id): return _send_tmp_message( bot, mes.chat.id, "Аккаунт с таким именем уже существует" ) bot_mes = bot.send_message(mes.chat.id, "Отправьте логин") data = {"name": text} bot.register_next_step_handler( mes, functools.partial(_add_account3, bot, engine, bot_mes, data) ) def _add_account3( bot: telebot.TeleBot, engine: Engine, prev_mes: Message, data: dict[str, str], mes: Message, ) -> None: _base(bot, mes, prev_mes) text = mes.text.strip() if text == "/cancel": return _send_tmp_message(bot, mes.chat.id, "Успешная отмена") data["login"] = text bot_mes = bot.send_message(mes.chat.id, "Отправьте пароль от аккаунта") bot.register_next_step_handler( mes, functools.partial(_add_account4, bot, engine, bot_mes, data) ) def _add_account4( bot: telebot.TeleBot, engine: Engine, prev_mes: Message, data: dict[str, str], mes: Message, ) -> None: _base(bot, mes, prev_mes) text = mes.text.strip() if text == "/cancel": return _send_tmp_message(bot, mes.chat.id, "Успешная отмена") data["passwd"] = text bot_mes = bot.send_message(mes.chat.id, "Отправьте мастер пароль") bot.register_next_step_handler( mes, functools.partial(_add_account5, bot, engine, bot_mes, data) ) def _add_account5( bot: telebot.TeleBot, engine: Engine, prev_mes: Message, data: dict[str, str], mes: Message, ) -> None: _base(bot, mes, prev_mes) text = mes.text.strip() if text == "/cancel": return _send_tmp_message(bot, mes.chat.id, "Успешная отмена") salt, hash_ = database.get.get_master_pass(engine, mes.from_user.id) if cryptography.master_pass.encrypt_master_pass(text, salt) != hash_: return _send_tmp_message(bot, mes.chat.id, "Не подходит главный пароль") name, login, passwd = data["name"], data["login"], data["passwd"] enc_login, enc_pass, salt = cryptography.other_accounts.encrypt_account_info( login, passwd, text.encode("utf-8") ) database.add.add_account(engine, mes.from_user.id, name, salt, enc_login, enc_pass) _send_tmp_message(bot, mes.chat.id, "Успех") del data, name, login, passwd, enc_login gc.collect() def get_account(bot: telebot.TeleBot, engine: Engine, mes: Message) -> None: _base(bot, mes) bot_mes = bot.send_message(mes.chat.id, "Отправьте название аккаунта") bot.register_next_step_handler( mes, functools.partial(_get_account2, bot, engine, bot_mes) ) def _get_account2( bot: telebot.TeleBot, engine: Engine, prev_mes: Message, mes: Message ) -> None: _base(bot, mes, prev_mes) text = mes.text.strip() if text == "/cancel": return _send_tmp_message(bot, mes.chat.id, "Успешная отмена") if text not in database.get.get_accounts(engine, mes.from_user.id): return _send_tmp_message(bot, mes.chat.id, "Нет такого аккаунта") bot_mes = bot.send_message(mes.chat.id, "Отправьте мастер пароль") bot.register_next_step_handler( mes, functools.partial(_get_account3, bot, engine, bot_mes, text) ) def _get_account3( bot: telebot.TeleBot, engine: Engine, prev_mes: Message, name: str, mes: Message ) -> None: _base(bot, mes, prev_mes) text = mes.text.strip() if text == "/cancel": return _send_tmp_message(bot, mes.chat.id, "Успешная отмена") master_pass = database.get.get_master_pass(engine, mes.from_user.id) if master_pass is None: return _send_tmp_message(bot, mes.chat.id, "Нет мастер пароля") master_salt, hash_pass = master_pass if cryptography.master_pass.encrypt_master_pass(text, master_salt) != hash_pass: return _send_tmp_message(bot, mes.chat.id, "Не подходит мастер пароль") salt, enc_login, enc_pass = database.get.get_account_info( engine, mes.from_user.id, name ) login, passwd = cryptography.other_accounts.decrypt_account_info( enc_login, enc_pass, text.encode("utf-8"), salt ) _send_tmp_message( bot, mes.chat.id, f"Логин:\n`{login}`\nПароль:\n`{passwd}`\nНажмите на логин или пароль, чтобы скопировать", 30, ) del text, mes, passwd, login gc.collect() def delete_account(bot: telebot.TeleBot, engine: Engine, mes: Message) -> None: _base(bot, mes) bot_mes = bot.send_message( mes.chat.id, "Отправьте название аккаунта, который вы хотите удалить" ) bot.register_next_step_handler( mes, functools.partial(_delete_account2, bot, engine, bot_mes) ) def _delete_account2( bot: telebot.TeleBot, engine: Engine, prev_mes: Message, mes: Message ) -> None: _base(bot, mes, prev_mes) text = mes.text.strip() if text == "/cancel": return _send_tmp_message(bot, mes.chat.id, "Успешная отмена") if text not in database.get.get_accounts(engine, mes.from_user.id): return _send_tmp_message(bot, mes.chat.id, "Нет такого аккаунта") database.delete.delete_account(engine, mes.from_user.id, text) _send_tmp_message(bot, mes.chat.id, "Аккаунт удалён") def help(bot: telebot.TeleBot, mes: telebot.types.Message) -> None: message = """Команды: /set_master_pass - установить мастер пароль /add_account - создать аккаунт /get_accounts - получить список аккаунтов /get_account - получить логин и пароль аккаунта /delete_account - удалить аккаунт /delete_all - удалить все аккаунты и мастер пароль /reset_master_pass - удалить все аккаунты и изменить мастер пароль /cancel - отмена текущего действия /help - помощь""" bot.send_message(mes.chat.id, message) def cancel(bot: telebot.TeleBot, mes: Message) -> None: _send_tmp_message(bot, mes.chat.id, "Нет активного действия")