/// <summary> /// Executes the specified command asynchronously. /// </summary> /// <param name="c">The command argument information.</param> /// <returns><c>true</c> if the command was successfully executed, otherwise <c>false</c>.</returns> /// <remarks>Helpers.GetArgVal(c, 1) if specified: user to check</remarks> public async Task <bool> ExecAsync(Cmd c) { StatusMessage = c.Args.Length > ((c.FromIrc) ? 2 : 1) ? string.Format("^5{0}'s^7 user level is: ^5[{1}]", Helpers.GetArgVal(c, 1), _users.GetUserLevel(Helpers.GetArgVal(c, 1))) : string.Format("^5{0}'s^7 user level is: ^5[{1}]", c.FromUser, _users.GetUserLevel(c.FromUser)); await SendServerSay(c, StatusMessage); return(true); }
/// <summary> /// Checks whether sufficient time has elapsed since the user last issued a command. /// </summary> /// <param name="user">The user.</param> /// <returns><c>true</c> if sufficient time has elapsed, otherwise <c>false</c>.</returns> private bool SufficientTimeElapsed(string user) { if (!Helpers.KeyExists(user, _playerCommandTime) || _users.GetUserLevel(user) >= UserLevel.Admin) { return(true); } var cfg = _cfgHandler.ReadConfiguration(); return(_playerCommandTime[user] .AddSeconds(cfg.CoreOptions.requiredTimeBetweenCommands) < DateTime.Now); }
/// <summary> /// Verifies the user's registration date and kicks the user if the requirement is not met. /// </summary> /// <param name="user">The user.</param> /// <param name="regDate">The user's registration date.</param> private async Task VerifyUserDate(string user, DateTime regDate) { if (regDate == default(DateTime)) { return; } if (_userDb.GetUserLevel(user) >= UserLevel.SuperUser) { return; } if ((DateTime.Now - regDate).TotalDays < MinimumDaysRequired) { if (_configHandler.ReadConfiguration().AccountDateOptions.showKickSoonMessage) { await _sst.QlCommands.QlCmdSay(string.Format( "^3[=> KICK SOON]: ^1{0}^7 (QL account date:^1 {1}^7)'s account is too new and does not meet the limit of ^2{2} ^7days", user, regDate.ToString("d"), MinimumDaysRequired), false); } // Inform the user as a courtesy await _sst.QlCommands.QlCmdDelayedTell( string.Format( "^3You will be kicked because your account is too new (^1{0}^3) and doesn't meet this server's limit of ^1{1}^3 days.", regDate.ToString("d"), MinimumDaysRequired), user, _kickTellDelaySecs); await _sst.QlCommands.CustCmdDelayedKickban(user, (_kickDelaySecs + 2)); Log.Write(string.Format( "Player {0}'s account is newer than minimum of {1} days that is required. Date created: {2}. Will attempt to kick player.", user, MinimumDaysRequired, regDate.ToString("G", DateTimeFormatInfo.InvariantInfo)), _logClassType, _logPrefix); } }
/// <summary> /// Executes the specified command asynchronously. /// </summary> /// <param name="c">The command argument information.</param> /// <returns><c>true</c> if the command was successfully executed, otherwise <c>false</c>.</returns> public async Task <bool> ExecAsync(Cmd c) { var todelUserLevel = _users.GetUserLevel(Helpers.GetArgVal(c, 1)); var result = _users.DeleteUserFromDb(Helpers.GetArgVal(c, 1), c.FromUser, _users.GetUserLevel(c.FromUser)); if (result == UserDbResult.Success) { StatusMessage = string.Format("^2[SUCCESS]^7 Removed user^2 {0}^7 from the^2 [{1}] ^7group.", Helpers.GetArgVal(c, 1), todelUserLevel); await SendServerSay(c, StatusMessage); // UI: reflect changes _sst.UserInterface.RefreshCurrentSstUsersDataSource(); // de-op if (!_sst.IsMonitoringServer) { return(true); } if (!_sst.ServerInfo.CurrentPlayers.ContainsKey(Helpers.GetArgVal(c, 1))) { return(true); } var id = _sst.ServerEventProcessor.GetPlayerId(Helpers.GetArgVal(c, 1)); if (id != -1) { // doesn't matter if not opped, since QL shows no error message await _sst.QlCommands.SendToQlAsync(string.Format("deop {0}", id), false); } return(true); } StatusMessage = string.Format( "^1[ERROR]^3 Unable to remove user^1 {0}^3 from the ^1[{1}]^3 group. Code:^1 {2}", Helpers.GetArgVal(c, 1), todelUserLevel, result); await SendServerTell(c, StatusMessage); return(false); }
/// <summary> /// Executes the specified command asynchronously. /// </summary> /// <param name="c">The command argument information.</param> /// <returns><c>true</c> if the command was successfully executed, otherwise <c>false</c>.</returns> public async Task <bool> ExecAsync(Cmd c) { if (!Helpers.GetArgVal(c, 2).Equals("1") && !Helpers.GetArgVal(c, 2).Equals("2") && !Helpers.GetArgVal(c, 2).Equals("3")) { await DisplayArgLengthError(c); return(false); } if ((Helpers.GetArgVal(c, 2).Equals("3"))) { var userLevel = IsIrcOwner(c) ? UserLevel.Owner : _users.GetUserLevel(c.FromUser); if (userLevel != UserLevel.Owner) { StatusMessage = "^1[ERROR]^3 Only owners can add admins."; await SendServerTell(c, StatusMessage); Log.Write(string.Format("Non-owner {0} attempted to add an admin from {1} Ignoring.", c.FromUser, ((c.FromIrc) ? "IRC." : "in-game.")), _logClassType, _logPrefix); return(false); } } var date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); var result = _users.AddUserToDb(Helpers.GetArgVal(c, 1), (UserLevel)Convert.ToInt32(Helpers.GetArgVal(c, 2)), c.FromUser, date); if (result == UserDbResult.Success) { StatusMessage = string.Format("^2[SUCCESS]^7 Added user^2 {0} ^7to the ^2[{1}] ^7group.", Helpers.GetArgVal(c, 1), (UserLevel)Convert.ToInt32(Helpers.GetArgVal(c, 2))); await SendServerSay(c, StatusMessage); // UI: reflect changes _sst.UserInterface.RefreshCurrentSstUsersDataSource(); // Auto-op if necessary if ((UserLevel)Convert.ToInt32(Helpers.GetArgVal(c, 2)) > UserLevel.SuperUser) { await _sst.ServerEventProcessor.AutoOpActiveAdmin(Helpers.GetArgVal(c, 1)); } return(true); } StatusMessage = string.Format( "^1[ERROR]^3 Unable to add user ^1{0}^3 to the ^1[{1}] ^3group. Code:^1 {2}", Helpers.GetArgVal(c, 1), (UserLevel)Convert.ToInt32(Helpers.GetArgVal(c, 2)), result); await SendServerTell(c, StatusMessage); return(false); }
/// <summary> /// Evaluates the ban addition command and executes it if all parameters are correct. /// </summary> /// <param name="c">The command argument information.</param> /// <returns> /// <c>true</c> if the ban addition evaluation successfully passed, otherwise <c>false</c>. /// </returns> private async Task <bool> EvalBanAddition(Cmd c) { // !timeban add user # scale if (c.Args.Length != (c.FromIrc ? 6 : 5)) { StatusMessage = string.Format( "^1[ERROR]^3 Usage: {0}{1} <add> <name> <time> <scale> - name is without clan, time is a number," + " scale: secs, mins, hours, days, months, or years.", CommandList.GameCommandPrefix, ((c.FromIrc) ? (string.Format("{0} {1}", c.CmdName, c.Args[1])) : c.CmdName)); await SendServerTell(c, StatusMessage); return(false); } double time; if (!double.TryParse(Helpers.GetArgVal(c, 3), out time) || time <= 0) { StatusMessage = "^1[ERROR]^3 Time must be a positive number!"; await SendServerTell(c, StatusMessage); return(false); } if (time > int.MaxValue) { // Just compare to int type's max size as in case of month and year it has to be // converted to int and can't be double anyway StatusMessage = "^1[ERROR]^3 Time is too large!"; await SendServerTell(c, StatusMessage); return(false); } var validScale = Helpers.ValidTimeScales.Contains(Helpers.GetArgVal(c, 4)); if (!validScale) { StatusMessage = "^1[ERROR]^3 Scale must be: secs, mins, hours, days, months, OR years"; await SendServerTell(c, StatusMessage); return(false); } if (_userDb.GetUserLevel(Helpers.GetArgVal(c, 2)) >= UserLevel.Admin) { StatusMessage = "^1[ERROR]^3 Users with user level admin or higher may not be time-banned!"; await SendServerTell(c, StatusMessage); return(false); } return(await AddBan(c)); }
/// <summary> /// Determines whether the vote is an attempted clientkick of an administrator. /// </summary> /// <param name="details">The vote details.</param> /// <returns> /// <c>true</c> if the attempted clientkick is a kick of an administrator, otherwise <c>false</c>. /// </returns> private bool IsAdminClientKickAttempt(Match details) { var type = details.Groups["votetype"].Value; var votearg = details.Groups["votearg"].Value; if (!type.Equals("clientkick", StringComparison.InvariantCultureIgnoreCase)) { return(false); } int id; if (!int.TryParse(votearg, out id)) { return(false); } var user = _sst.ServerEventProcessor.GetPlayerNameFromId(id); if (string.IsNullOrEmpty(user)) { return(false); } return(_users.GetUserLevel(user) >= UserLevel.Admin); }
/// <summary> /// Processes the early quit. /// </summary> /// <param name="player">The player.</param> /// <param name="doublePenalty"> /// if set to <c>true</c> double the penalty for particularly egregious early quits (i.e. /// during countdown). /// </param> private async Task ProcessEarlyQuit(string player, bool doublePenalty) { if (_usersDb.GetUserLevel(player) >= UserLevel.SuperUser) { Log.Write(string.Format( "Player {0} quit early, but will not be evaluated for early quitting due to excluded userlevel", player), _logClassType, _logPrefix); return; } if (_quitsDb.UserExistsInDb(player)) { _quitsDb.IncrementUserQuitCount(player, doublePenalty); // UI: reflect changes _sst.UserInterface.RefreshCurrentQuittersDataSource(); } else { _quitsDb.AddUserToDb(player, doublePenalty); // UI: reflect changes _sst.UserInterface.RefreshCurrentQuittersDataSource(); } var qCount = await EvaluateUserQuitCount(player); if (doublePenalty) { await _sst.QlCommands.QlCmdSay( string.Format( "^3{0}'s^7 penalty was doubled for unbalancing teams during match start!", player), false); Log.Write(string.Format("Active player {0} left during count-down. Penalty will be doubled.", player), _logClassType, _logPrefix); } // Only show the log msg if we're not banning the user this time (ban msg is shown in // that case) if (qCount < _maxQuitsAllowed) { await _sst.QlCommands.QlCmdSay( string.Format("^5[EARLYQUIT]^7 Early quit detected and logged for player ^3{0}", player), false); } }
/// <summary> /// Executes the specified command asynchronously. /// </summary> /// <param name="c">The command argument information.</param> /// <returns><c>true</c> if the command was successfully executed, otherwise <c>false</c>.</returns> public async Task <bool> ExecAsync(Cmd c) { var userDb = new DbUsers(); var senderLevel = userDb.GetUserLevel(c.FromUser); var senderLevelName = Enum.GetName(typeof(UserLevel), senderLevel); var cmds = new StringBuilder(); foreach (var cmd in _sst.CommandProcessor.Commands.Where(cmd => cmd.Value.UserLevel <= senderLevel)) { cmds.Append(string.Format("^3{0}^5{1} ", CommandList.GameCommandPrefix, cmd.Key)); } StatusMessage = string.Format( "^7Your user level - ^3{0}^7 - has access to these commands: {1}," + " ^7More help @ ^3sst.syncore.org^7, or ^3#sst^7 on QuakeNet.", (senderLevelName ?? "NONE"), cmds.ToString().TrimEnd(' ')); await SendServerTell(c, StatusMessage); return(true); }
/// <summary> /// Automatically gives operator status using QL's internal op system to the specified /// player who is currently on the server and is an SST user with userlevel Admin or higher. /// </summary> public async Task AutoOpActiveAdmin(string player) { if (!_sst.IsMonitoringServer) { return; } var cfgHandler = new ConfigHandler(); var cfg = cfgHandler.ReadConfiguration(); if (!cfg.CoreOptions.autoOpAdmins) { return; } var userDb = new DbUsers(); var userLevel = userDb.GetUserLevel(player); if (userLevel <= UserLevel.SuperUser) { return; } if (!_sst.ServerInfo.CurrentPlayers.ContainsKey(player)) { return; } var id = GetPlayerId(player); if (id == -1) { return; } await _sst.QlCommands.SendToQlAsync(string.Format("op {0}", id), false); Log.Write(string.Format("Auto-opping {0} (user level: {1})", player, userLevel), _logClassType, _logPrefix); }
/// <summary> /// Executes the specified command. /// </summary> /// <param name="c">The cmd args.</param> /// <returns> /// <c>true</c> if the command was successfully executed, otherwise returns <c>false</c>. /// </returns> public bool Exec(Cmd c) { if (_sst.ServerInfo.CurrentPlayers.Count == 0) { _irc.SendIrcMessage(_irc.IrcSettings.ircChannel, "My server has no players at this time."); } else { var sb = new StringBuilder(); foreach (var player in _sst.ServerInfo.CurrentPlayers) { sb.Append(string.Format("\u0003\u0002{0}\u0002 ({1}), ", player.Key, _usersDb.GetUserLevel(player.Key))); } _irc.SendIrcMessage(_irc.IrcSettings.ircChannel, string.Format("\u0003My server's current players have the following access levels: {0}", sb.ToString().TrimEnd(',', ' '))); } return(true); }
/// <summary> /// Executes the specified command asynchronously. /// </summary> /// <param name="c">The command argument information.</param> public async Task <bool> ExecAsync(Cmd c) { if (!_sst.Mod.Pickup.Active) { StatusMessage = string.Format( "^1[ERROR]^3 Pickup module is not active. An admin must first load it with:^7 {0}{1} {2}", CommandList.GameCommandPrefix, ((c.FromIrc) ? (string.Format("{0} {1}", IrcCommandList.IrcCmdQl, CommandList.CmdModule)) : CommandList.CmdModule), ModuleCmd.PickupArg); await SendServerTell(c, StatusMessage); Log.Write( string.Format( "{0} attempted {1} command from {2}, but {3} module is not loaded. Ignoring.", c.FromUser, c.CmdName, ((c.FromIrc) ? "from IRC" : "from in-game"), ModuleCmd.PickupArg), _logClassType, _logPrefix); return(false); } if (!Helpers.GetArgVal(c, 1).Equals("reset") && !Helpers.GetArgVal(c, 1).Equals("start") && !Helpers.GetArgVal(c, 1).Equals("stop") && !Helpers.GetArgVal(c, 1).Equals("unban") && !Helpers.GetArgVal(c, 1).Equals("help")) { await DisplayArgLengthError(c); return(false); } // These arguments to the the pickup command require elevated privileges. if (Helpers.GetArgVal(c, 1).Equals("reset") || Helpers.GetArgVal(c, 1).Equals("start") || Helpers.GetArgVal(c, 1).Equals("stop") || Helpers.GetArgVal(c, 1).Equals("unban")) { var userLevel = IsIrcOwner(c) ? UserLevel.Owner : _userDb.GetUserLevel(c.FromUser); if (userLevel < UserLevel.SuperUser) { await DisplayInsufficientAccessError(c); Log.Write( string.Format( "{0} attempted to use a {1} command with args that require a higher user level than {0} has. Ignoring.", c.FromUser, c.CmdName), _logClassType, _logPrefix); return(false); } } if (Helpers.GetArgVal(c, 1).Equals("reset")) { return(await _sst.Mod.Pickup.Manager.EvalPickupReset(c)); } if (Helpers.GetArgVal(c, 1).Equals("start")) { return(await _sst.Mod.Pickup.Manager.EvalPickupStart(c)); } if (Helpers.GetArgVal(c, 1).Equals("stop")) { return(await _sst.Mod.Pickup.Manager.EvalPickupStop(c)); } if (Helpers.GetArgVal(c, 1).Equals("unban")) { return(await _sst.Mod.Pickup.Manager.EvalPickupUnban(c)); } if (Helpers.GetArgVal(c, 1).Equals("help")) { await DisplayPickupHelp(c); return(true); } return(false); }
/// <summary> /// Executes the specified command asynchronously. /// </summary> /// <param name="c">The command argument information.</param> /// <remarks>Helpers.GetArgVal(c, 1) if specified: user to check</remarks> public async Task <bool> ExecAsync(Cmd c) { // Must be a team gametype that is supported by QLRanks if (!_sst.ServerInfo.IsQlRanksGameType()) { StatusMessage = "^1[ERROR]^3 Team balancing is not available on this server!"; // send to everyone (/say; success) await SendServerSay(c, StatusMessage); Log.Write(string.Format( "{0} attempted to request team balance suggestion, but server is running game type" + " unsupported by QLRanks. Ignoring.", c.FromUser), _logClassType, _logPrefix); return(false); } // Disable this command if the pickup module is active if (_sst.Mod.Pickup.Active) { // send to everyone (/say; success) await SendServerSay(c, StatusMessage); StatusMessage = "^1[ERROR]^3 Team balancing is unavailable when pickup module is active!"; Log.Write(string.Format( "{0} attempted to request team balance suggestion, but balancing is unavailable when" + " pickup module is active. Ignoring.", c.FromUser), _logClassType, _logPrefix); return(false); } await _sst.QlCommands.QlCmdSay("^2[TEAMBALANCE]^7 Please wait.... Calculating most even teams.", false); // Verify the teams (TEST) await _sst.QlCommands.QlCmdDelayedConfigStrings(1, 2); var blueTeam = _sst.ServerInfo.GetTeam(Team.Blue); var redTeam = _sst.ServerInfo.GetTeam(Team.Red); if ((blueTeam.Count + redTeam.Count) % 2 != 0) { StatusMessage = "^1[ERROR]^3 Teams can only be suggested if there are a total even number of red and blue players!"; // send to everyone (/say; success) await SendServerSay(c, StatusMessage); Log.Write(string.Format( "{0} attempted to request team balance suggestion, but teams are uneven. Red ({1}), Blue ({2}), Rem ({3}). Ignoring.", c.FromUser, blueTeam.Count, redTeam.Count, ((blueTeam.Count + redTeam.Count) % 2)), _logClassType, _logPrefix); return(false); } if ((blueTeam.Count + redTeam.Count) < 4) { StatusMessage = "^1[ERROR]^3 There must be at least 4 total players for the team suggestion!"; // send to everyone (/say; success) await SendServerSay(c, StatusMessage); Log.Write(string.Format( "{0} attempted to request team balance suggestion, but there are fewer than 4 players. Ignoring.", c.FromUser), _logClassType, _logPrefix); return(false); } if (_sst.VoteManager.IsTeamSuggestionVotePending) { StatusMessage = "^1[ERROR]^3 A team balance vote is already pending!"; // send to everyone (/say; success) await SendServerSay(c, StatusMessage); Log.Write(string.Format( "{0} attempted to request team balance suggestion, but balance suggestion vote is already pending. Ignoring.", c.FromUser), _logClassType, _logPrefix); return(false); } // SuperUser or higher... Force the vote if ((c.Args.Length == (c.FromIrc ? 3 : 2)) && (Helpers.GetArgVal(c, 1).Equals("force", StringComparison.InvariantCultureIgnoreCase))) { var userLevel = IsIrcOwner(c) ? UserLevel.Owner : _users.GetUserLevel(c.FromUser); if (userLevel < UserLevel.SuperUser) { StatusMessage = "^1[ERROR]^7 You do not have permission to use that command."; Log.Write(string.Format( "{0} tried to force team balance suggestion, but has an insufficient access level. Ignoring.", c.FromUser), _logClassType, _logPrefix); return(false); } Log.Write(string.Format("Player {0} with sufficient access level {1} forced balanced teams from {2}.", c.FromUser, Enum.GetName(typeof(UserLevel), userLevel), (c.FromIrc ? "IRC" : "in-game")), _logClassType, _logPrefix); await InitiateBalance(redTeam, blueTeam, true); return(true); } await InitiateBalance(redTeam, blueTeam, false); return(true); }
/// <summary> /// Kicks the player if player does not meet the server's elo requirements. /// </summary> /// <param name="player">The player.</param> private async Task KickPlayerIfEloNotMet(string player) { // Module values have(n't) been set? var hasMaxEloSpecified = MaximumRequiredElo != 0; var hasMinEloSpecified = MinimumRequiredElo != 0; if (!hasMinEloSpecified) { return; } // Elo limits don't apply to SuperUsers or higher if (_users.GetUserLevel(player) >= UserLevel.SuperUser) { return; } // Can't kick ourselves, though QL doesn't allow it anyway, don't show kick msg. if (player.Equals(_sst.AccountName, StringComparison.InvariantCultureIgnoreCase)) { return; } // Get Elo for the current gametype var playerElo = QlRanksHelper.GetEloForGameType(_sst.ServerInfo, player); // Player Elo is either invalid or we're not in a QLRanks-supported gametype. Abort. if (playerElo == 0) { return; } // Handle minimum if (playerElo < MinimumRequiredElo) { Log.Write( string.Format("{0}'s {1} Elo is less than minimum required ({2})...Will kick player shortly.", player, GameType.ToString().ToUpper(), MinimumRequiredElo), _logClassType, _logPrefix); if (_configHandler.ReadConfiguration().EloLimitOptions.showKickSoonMessage) { await _sst.QlCommands.QlCmdSay( string.Format( "^3[=> KICK SOON]: ^1{0}^7 ({1} Elo:^1 {2}^7) does not meet this server's Elo requirements. Min:^2 {3} {4}", player, GameType.ToString().ToUpper(), playerElo, MinimumRequiredElo, hasMaxEloSpecified ? string.Format("^7Max:^1 {0}", MaximumRequiredElo) : ""), false); } // Wait before notifying and kicking, so user's screen doesn't freeze on "awaiting // snapshot", especially when connecting await _sst.QlCommands.QlCmdDelayedTell( string.Format( "^3You will be kicked because your {0} QLRanks Elo (^1{1}^3) doesn't meet this server's requirements. Min: ^1{2} {3}", GameType.ToString().ToUpper(), playerElo, MinimumRequiredElo, hasMaxEloSpecified ? string.Format("^3Max:^1 {0}", MaximumRequiredElo) : ""), player, _kickTellDelaySecs); await _sst.QlCommands.CustCmdDelayedKickban(player, (_kickDelaySecs + 2)); return; } // Handle range if (!hasMaxEloSpecified) { return; } if (playerElo <= MaximumRequiredElo) { return; } Log.Write( string.Format("{0}'s {1} Elo is greater than maximum allowed ({2})...Will kick player shortly.", player, GameType.ToString().ToUpper(), MaximumRequiredElo), _logClassType, _logPrefix); if (_configHandler.ReadConfiguration().EloLimitOptions.showKickSoonMessage) { await _sst.QlCommands.QlCmdSay( string.Format( "^3[=> KICK SOON]: ^1{0}^7 ({1} Elo:^1 {2}^7) does not meet this server's Elo requirements. Min:^2 {3} ^7Max:^1 {4}", player, GameType.ToString().ToUpper(), playerElo, MinimumRequiredElo, MaximumRequiredElo), false); } await _sst.QlCommands.QlCmdDelayedTell( string.Format( "^3You will be kicked because your {0} QLRanks Elo (^1{1}^3) doesn't meet this server's requirements. Min: ^1{2}^3 Max: ^1{3}", GameType.ToString().ToUpper(), playerElo, MinimumRequiredElo, MaximumRequiredElo), player, _kickTellDelaySecs); await _sst.QlCommands.CustCmdDelayedKickban(player, (_kickDelaySecs + 2)); }