SboxSuperuser/Engine/ChatIntegration/ChatCommandIngress.cs

Chat command ingress component. It recognizes chat messages with a configured prefix, authenticates the sender as a player, parses the command into a route, records invalid attempts to audit, and dispatches valid commands to the host command dispatcher.

File Access
using SboxSuperuser.Core.Commands;
using SboxSuperuser.Engine.Audit;
using SboxSuperuser.Engine.Commands;

namespace SboxSuperuser.Engine.ChatIntegration;

internal sealed class ChatCommandIngress : IChatCommandIngress
{
	private readonly ChatCommandPrefix _prefix;
	private readonly HostCommandDispatcher _dispatcher;
	private readonly HostAuditService _audit;
	private readonly Func<bool> _available;

	internal ChatCommandIngress( string root, HostCommandDispatcher dispatcher, HostAuditService audit, Func<bool> available )
	{
		_prefix = new ChatCommandPrefix( root );
		_dispatcher = dispatcher;
		_audit = audit;
		_available = available;
	}

	public bool Owns( string message ) => _prefix.Owns( message );

	public async Task<CommandResult> ExecuteAsync( Func<ActorIdentity> authenticate, string message, Guid requestId, CancellationToken cancellation )
	{
		if ( !_available() || !Owns( message ) || requestId == Guid.Empty )
		{
			return new CommandResult( CommandStatus.Unsupported, "Chat command ingress is unavailable." );
		}
		var actor = authenticate();
		if ( actor is null || actor.Kind != ActorKind.Player )
		{
			return new CommandResult( CommandStatus.Denied, "An authenticated player connection is required." );
		}

		var parsed = _prefix.Parse( message );
		CommandRoute route = null;
		if ( parsed.Error is null )
		{
			try
			{
				route = CommandRoute.Resolve( parsed.Tokens );
			}
			catch ( ArgumentException )
			{
				// Invalid text is never copied to diagnostics or audit arguments.
			}
		}
		if ( route is null )
		{
			await _audit.RecordAsync( new AuditAction( actor, CommandOrigin.Chat, requestId, "command.invalid", DateTimeOffset.UtcNow ),
				CommandStatus.InvalidArguments, cancellation );
			return new CommandResult( CommandStatus.InvalidArguments, $"Malformed or oversized command. Use {_prefix.Value} help for syntax." );
		}

		return await _dispatcher.SubmitAsync( authenticate, CommandOrigin.Chat,
			new CommandRequest( route.Command, route.Arguments, requestId ), cancellation );
	}
}