Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,23 @@ func DecodeResponse(lines []string, v interface{}) error {
for _, part := range strings.Split(lines[0], "|") {
for _, val := range strings.Split(part, " ") {
parts := strings.SplitN(val, "=", 2)
// TODO(steve): support groups
key := Decode(parts[0])
if len(parts) == 2 {
v := Decode(parts[1])
if i, err := strconv.Atoi(v); err != nil {
input[key] = v
// Only support comma seperated lists
// by keyname to avoid incorrect decoding.
if key == "client_servergroups" {
var serverGroups []int
for _, s := range strings.Split(v, ",") {
if i, err := strconv.Atoi(s); err == nil {
serverGroups = append(serverGroups, i)
}
}
input[key] = serverGroups
} else {
input[key] = v
}
} else {
input[key] = i
}
Expand Down
12 changes: 8 additions & 4 deletions mockserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ var commands = map[string]string{
"instanceinfo": "serverinstance_database_version=26 serverinstance_filetransfer_port=30033 serverinstance_max_download_total_bandwidth=18446744073709551615 serverinstance_max_upload_total_bandwidth=18446744073709551615 serverinstance_guest_serverquery_group=1 serverinstance_serverquery_flood_commands=50 serverinstance_serverquery_flood_time=3 serverinstance_serverquery_ban_time=600 serverinstance_template_serveradmin_group=3 serverinstance_template_serverdefault_group=5 serverinstance_template_channeladmin_group=1 serverinstance_template_channeldefault_group=4 serverinstance_permissions_version=19 serverinstance_pending_connections_per_ip=0",
"serverrequestconnectioninfo": "connection_filetransfer_bandwidth_sent=0 connection_filetransfer_bandwidth_received=0 connection_filetransfer_bytes_sent_total=617 connection_filetransfer_bytes_received_total=0 connection_packets_sent_total=926413 connection_bytes_sent_total=92911395 connection_packets_received_total=650335 connection_bytes_received_total=61940731 connection_bandwidth_sent_last_second_total=0 connection_bandwidth_sent_last_minute_total=0 connection_bandwidth_received_last_second_total=0 connection_bandwidth_received_last_minute_total=0 connection_connected_time=49408 connection_packetloss_total=0.0000 connection_ping=0.0000 connection_packets_sent_speech=320432180 connection_bytes_sent_speech=43805818511 connection_packets_received_speech=174885295 connection_bytes_received_speech=24127808273 connection_packets_sent_keepalive=55230363 connection_bytes_sent_keepalive=2264444883 connection_packets_received_keepalive=55149547 connection_bytes_received_keepalive=2316390993 connection_packets_sent_control=2376088 connection_bytes_sent_control=525691022 connection_packets_received_control=2376138 connection_bytes_received_control=227044870",
"channellist": "cid=499 pid=0 channel_order=0 channel_name=Default\\sChannel total_clients=1 channel_needed_subscribe_power=0",
"clientlist": "clid=5 cid=7 client_database_id=40 client_nickname=ScP client_type=0 client_away=1 client_away_message=not\\shere",
"clientdblist": "cldbid=7 client_unique_identifier=DZhdQU58qyooEK4Fr8Ly738hEmc= client_nickname=MuhChy client_created=1259147468 client_lastconnected=1259421233",
"whoami": "virtualserver_status=online virtualserver_id=18 virtualserver_unique_identifier=gNITtWtKs9+Uh3L4LKv8\\/YHsn5c= virtualserver_port=9987 client_id=94 client_channel_id=432 client_nickname=serveradmin\\sfrom\\s127.0.0.1:49725 client_database_id=1 client_login_name=serveradmin client_unique_identifier=serveradmin client_origin_server_id=0",
cmdQuit: "",
"clientlist": `clid=42087 cid=39 client_database_id=19 client_nickname=bdeb1337 client_type=0`,
"clientlist -uid -away -voice -times -groups -info -icon -country -ip -badges": `clid=42087 cid=39 client_database_id=19 client_nickname=bdeb1337 client_type=0 client_away=1 client_away_message=afk client_flag_talking=0 client_input_muted=0 client_output_muted=0 client_input_hardware=1 client_output_hardware=1 client_talk_power=75 client_is_talker=0 client_is_priority_speaker=0 client_is_recording=0 client_is_channel_commander=0 client_unique_identifier=DZhdQU58qyooEK4Fr8Ly738hEmc= client_servergroups=6,8 client_channel_group_id=8 client_channel_group_inherited_channel_id=39 client_version=3.6.1\s[Build:\s1690193193] client_platform=OS\sX client_idle_time=1280228 client_created=1661793049 client_lastconnected=1691527133 client_icon_id=0 client_country=BE connection_client_ip=1.3.3.7 client_badges`,
"clientdblist": "cldbid=7 client_unique_identifier=DZhdQU58qyooEK4Fr8Ly738hEmc= client_nickname=MuhChy client_created=1259147468 client_lastconnected=1259421233",
"whoami": "virtualserver_status=online virtualserver_id=18 virtualserver_unique_identifier=gNITtWtKs9+Uh3L4LKv8\\/YHsn5c= virtualserver_port=9987 client_id=94 client_channel_id=432 client_nickname=serveradmin\\sfrom\\s127.0.0.1:49725 client_database_id=1 client_login_name=serveradmin client_unique_identifier=serveradmin client_origin_server_id=0",
cmdQuit: "",
}

// newLockListener creates a new listener on the local IP.
Expand Down Expand Up @@ -256,6 +257,9 @@ func (s *server) handle(conn net.Conn) {
l := sc.Text()
parts := strings.Split(l, " ")
cmd := strings.TrimSpace(parts[0])
if cmd == "clientlist" {
cmd = l
}
resp, ok := commands[cmd]
var err error
switch {
Expand Down
61 changes: 52 additions & 9 deletions server_cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ import (
const (
// ExtendedServerList can be passed to List to get extended server information.
ExtendedServerList = "-extended"

// Following variables can be passed to ClientList() to get extended client information.
ClientUid = "-uid"
ClientAway = "-away"
ClientVoice = "-voice"
ClientTimes = "-times"
ClientGroups = "-groups"
ClientInfo = "-info"
ClientIcon = "-icon"
ClientCountry = "-country"
ClientIp = "-ip"
ClientBadges = "-badges"
// ClientListFull can be passed to ClientList to get all extended client information.
ClientListFull = "-uid -away -voice -times -groups -info -icon -country -ip -badges"
)

// ServerMethods groups server methods.
Expand Down Expand Up @@ -351,19 +365,48 @@ func (s *ServerMethods) PrivilegeKeyAdd(ttype, id1, id2 int, options ...CmdArg)

// OnlineClient represents a client online on a virtual server.
type OnlineClient struct {
ID int `ms:"clid"`
ChannelID int `ms:"cid"`
DatabaseID int `ms:"client_database_id"`
Nickname string `ms:"client_nickname"`
Type int `ms:"client_type"`
Away bool `ms:"client_away"`
AwayMessage string `ms:"client_away_message"`
// Following variables are always returned by ClientList().
ID int `ms:"clid"`
ChannelID int `ms:"cid"`
DatabaseID int `ms:"client_database_id"`
Nickname string `ms:"client_nickname"`
Type int `ms:"client_type"`
// Following variables are optional and can be requested in ClientList() to get extended client information.
Away *bool `ms:"client_away"`
AwayMessage *string `ms:"client_away_message"`
FlagTalking *bool `ms:"client_flag_talking"`
InputMuted *bool `ms:"client_input_muted"`
OutputMuted *bool `ms:"client_output_muted"`
InputHardware *bool `ms:"client_input_hardware"`
OutputHardware *bool `ms:"client_output_hardware"`
TalkPower *int `ms:"client_talk_power"`
IsTalker *bool `ms:"client_is_talker"`
IsPrioritySpeaker *bool `ms:"client_is_priority_speaker"`
IsRecording *bool `ms:"client_is_recording"`
IsChannelCommander *bool `ms:"client_is_channel_commander"`
UniqueIdentifier *string `ms:"client_unique_identifier"`
ChannelGroupID *int `ms:"client_channel_group_id"`
ChannelGroupInheritedChannelID *int `ms:"client_channel_group_inherited_channel_id"`
Version *string `ms:"client_version"`
Platform *string `ms:"client_platform"`
IdleTime *int `ms:"client_idle_time"`
Created *int `ms:"client_created"`
LastConnected *int `ms:"client_lastconnected"`
IconID *int `ms:"client_icon_id"`
Country *string `ms:"client_country"`
IP *string `ms:"connection_client_ip"`
Badges *string `ms:"client_badges"`
ServerGroups *[]int `ms:"client_servergroups"`
}

// ClientList returns a list of online clients.
func (s *ServerMethods) ClientList() ([]*OnlineClient, error) {
func (s *ServerMethods) ClientList(options ...string) ([]*OnlineClient, error) {
var clients []*OnlineClient
if _, err := s.ExecCmd(NewCmd("clientlist").WithResponse(&clients)); err != nil {
var clientListOptions string
for _, option := range options {
clientListOptions += " " + option
}
if _, err := s.ExecCmd(NewCmd("clientlist" + clientListOptions).WithResponse(&clients)); err != nil {
return nil, err
}
return clients, nil
Expand Down
94 changes: 87 additions & 7 deletions server_cmds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,19 +306,98 @@ func testCmdsServer(t *testing.T, c *Client) {

expected := []*OnlineClient{
{
ID: 5,
ChannelID: 7,
DatabaseID: 40,
Nickname: "ScP",
Type: 0,
Away: true,
AwayMessage: "not here",
ID: 42087,
ChannelID: 39,
DatabaseID: 19,
Nickname: "bdeb1337",
Type: 0,
Away: nil,
AwayMessage: nil,
FlagTalking: nil,
InputMuted: nil,
OutputMuted: nil,
InputHardware: nil,
OutputHardware: nil,
TalkPower: nil,
IsTalker: nil,
IsPrioritySpeaker: nil,
IsRecording: nil,
IsChannelCommander: nil,
UniqueIdentifier: nil,
ChannelGroupID: nil,
ChannelGroupInheritedChannelID: nil,
Version: nil,
Platform: nil,
IdleTime: nil,
Created: nil,
LastConnected: nil,
IconID: nil,
Country: nil,
IP: nil,
Badges: nil,
ServerGroups: nil,
},
}

assert.Equal(t, expected, clients)
}

clientlistextended := func(t *testing.T) {
t.Helper()
clientz, err := c.Server.ClientList(ClientListFull)
if !assert.NoError(t, err) {
return
}

// helper functions to return pointers
boolptr := func(b bool) *bool {
return &b
}
stringptr := func(s string) *string {
return &s
}
intptr := func(i int) *int {
return &i
}

expected := []*OnlineClient{
{
ID: 42087,
ChannelID: 39,
DatabaseID: 19,
Nickname: "bdeb1337",
Type: 0,
Away: boolptr(true),
AwayMessage: stringptr("afk"),
FlagTalking: boolptr(false),
InputMuted: boolptr(false),
OutputMuted: boolptr(false),
InputHardware: boolptr(true),
OutputHardware: boolptr(true),
TalkPower: intptr(75),
IsTalker: boolptr(false),
IsPrioritySpeaker: boolptr(false),
IsRecording: boolptr(false),
IsChannelCommander: boolptr(false),
UniqueIdentifier: stringptr("DZhdQU58qyooEK4Fr8Ly738hEmc="),
ChannelGroupID: intptr(8),
ChannelGroupInheritedChannelID: intptr(39),
Version: stringptr("3.6.1 [Build: 1690193193]"),
Platform: stringptr("OS X"),
IdleTime: intptr(1280228),
Created: intptr(1661793049),
LastConnected: intptr(1691527133),
IconID: intptr(0),
Country: stringptr("BE"),
IP: stringptr("1.3.3.7"),
Badges: stringptr(""),
ServerGroups: &[]int{6, 8},
},
}

assert.Equal(t, expected, clientz)
}

clientdblist := func(t *testing.T) {
t.Helper()
clients, err := c.Server.ClientDBList()
Expand Down Expand Up @@ -358,6 +437,7 @@ func testCmdsServer(t *testing.T, c *Client) {
{"instanceinfo", instanceinfo},
{"channellist", channellist},
{"clientlist", clientlist},
{"clientlistextended", clientlistextended},
{"clientdblist", clientdblist},
}

Expand Down