Skip to content

Commit

Permalink
feat: threads
Browse files Browse the repository at this point in the history
  • Loading branch information
ckohen committed May 20, 2021
1 parent c8d20a4 commit 753301a
Show file tree
Hide file tree
Showing 33 changed files with 1,418 additions and 23 deletions.
5 changes: 5 additions & 0 deletions esm/discord.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const {
SnowflakeUtil,
Structures,
SystemChannelFlags,
ThreadMemberFlags,
UserFlags,
Util,
version,
Expand All @@ -44,6 +45,8 @@ export const {
MessageManager,
PresenceManager,
RoleManager,
ThreadManager,
ThreadMemberManager,
UserManager,
discordSort,
escapeMarkdown,
Expand Down Expand Up @@ -98,6 +101,8 @@ export const {
Team,
TeamMember,
TextChannel,
ThreadChannel,
ThreadMember,
User,
VoiceChannel,
VoiceRegion,
Expand Down
5 changes: 5 additions & 0 deletions src/client/actions/ActionsManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ class ActionsManager {
this.register(require('./GuildEmojiDelete'));
this.register(require('./GuildEmojiUpdate'));
this.register(require('./GuildEmojisUpdate'));
this.register(require('./ThreadCreate'));
this.register(require('./ThreadDelete'));
this.register(require('./ThreadListSync'));
this.register(require('./ThreadMemberUpdate'));
this.register(require('./ThreadMembersUpdate'));
this.register(require('./GuildRolesPositionUpdate'));
this.register(require('./GuildChannelsPositionUpdate'));
this.register(require('./GuildIntegrationsUpdate'));
Expand Down
23 changes: 23 additions & 0 deletions src/client/actions/ThreadCreate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

const Action = require('./Action');
const { Events } = require('../../util/Constants');

class ThreadCreateAction extends Action {
handle(data) {
const client = this.client;
const existing = client.channels.cache.has(data.id);
const thread = client.channels.add(data);
if (!existing && thread) {
/**
* Emitted whenever a thread is created or when the client user is added to a thread.
* @event Client#threadCreate
* @param {ThreadChannel} thread The thread that was created
*/
client.emit(Events.THREAD_CREATE, thread);
}
return { thread };
}
}

module.exports = ThreadCreateAction;
34 changes: 34 additions & 0 deletions src/client/actions/ThreadDelete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

const Action = require('./Action');
const { Events } = require('../../util/Constants');

class ThreadDeleteAction extends Action {
constructor(client) {
super(client);
this.deleted = new Map();
}

handle(data) {
const client = this.client;
let thread = client.channels.cache.get(data.id);

if (thread) {
client.channels.remove(thread.id);
thread.deleted = true;
for (const message of thread.messages.cache.values()) {
message.deleted = true;
}
/**
* Emitted whenever a thread is deleted.
* @event Client#threadDelete
* @param {ThreadChannel} thread The thread that was deleted
*/
client.emit(Events.THREAD_DELETE, thread);
}

return { thread };
}
}

module.exports = ThreadDeleteAction;
68 changes: 68 additions & 0 deletions src/client/actions/ThreadListSync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use strict';

const Action = require('./Action');
const Collection = require('../../util/Collection');
const { Events } = require('../../util/Constants');

class ThreadListSyncAction extends Action {
handle(data) {
const client = this.client;

let guild = client.guilds.cache.get(data.guild_id);
if (guild) {
if (!data.channels_ids) {
guild.channels.cache.forEach(channel => {
this.removeStale(channel);
});
} else {
data.channel_ids.forEach(id => {
const channel = client.channels.resolve(id);
if (channel) this.removeStale(channel);
});
}

const syncedThreads = new Collection();
data.threads.forEach(rawThread => {
const thread = client.channels.add(rawThread);
syncedThreads.set(thread.id, thread);
});

let members = data.members;
if (typeof members === 'object') {
members = Object.values(members);
}

members.forEach(rawMember => {
const thread = client.channels.cache.get(rawMember.id);
if (thread) {
thread.members.add(rawMember);
}
});

/**
* Emitted whenever the client user gains access to a text or news channel
* @event Client#threadListSync
* @param {Collection<Snwoflake, ThreadChannel>} threads The threads that were synced
*/
client.emit(Events.THREAD_LIST_SYNC, syncedThreads);

return {
syncedThreads,
};
}

return {};
}

removeStale(channel) {
if (channel.threads) {
channel.threads.cache.forEach(thread => {
if (!thread.archived) {
this.client.channels.remove(thread.id);
}
});
}
}
}

module.exports = ThreadListSyncAction;
29 changes: 29 additions & 0 deletions src/client/actions/ThreadMemberUpdate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

const Action = require('./Action');
const { Events } = require('../../util/Constants');

class ThreadMemberUpdateAction extends Action {
handle(data) {
const client = this.client;
const thread = client.channels.cache.get(data.id);
if (thread) {
const member = thread.members.cache.get(data.user_id);
if (!member) {
const newMember = thread.members.add(data);
return { newMember };
}
const old = member._update(data);
/**
* Emitted whenever the client users thread member is updated.
* @event Client#threadMemberUpdate
* @param {ThreadMember} oldMember The member before the update
* @param {ThreadMember} newMember The member after the update
*/
client.emit(Events.THREAD_MEMBER_UPDATE, old, member);
}
return {};
}
}

module.exports = ThreadMemberUpdateAction;
37 changes: 37 additions & 0 deletions src/client/actions/ThreadMembersUpdate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

const Action = require('./Action');
const { Events } = require('../../util/Constants');

class ThreadMembersUpdateAction extends Action {
handle(data) {
const client = this.client;
const thread = client.channels.cache.get(data.id);
if (thread) {
const old = thread.members.cache.clone();
thread.memberCount = data.member_count;

if (data.added_members) {
data.added_members.forEach(rawMember => {
thread.members.add(rawMember);
});
}

if (data.removed_member_ids) {
data.removed_member_ids.forEach(memberId => {
thread.members.cache.delete(memberId);
});
}
/**
* Emitted whenever members are added or removed from a thread. Requires `GUILD_MEMBERS` privileged intent
* @event Client#threadMembersUpdate
* @param {Collection<Snowflake, ThreadMember>} oldMembers The members before the update
* @param {Collection<Snowflake, ThreadMember>} newMembers The members after the update
*/
client.emit(Events.THREAD_MEMBERS_UPDATE, old, thread.members.cache);
}
return {};
}
}

module.exports = ThreadMembersUpdateAction;
2 changes: 1 addition & 1 deletion src/client/actions/TypingStart.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const Action = require('./Action');
const { Events } = require('../../util/Constants');
const textBasedChannelTypes = ['dm', 'text', 'news'];
const textBasedChannelTypes = ['dm', 'text', 'news', 'news_thread', 'public_thread', 'private_thread'];

class TypingStart extends Action {
handle(data) {
Expand Down
5 changes: 5 additions & 0 deletions src/client/websocket/handlers/THREAD_CREATE.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

module.exports = (client, packet) => {
client.actions.ThreadCreate.handle(packet.d);
};
5 changes: 5 additions & 0 deletions src/client/websocket/handlers/THREAD_DELETE.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

module.exports = (client, packet) => {
client.actions.ThreadDelete.handle(packet.d);
};
5 changes: 5 additions & 0 deletions src/client/websocket/handlers/THREAD_LIST_SYNC.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

module.exports = (client, packet) => {
client.actions.ThreadListSync.handle(packet.d);
};
5 changes: 5 additions & 0 deletions src/client/websocket/handlers/THREAD_MEMBERS_UPDATE.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

module.exports = (client, packet) => {
client.actions.ThreadMembersUpdate.handle(packet.d);
};
5 changes: 5 additions & 0 deletions src/client/websocket/handlers/THREAD_MEMBER_UPDATE.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

module.exports = (client, packet) => {
client.actions.ThreadMemberUpdate.handle(packet.d);
};
16 changes: 16 additions & 0 deletions src/client/websocket/handlers/THREAD_UPDATE.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict';

const { Events } = require('../../../util/Constants');

module.exports = (client, packet) => {
const { old, updated } = client.actions.ChannelUpdate.handle(packet.d);
if (old && updated) {
/**
* Emitted whenever a thread is updated - e.g. name change, archive state change, locked state change.
* @event Client#threadUpdate
* @param {ThreadChannel} oldThread The thread before the update
* @param {ThreadChannel} newThread The thread after the update
*/
client.emit(Events.THREAD_UPDATE, old, updated);
}
};
2 changes: 2 additions & 0 deletions src/errors/Messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ const Messages = {
INVALID_TYPE: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`,
INVALID_ELEMENT: (type, name, elem) => `Supplied ${type} ${name} includes an invalid element: ${elem}`,

MESSAGE_THREAD_PARENT: 'The message was not sent in a guild text or news channel',

WEBHOOK_MESSAGE: 'The message was not sent by a webhook.',
MESSAGE_REFERENCE_MISSING: 'The message does not reference another message',

Expand Down
5 changes: 5 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports = {
SnowflakeUtil: require('./util/SnowflakeUtil'),
Structures: require('./util/Structures'),
SystemChannelFlags: require('./util/SystemChannelFlags'),
ThreadMemberFlags: require('./util/ThreadMemberFlags'),
UserFlags: require('./util/UserFlags'),
Util: Util,
version: require('../package.json').version,
Expand All @@ -49,6 +50,8 @@ module.exports = {
MessageManager: require('./managers/MessageManager'),
PresenceManager: require('./managers/PresenceManager'),
RoleManager: require('./managers/RoleManager'),
ThreadManager: require('./managers/ThreadManager'),
ThreadMemberManager: require('./managers/ThreadMemberManager'),
UserManager: require('./managers/UserManager'),

// Shortcuts to Util methods
Expand Down Expand Up @@ -110,6 +113,8 @@ module.exports = {
Team: require('./structures/Team'),
TeamMember: require('./structures/TeamMember'),
TextChannel: require('./structures/TextChannel'),
ThreadChannel: require('./structures/ThreadChannel'),
ThreadMember: require('./structures/ThreadMember'),
User: require('./structures/User'),
VoiceChannel: require('./structures/VoiceChannel'),
VoiceRegion: require('./structures/VoiceRegion'),
Expand Down
6 changes: 5 additions & 1 deletion src/managers/ChannelManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const BaseManager = require('./BaseManager');
const Channel = require('../structures/Channel');
const { Events } = require('../util/Constants');
const { Events, ThreadChannelTypes } = require('../util/Constants');

/**
* A manager of channels belonging to a client
Expand All @@ -24,6 +24,9 @@ class ChannelManager extends BaseManager {
if (existing) {
if (existing._patch && cache) existing._patch(data);
if (guild) guild.channels.add(existing);
if (ThreadChannelTypes.includes(existing.type) && typeof existing.parent?.threads !== 'undefined') {
existing.parent.threads.add(existing);
}
return existing;
}

Expand All @@ -42,6 +45,7 @@ class ChannelManager extends BaseManager {
remove(id) {
const channel = this.cache.get(id);
channel?.guild?.channels.cache.delete(id);
channel?.parent?.threads?.cache.delete(id);
this.cache.delete(id);
}

Expand Down
Loading

0 comments on commit 753301a

Please sign in to comment.