package lua import ( "cs-bridge/pkg/httpclient" "cs-bridge/pkg/logger" "encoding/base64" "encoding/json" "fmt" "io" "net/url" lua "github.com/yuin/gopher-lua" "resty.dev/v3" ) // RegisterHTTPModule registers the http module in Lua func RegisterHTTPModule(L *lua.LState) { httpMod := L.NewTable() // http.get(url, headers) httpMod.RawSetString("get", L.NewFunction(luaHTTPGet)) // http.post(url, data, headers) httpMod.RawSetString("post", L.NewFunction(luaHTTPPost)) // http.put(url, data, headers) httpMod.RawSetString("put", L.NewFunction(luaHTTPPut)) // http.delete(url, headers) httpMod.RawSetString("delete", L.NewFunction(luaHTTPDelete)) L.SetGlobal("http", httpMod) } // luaHTTPGet implements http.get(url, headers) func luaHTTPGet(L *lua.LState) int { url := L.CheckString(1) headers := L.OptTable(2, nil) req := httpclient.Default.R() // Set headers if provided if headers != nil { headers.ForEach(func(key, value lua.LValue) { req.SetHeader(key.String(), value.String()) }) } resp, err := req.Get(url) if err != nil { L.Push(lua.LNil) L.Push(lua.LString(err.Error())) return 2 } result := parseHTTPResponse(L, resp) L.Push(result) return 1 } // luaHTTPPost implements http.post(url, data, headers) func luaHTTPPost(L *lua.LState) int { url := L.CheckString(1) data := L.CheckTable(2) headers := L.OptTable(3, nil) req := httpclient.Default.R() // Convert Lua table to map for request body bodyMap := luaTableToMap(data) req.SetBody(bodyMap) req.SetHeader("Content-Type", "application/json") // Set additional headers if provided if headers != nil { headers.ForEach(func(key, value lua.LValue) { req.SetHeader(key.String(), value.String()) }) } resp, err := req.Post(url) if err != nil { L.Push(lua.LNil) L.Push(lua.LString(err.Error())) return 2 } result := parseHTTPResponse(L, resp) L.Push(result) return 1 } // luaHTTPPut implements http.put(url, data, headers) func luaHTTPPut(L *lua.LState) int { url := L.CheckString(1) data := L.CheckTable(2) headers := L.OptTable(3, nil) req := httpclient.Default.R() bodyMap := luaTableToMap(data) req.SetBody(bodyMap) req.SetHeader("Content-Type", "application/json") if headers != nil { headers.ForEach(func(key, value lua.LValue) { req.SetHeader(key.String(), value.String()) }) } resp, err := req.Put(url) if err != nil { L.Push(lua.LNil) L.Push(lua.LString(err.Error())) return 2 } result := parseHTTPResponse(L, resp) L.Push(result) return 1 } // luaHTTPDelete implements http.delete(url, headers) func luaHTTPDelete(L *lua.LState) int { url := L.CheckString(1) headers := L.OptTable(2, nil) req := httpclient.Default.R() if headers != nil { headers.ForEach(func(key, value lua.LValue) { req.SetHeader(key.String(), value.String()) }) } resp, err := req.Delete(url) if err != nil { L.Push(lua.LNil) L.Push(lua.LString(err.Error())) return 2 } result := parseHTTPResponse(L, resp) L.Push(result) return 1 } // parseHTTPResponse parses HTTP response and returns Lua table func parseHTTPResponse(L *lua.LState, resp *resty.Response) lua.LValue { // Read response body body, err := io.ReadAll(resp.Body) if err != nil { logger.GetLogger().Error(fmt.Sprintf("读取响应体失败: %v", err)) // Return error table result := L.NewTable() result.RawSetString("error", lua.LString(err.Error())) result.RawSetString("status", lua.LNumber(resp.StatusCode())) return result } defer resp.Body.Close() // Log response for debugging logger.GetLogger().Debug(fmt.Sprintf("HTTP响应 [%d]: %s", resp.StatusCode(), string(body))) // Try to parse as JSON first var jsonData any if err := json.Unmarshal(body, &jsonData); err == nil { logger.GetLogger().Debug(fmt.Sprintf("JSON解析成功,类型: %T, 值: %+v", jsonData, jsonData)) result := GoToLua(L, jsonData) logger.GetLogger().Debug(fmt.Sprintf("转换为Lua后类型: %s", result.Type().String())) // Try to access a test field if it's a table if tbl, ok := result.(*lua.LTable); ok { testVal := tbl.RawGetString("access_token") logger.GetLogger().Debug(fmt.Sprintf("测试访问access_token: %s (type: %s)", testVal.String(), testVal.Type().String())) } return result } // If not JSON, return as string in a table logger.GetLogger().Debug(fmt.Sprintf("响应不是有效JSON: %v", err)) result := L.NewTable() result.RawSetString("body", lua.LString(string(body))) result.RawSetString("status", lua.LNumber(resp.StatusCode())) return result } // luaTableToMap converts a Lua table to a Go map func luaTableToMap(t *lua.LTable) map[string]any { result := make(map[string]any) t.ForEach(func(key, value lua.LValue) { result[key.String()] = luaValueToGo(value) }) return result } // luaValueToGo converts a Lua value to a Go value func luaValueToGo(lv lua.LValue) any { switch v := lv.(type) { case *lua.LNilType: return nil case lua.LBool: return bool(v) case lua.LNumber: return float64(v) case lua.LString: return string(v) case *lua.LTable: // Check if it's an array or map if v.Len() > 0 { // Array arr := make([]any, 0, v.Len()) for i := 1; i <= v.Len(); i++ { arr = append(arr, luaValueToGo(v.RawGetInt(i))) } return arr } // Map m := make(map[string]any) v.ForEach(func(key, value lua.LValue) { m[key.String()] = luaValueToGo(value) }) return m default: return nil } } // RegisterJSONModule registers the json module in Lua func RegisterJSONModule(L *lua.LState) { jsonMod := L.NewTable() // json.encode(table) jsonMod.RawSetString("encode", L.NewFunction(luaJSONEncode)) // json.decode(string) jsonMod.RawSetString("decode", L.NewFunction(luaJSONDecode)) L.SetGlobal("json", jsonMod) } // luaJSONEncode implements json.encode(value) func luaJSONEncode(L *lua.LState) int { value := L.CheckAny(1) goValue := luaValueToGo(value) jsonBytes, err := json.Marshal(goValue) if err != nil { L.Push(lua.LNil) L.Push(lua.LString(err.Error())) return 2 } L.Push(lua.LString(string(jsonBytes))) return 1 } // luaJSONDecode implements json.decode(jsonString) func luaJSONDecode(L *lua.LState) int { jsonStr := L.CheckString(1) var data any if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { L.Push(lua.LNil) L.Push(lua.LString(err.Error())) return 2 } L.Push(GoToLua(L, data)) return 1 } // RegisterLogModule registers the log module in Lua func RegisterLogModule(L *lua.LState) { logMod := L.NewTable() logMod.RawSetString("debug", L.NewFunction(luaLogDebug)) logMod.RawSetString("info", L.NewFunction(luaLogInfo)) logMod.RawSetString("warn", L.NewFunction(luaLogWarn)) logMod.RawSetString("error", L.NewFunction(luaLogError)) L.SetGlobal("log", logMod) } // luaLogDebug implements log.debug(message, ...) func luaLogDebug(L *lua.LState) int { msg := formatLogMessage(L) logger.GetLogger().Debug(msg) return 0 } // luaLogInfo implements log.info(message, ...) func luaLogInfo(L *lua.LState) int { msg := formatLogMessage(L) logger.GetLogger().Info(msg) return 0 } // luaLogWarn implements log.warn(message, ...) func luaLogWarn(L *lua.LState) int { msg := formatLogMessage(L) logger.GetLogger().Warn(msg) return 0 } // luaLogError implements log.error(message, ...) func luaLogError(L *lua.LState) int { msg := formatLogMessage(L) logger.GetLogger().Error(msg) return 0 } // formatLogMessage formats log message from Lua arguments func formatLogMessage(L *lua.LState) string { n := L.GetTop() if n == 0 { return "" } if n == 1 { return L.CheckString(1) } // Format string with arguments format := L.CheckString(1) args := make([]any, n-1) for i := 2; i <= n; i++ { args[i-2] = luaValueToGo(L.Get(i)) } return fmt.Sprintf(format, args...) } // RegisterUtilModule registers utility functions in Lua func RegisterUtilModule(L *lua.LState) { utilMod := L.NewTable() // base64.encode(string) utilMod.RawSetString("base64_encode", L.NewFunction(luaBase64Encode)) // base64.decode(string) utilMod.RawSetString("base64_decode", L.NewFunction(luaBase64Decode)) // url.encode(string) utilMod.RawSetString("url_encode", L.NewFunction(luaURLEncode)) // url.decode(string) utilMod.RawSetString("url_decode", L.NewFunction(luaURLDecode)) L.SetGlobal("util", utilMod) } // luaBase64Encode implements base64_encode(str) func luaBase64Encode(L *lua.LState) int { str := L.CheckString(1) encoded := base64.StdEncoding.EncodeToString([]byte(str)) L.Push(lua.LString(encoded)) return 1 } // luaBase64Decode implements base64_decode(str) func luaBase64Decode(L *lua.LState) int { str := L.CheckString(1) decoded, err := base64.StdEncoding.DecodeString(str) if err != nil { L.Push(lua.LNil) L.Push(lua.LString(err.Error())) return 2 } L.Push(lua.LString(string(decoded))) return 1 } // luaURLEncode implements url_encode(str) func luaURLEncode(L *lua.LState) int { str := L.CheckString(1) encoded := url.QueryEscape(str) L.Push(lua.LString(encoded)) return 1 } // luaURLDecode implements url_decode(str) func luaURLDecode(L *lua.LState) int { str := L.CheckString(1) decoded, err := url.QueryUnescape(str) if err != nil { L.Push(lua.LNil) L.Push(lua.LString(err.Error())) return 2 } L.Push(lua.LString(decoded)) return 1 }