Files
discord.js/packages/builders/src/messages/embed/Embed.ts
2022-01-24 20:26:36 +01:00

336 lines
7.1 KiB
TypeScript

import type {
APIEmbed,
APIEmbedAuthor,
APIEmbedField,
APIEmbedFooter,
APIEmbedImage,
APIEmbedProvider,
APIEmbedThumbnail,
APIEmbedVideo,
} from 'discord-api-types/v9';
import {
authorNamePredicate,
colorPredicate,
descriptionPredicate,
embedFieldsArrayPredicate,
fieldInlinePredicate,
fieldNamePredicate,
fieldValuePredicate,
footerTextPredicate,
timestampPredicate,
titlePredicate,
urlPredicate,
validateFieldLength,
} from './Assertions';
export interface AuthorOptions {
name: string;
url?: string;
iconURL?: string;
}
export interface FooterOptions {
text: string;
iconURL?: string;
}
/**
* Represents an embed in a message (image/video preview, rich embed, etc.)
*/
export class Embed implements APIEmbed {
/**
* An array of fields of this embed
*/
public readonly fields: APIEmbedField[];
/**
* The embed title
*/
public readonly title?: string;
/**
* The embed description
*/
public readonly description?: string;
/**
* The embed url
*/
public readonly url?: string;
/**
* The embed color
*/
public readonly color?: number;
/**
* The timestamp of the embed in the ISO format
*/
public readonly timestamp?: string;
/**
* The embed thumbnail data
*/
public readonly thumbnail?: APIEmbedThumbnail;
/**
* The embed image data
*/
public readonly image?: APIEmbedImage;
/**
* Received video data
*/
public readonly video?: APIEmbedVideo;
/**
* The embed author data
*/
public readonly author?: APIEmbedAuthor;
/**
* Received data about the embed provider
*/
public readonly provider?: APIEmbedProvider;
/**
* The embed footer data
*/
public readonly footer?: APIEmbedFooter;
public constructor(data: APIEmbed = {}) {
this.title = data.title;
this.description = data.description;
this.url = data.url;
this.color = data.color;
this.thumbnail = data.thumbnail;
this.image = data.image;
this.video = data.video;
this.author = data.author;
this.provider = data.provider;
this.footer = data.footer;
this.fields = data.fields ?? [];
if (data.timestamp) this.timestamp = new Date(data.timestamp).toISOString();
}
/**
* The accumulated length for the embed title, description, fields, footer text, and author name
*/
public get length(): number {
return (
(this.title?.length ?? 0) +
(this.description?.length ?? 0) +
this.fields.reduce((prev, curr) => prev + curr.name.length + curr.value.length, 0) +
(this.footer?.text.length ?? 0) +
(this.author?.name.length ?? 0)
);
}
/**
* Adds a field to the embed (max 25)
*
* @param field The field to add.
*/
public addField(field: APIEmbedField): this {
return this.addFields(field);
}
/**
* Adds fields to the embed (max 25)
*
* @param fields The fields to add
*/
public addFields(...fields: APIEmbedField[]): this {
// Data assertions
embedFieldsArrayPredicate.parse(fields);
// Ensure adding these fields won't exceed the 25 field limit
validateFieldLength(this.fields, fields.length);
this.fields.push(...Embed.normalizeFields(...fields));
return this;
}
/**
* Removes, replaces, or inserts fields in the embed (max 25)
*
* @param index The index to start at
* @param deleteCount The number of fields to remove
* @param fields The replacing field objects
*/
public spliceFields(index: number, deleteCount: number, ...fields: APIEmbedField[]): this {
// Data assertions
embedFieldsArrayPredicate.parse(fields);
// Ensure adding these fields won't exceed the 25 field limit
validateFieldLength(this.fields, fields.length - deleteCount);
this.fields.splice(index, deleteCount, ...Embed.normalizeFields(...fields));
return this;
}
/**
* Sets the embed's fields (max 25).
* @param fields The fields to set
*/
public setFields(...fields: APIEmbedField[]) {
this.spliceFields(0, this.fields.length, ...fields);
return this;
}
/**
* Sets the author of this embed
*
* @param options The options for the author
*/
public setAuthor(options: AuthorOptions | null): this {
if (options === null) {
Reflect.set(this, 'author', undefined);
return this;
}
const { name, iconURL, url } = options;
// Data assertions
authorNamePredicate.parse(name);
urlPredicate.parse(iconURL);
urlPredicate.parse(url);
Reflect.set(this, 'author', { name, url, icon_url: iconURL });
return this;
}
/**
* Sets the color of this embed
*
* @param color The color of the embed
*/
public setColor(color: number | null): this {
// Data assertions
colorPredicate.parse(color);
Reflect.set(this, 'color', color ?? undefined);
return this;
}
/**
* Sets the description of this embed
*
* @param description The description
*/
public setDescription(description: string | null): this {
// Data assertions
descriptionPredicate.parse(description);
Reflect.set(this, 'description', description ?? undefined);
return this;
}
/**
* Sets the footer of this embed
*
* @param options The options for the footer
*/
public setFooter(options: FooterOptions | null): this {
if (options === null) {
Reflect.set(this, 'footer', undefined);
return this;
}
const { text, iconURL } = options;
// Data assertions
footerTextPredicate.parse(text);
urlPredicate.parse(iconURL);
Reflect.set(this, 'footer', { text, icon_url: iconURL });
return this;
}
/**
* Sets the image of this embed
*
* @param url The URL of the image
*/
public setImage(url: string | null): this {
// Data assertions
urlPredicate.parse(url);
Reflect.set(this, 'image', url ? { url } : undefined);
return this;
}
/**
* Sets the thumbnail of this embed
*
* @param url The URL of the thumbnail
*/
public setThumbnail(url: string | null): this {
// Data assertions
urlPredicate.parse(url);
Reflect.set(this, 'thumbnail', url ? { url } : undefined);
return this;
}
/**
* Sets the timestamp of this embed
*
* @param timestamp The timestamp or date
*/
public setTimestamp(timestamp: number | Date | null = Date.now()): this {
// Data assertions
timestampPredicate.parse(timestamp);
Reflect.set(this, 'timestamp', timestamp ? new Date(timestamp).toISOString() : undefined);
return this;
}
/**
* Sets the title of this embed
*
* @param title The title
*/
public setTitle(title: string | null): this {
// Data assertions
titlePredicate.parse(title);
Reflect.set(this, 'title', title ?? undefined);
return this;
}
/**
* Sets the URL of this embed
*
* @param url The URL
*/
public setURL(url: string | null): this {
// Data assertions
urlPredicate.parse(url);
Reflect.set(this, 'url', url ?? undefined);
return this;
}
/**
* Transforms the embed to a plain object
*/
public toJSON(): APIEmbed {
return { ...this };
}
/**
* Normalizes field input and resolves strings
*
* @param fields Fields to normalize
*/
public static normalizeFields(...fields: APIEmbedField[]): APIEmbedField[] {
return fields.flat(Infinity).map((field) => {
fieldNamePredicate.parse(field.name);
fieldValuePredicate.parse(field.value);
fieldInlinePredicate.parse(field.inline);
return { name: field.name, value: field.value, inline: field.inline ?? undefined };
});
}
}