WL_Bots/WL staff bot/bot_staff.py

1885 lines
76 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
TOKEN = "Ваш токен"
import discord
from discord.ext import commands
from discord import ui, ButtonStyle, SelectOption, app_commands
import json
import asyncio
from datetime import datetime, timedelta
import socket
import struct
import re
# =====================================================
# 📁 РАБОТА С ДАННЫМИ
# =====================================================
STAFF_FILE = "staff_data.json"
REQUESTS_FILE = "requests.json"
ACTIONS_FILE = "actions.json"
CONFIG_FILE = "config.json"
BANS_FILE = "bans.json"
RANKS_FILE = "ranks.json"
PANELS_FILE = "panels.json"
WARNS_FILE = "warns.json" # Файл для хранения предупреждений
def load_json(file):
try:
with open(file, 'r') as f:
return json.load(f)
except:
return {}
def save_json(file, data):
with open(file, 'w') as f:
json.dump(data, f, indent=4)
def load_staff():
return load_json(STAFF_FILE)
def save_staff(data):
save_json(STAFF_FILE, data)
def load_requests():
return load_json(REQUESTS_FILE)
def save_requests(data):
save_json(REQUESTS_FILE, data)
def load_actions():
return load_json(ACTIONS_FILE)
def save_actions(data):
save_json(ACTIONS_FILE, data)
def load_config():
config = load_json(CONFIG_FILE)
if not config:
config = {
"rcon_host": "localhost",
"rcon_port": 25575,
"rcon_password": "",
"log_channel_id": None,
"dm_on_staff_change": True,
"ban_command": "tempban",
"warn_limit": 3, # Количество предупреждений до бана
"ban_duration": 2880 # 2 дня в минутах
}
save_json(CONFIG_FILE, config)
return config
def save_config(data):
save_json(CONFIG_FILE, data)
def load_bans():
return load_json(BANS_FILE)
def save_bans(data):
save_json(BANS_FILE, data)
def load_ranks():
ranks = load_json(RANKS_FILE)
if not ranks:
ranks = {
"owner": {"name": "👑 Владелец", "weight": 16},
"dep.owner": {"name": "⚜️ Зам. Владельца", "weight": 15},
"create": {"name": "✨ Создатель", "weight": 14},
"moder": {"name": "🛡️ Модератор", "weight": 13},
"ml.moder": {"name": "🔰 Ст. Модератор", "weight": 12},
"dragon": {"name": "🐉 Дракон", "weight": 11},
"titan": {"name": "🗿 Титан", "weight": 10},
"magic": {"name": "🔮 Магия", "weight": 9},
"legend": {"name": "⭐ Легенда", "weight": 8},
"ultra": {"name": "💎 Ультра", "weight": 7},
"hero": {"name": "🦸 Герой", "weight": 6},
"premium+": {"name": "💠 Премиум+", "weight": 5},
"premium": {"name": "💠 Премиум", "weight": 4},
"vip+": {"name": "⭐ ВИП+", "weight": 3},
"vip": {"name": "⭐ ВИП", "weight": 2},
"default": {"name": "📌 Обычный", "weight": 1}
}
save_json(RANKS_FILE, ranks)
return ranks
def save_ranks(data):
save_json(RANKS_FILE, data)
def load_panels():
return load_json(PANELS_FILE)
def save_panels(data):
save_json(PANELS_FILE, data)
def load_warns():
return load_json(WARNS_FILE)
def save_warns(data):
save_json(WARNS_FILE, data)
def add_panel(guild_id, channel_id, message_id, panel_type="main"):
panels = load_panels()
key = f"{guild_id}_{channel_id}_{message_id}"
panels[key] = {
"guild_id": guild_id,
"channel_id": channel_id,
"message_id": message_id,
"panel_type": panel_type,
"created_at": datetime.now().isoformat()
}
save_panels(panels)
def remove_panel(guild_id, channel_id, message_id):
panels = load_panels()
key = f"{guild_id}_{channel_id}_{message_id}"
if key in panels:
del panels[key]
save_panels(panels)
return True
return False
def log_action(mod_id, action, target, reason=""):
actions = load_actions()
if str(mod_id) not in actions:
actions[str(mod_id)] = []
actions[str(mod_id)].append({
"action": action,
"target": target,
"reason": reason,
"date": datetime.now().isoformat()
})
save_actions(actions)
# =====================================================
# 🎮 RCON КЛИЕНТ
# =====================================================
class RconClient:
def __init__(self, host, port, password):
self.host = host
self.port = port
self.password = password
self.socket = None
self.request_id = 0
def connect(self):
try:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(3)
self.socket.connect((self.host, self.port))
auth_packet = self._create_packet(3, self.password)
self.socket.send(auth_packet)
response = self.socket.recv(4096)
if response and len(response) > 8:
response_id = struct.unpack('<i', response[:4])[0]
if response_id == -1:
self.socket.close()
self.socket = None
return False
return True
return False
except Exception as e:
print(f"RCON connection error: {e}")
self.socket = None
return False
def _create_packet(self, packet_type, body):
self.request_id += 1
body_bytes = body.encode('utf-8')
packet = struct.pack('<ii', self.request_id, packet_type) + body_bytes + b'\x00\x00'
return struct.pack('<i', len(packet)) + packet
def send_command(self, command):
if not self.socket:
if not self.connect():
return None
try:
packet = self._create_packet(2, command)
self.socket.send(packet)
response = self.socket.recv(4096)
if len(response) > 8:
return response[8:-2].decode('utf-8', errors='ignore')
return None
except Exception as e:
print(f"RCON command error: {e}")
self.socket = None
return None
def close(self):
if self.socket:
try:
self.socket.close()
except:
pass
self.socket = None
rcon_client = None
def get_rcon():
global rcon_client
config = load_config()
if rcon_client is None:
rcon_client = RconClient(
config.get("rcon_host", "localhost"),
config.get("rcon_port", 25575),
config.get("rcon_password", "")
)
return rcon_client
async def execute_mc_command_async(command):
"""Асинхронная обертка для выполнения RCON команд"""
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, execute_mc_command_sync, command)
def execute_mc_command_sync(command):
"""Синхронная версия выполнения RCON команд"""
try:
rcon = get_rcon()
if not rcon.connect():
return None, "❌ Не удалось подключиться к серверу! Проверьте настройки RCON."
result = rcon.send_command(command)
rcon.close()
return result, None
except Exception as e:
return None, f"❌ Ошибка: {e}"
def parse_duration(duration_str):
if not duration_str:
return None
duration_str = duration_str.lower().strip()
if duration_str.isdigit():
return int(duration_str)
total_minutes = 0
patterns = [
(r'(\d+)w', 7 * 24 * 60),
(r'(\d+)d', 24 * 60),
(r'(\d+)h', 60),
(r'(\d+)m', 1),
]
for pattern, multiplier in patterns:
match = re.search(pattern, duration_str)
if match:
total_minutes += int(match.group(1)) * multiplier
return total_minutes if total_minutes > 0 else None
def format_duration(minutes):
if not minutes:
return "Навсегда"
if minutes < 60:
return f"{minutes} мин"
elif minutes < 24 * 60:
hours = minutes // 60
mins = minutes % 60
return f"{hours} ч {mins} мин" if mins else f"{hours} ч"
elif minutes < 7 * 24 * 60:
days = minutes // (24 * 60)
hours = (minutes % (24 * 60)) // 60
return f"{days} д {hours} ч" if hours else f"{days} д"
else:
weeks = minutes // (7 * 24 * 60)
days = (minutes % (7 * 24 * 60)) // (24 * 60)
return f"{weeks} нед {days} д" if days else f"{weeks} нед"
# =====================================================
# 🎮 БОТ
# =====================================================
intents = discord.Intents.default()
intents.message_content = True
intents.guilds = True
intents.members = True
bot = commands.Bot(command_prefix='!', intents=intents)
@bot.event
async def on_ready():
print(f'✅ Бот {bot.user} запущен!')
print(f'📡 Серверов: {len(bot.guilds)}')
print('=' * 50)
await bot.change_presence(activity=discord.Activity(
type=discord.ActivityType.watching,
name="👮 Управление персоналом | /staff"
))
await restore_panels()
try:
await bot.tree.sync()
print("✅ Слеш-команды синхронизированы!")
except Exception as e:
print(f"❌ Ошибка синхронизации: {e}")
async def restore_panels():
panels = load_panels()
if not panels:
print("📭 Нет сохраненных панелей для восстановления")
return
restored = 0
failed = 0
for key, data in panels.items():
try:
guild = bot.get_guild(data["guild_id"])
if not guild:
failed += 1
continue
channel = guild.get_channel(data["channel_id"])
if not channel:
failed += 1
continue
try:
message = await channel.fetch_message(data["message_id"])
if message.author.id == bot.user.id:
await message.edit(view=StaffMainMenu())
restored += 1
print(f"✅ Восстановлена панель в #{channel.name} ({message.id})")
else:
remove_panel(data["guild_id"], data["channel_id"], data["message_id"])
failed += 1
except discord.NotFound:
remove_panel(data["guild_id"], data["channel_id"], data["message_id"])
failed += 1
print(f"❌ Сообщение не найдено, удалено из сохраненных")
except Exception as e:
failed += 1
print(f"❌ Ошибка восстановления панели: {e}")
print(f"📊 Восстановлено панелей: {restored}, ошибок: {failed}")
# =====================================================
# 📢 ОТПРАВКА УВЕДОМЛЕНИЙ
# =====================================================
async def send_notification(interaction, title, description, color, user_id=None, dm_message=None):
config = load_config()
embed = discord.Embed(
title=title,
description=description,
color=color,
timestamp=datetime.now()
)
log_channel_id = config.get("log_channel_id")
if log_channel_id:
channel = interaction.guild.get_channel(log_channel_id)
if channel:
await channel.send(embed=embed)
await interaction.followup.send(embed=embed, ephemeral=True)
if user_id and config.get("dm_on_staff_change", True):
try:
user = await bot.fetch_user(user_id)
if dm_message:
await user.send(dm_message)
else:
await user.send(embed=embed)
except:
pass
async def send_punishment_notification(guild, target, punishment_type, reason, duration=None, moderator=None):
config = load_config()
log_channel_id = config.get("log_channel_id")
if not log_channel_id:
return
channel = guild.get_channel(log_channel_id)
if not channel:
return
embed = discord.Embed(
title=f"⚔️ {punishment_type}",
description=f"Игрок: **{target}**",
color=discord.Color.red(),
timestamp=datetime.now()
)
embed.add_field(name="📝 Причина", value=reason, inline=False)
if duration:
embed.add_field(name="⏱️ Длительность", value=format_duration(duration), inline=True)
if moderator:
embed.add_field(name="👮 Модератор", value=moderator.mention, inline=True)
await channel.send(embed=embed)
# =====================================================
# ⚠️ СИСТЕМА ПРЕДУПРЕЖДЕНИЙ
# =====================================================
def get_warns(nickname):
"""Получить список предупреждений для игрока"""
warns = load_warns()
if nickname not in warns:
warns[nickname] = []
save_warns(warns)
return warns[nickname]
def add_warn(nickname, reason, moderator):
"""Добавить предупреждение игроку"""
warns = load_warns()
if nickname not in warns:
warns[nickname] = []
warns[nickname].append({
"reason": reason,
"moderator": moderator,
"date": datetime.now().isoformat(),
"id": len(warns[nickname]) + 1
})
save_warns(warns)
return len(warns[nickname])
def remove_warn(nickname, warn_id):
"""Удалить предупреждение у игрока"""
warns = load_warns()
if nickname not in warns:
return False
# Находим и удаляем предупреждение по ID
for i, warn in enumerate(warns[nickname]):
if warn.get("id") == warn_id:
del warns[nickname][i]
# Перенумеровываем ID
for j, w in enumerate(warns[nickname]):
w["id"] = j + 1
save_warns(warns)
return True
return False
def get_warn_count(nickname):
"""Получить количество предупреждений у игрока"""
warns = load_warns()
return len(warns.get(nickname, []))
def clear_warns(nickname):
"""Очистить все предупреждения у игрока"""
warns = load_warns()
if nickname in warns:
warns[nickname] = []
save_warns(warns)
return True
return False
# =====================================================
# 🎯 КЛАССЫ ДЛЯ МЕНЮ
# =====================================================
class StaffMainMenu(ui.View):
def __init__(self):
super().__init__(timeout=None)
async def on_timeout(self):
pass
async def interaction_check(self, interaction: discord.Interaction) -> bool:
return True
@ui.button(label="👮 Назначение/увольнение", style=ButtonStyle.primary, row=0)
async def staff_btn(self, interaction: discord.Interaction, button: ui.Button):
await interaction.response.send_message(
"👮 **Управление персоналом**\nВыберите действие:",
view=StaffManagementView(interaction.user.id)
)
@ui.button(label="⚔️ Наказания", style=ButtonStyle.danger, row=0)
async def punish_btn(self, interaction: discord.Interaction, button: ui.Button):
await interaction.response.send_message(
"⚔️ **Выдача наказания**\nВыберите тип наказания:",
view=PunishmentView(interaction.user.id)
)
@ui.button(label="👑 Привилегии", style=ButtonStyle.success, row=0)
async def lp_btn(self, interaction: discord.Interaction, button: ui.Button):
await interaction.response.send_message(
"👑 **Управление привилегиями**\nВыберите действие:",
view=LPView(interaction.user.id)
)
@ui.button(label="➕ Добавить ранг", style=ButtonStyle.success, row=0)
async def add_rank_btn(self, interaction: discord.Interaction, button: ui.Button):
modal = AddRankModal()
await interaction.response.send_modal(modal)
@ui.button(label="📝 Заявки", style=ButtonStyle.secondary, row=1)
async def requests_btn(self, interaction: discord.Interaction, button: ui.Button):
await interaction.response.send_message(
"📝 **Управление заявками**\nВыберите действие:",
view=RequestsView(interaction.user.id)
)
@ui.button(label="📢 Рассылки", style=ButtonStyle.secondary, row=1)
async def broadcast_btn(self, interaction: discord.Interaction, button: ui.Button):
modal = BroadcastModal()
await interaction.response.send_modal(modal)
@ui.button(label="📋 Журнал действий", style=ButtonStyle.primary, row=2)
async def actions_btn(self, interaction: discord.Interaction, button: ui.Button):
await interaction.response.defer()
actions = load_actions()
if not actions:
await interaction.followup.send("📭 Журнал действий пуст")
return
embed = discord.Embed(
title="📋 Журнал действий",
description="Последние действия",
color=discord.Color.blue()
)
all_actions = []
for mod_id, acts in actions.items():
for act in acts:
all_actions.append((act["date"], mod_id, act))
all_actions.sort(reverse=True)
for date, mod_id, act in all_actions[:10]:
embed.add_field(
name=f"{act['action']}",
value=f"👮 <@{mod_id}>\n🎯 {act['target']}\n📅 {date[:16]}",
inline=False
)
await interaction.followup.send(embed=embed)
@ui.button(label="🔐 Роли", style=ButtonStyle.primary, row=2)
async def roles_btn(self, interaction: discord.Interaction, button: ui.Button):
embed = discord.Embed(
title="🔐 Управление ролями",
description="Используйте команды для управления ролями",
color=discord.Color.blue()
)
embed.add_field(
name="📋 Команды",
value="`/role add <участник> <роль>` - Выдать роль\n"
"`/role remove <участник> <роль>` - Убрать роль\n"
"`/role list` - Список ролей\n"
"`/role create <название>` - Создать роль",
inline=False
)
await interaction.response.send_message(embed=embed)
@ui.button(label="📊 Статистика", style=ButtonStyle.success, row=2)
async def stats_btn(self, interaction: discord.Interaction, button: ui.Button):
await interaction.response.defer()
staff = load_staff()
actions = load_actions()
embed = discord.Embed(
title="📊 Статистика персонала",
color=discord.Color.blue()
)
embed.add_field(
name="👥 Всего сотрудников",
value=str(len(staff)),
inline=True
)
roles_stats = {}
for uid, data in staff.items():
role = data.get("role", "Неизвестно")
roles_stats[role] = roles_stats.get(role, 0) + 1
roles_text = "\n".join([f"{role}: {count}" for role, count in list(roles_stats.items())[:10]])
if len(roles_stats) > 10:
roles_text += f"\nИ ещё {len(roles_stats)-10} должностей..."
embed.add_field(
name="📋 По ролям",
value=roles_text if roles_text else "Нет данных",
inline=True
)
total_actions = sum(len(acts) for acts in actions.values())
embed.add_field(
name="📋 Всего действий",
value=str(total_actions),
inline=True
)
# Статистика предупреждений
warns = load_warns()
total_warns = sum(len(w) for w in warns.values())
embed.add_field(
name="⚠️ Всего предупреждений",
value=str(total_warns),
inline=True
)
await interaction.followup.send(embed=embed)
@ui.button(label="⚙️ Настройки RCON", style=ButtonStyle.secondary, row=3)
async def rcon_settings_btn(self, interaction: discord.Interaction, button: ui.Button):
modal = RconSettingsModal()
await interaction.response.send_modal(modal)
class RconSettingsModal(ui.Modal, title="⚙️ Настройки RCON"):
host = ui.TextInput(
label="Хост",
placeholder="IP или домен сервера",
required=True,
style=discord.TextStyle.short
)
port = ui.TextInput(
label="Порт",
placeholder="Обычно 25575",
required=True,
style=discord.TextStyle.short,
default="25575"
)
password = ui.TextInput(
label="Пароль RCON",
placeholder="Пароль от RCON",
required=True,
style=discord.TextStyle.short
)
log_channel_id = ui.TextInput(
label="ID канала для логов",
placeholder="ID канала, куда отправлять уведомления",
required=False,
style=discord.TextStyle.short
)
warn_limit = ui.TextInput(
label="Кол-во предупреждений до бана",
placeholder="По умолчанию: 3",
required=False,
style=discord.TextStyle.short,
default="3"
)
ban_duration = ui.TextInput(
label="Длительность бана после 3 предупреждений (минуты)",
placeholder="По умолчанию: 2880 (2 дня)",
required=False,
style=discord.TextStyle.short,
default="2880"
)
async def on_submit(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
config = load_config()
config["rcon_host"] = self.host.value
config["rcon_port"] = int(self.port.value)
config["rcon_password"] = self.password.value
if self.log_channel_id.value:
config["log_channel_id"] = int(self.log_channel_id.value)
if self.warn_limit.value and self.warn_limit.value.isdigit():
config["warn_limit"] = int(self.warn_limit.value)
if self.ban_duration.value and self.ban_duration.value.isdigit():
config["ban_duration"] = int(self.ban_duration.value)
save_config(config)
global rcon_client
rcon_client = None
embed = discord.Embed(
title="✅ Настройки RCON сохранены!",
description="Настройки успешно обновлены",
color=discord.Color.green()
)
await interaction.followup.send(embed=embed, ephemeral=True)
# =====================================================
# 👑 УПРАВЛЕНИЕ ПРИВИЛЕГИЯМИ (LUCKPERMS)
# =====================================================
class LPView(ui.View):
def __init__(self, user_id):
super().__init__(timeout=120)
self.user_id = user_id
ranks_data = load_ranks()
self.ranks = [(rank_id, data["name"], data.get("weight", 0)) for rank_id, data in ranks_data.items()]
self.ranks.sort(key=lambda x: x[2], reverse=True)
self.current_page = 0
self.page_size = 15
options = []
for rank_id, name, weight in self.ranks[:self.page_size]:
emoji = name.split()[0] if name.split() else "📌"
options.append(SelectOption(
label=name[:100],
value=rank_id,
description=f"Вес: {weight}",
emoji=emoji
))
self.select = ui.Select(
placeholder="👑 Выберите привилегию...",
options=options,
row=0
)
self.select.callback = self.select_callback
self.add_item(self.select)
self.prev_btn = ui.Button(label="◀️ Назад", style=ButtonStyle.secondary, row=1, disabled=True)
self.prev_btn.callback = self.prev_callback
self.add_item(self.prev_btn)
total_pages = (len(self.ranks) + self.page_size - 1) // self.page_size
self.next_btn = ui.Button(label="Вперед ▶️", style=ButtonStyle.secondary, row=1, disabled=len(self.ranks) <= self.page_size)
self.next_btn.callback = self.next_callback
self.add_item(self.next_btn)
check_btn = ui.Button(label="🔍 Проверить привилегию", style=ButtonStyle.secondary, row=2)
check_btn.callback = self.check_callback
self.add_item(check_btn)
delete_btn = ui.Button(label="🗑️ Удалить ранг", style=ButtonStyle.danger, row=2)
delete_btn.callback = self.delete_callback
self.add_item(delete_btn)
close = ui.Button(label="❌ Закрыть", style=ButtonStyle.danger, row=2)
close.callback = self.close_callback
self.add_item(close)
def update_select(self):
ranks_data = load_ranks()
self.ranks = [(rank_id, data["name"], data.get("weight", 0)) for rank_id, data in ranks_data.items()]
self.ranks.sort(key=lambda x: x[2], reverse=True)
start = self.current_page * self.page_size
end = start + self.page_size
page_ranks = self.ranks[start:end]
options = []
for rank_id, name, weight in page_ranks:
emoji = name.split()[0] if name.split() else "📌"
options.append(SelectOption(
label=name[:100],
value=rank_id,
description=f"Вес: {weight}",
emoji=emoji
))
self.select.options = options
self.select.placeholder = f"👑 Выберите привилегию (стр. {self.current_page + 1})"
total_pages = (len(self.ranks) + self.page_size - 1) // self.page_size
self.prev_btn.disabled = self.current_page == 0
self.next_btn.disabled = self.current_page >= total_pages - 1
async def prev_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
self.current_page -= 1
self.update_select()
await interaction.response.edit_message(view=self)
async def next_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
self.current_page += 1
self.update_select()
await interaction.response.edit_message(view=self)
async def select_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
rank = self.select.values[0]
modal = LPModal(rank)
await interaction.response.send_modal(modal)
async def check_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
modal = CheckRankModal()
await interaction.response.send_modal(modal)
async def delete_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
modal = DeleteRankModal()
await interaction.response.send_modal(modal)
async def close_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
await interaction.message.delete()
class LPModal(ui.Modal, title="👑 Выдача привилегии"):
def __init__(self, rank):
super().__init__(title=f"👑 Выдача {rank}")
self.rank = rank
self.nickname = ui.TextInput(
label="Ник в Minecraft",
placeholder="Введите ник игрока...",
required=True,
style=discord.TextStyle.short
)
self.add_item(self.nickname)
self.reason = ui.TextInput(
label="Причина (будет отображаться в чате)",
placeholder="Введите причину выдачи...",
required=False,
style=discord.TextStyle.short,
default="Выдача привилегии"
)
self.add_item(self.reason)
self.server = ui.TextInput(
label="Сервер (опционально)",
placeholder="Оставьте пустым для глобальной",
required=False,
style=discord.TextStyle.short
)
self.add_item(self.server)
async def on_submit(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
nickname = self.nickname.value
rank = self.rank
reason = self.reason.value if self.reason.value else "Выдача привилегии"
server = self.server.value if self.server.value else "global"
ranks_data = load_ranks()
rank_name = ranks_data.get(rank, {}).get("name", rank)
if server == "global":
lp_command = f"lp user {nickname} parent set {rank}"
else:
lp_command = f"lp user {nickname} parent set {rank} server={server}"
chat_message = f"§6§l🔔 Игроку §e{nickname} §6выдана привилегия §b{rank_name} §6причина: §f{reason}"
chat_command = f"say {chat_message}"
lp_result, lp_error = await execute_mc_command_async(lp_command)
chat_result, chat_error = await execute_mc_command_async(chat_command)
embed = discord.Embed(
title="👑 Привилегия выдана!",
description=f"Игроку **{nickname}** выдана привилегия **{rank_name}**",
color=discord.Color.gold()
)
embed.add_field(name="📌 Сервер", value=server, inline=True)
embed.add_field(name="📝 Причина", value=reason, inline=True)
status = ""
if lp_error:
status += f"❌ LP: {lp_error}\n"
else:
status += f"✅ LP: Команда выполнена\n"
if chat_error:
status += f"❌ Чат: {chat_error}"
else:
status += f"✅ Чат: Сообщение отправлено"
embed.add_field(name="📡 Статус", value=status, inline=False)
embed.add_field(
name="💬 Сообщение в чате",
value=f"```{chat_message}```",
inline=False
)
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, f"выдача привилегии {rank}", nickname, f"Сервер: {server}, Причина: {reason}")
class CheckRankModal(ui.Modal, title="🔍 Проверка привилегии"):
nickname = ui.TextInput(
label="Ник в Minecraft",
placeholder="Введите ник игрока...",
required=True,
style=discord.TextStyle.short
)
async def on_submit(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
nickname = self.nickname.value
command = f"lp user {nickname} info"
result, error = await execute_mc_command_async(command)
if error:
embed = discord.Embed(
title=f"❌ Ошибка",
description=f"```{error}```",
color=discord.Color.red()
)
else:
embed = discord.Embed(
title=f"🔍 Информация о {nickname}",
description=f"```{result[:1900]}```" if result else "❌ Игрок не найден или нет данных",
color=discord.Color.blue()
)
await interaction.followup.send(embed=embed, ephemeral=True)
class AddRankModal(ui.Modal, title="➕ Добавить новый ранг"):
rank_id = ui.TextInput(
label="ID ранга",
placeholder="Например: admin, helper, builder",
required=True,
style=discord.TextStyle.short
)
rank_name = ui.TextInput(
label="Название ранга",
placeholder="👑 Администратор",
required=True,
style=discord.TextStyle.short
)
weight = ui.TextInput(
label="Вес (чем больше, тем выше)",
placeholder="Например: 20, 30, 100",
required=True,
style=discord.TextStyle.short,
default="10"
)
async def on_submit(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
rank_id = self.rank_id.value.lower().strip()
rank_name = self.rank_name.value
weight = int(self.weight.value) if self.weight.value.isdigit() else 10
ranks = load_ranks()
if rank_id in ranks:
await interaction.followup.send(f"❌ Ранг с ID **{rank_id}** уже существует!", ephemeral=True)
return
ranks[rank_id] = {
"name": rank_name,
"weight": weight
}
save_ranks(ranks)
embed = discord.Embed(
title="✅ Новый ранг добавлен!",
description=f"Ранг **{rank_name}** (ID: {rank_id}) добавлен с весом {weight}",
color=discord.Color.green()
)
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, "добавление ранга", rank_id, f"{rank_name} (вес: {weight})")
class DeleteRankModal(ui.Modal, title="🗑️ Удалить ранг"):
rank_id = ui.TextInput(
label="ID ранга",
placeholder="Введите ID ранга для удаления",
required=True,
style=discord.TextStyle.short
)
async def on_submit(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
rank_id = self.rank_id.value.lower().strip()
ranks = load_ranks()
if rank_id not in ranks:
await interaction.followup.send(f"❌ Ранг с ID **{rank_id}** не найден!", ephemeral=True)
return
rank_name = ranks[rank_id]["name"]
del ranks[rank_id]
save_ranks(ranks)
embed = discord.Embed(
title="🗑️ Ранг удален!",
description=f"Ранг **{rank_name}** (ID: {rank_id}) удален",
color=discord.Color.red()
)
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, "удаление ранга", rank_id, rank_name)
# =====================================================
# 👮 УПРАВЛЕНИЕ ПЕРСОНАЛОМ
# =====================================================
class StaffManagementView(ui.View):
def __init__(self, user_id):
super().__init__(timeout=120)
self.user_id = user_id
options = [
SelectOption(label="📥 Назначить", value="hire", description="Назначить сотрудника", emoji="📥"),
SelectOption(label="📤 Уволить", value="fire", description="Уволить сотрудника", emoji="📤"),
SelectOption(label="📋 Список", value="list", description="Показать всех сотрудников", emoji="📋"),
]
self.select = ui.Select(placeholder="Выберите действие...", options=options, row=0)
self.select.callback = self.select_callback
self.add_item(self.select)
close = ui.Button(label="❌ Закрыть", style=ButtonStyle.danger, row=1)
close.callback = self.close_callback
self.add_item(close)
async def select_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
action = self.select.values[0]
if action == "hire":
modal = HireModal()
await interaction.response.send_modal(modal)
elif action == "fire":
modal = FireModal()
await interaction.response.send_modal(modal)
elif action == "list":
await interaction.response.defer()
staff = load_staff()
if not staff:
await interaction.followup.send("📭 Нет сотрудников")
return
embed = discord.Embed(
title="👮 Список сотрудников",
color=discord.Color.blue()
)
for uid, data in list(staff.items())[:15]:
member = interaction.guild.get_member(int(uid))
name = member.mention if member else f"<@{uid}>"
embed.add_field(
name=f"📌 {data.get('role', 'Без роли')}",
value=f"{name}\n📅 {data.get('hired', 'Неизвестно')[:16]}",
inline=False
)
if len(staff) > 15:
embed.set_footer(text=f"И ещё {len(staff)-15} сотрудников...")
await interaction.followup.send(embed=embed)
async def close_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
await interaction.message.delete()
class HireModal(ui.Modal, title="📥 Назначение сотрудника"):
member_id = ui.TextInput(
label="ID участника",
placeholder="Введите ID пользователя...",
required=True,
style=discord.TextStyle.short
)
role = ui.TextInput(
label="Должность",
placeholder="Введите должность...",
required=True,
style=discord.TextStyle.short
)
async def on_submit(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
try:
member = await interaction.guild.fetch_member(int(self.member_id.value))
except:
await interaction.followup.send("❌ Участник не найден!", ephemeral=True)
return
staff = load_staff()
if str(member.id) in staff:
await interaction.followup.send(f"⚠️ {member.mention} уже является сотрудником!", ephemeral=True)
return
staff[str(member.id)] = {
"role": self.role.value,
"hired": datetime.now().isoformat(),
"hired_by": interaction.user.id
}
save_staff(staff)
log_action(interaction.user.id, "назначение", member.name, self.role.value)
await send_notification(
interaction,
"✅ Сотрудник назначен!",
f"{member.mention} назначен на должность **{self.role.value}**",
discord.Color.green(),
member.id,
f"🎉 Поздравляем! Вы назначены на должность **{self.role.value}** на сервере {interaction.guild.name}!"
)
class FireModal(ui.Modal, title="📤 Увольнение сотрудника"):
member_id = ui.TextInput(
label="ID участника",
placeholder="Введите ID пользователя...",
required=True,
style=discord.TextStyle.short
)
reason = ui.TextInput(
label="Причина",
placeholder="Введите причину...",
required=False,
style=discord.TextStyle.short,
default="Не указана"
)
async def on_submit(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
try:
member = await interaction.guild.fetch_member(int(self.member_id.value))
except:
await interaction.followup.send("❌ Участник не найден!", ephemeral=True)
return
staff = load_staff()
if str(member.id) not in staff:
await interaction.followup.send(f"⚠️ {member.mention} не является сотрудником!", ephemeral=True)
return
role = staff[str(member.id)]["role"]
del staff[str(member.id)]
save_staff(staff)
log_action(interaction.user.id, "увольнение", member.name, self.reason.value)
await send_notification(
interaction,
"❌ Сотрудник уволен!",
f"{member.mention} уволен с должности **{role}**\n📝 Причина: {self.reason.value}",
discord.Color.red(),
member.id,
f"⚠️ Вы были уволены с должности **{role}** на сервере {interaction.guild.name}.\n📝 Причина: {self.reason.value}"
)
# =====================================================
# ⚔️ НАКАЗАНИЯ (С RCON)
# =====================================================
class PunishmentView(ui.View):
def __init__(self, user_id):
super().__init__(timeout=120)
self.user_id = user_id
options = [
SelectOption(label="⚠️ Предупреждение", value="warn", emoji="⚠️"),
SelectOption(label="🔽 Снять предупреждение", value="unwarn", emoji="🔽"),
SelectOption(label="📋 Список предупреждений", value="list_warns", emoji="📋"),
SelectOption(label="🔇 Мут", value="mute", emoji="🔇"),
SelectOption(label="🔊 Размут", value="unmute", emoji="🔊"),
SelectOption(label="⛔ Кик", value="kick", emoji="⛔"),
SelectOption(label="❌ Бан", value="ban", emoji="❌"),
SelectOption(label="🔓 Разбан", value="unban", emoji="🔓"),
SelectOption(label="🌐 Бан IP", value="banip", emoji="🌐"),
SelectOption(label="🌐 Разбан IP", value="unbanip", emoji="🌐"),
]
self.select = ui.Select(placeholder="Выберите наказание...", options=options, row=0)
self.select.callback = self.select_callback
self.add_item(self.select)
close = ui.Button(label="❌ Закрыть", style=ButtonStyle.danger, row=1)
close.callback = self.close_callback
self.add_item(close)
async def select_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
punishment = self.select.values[0]
if punishment == "list_warns":
modal = ListWarnsModal()
await interaction.response.send_modal(modal)
else:
modal = PunishmentModal(punishment)
await interaction.response.send_modal(modal)
async def close_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
await interaction.message.delete()
class ListWarnsModal(ui.Modal, title="📋 Список предупреждений"):
nickname = ui.TextInput(
label="Ник в Minecraft",
placeholder="Введите ник игрока...",
required=True,
style=discord.TextStyle.short
)
async def on_submit(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
nickname = self.nickname.value
warns = get_warns(nickname)
if not warns:
embed = discord.Embed(
title=f"📋 Предупреждения игрока {nickname}",
description="У игрока нет предупреждений ✅",
color=discord.Color.green()
)
await interaction.followup.send(embed=embed, ephemeral=True)
return
embed = discord.Embed(
title=f"📋 Предупреждения игрока {nickname}",
description=f"Всего: **{len(warns)}** предупреждений",
color=discord.Color.orange()
)
for warn in warns:
date = warn.get("date", "")[:16]
embed.add_field(
name=f"#{warn.get('id', '?')}",
value=f"📝 {warn.get('reason', 'Без причины')}\n👮 {warn.get('moderator', 'Неизвестно')}\n📅 {date}",
inline=False
)
await interaction.followup.send(embed=embed, ephemeral=True)
class PunishmentModal(ui.Modal, title="⚔️ Выдача наказания"):
def __init__(self, punishment_type):
super().__init__(title=f"⚔️ {self._get_title(punishment_type)}")
self.punishment_type = punishment_type
if punishment_type in ["banip", "unbanip"]:
self.target_label = "IP адрес"
self.target_placeholder = "Введите IP адрес..."
else:
self.target_label = "Ник в Minecraft"
self.target_placeholder = "Введите ник игрока..."
self.target = ui.TextInput(
label=self.target_label,
placeholder=self.target_placeholder,
required=True,
style=discord.TextStyle.short
)
self.add_item(self.target)
self.reason = ui.TextInput(
label="Причина",
placeholder="Введите причину...",
required=False,
style=discord.TextStyle.short,
default="Нарушение правил"
)
self.add_item(self.reason)
if punishment_type in ["mute", "ban", "banip"]:
self.duration = ui.TextInput(
label="Длительность (1d, 2h, 30m, 1w или число в минутах)",
placeholder="Например: 10, 60, 1d, 2h, 1w (0 - навсегда)",
required=False,
style=discord.TextStyle.short,
default="10"
)
self.add_item(self.duration)
if punishment_type == "unwarn":
self.warn_id = ui.TextInput(
label="ID предупреждения (из списка)",
placeholder="Введите ID для удаления",
required=True,
style=discord.TextStyle.short
)
self.add_item(self.warn_id)
def _get_title(self, punishment_type):
titles = {
"warn": "Предупреждение",
"unwarn": "Снять предупреждение",
"mute": "Мут",
"unmute": "Размут",
"kick": "Кик",
"ban": "Бан",
"unban": "Разбан",
"banip": "Бан по IP",
"unbanip": "Разбан по IP"
}
return titles.get(punishment_type, punishment_type)
async def on_submit(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
target = self.target.value
reason = self.reason.value if self.reason.value else "Нарушение правил"
punishment_names = {
"warn": "Предупреждение",
"unwarn": "Снятие предупреждения",
"mute": "Мут",
"unmute": "Размут",
"kick": "Кик",
"ban": "Бан",
"unban": "Разбан",
"banip": "Бан по IP",
"unbanip": "Разбан по IP"
}
duration = None
if self.punishment_type in ["mute", "ban", "banip"] and hasattr(self, 'duration'):
duration_str = self.duration.value if self.duration.value else "0"
if duration_str != "0":
duration = parse_duration(duration_str)
# ============ ПРЕДУПРЕЖДЕНИЕ ============
if self.punishment_type == "warn":
warn_count = add_warn(target, reason, interaction.user.display_name)
config = load_config()
warn_limit = config.get("warn_limit", 3)
ban_duration = config.get("ban_duration", 2880) # 2 дня
# Отправляем только ОДНО сообщение в чат
chat_message = f"§e⚠️ Игроку §6{target} §eвынесено предупреждение №{warn_count} §fпричина: §c{reason}"
await execute_mc_command_async(f"say {chat_message}")
await send_punishment_notification(
interaction.guild,
target,
f"Предупреждение #{warn_count}",
reason,
None,
interaction.user
)
embed = discord.Embed(
title="⚠️ Предупреждение выдано!",
description=f"Игроку **{target}** выдано предупреждение №{warn_count}",
color=discord.Color.gold()
)
embed.add_field(name="📝 Причина", value=reason, inline=False)
embed.add_field(name="📊 Всего предупреждений", value=f"{warn_count}/{warn_limit}", inline=True)
# Проверяем, нужно ли забанить
if warn_count >= warn_limit:
ban_reason = f"Авто-бан: {warn_limit} предупреждений"
ban_command = f"tempban {target} {ban_duration}m {ban_reason}"
ban_result, ban_error = await execute_mc_command_async(ban_command)
embed.add_field(
name="❌ Авто-бан!",
value=f"Игрок **{target}** автоматически забанен на {format_duration(ban_duration)} за {warn_limit} предупреждений",
inline=False
)
# Отправляем сообщение о бане в чат (только одно)
ban_chat = f"§c§l❌ Игрок §e{target} §cавтоматически забанен за {warn_limit} предупреждений §f(причина: {reason})"
await execute_mc_command_async(f"say {ban_chat}")
# Очищаем предупреждения после бана
clear_warns(target)
log_action(interaction.user.id, "авто-бан", target, f"{warn_limit} предупреждений")
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, "предупреждение", target, f"{reason} (№{warn_count})")
# ============ СНЯТИЕ ПРЕДУПРЕЖДЕНИЯ ============
elif self.punishment_type == "unwarn":
warn_id = int(self.warn_id.value) if self.warn_id.value.isdigit() else None
if not warn_id:
await interaction.followup.send("❌ Укажите корректный ID предупреждения!", ephemeral=True)
return
if remove_warn(target, warn_id):
# Отправляем сообщение в чат
chat_message = f"§a✅ Игроку §6{target} §aснято предупреждение #{warn_id}"
await execute_mc_command_async(f"say {chat_message}")
embed = discord.Embed(
title="✅ Предупреждение снято!",
description=f"У игрока **{target}** снято предупреждение #{warn_id}",
color=discord.Color.green()
)
embed.add_field(
name="📊 Осталось предупреждений",
value=str(get_warn_count(target)),
inline=True
)
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, "снятие предупреждения", target, f"ID: {warn_id}")
else:
await interaction.followup.send(f"❌ Предупреждение #{warn_id} у игрока **{target}** не найдено!", ephemeral=True)
# ============ ОСТАЛЬНЫЕ НАКАЗАНИЯ ============
elif self.punishment_type == "mute":
if duration:
command = f"mute {target} {duration}m {reason}"
else:
command = f"mute {target} {reason}"
chat_message = f"§c§l🔇 Игроку §e{target} §cвыдан мут §fпричина: §e{reason}"
if duration:
chat_message += f" §fдлительность: §e{format_duration(duration)}"
await execute_mc_command_async(f"say {chat_message}")
result, error = await execute_mc_command_async(command)
await send_punishment_notification(interaction.guild, target, "Мут", reason, duration, interaction.user)
embed = discord.Embed(title="✅ Мут выдан!", description=f"Игрок **{target}** замучен", color=discord.Color.red())
embed.add_field(name="📝 Причина", value=reason, inline=False)
if duration:
embed.add_field(name="⏱️ Длительность", value=format_duration(duration), inline=True)
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, "мут", target, reason)
elif self.punishment_type == "unmute":
command = f"unmute {target}"
chat_message = f"§a✅ Игрок §e{target} §aразмучен"
await execute_mc_command_async(f"say {chat_message}")
result, error = await execute_mc_command_async(command)
embed = discord.Embed(title="✅ Размут выполнен!", description=f"Игрок **{target}** размучен", color=discord.Color.green())
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, "размут", target, reason)
elif self.punishment_type == "kick":
command = f"kick {target} {reason}"
chat_message = f"§c§l⛔ Игрок §e{target} §cкикнут §fпричина: §e{reason}"
await execute_mc_command_async(f"say {chat_message}")
result, error = await execute_mc_command_async(command)
await send_punishment_notification(interaction.guild, target, "Кик", reason, None, interaction.user)
embed = discord.Embed(title="✅ Кик выполнен!", description=f"Игрок **{target}** кикнут", color=discord.Color.orange())
embed.add_field(name="📝 Причина", value=reason, inline=False)
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, "кик", target, reason)
elif self.punishment_type == "ban":
if duration:
command = f"tempban {target} {duration}m {reason}"
else:
command = f"ban {target} {reason}"
chat_message = f"§c§l❌ Игрок §e{target} §cзабанен §fпричина: §e{reason}"
if duration:
chat_message += f" §fдлительность: §e{format_duration(duration)}"
await execute_mc_command_async(f"say {chat_message}")
result, error = await execute_mc_command_async(command)
await send_punishment_notification(interaction.guild, target, "Бан", reason, duration, interaction.user)
embed = discord.Embed(title="✅ Бан выдан!", description=f"Игрок **{target}** забанен", color=discord.Color.dark_red())
embed.add_field(name="📝 Причина", value=reason, inline=False)
if duration:
embed.add_field(name="⏱️ Длительность", value=format_duration(duration), inline=True)
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, "бан", target, reason)
elif self.punishment_type == "unban":
command = f"unban {target}"
chat_message = f"§a✅ Игрок §e{target} §aразбанен"
await execute_mc_command_async(f"say {chat_message}")
result, error = await execute_mc_command_async(command)
embed = discord.Embed(title="✅ Разбан выполнен!", description=f"Игрок **{target}** разбанен", color=discord.Color.green())
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, "разбан", target, reason)
elif self.punishment_type == "banip":
if duration:
command = f"tempbanip {target} {duration}m {reason}"
else:
command = f"banip {target} {reason}"
chat_message = f"§c§l🌐 IP §e{target} §cзабанен §fпричина: §e{reason}"
if duration:
chat_message += f" §fдлительность: §e{format_duration(duration)}"
await execute_mc_command_async(f"say {chat_message}")
result, error = await execute_mc_command_async(command)
await send_punishment_notification(interaction.guild, f"{target} (IP)", "Бан по IP", reason, duration, interaction.user)
embed = discord.Embed(title="✅ Бан по IP выдан!", description=f"IP **{target}** забанен", color=discord.Color.dark_red())
embed.add_field(name="📝 Причина", value=reason, inline=False)
if duration:
embed.add_field(name="⏱️ Длительность", value=format_duration(duration), inline=True)
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, "бан по IP", target, reason)
elif self.punishment_type == "unbanip":
command = f"unbanip {target}"
chat_message = f"§a✅ IP §e{target} §aразбанен"
await execute_mc_command_async(f"say {chat_message}")
result, error = await execute_mc_command_async(command)
embed = discord.Embed(title="✅ Разбан по IP выполнен!", description=f"IP **{target}** разбанен", color=discord.Color.green())
await interaction.followup.send(embed=embed, ephemeral=True)
log_action(interaction.user.id, "разбан по IP", target, reason)
# =====================================================
# 📝 ЗАЯВКИ
# =====================================================
class RequestsView(ui.View):
def __init__(self, user_id):
super().__init__(timeout=120)
self.user_id = user_id
options = [
SelectOption(label="📝 Создать заявку", value="create", emoji="📝"),
SelectOption(label="📋 Мои заявки", value="my", emoji="📋"),
SelectOption(label="✅ Все заявки", value="all", emoji="✅"),
]
self.select = ui.Select(placeholder="Выберите действие...", options=options, row=0)
self.select.callback = self.select_callback
self.add_item(self.select)
close = ui.Button(label="❌ Закрыть", style=ButtonStyle.danger, row=1)
close.callback = self.close_callback
self.add_item(close)
async def select_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
action = self.select.values[0]
if action == "create":
modal = RequestModal()
await interaction.response.send_modal(modal)
elif action == "my":
await interaction.response.defer()
requests = load_requests()
user_requests = [r for r in requests.values() if r.get("author") == str(interaction.user.id)]
if not user_requests:
await interaction.followup.send("📭 У вас нет заявок")
return
embed = discord.Embed(
title="📋 Мои заявки",
color=discord.Color.blue()
)
for req in user_requests[-5:]:
embed.add_field(
name=f"#{req.get('id', '?')} - {req.get('status', 'Неизвестно')}",
value=f"📝 {req.get('text', '')[:50]}...\n📅 {req.get('date', '')[:16]}",
inline=False
)
await interaction.followup.send(embed=embed)
elif action == "all":
await interaction.response.defer()
requests = load_requests()
if not requests:
await interaction.followup.send("📭 Нет заявок")
return
embed = discord.Embed(
title="✅ Все заявки",
color=discord.Color.blue()
)
for req_id, req in list(requests.items())[-10:]:
embed.add_field(
name=f"#{req_id} - {req.get('status', 'Неизвестно')}",
value=f"👤 <@{req.get('author', '?')}>\n📝 {req.get('text', '')[:50]}...",
inline=False
)
await interaction.followup.send(embed=embed)
async def close_callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваше меню!", ephemeral=True)
return
await interaction.message.delete()
class RequestModal(ui.Modal, title="📝 Создание заявки"):
title = ui.TextInput(
label="Тема",
placeholder="Кратко опишите тему...",
required=True,
style=discord.TextStyle.short
)
text = ui.TextInput(
label="Текст заявки",
placeholder="Подробно опишите вашу заявку...",
required=True,
style=discord.TextStyle.paragraph
)
async def on_submit(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
requests = load_requests()
req_id = str(len(requests) + 1)
requests[req_id] = {
"id": req_id,
"title": self.title.value,
"text": self.text.value,
"author": str(interaction.user.id),
"status": "Новая",
"date": datetime.now().isoformat()
}
save_requests(requests)
embed = discord.Embed(
title=f"📝 Новая заявка #{req_id}",
description=self.text.value,
color=discord.Color.blue()
)
embed.add_field(name="📌 Тема", value=self.title.value, inline=False)
embed.add_field(name="👤 Автор", value=interaction.user.mention, inline=True)
embed.add_field(name="📅 Дата", value=datetime.now().strftime("%d.%m.%Y %H:%M"), inline=True)
for channel in interaction.guild.text_channels:
if "заявк" in channel.name.lower() or "request" in channel.name.lower():
await channel.send(embed=embed)
break
await interaction.followup.send(f"✅ Заявка #{req_id} создана!", ephemeral=True)
# =====================================================
# 📢 РАССЫЛКИ
# =====================================================
class BroadcastModal(ui.Modal, title="📢 Отправить рассылку"):
channel_id = ui.TextInput(
label="ID канала",
placeholder="Введите ID канала...",
required=True,
style=discord.TextStyle.short
)
message = ui.TextInput(
label="Сообщение",
placeholder="Введите текст рассылки...",
required=True,
style=discord.TextStyle.paragraph
)
async def on_submit(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
try:
channel = interaction.guild.get_channel(int(self.channel_id.value))
if not channel:
await interaction.followup.send("❌ Канал не найден!", ephemeral=True)
return
embed = discord.Embed(
title="📢 Важное сообщение",
description=self.message.value,
color=discord.Color.blue()
)
embed.set_footer(text=f"Отправлено: {interaction.user.display_name}")
await channel.send(embed=embed)
log_action(interaction.user.id, "рассылка", channel.name, self.message.value[:50])
await interaction.followup.send(f"✅ Рассылка отправлена в <#{self.channel_id.value}>!", ephemeral=True)
except:
await interaction.followup.send("❌ Ошибка! Проверьте ID канала.", ephemeral=True)
# =====================================================
# 🔐 КОМАНДЫ РОЛЕЙ
# =====================================================
@bot.tree.command(name="role", description="🔐 Управление ролями")
@app_commands.describe(
action="Действие: add, remove, list, create",
member="Участник (для add/remove)",
role="Название роли или @упоминание",
name="Название для создания"
)
async def slash_role(interaction: discord.Interaction, action: str, member: discord.Member = None, role: str = None, name: str = None):
if action == "add":
if not member or not role:
await interaction.response.send_message("❌ Использование: `/role add <участник> <роль>`")
return
role_obj = discord.utils.get(interaction.guild.roles, name=role)
if not role_obj:
await interaction.response.send_message(f"❌ Роль **{role}** не найдена!")
return
await member.add_roles(role_obj)
log_action(interaction.user.id, "выдача роли", member.name, role)
await interaction.response.send_message(f"✅ Роль **{role}** выдана {member.mention}!")
elif action == "remove":
if not member or not role:
await interaction.response.send_message("❌ Использование: `/role remove <участник> <роль>`")
return
role_obj = discord.utils.get(interaction.guild.roles, name=role)
if not role_obj:
await interaction.response.send_message(f"❌ Роль **{role}** не найдена!")
return
await member.remove_roles(role_obj)
log_action(interaction.user.id, "удаление роли", member.name, role)
await interaction.response.send_message(f"✅ Роль **{role}** убрана у {member.mention}!")
elif action == "list":
embed = discord.Embed(
title="🔐 Список ролей",
description="Список всех ролей на сервере",
color=discord.Color.blue()
)
roles = [r for r in interaction.guild.roles if r.name != "@everyone"]
roles.sort(key=lambda x: x.position, reverse=True)
if not roles:
embed.description = "📭 На сервере нет ролей (кроме @everyone)"
await interaction.response.send_message(embed=embed)
return
for r in roles[:25]:
member_count = len(r.members)
embed.add_field(
name=r.name,
value=f"ID: {r.id}\n👥 {member_count} чел.",
inline=True
)
if len(roles) > 25:
embed.set_footer(text=f"И ещё {len(roles)-25} ролей...")
await interaction.response.send_message(embed=embed)
elif action == "create":
if not name:
await interaction.response.send_message("❌ Использование: `/role create <название>`")
return
role = await interaction.guild.create_role(name=name)
log_action(interaction.user.id, "создание роли", name, "")
await interaction.response.send_message(f"✅ Роль **{name}** создана (ID: {role.id})!")
else:
await interaction.response.send_message("❌ Доступные действия: `add`, `remove`, `list`, `create`")
# =====================================================
# 🎯 ОСНОВНАЯ КОМАНДА
# =====================================================
@bot.tree.command(name="staff", description="👮 Открыть панель управления персоналом")
async def slash_staff(interaction: discord.Interaction):
embed = discord.Embed(
title="👮 Панель управления персоналом",
description="Управляйте персоналом и модерацией",
color=discord.Color.blue()
)
embed.add_field(
name="📋 Доступные действия",
value="• 👮 Назначение/увольнение\n"
"• ⚔️ Наказания (Бан/Мут/Кик/Разбан/Размут)\n"
"• ⚠️ Предупреждения (выдача/снятие/список)\n"
"• 🌐 Бан по IP / Разбан по IP\n"
"• 👑 Управление привилегиями (LP)\n"
"• ➕ Добавить новый ранг\n"
"• 📝 Заявки\n"
"• 📢 Рассылки\n"
"• 📋 Журнал действий\n"
"• 🔐 Управление ролями\n"
"• 📊 Статистика\n"
"• ⚙️ Настройки RCON",
inline=False
)
embed.set_footer(text="При 3 предупреждениях - авто-бан на 2 дня")
await interaction.response.send_message(embed=embed, view=StaffMainMenu())
message = await interaction.original_response()
add_panel(interaction.guild_id, interaction.channel_id, message.id, "main")
# =====================================================
# 🎮 КОМАНДЫ ДЛЯ RCON
# =====================================================
@bot.tree.command(name="mc", description="🎮 Выполнить команду на Minecraft сервере")
@app_commands.describe(
command="Команда для выполнения на сервере"
)
async def slash_mc(interaction: discord.Interaction, command: str):
if not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("❌ У вас нет прав для выполнения этой команды!", ephemeral=True)
return
await interaction.response.defer(ephemeral=True)
result, error = await execute_mc_command_async(command)
embed = discord.Embed(
title="🎮 Выполнение команды",
description=f"```{command}```",
color=discord.Color.blue()
)
embed.add_field(name="📡 Результат", value=f"```{error if error else result or '✅ Команда выполнена'}```", inline=False)
await interaction.followup.send(embed=embed, ephemeral=True)
# =====================================================
# 🔄 KEEP-ALIVE ДЛЯ RENDER
# =====================================================
from flask import Flask
import threading
app = Flask('')
@app.route('/')
def home():
return "👮 Mohist_Staff работает!"
def run():
app.run(host='0.0.0.0', port=10000)
def keep_alive():
t = threading.Thread(target=run)
t.start()
keep_alive()
print("✅ Keep-Alive запущен!")
# =====================================================
# 🚀 ЗАПУСК
# =====================================================
async def main():
async with bot:
print("🔄 Запуск Mohist_Staff...")
await bot.start(TOKEN)
if __name__ == "__main__":
try:
asyncio.run(main())
except discord.LoginFailure:
print("❌ Неверный токен!")
except KeyboardInterrupt:
print("\n🛑 Бот остановлен")
except Exception as e:
print(f"❌ Ошибка: {e}")