Files
nostr_terminal/src/menu_follows.c
T

271 lines
9.6 KiB
C

#include "tui.h"
#include "state.h"
#include "net.h"
#include "publish.h"
#include "../resources/nostr_core_lib/cjson/cJSON.h"
#include "../resources/nostr_core_lib/nostr_core/nip019.h"
#include "../resources/nostr_core_lib/nostr_core/utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int follows_publish_kind3(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("Follows",
3,
(cJSON_IsString(content) && content->valuestring) ? content->valuestring : "",
tags,
8000);
}
void menu_follows(void) {
cJSON *working = NULL;
cJSON *tags = NULL;
char input[256];
working = g_state.kind3_json ? cJSON_Parse(g_state.kind3_json) : NULL;
if (!working) {
working = cJSON_CreateObject();
cJSON_AddNumberToObject(working, "kind", 3);
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("FOLLOWS");
tui_print("\n\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 *t3 = cJSON_GetArrayItem(tag, 3);
char npub[128] = {0};
if (!cJSON_IsArray(tag) || !cJSON_IsString(t0) || strcmp(t0->valuestring, "p") != 0) {
continue;
}
if (cJSON_IsString(t1) && t1->valuestring) {
unsigned char pub[32];
if (strlen(t1->valuestring) == 64 && nostr_hex_to_bytes(t1->valuestring, pub, 32) == 0) {
if (nostr_key_to_bech32(pub, "npub", npub) != 0) {
snprintf(npub, sizeof(npub), "%s", t1->valuestring);
}
} else {
snprintf(npub, sizeof(npub), "%s", t1->valuestring);
}
}
tui_print("[%2d] %s %s",
i + 1,
npub,
(cJSON_IsString(t3) && t3->valuestring) ? t3->valuestring : "");
}
tui_print("\n\n");
tui_print("P^_r^:ofile");
tui_print("^_A^:dd follow");
tui_print("^_D^:elete follow");
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 who[256];
char yn[32];
char follow_hex[65] = {0};
char filter[256];
char *follow_kind0 = NULL;
cJSON *ev;
cJSON *content;
cJSON *meta;
cJSON *name;
tui_get_line("npub to add >", who, (int)sizeof(who));
if (who[0] == '\0') {
continue;
}
if (strncmp(who, "npub", 4) == 0) {
unsigned char pub[32];
if (nostr_decode_npub(who, pub) != 0) {
tui_print("Invalid npub.");
tui_get_line(">", input, (int)sizeof(input));
continue;
}
nostr_bytes_to_hex(pub, 32, follow_hex);
} else if (strlen(who) == 64) {
unsigned char pub[32];
if (nostr_hex_to_bytes(who, pub, 32) != 0) {
tui_print("Invalid hex pubkey.");
tui_get_line(">", input, (int)sizeof(input));
continue;
}
snprintf(follow_hex, sizeof(follow_hex), "%s", who);
} else {
tui_print("Enter npub or 64-char hex pubkey.");
tui_get_line(">", input, (int)sizeof(input));
continue;
}
snprintf(filter, sizeof(filter), "{\"authors\":[\"%s\"],\"kinds\":[0],\"limit\":1}", follow_hex);
if (nt_get_first(filter,
state_get_read_relays(NULL),
g_state.read_relay_count > 0 ? g_state.read_relay_count : g_state.bootstrap_relay_count,
7000,
&follow_kind0) != 0 || !follow_kind0) {
tui_print("Can't find user. Try adding more relays.");
tui_get_line(">", input, (int)sizeof(input));
continue;
}
ev = cJSON_Parse(follow_kind0);
meta = NULL;
name = NULL;
if (ev) {
content = cJSON_GetObjectItemCaseSensitive(ev, "content");
if (cJSON_IsString(content) && content->valuestring) {
meta = cJSON_Parse(content->valuestring);
if (meta) {
name = cJSON_GetObjectItemCaseSensitive(meta, "name");
}
}
}
tui_get_line((cJSON_IsString(name) && name->valuestring)
? "Add follow [y/n] >"
: "Add this follow [y/n] >",
yn,
(int)sizeof(yn));
if (yn[0] == 'y' || yn[0] == 'Y') {
cJSON *tag = cJSON_CreateArray();
cJSON_AddItemToArray(tag, cJSON_CreateString("p"));
cJSON_AddItemToArray(tag, cJSON_CreateString(follow_hex));
cJSON_AddItemToArray(tag, cJSON_CreateString(""));
cJSON_AddItemToArray(tag, cJSON_CreateString((cJSON_IsString(name) && name->valuestring) ? name->valuestring : ""));
cJSON_AddItemToArray(tags, tag);
}
cJSON_Delete(meta);
cJSON_Delete(ev);
free(follow_kind0);
} else if (input[0] == 'd' || input[0] == 'D') {
char numbuf[32];
char yn[32];
int idx;
tui_get_line("# to delete >", numbuf, (int)sizeof(numbuf));
idx = atoi(numbuf) - 1;
if (idx < 0 || idx >= cJSON_GetArraySize(tags)) {
continue;
}
tui_get_line("Delete follow [y/n] >", yn, (int)sizeof(yn));
if (yn[0] == 'y' || yn[0] == 'Y') {
cJSON_DeleteItemFromArray(tags, idx);
}
} else if (input[0] == 'r' || input[0] == 'R') {
char numbuf[32];
int idx;
cJSON *tag;
cJSON *pub;
char filter[256];
char *follow_kind0 = NULL;
cJSON *ev;
cJSON *content;
cJSON *meta;
tui_get_line("#>", numbuf, (int)sizeof(numbuf));
idx = atoi(numbuf) - 1;
if (idx < 0 || idx >= cJSON_GetArraySize(tags)) {
continue;
}
tag = cJSON_GetArrayItem(tags, idx);
pub = cJSON_GetArrayItem(tag, 1);
if (!cJSON_IsString(pub) || !pub->valuestring) {
continue;
}
snprintf(filter, sizeof(filter), "{\"authors\":[\"%s\"],\"kinds\":[0],\"limit\":1}", pub->valuestring);
if (nt_get_first(filter,
state_get_read_relays(NULL),
g_state.read_relay_count > 0 ? g_state.read_relay_count : g_state.bootstrap_relay_count,
7000,
&follow_kind0) != 0 || !follow_kind0) {
tui_print("Unable to fetch profile.");
tui_get_line(">", input, (int)sizeof(input));
continue;
}
ev = cJSON_Parse(follow_kind0);
meta = NULL;
if (ev) {
content = cJSON_GetObjectItemCaseSensitive(ev, "content");
if (cJSON_IsString(content) && content->valuestring) {
meta = cJSON_Parse(content->valuestring);
}
}
tui_print("PROFILE");
if (meta) {
cJSON *it = NULL;
cJSON_ArrayForEach(it, meta) {
if (cJSON_IsString(it) && it->valuestring) {
tui_print("%s: %s", it->string ? it->string : "field", it->valuestring);
}
}
} else {
tui_print("No metadata content.");
}
tui_get_line(">", input, (int)sizeof(input));
cJSON_Delete(meta);
cJSON_Delete(ev);
free(follow_kind0);
} else if (input[0] == 'p' || input[0] == 'P') {
int posted = follows_publish_kind3(working);
char *new_json = cJSON_PrintUnformatted(working);
if (new_json) {
free(g_state.kind3_json);
g_state.kind3_json = new_json;
}
if (posted < 0) {
tui_print("Follows publish failed.");
}
tui_get_line(">", input, (int)sizeof(input));
break;
} else if (input[0] == 'x' || input[0] == 'X') {
break;
}
}
cJSON_Delete(working);
}