Skip to content

Commit 07d2f55

Browse files
committed
misc help thread system changes
1 parent 4802673 commit 07d2f55

File tree

3 files changed

+99
-52
lines changed

3 files changed

+99
-52
lines changed

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ TRUSTED_ROLE_ID=
1414
RULES_CHANNEL=
1515

1616
HELP_CATEGORY=
17+
HOW_TO_GET_HELP_CHANNEL=
18+
GENERAL_HELP_CHANNEL=
1719

1820
# Time in milliseconds before !helper can be run
1921
TIME_BEFORE_HELPER_PING=

src/env.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,15 @@ export const autorole = process.env.AUTOROLE!.split(',').map(x => {
1717
export const dbUrl = process.env.DATABASE_URL!;
1818

1919
export const helpCategory = process.env.HELP_CATEGORY!;
20+
export const howToGetHelpChannel = process.env.HOW_TO_GET_HELP_CHANNEL!;
21+
export const generalHelpChannel = process.env.GENERAL_HELP_CHANNEL!;
2022

2123
export const trustedRoleId = process.env.TRUSTED_ROLE_ID!;
2224

2325
export const rulesChannelId = process.env.RULES_CHANNEL!;
2426

2527
export const TS_BLUE = '#007ACC';
26-
export const GREEN = '#77b155';
27-
// Picked from Discord's "hourglass" emoji (in ⌛ | Occupied Help Channels)
28-
export const HOURGLASS_ORANGE = '#ffa647';
29-
// Picked from Discord's :ballot_box_with_check: emoji (☑)
30-
export const BALLOT_BOX_BLUE = '#066696';
28+
export const GREEN = '#3ba55d';
3129
// Picked from Discord's blockquote line
3230
export const BLOCKQUOTE_GREY = '#4f545c';
3331

src/modules/helpthread.ts

Lines changed: 94 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,22 @@ import {
66
Channel,
77
ThreadChannel,
88
MessageEmbed,
9+
GuildMember,
910
} from 'discord.js';
10-
import { threadId } from 'worker_threads';
1111
import { HelpThread } from '../entities/HelpThread';
1212
import {
1313
trustedRoleId,
1414
helpCategory,
1515
timeBeforeHelperPing,
1616
GREEN,
1717
BLOCKQUOTE_GREY,
18+
generalHelpChannel,
19+
howToGetHelpChannel,
1820
} from '../env';
1921
import { isTrustedMember } from '../util/inhibitors';
20-
import { LimitedSizeMap } from '../util/limitedSizeMap';
2122
import { sendWithMessageOwnership } from '../util/send';
2223

23-
const THREAD_EXPIRE_MESSAGE = new MessageEmbed()
24+
const threadExpireEmbed = new MessageEmbed()
2425
.setColor(BLOCKQUOTE_GREY)
2526
.setTitle('This help thread expired.').setDescription(`
2627
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
3132
// A zero-width space (necessary to prevent discord from trimming the leading whitespace), followed by a three non-breaking spaces.
3233
const indent = '\u200b\u00a0\u00a0\u00a0';
3334

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!');
4539

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}>.
4848
${indent}• It's always ok to just ask your question; you don't need permission.
4949
• Our bot will make a thread dedicated to answering your channel.
5050
• 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+
`
5459
• Explain what you want to happen and why…
5560
${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.
5762
${indent}• Text is better than screenshots. Start code blocks with ${'\\`\\`\\`ts'}.
5863
• 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.
6065
• 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+
`
6474
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+
];
6879

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+
`;
7184

7285
export class HelpThreadModule extends Module {
7386
@listener({ event: 'messageCreate' })
@@ -80,27 +93,38 @@ export class HelpThreadModule extends Module {
8093
autoArchiveDuration: ThreadAutoArchiveDuration.OneDay,
8194
});
8295
thread.setLocked(true);
96+
thread.send(helpThreadWelcomeMessage(msg.member!));
8397
await HelpThread.create({
8498
threadId: thread.id,
8599
ownerId: msg.author.id,
86100
}).save();
87101
}
88102

103+
// Used to differentiate automatic archive from bot archive
104+
manuallyArchivedThreads = new Set<string>();
105+
89106
@listener({ event: 'threadUpdate' })
90107
async onThreadExpire(thread: ThreadChannel) {
91108
if (
92109
!this.isHelpThread(thread) ||
93-
manuallyArchivedThreads.has(thread.id) ||
110+
this.manuallyArchivedThreads.delete(thread.id) ||
94111
!((await thread.fetch()) as ThreadChannel).archived
95112
)
96113
return;
97-
await thread.send({ embeds: [THREAD_EXPIRE_MESSAGE] });
114+
await thread.send({ embeds: [threadExpireEmbed] });
98115
await this.closeThread(thread);
99116
}
100117

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+
);
104128

105129
const threadData = (await HelpThread.findOne(msg.channel.id))!;
106130

@@ -110,29 +134,31 @@ export class HelpThreadModule extends Module {
110134
) {
111135
await this.closeThread(msg.channel);
112136
} 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.',
115140
);
116141
}
117142
}
118143

119144
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);
122147
await HelpThread.delete(thread.id);
123148
}
124149

150+
private helpInfoLocks = new Map<string, Promise<void>>();
125151
private updateHelpInfo(channel: TextChannel) {
126-
helpInfoLocks.set(
152+
this.helpInfoLocks.set(
127153
channel.id,
128-
(helpInfoLocks.get(channel.id) ?? Promise.resolve()).then(
154+
(this.helpInfoLocks.get(channel.id) ?? Promise.resolve()).then(
129155
async () => {
130156
await Promise.all([
131157
...(await channel.messages.fetchPinned()).map(x =>
132158
x.delete(),
133159
),
134160
channel
135-
.send({ embeds: [HELP_INFO(channel)] })
161+
.send({ embeds: [helpInfo(channel)] })
136162
.then(x => x.pin()),
137163
]);
138164
},
@@ -153,7 +179,9 @@ export class HelpThreadModule extends Module {
153179
channel: Omit<Channel, 'partial'>,
154180
): channel is TextChannel {
155181
return (
156-
channel instanceof TextChannel && channel.parentId == helpCategory
182+
channel instanceof TextChannel &&
183+
channel.parentId == helpCategory &&
184+
channel.id !== howToGetHelpChannel
157185
);
158186
}
159187

@@ -167,7 +195,7 @@ export class HelpThreadModule extends Module {
167195
}
168196

169197
@command({
170-
description: 'Pings a helper in a help-thread',
198+
description: 'Help System: Ping the @Helper role from a help thread',
171199
aliases: ['helpers'],
172200
})
173201
async helper(msg: Message) {
@@ -219,13 +247,32 @@ export class HelpThreadModule extends Module {
219247
]);
220248
}
221249

222-
@command({ single: true })
250+
@command({ single: true, description: 'Help System: Rename a help thread' })
223251
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');
226259
let username = msg.member?.nickname ?? msg.author.username;
227260
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+
);
229265
msg.channel.setName(`${username} - ${title}`);
230266
}
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+
}
231278
}

0 commit comments

Comments
 (0)