|
|
|
|
@ -13,6 +13,7 @@
|
|
|
|
|
#include "chat_core.h" |
|
|
|
|
#include "chat_core_priv.h" |
|
|
|
|
#include "chat_event.h" |
|
|
|
|
#include "chat_poll.h" |
|
|
|
|
#include "chat_sync.h" |
|
|
|
|
#include "chat_join.h" |
|
|
|
|
#include "invite_link.h" |
|
|
|
|
@ -30,6 +31,8 @@
|
|
|
|
|
#include "../../lib/ll_queue.h" |
|
|
|
|
#include "../../lib/u_async.h" |
|
|
|
|
#include "../../lib/mem.h" |
|
|
|
|
#include "../../lib/strbuf.h" |
|
|
|
|
#include "../../lib/json_flat.h" |
|
|
|
|
#include "../../lib/socket_compat.h" |
|
|
|
|
#include "../../lib/debug_config.h" |
|
|
|
|
#include "../../lib/platform_compat.h" |
|
|
|
|
@ -167,11 +170,20 @@ static void cli_send_str(struct headless_client* cli, const char* s) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void send_response(struct headless_client* cli, int id, const char* ok_data, const char* error) { |
|
|
|
|
char buf[4096]; int len; |
|
|
|
|
if (error) len = snprintf(buf, sizeof(buf), "{\"id\":%d,\"ok\":false,\"error\":\"%s\"}\n", id, error); |
|
|
|
|
else if (ok_data) len = snprintf(buf, sizeof(buf), "{\"id\":%d,\"ok\":true,\"data\":%s}\n", id, ok_data); |
|
|
|
|
else len = snprintf(buf, sizeof(buf), "{\"id\":%d,\"ok\":true}\n", id); |
|
|
|
|
if (len > 0) cli_send(cli, buf, (size_t)len); |
|
|
|
|
struct strbuf out = strbuf_new(); int rc; |
|
|
|
|
if (error) { |
|
|
|
|
rc = strbuf_addf(&out, "{\"id\":%d,\"ok\":false,\"error\":", id); |
|
|
|
|
if (rc >= 0) rc = strbuf_add_json_string(&out, error); |
|
|
|
|
if (rc >= 0) rc = strbuf_addf(&out, "}\n"); |
|
|
|
|
} else rc = strbuf_addf(&out, "{\"id\":%d,\"ok\":true,\"data\":%s}\n", id, ok_data ? ok_data : "null"); |
|
|
|
|
if (rc < 0 || out.len + (size_t)cli->send_len > SEND_BUF_SIZE) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_HEADLESS, "headless: response rejected id=%d bytes=%zu queued=%d rc=%d", |
|
|
|
|
id, out.len, cli->send_len, rc); |
|
|
|
|
char failure[160]; int bytes = snprintf(failure, sizeof(failure), |
|
|
|
|
"{\"id\":%d,\"ok\":false,\"error\":\"Response too large or out of memory; request fewer records\"}\n", id); |
|
|
|
|
cli_send(cli, failure, bytes); |
|
|
|
|
} else cli_send(cli, strbuf_str(&out), out.len); |
|
|
|
|
strbuf_free(&out); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ── Broadcast event ── */ |
|
|
|
|
@ -191,7 +203,12 @@ static void hc_on_chat_event(struct UTUN_INSTANCE* inst, int type, const uint8_t
|
|
|
|
|
if (!hc) return; |
|
|
|
|
if (type == 10) return; /* skip status text */ |
|
|
|
|
char json[1024]; json[0] = '\0'; |
|
|
|
|
if (type == 1 && data && len >= 10) { /* MSG_RECEIVED */ |
|
|
|
|
if (type == CHAT_EVT_FEEDBACK_CHANGED && data && len >= 34 && len == data[0] + 34) { |
|
|
|
|
uint8_t cl = data[0]; char message_id[65]; |
|
|
|
|
for (unsigned i = 0; i < 32; i++) snprintf(message_id + i * 2, 3, "%02x", data[1 + cl + i]); |
|
|
|
|
snprintf(json, sizeof(json), "{\"event\":\"feedback\",\"ch\":\"%.*s\",\"message_id\":\"%s\",\"kind\":%u}", |
|
|
|
|
(int)cl, data + 1, message_id, data[33 + cl]); |
|
|
|
|
} else if (type == 1 && data && len >= 10) { /* MSG_RECEIVED */ |
|
|
|
|
uint8_t cl = data[0]; |
|
|
|
|
uint64_t author; memcpy(&author, data + 1 + cl, 8); |
|
|
|
|
snprintf(json, sizeof(json), "{\"event\":\"msg\",\"ch\":\"%.*s\",\"author_id\":\"0x%016llx\"}", (int)cl, data + 1, (unsigned long long)author); |
|
|
|
|
@ -794,6 +811,19 @@ static void hc_handle_radio_subscribers(struct headless_client* cli, int id, con
|
|
|
|
|
|
|
|
|
|
typedef void (*hc_cmd_fn)(struct headless_client* cli, int id, const char* json); |
|
|
|
|
|
|
|
|
|
/* Опросы и реакции используют общий командный API ядра. */ |
|
|
|
|
static void hc_handle_feedback(struct headless_client* cli, int id, const char* json) { |
|
|
|
|
char* result = NULL; int rc = chat_feedback_command(cli->hc->inst, json, &result); |
|
|
|
|
if (rc == 0) send_response(cli, id, result, NULL); |
|
|
|
|
else { |
|
|
|
|
char error[256] = "Interaction failed"; |
|
|
|
|
if (result && json_flat_get(result, "error", error, sizeof(error)) < 0) |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_HEADLESS, "headless: invalid interaction error JSON"); |
|
|
|
|
send_response(cli, id, NULL, error); |
|
|
|
|
} |
|
|
|
|
u_free(result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const char* cmd_names[] = { "ping", "status", "channels", "members", "messages", "send", "attach", "invite", "invite_nodes", "connect", "create_channel", "invite_to", "subscribe", "dm_start", "dm_list", "dm_messages", "dm_send", "call_start", "call_accept", "call_decline", "call_hangup", "radio_on", "radio_off", "radio_subscribers", "quit", NULL }; |
|
|
|
|
static hc_cmd_fn cmd_handlers[] = { hc_handle_ping, hc_handle_status, hc_handle_channels, hc_handle_members, hc_handle_messages, hc_handle_send, hc_handle_attach, hc_handle_invite, hc_handle_invite_nodes, hc_handle_connect, hc_handle_create_channel, hc_handle_invite_to, hc_handle_subscribe, hc_handle_dm_start, hc_handle_dm_list, hc_handle_dm_messages, hc_handle_dm_send, hc_handle_call_start, hc_handle_call_accept, hc_handle_call_decline, hc_handle_call_hangup, hc_handle_radio_on, hc_handle_radio_off, hc_handle_radio_subscribers, hc_handle_quit }; |
|
|
|
|
|
|
|
|
|
@ -806,6 +836,10 @@ static void hc_handle_command(struct headless_client* cli, const char* json) {
|
|
|
|
|
size_t clen = (size_t)(ce - cs); |
|
|
|
|
if (clen == 0 || clen > 31) { send_response(cli, id, NULL, "invalid cmd"); return; } |
|
|
|
|
char cmd[32]; memcpy(cmd, cs, clen); cmd[clen] = '\0'; |
|
|
|
|
if (!strcmp(cmd, "poll_create") || !strcmp(cmd, "vote") || !strcmp(cmd, "react") |
|
|
|
|
|| !strcmp(cmd, "feedback") || !strcmp(cmd, "reaction_catalog")) { |
|
|
|
|
hc_handle_feedback(cli, id, json); return; |
|
|
|
|
} |
|
|
|
|
for (int i = 0; cmd_names[i]; i++) { |
|
|
|
|
if (strcmp(cmd, cmd_names[i]) == 0) { cmd_handlers[i](cli, id, json); return; } |
|
|
|
|
} |
|
|
|
|
|