From 7d4aaf99d0a6437064dba711622919fbd3d26dd8 Mon Sep 17 00:00:00 2001 From: djimbo Date: Fri, 18 Nov 2022 14:29:52 +0300 Subject: [PATCH] Update --- main.py | 11 ++- requirements.txt | 8 +- tgbot/data/config.py | 8 +- tgbot/keyboards/inline_main.py | 10 ++- tgbot/keyboards/reply_misc.py | 4 +- tgbot/routers/__init__.py | 2 +- tgbot/routers/admin/admin_menu.py | 36 ++++++--- tgbot/routers/main_errors.py | 39 +++++----- tgbot/routers/main_missed.py | 19 ++--- tgbot/routers/main_start.py | 4 +- tgbot/routers/user/user_menu.py | 23 +++++- tgbot/services/api_sqlite.py | 3 +- tgbot/utils/const_functions.py | 123 +++++++++++++++++++----------- tgbot/utils/misc_functions.py | 4 +- 14 files changed, 185 insertions(+), 109 deletions(-) diff --git a/main.py b/main.py index 5128f9e..ffa8bb2 100644 --- a/main.py +++ b/main.py @@ -13,15 +13,15 @@ from tgbot.services.api_session import AsyncSession from tgbot.services.api_sqlite import create_dbx from tgbot.utils.misc.bot_commands import set_commands from tgbot.utils.misc.bot_logging import bot_logger -from tgbot.utils.misc_functions import autobackup, startup_notify +from tgbot.utils.misc_functions import autobackup_admin, startup_notify colorama.init() # Запуск шедулеров async def scheduler_start(bot): - scheduler.add_job(autobackup, "cron", hour=00, args=(bot,)) # Ежедневный Автобэкап в 00:00 - scheduler.add_job(autobackup, "cron", hour=12, args=(bot,)) # Ежедневный Автобэкап в 12:00 + scheduler.add_job(autobackup_admin, "cron", hour=00, args=(bot,)) # Ежедневный Автобэкап в 00:00 + scheduler.add_job(autobackup_admin, "cron", hour=12, args=(bot,)) # Ежедневный Автобэкап в 12:00 # Запуск бота и функций @@ -38,11 +38,10 @@ async def main(): await set_commands(bot) await startup_notify(bot) await scheduler_start(bot) - bot_info = await bot.get_me() bot_logger.warning("Bot was started") - print(colorama.Fore.LIGHTYELLOW_EX + f"~~~~~ Bot was started - @{bot_info.username} ~~~~~") - print(colorama.Fore.LIGHTBLUE_EX + "~~~~~ TG developer: @djimbox ~~~~~") + print(colorama.Fore.LIGHTYELLOW_EX + f"~~~~~ Bot was started - @{(await bot.get_me()).username} ~~~~~") + print(colorama.Fore.LIGHTBLUE_EX + "~~~~~ TG developer - @djimbox ~~~~~") print(colorama.Fore.RESET) if len(get_admins()) == 0: print("***** ENTER ADMIN ID IN settings.ini *****") diff --git a/requirements.txt b/requirements.txt index b8ef0f1..530fbe9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ +APScheduler==3.9.1 +aiogram==3.0.0b5 colorlog==6.7.0 -aiogram>=3.0.0b4 -aiohttp==3.8.1 typing==3.7.4.3 -APScheduler==3.8.0 -colorama==0.4.5 +aiohttp==3.8.3 +colorama==0.4.5 \ No newline at end of file diff --git a/tgbot/data/config.py b/tgbot/data/config.py index 3c163ac..82adf14 100644 --- a/tgbot/data/config.py +++ b/tgbot/data/config.py @@ -4,7 +4,7 @@ import configparser from apscheduler.schedulers.asyncio import AsyncIOScheduler # Токен бота -BOT_TOKEN = configparser.ConfigParser(timezone="Europe/Moscow") +BOT_TOKEN = configparser.ConfigParser() BOT_TOKEN.read("settings.ini") BOT_TOKEN = BOT_TOKEN['settings']['token'].strip().replace(' ', '') @@ -22,10 +22,10 @@ def get_admins(): read_admins = configparser.ConfigParser() read_admins.read('settings.ini') - admins = read_admins['settings']['admin_id'].strip().replace(' ', '') + admins = read_admins['settings']['admin_id'].strip().replace(" ", "") - if ',' in admins: - admins = admins.split(',') + if "," in admins: + admins = admins.split(",") else: if len(admins) >= 1: admins = [admins] diff --git a/tgbot/keyboards/inline_main.py b/tgbot/keyboards/inline_main.py index 0768068..a883ca7 100644 --- a/tgbot/keyboards/inline_main.py +++ b/tgbot/keyboards/inline_main.py @@ -9,14 +9,16 @@ from tgbot.utils.const_functions import ikb def menu_finl(user_id): keyboard = InlineKeyboardBuilder( ).row( - ikb("User 1", data="..."), - ikb("User 2", data="..."), + ikb("User X", data="user_inline_x"), + ikb("User 1", data="user_inline:1"), + ikb("User 2", data="user_inline:2"), ) if user_id in get_admins(): keyboard.row( - ikb("Admin 1", data="..."), - ikb("Admin 2", data="..."), + ikb("Admin X", data="admin_inline_x"), + ikb("Admin 1", data="admin_inline:1"), + ikb("Admin 2", data="admin_inline:2"), ) return keyboard.as_markup() diff --git a/tgbot/keyboards/reply_misc.py b/tgbot/keyboards/reply_misc.py index 36e9a30..352c082 100644 --- a/tgbot/keyboards/reply_misc.py +++ b/tgbot/keyboards/reply_misc.py @@ -9,7 +9,7 @@ admin_rep = ReplyKeyboardBuilder( rkb("Admin Reply 1"), rkb("Admin Reply 2"), ).row( - rkb("⬅ Главное меню"), + rkb("🔙 Главное меню"), ).as_markup(resize_keyboard=True) # Тестовые юзер реплай кнопки @@ -18,5 +18,5 @@ user_rep = ReplyKeyboardBuilder( rkb("User Reply 1"), rkb("User Reply 2"), ).row( - rkb("⬅ Главное меню"), + rkb("🔙 Главное меню"), ).as_markup(resize_keyboard=True) diff --git a/tgbot/routers/__init__.py b/tgbot/routers/__init__.py index eb67939..00c6c3c 100644 --- a/tgbot/routers/__init__.py +++ b/tgbot/routers/__init__.py @@ -22,7 +22,7 @@ def register_all_routers(dp: Dispatcher): dp.include_router(main_errors.router) dp.include_router(main_start.router) - # Подключение хендлеров (юзеров и админов) + # Подключение пользовательских роутеров (юзеров и админов) dp.include_router(user_menu.router) # Юзер хендлер dp.include_router(admin_menu.router) # Админ хендлер diff --git a/tgbot/routers/admin/admin_menu.py b/tgbot/routers/admin/admin_menu.py index 2a3636a..067047d 100644 --- a/tgbot/routers/admin/admin_menu.py +++ b/tgbot/routers/admin/admin_menu.py @@ -1,6 +1,7 @@ # - *- coding: utf- 8 - *- from aiogram import Router, Bot -from aiogram.types import FSInputFile, Message +from aiogram.filters import Text, Command +from aiogram.types import FSInputFile, Message, CallbackQuery from tgbot.data.config import PATH_DATABASE, PATH_LOGS from tgbot.keyboards.inline_misc import admin_inl @@ -12,7 +13,7 @@ router = Router() # Кнопка - Admin Inline -@router.message(text="Admin Inline") +@router.message(Text(text="Admin Inline")) async def admin_button_inline(message: Message, bot: Bot, state: FSM, aSession: AS, my_user): await state.clear() @@ -20,35 +21,50 @@ async def admin_button_inline(message: Message, bot: Bot, state: FSM, aSession: # Кнопка - Admin Reply -@router.message(text="Admin Reply") +@router.message(Text(text="Admin Reply")) async def admin_button_reply(message: Message, bot: Bot, state: FSM, aSession: AS, my_user): await state.clear() await message.answer("Click Button - Admin Reply", reply_markup=admin_rep) +# Колбэк - Admin X +@router.callback_query(Text(text="admin_inline_x")) +async def admin_callback_inline_x(call: CallbackQuery, bot: Bot, state: FSM, aSession: AS, my_user): + await call.answer(f"Click Admin X") + + +# Колбэк - Admin +@router.callback_query(Text(startswith="admin_inline:")) +async def admin_callback_inline(call: CallbackQuery, bot: Bot, state: FSM, aSession: AS, my_user): + get_data = call.data.split(":")[1] + + await call.answer(f"Click Admin - {get_data}", True) + + # Получение Базы Данных -@router.message(commands=['db', 'database']) +@router.message(Command(commands=['db', 'database'])) async def admin_database(message: Message, bot: Bot, state: FSM, aSession: AS, my_user): await state.clear() await message.answer_document(FSInputFile(PATH_DATABASE), caption=f"📦 BACKUP\n" - f"🕰 {get_date()}") + f"🕰 {get_date()}") # Получение логов -@router.message(commands=['log', 'logs']) +@router.message(Command(commands=['log', 'logs'])) async def admin_log(message: Message, bot: Bot, state: FSM, aSession: AS, my_user): await state.clear() - await message.answer_document(FSInputFile(PATH_LOGS), caption=f"🕰 {get_date()}") + await message.answer_document(FSInputFile(PATH_LOGS), + caption=f"🖨 LOGS\n" + f"🕰 {get_date()}") # Очистить логи -@router.message(text_contains=['log', '_', 'clean']) -@router.message(text_contains=['log', '_', 'clear']) -async def admin_log_clear(message: Message, bot: Bot, state: FSM, aSession: AS, my_user): +@router.message(Command(commands=['clear_log', 'clear_logs', 'log_clear', 'logs_clear'])) +async def admin_logs_clear(message: Message, bot: Bot, state: FSM, aSession: AS, my_user): await state.clear() with open(PATH_LOGS, "w") as file: diff --git a/tgbot/routers/main_errors.py b/tgbot/routers/main_errors.py index 593c9e1..cf2a7dc 100644 --- a/tgbot/routers/main_errors.py +++ b/tgbot/routers/main_errors.py @@ -1,26 +1,29 @@ # - *- coding: utf- 8 - *- -from aiogram import Router, Bot -from aiogram.exceptions import TelegramAPIError -from aiogram.types import Update +from aiogram import Router +from aiogram.exceptions import TelegramForbiddenError +from aiogram.filters import ExceptionTypeFilter, ExceptionMessageFilter +from aiogram.handlers import ErrorHandler from tgbot.utils.misc.bot_logging import bot_logger -from tgbot.utils.misc_functions import send_admins router = Router() -# Обработка ошибок -@router.errors() -async def processing_errors(update: Update, exception: TelegramAPIError, bot: Bot): - print(f"-Exception | {exception}") - await send_admins( - bot, - f"❌ Ошибка\n\n" - f"Exception: {exception}\n\n" - f"Update: {update.dict()}" - ) +# Ошибка с блокировкой бота пользователем +@router.errors(ExceptionTypeFilter(TelegramForbiddenError)) +class MyHandler(ErrorHandler): + async def handle(self): + pass - bot_logger.exception( - f"Exception: {exception}\n" - f"Update: {update.dict()}" - ) + +# Ошибка с редактированием одинакового сообщения +@router.errors(ExceptionMessageFilter( + "Bad Request: message is not modified: specified new message content and reply markup are exactly the same as a current content and reply markup of the message")) +class MyHandler(ErrorHandler): + async def handle(self): + bot_logger.exception( + f"====================\n" + f"Exception name: {self.exception_name}\n" + f"Exception message: {self.exception_message}\n" + f"====================" + ) diff --git a/tgbot/routers/main_missed.py b/tgbot/routers/main_missed.py index 52516c2..7b405e5 100644 --- a/tgbot/routers/main_missed.py +++ b/tgbot/routers/main_missed.py @@ -1,28 +1,29 @@ # - *- coding: utf- 8 - *- -from aiogram import types, Router -from aiogram.types import CallbackQuery +from aiogram import Router, Bot +from aiogram.filters import Text +from aiogram.types import CallbackQuery, Message from tgbot.keyboards.reply_main import menu_frep -from tgbot.utils.misc.bot_models import FSM +from tgbot.utils.misc.bot_models import FSM, AS router = Router() # Колбэк с удалением сообщения -@router.callback_query(text="close_this") -async def processing_callback_close(call: CallbackQuery, state: FSM): +@router.callback_query(Text(text="close_this")) +async def main_callback_close(call: CallbackQuery, bot: Bot, state: FSM, aSession: AS, my_user): await call.message.delete() # Колбэк с обработкой кнопки -@router.callback_query(text="...") -async def processing_callback_answer(call: CallbackQuery, state: FSM): +@router.callback_query(Text(text="...")) +async def main_callback_answer(call: CallbackQuery, bot: Bot, state: FSM, aSession: AS, my_user): await call.answer(cache_time=20) # Колбэк с обработкой удаления сообщений потерявших стейт @router.callback_query() -async def processing_callback_missed(call: CallbackQuery, state: FSM): +async def main_callback_missed(call: CallbackQuery, bot: Bot, state: FSM, aSession: AS, my_user): try: await call.message.delete() except: @@ -35,6 +36,6 @@ async def processing_callback_missed(call: CallbackQuery, state: FSM): # Обработка всех неизвестных команд @router.message() -async def processing_message_missed(message: types.Message, state: FSM): +async def main_message_missed(message: Message, bot: Bot, state: FSM, aSession: AS, my_user): await message.answer("♦ Неизвестная команда.\n" "▶ Введите /start") diff --git a/tgbot/routers/main_start.py b/tgbot/routers/main_start.py index 431d221..eaed047 100644 --- a/tgbot/routers/main_start.py +++ b/tgbot/routers/main_start.py @@ -1,5 +1,6 @@ # - *- coding: utf- 8 - *- from aiogram import Router, Bot +from aiogram.filters import Text, Command from aiogram.types import Message from tgbot.keyboards.reply_main import menu_frep @@ -9,7 +10,8 @@ router = Router() # Открытие главного меню -@router.message(text_startswith=["⬅ Главное меню", "/start"]) +@router.message(Text(text=['🔙 Главное меню'])) +@router.message(Command(commands=['start'])) async def main_start(message: Message, bot: Bot, state: FSM, aSession: AS, my_user): await state.clear() diff --git a/tgbot/routers/user/user_menu.py b/tgbot/routers/user/user_menu.py index 68c0473..79a0571 100644 --- a/tgbot/routers/user/user_menu.py +++ b/tgbot/routers/user/user_menu.py @@ -1,6 +1,7 @@ # - *- coding: utf- 8 - *- from aiogram import Router, Bot -from aiogram.types import Message +from aiogram.filters import Command, Text +from aiogram.types import Message, CallbackQuery from tgbot.keyboards.inline_main import menu_finl from tgbot.keyboards.inline_misc import user_inl @@ -11,7 +12,7 @@ router = Router() # Кнопка - User Inline -@router.message(text="User Inline") +@router.message(Text(text="User Inline")) async def user_button_inline(message: Message, bot: Bot, state: FSM, aSession: AS, my_user): await state.clear() @@ -19,7 +20,7 @@ async def user_button_inline(message: Message, bot: Bot, state: FSM, aSession: A # Кнопка - User Reply -@router.message(text="User Reply") +@router.message(Text(text="User Reply")) async def user_button_reply(message: Message, bot: Bot, state: FSM, aSession: AS, my_user): await state.clear() @@ -27,8 +28,22 @@ async def user_button_reply(message: Message, bot: Bot, state: FSM, aSession: AS # Команда - /inline -@router.message(commands="inline") +@router.message(Command(commands="inline")) async def user_command_inline(message: Message, bot: Bot, state: FSM, aSession: AS, my_user): await state.clear() await message.answer("Click command - /inline", reply_markup=menu_finl(message.from_user.id)) + + +# Колбэк - User X +@router.callback_query(Text(text="user_inline_x")) +async def user_callback_inline_x(call: CallbackQuery, bot: Bot, state: FSM, aSession: AS, my_user): + await call.answer(f"Click User X") + + +# Колбэк - User +@router.callback_query(Text(startswith="user_inline:")) +async def user_callback_inline(call: CallbackQuery, bot: Bot, state: FSM, aSession: AS, my_user): + get_data = call.data.split(":")[1] + + await call.answer(f"Click User - {get_data}", True) diff --git a/tgbot/services/api_sqlite.py b/tgbot/services/api_sqlite.py index c72aaf1..adf2f0b 100644 --- a/tgbot/services/api_sqlite.py +++ b/tgbot/services/api_sqlite.py @@ -116,5 +116,6 @@ def create_dbx(): "user_surname TEXT," "user_fullname TEXT," "user_date TIMESTAMP," - "user_unix INTEGER)") + "user_unix INTEGER" + ")") print("DB was not found(1/1) | Creating...") diff --git a/tgbot/utils/const_functions.py b/tgbot/utils/const_functions.py index f2676eb..0a1788e 100644 --- a/tgbot/utils/const_functions.py +++ b/tgbot/utils/const_functions.py @@ -2,20 +2,22 @@ import random import time from datetime import datetime +from typing import Union from aiogram import Bot, types from aiogram.types import InlineKeyboardButton, KeyboardButton + from tgbot.data.config import get_admins ######################################## AIOGRAM ######################################## # Генерация реплай кнопки -def rkb(text): +def rkb(text) -> KeyboardButton: return KeyboardButton(text=text) # Генерация инлайн кнопки -def ikb(text, data=None, url=None, switch=None): +def ikb(text, data=None, url=None, switch=None) -> InlineKeyboardButton: if data is not None: return InlineKeyboardButton(text=text, callback_data=data) elif url is not None: @@ -71,7 +73,7 @@ async def smart_send(bot: Bot, message: types.Message, user_id, text_add=None, r ######################################## ПРОЧЕЕ ######################################## # Удаление отступов у текста -def ded(get_text: str): +def ded(get_text: str) -> str: if get_text is not None: split_text = get_text.split("\n") if split_text[0] == "": split_text.pop(0) @@ -84,20 +86,25 @@ def ded(get_text: str): save_text.append(text) get_text = "\n".join(save_text) + else: + get_text = "" return get_text + # Очистка HTML тэгов -def clear_html(get_text: str): +def clear_html(get_text: str) -> str: if get_text is not None: if "<" in get_text: get_text = get_text.replace("<", "*") if ">" in get_text: get_text = get_text.replace(">", "*") + else: + get_text = "" return get_text # Очистка пробелов в списке -def clear_list(get_list: list): +def clear_list(get_list: list) -> list: while "" in get_list: get_list.remove("") while " " in get_list: get_list.remove(" ") while "," in get_list: get_list.remove(",") @@ -112,7 +119,7 @@ def split_messages(get_list: list, count: int): # Получение даты -def get_date(): +def get_date() -> str: this_date = datetime.today().replace(microsecond=0) this_date = this_date.strftime("%d.%m.%Y %H:%M:%S") @@ -120,35 +127,50 @@ def get_date(): # Получение юникс даты -def get_unix(): - return int(time.time()) +def get_unix(full=False) -> int: + if full: + return time.time_ns() + else: + return int(time.time()) # Генерация пароля -def generate_password(len_password: int): - passwd = list("1234567890abcdefghigklmnopqrstuvyxwzABCDEFGHIGKLMNOPQRSTUVYXWZ") - random.shuffle(passwd) - random_chars = "".join([random.choice(passwd) for x in range(len_password)]) +def gen_password(len_password: int = 16, type_password: str = "default") -> str: # default, number, letter, onechar + if type_password == "default": + char_password = list("1234567890abcdefghigklmnopqrstuvyxwzABCDEFGHIGKLMNOPQRSTUVYXWZ") + elif type_password == "letter": + char_password = list("abcdefghigklmnopqrstuvyxwzABCDEFGHIGKLMNOPQRSTUVYXWZ") + elif type_password == "number": + char_password = list("1234567890") + elif type_password == "onechar": + char_password = list("1234567890") + + random.shuffle(char_password) + random_chars = "".join([random.choice(char_password) for x in range(len_password)]) + + if type_password == "onechar": + random_chars = f"{random.choice('abcdefghigklmnopqrstuvyxwzABCDEFGHIGKLMNOPQRSTUVYXWZ')}{random_chars[1:]}" return random_chars # Корректный вывод времени -def convert_times(get_time, get_type="day"): +def convert_times(get_time, get_type="day") -> str: get_time = int(get_time) + if get_time < 0: get_time = 0 if get_type == "second": - get_list = ["секунда", "секунды", "секунд"] + get_list = ['секунда', 'секунды', 'секунд'] elif get_type == "minute": - get_list = ["минута", "минуты", "минут"] + get_list = ['минута', 'минуты', 'минут'] elif get_type == "hour": - get_list = ["час", "часа", "часов"] + get_list = ['час', 'часа', 'часов'] elif get_type == "day": - get_list = ["день", "дня", "дней"] + get_list = ['день', 'дня', 'дней'] elif get_type == "month": - get_list = ["месяц", "месяца", "месяцев"] + get_list = ['месяц', 'месяца', 'месяцев'] else: - get_list = ["год", "года", "лет"] + get_list = ['год', 'года', 'лет'] if get_time % 10 == 1 and get_time % 100 != 11: count = 0 @@ -161,35 +183,57 @@ def convert_times(get_time, get_type="day"): ######################################## ЧИСЛА ######################################## +# Преобразование длинных вещественных чисел в читаемый вид +def snum(amount, remains=0) -> str: + if "-" in str(amount): + str_amount = "%.*f" % (int(str(amount).split("-")[1]), amount) + elif "." in str(amount): + str_amount = f"{float(amount):.{len(str(amount).split('.')[1])}f}" + else: + str_amount = str(amount) + + while str_amount.endswith('0'): str_amount = str_amount[:-1] + if str_amount.endswith('.'): str_amount = str_amount[:-1] + + if remains != 0: + str_amount = round(float(str_amount), remains) + + return str(str_amount) + + # Конвертация числа в вещественное -def to_float(get_number, remains=2): +def to_float(get_number, remains=2) -> Union[int, float]: if "," in str(get_number): get_number = str(get_number).replace(",", ".") - if "." in get_number: - get_last = get_number.split(".") + if "." in str(get_number): + get_last = str(get_number).split(".") - if get_last[1].endswith("0"): + if str(get_last[1]).endswith("0"): while True: - if get_number.endswith("0"): - get_number = get_number[:-1] + if str(get_number).endswith("0"): + get_number = str(get_number)[:-1] else: break get_number = round(float(get_number), remains) - if str(float(get_number)).split(".")[1] == "0": - get_number = int(get_number) + str_number = snum(get_number) + if "." in str_number: + if str_number.split(".")[1] == "0": + get_number = int(get_number) + else: + get_number = float(get_number) else: - get_number = float(get_number) + get_number = int(get_number) return get_number # Конвертация числа в целочисленное -def to_int(get_number): +def to_int(get_number) -> int: if "," in get_number: - get_number = get_number.replace(",", ".") + get_number = str(get_number).replace(",", ".") get_number = int(round(float(get_number))) @@ -197,8 +241,8 @@ def to_int(get_number): # Проверка числа на вещественное -def is_number(get_number): - if get_number.isdigit(): +def is_number(get_number) -> bool: + if str(get_number).isdigit(): return False else: if "," in str(get_number): get_number = str(get_number).replace(",", ".") @@ -210,18 +254,11 @@ def is_number(get_number): return True -# Преобразование длинных вещественных чисел в читаемый вид -def show_floats(amount): - return f"{float(amount):.{len(str(amount).split('.')[1])}f}" - - -# Форматирование чисел в читаемый вид -def format_rate(rate, around=False): +# Форматирование числа в читаемый вид +def format_rate(rate, around=False) -> str: rate = str(round(float(rate), 2)) - if "," in rate: - rate = float(rate.replace(",", ".")) - + if "," in rate: rate = float(rate.replace(",", ".")) len_rate = str(int(float(rate))) if len(len_rate) == 3: @@ -241,7 +278,7 @@ def format_rate(rate, around=False): elif len(len_rate) == 10: get_rate = f"{len_rate[0:1]} {len_rate[1:4]} {len_rate[4:7]} {len_rate[7:]}" else: - get_rate = 0 + get_rate = "0" if not around: if "." in rate: diff --git a/tgbot/utils/misc_functions.py b/tgbot/utils/misc_functions.py index 06f1d28..69bced4 100644 --- a/tgbot/utils/misc_functions.py +++ b/tgbot/utils/misc_functions.py @@ -13,12 +13,12 @@ async def startup_notify(bot: Bot): # Автоматические бэкапы БД -async def autobackup(bot: Bot): +async def autobackup_admin(bot: Bot): for admin in get_admins(): try: await bot.send_document(admin, FSInputFile(PATH_DATABASE), caption=f"📦 AUTOBACKUP\n" - f"🕰 {get_date()}") + f"🕰 {get_date()}") except: pass