diff --git a/main.py b/main.py index 1e68e41..9b7e7ae 100644 --- a/main.py +++ b/main.py @@ -21,7 +21,7 @@ colorama.init() # Запуск шедулеров async def scheduler_start(bot): scheduler.add_job(autobackup_admin, "cron", hour=00, args=(bot,)) # Ежедневный Автобэкап в 00:00 - scheduler.add_job(autobackup_admin, "cron", hour=12, args=(bot,)) # Ежедневный Автобэкап в 12:00 + # scheduler.add_job(autobackup_admin, "cron", hour=12, args=(bot,)) # Ежедневный Автобэкап в 12:00 # Запуск бота и функций @@ -39,7 +39,7 @@ async def main(): await startup_notify(bot) await scheduler_start(bot) - bot_logger.warning("Bot was started") + bot_logger.warning("BOT WAS STARTED") 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) @@ -49,7 +49,11 @@ async def main(): await bot.delete_webhook() await bot.get_updates(offset=-1) - await dp.start_polling(bot, allowed_updates=dp.resolve_used_update_types(), rSession=rSession) + await dp.start_polling( + bot, + allowed_updates=dp.resolve_used_update_types(), + rSession=rSession, + ) finally: await rSession.close() await bot.session.close() diff --git a/requirements.txt b/requirements.txt index 0745a10..7018df4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,8 @@ -APScheduler==3.9.1 -aiogram==3.0.0b7 -colorlog==6.7.0 -typing==3.7.4.3 -aiohttp==3.8.4 -colorama==0.4.5 -cachetools==5.3.0 \ No newline at end of file +APScheduler~=3.9.1 +aiogram~=3.0.0 +colorlog~=6.7.0 +pytz~=2022.6 +typing~=3.7.4.3 +aiohttp~=3.8.5 +cachetools~=4.2.4 +colorama~=0.4.5 \ No newline at end of file diff --git a/tgbot/data/config.py b/tgbot/data/config.py index 06b1ece..1a36fc9 100644 --- a/tgbot/data/config.py +++ b/tgbot/data/config.py @@ -15,7 +15,7 @@ PATH_LOGS = "tgbot/data/logs.log" # Путь к Логам # Образы и конфиги scheduler = AsyncIOScheduler(timezone=BOT_TIMEZONE) # Образ шедулера -start_status = True # Оповещение админам при запуске бота (True или False) +start_status = True # Оповещение админам о запуске бота (True или False) # Получение администраторов бота diff --git a/tgbot/keyboards/inline_main.py b/tgbot/keyboards/inline_main.py index fdc7e06..088e7aa 100644 --- a/tgbot/keyboards/inline_main.py +++ b/tgbot/keyboards/inline_main.py @@ -8,18 +8,19 @@ from tgbot.utils.const_functions import ikb # Кнопки инлайн меню def menu_finl(user_id) -> InlineKeyboardMarkup: - keyboard = InlineKeyboardBuilder( - ).row( + keyboard = InlineKeyboardBuilder() + + keyboard.row( ikb("User X", data="user_inline_x"), - ikb("User 1", data="user_inline:1"), - ikb("User 2", data="user_inline:2"), + ikb("User 1", data="user_inline:user_btn"), + ikb("User 2", data="..."), ) if user_id in get_admins(): keyboard.row( ikb("Admin X", data="admin_inline_x"), - ikb("Admin 1", data="admin_inline:1"), - ikb("Admin 2", data="admin_inline:2"), + ikb("Admin 1", data="admin_inline:admin_btn"), + ikb("Admin 2", data="unknown"), ) return keyboard.as_markup() diff --git a/tgbot/keyboards/reply_misc.py b/tgbot/keyboards/reply_misc.py index 352c082..7dd1c22 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("🔙 Main menu"), ).as_markup(resize_keyboard=True) # Тестовые юзер реплай кнопки @@ -18,5 +18,5 @@ user_rep = ReplyKeyboardBuilder( rkb("User Reply 1"), rkb("User Reply 2"), ).row( - rkb("🔙 Главное меню"), + rkb("🔙 Main menu"), ).as_markup(resize_keyboard=True) diff --git a/tgbot/middlewares/throttling.py b/tgbot/middlewares/throttling.py index 356a9a2..eff89da 100644 --- a/tgbot/middlewares/throttling.py +++ b/tgbot/middlewares/throttling.py @@ -46,11 +46,15 @@ class ThrottlingMiddleware(BaseMiddleware): self.users[this_user.id]['count_throttled'] = 2 self.users[this_user.id]['now_rate'] = self.default_rate + 3 - await event.reply("❗ Пожалуйста, не спамьте.\n" - "❗ Please, do not spam.") + await event.reply( + "❗ Пожалуйста, не спамьте.\n" + "❗ Please, do not spam.", + ) elif self.users[this_user.id]['count_throttled'] == 2: self.users[this_user.id]['count_throttled'] = 3 self.users[this_user.id]['now_rate'] = self.default_rate + 5 - await event.reply("❗ Бот не будет отвечать до прекращения спама.\n" - "❗ The bot will not respond until the spam stops.") + await event.reply( + "❗ Бот не будет отвечать до прекращения спама.\n" + "❗ The bot will not respond until the spam stops.", + ) diff --git a/tgbot/routers/admin/admin_menu.py b/tgbot/routers/admin/admin_menu.py index b037155..905b00e 100644 --- a/tgbot/routers/admin/admin_menu.py +++ b/tgbot/routers/admin/admin_menu.py @@ -1,6 +1,6 @@ # - *- coding: utf- 8 - *- -from aiogram import Router, Bot -from aiogram.filters import Text, Command +from aiogram import Router, Bot, F +from aiogram.filters import Command from aiogram.types import FSInputFile, Message, CallbackQuery from tgbot.data.config import PATH_DATABASE, PATH_LOGS @@ -13,7 +13,7 @@ router = Router() # Кнопка - Admin Inline -@router.message(Text(text="Admin Inline")) +@router.message(F.text == 'Admin Inline') async def admin_button_inline(message: Message, bot: Bot, state: FSM, rSession: RS, my_user): await state.clear() @@ -21,7 +21,7 @@ async def admin_button_inline(message: Message, bot: Bot, state: FSM, rSession: # Кнопка - Admin Reply -@router.message(Text(text="Admin Reply")) +@router.message(F.text == 'Admin Reply') async def admin_button_reply(message: Message, bot: Bot, state: FSM, rSession: RS, my_user): await state.clear() @@ -29,13 +29,13 @@ async def admin_button_reply(message: Message, bot: Bot, state: FSM, rSession: R # Колбэк - Admin X -@router.callback_query(Text(text="admin_inline_x")) +@router.callback_query(F.data == 'admin_inline_x') async def admin_callback_inline_x(call: CallbackQuery, bot: Bot, state: FSM, rSession: RS, my_user): await call.answer(f"Click Admin X") # Колбэк - Admin -@router.callback_query(Text(startswith="admin_inline:")) +@router.callback_query(F.data.startswith('admin_inline:')) async def admin_callback_inline(call: CallbackQuery, bot: Bot, state: FSM, rSession: RS, my_user): get_data = call.data.split(":")[1] diff --git a/tgbot/routers/main_missed.py b/tgbot/routers/main_missed.py index 456391b..fff3304 100644 --- a/tgbot/routers/main_missed.py +++ b/tgbot/routers/main_missed.py @@ -1,22 +1,21 @@ # - *- coding: utf- 8 - *- -from aiogram import Router, Bot -from aiogram.filters import Text +from aiogram import Router, Bot, F from aiogram.types import CallbackQuery, Message -from tgbot.keyboards.reply_main import menu_frep +from tgbot.utils.const_functions import del_message from tgbot.utils.misc.bot_models import FSM, RS router = Router() # Колбэк с удалением сообщения -@router.callback_query(Text(text="close_this")) +@router.callback_query(F.data == 'close_this') async def main_callback_close(call: CallbackQuery, bot: Bot, state: FSM, rSession: RS, my_user): - await call.message.delete() + await del_message(call.message) # Колбэк с обработкой кнопки -@router.callback_query(Text(text="...")) +@router.callback_query(F.data == '...') async def main_callback_answer(call: CallbackQuery, bot: Bot, state: FSM, rSession: RS, my_user): await call.answer(cache_time=30) @@ -24,20 +23,13 @@ async def main_callback_answer(call: CallbackQuery, bot: Bot, state: FSM, rSessi # Колбэк с обработкой удаления сообщений потерявших стейт @router.callback_query() async def main_callback_missed(call: CallbackQuery, bot: Bot, state: FSM, rSession: RS, my_user): - try: - await call.message.delete() - except: - pass - - await call.message.answer( - "❌ Данные не были найдены из-за перезапуска скрипта.\n" - "♻ Выполните действие заново.", - reply_markup=menu_frep(call.from_user.id), - ) + await call.answer(f"❗️ Miss callback: {call.data}", True) # Обработка всех неизвестных команд @router.message() async def main_message_missed(message: Message, bot: Bot, state: FSM, rSession: RS, my_user): - await message.answer("♦ Неизвестная команда.\n" - "♦ Введите /start") + await message.answer( + "♦️ Unknown command.\n" + "♦️ Enter /start", + ) diff --git a/tgbot/routers/main_start.py b/tgbot/routers/main_start.py index ca37688..8eb9fcc 100644 --- a/tgbot/routers/main_start.py +++ b/tgbot/routers/main_start.py @@ -1,6 +1,6 @@ # - *- coding: utf- 8 - *- -from aiogram import Router, Bot -from aiogram.filters import Text, Command +from aiogram import Router, Bot, F +from aiogram.filters import Command from aiogram.types import Message from tgbot.keyboards.reply_main import menu_frep @@ -11,16 +11,15 @@ router = Router() # Открытие главного меню -@router.message(Text(text=['🔙 Главное меню'])) +@router.message(F.text.in_(('🔙 Main menu', '🔙 Return'))) @router.message(Command(commands=['start'])) async def main_start(message: Message, bot: Bot, state: FSM, rSession: RS, my_user): await state.clear() await message.answer( - ded(""" - 🔸 Бот готов к использованию. - 🔸 Если не появились вспомогательные кнопки - 🔸 Введите /start + ded(f""" + 🔸 Hello, {my_user['user_name']} + 🔸 Enter /start or /inline """), reply_markup=menu_frep(message.from_user.id), ) diff --git a/tgbot/routers/user/user_menu.py b/tgbot/routers/user/user_menu.py index f14f956..eb06fd1 100644 --- a/tgbot/routers/user/user_menu.py +++ b/tgbot/routers/user/user_menu.py @@ -1,6 +1,6 @@ # - *- coding: utf- 8 - *- -from aiogram import Router, Bot -from aiogram.filters import Command, Text +from aiogram import Router, Bot, F +from aiogram.filters import Command from aiogram.types import Message, CallbackQuery from tgbot.keyboards.inline_main import menu_finl @@ -12,19 +12,25 @@ router = Router() # Кнопка - User Inline -@router.message(Text(text="User Inline")) +@router.message(F.text == 'User Inline') async def user_button_inline(message: Message, bot: Bot, state: FSM, rSession: RS, my_user): await state.clear() - await message.answer("Click Button - User Inline", reply_markup=user_inl) + await message.answer( + "Click Button - User Inline", + reply_markup=user_inl, + ) # Кнопка - User Reply -@router.message(Text(text="User Reply")) +@router.message(F.text == 'User Reply') async def user_button_reply(message: Message, bot: Bot, state: FSM, rSession: RS, my_user): await state.clear() - await message.answer("Click Button - User Reply", reply_markup=user_rep) + await message.answer( + "Click Button - User Reply", + reply_markup=user_rep, + ) # Команда - /inline @@ -32,17 +38,20 @@ async def user_button_reply(message: Message, bot: Bot, state: FSM, rSession: RS async def user_command_inline(message: Message, bot: Bot, state: FSM, rSession: RS, my_user): await state.clear() - await message.answer("Click command - /inline", reply_markup=menu_finl(message.from_user.id)) + await message.answer( + "Click command - /inline", + reply_markup=menu_finl(message.from_user.id), + ) # Колбэк - User X -@router.callback_query(Text(text="user_inline_x")) +@router.callback_query(F.data == 'user_inline_x') async def user_callback_inline_x(call: CallbackQuery, bot: Bot, state: FSM, rSession: RS, my_user): await call.answer(f"Click User X") # Колбэк - User -@router.callback_query(Text(startswith="user_inline:")) +@router.callback_query(F.data.startswith('user_inline:')) async def user_callback_inline(call: CallbackQuery, bot: Bot, state: FSM, rSession: RS, my_user): get_data = call.data.split(":")[1] diff --git a/tgbot/services/api_sqlite.py b/tgbot/services/api_sqlite.py index edeab13..a9cc806 100644 --- a/tgbot/services/api_sqlite.py +++ b/tgbot/services/api_sqlite.py @@ -2,11 +2,11 @@ import sqlite3 from tgbot.data.config import PATH_DATABASE -from tgbot.utils.const_functions import get_unix, get_date +from tgbot.utils.const_functions import get_unix # Преобразование БД словаря -def dict_factory(cursor, row): +def dict_factory(cursor, row) -> dict: this_dict = {} for idx, col in enumerate(cursor.description): @@ -18,7 +18,7 @@ def dict_factory(cursor, row): #################################################################################################### ##################################### ФОРМАТИРОВАНИЕ ЗАПРОСОВ ###################################### # Форматирование запроса без аргументов -def update_format(sql, parameters: dict): +def update_format(sql, parameters: dict) -> tuple[str, list]: values = ", ".join([ f"{item} = ?" for item in parameters ]) @@ -28,7 +28,7 @@ def update_format(sql, parameters: dict): # Форматирование запроса с аргументами -def update_format_where(sql, parameters: dict): +def update_format_args(sql, parameters: dict) -> tuple[str, list]: sql += " WHERE " sql += " AND ".join([ @@ -44,10 +44,12 @@ def update_format_where(sql, parameters: dict): def add_userx(user_id, user_login, user_name, user_surname, user_fullname): with sqlite3.connect(PATH_DATABASE) as con: con.row_factory = dict_factory - con.execute("INSERT INTO storage_users " - "(user_id, user_login, user_name, user_surname, user_fullname, user_unix) " - "VALUES (?, ?, ?, ?, ?, ?)", - [user_id, user_login, user_name, user_surname, user_fullname, get_unix()]) + con.execute( + "INSERT INTO storage_users " + "(user_id, user_login, user_name, user_surname, user_fullname, user_unix) " + "VALUES (?, ?, ?, ?, ?, ?)", + [user_id, user_login, user_name, user_surname, user_fullname, get_unix()], + ) # Получение пользователя @@ -55,7 +57,7 @@ def get_userx(**kwargs): with sqlite3.connect(PATH_DATABASE) as con: con.row_factory = dict_factory sql = "SELECT * FROM storage_users" - sql, parameters = update_format_where(sql, kwargs) + sql, parameters = update_format_args(sql, kwargs) return con.execute(sql, parameters).fetchone() @@ -64,7 +66,7 @@ def get_usersx(**kwargs): with sqlite3.connect(PATH_DATABASE) as con: con.row_factory = dict_factory sql = "SELECT * FROM storage_users" - sql, parameters = update_format_where(sql, kwargs) + sql, parameters = update_format_args(sql, kwargs) return con.execute(sql, parameters).fetchall() @@ -91,7 +93,7 @@ def delete_userx(**kwargs): with sqlite3.connect(PATH_DATABASE) as con: con.row_factory = dict_factory sql = "DELETE FROM storage_users" - sql, parameters = update_format_where(sql, kwargs) + sql, parameters = update_format_args(sql, kwargs) con.execute(sql, parameters) @@ -105,13 +107,15 @@ def create_dbx(): if len(con.execute("PRAGMA table_info(storage_users)").fetchall()) == 7: print("DB was found(1/1)") else: - con.execute("CREATE TABLE storage_users(" - "increment INTEGER PRIMARY KEY AUTOINCREMENT, " - "user_id INTEGER," - "user_login TEXT," - "user_name TEXT," - "user_surname TEXT," - "user_fullname TEXT," - "user_unix INTEGER" - ")") + con.execute( + "CREATE TABLE storage_users(" + "increment INTEGER PRIMARY KEY AUTOINCREMENT, " + "user_id INTEGER," + "user_login TEXT," + "user_name TEXT," + "user_surname TEXT," + "user_fullname TEXT," + "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 285928b..32b449a 100644 --- a/tgbot/utils/const_functions.py +++ b/tgbot/utils/const_functions.py @@ -6,7 +6,7 @@ from typing import Union import pytz from aiogram import Bot, types -from aiogram.types import InlineKeyboardButton, KeyboardButton, WebAppInfo +from aiogram.types import InlineKeyboardButton, KeyboardButton, WebAppInfo, Message from tgbot.data.config import get_admins, BOT_TIMEZONE @@ -29,6 +29,14 @@ def ikb(text: str, data: str = None, url: str = None, switch: str = None, web: s return InlineKeyboardButton(text=text, web_app=WebAppInfo(url=web)) +# Удаление сообщений с обработкой ошибки от телеграма +async def del_message(message: Message): + try: + await message.delete() + except: + ... + + # Отправка сообщения всем админам async def send_admins(bot: Bot, text: str, markup=None, not_me=0): for admin in get_admins(): @@ -36,7 +44,7 @@ async def send_admins(bot: Bot, text: str, markup=None, not_me=0): if str(admin) != str(not_me): await bot.send_message(admin, text, reply_markup=markup, disable_web_page_preview=True) except: - pass + ... # Умная отправка сообщений diff --git a/tgbot/utils/misc/bot_commands.py b/tgbot/utils/misc/bot_commands.py index a584cbc..5a7ddc3 100644 --- a/tgbot/utils/misc/bot_commands.py +++ b/tgbot/utils/misc/bot_commands.py @@ -6,16 +6,16 @@ from tgbot.data.config import get_admins # Команды для юзеров user_commands = [ - BotCommand(command="start", description="♻ Перезапустить бота"), - BotCommand(command="inline", description="🌀 Получить Inline клавиатуру"), + BotCommand(command="start", description="♻️ Restart bot"), + BotCommand(command="inline", description="🌀 Get Inline keyboard"), ] # Команды для админов admin_commands = [ - BotCommand(command="start", description="♻ Перезапустить бота"), - BotCommand(command="inline", description="🌀 Получить Inline клавиатуру"), - BotCommand(command="log", description="🖨 Получить Логи"), - BotCommand(command="db", description="📦 Получить Базу Данных"), + BotCommand(command="start", description="♻️ Restart bot"), + BotCommand(command="inline", description="🌀 Get Inline keyboard"), + BotCommand(command="log", description="🖨 Get Logs"), + BotCommand(command="db", description="📦 Get Database"), ] diff --git a/tgbot/utils/misc_functions.py b/tgbot/utils/misc_functions.py index 856736c..64bc827 100644 --- a/tgbot/utils/misc_functions.py +++ b/tgbot/utils/misc_functions.py @@ -9,7 +9,7 @@ from tgbot.utils.const_functions import get_date, send_admins # Выполнение функции после запуска бота (рассылка админам о запуске бота) async def startup_notify(bot: Bot): if len(get_admins()) >= 1 and start_status: - await send_admins(bot, "✅ Бот был запущен") + await send_admins(bot, "✅ Bot was started") # Автоматические бэкапы БД