WebEmbed test

This commit is contained in:
March 7th 2022-04-01 12:09:46 +07:00
parent 69bc495e81
commit 42911f72b1
5 changed files with 533 additions and 123 deletions

View File

@ -285,6 +285,53 @@ await message.contextMenu(botID, commandName);
- Credit: [Here](https://www.reddit.com/r/Discord_selfbots/comments/tczprx/discum_help_creating_a_selfbot_that_can_do_ping/)
</details>
## MessageEmbed ?
- Because Discord has removed the ability to send Embeds in its API, that means MessageEmbed is unusable. But I have created a constructor that uses oEmbed from [page](https://embed.benny.fun/) (known to this site by module py discord.py-self_embed, thanks)
<details>
<summary><strong>Click to show</strong></summary>
Code:
```js
const Discord = require('discord.js-selfbot-v13');
const w = new Discord.WebEmbed()
.setAuthor({ name: 'hello', url: 'https://google.com' })
.setColor('RED')
.setDescription('description uh')
.setProvider({ name: 'provider', url: 'https://google.com' })
.setTitle('This is Title')
.setImage(
'https://cdn.discordapp.com/attachments/820557032016969751/959093026695835648/unknown.png',
)
.setVideo(
'https://cdn.discordapp.com/attachments/877060758092021801/957691816143097936/The_Quintessential_Quintuplets_And_Rick_Astley_Autotune_Remix.mp4',
);
/**
* w.toMessage(hidden: true | false, shorten: true | false) => Promise<string>
*/
// Normal mode (Auto shorten)
message.channel.send({ content: `${await w.toMessage()}` })
// Normal mode (Not shorten)
message.channel.send({ content: `${await w.toMessage(false, false)}` })
// Hidden mode (with shorten)
message.channel.send({ content: `${await w.toMessage(true, true)}` })
// Hidden mode (no shorten)
message.channel.send({ content: `${await w.toMessage(true, false)}` })
// Custom content + Shorten + Hidden
message.channel.send({ content: `Hello world ${await w.toMessage(true, true)}` })
// etc ...
```
### Features & Issues
- No Timestamp, Footer, Thumbnail (but embed video, thumbnail working), Fields, Author iconURL
- Video with Embed working
- Description limit 350 characters
- If you use hidden mode you must make sure your custom content is less than 1000 characters without nitro (because hidden mode uses 1000 characters + URL)
</details>
## User & ClientUser Method
<details>
<summary>Click to show</summary>

View File

@ -3,165 +3,203 @@
const { register } = require('./DJSError');
const Messages = {
CLIENT_INVALID_OPTION: (prop, must) => `The ${prop} option must be ${must}`,
CLIENT_INVALID_PROVIDED_SHARDS: 'None of the provided shards were valid.',
CLIENT_MISSING_INTENTS: 'Valid intents must be provided for the Client.',
CLIENT_NOT_READY: action => `The client needs to be logged in to ${action}.`,
CLIENT_INVALID_OPTION: (prop, must) => `The ${prop} option must be ${must}`,
CLIENT_INVALID_PROVIDED_SHARDS: 'None of the provided shards were valid.',
CLIENT_MISSING_INTENTS: 'Valid intents must be provided for the Client.',
CLIENT_NOT_READY: (action) =>
`The client needs to be logged in to ${action}.`,
TOKEN_INVALID: 'An invalid token was provided.',
TOKEN_MISSING: 'Request to use token, but token was unavailable to the client.',
TOKEN_INVALID: 'An invalid token was provided.',
TOKEN_MISSING:
'Request to use token, but token was unavailable to the client.',
WS_CLOSE_REQUESTED: 'WebSocket closed due to user request.',
WS_CONNECTION_EXISTS: 'There is already an existing WebSocket connection.',
WS_NOT_OPEN: (data = 'data') => `WebSocket not open to send ${data}`,
MANAGER_DESTROYED: 'Manager was destroyed.',
WS_CLOSE_REQUESTED: 'WebSocket closed due to user request.',
WS_CONNECTION_EXISTS: 'There is already an existing WebSocket connection.',
WS_NOT_OPEN: (data = 'data') => `WebSocket not open to send ${data}`,
MANAGER_DESTROYED: 'Manager was destroyed.',
BITFIELD_INVALID: bit => `Invalid bitfield flag or number: ${bit}.`,
BITFIELD_INVALID: (bit) => `Invalid bitfield flag or number: ${bit}.`,
SHARDING_INVALID: 'Invalid shard settings were provided.',
SHARDING_REQUIRED: 'This session would have handled too many guilds - Sharding is required.',
INVALID_INTENTS: 'Invalid intent provided for WebSocket intents.',
DISALLOWED_INTENTS: 'Privileged intent provided is not enabled or whitelisted.',
SHARDING_NO_SHARDS: 'No shards have been spawned.',
SHARDING_IN_PROCESS: 'Shards are still being spawned.',
SHARDING_INVALID_EVAL_BROADCAST: 'Script to evaluate must be a function',
SHARDING_SHARD_NOT_FOUND: id => `Shard ${id} could not be found.`,
SHARDING_ALREADY_SPAWNED: count => `Already spawned ${count} shards.`,
SHARDING_PROCESS_EXISTS: id => `Shard ${id} already has an active process.`,
SHARDING_WORKER_EXISTS: id => `Shard ${id} already has an active worker.`,
SHARDING_READY_TIMEOUT: id => `Shard ${id}'s Client took too long to become ready.`,
SHARDING_READY_DISCONNECTED: id => `Shard ${id}'s Client disconnected before becoming ready.`,
SHARDING_READY_DIED: id => `Shard ${id}'s process exited before its Client became ready.`,
SHARDING_NO_CHILD_EXISTS: id => `Shard ${id} has no active process or worker.`,
SHARDING_SHARD_MISCALCULATION: (shard, guild, count) =>
`Calculated invalid shard ${shard} for guild ${guild} with ${count} shards.`,
SHARDING_INVALID: 'Invalid shard settings were provided.',
SHARDING_REQUIRED:
'This session would have handled too many guilds - Sharding is required.',
INVALID_INTENTS: 'Invalid intent provided for WebSocket intents.',
DISALLOWED_INTENTS:
'Privileged intent provided is not enabled or whitelisted.',
SHARDING_NO_SHARDS: 'No shards have been spawned.',
SHARDING_IN_PROCESS: 'Shards are still being spawned.',
SHARDING_INVALID_EVAL_BROADCAST: 'Script to evaluate must be a function',
SHARDING_SHARD_NOT_FOUND: (id) => `Shard ${id} could not be found.`,
SHARDING_ALREADY_SPAWNED: (count) => `Already spawned ${count} shards.`,
SHARDING_PROCESS_EXISTS: (id) => `Shard ${id} already has an active process.`,
SHARDING_WORKER_EXISTS: (id) => `Shard ${id} already has an active worker.`,
SHARDING_READY_TIMEOUT: (id) =>
`Shard ${id}'s Client took too long to become ready.`,
SHARDING_READY_DISCONNECTED: (id) =>
`Shard ${id}'s Client disconnected before becoming ready.`,
SHARDING_READY_DIED: (id) =>
`Shard ${id}'s process exited before its Client became ready.`,
SHARDING_NO_CHILD_EXISTS: (id) =>
`Shard ${id} has no active process or worker.`,
SHARDING_SHARD_MISCALCULATION: (shard, guild, count) =>
`Calculated invalid shard ${shard} for guild ${guild} with ${count} shards.`,
COLOR_RANGE: 'Color must be within the range 0 - 16777215 (0xFFFFFF).',
COLOR_CONVERT: 'Unable to convert color to a number.',
COLOR_RANGE: 'Color must be within the range 0 - 16777215 (0xFFFFFF).',
COLOR_CONVERT: 'Unable to convert color to a number.',
INVITE_OPTIONS_MISSING_CHANNEL: 'A valid guild channel must be provided when GuildScheduledEvent is EXTERNAL.',
INVITE_OPTIONS_MISSING_CHANNEL:
'A valid guild channel must be provided when GuildScheduledEvent is EXTERNAL.',
EMBED_TITLE: 'MessageEmbed title must be a string.',
EMBED_FIELD_NAME: 'MessageEmbed field names must be non-empty strings.',
EMBED_FIELD_VALUE: 'MessageEmbed field values must be non-empty strings.',
EMBED_FOOTER_TEXT: 'MessageEmbed footer text must be a string.',
EMBED_DESCRIPTION: 'MessageEmbed description must be a string.',
EMBED_AUTHOR_NAME: 'MessageEmbed author name must be a string.',
EMBED_TITLE: 'MessageEmbed title must be a string.',
EMBED_FIELD_NAME: 'MessageEmbed field names must be non-empty strings.',
EMBED_FIELD_VALUE: 'MessageEmbed field values must be non-empty strings.',
EMBED_FOOTER_TEXT: 'MessageEmbed footer text must be a string.',
EMBED_DESCRIPTION: 'MessageEmbed description must be a string.',
EMBED_AUTHOR_NAME: 'MessageEmbed author name must be a string.',
/* add */
EMBED_PROVIDER_NAME: 'MessageEmbed provider name must be a string.',
BUTTON_LABEL: 'MessageButton label must be a string',
BUTTON_URL: 'MessageButton URL must be a string',
BUTTON_CUSTOM_ID: 'MessageButton customId must be a string',
BUTTON_LABEL: 'MessageButton label must be a string',
BUTTON_URL: 'MessageButton URL must be a string',
BUTTON_CUSTOM_ID: 'MessageButton customId must be a string',
SELECT_MENU_CUSTOM_ID: 'MessageSelectMenu customId must be a string',
SELECT_MENU_PLACEHOLDER: 'MessageSelectMenu placeholder must be a string',
SELECT_OPTION_LABEL: 'MessageSelectOption label must be a string',
SELECT_OPTION_VALUE: 'MessageSelectOption value must be a string',
SELECT_OPTION_DESCRIPTION: 'MessageSelectOption description must be a string',
SELECT_MENU_CUSTOM_ID: 'MessageSelectMenu customId must be a string',
SELECT_MENU_PLACEHOLDER: 'MessageSelectMenu placeholder must be a string',
SELECT_OPTION_LABEL: 'MessageSelectOption label must be a string',
SELECT_OPTION_VALUE: 'MessageSelectOption value must be a string',
SELECT_OPTION_DESCRIPTION: 'MessageSelectOption description must be a string',
INTERACTION_COLLECTOR_ERROR: reason => `Collector received no interactions before ending with reason: ${reason}`,
INTERACTION_COLLECTOR_ERROR: (reason) =>
`Collector received no interactions before ending with reason: ${reason}`,
FILE_NOT_FOUND: file => `File could not be found: ${file}`,
FILE_NOT_FOUND: (file) => `File could not be found: ${file}`,
USER_BANNER_NOT_FETCHED: "You must fetch this user's banner before trying to generate its URL!",
USER_NO_DM_CHANNEL: 'No DM Channel exists!',
USER_BANNER_NOT_FETCHED:
"You must fetch this user's banner before trying to generate its URL!",
USER_NO_DM_CHANNEL: 'No DM Channel exists!',
VOICE_NOT_STAGE_CHANNEL: 'You are only allowed to do this in stage channels.',
VOICE_NOT_STAGE_CHANNEL: 'You are only allowed to do this in stage channels.',
VOICE_STATE_NOT_OWN:
'You cannot self-deafen/mute/request to speak on VoiceStates that do not belong to the ClientUser.',
VOICE_STATE_INVALID_TYPE: name => `${name} must be a boolean.`,
VOICE_STATE_NOT_OWN:
'You cannot self-deafen/mute/request to speak on VoiceStates that do not belong to the ClientUser.',
VOICE_STATE_INVALID_TYPE: (name) => `${name} must be a boolean.`,
REQ_RESOURCE_TYPE: 'The resource must be a string, Buffer or a valid file stream.',
REQ_RESOURCE_TYPE:
'The resource must be a string, Buffer or a valid file stream.',
IMAGE_FORMAT: format => `Invalid image format: ${format}`,
IMAGE_SIZE: size => `Invalid image size: ${size}`,
IMAGE_FORMAT: (format) => `Invalid image format: ${format}`,
IMAGE_SIZE: (size) => `Invalid image size: ${size}`,
MESSAGE_BULK_DELETE_TYPE: 'The messages must be an Array, Collection, or number.',
MESSAGE_NONCE_TYPE: 'Message nonce must be an integer or a string.',
MESSAGE_CONTENT_TYPE: 'Message content must be a non-empty string.',
MESSAGE_BULK_DELETE_TYPE:
'The messages must be an Array, Collection, or number.',
MESSAGE_NONCE_TYPE: 'Message nonce must be an integer or a string.',
MESSAGE_CONTENT_TYPE: 'Message content must be a non-empty string.',
SPLIT_MAX_LEN: 'Chunk exceeds the max length and contains no split characters.',
SPLIT_MAX_LEN:
'Chunk exceeds the max length and contains no split characters.',
BAN_RESOLVE_ID: (ban = false) => `Couldn't resolve the user id to ${ban ? 'ban' : 'unban'}.`,
FETCH_BAN_RESOLVE_ID: "Couldn't resolve the user id to fetch the ban.",
BAN_RESOLVE_ID: (ban = false) =>
`Couldn't resolve the user id to ${ban ? 'ban' : 'unban'}.`,
FETCH_BAN_RESOLVE_ID: "Couldn't resolve the user id to fetch the ban.",
PRUNE_DAYS_TYPE: 'Days must be a number',
PRUNE_DAYS_TYPE: 'Days must be a number',
GUILD_CHANNEL_RESOLVE: 'Could not resolve channel to a guild channel.',
GUILD_VOICE_CHANNEL_RESOLVE: 'Could not resolve channel to a guild voice channel.',
GUILD_CHANNEL_ORPHAN: 'Could not find a parent to this guild channel.',
GUILD_CHANNEL_UNOWNED: "The fetched channel does not belong to this manager's guild.",
GUILD_OWNED: 'Guild is owned by the client.',
GUILD_MEMBERS_TIMEOUT: "Members didn't arrive in time.",
GUILD_UNCACHED_ME: 'The client user as a member of this guild is uncached.',
CHANNEL_NOT_CACHED: 'Could not find the channel where this message came from in the cache!',
STAGE_CHANNEL_RESOLVE: 'Could not resolve channel to a stage channel.',
GUILD_SCHEDULED_EVENT_RESOLVE: 'Could not resolve the guild scheduled event.',
GUILD_CHANNEL_RESOLVE: 'Could not resolve channel to a guild channel.',
GUILD_VOICE_CHANNEL_RESOLVE:
'Could not resolve channel to a guild voice channel.',
GUILD_CHANNEL_ORPHAN: 'Could not find a parent to this guild channel.',
GUILD_CHANNEL_UNOWNED:
"The fetched channel does not belong to this manager's guild.",
GUILD_OWNED: 'Guild is owned by the client.',
GUILD_MEMBERS_TIMEOUT: "Members didn't arrive in time.",
GUILD_UNCACHED_ME: 'The client user as a member of this guild is uncached.',
CHANNEL_NOT_CACHED:
'Could not find the channel where this message came from in the cache!',
STAGE_CHANNEL_RESOLVE: 'Could not resolve channel to a stage channel.',
GUILD_SCHEDULED_EVENT_RESOLVE: 'Could not resolve the guild scheduled event.',
INVALID_TYPE: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`,
INVALID_ELEMENT: (type, name, elem) => `Supplied ${type} ${name} includes an invalid element: ${elem}`,
INVALID_TYPE: (name, expected, an = false) =>
`Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`,
INVALID_ELEMENT: (type, name, elem) =>
`Supplied ${type} ${name} includes an invalid element: ${elem}`,
MESSAGE_THREAD_PARENT: 'The message was not sent in a guild text or news channel',
MESSAGE_EXISTING_THREAD: 'The message already has a thread',
THREAD_INVITABLE_TYPE: type => `Invitable cannot be edited on ${type}`,
MESSAGE_THREAD_PARENT:
'The message was not sent in a guild text or news channel',
MESSAGE_EXISTING_THREAD: 'The message already has a thread',
THREAD_INVITABLE_TYPE: (type) => `Invitable cannot be edited on ${type}`,
WEBHOOK_MESSAGE: 'The message was not sent by a webhook.',
WEBHOOK_TOKEN_UNAVAILABLE: 'This action requires a webhook token, but none is available.',
WEBHOOK_URL_INVALID: 'The provided webhook URL is not valid.',
WEBHOOK_APPLICATION: 'This message webhook belongs to an application and cannot be fetched.',
MESSAGE_REFERENCE_MISSING: 'The message does not reference another message',
WEBHOOK_MESSAGE: 'The message was not sent by a webhook.',
WEBHOOK_TOKEN_UNAVAILABLE:
'This action requires a webhook token, but none is available.',
WEBHOOK_URL_INVALID: 'The provided webhook URL is not valid.',
WEBHOOK_APPLICATION:
'This message webhook belongs to an application and cannot be fetched.',
MESSAGE_REFERENCE_MISSING: 'The message does not reference another message',
EMOJI_TYPE: 'Emoji must be a string or GuildEmoji/ReactionEmoji',
EMOJI_MANAGED: 'Emoji is managed and has no Author.',
MISSING_MANAGE_EMOJIS_AND_STICKERS_PERMISSION: guild =>
`Client must have Manage Emojis and Stickers permission in guild ${guild} to see emoji authors.`,
NOT_GUILD_STICKER: 'Sticker is a standard (non-guild) sticker and has no author.',
EMOJI_TYPE: 'Emoji must be a string or GuildEmoji/ReactionEmoji',
EMOJI_MANAGED: 'Emoji is managed and has no Author.',
MISSING_MANAGE_EMOJIS_AND_STICKERS_PERMISSION: (guild) =>
`Client must have Manage Emojis and Stickers permission in guild ${guild} to see emoji authors.`,
NOT_GUILD_STICKER:
'Sticker is a standard (non-guild) sticker and has no author.',
REACTION_RESOLVE_USER: "Couldn't resolve the user id to remove from the reaction.",
REACTION_RESOLVE_USER:
"Couldn't resolve the user id to remove from the reaction.",
VANITY_URL: 'This guild does not have the VANITY_URL feature enabled.',
VANITY_URL: 'This guild does not have the VANITY_URL feature enabled.',
INVITE_RESOLVE_CODE: 'Could not resolve the code to fetch the invite.',
INVITE_RESOLVE_CODE: 'Could not resolve the code to fetch the invite.',
INVITE_NOT_FOUND: 'Could not find the requested invite.',
INVITE_NOT_FOUND: 'Could not find the requested invite.',
DELETE_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot delete them",
FETCH_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot fetch them",
DELETE_GROUP_DM_CHANNEL:
"Bots don't have access to Group DM Channels and cannot delete them",
FETCH_GROUP_DM_CHANNEL:
"Bots don't have access to Group DM Channels and cannot fetch them",
MEMBER_FETCH_NONCE_LENGTH: 'Nonce length must not exceed 32 characters.',
MEMBER_FETCH_NONCE_LENGTH: 'Nonce length must not exceed 32 characters.',
GLOBAL_COMMAND_PERMISSIONS:
'Permissions for global commands may only be fetched or modified by providing a GuildResolvable ' +
"or from a guild's application command manager.",
GUILD_UNCACHED_ROLE_RESOLVE: 'Cannot resolve roles from an arbitrary guild, provide an id instead',
GLOBAL_COMMAND_PERMISSIONS:
'Permissions for global commands may only be fetched or modified by providing a GuildResolvable ' +
"or from a guild's application command manager.",
GUILD_UNCACHED_ROLE_RESOLVE:
'Cannot resolve roles from an arbitrary guild, provide an id instead',
INTERACTION_ALREADY_REPLIED: 'The reply to this interaction has already been sent or deferred.',
INTERACTION_NOT_REPLIED: 'The reply to this interaction has not been sent or deferred.',
INTERACTION_EPHEMERAL_REPLIED: 'Ephemeral responses cannot be deleted.',
INTERACTION_ALREADY_REPLIED:
'The reply to this interaction has already been sent or deferred.',
INTERACTION_NOT_REPLIED:
'The reply to this interaction has not been sent or deferred.',
INTERACTION_EPHEMERAL_REPLIED: 'Ephemeral responses cannot be deleted.',
COMMAND_INTERACTION_OPTION_NOT_FOUND: name => `Required option "${name}" not found.`,
COMMAND_INTERACTION_OPTION_TYPE: (name, type, expected) =>
`Option "${name}" is of type: ${type}; expected ${expected}.`,
COMMAND_INTERACTION_OPTION_EMPTY: (name, type) =>
`Required option "${name}" is of type: ${type}; expected a non-empty value.`,
COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND: 'No subcommand specified for interaction.',
COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND_GROUP: 'No subcommand group specified for interaction.',
AUTOCOMPLETE_INTERACTION_OPTION_NO_FOCUSED_OPTION: 'No focused option for autocomplete interaction.',
COMMAND_INTERACTION_OPTION_NOT_FOUND: (name) =>
`Required option "${name}" not found.`,
COMMAND_INTERACTION_OPTION_TYPE: (name, type, expected) =>
`Option "${name}" is of type: ${type}; expected ${expected}.`,
COMMAND_INTERACTION_OPTION_EMPTY: (name, type) =>
`Required option "${name}" is of type: ${type}; expected a non-empty value.`,
COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND:
'No subcommand specified for interaction.',
COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND_GROUP:
'No subcommand group specified for interaction.',
AUTOCOMPLETE_INTERACTION_OPTION_NO_FOCUSED_OPTION:
'No focused option for autocomplete interaction.',
INVITE_MISSING_SCOPES: 'At least one valid scope must be provided for the invite',
INVITE_MISSING_SCOPES:
'At least one valid scope must be provided for the invite',
NOT_IMPLEMENTED: (what, name) => `Method ${what} not implemented on ${name}.`,
NOT_IMPLEMENTED: (what, name) => `Method ${what} not implemented on ${name}.`,
SWEEP_FILTER_RETURN: 'The return value of the sweepFilter function was not false or a Function',
SWEEP_FILTER_RETURN:
'The return value of the sweepFilter function was not false or a Function',
INVALID_BOT_METHOD: `Bot accounts cannot use this method`,
INVALID_USER_METHOD: `User accounts cannot use this method`,
INVALID_LOCALE: 'Unable to select this location',
FOLDER_NOT_FOUND: 'Server directory not found',
FOLDER_POSITION_INVALID: 'The server index in the directory is invalid',
APPLICATION_ID_INVALID: 'The application isn\'t BOT',
INVALID_NITRO: 'Invalid Nitro Code',
MESSAGE_ID_NOT_FOUND: 'Message ID not found',
INVALID_BOT_METHOD: `Bot accounts cannot use this method`,
INVALID_USER_METHOD: `User accounts cannot use this method`,
INVALID_LOCALE: 'Unable to select this location',
FOLDER_NOT_FOUND: 'Server directory not found',
FOLDER_POSITION_INVALID: 'The server index in the directory is invalid',
APPLICATION_ID_INVALID: "The application isn't BOT",
INVALID_NITRO: 'Invalid Nitro Code',
MESSAGE_ID_NOT_FOUND: 'Message ID not found',
};
for (const [name, message] of Object.entries(Messages)) register(name, message);

View File

@ -118,6 +118,7 @@ exports.MessageCollector = require('./structures/MessageCollector');
exports.MessageComponentInteraction = require('./structures/MessageComponentInteraction');
exports.MessageContextMenuInteraction = require('./structures/MessageContextMenuInteraction');
exports.MessageEmbed = require('./structures/MessageEmbed');
exports.WebEmbed = require('./structures/WebEmbed');
exports.MessageMentions = require('./structures/MessageMentions');
exports.MessagePayload = require('./structures/MessagePayload');
exports.MessageReaction = require('./structures/MessageReaction');
@ -153,5 +154,4 @@ exports.Widget = require('./structures/Widget');
exports.WidgetMember = require('./structures/WidgetMember');
exports.WelcomeChannel = require('./structures/WelcomeChannel');
exports.WelcomeScreen = require('./structures/WelcomeScreen');
exports.WebSocket = require('./WebSocket');
exports.WebSocket = require('./WebSocket');

303
src/structures/WebEmbed.js Normal file
View File

@ -0,0 +1,303 @@
'use strict';
const axios = require('axios');
const baseURL = 'https://embed.benny.fun/?';
const hiddenCharter = '|'.repeat(1000);
const { RangeError } = require('../errors');
const Util = require('../util/Util');
class WebEmbed {
constructor(data) {
this._setup(data);
}
_setup(data) {
/**
* The title of this embed
* @type {?string}
*/
this.title = data.title ?? null;
/**
* The description of this embed
* @type {?string}
*/
this.description = data.description ?? null;
/**
* The URL of this embed
* @type {?string}
*/
this.url = data.url ?? null;
/**
* The color of this embed
* @type {?number}
*/
this.color = 'color' in data ? Util.resolveColor(data.color) : null;
/**
* Represents the image of a MessageEmbed
* @typedef {Object} MessageEmbedImage
* @property {string} url URL for this image
* @property {string} proxyURL ProxyURL for this image
* @property {number} height Height of this image
* @property {number} width Width of this image
*/
/**
* The image of this embed, if there is one
* @type {?MessageEmbedImage}
*/
this.image = data.image
? {
url: data.image.url,
proxyURL: data.image.proxyURL ?? data.image.proxy_url,
height: data.image.height,
width: data.image.width,
}
: null;
/**
* Represents the video of a MessageEmbed
* @typedef {Object} MessageEmbedVideo
* @property {string} url URL of this video
* @property {string} proxyURL ProxyURL for this video
* @property {number} height Height of this video
* @property {number} width Width of this video
*/
/**
* The video of this embed (if there is one)
* @type {?MessageEmbedVideo}
* @readonly
*/
this.video = data.video
? {
url: data.video.url,
proxyURL: data.video.proxyURL ?? data.video.proxy_url,
height: data.video.height,
width: data.video.width,
}
: null;
/**
* Represents the author field of a MessageEmbed
* @typedef {Object} MessageEmbedAuthor
* @property {string} name The name of this author
* @property {string} url URL of this author
* @property {string} iconURL URL of the icon for this author
* @property {string} proxyIconURL Proxied URL of the icon for this author
*/
/**
* The author of this embed (if there is one)
* @type {?MessageEmbedAuthor}
*/
this.author = data.author
? {
name: data.author.name,
url: data.author.url,
}
: null;
/**
* Represents the provider of a MessageEmbed
* @typedef {Object} MessageEmbedProvider
* @property {string} name The name of this provider
* @property {string} url URL of this provider
*/
/**
* The provider of this embed (if there is one)
* @type {?MessageEmbedProvider}
*/
this.provider = data.provider
? {
name: data.provider.name,
url: data.provider.name,
}
: null;
}
/**
* The options to provide for setting an author for a {@link MessageEmbed}.
* @typedef {Object} EmbedAuthorData
* @property {string} name The name of this author.
*/
/**
* Sets the author of this embed.
* @param {string|EmbedAuthorData|null} options The options to provide for the author.
* Provide `null` to remove the author data.
* @returns {MessageEmbed}
*/
setAuthor(options) {
if (options === null) {
this.author = {};
return this;
}
const { name, url } = options;
this.author = {
name: Util.verifyString(name, RangeError, 'EMBED_AUTHOR_NAME'),
url,
};
return this;
}
/**
* The options to provide for setting an provider for a {@link MessageEmbed}.
* @typedef {Object} EmbedProviderData
* @property {string} name The name of this provider.
*/
/**
* Sets the provider of this embed.
* @param {string|EmbedProviderData|null} options The options to provide for the provider.
* Provide `null` to remove the provider data.
* @returns {MessageEmbed}
*/
setProvider(options) {
if (options === null) {
this.provider = {};
return this;
}
const { name, url } = options;
this.provider = {
name: Util.verifyString(name, RangeError, 'EMBED_PROVIDER_NAME'),
url,
};
return this;
}
/**
* Sets the color of this embed.
* @param {ColorResolvable} color The color of the embed
* @returns {MessageEmbed}
*/
setColor(color) {
this.color = Util.resolveColor(color);
return this;
}
/**
* Sets the description of this embed.
* @param {string} description The description (Limit 350 characters)
* @returns {MessageEmbed}
*/
setDescription(description) {
this.description = Util.verifyString(
description,
RangeError,
'EMBED_DESCRIPTION',
);
return this;
}
/**
* Sets the image of this embed.
* @param {string} url The URL of the image
* @returns {MessageEmbed}
*/
setImage(url) {
this.image = { url };
return this;
}
/**
* Sets the video of this embed.
* @param {string} url The URL of the video
* @returns {MessageEmbed}
*/
setVideo(url) {
this.video = { url };
return this;
}
/**
* Sets the title of this embed.
* @param {string} title The title
* @returns {MessageEmbed}
*/
setTitle(title) {
this.title = Util.verifyString(title, RangeError, 'EMBED_TITLE');
return this;
}
/**
* Sets the URL of this embed.
* @param {string} url The URL
* @returns {MessageEmbed}
*/
setURL(url) {
this.url = url;
return this;
}
/**
* Return Message Content + Embed (if hidden, pls check content length because it has 1000+ length)
* @param {boolean} hidden Hidden Embed link
* @param {boolean} shorten Shorten link ?
* @returns {string} Message Content
*/
async toMessage(hidden = false, shorten = true) {
const arrayQuery = [];
if (this.title) {
arrayQuery.push(`title=${this.title}`);
}
if (this.description) {
arrayQuery.push(`description=${this.description}`);
}
if (this.url) {
arrayQuery.push(`url=${encodeURIComponent(this.url)}`);
}
if (this.color) {
arrayQuery.push(
`colour=${encodeURIComponent('#' + this.color.toString(16))}`,
);
}
if (this.image?.url) {
arrayQuery.push(`image=${encodeURIComponent(this.image.url)}`);
}
if (this.video?.url) {
arrayQuery.push(`video=${encodeURIComponent(this.video.url)}`);
}
if (this.author) {
if (this.author.name) arrayQuery.push(
`author_name=${this.author.name}`,
);
if (this.author.url) arrayQuery.push(
`author_url=${encodeURIComponent(this.author.url)}`,
);
}
if (this.provider) {
if (this.provider.name) arrayQuery.push(
`provider_name=${this.provider.name}`,
);
if (this.provider.url) arrayQuery.push(
`provider_url=${encodeURIComponent(this.provider.url)}`,
);
}
const fullURL = `${baseURL}${arrayQuery.join('&')}`;
if (shorten) {
const url = await getShorten(fullURL);
if (!url) console.log('Cannot shorten URL in WebEmbed');
return hidden ? `${hiddenCharter} ${url || fullURL}` : (url || fullURL);
} else {
return hidden ? `${hiddenCharter} ${fullURL}` : fullURL;
}
}
}
// API by Shiraori#1782 (me)
const getShorten = async (url) => {
// Please not using this API, it's hosting in Heroku, very slow
try {
const res = await axios
.post('https://sagiri-fansub.tk/api/v1/embed', {
url,
})
return `https://sagiri-fansub.tk/api/v1/embed/${res.data.path}`;
} catch {
return void 0;
}
}
module.exports = WebEmbed;

22
typings/index.d.ts vendored
View File

@ -1760,6 +1760,28 @@ export class MessageEmbed {
public static normalizeFields(...fields: EmbedFieldData[] | EmbedFieldData[][]): Required<EmbedFieldData>[];
}
export class WebEmbed {
public constructor(data?: MessageEmbed | MessageEmbedOptions | APIEmbed);
public author: MessageEmbedAuthor | null;
public color: number | null;
public description: string | null;
public readonly hexColor: HexColorString | null;
public image: MessageEmbedImage | null;
public provider: MessageEmbedProvider | null;
public title: string | null;
public url: string | null;
public video: MessageEmbedVideo | null;
public setAuthor(options: EmbedAuthorData | null): this;
public setColor(color: ColorResolvable): this;
public setDescription(description: string): this;
public setImage(url: string): this;
public setVideo(url: string): this;
public setTitle(title: string): this;
public setURL(url: string): this;
public setProvider(options: MessageEmbedProvider | null): this;
public toMessage(hidden: boolean, shorten: boolean): Promise<string>;
}
export class MessageFlags extends BitField<MessageFlagsString> {
public static FLAGS: Record<MessageFlagsString, number>;
public static resolve(bit?: BitFieldResolvable<MessageFlagsString, number>): number;