diff --git a/src/index.js b/src/index.js
index 916056fe0..77c56336d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,7 @@ module.exports = {
DataStore: require('./stores/DataStore'),
DiscordAPIError: require('./rest/DiscordAPIError'),
HTTPError: require('./rest/HTTPError'),
+ MessageFlags: require('./util/MessageFlags'),
Permissions: require('./util/Permissions'),
Speaking: require('./util/Speaking'),
Snowflake: require('./util/Snowflake'),
diff --git a/src/structures/Message.js b/src/structures/Message.js
index 5b6cc4754..8a9df07ea 100644
--- a/src/structures/Message.js
+++ b/src/structures/Message.js
@@ -13,6 +13,7 @@ const Permissions = require('../util/Permissions');
const Base = require('./Base');
const { Error, TypeError } = require('../errors');
const APIMessage = require('./APIMessage');
+const MessageFlags = require('../util/MessageFlags');
/**
* Represents a message on Discord.
@@ -81,7 +82,9 @@ class Message extends Base {
/**
* A random number or string used for checking message delivery
- * @type {string}
+ * This is only received after the message was sent successfully, and
+ * lost if re-fetched
+ * @type {?string}
*/
this.nonce = data.nonce;
@@ -89,7 +92,7 @@ class Message extends Base {
* Whether or not this message was sent by Discord, not actually a user (e.g. pin notifications)
* @type {boolean}
*/
- this.system = data.type === 6;
+ this.system = data.type !== 0;
/**
* A list of embeds in the message - e.g. YouTube Player
@@ -137,7 +140,7 @@ class Message extends Base {
* All valid mentions that the message contains
* @type {MessageMentions}
*/
- this.mentions = new Mentions(this, data.mentions, data.mention_roles, data.mention_everyone);
+ this.mentions = new Mentions(this, data.mentions, data.mention_roles, data.mention_everyone, data.mention_channels);
/**
* ID of the webhook that sent the message, if applicable
@@ -172,6 +175,30 @@ class Message extends Base {
} else if (data.member && this.guild && this.author) {
this.guild.members.add(Object.assign(data.member, { user: this.author }));
}
+
+ /**
+ * Flags that are applied to the message
+ * @type {Readonly}
+ */
+ this.flags = new MessageFlags(data.flags).freeze();
+
+ /**
+ * Reference data sent in a crossposted message.
+ * @typedef {Object} MessageReference
+ * @property {string} channelID ID of the channel the message was crossposted from
+ * @property {?string} guildID ID of the guild the message was crossposted from
+ * @property {?string} messageID ID of the message that was crossposted
+ */
+
+ /**
+ * Message reference data
+ * @type {?MessageReference}
+ */
+ this.reference = data.message_reference ? {
+ channelID: data.message_reference.channel_id,
+ guildID: data.message_reference.guild_id,
+ messageID: data.message_reference.message_id,
+ } : null;
}
/**
@@ -214,8 +241,11 @@ class Message extends Base {
this,
'mentions' in data ? data.mentions : this.mentions.users,
'mentions_roles' in data ? data.mentions_roles : this.mentions.roles,
- 'mention_everyone' in data ? data.mention_everyone : this.mentions.everyone
+ 'mention_everyone' in data ? data.mention_everyone : this.mentions.everyone,
+ 'mention_channels' in data ? data.mention_channels : this.mentions.crosspostedChannels
);
+
+ this.flags = new MessageFlags('flags' in data ? data.flags : 0).freeze();
}
/**
diff --git a/src/structures/MessageMentions.js b/src/structures/MessageMentions.js
index 610c8c127..9b17685f6 100644
--- a/src/structures/MessageMentions.js
+++ b/src/structures/MessageMentions.js
@@ -3,12 +3,13 @@
const Collection = require('../util/Collection');
const Util = require('../util/Util');
const GuildMember = require('./GuildMember');
+const { ChannelTypes } = require('../util/Constants');
/**
* Keeps track of mentions in a {@link Message}.
*/
class MessageMentions {
- constructor(message, users, roles, everyone) {
+ constructor(message, users, roles, everyone, crosspostedChannels) {
/**
* The client the message is from
* @type {Client}
@@ -86,6 +87,39 @@ class MessageMentions {
* @private
*/
this._channels = null;
+
+ /**
+ * Crossposted channel data.
+ * @typedef {Object} CrosspostedChannel
+ * @property {string} channelID ID of the mentioned channel
+ * @property {string} guildID ID of the guild that has the channel
+ * @property {string} type Type of the channel
+ * @property {string} name The name of the channel
+ */
+
+ if (crosspostedChannels) {
+ if (crosspostedChannels instanceof Collection) {
+ /**
+ * A collection of crossposted channels
+ * @type {Collection}
+ */
+ this.crosspostedChannels = new Collection(crosspostedChannels);
+ } else {
+ this.crosspostedChannels = new Collection();
+ const channelTypes = Object.keys(ChannelTypes);
+ for (const d of crosspostedChannels) {
+ const type = channelTypes[d.type];
+ this.crosspostedChannels.set(d.id, {
+ channelID: d.id,
+ guildID: d.guild_id,
+ type: type ? type.toLowerCase() : 'unknown',
+ name: d.name,
+ });
+ }
+ }
+ } else {
+ this.crosspostedChannels = new Collection();
+ }
}
/**
diff --git a/src/util/Constants.js b/src/util/Constants.js
index 9d742e784..7bb3bb50e 100644
--- a/src/util/Constants.js
+++ b/src/util/Constants.js
@@ -386,6 +386,7 @@ exports.WSEvents = keyMirror([
* * USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1
* * USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2
* * USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3
+ * * CHANNEL_FOLLOW_ADD
* @typedef {string} MessageType
*/
exports.MessageTypes = [
@@ -401,6 +402,7 @@ exports.MessageTypes = [
'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1',
'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2',
'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3',
+ 'CHANNEL_FOLLOW_ADD',
];
/**
diff --git a/src/util/MessageFlags.js b/src/util/MessageFlags.js
new file mode 100644
index 000000000..b1c86b7d6
--- /dev/null
+++ b/src/util/MessageFlags.js
@@ -0,0 +1,25 @@
+'use strict';
+
+const BitField = require('./BitField');
+
+/**
+ * Data structure that makes it easy to interact with an {@link Message#flags} bitfield.
+ * @extends {BitField}
+ */
+class MessageFlags extends BitField {}
+
+/**
+ * Numeric message flags. All available properties:
+ * * `CROSSPOSTED`
+ * * `IS_CROSSPOST`
+ * * `SUPPRESS_EMBEDS`
+ * @type {Object}
+ * @see {@link https://discordapp.com/developers/docs/resources/channel#message-object-message-flags}
+ */
+MessageFlags.FLAGS = {
+ CROSSPOSTED: 1 << 0,
+ IS_CROSSPOST: 1 << 1,
+ SUPPRESS_EMBEDS: 1 << 2,
+};
+
+module.exports = MessageFlags;
diff --git a/typings/index.d.ts b/typings/index.d.ts
index 4f03ef11d..cb08e2ffe 100644
--- a/typings/index.d.ts
+++ b/typings/index.d.ts
@@ -1,3 +1,14 @@
+declare enum ChannelType {
+ text,
+ dm,
+ voice,
+ group,
+ category,
+ news,
+ store,
+ unknown
+}
+
declare module 'discord.js' {
import BaseCollection from '@discordjs/collection';
import { EventEmitter } from 'events';
@@ -125,7 +136,7 @@ declare module 'discord.js' {
public readonly createdTimestamp: number;
public deleted: boolean;
public id: Snowflake;
- public type: 'dm' | 'text' | 'voice' | 'category' | 'news' | 'store' | 'unknown';
+ public type: keyof typeof ChannelType;
public delete(reason?: string): Promise;
public fetch(): Promise;
public toString(): string;
@@ -914,12 +925,17 @@ declare module 'discord.js' {
public toString(): string;
}
+ export class MessageFlags extends BitField {
+ public static FLAGS: Record;
+ public static resolve(bit?: BitFieldResolvable): number;
+ }
+
export class Message extends Base {
constructor(client: Client, data: object, channel: TextChannel | DMChannel);
private _edits: Message[];
private patch(data: object): void;
- public activity: GroupActivity | null;
+ public activity: MessageActivity | null;
public application: ClientApplication | null;
public attachments: Collection;
public author: User | null;
@@ -939,7 +955,7 @@ declare module 'discord.js' {
public id: Snowflake;
public readonly member: GuildMember | null;
public mentions: MessageMentions;
- public nonce: string;
+ public nonce: string | null;
public readonly partial: boolean;
public readonly pinnable: boolean;
public pinned: boolean;
@@ -949,6 +965,8 @@ declare module 'discord.js' {
public type: MessageType;
public readonly url: string;
public webhookID: Snowflake | null;
+ public flags: Readonly;
+ public reference: MessageReference | null;
public awaitReactions(filter: CollectorFilter, options?: AwaitReactionsOptions): Promise>;
public createReactionCollector(filter: CollectorFilter, options?: ReactionCollectorOptions): ReactionCollector;
public delete(options?: { timeout?: number, reason?: string }): Promise;
@@ -1054,6 +1072,7 @@ declare module 'discord.js' {
public readonly members: Collection | null;
public roles: Collection;
public users: Collection;
+ public crosspostedChannels: Collection;
public toJSON(): object;
public static CHANNELS_PATTERN: RegExp;
@@ -1886,6 +1905,10 @@ declare module 'discord.js' {
| 'LISTENING'
| 'WATCHING';
+ type MessageFlagsString = 'CROSSPOSTED'
+ | 'IS_CROSSPOST'
+ | 'SUPPRESS_EMBEDS';
+
interface APIErrror {
UNKNOWN_ACCOUNT: number;
UNKNOWN_APPLICATION: number;
@@ -2128,11 +2151,17 @@ declare module 'discord.js' {
name?: string;
}
- interface GroupActivity {
+ interface MessageActivity {
partyID: string;
type: number;
}
+ interface MessageReference {
+ channelID: string;
+ guildID: string;
+ messageID: string | null;
+ }
+
type GuildAuditLogsAction = keyof GuildAuditLogsActions;
interface GuildAuditLogsActions {
@@ -2196,7 +2225,7 @@ declare module 'discord.js' {
interface GuildCreateChannelOptions {
permissionOverwrites?: OverwriteResolvable[] | Collection;
topic?: string;
- type?: 'text' | 'voice' | 'category';
+ type?: Exclude;
nsfw?: boolean;
parent?: ChannelResolvable;
bitrate?: number;
@@ -2373,7 +2402,8 @@ declare module 'discord.js' {
| 'USER_PREMIUM_GUILD_SUBSCRIPTION'
| 'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1'
| 'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2'
- | 'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3';
+ | 'USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3'
+ | 'CHANNEL_FOLLOW_ADD';
interface OverwriteData {
allow?: PermissionResolvable;
@@ -2611,5 +2641,12 @@ declare module 'discord.js' {
type CloseEvent = { wasClean: boolean; code: number; reason: string; target: WebSocket; };
type ErrorEvent = { error: any, message: string, type: string, target: WebSocket; };
+ interface CrosspostedChannel {
+ channelID: Snowflake;
+ guildID: Snowflake;
+ type: keyof typeof ChannelType;
+ name: string;
+ }
+
//#endregion
}