Загрузка данных
import discord
from discord.ext import commands
from discord import app_commands
import json
import os
import re
import time
import asyncio
import random
from collections import deque, defaultdict
from datetime import datetime, timedelta
# ================= КОНФИГ =================
TOKEN = "MTU1MTY2MDA3NDI2NTYxMjQ4OA.GCcw05.AStK2K9jc_kvHz-BwSYKrO-ZCHTxOuI6Dmgks8"
GUILD_ID = 1548045835143749822
# СУПЕР-АДМИНЫ (только они могут использовать команды модерации)
OWNER_IDS = [984844682087530626, 1462075826492407878]
# Каналы
LEADER_CHANNEL_ID = 1562802314576789585
ADMIN_CHANNEL_ID = 1562802325729452103
MEDIA_CHANNEL_ID = 1562802309707075627
REVIEW_CHANNEL_ID = 1563190052509458544
LOG_CHANNEL_ID = REVIEW_CHANNEL_ID
WELCOME_CHANNEL_ID = 1563190052509458544
TICKET_CATEGORY_NAME = "🎫 Тикеты"
STAFF_ROLE_NAME = "Администратор"
CURRENCY = "🪙"
# ==========================================
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
intents.reactions = True
bot = commands.Bot(command_prefix="!", intents=intents)
DATA_FILE = "applications.json"
ANTI_FILE = "anti.json"
WARN_FILE = "warnings.json"
USER_STATS_FILE = "user_stats.json"
TICKET_FILE = "active_tickets.json"
ECONOMY_FILE = "economy.json"
NOTES_FILE = "notes.json"
WHITELIST_FILE = "whitelist.json"
BACKUP_FILE = "channel_backups.json"
# ---------- Настройки защиты по умолчанию ----------
ANTI_DEFAULT = {
"anti_spam": True, "anti_link": True, "anti_raid": True, "anti_nuke": True,
"anti_mention": True, "anti_emoji": True, "anti_caps": True,
"nuke_hits": 2,
"nuke_window": 30,
"spam_max": 6,
"spam_window": 5,
"raid_joins": 6,
"raid_window": 10,
}
ANTI = dict(ANTI_DEFAULT)
# ---------- Whitelist ----------
WHITELIST = {
"users": [], # ID пользователей
"roles": [] # ID ролей
}
# ---------- Функции загрузки/сохранения ----------
def load_json(file, default):
if os.path.exists(file):
try:
with open(file, "r", encoding="utf-8") as f: return json.load(f)
except: pass
return default
def save_json(file, data):
try:
with open(file, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e: print(f"[SAVE] Ошибка {file}: {e}")
ANTI = {**ANTI_DEFAULT, **load_json(ANTI_FILE, {})}
WARNINGS = load_json(WARN_FILE, {})
USER_STATS = load_json(USER_STATS_FILE, {})
ACTIVE_TICKETS = load_json(TICKET_FILE, {})
ECONOMY = load_json(ECONOMY_FILE, {})
NOTES = load_json(NOTES_FILE, {})
WHITELIST = {**WHITELIST, **load_json(WHITELIST_FILE, {})}
CHANNEL_BACKUPS = load_json(BACKUP_FILE, {})
SHOP_ITEMS = {"VIP": {"price": 5000, "role": "VIP"}, "Premium": {"price": 10000, "role": "Premium"}}
def save_anti(): save_json(ANTI_FILE, ANTI)
def save_warnings(): save_json(WARN_FILE, WARNINGS)
def save_stats(): save_json(USER_STATS_FILE, USER_STATS)
def save_tickets(): save_json(TICKET_FILE, ACTIVE_TICKETS)
def save_economy(): save_json(ECONOMY_FILE, ECONOMY)
def save_notes(): save_json(NOTES_FILE, NOTES)
def save_whitelist(): save_json(WHITELIST_FILE, WHITELIST)
def save_backups(): save_json(BACKUP_FILE, CHANNEL_BACKUPS)
def get_stat(user_id, key="reputation"):
uid = str(user_id)
if uid not in USER_STATS: USER_STATS[uid] = {"tickets": 0, "reputation": 0, "messages": 0}
return USER_STATS[uid].get(key, 0)
def add_stat(user_id, key, amount=1):
uid = str(user_id)
if uid not in USER_STATS: USER_STATS[uid] = {"tickets": 0, "reputation": 0, "messages": 0}
USER_STATS[uid][key] = USER_STATS[uid].get(key, 0) + amount
save_stats()
def get_coins(user_id):
uid = str(user_id)
if uid not in ECONOMY: ECONOMY[uid] = {"coins": 0, "last_daily": 0}
return ECONOMY[uid].get("coins", 0)
def add_coins(user_id, amount):
uid = str(user_id)
if uid not in ECONOMY: ECONOMY[uid] = {"coins": 0, "last_daily": 0}
ECONOMY[uid]["coins"] = ECONOMY[uid].get("coins", 0) + amount
save_economy()
def find_app(app_id):
data = load_json(DATA_FILE, {})
for apps in data.values():
for app in apps:
if app.get("id") == app_id: return data, app
return data, None
# ---------- Служебное для защиты ----------
_HITS = {}
_spam_hist = defaultdict(deque)
_join_hist = defaultdict(deque)
_raid_until = {}
_orig_verification = {}
def is_owner(user_id): return user_id in OWNER_IDS
# ВАЖНО: теперь is_admin и is_staff = только овнер
def is_admin(member): return is_owner(member.id)
def is_staff(member): return is_owner(member.id)
def in_whitelist(member: discord.Member) -> bool:
"""Проверка: пользователь или одна из его ролей в вайт-листе защиты."""
if member.id in WHITELIST.get("users", []): return True
user_roles = {r.id for r in getattr(member, "roles", [])}
return bool(user_roles & set(WHITELIST.get("roles", [])))
def is_trusted(member):
"""Доверенный = овнер, владелец сервера, в вайт-листе защиты.
ВАЖНО: админ-права больше НЕ дают иммунитет от анти-нюка — это защита от взлома."""
if is_owner(member.id): return True
if member.guild.owner_id == member.id: return True
if in_whitelist(member): return True
return False
def _hit(key, window):
now = time.time()
lst = _HITS.setdefault(key, [])
lst[:] = [t for t in lst if now - t < window]
lst.append(now)
return len(lst)
async def log_alert(guild, title, desc, color=None):
ch = guild.get_channel(LOG_CHANNEL_ID) or bot.get_channel(LOG_CHANNEL_ID)
if not ch: return
embed = discord.Embed(title=title, description=desc, color=color or discord.Color.red(), timestamp=discord.utils.utcnow())
try: await ch.send(embed=embed)
except: pass
async def add_warning(member, reason, moderator=None):
uid = str(member.id)
if uid not in WARNINGS: WARNINGS[uid] = {"count": 0, "reasons": []}
WARNINGS[uid]["count"] += 1
WARNINGS[uid]["reasons"].append({"reason": reason, "mod": str(moderator), "time": str(datetime.now())})
save_warnings()
count = WARNINGS[uid]["count"]
await log_alert(member.guild, "⚠️ Предупреждение", f"{member.mention}\nМодератор: {moderator.mention if moderator else 'Система'}\nПричина: {reason}\nВсего: {count}/3", discord.Color.yellow())
if count >= 3:
try:
await member.timeout(timedelta(minutes=60), reason="Лимит предупреждений")
WARNINGS[uid]["count"] = 0
WARNINGS[uid]["reasons"].clear()
save_warnings()
await log_alert(member.guild, "🔇 Автомут", f"{member.mention} получил мут на 60 мин.", discord.Color.orange())
except: pass
# ================= БЭКАП КАНАЛОВ =================
def backup_channel(channel: discord.abc.GuildChannel):
"""Сохраняет данные канала для восстановления."""
gid = str(channel.guild.id)
CHANNEL_BACKUPS.setdefault(gid, {})
if isinstance(channel, discord.CategoryChannel):
data = {"type": "category", "name": channel.name, "position": channel.position}
elif isinstance(channel, discord.TextChannel):
data = {
"type": "text", "name": channel.name, "topic": channel.topic,
"position": channel.position, "nsfw": channel.nsfw, "slowmode": channel.slowmode_delay,
"category_id": channel.category_id,
"overwrites": [
{"target_id": t.id, "target_type": "role" if isinstance(t, discord.Role) else "member",
"allow": o.pair()[0].value, "deny": o.pair()[1].value}
for t, o in channel.overwrites.items()
]
}
elif isinstance(channel, discord.VoiceChannel):
data = {
"type": "voice", "name": channel.name, "bitrate": channel.bitrate,
"user_limit": channel.user_limit, "position": channel.position,
"category_id": channel.category_id,
"overwrites": [
{"target_id": t.id, "target_type": "role" if isinstance(t, discord.Role) else "member",
"allow": o.pair()[0].value, "deny": o.pair()[1].value}
for t, o in channel.overwrites.items()
]
}
else:
return
CHANNEL_BACKUPS[gid][str(channel.id)] = data
save_backups()
async def restore_channel(guild: discord.Guild, channel_id: int) -> bool:
"""Восстанавливает канал из бэкапа."""
gid = str(guild.id)
data = CHANNEL_BACKUPS.get(gid, {}).pop(str(channel_id), None)
if not data: return False
try:
category = None
if data.get("category_id"):
category = guild.get_channel(data["category_id"])
overwrites = {}
for ow in data.get("overwrites", []):
target = guild.get_role(ow["target_id"]) if ow["target_type"] == "role" else guild.get_member(ow["target_id"])
if target:
overwrites[target] = discord.PermissionOverwrite.from_pair(
discord.Permissions(ow["allow"]), discord.Permissions(ow["deny"])
)
if data["type"] == "category":
await guild.create_category(data["name"], position=data.get("position", 0))
elif data["type"] == "text":
await guild.create_text_channel(
data["name"], category=category, topic=data.get("topic"),
nsfw=data.get("nsfw", False), slowmode_delay=data.get("slowmode", 0),
overwrites=overwrites, position=data.get("position", 0)
)
elif data["type"] == "voice":
await guild.create_voice_channel(
data["name"], category=category, bitrate=data.get("bitrate", 64000),
user_limit=data.get("user_limit", 0), overwrites=overwrites,
position=data.get("position", 0)
)
save_backups()
return True
except Exception as e:
print(f"[RESTORE] Ошибка: {e}")
save_backups()
return False
async def strip_roles_and_kick(guild: discord.Guild, member: discord.Member, reason: str):
"""Снимает все роли и кикает (для анти-нюка)."""
try:
# Снимаем все роли, которые бот может снять
removable = [r for r in member.roles if r != guild.default_role and r.managed is False
and r < guild.me.top_role]
if removable:
await member.remove_roles(*removable, reason=reason)
except Exception as e:
print(f"[STRIP] {e}")
try:
await member.kick(reason=reason)
except Exception as e:
print(f"[KICK] {e}")
# ================= МОДАЛЬНЫЕ ОКНА =================
class NoteModal(discord.ui.Modal, title="Заметка о пользователе"):
def __init__(self, user_id: int):
super().__init__()
self.user_id = str(user_id)
self.note = discord.ui.TextInput(label="Текст заметки", style=discord.TextStyle.paragraph, required=True, max_length=500)
self.add_item(self.note)
async def on_submit(self, interaction: discord.Interaction):
if self.user_id not in NOTES: NOTES[self.user_id] = []
NOTES[self.user_id].append({
"text": self.note.value,
"author": str(interaction.user),
"time": datetime.now().strftime("%Y-%m-%d %H:%M")
})
save_notes()
await interaction.response.send_message("✅ Заметка добавлена (видна только модераторам).", ephemeral=True)
# ================= НАСТРОЙКА ЗАЩИТЫ =================
class AntiSettingsView(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
for key in ["anti_spam", "anti_link", "anti_raid", "anti_nuke", "anti_mention", "anti_emoji", "anti_caps"]:
label = f"{'✅' if ANTI.get(key) else '❌'} {key}"
b = discord.ui.Button(label=label, custom_id=f"toggle_{key}", style=discord.ButtonStyle.secondary)
b.callback = self._make_toggle(key)
self.add_item(b)
def _make_toggle(self, key):
async def cb(i: discord.Interaction):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
ANTI[key] = not ANTI[key]; save_anti()
await i.response.edit_message(embed=anti_embed(), view=AntiSettingsView())
return cb
def anti_embed():
icons = {"anti_spam": "💬", "anti_link": "🔗", "anti_raid": "🛡️", "anti_nuke": "💣", "anti_mention": "📢", "anti_emoji": "😡", "anti_caps": "🔠"}
desc = "\n".join(f"{icons.get(k, '•')} **{k}** — {'включено ✅' if v else 'выключено ❌'}" for k, v in ANTI.items() if k in icons)
desc += f"\n\n⚙️ **Анти-Нюк:** {ANTI['nuke_hits']} удалений за {ANTI['nuke_window']} сек."
desc += f"\n📋 **Whitelist:** {len(WHITELIST.get('users', []))} юзеров, {len(WHITELIST.get('roles', []))} ролей"
return discord.Embed(title="⚙️ Панель защиты", description=desc, color=discord.Color.blurple())
# ================= ЗАЩИТА =================
class Protection(commands.Cog):
def __init__(self, bot_): self.bot = bot_
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
if message.guild is None or message.author.bot or message.webhook_id: return
if is_trusted(message.author): return
add_stat(message.author.id, "messages", 1)
content = message.content or ""
if ANTI["anti_link"]:
INVITE_RE = re.compile(r"discord(\.gg|\.com/invite|app\.com/invite)/[\w-]+", re.I)
LINK_RE = re.compile(r"https?://|www\.", re.I)
allowed = ("youtube.com", "youtu.be", "twitch.tv", "tiktok.com", "vk.com", "flowdm.fun", "discord.com/channels")
if INVITE_RE.search(content) or (LINK_RE.search(content) and not any(d in content.lower() for d in allowed)):
try: await message.delete()
except: pass
return await add_warning(message.author, "Ссылки", bot.user)
if ANTI["anti_mention"] and len(message.mentions) >= 5:
try: await message.delete()
except: pass
return await add_warning(message.author, "Массовые упоминания", bot.user)
if ANTI["anti_emoji"] and len(re.findall(r'<a?:\w+:\d+>|[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF\U0001F1E0-\U0001F1FF]', content)) > 10:
try: await message.delete()
except: pass
return await add_warning(message.author, "Спам эмодзи", bot.user)
if ANTI["anti_caps"] and len(content) > 10:
letters = [c for c in content if c.isalpha()]
if letters and (sum(1 for c in letters if c.isupper()) / len(letters)) > 0.7:
try: await message.delete()
except: pass
return await add_warning(message.author, "КАПС", bot.user)
if ANTI["anti_spam"]:
uid = message.author.id; now = time.time()
dq = _spam_hist[uid]; dq.append(now)
while dq and now - dq[0] > ANTI["spam_window"]: dq.popleft()
if len(dq) > ANTI["spam_max"]:
dq.clear()
try: await message.delete()
except: pass
await add_warning(message.author, "Флуд", bot.user)
@commands.Cog.listener()
async def on_member_join(self, member: discord.Member):
ch = member.guild.get_channel(WELCOME_CHANNEL_ID)
if ch:
embed = discord.Embed(title=f"👋 Добро пожаловать, {member.name}!", description=f"Ты вступил на сервер **{member.guild.name}**.", color=discord.Color.green())
embed.set_thumbnail(url=member.display_avatar.url)
try: await ch.send(embed=embed)
except: pass
if not ANTI["anti_raid"] or member.bot: return
if in_whitelist(member): return
g = member.guild; now = time.time()
dq = _join_hist[g.id]; dq.append(now)
while dq and now - dq[0] > ANTI["raid_window"]: dq.popleft()
if _raid_until.get(g.id) is None and len(dq) >= ANTI["raid_joins"]:
_raid_until[g.id] = discord.utils.utcnow() + timedelta(minutes=15)
_orig_verification[g.id] = g.verification_level
try: await g.edit(verification_level=discord.VerificationLevel.high)
except: pass
await log_alert(g, "🛡️ РЕЖИМ АНТИРЕЙДА", f"Включен на 15 мин. Верификация: high.", discord.Color.red())
asyncio.create_task(self._end_raid_later(g))
if _raid_until.get(g.id) and _raid_until[g.id] > discord.utils.utcnow():
if (discord.utils.utcnow() - member.created_at) < timedelta(days=3):
try: await member.kick(reason="Анти-рейд")
except: pass
async def _end_raid_later(self, g):
await asyncio.sleep(15 * 60)
if _raid_until.get(g.id) and _raid_until[g.id] <= discord.utils.utcnow():
_raid_until.pop(g.id, None)
orig = _orig_verification.pop(g.id, None)
if orig:
try: await g.edit(verification_level=orig)
except: pass
await log_alert(g, "🛡️ Антирейд снят", "Верификация возвращена.", discord.Color.green())
# ---- БЭКАПЫ ----
@commands.Cog.listener()
async def on_guild_channel_create(self, channel):
backup_channel(channel)
@commands.Cog.listener()
async def on_guild_channel_update(self, before, after):
backup_channel(after)
@commands.Cog.listener()
async def on_guild_join(self, guild):
for ch in guild.channels: backup_channel(ch)
# ---- АНТИ-НЮК ----
async def _find_actor(self, guild, action, target_id):
try:
async for entry in guild.audit_logs(limit=5, action=action):
if entry.target and entry.target.id == target_id:
if (discord.utils.utcnow() - entry.created_at) <= timedelta(seconds=20):
return entry.user
except: pass
return None
async def _handle_nuke(self, guild, actor, what, target_name):
member = guild.get_member(actor.id)
if not member:
await log_alert(guild, "💣 Анти-нюк", f"**{actor}** {what} **{target_name}** (не на сервере, действия не применены).", discord.Color.red())
return
# ВАЖНО: анти-нюк работает на всех, кроме овнеров
if is_owner(member.id):
await log_alert(guild, "🛠️ Доверенный (овнер)", f"**{actor}** {what} **{target_name}**. Игнорировано.", discord.Color.blue())
return
hits = _hit((guild.id, actor.id, "destroy"), ANTI["nuke_window"])
await log_alert(guild, "💣 Анти-нюк", f"**{actor}** {what} **{target_name}**.\nСрабатываний: {hits}/{ANTI['nuke_hits']}.", discord.Color.red())
if hits >= ANTI["nuke_hits"]:
await strip_roles_and_kick(guild, member, "Анти-нюк: массовое удаление каналов/ролей")
await log_alert(guild, "🔨 Анти-нюк: КИК + снятие ролей", f"**{actor}** наказан.", discord.Color.dark_red())
@commands.Cog.listener()
async def on_guild_channel_delete(self, channel):
if not ANTI["anti_nuke"]: return
# Восстанавливаем ВСЕГДА, даже если это овнер (бэкапы не помешают)
g = channel.guild
actor = await self._find_actor(g, discord.AuditLogAction.channel_delete, channel.id)
# Восстановление
restored = await restore_channel(g, channel.id)
if not actor or actor.bot:
if restored:
await log_alert(g, "♻️ Канал восстановлен", f"**{channel.name}** пересоздан (актор не найден).", discord.Color.green())
return
await self._handle_nuke(g, actor, "удалил канал", channel.name)
if restored:
await log_alert(g, "♻️ Канал восстановлен", f"**{channel.name}** пересоздан.", discord.Color.green())
@commands.Cog.listener()
async def on_guild_role_delete(self, role):
if not ANTI["anti_nuke"]: return
g = role.guild
actor = await self._find_actor(g, discord.AuditLogAction.role_delete, role.id)
if not actor or actor.bot: return
await self._handle_nuke(g, actor, "удалил роль", role.name)
# ---- Остальные анти-нюк хуки: баны/кики ----
@commands.Cog.listener()
async def on_member_ban(self, guild, user):
if not ANTI["anti_nuke"]: return
actor = await self._find_actor(guild, discord.AuditLogAction.ban, user.id)
if not actor or actor.bot: return
await self._handle_nuke(guild, actor, "забанил", str(user))
@commands.Cog.listener()
async def on_member_remove(self, member):
if not ANTI["anti_nuke"]: return
g = member.guild
actor = await self._find_actor(g, discord.AuditLogAction.kick, member.id)
if not actor or actor.bot: return
await self._handle_nuke(g, actor, "кикнул", str(member))
# ================= МОДАЛЬНЫЕ ОКНА ДЛЯ ЗАЯВОК =================
class ApplicationModal(discord.ui.Modal):
def __init__(self, role_type: str):
super().__init__(title=f"Анкета на {role_type}")
self.role_type = role_type
self.age = discord.ui.TextInput(label="Ваш возраст", placeholder="18", required=True, max_length=3)
self.experience = discord.ui.TextInput(label="Опыт", style=discord.TextStyle.paragraph, required=True, max_length=500)
self.why_me = discord.ui.TextInput(label="Почему именно вы?", style=discord.TextStyle.paragraph, required=True, max_length=500)
self.time_online = discord.ui.TextInput(label="Онлайн", placeholder="Сколько часов в день?", required=True, max_length=100)
self.contact = discord.ui.TextInput(label="Контакт (Discord/Telegram)", placeholder="@nickname", required=True, max_length=100)
for item in (self.age, self.experience, self.why_me, self.time_online, self.contact): self.add_item(item)
async def on_submit(self, interaction: discord.Interaction):
data = load_json(DATA_FILE, {})
uid = str(interaction.user.id)
data.setdefault(uid, [])
app_id = f"{int(datetime.now().timestamp())}_{uid}"
app = {
"id": app_id, "role_type": self.role_type, "user_id": uid, "user_name": str(interaction.user),
"age": self.age.value, "experience": self.experience.value, "why_me": self.why_me.value,
"time_online": self.time_online.value, "contact": self.contact.value,
"status": "pending", "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}
data[uid].append(app)
save_json(DATA_FILE, data)
ch = bot.get_channel(REVIEW_CHANNEL_ID)
if ch:
embed = discord.Embed(title=f"📋 Новая заявка: {self.role_type}", color=discord.Color.blue(), timestamp=datetime.now())
embed.add_field(name="Кандидат", value=f"{interaction.user.mention} ({interaction.user})", inline=False)
embed.add_field(name="ID", value=app_id, inline=False)
embed.add_field(name="Возраст", value=self.age.value, inline=True)
embed.add_field(name="Онлайн", value=self.time_online.value, inline=True)
embed.add_field(name="Контакт", value=self.contact.value, inline=True)
embed.add_field(name="Опыт", value=self.experience.value, inline=False)
embed.add_field(name="Мотивация", value=self.why_me.value, inline=False)
embed.set_footer(text=f"User ID: {uid}")
await ch.send(embed=embed, view=ReviewView(app_id, uid, self.role_type))
await interaction.response.send_message("✅ Заявка отправлена! Ожидайте.", ephemeral=True)
class ReviewView(discord.ui.View):
def __init__(self, app_id: str, user_id: str, role_type: str):
super().__init__(timeout=None)
self.app_id, self.user_id, self.role_type = app_id, user_id, role_type
b1 = discord.ui.Button(label="Принять", style=discord.ButtonStyle.success, emoji="✅", custom_id=f"rv:acc:{app_id}")
b1.callback = self.accept
b2 = discord.ui.Button(label="Отклонить", style=discord.ButtonStyle.danger, emoji="❌", custom_id=f"rv:rej:{app_id}")
b2.callback = self.reject
self.add_item(b1); self.add_item(b2)
async def _finish(self, interaction: discord.Interaction, accepted: bool):
if not is_staff(interaction.user):
return await interaction.response.send_message("❌ Только овнер.", ephemeral=True)
data, app = find_app(self.app_id)
if app:
app["status"] = "accepted" if accepted else "rejected"
app["reviewed_by"] = str(interaction.user)
save_json(DATA_FILE, data)
if accepted: add_stat(self.user_id, "reputation", 10)
embed = interaction.message.embeds[0]
embed.color = discord.Color.green() if accepted else discord.Color.red()
embed.add_field(name="Статус", value=("✅ Принято" if accepted else "❌ Отклонено") + f" ({interaction.user.mention})", inline=False)
await interaction.response.edit_message(embed=embed, view=None)
try:
user = await bot.fetch_user(int(self.user_id))
await user.send(f"🎉 Ваша заявка на **{self.role_type}** была **принята**!" if accepted else f"😔 Ваша заявка на **{self.role_type}** была **отклонена**.")
except: pass
async def accept(self, i): await self._finish(i, True)
async def reject(self, i): await self._finish(i, False)
class ApplyButtonView(discord.ui.View):
def __init__(self, role_type: str):
super().__init__(timeout=None)
b = discord.ui.Button(label=f"Подать заявку", style=discord.ButtonStyle.primary, emoji="📝", custom_id=f"apply_{role_type.lower()}")
b.callback = self.open_modal
self.role_type = role_type
self.add_item(b)
async def open_modal(self, i: discord.Interaction): await i.response.send_modal(ApplicationModal(self.role_type))
# ================= ТИКЕТЫ =================
class TicketPanelView(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
b = discord.ui.Button(label="Создать обращение", style=discord.ButtonStyle.success, emoji="🎫", custom_id="create_ticket")
b.callback = self.create_ticket
self.add_item(b)
async def create_ticket(self, interaction: discord.Interaction):
guild = interaction.guild; uid = interaction.user.id
for ch_id, owner_id in list(ACTIVE_TICKETS.items()):
if owner_id == uid:
ch = guild.get_channel(int(ch_id))
if ch: return await interaction.response.send_message(f"❌ У вас уже есть тикет: {ch.mention}", ephemeral=True)
else: del ACTIVE_TICKETS[ch_id]; save_tickets()
overwrites = {
guild.default_role: discord.PermissionOverwrite(read_messages=False),
interaction.user: discord.PermissionOverwrite(read_messages=True, send_messages=True),
guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True)
}
staff_role = discord.utils.get(guild.roles, name=STAFF_ROLE_NAME)
if staff_role: overwrites[staff_role] = discord.PermissionOverwrite(read_messages=True, send_messages=True)
category = discord.utils.get(guild.categories, name=TICKET_CATEGORY_NAME)
if not category:
try: category = await guild.create_category(TICKET_CATEGORY_NAME)
except: pass
try:
channel = await guild.create_text_channel(name=f"ticket-{interaction.user.name}", category=category, overwrites=overwrites)
ACTIVE_TICKETS[str(channel.id)] = uid
save_tickets()
add_stat(uid, "tickets", 1)
embed = discord.Embed(title="🎫 Новое обращение", description=f"Привет, {interaction.user.mention}!\nОпишите вашу проблему, и персонал скоро ответит.", color=discord.Color.green())
close_btn = discord.ui.Button(label="Закрыть тикет", style=discord.ButtonStyle.danger, emoji="🔒", custom_id="close_ticket")
async def close_cb(i: discord.Interaction):
if not (is_staff(i.user) or i.user.id == uid): return await i.response.send_message("❌ Вы не можете закрыть этот тикет.", ephemeral=True)
await i.response.send_message("🔒 Тикет закрывается...", ephemeral=True)
if str(i.channel.id) in ACTIVE_TICKETS: del ACTIVE_TICKETS[str(i.channel.id)]; save_tickets()
await asyncio.sleep(3)
try: await i.channel.delete(reason="Тикет закрыт")
except: pass
close_btn.callback = close_cb
view = discord.ui.View(); view.add_item(close_btn)
await channel.send(embed=embed, view=view)
await interaction.response.send_message(f"✅ Тикет создан: {channel.mention}", ephemeral=True)
except Exception as e:
await interaction.response.send_message(f"❌ Ошибка: {e}", ephemeral=True)
# ================= КОМАНДЫ =================
# -------- ПАНЕЛЬ ЗАЩИТЫ --------
@bot.tree.command(name="защита", description="Панель настройки защиты (овнер)")
async def protect_cmd(i: discord.Interaction):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
await i.response.send_message(embed=anti_embed(), view=AntiSettingsView())
# -------- НАСТРОЙКИ АНТИ-НЮКА --------
@bot.tree.command(name="антинюк", description="Настроить анти-нюк (овнер)")
@app_commands.describe(hits="Сколько удалений до кика", window="За сколько секунд")
async def antinuke_cfg(i: discord.Interaction, hits: int, window: int):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
ANTI["nuke_hits"] = hits
ANTI["nuke_window"] = window
save_anti()
await i.response.send_message(f"✅ Анти-нюк: {hits} удалений за {window} сек.", ephemeral=True)
# -------- WHITELIST --------
@bot.tree.command(name="вайтлист", description="Посмотреть whitelist защиты (овнер)")
async def whitelist_show(i: discord.Interaction):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
users = WHITELIST.get("users", [])
roles = WHITELIST.get("roles", [])
embed = discord.Embed(title="📋 Whitelist защиты", color=discord.Color.blue())
embed.add_field(name="👤 Пользователи", value=", ".join(f"<@{u}>" for u in users) or "Пусто", inline=False)
embed.add_field(name="🎭 Роли", value=", ".join(f"<@&{r}>" for r in roles) or "Пусто", inline=False)
await i.response.send_message(embed=embed, ephemeral=True)
@bot.tree.command(name="вайтлист_добавить_юзера", description="Добавить пользователя в whitelist (овнер)")
async def whitelist_add_user(i: discord.Interaction, user: discord.User):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
if user.id in WHITELIST["users"]: return await i.response.send_message("❌ Уже в whitelist.", ephemeral=True)
WHITELIST["users"].append(user.id); save_whitelist()
await i.response.send_message(f"✅ {user.mention} добавлен в whitelist защиты (анти-спам, анти-линк, анти-рейд и т.д. не срабатывают).", ephemeral=True)
@bot.tree.command(name="вайтлист_удалить_юзера", description="Убрать пользователя из whitelist (овнер)")
async def whitelist_remove_user(i: discord.Interaction, user: discord.User):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
if user.id not in WHITELIST["users"]: return await i.response.send_message("❌ Не в whitelist.", ephemeral=True)
WHITELIST["users"].remove(user.id); save_whitelist()
await i.response.send_message(f"✅ {user.mention} убран из whitelist.", ephemeral=True)
@bot.tree.command(name="вайтлист_добавить_роль", description="Добавить роль в whitelist (овнер)")
async def whitelist_add_role(i: discord.Interaction, role: discord.Role):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
if role.id in WHITELIST["roles"]: return await i.response.send_message("❌ Уже в whitelist.", ephemeral=True)
WHITELIST["roles"].append(role.id); save_whitelist()
await i.response.send_message(f"✅ Роль {role.mention} добавлена в whitelist защиты.", ephemeral=True)
@bot.tree.command(name="вайтлист_удалить_роль", description="Убрать роль из whitelist (овнер)")
async def whitelist_remove_role(i: discord.Interaction, role: discord.Role):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
if role.id not in WHITELIST["roles"]: return await i.response.send_message("❌ Не в whitelist.", ephemeral=True)
WHITELIST["roles"].remove(role.id); save_whitelist()
await i.response.send_message(f"✅ Роль {role.mention} убрана из whitelist.", ephemeral=True)
# -------- МОДЕРАЦИЯ (ТОЛЬКО ОВНЕР) --------
@bot.tree.command(name="заметка", description="Добавить заметку о пользователе (скрыто) (овнер)")
async def add_note(i: discord.Interaction, member: discord.Member):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
await i.response.send_modal(NoteModal(member.id))
@bot.tree.command(name="заметки", description="Посмотреть заметки о пользователе (овнер)")
async def view_notes(i: discord.Interaction, member: discord.Member):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
uid = str(member.id)
if uid not in NOTES or not NOTES[uid]: return await i.response.send_message("📭 Заметок нет.", ephemeral=True)
embed = discord.Embed(title=f"📝 Заметки о {member.name}", color=discord.Color.orange())
for n in NOTES[uid]:
embed.add_field(name=f"Автор: {n['author']} ({n['time']})", value=n['text'], inline=False)
await i.response.send_message(embed=embed, ephemeral=True)
@bot.tree.command(name="мут", description="Выдать мут (овнер)")
async def mute_cmd(i: discord.Interaction, member: discord.Member, minutes: int, reason: str = "Не указана"):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
if is_trusted(member): return await i.response.send_message("❌ Нельзя наказать этого пользователя.", ephemeral=True)
try:
await member.timeout(timedelta(minutes=minutes), reason=reason)
await i.response.send_message(f"🔇 {member.mention} замучен на {minutes} мин. Причина: {reason}")
await log_alert(i.guild, "🔇 Ручной мут", f"Модератор: {i.user.mention}\nЦель: {member.mention}\nВремя: {minutes} мин.\nПричина: {reason}", discord.Color.orange())
except Exception as e: await i.response.send_message(f"❌ Ошибка: {e}", ephemeral=True)
@bot.tree.command(name="размут", description="Снять мут (овнер)")
async def unmute_cmd(i: discord.Interaction, member: discord.Member):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
try:
await member.timeout(None)
await i.response.send_message(f"🔊 {member.mention} размучен.")
except Exception as e: await i.response.send_message(f"❌ Ошибка: {e}", ephemeral=True)
@bot.tree.command(name="варн", description="Выдать предупреждение (овнер)")
async def warn_cmd(i: discord.Interaction, member: discord.Member, reason: str = "Не указана"):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
if is_trusted(member): return await i.response.send_message("❌ Нельзя выдать варн этому пользователю.", ephemeral=True)
await add_warning(member, reason, i.user)
await i.response.send_message(f"⚠️ {member.mention} получил предупреждение. Причина: {reason}", ephemeral=True)
@bot.tree.command(name="снять_варн", description="Снять 1 варн (овнер)")
async def unwarn_cmd(i: discord.Interaction, member: discord.Member):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
uid = str(member.id)
if uid in WARNINGS and WARNINGS[uid]["count"] > 0:
WARNINGS[uid]["count"] -= 1; save_warnings()
await i.response.send_message(f"✅ Снят 1 варн с {member.mention}. Осталось: {WARNINGS[uid]['count']}", ephemeral=True)
else:
await i.response.send_message(f"❌ У {member.mention} нет варнов.", ephemeral=True)
@bot.tree.command(name="очистить_варны", description="Полностью очистить варны (овнер)")
async def clear_warns(i: discord.Interaction, member: discord.Member):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
uid = str(member.id)
if uid in WARNINGS:
WARNINGS[uid] = {"count": 0, "reasons": []}; save_warnings()
await i.response.send_message(f"✅ Все варны сняты с {member.mention}.", ephemeral=True)
@bot.tree.command(name="варны", description="Посмотреть варны (овнер)")
async def list_warns(i: discord.Interaction, member: discord.Member):
uid = str(member.id)
if uid not in WARNINGS or WARNINGS[uid]["count"] == 0: return await i.response.send_message(f"У {member.mention} нет варнов.", ephemeral=True)
embed = discord.Embed(title=f"⚠️ Варны {member.name}", description=f"Всего: {WARNINGS[uid]['count']}/3", color=discord.Color.red())
for w in WARNINGS[uid]["reasons"]:
embed.add_field(name=w["reason"], value=f"Модератор: {w['mod']}\nДата: {w['time']}", inline=False)
await i.response.send_message(embed=embed, ephemeral=True)
@bot.tree.command(name="бан", description="Забанить (овнер)")
async def ban_cmd(i: discord.Interaction, member: discord.Member, reason: str = "Не указана"):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
if is_trusted(member): return await i.response.send_message("❌ Нельзя забанить этого пользователя.", ephemeral=True)
try:
await member.ban(reason=f"{reason} | {i.user}")
await i.response.send_message(f"🔨 {member.mention} забанен. Причина: {reason}")
await log_alert(i.guild, "🔨 Бан", f"Модератор: {i.user.mention}\nЦель: {member}\nПричина: {reason}", discord.Color.dark_red())
except Exception as e: await i.response.send_message(f"❌ Ошибка: {e}", ephemeral=True)
@bot.tree.command(name="кик", description="Кикнуть (овнер)")
async def kick_cmd(i: discord.Interaction, member: discord.Member, reason: str = "Не указана"):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
if is_trusted(member): return await i.response.send_message("❌ Нельзя кикнуть этого пользователя.", ephemeral=True)
try:
await member.kick(reason=f"{reason} | {i.user}")
await i.response.send_message(f"👢 {member.mention} кикнут. Причина: {reason}")
except Exception as e: await i.response.send_message(f"❌ Ошибка: {e}", ephemeral=True)
@bot.tree.command(name="разбан", description="Разбанить по ID (овнер)")
async def unban_cmd(i: discord.Interaction, user_id: str, reason: str = "Не указана"):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
try:
user = await bot.fetch_user(int(user_id))
await i.guild.unban(user, reason=reason)
await i.response.send_message(f"✅ {user} разбанен. Причина: {reason}", ephemeral=True)
except Exception as e: await i.response.send_message(f"❌ Ошибка: {e}", ephemeral=True)
@bot.tree.command(name="выдать_роль", description="Выдать роль (овнер)")
async def give_role_cmd(i: discord.Interaction, member: discord.Member, role: discord.Role):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
try:
await member.add_roles(role)
await i.response.send_message(f"✅ {member.mention} выдана роль {role.mention}.", ephemeral=True)
except Exception as e: await i.response.send_message(f"❌ Ошибка: {e}", ephemeral=True)
@bot.tree.command(name="снять_роль", description="Снять роль (овнер)")
async def remove_role_cmd(i: discord.Interaction, member: discord.Member, role: discord.Role):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
try:
await member.remove_roles(role)
await i.response.send_message(f"✅ У {member.mention} снята роль {role.mention}.", ephemeral=True)
except Exception as e: await i.response.send_message(f"❌ Ошибка: {e}", ephemeral=True)
@bot.tree.command(name="очистить", description="Удалить сообщения (овнер)")
async def clear_cmd(i: discord.Interaction, amount: int):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
await i.response.defer(ephemeral=True)
deleted = await i.channel.purge(limit=amount)
await i.followup.send(f"✅ Удалено {len(deleted)}.", ephemeral=True)
@bot.tree.command(name="сказать", description="Отправить сообщение от имени бота (овнер)")
async def say_cmd(i: discord.Interaction, text: str):
if not is_staff(i.user): return await i.response.send_message("❌ Нет прав.", ephemeral=True)
await i.channel.send(text)
await i.response.send_message("✅ Отправлено.", ephemeral=True)
# -------- ПУБЛИЧНЫЕ КОМАНДЫ --------
@bot.tree.command(name="инфо", description="Информация о пользователе")
async def user_info(i: discord.Interaction, member: discord.Member):
rep = get_stat(member.id, "reputation")
tickets = get_stat(member.id, "tickets")
msgs = get_stat(member.id, "messages")
warns = WARNINGS.get(str(member.id), {}).get("count", 0)
coins = get_coins(member.id)
notes_count = len(NOTES.get(str(member.id), []))
embed = discord.Embed(title=f"👤 Информация: {member.name}", color=discord.Color.blue())
embed.set_thumbnail(url=member.display_avatar.url)
embed.add_field(name="ID", value=member.id, inline=False)
embed.add_field(name="Создан", value=member.created_at.strftime("%d.%m.%Y"), inline=True)
embed.add_field(name="Зашел", value=member.joined_at.strftime("%d.%m.%Y") if member.joined_at else "Неизвестно", inline=True)
embed.add_field(name="Репутация", value=f"⭐ {rep}", inline=True)
embed.add_field(name="Тикеты", value=f"🎫 {tickets}", inline=True)
embed.add_field(name="Сообщений", value=f"💬 {msgs}", inline=True)
embed.add_field(name="Монеты", value=f"{CURRENCY} {coins}", inline=True)
embed.add_field(name="Варны", value=f"⚠️ {warns}/3", inline=True)
embed.add_field(name="Заметки", value=f"📝 {notes_count}", inline=True)
await i.response.send_message(embed=embed)
@bot.tree.command(name="баланс", description="Посмотреть баланс монет")
async def balance_cmd(i: discord.Interaction):
await i.response.send_message(f"💰 Ваш баланс: **{get_coins(i.user.id)}** {CURRENCY}", ephemeral=True)
@bot.tree.command(name="бонус", description="Ежедневная награда")
async def daily_cmd(i: discord.Interaction):
uid = str(i.user.id)
if uid not in ECONOMY: ECONOMY[uid] = {"coins": 0, "last_daily": 0}
last = ECONOMY[uid].get("last_daily", 0); now = time.time()
if now - last < 86400: return await i.response.send_message("⏳ Вы уже получали награду сегодня.", ephemeral=True)
reward = random.randint(100, 500)
ECONOMY[uid]["coins"] += reward; ECONOMY[uid]["last_daily"] = now
save_economy()
await i.response.send_message(f"🎁 Вы получили бонус: **{reward}** {CURRENCY}!", ephemeral=True)
@bot.tree.command(name="магазин", description="Магазин ролей")
async def shop_cmd(i: discord.Interaction):
embed = discord.Embed(title="🛒 Магазин", color=discord.Color.gold())
for name, data in SHOP_ITEMS.items():
embed.add_field(name=name, value=f"Цена: {data['price']} {CURRENCY}\nРоль: {data['role']}", inline=False)
embed.set_footer(text="Используйте /купить [название]")
await i.response.send_message(embed=embed)
@bot.tree.command(name="купить", description="Купить предмет")
async def buy_cmd(i: discord.Interaction, item: str):
if item not in SHOP_ITEMS: return await i.response.send_message("❌ Нет такого предмета.", ephemeral=True)
price = SHOP_ITEMS[item]["price"]; coins = get_coins(i.user.id)
if coins < price: return await i.response.send_message("❌ Недостаточно монет.", ephemeral=True)
role = discord.utils.get(i.guild.roles, name=SHOP_ITEMS[item]["role"])
if not role: return await i.response.send_message("❌ Роль не найдена.", ephemeral=True)
add_coins(i.user.id, -price); await i.user.add_roles(role)
await i.response.send_message(f"✅ Вы купили **{item}**!", ephemeral=True)
@bot.tree.command(name="опрос", description="Создать опрос")
async def poll_cmd(i: discord.Interaction, question: str, options: str):
opts = [o.strip() for o in options.split(",") if o.strip()]
if len(opts) < 2 or len(opts) > 4: return await i.response.send_message("❌ 2-4 варианта.", ephemeral=True)
embed = discord.Embed(title=f"📊 {question}", description="\n".join(f"**{o}**: 0 голосов" for o in opts), color=discord.Color.green())
class PollView(discord.ui.View):
def __init__(self):
super().__init__(timeout=None); self.votes = {opt: 0 for opt in opts}
for opt in opts:
b = discord.ui.Button(label=opt, style=discord.ButtonStyle.secondary)
b.callback = self._make_cb(opt); self.add_item(b)
def _make_cb(self, opt):
async def cb(inter):
self.votes[opt] += 1
desc = "\n".join(f"**{k}**: {v} голосов" for k, v in self.votes.items())
await inter.response.edit_message(embed=discord.Embed(title=f"📊 {question}", description=desc, color=discord.Color.green()), view=self)
return cb
await i.response.send_message(embed=embed, view=PollView())
@bot.tree.command(name="топ", description="Топ по сообщениям")
async def top_cmd(i: discord.Interaction):
sorted_users = sorted(USER_STATS.items(), key=lambda x: x[1].get("messages", 0), reverse=True)[:10]
desc = ""
for idx, (uid, stats) in enumerate(sorted_users, 1):
user = i.guild.get_member(int(uid))
name = user.display_name if user else f"ID: {uid}"
desc += f"{idx}. **{name}** — {stats.get('messages', 0)} сообщений\n"
await i.response.send_message(embed=discord.Embed(title="🏆 Топ", description=desc or "Нет данных", color=discord.Color.gold()))
# -------- АДМИН-ПАНЕЛИ (ОВНЕР) --------
@bot.tree.command(name="автороль", description="Кнопка выдачи роли (овнер)")
async def autorole_cmd(i: discord.Interaction, role: discord.Role, emoji: str = "🎭"):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
class AutoRoleView(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
b = discord.ui.Button(label="Получить роль", style=discord.ButtonStyle.primary, emoji=emoji)
b.callback = self.give_role; self.add_item(b)
async def give_role(self, inter: discord.Interaction):
if role in inter.user.roles:
await inter.user.remove_roles(role); await inter.response.send_message(f"✅ Снята {role.name}.", ephemeral=True)
else:
await inter.user.add_roles(role); await inter.response.send_message(f"✅ Выдана {role.name}.", ephemeral=True)
await i.channel.send(embed=discord.Embed(title="🎭 Получение роли", description=f"Нажмите, чтобы получить {role.mention}", color=discord.Color.blue()), view=AutoRoleView())
await i.response.send_message("✅ Панель создана.", ephemeral=True)
@bot.tree.command(name="setup_panels", description="Панели заявок (овнер)")
async def setup_panels(i: discord.Interaction):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
await i.response.defer(ephemeral=True)
panels = [
(LEADER_CHANNEL_ID, "Лидер", "👑", discord.Color.gold(), "Хочешь стать лидером?"),
(ADMIN_CHANNEL_ID, "Администратор", "🛡️", discord.Color.blue(), "Хочешь помогать серверу?"),
(MEDIA_CHANNEL_ID, "Медиа", "🎥", discord.Color.purple(), "Создаёшь контент?")
]
for cid, role, emoji, color, desc in panels:
ch = bot.get_channel(cid)
if ch:
try: await ch.purge(limit=10, check=lambda m: m.author == bot.user)
except: pass
await ch.send(embed=discord.Embed(title=f"{emoji} Заявка на роль: {role}", description=desc, color=color), view=ApplyButtonView(role))
await i.followup.send("✅ Панели обновлены.", ephemeral=True)
@bot.tree.command(name="setup_tickets", description="Панель тикетов (овнер)")
async def setup_tickets(i: discord.Interaction):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
embed = discord.Embed(title="🎫 Служба поддержки", description="Нажмите кнопку ниже, чтобы создать обращение.", color=discord.Color.teal())
await i.channel.send(embed=embed, view=TicketPanelView())
await i.response.send_message("✅ Панель тикетов установлена.", ephemeral=True)
@bot.tree.command(name="статистика", description="Статистика сервера (овнер)")
async def stats_cmd(i: discord.Interaction):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
data = load_json(DATA_FILE, {})
total = sum(len(a) for a in data.values())
pend = sum(1 for a in data.values() for x in a if x["status"] == "pending")
acc = sum(1 for a in data.values() for x in a if x["status"] == "accepted")
embed = discord.Embed(title="📊 Статистика", color=discord.Color.gold())
embed.add_field(name="Всего заявок", value=str(total))
embed.add_field(name="В ожидании", value=str(pend))
embed.add_field(name="Принято", value=str(acc))
embed.add_field(name="Открытых тикетов", value=str(len(ACTIVE_TICKETS)))
embed.add_field(name="Whitelist юзеров", value=str(len(WHITELIST.get("users", []))))
embed.add_field(name="Whitelist ролей", value=str(len(WHITELIST.get("roles", []))))
await i.response.send_message(embed=embed, ephemeral=True)
@bot.tree.command(name="пинг", description="Проверить задержку бота")
async def ping_cmd(i: discord.Interaction):
await i.response.send_message(f"🏓 Понг! Задержка: **{round(bot.latency*1000)}ms**", ephemeral=True)
@bot.tree.command(name="создать_бэкапы", description="Сделать бэкап всех каналов (овнер)")
async def backup_all(i: discord.Interaction):
if not is_admin(i.user): return await i.response.send_message("❌ Только овнер.", ephemeral=True)
await i.response.defer(ephemeral=True)
for ch in i.guild.channels: backup_channel(ch)
await i.followup.send(f"✅ Бэкапы сохранены: {len(i.guild.channels)} каналов.", ephemeral=True)
# ================= ЗАПУСК =================
@bot.event
async def setup_hook():
for role in ("Лидер", "Администратор", "Медиа"): bot.add_view(ApplyButtonView(role))
bot.add_view(AntiSettingsView())
bot.add_view(TicketPanelView())
for ch_id in list(ACTIVE_TICKETS.keys()):
close_btn = discord.ui.Button(label="Закрыть тикет", style=discord.ButtonStyle.danger, emoji="🔒", custom_id="close_ticket")
async def close_cb(i: discord.Interaction):
await i.response.send_message("🔒 Закрывается...", ephemeral=True)
if str(i.channel.id) in ACTIVE_TICKETS: del ACTIVE_TICKETS[str(i.channel.id)]; save_tickets()
await asyncio.sleep(3)
try: await i.channel.delete()
except: pass
close_btn.callback = close_cb
v = discord.ui.View(timeout=None); v.add_item(close_btn)
bot.add_view(v)
data = load_json(DATA_FILE, {})
for apps in data.values():
for app in apps:
if app.get("status") == "pending":
bot.add_view(ReviewView(app["id"], app["user_id"], app["role_type"]))
await bot.add_cog(Protection(bot))
@bot.event
async def on_ready():
print(f"Бот {bot.user} запущен! Владельцы: {OWNER_IDS}")
try:
await bot.tree.sync()
print("Команды синхронизированы.")
except Exception as e: print(f"Ошибка синхронизации: {e}")
if __name__ == "__main__":
bot.run(TOKEN)