Исправление ошибок с event

This commit is contained in:
2025-12-08 18:19:14 +07:00
parent 25df391f32
commit 869dd545b7
3 changed files with 121 additions and 54 deletions

View File

@@ -1,5 +1,4 @@
from typing import Optional from typing import Optional
import logging
from discord import Intents from discord import Intents
from discord.ext import commands from discord.ext import commands
@@ -15,8 +14,6 @@ __all__ = ("Bot", "discbot")
class Bot(commands.Bot): class Bot(commands.Bot):
""" """
Основной класс Discord-бота с методами настройки и запуска. Основной класс Discord-бота с методами настройки и запуска.
Поддерживает передачу token, prefix, intents и help_command в конструктор.
""" """
def __init__( def __init__(
@@ -26,12 +23,6 @@ class Bot(commands.Bot):
intents: Optional[Intents] = None, intents: Optional[Intents] = None,
help_command: Optional[commands.HelpCommand] = None, help_command: Optional[commands.HelpCommand] = None,
) -> None: ) -> None:
"""
:param token: Токен бота (если None — берётся из settings.BOT_TOKEN).
:param prefix: Префикс команд (если None — берётся из settings.PREFIX или '!').
:param intents: Intents (если None — создаются стандартные + privileged).
:param help_command: Кастомная команда помощи.
"""
# Intents по умолчанию # Intents по умолчанию
if intents is None: if intents is None:
intents = Intents.default() intents = Intents.default()
@@ -52,7 +43,6 @@ class Bot(commands.Bot):
help_command=help_command, help_command=help_command,
) )
# Сохраняем токен и хранилище
self._token: Optional[str] = token self._token: Optional[str] = token
self.storage = storage # type: ignore[assignment] self.storage = storage # type: ignore[assignment]
@@ -65,54 +55,47 @@ class Bot(commands.Bot):
async def setup(self) -> None: async def setup(self) -> None:
""" """
Инициализация бота: логгер, cogs, логирование discord.py. Инициализация бота: логгер и загрузка cogs.
""" """
logger.setup(start=True) logger.setup(start=True)
logger.info(text="Настройка бота...", log_type="SYSTEM") logger.info(text="Настройка бота...", log_type="SYSTEM")
await self.load_cogs() await self.load_cogs()
logging.basicConfig(
level=logging.WARNING,
format="%(asctime)s:%(levelname)s:%(name)s: %(message)s",
)
logging.getLogger("discord").setLevel(logging.INFO)
async def load_cogs(self) -> None: async def load_cogs(self) -> None:
""" """
Загрузить все модули cogs. Загрузить все модули cogs.
""" """
logger.info(text="Начинаю загрузку cogs...", log_type="COGS")
cogs: list[str] = [ cogs: list[str] = [
"cogs.events", "bot.cogs.events",
"cogs.moderation", "bot.cogs.moderation",
"cogs.blacklist", "bot.cogs.blacklist",
"cogs.reminders", "bot.cogs.reminders",
] ]
for cog in cogs: for cog in cogs:
try: try:
await self.load_extension(cog) await self.load_extension(cog)
logger.info(f"Загружен cog: {cog}", log_type="COGS") logger.info(text=f"Загружен cog: {cog}", log_type="COGS")
except Exception as e: except Exception as e:
logger.error(f"Ошибка загрузки {cog}: {e}", log_type="COGS") logger.error(text=f"Ошибка загрузки {cog}: {e!r}", log_type="COGS")
async def start_bot(self, token: Optional[str] = None) -> None: async def start_bot(self, token: Optional[str] = None) -> None:
""" """
Запуск бота с использованием сохранённого токена или переданного. Запуск бота с использованием сохранённого токена или переданного.
:param token: Токен бота (если None — используется self.token).
""" """
use_token: Optional[str] = token or self.token use_token: Optional[str] = token or self.token
if not use_token: if not use_token:
error: str = "BOT_TOKEN не задан (ни в конструкторе, ни в settings)" error: str = "BOT_TOKEN не задан (ни в конструкторе, ни в settings)"
logger.error(error) logger.error(text=error, log_type="START")
raise ValueError(error) raise ValueError(error)
logger.info(text="Запуск бота...", log_type="START") logger.info(text="Запуск бота...", log_type="START")
await self.start(use_token) await self.start(use_token)
# Глобальный экземпляр — МОЖНО ПЕРЕДАВАТЬ token/prefix ПРЯМО ЗДЕСЬ
discbot: Bot = Bot( discbot: Bot = Bot(
token=settings.BOT_TOKEN, # кастомный токен token=settings.BOT_TOKEN,
prefix=settings.PREFIX, # кастомный префикс prefix=settings.PREFIX,
) )

View File

@@ -2,11 +2,11 @@ from datetime import datetime
import discord import discord
from discord.ext import tasks from discord.ext import tasks
from discord.ext.commands import Bot, Cog from discord.ext.commands import Bot, Cog, Context, CommandError
from discord.utils import get from discord.utils import get
from configs import settings from configs import settings
from middleware import logger from middleware.loggers import logger
from ..storage import storage, Reminder from ..storage import storage, Reminder
@@ -25,7 +25,7 @@ class Events(Cog):
Событие запуска бота. Событие запуска бота.
Загружает данные и создаёт необходимые роли. Загружает данные и создаёт необходимые роли.
""" """
logger.info(text=f"Бот запущен как {self.bot.user}") logger.info(text=f"Бот запущен как {self.bot.user}", log_type="SYSTEM")
storage.load_all() storage.load_all()
await self.ensure_roles_exist() await self.ensure_roles_exist()
@@ -38,13 +38,25 @@ class Events(Cog):
""" """
new_member_role: discord.Role | None = get(member.guild.roles, name="New Member") new_member_role: discord.Role | None = get(member.guild.roles, name="New Member")
if new_member_role: if new_member_role:
try:
await member.add_roles(new_member_role) await member.add_roles(new_member_role)
except discord.Forbidden:
channel: discord.abc.MessageableChannel | None = self.bot.get_channel( logger.warning(
settings.WELCOME_CHANNEL_ID text=f"Нет прав выдать роль New Member пользователю {member}",
log_type="EVENT",
user=str(member),
) )
channel = self.bot.get_channel(settings.WELCOME_CHANNEL_ID)
if isinstance(channel, discord.TextChannel): if isinstance(channel, discord.TextChannel):
try:
await channel.send(f"Приветствуем {member.mention} на сервере!") await channel.send(f"Приветствуем {member.mention} на сервере!")
except discord.HTTPException as e:
logger.error(
text=f"Не удалось отправить приветствие для {member}: {e!r}",
log_type="EVENT",
user=str(member),
)
@Cog.listener() @Cog.listener()
async def on_message(self, message: discord.Message) -> None: async def on_message(self, message: discord.Message) -> None:
@@ -63,12 +75,50 @@ class Events(Cog):
await message.channel.send( await message.channel.send(
f"{message.author.mention}, ваше сообщение содержит запрещённые слова." f"{message.author.mention}, ваше сообщение содержит запрещённые слова."
) )
except Exception: logger.info(
pass text=f"Удалено сообщение с запрещённым словом от {message.author}: {message.content!r}",
log_type="BLACKLIST",
user=str(message.author),
)
except discord.Forbidden:
logger.warning(
text=f"Нет прав удалить сообщение {message.author}: {message.content!r}",
log_type="BLACKLIST",
user=str(message.author),
)
except discord.HTTPException as e:
logger.error(
text=f"Ошибка при удалении сообщения {message.author}: {e!r}",
log_type="BLACKLIST",
user=str(message.author),
)
return return
# Обработка команд ОБЯЗАТЕЛЬНО в конце, иначе команды не будут работать
await self.bot.process_commands(message) await self.bot.process_commands(message)
@Cog.listener()
async def on_command(self, ctx: Context) -> None:
"""
Логирование всех вызванных команд.
"""
logger.info(
text=f"Команда: {ctx.command} | Автор: {ctx.author} | Гильдия: {ctx.guild} | Сообщение: {ctx.message.content}",
log_type="COMMAND",
user=str(ctx.author),
)
@Cog.listener()
async def on_command_error(self, ctx: Context, error: CommandError) -> None:
"""
Логирование ошибок команд.
"""
logger.error(
text=f"Ошибка команды: {ctx.command} | Автор: {ctx.author} | Ошибка: {error!r}",
log_type="COMMAND",
user=str(ctx.author),
)
async def ensure_roles_exist(self) -> None: async def ensure_roles_exist(self) -> None:
""" """
Проверяет наличие ролей Muted и New Member и создаёт их при необходимости. Проверяет наличие ролей Muted и New Member и создаёт их при необходимости.
@@ -79,18 +129,40 @@ class Events(Cog):
try: try:
muted_role = await guild.create_role(name="Muted") muted_role = await guild.create_role(name="Muted")
for channel in guild.channels: for channel in guild.channels:
try:
await channel.set_permissions( await channel.set_permissions(
muted_role, send_messages=False, speak=False muted_role, send_messages=False, speak=False
) )
except Exception: except discord.Forbidden:
pass logger.warning(
text=f"Нет прав настроить права для канала {channel} в {guild}",
log_type="ROLES",
)
except discord.Forbidden:
logger.warning(
text=f"Нет прав создать роль Muted в {guild}",
log_type="ROLES",
)
except discord.HTTPException as e:
logger.error(
text=f"Ошибка при создании роли Muted в {guild}: {e!r}",
log_type="ROLES",
)
new_member_role: discord.Role | None = get(guild.roles, name="New Member") new_member_role: discord.Role | None = get(guild.roles, name="New Member")
if new_member_role is None: if new_member_role is None:
try: try:
await guild.create_role(name="New Member") await guild.create_role(name="New Member")
except Exception: except discord.Forbidden:
pass logger.warning(
text=f"Нет прав создать роль New Member в {guild}",
log_type="ROLES",
)
except discord.HTTPException as e:
logger.error(
text=f"Ошибка при создании роли New Member в {guild}: {e!r}",
log_type="ROLES",
)
@tasks.loop(seconds=30) @tasks.loop(seconds=30)
async def check_reminders(self) -> None: async def check_reminders(self) -> None:
@@ -101,16 +173,29 @@ class Events(Cog):
now: float = datetime.now().timestamp() now: float = datetime.now().timestamp()
to_remove: list[Reminder] = [] to_remove: list[Reminder] = []
for rem in storage.reminders: for rem in list(storage.reminders):
if rem.time <= now: if rem.time <= now:
channel = self.bot.get_channel(rem.channel_id) channel = self.bot.get_channel(rem.channel_id)
if isinstance(channel, discord.TextChannel): if isinstance(channel, discord.TextChannel):
try:
await channel.send(f"{rem.user_mention} Напоминание: {rem.text}") await channel.send(f"{rem.user_mention} Напоминание: {rem.text}")
logger.info(
text=f"Отправлено напоминание пользователю {rem.user_mention}: {rem.text!r}",
log_type="REMINDER",
)
except discord.HTTPException as e:
logger.error(
text=f"Ошибка при отправке напоминания в канал {channel.id}: {e!r}",
log_type="REMINDER",
)
to_remove.append(rem) to_remove.append(rem)
if to_remove: if to_remove:
for rem in to_remove: for rem in to_remove:
try:
storage.reminders.remove(rem) storage.reminders.remove(rem)
except ValueError:
pass
storage.save_reminders() storage.save_reminders()
@check_reminders.before_loop @check_reminders.before_loop

View File

@@ -1,17 +1,16 @@
from asyncio import run from asyncio import run
from bot import discbot from bot import discbot
from configs import settings from middleware.loggers import logger
from middleware import logger
async def main() -> None: async def main() -> None:
""" """
Точка входа для асинхронного запуска бота. Точка входа для асинхронного запуска бота.
""" """
logger.setup() logger.setup() # настройка логера
await discbot.start(settings.BOT_TOKEN) await discbot.setup() # ЗАГРУЗКА COGS + настройка discord-логов
await discbot.start_bot() await discbot.start_bot() # запуск бота (внутри возьмёт token из settings)
if __name__ == "__main__": if __name__ == "__main__":