Skip to content

Commit 4e680e5

Browse files
authored
Merge branch 'master' into master
2 parents 6986ac1 + f58e811 commit 4e680e5

File tree

6 files changed

+114
-28
lines changed

6 files changed

+114
-28
lines changed

.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ AUTOROLE=MSG_ID:ROLE_ID:EMOJI:AUTOREMOVE
77

88
DATABASE_URL="localhost:5432/tsc-bot"
99

10+
# Role given to trusted members, not full moderators, but can use some commands which
11+
# are not given to all server members.
12+
TRUSTED_ROLE_ID=
13+
14+
# Channel ID to direct users toward for an explanation of the help system.
15+
ASK_HELP_CHANNEL=
16+
1017
ASK_CATEGORY=
1118
ONGOING_CATEGORY=
1219
DORMANT_CATEGORY=

src/env.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ export const categories = {
2424

2525
export const askCooldownRoleId = process.env.ASK_COOLDOWN_ROLE!;
2626

27+
export const trustedRoleId = process.env.TRUSTED_ROLE_ID!;
28+
export const askHelpChannelId = process.env.ASK_HELP_CHANNEL!;
29+
2730
export const channelNames = process.env.CHANNEL_NAMES!.split(',');
2831
export const dormantChannelTimeout = parseInt(
2932
process.env.DORMANT_CHANNEL_TIMEOUT!,

src/modules/helpchan.ts

Lines changed: 78 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import {
2121
channelNames,
2222
dormantChannelTimeout,
2323
dormantChannelLoop,
24+
askHelpChannelId,
2425
} from '../env';
26+
import { isTrustedMember } from '../util/inhibitors';
2527

2628
export class HelpChanModule extends Module {
2729
constructor(client: CookiecordClient) {
@@ -103,13 +105,8 @@ export class HelpChanModule extends Module {
103105

104106
this.busyChannels.add(msg.channel.id);
105107

106-
const helpUser = new HelpUser();
107-
helpUser.userId = msg.author.id;
108-
helpUser.channelId = msg.channel.id;
109-
await helpUser.save();
110-
111108
await msg.pin();
112-
await msg.member.roles.add(askCooldownRoleId);
109+
await this.addCooldown(msg.member, msg.channel);
113110
await this.moveChannel(msg.channel, categories.ongoing);
114111

115112
await this.ensureAskChannels(msg.guild);
@@ -149,7 +146,7 @@ export class HelpChanModule extends Module {
149146
}
150147

151148
const owner = await HelpUser.findOne({
152-
where: { channelId: msg.channel.id },
149+
channelId: msg.channel.id,
153150
});
154151

155152
if (
@@ -219,7 +216,7 @@ export class HelpChanModule extends Module {
219216
await Promise.all(pinned.map(msg => msg.unpin()));
220217

221218
const helpUser = await HelpUser.findOne({
222-
where: { channelId: channel.id },
219+
channelId: channel.id,
223220
});
224221
if (helpUser) {
225222
const member = await channel.guild.members.fetch({
@@ -255,12 +252,19 @@ export class HelpChanModule extends Module {
255252
}
256253
}
257254

258-
@command()
259-
async cooldown(msg: Message, @optional user?: GuildMember) {
260-
console.log('Cooldown', msg.content);
261-
if (!msg.guild) return;
255+
private async addCooldown(member: GuildMember, channel: TextChannel) {
256+
await member.roles.add(askCooldownRoleId);
257+
const helpUser = new HelpUser();
258+
helpUser.userId = member.user.id;
259+
helpUser.channelId = channel.id;
260+
await helpUser.save();
261+
}
262262

263-
const guildTarget = await msg.guild.members.fetch(user ?? msg.author);
263+
@command({ inhibitors: [CommonInhibitors.guildsOnly] })
264+
async cooldown(msg: Message, @optional member?: GuildMember) {
265+
const guildTarget = await msg.guild!.members.fetch(
266+
member ?? msg.author,
267+
);
264268

265269
if (!guildTarget) return;
266270

@@ -272,19 +276,13 @@ export class HelpChanModule extends Module {
272276
}
273277

274278
const helpUser = await HelpUser.findOne({
275-
where: { userId: guildTarget.id },
279+
userId: guildTarget.id,
276280
});
277281

278282
if (helpUser) {
279-
const channel = msg.guild.channels.resolve(helpUser.channelId);
280-
// If we don't have a channel, just remove the cooldown. This should
281-
// only happen if someone deletes a help channel.
282-
if (channel) {
283-
await msg.channel.send(
284-
`${guildTarget.displayName} has an active help channel: ${channel.name}`,
285-
);
286-
return;
287-
}
283+
return msg.channel.send(
284+
`${guildTarget.displayName} has an active help channel: <#${helpUser.channelId}>`,
285+
);
288286
}
289287

290288
await guildTarget.roles.remove(askCooldownRoleId);
@@ -293,6 +291,63 @@ export class HelpChanModule extends Module {
293291
);
294292
}
295293

294+
@command({ inhibitors: [isTrustedMember] })
295+
async claim(msg: Message, member: GuildMember) {
296+
const helpUser = await HelpUser.findOne({
297+
userId: member.id,
298+
});
299+
if (helpUser) {
300+
return msg.channel.send(
301+
`${member.displayName} already has an open help channel: <#${helpUser.channelId}>`,
302+
);
303+
}
304+
305+
const channelMessages = await msg.channel.messages.fetch({ limit: 50 });
306+
const questionMessages = channelMessages.filter(
307+
questionMsg =>
308+
questionMsg.author.id === member.id &&
309+
questionMsg.id !== msg.id,
310+
);
311+
312+
const msgContent = questionMessages
313+
.array()
314+
.slice(0, 10)
315+
.map(msg => msg.content)
316+
.reverse()
317+
.join('\n')
318+
.slice(0, 2000);
319+
320+
const claimedChannel = msg.guild!.channels.cache.find(
321+
channel =>
322+
channel.type === 'text' &&
323+
channel.parentID == categories.ask &&
324+
channel.name.startsWith(this.CHANNEL_PREFIX) &&
325+
!this.busyChannels.has(channel.id),
326+
) as TextChannel | undefined;
327+
328+
if (!claimedChannel) {
329+
return msg.channel.send(
330+
':warning: failed to claim a help channel, no available channel.',
331+
);
332+
}
333+
334+
this.busyChannels.add(claimedChannel.id);
335+
const toPin = await claimedChannel.send(
336+
new MessageEmbed()
337+
.setAuthor(member.displayName, member.user.displayAvatarURL())
338+
.setDescription(msgContent),
339+
);
340+
await toPin.pin();
341+
await this.addCooldown(member, claimedChannel);
342+
await this.moveChannel(claimedChannel, categories.ongoing);
343+
await claimedChannel.send(
344+
`${member.user} this channel has been claimed for your question. Please review <#${askHelpChannelId}> for how to get help.`,
345+
);
346+
await this.ensureAskChannels(msg.guild!);
347+
348+
this.busyChannels.delete(claimedChannel.id);
349+
}
350+
296351
// Commands to fix race conditions
297352
@command({
298353
inhibitors: [CommonInhibitors.hasGuildPermission('MANAGE_MESSAGES')],

src/modules/rep.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ export class RepModule extends Module {
1919

2020
MAX_REP = 3;
2121

22-
// all messages have to be fully lowercase
23-
THANKS_REGEX = /(?:thanks|thx|cheers|thanx|ty|tks|tkx)\b/i;
22+
THANKS_REGEX = /\b(?:thanks|thx|cheers|thanx|thnks|ty|tks|tkx)\b/i;
2423

2524
async getOrMakeUser(user: User) {
2625
let ru = await RepUser.findOne(
@@ -158,6 +157,7 @@ export class RepModule extends Module {
158157
description: 'See who has the most reputation',
159158
})
160159
async leaderboard(msg: Message) {
160+
const topEmojis = [':first_place:', ':second_place:', ':third_place:'];
161161
const data = ((await RepGive.createQueryBuilder('give')
162162
.select(['give.to', 'COUNT(*)'])
163163
.groupBy('give.to')
@@ -173,8 +173,10 @@ export class RepModule extends Module {
173173
.setDescription(
174174
data
175175
.map(
176-
x =>
177-
`:white_small_square: **<@${x.id}>** with **${x.count}** points.`,
176+
(x, index) =>
177+
`${
178+
topEmojis[index] || ':white_small_square:'
179+
} **<@${x.id}>** with **${x.count}** points.`,
178180
)
179181
.join('\n'),
180182
);

src/modules/twoslash.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,11 @@ export class TwoslashModule extends Module {
105105
linkWithHats += ' '.repeat(spaceBefore);
106106
linkWithHats += '^'.repeat(e.length || 0);
107107
});
108-
resultLines.push(linkWithHats);
108+
109+
if (linkWithHats.length > 0) {
110+
resultLines.push('//' + linkWithHats.substr(2));
111+
}
112+
109113
resultLines.push(...errors);
110114
}
111115

src/util/inhibitors.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Inhibitor } from 'cookiecord';
2+
import { trustedRoleId } from '../env';
3+
4+
export const isTrustedMember: Inhibitor = async (msg, client) => {
5+
if (!msg.guild || !msg.member || msg.channel.type !== 'text') {
6+
return ":warning: you can't use that command here.";
7+
}
8+
9+
if (
10+
!msg.member.roles.cache.has(trustedRoleId) &&
11+
!msg.member.hasPermission('MANAGE_MESSAGES')
12+
) {
13+
return ":warning: you don't have permission to use that command.";
14+
}
15+
};

0 commit comments

Comments
 (0)