- 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');
@ -49,13 +48,19 @@ class APIRequest {
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 (
typeof this.options.data !== 'undefined' ||
typeof this.options.body !== 'undefined'
) {
if (this.options.dontUsePayloadJSON) { if (this.options.dontUsePayloadJSON) {
for (const [key, value] of Object.entries(this.options.data)) body.append(key, value); for (const [key, value] of Object.entries(this.options.data || this.options.body)) {
body.append(key, value);
}
} else { } else {
body.append('payload_json', JSON.stringify(this.options.data)); body.append('payload_json', JSON.stringify(this.options.data || this.options.body));
} }
} }
headers = Object.assign(headers, body.getHeaders()); headers = Object.assign(headers, body.getHeaders());
@ -63,6 +68,9 @@ class APIRequest {
} else if (this.options.data != null) { } else if (this.options.data != null) {
body = JSON.stringify(this.options.data); body = JSON.stringify(this.options.data);
headers['Content-Type'] = 'application/json'; 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();

View File

@ -50,7 +50,9 @@ class MessagePayload {
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
);
} }
/** /**
@ -92,7 +94,10 @@ class MessagePayload {
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
);
} }
/** /**
@ -104,7 +109,12 @@ class MessagePayload {
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;
@ -125,7 +135,11 @@ class MessagePayload {
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 (
typeof nonce === 'number'
? !Number.isInteger(nonce)
: typeof nonce !== 'string'
) {
throw new RangeError('MESSAGE_NONCE_TYPE'); throw new RangeError('MESSAGE_NONCE_TYPE');
} }
} }
@ -172,11 +186,15 @@ class MessagePayload {
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
? reference.id ?? reference
: this.target.messages.resolveId(reference);
if (message_id) { if (message_id) {
message_reference = { message_reference = {
message_id, message_id,
fail_if_not_exists: this.options.reply.failIfNotExists ?? this.target.client.options.failIfNotExists, fail_if_not_exists:
this.options.reply.failIfNotExists ??
this.target.client.options.failIfNotExists,
}; };
} }
} }
@ -223,7 +241,10 @@ class 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(
this.options.files?.map((file) => this.constructor.resolveFile(file)) ??
[],
);
return this; return this;
} }
@ -236,7 +257,7 @@ class MessagePayload {
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);
} }
@ -249,7 +270,9 @@ class MessagePayload {
}; };
const ownAttachment = const ownAttachment =
typeof fileLike === 'string' || fileLike instanceof Buffer || typeof fileLike.pipe === 'function'; typeof fileLike === 'string' ||
fileLike instanceof Buffer ||
typeof fileLike.pipe === 'function';
if (ownAttachment) { if (ownAttachment) {
attachment = fileLike; attachment = fileLike;
name = findName(attachment); name = findName(attachment);
@ -258,10 +281,9 @@ class MessagePayload {
name = fileLike.name ?? findName(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
@ -272,7 +294,9 @@ class MessagePayload {
static create(target, options, extra = {}) { static create(target, options, extra = {}) {
return new this( return new this(
target, target,
typeof options !== 'object' || options === null ? { content: options, ...extra } : { ...options, ...extra }, typeof options !== 'object' || options === null
? { content: options, ...extra }
: { ...options, ...extra },
); );
} }
} }

View File

@ -7,109 +7,6 @@ const { Error } = require('../errors');
const DataResolver = require('../util/DataResolver'); const DataResolver = require('../util/DataResolver');
const DiscordAPIError = require('../rest/DiscordAPIError'); const DiscordAPIError = require('../rest/DiscordAPIError');
const _send = (
client,
webhookID,
webhookToken,
data,
files,
query,
auth = false,
) => {
return new Promise((resolve, reject) => {
require('axios')({
method: 'post',
url: `${client.options.http.api}/v${
client.options.http.version
}/webhooks/${webhookID}/${webhookToken}${query ? `?${query}` : ''}`,
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,
auth,
})
.then((res) => resolve(res.data))
.catch((err) => {
err.request.options = {
data,
files,
};
return reject(
new DiscordAPIError(
err.response.data,
err.response.status,
err.request,
),
);
});
});
};
const _edit = (
client,
webhookID,
webhookToken,
messageID,
data,
files,
query,
auth = false,
) => {
return new Promise((resolve, reject) => {
require('axios')({
method: 'patch',
url: `${client.options.http.api}/v${client.options.http.version}/webhooks/${webhookID}/${webhookToken}/messages/${messageID}${query ? `?${query}` : ''}`,
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,
auth,
})
.then((res) => resolve(res.data))
.catch((err) => {
err.request.options = {
data,
files,
};
return reject(
new DiscordAPIError(
err.response.data,
err.response.status,
err.request,
),
);
});
});
};
/** /**
* Represents a webhook. * Represents a webhook.
*/ */
@ -139,7 +36,11 @@ class Webhook {
* @name Webhook#token * @name Webhook#token
* @type {?string} * @type {?string}
*/ */
Object.defineProperty(this, 'token', { value: data.token ?? null, writable: true, configurable: true }); Object.defineProperty(this, 'token', {
value: data.token ?? null,
writable: true,
configurable: true,
});
if ('avatar' in data) { if ('avatar' in data) {
/** /**
@ -204,7 +105,8 @@ class Webhook {
* The source guild of the webhook * The source guild of the webhook
* @type {?(Guild|APIGuild)} * @type {?(Guild|APIGuild)}
*/ */
this.sourceGuild = this.client.guilds?.resolve(data.source_guild.id) ?? data.source_guild; this.sourceGuild =
this.client.guilds?.resolve(data.source_guild.id) ?? data.source_guild;
} else { } else {
this.sourceGuild ??= null; this.sourceGuild ??= null;
} }
@ -214,7 +116,9 @@ class Webhook {
* The source channel of the webhook * The source channel of the webhook
* @type {?(NewsChannel|APIChannel)} * @type {?(NewsChannel|APIChannel)}
*/ */
this.sourceChannel = this.client.channels?.resolve(data.source_channel?.id) ?? data.source_channel; this.sourceChannel =
this.client.channels?.resolve(data.source_channel?.id) ??
data.source_channel;
} else { } else {
this.sourceChannel ??= null; this.sourceChannel ??= null;
} }
@ -310,8 +214,16 @@ class Webhook {
} }
const { body, files } = await messagePayload.resolveFiles(); const { body, files } = await messagePayload.resolveFiles();
const d = await _send(this.client, this.id, this.token, body, files, query, false); const d = await this.client.api.webhooks(this.id, this.token).post({
return this.client.channels?.cache.get(d.channel_id)?.messages._add(d, false) ?? d; body,
files,
query,
auth: false,
});
return (
this.client.channels?.cache.get(d.channel_id)?.messages._add(d, false) ??
d
);
} }
/** /**
@ -335,7 +247,9 @@ class Webhook {
async sendSlackMessage(body) { async sendSlackMessage(body) {
if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE'); if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE');
const data = await this.client.api.webhooks(this.id, this.token).slack.post({ const data = await this.client.api
.webhooks(this.id, this.token)
.slack.post({
query: new URLSearchParams({ wait: true }), query: new URLSearchParams({ wait: true }),
auth: false, auth: false,
body, body,
@ -362,7 +276,9 @@ class Webhook {
avatar = await DataResolver.resolveImage(avatar); avatar = await DataResolver.resolveImage(avatar);
} }
channel &&= channel.id ?? channel; channel &&= channel.id ?? channel;
const data = await this.client.api.webhooks(this.id, channel ? undefined : this.token).patch({ const data = await this.client.api
.webhooks(this.id, channel ? undefined : this.token)
.patch({
data: { name, avatar, channel_id: channel }, data: { name, avatar, channel_id: channel },
reason, reason,
auth: !this.token || Boolean(channel), auth: !this.token || Boolean(channel),
@ -392,15 +308,22 @@ class Webhook {
async fetchMessage(message, { cache = true, threadId } = {}) { async fetchMessage(message, { cache = true, threadId } = {}) {
if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE'); if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE');
const data = await this.client.api.webhooks(this.id, this.token).messages(message).get({ const data = await this.client.api
.webhooks(this.id, this.token)
.messages(message)
.get({
query: threadId query: threadId
? new URLSearchParams({ ? new URLSearchParams({
thread_id: threadId, thread_id: threadId,
}) })
: undefined, : undefined,
auth: false auth: false,
}); });
return this.client.channels?.cache.get(data.channel_id)?.messages._add(data, cache) ?? data; return (
this.client.channels?.cache
.get(data.channel_id)
?.messages._add(data, cache) ?? data
);
} }
/** /**
@ -420,26 +343,21 @@ class Webhook {
const { body, files } = await messagePayload.resolveBody().resolveFiles(); const { body, files } = await messagePayload.resolveBody().resolveFiles();
/* const d = await this.client.api
const d = await this.client.api.webhooks(this.id, this.token).messages(typeof message === 'string' ? message : message.id).patch({ .webhooks(this.id, this.token)
body, files, query: messagePayload.options.threadId ? new URLSearchParams({ thread_id: messagePayload.options.threadId }) : undefined, auth: false .messages(typeof message === 'string' ? message : message.id)
}); .patch({
*/
const d = await _edit(
this.client,
this.id,
this.token,
typeof message === 'string' ? message : message.id,
body, body,
files, files,
messagePayload.options.threadId query: messagePayload.options.threadId
? new URLSearchParams({ thread_id: messagePayload.options.threadId }) ? new URLSearchParams({ thread_id: messagePayload.options.threadId })
: undefined, : undefined,
false auth: false,
); });
const messageManager = this.client.channels?.cache.get(d.channel_id)?.messages; const messageManager = this.client.channels?.cache.get(
d.channel_id,
)?.messages;
if (!messageManager) return d; if (!messageManager) return d;
const existing = messageManager.cache.get(d.id); const existing = messageManager.cache.get(d.id);
@ -456,7 +374,9 @@ class Webhook {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async delete(reason) { async delete(reason) {
await this.client.api.webhooks(this.id, this.token).delete({ reason, auth: !this.token }); await this.client.api
.webhooks(this.id, this.token)
.delete({ reason, auth: !this.token });
} }
/** /**
@ -468,14 +388,17 @@ class Webhook {
async deleteMessage(message, threadId) { async deleteMessage(message, threadId) {
if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE'); if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE');
await this.client.api.webhooks(this.id, this.token).messages(typeof message === 'string' ? message : message.id ).delete({ await this.client.api
.webhooks(this.id, this.token)
.messages(typeof message === 'string' ? message : message.id)
.delete({
query: threadId query: threadId
? new URLSearchParams({ ? new URLSearchParams({
thread_id: threadId, thread_id: threadId,
}) })
: undefined, : undefined,
auth: false, auth: false,
}) });
} }
/** /**
@ -511,7 +434,9 @@ class Webhook {
* @returns {?string} * @returns {?string}
*/ */
avatarURL(options = {}) { avatarURL(options = {}) {
return this.avatar && this.client.rest.cdn.avatar(this.id, this.avatar, options); return (
this.avatar && this.client.rest.cdn.avatar(this.id, this.avatar, options)
);
} }
/** /**
@ -519,7 +444,9 @@ class Webhook {
* @returns {boolean} * @returns {boolean}
*/ */
isUserCreated() { isUserCreated() {
return Boolean(this.type === WebhookType.Incoming && this.owner && !this.owner.bot); return Boolean(
this.type === WebhookType.Incoming && this.owner && !this.owner.bot,
);
} }
/** /**
@ -560,7 +487,11 @@ class Webhook {
'url', 'url',
]) { ]) {
if (ignore.includes(prop)) continue; if (ignore.includes(prop)) continue;
Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(Webhook.prototype, prop)); Object.defineProperty(
structure.prototype,
prop,
Object.getOwnPropertyDescriptor(Webhook.prototype, prop),
);
} }
} }
} }

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" "',