v0.0.2 - Got it basically working
This commit is contained in:
186
main.c
186
main.c
@@ -29,7 +29,7 @@
|
|||||||
#include "cJSON.h"
|
#include "cJSON.h"
|
||||||
|
|
||||||
// Version
|
// Version
|
||||||
#define THROWER_VERSION "v0.0.1"
|
#define THROWER_VERSION "v0.0.2"
|
||||||
|
|
||||||
// Configuration constants
|
// Configuration constants
|
||||||
#define MAX_RELAYS 50
|
#define MAX_RELAYS 50
|
||||||
@@ -177,6 +177,8 @@ static void free_padding_payload(padding_payload_t* payload);
|
|||||||
|
|
||||||
// Thrower info functions
|
// Thrower info functions
|
||||||
static int publish_thrower_info(superball_thrower_t* thrower);
|
static int publish_thrower_info(superball_thrower_t* thrower);
|
||||||
|
static int publish_metadata(superball_thrower_t* thrower);
|
||||||
|
static int publish_relay_list(superball_thrower_t* thrower);
|
||||||
static void* auto_publish_thread_func(void* arg);
|
static void* auto_publish_thread_func(void* arg);
|
||||||
|
|
||||||
// Main functions
|
// Main functions
|
||||||
@@ -793,6 +795,13 @@ static void forward_to_next_thrower(superball_thrower_t* thrower, cJSON* event,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log the event JSON being published
|
||||||
|
char* event_json = cJSON_Print(signed_event);
|
||||||
|
if (event_json) {
|
||||||
|
log_message(LOG_INFO, "Publishing routing event JSON:\n%s", event_json);
|
||||||
|
free(event_json);
|
||||||
|
}
|
||||||
|
|
||||||
// Publish to relays
|
// Publish to relays
|
||||||
nostr_relay_pool_publish_async(thrower->pool, (const char**)routing->relays,
|
nostr_relay_pool_publish_async(thrower->pool, (const char**)routing->relays,
|
||||||
routing->relay_count, signed_event,
|
routing->relay_count, signed_event,
|
||||||
@@ -803,14 +812,29 @@ static void forward_to_next_thrower(superball_thrower_t* thrower, cJSON* event,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void post_final_event(superball_thrower_t* thrower, cJSON* event, routing_payload_t* routing) {
|
static void post_final_event(superball_thrower_t* thrower, cJSON* event, routing_payload_t* routing) {
|
||||||
|
(void)event; // The wrapped event is not used - we publish the inner event from routing
|
||||||
|
|
||||||
log_message(LOG_INFO, "Posting final event to %d relays", routing->relay_count);
|
log_message(LOG_INFO, "Posting final event to %d relays", routing->relay_count);
|
||||||
|
|
||||||
// Publish the inner event directly
|
// The inner event is in routing->event (this is the kind 1 note, not the kind 22222 wrapper)
|
||||||
|
if (!routing->event) {
|
||||||
|
log_message(LOG_ERROR, "No inner event to publish");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the inner event JSON being published
|
||||||
|
char* event_json = cJSON_Print(routing->event);
|
||||||
|
if (event_json) {
|
||||||
|
log_message(LOG_INFO, "Publishing final event JSON:\n%s", event_json);
|
||||||
|
free(event_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish the inner event directly (this is the actual kind 1 note)
|
||||||
nostr_relay_pool_publish_async(thrower->pool, (const char**)routing->relays,
|
nostr_relay_pool_publish_async(thrower->pool, (const char**)routing->relays,
|
||||||
routing->relay_count, event,
|
routing->relay_count, routing->event,
|
||||||
publish_callback, thrower);
|
publish_callback, thrower);
|
||||||
|
|
||||||
cJSON* id = cJSON_GetObjectItem(event, "id");
|
cJSON* id = cJSON_GetObjectItem(routing->event, "id");
|
||||||
if (id) {
|
if (id) {
|
||||||
log_message(LOG_INFO, "Final event posted: %.16s...", cJSON_GetStringValue(id));
|
log_message(LOG_INFO, "Final event posted: %.16s...", cJSON_GetStringValue(id));
|
||||||
}
|
}
|
||||||
@@ -818,12 +842,23 @@ static void post_final_event(superball_thrower_t* thrower, cJSON* event, routing
|
|||||||
|
|
||||||
static void publish_callback(const char* relay_url, const char* event_id, int success,
|
static void publish_callback(const char* relay_url, const char* event_id, int success,
|
||||||
const char* message, void* user_data) {
|
const char* message, void* user_data) {
|
||||||
(void)user_data; // Suppress unused parameter warning
|
superball_thrower_t* thrower = (superball_thrower_t*)user_data;
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
log_message(LOG_INFO, "✅ Published to %s: %.16s...", relay_url, event_id);
|
log_message(LOG_INFO, "✅ Published to %s: %.16s...", relay_url, event_id);
|
||||||
|
|
||||||
|
// Print relay response if available
|
||||||
|
if (message) {
|
||||||
|
log_message(LOG_DEBUG, "Relay response: %s", message);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log_message(LOG_ERROR, "❌ Failed to publish to %s: %s", relay_url, message ? message : "unknown error");
|
log_message(LOG_ERROR, "❌ Failed to publish to %s: %s", relay_url, message ? message : "unknown error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: We don't have access to the full event JSON here in the callback
|
||||||
|
// The event was already published. To see the full event, we'd need to
|
||||||
|
// log it before calling nostr_relay_pool_publish_async
|
||||||
|
(void)thrower; // Suppress unused warning for now
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_routing_payload(routing_payload_t* payload) {
|
static void free_routing_payload(routing_payload_t* payload) {
|
||||||
@@ -940,6 +975,143 @@ static int publish_thrower_info(superball_thrower_t* thrower) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int publish_metadata(superball_thrower_t* thrower) {
|
||||||
|
log_message(LOG_INFO, "Publishing metadata (kind 0)...");
|
||||||
|
|
||||||
|
// Create metadata JSON content
|
||||||
|
cJSON* metadata = cJSON_CreateObject();
|
||||||
|
|
||||||
|
if (thrower->config->name) {
|
||||||
|
cJSON_AddStringToObject(metadata, "name", thrower->config->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thrower->config->description) {
|
||||||
|
cJSON_AddStringToObject(metadata, "about", thrower->config->description);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Superball-specific fields
|
||||||
|
if (thrower->config->software) {
|
||||||
|
cJSON_AddStringToObject(metadata, "nip05", thrower->config->software);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add version and supported SUPs
|
||||||
|
if (thrower->config->version) {
|
||||||
|
cJSON_AddStringToObject(metadata, "display_name",
|
||||||
|
thrower->config->version);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thrower->config->supported_sups) {
|
||||||
|
cJSON_AddStringToObject(metadata, "website",
|
||||||
|
thrower->config->supported_sups);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* content = cJSON_PrintUnformatted(metadata);
|
||||||
|
cJSON_Delete(metadata);
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
log_message(LOG_ERROR, "Failed to create metadata JSON");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create kind 0 event with empty tags
|
||||||
|
cJSON* tags = cJSON_CreateArray();
|
||||||
|
cJSON* event = nostr_create_and_sign_event(0, content, tags,
|
||||||
|
thrower->private_key, time(NULL));
|
||||||
|
free(content);
|
||||||
|
cJSON_Delete(tags);
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
log_message(LOG_ERROR, "Failed to create metadata event");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get write-capable relays
|
||||||
|
const char** relay_urls = malloc(thrower->config->relay_count * sizeof(char*));
|
||||||
|
int relay_count = 0;
|
||||||
|
for (int i = 0; i < thrower->config->relay_count; i++) {
|
||||||
|
if (thrower->config->relays[i].write &&
|
||||||
|
strcmp(thrower->config->relays[i].auth_status, "no-auth") == 0) {
|
||||||
|
relay_urls[relay_count++] = thrower->config->relays[i].url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relay_count == 0) {
|
||||||
|
log_message(LOG_WARN, "No write-capable relays for metadata");
|
||||||
|
free(relay_urls);
|
||||||
|
cJSON_Delete(event);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nostr_relay_pool_publish_async(thrower->pool, relay_urls, relay_count,
|
||||||
|
event, publish_callback, thrower);
|
||||||
|
|
||||||
|
free(relay_urls);
|
||||||
|
cJSON_Delete(event);
|
||||||
|
|
||||||
|
log_message(LOG_INFO, "Metadata published to %d relays", relay_count);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int publish_relay_list(superball_thrower_t* thrower) {
|
||||||
|
log_message(LOG_INFO, "Publishing relay list (kind 10002)...");
|
||||||
|
|
||||||
|
// Create tags array with relay information
|
||||||
|
cJSON* tags = cJSON_CreateArray();
|
||||||
|
|
||||||
|
for (int i = 0; i < thrower->config->relay_count; i++) {
|
||||||
|
cJSON* relay_tag = cJSON_CreateArray();
|
||||||
|
cJSON_AddItemToArray(relay_tag, cJSON_CreateString("r"));
|
||||||
|
cJSON_AddItemToArray(relay_tag, cJSON_CreateString(thrower->config->relays[i].url));
|
||||||
|
|
||||||
|
// Add read/write markers
|
||||||
|
if (thrower->config->relays[i].read && thrower->config->relays[i].write) {
|
||||||
|
// Both read and write - no marker needed (default)
|
||||||
|
} else if (thrower->config->relays[i].read) {
|
||||||
|
cJSON_AddItemToArray(relay_tag, cJSON_CreateString("read"));
|
||||||
|
} else if (thrower->config->relays[i].write) {
|
||||||
|
cJSON_AddItemToArray(relay_tag, cJSON_CreateString("write"));
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_AddItemToArray(tags, relay_tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create kind 10002 event with empty content
|
||||||
|
cJSON* event = nostr_create_and_sign_event(10002, "", tags,
|
||||||
|
thrower->private_key, time(NULL));
|
||||||
|
cJSON_Delete(tags);
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
log_message(LOG_ERROR, "Failed to create relay list event");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get write-capable relays
|
||||||
|
const char** relay_urls = malloc(thrower->config->relay_count * sizeof(char*));
|
||||||
|
int relay_count = 0;
|
||||||
|
for (int i = 0; i < thrower->config->relay_count; i++) {
|
||||||
|
if (thrower->config->relays[i].write &&
|
||||||
|
strcmp(thrower->config->relays[i].auth_status, "no-auth") == 0) {
|
||||||
|
relay_urls[relay_count++] = thrower->config->relays[i].url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relay_count == 0) {
|
||||||
|
log_message(LOG_WARN, "No write-capable relays for relay list");
|
||||||
|
free(relay_urls);
|
||||||
|
cJSON_Delete(event);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nostr_relay_pool_publish_async(thrower->pool, relay_urls, relay_count,
|
||||||
|
event, publish_callback, thrower);
|
||||||
|
|
||||||
|
free(relay_urls);
|
||||||
|
cJSON_Delete(event);
|
||||||
|
|
||||||
|
log_message(LOG_INFO, "Relay list published to %d relays", relay_count);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void* auto_publish_thread_func(void* arg) {
|
static void* auto_publish_thread_func(void* arg) {
|
||||||
superball_thrower_t* thrower = (superball_thrower_t*)arg;
|
superball_thrower_t* thrower = (superball_thrower_t*)arg;
|
||||||
|
|
||||||
@@ -1078,6 +1250,10 @@ static int thrower_start(superball_thrower_t* thrower) {
|
|||||||
|
|
||||||
log_message(LOG_INFO, "Monitoring %d relays for routing events", thrower->config->relay_count);
|
log_message(LOG_INFO, "Monitoring %d relays for routing events", thrower->config->relay_count);
|
||||||
|
|
||||||
|
// Publish initial metadata and relay list
|
||||||
|
publish_metadata(thrower);
|
||||||
|
publish_relay_list(thrower);
|
||||||
|
|
||||||
// Publish initial thrower info
|
// Publish initial thrower info
|
||||||
publish_thrower_info(thrower);
|
publish_thrower_info(thrower);
|
||||||
|
|
||||||
|
|||||||
200
throw_superball.sh
Executable file
200
throw_superball.sh
Executable file
@@ -0,0 +1,200 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
CONFIG_FILE="config.json"
|
||||||
|
DELAY=10 # Default 10 second delay
|
||||||
|
|
||||||
|
show_usage() {
|
||||||
|
echo "Superball Test Thrower"
|
||||||
|
echo ""
|
||||||
|
echo "Usage:"
|
||||||
|
echo " $0 [options]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -c, --config <file> Config file path (default: config.json)"
|
||||||
|
echo " -d, --delay <seconds> Delay in seconds (default: 10)"
|
||||||
|
echo " -m, --message <text> Message to send (default: 'Test superball')"
|
||||||
|
echo " -h, --help Show this help message"
|
||||||
|
echo ""
|
||||||
|
echo "Creates a Superball routing event and throws it to the thrower."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Default message includes timestamp
|
||||||
|
MESSAGE="Test superball - $(date '+%Y-%m-%d %H:%M:%S %Z')"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-c|--config)
|
||||||
|
CONFIG_FILE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-d|--delay)
|
||||||
|
DELAY="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-m|--message)
|
||||||
|
MESSAGE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
show_usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1"
|
||||||
|
show_usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if nak is installed
|
||||||
|
if ! command -v nak &> /dev/null; then
|
||||||
|
echo -e "${RED}Error: 'nak' command not found${NC}"
|
||||||
|
echo "Please install nak: https://github.com/fiatjaf/nak"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if jq is installed
|
||||||
|
if ! command -v jq &> /dev/null; then
|
||||||
|
echo -e "${RED}Error: 'jq' command not found${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load config.json
|
||||||
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||||
|
echo -e "${RED}Error: Config file not found: $CONFIG_FILE${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${BLUE}Loading configuration from $CONFIG_FILE...${NC}"
|
||||||
|
|
||||||
|
# Extract thrower pubkey from config
|
||||||
|
THROWER_PRIVKEY=$(jq -r '.thrower.privateKey // empty' "$CONFIG_FILE")
|
||||||
|
if [[ -z "$THROWER_PRIVKEY" ]]; then
|
||||||
|
echo -e "${RED}Error: Could not find thrower.privateKey in config${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
THROWER_PUBKEY=$(echo "$THROWER_PRIVKEY" | nak key public)
|
||||||
|
if [[ -z "$THROWER_PUBKEY" ]]; then
|
||||||
|
echo -e "${RED}Error: Could not derive thrower pubkey${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Thrower pubkey: ${THROWER_PUBKEY:0:16}...${NC}"
|
||||||
|
|
||||||
|
# Get relays from config
|
||||||
|
mapfile -t RELAYS < <(jq -r '.relays[].url' "$CONFIG_FILE")
|
||||||
|
if [[ ${#RELAYS[@]} -eq 0 ]]; then
|
||||||
|
echo -e "${RED}Error: No relays found in config${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Loaded ${#RELAYS[@]} relay(s)${NC}"
|
||||||
|
|
||||||
|
# Generate ephemeral keypair for this superball
|
||||||
|
echo -e "${BLUE}Generating ephemeral keypair...${NC}"
|
||||||
|
EPHEMERAL_PRIVKEY=$(nak key generate)
|
||||||
|
EPHEMERAL_PUBKEY=$(echo "$EPHEMERAL_PRIVKEY" | nak key public)
|
||||||
|
echo -e "${GREEN}✓ Ephemeral pubkey: ${EPHEMERAL_PUBKEY:0:16}...${NC}"
|
||||||
|
|
||||||
|
# Generate audit tag (random 32-byte hex)
|
||||||
|
AUDIT_TAG=$(openssl rand -hex 32)
|
||||||
|
echo -e "${GREEN}✓ Audit tag: ${AUDIT_TAG:0:16}...${NC}"
|
||||||
|
|
||||||
|
# Create a simple kind 1 note as the inner event
|
||||||
|
echo -e "${BLUE}Creating inner event (kind 1 note)...${NC}"
|
||||||
|
INNER_EVENT=$(nak event -c "$MESSAGE" --kind 1 --sec "$EPHEMERAL_PRIVKEY")
|
||||||
|
echo -e "${GREEN}✓ Inner event created${NC}"
|
||||||
|
|
||||||
|
# Build the routing payload
|
||||||
|
ROUTING_PAYLOAD=$(jq -n \
|
||||||
|
--argjson event "$INNER_EVENT" \
|
||||||
|
--arg relays "$(printf '%s\n' "${RELAYS[@]}" | jq -R . | jq -s .)" \
|
||||||
|
--argjson delay "$DELAY" \
|
||||||
|
--arg audit "$AUDIT_TAG" \
|
||||||
|
'{
|
||||||
|
event: $event,
|
||||||
|
routing: {
|
||||||
|
relays: ($relays | fromjson),
|
||||||
|
delay: $delay,
|
||||||
|
padding: "+0",
|
||||||
|
audit: $audit
|
||||||
|
}
|
||||||
|
}')
|
||||||
|
|
||||||
|
echo -e "${CYAN}Routing payload:${NC}"
|
||||||
|
echo "$ROUTING_PAYLOAD" | jq '.'
|
||||||
|
|
||||||
|
# Encrypt the payload with NIP-44 using thrower's pubkey
|
||||||
|
echo -e "${BLUE}Encrypting payload with NIP-44...${NC}"
|
||||||
|
ENCRYPTED_CONTENT=$(nak encrypt --sec "$EPHEMERAL_PRIVKEY" --recipient-pubkey "$THROWER_PUBKEY" "$ROUTING_PAYLOAD")
|
||||||
|
|
||||||
|
if [[ -z "$ENCRYPTED_CONTENT" ]]; then
|
||||||
|
echo -e "${RED}Error: Failed to encrypt payload${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Payload encrypted (length: ${#ENCRYPTED_CONTENT})${NC}"
|
||||||
|
|
||||||
|
# Create the routing event (kind 22222)
|
||||||
|
echo -e "${BLUE}Creating routing event (kind 22222)...${NC}"
|
||||||
|
|
||||||
|
# Build tags array
|
||||||
|
TAGS=$(jq -n \
|
||||||
|
--arg thrower "$THROWER_PUBKEY" \
|
||||||
|
--arg audit "$AUDIT_TAG" \
|
||||||
|
'[
|
||||||
|
["p", $thrower],
|
||||||
|
["p", $audit]
|
||||||
|
]')
|
||||||
|
|
||||||
|
# Create the event
|
||||||
|
ROUTING_EVENT=$(nak event \
|
||||||
|
--kind 22222 \
|
||||||
|
--sec "$EPHEMERAL_PRIVKEY" \
|
||||||
|
-c "$ENCRYPTED_CONTENT" \
|
||||||
|
-t "p=$THROWER_PUBKEY" \
|
||||||
|
-t "p=$AUDIT_TAG")
|
||||||
|
|
||||||
|
if [[ -z "$ROUTING_EVENT" ]]; then
|
||||||
|
echo -e "${RED}Error: Failed to create routing event${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Routing event created${NC}"
|
||||||
|
echo -e "${CYAN}Full routing event JSON:${NC}"
|
||||||
|
echo "$ROUTING_EVENT" | jq '.'
|
||||||
|
|
||||||
|
# Publish to all relays
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
echo -e "${YELLOW}Throwing Superball to relays...${NC}"
|
||||||
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
|
||||||
|
for relay in "${RELAYS[@]}"; do
|
||||||
|
echo -e "${BLUE}Publishing to: $relay${NC}"
|
||||||
|
RESPONSE=$(echo "$ROUTING_EVENT" | nak event "$relay" 2>&1)
|
||||||
|
echo "$RESPONSE"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✓ Superball thrown!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
echo -e "${YELLOW}Monitoring for audit tag appearance...${NC}"
|
||||||
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
echo -e "Audit tag: ${GREEN}$AUDIT_TAG${NC}"
|
||||||
|
echo -e "Expected delay: ${GREEN}${DELAY}s${NC}"
|
||||||
|
echo -e "Watch with: ${BLUE}nak req -k 22222 --stream '#p=$AUDIT_TAG' ${RELAYS[0]}${NC}"
|
||||||
|
echo ""
|
||||||
183
watch_relays.sh
Executable file
183
watch_relays.sh
Executable file
@@ -0,0 +1,183 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
CONFIG_FILE="config.json"
|
||||||
|
OUR_PUBKEY=""
|
||||||
|
RELAYS=()
|
||||||
|
|
||||||
|
show_usage() {
|
||||||
|
echo "Superball Relay Watcher - Simple JSON Display"
|
||||||
|
echo ""
|
||||||
|
echo "Usage:"
|
||||||
|
echo " $0 [options]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -c, --config <file> Config file path (default: config.json)"
|
||||||
|
echo " -h, --help Show this help message"
|
||||||
|
echo ""
|
||||||
|
echo "Watches all relays from config.json for:"
|
||||||
|
echo " - All events from our thrower (pubkey from config.json)"
|
||||||
|
echo " - Kind 0 (Metadata), Kind 10002 (Relay List)"
|
||||||
|
echo " - Kind 12222 (Thrower Info), Kind 22222 (Routing)"
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-c|--config)
|
||||||
|
CONFIG_FILE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
show_usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1"
|
||||||
|
show_usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if nak is installed
|
||||||
|
if ! command -v nak &> /dev/null; then
|
||||||
|
echo "Error: 'nak' command not found"
|
||||||
|
echo "Please install nak: https://github.com/fiatjaf/nak"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if jq is installed
|
||||||
|
if ! command -v jq &> /dev/null; then
|
||||||
|
echo "Error: 'jq' command not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load config.json if it exists
|
||||||
|
if [[ -f "$CONFIG_FILE" ]]; then
|
||||||
|
echo -e "${BLUE}Loading configuration from $CONFIG_FILE...${NC}"
|
||||||
|
|
||||||
|
# Extract private key and derive public key
|
||||||
|
PRIVATE_KEY=$(jq -r '.thrower.privateKey // empty' "$CONFIG_FILE")
|
||||||
|
if [[ -n "$PRIVATE_KEY" ]]; then
|
||||||
|
OUR_PUBKEY=$(echo "$PRIVATE_KEY" | nak key public)
|
||||||
|
if [[ -n "$OUR_PUBKEY" ]]; then
|
||||||
|
echo -e "${GREEN}✓ Our thrower pubkey: ${OUR_PUBKEY:0:16}...${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get all relays from config
|
||||||
|
mapfile -t RELAYS < <(jq -r '.relays[].url' "$CONFIG_FILE")
|
||||||
|
if [[ ${#RELAYS[@]} -gt 0 ]]; then
|
||||||
|
echo -e "${GREEN}✓ Loaded ${#RELAYS[@]} relay(s) from config${NC}"
|
||||||
|
for relay in "${RELAYS[@]}"; do
|
||||||
|
echo -e " - $relay"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#RELAYS[@]} -eq 0 ]]; then
|
||||||
|
echo "Error: No relays found in config.json"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$OUR_PUBKEY" ]]; then
|
||||||
|
echo "Warning: Could not derive pubkey from config.json"
|
||||||
|
echo "Will only show kinds 0, 10002, 12222, 22222"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
if [[ -n "$OUR_PUBKEY" ]]; then
|
||||||
|
echo -e "${BLUE}Watching:${NC} Events from ${OUR_PUBKEY:0:16}... + kinds 0, 10002, 12222, 22222"
|
||||||
|
else
|
||||||
|
echo -e "${BLUE}Watching:${NC} Kinds 0, 10002, 12222, 22222"
|
||||||
|
fi
|
||||||
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}Listening for events... (Press Ctrl+C to stop)${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Build two separate nak commands:
|
||||||
|
# 1. Watch for kinds 12222 and 22222 from anyone
|
||||||
|
NAK_CMD1="nak req --stream -k 12222 -k 22222"
|
||||||
|
for relay in "${RELAYS[@]}"; do
|
||||||
|
NAK_CMD1="$NAK_CMD1 $relay"
|
||||||
|
done
|
||||||
|
|
||||||
|
# 2. Watch for all events from our thrower (if we have the pubkey)
|
||||||
|
NAK_CMD2=""
|
||||||
|
if [[ -n "$OUR_PUBKEY" ]]; then
|
||||||
|
NAK_CMD2="nak req --stream -a $OUR_PUBKEY"
|
||||||
|
for relay in "${RELAYS[@]}"; do
|
||||||
|
NAK_CMD2="$NAK_CMD2 $relay"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Combine both subscriptions and stream events
|
||||||
|
{
|
||||||
|
if [[ -n "$NAK_CMD2" ]]; then
|
||||||
|
# Run both subscriptions in parallel, merge output
|
||||||
|
eval "$NAK_CMD1" 2>/dev/null &
|
||||||
|
eval "$NAK_CMD2" 2>/dev/null
|
||||||
|
else
|
||||||
|
# Only run the first subscription
|
||||||
|
eval "$NAK_CMD1" 2>/dev/null
|
||||||
|
fi
|
||||||
|
} | while IFS= read -r line; do
|
||||||
|
# Skip non-JSON lines
|
||||||
|
if echo "$line" | jq empty 2>/dev/null; then
|
||||||
|
# Check if it's an event array ["EVENT", "sub_id", {...}]
|
||||||
|
if echo "$line" | jq -e '.[0] == "EVENT"' > /dev/null 2>&1; then
|
||||||
|
event=$(echo "$line" | jq '.[2]')
|
||||||
|
|
||||||
|
# Get event details
|
||||||
|
kind=$(echo "$event" | jq -r '.kind')
|
||||||
|
pubkey=$(echo "$event" | jq -r '.pubkey')
|
||||||
|
id=$(echo "$event" | jq -r '.id')
|
||||||
|
|
||||||
|
# Check if it's from our thrower
|
||||||
|
is_ours=""
|
||||||
|
if [[ -n "$OUR_PUBKEY" && "$pubkey" == "$OUR_PUBKEY" ]]; then
|
||||||
|
is_ours=" ${GREEN}[OUR THROWER]${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Display separator and event info
|
||||||
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
echo -e "${BLUE}Kind ${kind} | ID: ${id:0:16}...${is_ours}${NC}"
|
||||||
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
|
||||||
|
# Display formatted JSON
|
||||||
|
echo "$event" | jq '.'
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Or if it's a direct event object
|
||||||
|
elif echo "$line" | jq -e '.kind' > /dev/null 2>&1; then
|
||||||
|
kind=$(echo "$line" | jq -r '.kind')
|
||||||
|
pubkey=$(echo "$line" | jq -r '.pubkey')
|
||||||
|
id=$(echo "$line" | jq -r '.id')
|
||||||
|
|
||||||
|
# Check if it's from our thrower
|
||||||
|
is_ours=""
|
||||||
|
if [[ -n "$OUR_PUBKEY" && "$pubkey" == "$OUR_PUBKEY" ]]; then
|
||||||
|
is_ours=" ${GREEN}[OUR THROWER]${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Display separator and event info
|
||||||
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
echo -e "${BLUE}Kind ${kind} | ID: ${id:0:16}...${is_ours}${NC}"
|
||||||
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
|
||||||
|
# Display formatted JSON
|
||||||
|
echo "$line" | jq '.'
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user