增加文字开始故事接口
authorhcj <1224667075@qq.com>
Sun, 28 Sep 2025 10:53:25 +0000 (18:53 +0800)
committerhcj <1224667075@qq.com>
Sun, 28 Sep 2025 10:53:25 +0000 (18:53 +0800)
code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c

index c01da65..5fa75a2 100644 (file)
@@ -817,6 +817,35 @@ static uint32_t ln_ws_end_audio_chat_at(uint8_t *para, uint32_t paraLen, char *s
     return ERRCODE_SUCC;
 }
 
+/* AT^LN_WS_SEND_RANDOM_STORY - 发送随机故事请求 */
+static uint32_t ln_ws_send_random_story_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(strCommonCmd);
+    
+    const char *story_theme = "冒险"; // 默认主题
+    
+    // 解析参数
+    if (para != NULL && paraLen > 0) {
+        uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
+        uint32_t argc = 0;
+        
+        if (ConvertCmdToParam(para, paraLen, argv, &argc) == VAD_SUCCESS && argc >= 1) {
+            story_theme = (const char *)argv[0];
+        }
+    }
+    
+    printf("[LN][WS_SEND_RANDOM_STORY] Sending random story request with theme: %s\n", story_theme);
+    
+    int ret = ln_ws_send_random_story_request(story_theme);
+    if (ret != 0)
+    {
+        printf("[LN][WS_SEND_RANDOM_STORY] failed: %d\n", ret);
+        return ERRCODE_FAIL;
+    }
+    printf("[LN][WS_SEND_RANDOM_STORY] success\n");
+    return ERRCODE_SUCC;
+}
+
 /* AT^LN_WS_SLEEP */
 static uint32_t ln_ws_sleep_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
@@ -1853,6 +1882,7 @@ static const at_cmd_table_t g_ln_api_cmd[] = {
     {(char *)"AT^LN_WS_CLOSE_STATUS", ln_ws_close_status_at, (char *)"查询WebSocket关闭状态: AT^LN_WS_CLOSE_STATUS"},
     {(char *)"AT^LN_WS_START_AUDIO_CHAT", ln_ws_start_audio_chat_at, (char *)"开始音频聊天: AT^LN_WS_START_AUDIO_CHAT[=0/1]"},
     {(char *)"AT^LN_WS_END_AUDIO_CHAT", ln_ws_end_audio_chat_at, (char *)"结束音频聊天: AT^LN_WS_END_AUDIO_CHAT"},
+    {(char *)"AT^LN_WS_SEND_RANDOM_STORY", ln_ws_send_random_story_at, (char *)"发送随机故事请求: AT^LN_WS_SEND_RANDOM_STORY[=storyTheme]"},
     
     /* 音频播放 */
     {(char *)"AT^LN_STORY_PLAY_AT", ln_audio_play_at, (char *)"播放音频: AT^LN_STORY_PLAY_AT"},
index e2dcdef..7918983 100644 (file)
@@ -312,6 +312,7 @@ typedef enum {
     WS_CMD_PONG,                     /* 心跳回应 (Pong) */
     WS_CMD_VOICE_CHAT_RESPONSE,      /* 语音聊天响应 (VoiceChatResponse) */
     WS_CMD_CHOOSE_STORY_RESPONSE,    /* 选择故事响应 (ChooseStoryResponse) */
+    WS_CMD_RANDOM_STORY_RESPONSE,    /* 随机故事响应 (RandomStoryResponse) */
     WS_CMD_CONNECTED,                /* WebSocket连接成功 */
     WS_CMD_DISCONNECTED,             /* WebSocket断连 */
     WS_CMD_MAX                       /* 最大命令 */
@@ -350,6 +351,7 @@ typedef enum {
 int ln_ws_start(ws_message_callback_t_ln callback);
 int ln_ws_send_text(const char *text);
 int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool finished);
+int ln_ws_send_random_story_request(const char *story_theme);
 int ln_ws_start_audio_chat(ln_audio_chat_command_t command_type);
 int ln_ws_end_audio_chat(void);
 const char* ln_ws_get_current_chat_id(void);
index 3bb39a8..2b08d7a 100644 (file)
@@ -137,19 +137,19 @@ static void ln_ws_event_callback(ws_event_type_t event_type)
             break;
         case WS_EVENT_DISCONNECTED:
             printf("[LN][WS] Event: Disconnected\n");
-            g_ws_connected = WS_STATE_DISCONNECTED;
+    g_ws_connected = WS_STATE_DISCONNECTED;
             
             // 触发断连事件
             if (g_ws_message_callback != NULL) {
                 g_ws_message_callback(NULL, WS_CMD_DISCONNECTED);
             }
             
-            /* 断开后增加重连计数,由监督线程在下一次连接时携带 */
-            if (g_ws_running) {
-                if (g_ws_reconnect_count < 0xFFFFFFFFu) {
-                    g_ws_reconnect_count++;
-                }
-            }
+    /* 断开后增加重连计数,由监督线程在下一次连接时携带 */
+    if (g_ws_running) {
+        if (g_ws_reconnect_count < 0xFFFFFFFFu) {
+            g_ws_reconnect_count++;
+        }
+    }
             break;
         default:
             printf("[LN][WS] Event: Unknown event type %d\n", event_type);
@@ -408,10 +408,17 @@ int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool f
 // 开始新的音频聊天
 int ln_ws_start_audio_chat(ln_audio_chat_command_t command_type)
 {
+    if(g_ws_connected != WS_STATE_CONNECTED)
+    {
+        printf("[LN][WS] g_ws_connected!=WS_STATE_CONNECTED");
+        return 0;
+    }
+    
     if (g_audio_chat_ctx.is_active) {
         printf("[LN][WS] Audio chat already active: %s\n", g_audio_chat_ctx.chat_id);
         return 0;
     }
+    // AI_ForceInterrupt();
     
     // 设置命令类型
     g_audio_chat_command_type = command_type;
@@ -650,6 +657,78 @@ int ln_ws_send_text(const char *text)
     return uapi_ws_send_text(text);
 }
 
+/* 发送随机故事请求 */
+int ln_ws_send_random_story_request(const char *story_theme)
+{
+    if (!story_theme) {
+        printf("[LN][WS] Invalid story theme parameter\n");
+        return -1;
+    }
+    
+    if (g_ws_connected != WS_STATE_CONNECTED) {
+        printf("[LN][WS] Cannot send random story request, connection state: %d\n", g_ws_connected);
+        return -1;
+    }
+    
+    // 生成聊天ID(使用当前对话ID)
+    char chat_id[128];
+    generate_chat_id(chat_id, sizeof(chat_id));
+    
+    // 生成消息ID
+    char message_id[128];
+    generate_message_id(message_id, sizeof(message_id), chat_id, 1);
+    
+    // 构建JSON消息
+    cJSON *json = cJSON_CreateObject();
+    if (!json) {
+        printf("[LN][WS] Failed to create JSON object\n");
+        return -1;
+    }
+    
+    // 添加基本字段
+    cJSON_AddStringToObject(json, "deviceId", uapi_ln_get_device_id());
+    cJSON_AddNumberToObject(json, "timestamp", (double)time(NULL));
+    cJSON_AddStringToObject(json, "messageId", message_id);
+    cJSON_AddStringToObject(json, "command", "RandomStoryRequest");
+    
+    // 添加data字段
+    cJSON *data = cJSON_CreateObject();
+    if (!data) {
+        printf("[LN][WS] Failed to create data JSON object\n");
+        cJSON_Delete(json);
+        return -1;
+    }
+    
+    cJSON_AddStringToObject(data, "chatId", chat_id);
+    cJSON_AddStringToObject(data, "storyTheme", story_theme);
+    cJSON_AddItemToObject(json, "data", data);
+    
+    // 转换为字符串
+    char *json_string = cJSON_Print(json);
+    if (!json_string) {
+        printf("[LN][WS] Failed to convert JSON to string\n");
+        cJSON_Delete(json);
+        return -1;
+    }
+    
+    printf("[LN][WS] Sending RandomStoryRequest: %s\n", json_string);
+    
+    // 发送消息
+    int ret = uapi_ws_send_text(json_string);
+    
+    // 清理资源
+    free(json_string);
+    cJSON_Delete(json);
+    
+    if (ret == 0) {
+        printf("[LN][WS] RandomStoryRequest sent successfully\n");
+    } else {
+        printf("[LN][WS] Failed to send RandomStoryRequest\n");
+    }
+    
+    return ret;
+}
+
 
 /* 停止:关闭心跳并关闭连接 */
 int ln_ws_stop(void)
@@ -744,6 +823,8 @@ static ws_command_t ln_ws_parse_command_string(const char *command_str)
         return WS_CMD_VOICE_CHAT_RESPONSE;
     } else if (strcmp(command_str, "ChooseStoryResponse") == 0) {
         return WS_CMD_CHOOSE_STORY_RESPONSE;
+    } else if (strcmp(command_str, "RandomStoryResponse") == 0) {
+        return WS_CMD_RANDOM_STORY_RESPONSE;
     } else {
         return WS_CMD_UNKNOWN;
     }
@@ -778,6 +859,24 @@ static ws_command_t ln_ws_parse_command_string(const char *command_str)
 // "data":{"audioUrl":"http://ambybox-api.test.leiniao.com/ambybox-device-api/file/774317549446_20250926175018_1758880218961.mp3",
 // "textContent":"(颔首,夹着烟的手指轻敲桌面)来者何人?"}}}
 
+
+// {
+//     "deviceId": "DEVICE_001",
+//     "timestamp": 1640995201,
+//     "messageId": "random_story_response_001",
+//     "command": "RandomStoryResponse",
+//     "data": {
+//       "chatId": "random_story_12345",
+//       "finished": false,
+//       "data": {
+//         "storyTheme": "冒险故事",
+//         "storyContent": "在一个阳光明媚的早晨",
+//         "storyAudioUrl": "https://example.com/story/audio/chunk_001.mp3",
+//         "storyDuration": null
+//       },
+//       "errorCode": null
+//     }
+//   }
 static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_msg)
 {
     if (!json_str || !data_msg) {
@@ -919,6 +1018,57 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms
                data_msg->finished ? "true" : "false", data_msg->chat_id, data_msg->text_content, data_msg->audio_url);
     }
     
+    // 解析RandomStoryResponse特定字段
+    if (data_msg->command == WS_CMD_RANDOM_STORY_RESPONSE) {
+        cJSON *data_obj = cJSON_GetObjectItem(root, "data");
+        if (data_obj && cJSON_IsObject(data_obj)) {
+            // 解析finished字段
+            cJSON *finished = cJSON_GetObjectItem(data_obj, "finished");
+            if (finished && cJSON_IsBool(finished)) {
+                data_msg->finished = cJSON_IsTrue(finished);
+            }
+            
+            // 解析chatId字段
+            cJSON *chat_id = cJSON_GetObjectItem(data_obj, "chatId");
+            if (chat_id && cJSON_IsString(chat_id) && chat_id->valuestring) {
+                strncpy(data_msg->chat_id, chat_id->valuestring, sizeof(data_msg->chat_id) - 1);
+                data_msg->chat_id[sizeof(data_msg->chat_id) - 1] = '\0';
+            }
+            
+            // 解析data.data字段
+            cJSON *data_data = cJSON_GetObjectItem(data_obj, "data");
+            if (data_data && cJSON_IsObject(data_data)) {
+                // 解析storyTheme字段,存储到text_content中
+                cJSON *story_theme = cJSON_GetObjectItem(data_data, "storyTheme");
+                if (story_theme && cJSON_IsString(story_theme) && story_theme->valuestring) {
+                    strncpy(data_msg->text_content, story_theme->valuestring, sizeof(data_msg->text_content) - 1);
+                    data_msg->text_content[sizeof(data_msg->text_content) - 1] = '\0';
+                }
+                
+                // 解析storyContent字段(RandomStoryResponse特有)
+                cJSON *story_content = cJSON_GetObjectItem(data_data, "storyContent");
+                if (story_content && cJSON_IsString(story_content) && story_content->valuestring) {
+                    // 如果text_content为空,使用storyContent填充
+                    if (strlen(data_msg->text_content) == 0) {
+                        strncpy(data_msg->text_content, story_content->valuestring, sizeof(data_msg->text_content) - 1);
+                        data_msg->text_content[sizeof(data_msg->text_content) - 1] = '\0';
+                    }
+                }
+                
+                // 解析storyAudioUrl字段,存储到audio_url中
+                cJSON *story_audio_url = cJSON_GetObjectItem(data_data, "storyAudioUrl");
+                if (story_audio_url && cJSON_IsString(story_audio_url) && story_audio_url->valuestring) {
+                    strncpy(data_msg->audio_url, story_audio_url->valuestring, sizeof(data_msg->audio_url) - 1);
+                    data_msg->audio_url[sizeof(data_msg->audio_url) - 1] = '\0';
+                }
+            }
+        }
+        
+        printf("[LN][WS] RandomStoryResponse parsed: finished=%s, chatId=%s, storyTheme=%s, storyContent=%s, storyAudioUrl=%s\n",
+               data_msg->finished ? "true" : "false", data_msg->chat_id, data_msg->text_content, 
+               data_msg->text_content, data_msg->audio_url);
+    }
+    
     // 保存原始JSON字符串
     data_msg->raw_json = (char*)malloc(strlen(json_str) + 1);
     if (data_msg->raw_json) {
@@ -969,43 +1119,43 @@ static void ln_ws_default_message_handler(const ws_data_msg_t *data_msg, ws_comm
             break;
         case WS_CMD_PONG:
             if (data_msg) {
-                printf("[LN][WS] Default: Processing Pong message\n");
-                // 更新心跳状态、记录响应时间等
+            printf("[LN][WS] Default: Processing Pong message\n");
+            // 更新心跳状态、记录响应时间等
             }
             break;
         case WS_CMD_CHARACTER_CHANGE:
             if (data_msg) {
-                printf("[LN][WS] Default: Processing CharacterChange message\n");
+            printf("[LN][WS] Default: Processing CharacterChange message\n");
             }
             break;
         case WS_CMD_SLEEP_MODE_START:
             if (data_msg) {
-                printf("[LN][WS] Default: Processing SleepModeStart message\n");
+            printf("[LN][WS] Default: Processing SleepModeStart message\n");
             }
             break;
         case WS_CMD_SLEEP_MODE_END:
             if (data_msg) {
-                printf("[LN][WS] Default: Processing SleepModeEnd message\n");
+            printf("[LN][WS] Default: Processing SleepModeEnd message\n");
             }
             break;
         case WS_CMD_DEVICE_BIND:
             if (data_msg) {
-                printf("[LN][WS] Default: Processing DeviceBind message\n");
+            printf("[LN][WS] Default: Processing DeviceBind message\n");
             }
             break;
         case WS_CMD_DEVICE_UNBIND:
             if (data_msg) {
-                printf("[LN][WS] Default: Processing DeviceUnbind message\n");
+            printf("[LN][WS] Default: Processing DeviceUnbind message\n");
             }
             break;
         case WS_CMD_SCHEDULE_REMINDER:
             if (data_msg) {
-                printf("[LN][WS] Default: Processing ScheduleReminder message\n");
+            printf("[LN][WS] Default: Processing ScheduleReminder message\n");
             }
             break;
         case WS_CMD_SCHEDULE_REMINDER_CFG:
             if (data_msg) {
-                printf("[LN][WS] Default: Processing ScheduleReminderConfig message\n");
+            printf("[LN][WS] Default: Processing ScheduleReminderConfig message\n");
             }
             break;
         case WS_CMD_VOICE_CHAT_RESPONSE:
@@ -1026,6 +1176,15 @@ static void ln_ws_default_message_handler(const ws_data_msg_t *data_msg, ws_comm
                 printf("[LN][WS] Story Audio URL: %s\n", data_msg->audio_url);
             }
             break;
+        case WS_CMD_RANDOM_STORY_RESPONSE:
+            if (data_msg) {
+                printf("[LN][WS] Default: Processing RandomStoryResponse message\n");
+                printf("[LN][WS] RandomStoryResponse details: finished=%s, chatId=%s\n", 
+                       data_msg->finished ? "true" : "false", data_msg->chat_id);
+                printf("[LN][WS] Story Theme: %s\n", data_msg->text_content);
+                printf("[LN][WS] Story Audio URL: %s\n", data_msg->audio_url);
+            }
+            break;
         default:
             printf("[LN][WS] Default: Processing unknown command: %d\n", type);
             break;
@@ -1345,6 +1504,20 @@ static void ln_ws_process_incremental_base64_data(const char *new_data, size_t n
         printf("[LN][WS] Not enough data for Base64 decode yet: %zu\n", g_base64_cache_size);
     }
 }
+
+
+// {
+//   "deviceId": "DEVICE_001",
+//   "timestamp": 1640995200,
+//   "messageId": "random_story_001",
+//   "command": "RandomStoryRequest",
+//   "data": {
+//     "chatId": "random_story_12345",
+//     "storyTheme": "冒险"
+//   }
+// }
+
+
 ////////////////////////////////////////////////////////////////////////////
 
 // int uapi_ln_download_mp3_file(const char *url)