v0.0.19 - Add nostr_delete/nostr_react/nostr_profile_get/nostr_relay_status tools with relay status backend
This commit is contained in:
+2
-2
@@ -12,8 +12,8 @@
|
||||
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
||||
#define DIDACTYL_VERSION_MAJOR 0
|
||||
#define DIDACTYL_VERSION_MINOR 0
|
||||
#define DIDACTYL_VERSION_PATCH 18
|
||||
#define DIDACTYL_VERSION "v0.0.18"
|
||||
#define DIDACTYL_VERSION_PATCH 19
|
||||
#define DIDACTYL_VERSION "v0.0.19"
|
||||
|
||||
// Agent metadata
|
||||
#define DIDACTYL_NAME "Didactyl"
|
||||
|
||||
@@ -1295,6 +1295,94 @@ int nostr_handler_connected_relay_count(void) {
|
||||
return connected;
|
||||
}
|
||||
|
||||
char* nostr_handler_relay_status_json(void) {
|
||||
if (!g_pool) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char** relay_urls = NULL;
|
||||
nostr_pool_relay_status_t* statuses = NULL;
|
||||
int relay_count = nostr_relay_pool_list_relays(g_pool, &relay_urls, &statuses);
|
||||
if (relay_count < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
cJSON* relays = cJSON_CreateArray();
|
||||
if (!root || !relays) {
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(relays);
|
||||
if (relay_urls) {
|
||||
for (int i = 0; i < relay_count; i++) {
|
||||
free(relay_urls[i]);
|
||||
}
|
||||
free(relay_urls);
|
||||
}
|
||||
free(statuses);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int connected_count = 0;
|
||||
for (int i = 0; i < relay_count; i++) {
|
||||
cJSON* relay = cJSON_CreateObject();
|
||||
if (!relay) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* url = (relay_urls && relay_urls[i]) ? relay_urls[i] : "";
|
||||
nostr_pool_relay_status_t st = statuses ? statuses[i] : NOSTR_POOL_RELAY_DISCONNECTED;
|
||||
if (st == NOSTR_POOL_RELAY_CONNECTED) {
|
||||
connected_count++;
|
||||
}
|
||||
|
||||
const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(g_pool, url);
|
||||
|
||||
cJSON_AddStringToObject(relay, "url", url);
|
||||
cJSON_AddStringToObject(relay, "status", relay_status_str(st));
|
||||
|
||||
if (stats) {
|
||||
cJSON_AddNumberToObject(relay, "events_received", stats->events_received);
|
||||
cJSON_AddNumberToObject(relay, "events_published", stats->events_published);
|
||||
cJSON_AddNumberToObject(relay, "events_published_ok", stats->events_published_ok);
|
||||
cJSON_AddNumberToObject(relay, "events_published_failed", stats->events_published_failed);
|
||||
cJSON_AddNumberToObject(relay, "ping_latency_current", stats->ping_latency_current);
|
||||
cJSON_AddNumberToObject(relay, "ping_latency_avg", stats->ping_latency_avg);
|
||||
cJSON_AddNumberToObject(relay, "query_latency_avg", stats->query_latency_avg);
|
||||
cJSON_AddNumberToObject(relay, "publish_latency_avg", stats->publish_latency_avg);
|
||||
cJSON_AddNumberToObject(relay, "connection_uptime_start", (double)stats->connection_uptime_start);
|
||||
cJSON_AddNumberToObject(relay, "last_event_time", (double)stats->last_event_time);
|
||||
}
|
||||
|
||||
const char* last_pub_err = nostr_relay_pool_get_relay_last_publish_error(g_pool, url);
|
||||
const char* last_conn_err = nostr_relay_pool_get_relay_last_connection_error(g_pool, url);
|
||||
if (last_pub_err && last_pub_err[0] != '\0') {
|
||||
cJSON_AddStringToObject(relay, "last_publish_error", last_pub_err);
|
||||
}
|
||||
if (last_conn_err && last_conn_err[0] != '\0') {
|
||||
cJSON_AddStringToObject(relay, "last_connection_error", last_conn_err);
|
||||
}
|
||||
|
||||
cJSON_AddItemToArray(relays, relay);
|
||||
}
|
||||
|
||||
cJSON_AddNumberToObject(root, "relay_count", relay_count);
|
||||
cJSON_AddNumberToObject(root, "connected_count", connected_count);
|
||||
cJSON_AddItemToObject(root, "relays", relays);
|
||||
|
||||
char* out = cJSON_PrintUnformatted(root);
|
||||
cJSON_Delete(root);
|
||||
|
||||
if (relay_urls) {
|
||||
for (int i = 0; i < relay_count; i++) {
|
||||
free(relay_urls[i]);
|
||||
}
|
||||
free(relay_urls);
|
||||
}
|
||||
free(statuses);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
char* nostr_handler_get_admin_kind0_context(void) {
|
||||
if (!g_cfg || !g_cfg->admin_context.enabled || !g_cfg->admin_context.track_kind_0) {
|
||||
return NULL;
|
||||
|
||||
@@ -43,6 +43,7 @@ char* nostr_handler_get_admin_kind0_context(void);
|
||||
char* nostr_handler_get_admin_kind10002_context(void);
|
||||
char* nostr_handler_get_admin_kind1_notes_context(void);
|
||||
int nostr_handler_is_wot_contact(const char* pubkey_hex);
|
||||
char* nostr_handler_relay_status_json(void);
|
||||
void nostr_handler_cleanup(void);
|
||||
|
||||
#endif
|
||||
+469
@@ -508,6 +508,51 @@ void tools_cleanup(tools_context_t* ctx) {
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
|
||||
static int is_hex_string_len(const char* s, size_t expected_len) {
|
||||
if (!s || strlen(s) != expected_len) return 0;
|
||||
for (size_t i = 0; i < expected_len; i++) {
|
||||
unsigned char c = (unsigned char)s[i];
|
||||
if (!isxdigit(c)) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cJSON* parse_tool_args_json(const char* args_json) {
|
||||
const char* raw_args_json = args_json ? args_json : "{}";
|
||||
raw_args_json = skip_ws(raw_args_json);
|
||||
if (!raw_args_json || raw_args_json[0] == '\0') {
|
||||
raw_args_json = "{}";
|
||||
}
|
||||
|
||||
cJSON* args = cJSON_Parse(raw_args_json);
|
||||
char* repaired_args_json = NULL;
|
||||
|
||||
if (args && cJSON_IsString(args) && args->valuestring) {
|
||||
cJSON* nested = cJSON_Parse(args->valuestring);
|
||||
if (nested) {
|
||||
cJSON_Delete(args);
|
||||
args = nested;
|
||||
}
|
||||
}
|
||||
|
||||
if (!args) {
|
||||
repaired_args_json = sanitize_json_string_controls(raw_args_json);
|
||||
if (repaired_args_json) {
|
||||
args = cJSON_Parse(repaired_args_json);
|
||||
if (args && cJSON_IsString(args) && args->valuestring) {
|
||||
cJSON* nested = cJSON_Parse(args->valuestring);
|
||||
if (nested) {
|
||||
cJSON_Delete(args);
|
||||
args = nested;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(repaired_args_json);
|
||||
return args;
|
||||
}
|
||||
|
||||
char* tools_build_openai_schema_json(const tools_context_t* ctx) {
|
||||
(void)ctx;
|
||||
|
||||
@@ -668,6 +713,117 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) {
|
||||
cJSON_AddItemToObject(t6, "function", t6_fn);
|
||||
cJSON_AddItemToArray(tools, t6);
|
||||
|
||||
cJSON* t7 = cJSON_CreateObject();
|
||||
cJSON* t7_fn = cJSON_CreateObject();
|
||||
cJSON* t7_params = cJSON_CreateObject();
|
||||
cJSON* t7_props = cJSON_CreateObject();
|
||||
cJSON* t7_required = cJSON_CreateArray();
|
||||
|
||||
cJSON_AddStringToObject(t7, "type", "function");
|
||||
cJSON_AddStringToObject(t7_fn, "name", "nostr_delete");
|
||||
cJSON_AddStringToObject(t7_fn, "description", "Request deletion of one or more previously published events (NIP-09 kind 5)");
|
||||
cJSON_AddStringToObject(t7_params, "type", "object");
|
||||
cJSON_AddItemToObject(t7_params, "properties", t7_props);
|
||||
cJSON_AddItemToObject(t7_params, "required", t7_required);
|
||||
|
||||
cJSON* p_del_ids = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(p_del_ids, "type", "array");
|
||||
cJSON* p_del_ids_items = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(p_del_ids_items, "type", "string");
|
||||
cJSON_AddItemToObject(p_del_ids, "items", p_del_ids_items);
|
||||
cJSON_AddItemToObject(t7_props, "event_ids", p_del_ids);
|
||||
|
||||
cJSON* p_del_kinds = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(p_del_kinds, "type", "array");
|
||||
cJSON* p_del_kinds_items = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(p_del_kinds_items, "type", "integer");
|
||||
cJSON_AddItemToObject(p_del_kinds, "items", p_del_kinds_items);
|
||||
cJSON_AddItemToObject(t7_props, "kinds", p_del_kinds);
|
||||
|
||||
cJSON* p_del_reason = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(p_del_reason, "type", "string");
|
||||
cJSON_AddItemToObject(t7_props, "reason", p_del_reason);
|
||||
|
||||
cJSON_AddItemToArray(t7_required, cJSON_CreateString("event_ids"));
|
||||
|
||||
cJSON_AddItemToObject(t7_fn, "parameters", t7_params);
|
||||
cJSON_AddItemToObject(t7, "function", t7_fn);
|
||||
cJSON_AddItemToArray(tools, t7);
|
||||
|
||||
cJSON* t8 = cJSON_CreateObject();
|
||||
cJSON* t8_fn = cJSON_CreateObject();
|
||||
cJSON* t8_params = cJSON_CreateObject();
|
||||
cJSON* t8_props = cJSON_CreateObject();
|
||||
cJSON* t8_required = cJSON_CreateArray();
|
||||
|
||||
cJSON_AddStringToObject(t8, "type", "function");
|
||||
cJSON_AddStringToObject(t8_fn, "name", "nostr_react");
|
||||
cJSON_AddStringToObject(t8_fn, "description", "React to a Nostr event with like/dislike/emoji (NIP-25 kind 7)");
|
||||
cJSON_AddStringToObject(t8_params, "type", "object");
|
||||
cJSON_AddItemToObject(t8_params, "properties", t8_props);
|
||||
cJSON_AddItemToObject(t8_params, "required", t8_required);
|
||||
|
||||
cJSON* p_react_event_id = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(p_react_event_id, "type", "string");
|
||||
cJSON_AddItemToObject(t8_props, "event_id", p_react_event_id);
|
||||
|
||||
cJSON* p_react_event_pubkey = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(p_react_event_pubkey, "type", "string");
|
||||
cJSON_AddItemToObject(t8_props, "event_pubkey", p_react_event_pubkey);
|
||||
|
||||
cJSON* p_react_event_kind = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(p_react_event_kind, "type", "integer");
|
||||
cJSON_AddItemToObject(t8_props, "event_kind", p_react_event_kind);
|
||||
|
||||
cJSON* p_react_reaction = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(p_react_reaction, "type", "string");
|
||||
cJSON_AddItemToObject(t8_props, "reaction", p_react_reaction);
|
||||
|
||||
cJSON_AddItemToArray(t8_required, cJSON_CreateString("event_id"));
|
||||
cJSON_AddItemToArray(t8_required, cJSON_CreateString("event_pubkey"));
|
||||
|
||||
cJSON_AddItemToObject(t8_fn, "parameters", t8_params);
|
||||
cJSON_AddItemToObject(t8, "function", t8_fn);
|
||||
cJSON_AddItemToArray(tools, t8);
|
||||
|
||||
cJSON* t9 = cJSON_CreateObject();
|
||||
cJSON* t9_fn = cJSON_CreateObject();
|
||||
cJSON* t9_params = cJSON_CreateObject();
|
||||
cJSON* t9_props = cJSON_CreateObject();
|
||||
cJSON* t9_required = cJSON_CreateArray();
|
||||
|
||||
cJSON_AddStringToObject(t9, "type", "function");
|
||||
cJSON_AddStringToObject(t9_fn, "name", "nostr_profile_get");
|
||||
cJSON_AddStringToObject(t9_fn, "description", "Look up a Nostr profile (kind 0 metadata) by pubkey");
|
||||
cJSON_AddStringToObject(t9_params, "type", "object");
|
||||
cJSON_AddItemToObject(t9_params, "properties", t9_props);
|
||||
cJSON_AddItemToObject(t9_params, "required", t9_required);
|
||||
|
||||
cJSON* p_profile_pubkey = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(p_profile_pubkey, "type", "string");
|
||||
cJSON_AddItemToObject(t9_props, "pubkey", p_profile_pubkey);
|
||||
|
||||
cJSON_AddItemToArray(t9_required, cJSON_CreateString("pubkey"));
|
||||
|
||||
cJSON_AddItemToObject(t9_fn, "parameters", t9_params);
|
||||
cJSON_AddItemToObject(t9, "function", t9_fn);
|
||||
cJSON_AddItemToArray(tools, t9);
|
||||
|
||||
cJSON* t10 = cJSON_CreateObject();
|
||||
cJSON* t10_fn = cJSON_CreateObject();
|
||||
cJSON* t10_params = cJSON_CreateObject();
|
||||
cJSON* t10_props = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(t10, "type", "function");
|
||||
cJSON_AddStringToObject(t10_fn, "name", "nostr_relay_status");
|
||||
cJSON_AddStringToObject(t10_fn, "description", "Get connection status and statistics for all relays");
|
||||
cJSON_AddStringToObject(t10_params, "type", "object");
|
||||
cJSON_AddItemToObject(t10_params, "properties", t10_props);
|
||||
|
||||
cJSON_AddItemToObject(t10_fn, "parameters", t10_params);
|
||||
cJSON_AddItemToObject(t10, "function", t10_fn);
|
||||
cJSON_AddItemToArray(tools, t10);
|
||||
|
||||
char* out = cJSON_PrintUnformatted(tools);
|
||||
cJSON_Delete(tools);
|
||||
return out;
|
||||
@@ -955,6 +1111,307 @@ static char* execute_nostr_post_readme(tools_context_t* ctx, const char* args_js
|
||||
return json;
|
||||
}
|
||||
|
||||
static char* execute_nostr_delete(const char* args_json) {
|
||||
cJSON* args = parse_tool_args_json(args_json);
|
||||
if (!args) return json_error("invalid arguments JSON");
|
||||
|
||||
cJSON* event_ids = cJSON_GetObjectItemCaseSensitive(args, "event_ids");
|
||||
cJSON* kinds = cJSON_GetObjectItemCaseSensitive(args, "kinds");
|
||||
cJSON* reason = cJSON_GetObjectItemCaseSensitive(args, "reason");
|
||||
|
||||
if (!event_ids || !cJSON_IsArray(event_ids) || cJSON_GetArraySize(event_ids) <= 0) {
|
||||
cJSON_Delete(args);
|
||||
return json_error("nostr_delete requires non-empty event_ids array");
|
||||
}
|
||||
if (kinds && !cJSON_IsArray(kinds)) {
|
||||
cJSON_Delete(args);
|
||||
return json_error("nostr_delete kinds must be an array when provided");
|
||||
}
|
||||
if (reason && !cJSON_IsString(reason)) {
|
||||
cJSON_Delete(args);
|
||||
return json_error("nostr_delete reason must be a string when provided");
|
||||
}
|
||||
|
||||
cJSON* tags = cJSON_CreateArray();
|
||||
if (!tags) {
|
||||
cJSON_Delete(args);
|
||||
return json_error("failed to create tags");
|
||||
}
|
||||
|
||||
int event_id_count = cJSON_GetArraySize(event_ids);
|
||||
for (int i = 0; i < event_id_count; i++) {
|
||||
cJSON* id = cJSON_GetArrayItem(event_ids, i);
|
||||
if (!id || !cJSON_IsString(id) || !id->valuestring || !is_hex_string_len(id->valuestring, 64U)) {
|
||||
cJSON_Delete(tags);
|
||||
cJSON_Delete(args);
|
||||
return json_error("nostr_delete event_ids must contain 64-char hex strings");
|
||||
}
|
||||
if (add_string_tag(tags, "e", id->valuestring) != 0) {
|
||||
cJSON_Delete(tags);
|
||||
cJSON_Delete(args);
|
||||
return json_error("failed to add e tag");
|
||||
}
|
||||
}
|
||||
|
||||
if (kinds) {
|
||||
int kind_count = cJSON_GetArraySize(kinds);
|
||||
for (int i = 0; i < kind_count; i++) {
|
||||
cJSON* k = cJSON_GetArrayItem(kinds, i);
|
||||
if (!k || !cJSON_IsNumber(k)) {
|
||||
cJSON_Delete(tags);
|
||||
cJSON_Delete(args);
|
||||
return json_error("nostr_delete kinds must contain integers");
|
||||
}
|
||||
char kind_buf[32];
|
||||
snprintf(kind_buf, sizeof(kind_buf), "%d", (int)k->valuedouble);
|
||||
if (add_string_tag(tags, "k", kind_buf) != 0) {
|
||||
cJSON_Delete(tags);
|
||||
cJSON_Delete(args);
|
||||
return json_error("failed to add k tag");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nostr_publish_result_t publish_result;
|
||||
memset(&publish_result, 0, sizeof(publish_result));
|
||||
|
||||
const char* reason_content = (reason && reason->valuestring) ? reason->valuestring : "";
|
||||
int rc = nostr_handler_publish_kind_event(5, reason_content, tags, &publish_result);
|
||||
cJSON_Delete(tags);
|
||||
cJSON_Delete(args);
|
||||
if (rc != 0) {
|
||||
nostr_handler_publish_result_free(&publish_result);
|
||||
return json_error("nostr_delete failed");
|
||||
}
|
||||
|
||||
cJSON* out = cJSON_CreateObject();
|
||||
if (!out) {
|
||||
nostr_handler_publish_result_free(&publish_result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON_AddBoolToObject(out, "success", publish_result.success ? 1 : 0);
|
||||
cJSON_AddStringToObject(out, "message", "nostr_delete published");
|
||||
cJSON_AddNumberToObject(out, "kind", publish_result.kind);
|
||||
cJSON_AddStringToObject(out, "event_id", publish_result.event_id);
|
||||
cJSON_AddNumberToObject(out, "requested_event_count", event_id_count);
|
||||
cJSON_AddNumberToObject(out, "relay_count", publish_result.relay_count);
|
||||
cJSON_AddNumberToObject(out, "accepted_by_pool_count", publish_result.accepted_by_pool_count);
|
||||
|
||||
cJSON* relays = cJSON_CreateArray();
|
||||
if (!relays) {
|
||||
nostr_handler_publish_result_free(&publish_result);
|
||||
cJSON_Delete(out);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < publish_result.relay_count; i++) {
|
||||
cJSON_AddItemToArray(relays, cJSON_CreateString(publish_result.relays[i] ? publish_result.relays[i] : ""));
|
||||
}
|
||||
cJSON_AddItemToObject(out, "relays", relays);
|
||||
|
||||
char* json = cJSON_PrintUnformatted(out);
|
||||
cJSON_Delete(out);
|
||||
nostr_handler_publish_result_free(&publish_result);
|
||||
return json;
|
||||
}
|
||||
|
||||
static char* execute_nostr_react(const char* args_json) {
|
||||
cJSON* args = parse_tool_args_json(args_json);
|
||||
if (!args) return json_error("invalid arguments JSON");
|
||||
|
||||
cJSON* event_id = cJSON_GetObjectItemCaseSensitive(args, "event_id");
|
||||
cJSON* event_pubkey = cJSON_GetObjectItemCaseSensitive(args, "event_pubkey");
|
||||
cJSON* event_kind = cJSON_GetObjectItemCaseSensitive(args, "event_kind");
|
||||
cJSON* reaction = cJSON_GetObjectItemCaseSensitive(args, "reaction");
|
||||
|
||||
if (!event_id || !cJSON_IsString(event_id) || !event_id->valuestring || !is_hex_string_len(event_id->valuestring, 64U) ||
|
||||
!event_pubkey || !cJSON_IsString(event_pubkey) || !event_pubkey->valuestring || !is_hex_string_len(event_pubkey->valuestring, 64U)) {
|
||||
cJSON_Delete(args);
|
||||
return json_error("nostr_react requires event_id and event_pubkey as 64-char hex strings");
|
||||
}
|
||||
if (event_kind && !cJSON_IsNumber(event_kind)) {
|
||||
cJSON_Delete(args);
|
||||
return json_error("nostr_react event_kind must be an integer when provided");
|
||||
}
|
||||
if (reaction && !cJSON_IsString(reaction)) {
|
||||
cJSON_Delete(args);
|
||||
return json_error("nostr_react reaction must be a string when provided");
|
||||
}
|
||||
|
||||
cJSON* tags = cJSON_CreateArray();
|
||||
if (!tags) {
|
||||
cJSON_Delete(args);
|
||||
return json_error("failed to create tags");
|
||||
}
|
||||
|
||||
if (add_string_tag(tags, "e", event_id->valuestring) != 0 ||
|
||||
add_string_tag(tags, "p", event_pubkey->valuestring) != 0) {
|
||||
cJSON_Delete(tags);
|
||||
cJSON_Delete(args);
|
||||
return json_error("failed to add required reaction tags");
|
||||
}
|
||||
|
||||
if (event_kind) {
|
||||
char kind_buf[32];
|
||||
snprintf(kind_buf, sizeof(kind_buf), "%d", (int)event_kind->valuedouble);
|
||||
if (add_string_tag(tags, "k", kind_buf) != 0) {
|
||||
cJSON_Delete(tags);
|
||||
cJSON_Delete(args);
|
||||
return json_error("failed to add k tag");
|
||||
}
|
||||
}
|
||||
|
||||
const char* reaction_content = (reaction && reaction->valuestring && reaction->valuestring[0] != '\0') ? reaction->valuestring : "+";
|
||||
|
||||
nostr_publish_result_t publish_result;
|
||||
memset(&publish_result, 0, sizeof(publish_result));
|
||||
|
||||
int rc = nostr_handler_publish_kind_event(7, reaction_content, tags, &publish_result);
|
||||
cJSON_Delete(tags);
|
||||
cJSON_Delete(args);
|
||||
if (rc != 0) {
|
||||
nostr_handler_publish_result_free(&publish_result);
|
||||
return json_error("nostr_react failed");
|
||||
}
|
||||
|
||||
cJSON* out = cJSON_CreateObject();
|
||||
if (!out) {
|
||||
nostr_handler_publish_result_free(&publish_result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON_AddBoolToObject(out, "success", publish_result.success ? 1 : 0);
|
||||
cJSON_AddStringToObject(out, "message", "nostr_react published");
|
||||
cJSON_AddStringToObject(out, "reaction", reaction_content);
|
||||
cJSON_AddNumberToObject(out, "kind", publish_result.kind);
|
||||
cJSON_AddStringToObject(out, "event_id", publish_result.event_id);
|
||||
cJSON_AddNumberToObject(out, "relay_count", publish_result.relay_count);
|
||||
cJSON_AddNumberToObject(out, "accepted_by_pool_count", publish_result.accepted_by_pool_count);
|
||||
|
||||
cJSON* relays = cJSON_CreateArray();
|
||||
if (!relays) {
|
||||
nostr_handler_publish_result_free(&publish_result);
|
||||
cJSON_Delete(out);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < publish_result.relay_count; i++) {
|
||||
cJSON_AddItemToArray(relays, cJSON_CreateString(publish_result.relays[i] ? publish_result.relays[i] : ""));
|
||||
}
|
||||
cJSON_AddItemToObject(out, "relays", relays);
|
||||
|
||||
char* json = cJSON_PrintUnformatted(out);
|
||||
cJSON_Delete(out);
|
||||
nostr_handler_publish_result_free(&publish_result);
|
||||
return json;
|
||||
}
|
||||
|
||||
static char* execute_nostr_profile_get(const char* args_json) {
|
||||
cJSON* args = parse_tool_args_json(args_json);
|
||||
if (!args) return json_error("invalid arguments JSON");
|
||||
|
||||
cJSON* pubkey = cJSON_GetObjectItemCaseSensitive(args, "pubkey");
|
||||
if (!pubkey || !cJSON_IsString(pubkey) || !pubkey->valuestring || !is_hex_string_len(pubkey->valuestring, 64U)) {
|
||||
cJSON_Delete(args);
|
||||
return json_error("nostr_profile_get requires pubkey as 64-char hex string");
|
||||
}
|
||||
|
||||
cJSON* filter = cJSON_CreateObject();
|
||||
cJSON* kinds = cJSON_CreateArray();
|
||||
cJSON* authors = cJSON_CreateArray();
|
||||
if (!filter || !kinds || !authors) {
|
||||
cJSON_Delete(filter);
|
||||
cJSON_Delete(kinds);
|
||||
cJSON_Delete(authors);
|
||||
cJSON_Delete(args);
|
||||
return json_error("failed to create profile query filter");
|
||||
}
|
||||
|
||||
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(0));
|
||||
cJSON_AddItemToObject(filter, "kinds", kinds);
|
||||
cJSON_AddItemToArray(authors, cJSON_CreateString(pubkey->valuestring));
|
||||
cJSON_AddItemToObject(filter, "authors", authors);
|
||||
cJSON_AddNumberToObject(filter, "limit", 1);
|
||||
|
||||
char* events_json = nostr_handler_query_json(filter, 8000);
|
||||
cJSON_Delete(filter);
|
||||
cJSON_Delete(args);
|
||||
if (!events_json) {
|
||||
return json_error("nostr_profile_get query failed");
|
||||
}
|
||||
|
||||
cJSON* events = cJSON_Parse(events_json);
|
||||
free(events_json);
|
||||
if (!events || !cJSON_IsArray(events)) {
|
||||
cJSON_Delete(events);
|
||||
return json_error("nostr_profile_get returned invalid events JSON");
|
||||
}
|
||||
|
||||
cJSON* out = cJSON_CreateObject();
|
||||
if (!out) {
|
||||
cJSON_Delete(events);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON_AddBoolToObject(out, "success", 1);
|
||||
|
||||
int found = cJSON_GetArraySize(events) > 0;
|
||||
cJSON_AddBoolToObject(out, "found", found ? 1 : 0);
|
||||
|
||||
if (found) {
|
||||
cJSON* ev = cJSON_GetArrayItem(events, 0);
|
||||
cJSON* ev_dup = ev ? cJSON_Duplicate(ev, 1) : NULL;
|
||||
if (ev_dup) {
|
||||
cJSON_AddItemToObject(out, "event", ev_dup);
|
||||
}
|
||||
|
||||
cJSON* content = ev ? cJSON_GetObjectItemCaseSensitive(ev, "content") : NULL;
|
||||
if (content && cJSON_IsString(content) && content->valuestring) {
|
||||
cJSON* profile = cJSON_Parse(content->valuestring);
|
||||
if (profile && cJSON_IsObject(profile)) {
|
||||
cJSON_AddItemToObject(out, "profile", profile);
|
||||
} else {
|
||||
cJSON_Delete(profile);
|
||||
cJSON_AddStringToObject(out, "profile_raw", content->valuestring);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(events);
|
||||
char* json = cJSON_PrintUnformatted(out);
|
||||
cJSON_Delete(out);
|
||||
return json;
|
||||
}
|
||||
|
||||
static char* execute_nostr_relay_status(const char* args_json) {
|
||||
cJSON* args = parse_tool_args_json(args_json);
|
||||
if (!args) return json_error("invalid arguments JSON");
|
||||
cJSON_Delete(args);
|
||||
|
||||
char* status_json = nostr_handler_relay_status_json();
|
||||
if (!status_json) {
|
||||
return json_error("nostr_relay_status failed");
|
||||
}
|
||||
|
||||
cJSON* status = cJSON_Parse(status_json);
|
||||
free(status_json);
|
||||
if (!status) {
|
||||
return json_error("nostr_relay_status returned invalid JSON");
|
||||
}
|
||||
|
||||
cJSON* out = cJSON_CreateObject();
|
||||
if (!out) {
|
||||
cJSON_Delete(status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON_AddBoolToObject(out, "success", 1);
|
||||
cJSON_AddItemToObject(out, "status", status);
|
||||
|
||||
char* json = cJSON_PrintUnformatted(out);
|
||||
cJSON_Delete(out);
|
||||
return json;
|
||||
}
|
||||
|
||||
static char* execute_nostr_query(const char* args_json) {
|
||||
cJSON* args = cJSON_Parse(args_json ? args_json : "{}");
|
||||
if (!args) return json_error("invalid arguments JSON");
|
||||
@@ -1167,6 +1624,18 @@ char* tools_execute(tools_context_t* ctx, const char* tool_name, const char* arg
|
||||
if (strcmp(tool_name, "nostr_post") == 0) {
|
||||
return execute_nostr_post(args_json);
|
||||
}
|
||||
if (strcmp(tool_name, "nostr_delete") == 0) {
|
||||
return execute_nostr_delete(args_json);
|
||||
}
|
||||
if (strcmp(tool_name, "nostr_react") == 0) {
|
||||
return execute_nostr_react(args_json);
|
||||
}
|
||||
if (strcmp(tool_name, "nostr_profile_get") == 0) {
|
||||
return execute_nostr_profile_get(args_json);
|
||||
}
|
||||
if (strcmp(tool_name, "nostr_relay_status") == 0) {
|
||||
return execute_nostr_relay_status(args_json);
|
||||
}
|
||||
if (strcmp(tool_name, "nostr_query") == 0) {
|
||||
return execute_nostr_query(args_json);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user