feat: Refactor leveling commands to use UI components for enhanced user experience
This commit is contained in:
parent
8fbfb8eb65
commit
859b20dfe1
@ -1,5 +1,6 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord import ui # Add ui for LayoutView
|
||||
import json
|
||||
import os
|
||||
import asyncio
|
||||
@ -373,14 +374,30 @@ class LevelingCog(commands.Cog):
|
||||
filled_length = int(progress_bar_length * progress)
|
||||
bar = '█' * filled_length + '░' * (progress_bar_length - filled_length)
|
||||
|
||||
embed = discord.Embed(
|
||||
title=f"{target.display_name}'s Level",
|
||||
description=f"**Level:** {level}\n**XP:** {xp} / {xp_needed}\n\n**Progress to Level {next_level}:**\n[{bar}] {int(progress * 100)}%",
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
embed.set_thumbnail(url=target.display_avatar.url)
|
||||
class LevelCheckView(ui.LayoutView):
|
||||
def __init__(self, target_member: discord.Member, level: int, xp: int, xp_needed: int, next_level: int, bar: str, progress_percent: int):
|
||||
super().__init__()
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
# Container for overall layout, possibly with accent color
|
||||
container = ui.Container(accent_colour=discord.Color.blue())
|
||||
self.add_item(container)
|
||||
|
||||
# Title
|
||||
container.add_item(ui.TextDisplay(f"**{target_member.display_name}'s Level**")) # Using TextDisplay for title
|
||||
|
||||
# Thumbnail
|
||||
if target_member.display_avatar:
|
||||
container.add_item(ui.Thumbnail(media=target_member.display_avatar.url, description="User Avatar"))
|
||||
|
||||
# Main content
|
||||
container.add_item(ui.TextDisplay(f"**Level:** {level}"))
|
||||
container.add_item(ui.TextDisplay(f"**XP:** {xp} / {xp_needed}"))
|
||||
container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small)) # Adding a separator
|
||||
container.add_item(ui.TextDisplay(f"**Progress to Level {next_level}:**"))
|
||||
container.add_item(ui.TextDisplay(f"[{bar}] {progress_percent}%"))
|
||||
|
||||
view = LevelCheckView(target, level, xp, xp_needed, next_level, bar, int(progress * 100))
|
||||
await ctx.send(view=view)
|
||||
|
||||
@level.command(name="leaderboard", description="Show the server's level leaderboard")
|
||||
async def leaderboard_command(self, ctx: commands.Context):
|
||||
@ -401,22 +418,34 @@ class LevelingCog(commands.Cog):
|
||||
# Sort by XP (descending)
|
||||
sorted_data = sorted(guild_data.items(), key=lambda x: x[1]["xp"], reverse=True)
|
||||
|
||||
# Create embed
|
||||
embed = discord.Embed(
|
||||
title=f"{ctx.guild.name} Level Leaderboard",
|
||||
color=discord.Color.gold()
|
||||
)
|
||||
class LeaderboardView(ui.LayoutView):
|
||||
def __init__(self, guild_name: str, sorted_leaderboard_data: list, guild_members_dict: dict):
|
||||
super().__init__()
|
||||
|
||||
# Add top 10 users to embed
|
||||
for i, (user_id, data) in enumerate(sorted_data[:10], 1):
|
||||
member = guild_members[user_id]
|
||||
embed.add_field(
|
||||
name=f"{i}. {member.display_name}",
|
||||
value=f"Level: {data['level']} | XP: {data['xp']}",
|
||||
inline=False
|
||||
)
|
||||
main_container = ui.Container(accent_colour=discord.Color.gold())
|
||||
self.add_item(main_container)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
main_container.add_item(ui.TextDisplay(f"**{guild_name} Level Leaderboard**"))
|
||||
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small))
|
||||
|
||||
if not sorted_leaderboard_data:
|
||||
main_container.add_item(ui.TextDisplay("The leaderboard is empty!"))
|
||||
return
|
||||
|
||||
for i, (user_id, data) in enumerate(sorted_leaderboard_data[:10], 1):
|
||||
member = guild_members_dict.get(user_id)
|
||||
if not member:
|
||||
continue
|
||||
|
||||
user_section = ui.Section()
|
||||
user_section.add_item(ui.TextDisplay(f"**{i}. {member.display_name}**"))
|
||||
user_section.add_item(ui.TextDisplay(f"Level: {data['level']} | XP: {data['xp']}"))
|
||||
main_container.add_item(user_section)
|
||||
if i < len(sorted_leaderboard_data[:10]): # Add separator between users, but not after the last one
|
||||
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small, visible=False)) # Invisible separator for spacing
|
||||
|
||||
view = LeaderboardView(ctx.guild.name, sorted_data, guild_members)
|
||||
await ctx.send(view=view)
|
||||
|
||||
@level.command(name="register_role", description="Register a role for a specific level")
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
@ -469,24 +498,43 @@ class LevelingCog(commands.Cog):
|
||||
await ctx.send("No level roles are registered for this server.")
|
||||
return
|
||||
|
||||
embed = discord.Embed(
|
||||
title=f"Level Roles for {ctx.guild.name}",
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
class ListLevelRolesView(ui.LayoutView):
|
||||
def __init__(self, guild: discord.Guild, level_roles_data: dict):
|
||||
super().__init__()
|
||||
|
||||
# Sort by level
|
||||
sorted_roles = sorted(self.level_roles[ctx.guild.id].items())
|
||||
main_container = ui.Container(accent_colour=discord.Color.blue())
|
||||
self.add_item(main_container)
|
||||
|
||||
for level, role_id in sorted_roles:
|
||||
role = ctx.guild.get_role(role_id)
|
||||
role_name = role.mention if role else f"Unknown Role (ID: {role_id})"
|
||||
embed.add_field(
|
||||
name=f"Level {level}",
|
||||
value=role_name,
|
||||
inline=False
|
||||
)
|
||||
main_container.add_item(ui.TextDisplay(f"**Level Roles for {guild.name}**"))
|
||||
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small))
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
if not level_roles_data: # Should be caught by the check above, but good practice
|
||||
main_container.add_item(ui.TextDisplay("No level roles are registered for this server."))
|
||||
return
|
||||
|
||||
sorted_roles_items = sorted(level_roles_data.items())
|
||||
|
||||
for level, role_data_or_id in sorted_roles_items:
|
||||
role_section = ui.Section()
|
||||
role_section.add_item(ui.TextDisplay(f"**Level {level}:**"))
|
||||
|
||||
if isinstance(role_data_or_id, dict): # Gendered roles
|
||||
for gender, role_id in role_data_or_id.items():
|
||||
role = guild.get_role(role_id)
|
||||
role_name = role.mention if role else f"Unknown Role (ID: {role_id})"
|
||||
role_section.add_item(ui.TextDisplay(f" - {gender.capitalize()}: {role_name}"))
|
||||
else: # Regular role
|
||||
role = guild.get_role(role_data_or_id)
|
||||
role_name = role.mention if role else f"Unknown Role (ID: {role_data_or_id})"
|
||||
role_section.add_item(ui.TextDisplay(f" {role_name}"))
|
||||
|
||||
main_container.add_item(role_section)
|
||||
if level != sorted_roles_items[-1][0]: # Add separator if not the last item
|
||||
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small, visible=False))
|
||||
|
||||
|
||||
view = ListLevelRolesView(ctx.guild, self.level_roles[ctx.guild.id])
|
||||
await ctx.send(view=view)
|
||||
|
||||
@level.command(name="restrict_channel", description="Restrict a channel from giving XP")
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
@ -637,21 +685,49 @@ class LevelingCog(commands.Cog):
|
||||
async def xp_config(self, ctx: commands.Context, setting: str = None, value: str = None):
|
||||
"""Configure XP settings for the leveling system"""
|
||||
if not setting:
|
||||
# Display current settings
|
||||
embed = discord.Embed(
|
||||
title="XP Configuration Settings",
|
||||
description="Current XP settings for the leveling system:",
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
class XPConfigView(ui.LayoutView):
|
||||
def __init__(self, config_data: dict, prefix: str):
|
||||
super().__init__()
|
||||
|
||||
embed.add_field(name="XP Per Message", value=str(self.config["xp_per_message"]), inline=True)
|
||||
embed.add_field(name="XP Per Reaction", value=str(self.config["xp_per_reaction"]), inline=True)
|
||||
embed.add_field(name="Message Cooldown", value=f"{self.config['message_cooldown']} seconds", inline=True)
|
||||
embed.add_field(name="Reaction Cooldown", value=f"{self.config['reaction_cooldown']} seconds", inline=True)
|
||||
embed.add_field(name="Reaction XP Enabled", value="Yes" if self.config["reaction_xp_enabled"] else "No", inline=True)
|
||||
main_container = ui.Container(accent_colour=discord.Color.blue())
|
||||
self.add_item(main_container)
|
||||
|
||||
embed.set_footer(text="Use !xp_config <setting> <value> to change a setting")
|
||||
await ctx.send(embed=embed)
|
||||
main_container.add_item(ui.TextDisplay("**XP Configuration Settings**"))
|
||||
main_container.add_item(ui.TextDisplay("Current XP settings for the leveling system:"))
|
||||
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small))
|
||||
|
||||
settings_to_display = [
|
||||
("XP Per Message", str(config_data["xp_per_message"])),
|
||||
("XP Per Reaction", str(config_data["xp_per_reaction"])),
|
||||
("Message Cooldown", f"{config_data['message_cooldown']} seconds"),
|
||||
("Reaction Cooldown", f"{config_data['reaction_cooldown']} seconds"),
|
||||
("Reaction XP Enabled", "Yes" if config_data["reaction_xp_enabled"] else "No")
|
||||
]
|
||||
|
||||
for name, value_str in settings_to_display:
|
||||
setting_section = ui.Section()
|
||||
setting_section.add_item(ui.TextDisplay(f"**{name}:** {value_str}"))
|
||||
main_container.add_item(setting_section)
|
||||
|
||||
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small))
|
||||
main_container.add_item(ui.TextDisplay(f"Use {prefix}level config <setting> <value> to change a setting")) # Updated help text
|
||||
|
||||
# Attempt to get the prefix
|
||||
try:
|
||||
# This is a common way to get the prefix, but might need adjustment
|
||||
# depending on how the bot is structured (e.g., if get_prefix is an async method or part of bot instance)
|
||||
# For simplicity, assuming ctx.prefix exists or can be hardcoded if necessary.
|
||||
# If ctx.prefix is not available, a default like "!" or the bot's mention could be used.
|
||||
# Let's assume ctx.prefix is available for now.
|
||||
# If not, we might need to ask the user or make an assumption.
|
||||
# For now, let's try with ctx.prefix. If it causes an error, we can adjust.
|
||||
# A safer bet might be to use the command's qualified name.
|
||||
command_prefix = ctx.prefix if ctx.prefix else "!" # Fallback to "!"
|
||||
except AttributeError:
|
||||
command_prefix = "!" # Fallback if ctx.prefix doesn't exist
|
||||
|
||||
view = XPConfigView(self.config, command_prefix)
|
||||
await ctx.send(view=view)
|
||||
return
|
||||
|
||||
if not value:
|
||||
@ -889,28 +965,41 @@ class LevelingCog(commands.Cog):
|
||||
# Save the updated level roles
|
||||
self.save_level_roles()
|
||||
|
||||
# Update status message
|
||||
created_str = "\n".join(created_roles) if created_roles else "None"
|
||||
updated_str = "\n".join(updated_roles) if updated_roles else "None"
|
||||
class MedievalRolesSetupView(ui.LayoutView):
|
||||
def __init__(self, created_roles_list: list, updated_roles_list: list, has_pronoun_roles_flag: bool):
|
||||
super().__init__()
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Medieval Level Roles Setup",
|
||||
description="The following roles have been set up for the medieval leveling system:",
|
||||
color=discord.Color.gold()
|
||||
)
|
||||
main_container = ui.Container(accent_colour=discord.Color.gold())
|
||||
self.add_item(main_container)
|
||||
|
||||
if created_roles:
|
||||
embed.add_field(name="Created Roles", value=created_str, inline=False)
|
||||
if updated_roles:
|
||||
embed.add_field(name="Updated Roles", value=updated_str, inline=False)
|
||||
main_container.add_item(ui.TextDisplay("**Medieval Level Roles Setup**"))
|
||||
main_container.add_item(ui.TextDisplay("The following roles have been set up for the medieval leveling system:"))
|
||||
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small))
|
||||
|
||||
embed.add_field(
|
||||
name="Gender Detection",
|
||||
value="Gender-specific roles will be assigned based on pronoun roles." if has_pronoun_roles else "No pronoun roles detected. Using default titles.",
|
||||
inline=False
|
||||
)
|
||||
if created_roles_list:
|
||||
created_section = ui.Section()
|
||||
created_section.add_item(ui.TextDisplay("**Created Roles:**"))
|
||||
# For potentially long lists, join with newline. TextDisplay handles multiline.
|
||||
created_section.add_item(ui.TextDisplay("\n".join(created_roles_list) if created_roles_list else "None"))
|
||||
main_container.add_item(created_section)
|
||||
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small, visible=False))
|
||||
|
||||
await status_message.edit(content=None, embed=embed)
|
||||
|
||||
if updated_roles_list:
|
||||
updated_section = ui.Section()
|
||||
updated_section.add_item(ui.TextDisplay("**Updated Roles:**"))
|
||||
updated_section.add_item(ui.TextDisplay("\n".join(updated_roles_list) if updated_roles_list else "None"))
|
||||
main_container.add_item(updated_section)
|
||||
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small, visible=False))
|
||||
|
||||
gender_detection_section = ui.Section()
|
||||
gender_detection_section.add_item(ui.TextDisplay("**Gender Detection:**"))
|
||||
gender_text = "Gender-specific roles will be assigned based on pronoun roles." if has_pronoun_roles_flag else "No pronoun roles detected. Using default titles."
|
||||
gender_detection_section.add_item(ui.TextDisplay(gender_text))
|
||||
main_container.add_item(gender_detection_section)
|
||||
|
||||
view = MedievalRolesSetupView(created_roles, updated_roles, has_pronoun_roles)
|
||||
await status_message.edit(content=None, view=view)
|
||||
|
||||
async def setup(bot: commands.Bot):
|
||||
await bot.add_cog(LevelingCog(bot))
|
||||
|
Loading…
x
Reference in New Issue
Block a user