Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad99f74bc0 | ||
|
|
cf9c300fdf | ||
|
|
97585890b9 | ||
|
|
7b3d36a797 | ||
|
|
49579b17a4 | ||
|
|
17083de47d | ||
|
|
33884046af | ||
|
|
7e69819be5 | ||
|
|
d189d0ba8c | ||
|
|
2cda3b6a58 | ||
|
|
ecb58b7e11 | ||
|
|
c3645e6af5 | ||
|
|
6cc46d2c25 |
@@ -51,11 +51,11 @@ Agents learn capabilities through skills — Nostr events that any agent can di
|
||||
|
||||
Didactyl will support local inference, which is very privacy preserving. Remote inference does however have it's advantages, and in those cases Didactyl supports using Bitcoin Lightning and eCash inference providers.
|
||||
|
||||
## Current Status — v0.0.30
|
||||
## Current Status — v0.0.40
|
||||
|
||||
**Active build — this project is barely working. Experiment at your own risk.**
|
||||
|
||||
> Last release update: v0.0.30 — Add config-controlled dm_protocol (nip04/nip17/both), NIP-17 receive handling, protocol-aware auto DM routing, and config updates
|
||||
> Last release update: v0.0.40 — Reduce query churn and add relay disconnect-cause visibility
|
||||
|
||||
- Connects to configured relays with auto-reconnect and relay state transition logging
|
||||
- Publishes configured startup events per relay as each relay becomes connected
|
||||
|
||||
+2711
-7775
File diff suppressed because one or more lines are too long
+4
-4
@@ -35,7 +35,7 @@ static pthread_mutex_t g_context_part_names_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define AGENT_DEBOUNCE_CACHE_SIZE 256
|
||||
#define AGENT_HISTORY_TURNS 12
|
||||
#define AGENT_HISTORY_QUERY_LIMIT 200
|
||||
#define AGENT_ADOPTED_SKILLS_CACHE_TTL_SECONDS 30
|
||||
#define AGENT_ADOPTED_SKILLS_CACHE_TTL_SECONDS 300
|
||||
#define AGENT_ADOPTED_SKILLS_MAX 24
|
||||
#define AGENT_ADOPTED_SKILL_CONTENT_MAX_CHARS 1800
|
||||
#define AGENT_ADOPTED_SKILLS_TOTAL_MAX_CHARS 7200
|
||||
@@ -1415,7 +1415,7 @@ static int append_recent_admin_dm_history(cJSON* messages, const char* current_m
|
||||
cJSON_AddItemToObject(filter, "authors", authors);
|
||||
cJSON_AddNumberToObject(filter, "limit", AGENT_HISTORY_QUERY_LIMIT);
|
||||
|
||||
char* events_json = nostr_handler_query_json(filter, 8000);
|
||||
char* events_json = nostr_handler_query_json(filter, 1500);
|
||||
cJSON_Delete(filter);
|
||||
if (!events_json) {
|
||||
return 0;
|
||||
@@ -1648,7 +1648,7 @@ static int refresh_adopted_skills_cache_if_needed(void) {
|
||||
cJSON_AddItemToObject(adopt_filter, "authors", authors);
|
||||
cJSON_AddNumberToObject(adopt_filter, "limit", 1);
|
||||
|
||||
char* adoption_json = nostr_handler_query_json(adopt_filter, 8000);
|
||||
char* adoption_json = nostr_handler_query_json(adopt_filter, 2000);
|
||||
cJSON_Delete(adopt_filter);
|
||||
|
||||
cJSON* adoption_events = adoption_json ? cJSON_Parse(adoption_json) : NULL;
|
||||
@@ -1700,7 +1700,7 @@ static int refresh_adopted_skills_cache_if_needed(void) {
|
||||
cJSON_AddItemToObject(skill_filter, "#d", d_values);
|
||||
cJSON_AddNumberToObject(skill_filter, "limit", 1);
|
||||
|
||||
char* skill_json = nostr_handler_query_json(skill_filter, 8000);
|
||||
char* skill_json = nostr_handler_query_json(skill_filter, 2000);
|
||||
cJSON_Delete(skill_filter);
|
||||
if (!skill_json) {
|
||||
continue;
|
||||
|
||||
+10
-1
@@ -245,10 +245,19 @@ int main(int argc, char** argv) {
|
||||
char startup_dm[768];
|
||||
const char* startup_name = nostr_handler_get_startup_display_name();
|
||||
int connected_relays = nostr_handler_connected_relay_count();
|
||||
time_t startup_now = time(NULL);
|
||||
struct tm startup_tm;
|
||||
char startup_time_str[32] = {0};
|
||||
if (localtime_r(&startup_now, &startup_tm)) {
|
||||
(void)strftime(startup_time_str, sizeof(startup_time_str), "%Y-%m-%d %H:%M:%S", &startup_tm);
|
||||
} else {
|
||||
snprintf(startup_time_str, sizeof(startup_time_str), "%ld", (long)startup_now);
|
||||
}
|
||||
snprintf(startup_dm,
|
||||
sizeof(startup_dm),
|
||||
"%s has started up and is online (version %s, connected relays: %d/%d).",
|
||||
"%s has started up and is online at %s (version %s, connected relays: %d/%d).",
|
||||
(startup_name && startup_name[0] != '\0') ? startup_name : "Didactyl",
|
||||
startup_time_str,
|
||||
DIDACTYL_VERSION,
|
||||
connected_relays,
|
||||
cfg.relay_count);
|
||||
|
||||
+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 30
|
||||
#define DIDACTYL_VERSION "v0.0.30"
|
||||
#define DIDACTYL_VERSION_PATCH 40
|
||||
#define DIDACTYL_VERSION "v0.0.40"
|
||||
|
||||
// Agent metadata
|
||||
#define DIDACTYL_NAME "Didactyl"
|
||||
|
||||
+195
-172
@@ -218,10 +218,19 @@ static void log_relay_state_changes(void) {
|
||||
nostr_pool_relay_status_t now = nostr_relay_pool_get_relay_status(g_pool, relay);
|
||||
nostr_pool_relay_status_t prev = g_last_relay_statuses[i];
|
||||
if (now != prev) {
|
||||
DEBUG_INFO("[didactyl] relay state changed: %s %s -> %s",
|
||||
relay,
|
||||
relay_status_str(prev),
|
||||
relay_status_str(now));
|
||||
const char* conn_err = nostr_relay_pool_get_relay_last_connection_error(g_pool, relay);
|
||||
if (conn_err && (now == NOSTR_POOL_RELAY_DISCONNECTED || now == NOSTR_POOL_RELAY_ERROR)) {
|
||||
DEBUG_INFO("[didactyl] relay state changed: %s %s -> %s (cause: %s)",
|
||||
relay,
|
||||
relay_status_str(prev),
|
||||
relay_status_str(now),
|
||||
conn_err);
|
||||
} else {
|
||||
DEBUG_INFO("[didactyl] relay state changed: %s %s -> %s",
|
||||
relay,
|
||||
relay_status_str(prev),
|
||||
relay_status_str(now));
|
||||
}
|
||||
g_last_relay_statuses[i] = now;
|
||||
|
||||
if (now == NOSTR_POOL_RELAY_CONNECTED) {
|
||||
@@ -628,14 +637,25 @@ static void on_event(cJSON* event, const char* relay_url, void* user_data) {
|
||||
cJSON* rumor_kind = cJSON_GetObjectItemCaseSensitive(rumor, "kind");
|
||||
cJSON* rumor_pubkey = cJSON_GetObjectItemCaseSensitive(rumor, "pubkey");
|
||||
cJSON* rumor_content = cJSON_GetObjectItemCaseSensitive(rumor, "content");
|
||||
cJSON* rumor_created_at = cJSON_GetObjectItemCaseSensitive(rumor, "created_at");
|
||||
|
||||
if (!rumor_kind || !rumor_pubkey || !rumor_content ||
|
||||
if (!rumor_kind || !rumor_pubkey || !rumor_content || !rumor_created_at ||
|
||||
!cJSON_IsNumber(rumor_kind) || !cJSON_IsString(rumor_pubkey) || !cJSON_IsString(rumor_content) ||
|
||||
!cJSON_IsNumber(rumor_created_at) ||
|
||||
!rumor_pubkey->valuestring || strlen(rumor_pubkey->valuestring) != 64U) {
|
||||
cJSON_Delete(rumor);
|
||||
return;
|
||||
}
|
||||
|
||||
time_t rumor_created_at_ts = (time_t)rumor_created_at->valuedouble;
|
||||
if (rumor_created_at_ts < g_start_time) {
|
||||
DEBUG_TRACE("[didactyl] DEBUG on_event: skipping old NIP-17 rumor created_at=%ld start=%ld",
|
||||
(long)rumor_created_at_ts,
|
||||
(long)g_start_time);
|
||||
cJSON_Delete(rumor);
|
||||
return;
|
||||
}
|
||||
|
||||
int rumor_kind_val = (int)rumor_kind->valuedouble;
|
||||
if (rumor_kind_val != 14 && rumor_kind_val != 15 && rumor_kind_val != 7) {
|
||||
DEBUG_TRACE("[didactyl] DEBUG on_event: ignoring NIP-17 rumor kind=%d", rumor_kind_val);
|
||||
@@ -670,6 +690,14 @@ static void on_event(cJSON* event, const char* relay_url, void* user_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(sender_pubkey_hex, g_cfg->keys.public_key_hex) == 0) {
|
||||
DEBUG_TRACE("[didactyl] DEBUG on_event: ignoring self-sent DM %.16s... via %s",
|
||||
sender_pubkey_hex,
|
||||
relay_url ? relay_url : "unknown relay");
|
||||
free(decrypted);
|
||||
return;
|
||||
}
|
||||
|
||||
didactyl_sender_tier_t tier = sender_tier_from_pubkey(sender_pubkey_hex);
|
||||
|
||||
DEBUG_TRACE("[didactyl] DEBUG on_event: sender=%.16s... tier=%d (admin=%.16s...)",
|
||||
@@ -1077,58 +1105,122 @@ int nostr_handler_subscribe_dms(dm_callback_t callback, void* user_data) {
|
||||
g_dm_callback = callback;
|
||||
g_dm_user_data = user_data;
|
||||
|
||||
cJSON* filter = cJSON_CreateObject();
|
||||
cJSON* kinds = cJSON_CreateArray();
|
||||
cJSON* p_values = cJSON_CreateArray();
|
||||
if (!filter || !kinds || !p_values) {
|
||||
cJSON_Delete(filter);
|
||||
cJSON_Delete(kinds);
|
||||
cJSON_Delete(p_values);
|
||||
const int need_kind4 = (g_cfg->dm_protocol == DM_PROTOCOL_NIP04 || g_cfg->dm_protocol == DM_PROTOCOL_BOTH) ? 1 : 0;
|
||||
const int need_kind1059 = (g_cfg->dm_protocol == DM_PROTOCOL_NIP17 || g_cfg->dm_protocol == DM_PROTOCOL_BOTH) ? 1 : 0;
|
||||
|
||||
if (!need_kind4 && !need_kind1059) {
|
||||
DEBUG_WARN("[didactyl] DM subscription skipped: no protocol selected");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (g_cfg->dm_protocol == DM_PROTOCOL_NIP04 || g_cfg->dm_protocol == DM_PROTOCOL_BOTH) {
|
||||
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(4));
|
||||
}
|
||||
if (g_cfg->dm_protocol == DM_PROTOCOL_NIP17 || g_cfg->dm_protocol == DM_PROTOCOL_BOTH) {
|
||||
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(1059));
|
||||
}
|
||||
cJSON_AddItemToObject(filter, "kinds", kinds);
|
||||
cJSON_AddItemToArray(p_values, cJSON_CreateString(g_cfg->keys.public_key_hex));
|
||||
cJSON_AddItemToObject(filter, "#p", p_values);
|
||||
cJSON_AddNumberToObject(filter, "since", (double)g_start_time);
|
||||
cJSON_AddNumberToObject(filter, "limit", 100);
|
||||
int subscribed_any = 0;
|
||||
|
||||
{
|
||||
char* filter_str = cJSON_PrintUnformatted(filter);
|
||||
DEBUG_TRACE("[didactyl] DEBUG DM subscription filter: %s", filter_str ? filter_str : "<null>");
|
||||
DEBUG_TRACE("[didactyl] DEBUG DM subscription g_start_time=%ld now=%ld delta=%ld relay_count=%d",
|
||||
(long)g_start_time, (long)time(NULL), (long)(time(NULL) - g_start_time), g_cfg->relay_count);
|
||||
free(filter_str);
|
||||
if (need_kind4) {
|
||||
cJSON* filter4 = cJSON_CreateObject();
|
||||
cJSON* kinds4 = cJSON_CreateArray();
|
||||
cJSON* p_values4 = cJSON_CreateArray();
|
||||
if (!filter4 || !kinds4 || !p_values4) {
|
||||
cJSON_Delete(filter4);
|
||||
cJSON_Delete(kinds4);
|
||||
cJSON_Delete(p_values4);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cJSON_AddItemToArray(kinds4, cJSON_CreateNumber(4));
|
||||
cJSON_AddItemToObject(filter4, "kinds", kinds4);
|
||||
cJSON_AddItemToArray(p_values4, cJSON_CreateString(g_cfg->keys.public_key_hex));
|
||||
cJSON_AddItemToObject(filter4, "#p", p_values4);
|
||||
cJSON_AddNumberToObject(filter4, "since", (double)g_start_time);
|
||||
cJSON_AddNumberToObject(filter4, "limit", 100);
|
||||
|
||||
{
|
||||
char* filter_str = cJSON_PrintUnformatted(filter4);
|
||||
DEBUG_TRACE("[didactyl] DEBUG DM subscription filter kind4: %s", filter_str ? filter_str : "<null>");
|
||||
free(filter_str);
|
||||
}
|
||||
|
||||
nostr_pool_subscription_t* sub4 = nostr_relay_pool_subscribe(
|
||||
g_pool,
|
||||
(const char**)g_cfg->relays,
|
||||
g_cfg->relay_count,
|
||||
filter4,
|
||||
on_event,
|
||||
on_eose,
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
NOSTR_POOL_EOSE_FULL_SET,
|
||||
30,
|
||||
120);
|
||||
|
||||
cJSON_Delete(filter4);
|
||||
if (!sub4) {
|
||||
fprintf(stderr, "[didactyl] kind4 DM subscription failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
subscribed_any = 1;
|
||||
DEBUG_TRACE("[didactyl] DEBUG kind4 DM subscription sub=%p", (void*)sub4);
|
||||
}
|
||||
|
||||
nostr_pool_subscription_t* sub = nostr_relay_pool_subscribe(
|
||||
g_pool,
|
||||
(const char**)g_cfg->relays,
|
||||
g_cfg->relay_count,
|
||||
filter,
|
||||
on_event,
|
||||
on_eose,
|
||||
NULL,
|
||||
0,
|
||||
1,
|
||||
NOSTR_POOL_EOSE_FULL_SET,
|
||||
30,
|
||||
120);
|
||||
if (need_kind1059) {
|
||||
time_t since_1059 = g_start_time;
|
||||
|
||||
cJSON_Delete(filter);
|
||||
if (!sub) {
|
||||
fprintf(stderr, "[didactyl] DM subscription failed\n");
|
||||
cJSON* filter1059 = cJSON_CreateObject();
|
||||
cJSON* kinds1059 = cJSON_CreateArray();
|
||||
cJSON* p_values1059 = cJSON_CreateArray();
|
||||
if (!filter1059 || !kinds1059 || !p_values1059) {
|
||||
cJSON_Delete(filter1059);
|
||||
cJSON_Delete(kinds1059);
|
||||
cJSON_Delete(p_values1059);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cJSON_AddItemToArray(kinds1059, cJSON_CreateNumber(1059));
|
||||
cJSON_AddItemToObject(filter1059, "kinds", kinds1059);
|
||||
cJSON_AddItemToArray(p_values1059, cJSON_CreateString(g_cfg->keys.public_key_hex));
|
||||
cJSON_AddItemToObject(filter1059, "#p", p_values1059);
|
||||
cJSON_AddNumberToObject(filter1059, "since", (double)since_1059);
|
||||
cJSON_AddNumberToObject(filter1059, "limit", 400);
|
||||
|
||||
{
|
||||
char* filter_str = cJSON_PrintUnformatted(filter1059);
|
||||
DEBUG_TRACE("[didactyl] DEBUG DM subscription filter kind1059: %s", filter_str ? filter_str : "<null>");
|
||||
DEBUG_TRACE("[didactyl] DEBUG kind1059 since=%ld start=%ld", (long)since_1059, (long)g_start_time);
|
||||
free(filter_str);
|
||||
}
|
||||
|
||||
nostr_pool_subscription_t* sub1059 = nostr_relay_pool_subscribe(
|
||||
g_pool,
|
||||
(const char**)g_cfg->relays,
|
||||
g_cfg->relay_count,
|
||||
filter1059,
|
||||
on_event,
|
||||
on_eose,
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
NOSTR_POOL_EOSE_FULL_SET,
|
||||
30,
|
||||
120);
|
||||
|
||||
cJSON_Delete(filter1059);
|
||||
if (!sub1059) {
|
||||
fprintf(stderr, "[didactyl] kind1059 DM subscription failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
subscribed_any = 1;
|
||||
DEBUG_TRACE("[didactyl] DEBUG kind1059 DM subscription sub=%p", (void*)sub1059);
|
||||
}
|
||||
|
||||
if (!subscribed_any) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_INFO("[didactyl] DM subscription active for pubkey %.16s...", g_cfg->keys.public_key_hex);
|
||||
DEBUG_TRACE("[didactyl] DEBUG DM subscription sub=%p close_on_eose=0 dedup=1", (void*)sub);
|
||||
DEBUG_TRACE("[didactyl] DEBUG DM subscription g_start_time=%ld now=%ld delta=%ld relay_count=%d",
|
||||
(long)g_start_time, (long)time(NULL), (long)(time(NULL) - g_start_time), g_cfg->relay_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1186,21 +1278,6 @@ int nostr_handler_send_dm(const char* recipient_pubkey_hex, const char* message)
|
||||
}
|
||||
}
|
||||
|
||||
int* pre_publish_ok = NULL;
|
||||
if (connected_count > 0) {
|
||||
pre_publish_ok = (int*)calloc((size_t)connected_count, sizeof(int));
|
||||
if (!pre_publish_ok) {
|
||||
free(connected_relays);
|
||||
cJSON_Delete(event);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < connected_count; i++) {
|
||||
const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(g_pool, connected_relays[i]);
|
||||
pre_publish_ok[i] = stats ? stats->events_published_ok : 0;
|
||||
}
|
||||
}
|
||||
|
||||
int sent = 0;
|
||||
if (connected_count > 0) {
|
||||
sent = nostr_relay_pool_publish_async(
|
||||
@@ -1214,48 +1291,6 @@ int nostr_handler_send_dm(const char* recipient_pubkey_hex, const char* message)
|
||||
for (int i = 0; i < connected_count; i++) {
|
||||
DEBUG_INFO("[didactyl] kind 4 event published to %s (async)", connected_relays[i]);
|
||||
}
|
||||
|
||||
// Briefly drain relay messages so NIP-42 AUTH handshake can complete.
|
||||
for (int i = 0; i < 5; i++) {
|
||||
(void)nostr_relay_pool_poll(g_pool, 100);
|
||||
}
|
||||
|
||||
int any_publish_ok = 0;
|
||||
int auth_required_seen = 0;
|
||||
for (int i = 0; i < connected_count; i++) {
|
||||
const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(g_pool, connected_relays[i]);
|
||||
if (stats && stats->events_published_ok > pre_publish_ok[i]) {
|
||||
any_publish_ok = 1;
|
||||
}
|
||||
|
||||
const char* pub_err = nostr_relay_pool_get_relay_last_publish_error(g_pool, connected_relays[i]);
|
||||
if (pub_err && strstr(pub_err, "auth-required") != NULL) {
|
||||
auth_required_seen = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Always retry this same signed event once after handshake drain.
|
||||
// Some relays issue AUTH challenge for the first EVENT and only accept
|
||||
// the subsequent resend after AUTH succeeds.
|
||||
if (sent > 0) {
|
||||
if (auth_required_seen || !any_publish_ok) {
|
||||
DEBUG_WARN("[didactyl] retrying kind 4 event once after auth handshake window");
|
||||
}
|
||||
int resent = nostr_relay_pool_publish_async(
|
||||
g_pool,
|
||||
connected_relays,
|
||||
connected_count,
|
||||
event,
|
||||
NULL,
|
||||
NULL);
|
||||
if (resent > sent) {
|
||||
sent = resent;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
(void)nostr_relay_pool_poll(g_pool, 100);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_WARN("[didactyl] kind 4 event not queued: no connected relays");
|
||||
}
|
||||
@@ -1268,7 +1303,6 @@ int nostr_handler_send_dm(const char* recipient_pubkey_hex, const char* message)
|
||||
recipient_pubkey_hex,
|
||||
sent);
|
||||
|
||||
free(pre_publish_ok);
|
||||
free(connected_relays);
|
||||
cJSON_Delete(event);
|
||||
return sent > 0 ? 0 : -1;
|
||||
@@ -1793,79 +1827,22 @@ int nostr_handler_send_dm_nip17(const char* recipient_pubkey_hex, const char* me
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(10050));
|
||||
cJSON_AddItemToObject(filter, "kinds", kinds);
|
||||
cJSON_AddItemToArray(authors, cJSON_CreateString(recipient_pubkey_hex));
|
||||
cJSON_AddItemToObject(filter, "authors", authors);
|
||||
cJSON_AddNumberToObject(filter, "limit", 1);
|
||||
|
||||
int event_count = 0;
|
||||
cJSON** events = nostr_relay_pool_query_sync(
|
||||
g_pool,
|
||||
(const char**)g_cfg->relays,
|
||||
g_cfg->relay_count,
|
||||
filter,
|
||||
&event_count,
|
||||
5000);
|
||||
|
||||
cJSON_Delete(filter);
|
||||
|
||||
char* extracted_dm_relays[NIP17_MAX_RELAYS] = {0};
|
||||
int extracted_count = 0;
|
||||
if (events && event_count > 0 && events[0]) {
|
||||
int extracted = nostr_nip17_extract_dm_relays(events[0], extracted_dm_relays, NIP17_MAX_RELAYS);
|
||||
if (extracted > 0) {
|
||||
extracted_count = extracted;
|
||||
}
|
||||
}
|
||||
|
||||
if (events) {
|
||||
for (int i = 0; i < event_count; i++) {
|
||||
cJSON_Delete(events[i]);
|
||||
}
|
||||
free(events);
|
||||
}
|
||||
|
||||
const char* target_relays[NIP17_MAX_RELAYS] = {0};
|
||||
int target_count = 0;
|
||||
|
||||
if (extracted_count > 0) {
|
||||
for (int i = 0; i < g_cfg->relay_count && target_count < NIP17_MAX_RELAYS; i++) {
|
||||
if (nostr_relay_pool_get_relay_status(g_pool, g_cfg->relays[i]) != NOSTR_POOL_RELAY_CONNECTED) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < extracted_count; j++) {
|
||||
if (extracted_dm_relays[j] && strcmp(g_cfg->relays[i], extracted_dm_relays[j]) == 0) {
|
||||
target_relays[target_count++] = g_cfg->relays[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < g_cfg->relay_count && target_count < NIP17_MAX_RELAYS; i++) {
|
||||
if (nostr_relay_pool_get_relay_status(g_pool, g_cfg->relays[i]) == NOSTR_POOL_RELAY_CONNECTED) {
|
||||
target_relays[target_count++] = g_cfg->relays[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (target_count == 0) {
|
||||
for (int i = 0; i < g_cfg->relay_count && target_count < NIP17_MAX_RELAYS; i++) {
|
||||
if (nostr_relay_pool_get_relay_status(g_pool, g_cfg->relays[i]) == NOSTR_POOL_RELAY_CONNECTED) {
|
||||
target_relays[target_count++] = g_cfg->relays[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < extracted_count; i++) {
|
||||
free(extracted_dm_relays[i]);
|
||||
}
|
||||
DEBUG_TRACE("[didactyl] NIP-17 send prep: recipient=%.16s... connected_targets=%d",
|
||||
recipient_pubkey_hex,
|
||||
target_count);
|
||||
|
||||
if (target_count <= 0) {
|
||||
DEBUG_WARN("[didactyl] NIP-17 send aborted: no connected relays for recipient %.16s...",
|
||||
recipient_pubkey_hex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1892,14 +1869,19 @@ int nostr_handler_send_dm_nip17(const char* recipient_pubkey_hex, const char* me
|
||||
0);
|
||||
cJSON_Delete(chat_event);
|
||||
if (gift_count <= 0) {
|
||||
DEBUG_WARN("[didactyl] NIP-17 send aborted: gift_wrap creation failed for %.16s...",
|
||||
recipient_pubkey_hex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int any_sent = 0;
|
||||
int sent_count = 0;
|
||||
|
||||
for (int i = 0; i < gift_count; i++) {
|
||||
if (!gift_wraps[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int sent = nostr_relay_pool_publish_async(g_pool,
|
||||
target_relays,
|
||||
target_count,
|
||||
@@ -1908,15 +1890,27 @@ int nostr_handler_send_dm_nip17(const char* recipient_pubkey_hex, const char* me
|
||||
NULL);
|
||||
if (sent > 0) {
|
||||
any_sent = 1;
|
||||
sent_count += sent;
|
||||
}
|
||||
|
||||
for (int r = 0; r < target_count; r++) {
|
||||
DEBUG_INFO("[didactyl] kind 1059 event published to %s (async)", target_relays[r]);
|
||||
}
|
||||
|
||||
cJSON_Delete(gift_wraps[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
(void)nostr_relay_pool_poll(g_pool, 100);
|
||||
DEBUG_TRACE("[didactyl] NIP-17 send complete: recipient=%.16s... sent_count=%d",
|
||||
recipient_pubkey_hex,
|
||||
sent_count);
|
||||
|
||||
if (!any_sent) {
|
||||
DEBUG_WARN("[didactyl] NIP-17 send failed: no relay accepted publish for recipient %.16s...",
|
||||
recipient_pubkey_hex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return any_sent ? 0 : -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* nostr_handler_get_admin_kind0_context(void) {
|
||||
@@ -1998,9 +1992,38 @@ int nostr_handler_poll(int timeout_ms) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
double start_ms = -1.0;
|
||||
struct timespec ts_start;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts_start) == 0) {
|
||||
start_ms = ts_start.tv_sec * 1000.0 + ts_start.tv_nsec / 1000000.0;
|
||||
}
|
||||
|
||||
int rc = nostr_relay_pool_poll(g_pool, timeout_ms);
|
||||
g_poll_counter++;
|
||||
|
||||
if (start_ms >= 0.0) {
|
||||
struct timespec ts_end;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts_end) == 0) {
|
||||
const double end_ms = ts_end.tv_sec * 1000.0 + ts_end.tv_nsec / 1000000.0;
|
||||
const double elapsed_ms = end_ms - start_ms;
|
||||
const double expected_ms = timeout_ms > 0 ? (double)timeout_ms : 0.0;
|
||||
|
||||
if (elapsed_ms > expected_ms + 250.0) {
|
||||
DEBUG_WARN("[didactyl] poll latency spike: nostr_relay_pool_poll(timeout=%d) took %.1fms rc=%d count=%llu",
|
||||
timeout_ms,
|
||||
elapsed_ms,
|
||||
rc,
|
||||
(unsigned long long)g_poll_counter);
|
||||
} else if ((g_poll_counter % 200ULL) == 0ULL) {
|
||||
DEBUG_TRACE("[didactyl] poll heartbeat: timeout=%d elapsed=%.1fms rc=%d count=%llu",
|
||||
timeout_ms,
|
||||
elapsed_ms,
|
||||
rc,
|
||||
(unsigned long long)g_poll_counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_relay_state_changes();
|
||||
|
||||
return rc;
|
||||
|
||||
@@ -268,7 +268,7 @@ int trigger_manager_load_from_skills(trigger_manager_t* mgr) {
|
||||
cJSON_AddItemToObject(adopt_filter, "authors", authors);
|
||||
cJSON_AddNumberToObject(adopt_filter, "limit", 1);
|
||||
|
||||
char* adoption_json = nostr_handler_query_json(adopt_filter, 8000);
|
||||
char* adoption_json = nostr_handler_query_json(adopt_filter, 2000);
|
||||
cJSON_Delete(adopt_filter);
|
||||
if (!adoption_json) {
|
||||
return 0;
|
||||
@@ -328,7 +328,7 @@ int trigger_manager_load_from_skills(trigger_manager_t* mgr) {
|
||||
cJSON_AddItemToObject(skill_filter, "#d", d_values);
|
||||
cJSON_AddNumberToObject(skill_filter, "limit", 1);
|
||||
|
||||
char* skill_json = nostr_handler_query_json(skill_filter, 8000);
|
||||
char* skill_json = nostr_handler_query_json(skill_filter, 2000);
|
||||
cJSON_Delete(skill_filter);
|
||||
if (!skill_json) {
|
||||
continue;
|
||||
@@ -513,7 +513,7 @@ int trigger_manager_poll(trigger_manager_t* mgr) {
|
||||
}
|
||||
|
||||
time_t now = time(NULL);
|
||||
if (mgr->last_poll_at > 0 && (now - mgr->last_poll_at) < 2) {
|
||||
if (mgr->last_poll_at > 0 && (now - mgr->last_poll_at) < 10) {
|
||||
return 0;
|
||||
}
|
||||
mgr->last_poll_at = now;
|
||||
@@ -538,7 +538,7 @@ int trigger_manager_poll(trigger_manager_t* mgr) {
|
||||
cJSON_AddNumberToObject(filter, "since", (double)since);
|
||||
cJSON_AddNumberToObject(filter, "limit", 8);
|
||||
|
||||
char* events_json = nostr_handler_query_json(filter, 4000);
|
||||
char* events_json = nostr_handler_query_json(filter, 1200);
|
||||
cJSON_Delete(filter);
|
||||
if (!events_json) {
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user