2020-10-22 16:06:28 -05:00
import BasePlugin from './base-plugin.js' ;
2020-10-25 18:31:46 -05:00
import Logger from 'core/logger' ;
2020-10-22 16:06:28 -05:00
export default class AutoKickAFK extends BasePlugin {
static get description ( ) {
return 'The <code>AutoKickAFK</code> plugin will automatically kick players that are not in a squad after a specified ammount of time.' ;
}
static get defaultEnabled ( ) {
return true ;
}
static get optionsSpecification ( ) {
return {
2020-10-25 18:18:24 -05:00
warningMessage : {
2020-10-22 16:06:28 -05:00
required : false ,
2020-10-25 18:18:24 -05:00
description : 'Message SquadJS will send to players warning them they will be kicked' ,
default : 'Join a squad, you are are unassigned and will be kicked'
2020-10-22 16:06:28 -05:00
} ,
2020-10-25 18:18:24 -05:00
kickMessage : {
2020-10-22 19:22:34 -05:00
required : false ,
2020-10-25 18:18:24 -05:00
description : 'Message to send to players when they are kicked' ,
default : 'Unassigned - automatically removed'
} ,
2020-10-27 16:31:05 -05:00
frequencyOfWarnings : {
2020-10-25 18:18:24 -05:00
required : false ,
description : 'How often in seconds should we warn the player about being AFK?' ,
default : 30
} ,
afkTimer : {
required : false ,
description : 'How long in minutes to wait before a player that is AFK is kicked' ,
default : 6
2020-10-22 19:22:34 -05:00
} ,
2020-10-27 16:31:05 -05:00
playerThreshold : {
2020-10-22 19:22:34 -05:00
required : false ,
2020-10-27 16:31:05 -05:00
description :
'Player count required for Auto Kick to start kicking players to disable set to -1 to disable' ,
2020-10-22 19:22:34 -05:00
default : 93
} ,
2020-10-27 16:31:05 -05:00
queueThreshold : {
2020-10-22 19:22:34 -05:00
required : false ,
2020-10-27 16:31:05 -05:00
description :
'The number of players in the queue before Auto Kick starts kicking players set to -1 to disable' ,
2020-10-22 19:22:34 -05:00
default : - 1
2020-10-25 19:24:28 -05:00
} ,
2020-10-27 16:31:05 -05:00
roundStartDelay : {
2020-10-25 19:24:28 -05:00
required : false ,
2020-10-27 16:31:05 -05:00
description :
'Time delay in minutes from start of the round before auto AFK starts kicking again' ,
2020-10-25 19:24:28 -05:00
default : 15
2020-10-27 16:48:09 -05:00
} ,
ignoreAdmins : {
2020-10-22 19:22:34 -05:00
required : false ,
description : 'Whether or not admins will be auto kicked for being unassigned' ,
default : false
2020-10-27 16:48:09 -05:00
}
2020-10-22 16:06:28 -05:00
} ;
}
constructor ( server , options ) {
super ( ) ;
2020-10-27 16:31:05 -05:00
this . kickTimeout = options . afkTimer * 60 * 1000 ;
2020-10-25 18:18:24 -05:00
this . warningInterval = options . frequencyOfWarnings * 1000 ;
2020-10-25 19:24:28 -05:00
this . gracePeriod = options . roundStartDelay * 60 * 1000 ;
this . betweenRounds = false ;
2020-10-25 18:18:24 -05:00
this . trackedPlayers = { } ;
2020-10-25 19:24:28 -05:00
server . on ( 'NEW_GAME' , async ( info ) => {
this . betweenRounds = true ;
updateTrackingList ( ) ;
2020-10-27 16:31:05 -05:00
setTimeout ( async ( ) => {
2020-10-25 19:24:28 -05:00
this . betweenRounds = false ;
} , this . gracePeriod ) ;
} ) ;
2020-10-27 16:31:05 -05:00
server . on ( 'PLAYER_SQUAD_CHANGE' , async ( player ) => {
if ( player . steamID in this . trackedPlayers && player . squadID !== null ) {
2020-10-25 19:24:28 -05:00
untrackPlayer ( player . steamID ) ;
}
} ) ;
2020-10-22 16:06:28 -05:00
2020-10-27 16:31:05 -05:00
const runConditions = ( ) => {
2020-10-27 18:02:55 -05:00
// return true; // force run for testing
2020-10-27 16:31:05 -05:00
return (
! this . betweenRounds ||
options . playerCountThreshold > 0 < server . players . count ||
options . queueThreshold > 0 < server . publicQueue + server . reserveQueue
) ;
} ;
2020-10-22 16:06:28 -05:00
2020-10-27 16:31:05 -05:00
const updateTrackingList = async ( ) => {
if ( ! runConditions ( ) ) {
2020-10-25 18:18:24 -05:00
// clear all tracked players if run conditions are not met.
2020-10-27 16:31:05 -05:00
for ( const steamID of Object . keys ( this . trackedPlayers ) ) untrackPlayer ( steamID ) ;
2020-10-22 19:22:34 -05:00
return ;
2020-10-27 16:31:05 -05:00
}
2020-10-25 19:24:28 -05:00
2020-10-27 16:31:05 -05:00
// await server.updatePlayerList(); //possibly unneeded as updates to player list are already common
2020-10-25 19:24:28 -05:00
2020-10-25 18:18:24 -05:00
// loop through players on server and start tracking players not in a squad
2020-10-22 19:22:34 -05:00
for ( const player of server . players ) {
2020-10-27 16:31:05 -05:00
const isTracked = player . steamID in this . trackedPlayers ;
const isUnassigned = player . squadID === null ;
2020-10-27 16:48:09 -05:00
const isAdmin =
player . steamID in server . admins . map ( ( a ) => a . steamID ) && options . ignoreAdmins ;
2020-10-25 18:18:24 -05:00
2020-10-27 16:31:05 -05:00
if ( isUnassigned && ! isTracked && ! isAdmin )
2020-10-26 02:03:45 -05:00
this . trackedPlayers [ player . steamID ] = trackPlayer ( player ) ; // start tracking player
2020-10-27 16:31:05 -05:00
if ( ! isUnassigned && isTracked ) untrackPlayer ( player . steamID ) ; // tracked player joined a squad remove them (redundant afer addming PLAYER_SQUAD_CHANGE, keeping for now)
2020-10-22 16:06:28 -05:00
}
2020-10-27 16:31:05 -05:00
} ;
const msFormat = ( ms ) => {
const min = Math . floor ( ( ms / 1000 / 60 ) << 0 ) ;
const sec = Math . floor ( ( ms / 1000 ) % 60 ) ;
2020-10-26 02:03:45 -05:00
return ` ${ min } : ${ sec } ` ;
2020-10-27 16:31:05 -05:00
} ;
2020-10-25 18:18:24 -05:00
2020-10-27 16:31:05 -05:00
const trackPlayer = ( player ) => {
2020-10-26 02:03:45 -05:00
Logger . verbose ( 'AutoAFK' , 1 , ` Tracking: ${ player . name } ` ) ;
2020-10-27 16:31:05 -05:00
const tracker = { } ;
tracker . player = player ;
2020-10-27 18:02:55 -05:00
tracker . warnings = 0 ;
2020-10-26 02:03:45 -05:00
tracker . startTime = Date . now ( ) ;
2020-10-27 16:31:05 -05:00
tracker . warnTimerID = setInterval ( async ( ) => {
const timeLeft = msFormat ( this . kickTimeout - ( Date . now ( ) - tracker . startTime ) ) ;
2020-10-26 02:03:45 -05:00
Logger . verbose ( 'AutoAFK' , 1 , ` Warning: ${ player . name } ( ${ timeLeft } ) ` ) ;
server . rcon . warn ( player . steamID , ` ${ options . warningMessage } - ${ timeLeft } ` ) ;
2020-10-27 18:02:55 -05:00
tracker . warnings ++ ;
2020-10-25 18:18:24 -05:00
} , this . warningInterval ) ;
2020-10-27 16:31:05 -05:00
tracker . kickTimerID = setTimeout ( async ( ) => {
2020-10-26 02:03:45 -05:00
Logger . verbose ( 'AutoAFK' , 1 , ` Kicked: ${ player . name } ` ) ;
server . rcon . kick ( player . steamID , options . kickMessage ) ;
untrackPlayer ( player . steamID ) ;
2020-10-25 18:18:24 -05:00
} , this . kickTimeout ) ;
2020-10-26 02:03:45 -05:00
return tracker ;
2020-10-27 16:31:05 -05:00
} ;
2020-10-25 18:18:24 -05:00
2020-10-27 16:31:05 -05:00
const untrackPlayer = ( steamID ) => {
const tracker = this . trackedPlayers [ steamID ] ;
2020-10-27 18:02:55 -05:00
server . emit ( 'PLAYER_AFK_KICKED' , tracker ) ;
2020-10-26 02:03:45 -05:00
Logger . verbose ( 'AutoAFK' , 1 , ` [AutoAFK] unTrack: ${ tracker . player . name } ` ) ;
clearInterval ( tracker . warnTimerID ) ; // clears warning interval
clearTimeout ( tracker . kickTimerID ) ; // clears kick timeout
2020-10-25 18:18:24 -05:00
delete this . trackedPlayers [ steamID ] ;
2020-10-27 16:31:05 -05:00
} ;
2020-10-25 18:18:24 -05:00
2020-10-27 16:31:05 -05:00
setInterval ( updateTrackingList , 1 * 60 * 1000 ) ; // tracking list update loop
2020-10-22 19:22:34 -05:00
2020-10-27 16:31:05 -05:00
// clean up every 20 minutes, removes players no longer on the server that may be stuck in the tracking dict
const cleanupMS = 20 * 60 * 1000 ;
setInterval ( ( ) => {
for ( const steamID of Object . keys ( this . trackedPlayers ) )
if ( ! ( steamID in server . players . map ( ( p ) => p . steamID ) ) ) untrackPlayer ( steamID ) ;
2020-10-22 19:22:34 -05:00
} , cleanupMS ) ;
2020-10-22 16:06:28 -05:00
}
}