Files
bot/bot.py
2025-12-08 13:17:28 +07:00

406 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)