Alias support for commands

Hi, I’ve been trying to add aliases to my commands, however the complexity increases when I prefer doing it in my own way, the issue with how it is set up currently, is that I can’t get the function to run if I’m using aliases, as it’s binded to the actual command name.

Any suggestions would be helpful.
Commands handler:

CommandsManager.Commands = {
	commands = {
		Aliases = {['c'] = 'c', ['cmds'] = 'cmds'},
		Description = 'No description!',
		Execute = function(Player, Args)
			local RequiredRank = 'Helper'
			local Command = Args[1]
			local PlayerRank = Player:FindFirstChild('RankNumber')

			if (PlayerRank) then
				if (PlayerRank.Value >= RankManager.Ranks[RequiredRank].Rank)
				then
					CommandsManager.Functions.Alert(Player, table.concat(CommandsManager.Commands, '\n'))
				else
					CommandsManager.Functions.Alert(Player, CommandsManager.Messages.NoPermission)
				end
			else
				CommandsManager.Functions.Alert(Player, CommandsManager.Messages.Error)
			end
		end	
	}
}

Commands runner:

Player.Chatted:Connect(function(Message)
		local Args = Message:split(' ')
		local Command = Args[1]
		local FixedCommand = Command:gsub(CommandsManager.Settings.Prefix, ''):lower()
		
		for Index, Value in pairs(CommandsManager.Commands) do
			local Execute
			if FixedCommand == Index then
				Execute = CommandsManager.Commands[FixedCommand].Execute
				Execute(Player, Args)
				break
			end
		end
	end)

Clearly, I’m able to add aliases if I really wanted to, but it would be way messier, I do prefer this method or something similar to it, but I don’t believe there’s any way around it due to how it’s set up currently.

1 Like

I don’t know why you’d think it’d change much? simply change the table of “Aliases” to something like

Aliases = {'c', 'cmds'}

and use table.find() to check.

Player.Chatted:Connect(function(Message)
		local Args = Message:split(' ')
		local Command = Args[1]
		local FixedCommand = Command:gsub(CommandsManager.Settings.Prefix, ''):lower()
		
		for Index, Value in pairs(CommandsManager.Commands) do
			local Execute
			if FixedCommand == Index or table.find(Value.Aliases, FixedCommand) then
				Execute = CommandsManager.Commands[FixedCommand].Execute
				Execute(Player, Args)
				break
			end
		end
	end)

Sorry, just trying to understand your code for now.

Does this mean that CommandsManager.Commands is a dictionary of strings to commands, where a command is an object with keys Aliases, Description and Execute? So you’ve really got a command just
called “commands”? :sweat_smile: Sorry that’s just a bit confusing to me, I’m sure it makes sense in context.

Assuming this understanding is right: why not just populate the Commands dictionary with additional references to each command for each of it’s aliases? I.e.

local CommandsManager.Commands = {
    commands = {
        Aliases = {"c", "cmds",},
        ...
    },
    kick = {
        Aliases = {"k", "yeet",},
        ...
    }
}

--Populate command names by their keys in the Commands dictionary
for commandName, command in pairs(CommandsManager.Commands) do
    command.Name = commandName
end

--Populate CommandsManager.Commands with additional refs to each command for each command alias
--Warns and ignores on duplicate names/aliases
for commandName, command in pairs(CommandsManager.Commands) do
    for _, alias in ipairs(command.Aliases) do
        if CommandsManager.Commands[alias] then
            warn(("CommandsManager.Commands already has name %s (alias for %s) for command %s, ignoring!"):format(alias, commandName, CommandsManager.Commands[alias].Name))
        else
            CommandsManager.Commands[alias] = command
        end
    end
end

That way you can get a command by either it’s name or an alias like this:

local command = CommandsManager.Commands[daCommandName]

Does this mean that CommandsManager.Commands is a dictionary of strings to commands, where a command is an object with keys Aliases, Description and Execute? So you’ve really got a command just
called “commands”? :sweat_smile: Sorry that’s just a bit confusing to me, I’m sure it makes sense in context.

CommandsManager.Commands holds the commands.

The issue here would be that I can’t call the execute function inside the command because the command name cannot be 2 things, it’s limited to 1, which would be the command name…

Do you mean for this part?

Yeah I can see how that’s an issue. If I were making this, I’d keep two separate structures to solve this. One that’s just an array of commands, and another that’s a dictionary from command name/alias to command. Or you could use a single table and just keep the array and dict parts separate, but that’s not especially readable IMO.

That way you can iterate over each command uniquely, and also look them up by their name/alias in constant time. Although at this point it’s getting kinda complicated, you might want to just have a function for finding a command from it’s alias by searching through commands, like Kaiden suggested

EDIT: Oh, but I don’t think concat even works on dictionaries in the first place?

Although at this point it’s getting kinda complicated, you might want to just have a function for finding a command from it’s alias by searching through commands, like Kaiden suggested

Yes, I’m aware, but another issue will come if I do that.
I would need the aliases to be able to get to the Execute function somehow.

EDIT: Oh, but I don’t think concat even works on dictionaries in the first place?

This was just a test, it serves no functionality at the moment. Just a test command.

CommandsManager.Commands[FixedCommand].Execute won’t support aliases when indexing it like that, so no, it won’t work because it relies the command to be the command’s name, not any alias of it.

I would need to create another command just to add aliases if I want it to work like that, sadly.
Or, if it were possible to assign multiple strings to it, that would work too.

Oh sorry, forgot to change that part. Simply change CommandsManager.Commands[FixedCommand] to Value.

Player.Chatted:Connect(function(Message)
		local Args = Message:split(' ')
		local Command = Args[1]
		local FixedCommand = Command:gsub(CommandsManager.Settings.Prefix, ''):lower()
		
		for Index, Value in pairs(CommandsManager.Commands) do
			local Execute
			if FixedCommand == Index or table.find(Value.Aliases, FixedCommand) then
				Execute = Value.Execute
				Execute(Player, Args)
				break
			end
		end
	end)

Well, no. Execute is outside of the aliases. That wouldn’t work.
If that were to work, I’d have to copy-paste the execute inside the aliases part, which might create another problem if I don’t include the actual command name in there as well.

Value would be the table. If you didn’t notice, Index would be the command name that references the table and Value would be the table itself.

Still, read my edited post above.

You really didn’t read your script again, did you?

You go through the table of all Commands with the for loop.

Index would be commands and Value would be the table that commands equals, being this:

Aliases, Description, AND Execute are all under and defined INSIDE of the table.

That still wouldn’t work, and it doesn’t work like that, unfortunately.

Honestly, just try the script, and don’t say it’s wrong UNTIL you try it. You keep saying it won’t work, what am I supposed to say if you won’t even listen to what I have to say? I’m not gonna go into this and keep responding. It’s quite literally almost 4 am for me and I’m trying to help but you won’t even take into account what I am saying.

Honestly, just try the script, and don’t say it’s wrong UNTIL you try it.

Knew before, and after. Result: Doesn’t work.
Perhaps you could try it yourself before posting? Just a tip, instead of arguing over it.

Alright, if it’s gonna be like that, I tested it for you. And yes, it did work quite well.


Now please, stop trying to fight about it. Life lesson: listen to people and try things out before saying it won’t work.

1 Like

I appreciate the help from you guys, however - it seems like I have found my own solution to this problem.

Why not

for i, cmd in pairs(commandsManager) do
    if table.find(cmd.Aliases, fixedCommand) then
        -- run the command
        break
    end
end

Also instead of using a dictionary just use a simple array:

{"c", "cmds"}

Solid suggestion, and I appreciate it, however - I redid my script and made it less complex, and along the way I found my own solution to the problem.

Thanks anyways!

for i, cmd in pairs(commandsManager) do
if table.find(cmd.Aliases, fixedCommand) then
– run the command
break
end
end

Both yes and no, but not what I prefer.

{“c”, “cmds”}

The issue with this is that it’s a value, and the index would be something like: [1] = ‘c’, [2] = ‘cmds’
The only way to bypass it is by giving it a name so it can be referenced.