forked from NotFate/bot
Исправление ошибок с event
This commit is contained in:
41
bot/bot.py
41
bot/bot.py
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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:
|
||||||
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(
|
channel = self.bot.get_channel(settings.WELCOME_CHANNEL_ID)
|
||||||
settings.WELCOME_CHANNEL_ID
|
|
||||||
)
|
|
||||||
if isinstance(channel, discord.TextChannel):
|
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()
|
@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:
|
||||||
await channel.set_permissions(
|
try:
|
||||||
muted_role, send_messages=False, speak=False
|
await channel.set_permissions(
|
||||||
)
|
muted_role, send_messages=False, speak=False
|
||||||
except Exception:
|
)
|
||||||
pass
|
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")
|
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):
|
||||||
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)
|
to_remove.append(rem)
|
||||||
|
|
||||||
if to_remove:
|
if to_remove:
|
||||||
for rem in to_remove:
|
for rem in to_remove:
|
||||||
storage.reminders.remove(rem)
|
try:
|
||||||
|
storage.reminders.remove(rem)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
storage.save_reminders()
|
storage.save_reminders()
|
||||||
|
|
||||||
@check_reminders.before_loop
|
@check_reminders.before_loop
|
||||||
|
|||||||
9
main.py
9
main.py
@@ -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__":
|
||||||
|
|||||||
Reference in New Issue
Block a user