Move 9 development/testing .mjs scripts out of the package root into dev/ to keep the published package clean. Update relative imports and dev.sh reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
221 lines
7.3 KiB
JavaScript
221 lines
7.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* ATM Debit Approval Agent (TESTING ONLY)
|
|
*
|
|
* ⚠️ WARNING: This script auto-approves ALL debit requests without validation!
|
|
* ⚠️ DO NOT use in production - use the integrated debit approval service instead.
|
|
*
|
|
* Purpose:
|
|
* - Standalone debugging tool for testing the GetLiveDebitRequests subscription
|
|
* - Helps diagnose relay connectivity and message decryption issues
|
|
* - Useful when the integrated service isn't receiving events
|
|
*
|
|
* Usage:
|
|
* # Set environment variables (or create .env file in apps/machine/)
|
|
* export ATM_PRIVATE_KEY=<hex-private-key>
|
|
* export LIGHTNING_PUB_PUBKEY=<hex-pubkey>
|
|
* export RELAY_URL=ws://localhost:7777
|
|
*
|
|
* # Run the script
|
|
* node run-debit-agent.mjs
|
|
*
|
|
* Production alternative:
|
|
* The ATM app (apps/machine) includes an integrated debit approval service
|
|
* with session-based single-use protection. See:
|
|
* - apps/machine/src/services/lightning.ts (startDebitApprovalService)
|
|
* - docs/ndebit-cash-in-flow.md
|
|
*/
|
|
|
|
import { Relay } from 'nostr-tools/relay'
|
|
import { finalizeEvent, getPublicKey } from 'nostr-tools'
|
|
import * as nip44v1 from './nip44v1.mjs'
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
// Load .env file from apps/machine if it exists
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const envPath = path.join(__dirname, '../../apps/machine/.env')
|
|
if (fs.existsSync(envPath)) {
|
|
const envContent = fs.readFileSync(envPath, 'utf-8')
|
|
for (const line of envContent.split('\n')) {
|
|
const trimmed = line.trim()
|
|
if (trimmed && !trimmed.startsWith('#')) {
|
|
const [key, ...valueParts] = trimmed.split('=')
|
|
if (key && valueParts.length > 0) {
|
|
// Map VITE_ prefixed vars to non-prefixed
|
|
const envKey = key.replace(/^VITE_/, '')
|
|
process.env[envKey] = valueParts.join('=')
|
|
}
|
|
}
|
|
}
|
|
console.log('[Config] Loaded .env from:', envPath)
|
|
}
|
|
|
|
// Configuration from environment
|
|
const ATM_PRIVATE_KEY_HEX = process.env.ATM_PRIVATE_KEY
|
|
const LIGHTNING_PUB_PUBKEY = process.env.LIGHTNING_PUB_PUBKEY
|
|
const RELAY_URL = process.env.RELAY_URL || 'ws://localhost:7777'
|
|
|
|
// Validate required config
|
|
if (!ATM_PRIVATE_KEY_HEX) {
|
|
console.error('ERROR: ATM_PRIVATE_KEY environment variable is required')
|
|
console.error('Set it directly or create apps/machine/.env with VITE_ATM_PRIVATE_KEY')
|
|
process.exit(1)
|
|
}
|
|
|
|
if (!LIGHTNING_PUB_PUBKEY) {
|
|
console.error('ERROR: LIGHTNING_PUB_PUBKEY environment variable is required')
|
|
console.error('Set it directly or create apps/machine/.env with VITE_LIGHTNING_PUB_PUBKEY')
|
|
process.exit(1)
|
|
}
|
|
|
|
const ATM_PRIVATE_KEY = Uint8Array.from(Buffer.from(ATM_PRIVATE_KEY_HEX, 'hex'))
|
|
const ATM_PUBLIC_KEY = getPublicKey(ATM_PRIVATE_KEY)
|
|
|
|
console.log('')
|
|
console.log('='.repeat(60))
|
|
console.log(' ATM Debit Approval Agent (TESTING ONLY)')
|
|
console.log('='.repeat(60))
|
|
console.log('')
|
|
console.log('⚠️ WARNING: Auto-approves ALL requests without validation!')
|
|
console.log('⚠️ For production, use the integrated service in apps/machine')
|
|
console.log('')
|
|
console.log('ATM Pubkey:', ATM_PUBLIC_KEY)
|
|
console.log('Lightning.Pub Pubkey:', LIGHTNING_PUB_PUBKEY)
|
|
console.log('Relay URL:', RELAY_URL)
|
|
console.log('')
|
|
|
|
async function main() {
|
|
// Connect to relay
|
|
console.log('Connecting to relay...')
|
|
const relay = await Relay.connect(RELAY_URL)
|
|
console.log('Connected!')
|
|
console.log('')
|
|
|
|
// Create conversation key for NIP-44 v1 encryption
|
|
const conversationKey = nip44v1.getConversationKey(ATM_PRIVATE_KEY_HEX, LIGHTNING_PUB_PUBKEY)
|
|
|
|
// Subscribe to GetLiveDebitRequests
|
|
console.log('Subscribing to live debit requests...')
|
|
|
|
const subscribeRequest = {
|
|
rpcName: 'GetLiveDebitRequests',
|
|
authIdentifier: ATM_PUBLIC_KEY,
|
|
body: {},
|
|
}
|
|
|
|
const subEvent = finalizeEvent(
|
|
{
|
|
kind: 21000,
|
|
created_at: Math.floor(Date.now() / 1000),
|
|
tags: [['p', LIGHTNING_PUB_PUBKEY]],
|
|
content: nip44v1.encrypt(JSON.stringify(subscribeRequest), conversationKey),
|
|
},
|
|
ATM_PRIVATE_KEY
|
|
)
|
|
|
|
// Listen for debit requests
|
|
console.log('Listening for debit requests...')
|
|
console.log('(Test by scanning ndebit QR with ShockWallet)')
|
|
console.log('')
|
|
|
|
const debitSub = relay.subscribe(
|
|
[
|
|
{
|
|
kinds: [21000],
|
|
authors: [LIGHTNING_PUB_PUBKEY],
|
|
'#p': [ATM_PUBLIC_KEY],
|
|
since: Math.floor(Date.now() / 1000) - 5,
|
|
},
|
|
],
|
|
{
|
|
async onevent(evt) {
|
|
try {
|
|
const decrypted = nip44v1.decrypt(evt.content, conversationKey)
|
|
const message = JSON.parse(decrypted)
|
|
|
|
// Check if this is a live debit request
|
|
if (message.requestId === 'GetLiveDebitRequests' && message.debit) {
|
|
console.log('')
|
|
console.log('========================================')
|
|
console.log('DEBIT REQUEST RECEIVED!')
|
|
console.log(' Request ID:', message.request_id)
|
|
console.log(' From npub:', message.npub?.substring(0, 16) + '...')
|
|
console.log(' Debit type:', message.debit.type)
|
|
|
|
if (message.debit.invoice) {
|
|
console.log(' Invoice:', message.debit.invoice.substring(0, 50) + '...')
|
|
|
|
// Auto-approve by responding with INVOICE type
|
|
console.log('')
|
|
console.log('⚠️ AUTO-APPROVING debit request (NO VALIDATION)...')
|
|
|
|
const approveRequest = {
|
|
rpcName: 'RespondToDebit',
|
|
authIdentifier: ATM_PUBLIC_KEY,
|
|
body: {
|
|
npub: message.npub,
|
|
request_id: message.request_id,
|
|
response: {
|
|
type: 'invoice',
|
|
invoice: message.debit.invoice,
|
|
},
|
|
},
|
|
}
|
|
|
|
const approveEvent = finalizeEvent(
|
|
{
|
|
kind: 21000,
|
|
created_at: Math.floor(Date.now() / 1000),
|
|
tags: [['p', LIGHTNING_PUB_PUBKEY]],
|
|
content: nip44v1.encrypt(JSON.stringify(approveRequest), conversationKey),
|
|
},
|
|
ATM_PRIVATE_KEY
|
|
)
|
|
|
|
await relay.publish(approveEvent)
|
|
console.log('APPROVED! (event id:', approveEvent.id.substring(0, 16) + '...)')
|
|
}
|
|
|
|
console.log('========================================')
|
|
console.log('')
|
|
} else if (message.rpcName) {
|
|
console.log('RPC response:', message.rpcName, ':', message.status || 'received')
|
|
} else if (message.requestId) {
|
|
console.log('Live subscription active:', message.status)
|
|
}
|
|
} catch (err) {
|
|
// Ignore decryption failures for events not meant for us
|
|
if (!err.message?.includes('Unsupported')) {
|
|
// Uncomment for debugging:
|
|
// console.log('Decryption error:', err.message)
|
|
}
|
|
}
|
|
},
|
|
}
|
|
)
|
|
|
|
await relay.publish(subEvent)
|
|
console.log('Subscription sent, waiting for debit requests...')
|
|
console.log('')
|
|
|
|
// Keep running
|
|
process.on('SIGINT', () => {
|
|
console.log('\nShutting down...')
|
|
debitSub.close()
|
|
relay.close()
|
|
process.exit(0)
|
|
})
|
|
|
|
// Heartbeat
|
|
while (true) {
|
|
await new Promise((r) => setTimeout(r, 30000))
|
|
console.log('Still listening...')
|
|
}
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error('Error:', err.message)
|
|
process.exit(1)
|
|
})
|