mirror of
https://github.com/Lilac-Rose/Lacie.git
synced 2026-08-25 10:14:32 -05:00
removed help command and fixed rm .git/COMMIT_EDITMSG bug
This commit is contained in:
@@ -9,7 +9,6 @@ EMPTY = "⚪"
|
||||
RED = "🔴"
|
||||
YELLOW = "🟡"
|
||||
|
||||
|
||||
class ColumnButton(discord.ui.Button):
|
||||
def __init__(self, col: int):
|
||||
super().__init__(
|
||||
|
||||
249
commands/help.py
249
commands/help.py
@@ -1,249 +0,0 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord import app_commands
|
||||
from typing import Optional
|
||||
import difflib
|
||||
|
||||
class Help(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
def get_command_signature(self, command):
|
||||
"""Get the usage signature for a command"""
|
||||
if isinstance(command, commands.HybridCommand):
|
||||
# For hybrid commands, show both formats
|
||||
params = []
|
||||
for name, param in command.clean_params.items():
|
||||
if param.default == param.empty:
|
||||
params.append(f"<{name}>")
|
||||
else:
|
||||
params.append(f"[{name}]")
|
||||
param_str = " ".join(params)
|
||||
return f"!{command.name} {param_str}\n/{command.name} {param_str}"
|
||||
else:
|
||||
return f"{command.qualified_name} {command.signature}"
|
||||
|
||||
def get_all_commands(self):
|
||||
"""Get all commands organized by cog"""
|
||||
cog_commands = {}
|
||||
|
||||
for cog_name, cog in self.bot.cogs.items():
|
||||
commands_list = []
|
||||
|
||||
# Get regular commands
|
||||
for cmd in cog.get_commands():
|
||||
if not cmd.hidden:
|
||||
commands_list.append(cmd)
|
||||
|
||||
# Get app commands (slash only)
|
||||
if hasattr(cog, '__cog_app_commands__'):
|
||||
for cmd in cog.__cog_app_commands__:
|
||||
if isinstance(cmd, app_commands.Command):
|
||||
commands_list.append(cmd)
|
||||
elif isinstance(cmd, app_commands.Group):
|
||||
# Add group commands
|
||||
commands_list.append(cmd)
|
||||
|
||||
if commands_list:
|
||||
cog_commands[cog_name] = commands_list
|
||||
|
||||
# Get commands not in cogs
|
||||
no_cog_commands = [cmd for cmd in self.bot.commands if not cmd.cog and not cmd.hidden]
|
||||
if no_cog_commands:
|
||||
cog_commands["Other"] = no_cog_commands
|
||||
|
||||
return cog_commands
|
||||
|
||||
def search_commands(self, query):
|
||||
"""Search for commands matching the query"""
|
||||
query = query.lower()
|
||||
results = []
|
||||
|
||||
for cog_name, cog in self.bot.cogs.items():
|
||||
for cmd in cog.get_commands():
|
||||
if not cmd.hidden:
|
||||
# Check command name
|
||||
if query in cmd.name.lower():
|
||||
results.append((cmd, cog_name, 100))
|
||||
# Check command aliases
|
||||
elif any(query in alias.lower() for alias in cmd.aliases):
|
||||
results.append((cmd, cog_name, 90))
|
||||
# Check description
|
||||
elif cmd.description and query in cmd.description.lower():
|
||||
results.append((cmd, cog_name, 70))
|
||||
# Check help text
|
||||
elif cmd.help and query in cmd.help.lower():
|
||||
results.append((cmd, cog_name, 60))
|
||||
|
||||
# Sort by relevance
|
||||
results.sort(key=lambda x: x[2], reverse=True)
|
||||
return results
|
||||
|
||||
def create_help_embed(self, title, description=None):
|
||||
"""Create a base help embed"""
|
||||
embed = discord.Embed(
|
||||
title=title,
|
||||
description=description,
|
||||
color=discord.Color.blurple()
|
||||
)
|
||||
embed.set_footer(text="Use !help <command> or /help <command> for more info on a specific command")
|
||||
return embed
|
||||
|
||||
@commands.hybrid_command(name="help", description="Shows help information for commands")
|
||||
@app_commands.describe(command="The command to get help for")
|
||||
async def help_command(self, ctx: commands.Context, *, command: Optional[str] = None):
|
||||
"""
|
||||
Get help for bot commands
|
||||
|
||||
Usage:
|
||||
!help - Show all commands
|
||||
!help <command> - Get detailed help for a specific command
|
||||
!help <search> - Search for commands
|
||||
"""
|
||||
|
||||
if command:
|
||||
# Try to find exact command match first
|
||||
cmd = self.bot.get_command(command.lower())
|
||||
|
||||
if cmd:
|
||||
# Show detailed help for specific command
|
||||
embed = self.create_help_embed(
|
||||
title=f"Help: {cmd.name}",
|
||||
description=cmd.help or cmd.description or "No description available"
|
||||
)
|
||||
|
||||
# Usage
|
||||
embed.add_field(
|
||||
name="Usage",
|
||||
value=f"```\n{self.get_command_signature(cmd)}\n```",
|
||||
inline=False
|
||||
)
|
||||
|
||||
# Aliases
|
||||
if cmd.aliases:
|
||||
embed.add_field(
|
||||
name="Aliases",
|
||||
value=", ".join(f"`{alias}`" for alias in cmd.aliases),
|
||||
inline=False
|
||||
)
|
||||
|
||||
# Category
|
||||
if cmd.cog:
|
||||
embed.add_field(name="Category", value=cmd.cog.qualified_name, inline=True)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
# Search for commands
|
||||
results = self.search_commands(command)
|
||||
|
||||
if not results:
|
||||
# Suggest similar commands
|
||||
all_cmd_names = [c.name for c in self.bot.commands]
|
||||
suggestions = difflib.get_close_matches(command.lower(), all_cmd_names, n=3, cutoff=0.6)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="❌ Command Not Found",
|
||||
description=f"No command found matching `{command}`",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
|
||||
if suggestions:
|
||||
embed.add_field(
|
||||
name="Did you mean?",
|
||||
value="\n".join(f"• `{s}`" for s in suggestions),
|
||||
inline=False
|
||||
)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
# Show search results
|
||||
embed = self.create_help_embed(
|
||||
title=f"🔍 Search Results for '{command}'",
|
||||
description=f"Found {len(results)} matching command(s)"
|
||||
)
|
||||
|
||||
for cmd, cog_name, score in results[:10]: # Limit to 10 results
|
||||
desc = cmd.description or cmd.help or "No description"
|
||||
if len(desc) > 100:
|
||||
desc = desc[:97] + "..."
|
||||
|
||||
embed.add_field(
|
||||
name=f"{cmd.name} ({cog_name})",
|
||||
value=desc,
|
||||
inline=False
|
||||
)
|
||||
|
||||
if len(results) > 10:
|
||||
embed.set_footer(text=f"Showing top 10 of {len(results)} results. Use !help <command> for details.")
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
# Show all commands organized by category
|
||||
cog_commands = self.get_all_commands()
|
||||
|
||||
embed = self.create_help_embed(
|
||||
title="📚 Bot Commands",
|
||||
description=f"Prefix: `!` | Total Commands: {len(list(self.bot.commands))}\n\n"
|
||||
"Commands work with both `!` and `/`"
|
||||
)
|
||||
|
||||
for cog_name, cmds in sorted(cog_commands.items()):
|
||||
if not cmds:
|
||||
continue
|
||||
|
||||
# Group commands by name
|
||||
cmd_names = []
|
||||
seen = set()
|
||||
|
||||
for cmd in cmds:
|
||||
if isinstance(cmd, (commands.Command, commands.HybridCommand)):
|
||||
name = cmd.name
|
||||
elif isinstance(cmd, (app_commands.Command, app_commands.Group)):
|
||||
name = cmd.name
|
||||
else:
|
||||
continue
|
||||
|
||||
if name not in seen:
|
||||
cmd_names.append(f"`{name}`")
|
||||
seen.add(name)
|
||||
|
||||
if cmd_names:
|
||||
embed.add_field(
|
||||
name=f"**{cog_name}**",
|
||||
value=" • ".join(cmd_names),
|
||||
inline=False
|
||||
)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_command_error(self, ctx: commands.Context, error):
|
||||
"""Show help if command not found"""
|
||||
if isinstance(error, commands.CommandNotFound):
|
||||
# Extract the attempted command
|
||||
cmd_used = ctx.message.content.split()[0][len(ctx.prefix):]
|
||||
|
||||
# Suggest similar commands
|
||||
all_cmd_names = [c.name for c in self.bot.commands]
|
||||
suggestions = difflib.get_close_matches(cmd_used, all_cmd_names, n=3, cutoff=0.6)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="❓ Unknown Command",
|
||||
description=f"Command `{cmd_used}` not found.",
|
||||
color=discord.Color.orange()
|
||||
)
|
||||
|
||||
if suggestions:
|
||||
embed.add_field(
|
||||
name="Did you mean?",
|
||||
value="\n".join(f"• `!{s}` or `/{s}`" for s in suggestions),
|
||||
inline=False
|
||||
)
|
||||
|
||||
embed.set_footer(text="Use !help to see all commands")
|
||||
await ctx.send(embed=embed, delete_after=10)
|
||||
|
||||
async def setup(bot):
|
||||
# Remove default help command
|
||||
bot.remove_command('help')
|
||||
await bot.add_cog(Help(bot))
|
||||
@@ -24,6 +24,9 @@ class DenyModal(discord.ui.Modal, title="Reason for denying suggestion"):
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
try:
|
||||
# Defer the response immediately to prevent timeout
|
||||
await interaction.response.defer(ephemeral=False)
|
||||
|
||||
reason_text = self.reason.value or None
|
||||
|
||||
db_path = os.path.join(os.path.dirname(__file__), "suggestions.db")
|
||||
@@ -31,17 +34,39 @@ class DenyModal(discord.ui.Modal, title="Reason for denying suggestion"):
|
||||
await db.execute("UPDATE suggestions SET status = ?, reason = ? WHERE id = ?", ("Denied", reason_text, self.suggestion_id))
|
||||
await db.commit()
|
||||
|
||||
await interaction.response.send_message(f"❌ Suggestion #{self.suggestion_id} denied.", ephemeral=False)
|
||||
# Use followup instead of response since we deferred
|
||||
await interaction.followup.send(f"❌ Suggestion #{self.suggestion_id} denied.", ephemeral=False)
|
||||
|
||||
# Disable buttons on the admin message
|
||||
if self.admin_message_id:
|
||||
try:
|
||||
admin_user = await self.bot.fetch_user(ADMIN_ID)
|
||||
dm = admin_user.dm_channel or await admin_user.create_dm()
|
||||
orig_msg = await dm.fetch_message(self.admin_message_id)
|
||||
disabled_view = SuggestionButtons(
|
||||
self.bot,
|
||||
suggestion_id=self.suggestion_id,
|
||||
user_id=self.user_id,
|
||||
suggestion_text=self.suggestion_text,
|
||||
channel_id=self.channel_id,
|
||||
admin_message_id=self.admin_message_id,
|
||||
disabled=True
|
||||
)
|
||||
await orig_msg.edit(view=disabled_view)
|
||||
except Exception as e:
|
||||
print(f"Failed to edit admin message: {e}")
|
||||
|
||||
# Send DM to user
|
||||
try:
|
||||
user = await self.bot.fetch_user(self.user_id)
|
||||
dm_note = f"❌ Your suggestion (ID: {self.suggestion_id}) — `{self.suggestion_text}` has been **denied**."
|
||||
if reason_text:
|
||||
dm_note += f"\n**Reason:** {reason_text}"
|
||||
await user.send(dm_note)
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Failed to DM user: {e}")
|
||||
|
||||
# Send message in original channel
|
||||
channel = self.bot.get_channel(self.channel_id)
|
||||
if channel:
|
||||
try:
|
||||
@@ -49,21 +74,16 @@ class DenyModal(discord.ui.Modal, title="Reason for denying suggestion"):
|
||||
if reason_text:
|
||||
msg += f"\n**Reason:** {reason_text}"
|
||||
await channel.send(msg)
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Failed to send message in channel: {e}")
|
||||
|
||||
if self.admin_message_id:
|
||||
try:
|
||||
admin_user = await self.bot.fetch_user(ADMIN_ID)
|
||||
dm = admin_user.dm_channel or await admin_user.create_dm()
|
||||
orig_msg = await dm.fetch_message(self.admin_message_id)
|
||||
disabled_view = SuggestionButtons(self.bot, suggestion_id=self.suggestion_id, user_id=self.user_id, suggestion_text=self.suggestion_text, channel_id=self.channel_id, admin_message_id=self.admin_message_id, disabled=True)
|
||||
await orig_msg.edit(view=disabled_view)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
error_msg = f"❌ Error denying suggestion: {str(e)}\n```{traceback.format_exc()}```"
|
||||
await interaction.response.send_message(error_msg[:2000], ephemeral=True)
|
||||
print(error_msg)
|
||||
try:
|
||||
await interaction.followup.send(error_msg[:2000], ephemeral=True)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class SuggestionButtons(discord.ui.View):
|
||||
@@ -76,8 +96,8 @@ class SuggestionButtons(discord.ui.View):
|
||||
self.channel_id = channel_id
|
||||
self.admin_message_id = admin_message_id
|
||||
|
||||
approve_cid = f"suggest_approve_{suggestion_id}"
|
||||
deny_cid = f"suggest_deny_{suggestion_id}"
|
||||
approve_cid = f"suggest_approve_{suggestion_id}" if suggestion_id else "suggest_approve"
|
||||
deny_cid = f"suggest_deny_{suggestion_id}" if suggestion_id else "suggest_deny"
|
||||
|
||||
approve_btn = discord.ui.Button(label="Approve ✅", style=discord.ButtonStyle.success, custom_id=approve_cid, disabled=disabled)
|
||||
approve_btn.callback = self.approve
|
||||
@@ -97,43 +117,61 @@ class SuggestionButtons(discord.ui.View):
|
||||
await interaction.response.send_message("⚠️ This button is no longer active.", ephemeral=True)
|
||||
return
|
||||
|
||||
# Defer immediately to prevent timeout
|
||||
await interaction.response.defer(ephemeral=False)
|
||||
|
||||
db_path = os.path.join(os.path.dirname(__file__), "suggestions.db")
|
||||
async with aiosqlite.connect(db_path) as db:
|
||||
await db.execute("UPDATE suggestions SET status = ? WHERE id = ?", ("Approved", self.suggestion_id))
|
||||
await db.commit()
|
||||
|
||||
await interaction.response.send_message(f"✅ Suggestion #{self.suggestion_id} approved.", ephemeral=False)
|
||||
|
||||
try:
|
||||
user = await self.bot.fetch_user(self.user_id)
|
||||
await user.send(f"✅ Your suggestion (ID: {self.suggestion_id}) — `{self.suggestion_text}` has been **approved!**")
|
||||
except:
|
||||
pass
|
||||
|
||||
channel = self.bot.get_channel(self.channel_id)
|
||||
if channel:
|
||||
await channel.send(f"✅ Suggestion **#{self.suggestion_id}** (`{self.suggestion_text}`) has been **approved!**")
|
||||
# Use followup since we deferred
|
||||
await interaction.followup.send(f"✅ Suggestion #{self.suggestion_id} approved.", ephemeral=False)
|
||||
|
||||
# Disable buttons on the admin message
|
||||
if self.admin_message_id:
|
||||
try:
|
||||
admin_user = await self.bot.fetch_user(ADMIN_ID)
|
||||
dm = admin_user.dm_channel or await admin_user.create_dm()
|
||||
orig_msg = await dm.fetch_message(self.admin_message_id)
|
||||
disabled_view = SuggestionButtons(self.bot, suggestion_id=self.suggestion_id, user_id=self.user_id, suggestion_text=self.suggestion_text, channel_id=self.channel_id, admin_message_id=self.admin_message_id, disabled=True)
|
||||
disabled_view = SuggestionButtons(
|
||||
self.bot,
|
||||
suggestion_id=self.suggestion_id,
|
||||
user_id=self.user_id,
|
||||
suggestion_text=self.suggestion_text,
|
||||
channel_id=self.channel_id,
|
||||
admin_message_id=self.admin_message_id,
|
||||
disabled=True
|
||||
)
|
||||
await orig_msg.edit(view=disabled_view)
|
||||
except Exception:
|
||||
for item in self.children:
|
||||
item.disabled = True
|
||||
try:
|
||||
await interaction.message.edit(view=self)
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Failed to edit admin message: {e}")
|
||||
|
||||
# Send DM to user
|
||||
try:
|
||||
user = await self.bot.fetch_user(self.user_id)
|
||||
await user.send(f"✅ Your suggestion (ID: {self.suggestion_id}) — `{self.suggestion_text}` has been **approved!**")
|
||||
except Exception as e:
|
||||
print(f"Failed to DM user: {e}")
|
||||
|
||||
# Send message in original channel
|
||||
channel = self.bot.get_channel(self.channel_id)
|
||||
if channel:
|
||||
try:
|
||||
await channel.send(f"✅ Suggestion **#{self.suggestion_id}** (`{self.suggestion_text}`) has been **approved!**")
|
||||
except Exception as e:
|
||||
print(f"Failed to send message in channel: {e}")
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"❌ Error approving suggestion: {str(e)}\n```{traceback.format_exc()}```"
|
||||
print(error_msg)
|
||||
try:
|
||||
await interaction.response.send_message(error_msg[:2000], ephemeral=True)
|
||||
if not interaction.response.is_done():
|
||||
await interaction.response.send_message(error_msg[:2000], ephemeral=True)
|
||||
else:
|
||||
await interaction.followup.send(error_msg[:2000], ephemeral=True)
|
||||
except:
|
||||
await interaction.followup.send(error_msg[:2000], ephemeral=True)
|
||||
pass
|
||||
|
||||
async def deny(self, interaction: discord.Interaction):
|
||||
try:
|
||||
@@ -145,14 +183,25 @@ class SuggestionButtons(discord.ui.View):
|
||||
await interaction.response.send_message("⚠️ This button is no longer active.", ephemeral=True)
|
||||
return
|
||||
|
||||
modal = DenyModal(suggestion_id=self.suggestion_id, user_id=self.user_id, suggestion_text=self.suggestion_text, channel_id=self.channel_id, admin_message_id=self.admin_message_id, bot=self.bot)
|
||||
modal = DenyModal(
|
||||
suggestion_id=self.suggestion_id,
|
||||
user_id=self.user_id,
|
||||
suggestion_text=self.suggestion_text,
|
||||
channel_id=self.channel_id,
|
||||
admin_message_id=self.admin_message_id,
|
||||
bot=self.bot
|
||||
)
|
||||
await interaction.response.send_modal(modal)
|
||||
except Exception as e:
|
||||
error_msg = f"❌ Error opening deny modal: {str(e)}\n```{traceback.format_exc()}```"
|
||||
print(error_msg)
|
||||
try:
|
||||
await interaction.response.send_message(error_msg[:2000], ephemeral=True)
|
||||
if not interaction.response.is_done():
|
||||
await interaction.response.send_message(error_msg[:2000], ephemeral=True)
|
||||
else:
|
||||
await interaction.followup.send(error_msg[:2000], ephemeral=True)
|
||||
except:
|
||||
await interaction.followup.send(error_msg[:2000], ephemeral=True)
|
||||
pass
|
||||
|
||||
|
||||
class PaginationView(discord.ui.View):
|
||||
@@ -218,16 +267,17 @@ class Suggestion(commands.Cog):
|
||||
rows = await cursor.fetchall()
|
||||
|
||||
for sid, uid, suggestion_text, channel_id, admin_msg_id in rows:
|
||||
view = SuggestionButtons(
|
||||
self.bot,
|
||||
suggestion_id=sid,
|
||||
user_id=uid,
|
||||
suggestion_text=suggestion_text,
|
||||
channel_id=channel_id,
|
||||
admin_message_id=admin_msg_id
|
||||
)
|
||||
self.bot.add_view(view, message_id=admin_msg_id)
|
||||
print(f"Re-registered view for suggestion #{sid} (message {admin_msg_id})")
|
||||
if admin_msg_id: # Only register if we have a message ID
|
||||
view = SuggestionButtons(
|
||||
self.bot,
|
||||
suggestion_id=sid,
|
||||
user_id=uid,
|
||||
suggestion_text=suggestion_text,
|
||||
channel_id=channel_id,
|
||||
admin_message_id=admin_msg_id
|
||||
)
|
||||
self.bot.add_view(view, message_id=admin_msg_id)
|
||||
print(f"Re-registered view for suggestion #{sid} (message {admin_msg_id})")
|
||||
|
||||
async def cog_unload(self):
|
||||
if self.db:
|
||||
@@ -280,6 +330,7 @@ class Suggestion(commands.Cog):
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to send DM to admin: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"❌ An error occurred: {str(e)}\n```{traceback.format_exc()}```"
|
||||
|
||||
@@ -145,25 +145,8 @@ class BackupXP(commands.Cog):
|
||||
annual_size = os.path.getsize(annual_db) / (1024 * 1024)
|
||||
total_size = lifetime_size + annual_size
|
||||
|
||||
backup_type = "Auto Backup" if is_auto else "Manual Backup"
|
||||
msg = (
|
||||
f"✅ **{backup_type}** - Databases backed up successfully!\n"
|
||||
f"**Lifetime:** `{os.path.basename(lifetime_backup)}` ({lifetime_size:.2f} MB)\n"
|
||||
f"**Annual:** `{os.path.basename(annual_backup)}` ({annual_size:.2f} MB)\n"
|
||||
f"**Total size:** {total_size:.2f} MB"
|
||||
)
|
||||
|
||||
if log_channel:
|
||||
# Send to notification channel only
|
||||
notification_channel = self.bot.get_channel(NOTIFICATION_CHANNEL_ID)
|
||||
if notification_channel:
|
||||
print(f"[Backup] Sending notification to channel {NOTIFICATION_CHANNEL_ID}")
|
||||
await notification_channel.send(msg)
|
||||
else:
|
||||
print(f"[Backup] Could not find notification channel {NOTIFICATION_CHANNEL_ID}")
|
||||
|
||||
print(f"[Backup] Backup completed successfully")
|
||||
return True, msg
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"❌ Backup failed: `{e}`"
|
||||
|
||||
@@ -45,18 +45,6 @@ class ResetTask(commands.Cog):
|
||||
if last_reset_month != now.month:
|
||||
reset_leaderboard("monthly")
|
||||
print(f"[XP System] Monthly leaderboard reset at {now}")
|
||||
await self.send_reset_notification(notification_channel_id, user_id, "Monthly")
|
||||
|
||||
async def send_reset_notification(self, channel_id, user_id, reset_type):
|
||||
"""Send a notification message when a reset occurs."""
|
||||
try:
|
||||
channel = self.bot.get_channel(channel_id)
|
||||
if channel:
|
||||
await channel.send(f"<@{user_id}> {reset_type} leaderboard has been reset! 🔄")
|
||||
else:
|
||||
print(f"[XP System] Could not find channel {channel_id} for reset notification")
|
||||
except Exception as e:
|
||||
print(f"[XP System] Error sending reset notification: {e}")
|
||||
|
||||
def should_reset_daily(self, now):
|
||||
"""Check if it's time for daily reset (00:00 UTC)."""
|
||||
|
||||
Reference in New Issue
Block a user