Edit from coplilot review

This commit is contained in:
Huaaudio 2026-03-21 09:31:16 +01:00
parent 9ebdb0ad57
commit 2d065f9ab3
4 changed files with 18 additions and 52 deletions

View file

@ -57,6 +57,7 @@ type AudioChunk struct {
SessionID string `json:"session_id"`
SpeakerID string `json:"speaker_id"` // User ID or SSRC
ChatID string `json:"chat_id"` // Where to respond
Channel string `json:"channel"` // Source channel type (e.g. "discord")
Sequence uint64 `json:"sequence"`
Timestamp uint32 `json:"timestamp"`
SampleRate int `json:"sample_rate"`

View file

@ -162,7 +162,7 @@ func (c *DiscordChannel) Send(ctx context.Context, msg bus.OutboundMessage) erro
if c.cancelTTS != nil {
c.cancelTTS()
}
ttsCtx, ttsCancel := context.WithCancel(context.Background())
ttsCtx, ttsCancel := context.WithCancel(c.ctx)
c.cancelTTS = ttsCancel
c.ttsMu.Unlock()
@ -651,7 +651,10 @@ func (c *DiscordChannel) listenVoiceControl(ctx context.Context) {
select {
case <-ctx.Done():
return
case ctrl := <-c.bus.VoiceControlsChan():
case ctrl, ok := <-c.bus.VoiceControlsChan():
if !ok {
return
}
if ctrl.Type == "command" && ctrl.Action == "leave" {
if strings.HasPrefix(ctrl.SessionID, "discord_vc_") {
guildID := strings.TrimPrefix(ctrl.SessionID, "discord_vc_")

View file

@ -1,7 +1,6 @@
package discord
import (
"bytes"
"context"
"fmt"
"io"
@ -9,6 +8,7 @@ import (
"github.com/bwmarrin/discordgo"
"github.com/sipeed/picoclaw/pkg/audio"
"github.com/sipeed/picoclaw/pkg/bus"
"github.com/sipeed/picoclaw/pkg/logger"
)
@ -60,58 +60,14 @@ func streamOggOpusToDiscord(ctx context.Context, vc *discordgo.VoiceConnection,
vc.Speaking(true)
defer vc.Speaking(false)
var packet []byte
header := make([]byte, 27)
for {
// Check for interruption
return audio.DecodeOggOpus(r, func(frame []byte) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
if _, err := io.ReadFull(r, header); err != nil {
if err == io.EOF || err == io.ErrUnexpectedEOF {
case vc.OpusSend <- frame:
return nil
}
return fmt.Errorf("failed to read ogg header: %w", err)
}
if string(header[:4]) != "OggS" {
return fmt.Errorf("invalid ogg magic string")
}
pageSegments := int(header[26])
segmentTable := make([]byte, pageSegments)
if _, err := io.ReadFull(r, segmentTable); err != nil {
return fmt.Errorf("failed to read segment table: %w", err)
}
for _, lacing := range segmentTable {
segment := make([]byte, lacing)
if _, err := io.ReadFull(r, segment); err != nil {
return fmt.Errorf("failed to read segment data: %w", err)
}
packet = append(packet, segment...)
// If lacing is less than 255, the packet is complete
if lacing < 255 {
if len(packet) > 0 {
// Ignore Ogg Opus headers
if !bytes.HasPrefix(packet, []byte("OpusHead")) && !bytes.HasPrefix(packet, []byte("OpusTags")) {
select {
case <-ctx.Done():
return ctx.Err()
case vc.OpusSend <- packet:
}
}
// Start new packet
packet = nil
}
}
}
}
})
}
func (c *DiscordChannel) receiveVoice(vc *discordgo.VoiceConnection, guildID string, chatID string) {
@ -193,6 +149,7 @@ func (c *DiscordChannel) receiveVoice(vc *discordgo.VoiceConnection, guildID str
SessionID: sessionID,
SpeakerID: fmt.Sprintf("%d", p.SSRC),
ChatID: chatID,
Channel: "discord",
Sequence: sequence,
Timestamp: p.Timestamp,
SampleRate: 48000,

View file

@ -26,6 +26,7 @@ type speechAccumulator struct {
chatID string
speakerID string
sessionID string
channel string
}
func (a *speechAccumulator) Push(chunk bus.AudioChunk) {
@ -117,6 +118,7 @@ func (a *Agent) handleChunk(chunk bus.AudioChunk) {
chatID: chunk.ChatID,
speakerID: chunk.SpeakerID,
sessionID: chunk.SessionID,
channel: chunk.Channel,
}
a.sessions[key] = acc
logger.DebugCF("voice-agent", "Started accumulating voice", map[string]any{"key": key, "file": filename})
@ -185,7 +187,10 @@ func (a *Agent) processUtterance(ctx context.Context, acc *speechAccumulator) {
logger.InfoCF("voice-agent", "Transcription result", map[string]any{"text": res.Text, "duration": res.Duration})
channelType := "discord"
channelType := acc.channel
if channelType == "" {
channelType = "discord" // fallback for legacy chunks
}
text := strings.ToLower(strings.TrimSpace(res.Text))
if strings.Contains(text, "leave the voice channel") || strings.Contains(text, "leave voice") ||