1
1
import { default as CookiecordClient , Module , listener } from 'cookiecord' ;
2
- import { Message } from 'discord.js' ;
2
+ import { Message , Snowflake , User } from 'discord.js' ;
3
3
import { rulesChannelId } from '../env' ;
4
4
5
5
// Most job posts are in this format:
6
6
// > [FOR HIRE][REMOTE][SOMETHING ELSE]
7
7
// > Hi, I'm ponyman6000. Hire me!
8
8
const jobPostRegex = / ^ (?: \[ [ A - Z ] + \] ) { 2 , } \n / i;
9
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 ;
21
+
10
22
export class ModModule extends Module {
11
23
constructor ( client : CookiecordClient ) {
12
24
super ( client ) ;
@@ -20,4 +32,43 @@ export class ModModule extends Module {
20
32
`${ msg . author } We don't do job posts here; see <#${ rulesChannelId } >` ,
21
33
) ;
22
34
}
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
+ }
23
66
}
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 ) ;
0 commit comments