Getting the relay pool up to speed
This commit is contained in:
parent
0d910ca181
commit
0f897ab1b3
26
README.md
26
README.md
|
@ -302,20 +302,32 @@ publish_result_t* synchronous_publish_event_with_progress(const char** relay_url
|
|||
|
||||
### Relay Pools (Asynchronous)
|
||||
```c
|
||||
// Create and manage relay pool
|
||||
nostr_relay_pool_t* nostr_relay_pool_create(void);
|
||||
// Create and manage relay pool with reconnection
|
||||
nostr_pool_reconnect_config_t* config = nostr_pool_reconnect_config_default();
|
||||
nostr_relay_pool_t* nostr_relay_pool_create(nostr_pool_reconnect_config_t* config);
|
||||
int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url);
|
||||
void nostr_relay_pool_destroy(nostr_relay_pool_t* pool);
|
||||
|
||||
// Subscribe to events
|
||||
// Subscribe to events (with auto-reconnection)
|
||||
nostr_pool_subscription_t* nostr_relay_pool_subscribe(
|
||||
nostr_relay_pool_t* pool, const char** relay_urls, int relay_count, cJSON* filter,
|
||||
void (*on_event)(cJSON* event, const char* relay_url, void* user_data),
|
||||
void (*on_eose)(void* user_data), void* user_data);
|
||||
void (*on_eose)(void* user_data), void* user_data, int close_on_eose);
|
||||
|
||||
// Run event loop
|
||||
// Run event loop (handles reconnection automatically)
|
||||
int nostr_relay_pool_run(nostr_relay_pool_t* pool, int timeout_ms);
|
||||
int nostr_relay_pool_poll(nostr_relay_pool_t* pool, int timeout_ms);
|
||||
|
||||
// Reconnection configuration
|
||||
typedef struct {
|
||||
int enable_auto_reconnect; // Enable automatic reconnection
|
||||
int max_reconnect_attempts; // Maximum retry attempts
|
||||
int initial_reconnect_delay_ms; // Initial delay between attempts
|
||||
int max_reconnect_delay_ms; // Maximum delay cap
|
||||
int reconnect_backoff_multiplier; // Exponential backoff factor
|
||||
int ping_interval_seconds; // Health check ping interval
|
||||
int pong_timeout_seconds; // Pong response timeout
|
||||
} nostr_pool_reconnect_config_t;
|
||||
```
|
||||
|
||||
### NIP-05 Identifier Verification
|
||||
|
@ -370,6 +382,9 @@ The library includes extensive tests:
|
|||
|
||||
# Individual test categories
|
||||
cd tests && make test
|
||||
|
||||
# Interactive relay pool testing
|
||||
cd tests && ./pool_test
|
||||
```
|
||||
|
||||
**Test Categories:**
|
||||
|
@ -383,6 +398,7 @@ cd tests && make test
|
|||
- **Relay Communication**: `relay_pool_test`, `sync_test`
|
||||
- **HTTP/WebSocket**: `http_test`, `wss_test`
|
||||
- **Proof of Work**: `test_pow_loop`
|
||||
- **Interactive Pool Testing**: `pool_test` (menu-driven interface with reconnection testing)
|
||||
|
||||
## 🏗️ Integration
|
||||
|
||||
|
|
1
build.sh
1
build.sh
|
@ -491,6 +491,7 @@ SOURCES="$SOURCES cjson/cJSON.c"
|
|||
SOURCES="$SOURCES nostr_core/utils.c"
|
||||
SOURCES="$SOURCES nostr_core/nostr_common.c"
|
||||
SOURCES="$SOURCES nostr_core/core_relays.c"
|
||||
SOURCES="$SOURCES nostr_core/core_relay_pool.c"
|
||||
SOURCES="$SOURCES nostr_websocket/nostr_websocket_openssl.c"
|
||||
SOURCES="$SOURCES nostr_core/request_validator.c"
|
||||
|
||||
|
|
|
@ -65,22 +65,26 @@ typedef struct relay_connection {
|
|||
char* url;
|
||||
nostr_ws_client_t* ws_client;
|
||||
nostr_pool_relay_status_t status;
|
||||
|
||||
|
||||
// Connection management
|
||||
time_t last_ping;
|
||||
time_t connect_time;
|
||||
nostr_relay_pool_t* pool; // Back reference to pool for config access
|
||||
|
||||
// Reconnection management
|
||||
int reconnect_attempts;
|
||||
|
||||
// Ping management for latency measurement
|
||||
time_t last_reconnect_attempt;
|
||||
time_t next_reconnect_time;
|
||||
|
||||
// Connection health monitoring (ping/pong)
|
||||
time_t last_ping_sent;
|
||||
time_t next_ping_time; // last_ping_sent + NOSTR_POOL_PING_INTERVAL
|
||||
time_t last_pong_received;
|
||||
int ping_pending;
|
||||
double pending_ping_start_ms; // High-resolution timestamp for ping measurement
|
||||
int ping_pending; // Flag to track if ping response is expected
|
||||
|
||||
|
||||
// Multi-subscription latency tracking (REQ->first EVENT/EOSE)
|
||||
subscription_timing_t pending_subscriptions[NOSTR_POOL_MAX_PENDING_SUBSCRIPTIONS];
|
||||
int pending_subscription_count;
|
||||
|
||||
|
||||
// Statistics
|
||||
nostr_relay_stats_t stats;
|
||||
} relay_connection_t;
|
||||
|
@ -88,34 +92,38 @@ typedef struct relay_connection {
|
|||
struct nostr_pool_subscription {
|
||||
char subscription_id[NOSTR_POOL_SUBSCRIPTION_ID_SIZE];
|
||||
cJSON* filter;
|
||||
|
||||
|
||||
// Relay-specific subscription tracking
|
||||
char** relay_urls;
|
||||
int relay_count;
|
||||
int* eose_received; // Track EOSE from each relay
|
||||
|
||||
|
||||
// Callbacks
|
||||
void (*on_event)(cJSON* event, const char* relay_url, void* user_data);
|
||||
void (*on_eose)(void* user_data);
|
||||
void* user_data;
|
||||
|
||||
|
||||
int closed;
|
||||
int close_on_eose; // Auto-close subscription when all relays send EOSE
|
||||
nostr_relay_pool_t* pool; // Back reference to pool
|
||||
};
|
||||
|
||||
struct nostr_relay_pool {
|
||||
relay_connection_t* relays[NOSTR_POOL_MAX_RELAYS];
|
||||
int relay_count;
|
||||
|
||||
|
||||
// Reconnection configuration
|
||||
nostr_pool_reconnect_config_t reconnect_config;
|
||||
|
||||
// Event deduplication - simple hash table with linear probing
|
||||
char seen_event_ids[NOSTR_POOL_MAX_SEEN_EVENTS][65]; // 64 hex chars + null terminator
|
||||
int seen_count;
|
||||
int seen_next_index;
|
||||
|
||||
|
||||
// Active subscriptions
|
||||
nostr_pool_subscription_t* subscriptions[NOSTR_POOL_MAX_SUBSCRIPTIONS];
|
||||
int subscription_count;
|
||||
|
||||
|
||||
// Pool-wide settings
|
||||
int default_timeout_ms;
|
||||
};
|
||||
|
@ -163,33 +171,211 @@ static int add_subscription_timing(relay_connection_t* relay, const char* subscr
|
|||
// Helper function to find and remove subscription timing
|
||||
static double remove_subscription_timing(relay_connection_t* relay, const char* subscription_id) {
|
||||
if (!relay || !subscription_id) return -1.0;
|
||||
|
||||
|
||||
for (int i = 0; i < relay->pending_subscription_count; i++) {
|
||||
if (relay->pending_subscriptions[i].active &&
|
||||
if (relay->pending_subscriptions[i].active &&
|
||||
strcmp(relay->pending_subscriptions[i].subscription_id, subscription_id) == 0) {
|
||||
|
||||
|
||||
// Calculate latency
|
||||
double current_time_ms = get_current_time_ms();
|
||||
double latency_ms = current_time_ms - relay->pending_subscriptions[i].start_time_ms;
|
||||
|
||||
|
||||
// Mark as inactive and remove by shifting remaining entries
|
||||
relay->pending_subscriptions[i].active = 0;
|
||||
for (int j = i; j < relay->pending_subscription_count - 1; j++) {
|
||||
relay->pending_subscriptions[j] = relay->pending_subscriptions[j + 1];
|
||||
}
|
||||
relay->pending_subscription_count--;
|
||||
|
||||
|
||||
return latency_ms;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return -1.0; // Not found
|
||||
}
|
||||
|
||||
// Helper function to check if event ID has been seen
|
||||
// Helper function to ensure relay connection
|
||||
static int ensure_relay_connection(relay_connection_t* relay) {
|
||||
if (!relay) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (relay->ws_client && nostr_ws_get_state(relay->ws_client) == NOSTR_WS_CONNECTED) {
|
||||
relay->status = NOSTR_POOL_RELAY_CONNECTED;
|
||||
return 0; // Already connected
|
||||
}
|
||||
|
||||
// Close existing connection if any
|
||||
if (relay->ws_client) {
|
||||
nostr_ws_close(relay->ws_client);
|
||||
relay->ws_client = NULL;
|
||||
}
|
||||
|
||||
// Attempt connection
|
||||
relay->status = NOSTR_POOL_RELAY_CONNECTING;
|
||||
relay->stats.connection_attempts++;
|
||||
|
||||
relay->ws_client = nostr_ws_connect(relay->url);
|
||||
|
||||
if (!relay->ws_client) {
|
||||
relay->status = NOSTR_POOL_RELAY_ERROR;
|
||||
relay->reconnect_attempts++;
|
||||
relay->stats.connection_failures++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
nostr_ws_state_t state = nostr_ws_get_state(relay->ws_client);
|
||||
|
||||
if (state == NOSTR_WS_CONNECTED) {
|
||||
relay->status = NOSTR_POOL_RELAY_CONNECTED;
|
||||
relay->connect_time = time(NULL);
|
||||
relay->reconnect_attempts = 0;
|
||||
|
||||
// Initialize ping/pong monitoring on new connection
|
||||
relay->last_ping_sent = time(NULL);
|
||||
relay->last_pong_received = time(NULL);
|
||||
relay->ping_pending = 0;
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
relay->status = NOSTR_POOL_RELAY_ERROR;
|
||||
relay->reconnect_attempts++;
|
||||
relay->stats.connection_failures++;
|
||||
|
||||
// Close the failed connection
|
||||
nostr_ws_close(relay->ws_client);
|
||||
relay->ws_client = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Reconnection helper functions
|
||||
static int should_attempt_reconnect(relay_connection_t* relay) {
|
||||
if (!relay->pool->reconnect_config.enable_auto_reconnect) return 0;
|
||||
if (relay->status == NOSTR_POOL_RELAY_CONNECTED) return 0;
|
||||
if (relay->reconnect_attempts >= relay->pool->reconnect_config.max_reconnect_attempts) return 0;
|
||||
|
||||
time_t now = time(NULL);
|
||||
return (now >= relay->next_reconnect_time);
|
||||
}
|
||||
|
||||
static int calculate_reconnect_delay(relay_connection_t* relay) {
|
||||
int delay = relay->pool->reconnect_config.initial_reconnect_delay_ms;
|
||||
|
||||
// Apply exponential backoff
|
||||
for (int i = 1; i < relay->reconnect_attempts; i++) {
|
||||
delay *= relay->pool->reconnect_config.reconnect_backoff_multiplier;
|
||||
if (delay > relay->pool->reconnect_config.max_reconnect_delay_ms) {
|
||||
delay = relay->pool->reconnect_config.max_reconnect_delay_ms;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return delay;
|
||||
}
|
||||
|
||||
static void restore_subscriptions_on_reconnect(relay_connection_t* relay) {
|
||||
// Find subscriptions that should be active on this relay
|
||||
for (int i = 0; i < relay->pool->subscription_count; i++) {
|
||||
nostr_pool_subscription_t* sub = relay->pool->subscriptions[i];
|
||||
if (!sub->closed) {
|
||||
// Check if this subscription should be on this relay
|
||||
for (int j = 0; j < sub->relay_count; j++) {
|
||||
if (strcmp(sub->relay_urls[j], relay->url) == 0) {
|
||||
// Re-send the subscription
|
||||
if (nostr_relay_send_req(relay->ws_client, sub->subscription_id, sub->filter) >= 0) {
|
||||
// Add timing for latency measurement
|
||||
add_subscription_timing(relay, sub->subscription_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void attempt_reconnect(relay_connection_t* relay) {
|
||||
relay->status = NOSTR_POOL_RELAY_CONNECTING;
|
||||
relay->last_reconnect_attempt = time(NULL);
|
||||
relay->reconnect_attempts++;
|
||||
|
||||
if (ensure_relay_connection(relay) == 0) {
|
||||
// Success! Reset reconnection state
|
||||
relay->reconnect_attempts = 0;
|
||||
relay->next_reconnect_time = 0;
|
||||
|
||||
// Restore subscriptions on reconnect
|
||||
restore_subscriptions_on_reconnect(relay);
|
||||
} else {
|
||||
// Failed - schedule next attempt with backoff
|
||||
int delay_ms = calculate_reconnect_delay(relay);
|
||||
relay->next_reconnect_time = time(NULL) + (delay_ms / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Connection health monitoring (ping/pong)
|
||||
static void check_connection_health(relay_connection_t* relay) {
|
||||
time_t now = time(NULL);
|
||||
|
||||
// Send ping if interval elapsed and ping is enabled
|
||||
if (relay->pool->reconnect_config.ping_interval_seconds > 0 &&
|
||||
now - relay->last_ping_sent >= relay->pool->reconnect_config.ping_interval_seconds &&
|
||||
!relay->ping_pending) {
|
||||
|
||||
if (nostr_ws_ping(relay->ws_client) == 0) {
|
||||
relay->last_ping_sent = now;
|
||||
relay->ping_pending = 1;
|
||||
// Store high-resolution start time for latency measurement
|
||||
relay->pending_ping_start_ms = get_current_time_ms();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for pong timeout
|
||||
if (relay->ping_pending &&
|
||||
now - relay->last_ping_sent > relay->pool->reconnect_config.pong_timeout_seconds) {
|
||||
|
||||
// No pong received - connection is dead
|
||||
relay->status = NOSTR_POOL_RELAY_DISCONNECTED;
|
||||
relay->ping_pending = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pong_response(relay_connection_t* relay) {
|
||||
relay->last_pong_received = time(NULL);
|
||||
|
||||
if (relay->ping_pending) {
|
||||
// Calculate ping latency
|
||||
double current_time_ms = get_current_time_ms();
|
||||
double ping_latency = current_time_ms - relay->pending_ping_start_ms;
|
||||
|
||||
// Update ping statistics
|
||||
if (relay->stats.ping_samples == 0) {
|
||||
relay->stats.ping_latency_avg = ping_latency;
|
||||
relay->stats.ping_latency_min = ping_latency;
|
||||
relay->stats.ping_latency_max = ping_latency;
|
||||
} else {
|
||||
relay->stats.ping_latency_avg =
|
||||
(relay->stats.ping_latency_avg * relay->stats.ping_samples + ping_latency) /
|
||||
(relay->stats.ping_samples + 1);
|
||||
|
||||
if (ping_latency < relay->stats.ping_latency_min) {
|
||||
relay->stats.ping_latency_min = ping_latency;
|
||||
}
|
||||
if (ping_latency > relay->stats.ping_latency_max) {
|
||||
relay->stats.ping_latency_max = ping_latency;
|
||||
}
|
||||
}
|
||||
|
||||
relay->stats.ping_latency_current = ping_latency;
|
||||
relay->stats.ping_samples++;
|
||||
relay->ping_pending = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int is_event_seen(nostr_relay_pool_t* pool, const char* event_id) {
|
||||
if (!pool || !event_id) return 0;
|
||||
|
||||
|
||||
for (int i = 0; i < pool->seen_count; i++) {
|
||||
if (strcmp(pool->seen_event_ids[i], event_id) == 0) {
|
||||
return 1;
|
||||
|
@ -201,28 +387,49 @@ static int is_event_seen(nostr_relay_pool_t* pool, const char* event_id) {
|
|||
// Helper function to mark event as seen
|
||||
static void mark_event_seen(nostr_relay_pool_t* pool, const char* event_id) {
|
||||
if (!pool || !event_id) return;
|
||||
|
||||
|
||||
// Don't add duplicates
|
||||
if (is_event_seen(pool, event_id)) return;
|
||||
|
||||
|
||||
// Use circular buffer for seen events
|
||||
strncpy(pool->seen_event_ids[pool->seen_next_index], event_id, 64);
|
||||
pool->seen_event_ids[pool->seen_next_index][64] = '\0';
|
||||
|
||||
|
||||
pool->seen_next_index = (pool->seen_next_index + 1) % NOSTR_POOL_MAX_SEEN_EVENTS;
|
||||
if (pool->seen_count < NOSTR_POOL_MAX_SEEN_EVENTS) {
|
||||
pool->seen_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Default configuration helper
|
||||
nostr_pool_reconnect_config_t* nostr_pool_reconnect_config_default(void) {
|
||||
static nostr_pool_reconnect_config_t config = {
|
||||
.enable_auto_reconnect = 1,
|
||||
.max_reconnect_attempts = 10,
|
||||
.initial_reconnect_delay_ms = 1000,
|
||||
.max_reconnect_delay_ms = 30000,
|
||||
.reconnect_backoff_multiplier = 2,
|
||||
.ping_interval_seconds = 30,
|
||||
.pong_timeout_seconds = 10
|
||||
};
|
||||
return &config;
|
||||
}
|
||||
|
||||
// Pool management functions
|
||||
nostr_relay_pool_t* nostr_relay_pool_create(void) {
|
||||
nostr_relay_pool_t* nostr_relay_pool_create(nostr_pool_reconnect_config_t* config) {
|
||||
if (!config) {
|
||||
config = nostr_pool_reconnect_config_default();
|
||||
}
|
||||
|
||||
nostr_relay_pool_t* pool = calloc(1, sizeof(nostr_relay_pool_t));
|
||||
if (!pool) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Copy configuration
|
||||
pool->reconnect_config = *config;
|
||||
pool->default_timeout_ms = NOSTR_POOL_DEFAULT_TIMEOUT;
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
|
@ -250,15 +457,19 @@ int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url)
|
|||
|
||||
relay->status = NOSTR_POOL_RELAY_DISCONNECTED;
|
||||
relay->ws_client = NULL;
|
||||
relay->last_ping = 0;
|
||||
relay->connect_time = 0;
|
||||
relay->pool = pool; // Set back reference
|
||||
|
||||
// Initialize reconnection state
|
||||
relay->reconnect_attempts = 0;
|
||||
|
||||
// Initialize ping management
|
||||
relay->last_reconnect_attempt = 0;
|
||||
relay->next_reconnect_time = 0;
|
||||
|
||||
// Initialize ping/pong monitoring
|
||||
relay->last_ping_sent = 0;
|
||||
relay->next_ping_time = 0;
|
||||
relay->pending_ping_start_ms = 0.0;
|
||||
relay->last_pong_received = 0;
|
||||
relay->ping_pending = 0;
|
||||
relay->pending_ping_start_ms = 0.0;
|
||||
|
||||
// Initialize statistics
|
||||
memset(&relay->stats, 0, sizeof(relay->stats));
|
||||
|
@ -325,85 +536,17 @@ void nostr_relay_pool_destroy(nostr_relay_pool_t* pool) {
|
|||
free(pool);
|
||||
}
|
||||
|
||||
// Helper function to ensure relay connection
|
||||
static int ensure_relay_connection(relay_connection_t* relay) {
|
||||
if (!relay) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (relay->ws_client && nostr_ws_get_state(relay->ws_client) == NOSTR_WS_CONNECTED) {
|
||||
relay->status = NOSTR_POOL_RELAY_CONNECTED;
|
||||
return 0; // Already connected
|
||||
}
|
||||
|
||||
// Close existing connection if any
|
||||
if (relay->ws_client) {
|
||||
nostr_ws_close(relay->ws_client);
|
||||
relay->ws_client = NULL;
|
||||
}
|
||||
|
||||
// Attempt connection
|
||||
|
||||
relay->status = NOSTR_POOL_RELAY_CONNECTING;
|
||||
relay->stats.connection_attempts++;
|
||||
|
||||
relay->ws_client = nostr_ws_connect(relay->url);
|
||||
|
||||
if (!relay->ws_client) {
|
||||
relay->status = NOSTR_POOL_RELAY_ERROR;
|
||||
relay->reconnect_attempts++;
|
||||
relay->stats.connection_failures++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
nostr_ws_state_t state = nostr_ws_get_state(relay->ws_client);
|
||||
|
||||
|
||||
if (state == NOSTR_WS_CONNECTED) {
|
||||
|
||||
relay->status = NOSTR_POOL_RELAY_CONNECTED;
|
||||
relay->connect_time = time(NULL);
|
||||
relay->reconnect_attempts = 0;
|
||||
|
||||
// PING FUNCTIONALITY DISABLED - Initial ping on connection establishment
|
||||
/* COMMENTED OUT - PING FUNCTIONALITY DISABLED
|
||||
// Trigger immediate ping on new connection
|
||||
time_t current_time = time(NULL);
|
||||
relay->pending_ping_start_ms = get_current_time_ms();
|
||||
relay->ping_pending = 1;
|
||||
relay->last_ping_sent = current_time;
|
||||
relay->next_ping_time = current_time + NOSTR_POOL_PING_INTERVAL;
|
||||
|
||||
if (nostr_ws_send_ping(relay->ws_client, "ping", 4) < 0) {
|
||||
relay->ping_pending = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
|
||||
relay->status = NOSTR_POOL_RELAY_ERROR;
|
||||
relay->reconnect_attempts++;
|
||||
relay->stats.connection_failures++;
|
||||
|
||||
// Close the failed connection
|
||||
nostr_ws_close(relay->ws_client);
|
||||
relay->ws_client = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Subscription management
|
||||
nostr_pool_subscription_t* nostr_relay_pool_subscribe(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char** relay_urls,
|
||||
const char** relay_urls,
|
||||
int relay_count,
|
||||
cJSON* filter,
|
||||
void (*on_event)(cJSON* event, const char* relay_url, void* user_data),
|
||||
void (*on_eose)(void* user_data),
|
||||
void* user_data) {
|
||||
void* user_data,
|
||||
int close_on_eose) {
|
||||
|
||||
if (!pool || !relay_urls || relay_count <= 0 || !filter ||
|
||||
pool->subscription_count >= NOSTR_POOL_MAX_SUBSCRIPTIONS) {
|
||||
|
@ -458,6 +601,7 @@ nostr_pool_subscription_t* nostr_relay_pool_subscribe(
|
|||
sub->on_eose = on_eose;
|
||||
sub->user_data = user_data;
|
||||
sub->closed = 0;
|
||||
sub->close_on_eose = close_on_eose;
|
||||
sub->pool = pool;
|
||||
|
||||
// Add to pool
|
||||
|
@ -630,8 +774,15 @@ static void process_relay_message(nostr_relay_pool_t* pool, relay_connection_t*
|
|||
}
|
||||
}
|
||||
|
||||
if (all_eose && sub->on_eose) {
|
||||
sub->on_eose(sub->user_data);
|
||||
if (all_eose) {
|
||||
if (sub->on_eose) {
|
||||
sub->on_eose(sub->user_data);
|
||||
}
|
||||
|
||||
// Auto-close subscription if close_on_eose is enabled
|
||||
if (sub->close_on_eose && !sub->closed) {
|
||||
nostr_pool_subscription_close(sub);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -652,39 +803,8 @@ static void process_relay_message(nostr_relay_pool_t* pool, relay_connection_t*
|
|||
}
|
||||
}
|
||||
} else if (strcmp(msg_type, "PONG") == 0) {
|
||||
// PING FUNCTIONALITY DISABLED - Handle PONG response
|
||||
/* COMMENTED OUT - PING FUNCTIONALITY DISABLED
|
||||
if (relay->ping_pending) {
|
||||
double current_time_ms = get_current_time_ms();
|
||||
double ping_latency = current_time_ms - relay->pending_ping_start_ms;
|
||||
|
||||
// Update ping statistics
|
||||
if (relay->stats.ping_samples == 0) {
|
||||
relay->stats.ping_latency_avg = ping_latency;
|
||||
relay->stats.ping_latency_min = ping_latency;
|
||||
relay->stats.ping_latency_max = ping_latency;
|
||||
} else {
|
||||
relay->stats.ping_latency_avg =
|
||||
(relay->stats.ping_latency_avg * relay->stats.ping_samples + ping_latency) /
|
||||
(relay->stats.ping_samples + 1);
|
||||
|
||||
if (ping_latency < relay->stats.ping_latency_min) {
|
||||
relay->stats.ping_latency_min = ping_latency;
|
||||
}
|
||||
if (ping_latency > relay->stats.ping_latency_max) {
|
||||
relay->stats.ping_latency_max = ping_latency;
|
||||
}
|
||||
}
|
||||
|
||||
relay->stats.ping_latency_current = ping_latency;
|
||||
relay->stats.ping_samples++;
|
||||
relay->ping_pending = 0;
|
||||
|
||||
#ifdef NOSTR_DEBUG_ENABLED
|
||||
printf("🏓 DEBUG: PONG from %s - latency: %.2f ms\n", relay->url, ping_latency);
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
// Handle PONG response for connection health monitoring
|
||||
handle_pong_response(relay);
|
||||
}
|
||||
|
||||
if (msg_type) free(msg_type);
|
||||
|
@ -757,11 +877,17 @@ cJSON** nostr_relay_pool_query_sync(
|
|||
int len = nostr_ws_receive(relay->ws_client, buffer, sizeof(buffer) - 1, 100);
|
||||
if (len > 0) {
|
||||
buffer[len] = '\0';
|
||||
|
||||
char* msg_type = NULL;
|
||||
cJSON* parsed = NULL;
|
||||
if (nostr_parse_relay_message(buffer, &msg_type, &parsed) == 0) {
|
||||
if (msg_type && strcmp(msg_type, "EVENT") == 0) {
|
||||
|
||||
// Check if this is a pong message (WebSocket library prefixes pong messages)
|
||||
if (strncmp(buffer, "__PONG__", 8) == 0) {
|
||||
// Handle pong response for connection health monitoring
|
||||
handle_pong_response(relay);
|
||||
} else {
|
||||
// Process as regular NOSTR message
|
||||
char* msg_type = NULL;
|
||||
cJSON* parsed = NULL;
|
||||
if (nostr_parse_relay_message(buffer, &msg_type, &parsed) == 0) {
|
||||
if (msg_type && strcmp(msg_type, "EVENT") == 0) {
|
||||
// Handle EVENT message
|
||||
if (cJSON_IsArray(parsed) && cJSON_GetArraySize(parsed) >= 3) {
|
||||
cJSON* sub_id_json = cJSON_GetArrayItem(parsed, 1);
|
||||
|
@ -806,6 +932,7 @@ cJSON** nostr_relay_pool_query_sync(
|
|||
}
|
||||
if (msg_type) free(msg_type);
|
||||
if (parsed) cJSON_Delete(parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1059,76 +1186,78 @@ double nostr_relay_pool_get_relay_query_latency(
|
|||
}
|
||||
|
||||
int nostr_relay_pool_ping_relay(
|
||||
nostr_relay_pool_t* pool,
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url) {
|
||||
|
||||
|
||||
// PING FUNCTIONALITY DISABLED
|
||||
/* COMMENTED OUT - PING FUNCTIONALITY DISABLED
|
||||
if (!pool || !relay_url) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
|
||||
relay_connection_t* relay = find_relay_by_url(pool, relay_url);
|
||||
if (!relay || !relay->ws_client) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
|
||||
if (ensure_relay_connection(relay) != 0) {
|
||||
return NOSTR_ERROR_NETWORK_FAILED;
|
||||
}
|
||||
|
||||
|
||||
time_t current_time = time(NULL);
|
||||
relay->pending_ping_start_ms = get_current_time_ms();
|
||||
relay->ping_pending = 1;
|
||||
relay->last_ping_sent = current_time;
|
||||
relay->next_ping_time = current_time + NOSTR_POOL_PING_INTERVAL;
|
||||
|
||||
|
||||
if (nostr_ws_send_ping(relay->ws_client, "ping", 4) < 0) {
|
||||
relay->ping_pending = 0;
|
||||
return NOSTR_ERROR_NETWORK_FAILED;
|
||||
}
|
||||
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
*/
|
||||
|
||||
|
||||
(void)pool; // Suppress unused parameter warning
|
||||
(void)relay_url; // Suppress unused parameter warning
|
||||
return NOSTR_ERROR_INVALID_INPUT; // Function disabled
|
||||
}
|
||||
|
||||
int nostr_relay_pool_ping_relay_sync(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url,
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url,
|
||||
int timeout_seconds) {
|
||||
|
||||
|
||||
// PING FUNCTIONALITY DISABLED
|
||||
/* COMMENTED OUT - PING FUNCTIONALITY DISABLED
|
||||
if (!pool || !relay_url) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
|
||||
relay_connection_t* relay = find_relay_by_url(pool, relay_url);
|
||||
if (!relay || !relay->ws_client) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
|
||||
if (ensure_relay_connection(relay) != 0) {
|
||||
return NOSTR_ERROR_NETWORK_FAILED;
|
||||
}
|
||||
|
||||
|
||||
if (timeout_seconds <= 0) {
|
||||
timeout_seconds = 5;
|
||||
}
|
||||
|
||||
|
||||
time_t current_time = time(NULL);
|
||||
relay->pending_ping_start_ms = get_current_time_ms();
|
||||
relay->ping_pending = 1;
|
||||
relay->last_ping_sent = current_time;
|
||||
relay->next_ping_time = current_time + NOSTR_POOL_PING_INTERVAL;
|
||||
|
||||
|
||||
if (nostr_ws_send_ping(relay->ws_client, "ping", 4) < 0) {
|
||||
relay->ping_pending = 0;
|
||||
return NOSTR_ERROR_NETWORK_FAILED;
|
||||
}
|
||||
|
||||
|
||||
// Wait for PONG response
|
||||
time_t wait_start = time(NULL);
|
||||
while (time(NULL) - wait_start < timeout_seconds && relay->ping_pending) {
|
||||
|
@ -1136,7 +1265,7 @@ int nostr_relay_pool_ping_relay_sync(
|
|||
int len = nostr_ws_receive(relay->ws_client, buffer, sizeof(buffer) - 1, 1000);
|
||||
if (len > 0) {
|
||||
buffer[len] = '\0';
|
||||
|
||||
|
||||
char* msg_type = NULL;
|
||||
cJSON* parsed = NULL;
|
||||
if (nostr_parse_relay_message(buffer, &msg_type, &parsed) == 0) {
|
||||
|
@ -1146,17 +1275,17 @@ int nostr_relay_pool_ping_relay_sync(
|
|||
if (relay->ping_pending) {
|
||||
double current_time_ms = get_current_time_ms();
|
||||
double ping_latency = current_time_ms - relay->pending_ping_start_ms;
|
||||
|
||||
|
||||
// Update ping statistics
|
||||
if (relay->stats.ping_samples == 0) {
|
||||
relay->stats.ping_latency_avg = ping_latency;
|
||||
relay->stats.ping_latency_min = ping_latency;
|
||||
relay->stats.ping_latency_max = ping_latency;
|
||||
} else {
|
||||
relay->stats.ping_latency_avg =
|
||||
(relay->stats.ping_latency_avg * relay->stats.ping_samples + ping_latency) /
|
||||
relay->stats.ping_latency_avg =
|
||||
(relay->stats.ping_latency_avg * relay->stats.ping_samples + ping_latency) /
|
||||
(relay->stats.ping_samples + 1);
|
||||
|
||||
|
||||
if (ping_latency < relay->stats.ping_latency_min) {
|
||||
relay->stats.ping_latency_min = ping_latency;
|
||||
}
|
||||
|
@ -1164,11 +1293,11 @@ int nostr_relay_pool_ping_relay_sync(
|
|||
relay->stats.ping_latency_max = ping_latency;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
relay->stats.ping_latency_current = ping_latency;
|
||||
relay->stats.ping_samples++;
|
||||
relay->ping_pending = 0;
|
||||
|
||||
|
||||
if (msg_type) free(msg_type);
|
||||
if (parsed) cJSON_Delete(parsed);
|
||||
return NOSTR_SUCCESS;
|
||||
|
@ -1179,12 +1308,15 @@ int nostr_relay_pool_ping_relay_sync(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Timeout
|
||||
relay->ping_pending = 0;
|
||||
return NOSTR_ERROR_NETWORK_FAILED;
|
||||
*/
|
||||
|
||||
|
||||
(void)pool; // Suppress unused parameter warning
|
||||
(void)relay_url; // Suppress unused parameter warning
|
||||
(void)timeout_seconds; // Suppress unused parameter warning
|
||||
return NOSTR_ERROR_INVALID_INPUT; // Function disabled
|
||||
}
|
||||
|
||||
|
@ -1233,52 +1365,58 @@ int nostr_relay_pool_poll(nostr_relay_pool_t* pool, int timeout_ms) {
|
|||
if (!pool) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int events_processed = 0;
|
||||
|
||||
|
||||
for (int i = 0; i < pool->relay_count; i++) {
|
||||
relay_connection_t* relay = pool->relays[i];
|
||||
if (!relay || !relay->ws_client) {
|
||||
if (!relay) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Check if reconnection is needed
|
||||
if (should_attempt_reconnect(relay)) {
|
||||
attempt_reconnect(relay);
|
||||
}
|
||||
|
||||
// Skip if no WebSocket client
|
||||
if (!relay->ws_client) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check connection state
|
||||
nostr_ws_state_t state = nostr_ws_get_state(relay->ws_client);
|
||||
|
||||
|
||||
if (state != NOSTR_WS_CONNECTED) {
|
||||
relay->status = (state == NOSTR_WS_ERROR) ? NOSTR_POOL_RELAY_ERROR : NOSTR_POOL_RELAY_DISCONNECTED;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
relay->status = NOSTR_POOL_RELAY_CONNECTED;
|
||||
|
||||
// PING FUNCTIONALITY DISABLED - Automatic ping management
|
||||
/* COMMENTED OUT - PING FUNCTIONALITY DISABLED
|
||||
// Check if we need to send a ping to keep the connection alive
|
||||
if (current_time >= relay->next_ping_time && !relay->ping_pending) {
|
||||
relay->pending_ping_start_ms = get_current_time_ms();
|
||||
relay->ping_pending = 1;
|
||||
relay->last_ping_sent = current_time;
|
||||
relay->next_ping_time = current_time + NOSTR_POOL_PING_INTERVAL;
|
||||
|
||||
if (nostr_ws_send_ping(relay->ws_client, "ping", 4) < 0) {
|
||||
relay->ping_pending = 0;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// Connection health monitoring (ping/pong)
|
||||
check_connection_health(relay);
|
||||
|
||||
// Process incoming messages
|
||||
char buffer[8192];
|
||||
int timeout_per_relay = timeout_ms / pool->relay_count;
|
||||
|
||||
|
||||
int len = nostr_ws_receive(relay->ws_client, buffer, sizeof(buffer) - 1, timeout_per_relay);
|
||||
|
||||
|
||||
if (len > 0) {
|
||||
buffer[len] = '\0';
|
||||
process_relay_message(pool, relay, buffer);
|
||||
|
||||
// Check if this is a pong message (WebSocket library prefixes pong messages)
|
||||
if (strncmp(buffer, "__PONG__", 8) == 0) {
|
||||
// Handle pong response for connection health monitoring
|
||||
handle_pong_response(relay);
|
||||
} else {
|
||||
// Process as regular NOSTR message
|
||||
process_relay_message(pool, relay, buffer);
|
||||
}
|
||||
events_processed++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return events_processed;
|
||||
}
|
||||
|
|
|
@ -184,8 +184,20 @@ typedef struct {
|
|||
typedef struct nostr_relay_pool nostr_relay_pool_t;
|
||||
typedef struct nostr_pool_subscription nostr_pool_subscription_t;
|
||||
|
||||
// Reconnection configuration
|
||||
typedef struct {
|
||||
int enable_auto_reconnect; // 1 = enable, 0 = disable
|
||||
int max_reconnect_attempts; // Max attempts per relay
|
||||
int initial_reconnect_delay_ms; // Initial delay between attempts
|
||||
int max_reconnect_delay_ms; // Max delay (cap exponential backoff)
|
||||
int reconnect_backoff_multiplier; // Delay multiplier
|
||||
int ping_interval_seconds; // How often to ping (0 = disable)
|
||||
int pong_timeout_seconds; // How long to wait for pong before reconnecting
|
||||
} nostr_pool_reconnect_config_t;
|
||||
|
||||
// Relay pool management functions
|
||||
nostr_relay_pool_t* nostr_relay_pool_create(void);
|
||||
nostr_relay_pool_t* nostr_relay_pool_create(nostr_pool_reconnect_config_t* config);
|
||||
nostr_pool_reconnect_config_t* nostr_pool_reconnect_config_default(void);
|
||||
int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url);
|
||||
int nostr_relay_pool_remove_relay(nostr_relay_pool_t* pool, const char* relay_url);
|
||||
void nostr_relay_pool_destroy(nostr_relay_pool_t* pool);
|
||||
|
@ -198,7 +210,8 @@ nostr_pool_subscription_t* nostr_relay_pool_subscribe(
|
|||
cJSON* filter,
|
||||
void (*on_event)(cJSON* event, const char* relay_url, void* user_data),
|
||||
void (*on_eose)(void* user_data),
|
||||
void* user_data);
|
||||
void* user_data,
|
||||
int close_on_eose);
|
||||
int nostr_pool_subscription_close(nostr_pool_subscription_t* subscription);
|
||||
|
||||
// Event loop functions
|
||||
|
@ -242,6 +255,9 @@ int nostr_relay_pool_reset_relay_stats(
|
|||
double nostr_relay_pool_get_relay_query_latency(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url);
|
||||
double nostr_relay_pool_get_relay_ping_latency(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url);
|
||||
|
||||
// Synchronous relay operations (one-off queries/publishes)
|
||||
typedef enum {
|
||||
|
|
|
@ -0,0 +1,852 @@
|
|||
[Thu Oct 2 11:42:57 2025] 🚀 Pool test started
|
||||
|
||||
[Thu Oct 2 11:43:01 2025] ðŸ<C3B0>Š Pool started with default relay
|
||||
|
||||
[Thu Oct 2 11:43:14 2025] âž• Relay added: wss://nos.lol
|
||||
|
||||
[Thu Oct 2 11:43:36 2025] ðŸ”<C5B8> New subscription created (ID: 1)
|
||||
Filter: {
|
||||
"kinds": [1],
|
||||
"since": 1759419809,
|
||||
"limit": 10
|
||||
}
|
||||
|
||||
[Thu Oct 2 11:43:36 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 5449492f915a...
|
||||
├── Pubkey: c231760b10ce...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419811
|
||||
└── Content: 💜💜💜💜💜💜💜💜💜💜💜💜 https://nostr.download/e27f32b258bbdfe1e73dc7d06...
|
||||
|
||||
[Thu Oct 2 11:43:36 2025] 📋 EOSE received - all stored events delivered
|
||||
|
||||
[Thu Oct 2 11:43:41 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: caf09bb60e64...
|
||||
├── Pubkey: e62adca21cf6...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419821
|
||||
└── Content: john is the best
|
||||
he likes to just stand there
|
||||
he likes to eat sugar cubes
|
||||
he mostly just stands t...
|
||||
|
||||
[Thu Oct 2 11:43:44 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: e01356b1bf0a...
|
||||
├── Pubkey: 0406b1bbbe74...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419823
|
||||
└── Content: ðŸ<C3B0>…ギルティーギアXX
|
||||
|
||||
[Thu Oct 2 11:44:00 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 5fad5c083762...
|
||||
├── Pubkey: db0445869e55...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419839
|
||||
└── Content: Nothing but the best will do for magic internet money.
|
||||
https://blossom.primal.net/5f7fbea898bfcf9...
|
||||
|
||||
[Thu Oct 2 11:44:10 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 7cc57da68132...
|
||||
├── Pubkey: 087fe3adb094...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419850
|
||||
└── Content: That is a classic film
|
||||
|
||||
[Thu Oct 2 11:44:15 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 609a860530f5...
|
||||
├── Pubkey: 17538dc2a627...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419855
|
||||
└── Content: ðŸ˜<C5B8>
|
||||
|
||||
Zurich?
|
||||
|
||||
[Thu Oct 2 11:44:16 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 6af068b23493...
|
||||
├── Pubkey: 2b1de1346ff1...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419856
|
||||
└── Content: Does your door work with a string? How is the door locked? Or can you still push it up?
|
||||
|
||||
[Thu Oct 2 11:44:23 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 8ab3cd207956...
|
||||
├── Pubkey: deab79dafa1c...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419864
|
||||
└── Content: I'll never understand why people believe folks who are paid to say others words professionally ðŸ...
|
||||
|
||||
[Thu Oct 2 11:44:31 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: d59a8d8dc608...
|
||||
├── Pubkey: e62adca21cf6...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419871
|
||||
└── Content: sometimes he hangs out with the unnamed donkey made of moonlight
|
||||
|
||||
he has friends
|
||||
|
||||
[Thu Oct 2 11:44:32 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 6c4978b1fce6...
|
||||
├── Pubkey: c831e221f166...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419872
|
||||
└── Content: It was the Russian... but then again, maybe not 🤣
|
||||
|
||||
https://blossom.primal.net/986df1efadeff5...
|
||||
|
||||
[Thu Oct 2 11:44:34 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 2a2f196a072c...
|
||||
├── Pubkey: d3f94b353542...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419872
|
||||
└── Content: #meme #bitcoin
|
||||
|
||||
|
||||
|
||||
https://blossom.primal.net/813e03107f57f4606a2d8a8c129c6df03524fcdcbcdce6cbbf...
|
||||
|
||||
[Thu Oct 2 11:44:43 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 9570719ae3ca...
|
||||
├── Pubkey: f7922a0adb3f...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419882
|
||||
└── Content: This is the show that started all this #DeMu talk and its unlike anything you've ever listened to...
|
||||
|
||||
[Thu Oct 2 11:44:54 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 4c9de90e324d...
|
||||
├── Pubkey: 16cb4b38fe9a...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419893
|
||||
└── Content: https://i.nostr.build/q6whqe8wlzMy6ubL.png
|
||||
Its that time again! We're closing in on the end of th...
|
||||
|
||||
[Thu Oct 2 11:44:55 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 3b5a6c87be19...
|
||||
├── Pubkey: 036533caa872...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419894
|
||||
└── Content: Yeah! I like to think I don't save anything unnecessary, or totally unusable, but it happens. Let...
|
||||
|
||||
[Thu Oct 2 11:44:57 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 37bb53f99114...
|
||||
├── Pubkey: b834a8c07a51...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419897
|
||||
└── Content: #Lafayette Indiana https://v.nostr.build/x3WmXwgR0CTEBJq9.mp4
|
||||
|
||||
[Thu Oct 2 11:45:00 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 3a7898a12c5e...
|
||||
├── Pubkey: d7c13d1edc3e...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419900
|
||||
└── Content: ✄------------ 0:45 ------------✄
|
||||
|
||||
[Thu Oct 2 11:45:01 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 315bfab7c206...
|
||||
├── Pubkey: 087fe3adb094...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419901
|
||||
└── Content: That is freaky
|
||||
|
||||
[Thu Oct 2 11:45:03 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 6f06e3704059...
|
||||
├── Pubkey: 35edf1096d3c...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419902
|
||||
└── Content: And how a normie is supposed to safeguard such an id from hacks? RFID under the skin?
|
||||
|
||||
|
||||
|
||||
[Thu Oct 2 11:45:03 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 0ae20b5693f0...
|
||||
├── Pubkey: ad0de68eb660...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419904
|
||||
└── Content: ความà¸<C3A0>ันà¸<C3A0>ับความหวังต่างà¸<C3A0>ันตรงไหน
|
||||
#si...
|
||||
|
||||
[Thu Oct 2 11:45:05 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: a11231aea6b5...
|
||||
├── Pubkey: f768fae9f239...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419904
|
||||
└── Content: #ãƒ<C3A3>ズワードランã‚ング
|
||||
|
||||
1ä½<EFBFBD>: #リボ (11)
|
||||
2ä½<EFBFBD>: #ã<>ªã<C2AA><C3A3> (5)
|
||||
3ä½<EFBFBD>: #ミã‚サー (5)
|
||||
4ä½...
|
||||
|
||||
[Thu Oct 2 11:45:10 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: fda1a94c635b...
|
||||
├── Pubkey: bdb827d5dd18...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419931
|
||||
└── Content: Have been saying this for minimum 6-10 months now
|
||||
|
||||
[Thu Oct 2 11:45:15 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: fbbb1a8e1883...
|
||||
├── Pubkey: d3f94b353542...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419913
|
||||
└── Content: #meme #bitcoin
|
||||
|
||||
|
||||
|
||||
https://blossom.primal.net/ea65f26f04fe282a56efc0c74b6f7881dcf95cf1bed76e9646...
|
||||
|
||||
[Thu Oct 2 11:45:18 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: dae37e3e2a01...
|
||||
├── Pubkey: f683e87035f7...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419916
|
||||
└── Content: 📊 Reliable nostr statistics now available at npub.world
|
||||
|
||||
https://blossom.primal.net/fe6d77745c...
|
||||
|
||||
[Thu Oct 2 11:45:21 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: e98f692cd821...
|
||||
├── Pubkey: 4cf2e85f2ecf...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419902
|
||||
└── Content: Market snapshot (2 Oct): PX 2,359.40 +0.33%; DAX 24,423.61 +1.29%; DJ STOXX 600 567.60 +0.52%; NA...
|
||||
|
||||
[Thu Oct 2 11:45:25 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: a4debf6eda9d...
|
||||
├── Pubkey: 6a02b7d5d5c1...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419924
|
||||
└── Content: Well, nostr:npub1f5kc2agn63ecv2ua4909z9ahgmr2x9263na36jh6r908ql0926jq3nvk2u has done an amazing j...
|
||||
|
||||
[Thu Oct 2 11:45:36 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 75edaab76e6a...
|
||||
├── Pubkey: d3f94b353542...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419934
|
||||
└── Content: #meme #bitcoin
|
||||
|
||||
|
||||
|
||||
https://blossom.primal.net/47e112b56565c473db0300b9f1c8e9026d31f29b2e5a9f26b6...
|
||||
|
||||
[Thu Oct 2 11:45:42 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: cce964ee26c3...
|
||||
├── Pubkey: 4e64c603aceb...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419942
|
||||
└── Content: ðŸ‘<C5B8>ðŸ<C3B0>»ðŸ‘<C5B8>ðŸ<C3B0>»ðŸ‘<C5B8>ðŸ<C3B0>»ðŸ‘<C5B8>ðŸ<C3B0>»ðŸ™<C5B8>ðŸ<C3B0>»
|
||||
|
||||
nostr:nevent1qqswaa533cykjc0vgxxmf8nmu2dg3g6uu0xdm5cw4...
|
||||
|
||||
[Thu Oct 2 11:45:43 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: bd66386bb0e4...
|
||||
├── Pubkey: fdf22dc28791...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419943
|
||||
└── Content: https://image.nostr.build/ebf0ff9e9675e82b53718cf515dcb1168f03968fe0108993f43991559093c853.jpg
|
||||
|
||||
[Thu Oct 2 11:45:44 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 3f1089f6f622...
|
||||
├── Pubkey: adc14fa3ad59...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419943
|
||||
└── Content: 🫵ðŸ<C3B0>»ðŸ™<C5B8>
|
||||
|
||||
[Thu Oct 2 11:45:47 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: b2db18db5304...
|
||||
├── Pubkey: d3f94b353542...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419945
|
||||
└── Content: #meme #bitcoin
|
||||
|
||||
|
||||
|
||||
https://blossom.primal.net/cb9eb04c5bb49d58a33c0e17425606f1e3357893cb6d47d3e0...
|
||||
|
||||
[Thu Oct 2 11:45:47 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: c0abb73c57da...
|
||||
├── Pubkey: 96ae8563bd22...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419947
|
||||
└── Content: Health insurance is a scam.
|
||||
|
||||
[Thu Oct 2 11:45:54 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 25eaedc2c644...
|
||||
├── Pubkey: d1f8ac7cfbac...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419952
|
||||
└── Content: Happy Belated Birthday! Hope you had a Funtastic day 🎉ðŸŽ<C5B8>ðŸ’<C5B8>🎂🎊
|
||||
|
||||
[Thu Oct 2 11:45:57 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 3b6919f20ecb...
|
||||
├── Pubkey: 52b4a076bcbb...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419957
|
||||
└── Content: gm Nostr.
|
||||
|
||||
[Thu Oct 2 11:45:58 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: d05a586a961a...
|
||||
├── Pubkey: 71c20e5545c5...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419957
|
||||
└── Content: Mood every morning
|
||||
https://blossom.primal.net/a4b9b97b0948f6ab25036c52ce3580db28dd52396f1f3d7277...
|
||||
|
||||
[Thu Oct 2 11:46:02 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 93af19a07911...
|
||||
├── Pubkey: f683e87035f7...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419961
|
||||
└── Content: cc nostr:nprofile1qy2hwumn8ghj7ct8vaezumn0wd68ytnvv9hxgqg4waehxw309a5xjum59ehx7um5wghxcctwvsqzpmn...
|
||||
|
||||
[Thu Oct 2 11:46:06 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 92450741c3db...
|
||||
├── Pubkey: 8384e79741c1...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419967
|
||||
└── Content: It looks like some one with Parkinson's made it.
|
||||
|
||||
|
||||
|
||||
[Thu Oct 2 11:46:16 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: f0b1fa224498...
|
||||
├── Pubkey: d3f94b353542...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419974
|
||||
└── Content: #meme #bitcoin
|
||||
BUY BITCOIN
|
||||
|
||||
|
||||
|
||||
https://blossom.primal.net/cc20ee3b13ecfc84fef02edf5c6bde6ee12431...
|
||||
|
||||
[Thu Oct 2 11:46:20 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 8b05cc12eb5c...
|
||||
├── Pubkey: f40901c9f844...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419979
|
||||
└── Content: Pick a Random Open PR #2003
|
||||
タイトル: Use nip22 style tags in git statuses
|
||||
作æˆ<EFBFBD>者: dluvian...
|
||||
|
||||
[Thu Oct 2 11:46:23 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: e48131f4ad3e...
|
||||
├── Pubkey: 899ab335d3b0...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419983
|
||||
└── Content: Bitcoin is invading trad-fi.
|
||||
nostr:nevent1qqsxnxk6dc6gwthdd74em4tlrkecnyfg2zss3thdl390ja06qyhaf6g...
|
||||
|
||||
[Thu Oct 2 11:46:26 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: db3417a1ea38...
|
||||
├── Pubkey: 4cf2e85f2ecf...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419967
|
||||
└── Content: Commerzbank’s latest forecasts suggest the US economy will grow this year and next slightly bel...
|
||||
|
||||
[Thu Oct 2 11:46:27 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 9ef2f62d6b90...
|
||||
├── Pubkey: e62adca21cf6...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419986
|
||||
└── Content: they, john and the no name donkey,
|
||||
just kinda walk around or are still
|
||||
|
||||
ive never seen john get ...
|
||||
|
||||
[Thu Oct 2 11:46:28 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 764def10463e...
|
||||
├── Pubkey: f5e67a824944...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419986
|
||||
└── Content: compro mosquitos wolbachia ¿alguien?
|
||||
|
||||
[Thu Oct 2 11:46:33 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 67c7dd1bcba2...
|
||||
├── Pubkey: 04c960497af6...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419993
|
||||
└── Content: Oh, I didn't now we were orange , had a tail and ðŸ<C3B0>¾ðŸ<C3B0>¾ 🤔
|
||||
Maybe I am delusional 🤔😅
|
||||
|
||||
...
|
||||
|
||||
[Thu Oct 2 11:46:36 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: afe890faef3e...
|
||||
├── Pubkey: 17538dc2a627...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419996
|
||||
└── Content: https://npub.world/stats
|
||||
|
||||
🤌🤌
|
||||
|
||||
nostr:nevent1qqsd4cm78c4qrlskgp870qhh7qt9sepn98gp0utj2gk6v5j4...
|
||||
|
||||
[Thu Oct 2 11:46:51 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 7acc74259799...
|
||||
├── Pubkey: a3e4cba409d3...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419989
|
||||
└── Content:
|
||||
|
||||
https://uploads.postiz.com/3264e94fb1b846c8bfccf59e48d8bf85.png
|
||||
|
||||
[Thu Oct 2 11:46:58 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 2fd4cd59789e...
|
||||
├── Pubkey: 0b26f590631b...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420017
|
||||
└── Content: 🤣🤣🤣
|
||||
|
||||
[Thu Oct 2 11:47:01 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 9c46471834c1...
|
||||
├── Pubkey: 2b1de1346ff1...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420021
|
||||
└── Content: https://cdn.nostrcheck.me/1c674155c4f713054ec8a10df5eaa5636d243df40d07447980f1885642d706c1.webp
|
||||
|
||||
...
|
||||
|
||||
[Thu Oct 2 11:47:04 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 51a4d7c2106c...
|
||||
├── Pubkey: bc0bcc50f9a1...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420023
|
||||
└── Content: ألÙ<E2809E> مبروك 🥳 والله يرزقكم برهم وصلاØÙ‡Ù… والØÙ…دلله على ...
|
||||
|
||||
[Thu Oct 2 11:47:06 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: b63745941271...
|
||||
├── Pubkey: 6a02b7d5d5c1...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420018
|
||||
└── Content: Gm agitator
|
||||
|
||||
[Thu Oct 2 11:47:06 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: ed874935ec1d...
|
||||
├── Pubkey: 0406b1bbbe74...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420025
|
||||
└── Content: ã<>Šã‚„ã<E2809E>®ã<C2AE>™ðŸŒ”
|
||||
https://youtu.be/bOSWJTu5Prw?si=oT3DtqbpbOp0VDrr
|
||||
|
||||
[Thu Oct 2 11:47:06 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 0e32734cbc67...
|
||||
├── Pubkey: 17538dc2a627...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420025
|
||||
└── Content: https://image.nostr.build/7304775744fef9bc979a1940fb31202045b8b06f1ebfe90141266eeb28c52417.jpg
|
||||
|
||||
n...
|
||||
|
||||
[Thu Oct 2 11:47:21 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 56acfa902e7d...
|
||||
├── Pubkey: d981591e0ea6...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419861
|
||||
└── Content: DiversityWatch (October 2, 2025)
|
||||
|
||||
From Amerika.org
|
||||
|
||||
~~~ ADL Deletes ‘Extremism’ Database Afte...
|
||||
|
||||
[Thu Oct 2 11:47:25 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: faf15824f3bf...
|
||||
├── Pubkey: deab79dafa1c...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420047
|
||||
└── Content: GM semi 🫡
|
||||
|
||||
[Thu Oct 2 11:47:32 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: ea997cd30e54...
|
||||
├── Pubkey: e62adca21cf6...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420052
|
||||
└── Content: firewood or foraged pennyroyal
|
||||
|
||||
but he doesnt work that much
|
||||
|
||||
i would never call him a beast of b...
|
||||
|
||||
[Thu Oct 2 11:47:38 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: fe973c7c45f5...
|
||||
├── Pubkey: 0b26f590631b...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420057
|
||||
└── Content: Sorry brother 😬🤣 I was restarting router
|
||||
|
||||
[Thu Oct 2 11:47:39 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: f7f07c0a6a4d...
|
||||
├── Pubkey: 356875ffd729...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420059
|
||||
└── Content: You want to get as far away from the lipid profile of seed oils as you can.
|
||||
|
||||
Terrible = canola oi...
|
||||
|
||||
[Thu Oct 2 11:47:51 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: d5af40b4e947...
|
||||
├── Pubkey: edb470271297...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420072
|
||||
└── Content: 10 years ago I emerged from the woods to build something more
|
||||
|
||||
Now after having done so I feel t...
|
||||
|
||||
[Thu Oct 2 11:47:59 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 4abc3d65f486...
|
||||
├── Pubkey: af5e5c0f30b2...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420078
|
||||
└── Content: Dear me✨
|
||||
|
||||
Brave and beautiful one.
|
||||
From now on and till the end of your earthly journey, I will...
|
||||
|
||||
[Thu Oct 2 11:48:01 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: f0f30b54f0ae...
|
||||
├── Pubkey: d3f94b353542...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420079
|
||||
└── Content: #meme #bitcoin
|
||||
Ignore the direction of the school.
|
||||
Keep stacking sats, fish.
|
||||
|
||||
|
||||
|
||||
https://blossom....
|
||||
|
||||
[Thu Oct 2 11:48:05 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: c5935c3e834b...
|
||||
├── Pubkey: 3f770d65d3a7...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420085
|
||||
└── Content: Nice looking rooster, bro.
|
||||
|
||||
[Thu Oct 2 11:48:07 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: db0b8148c793...
|
||||
├── Pubkey: a4132de3f6fe...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420086
|
||||
└── Content: Intercepted Gaza flotilla boats arrive at Israel’s Ashdod port
|
||||
|
||||
Several boats from the Global S...
|
||||
|
||||
[Thu Oct 2 11:48:09 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: a61672484b30...
|
||||
├── Pubkey: e62adca21cf6...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420089
|
||||
└── Content: thats what i know about john
|
||||
|
||||
he stands there and he sometimes has company
|
||||
|
||||
[Thu Oct 2 11:48:10 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: be57235ebb4b...
|
||||
├── Pubkey: 01438c6f3044...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419985
|
||||
└── Content: Ex-OpenAI researcher dissects one of ChatGPT’s delusional spirals
|
||||
https://techcrunch.com/2025/1...
|
||||
|
||||
[Thu Oct 2 11:48:10 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 299ebf1ac3aa...
|
||||
├── Pubkey: 0d211376b2c9...
|
||||
├── Kind: 1
|
||||
├── Created: 1759419938
|
||||
└── Content: The best Amazon Prime Day deals on Anker charging gear and other accessories
|
||||
https://www.engadget...
|
||||
|
||||
[Thu Oct 2 11:48:16 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 1b9e8c4b3ee3...
|
||||
├── Pubkey: 17538dc2a627...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420095
|
||||
└── Content: ðŸ<C3B0>”
|
||||
|
||||
nostr:nevent1qqsfc3j8rq6vr75c3k7xsmd945u49dfxwsshnh9z4pm2s27wl4ymjtcppamhxue69uhkumewwd68yt...
|
||||
|
||||
[Thu Oct 2 11:48:16 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: dbce08f706dd...
|
||||
├── Pubkey: 36af108c2769...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420096
|
||||
└── Content: You don’t want your total cholesterol crazy high but don’t want it too low either. Mainstream...
|
||||
|
||||
[Thu Oct 2 11:48:42 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: a69f3dd53072...
|
||||
├── Pubkey: 6a02b7d5d5c1...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420119
|
||||
└── Content: Well looks like I have to look back for that one
|
||||
|
||||
[Thu Oct 2 11:48:45 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 8c11338af6bb...
|
||||
├── Pubkey: 71c20e5545c5...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420125
|
||||
└── Content: mood every morning
|
||||
https://blossom.primal.net/2de495138c52e3e71f19c437d4e2a83a78b215018dcf88f074...
|
||||
|
||||
[Thu Oct 2 11:48:55 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 09c37f81cd31...
|
||||
├── Pubkey: 8c5923931963...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420132
|
||||
└── Content: プラãƒ<C3A3>ナã<C5A0>«ã<C2AB>ªã<C2AA>£ã<C2A3>¦ã‚‹
|
||||
|
||||
[Thu Oct 2 11:48:56 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 7d80f283a966...
|
||||
├── Pubkey: 17538dc2a627...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420135
|
||||
└── Content: ðŸ˜<C5B8>
|
||||
|
||||
nostr:nevent1qqsxkzjr6tmef5kxng3c4p4nk58lkmr2jm60y60jmszrha64fpnuy2sppamhxue69uhkumewwd68yt...
|
||||
|
||||
[Thu Oct 2 11:48:57 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 55927691ba22...
|
||||
├── Pubkey: b63581fed371...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420136
|
||||
└── Content: â€<C3A2>Crypto Live Dealer Baccarat Redefines Online Gaming
|
||||
|
||||
The integration of blockchain technology ...
|
||||
|
||||
[Thu Oct 2 11:49:04 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 230569d0c203...
|
||||
├── Pubkey: 367cccfb659a...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420143
|
||||
└── Content: Original post: https://bsky.app/profile/did:plc:vrg5pyvxe6havmn5hiqqae22/post/3m274m4enuk2r https...
|
||||
|
||||
[Thu Oct 2 11:49:12 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: d9578c93625a...
|
||||
├── Pubkey: 50d94fc2d858...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420152
|
||||
└── Content: nostr:nprofile1qqsqa6p85dhghvx0cjpu7xrj0qgc939pd3v2ew36uttmz40qxu8f8wq8vdeta what's your favorit...
|
||||
|
||||
[Thu Oct 2 11:49:12 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: b8c5d604f68f...
|
||||
├── Pubkey: 17538dc2a627...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420152
|
||||
└── Content: If you have failed hard at birds I want to follow you
|
||||
|
||||
#farmstr
|
||||
|
||||
nostr:nevent1qqsqesh3dfqcyxmpf4p...
|
||||
|
||||
[Thu Oct 2 11:49:19 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: db5cb3126f16...
|
||||
├── Pubkey: cb230a5e9341...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420158
|
||||
└── Content: ã‚<C3A3>ã<EFBFBD><C3A3>ã€<C3A3>プラãƒ<C3A3>ナã<C5A0>£ã<C2A3>¦ã<C2A6>™ã<E284A2>”ã<E2809D>„ã<E2809E>§ã<C2A7>™ã<E284A2>〜ï¼<C3AF>ã<EFBFBD>Šã‚<C3A3>ã<EFBFBD>§ã<C2A7>¨ã<C2A8>†ã<E280A0>”ã<E2809D>–ã<E28093>„ã<E2809E>¾ã<C2BE>™ï¼<C3AF>ã<EFBFBD>©ã‚“ã<E2809C>ªã<C2AA>µã...
|
||||
|
||||
[Thu Oct 2 11:49:20 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 0b559f2c7aab...
|
||||
├── Pubkey: bf2376e17ba4...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420160
|
||||
└── Content: funny that I haven’t seen people being unnecessarily mean about core/knots on nostr, or maybe I...
|
||||
|
||||
[Thu Oct 2 11:49:22 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 225ec974be70...
|
||||
├── Pubkey: d981591e0ea6...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420041
|
||||
└── Content: The Shadowy Legacy of Andrew McCabe
|
||||
|
||||
From Roger Stone
|
||||
|
||||
Andrew McCabe, former Deputy Director of t...
|
||||
|
||||
[Thu Oct 2 11:49:23 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 6409e853ab14...
|
||||
├── Pubkey: d3f94b353542...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420162
|
||||
└── Content: It's a neverending paretto within the 20%.
|
||||
|
||||
|
||||
|
||||
[Thu Oct 2 11:49:32 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 492c3b86c6b2...
|
||||
├── Pubkey: 1848313553d3...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420172
|
||||
└── Content: Again, allowed yes. You should phrase your questions differently.
|
||||
The answer to if we "should all...
|
||||
|
||||
[Thu Oct 2 11:49:39 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 3df638b9b7bd...
|
||||
├── Pubkey: 5bab615f042d...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420178
|
||||
└── Content: betaã<61>«äººæ¨©ã<C2A9>ªã<C2AA>„ã<E2809E>£ã<C2A3>¦ã<C2A6>„ã<E2809E><C3A3>らã<E280B0>©ã‚“ã<E2809C>•ã‚“ã<E2809C>«è¨€ã‚<C3A3>れã<C592>Ÿã<C5B8>‹ã‚‰
|
||||
|
||||
[Thu Oct 2 11:49:44 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 9fc1d1f3c429...
|
||||
├── Pubkey: cf3a2db57981...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420184
|
||||
└── Content: Das ist nun die geputzte „Auslese“ für die erste richtige Pilzfanne in diesem Jahr 😋😋ð...
|
||||
|
||||
[Thu Oct 2 11:49:54 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 6b20ca81b8d9...
|
||||
├── Pubkey: 50d94fc2d858...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420193
|
||||
└── Content: nostr:nprofile1qqsqa6p85dhghvx0cjpu7xrj0qgc939pd3v2ew36uttmz40qxu8f8wq8vdeta can you hear me
|
||||
|
||||
[Thu Oct 2 11:49:54 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: a28bc5751cf3...
|
||||
├── Pubkey: 479ec16d345e...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420194
|
||||
└── Content: คิดถึงà¹<C3A0>ชมป์เหมืà¸à¸™à¸<C3A0>ัน เจà¸à¸<C3A0>ันเชียงใ...
|
||||
|
||||
[Thu Oct 2 11:50:00 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: bb9570ecdbb2...
|
||||
├── Pubkey: b1b4105a564a...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420200
|
||||
└── Content: ✄------------ 0:50 ------------✄
|
||||
|
||||
[Thu Oct 2 11:50:02 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 877bfd84cb78...
|
||||
├── Pubkey: 8c5923931963...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420199
|
||||
└── Content: ã<>Ÿã<C5B8>¶ã‚“有料ã<E284A2>®ã<C2AE>—ã<E28094>‹ã‚‚ã<E2809A>¡ã‚‡ã<E280A1>£ã<C2A3>¨ã<C2A8>„ã<E2809E>„プランã<C2B3>˜ã‚ƒã‚“
|
||||
|
||||
[Thu Oct 2 11:50:08 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: d2a24f8bd654...
|
||||
├── Pubkey: 832b77d5ecb0...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420208
|
||||
└── Content: 🟩BUY BTC with EUR
|
||||
Price: 102390.56EUR (0%)
|
||||
BTC: 0.023
|
||||
EUR: 2355
|
||||
Method: SEPA Instant
|
||||
Created: ...
|
||||
|
||||
[Thu Oct 2 11:50:09 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 4d617918bff3...
|
||||
├── Pubkey: b7a07661869d...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420209
|
||||
└── Content: Le consensus de Nakamoto – Episode 22
|
||||
Dans cette vidéo, Cyril Grunspan utilise à nouveau un m...
|
||||
|
||||
[Thu Oct 2 11:50:14 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: d60c3b59af2e...
|
||||
├── Pubkey: ff9f3daff5dc...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420210
|
||||
└── Content: 🥞 ¡#PancakeSwap pulveriza su propio récord! El DEX multichain alcanzó $749 mil millones en ...
|
||||
|
||||
[Thu Oct 2 11:50:19 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 347c25fe60e8...
|
||||
├── Pubkey: 45835c36f41d...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420218
|
||||
└── Content: btw i use this for everything, i love it:
|
||||
https://github.com/purifyjs/core
|
||||
|
||||
[Thu Oct 2 11:50:20 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 21c1e474706a...
|
||||
├── Pubkey: 367cccfb659a...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420219
|
||||
└── Content: Original post: https://bsky.app/profile/did:plc:keg3c3lhpxiihjho4e7auzaq/post/3m27frfqb7225 https...
|
||||
|
||||
[Thu Oct 2 11:50:25 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 540e8564dd09...
|
||||
├── Pubkey: 5bab615f042d...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420225
|
||||
└── Content: 何ã<E280A2>‹ã<E280B9>—らã<E280B0>®ç„¡åˆ¶é™<C3A9>ã<EFBFBD>Œã<C592>¤ã<C2A4><C3A3>ã‚„ã<E2809E>¤
|
||||
|
||||
[Thu Oct 2 11:50:26 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: b40bae265610...
|
||||
├── Pubkey: 957dd3687817...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420216
|
||||
└── Content: Jimmy Kimmel Calls Trump a 'Son of a Bitch,’ Says Government Shutdown Allows Him to ‘Say What...
|
||||
|
||||
[Thu Oct 2 11:50:27 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 4d880eb2bdfb...
|
||||
├── Pubkey: 5729ad991a7e...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420226
|
||||
└── Content: #asknostr how do you channel your creativity or excess energy?
|
||||
Or when you feel low, how do you ...
|
||||
|
||||
[Thu Oct 2 11:50:30 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 590e23e23d0f...
|
||||
├── Pubkey: ded3db391584...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420229
|
||||
└── Content: insert mia martini
|
||||
|
||||
https://image.nostr.build/9d8ebd836f785a679932f89113809ee63fcbb70d05f31daf5bc...
|
||||
|
||||
[Thu Oct 2 11:50:30 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 239892245953...
|
||||
├── Pubkey: 50d94fc2d858...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420230
|
||||
└── Content: nostr:nprofile1qqsqa6p85dhghvx0cjpu7xrj0qgc939pd3v2ew36uttmz40qxu8f8wq8vdeta hey
|
||||
|
||||
[Thu Oct 2 11:50:37 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 19ec03e4b586...
|
||||
├── Pubkey: 2ff7f5a3df59...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420236
|
||||
└── Content: Hulu Bender's New Look | Futurama | Hulu commercial
|
||||
#Hulu #abancommercials #commercial Video Hulu...
|
||||
|
||||
[Thu Oct 2 11:50:38 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 9575e4b84ac2...
|
||||
├── Pubkey: e62adca21cf6...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420237
|
||||
└── Content: john-knee?
|
||||
johnny yea i know a johnny
|
||||
like human john
|
||||
not active friendship but no quarrel
|
||||
|
||||
[Thu Oct 2 11:50:47 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 9ca71ff6759f...
|
||||
├── Pubkey: 2ff7f5a3df59...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420247
|
||||
└── Content: Xbox Announcing new updates to Xbox Game Pass Ultimate commercial
|
||||
#Xbox #abancommercials #commerc...
|
||||
|
||||
[Thu Oct 2 11:50:48 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 95a8a2fc10cb...
|
||||
├── Pubkey: edc615f59aa8...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420247
|
||||
└── Content: African what? Lol...
|
||||
|
||||
[Thu Oct 2 11:50:50 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 6156127e4923...
|
||||
├── Pubkey: 000001c66890...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420249
|
||||
└── Content: You think the neckline is bold? You should meet the attitude
|
||||
https://images2.imgbox.com/bf/a0/90...
|
||||
|
||||
[Thu Oct 2 11:50:58 2025] 📨 EVENT from wss://nos.lol
|
||||
├── ID: 92d07c3f1344...
|
||||
├── Pubkey: 2ff7f5a3df59...
|
||||
├── Kind: 1
|
||||
├── Created: 1759420257
|
||||
└── Content: How to save your progress in Ghost of Yotei
|
||||
#GhostofYotei #GamingGuide #GameTips Learn
|
||||
how to sa...
|
||||
|
Binary file not shown.
|
@ -0,0 +1,679 @@
|
|||
/*
|
||||
* Interactive Relay Pool Test Program
|
||||
*
|
||||
* Interactive command-line interface for testing nostr_relay_pool functionality.
|
||||
* All output is logged to pool.log while the menu runs in the terminal.
|
||||
*
|
||||
* Usage: ./pool_test
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include "../nostr_core/nostr_core.h"
|
||||
#include "../cjson/cJSON.h"
|
||||
|
||||
// Global variables
|
||||
volatile sig_atomic_t running = 1;
|
||||
nostr_relay_pool_t* pool = NULL;
|
||||
nostr_pool_subscription_t** subscriptions = NULL;
|
||||
int subscription_count = 0;
|
||||
int subscription_capacity = 0;
|
||||
pthread_t poll_thread;
|
||||
int log_fd = -1;
|
||||
|
||||
// Signal handler for clean shutdown
|
||||
void signal_handler(int signum) {
|
||||
(void)signum;
|
||||
running = 0;
|
||||
}
|
||||
|
||||
// Event callback - called when an event is received
|
||||
void on_event(cJSON* event, const char* relay_url, void* user_data) {
|
||||
(void)user_data;
|
||||
|
||||
// Extract basic event information
|
||||
cJSON* id = cJSON_GetObjectItem(event, "id");
|
||||
cJSON* pubkey = cJSON_GetObjectItem(event, "pubkey");
|
||||
cJSON* created_at = cJSON_GetObjectItem(event, "created_at");
|
||||
cJSON* kind = cJSON_GetObjectItem(event, "kind");
|
||||
cJSON* content = cJSON_GetObjectItem(event, "content");
|
||||
|
||||
time_t now = time(NULL);
|
||||
char timestamp[26];
|
||||
ctime_r(&now, timestamp);
|
||||
timestamp[24] = '\0'; // Remove newline
|
||||
|
||||
dprintf(log_fd, "[%s] 📨 EVENT from %s\n", timestamp, relay_url);
|
||||
dprintf(log_fd, "├── ID: %.12s...\n", id && cJSON_IsString(id) ? cJSON_GetStringValue(id) : "unknown");
|
||||
dprintf(log_fd, "├── Pubkey: %.12s...\n", pubkey && cJSON_IsString(pubkey) ? cJSON_GetStringValue(pubkey) : "unknown");
|
||||
dprintf(log_fd, "├── Kind: %d\n", kind && cJSON_IsNumber(kind) ? (int)cJSON_GetNumberValue(kind) : -1);
|
||||
dprintf(log_fd, "├── Created: %lld\n", created_at && cJSON_IsNumber(created_at) ? (long long)cJSON_GetNumberValue(created_at) : 0);
|
||||
|
||||
// Truncate content if too long
|
||||
if (content && cJSON_IsString(content)) {
|
||||
const char* content_str = cJSON_GetStringValue(content);
|
||||
size_t content_len = strlen(content_str);
|
||||
if (content_len > 100) {
|
||||
dprintf(log_fd, "└── Content: %.97s...\n", content_str);
|
||||
} else {
|
||||
dprintf(log_fd, "└── Content: %s\n", content_str);
|
||||
}
|
||||
} else {
|
||||
dprintf(log_fd, "└── Content: (empty)\n");
|
||||
}
|
||||
dprintf(log_fd, "\n");
|
||||
}
|
||||
|
||||
// EOSE callback - called when End of Stored Events is received
|
||||
void on_eose(void* user_data) {
|
||||
(void)user_data;
|
||||
time_t now = time(NULL);
|
||||
char timestamp[26];
|
||||
ctime_r(&now, timestamp);
|
||||
timestamp[24] = '\0';
|
||||
dprintf(log_fd, "[%s] 📋 EOSE received - all stored events delivered\n\n", timestamp);
|
||||
}
|
||||
|
||||
// Background polling thread
|
||||
void* poll_thread_func(void* arg) {
|
||||
(void)arg;
|
||||
|
||||
while (running) {
|
||||
if (pool) {
|
||||
nostr_relay_pool_poll(pool, 100);
|
||||
}
|
||||
struct timespec ts = {0, 10000000}; // 10ms
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Print menu
|
||||
void print_menu() {
|
||||
printf("\n=== NOSTR Relay Pool Test Menu ===\n");
|
||||
printf("1. Start Pool (wss://relay.laantungir.net)\n");
|
||||
printf("2. Stop Pool\n");
|
||||
printf("3. Add relay to pool\n");
|
||||
printf("4. Remove relay from pool\n");
|
||||
printf("5. Add subscription\n");
|
||||
printf("6. Remove subscription\n");
|
||||
printf("7. Show pool status\n");
|
||||
printf("8. Test reconnection (simulate disconnect)\n");
|
||||
printf("9. Exit\n");
|
||||
printf("Choice: ");
|
||||
}
|
||||
|
||||
// Get user input with default
|
||||
char* get_input(const char* prompt, const char* default_value) {
|
||||
static char buffer[1024];
|
||||
printf("%s", prompt);
|
||||
if (default_value) {
|
||||
printf(" [%s]", default_value);
|
||||
}
|
||||
printf(": ");
|
||||
|
||||
if (!fgets(buffer, sizeof(buffer), stdin)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Remove newline
|
||||
size_t len = strlen(buffer);
|
||||
if (len > 0 && buffer[len-1] == '\n') {
|
||||
buffer[len-1] = '\0';
|
||||
}
|
||||
|
||||
// Return default if empty
|
||||
if (strlen(buffer) == 0 && default_value) {
|
||||
return strdup(default_value);
|
||||
}
|
||||
|
||||
return strdup(buffer);
|
||||
}
|
||||
|
||||
// Parse comma-separated list into cJSON array
|
||||
cJSON* parse_comma_list(const char* input, int is_number) {
|
||||
if (!input || strlen(input) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON* array = cJSON_CreateArray();
|
||||
if (!array) return NULL;
|
||||
|
||||
char* input_copy = strdup(input);
|
||||
char* token = strtok(input_copy, ",");
|
||||
|
||||
while (token) {
|
||||
// Trim whitespace
|
||||
while (*token == ' ') token++;
|
||||
char* end = token + strlen(token) - 1;
|
||||
while (end > token && *end == ' ') *end-- = '\0';
|
||||
|
||||
if (is_number) {
|
||||
int num = atoi(token);
|
||||
cJSON_AddItemToArray(array, cJSON_CreateNumber(num));
|
||||
} else {
|
||||
cJSON_AddItemToArray(array, cJSON_CreateString(token));
|
||||
}
|
||||
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
free(input_copy);
|
||||
return array;
|
||||
}
|
||||
|
||||
// Add subscription interactively
|
||||
void add_subscription() {
|
||||
if (!pool) {
|
||||
printf("❌ Pool not started\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\n--- Add Subscription ---\n");
|
||||
printf("Enter filter values (press Enter for no value):\n");
|
||||
|
||||
cJSON* filter = cJSON_CreateObject();
|
||||
|
||||
// ids
|
||||
char* ids_input = get_input("ids (comma-separated event ids)", NULL);
|
||||
if (ids_input && strlen(ids_input) > 0) {
|
||||
cJSON* ids = parse_comma_list(ids_input, 0);
|
||||
if (ids) cJSON_AddItemToObject(filter, "ids", ids);
|
||||
}
|
||||
free(ids_input);
|
||||
|
||||
// authors
|
||||
char* authors_input = get_input("authors (comma-separated pubkeys)", NULL);
|
||||
if (authors_input && strlen(authors_input) > 0) {
|
||||
cJSON* authors = parse_comma_list(authors_input, 0);
|
||||
if (authors) cJSON_AddItemToObject(filter, "authors", authors);
|
||||
}
|
||||
free(authors_input);
|
||||
|
||||
// kinds
|
||||
char* kinds_input = get_input("kinds (comma-separated numbers)", NULL);
|
||||
if (kinds_input && strlen(kinds_input) > 0) {
|
||||
cJSON* kinds = parse_comma_list(kinds_input, 1);
|
||||
if (kinds) cJSON_AddItemToObject(filter, "kinds", kinds);
|
||||
}
|
||||
free(kinds_input);
|
||||
|
||||
// #e tag
|
||||
char* e_input = get_input("#e (comma-separated event ids)", NULL);
|
||||
if (e_input && strlen(e_input) > 0) {
|
||||
cJSON* e_array = parse_comma_list(e_input, 0);
|
||||
if (e_array) cJSON_AddItemToObject(filter, "#e", e_array);
|
||||
}
|
||||
free(e_input);
|
||||
|
||||
// #p tag
|
||||
char* p_input = get_input("#p (comma-separated pubkeys)", NULL);
|
||||
if (p_input && strlen(p_input) > 0) {
|
||||
cJSON* p_array = parse_comma_list(p_input, 0);
|
||||
if (p_array) cJSON_AddItemToObject(filter, "#p", p_array);
|
||||
}
|
||||
free(p_input);
|
||||
|
||||
// since
|
||||
char* since_input = get_input("since (unix timestamp or 'n' for now)", NULL);
|
||||
if (since_input && strlen(since_input) > 0) {
|
||||
if (strcmp(since_input, "n") == 0) {
|
||||
// Use current timestamp
|
||||
time_t now = time(NULL);
|
||||
cJSON_AddItemToObject(filter, "since", cJSON_CreateNumber((int)now));
|
||||
printf("Using current timestamp: %ld\n", now);
|
||||
} else {
|
||||
int since = atoi(since_input);
|
||||
if (since > 0) cJSON_AddItemToObject(filter, "since", cJSON_CreateNumber(since));
|
||||
}
|
||||
}
|
||||
free(since_input);
|
||||
|
||||
// until
|
||||
char* until_input = get_input("until (unix timestamp)", NULL);
|
||||
if (until_input && strlen(until_input) > 0) {
|
||||
int until = atoi(until_input);
|
||||
if (until > 0) cJSON_AddItemToObject(filter, "until", cJSON_CreateNumber(until));
|
||||
}
|
||||
free(until_input);
|
||||
|
||||
// limit
|
||||
char* limit_input = get_input("limit (max events)", "10");
|
||||
if (limit_input && strlen(limit_input) > 0) {
|
||||
int limit = atoi(limit_input);
|
||||
if (limit > 0) cJSON_AddItemToObject(filter, "limit", cJSON_CreateNumber(limit));
|
||||
}
|
||||
free(limit_input);
|
||||
|
||||
// Get relay URLs from pool
|
||||
char** relay_urls = NULL;
|
||||
nostr_pool_relay_status_t* statuses = NULL;
|
||||
int relay_count = nostr_relay_pool_list_relays(pool, &relay_urls, &statuses);
|
||||
|
||||
if (relay_count <= 0) {
|
||||
printf("❌ No relays in pool\n");
|
||||
cJSON_Delete(filter);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ask about close_on_eose behavior
|
||||
char* close_input = get_input("Close subscription on EOSE? (y/n)", "n");
|
||||
int close_on_eose = (close_input && strcmp(close_input, "y") == 0) ? 1 : 0;
|
||||
free(close_input);
|
||||
|
||||
// Create subscription
|
||||
nostr_pool_subscription_t* sub = nostr_relay_pool_subscribe(
|
||||
pool,
|
||||
(const char**)relay_urls,
|
||||
relay_count,
|
||||
filter,
|
||||
on_event,
|
||||
on_eose,
|
||||
NULL,
|
||||
close_on_eose
|
||||
);
|
||||
|
||||
// Free relay URLs
|
||||
for (int i = 0; i < relay_count; i++) {
|
||||
free(relay_urls[i]);
|
||||
}
|
||||
free(relay_urls);
|
||||
free(statuses);
|
||||
|
||||
if (!sub) {
|
||||
printf("❌ Failed to create subscription\n");
|
||||
cJSON_Delete(filter);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store subscription
|
||||
if (subscription_count >= subscription_capacity) {
|
||||
subscription_capacity = subscription_capacity == 0 ? 10 : subscription_capacity * 2;
|
||||
subscriptions = realloc(subscriptions, subscription_capacity * sizeof(nostr_pool_subscription_t*));
|
||||
}
|
||||
subscriptions[subscription_count++] = sub;
|
||||
|
||||
printf("✅ Subscription created (ID: %d)\n", subscription_count);
|
||||
|
||||
// Log the filter
|
||||
char* filter_json = cJSON_Print(filter);
|
||||
time_t now = time(NULL);
|
||||
char timestamp[26];
|
||||
ctime_r(&now, timestamp);
|
||||
timestamp[24] = '\0';
|
||||
dprintf(log_fd, "[%s] 🔍 New subscription created (ID: %d)\n", timestamp, subscription_count);
|
||||
dprintf(log_fd, "Filter: %s\n\n", filter_json);
|
||||
free(filter_json);
|
||||
}
|
||||
|
||||
// Remove subscription
|
||||
void remove_subscription() {
|
||||
if (subscription_count == 0) {
|
||||
printf("❌ No subscriptions to remove\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\n--- Remove Subscription ---\n");
|
||||
printf("Available subscriptions:\n");
|
||||
for (int i = 0; i < subscription_count; i++) {
|
||||
printf("%d. Subscription %d\n", i + 1, i + 1);
|
||||
}
|
||||
|
||||
char* choice_input = get_input("Enter subscription number to remove", NULL);
|
||||
if (!choice_input || strlen(choice_input) == 0) {
|
||||
free(choice_input);
|
||||
return;
|
||||
}
|
||||
|
||||
int choice = atoi(choice_input) - 1;
|
||||
free(choice_input);
|
||||
|
||||
if (choice < 0 || choice >= subscription_count) {
|
||||
printf("❌ Invalid subscription number\n");
|
||||
return;
|
||||
}
|
||||
|
||||
nostr_pool_subscription_close(subscriptions[choice]);
|
||||
|
||||
// Shift remaining subscriptions
|
||||
for (int i = choice; i < subscription_count - 1; i++) {
|
||||
subscriptions[i] = subscriptions[i + 1];
|
||||
}
|
||||
subscription_count--;
|
||||
|
||||
printf("✅ Subscription removed\n");
|
||||
|
||||
time_t now = time(NULL);
|
||||
char timestamp[26];
|
||||
ctime_r(&now, timestamp);
|
||||
timestamp[24] = '\0';
|
||||
dprintf(log_fd, "[%s] 🗑️ Subscription removed (was ID: %d)\n\n", timestamp, choice + 1);
|
||||
}
|
||||
|
||||
// Show pool status
|
||||
void show_pool_status() {
|
||||
if (!pool) {
|
||||
printf("❌ Pool not started\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Give polling thread time to establish connections
|
||||
printf("⏳ Waiting for connections to establish...\n");
|
||||
sleep(3);
|
||||
|
||||
char** relay_urls = NULL;
|
||||
nostr_pool_relay_status_t* statuses = NULL;
|
||||
int relay_count = nostr_relay_pool_list_relays(pool, &relay_urls, &statuses);
|
||||
|
||||
printf("\n📊 POOL STATUS\n");
|
||||
printf("Relays: %d\n", relay_count);
|
||||
printf("Subscriptions: %d\n", subscription_count);
|
||||
|
||||
if (relay_count > 0) {
|
||||
printf("\nRelay Details:\n");
|
||||
for (int i = 0; i < relay_count; i++) {
|
||||
const char* status_str;
|
||||
switch (statuses[i]) {
|
||||
case NOSTR_POOL_RELAY_CONNECTED: status_str = "🟢 CONNECTED"; break;
|
||||
case NOSTR_POOL_RELAY_CONNECTING: status_str = "🟡 CONNECTING"; break;
|
||||
case NOSTR_POOL_RELAY_DISCONNECTED: status_str = "⚪ DISCONNECTED"; break;
|
||||
case NOSTR_POOL_RELAY_ERROR: status_str = "🔴 ERROR"; break;
|
||||
default: status_str = "❓ UNKNOWN"; break;
|
||||
}
|
||||
|
||||
printf("├── %s: %s\n", relay_urls[i], status_str);
|
||||
|
||||
const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(pool, relay_urls[i]);
|
||||
if (stats) {
|
||||
printf("│ ├── Events received: %d\n", stats->events_received);
|
||||
printf("│ ├── Connection attempts: %d\n", stats->connection_attempts);
|
||||
printf("│ ├── Connection failures: %d\n", stats->connection_failures);
|
||||
printf("│ ├── Ping latency: %.2f ms\n", stats->ping_latency_current);
|
||||
printf("│ └── Query latency: %.2f ms\n", stats->query_latency_avg);
|
||||
}
|
||||
|
||||
free(relay_urls[i]);
|
||||
}
|
||||
free(relay_urls);
|
||||
free(statuses);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Setup logging to file
|
||||
log_fd = open("pool.log", O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (log_fd == -1) {
|
||||
fprintf(stderr, "❌ Failed to open pool.log for writing\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize NOSTR library
|
||||
if (nostr_init() != NOSTR_SUCCESS) {
|
||||
fprintf(stderr, "❌ Failed to initialize NOSTR library\n");
|
||||
close(log_fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Setup signal handler
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
// Start polling thread
|
||||
if (pthread_create(&poll_thread, NULL, poll_thread_func, NULL) != 0) {
|
||||
fprintf(stderr, "❌ Failed to create polling thread\n");
|
||||
nostr_cleanup();
|
||||
close(log_fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("🔗 NOSTR Relay Pool Interactive Test\n");
|
||||
printf("=====================================\n");
|
||||
printf("All event output is logged to pool.log\n");
|
||||
printf("Press Ctrl+C to exit\n\n");
|
||||
|
||||
time_t now = time(NULL);
|
||||
char timestamp[26];
|
||||
ctime_r(&now, timestamp);
|
||||
timestamp[24] = '\0';
|
||||
dprintf(log_fd, "[%s] 🚀 Pool test started\n\n", timestamp);
|
||||
|
||||
// Main menu loop
|
||||
while (running) {
|
||||
print_menu();
|
||||
|
||||
char choice;
|
||||
if (scanf("%c", &choice) != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Consume newline
|
||||
int c;
|
||||
while ((c = getchar()) != '\n' && c != EOF);
|
||||
|
||||
switch (choice) {
|
||||
case '1': { // Start Pool
|
||||
if (pool) {
|
||||
printf("❌ Pool already started\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// Create pool with custom reconnection configuration for faster testing
|
||||
nostr_pool_reconnect_config_t* config = nostr_pool_reconnect_config_default();
|
||||
config->ping_interval_seconds = 5; // Ping every 5 seconds for testing
|
||||
pool = nostr_relay_pool_create(config);
|
||||
if (!pool) {
|
||||
printf("❌ Failed to create pool\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (nostr_relay_pool_add_relay(pool, "wss://relay.laantungir.net") != NOSTR_SUCCESS) {
|
||||
printf("❌ Failed to add default relay\n");
|
||||
nostr_relay_pool_destroy(pool);
|
||||
pool = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
printf("✅ Pool started with wss://relay.laantungir.net\n");
|
||||
|
||||
now = time(NULL);
|
||||
ctime_r(&now, timestamp);
|
||||
timestamp[24] = '\0';
|
||||
dprintf(log_fd, "[%s] 🏊 Pool started with default relay\n\n", timestamp);
|
||||
break;
|
||||
}
|
||||
|
||||
case '2': { // Stop Pool
|
||||
if (!pool) {
|
||||
printf("❌ Pool not started\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// Close all subscriptions
|
||||
for (int i = 0; i < subscription_count; i++) {
|
||||
if (subscriptions[i]) {
|
||||
nostr_pool_subscription_close(subscriptions[i]);
|
||||
}
|
||||
}
|
||||
free(subscriptions);
|
||||
subscriptions = NULL;
|
||||
subscription_count = 0;
|
||||
subscription_capacity = 0;
|
||||
|
||||
nostr_relay_pool_destroy(pool);
|
||||
pool = NULL;
|
||||
|
||||
printf("✅ Pool stopped\n");
|
||||
|
||||
now = time(NULL);
|
||||
ctime_r(&now, timestamp);
|
||||
timestamp[24] = '\0';
|
||||
dprintf(log_fd, "[%s] 🛑 Pool stopped\n\n", timestamp);
|
||||
break;
|
||||
}
|
||||
|
||||
case '3': { // Add relay
|
||||
if (!pool) {
|
||||
printf("❌ Pool not started\n");
|
||||
break;
|
||||
}
|
||||
|
||||
char* url = get_input("Enter relay URL", "wss://relay.example.com");
|
||||
if (url && strlen(url) > 0) {
|
||||
if (nostr_relay_pool_add_relay(pool, url) == NOSTR_SUCCESS) {
|
||||
printf("✅ Relay added: %s\n", url);
|
||||
|
||||
now = time(NULL);
|
||||
ctime_r(&now, timestamp);
|
||||
timestamp[24] = '\0';
|
||||
dprintf(log_fd, "[%s] ➕ Relay added: %s\n\n", timestamp, url);
|
||||
} else {
|
||||
printf("❌ Failed to add relay\n");
|
||||
}
|
||||
}
|
||||
free(url);
|
||||
break;
|
||||
}
|
||||
|
||||
case '4': { // Remove relay
|
||||
if (!pool) {
|
||||
printf("❌ Pool not started\n");
|
||||
break;
|
||||
}
|
||||
|
||||
char* url = get_input("Enter relay URL to remove", NULL);
|
||||
if (url && strlen(url) > 0) {
|
||||
if (nostr_relay_pool_remove_relay(pool, url) == NOSTR_SUCCESS) {
|
||||
printf("✅ Relay removed: %s\n", url);
|
||||
|
||||
now = time(NULL);
|
||||
ctime_r(&now, timestamp);
|
||||
timestamp[24] = '\0';
|
||||
dprintf(log_fd, "[%s] ➖ Relay removed: %s\n\n", timestamp, url);
|
||||
} else {
|
||||
printf("❌ Failed to remove relay\n");
|
||||
}
|
||||
}
|
||||
free(url);
|
||||
break;
|
||||
}
|
||||
|
||||
case '5': // Add subscription
|
||||
add_subscription();
|
||||
break;
|
||||
|
||||
case '6': // Remove subscription
|
||||
remove_subscription();
|
||||
break;
|
||||
|
||||
case '7': // Show status
|
||||
show_pool_status();
|
||||
break;
|
||||
|
||||
case '8': { // Test reconnection
|
||||
if (!pool) {
|
||||
printf("❌ Pool not started\n");
|
||||
break;
|
||||
}
|
||||
|
||||
char** relay_urls = NULL;
|
||||
nostr_pool_relay_status_t* statuses = NULL;
|
||||
int relay_count = nostr_relay_pool_list_relays(pool, &relay_urls, &statuses);
|
||||
|
||||
if (relay_count <= 0) {
|
||||
printf("❌ No relays in pool\n");
|
||||
break;
|
||||
}
|
||||
|
||||
printf("\n--- Test Reconnection ---\n");
|
||||
printf("Available relays:\n");
|
||||
for (int i = 0; i < relay_count; i++) {
|
||||
printf("%d. %s (%s)\n", i + 1, relay_urls[i],
|
||||
statuses[i] == NOSTR_POOL_RELAY_CONNECTED ? "CONNECTED" : "NOT CONNECTED");
|
||||
}
|
||||
|
||||
char* choice_input = get_input("Enter relay number to test reconnection with", NULL);
|
||||
if (!choice_input || strlen(choice_input) == 0) {
|
||||
for (int i = 0; i < relay_count; i++) free(relay_urls[i]);
|
||||
free(relay_urls);
|
||||
free(statuses);
|
||||
free(choice_input);
|
||||
break;
|
||||
}
|
||||
|
||||
int choice = atoi(choice_input) - 1;
|
||||
free(choice_input);
|
||||
|
||||
if (choice < 0 || choice >= relay_count) {
|
||||
printf("❌ Invalid relay number\n");
|
||||
for (int i = 0; i < relay_count; i++) free(relay_urls[i]);
|
||||
free(relay_urls);
|
||||
free(statuses);
|
||||
break;
|
||||
}
|
||||
|
||||
printf("🔄 Testing reconnection with %s...\n", relay_urls[choice]);
|
||||
printf(" The pool is configured with automatic reconnection enabled.\n");
|
||||
printf(" If the connection drops, it will automatically attempt to reconnect\n");
|
||||
printf(" with exponential backoff (1s → 2s → 4s → 8s → 16s → 30s max).\n");
|
||||
printf(" Connection health is monitored with ping/pong every 30 seconds.\n");
|
||||
|
||||
time_t now = time(NULL);
|
||||
char timestamp[26];
|
||||
ctime_r(&now, timestamp);
|
||||
timestamp[24] = '\0';
|
||||
dprintf(log_fd, "[%s] 🔄 TEST: Testing reconnection behavior with %s\n", timestamp, relay_urls[choice]);
|
||||
dprintf(log_fd, " Pool configured with: auto-reconnect=ON, max_attempts=10, ping_interval=30s\n\n");
|
||||
|
||||
printf("✅ Reconnection test initiated. Monitor the status and logs for reconnection activity.\n");
|
||||
|
||||
for (int i = 0; i < relay_count; i++) free(relay_urls[i]);
|
||||
free(relay_urls);
|
||||
free(statuses);
|
||||
break;
|
||||
}
|
||||
|
||||
case '9': // Exit
|
||||
running = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("❌ Invalid choice\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n🧹 Cleaning up...\n");
|
||||
|
||||
// Stop polling thread
|
||||
running = 0;
|
||||
pthread_join(poll_thread, NULL);
|
||||
|
||||
// Clean up pool and subscriptions
|
||||
if (pool) {
|
||||
for (int i = 0; i < subscription_count; i++) {
|
||||
if (subscriptions[i]) {
|
||||
nostr_pool_subscription_close(subscriptions[i]);
|
||||
}
|
||||
}
|
||||
free(subscriptions);
|
||||
nostr_relay_pool_destroy(pool);
|
||||
printf("✅ Pool destroyed\n");
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
nostr_cleanup();
|
||||
close(log_fd);
|
||||
|
||||
printf("👋 Test completed\n");
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Relay Pool Test Program
|
||||
*
|
||||
* Tests the nostr_relay_pool functionality with persistent connections
|
||||
* and subscriptions. Prints events as they arrive and shows connection status.
|
||||
*
|
||||
* Usage: ./pool_test
|
||||
* Press Ctrl+C to exit
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include "../nostr_core/nostr_core.h"
|
||||
#include "../cjson/cJSON.h"
|
||||
|
||||
// Global variables for signal handling
|
||||
volatile sig_atomic_t running = 1;
|
||||
time_t last_status_time = 0;
|
||||
|
||||
// Signal handler for clean shutdown
|
||||
void signal_handler(int signum) {
|
||||
(void)signum; // Suppress unused parameter warning
|
||||
printf("\n🛑 Received signal, shutting down...\n");
|
||||
running = 0;
|
||||
}
|
||||
|
||||
// Event callback - called when an event is received
|
||||
void on_event(cJSON* event, const char* relay_url, void* user_data) {
|
||||
(void)user_data; // Suppress unused parameter warning
|
||||
|
||||
// Extract basic event information
|
||||
cJSON* id = cJSON_GetObjectItem(event, "id");
|
||||
cJSON* pubkey = cJSON_GetObjectItem(event, "pubkey");
|
||||
cJSON* created_at = cJSON_GetObjectItem(event, "created_at");
|
||||
cJSON* kind = cJSON_GetObjectItem(event, "kind");
|
||||
cJSON* content = cJSON_GetObjectItem(event, "content");
|
||||
|
||||
printf("\n📨 EVENT from %s\n", relay_url);
|
||||
printf("├── ID: %.12s...\n", id && cJSON_IsString(id) ? cJSON_GetStringValue(id) : "unknown");
|
||||
printf("├── Pubkey: %.12s...\n", pubkey && cJSON_IsString(pubkey) ? cJSON_GetStringValue(pubkey) : "unknown");
|
||||
printf("├── Kind: %d\n", kind && cJSON_IsNumber(kind) ? (int)cJSON_GetNumberValue(kind) : -1);
|
||||
printf("├── Created: %lld\n", created_at && cJSON_IsNumber(created_at) ? (long long)cJSON_GetNumberValue(created_at) : 0);
|
||||
|
||||
// Truncate content if too long
|
||||
if (content && cJSON_IsString(content)) {
|
||||
const char* content_str = cJSON_GetStringValue(content);
|
||||
size_t content_len = strlen(content_str);
|
||||
if (content_len > 100) {
|
||||
printf("└── Content: %.97s...\n", content_str);
|
||||
} else {
|
||||
printf("└── Content: %s\n", content_str);
|
||||
}
|
||||
} else {
|
||||
printf("└── Content: (empty)\n");
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
// EOSE callback - called when End of Stored Events is received
|
||||
void on_eose(void* user_data) {
|
||||
(void)user_data; // Suppress unused parameter warning
|
||||
printf("📋 EOSE received - all stored events delivered\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
// Print connection status for all relays
|
||||
void print_relay_status(nostr_relay_pool_t* pool) {
|
||||
char** relay_urls = NULL;
|
||||
nostr_pool_relay_status_t* statuses = NULL;
|
||||
int relay_count = nostr_relay_pool_list_relays(pool, &relay_urls, &statuses);
|
||||
|
||||
if (relay_count <= 0) {
|
||||
printf("❌ No relays in pool\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\n📊 RELAY STATUS (%d relays):\n", relay_count);
|
||||
for (int i = 0; i < relay_count; i++) {
|
||||
const char* status_str;
|
||||
switch (statuses[i]) {
|
||||
case NOSTR_POOL_RELAY_CONNECTED:
|
||||
status_str = "🟢 CONNECTED";
|
||||
break;
|
||||
case NOSTR_POOL_RELAY_CONNECTING:
|
||||
status_str = "🟡 CONNECTING";
|
||||
break;
|
||||
case NOSTR_POOL_RELAY_DISCONNECTED:
|
||||
status_str = "⚪ DISCONNECTED";
|
||||
break;
|
||||
case NOSTR_POOL_RELAY_ERROR:
|
||||
status_str = "🔴 ERROR";
|
||||
break;
|
||||
default:
|
||||
status_str = "❓ UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
printf("├── %s: %s\n", relay_urls[i], status_str);
|
||||
|
||||
// Show additional stats if available
|
||||
const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(pool, relay_urls[i]);
|
||||
if (stats) {
|
||||
printf("│ ├── Events received: %d\n", stats->events_received);
|
||||
printf("│ ├── Connection attempts: %d\n", stats->connection_attempts);
|
||||
printf("│ └── Connection failures: %d\n", stats->connection_failures);
|
||||
}
|
||||
|
||||
free(relay_urls[i]);
|
||||
}
|
||||
printf("\n");
|
||||
free(relay_urls);
|
||||
free(statuses);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("🔗 NOSTR Relay Pool Test\n");
|
||||
printf("========================\n");
|
||||
printf("Testing persistent relay connections with subscriptions.\n");
|
||||
printf("Press Ctrl+C to exit.\n\n");
|
||||
|
||||
// Initialize NOSTR library
|
||||
if (nostr_init() != NOSTR_SUCCESS) {
|
||||
fprintf(stderr, "❌ Failed to initialize NOSTR library\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Setup signal handler for clean shutdown
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
// Create relay pool with default configuration
|
||||
nostr_pool_reconnect_config_t* config = nostr_pool_reconnect_config_default();
|
||||
nostr_relay_pool_t* pool = nostr_relay_pool_create(config);
|
||||
if (!pool) {
|
||||
fprintf(stderr, "❌ Failed to create relay pool\n");
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Add relays to the pool
|
||||
const char* relay_urls[] = {
|
||||
"wss://nostr.mom",
|
||||
"wss://relay.laantungir.net",
|
||||
"wss://nos.lol"
|
||||
};
|
||||
int relay_count = 3;
|
||||
|
||||
printf("📡 Adding %d relays to pool:\n", relay_count);
|
||||
for (int i = 0; i < relay_count; i++) {
|
||||
printf("├── %s\n", relay_urls[i]);
|
||||
if (nostr_relay_pool_add_relay(pool, relay_urls[i]) != NOSTR_SUCCESS) {
|
||||
printf("│ ❌ Failed to add relay\n");
|
||||
} else {
|
||||
printf("│ ✅ Added successfully\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Create filter for subscription (kind 1 events - text notes)
|
||||
cJSON* filter = cJSON_CreateObject();
|
||||
cJSON* kinds = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(1));
|
||||
cJSON_AddItemToObject(filter, "kinds", kinds);
|
||||
cJSON_AddItemToObject(filter, "limit", cJSON_CreateNumber(10)); // Limit to 10 events per relay
|
||||
|
||||
printf("🔍 Creating subscription with filter:\n");
|
||||
char* filter_json = cJSON_Print(filter);
|
||||
printf("%s\n\n", filter_json);
|
||||
free(filter_json);
|
||||
|
||||
// Create subscription
|
||||
nostr_pool_subscription_t* subscription = nostr_relay_pool_subscribe(
|
||||
pool,
|
||||
relay_urls,
|
||||
relay_count,
|
||||
filter,
|
||||
on_event, // Event callback
|
||||
on_eose, // EOSE callback
|
||||
NULL, // User data (not used)
|
||||
0 // close_on_eose (false - keep subscription open)
|
||||
);
|
||||
|
||||
if (!subscription) {
|
||||
fprintf(stderr, "❌ Failed to create subscription\n");
|
||||
cJSON_Delete(filter);
|
||||
nostr_relay_pool_destroy(pool);
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("✅ Subscription created successfully\n");
|
||||
printf("🎯 Listening for events... (Ctrl+C to exit)\n\n");
|
||||
|
||||
// Record start time for status updates
|
||||
last_status_time = time(NULL);
|
||||
|
||||
// Main event loop
|
||||
while (running) {
|
||||
// Poll for events (100ms timeout)
|
||||
int events_processed = nostr_relay_pool_poll(pool, 100);
|
||||
|
||||
// Check if we should print status (every 30 seconds)
|
||||
time_t current_time = time(NULL);
|
||||
if (current_time - last_status_time >= 30) {
|
||||
print_relay_status(pool);
|
||||
last_status_time = current_time;
|
||||
}
|
||||
|
||||
// Small delay to prevent busy waiting
|
||||
if (events_processed == 0) {
|
||||
struct timespec ts = {0, 10000000}; // 10ms
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n🧹 Cleaning up...\n");
|
||||
|
||||
// Close subscription
|
||||
if (subscription) {
|
||||
nostr_pool_subscription_close(subscription);
|
||||
printf("✅ Subscription closed\n");
|
||||
}
|
||||
|
||||
// Destroy pool
|
||||
nostr_relay_pool_destroy(pool);
|
||||
printf("✅ Relay pool destroyed\n");
|
||||
|
||||
// Cleanup JSON
|
||||
cJSON_Delete(filter);
|
||||
|
||||
// Cleanup library
|
||||
nostr_cleanup();
|
||||
|
||||
printf("👋 Test completed successfully\n");
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -10,7 +10,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include "../nostr_core/nostr_common.h"
|
||||
#include "../nostr_core/nostr_core.h"
|
||||
#include "../cjson/cJSON.h"
|
||||
|
||||
|
||||
|
@ -80,7 +80,7 @@ int main() {
|
|||
const char* filter_json =
|
||||
"{"
|
||||
" \"kinds\": [1],"
|
||||
" \"limit\": 1"
|
||||
" \"limit\": 4"
|
||||
"}";
|
||||
|
||||
// Alternative filter examples (comment out the one above, uncomment one below):
|
||||
|
@ -133,7 +133,8 @@ int main() {
|
|||
|
||||
cJSON** results = synchronous_query_relays_with_progress(
|
||||
test_relays, relay_count, filter, test_mode,
|
||||
&result_count, 5, progress_callback, NULL
|
||||
&result_count, 5, progress_callback, NULL,
|
||||
1, NULL // nip42_enabled = true, private_key = NULL (no auth)
|
||||
);
|
||||
|
||||
time_t end_time = time(NULL);
|
||||
|
|
BIN
tests/wss_test
BIN
tests/wss_test
Binary file not shown.
103
tests/wss_test.c
103
tests/wss_test.c
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* WebSocket SSL Test - Test OpenSSL WebSocket implementation
|
||||
* Connect to a NOSTR relay and fetch one type 1 event
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../cjson/cJSON.h"
|
||||
#include "../nostr_core/nostr_common.h"
|
||||
|
||||
// Progress callback to show connection status
|
||||
static void progress_callback(
|
||||
const char* relay_url,
|
||||
const char* status,
|
||||
const char* event_id,
|
||||
int events_received,
|
||||
int total_relays,
|
||||
int completed_relays,
|
||||
void* user_data)
|
||||
{
|
||||
printf("Progress: %s - %s", relay_url ? relay_url : "Summary", status);
|
||||
if (event_id) {
|
||||
printf(" (Event: %.12s...)", event_id);
|
||||
}
|
||||
printf(" [%d/%d events, %d/%d relays]\n",
|
||||
events_received, *(int*)user_data, completed_relays, total_relays);
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("WebSocket SSL Test - Testing OpenSSL WebSocket with NOSTR relay\n");
|
||||
printf("================================================================\n");
|
||||
|
||||
// Initialize NOSTR library
|
||||
if (nostr_init() != NOSTR_SUCCESS) {
|
||||
printf("❌ Failed to initialize NOSTR library\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("✅ NOSTR library initialized\n");
|
||||
|
||||
// Setup relay and filter
|
||||
const char* relay_urls[] = {"wss://nostr.mom"};
|
||||
int relay_count = 1;
|
||||
|
||||
// Create filter for type 1 events (text notes), limit to 1 event
|
||||
cJSON* filter = cJSON_CreateObject();
|
||||
cJSON* kinds = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(1));
|
||||
cJSON_AddItemToObject(filter, "kinds", kinds);
|
||||
cJSON_AddItemToObject(filter, "limit", cJSON_CreateNumber(1));
|
||||
|
||||
printf("📡 Connecting to %s...\n", relay_urls[0]);
|
||||
printf("🔍 Requesting 1 type 1 event (text note)...\n\n");
|
||||
|
||||
// Query the relay
|
||||
int result_count = 0;
|
||||
int expected_events = 1;
|
||||
cJSON** events = synchronous_query_relays_with_progress(
|
||||
relay_urls,
|
||||
relay_count,
|
||||
filter,
|
||||
RELAY_QUERY_FIRST_RESULT, // Return as soon as we get the first event
|
||||
&result_count,
|
||||
10, // 10 second timeout
|
||||
progress_callback,
|
||||
&expected_events
|
||||
);
|
||||
|
||||
// Process results
|
||||
if (events && result_count > 0) {
|
||||
printf("\n✅ Successfully received %d event(s)!\n", result_count);
|
||||
printf("📄 Raw JSON event data:\n");
|
||||
printf("========================\n");
|
||||
|
||||
for (int i = 0; i < result_count; i++) {
|
||||
char* json_string = cJSON_Print(events[i]);
|
||||
if (json_string) {
|
||||
printf("%s\n\n", json_string);
|
||||
free(json_string);
|
||||
}
|
||||
cJSON_Delete(events[i]);
|
||||
}
|
||||
free(events);
|
||||
|
||||
printf("🎉 WebSocket SSL Test PASSED - OpenSSL WebSocket working correctly!\n");
|
||||
} else {
|
||||
printf("\n❌ No events received or query failed\n");
|
||||
printf("❌ WebSocket SSL Test FAILED\n");
|
||||
|
||||
// Cleanup and return error
|
||||
cJSON_Delete(filter);
|
||||
nostr_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
cJSON_Delete(filter);
|
||||
nostr_cleanup();
|
||||
|
||||
printf("✅ WebSocket connection and TLS working with OpenSSL\n");
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue