Исправление ошибок с event
All checks were successful
CI / basic-checks (push) Successful in 12s

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

View File

@@ -2,11 +2,11 @@ from datetime import datetime
import discord
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 configs import settings
from middleware import logger
from middleware.loggers import logger
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()
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")
if new_member_role:
await member.add_roles(new_member_role)
try:
await member.add_roles(new_member_role)
except discord.Forbidden:
logger.warning(
text=f"Нет прав выдать роль New Member пользователю {member}",
log_type="EVENT",
user=str(member),
)
channel: discord.abc.MessageableChannel | None = self.bot.get_channel(
settings.WELCOME_CHANNEL_ID
)
channel = self.bot.get_channel(settings.WELCOME_CHANNEL_ID)
if isinstance(channel, discord.TextChannel):
await channel.send(f"Приветствуем {member.mention} на сервере!")
try:
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()
async def on_message(self, message: discord.Message) -> None:
@@ -63,12 +75,50 @@ class Events(Cog):
await message.channel.send(
f"{message.author.mention}, ваше сообщение содержит запрещённые слова."
)
except Exception:
pass
logger.info(
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
# Обработка команд ОБЯЗАТЕЛЬНО в конце, иначе команды не будут работать
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:
"""
Проверяет наличие ролей Muted и New Member и создаёт их при необходимости.
@@ -79,18 +129,40 @@ class Events(Cog):
try:
muted_role = await guild.create_role(name="Muted")
for channel in guild.channels:
await channel.set_permissions(
muted_role, send_messages=False, speak=False
)
except Exception:
pass
try:
await channel.set_permissions(
muted_role, send_messages=False, speak=False
)
except discord.Forbidden:
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")
if new_member_role is None:
try:
await guild.create_role(name="New Member")
except Exception:
pass
except discord.Forbidden:
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)
async def check_reminders(self) -> None:
@@ -101,16 +173,29 @@ class Events(Cog):
now: float = datetime.now().timestamp()
to_remove: list[Reminder] = []
for rem in storage.reminders:
for rem in list(storage.reminders):
if rem.time <= now:
channel = self.bot.get_channel(rem.channel_id)
if isinstance(channel, discord.TextChannel):
await channel.send(f"{rem.user_mention} Напоминание: {rem.text}")
try:
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)
if to_remove:
for rem in to_remove:
storage.reminders.remove(rem)
try:
storage.reminders.remove(rem)
except ValueError:
pass
storage.save_reminders()
@check_reminders.before_loop

View File

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