feat: refactor client into reusable ts3client library

This commit is contained in:
Jose Luis Montañes Ojados
2026-01-15 22:06:35 +01:00
parent 7878ad3d5b
commit 02318b1490
10 changed files with 1050 additions and 47 deletions

View File

@@ -172,8 +172,13 @@ func (c *Client) handleCommand(pkt *protocol.Packet) error {
log.Printf("Assigned ClientID: %d", c.ClientID)
}
if name, ok := args["virtualserver_name"]; ok {
log.Printf("Server Name: %s", protocol.Unescape(name))
c.ServerName = protocol.Unescape(name)
log.Printf("Server Name: %s", c.ServerName)
}
c.emitEvent("connected", map[string]any{
"clientID": c.ClientID,
"serverName": c.ServerName,
})
case "channellist":
// Parse channel info
ch := &Channel{}
@@ -193,13 +198,18 @@ func (c *Client) handleCommand(pkt *protocol.Packet) error {
log.Printf("Channel: [%d] NameRaw=%q Order=%d Args=%v", ch.ID, ch.Name, ch.Order, args)
case "channellistfinished":
log.Printf("=== Channel List Complete (%d channels) ===", len(c.Channels))
var channelList []*Channel
var targetChan *Channel
for _, ch := range c.Channels {
log.Printf(" - [%d] %s (parent=%d)", ch.ID, ch.Name, ch.ParentID)
channelList = append(channelList, ch)
if ch.Name == "Test" {
targetChan = ch
}
}
c.emitEvent("channel_list", map[string]any{
"channels": channelList,
})
if targetChan == nil {
if ch, ok := c.Channels[2]; ok {
@@ -226,34 +236,65 @@ func (c *Client) handleCommand(pkt *protocol.Packet) error {
case "notifycliententerview":
// A client entered the server
nick := ""
clientID := uint16(0)
channelID := uint64(0)
if n, ok := args["client_nickname"]; ok {
nick = protocol.Unescape(n)
log.Printf("Client entered: %s", nick)
}
if cid, ok := args["clid"]; ok {
var id uint64
fmt.Sscanf(cid, "%d", &id)
clientID = uint16(id)
}
if ctid, ok := args["ctid"]; ok {
fmt.Sscanf(ctid, "%d", &channelID)
}
log.Printf("Client entered: %s (ID=%d)", nick, clientID)
c.emitEvent("client_enter", map[string]any{
"clientID": clientID,
"nickname": nick,
"channelID": channelID,
})
case "notifytextmessage":
// targetmode: 1=Private, 2=Channel, 3=Server
msg := ""
invoker := "Unknown"
var invokerID uint16
var targetModeInt int
if m, ok := args["msg"]; ok {
msg = protocol.Unescape(m)
}
if name, ok := args["invokername"]; ok {
invoker = protocol.Unescape(name)
}
if iid, ok := args["invokerid"]; ok {
var id uint64
fmt.Sscanf(iid, "%d", &id)
invokerID = uint16(id)
}
targetMode := "Unknown"
if tm, ok := args["targetmode"]; ok {
switch tm {
case "1":
targetMode = "Private"
targetModeInt = 1
case "2":
targetMode = "Channel"
targetModeInt = 2
case "3":
targetMode = "Server"
targetModeInt = 3
}
}
log.Printf("[Chat][%s] %s: %s", targetMode, invoker, msg)
c.emitEvent("message", map[string]any{
"senderID": invokerID,
"senderName": invoker,
"message": msg,
"targetMode": targetModeInt,
})
case "notifyclientchatcomposing":
// Someone is typing
@@ -266,10 +307,21 @@ func (c *Client) handleCommand(pkt *protocol.Packet) error {
case "notifyclientmoved":
// Client moved to another channel
clid := args["clid"]
ctid := args["ctid"]
// reasonid: 0=switched, 1=moved, 2=timeout, 3=kick, 4=unknown
log.Printf("Client %s moved to Channel %s", clid, ctid)
var clientID uint16
var channelID uint64
if cid, ok := args["clid"]; ok {
var id uint64
fmt.Sscanf(cid, "%d", &id)
clientID = uint16(id)
}
if ctid, ok := args["ctid"]; ok {
fmt.Sscanf(ctid, "%d", &channelID)
}
log.Printf("Client %d moved to Channel %d", clientID, channelID)
c.emitEvent("client_moved", map[string]any{
"clientID": clientID,
"channelID": channelID,
})
case "notifyclientchannelgroupchanged":
// Client channel group changed
@@ -306,6 +358,40 @@ func (c *Client) handleCommand(pkt *protocol.Packet) error {
return c.SendCommand(cmd)
case "notifyclientleftview":
// Client left the server
var clientID uint16
reason := ""
if cid, ok := args["clid"]; ok {
var id uint64
fmt.Sscanf(cid, "%d", &id)
clientID = uint16(id)
}
if rid, ok := args["reasonid"]; ok {
switch rid {
case "3":
reason = "connection lost"
case "5":
reason = "kicked"
case "6":
reason = "banned"
case "8":
reason = "leaving"
default:
reason = "unknown"
}
}
if rmsg, ok := args["reasonmsg"]; ok {
if rmsg != "" {
reason = protocol.Unescape(rmsg)
}
}
log.Printf("Client %d left: %s", clientID, reason)
c.emitEvent("client_left", map[string]any{
"clientID": clientID,
"reason": reason,
})
case "notifyclientupdated":
// Client updated (e.g. muted/unmuted)
clid := args["clid"]
@@ -316,6 +402,10 @@ func (c *Client) handleCommand(pkt *protocol.Packet) error {
id := args["id"]
msg := protocol.Unescape(args["msg"])
log.Printf("SERVER ERROR: ID=%s MSG=%s", id, msg)
c.emitEvent("error", map[string]any{
"id": id,
"message": msg,
})
case "notifyservergrouplist", "notifychannelgrouplist", "notifyclientneededpermissions":
// Ignore verbose noisy setup commands