265 lines
12 KiB
Python
265 lines
12 KiB
Python
# ╔══════════════════════════════════════════════════════════════════╗
|
|
# ║ ║
|
|
# ║ +-+-+-+-+-+-+-+-+ ║
|
|
# ║ |H|e|x|a|H|o|s|t| ║
|
|
# ║ +-+-+-+-+-+-+-+-+ ║
|
|
# ║ ║
|
|
# ║ © 2026 HexaHost — All Rights Reserved ║
|
|
# ║ ║
|
|
# ║ discord ── https://discord.gg/hexahost ║
|
|
# ║ github ── https://github.com/theoneandonlymace ║
|
|
# ║ ║
|
|
# ╚══════════════════════════════════════════════════════════════════╝
|
|
|
|
import os
|
|
import discord
|
|
from utils.emoji import CROSS, TICK
|
|
from discord.ext import commands
|
|
from discord import ui
|
|
import asyncio
|
|
from utils.Tools import *
|
|
import re
|
|
|
|
|
|
class EmbedBuilder(ui.LayoutView):
|
|
def __init__(self, ctx):
|
|
super().__init__(timeout=180)
|
|
self.ctx = ctx
|
|
self.message = None
|
|
self.embed_data = {
|
|
"title": "Edit your Embed!",
|
|
"description": "Select Options from the menu below to customize.",
|
|
"color": 0xFF0000,
|
|
"thumbnail": None,
|
|
"image": None,
|
|
"footer_text": None,
|
|
"footer_icon": None,
|
|
"author_text": None,
|
|
"author_icon": None,
|
|
"fields": []
|
|
}
|
|
self.container = ui.Container(accent_color=None)
|
|
self._build_view()
|
|
self.add_item(self.container)
|
|
|
|
def _get_preview(self):
|
|
d = self.embed_data
|
|
lines = []
|
|
if d["title"]:
|
|
lines.append(f"**Title:** {d['title']}")
|
|
if d["description"]:
|
|
lines.append(f"**Description:** {d['description']}")
|
|
if d["color"]:
|
|
lines.append(f"**Color:** `#{d['color']:06X}`")
|
|
if d["thumbnail"]:
|
|
lines.append(f"**Thumbnail:** [Set]({d['thumbnail']})")
|
|
if d["image"]:
|
|
lines.append(f"**Image:** [Set]({d['image']})")
|
|
if d["footer_text"]:
|
|
lines.append(f"**Footer:** {d['footer_text']}")
|
|
if d["footer_icon"]:
|
|
lines.append(f"**Footer Icon:** [Set]({d['footer_icon']})")
|
|
if d["author_text"]:
|
|
lines.append(f"**Author:** {d['author_text']}")
|
|
if d["author_icon"]:
|
|
lines.append(f"**Author Icon:** [Set]({d['author_icon']})")
|
|
if d["fields"]:
|
|
for i, f in enumerate(d["fields"]):
|
|
lines.append(f"**Field {i+1}:** {f['name']} — {f['value']}")
|
|
return "\n".join(lines) if lines else "No properties set yet."
|
|
|
|
def _build_view(self):
|
|
self.container.clear_items()
|
|
|
|
self.container.add_item(ui.TextDisplay("# Embed Builder"))
|
|
self.container.add_item(ui.Separator())
|
|
self.container.add_item(ui.TextDisplay(self._get_preview()))
|
|
self.container.add_item(ui.Separator())
|
|
self.container.add_item(ui.TextDisplay("*Select an option to edit. Respond within 30 seconds.*"))
|
|
|
|
# Select menu
|
|
select = ui.Select(
|
|
placeholder="Choose an option to edit the Embed",
|
|
min_values=1, max_values=1,
|
|
options=[
|
|
discord.SelectOption(label="Title", description="Edit the title"),
|
|
discord.SelectOption(label="Description", description="Edit the description"),
|
|
discord.SelectOption(label="Add Field", description="Add a field"),
|
|
discord.SelectOption(label="Color", description="Edit the color (hex)"),
|
|
discord.SelectOption(label="Thumbnail", description="Set thumbnail URL"),
|
|
discord.SelectOption(label="Image", description="Set image URL"),
|
|
discord.SelectOption(label="Footer Text", description="Edit footer text"),
|
|
discord.SelectOption(label="Footer Icon", description="Set footer icon URL"),
|
|
discord.SelectOption(label="Author Text", description="Edit author text"),
|
|
discord.SelectOption(label="Author Icon", description="Set author icon URL"),
|
|
]
|
|
)
|
|
select.callback = self._select_callback
|
|
self.container.add_item(ui.ActionRow(select))
|
|
|
|
# Buttons
|
|
send_btn = ui.Button(label="Send Embed", emoji=TICK, style=discord.ButtonStyle.success)
|
|
send_btn.callback = self._send_callback
|
|
cancel_btn = ui.Button(label="Cancel Setup", emoji=CROSS, style=discord.ButtonStyle.danger)
|
|
cancel_btn.callback = self._cancel_callback
|
|
self.container.add_item(ui.ActionRow(send_btn, cancel_btn))
|
|
|
|
def _build_embed(self):
|
|
"""Build a real discord.Embed from stored data"""
|
|
d = self.embed_data
|
|
embed = discord.Embed(
|
|
title=d["title"],
|
|
description=d["description"],
|
|
color=d["color"]
|
|
)
|
|
if d["thumbnail"]:
|
|
embed.set_thumbnail(url=d["thumbnail"])
|
|
if d["image"]:
|
|
embed.set_image(url=d["image"])
|
|
if d["footer_text"] or d["footer_icon"]:
|
|
embed.set_footer(text=d["footer_text"] or "", icon_url=d["footer_icon"] or discord.Embed.Empty)
|
|
if d["author_text"] or d["author_icon"]:
|
|
embed.set_author(name=d["author_text"] or "", icon_url=d["author_icon"] or discord.Embed.Empty)
|
|
for field in d["fields"]:
|
|
embed.add_field(name=field["name"], value=field["value"], inline=False)
|
|
return embed
|
|
|
|
async def _select_callback(self, interaction: discord.Interaction):
|
|
if interaction.user.id != self.ctx.author.id:
|
|
await interaction.response.send_message("This builder doesn't belong to you.", ephemeral=True)
|
|
return
|
|
await interaction.response.defer()
|
|
|
|
value = interaction.data["values"][0]
|
|
|
|
def chk(m):
|
|
return m.channel.id == self.ctx.channel.id and m.author.id == self.ctx.author.id
|
|
|
|
prompts = {
|
|
"Title": "Enter the **Title** of the embed:",
|
|
"Description": "Enter the **Description** of the embed:",
|
|
"Color": "Enter the color as a hex value (e.g., `#FF0000`):",
|
|
"Thumbnail": "Enter the **Thumbnail URL**:",
|
|
"Image": "Enter the **Image URL**:",
|
|
"Footer Text": "Enter the **Footer text**:",
|
|
"Footer Icon": "Enter the **Footer icon URL**:",
|
|
"Author Text": "Enter the **Author text**:",
|
|
"Author Icon": "Enter the **Author icon URL**:",
|
|
"Add Field": "Enter the **Field title**:",
|
|
}
|
|
|
|
await self.ctx.send(prompts.get(value, "Enter a value:"))
|
|
|
|
try:
|
|
msg = await self.ctx.bot.wait_for("message", timeout=30, check=chk)
|
|
|
|
if value == "Title":
|
|
self.embed_data["title"] = msg.content
|
|
elif value == "Description":
|
|
self.embed_data["description"] = msg.content
|
|
elif value == "Color":
|
|
try:
|
|
self.embed_data["color"] = int(msg.content.strip("#"), 16)
|
|
except ValueError:
|
|
await self.ctx.send("Invalid hex color. Please try again.")
|
|
return
|
|
elif value == "Thumbnail":
|
|
if not msg.content.startswith("http"):
|
|
await self.ctx.send("Invalid URL format.")
|
|
return
|
|
self.embed_data["thumbnail"] = msg.content
|
|
elif value == "Image":
|
|
if not msg.content.startswith("http"):
|
|
await self.ctx.send("Invalid URL format.")
|
|
return
|
|
self.embed_data["image"] = msg.content
|
|
elif value == "Footer Text":
|
|
self.embed_data["footer_text"] = msg.content
|
|
elif value == "Footer Icon":
|
|
if not msg.content.startswith("http"):
|
|
await self.ctx.send("Invalid URL format.")
|
|
return
|
|
self.embed_data["footer_icon"] = msg.content
|
|
elif value == "Author Text":
|
|
self.embed_data["author_text"] = msg.content
|
|
elif value == "Author Icon":
|
|
if not msg.content.startswith("http"):
|
|
await self.ctx.send("Invalid URL format.")
|
|
return
|
|
self.embed_data["author_icon"] = msg.content
|
|
elif value == "Add Field":
|
|
field_name = msg.content
|
|
await self.ctx.send("Enter the **Field value**:")
|
|
val_msg = await self.ctx.bot.wait_for("message", timeout=30, check=chk)
|
|
self.embed_data["fields"].append({"name": field_name, "value": val_msg.content})
|
|
|
|
# Rebuild and update
|
|
self._build_view()
|
|
await self.message.edit(view=self)
|
|
|
|
except asyncio.TimeoutError:
|
|
await self.ctx.send("Timed Out.")
|
|
|
|
async def _send_callback(self, interaction: discord.Interaction):
|
|
if interaction.user.id != self.ctx.author.id:
|
|
await interaction.response.send_message("This builder doesn't belong to you.", ephemeral=True)
|
|
return
|
|
await interaction.response.defer()
|
|
|
|
await self.ctx.send("Mention the **channel** where you want to send this embed:")
|
|
|
|
def chk(m):
|
|
return m.channel.id == self.ctx.channel.id and m.author.id == self.ctx.author.id
|
|
|
|
try:
|
|
msg = await self.ctx.bot.wait_for("message", timeout=30, check=chk)
|
|
chnl = msg.channel_mentions[0]
|
|
embed = self._build_embed()
|
|
await chnl.send(embed=embed)
|
|
|
|
# Show success
|
|
self.container.clear_items()
|
|
self.container.add_item(ui.TextDisplay(f"# {TICK} Embed Sent"))
|
|
self.container.add_item(ui.Separator())
|
|
self.container.add_item(ui.TextDisplay(f"Successfully sent the embed to {chnl.mention}"))
|
|
await self.message.edit(view=self)
|
|
|
|
except asyncio.TimeoutError:
|
|
await self.ctx.send("Timed Out.")
|
|
except (IndexError, AttributeError):
|
|
await self.ctx.send("Please mention a valid channel.")
|
|
|
|
async def _cancel_callback(self, interaction: discord.Interaction):
|
|
if interaction.user.id != self.ctx.author.id:
|
|
await interaction.response.send_message("This builder doesn't belong to you.", ephemeral=True)
|
|
return
|
|
self.container.clear_items()
|
|
self.container.add_item(ui.TextDisplay("# Embed Builder"))
|
|
self.container.add_item(ui.Separator())
|
|
self.container.add_item(ui.TextDisplay(f"{CROSS} Embed setup cancelled."))
|
|
await interaction.response.edit_message(view=self)
|
|
self.stop()
|
|
|
|
async def on_timeout(self):
|
|
try:
|
|
self.container.clear_items()
|
|
self.container.add_item(ui.TextDisplay("# Embed Builder"))
|
|
self.container.add_item(ui.Separator())
|
|
self.container.add_item(ui.TextDisplay("⏰ Builder timed out. Use the command again."))
|
|
await self.message.edit(view=self)
|
|
except:
|
|
pass
|
|
|
|
|
|
class Embed(commands.Cog):
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
|
|
@commands.hybrid_command(name="embed")
|
|
@blacklist_check()
|
|
@ignore_check()
|
|
@commands.cooldown(1, 7, commands.BucketType.user)
|
|
@commands.has_permissions(manage_messages=True)
|
|
async def _embed(self, ctx):
|
|
view = EmbedBuilder(ctx)
|
|
view.message = await ctx.send(view=view) |