using CS2_SimpleAdmin.Database; using CS2_SimpleAdminApi; using Dapper; using Microsoft.Extensions.Logging; namespace CS2_SimpleAdmin.Managers; internal class MuteManager(IDatabaseProvider? databaseProvider) { /// /// Adds a mute entry for a specified player with detailed information. /// /// Player to be muted. /// Admin issuing the mute; null if issued from console. /// Reason for muting the player. /// Duration of the mute in minutes. Zero means permanent mute. /// Mute type: 0 = GAG, 1 = MUTE, 2 = SILENCE. /// Mute ID if successfully added, otherwise null. public async Task MutePlayer(PlayerInfo player, PlayerInfo? issuer, string reason, int time = 0, int type = 0) { if (databaseProvider == null) return null; var now = Time.ActualDateTime(); var futureTime = now.AddMinutes(time); var muteType = type switch { 1 => "MUTE", 2 => "SILENCE", _ => "GAG" }; try { await using var connection = await databaseProvider.CreateConnectionAsync(); var sql = databaseProvider.GetAddMuteQuery(true); var muteId = await connection.ExecuteScalarAsync(sql, new { playerSteamid = player.SteamId.SteamId64, playerName = player.Name, adminSteamid = issuer?.SteamId.SteamId64 ?? 0, adminName = issuer?.Name ?? CS2_SimpleAdmin._localizer?["sa_console"] ?? "Console", muteReason = reason, duration = time, ends = futureTime, created = now, type = muteType, serverid = CS2_SimpleAdmin.ServerId }); return muteId; } catch (Exception ex) { CS2_SimpleAdmin._logger?.LogError(ex.Message); return null; } } /// /// Adds a mute entry for a offline player identified by their SteamID. /// /// SteamID64 of the player to mute. /// Admin issuing the mute; can be null if from console. /// Reason for the mute. /// Mute duration in minutes; 0 for permanent. /// Mute type: 0 = GAG, 1 = MUTE, 2 = SILENCE. /// Mute ID if successful, otherwise null. public async Task AddMuteBySteamid(ulong playerSteamId, PlayerInfo? issuer, string reason, int time = 0, int type = 0) { if (databaseProvider == null) return null; var now = Time.ActualDateTime(); var futureTime = now.AddMinutes(time); var muteType = type switch { 1 => "MUTE", 2 => "SILENCE", _ => "GAG" }; try { await using var connection = await databaseProvider.CreateConnectionAsync(); var sql = databaseProvider.GetAddMuteQuery(false); var muteId = await connection.ExecuteScalarAsync(sql, new { playerSteamid = playerSteamId, adminSteamid = issuer?.SteamId.SteamId64 ?? 0, adminName = issuer?.Name ?? CS2_SimpleAdmin._localizer?["sa_console"] ?? "Console", muteReason = reason, duration = time, ends = futureTime, created = now, type = muteType, serverid = CS2_SimpleAdmin.ServerId }); return muteId; } catch { return null; } } /// /// Checks if a player with the given SteamID currently has any active mutes. /// /// SteamID64 of the player to check. /// List of active mute records; empty list if none or on error. public async Task> IsPlayerMuted(string steamId) { if (databaseProvider == null) return []; if (string.IsNullOrEmpty(steamId)) { return []; } #if DEBUG if (CS2_SimpleAdmin._logger != null) CS2_SimpleAdmin._logger.LogCritical($"IsPlayerMuted for {steamId}"); #endif try { await using var connection = await databaseProvider.CreateConnectionAsync(); var currentTime = Time.ActualDateTime(); var sql = databaseProvider.GetIsMutedQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode); var parameters = new { PlayerSteamID = steamId, CurrentTime = currentTime, serverid = CS2_SimpleAdmin.ServerId }; var activeMutes = (await connection.QueryAsync(sql, parameters)).ToList(); return activeMutes; } catch (Exception) { return []; } } /// /// Retrieves counts of total mutes, gags, and silences for a given player. /// /// Information about the player. /// /// Tuple containing total mutes, total gags, and total silences respectively. /// Returns zeros if no data or on error. /// public async Task<(int TotalMutes, int TotalGags, int TotalSilences)> GetPlayerMutes(PlayerInfo playerInfo) { if (databaseProvider == null) return (0,0,0); try { await using var connection = await databaseProvider.CreateConnectionAsync(); var sql = databaseProvider.GetRetrieveMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode); var result = await connection.QuerySingleAsync<(int TotalMutes, int TotalGags, int TotalSilences)>(sql, new { PlayerSteamID = playerInfo.SteamId.SteamId64, CS2_SimpleAdmin.ServerId }); return result; } catch (Exception) { return (0, 0, 0); } } /// /// Processes a batch of online players to update their mute status and remove expired penalties. /// /// List of tuples containing player SteamID, optional UserID, and slot index. /// Task representing the asynchronous operation. public async Task CheckOnlineModeMutes(List<(ulong SteamID, int? UserId, int Slot)> players) { if (databaseProvider == null) return; try { const int batchSize = 20; await using var connection = await databaseProvider.CreateConnectionAsync(); var sql = databaseProvider.GetUpdateMutePassedQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode); for (var i = 0; i < players.Count; i += batchSize) { var batch = players.Skip(i).Take(batchSize); var parametersList = new List(); foreach (var (steamId, _, _) in batch) { parametersList.Add(new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId }); } await connection.ExecuteAsync(sql, parametersList); } sql = databaseProvider.GetCheckExpiredMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode); foreach (var (steamId, _, slot) in players) { var muteRecords = await connection.QueryAsync(sql, new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId }); foreach (var muteRecord in muteRecords) { DateTime endDateTime = muteRecord.ends; PlayerPenaltyManager.RemovePenaltiesByDateTime(slot, endDateTime); } } } catch { } } /// /// Removes active mutes for players matching the specified pattern. /// /// Pattern to match player names or identifiers. /// SteamID64 of the admin performing the unmute. /// Reason for unmuting the player(s). /// Mute type to remove: 0 = GAG, 1 = MUTE, 2 = SILENCE. /// Task representing the asynchronous operation. public async Task UnmutePlayer(string playerPattern, string adminSteamId, string reason, int type = 0) { if (databaseProvider == null) return; if (playerPattern.Length <= 1) { return; } try { await using var connection = await databaseProvider.CreateConnectionAsync(); var muteType = type switch { 1 => "MUTE", 2 => "SILENCE", _ => "GAG" }; var sqlRetrieveMutes = databaseProvider.GetRetrieveMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode); var mutes = await connection.QueryAsync(sqlRetrieveMutes, new { pattern = playerPattern, muteType, serverid = CS2_SimpleAdmin.ServerId }); var mutesList = mutes as dynamic[] ?? mutes.ToArray(); if (mutesList.Length == 0) return; var sqlAdmin = databaseProvider.GetUnmuteAdminIdQuery(); var sqlInsertUnmute = databaseProvider.GetInsertUnmuteQuery(string.IsNullOrEmpty(reason)); var sqlAdminId = await connection.ExecuteScalarAsync(sqlAdmin, new { adminSteamId }); var adminId = sqlAdminId ?? 0; foreach (var mute in mutesList) { int muteId = mute.id; int? unmuteId = await connection.ExecuteScalarAsync(sqlInsertUnmute, new { muteId, adminId, reason }); var sqlUpdateMute = databaseProvider.GetUpdateMuteStatusQuery(); await connection.ExecuteAsync(sqlUpdateMute, new { unmuteId, muteId }); } } catch (Exception ex) { Console.WriteLine(ex); } } /// /// Expires all old mutes that have passed their duration according to current time. /// /// Task representing the asynchronous expiration operation. public async Task ExpireOldMutes() { if (databaseProvider == null) return; try { await using var connection = await databaseProvider.CreateConnectionAsync(); var sql = databaseProvider.GetExpireMutesQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode, CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode); await connection.ExecuteAsync(sql, new { CurrentTime = Time.ActualDateTime(), serverid = CS2_SimpleAdmin.ServerId }); } catch (Exception) { CS2_SimpleAdmin._logger?.LogCritical("Unable to remove expired mutes"); } } }