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/common" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/BotCode/utils" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/BotLibrary/sql" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.13 (PRIMOWORLD)" jdkType="Python SDK" />

View File

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

View File

@@ -2,15 +2,14 @@
# Работа с командой /stats, для получения информации о себе
from aiogram import types
from BotLibrary import CommandHandler, bot
from SQLite3 import status_user
from BotLibrary import CommandHandler, bot, db
# Настройки экспорта в модули
__all__ = ("ban_cmd",)
# Функция блокировки пользователя
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 ('Пользователь', 'Забаннен'):
# Проверка, что команда вызвана с упоминанием пользователя
args = message.text.split()

View File

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

View File

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

View File

@@ -1,22 +1,34 @@
# BotCode/utils/admin_lists.py
# Составления листа администраторов
from aiogram.types import Message
from BotLibrary import bot
from ProjectsFiles import BotVar
# Настройки экспорта в модули
__all__ = ("admin_lists",)
async def admin_lists(chat_id: int = None, message: Message = None) -> str:
"""
Функция составления словаря администраторов.
# Функция составления словаря администраторов
async def admin_lists(chat_id: int) -> str:
:param message: Объект сообщения от пользователя.
: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)
# Формируем список упоминаний администраторов
admin_mentions = []
for admin in admins:
if admin.user.is_bot:
continue
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>")
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 "Нет администраторов"
return admins_text

View File

@@ -8,18 +8,28 @@ from BotLibrary import bot
# Настройки экспорта в модули
__all__ = ("hidden_admins_message",)
# Функция составления словаря администраторов
async def hidden_admins_message(message: types.Message = None,
chat_id: int = None,
text: str = "",
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
admins = await bot.get_chat_administrators(chat_id)
hidden_links = "".join(
markdown.hide_link(f"tg://user?id={admin.user.id}")
for admin in admins if not admin.user.is_bot
)
result = f"{hidden_links}{text}"
if msg:
return result

View File

@@ -1,10 +1,17 @@
import aiohttp
from aiogram.types import Message
from ProjectsFiles import weather_api_key
# Настройки экспорта в модули
__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)
print(command_parts[1])

View File

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

View File

@@ -6,7 +6,6 @@ from aiogram.types import ContentType, Message
# Настройка экспорта из модуля
__all__ = ("type_msg",)
# Функция определения типа сообщения
def type_msg(message: Message) -> str:
"""
Функция для определения типа сообщения на основе его содержимого.
@@ -50,7 +49,7 @@ def type_msg(message: Message) -> str:
ContentType.INVOICE: "Счет",
ContentType.SUCCESSFUL_PAYMENT: "Успешный платеж",
ContentType.REFUNDED_PAYMENT: "Возврат платежа",
ContentType.USERS_SHARED: "Пользователи поделились",
ContentType.USERS_SHARED: "Пользователь поделился",
ContentType.CHAT_SHARED: "Чат был передан",
ContentType.CONNECTED_WEBSITE: "Подключенный веб-сайт",
ContentType.WRITE_ACCESS_ALLOWED: "Разрешение на запись",
@@ -77,9 +76,20 @@ def type_msg(message: Message) -> str:
# Получение типа сообщения
message_type: str = message.content_type
# Если это контакт, добавляем номер телефона
if message_type == ContentType.CONTACT and message.contact:
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, "Неизвестный тип")

View File

@@ -3,4 +3,3 @@
# Экспортирование модулей во внешние слои проекта
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,15 +1,23 @@
# BotLibrary/loggers/logs.py
# Создание логгеров и их шаблон для проекта
# Кастомные логгеры для проекта, с более стандартизированным использованием
import sys
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,
class Logs:
"""Класс для логирования с разными уровнями через loguru."""
@staticmethod
def setup(logging: bool = BotLogs.permission,
to_file: bool = BotLogs.permission_to_file) -> None:
"""
Настройка логгеров для проекта, выводящих логи в консоль и файлы.
@@ -20,12 +28,10 @@ async def setup_logger(logging: bool = BotLogs.permission,
:param logging: Разрешение на логирование в консоль (config)
:param to_file: Разрешение на логирование в файл (config)
:return: Создание логеров под различные уровни
"""
logger.remove() # Удаляем все стандартные логгеры
# Если есть разрешение, то он создает новые уровни
if logging and BotLogs.permission_to_file:
# Добавляем новый уровень START
@@ -37,37 +43,36 @@ async def setup_logger(logging: bool = BotLogs.permission,
# Добавляем новый уровень LEAVE_USER
logger.level("LEAVE_USER", no=3, color="white", icon="🫰")
# Настройка логирования в консоль для каждого уровня
if logging:
logger.add(sys.stderr,
from sys import stderr
logger.add(stderr,
colorize=True,
format=BotLogs.start_text,
level="START",
filter=lambda record: record["level"].name == "START"
)
logger.add(sys.stderr,
logger.add(stderr,
colorize=True,
format=BotLogs.debug_text,
level="DEBUG",
filter=lambda record: record["level"].name == "DEBUG")
logger.add(sys.stderr,
logger.add(stderr,
colorize=True,
format=BotLogs.info_text,
level="INFO",
filter=lambda record: record["level"].name == "INFO")
logger.add(sys.stderr,
logger.add(stderr,
colorize=True,
format=BotLogs.warning_text,
level="WARNING",
filter=lambda record: record["level"].name == "WARNING")
logger.add(sys.stderr,
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,
@@ -105,3 +110,186 @@ async def setup_logger(logging: bool = BotLogs.permission,
diagnose=True,
level="ERROR",
filter=lambda record: record["level"].name == "ERROR")
@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: 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
# Шаблон для создания инлайн клавиатур
from aiogram.types import InlineKeyboardMarkup, ReplyKeyboardRemove
from aiogram.types import InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder
from typing import List, Tuple, Optional
# Настройка экспорта в модули
__all__ = ("BaseInlineKeyboard",)
class BaseInlineKeyboard:
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:
"""
Создаёт инлайн-клавиатуру и возвращает её вместе с объектом для удаления reply-клавиатуры.
:return: кортеж (InlineKeyboardMarkup, ReplyKeyboardRemove)
:return: кортеж InlineKeyboardMarkup
"""
ikb = InlineKeyboardBuilder()
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 typing import List, Union, Tuple, Optional, Dict, Any
# Настройка экспорта в модули
__all__ = ("BaseReplyKeyboard",)
class BaseReplyKeyboard:
def __init__(
self,

View File

@@ -11,16 +11,47 @@ from typing import Optional, Callable
from ..loggers import Logs
from ..validators import username, valid_url
from ProjectsFiles import BotVar
from SQLite3 import base_sql
from ..sql import db
# Настройки экспорта в модули
__all__ = ("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,
keywords: list,
func: Optional[list[Callable]] = None,
text_msg=None,
text_msg = None,
chat_action: bool = False,
description: str = "Описание команды",
tg_links: bool = False,
@@ -34,15 +65,16 @@ class CommandHandler:
activate_commands: bool = True,
activate_callback: bool = True,
media: str = "message",
path_to_media=None,
path_to_media = None,
parse_mode: str = BotVar.parse_mode,
disable_notification: bool = BotVar.disable_notification,
protect: bool = BotVar.protect_content):
self.router = Router(name=f"{name}_router")
self.name = name
self.log_type = name.capitalize()
self.description = description
self.last_bot_message = {} # {chat_id: message_id}
self.last_bot_message = {}
self.keywords = keywords
self.text_msg = text_msg
@@ -125,7 +157,10 @@ class CommandHandler:
text = text.replace("<users>", str(message.from_user.id))
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):

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
# Под-пакет установки настроек бота
from aiogram.types import ChatAdministratorRights
from aiogram import Bot
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",)
# Функция установки прав администратора
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:
class BotRights:
"""
Устанавливает права администратора для бота, если они отличаются от текущих.
Все через конфиги!!!
: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(
is_anonymous=anonym,
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,
@staticmethod
async def set_administrator_rights(bot: Bot) -> None:
"""
Установка прав администратора в чатах.
:param bot: Базовый объект бота.
:return: Измененные права по конфигу.
"""
from aiogram.types import ChatAdministratorRights
rights: ChatAdministratorRights = ChatAdministratorRights(
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 = await bot.get_my_default_administrator_rights()
current_rights: ChatAdministratorRights = await bot.get_my_default_administrator_rights()
if current_rights != rights:
await bot.set_my_default_administrator_rights(rights)
else:
@staticmethod
async def set_name(bot: Bot) -> None:
"""
Установка имени бота.
:param bot: Базовый объект бота.
:return: Измененное имя бота.
"""
current_name: str = (await bot.get_me()).first_name
new_name: str = str(BotEdit.name)
if not (1 <= len(new_name) <= 32):
from ..loggers import Logs
Logs.error(log_type="SET_NAME", user="BOT", text="Имя бота должно быть от 1 до 32 символов.")
return
# Функция установки имени бота с проверкой на ограничения
async def set_bot_name(new_name: str = BotEdit.name) -> None:
"""
Устанавливает имя бота, если оно отличается от текущего и соответствует ограничениям.
:param new_name: Новое имя бота (config)
:return: Имя бота
"""
# Импортируем Logs внутри функции, чтобы избежать циклического импорта
from ..loggers.custom_loggers import Logs
# Получаем текущее имя бота
current_name = (await bot.get_me()).first_name
# Проверка длины имени
if len(new_name) < 1 or len(new_name) > 32:
Logs.error(log_type=log_type, user="NAME_BOT", text="Имя бота должно быть от 1 до 32 символов.")
return # Выходим из функции, если имя некорректно
# Проверяем, совпадает ли текущее имя с тем, которое мы хотим установить
if current_name != new_name:
await bot.set_my_name(new_name)
else:
@staticmethod
async def set_description(bot: Bot) -> None:
"""
Установка описания бота.
: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):
from ..loggers import Logs
Logs.error(log_type="SET_DESCRIPTION", user="BOT", text="Описание должно быть от 1 до 255 символов.")
return
# Функция установки описания бота с проверкой на ограничения
async def set_bot_description(new_description: str = BotEdit.description) -> None:
"""
Устанавливает описание бота, если оно отличается от текущего и соответствует ограничениям.
:param new_description: Новое описание для бота (config)
:return: Описание бота
"""
# Импортируем Logs внутри функции, чтобы избежать циклического импорта
from ..loggers.custom_loggers import Logs
# Получаем текущее описание бота
current_description = await bot.get_my_description()
# Проверка длины описания
if len(new_description) > 255 or len(new_description)==0:
Logs.error(log_type=log_type, user="DISCRIPT", text="Короткое описание бота не может превышать 255 символов или быть равно 0.")
return # Выходим из функции, если описание некорректно
# Проверяем, совпадает ли текущее описание с тем, которое мы хотим установить
if current_description != new_description:
await bot.set_my_description(description=new_description)
else:
@staticmethod
async def set_short_description(bot: Bot) -> None:
"""
Установка описания виджета.
:param bot: Базовый объект бота.
: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):
from ..loggers import Logs
Logs.error(log_type="SET_SHORT_DESCRIPTION", user="BOT", text="Короткое описание должно быть от 1 до 512 символов.")
return
# Функция установки короткого описания бота с проверкой на ограничения
async def set_bot_short_description(new_short_description: str = BotEdit.short_description) -> None:
"""
Устанавливает короткое описание бота, если оно отличается от текущего и соответствует ограничениям.
:param new_short_description: Новое описание виджета для бота (config)
:return: Короткое описание бота
"""
# Импортируем Logs внутри функции, чтобы избежать циклического импорта
from ..loggers.custom_loggers import Logs
# Получаем текущее короткое описание бота
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
@staticmethod
async def all(bot: Bot) -> None:
"""
Применяет все настройки: права, имя, описание и короткое описание.
:param bot: Базовый объект бота.
:return: Изменение всех основных параметров бота.
"""
await BotRights.set_administrator_rights(bot)
await BotRights.set_name(bot)
await BotRights.set_description(bot)
await BotRights.set_short_description(bot)

View File

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

View File

@@ -2,26 +2,25 @@
# Создание пустых директорий при первом запуске
import os
from ProjectsFiles import ProjectPath, TypeDirectory
from typing import List
from ProjectsFiles import ProjectPath, TypeDirectory
# Настройка экспорта из модуля
__all__ = ("create_directories", "setup_directories", "create_directory")
__all__ = ("Directory",)
# Функция создания директории
async def create_directory(directory : str) -> None:
class Directory:
@staticmethod
async def create_directory(directory: str) -> None:
"""
Создает директории, если они еще не существуют.
:param directory: Путь к базовой директории.
:return: Создание директорий по определенному пути.
"""
os.makedirs(directory)
os.makedirs(directory, exist_ok=True)
# Функция создания поддиректорий
async def create_directories(base_directory: str, subdirectories: List[str]) -> None:
@staticmethod
async def create_directories(base_directory: str, subdirectories: List[str]) -> None:
"""
Создает указанные поддиректории в указанной базовой директории.
@@ -34,19 +33,16 @@ async def create_directories(base_directory: str, subdirectories: List[str]) ->
directory_path = os.path.join(base_directory, subdirectory)
# Проверка, существует ли директория, если нет - создаём
if not os.path.exists(directory_path):
os.makedirs(directory_path)
os.makedirs(directory_path, exist_ok=True)
# Функция установки начальных директорий
async def setup_directories() -> None:
@staticmethod
async def setup() -> 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)
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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,15 +14,6 @@
-...
Второй модуль loggers:
logs.py - создает логгеры, три кастомных уровня и 4 обычных. Он может создавать
-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
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
name: str = "Стартовый бот"
description: str = "Описание бота"
short_description: str = "Описание виджета"
name: str = "Первозданная Жемчужина"
description: str = "Ваш помощник в удивительные миры! Prod. by:『@verdise』"
short_description: str = "Тех.поддержка: @verdise"
is_anonymous: bool = False
manage_chat: bool = True
@@ -61,7 +61,8 @@ class BotVar:
encod: str = "utf-8"
language: str = "Python3-Aiogram"
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, ...] = ('$', '!', '.', '%', '&', ':', '|', '+', '-', '/', '~', '?')
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
# Основной код проекта, который и соединяет в себе все его возможности
import asyncio
from BotLibrary import *
from BotCode import router as main_router
from ProjectsFiles import Permissions
from BotCode import router as main_routers
# Запуск основного кода
async def main():
# Функция установки
await setup()
async def main() -> None:
# Запуск логеров
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)
# Вечная загрузка бота
if __name__ == "__main__":
asyncio.run(main())
from asyncio import run
run(main())

BIN
requirements.txt Normal file

Binary file not shown.