3.0 Выпуск в PrimoRU

This commit is contained in:
Verum
2025-04-13 06:50:23 +07:00
parent b8f7ce5b2a
commit 17d10fbf78
51 changed files with 1191 additions and 1611 deletions

1
.idea/PRIMOWORLD.iml generated
View File

@@ -23,6 +23,7 @@
<sourceFolder url="file://$MODULE_DIR$/BotCode/routers/commands" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/BotCode/routers/commands" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/BotCode/routers/common" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/BotCode/routers/common" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/BotCode/utils" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/BotCode/utils" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/BotLibrary/sql" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.venv" /> <excludeFolder url="file://$MODULE_DIR$/.venv" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.13 (PRIMOWORLD)" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3.13 (PRIMOWORLD)" jdkType="Python SDK" />

View File

@@ -1,8 +1,9 @@
# BotCode/inline/reklama.py # BotCode/inline/reklama.py
# Работа с инлайн запросами на рекламу # Работа с инлайн запросами на рекламу
from aiogram import Router, types from aiogram import Router
from aiogram.types import InlineQueryResultPhoto from aiogram.types import (InlineQueryResultPhoto, InlineQuery, CallbackQuery,
InlineKeyboardMarkup, InlineKeyboardButton)
from BotLibrary import bot from BotLibrary import bot
# Настройка экспорта в модули # Настройка экспорта в модули
@@ -14,13 +15,13 @@ f"""Это сообщение с изображением и инлайн кно
@router.callback_query(lambda c: c.data == 'button_1') @router.callback_query(lambda c: c.data == 'button_1')
async def process_callback_button(callback_query: types.CallbackQuery) -> None: async def process_callback_button(callback_query: CallbackQuery) -> None:
await bot.answer_callback_query(callback_query.id, text="Вы нажали первую кнопку!") await bot.answer_callback_query(callback_query.id, text="Вы нажали первую кнопку!")
await bot.send_message(callback_query.from_user.id, "Ответ на вашу кнопку.") await bot.send_message(callback_query.from_user.id, "Ответ на вашу кнопку.")
@router.inline_query() @router.inline_query()
async def inline_echo(inline_query: types.InlineQuery) -> None: async def inline_echo(inline_query: InlineQuery) -> None:
# Содержимое запроса # Содержимое запроса
query = inline_query.query query = inline_query.query
@@ -36,10 +37,10 @@ async def inline_echo(inline_query: types.InlineQuery) -> None:
photo_url=image_url, # URL изображения photo_url=image_url, # URL изображения
thumbnail_url=image_url, # Миниатюра изображения thumbnail_url=image_url, # Миниатюра изображения
caption=text_msg, # Текст, который будет показываться под изображением caption=text_msg, # Текст, который будет показываться под изображением
reply_markup=types.InlineKeyboardMarkup( reply_markup=InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[types.InlineKeyboardButton(text="Посмотреть инфо-канал", url="https://t.me/adeptusfiziks")], [InlineKeyboardButton(text="Посмотреть инфо-канал", url="https://t.me/adeptusfiziks")],
[types.InlineKeyboardButton(text="Вторая кнопка", callback_data="button_1")], [InlineKeyboardButton(text="Вторая кнопка", callback_data="button_1")],
] ]
) )
) )

View File

@@ -2,15 +2,14 @@
# Работа с командой /stats, для получения информации о себе # Работа с командой /stats, для получения информации о себе
from aiogram import types from aiogram import types
from BotLibrary import CommandHandler, bot from BotLibrary import CommandHandler, bot, db
from SQLite3 import status_user
# Настройки экспорта в модули # Настройки экспорта в модули
__all__ = ("ban_cmd",) __all__ = ("ban_cmd",)
# Функция блокировки пользователя # Функция блокировки пользователя
async def ban_user(message: types.Message, *args, **kwargs) -> None: async def ban_user(message: types.Message, *args, **kwargs) -> None:
status = await status_user(message) status = db.get_user_status(message)
if status not in ('Пользователь', 'Забаннен'): if status not in ('Пользователь', 'Забаннен'):
# Проверка, что команда вызвана с упоминанием пользователя # Проверка, что команда вызвана с упоминанием пользователя
args = message.text.split() args = message.text.split()

View File

@@ -1,5 +1,5 @@
# BotCode/routers/commands/user_cmd/start_time_cmd.py # BotCode/routers/commands/user_cmd/start_time_cmd.py
# # Команда на выдачу погоды определенного города
from BotLibrary import CommandHandler from BotLibrary import CommandHandler
from BotCode.utils import get_weather from BotCode.utils import get_weather

View File

@@ -3,7 +3,6 @@
from aiogram import Router, types from aiogram import Router, types
from BotLibrary import * from BotLibrary import *
from SQLite3 import base_sql, status_user
# Настройка экспорта модулей и роутера # Настройка экспорта модулей и роутера
__all__ = ("router",) __all__ = ("router",)
@@ -12,6 +11,6 @@ router = Router(name="common_msg_router")
# Обработчик всех сообщений # Обработчик всех сообщений
@router.message() @router.message()
async def all_messages(message: types.Message) -> None: async def all_messages(message: types.Message) -> None:
await base_sql(message) db.update_user(message)
await status_user(message) db.update_user_messages(message)
Logs.msg(message) Logs.msg(message)

View File

@@ -1,22 +1,34 @@
# BotCode/utils/admin_lists.py # BotCode/utils/admin_lists.py
# Составления листа администраторов # Составления листа администраторов
from aiogram.types import Message
from BotLibrary import bot from BotLibrary import bot
from ProjectsFiles import BotVar
# Настройки экспорта в модули # Настройки экспорта в модули
__all__ = ("admin_lists",) __all__ = ("admin_lists",)
async def admin_lists(chat_id: int = None, message: Message = None) -> str:
"""
Функция составления словаря администраторов.
# Функция составления словаря администраторов :param message: Объект сообщения от пользователя.
async def admin_lists(chat_id: int) -> str: :param chat_id: ID-чата в котором будет проводиться работа.
:return: Строка с юзерами администрации.
"""
chat_id = chat_id if isinstance(chat_id, int) else message.chat.id
admins = await bot.get_chat_administrators(chat_id) admins = await bot.get_chat_administrators(chat_id)
# Формируем список упоминаний администраторов # Формируем список упоминаний администраторов
admin_mentions = [] admin_mentions = []
for admin in admins: for admin in admins:
if admin.user.is_bot: if admin.user.is_bot:
continue continue
admin_mentions.append( if BotVar.parse_mode == "HTML":
admin_mentions.append(
f"@{admin.user.username}" if admin.user.username else f"<a href=\"tg://user?id={admin.user.id}\">{admin.user.full_name}</a>") f"@{admin.user.username}" if admin.user.username else f"<a href=\"tg://user?id={admin.user.id}\">{admin.user.full_name}</a>")
elif BotVar.parse_mode == "MarkdownV2":
admin_mentions.append(
f"@{admin.user.username}" if admin.user.username else f"[{admin.user.full_name}](tg://user?id={admin.user.id})")
admins_text = ", ".join(admin_mentions) if admin_mentions else "Нет администраторов" admins_text = ", ".join(admin_mentions) if admin_mentions else "Нет администраторов"
return admins_text return admins_text

View File

@@ -8,18 +8,28 @@ from BotLibrary import bot
# Настройки экспорта в модули # Настройки экспорта в модули
__all__ = ("hidden_admins_message",) __all__ = ("hidden_admins_message",)
# Функция составления словаря администраторов
async def hidden_admins_message(message: types.Message = None, async def hidden_admins_message(message: types.Message = None,
chat_id: int = None, chat_id: int = None,
text: str = "", text: str = "",
msg: bool = True, *args) -> str | None: msg: bool = True, *args) -> str | None:
"""
Формирует скрытые ссылки на администраторов чата в Markdown-разметке.
:param message: Объект сообщения от пользователя (если chat_id не указан, ID чата берется из него).
:param chat_id: ID чата, в котором нужно получить список администраторов (если не указан, берется из message).
:param text: Дополнительный текст, который будет добавлен к результату.
:param msg: Определяет, возвращать ли результат (True) или отправлять его в чат (False).
:param args: Дополнительные аргументы (не используются, оставлены для совместимости с шаблоном).
:return: Строка со скрытыми ссылками на администраторов и добавленным текстом (если msg=True), иначе None.
"""
chat_id = chat_id if isinstance(chat_id, int) else message.chat.id chat_id = chat_id if isinstance(chat_id, int) else message.chat.id
admins = await bot.get_chat_administrators(chat_id) admins = await bot.get_chat_administrators(chat_id)
hidden_links = "".join( hidden_links = "".join(
markdown.hide_link(f"tg://user?id={admin.user.id}") markdown.hide_link(f"tg://user?id={admin.user.id}")
for admin in admins if not admin.user.is_bot for admin in admins if not admin.user.is_bot
) )
result = f"{hidden_links}{text}" result = f"{hidden_links}{text}"
if msg: if msg:
return result return result

View File

@@ -1,10 +1,17 @@
import aiohttp import aiohttp
from aiogram.types import Message
from ProjectsFiles import weather_api_key from ProjectsFiles import weather_api_key
# Настройки экспорта в модули # Настройки экспорта в модули
__all__ = ("get_weather",) __all__ = ("get_weather",)
async def get_weather(message, *args) -> str: async def get_weather(message: Message, *args) -> str:
"""
Обрабатывает запрос о погоде для указанного города и возвращает информацию о текущей погоде.
:param message: Объект сообщения от пользователя.
:return: Возвращает ответ о погоде в указанном городе.
"""
# Извлекаем город из сообщения # Извлекаем город из сообщения
command_parts = message.text.split(maxsplit=1) command_parts = message.text.split(maxsplit=1)
print(command_parts[1]) print(command_parts[1])
@@ -33,10 +40,10 @@ async def get_weather(message, *args) -> str:
wind = data["wind"]["speed"] wind = data["wind"]["speed"]
weather_today: str = (f"Погода <b>{city}</b>\n" weather_today: str = (f"Погода <b>{city}</b>\n"
f"☁️Погода: <b>{weather}</b>\n" f"☁️Погода: <b>{weather}</b>\n"
f"🌡Температура: <b>{temp}°C</b>\n" f"🌡Температура: <b>{temp}°C</b>\n"
f"💧Влажность: <b>{humidity}%</b>\n" f"💧Влажность: <b>{humidity}%</b>\n"
f"💨Скорость ветра: <b>{wind} м/с</b>") f"💨Скорость ветра: <b>{wind} м/с</b>")
await message.answer(weather_today) await message.answer(weather_today)
return weather_today return weather_today
except Exception as e: except Exception as e:

View File

@@ -5,37 +5,7 @@
from .analytics import * from .analytics import *
from .loggers import * from .loggers import *
from .samples import * from .samples import *
from .sql import *
from .system import * from .system import *
from .timer import * from .timer import *
from .validators import * from .validators 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,25 +1,23 @@
# BotLibrary/analytics/type_chat.py # BotLibrary/analytics/type_chat.py
# Определение типа чата # Определение типа чата
from aiogram import types from aiogram.types import Message
# Настройка экспорта в модули # Настройка экспорта в модули
__all__ = ("type_chat",) __all__ = ("type_chat",)
# Проверка на тип чата async def type_chat(message: Message) -> str:
async def type_chat(message: types.Message) -> str:
""" """
Преобразует информацию о чате в понятные значения. Преобразует информацию о чате в его тип на русском языке.
:param message: Объект сообщения из aiogram. :param message: Объект сообщения из aiogram, содержащий информацию о чате.
:return: Тип чата строкой. :return: Тип чата строкой.
""" """
chat_type: str = message.chat.type chat_types: dict[str, str] = {
if chat_type == "private": "private": "Личный",
return "Личный" "group": "Группа",
elif chat_type == "group" or chat_type == "supergroup": "supergroup": "Группа",
return "Группа" "channel": "Канал",
elif chat_type == "channel": }
return "Канал"
else: return chat_types.get(message.chat.type, "Неизвестный тип чата")
return "Неизвестный тип чата."

View File

@@ -6,7 +6,6 @@ from aiogram.types import ContentType, Message
# Настройка экспорта из модуля # Настройка экспорта из модуля
__all__ = ("type_msg",) __all__ = ("type_msg",)
# Функция определения типа сообщения
def type_msg(message: Message) -> str: def type_msg(message: Message) -> str:
""" """
Функция для определения типа сообщения на основе его содержимого. Функция для определения типа сообщения на основе его содержимого.
@@ -50,7 +49,7 @@ def type_msg(message: Message) -> str:
ContentType.INVOICE: "Счет", ContentType.INVOICE: "Счет",
ContentType.SUCCESSFUL_PAYMENT: "Успешный платеж", ContentType.SUCCESSFUL_PAYMENT: "Успешный платеж",
ContentType.REFUNDED_PAYMENT: "Возврат платежа", ContentType.REFUNDED_PAYMENT: "Возврат платежа",
ContentType.USERS_SHARED: "Пользователи поделились", ContentType.USERS_SHARED: "Пользователь поделился",
ContentType.CHAT_SHARED: "Чат был передан", ContentType.CHAT_SHARED: "Чат был передан",
ContentType.CONNECTED_WEBSITE: "Подключенный веб-сайт", ContentType.CONNECTED_WEBSITE: "Подключенный веб-сайт",
ContentType.WRITE_ACCESS_ALLOWED: "Разрешение на запись", ContentType.WRITE_ACCESS_ALLOWED: "Разрешение на запись",
@@ -77,9 +76,20 @@ def type_msg(message: Message) -> str:
# Получение типа сообщения # Получение типа сообщения
message_type: str = message.content_type message_type: str = message.content_type
# Если это контакт, добавляем номер телефона # Если это контакт, добавляем номер телефона
if message_type == ContentType.CONTACT and message.contact: if message_type == ContentType.CONTACT and message.contact:
return f"{content_types.get(message_type, 'Контакт')}: {message.contact.phone_number}" return f"{content_types.get(message_type, 'Контакт')}: {message.contact.phone_number}"
# Если это пользователи, добавляем их ID
if message_type == ContentType.USERS_SHARED and message.users_shared:
user_ids = ", ".join(map(str, message.users_shared.user_ids))
return f"{content_types.get(message_type, 'Пользователь поделился')}: {user_ids}"
# Если это переданный чат, добавляем его ID
if message_type == ContentType.CHAT_SHARED and message.chat_shared:
return f"{content_types.get(message_type, 'Чат был передан')}: {message.chat_shared.chat_id}"
# Возвращаем описание типа сообщения, если оно есть в словаре, иначе "Неизвестный тип" # Возвращаем описание типа сообщения, если оно есть в словаре, иначе "Неизвестный тип"
return content_types.get(message_type, "Неизвестный тип") return content_types.get(message_type, "Неизвестный тип")

View File

@@ -3,4 +3,3 @@
# Экспортирование модулей во внешние слои проекта # Экспортирование модулей во внешние слои проекта
from .logs import * from .logs import *
from .custom_loggers import *

View File

@@ -1,207 +0,0 @@
# BotLibrary/loggers/custom_loggers.py
# Кастомные логгеры для проекта, с более стандартизированным использованием
from time import sleep
from aiogram import types
from colorama import Fore
from loguru import logger
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",)
class Logs:
"""Класс для логирования с разными уровнями через loguru."""
@staticmethod
def start(text: str = "Логирование!",
system: str = "PRIMO",
log_type: str = "AEP",
user: str = "@Console") -> None:
"""
Логирует сообщение на уровне START.
:param text: Сообщение для логирования.
:param system: Тип системы логирования.
:param log_type: Тип лога (например, "Help").
:param user: Имя пользователя или источник вызова лога.
:return: Вывод сообщения об старте бота
"""
logger.bind(system=system, user=user, log_type=log_type).log("START", text)
@staticmethod
def debug(text: str = "Логирование!",
system: str = "DEBUG",
log_type: str = "Logs",
user: str = "@Console",
message: Message = None) -> None:
"""
Логирует сообщение на уровне DEBUG.
:param text: Сообщение для логирования.
:param system: Тип системы логирования.
:param log_type: Тип лога (например, "Help").
:param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя.
:return: Вывод сообщения об дебаг-информации
"""
if message:
user = username(message)
logger.bind(system=system, log_type=log_type, user=user).debug(text)
@staticmethod
def info(text: str = "Логирование!",
system: str = "PRIMO",
log_type: str = "Logs",
user: str = "@Console",
message: Message = None) -> None:
"""
Логирует сообщение на уровне INFO.
:param text: Сообщение для логирования.
:param system: Тип системы логирования.
:param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя.
:return: Вывод сообщения об некой информации
"""
if message:
user = username(message)
logger.bind(system=system, log_type=log_type, user=user).info(text)
@staticmethod
def warning(text: str = "Логирование!",
system: str = "WARNING",
log_type: str = "Logs",
user: str = "@Console",
message: Message = None) -> None:
"""
Логирует сообщение на уровне WARNING.
:param text: Сообщение для логирования.
:param system: Тип системы логирования.
:param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя.
:return: Вывод сообщения об предупреждении
"""
if message:
user = username(message)
logger.bind(system=system, log_type=log_type, user=user).warning(text)
@staticmethod
def error(text: str = "Логирование!",
system: str = "ERROR",
log_type: str = "Logs",
user: str = "@Console",
message: Message = None) -> None:
"""
Логирует сообщение на уровне ERROR.
:param text: Сообщение для логирования.
:param system: Тип системы логирования.
:param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя.
:return: Вывод сообщения об ошибке
"""
if message:
user = username(message)
logger.bind(system=system, log_type=log_type, user=user).error(text)
@staticmethod
def msg(message: types.Message,
log_type: str = "Message",
user: str = None,
msg_type: str = None,
permission: bool = BotLogs.permission) -> None:
"""
Логирует сообщение, если оно не обработано.
:param message: Сообщение от пользователя.
:param log_type: Тип лога (по умолчанию "Message").
:param permission: Разрешение на логирование (config).
:param user: Получение пользователя (автоматически).
:param msg_type: Получение типа сообщения (автоматически).
.
:return: Вывод сообщения об обычном сообщении пользователя.
"""
# Получаем айди чата
chat_id = message.chat.id
# Получаем username или id пользователя
if user is None:
user: str = f"@{message.from_user.username or message.from_user.id}"
if msg_type is None:
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"Получено сообщение из ({chat_id}) : {msg_type}")
elif message.text is not None:
Logs.info(log_type=log_type, user=user,
text=f"Получено сообщение из ({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,107 +1,295 @@
# BotLibrary/loggers/logs.py # BotLibrary/loggers/logs.py
# Создание логгеров и их шаблон для проекта # Кастомные логгеры для проекта, с более стандартизированным использованием
import sys
from loguru import logger from loguru import logger
from ProjectsFiles import BotLogs, ProjectPath 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__ = ("setup_logger",) __all__ = ("Logs",)
# Создание обычного логгера + логгер в файл
async def setup_logger(logging: bool = BotLogs.permission,
to_file: bool = BotLogs.permission_to_file) -> None:
"""
Настройка логгеров для проекта, выводящих логи в консоль и файлы.
Логгеры конфигурируются в зависимости от настроек в конфигах проекта.
Если разрешено логирование, добавляются логи для уровней DEBUG, INFO, WARNING, ERROR.
И кастомные такие, как START, NEW_USER, LEAVE_USER
:param logging: Разрешение на логирование в консоль (config)
:param to_file: Разрешение на логирование в файл (config)
:return: Создание логеров под различные уровни
"""
logger.remove() # Удаляем все стандартные логгеры
# Если есть разрешение, то он создает новые уровни class Logs:
if logging and BotLogs.permission_to_file: """Класс для логирования с разными уровнями через loguru."""
# Добавляем новый уровень START @staticmethod
logger.level("START", no=25, color="white", icon="🔸") def setup(logging: bool = BotLogs.permission,
if logging and BotLogs.permission_new_user: to_file: bool = BotLogs.permission_to_file) -> None:
# Добавляем новый уровень NEW_USER """
logger.level("NEW_USER", no=4, color="white", icon="👋") Настройка логгеров для проекта, выводящих логи в консоль и файлы.
if logging and BotLogs.permission_leave_user: Логгеры конфигурируются в зависимости от настроек в конфигах проекта.
# Добавляем новый уровень LEAVE_USER
logger.level("LEAVE_USER", no=3, color="white", icon="🫰") Если разрешено логирование, добавляются логи для уровней DEBUG, INFO, WARNING, ERROR.
И кастомные такие, как START, NEW_USER, LEAVE_USER
:param logging: Разрешение на логирование в консоль (config)
:param to_file: Разрешение на логирование в файл (config)
:return: Создание логеров под различные уровни
"""
logger.remove() # Удаляем все стандартные логгеры
# Если есть разрешение, то он создает новые уровни
if logging and BotLogs.permission_to_file:
# Добавляем новый уровень START
logger.level("START", no=25, color="white", icon="🔸")
if logging and BotLogs.permission_new_user:
# Добавляем новый уровень NEW_USER
logger.level("NEW_USER", no=4, color="white", icon="👋")
if logging and BotLogs.permission_leave_user:
# Добавляем новый уровень LEAVE_USER
logger.level("LEAVE_USER", no=3, color="white", icon="🫰")
# Настройка логирования в консоль для каждого уровня
if logging:
from sys import stderr
logger.add(stderr,
colorize=True,
format=BotLogs.start_text,
level="START",
filter=lambda record: record["level"].name == "START"
)
logger.add(stderr,
colorize=True,
format=BotLogs.debug_text,
level="DEBUG",
filter=lambda record: record["level"].name == "DEBUG")
logger.add(stderr,
colorize=True,
format=BotLogs.info_text,
level="INFO",
filter=lambda record: record["level"].name == "INFO")
logger.add(stderr,
colorize=True,
format=BotLogs.warning_text,
level="WARNING",
filter=lambda record: record["level"].name == "WARNING")
logger.add(stderr,
colorize=True,
format=BotLogs.error_text,
level="ERROR",
filter=lambda record: record["level"].name == "ERROR")
# Добавление логгера для записи в файл
if to_file:
logger.add(ProjectPath.start_log_file,
rotation=BotLogs.max_size,
format=BotLogs.start_text,
backtrace=True,
diagnose=True,
level="START",
filter=lambda record: record["level"].name == "START")
logger.add(ProjectPath.debug_log_file,
rotation=BotLogs.max_size,
format=BotLogs.debug_text,
backtrace=True,
diagnose=True,
level="DEBUG",
filter=lambda record: record["level"].name == "DEBUG")
logger.add(ProjectPath.info_log_file,
rotation=BotLogs.max_size,
format=BotLogs.info_text,
backtrace=True,
diagnose=True,
level="INFO",
filter=lambda record: record["level"].name == "INFO")
logger.add(ProjectPath.warning_log_file,
rotation=BotLogs.max_size,
format=BotLogs.warning_text,
backtrace=True,
diagnose=True,
level="WARNING",
filter=lambda record: record["level"].name == "WARNING")
logger.add(ProjectPath.error_log_file,
rotation=BotLogs.max_size,
format=BotLogs.error_text,
backtrace=True,
diagnose=True,
level="ERROR",
filter=lambda record: record["level"].name == "ERROR")
# Настройка логирования в консоль для каждого уровня @staticmethod
if logging: def start(text: str = "Логирование!",
logger.add(sys.stderr, system: str = "PRIMO",
colorize=True, log_type: str = "AEP",
format=BotLogs.start_text, user: str = "@Console") -> None:
level="START", """
filter=lambda record: record["level"].name == "START" Логирует сообщение на уровне START.
)
logger.add(sys.stderr, :param text: Сообщение для логирования.
colorize=True, :param system: Тип системы логирования.
format=BotLogs.debug_text, :param log_type: Тип лога (например, "Help").
level="DEBUG", :param user: Имя пользователя или источник вызова лога.
filter=lambda record: record["level"].name == "DEBUG")
logger.add(sys.stderr, :return: Вывод сообщения об старте бота
colorize=True, """
format=BotLogs.info_text, logger.bind(system=system, user=user, log_type=log_type).log("START", text)
level="INFO",
filter=lambda record: record["level"].name == "INFO")
logger.add(sys.stderr,
colorize=True,
format=BotLogs.warning_text,
level="WARNING",
filter=lambda record: record["level"].name == "WARNING")
logger.add(sys.stderr,
colorize=True,
format=BotLogs.error_text,
level="ERROR",
filter=lambda record: record["level"].name == "ERROR")
# Добавление логгера для записи в файл @staticmethod
if to_file: def debug(text: str = "Логирование!",
logger.add(ProjectPath.start_log_file, system: str = "DEBUG",
rotation=BotLogs.max_size, log_type: str = "Logs",
format=BotLogs.start_text, user: str = "@Console",
backtrace=True, message: Message = None) -> None:
diagnose=True, """
level="START", Логирует сообщение на уровне DEBUG.
filter=lambda record: record["level"].name == "START")
logger.add(ProjectPath.debug_log_file, :param text: Сообщение для логирования.
rotation=BotLogs.max_size, :param system: Тип системы логирования.
format=BotLogs.debug_text, :param log_type: Тип лога (например, "Help").
backtrace=True, :param user: Имя пользователя или источник вызова лога.
diagnose=True, :param message: Сообщение от пользователя, если необходимо извлечь имя.
level="DEBUG",
filter=lambda record: record["level"].name == "DEBUG") :return: Вывод сообщения об дебаг-информации
logger.add(ProjectPath.info_log_file, """
rotation=BotLogs.max_size, if message:
format=BotLogs.info_text, user = username(message)
backtrace=True, logger.bind(system=system, log_type=log_type, user=user).debug(text)
diagnose=True,
level="INFO",
filter=lambda record: record["level"].name == "INFO") @staticmethod
logger.add(ProjectPath.warning_log_file, def info(text: str = "Логирование!",
rotation=BotLogs.max_size, system: str = "PRIMO",
format=BotLogs.warning_text, log_type: str = "Logs",
backtrace=True, user: str = "@Console",
diagnose=True, message: Message = None) -> None:
level="WARNING", """
filter=lambda record: record["level"].name == "WARNING") Логирует сообщение на уровне INFO.
logger.add(ProjectPath.error_log_file,
rotation=BotLogs.max_size, :param text: Сообщение для логирования.
format=BotLogs.error_text, :param system: Тип системы логирования.
backtrace=True, :param log_type: Тип лога (например, "Logs").
diagnose=True, :param user: Имя пользователя или источник вызова лога.
level="ERROR", :param message: Сообщение от пользователя, если необходимо извлечь имя.
filter=lambda record: record["level"].name == "ERROR")
:return: Вывод сообщения об некой информации
"""
if message:
user = username(message)
logger.bind(system=system, log_type=log_type, user=user).info(text)
@staticmethod
def warning(text: str = "Логирование!",
system: str = "WARNING",
log_type: str = "Logs",
user: str = "@Console",
message: Message = None) -> None:
"""
Логирует сообщение на уровне WARNING.
:param text: Сообщение для логирования.
:param system: Тип системы логирования.
:param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя.
:return: Вывод сообщения об предупреждении
"""
if message:
user = username(message)
logger.bind(system=system, log_type=log_type, user=user).warning(text)
@staticmethod
def error(text: str = "Логирование!",
system: str = "ERROR",
log_type: str = "Logs",
user: str = "@Console",
message: Message = None) -> None:
"""
Логирует сообщение на уровне ERROR.
:param text: Сообщение для логирования.
:param system: Тип системы логирования.
:param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя.
:return: Вывод сообщения об ошибке
"""
if message:
user = username(message)
logger.bind(system=system, log_type=log_type, user=user).error(text)
@staticmethod
def msg(message: Message,
log_type: str = "Message",
user: str = None,
msg_type: str = None,
permission: bool = BotLogs.permission) -> None:
"""
Логирует сообщение, если оно не обработано.
:param message: Сообщение от пользователя.
:param log_type: Тип лога (по умолчанию "Message").
:param permission: Разрешение на логирование (config).
:param user: Получение пользователя (автоматически).
:param msg_type: Получение типа сообщения (автоматически).
.
:return: Вывод сообщения об обычном сообщении пользователя.
"""
# Получаем айди чата
chat_id = message.chat.id
# Получаем username или id пользователя
if user is None:
user: str = f"@{message.from_user.username or message.from_user.id}"
if msg_type is None:
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"Получено сообщение из ({chat_id}) : {msg_type}")
elif message.text is not None:
Logs.info(log_type=log_type, user=user,
text=f"Получено сообщение из ({chat_id}) : {message.text}")
@staticmethod
def console(console: bool = Permissions.start_info_console,
file: bool = Permissions.start_info_to_file,
path: str = ProjectPath.bot_info_log_file) -> None:
"""
Собирает информацию о боте и выводит её в консоль, а также возвращает как строку.
: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:
from colorama import Fore
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,10 +1,13 @@
# BotLibrary/samples/inline_kb_sample.py # BotLibrary/samples/inline_kb_sample.py
# Шаблон для создания инлайн клавиатур # Шаблон для создания инлайн клавиатур
from aiogram.types import InlineKeyboardMarkup, ReplyKeyboardRemove from aiogram.types import InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from typing import List, Tuple, Optional from typing import List, Tuple, Optional
# Настройка экспорта в модули
__all__ = ("BaseInlineKeyboard",)
class BaseInlineKeyboard: class BaseInlineKeyboard:
def __init__(self, buttons: List[Tuple[str, Optional[str], Optional[str]]], row_width: int = 1): def __init__(self, buttons: List[Tuple[str, Optional[str], Optional[str]]], row_width: int = 1):
""" """
@@ -17,7 +20,7 @@ class BaseInlineKeyboard:
def get_keyboard(self) -> InlineKeyboardMarkup: def get_keyboard(self) -> InlineKeyboardMarkup:
""" """
Создаёт инлайн-клавиатуру и возвращает её вместе с объектом для удаления reply-клавиатуры. Создаёт инлайн-клавиатуру и возвращает её вместе с объектом для удаления reply-клавиатуры.
:return: кортеж (InlineKeyboardMarkup, ReplyKeyboardRemove) :return: кортеж InlineKeyboardMarkup
""" """
ikb = InlineKeyboardBuilder() ikb = InlineKeyboardBuilder()
for text, url, callback_data in self.buttons: for text, url, callback_data in self.buttons:

View File

@@ -5,6 +5,9 @@ from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, KeyboardButtonPol
from aiogram.utils.keyboard import ReplyKeyboardBuilder from aiogram.utils.keyboard import ReplyKeyboardBuilder
from typing import List, Union, Tuple, Optional, Dict, Any from typing import List, Union, Tuple, Optional, Dict, Any
# Настройка экспорта в модули
__all__ = ("BaseReplyKeyboard",)
class BaseReplyKeyboard: class BaseReplyKeyboard:
def __init__( def __init__(
self, self,
@@ -100,4 +103,4 @@ class BaseReplyKeyboard:
buttons.append(KeyboardButton(text=button)) buttons.append(KeyboardButton(text=button))
rkb.row(*buttons) rkb.row(*buttons)
return rkb.as_markup(resize_keyboard=self.resize_keyboard, one_time_keyboard=self.one_time_keyboard) return rkb.as_markup(resize_keyboard=self.resize_keyboard, one_time_keyboard=self.one_time_keyboard)

View File

@@ -11,16 +11,47 @@ from typing import Optional, Callable
from ..loggers import Logs from ..loggers import Logs
from ..validators import username, valid_url from ..validators import username, valid_url
from ProjectsFiles import BotVar from ProjectsFiles import BotVar
from SQLite3 import base_sql from ..sql import db
# Настройки экспорта в модули # Настройки экспорта в модули
__all__ = ("CommandHandler",) __all__ = ("CommandHandler",)
class CommandHandler: class CommandHandler:
"""
Класс для создания и управления командами Telegram-бота.
Этот класс позволяет создавать команды с различными настройками, включая
текстовые сообщения, медиафайлы, клавиатуры, выполнение функций и обработку
callback-запросов.
:param name: Название команды.
:param keywords: Список ключевых слов, вызывающих команду.
:param func: Список функций, которые выполняются при вызове команды.
:param text_msg: Текстовое сообщение, которое отправляется в ответ на команду.
:param chat_action: Флаг отправки анимации набора текста перед ответом.
:param description: Описание команды.
:param tg_links: Флаг обработки ссылок на пользователей в тексте.
:param keyboard: Клавиатура, привязанная к команде.
:param prefix: Префикс команды (по умолчанию `BotVar.prefix`).
:param callbackdata: Список callback-данных, связанных с командой.
:param only_admin: Флаг, разрешающий использование команды только администраторам.
:param ignore_case: Флаг игнорирования регистра в ключевых словах.
:param activate_keywords: Флаг активации команды по ключевым словам.
:param delete_msg: Флаг удаления исходного сообщения после обработки команды.
:param activate_commands: Флаг активации команды через стандартный обработчик команд.
:param activate_callback: Флаг активации обработки callback-запросов.
:param media: Тип медиафайла, который отправляется (`"message"`, `"photo"`, `"video"`, и т. д.).
:param path_to_media: Путь к медиафайлу (или список путей).
:param parse_mode: Форматирование текста (`Markdown`, `HTML` и т. д.).
:param disable_notification: Флаг отключения уведомлений при отправке сообщений.
:param protect: Флаг защиты контента сообщений.
:return: Готовый шаблон для команды.
"""
def __init__(self, name: str, def __init__(self, name: str,
keywords: list, keywords: list,
func: Optional[list[Callable]] = None, func: Optional[list[Callable]] = None,
text_msg=None, text_msg = None,
chat_action: bool = False, chat_action: bool = False,
description: str = "Описание команды", description: str = "Описание команды",
tg_links: bool = False, tg_links: bool = False,
@@ -34,15 +65,16 @@ class CommandHandler:
activate_commands: bool = True, activate_commands: bool = True,
activate_callback: bool = True, activate_callback: bool = True,
media: str = "message", media: str = "message",
path_to_media=None, path_to_media = None,
parse_mode: str = BotVar.parse_mode, parse_mode: str = BotVar.parse_mode,
disable_notification: bool = BotVar.disable_notification, disable_notification: bool = BotVar.disable_notification,
protect: bool = BotVar.protect_content): protect: bool = BotVar.protect_content):
self.router = Router(name=f"{name}_router") self.router = Router(name=f"{name}_router")
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.last_bot_message = {}
self.keywords = keywords self.keywords = keywords
self.text_msg = text_msg self.text_msg = text_msg
@@ -125,7 +157,10 @@ class CommandHandler:
text = text.replace("<users>", str(message.from_user.id)) text = text.replace("<users>", str(message.from_user.id))
Logs.info(log_type=self.log_type, user=username(message), text=f"использовал(а) команду /{self.name}") Logs.info(log_type=self.log_type, user=username(message), text=f"использовал(а) команду /{self.name}")
await base_sql(message)
# Работа с базами данных
db.update_user(message)
db.update_user_messages(message)
# Обрабатываем текстовое сообщение # Обрабатываем текстовое сообщение
if callable(self.text_msg): if callable(self.text_msg):

View File

@@ -0,0 +1,12 @@
# BotLibrary/system/__init__.py
# Инициализация пакета system, для библиотек запуска
# Экспортирование модулей во внешние слои проекта
from .db_class import *
from ProjectsFiles import BotVar
# Создание экземпляра класса
db = Database(BotVar.bd_names)
# Создание базы данных
db.create_db()

563
BotLibrary/sql/db_class.py Normal file
View File

@@ -0,0 +1,563 @@
# BotLibrary/system/db_class.py
# Создание базы данных
import os
import sqlite3
from datetime import datetime, timedelta, timezone
from aiogram import types
from typing import Optional, List, Tuple
from ProjectsFiles import BotVar
# Настройка экспорта в модули
__all__ = ("Database",)
class Database:
"""Класс для управления базой данных пользователей чата с использованием SQLite3."""
def __init__(self, db_name: str = BotVar.bd_path) -> None:
"""Инициализация класса с именем базы данных."""
self.db_name = db_name
# --- Основные методы из предоставленных функций ---
def create_db(self) -> None:
"""Создание базы данных и таблиц с начальными данными."""
# Создание директории, если её нет
db_directory = os.path.dirname(self.db_name)
if db_directory and not os.path.exists(db_directory):
os.makedirs(db_directory)
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
# Таблица пользователей
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY,
tg_id INTEGER NOT NULL UNIQUE,
username TEXT,
first_name TEXT,
last_name TEXT,
role TEXT DEFAULT NULL,
status TEXT DEFAULT 'active',
user TEXT DEFAULT 'user'
);''')
# Таблица сообщений пользователей
cursor.execute('''
CREATE TABLE IF NOT EXISTS user_messages (
user_id INTEGER PRIMARY KEY,
last_message TEXT,
last_message_id INTEGER,
last_message_time TEXT,
messages_per_day INTEGER DEFAULT 0,
messages_per_week INTEGER DEFAULT 0,
messages_per_month INTEGER DEFAULT 0,
total_messages INTEGER DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE CASCADE
);''')
# Таблица персонажей Genshin Impact
cursor.execute('''
CREATE TABLE IF NOT EXISTS characters (
id INTEGER PRIMARY KEY AUTOINCREMENT,
region TEXT NOT NULL,
name TEXT NOT NULL,
status TEXT DEFAULT 'Свободно',
user_id INTEGER DEFAULT NULL,
comment TEXT DEFAULT '',
FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE SET NULL
);''')
# Таблица персонажей Honkai: Star Rail
cursor.execute('''
CREATE TABLE IF NOT EXISTS characters_hsr (
id INTEGER PRIMARY KEY AUTOINCREMENT,
region TEXT NOT NULL,
name TEXT NOT NULL,
status TEXT DEFAULT 'Свободно',
user_id INTEGER DEFAULT NULL,
comment TEXT DEFAULT '',
FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE SET NULL
);''')
# Начальные данные для characters (Genshin Impact)
characters_genshin: list[tuple[str, str, str, str]] = [
# Мондштадт
("Мондштадт", "Венти", "Свободно", ""),
("Мондштадт", "Кэйа", "Свободно", ""),
("Мондштадт", "Альбедо", "Свободно", ""),
("Мондштадт", "Дилюк", "Свободно", ""),
("Мондштадт", "Мика", "Свободно", ""),
("Мондштадт", "Беннет", "Свободно", ""),
("Мондштадт", "Рэйзор", "Свободно", ""),
("Мондштадт", "Эола", "Свободно", ""),
("Мондштадт", "Мона", "Свободно", ""),
("Мондштадт", "Джинн", "Свободно", ""),
("Мондштадт", "Диона", "Свободно", ""),
("Мондштадт", "Лиза", "Свободно", ""),
("Мондштадт", "Ноэлль", "Свободно", ""),
("Мондштадт", "Сахароза", "Свободно", ""),
("Мондштадт", "Розария", "Свободно", ""),
("Мондштадт", "Эмбер", "Свободно", ""),
("Мондштадт", "Фишль", "Свободно", ""),
("Мондштадт", "Барбара", "Свободно", ""),
# Ли Юэ
("Ли Юэ", "Чжун Ли", "Свободно", ""),
("Ли Юэ", "Сяо", "Свободно", ""),
("Ли Юэ", "Син Цю", "Свободно", ""),
("Ли Юэ", "Чун Юнь", "Свободно", ""),
("Ли Юэ", "Бай Чжу", "Свободно", ""),
("Ли Юэ", "Е Лань", "Свободно", ""),
("Ли Юэ", "Шень Хэ", "Свободно", ""),
("Ли Юэ", "Гань Юй", "Свободно", ""),
("Ли Юэ", "Ци Ци", "Свободно", ""),
("Ли Юэ", "Кэ Цин", "Свободно", ""),
("Ли Юэ", "Янь Фэй", "Свободно", ""),
("Ли Юэ", "Нин Гуан", "Свободно", ""),
("Ли Юэ", "Бэй Доу", "Свободно", ""),
("Ли Юэ", "Яо Яо", "Свободно", ""),
("Ли Юэ", "Ка Мин", "Свободно", ""),
("Ли Юэ", "Сянь Юнь", "Свободно", ""),
("Ли Юэ", "Юнь Цзинь", "Свободно", ""),
("Ли Юэ", "Ху Тао", "Свободно", ""),
("Ли Юэ", "Лань Янь", "Свободно", ""),
# Инадзума
("Инадзума", "Итто", "Свободно", ""),
("Инадзума", "Горо", "Свободно", ""),
("Инадзума", "Аято", "Свободно", ""),
("Инадзума", "Хэйдзо", "Свободно", ""),
("Инадзума", "Тома", "Свободно", ""),
("Инадзума", "Кадзуха", "Свободно", ""),
("Инадзума", "Кирара", "Свободно", ""),
("Инадзума", "Кудзё Сара", "Свободно", ""),
("Инадзума", "Ёимия", "Свободно", ""),
("Инадзума", "Аяка", "Свободно", ""),
("Инадзума", "Сангономия Кокоми", "Свободно", ""),
("Инадзума", "Яэ Мико", "Свободно", ""),
("Инадзума", "Райдэн Эи", "Свободно", ""),
("Инадзума", "Саю", "Свободно", ""),
("Инадзума", "Куки Синобу", "Свободно", ""),
("Инадзума", "Мидзуки", "Свободно", ""),
# Сумеру
("Сумеру", "Аль-Хайтам", "Свободно", ""),
("Сумеру", "Кавех", "Свободно", ""),
("Сумеру", "Сайно", "Свободно", ""),
("Сумеру", "Тигнари", "Свободно", ""),
("Сумеру", "Сетос", "Свободно", ""),
("Сумеру", "Нилу", "Свободно", ""),
("Сумеру", "Нахида", "Свободно", ""),
("Сумеру", "Лайла", "Свободно", ""),
("Сумеру", "Кандакия", "Свободно", ""),
("Сумеру", "Дори", "Свободно", ""),
("Сумеру", "Дэхья", "Свободно", ""),
("Сумеру", "Коллеи", "Свободно", ""),
("Сумеру", "Фарузан", "Свободно", ""),
# Фонтейн
("Фонтейн", "Лини", "Свободно", ""),
("Фонтейн", "Ризли", "Свободно", ""),
("Фонтейн", "Невиллет", "Свободно", ""),
("Фонтейн", "Фремине", "Свободно", ""),
("Фонтейн", "Линетт", "Свободно", ""),
("Фонтейн", "Эмилия", "Свободно", ""),
("Фонтейн", "Клоринда", "Свободно", ""),
("Фонтейн", "Навия", "Свободно", ""),
("Фонтейн", "Шарлотта", "Свободно", ""),
("Фонтейн", "Фурина", "Свободно", ""),
("Фонтейн", "Тиори", "Свободно", ""),
("Фонтейн", "Сиджвин", "Свободно", ""),
# Натлан
("Натлан", "Кинич", "Свободно", ""),
("Натлан", "Оророн", "Свободно", ""),
("Натлан", "Муалани", "Свободно", ""),
("Натлан", "Ситлали", "Свободно", ""),
("Натлан", "Шилонен", "Свободно", ""),
("Натлан", "Иансан", "Свободно", ""),
("Натлан", "Мавуика", "Свободно", ""),
("Натлан", "Часка", "Свободно", ""),
# Фатуи
("Фатуи", "Тарталья", "Свободно", ""),
("Фатуи", "Панталоне", "Свободно", ""),
("Фатуи", "Дотторе", "Свободно", ""),
("Фатуи", "Капитано", "Свободно", ""),
("Фатуи", "Пьеро", "Свободно", ""),
("Фатуи", "Пульничелла", "Свободно", ""),
("Фатуи", "Синьора", "Свободно", ""),
("Фатуи", "Арлекино", "Свободно", ""),
("Фатуи", "Коломбина", "Свободно", ""),
("Фатуи", "Царица", "Свободно", ""),
("Фатуи", "Странник", "Свободно", ""),
# Иные персонажи
("Иные персонажи", "Итэр", "Свободно", ""),
("Иные персонажи", "Люмин", "Свободно", ""),
("Иные персонажи", "Элой", "Свободно", ""),
("Иные персонажи", "Паймон", "Свободно", ""),
("Иные персонажи", "Дайнслейф", "Свободно", ""),
]
# Начальные данные для characters_hsr (Honkai: Star Rail)
characters_hsr: list[tuple[str, str, str, str]] = [
# Ярило-6
("Ярило-6", "Броня", "Свободно", ""),
("Ярило-6", "Гепард", "Свободно", ""),
("Ярило-6", "Зеле", "Свободно", ""),
("Ярило-6", "Клара", "Свободно", ""),
("Ярило-6", "Лука", "Свободно", ""),
("Ярило-6", "Наташа", "Свободно", ""),
("Ярило-6", "Пела", "Свободно", ""),
("Ярило-6", "Рысь", "Свободно", ""),
("Ярило-6", "Сампо", "Свободно", ""),
("Ярило-6", "Сервал", "Свободно", ""),
("Ярило-6", "Хук", "Свободно", ""),
# Станция «Герта»
("Станция «Герта»", "Арлан", "Свободно", ""),
("Станция «Герта»", "Аста", "Свободно", ""),
("Станция «Герта»", "Великая Герта", "Свободно", ""),
("Станция «Герта»", "Кукла «Герта»", "Свободно", ""),
("Станция «Герта»", "Жуань Мэй", "Свободно", ""),
# Пенакония
("Пенакония", "Авантюрин", "Свободно", ""),
("Пенакония", "Ахерон", "Свободно", ""),
("Пенакония", "Галлахер", "Свободно", ""),
("Пенакония", "Зарянка", "Свободно", ""),
("Пенакония", "Миша", "Свободно", ""),
("Пенакония", "Мистер Река", "Свободно", ""),
("Пенакония", "Раппа", "Свободно", ""),
("Пенакония", "Чёрный Лебедь", "Свободно", ""),
("Пенакония", "Яшма", "Свободно", ""),
("Пенакония", "Воскресенье", "Свободно", ""),
# Звёздный Экспресс
("Звёздный Экспресс", "Вельт", "Свободно", ""),
("Звёздный Экспресс", "Келус", "Свободно", ""),
("Звёздный Экспресс", "Стелла", "Свободно", ""),
("Звёздный Экспресс", "Дань Хэн", "Свободно", ""),
("Звёздный Экспресс", "Март 7", "Свободно", ""),
("Звёздный Экспресс", "Химеко", "Свободно", ""),
("Звёздный Экспресс", "Пом Пом", "Свободно", ""),
# Галактика
("Галактика", "Аргенти", "Свободно", ""),
("Галактика", "Блэйд", "Свободно", ""),
("Галактика", "Бутхилл", "Свободно", ""),
("Галактика", "Доктор Рацио", "Свободно", ""),
("Галактика", "Кафка", "Свободно", ""),
("Галактика", "Светлячок", "Свободно", ""),
("Галактика", "Искорка", "Свободно", ""),
("Галактика", "Серебряный Волк", "Свободно", ""),
("Галактика", "Топаз", "Свободно", ""),
# Амфореус
("Амфореус", "Аглая", "Свободно", ""),
("Амфореус", "Мидей", "Свободно", ""),
("Амфореус", "Трибби", "Свободно", ""),
# Альянс Сяньчжоу
("Альянс Сяньчжоу", "Байлу", "Свободно", ""),
("Альянс Сяньчжоу", "Гуйнайфей", "Свободно", ""),
("Альянс Сяньчжоу", "Линша", "Свободно", ""),
("Альянс Сяньчжоу", "Лоча", "Свободно", ""),
("Альянс Сяньчжоу", "Моцзэ", "Свободно", ""),
("Альянс Сяньчжоу", "Пожиратель Луны", "Свободно", ""),
("Альянс Сяньчжоу", "Сушан", "Свободно", ""),
("Альянс Сяньчжоу", "Сюзи", "Свободно", ""),
("Альянс Сяньчжоу", "Фуга", "Свободно", ""),
("Альянс Сяньчжоу", "Фэйсяо", "Свободно", ""),
("Альянс Сяньчжоу", "Ханья", "Свободно", ""),
("Альянс Сяньчжоу", "Хохо", "Свободно", ""),
("Альянс Сяньчжоу", "Цзин Юань", "Свободно", ""),
("Альянс Сяньчжоу", "Цзиннлю", "Свободно", ""),
("Альянс Сяньчжоу", "Цзяоцю", "Свободно", ""),
("Альянс Сяньчжоу", "Цинцюэ", "Свободно", ""),
("Альянс Сяньчжоу", "Юйкун", "Свободно", ""),
("Альянс Сяньчжоу", "Юньли", "Свободно", ""),
("Альянс Сяньчжоу", "Яньцин", "Свободно", ""),
]
# Заполнение таблиц начальными данными, если они пусты
cursor.execute("SELECT COUNT(*) FROM characters")
if cursor.fetchone()[0] == 0:
cursor.executemany('INSERT INTO characters (region, name, status, comment) VALUES (?, ?, ?, ?)',
characters_genshin)
cursor.execute("SELECT COUNT(*) FROM characters_hsr")
if cursor.fetchone()[0] == 0:
cursor.executemany('INSERT INTO characters_hsr (region, name, status, comment) VALUES (?, ?, ?, ?)',
characters_hsr)
db.commit()
def add_user(self, tg_id: int, username: str, first_name: str, last_name: str, role: str, status: str,
user: str) -> None:
"""Добавление нового пользователя в базу данных."""
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
# Проверка на существование пользователя
cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,))
if cursor.fetchone():
return # Пользователь уже существует
# Определение нового user_id
cursor.execute("SELECT MAX(user_id) FROM users")
max_id = cursor.fetchone()[0]
new_user_id = 1 if max_id is None else max_id + 1
# Вставка пользователя
cursor.execute('''
INSERT INTO users (user_id, tg_id, username, first_name, last_name, role, status, user)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (new_user_id, tg_id, username, first_name, last_name, role, status, user))
# Создание записи в user_messages
cursor.execute('INSERT INTO user_messages (user_id) VALUES (?)', (new_user_id,))
db.commit()
def get_user(self, tg_id: int) -> Optional[Tuple]:
"""Получение информации о пользователе по tg_id."""
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute("SELECT * FROM users WHERE tg_id = ?", (tg_id,))
return cursor.fetchone()
def update_user(self, tg_id: int, username: Optional[str] = None, first_name: Optional[str] = None,
last_name: Optional[str] = None, role: Optional[str] = None, user: Optional[str] = None) -> None:
"""Обновление данных о пользователе."""
updates = []
params = []
if username:
updates.append("username = ?")
params.append(username)
if first_name:
updates.append("first_name = ?")
params.append(first_name)
if last_name:
updates.append("last_name = ?")
params.append(last_name)
if role:
updates.append("role = ?")
params.append(role)
if user:
updates.append("user = ?")
params.append(user)
if updates:
query = f"UPDATE users SET {', '.join(updates)} WHERE tg_id = ?"
params.append(tg_id)
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute(query, params)
db.commit()
def update_user_messages(self, message: types.Message) -> None:
"""Обновление статистики сообщений пользователя."""
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
tg_id = message.from_user.id
# Получение user_id
cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,))
result = cursor.fetchone()
if not result:
return # Пользователь не найден
user_id = result[0]
# Проверка существующей записи в user_messages
cursor.execute(
"SELECT last_message_time, messages_per_day, messages_per_week, messages_per_month, total_messages "
"FROM user_messages WHERE user_id = ?", (user_id,))
result = cursor.fetchone()
# Время сообщения в московском часовом поясе
now = message.date.astimezone(timezone(timedelta(hours=3)))
today = now.date()
start_of_week = today - timedelta(days=today.weekday())
current_month = now.month
current_year = now.year
last_message = message.text or "Н" # Замените на type_msg(message) при необходимости
last_message_id = message.message_id
if result:
last_message_time, messages_per_day, messages_per_week, messages_per_month, total_messages = result
if last_message_time:
last_message_time = datetime.fromisoformat(last_message_time).astimezone(
timezone(timedelta(hours=3)))
last_date = last_message_time.date()
last_week = last_date - timedelta(days=last_date.weekday())
last_month = last_message_time.month
last_year = last_message_time.year
# Обнуление счетчиков при смене периода
if last_date != today:
messages_per_day = 0
if last_week != start_of_week:
messages_per_week = 0
if last_month != current_month or last_year != current_year:
messages_per_month = 0
else:
messages_per_day, messages_per_week, messages_per_month = 0, 0, 0
# Увеличение счетчиков
messages_per_day += 1
messages_per_week += 1
messages_per_month += 1
total_messages += 1
else:
messages_per_day, messages_per_week, messages_per_month, total_messages = 1, 1, 1, 1
cursor.execute('''
INSERT INTO user_messages (user_id, last_message, last_message_id, last_message_time,
messages_per_day, messages_per_week, messages_per_month, total_messages)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (user_id, last_message, last_message_id, now.isoformat(), messages_per_day,
messages_per_week, messages_per_month, total_messages))
db.commit()
return
# Обновление записи
cursor.execute('''
UPDATE user_messages
SET last_message = ?, last_message_id = ?, last_message_time = ?,
messages_per_day = ?, messages_per_week = ?, messages_per_month = ?,
total_messages = ?
WHERE user_id = ?
''', (last_message, last_message_id, now.isoformat(), messages_per_day,
messages_per_week, messages_per_month, total_messages, user_id))
db.commit()
def get_user_status(self, message: types.Message) -> str:
"""Получение статуса пользователя."""
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute("SELECT user FROM users WHERE tg_id = ?", (message.from_user.id,))
row = cursor.fetchone()
status_map = {
"ban": "Забанен",
"user": "Пользователь",
"moderator": "Модератор",
"admin": "Администратор",
"so-owner": "Совладелец",
"owner": "Владелец",
}
return status_map.get(row[0], "Ошибка!") if row else "Пользователь не найден"
# --- Новые и улучшенные методы ---
def delete_user(self, tg_id: int) -> None:
"""Удаление пользователя из базы данных."""
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute("DELETE FROM users WHERE tg_id = ?", (tg_id,))
db.commit()
def get_all_users(self) -> List[Tuple]:
"""Получение списка всех пользователей."""
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute("SELECT * FROM users")
return cursor.fetchall()
def get_user_messages(self, tg_id: int) -> Optional[Tuple]:
"""Получение статистики сообщений пользователя."""
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,))
result = cursor.fetchone()
if not result:
return None
user_id = result[0]
cursor.execute("SELECT * FROM user_messages WHERE user_id = ?", (user_id,))
return cursor.fetchone()
def ban_user(self, tg_id: int) -> None:
"""Блокировка пользователя."""
self.update_user(tg_id, user="ban")
def unban_user(self, tg_id: int) -> None:
"""Разблокировка пользователя."""
self.update_user(tg_id, user="user")
def promote_user(self, tg_id: int, new_role: str) -> None:
"""Изменение роли пользователя."""
valid_roles = ["user", "moderator", "admin", "so-owner", "owner"]
if new_role in valid_roles:
self.update_user(tg_id, user=new_role)
def get_top_active_users(self, limit: int = 10) -> List[Tuple]:
"""Получение самых активных пользователей по количеству сообщений."""
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute('''
SELECT u.tg_id, u.username, um.total_messages
FROM users u
JOIN user_messages um ON u.user_id = um.user_id
ORDER BY um.total_messages DESC
LIMIT ?
''', (limit,))
return cursor.fetchall()
def assign_character(self, tg_id: int, character_id: int, game: str = "genshin") -> bool:
"""Назначение персонажа пользователю."""
table = "characters" if game == "genshin" else "characters_hsr"
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute(f"SELECT user_id FROM {table} WHERE id = ?", (character_id,))
result = cursor.fetchone()
if result and result[0] is not None:
return False # Персонаж уже занят
cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,))
user_id = cursor.fetchone()
if not user_id:
return False # Пользователь не найден
cursor.execute(f"UPDATE {table} SET user_id = ?, status = 'Занят' WHERE id = ?", (user_id[0], character_id))
db.commit()
return True
def free_character(self, character_id: int, game: str = "genshin") -> None:
"""Освобождение персонажа."""
table = "characters" if game == "genshin" else "characters_hsr"
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute(f"UPDATE {table} SET user_id = NULL, status = 'Свободно' WHERE id = ?", (character_id,))
db.commit()
def get_user_characters(self, tg_id: int, game: str = "genshin") -> List[Tuple]:
"""Получение списка персонажей пользователя."""
table = "characters" if game == "genshin" else "characters_hsr"
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,))
user_id = cursor.fetchone()
if not user_id:
return []
cursor.execute(f"SELECT * FROM {table} WHERE user_id = ?", (user_id[0],))
return cursor.fetchall()
def search_users(self, query: str) -> List[Tuple]:
"""Поиск пользователей по имени или юзернейму."""
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute("""
SELECT * FROM users
WHERE username LIKE ? OR first_name LIKE ? OR last_name LIKE ?
""", (f"%{query}%", f"%{query}%", f"%{query}%"))
return cursor.fetchall()
def get_character_by_name(self, name: str, game: str = "genshin") -> Optional[Tuple]:
"""Поиск персонажа по имени."""
table = "characters" if game == "genshin" else "characters_hsr"
with sqlite3.connect(self.db_name) as db:
cursor = db.cursor()
cursor.execute(f"SELECT * FROM {table} WHERE name = ?", (name,))
return cursor.fetchone()

View File

@@ -1,153 +1,115 @@
# BotLibrary/system/bot_edit.py # BotLibrary/system/bot_edit.py
# Под-пакет установки настроек бота # Под-пакет установки настроек бота
from aiogram.types import ChatAdministratorRights from aiogram import Bot
from ProjectsFiles import BotEdit from ProjectsFiles import BotEdit
from .bots import bot
# Настройка логирования
log_type = "Edit"
# Настройка экспорта из модуля # Настройка экспорта из модуля
__all__ = ("set_adm_rights", "set_bot_name", "set_bot_description", "set_bot_short_description") __all__ = ("BotRights",)
# Функция установки прав администратора class BotRights:
async def set_adm_rights(anonym: bool = BotEdit.is_anonymous,
manage_chat: bool = BotEdit.manage_chat,
delete_msg: bool = BotEdit.delete_messages,
manage_video_chats: bool = BotEdit.manage_video_chats,
restrict_members: bool = BotEdit.restrict_members,
promote_members: bool = BotEdit.promote_members,
change_info: bool = BotEdit.change_info,
invite_users: bool = BotEdit.invite_users,
post_stories: bool = BotEdit.post_stories,
edit_stories: bool = BotEdit.edit_stories,
delete_stories: bool = BotEdit.delete_stories,
post_messages: bool = BotEdit.post_messages,
edit_messages: bool = BotEdit.edit_messages,
pin_messages: bool = BotEdit.pin_messages,
manage_topics: bool = BotEdit.manage_topics,) -> None:
""" """
Устанавливает права администратора для бота, если они отличаются от текущих. Класс для установки прав администратора и метаинформации бота (имя, описания).
Все через конфиги!!!
:param anonym: Позволяет ли боту быть анонимным.
:param manage_chat: Разрешение на управление чатом.
:param delete_msg: Разрешение на удаление сообщений.
:param manage_video_chats: Разрешение на управление видеочатами.
:param restrict_members: Разрешение на ограничение участников (мут, бан).
:param promote_members: Разрешение на назначение администраторов.
:param change_info: Разрешение на изменение информации о группе/канале.
:param invite_users: Разрешение на приглашение новых участников.
:param post_stories: Разрешение на публикацию историй.
:param edit_stories: Разрешение на редактирование историй.
:param delete_stories: Разрешение на удаление историй.
:param post_messages: Разрешение на публикацию сообщений (только для каналов).
:param edit_messages: Разрешение на редактирование сообщений (только для каналов).
:param pin_messages: Разрешение на закрепление сообщений.
:param manage_topics: Разрешение на управление темами (в супергруппах).
:return: Изменение прав администратора
""" """
rights = ChatAdministratorRights( @staticmethod
is_anonymous=anonym, async def set_administrator_rights(bot: Bot) -> None:
can_manage_chat=manage_chat, """
can_delete_messages=delete_msg, Установка прав администратора в чатах.
can_manage_video_chats=manage_video_chats,
can_restrict_members=restrict_members,
can_promote_members=promote_members,
can_change_info=change_info,
can_invite_users=invite_users,
can_post_stories=post_stories,
can_edit_stories=edit_stories,
can_delete_stories=delete_stories,
can_post_messages=post_messages,
can_edit_messages=edit_messages,
can_pin_messages=pin_messages,
can_manage_topics=manage_topics,
)
# Применяем права только в случае изменения :param bot: Базовый объект бота.
current_rights = await bot.get_my_default_administrator_rights() :return: Измененные права по конфигу.
if current_rights != rights: """
await bot.set_my_default_administrator_rights(rights) from aiogram.types import ChatAdministratorRights
else: rights: ChatAdministratorRights = ChatAdministratorRights(
return is_anonymous=BotEdit.is_anonymous,
can_manage_chat=BotEdit.manage_chat,
can_delete_messages=BotEdit.delete_messages,
can_manage_video_chats=BotEdit.manage_video_chats,
can_restrict_members=BotEdit.restrict_members,
can_promote_members=BotEdit.promote_members,
can_change_info=BotEdit.change_info,
can_invite_users=BotEdit.invite_users,
can_post_stories=BotEdit.post_stories,
can_edit_stories=BotEdit.edit_stories,
can_delete_stories=BotEdit.delete_stories,
can_post_messages=BotEdit.post_messages,
can_edit_messages=BotEdit.edit_messages,
can_pin_messages=BotEdit.pin_messages,
can_manage_topics=BotEdit.manage_topics,
)
current_rights: ChatAdministratorRights = await bot.get_my_default_administrator_rights()
if current_rights != rights:
await bot.set_my_default_administrator_rights(rights)
# Функция установки имени бота с проверкой на ограничения @staticmethod
async def set_bot_name(new_name: str = BotEdit.name) -> None: async def set_name(bot: Bot) -> None:
""" """
Устанавливает имя бота, если оно отличается от текущего и соответствует ограничениям. Установка имени бота.
:param new_name: Новое имя бота (config) :param bot: Базовый объект бота.
:return: Имя бота :return: Измененное имя бота.
""" """
# Импортируем Logs внутри функции, чтобы избежать циклического импорта current_name: str = (await bot.get_me()).first_name
from ..loggers.custom_loggers import Logs new_name: str = str(BotEdit.name)
# Получаем текущее имя бота if not (1 <= len(new_name) <= 32):
current_name = (await bot.get_me()).first_name from ..loggers import Logs
Logs.error(log_type="SET_NAME", user="BOT", text="Имя бота должно быть от 1 до 32 символов.")
return
# Проверка длины имени if current_name != new_name:
if len(new_name) < 1 or len(new_name) > 32: await bot.set_my_name(new_name)
Logs.error(log_type=log_type, user="NAME_BOT", text="Имя бота должно быть от 1 до 32 символов.")
return # Выходим из функции, если имя некорректно
# Проверяем, совпадает ли текущее имя с тем, которое мы хотим установить @staticmethod
if current_name != new_name: async def set_description(bot: Bot) -> None:
await bot.set_my_name(new_name) """
else: Установка описания бота.
return
:param bot: Базовый объект бота.
:return: Измененное описание бота.
"""
from aiogram.types import BotDescription
current_description: BotDescription = await bot.get_my_description()
new_description: str = str(BotEdit.description)
# Функция установки описания бота с проверкой на ограничения if not (0 < len(new_description) <= 255):
async def set_bot_description(new_description: str = BotEdit.description) -> None: from ..loggers import Logs
""" Logs.error(log_type="SET_DESCRIPTION", user="BOT", text="Описание должно быть от 1 до 255 символов.")
Устанавливает описание бота, если оно отличается от текущего и соответствует ограничениям. return
:param new_description: Новое описание для бота (config) if current_description != new_description:
:return: Описание бота await bot.set_my_description(description=new_description)
"""
# Импортируем Logs внутри функции, чтобы избежать циклического импорта
from ..loggers.custom_loggers import Logs
# Получаем текущее описание бота @staticmethod
current_description = await bot.get_my_description() async def set_short_description(bot: Bot) -> None:
"""
Установка описания виджета.
# Проверка длины описания :param bot: Базовый объект бота.
if len(new_description) > 255 or len(new_description)==0: :return: Измененное описание виджета бота.
Logs.error(log_type=log_type, user="DISCRIPT", text="Короткое описание бота не может превышать 255 символов или быть равно 0.") """
return # Выходим из функции, если описание некорректно from aiogram.types import BotShortDescription
current_short_description: BotShortDescription = await bot.get_my_short_description()
new_short_description: str = str(BotEdit.short_description)
# Проверяем, совпадает ли текущее описание с тем, которое мы хотим установить if not (0 < len(new_short_description) <= 512):
if current_description != new_description: from ..loggers import Logs
await bot.set_my_description(description=new_description) Logs.error(log_type="SET_SHORT_DESCRIPTION", user="BOT", text="Короткое описание должно быть от 1 до 512 символов.")
else: return
return
if current_short_description != new_short_description:
await bot.set_my_short_description(short_description=new_short_description)
# Функция установки короткого описания бота с проверкой на ограничения @staticmethod
async def set_bot_short_description(new_short_description: str = BotEdit.short_description) -> None: async def all(bot: Bot) -> None:
""" """
Устанавливает короткое описание бота, если оно отличается от текущего и соответствует ограничениям. Применяет все настройки: права, имя, описание и короткое описание.
:param new_short_description: Новое описание виджета для бота (config) :param bot: Базовый объект бота.
:return: Короткое описание бота :return: Изменение всех основных параметров бота.
""" """
# Импортируем Logs внутри функции, чтобы избежать циклического импорта await BotRights.set_administrator_rights(bot)
from ..loggers.custom_loggers import Logs await BotRights.set_name(bot)
await BotRights.set_description(bot)
# Получаем текущее короткое описание бота await BotRights.set_short_description(bot)
current_short_description = await bot.get_my_short_description()
# Проверка длины короткого описания
if len(new_short_description) > 512 or len(new_short_description) == 0:
Logs.error(log_type=log_type, user="SHORT_DISCRIPT", text="Описание виджета не может превышать 512 символов или быть равно 0.")
return # Выходим из функции, если короткое описание некорректно
# Проверяем, совпадает ли текущее короткое описание с тем, которое мы хотим установить
if current_short_description != new_short_description:
await bot.set_my_short_description(short_description=new_short_description)
else:
return

View File

@@ -17,23 +17,14 @@ dp = Dispatcher()
dp["started_at"] = get_host_time() dp["started_at"] = get_host_time()
dp["started_at_city"] = get_city_time() dp["started_at_city"] = get_city_time()
dp["is_active"] = True # Флаг активности бота dp["is_active"] = True # Флаг активности бота
dp["logs"] = []
dp["users"] = {}
dp["admins"] = {}
dp["sessions"] = {}
dp["task_queue"] = []
dp["configs"] = {"max_connections": 100, "retry_interval": 5, "time_format": BotVar.time_format} dp["configs"] = {"max_connections": 100, "retry_interval": 5, "time_format": BotVar.time_format}
dp["metrics"] = {"messages_received": 0, "messages_sent": 0, "errors": 0} dp["metrics"] = {"messages_received": 0, "messages_sent": 0, "errors": 0}
dp["modules"] = {}
dp["state"] = {}
dp["scheduler"] = []
dp["handlers"] = {"on_message": [], "on_error": []} dp["handlers"] = {"on_message": [], "on_error": []}
dp["storage"] = {}
dp["database"] = "SQLite3" dp["database"] = "SQLite3"
# Создание экземпляра бота и его настройка # Создание экземпляра бота и его настройка
bot = Bot(token=bot_token, default=DefaultBotProperties( bot: Bot = Bot(token=bot_token, default=DefaultBotProperties(
parse_mode=BotVar.parse_mode, parse_mode=BotVar.parse_mode,
disable_notification=BotVar.disable_notification, disable_notification=BotVar.disable_notification,
protect_content=BotVar.protect_content, protect_content=BotVar.protect_content,
@@ -57,27 +48,31 @@ class BotInfo:
""" """
id: int = None id: int = None
first_name: str = None first_name: str = None
bot_owner: str = BotVar
last_name: str = None last_name: str = None
username: str = None username: str = None
description: str = None description: str = ''
short_description: str = None short_description: str = ''
can_join_groups: bool = False
can_read_all_group_messages: bool = False
language_code: str = BotVar.language language_code: str = BotVar.language
prefix: str = BotVar.prefix prefix: str = BotVar.prefix
bot_owner: str = BotVar
is_premium: bool = False is_premium: bool = False
added_to_attachment_menu: bool = False added_to_attachment_menu: bool = False
supports_inline_queries: bool = False supports_inline_queries: bool = False
can_connect_to_business: bool = False can_connect_to_business: bool = False
has_main_web_app: bool = False has_main_web_app: bool = False
can_join_groups: bool = False
can_read_all_group_messages: bool = False
@classmethod @classmethod
def update(cls, bot_info) -> None: async def info(cls, bots: Bot = bot) -> dict:
""" """
Обновляет данные о боте. Получает информацию о боте через API и обновляет данные в классе.
:param bot_info: Объект с данными о боте, полученные через API Telegram.
:param bots: Объект бота
:return: Словарь с данными о боте
""" """
bot_info = await bots.get_me()
cls.id = bot_info.id cls.id = bot_info.id
cls.first_name = bot_info.first_name cls.first_name = bot_info.first_name
cls.last_name = bot_info.last_name cls.last_name = bot_info.last_name
@@ -93,31 +88,28 @@ class BotInfo:
cls.can_join_groups = getattr(bot_info, 'can_join_groups', False) cls.can_join_groups = getattr(bot_info, 'can_join_groups', False)
cls.can_read_all_group_messages = getattr(bot_info, 'can_read_all_group_messages', False) cls.can_read_all_group_messages = getattr(bot_info, 'can_read_all_group_messages', False)
return cls.to_dict()
# Функция получения информации о боте @classmethod
async def bot_get_info(bots: Bot = bot) -> dict: def to_dict(cls) -> dict:
""" """
Получает информацию о боте и обновляет данные в классе BotInfo. Возвращает текущие данные в виде словаря.
:param bots: Получение объекта бота в функцию. """
:return: Словарь с данными о боте. return {
""" 'id': cls.id,
bot_info_data = await bots.get_me() 'first_name': cls.first_name,
BotInfo.update(bot_info_data) 'last_name': cls.last_name,
return { 'username': cls.username,
'bot_info': bot_info_data, 'description': cls.description,
'id': bot_info_data.id, 'short_description': cls.short_description,
'first_name': bot_info_data.first_name, 'language_code': cls.language_code,
'last_name': bot_info_data.last_name, 'prefix': cls.prefix,
'username': bot_info_data.username, 'bot_owner': cls.bot_owner,
'description': getattr(bot_info_data, 'description', ''), 'is_premium': cls.is_premium,
'short_description': getattr(bot_info_data, 'short_description', ''), 'added_to_attachment_menu': cls.added_to_attachment_menu,
'language_code': bot_info_data.language_code, 'supports_inline_queries': cls.supports_inline_queries,
'prefix': BotVar.prefix, 'can_connect_to_business': cls.can_connect_to_business,
'is_premium': bot_info_data.is_premium, 'has_main_web_app': cls.has_main_web_app,
'added_to_attachment_menu': bot_info_data.added_to_attachment_menu, 'can_join_groups': cls.can_join_groups,
'supports_inline_queries': bot_info_data.supports_inline_queries, 'can_read_all_group_messages': cls.can_read_all_group_messages,
'can_connect_to_business': bot_info_data.can_connect_to_business, }
'has_main_web_app': bot_info_data.has_main_web_app,
'can_join_groups': getattr(bot_info_data, 'can_join_groups', False),
'can_read_all_group_messages': getattr(bot_info_data, 'can_read_all_group_messages', False),
}

View File

@@ -2,51 +2,47 @@
# Создание пустых директорий при первом запуске # Создание пустых директорий при первом запуске
import os import os
from ProjectsFiles import ProjectPath, TypeDirectory
from typing import List from typing import List
from ProjectsFiles import ProjectPath, TypeDirectory
# Настройка экспорта из модуля # Настройка экспорта из модуля
__all__ = ("create_directories", "setup_directories", "create_directory") __all__ = ("Directory",)
class Directory:
@staticmethod
async def create_directory(directory: str) -> None:
"""
Создает директории, если они еще не существуют.
# Функция создания директории :param directory: Путь к базовой директории.
async def create_directory(directory : str) -> None: :return: Создание директорий по определенному пути.
""" """
Создает директории, если они еще не существуют. os.makedirs(directory, exist_ok=True)
:param directory: Путь к базовой директории. @staticmethod
:return: Создание директорий по определенному пути. async def create_directories(base_directory: str, subdirectories: List[str]) -> None:
""" """
os.makedirs(directory) Создает указанные поддиректории в указанной базовой директории.
:param base_directory: Путь к базовой директории.
:param subdirectories: Список поддиректорий, которые необходимо создать.
:return: Создание директорий по определенному пути.
"""
# Создание директорий и файлов в каждой из них
for subdirectory in subdirectories:
directory_path = os.path.join(base_directory, subdirectory)
# Функция создания поддиректорий # Проверка, существует ли директория, если нет - создаём
async def create_directories(base_directory: str, subdirectories: List[str]) -> None: os.makedirs(directory_path, exist_ok=True)
"""
Создает указанные поддиректории в указанной базовой директории.
:param base_directory: Путь к базовой директории. @staticmethod
:param subdirectories: Список поддиректорий, которые необходимо создать. async def setup() -> None:
:return: Создание директорий по определенному пути. """
""" Настройка начальных пустых директорий для проекта.
# Создание директорий и файлов в каждой из них
for subdirectory in subdirectories:
directory_path = os.path.join(base_directory, subdirectory)
# Проверка, существует ли директория, если нет - создаём :return: Создание системы директорий по определенному пути.
if not os.path.exists(directory_path): """
os.makedirs(directory_path) # Создание директорий для медиа файлов
await Directory.create_directories(ProjectPath.personal_media, TypeDirectory.media_directories)
await Directory.create_directories(ProjectPath.received_media, TypeDirectory.media_directories)
# Функция установки начальных директорий await Directory.create_directories(ProjectPath.received_avatars, TypeDirectory.avatar_directories)
async def setup_directories() -> None:
"""
Настройка начальных пустых директорий для проекта.
:return: Создание системы директорий по определенному пути.
"""
# Создание директорий для медиа файлов
await create_directories(ProjectPath.personal_media, TypeDirectory.media_directories)
await create_directories(ProjectPath.received_media, TypeDirectory.media_directories)
await create_directories(ProjectPath.received_avatars, TypeDirectory.avatar_directories)
# await create_directories(ProjectPath.msg, TypeDirectory.msg_directories)

View File

@@ -1,7 +1,6 @@
# BotLibrary/timer/start_time.py # BotLibrary/timer/start_time.py
# Получение времени по разным часовым поясам # Получение времени по разным часовым поясам
import pytz
from datetime import datetime from datetime import datetime
from tzlocal import get_localzone from tzlocal import get_localzone
from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.schedulers.asyncio import AsyncIOScheduler
@@ -14,7 +13,6 @@ __all__ = ("scheduler", "get_city_time", "get_host_time")
scheduler = AsyncIOScheduler(timezone=get_localzone().key) scheduler = AsyncIOScheduler(timezone=get_localzone().key)
# Функция получение иного времени
def get_city_time(city: str = 'Europe/Moscow', def get_city_time(city: str = 'Europe/Moscow',
time_format: str = BotVar.time_format) -> str: time_format: str = BotVar.time_format) -> str:
""" """
@@ -24,13 +22,13 @@ def get_city_time(city: str = 'Europe/Moscow',
:param time_format: Шаблон форматирования времени (конфиг). :param time_format: Шаблон форматирования времени (конфиг).
:return: Строка, представляющая время в формате, заданном в BotVar.time_format. :return: Строка, представляющая время в формате, заданном в BotVar.time_format.
""" """
from pytz import timezone
# Устанавливаем временную зону для Москвы # Устанавливаем временную зону для Москвы
city_tz = pytz.timezone(city) city_tz = timezone(city)
# Возвращаем строку с форматом времени # Возвращаем строку с форматом времени
return datetime.now(city_tz).strftime(time_format) return datetime.now(city_tz).strftime(time_format)
# Функция получение времени хоста
def get_host_time(time_format: str = BotVar.time_format) -> str: def get_host_time(time_format: str = BotVar.time_format) -> str:
""" """
Получение текущего времени хоста (локального времени). Получение текущего времени хоста (локального времени).

View File

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

View File

@@ -13,8 +13,9 @@ async def normal_words(word: str) -> str:
""" """
try: try:
return word.lower().capitalize() return word.lower().capitalize()
except Exception as e: except Exception as e:
# Импортируем Logs внутри функции, чтобы избежать циклического импорта # Импортируем Logs внутри функции, чтобы избежать циклического импорта
from ..loggers.custom_loggers import Logs from ..loggers.logs import Logs
Logs.error(text=f"Ошибка в нормализировании слова: {e}", log_type="NormalWord") Logs.error(text=f"Ошибка в нормализировании слова: {e}", log_type="NormalWord")
return word return word

View File

@@ -1,13 +1,9 @@
# BotLibrary/validators/url_valid.py # BotLibrary/validators/url_valid.py
# Валидатор ссылок на регулярных выражениях # Валидатор ссылок на регулярных выражениях
import re
# Настройка экспорта из этого модуля # Настройка экспорта из этого модуля
__all__ = ("valid_url", "url_to_text") __all__ = ("valid_url", "url_to_text")
# Функция определения является ли строка ссылкой
def valid_url(url: str) -> bool: def valid_url(url: str) -> bool:
""" """
Проверяет, является ли строка валидной ссылкой (URL). Проверяет, является ли строка валидной ссылкой (URL).
@@ -15,7 +11,8 @@ def valid_url(url: str) -> bool:
:param url: Строка для проверки. :param url: Строка для проверки.
:return: True, если строка является валидным URL, иначе False. :return: True, если строка является валидным URL, иначе False.
""" """
url_pattern = re.compile( from re import compile
url_pattern = compile(
r'^(https?://)?' # Протокол (http или https, необязателен) r'^(https?://)?' # Протокол (http или https, необязателен)
r'([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}' # Домен r'([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}' # Домен
r'(:\d+)?' # Порт (необязателен) r'(:\d+)?' # Порт (необязателен)
@@ -24,7 +21,6 @@ def valid_url(url: str) -> bool:
return bool(url_pattern.match(url)) return bool(url_pattern.match(url))
# Функция, что дает тексту ссылку на HTML
def url_to_text(text: str, url: str) -> str: def url_to_text(text: str, url: str) -> str:
""" """
Преобразует текст в HTML ссылку с указанным URL. Преобразует текст в HTML ссылку с указанным URL.
@@ -45,7 +41,7 @@ def url_to_text(text: str, url: str) -> str:
except ValueError as e: except ValueError as e:
# Импортируем Logs внутри функции, чтобы избежать циклического импорта # Импортируем Logs внутри функции, чтобы избежать циклического импорта
from ..loggers.custom_loggers import Logs from ..loggers.logs import Logs
# Логируем ошибку с использованием Logs.error, как указано # Логируем ошибку с использованием Logs.error, как указано
Logs.error(text=f"Ошибка при создании ссылки: {e}", log_type="InvalidURL") Logs.error(text=f"Ошибка при создании ссылки: {e}", log_type="InvalidURL")
raise e # Перебрасываем ошибку выше для дальнейшей обработки или уведомления raise e # Перебрасываем ошибку выше для дальнейшей обработки или уведомления

View File

@@ -7,7 +7,6 @@ from aiogram.types import Message
__all__ = ("username", "username_to_text") __all__ = ("username", "username_to_text")
# Функция получения юзера или ID пользователя
def username(message: Message) -> str: def username(message: Message) -> str:
""" """
Возвращает юзернейм пользователя из сообщения, или ID, если юзернейм не указан. Возвращает юзернейм пользователя из сообщения, или ID, если юзернейм не указан.
@@ -26,7 +25,6 @@ def username(message: Message) -> str:
raise e # Перебрасываем ошибку выше для дальнейшей обработки raise e # Перебрасываем ошибку выше для дальнейшей обработки
# Функция получение имени пользователя + ссылка на него
def username_to_text(message: Message) -> str: def username_to_text(message: Message) -> str:
""" """
Преобразует информацию о пользователе в строку с HTML-ссылкой. Преобразует информацию о пользователе в строку с HTML-ссылкой.

View File

@@ -14,15 +14,6 @@
-... -...
Второй модуль loggers: Второй модуль loggers:
logs.py - создает логгеры, три кастомных уровня и 4 обычных. Он может создавать logs.py - создает логгеры, три кастомных уровня и 4 обычных. Он может создавать
-NEW_USER -NEW_USER

View File

@@ -1,4 +0,0 @@
# GUI/__init__.py
# Инициализация пакета GUI, для работы с графическим интерфейсом
from .console import *

View File

@@ -1,167 +0,0 @@
from tkinter import PhotoImage
import customtkinter as ctk # модуль для создания стилизованных интерфейсов
from PIL import Image, ImageTk
from BotLibrary import * # импорт настроек бота (например, имени, фамилии, и имени пользователя)
# Создание роутера и настройка экспорта
__all__ = ("App", )
command_text = "GUI"
# Настройка внешнего вида интерфейса
ctk.set_appearance_mode("System") # Установка режима внешнего вида: "System", "Dark" или "Light"
ctk.set_default_color_theme("blue") # Установка цветовой темы: "blue", "green", "dark-blue"
# Класс приложения, наследующий от customtkinter.CTk
class App(ctk.CTk):
def __init__(self):
super().__init__() # Инициализация базового класса
# Настройка главного окна
self.title(f"{BotInfo.first_name} {BotInfo.last_name} - @{BotInfo.username}") # Заголовок окна
self.geometry(f"{700}x{580}") # Размер окна (ширина x высота)
# Настройка сетки (разметка окна на ячейки)
self.grid_columnconfigure(0, weight=1) # Установка растяжения для 1-го столбца (боковая панель)
self.grid_columnconfigure(1, weight=9) # Установка растяжения для 2-го столбца (содержимое вкладок)
self.grid_rowconfigure((0, 1), weight=1) # Установка растяжения для первых трех строк
# Создание боковой панели
self.sidebar_frame = ctk.CTkFrame(self, width=100, corner_radius=0) # Рамка боковой панели с уменьшенной шириной
self.sidebar_frame.grid(row=0, column=0, rowspan=4, sticky="nsew") # Расположение в сетке
self.sidebar_frame.grid_rowconfigure(4, weight=1) # Установка растяжения для 4-й строки
# Логотип на боковой панели
self.logo_label = ctk.CTkLabel(self.sidebar_frame, text=BotInfo.username,
font=ctk.CTkFont(size=16, weight="bold")) # Метка с текстом
self.logo_label.grid(row=0, column=0, padx=10, pady=(20, 10)) # Расположение метки в сетке
# Кнопки на боковой панели
self.sidebar_button_1 = ctk.CTkButton(self.sidebar_frame, text="Консоль",
command=self.switch_tab_console,
font=ctk.CTkFont(size=14))
self.sidebar_button_1.grid(row=1, column=0, padx=10, pady=10) # Первая кнопка
self.sidebar_button_2 = ctk.CTkButton(self.sidebar_frame, text="База данных",
command=self.switch_tab_database,
font=ctk.CTkFont(size=14))
self.sidebar_button_2.grid(row=2, column=0, padx=10, pady=10) # Вторая кнопка
self.sidebar_button_3 = ctk.CTkButton(self.sidebar_frame, text="Иное",
command=self.switch_tab_other,
font=ctk.CTkFont(size=14))
self.sidebar_button_3.grid(row=3, column=0, padx=10, pady=10) # Третья кнопка
# Новые кнопки "Запуск" и "Выключение"
self.start_button = ctk.CTkButton(self.sidebar_frame, text="Запуск", command=self.start_button_click,
font=ctk.CTkFont(size=14))
self.start_button.grid(row=5, column=0, padx=10, pady=10) # Кнопка "Запуск"
self.stop_button = ctk.CTkButton(self.sidebar_frame, text="Выключение",
command=self.stop_button_click,
font=ctk.CTkFont(size=14))
self.stop_button.grid(row=6, column=0, padx=10, pady=10) # Кнопка "Выключение"
# Элементы управления режимами и масштабированием интерфейса
self.appearance_mode_label = ctk.CTkLabel(self.sidebar_frame, text="Тема UI", anchor="w",
font=ctk.CTkFont(size=12))
self.appearance_mode_label.grid(row=7, column=0, padx=10, pady=(10, 0)) # Метка для выбора темы
self.appearance_mode_optionemenu = ctk.CTkOptionMenu(
self.sidebar_frame, values=["Светлая", "Темная", "Система"], command=self.change_appearance_mode_event
) # Меню выбора темы
self.appearance_mode_optionemenu.grid(row=8, column=0, padx=10, pady=(10, 10)) # Расположение меню
self.scaling_label = ctk.CTkLabel(self.sidebar_frame, text="Масштабирование",
anchor="w", font=ctk.CTkFont(size=12)) # Метка для масштабирования
self.scaling_label.grid(row=9, column=0, padx=10, pady=(10, 0)) # Расположение метки
self.scaling_optionemenu = ctk.CTkOptionMenu(
self.sidebar_frame, values=["80%", "90%", "100%", "110%", "120%"], command=self.change_scaling_event
) # Меню выбора масштаба
self.scaling_optionemenu.grid(row=10, column=0, padx=10, pady=(10, 20)) # Расположение меню
# Создание вкладок
self.tabview = ctk.CTkTabview(self, width=250) # Виджет вкладок
self.tabview.grid(row=0, column=1, rowspan=3, columnspan=2, padx=(20, 0), pady=(20, 0),
sticky="nsew") # Растягиваем на все пустое пространство
self.tabview.add("Консоль") # Первая вкладка
self.tabview.add("База данных") # Вторая вкладка
self.tabview.add("Иное") # Третья вкладка
# Вкладка "Консоль" с текстовым полем
self.textbox_tab_1 = ctk.CTkTextbox(self.tabview.tab("Консоль"))
self.textbox_tab_1.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
self.textbox_tab_1.insert("0.0", "Текст в Textbox на вкладке Консоль\n\n")
# Вкладка "База данных" с текстовым полем
self.textbox_tab_2 = ctk.CTkTextbox(self.tabview.tab("База данных"))
self.textbox_tab_2.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
self.textbox_tab_2.insert("0.0", "Текст в Textbox на вкладке База данных\n\n")
# Вкладка "Иное" с текстовым полем
self.textbox_tab_3 = ctk.CTkTextbox(self.tabview.tab("Иное"))
self.textbox_tab_3.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
self.textbox_tab_3.insert("0.0", "Текст в Textbox на вкладке Иное\n\n")
# Растягиваем строки и столбцы вкладок
self.tabview.grid_rowconfigure(0, weight=1) # Растягиваем строку, где находится текстовое поле
self.tabview.grid_columnconfigure(0, weight=1) # Растягиваем столбец, где находится текстовое поле
# Убедитесь, что в каждой из вкладок разрешено растяжение:
self.tabview.tab("Консоль").grid_rowconfigure(0, weight=1)
self.tabview.tab("База данных").grid_rowconfigure(0, weight=1)
self.tabview.tab("Иное").grid_rowconfigure(0, weight=1)
self.tabview.tab("Консоль").grid_columnconfigure(0, weight=1)
self.tabview.tab("База данных").grid_columnconfigure(0, weight=1)
self.tabview.tab("Иное").grid_columnconfigure(0, weight=1)
# Настройка элементов
self.appearance_mode_optionemenu.set("Темная") # Режим отображения установлен на "Dark"
self.scaling_optionemenu.set("100%") # Масштаб интерфейса установлен на 100%
self.update() # Принудительное обновление окна после создания
def change_appearance_mode_event(self, new_appearance_mode: str):
# Метод для изменения режима отображения интерфейса
if new_appearance_mode == "Светлая":
ctk.set_appearance_mode("Light")
elif new_appearance_mode == "Темная":
ctk.set_appearance_mode("Dark")
elif new_appearance_mode == "Система":
ctk.set_appearance_mode("System")
def change_scaling_event(self, new_scaling: str):
# Метод для изменения масштаба интерфейса
new_scaling_float = int(new_scaling.replace("%", "")) / 100
# Преобразование процента масштаба в дробное число
ctk.set_widget_scaling(new_scaling_float)
# Применение нового масштаба ко всем виджетам
def switch_tab_console(self):
# Переключение на вкладку "Консоль"
self.tabview.grid(row=0, column=1, rowspan=3, columnspan=2, padx=(20, 0), pady=(20, 0), sticky="nsew")
self.tabview.set("Консоль")
def switch_tab_database(self):
# Переключение на вкладку "База данных"
# Для возвращения вкладок
self.tabview.grid(row=0, column=1, rowspan=3, columnspan=2, padx=(20, 0), pady=(20, 0), sticky="nsew")
self.tabview.set("База данных")
def switch_tab_other(self):
# Переключение на вкладку "Иное"
self.tabview.grid(row=0, column=1, rowspan=3, columnspan=2, padx=(20, 0), pady=(20, 0), sticky="nsew")
self.tabview.set("Иное")
def start_button_click(self):
# Обработчик для кнопки "Запуск"
print("Запуск")
def stop_button_click(self):
# Обработчик для кнопки "Выключение"
print("Выключение")
# Начало кода и запуск GUI
if __name__ == "__main__":
app = App()
app.mainloop()

View File

@@ -1,74 +0,0 @@
import sys
import customtkinter as ctk
import asyncio
import threading
# Ваши настройки и модули
from BotLibrary import *
from BotCode.routers import router as main_router
from BotCode.routers import set_commands
# Класс для перенаправления стандартного вывода в текстовое поле
class Logger:
def __init__(self, text_widget):
self.text_widget = text_widget
def write(self, message):
self.text_widget.insert("end", message)
self.text_widget.see("end") # Прокрутка к последнему сообщению
def flush(self):
pass
# Основная функция для запуска бота
async def main_bot():
just_fix_windows_console() # Подключение ANSI в Windows CMD
dp.include_router(main_router) # Подключение главного роутера
await set_all() # Установка настроек бота
await set_commands() # Установка команд бота
await bot_get_info() # Получение информации о боте
# Логирование в консоль и текстовое поле
logger.add(sys.stderr, colorize=True, format=logs_text, level="INFO")
logger.info(f"Начало запуска бота @{BotInfo.username}...")
bot_info_out() # Включение опроса бота
await dp.start_polling(bot) # Запуск бота
# Класс графического интерфейса
class BotConsoleWindow(ctk.CTk):
def __init__(self):
super().__init__()
# Настройка окна
self.title("Bot Console")
self.geometry("800x600")
# Создание текстового поля для логов
self.log_text = ctk.CTkTextbox(self, wrap="word", width=780, height=500)
self.log_text.pack(padx=10, pady=10)
# Перенаправление вывода в текстовое поле
sys.stdout = Logger(self.log_text)
sys.stderr = Logger(self.log_text)
# Кнопка запуска бота
self.start_button = ctk.CTkButton(self, text="Запустить бота", command=self.start_bot)
self.start_button.pack(pady=10)
def start_bot(self):
self.start_button.configure(state="disabled") # Отключить кнопку
threading.Thread(target=self.run_bot, daemon=True).start()
def run_bot(self):
asyncio.run(main_bot())
self.start_button.configure(state="normal") # Включить кнопку после завершения
if __name__ == "__main__":
app = BotConsoleWindow()
app.mainloop()

View File

@@ -2,7 +2,7 @@
# Файл-хранилище всех секретных токенов и ключей # Файл-хранилище всех секретных токенов и ключей
# Токены от ботов телеграмма # Токены от ботов телеграмма
BOT_TOKEN=7162745909:AAF9023WMRMm6uZYldtUcgqB_al4VmZpH1c BOT_TOKEN=6393974380:AAFM0F3opRVzCvnhfmNaaNeW5-gJKXwSicU
BOT1_TOKEN=7193685715:AAHFEnFreZGLQcHj8_wdWYJ2FLPrB-A-hzY BOT1_TOKEN=7193685715:AAHFEnFreZGLQcHj8_wdWYJ2FLPrB-A-hzY
BOT2_TOKEN=8076305634:AAGNoo4N-WVP9mbeD76G7SLClSsySw23nGw BOT2_TOKEN=8076305634:AAGNoo4N-WVP9mbeD76G7SLClSsySw23nGw

View File

@@ -30,11 +30,11 @@ class BotEdit:
Класс для хранения данных о боте: имя, описание, разрешения и настройки. Класс для хранения данных о боте: имя, описание, разрешения и настройки.
""" """
# Разрешение на ведение логов # Разрешение на ведение логов
project_name: str = "Свалка Флуд" project_name: str = "PRIMO"
permission: bool = Permissions.bot_edit permission: bool = Permissions.bot_edit
name: str = "Стартовый бот" name: str = "Первозданная Жемчужина"
description: str = "Описание бота" description: str = "Ваш помощник в удивительные миры! Prod. by:『@verdise』"
short_description: str = "Описание виджета" short_description: str = "Тех.поддержка: @verdise"
is_anonymous: bool = False is_anonymous: bool = False
manage_chat: bool = True manage_chat: bool = True
@@ -61,7 +61,8 @@ class BotVar:
encod: str = "utf-8" encod: str = "utf-8"
language: str = "Python3-Aiogram" language: str = "Python3-Aiogram"
time_format: str = "%Y-%m-%d %H:%M:%S" time_format: str = "%Y-%m-%d %H:%M:%S"
bd_names: str = 'SQLite3/bd.db' bd_names: str = 'ProjectsFiles/database/users.db'
bd_path: str = 'ProjectsFiles/database'
prefix: Tuple[str, ...] = ('$', '!', '.', '%', '&', ':', '|', '+', '-', '/', '~', '?') prefix: Tuple[str, ...] = ('$', '!', '.', '%', '&', ':', '|', '+', '-', '/', '~', '?')
parse_mode: str = "HTML" # Устанавливаем формат HTML для всех сообщений parse_mode: str = "HTML" # Устанавливаем формат HTML для всех сообщений

View File

@@ -1,5 +0,0 @@
# SQLite3/__init__.py
# Инициализация пакета SQLite3, для базы данных проекта
# Экспортирование модулей во внешние слои проекта
from .bd_func import *

View File

@@ -1,26 +0,0 @@
# SQLite3/bd_func/__init__.py
# Инициализация модуля bd_func, для функция с БД
from aiogram import types
from BotLibrary.validators import username
from ProjectsFiles import Permissions
from .bd_add_user import *
from .bd_get_user import *
from .bd_update_user import *
from .bd_update_user_msg import *
from .bd_user_create import *
from .status_user import *
# Основная обработка SQL
async def base_sql(message: types.Message) -> None:
tg_id = message.from_user.id
usernames = username(message)
first_name = message.from_user.first_name
last_name = message.from_user.last_name
if Permissions.sql_user:
await add_user(tg_id, usernames, first_name, last_name, role="", status="active", user="user")
await update_user(tg_id=tg_id, first_name=first_name, last_name=last_name)
await update_user_messages(message=message)

View File

@@ -1,39 +0,0 @@
# SQLite3/bd_func/bd_add_user.py
# Добавление пользователя в базу данных bd|user
import sqlite3
from ProjectsFiles import BotVar
# Настройка экспорта в модули
__all__ = ("add_user",)
# Функция добавления пользователя с последовательным user_id
async def add_user(tg_id: int, username: str, first_name: str, last_name: str,
role: str, status: str, user: str, bd_name: str = BotVar.bd_names) -> None:
with sqlite3.connect(bd_name) as db:
cursor = db.cursor()
# Проверяем, существует ли пользователь с таким tg_id
cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,))
if cursor.fetchone():
return # Пользователь уже существует, ничего не добавляем
# Находим максимальный user_id
cursor.execute("SELECT MAX(user_id) FROM users")
max_id = cursor.fetchone()[0]
new_user_id = 1 if max_id is None else max_id + 1
# Добавляем нового пользователя
cursor.execute('''
INSERT INTO users (user_id, tg_id, username, first_name, last_name, role, status, user)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (new_user_id, tg_id, username, first_name, last_name, role, status, user))
# Добавляем запись в user_messages
cursor.execute('''
INSERT INTO user_messages (user_id)
VALUES (?)
''', (new_user_id,))
db.commit()

View File

@@ -1,16 +0,0 @@
# SQLite3/bd_func/bd_get_user.py
# Получение информации о пользователе из базы данных bd|user
import sqlite3
from ProjectsFiles import BotVar
# Настройка экспорта в модули
__all__ = ("get_user",)
# Функция для получения данных о пользователе
async def get_user(tg_id: int, bd_name: str = BotVar.bd_names) -> None:
with sqlite3.connect(bd_name) as db:
cursor = db.cursor()
cursor.execute("SELECT * FROM users WHERE tg_id = ?", (tg_id,))
return cursor.fetchone()

View File

@@ -1,39 +0,0 @@
# SQLite3/bd_func/bd_update_user.py
# Обновление данных о пользователях в базу данных bd|user
import sqlite3
from ProjectsFiles import BotVar
# Настройка экспорта в модули
__all__ = ("update_user",)
# Функция обновления пользователя
async def update_user(tg_id: int, username: str = None, first_name: str = None, last_name: str = None,
bd_name: str = BotVar.bd_names, role: str = None, user: str = None) -> None:
updates = []
params = []
if username:
updates.append("username = ?")
params.append(username)
if first_name:
updates.append("first_name = ?")
params.append(first_name)
if last_name:
updates.append("last_name = ?")
params.append(last_name)
if role:
updates.append("role = ?")
params.append(role)
if user:
updates.append("user = ?")
params.append(user)
if updates:
query = f"UPDATE users SET {', '.join(updates)} WHERE tg_id = ?"
params.append(tg_id)
with sqlite3.connect(bd_name) as db:
cursor = db.cursor()
cursor.execute(query, params)
db.commit()

View File

@@ -1,78 +0,0 @@
# SQLite3/bd_func/bd_update_user_msg.py
# Обновление данных о сообщениях пользователя в базу данных bd|message
import sqlite3
from aiogram import types
from datetime import timezone, datetime, timedelta
from BotLibrary import type_msg
from ProjectsFiles import BotVar
# Настройка экспорта в модули
__all__ = ("update_user_messages",)
# Функция обновления статистики сообщений пользователя
async def update_user_messages(message: types.Message, bd_name: str = BotVar.bd_names) -> None:
with sqlite3.connect(bd_name) as db:
cursor = db.cursor()
user_id = message.from_user.id # Используем user_id напрямую
# Проверяем, существует ли запись в user_messages
cursor.execute(
"SELECT last_message_time, messages_per_day, messages_per_week, messages_per_month, total_messages FROM user_messages WHERE user_id = ?",
(user_id,))
result = cursor.fetchone()
# Время сообщения в московском времени
now = message.date.astimezone(timezone(timedelta(hours=3)))
today = now.date()
start_of_week = today - timedelta(days=today.weekday()) # Понедельник текущей недели
current_month = now.month
current_year = now.year
last_message = message.text or type_msg(message)
last_message_id = message.message_id
if result:
last_message_time, messages_per_day, messages_per_week, messages_per_month, total_messages = result
if last_message_time:
last_message_time = datetime.fromisoformat(last_message_time).astimezone(timezone(timedelta(hours=3)))
last_date = last_message_time.date()
last_week = last_date - timedelta(days=last_date.weekday())
last_month = last_message_time.month
last_year = last_message_time.year
# Обнуляем счетчики, если наступил новый день, неделя или месяц
if last_date != today:
messages_per_day = 0
if last_week != start_of_week:
messages_per_week = 0
if last_month != current_month or last_year != current_year:
messages_per_month = 0
else:
messages_per_day, messages_per_week, messages_per_month = 0, 0, 0
# Увеличиваем счетчики
messages_per_day += 1
messages_per_week += 1
messages_per_month += 1
total_messages += 1
else:
# Если записи нет, создаем новую
messages_per_day, messages_per_week, messages_per_month, total_messages = 1, 1, 1, 1
cursor.execute('INSERT INTO user_messages (user_id, last_message, last_message_id, last_message_time, messages_per_day, messages_per_week, messages_per_month, total_messages) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
(user_id, last_message, last_message_id, now.isoformat(), messages_per_day, messages_per_week, messages_per_month, total_messages))
db.commit()
return
# Обновляем существующую запись
cursor.execute('''
UPDATE user_messages
SET last_message = ?, last_message_id = ?, last_message_time = ?,
messages_per_day = ?, messages_per_week = ?, messages_per_month = ?,
total_messages = ?
WHERE user_id = ?
''', (last_message, last_message_id, now.isoformat(), messages_per_day,
messages_per_week, messages_per_month, total_messages, user_id))
db.commit()

View File

@@ -1,283 +0,0 @@
# SQLite3/bd_func/bd_user_create.py
# Создание базы данных
import sqlite3
from ProjectsFiles import BotVar
# Настройка экспорта в модули
__all__ = ("create_user_db",)
# Функция создания базы данных
async def create_user_db(bd_name: str = BotVar.bd_names) -> None:
with sqlite3.connect(bd_name) as db:
cursor = db.cursor()
# Таблица пользователей
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY,
tg_id INTEGER NOT NULL UNIQUE,
username TEXT,
first_name TEXT,
last_name TEXT,
role TEXT DEFAULT NULL,
status TEXT DEFAULT 'active',
user TEXT DEFAULT 'user'
);''')
# Таблица сообщений пользователей
cursor.execute('''
CREATE TABLE IF NOT EXISTS user_messages (
user_id INTEGER PRIMARY KEY, -- Уникальный ключ
last_message TEXT,
last_message_id INTEGER,
last_message_time TEXT,
messages_per_day INTEGER DEFAULT 0,
messages_per_week INTEGER DEFAULT 0,
messages_per_month INTEGER DEFAULT 0,
total_messages INTEGER DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE CASCADE
);''')
# Таблица персонажей
cursor.execute('''
CREATE TABLE IF NOT EXISTS characters (
id INTEGER PRIMARY KEY AUTOINCREMENT,
region TEXT NOT NULL,
name TEXT NOT NULL,
status TEXT DEFAULT 'Свободно',
user_id INTEGER DEFAULT NULL,
comment TEXT DEFAULT '',
FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE SET NULL
);''')
# Таблица персонажей Хонкай
cursor.execute('''
CREATE TABLE IF NOT EXISTS characters_hsr (
id INTEGER PRIMARY KEY AUTOINCREMENT,
region TEXT NOT NULL,
name TEXT NOT NULL,
status TEXT DEFAULT 'Свободно',
user_id INTEGER DEFAULT NULL,
comment TEXT DEFAULT '',
FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE SET NULL
);''')
characters_genshin = [
# Мондштадт
("Мондштадт", "Венти", "Свободно", ""),
("Мондштадт", "Кэйа", "Свободно", ""),
("Мондштадт", "Альбедо", "Свободно", ""),
("Мондштадт", "Дилюк", "Свободно", ""),
("Мондштадт", "Мика", "Свободно", ""),
("Мондштадт", "Беннет", "Свободно", ""),
("Мондштадт", "Рэйзор", "Свободно", ""),
("Мондштадт", "Эола", "Свободно", ""),
("Мондштадт", "Мона", "Свободно", ""),
("Мондштадт", "Джинн", "Свободно", ""),
("Мондштадт", "Диона", "Свободно", ""),
("Мондштадт", "Лиза", "Свободно", ""),
("Мондштадт", "Ноэлль", "Свободно", ""),
("Мондштадт", "Сахароза", "Свободно", ""),
("Мондштадт", "Розария", "Свободно", ""),
("Мондштадт", "Эмбер", "Свободно", ""),
("Мондштадт", "Фишль", "Свободно", ""),
("Мондштадт", "Барбара", "Свободно", ""),
# Ли Юэ
("Ли Юэ", "Чжун Ли", "Свободно", ""),
("Ли Юэ", "Сяо", "Свободно", ""),
("Ли Юэ", "Син Цю", "Свободно", ""),
("Ли Юэ", "Чун Юнь", "Свободно", ""),
("Ли Юэ", "Бай Чжу", "Свободно", ""),
("Ли Юэ", "Е Лань", "Свободно", ""),
("Ли Юэ", "Шень Хэ", "Свободно", ""),
("Ли Юэ", "Гань Юй", "Свободно", ""),
("Ли Юэ", "Ци Ци", "Свободно", ""),
("Ли Юэ", "Кэ Цин", "Свободно", ""),
("Ли Юэ", "Янь Фэй", "Свободно", ""),
("Ли Юэ", "Нин Гуан", "Свободно", ""),
("Ли Юэ", "Бэй Доу", "Свободно", ""),
("Ли Юэ", "Яо Яо", "Свободно", ""),
("Ли Юэ", "Ка Мин", "Свободно", ""),
("Ли Юэ", "Сянь Юнь", "Свободно", ""),
("Ли Юэ", "Юнь Цзинь", "Свободно", ""),
("Ли Юэ", "Ху Тао", "Свободно", ""),
("Ли Юэ", "Лань Янь", "Свободно", ""),
# Инадзума
("Инадзума", "Итто", "Свободно", ""),
("Инадзума", "Горо", "Свободно", ""),
("Инадзума", "Аято", "Свободно", ""),
("Инадзума", "Хэйдзо", "Свободно", ""),
("Инадзума", "Тома", "Свободно", ""),
("Инадзума", "Кадзуха", "Свободно", ""),
("Инадзума", "Кирара", "Свободно", ""),
("Инадзума", "Кудзё Сара", "Свободно", ""),
("Инадзума", "Ёимия", "Свободно", ""),
("Инадзума", "Аяка", "Свободно", ""),
("Инадзума", "Сангономия Кокоми", "Свободно", ""),
("Инадзума", "Яэ Мико", "Свободно", ""),
("Инадзума", "Райдэн Эи", "Свободно", ""),
("Инадзума", "Саю", "Свободно", ""),
("Инадзума", "Куки Синобу", "Свободно", ""),
("Инадзума", "Мидзуки", "Свободно", ""),
# Сумеру
("Сумеру", "Аль-Хайтам", "Свободно", ""),
("Сумеру", "Кавех", "Свободно", ""),
("Сумеру", "Сайно", "Свободно", ""),
("Сумеру", "Тигнари", "Свободно", ""),
("Сумеру", "Сетос", "Свободно", ""),
("Сумеру", "Нилу", "Свободно", ""),
("Сумеру", "Нахида", "Свободно", ""),
("Сумеру", "Лайла", "Свободно", ""),
("Сумеру", "Кандакия", "Свободно", ""),
("Сумеру", "Дори", "Свободно", ""),
("Сумеру", "Дэхья", "Свободно", ""),
("Сумеру", "Коллеи", "Свободно", ""),
("Сумеру", "Фарузан", "Свободно", ""),
# Фонтейн
("Фонтейн", "Лини", "Свободно", ""),
("Фонтейн", "Ризли", "Свободно", ""),
("Фонтейн", "Невиллет", "Свободно", ""),
("Фонтейн", "Фремине", "Свободно", ""),
("Фонтейн", "Линетт", "Свободно", ""),
("Фонтейн", "Эмилия", "Свободно", ""),
("Фонтейн", "Клоринда", "Свободно", ""),
("Фонтейн", "Навия", "Свободно", ""),
("Фонтейн", "Шарлотта", "Свободно", ""),
("Фонтейн", "Фурина", "Свободно", ""),
("Фонтейн", "Тиори", "Свободно", ""),
("Фонтейн", "Сиджвин", "Свободно", ""),
# Натлан
("Натлан", "Кинич", "Свободно", ""),
("Натлан", "Оророн", "Свободно", ""),
("Натлан", "Муалани", "Свободно", ""),
("Натлан", "Ситлали", "Свободно", ""),
("Натлан", "Шилонен", "Свободно", ""),
("Натлан", "Иансан", "Свободно", ""),
("Натлан", "Мавуика", "Свободно", ""),
("Натлан", "Часка", "Свободно", ""),
# Фатуи
("Фатуи", "Тарталья", "Свободно", ""),
("Фатуи", "Панталоне", "Свободно", ""),
("Фатуи", "Дотторе", "Свободно", ""),
("Фатуи", "Капитано", "Свободно", ""),
("Фатуи", "Пьеро", "Свободно", ""),
("Фатуи", "Пульничелла", "Свободно", ""),
("Фатуи", "Синьора", "Свободно", ""),
("Фатуи", "Арлекино", "Свободно", ""),
("Фатуи", "Коломбина", "Свободно", ""),
("Фатуи", "Царица", "Свободно", ""),
("Фатуи", "Странник", "Свободно", ""),
# Иные персонажи
("Иные персонажи", "Итэр", "Свободно", ""),
("Иные персонажи", "Люмин", "Свободно", ""),
("Иные персонажи", "Элой", "Свободно", ""),
("Иные персонажи", "Паймон", "Свободно", ""),
("Иные персонажи", "Дайнслейф", "Свободно", ""),
]
characters_hsr = [
# Ярило-6
("Ярило-6", "Броня", "Свободно", ""),
("Ярило-6", "Гепард", "Свободно", ""),
("Ярило-6", "Зеле", "Свободно", ""),
("Ярило-6", "Клара", "Свободно", ""),
("Ярило-6", "Лука", "Свободно", ""),
("Ярило-6", "Наташа", "Свободно", ""),
("Ярило-6", "Пела", "Свободно", ""),
("Ярило-6", "Рысь", "Свободно", ""),
("Ярило-6", "Сампо", "Свободно", ""),
("Ярило-6", "Сервал", "Свободно", ""),
("Ярило-6", "Хук", "Свободно", ""),
# Станция «Герта»
("Станция «Герта»", "Арлан", "Свободно", ""),
("Станция «Герта»", "Аста", "Свободно", ""),
("Станция «Герта»", "Великая Герта", "Свободно", ""),
("Станция «Герта»", "Кукла «Герта»", "Свободно", ""),
("Станция «Герта»", "Жуань Мэй", "Свободно", ""),
# Пенакония
("Пенакония", "Авантюрин", "Свободно", ""),
("Пенакония", "Ахерон", "Свободно", ""),
("Пенакония", "Галлахер", "Свободно", ""),
("Пенакония", "Зарянка", "Свободно", ""),
("Пенакония", "Миша", "Свободно", ""),
("Пенакония", "Мистер Река", "Свободно", ""),
("Пенакония", "Раппа", "Свободно", ""),
("Пенакония", "Чёрный Лебедь", "Свободно", ""),
("Пенакония", "Яшма", "Свободно", ""),
("Пенакония", "Воскресенье", "Свободно", ""),
# Звёздный Экспресс
("Звёздный Экспресс", "Вельт", "Свободно", ""),
("Звёздный Экспресс", "Келус", "Свободно", ""),
("Звёздный Экспресс", "Стелла", "Свободно", ""),
("Звёздный Экспресс", "Дань Хэн", "Свободно", ""),
("Звёздный Экспресс", "Март 7", "Свободно", ""),
("Звёздный Экспресс", "Химеко", "Свободно", ""),
("Звёздный Экспресс", "Пом Пом", "Свободно", ""),
# Галактика
("Галактика", "Аргенти", "Свободно", ""),
("Галактика", "Блэйд", "Свободно", ""),
("Галактика", "Бутхилл", "Свободно", ""),
("Галактика", "Доктор Рацио", "Свободно", ""),
("Галактика", "Кафка", "Свободно", ""),
("Галактика", "Светлячок", "Свободно", ""),
("Галактика", "Искорка", "Свободно", ""),
("Галактика", "Серебряный Волк", "Свободно", ""),
("Галактика", "Топаз", "Свободно", ""),
# Амфореус
("Амфореус", "Аглая", "Свободно", ""),
("Амфореус", "Мидей", "Свободно", ""),
("Амфореус", "Трибби", "Свободно", ""),
# Альянс Сяньчжоу
("Альянс Сяньчжоу", "Байлу", "Свободно", ""),
("Альянс Сяньчжоу", "Гуйнайфей", "Свободно", ""),
("Альянс Сяньчжоу", "Линша", "Свободно", ""),
("Альянс Сяньчжоу", "Лоча", "Свободно", ""),
("Альянс Сяньчжоу", "Моцзэ", "Свободно", ""),
("Альянс Сяньчжоу", "Пожиратель Луны", "Свободно", ""),
("Альянс Сяньчжоу", "Сушан", "Свободно", ""),
("Альянс Сяньчжоу", "Сюзи", "Свободно", ""),
("Альянс Сяньчжоу", "Фуга", "Свободно", ""),
("Альянс Сяньчжоу", "Фэйсяо", "Свободно", ""),
("Альянс Сяньчжоу", "Ханья", "Свободно", ""),
("Альянс Сяньчжоу", "Хохо", "Свободно", ""),
("Альянс Сяньчжоу", "Цзин Юань", "Свободно", ""),
("Альянс Сяньчжоу", "Цзиннлю", "Свободно", ""),
("Альянс Сяньчжоу", "Цзяоцю", "Свободно", ""),
("Альянс Сяньчжоу", "Цинцюэ", "Свободно", ""),
("Альянс Сяньчжоу", "Юйкун", "Свободно", ""),
("Альянс Сяньчжоу", "Юньли", "Свободно", ""),
("Альянс Сяньчжоу", "Яньцин", "Свободно", ""),
]
# Проверяем, пуста ли таблица characters
cursor.execute("SELECT COUNT(*) FROM characters;")
if cursor.fetchone()[0] == 0:
cursor.executemany('''
INSERT INTO characters (region, name, status, comment)
VALUES (?, ?, ?, ?);
''', characters_genshin)
# Проверяем, пуста ли таблица characters_hsr
cursor.execute("SELECT COUNT(*) FROM characters_hsr;")
if cursor.fetchone()[0] == 0:
cursor.executemany('''
INSERT INTO characters_hsr (region, name, status, comment)
VALUES (?, ?, ?, ?);
''', characters_hsr)
db.commit()

View File

@@ -1,41 +0,0 @@
# SQLite3/bd_func/status_user.py
# Проверка статусов пользователя с БД
import sqlite3
from aiogram import types
from ProjectsFiles import BotVar
# Функция проверки статуса пользователя
async def status_user(message: types.Message, bd_path: str = BotVar.bd_names) -> str:
# Подключение к базе данных
bd = sqlite3.connect(bd_path)
tg_id = message.from_user.id
cursor = bd.cursor()
# Запрос к базе данных для получения значения из столбца 'user' для конкретного tg_id
cursor.execute("SELECT user FROM users WHERE tg_id = ?", (tg_id,))
# Получаем результат
row = cursor.fetchone()
# Словарь для сопоставления статусов
status_map = {
"ban": "Забаннен",
"user": "Пользователь",
"moderator": "Модератор",
"admin": "Администратор",
"so-owner": "Совладелец",
"owner": "Владелец",
}
if row:
user_type = row[0] # предполагаем, что в столбце 'user' находится только одно значение
status: str = status_map.get(user_type, "Ошибка!") # Получаем статус или "Ошибка!"
else:
status: str = "Пользователь не найден"
# Закрываем соединение с базой данных
bd.close()
# Выводим статус
return status

View File

@@ -1,3 +0,0 @@
# SQLite3/bd_func/username_to_id.py
#

View File

@@ -1,35 +0,0 @@
{
"ban_list_ids": {
"6666666666666": "Забанненый"
},
"important_adm_ids": {
"6751720805": "Лейн",
"7051557370": "Рикси",
"1570652377": "Риша",
"1398573474": "Финаки",
"1851081467": "Финик",
"929782381": "Хиде",
"6714237814": "Слешик",
"1686743480": "Полина",
"1184519857": "Катаз"
},
"important_groups_ids": {
"1087968824": "GroupAnonymousBot",
"-1002442589033": "Любовники Сергея",
"-1002124483077": "Труба_Сквад",
"-1002123850090": "Тест_Чат",
"-1001552311087": "Всеудет_Хорошо"
},
"important_users_list_ids": {
"7145369362": "Артур",
"1219440132": "Данил",
"1443833264": "Виктор",
"5424384921": "Олег",
"556943853": "Ваня",
"1295708467": "Степан"
},
"important_channel_ids": {
"10000000000000": "Канал1",
"20000000000000": "Канал2"
}
}

View File

@@ -1,63 +0,0 @@
# BotCode/routers/commands/admin_cmd/ban_cmd.py
# Работа с админ-командой /ban, для блокировки пользователей (в разработке)
# Проверка на наличие блокировки пользователя в боте
from aiogram import Router, types
from aiogram.filters import Command
from BotLibrary import *
# Создание роутера и настройка экспорта
__all__ = ("router", "banned_user", "ban_user_by_username",)
router = Router(name="ban_router")
command_text = "BAN"
# Функция проверки блокировки пользователя в боте
@router.message(lambda message: message.from_user.id in ListId.ban_list_id)
async def banned_user(message: types_msg.Message):
try:
# Вывод сообщения пользователю
chat_id = await find_chat_id(message)
text = (f"{TextDecorator.RED}Получено сообщение от забанненго пользователя"
f" из ({chat_id}) : {message.text}{TextDecorator.RESET_DECORATOR}")
await message.answer(f"Вы были забаннены в боте @{BotInfo.username}!")
# Активация логгера
await cmd_logginger(message, command_text, text)
return text
# Проверка на ошибку и ее логирование
except Exception as e:
text_error = await error_cmd_logginger(message, command_text, e)
return text_error
# Обработчик команды /ban
@router.message(Command("ban", "ифт", "бан", ",fy", prefix=BotEdit.prefixs, ignore_case=True))
async def ban_user_by_username(message: types_msg.Message):
try:
text = f"использовал(а) команду /{command_text.lower()}"
# Получаем аргументы команды
args = message.get_args() # Вернет все, что идет после /ban
# Проверка на наличие аргумента
if not args:
text = f"Пожалуйста, укажите ID или имя пользователя для бана. Пример: /ban 123456"
await message.reply(text)
return text
# Вывод сообщения пользователю
await message.reply(text=f"Вы попытались забанить, обидно да?")
# Активация логгера
await cmd_logginger(message, command_text, text)
return text
# Проверка на ошибку и ее логирование
except Exception as e:
text_error = await error_cmd_logginger(message, command_text, e)
return text_error

View File

@@ -1,25 +0,0 @@
# BotLibrary/analitics/find_username.py
# Нахождение юзернейма пользователя по id (в разработке)
from loguru import logger
from BotLibrary.library.bots import bot
# Настройка экспорта
__all__ = ("get_user_id_by_username",)
type_messages = "ID_USERNAME"
# Получение ID пользователя по юзернейму (в разработке)
async def get_user_id_by_username(chat_id, username):
try:
user = await bot.get_chat_member_by_username(chat_id, username)
if user:
return user.user.id
else:
return None
# Проверка на ошибку и ее логирование (в разработке)
except Exception as e:
text_error = f"Ошибка при получении ID пользователя: {e}"
logger.bind(custom_variable="IDS", user_var=type_messages).error(text_error)
return text_error

View File

@@ -1,22 +0,0 @@
# BotCode/routers/old_files/__init__.py
# Инициализация старого пакета old_files, для хранения старых функций
from aiogram import Router
from .media_func import router as media_old_router
from .regular_handlers import router as regular_router
# Объявление роутера и настройка экспорта
__all__ = ("router",)
router = Router(name="old_files_router")
# Список подключаемых роутеров сверху-вниз
router.include_routers(
regular_router,
media_old_router,
)
# Список подключаемых роутеров сверху-вниз
router.include_routers()

View File

@@ -1,55 +0,0 @@
# BotCode/routers/old_files/media_func.py
# Некоторые функции для работы с медиа-сообщениями
from re import Match
from aiogram import Router, F, types
from magic_filter import RegexpMode
from BotLibrary import *
# Настройка экспорта модулей и роутера
__all__ = ("router",)
router = Router(name="media_func")
# @router.message(F.photo, ~F.caption)
async def handle_photo_wo_caption(message: types_msg.Message):
caption = f"Простите, я не могу это увидеть. Вы можете описать что это?"
await message.reply_photo(
photo=message.photo[-1].file_id,
caption=caption,
)
return caption
# @router.message(F.photo, F.caption.contains("please"))
async def handle_photo_with_please_caption(message: types_msg.Message):
text = f"Простите, я не могу это увидеть."
await message.reply(text)
return text
# @router.message(any_media_filter, ~F.caption)
async def handle_any_media_wo_caption(message: types_msg.Message):
if message.document:
await message.reply_document(
document=message.document.file_id,
)
return f"Перессылка документа"
elif message.video:
await message.reply_video(
video=message.video.file_id,
)
return f"Перессылка видео"
else:
text = f"Я не могу это увидеть."
await message.reply(text)
return text
# @router.message(any_media_filter, F.caption)
async def handle_any_media_w_caption(message: types_msg.Message):
text = f"Что-то на медиа. Твой текст: {message.caption!r}"
await message.reply(text)
return text

32
main.py
View File

@@ -1,21 +1,39 @@
# main.py # main.py
# Основной код проекта, который и соединяет в себе все его возможности # Основной код проекта, который и соединяет в себе все его возможности
import asyncio
from BotLibrary import * from BotLibrary import *
from BotCode import router as main_router from ProjectsFiles import Permissions
from BotCode import router as main_routers
# Запуск основного кода # Запуск основного кода
async def main(): async def main() -> None:
# Функция установки # Запуск логеров
await setup() Logs.setup()
# Получение информации о боте
await BotInfo.info()
# Вывод сообщение о запуске
Logs.start(text=f"Начало запуска бота @{BotInfo.username}...")
# Создание пустых директорий
await Directory.setup()
# Нужно ли удалить веб-хук
if Permissions.delete_webhook:
await bot.delete_webhook()
# Установка необходимых прав
await BotRights.all(bot)
Logs.console()
# Подключение главного маршрутизатора # Подключение главного маршрутизатора
dp.include_router(main_router) dp.include_router(main_routers)
# Включение опроса бота # Включение опроса бота
await dp.start_polling(bot) await dp.start_polling(bot)
# Вечная загрузка бота # Вечная загрузка бота
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(main()) from asyncio import run
run(main())

BIN
requirements.txt Normal file

Binary file not shown.