SboxSuperuser/Engine/Admin/HostAdminQueries.Alts.cs

Part of the host admin command handler. Answers alt-account queries: a per-player lookup returns linked accounts with evidence labels and IP history, and the list query returns a page of suspected alts with score, alt count and banned linkage. Both are gated by permission checks.

using SboxSuperuser.Core.Alts;
using SboxSuperuser.Core.Moderation;

namespace SboxSuperuser.Engine.Admin;

internal sealed partial class HostAdminQueries
{
	internal const string AltsPermission = "admin.alts.read";
	/// <summary>Seeing raw IP addresses; owners only unless granted.</summary>
	internal const string AddressesPermission = "admin.alts.ips";
	private const int AltPageSize = 20;

	private AdminReply ReadAlts( Guid id, AdminQuery query, Func<string, bool> can )
	{
		if ( !can( AltsPermission ) ) return Failure( id, CommandStatus.Denied, $"Alt-account detection needs {AltsPermission}." );
		if ( _alts is null ) return Failure( id, CommandStatus.Unsupported, "Alt-account detection isn't running." );
		var online = _players()?.Select( player => player.SteamId ).ToHashSet() ?? new HashSet<string>();
		bool Banned( string steamId ) => _moderation.IsUsable && _moderation.CheckRestriction( steamId, ModerationKind.Ban ).Status == CommandStatus.Denied;
		string NameOf( string steamId ) => _alts.Ledger.Find( steamId )?.Names.FirstOrDefault() ?? steamId;
		if ( query.Kind == AdminQueryKind.Alts )
		{
			var links = _alts.Ledger.AltsOf( query.Search );
			return new AdminReply
			{
				RequestId = id, Status = CommandStatus.Success, Message = links.Count == 0 ? "No linked accounts." : $"{links.Count} linked accounts.",
				SteamId = query.Search,
				Addresses = can( AddressesPermission ) ? (_alts.Ledger.Find( query.Search )?.Addresses ?? new List<AltAddress>())
					.Select( address => new AdminAddressRow { Address = address.Address, FirstSeenUtc = address.FirstSeenUtc, LastSeenUtc = address.LastSeenUtc } ).ToArray()
					: Array.Empty<AdminAddressRow>(),
				Alts = links.Select( link => new AdminAltRow
				{
					SteamId = link.SteamId, Name = NameOf( link.SteamId ), Online = online.Contains( link.SteamId ), Banned = Banned( link.SteamId ),
					Evidence = link.Evidence.Select( Label ).ToArray(), LastSeenUtc = link.LastSeenUtc
				} ).ToArray()
			};
		}
		var suspects = _alts.Ledger.Suspects( Banned ).Skip( query.Page * AltPageSize ).Take( AltPageSize + 1 ).ToArray();
		return new AdminReply
		{
			RequestId = id, Status = CommandStatus.Success, Message = "Suspected alt accounts.", HasMore = suspects.Length > AltPageSize,
			Alts = suspects.Take( AltPageSize ).Select( suspect => new AdminAltRow
			{
				SteamId = suspect.SteamId, Name = NameOf( suspect.SteamId ), Online = online.Contains( suspect.SteamId ), Banned = Banned( suspect.SteamId ),
				Score = suspect.Score, AltCount = suspect.Alts.Count, LinkedToBanned = suspect.Alts.Any( link => Banned( link.SteamId ) ),
				LastSeenUtc = _alts.Ledger.Find( suspect.SteamId )?.LastSeenUtc ?? default
			} ).ToArray()
		};
	}

	internal static string Label( AltEvidence evidence ) => evidence switch
	{
		AltEvidence.FamilyShare => "Family share",
		AltEvidence.SameNetwork => "Same IP",
		_ => "Same party"
	};
}