commit dc0a952140580b8e90152167c2d63d434495fcb8 Author: stephen <stephen@vanderwarker.family> Date: Tue Dec 24 17:45:28 2024 -0500 LFG diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ceaea36 --- /dev/null +++ b/.gitignore @@ -0,0 +1,132 @@ +# ---> Node +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + diff --git a/README.md b/README.md new file mode 100644 index 0000000..6dbc2d0 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# tickstr + +A ChatGPT pipe dreamed script for: + +https://github.com/nostr-protocol/nips/pull/1658 + + 1. <code>git clone https://git.vanderwarker.family/nostr/tickstr</code> + 2. <code>cd tickstr</code> + 3. <code>npm i</code> + 4. <code>edit settings.json</code> + 5. <code>nodejs main.js</code> + 6. <code>????</code> + 7. <code>PROFIT!</code> + 8. <code>Remember, that one sat is one sat so it doesn't matter</code> + 9. <code>cd ..</code> + 10. <code>rm tickstr</code> diff --git a/main.js b/main.js new file mode 100644 index 0000000..27feb48 --- /dev/null +++ b/main.js @@ -0,0 +1,142 @@ +const WebSocket = require('ws'); +const crypto = require('crypto'); +const elliptic = require('elliptic'); +const Buffer = require('safe-buffer').Buffer +const schnorr = require('bip-schnorr'); + +// Use elliptic for ECDSA signing +const EC = elliptic.ec; +const ecdsa = new EC('secp256k1'); + +// Your Nostr private key (replace with your own) +const privateKeyHex = ''; // Use your actual private key here + +// Ensure private key is a valid 32-byte hex string +if (!/^([0-9a-fA-F]{64})$/.test(privateKeyHex)) { + console.error('Invalid private key format. Ensure it is 64 hexadecimal characters.'); + process.exit(1); +} + +const publicKeyHex = generatePublicKey(privateKeyHex); + +// Connect to Binance WebSocket +const ws = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt@ticker'); + +// Handle WebSocket connection and data +ws.on('open', function open() { + console.log('Connected to Binance WebSocket'); +}); + +ws.on('message', function incoming(message) { + const data = JSON.parse(message); + + // Extract relevant data + const symbol = data.s; + const lastPrice = parseFloat(data.c); + const weightedAvgPrice = parseFloat(data.w); + const highPrice = parseFloat(data.h); + const lowPrice = parseFloat(data.l); + + // Create Nostr event + const timestamp = Math.floor(Date.now() / 1000); + const eventContent = JSON.stringify({ + symbol, + last_price: lastPrice, + weighted_avg_price: weightedAvgPrice, + high_price: highPrice, + low_price: lowPrice + }); + + const event = { + id: '', + pubkey: publicKeyHex, // 32-byte public key + created_at: timestamp, + kind: 31892, // Note (you can change this based on your event type) + tags: [['d','BTCUSD'],['n', 'BTC'],['n','USD'],['value',lastPrice]], // Optional tag (you can customize this) + content: eventContent, + sig: '' // Initialize sig here, it will be added after content + }; + + // Compute event ID (sha256 of serialized event) + const serializedEvent = serializeEvent(publicKeyHex, timestamp, event.kind, event.tags, eventContent); + const eventId = crypto.createHash('sha256').update(serializedEvent).digest('hex'); + event.id = eventId; + + // Sign the event using elliptic library + event.sig = signEvent(privateKeyHex, serializedEvent, event.id); + + // Print the Nostr event with signature under content + console.log('Nostr Event:', JSON.stringify(event, null, 2)); + + // Optionally, broadcast the event to a Nostr relay (example) + sendToRelay(event); +}); + +// Function to generate compressed public key from private key using elliptic +function generatePublicKey(privateKeyHex) { + const privateKey = Buffer.from(privateKeyHex, 'hex'); + + if (privateKey.length !== 32) { + console.error('Private key must be 32 bytes.'); + process.exit(1); + } + + const key = ecdsa.keyFromPrivate(privateKey); + const publicKey = key.getPublic(true, 'hex'); // Compressed format + + return publicKey.slice(2); // Remove the '0x' prefix to get 32 bytes +} + +// Function to serialize the event according to NIP-01 +function serializeEvent(pubkey, createdAt, kind, tags, content) { + const eventData = [ + 0, // Prefix for NIP-01 event serialization + pubkey, + createdAt, + kind, + tags, + content + ]; + + // Serialize the event to JSON string + let eventJson = JSON.stringify(eventData, (key, value) => { + // Escape content field (you can also add custom escaping here if necessary) + if (key === 'content') { + value = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + } + return value; + }); + + return eventJson; +} + +// Function to sign the event with the private key using elliptic +function signEvent(privateKeyHex, serializedEvent, eventID) { + const message = Buffer.from(eventID, 'hex'); + const signature = schnorr.sign(privateKeyHex, message); + return signature.toString('hex'); +} + +// Function to send the event to a Nostr relay +function sendToRelay(event) { + // Example relay URL (replace with the actual Nostr relay URL) + const relayUrl = ''; // Replace with your relay URL + + // Create a WebSocket connection to the relay + const relayWs = new WebSocket(relayUrl); + + relayWs.on('open', () => { + // Once the connection is open, send the event + const eventMessage = ['EVENT', event]; // Relay expects an array with 'EVENT' and event data + relayWs.send(JSON.stringify(eventMessage)); + console.log('Event sent to relay:', JSON.stringify(event, null, 2)); + }); + + relayWs.on('error', (err) => { + console.error('Error sending event to relay:', err); + }); + + relayWs.on('close', () => { + console.log('Connection to relay closed'); + }); +}