import discord import time from discord.ext import commands import logging from datetime import datetime from utils.sql_commands import DatabaseManager # --- Localization dictionary (expand as needed) --- LOCALE = { "en": { "afk_set": "{user} has gone afk.{reason}", "afk_reason": " Reason: {reason}", "afk_notify": "{user} is currently unavailable.{reason}", "feedback_received": "Feedback received. Thank you!", "missing_perms": "⚠️ The bot is missing the following permissions in **{guild}**: {perms}", } } LANG = "en" # Set your language code here def _(key, **kwargs): return LOCALE[LANG][key].format(**kwargs) class Informational(commands.Cog): def __init__(self, client): self.client = client self.db = DatabaseManager() async def cog_load(self): # Bot Permissions Check at startup for guild in self.client.guilds: me = guild.me missing = [] if not me.guild_permissions.manage_roles: missing.append("Manage Roles") if not me.guild_permissions.manage_nicknames: missing.append("Manage Nicknames") if missing: owner = guild.owner try: await owner.send( _("missing_perms", guild=guild.name, perms=", ".join(missing)) ) except Exception: logging.warning( f"Could not DM owner of {guild.name} about missing permissions." ) @commands.command(name="whois") async def _userinfo(self, ctx, member: discord.Member = None): # type: ignore if member is None: member = ctx.message.author roles = [role for role in member.roles][1:] embed = discord.Embed( colour=discord.Colour.purple(), timestamp=ctx.message.created_at, title=f"User Info - {member}", ) embed.set_thumbnail(url=member.display_avatar) embed.set_footer(text=f"Requested by {ctx.author}") embed.add_field(name="ID:", value=member.id) embed.add_field(name="Display Name:", value=member.display_name) embed.add_field( name="Created Account On:", value=member.created_at.strftime("%a, %#d %B %Y, %I:%M %p UTC"), ) embed.add_field( name="Joined Server On:", value=member.joined_at.strftime("%a, %#d %B %Y, %I:%M %p UTC"), # type: ignore ) embed.add_field(name="Roles:", value="".join([role.mention for role in roles])) embed.add_field(name="Highest Role:", value=member.top_role.mention) await ctx.send(embed=embed) @commands.command(name="serverInfo") @commands.has_permissions(view_audit_log=True) # Example permission async def _server(self, ctx): embed = discord.Embed( title=f"{ctx.guild.name} Info", description="Information of this Server", color=discord.Colour.blue(), ) embed.add_field(name="🆔Server ID", value=f"{ctx.guild.id}", inline=True) embed.add_field( name="📆Created On", value=ctx.guild.created_at.strftime("%b %d %Y"), inline=True, ) embed.add_field(name="👑Owner", value=f"{ctx.guild.owner.mention}", inline=True) embed.add_field( name="👥Members", value=f"{ctx.guild.member_count} Members", inline=True ) embed.add_field( name="💬Channels", value=f"{len(ctx.guild.text_channels)} Text | {len(ctx.guild.voice_channels)} Voice", inline=True, ) embed.set_thumbnail(url=ctx.guild.icon) embed.set_footer(text="⭐ • Duo") embed.set_author(name=f"{ctx.author.name}", icon_url=ctx.message.author.avatar) await ctx.send(embed=embed) @commands.command(name="afk") @commands.cooldown(1, 10, commands.BucketType.user) async def _afk(self, ctx, *reason): afk_reason = " ".join(reason) timestamp = datetime.utcnow().isoformat() # Store AFK reason and timestamp in DB self.db.execute_query( "REPLACE INTO afk_status (USERID, GUILDID, REASON, TIMESTAMP) VALUES (%s, %s, %s, %s)", (ctx.author.id, ctx.guild.id, afk_reason, timestamp), ) msg = _( "afk_set", user=ctx.author.mention, reason=_("afk_reason", reason=afk_reason) if afk_reason else "", ) await ctx.send(msg) # Prevent stacking [AFK] try: current_nick = ctx.author.nick or ctx.author.name if "[AFK]" not in current_nick: await ctx.author.edit(nick=f"{current_nick} [AFK]") except discord.Forbidden: logging.warning(f"Missing permissions to edit nickname for {ctx.author}") except Exception as e: logging.error(f"Error editing nickname: {e}") guild = ctx.guild try: role = discord.utils.get(ctx.guild.roles, name="AFK") if not role: role = await guild.create_role(name="AFK", hoist=True) all_roles = await guild.fetch_roles() num_roles = len(all_roles) await role.edit(reason=None, position=num_roles - 2) await ctx.author.add_roles(role) except discord.Forbidden: logging.warning("Missing permissions to create/add AFK role.") except Exception as e: logging.error(f"Error handling AFK role: {e}") try: await ctx.message.delete() except discord.Forbidden: pass @commands.command(name="afklist") async def afk_list(self, ctx): afks = self.db.fetch_all( "SELECT USERID, REASON, TIMESTAMP FROM afk_status WHERE GUILDID = %s", (ctx.guild.id,), ) if not afks: await ctx.reply("No one is AFK right now.") return lines = [] for afk in afks: member = ctx.guild.get_member(int(afk["USERID"])) reason = afk["REASON"] or "No reason given." timestamp = afk.get("TIMESTAMP") # Convert timestamp string to datetime if isinstance(timestamp, str): try: timestamp = datetime.fromisoformat(timestamp) except Exception: timestamp = None if timestamp: duration = datetime.utcnow() - timestamp duration_str = str(duration).split(".")[0] else: duration_str = "unknown duration" lines.append( f"{member.mention if member else afk['USERID']} - {reason} (AFK for {duration_str})" ) await ctx.send("\n".join(lines)) @commands.command(name="feedback") @commands.cooldown(1, 30, commands.BucketType.user) async def _feedback(self, ctx, *info): feedback_text = " ".join(info) timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") user = f"{ctx.author} ({ctx.author.id})" # Store feedback in DB self.db.execute_query( "INSERT INTO feedback (USER, GUILDID, TIMESTAMP, CONTENT) VALUES (%s, %s, %s, %s)", (user, ctx.guild.id if ctx.guild else None, timestamp, feedback_text), ) await ctx.reply(_("feedback_received")) @commands.Cog.listener() async def on_command_error(self, ctx, error): # Notify admin if permissions are missing if isinstance( error, (commands.MissingPermissions, commands.BotMissingPermissions) ): perms = getattr(error, "missing_perms", None) perms_str = ", ".join(perms) if perms else "Unknown" owner = ctx.guild.owner if ctx.guild else None if owner: try: await owner.send( _("missing_perms", guild=ctx.guild.name, perms=perms_str) ) except Exception: logging.warning( f"Could not DM owner of {ctx.guild.name} about missing permissions." ) raise error # Let default handler run too async def setup(client): await client.add_cog(Informational(client))