@@ -23,6 +23,7 @@ import {
2323 dormantChannelTimeout ,
2424 dormantChannelLoop ,
2525 askHelpChannelId ,
26+ ongoingEmptyTimeout ,
2627} from '../env' ;
2728import { isTrustedMember } from '../util/inhibitors' ;
2829
@@ -66,6 +67,7 @@ export class HelpChanModule extends Module {
6667 . setDescription ( DORMANT_MESSAGE ) ;
6768
6869 busyChannels : Set < string > = new Set ( ) ; // a lock to eliminate race conditions
70+ ongoingEmptyTimeouts : Map < string , NodeJS . Timeout > = new Map ( ) ; // a lock used to prevent multiple timeouts running on the same channel
6971
7072 private getChannelName ( guild : Guild ) {
7173 const takenChannelNames = guild . channels . cache
@@ -81,13 +83,67 @@ export class HelpChanModule extends Module {
8183 return `${ this . CHANNEL_PREFIX } ${ decidedChannel } ` ;
8284 }
8385
86+ private getOngoingChannels ( ) {
87+ return this . client . channels . cache
88+ . filter (
89+ channel =>
90+ ( channel as TextChannel ) . parentID === categories . ongoing ,
91+ )
92+ . array ( ) as TextChannel [ ] ;
93+ }
94+
8495 @listener ( { event : 'ready' } )
8596 async startDormantLoop ( ) {
8697 setInterval ( ( ) => {
8798 this . checkDormantPossibilities ( ) ;
8899 } , dormantChannelLoop ) ;
89100 }
90101
102+ @listener ( { event : 'ready' } )
103+ async initialCheckEmptyOngoing ( ) {
104+ for ( const channel of this . getOngoingChannels ( ) ) {
105+ if ( await this . checkEmptyOngoing ( channel ) ) {
106+ await this . startEmptyTimeout ( channel ) ;
107+ }
108+ }
109+ }
110+
111+ // Utility function used to check if there are no messages in an ongoing channel, meaning the bot
112+ // is the most recent message. This will be caused if somebody deletes their message after they
113+ // claim a channel.
114+ async checkEmptyOngoing ( channel : TextChannel ) {
115+ const messages = await channel . messages . fetch ( ) ;
116+
117+ return messages . first ( ) ?. author . id === this . client . user ?. id ;
118+ }
119+
120+ async startEmptyTimeout ( channel : TextChannel ) {
121+ const existingTimeout = this . ongoingEmptyTimeouts . get ( channel . id ) ;
122+ if ( existingTimeout ) clearTimeout ( existingTimeout ) ;
123+
124+ const timeout = setTimeout ( async ( ) => {
125+ this . ongoingEmptyTimeouts . delete ( channel . id ) ;
126+
127+ if ( await this . checkEmptyOngoing ( channel ) ) {
128+ await this . markChannelAsDormant ( channel ) ;
129+ }
130+ } , ongoingEmptyTimeout ) ;
131+
132+ this . ongoingEmptyTimeouts . set ( channel . id , timeout ) ;
133+ }
134+
135+ @listener ( { event : 'messageDelete' } )
136+ async onMessageDeleted ( msg : Message ) {
137+ if (
138+ msg . channel . type !== 'text' ||
139+ ! msg . channel . parentID ||
140+ msg . channel . parentID !== categories . ongoing
141+ )
142+ return ;
143+
144+ await this . startEmptyTimeout ( msg . channel ) ;
145+ }
146+
91147 async moveChannel ( channel : TextChannel , category : string ) {
92148 const parent = channel . guild . channels . resolve ( category ) ;
93149 if ( parent == null ) return ;
@@ -248,20 +304,14 @@ export class HelpChanModule extends Module {
248304 }
249305
250306 private async checkDormantPossibilities ( ) {
251- const ongoingChannels = this . client . channels . cache . filter ( channel => {
252- if ( channel . type === 'dm' ) return false ;
253-
254- return ( channel as TextChannel ) . parentID === categories . ongoing ;
255- } ) ;
256-
257- for ( const channel of ongoingChannels . array ( ) ) {
258- const messages = await ( channel as TextChannel ) . messages . fetch ( ) ;
307+ for ( const channel of this . getOngoingChannels ( ) ) {
308+ const messages = await channel . messages . fetch ( ) ;
259309
260310 const diff =
261- ( Date . now ( ) - messages . array ( ) [ 0 ] . createdAt . getTime ( ) ) / 1000 ;
311+ Date . now ( ) - ( messages . first ( ) ? .createdAt . getTime ( ) ?? 0 ) ;
262312
263313 if ( diff > dormantChannelTimeout )
264- await this . markChannelAsDormant ( channel as TextChannel ) ;
314+ await this . markChannelAsDormant ( channel ) ;
265315 }
266316 }
267317
0 commit comments