Constructor and shared helpers for the administration command module in the SboxSuperuser package. Builds a CommandRegistrationScope against the CommandRegistry, registers discovery, access, module and documentation commands plus optional moderation, messaging, vote, content and alt-account commands, and supplies argument descriptors (player, page), owner/dedicated-console authorization, and a paged row formatter for command output.
using SboxSuperuser.Engine.Audit;
using SboxSuperuser.Engine.Authority;
namespace SboxSuperuser.Engine.Commands;
internal sealed partial class AdministrationCommands : IDisposable
{
private const string Module = "superuser.administration";
private readonly HostPolicyService _policy;
private readonly HostAuditService _audit;
private readonly CommandRegistry _registry;
private readonly HostCommandDispatcher _dispatcher;
private readonly Func<CommandResult> _diagnostics;
private readonly CommandRegistrationScope _scope;
private readonly Modules.ModuleManager _modules;
private readonly Moderation.HostModerationService _moderation;
private readonly Func<CommandContext, string, string, bool, Task<CommandResult>> _disconnect;
private readonly Func<CommandContext, string, string, CommandResult> _notifyWarning;
private readonly Action<string> _announce;
private readonly Func<string,string> _playerName;
private readonly Core.Messages.MessageCatalog _messages;
private readonly Func<string, bool> _mapAllowed;
internal AdministrationCommands( HostPolicyService policy, HostAuditService audit, CommandRegistry registry,
HostCommandDispatcher dispatcher, Func<CommandResult> diagnostics, Modules.ModuleManager modules = null,
Moderation.HostModerationService moderation = null, Func<CommandContext, string, string, bool, Task<CommandResult>> disconnect = null,
Func<CommandContext, string, string, CommandResult> notifyWarning = null, Action<string> announce = null, Func<string,string> playerName = null,
Messaging.IPlayerMessenger messenger = null, Messages.HostMessageService messages = null,
Votes.VoteSession votes = null, Func<string, CommandResult> changeMap = null, Content.HostContentService content = null,
Alts.HostAltService alts = null, Content.IContentCatalog contentCatalog = null )
{
_policy = policy;
_audit = audit;
_registry = registry;
_dispatcher = dispatcher;
_diagnostics = diagnostics;
_modules = modules;
_moderation = moderation;
_disconnect = disconnect;
_notifyWarning = notifyWarning;
_announce = announce;
_playerName = playerName ?? (id => id);
_messages = messages?.Catalog ?? new Core.Messages.MessageCatalog();
_mapAllowed = content is null ? null : map => content.IsAllowed( Core.Content.ContentBanList.Map, map );
_scope = new CommandRegistrationScope( registry, Module, managed: true );
RegisterDiscovery();
RegisterAccess();
RegisterModules();
if ( _moderation is not null ) RegisterModeration();
if ( messenger is not null ) RegisterMessages( messenger );
if ( messages is not null ) RegisterMessageTemplates( messages );
if ( votes is not null ) RegisterVotes( votes, changeMap );
if ( content is not null ) RegisterContent( content, contentCatalog );
if ( alts is not null ) RegisterAlts( alts );
RegisterDocumentation();
_scope.Commit();
}
private void Register( string name, string description, string permission, CommandArgumentDescriptor[] arguments,
CommandHandler handler, bool mutation = false, bool auditQuery = false, bool lifecycleControl = false )
{
var descriptor = new CommandDescriptor( name, description, permission, Module, arguments,
executionPolicy: mutation ? CommandExecutionPolicy.Mutation : CommandExecutionPolicy.ReadOnly );
if ( lifecycleControl )
{
_scope.RegisterLifecycleControl( descriptor, handler );
}
else if ( auditQuery )
{
_scope.RegisterAuditQuery( descriptor, handler );
}
else
{
_scope.RegisterCommand( descriptor, handler );
}
}
private static CommandArgumentDescriptor Text( string name, string help, bool optional = false )
=> new( name, help, CommandArgumentKind.String, isOptional: optional );
private static CommandArgumentDescriptor Player() => new( "user", "Exact Steam ID or unique online name", CommandArgumentKind.Player,
targetPolicy: CommandTargetPolicy.AnyPlayer, audit: CommandArgumentAudit.SafeValue );
private static CommandArgumentDescriptor Page() => new( "page", "One-based page number", CommandArgumentKind.Integer,
isOptional: true, minimum: 1, maximum: 10000, audit: CommandArgumentAudit.SafeValue );
private static int PageNumber( CommandArguments arguments ) => arguments.Contains( "page" ) ? (int)arguments.Get<long>( "page" ) : 1;
private static ActorIdentity Authenticate( CommandContext context ) => context.Services is CommandExecutionServices services
? services.Authenticate() : null;
private bool IsOwner( CommandContext context )
{
var actor = Authenticate( context );
return actor is not null && _policy.IsUsable && (actor.Kind == ActorKind.DedicatedConsole
|| actor.Kind == ActorKind.Player && _policy.IsOwner( actor.SubjectId ));
}
private static CommandResult Denied() => new( CommandStatus.Denied, "Current owner or dedicated local console authority is required." );
private static CommandResult Rows( IEnumerable<string> rows, int page, string label )
{
var selected = rows.Skip( (page - 1) * 8 ).Take( 9 ).ToArray();
var fields = selected.Take( 8 ).Select( (row, index) => new KeyValuePair<string, string>( "row" + (index + 1), Clip( row, 880 ) ) )
.ToDictionary( pair => pair.Key, pair => pair.Value );
fields[ "page" ] = page.ToString( System.Globalization.CultureInfo.InvariantCulture );
fields[ "more" ] = (selected.Length > 8).ToString();
return new CommandResult( CommandStatus.Success, label, fields );
}
private static string Clip( string text, int maximum ) => text.Length <= maximum ? text : text.Substring( 0, maximum - 1 ) + "…";
public void Dispose() => _scope.Dispose();
}