Skip to content

Commit

Permalink
feat(ForumChannels): Support Forum Channels (abalabahaha#1452)
Browse files Browse the repository at this point in the history
  • Loading branch information
conorwastakenwastaken authored and CaptainM777 committed Jul 18, 2024
1 parent d3f8b90 commit a62cb39
Show file tree
Hide file tree
Showing 17 changed files with 811 additions and 245 deletions.
1 change: 1 addition & 0 deletions esm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const {
DiscordHTTPError,
DiscordRESTError,
ExtendedUser,
ForumChannel,
GroupChannel,
Guild,
GuildChannel,
Expand Down
187 changes: 169 additions & 18 deletions index.d.ts

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Eris.Constants = require("./lib/Constants");
Eris.DiscordHTTPError = require("./lib/errors/DiscordHTTPError");
Eris.DiscordRESTError = require("./lib/errors/DiscordRESTError");
Eris.ExtendedUser = require("./lib/structures/ExtendedUser");
Eris.ForumChannel = require("./lib/structures/ForumChannel");
Eris.GroupChannel = require("./lib/structures/GroupChannel");
Eris.Guild = require("./lib/structures/Guild");
Eris.GuildChannel = require("./lib/structures/GuildChannel");
Expand Down
241 changes: 171 additions & 70 deletions lib/Client.js

Large diffs are not rendered by default.

20 changes: 18 additions & 2 deletions lib/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ module.exports.ButtonStyles = {
LINK: 5
};

module.exports.ChannelFlags = {
PINNED: 1 << 1,
REQUIRE_TAG: 1 << 4
};

module.exports.ChannelTypes = {
GUILD_TEXT: 0,
DM: 1,
Expand All @@ -121,8 +126,8 @@ module.exports.ChannelTypes = {
GUILD_PUBLIC_THREAD: 11,
GUILD_PRIVATE_THREAD: 12,
GUILD_STAGE_VOICE: 13, GUILD_STAGE: 13, // [DEPRECATED]
GUILD_DIRECTORY: 14,
GUILD_FORUM: 15,

GUILD_FORUM: 15
};

module.exports.ComponentTypes = {
Expand All @@ -142,11 +147,22 @@ module.exports.ConnectionVisibilityTypes = {
EVERYONE: 1
};

module.exports.ForumLayoutTypes = {
NOT_SET: 0,
LIST_VIEW: 1,
GALLERY_VIEW: 2
};

module.exports.DefaultMessageNotificationLevels = {
ALL_MESSAGES: 0,
ONLY_MENTIONS: 1
};

module.exports.SortOrderTypes = {
LATEST_ACTIVITY: 0,
CREATION_DATE: 1
};

module.exports.ExplicitContentFilterLevels = {
DISABLED: 0,
MEMBERS_WITHOUT_ROLES: 1,
Expand Down
114 changes: 75 additions & 39 deletions lib/gateway/Shard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const Base = require("../structures/Base");
const Bucket = require("../util/Bucket");
const Call = require("../structures/Call");
const Channel = require("../structures/Channel");
const ForumChannel = require("../structures/ForumChannel");
const GroupChannel = require("../structures/GroupChannel");
const GuildChannel = require("../structures/GuildChannel");
const Message = require("../structures/Message");
Expand Down Expand Up @@ -859,6 +860,10 @@ class Shard extends EventEmitter {
const channel = this.client.getChannel(packet.d.channel_id);
if(channel) { // MESSAGE_CREATE just when deleting o.o
channel.lastMessageID = packet.d.id;
if(channel instanceof ThreadChannel) {
channel.messageCount++;
channel.totalMessageSent++;
}
/**
* Fired when a message is created
* @event Client#messageCreate
Expand Down Expand Up @@ -922,7 +927,9 @@ class Shard extends EventEmitter {
}
case "MESSAGE_DELETE": {
const channel = this.client.getChannel(packet.d.channel_id);

if(channel instanceof ThreadChannel) {
channel.messageCount--;
}
/**
* Fired when a cached message is deleted
* @event Client#messageDelete
Expand All @@ -940,7 +947,9 @@ class Shard extends EventEmitter {
}
case "MESSAGE_DELETE_BULK": {
const channel = this.client.getChannel(packet.d.channel_id);

if(channel instanceof ThreadChannel) {
channel.messageCount -= packet.d.ids.length;
}
/**
* Fired when a bulk delete occurs
* @event Client#messageDeleteBulk
Expand Down Expand Up @@ -1530,10 +1539,10 @@ class Shard extends EventEmitter {
channel.guild.channels.add(channel, this.client);
this.client.channelGuildMap[packet.d.id] = packet.d.guild_id;
/**
* Fired when a channel is created
* @event Client#channelCreate
* @prop {TextChannel | TextVoiceChannel | CategoryChannel | StoreChannel | NewsChannel | GuildChannel} channel The channel
*/
* Fired when a channel is created
* @event Client#channelCreate
* @prop {ForumChannel | TextChannel | TextVoiceChannel | CategoryChannel | NewsChannel | GuildChannel} channel The channel
*/
this.emit("channelCreate", channel);
} else {
this.emit("warn", new Error("Unhandled CHANNEL_CREATE type: " + JSON.stringify(packet, null, 2)));
Expand All @@ -1555,7 +1564,14 @@ class Shard extends EventEmitter {
};
} else if(channel instanceof GuildChannel) {
oldChannel = {
availableTags: channel.availableTags,
bitrate: channel.bitrate,
defaultAutoArchiveDuration: channel.defaultAutoArchiveDuration,
defaultForumLayout: channel.defaultForumLayout,
defaultReactionEmoji: channel.defaultReactionEmoji,
defaultSortOrder: channel.defaultSortOrder,
defaultThreadRateLimitPerUser: channel.defaultThreadRateLimitPerUser,
flags: channel.flags,
name: channel.name,
nsfw: channel.nsfw,
parentID: channel.parentID,
Expand Down Expand Up @@ -1601,23 +1617,30 @@ class Shard extends EventEmitter {
}

/**
* Fired when a channel is updated
* @event Client#channelUpdate
* @prop {TextChannel | TextVoiceChannel | CategoryChannel | StoreChannel | NewsChannel | GuildChannel | PrivateChannel} channel The updated channel
* @prop {Object} oldChannel The old channel data
* @prop {Number} oldChannel.bitrate The bitrate of the channel (voice channels only)
* @prop {String} oldChannel.name The name of the channel
* @prop {Boolean} oldChannel.nsfw Whether the channel is NSFW or not (text channels only)
* @prop {String?} oldChannel.parentID The ID of the category this channel belongs to (guild channels only)
* @prop {Collection} oldChannel.permissionOverwrites Collection of PermissionOverwrites in this channel (guild channels only)
* @prop {Number} oldChannel.position The position of the channel (guild channels only)
* @prop {Number?} oldChannel.rateLimitPerUser The ratelimit of the channel, in seconds. 0 means no ratelimit is enabled (text channels only)
* @prop {String?} oldChannel.rtcRegion The RTC region ID of the channel (automatic when `null`) (voice channels only)
* @prop {String?} oldChannel.topic The topic of the channel (text channels only)
* @prop {Number} oldChannel.type The type of the old channel (text/news channels only)
* @prop {Number?} oldChannel.userLimit The max number of users that can join the channel (voice channels only)
* @prop {Number?} oldChannel.videoQualityMode The camera video quality mode of the channel (voice channels only)
*/
* Fired when a channel is updated
* @event Client#channelUpdate
* @prop {ForumChannel | TextChannel | TextVoiceChannel | CategoryChannel | NewsChannel | GuildChannel | PrivateChannel} channel The updated channel
* @prop {Object} oldChannel The old channel data
* @prop {Array<Object>} oldChannel.availableTags The available tags that can be applied to threads in a forum channel. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#forum-tag-object) for object structure (forum channels only, max 20)
* @prop {Number} oldChannel.bitrate The bitrate of the channel (voice channels only)
* @prop {Number} oldChannel.defaultAutoArchiveDuration The default duration of newly created threads in minutes to automatically archive the thread after inactivity (60, 1440, 4320, 10080) (text/news/forum channels only)
* @prop {Number} oldChannel.defaultForumLayout The default forum layout type used to display posts in forum channels (forum channels only)
* @prop {Object} oldChannel.defaultReactionEmoji The emoji to show in the add reaction button on a thread in a forum channel (forum channels only)
* @prop {Number} oldChannel.defaultSortOrder The default sort order type used to order posts in forum channels (forum channels only)
* @prop {Number} oldChannel.defaultThreadRateLimitPerUser The initial rateLimitPerUser to set on newly created threads in a channel (text/forum channels only)
* @prop {Number?} oldChannel.flags The flags for the channel combined as a bitfield (thread/forum channels only)
* @prop {String} oldChannel.name The name of the channel
* @prop {Boolean} oldChannel.nsfw Whether the channel is NSFW or not (text channels only)
* @prop {String?} oldChannel.parentID The ID of the category this channel belongs to (guild channels only)
* @prop {Collection} oldChannel.permissionOverwrites Collection of PermissionOverwrites in this channel (guild channels only)
* @prop {Number} oldChannel.position The position of the channel (guild channels only)
* @prop {Number?} oldChannel.rateLimitPerUser The time in seconds a user has to wait before sending another message (0-21600) (text/voice/stage/forum channels only)
* @prop {String?} oldChannel.rtcRegion The RTC region ID of the channel (automatic when `null`) (voice channels only)
* @prop {String?} oldChannel.topic The topic of the channel (text channels only)
* @prop {Number} oldChannel.type The type of the old channel (text/news channels only)
* @prop {Number?} oldChannel.userLimit The max number of users that can join the channel (voice channels only)
* @prop {Number?} oldChannel.videoQualityMode The camera video quality mode of the channel (voice channels only)
*/
this.emit("channelUpdate", channel, oldChannel);
break;
}
Expand All @@ -1628,10 +1651,10 @@ class Shard extends EventEmitter {
if(channel) {
delete this.client.privateChannelMap[channel.recipient.id];
/**
* Fired when a channel is deleted
* @event Client#channelDelete
* @prop {PrivateChannel | TextChannel | NewsChannel | TextVoiceChannel | CategoryChannel} channel The channel
*/
* Fired when a channel is deleted
* @event Client#channelDelete
* @prop {ForumChannel | PrivateChannel | TextChannel | NewsChannel | TextVoiceChannel | CategoryChannel} channel The channel
*/
this.emit("channelDelete", channel);
}
}
Expand Down Expand Up @@ -2157,6 +2180,13 @@ class Shard extends EventEmitter {
}
channel.guild.threads.add(channel, this.client);
this.client.threadGuildMap[packet.d.id] = packet.d.guild_id;

const parent = channel.guild.channels.get(channel.parentID);

if(parent instanceof ForumChannel) {
parent.lastMessageID = channel.id;
}

/**
* Fired when a channel is created
* @event Client#threadCreate
Expand All @@ -2178,25 +2208,31 @@ class Shard extends EventEmitter {
break;
}
const oldChannel = {
appliedTags: channel.appliedTags,
autoArchiveDuration: channel.autoArchiveDuration,
flags: channel.flags,
name: channel.name,
rateLimitPerUser: channel.rateLimitPerUser,
threadMetadata: channel.threadMetadata
};
channel.update(packet.d);

/**
* Fired when a thread channel is updated
* @event Client#threadUpdate
* @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The updated channel
* @prop {Object?} oldChannel The old thread channel. This will be null if the channel was uncached
* @prop {String} oldChannel.name The name of the channel
* @prop {Number} oldChannel.rateLimitPerUser The ratelimit of the channel, in seconds. 0 means no ratelimit is enabled
* @prop {Object} oldChannel.threadMetadata Metadata for the thread
* @prop {Number} oldChannel.threadMetadata.archiveTimestamp Timestamp when the thread's archive status was last changed, used for calculating recent activity
* @prop {Boolean} oldChannel.threadMetadata.archived Whether the thread is archived
* @prop {Number} oldChannel.threadMetadata.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080
* @prop {Boolean?} oldChannel.threadMetadata.locked Whether the thread is locked
*/
* Fired when a thread channel is updated
* @event Client#threadUpdate
* @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The updated channel
* @prop {Object?} oldChannel The old thread channel. This will be null if the channel was uncached
* @prop {Array<String>?} oldChannel.appliedTags The IDs of the set of tags that have been applied to a thread in a forum channel
* @prop {Number} oldChannel.autoArchiveDuration The duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080
* @prop {Number} oldChannel.flags The flags for the channel combined as a bitfield
* @prop {String} oldChannel.name The name of the channel
* @prop {Number} oldChannel.rateLimitPerUser The time in seconds a user has to wait before sending another message (0-21600)
* @prop {Object} oldChannel.threadMetadata Metadata for the thread
* @prop {Boolean} oldChannel.threadMetadata.archived Whether the thread is archived
* @prop {Number} oldChannel.threadMetadata.archiveTimestamp Timestamp when the thread's archive status was last changed, used for calculating recent activity
* @prop {Number} oldChannel.threadMetadata.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080
* @prop {Boolean?} oldChannel.threadMetadata.locked Whether the thread is locked
*/
this.emit("threadUpdate", channel, oldChannel);
break;
}
Expand Down
4 changes: 4 additions & 0 deletions lib/structures/Channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ class Channel extends Base {
case ChannelTypes.GUILD_STAGE_VOICE: {
return new StageChannel(data, client);
}
case ChannelTypes.GUILD_FORUM: {
return new ForumChannel(data, client);
}
}
if(data.guild_id) {
if(data.last_message_id !== undefined) {
Expand All @@ -83,6 +86,7 @@ module.exports = Channel;
// Circular import
const CategoryChannel = require("./CategoryChannel");
const GuildChannel = require("./GuildChannel");
const ForumChannel = require("./ForumChannel");
const GroupChannel = require("./GroupChannel");
const NewsChannel = require("./NewsChannel");
const NewsThreadChannel = require("./NewsThreadChannel");
Expand Down
Loading

0 comments on commit a62cb39

Please sign in to comment.