This commit is contained in:
HugeFrog24
2026-05-25 22:56:12 +02:00
parent 8c699ab70a
commit b22b8b98fe
4 changed files with 95 additions and 19 deletions
+42 -10
View File
@@ -55,7 +55,7 @@ func (b *Bot) handleVoiceMessage(ctx context.Context, message *models.Message, u
chatMemory := b.getOrCreateChatMemory(chatID)
contextMessages := b.prepareContextMessages(chatMemory)
response, err := b.getAnthropicResponse(ctx, contextMessages, isNewChat, isOwner, false, username, firstName, lastName, isPremium, languageCode, messageTime)
segments, err := b.getAnthropicResponse(ctx, contextMessages, isNewChat, isOwner, false, username, firstName, lastName, isPremium, languageCode, messageTime)
if err != nil {
ErrorLogger.Printf("Error getting Anthropic response for voice: %v", err)
if err := b.sendResponse(ctx, chatID, b.anthropicErrorResponse(err, userID), businessConnectionID); err != nil {
@@ -64,6 +64,10 @@ func (b *Bot) handleVoiceMessage(ctx context.Context, message *models.Message, u
return
}
// Voice replies always synthesize as one audio clip — tool-call narration
// across multiple TTS clips would be jarring, so we join here.
response := strings.Join(segments, "\n\n")
audioReader, err := b.generateSpeech(ctx, response)
if err != nil {
// TTS failed — fall back to text so the user still gets a reply.
@@ -92,16 +96,37 @@ func (b *Bot) handleVoiceMessage(ctx context.Context, message *models.Message, u
}
// anthropicErrorResponse returns the message to send back to the user when getAnthropicResponse
// fails. Admins and owners receive an actionable hint when the model is deprecated; regular users
// always get the generic fallback to avoid leaking internal details.
// fails. Admins and owners (anyone with model:set scope) receive the underlying API error so they
// can act on it — actionable hint for model-deprecation, raw status+body+request-id for everything
// else. Regular users always get the generic fallback to avoid leaking internal details.
func (b *Bot) anthropicErrorResponse(err error, userID int64) string {
if errors.Is(err, ErrModelNotFound) && b.hasScope(userID, ScopeModelSet) {
isElevated := b.hasScope(userID, ScopeModelSet)
if errors.Is(err, ErrModelNotFound) && isElevated {
return fmt.Sprintf(
"⚠️ Model `%s` is no longer available (deprecated or removed by Anthropic).\n"+
"Use /set_model <model-id> to switch. Current models: https://platform.claude.com/docs/en/about-claude/models/overview",
b.config.Model,
)
}
if isElevated {
var apiErr *anthropic.Error
if errors.As(err, &apiErr) {
body := apiErr.RawJSON()
if len(body) > 800 {
body = body[:800] + "...(truncated)"
}
out := fmt.Sprintf("⚠️ Anthropic API error %d:\n%s", apiErr.StatusCode, body)
if apiErr.RequestID != "" {
out += fmt.Sprintf("\nRequest-ID: %s", apiErr.RequestID)
}
return out
}
// Non-API errors (network, context cancel, etc.) — show the Go error text.
return fmt.Sprintf("⚠️ Anthropic call failed: %v", err)
}
return "I'm sorry, I'm having trouble processing your request right now."
}
@@ -341,14 +366,19 @@ func (b *Bot) handleUpdate(ctx context.Context, tgBot *bot.Bot, update *models.U
isEmojiOnly := isOnlyEmojis(text)
// Get response from Anthropic
response, err := b.getAnthropicResponse(ctx, contextMessages, isNewChatFlag, isOwner, isEmojiOnly, username, firstName, lastName, isPremium, languageCode, messageTime)
segments, err := b.getAnthropicResponse(ctx, contextMessages, isNewChatFlag, isOwner, isEmojiOnly, username, firstName, lastName, isPremium, languageCode, messageTime)
if err != nil {
ErrorLogger.Printf("Error getting Anthropic response: %v", err)
response = b.anthropicErrorResponse(err, userID)
// Errors go out as a single message — no need to fan out a one-line error.
if err := b.sendResponse(ctx, chatID, b.anthropicErrorResponse(err, userID), businessConnectionID); err != nil {
ErrorLogger.Printf("Error sending response: %v", err)
}
return
}
// Send the response
if err := b.sendResponse(ctx, chatID, response, businessConnectionID); err != nil {
// Successful LLM reply: deliver each text block as its own Telegram bubble,
// matching the conversational rhythm Claude uses around tool calls.
if err := b.sendMultiResponse(ctx, chatID, segments, businessConnectionID); err != nil {
ErrorLogger.Printf("Error sending response: %v", err)
return
}
@@ -389,11 +419,13 @@ func (b *Bot) generateStickerResponse(ctx context.Context, message Message, cont
// "Sent a sticker: <emoji>"), so the full conversation history is preserved.
if message.StickerFileID != "" {
messageTime := int(message.Timestamp.Unix())
response, err := b.getAnthropicResponse(ctx, contextMessages, false, false, true, message.Username, "", "", false, "", messageTime)
segments, err := b.getAnthropicResponse(ctx, contextMessages, false, false, true, message.Username, "", "", false, "", messageTime)
if err != nil {
return "", err
}
return response, nil
// Sticker reactions are casual chit-chat; tool use is unusual here, so
// join into one message rather than fanning out as multiple bubbles.
return strings.Join(segments, "\n\n"), nil
}
return "Hmm, that's interesting!", nil