Files
nostr_terminal/src/menu_relays.c

267 lines
9.9 KiB
C

#include "tui.h"
#include "state.h"
#include "net.h"
#include "publish.h"
#include "../resources/nostr_core_lib/cjson/cJSON.h"
#include <stdlib.h>
#include <string.h>
static int relays_publish_kind10002(cJSON *event_obj) {
cJSON *tags;
cJSON *content;
if (!event_obj) {
return -1;
}
tags = cJSON_GetObjectItemCaseSensitive(event_obj, "tags");
content = cJSON_GetObjectItemCaseSensitive(event_obj, "content");
return publish_signed_event_with_report("Relay list",
10002,
(cJSON_IsString(content) && content->valuestring) ? content->valuestring : "",
tags,
8000);
}
static void relays_print_nip11_summary(const char *nip11_json) {
cJSON *doc;
cJSON *name;
cJSON *description;
cJSON *software;
cJSON *version;
cJSON *pubkey;
cJSON *contact;
cJSON *supported_nips;
cJSON *limitation;
if (!nip11_json || nip11_json[0] == '\0') {
tui_print("NIP-11: (empty response)");
return;
}
doc = cJSON_Parse(nip11_json);
if (!doc || !cJSON_IsObject(doc)) {
tui_print("NIP-11 (raw): %s", nip11_json);
cJSON_Delete(doc);
return;
}
name = cJSON_GetObjectItemCaseSensitive(doc, "name");
description = cJSON_GetObjectItemCaseSensitive(doc, "description");
software = cJSON_GetObjectItemCaseSensitive(doc, "software");
version = cJSON_GetObjectItemCaseSensitive(doc, "version");
pubkey = cJSON_GetObjectItemCaseSensitive(doc, "pubkey");
contact = cJSON_GetObjectItemCaseSensitive(doc, "contact");
supported_nips = cJSON_GetObjectItemCaseSensitive(doc, "supported_nips");
limitation = cJSON_GetObjectItemCaseSensitive(doc, "limitation");
tui_print("NIP-11 relay profile:");
tui_print(" Name: %s", (cJSON_IsString(name) && name->valuestring) ? name->valuestring : "(n/a)");
tui_print(" Description: %s", (cJSON_IsString(description) && description->valuestring) ? description->valuestring : "(n/a)");
tui_print(" Software: %s", (cJSON_IsString(software) && software->valuestring) ? software->valuestring : "(n/a)");
tui_print(" Version: %s", (cJSON_IsString(version) && version->valuestring) ? version->valuestring : "(n/a)");
tui_print(" Contact: %s", (cJSON_IsString(contact) && contact->valuestring) ? contact->valuestring : "(n/a)");
tui_print(" Pubkey: %s", (cJSON_IsString(pubkey) && pubkey->valuestring) ? pubkey->valuestring : "(n/a)");
if (cJSON_IsArray(supported_nips)) {
char *nips_json = cJSON_PrintUnformatted(supported_nips);
if (nips_json) {
tui_print(" Supported NIPs: %s", nips_json);
free(nips_json);
}
}
if (cJSON_IsObject(limitation)) {
cJSON *max_subscriptions = cJSON_GetObjectItemCaseSensitive(limitation, "max_subscriptions");
cJSON *max_message_length = cJSON_GetObjectItemCaseSensitive(limitation, "max_message_length");
cJSON *max_limit = cJSON_GetObjectItemCaseSensitive(limitation, "max_limit");
tui_print(" Limits:");
if (cJSON_IsNumber(max_subscriptions)) {
tui_print(" max_subscriptions: %d", max_subscriptions->valueint);
}
if (cJSON_IsNumber(max_message_length)) {
tui_print(" max_message_length: %d", max_message_length->valueint);
}
if (cJSON_IsNumber(max_limit)) {
tui_print(" max_limit: %d", max_limit->valueint);
}
}
cJSON_Delete(doc);
}
void menu_relays(void) {
cJSON *working = NULL;
cJSON *tags = NULL;
char input[512];
working = g_state.kind10002_json ? cJSON_Parse(g_state.kind10002_json) : NULL;
if (!working) {
working = cJSON_CreateObject();
cJSON_AddNumberToObject(working, "kind", 10002);
cJSON_AddStringToObject(working, "content", "");
cJSON_AddItemToObject(working, "tags", cJSON_CreateArray());
}
tags = cJSON_GetObjectItemCaseSensitive(working, "tags");
if (!cJSON_IsArray(tags)) {
cJSON_DeleteItemFromObjectCaseSensitive(working, "tags");
cJSON_AddItemToObject(working, "tags", cJSON_CreateArray());
tags = cJSON_GetObjectItemCaseSensitive(working, "tags");
}
while (1) {
int i;
int n;
tui_clear_screen();
tui_print("RELAYS\n");
n = cJSON_GetArraySize(tags);
for (i = 0; i < n; i++) {
cJSON *tag = cJSON_GetArrayItem(tags, i);
cJSON *t0 = cJSON_GetArrayItem(tag, 0);
cJSON *t1 = cJSON_GetArrayItem(tag, 1);
cJSON *t2 = cJSON_GetArrayItem(tag, 2);
const char *rw = "read write";
if (!cJSON_IsArray(tag) || !cJSON_IsString(t0) || !t0->valuestring || strcmp(t0->valuestring, "r") != 0) {
continue;
}
if (cJSON_IsString(t2) && t2->valuestring) {
rw = t2->valuestring;
}
tui_print("[%2d] %-30s %s", i + 1, (cJSON_IsString(t1) && t1->valuestring) ? t1->valuestring : "", rw);
}
tui_print("\n\n");
tui_print("^_A^:dd relay");
tui_print("^_D^:elete relay");
tui_print("^_M^:odify relay");
tui_print("^_P^:ost changes and exit.");
tui_print("E^_x^:it without saving");
tui_get_line(">", input, (int)sizeof(input));
if (input[0] == 'a' || input[0] == 'A') {
char relay_url[256];
char yn[32];
char read_yn[32];
char write_yn[32];
char *nip11 = NULL;
tui_print("\n\nADD RELAY\n");
tui_get_line("Relay URL >", relay_url, (int)sizeof(relay_url));
if (relay_url[0] == '\0') {
continue;
}
tui_print("Gathering relay data ...");
if (nt_fetch_nip11(relay_url, &nip11) == 0 && nip11) {
relays_print_nip11_summary(nip11);
free(nip11);
} else {
tui_print("NIP-11 fetch failed.");
}
tui_get_line("Add relay [y/n] >", yn, (int)sizeof(yn));
if (yn[0] == 'y' || yn[0] == 'Y') {
cJSON *tag = cJSON_CreateArray();
cJSON_AddItemToArray(tag, cJSON_CreateString("r"));
cJSON_AddItemToArray(tag, cJSON_CreateString(relay_url));
tui_get_line("Read from relay [y/n] >", read_yn, (int)sizeof(read_yn));
tui_get_line("Write to relay [y/n] >", write_yn, (int)sizeof(write_yn));
if ((read_yn[0] == 'n' || read_yn[0] == 'N') && (write_yn[0] == 'y' || write_yn[0] == 'Y')) {
cJSON_AddItemToArray(tag, cJSON_CreateString("write"));
} else if ((read_yn[0] == 'y' || read_yn[0] == 'Y') && (write_yn[0] == 'n' || write_yn[0] == 'N')) {
cJSON_AddItemToArray(tag, cJSON_CreateString("read"));
}
cJSON_AddItemToArray(tags, tag);
}
} else if (input[0] == 'd' || input[0] == 'D') {
char numbuf[32];
char yn[32];
int idx;
cJSON *tag;
cJSON *url;
tui_get_line("relay to delete >", numbuf, (int)sizeof(numbuf));
idx = atoi(numbuf) - 1;
if (idx < 0 || idx >= cJSON_GetArraySize(tags)) {
continue;
}
tag = cJSON_GetArrayItem(tags, idx);
url = cJSON_GetArrayItem(tag, 1);
tui_get_line("Delete selected relay [y/n] >", yn, (int)sizeof(yn));
if (yn[0] == 'y' || yn[0] == 'Y') {
cJSON_DeleteItemFromArray(tags, idx);
if (cJSON_IsString(url) && url->valuestring) {
tui_print("Deleted %s", url->valuestring);
}
}
} else if (input[0] == 'm' || input[0] == 'M') {
char numbuf[32];
char read_yn[32];
char write_yn[32];
int idx;
cJSON *old_tag;
cJSON *url;
cJSON *new_tag;
tui_get_line("# relay to modify >", numbuf, (int)sizeof(numbuf));
idx = atoi(numbuf) - 1;
if (idx < 0 || idx >= cJSON_GetArraySize(tags)) {
continue;
}
old_tag = cJSON_GetArrayItem(tags, idx);
url = cJSON_GetArrayItem(old_tag, 1);
if (!cJSON_IsString(url) || !url->valuestring || url->valuestring[0] == '\0') {
continue;
}
new_tag = cJSON_CreateArray();
cJSON_AddItemToArray(new_tag, cJSON_CreateString("r"));
cJSON_AddItemToArray(new_tag, cJSON_CreateString(url->valuestring));
tui_get_line("Read from relay [y/n]?", read_yn, (int)sizeof(read_yn));
tui_get_line("Write to relay [y/n]?", write_yn, (int)sizeof(write_yn));
if ((read_yn[0] == 'n' || read_yn[0] == 'N') && (write_yn[0] == 'y' || write_yn[0] == 'Y')) {
cJSON_AddItemToArray(new_tag, cJSON_CreateString("write"));
} else if ((read_yn[0] == 'y' || read_yn[0] == 'Y') && (write_yn[0] == 'n' || write_yn[0] == 'N')) {
cJSON_AddItemToArray(new_tag, cJSON_CreateString("read"));
}
cJSON_ReplaceItemInArray(tags, idx, new_tag);
} else if (input[0] == 'p' || input[0] == 'P') {
int posted = relays_publish_kind10002(working);
char *new_json = cJSON_PrintUnformatted(working);
if (new_json) {
free(g_state.kind10002_json);
g_state.kind10002_json = new_json;
state_parse_relay_list();
}
if (posted < 0) {
tui_print("Relay list publish failed.");
}
tui_get_line(">", input, (int)sizeof(input));
break;
} else if (input[0] == 'x' || input[0] == 'X' || input[0] == 'q' || input[0] == 'Q') {
break;
}
}
cJSON_Delete(working);
}