import discord from discord.ext import commands, tasks from discord.utils import get import datetime import logging import os import json # --- Настройки и константы --- BOT_TOKEN = 'MTM0Mjc5ODAyMjg5NTA3NTMzOQ.Gqipu1.-ZEvR6Oz-dxyyXJAnr6F7aqGCvHzLLCQ0kPdak' WELCOME_CHANNEL_ID = 1342797233250107482 # ID канала для приветствий ADMIN_ROLE_NAME = "Администратор" WARNINGS_FILE = "warnings.json" REMINDERS_FILE = "reminders.json" BLACKLIST_FILE = "blacklist.json" # --- Логирование --- logging.basicConfig( filename='bot.log', level=logging.INFO, format='%(asctime)s:%(levelname)s:%(name)s: %(message)s' ) # --- Интенты --- intents = discord.Intents.default() intents.message_content = True intents.members = True # --- Глобальные переменные --- reminders = [] user_warnings = {} blacklist = [] # --- Хелп команда --- class MyHelpCommand(commands.HelpCommand): async def send_bot_help(self, mapping): channel = self.get_destination() help_text = ( "**Доступные команды:**\n" "`!help` — показать это сообщение\n" "`!rules` — показать правила сервера\n" "`!reminder add <минуты> <текст>` — добавить напоминание (только для админов)\n" "`!reminder list` — показать все активные напоминания\n" "`!reminder remove <номер>` — удалить напоминание\n" "`!kick @пользователь [причина]` — исключить участника\n" "`!ban @пользователь [причина]` — забанить участника\n" "`!unban имя#дискриминатор` — разбанить участника\n" "`!mute @пользователь [причина]` — заглушить участника\n" "`!unmute @пользователь` — снять заглушение\n" "`!warn @пользователь [причина]` — выдать предупреждение\n" "`!warnings @пользователь` — посмотреть предупреждения\n" "`!clear <кол-во>` — удалить сообщения\n" "`!blacklist_show` — показать чёрный список (админ)\n" "`!blacklist_add <слово>` — добавить слово в чёрный список (админ)\n" "`!blacklist_remove <слово>` — удалить слово из чёрного списка (админ)\n" ) await channel.send(help_text) # --- Инициализация бота --- bot = commands.Bot(command_prefix='!', intents=intents, help_command=MyHelpCommand()) # --- Функции загрузки и сохранения данных --- def load_data(): global reminders, user_warnings if os.path.isfile(WARNINGS_FILE): with open(WARNINGS_FILE, 'r', encoding='utf-8') as f: try: user_warnings.update(json.load(f)) except json.JSONDecodeError: user_warnings.clear() if os.path.isfile(REMINDERS_FILE): with open(REMINDERS_FILE, 'r', encoding='utf-8') as f: try: reminders.extend(json.load(f)) except json.JSONDecodeError: reminders.clear() def save_warnings(): with open(WARNINGS_FILE, 'w', encoding='utf-8') as f: json.dump(user_warnings, f, ensure_ascii=False, indent=2) def save_reminders(): with open(REMINDERS_FILE, 'w', encoding='utf-8') as f: json.dump(reminders, f, ensure_ascii=False, indent=2) def load_blacklist_local(): global blacklist if os.path.isfile(BLACKLIST_FILE): try: with open(BLACKLIST_FILE, 'r', encoding='utf-8') as f: blacklist = json.load(f) except Exception: blacklist = [] else: blacklist = [] def save_blacklist_local(): with open(BLACKLIST_FILE, 'w', encoding='utf-8') as f: json.dump(blacklist, f, ensure_ascii=False, indent=2) # --- Проверка и создание ролей --- async def ensure_roles_exist(): for guild in bot.guilds: muted_role = get(guild.roles, name="Muted") if not muted_role: 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) logging.info(f"Создана роль 'Muted' в {guild.name}") except Exception as e: logging.error(f"Ошибка при создании роли Muted в {guild.name}: {e}") new_member_role = get(guild.roles, name="New Member") if not new_member_role: try: new_member_role = await guild.create_role(name="New Member") logging.info(f"Создана роль 'New Member' в {guild.name}") except Exception as e: logging.error(f"Ошибка при создании роли New Member в {guild.name}: {e}") # --- События --- @bot.event async def on_ready(): print(f'Бот запущен как {bot.user}') if not check_reminders.is_running(): check_reminders.start() load_data() load_blacklist_local() await ensure_roles_exist() @bot.event async def on_member_join(member): role = get(member.guild.roles, name="New Member") if role: await member.add_roles(role) channel = bot.get_channel(WELCOME_CHANNEL_ID) if channel: await channel.send(f"Приветствуем {member.mention} на сервере!") @bot.event async def on_message(message): if message.author.bot: return # Проверка на запрещённые слова msg_lower = message.content.lower() if any(word in msg_lower for word in blacklist): try: await message.delete() await message.channel.send(f"{message.author.mention}, ваше сообщение содержит запрещённые слова.") logging.info(f"Удалено сообщение с запрещёнными словами от {message.author} в {message.channel}") except Exception as e: logging.error(f"Ошибка при удалении сообщения: {e}") return await bot.process_commands(message) # --- Проверка прав администратора --- def is_admin(): def predicate(ctx): return get(ctx.author.roles, name=ADMIN_ROLE_NAME) is not None or ctx.author.guild_permissions.administrator return commands.check(predicate) # --- Команды модерации и управления --- @bot.command() @is_admin() async def blacklist_show(ctx): if not blacklist: await ctx.send("Чёрный список пуст.") else: await ctx.send("Чёрный список:\n" + ", ".join(blacklist)) @bot.command() @is_admin() async def blacklist_add(ctx, *, word: str): word = word.lower() if word in blacklist: await ctx.send(f"Слово `{word}` уже в чёрном списке.") else: blacklist.append(word) save_blacklist_local() await ctx.send(f"Слово `{word}` добавлено в чёрный список.") @bot.command() @is_admin() async def blacklist_remove(ctx, *, word: str): word = word.lower() if word not in blacklist: await ctx.send(f"Слово `{word}` отсутствует в чёрном списке.") else: blacklist.remove(word) save_blacklist_local() await ctx.send(f"Слово `{word}` удалено из чёрного списка.") @bot.command() @is_admin() async def rules(ctx): rules_text = ( "**Правила сервера:**\n" "1. Уважайте других участников.\n" "2. Запрещена реклама и спам.\n" "3. Не используйте запрещённые слова.\n" "4. Соблюдайте тематику каналов.\n" "5. Выполняйте указания модераторов.\n" ) await ctx.send(rules_text) @bot.command() @is_admin() async def kick(ctx, member: discord.Member, *, reason=None): try: await member.kick(reason=reason) await ctx.send(f"{member} был исключён. Причина: {reason}") logging.info(f"{member} был исключён администратором {ctx.author}. Причина: {reason}") except Exception as e: await ctx.send(f"Не удалось исключить {member}.") logging.error(f"Ошибка при исключении: {e}") @bot.command() @is_admin() async def ban(ctx, member: discord.Member, *, reason=None): try: await member.ban(reason=reason) await ctx.send(f"{member} был забанен. Причина: {reason}") logging.info(f"{member} был забанен администратором {ctx.author}. Причина: {reason}") except Exception as e: await ctx.send(f"Не удалось забанить {member}.") logging.error(f"Ошибка при бане: {e}") @bot.command() @commands.has_permissions(ban_members=True) async def unban(ctx, *, member_name): banned_users = [] async for ban_entry in ctx.guild.bans(): banned_users.append(ban_entry) # Если указан полный тег с # if '#' in member_name: try: name, discriminator = member_name.split('#') except ValueError: await ctx.send("Неверный формат пользователя. Используйте Имя#Тег.") return for ban_entry in banned_users: user = ban_entry.user if (user.name, user.discriminator) == (name, discriminator): try: await ctx.guild.unban(user) await ctx.send(f"Пользователь {user} разбанен.") return except Exception as e: await ctx.send("Ошибка при разбане.") logging.error(f"Ошибка при разбане: {e}") return await ctx.send(f"Пользователь {member_name} не найден в бан-листе.") return # Если указан только имя без тега — ищем все совпадения по имени matching = [ban_entry.user for ban_entry in banned_users if ban_entry.user.name.lower() == member_name.lower()] if not matching: await ctx.send(f"Пользователь с именем `{member_name}` не найден в бан-листе.") return if len(matching) == 1: user = matching[0] try: await ctx.guild.unban(user) await ctx.send(f"Пользователь {user} разбанен.") except Exception as e: await ctx.send("Ошибка при разбане.") logging.error(f"Ошибка при разбане: {e}") return # Если совпадений несколько — выводим список для выбора msg = "Найдено несколько пользователей с таким именем. Укажите полный тег для разбанивания:\n" for user in matching: msg += f"- {user.name}#{user.discriminator}\n" await ctx.send(msg) @bot.command() @is_admin() async def mute(ctx, member: discord.Member, *, reason=None): muted_role = get(ctx.guild.roles, name="Muted") if not muted_role: await ctx.send("Роль Muted не найдена.") return try: await member.add_roles(muted_role) await ctx.send(f"{member} заглушен. Причина: {reason}") except Exception as e: await ctx.send("Не удалось выдать мут.") logging.error(f"Ошибка при муте: {e}") @bot.command() @is_admin() async def unmute(ctx, member: discord.Member): muted_role = get(ctx.guild.roles, name="Muted") if not muted_role: await ctx.send("Роль Muted не найдена.") return try: await member.remove_roles(muted_role) await ctx.send(f"С мутом снято с {member}.") except Exception as e: await ctx.send("Не удалось снять мут.") logging.error(f"Ошибка при снятии мута: {e}") @bot.command() @is_admin() async def warn(ctx, member: discord.Member, *, reason=None): user_id = str(member.id) if user_id not in user_warnings: user_warnings[user_id] = [] user_warnings[user_id].append({"reason": reason or "Без причины", "date": str(datetime.datetime.now())}) save_warnings() await ctx.send(f"{member} получил предупреждение. Причина: {reason or 'Без причины'}") @bot.command() async def warnings(ctx, member: discord.Member): user_id = str(member.id) warns = user_warnings.get(user_id, []) if not warns: await ctx.send(f"У пользователя {member} нет предупреждений.") return msg = f"Предупреждения пользователя {member}:\n" for i, w in enumerate(warns, 1): msg += f"{i}. {w['reason']} ({w['date']})\n" await ctx.send(msg) @bot.command() @is_admin() async def clear(ctx, amount: int): if amount <= 0: await ctx.send("Количество должно быть положительным числом.") return deleted = await ctx.channel.purge(limit=amount + 1) await ctx.send(f"Удалено сообщений: {len(deleted)-1}", delete_after=5) # --- Команды напоминаний --- @bot.group() @is_admin() async def reminder(ctx): if ctx.invoked_subcommand is None: await ctx.send("Используйте `!reminder add <минуты> <текст>`, `!reminder list` или `!reminder remove <номер>`") @reminder.command(name="add") async def reminder_add(ctx, minutes: int, *, text: str): if minutes <= 0: await ctx.send("Время должно быть положительным числом минут.") return remind_time = datetime.datetime.now() + datetime.timedelta(minutes=minutes) reminders.append({ "time": remind_time.timestamp(), "channel_id": ctx.channel.id, "user_mention": ctx.author.mention, "text": text }) save_reminders() await ctx.send(f"Напоминание добавлено через {minutes} минут: {text}") @reminder.command(name="list") async def reminder_list(ctx): if not reminders: await ctx.send("Активных напоминаний нет.") return msg = "Активные напоминания:\n" for i, rem in enumerate(reminders, 1): t = datetime.datetime.fromtimestamp(rem["time"]).strftime("%Y-%m-%d %H:%M:%S") msg += f"{i}. Через {t} — {rem['text']} (от {rem['user_mention']})\n" await ctx.send(msg) @reminder.command(name="remove") async def reminder_remove(ctx, number: int): if number <= 0 or number > len(reminders): await ctx.send("Неверный номер напоминания.") return removed = reminders.pop(number - 1) save_reminders() await ctx.send(f"Удалено напоминание: {removed['text']}") # --- Фоновая задача проверки напоминаний --- @tasks.loop(seconds=30) async def check_reminders(): now = datetime.datetime.now().timestamp() to_remove = [] for rem in reminders: if rem['time'] <= now: channel = bot.get_channel(rem['channel_id']) if channel: await channel.send(f"{rem['user_mention']} Напоминание: {rem['text']}") to_remove.append(rem) for rem in to_remove: reminders.remove(rem) if to_remove: save_reminders() # --- Запуск бота --- bot.run(BOT_TOKEN)