- Message Send and Edit (Using REST, not patch Axios)
- Send Attachment Fix
This commit is contained in:
March 7th 2022-03-23 20:34:03 +07:00
parent 6f6c986fd2
commit 9596b1a210
7 changed files with 713 additions and 835 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "discord.js-selfbot-v13", "name": "discord.js-selfbot-v13",
"version": "0.2.4", "version": "0.3.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",

View File

@ -9,49 +9,6 @@ const MessagePayload = require('../structures/MessagePayload');
const Util = require('../util/Util'); const Util = require('../util/Util');
const DiscordAPIError = require('../rest/DiscordAPIError'); const DiscordAPIError = require('../rest/DiscordAPIError');
const _edit = (client, channelID, messageID, data, files) => {
return new Promise((resolve, reject) => {
require('axios')({
method: 'patch',
url: `${client.options.http.api}/v${client.options.http.version}/channels/${channelID}/messages/${messageID}`,
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,
),
);
});
});
};
/** /**
* Manages API methods for Messages and holds their cache. * Manages API methods for Messages and holds their cache.
* @extends {CachedManager} * @extends {CachedManager}
@ -174,8 +131,7 @@ class MessageManager extends CachedManager {
) )
.resolveBody() .resolveBody()
.resolveFiles(); .resolveFiles();
// const d = await this.client.api.channels(this.channel.id).messages(messageId).patch({ body, files }); const d = await this.client.api.channels(this.channel.id).messages(messageId).patch({ body, files });
const d = await _edit(this.client, this.channel.id, messageId, body, files);
const existing = this.cache.get(messageId); const existing = this.cache.get(messageId);
if (existing) { if (existing) {
const clone = existing._clone(); const clone = existing._clone();

View File

@ -2,7 +2,6 @@
const https = require('node:https'); const https = require('node:https');
const { setTimeout } = require('node:timers'); const { setTimeout } = require('node:timers');
const FormData = require('form-data');
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
const { UserAgent } = require('../util/Constants'); const { UserAgent } = require('../util/Constants');
@ -47,23 +46,32 @@ class APIRequest {
let body; let body;
if (this.options.files?.length) { if (this.options.files?.length) {
body = new FormData(); body = new FormData();
for (const [index, file] of this.options.files.entries()) { for (const [index, file] of this.options.files.entries()) {
if (file?.file) body.append(file.key ?? `files[${index}]`, file.file, file.name); if (file?.file)
} body.append(file.key ?? `files[${index}]`, file.file, file.name);
if (typeof this.options.data !== 'undefined') { }
if (this.options.dontUsePayloadJSON) { if (
for (const [key, value] of Object.entries(this.options.data)) body.append(key, value); typeof this.options.data !== 'undefined' ||
} else { typeof this.options.body !== 'undefined'
body.append('payload_json', JSON.stringify(this.options.data)); ) {
} if (this.options.dontUsePayloadJSON) {
} for (const [key, value] of Object.entries(this.options.data || this.options.body)) {
headers = Object.assign(headers, body.getHeaders()); body.append(key, value);
// eslint-disable-next-line eqeqeq }
} else if (this.options.data != null) { } else {
body = JSON.stringify(this.options.data); body.append('payload_json', JSON.stringify(this.options.data || this.options.body));
headers['Content-Type'] = 'application/json'; }
} }
headers = Object.assign(headers, body.getHeaders());
// eslint-disable-next-line eqeqeq
} else if (this.options.data != null) {
body = JSON.stringify(this.options.data);
headers['Content-Type'] = 'application/json';
} else if (this.options.body != null) {
body = JSON.stringify(this.options.body);
headers['Content-Type'] = 'application/json';
}
const controller = new AbortController(); const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), this.client.options.restRequestTimeout).unref(); const timeout = setTimeout(() => controller.abort(), this.client.options.restRequestTimeout).unref();

View File

@ -12,186 +12,204 @@ const Util = require('../util/Util');
* Represents a message to be sent to the API. * Represents a message to be sent to the API.
*/ */
class MessagePayload { class MessagePayload {
/** /**
* @param {MessageTarget} target The target for this message to be sent to * @param {MessageTarget} target The target for this message to be sent to
* @param {MessageOptions|WebhookMessageOptions} options Options passed in from send * @param {MessageOptions|WebhookMessageOptions} options Options passed in from send
*/ */
constructor(target, options) { constructor(target, options) {
/** /**
* The target for this message to be sent to * The target for this message to be sent to
* @type {MessageTarget} * @type {MessageTarget}
*/ */
this.target = target; this.target = target;
/** /**
* Options passed in from send * Options passed in from send
* @type {MessageOptions|WebhookMessageOptions} * @type {MessageOptions|WebhookMessageOptions}
*/ */
this.options = options; this.options = options;
/** /**
* Body sendable to the API * Body sendable to the API
* @type {?APIMessage} * @type {?APIMessage}
*/ */
this.body = null; this.body = null;
/** /**
* Files sendable to the API * Files sendable to the API
* @type {?RawFile[]} * @type {?RawFile[]}
*/ */
this.files = null; this.files = null;
} }
/** /**
* Whether or not the target is a {@link Webhook} or a {@link WebhookClient} * Whether or not the target is a {@link Webhook} or a {@link WebhookClient}
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get isWebhook() { get isWebhook() {
const Webhook = require('./Webhook'); const Webhook = require('./Webhook');
const WebhookClient = require('../client/WebhookClient'); const WebhookClient = require('../client/WebhookClient');
return this.target instanceof Webhook || this.target instanceof WebhookClient; return (
} this.target instanceof Webhook || this.target instanceof WebhookClient
);
}
/** /**
* Whether or not the target is a {@link User} * Whether or not the target is a {@link User}
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get isUser() { get isUser() {
const User = require('./User'); const User = require('./User');
const { GuildMember } = require('./GuildMember'); const { GuildMember } = require('./GuildMember');
return this.target instanceof User || this.target instanceof GuildMember; return this.target instanceof User || this.target instanceof GuildMember;
} }
/** /**
* Whether or not the target is a {@link Message} * Whether or not the target is a {@link Message}
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get isMessage() { get isMessage() {
const { Message } = require('./Message'); const { Message } = require('./Message');
return this.target instanceof Message; return this.target instanceof Message;
} }
/** /**
* Whether or not the target is a {@link MessageManager} * Whether or not the target is a {@link MessageManager}
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get isMessageManager() { get isMessageManager() {
const MessageManager = require('../managers/MessageManager'); const MessageManager = require('../managers/MessageManager');
return this.target instanceof MessageManager; return this.target instanceof MessageManager;
} }
/** /**
* Whether or not the target is an {@link Interaction} or an {@link InteractionWebhook} * Whether or not the target is an {@link Interaction} or an {@link InteractionWebhook}
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get isInteraction() { get isInteraction() {
const Interaction = require('./Interaction'); const Interaction = require('./Interaction');
const InteractionWebhook = require('./InteractionWebhook'); const InteractionWebhook = require('./InteractionWebhook');
return this.target instanceof Interaction || this.target instanceof InteractionWebhook; return (
} this.target instanceof Interaction ||
this.target instanceof InteractionWebhook
);
}
/** /**
* Makes the content of this message. * Makes the content of this message.
* @returns {?string} * @returns {?string}
*/ */
makeContent() { makeContent() {
let content; let content;
if (this.options.content === null) { if (this.options.content === null) {
content = ''; content = '';
} else if (typeof this.options.content !== 'undefined') { } else if (typeof this.options.content !== 'undefined') {
content = Util.verifyString(this.options.content, RangeError, 'MESSAGE_CONTENT_TYPE', false); content = Util.verifyString(
} this.options.content,
RangeError,
'MESSAGE_CONTENT_TYPE',
false,
);
}
return content; return content;
} }
/** /**
* Resolves the body. * Resolves the body.
* @returns {MessagePayload} * @returns {MessagePayload}
*/ */
resolveBody() { resolveBody() {
if (this.data) return this; if (this.data) return this;
const isInteraction = this.isInteraction; const isInteraction = this.isInteraction;
const isWebhook = this.isWebhook; const isWebhook = this.isWebhook;
const content = this.makeContent(); const content = this.makeContent();
const tts = Boolean(this.options.tts); const tts = Boolean(this.options.tts);
let nonce; let nonce;
if (typeof this.options.nonce !== 'undefined') { if (typeof this.options.nonce !== 'undefined') {
nonce = this.options.nonce; nonce = this.options.nonce;
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
if (typeof nonce === 'number' ? !Number.isInteger(nonce) : typeof nonce !== 'string') { if (
throw new RangeError('MESSAGE_NONCE_TYPE'); typeof nonce === 'number'
} ? !Number.isInteger(nonce)
} : typeof nonce !== 'string'
) {
throw new RangeError('MESSAGE_NONCE_TYPE');
}
}
const components = this.options.components?.map((c) => const components = this.options.components?.map((c) =>
BaseMessageComponent.create(c).toJSON(), BaseMessageComponent.create(c).toJSON(),
); );
let username; let username;
let avatarURL; let avatarURL;
if (isWebhook) { if (isWebhook) {
username = this.options.username ?? this.target.name; username = this.options.username ?? this.target.name;
if (this.options.avatarURL) avatarURL = this.options.avatarURL; if (this.options.avatarURL) avatarURL = this.options.avatarURL;
} }
let flags; let flags;
if ( if (
typeof this.options.flags !== 'undefined' || typeof this.options.flags !== 'undefined' ||
(this.isMessage && typeof this.options.reply === 'undefined') || (this.isMessage && typeof this.options.reply === 'undefined') ||
this.isMessageManager this.isMessageManager
) { ) {
flags = flags =
// eslint-disable-next-line eqeqeq // eslint-disable-next-line eqeqeq
this.options.flags != null this.options.flags != null
? new MessageFlagsBitField(this.options.flags).bitfield ? new MessageFlagsBitField(this.options.flags).bitfield
: this.target.flags?.bitfield; : this.target.flags?.bitfield;
} }
if (isInteraction && this.options.ephemeral) { if (isInteraction && this.options.ephemeral) {
flags |= MessageFlags.Ephemeral; flags |= MessageFlags.Ephemeral;
} }
let allowedMentions = let allowedMentions =
typeof this.options.allowedMentions === 'undefined' typeof this.options.allowedMentions === 'undefined'
? this.target.client.options.allowedMentions ? this.target.client.options.allowedMentions
: this.options.allowedMentions; : this.options.allowedMentions;
if (allowedMentions) { if (allowedMentions) {
allowedMentions = Util.cloneObject(allowedMentions); allowedMentions = Util.cloneObject(allowedMentions);
allowedMentions.replied_user = allowedMentions.repliedUser; allowedMentions.replied_user = allowedMentions.repliedUser;
delete allowedMentions.repliedUser; delete allowedMentions.repliedUser;
} }
let message_reference; let message_reference;
if (typeof this.options.reply === 'object') { if (typeof this.options.reply === 'object') {
const reference = this.options.reply.messageReference; const reference = this.options.reply.messageReference;
const message_id = this.isMessage ? reference.id ?? reference : this.target.messages.resolveId(reference); const message_id = this.isMessage
if (message_id) { ? reference.id ?? reference
message_reference = { : this.target.messages.resolveId(reference);
message_id, if (message_id) {
fail_if_not_exists: this.options.reply.failIfNotExists ?? this.target.client.options.failIfNotExists, message_reference = {
}; message_id,
} fail_if_not_exists:
} this.options.reply.failIfNotExists ??
this.target.client.options.failIfNotExists,
};
}
}
const attachments = this.options.files?.map((file, index) => ({ const attachments = this.options.files?.map((file, index) => ({
id: index.toString(), id: index.toString(),
description: file.description, description: file.description,
})); }));
if (Array.isArray(this.options.attachments)) { if (Array.isArray(this.options.attachments)) {
this.options.attachments.push(...(attachments ?? [])); this.options.attachments.push(...(attachments ?? []));
} else { } else {
this.options.attachments = attachments; this.options.attachments = attachments;
} }
this.body = { this.body = {
content, content,
tts, tts,
nonce, nonce,
@ -213,68 +231,74 @@ class MessagePayload {
(sticker) => sticker.id ?? sticker, (sticker) => sticker.id ?? sticker,
), ),
}; };
return this; return this;
} }
/** /**
* Resolves files. * Resolves files.
* @returns {Promise<MessagePayload>} * @returns {Promise<MessagePayload>}
*/ */
async resolveFiles() { async resolveFiles() {
if (this.files) return this; if (this.files) return this;
this.files = await Promise.all(this.options.files?.map(file => this.constructor.resolveFile(file)) ?? []); this.files = await Promise.all(
return this; this.options.files?.map((file) => this.constructor.resolveFile(file)) ??
} [],
);
return this;
}
/** /**
* Resolves a single file into an object sendable to the API. * Resolves a single file into an object sendable to the API.
* @param {BufferResolvable|Stream|FileOptions|MessageAttachment} fileLike Something that could be resolved to a file * @param {BufferResolvable|Stream|FileOptions|MessageAttachment} fileLike Something that could be resolved to a file
* @returns {Promise<RawFile>} * @returns {Promise<RawFile>}
*/ */
static async resolveFile(fileLike) { static async resolveFile(fileLike) {
let attachment; let attachment;
let name; let name;
const findName = thing => { const findName = (thing) => {
if (typeof thing === 'string') { if (typeof thing === 'string') {
return Util.basename(thing); return Util.basename(thing);
} }
if (thing.path) { if (thing.path) {
return Util.basename(thing.path); return Util.basename(thing.path);
} }
return 'file.jpg'; return 'file.jpg';
}; };
const ownAttachment = const ownAttachment =
typeof fileLike === 'string' || fileLike instanceof Buffer || typeof fileLike.pipe === 'function'; typeof fileLike === 'string' ||
if (ownAttachment) { fileLike instanceof Buffer ||
attachment = fileLike; typeof fileLike.pipe === 'function';
name = findName(attachment); if (ownAttachment) {
} else { attachment = fileLike;
attachment = fileLike.attachment; name = findName(attachment);
name = fileLike.name ?? findName(attachment); } else {
} attachment = fileLike.attachment;
name = fileLike.name ?? findName(attachment);
}
const data = await DataResolver.resolveFile(attachment); const resource = await DataResolver.resolveFile(attachment);
return { data, name }; return { attachment, name, file: resource };
} }
/**
/** * Creates a {@link MessagePayload} from user-level arguments.
* Creates a {@link MessagePayload} from user-level arguments. * @param {MessageTarget} target Target to send to
* @param {MessageTarget} target Target to send to * @param {string|MessageOptions|WebhookMessageOptions} options Options or content to use
* @param {string|MessageOptions|WebhookMessageOptions} options Options or content to use * @param {MessageOptions|WebhookMessageOptions} [extra={}] Extra options to add onto specified options
* @param {MessageOptions|WebhookMessageOptions} [extra={}] Extra options to add onto specified options * @returns {MessagePayload}
* @returns {MessagePayload} */
*/ static create(target, options, extra = {}) {
static create(target, options, extra = {}) { return new this(
return new this( target,
target, typeof options !== 'object' || options === null
typeof options !== 'object' || options === null ? { content: options, ...extra } : { ...options, ...extra }, ? { content: options, ...extra }
); : { ...options, ...extra },
} );
}
} }
module.exports = MessagePayload; module.exports = MessagePayload;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
'use strict'; 'use strict';
const FormData = require('form-data');
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const { DiscordSnowflake } = require('@sapphire/snowflake'); const { DiscordSnowflake } = require('@sapphire/snowflake');
const { InteractionType, Routes } = require('discord-api-types/v9'); const { InteractionType, Routes } = require('discord-api-types/v9');
@ -9,47 +9,6 @@ const MessageCollector = require('../MessageCollector');
const MessagePayload = require('../MessagePayload'); const MessagePayload = require('../MessagePayload');
const DiscordAPIError = require('../../rest/DiscordAPIError'); 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
@ -215,8 +174,8 @@ class TextBasedChannel {
} }
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 _send(this.client, this.id, body, files); const d = await this.client.api.channels[this.id].messages.post({ body, files });
return this.messages.cache.get(d.id) ?? this.messages._add(d); return this.messages.cache.get(d.id) ?? this.messages._add(d);
} }

View File

@ -109,7 +109,7 @@ class Options extends null {
// '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',
'Content-Type': 'application/json', // 'Content-Type': 'application/json',
Pragma: 'no-cache', Pragma: 'no-cache',
Referer: 'https://discord.com/channels/@me', Referer: 'https://discord.com/channels/@me',
'Sec-Ch-Ua': '" Not A;Brand";v="99" "', 'Sec-Ch-Ua': '" Not A;Brand";v="99" "',