From 8994d3af144b8b2020b544906d4a25e591e4047f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Mon, 16 Jun 2025 18:51:01 +0800 Subject: [PATCH] fix --- src/core/index.ts | 22 +- src/framework/proxy/remoteSession.ts | 48 +- src/framework/proxy/serialize.cpp | 662 ++++++++++++++++++++++++++- 3 files changed, 686 insertions(+), 46 deletions(-) diff --git a/src/core/index.ts b/src/core/index.ts index 675db5fe..b0650d2b 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -31,6 +31,8 @@ import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/liste import { proxiedListenerOf } from '@/common/proxy-handler'; import { NTQQPacketApi } from './apis/packet'; import { createRemoteSession } from '../framework/proxy/remoteSession'; +import { handleServiceServerOnce, ServiceMethodCommand } from '@/framework/proxy/service'; +import { rpc_decode, rpc_encode } from '@/framework/proxy/serialize'; export * from './wrapper'; export * from './types'; export * from './services'; @@ -101,7 +103,25 @@ export class NapCatCore { this.util = this.context.wrapper.NodeQQNTWrapperUtil; this.eventWrapper = new NTEventWrapper(context.session); - this.context.session = createRemoteSession(this.eventWrapper); + + this.context.session = createRemoteSession(async (serviceClient, serviceCommand, ...args) => { + // 模拟远程服务调用 + const call_dto = rpc_encode({ command: serviceCommand, params: args }); + // 得到远程服务调用的命令和参数 + const call_data = rpc_decode<{ command: ServiceMethodCommand; params: any[] }>(call_dto); + // 处理并回应结果 + return await handleServiceServerOnce( + call_data.command, + async (listenerCommand: string, ...args: any[]) => { + const listener_dto = rpc_encode({ command: listenerCommand, params: args }); + const listener_data = rpc_decode<{ command: string; params: any[] }>(listener_dto); + serviceClient.receiverListener(listener_data.command, ...listener_data.params); + }, + this.eventWrapper, + ...call_data.params + ); + }); + this.configLoader = new NapCatConfigLoader(this, this.context.pathWrapper.configPath, NapcatConfigSchema); this.apis = { FileApi: new NTQQFileApi(this.context, this), diff --git a/src/framework/proxy/remoteSession.ts b/src/framework/proxy/remoteSession.ts index 056435d9..df119865 100644 --- a/src/framework/proxy/remoteSession.ts +++ b/src/framework/proxy/remoteSession.ts @@ -1,7 +1,4 @@ -import { NTEventWrapper } from "@/common/event"; import { createRemoteServiceClient } from "@/framework/proxy/service"; -import { handleServiceServerOnce } from "@/framework/proxy/service"; -import { ServiceMethodCommand } from "@/framework/proxy/service"; import { NodeIQQNTWrapperSession, WrapperSessionInitConfig @@ -11,57 +8,46 @@ import { NodeIDependsAdapter, NodeIDispatcherAdapter } from "../../core/adapters"; -import { rpc_decode, rpc_encode } from "./serialize"; +import { ServiceNamingMapping } from "@/core"; class RemoteServiceManager { private services: Map = new Map(); - private eventWrapper: NTEventWrapper; + private handler; - constructor(eventWrapper: NTEventWrapper) { - this.eventWrapper = eventWrapper; + constructor(handler: (client: any, listenerCommand: string, ...args: any[]) => Promise) { + this.handler = handler; } - private createRemoteService( + private createRemoteService( serviceName: T - ): import("@/core/services").ServiceNamingMapping[T] { + ): ServiceNamingMapping[T] { if (this.services.has(serviceName)) { return this.services.get(serviceName); } - const serviceClient = createRemoteServiceClient(serviceName, async (serviceCommand, ...args) => { - const call_dto = rpc_encode({ command: serviceCommand, params: args }); - const call_data = rpc_decode<{ command: ServiceMethodCommand; params: any[] }>(call_dto); - - return handleServiceServerOnce( - call_data.command, - async (listenerCommand: string, ...args: any[]) => { - const listener_dto = rpc_encode({ command: listenerCommand, params: args }); - const listener_data = rpc_decode<{ command: string; params: any[] }>(listener_dto); - serviceClient.receiverListener(listener_data.command, ...listener_data.params); - }, - this.eventWrapper, - ...call_data.params - ); + let serviceClient: any; + serviceClient = createRemoteServiceClient(serviceName, async (serviceCommand, ...args) => { + return await this.handler(serviceClient, serviceCommand, ...args); }); this.services.set(serviceName, serviceClient.object); return serviceClient.object; } - getService( + getService( serviceName: T - ): import("@/core/services").ServiceNamingMapping[T] { + ): ServiceNamingMapping[T] { return this.createRemoteService(serviceName); } } export class RemoteWrapperSession implements NodeIQQNTWrapperSession { private serviceManager: RemoteServiceManager; - constructor(eventWrapper: NTEventWrapper) { - this.serviceManager = new RemoteServiceManager(eventWrapper); + constructor(handler: (client: { object: keyof ServiceNamingMapping, receiverListener: (command: string, ...args: any[]) => void }, listenerCommand: string, ...args: any[]) => Promise) { + this.serviceManager = new RemoteServiceManager(handler); } - create(): NodeIQQNTWrapperSession { - return new RemoteWrapperSession(this.serviceManager['eventWrapper']); + create(): RemoteWrapperSession { + return this; } init( @@ -121,6 +107,6 @@ export class RemoteWrapperSession implements NodeIQQNTWrapperSession { getConfigMgrService() { return null; } } -export function createRemoteSession(eventWrapper: NTEventWrapper): NodeIQQNTWrapperSession { - return new RemoteWrapperSession(eventWrapper) as NodeIQQNTWrapperSession; +export function createRemoteSession(handler: (client: { object: keyof ServiceNamingMapping, receiverListener: (command: string, ...args: any[]) => void }, listenerCommand: string, ...args: any[]) => Promise): NodeIQQNTWrapperSession { + return new RemoteWrapperSession(handler) as NodeIQQNTWrapperSession; } diff --git a/src/framework/proxy/serialize.cpp b/src/framework/proxy/serialize.cpp index 93d3d5fe..ffa5cdea 100644 --- a/src/framework/proxy/serialize.cpp +++ b/src/framework/proxy/serialize.cpp @@ -5,6 +5,7 @@ #include #include #include +#include // Base64 编码表 static const char *BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -137,6 +138,19 @@ bool is_buffer(napi_env env, napi_value val) return result; } +bool is_uint8_array(napi_env env, napi_value val) +{ + napi_value global, uint8_array_constructor; + if (napi_get_global(env, &global) != napi_ok) + return false; + if (napi_get_named_property(env, global, "Uint8Array", &uint8_array_constructor) != napi_ok) + return false; + + bool result = false; + napi_instanceof(env, val, uint8_array_constructor, &result); + return result; +} + bool is_array(napi_env env, napi_value val) { bool result = false; @@ -144,6 +158,32 @@ bool is_array(napi_env env, napi_value val) return result; } +bool is_array_like(napi_env env, napi_value val) +{ + // 检查是否是数组 + if (is_array(env, val)) + return true; + + // 检查是否有length属性且为数字 + napi_valuetype type; + if (napi_typeof(env, val, &type) != napi_ok || type != napi_object) + return false; + + bool has_length = false; + if (napi_has_named_property(env, val, "length", &has_length) != napi_ok || !has_length) + return false; + + napi_value length_val; + if (napi_get_named_property(env, val, "length", &length_val) != napi_ok) + return false; + + napi_valuetype length_type; + if (napi_typeof(env, length_val, &length_type) != napi_ok || length_type != napi_number) + return false; + + return true; +} + bool is_map(napi_env env, napi_value val) { napi_value global, map_constructor; @@ -229,16 +269,32 @@ napi_value encode_map(napi_env env, napi_value map) return result; } -// 编码数组类型 +// 编码数组或类数组类型 napi_value encode_array(napi_env env, napi_value arr) { napi_value result = create_typed_object(env, "Array"); if (!result) return nullptr; - uint32_t length; - if (napi_get_array_length(env, arr, &length) != napi_ok) - return nullptr; + uint32_t length = 0; + + // 获取长度 + if (is_array(env, arr)) + { + if (napi_get_array_length(env, arr, &length) != napi_ok) + return nullptr; + } + else + { + // 类数组对象,获取length属性 + napi_value length_val; + if (napi_get_named_property(env, arr, "length", &length_val) != napi_ok) + return nullptr; + double length_double; + if (napi_get_value_double(env, length_val, &length_double) != napi_ok) + return nullptr; + length = static_cast(length_double); + } napi_value encoded_array; if (napi_create_array_with_length(env, length, &encoded_array) != napi_ok) @@ -290,6 +346,11 @@ napi_value encode_object(napi_env env, napi_value obj) if (key.empty()) continue; + // 检查属性是否存在(hasOwnProperty) + bool has_own_property = false; + if (napi_has_own_property(env, obj, prop_name, &has_own_property) != napi_ok || !has_own_property) + continue; + napi_value prop_value; if (napi_get_named_property(env, obj, key.c_str(), &prop_value) != napi_ok) continue; @@ -354,13 +415,25 @@ napi_value encode_value(napi_env env, napi_value value) case napi_object: { - if (is_buffer(env, value)) + if (is_buffer(env, value) || is_uint8_array(env, value)) { - // 处理 Buffer + // 处理 Buffer 和 Uint8Array void *data; size_t length; - if (napi_get_buffer_info(env, value, &data, &length) != napi_ok) - return nullptr; + + if (is_buffer(env, value)) + { + if (napi_get_buffer_info(env, value, &data, &length) != napi_ok) + return nullptr; + } + else + { + // Uint8Array + napi_value buffer; + size_t byte_offset; + if (napi_get_typedarray_info(env, value, nullptr, &length, &data, &buffer, &byte_offset) != napi_ok) + return nullptr; + } std::string base64_str = base64_encode(static_cast(data), length); napi_value result = create_typed_object(env, "Buffer"); @@ -377,7 +450,7 @@ napi_value encode_value(napi_env env, napi_value value) return encode_map(env, value); } - if (is_array(env, value)) + if (is_array_like(env, value)) { return encode_array(env, value); } @@ -508,6 +581,11 @@ napi_value decode_value(napi_env env, napi_value obj) if (napi_get_named_property(env, value_obj, key.c_str(), &prop_value) != napi_ok) continue; + // 只有当值不是undefined时才设置属性 + bool is_undefined_val = false; + if (napi_is_undefined(env, prop_value, &is_undefined_val) == napi_ok && is_undefined_val) + continue; + napi_value decoded_value = decode_value(env, prop_value); if (decoded_value) { @@ -564,13 +642,569 @@ napi_value decode_value(napi_env env, napi_value obj) return nullptr; } -// 导出函数 -napi_value Encode(napi_env env, napi_value value) +// 导出的 rpc_encode 函数 +napi_value rpc_encode(napi_env env, napi_callback_info info) { - return encode_value(env, value); + size_t argc = 1; + napi_value args[1]; + + if (napi_get_cb_info(env, info, &argc, args, nullptr, nullptr) != napi_ok) + { + napi_throw_error(env, nullptr, "Failed to get callback info"); + return nullptr; + } + + if (argc < 1) + { + napi_throw_error(env, nullptr, "Expected 1 argument"); + return nullptr; + } + + napi_value result = encode_value(env, args[0]); + if (!result) + { + napi_throw_error(env, nullptr, "Unsupported type"); + return nullptr; + } + + return result; } -napi_value Decode(napi_env env, napi_value obj) +// 导出的 rpc_decode 函数 +napi_value rpc_decode(napi_env env, napi_callback_info info) { - return decode_value(env, obj); + size_t argc = 1; + napi_value args[1]; + + if (napi_get_cb_info(env, info, &argc, args, nullptr, nullptr) != napi_ok) + { + napi_throw_error(env, nullptr, "Failed to get callback info"); + return nullptr; + } + + if (argc < 1) + { + napi_throw_error(env, nullptr, "Expected 1 argument"); + return nullptr; + } + + napi_value result = decode_value(env, args[0]); + if (!result) + { + napi_throw_error(env, nullptr, "Invalid encoded object"); + return nullptr; + } + + return result; +} + +// JSON字符串转义 +std::string escape_json_string(const std::string &str) +{ + std::string result; + result.reserve(str.size() * 2); // 预分配足够空间 + + for (char c : str) + { + switch (c) + { + case '"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + default: + if (c >= 0 && c < 32) + { + // 控制字符用Unicode转义 + char buffer[8]; + snprintf(buffer, sizeof(buffer), "\\u%04x", static_cast(c)); + result += buffer; + } + else + { + result += c; + } + break; + } + } + return result; +} + +// 将napi_value转换为JSON字符串 +std::string value_to_json(napi_env env, napi_value value) +{ + napi_valuetype type; + if (napi_typeof(env, value, &type) != napi_ok) + { + return "null"; + } + + // 检查null + bool is_null_val = false; + if (napi_is_null(env, value, &is_null_val) == napi_ok && is_null_val) + { + return "null"; + } + + // 检查undefined + bool is_undefined_val = false; + if (napi_is_undefined(env, value, &is_undefined_val) == napi_ok && is_undefined_val) + { + return "null"; // JSON中没有undefined,用null代替 + } + + switch (type) + { + case napi_boolean: + { + bool bool_val; + if (napi_get_value_bool(env, value, &bool_val) == napi_ok) + { + return bool_val ? "true" : "false"; + } + return "null"; + } + + case napi_number: + { + double num_val; + if (napi_get_value_double(env, value, &num_val) == napi_ok) + { + // 检查是否为整数 + if (num_val == static_cast(num_val)) + { + return std::to_string(static_cast(num_val)); + } + else + { + return std::to_string(num_val); + } + } + return "null"; + } + + case napi_string: + { + std::string str_val = get_string_value(env, value); + return "\"" + escape_json_string(str_val) + "\""; + } + + case napi_object: + { + // 检查是否为数组 + if (is_array(env, value)) + { + std::string result = "["; + uint32_t length; + if (napi_get_array_length(env, value, &length) == napi_ok) + { + for (uint32_t i = 0; i < length; i++) + { + if (i > 0) + result += ","; + napi_value element; + if (napi_get_element(env, value, i, &element) == napi_ok) + { + result += value_to_json(env, element); + } + else + { + result += "null"; + } + } + } + result += "]"; + return result; + } + + // 处理普通对象 + std::string result = "{"; + napi_value prop_names; + if (napi_get_property_names(env, value, &prop_names) == napi_ok) + { + uint32_t prop_count; + if (napi_get_array_length(env, prop_names, &prop_count) == napi_ok) + { + bool first = true; + for (uint32_t i = 0; i < prop_count; i++) + { + napi_value prop_name; + if (napi_get_element(env, prop_names, i, &prop_name) == napi_ok) + { + std::string key = get_string_value(env, prop_name); + if (!key.empty()) + { + napi_value prop_value; + if (napi_get_named_property(env, value, key.c_str(), &prop_value) == napi_ok) + { + if (!first) + result += ","; + first = false; + result += "\"" + escape_json_string(key) + "\":" + value_to_json(env, prop_value); + } + } + } + } + } + } + result += "}"; + return result; + } + + default: + return "null"; + } +} + +// JSON解析辅助函数 +class JSONParser +{ +private: + const char *json; + size_t pos; + size_t len; + + void skip_whitespace() + { + while (pos < len && (json[pos] == ' ' || json[pos] == '\t' || + json[pos] == '\n' || json[pos] == '\r')) + { + pos++; + } + } + + bool match_string(const char *str) + { + size_t str_len = strlen(str); + if (pos + str_len > len) + return false; + + for (size_t i = 0; i < str_len; i++) + { + if (json[pos + i] != str[i]) + return false; + } + pos += str_len; + return true; + } + + std::string parse_string() + { + if (pos >= len || json[pos] != '"') + return ""; + pos++; // skip opening quote + + std::string result; + while (pos < len && json[pos] != '"') + { + if (json[pos] == '\\' && pos + 1 < len) + { + pos++; + switch (json[pos]) + { + case '"': + result += '"'; + break; + case '\\': + result += '\\'; + break; + case '/': + result += '/'; + break; + case 'b': + result += '\b'; + break; + case 'f': + result += '\f'; + break; + case 'n': + result += '\n'; + break; + case 'r': + result += '\r'; + break; + case 't': + result += '\t'; + break; + case 'u': + if (pos + 4 < len) + { + // 简单的Unicode处理,这里只处理基本的ASCII范围 + char hex_str[5] = {0}; + memcpy(hex_str, json + pos + 1, 4); + unsigned int code_point = strtoul(hex_str, nullptr, 16); + if (code_point <= 0x7F) + { + result += static_cast(code_point); + } + pos += 4; + } + break; + default: + result += json[pos]; + break; + } + } + else + { + result += json[pos]; + } + pos++; + } + + if (pos < len && json[pos] == '"') + { + pos++; // skip closing quote + } + return result; + } + + double parse_number() + { + size_t start = pos; + + // 处理负号 + if (pos < len && json[pos] == '-') + { + pos++; + } + + // 处理整数部分 + if (pos < len && json[pos] == '0') + { + pos++; + } + else if (pos < len && json[pos] >= '1' && json[pos] <= '9') + { + while (pos < len && json[pos] >= '0' && json[pos] <= '9') + { + pos++; + } + } + + // 处理小数部分 + if (pos < len && json[pos] == '.') + { + pos++; + while (pos < len && json[pos] >= '0' && json[pos] <= '9') + { + pos++; + } + } + + // 处理指数部分 + if (pos < len && (json[pos] == 'e' || json[pos] == 'E')) + { + pos++; + if (pos < len && (json[pos] == '+' || json[pos] == '-')) + { + pos++; + } + while (pos < len && json[pos] >= '0' && json[pos] <= '9') + { + pos++; + } + } + + std::string num_str(json + start, pos - start); + return strtod(num_str.c_str(), nullptr); + } + +public: + JSONParser(const char *json_str) : json(json_str), pos(0) + { + len = strlen(json_str); + } + + napi_value parse_value(napi_env env) + { + skip_whitespace(); + + if (pos >= len) + return nullptr; + + char c = json[pos]; + + // null + if (c == 'n' && match_string("null")) + { + napi_value result; + napi_get_null(env, &result); + return result; + } + + // true + if (c == 't' && match_string("true")) + { + napi_value result; + napi_get_boolean(env, true, &result); + return result; + } + + // false + if (c == 'f' && match_string("false")) + { + napi_value result; + napi_get_boolean(env, false, &result); + return result; + } + + // string + if (c == '"') + { + std::string str = parse_string(); + return create_string(env, str); + } + + // number + if (c == '-' || (c >= '0' && c <= '9')) + { + double num = parse_number(); + napi_value result; + napi_create_double(env, num, &result); + return result; + } + + // array + if (c == '[') + { + pos++; // skip '[' + skip_whitespace(); + + napi_value array; + if (napi_create_array(env, &array) != napi_ok) + { + return nullptr; + } + + uint32_t index = 0; + + // 处理空数组 + if (pos < len && json[pos] == ']') + { + pos++; + return array; + } + + while (pos < len) + { + napi_value element = parse_value(env); + if (element) + { + napi_set_element(env, array, index++, element); + } + + skip_whitespace(); + if (pos < len && json[pos] == ',') + { + pos++; // skip ',' + skip_whitespace(); + } + else if (pos < len && json[pos] == ']') + { + pos++; // skip ']' + break; + } + else + { + break; // 格式错误 + } + } + + return array; + } + + // object + if (c == '{') + { + pos++; // skip '{' + skip_whitespace(); + + napi_value obj; + if (napi_create_object(env, &obj) != napi_ok) + { + return nullptr; + } + + // 处理空对象 + if (pos < len && json[pos] == '}') + { + pos++; + return obj; + } + + while (pos < len) + { + skip_whitespace(); + + // 解析键 + if (pos >= len || json[pos] != '"') + break; + std::string key = parse_string(); + if (key.empty()) + break; + + skip_whitespace(); + if (pos >= len || json[pos] != ':') + break; + pos++; // skip ':' + + // 解析值 + napi_value value = parse_value(env); + if (value) + { + napi_set_named_property(env, obj, key.c_str(), value); + } + + skip_whitespace(); + if (pos < len && json[pos] == ',') + { + pos++; // skip ',' + skip_whitespace(); + } + else if (pos < len && json[pos] == '}') + { + pos++; // skip '}' + break; + } + else + { + break; // 格式错误 + } + } + + return obj; + } + + return nullptr; + } +}; + +// 将JSON字符串转换为napi_value +napi_value json_to_value(napi_env env, const std::string &json_str) +{ + if (json_str.empty()) + { + return nullptr; + } + + JSONParser parser(json_str.c_str()); + return parser.parse_value(env); } \ No newline at end of file