Skip to content

Commit 91a64a2

Browse files
authored
Merge pull request #223 from tjjfvi/help-forum-changes
Misc help forum changes
2 parents d801807 + be1a2b6 commit 91a64a2

File tree

4 files changed

+140
-20
lines changed

4 files changed

+140
-20
lines changed

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ HOW_TO_GIVE_HELP_CHANNEL=
2020

2121
HELP_FORUM_CHANNEL=
2222
HELP_REQUESTS_CHANNEL=
23+
HELP_FORUM_OPEN_TAG=Open
24+
HELP_FORUM_RESOLVED_TAG=Resolved
2325

2426
# Time in milliseconds before !helper can be run
2527
TIME_BEFORE_HELPER_PING=

src/bot.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,19 @@ export class Bot {
7474
return botAdmins.includes(user.id);
7575
}
7676

77-
getTrustedMemberError(msg: Message) {
77+
isTrusted(msg: Message) {
7878
if (!msg.guild || !msg.member || !msg.channel.isTextBased()) {
79-
return ":warning: you can't use that command here.";
79+
return false;
8080
}
8181

8282
if (
8383
!msg.member.roles.cache.has(trustedRoleId) &&
8484
!msg.member.permissions.has('ManageMessages')
8585
) {
86-
return ":warning: you don't have permission to use that command.";
86+
return false;
8787
}
88+
89+
return true;
8890
}
8991

9092
async getTargetUser(msg: Message): Promise<User | undefined> {

src/env.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ export const howToGiveHelpChannel = process.env.HOW_TO_GIVE_HELP_CHANNEL!;
2121
export const helpForumChannel = process.env.HELP_FORUM_CHANNEL!;
2222
export const helpRequestsChannel = process.env.HELP_REQUESTS_CHANNEL!;
2323

24+
export const helpForumOpenTagName = process.env.HELP_FORUM_OPEN_TAG!;
25+
export const helpForumResolvedTagName = process.env.HELP_FORUM_RESOLVED_TAG!;
26+
2427
export const trustedRoleId = process.env.TRUSTED_ROLE_ID!;
2528

2629
export const rulesChannelId = process.env.RULES_CHANNEL!;

src/modules/helpForum.ts

Lines changed: 130 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
import { ChannelType, ThreadChannel, TextChannel, Channel } from 'discord.js';
1+
import {
2+
ChannelType,
3+
ThreadChannel,
4+
TextChannel,
5+
Channel,
6+
ForumChannel,
7+
Message,
8+
} from 'discord.js';
29
import { Bot } from '../bot';
310
import { HelpThread } from '../entities/HelpThread';
411
import {
512
helpForumChannel,
13+
helpForumOpenTagName,
14+
helpForumResolvedTagName,
615
helpRequestsChannel,
716
howToGetHelpChannel,
817
howToGiveHelpChannel,
@@ -12,14 +21,20 @@ import {
1221
} from '../env';
1322
import { sendWithMessageOwnership } from '../util/send';
1423

24+
const MAX_TAG_COUNT = 5;
25+
1526
// Use a non-breaking space to force Discord to leave empty lines alone
16-
const postGuidelines = listify(`
17-
How To Get Help
18-
- Create a new post here with your question.
27+
const postGuidelines = (here = true) =>
28+
listify(`
29+
**How To Get Help**
30+
- Create a new post ${
31+
here ? 'here' : `in <#${helpForumChannel}>`
32+
} with your question.
1933
- It's always ok to just ask your question; you don't need permission.
2034
- Someone will (hopefully!) come along and help you.
35+
- When your question is resolved, type \`!resolved\`.
2136
\u200b
22-
How To Get Better Help
37+
**How To Get Better Help**
2338
- Explain what you want to happen and why…
2439
- …and what actually happens, and your best guess at why.
2540
- Include a short code sample and any error messages you got.
@@ -28,24 +43,36 @@ How To Get Better Help
2843
- Send the full link in its own message; do not use a link shortener.
2944
- For more tips, check out StackOverflow's guide on asking good questions: <https://stackoverflow.com/help/how-to-ask>
3045
\u200b
31-
If You Haven't Gotten Help
46+
**If You Haven't Gotten Help**
3247
Usually someone will try to answer and help solve the issue within a few hours. If not, and if you have followed the bullets above, you can ping helpers by running !helper.
3348
`);
3449

3550
const howToGiveHelp = listify(`
36-
How To Give Help
37-
- The channel sidebar on the left will list threads you have joined.
51+
**How To Give Help**
52+
- The channel sidebar on the left will list posts you have joined.
3853
- You can scroll through the channel to see all recent questions.
3954
40-
How To Give *Better* Help
55+
**How To Give *Better* Help**
4156
- Get yourself the <@&${trustedRoleId}> role at <#${rolesChannelId}>
4257
- (If you don't like the pings, you can disable role mentions for the server.)
58+
- As a <@&${trustedRoleId}>, you can:
59+
- React to a help post to add tags.
60+
- If a post appears to be resolved, run \`!resolved\` to mark it as such.
61+
- *Only do this if the asker has indicated that their question has been resolved.*
62+
- Conversely, you can run \`!reopen\` if the asker has follow-up questions.
4363
44-
Useful Snippets
64+
**Useful Snippets**
4565
- \`!screenshot\` — for if an asker posts a screenshot of code
4666
- \`!ask\` — for if an asker only posts "can I get help?"
4767
`);
4868

69+
const helperResolve = (owner: string, helper: string) => `
70+
<@${owner}>
71+
Because your issue seemed to be resolved, this post was marked as resolved by <@${helper}>.
72+
If your issue is not resolved, **you can reopen this post by running \`!reopen\`**.
73+
*If you have a different question, make a new post in <#${helpForumChannel}>.*
74+
`;
75+
4976
export async function helpForumModule(bot: Bot) {
5077
const channel = await bot.client.guilds.cache
5178
.first()
@@ -55,6 +82,8 @@ export async function helpForumModule(bot: Bot) {
5582
return;
5683
}
5784
const forumChannel = channel;
85+
const openTag = getTag(forumChannel, helpForumOpenTagName);
86+
const resolvedTag = getTag(forumChannel, helpForumResolvedTagName);
5887

5988
const helpRequestChannel = await bot.client.guilds.cache
6089
.first()
@@ -64,7 +93,7 @@ export async function helpForumModule(bot: Bot) {
6493
return;
6594
}
6695

67-
await forumChannel.setTopic(postGuidelines);
96+
await forumChannel.setTopic(postGuidelines());
6897

6998
bot.client.on('threadCreate', async thread => {
7099
const owner = await thread.fetchOwner();
@@ -80,6 +109,8 @@ export async function helpForumModule(bot: Bot) {
80109
threadId: thread.id,
81110
ownerId: owner.user.id,
82111
}).save();
112+
113+
await setStatus(thread, openTag);
83114
});
84115

85116
bot.client.on('threadDelete', async thread => {
@@ -91,12 +122,12 @@ export async function helpForumModule(bot: Bot) {
91122

92123
bot.registerCommand({
93124
aliases: ['helper', 'helpers'],
94-
description: 'Help System: Ping the @Helper role from a help thread',
125+
description: 'Help System: Ping the @Helper role from a help post',
95126
async listener(msg, comment) {
96127
if (!isHelpThread(msg.channel)) {
97128
return sendWithMessageOwnership(
98129
msg,
99-
':warning: You may only ping helpers from a help thread',
130+
':warning: You may only ping helpers from a help post',
100131
);
101132
}
102133

@@ -105,7 +136,7 @@ export async function helpForumModule(bot: Bot) {
105136

106137
// Ensure the user has permission to ping helpers
107138
const isAsker = msg.author.id === threadData.ownerId;
108-
const isTrusted = bot.getTrustedMemberError(msg) === undefined; // No error if trusted
139+
const isTrusted = bot.isTrusted(msg);
109140

110141
if (!isAsker && !isTrusted) {
111142
return sendWithMessageOwnership(
@@ -157,10 +188,75 @@ export async function helpForumModule(bot: Bot) {
157188
},
158189
});
159190

191+
bot.registerCommand({
192+
aliases: ['resolved', 'resolve', 'close', 'closed', 'done'],
193+
description: 'Help System: Mark a post as resolved',
194+
async listener(msg) {
195+
changeStatus(msg, true);
196+
},
197+
});
198+
199+
bot.registerCommand({
200+
aliases: ['reopen', 'open', 'unresolved', 'unresolve'],
201+
description: 'Help System: Reopen a resolved post',
202+
async listener(msg) {
203+
changeStatus(msg, false);
204+
},
205+
});
206+
207+
bot.client.on('messageReactionAdd', async reaction => {
208+
const message = reaction.message;
209+
const thread = await message.channel.fetch();
210+
if (!isHelpThread(thread)) {
211+
return;
212+
}
213+
const initial = await thread.fetchStarterMessage();
214+
if (initial?.id !== message.id) return;
215+
const tag = forumChannel.availableTags.find(
216+
t =>
217+
t.emoji &&
218+
!t.moderated &&
219+
t.emoji.id === reaction.emoji.id &&
220+
t.emoji.name === reaction.emoji.name,
221+
);
222+
if (!tag) return;
223+
if (thread.appliedTags.length < MAX_TAG_COUNT) {
224+
await thread.setAppliedTags([...thread.appliedTags, tag.id]);
225+
}
226+
await reaction.remove();
227+
});
228+
229+
async function changeStatus(msg: Message, resolved: boolean) {
230+
const thread = msg.channel;
231+
if (!isHelpThread(thread)) {
232+
return sendWithMessageOwnership(
233+
msg,
234+
':warning: Can only be run in a help post',
235+
);
236+
}
237+
238+
const threadData = await getHelpThread(thread.id);
239+
const isAsker = msg.author.id === threadData.ownerId;
240+
const isTrusted = bot.isTrusted(msg);
241+
242+
if (!isAsker && !isTrusted) {
243+
return sendWithMessageOwnership(
244+
msg,
245+
':warning: Only the asker can change the status of a help post',
246+
);
247+
}
248+
249+
await setStatus(thread, resolved ? resolvedTag : openTag);
250+
await msg.react('✅');
251+
252+
if (resolved && !isAsker) {
253+
await thread.send(helperResolve(thread.ownerId!, msg.author.id));
254+
}
255+
}
256+
160257
bot.registerAdminCommand({
161258
aliases: ['htgh'],
162259
async listener(msg) {
163-
if (!bot.isMod(msg.member)) return;
164260
if (
165261
msg.channel.id !== howToGetHelpChannel &&
166262
msg.channel.id !== howToGiveHelpChannel
@@ -170,9 +266,10 @@ export async function helpForumModule(bot: Bot) {
170266
(await msg.channel.messages.fetch()).forEach(x => x.delete());
171267
const message =
172268
msg.channel.id === howToGetHelpChannel
173-
? postGuidelines
269+
? postGuidelines(false)
174270
: howToGiveHelp;
175-
msg.channel.send(message);
271+
// Force a blank line at the beginning of the message for compact-mode users
272+
msg.channel.send(`** **\n` + message.trim());
176273
},
177274
});
178275

@@ -202,6 +299,22 @@ export async function helpForumModule(bot: Bot) {
202299
channel.parent?.id === forumChannel.id
203300
);
204301
}
302+
303+
function getTag(channel: ForumChannel, name: string) {
304+
const tag = channel.availableTags.find(x => x.name === name);
305+
if (!tag) throw new Error(`Could not find tag ${name}`);
306+
return tag.id;
307+
}
308+
309+
async function setStatus(thread: ThreadChannel, tag: string) {
310+
let tags = thread.appliedTags.filter(
311+
x => x !== openTag && x !== resolvedTag,
312+
);
313+
if (tags.length === MAX_TAG_COUNT) {
314+
tags = tags.slice(0, -1);
315+
}
316+
await thread.setAppliedTags([tag, ...tags]);
317+
}
205318
}
206319

207320
function listify(text: string) {

0 commit comments

Comments
 (0)