2.0 Да я ебал это все рассписывать но тут типо новая система логгирования

This commit is contained in:
Verum
2025-03-12 06:44:14 +07:00
parent 0704b2600f
commit 142753dc81
48 changed files with 634 additions and 239 deletions

View File

@@ -4,9 +4,6 @@
from aiogram import Router from aiogram import Router
from .routers import router as all_routers from .routers import router as all_routers
from .inline import router as inline_routers from .inline import router as inline_routers
from .inline import *
from .keyboards import *
from .utils import *
# Объявление главного роутера # Объявление главного роутера
router = Router(name="main_router") router = Router(name="main_router")

View File

@@ -0,0 +1,13 @@
# BotCode/easteggs/__init__.py
# Инициализация модуля easteggs, для создания пасхалок
from aiogram import Router
from .holidays import router as holiday_router
# Объявление роутера и настройка экспорта модулей
__all__ = ("router",)
router = Router(name="easteggs_router")
# Список подключаемых роутеров сверху-вниз
router.include_router(holiday_router)

View File

@@ -0,0 +1,21 @@
# BotCode/easteggs/holidays/__init__.py
# Инициализация модуля holidays, для пасхальных поздравлений
# Экспортирование модулей во внешние слои проекта
from aiogram import Router
from .march8 import *
# Объявление роутера и настройка экспорта модулей
__all__ = ("router",)
router = Router(name="holidays_router")
# Список подключаемых роутеров сверху-вниз
router.include_routers(
March8.router,
March8_Finaki.router,
March8_sleshik.router,
March8_polina.router,
March8_finik.router,
March8_kataz.router,
)

View File

@@ -0,0 +1,106 @@
# BotCode/easteggs/holidays/8March.py
# Работа с командой /march8, для вывода поздравления с 8 Марта!
from BotLibrary import CommandHandler
from BotCode.keyboards import get_march8_inline_kb, get_return_march8_inline_kb
# Настройки экспорта в модули
__all__ = ("March8", "March8_Finaki", "March8_finik", "March8_kataz", "March8_sleshik", "March8_polina")
march8_happy_text = ("""🌸 <b>С 8 Марта!</b> 🌸
Вы наши дорогие девушки, мы хотим поздравить вас в честь этого праздника!
Пусть этот день принесёт вам <i>море улыбок</i>, <i>приятные воспоминания</i> и <i>теплые слова</i>! 🌷
Пусть каждый день будет наполнен <b>радостью</b>, <b>счастьем</b> и <b>любовью</b>, а мечты сбываются <b>легко и красиво!</b> 💐
Оставайтесь такими же прекрасными, вдохновляющими и неповторимыми! ✨ С праздником! 💖
Вы и сами знаете, что нужно сделать. Тогда <i>Вперед за Истиной</i>, наши любимые!""")
finaki_text = ("""
Финаки, милая, поздравляем тебя с 8 Марта!🌸 Помни и старайся не забывать, о том, что все таки по настоящему важно.
Ты сильно повзрослела, за то время сколько мы знакомы и я рад видеть, как ты превращаешься из той мелкой балбески-финаки, во взрослую Аню.💪
Надеюсь, что ты все также будешь покорять вершины, а главное не будешь ничего бояться. С 8 Марта Финаки!✨
""")
lostic_text = ("""
Слешик-Лостик, сколько имен, но ты навсегда останешься для нас той самой малышкой, с которой мы прошли через огонь, воду и медные кафешки. 🌸
Желаем тебе только счастья, чтобы с каждым днем ты становилась всё более радостной и яркой. Пусть твои глаза никогда не наполняются слезами, а сердце всегда согревает любовь и счастье. ❤️
Слешик, с 8 Марта! Радуйся и дари радость всем вокруг, милаш! ✨ Ты заслуживаешь только самого лучшего, пусть каждый твой день будет наполнен теплом и светом!
""")
kataz_text = ("""
Катаз? А это кто? 😜
Хахах, мы шутим! Катаз, с 8 Марта! 🌸 Помним тебя такой мелкой, а теперь уже, смотри, мешки с песком совращаешь! 💪 Расти, развивайся, не забывай учиться и, главное, думай, ведь ты всегда была умницей, Катазик! 🤔
Пусть этот день принесет тебе море радости, а впереди будет только светлое будущее. Помни, что именно поэтому ты — Катазик! 🌟
С праздником, кактус! 🌵 С праздником! Пусть каждый день будет полон ярких моментов и вдохновения! 🎉
""")
finik_text = ("""
def main(): print("Ох, кажется, что-то не тому мы это пишем! Финик, малыш, с 8 Марта! 🌸 Многое изменилось, как и ты, но даже так, я безмерно рад, что знаком с таким удивительным нефоренком!🌟)
Расти, познавай мир и следуй за Истиной! То, что мы найдем с тобой - это место, место, в кототором мы впервые услышим твой смех, а не крики от ужастиков😂 Будь умницей,")
мы ведь тобой очень дорожим.❤️ С праздником, малыш!✨
""")
polina_text = ("""
Полина-Полина, с тобой мы знакомы меньше всего, но уже ты стала нам очень дорогим человеком и настоящей подругой. 🌸 Ты как цветок, что появился из пепла — как же он звался? Не важно… Главное, что ты, как этот цветок, продолжаешь расцветать, быть такой же уникальной и прекрасной, как нечто совершенно особенное, созданное из другой материи. 💫
С 8 Марта, балбеска! 😄 Пусть этот день будет полон радости и вдохновения! Мы всегда будем рады выслушать твои истории и, главное, поддержать тебя в любом начинании. Ты заслуживаешь только самого лучшего!
Будь умничкой, Поляк! 🌷
""")
# Создание команды /march8 с несколькими медиа
March8 = CommandHandler(
name="march8",
description="Поздравление с 8 Марта!",
keywords=["march8"],
keyboard=get_march8_inline_kb, delete_msg=True,
media="photo", path_to_media=["ProjectsFiles/media/Banners/march8_banner.jpeg"],
text_msg=march8_happy_text,
)
# Создание команды /march8_finaki
March8_Finaki = CommandHandler(
name="march8_finaki",
description="Поздравление с 8 Марта Финаки!",
keywords=["march8_finaki"],
keyboard=get_return_march8_inline_kb, delete_msg=True,
media="photo", path_to_media=["ProjectsFiles/media/Banners/march8_finaki_banner.jpeg"],
text_msg=finaki_text,
)
# Создание команды /march8_finik
March8_finik = CommandHandler(
name="march8_finik",
description="Поздравление с 8 Марта Финик!",
keywords=["march8_finik"],
keyboard=get_return_march8_inline_kb, delete_msg=True,
media="photo", path_to_media=["ProjectsFiles/media/Banners/march8_finik_banner.jpeg"],
text_msg=finik_text,
)
# Создание команды /march8_polina
March8_polina = CommandHandler(
name="march8_polina",
description="Поздравление с 8 Марта Полина!",
keywords=["march8_polina"],
keyboard=get_return_march8_inline_kb, delete_msg=True,
media="photo", path_to_media=["ProjectsFiles/media/Banners/march8_polina_banner.png"],
text_msg=polina_text,
)
# Создание команды /march8_kataz
March8_kataz = CommandHandler(
name="march8_kataz",
description="Поздравление с 8 Марта Катаз!",
keywords=["march8_kataz"],
keyboard=get_return_march8_inline_kb, delete_msg=True,
media="photo", path_to_media=["ProjectsFiles/media/Banners/march8_kataz_banner.png"],
text_msg=kataz_text,
)
# Создание команды /march8_sleshik
March8_sleshik = CommandHandler(
name="march8_sleshik",
description="Поздравление с 8 Марта Слешик!",
keywords=["march8_sleshik"],
keyboard=get_return_march8_inline_kb, delete_msg=True,
media="photo", path_to_media=["ProjectsFiles/media/Banners/march8_lostik_banner.png"],
text_msg=lostic_text,
)

View File

@@ -4,3 +4,4 @@
# Экспортирование модулей во внешние слои проекта # Экспортирование модулей во внешние слои проекта
from .start_inline_kb import get_start_kb from .start_inline_kb import get_start_kb
from .my_inline_kb import get_my_inline_kb from .my_inline_kb import get_my_inline_kb
from .march8_inline_kb import get_march8_inline_kb, get_return_march8_inline_kb

View File

@@ -0,0 +1,26 @@
# BotCode/keyboards/inline_kb/8march_inline_kb.py
# Создание инлайн-клавиатуры на команду: /march8
from BotLibrary import BaseInlineKeyboard
# Настройка экспорта в модули
__all__ = ("get_march8_inline_kb", "get_return_march8_inline_kb")
# Функция создания клавиатуры
def get_march8_inline_kb(row_width : int = 2):
buttons = [
("🍓Финаки", None, "march8_finaki"),
("🍬Финик", None, "march8_finik"),
("💋Поля", None, "march8_polina"),
("😈Катазик", None, "march8_kataz"),
("🪭Слешик", None, "march8_sleshik"),
]
return BaseInlineKeyboard(buttons, row_width=row_width).get_keyboard()
# Функция возвратной клавиатуры
def get_return_march8_inline_kb(row_width : int = 1):
buttons = [
("🥰Назад", None, "march8"),
]
return BaseInlineKeyboard(buttons, row_width=row_width).get_keyboard()

View File

@@ -15,7 +15,6 @@ all_cmd = CommandHandler(
name="all", name="all",
description="Всеобщий призыв", description="Всеобщий призыв",
keywords=["all", "фдд", "@all"], keywords=["all", "фдд", "@all"],
callbackdata=["keywords"],
media="command", media="command",
func=[lambda message, *args: hidden_admins_message(message, msg=False, text=message.text.split(" ", 1)[1] if len(message.text.split(" ", 1)) > 1 else custom_text)], func=[lambda message, *args: hidden_admins_message(message, msg=False, text=message.text.split(" ", 1)[1] if len(message.text.split(" ", 1)) > 1 else custom_text)],
) )

View File

@@ -43,6 +43,5 @@ ban_cmd = CommandHandler(
name="ban", name="ban",
description="Блокировка пользователя", description="Блокировка пользователя",
keywords=["ban", "бан", "banhammer", "ифтрфььук", "ифт"], keywords=["ban", "бан", "banhammer", "ифтрфььук", "ифт"],
callbackdata=["keywords"],
media="command", func=[ban_user], media="command", func=[ban_user],
) )

View File

@@ -5,6 +5,7 @@
from aiogram import Router from aiogram import Router
from .polina_anketa import polina_za_tri_eleksira_cmd from .polina_anketa import polina_za_tri_eleksira_cmd
from .kataz_pidaraz_2020 import kataz_pidaraz_2020_cmd from .kataz_pidaraz_2020 import kataz_pidaraz_2020_cmd
from .finaki_succub import finaki_succub_cmd
# Объявление роутера и настройка экспорта модулей # Объявление роутера и настройка экспорта модулей
__all__ = ("router",) __all__ = ("router",)
@@ -15,4 +16,5 @@ router = Router(name="easteggs_cmd_router")
router.include_routers( router.include_routers(
polina_za_tri_eleksira_cmd.router, polina_za_tri_eleksira_cmd.router,
kataz_pidaraz_2020_cmd.router, kataz_pidaraz_2020_cmd.router,
finaki_succub_cmd.router,
) )

View File

@@ -0,0 +1,43 @@
# BotCode/routers/commands/easteggs_cmd/finaki_succub.py
# Работа с командой /finaki_succub, для вывода анкеты
from BotLibrary import CommandHandler
from BotCode.keyboards import get_my_inline_kb
# Настройки экспорта в модули
__all__ = ("finaki_succub_cmd",)
# Шаблон анкеты
shablon_anketa = ("""📜 \\| **Статистика персонажа**
👤 **Пользователь:** [Е Лань: Цветок Орхидеи](https://t.me/fin_aki)
🏅 **Ранг:** Участник
🌀 **Раса:** Суккуб
📊 **Активность \\\\\\\|всего\):**
🗨 **День:** 69 \\| **Неделя:** 69 \\| **Месяц:** 69 \\| **Всего:** 69
🏠 **Группа:** Мемори\-IX \\| Заместитель
🧭 **Состояние персонажа**
❤️ **Здоровье:** ▰▰▰▰▰▰▰▱▱▱ \(70%\)
🍖 **Голод:** ▰▰▰▰▰▰▰▰▱▱ \(80%\)
🧠 **Рассудок:** ▰▰▰▰▰▰▱▱▱▱ \(60%\)
📌 **Важные события**
🏠 **Бункер:** Создание нового бункера в северных окрестностях города
🕵 **Разведка:** Нашла временную базу культистов "Мертвой души"
🎒 **Поиски:** Обнаружила картину Художника "Чужой \- муза культистов"
""")
# Создание команды /my с несколькими медиа
finaki_succub_cmd = CommandHandler(
name="finaki_succub",
description="Получение личной анкеты Финаки",
keywords=["finaki_succub"],
keyboard=get_my_inline_kb,
media="photo", path_to_media=["ProjectsFiles/media/Banners/finaki_my.jpeg"],
text_msg=shablon_anketa,
parse_mode="MarkdownV2",
)

View File

@@ -34,7 +34,7 @@ kataz_pidaraz_2020_cmd = CommandHandler(
name="kataz_pidaraz_2020", name="kataz_pidaraz_2020",
description="Получение личной анкеты Катаза", description="Получение личной анкеты Катаза",
keywords=["kataz_pidaraz_2020"], keywords=["kataz_pidaraz_2020"],
keyboard=get_my_inline_kb, callbackdata=["keywords"], keyboard=get_my_inline_kb,
media="photo", path_to_media=["ProjectsFiles/media/Anketa/kataz_easteggs.jpeg"], media="photo", path_to_media=["ProjectsFiles/media/Anketa/kataz_easteggs.jpeg"],
text_msg=shablon_anketa, text_msg=shablon_anketa,
parse_mode="MarkdownV2", parse_mode="MarkdownV2",

View File

@@ -33,7 +33,7 @@ polina_za_tri_eleksira_cmd = CommandHandler(
name="polina_za_tri_eleksira", name="polina_za_tri_eleksira",
description="Получение личной анкеты Поляка", description="Получение личной анкеты Поляка",
keywords=["polina_za_tri_eleksira"], keywords=["polina_za_tri_eleksira"],
keyboard=get_my_inline_kb, callbackdata=["keywords"], keyboard=get_my_inline_kb,
media="photo", path_to_media=["ProjectsFiles/media/Anketa/polina_easteggs_anketa.jpeg"], media="photo", path_to_media=["ProjectsFiles/media/Anketa/polina_easteggs_anketa.jpeg"],
text_msg=shablon_anketa, text_msg=shablon_anketa,
parse_mode="MarkdownV2", parse_mode="MarkdownV2",

View File

@@ -12,6 +12,6 @@ help_cmd = CommandHandler(
name="help", name="help",
description="Получить помощь", description="Получить помощь",
keywords=["help", "info", "помощь", "инфо", "информация", "рудз", "штащ", "byaj", "gjvjom", "byajhvfwbz"], keywords=["help", "info", "помощь", "инфо", "информация", "рудз", "штащ", "byaj", "gjvjom", "byajhvfwbz"],
keyboard=get_help_kb, callbackdata=["keywords"], keyboard=get_help_kb,
text_msg="Привет! Это команда помощи. Тут ты можешь узнать, как пользоваться ботом.", text_msg="Привет! Это команда помощи. Тут ты можешь узнать, как пользоваться ботом.",
) )

View File

@@ -8,7 +8,7 @@ from BotCode.keyboards import get_my_inline_kb
__all__ = ("my_cmd",) __all__ = ("my_cmd",)
# Шаблон анкеты # Шаблон анкеты
shablon_anketa = """ shablon_anketa = ("""
📜 \\| **Статистика персонажа** 📜 \\| **Статистика персонажа**
👤 **Пользователь:** [Альбедо](http://t.me/verdise) 👤 **Пользователь:** [Альбедо](http://t.me/verdise)
@@ -27,15 +27,14 @@ shablon_anketa = """
📌 **Важные события** 📌 **Важные события**
🕵 **Разведка:** Обнаружена Лаборатория X\-18 🕵 **Разведка:** Обнаружена Лаборатория X\-18
💀 **Смерть:** Удушение 💀 **Смерть:** Удушение
""")
"""
# Создание команды /my с несколькими медиа # Создание команды /my с несколькими медиа
my_cmd = CommandHandler( my_cmd = CommandHandler(
name="my", name="my",
description="Получение личной анкеты", description="Получение личной анкеты",
keywords=["my", "ьн", "me", "ьу"], keywords=["my", "ьн", "me", "ьу"],
keyboard=get_my_inline_kb, callbackdata=["keywords"], keyboard=get_my_inline_kb,
media="photo", path_to_media=["ProjectsFiles/media/Anketa/albedo_anketa.png"], media="photo", path_to_media=["ProjectsFiles/media/Anketa/albedo_anketa.png"],
text_msg=shablon_anketa, text_msg=shablon_anketa,
parse_mode="MarkdownV2", parse_mode="MarkdownV2",

View File

@@ -12,7 +12,7 @@ start_cmd = CommandHandler(
name="start", name="start",
description="Добро пожаловать!", description="Добро пожаловать!",
keywords=["start", "старт", "cnfhn", "ыефке", "пуск", "gecr", "on"], keywords=["start", "старт", "cnfhn", "ыефке", "пуск", "gecr", "on"],
keyboard=get_start_kb, callbackdata=["keywords"], keyboard=get_start_kb,
media="photo", path_to_media=["ProjectsFiles/media/Banners/start_banner.jpg",], media="photo", path_to_media=["ProjectsFiles/media/Banners/start_banner.jpg",],
text_msg="Привет! Вот группа фото!", text_msg="Привет! Вот группа фото!",
) )

View File

@@ -12,7 +12,6 @@ start_time_cmd = CommandHandler(
keywords=["start_time", "stime", "старт_время", "время_старта", "сремя", keywords=["start_time", "stime", "старт_время", "время_старта", "сремя",
"ыефке_ешьу", "ыешьу", "cnfhn_dhtvcz", "dhtvz_cnfhnf", "c_dhtvz", "ыефке_ешьу", "ыешьу", "cnfhn_dhtvcz", "dhtvz_cnfhnf", "c_dhtvz",
"бот_время", "время_запуска", "бот_врем", "on_time", "щт_ешьу"], "бот_время", "время_запуска", "бот_врем", "on_time", "щт_ешьу"],
callbackdata=["keywords"],
text_msg=lambda: f"Бот @{BotInfo.username} запущен: " text_msg=lambda: f"Бот @{BotInfo.username} запущен: "
f"\nХост: <b>{dp['started_at']}</b> " f"\nХост: <b>{dp['started_at']}</b> "
f"\nМСК: <b>{dp['started_at_msk']}</b>", f"\nМСК: <b>{dp['started_at_msk']}</b>",

View File

@@ -31,6 +31,5 @@ stats_cmd = CommandHandler(
name="stats", name="stats",
description="Вывод статистики о пользователи", description="Вывод статистики о пользователи",
keywords=["stats", "ыефеы", "cnfnf", "стата", "Кто я", "Rnj z", "vjbcjj,otybz", "моисообщения"], keywords=["stats", "ыефеы", "cnfnf", "стата", "Кто я", "Rnj z", "vjbcjj,otybz", "моисообщения"],
callbackdata=["keywords"],
media="command", func=[send_stats], media="command", func=[send_stats],
) )

View File

@@ -10,7 +10,6 @@ weather_cmd = CommandHandler(
name="weather", name="weather",
description="Погода", description="Погода",
keywords=["weather", "gjujlf", "цуферук", "погода"], keywords=["weather", "gjujlf", "цуферук", "погода"],
callbackdata=["keywords"],
media="command", media="command",
func=[get_weather], func=[get_weather],
) )

View File

@@ -17,4 +17,4 @@ router = Router(name="common_msg_router")
async def handle_all_messages(message: types.Message): async def handle_all_messages(message: types.Message):
await base_sql(message) await base_sql(message)
await status_user(message) await status_user(message)
await logger_msg(message) # Это твой метод для логирования Logs.msg(message)

View File

@@ -4,7 +4,38 @@
# Экспортирование модулей во внешние слои проекта # Экспортирование модулей во внешние слои проекта
from .analytics import * from .analytics import *
from .loggers import * from .loggers import *
from .samples import *
from .system import * from .system import *
from .timer import * from .timer import *
from .validators import * from .validators import *
from .samples import *
from SQLite3 import create_user_db
from ProjectsFiles import Permissions
# Функция установки
async def setup():
# Запуск логеров
await setup_logger()
# Получение информации о боте
await bot_get_info()
# Вывод сообщение о запуске
Logs.start(text=f"Начало запуска бота @{BotInfo.username}...")
Logs.console()
# Автоматическое создание базы данных при отсутствии
await create_user_db()
# Создание пустых директорий
await setup_directories()
# Нужно ли удалить веб-хук
if Permissions.delete_webhook:
await bot.delete_webhook()
await set_adm_rights()
await set_bot_name()
await set_bot_description()
await set_bot_short_description()

View File

@@ -1,10 +1,13 @@
# BotLibrary/analytics/type_msg.py
# Проверяет тип сообщения
from aiogram.types import ContentType, Message from aiogram.types import ContentType, Message
# Настройка экспорта из модуля # Настройка экспорта из модуля
__all__ = ("types_message",) __all__ = ("type_msg",)
# Функция определения типа сообщения # Функция определения типа сообщения
def types_message(message: Message) -> str: def type_msg(message: Message) -> str:
""" """
Функция для определения типа сообщения на основе его содержимого. Функция для определения типа сообщения на основе его содержимого.
@@ -68,7 +71,7 @@ def types_message(message: Message) -> str:
ContentType.GROUP_CHAT_CREATED: "Создание группового чата", ContentType.GROUP_CHAT_CREATED: "Создание группового чата",
ContentType.SUPERGROUP_CHAT_CREATED: "Создание супергруппы", ContentType.SUPERGROUP_CHAT_CREATED: "Создание супергруппы",
ContentType.CHANNEL_CHAT_CREATED: "Создание канала", ContentType.CHANNEL_CHAT_CREATED: "Создание канала",
ContentType.MESSAGE_AUTO_DELETE_TIMER_CHANGED: "Изменение таймера автоудаления сообщения", ContentType.MESSAGE_AUTO_DELETE_TIMER_CHANGED: "Изменение таймера авто-удаления сообщения",
} }
# Получение типа сообщения # Получение типа сообщения

View File

@@ -1,8 +1,6 @@
# BotLibrary/loggers/__init__.py # BotLibrary/loggers/__init__.py
# Инициализация пакета loggers, для создания логеров # Инициализация под-пакета loggers, для настройки логеров
# Экспортирование модулей во внешние слои проекта # Экспортирование модулей во внешние слои проекта
from .logs import * from .logs import *
from .msg_logger import *
from .custom_loggers import * from .custom_loggers import *
from .start_info_out import *

View File

@@ -1,10 +1,17 @@
# BotLibrary/loggers/custom_loggers.py # BotLibrary/loggers/custom_loggers.py
# Кастомные логгеры для проекта, с более стандартизированным использованием # Кастомные логгеры для проекта, с более стандартизированным использованием
from time import sleep
from colorama import Fore
from loguru import logger from loguru import logger
from ..validators import username
from aiogram.types import Message from aiogram.types import Message
from BotLibrary.system.bots import BotInfo
from BotLibrary.validators.username import username
from BotLibrary.analytics.type_msg import type_msg
from ProjectsFiles import BotLogs, Permissions, ProjectPath, BotVar, bot_owner
# Настройка экспорта из модуля # Настройка экспорта из модуля
__all__ = ("Logs",) __all__ = ("Logs",)
@@ -13,8 +20,10 @@ class Logs:
"""Класс для логирования с разными уровнями через loguru.""" """Класс для логирования с разными уровнями через loguru."""
@staticmethod @staticmethod
def start(text: str = "Логирование!", system: str = "PRIMO", def start(text: str = "Логирование!",
log_type: str = "AEP", user: str = "@Console") -> None: system: str = "PRIMO",
log_type: str = "AEP",
user: str = "@Console") -> None:
""" """
Логирует сообщение на уровне DEBUG. Логирует сообщение на уровне DEBUG.
@@ -22,12 +31,18 @@ class Logs:
:param text: Сообщение для логирования. :param text: Сообщение для логирования.
:param log_type: Тип лога (например, "Logs"). :param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога. :param user: Имя пользователя или источник вызова лога.
:return: Вывод сообщения об старте бота
""" """
logger.bind(system=system, user=user, log_type=log_type).log("START", text) logger.bind(system=system, user=user, log_type=log_type).log("START", text)
@staticmethod @staticmethod
def debug(text: str = "Логирование!", system : str = "DEBUG", def debug(text: str = "Логирование!",
log_type: str = "Logs", user: str = "@Console", message: Message = None) -> None: system: str = "DEBUG",
log_type: str = "Logs",
user: str = "@Console",
message: Message = None) -> None:
""" """
Логирует сообщение на уровне DEBUG. Логирует сообщение на уровне DEBUG.
@@ -36,14 +51,20 @@ class Logs:
:param log_type: Тип лога (например, "Logs"). :param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога. :param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя. :param message: Сообщение от пользователя, если необходимо извлечь имя.
:return: Вывод сообщения об дебаг-информации
""" """
if message: if message:
user = username(message) user = username(message)
logger.bind(system=system, log_type=log_type, user=user).debug(text) logger.bind(system=system, log_type=log_type, user=user).debug(text)
@staticmethod @staticmethod
def info(text: str = "Логирование!", system : str = "PRIMO", def info(text: str = "Логирование!",
log_type: str = "Logs", user: str = "@Console", message: Message = None) -> None: system: str = "PRIMO",
log_type: str = "Logs",
user: str = "@Console",
message: Message = None) -> None:
""" """
Логирует сообщение на уровне INFO. Логирует сообщение на уровне INFO.
@@ -52,14 +73,20 @@ class Logs:
:param log_type: Тип лога (например, "Logs"). :param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога. :param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя. :param message: Сообщение от пользователя, если необходимо извлечь имя.
:return: Вывод сообщения об некой информации
""" """
if message: if message:
user = username(message) user = username(message)
logger.bind(system=system, log_type=log_type, user=user).info(text) logger.bind(system=system, log_type=log_type, user=user).info(text)
@staticmethod @staticmethod
def warning(text: str = "Логирование!", system : str = "WARNING", def warning(text: str = "Логирование!",
log_type: str = "Logs", user: str = "@Console", message: Message = None) -> None: system: str = "WARNING",
log_type: str = "Logs",
user: str = "@Console",
message: Message = None) -> None:
""" """
Логирует сообщение на уровне WARNING. Логирует сообщение на уровне WARNING.
@@ -68,14 +95,20 @@ class Logs:
:param log_type: Тип лога (например, "Logs"). :param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога. :param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя. :param message: Сообщение от пользователя, если необходимо извлечь имя.
:return: Вывод сообщения об предупреждении
""" """
if message: if message:
user = username(message) user = username(message)
logger.bind(system=system, log_type=log_type, user=user).warning(text) logger.bind(system=system, log_type=log_type, user=user).warning(text)
@staticmethod @staticmethod
def error(text: str = "Логирование!", system : str = "ERROR", def error(text: str = "Логирование!",
log_type: str = "Logs", user: str = "@Console", message: Message = None) -> None: system: str = "ERROR",
log_type: str = "Logs",
user: str = "@Console",
message: Message = None) -> None:
""" """
Логирует сообщение на уровне ERROR. Логирует сообщение на уровне ERROR.
@@ -84,7 +117,80 @@ class Logs:
:param log_type: Тип лога (например, "Logs"). :param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога. :param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя. :param message: Сообщение от пользователя, если необходимо извлечь имя.
:return: Вывод сообщения об ошибке
""" """
if message: if message:
user = username(message) user = username(message)
logger.bind(system=system, log_type=log_type, user=user).error(text) logger.bind(system=system, log_type=log_type, user=user).error(text)
@staticmethod
def msg(message: Message,
log_type: str = "Message",
permission: bool = BotLogs.permission) -> None:
"""
Логирует сообщение, если оно не обработано.
:param message: Сообщение от пользователя
:param log_type: Тип лога (по умолчанию "Message")
:param permission: Разрешение на логирование (config)
:return: Вывод сообщения об обычном сообщении пользователя
"""
# Получаем username или id пользователя
user: str = f"@{message.from_user.username or message.from_user.id}"
msg_type: str = type_msg(message)
# Логирование только если разрешено
if permission:
# Проверка на наличие текста и его типа
if message.text is None and msg_type not in ("Новые участники чата", "Ушедший участник чата"):
Logs.info(log_type=log_type, user=user, text=f"Получено сообщение из ({message.chat.id}) : {msg_type}")
elif message.text is not None:
Logs.info(log_type=log_type, user=user,
text=f"Получено сообщение из ({message.chat.id}) : {message.text}")
@staticmethod
def console(stop_time: int = 1,
console: bool = Permissions.start_info_console,
file: bool = Permissions.start_info_to_file,
path: str = ProjectPath.bot_info_log_file,) -> None:
"""
Собирает информацию о боте и выводит её в консоль, а также возвращает как строку.
:param stop_time: Количество времени в секундах, после которых выведется информация (1 сек)
:param console: Разрешение на внесение информации в консоль (config)
:param file: Разрешение на внесение информации в файл (config)
:param path: Путь до файла для сохранения информации о боте (config)
:return: Информация о боте в виде строки.
"""
# Собираем данные о боте
bot_name: str = f"Основное имя: {BotInfo.first_name}\n"
bot_post_name: str = f"Владельцы бота: {bot_owner}\n"
bot_username: str = f"Юзернейм: @{BotInfo.username}\n"
bot_id: str = f"ID: {BotInfo.id}\n"
bot_can_join_groups: str = f"Может ли вступать в группы: {BotInfo.can_join_groups}\n"
bot_can_read_all_group_messages: str = f"Чтение всех сообщений: {BotInfo.can_read_all_group_messages}\n"
bot_supports_inline_queries: str = f"Поддерживает инлайн-запросы: {BotInfo.supports_inline_queries}\n"
bot_can_connect_to_business: str = f"Подключение к бизнес-аккаунтам: {BotInfo.can_connect_to_business}\n"
bot_has_main_web_app: str = f"Основное веб-приложение: {BotInfo.has_main_web_app}\n"
# Формируем полный текст с выводом информации о боте
bot_all_info: str = (f"{bot_name} {bot_post_name} {bot_username} {bot_id} "
f"{bot_can_join_groups} {bot_can_read_all_group_messages} "
f"{bot_supports_inline_queries} {bot_can_connect_to_business} "
f"{bot_has_main_web_app}")
# Печатаем всю информацию в консоль с задержкой
if console:
sleep(stop_time)
print(Fore.CYAN + bot_all_info)
# Печатаем всю информацию в файл
if file:
# Преобразуем словарь bot_all_info в строку и записываем в файл
with open(path, 'w', encoding=BotVar.encod) as file:
file.write(str(bot_all_info))

View File

@@ -1,33 +1,45 @@
# BotLibrary/system/logs.py # BotLibrary/loggers/logs.py
# Создание логгеров и их шаблон для проекта # Создание логгеров и их шаблон для проекта
import sys import sys
from loguru import logger from loguru import logger
from ProjectsFiles import BotLogs, ProjectPath from ProjectsFiles import BotLogs, ProjectPath
# Настройка экспорта из модуля
__all__ = ("setup_logger",)
# Создание обычного логгера + логгер в файл # Создание обычного логгера + логгер в файл
async def setup_logger() -> None: async def setup_logger(logging: bool = BotLogs.permission,
to_file: bool = BotLogs.permission_to_file) -> None:
""" """
Настройка логгеров для проекта, выводящих логи в консоль. Настройка логгеров для проекта, выводящих логи в консоль и файлы.
Логгеры конфигурируются в зависимости от настроек в BotLogs. Логгеры конфигурируются в зависимости от настроек в конфигах проекта.
Если разрешено логирование, добавляются логи для уровней DEBUG, INFO, WARNING, ERROR. Если разрешено логирование, добавляются логи для уровней DEBUG, INFO, WARNING, ERROR.
""" И кастомные такие, как START, NEW_USER, LEAVE_USER
logger.remove() # Удаляем все логгеры
if BotLogs.permission and BotLogs.permission_to_file: :param logging: Разрешение на логирование в консоль (config)
:param to_file: Разрешение на логирование в файл (config)
:return: Создание логеров под различные уровни
"""
logger.remove() # Удаляем все стандартные логгеры
# Если есть разрешение, то он создает новые уровни
if logging and BotLogs.permission_to_file:
# Добавляем новый уровень START # Добавляем новый уровень START
logger.level("START", no=25, color="white", icon="🔸") logger.level("START", no=25, color="white", icon="🔸")
if BotLogs.permission and BotLogs.permission_new_user: if logging and BotLogs.permission_new_user:
# Добавляем новый уровень NEW_USER # Добавляем новый уровень NEW_USER
logger.level("NEW_USER", no=4, color="white", icon="👋") logger.level("NEW_USER", no=4, color="white", icon="👋")
if BotLogs.permission and BotLogs.permission_leave_user: if logging and BotLogs.permission_leave_user:
# Добавляем новый уровень LEAVE_USER # Добавляем новый уровень LEAVE_USER
logger.level("LEAVE_USER", no=3, color="white", icon="🫰") logger.level("LEAVE_USER", no=3, color="white", icon="🫰")
# Настройка логирования в консоль для каждого уровня # Настройка логирования в консоль для каждого уровня
if BotLogs.permission: if logging:
logger.add(sys.stderr, logger.add(sys.stderr,
colorize=True, colorize=True,
format=BotLogs.start_text, format=BotLogs.start_text,
@@ -55,8 +67,9 @@ async def setup_logger() -> None:
level="ERROR", level="ERROR",
filter=lambda record: record["level"].name == "ERROR") filter=lambda record: record["level"].name == "ERROR")
# Добавление логгера для записи в файл # Добавление логгера для записи в файл
if BotLogs.permission_to_file: if to_file:
logger.add(ProjectPath.start_log_file, logger.add(ProjectPath.start_log_file,
rotation=BotLogs.max_size, rotation=BotLogs.max_size,
format=BotLogs.start_text, format=BotLogs.start_text,

View File

@@ -1,32 +0,0 @@
# BotLibrary/loggers/msg_logger.py
# Логгер для всех не обработанных сообщений
from ProjectsFiles import BotLogs
from .custom_loggers import Logs
from ..analytics.type_msg import types_message
from aiogram.types import Message
# Настройка экспорта из модуля
__all__ = ("logger_msg",)
# Создание функции логирования на обычные сообщения
async def logger_msg(message: Message, log_type: str = "Message") -> None:
"""
Логирует сообщение, если оно не обработано.
:param message: Сообщение от пользователя.
:param log_type: Тип лога (по умолчанию "Message").
"""
# Получаем username или id пользователя
user: str = f"@{message.from_user.username or message.from_user.id}"
msg_type = types_message(message)
# Логирование только если разрешено
if BotLogs.permission:
# Проверка на наличие текста и его типа
if message.text is None and msg_type not in ("Новые участники чата", "Ушедший участник чата"):
Logs.info(log_type=log_type, user=user, text=f"Получено сообщение из ({message.chat.id}) : {msg_type}")
elif message.text is not None:
Logs.info(log_type=log_type, user=user, text=f"Получено сообщение из ({message.chat.id}) : {message.text}")
else:
return

View File

@@ -1,48 +0,0 @@
# BotLibrary/loggers/start_info_out.py
# Вывод данных бота в консоль для начальной проверки
from time import sleep
from colorama import Fore
from ProjectsFiles import Permissions, ProjectPath, BotVar, bot_owner
from .custom_loggers import Logs
from ..system import BotInfo
# Функция для получения информации о боте и выводе ее в консоль и файл
def bot_info_out() -> str:
"""
Собирает информацию о боте и выводит её в консоль, а также возвращает как строку.
:return: Информация о боте в виде строки.
"""
try:
# Собираем данные о боте
bot_name: str = f"Основное имя: {BotInfo.first_name}\n"
bot_post_name: str = f"Владельцы бота: {bot_owner}\n"
bot_username: str = f"Юзернейм: @{BotInfo.username}\n"
bot_id: str = f"ID: {BotInfo.id}\n"
bot_can_join_groups: str = f"Может ли вступать в группы: {BotInfo.can_join_groups}\n"
bot_can_read_all_group_messages: str = f"Чтение всех сообщений: {BotInfo.can_read_all_group_messages}\n"
bot_supports_inline_queries: str = f"Поддерживает инлайн-запросы: {BotInfo.supports_inline_queries}\n"
bot_can_connect_to_business: str = f"Подключение к бизнес-аккаунтам: {BotInfo.can_connect_to_business}\n"
bot_has_main_web_app: str = f"Основное веб-приложение: {BotInfo.has_main_web_app}\n"
# Формируем полный текст с выводом информации о боте
bot_all_info: str = (f"{bot_name} {bot_post_name} {bot_username} {bot_id} "
f"{bot_can_join_groups} {bot_can_read_all_group_messages} "
f"{bot_supports_inline_queries} {bot_can_connect_to_business} "
f"{bot_has_main_web_app}")
# Печатаем все данные в консоль с задержкой в 1 секунду
sleep(1)
if Permissions.start_info_console:
print(Fore.CYAN + bot_all_info)
if Permissions.start_info_to_file:
# Преобразуем словарь bot_all_info в строку и записываем в файл
with open(ProjectPath.bot_info_log_file, 'w', encoding=BotVar.encod) as file:
file.write(str(bot_all_info))
return bot_all_info
except Exception as e:
Logs.error(log_type="SYS", user="Start_INFO", text=f"Ошибка при получении ID пользователя: {e}")

View File

@@ -1,3 +1,6 @@
# BotLibrary/samples/inline_kb_sample.py
# Шаблон для создания инлайн клавиатур
from aiogram.types import InlineKeyboardMarkup, ReplyKeyboardRemove from aiogram.types import InlineKeyboardMarkup, ReplyKeyboardRemove
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from typing import List, Tuple, Optional from typing import List, Tuple, Optional

View File

@@ -1,5 +1,5 @@
# BotCode/keyboards/reply_kb/base_reply_kb.py # BotCode/keyboards/reply_kb/base_reply_kb.py
# Базовый класс для создания reply-клавиатур с расширенными возможностями и поддержкой row_width # Базовый класс для создания reply-клавиатур
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, KeyboardButtonPollType, WebAppInfo, KeyboardButtonRequestUsers, KeyboardButtonRequestChat, KeyboardButtonRequestUser from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, KeyboardButtonPollType, WebAppInfo, KeyboardButtonRequestUsers, KeyboardButtonRequestChat, KeyboardButtonRequestUser
from aiogram.utils.keyboard import ReplyKeyboardBuilder from aiogram.utils.keyboard import ReplyKeyboardBuilder

View File

@@ -8,7 +8,8 @@ from aiogram.filters import Command
from aiogram.types import InputMediaPhoto, InputMediaVideo, InputMediaDocument from aiogram.types import InputMediaPhoto, InputMediaVideo, InputMediaDocument
from typing import Optional, Callable from typing import Optional, Callable
from BotLibrary import Logs, valid_url, username from ..loggers import Logs
from ..validators import username, valid_url
from ProjectsFiles import BotVar from ProjectsFiles import BotVar
from SQLite3 import base_sql from SQLite3 import base_sql
@@ -19,7 +20,7 @@ class CommandHandler:
def __init__(self, name: str, keywords: list, func: Optional[list[Callable]] = None, text_msg=None, chat_action: bool = False, def __init__(self, name: str, keywords: list, func: Optional[list[Callable]] = None, text_msg=None, chat_action: bool = False,
description: str = "Описание команды", tg_links: bool = False, description: str = "Описание команды", tg_links: bool = False,
keyboard=None, prefix=BotVar.prefix, callbackdata: list = None, only_admin: bool = False, keyboard=None, prefix=BotVar.prefix, callbackdata: list = None, only_admin: bool = False,
ignore_case: bool = True, activate_keywoards: bool = True, ignore_case: bool = True, activate_keywords: bool = True, delete_msg: bool = False,
activate_commands: bool = True, activate_callback: bool = True, activate_commands: bool = True, activate_callback: bool = True,
media: str = "message", path_to_media=None, parse_mode: str = BotVar.parse_mode, media: str = "message", path_to_media=None, parse_mode: str = BotVar.parse_mode,
disable_notification: bool = BotVar.disable_notification, protect: bool = BotVar.protect_content): disable_notification: bool = BotVar.disable_notification, protect: bool = BotVar.protect_content):
@@ -27,6 +28,7 @@ class CommandHandler:
self.name = name self.name = name
self.log_type = name.capitalize() self.log_type = name.capitalize()
self.description = description self.description = description
self.last_bot_message = {} # {chat_id: message_id}
self.keywords = keywords self.keywords = keywords
self.text_msg = text_msg self.text_msg = text_msg
@@ -39,6 +41,7 @@ class CommandHandler:
self.protect = protect self.protect = protect
self.only_admin = only_admin self.only_admin = only_admin
self.func = func self.func = func
self.delete_msg = delete_msg
# Поддержка до 10 медиафайлов через список # Поддержка до 10 медиафайлов через список
if path_to_media is None: if path_to_media is None:
@@ -49,25 +52,30 @@ class CommandHandler:
self.path_to_media = path_to_media[:10] # Ограничение до 10 элементов self.path_to_media = path_to_media[:10] # Ограничение до 10 элементов
self.tg_links = tg_links self.tg_links = tg_links
if callbackdata == "keywords": if callbackdata:
self.callbackdata = keywords
else:
self.callbackdata = callbackdata self.callbackdata = callbackdata
else:
self.callbackdata = keywords
# Привязываем хэндлер к роутеру # Привязываем хэндлер к роутеру
if activate_commands: if activate_commands:
self.router.message(Command(*keywords, prefix=prefix, ignore_case=ignore_case))(self.handler) self.router.message(Command(*keywords, prefix=prefix, ignore_case=ignore_case))(self.handler)
if activate_keywoards: if activate_keywords:
self.router.message(F.text.lower().in_(keywords))(self.handler) self.router.message(F.text.lower().in_(keywords))(self.handler)
if activate_callback and self.callbackdata: if activate_callback and self.callbackdata:
self.router.message(F.text.lower().in_(self.callbackdata))(self.handler) self.router.callback_query(F.data.in_(self.callbackdata))(self.callback_handler)
async def callback_handler(self, callback: types.CallbackQuery):
"""Обработчик callback-запросов."""
await self.handler(callback.message) # Передаем сообщение в основном обработчике
await callback.answer() # Закрываем callback-запрос
async def handler(self, message: types.Message): async def handler(self, message: types.Message):
"""Основной хэндлер команды.""" """Основной хэндлер команды."""
try: try:
# Извлекаем текст после команды # Извлекаем текст после команды
command_text = message.text[len(message.text.split()[0]):].strip() # Убираем команду из текста command_text = (message.text or "").strip() # Защита от NoneType
args = command_text.split() # Разделяем команду на аргументы args = command_text.split() if command_text else [] # Если текст есть — разделяем, иначе пустой список
# Проверка на выполнение дополнительной функции (если она есть) # Проверка на выполнение дополнительной функции (если она есть)
if self.func: # Проверяем, что функция не None if self.func: # Проверяем, что функция не None
@@ -115,14 +123,16 @@ class CommandHandler:
if self.tg_links: if self.tg_links:
text = text.replace("<users>", str(message.from_user.id)) text = text.replace("<users>", str(message.from_user.id))
sent_msg = None
if self.media == "message": if self.media == "message":
await message.reply( sent_msg = await message.reply(
text=text, text=text,
reply_markup=self.keyboard() if self.keyboard else None, reply_markup=self.keyboard() if self.keyboard else None,
parse_mode=self.parse_mode, parse_mode=self.parse_mode,
disable_notification=self.disable_notification, disable_notification=self.disable_notification,
protect_content=self.protect, protect_content=self.protect,
) )
self.last_bot_message[message.chat.id] = sent_msg.message_id
if self.chat_action: if self.chat_action:
await message.bot.send_chat_action( await message.bot.send_chat_action(
chat_id=message.chat.id, chat_id=message.chat.id,
@@ -142,7 +152,7 @@ class CommandHandler:
media_group[-1].caption = text media_group[-1].caption = text
media_group[-1].parse_mode = self.parse_mode media_group[-1].parse_mode = self.parse_mode
await message.reply_media_group( sent_msg = await message.reply_media_group(
media=media_group, media=media_group,
disable_notification=self.disable_notification, disable_notification=self.disable_notification,
protect_content=self.protect, protect_content=self.protect,
@@ -173,7 +183,7 @@ class CommandHandler:
media_group[-1].caption = text media_group[-1].caption = text
media_group[-1].parse_mode = self.parse_mode media_group[-1].parse_mode = self.parse_mode
await message.reply_media_group( sent_msg = await message.reply_media_group(
media=media_group, media=media_group,
disable_notification=self.disable_notification, disable_notification=self.disable_notification,
protect_content=self.protect, protect_content=self.protect,
@@ -204,7 +214,7 @@ class CommandHandler:
media_group[-1].caption = text media_group[-1].caption = text
media_group[-1].parse_mode = self.parse_mode media_group[-1].parse_mode = self.parse_mode
await message.reply_media_group( sent_msg = await message.reply_media_group(
media=media_group, media=media_group,
disable_notification=self.disable_notification, disable_notification=self.disable_notification,
protect_content=self.protect, protect_content=self.protect,
@@ -229,7 +239,7 @@ class CommandHandler:
if self.media == "photo": if self.media == "photo":
if url: if url:
await message.reply_photo( sent_msg = await message.reply_photo(
photo=media_path, photo=media_path,
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -238,7 +248,7 @@ class CommandHandler:
protect_content=self.protect, protect_content=self.protect,
) )
else: else:
await message.reply_photo( sent_msg = await message.reply_photo(
photo=types.FSInputFile(path=media_path), photo=types.FSInputFile(path=media_path),
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -254,7 +264,7 @@ class CommandHandler:
elif self.media == "gif": elif self.media == "gif":
if url: if url:
await message.reply_animation( sent_msg = await message.reply_animation(
animation=media_path, animation=media_path,
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -263,7 +273,7 @@ class CommandHandler:
protect_content=self.protect, protect_content=self.protect,
) )
else: else:
await message.reply_animation( sent_msg = await message.reply_animation(
animation=types.FSInputFile(path=media_path), animation=types.FSInputFile(path=media_path),
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -279,7 +289,7 @@ class CommandHandler:
elif self.media == "video": elif self.media == "video":
if url: if url:
await message.reply_video( sent_msg = await message.reply_video(
video=media_path, video=media_path,
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -288,7 +298,7 @@ class CommandHandler:
protect_content=self.protect, protect_content=self.protect,
) )
else: else:
await message.reply_video( sent_msg = await message.reply_video(
video=types.FSInputFile(path=media_path), video=types.FSInputFile(path=media_path),
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -304,7 +314,7 @@ class CommandHandler:
elif self.media == "videonote": elif self.media == "videonote":
if url: if url:
await message.reply_video_note( sent_msg = await message.reply_video_note(
video_note=media_path, video_note=media_path,
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -313,7 +323,7 @@ class CommandHandler:
protect_content=self.protect, protect_content=self.protect,
) )
else: else:
await message.reply_video_note( sent_msg = await message.reply_video_note(
video_note=types.FSInputFile(path=media_path), video_note=types.FSInputFile(path=media_path),
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -329,7 +339,7 @@ class CommandHandler:
elif self.media == "audio": elif self.media == "audio":
if url: if url:
await message.reply_audio( sent_msg = await message.reply_audio(
audio=media_path, audio=media_path,
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -338,7 +348,7 @@ class CommandHandler:
protect_content=self.protect, protect_content=self.protect,
) )
else: else:
await message.reply_audio( sent_msg = await message.reply_audio(
audio=types.FSInputFile(path=media_path), audio=types.FSInputFile(path=media_path),
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -354,7 +364,7 @@ class CommandHandler:
elif self.media == "file": elif self.media == "file":
if url: if url:
await message.reply_document( sent_msg = await message.reply_document(
document=media_path, document=media_path,
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -363,7 +373,7 @@ class CommandHandler:
protect_content=self.protect, protect_content=self.protect,
) )
else: else:
await message.reply_document( sent_msg = await message.reply_document(
document=types.FSInputFile(path=media_path), document=types.FSInputFile(path=media_path),
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -378,7 +388,7 @@ class CommandHandler:
) )
elif self.media == "dice": elif self.media == "dice":
await message.reply_dice( sent_msg = await message.reply_dice(
emoji="🎲", # Эмодзи кубика как стандартное значение, если нет URL emoji="🎲", # Эмодзи кубика как стандартное значение, если нет URL
caption=text if is_last else None, caption=text if is_last else None,
reply_markup=self.keyboard() if is_last and self.keyboard else None, reply_markup=self.keyboard() if is_last and self.keyboard else None,
@@ -392,6 +402,16 @@ class CommandHandler:
action=ChatAction.CHOOSE_STICKER, action=ChatAction.CHOOSE_STICKER,
) )
# Сохраняем идентификатор последнего сообщения, если необходимо
if sent_msg:
self.last_bot_message[message.chat.id] = sent_msg.message_id
if self.delete_msg:
await message.bot.delete_message(
chat_id=message.chat.id,
message_id=message.message_id
)
# Проверка на ошибку # Проверка на ошибку
except Exception as e: except Exception as e:
Logs.error(log_type=self.log_type, user=username(message), text=f"Ошибка команды: {e}") Logs.error(log_type=self.log_type, user=username(message), text=f"Ошибка команды: {e}")

View File

@@ -1,33 +1,22 @@
# BotLibrary/system/edit_bot.py # BotLibrary/system/bot_edit.py
# Библиотека установки настроек бота через проект и конфиги # Под-пакет установки настроек бота
from aiogram.types import ChatAdministratorRights from aiogram.types import ChatAdministratorRights
from ProjectsFiles import BotEdit from ProjectsFiles import BotEdit
from .bots import bot from .bots import bot
from ..loggers import Logs
# Настройка логирования # Настройка логирования
log_type = "Edit" log_type = "Edit"
# Функция для выполнения всех настроек, если они не совпадают # Настройка экспорта из модуля
async def set_all() -> None: __all__ = ("set_adm_rights", "set_bot_name", "set_bot_description", "set_bot_short_description")
"""
Выполняет все необходимые настройки бота, если они не совпадают с текущими значениями.
:return: None
"""
await set_adm_rights()
await set_bot_name()
await set_bot_description()
await set_bot_short_description()
# Функция установки прав администратора # Функция установки прав администратора
async def set_adm_rights() -> None: async def set_adm_rights() -> None:
""" """
Устанавливает права администратора для бота, если они отличаются от текущих. Устанавливает права администратора для бота, если они отличаются от текущих.
:return: None :return: Изменение прав администратора
""" """
rights = ChatAdministratorRights( rights = ChatAdministratorRights(
is_anonymous=BotEdit.is_anonymous, is_anonymous=BotEdit.is_anonymous,
@@ -54,57 +43,72 @@ async def set_adm_rights() -> None:
# Функция установки имени бота с проверкой на ограничения # Функция установки имени бота с проверкой на ограничения
async def set_bot_name() -> None: async def set_bot_name(new_name: str = BotEdit.name) -> None:
""" """
Устанавливает имя бота, если оно отличается от текущего и соответствует ограничениям. Устанавливает имя бота, если оно отличается от текущего и соответствует ограничениям.
:return: None :param new_name: Новое имя бота (config)
:return: Имя бота
""" """
# Импортируем Logs внутри функции, чтобы избежать циклического импорта
from ..loggers.custom_loggers import Logs
# Получаем текущее имя бота # Получаем текущее имя бота
current_name = (await bot.get_me()).first_name current_name = (await bot.get_me()).first_name
# Проверка длины имени # Проверка длины имени
if len(BotEdit.name) < 1 or len(BotEdit.name) > 32: if len(new_name) < 1 or len(new_name) > 32:
Logs.error(log_type=log_type, user="NAME_BOT", text="Имя бота должно быть от 1 до 32 символов.") Logs.error(log_type=log_type, user="NAME_BOT", text="Имя бота должно быть от 1 до 32 символов.")
return # Выходим из функции, если имя некорректно
# Проверяем, совпадает ли текущее имя с тем, которое мы хотим установить # Проверяем, совпадает ли текущее имя с тем, которое мы хотим установить
if current_name != BotEdit.name: if current_name != new_name:
await bot.set_my_name(BotEdit.name) await bot.set_my_name(new_name)
# Функция установки описания бота с проверкой на ограничения # Функция установки описания бота с проверкой на ограничения
async def set_bot_description() -> None: async def set_bot_description(new_description: str = BotEdit.description) -> None:
""" """
Устанавливает описание бота, если оно отличается от текущего и соответствует ограничениям. Устанавливает описание бота, если оно отличается от текущего и соответствует ограничениям.
:return: None :param new_description: Новое описание для бота (config)
:return: Описание бота
""" """
# Импортируем Logs внутри функции, чтобы избежать циклического импорта
from ..loggers.custom_loggers import Logs
# Получаем текущее описание бота # Получаем текущее описание бота
current_description = await bot.get_my_description() current_description = await bot.get_my_description()
# Проверка длины описания # Проверка длины описания
if len(BotEdit.description) > 255: if len(new_description) > 255 or len(new_description)==0:
Logs.error(log_type=log_type, user="DISCRIPT", text="Короткое описание бота не может превышать 255 символов.") Logs.error(log_type=log_type, user="DISCRIPT", text="Короткое описание бота не может превышать 255 символов или быть равно 0.")
return # Выходим из функции, если описание некорректно
# Проверяем, совпадает ли текущее описание с тем, которое мы хотим установить # Проверяем, совпадает ли текущее описание с тем, которое мы хотим установить
if current_description != BotEdit.description: if current_description != new_description:
await bot.set_my_description(description=BotEdit.description) await bot.set_my_description(description=new_description)
# Функция установки короткого описания бота с проверкой на ограничения # Функция установки короткого описания бота с проверкой на ограничения
async def set_bot_short_description() -> None: async def set_bot_short_description(new_short_description: str = BotEdit.short_description) -> None:
""" """
Устанавливает короткое описание бота, если оно отличается от текущего и соответствует ограничениям. Устанавливает короткое описание бота, если оно отличается от текущего и соответствует ограничениям.
:return: None :param new_short_description: Новое описание виджета для бота (config)
:return: Короткое описание бота
""" """
# Импортируем Logs внутри функции, чтобы избежать циклического импорта
from ..loggers.custom_loggers import Logs
# Получаем текущее короткое описание бота # Получаем текущее короткое описание бота
current_short_description = await bot.get_my_short_description() current_short_description = await bot.get_my_short_description()
# Проверка длины короткого описания # Проверка длины короткого описания
if len(BotEdit.short_description) > 512: if len(new_short_description) > 512 or len(new_short_description) == 0:
Logs.error(log_type=log_type, user="SHORT_DISCRIPT", text="Описание виджета не может превышать 512 символов.") Logs.error(log_type=log_type, user="SHORT_DISCRIPT", text="Описание виджета не может превышать 512 символов или быть равно 0.")
return # Выходим из функции, если короткое описание некорректно
# Проверяем, совпадает ли текущее короткое описание с тем, которое мы хотим установить # Проверяем, совпадает ли текущее короткое описание с тем, которое мы хотим установить
if current_short_description != BotEdit.short_description: if current_short_description != new_short_description:
await bot.set_my_short_description(short_description=BotEdit.short_description) await bot.set_my_short_description(short_description=new_short_description)

View File

@@ -5,7 +5,7 @@ from aiogram import Dispatcher, Bot, F
from aiogram.client.default import DefaultBotProperties from aiogram.client.default import DefaultBotProperties
from aiogram.utils.keyboard import InlineKeyboardBuilder, ReplyKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder, ReplyKeyboardBuilder
from ..timer import get_host_time, get_moscow_time from ..timer import get_host_time, get_city_time
from ProjectsFiles import bot_token, BotVar from ProjectsFiles import bot_token, BotVar
@@ -16,7 +16,7 @@ ikb = InlineKeyboardBuilder()
# Настройка параметров диспатчера # Настройка параметров диспатчера
dp = Dispatcher() dp = Dispatcher()
dp["started_at"] = get_host_time() dp["started_at"] = get_host_time()
dp["started_at_msk"] = get_moscow_time() dp["started_at_msk"] = get_city_time()
dp["is_active"] = True # Флаг активности бота dp["is_active"] = True # Флаг активности бота
dp["logs"] = [] dp["logs"] = []
dp["users"] = {} dp["users"] = {}

View File

@@ -7,31 +7,33 @@ from tzlocal import get_localzone
from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.schedulers.asyncio import AsyncIOScheduler
from ProjectsFiles import BotVar from ProjectsFiles import BotVar
# Настройка экспорта из этого модуля
__all__ = ("scheduler", "get_city_time", "get_host_time")
# Создание планировщика для работы с задачами по времени # Создание планировщика для работы с задачами по времени
scheduler = AsyncIOScheduler(timezone=get_localzone().key) scheduler = AsyncIOScheduler(timezone=get_localzone().key)
def get_moscow_time() -> str: # Функция получение иного времени
def get_city_time(city: str = 'Europe/Moscow') -> str:
""" """
Получение текущего времени по московскому времени. Получение текущего времени по иному городскому времени.
:param city: Город, что будет вторым временем
:return: Строка, представляющая время в формате, заданном в BotVar.time_format. :return: Строка, представляющая время в формате, заданном в BotVar.time_format.
""" """
# Устанавливаем временную зону для Москвы # Устанавливаем временную зону для Москвы
moscow_tz = pytz.timezone('Europe/Moscow') city_tz = pytz.timezone(city)
# Получаем текущее время по московскому времени
moscow_time = datetime.now(moscow_tz)
# Возвращаем строку с форматом времени # Возвращаем строку с форматом времени
return moscow_time.strftime(BotVar.time_format) return datetime.now(city_tz).strftime(BotVar.time_format)
# Функция получение времени хоста
def get_host_time() -> str: def get_host_time() -> str:
""" """
Получение текущего времени хоста (локального времени). Получение текущего времени хоста (локального времени).
:return: Строка, представляющая локальное время в формате, заданном в BotVar.time_format. :return: Строка, представляющая локальное время в формате, заданном в BotVar.time_format.
""" """
# Получаем текущее время на хосте
host_time = datetime.now()
# Возвращаем строку с форматом времени # Возвращаем строку с форматом времени
return host_time.strftime(BotVar.time_format) return datetime.now().strftime(BotVar.time_format)

View File

@@ -1,24 +1,25 @@
# BotLibrary/validators/email_validators.py # BotLibrary/validators/email_validators.py
# Создание валидации почты для проекта # Создание валидации почты для проекта
from email_validator import validate_email, EmailNotValidError
from typing import Optional from typing import Optional
from email_validator import validate_email, EmailNotValidError
# Настройка экспорта из этого модуля # Настройка экспорта из этого модуля
__all__ = ("valid_email",) __all__ = ("valid_email",)
# Функция проверки почты на корректность
def valid_email(text: str) -> Optional[str]:
"""
Проверяет корректность почтового адреса.
:param text: Почтовый адрес в виде строки. # Функция проверки почты на корректность
def valid_email(email: str) -> Optional[str]:
"""
Делает почтовый адрес корректным.
:param email: Почтовый адрес в виде строки.
:return: Нормализованный почтовый адрес, если он валиден, иначе None. :return: Нормализованный почтовый адрес, если он валиден, иначе None.
""" """
try: try:
# Проверка и нормализация email return validate_email(email).normalized
email = validate_email(text) except EmailNotValidError as e:
return email.normalized # Импортируем Logs внутри функции, чтобы избежать циклического импорта
except EmailNotValidError: from ..loggers.custom_loggers import Logs
# Если email невалиден, можно добавить логирование или обработку ошибок Logs.error(text=f"Ошибка в нормализировании почты: {e}", log_type="NormalEmail")
return None return None

View File

@@ -1,5 +1,20 @@
# BotLibrary/validators/normal_word.py # BotLibrary/validators/normal_word.py
# Нормализирует вид слова автоматически # Нормализирует вид слова автоматически
async def normal_words(word : str = "Тестовое слово") -> str: # Настройка экспорта из этого модуля
__all__ = ("normal_words",)
async def normal_words(word: str) -> str:
"""
Делает слово корректного вида.
:param word: Слово, которое будет приводиться к виду (Тесты).
:return: Нормализованное слово
"""
try:
return word.lower().capitalize() return word.lower().capitalize()
except Exception as e:
# Импортируем Logs внутри функции, чтобы избежать циклического импорта
from ..loggers.custom_loggers import Logs
Logs.error(text=f"Ошибка в нормализировании слова: {e}", log_type="NormalWord")
return word

View File

@@ -25,5 +25,27 @@ def valid_url(url: str) -> bool:
# Функция, что дает тексту ссылку на HTML # Функция, что дает тексту ссылку на HTML
def url_to_text(text: str = "Тест", url: str = "www.google.com") -> str: def url_to_text(text: str, url: str) -> str:
"""
Преобразует текст в HTML ссылку с указанным URL.
Эта функция генерирует HTML-ссылку с переданным текстом и URL, используя тег `<а>`, и делает ссылку жирной.
:param text: Текст, который будет отображаться для ссылки.
:param url: URL, который будет привязан к тексту.
:return: Строка с HTML кодом для ссылки, если URL валиден.
:raises ValueError: Если URL невалиден.
"""
try:
if not valid_url(url): # Проверяем, является ли URL валидным
raise ValueError(f"Переданный URL '{url}' невалиден.")
# Генерация HTML-ссылки
return f'<b><a href="{url}">{text}</a></b>' return f'<b><a href="{url}">{text}</a></b>'
except ValueError as e:
# Импортируем Logs внутри функции, чтобы избежать циклического импорта
from ..loggers.custom_loggers import Logs
# Логируем ошибку с использованием Logs.error, как указано
Logs.error(text=f"Ошибка при создании ссылки: {e}", log_type="InvalidURL")
raise e # Перебрасываем ошибку выше для дальнейшей обработки или уведомления

View File

@@ -6,6 +6,7 @@ from aiogram.types import Message
# Настройка экспорта из модуля # Настройка экспорта из модуля
__all__ = ("username", "username_to_text") __all__ = ("username", "username_to_text")
# Функция получения юзера или ID пользователя # Функция получения юзера или ID пользователя
def username(message: Message) -> str: def username(message: Message) -> str:
""" """
@@ -13,12 +14,32 @@ def username(message: Message) -> str:
:param message: Объект сообщения из aiogram. :param message: Объект сообщения из aiogram.
:return: Строка с юзернеймом пользователя или его ID. :return: Строка с юзернеймом пользователя или его ID.
:raises ValueError: Если в сообщении отсутствует информация о пользователе.
""" """
try:
if message.from_user: if message.from_user:
return f"@{message.from_user.username}" if message.from_user.username else f"@{message.from_user.id}" return f"@{message.from_user.username}" if message.from_user.username else f"@{message.from_user.id}"
return "@Unknown_User" # Если from_user отсутствует raise ValueError("Информация о пользователе отсутствует в сообщении.")
except ValueError as e:
# Логируем ошибку с использованием Logs.error
raise e # Перебрасываем ошибку выше для дальнейшей обработки
# Функция получение имени пользователя + ссылка на него # Функция получение имени пользователя + ссылка на него
def username_to_text(message: Message) -> str: def username_to_text(message: Message) -> str:
"""
Преобразует информацию о пользователе в строку с HTML-ссылкой.
:param message: Объект сообщения из aiogram.
:return: Строка с HTML-кодом для ссылки на пользователя.
:raises ValueError: Если в сообщении отсутствует информация о пользователе.
"""
try:
if message.from_user:
return f'<b><a href="tg://user?id={message.from_user.id}">{message.from_user.full_name}</a></b>' return f'<b><a href="tg://user?id={message.from_user.id}">{message.from_user.full_name}</a></b>'
raise ValueError("Информация о пользователе отсутствует в сообщении.")
except ValueError as e:
# Логируем ошибку с использованием Logs.error
raise e # Перебрасываем ошибку выше для дальнейшей обработки

49
Documentation/docs.md Normal file
View File

@@ -0,0 +1,49 @@
Начнем пожалуй с BotLibrary, я думаю после преобразовать его в пакет для ботов
Первый модуль analytics V
type_chat.py - определяет тип чата type_chat(message):
-Личный
-Группы
-Канал
type_msg.py - определяет тип сообщения type_msg(message):
-Текст
-Фото
-Стикер
-...
Второй модуль loggers:
logs.py - создает логгеры, три кастомных уровня и 4 обычных. Он может создавать
-NEW_USER
-LEAVE_USER
-START показывает
Последний модуль validators:
email_valid.py - содержит проверку на почту, для будущих добавлений [valid_email(email)]
normal_word.py - проводит к виду "Видообразование" [normal_words]
url_valid.py -

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

View File

@@ -2,7 +2,7 @@
# Инициализация модуля bd_func, для функция с БД # Инициализация модуля bd_func, для функция с БД
from aiogram import types from aiogram import types
from BotLibrary import username from BotLibrary.validators import username
from ProjectsFiles import Permissions from ProjectsFiles import Permissions
from .bd_add_user import * from .bd_add_user import *

View File

@@ -5,7 +5,7 @@ import sqlite3
from aiogram import types from aiogram import types
from datetime import timezone, datetime, timedelta from datetime import timezone, datetime, timedelta
from BotLibrary import types_message from BotLibrary import type_msg
from ProjectsFiles import BotVar from ProjectsFiles import BotVar
# Настройка экспорта в модули # Настройка экспорта в модули
@@ -31,7 +31,7 @@ async def update_user_messages(message: types.Message, bd_name: str = BotVar.bd_
current_month = now.month current_month = now.month
current_year = now.year current_year = now.year
last_message = message.text or types_message(message) last_message = message.text or type_msg(message)
last_message_id = message.message_id last_message_id = message.message_id
if result: if result:

20
main.py
View File

@@ -4,34 +4,18 @@
import asyncio import asyncio
from BotLibrary import * from BotLibrary import *
from BotCode import router as main_router from BotCode import router as main_router
from SQLite3 import create_user_db
# Запуск основного кода # Запуск основного кода
async def main(): async def main():
# Функция создания логеров и получения информации о боте # Функция установки
await setup_logger() await setup()
await bot_get_info()
Logs.start(text=f"Начало запуска бота @{BotInfo.username}...")
bot_info_out()
# Автоматическое создание базы данных при отсутствии
await create_user_db()
# Создание пустых директорий
await setup_directories()
# Подключение главного маршрутизатора # Подключение главного маршрутизатора
dp.include_router(main_router) dp.include_router(main_router)
# Нужно ли удалить веб-хук
if Permissions.delete_webhook:
await bot.delete_webhook()
# Включение опроса бота # Включение опроса бота
await dp.start_polling(bot) await dp.start_polling(bot)
# Вечная загрузка бота # Вечная загрузка бота
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(main()) asyncio.run(main())