refactor: add default help command and embed structure

This commit is contained in:
2025-10-29 10:33:39 -05:00
parent 2dae0b066f
commit f0f526e5c5
6 changed files with 219 additions and 28 deletions
+20 -15
View File
@@ -4,10 +4,11 @@ import type {
UnicordArgumentDefinition, UnicordArgumentDefinition,
UnicordCommandHandler, UnicordCommandHandler,
UnicordCommandOptions, UnicordCommandOptions,
UnicordHandler UnicordHandler,
} from '.'; } from '.';
import { UnicordCommandContext } from './context'; import { UnicordCommandContext } from './context';
import type { UnicordEventProcessor } from './eventProcessor'; import type { UnicordEventProcessor } from './eventProcessor';
import { Embed } from './types';
import { UnicordArgumentType, UnicordEventType } from './types/common'; import { UnicordArgumentType, UnicordEventType } from './types/common';
type UnicordCommandDefinition<Options extends UnicordCommandOptions> = { type UnicordCommandDefinition<Options extends UnicordCommandOptions> = {
@@ -46,32 +47,36 @@ export class UnicordCommandManager {
'help', 'help',
async (context) => { async (context) => {
// Just list chat commands for now // Just list chat commands for now
let reply = '## Available Commands:\n'; const reply = new Embed().setTitle('Available Commands');
for (const [command, defs] of this.handlers[UnicordEventType.ChatCommands]) { for (const [command, defs] of this.handlers[UnicordEventType.ChatCommands]) {
reply += `> \n`;
for (const def of defs) { for (const def of defs) {
reply += `> **${command}**: ${def.options.description ?? 'No description'}\n`; let fieldValue = def.options.description ?? 'No description';
// List arguments fieldValue += '\n';
if (def.options.args.length > 0) { if (def.options.args.length > 0) {
fieldValue += `\n\`${command}`;
for (const arg of def.options.args) { for (const arg of def.options.args) {
reply += `> \`${arg.name}\` (${UnicordArgumentType[arg.type]}${ fieldValue += arg.required ? ` <${arg.name}>` : ` <?${arg.name}>`;
}
fieldValue += '`\n';
for (const arg of def.options.args) {
fieldValue += `- \`${arg.name}\` (${UnicordArgumentType[arg.type]}${
arg.required ? ', required' : '' arg.required ? ', required' : ''
}): ${arg.description ?? 'No description'}\n`; }): ${arg.description ?? 'No description'}\n`;
} }
} }
// Show usage
reply += `> \n`; fieldValue += '\n';
reply += `> \`${command}`;
for (const arg of def.options.args) { reply.fields.push({
reply += arg.required ? ` <${arg.name}>` : ` <?${arg.name}>`; name: command,
} value: fieldValue,
reply += `\`\n`; });
} }
} }
await context.reply(reply); await context.reply(reply.toMessage());
}, },
{ {
description: 'Lists all available commands', description: 'Lists all available commands',
+4
View File
@@ -126,3 +126,7 @@ export class Unicord {
} }
export * from './types/common'; export * from './types/common';
// export * from './types/message';
// export * from './types/channel';
// export * from './types/guild';
// export * from './types/embed';
+182
View File
@@ -0,0 +1,182 @@
import { Message } from './message';
type EmbedFooter = {
text: string;
icon_url?: string;
proxy_icon_url?: string;
};
type EmbedImage = {
url?: string;
proxy_url?: string;
height?: number;
width?: number;
};
type EmbedAuthor = {
name: string;
url?: string;
icon_url?: string;
proxy_icon_url?: string;
};
type EmbedVideo = {
url?: string;
height?: number;
width?: number;
};
type EmbedField = {
name: string;
value: string;
inline?: boolean;
};
// === Embed Structure ===
// | Field | Type | Description |
// |--------------|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|
// | title? | string | title of embed |
// | type? | string | [type of embed](/docs/resources/message#embed-object-embed-types) (always "rich" for webhook embeds) |
// | description? | string | description of embed |
// | url? | string | url of embed |
// | timestamp? | ISO8601 timestamp | timestamp of embed content |
// | color? | integer | color code of the embed |
// | footer? | [embed footer](/docs/resources/message#embed-object-embed-footer-structure) object | footer information |
// | image? | [embed image](/docs/resources/message#embed-object-embed-image-structure) object | image information |
// | thumbnail? | [embed thumbnail](/docs/resources/message#embed-object-embed-thumbnail-structure) object | thumbnail information |
// | video? | [embed video](/docs/resources/message#embed-object-embed-video-structure) object | video information |
// | provider? | [embed provider](/docs/resources/message#embed-object-embed-provider-structure) object | provider information |
// | author? | [embed author](/docs/resources/message#embed-object-embed-author-structure) object | author information |
// | fields? | array of [embed field](/docs/resources/message#embed-object-embed-field-structure) objects | fields information, max of 25 |
export class Embed {
title?: string;
description?: string;
url?: string;
timestamp?: string;
color?: number;
footer?: EmbedFooter;
image?: EmbedImage;
thumbnail?: EmbedImage;
video?: EmbedVideo;
provider?: {
name?: string;
url?: string;
};
author?: EmbedImage & {
name?: string;
};
fields: EmbedField[];
constructor() {
this.fields = [];
}
setTitle = (title: string) => {
this.title = title;
return this;
};
setDescription = (description: string) => {
this.description = description;
return this;
};
setUrl = (url: string) => {
this.url = url;
return this;
};
setTimestamp = (timestamp: string) => {
this.timestamp = timestamp;
return this;
};
setColor = (color: number | string) => {
this.color = typeof color === 'string' ? parseInt(`0x${color.replace('#', '')}`) : color;
return this;
};
setFooter = (footer: EmbedFooter) => {
this.footer = footer;
return this;
};
setImage = (image: EmbedImage) => {
this.image = image;
return this;
};
setThumbnail = (thumbnail: EmbedImage) => {
this.thumbnail = thumbnail;
return this;
};
setVideo = (video: EmbedVideo) => {
this.video = video;
return this;
};
setProvider = (provider: { name?: string; url?: string }) => {
this.provider = provider;
return this;
};
setAuthor = (author: EmbedAuthor) => {
this.author = author;
return this;
};
addField = (field: EmbedField) => {
this.fields.push(field);
return this;
};
addFields = (fields: EmbedField[]) => {
this.fields.push(...fields);
return this;
};
toMessage = () => {
return new Message().addEmbed(this);
};
toJSON = () => {
return {
title: this.title,
description: this.description,
url: this.url,
timestamp: this.timestamp,
color: this.color,
footer: this.footer,
image: this.image,
thumbnail: this.thumbnail,
video: this.video,
provider: this.provider,
author: this.author,
fields: this.fields
};
};
}
export interface DiscordEmbed {
title?: string;
type?: string;
description?: string;
url?: string;
timestamp?: string;
color?: number;
footer?: EmbedFooter;
image?: EmbedImage;
thumbnail?: EmbedImage;
video?: EmbedVideo;
provider?: {
name?: string;
url?: string;
};
author?: EmbedImage & {
name?: string;
};
fields?: EmbedField[];
}
+2 -1
View File
@@ -2,4 +2,5 @@ export * from './common';
export * from './user'; export * from './user';
export * from './channel'; export * from './channel';
export * from './guild'; export * from './guild';
export * from './message'; export * from './message';
export * from './embed';
+9 -10
View File
@@ -2,7 +2,7 @@ import { exists, isNil } from '../utils';
import { UnicordGatewayObject } from './base'; import { UnicordGatewayObject } from './base';
import type { Channel } from './channel'; import type { Channel } from './channel';
import { fromDiscord, Trait, type Expectation } from './common'; import { fromDiscord, Trait, type Expectation } from './common';
// import type { Embed } from './embed'; import type { DiscordEmbed, Embed } from './embed';
import type { Guild } from './guild'; import type { Guild } from './guild';
import { User, type DiscordUser } from './user'; import { User, type DiscordUser } from './user';
@@ -94,7 +94,7 @@ export type MessagePayload = Message & {
author: User; author: User;
channel_id: string; channel_id: string;
content: string; content: string;
// embeds: Embed[]; embeds: Embed[];
components: Component[]; components: Component[];
message_reference?: { message_reference?: {
id: string; id: string;
@@ -110,7 +110,7 @@ export class Message extends UnicordGatewayObject<Message, DiscordMessage> {
channel_id?: string; channel_id?: string;
guild_id?: string; guild_id?: string;
public content: string; public content: string;
// public embeds: Embed[] = []; public embeds: Embed[] = [];
public components: Component[] = []; public components: Component[] = [];
public message_reference?: { public message_reference?: {
id: string; id: string;
@@ -178,10 +178,10 @@ export class Message extends UnicordGatewayObject<Message, DiscordMessage> {
return this; return this;
}; };
// public addEmbed = (embed: Embed) => { public addEmbed = (embed: Embed) => {
// this.embeds.push(embed); this.embeds.push(embed);
// return this; return this;
// }; };
public addComponent = (content: Component | string, ...components: Component[]) => { public addComponent = (content: Component | string, ...components: Component[]) => {
if (typeof content === 'string') { if (typeof content === 'string') {
@@ -223,7 +223,7 @@ export class Message extends UnicordGatewayObject<Message, DiscordMessage> {
public toJSON = () => { public toJSON = () => {
return { return {
content: this.content, content: this.content,
// embeds: this.embeds, embeds: this.embeds,
components: this.components, components: this.components,
message_reference: this.message_reference message_reference: this.message_reference
? { ? {
@@ -236,7 +236,6 @@ export class Message extends UnicordGatewayObject<Message, DiscordMessage> {
}; };
} }
// Raw API Object
export type DiscordMessage = { export type DiscordMessage = {
id: string; id: string;
channel_id: string; channel_id: string;
@@ -253,7 +252,7 @@ export type DiscordMessage = {
mention_roles: string[]; mention_roles: string[];
mention_channels?: { id: string; guild_id: string; type: number; name: string }[]; mention_channels?: { id: string; guild_id: string; type: number; name: string }[];
attachments: { id: string; filename: string; url: string; proxy_url: string; size: number }[]; attachments: { id: string; filename: string; url: string; proxy_url: string; size: number }[];
// embeds: DiscordEmbed[]; embeds: DiscordEmbed[];
reactions?: { count: number; me: boolean; emoji: Emoji }[]; reactions?: { count: number; me: boolean; emoji: Emoji }[];
nonce?: string | number; nonce?: string | number;
pinned: boolean; pinned: boolean;
+2 -2
View File
@@ -22,7 +22,7 @@ const client = new Unicord({
await context.reply(response); await context.reply(response);
}, },
{ {
description: 'Ping command', description: 'Say hello',
args: [ args: [
{ {
name: 'name', name: 'name',
@@ -48,7 +48,7 @@ const client = new Unicord({
await context.reply(hydrated.channel.name ?? 'Unnamed Channel'); await context.reply(hydrated.channel.name ?? 'Unnamed Channel');
}, },
{ {
description: 'Channel command', description: 'Get info about a channel',
args: [], args: [],
}, },
) )