-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
ThreadManager.js
226 lines (205 loc) · 8.06 KB
/
ThreadManager.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
'use strict';
const BaseManager = require('./BaseManager');
const { TypeError } = require('../errors');
const ThreadChannel = require('../structures/ThreadChannel');
const { Collection } = require('../util/Collection');
/**
* Manages API methods for ThreadChannels and stores their cache.
* @extends {BaseManager}
*/
class ThreadManager extends BaseManager {
constructor(channel, iterable) {
super(channel.client, iterable, ThreadChannel);
/**
* The channel this Manager belongs to
* @type {NewsChannel|TextChannel}
*/
this.channel = channel;
}
/**
* The cache of this Manager
* @type {Collection<Snowflake, ThreadChannel>}
* @name ThreadManager#cache
*/
add(thread) {
const existing = this.cache.get(thread.id);
if (existing) return existing;
this.cache.set(thread.id, thread);
return thread;
}
/**
* Data that can be resolved to give a Thread Channel object. This can be:
* * A ThreadChannel object
* * A Snowflake
* @typedef {ThreadChannel|Snowflake} ThreadChannelResolvable
*/
/**
* Resolves a ThreadChannelResolvable to a Thread Channel object.
* @method resolve
* @memberof ThreadManager
* @instance
* @param {ThreadChannelResolvable} thread The ThreadChannel resolvable to resolve
* @returns {?ThreadChannel}
*/
/**
* Resolves a ThreadChannelResolvable to a thread channel ID string.
* @method resolveID
* @memberof ThreadManager
* @instance
* @param {ThreadChannelResolvable} thread The ThreadChannel resolvable to resolve
* @returns {?Snowflake}
*/
/**
* A number that is allowed to be the duration in minutes before a thread is automatically archived. This can be:
* * `60` (1 hour)
* * `1440` (1 day)
* * `4320` (3 days)
* * `10080` (7 days)
* @typedef {number} ThreadAutoArchiveDuration
*/
/**
* Creates a new thread in the channel.
* @param {string} name The name of the new Thread
* @param {ThreadAutoArchiveDuration} autoArchiveDuration How long before the thread is automatically archived
* @param {Object} [options] Options
* @param {MessageResolvable} [options.startMessage] The message to start a public or news thread from,
* creates a private thread if not provided
* @param {string} [options.reason] Reason for creating the thread
* @returns {Promise<ThreadChannel>}
* @example
* // Create a new public thread
* channel.threads
* .create('food-talk', {
* autoArchiveDuration: 60,
* startMessage: channel.lastMessageId,
* reason: 'Needed a separate thread for food',
* })
* .then(console.log)
* .catch(console.error);
* // Create a new private thread
* channel.threads.create('mod-talk', { autoArchiveDuration: 60, reason: 'Needed a separate thread for moderation' })
* .then(console.log)
* .catch(console.error);
*/
async create(name, autoArchiveDuration, { startMessage, reason } = {}) {
let path = this.client.api.channels(this.channel.id);
if (startMessage) {
startMessage = this.channel.messages.resolveID(startMessage);
path = path.messages(startMessage);
}
const data = await path.threads.post({
data: {
name,
auto_archive_duration: autoArchiveDuration,
},
reason,
});
return this.client.actions.ThreadCreate.handle(data).thread;
}
/**
* The options for fetching multiple threads, the properties are mutually exclusive
* @typedef {Object} FetchThreadsOptions
* @property {FetchArchivedThreadOptions} [archived] The options used to fetch archived threads
* @property {boolean} [active] When true, fetches active threads, if archived is set, this is overriden!
*/
/**
* Obtains a thread from Discord, or the channel cache if it's already available.
* @param {ThreadChannelResolvable|FetchThreadsOptions} [options] If a ThreadChannelResolvable, the thread to fetch.
* If undefined, fetches all active threads.
* If an Object, fetches the specified threads.
* @param {boolean} [cache=true] Whether to cache the new thread object if it isn't already
* @param {boolean} [force=false] Whether to skip the cache check and request the API,
* only applies when fetching a single thread
* @returns {Promise<?ThreadChannel>}
* @example
* // Fetch a thread by its id
* channel.threads.fetch('831955138126104859')
* .then(channel => console.log(channel.name))
* .catch(console.error);
*/
fetch(options, cache = true, force = false) {
if (!options) return this.fetchActive(cache);
const channel = this.client.channels.resolveID(options);
if (channel) return this.client.channels.fetch(channel, cache, force);
if (options.archived) {
return this.fetchArchived(options.archived, cache);
}
return this.fetchActive(cache);
}
/**
* Data that can be resolved to give a Date object. This can be:
* * A Date object
* * A number representing a timestamp
* * An ISO8601 string
* @typedef {Date|number|string} DateResolvable
*/
/**
* The options used to fetch archived threads.
* @typedef {Object} FetchArchivedThreadOptions
* @property {string} [type='public'] The type of threads to fetch, either `public` or `private`
* @property {boolean} [fetchAll=false] When type is `private` whether to fetch **all** archived threads,
* requires `MANAGE_THREADS` if true
* @property {DateResolvable|ThreadChannelResolvable} [before] Identifier for a Date or Snowflake
* to get threads that were created before it,
* must be a ThreadChannelResolvable when type is `private` and fetchAll is `false`
* @property {number} [limit] Maximum number of threads to return
*/
/**
* The data returned from a thread fetch that returns multiple threads.
* @typedef {FetchedThreads}
* @property {Collection<Snowflake, ThreadChannel>} threads The threads fetched, with any members returned
* @property {?boolean} hasMore Whether there are potentially additional threads that require a subsequent call
*/
/**
* Obtains a set of archived threads from Discord, requires `READ_MESSAGE_HISTORY` in the parent channel.
* @param {FetchArchivedThreadOptions} [options] The options to use when fetch archived threads
* @param {boolean} [cache=true] Whether to cache the new thread objects if they aren't already
* @returns {Promise<?FetchedThreads>}
*/
async fetchArchived({ type = 'public', fetchAll = false, before, limit } = {}, cache = true) {
let path = this.client.api.channels(this.channel.id);
if (type === 'private' && fetchAll) {
path = path.users('@me');
}
let timestamp;
let id;
if (typeof before !== 'undefined') {
if (before instanceof ThreadChannel || !!String(before).match(/[0-9]{16,18}/g)) {
id = this.resolveID(before);
timestamp = this.resolve(before)?.archivedAt.toISOString();
} else {
try {
timestamp = new Date(before).toISOString();
} catch {
throw new TypeError('INVALID_TYPE', 'before', 'DateResolvable or ThreadChannelResolvable');
}
}
}
const raw = await path.threads
.archived(type)
.get({ query: { before: type === 'private' && !fetchAll ? id : timestamp, limit } });
return this._mapThreads(raw, cache);
}
/**
* Obtains the accessible active threads from Discord, requires `READ_MESSAGE_HISTORY` in the parent channel.
* @param {boolean} [cache=true] Whether to cache the new thread objects if they aren't already
* @returns {Promise<?FetchedThreads>}
*/
async fetchActive(cache = true) {
const raw = await this.client.api.channels(this.channel.id).threads.active.get();
return this._mapThreads(raw, cache);
}
_mapThreads(rawThreads, cache) {
const threads = new Collection();
rawThreads.threads.forEach(raw => {
const thread = this.client.channels.add(raw, null, cache);
threads.set(thread.id, thread);
});
rawThreads.members.forEach(rawMember => this.client.channels.cache.get(rawMember.id)?.members.add(rawMember));
return {
threads,
hasMore: rawThreads.has_more,
};
}
}
module.exports = ThreadManager;