12 Commits

Author SHA1 Message Date
Your Name
5152bb6e5e Update the profile information in the fields if a profile already exists. 2025-12-01 14:28:49 -04:00
9285f8e583 Update web/superball.html 2025-11-18 04:11:37 +00:00
c5ec067a79 Update web/superball.html 2025-11-18 03:54:22 +00:00
387f5d4725 Update web/superball.html 2025-11-18 02:37:28 +00:00
c4d73026b5 CSS 2025-11-16 04:38:00 +00:00
516c4f2f80 Adds reply and profile creation along with post functionality
This commit introduces new features to enhance user interaction and profile management:

- Adds the ability to create replies for comments
- Enables profile creation for users
- Supports post creation for sharing updates and content
2025-11-16 04:36:10 +00:00
Your Name
6ff47cec92 Fixed bugs with install script 2025-09-29 10:41:04 -04:00
Your Name
5b1fa185f7 . 2025-09-29 10:30:31 -04:00
Your Name
d941638779 . 2025-09-29 09:57:36 -04:00
Your Name
e6bef22494 . 2025-09-29 09:39:14 -04:00
Your Name
3edc477069 install.sh 2025-09-29 09:28:19 -04:00
Your Name
5b618c08ed fix ws issue 2025-09-29 07:36:12 -04:00
7 changed files with 548 additions and 122 deletions

49
Trash/trash.json Normal file
View File

@@ -0,0 +1,49 @@
Reply to a post from Primal
{
"kind": 1,
"created_at": 1763420960,
"content": "From Primal.",
"pubkey": "8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e",
"id": "555c77e419c6f34d1d36b6baf36489c5eb17d829645c940fc996d90c26df59a1",
"tags": [
[
"e",
"ac50dd87901863590d577fb9080ab294b8dd1b6b10563e9733d5bad817c735db",
"wss://nos.lol",
"root"
],
[
"p",
"8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e"
],
[
"r",
"wss://nostr.mom/"
],
[
"r",
"wss://relay.laantungir.net/"
]
],
"sig": "5ab8f7b43e972e2089ebc95f68c49cace183832a3f867801a85c689ce6797eaa52a60ba07847212019fc2361ffca276c74f9cd69402095c6c783faac2c827bde"
}
Reply to post from proposed superball builder change.
{
"id": "56e6dadc32afe11f13e83fcf8107ea8a819e4a96232d64eacafd10370224e685",
"sig": "3766102783acfc7654f134b1dc5fd716e89e154fc81a96c7f60d20a867e5b52919cdfa7a01151550880e4e8a2945ef05feb43c022bbd7761724ded8308254b1d",
"kind": 1,
"tags": [
[
"e",
"ac50dd87901863590d577fb9080ab294b8dd1b6b10563e9733d5bad817c735db",
"root"
]
],
"pubkey": "8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e",
"content": "Yeah, it is a great night.",
"created_at": 1763420892
}

View File

@@ -418,7 +418,7 @@ class EventProcessor {
if (!routing.audit || typeof routing.audit !== 'string') return false;
// Check maximum delay limit
const maxDelay = this.config.get('thrower.maxDelay') || 3600;
const maxDelay = this.config.get('thrower.maxDelay') || 86460;
if (routing.delay > maxDelay) {
this.logger.error(`Routing delay ${routing.delay}s exceeds maximum allowed delay of ${maxDelay}s`);
return false;
@@ -648,7 +648,7 @@ class ThrowerInfoManager {
if (throwerInfo.privacyPolicy) tags.push(['privacy_policy', throwerInfo.privacyPolicy]);
if (throwerInfo.termsOfService) tags.push(['terms_of_service', throwerInfo.termsOfService]);
tags.push(['refresh_rate', throwerInfo.refreshRate.toString()]);
tags.push(['max_delay', (throwerInfo.maxDelay || 3600).toString()]);
tags.push(['max_delay', (throwerInfo.maxDelay || 86460).toString()]);
const eventTemplate = {
kind: 12222,
@@ -933,7 +933,7 @@ class WebSocketManager {
this.logger.info('Stopping WebSocket monitoring...');
this.connections.forEach(({ url, ws }) => {
if (ws.readyState === WebSocket.OPEN) {
if (ws.readyState === ws.OPEN) {
// Send CLOSE message for subscription
if (this.subscriptionId) {
const closeMsg = JSON.stringify(['CLOSE', this.subscriptionId]);

View File

@@ -5,126 +5,261 @@
set -e
echo "=== Superball Thrower Daemon Installation ==="
echo
echo "🏀 Superball Thrower Daemon Installation"
echo "========================================"
# Check if running as root
if [[ $EUID -eq 0 ]]; then
echo "This script should not be run as root for security reasons."
echo "Please run as a regular user with sudo privileges."
echo "This script should not be run as root for security reasons"
echo " Please run as a regular user with sudo privileges"
exit 1
fi
# Check if Node.js is installed
if ! command -v node &> /dev/null; then
echo "Error: Node.js is not installed."
echo "Please install Node.js 16 or later from https://nodejs.org/"
# Function to check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check for required system dependencies
echo "📋 Checking system dependencies..."
# Check for curl
if ! command_exists curl; then
echo "❌ curl is required but not installed"
echo " Please install curl: sudo apt update && sudo apt install curl"
exit 1
fi
# Check Node.js version
NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1)
if [ "$NODE_VERSION" -lt 16 ]; then
echo "Error: Node.js version 16 or later is required."
echo "Current version: $(node --version)"
# Check for systemctl (systemd)
if ! command_exists systemctl; then
echo "❌ systemd is required but not found"
echo " This script requires a systemd-based system"
exit 1
fi
echo "✓ Node.js $(node --version) detected"
# Install latest Node.js using nvm
echo "🔧 Installing latest Node.js using nvm..."
# Check if npm is installed
if ! command -v npm &> /dev/null; then
echo "Error: npm is not installed."
# Download and install nvm
if [ ! -d "$HOME/.nvm" ]; then
echo " Downloading and installing nvm..."
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
fi
# Load nvm
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
# Install Node.js 22 (latest LTS)
echo " Installing Node.js 22..."
nvm install 22
nvm use 22
# Verify installation
NODE_VERSION=$(node --version)
NPM_VERSION=$(npm --version)
echo "✅ Node.js $NODE_VERSION installed"
echo "✅ npm $NPM_VERSION installed"
# Copy Node.js binaries to system-wide location
echo "📋 Installing Node.js binaries system-wide..."
NODE_PATH=$(which node)
NPM_PATH=$(which npm)
if [ -n "$NODE_PATH" ] && [ -x "$NODE_PATH" ]; then
# Remove any existing symlinks first
sudo rm -f /usr/local/bin/node
sudo cp "$NODE_PATH" /usr/local/bin/node
sudo chmod +x /usr/local/bin/node
echo "✅ Node.js binary copied to /usr/local/bin/node"
else
echo "❌ Failed to find Node.js executable"
exit 1
fi
echo "✓ npm $(npm --version) detected"
# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DAEMON_DIR="$SCRIPT_DIR"
echo "Installing to: $DAEMON_DIR"
echo
# Install Node.js dependencies
echo "Installing Node.js dependencies..."
cd "$DAEMON_DIR"
npm install
if [ $? -ne 0 ]; then
echo "Error: Failed to install Node.js dependencies"
if [ -n "$NPM_PATH" ] && [ -x "$NPM_PATH" ]; then
# Remove any existing symlinks first
sudo rm -f /usr/local/bin/npm
sudo cp "$NPM_PATH" /usr/local/bin/npm
sudo chmod +x /usr/local/bin/npm
echo "✅ npm binary copied to /usr/local/bin/npm"
else
echo "❌ Failed to find npm executable"
exit 1
fi
echo "✓ Dependencies installed successfully"
echo
# Install npm dependencies
echo "📦 Installing npm dependencies..."
if ! npm install; then
echo "❌ Failed to install npm dependencies"
exit 1
fi
# Create user for daemon (if it doesn't exist)
DAEMON_USER="superball"
if ! id "$DAEMON_USER" &>/dev/null; then
echo "Creating system user: $DAEMON_USER"
echo "✅ Dependencies installed successfully"
# Interactive configuration
echo ""
echo "⚙️ Thrower Configuration"
echo "========================"
echo "Please provide the following information for your Superball Thrower:"
echo ""
# Prompt for configuration values
read -p "Enter your private key (hex format, leave empty to generate new): " PRIVATE_KEY
read -p "Enter your public key (hex format, leave empty if generating new): " PUBLIC_KEY
read -p "Enter thrower name (e.g., 'My Superball Thrower'): " THROWER_NAME
read -p "Enter thrower description: " THROWER_DESCRIPTION
read -p "Enter banner URL (optional, press enter to skip): " BANNER_URL
read -p "Enter icon URL (optional, press enter to skip): " ICON_URL
# Generate keypair if not provided
if [ -z "$PRIVATE_KEY" ] || [ -z "$PUBLIC_KEY" ]; then
echo "🔑 Generating new keypair..."
# We'll let the daemon generate the keypair on first run
PRIVATE_KEY=""
PUBLIC_KEY=""
fi
# Update config.json with user input
echo "📝 Updating configuration file..."
# Create a temporary config file with user values
cat > config.json << EOF
{
"thrower": {
"privateKey": "$PRIVATE_KEY",
"publicKey": "$PUBLIC_KEY",
"name": "$THROWER_NAME",
"description": "$THROWER_DESCRIPTION",
"banner": "$BANNER_URL",
"icon": "$ICON_URL",
"adminPubkey": "",
"contact": "",
"supportedSups": "1,2,3,4,5,6",
"software": "https://git.laantungir.net/laantungir/super_ball.git",
"version": "1.0.0",
"privacyPolicy": "",
"termsOfService": "",
"refreshRate": 300,
"maxDelay": 86460
},
"relays": [
{ "url": "wss://relay.laantungir.net", "read": true, "write": true, "authStatus": "unknown" },
{ "url": "wss://relay.damus.io", "read": true, "write": true, "authStatus": "unknown" },
{ "url": "wss://nos.lol", "read": true, "write": true, "authStatus": "unknown" },
{ "url": "wss://relay.snort.social", "read": true, "write": true, "authStatus": "unknown" },
{ "url": "wss://relay.primal.net", "read": true, "write": true, "authStatus": "unknown" },
{ "url": "wss://relay.nostr.band", "read": true, "write": true, "authStatus": "unknown" },
{ "url": "wss://nostr.oxtr.dev", "read": true, "write": true, "authStatus": "unknown" },
{ "url": "wss://offchain.pub", "read": true, "write": true, "authStatus": "unknown" },
{ "url": "wss://nostr-pub.wellorder.net", "read": true, "write": true, "authStatus": "unknown" }
],
"daemon": {
"logLevel": "info",
"maxQueueSize": 1000,
"maxLogEntries": 1000,
"autoStart": false
}
}
EOF
echo "✅ Configuration updated"
# Create system user for the daemon
echo "👤 Creating system user..."
DAEMON_USER="superball-thrower"
if id "$DAEMON_USER" &>/dev/null; then
echo "✅ User $DAEMON_USER already exists"
else
echo " Creating user: $DAEMON_USER"
sudo useradd --system --no-create-home --shell /bin/false "$DAEMON_USER"
echo " User $DAEMON_USER created"
else
echo "✓ User $DAEMON_USER already exists"
echo " User $DAEMON_USER created"
fi
# Create directories
echo "Creating directories..."
sudo mkdir -p /var/log/superball
sudo mkdir -p /etc/superball
sudo chown "$DAEMON_USER:$DAEMON_USER" /var/log/superball
echo "✓ Directories created"
# Create installation directory
INSTALL_DIR="/opt/superball-thrower"
echo "📁 Setting up installation directory: $INSTALL_DIR"
# Copy configuration file
if [ ! -f "/etc/superball/config.json" ]; then
echo "Installing default configuration..."
sudo cp "$DAEMON_DIR/config.json" /etc/superball/config.json
sudo chown "$DAEMON_USER:$DAEMON_USER" /etc/superball/config.json
sudo chmod 600 /etc/superball/config.json
echo "✓ Configuration installed to /etc/superball/config.json"
echo "⚠️ IMPORTANT: Edit /etc/superball/config.json with your private key and settings"
else
echo "✓ Configuration already exists at /etc/superball/config.json"
sudo mkdir -p "$INSTALL_DIR"
sudo cp daemon.js "$INSTALL_DIR/"
sudo cp package.json "$INSTALL_DIR/"
sudo cp config.json "$INSTALL_DIR/"
# Copy node_modules if it exists
if [ -d "node_modules" ]; then
sudo cp -r node_modules "$INSTALL_DIR/"
fi
# Set ownership and permissions
sudo chown -R "$DAEMON_USER:$DAEMON_USER" "$INSTALL_DIR"
sudo chmod 755 "$INSTALL_DIR"
sudo chmod 644 "$INSTALL_DIR"/*.js "$INSTALL_DIR"/*.json
sudo chmod -R 755 "$INSTALL_DIR/node_modules" 2>/dev/null || true
echo "✅ Files installed to $INSTALL_DIR"
# Create log directory
LOG_DIR="/var/log/superball-thrower"
echo "📝 Creating log directory: $LOG_DIR"
sudo mkdir -p "$LOG_DIR"
sudo chown "$DAEMON_USER:$DAEMON_USER" "$LOG_DIR"
sudo chmod 755 "$LOG_DIR"
echo "✅ Log directory created"
# Install systemd service
echo "Installing systemd service..."
sudo cp "$DAEMON_DIR/superball-thrower.service" /etc/systemd/system/
sudo sed -i "s|/path/to/thrower_daemon|$DAEMON_DIR|g" /etc/systemd/system/superball-thrower.service
echo "⚙️ Installing systemd service..."
sudo cp superball-thrower.service /etc/systemd/system/
sudo systemctl daemon-reload
echo "✓ Systemd service installed"
sudo systemctl enable superball-thrower.service
# Set permissions
echo "Setting permissions..."
sudo chown -R "$DAEMON_USER:$DAEMON_USER" "$DAEMON_DIR"
sudo chmod +x "$DAEMON_DIR/daemon.js"
echo "✓ Permissions set"
echo "✅ Systemd service installed and enabled"
echo
echo "=== Installation Complete ==="
echo
echo "Next steps:"
echo "1. Edit the configuration file:"
echo " sudo nano /etc/superball/config.json"
echo
echo "2. Add your private key and configure relays"
echo
echo "3. Enable and start the service:"
echo " sudo systemctl enable superball-thrower"
echo " sudo systemctl start superball-thrower"
echo
echo "4. Check service status:"
echo " sudo systemctl status superball-thrower"
echo
echo "5. View logs:"
echo " sudo journalctl -u superball-thrower -f"
echo
echo "Configuration file location: /etc/superball/config.json"
echo "Log file location: /var/log/superball/daemon.log"
echo "Service name: superball-thrower"
echo
echo "⚠️ SECURITY WARNING:"
echo " Make sure to secure your private key in the configuration file!"
echo " The config file should only be readable by the superball user."
# Initialize daemon if no keypair was provided
if [ -z "$PRIVATE_KEY" ]; then
echo "🔑 Initializing daemon to generate keypair..."
# Create a simple init script that generates keypair
sudo -u "$DAEMON_USER" bash -c "
cd \"$INSTALL_DIR\"
/usr/local/bin/node -e \"
const { generateSecretKey, getPublicKey } = require('nostr-tools/pure');
const fs = require('fs');
const config = JSON.parse(fs.readFileSync('config.json', 'utf8'));
const sk = generateSecretKey();
const pk = getPublicKey(sk);
config.thrower.privateKey = Buffer.from(sk).toString('hex');
config.thrower.publicKey = pk;
fs.writeFileSync('config.json', JSON.stringify(config, null, 2));
console.log('Generated keypair:');
console.log('Private key:', Buffer.from(sk).toString('hex'));
console.log('Public key:', pk);
\"
"
echo "✅ Keypair generated"
fi
# Installation complete
echo ""
echo "🎉 Installation completed successfully!"
echo ""
echo "📋 Next steps:"
echo " 1. Start the service: sudo systemctl start superball-thrower"
echo " 2. Check status: sudo systemctl status superball-thrower"
echo " 3. View logs: sudo journalctl -u superball-thrower -f"
echo ""
echo "📖 For more information, see the README.md file"
echo ""
echo "🔐 Security notes:"
echo " - The daemon runs as user '$DAEMON_USER' with limited privileges"
echo " - Configuration files are owned by the daemon user"
echo " - Logs are written to $LOG_DIR"
echo ""
echo "⚙️ Configuration summary:"
echo " - Thrower name: $THROWER_NAME"
echo " - Description: $THROWER_DESCRIPTION"
echo " - Config file: $INSTALL_DIR/config.json"
echo ""

View File

@@ -3,9 +3,19 @@
# Superball Thrower Daemon Installation Script
# This script installs and configures the Superball Thrower daemon
sudo apt install nodejs npm
# Download and install nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
# in lieu of restarting the shell
\. "$HOME/.nvm/nvm.sh"
# Download and install Node.js:
nvm install 22
# Verify the Node.js version:
node -v # Should print "v22.20.0".
# Verify npm version:
npm -v # Should print "10.9.3".
# Verify installation
node --version
npm --version

View File

@@ -6,10 +6,11 @@ Wants=network-online.target
[Service]
Type=simple
User=superball
Group=superball
WorkingDirectory=/path/to/thrower_daemon
ExecStart=/usr/bin/node daemon.js /etc/superball/config.json
User=superball-thrower
Group=superball-thrower
WorkingDirectory=/opt/superball-thrower
ExecStart=/usr/local/bin/node daemon.js start
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Restart=always
RestartSec=10
StandardOutput=journal
@@ -21,14 +22,14 @@ NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/superball
ReadWritePaths=/var/log/superball-thrower /opt/superball-thrower
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictRealtime=true
RestrictSUIDSGID=true
LockPersonality=true
MemoryDenyWriteExecute=true
# MemoryDenyWriteExecute=true # Disabled - conflicts with Node.js JIT compilation
RestrictNamespaces=true
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM

View File

@@ -216,11 +216,11 @@ label {
@keyframes pulse {
0%,
100% {
opacity: 1;
opacity: 1;
}
50% {
opacity: 0.5;
opacity: 0.5;
}
}
@@ -738,3 +738,54 @@ small {
.floating-tab:hover {
transform: scale(1.05);
}
/* .input-group {
margin: 20px;
} */
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
textarea {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.tabs {
display: flex;
margin: 20px 20px 0;
border-bottom: 1px solid #ccc;
}
.tab {
padding: 10px 20px;
cursor: pointer;
background-color: #f1f1f1;
border: 1px solid #ccc;
border-bottom: none;
margin-right: 5px;
border-radius: 4px 4px 0 0;
}
.tab.active {
background-color: #fff;
border-bottom: 1px solid #fff;
}
.tab-content {
display: none;
padding: 20px;
border: 1px solid #ccc;
border-radius: 4px;
}
.tab-content.active {
display: block;
}

View File

@@ -29,8 +29,44 @@
<div class="section">
<h2>Final Event (What gets posted at the end)</h2>
<div class="input-group">
<label for="final-content">Message Content:</label>
<textarea id="final-content" rows="3" placeholder="Enter your message content..."></textarea>
<div class="tabs">
<div class="tab active" data-tab="tab1">Post</div>
<div class="tab" data-tab="tab2">Reply</div>
<div class="tab" data-tab="tab3">Create/Edit Profile</div>
</div>
<div class="tab-content active" id="tab1">
<h3>Post</h3>
<label for="final-content">Message Content:</label>
<textarea id="final-content" rows="3" placeholder="Enter your message content..."></textarea>
</div>
<div class="tab-content" id="tab2">
<h3>Reply</h3>
<label for="nevent">Nevent:</label>
<textarea id="nevent" placeholder="Enter the nevent for the note..."></textarea>
<label for="reply-content">Message Content:</label>
<textarea id="reply-content" rows="3" placeholder="Enter your message content..."></textarea>
</div>
<div class="tab-content" id="tab3">
<h3>Create/Edit Profile</h3>
<label for="name">Name:</label>
<textarea id="name" placeholder="Enter your name..."></textarea>
<label for="about">About:</label>
<textarea id="about" rows="2" placeholder="A short bio..."></textarea>
<label for="profile-pic">Profile Picture:</label>
<textarea id="profile-pic" placeholder="URL of your profile pic..."></textarea>
<label for="display-name">Display Name:</label>
<textarea id="display-name" placeholder="Enter your display name..."></textarea>
<label for="website">Website:</label>
<textarea id="website" placeholder="Web URL..."></textarea>
<label for="banner">Banner:</label>
<textarea id="banner" placeholder="Enter your bannerm a (~1024x768) wide picture url..."></textarea>
<label for="nip05">NIP05:</label>
<textarea id="nip05" placeholder="Enter your nip05 in the format username@domain.com..."></textarea>
<label for="lud16">Lightning Address:</label>
<textarea id="lud16" placeholder="Enter your lightning address..."></textarea>
</div>
</div>
<button onclick="createFinalEvent()">Create Event That Will Be Published Publicly</button>
@@ -79,6 +115,31 @@
<!-- <script src="./nostr-lite.js"></script> -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const tabs = document.querySelectorAll('.tab');
const tabContents = document.querySelectorAll('.tab-content');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
// Remove active class from all tabs and contents
tabs.forEach(t => t.classList.remove('active'));
tabContents.forEach(c => c.classList.remove('active'));
// Add active class to clicked tab
tab.classList.add('active');
// Show corresponding content
const tabId = tab.getAttribute('data-tab');
document.getElementById(tabId).classList.add('active');
// If tab3 (Create/Edit Profile) is activated, populate form with existing profile data
if (tabId === 'tab3' && userProfileData) {
populateProfileForm(userProfileData);
}
});
});
});
// Global variables
let nlLite = null;
let userPubkey = null;
@@ -88,6 +149,7 @@
let bounceCounter = 0;
let discoveredThrowers = [];
let userRelays = [];
let userProfileData = null; // Store loaded profile data
// Initialize NOSTR_LOGIN_LITE
async function initializeApp() {
@@ -173,6 +235,7 @@
if (events.length > 0) {
console.log('SUCCESS', 'Profile event received');
const profile = JSON.parse(events[0].content);
userProfileData = profile; // Store profile data globally
displayProfile(profile);
} else {
console.log('INFO', 'No profile found');
@@ -200,6 +263,23 @@
console.log('SUCCESS', `Profile displayed: ${name}`);
}
// Populate profile form with existing profile data
function populateProfileForm(profile) {
if (!profile) return;
// Populate each field with existing data if available
document.getElementById('name').value = profile.name || '';
document.getElementById('about').value = profile.about || '';
document.getElementById('profile-pic').value = profile.picture || '';
document.getElementById('display-name').value = profile.display_name || profile.displayName || '';
document.getElementById('website').value = profile.website || '';
document.getElementById('banner').value = profile.banner || '';
document.getElementById('nip05').value = profile.nip05 || '';
document.getElementById('lud16').value = profile.lud16 || '';
console.log('INFO', 'Profile form populated with existing data');
}
// Load user's NIP-65 relay list
async function loadUserRelayList() {
if (!userPubkey) return [];
@@ -232,7 +312,11 @@
} else {
console.log('INFO', 'No relay list found, using defaults');
userRelays = [
{ url: 'wss://relay.laantungir.net', type: 'both' }
{ url: 'wss://relay.laantungir.net', type: 'both' },
{ url: 'wss://relay.primal.net', type: 'both' },
{ url: 'wss://nos.lol', type: 'both' },
{ url: 'wss://relay.damus.io', type: 'both' },
{ url: 'wss://offchain.pub', type: 'both' }
];
return userRelays;
}
@@ -712,21 +796,117 @@
// Create final event (kind 1)
async function createFinalEvent() {
const content = document.getElementById('final-content').value.trim();
// Get the active tab
const activeTab = document.querySelector('.tab.active').getAttribute('data-tab');
if (!content) {
alert('Please enter message content');
return;
// Get content based on active tab
let content = '';
let nevent = '';
let neventData;
let name = '';
let about = '';
let profilePic = '';
let displayName = '';
let website = '';
let banner = '';
let nip05 = '';
let lud16 = '';
switch(activeTab) {
case 'tab1': // Post
content = document.getElementById('final-content').value.trim();
break;
case 'tab2': // Reply
content = document.getElementById('reply-content').value.trim();
nevent = document.getElementById('nevent').value.trim();
break;
case 'tab3': // Create Profile
name = document.getElementById('name').value.trim();
about = document.getElementById('about').value.trim();
profilePic = document.getElementById('profile-pic').value.trim();
displayName = document.getElementById('display-name').value.trim();
website = document.getElementById('website').value.trim();
banner = document.getElementById('banner').value.trim();
nip05 = document.getElementById('nip05').value.trim();
lud16 = document.getElementById('lud16').value.trim();
break;
}
// Validate content based on tab
if (activeTab === 'tab1') {
if (!content) {
alert('Please enter message content');
return;
}
} else if (activeTab === 'tab2') {
if (!content) {
alert('Please enter message content');
return;
}
if (!nevent.startsWith('nevent')) {
alert('Please enter a valid nevent');
return;
}
try {
neventData = window.NostrTools.nip19.decode(nevent).data;
} catch (error) {
alert('Error decoding nevent string', error.message);
return;
}
} else if (activeTab === 'tab3') {
if (!name) {
alert('Please enter your name');
return;
}
}
try {
// Create the final event (kind 1) - pure message, no relay info
const eventTemplate = {
kind: 1,
content: content,
tags: [],
created_at: Math.floor(Date.now() / 1000)
};
let eventTemplate = {};
switch(activeTab) {
case 'tab1': // Post
eventTemplate = {
kind: 1,
content: content,
tags: [],
created_at: Math.floor(Date.now() / 1000)
};
break;
case 'tab2': // Reply
eventTemplate = {
kind: 1,
content: content,
tags: [['e', neventData.id, neventData.relays[0], 'root'], ['p', neventData.author]],
created_at: Math.floor(Date.now() / 1000)
};
neventData.relays.slice(1).forEach(relay => {
eventTemplate.tags.push(['r', relay]);
});
break;
case 'tab3': // Create Profile
eventTemplate = {
kind: 0,
content: JSON.stringify({
name: name,
about: about,
picture: profilePic,
display_name: displayName,
website: website,
banner: banner,
nip05: nip05,
lud16: lud16
}),
tags: [],
created_at: Math.floor(Date.now() / 1000)
};
break;
}
// Your existing event publishing logic here
console.log('Event to publish:', eventTemplate);
// ... rest of your publishing code
// Sign the event using window.nostr (NIP-07)
finalEvent = await window.nostr.signEvent(eventTemplate);