WL_Bots/WL level bots/bot_level.py

951 lines
37 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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
import math
from datetime import datetime, timedelta
# =====================================================
# 📁 РАБОТА С ДАННЫМИ
# =====================================================
LEVEL_FILE = "level_data.json"
CONFIG_FILE = "level_config.json"
VOICE_TIME_FILE = "voice_time.json"
PROFILE_FILE = "user_profiles.json"
def load_level_data():
try:
with open(LEVEL_FILE, 'r') as f:
return json.load(f)
except:
return {}
def save_level_data(data):
with open(LEVEL_FILE, 'w') as f:
json.dump(data, f, indent=4)
def load_voice_time():
try:
with open(VOICE_TIME_FILE, 'r') as f:
return json.load(f)
except:
return {}
def save_voice_time(data):
with open(VOICE_TIME_FILE, 'w') as f:
json.dump(data, f, indent=4)
def load_profiles():
try:
with open(PROFILE_FILE, 'r') as f:
return json.load(f)
except:
return {}
def save_profiles(data):
with open(PROFILE_FILE, 'w') as f:
json.dump(data, f, indent=4)
def get_user_profile(user_id, guild_id):
profiles = load_profiles()
guild_profiles = profiles.get(str(guild_id), {})
return guild_profiles.get(str(user_id), {})
def update_user_profile(user_id, guild_id, data):
profiles = load_profiles()
if str(guild_id) not in profiles:
profiles[str(guild_id)] = {}
profiles[str(guild_id)][str(user_id)] = data
save_profiles(profiles)
def load_config():
try:
with open(CONFIG_FILE, 'r') as f:
return json.load(f)
except:
return {
"message_xp": 1,
"voice_xp": 2,
"voice_interval": 60,
"level_up_message": True,
"level_up_channel": None,
"roles": {}
}
def save_config(data):
with open(CONFIG_FILE, 'w') as f:
json.dump(data, f, indent=4)
def format_time(seconds):
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
secs = int(seconds % 60)
if hours > 0:
return f"{hours} дн {hours} ч {minutes} мин {secs} сек" if hours > 24 else f"{hours} ч {minutes} мин {secs} сек"
elif minutes > 0:
return f"{minutes} мин {secs} сек"
else:
return f"{secs} сек"
def create_progress_bar(progress, length=15):
filled = int(progress / 100 * length)
empty = length - filled
return f"`{'█' * filled}{'░' * empty}`"
# =====================================================
# 🎮 БОТ
# =====================================================
intents = discord.Intents.default()
intents.message_content = True
intents.guilds = True
intents.members = True
intents.voice_states = True
bot = commands.Bot(command_prefix='!', intents=intents)
voice_tracker = {}
message_tracker = {}
# =====================================================
# 📊 ФУНКЦИИ ДЛЯ XP И УРОВНЕЙ
# =====================================================
def get_xp_for_level(level):
return 5 * (level ** 2) + 50 * level + 100
def get_level_from_xp(xp):
level = 0
while True:
required = get_xp_for_level(level)
if xp < required:
break
xp -= required
level += 1
return level
def get_progress(user_data):
xp = user_data.get("xp", 0)
level = user_data.get("level", 0)
current_level_xp = get_xp_for_level(level - 1) if level > 0 else 0
next_level_xp = get_xp_for_level(level)
progress = (xp - current_level_xp) / (next_level_xp - current_level_xp) * 100
return min(progress, 100)
def get_rank(user_id, guild_id):
data = load_level_data()
guild_data = data.get(str(guild_id), {})
sorted_users = sorted(
guild_data.items(),
key=lambda x: (x[1].get("level", 0), x[1].get("xp", 0)),
reverse=True
)
for i, (uid, _) in enumerate(sorted_users, 1):
if uid == str(user_id):
return i
return None
def get_voice_time(user_id, guild_id):
data = load_voice_time()
guild_data = data.get(str(guild_id), {})
return guild_data.get(str(user_id), 0)
# =====================================================
# ✏️ РЕДАКТИРОВАНИЕ ПРОФИЛЯ
# =====================================================
class ProfileEditModal(ui.Modal, title="✏️ Изменение профиля"):
def __init__(self, user_id, guild_id):
super().__init__()
self.user_id = user_id
self.guild_id = guild_id
profile = get_user_profile(user_id, guild_id)
self.name = ui.TextInput(
label="Имя",
placeholder="Введите ваше имя...",
default=profile.get("name", ""),
required=False,
style=discord.TextStyle.short,
max_length=50
)
self.add_item(self.name)
self.age = ui.TextInput(
label="Возраст",
placeholder="Введите ваш возраст...",
default=profile.get("age", ""),
required=False,
style=discord.TextStyle.short,
max_length=3
)
self.add_item(self.age)
self.gender = ui.TextInput(
label="Пол",
placeholder="Муж / Жен / Другой...",
default=profile.get("gender", ""),
required=False,
style=discord.TextStyle.short,
max_length=20
)
self.add_item(self.gender)
self.bio = ui.TextInput(
label="Биография",
placeholder="Расскажите о себе...",
default=profile.get("bio", ""),
required=False,
style=discord.TextStyle.paragraph,
max_length=500
)
self.add_item(self.bio)
async def on_submit(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваш профиль!", ephemeral=True)
return
data = {
"name": self.name.value,
"age": self.age.value,
"gender": self.gender.value,
"bio": self.bio.value,
"updated_at": datetime.now().isoformat()
}
update_user_profile(self.user_id, self.guild_id, data)
embed = discord.Embed(
title="✅ Профиль обновлён!",
description="Ваши данные успешно сохранены.",
color=discord.Color.green()
)
await interaction.response.send_message(embed=embed, ephemeral=True)
class ProfileView(ui.View):
def __init__(self, user_id, guild_id):
super().__init__(timeout=180)
self.user_id = user_id
self.guild_id = guild_id
@ui.button(label="✏️ Редактировать профиль", style=ButtonStyle.primary, row=0)
async def edit_profile(self, interaction: discord.Interaction, button: ui.Button):
if interaction.user.id != self.user_id:
await interaction.response.send_message("❌ Это не ваш профиль!", ephemeral=True)
return
modal = ProfileEditModal(self.user_id, self.guild_id)
await interaction.response.send_modal(modal)
# =====================================================
# 🎯 КРАСИВЫЙ ПРОФИЛЬ
# =====================================================
@bot.tree.command(name="profile", description="📊 Показать красивый профиль")
@app_commands.describe(member="Участник (опционально)")
async def slash_profile(interaction: discord.Interaction, member: discord.Member = None):
if not member:
member = interaction.user
data = load_level_data()
voice_data = load_voice_time()
user_id = str(member.id)
guild_id = str(interaction.guild.id)
profile = get_user_profile(member.id, interaction.guild.id)
name = profile.get("name", member.display_name)
age = profile.get("age", "Не указан")
gender = profile.get("gender", "Не указан")
bio = profile.get("bio", "Не указана")
if guild_id not in data or user_id not in data[guild_id]:
data[guild_id] = data.get(guild_id, {})
data[guild_id][user_id] = {"xp": 0, "level": 0, "messages": 0}
save_level_data(data)
user_data = data[guild_id][user_id]
level = user_data.get("level", 0)
xp = user_data.get("xp", 0)
next_xp = get_xp_for_level(level)
progress = get_progress(user_data)
rank = get_rank(member.id, interaction.guild.id)
voice_time = get_voice_time(member.id, interaction.guild.id)
messages_count = user_data.get("messages", 0)
embed = discord.Embed(
title=f"📊 Профиль участника {member.display_name}",
color=discord.Color.blue()
)
embed.set_thumbnail(url=member.avatar.url if member.avatar else member.default_avatar.url)
embed.add_field(name="📇 Имя", value=f"**{name}**", inline=True)
embed.add_field(name="🎂 Возраст", value=f"**{age}**", inline=True)
embed.add_field(name="⚧ Пол", value=f"**{gender}**", inline=True)
embed.add_field(name="💬 Активность", value=f"за всё время было отправлено **{messages_count}** сообщений", inline=False)
embed.add_field(name="🎤 Голосовая активность", value=f"**{format_time(voice_time)}**", inline=False)
embed.add_field(name="📝 Биография", value=bio if bio and bio != "Не указана" else "Не указана", inline=False)
bar = create_progress_bar(progress)
embed.add_field(
name="📊 Прогресс до следующего уровня",
value=f"{bar} **{progress:.1f}%**",
inline=False
)
embed.add_field(name="🎯 Уровень", value=f"**{level}**", inline=True)
embed.add_field(name="⭐ XP", value=f"**{xp}** / {next_xp}", inline=True)
embed.add_field(name="🏆 Место", value=f"**#{rank if rank else '—'}**", inline=True)
embed.set_footer(
text=f"🆔 {member.id} • Запрошено: {interaction.user.display_name}",
icon_url=interaction.user.avatar.url if interaction.user.avatar else None
)
view = ProfileView(member.id, interaction.guild.id)
await interaction.response.send_message(embed=embed, view=view)
# =====================================================
# 🏆 ТОП И РЕЙТИНГ
# =====================================================
@bot.tree.command(name="level", description="🎯 Открыть меню")
async def slash_level(interaction: discord.Interaction):
embed = discord.Embed(
title="🎯 Система уровней",
description="💬 За сообщения + 🎤 За голосовой канал",
color=discord.Color.blue()
)
embed.add_field(
name="📋 Команды",
value="`/level` - Меню\n`/profile` - Красивый профиль\n`/top` - Топ\n`/rank` - Рейтинг\n`/settings` - Настройки (админ)",
inline=False
)
embed.set_footer(text="💡 Будьте активны!")
await interaction.response.send_message(embed=embed, view=LevelMainMenu())
class LevelMainMenu(ui.View):
def __init__(self):
super().__init__(timeout=180)
@ui.button(label="📊 Профиль", style=ButtonStyle.primary, row=0)
async def profile(self, interaction: discord.Interaction, button: ui.Button):
await slash_profile.callback(interaction)
@ui.button(label="🏆 Топ", style=ButtonStyle.primary, row=0)
async def top(self, interaction: discord.Interaction, button: ui.Button):
await slash_top(interaction)
@ui.button(label="⚙️ Настройки", style=ButtonStyle.secondary, row=1)
async def settings(self, interaction: discord.Interaction, button: ui.Button):
if not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("❌ Только администраторы!", ephemeral=True)
return
await slash_settings.callback(interaction)
@bot.tree.command(name="top", description="🏆 Топ пользователей")
async def slash_top(interaction: discord.Interaction):
data = load_level_data()
guild_id = str(interaction.guild.id)
if guild_id not in data:
await interaction.response.send_message("📭 Нет активных пользователей")
return
sorted_users = sorted(
data[guild_id].items(),
key=lambda x: (x[1].get("level", 0), x[1].get("xp", 0)),
reverse=True
)
embed = discord.Embed(
title="🏆 Топ пользователей",
color=discord.Color.gold()
)
for i, (uid, user_data) in enumerate(sorted_users[:10], 1):
member = interaction.guild.get_member(int(uid))
name = member.display_name if member else f"<@{uid}>"
level = user_data.get("level", 0)
xp = user_data.get("xp", 0)
voice_time = get_voice_time(int(uid), interaction.guild.id)
medal = ["🥇", "🥈", "🥉"][i-1] if i <= 3 else f"{i}."
embed.add_field(
name=f"{medal} {name}",
value=f"🎯 Уровень {level} | ⭐ {xp} XP\n🎤 {format_time(voice_time)}",
inline=False
)
if len(sorted_users) > 10:
embed.set_footer(text=f"И ещё {len(sorted_users)-10} участников...")
await interaction.response.send_message(embed=embed)
@bot.tree.command(name="rank", description="🏆 Ваше место в рейтинге")
async def slash_rank(interaction: discord.Interaction):
data = load_level_data()
user_id = str(interaction.user.id)
guild_id = str(interaction.guild.id)
if guild_id not in data or user_id not in data[guild_id]:
await interaction.response.send_message("📭 У вас пока нет XP!", ephemeral=True)
return
rank = get_rank(interaction.user.id, interaction.guild.id)
user_data = data[guild_id][user_id]
total = len(data[guild_id])
voice_time = get_voice_time(interaction.user.id, interaction.guild.id)
embed = discord.Embed(
title="🏆 Ваш рейтинг",
color=discord.Color.blue()
)
embed.add_field(name="📊 Место", value=f"**#{rank}** из {total}", inline=True)
embed.add_field(name="🎯 Уровень", value=f"**{user_data.get('level', 0)}**", inline=True)
embed.add_field(name="⭐ XP", value=f"**{user_data.get('xp', 0)}**", inline=True)
embed.add_field(name="🎤 Время в голосовых", value=format_time(voice_time), inline=True)
await interaction.response.send_message(embed=embed)
# =====================================================
# ⚙️ НАСТРОЙКИ
# =====================================================
@bot.tree.command(name="settings", description="⚙️ Настройки (админ)")
@app_commands.default_permissions(administrator=True)
async def slash_settings(interaction: discord.Interaction):
await interaction.response.defer()
config = load_config()
embed = discord.Embed(
title="⚙️ Настройки",
color=discord.Color.blue()
)
embed.add_field(
name="📊 Текущие настройки",
value=f"💬 XP за сообщение: **{config.get('message_xp', 1)}**\n"
f"🎵 XP за голос: **{config.get('voice_xp', 2)}**\n"
f"⏱️ Интервал: **{config.get('voice_interval', 60)}** сек",
inline=False
)
await interaction.followup.send(embed=embed, view=FullSettingsView())
class FullSettingsView(ui.View):
def __init__(self):
super().__init__(timeout=180)
self.update_buttons()
def update_buttons(self):
for item in self.children[:]:
self.remove_item(item)
config = load_config()
self.add_item(ui.Button(
label=f"💬 XP за сообщ: {config.get('message_xp', 1)}",
style=ButtonStyle.primary,
row=0,
custom_id="msg_xp"
))
self.add_item(ui.Button(
label=f"🎵 XP за голос: {config.get('voice_xp', 2)}",
style=ButtonStyle.primary,
row=0,
custom_id="voice_xp"
))
self.add_item(ui.Button(
label=f"⏱️ Интервал: {config.get('voice_interval', 60)}с",
style=ButtonStyle.primary,
row=1,
custom_id="interval_settings"
))
status = "✅ Вкл" if config.get('level_up_message', True) else "❌ Выкл"
self.add_item(ui.Button(
label=f"📢 Оповещения: {status}",
style=ButtonStyle.success if config.get('level_up_message', True) else ButtonStyle.danger,
row=1,
custom_id="notify_settings"
))
self.add_item(ui.Button(
label="📌 Канал оповещений",
style=ButtonStyle.secondary,
row=2,
custom_id="channel_settings"
))
self.add_item(ui.Button(
label="📊 Статистика",
style=ButtonStyle.secondary,
row=2,
custom_id="stats_settings"
))
self.add_item(ui.Button(
label="🗑️ Сбросить всё",
style=ButtonStyle.danger,
row=3,
custom_id="reset_settings"
))
self.add_item(ui.Button(
label="❌ Закрыть",
style=ButtonStyle.danger,
row=3,
custom_id="close_settings"
))
async def interaction_check(self, interaction: discord.Interaction):
if not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("❌ Только администраторы!", ephemeral=True)
return False
return True
async def callback(self, interaction: discord.Interaction):
custom_id = interaction.data.get('custom_id')
if custom_id == "msg_xp":
await self.msg_xp_menu(interaction)
elif custom_id == "voice_xp":
await self.voice_xp_menu(interaction)
elif custom_id == "interval_settings":
await self.interval_menu(interaction)
elif custom_id == "notify_settings":
await self.toggle_notify(interaction)
elif custom_id == "channel_settings":
await self.set_channel(interaction)
elif custom_id == "stats_settings":
await self.show_stats(interaction)
elif custom_id == "reset_settings":
await self.reset_data(interaction)
elif custom_id == "close_settings":
await interaction.message.delete()
await interaction.response.send_message("✅ Меню закрыто", ephemeral=True)
async def msg_xp_menu(self, interaction: discord.Interaction):
await interaction.response.defer()
view = MsgXPView()
embed = discord.Embed(
title="💬 XP за сообщения",
description="Выберите XP за одно сообщение",
color=discord.Color.blue()
)
await interaction.followup.send(embed=embed, view=view)
async def voice_xp_menu(self, interaction: discord.Interaction):
await interaction.response.defer()
view = VoiceXPView()
embed = discord.Embed(
title="🎵 XP за голосовой канал",
description="Выберите XP за минуту в голосовом",
color=discord.Color.blue()
)
await interaction.followup.send(embed=embed, view=view)
async def interval_menu(self, interaction: discord.Interaction):
await interaction.response.defer()
view = IntervalView()
embed = discord.Embed(
title="⏱️ Интервал проверки",
description="Выберите интервал в секундах",
color=discord.Color.blue()
)
await interaction.followup.send(embed=embed, view=view)
async def toggle_notify(self, interaction: discord.Interaction):
config = load_config()
config['level_up_message'] = not config.get('level_up_message', True)
save_config(config)
self.update_buttons()
status = "Включены" if config['level_up_message'] else "Выключены"
embed = discord.Embed(
title="📢 Оповещения",
description=f"Оповещения **{status}**",
color=discord.Color.green() if config['level_up_message'] else discord.Color.red()
)
await interaction.response.edit_message(embed=embed, view=self)
async def set_channel(self, interaction: discord.Interaction):
modal = ChannelModal()
await interaction.response.send_modal(modal)
async def show_stats(self, interaction: discord.Interaction):
await interaction.response.defer()
data = load_level_data()
guild_id = str(interaction.guild.id)
total_users = len(data.get(guild_id, {}))
total_xp = sum(u.get("xp", 0) for u in data.get(guild_id, {}).values())
max_level = max((u.get("level", 0) for u in data.get(guild_id, {}).values()), default=0)
embed = discord.Embed(
title="📊 Статистика сервера",
color=discord.Color.blue()
)
embed.add_field(name="👥 Участников", value=str(total_users), inline=True)
embed.add_field(name="⭐ Всего XP", value=str(total_xp), inline=True)
embed.add_field(name="🎯 Макс. уровень", value=str(max_level), inline=True)
await interaction.followup.send(embed=embed, ephemeral=True)
async def reset_data(self, interaction: discord.Interaction):
embed = discord.Embed(
title="⚠️ Подтверждение",
description="Вы уверены, что хотите сбросить ВСЕ данные?",
color=discord.Color.red()
)
view = ConfirmResetView()
await interaction.response.edit_message(embed=embed, view=view)
class MsgXPView(ui.View):
def __init__(self):
super().__init__(timeout=60)
for val in [1, 2, 3, 4, 5, 8, 10]:
self.add_item(ui.Button(
label=f"{val} XP",
style=ButtonStyle.primary if val == load_config().get('message_xp', 1) else ButtonStyle.secondary,
row=0 if val <= 5 else 1,
custom_id=f"msgxp_{val}"
))
self.add_item(ui.Button(label="🔙 Назад", style=ButtonStyle.danger, row=2, custom_id="back"))
async def interaction_check(self, interaction: discord.Interaction):
if not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("❌ Только администраторы!", ephemeral=True)
return False
return True
async def callback(self, interaction: discord.Interaction):
custom_id = interaction.data.get('custom_id')
if custom_id == "back":
await interaction.response.defer()
await interaction.followup.send(embed=discord.Embed(title="⚙️ Настройки", color=discord.Color.blue()), view=FullSettingsView())
return
val = int(custom_id.split("_")[1])
config = load_config()
config['message_xp'] = val
save_config(config)
embed = discord.Embed(
title="✅ Обновлено!",
description=f"Теперь за сообщение даётся **{val}** XP",
color=discord.Color.green()
)
await interaction.response.edit_message(embed=embed, view=MsgXPView())
class VoiceXPView(ui.View):
def __init__(self):
super().__init__(timeout=60)
for val in [1, 2, 3, 4, 5, 8, 10, 15, 20]:
self.add_item(ui.Button(
label=f"{val} XP",
style=ButtonStyle.primary if val == load_config().get('voice_xp', 2) else ButtonStyle.secondary,
row=0 if val <= 5 else 1,
custom_id=f"voicexp_{val}"
))
self.add_item(ui.Button(label="🔙 Назад", style=ButtonStyle.danger, row=2, custom_id="back"))
async def interaction_check(self, interaction: discord.Interaction):
if not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("❌ Только администраторы!", ephemeral=True)
return False
return True
async def callback(self, interaction: discord.Interaction):
custom_id = interaction.data.get('custom_id')
if custom_id == "back":
await interaction.response.defer()
await interaction.followup.send(embed=discord.Embed(title="⚙️ Настройки", color=discord.Color.blue()), view=FullSettingsView())
return
val = int(custom_id.split("_")[1])
config = load_config()
config['voice_xp'] = val
save_config(config)
embed = discord.Embed(
title="✅ Обновлено!",
description=f"Теперь за минуту в голосовом даётся **{val}** XP",
color=discord.Color.green()
)
await interaction.response.edit_message(embed=embed, view=VoiceXPView())
class IntervalView(ui.View):
def __init__(self):
super().__init__(timeout=60)
for val in [15, 30, 45, 60, 90, 120, 180, 300]:
self.add_item(ui.Button(
label=f"{val}с",
style=ButtonStyle.primary if val == load_config().get('voice_interval', 60) else ButtonStyle.secondary,
row=0 if val <= 60 else 1,
custom_id=f"int_{val}"
))
self.add_item(ui.Button(label="🔙 Назад", style=ButtonStyle.danger, row=2, custom_id="back"))
async def interaction_check(self, interaction: discord.Interaction):
if not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("❌ Только администраторы!", ephemeral=True)
return False
return True
async def callback(self, interaction: discord.Interaction):
custom_id = interaction.data.get('custom_id')
if custom_id == "back":
await interaction.response.defer()
await interaction.followup.send(embed=discord.Embed(title="⚙️ Настройки", color=discord.Color.blue()), view=FullSettingsView())
return
val = int(custom_id.split("_")[1])
config = load_config()
config['voice_interval'] = val
save_config(config)
embed = discord.Embed(
title="✅ Обновлено!",
description=f"Теперь проверка каждые **{val}** секунд",
color=discord.Color.green()
)
await interaction.response.edit_message(embed=embed, view=IntervalView())
class ChannelModal(ui.Modal, title="📌 Канал оповещений"):
channel_id = ui.TextInput(
label="ID канала",
placeholder="Введите ID...",
required=False,
style=discord.TextStyle.short
)
async def on_submit(self, interaction: discord.Interaction):
config = load_config()
if self.channel_id.value:
try:
channel_id = int(self.channel_id.value)
channel = interaction.guild.get_channel(channel_id)
if not channel:
await interaction.response.send_message("❌ Канал не найден!", ephemeral=True)
return
config['level_up_channel'] = channel_id
save_config(config)
await interaction.response.send_message(f"✅ Канал установлен: {channel.mention}", ephemeral=True)
except:
await interaction.response.send_message("❌ Неверный ID!", ephemeral=True)
else:
config['level_up_channel'] = None
save_config(config)
await interaction.response.send_message("✅ Канал сброшен", ephemeral=True)
class ConfirmResetView(ui.View):
def __init__(self):
super().__init__(timeout=60)
@ui.button(label="✅ Да, сбросить", style=ButtonStyle.danger, row=0)
async def confirm(self, interaction: discord.Interaction, button: ui.Button):
if not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("❌ Только администраторы!", ephemeral=True)
return
data = load_level_data()
voice_data = load_voice_time()
guild_id = str(interaction.guild.id)
if guild_id in data:
del data[guild_id]
save_level_data(data)
if guild_id in voice_data:
del voice_data[guild_id]
save_voice_time(voice_data)
embed = discord.Embed(
title="🗑️ Данные сброшены!",
color=discord.Color.red()
)
await interaction.response.edit_message(embed=embed, view=None)
@ui.button(label="❌ Отмена", style=ButtonStyle.secondary, row=0)
async def cancel(self, interaction: discord.Interaction, button: ui.Button):
embed = discord.Embed(
title="✅ Отменено",
color=discord.Color.green()
)
await interaction.response.edit_message(embed=embed, view=FullSettingsView())
# =====================================================
# 💬 XP ЗА СООБЩЕНИЯ
# =====================================================
@bot.event
async def on_message(message):
if message.author.bot or not message.guild:
await bot.process_commands(message)
return
config = load_config()
message_xp = config.get('message_xp', 1)
cooldown = config.get('message_cooldown', 60)
user_id = str(message.author.id)
guild_id = str(message.guild.id)
if user_id not in message_tracker:
message_tracker[user_id] = {}
last_time = message_tracker[user_id].get(guild_id)
if last_time and (datetime.now() - last_time).seconds < cooldown:
await bot.process_commands(message)
return
message_tracker[user_id][guild_id] = datetime.now()
data = load_level_data()
if guild_id not in data:
data[guild_id] = {}
if user_id not in data[guild_id]:
data[guild_id][user_id] = {"xp": 0, "level": 0, "messages": 0}
data[guild_id][user_id]["xp"] += message_xp
data[guild_id][user_id]["messages"] = data[guild_id][user_id].get("messages", 0) + 1
current_level = data[guild_id][user_id]["level"]
new_level = get_level_from_xp(data[guild_id][user_id]["xp"])
if new_level > current_level:
data[guild_id][user_id]["level"] = new_level
if config.get('level_up_message', True):
try:
channel_id = config.get('level_up_channel')
channel = message.guild.get_channel(channel_id) if channel_id else message.guild.system_channel
if channel:
embed = discord.Embed(
title="🎉 Повышение уровня!",
description=f"{message.author.mention} достиг **{new_level}** уровня!",
color=discord.Color.gold()
)
await channel.send(embed=embed)
except:
pass
save_level_data(data)
await bot.process_commands(message)
# =====================================================
# 🎵 XP ЗА ГОЛОСОВОЙ КАНАЛ
# =====================================================
async def give_voice_xp():
await bot.wait_until_ready()
while not bot.is_closed():
try:
config = load_config()
voice_xp = config.get('voice_xp', 2)
interval = config.get('voice_interval', 60)
data = load_level_data()
voice_data = load_voice_time()
for guild in bot.guilds:
guild_id = str(guild.id)
if guild_id not in data:
data[guild_id] = {}
if guild_id not in voice_data:
voice_data[guild_id] = {}
for member in guild.members:
if member.voice and member.voice.channel:
user_id = str(member.id)
if user_id not in voice_tracker:
voice_tracker[user_id] = {}
if guild_id not in voice_tracker[user_id]:
voice_tracker[user_id][guild_id] = datetime.now()
continue
last_time = voice_tracker[user_id][guild_id]
seconds_passed = (datetime.now() - last_time).seconds
if seconds_passed < interval:
continue
voice_tracker[user_id][guild_id] = datetime.now()
if user_id not in voice_data[guild_id]:
voice_data[guild_id][user_id] = 0
voice_data[guild_id][user_id] += interval
save_voice_time(voice_data)
if user_id not in data[guild_id]:
data[guild_id][user_id] = {"xp": 0, "level": 0}
data[guild_id][user_id]["xp"] += voice_xp
current_level = data[guild_id][user_id]["level"]
new_level = get_level_from_xp(data[guild_id][user_id]["xp"])
if new_level > current_level:
data[guild_id][user_id]["level"] = new_level
if config.get('level_up_message', True):
try:
channel_id = config.get('level_up_channel')
channel = guild.get_channel(channel_id) if channel_id else guild.system_channel
if channel:
embed = discord.Embed(
title="🎉 Повышение уровня!",
description=f"{member.mention} достиг **{new_level}** уровня в голосовом канале!",
color=discord.Color.gold()
)
await channel.send(embed=embed)
except:
pass
save_level_data(data)
except Exception as e:
print(f"❌ Ошибка: {e}")
await asyncio.sleep(interval)
@bot.event
async def on_voice_state_update(member, before, after):
if before.channel and not after.channel:
user_id = str(member.id)
guild_id = str(member.guild.id)
if user_id in voice_tracker and guild_id in voice_tracker[user_id]:
del voice_tracker[user_id][guild_id]
if not voice_tracker[user_id]:
del voice_tracker[user_id]
@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="🎯 Уровни | /profile"
))
try:
await bot.tree.sync()
print("✅ Команды синхронизированы!")
except Exception as e:
print(f"❌ Ошибка: {e}")
# =====================================================
# 🚀 ЗАПУСК
# =====================================================
async def main():
async with bot:
print("🔄 Запуск Mohist_Level...")
bot.loop.create_task(give_voice_xp())
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}")