feat: add emoji parsing and link accessory building for display components
This commit is contained in:
@@ -416,7 +416,7 @@ export const command: CommandMessage = {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const reply = await i.reply({ flags: 64, content: 'Selecciona el elemento que quieres duplicar:', components: [{ type: 1, components: [{ type: 3, custom_id: 'duplicate_select', placeholder: 'Elige un elemento', options }] }], fetchReply: true });
|
await i.reply({ flags: 64, content: 'Selecciona el elemento que quieres duplicar:', components: [{ type: 1, components: [{ type: 3, custom_id: 'duplicate_select', placeholder: 'Elige un elemento', options }] }] });
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const replyMsg = await i.fetchReply();
|
const replyMsg = await i.fetchReply();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@@ -282,19 +282,55 @@ async function sendBlockConfigV2(message: Message, blockConfigName: string, guil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper: parsear emojis (unicode o personalizados <:name:id> / <a:name:id>)
|
||||||
|
function parseEmojiInput(input?: string): any | null {
|
||||||
|
if (!input) return null;
|
||||||
|
const trimmed = input.trim();
|
||||||
|
if (!trimmed) return null;
|
||||||
|
const match = trimmed.match(/^<(a?):(\w+):(\d+)>$/);
|
||||||
|
if (match) {
|
||||||
|
const animated = match[1] === 'a';
|
||||||
|
const name = match[2];
|
||||||
|
const id = match[3];
|
||||||
|
return { id, name, animated };
|
||||||
|
}
|
||||||
|
// Asumimos unicode si no es formato de emoji personalizado
|
||||||
|
return { name: trimmed };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: construir accessory de Link Button para Display Components
|
||||||
|
async function buildLinkAccessory(link: any, user: any, guild: any) {
|
||||||
|
try {
|
||||||
|
if (!link || !link.url) return null;
|
||||||
|
// @ts-ignore
|
||||||
|
const processedUrl = await replaceVars(link.url, user, guild);
|
||||||
|
if (!isValidUrl(processedUrl)) return null;
|
||||||
|
const accessory: any = { type: 2, style: 5, url: processedUrl };
|
||||||
|
if (link.label && typeof link.label === 'string' && link.label.trim()) {
|
||||||
|
accessory.label = link.label.trim().slice(0, 80);
|
||||||
|
}
|
||||||
|
if (link.emoji && typeof link.emoji === 'string') {
|
||||||
|
const parsed = parseEmojiInput(link.emoji);
|
||||||
|
if (parsed) accessory.emoji = parsed;
|
||||||
|
}
|
||||||
|
// Debe tener al menos label o emoji
|
||||||
|
if (!accessory.label && !accessory.emoji) return null;
|
||||||
|
return accessory;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function convertConfigToDisplayComponent(config: any, user: any, guild: any): Promise<any> {
|
async function convertConfigToDisplayComponent(config: any, user: any, guild: any): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const previewComponents = [];
|
const previewComponents: any[] = [];
|
||||||
|
|
||||||
// Añadir imagen de portada primero si existe
|
// Añadir imagen de portada primero si existe
|
||||||
if (config.coverImage && isValidUrl(config.coverImage)) {
|
if (config.coverImage && isValidUrl(config.coverImage)) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const processedCoverUrl = await replaceVars(config.coverImage, user, guild);
|
const processedCoverUrl = await replaceVars(config.coverImage, user, guild);
|
||||||
if (isValidUrl(processedCoverUrl)) {
|
if (isValidUrl(processedCoverUrl)) {
|
||||||
previewComponents.push({
|
previewComponents.push({ type: 12, items: [{ media: { url: processedCoverUrl } }] });
|
||||||
type: 12,
|
|
||||||
items: [{ media: { url: processedCoverUrl } }]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,74 +347,44 @@ async function convertConfigToDisplayComponent(config: any, user: any, guild: an
|
|||||||
if (config.components && Array.isArray(config.components)) {
|
if (config.components && Array.isArray(config.components)) {
|
||||||
for (const c of config.components) {
|
for (const c of config.components) {
|
||||||
if (c.type === 10) {
|
if (c.type === 10) {
|
||||||
// Componente de texto con thumbnail opcional
|
// Texto con accessory opcional: priorizar linkButton > thumbnail
|
||||||
|
// @ts-ignore
|
||||||
|
const processedContent = await replaceVars(c.content || " ", user, guild);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const processedThumbnail = c.thumbnail ? await replaceVars(c.thumbnail, user, guild) : null;
|
const processedThumbnail = c.thumbnail ? await replaceVars(c.thumbnail, user, guild) : null;
|
||||||
|
|
||||||
if (processedThumbnail && isValidUrl(processedThumbnail)) {
|
let accessory: any = null;
|
||||||
// Si tiene thumbnail válido, usar contenedor tipo 9 con accessory
|
if (c.linkButton) {
|
||||||
previewComponents.push({
|
accessory = await buildLinkAccessory(c.linkButton, user, guild);
|
||||||
type: 9,
|
}
|
||||||
components: [
|
if (!accessory && processedThumbnail && isValidUrl(processedThumbnail)) {
|
||||||
{
|
accessory = { type: 11, media: { url: processedThumbnail } };
|
||||||
type: 10,
|
}
|
||||||
// @ts-ignore
|
|
||||||
content: await replaceVars(c.content || " ", user, guild)
|
if (accessory) {
|
||||||
}
|
previewComponents.push({ type: 9, components: [{ type: 10, content: processedContent }], accessory });
|
||||||
],
|
|
||||||
accessory: {
|
|
||||||
type: 11,
|
|
||||||
media: { url: processedThumbnail }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// Sin thumbnail o thumbnail inválido, componente normal
|
previewComponents.push({ type: 10, content: processedContent });
|
||||||
previewComponents.push({
|
|
||||||
type: 10,
|
|
||||||
// @ts-ignore
|
|
||||||
content: await replaceVars(c.content || " ", user, guild)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else if (c.type === 14) {
|
} else if (c.type === 14) {
|
||||||
// Separador
|
previewComponents.push({ type: 14, divider: c.divider ?? true, spacing: c.spacing ?? 1 });
|
||||||
previewComponents.push({
|
|
||||||
type: 14,
|
|
||||||
divider: c.divider ?? true,
|
|
||||||
spacing: c.spacing ?? 1
|
|
||||||
});
|
|
||||||
} else if (c.type === 12) {
|
} else if (c.type === 12) {
|
||||||
// Imagen - validar URL también
|
// Imagen - validar URL también
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const processedImageUrl = await replaceVars(c.url, user, guild);
|
const processedImageUrl = await replaceVars(c.url, user, guild);
|
||||||
|
|
||||||
if (isValidUrl(processedImageUrl)) {
|
if (isValidUrl(processedImageUrl)) {
|
||||||
previewComponents.push({
|
previewComponents.push({ type: 12, items: [{ media: { url: processedImageUrl } }] });
|
||||||
type: 12,
|
|
||||||
items: [{ media: { url: processedImageUrl } }]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retornar la estructura exacta que usa el editor
|
// Retornar la estructura exacta que usa el editor
|
||||||
return {
|
return { type: 17, accent_color: config.color ?? null, components: previewComponents };
|
||||||
type: 17, // Container type
|
|
||||||
accent_color: config.color ?? null,
|
|
||||||
components: previewComponents
|
|
||||||
};
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error convirtiendo configuración a Display Component:', error);
|
console.error('Error convirtiendo configuración a Display Component:', error);
|
||||||
|
return { type: 17, accent_color: null, components: [ { type: 10, content: 'Error al procesar la configuración del bloque.' } ] };
|
||||||
// Fallback: crear un componente básico
|
|
||||||
return {
|
|
||||||
type: 17,
|
|
||||||
accent_color: null,
|
|
||||||
components: [
|
|
||||||
{ type: 10, content: 'Error al procesar la configuración del bloque.' }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user