116 lines
2.3 KiB
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()
|
|
}
|