feat: reimplement disableEveryone into disableMentions

* User input sanitation: reimplement disableEveryone into disableMentions

* Change default value of ClientOptions#disableMentions to 'none'

* Update type declarations of disableMentions to include default

* update for compliance with ESLint

* Overlooked these files. Updated for complete compliance with ESLint
This commit is contained in:
Papa
2020-02-29 06:20:39 -07:00
committed by GitHub
parent bbe169deac
commit 9c8aaf1bbc
8 changed files with 51 additions and 29 deletions

View File

@@ -386,8 +386,8 @@ class Client extends BaseClient {
if (typeof options.fetchAllMembers !== 'boolean') {
throw new TypeError('CLIENT_INVALID_OPTION', 'fetchAllMembers', 'a boolean');
}
if (typeof options.disableMentions !== 'boolean') {
throw new TypeError('CLIENT_INVALID_OPTION', 'disableMentions', 'a boolean');
if (typeof options.disableMentions !== 'string') {
throw new TypeError('CLIENT_INVALID_OPTION', 'disableMentions', 'a string');
}
if (!Array.isArray(options.partials)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'partials', 'an Array');

View File

@@ -91,8 +91,16 @@ class APIMessage {
const disableMentions = typeof this.options.disableMentions === 'undefined' ?
this.target.client.options.disableMentions :
this.options.disableMentions;
if (disableMentions) {
if (disableMentions === 'all') {
content = Util.removeMentions(content || '');
} else if (disableMentions === 'everyone') {
content = (content || '').replace(/@([^<>@ ]*)/gsmu, (match, target) => {
if (target.match(/^[&!]?\d+$/)) {
return `@${target}`;
} else {
return `@\u200b${target}`;
}
});
}
const isSplit = typeof this.options.split !== 'undefined' && this.options.split !== false;

View File

@@ -85,8 +85,8 @@ class Webhook {
* @property {string} [nonce=''] The nonce for the message
* @property {Object[]} [embeds] An array of embeds for the message
* (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
* @property {boolean} [disableMentions=this.client.options.disableMentions] Whether or not a zero width space
* should be placed after every @ character to prevent unexpected mentions
* @property {'none' | 'all' | 'everyone'} [disableMentions=this.client.options.disableMentions] Whether or not
* all mentions or everyone/here mentions should be sanitized to prevent unexpected mentions
* @property {FileOptions[]|string[]} [files] Files to send with the message
* @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

View File

@@ -57,8 +57,8 @@ class TextBasedChannel {
* @property {string} [content=''] The content for the message
* @property {MessageEmbed|Object} [embed] An embed for the message
* (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
* @property {boolean} [disableMentions=this.client.options.disableMentions] Whether or not a zero width space
* should be placed after every @ character to prevent unexpected mentions
* @property {'none' | 'all' | 'everyone'} [disableMentions=this.client.options.disableMentions] Whether or not
* all mentionsor everyone/here mentions should be sanitized to prevent unexpected mentions
* @property {FileOptions[]|BufferResolvable[]} [files] Files to send with the message
* @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

View File

@@ -21,7 +21,8 @@ const browser = exports.browser = typeof window !== 'undefined';
* the message cache lifetime (in seconds, 0 for never)
* @property {boolean} [fetchAllMembers=false] Whether to cache all guild members and users upon startup, as well as
* upon joining a guild (should be avoided whenever possible)
* @property {boolean} [disableMentions=false] Default value for {@link MessageOptions#disableMentions}
* @property {'none' | 'all' | 'everyone'} [disableMentions='none'] Default value
* for {@link MessageOptions#disableMentions}
* @property {PartialType[]} [partials] Structures allowed to be partial. This means events can be emitted even when
* they're missing all the data for a particular structure. See the "Partials" topic listed in the sidebar for some
* important usage information, as partials require you to put checks in place when handling data.
@@ -47,7 +48,7 @@ exports.DefaultOptions = {
messageCacheLifetime: 0,
messageSweepInterval: 0,
fetchAllMembers: false,
disableMentions: false,
disableMentions: 'none',
partials: [],
restWsBridgeTimeout: 5000,
disabledEvents: [],

View File

@@ -534,22 +534,30 @@ class Util {
* @returns {string}
*/
static cleanContent(str, message) {
return Util.removeMentions(str
.replace(/<@!?[0-9]+>/g, input => {
const id = input.replace(/<|!|>|@/g, '');
if (message.channel.type === 'dm') {
const user = message.client.users.cache.get(id);
return user ? `@${user.username}` : input;
}
const member = message.channel.guild.members.cache.get(id);
if (member) {
return `@${member.displayName}`;
if (message.client.options.disableMentions === 'everyone') {
str = str.replace(/@([^<>@ ]*)/gsmu, (match, target) => {
if (target.match(/^[&!]?\d+$/)) {
return `@${target}`;
} else {
const user = message.client.users.cache.get(id);
return user ? `@${user.username}` : input;
return `@\u200b${target}`;
}
})
});
}
str = str.replace(/<@!?[0-9]+>/g, input => {
const id = input.replace(/<|!|>|@/g, '');
if (message.channel.type === 'dm') {
const user = message.client.users.cache.get(id);
return user ? `@${user.username}` : input;
}
const member = message.channel.guild.members.cache.get(id);
if (member) {
return `@${member.displayName}`;
} else {
const user = message.client.users.cache.get(id);
return user ? `@${user.username}` : input;
}
})
.replace(/<#[0-9]+>/g, input => {
const channel = message.client.channels.cache.get(input.replace(/<|#|>/g, ''));
return channel ? `#${channel.name}` : input;
@@ -558,7 +566,12 @@ class Util {
if (message.channel.type === 'dm') return input;
const role = message.guild.roles.cache.get(input.replace(/<|@|>|&/g, ''));
return role ? `@${role.name}` : input;
}));
});
if (message.client.options.disableMentions === 'all') {
return Util.removeMentions(str);
} else {
return str;
}
}
/**

View File

@@ -6,7 +6,7 @@ const client = new Discord.Client({
// To see a difference, comment out disableMentions and run the same tests using disableEveryone
// You will notice that all messages will mention @everyone
//disableEveryone: true
disableMentions: true
disableMentions: 'everyone'
});
const tests = [
@@ -44,4 +44,4 @@ client.on('message', message => {
message.reply(tests[2]);
});
client.login(token).catch(console.error);
client.login(token).catch(console.error);

6
typings/index.d.ts vendored
View File

@@ -2062,7 +2062,7 @@ declare module 'discord.js' {
messageCacheLifetime?: number;
messageSweepInterval?: number;
fetchAllMembers?: boolean;
disableMentions?: boolean;
disableMentions?: 'none' | 'all' | 'everyone';
partials?: PartialTypes[];
restWsBridgeTimeout?: number;
restTimeOffset?: number;
@@ -2514,7 +2514,7 @@ declare module 'discord.js' {
nonce?: string;
content?: string;
embed?: MessageEmbed | MessageEmbedOptions;
disableMentions?: boolean;
disableMentions?: 'none' | 'all' | 'everyone';
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
code?: string | boolean;
split?: boolean | SplitOptions;
@@ -2763,7 +2763,7 @@ declare module 'discord.js' {
tts?: boolean;
nonce?: string;
embeds?: (MessageEmbed | object)[];
disableMentions?: boolean;
disableMentions?: 'none' | 'all' | 'everyone';
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
code?: string | boolean;
split?: boolean | SplitOptions;