update
This commit is contained in:
parent
201e4376ac
commit
609cce3631
@ -1,10 +1,9 @@
|
|||||||
|
/* eslint-disable newline-per-chained-call */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { Buffer } = require('node:buffer');
|
const { Buffer } = require('node:buffer');
|
||||||
const { setTimeout } = require('node:timers');
|
const { setTimeout } = require('node:timers');
|
||||||
const { Collection } = require('@discordjs/collection');
|
const { Collection } = require('@discordjs/collection');
|
||||||
require('lodash.permutations');
|
|
||||||
const _ = require('lodash');
|
|
||||||
const CachedManager = require('./CachedManager');
|
const CachedManager = require('./CachedManager');
|
||||||
const { Error, TypeError, RangeError } = require('../errors');
|
const { Error, TypeError, RangeError } = require('../errors');
|
||||||
const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel');
|
const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel');
|
||||||
@ -191,25 +190,17 @@ class GuildMemberManager extends CachedManager {
|
|||||||
* guild.members.fetch({ query: 'hydra', limit: 1 })
|
* guild.members.fetch({ query: 'hydra', limit: 1 })
|
||||||
* .then(console.log)
|
* .then(console.log)
|
||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
* @see {@link https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Document/FetchGuildMember.md}
|
|
||||||
*/
|
*/
|
||||||
fetch(options) {
|
fetch(options) {
|
||||||
if (!options || (typeof options === 'object' && !('user' in options) && !('query' in options))) {
|
if (!options) {
|
||||||
if (
|
if (
|
||||||
this.guild.members.me.permissions.has('KICK_MEMBERS') ||
|
this.me.permissions.has('KICK_MEMBERS') ||
|
||||||
this.guild.members.me.permissions.has('BAN_MEMBERS') ||
|
this.me.permissions.has('BAN_MEMBERS') ||
|
||||||
this.guild.members.me.permissions.has('MANAGE_ROLES')
|
this.me.permissions.has('MANAGE_ROLES')
|
||||||
) {
|
) {
|
||||||
return this._fetchMany();
|
return this._fetchMany();
|
||||||
} else if (this.guild.memberCount <= 10000) {
|
|
||||||
return this.fetchByMemberSafety();
|
|
||||||
} else {
|
} else {
|
||||||
// NOTE: This is a very slow method, and can take up to 999+ minutes to complete.
|
return this.fetchByMemberSafety();
|
||||||
return this.fetchBruteforce({
|
|
||||||
delay: 50,
|
|
||||||
skipWarn: true,
|
|
||||||
depth: 1,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const user = this.client.users.resolveId(options);
|
const user = this.client.users.resolveId(options);
|
||||||
@ -471,221 +462,6 @@ class GuildMemberManager extends CachedManager {
|
|||||||
return this._add(data, cache);
|
return this._add(data, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Options used to fetch multiple members from a guild.
|
|
||||||
* @typedef {Object} BruteforceOptions
|
|
||||||
* @property {number} [limit=100] Maximum number of members per request
|
|
||||||
* @property {number} [delay=500] Timeout for new requests in ms
|
|
||||||
* @property {number} [depth=1] Permutations length
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches multiple members from the guild.
|
|
||||||
* @param {BruteforceOptions} options Options for the bruteforce
|
|
||||||
* @returns {Collection<Snowflake, GuildMember>} (All) members in the guild
|
|
||||||
* @see https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Document/FetchGuildMember.md
|
|
||||||
* @example
|
|
||||||
* guild.members.fetchBruteforce()
|
|
||||||
* .then(members => console.log(`Fetched ${members.size} members`))
|
|
||||||
* .catch(console.error);
|
|
||||||
*/
|
|
||||||
fetchBruteforce(options = {}) {
|
|
||||||
const defaultQuery = 'abcdefghijklmnopqrstuvwxyz0123456789!"#$%&\'()*+,-./:;<=>?@[]^_`{|}~ ';
|
|
||||||
let dictionary;
|
|
||||||
let limit = 100;
|
|
||||||
let delay = 500;
|
|
||||||
let depth = 1;
|
|
||||||
if (options?.limit) limit = options?.limit;
|
|
||||||
if (options?.delay) delay = options?.delay;
|
|
||||||
if (options?.depth) depth = options?.depth;
|
|
||||||
if (typeof limit !== 'number') throw new TypeError('INVALID_TYPE', 'limit', 'Number');
|
|
||||||
if (limit < 1 || limit > 100) throw new RangeError('INVALID_RANGE_QUERY_MEMBER');
|
|
||||||
if (typeof delay !== 'number') throw new TypeError('INVALID_TYPE', 'delay', 'Number');
|
|
||||||
if (typeof depth !== 'number') throw new TypeError('INVALID_TYPE', 'depth', 'Number');
|
|
||||||
if (depth < 1) throw new RangeError('INVALID_RANGE_QUERY_MEMBER');
|
|
||||||
if (depth > 2) {
|
|
||||||
console.warn(`[WARNING] GuildMemberManager#fetchBruteforce: depth greater than 2, can lead to very slow speeds`);
|
|
||||||
}
|
|
||||||
if (delay < 500 && !options?.skipWarn) {
|
|
||||||
console.warn(
|
|
||||||
`[WARNING] GuildMemberManager#fetchBruteforce: delay is less than 500ms, this may cause rate limits.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let skipValues = [];
|
|
||||||
// eslint-disable-next-line no-async-promise-executor
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
for (let i = 1; i <= depth; i++) {
|
|
||||||
dictionary = _(defaultQuery)
|
|
||||||
.permutations(i)
|
|
||||||
.map(v => _.join(v, ''))
|
|
||||||
.value();
|
|
||||||
for (const query of dictionary) {
|
|
||||||
if (this.guild.members.cache.size >= this.guild.memberCount) break;
|
|
||||||
this.client.emit(
|
|
||||||
'debug',
|
|
||||||
`[INFO] GuildMemberManager#fetchBruteforce: Querying ${query}, Skip: [${skipValues.join(', ')}]`,
|
|
||||||
);
|
|
||||||
if (skipValues.some(v => query.startsWith(v))) continue;
|
|
||||||
await this._fetchMany({ query, limit })
|
|
||||||
.then(members => {
|
|
||||||
if (members.size === 0) skipValues.push(query);
|
|
||||||
})
|
|
||||||
.catch(reject);
|
|
||||||
await this.guild.client.sleep(delay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve(this.guild.members.cache);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Experimental method to fetch members from the guild.
|
|
||||||
* <info>Lists up to 10000 members of the guild.</info>
|
|
||||||
* @param {number} [timeout=15_000] Timeout for receipt of members in ms
|
|
||||||
* @returns {Promise<Collection<Snowflake, GuildMember>>}
|
|
||||||
*/
|
|
||||||
fetchByMemberSafety(timeout = 15_000) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const nonce = SnowflakeUtil.generate();
|
|
||||||
let timeout_ = setTimeout(() => {
|
|
||||||
this.client.removeListener(Events.GUILD_MEMBER_LIST_UPDATE, handler);
|
|
||||||
resolve(this.guild.members.cache);
|
|
||||||
}, timeout).unref();
|
|
||||||
const handler = (members, guild, raw) => {
|
|
||||||
if (guild.id == this.guild.id && raw.nonce == nonce) {
|
|
||||||
if (members.size > 0) {
|
|
||||||
this.client.ws.broadcast({
|
|
||||||
op: 35,
|
|
||||||
d: {
|
|
||||||
guild_id: this.guild.id,
|
|
||||||
query: '',
|
|
||||||
continuation_token: members.first()?.id,
|
|
||||||
nonce,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
clearTimeout(timeout_);
|
|
||||||
this.client.removeListener(Events.GUILD_MEMBER_LIST_UPDATE, handler);
|
|
||||||
resolve(this.guild.members.cache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.client.on('guildMembersChunk', handler);
|
|
||||||
this.client.ws.broadcast({
|
|
||||||
op: 35,
|
|
||||||
d: {
|
|
||||||
guild_id: this.guild.id,
|
|
||||||
query: '',
|
|
||||||
continuation_token: null,
|
|
||||||
nonce,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches multiple members from the guild in the channel.
|
|
||||||
* @param {GuildTextChannelResolvable} channel The channel to get members from (Members has VIEW_CHANNEL permission)
|
|
||||||
* @param {number} [offset=0] Start index of the members to get
|
|
||||||
* @param {boolean} [double=false] Whether to use double range
|
|
||||||
* @param {number} [retryMax=3] Number of retries
|
|
||||||
* @param {number} [time=10e3] Timeout for receipt of members
|
|
||||||
* @returns {Collection<Snowflake, GuildMember>} Members in the guild
|
|
||||||
* @see {@link https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Document/FetchGuildMember.md}
|
|
||||||
* @example
|
|
||||||
* const guild = client.guilds.cache.get('id');
|
|
||||||
* const channel = guild.channels.cache.get('id');
|
|
||||||
* // Overlap (slow)
|
|
||||||
* for (let index = 0; index <= guild.memberCount; index += 100) {
|
|
||||||
* await guild.members.fetchMemberList(channel, index, index !== 100).catch(() => {});
|
|
||||||
* await client.sleep(500);
|
|
||||||
* }
|
|
||||||
* // Non-overlap (fast)
|
|
||||||
* for (let index = 0; index <= guild.memberCount; index += 200) {
|
|
||||||
* await guild.members.fetchMemberList(channel, index == 0 ? 100 : index, index !== 100).catch(() => {});
|
|
||||||
* await client.sleep(500);
|
|
||||||
* }
|
|
||||||
* console.log(guild.members.cache.size); // will print the number of members in the guild
|
|
||||||
*/
|
|
||||||
fetchMemberList(channel, offset = 0, double = false, retryMax = 3, time = 10_000) {
|
|
||||||
const channel_ = this.guild.channels.resolve(channel);
|
|
||||||
if (!channel_?.isText()) throw new TypeError('INVALID_TYPE', 'channel', 'GuildTextChannelResolvable');
|
|
||||||
if (typeof offset !== 'number') throw new TypeError('INVALID_TYPE', 'offset', 'Number');
|
|
||||||
if (typeof time !== 'number') throw new TypeError('INVALID_TYPE', 'time', 'Number');
|
|
||||||
if (typeof retryMax !== 'number') throw new TypeError('INVALID_TYPE', 'retryMax', 'Number');
|
|
||||||
if (retryMax < 1) throw new RangeError('INVALID_RANGE_RETRY');
|
|
||||||
if (typeof double !== 'boolean') throw new TypeError('INVALID_TYPE', 'double', 'Boolean');
|
|
||||||
// TODO: if (this.guild.large) throw new Error('GUILD_IS_LARGE');
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const default_ = [[0, 99]];
|
|
||||||
const fetchedMembers = new Collection();
|
|
||||||
if (offset > 99) {
|
|
||||||
// eslint-disable-next-line no-unused-expressions
|
|
||||||
double
|
|
||||||
? default_.push([offset, offset + 99], [offset + 100, offset + 199])
|
|
||||||
: default_.push([offset, offset + 99]);
|
|
||||||
}
|
|
||||||
let retry = 0;
|
|
||||||
const handler = (members, guild, type, raw) => {
|
|
||||||
timeout.refresh();
|
|
||||||
if (guild.id !== this.guild.id) return;
|
|
||||||
if (type == 'INVALIDATE' && offset > 100) {
|
|
||||||
if (retry < retryMax) {
|
|
||||||
this.guild.shard.send({
|
|
||||||
op: Opcodes.GUILD_SUBSCRIPTIONS,
|
|
||||||
d: {
|
|
||||||
guild_id: this.guild.id,
|
|
||||||
typing: true,
|
|
||||||
threads: true,
|
|
||||||
activities: true,
|
|
||||||
channels: {
|
|
||||||
[channel_.id]: default_,
|
|
||||||
},
|
|
||||||
thread_member_lists: [],
|
|
||||||
members: [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
retry++;
|
|
||||||
} else {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
this.client.removeListener(Events.GUILD_MEMBER_LIST_UPDATE, handler);
|
|
||||||
this.client.decrementMaxListeners();
|
|
||||||
reject(new Error('INVALIDATE_MEMBER', raw.ops[0].range));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const member of members.values()) {
|
|
||||||
fetchedMembers.set(member.id, member);
|
|
||||||
}
|
|
||||||
clearTimeout(timeout);
|
|
||||||
this.client.removeListener(Events.GUILD_MEMBER_LIST_UPDATE, handler);
|
|
||||||
this.client.decrementMaxListeners();
|
|
||||||
resolve(fetchedMembers);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
this.client.removeListener(Events.GUILD_MEMBER_LIST_UPDATE, handler);
|
|
||||||
this.client.decrementMaxListeners();
|
|
||||||
reject(new Error('GUILD_MEMBERS_TIMEOUT'));
|
|
||||||
}, time).unref();
|
|
||||||
this.client.incrementMaxListeners();
|
|
||||||
this.client.on(Events.GUILD_MEMBER_LIST_UPDATE, handler);
|
|
||||||
this.guild.shard.send({
|
|
||||||
op: Opcodes.GUILD_SUBSCRIPTIONS,
|
|
||||||
d: {
|
|
||||||
guild_id: this.guild.id,
|
|
||||||
typing: true,
|
|
||||||
threads: true,
|
|
||||||
activities: true,
|
|
||||||
channels: {
|
|
||||||
[channel_.id]: default_,
|
|
||||||
},
|
|
||||||
thread_member_lists: [],
|
|
||||||
members: [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a role to a member.
|
* Adds a role to a member.
|
||||||
* @param {GuildMemberResolvable} user The user to add the role from
|
* @param {GuildMemberResolvable} user The user to add the role from
|
||||||
@ -718,6 +494,51 @@ class GuildMemberManager extends CachedManager {
|
|||||||
return this.resolve(user) ?? this.client.users.resolve(user) ?? userId;
|
return this.resolve(user) ?? this.client.users.resolve(user) ?? userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Experimental method to fetch members from the guild.
|
||||||
|
* <info>Lists up to 10000 members of the guild.</info>
|
||||||
|
* @param {number} [timeout=15_000] Timeout for receipt of members in ms
|
||||||
|
* @returns {Promise<Collection<Snowflake, GuildMember>>}
|
||||||
|
*/
|
||||||
|
fetchByMemberSafety(timeout = 15_000) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const nonce = SnowflakeUtil.generate();
|
||||||
|
let timeout_ = setTimeout(() => {
|
||||||
|
this.client.removeListener(Events.GUILD_MEMBER_LIST_UPDATE, handler);
|
||||||
|
resolve(this.guild.members.cache);
|
||||||
|
}, timeout).unref();
|
||||||
|
const handler = (members, guild, raw) => {
|
||||||
|
if (guild.id == this.guild.id && raw.nonce == nonce) {
|
||||||
|
if (members.size > 0) {
|
||||||
|
this.client.ws.broadcast({
|
||||||
|
op: Opcodes.SEARCH_RECENT_MEMBERS,
|
||||||
|
d: {
|
||||||
|
guild_id: this.guild.id,
|
||||||
|
query: '',
|
||||||
|
continuation_token: members.first()?.id,
|
||||||
|
nonce,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
clearTimeout(timeout_);
|
||||||
|
this.client.removeListener(Events.GUILD_MEMBER_LIST_UPDATE, handler);
|
||||||
|
resolve(this.guild.members.cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.client.on('guildMembersChunk', handler);
|
||||||
|
this.client.ws.broadcast({
|
||||||
|
op: Opcodes.SEARCH_RECENT_MEMBERS,
|
||||||
|
d: {
|
||||||
|
guild_id: this.guild.id,
|
||||||
|
query: '',
|
||||||
|
continuation_token: null,
|
||||||
|
nonce,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_fetchMany({
|
_fetchMany({
|
||||||
limit = 0,
|
limit = 0,
|
||||||
withPresences: presences = true,
|
withPresences: presences = true,
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { Collection } = require('@discordjs/collection');
|
const { Collection } = require('@discordjs/collection');
|
||||||
const { joinVoiceChannel, entersState, VoiceConnectionStatus } = require('@discordjs/voice');
|
|
||||||
const { Channel } = require('./Channel');
|
const { Channel } = require('./Channel');
|
||||||
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
||||||
const InteractionManager = require('../managers/InteractionManager');
|
|
||||||
const MessageManager = require('../managers/MessageManager');
|
const MessageManager = require('../managers/MessageManager');
|
||||||
const { Status, Opcodes } = require('../util/Constants');
|
const { Opcodes, Status } = require('../util/Constants');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a direct message channel between two users.
|
* Represents a direct message channel between two users.
|
||||||
@ -25,12 +23,6 @@ class DMChannel extends Channel {
|
|||||||
* @type {MessageManager}
|
* @type {MessageManager}
|
||||||
*/
|
*/
|
||||||
this.messages = new MessageManager(this);
|
this.messages = new MessageManager(this);
|
||||||
|
|
||||||
/**
|
|
||||||
* A manager of the interactions sent to this channel
|
|
||||||
* @type {InteractionManager}
|
|
||||||
*/
|
|
||||||
this.interactions = new InteractionManager(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_patch(data) {
|
_patch(data) {
|
||||||
@ -65,7 +57,7 @@ class DMChannel extends Channel {
|
|||||||
if ('is_message_request' in data) {
|
if ('is_message_request' in data) {
|
||||||
/**
|
/**
|
||||||
* Whether the channel is a message request
|
* Whether the channel is a message request
|
||||||
* @type {boolean}
|
* @type {?boolean}
|
||||||
*/
|
*/
|
||||||
this.messageRequest = data.is_message_request;
|
this.messageRequest = data.is_message_request;
|
||||||
}
|
}
|
||||||
@ -138,78 +130,6 @@ class DMChannel extends Channel {
|
|||||||
return this.recipient.toString();
|
return this.recipient.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are here only for documentation purposes - they are implemented by TextBasedChannel
|
|
||||||
/* eslint-disable no-empty-function */
|
|
||||||
get lastMessage() {}
|
|
||||||
get lastPinAt() {}
|
|
||||||
send() {}
|
|
||||||
sendTyping() {}
|
|
||||||
createMessageCollector() {}
|
|
||||||
awaitMessages() {}
|
|
||||||
createMessageComponentCollector() {}
|
|
||||||
awaitMessageComponent() {}
|
|
||||||
sendSlash() {}
|
|
||||||
searchInteraction() {}
|
|
||||||
// Doesn't work on DM channels; bulkDelete() {}
|
|
||||||
// Doesn't work on DM channels; setRateLimitPerUser() {}
|
|
||||||
// Doesn't work on DM channels; setNSFW() {}
|
|
||||||
// Testing feature: Call
|
|
||||||
// URL: https://discord.com/api/v9/channels/:DMchannelId/call/ring
|
|
||||||
/**
|
|
||||||
* Call this DMChannel. Return discordjs/voice VoiceConnection
|
|
||||||
* @param {CallOptions} options Options for the call
|
|
||||||
* @returns {Promise<VoiceConnection>}
|
|
||||||
*/
|
|
||||||
call(options = {}) {
|
|
||||||
options = Object.assign(
|
|
||||||
{
|
|
||||||
ring: true,
|
|
||||||
},
|
|
||||||
options || {},
|
|
||||||
);
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!this.client.options.patchVoice) {
|
|
||||||
reject(
|
|
||||||
new Error(
|
|
||||||
'VOICE_NOT_PATCHED',
|
|
||||||
'Enable voice patching in client options\nhttps://discordjs-self-v13.netlify.app/#/docs/docs/main/typedef/ClientOptions',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (options.ring) {
|
|
||||||
this.ring();
|
|
||||||
}
|
|
||||||
const connection = joinVoiceChannel({
|
|
||||||
channelId: this.id,
|
|
||||||
guildId: null,
|
|
||||||
adapterCreator: this.voiceAdapterCreator,
|
|
||||||
selfDeaf: options.selfDeaf ?? false,
|
|
||||||
selfMute: options.selfMute ?? false,
|
|
||||||
});
|
|
||||||
entersState(connection, VoiceConnectionStatus.Ready, 30000)
|
|
||||||
.then(connection => {
|
|
||||||
resolve(connection);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
connection.destroy();
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ring the user's phone / PC (call)
|
|
||||||
* @returns {Promise<any>}
|
|
||||||
*/
|
|
||||||
ring() {
|
|
||||||
return this.client.api.channels(this.id).call.ring.post({
|
|
||||||
data: {
|
|
||||||
recipients: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync VoiceState of this DMChannel.
|
* Sync VoiceState of this DMChannel.
|
||||||
* @returns {undefined}
|
* @returns {undefined}
|
||||||
@ -222,6 +142,19 @@ class DMChannel extends Channel {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ring the user's phone / PC (call)
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
ring() {
|
||||||
|
return this.client.api.channels(this.id).call.ring.post({
|
||||||
|
data: {
|
||||||
|
recipients: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user in this voice-based channel
|
* The user in this voice-based channel
|
||||||
* @type {Collection<Snowflake, User>}
|
* @type {Collection<Snowflake, User>}
|
||||||
@ -236,18 +169,7 @@ class DMChannel extends Channel {
|
|||||||
}
|
}
|
||||||
return coll;
|
return coll;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Get connection to current call
|
|
||||||
* @type {?VoiceConnection}
|
|
||||||
* @readonly
|
|
||||||
*/
|
|
||||||
get voiceConnection() {
|
|
||||||
const check = this.client.callVoice?.joinConfig?.channelId == this.id;
|
|
||||||
if (check) {
|
|
||||||
return this.client.callVoice;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Get current shard
|
* Get current shard
|
||||||
* @type {WebSocketShard}
|
* @type {WebSocketShard}
|
||||||
@ -256,6 +178,7 @@ class DMChannel extends Channel {
|
|||||||
get shard() {
|
get shard() {
|
||||||
return this.client.ws.shards.first();
|
return this.client.ws.shards.first();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The voice state adapter for this client that can be used with @discordjs/voice to play audio in DM / Group DM channels.
|
* The voice state adapter for this client that can be used with @discordjs/voice to play audio in DM / Group DM channels.
|
||||||
* @type {?Function}
|
* @type {?Function}
|
||||||
@ -276,14 +199,19 @@ class DMChannel extends Channel {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These are here only for documentation purposes - they are implemented by TextBasedChannel
|
||||||
|
/* eslint-disable no-empty-function */
|
||||||
|
get lastMessage() {}
|
||||||
|
get lastPinAt() {}
|
||||||
|
send() {}
|
||||||
|
sendTyping() {}
|
||||||
|
createMessageCollector() {}
|
||||||
|
awaitMessages() {}
|
||||||
|
// Doesn't work on DM channels; setRateLimitPerUser() {}
|
||||||
|
// Doesn't work on DM channels; setNSFW() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextBasedChannel.applyToClass(DMChannel, true, [
|
TextBasedChannel.applyToClass(DMChannel, true, ['fetchWebhooks', 'createWebhook', 'setRateLimitPerUser', 'setNSFW']);
|
||||||
'bulkDelete',
|
|
||||||
'fetchWebhooks',
|
|
||||||
'createWebhook',
|
|
||||||
'setRateLimitPerUser',
|
|
||||||
'setNSFW',
|
|
||||||
]);
|
|
||||||
|
|
||||||
module.exports = DMChannel;
|
module.exports = DMChannel;
|
||||||
|
@ -5,6 +5,7 @@ const { Channel } = require('./Channel');
|
|||||||
const Invite = require('./Invite');
|
const Invite = require('./Invite');
|
||||||
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
||||||
const MessageManager = require('../managers/MessageManager');
|
const MessageManager = require('../managers/MessageManager');
|
||||||
|
const { Status, Opcodes } = require('../util/Constants');
|
||||||
const DataResolver = require('../util/DataResolver');
|
const DataResolver = require('../util/DataResolver');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -291,6 +292,79 @@ class GroupDMChannel extends Channel {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ring the user's phone / PC (call)
|
||||||
|
* @param {UserResolvable[]} [recipients] Array of recipients
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
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));
|
||||||
|
return this.client.api.channels(this.id).call.ring.post({
|
||||||
|
data: {
|
||||||
|
recipients,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync VoiceState of this Group DMChannel.
|
||||||
|
* @returns {undefined}
|
||||||
|
*/
|
||||||
|
sync() {
|
||||||
|
this.client.ws.broadcast({
|
||||||
|
op: Opcodes.DM_UPDATE,
|
||||||
|
d: {
|
||||||
|
channel_id: this.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user in this voice-based channel
|
||||||
|
* @type {Collection<Snowflake, User>}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get voiceUsers() {
|
||||||
|
const coll = new Collection();
|
||||||
|
for (const state of this.client.voiceStates.cache.values()) {
|
||||||
|
if (state.channelId === this.id && state.user) {
|
||||||
|
coll.set(state.id, state.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return coll;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current shard
|
||||||
|
* @type {WebSocketShard}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get shard() {
|
||||||
|
return this.client.ws.shards.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The voice state adapter for this client that can be used with @discordjs/voice to play audio in DM / Group DM channels.
|
||||||
|
* @type {?Function}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get voiceAdapterCreator() {
|
||||||
|
return methods => {
|
||||||
|
this.client.voice.adapters.set(this.id, methods);
|
||||||
|
return {
|
||||||
|
sendPayload: data => {
|
||||||
|
if (this.shard.status !== Status.READY) return false;
|
||||||
|
this.shard.send(data);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
destroy: () => {
|
||||||
|
this.client.voice.adapters.delete(this.id);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// These are here only for documentation purposes - they are implemented by TextBasedChannel
|
// These are here only for documentation purposes - they are implemented by TextBasedChannel
|
||||||
/* eslint-disable no-empty-function */
|
/* eslint-disable no-empty-function */
|
||||||
get lastMessage() {}
|
get lastMessage() {}
|
||||||
|
Loading…
Reference in New Issue
Block a user