mirror of
https://github.com/danbulant/discord.js
synced 2026-06-24 17:21:59 +00:00
Compare commits
56 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2eaafaf1d3 | ||
|
|
c513df444e | ||
|
|
4f5cdf2814 | ||
|
|
0ca50889ae | ||
|
|
53412f6b9f | ||
|
|
2b8a6294af | ||
|
|
8c65961a07 | ||
|
|
4599ef954f | ||
|
|
790b6b3b5c | ||
|
|
5be32161b9 | ||
|
|
db6d1b3ba9 | ||
|
|
21bfe3da7c | ||
|
|
a626dc8c41 | ||
|
|
c6f87aa32d | ||
|
|
2685b960d7 | ||
|
|
60e5a0e46f | ||
|
|
7365f40300 | ||
|
|
09d07553ab | ||
|
|
e272fd6909 | ||
|
|
90d458820b | ||
|
|
9f3c3e0918 | ||
|
|
ec560b8107 | ||
|
|
fbb1c93454 | ||
|
|
6b322f47a0 | ||
|
|
4fcb9ebf30 | ||
|
|
53529bd05d | ||
|
|
88625a5b7d | ||
|
|
e43ad1eea9 | ||
|
|
8d650a7250 | ||
|
|
12a096b5f1 | ||
|
|
6f3076325e | ||
|
|
8c8883ef26 | ||
|
|
4b555fdf4c | ||
|
|
863734aba4 | ||
|
|
eadeeed72e | ||
|
|
939c495ebb | ||
|
|
065e89337e | ||
|
|
1028183c23 | ||
|
|
b61a367392 | ||
|
|
ff2dbfa52d | ||
|
|
3463acafca | ||
|
|
274ae9935e | ||
|
|
2eafeeca55 | ||
|
|
08286459cb | ||
|
|
2e96b9a606 | ||
|
|
ef1856bb1f | ||
|
|
4f7c207c99 | ||
|
|
975b6cbd94 | ||
|
|
67c2e56647 | ||
|
|
ab0d6fc5c9 | ||
|
|
b8f50a09d2 | ||
|
|
9f3108052c | ||
|
|
a5ce6cfa9a | ||
|
|
ab5ee838a3 | ||
|
|
0ed281888d | ||
|
|
bc4dc22c1f |
53 changed files with 843 additions and 259 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"root": true,
|
||||||
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
|
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
|
||||||
"plugins": ["import"],
|
"plugins": ["import"],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
|
|
|
||||||
8
.github/workflows/deploy.yml
vendored
8
.github/workflows/deploy.yml
vendored
|
|
@ -15,10 +15,10 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@master
|
uses: actions/checkout@master
|
||||||
|
|
||||||
- name: Install Node v12
|
- name: Install Node v14
|
||||||
uses: actions/setup-node@master
|
uses: actions/setup-node@master
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
@ -35,10 +35,10 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@master
|
uses: actions/checkout@master
|
||||||
|
|
||||||
- name: Install Node v12
|
- name: Install Node v14
|
||||||
uses: actions/setup-node@master
|
uses: actions/setup-node@master
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
|
||||||
16
.github/workflows/test-cron.yml
vendored
16
.github/workflows/test-cron.yml
vendored
|
|
@ -10,10 +10,10 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
- name: Install Node v12
|
- name: Install Node v14
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
@ -28,10 +28,10 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
- name: Install Node v12
|
- name: Install Node v14
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
@ -46,10 +46,10 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install Node v12
|
- name: Install Node v14
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
@ -67,10 +67,10 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
- name: Install Node v12
|
- name: Install Node v14
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
|
||||||
16
.github/workflows/test.yml
vendored
16
.github/workflows/test.yml
vendored
|
|
@ -8,10 +8,10 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install Node v12
|
- name: Install Node v14
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
@ -26,10 +26,10 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install Node v12
|
- name: Install Node v14
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
@ -44,10 +44,10 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install Node v12
|
- name: Install Node v14
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
@ -65,10 +65,10 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install Node v12
|
- name: Install Node v14
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
## Table of contents
|
## Table of contents
|
||||||
|
|
||||||
|
- [Changes](#changes)
|
||||||
- [About](#about)
|
- [About](#about)
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [Audio engines](#audio-engines)
|
- [Audio engines](#audio-engines)
|
||||||
|
|
@ -29,6 +30,10 @@
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
- [Help](#help)
|
- [Help](#help)
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
This fork has the inline replies and slash commands for **testing** purposes.
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to easily interact with the
|
discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to easily interact with the
|
||||||
|
|
@ -41,7 +46,7 @@ discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
**Node.js 12.0.0 or newer is required.**
|
**Node.js 14.0.0 or newer is required.**
|
||||||
Ignore any warnings about unmet peer dependencies, as they're all optional.
|
Ignore any warnings about unmet peer dependencies, as they're all optional.
|
||||||
|
|
||||||
Without voice support: `npm install discord.js`
|
Without voice support: `npm install discord.js`
|
||||||
|
|
@ -76,7 +81,7 @@ client.on('ready', () => {
|
||||||
|
|
||||||
client.on('message', msg => {
|
client.on('message', msg => {
|
||||||
if (msg.content === 'ping') {
|
if (msg.content === 'ping') {
|
||||||
msg.reply('pong');
|
msg.channel.send('pong');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ client.on('message', message => {
|
||||||
// If the message is "what is my avatar"
|
// If the message is "what is my avatar"
|
||||||
if (message.content === 'what is my avatar') {
|
if (message.content === 'what is my avatar') {
|
||||||
// Send the user's avatar URL
|
// Send the user's avatar URL
|
||||||
message.reply(message.author.displayAvatarURL());
|
message.channel.send(message.author.displayAvatarURL());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ client.on('message', message => {
|
||||||
// If we have a user mentioned
|
// If we have a user mentioned
|
||||||
if (user) {
|
if (user) {
|
||||||
// Now we get the member from the user
|
// Now we get the member from the user
|
||||||
const member = message.guild.member(user);
|
const member = message.guild.members.resolve(user);
|
||||||
// If the member is in the guild
|
// If the member is in the guild
|
||||||
if (member) {
|
if (member) {
|
||||||
/**
|
/**
|
||||||
|
|
@ -45,23 +45,23 @@ client.on('message', message => {
|
||||||
.kick('Optional reason that will display in the audit logs')
|
.kick('Optional reason that will display in the audit logs')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// We let the message author know we were able to kick the person
|
// We let the message author know we were able to kick the person
|
||||||
message.reply(`Successfully kicked ${user.tag}`);
|
message.channel.send(`Successfully kicked ${user.tag}`);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
// An error happened
|
// An error happened
|
||||||
// This is generally due to the bot not being able to kick the member,
|
// This is generally due to the bot not being able to kick the member,
|
||||||
// either due to missing permissions or role hierarchy
|
// either due to missing permissions or role hierarchy
|
||||||
message.reply('I was unable to kick the member');
|
message.channel.send('I was unable to kick the member');
|
||||||
// Log the error
|
// Log the error
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// The mentioned user isn't in this guild
|
// The mentioned user isn't in this guild
|
||||||
message.reply("That user isn't in this guild!");
|
message.channel.send("That user isn't in this guild!");
|
||||||
}
|
}
|
||||||
// Otherwise, if no user was mentioned
|
// Otherwise, if no user was mentioned
|
||||||
} else {
|
} else {
|
||||||
message.reply("You didn't mention the user to kick!");
|
message.channel.send("You didn't mention the user to kick!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -105,7 +105,7 @@ client.on('message', message => {
|
||||||
// If we have a user mentioned
|
// If we have a user mentioned
|
||||||
if (user) {
|
if (user) {
|
||||||
// Now we get the member from the user
|
// Now we get the member from the user
|
||||||
const member = message.guild.member(user);
|
const member = message.guild.members.resolve(user);
|
||||||
// If the member is in the guild
|
// If the member is in the guild
|
||||||
if (member) {
|
if (member) {
|
||||||
/**
|
/**
|
||||||
|
|
@ -121,23 +121,23 @@ client.on('message', message => {
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// We let the message author know we were able to ban the person
|
// We let the message author know we were able to ban the person
|
||||||
message.reply(`Successfully banned ${user.tag}`);
|
message.channel.send(`Successfully banned ${user.tag}`);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
// An error happened
|
// An error happened
|
||||||
// This is generally due to the bot not being able to ban the member,
|
// This is generally due to the bot not being able to ban the member,
|
||||||
// either due to missing permissions or role hierarchy
|
// either due to missing permissions or role hierarchy
|
||||||
message.reply('I was unable to ban the member');
|
message.channel.send('I was unable to ban the member');
|
||||||
// Log the error
|
// Log the error
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// The mentioned user isn't in this guild
|
// The mentioned user isn't in this guild
|
||||||
message.reply("That user isn't in this guild!");
|
message.channel.send("That user isn't in this guild!");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, if no user was mentioned
|
// Otherwise, if no user was mentioned
|
||||||
message.reply("You didn't mention the user to ban!");
|
message.channel.send("You didn't mention the user to ban!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ These questions are some of the most frequently asked.
|
||||||
|
|
||||||
## No matter what, I get `SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode`‽
|
## No matter what, I get `SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode`‽
|
||||||
|
|
||||||
Update to Node.js 12.0.0 or newer.
|
Update to Node.js 14.0.0 or newer.
|
||||||
|
|
||||||
## How do I get voice working?
|
## How do I get voice working?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
**Node.js 12.0.0 or newer is required.**
|
**Node.js 14.0.0 or newer is required.**
|
||||||
Ignore any warnings about unmet peer dependencies, as they're all optional.
|
Ignore any warnings about unmet peer dependencies, as they're all optional.
|
||||||
|
|
||||||
Without voice support: `npm install discord.js`
|
Without voice support: `npm install discord.js`
|
||||||
|
|
@ -68,7 +68,7 @@ client.on('ready', () => {
|
||||||
|
|
||||||
client.on('message', msg => {
|
client.on('message', msg => {
|
||||||
if (msg.content === 'ping') {
|
if (msg.content === 'ping') {
|
||||||
msg.reply('pong');
|
msg.channel.send('pong');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ client.on('message', async message => {
|
||||||
if (message.member.voice.channel) {
|
if (message.member.voice.channel) {
|
||||||
const connection = await message.member.voice.channel.join();
|
const connection = await message.member.voice.channel.join();
|
||||||
} else {
|
} else {
|
||||||
message.reply('You need to join a voice channel first!');
|
message.channel.send('You need to join a voice channel first!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ export const {
|
||||||
UserFlags,
|
UserFlags,
|
||||||
Util,
|
Util,
|
||||||
version,
|
version,
|
||||||
|
BaseGuildEmojiManager,
|
||||||
ChannelManager,
|
ChannelManager,
|
||||||
GuildChannelManager,
|
GuildChannelManager,
|
||||||
GuildEmojiManager,
|
GuildEmojiManager,
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
"webpack-cli": "^3.3.12"
|
"webpack-cli": "^3.3.12"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=14.0.0"
|
||||||
},
|
},
|
||||||
"browser": {
|
"browser": {
|
||||||
"@discordjs/opus": false,
|
"@discordjs/opus": false,
|
||||||
|
|
@ -110,9 +110,9 @@
|
||||||
"src/client/voice/receiver/PacketHandler.js": false,
|
"src/client/voice/receiver/PacketHandler.js": false,
|
||||||
"src/client/voice/receiver/Receiver.js": false,
|
"src/client/voice/receiver/Receiver.js": false,
|
||||||
"src/client/voice/util/PlayInterface.js": false,
|
"src/client/voice/util/PlayInterface.js": false,
|
||||||
"src/client/voice/util/Secretbox.js": false,
|
|
||||||
"src/client/voice/util/Silence.js": false,
|
"src/client/voice/util/Silence.js": false,
|
||||||
"src/client/voice/util/VolumeInterface.js": false
|
"src/client/voice/util/VolumeInterface.js": false,
|
||||||
|
"src/util/Sodium.js": false
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const BaseClient = require('./BaseClient');
|
const BaseClient = require('./BaseClient');
|
||||||
|
const InteractionClient = require('./InteractionClient');
|
||||||
const ActionsManager = require('./actions/ActionsManager');
|
const ActionsManager = require('./actions/ActionsManager');
|
||||||
const ClientVoiceManager = require('./voice/ClientVoiceManager');
|
const ClientVoiceManager = require('./voice/ClientVoiceManager');
|
||||||
const WebSocketManager = require('./websocket/WebSocketManager');
|
const WebSocketManager = require('./websocket/WebSocketManager');
|
||||||
const { Error, TypeError, RangeError } = require('../errors');
|
const { Error, TypeError, RangeError } = require('../errors');
|
||||||
|
const BaseGuildEmojiManager = require('../managers/BaseGuildEmojiManager');
|
||||||
const ChannelManager = require('../managers/ChannelManager');
|
const ChannelManager = require('../managers/ChannelManager');
|
||||||
const GuildEmojiManager = require('../managers/GuildEmojiManager');
|
|
||||||
const GuildManager = require('../managers/GuildManager');
|
const GuildManager = require('../managers/GuildManager');
|
||||||
const UserManager = require('../managers/UserManager');
|
const UserManager = require('../managers/UserManager');
|
||||||
const ShardClientUtil = require('../sharding/ShardClientUtil');
|
const ShardClientUtil = require('../sharding/ShardClientUtil');
|
||||||
|
|
@ -103,6 +104,12 @@ class Client extends BaseClient {
|
||||||
? ShardClientUtil.singleton(this, process.env.SHARDING_MANAGER_MODE)
|
? ShardClientUtil.singleton(this, process.env.SHARDING_MANAGER_MODE)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interaction client.
|
||||||
|
* @type {InteractionClient}
|
||||||
|
*/
|
||||||
|
this.interactionClient = new InteractionClient(options, this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All of the {@link User} objects that have been cached at any point, mapped by their IDs
|
* All of the {@link User} objects that have been cached at any point, mapped by their IDs
|
||||||
* @type {UserManager}
|
* @type {UserManager}
|
||||||
|
|
@ -166,11 +173,11 @@ class Client extends BaseClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All custom emojis that the client has access to, mapped by their IDs
|
* All custom emojis that the client has access to, mapped by their IDs
|
||||||
* @type {GuildEmojiManager}
|
* @type {BaseGuildEmojiManager}
|
||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get emojis() {
|
get emojis() {
|
||||||
const emojis = new GuildEmojiManager({ client: this });
|
const emojis = new BaseGuildEmojiManager(this);
|
||||||
for (const guild of this.guilds.cache.values()) {
|
for (const guild of this.guilds.cache.values()) {
|
||||||
if (guild.available) for (const emoji of guild.emojis.cache.values()) emojis.cache.set(emoji.id, emoji);
|
if (guild.available) for (const emoji of guild.emojis.cache.values()) emojis.cache.set(emoji.id, emoji);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
207
src/client/InteractionClient.js
Normal file
207
src/client/InteractionClient.js
Normal file
|
|
@ -0,0 +1,207 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const BaseClient = require('./BaseClient');
|
||||||
|
const ApplicationCommand = require('../structures/ApplicationCommand');
|
||||||
|
const Interaction = require('../structures/Interaction');
|
||||||
|
const { Events, ApplicationCommandOptionType, InteractionType, InteractionResponseType } = require('../util/Constants');
|
||||||
|
|
||||||
|
let sodium;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interaction client is used for interactions.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const client = new InteractionClient({
|
||||||
|
* token: ABC,
|
||||||
|
* publicKey: XYZ,
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* client.on('interactionCreate', () => {
|
||||||
|
* // automatically handles long responses
|
||||||
|
* if (will take a long time) {
|
||||||
|
* doSomethingLong.then((d) => {
|
||||||
|
* interaction.reply({
|
||||||
|
* content: 'wow that took long',
|
||||||
|
* });
|
||||||
|
* });
|
||||||
|
* } else {
|
||||||
|
* interaction.reply('hi!');
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
class InteractionClient extends BaseClient {
|
||||||
|
/**
|
||||||
|
* @param {Options} options Options for the client.
|
||||||
|
* @param {undefined} client For internal use.
|
||||||
|
*/
|
||||||
|
constructor(options, client) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'token', {
|
||||||
|
value: options.token,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'clientID', {
|
||||||
|
value: options.clientID,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'publicKey', {
|
||||||
|
value: options.publicKey ? Buffer.from(options.publicKey, 'hex') : undefined,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Compat for direct usage
|
||||||
|
this.client = client || this;
|
||||||
|
this.interactionClient = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get registered slash commands.
|
||||||
|
* @param {Snowflake} [guildID] Optional guild ID.
|
||||||
|
* @returns {Command[]}
|
||||||
|
*/
|
||||||
|
async getCommands(guildID) {
|
||||||
|
let path = this.client.api.applications('@me');
|
||||||
|
if (guildID) {
|
||||||
|
path = path.guilds(guildID);
|
||||||
|
}
|
||||||
|
const commands = await path.commands.get();
|
||||||
|
return commands.map(c => new ApplicationCommand(this, c, guildID));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a command.
|
||||||
|
* @param {Object} command The command description.
|
||||||
|
* @param {Snowflake?} guildID Optional guild ID.
|
||||||
|
* @returns {Promise<ApplicationCommand>} The created command.
|
||||||
|
*/
|
||||||
|
async createCommand(command, guildID) {
|
||||||
|
let path = this.client.api.applications(this.client.user.id);
|
||||||
|
if (guildID) {
|
||||||
|
path = path.guilds(guildID);
|
||||||
|
}
|
||||||
|
const c = await path.commands.post({
|
||||||
|
data: {
|
||||||
|
name: command.name,
|
||||||
|
description: command.description,
|
||||||
|
options: command.options.map(function m(o) {
|
||||||
|
return {
|
||||||
|
type: ApplicationCommandOptionType[o.type],
|
||||||
|
name: o.name,
|
||||||
|
description: o.description,
|
||||||
|
default: o.default,
|
||||||
|
required: o.required,
|
||||||
|
choices: o.choices,
|
||||||
|
options: o.options ? o.options.map(m) : undefined,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return new ApplicationCommand(this, c, guildID);
|
||||||
|
}
|
||||||
|
|
||||||
|
handle(data) {
|
||||||
|
switch (data.type) {
|
||||||
|
case InteractionType.PING:
|
||||||
|
return {
|
||||||
|
type: InteractionResponseType.PONG,
|
||||||
|
};
|
||||||
|
case InteractionType.APPLICATION_COMMAND: {
|
||||||
|
let timedOut = false;
|
||||||
|
let resolve;
|
||||||
|
const directPromise = new Promise(r => {
|
||||||
|
resolve = r;
|
||||||
|
this.client.setTimeout(() => {
|
||||||
|
timedOut = true;
|
||||||
|
r({
|
||||||
|
type: InteractionResponseType.ACKNOWLEDGE_WITH_SOURCE,
|
||||||
|
});
|
||||||
|
}, 250);
|
||||||
|
});
|
||||||
|
|
||||||
|
const syncHandle = {
|
||||||
|
acknowledge() {
|
||||||
|
if (!timedOut) {
|
||||||
|
resolve({
|
||||||
|
type: InteractionResponseType.ACKNOWLEDGE_WITH_SOURCE,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reply(resolved) {
|
||||||
|
if (timedOut) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
resolve({
|
||||||
|
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||||
|
data: resolved.data,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const interaction = new Interaction(this.client, data, syncHandle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emitted when an interaction is created.
|
||||||
|
* @event Client#interactionCreate
|
||||||
|
* @param {Interaction} interaction The interaction which was created.
|
||||||
|
*/
|
||||||
|
this.client.emit(Events.INTERACTION_CREATE, interaction);
|
||||||
|
|
||||||
|
return directPromise;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new RangeError('Invalid interaction data');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An express-like middleware factory which can be used
|
||||||
|
* with webhook interactions.
|
||||||
|
* @returns {Function} The middleware function.
|
||||||
|
*/
|
||||||
|
middleware() {
|
||||||
|
return async (req, res) => {
|
||||||
|
const timestamp = req.get('x-signature-timestamp');
|
||||||
|
const signature = req.get('x-signature-ed25519');
|
||||||
|
|
||||||
|
const chunks = [];
|
||||||
|
for await (const chunk of req) {
|
||||||
|
chunks.push(chunk);
|
||||||
|
}
|
||||||
|
const body = Buffer.concat(chunks);
|
||||||
|
|
||||||
|
if (sodium === undefined) {
|
||||||
|
sodium = require('../util/Sodium');
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!sodium.methods.verify(
|
||||||
|
Buffer.from(signature, 'hex'),
|
||||||
|
Buffer.concat([Buffer.from(timestamp), body]),
|
||||||
|
this.publicKey,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
res.status(403).end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = JSON.parse(body.toString());
|
||||||
|
|
||||||
|
const result = await this.handle(data);
|
||||||
|
res.status(200).end(JSON.stringify(result));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleFromGateway(data) {
|
||||||
|
const result = await this.handle(data);
|
||||||
|
|
||||||
|
await this.client.api.interactions(data.id, data.token).callback.post({
|
||||||
|
data: result,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = InteractionClient;
|
||||||
|
|
@ -37,6 +37,7 @@ class ActionsManager {
|
||||||
this.register(require('./GuildIntegrationsUpdate'));
|
this.register(require('./GuildIntegrationsUpdate'));
|
||||||
this.register(require('./WebhooksUpdate'));
|
this.register(require('./WebhooksUpdate'));
|
||||||
this.register(require('./TypingStart'));
|
this.register(require('./TypingStart'));
|
||||||
|
this.register(require('./InteractionCreate'));
|
||||||
}
|
}
|
||||||
|
|
||||||
register(Action) {
|
register(Action) {
|
||||||
|
|
|
||||||
15
src/client/actions/InteractionCreate.js
Normal file
15
src/client/actions/InteractionCreate.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Action = require('./Action');
|
||||||
|
|
||||||
|
class InteractionCreateAction extends Action {
|
||||||
|
handle(data) {
|
||||||
|
this.client.interactionClient.handleFromGateway(data).catch(e => {
|
||||||
|
this.client.emit('error', e);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = InteractionCreateAction;
|
||||||
|
|
@ -473,7 +473,11 @@ class VoiceConnection extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
onStartSpeaking({ user_id, ssrc, speaking }) {
|
onStartSpeaking({ user_id, ssrc, speaking }) {
|
||||||
this.ssrcMap.set(+ssrc, { userID: user_id, speaking: speaking });
|
this.ssrcMap.set(+ssrc, {
|
||||||
|
...(this.ssrcMap.get(+ssrc) || {}),
|
||||||
|
userID: user_id,
|
||||||
|
speaking: speaking,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -501,7 +505,7 @@ class VoiceConnection extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (guild && user && !speaking.equals(old)) {
|
if (guild && user && !speaking.equals(old)) {
|
||||||
const member = guild.member(user);
|
const member = guild.members.resolve(user);
|
||||||
if (member) {
|
if (member) {
|
||||||
/**
|
/**
|
||||||
* Emitted once a guild member changes speaking state.
|
* Emitted once a guild member changes speaking state.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { Writable } = require('stream');
|
const { Writable } = require('stream');
|
||||||
const secretbox = require('../util/Secretbox');
|
const secretbox = require('../../../util/Sodium');
|
||||||
const Silence = require('../util/Silence');
|
const Silence = require('../util/Silence');
|
||||||
const VolumeInterface = require('../util/VolumeInterface');
|
const VolumeInterface = require('../util/VolumeInterface');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,11 @@ class VoiceWebSocket extends EventEmitter {
|
||||||
this.emit('sessionDescription', packet.d);
|
this.emit('sessionDescription', packet.d);
|
||||||
break;
|
break;
|
||||||
case VoiceOPCodes.CLIENT_CONNECT:
|
case VoiceOPCodes.CLIENT_CONNECT:
|
||||||
this.connection.ssrcMap.set(+packet.d.audio_ssrc, { userID: packet.d.user_id, speaking: 0 });
|
this.connection.ssrcMap.set(+packet.d.audio_ssrc, {
|
||||||
|
userID: packet.d.user_id,
|
||||||
|
speaking: 0,
|
||||||
|
hasVideo: Boolean(packet.d.video_ssrc),
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case VoiceOPCodes.CLIENT_DISCONNECT:
|
case VoiceOPCodes.CLIENT_DISCONNECT:
|
||||||
const streamInfo = this.connection.receiver && this.connection.receiver.packets.streams.get(packet.d.user_id);
|
const streamInfo = this.connection.receiver && this.connection.receiver.packets.streams.get(packet.d.user_id);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
const secretbox = require('../util/Secretbox');
|
const sodium = require('../../../util/Sodium');
|
||||||
|
const Speaking = require('../../../util/Speaking');
|
||||||
|
const { SILENCE_FRAME } = require('../util/Silence');
|
||||||
|
|
||||||
// The delay between packets when a user is considered to have stopped speaking
|
// The delay between packets when a user is considered to have stopped speaking
|
||||||
// https://github.com/discordjs/discord.js/issues/3524#issuecomment-540373200
|
// https://github.com/discordjs/discord.js/issues/3524#issuecomment-540373200
|
||||||
|
|
@ -56,7 +58,7 @@ class PacketHandler extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open packet
|
// Open packet
|
||||||
let packet = secretbox.methods.open(buffer.slice(12, end), this.nonce, secret_key);
|
let packet = sodium.methods.open(buffer.slice(12, end), this.nonce, secret_key);
|
||||||
if (!packet) return new Error('Failed to decrypt voice packet');
|
if (!packet) return new Error('Failed to decrypt voice packet');
|
||||||
packet = Buffer.from(packet);
|
packet = Buffer.from(packet);
|
||||||
|
|
||||||
|
|
@ -84,12 +86,30 @@ class PacketHandler extends EventEmitter {
|
||||||
const userStat = this.connection.ssrcMap.get(ssrc);
|
const userStat = this.connection.ssrcMap.get(ssrc);
|
||||||
if (!userStat) return;
|
if (!userStat) return;
|
||||||
|
|
||||||
|
let opusPacket;
|
||||||
|
const streamInfo = this.streams.get(userStat.userID);
|
||||||
|
// If the user is in video, we need to check if the packet is just silence
|
||||||
|
if (userStat.hasVideo) {
|
||||||
|
opusPacket = this.parseBuffer(buffer);
|
||||||
|
if (opusPacket instanceof Error) {
|
||||||
|
// Only emit an error if we were actively receiving packets from this user
|
||||||
|
if (streamInfo) {
|
||||||
|
this.emit('error', opusPacket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SILENCE_FRAME.equals(opusPacket)) {
|
||||||
|
// If this is a silence frame, pretend we never received it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let speakingTimeout = this.speakingTimeouts.get(ssrc);
|
let speakingTimeout = this.speakingTimeouts.get(ssrc);
|
||||||
if (typeof speakingTimeout === 'undefined') {
|
if (typeof speakingTimeout === 'undefined') {
|
||||||
// Ensure at least the speaking bit is set.
|
// Ensure at least the speaking bit is set.
|
||||||
// As the object is by reference, it's only needed once per client re-connect.
|
// As the object is by reference, it's only needed once per client re-connect.
|
||||||
if (userStat.speaking === 0) {
|
if (userStat.speaking === 0) {
|
||||||
userStat.speaking = 1;
|
userStat.speaking = Speaking.FLAGS.SPEAKING;
|
||||||
}
|
}
|
||||||
this.connection.onSpeaking({ user_id: userStat.userID, ssrc: ssrc, speaking: userStat.speaking });
|
this.connection.onSpeaking({ user_id: userStat.userID, ssrc: ssrc, speaking: userStat.speaking });
|
||||||
speakingTimeout = this.receiver.connection.client.setTimeout(() => {
|
speakingTimeout = this.receiver.connection.client.setTimeout(() => {
|
||||||
|
|
@ -106,15 +126,17 @@ class PacketHandler extends EventEmitter {
|
||||||
speakingTimeout.refresh();
|
speakingTimeout.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
let stream = this.streams.get(userStat.userID);
|
if (streamInfo) {
|
||||||
if (!stream) return;
|
const { stream } = streamInfo;
|
||||||
stream = stream.stream;
|
if (!opusPacket) {
|
||||||
const opusPacket = this.parseBuffer(buffer);
|
opusPacket = this.parseBuffer(buffer);
|
||||||
if (opusPacket instanceof Error) {
|
if (opusPacket instanceof Error) {
|
||||||
this.emit('error', opusPacket);
|
this.emit('error', opusPacket);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream.push(opusPacket);
|
||||||
}
|
}
|
||||||
stream.push(opusPacket);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,6 @@ class Silence extends Readable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Silence.SILENCE_FRAME = SILENCE_FRAME;
|
||||||
|
|
||||||
module.exports = Silence;
|
module.exports = Silence;
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ class WebSocketManager extends EventEmitter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current status of this WebSocketManager
|
* The current status of this WebSocketManager
|
||||||
* @type {number}
|
* @type {Status}
|
||||||
*/
|
*/
|
||||||
this.status = Status.IDLE;
|
this.status = Status.IDLE;
|
||||||
|
|
||||||
|
|
|
||||||
5
src/client/websocket/handlers/INTERACTION_CREATE.js
Normal file
5
src/client/websocket/handlers/INTERACTION_CREATE.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = (client, packet) => {
|
||||||
|
client.actions.InteractionCreate.handle(packet.d);
|
||||||
|
};
|
||||||
|
|
@ -71,7 +71,7 @@ const Messages = {
|
||||||
IMAGE_SIZE: size => `Invalid image size: ${size}`,
|
IMAGE_SIZE: size => `Invalid image size: ${size}`,
|
||||||
|
|
||||||
MESSAGE_BULK_DELETE_TYPE: 'The messages must be an Array, Collection, or number.',
|
MESSAGE_BULK_DELETE_TYPE: 'The messages must be an Array, Collection, or number.',
|
||||||
MESSAGE_NONCE_TYPE: 'Message nonce must fit in an unsigned 64-bit integer.',
|
MESSAGE_NONCE_TYPE: 'Message nonce must be an integer or a string.',
|
||||||
|
|
||||||
TYPING_COUNT: 'Count must be at least 1',
|
TYPING_COUNT: 'Count must be at least 1',
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ module.exports = {
|
||||||
// "Root" classes (starting points)
|
// "Root" classes (starting points)
|
||||||
BaseClient: require('./client/BaseClient'),
|
BaseClient: require('./client/BaseClient'),
|
||||||
Client: require('./client/Client'),
|
Client: require('./client/Client'),
|
||||||
|
InteractionClient: require('./client/InteractionClient'),
|
||||||
Shard: require('./sharding/Shard'),
|
Shard: require('./sharding/Shard'),
|
||||||
ShardClientUtil: require('./sharding/ShardClientUtil'),
|
ShardClientUtil: require('./sharding/ShardClientUtil'),
|
||||||
ShardingManager: require('./sharding/ShardingManager'),
|
ShardingManager: require('./sharding/ShardingManager'),
|
||||||
|
|
@ -33,6 +34,7 @@ module.exports = {
|
||||||
version: require('../package.json').version,
|
version: require('../package.json').version,
|
||||||
|
|
||||||
// Managers
|
// Managers
|
||||||
|
BaseGuildEmojiManager: require('./managers/BaseGuildEmojiManager'),
|
||||||
ChannelManager: require('./managers/ChannelManager'),
|
ChannelManager: require('./managers/ChannelManager'),
|
||||||
GuildChannelManager: require('./managers/GuildChannelManager'),
|
GuildChannelManager: require('./managers/GuildChannelManager'),
|
||||||
GuildEmojiManager: require('./managers/GuildEmojiManager'),
|
GuildEmojiManager: require('./managers/GuildEmojiManager'),
|
||||||
|
|
@ -57,6 +59,7 @@ module.exports = {
|
||||||
|
|
||||||
// Structures
|
// Structures
|
||||||
Application: require('./structures/interfaces/Application'),
|
Application: require('./structures/interfaces/Application'),
|
||||||
|
ApplicationCommand: require('./structures/ApplicationCommand'),
|
||||||
Base: require('./structures/Base'),
|
Base: require('./structures/Base'),
|
||||||
Activity: require('./structures/Presence').Activity,
|
Activity: require('./structures/Presence').Activity,
|
||||||
APIMessage: require('./structures/APIMessage'),
|
APIMessage: require('./structures/APIMessage'),
|
||||||
|
|
@ -79,6 +82,7 @@ module.exports = {
|
||||||
GuildPreview: require('./structures/GuildPreview'),
|
GuildPreview: require('./structures/GuildPreview'),
|
||||||
GuildTemplate: require('./structures/GuildTemplate'),
|
GuildTemplate: require('./structures/GuildTemplate'),
|
||||||
Integration: require('./structures/Integration'),
|
Integration: require('./structures/Integration'),
|
||||||
|
Interaction: require('./structures/Interaction'),
|
||||||
Invite: require('./structures/Invite'),
|
Invite: require('./structures/Invite'),
|
||||||
Message: require('./structures/Message'),
|
Message: require('./structures/Message'),
|
||||||
MessageAttachment: require('./structures/MessageAttachment'),
|
MessageAttachment: require('./structures/MessageAttachment'),
|
||||||
|
|
|
||||||
80
src/managers/BaseGuildEmojiManager.js
Normal file
80
src/managers/BaseGuildEmojiManager.js
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const BaseManager = require('./BaseManager');
|
||||||
|
const GuildEmoji = require('../structures/GuildEmoji');
|
||||||
|
const ReactionEmoji = require('../structures/ReactionEmoji');
|
||||||
|
const { parseEmoji } = require('../util/Util');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds methods to resolve GuildEmojis and stores their cache.
|
||||||
|
* @extends {BaseManager}
|
||||||
|
*/
|
||||||
|
class BaseGuildEmojiManager extends BaseManager {
|
||||||
|
constructor(client, iterable) {
|
||||||
|
super(client, iterable, GuildEmoji);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cache of GuildEmojis
|
||||||
|
* @type {Collection<Snowflake, GuildEmoji>}
|
||||||
|
* @name BaseGuildEmojiManager#cache
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data that can be resolved into a GuildEmoji object. This can be:
|
||||||
|
* * A custom emoji ID
|
||||||
|
* * A GuildEmoji object
|
||||||
|
* * A ReactionEmoji object
|
||||||
|
* @typedef {Snowflake|GuildEmoji|ReactionEmoji} EmojiResolvable
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves an EmojiResolvable to an Emoji object.
|
||||||
|
* @param {EmojiResolvable} emoji The Emoji resolvable to identify
|
||||||
|
* @returns {?GuildEmoji}
|
||||||
|
*/
|
||||||
|
resolve(emoji) {
|
||||||
|
if (emoji instanceof ReactionEmoji) return super.resolve(emoji.id);
|
||||||
|
return super.resolve(emoji);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves an EmojiResolvable to an Emoji ID string.
|
||||||
|
* @param {EmojiResolvable} emoji The Emoji resolvable to identify
|
||||||
|
* @returns {?Snowflake}
|
||||||
|
*/
|
||||||
|
resolveID(emoji) {
|
||||||
|
if (emoji instanceof ReactionEmoji) return emoji.id;
|
||||||
|
return super.resolveID(emoji);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data that can be resolved to give an emoji identifier. This can be:
|
||||||
|
* * The unicode representation of an emoji
|
||||||
|
* * The `<a:name:id>`, `<:name:id>`, `a:name:id` or `name:id` emoji identifier string of an emoji
|
||||||
|
* * An EmojiResolvable
|
||||||
|
* @typedef {string|EmojiResolvable} EmojiIdentifierResolvable
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves an EmojiResolvable to an emoji identifier.
|
||||||
|
* @param {EmojiIdentifierResolvable} emoji The emoji resolvable to resolve
|
||||||
|
* @returns {?string}
|
||||||
|
*/
|
||||||
|
resolveIdentifier(emoji) {
|
||||||
|
const emojiResolvable = this.resolve(emoji);
|
||||||
|
if (emojiResolvable) return emojiResolvable.identifier;
|
||||||
|
if (emoji instanceof ReactionEmoji) return emoji.identifier;
|
||||||
|
if (typeof emoji === 'string') {
|
||||||
|
const res = parseEmoji(emoji);
|
||||||
|
if (res && res.name.length) {
|
||||||
|
emoji = `${res.animated ? 'a:' : ''}${res.name}${res.id ? `:${res.id}` : ''}`;
|
||||||
|
}
|
||||||
|
if (!emoji.includes('%')) return encodeURIComponent(emoji);
|
||||||
|
return emoji;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BaseGuildEmojiManager;
|
||||||
|
|
@ -1,20 +1,18 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const BaseManager = require('./BaseManager');
|
const BaseGuildEmojiManager = require('./BaseGuildEmojiManager');
|
||||||
const { TypeError } = require('../errors');
|
const { TypeError } = require('../errors');
|
||||||
const GuildEmoji = require('../structures/GuildEmoji');
|
|
||||||
const ReactionEmoji = require('../structures/ReactionEmoji');
|
|
||||||
const Collection = require('../util/Collection');
|
const Collection = require('../util/Collection');
|
||||||
const DataResolver = require('../util/DataResolver');
|
const DataResolver = require('../util/DataResolver');
|
||||||
const { parseEmoji } = require('../util/Util');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages API methods for GuildEmojis and stores their cache.
|
* Manages API methods for GuildEmojis and stores their cache.
|
||||||
* @extends {BaseManager}
|
* @extends {BaseGuildEmojiManager}
|
||||||
*/
|
*/
|
||||||
class GuildEmojiManager extends BaseManager {
|
class GuildEmojiManager extends BaseGuildEmojiManager {
|
||||||
constructor(guild, iterable) {
|
constructor(guild, iterable) {
|
||||||
super(guild.client, iterable, GuildEmoji);
|
super(guild.client, iterable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The guild this manager belongs to
|
* The guild this manager belongs to
|
||||||
* @type {Guild}
|
* @type {Guild}
|
||||||
|
|
@ -22,12 +20,6 @@ class GuildEmojiManager extends BaseManager {
|
||||||
this.guild = guild;
|
this.guild = guild;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The cache of GuildEmojis
|
|
||||||
* @type {Collection<Snowflake, GuildEmoji>}
|
|
||||||
* @name GuildEmojiManager#cache
|
|
||||||
*/
|
|
||||||
|
|
||||||
add(data, cache) {
|
add(data, cache) {
|
||||||
return super.add(data, cache, { extras: [this.guild] });
|
return super.add(data, cache, { extras: [this.guild] });
|
||||||
}
|
}
|
||||||
|
|
@ -74,62 +66,6 @@ class GuildEmojiManager extends BaseManager {
|
||||||
.emojis.post({ data, reason })
|
.emojis.post({ data, reason })
|
||||||
.then(emoji => this.client.actions.GuildEmojiCreate.handle(this.guild, emoji).emoji);
|
.then(emoji => this.client.actions.GuildEmojiCreate.handle(this.guild, emoji).emoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Data that can be resolved into an GuildEmoji object. This can be:
|
|
||||||
* * A custom emoji ID
|
|
||||||
* * A GuildEmoji object
|
|
||||||
* * A ReactionEmoji object
|
|
||||||
* @typedef {Snowflake|GuildEmoji|ReactionEmoji} EmojiResolvable
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves an EmojiResolvable to an Emoji object.
|
|
||||||
* @param {EmojiResolvable} emoji The Emoji resolvable to identify
|
|
||||||
* @returns {?GuildEmoji}
|
|
||||||
*/
|
|
||||||
resolve(emoji) {
|
|
||||||
if (emoji instanceof ReactionEmoji) return super.resolve(emoji.id);
|
|
||||||
return super.resolve(emoji);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves an EmojiResolvable to an Emoji ID string.
|
|
||||||
* @param {EmojiResolvable} emoji The Emoji resolvable to identify
|
|
||||||
* @returns {?Snowflake}
|
|
||||||
*/
|
|
||||||
resolveID(emoji) {
|
|
||||||
if (emoji instanceof ReactionEmoji) return emoji.id;
|
|
||||||
return super.resolveID(emoji);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data that can be resolved to give an emoji identifier. This can be:
|
|
||||||
* * The unicode representation of an emoji
|
|
||||||
* * The `<a:name:id>`, `<:name:id>`, `:name:id` or `a:name:id` emoji identifier string of an emoji
|
|
||||||
* * An EmojiResolvable
|
|
||||||
* @typedef {string|EmojiResolvable} EmojiIdentifierResolvable
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves an EmojiResolvable to an emoji identifier.
|
|
||||||
* @param {EmojiIdentifierResolvable} emoji The emoji resolvable to resolve
|
|
||||||
* @returns {?string}
|
|
||||||
*/
|
|
||||||
resolveIdentifier(emoji) {
|
|
||||||
const emojiResolvable = this.resolve(emoji);
|
|
||||||
if (emojiResolvable) return emojiResolvable.identifier;
|
|
||||||
if (emoji instanceof ReactionEmoji) return emoji.identifier;
|
|
||||||
if (typeof emoji === 'string') {
|
|
||||||
const res = parseEmoji(emoji);
|
|
||||||
if (res && res.name.length) {
|
|
||||||
emoji = `${res.animated ? 'a:' : ''}${res.name}${res.id ? `:${res.id}` : ''}`;
|
|
||||||
}
|
|
||||||
if (!emoji.includes('%')) return encodeURIComponent(emoji);
|
|
||||||
else return emoji;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = GuildEmojiManager;
|
module.exports = GuildEmojiManager;
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,7 @@ class GuildMemberManager extends BaseManager {
|
||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
ban(user, options = { days: 0 }) {
|
ban(user, options = { days: 0 }) {
|
||||||
|
if (typeof options !== 'object') return Promise.reject(new TypeError('INVALID_TYPE', 'options', 'object', true));
|
||||||
if (options.days) options.delete_message_days = options.days;
|
if (options.days) options.delete_message_days = options.days;
|
||||||
const id = this.client.users.resolveID(user);
|
const id = this.client.users.resolveID(user);
|
||||||
if (!id) return Promise.reject(new Error('BAN_RESOLVE_ID', true));
|
if (!id) return Promise.reject(new Error('BAN_RESOLVE_ID', true));
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
const BaseManager = require('./BaseManager');
|
const BaseManager = require('./BaseManager');
|
||||||
const Role = require('../structures/Role');
|
const Role = require('../structures/Role');
|
||||||
|
const Collection = require('../util/Collection');
|
||||||
const Permissions = require('../util/Permissions');
|
const Permissions = require('../util/Permissions');
|
||||||
const { resolveColor } = require('../util/Util');
|
const { resolveColor } = require('../util/Util');
|
||||||
|
|
||||||
|
|
@ -31,10 +32,10 @@ class RoleManager extends BaseManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains one or more roles from Discord, or the role cache if they're already available.
|
* Obtains one or more roles from Discord, or the role cache if they're already available.
|
||||||
* @param {Snowflake} [id] ID or IDs of the role(s)
|
* @param {Snowflake} [id] ID of the role to fetch
|
||||||
* @param {boolean} [cache=true] Whether to cache the new roles objects if it weren't already
|
* @param {boolean} [cache=true] Whether to cache the new role object(s) if they weren't already
|
||||||
* @param {boolean} [force=false] Whether to skip the cache check and request the API
|
* @param {boolean} [force=false] Whether to skip the cache check and request the API
|
||||||
* @returns {Promise<Role|RoleManager>}
|
* @returns {Promise<?Role|Collection<Snowflake, Role>>}
|
||||||
* @example
|
* @example
|
||||||
* // Fetch all roles from the guild
|
* // Fetch all roles from the guild
|
||||||
* message.guild.roles.fetch()
|
* message.guild.roles.fetch()
|
||||||
|
|
@ -53,9 +54,10 @@ class RoleManager extends BaseManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We cannot fetch a single role, as of this commit's date, Discord API throws with 405
|
// We cannot fetch a single role, as of this commit's date, Discord API throws with 405
|
||||||
const roles = await this.client.api.guilds(this.guild.id).roles.get();
|
const data = await this.client.api.guilds(this.guild.id).roles.get();
|
||||||
for (const role of roles) this.add(role, cache);
|
const roles = new Collection();
|
||||||
return id ? this.cache.get(id) || null : this;
|
for (const role of data) roles.set(role.id, this.add(role, cache));
|
||||||
|
return id ? roles.get(id) || null : roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -74,13 +74,21 @@ class APIMessage {
|
||||||
return this.target instanceof Message;
|
return this.target instanceof Message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the target is an interaction
|
||||||
|
* @type {boolean}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get isInteraction() {
|
||||||
|
const Interaction = require('./Interaction');
|
||||||
|
return this.target instanceof Interaction;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the content of this message.
|
* Makes the content of this message.
|
||||||
* @returns {?(string|string[])}
|
* @returns {?(string|string[])}
|
||||||
*/
|
*/
|
||||||
makeContent() {
|
makeContent() {
|
||||||
const GuildMember = require('./GuildMember');
|
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
if (this.options.content === null) {
|
if (this.options.content === null) {
|
||||||
content = '';
|
content = '';
|
||||||
|
|
@ -110,25 +118,14 @@ class APIMessage {
|
||||||
const isCode = typeof this.options.code !== 'undefined' && this.options.code !== false;
|
const isCode = typeof this.options.code !== 'undefined' && this.options.code !== false;
|
||||||
const splitOptions = isSplit ? { ...this.options.split } : undefined;
|
const splitOptions = isSplit ? { ...this.options.split } : undefined;
|
||||||
|
|
||||||
let mentionPart = '';
|
if (content) {
|
||||||
if (this.options.reply && !this.isUser && this.target.type !== 'dm') {
|
|
||||||
const id = this.target.client.users.resolveID(this.options.reply);
|
|
||||||
mentionPart = `<@${this.options.reply instanceof GuildMember && this.options.reply.nickname ? '!' : ''}${id}>, `;
|
|
||||||
if (isSplit) {
|
|
||||||
splitOptions.prepend = `${mentionPart}${splitOptions.prepend || ''}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content || mentionPart) {
|
|
||||||
if (isCode) {
|
if (isCode) {
|
||||||
const codeName = typeof this.options.code === 'string' ? this.options.code : '';
|
const codeName = typeof this.options.code === 'string' ? this.options.code : '';
|
||||||
content = `${mentionPart}\`\`\`${codeName}\n${Util.cleanCodeBlockContent(content)}\n\`\`\``;
|
content = `\`\`\`${codeName}\n${Util.cleanCodeBlockContent(content)}\n\`\`\``;
|
||||||
if (isSplit) {
|
if (isSplit) {
|
||||||
splitOptions.prepend = `${splitOptions.prepend || ''}\`\`\`${codeName}\n`;
|
splitOptions.prepend = `${splitOptions.prepend || ''}\`\`\`${codeName}\n`;
|
||||||
splitOptions.append = `\n\`\`\`${splitOptions.append || ''}`;
|
splitOptions.append = `\n\`\`\`${splitOptions.append || ''}`;
|
||||||
}
|
}
|
||||||
} else if (mentionPart) {
|
|
||||||
content = `${mentionPart}${content}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSplit) {
|
if (isSplit) {
|
||||||
|
|
@ -151,8 +148,11 @@ class APIMessage {
|
||||||
|
|
||||||
let nonce;
|
let nonce;
|
||||||
if (typeof this.options.nonce !== 'undefined') {
|
if (typeof this.options.nonce !== 'undefined') {
|
||||||
nonce = parseInt(this.options.nonce);
|
nonce = this.options.nonce;
|
||||||
if (isNaN(nonce) || nonce < 0) throw new RangeError('MESSAGE_NONCE_TYPE');
|
// eslint-disable-next-line max-len
|
||||||
|
if (typeof nonce === 'number' ? !Number.isInteger(nonce) : typeof nonce !== 'string') {
|
||||||
|
throw new RangeError('MESSAGE_NONCE_TYPE');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const embedLikes = [];
|
const embedLikes = [];
|
||||||
|
|
@ -176,25 +176,28 @@ class APIMessage {
|
||||||
if (this.isMessage) {
|
if (this.isMessage) {
|
||||||
// eslint-disable-next-line eqeqeq
|
// eslint-disable-next-line eqeqeq
|
||||||
flags = this.options.flags != null ? new MessageFlags(this.options.flags).bitfield : this.target.flags.bitfield;
|
flags = this.options.flags != null ? new MessageFlags(this.options.flags).bitfield : this.target.flags.bitfield;
|
||||||
|
} else if (this.isInteraction) {
|
||||||
|
flags = this.options.ephemeral ? MessageFlags.EPHEMERAL : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let allowedMentions =
|
let allowedMentions =
|
||||||
typeof this.options.allowedMentions === 'undefined'
|
typeof this.options.allowedMentions === 'undefined'
|
||||||
? this.target.client.options.allowedMentions
|
? this.target.client.options.allowedMentions
|
||||||
: this.options.allowedMentions;
|
: this.options.allowedMentions;
|
||||||
if (this.options.reply) {
|
|
||||||
const id = this.target.client.users.resolveID(this.options.reply);
|
if (allowedMentions) {
|
||||||
if (allowedMentions) {
|
allowedMentions = Util.cloneObject(allowedMentions);
|
||||||
// Clone the object as not to alter the ClientOptions object
|
allowedMentions.replied_user = allowedMentions.repliedUser;
|
||||||
allowedMentions = Util.cloneObject(allowedMentions);
|
delete allowedMentions.repliedUser;
|
||||||
const parsed = allowedMentions.parse && allowedMentions.parse.includes('users');
|
}
|
||||||
// Check if the mention won't be parsed, and isn't supplied in `users`
|
|
||||||
if (!parsed && !(allowedMentions.users && allowedMentions.users.includes(id))) {
|
let message_reference;
|
||||||
if (!allowedMentions.users) allowedMentions.users = [];
|
if (typeof this.options.replyTo !== 'undefined') {
|
||||||
allowedMentions.users.push(id);
|
const message_id = this.isMessage
|
||||||
}
|
? this.target.channel.messages.resolveID(this.options.replyTo)
|
||||||
} else {
|
: this.target.messages.resolveID(this.options.replyTo);
|
||||||
allowedMentions = { users: [id] };
|
if (message_id) {
|
||||||
|
message_reference = { message_id };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,8 +209,10 @@ class APIMessage {
|
||||||
embeds,
|
embeds,
|
||||||
username,
|
username,
|
||||||
avatar_url: avatarURL,
|
avatar_url: avatarURL,
|
||||||
allowed_mentions: typeof content === 'undefined' ? undefined : allowedMentions,
|
allowed_mentions:
|
||||||
|
typeof content === 'undefined' && typeof message_reference === 'undefined' ? undefined : allowedMentions,
|
||||||
flags,
|
flags,
|
||||||
|
message_reference,
|
||||||
};
|
};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
102
src/structures/ApplicationCommand.js
Normal file
102
src/structures/ApplicationCommand.js
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Base = require('./Base');
|
||||||
|
const { ApplicationCommandOptionType } = require('../util/Constants');
|
||||||
|
const Snowflake = require('../util/Snowflake');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an application command, see {@link InteractionClient}.
|
||||||
|
* @extends {Base}
|
||||||
|
*/
|
||||||
|
class ApplicationCommand extends Base {
|
||||||
|
constructor(client, data, guildID) {
|
||||||
|
super(client);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the guild this command is part of, if any.
|
||||||
|
* @type {Snowflake?}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.guildID = guildID || null;
|
||||||
|
|
||||||
|
this._patch(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
_patch(data) {
|
||||||
|
/**
|
||||||
|
* The ID of this command.
|
||||||
|
* @type {Snowflake}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.id = data.id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the application which owns this command.
|
||||||
|
* @type {Snowflake}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.appplicationID = data.application_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of this command.
|
||||||
|
* @type {string}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.name = data.name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The description of this command.
|
||||||
|
* @type {string}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.description = data.description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The options of this command.
|
||||||
|
* @type {Object[]}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.options = data.options?.map(function m(o) {
|
||||||
|
return {
|
||||||
|
type: ApplicationCommandOptionType[o.type],
|
||||||
|
name: o.name,
|
||||||
|
description: o.description,
|
||||||
|
default: o.default,
|
||||||
|
required: o.required,
|
||||||
|
choices: o.choices,
|
||||||
|
options: o.options ? o.options.map(m) : undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timestamp the command was created at.
|
||||||
|
* @type {number}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get createdTimestamp() {
|
||||||
|
return Snowflake.deconstruct(this.id).timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time the command was created at.
|
||||||
|
* @type {Date}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get createdAt() {
|
||||||
|
return new Date(this.createdTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete this command.
|
||||||
|
*/
|
||||||
|
async delete() {
|
||||||
|
let path = this.client.api.applications('@me');
|
||||||
|
if (this.guildID) {
|
||||||
|
path = path.guilds(this.guildID);
|
||||||
|
}
|
||||||
|
await path.commands(this.id).delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ApplicationCommand;
|
||||||
|
|
@ -17,7 +17,7 @@ class BaseGuildEmoji extends Emoji {
|
||||||
*/
|
*/
|
||||||
this.guild = guild;
|
this.guild = guild;
|
||||||
|
|
||||||
this.requireColons = null;
|
this.requiresColons = null;
|
||||||
this.managed = null;
|
this.managed = null;
|
||||||
this.available = null;
|
this.available = null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class Emoji extends Base {
|
||||||
* @example
|
* @example
|
||||||
* // Send a custom emoji from a guild:
|
* // Send a custom emoji from a guild:
|
||||||
* const emoji = guild.emojis.cache.first();
|
* const emoji = guild.emojis.cache.first();
|
||||||
* msg.reply(`Hello! ${emoji}`);
|
* msg.channel.send(`Hello! ${emoji}`);
|
||||||
* @example
|
* @example
|
||||||
* // Send the emoji used in a reaction to the channel the reaction is part of
|
* // Send the emoji used in a reaction to the channel the reaction is part of
|
||||||
* reaction.message.channel.send(`The emoji used was: ${reaction.emoji}`);
|
* reaction.message.channel.send(`The emoji used was: ${reaction.emoji}`);
|
||||||
|
|
|
||||||
|
|
@ -637,18 +637,6 @@ class Guild extends Base {
|
||||||
return this.voiceStates.cache.get(this.client.user.id);
|
return this.voiceStates.cache.get(this.client.user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the GuildMember form of a User object, if the user is present in the guild.
|
|
||||||
* @param {UserResolvable} user The user that you want to obtain the GuildMember of
|
|
||||||
* @returns {?GuildMember}
|
|
||||||
* @example
|
|
||||||
* // Get the guild member of a user
|
|
||||||
* const member = guild.member(message.author);
|
|
||||||
*/
|
|
||||||
member(user) {
|
|
||||||
return this.members.resolve(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches this guild.
|
* Fetches this guild.
|
||||||
* @returns {Promise<Guild>}
|
* @returns {Promise<Guild>}
|
||||||
|
|
@ -1444,6 +1432,23 @@ class Guild extends Base {
|
||||||
.then(() => this);
|
.then(() => this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the commands associated with this guild.
|
||||||
|
* @returns {ApplicationCommand[]} A list of commands.
|
||||||
|
*/
|
||||||
|
getCommands() {
|
||||||
|
return this.client.interactionClient.getCommands(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a command. See {@link InteractionClient}.
|
||||||
|
* @param {Object} command The command description.
|
||||||
|
* @returns {ApplicationCommand} The created command.
|
||||||
|
*/
|
||||||
|
createCommand(command) {
|
||||||
|
return this.client.interactionClient.createCommand(command, this.id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Leaves the guild.
|
* Leaves the guild.
|
||||||
* @returns {Promise<Guild>}
|
* @returns {Promise<Guild>}
|
||||||
|
|
|
||||||
|
|
@ -198,8 +198,9 @@ class GuildTemplate extends Base {
|
||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get guild() {
|
get guild() {
|
||||||
return this.client.guilds.get(this.guildID) || null;
|
return this.client.guilds.cache.get(this.guildID) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The URL of this template
|
* The URL of this template
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
|
|
||||||
133
src/structures/Interaction.js
Normal file
133
src/structures/Interaction.js
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const APIMessage = require('./APIMessage');
|
||||||
|
const Base = require('./Base');
|
||||||
|
const Snowflake = require('../util/Snowflake');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an interaction, see {@link InteractionClient}.
|
||||||
|
* @extends {Base}
|
||||||
|
*/
|
||||||
|
class Interaction extends Base {
|
||||||
|
constructor(client, data, syncHandle) {
|
||||||
|
super(client);
|
||||||
|
this.syncHandle = syncHandle;
|
||||||
|
this._patch(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
_patch(data) {
|
||||||
|
/**
|
||||||
|
* The ID of this interaction.
|
||||||
|
* @type {Snowflake}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.id = data.id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The token of this interaction.
|
||||||
|
* @type {string}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.token = data.token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the invoked command.
|
||||||
|
* @type {Snowflake}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.commandID = data.data.id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the invoked command.
|
||||||
|
* @type {string}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.commandName = data.data.name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The options passed to the command.
|
||||||
|
* @type {Object}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.options = data.data.options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The channel this interaction was sent in.
|
||||||
|
* @type {?Channel}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.channel = this.client.channels?.cache.get(data.channel_id) || null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The guild this interaction was sent in, if any.
|
||||||
|
* @type {?Guild}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.guild = data.guild_id ? this.client.guilds?.cache.get(data.guild_id) : null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this interaction was sent in a guild, the member which sent it.
|
||||||
|
* @type {?Member}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.member = data.member ? this.guild?.members.add(data.member, false) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timestamp the interaction was created at.
|
||||||
|
* @type {number}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get createdTimestamp() {
|
||||||
|
return Snowflake.deconstruct(this.id).timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time the interaction was created at.
|
||||||
|
* @type {Date}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get createdAt() {
|
||||||
|
return new Date(this.createdTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledge this interaction without content.
|
||||||
|
*/
|
||||||
|
async acknowledge() {
|
||||||
|
await this.syncHandle.acknowledge();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reply to this interaction.
|
||||||
|
* @param {(StringResolvable | APIMessage)?} content The content for the message.
|
||||||
|
* @param {(MessageOptions | MessageAdditions)?} options The options to provide.
|
||||||
|
*/
|
||||||
|
async reply(content, options) {
|
||||||
|
let apiMessage;
|
||||||
|
|
||||||
|
if (content instanceof APIMessage) {
|
||||||
|
apiMessage = content.resolveData();
|
||||||
|
} else {
|
||||||
|
apiMessage = APIMessage.create(this, content, options).resolveData();
|
||||||
|
if (Array.isArray(apiMessage.data.content)) {
|
||||||
|
throw new Error('Message is too long');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolved = await apiMessage.resolveFiles();
|
||||||
|
|
||||||
|
if (!this.syncHandle.reply(resolved)) {
|
||||||
|
const clientID =
|
||||||
|
this.client.interactionClient.clientID || (await this.client.api.oauth2.applications('@me').get()).id;
|
||||||
|
|
||||||
|
await this.client.api.webhooks(clientID, this.token).post({
|
||||||
|
auth: false,
|
||||||
|
data: resolved.data,
|
||||||
|
files: resolved.files,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Interaction;
|
||||||
|
|
@ -205,11 +205,11 @@ class Message extends Base {
|
||||||
this.flags = new MessageFlags(data.flags).freeze();
|
this.flags = new MessageFlags(data.flags).freeze();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference data sent in a crossposted message.
|
* Reference data sent in a crossposted message or inline reply.
|
||||||
* @typedef {Object} MessageReference
|
* @typedef {Object} MessageReference
|
||||||
* @property {string} channelID ID of the channel the message was crossposted from
|
* @property {string} channelID ID of the channel the message was referenced
|
||||||
* @property {?string} guildID ID of the guild the message was crossposted from
|
* @property {?string} guildID ID of the guild the message was referenced
|
||||||
* @property {?string} messageID ID of the message that was crossposted
|
* @property {?string} messageID ID of the message that was referenced
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -223,6 +223,10 @@ class Message extends Base {
|
||||||
messageID: data.message_reference.message_id,
|
messageID: data.message_reference.message_id,
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
if (data.referenced_message) {
|
||||||
|
this.channel.messages.add(data.referenced_message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -284,7 +288,7 @@ class Message extends Base {
|
||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get member() {
|
get member() {
|
||||||
return this.guild ? this.guild.member(this.author) || null : null;
|
return this.guild ? this.guild.members.resolve(this.author) || null : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -425,6 +429,18 @@ class Message extends Base {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Message this crosspost/reply/pin-add references, if cached
|
||||||
|
* @type {?Message}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get referencedMessage() {
|
||||||
|
if (!this.reference) return null;
|
||||||
|
const referenceChannel = this.client.channels.resolve(this.reference.channelID);
|
||||||
|
if (!referenceChannel) return null;
|
||||||
|
return referenceChannel.messages.resolve(this.reference.messageID);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the message is crosspostable by the client user
|
* Whether the message is crosspostable by the client user
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
|
|
@ -588,21 +604,19 @@ class Message extends Base {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replies to the message.
|
* Send an inline reply to this message.
|
||||||
* @param {StringResolvable|APIMessage} [content=''] The content for the message
|
* @param {StringResolvable|APIMessage} [content=''] The content for the message
|
||||||
* @param {MessageOptions|MessageAdditions} [options={}] The options to provide
|
* @param {MessageOptions|MessageAdditions} [options] The additional options to provide
|
||||||
|
* @param {MessageResolvable} [options.replyTo=this] The message to reply to
|
||||||
* @returns {Promise<Message|Message[]>}
|
* @returns {Promise<Message|Message[]>}
|
||||||
* @example
|
|
||||||
* // Reply to a message
|
|
||||||
* message.reply('Hey, I\'m a reply!')
|
|
||||||
* .then(() => console.log(`Sent a reply to ${message.author.username}`))
|
|
||||||
* .catch(console.error);
|
|
||||||
*/
|
*/
|
||||||
reply(content, options) {
|
reply(content, options) {
|
||||||
return this.channel.send(
|
return this.channel.send(
|
||||||
content instanceof APIMessage
|
content instanceof APIMessage
|
||||||
? content
|
? content
|
||||||
: APIMessage.transformOptions(content, options, { reply: this.member || this.author }),
|
: APIMessage.transformOptions(content, options, {
|
||||||
|
replyTo: this,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ class MessageMentions {
|
||||||
if (!this.guild) return null;
|
if (!this.guild) return null;
|
||||||
this._members = new Collection();
|
this._members = new Collection();
|
||||||
this.users.forEach(user => {
|
this.users.forEach(user => {
|
||||||
const member = this.guild.member(user);
|
const member = this.guild.members.resolve(user);
|
||||||
if (member) this._members.set(member.user.id, member);
|
if (member) this._members.set(member.user.id, member);
|
||||||
});
|
});
|
||||||
return this._members;
|
return this._members;
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,6 @@ class MessageReaction {
|
||||||
*/
|
*/
|
||||||
this.message = message;
|
this.message = message;
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the client has given this reaction
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
this.me = data.me;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A manager of the users that have given this reaction
|
* A manager of the users that have given this reaction
|
||||||
* @type {ReactionUserManager}
|
* @type {ReactionUserManager}
|
||||||
|
|
@ -54,6 +48,12 @@ class MessageReaction {
|
||||||
*/
|
*/
|
||||||
this.count = data.count;
|
this.count = data.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the client has given this reaction
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
this.me = data.me;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ class Role extends Base {
|
||||||
*/
|
*/
|
||||||
get editable() {
|
get editable() {
|
||||||
if (this.managed) return false;
|
if (this.managed) return false;
|
||||||
const clientMember = this.guild.member(this.client.user);
|
const clientMember = this.guild.members.resolve(this.client.user);
|
||||||
if (!clientMember.permissions.has(Permissions.FLAGS.MANAGE_ROLES)) return false;
|
if (!clientMember.permissions.has(Permissions.FLAGS.MANAGE_ROLES)) return false;
|
||||||
return clientMember.roles.highest.comparePositionTo(this) > 0;
|
return clientMember.roles.highest.comparePositionTo(this) > 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ class User extends Base {
|
||||||
this.id = data.id;
|
this.id = data.id;
|
||||||
|
|
||||||
this.system = null;
|
this.system = null;
|
||||||
this.locale = null;
|
|
||||||
this.flags = null;
|
this.flags = null;
|
||||||
|
|
||||||
this._patch(data);
|
this._patch(data);
|
||||||
|
|
@ -81,14 +80,6 @@ class User extends Base {
|
||||||
this.system = Boolean(data.system);
|
this.system = Boolean(data.system);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('locale' in data) {
|
|
||||||
/**
|
|
||||||
* The locale of the user's client (ISO 639-1)
|
|
||||||
* @type {?string}
|
|
||||||
*/
|
|
||||||
this.locale = data.locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('public_flags' in data) {
|
if ('public_flags' in data) {
|
||||||
/**
|
/**
|
||||||
* The flags for this user
|
* The flags for this user
|
||||||
|
|
@ -287,7 +278,7 @@ class User extends Base {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches this user's flags.
|
* Fetches this user's flags.
|
||||||
* @param {boolean} [force=false] Whether to skip the cache check and request the AP
|
* @param {boolean} [force=false] Whether to skip the cache check and request the API
|
||||||
* @returns {Promise<UserFlags>}
|
* @returns {Promise<UserFlags>}
|
||||||
*/
|
*/
|
||||||
async fetchFlags(force = false) {
|
async fetchFlags(force = false) {
|
||||||
|
|
@ -299,7 +290,7 @@ class User extends Base {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches this user.
|
* Fetches this user.
|
||||||
* @param {boolean} [force=false] Whether to skip the cache check and request the AP
|
* @param {boolean} [force=false] Whether to skip the cache check and request the API
|
||||||
* @returns {Promise<User>}
|
* @returns {Promise<User>}
|
||||||
*/
|
*/
|
||||||
fetch(force = false) {
|
fetch(force = false) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
|
const { TypeError } = require('../../errors');
|
||||||
const Collection = require('../../util/Collection');
|
const Collection = require('../../util/Collection');
|
||||||
const Util = require('../../util/Util');
|
const Util = require('../../util/Util');
|
||||||
|
|
||||||
|
|
@ -74,6 +75,10 @@ class Collector extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
this._idletimeout = null;
|
this._idletimeout = null;
|
||||||
|
|
||||||
|
if (typeof filter !== 'function') {
|
||||||
|
throw new TypeError('INVALID_TYPE', 'filter', 'function');
|
||||||
|
}
|
||||||
|
|
||||||
this.handleCollect = this.handleCollect.bind(this);
|
this.handleCollect = this.handleCollect.bind(this);
|
||||||
this.handleDispose = this.handleDispose.bind(this);
|
this.handleDispose = this.handleDispose.bind(this);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ class TextBasedChannel {
|
||||||
* @property {string|boolean} [code] Language for optional codeblock formatting to apply
|
* @property {string|boolean} [code] Language for optional codeblock formatting to apply
|
||||||
* @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if
|
* @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if
|
||||||
* it exceeds the character limit. If an object is provided, these are the options for splitting the message
|
* it exceeds the character limit. If an object is provided, these are the options for splitting the message
|
||||||
* @property {UserResolvable} [reply] User to reply to (prefixes the message with a mention, except in DMs)
|
* @property {MessageResolvable} [replyTo] The message to reply to (must be in the same channel)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -74,6 +74,7 @@ class TextBasedChannel {
|
||||||
* @property {MessageMentionTypes[]} [parse] Types of mentions to be parsed
|
* @property {MessageMentionTypes[]} [parse] Types of mentions to be parsed
|
||||||
* @property {Snowflake[]} [users] Snowflakes of Users to be parsed as mentions
|
* @property {Snowflake[]} [users] Snowflakes of Users to be parsed as mentions
|
||||||
* @property {Snowflake[]} [roles] Snowflakes of Roles to be parsed as mentions
|
* @property {Snowflake[]} [roles] Snowflakes of Roles to be parsed as mentions
|
||||||
|
* @property {boolean} [repliedUser] Whether the author of the Message being replied to should be pinged
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -77,14 +77,14 @@ exports.DefaultOptions = {
|
||||||
/**
|
/**
|
||||||
* HTTP options
|
* HTTP options
|
||||||
* @typedef {Object} HTTPOptions
|
* @typedef {Object} HTTPOptions
|
||||||
* @property {number} [version=7] API version to use
|
* @property {number} [version=8] API version to use
|
||||||
* @property {string} [api='https://discord.com/api'] Base url of the API
|
* @property {string} [api='https://discord.com/api'] Base url of the API
|
||||||
* @property {string} [cdn='https://cdn.discordapp.com'] Base url of the CDN
|
* @property {string} [cdn='https://cdn.discordapp.com'] Base url of the CDN
|
||||||
* @property {string} [invite='https://discord.gg'] Base url of invites
|
* @property {string} [invite='https://discord.gg'] Base url of invites
|
||||||
* @property {string} [template='https://discord.new'] Base url of templates
|
* @property {string} [template='https://discord.new'] Base url of templates
|
||||||
*/
|
*/
|
||||||
http: {
|
http: {
|
||||||
version: 7,
|
version: 8,
|
||||||
api: 'https://discord.com/api',
|
api: 'https://discord.com/api',
|
||||||
cdn: 'https://cdn.discordapp.com',
|
cdn: 'https://cdn.discordapp.com',
|
||||||
invite: 'https://discord.gg',
|
invite: 'https://discord.gg',
|
||||||
|
|
@ -282,6 +282,7 @@ exports.Events = {
|
||||||
SHARD_READY: 'shardReady',
|
SHARD_READY: 'shardReady',
|
||||||
SHARD_RESUME: 'shardResume',
|
SHARD_RESUME: 'shardResume',
|
||||||
INVALIDATED: 'invalidated',
|
INVALIDATED: 'invalidated',
|
||||||
|
INTERACTION_CREATE: 'interactionCreate',
|
||||||
RAW: 'raw',
|
RAW: 'raw',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -345,6 +346,7 @@ exports.PartialTypes = keyMirror(['USER', 'CHANNEL', 'GUILD_MEMBER', 'MESSAGE',
|
||||||
* * VOICE_STATE_UPDATE
|
* * VOICE_STATE_UPDATE
|
||||||
* * VOICE_SERVER_UPDATE
|
* * VOICE_SERVER_UPDATE
|
||||||
* * WEBHOOKS_UPDATE
|
* * WEBHOOKS_UPDATE
|
||||||
|
* * INTERACTION_CREATE
|
||||||
* @typedef {string} WSEventType
|
* @typedef {string} WSEventType
|
||||||
*/
|
*/
|
||||||
exports.WSEvents = keyMirror([
|
exports.WSEvents = keyMirror([
|
||||||
|
|
@ -384,6 +386,7 @@ exports.WSEvents = keyMirror([
|
||||||
'VOICE_STATE_UPDATE',
|
'VOICE_STATE_UPDATE',
|
||||||
'VOICE_SERVER_UPDATE',
|
'VOICE_SERVER_UPDATE',
|
||||||
'WEBHOOKS_UPDATE',
|
'WEBHOOKS_UPDATE',
|
||||||
|
'INTERACTION_CREATE',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -403,6 +406,7 @@ exports.WSEvents = keyMirror([
|
||||||
* * CHANNEL_FOLLOW_ADD
|
* * CHANNEL_FOLLOW_ADD
|
||||||
* * GUILD_DISCOVERY_DISQUALIFIED
|
* * GUILD_DISCOVERY_DISQUALIFIED
|
||||||
* * GUILD_DISCOVERY_REQUALIFIED
|
* * GUILD_DISCOVERY_REQUALIFIED
|
||||||
|
* * REPLY
|
||||||
* @typedef {string} MessageType
|
* @typedef {string} MessageType
|
||||||
*/
|
*/
|
||||||
exports.MessageTypes = [
|
exports.MessageTypes = [
|
||||||
|
|
@ -422,6 +426,10 @@ exports.MessageTypes = [
|
||||||
null,
|
null,
|
||||||
'GUILD_DISCOVERY_DISQUALIFIED',
|
'GUILD_DISCOVERY_DISQUALIFIED',
|
||||||
'GUILD_DISCOVERY_REQUALIFIED',
|
'GUILD_DISCOVERY_REQUALIFIED',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'REPLY',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -671,6 +679,33 @@ exports.WebhookTypes = [
|
||||||
'Channel Follower',
|
'Channel Follower',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
exports.ApplicationCommandOptionType = {
|
||||||
|
SUB_COMMAND: 1,
|
||||||
|
SUB_COMMAND_GROUP: 2,
|
||||||
|
STRING: 3,
|
||||||
|
INTEGER: 4,
|
||||||
|
BOOLEAN: 5,
|
||||||
|
USER: 6,
|
||||||
|
CHANNEL: 7,
|
||||||
|
ROLE: 8,
|
||||||
|
};
|
||||||
|
Object.entries(exports.ApplicationCommandOptionType).forEach(([k, v]) => {
|
||||||
|
exports.ApplicationCommandOptionType[v] = k;
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.InteractionType = {
|
||||||
|
PING: 1,
|
||||||
|
APPLICATION_COMMAND: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.InteractionResponseType = {
|
||||||
|
PONG: 1,
|
||||||
|
ACKNOWLEDGE: 2,
|
||||||
|
CHANNEL_MESSAGE: 3,
|
||||||
|
CHANNEL_MESSAGE_WITH_SOURCE: 4,
|
||||||
|
ACKNOWLEDGE_WITH_SOURCE: 5,
|
||||||
|
};
|
||||||
|
|
||||||
function keyMirror(arr) {
|
function keyMirror(arr) {
|
||||||
let tmp = Object.create(null);
|
let tmp = Object.create(null);
|
||||||
for (const value of arr) tmp[value] = value;
|
for (const value of arr) tmp[value] = value;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ class MessageFlags extends BitField {}
|
||||||
* * `SUPPRESS_EMBEDS`
|
* * `SUPPRESS_EMBEDS`
|
||||||
* * `SOURCE_MESSAGE_DELETED`
|
* * `SOURCE_MESSAGE_DELETED`
|
||||||
* * `URGENT`
|
* * `URGENT`
|
||||||
|
* * `EPHEMERAL`
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
* @see {@link https://discord.com/developers/docs/resources/channel#message-object-message-flags}
|
* @see {@link https://discord.com/developers/docs/resources/channel#message-object-message-flags}
|
||||||
*/
|
*/
|
||||||
|
|
@ -31,6 +32,7 @@ MessageFlags.FLAGS = {
|
||||||
SUPPRESS_EMBEDS: 1 << 2,
|
SUPPRESS_EMBEDS: 1 << 2,
|
||||||
SOURCE_MESSAGE_DELETED: 1 << 3,
|
SOURCE_MESSAGE_DELETED: 1 << 3,
|
||||||
URGENT: 1 << 4,
|
URGENT: 1 << 4,
|
||||||
|
EPHEMERAL: 1 << 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = MessageFlags;
|
module.exports = MessageFlags;
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,19 @@ const libs = {
|
||||||
open: sodium.api.crypto_secretbox_open_easy,
|
open: sodium.api.crypto_secretbox_open_easy,
|
||||||
close: sodium.api.crypto_secretbox_easy,
|
close: sodium.api.crypto_secretbox_easy,
|
||||||
random: n => sodium.randombytes_buf(n),
|
random: n => sodium.randombytes_buf(n),
|
||||||
|
verify: sodium.api.crypto_sign_verify_detached,
|
||||||
}),
|
}),
|
||||||
'libsodium-wrappers': sodium => ({
|
'libsodium-wrappers': sodium => ({
|
||||||
open: sodium.crypto_secretbox_open_easy,
|
open: sodium.crypto_secretbox_open_easy,
|
||||||
close: sodium.crypto_secretbox_easy,
|
close: sodium.crypto_secretbox_easy,
|
||||||
random: n => sodium.randombytes_buf(n),
|
random: n => sodium.randombytes_buf(n),
|
||||||
|
verify: sodium.crypto_sign_verify_detached,
|
||||||
}),
|
}),
|
||||||
tweetnacl: tweetnacl => ({
|
tweetnacl: tweetnacl => ({
|
||||||
open: tweetnacl.secretbox.open,
|
open: tweetnacl.secretbox.open,
|
||||||
close: tweetnacl.secretbox,
|
close: tweetnacl.secretbox,
|
||||||
random: n => tweetnacl.randomBytes(n),
|
random: n => tweetnacl.randomBytes(n),
|
||||||
|
verify: (s, d, p) => tweetnacl.sign.detached.verify(d, s, p),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -36,9 +36,9 @@ client.on('message', message => {
|
||||||
// Clean content and log each character
|
// Clean content and log each character
|
||||||
console.log(Util.cleanContent(args.join(' '), message).split(''));
|
console.log(Util.cleanContent(args.join(' '), message).split(''));
|
||||||
|
|
||||||
if (command === 'test1') message.reply(tests[0]);
|
if (command === 'test1') message.channel.send(tests[0]);
|
||||||
else if (command === 'test2') message.reply(tests[1]);
|
else if (command === 'test2') message.channel.send(tests[1]);
|
||||||
else if (command === 'test3') message.reply(tests[2]);
|
else if (command === 'test3') message.channel.send(tests[2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
client.login(token).catch(console.error);
|
client.login(token).catch(console.error);
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,8 @@ client.on('message', message => {
|
||||||
|
|
||||||
if (message.content.startsWith('kick')) {
|
if (message.content.startsWith('kick')) {
|
||||||
message.guild
|
message.guild
|
||||||
.member(message.mentions.users.first())
|
.members
|
||||||
|
.resolve(message.mentions.users.first())
|
||||||
.kick()
|
.kick()
|
||||||
.then(member => {
|
.then(member => {
|
||||||
console.log(member);
|
console.log(member);
|
||||||
|
|
@ -134,7 +135,7 @@ client.on('message', message => {
|
||||||
}
|
}
|
||||||
message.channel.send('last one...').then(m => {
|
message.channel.send('last one...').then(m => {
|
||||||
const diff = Date.now() - start;
|
const diff = Date.now() - start;
|
||||||
m.reply(`Each message took ${diff / 21}ms to send`);
|
m.channel.send(`Each message took ${diff / 21}ms to send`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,7 +206,7 @@ client.on('message', msg => {
|
||||||
.join()
|
.join()
|
||||||
.then(conn => {
|
.then(conn => {
|
||||||
con = conn;
|
con = conn;
|
||||||
msg.reply('done');
|
msg.channel.send('done');
|
||||||
const s = ytdl(song, { filter: 'audioonly' }, { passes: 3 });
|
const s = ytdl(song, { filter: 'audioonly' }, { passes: 3 });
|
||||||
s.on('error', e => console.log(`e w stream 2 ${e}`));
|
s.on('error', e => console.log(`e w stream 2 ${e}`));
|
||||||
disp = conn.playStream(s);
|
disp = conn.playStream(s);
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ const tests = [
|
||||||
|
|
||||||
m => m.channel.send(fill('x'), { split: true }),
|
m => m.channel.send(fill('x'), { split: true }),
|
||||||
m => m.channel.send(fill('1'), { code: 'js', split: true }),
|
m => m.channel.send(fill('1'), { code: 'js', split: true }),
|
||||||
m => m.channel.send(fill('x'), { reply: m.author, code: 'js', split: true }),
|
|
||||||
m => m.channel.send(fill('xyz '), { split: { char: ' ' } }),
|
m => m.channel.send(fill('xyz '), { split: { char: ' ' } }),
|
||||||
|
|
||||||
m => m.channel.send('x', { embed: { description: 'a' } }),
|
m => m.channel.send('x', { embed: { description: 'a' } }),
|
||||||
|
|
@ -99,7 +98,6 @@ const tests = [
|
||||||
async m => m.channel.send({ files: [await read(fileA)] }),
|
async m => m.channel.send({ files: [await read(fileA)] }),
|
||||||
async m =>
|
async m =>
|
||||||
m.channel.send(fill('x'), {
|
m.channel.send(fill('x'), {
|
||||||
reply: m.author,
|
|
||||||
code: 'js',
|
code: 'js',
|
||||||
split: true,
|
split: true,
|
||||||
embed: embed().setImage('attachment://zero.png'),
|
embed: embed().setImage('attachment://zero.png'),
|
||||||
|
|
@ -111,7 +109,6 @@ const tests = [
|
||||||
m => m.channel.send({ files: [{ attachment: readStream(fileA) }] }),
|
m => m.channel.send({ files: [{ attachment: readStream(fileA) }] }),
|
||||||
async m =>
|
async m =>
|
||||||
m.channel.send(fill('xyz '), {
|
m.channel.send(fill('xyz '), {
|
||||||
reply: m.author,
|
|
||||||
code: 'js',
|
code: 'js',
|
||||||
split: { char: ' ', prepend: 'hello! ', append: '!!!' },
|
split: { char: ' ', prepend: 'hello! ', append: '!!!' },
|
||||||
embed: embed().setImage('attachment://zero.png'),
|
embed: embed().setImage('attachment://zero.png'),
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,13 @@ const commands = {
|
||||||
}
|
}
|
||||||
message.channel.send(res, { code: 'js' });
|
message.channel.send(res, { code: 'js' });
|
||||||
},
|
},
|
||||||
ping: message => message.reply('pong'),
|
ping: message => message.channel.send('pong'),
|
||||||
};
|
};
|
||||||
|
|
||||||
client.on('message', message => {
|
client.on('message', message => {
|
||||||
if (!message.content.startsWith(prefix) || message.author.bot) return;
|
if (!message.content.startsWith(prefix) || message.author.bot) return;
|
||||||
|
|
||||||
message.content = message.content
|
message.content = message.content.replace(prefix, '').trim().split(' ');
|
||||||
.replace(prefix, '')
|
|
||||||
.trim()
|
|
||||||
.split(' ');
|
|
||||||
const command = message.content.shift();
|
const command = message.content.shift();
|
||||||
message.content = message.content.join(' ');
|
message.content = message.content.join(' ');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,20 +43,15 @@ client.on('message', m => {
|
||||||
conn.receiver.createStream(m.author, true).on('data', b => console.log(b.toString()));
|
conn.receiver.createStream(m.author, true).on('data', b => console.log(b.toString()));
|
||||||
conn.player.on('error', (...e) => console.log('player', ...e));
|
conn.player.on('error', (...e) => console.log('player', ...e));
|
||||||
if (!connections.has(m.guild.id)) connections.set(m.guild.id, { conn, queue: [] });
|
if (!connections.has(m.guild.id)) connections.set(m.guild.id, { conn, queue: [] });
|
||||||
m.reply('ok!');
|
m.channel.send('ok!');
|
||||||
conn.play(ytdl('https://www.youtube.com/watch?v=_XXOSf0s2nk', { filter: 'audioonly' }, { passes: 3 }));
|
conn.play(ytdl('https://www.youtube.com/watch?v=_XXOSf0s2nk', { filter: 'audioonly' }, { passes: 3 }));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
m.reply('Specify a voice channel!');
|
m.channel.send('Specify a voice channel!');
|
||||||
}
|
}
|
||||||
} else if (m.content.startsWith('#eval') && m.author.id === '66564597481480192') {
|
} else if (m.content.startsWith('#eval') && m.author.id === '66564597481480192') {
|
||||||
try {
|
try {
|
||||||
const com = eval(
|
const com = eval(m.content.split(' ').slice(1).join(' '));
|
||||||
m.content
|
|
||||||
.split(' ')
|
|
||||||
.slice(1)
|
|
||||||
.join(' '),
|
|
||||||
);
|
|
||||||
m.channel.send(com, { code: true });
|
m.channel.send(com, { code: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ const tests = [
|
||||||
|
|
||||||
(m, hook) => hook.send(fill('x'), { split: true }),
|
(m, hook) => hook.send(fill('x'), { split: true }),
|
||||||
(m, hook) => hook.send(fill('1'), { code: 'js', split: true }),
|
(m, hook) => hook.send(fill('1'), { code: 'js', split: true }),
|
||||||
(m, hook) => hook.send(fill('x'), { reply: m.author, code: 'js', split: true }),
|
|
||||||
(m, hook) => hook.send(fill('xyz '), { split: { char: ' ' } }),
|
(m, hook) => hook.send(fill('xyz '), { split: { char: ' ' } }),
|
||||||
|
|
||||||
(m, hook) => hook.send({ embeds: [{ description: 'a' }] }),
|
(m, hook) => hook.send({ embeds: [{ description: 'a' }] }),
|
||||||
|
|
@ -96,7 +95,6 @@ const tests = [
|
||||||
async (m, hook) => hook.send({ files: [await read(fileA)] }),
|
async (m, hook) => hook.send({ files: [await read(fileA)] }),
|
||||||
async (m, hook) =>
|
async (m, hook) =>
|
||||||
hook.send(fill('x'), {
|
hook.send(fill('x'), {
|
||||||
reply: m.author,
|
|
||||||
code: 'js',
|
code: 'js',
|
||||||
split: true,
|
split: true,
|
||||||
embeds: [embed().setImage('attachment://zero.png')],
|
embeds: [embed().setImage('attachment://zero.png')],
|
||||||
|
|
@ -108,7 +106,6 @@ const tests = [
|
||||||
(m, hook) => hook.send({ files: [{ attachment: readStream(fileA) }] }),
|
(m, hook) => hook.send({ files: [{ attachment: readStream(fileA) }] }),
|
||||||
async (m, hook) =>
|
async (m, hook) =>
|
||||||
hook.send(fill('xyz '), {
|
hook.send(fill('xyz '), {
|
||||||
reply: m.author,
|
|
||||||
code: 'js',
|
code: 'js',
|
||||||
split: { char: ' ', prepend: 'hello! ', append: '!!!' },
|
split: { char: ' ', prepend: 'hello! ', append: '!!!' },
|
||||||
embeds: [embed().setImage('attachment://zero.png')],
|
embeds: [embed().setImage('attachment://zero.png')],
|
||||||
|
|
|
||||||
39
typings/index.d.ts
vendored
39
typings/index.d.ts
vendored
|
|
@ -199,7 +199,7 @@ declare module 'discord.js' {
|
||||||
private _validateOptions(options?: ClientOptions): void;
|
private _validateOptions(options?: ClientOptions): void;
|
||||||
|
|
||||||
public channels: ChannelManager;
|
public channels: ChannelManager;
|
||||||
public readonly emojis: GuildEmojiManager;
|
public readonly emojis: BaseGuildEmojiManager;
|
||||||
public guilds: GuildManager;
|
public guilds: GuildManager;
|
||||||
public readyAt: Date | null;
|
public readyAt: Date | null;
|
||||||
public readonly readyTimestamp: number | null;
|
public readonly readyTimestamp: number | null;
|
||||||
|
|
@ -658,7 +658,6 @@ declare module 'discord.js' {
|
||||||
public fetchWidget(): Promise<GuildWidget>;
|
public fetchWidget(): Promise<GuildWidget>;
|
||||||
public iconURL(options?: ImageURLOptions & { dynamic?: boolean }): string | null;
|
public iconURL(options?: ImageURLOptions & { dynamic?: boolean }): string | null;
|
||||||
public leave(): Promise<Guild>;
|
public leave(): Promise<Guild>;
|
||||||
public member(user: UserResolvable): GuildMember | null;
|
|
||||||
public setAFKChannel(afkChannel: ChannelResolvable | null, reason?: string): Promise<Guild>;
|
public setAFKChannel(afkChannel: ChannelResolvable | null, reason?: string): Promise<Guild>;
|
||||||
public setAFKTimeout(afkTimeout: number, reason?: string): Promise<Guild>;
|
public setAFKTimeout(afkTimeout: number, reason?: string): Promise<Guild>;
|
||||||
public setBanner(banner: Base64Resolvable | null, reason?: string): Promise<Guild>;
|
public setBanner(banner: Base64Resolvable | null, reason?: string): Promise<Guild>;
|
||||||
|
|
@ -976,7 +975,7 @@ declare module 'discord.js' {
|
||||||
public id: Snowflake;
|
public id: Snowflake;
|
||||||
public readonly member: GuildMember | null;
|
public readonly member: GuildMember | null;
|
||||||
public mentions: MessageMentions;
|
public mentions: MessageMentions;
|
||||||
public nonce: string | null;
|
public nonce: string | number | null;
|
||||||
public readonly partial: false;
|
public readonly partial: false;
|
||||||
public readonly pinnable: boolean;
|
public readonly pinnable: boolean;
|
||||||
public pinned: boolean;
|
public pinned: boolean;
|
||||||
|
|
@ -988,6 +987,7 @@ declare module 'discord.js' {
|
||||||
public webhookID: Snowflake | null;
|
public webhookID: Snowflake | null;
|
||||||
public flags: Readonly<MessageFlags>;
|
public flags: Readonly<MessageFlags>;
|
||||||
public reference: MessageReference | null;
|
public reference: MessageReference | null;
|
||||||
|
public readonly referencedMessage: Message | null;
|
||||||
public awaitReactions(
|
public awaitReactions(
|
||||||
filter: CollectorFilter,
|
filter: CollectorFilter,
|
||||||
options?: AwaitReactionsOptions,
|
options?: AwaitReactionsOptions,
|
||||||
|
|
@ -1529,7 +1529,6 @@ declare module 'discord.js' {
|
||||||
public flags: Readonly<UserFlags> | null;
|
public flags: Readonly<UserFlags> | null;
|
||||||
public id: Snowflake;
|
public id: Snowflake;
|
||||||
public lastMessageID: Snowflake | null;
|
public lastMessageID: Snowflake | null;
|
||||||
public locale: string | null;
|
|
||||||
public readonly partial: false;
|
public readonly partial: false;
|
||||||
public readonly presence: Presence;
|
public readonly presence: Presence;
|
||||||
public system: boolean | null;
|
public system: boolean | null;
|
||||||
|
|
@ -1872,11 +1871,6 @@ declare module 'discord.js' {
|
||||||
|
|
||||||
//#region Managers
|
//#region Managers
|
||||||
|
|
||||||
export class ChannelManager extends BaseManager<Snowflake, Channel, ChannelResolvable> {
|
|
||||||
constructor(client: Client, iterable: Iterable<any>);
|
|
||||||
public fetch(id: Snowflake, cache?: boolean, force?: boolean): Promise<Channel>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class BaseManager<K, Holds, R> {
|
export abstract class BaseManager<K, Holds, R> {
|
||||||
constructor(client: Client, iterable: Iterable<any>, holds: Constructable<Holds>, cacheType: Collection<K, Holds>);
|
constructor(client: Client, iterable: Iterable<any>, holds: Constructable<Holds>, cacheType: Collection<K, Holds>);
|
||||||
public holds: Constructable<Holds>;
|
public holds: Constructable<Holds>;
|
||||||
|
|
@ -1889,6 +1883,16 @@ declare module 'discord.js' {
|
||||||
public valueOf(): Collection<K, Holds>;
|
public valueOf(): Collection<K, Holds>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class BaseGuildEmojiManager extends BaseManager<Snowflake, GuildEmoji, EmojiResolvable> {
|
||||||
|
constructor(client: Client, iterable?: Iterable<any>);
|
||||||
|
public resolveIdentifier(emoji: EmojiIdentifierResolvable): string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChannelManager extends BaseManager<Snowflake, Channel, ChannelResolvable> {
|
||||||
|
constructor(client: Client, iterable: Iterable<any>);
|
||||||
|
public fetch(id: Snowflake, cache?: boolean, force?: boolean): Promise<Channel>;
|
||||||
|
}
|
||||||
|
|
||||||
export class GuildChannelManager extends BaseManager<Snowflake, GuildChannel, GuildChannelResolvable> {
|
export class GuildChannelManager extends BaseManager<Snowflake, GuildChannel, GuildChannelResolvable> {
|
||||||
constructor(guild: Guild, iterable?: Iterable<any>);
|
constructor(guild: Guild, iterable?: Iterable<any>);
|
||||||
public guild: Guild;
|
public guild: Guild;
|
||||||
|
|
@ -1901,7 +1905,7 @@ declare module 'discord.js' {
|
||||||
): Promise<TextChannel | VoiceChannel | CategoryChannel>;
|
): Promise<TextChannel | VoiceChannel | CategoryChannel>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GuildEmojiManager extends BaseManager<Snowflake, GuildEmoji, EmojiResolvable> {
|
export class GuildEmojiManager extends BaseGuildEmojiManager {
|
||||||
constructor(guild: Guild, iterable?: Iterable<any>);
|
constructor(guild: Guild, iterable?: Iterable<any>);
|
||||||
public guild: Guild;
|
public guild: Guild;
|
||||||
public create(
|
public create(
|
||||||
|
|
@ -1909,7 +1913,6 @@ declare module 'discord.js' {
|
||||||
name: string,
|
name: string,
|
||||||
options?: GuildEmojiCreateOptions,
|
options?: GuildEmojiCreateOptions,
|
||||||
): Promise<GuildEmoji>;
|
): Promise<GuildEmoji>;
|
||||||
public resolveIdentifier(emoji: EmojiIdentifierResolvable): string | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GuildEmojiRoleManager {
|
export class GuildEmojiRoleManager {
|
||||||
|
|
@ -2013,7 +2016,7 @@ declare module 'discord.js' {
|
||||||
|
|
||||||
public create(options?: { data?: RoleData; reason?: string }): Promise<Role>;
|
public create(options?: { data?: RoleData; reason?: string }): Promise<Role>;
|
||||||
public fetch(id: Snowflake, cache?: boolean, force?: boolean): Promise<Role | null>;
|
public fetch(id: Snowflake, cache?: boolean, force?: boolean): Promise<Role | null>;
|
||||||
public fetch(id?: Snowflake, cache?: boolean, force?: boolean): Promise<this>;
|
public fetch(id?: Snowflake, cache?: boolean, force?: boolean): Promise<Collection<Snowflake, Role>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserManager extends BaseManager<Snowflake, User, UserResolvable> {
|
export class UserManager extends BaseManager<Snowflake, User, UserResolvable> {
|
||||||
|
|
@ -2824,13 +2827,14 @@ declare module 'discord.js' {
|
||||||
parse?: MessageMentionTypes[];
|
parse?: MessageMentionTypes[];
|
||||||
roles?: Snowflake[];
|
roles?: Snowflake[];
|
||||||
users?: Snowflake[];
|
users?: Snowflake[];
|
||||||
|
repliedUser?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type MessageMentionTypes = 'roles' | 'users' | 'everyone';
|
type MessageMentionTypes = 'roles' | 'users' | 'everyone';
|
||||||
|
|
||||||
interface MessageOptions {
|
interface MessageOptions {
|
||||||
tts?: boolean;
|
tts?: boolean;
|
||||||
nonce?: string;
|
nonce?: string | number;
|
||||||
content?: StringResolvable;
|
content?: StringResolvable;
|
||||||
embed?: MessageEmbed | MessageEmbedOptions;
|
embed?: MessageEmbed | MessageEmbedOptions;
|
||||||
disableMentions?: 'none' | 'all' | 'everyone';
|
disableMentions?: 'none' | 'all' | 'everyone';
|
||||||
|
|
@ -2838,7 +2842,7 @@ declare module 'discord.js' {
|
||||||
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
||||||
code?: string | boolean;
|
code?: string | boolean;
|
||||||
split?: boolean | SplitOptions;
|
split?: boolean | SplitOptions;
|
||||||
reply?: UserResolvable;
|
replyTo?: MessageResolvable;
|
||||||
}
|
}
|
||||||
|
|
||||||
type MessageReactionResolvable = MessageReaction | Snowflake;
|
type MessageReactionResolvable = MessageReaction | Snowflake;
|
||||||
|
|
@ -2868,7 +2872,8 @@ declare module 'discord.js' {
|
||||||
| 'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3'
|
| 'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3'
|
||||||
| 'CHANNEL_FOLLOW_ADD'
|
| 'CHANNEL_FOLLOW_ADD'
|
||||||
| 'GUILD_DISCOVERY_DISQUALIFIED'
|
| 'GUILD_DISCOVERY_DISQUALIFIED'
|
||||||
| 'GUILD_DISCOVERY_REQUALIFIED';
|
| 'GUILD_DISCOVERY_REQUALIFIED'
|
||||||
|
| 'REPLY';
|
||||||
|
|
||||||
interface OverwriteData {
|
interface OverwriteData {
|
||||||
allow?: PermissionResolvable;
|
allow?: PermissionResolvable;
|
||||||
|
|
@ -3055,11 +3060,9 @@ declare module 'discord.js' {
|
||||||
|
|
||||||
type PartialTypes = 'USER' | 'CHANNEL' | 'GUILD_MEMBER' | 'MESSAGE' | 'REACTION';
|
type PartialTypes = 'USER' | 'CHANNEL' | 'GUILD_MEMBER' | 'MESSAGE' | 'REACTION';
|
||||||
|
|
||||||
interface PartialUser
|
interface PartialUser extends Omit<Partialize<User, 'bot' | 'flags' | 'system' | 'tag' | 'username'>, 'deleted'> {
|
||||||
extends Omit<Partialize<User, 'bot' | 'flags' | 'locale' | 'system' | 'tag' | 'username'>, 'deleted'> {
|
|
||||||
bot: User['bot'];
|
bot: User['bot'];
|
||||||
flags: User['flags'];
|
flags: User['flags'];
|
||||||
locale: User['locale'];
|
|
||||||
system: User['system'];
|
system: User['system'];
|
||||||
readonly tag: null;
|
readonly tag: null;
|
||||||
username: null;
|
username: null;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue