Add `allowed_mentions` to the `ctx.send` call for the user info command. This prevents the bot from accidentally pinging users, roles, or everyone when displaying user information, avoiding unwanted notifications.
112 lines
5.8 KiB
Python
112 lines
5.8 KiB
Python
import discord
|
|
from discord.ext import commands
|
|
from discord import AllowedMentions, 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=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
|
|
main_container.add_item(ui.TextDisplay(f"**Joined Server:** {joined_at_str}"))
|
|
main_container.add_item(ui.TextDisplay(f"**Account Created:** {created_at_str}"))
|
|
|
|
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small))
|
|
|
|
# Status & Activity
|
|
main_container.add_item(ui.TextDisplay(f"**Status:** {status_str}"))
|
|
main_container.add_item(ui.TextDisplay(f"**Activity:** {activity_str}"))
|
|
|
|
if target_member.nick:
|
|
main_container.add_item(ui.TextDisplay(f"**Nickname:** {target_member.nick}"))
|
|
|
|
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small))
|
|
|
|
# Roles
|
|
main_container.add_item(ui.TextDisplay(f"**Roles ({len(roles)}):**"))
|
|
if roles:
|
|
main_container.add_item(ui.TextDisplay(roles_str))
|
|
else:
|
|
main_container.add_item(ui.TextDisplay("None"))
|
|
|
|
# Voice State
|
|
if target_member.voice:
|
|
main_container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small))
|
|
main_container.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:
|
|
main_container.add_item(ui.TextDisplay(f"**Voice State:** {', '.join(voice_state_details)}"))
|
|
|
|
|
|
# Add more sections as needed (e.g., permissions)
|
|
|
|
try:
|
|
view = UserInfoView(member)
|
|
await ctx.send(view=view, ephemeral=False, allowed_mentions=AllowedMentions(roles=False, users=False, everyone=False)) # Send publicly by default
|
|
except Exception as e:
|
|
import traceback
|
|
traceback.print_exc() # Print full traceback to console
|
|
await ctx.send(f"An error occurred while creating the user info display: `{e}`", ephemeral=True)
|
|
|
|
@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))
|