Implement Poke functionality, refine talking status and add local log events
This commit is contained in:
@@ -183,6 +183,16 @@ type clientLeftMsg struct {
|
||||
clientID uint16
|
||||
}
|
||||
|
||||
type clientMovedMsg struct {
|
||||
clientID uint16
|
||||
channelID uint64
|
||||
}
|
||||
|
||||
type pokeMsg struct {
|
||||
senderName string
|
||||
message string
|
||||
}
|
||||
|
||||
type chatMsg struct {
|
||||
senderID uint16
|
||||
senderName string
|
||||
@@ -240,6 +250,20 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.audioPlayer.PlayPCM(e.SenderID, e.PCM)
|
||||
}
|
||||
})
|
||||
|
||||
m.client.On(ts3client.EventClientMoved, func(e *ts3client.ClientMovedEvent) {
|
||||
m.program.Send(clientMovedMsg{
|
||||
clientID: e.ClientID,
|
||||
channelID: e.ChannelID,
|
||||
})
|
||||
})
|
||||
|
||||
m.client.On(ts3client.EventPoke, func(e *ts3client.PokeEvent) {
|
||||
m.program.Send(pokeMsg{
|
||||
senderName: e.SenderName,
|
||||
message: e.Message,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Initialize audio player
|
||||
@@ -349,6 +373,29 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case clientMovedMsg:
|
||||
if msg.clientID == m.selfID {
|
||||
chName := "Unknown"
|
||||
if ch := m.client.GetChannel(msg.channelID); ch != nil {
|
||||
chName = ch.Name
|
||||
}
|
||||
m.chatMessages = append(m.chatMessages, ChatMessage{
|
||||
Time: time.Now(),
|
||||
Sender: "SYSTEM",
|
||||
Content: fmt.Sprintf("You moved to channel: %s", chName),
|
||||
})
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case pokeMsg:
|
||||
m.chatMessages = append(m.chatMessages, ChatMessage{
|
||||
Time: time.Now(),
|
||||
Sender: "POKE",
|
||||
Content: fmt.Sprintf("[%s]: %s", msg.senderName, msg.message),
|
||||
})
|
||||
m.addLog("Received poke from %s: %s", msg.senderName, msg.message)
|
||||
return m, nil
|
||||
|
||||
case chatMsg:
|
||||
m.chatMessages = append(m.chatMessages, ChatMessage{
|
||||
Time: time.Now(),
|
||||
@@ -569,7 +616,7 @@ func (m *Model) handleKeyPress(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *Model) handleChatKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
func (m *Model) handleChatKeys(_ tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -134,26 +134,41 @@ func (c *Client) Connect(address string) error {
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
// Recovery from panics in the main loop
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("PANIC in Client loop: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-c.done:
|
||||
log.Println("Client loop stopped")
|
||||
return
|
||||
case pkt := <-pktChan:
|
||||
if pkt == nil {
|
||||
// Channel closed
|
||||
return
|
||||
}
|
||||
if err := c.handlePacket(pkt); err != nil {
|
||||
log.Printf("Error handling packet: %v", err)
|
||||
}
|
||||
case <-ticker.C:
|
||||
if !c.Connected {
|
||||
return // Don't send pings if not connected yet
|
||||
}
|
||||
// Send KeepAlive Ping (Encrypted, No NewProtocol)
|
||||
if err := c.sendPing(); err != nil {
|
||||
log.Printf("Error sending Ping: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
// Check if we should exit after the inner function
|
||||
select {
|
||||
case <-c.done:
|
||||
log.Println("Client loop stopped")
|
||||
return nil
|
||||
case pkt := <-pktChan:
|
||||
if pkt == nil {
|
||||
// Channel closed
|
||||
return nil
|
||||
}
|
||||
if err := c.handlePacket(pkt); err != nil {
|
||||
log.Printf("Error handling packet: %v", err)
|
||||
}
|
||||
case <-ticker.C:
|
||||
if !c.Connected {
|
||||
continue // Don't send pings if not connected yet
|
||||
}
|
||||
// Send KeepAlive Ping (Encrypted, No NewProtocol)
|
||||
if err := c.sendPing(); err != nil {
|
||||
log.Printf("Error sending Ping: %v", err)
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,8 +226,8 @@ func (c *Client) processCommand(data []byte, pkt *protocol.Packet) error {
|
||||
cmdStr := string(data)
|
||||
|
||||
// Debug: Log packet flags and raw command preview (sanitized)
|
||||
log.Printf("Debug Packet: Compressed=%v, Fragmented=%v, RawLen=%d, Preview=%q",
|
||||
pkt.Header.FlagCompressed(), pkt.Header.FlagFragmented(), len(data),
|
||||
log.Printf("Debug Packet: PID=%d, Compressed=%v, Fragmented=%v, RawLen=%d, Preview=%q",
|
||||
pkt.Header.PacketID, pkt.Header.FlagCompressed(), pkt.Header.FlagFragmented(), len(data),
|
||||
func() string {
|
||||
preview := cmdStr
|
||||
if len(preview) > 100 {
|
||||
@@ -544,6 +544,28 @@ func (c *Client) processCommand(data []byte, pkt *protocol.Packet) error {
|
||||
"message": msg,
|
||||
})
|
||||
|
||||
case "notifyclientpoke":
|
||||
msg := ""
|
||||
invoker := "Unknown"
|
||||
var invokerID uint16
|
||||
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)
|
||||
}
|
||||
log.Printf("[Poke] %s: %s", invoker, msg)
|
||||
c.emitEvent("client_poke", map[string]any{
|
||||
"senderID": invokerID,
|
||||
"senderName": invoker,
|
||||
"message": msg,
|
||||
})
|
||||
|
||||
case "notifyservergrouplist", "notifychannelgrouplist", "notifyclientneededpermissions":
|
||||
// Ignore verbose noisy setup commands
|
||||
default:
|
||||
|
||||
@@ -124,6 +124,10 @@ func (c *Client) emit(event EventType, data any) {
|
||||
if fn, ok := h.(func(*TalkingStatusEvent)); ok {
|
||||
fn(data.(*TalkingStatusEvent))
|
||||
}
|
||||
case EventPoke:
|
||||
if fn, ok := h.(func(*PokeEvent)); ok {
|
||||
fn(data.(*PokeEvent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,6 +409,13 @@ func (c *Client) handleInternalEvent(eventType string, data map[string]any) {
|
||||
Channels: getInt(data, "channels"),
|
||||
})
|
||||
|
||||
case "client_poke":
|
||||
c.emit(EventPoke, &PokeEvent{
|
||||
SenderID: getUint16(data, "senderID"),
|
||||
SenderName: getString(data, "senderName"),
|
||||
Message: getString(data, "message"),
|
||||
})
|
||||
|
||||
case "error":
|
||||
c.emit(EventError, &ErrorEvent{
|
||||
ID: getString(data, "id"),
|
||||
|
||||
@@ -25,8 +25,18 @@ const (
|
||||
|
||||
// Error events
|
||||
EventError EventType = "error"
|
||||
|
||||
// Poke events
|
||||
EventPoke EventType = "poke"
|
||||
)
|
||||
|
||||
// PokeEvent is emitted when a poke message is received
|
||||
type PokeEvent struct {
|
||||
SenderID uint16
|
||||
SenderName string
|
||||
Message string
|
||||
}
|
||||
|
||||
// ConnectedEvent is emitted when the client successfully connects
|
||||
type ConnectedEvent struct {
|
||||
ClientID uint16
|
||||
|
||||
Reference in New Issue
Block a user