This repository has been archived by the owner on Jan 8, 2022. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: DTrombett <[email protected]> Co-authored-by: Antonio Román <[email protected]> Co-authored-by: Sugden <[email protected]> Co-authored-by: Vlad Frangu <[email protected]>
- Loading branch information
1 parent
5d53759
commit eb942a4
Showing
5 changed files
with
780 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import type { APIEmbedField } from 'discord-api-types/v9'; | ||
import ow from 'ow'; | ||
|
||
export const fieldNamePredicate = ow.string.minLength(1).maxLength(256); | ||
|
||
export const fieldValuePredicate = ow.string.minLength(1).maxLength(1024); | ||
|
||
export const fieldInlinePredicate = ow.optional.boolean; | ||
|
||
export const embedFieldPredicate = ow.object.exactShape({ | ||
name: fieldNamePredicate, | ||
value: fieldValuePredicate, | ||
inline: fieldInlinePredicate, | ||
}); | ||
|
||
export const embedFieldsArrayPredicate = ow.array.ofType(embedFieldPredicate); | ||
|
||
export function validateFieldLength(fields: APIEmbedField[], amountAdding: number): void { | ||
ow(fields.length + amountAdding, 'field amount', ow.number.lessThanOrEqual(25)); | ||
} | ||
|
||
export const authorNamePredicate = ow.any(fieldNamePredicate, ow.null); | ||
|
||
export const urlPredicate = ow.any(ow.string.url, ow.nullOrUndefined); | ||
|
||
export const colorPredicate = ow.any(ow.number.greaterThanOrEqual(0).lessThanOrEqual(0xffffff), ow.null); | ||
|
||
export const descriptionPredicate = ow.any(ow.string.minLength(1).maxLength(4096), ow.null); | ||
|
||
export const footerTextPredicate = ow.any(ow.string.minLength(1).maxLength(2048), ow.null); | ||
|
||
export const timestampPredicate = ow.any(ow.number, ow.date, ow.null); | ||
|
||
export const titlePredicate = ow.any(fieldNamePredicate, ow.null); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,314 @@ | ||
import type { | ||
APIEmbed, | ||
APIEmbedAuthor, | ||
APIEmbedField, | ||
APIEmbedFooter, | ||
APIEmbedImage, | ||
APIEmbedProvider, | ||
APIEmbedThumbnail, | ||
APIEmbedVideo, | ||
} from 'discord-api-types/v9'; | ||
import ow from 'ow'; | ||
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 fields: APIEmbedField[]; | ||
|
||
/** | ||
* The embed title. | ||
*/ | ||
public title?: string; | ||
|
||
/** | ||
* The embed description. | ||
*/ | ||
public description?: string; | ||
|
||
/** | ||
* The embed url. | ||
*/ | ||
public url?: string; | ||
|
||
/** | ||
* The embed color. | ||
*/ | ||
public color?: number; | ||
|
||
/** | ||
* The timestamp of the embed in the ISO format. | ||
*/ | ||
public timestamp?: string; | ||
|
||
/** | ||
* The embed thumbnail data. | ||
*/ | ||
public thumbnail?: APIEmbedThumbnail; | ||
|
||
/** | ||
* The embed image data. | ||
*/ | ||
public image?: APIEmbedImage; | ||
|
||
/** | ||
* Received video data. | ||
*/ | ||
public video?: APIEmbedVideo; | ||
|
||
/** | ||
* The embed author data. | ||
*/ | ||
public author?: APIEmbedAuthor; | ||
|
||
/** | ||
* Received data about the embed provider. | ||
*/ | ||
public provider?: APIEmbedProvider; | ||
|
||
/** | ||
* The embed footer data. | ||
*/ | ||
public 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 | ||
ow(fields, 'fields', embedFieldsArrayPredicate); | ||
|
||
// 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 | ||
ow(fields, 'fields', embedFieldsArrayPredicate); | ||
|
||
// 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 author of this embed. | ||
* @param options The options for the author. | ||
*/ | ||
public setAuthor(options: AuthorOptions | null): this { | ||
if (options === null) { | ||
this.author = undefined; | ||
return this; | ||
} | ||
|
||
const { name, iconURL, url } = options; | ||
// Data assertions | ||
ow(name, 'name', authorNamePredicate); | ||
ow(iconURL, 'iconURL', urlPredicate); | ||
ow(url, 'url', urlPredicate); | ||
|
||
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 | ||
ow(color, 'color', colorPredicate); | ||
|
||
this.color = color ?? undefined; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets the description of this embed. | ||
* @param description The description. | ||
*/ | ||
public setDescription(description: string | null): this { | ||
// Data assertions | ||
ow(description, 'description', descriptionPredicate); | ||
|
||
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) { | ||
this.footer = undefined; | ||
return this; | ||
} | ||
|
||
const { text, iconURL } = options; | ||
// Data assertions | ||
ow(text, 'text', footerTextPredicate); | ||
ow(iconURL, 'iconURL', urlPredicate); | ||
|
||
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 | ||
ow(url, 'url', urlPredicate); | ||
|
||
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 | ||
ow(url, 'url', urlPredicate); | ||
|
||
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 | ||
ow(timestamp, 'timestamp', timestampPredicate); | ||
|
||
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 | ||
ow(title, 'title', titlePredicate); | ||
|
||
this.title = title ?? undefined; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets the URL of this embed. | ||
* @param url The URL. | ||
*/ | ||
public setURL(url: string | null): this { | ||
// Data assertions | ||
ow(url, 'url', urlPredicate); | ||
|
||
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) => { | ||
ow(field.name, 'field name', fieldNamePredicate); | ||
ow(field.value, 'field value', fieldValuePredicate); | ||
ow(field.inline, 'field inline', fieldInlinePredicate); | ||
|
||
return { name: field.name, value: field.value, inline: field.inline ?? undefined }; | ||
}); | ||
} | ||
} |
Oops, something went wrong.