From 0a5f6571125f2cc61b2dd957762164a13b97ee69 Mon Sep 17 00:00:00 2001 From: bdeb1337 Date: Fri, 11 Aug 2023 23:57:08 +0200 Subject: [PATCH 1/6] Extended OnlineClient information variables --- mockserver_test.go | 2 +- server_cmds.go | 40 ++++++++++++++++++++++++++++++++-------- server_cmds_test.go | 36 +++++++++++++++++++++++++++++------- 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/mockserver_test.go b/mockserver_test.go index 726e346..cbcaa08 100644 --- a/mockserver_test.go +++ b/mockserver_test.go @@ -43,7 +43,7 @@ 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", + "clientlist": `clid=42087 cid=39 client_database_id=19 client_nickname=bdeb1337 client_type=0 client_away=1 client_away_message 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: "", diff --git a/server_cmds.go b/server_cmds.go index 411af27..578a051 100644 --- a/server_cmds.go +++ b/server_cmds.go @@ -351,19 +351,43 @@ 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"` + 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"` + 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"` + // TODO: + // ServerGroups []int `ms:"client_servergroups"` } // ClientList returns a list of online clients. func (s *ServerMethods) ClientList() ([]*OnlineClient, error) { var clients []*OnlineClient - if _, err := s.ExecCmd(NewCmd("clientlist").WithResponse(&clients)); err != nil { + if _, err := s.ExecCmd(NewCmd("clientlist -uid -away -voice -times -groups -info -icon -country -ip -badges").WithResponse(&clients)); err != nil { return nil, err } return clients, nil diff --git a/server_cmds_test.go b/server_cmds_test.go index 1243b1f..5695819 100644 --- a/server_cmds_test.go +++ b/server_cmds_test.go @@ -306,13 +306,35 @@ 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: true, + AwayMessage: "", + FlagTalking: false, + InputMuted: false, + OutputMuted: false, + InputHardware: true, + OutputHardware: true, + TalkPower: 75, + IsTalker: false, + IsPrioritySpeaker: false, + IsRecording: false, + IsChannelCommander: false, + UniqueIdentifier: "DZhdQU58qyooEK4Fr8Ly738hEmc=", + ChannelGroupID: 8, + ChannelGroupInheritedChannelID: 39, + Version: "3.6.1 [Build: 1690193193]", + Platform: "OS X", + IdleTime: 1280228, + Created: 1661793049, + LastConnected: 1691527133, + IconID: 0, + Country: "BE", + IP: "1.3.3.7", + Badges: "", }, } From cd7652824b36bccf636201bdf3ed248ee935c05b Mon Sep 17 00:00:00 2001 From: bdeb1337 Date: Wed, 30 Aug 2023 21:30:09 +0200 Subject: [PATCH 2/6] add optional clientlist parameters --- server_cmds.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/server_cmds.go b/server_cmds.go index 578a051..6ee8320 100644 --- a/server_cmds.go +++ b/server_cmds.go @@ -385,9 +385,18 @@ type OnlineClient struct { } // ClientList returns a list of online clients. -func (s *ServerMethods) ClientList() ([]*OnlineClient, error) { +func (s *ServerMethods) ClientList(params ...string) ([]*OnlineClient, error) { var clients []*OnlineClient - if _, err := s.ExecCmd(NewCmd("clientlist -uid -away -voice -times -groups -info -icon -country -ip -badges").WithResponse(&clients)); err != nil { + var clientListParams string + for _, param := range params { + if param == "all" { + clientListParams = " -uid -away -voice -times -groups -info -icon -country -ip -badges" + break + } else { + clientListParams += " " + param + } + } + if _, err := s.ExecCmd(NewCmd("clientlist" + clientListParams).WithResponse(&clients)); err != nil { return nil, err } return clients, nil From 914f1ab209da1bddeb7067f393905f0e86731d1f Mon Sep 17 00:00:00 2001 From: bdeb1337 Date: Tue, 5 Sep 2023 17:11:00 +0200 Subject: [PATCH 3/6] Flags, pointers ,servergroups and testing Added flags for ClientList to use Changed OnlineClient struct to use pointers for optional parameters so it would be clear when no value was returned Added support for client_servergroups which needed comma delimiting. Tried to add/adapt relevant testing for everything changed. --- helpers.go | 15 ++++++- mockserver_test.go | 12 +++-- server_cmds.go | 92 +++++++++++++++++++++----------------- server_cmds_test.go | 106 ++++++++++++++++++++++++++++++++++---------- 4 files changed, 154 insertions(+), 71 deletions(-) diff --git a/helpers.go b/helpers.go index 9052d59..bf1c868 100644 --- a/helpers.go +++ b/helpers.go @@ -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 } diff --git a/mockserver_test.go b/mockserver_test.go index cbcaa08..6ea5abe 100644 --- a/mockserver_test.go +++ b/mockserver_test.go @@ -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=42087 cid=39 client_database_id=19 client_nickname=bdeb1337 client_type=0 client_away=1 client_away_message 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: "", + "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. @@ -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 { diff --git a/server_cmds.go b/server_cmds.go index 6ee8320..2ddcc74 100644 --- a/server_cmds.go +++ b/server_cmds.go @@ -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. @@ -351,52 +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"` - 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"` - // TODO: - // ServerGroups []int `ms:"client_servergroups"` + // 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(params ...string) ([]*OnlineClient, error) { +func (s *ServerMethods) ClientList(options ...string) ([]*OnlineClient, error) { var clients []*OnlineClient - var clientListParams string - for _, param := range params { - if param == "all" { - clientListParams = " -uid -away -voice -times -groups -info -icon -country -ip -badges" - break - } else { - clientListParams += " " + param - } + var clientListOptions string + for _, option := range options { + clientListOptions += " " + option } - if _, err := s.ExecCmd(NewCmd("clientlist" + clientListParams).WithResponse(&clients)); err != nil { + if _, err := s.ExecCmd(NewCmd("clientlist" + clientListOptions).WithResponse(&clients)); err != nil { return nil, err } return clients, nil diff --git a/server_cmds_test.go b/server_cmds_test.go index 5695819..be127f7 100644 --- a/server_cmds_test.go +++ b/server_cmds_test.go @@ -311,36 +311,93 @@ func testCmdsServer(t *testing.T, c *Client) { DatabaseID: 19, Nickname: "bdeb1337", Type: 0, - Away: true, - AwayMessage: "", - FlagTalking: false, - InputMuted: false, - OutputMuted: false, - InputHardware: true, - OutputHardware: true, - TalkPower: 75, - IsTalker: false, - IsPrioritySpeaker: false, - IsRecording: false, - IsChannelCommander: false, - UniqueIdentifier: "DZhdQU58qyooEK4Fr8Ly738hEmc=", - ChannelGroupID: 8, - ChannelGroupInheritedChannelID: 39, - Version: "3.6.1 [Build: 1690193193]", - Platform: "OS X", - IdleTime: 1280228, - Created: 1661793049, - LastConnected: 1691527133, - IconID: 0, - Country: "BE", - IP: "1.3.3.7", - Badges: "", + 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() @@ -380,6 +437,7 @@ func testCmdsServer(t *testing.T, c *Client) { {"instanceinfo", instanceinfo}, {"channellist", channellist}, {"clientlist", clientlist}, + {"clientlistextended", clientlistextended}, {"clientdblist", clientdblist}, } From 151f8339d58655d4fda58de17b0e68282fe1f511 Mon Sep 17 00:00:00 2001 From: bdeb1337 Date: Thu, 7 Sep 2023 21:55:45 +0200 Subject: [PATCH 4/6] Optimalisations & embedded structs onlineClient --- helpers.go | 11 +++-- mockserver_test.go | 4 +- server_cmds.go | 80 +++++++++++++++++++------------- server_cmds_test.go | 109 ++++++++++++++++++++------------------------ 4 files changed, 108 insertions(+), 96 deletions(-) diff --git a/helpers.go b/helpers.go index bf1c868..f877569 100644 --- a/helpers.go +++ b/helpers.go @@ -78,11 +78,14 @@ func DecodeResponse(lines []string, v interface{}) error { // 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) + parts := strings.Split(v, ",") + serverGroups := make([]int, len(parts)) + for i, s := range parts { + group, err := strconv.Atoi(s) + if err != nil { + return fmt.Errorf("decode server group: %w", err) } + serverGroups[i] = group } input[key] = serverGroups } else { diff --git a/mockserver_test.go b/mockserver_test.go index 6ea5abe..e5d845e 100644 --- a/mockserver_test.go +++ b/mockserver_test.go @@ -43,7 +43,7 @@ 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=42087 cid=39 client_database_id=19 client_nickname=bdeb1337 client_type=0`, + "clientlist": `clid=42087 cid=39 client_database_id=19 client_nickname=bdeb1337 client_type=0 client_away=0 client_away_message`, "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", @@ -257,6 +257,8 @@ func (s *server) handle(conn net.Conn) { l := sc.Text() parts := strings.Split(l, " ") cmd := strings.TrimSpace(parts[0]) + // to support server commands with specific optional parameters, + // they can be bypassed from the usual parameter trimming here. if cmd == "clientlist" { cmd = l } diff --git a/server_cmds.go b/server_cmds.go index 2ddcc74..466aa9c 100644 --- a/server_cmds.go +++ b/server_cmds.go @@ -17,7 +17,7 @@ const ( ClientInfo = "-info" ClientIcon = "-icon" ClientCountry = "-country" - ClientIp = "-ip" + 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" @@ -372,41 +372,59 @@ type OnlineClient struct { 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"` + // note: Away and AwayMessage are currently optional but not using pointers for compatibility with older versions of teamspeakserver. + // This means they will always be returned, but their value will only be representative when the ClientAway option is specified. + OnlineClientAway `ms:",squash"` // ts3.ClientAway + UniqueIdentifier *string `ms:"client_unique_identifier"` // ts3.ClientUid + OnlineClientVoice `ms:",squash"` // ts3.ClientVoice + OnlineClientTimes `ms:",squash"` // ts3.ClientTimes + OnlineClientGroups `ms:",squash"` // ts3.ClientGroups + OnlineClientInfo `ms:",squash"` // ts3.ClientInfo + IconID *int `ms:"client_icon_id"` // ts3.ClientIcon + Country *string `ms:"client_country"` // ts3.ClientCountry + IP *string `ms:"connection_client_ip"` // ts3.ClientIP + Badges *string `ms:"client_badges"` // ts3.ClientBadges +} + +type OnlineClientAway struct { + Away bool `ms:"client_away"` + AwayMessage string `ms:"client_away_message"` +} + +type OnlineClientVoice struct { + 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"` +} + +type OnlineClientTimes struct { + IdleTime *int `ms:"client_idle_time"` + Created *int `ms:"client_created"` + LastConnected *int `ms:"client_lastconnected"` +} + +type OnlineClientGroups struct { + ChannelGroupID *int `ms:"client_channel_group_id"` + ChannelGroupInheritedChannelID *int `ms:"client_channel_group_inherited_channel_id"` + ServerGroups *[]int `ms:"client_servergroups"` +} + +type OnlineClientInfo struct { + Version *string `ms:"client_version"` + Platform *string `ms:"client_platform"` } // ClientList returns a list of online clients. func (s *ServerMethods) ClientList(options ...string) ([]*OnlineClient, error) { var clients []*OnlineClient - var clientListOptions string - for _, option := range options { - clientListOptions += " " + option - } - if _, err := s.ExecCmd(NewCmd("clientlist" + clientListOptions).WithResponse(&clients)); err != nil { + if _, err := s.ExecCmd(NewCmd("clientlist").WithOptions(options...).WithResponse(&clients)); err != nil { return nil, err } return clients, nil diff --git a/server_cmds_test.go b/server_cmds_test.go index be127f7..c15fff5 100644 --- a/server_cmds_test.go +++ b/server_cmds_test.go @@ -306,36 +306,15 @@ func testCmdsServer(t *testing.T, c *Client) { expected := []*OnlineClient{ { - 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, + ID: 42087, + ChannelID: 39, + DatabaseID: 19, + Nickname: "bdeb1337", + Type: 0, + OnlineClientAway: OnlineClientAway{ + Away: false, + AwayMessage: "", + }, }, } @@ -362,36 +341,46 @@ func testCmdsServer(t *testing.T, c *Client) { 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}, + ID: 42087, + ChannelID: 39, + DatabaseID: 19, + Nickname: "bdeb1337", + Type: 0, + OnlineClientAway: OnlineClientAway{ + Away: true, + AwayMessage: "afk", + }, + UniqueIdentifier: stringptr("DZhdQU58qyooEK4Fr8Ly738hEmc="), + OnlineClientVoice: OnlineClientVoice{ + 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), + }, + OnlineClientTimes: OnlineClientTimes{ + IdleTime: intptr(1280228), + Created: intptr(1661793049), + LastConnected: intptr(1691527133), + }, + OnlineClientGroups: OnlineClientGroups{ + ChannelGroupID: intptr(8), + ChannelGroupInheritedChannelID: intptr(39), + ServerGroups: &[]int{6, 8}, + }, + OnlineClientInfo: OnlineClientInfo{ + Version: stringptr("3.6.1 [Build: 1690193193]"), + Platform: stringptr("OS X"), + }, + IconID: intptr(0), + Country: stringptr("BE"), + IP: stringptr("1.3.3.7"), + Badges: stringptr(""), }, } From a9070bcdc8734d2c424e2a0a8657604c335efca5 Mon Sep 17 00:00:00 2001 From: bdeb1337 Date: Thu, 14 Sep 2023 18:36:55 +0200 Subject: [PATCH 5/6] Added embedded struct pointers in OnlineClient --- helpers.go | 46 +++++++++++++++++++++++ mockserver_test.go | 2 +- server_cmds.go | 29 +++++++-------- server_cmds_test.go | 89 +++++++++++++++++++++------------------------ 4 files changed, 103 insertions(+), 63 deletions(-) diff --git a/helpers.go b/helpers.go index f877569..aabb365 100644 --- a/helpers.go +++ b/helpers.go @@ -150,10 +150,56 @@ func decodeSlice(elemType reflect.Type, slice reflect.Value, input map[string]in return fmt.Errorf("can't interface %#v", v) } + // The mapstructure's decoder doesn't support squashing + // for embedded pointers to structs (the type is lost when + // using reflection for nil values). We need to add pointers + // to empty structs within the interface to get around this. + switch v.Interface().(type) { + case *OnlineClient: + ext := &OnlineClientExt{ + OnlineClientGroups: &OnlineClientGroups{}, + OnlineClientInfo: &OnlineClientInfo{}, + OnlineClientTimes: &OnlineClientTimes{}, + OnlineClientVoice: &OnlineClientVoice{}, + } + v.Interface().(*OnlineClient).OnlineClientExt = ext + } + if err := decodeMap(input, v.Interface()); err != nil { return err } + // nil out empty structs + switch v.Interface().(type) { + case *OnlineClient: + ext := v.Interface().(*OnlineClient).OnlineClientExt + emptyExt := OnlineClientExt{} + emptyExtGroups := OnlineClientGroups{} + emptyExtInfo := OnlineClientInfo{} + emptyExtTimes := OnlineClientTimes{} + emptyExtVoice := OnlineClientVoice{} + + if *ext.OnlineClientGroups == emptyExtGroups { + v.Interface().(*OnlineClient).OnlineClientExt.OnlineClientGroups = nil + } + + if *ext.OnlineClientInfo == emptyExtInfo { + v.Interface().(*OnlineClient).OnlineClientExt.OnlineClientInfo = nil + } + + if *ext.OnlineClientTimes == emptyExtTimes { + v.Interface().(*OnlineClient).OnlineClientExt.OnlineClientTimes = nil + } + + if *ext.OnlineClientVoice == emptyExtVoice { + v.Interface().(*OnlineClient).OnlineClientExt.OnlineClientVoice = nil + } + + if *ext == emptyExt { + v.Interface().(*OnlineClient).OnlineClientExt = nil + } + } + if elemType.Kind() == reflect.Struct { v = v.Elem() } diff --git a/mockserver_test.go b/mockserver_test.go index e5d845e..8c503f9 100644 --- a/mockserver_test.go +++ b/mockserver_test.go @@ -257,7 +257,7 @@ func (s *server) handle(conn net.Conn) { l := sc.Text() parts := strings.Split(l, " ") cmd := strings.TrimSpace(parts[0]) - // to support server commands with specific optional parameters, + // Support server commands with specific optional parameters, // they can be bypassed from the usual parameter trimming here. if cmd == "clientlist" { cmd = l diff --git a/server_cmds.go b/server_cmds.go index 466aa9c..bfff11e 100644 --- a/server_cmds.go +++ b/server_cmds.go @@ -372,23 +372,22 @@ type OnlineClient struct { 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. - // note: Away and AwayMessage are currently optional but not using pointers for compatibility with older versions of teamspeakserver. - // This means they will always be returned, but their value will only be representative when the ClientAway option is specified. - OnlineClientAway `ms:",squash"` // ts3.ClientAway - UniqueIdentifier *string `ms:"client_unique_identifier"` // ts3.ClientUid - OnlineClientVoice `ms:",squash"` // ts3.ClientVoice - OnlineClientTimes `ms:",squash"` // ts3.ClientTimes - OnlineClientGroups `ms:",squash"` // ts3.ClientGroups - OnlineClientInfo `ms:",squash"` // ts3.ClientInfo - IconID *int `ms:"client_icon_id"` // ts3.ClientIcon - Country *string `ms:"client_country"` // ts3.ClientCountry - IP *string `ms:"connection_client_ip"` // ts3.ClientIP - Badges *string `ms:"client_badges"` // ts3.ClientBadges + // note: Away and AwayMessage are currently optional but not using pointers for compatibility considerations. + Away bool `ms:"client_away"` // Only populated if ClientAway or ClientListFull is passed to ClientList. + AwayMessage string `ms:"client_away_message"` // Only populated if ClientAway or ClientListFull is passed to ClientList. + *OnlineClientExt `ms:",squash"` // Only populated if any of the options is passed to ClientList. } -type OnlineClientAway struct { - Away bool `ms:"client_away"` - AwayMessage string `ms:"client_away_message"` +type OnlineClientExt struct { + UniqueIdentifier *string `ms:"client_unique_identifier"` // Only populated if ClientUid or ClientListFull is passed to ClientList. + *OnlineClientVoice `ms:",squash"` // Only populated if ClientVoice or ClientListFull is passed to ClientList. + *OnlineClientTimes `ms:",squash"` // Only populated if ClientTimes or ClientListFull is passed to ClientList. + *OnlineClientGroups `ms:",squash"` // Only populated if ClientGroups or ClientListFull is passed to ClientList. + *OnlineClientInfo `ms:",squash"` // Only populated if ClientInfo or ClientListFull is passed to ClientList. + Country *string `ms:"client_country"` // Only populated if ClientCountry or ClientListFull is passed to ClientList. + IP *string `ms:"connection_client_ip"` // Only populated if ClientIP or ClientListFull is passed to ClientList. + Badges *string `ms:"client_badges"` // Only populated if ClientBadges or ClientListFull is passed to ClientList. + IconID *int `ms:"client_icon_id"` // Only populated if ClientIcon or ClientListFull is passed to ClientList. } type OnlineClientVoice struct { diff --git a/server_cmds_test.go b/server_cmds_test.go index c15fff5..451fbac 100644 --- a/server_cmds_test.go +++ b/server_cmds_test.go @@ -311,10 +311,6 @@ func testCmdsServer(t *testing.T, c *Client) { DatabaseID: 19, Nickname: "bdeb1337", Type: 0, - OnlineClientAway: OnlineClientAway{ - Away: false, - AwayMessage: "", - }, }, } @@ -328,10 +324,9 @@ func testCmdsServer(t *testing.T, c *Client) { return } - // helper functions to return pointers - boolptr := func(b bool) *bool { - return &b - } + // helper variables & functions for pointers + falseP := false + trueP := true stringptr := func(s string) *string { return &s } @@ -341,46 +336,46 @@ func testCmdsServer(t *testing.T, c *Client) { expected := []*OnlineClient{ { - ID: 42087, - ChannelID: 39, - DatabaseID: 19, - Nickname: "bdeb1337", - Type: 0, - OnlineClientAway: OnlineClientAway{ - Away: true, - AwayMessage: "afk", - }, - UniqueIdentifier: stringptr("DZhdQU58qyooEK4Fr8Ly738hEmc="), - OnlineClientVoice: OnlineClientVoice{ - 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), - }, - OnlineClientTimes: OnlineClientTimes{ - IdleTime: intptr(1280228), - Created: intptr(1661793049), - LastConnected: intptr(1691527133), - }, - OnlineClientGroups: OnlineClientGroups{ - ChannelGroupID: intptr(8), - ChannelGroupInheritedChannelID: intptr(39), - ServerGroups: &[]int{6, 8}, - }, - OnlineClientInfo: OnlineClientInfo{ - Version: stringptr("3.6.1 [Build: 1690193193]"), - Platform: stringptr("OS X"), + ID: 42087, + ChannelID: 39, + DatabaseID: 19, + Nickname: "bdeb1337", + Type: 0, + Away: true, + AwayMessage: "afk", + OnlineClientExt: &OnlineClientExt{ + UniqueIdentifier: stringptr("DZhdQU58qyooEK4Fr8Ly738hEmc="), + OnlineClientVoice: &OnlineClientVoice{ + FlagTalking: &falseP, + InputMuted: &falseP, + OutputMuted: &falseP, + InputHardware: &trueP, + OutputHardware: &trueP, + TalkPower: intptr(75), + IsTalker: &falseP, + IsPrioritySpeaker: &falseP, + IsRecording: &falseP, + IsChannelCommander: &falseP, + }, + OnlineClientTimes: &OnlineClientTimes{ + IdleTime: intptr(1280228), + Created: intptr(1661793049), + LastConnected: intptr(1691527133), + }, + OnlineClientGroups: &OnlineClientGroups{ + ChannelGroupID: intptr(8), + ChannelGroupInheritedChannelID: intptr(39), + ServerGroups: &[]int{6, 8}, + }, + OnlineClientInfo: &OnlineClientInfo{ + Version: stringptr("3.6.1 [Build: 1690193193]"), + Platform: stringptr("OS X"), + }, + IconID: intptr(0), + Country: stringptr("BE"), + IP: stringptr("1.3.3.7"), + Badges: stringptr(""), }, - IconID: intptr(0), - Country: stringptr("BE"), - IP: stringptr("1.3.3.7"), - Badges: stringptr(""), }, } From 103a1a2761e2c0788ad7cbf9c225a51e0b12753c Mon Sep 17 00:00:00 2001 From: bdeb Date: Fri, 1 Dec 2023 23:26:12 +0100 Subject: [PATCH 6/6] Comments work * Add comments for forgotten exported types * Add proper comments for the extension consts --- server_cmds.go | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/server_cmds.go b/server_cmds.go index bfff11e..d986225 100644 --- a/server_cmds.go +++ b/server_cmds.go @@ -8,17 +8,26 @@ 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" + // ClientUID can be passed to ClientList to retrieve client UID information. + ClientUID = "-uid" + // ClientAway can be passed to ClientList to retrieve client away information. + ClientAway = "-away" + // ClientVoice can be passed to ClientList to retrieve client voice information. + ClientVoice = "-voice" + // ClientTimes can be passed to ClientList to retrieve client time information. + ClientTimes = "-times" + // ClientGroups can be passed to ClientList to retrieve client groups information. + ClientGroups = "-groups" + // ClientInfo can be passed to ClientList to retrieve client information. + ClientInfo = "-info" + // ClientIcon can be passed to ClientList to retrieve client icon information. + ClientIcon = "-icon" + // ClientCountry can be passed to ClientList to retrieve client country information. ClientCountry = "-country" - ClientIP = "-ip" - ClientBadges = "-badges" + // ClientIP can be passed to ClientList to retrieve client IP information. + ClientIP = "-ip" + // ClientBadges can be passed to ClientList to retrieve client badge information. + 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" ) @@ -378,8 +387,9 @@ type OnlineClient struct { *OnlineClientExt `ms:",squash"` // Only populated if any of the options is passed to ClientList. } +// OnlineClientExt represents all ClientList extensions. type OnlineClientExt struct { - UniqueIdentifier *string `ms:"client_unique_identifier"` // Only populated if ClientUid or ClientListFull is passed to ClientList. + UniqueIdentifier *string `ms:"client_unique_identifier"` // Only populated if ClientUID or ClientListFull is passed to ClientList. *OnlineClientVoice `ms:",squash"` // Only populated if ClientVoice or ClientListFull is passed to ClientList. *OnlineClientTimes `ms:",squash"` // Only populated if ClientTimes or ClientListFull is passed to ClientList. *OnlineClientGroups `ms:",squash"` // Only populated if ClientGroups or ClientListFull is passed to ClientList. @@ -390,6 +400,7 @@ type OnlineClientExt struct { IconID *int `ms:"client_icon_id"` // Only populated if ClientIcon or ClientListFull is passed to ClientList. } +// OnlineClientVoice represents all ClientList extensions when the ClientVoice parameter is passed. type OnlineClientVoice struct { FlagTalking *bool `ms:"client_flag_talking"` InputMuted *bool `ms:"client_input_muted"` @@ -403,18 +414,21 @@ type OnlineClientVoice struct { IsChannelCommander *bool `ms:"client_is_channel_commander"` } +// OnlineClientTimes represents all ClientList extensions when the ClientTimes parameter is passed. type OnlineClientTimes struct { IdleTime *int `ms:"client_idle_time"` Created *int `ms:"client_created"` LastConnected *int `ms:"client_lastconnected"` } +// OnlineClientGroups represents all ClientList extensions when the ClientGroups parameter is passed. type OnlineClientGroups struct { ChannelGroupID *int `ms:"client_channel_group_id"` ChannelGroupInheritedChannelID *int `ms:"client_channel_group_inherited_channel_id"` ServerGroups *[]int `ms:"client_servergroups"` } +// OnlineClientInfo represents all ClientList extensions when the ClientInfo parameter is passed. type OnlineClientInfo struct { Version *string `ms:"client_version"` Platform *string `ms:"client_platform"`