MessageMentions.js (6887B)
1 'use strict'; 2 3 const Collection = require('../util/Collection'); 4 const { ChannelTypes } = require('../util/Constants'); 5 const Util = require('../util/Util'); 6 7 /** 8 * Keeps track of mentions in a {@link Message}. 9 */ 10 class MessageMentions { 11 constructor(message, users, roles, everyone, crosspostedChannels) { 12 /** 13 * The client the message is from 14 * @type {Client} 15 * @readonly 16 */ 17 Object.defineProperty(this, 'client', { value: message.client }); 18 19 /** 20 * The guild the message is in 21 * @type {?Guild} 22 * @readonly 23 */ 24 Object.defineProperty(this, 'guild', { value: message.guild }); 25 26 /** 27 * The initial message content 28 * @type {string} 29 * @readonly 30 * @private 31 */ 32 Object.defineProperty(this, '_content', { value: message.content }); 33 34 /** 35 * Whether `@everyone` or `@here` were mentioned 36 * @type {boolean} 37 */ 38 this.everyone = Boolean(everyone); 39 40 if (users) { 41 if (users instanceof Collection) { 42 /** 43 * Any users that were mentioned 44 * <info>Order as received from the API, not as they appear in the message content</info> 45 * @type {Collection<Snowflake, User>} 46 */ 47 this.users = new Collection(users); 48 } else { 49 this.users = new Collection(); 50 for (const mention of users) { 51 if (mention.member && message.guild) { 52 message.guild.members.add(Object.assign(mention.member, { user: mention })); 53 } 54 const user = message.client.users.add(mention); 55 this.users.set(user.id, user); 56 } 57 } 58 } else { 59 this.users = new Collection(); 60 } 61 62 if (roles) { 63 if (roles instanceof Collection) { 64 /** 65 * Any roles that were mentioned 66 * <info>Order as received from the API, not as they appear in the message content</info> 67 * @type {Collection<Snowflake, Role>} 68 */ 69 this.roles = new Collection(roles); 70 } else { 71 this.roles = new Collection(); 72 for (const mention of roles) { 73 const role = message.channel.guild.roles.cache.get(mention); 74 if (role) this.roles.set(role.id, role); 75 } 76 } 77 } else { 78 this.roles = new Collection(); 79 } 80 81 /** 82 * Cached members for {@link MessageMention#members} 83 * @type {?Collection<Snowflake, GuildMember>} 84 * @private 85 */ 86 this._members = null; 87 88 /** 89 * Cached channels for {@link MessageMention#channels} 90 * @type {?Collection<Snowflake, GuildChannel>} 91 * @private 92 */ 93 this._channels = null; 94 95 /** 96 * Crossposted channel data. 97 * @typedef {Object} CrosspostedChannel 98 * @property {string} channelID ID of the mentioned channel 99 * @property {string} guildID ID of the guild that has the channel 100 * @property {string} type Type of the channel 101 * @property {string} name The name of the channel 102 */ 103 104 if (crosspostedChannels) { 105 if (crosspostedChannels instanceof Collection) { 106 /** 107 * A collection of crossposted channels 108 * <info>Order as received from the API, not as they appear in the message content</info> 109 * @type {Collection<Snowflake, CrosspostedChannel>} 110 */ 111 this.crosspostedChannels = new Collection(crosspostedChannels); 112 } else { 113 this.crosspostedChannels = new Collection(); 114 const channelTypes = Object.keys(ChannelTypes); 115 for (const d of crosspostedChannels) { 116 const type = channelTypes[d.type]; 117 this.crosspostedChannels.set(d.id, { 118 channelID: d.id, 119 guildID: d.guild_id, 120 type: type ? type.toLowerCase() : 'unknown', 121 name: d.name, 122 }); 123 } 124 } 125 } else { 126 this.crosspostedChannels = new Collection(); 127 } 128 } 129 130 /** 131 * Any members that were mentioned (only in {@link TextChannel}s) 132 * <info>Order as received from the API, not as they appear in the message content</info> 133 * @type {?Collection<Snowflake, GuildMember>} 134 * @readonly 135 */ 136 get members() { 137 if (this._members) return this._members; 138 if (!this.guild) return null; 139 this._members = new Collection(); 140 this.users.forEach(user => { 141 const member = this.guild.member(user); 142 if (member) this._members.set(member.user.id, member); 143 }); 144 return this._members; 145 } 146 147 /** 148 * Any channels that were mentioned 149 * <info>Order as they appear first in the message content</info> 150 * @type {Collection<Snowflake, GuildChannel>} 151 * @readonly 152 */ 153 get channels() { 154 if (this._channels) return this._channels; 155 this._channels = new Collection(); 156 let matches; 157 while ((matches = this.constructor.CHANNELS_PATTERN.exec(this._content)) !== null) { 158 const chan = this.client.channels.cache.get(matches[1]); 159 if (chan) this._channels.set(chan.id, chan); 160 } 161 return this._channels; 162 } 163 164 /** 165 * Checks if a user, guild member, role, or channel is mentioned. 166 * Takes into account user mentions, role mentions, and @everyone/@here mentions. 167 * @param {UserResolvable|GuildMember|Role|GuildChannel} data User/GuildMember/Role/Channel to check 168 * @param {Object} [options] Options 169 * @param {boolean} [options.ignoreDirect=false] - Whether to ignore direct mentions to the item 170 * @param {boolean} [options.ignoreRoles=false] - Whether to ignore role mentions to a guild member 171 * @param {boolean} [options.ignoreEveryone=false] - Whether to ignore everyone/here mentions 172 * @returns {boolean} 173 */ 174 has(data, { ignoreDirect = false, ignoreRoles = false, ignoreEveryone = false } = {}) { 175 if (!ignoreEveryone && this.everyone) return true; 176 const GuildMember = require('./GuildMember'); 177 if (!ignoreRoles && data instanceof GuildMember) { 178 for (const role of this.roles.values()) if (data.roles.cache.has(role.id)) return true; 179 } 180 181 if (!ignoreDirect) { 182 const id = data.id || data; 183 return this.users.has(id) || this.channels.has(id) || this.roles.has(id); 184 } 185 186 return false; 187 } 188 189 toJSON() { 190 return Util.flatten(this, { 191 members: true, 192 channels: true, 193 }); 194 } 195 } 196 197 /** 198 * Regular expression that globally matches `@everyone` and `@here` 199 * @type {RegExp} 200 */ 201 MessageMentions.EVERYONE_PATTERN = /@(everyone|here)/g; 202 203 /** 204 * Regular expression that globally matches user mentions like `<@81440962496172032>` 205 * @type {RegExp} 206 */ 207 MessageMentions.USERS_PATTERN = /<@!?(\d{17,19})>/g; 208 209 /** 210 * Regular expression that globally matches role mentions like `<@&297577916114403338>` 211 * @type {RegExp} 212 */ 213 MessageMentions.ROLES_PATTERN = /<@&(\d{17,19})>/g; 214 215 /** 216 * Regular expression that globally matches channel mentions like `<#222079895583457280>` 217 * @type {RegExp} 218 */ 219 MessageMentions.CHANNELS_PATTERN = /<#(\d{17,19})>/g; 220 221 module.exports = MessageMentions;