Fix Message cannot Send

- Known Issue: Unable to send Embed
This commit is contained in:
March 7th 2022-03-20 19:08:18 +07:00
parent 8e987355f0
commit 721d1216e0
4 changed files with 464 additions and 396 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "discord.js-selfbot-v13", "name": "discord.js-selfbot-v13",
"version": "0.0.3", "version": "0.1.0",
"description": "A unofficial discord.js fork for creating selfbots [Based on discord.js v13]", "description": "A unofficial discord.js fork for creating selfbots [Based on discord.js v13]",
"main": "./src/index.js", "main": "./src/index.js",
"types": "./typings/index.d.ts", "types": "./typings/index.d.ts",
@ -44,8 +44,11 @@
"@sapphire/async-queue": "^1.3.0", "@sapphire/async-queue": "^1.3.0",
"@sapphire/snowflake": "^3.2.0", "@sapphire/snowflake": "^3.2.0",
"@types/ws": "^8.5.2", "@types/ws": "^8.5.2",
"axios": "^0.26.1",
"discord-api-types": "^0.27.3", "discord-api-types": "^0.27.3",
"discord.js": "^13.6.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"json-bigint": "^1.0.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lodash.snakecase": "^4.1.1", "lodash.snakecase": "^4.1.1",
"node-fetch": "^3.2.2", "node-fetch": "^3.2.2",

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const { Buffer } = require('node:buffer'); const { Buffer } = require('node:buffer');
const { isJSONEncodable } = require('@discordjs/builders'); const { BaseMessageComponent, MessageEmbed } = require('discord.js');
const { MessageFlags } = require('discord-api-types/v9'); const { MessageFlags } = require('discord-api-types/v9');
const { RangeError } = require('../errors'); const { RangeError } = require('../errors');
const DataResolver = require('../util/DataResolver'); const DataResolver = require('../util/DataResolver');
@ -109,7 +109,6 @@ class MessagePayload {
return content; return content;
} }
/** /**
* Resolves the body. * Resolves the body.
* @returns {MessagePayload} * @returns {MessagePayload}
@ -131,9 +130,9 @@ class MessagePayload {
} }
} }
const components = this.options.components?.map(c => const components = this.options.components?.map((c) =>
isJSONEncodable(c) ? c.toJSON() : this.target.client.options.jsonTransformer(c), BaseMessageComponent.create(c).toJSON(),
); );
let username; let username;
let avatarURL; let avatarURL;
@ -193,22 +192,27 @@ class MessagePayload {
} }
this.body = { this.body = {
content, content,
tts, tts,
nonce, nonce,
embeds: this.options.embeds?.map(embed => embeds: this.options.embeds?.map((embed) =>
isJSONEncodable(embed) ? embed.toJSON() : this.target.client.options.jsonTransformer(embed), new MessageEmbed(embed).toJSON(),
), ),
components, components,
username, username,
avatar_url: avatarURL, avatar_url: avatarURL,
allowed_mentions: allowed_mentions:
typeof content === 'undefined' && typeof message_reference === 'undefined' ? undefined : allowedMentions, typeof content === 'undefined' &&
flags, typeof message_reference === 'undefined'
message_reference, ? undefined
attachments: this.options.attachments, : allowedMentions,
sticker_ids: this.options.stickers?.map(sticker => sticker.id ?? sticker), flags,
}; message_reference,
attachments: this.options.attachments,
sticker_ids: this.options.stickers?.map(
(sticker) => sticker.id ?? sticker,
),
};
return this; return this;
} }

View File

@ -7,353 +7,411 @@ const { TypeError, Error } = require('../../errors');
const InteractionCollector = require('../InteractionCollector'); const InteractionCollector = require('../InteractionCollector');
const MessageCollector = require('../MessageCollector'); const MessageCollector = require('../MessageCollector');
const MessagePayload = require('../MessagePayload'); const MessagePayload = require('../MessagePayload');
const DiscordAPIError = require('../../rest/DiscordAPIError');
const _send = (client, channelID, data, files) => {
return new Promise((resolve, reject) => {
require('axios')({
method: 'post',
url: `${client.options.http.api}/v${client.options.http.version}/channels/${channelID}/messages`,
headers: {
authorization: client.token,
Accept: '*/*',
'Accept-Language': 'en-US,en;q=0.9',
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Referer: 'https://discord.com/channels/@me',
'Sec-Ch-Ua': '" Not A;Brand";v="99" "',
'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"iOS"',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'X-Debug-Options': 'bugReporterEnabled',
'X-Discord-Locale': 'en-US',
Origin: 'https://discord.com',
},
data,
files,
})
.then((res) => resolve(res.data))
.catch((err) => {
err.request.options = {
data,
files,
};
return reject(
new DiscordAPIError(
err.response.data,
err.response.status,
err.request,
),
);
});
});
}
/** /**
* Interface for classes that have text-channel-like features. * Interface for classes that have text-channel-like features.
* @interface * @interface
*/ */
class TextBasedChannel { class TextBasedChannel {
constructor() { constructor() {
/** /**
* A manager of the messages sent to this channel * A manager of the messages sent to this channel
* @type {MessageManager} * @type {MessageManager}
*/ */
this.messages = new MessageManager(this); this.messages = new MessageManager(this);
/** /**
* The channel's last message id, if one was sent * The channel's last message id, if one was sent
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.lastMessageId = null; this.lastMessageId = null;
/** /**
* The timestamp when the last pinned message was pinned, if there was one * The timestamp when the last pinned message was pinned, if there was one
* @type {?number} * @type {?number}
*/ */
this.lastPinTimestamp = null; this.lastPinTimestamp = null;
} }
/** /**
* The Message object of the last message in the channel, if one was sent * The Message object of the last message in the channel, if one was sent
* @type {?Message} * @type {?Message}
* @readonly * @readonly
*/ */
get lastMessage() { get lastMessage() {
return this.messages.resolve(this.lastMessageId); return this.messages.resolve(this.lastMessageId);
} }
/** /**
* The date when the last pinned message was pinned, if there was one * The date when the last pinned message was pinned, if there was one
* @type {?Date} * @type {?Date}
* @readonly * @readonly
*/ */
get lastPinAt() { get lastPinAt() {
return this.lastPinTimestamp && new Date(this.lastPinTimestamp); return this.lastPinTimestamp && new Date(this.lastPinTimestamp);
} }
/** /**
* Base options provided when sending. * Base options provided when sending.
* @typedef {Object} BaseMessageOptions * @typedef {Object} BaseMessageOptions
* @property {boolean} [tts=false] Whether or not the message should be spoken aloud * @property {boolean} [tts=false] Whether or not the message should be spoken aloud
* @property {string} [nonce=''] The nonce for the message * @property {string} [nonce=''] The nonce for the message
* @property {string} [content=''] The content for the message * @property {string} [content=''] The content for the message
* @property {Embed[]|APIEmbed[]} [embeds] The embeds for the message * @property {Embed[]|APIEmbed[]} [embeds] The embeds for the message
* (see [here](https://discord.com/developers/docs/resources/channel#embed-object) for more details) * (see [here](https://discord.com/developers/docs/resources/channel#embed-object) for more details)
* @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content * @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content
* (see [here](https://discord.com/developers/docs/resources/channel#allowed-mentions-object) for more details) * (see [here](https://discord.com/developers/docs/resources/channel#allowed-mentions-object) for more details)
* @property {FileOptions[]|BufferResolvable[]|MessageAttachment[]} [files] Files to send with the message * @property {FileOptions[]|BufferResolvable[]|MessageAttachment[]} [files] Files to send with the message
* @property {ActionRow[]|ActionRowOptions[]} [components] * @property {ActionRow[]|ActionRowOptions[]} [components]
* Action rows containing interactive components for the message (buttons, select menus) * Action rows containing interactive components for the message (buttons, select menus)
* @property {MessageAttachment[]} [attachments] Attachments to send in the message * @property {MessageAttachment[]} [attachments] Attachments to send in the message
*/ */
/** /**
* Options provided when sending or editing a message. * Options provided when sending or editing a message.
* @typedef {BaseMessageOptions} MessageOptions * @typedef {BaseMessageOptions} MessageOptions
* @property {ReplyOptions} [reply] The options for replying to a message * @property {ReplyOptions} [reply] The options for replying to a message
* @property {StickerResolvable[]} [stickers=[]] Stickers to send in the message * @property {StickerResolvable[]} [stickers=[]] Stickers to send in the message
* @property {MessageFlags} [flags] Which flags to set for the message. Only `MessageFlags.SuppressEmbeds` can be set. * @property {MessageFlags} [flags] Which flags to set for the message. Only `MessageFlags.SuppressEmbeds` can be set.
*/ */
/** /**
* Options provided to control parsing of mentions by Discord * Options provided to control parsing of mentions by Discord
* @typedef {Object} MessageMentionOptions * @typedef {Object} MessageMentionOptions
* @property {MessageMentionTypes[]} [parse] Types of mentions to be parsed * @property {MessageMentionTypes[]} [parse] Types of mentions to be parsed
* @property {Snowflake[]} [users] Snowflakes of Users to be parsed as mentions * @property {Snowflake[]} [users] Snowflakes of Users to be parsed as mentions
* @property {Snowflake[]} [roles] Snowflakes of Roles to be parsed as mentions * @property {Snowflake[]} [roles] Snowflakes of Roles to be parsed as mentions
* @property {boolean} [repliedUser=true] Whether the author of the Message being replied to should be pinged * @property {boolean} [repliedUser=true] Whether the author of the Message being replied to should be pinged
*/ */
/** /**
* Types of mentions to enable in MessageMentionOptions. * Types of mentions to enable in MessageMentionOptions.
* - `roles` * - `roles`
* - `users` * - `users`
* - `everyone` * - `everyone`
* @typedef {string} MessageMentionTypes * @typedef {string} MessageMentionTypes
*/ */
/** /**
* @typedef {Object} FileOptions * @typedef {Object} FileOptions
* @property {BufferResolvable} attachment File to attach * @property {BufferResolvable} attachment File to attach
* @property {string} [name='file.jpg'] Filename of the attachment * @property {string} [name='file.jpg'] Filename of the attachment
* @property {string} description The description of the file * @property {string} description The description of the file
*/ */
/** /**
* Options for sending a message with a reply. * Options for sending a message with a reply.
* @typedef {Object} ReplyOptions * @typedef {Object} ReplyOptions
* @property {MessageResolvable} messageReference The message to reply to (must be in the same channel and not system) * @property {MessageResolvable} messageReference The message to reply to (must be in the same channel and not system)
* @property {boolean} [failIfNotExists=this.client.options.failIfNotExists] Whether to error if the referenced * @property {boolean} [failIfNotExists=this.client.options.failIfNotExists] Whether to error if the referenced
* message does not exist (creates a standard message in this case when false) * message does not exist (creates a standard message in this case when false)
*/ */
/** /**
* Sends a message to this channel. * Sends a message to this channel.
* @param {string|MessagePayload|MessageOptions} options The options to provide * @param {string|MessagePayload|MessageOptions} options The options to provide
* @returns {Promise<Message>} * @returns {Promise<Message>}
* @example * @example
* // Send a basic message * // Send a basic message
* channel.send('hello!') * channel.send('hello!')
* .then(message => console.log(`Sent message: ${message.content}`)) * .then(message => console.log(`Sent message: ${message.content}`))
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Send a remote file * // Send a remote file
* channel.send({ * channel.send({
* files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048'] * files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048']
* }) * })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Send a local file * // Send a local file
* channel.send({ * channel.send({
* files: [{ * files: [{
* attachment: 'entire/path/to/file.jpg', * attachment: 'entire/path/to/file.jpg',
* name: 'file.jpg', * name: 'file.jpg',
* description: 'A description of the file' * description: 'A description of the file'
* }] * }]
* }) * })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Send an embed with a local image inside * // Send an embed with a local image inside
* channel.send({ * channel.send({
* content: 'This is an embed', * content: 'This is an embed',
* embeds: [ * embeds: [
* { * {
* thumbnail: { * thumbnail: {
* url: 'attachment://file.jpg' * url: 'attachment://file.jpg'
* } * }
* } * }
* ], * ],
* files: [{ * files: [{
* attachment: 'entire/path/to/file.jpg', * attachment: 'entire/path/to/file.jpg',
* name: 'file.jpg', * name: 'file.jpg',
* description: 'A description of the file' * description: 'A description of the file'
* }] * }]
* }) * })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async send(options) { async send(options) {
await this.client.api.channels(this.id).typing.post(); await this.client.api.channels(this.id).typing.post();
const User = require('../User'); const User = require('../User');
const { GuildMember } = require('../GuildMember'); const { GuildMember } = require('../GuildMember');
if (this instanceof User || this instanceof GuildMember) { if (this instanceof User || this instanceof GuildMember) {
const dm = await this.createDM(); const dm = await this.createDM();
return dm.send(options); return dm.send(options);
} }
let messagePayload; let messagePayload;
if (options instanceof MessagePayload) { if (options instanceof MessagePayload) {
messagePayload = options.resolveBody(); messagePayload = options.resolveBody();
} else { } else {
messagePayload = MessagePayload.create(this, options).resolveBody(); messagePayload = MessagePayload.create(this, options).resolveBody();
} }
const { body, files } = await messagePayload.resolveFiles(); const { body, files } = await messagePayload.resolveFiles();
const d = await this.client.api.channels[this.id].messages.post({ body, files }); console.log(body);
// const d = await this.client.api.channels[this.id].messages.post({ body, files });
const d = await _send(this.client, this.id, body, files);
console.log(d);
await this.client.api.channels(this.id).typing.delete();
return this.messages.cache.get(d.id) ?? this.messages._add(d);
}
await this.client.api.channels(this.id).typing.delete(); // Patch send message [fck :(]
return this.messages.cache.get(d.id) ?? this.messages._add(d); /**
} * Sends a typing indicator in the channel.
* @returns {Promise<void>} Resolves upon the typing status being sent
* @example
* // Start typing in a channel
* channel.sendTyping();
*/
async sendTyping() {
await this.client.api.channels(this.id).typing.post();
}
/** /**
* Sends a typing indicator in the channel. * Creates a Message Collector.
* @returns {Promise<void>} Resolves upon the typing status being sent * @param {MessageCollectorOptions} [options={}] The options to pass to the collector
* @example * @returns {MessageCollector}
* // Start typing in a channel * @example
* channel.sendTyping(); * // Create a message collector
*/ * const filter = m => m.content.includes('discord');
async sendTyping() { * const collector = channel.createMessageCollector({ filter, time: 15_000 });
await this.client.api.channels(this.id).typing.post(); * collector.on('collect', m => console.log(`Collected ${m.content}`));
} * collector.on('end', collected => console.log(`Collected ${collected.size} items`));
*/
createMessageCollector(options = {}) {
return new MessageCollector(this, options);
}
/** /**
* Creates a Message Collector. * An object containing the same properties as CollectorOptions, but a few more:
* @param {MessageCollectorOptions} [options={}] The options to pass to the collector * @typedef {MessageCollectorOptions} AwaitMessagesOptions
* @returns {MessageCollector} * @property {string[]} [errors] Stop/end reasons that cause the promise to reject
* @example */
* // Create a message collector
* const filter = m => m.content.includes('discord');
* const collector = channel.createMessageCollector({ filter, time: 15_000 });
* collector.on('collect', m => console.log(`Collected ${m.content}`));
* collector.on('end', collected => console.log(`Collected ${collected.size} items`));
*/
createMessageCollector(options = {}) {
return new MessageCollector(this, options);
}
/** /**
* An object containing the same properties as CollectorOptions, but a few more: * Similar to createMessageCollector but in promise form.
* @typedef {MessageCollectorOptions} AwaitMessagesOptions * Resolves with a collection of messages that pass the specified filter.
* @property {string[]} [errors] Stop/end reasons that cause the promise to reject * @param {AwaitMessagesOptions} [options={}] Optional options to pass to the internal collector
*/ * @returns {Promise<Collection<Snowflake, Message>>}
* @example
* // Await !vote messages
* const filter = m => m.content.startsWith('!vote');
* // Errors: ['time'] treats ending because of the time limit as an error
* channel.awaitMessages({ filter, max: 4, time: 60_000, errors: ['time'] })
* .then(collected => console.log(collected.size))
* .catch(collected => console.log(`After a minute, only ${collected.size} out of 4 voted.`));
*/
awaitMessages(options = {}) {
return new Promise((resolve, reject) => {
const collector = this.createMessageCollector(options);
collector.once('end', (collection, reason) => {
if (options.errors?.includes(reason)) {
reject(collection);
} else {
resolve(collection);
}
});
});
}
/** /**
* Similar to createMessageCollector but in promise form. * Creates a component interaction collector.
* Resolves with a collection of messages that pass the specified filter. * @param {MessageComponentCollectorOptions} [options={}] Options to send to the collector
* @param {AwaitMessagesOptions} [options={}] Optional options to pass to the internal collector * @returns {InteractionCollector}
* @returns {Promise<Collection<Snowflake, Message>>} * @example
* @example * // Create a button interaction collector
* // Await !vote messages * const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId';
* const filter = m => m.content.startsWith('!vote'); * const collector = channel.createMessageComponentCollector({ filter, time: 15_000 });
* // Errors: ['time'] treats ending because of the time limit as an error * collector.on('collect', i => console.log(`Collected ${i.customId}`));
* channel.awaitMessages({ filter, max: 4, time: 60_000, errors: ['time'] }) * collector.on('end', collected => console.log(`Collected ${collected.size} items`));
* .then(collected => console.log(collected.size)) */
* .catch(collected => console.log(`After a minute, only ${collected.size} out of 4 voted.`)); createMessageComponentCollector(options = {}) {
*/ return new InteractionCollector(this.client, {
awaitMessages(options = {}) { ...options,
return new Promise((resolve, reject) => { interactionType: InteractionType.MessageComponent,
const collector = this.createMessageCollector(options); channel: this,
collector.once('end', (collection, reason) => { });
if (options.errors?.includes(reason)) { }
reject(collection);
} else {
resolve(collection);
}
});
});
}
/** /**
* Creates a component interaction collector. * Collects a single component interaction that passes the filter.
* @param {MessageComponentCollectorOptions} [options={}] Options to send to the collector * The Promise will reject if the time expires.
* @returns {InteractionCollector} * @param {AwaitMessageComponentOptions} [options={}] Options to pass to the internal collector
* @example * @returns {Promise<MessageComponentInteraction>}
* // Create a button interaction collector * @example
* const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId'; * // Collect a message component interaction
* const collector = channel.createMessageComponentCollector({ filter, time: 15_000 }); * const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId';
* collector.on('collect', i => console.log(`Collected ${i.customId}`)); * channel.awaitMessageComponent({ filter, time: 15_000 })
* collector.on('end', collected => console.log(`Collected ${collected.size} items`)); * .then(interaction => console.log(`${interaction.customId} was clicked!`))
*/ * .catch(console.error);
createMessageComponentCollector(options = {}) { */
return new InteractionCollector(this.client, { awaitMessageComponent(options = {}) {
...options, const _options = { ...options, max: 1 };
interactionType: InteractionType.MessageComponent, return new Promise((resolve, reject) => {
channel: this, const collector = this.createMessageComponentCollector(_options);
}); collector.once('end', (interactions, reason) => {
} const interaction = interactions.first();
if (interaction) resolve(interaction);
else reject(new Error('INTERACTION_COLLECTOR_ERROR', reason));
});
});
}
/** /**
* Collects a single component interaction that passes the filter. * Bulk deletes given messages that are newer than two weeks.
* The Promise will reject if the time expires. * @param {Collection<Snowflake, Message>|MessageResolvable[]|number} messages
* @param {AwaitMessageComponentOptions} [options={}] Options to pass to the internal collector * Messages or number of messages to delete
* @returns {Promise<MessageComponentInteraction>} * @param {boolean} [filterOld=false] Filter messages to remove those which are older than two weeks automatically
* @example * @returns {Promise<Collection<Snowflake, Message>>} Returns the deleted messages
* // Collect a message component interaction * @example
* const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId'; * // Bulk delete messages
* channel.awaitMessageComponent({ filter, time: 15_000 }) * channel.bulkDelete(5)
* .then(interaction => console.log(`${interaction.customId} was clicked!`)) * .then(messages => console.log(`Bulk deleted ${messages.size} messages`))
* .catch(console.error); * .catch(console.error);
*/ */
awaitMessageComponent(options = {}) { async bulkDelete(messages, filterOld = false) {
const _options = { ...options, max: 1 }; if (Array.isArray(messages) || messages instanceof Collection) {
return new Promise((resolve, reject) => { let messageIds =
const collector = this.createMessageComponentCollector(_options); messages instanceof Collection
collector.once('end', (interactions, reason) => { ? [...messages.keys()]
const interaction = interactions.first(); : messages.map((m) => m.id ?? m);
if (interaction) resolve(interaction); if (filterOld) {
else reject(new Error('INTERACTION_COLLECTOR_ERROR', reason)); messageIds = messageIds.filter(
}); (id) =>
}); Date.now() - DiscordSnowflake.timestampFrom(id) < 1_209_600_000,
} );
}
if (messageIds.length === 0) return new Collection();
if (messageIds.length === 1) {
await this.client.api
.channels(this.id)
.messages(messageIds[0])
.delete();
const message = this.client.actions.MessageDelete.getMessage(
{
message_id: messageIds[0],
},
this,
);
return message
? new Collection([[message.id, message]])
: new Collection();
}
await this.client.api
.channels(this.id)
.messages['bulk-delete'].post({ body: { messages: messageIds } });
return messageIds.reduce(
(col, id) =>
col.set(
id,
this.client.actions.MessageDeleteBulk.getMessage(
{
message_id: id,
},
this,
),
),
new Collection(),
);
}
if (!isNaN(messages)) {
const msgs = await this.messages.fetch({ limit: messages });
return this.bulkDelete(msgs, filterOld);
}
throw new TypeError('MESSAGE_BULK_DELETE_TYPE');
}
/** static applyToClass(structure, full = false, ignore = []) {
* Bulk deletes given messages that are newer than two weeks. const props = ['send'];
* @param {Collection<Snowflake, Message>|MessageResolvable[]|number} messages if (full) {
* Messages or number of messages to delete props.push(
* @param {boolean} [filterOld=false] Filter messages to remove those which are older than two weeks automatically 'lastMessage',
* @returns {Promise<Collection<Snowflake, Message>>} Returns the deleted messages 'lastPinAt',
* @example 'bulkDelete',
* // Bulk delete messages 'sendTyping',
* channel.bulkDelete(5) 'createMessageCollector',
* .then(messages => console.log(`Bulk deleted ${messages.size} messages`)) 'awaitMessages',
* .catch(console.error); 'createMessageComponentCollector',
*/ 'awaitMessageComponent',
async bulkDelete(messages, filterOld = false) { );
if (Array.isArray(messages) || messages instanceof Collection) { }
let messageIds = messages instanceof Collection ? [...messages.keys()] : messages.map(m => m.id ?? m); for (const prop of props) {
if (filterOld) { if (ignore.includes(prop)) continue;
messageIds = messageIds.filter(id => Date.now() - DiscordSnowflake.timestampFrom(id) < 1_209_600_000); Object.defineProperty(
} structure.prototype,
if (messageIds.length === 0) return new Collection(); prop,
if (messageIds.length === 1) { Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop),
await this.client.api.channels(this.id).messages(messageIds[0]).delete(); );
const message = this.client.actions.MessageDelete.getMessage( }
{ }
message_id: messageIds[0],
},
this,
);
return message ? new Collection([[message.id, message]]) : new Collection();
}
await this.client.api.channels(this.id).messages['bulk-delete'].post({ body: { messages: messageIds } });
return messageIds.reduce(
(col, id) =>
col.set(
id,
this.client.actions.MessageDeleteBulk.getMessage(
{
message_id: id,
},
this,
),
),
new Collection(),
);
}
if (!isNaN(messages)) {
const msgs = await this.messages.fetch({ limit: messages });
return this.bulkDelete(msgs, filterOld);
}
throw new TypeError('MESSAGE_BULK_DELETE_TYPE');
}
static applyToClass(structure, full = false, ignore = []) {
const props = ['send'];
if (full) {
props.push(
'lastMessage',
'lastPinAt',
'bulkDelete',
'sendTyping',
'createMessageCollector',
'awaitMessages',
'createMessageComponentCollector',
'awaitMessageComponent',
);
}
for (const prop of props) {
if (ignore.includes(prop)) continue;
Object.defineProperty(
structure.prototype,
prop,
Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop),
);
}
}
} }
module.exports = TextBasedChannel; module.exports = TextBasedChannel;

View File

@ -2,6 +2,7 @@
const process = require('node:process'); const process = require('node:process');
const Transformers = require('./Transformers'); const Transformers = require('./Transformers');
const JSONBig = require('json-bigint');
/** /**
* @typedef {Function} CacheFactory * @typedef {Function} CacheFactory
@ -71,61 +72,63 @@ class Options extends null {
*/ */
static createDefault() { static createDefault() {
return { return {
waitGuildTimeout: 15_000, waitGuildTimeout: 15_000,
shardCount: 1, shardCount: 1,
makeCache: this.cacheWithLimits(this.defaultMakeCacheSettings), makeCache: this.cacheWithLimits(this.defaultMakeCacheSettings),
messageCacheLifetime: 0, messageCacheLifetime: 0,
messageSweepInterval: 0, messageSweepInterval: 0,
invalidRequestWarningInterval: 0, invalidRequestWarningInterval: 0,
intents: 32767, intents: 32767,
partials: [], partials: [],
restWsBridgeTimeout: 5_000, restWsBridgeTimeout: 5_000,
restRequestTimeout: 15_000, restRequestTimeout: 15_000,
restGlobalRateLimit: 0, restGlobalRateLimit: 0,
retryLimit: 1, retryLimit: 1,
restTimeOffset: 500, restTimeOffset: 500,
restSweepInterval: 60, restSweepInterval: 60,
failIfNotExists: true, failIfNotExists: true,
userAgentSuffix: [], userAgentSuffix: [],
presence: {}, presence: {},
sweepers: {}, sweepers: {},
ws: { ws: {
large_threshold: 50, large_threshold: 50,
compress: false, compress: false,
properties: { properties: {
$os: 'iPhone14,5', $os: 'iPhone14,5',
$browser: 'Discord iOS', $browser: 'Discord iOS',
$device: 'iPhone14,5 OS 15.2', $device: 'iPhone14,5 OS 15.2',
}, },
version: 9, version: 9,
}, },
http: { http: {
headers: { headers: {
"Accept": "*/*", Accept: '*/*',
"Accept-Encoding": "gzip, deflate, br", // 'Accept-Encoding': 'gzip, deflate, br',
"Accept-Language": 'en-US,en;q=0.9', 'Accept-Language': 'en-US,en;q=0.9',
"Cache-Control": "no-cache", 'Cache-Control': 'no-cache',
"Pragma": "no-cache", 'Content-Type': 'application/json',
"Referer": "https://discord.com/channels/@me", Pragma: 'no-cache',
"Sec-Ch-Ua": '" Not A;Brand";v="99" "', Referer: 'https://discord.com/channels/@me',
"Sec-Ch-Ua-Mobile": '?0', 'Sec-Ch-Ua': '" Not A;Brand";v="99" "',
"Sec-Ch-Ua-Platform": '"iOS"', 'Sec-Ch-Ua-Mobile': '?0',
"Sec-Fetch-Dest": "empty", 'Sec-Ch-Ua-Platform': '"iOS"',
"Sec-Fetch-Mode": "cors", 'Sec-Fetch-Dest': 'empty',
"Sec-Fetch-Site": "same-origin", 'Sec-Fetch-Mode': 'cors',
"X-Debug-Options": "bugReporterEnabled", 'Sec-Fetch-Site': 'same-origin',
"X-Discord-Locale": 'en-US', 'X-Debug-Options': 'bugReporterEnabled',
"Origin": "https://discord.com" 'X-Discord-Locale': 'en-US',
}, Origin: 'https://discord.com',
agent: {}, },
version: 9, agent: {},
api: 'https://discord.com/api', version: 9,
cdn: 'https://cdn.discordapp.com', api: 'https://discord.com/api',
invite: 'https://discord.gg', cdn: 'https://cdn.discordapp.com',
template: 'https://discord.new', invite: 'https://discord.gg',
scheduledEvent: 'https://discord.com/events', template: 'https://discord.new',
}, scheduledEvent: 'https://discord.com/events',
}; },
jsonTransformer: (object) => JSONBig.stringify(object),
};
} }
/** /**