Files
go-ts/pkg/protocol/commands.go

116 lines
2.3 KiB
Go

package protocol
import (
"strings"
)
// Command Parsing Helpers
func ParseCommand(data []byte) (string, map[string]string) {
s := string(data)
parts := strings.Split(s, " ")
cmd := parts[0]
args := make(map[string]string)
for _, p := range parts[1:] {
kv := strings.SplitN(p, "=", 2)
if len(kv) == 2 {
val := Unescape(kv[1])
args[kv[0]] = val
} else {
args[p] = ""
}
}
return cmd, args
}
// ParseCommands parses response that may contain multiple items separated by pipe (|)
func ParseCommands(data []byte) []*Command {
s := string(data)
// TS3 uses pipe | to separate list items
items := strings.Split(s, "|")
cmds := make([]*Command, 0, len(items))
// First item contains the command name
name, args := ParseCommand([]byte(items[0]))
cmds = append(cmds, &Command{Name: name, Params: args})
// Subsequent items reuse the same command name
for _, item := range items[1:] {
// Hack: Prepend command name to reuse ParseCommand logic
// or better: manually parse args.
// Since ParseCommand splits by space, we can just use "DUMMY " + item
// ensuring we trim properly.
_, itemArgs := ParseCommand([]byte("CMD " + strings.TrimSpace(item)))
cmds = append(cmds, &Command{Name: name, Params: itemArgs})
}
return cmds
}
// Unescape TS3 string
func Unescape(s string) string {
r := strings.NewReplacer(
`\/`, `/`,
`\s`, ` `,
`\p`, `|`,
`\a`, "\a",
`\b`, "\b",
`\f`, "\f",
`\n`, "\n",
`\r`, "\r",
`\t`, "\t",
`\v`, "\v",
`\\`, `\`,
)
return r.Replace(s)
}
func Escape(s string) string {
r := strings.NewReplacer(
`\`, `\\`,
`/`, `\/`,
` `, `\s`,
`|`, `\p`,
"\a", `\a`,
"\b", `\b`,
"\f", `\f`,
"\n", `\n`,
"\r", `\r`,
"\t", `\t`,
"\v", `\v`,
)
return r.Replace(s)
}
// Command represents a TeamSpeak 3 command for building/encoding
type Command struct {
Name string
Params map[string]string
}
func NewCommand(name string) *Command {
return &Command{
Name: name,
Params: make(map[string]string),
}
}
func (c *Command) AddParam(key, value string) {
c.Params[key] = value
}
func (c *Command) Encode() string {
var sb strings.Builder
sb.WriteString(c.Name)
for k, v := range c.Params {
sb.WriteString(" ")
sb.WriteString(k)
if v != "" {
sb.WriteString("=")
sb.WriteString(Escape(v))
}
}
return sb.String()
}