Make BouncerServ commands a tree

For instance, replace "create-network" with "network create".
This commit is contained in:
Simon Ser 2020-03-25 20:58:07 +01:00
parent 04dd9d51f3
commit 448464b0e4
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
2 changed files with 85 additions and 36 deletions

View File

@ -12,10 +12,13 @@ import (
const serviceNick = "BouncerServ" const serviceNick = "BouncerServ"
type serviceCommandSet map[string]*serviceCommand
type serviceCommand struct { type serviceCommand struct {
usage string usage string
desc string desc string
handle func(dc *downstreamConn, params []string) error handle func(dc *downstreamConn, params []string) error
children serviceCommandSet
} }
func sendServicePRIVMSG(dc *downstreamConn, text string) { func sendServicePRIVMSG(dc *downstreamConn, text string) {
@ -33,16 +36,9 @@ func handleServicePRIVMSG(dc *downstreamConn, text string) {
return return
} }
var name string cmd, params, err := serviceCommands.Get(words)
var params []string if err != nil {
if len(words) > 0 { sendServicePRIVMSG(dc, fmt.Sprintf(`error: %v (type "help" for a list of commands)`, err))
name = strings.ToLower(words[0])
params = words[1:]
}
cmd, ok := serviceCommands[name]
if !ok {
sendServicePRIVMSG(dc, fmt.Sprintf(`error: unknown command %q (type "help" for a list of commands)`, name))
return return
} }
@ -51,43 +47,93 @@ func handleServicePRIVMSG(dc *downstreamConn, text string) {
} }
} }
var serviceCommands map[string]serviceCommand func (cmds serviceCommandSet) Get(params []string) (*serviceCommand, []string, error) {
if len(params) == 0 {
return nil, nil, fmt.Errorf("no command specified")
}
name := params[0]
params = params[1:]
cmd, ok := cmds[name]
if !ok {
for k := range cmds {
if !strings.HasPrefix(k, name) {
continue
}
if cmd != nil {
return nil, params, fmt.Errorf("command %q is ambiguous", name)
}
cmd = cmds[k]
}
}
if cmd == nil {
return nil, params, fmt.Errorf("command %q not found", name)
}
if len(params) == 0 || len(cmd.children) == 0 {
return cmd, params, nil
}
return cmd.children.Get(params)
}
var serviceCommands serviceCommandSet
func init() { func init() {
serviceCommands = map[string]serviceCommand{ serviceCommands = serviceCommandSet{
"help": { "help": {
usage: "[command]", usage: "[command]",
desc: "print help message", desc: "print help message",
handle: handleServiceHelp, handle: handleServiceHelp,
}, },
"create-network": { "network": {
children: serviceCommandSet{
"create": {
usage: "-addr <addr> [-name name] [-username username] [-pass pass] [-realname realname] [-nick nick]", usage: "-addr <addr> [-name name] [-username username] [-pass pass] [-realname realname] [-nick nick]",
desc: "add a new network", desc: "add a new network",
handle: handleServiceCreateNetwork, handle: handleServiceCreateNetwork,
}, },
},
},
}
}
func appendServiceCommandSetHelp(cmds serviceCommandSet, prefix []string, l *[]string) {
for name, cmd := range cmds {
words := append(prefix, name)
if len(cmd.children) == 0 {
s := strings.Join(words, " ")
*l = append(*l, s)
} else {
appendServiceCommandSetHelp(cmd.children, words, l)
}
} }
} }
func handleServiceHelp(dc *downstreamConn, params []string) error { func handleServiceHelp(dc *downstreamConn, params []string) error {
if len(params) > 0 { if len(params) > 0 {
name := strings.ToLower(params[0]) cmd, rest, err := serviceCommands.Get(params)
cmd, ok := serviceCommands[name] if err != nil {
if !ok { return err
return fmt.Errorf("unknown command %q", name)
} }
words := params[:len(params)-len(rest)]
text := name if len(cmd.children) > 0 {
var l []string
appendServiceCommandSetHelp(cmd.children, words, &l)
sendServicePRIVMSG(dc, "available commands: "+strings.Join(l, ", "))
} else {
text := strings.Join(words, " ")
if cmd.usage != "" { if cmd.usage != "" {
text += " " + cmd.usage text += " " + cmd.usage
} }
text += ": " + cmd.desc text += ": " + cmd.desc
sendServicePRIVMSG(dc, text) sendServicePRIVMSG(dc, text)
}
} else { } else {
var l []string var l []string
for name := range serviceCommands { appendServiceCommandSetHelp(serviceCommands, nil, &l)
l = append(l, name)
}
sendServicePRIVMSG(dc, "available commands: "+strings.Join(l, ", ")) sendServicePRIVMSG(dc, "available commands: "+strings.Join(l, ", "))
} }
return nil return nil
@ -107,7 +153,7 @@ func handleServiceCreateNetwork(dc *downstreamConn, params []string) error {
return err return err
} }
if *addr == "" { if *addr == "" {
return fmt.Errorf("flag addr is required") return fmt.Errorf("flag -addr is required")
} }
if *nick == "" { if *nick == "" {

View File

@ -71,14 +71,17 @@ The config file has one directive per line.
# IRC SERVICE # IRC SERVICE
soju exposes an IRC service called BouncerServ to manage the bouncer. Commands soju exposes an IRC service called *BouncerServ* to manage the bouncer.
can be sent via regular private messages (_/msg BouncerServ <command> [args...]_). Commands can be sent via regular private messages
(_/msg BouncerServ <command> [args...]_). Commands may be written in full or
abbreviated form, for instance *network* can be abbreviated as *net* or just
*n*.
*help* [command] *help* [command]
Show a list of commands. If _command_ is specified, show a help message for Show a list of commands. If _command_ is specified, show a help message for
the command. the command.
*create-network* *-addr* <addr> [options...] *network create* *-addr* <addr> [options...]
Connect to a new network at _addr_. _-addr_ is mandatory. Other options are: Connect to a new network at _addr_. _-addr_ is mandatory. Other options are:
*-name* <name> *-name* <name>