GroupVerify/GroupVerifyApi/GroupVerifyBridge.cs
Astra 1f61958099 GroupVerify 0.2.0: Steam group / override verification with an API for other plugins
Players are verified when they're in the configured Steam group (public memberslistxml,
refetched every 10 minutes, kept on failure) or have an unexpired override row in
gv_verified, written by the web panel. Settings and data live in CS2-SimpleAdmin's MySQL
database (gv_verified, gv_settings, gv_migrations), reached through its API.

- css_gv_group <groupId64|0>, css_gv_enable, css_gv_disable (@css/root): verification runs
  only when enabled with a group set.
- css_verify <target>: why a player is or isn't verified. Admin and above (by immunity from
  SimpleAdmin's groups) plus Guardian, unless admin_overrides.json says otherwise.
- groupverify:api (shared/GroupVerifyApi): IsVerified, GetStatus, IsActive and
  OnVerifiedChanged. The API object lives in the shared assembly, so it survives hot
  reloads of GroupVerify.
2026-10-02 15:37:47 +01:00

86 lines
2.9 KiB
C#

using System.ComponentModel;
using CounterStrikeSharp.API.Core;
namespace GroupVerifyApi;
/// <summary>What GroupVerify itself implements behind the API. Not for other plugins.</summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public interface IGroupVerifyBackend
{
/// <summary>See <see cref="IGroupVerifyApi.IsActive"/>.</summary>
bool IsActive { get; }
/// <summary>See <see cref="IGroupVerifyApi.GetStatus"/>.</summary>
VerifyStatus GetStatus(ulong steamId64);
}
/// <summary>
/// The one API object, living in this shared assembly so it outlives GroupVerify reloads: CSS keeps
/// the first capability supplier registered forever, and subscribers keep their handlers here.
/// GroupVerify attaches its backend on load and detaches it on unload. Not for other plugins.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class GroupVerifyBridge : IGroupVerifyApi
{
/// <summary>The instance GroupVerify registers as the capability.</summary>
public static GroupVerifyBridge Instance { get; } = new();
private IGroupVerifyBackend? _backend;
private GroupVerifyBridge()
{
}
/// <inheritdoc />
public event Action<CCSPlayerController, VerifyStatus>? OnVerifiedChanged;
/// <inheritdoc />
public bool IsActive => _backend?.IsActive ?? false;
/// <inheritdoc />
public bool IsVerified(CCSPlayerController player)
{
if (player is not { IsValid: true, IsBot: false, IsHLTV: false })
return false;
var steamId = player.AuthorizedSteamID?.SteamId64;
return steamId != null && IsVerified(steamId.Value);
}
/// <inheritdoc />
public bool IsVerified(ulong steamId64) => GetStatus(steamId64).Verified;
/// <inheritdoc />
public VerifyStatus GetStatus(ulong steamId64) =>
_backend is { IsActive: true } backend ? backend.GetStatus(steamId64) : default;
/// <summary>Called by GroupVerify on load.</summary>
public void Attach(IGroupVerifyBackend backend) => _backend = backend;
/// <summary>Called by GroupVerify on unload; ignored if a newer backend has attached since.</summary>
public void Detach(IGroupVerifyBackend backend)
{
if (ReferenceEquals(_backend, backend))
_backend = null;
}
/// <summary>
/// Called by GroupVerify on the game thread. Each handler runs on its own, so one that throws
/// doesn't stop the others; the exceptions are returned for GroupVerify to log.
/// </summary>
public List<Exception> RaiseVerifiedChanged(CCSPlayerController player, VerifyStatus status)
{
var errors = new List<Exception>();
foreach (var handler in OnVerifiedChanged?.GetInvocationList() ?? [])
{
try
{
((Action<CCSPlayerController, VerifyStatus>)handler)(player, status);
}
catch (Exception ex)
{
errors.Add(ex);
}
}
return errors;
}
}