/pm (/w) with names, #userid, SteamIDs, @me, @aim, @random, @randomt and @randomct, /r to reply, and /block, /blocklist, /unblock. Lines use CS2-Tags' rank prefixes and colours: >[[M] Kalus -> me] message. Gagged players can't send; blocks stop everyone but staff. Messages are kept in CS2-SimpleAdmin's database while History is on (css_whisper_history).
97 lines
3.7 KiB
C#
97 lines
3.7 KiB
C#
using System.Reflection;
|
|
using System.Text.Json;
|
|
using CounterStrikeSharp.API.Core;
|
|
using CounterStrikeSharp.API.Modules.Admin;
|
|
using CounterStrikeSharp.API.Modules.Utils;
|
|
|
|
namespace Whisper;
|
|
|
|
// Reads CS2-Tags' tags.json so private messages show the same rank prefix and colours as chat, and
|
|
// picks a player's tag the way CS2-Tags does: their SteamID64 first, then the first "#group" or
|
|
// "@permission" key they match in file order, then "everyone". Reread when the file changes.
|
|
public sealed class Tags(string path, Action<string> warn)
|
|
{
|
|
public sealed record Tag(string Prefix, string NickColor, string MessageColor);
|
|
|
|
private List<KeyValuePair<string, Tag>> _tags = [];
|
|
private DateTime _loadedWrite;
|
|
|
|
public Tag? Find(CCSPlayerController player)
|
|
{
|
|
Refresh();
|
|
if (player.IsBot || player.IsHLTV || player.AuthorizedSteamID == null)
|
|
return null;
|
|
|
|
var steamid = player.AuthorizedSteamID.SteamId64.ToString();
|
|
foreach (var (key, tag) in _tags)
|
|
if (key == steamid)
|
|
return tag;
|
|
foreach (var (key, tag) in _tags)
|
|
{
|
|
if (key.StartsWith('#') && AdminManager.PlayerInGroup(player, key)) return tag;
|
|
if (key.StartsWith('@') && AdminManager.PlayerHasPermissions(player, key)) return tag;
|
|
}
|
|
foreach (var (key, tag) in _tags)
|
|
if (key == "everyone")
|
|
return tag;
|
|
return null;
|
|
}
|
|
|
|
private void Refresh()
|
|
{
|
|
DateTime written;
|
|
try
|
|
{
|
|
written = File.GetLastWriteTimeUtc(path);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return;
|
|
}
|
|
// GetLastWriteTimeUtc gives 1601 for a missing file.
|
|
if (written == _loadedWrite || written.Year < 1700)
|
|
{
|
|
if (written.Year < 1700 && _tags.Count > 0)
|
|
_tags = [];
|
|
_loadedWrite = written;
|
|
return;
|
|
}
|
|
_loadedWrite = written;
|
|
try
|
|
{
|
|
using var doc = JsonDocument.Parse(File.ReadAllText(path),
|
|
new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true });
|
|
var tags = new List<KeyValuePair<string, Tag>>();
|
|
if (doc.RootElement.TryGetProperty("tags", out var section))
|
|
foreach (var entry in section.EnumerateObject())
|
|
tags.Add(new(entry.Name, new Tag(Str(entry.Value, "prefix"), Str(entry.Value, "nick_color"), Str(entry.Value, "message_color"))));
|
|
_tags = tags;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Keep the tags from before, as CS2-Tags does with a broken file.
|
|
warn($"Couldn't read {path}: {ex.Message}; keeping the previous tags");
|
|
}
|
|
}
|
|
|
|
private static string Str(JsonElement e, string name) =>
|
|
e.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.String ? v.GetString() ?? "" : "";
|
|
|
|
public static string TeamColor(int team) => team switch
|
|
{
|
|
(int)CsTeam.Terrorist => ChatColors.Gold.ToString(),
|
|
(int)CsTeam.CounterTerrorist => ChatColors.Blue.ToString(),
|
|
_ => "",
|
|
};
|
|
|
|
// Colour tags in config strings only, never in names or messages: {Red}, {Default}, {TEAMCOLOR}.
|
|
public static string Colors(string text, int team = 0)
|
|
{
|
|
if (!text.Contains('{'))
|
|
return text;
|
|
foreach (var field in typeof(ChatColors).GetFields(BindingFlags.Public | BindingFlags.Static))
|
|
if (field.FieldType == typeof(char))
|
|
text = text.Replace($"{{{field.Name}}}", field.GetValue(null)!.ToString(), StringComparison.OrdinalIgnoreCase);
|
|
return text.Replace("{TEAMCOLOR}", TeamColor(team), StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
}
|