Skip to content

Commit c8fb937

Browse files
authored
Merge branch 'master' into less-at-helper-spam
2 parents 6a8709d + a27a172 commit c8fb937

File tree

4 files changed

+88
-16
lines changed

4 files changed

+88
-16
lines changed

src/modules/helpchan.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -406,19 +406,27 @@ export class HelpChanModule extends Module {
406406
else askHelpChannel.send(newMessageText);
407407
}
408408

409+
private async getChannelMember(channel: TextChannel) {
410+
return (
411+
HelpUser.findOneOrFail({
412+
channelId: channel.id,
413+
})
414+
.then(helpUser => {
415+
// Clear from the cache in case they've left the server
416+
channel.guild.members.cache.delete(helpUser.userId);
417+
return channel.guild.members.fetch({
418+
user: helpUser.userId,
419+
});
420+
})
421+
// Do nothing, member left the guild
422+
.catch(() => undefined)
423+
);
424+
}
425+
409426
private async markChannelAsDormant(channel: TextChannel) {
410427
this.busyChannels.add(channel.id);
411428

412-
const memberPromise = HelpUser.findOneOrFail({
413-
channelId: channel.id,
414-
})
415-
.then(helpUser =>
416-
channel.guild.members.fetch({
417-
user: helpUser.userId,
418-
}),
419-
)
420-
// Do nothing, member left the guild
421-
.catch(() => undefined);
429+
const memberPromise = this.getChannelMember(channel);
422430

423431
const pinnedPromise = channel.messages.fetchPinned();
424432

@@ -453,13 +461,23 @@ export class HelpChanModule extends Module {
453461

454462
private async checkDormantPossibilities() {
455463
for (const channel of this.getOngoingChannels()) {
456-
const messages = await channel.messages.fetch();
464+
const [messages, member] = await Promise.all([
465+
channel.messages.fetch(),
466+
this.getChannelMember(channel),
467+
]);
457468

458469
const diff =
459470
Date.now() - (messages.first()?.createdAt.getTime() ?? 0);
460471

461-
if (diff > dormantChannelTimeout)
472+
if (!member) {
473+
await channel.send(
474+
'Asker has left the server, closing channel...',
475+
);
476+
}
477+
if (!member || diff > dormantChannelTimeout) {
462478
await this.markChannelAsDormant(channel);
479+
continue;
480+
}
463481
}
464482
}
465483

src/modules/mod.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
import { default as CookiecordClient, Module, listener } from 'cookiecord';
2-
import { Message } from 'discord.js';
2+
import { Message, Snowflake, User } from 'discord.js';
33
import { rulesChannelId } from '../env';
44

55
// Most job posts are in this format:
66
// > [FOR HIRE][REMOTE][SOMETHING ELSE]
77
// > Hi, I'm ponyman6000. Hire me!
8-
const jobPostRegex = /^(?:\[[A-Z ]+\]){2,}\n/i;
8+
const jobPostRegex = /^(?:\[[A-Z /-]+\]\s*){2,}\n/i;
9+
10+
const SPAM_CHANNEL_THRESHOLD = 3;
11+
const SPAM_MAX_TIME = 5000;
12+
13+
interface RecentMessageInfo {
14+
author: User;
15+
firstPost: Date;
16+
channels: Set<Snowflake>;
17+
messages: Message[];
18+
}
19+
const recentMessages = new Map<string, RecentMessageInfo>();
20+
const CLEANUP_RECENT_MESSAGES_MAP_INTERVAL = 10000;
921

1022
export class ModModule extends Module {
1123
constructor(client: CookiecordClient) {
@@ -20,4 +32,43 @@ export class ModModule extends Module {
2032
`${msg.author} We don't do job posts here; see <#${rulesChannelId}>`,
2133
);
2234
}
35+
36+
@listener({ event: 'message' })
37+
async onRepeatedMessage(msg: Message) {
38+
if (!msg.guild || msg.author.id === this.client.user!.id) return;
39+
const messageIdentifier = msg.content.trim().toLowerCase();
40+
if (!messageIdentifier) return;
41+
let recentMessageInfo = recentMessages.get(messageIdentifier);
42+
if (
43+
recentMessageInfo &&
44+
recentMessageInfo.author.id === msg.author.id &&
45+
+recentMessageInfo.firstPost + SPAM_MAX_TIME > Date.now()
46+
) {
47+
recentMessageInfo.channels.add(msg.channel.id);
48+
recentMessageInfo.messages.push(msg);
49+
if (recentMessageInfo.channels.size >= SPAM_CHANNEL_THRESHOLD) {
50+
await Promise.all([
51+
msg.guild.members
52+
.fetch(msg.author)
53+
.then(member => void member.kick('Spam')),
54+
...recentMessageInfo.messages.map(msg => void msg.delete()),
55+
]);
56+
}
57+
} else {
58+
recentMessages.set(messageIdentifier, {
59+
author: msg.author,
60+
firstPost: new Date(),
61+
channels: new Set([msg.channel.id]),
62+
messages: [msg],
63+
});
64+
}
65+
}
2366
}
67+
68+
setInterval(() => {
69+
for (const [messageText, recentMessageInfo] of recentMessages) {
70+
if (+recentMessageInfo.firstPost + SPAM_MAX_TIME < Date.now()) {
71+
recentMessages.delete(messageText);
72+
}
73+
}
74+
}, CLEANUP_RECENT_MESSAGES_MAP_INTERVAL);

src/modules/playground.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,10 @@ export class PlaygroundModule extends Module {
101101
const exec = PLAYGROUND_REGEX.exec(msg.content);
102102
if (msg.author.bot || !this.editedLongLink.has(msg.id) || exec) return;
103103
const botMsg = this.editedLongLink.get(msg.id);
104-
await botMsg?.edit('');
104+
// Edit the message to only have the embed and not the "please edit your message" message
105+
await botMsg?.edit('', {
106+
embed: botMsg.embeds[0],
107+
});
105108
this.editedLongLink.delete(msg.id);
106109
}
107110
}

src/util/codeBlocks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { getReferencedMessage } from './getReferencedMessage';
44

55
const CODEBLOCK_REGEX = /```(?:ts|typescript)?\n([\s\S]+)```/;
66

7-
export const PLAYGROUND_REGEX = /https?:\/\/(?:www\.)?(?:typescriptlang|staging-typescript)\.org\/(?:play|dev\/bug-workbench)(?:\/index\.html)?\/?(\??(?:\w+=[^\s#&]+)?(?:\&\w+=[^\s#&]+)*)#code\/([\w\-+_]+={0,4})/;
7+
export const PLAYGROUND_REGEX = /https?:\/\/(?:www\.)?(?:typescriptlang|staging-typescript)\.org\/(?:play|dev\/bug-workbench)(?:\/index\.html)?\/?(\??(?:\w+=[^\s#&]+)?(?:\&\w+=[^\s#&]+)*)#code\/([\w\-%+_]+={0,4})/;
88

99
export async function findCode(message: Message, ignoreLinks = false) {
1010
const codeInMessage = findCodeInMessage(message, ignoreLinks);

0 commit comments

Comments
 (0)