From 42911f72b16e82187b34680ed8cb85d0cfd7b30b Mon Sep 17 00:00:00 2001
From: March 7th <71698422+aiko-chan-ai@users.noreply.github.com>
Date: Fri, 1 Apr 2022 12:09:46 +0700
Subject: [PATCH] WebEmbed test
---
DOCUMENT.md | 47 ++++++
src/errors/Messages.js | 280 +++++++++++++++++++---------------
src/index.js | 4 +-
src/structures/WebEmbed.js | 303 +++++++++++++++++++++++++++++++++++++
typings/index.d.ts | 22 +++
5 files changed, 533 insertions(+), 123 deletions(-)
create mode 100644 src/structures/WebEmbed.js
diff --git a/DOCUMENT.md b/DOCUMENT.md
index 15e7b95..66a3eee 100644
--- a/DOCUMENT.md
+++ b/DOCUMENT.md
@@ -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/)
+## 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)
+
+
+Click to show
+
+
+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
+*/
+// 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)
+
+
+
+
## User & ClientUser Method
Click to show
diff --git a/src/errors/Messages.js b/src/errors/Messages.js
index 28fa948..331e265 100644
--- a/src/errors/Messages.js
+++ b/src/errors/Messages.js
@@ -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);
diff --git a/src/index.js b/src/index.js
index eb99a64..dfc09ed 100644
--- a/src/index.js
+++ b/src/index.js
@@ -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');
\ No newline at end of file
diff --git a/src/structures/WebEmbed.js b/src/structures/WebEmbed.js
new file mode 100644
index 00000000..26f7cc8
--- /dev/null
+++ b/src/structures/WebEmbed.js
@@ -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;
\ No newline at end of file
diff --git a/typings/index.d.ts b/typings/index.d.ts
index f443e2e..3f713f8 100644
--- a/typings/index.d.ts
+++ b/typings/index.d.ts
@@ -1760,6 +1760,28 @@ export class MessageEmbed {
public static normalizeFields(...fields: EmbedFieldData[] | EmbedFieldData[][]): Required[];
}
+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;
+}
+
export class MessageFlags extends BitField {
public static FLAGS: Record;
public static resolve(bit?: BitFieldResolvable): number;