@@ -6,21 +6,22 @@ import {
6
6
Channel ,
7
7
ThreadChannel ,
8
8
MessageEmbed ,
9
+ GuildMember ,
9
10
} from 'discord.js' ;
10
- import { threadId } from 'worker_threads' ;
11
11
import { HelpThread } from '../entities/HelpThread' ;
12
12
import {
13
13
trustedRoleId ,
14
14
helpCategory ,
15
15
timeBeforeHelperPing ,
16
16
GREEN ,
17
17
BLOCKQUOTE_GREY ,
18
+ generalHelpChannel ,
19
+ howToGetHelpChannel ,
18
20
} from '../env' ;
19
21
import { isTrustedMember } from '../util/inhibitors' ;
20
- import { LimitedSizeMap } from '../util/limitedSizeMap' ;
21
22
import { sendWithMessageOwnership } from '../util/send' ;
22
23
23
- const THREAD_EXPIRE_MESSAGE = new MessageEmbed ( )
24
+ const threadExpireEmbed = new MessageEmbed ( )
24
25
. setColor ( BLOCKQUOTE_GREY )
25
26
. setTitle ( 'This help thread expired.' ) . setDescription ( `
26
27
If your question was not resolved, you can make a new thread by simply asking your question again. \
@@ -31,43 +32,55 @@ If you're not sure how, have a look through [StackOverflow's guide on asking a g
31
32
// A zero-width space (necessary to prevent discord from trimming the leading whitespace), followed by a three non-breaking spaces.
32
33
const indent = '\u200b\u00a0\u00a0\u00a0' ;
33
34
34
- const HELP_INFO = ( channel : TextChannel ) =>
35
- new MessageEmbed ( ) . setColor ( GREEN ) . setTitle ( 'How To Get Help' )
36
- . setDescription ( `
37
- ${
38
- channel . topic
39
- ? `This channel is for ${
40
- channel . topic [ 0 ] . toLowerCase ( ) +
41
- channel . topic . slice ( 1 ) . split ( '\n' ) [ 0 ]
42
- } `
43
- : ''
44
- }
35
+ const helpInfo = ( channel : TextChannel ) =>
36
+ new MessageEmbed ( )
37
+ . setColor ( GREEN )
38
+ . setDescription ( channel . topic ?? 'Ask your questions here!' ) ;
45
39
46
- **To get help:**
47
- • Post your question to this channel.
40
+ const howToGetHelpEmbeds = ( ) => [
41
+ new MessageEmbed ( )
42
+ . setColor ( GREEN )
43
+ . setTitle ( 'How To Get Help' )
44
+ . setDescription (
45
+ `
46
+ • Post your question to one of the channels in this category.
47
+ ${ indent } • If you're not sure which channel is best, just post in <#${ generalHelpChannel } >.
48
48
${ indent } • It's always ok to just ask your question; you don't need permission.
49
49
• Our bot will make a thread dedicated to answering your channel.
50
50
• Someone will (hopefully!) come along and help you.
51
- • When your question is resolved, type \`!tclose\`.
52
-
53
- **For better & faster answers:**
51
+ • When your question is resolved, type \`!close\`.
52
+ ` ,
53
+ ) ,
54
+ new MessageEmbed ( )
55
+ . setColor ( GREEN )
56
+ . setTitle ( 'How To Get *Better* Help' )
57
+ . setDescription (
58
+ `
54
59
• Explain what you want to happen and why…
55
60
${ indent } • …and what actually happens, and your best guess at why.
56
- • Include a short code sample and error messages, if you got any .
61
+ • Include a short code sample and any error messages you got.
57
62
${ indent } • Text is better than screenshots. Start code blocks with ${ '\\`\\`\\`ts' } .
58
63
• If possible, create a minimal reproduction in the **[TypeScript Playground](https://www.typescriptlang.org/play)**.
59
- ${ indent } • Send the full link in its own message. Do not use a link shortener.
64
+ ${ indent } • Send the full link in its own message; do not use a link shortener.
60
65
• Run \`!title <brief description>\` to make your help thread easier to spot.
61
-
62
- For more tips, check out StackOverflow's guide on **[asking good questions](https://stackoverflow.com/help/how-to-ask)**.
63
-
66
+ • For more tips, check out StackOverflow's guide on **[asking good questions](https://stackoverflow.com/help/how-to-ask)**.
67
+ ` ,
68
+ ) ,
69
+ new MessageEmbed ( )
70
+ . setColor ( GREEN )
71
+ . setTitle ( "If You Haven't Gotten Help" )
72
+ . setDescription (
73
+ `
64
74
Usually someone will try to answer and help solve the issue within a few hours. \
65
- If not, and **if you have followed the bullets above**, you may ping helpers by running \`!helper\`. \
66
- Please allow extra time at night in America/Europe.
67
- ` ) ;
75
+ If not, and if you have followed the bullets above, you can ping helpers by running \`!helper\`.
76
+ ` ,
77
+ ) ,
78
+ ] ;
68
79
69
- const helpInfoLocks = new Map < string , Promise < void > > ( ) ;
70
- const manuallyArchivedThreads = new LimitedSizeMap < string , void > ( 100 ) ;
80
+ const helpThreadWelcomeMessage = ( owner : GuildMember ) => `
81
+ ${ owner } This thread is for your question; when it's resolved, please type \`!close\`. \
82
+ See <#${ howToGetHelpChannel } > for info on how to get better help.
83
+ ` ;
71
84
72
85
export class HelpThreadModule extends Module {
73
86
@listener ( { event : 'messageCreate' } )
@@ -80,27 +93,38 @@ export class HelpThreadModule extends Module {
80
93
autoArchiveDuration : ThreadAutoArchiveDuration . OneDay ,
81
94
} ) ;
82
95
thread . setLocked ( true ) ;
96
+ thread . send ( helpThreadWelcomeMessage ( msg . member ! ) ) ;
83
97
await HelpThread . create ( {
84
98
threadId : thread . id ,
85
99
ownerId : msg . author . id ,
86
100
} ) . save ( ) ;
87
101
}
88
102
103
+ // Used to differentiate automatic archive from bot archive
104
+ manuallyArchivedThreads = new Set < string > ( ) ;
105
+
89
106
@listener ( { event : 'threadUpdate' } )
90
107
async onThreadExpire ( thread : ThreadChannel ) {
91
108
if (
92
109
! this . isHelpThread ( thread ) ||
93
- manuallyArchivedThreads . has ( thread . id ) ||
110
+ this . manuallyArchivedThreads . delete ( thread . id ) ||
94
111
! ( ( await thread . fetch ( ) ) as ThreadChannel ) . archived
95
112
)
96
113
return ;
97
- await thread . send ( { embeds : [ THREAD_EXPIRE_MESSAGE ] } ) ;
114
+ await thread . send ( { embeds : [ threadExpireEmbed ] } ) ;
98
115
await this . closeThread ( thread ) ;
99
116
}
100
117
101
- @command ( )
102
- async tclose ( msg : Message ) {
103
- if ( ! this . isHelpThread ( msg . channel ) ) return ;
118
+ @command ( {
119
+ aliases : [ 'closed' , 'resolved' , 'resolve' , 'done' ] ,
120
+ description : 'Help System: Close an active help thread' ,
121
+ } )
122
+ async close ( msg : Message ) {
123
+ if ( ! this . isHelpThread ( msg . channel ) )
124
+ return await sendWithMessageOwnership (
125
+ msg ,
126
+ ':warning: This can only be run in a help thread' ,
127
+ ) ;
104
128
105
129
const threadData = ( await HelpThread . findOne ( msg . channel . id ) ) ! ;
106
130
@@ -110,29 +134,31 @@ export class HelpThreadModule extends Module {
110
134
) {
111
135
await this . closeThread ( msg . channel ) ;
112
136
} else {
113
- return await msg . channel . send (
114
- ':warning: you have to be the asker to close the thread.' ,
137
+ return await sendWithMessageOwnership (
138
+ msg ,
139
+ ':warning: You have to be the asker to close the thread.' ,
115
140
) ;
116
141
}
117
142
}
118
143
119
144
private async closeThread ( thread : ThreadChannel ) {
120
- manuallyArchivedThreads . set ( thread . id ) ;
121
- await thread . setArchived ( true , 'grrr' ) ;
145
+ this . manuallyArchivedThreads . add ( thread . id ) ;
146
+ await thread . setArchived ( true ) ;
122
147
await HelpThread . delete ( thread . id ) ;
123
148
}
124
149
150
+ private helpInfoLocks = new Map < string , Promise < void > > ( ) ;
125
151
private updateHelpInfo ( channel : TextChannel ) {
126
- helpInfoLocks . set (
152
+ this . helpInfoLocks . set (
127
153
channel . id ,
128
- ( helpInfoLocks . get ( channel . id ) ?? Promise . resolve ( ) ) . then (
154
+ ( this . helpInfoLocks . get ( channel . id ) ?? Promise . resolve ( ) ) . then (
129
155
async ( ) => {
130
156
await Promise . all ( [
131
157
...( await channel . messages . fetchPinned ( ) ) . map ( x =>
132
158
x . delete ( ) ,
133
159
) ,
134
160
channel
135
- . send ( { embeds : [ HELP_INFO ( channel ) ] } )
161
+ . send ( { embeds : [ helpInfo ( channel ) ] } )
136
162
. then ( x => x . pin ( ) ) ,
137
163
] ) ;
138
164
} ,
@@ -153,7 +179,9 @@ export class HelpThreadModule extends Module {
153
179
channel : Omit < Channel , 'partial' > ,
154
180
) : channel is TextChannel {
155
181
return (
156
- channel instanceof TextChannel && channel . parentId == helpCategory
182
+ channel instanceof TextChannel &&
183
+ channel . parentId == helpCategory &&
184
+ channel . id !== howToGetHelpChannel
157
185
) ;
158
186
}
159
187
@@ -167,7 +195,7 @@ export class HelpThreadModule extends Module {
167
195
}
168
196
169
197
@command ( {
170
- description : 'Pings a helper in a help- thread' ,
198
+ description : 'Help System: Ping the @Helper role from a help thread' ,
171
199
aliases : [ 'helpers' ] ,
172
200
} )
173
201
async helper ( msg : Message ) {
@@ -219,13 +247,32 @@ export class HelpThreadModule extends Module {
219
247
] ) ;
220
248
}
221
249
222
- @command ( { single : true } )
250
+ @command ( { single : true , description : 'Help System: Rename a help thread' } )
223
251
async title ( msg : Message , title : string ) {
224
- if ( ! this . isHelpThread ( msg . channel ) ) return ;
225
- if ( ! title ) return sendWithMessageOwnership ( msg , ':x: Missing title' ) ;
252
+ if ( ! this . isHelpThread ( msg . channel ) )
253
+ return sendWithMessageOwnership (
254
+ msg ,
255
+ ':warning: This can only be run in a help thread' ,
256
+ ) ;
257
+ if ( ! title )
258
+ return sendWithMessageOwnership ( msg , ':warning: Missing title' ) ;
226
259
let username = msg . member ?. nickname ?? msg . author . username ;
227
260
if ( msg . channel . name !== username )
228
- return sendWithMessageOwnership ( msg , ':x: Already set thread name' ) ;
261
+ return sendWithMessageOwnership (
262
+ msg ,
263
+ ':warning: Already set thread name' ,
264
+ ) ;
229
265
msg . channel . setName ( `${ username } - ${ title } ` ) ;
230
266
}
267
+
268
+ @command ( )
269
+ async htgh ( msg : Message ) {
270
+ if (
271
+ msg . channel . id !== howToGetHelpChannel ||
272
+ ! msg . member ?. permissions . has ( 'MANAGE_MESSAGES' )
273
+ )
274
+ return ;
275
+ ( await msg . channel . messages . fetch ( ) ) . forEach ( x => x . delete ( ) ) ;
276
+ msg . channel . send ( { embeds : howToGetHelpEmbeds ( ) } ) ;
277
+ }
231
278
}
0 commit comments