import discord
from discord.ext import commands
import asyncio
BOT_TOKEN = '' # Token bot
COMMAND_PREFIX = '!'
intents = discord.Intents.default()
intents.guilds = True
intents.members = True
intents.messages = True
intents.message_content = True
intents.emojis_and_stickers = True
bot = commands.Bot(command_prefix=COMMAND_PREFIX, intents=intents)
async def send_embed_message(ctx, title, description, color=discord.Color.blue()):
"""Helper function to send formatted embed messages."""
embed = discord.Embed(title=title, description=description, color=color)
try:
await ctx.send(embed=embed)
except discord.Forbidden:
print(f"Error: Bot is missing permissions to send messages in {ctx.channel.name} of {ctx.guild.name}")
except discord.HTTPException as e:
print(f"Error: Failed to send embed message: {e}")
async def clone_roles(source_guild, target_guild, ctx):
"""Clones roles from source_guild to target_guild."""
await send_embed_message(ctx, "Role Cloning", f"Starting role cloning from '{source_guild.name}' to '{target_guild.name}'...")
role_map = {}
source_roles = sorted(source_guild.roles, key=lambda r: r.position, reverse=True)
existing_target_roles = {role.name: role for role in target_guild.roles}
cloned_count = 0
skipped_count = 0
for role in source_roles:
if role.name == "@everyone":
role_map[role.id] = target_guild.default_role
skipped_count += 1
continue
if role.is_bot_managed() or role.is_integration() or role.is_premium_subscriber():
skipped_count += 1
print(f"Skipping special role: {role.name} (bot managed, integration, or booster)")
continue
try:
if role.name in existing_target_roles:
new_role = existing_target_roles[role.name]
print(f"Role '{role.name}' already exists in target server. Using existing and mapping.")
skipped_count +=1 #
else:
print(f"Cloning role: {role.name}")
new_role = await target_guild.create_role(
name=role.name,
permissions=role.permissions,
colour=role.colour,
hoist=role.hoist,
mentionable=role.mentionable
)
cloned_count += 1
role_map[role.id] = new_role
await asyncio.sleep(1.5)
except discord.Forbidden:
await send_embed_message(ctx, "Role Cloning Error", f"Missing permissions to create or manage role: {role.name}", discord.Color.red())
print(f"Error: Missing permissions to create role {role.name}")
except discord.HTTPException as e:
await send_embed_message(ctx, "Role Cloning Error", f"HTTP error creating role {role.name}: {e.status} - {e.text}", discord.Color.red())
print(f"Error: HTTP error creating role {role.name}: {e}")
except Exception as e:
await send_embed_message(ctx, "Role Cloning Error", f"An unexpected error occurred creating role {role.name}: {e}", discord.Color.red())
print(f"An unexpected error occurred creating role {role.name}: {e}")
await send_embed_message(ctx, "Role Cloning Complete", f"Cloned {cloned_count} new roles. Found and mapped {skipped_count} existing or special roles.", discord.Color.green())
return role_map
async def clone_categories_and_channels(source_guild, target_guild, role_map, ctx):
"""Clones categories and their channels from source_guild to target_guild."""
await send_embed_message(ctx, "Category & Channel Cloning", f"Starting category and channel cloning...")
cloned_categories_count = 0
cloned_channels_count = 0
skipped_channels_count = 0
channel_map = {}
sorted_categories = sorted(source_guild.categories, key=lambda c: c.position)
for category in sorted_categories:
print(f"Cloning category: {category.name}")
try:
category_overwrites = {}
for target_obj, overwrite in category.overwrites.items():
if isinstance(target_obj, discord.Role):
if target_obj.id in role_map:
new_role = role_map[target_obj.id]
category_overwrites[new_role] = overwrite
elif isinstance(target_obj, discord.Member):
print(f"Skipping member-specific overwrite for {target_obj.name} in category {category.name}")
existing_category = discord.utils.get(target_guild.categories, name=category.name)
if existing_category:
new_category = existing_category
print(f"Category '{category.name}' already exists. Using existing.")
else:
new_category = await target_guild.create_category(
name=category.name,
overwrites=category_overwrites,
position=category.position
)
cloned_categories_count += 1
await asyncio.sleep(1.5)
sorted_channels_in_category = sorted(category.channels, key=lambda ch: ch.position)
for channel in sorted_channels_in_category:
print(f"Cloning channel: {channel.name} in category {new_category.name}")
try:
channel_overwrites = {}
for target_obj, overwrite in channel.overwrites.items():
if isinstance(target_obj, discord.Role):
if target_obj.id in role_map:
new_role = role_map[target_obj.id]
channel_overwrites[new_role] = overwrite
elif isinstance(target_obj, discord.Member):
print(f"Skipping member-specific overwrite for {target_obj.name} in channel {channel.name}")
common_channel_params = {
'name': channel.name,
'overwrites': channel_overwrites,
'category': new_category,
'position': channel.position,
}
new_cloned_channel = None
if isinstance(channel, discord.TextChannel):
if not discord.utils.get(new_category.text_channels, name=channel.name):
new_cloned_channel = await target_guild.create_text_channel(
**common_channel_params,
topic=channel.topic,
slowmode_delay=channel.slowmode_delay,
nsfw=channel.nsfw
)
cloned_channels_count +=1
else:
new_cloned_channel = discord.utils.get(new_category.text_channels, name=channel.name)
print(f"Text channel '{channel.name}' already exists in category '{new_category.name}'. Skipping creation.")
skipped_channels_count +=1
elif isinstance(channel, discord.VoiceChannel):
if not discord.utils.get(new_category.voice_channels, name=channel.name):
new_cloned_channel = await target_guild.create_voice_channel(
**common_channel_params,
bitrate=channel.bitrate,
user_limit=channel.user_limit
)
cloned_channels_count +=1
else:
new_cloned_channel = discord.utils.get(new_category.voice_channels, name=channel.name)
print(f"Voice channel '{channel.name}' already exists in category '{new_category.name}'. Skipping creation.")
skipped_channels_count +=1
else:
print(f"Skipping channel '{channel.name}' of type {type(channel)}. Cloning not implemented for this type.")
skipped_channels_count +=1
if new_cloned_channel:
channel_map[channel.id] = new_cloned_channel
await asyncio.sleep(1.5)
except discord.Forbidden:
await send_embed_message(ctx, "Channel Cloning Error", f"Missing permissions to create channel: {channel.name} in {new_category.name}", discord.Color.red())
except discord.HTTPException as e:
await send_embed_message(ctx, "Channel Cloning Error", f"HTTP error creating channel {channel.name}: {e.status} - {e.text}", discord.Color.red())
except Exception as e:
await send_embed_message(ctx, "Channel Cloning Error", f"Unexpected error creating channel {channel.name}: {e}", discord.Color.red())
except discord.Forbidden:
await send_embed_message(ctx, "Category Cloning Error", f"Missing permissions to create category: {category.name}", discord.Color.red())
except discord.HTTPException as e:
await send_embed_message(ctx, "Category Cloning Error", f"HTTP error creating category {category.name}: {e.status} - {e.text}", discord.Color.red())
except Exception as e:
await send_embed_message(ctx, "Category Cloning Error", f"Unexpected error creating category {category.name}: {e}", discord.Color.red())
standalone_channels = sorted([ch for ch in source_guild.channels if ch.category is None], key=lambda c: c.position)
for channel in standalone_channels:
print(f"Cloning channel (no category): {channel.name}")
try:
channel_overwrites = {}
for target_obj, overwrite in channel.overwrites.items():
if isinstance(target_obj, discord.Role):
if target_obj.id in role_map:
new_role = role_map[target_obj.id]
channel_overwrites[new_role] = overwrite
common_channel_params = {
'name': channel.name,
'overwrites': channel_overwrites,
'position': channel.position,
'category': None
}
new_cloned_channel = None
if isinstance(channel, discord.TextChannel):
if not discord.utils.get(target_guild.text_channels, name=channel.name, category=None):
new_cloned_channel = await target_guild.create_text_channel(
**common_channel_params,
topic=channel.topic,
slowmode_delay=channel.slowmode_delay,
nsfw=channel.nsfw
)
cloned_channels_count +=1
else:
new_cloned_channel = discord.utils.get(target_guild.text_channels, name=channel.name, category=None)
print(f"Skipping existing top-level text channel: {channel.name}")
skipped_channels_count +=1
elif isinstance(channel, discord.VoiceChannel):
if not discord.utils.get(target_guild.voice_channels, name=channel.name, category=None):
new_cloned_channel = await target_guild.create_voice_channel(
**common_channel_params,
bitrate=channel.bitrate,
user_limit=channel.user_limit
)
cloned_channels_count +=1
else:
new_cloned_channel = discord.utils.get(target_guild.voice_channels, name=channel.name, category=None)
print(f"Skipping existing top-level voice channel: {channel.name}")
skipped_channels_count +=1
else:
print(f"Skipping top-level channel '{channel.name}' of type {type(channel)}. Cloning not implemented.")
skipped_channels_count +=1
if new_cloned_channel:
channel_map[channel.id] = new_cloned_channel
await asyncio.sleep(1.5)
except discord.Forbidden:
await send_embed_message(ctx, "Channel Cloning Error", f"Missing permissions to create channel (no category): {channel.name}", discord.Color.red())
except discord.HTTPException as e:
await send_embed_message(ctx, "Channel Cloning Error", f"HTTP error creating channel (no category) {channel.name}: {e.status} - {e.text}", discord.Color.red())
except Exception as e:
await send_embed_message(ctx, "Channel Cloning Error", f"Unexpected error creating channel (no category) {channel.name}: {e}", discord.Color.red())
await send_embed_message(ctx, "Category & Channel Cloning Complete",
f"Cloned {cloned_categories_count} categories and {cloned_channels_count} channels. Skipped {skipped_channels_count} channels (e.g., existing, other types).",
discord.Color.green())
return channel_map
async def clone_messages_experimental(source_channel, target_channel, ctx):
"""
EXPERIMENTAL: Attempts to clone messages from source_channel to target_channel.
USE WITH EXTREME CAUTION. This can take a very long time and hit rate limits.
"""
await send_embed_message(ctx, f"Message Cloning: #{target_channel.name}",
f"Attempting to clone messages from #{source_channel.name} to #{target_channel.name}.\n"
f"**WARNING:** This is **highly experimental, very slow, and API intensive.**\n"
f"It may take a very long time and can lead to rate limiting or errors.",
discord.Color.orange())
cloned_message_count = 0
webhook = None
try:
webhooks = await target_channel.webhooks()
webhook = discord.utils.get(webhooks, name=f"{bot.user.name} Cloner")
if not webhook:
webhook = await target_channel.create_webhook(name=f"{bot.user.name} Cloner", reason="Server Cloning")
except discord.Forbidden:
await send_embed_message(ctx, "Webhook Info", f"Bot lacks 'Manage Webhooks' permission in #{target_channel.name}. Messages will be sent directly by the bot.", discord.Color.light_grey())
webhook = None # Proceed without webhook
except discord.HTTPException as e_wh:
await send_embed_message(ctx, "Webhook Error", f"Could not create/fetch webhook for #{target_channel.name}: {e_wh}. Messages will be sent directly.", discord.Color.light_grey())
webhook = None
try:
async for message in source_channel.history(limit=None, oldest_first=True):
if message.author.bot and message.webhook_id is None :
continue
text_content = message.content
embeds_to_send = []
if message.embeds:
for original_embed in message.embeds:
if original_embed.type == 'rich':
try:
embed_dict = original_embed.to_dict()
embed_dict.pop('provider', None)
embed_dict.pop('video', None)
if 'timestamp' in embed_dict:
embed_dict['timestamp'] = discord.utils.parse_time(embed_dict['timestamp']).isoformat() if isinstance(embed_dict['timestamp'], str) else message.created_at.isoformat()
# Truncate embed fields to their limits
if 'title' in embed_dict and embed_dict['title']:
embed_dict['title'] = embed_dict['title'][:256]
if 'description' in embed_dict and embed_dict['description']:
embed_dict['description'] = embed_dict['description'][:4096]
if 'author' in embed_dict and 'name' in embed_dict['author'] and embed_dict['author']['name']:
embed_dict['author']['name'] = embed_dict['author']['name'][:256]
if 'footer' in embed_dict and 'text' in embed_dict['footer'] and embed_dict['footer']['text']:
embed_dict['footer']['text'] = embed_dict['footer']['text'][:2048]
if 'fields' in embed_dict:
for field in embed_dict['fields']:
if 'name' in field and field['name']:
field['name'] = field['name'][:256]
if 'value' in field and field['value']:
field['value'] = field['value'][:1024]
embed_dict['fields'] = embed_dict['fields'][:25] # Max 25 fields
recreated_embed = discord.Embed.from_dict(embed_dict)
embeds_to_send.append(recreated_embed)
except Exception as e_embed:
print(f"Could not fully recreate embed from {message.jump_url}: {e_embed}")
text_content += f"\n[Original Embed by {message.author.name} was present but could not be fully cloned]"
files_to_send = []
attachment_links_text = ""
if message.attachments:
attachment_links_text += "\n\n**Attachments (links from original server):**"
for att in message.attachments:
attachment_links_text += f"\n- {att.filename}: {att.url}"
text_content += attachment_links_text
max_len = 2000
author_name = message.author.display_name[:80]
author_avatar_url = str(message.author.display_avatar.url) if message.author.display_avatar else None
content_parts = []
current_part = ""
for line in text_content.split('\n'):
if len(current_part) + len(line) + 1 > max_len:
content_parts.append(current_part)
current_part = line
else:
current_part += ("\n" + line) if current_part else line
if current_part:
content_parts.append(current_part)
if not content_parts and not embeds_to_send and not files_to_send:
continue
for i, part in enumerate(content_parts if content_parts else [""]):
final_embeds_for_part = embeds_to_send if i == len(content_parts) -1 or not content_parts else []
final_files_for_part = files_to_send if i == len(content_parts) -1 or not content_parts else []
if webhook:
await webhook.send(
content=part if part else None,
username=f"{author_name} (cloned)",
avatar_url=author_avatar_url,
embeds=final_embeds_for_part[:10],
files=final_files_for_part[:10]
)
else:
header = f"**Original Author:** {message.author.name}#{message.author.discriminator}\n**Original Timestamp:** {message.created_at.strftime('%Y-%m-%d %H:%M:%S UTC')}\n---\n"
full_content_part = header + part
if len(full_content_part) > max_len:
await target_channel.send(part, embeds=final_embeds_for_part[:10], files=final_files_for_part[:10])
else:
await target_channel.send(full_content_part, embeds=final_embeds_for_part[:10], files=final_files_for_part[:10])
cloned_message_count += 1
await asyncio.sleep(2.5)
await send_embed_message(ctx, f"Message Cloning for #{target_channel.name}", f"Cloned approximately {cloned_message_count} messages (experimental).", discord.Color.green())
except discord.Forbidden:
await send_embed_message(ctx, "Message Cloning Error", f"Missing permissions to read from #{source_channel.name} or write/manage webhooks in #{target_channel.name}.", discord.Color.red())
except discord.HTTPException as e:
await send_embed_message(ctx, "Message Cloning Error", f"HTTP error during message cloning for #{target_channel.name}: {e.status} - {e.text}", discord.Color.red())
except Exception as e:
await send_embed_message(ctx, "Message Cloning Error", f"Unexpected error during message cloning for #{target_channel.name}: {e}", discord.Color.red())
print(f"Detailed message cloning error for {target_channel.name}: {e.__class__.__name__}: {e}")
async def clone_emojis_func(source_guild, target_guild, ctx):
"""Clones custom emojis from source_guild to target_guild."""
await send_embed_message(ctx, "Emoji Cloning", f"Starting emoji cloning from '{source_guild.name}' to '{target_guild.name}'...")
cloned_count = 0
skipped_count = 0
failed_count = 0
existing_target_emoji_names = [emoji.name for emoji in target_guild.emojis]
for emoji in source_guild.emojis:
if not emoji.available:
print(f"Skipping unavailable emoji: {emoji.name}")
skipped_count +=1
continue
if emoji.name in existing_target_emoji_names:
print(f"Emoji '{emoji.name}' already exists in target server. Skipping.")
skipped_count += 1
continue
try:
print(f"Cloning emoji: {emoji.name}")
emoji_image_bytes = await emoji.read()
await target_guild.create_custom_emoji(
name=emoji.name,
image=emoji_image_bytes,
reason=f"Cloned from server {source_guild.name} by {ctx.author.name}"
)
cloned_count += 1
await asyncio.sleep(2)
except discord.Forbidden:
await send_embed_message(ctx, "Emoji Cloning Error", f"Missing 'Manage Emojis' permission in '{target_guild.name}' to create emoji: {emoji.name}", discord.Color.red())
failed_count += 1
break
except discord.HTTPException as e:
if e.status == 400 and e.code == 30008:
await send_embed_message(ctx, "Emoji Cloning Error", f"Could not create emoji '{emoji.name}'. Target server '{target_guild.name}' has reached its maximum emoji limit.", discord.Color.red())
else:
await send_embed_message(ctx, "Emoji Cloning Error", f"HTTP error creating emoji {emoji.name}: {e.status} - {e.text}", discord.Color.red())
failed_count += 1
print(f"Error: HTTP error creating emoji {emoji.name}: {e}")
if e.code == 30008:
await send_embed_message(ctx, "Emoji Cloning Halted", "Target server has reached its emoji limit. Further emoji cloning aborted.", discord.Color.orange())
break
except Exception as e:
await send_embed_message(ctx, "Emoji Cloning Error", f"An unexpected error occurred creating emoji {emoji.name}: {e}", discord.Color.red())
failed_count += 1
print(f"An unexpected error occurred creating emoji {emoji.name}: {e}")
summary_message = f"Emoji cloning complete for '{target_guild.name}':\n"
summary_message += f"- Cloned: {cloned_count}\n"
summary_message += f"- Skipped (existing/unavailable): {skipped_count}\n"
summary_message += f"- Failed: {failed_count}"
await send_embed_message(ctx, "Emoji Cloning Summary", summary_message, discord.Color.green() if failed_count == 0 else discord.Color.orange())
# --- Bot Commands ---
@bot.command(name='clone')
@commands.has_permissions(administrator=True)
@commands.guild_only()
async def clone_server(ctx, target_server_id: int, clone_messages_flag: str = "no"):
"""
Clones server structure (roles, categories, channels) to a target server.
Optionally attempts to clone messages (USE WITH EXTREME CAUTION).
Usage: !clone [clone_messages_yes_or_no]
Example: !clone 123456789012345678 no
Example (Risky): !clone 123456789012345678 yes
"""
source_guild = ctx.guild
try:
target_guild = bot.get_guild(target_server_id)
if target_guild is None:
target_guild = await bot.fetch_guild(target_server_id)
except discord.NotFound:
await send_embed_message(ctx, "Error", f"Could not find the target server with ID: {target_server_id}. Make sure the ID is correct and the bot is on that server.", discord.Color.red())
return
except discord.Forbidden:
await send_embed_message(ctx, "Error", f"Bot lacks permissions to fetch target server with ID: {target_server_id}.", discord.Color.red())
return
if not target_guild:
await send_embed_message(ctx, "Error", f"Could not find or access the target server with ID: {target_server_id}. Make sure the bot is on that server.", discord.Color.red())
return
if source_guild.id == target_guild.id:
await send_embed_message(ctx, "Error", "Source and target server cannot be the same.", discord.Color.red())
return
confirm_embed = discord.Embed(title="⚠️ Server Clone Confirmation ⚠️", color=discord.Color.orange(),
description=f"You are about to clone **{source_guild.name}** to **{target_guild.name}**.\n"
f"This will attempt to recreate roles, categories, and channels.\n"
f"If `clone_messages` is set to `yes`, it will also attempt to clone messages (VERY SLOW & RISKY).\n\n"
f"**This action can be disruptive and time-consuming. Ensure the target server is prepared (e.g., empty or a backup).**\n\n"
f"React with ✅ to confirm within 30 seconds, or ❌ to cancel.")
confirm_msg = await ctx.send(embed=confirm_embed)
await confirm_msg.add_reaction("✅")
await confirm_msg.add_reaction("❌")
def check(reaction, user):
return user == ctx.author and str(reaction.emoji) in ["✅", "❌"] and reaction.message.id == confirm_msg.id
try:
reaction, user = await bot.wait_for('reaction_add', timeout=30.0, check=check)
except asyncio.TimeoutError:
await confirm_msg.edit(embed=discord.Embed(title="Clone Cancelled", description="Confirmation timed out.", color=discord.Color.red()))
await confirm_msg.clear_reactions()
return
if str(reaction.emoji) == "❌":
await confirm_msg.edit(embed=discord.Embed(title="Clone Cancelled", description="Clone operation cancelled by user.", color=discord.Color.light_grey()))
await confirm_msg.clear_reactions()
return
await confirm_msg.clear_reactions()
await confirm_msg.edit(embed=discord.Embed(title="Clone Confirmed", description="Proceeding with server clone...", color=discord.Color.green()))
await send_embed_message(ctx, "Clone Process Started",
f"Preparing to clone '{source_guild.name}' to '{target_guild.name}'. This may take some time...",
discord.Color.gold())
# 1. Clone Roles
role_map = await clone_roles(source_guild, target_guild, ctx)
if not role_map:
if not (target_guild.default_role.id == role_map.get(source_guild.default_role.id, None)):
await send_embed_message(ctx, "Clone Halted", "Role cloning failed or produced no usable map. Halting clone process.", discord.Color.red())
return
await asyncio.sleep(2)
channel_map = await clone_categories_and_channels(source_guild, target_guild, role_map, ctx)
await asyncio.sleep(2)
if clone_messages_flag.lower() == "yes":
await send_embed_message(ctx, "Message Cloning Initiated (Experimental)",
"Attempting to clone messages. This is VERY SLOW and RISKY. Monitor bot console and channel messages for progress/errors.",
discord.Color.orange())
text_channels_cloned = 0
for old_channel_id, new_target_channel in channel_map.items():
source_channel = source_guild.get_channel(old_channel_id)
if source_channel and isinstance(source_channel, discord.TextChannel) and isinstance(new_target_channel, discord.TextChannel):
await clone_messages_experimental(source_channel, new_target_channel, ctx)
text_channels_cloned +=1
await asyncio.sleep(5) # Significant delay between cloning each channel's messages
elif source_channel and not isinstance(source_channel, discord.TextChannel):
pass # Not a text channel, skip message cloning
else:
print(f"Could not find source channel ID {old_channel_id} or target channel {new_target_channel.name} is not a text channel for message cloning.")
await send_embed_message(ctx, "Message Cloning Phase", f"Message cloning attempted for {text_channels_cloned} text channels. Check individual channel statuses.", discord.Color.light_grey())
await send_embed_message(ctx, "Cloning Process Summary", f"Initial cloning tasks for '{source_guild.name}' to '{target_guild.name}' are complete. Review logs and messages for details.", discord.Color.blurple())
print(f"Cloning process from {source_guild.name} to {target_guild.name} initiated by {ctx.author.name} has finished its main tasks.")
@bot.command(name='emoji', aliases=['emojis'])
@commands.has_permissions(administrator=True)
@commands.guild_only()
async def emoji_server(ctx, target_server_id: int):
"""
Clones all custom emojis from the current server to a target server.
Usage: !emoji
Example: !emoji 123456789012345678
"""
source_guild = ctx.guild
try:
target_guild = bot.get_guild(target_server_id)
if target_guild is None: # Try fetching if not in cache
target_guild = await bot.fetch_guild(target_server_id)
except discord.NotFound:
await send_embed_message(ctx, "Error", f"Could not find the target server with ID: {target_server_id}. Make sure the ID is correct and the bot is on that server.", discord.Color.red())
return
except discord.Forbidden:
await send_embed_message(ctx, "Error", f"Bot lacks permissions to fetch target server with ID: {target_server_id}.", discord.Color.red())
return
if not target_guild:
await send_embed_message(ctx, "Error", f"Could not find or access target server with ID: {target_server_id}. Make sure the bot is on that server.", discord.Color.red())
return
if source_guild.id == target_guild.id:
await send_embed_message(ctx, "Error", "Source and target server cannot be the same for emoji cloning.", discord.Color.red())
return
if not source_guild.emojis:
await send_embed_message(ctx, "Emoji Cloning", f"Source server '{source_guild.name}' has no custom emojis to clone.", discord.Color.light_grey())
return
# Confirmation step
confirm_embed = discord.Embed(title="⚠️ Emoji Clone Confirmation ⚠️", color=discord.Color.orange(),
description=f"You are about to clone all custom emojis from **{source_guild.name}** to **{target_guild.name}**.\n"
f"This may take some time and is subject to emoji slot limits on the target server.\n\n"
f"React with ✅ to confirm within 30 seconds, or ❌ to cancel.")
confirm_msg = await ctx.send(embed=confirm_embed)
await confirm_msg.add_reaction("✅")
await confirm_msg.add_reaction("❌")
def check(reaction, user):
return user == ctx.author and str(reaction.emoji) in ["✅", "❌"] and reaction.message.id == confirm_msg.id
try:
reaction, user = await bot.wait_for('reaction_add', timeout=30.0, check=check)
except asyncio.TimeoutError:
await confirm_msg.edit(embed=discord.Embed(title="Emoji Clone Cancelled", description="Confirmation timed out.", color=discord.Color.red()))
await confirm_msg.clear_reactions()
return
if str(reaction.emoji) == "❌":
await confirm_msg.edit(embed=discord.Embed(title="Emoji Clone Cancelled", description="Operation cancelled by user.", color=discord.Color.light_grey()))
await confirm_msg.clear_reactions()
return
await confirm_msg.clear_reactions()
await confirm_msg.edit(embed=discord.Embed(title="Emoji Clone Confirmed", description="Proceeding with emoji clone...", color=discord.Color.green()))
await clone_emojis_func(source_guild, target_guild, ctx)
@clone_server.error
@emoji_server.error
async def command_error_handler(ctx, error):
if isinstance(error, commands.MissingPermissions):
await send_embed_message(ctx, "Permission Denied", "You need Administrator permissions to use this command.", discord.Color.red())
elif isinstance(error, commands.MissingRequiredArgument):
await send_embed_message(ctx, "Missing Argument", f"You are missing: `{error.param.name}`. Usage: `{ctx.prefix}{ctx.command.name} {ctx.command.signature}`", discord.Color.red())
elif isinstance(error, commands.BadArgument):
await send_embed_message(ctx, "Invalid Argument", f"Argument `{error.param.name if hasattr(error, 'param') else 'unknown'}` was invalid. Check command usage: `{ctx.prefix}{ctx.command.name} {ctx.command.signature}`", discord.Color.red())
elif isinstance(error, commands.GuildNotFound):
await send_embed_message(ctx, "Server Not Found", f"Could not find a server with the ID: {error.argument}. Ensure the bot is in that server.", discord.Color.red())
elif isinstance(error, commands.CommandInvokeError) and isinstance(error.original, discord.NotFound): # For fetch_guild
await send_embed_message(ctx, "Server Not Found", f"Could not find the target server. Ensure the ID is correct and the bot is on that server.", discord.Color.red())
elif isinstance(error, commands.CommandInvokeError) and isinstance(error.original, discord.Forbidden): # For fetch_guild
await send_embed_message(ctx, "Permissions Error", f"The bot lacks permissions to access the target server or perform an action there.", discord.Color.red())
elif isinstance(error, commands.NoPrivateMessage):
await ctx.author.send("This command cannot be used in private messages.")
else:
await send_embed_message(ctx, "Command Error", f"An unexpected error occurred: {error}", discord.Color.red())
print(f"Error in '{ctx.command.name}' command by {ctx.author.name} ({ctx.author.id}) in {ctx.guild.name} ({ctx.guild.id}): {type(error)} - {error}")
# --- Bot Events ---
@bot.event
async def on_ready():
print(f'Logged in as {bot.user.name} (ID: {bot.user.id})')
print(f'Discord.py Version: {discord.__version__}')
print('Bot is ready and listening for commands.')
print('Make sure the bot has Administrator permissions on source and target servers for cloning.')
print('Make sure Privileged Intents (Server Members, Message Content, Emojis & Stickers) are enabled in the Discord Developer Portal.')
print('------')
await bot.change_presence(activity=discord.Game(name=f"{COMMAND_PREFIX}clone | {COMMAND_PREFIX}emoji"))
# --- Run the Bot ---
if __name__ == "__main__":
if BOT_TOKEN == 'YOUR_BOT_TOKEN_HERE' or not BOT_TOKEN:
print("CRITICAL ERROR: Please replace 'YOUR_BOT_TOKEN_HERE' with your actual bot token in the script.")
else:
try:
bot.run(BOT_TOKEN)
except discord.LoginFailure:
print("CRITICAL ERROR: Failed to log in. Please ensure your BOT_TOKEN is correct and valid.")
except discord.PrivilegedIntentsRequired:
print("CRITICAL ERROR: Privileged Intents (Server Members, Message Content, and/or Emojis & Stickers) are not enabled for the bot in the Discord Developer Portal. Please enable them.")
except Exception as e:
print(f"CRITICAL ERROR: An unexpected error occurred while trying to run the bot: {e}")