Добавить WL level bots/bot_level.py
This commit is contained in:
parent
fd3cbaeb8a
commit
06fad40ad0
951
WL level bots/bot_level.py
Normal file
951
WL level bots/bot_level.py
Normal file
@ -0,0 +1,951 @@
|
||||
#!/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}")
|
||||
Loading…
Reference in New Issue
Block a user