├── .replit ├── index.js ├── src ├── Tools.js ├── CreateMessage.js ├── CreateChannel.js ├── User.js └── MessageEmbed.js ├── package.json ├── util ├── Constants.js └── Util.js └── README.md /.replit: -------------------------------------------------------------------------------- 1 | language = "nodejs" 2 | run = "npm start" -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | MessageEmbed: require("./src/MessageEmbed"), 3 | CreateMessage: require('./src/CreateMessage'), 4 | CreateChannel: require('./src/CreateChannel'), 5 | Tools: require('./src/Tools.js'), 6 | User: require('./src/User.js'), 7 | } -------------------------------------------------------------------------------- /src/Tools.js: -------------------------------------------------------------------------------- 1 | const { FLAGS } = require("../util/Constants") 2 | 3 | class Tools { 4 | static getUserBadges(flag) { 5 | return Object.entries(FLAGS).reduce((badges, [key, bit]) => (flag & bit) > 0 ? [...badges, key] : badges, []) 6 | } 7 | } 8 | 9 | module.exports = Tools; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "autocode-discordjs", 3 | "version": "1.1.0", 4 | "description": "Allows you to use discord.js syntax in autocode.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/CTK-WARRIOR/autocode-discordjs" 12 | }, 13 | "keywords": [ 14 | "autocode", 15 | "discordjs", 16 | "autocode" 17 | ], 18 | "bugs": { 19 | "url": "https://github.com/CTK-WARRIOR/autocode-discordjs/issues" 20 | }, 21 | "homepage": "https://github.com/CTK-WARRIOR/autocode-discordjs#readme", 22 | "author": "ctk", 23 | "license": "ISC", 24 | "dependencies": { 25 | "lib": "^4.3.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/CreateMessage.js: -------------------------------------------------------------------------------- 1 | const lib = require('lib')({token: process.env.STDLIB_SECRET_TOKEN}); 2 | const CreateChannel = require('./CreateChannel') 3 | 4 | class createMessage { 5 | /** 6 | * @name MessageEmbed 7 | * @kind constructor 8 | */ 9 | 10 | constructor(data = {}) { 11 | /** 12 | * Duplicate all the data key and values 13 | */ 14 | Object.keys(data).forEach(key => { 15 | this[key] = data[key] || null 16 | }) 17 | 18 | /** 19 | * CreateChannel Class 20 | */ 21 | this.channel = new CreateChannel(data) 22 | } 23 | 24 | /** 25 | * Reply to the author of the command 26 | * @param {String} [content] 27 | * @param {Options} [channel_id, embed, tts, components, allowed_mentions] 28 | */ 29 | async reply(content, { channel_id, embed, tts, components, allowed_mentions }={}) { 30 | return await lib.discord.channels['@0.1.1'].messages.create({ 31 | content: content, 32 | channel_id: this.channel_id, 33 | embed, 34 | tts, 35 | components, 36 | allowed_mentions, 37 | message_reference : { 38 | message_id: this.id 39 | } 40 | }); 41 | } 42 | 43 | } 44 | 45 | module.exports = createMessage; -------------------------------------------------------------------------------- /util/Constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Colors: { 3 | DEFAULT: 0x000000, 4 | WHITE: 0xffffff, 5 | AQUA: 0x1abc9c, 6 | GREEN: 0x2ecc71, 7 | BLUE: 0x3498db, 8 | YELLOW: 0xffff00, 9 | PURPLE: 0x9b59b6, 10 | LUMINOUS_VIVID_PINK: 0xe91e63, 11 | GOLD: 0xf1c40f, 12 | ORANGE: 0xe67e22, 13 | RED: 0xe74c3c, 14 | GREY: 0x95a5a6, 15 | NAVY: 0x34495e, 16 | DARK_AQUA: 0x11806a, 17 | DARK_GREEN: 0x1f8b4c, 18 | DARK_BLUE: 0x206694, 19 | DARK_PURPLE: 0x71368a, 20 | DARK_VIVID_PINK: 0xad1457, 21 | DARK_GOLD: 0xc27c0e, 22 | DARK_ORANGE: 0xa84300, 23 | DARK_RED: 0x992d22, 24 | DARK_GREY: 0x979c9f, 25 | DARKER_GREY: 0x7f8c8d, 26 | LIGHT_GREY: 0xbcc0c0, 27 | DARK_NAVY: 0x2c3e50, 28 | BLURPLE: 0x7289da, 29 | GREYPLE: 0x99aab5, 30 | DARK_BUT_NOT_BLACK: 0x2c2f33, 31 | NOT_QUITE_BLACK: 0x23272a, 32 | }, 33 | 34 | FLAGS: { 35 | DISCORD_EMPLOYEE: 1 << 0, 36 | PARTNERED_SERVER_OWNER: 1 << 1, 37 | DISCORD_PARTNER: 1 << 1, 38 | HYPESQUAD_EVENTS: 1 << 2, 39 | BUGHUNTER_LEVEL_1: 1 << 3, 40 | HOUSE_BRAVERY: 1 << 6, 41 | HOUSE_BRILLIANCE: 1 << 7, 42 | HOUSE_BALANCE: 1 << 8, 43 | EARLY_SUPPORTER: 1 << 9, 44 | TEAM_USER: 1 << 10, 45 | SYSTEM: 1 << 12, 46 | BUGHUNTER_LEVEL_2: 1 << 14, 47 | VERIFIED_BOT: 1 << 16, 48 | EARLY_VERIFIED_DEVELOPER: 1 << 17, 49 | VERIFIED_DEVELOPER: 1 << 17, 50 | } 51 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @notedwin/autocode-discordjs 2 | 3 | ## Example Usage For Autocode 4 | 5 | ### **Tools:** get user's publicly displayed badges like hypesquad badge, discord moderator badge, etc. 6 | ```js 7 | let { User, Tools } = require('@notedwin/autocode-discordjs'); 8 | 9 | let user = new User(); 10 | 11 | Tools.getUserBadges(user.public_flags); 12 | ``` 13 | 14 | ### **CreateChannel:** ability to use this class similar like Discord.js to create new channels, set parent (category), set position (position of the channel in a category), and send message to a specific channel. 15 | ```js 16 | let { CreateChannel } = require('@notedwin/autocode-discordjs'); 17 | 18 | let event = context.params.event; 19 | 20 | /** CreateChannel.create(name, { guild_id, type, topic, bitrate, user_limit, rate_limit_per_user, position, permission_overwrites, parent_id, nsfw }); 21 | * bitrate and user_limit is only applicable for voice channels and stage channels. 22 | * check out more about bitrates here by reading the * section: https://discord.com/developers/docs/resources/channel#modify-channel-json-params-guild-channel 23 | * Check out more about channel type here: https://discord.com/developers/docs/resources/channel#channel-object-channel-types 24 | */ 25 | 26 | let channel = new CreateChannel(event); 27 | 28 | let createdChannel = await channel.create('test-channel', { 29 | type: 0, //for now, you will have to use the integer type from Discord Dev Portal. An alternative way is coming soon. 30 | topic: `channel topic here`, //optional 31 | }) 32 | 33 | /** replace the data with the created channel's data if you wanna update the created channel's info. 34 | * use the original context.params.event if you wanna update the current channel's info instead. 35 | */ 36 | channel = new CreateChannel(createdChannel) 37 | await channel.setName('new-channel-name') 38 | await channel.setParent('891309033884094525') 39 | ``` 40 | -------------------------------------------------------------------------------- /src/CreateChannel.js: -------------------------------------------------------------------------------- 1 | const lib = require('lib')({ token: process.env.STDLIB_SECRET_TOKEN }); 2 | 3 | class channel { 4 | constructor(data = {}) { 5 | 6 | if ('position' in data) { 7 | /** 8 | * The raw position of the channel from Discord 9 | * @type {number} 10 | */ 11 | this.rawPosition = data.position; 12 | } 13 | 14 | if ('guild_id' in data) { 15 | this.guildId = data.guild_id; 16 | } 17 | 18 | if ('parent_id' in data) { 19 | /** 20 | * The id of the category parent of this channel 21 | * @type {?Snowflake} 22 | */ 23 | this.parentId = data.parent_id; 24 | } 25 | 26 | this.id = data.id; 27 | } 28 | 29 | async send(content, { channel_id, embed, tts, components, allowed_mentions, message_reference, attachments } = {}) { 30 | return await lib.discord.channels['@0.3.2'].messages.create({ 31 | content: content, 32 | channel_id: channel_id || this.id, 33 | attachments, 34 | embed, 35 | tts, 36 | components, 37 | allowed_mentions, 38 | message_reference 39 | }); 40 | } 41 | 42 | async create(name, { guild_id, type, topic, bitrate, user_limit, rate_limit_per_user, position, permission_overwrites, parent_id, nsfw } = {}) { 43 | return await lib.discord.guilds['@0.2.4'].channels.create({ 44 | guild_id: guild_id ? guild_id : this.guildId, 45 | name, 46 | type, 47 | topic, 48 | bitrate, 49 | user_limit, 50 | rate_limit_per_user, 51 | position, 52 | permission_overwrites, 53 | parent_id, 54 | nsfw, 55 | }); 56 | } 57 | 58 | async setName(name) { 59 | return await lib.discord.channels['@0.3.2'].update({ 60 | channel_id: `${this.id}`, 61 | name, 62 | }); 63 | } 64 | 65 | async setParent(parent_id = '', options = {}){ 66 | return await lib.discord.guilds['@0.2.4'].channels.update({ 67 | guild_id: `${this.guildId}`, 68 | id: `${this.id}`, 69 | parent_id, 70 | }); 71 | } 72 | 73 | async setPosition(position, options = {}) { 74 | return await lib.discord.guilds['@0.2.4'].channels.update({ 75 | guild_id: `${this.guildId}`, 76 | id: `${this.id}`, 77 | position, 78 | }); 79 | } 80 | } 81 | 82 | module.exports = channel; 83 | -------------------------------------------------------------------------------- /util/Util.js: -------------------------------------------------------------------------------- 1 | const { Colors } = require('./Constants') 2 | 3 | class Util { 4 | 5 | /** 6 | * Data that can be resolved to give a string. This can be: 7 | * * A string 8 | * * An array (joined with a new line delimiter to give a string) 9 | * * Any value 10 | * @typedef {string|Array|*} StringResolvable 11 | */ 12 | 13 | /** 14 | * Resolves a StringResolvable to a string. 15 | * @param {StringResolvable} data The string resolvable to resolve 16 | * @returns {string} 17 | */ 18 | static resolveString(data) { 19 | if (typeof data === 'string') return data; 20 | if (Array.isArray(data)) return data.join('\n'); 21 | return String(data); 22 | } 23 | 24 | 25 | 26 | /** 27 | * Can be a number, hex string, an RGB array like: 28 | * ```js 29 | * [255, 0, 255] // purple 30 | * ``` 31 | * or one of the following strings: 32 | * - `DEFAULT` 33 | * - `WHITE` 34 | * - `AQUA` 35 | * - `GREEN` 36 | * - `BLUE` 37 | * - `YELLOW` 38 | * - `PURPLE` 39 | * - `LUMINOUS_VIVID_PINK` 40 | * - `GOLD` 41 | * - `ORANGE` 42 | * - `RED` 43 | * - `GREY` 44 | * - `DARKER_GREY` 45 | * - `NAVY` 46 | * - `DARK_AQUA` 47 | * - `DARK_GREEN` 48 | * - `DARK_BLUE` 49 | * - `DARK_PURPLE` 50 | * - `DARK_VIVID_PINK` 51 | * - `DARK_GOLD` 52 | * - `DARK_ORANGE` 53 | * - `DARK_RED` 54 | * - `DARK_GREY` 55 | * - `LIGHT_GREY` 56 | * - `DARK_NAVY` 57 | * - `BLURPLE` 58 | * - `GREYPLE` 59 | * - `DARK_BUT_NOT_BLACK` 60 | * - `NOT_QUITE_BLACK` 61 | * - `RANDOM` 62 | * @typedef {string|number|number[]} ColorResolvable 63 | */ 64 | 65 | /** 66 | * Resolves a ColorResolvable into a color number. 67 | * @param {ColorResolvable} color Color to resolve 68 | * @returns {number} A color 69 | */ 70 | static resolveColor(color) { 71 | if (typeof color === 'string') { 72 | if (color === 'RANDOM') return Math.floor(Math.random() * (0xffffff + 1)); 73 | if (color === 'DEFAULT') return 0; 74 | color = Colors[color] || parseInt(color.replace('#', ''), 16); 75 | } else if (Array.isArray(color)) { 76 | color = (color[0] << 16) + (color[1] << 8) + color[2]; 77 | } 78 | 79 | if (color < 0 || color > 0xffffff) throw new Error('COLOR_RANGE'); 80 | else if (color && isNaN(color)) throw new Error('COLOR_CONVERT'); 81 | 82 | return color; 83 | } 84 | 85 | } 86 | 87 | module.exports = Util; -------------------------------------------------------------------------------- /src/User.js: -------------------------------------------------------------------------------- 1 | const lib = require('lib')({ token: process.env.STDLIB_SECRET_TOKEN }); 2 | 3 | const Tools = require('./Tools'); 4 | 5 | class User { 6 | constructor(data) { 7 | /** 8 | * The user's id 9 | * @type {Snowflake} 10 | */ 11 | this.id = data.id; 12 | 13 | this.bot = null; 14 | 15 | this.system = null; 16 | 17 | this.flags = null; 18 | 19 | if ('username' in data) { 20 | /** 21 | * The username of the user 22 | * @type {?string} 23 | */ 24 | this.username = data.username; 25 | } else { 26 | this.username ?? null; 27 | } 28 | 29 | if ('bot' in data) { 30 | /** 31 | * Whether or not the user is a bot 32 | * @type {?boolean} 33 | */ 34 | this.bot = Boolean(data.bot); 35 | } else if (!this.partial && typeof this.bot !== 'boolean') { 36 | this.bot = false; 37 | } 38 | 39 | if ('discriminator' in data) { 40 | /** 41 | * A discriminator based on username for the user 42 | * @type {?string} 43 | */ 44 | this.discriminator = data.discriminator; 45 | } else { 46 | this.discriminator ?? null; 47 | } 48 | 49 | if ('avatar' in data) { 50 | /** 51 | * The user avatar's hash 52 | * @type {?string} 53 | */ 54 | this.avatar = data.avatar; 55 | } else { 56 | this.avatar ?? null; 57 | } 58 | 59 | if ('banner' in data) { 60 | /** 61 | * The user banner's hash 62 | * The user must be force fetched for this property to be present or be updated 63 | * @type {?string} 64 | */ 65 | this.banner = data.banner; 66 | } else if (this.banner !== null) { 67 | this.banner ?? undefined; 68 | } 69 | 70 | if ('accent_color' in data) { 71 | /** 72 | * The base 10 accent color of the user's banner 73 | * The user must be force fetched for this property to be present or be updated 74 | * @type {?number} 75 | */ 76 | this.accentColor = data.accent_color; 77 | } else if (this.accentColor !== null) { 78 | this.accentColor ?? undefined; 79 | } 80 | 81 | if ('system' in data) { 82 | /** 83 | * Whether the user is an Official Discord System user (part of the urgent message system) 84 | * @type {?boolean} 85 | */ 86 | this.system = Boolean(data.system); 87 | } else if (!this.partial && typeof this.system !== 'boolean') { 88 | this.system = false; 89 | } 90 | 91 | if ('public_flags' in data) { 92 | /** 93 | * The flags for this user 94 | * @type {?UserFlagsBitField} 95 | */ 96 | this.flags = new Tools.getUserBadges(data.public_flags); 97 | } 98 | } 99 | /** 100 | * Whether this User is a partial 101 | * @type {boolean} 102 | * @readonly 103 | */ 104 | get partial() { 105 | return typeof this.username !== 'string'; 106 | } 107 | 108 | /** 109 | * The timestamp the user was created at 110 | * @type {number} 111 | * @readonly 112 | */ 113 | get createdTimestamp() { 114 | const DISCORD_EPOCH = 1420070400000; 115 | 116 | function convertSnowflakeToDate(snowflake) { 117 | return new Date(snowflake / 4194304 + DISCORD_EPOCH); 118 | } 119 | 120 | return convertSnowflakeToDate(this.id) 121 | } 122 | 123 | /** 124 | * The time the user was created at 125 | * @type {Date} 126 | * @readonly 127 | */ 128 | get createdAt() { 129 | return new Date(this.createdTimestamp); 130 | } 131 | 132 | /** 133 | * The hexadecimal version of the user accent color, with a leading hash 134 | * @type {?string} 135 | * @readonly 136 | */ 137 | get hexAccentColor() { 138 | if (typeof this.accentColor !== 'number') return this.accentColor; 139 | return `#${this.accentColor.toString(16).padStart(6, '0')}`; 140 | } 141 | 142 | /** 143 | * The Discord "tag" (e.g. `CTK WARRIOR#7923`) for this user 144 | * @type {?string} 145 | * @readonly 146 | */ 147 | get tag() { 148 | return typeof this.username === 'string' ? `${this.username}#${this.discriminator}` : null; 149 | } 150 | 151 | async fetch(user_id) { 152 | const id = user_id ? user_id : this.id; 153 | 154 | return await lib.discord.users['@0.2.1'].retrieve({ 155 | user_id: id, 156 | }); 157 | } 158 | } -------------------------------------------------------------------------------- /src/MessageEmbed.js: -------------------------------------------------------------------------------- 1 | const Util = require('../util/Util.js'); 2 | 3 | class MessageEmbed { 4 | /** 5 | * @name MessageEmbed 6 | * @kind constructor 7 | * @memberof MessageEmbed 8 | * @param {MessageEmbed|Object} [data={}] MessageEmbed to clone or raw embed data 9 | */ 10 | 11 | constructor(data = {}) { 12 | /** 13 | * The type of this embed, either: 14 | * * `rich` - a rich embed 15 | * * `image` - an image embed 16 | * * `video` - a video embed 17 | * * `gifv` - a gifv embed 18 | * * `article` - an article embed 19 | * * `link` - a link embed 20 | * @type {string} 21 | */ 22 | this.type = data.type || 'rich'; 23 | 24 | /** 25 | * The title of this embed 26 | * @type {?string} 27 | */ 28 | this.title = 'title' in data ? data.title : null; 29 | 30 | /** 31 | * The description of this embed 32 | * @type {?string} 33 | */ 34 | this.description = 'description' in data ? data.description : null; 35 | 36 | /** 37 | * The URL of this embed 38 | * @type {?string} 39 | */ 40 | this.url = 'url' in data ? data.url : null; 41 | 42 | /** 43 | * The color of this embed 44 | * @type {?number} 45 | */ 46 | this.color = 'color' in data ? Util.resolveColor(data.color) : null; 47 | 48 | /** 49 | * The timestamp of this embed 50 | * @type {?number} 51 | */ 52 | this.timestamp = 'timestamp' in data ? new Date(data.timestamp).getTime() : null; 53 | 54 | /** 55 | * Represents a field of a MessageEmbed 56 | * @typedef {Object} EmbedField 57 | * @property {string} name The name of this field 58 | * @property {string} value The value of this field 59 | * @property {boolean} inline If this field will be displayed inline 60 | */ 61 | 62 | /** 63 | * The fields of this embed 64 | * @type {EmbedField[]} 65 | */ 66 | this.fields = []; 67 | if (data.fields) { 68 | this.fields = skipValidation ? data.fields.map(Util.cloneObject) : this.constructor.normalizeFields(data.fields); 69 | } 70 | 71 | /** 72 | * Represents the thumbnail of a MessageEmbed 73 | * @typedef {Object} MessageEmbedThumbnail 74 | * @property {string} url URL for this thumbnail 75 | * @property {string} proxyURL ProxyURL for this thumbnail 76 | * @property {number} height Height of this thumbnail 77 | * @property {number} width Width of this thumbnail 78 | */ 79 | 80 | /** 81 | * The thumbnail of this embed (if there is one) 82 | * @type {?MessageEmbedThumbnail} 83 | */ 84 | this.thumbnail = data.thumbnail 85 | ? { 86 | url: data.thumbnail.url, 87 | proxyURL: data.thumbnail.proxyURL || data.thumbnail.proxy_url, 88 | height: data.thumbnail.height, 89 | width: data.thumbnail.width, 90 | } 91 | : null; 92 | 93 | /** 94 | * Represents the image of a MessageEmbed 95 | * @typedef {Object} MessageEmbedImage 96 | * @property {string} url URL for this image 97 | * @property {string} proxyURL ProxyURL for this image 98 | * @property {number} height Height of this image 99 | * @property {number} width Width of this image 100 | */ 101 | 102 | /** 103 | * The image of this embed, if there is one 104 | * @type {?MessageEmbedImage} 105 | */ 106 | this.image = data.image 107 | ? { 108 | url: data.image.url, 109 | proxyURL: data.image.proxyURL || data.image.proxy_url, 110 | height: data.image.height, 111 | width: data.image.width, 112 | } 113 | : null; 114 | 115 | /** 116 | * Represents the author field of a MessageEmbed 117 | * @typedef {Object} MessageEmbedAuthor 118 | * @property {string} name The name of this author 119 | * @property {string} url URL of this author 120 | * @property {string} iconURL URL of the icon for this author 121 | * @property {string} proxyIconURL Proxied URL of the icon for this author 122 | */ 123 | 124 | /** 125 | * The author of this embed (if there is one) 126 | * @type {?MessageEmbedAuthor} 127 | */ 128 | this.author = data.author 129 | ? { 130 | name: data.author.name, 131 | url: data.author.url, 132 | iconURL: data.author.iconURL || data.author.icon_url, 133 | proxyIconURL: data.author.proxyIconURL || data.author.proxy_icon_url, 134 | } 135 | : null; 136 | 137 | /** 138 | * Represents the footer field of a MessageEmbed 139 | * @typedef {Object} MessageEmbedFooter 140 | * @property {string} text The text of this footer 141 | * @property {string} iconURL URL of the icon for this footer 142 | * @property {string} proxyIconURL Proxied URL of the icon for this footer 143 | */ 144 | 145 | /** 146 | * The footer of this embed 147 | * @type {?MessageEmbedFooter} 148 | */ 149 | this.footer = data.footer 150 | ? { 151 | text: data.footer.text, 152 | iconURL: data.footer.iconURL || data.footer.icon_url, 153 | proxyIconURL: data.footer.proxyIconURL || data.footer.proxy_icon_url, 154 | } 155 | : null; 156 | 157 | /** 158 | * The files of this embed 159 | * @type {Array} 160 | */ 161 | this.files = data.files || []; 162 | } 163 | 164 | /** 165 | * The date displayed on this embed 166 | * @type {?Date} 167 | * @readonly 168 | */ 169 | get createdAt() { 170 | return this.timestamp ? new Date(this.timestamp) : null; 171 | } 172 | 173 | /** 174 | * The hexadecimal version of the embed color, with a leading hash 175 | * @type {?string} 176 | * @readonly 177 | */ 178 | get hexColor() { 179 | return this.color ? `#${this.color.toString(16).padStart(6, '0')}` : null; 180 | } 181 | 182 | /** 183 | * The accumulated length for the embed title, description, fields and footer text 184 | * @type {number} 185 | * @readonly 186 | */ 187 | get length() { 188 | return ( 189 | (this.title ? this.title.length : 0) + 190 | (this.description ? this.description.length : 0) + 191 | (this.fields.length >= 1 192 | ? this.fields.reduce((prev, curr) => prev + curr.name.length + curr.value.length, 0) 193 | : 0) + 194 | (this.footer ? this.footer.text.length : 0) 195 | ); 196 | } 197 | 198 | /** 199 | * Adds a field to the embed (max 25). 200 | * @param {StringResolvable} name The name of this field 201 | * @param {StringResolvable} value The value of this field 202 | * @param {boolean} [inline=false] If this field will be displayed inline 203 | * @returns {MessageEmbed} 204 | */ 205 | addField(name, value, inline) { 206 | return this.addFields({ name, value, inline }); 207 | } 208 | 209 | /** 210 | * Adds fields to the embed (max 25). 211 | * @param {...EmbedFieldData|EmbedFieldData[]} fields The fields to add 212 | * @returns {MessageEmbed} 213 | */ 214 | addFields(...fields) { 215 | this.fields.push(...this.constructor.normalizeFields(fields)); 216 | return this; 217 | } 218 | 219 | /** 220 | * Removes, replaces, and inserts fields in the embed (max 25). 221 | * @param {number} index The index to start at 222 | * @param {number} deleteCount The number of fields to remove 223 | * @param {...EmbedFieldData|EmbedFieldData[]} [fields] The replacing field objects 224 | * @returns {MessageEmbed} 225 | */ 226 | spliceFields(index, deleteCount, ...fields) { 227 | this.fields.splice(index, deleteCount, ...this.constructor.normalizeFields(...fields)); 228 | return this; 229 | } 230 | 231 | /** 232 | * Sets the file to upload alongside the embed. This file can be accessed via `attachment://fileName.extension` when 233 | * setting an embed image or author/footer icons. Multiple files can be attached. 234 | * @param {Array} files Files to attach 235 | * @returns {MessageEmbed} 236 | */ 237 | attachFiles(files) { 238 | this.files = this.files.concat(files); 239 | return this; 240 | } 241 | 242 | /** 243 | * Sets the author of this embed. 244 | * @param {StringResolvable} name The name of the author 245 | * @param {string} [iconURL] The icon URL of the author 246 | * @param {string} [url] The URL of the author 247 | * @returns {MessageEmbed} 248 | */ 249 | setAuthor(name, iconURL, url) { 250 | this.author = { name: Util.resolveString(name), iconURL, url }; 251 | return this; 252 | } 253 | 254 | /** 255 | * Sets the color of this embed. 256 | * @param {ColorResolvable} color The color of the embed 257 | * @returns {MessageEmbed} 258 | */ 259 | setColor(color) { 260 | this.color = Util.resolveColor(color); 261 | return this; 262 | } 263 | 264 | /** 265 | * Sets the description of this embed. 266 | * @param {StringResolvable} description The description 267 | * @returns {MessageEmbed} 268 | */ 269 | setDescription(description) { 270 | description = Util.resolveString(description); 271 | this.description = description; 272 | return this; 273 | } 274 | 275 | /** 276 | * Sets the footer of this embed. 277 | * @param {StringResolvable} text The text of the footer 278 | * @param {string} [iconURL] The icon URL of the footer 279 | * @returns {MessageEmbed} 280 | */ 281 | setFooter(text, iconURL) { 282 | text = Util.resolveString(text); 283 | this.footer = { text, iconURL }; 284 | return this; 285 | } 286 | 287 | /** 288 | * Sets the image of this embed. 289 | * @param {string} url The URL of the image 290 | * @returns {MessageEmbed} 291 | */ 292 | setImage(url) { 293 | this.image = { url }; 294 | return this; 295 | } 296 | 297 | /** 298 | * Sets the thumbnail of this embed. 299 | * @param {string} url The URL of the thumbnail 300 | * @returns {MessageEmbed} 301 | */ 302 | setThumbnail(url) { 303 | this.thumbnail = { url }; 304 | return this; 305 | } 306 | 307 | /** 308 | * Sets the timestamp of this embed. 309 | * @param {Date|number} [timestamp=Date.now()] The timestamp or date 310 | * @returns {MessageEmbed} 311 | */ 312 | setTimestamp(timestamp = Date.now()) { 313 | if (timestamp instanceof Date) timestamp = timestamp.getTime(); 314 | this.timestamp = timestamp; 315 | return this; 316 | } 317 | 318 | /** 319 | * Sets the title of this embed. 320 | * @param {StringResolvable} title The title 321 | * @returns {MessageEmbed} 322 | */ 323 | setTitle(title) { 324 | title = Util.resolveString(title); 325 | this.title = title; 326 | return this; 327 | } 328 | 329 | /** 330 | * Sets the URL of this embed. 331 | * @param {string} url The URL 332 | * @returns {MessageEmbed} 333 | */ 334 | setURL(url) { 335 | this.url = url; 336 | return this; 337 | } 338 | 339 | /** 340 | * Transforms the embed to a plain object. 341 | * @returns {Object} The raw data of this embed 342 | */ 343 | toJSON() { 344 | return { 345 | title: this.title, 346 | type: 'rich', 347 | description: this.description, 348 | url: this.url, 349 | timestamp: this.timestamp ? new Date(this.timestamp) : null, 350 | color: this.color, 351 | fields: this.fields, 352 | thumbnail: this.thumbnail, 353 | image: this.image, 354 | author: this.author 355 | ? { 356 | name: this.author.name, 357 | url: this.author.url, 358 | icon_url: this.author.iconURL, 359 | } 360 | : null, 361 | footer: this.footer 362 | ? { 363 | text: this.footer.text, 364 | icon_url: this.footer.iconURL, 365 | } 366 | : null, 367 | }; 368 | } 369 | 370 | /** 371 | * Normalizes field input and resolves strings. 372 | * @param {StringResolvable} name The name of the field 373 | * @param {StringResolvable} value The value of the field 374 | * @param {boolean} [inline=false] Set the field to display inline 375 | * @returns {EmbedField} 376 | */ 377 | static normalizeField(name, value, inline = false) { 378 | name = Util.resolveString(name); 379 | if (!name) throw new Error('EMBED_FIELD_NAME'); 380 | value = Util.resolveString(value); 381 | if (!value) throw new Error('EMBED_FIELD_VALUE'); 382 | return { name, value, inline }; 383 | } 384 | 385 | /** 386 | * @typedef {Object} EmbedFieldData 387 | * @property {StringResolvable} name The name of this field 388 | * @property {StringResolvable} value The value of this field 389 | * @property {boolean} [inline] If this field will be displayed inline 390 | */ 391 | 392 | /** 393 | * Normalizes field input and resolves strings. 394 | * @param {...EmbedFieldData|EmbedFieldData[]} fields Fields to normalize 395 | * @returns {EmbedField[]} 396 | */ 397 | static normalizeFields(...fields) { 398 | return fields 399 | .flat(2) 400 | .map(field => 401 | this.normalizeField( 402 | field && field.name, 403 | field && field.value, 404 | field && typeof field.inline === 'boolean' ? field.inline : false, 405 | ), 406 | ); 407 | } 408 | } 409 | 410 | module.exports = MessageEmbed; --------------------------------------------------------------------------------