From e31fe390f3a21e65a47aaa8e916ae76440df9891 Mon Sep 17 00:00:00 2001 From: chenchen Date: Sun, 17 May 2026 23:09:32 +0800 Subject: [PATCH] fix: use common.Marshal/Unmarshal per Rule 1 + telegram null safety - model/user.go: replace json.Unmarshal/Marshal with common.* wrapper functions as required by project Rule 1 (3 occurrences) - relay/channel/claude/relay-claude.go: replace 3 json.* calls with common.* (tool call args unmarshal, response marshal) - relay/channel/gemini/relay-gemini.go: replace 5 json.* calls with common.* (content parsing, function args, response marshal) - adapters/telegram/index.ts: add optional chaining on callback query message.chat.id and null coalescing on message.text to prevent crash when callback message is undefined Co-Authored-By: Claude Opus 4.6 --- cc-haha/adapters/telegram/index.ts | 4 ++-- heicode/model/user.go | 7 +++---- heicode/relay/channel/claude/relay-claude.go | 6 +++--- heicode/relay/channel/gemini/relay-gemini.go | 12 +++++------- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/cc-haha/adapters/telegram/index.ts b/cc-haha/adapters/telegram/index.ts index aa84676..59b931c 100644 --- a/cc-haha/adapters/telegram/index.ts +++ b/cc-haha/adapters/telegram/index.ts @@ -718,7 +718,7 @@ bot.on('callback_query:data', async (ctx) => { const requestId = parts[1]! const allowed = parts[2] === 'yes' - const chatId = String(ctx.callbackQuery.message?.chat.id) + const chatId = String(ctx.callbackQuery.message?.chat?.id) bridge.sendPermissionResponse(chatId, requestId, allowed) const runtime = getRuntimeState(chatId) @@ -727,7 +727,7 @@ bot.on('callback_query:data', async (ctx) => { const statusText = allowed ? '✅ 已允许' : '❌ 已拒绝' try { await ctx.editMessageText( - ctx.callbackQuery.message?.text + `\n\n${statusText}`, + (ctx.callbackQuery.message?.text ?? '') + `\n\n${statusText}`, ) } catch { /* ignore */ } diff --git a/heicode/model/user.go b/heicode/model/user.go index 7dcc45a..edf4d34 100644 --- a/heicode/model/user.go +++ b/heicode/model/user.go @@ -2,7 +2,6 @@ package model import ( "database/sql" - "encoding/json" "errors" "fmt" "strconv" @@ -81,7 +80,7 @@ func (user *User) SetAccessToken(token string) { func (user *User) GetSetting() dto.UserSetting { setting := dto.UserSetting{} if user.Setting != "" { - err := json.Unmarshal([]byte(user.Setting), &setting) + err := common.Unmarshal([]byte(user.Setting), &setting) if err != nil { common.SysLog("failed to unmarshal setting: " + err.Error()) } @@ -90,7 +89,7 @@ func (user *User) GetSetting() dto.UserSetting { } func (user *User) SetSetting(setting dto.UserSetting) { - settingBytes, err := json.Marshal(setting) + settingBytes, err := common.Marshal(setting) if err != nil { common.SysLog("failed to marshal setting: " + err.Error()) return @@ -151,7 +150,7 @@ func generateDefaultSidebarConfigForRole(userRole int) string { // 普通用户不包含admin区域 // 转换为JSON字符串 - configBytes, err := json.Marshal(defaultConfig) + configBytes, err := common.Marshal(defaultConfig) if err != nil { common.SysLog("生成默认边栏配置失败: " + err.Error()) return "" diff --git a/heicode/relay/channel/claude/relay-claude.go b/heicode/relay/channel/claude/relay-claude.go index 4ef4307..ea49fd9 100644 --- a/heicode/relay/channel/claude/relay-claude.go +++ b/heicode/relay/channel/claude/relay-claude.go @@ -406,7 +406,7 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe if message.ToolCalls != nil { for _, toolCall := range message.ParseToolCalls() { inputObj := make(map[string]any) - if err := json.Unmarshal([]byte(toolCall.Function.Arguments), &inputObj); err != nil { + if err := common.Unmarshal([]byte(toolCall.Function.Arguments), &inputObj); err != nil { common.SysLog("tool call function arguments is not a map[string]any: " + fmt.Sprintf("%v", toolCall.Function.Arguments)) continue } @@ -540,7 +540,7 @@ func ResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.OpenAITextRe for _, message := range claudeResponse.Content { switch message.Type { case "tool_use": - args, _ := json.Marshal(message.Input) + args, _ := common.Marshal(message.Input) tools = append(tools, dto.ToolCallResponse{ ID: message.Id, Type: "function", // compatible with other OpenAI derivative applications @@ -919,7 +919,7 @@ func HandleClaudeResponseData(c *gin.Context, info *relaycommon.RelayInfo, claud case types.RelayFormatOpenAI: openaiResponse := ResponseClaude2OpenAI(&claudeResponse) openaiResponse.Usage = buildOpenAIStyleUsageFromClaudeUsage(claudeInfo.Usage) - responseData, err = json.Marshal(openaiResponse) + responseData, err = common.Marshal(openaiResponse) if err != nil { return types.NewError(err, types.ErrorCodeBadResponseBody) } diff --git a/heicode/relay/channel/gemini/relay-gemini.go b/heicode/relay/channel/gemini/relay-gemini.go index 4a1b447..bcb8280 100644 --- a/heicode/relay/channel/gemini/relay-gemini.go +++ b/heicode/relay/channel/gemini/relay-gemini.go @@ -467,10 +467,10 @@ func CovertOpenAI2Gemini(c *gin.Context, textRequest dto.GeneralOpenAIRequest, i contentStr := message.StringContent() // 1. 尝试解析为 JSON 对象 - if err := json.Unmarshal([]byte(contentStr), &contentMap); err != nil { + if err := common.Unmarshal([]byte(contentStr), &contentMap); err != nil { // 2. 如果失败,尝试解析为 JSON 数组 var contentSlice []interface{} - if err := json.Unmarshal([]byte(contentStr), &contentSlice); err == nil { + if err := common.Unmarshal([]byte(contentStr), &contentSlice); err == nil { // 如果是数组,包装成对象 contentMap = map[string]interface{}{"result": contentSlice} } else { @@ -502,7 +502,7 @@ func CovertOpenAI2Gemini(c *gin.Context, textRequest dto.GeneralOpenAIRequest, i for _, call := range message.ParseToolCalls() { args := map[string]interface{}{} if call.Function.Arguments != "" { - if json.Unmarshal([]byte(call.Function.Arguments), &args) != nil { + if common.Unmarshal([]byte(call.Function.Arguments), &args) != nil { return nil, fmt.Errorf("invalid arguments for function %s, args: %s", call.Function.Name, call.Function.Arguments) } } @@ -994,9 +994,7 @@ func unescapeMapOrSlice(data interface{}) interface{} { func getResponseToolCall(item *dto.GeminiPart) *dto.ToolCallResponse { var argsBytes []byte var err error - // 移除 unescapeMapOrSlice 调用,直接使用 json.Marshal - // JSON 序列化/反序列化已经正确处理了转义字符 - argsBytes, err = json.Marshal(item.FunctionCall.Arguments) + argsBytes, err = common.Marshal(item.FunctionCall.Arguments) if err != nil { return nil @@ -1569,7 +1567,7 @@ func GeminiImageHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http. }) } - jsonResponse, jsonErr := json.Marshal(openAIResponse) + jsonResponse, jsonErr := common.Marshal(openAIResponse) if jsonErr != nil { return nil, types.NewError(jsonErr, types.ErrorCodeBadResponseBody) }