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
|
clientID uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type clientMovedMsg struct {
|
||||||
|
clientID uint16
|
||||||
|
channelID uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type pokeMsg struct {
|
||||||
|
senderName string
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
type chatMsg struct {
|
type chatMsg struct {
|
||||||
senderID uint16
|
senderID uint16
|
||||||
senderName string
|
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.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
|
// Initialize audio player
|
||||||
@@ -349,6 +373,29 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
return m, nil
|
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:
|
case chatMsg:
|
||||||
m.chatMessages = append(m.chatMessages, ChatMessage{
|
m.chatMessages = append(m.chatMessages, ChatMessage{
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
@@ -569,7 +616,7 @@ func (m *Model) handleKeyPress(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|||||||
return m, nil
|
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
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,27 +134,42 @@ func (c *Client) Connect(address string) error {
|
|||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
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 {
|
select {
|
||||||
case <-c.done:
|
case <-c.done:
|
||||||
log.Println("Client loop stopped")
|
log.Println("Client loop stopped")
|
||||||
return nil
|
return
|
||||||
case pkt := <-pktChan:
|
case pkt := <-pktChan:
|
||||||
if pkt == nil {
|
if pkt == nil {
|
||||||
// Channel closed
|
// Channel closed
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
if err := c.handlePacket(pkt); err != nil {
|
if err := c.handlePacket(pkt); err != nil {
|
||||||
log.Printf("Error handling packet: %v", err)
|
log.Printf("Error handling packet: %v", err)
|
||||||
}
|
}
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
if !c.Connected {
|
if !c.Connected {
|
||||||
continue // Don't send pings if not connected yet
|
return // Don't send pings if not connected yet
|
||||||
}
|
}
|
||||||
// Send KeepAlive Ping (Encrypted, No NewProtocol)
|
// Send KeepAlive Ping (Encrypted, No NewProtocol)
|
||||||
if err := c.sendPing(); err != nil {
|
if err := c.sendPing(); err != nil {
|
||||||
log.Printf("Error sending Ping: %v", err)
|
log.Printf("Error sending Ping: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
// Check if we should exit after the inner function
|
||||||
|
select {
|
||||||
|
case <-c.done:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -226,8 +226,8 @@ func (c *Client) processCommand(data []byte, pkt *protocol.Packet) error {
|
|||||||
cmdStr := string(data)
|
cmdStr := string(data)
|
||||||
|
|
||||||
// Debug: Log packet flags and raw command preview (sanitized)
|
// Debug: Log packet flags and raw command preview (sanitized)
|
||||||
log.Printf("Debug Packet: Compressed=%v, Fragmented=%v, RawLen=%d, Preview=%q",
|
log.Printf("Debug Packet: PID=%d, Compressed=%v, Fragmented=%v, RawLen=%d, Preview=%q",
|
||||||
pkt.Header.FlagCompressed(), pkt.Header.FlagFragmented(), len(data),
|
pkt.Header.PacketID, pkt.Header.FlagCompressed(), pkt.Header.FlagFragmented(), len(data),
|
||||||
func() string {
|
func() string {
|
||||||
preview := cmdStr
|
preview := cmdStr
|
||||||
if len(preview) > 100 {
|
if len(preview) > 100 {
|
||||||
@@ -544,6 +544,28 @@ func (c *Client) processCommand(data []byte, pkt *protocol.Packet) error {
|
|||||||
"message": msg,
|
"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":
|
case "notifyservergrouplist", "notifychannelgrouplist", "notifyclientneededpermissions":
|
||||||
// Ignore verbose noisy setup commands
|
// Ignore verbose noisy setup commands
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -124,6 +124,10 @@ func (c *Client) emit(event EventType, data any) {
|
|||||||
if fn, ok := h.(func(*TalkingStatusEvent)); ok {
|
if fn, ok := h.(func(*TalkingStatusEvent)); ok {
|
||||||
fn(data.(*TalkingStatusEvent))
|
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"),
|
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":
|
case "error":
|
||||||
c.emit(EventError, &ErrorEvent{
|
c.emit(EventError, &ErrorEvent{
|
||||||
ID: getString(data, "id"),
|
ID: getString(data, "id"),
|
||||||
|
|||||||
@@ -25,8 +25,18 @@ const (
|
|||||||
|
|
||||||
// Error events
|
// Error events
|
||||||
EventError EventType = "error"
|
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
|
// ConnectedEvent is emitted when the client successfully connects
|
||||||
type ConnectedEvent struct {
|
type ConnectedEvent struct {
|
||||||
ClientID uint16
|
ClientID uint16
|
||||||
|
|||||||
Reference in New Issue
Block a user