import discord from discord.ext import commands from discord import ui from datetime import datetime class UserInfoCog(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot @commands.hybrid_command(name="userinfo", description="Displays detailed information about a user.") async def userinfo(self, ctx: commands.Context, member: discord.Member = None): """Displays detailed information about a user.""" if member is None: member = ctx.author # --- Information Gathering --- username_discriminator = f"{member.name}#{member.discriminator}" if member.discriminator != "0" else member.name created_at_str = member.created_at.strftime("%Y-%m-%d %H:%M:%S UTC") joined_at_str = member.joined_at.strftime("%Y-%m-%d %H:%M:%S UTC") if member.joined_at else "N/A" roles = [role.mention for role in reversed(member.roles) if role.name != "@everyone"] roles_str = ", ".join(roles) if roles else "None" if len(roles_str) > 1000: # Discord limits field values roles_str = roles_str[:997] + "..." status_str = str(member.status).title() if member.activity: if member.activity.type == discord.ActivityType.playing: activity_str = f"Playing {member.activity.name}" elif member.activity.type == discord.ActivityType.streaming: activity_str = f"Streaming {member.activity.name} on {member.activity.platform}" elif member.activity.type == discord.ActivityType.listening: activity_str = f"Listening to {member.activity.title} by {member.activity.artist}" elif member.activity.type == discord.ActivityType.watching: activity_str = f"Watching {member.activity.name}" elif member.activity.type == discord.ActivityType.custom: activity_str = f"{member.activity.emoji if member.activity.emoji else ''} {member.activity.name if member.activity.name else ''}".strip() else: activity_str = "None" else: activity_str = "None" # --- UI Components v2 View --- class UserInfoView(ui.LayoutView): def __init__(self, target_member: discord.Member): super().__init__(timeout=180) # 3 minutes timeout main_container = ui.Container(accent_colour=target_member.accent_color or target_member.color or discord.Color.blue()) self.add_item(main_container) # Header Section with Avatar header_section = ui.Section(accessory=ui.Thumbnail(media=target_member.display_avatar.url, description="User Avatar")) main_container.add_item(header_section) header_section.add_item(ui.TextDisplay(f"**{target_member.display_name}**")) header_section.add_item(ui.TextDisplay(f"({username_discriminator}) - ID: {target_member.id}")) main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small)) # Dates Section dates_section = ui.Section(accessory=None) main_container.add_item(dates_section) dates_section.add_item(ui.TextDisplay(f"**Joined Server:** {joined_at_str}")) dates_section.add_item(ui.TextDisplay(f"**Account Created:** {created_at_str}")) main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small)) # Status & Activity Section status_activity_section = ui.Section(accessory=None) main_container.add_item(status_activity_section) status_activity_section.add_item(ui.TextDisplay(f"**Status:** {status_str}")) status_activity_section.add_item(ui.TextDisplay(f"**Activity:** {activity_str}")) if target_member.nick: status_activity_section.add_item(ui.TextDisplay(f"**Nickname:** {target_member.nick}")) main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small)) # Roles Section roles_section = ui.Section(accessory=None) main_container.add_item(roles_section) roles_section.add_item(ui.TextDisplay(f"**Roles ({len(roles)}):**")) if roles: # For a long list of roles, it's better to display them as a single block # or handle pagination if it's extremely long. # For now, a single TextDisplay item. roles_section.add_item(ui.TextDisplay(roles_str)) else: roles_section.add_item(ui.TextDisplay("None")) # Voice State if target_member.voice: main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small)) voice_section = ui.Section(accessory=None) main_container.add_item(voice_section) voice_section.add_item(ui.TextDisplay(f"**Voice Channel:** {target_member.voice.channel.mention if target_member.voice.channel else 'Not in a channel'}")) voice_state_details = [] if target_member.voice.self_mute: voice_state_details.append("Muted (Self)") if target_member.voice.self_deaf: voice_state_details.append("Deafened (Self)") if target_member.voice.mute: voice_state_details.append("Muted (Server)") if target_member.voice.deaf: voice_state_details.append("Deafened (Server)") if target_member.voice.self_stream: voice_state_details.append("Streaming") if target_member.voice.self_video: voice_state_details.append("Video On") if voice_state_details: voice_section.add_item(ui.TextDisplay(f"**Voice State:** {', '.join(voice_state_details)}")) # Add more sections as needed (e.g., permissions) view = UserInfoView(member) await ctx.send(view=view, ephemeral=False) # Send publicly by default @commands.Cog.listener() async def on_ready(self): print(f'{self.__class__.__name__} cog has been loaded.') async def setup(bot: commands.Bot): await bot.add_cog(UserInfoCog(bot))