discord.js-selfbot-v13
Advanced tools
Comparing version
{ | ||
"name": "discord.js-selfbot-v13", | ||
"version": "3.4.2", | ||
"version": "3.4.3", | ||
"description": "A unofficial discord.js fork for creating selfbots [Based on discord.js v13]", | ||
@@ -58,11 +58,10 @@ "main": "./src/index.js", | ||
"@sapphire/shapeshift": "^3.9.5", | ||
"deasync": "^0.1.30", | ||
"discord-api-types": "^0.37.103", | ||
"fetch-cookie": "^2.1.0", | ||
"find-process": "^1.4.7", | ||
"form-data": "^4.0.1", | ||
"node-fetch": "^2.6.9", | ||
"fluent-ffmpeg": "^2.1.3", | ||
"prism-media": "^1.3.5", | ||
"qrcode": "^1.5.4", | ||
"tough-cookie": "^4.1.4", | ||
"tree-kill": "^1.2.2", | ||
"undici": "^6.21.0", | ||
"ws": "^8.16.0" | ||
@@ -80,3 +79,2 @@ }, | ||
"@types/node": "^20.14.12", | ||
"@types/node-fetch": "^2.6.11", | ||
"@types/ws": "^8.5.10", | ||
@@ -83,0 +81,0 @@ "conventional-changelog-cli": "^2.2.2", |
@@ -521,2 +521,24 @@ /* eslint-disable no-unreachable */ | ||
/** | ||
* Refresh the Discord CDN links with hashes so they can be usable. | ||
* @param {string[]} urls Discord CDN URLs | ||
* @returns {Promise<Array<{ original: string, refreshed: string }>>} | ||
*/ | ||
async refreshAttachmentURL(urls) { | ||
const data = await this.api.attachments('refresh-urls').post({ | ||
data: { attachment_urls: urls }, | ||
}); | ||
/** | ||
{ | ||
"refreshed_urls": [ | ||
{ | ||
"original": "url", | ||
"refreshed": "url with hash" | ||
} | ||
] | ||
} | ||
*/ | ||
return data.refreshed_urls; | ||
} | ||
/** | ||
* Options for {@link Client#generateInvite}. | ||
@@ -570,2 +592,3 @@ * @typedef {Object} InviteGenerationOptions | ||
async acceptInvite(invite, options = { bypassOnboarding: true, bypassVerify: true }) { | ||
throw new Error('METHOD_WARNING'); | ||
const code = DataResolver.resolveInviteCode(invite); | ||
@@ -692,3 +715,4 @@ if (!code) throw new Error('INVITE_RESOLVE_CODE'); | ||
*/ | ||
authorizeURL(url, options = { authorize: true, permissions: '0' }) { | ||
authorizeURL(url, options = { authorize: true }) { | ||
throw new Error('METHOD_WARNING'); | ||
const pathnameAPI = /\/api\/(v\d{1,2}\/)?oauth2\/authorize/; | ||
@@ -704,4 +728,15 @@ const pathnameURL = /\/oauth2\/authorize/; | ||
const searchParams = Object.fromEntries(url_.searchParams); | ||
options.permissions = `${Permissions.resolve(searchParams.permissions || options.permissions) || 0}`; | ||
options.permissions ??= `${Permissions.resolve(searchParams.permissions || 0)}`; | ||
options.integration_type ??= searchParams.integration_type || 0; | ||
options.location_context = { | ||
guild_id: '10000', | ||
channel_id: '10000', | ||
channel_type: 10000, | ||
}; | ||
options.guild_id ??= searchParams.guild_id; | ||
options.authorize ??= true; | ||
delete searchParams.permissions; | ||
delete searchParams.integration_type; | ||
delete searchParams.guild_id; | ||
if (!options.permissions || !options.guild_id) throw new Error('INVALID_OAUTH_OPTIONS'); | ||
return this.api.oauth2.authorize.post({ | ||
@@ -775,2 +810,5 @@ query: searchParams, | ||
_validateOptions(options = this.options) { | ||
options.captchaSolver = () => { | ||
throw new Error('METHOD_WARNING'); | ||
}; | ||
if (typeof options.makeCache !== 'function') { | ||
@@ -777,0 +815,0 @@ throw new TypeError('CLIENT_INVALID_OPTION', 'makeCache', 'a function'); |
@@ -7,4 +7,2 @@ 'use strict'; | ||
const { setTimeout } = require('node:timers'); | ||
const find = require('find-process'); | ||
const kill = require('tree-kill'); | ||
const secretbox = require('../util/Secretbox'); | ||
@@ -148,16 +146,13 @@ | ||
_cleanup() { | ||
if (this.player.dispatcher === this) this.player.dispatcher = null; | ||
if (this.player.dispatcher === this) { | ||
this.player.dispatcher.destroy(); | ||
this.player.dispatcher = null; | ||
} | ||
if (this.player.videoDispatcher === this) { | ||
this.player.videoDispatcher.destroy(); | ||
this.player.videoDispatcher = null; | ||
} | ||
const { streams } = this; | ||
if (streams.opus) streams.opus.destroy(); | ||
if (streams.ffmpeg?.process) { | ||
const ffmpegPid = streams.ffmpeg.process.pid; // But it is ppid ;-; | ||
const args = streams.ffmpeg.process.spawnargs.slice(1).join(' '); // Skip ffmpeg | ||
find('name', 'ffmpeg', true).then(list => { | ||
let process = list.find(o => o.pid === ffmpegPid || o.ppid === ffmpegPid || o.cmd.includes(args)); | ||
if (process) { | ||
kill(process.pid); | ||
} | ||
}); | ||
streams.ffmpeg.destroy(); | ||
} | ||
streams.ffmpeg?.destroy(); | ||
} | ||
@@ -164,0 +159,0 @@ |
@@ -22,2 +22,4 @@ 'use strict'; | ||
const { Readable: ReadableStream } = require('stream'); | ||
const deasync = require('deasync'); | ||
const fluent = require('fluent-ffmpeg'); | ||
const prism = require('prism-media'); | ||
@@ -30,16 +32,36 @@ const { H264NalSplitter } = require('./processing/AnnexBNalSplitter'); | ||
const FFMPEG_ARGUMENTS = [ | ||
'-use_wallclock_as_timestamps', | ||
const FFMPEG_OUTPUT_PREFIX = ['-use_wallclock_as_timestamps', '1', '-copyts', '-analyzeduration', '0']; | ||
const FFMPEG_INPUT_PREFIX = [ | ||
'-reconnect', | ||
'1', | ||
'-copyts', | ||
'-analyzeduration', | ||
'-reconnect_at_eof', | ||
'1', | ||
'-reconnect_streamed', | ||
'1', | ||
'-reconnect_delay_max', | ||
'4294', | ||
]; | ||
const FFMPEG_PCM_ARGUMENTS = ['-f', 's16le', '-ar', '48000', '-ac', '2']; | ||
const FFMPEG_VP8_ARGUMENTS = ['-f', 'ivf', '-deadline', 'realtime', '-c:v', 'libvpx']; | ||
const FFMPEG_H264_ARGUMENTS = options => [ | ||
'-c:v', | ||
'libx264', | ||
'-f', | ||
'h264', | ||
'-tune', | ||
'zerolatency', | ||
// '-pix_fmt', | ||
// 'yuv420p', | ||
'-preset', | ||
options?.presetH26x || 'faster', | ||
'-profile:v', | ||
'baseline', | ||
// '-g', | ||
// `${options?.fps}`, | ||
// '-x264-params', | ||
// `keyint=${options?.fps}:min-keyint=${options?.fps}`, | ||
'-bf', | ||
'0', | ||
'-loglevel', | ||
'0', | ||
'-f', | ||
's16le', | ||
'-ar', | ||
'48000', | ||
'-ac', | ||
'2', | ||
'-bsf:v', | ||
'h264_metadata=aud=insert', | ||
]; | ||
@@ -89,23 +111,18 @@ | ||
this.destroyDispatcher(); | ||
const isStream = input instanceof ReadableStream; | ||
const args = isStream ? FFMPEG_ARGUMENTS.slice() : ['-i', input, ...FFMPEG_ARGUMENTS]; | ||
const args = [...FFMPEG_OUTPUT_PREFIX, ...FFMPEG_PCM_ARGUMENTS]; | ||
if (!isStream) args.unshift('-i', input); | ||
if (options.seek) args.unshift('-ss', String(options.seek)); | ||
// Check input | ||
if (typeof input == 'string' && input.startsWith('http')) { | ||
args.unshift( | ||
'-reconnect', | ||
'1', | ||
'-reconnect_at_eof', | ||
'1', | ||
'-reconnect_streamed', | ||
'1', | ||
'-reconnect_delay_max', | ||
'4294', | ||
); | ||
args.unshift(...FFMPEG_INPUT_PREFIX); | ||
} | ||
const ffmpeg = new prism.FFmpeg({ args }); | ||
this.emit('debug', `[ffmpeg-audio_process] Spawn process with args:\n${args.join(' ')}`); | ||
ffmpeg.process.stderr.on('data', data => { | ||
this.emit('debug', `[ffmpeg-audio_process]: ${data.toString()}`); | ||
}); | ||
streams.ffmpeg = ffmpeg; | ||
@@ -150,15 +167,47 @@ if (isStream) { | ||
this.destroyVideoDispatcher(); | ||
const isStream = input instanceof ReadableStream; | ||
// Get video info | ||
let isDone = false; | ||
let data = null; | ||
if (!options?.fps) { | ||
fluent.ffprobe(input, (err, metadata) => { | ||
if (err) { | ||
this.emit('error', err); | ||
isDone = true; | ||
return; | ||
} | ||
let hasAudio = false; | ||
let hasVideo = false; | ||
let fps = 0; | ||
metadata.streams.forEach(stream => { | ||
if (stream.codec_type === 'audio') { | ||
hasAudio = true; | ||
} | ||
if (stream.codec_type === 'video') { | ||
hasVideo = true; | ||
if (stream.avg_frame_rate) { | ||
const frameRate = stream.avg_frame_rate.split('/'); | ||
fps = parseFloat(frameRate[0]) / parseFloat(frameRate[1]); | ||
} else if (stream.r_frame_rate) { | ||
const frameRate = stream.r_frame_rate.split('/'); | ||
fps = parseFloat(frameRate[0]) / parseFloat(frameRate[1]); | ||
} | ||
} | ||
}); | ||
data = { | ||
audio: hasAudio, | ||
video: hasVideo, | ||
fps: hasVideo ? fps : null, | ||
}; | ||
isDone = true; | ||
}); | ||
deasync.loopWhile(() => !isDone); | ||
} | ||
if (!options?.fps) options.fps = 30; | ||
if (!options?.fps) options.fps = data?.fps || 30; | ||
const args = [ | ||
'-i', | ||
'-', | ||
'-use_wallclock_as_timestamps', | ||
'1', | ||
'-copyts', | ||
'-analyzeduration', | ||
'0', | ||
isStream ? '-' : input, | ||
...FFMPEG_OUTPUT_PREFIX, | ||
'-flags', | ||
@@ -174,6 +223,2 @@ 'low_delay', | ||
if (!isStream) { | ||
args[1] = input; | ||
} | ||
if (options?.hwAccel === true) { | ||
@@ -187,12 +232,3 @@ args.unshift('-hwaccel', 'auto'); | ||
if (typeof input == 'string' && input.startsWith('http')) { | ||
args.unshift( | ||
'-reconnect', | ||
'1', | ||
'-reconnect_at_eof', | ||
'1', | ||
'-reconnect_streamed', | ||
'1', | ||
'-reconnect_delay_max', | ||
'4294', | ||
); | ||
args.unshift(...FFMPEG_INPUT_PREFIX); | ||
} | ||
@@ -202,3 +238,3 @@ | ||
if (this.voiceConnection.videoCodec == 'VP8') { | ||
args.push('-f', 'ivf', '-deadline', 'realtime', '-c:v', 'libvpx'); | ||
args.push(...FFMPEG_VP8_ARGUMENTS); | ||
// Remove '-speed', '5' bc bad quality | ||
@@ -208,24 +244,3 @@ } | ||
if (this.voiceConnection.videoCodec == 'H264') { | ||
args.push( | ||
'-c:v', | ||
'libx264', | ||
'-f', | ||
'h264', | ||
'-tune', | ||
'zerolatency', | ||
// '-pix_fmt', | ||
// 'yuv420p', | ||
'-preset', | ||
options?.presetH26x || 'faster', | ||
'-profile:v', | ||
'baseline', | ||
// '-g', | ||
// `${options?.fps}`, | ||
// '-x264-params', | ||
// `keyint=${options?.fps}:min-keyint=${options?.fps}`, | ||
'-bf', | ||
'0', | ||
'-bsf:v', | ||
'h264_metadata=aud=insert', | ||
); | ||
args.push(...FFMPEG_H264_ARGUMENTS(options)); | ||
} | ||
@@ -251,8 +266,12 @@ | ||
this.emit('debug', `[ffmpeg] Spawn process with args:\n${args.join(' ')}`); | ||
this.emit('debug', `[ffmpeg-video_process] Spawn process with args:\n${args.join(' ')}`); | ||
ffmpeg.process.stderr.on('data', data => { | ||
this.emit('debug', `[ffmpeg]: ${data.toString()}`); | ||
this.emit('debug', `[ffmpeg-video_process]: ${data.toString()}`); | ||
}); | ||
if (data?.audio && options?.includeAudio) { | ||
this.playUnknown(input, options?.audioOptions || {}); | ||
} | ||
switch (this.voiceConnection.videoCodec) { | ||
@@ -266,3 +285,3 @@ case 'VP8': { | ||
default: { | ||
throw new Error('Invalid codec'); | ||
throw new Error('Invalid codec (Supported: VP8, H264)'); | ||
} | ||
@@ -269,0 +288,0 @@ } |
@@ -81,3 +81,3 @@ 'use strict'; | ||
* @property {number} [seek=0] The time to seek to | ||
* @property {number} [fps=30] Video fps | ||
* @property {number} [fps] Video fps | ||
* @property {number} [highWaterMark=12] The maximum number of opus packets to make and store before they are | ||
@@ -84,0 +84,0 @@ * actually needed. See https://nodejs.org/en/docs/guides/backpressuring-in-streams/. Setting this value to |
'use strict'; | ||
const EventEmitter = require('events'); | ||
const { getCiphers } = require('node:crypto'); | ||
const { setTimeout } = require('node:timers'); | ||
@@ -26,3 +27,9 @@ const { Collection } = require('@discordjs/collection'); | ||
const SUPPORTED_MODES = ['aead_aes256_gcm_rtpsize', 'aead_xchacha20_poly1305_rtpsize']; | ||
const SUPPORTED_MODES = ['aead_xchacha20_poly1305_rtpsize']; | ||
// Just in case there's some system that doesn't come with aes-256-gcm, conditionally add it as supported | ||
if (getCiphers().includes('aes-256-gcm')) { | ||
SUPPORTED_MODES.unshift('aead_aes256_gcm_rtpsize'); | ||
} | ||
const SUPPORTED_CODECS = ['VP8', 'H264']; | ||
@@ -29,0 +36,0 @@ |
@@ -212,4 +212,7 @@ 'use strict'; | ||
POLL_ALREADY_EXPIRED: 'This poll has already expired.', | ||
INVALID_OAUTH_OPTIONS: 'Invalid options for authenticating with OAuth2.', | ||
METHOD_WARNING: | ||
'This method is flagged as it may lead to a temporary or permanent account ban. Do not use until further notice.', | ||
}; | ||
for (const [name, message] of Object.entries(Messages)) register(name, message); |
@@ -7,5 +7,4 @@ 'use strict'; | ||
const makeFetchCookie = require('fetch-cookie'); | ||
const FormData = require('form-data'); | ||
const fetchOriginal = require('node-fetch'); | ||
const { CookieJar } = require('tough-cookie'); | ||
const { fetch: fetchOriginal, FormData } = require('undici'); | ||
const { ciphers } = require('../util/Constants'); | ||
@@ -73,3 +72,2 @@ const Util = require('../util/Util'); | ||
let headers = { | ||
authority: 'discord.com', | ||
accept: '*/*', | ||
@@ -85,9 +83,8 @@ 'accept-language': 'en-US', | ||
'x-discord-locale': 'en-US', | ||
'x-discord-timezone': 'Asia/Saigon', | ||
'x-discord-timezone': Intl.DateTimeFormat().resolvedOptions().timeZone, | ||
'x-super-properties': `${Buffer.from(JSON.stringify(this.client.options.ws.properties), 'ascii').toString( | ||
'base64', | ||
)}`, | ||
Referer: 'https://discord.com/channels/@me', | ||
referer: 'https://discord.com/channels/@me', | ||
origin: 'https://discord.com', | ||
'Referrer-Policy': 'strict-origin-when-cross-origin', | ||
...this.client.options.http.headers, | ||
@@ -158,2 +155,3 @@ 'User-Agent': this.fullUserAgent, | ||
signal: controller.signal, | ||
redirect: 'follow', | ||
}).finally(() => clearTimeout(timeout)); | ||
@@ -160,0 +158,0 @@ } |
@@ -71,2 +71,17 @@ 'use strict'; | ||
/** | ||
* A special `40333` JSON error code is returned if your request is blocked by Cloudflare. | ||
* This may be due to a malformed request or improper user agent. | ||
* The response resembles a normal error structure: | ||
* @type {boolean} | ||
* @example | ||
* { | ||
* "message": "internal network error", | ||
* "code": 40333 | ||
* } | ||
*/ | ||
get isBlockedByCloudflare() { | ||
return this.code === 40333; | ||
} | ||
/** | ||
* Flattens an errors object returned from the API into an array. | ||
@@ -73,0 +88,0 @@ * @param {APIError} obj Discord errors object |
@@ -298,7 +298,10 @@ 'use strict'; | ||
* @param {UserResolvable[]} [recipients] Array of recipients | ||
* @returns {Promise<any>} | ||
* @returns {Promise<void>} | ||
*/ | ||
ring(recipients) { | ||
if (!recipients || !Array.isArray(recipients) || recipients.length == 0) recipients = null; | ||
recipients = recipients.map(r => this.client.users.resolveId(r)).filter(r => r && this.recipients.get(r)); | ||
if (!recipients || !Array.isArray(recipients) || recipients.length == 0) { | ||
recipients = null; | ||
} else { | ||
recipients = recipients.map(r => this.client.users.resolveId(r)).filter(r => r && this.recipients.get(r)); | ||
} | ||
return this.client.api.channels(this.id).call.ring.post({ | ||
@@ -305,0 +308,0 @@ data: { |
@@ -483,15 +483,2 @@ 'use strict'; | ||
/** | ||
* Ring the user's phone / PC (call) | ||
* @returns {Promise<void>} | ||
* @deprecated | ||
*/ | ||
ring() { | ||
return this.client.api.channels(this.dmChannel.id).call.ring.post({ | ||
data: { | ||
recipients: [this.id], | ||
}, | ||
}); | ||
} | ||
/** | ||
* Send Friend Request to the user | ||
@@ -498,0 +485,0 @@ * @type {boolean} |
@@ -7,3 +7,3 @@ 'use strict'; | ||
const stream = require('node:stream'); | ||
const fetch = require('node-fetch'); | ||
const { fetch } = require('undici'); | ||
const { Error: DiscordError, TypeError } = require('../errors'); | ||
@@ -10,0 +10,0 @@ const Invite = require('../structures/Invite'); |
@@ -8,3 +8,3 @@ 'use strict'; | ||
const { setTimeout } = require('node:timers'); | ||
const fetch = require('node-fetch'); | ||
const { fetch } = require('undici'); | ||
const WebSocket = require('ws'); | ||
@@ -11,0 +11,0 @@ const { UserAgent } = require('./Constants'); |
@@ -8,3 +8,3 @@ 'use strict'; | ||
const { Collection } = require('@discordjs/collection'); | ||
const fetch = require('node-fetch'); | ||
const { fetch } = require('undici'); | ||
const { Colors, Events } = require('./Constants'); | ||
@@ -11,0 +11,0 @@ const { Error: DiscordError, RangeError, TypeError } = require('../errors'); |
Sorry, the diff of this file is too big to display
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1860194
0.15%13
-7.14%20
-4.76%50315
0.13%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed