update
This commit is contained in:
parent
af1f561cfd
commit
4a1d615673
@ -113,14 +113,13 @@ class ChannelManager extends CachedManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.client.api.channels(id).get();
|
const data = await this.client.api.channels(id).get();
|
||||||
// Delete in cache
|
|
||||||
this._remove(id);
|
|
||||||
return this._add(data, null, { cache, allowUnknownGuild });
|
return this._add(data, null, { cache, allowUnknownGuild });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create Group DM
|
* Create Group DM
|
||||||
* @param {UserResolvable[]} recipients Array of recipients
|
* @param {UserResolvable[]} recipients Array of recipients
|
||||||
* @returns {Promise<PartialGroupDMChannel>} Channel
|
* @returns {Promise<GroupDMChannel>} Channel
|
||||||
*/
|
*/
|
||||||
async createGroupDM(recipients) {
|
async createGroupDM(recipients) {
|
||||||
// Check
|
// Check
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
const ThreadManager = require('./ThreadManager');
|
const ThreadManager = require('./ThreadManager');
|
||||||
const { TypeError } = require('../errors');
|
const { TypeError } = require('../errors');
|
||||||
const MessagePayload = require('../structures/MessagePayload');
|
const MessagePayload = require('../structures/MessagePayload');
|
||||||
const { resolveAutoArchiveMaxLimit, getAttachments, uploadFile } = require('../util/Util');
|
const { resolveAutoArchiveMaxLimit, getUploadURL, uploadFile } = require('../util/Util');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages API methods for threads in forum channels and stores their cache.
|
* Manages API methods for threads in forum channels and stores their cache.
|
||||||
@ -20,7 +20,7 @@ class GuildForumThreadManager extends ThreadManager {
|
|||||||
* @typedef {BaseMessageOptions} GuildForumThreadMessageCreateOptions
|
* @typedef {BaseMessageOptions} GuildForumThreadMessageCreateOptions
|
||||||
* @property {StickerResolvable} [stickers] The stickers to send with the message
|
* @property {StickerResolvable} [stickers] The stickers to send with the message
|
||||||
* @property {BitFieldResolvable} [flags] The flags to send with the message.
|
* @property {BitFieldResolvable} [flags] The flags to send with the message.
|
||||||
* Only `SUPPRESS_EMBEDS`, `SUPPRESS_NOTIFICATIONS` and `IS_VOICE_MESSAGE` can be set.
|
* Only `SUPPRESS_EMBEDS` and `SUPPRESS_NOTIFICATIONS` can be set.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,35 +63,29 @@ class GuildForumThreadManager extends ThreadManager {
|
|||||||
let messagePayload;
|
let messagePayload;
|
||||||
|
|
||||||
if (message instanceof MessagePayload) {
|
if (message instanceof MessagePayload) {
|
||||||
messagePayload = await message.resolveData();
|
messagePayload = message.resolveData();
|
||||||
} else {
|
} else {
|
||||||
messagePayload = await MessagePayload.create(this, message).resolveData();
|
messagePayload = MessagePayload.create(this, message).resolveData();
|
||||||
}
|
}
|
||||||
|
|
||||||
let { data: body, files } = await messagePayload.resolveFiles();
|
const { data: body, files } = await messagePayload.resolveFiles();
|
||||||
|
|
||||||
if (typeof message == 'object' && typeof message.usingNewAttachmentAPI !== 'boolean') {
|
// New API
|
||||||
message.usingNewAttachmentAPI = this.client.options.usingNewAttachmentAPI;
|
const attachments = await getUploadURL(this.client, this.channel.id, files);
|
||||||
}
|
const requestPromises = attachments.map(async attachment => {
|
||||||
|
await uploadFile(files[attachment.id].file, attachment.upload_url);
|
||||||
if (message?.usingNewAttachmentAPI === true) {
|
return {
|
||||||
const attachments = await getAttachments(this.client, this.channel.id, ...files);
|
id: attachment.id,
|
||||||
const requestPromises = attachments.map(async attachment => {
|
filename: files[attachment.id].name,
|
||||||
await uploadFile(files[attachment.id].file, attachment.upload_url);
|
uploaded_filename: attachment.upload_filename,
|
||||||
return {
|
description: files[attachment.id].description,
|
||||||
id: attachment.id,
|
duration_secs: files[attachment.id].duration_secs,
|
||||||
filename: files[attachment.id].name,
|
waveform: files[attachment.id].waveform,
|
||||||
uploaded_filename: attachment.upload_filename,
|
};
|
||||||
description: files[attachment.id].description,
|
});
|
||||||
duration_secs: files[attachment.id].duration_secs,
|
const attachmentsData = await Promise.all(requestPromises);
|
||||||
waveform: files[attachment.id].waveform,
|
attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id));
|
||||||
};
|
data.attachments = attachmentsData;
|
||||||
});
|
|
||||||
const attachmentsData = await Promise.all(requestPromises);
|
|
||||||
attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id));
|
|
||||||
body.attachments = attachmentsData;
|
|
||||||
files = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (autoArchiveDuration === 'MAX') autoArchiveDuration = resolveAutoArchiveMaxLimit(this.channel.guild);
|
if (autoArchiveDuration === 'MAX') autoArchiveDuration = resolveAutoArchiveMaxLimit(this.channel.guild);
|
||||||
|
|
||||||
@ -103,7 +97,7 @@ class GuildForumThreadManager extends ThreadManager {
|
|||||||
applied_tags: appliedTags,
|
applied_tags: appliedTags,
|
||||||
message: body,
|
message: body,
|
||||||
},
|
},
|
||||||
files,
|
files: [],
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const { Collection } = require('@discordjs/collection');
|
const { Collection } = require('@discordjs/collection');
|
||||||
const CachedManager = require('./CachedManager');
|
const CachedManager = require('./CachedManager');
|
||||||
const { TypeError, Error } = require('../errors');
|
const { TypeError } = require('../errors');
|
||||||
const { Message } = require('../structures/Message');
|
const { Message } = require('../structures/Message');
|
||||||
const MessagePayload = require('../structures/MessagePayload');
|
const MessagePayload = require('../structures/MessagePayload');
|
||||||
const Util = require('../util/Util');
|
const Util = require('../util/Util');
|
||||||
@ -123,38 +123,32 @@ class MessageManager extends CachedManager {
|
|||||||
const messageId = this.resolveId(message);
|
const messageId = this.resolveId(message);
|
||||||
if (!messageId) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
if (!messageId) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||||
|
|
||||||
let messagePayload;
|
const { data, files } = await (options instanceof MessagePayload
|
||||||
if (options instanceof MessagePayload) {
|
? options
|
||||||
messagePayload = await options.resolveData();
|
: MessagePayload.create(message instanceof Message ? message : this, options)
|
||||||
} else {
|
)
|
||||||
messagePayload = await MessagePayload.create(message instanceof Message ? message : this, options).resolveData();
|
.resolveData()
|
||||||
}
|
.resolveFiles();
|
||||||
let { data, files } = await messagePayload.resolveFiles();
|
|
||||||
|
|
||||||
if (typeof options == 'object' && typeof options.usingNewAttachmentAPI !== 'boolean') {
|
// New API
|
||||||
options.usingNewAttachmentAPI = this.client.options.usingNewAttachmentAPI;
|
const attachments = await Util.getUploadURL(this.client, this.channel.id, files);
|
||||||
}
|
const requestPromises = attachments.map(async attachment => {
|
||||||
|
await Util.uploadFile(files[attachment.id].file, attachment.upload_url);
|
||||||
|
return {
|
||||||
|
id: attachment.id,
|
||||||
|
filename: files[attachment.id].name,
|
||||||
|
uploaded_filename: attachment.upload_filename,
|
||||||
|
description: files[attachment.id].description,
|
||||||
|
duration_secs: files[attachment.id].duration_secs,
|
||||||
|
waveform: files[attachment.id].waveform,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const attachmentsData = await Promise.all(requestPromises);
|
||||||
|
attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id));
|
||||||
|
data.attachments = attachmentsData;
|
||||||
|
// Empty Files
|
||||||
|
|
||||||
if (options?.usingNewAttachmentAPI === true) {
|
const d = await this.client.api.channels[this.channel.id].messages[messageId].patch({ data });
|
||||||
const attachments = await Util.getAttachments(this.client, this.channel.id, ...files);
|
|
||||||
const requestPromises = attachments.map(async attachment => {
|
|
||||||
await Util.uploadFile(files[attachment.id].file, attachment.upload_url);
|
|
||||||
return {
|
|
||||||
id: attachment.id,
|
|
||||||
filename: files[attachment.id].name,
|
|
||||||
uploaded_filename: attachment.upload_filename,
|
|
||||||
description: files[attachment.id].description,
|
|
||||||
duration_secs: files[attachment.id].duration_secs,
|
|
||||||
waveform: files[attachment.id].waveform,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const attachmentsData = await Promise.all(requestPromises);
|
|
||||||
attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id));
|
|
||||||
data.attachments = attachmentsData;
|
|
||||||
files = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const d = await this.client.api.channels[this.channel.id].messages[messageId].patch({ data, files });
|
|
||||||
|
|
||||||
const existing = this.cache.get(messageId);
|
const existing = this.cache.get(messageId);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
@ -251,12 +245,16 @@ class MessageManager extends CachedManager {
|
|||||||
const existing = this.cache.get(messageId);
|
const existing = this.cache.get(messageId);
|
||||||
if (existing && !existing.partial) return existing;
|
if (existing && !existing.partial) return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://discord.com/api/v9/channels/:id/messages?limit=50&around=:msgid
|
// https://discord.com/api/v9/channels/:id/messages?limit=50&around=:msgid
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this._fetchMany({
|
this._fetchMany(
|
||||||
around: messageId,
|
{
|
||||||
limit: 50,
|
around: messageId,
|
||||||
})
|
limit: 50,
|
||||||
|
},
|
||||||
|
cache,
|
||||||
|
)
|
||||||
.then(data_ =>
|
.then(data_ =>
|
||||||
data_.has(messageId) ? resolve(data_.get(messageId)) : reject(new Error('MESSAGE_ID_NOT_FOUND')),
|
data_.has(messageId) ? resolve(data_.get(messageId)) : reject(new Error('MESSAGE_ID_NOT_FOUND')),
|
||||||
)
|
)
|
||||||
@ -264,13 +262,6 @@ class MessageManager extends CachedManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _fetchMany(options = {}, cache) {
|
|
||||||
const data = await this.client.api.channels[this.channel.id].messages.get({ query: options });
|
|
||||||
const messages = new Collection();
|
|
||||||
for (const message of data) messages.set(message.id, this._add(message, cache));
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} MessageSearchOptions
|
* @typedef {object} MessageSearchOptions
|
||||||
* @property {Array<UserResolvable>} [authors] An array of author to filter by
|
* @property {Array<UserResolvable>} [authors] An array of author to filter by
|
||||||
@ -388,6 +379,13 @@ class MessageManager extends CachedManager {
|
|||||||
total: data.total_results,
|
total: data.total_results,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _fetchMany(options = {}, cache) {
|
||||||
|
const data = await this.client.api.channels[this.channel.id].messages.get({ query: options });
|
||||||
|
const messages = new Collection();
|
||||||
|
for (const message of data) messages.set(message.id, this._add(message, cache));
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = MessageManager;
|
module.exports = MessageManager;
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const process = require('node:process');
|
const process = require('node:process');
|
||||||
|
const { setTimeout } = require('node:timers');
|
||||||
const { Collection } = require('@discordjs/collection');
|
const { Collection } = require('@discordjs/collection');
|
||||||
const Base = require('./Base');
|
const Base = require('./Base');
|
||||||
const BaseMessageComponent = require('./BaseMessageComponent');
|
const BaseMessageComponent = require('./BaseMessageComponent');
|
||||||
const ClientApplication = require('./ClientApplication');
|
|
||||||
const InteractionCollector = require('./InteractionCollector');
|
|
||||||
const MessageAttachment = require('./MessageAttachment');
|
const MessageAttachment = require('./MessageAttachment');
|
||||||
const MessageButton = require('./MessageButton');
|
|
||||||
const Embed = require('./MessageEmbed');
|
const Embed = require('./MessageEmbed');
|
||||||
const Mentions = require('./MessageMentions');
|
const Mentions = require('./MessageMentions');
|
||||||
const MessagePayload = require('./MessagePayload');
|
const MessagePayload = require('./MessagePayload');
|
||||||
const MessageSelectMenu = require('./MessageSelectMenu');
|
|
||||||
const ReactionCollector = require('./ReactionCollector');
|
const ReactionCollector = require('./ReactionCollector');
|
||||||
const { Sticker } = require('./Sticker');
|
const { Sticker } = require('./Sticker');
|
||||||
|
const Application = require('./interfaces/Application');
|
||||||
const { Error } = require('../errors');
|
const { Error } = require('../errors');
|
||||||
const ReactionManager = require('../managers/ReactionManager');
|
const ReactionManager = require('../managers/ReactionManager');
|
||||||
const { InteractionTypes, MessageTypes, SystemMessageTypes, MaxBulkDeletableMessageAge } = require('../util/Constants');
|
const {
|
||||||
|
InteractionTypes,
|
||||||
|
MessageTypes,
|
||||||
|
SystemMessageTypes,
|
||||||
|
MessageComponentTypes,
|
||||||
|
Events,
|
||||||
|
} = require('../util/Constants');
|
||||||
const MessageFlags = require('../util/MessageFlags');
|
const MessageFlags = require('../util/MessageFlags');
|
||||||
const Permissions = require('../util/Permissions');
|
const Permissions = require('../util/Permissions');
|
||||||
const SnowflakeUtil = require('../util/SnowflakeUtil');
|
const SnowflakeUtil = require('../util/SnowflakeUtil');
|
||||||
@ -256,9 +260,9 @@ class Message extends Base {
|
|||||||
if ('application' in data) {
|
if ('application' in data) {
|
||||||
/**
|
/**
|
||||||
* Supplemental application information for group activities
|
* Supplemental application information for group activities
|
||||||
* @type {?ClientApplication}
|
* @type {?Application}
|
||||||
*/
|
*/
|
||||||
this.groupActivityApplication = new ClientApplication(this.client, data.application);
|
this.groupActivityApplication = new Application(this.client, data.application);
|
||||||
} else {
|
} else {
|
||||||
this.groupActivityApplication ??= null;
|
this.groupActivityApplication ??= null;
|
||||||
}
|
}
|
||||||
@ -533,65 +537,6 @@ class Message extends Base {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {CollectorOptions} MessageComponentCollectorOptions
|
|
||||||
* @property {MessageComponentType} [componentType] The type of component to listen for
|
|
||||||
* @property {number} [max] The maximum total amount of interactions to collect
|
|
||||||
* @property {number} [maxComponents] The maximum number of components to collect
|
|
||||||
* @property {number} [maxUsers] The maximum number of users to interact
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a message component interaction collector.
|
|
||||||
* @param {MessageComponentCollectorOptions} [options={}] Options to send to the collector
|
|
||||||
* @returns {InteractionCollector}
|
|
||||||
* @example
|
|
||||||
* // Create a message component interaction collector
|
|
||||||
* const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId';
|
|
||||||
* const collector = message.createMessageComponentCollector({ filter, time: 15_000 });
|
|
||||||
* collector.on('collect', i => console.log(`Collected ${i.customId}`));
|
|
||||||
* collector.on('end', collected => console.log(`Collected ${collected.size} items`));
|
|
||||||
*/
|
|
||||||
createMessageComponentCollector(options = {}) {
|
|
||||||
return new InteractionCollector(this.client, {
|
|
||||||
...options,
|
|
||||||
interactionType: InteractionTypes.MESSAGE_COMPONENT,
|
|
||||||
message: this,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An object containing the same properties as CollectorOptions, but a few more:
|
|
||||||
* @typedef {Object} AwaitMessageComponentOptions
|
|
||||||
* @property {CollectorFilter} [filter] The filter applied to this collector
|
|
||||||
* @property {number} [time] Time to wait for an interaction before rejecting
|
|
||||||
* @property {MessageComponentType} [componentType] The type of component interaction to collect
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collects a single component interaction that passes the filter.
|
|
||||||
* The Promise will reject if the time expires.
|
|
||||||
* @param {AwaitMessageComponentOptions} [options={}] Options to pass to the internal collector
|
|
||||||
* @returns {Promise<MessageComponentInteraction>}
|
|
||||||
* @example
|
|
||||||
* // Collect a message component interaction
|
|
||||||
* const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId';
|
|
||||||
* message.awaitMessageComponent({ filter, time: 15_000 })
|
|
||||||
* .then(interaction => console.log(`${interaction.customId} was clicked!`))
|
|
||||||
* .catch(console.error);
|
|
||||||
*/
|
|
||||||
awaitMessageComponent(options = {}) {
|
|
||||||
const _options = { ...options, max: 1 };
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the message is editable by the client user
|
* Whether the message is editable by the client user
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
@ -653,14 +598,7 @@ class Message extends Base {
|
|||||||
* channel.bulkDelete(messages.filter(message => message.bulkDeletable));
|
* channel.bulkDelete(messages.filter(message => message.bulkDeletable));
|
||||||
*/
|
*/
|
||||||
get bulkDeletable() {
|
get bulkDeletable() {
|
||||||
return (
|
return false;
|
||||||
(this.inGuild() &&
|
|
||||||
this.client.user.bot &&
|
|
||||||
Date.now() - this.createdTimestamp < MaxBulkDeletableMessageAge &&
|
|
||||||
this.deletable &&
|
|
||||||
this.channel?.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_MESSAGES, false)) ??
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -722,7 +660,7 @@ class Message extends Base {
|
|||||||
* @property {MessageAttachment[]} [attachments] An array of attachments to keep,
|
* @property {MessageAttachment[]} [attachments] An array of attachments to keep,
|
||||||
* all attachments will be kept if omitted
|
* all attachments will be kept if omitted
|
||||||
* @property {FileOptions[]|BufferResolvable[]|MessageAttachment[]} [files] Files to add to the message
|
* @property {FileOptions[]|BufferResolvable[]|MessageAttachment[]} [files] Files to add to the message
|
||||||
* @property {Array<(MessageActionRow|MessageActionRowOptions)>} [components]
|
* @property {MessageActionRow[]|MessageActionRowOptions[]} [components]
|
||||||
* Action rows containing interactive components for the message (buttons, select menus)
|
* Action rows containing interactive components for the message (buttons, select menus)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -792,7 +730,7 @@ class Message extends Base {
|
|||||||
/**
|
/**
|
||||||
* Adds a reaction to the message.
|
* Adds a reaction to the message.
|
||||||
* @param {EmojiIdentifierResolvable} emoji The emoji to react with
|
* @param {EmojiIdentifierResolvable} emoji The emoji to react with
|
||||||
* @param {boolean} [burst=false] Super Reactions (Discord Nitro only)
|
* @param {boolean} [burst=false] Super Reactions
|
||||||
* @returns {Promise<MessageReaction>}
|
* @returns {Promise<MessageReaction>}
|
||||||
* @example
|
* @example
|
||||||
* // React to a message with a unicode emoji
|
* // React to a message with a unicode emoji
|
||||||
@ -1024,13 +962,221 @@ class Message extends Base {
|
|||||||
reactions: false,
|
reactions: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Added
|
|
||||||
|
// TypeScript
|
||||||
|
/**
|
||||||
|
* Check data
|
||||||
|
* @type {boolean}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get isMessage() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click specific button with X and Y
|
||||||
|
* @typedef {Object} MessageButtonLocation
|
||||||
|
* @property {number} X Index of the row
|
||||||
|
* @property {number} Y Index of the column
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click specific button or automatically click first button if no button is specified.
|
||||||
|
* @param {MessageButtonLocation|string|undefined} button button
|
||||||
|
* @returns {Promise<Message|Modal>}
|
||||||
|
* @example
|
||||||
|
* // Demo msg
|
||||||
|
* Some content
|
||||||
|
* ――――――――――――――――――――――――――――――――> X from 0
|
||||||
|
* │ [button1] [button2] [button3]
|
||||||
|
* │ [button4] [button5] [button6]
|
||||||
|
* ↓
|
||||||
|
* Y from 0
|
||||||
|
* // Click button6 with X and Y
|
||||||
|
* [0,0] [1,0] [2,0]
|
||||||
|
* [0,1] [1,1] [2,1]
|
||||||
|
* // Code
|
||||||
|
* message.clickButton({
|
||||||
|
* X: 2, Y: 1,
|
||||||
|
* });
|
||||||
|
* // Click button with customId (Ex button 5)
|
||||||
|
* message.clickButton('button5');
|
||||||
|
* // Click button 1
|
||||||
|
* message.clickButton();
|
||||||
|
*/
|
||||||
|
clickButton(button) {
|
||||||
|
if (typeof button == 'undefined') {
|
||||||
|
button = this.components
|
||||||
|
.flatMap(row => row.components)
|
||||||
|
.find(b => b.type === 'BUTTON' && b.customId && !b.disabled);
|
||||||
|
} else if (typeof button == 'string') {
|
||||||
|
button = this.components.flatMap(row => row.components).find(b => b.type === 'BUTTON' && b.customId == button);
|
||||||
|
} else {
|
||||||
|
button = this.components[button.Y]?.components[button.X];
|
||||||
|
}
|
||||||
|
button = button.toJSON();
|
||||||
|
if (!button) throw new TypeError('BUTTON_NOT_FOUND');
|
||||||
|
if (!button.custom_id || button.disabled) throw new TypeError('BUTTON_CANNOT_CLICK');
|
||||||
|
const nonce = SnowflakeUtil.generate();
|
||||||
|
const data = {
|
||||||
|
type: InteractionTypes.MESSAGE_COMPONENT,
|
||||||
|
nonce,
|
||||||
|
guild_id: this.guildId,
|
||||||
|
channel_id: this.channelId,
|
||||||
|
message_id: this.id,
|
||||||
|
application_id: this.applicationId ?? this.author.id,
|
||||||
|
session_id: this.client.ws.shards.first()?.sessionId,
|
||||||
|
message_flags: this.flags.bitfield,
|
||||||
|
data: {
|
||||||
|
component_type: MessageComponentTypes.BUTTON,
|
||||||
|
custom_id: button.custom_id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.client.api.interactions.post({
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timeoutMs = 5_000;
|
||||||
|
// Waiting for MsgCreate / ModalCreate
|
||||||
|
const handler = data => {
|
||||||
|
// UnhandledPacket
|
||||||
|
if (data.d?.nonce == nonce && data.t == 'INTERACTION_SUCCESS') {
|
||||||
|
// Interaction#deferUpdate
|
||||||
|
this.client.removeListener(Events.MESSAGE_CREATE, handler);
|
||||||
|
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
|
||||||
|
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
|
||||||
|
resolve(this);
|
||||||
|
}
|
||||||
|
if (data.nonce !== nonce) return;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
this.client.removeListener(Events.MESSAGE_CREATE, handler);
|
||||||
|
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
|
||||||
|
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
|
||||||
|
this.client.decrementMaxListeners();
|
||||||
|
resolve(data);
|
||||||
|
};
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
this.client.removeListener(Events.MESSAGE_CREATE, handler);
|
||||||
|
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
|
||||||
|
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
|
||||||
|
this.client.decrementMaxListeners();
|
||||||
|
reject(new Error('INTERACTION_FAILED'));
|
||||||
|
}, timeoutMs).unref();
|
||||||
|
this.client.incrementMaxListeners();
|
||||||
|
this.client.on(Events.MESSAGE_CREATE, handler);
|
||||||
|
this.client.on(Events.UNHANDLED_PACKET, handler);
|
||||||
|
this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select specific menu
|
||||||
|
* @param {number|string} menu Target
|
||||||
|
* @param {Array<UserResolvable | RoleResolvable | ChannelResolvable | string>} values Any value
|
||||||
|
* @returns {Promise<Message|Modal>}
|
||||||
|
*/
|
||||||
|
selectMenu(menu, values = []) {
|
||||||
|
let selectMenu;
|
||||||
|
if (/[0-4]/.test(menu)) {
|
||||||
|
selectMenu = this.components[menu]?.components[0];
|
||||||
|
} else {
|
||||||
|
selectMenu = this.components
|
||||||
|
.flatMap(row => row.components)
|
||||||
|
.find(
|
||||||
|
b =>
|
||||||
|
['STRING_SELECT', 'USER_SELECT', 'ROLE_SELECT', 'MENTIONABLE_SELECT', 'CHANNEL_SELECT'].includes(b.type) &&
|
||||||
|
b.customId == menu &&
|
||||||
|
!b.disabled,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (values.length < selectMenu.minValues) {
|
||||||
|
throw new RangeError(`[SELECT_MENU_MIN_VALUES] The minimum number of values is ${selectMenu.minValues}`);
|
||||||
|
}
|
||||||
|
if (values.length > selectMenu.maxValues) {
|
||||||
|
throw new RangeError(`[SELECT_MENU_MAX_VALUES] The maximum number of values is ${selectMenu.maxValues}`);
|
||||||
|
}
|
||||||
|
values = values.map(value => {
|
||||||
|
switch (selectMenu.type) {
|
||||||
|
case 'STRING_SELECT': {
|
||||||
|
return selectMenu.options.find(obj => obj.value === value || obj.label === value).value;
|
||||||
|
}
|
||||||
|
case 'USER_SELECT': {
|
||||||
|
return this.client.users.resolveId(value);
|
||||||
|
}
|
||||||
|
case 'ROLE_SELECT': {
|
||||||
|
return this.guild.roles.resolveId(value);
|
||||||
|
}
|
||||||
|
case 'MENTIONABLE_SELECT': {
|
||||||
|
return this.client.users.resolveId(value) || this.guild.roles.resolveId(value);
|
||||||
|
}
|
||||||
|
case 'CHANNEL_SELECT': {
|
||||||
|
return this.client.channels.resolveId(value);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const nonce = SnowflakeUtil.generate();
|
||||||
|
const data = {
|
||||||
|
type: InteractionTypes.MESSAGE_COMPONENT,
|
||||||
|
guild_id: this.guildId,
|
||||||
|
channel_id: this.channelId,
|
||||||
|
message_id: this.id,
|
||||||
|
application_id: this.applicationId ?? this.author.id,
|
||||||
|
session_id: this.client.ws.shards.first()?.sessionId,
|
||||||
|
message_flags: this.flags.bitfield,
|
||||||
|
data: {
|
||||||
|
component_type: MessageComponentTypes[selectMenu.type],
|
||||||
|
custom_id: selectMenu.customId,
|
||||||
|
type: MessageComponentTypes[selectMenu.type],
|
||||||
|
values,
|
||||||
|
},
|
||||||
|
nonce,
|
||||||
|
};
|
||||||
|
this.client.api.interactions.post({
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timeoutMs = 5_000;
|
||||||
|
// Waiting for MsgCreate / ModalCreate
|
||||||
|
const handler = data => {
|
||||||
|
// UnhandledPacket
|
||||||
|
if (data.d?.nonce == nonce && data.t == 'INTERACTION_SUCCESS') {
|
||||||
|
// Interaction#deferUpdate
|
||||||
|
this.client.removeListener(Events.MESSAGE_CREATE, handler);
|
||||||
|
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
|
||||||
|
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
|
||||||
|
resolve(this);
|
||||||
|
}
|
||||||
|
if (data.nonce !== nonce) return;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
this.client.removeListener(Events.MESSAGE_CREATE, handler);
|
||||||
|
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
|
||||||
|
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
|
||||||
|
this.client.decrementMaxListeners();
|
||||||
|
resolve(data);
|
||||||
|
};
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
this.client.removeListener(Events.MESSAGE_CREATE, handler);
|
||||||
|
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
|
||||||
|
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
|
||||||
|
this.client.decrementMaxListeners();
|
||||||
|
reject(new Error('INTERACTION_FAILED'));
|
||||||
|
}, timeoutMs).unref();
|
||||||
|
this.client.incrementMaxListeners();
|
||||||
|
this.client.on(Events.MESSAGE_CREATE, handler);
|
||||||
|
this.client.on(Events.UNHANDLED_PACKET, handler);
|
||||||
|
this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the message as unread.
|
* Marks the message as unread.
|
||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async markUnread() {
|
markUnread() {
|
||||||
await this.client.api.channels[this.channelId].messages[this.id].ack.post({
|
return this.client.api.channels[this.channelId].messages[this.id].ack.post({
|
||||||
data: {
|
data: {
|
||||||
manual: true,
|
manual: true,
|
||||||
mention_count:
|
mention_count:
|
||||||
@ -1042,145 +1188,18 @@ class Message extends Base {
|
|||||||
: 0,
|
: 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the message as read.
|
* Marks the message as read.
|
||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async markRead() {
|
markRead() {
|
||||||
await this.client.api.channels[this.channelId].messages[this.id].ack.post({
|
return this.client.api.channels[this.channelId].messages[this.id].ack.post({
|
||||||
data: {
|
data: {
|
||||||
token: null,
|
token: null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} MessageButtonLocation
|
|
||||||
* @property {number} row Index of the row
|
|
||||||
* @property {number} col Index of the column
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Click specific button or automatically click first button if no button is specified.
|
|
||||||
* @param {MessageButton|MessageButtonLocation|string} button Button ID
|
|
||||||
* @returns {Promise<InteractionResponse>}
|
|
||||||
* @example
|
|
||||||
* client.on('messageCreate', async message => {
|
|
||||||
* if (message.components.length) {
|
|
||||||
* // Find first button and click it
|
|
||||||
* await message.clickButton();
|
|
||||||
* // Click with button ID
|
|
||||||
* await message.clickButton('button-id');
|
|
||||||
* // Click with button location
|
|
||||||
* await message.clickButton({ row: 0, col: 0 });
|
|
||||||
* // Click with class MessageButton
|
|
||||||
* const button = message.components[0].components[0];
|
|
||||||
* await message.clickButton(button);
|
|
||||||
* // Click with class MessageButton (2)
|
|
||||||
* button.click(message);
|
|
||||||
* }
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
clickButton(button) {
|
|
||||||
if (!button) {
|
|
||||||
button = this.components.flatMap(row => row.components).find(b => b.type === 'BUTTON')?.customId;
|
|
||||||
} else if (button instanceof MessageButton) {
|
|
||||||
button = button.customId;
|
|
||||||
} else if (typeof button === 'object') {
|
|
||||||
if (!('row' in button) || !('col' in button)) throw new TypeError('INVALID_BUTTON_LOCATION');
|
|
||||||
button = this.components[button.row]?.components[button.col]?.customId;
|
|
||||||
}
|
|
||||||
if (!button) throw new TypeError('BUTTON_NOT_FOUND');
|
|
||||||
button = this.components.flatMap(row => row.components).find(b => b.customId === button && b.type === 'BUTTON');
|
|
||||||
return button ? button.click(this) : Promise.reject(new TypeError('BUTTON_NOT_FOUND'));
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Select specific menu or First Menu
|
|
||||||
* @param {MessageSelectMenu|string|number|Array<any>} menuID MenuId / MessageSelectMenu / Row of Menu / Array of Values (first menu)
|
|
||||||
* @param {Array<any>} options Array of Values
|
|
||||||
* @returns {Promise<InteractionResponse>}
|
|
||||||
* @example
|
|
||||||
* client.on('messageCreate', async message => {
|
|
||||||
* if (message.components.length) {
|
|
||||||
* // Row
|
|
||||||
* await message.selectMenu(1, [message.channel]); // row 1, type: Channel, multi: false
|
|
||||||
* // Id
|
|
||||||
* await message.selectMenu('menu-id', ['uid1', client.user, message.member]); // MenuId, type: User, multi: true
|
|
||||||
* // First Menu
|
|
||||||
* await message.selectMenu(['role-id']); // First Menu, type: role, multi: false
|
|
||||||
* // class MessageSelectMenu
|
|
||||||
* const menu = message.components[0].components[0];
|
|
||||||
* await message.selectMenu(menu, ['option1', 'option2']);
|
|
||||||
* // MessageSelectMenu (2)
|
|
||||||
* menu.select(message, ['option1', 'option2']);
|
|
||||||
* }
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
selectMenu(menuID, options = []) {
|
|
||||||
if (!this.components[0]) throw new TypeError('MESSAGE_NO_COMPONENTS');
|
|
||||||
if (menuID instanceof MessageSelectMenu) {
|
|
||||||
//
|
|
||||||
} else if (/[0-4]/.test(menuID)) {
|
|
||||||
menuID = this.components[menuID]?.components[0];
|
|
||||||
} else {
|
|
||||||
const menuAll = this.components
|
|
||||||
.flatMap(row => row.components)
|
|
||||||
.filter(b =>
|
|
||||||
[
|
|
||||||
'STRING_SELECT',
|
|
||||||
'USER_SELECT',
|
|
||||||
'ROLE_SELECT',
|
|
||||||
'MENTIONABLE_SELECT',
|
|
||||||
'CHANNEL_SELECT',
|
|
||||||
'SELECT_MENU',
|
|
||||||
].includes(b.type),
|
|
||||||
);
|
|
||||||
if (menuAll.length == 0) throw new TypeError('MENU_NOT_FOUND');
|
|
||||||
if (menuID) {
|
|
||||||
menuID = menuAll.find(b => b.customId === menuID);
|
|
||||||
} else {
|
|
||||||
menuID = menuAll[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!menuID.type.includes('SELECT')) throw new TypeError('MENU_NOT_FOUND');
|
|
||||||
return menuID.select(this, Array.isArray(menuID) ? menuID : options);
|
|
||||||
}
|
|
||||||
//
|
|
||||||
/**
|
|
||||||
* Send context Menu v2
|
|
||||||
* @param {Snowflake} botId Bot id
|
|
||||||
* @param {string} commandName Command name in Context Menu
|
|
||||||
* @returns {Promise<InteractionResponse>}
|
|
||||||
*/
|
|
||||||
async contextMenu(botId, commandName) {
|
|
||||||
if (!botId) throw new Error('Bot ID is required');
|
|
||||||
const user = await this.client.users.fetch(botId).catch(() => {});
|
|
||||||
if (!user || !user.bot || !user.application) {
|
|
||||||
throw new Error('BotID is not a bot or does not have an application slash command');
|
|
||||||
}
|
|
||||||
if (!commandName || typeof commandName !== 'string') {
|
|
||||||
throw new Error('Command name is required');
|
|
||||||
}
|
|
||||||
let contextCMD;
|
|
||||||
const data = await this.channel.searchInteraction(botId, 'MESSAGE');
|
|
||||||
for (const command of data.application_commands) {
|
|
||||||
user.application?.commands?._add(command, true);
|
|
||||||
}
|
|
||||||
contextCMD = user.application?.commands?.cache.find(c => c.name == commandName && c.type === 'MESSAGE');
|
|
||||||
if (!contextCMD) {
|
|
||||||
throw new Error(
|
|
||||||
'INTERACTION_SEND_FAILURE',
|
|
||||||
`Command ${commandName} is not found (with search)\nList command avalible: ${user.application?.commands?.cache
|
|
||||||
.filter(a => a.type == 'MESSAGE')
|
|
||||||
.map(a => a.name)
|
|
||||||
.join(', ')}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return contextCMD.sendContextMenu(this, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class MessageReaction {
|
|||||||
this.me = data.me || data.me_burst;
|
this.me = data.me || data.me_burst;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Super reaction
|
* Is super reaction
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
this.isBurst = Boolean(data.me_burst || data.burst);
|
this.isBurst = Boolean(data.me_burst || data.burst);
|
||||||
|
@ -86,11 +86,15 @@ class VoiceState extends Base {
|
|||||||
|
|
||||||
// The self_stream is property is omitted if false, check for another property
|
// The self_stream is property is omitted if false, check for another property
|
||||||
// here to avoid incorrectly clearing this when partial data is specified
|
// here to avoid incorrectly clearing this when partial data is specified
|
||||||
/**
|
if ('self_stream' in data) {
|
||||||
* Whether this member is streaming using "Screen Share"
|
/**
|
||||||
* @type {boolean}
|
* Whether this member is streaming using "Screen Share"
|
||||||
*/
|
* @type {boolean}
|
||||||
this.streaming = data.self_stream ?? false;
|
*/
|
||||||
|
this.streaming = data.self_stream ?? false;
|
||||||
|
} else {
|
||||||
|
this.streaming ??= null;
|
||||||
|
}
|
||||||
|
|
||||||
if ('channel_id' in data) {
|
if ('channel_id' in data) {
|
||||||
/**
|
/**
|
||||||
@ -144,12 +148,11 @@ class VoiceState extends Base {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The channel that the member is connected to
|
* The channel that the member is connected to
|
||||||
* @type {?(VoiceChannel|StageChannel)}
|
* @type {?(VoiceChannel|StageChannel|DMChannel|GroupDMChannel)}
|
||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get channel() {
|
get channel() {
|
||||||
if (!this.guild?.id) return this.guild.client.channels.cache.get(this.channelId) ?? null;
|
return (this.guild || this.client).channels.cache.get(this.channelId) ?? null;
|
||||||
return this.guild.channels.cache.get(this.channelId) ?? null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,7 +180,6 @@ class VoiceState extends Base {
|
|||||||
* @returns {Promise<GuildMember>}
|
* @returns {Promise<GuildMember>}
|
||||||
*/
|
*/
|
||||||
setMute(mute = true, reason) {
|
setMute(mute = true, reason) {
|
||||||
if (!this.guild?.id) return null;
|
|
||||||
return this.guild.members.edit(this.id, { mute }, reason);
|
return this.guild.members.edit(this.id, { mute }, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +190,6 @@ class VoiceState extends Base {
|
|||||||
* @returns {Promise<GuildMember>}
|
* @returns {Promise<GuildMember>}
|
||||||
*/
|
*/
|
||||||
setDeaf(deaf = true, reason) {
|
setDeaf(deaf = true, reason) {
|
||||||
if (!this.guild?.id) return null;
|
|
||||||
return this.guild.members.edit(this.id, { deaf }, reason);
|
return this.guild.members.edit(this.id, { deaf }, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +199,6 @@ class VoiceState extends Base {
|
|||||||
* @returns {Promise<GuildMember>}
|
* @returns {Promise<GuildMember>}
|
||||||
*/
|
*/
|
||||||
disconnect(reason) {
|
disconnect(reason) {
|
||||||
if (!this.guild?.id) return this.callVoice?.disconnect();
|
|
||||||
return this.setChannel(null, reason);
|
return this.setChannel(null, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,36 +210,9 @@ class VoiceState extends Base {
|
|||||||
* @returns {Promise<GuildMember>}
|
* @returns {Promise<GuildMember>}
|
||||||
*/
|
*/
|
||||||
setChannel(channel, reason) {
|
setChannel(channel, reason) {
|
||||||
if (!this.guild?.id) return null;
|
|
||||||
return this.guild.members.edit(this.id, { channel }, reason);
|
return this.guild.members.edit(this.id, { channel }, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the status of the voice channel
|
|
||||||
* @param {string} status The message to set the channel status to
|
|
||||||
* @example
|
|
||||||
* // Setting the status to something
|
|
||||||
* guild.members.me.voice.setStatus("something")
|
|
||||||
* @example
|
|
||||||
* // Removing the status
|
|
||||||
* guild.members.me.voice.setStatus("")
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
async setStatus(status) {
|
|
||||||
// PUT https://discord.com/api/v9/channels/channelID/voice-status
|
|
||||||
if (this.channel?.id) {
|
|
||||||
// You can set the staus in normal voice channels and in stages so any type starting with GUILD should work
|
|
||||||
if (!this.channel?.type.startsWith('GUILD')) throw new Error('VOICE_NOT_IN_GUILD');
|
|
||||||
|
|
||||||
await this.client.api.channels(this.channel.id, 'voice-status').put({
|
|
||||||
data: {
|
|
||||||
status,
|
|
||||||
},
|
|
||||||
versioned: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the request to speak in the channel.
|
* Toggles the request to speak in the channel.
|
||||||
* Only applicable for stage channels and for the client's own voice state.
|
* Only applicable for stage channels and for the client's own voice state.
|
||||||
@ -253,18 +226,16 @@ class VoiceState extends Base {
|
|||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async setRequestToSpeak(request = true) {
|
async setRequestToSpeak(request = true) {
|
||||||
if (this.guild?.id) {
|
if (this.channel?.type !== 'GUILD_STAGE_VOICE') throw new Error('VOICE_NOT_STAGE_CHANNEL');
|
||||||
if (this.channel?.type !== 'GUILD_STAGE_VOICE') throw new Error('VOICE_NOT_STAGE_CHANNEL');
|
|
||||||
|
|
||||||
if (this.client.user.id !== this.id) throw new Error('VOICE_STATE_NOT_OWN');
|
if (this.client.user.id !== this.id) throw new Error('VOICE_STATE_NOT_OWN');
|
||||||
|
|
||||||
await this.client.api.guilds(this.guild.id, 'voice-states', '@me').patch({
|
await this.client.api.guilds(this.guild.id, 'voice-states', '@me').patch({
|
||||||
data: {
|
data: {
|
||||||
channel_id: this.channelId,
|
channel_id: this.channelId,
|
||||||
request_to_speak_timestamp: request ? new Date().toISOString() : null,
|
request_to_speak_timestamp: request ? new Date().toISOString() : null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -285,21 +256,39 @@ class VoiceState extends Base {
|
|||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async setSuppressed(suppressed = true) {
|
async setSuppressed(suppressed = true) {
|
||||||
if (this.guild?.id) {
|
if (typeof suppressed !== 'boolean') throw new TypeError('VOICE_STATE_INVALID_TYPE', 'suppressed');
|
||||||
if (typeof suppressed !== 'boolean') throw new TypeError('VOICE_STATE_INVALID_TYPE', 'suppressed');
|
|
||||||
|
|
||||||
if (this.channel?.type !== 'GUILD_STAGE_VOICE') throw new Error('VOICE_NOT_STAGE_CHANNEL');
|
if (this.channel?.type !== 'GUILD_STAGE_VOICE') throw new Error('VOICE_NOT_STAGE_CHANNEL');
|
||||||
|
|
||||||
const target = this.client.user.id === this.id ? '@me' : this.id;
|
const target = this.client.user.id === this.id ? '@me' : this.id;
|
||||||
|
|
||||||
await this.client.api.guilds(this.guild.id, 'voice-states', target).patch({
|
await this.client.api.guilds(this.guild.id, 'voice-states', target).patch({
|
||||||
data: {
|
data: {
|
||||||
channel_id: this.channelId,
|
channel_id: this.channelId,
|
||||||
suppress: suppressed,
|
suppress: suppressed,
|
||||||
request_to_speak_timestamp: null,
|
request_to_speak_timestamp: null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the status of the voice channel
|
||||||
|
* @param {string} [status=""] The message to set the channel status to
|
||||||
|
* @example
|
||||||
|
* // Setting the status to something
|
||||||
|
* guild.members.me.voice.setStatus("something")
|
||||||
|
* @example
|
||||||
|
* // Removing the status
|
||||||
|
* guild.members.me.voice.setStatus()
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
setStatus(status = '') {
|
||||||
|
// PUT https://discord.com/api/v9/channels/:id/voice-status
|
||||||
|
return this.client.api.channels(this.channel.id, 'voice-status').put({
|
||||||
|
data: {
|
||||||
|
status,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -322,19 +311,18 @@ class VoiceState extends Base {
|
|||||||
* @param {string} base64Image Base64 URI (data:image/jpeg;base64,data)
|
* @param {string} base64Image Base64 URI (data:image/jpeg;base64,data)
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async postPreview(base64Image) {
|
postPreview(base64Image) {
|
||||||
if (!this.client.user.id === this.id || !this.streaming) throw new Error('USER_NOT_STREAMING');
|
if (!this.client.user.id === this.id || !this.streaming) throw new Error('USER_NOT_STREAMING');
|
||||||
// URL: https://discord.com/api/v9/streams/guild:guildid:voicechannelid:userid/preview
|
// URL: https://discord.com/api/v9/streams/guild:guildid:voicechannelid:userid/preview
|
||||||
// URL: https://discord.com/api/v9/streams/call:channelId:userId/preview
|
// URL: https://discord.com/api/v9/streams/call:channelId:userId/preview
|
||||||
const streamKey = this.guild.id
|
const streamKey = this.guild.id
|
||||||
? `guild:${this.guild.id}:${this.channelId}:${this.id}`
|
? `guild:${this.guild.id}:${this.channelId}:${this.id}`
|
||||||
: `call:${this.channelId}:${this.id}`;
|
: `call:${this.channelId}:${this.id}`;
|
||||||
await this.client.api.streams[encodeURIComponent(streamKey)].preview.post({
|
return this.client.api.streams[encodeURIComponent(streamKey)].preview.post({
|
||||||
data: {
|
data: {
|
||||||
thumbnail: base64Image,
|
thumbnail: base64Image,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
|
Loading…
Reference in New Issue
Block a user