Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
665ccf1259
commit
aa6fdff11e
3 changed files with 75 additions and 10 deletions
|
|
@ -70,5 +70,5 @@ type AudioChunk struct {
|
|||
type VoiceControl struct {
|
||||
SessionID string `json:"session_id"`
|
||||
Type string `json:"type"` // "state", "command"
|
||||
Action string `json:"action"` // "idle", "listening", "start", "stop"
|
||||
Action string `json:"action"` // "idle", "listening", "start", "stop", "leave"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,17 +73,46 @@ func streamOggOpusToDiscord(ctx context.Context, vc *discordgo.VoiceConnection,
|
|||
func (c *DiscordChannel) receiveVoice(vc *discordgo.VoiceConnection, guildID string, chatID string) {
|
||||
logger.InfoCF("discord", "Started listening for voice", map[string]any{"guild": guildID})
|
||||
|
||||
go func() {
|
||||
time.Sleep(250 * time.Millisecond) // Wait a bit for connection to settle
|
||||
vc.Speaking(true)
|
||||
for i := 0; i < 5; i++ {
|
||||
vc.OpusSend <- []byte{0xF8, 0xFF, 0xFE}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
go func(ctx context.Context, vc *discordgo.VoiceConnection) {
|
||||
// Recover from potential panics if OpusSend is closed mid-send.
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
logger.WarnCF("discord", "Recovered from panic while sending wake-up frames", map[string]any{
|
||||
"error": rec,
|
||||
"guild": guildID,
|
||||
})
|
||||
}
|
||||
vc.Speaking(false)
|
||||
logger.DebugCF("discord", "Sent wake-up silence frames", nil)
|
||||
}()
|
||||
|
||||
// If the voice connection or OpusSend are not available, nothing to do.
|
||||
if vc == nil || vc.OpusSend == nil {
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(250 * time.Millisecond) // Wait a bit for connection to settle
|
||||
|
||||
// Abort if the context has already been cancelled.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
vc.Speaking(true)
|
||||
defer vc.Speaking(false)
|
||||
|
||||
silenceFrame := []byte{0xF8, 0xFF, 0xFE}
|
||||
for i := 0; i < 5; i++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case vc.OpusSend <- silenceFrame:
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
|
||||
logger.DebugCF("discord", "Sent wake-up silence frames", map[string]any{"guild": guildID})
|
||||
}(c.ctx, vc)
|
||||
sessionID := fmt.Sprintf("discord_vc_%s", guildID)
|
||||
|
||||
c.bus.PublishVoiceControl(c.ctx, bus.VoiceControl{
|
||||
|
|
|
|||
|
|
@ -29,12 +29,48 @@ type OpenAITTSProvider struct {
|
|||
}
|
||||
|
||||
func NewOpenAITTSProvider(apiKey string, apiBase string, proxyURL string) *OpenAITTSProvider {
|
||||
if apiBase == "" || apiBase == "https://api.openai.com/v1" {
|
||||
// Normalize apiBase to avoid malformed endpoints like
|
||||
// "https://api.openai.com/audio/speech" when "/v1" is required.
|
||||
if apiBase == "" {
|
||||
apiBase = "https://api.openai.com/v1/audio/speech"
|
||||
} else {
|
||||
if u, err := url.Parse(apiBase); err == nil && u.Scheme != "" && u.Host != "" {
|
||||
path := u.Path
|
||||
if u.Host == "api.openai.com" {
|
||||
// For the official OpenAI host, ensure exactly one /v1 prefix and
|
||||
// that the path ends with /audio/speech.
|
||||
if path == "" || path == "/" || path == "/v1" {
|
||||
path = "/v1/audio/speech"
|
||||
} else {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
}
|
||||
if !strings.HasPrefix(path, "/v1/") {
|
||||
path = "/v1" + strings.TrimSuffix(path, "/")
|
||||
}
|
||||
if !strings.HasSuffix(path, "/audio/speech") {
|
||||
path = strings.TrimSuffix(path, "/") + "/audio/speech"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For non-OpenAI hosts (e.g., proxies), preserve the existing base
|
||||
// path and only ensure it ends with /audio/speech.
|
||||
if !strings.HasSuffix(path, "/audio/speech") {
|
||||
path = strings.TrimSuffix(path, "/") + "/audio/speech"
|
||||
}
|
||||
}
|
||||
u.Path = path
|
||||
apiBase = u.String()
|
||||
} else {
|
||||
// Fallback to the previous string-based behavior if parsing fails.
|
||||
if apiBase == "https://api.openai.com/v1" {
|
||||
apiBase = "https://api.openai.com/v1/audio/speech"
|
||||
} else if !strings.HasSuffix(apiBase, "/audio/speech") {
|
||||
// Just in case they provide openrouter base or standard base
|
||||
apiBase = strings.TrimSuffix(apiBase, "/") + "/audio/speech"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 60 * time.Second,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue