mirror of
https://github.com/nostr-connect/connect.git
synced 2025-12-17 05:04:20 +01:00
support for connect & disconnect
This commit is contained in:
@@ -5,9 +5,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>Playground</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
||||
</head>
|
||||
|
||||
<body style="background-color:lightgray;"}}>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="./index.tsx" type="module"></script>
|
||||
</body>
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
import 'react-app-polyfill/ie11';
|
||||
import * as React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { Session } from '../src/index';
|
||||
import { generatePrivateKey, getPublicKey, nip04, relayInit } from 'nostr-tools'
|
||||
|
||||
const App = () => {
|
||||
/* useEffect(() => {
|
||||
(async () => {
|
||||
})();
|
||||
}, []); */
|
||||
|
||||
const [walletKey, setWalletKey] = React.useState<{ pk: string; sk: string }>();
|
||||
const [sessionHolder, setSession] = React.useState<Session>();
|
||||
const [request, setRequest] = React.useState<any>();
|
||||
|
||||
|
||||
const newWallet = () => {
|
||||
//this is the wallet public key
|
||||
let sk = generatePrivateKey()
|
||||
let pk = getPublicKey(sk)
|
||||
setWalletKey({ pk, sk });
|
||||
}
|
||||
|
||||
const newSession = async () => {
|
||||
|
||||
const session = new Session({
|
||||
name: 'Auth',
|
||||
description: 'lorem ipsum dolor sit amet',
|
||||
url: 'https://vulpem.com',
|
||||
icons: ['https://vulpem.com/1000x860-p-500.422be1bc.png'],
|
||||
});
|
||||
|
||||
await session.pair(walletKey?.pk);
|
||||
setSession(session);
|
||||
};
|
||||
|
||||
const listen = async () => {
|
||||
if (!sessionHolder) return;
|
||||
|
||||
// let's query for an event to this wallet pub key
|
||||
const relay = relayInit('wss://nostr.vulpem.com');
|
||||
await relay.connect()
|
||||
|
||||
relay.on('connect', () => {
|
||||
console.log(`wallet: connected to ${relay.url}`)
|
||||
})
|
||||
relay.on('error', () => {
|
||||
console.log(`wallet: failed to connect to ${relay.url}`)
|
||||
})
|
||||
|
||||
let sub = relay.sub([{ kinds: [4] }])
|
||||
// on the receiver side
|
||||
sub.on('event', async (event) => {
|
||||
if (!walletKey) return;
|
||||
|
||||
const mention = event.tags.find(([k, v]) => k === 'p' && v && v !== '')[1]
|
||||
if (mention !== walletKey.pk) return;
|
||||
|
||||
const plaintext = await nip04.decrypt(walletKey?.sk, sessionHolder.pubKey, event.content);
|
||||
console.log('wallet', event.id, event.pubkey, JSON.parse(plaintext));
|
||||
setRequest(JSON.parse(plaintext));
|
||||
})
|
||||
|
||||
sub.on('eose', () => {
|
||||
sub.unsub()
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>💸 Wallet</h1>
|
||||
{walletKey && <p> 🔎 Wallet Pub: {walletKey?.pk} </p>}
|
||||
<button onClick={newWallet}>Create Wallet</button>
|
||||
<button disabled={!sessionHolder} onClick={listen}>Listen</button>
|
||||
{request && <div>
|
||||
<h1>📨 Incoming Request</h1>
|
||||
<img height={80} src={request?.icons[0]} />
|
||||
<p>
|
||||
Name <b>{request?.name}</b>
|
||||
</p>
|
||||
<p>
|
||||
Description <b>{request?.description}</b>
|
||||
</p>
|
||||
<p>
|
||||
URL: <b>{request?.url}</b>
|
||||
</p>
|
||||
</div>}
|
||||
<hr />
|
||||
<h1> App </h1>
|
||||
<button disabled={!walletKey} onClick={newSession}>New Session</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
@@ -1,57 +1,151 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useStatePersist } from 'use-state-persist';
|
||||
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { NostrRPC } from '../src/rpc';
|
||||
import { broadcastToRelay, Connect, connectToRelay, ConnectURI } from '../src';
|
||||
|
||||
import { QRCodeSVG } from 'qrcode.react';
|
||||
import { getEventHash, getPublicKey, Event } from 'nostr-tools';
|
||||
|
||||
class Server extends NostrRPC {
|
||||
async ping(): Promise<string> {
|
||||
return 'pong';
|
||||
}
|
||||
}
|
||||
const server = new Server({
|
||||
secretKey:
|
||||
'ed779ff047f99c95f732b22c9f8f842afb870c740aab591776ebc7b64e83cf6c',
|
||||
});
|
||||
const client = new NostrRPC({
|
||||
secretKey:
|
||||
'5acff99d1ad3e1706360d213fd69203312d9b5e91a2d5f2e06100cc6f686e5b3',
|
||||
const secretKey = "5acff99d1ad3e1706360d213fd69203312d9b5e91a2d5f2e06100cc6f686e5b3";
|
||||
const connectURI = new ConnectURI({
|
||||
target: getPublicKey(secretKey),
|
||||
relayURL: 'wss://nostr.vulpem.com',
|
||||
metadata: {
|
||||
name: 'Example',
|
||||
description: '🔉🔉🔉',
|
||||
url: 'https://example.com',
|
||||
icons: ['https://example.com/icon.png'],
|
||||
},
|
||||
});
|
||||
|
||||
const App = () => {
|
||||
|
||||
const [response, setResponse] = useState('');
|
||||
const [pubkey, setPubkey] = useStatePersist('@pubkey', '');
|
||||
const [getPublicKeyReply, setGetPublicKeyReply] = useState('');
|
||||
const [eventWithSig, setEvent] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const sub = await server.listen();
|
||||
sub.on('event', (evt) => {
|
||||
console.log('server received message', evt);
|
||||
const target = pubkey.length > 0 ? pubkey : undefined;
|
||||
const connect = new Connect({
|
||||
secretKey,
|
||||
target,
|
||||
});
|
||||
connect.events.on('connect', (pubkey: string) => {
|
||||
console.log('We are connected to ' + pubkey)
|
||||
setPubkey(pubkey);
|
||||
});
|
||||
connect.events.on('disconnect', () => {
|
||||
console.log('We got disconnected')
|
||||
setEvent({});
|
||||
setPubkey('');
|
||||
setGetPublicKeyReply('');
|
||||
});
|
||||
await connect.init();
|
||||
})();
|
||||
}, []);
|
||||
|
||||
|
||||
const makeCall = async () => {
|
||||
const result = await client.call({
|
||||
target: server.self.pubkey,
|
||||
request: { method: 'ping' },
|
||||
const getPub = async () => {
|
||||
if (pubkey.length === 0) return;
|
||||
const connect = new Connect({
|
||||
secretKey,
|
||||
target: pubkey,
|
||||
});
|
||||
setResponse(result);
|
||||
const pk = await connect.getPublicKey();
|
||||
setGetPublicKeyReply(pk);
|
||||
}
|
||||
|
||||
const sendMessage = async () => {
|
||||
if (pubkey.length === 0) return;
|
||||
|
||||
const connect = new Connect({
|
||||
secretKey,
|
||||
target: pubkey,
|
||||
});
|
||||
|
||||
let event: Event = {
|
||||
kind: 1,
|
||||
pubkey: pubkey,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [],
|
||||
content: "Running Nostr Connect 🔌"
|
||||
};
|
||||
event.id = getEventHash(event)
|
||||
event.sig = await connect.signEvent(event);
|
||||
const relay = await connectToRelay('wss://relay.damus.io');
|
||||
await broadcastToRelay(relay, event);
|
||||
|
||||
setEvent(event);
|
||||
}
|
||||
|
||||
const isConnected = () => {
|
||||
return pubkey.length > 0;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Nostr Connect Playground</h1>
|
||||
<p>Server pubkey: {server.self.pubkey}</p>
|
||||
<p>Client pubkey: {client.self.pubkey}</p>
|
||||
<hr />
|
||||
<button onClick={makeCall}>Ping</button>
|
||||
<br />
|
||||
<p> {response} </p>
|
||||
</div>
|
||||
<>
|
||||
<section className="container">
|
||||
<div className='content'>
|
||||
<h1 className='title'>Nostr Connect Playground</h1>
|
||||
</div>
|
||||
<div className='content'>
|
||||
<p className='subtitle is-6'><strong>Nostr ID</strong> {getPublicKey(secretKey)}</p>
|
||||
</div>
|
||||
<div className='content'>
|
||||
<p className='subtitle is-6'><strong>Status</strong> {isConnected() ? '🟢 Connected' : '🔴 Disconnected'}</p>
|
||||
</div>
|
||||
{!isConnected() && <div className='content has-text-centered'>
|
||||
<div className='notification is-info'>
|
||||
<h2 className='title is-5'>Connect with Nostr</h2>
|
||||
<QRCodeSVG value={connectURI.toString()} />
|
||||
<input
|
||||
className='input is-info'
|
||||
type='text'
|
||||
value={connectURI.toString()}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
</div>}
|
||||
</section>
|
||||
<section className="container mt-6">
|
||||
{
|
||||
isConnected() &&
|
||||
<>
|
||||
<div className='content'>
|
||||
<h2 className='title is-5'>Get Public Key</h2>
|
||||
<button className='button is-info' onClick={getPub}>
|
||||
Get public key
|
||||
</button>
|
||||
{getPublicKeyReply.length > 0 && <input
|
||||
className='input is-info mt-3'
|
||||
type='text'
|
||||
value={getPublicKeyReply}
|
||||
readOnly
|
||||
/>}
|
||||
</div>
|
||||
<div className='content'>
|
||||
<h2 className='title is-5'>Send a message with text <b>Running Nostr Connect 🔌</b></h2>
|
||||
<button className='button is-info' onClick={sendMessage}>
|
||||
Send message to Nostr
|
||||
</button>
|
||||
{
|
||||
Object.keys(eventWithSig).length > 0 &&
|
||||
<textarea
|
||||
className="textarea"
|
||||
readOnly
|
||||
rows={12}
|
||||
defaultValue={JSON.stringify(eventWithSig, null, 2)}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</section>
|
||||
</>
|
||||
|
||||
|
||||
)
|
||||
}
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"build": "parcel build index.html"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-app-polyfill": "^1.0.0"
|
||||
"qrcode.react": "^3.1.0",
|
||||
"react-app-polyfill": "^1.0.0",
|
||||
"use-state-persist": "^0.3.1"
|
||||
},
|
||||
"alias": {
|
||||
"react": "../node_modules/react",
|
||||
@@ -18,7 +20,10 @@
|
||||
"devDependencies": {
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"events": "^3.1.0",
|
||||
"parcel": "^2.8.2",
|
||||
"path-browserify": "^1.0.0",
|
||||
"process": "^0.11.10",
|
||||
"typescript": "^3.4.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1002,6 +1002,16 @@ escape-string-regexp@^1.0.5:
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
|
||||
|
||||
events@^3.1.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
||||
|
||||
fast-deep-equal@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
get-port@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119"
|
||||
@@ -1265,6 +1275,11 @@ parse-json@^5.0.0:
|
||||
json-parse-even-better-errors "^2.3.0"
|
||||
lines-and-columns "^1.1.6"
|
||||
|
||||
path-browserify@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
|
||||
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
|
||||
|
||||
path-type@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
@@ -1314,6 +1329,11 @@ posthtml@^0.16.4, posthtml@^0.16.5:
|
||||
posthtml-parser "^0.11.0"
|
||||
posthtml-render "^3.0.0"
|
||||
|
||||
process@^0.11.10:
|
||||
version "0.11.10"
|
||||
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
|
||||
|
||||
promise@^8.0.3:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a"
|
||||
@@ -1321,6 +1341,11 @@ promise@^8.0.3:
|
||||
dependencies:
|
||||
asap "~2.0.6"
|
||||
|
||||
qrcode.react@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-3.1.0.tgz#5c91ddc0340f768316fbdb8fff2765134c2aecd8"
|
||||
integrity sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==
|
||||
|
||||
raf@^3.4.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
|
||||
@@ -1458,6 +1483,13 @@ update-browserslist-db@^1.0.9:
|
||||
escalade "^3.1.1"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
use-state-persist@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/use-state-persist/-/use-state-persist-0.3.1.tgz#ab65aacfdeb4adcba96a7bba60d5bdd7c1531d7c"
|
||||
integrity sha512-bNU/9uZMNZDhFGSFfC2DOtsLvNU41FU2/87AVsC2kJuEKfqD4ksCdqGD9mUVfoGkahu4nZAMYvvmOVwpT2kTYw==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.3"
|
||||
|
||||
utility-types@^3.10.0:
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b"
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"events": "^3.3.0",
|
||||
"nostr-tools": "^1.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,7 @@ export interface Metadata {
|
||||
icons?: string[];
|
||||
}
|
||||
|
||||
export enum SessionStatus {
|
||||
Paired = 'paired',
|
||||
Unpaired = 'unpaired',
|
||||
}
|
||||
|
||||
export class ConnectURI {
|
||||
status: SessionStatus = SessionStatus.Unpaired;
|
||||
target: string;
|
||||
metadata: Metadata;
|
||||
relayURL: string;
|
||||
@@ -68,14 +62,13 @@ export class ConnectURI {
|
||||
relay: this.relayURL,
|
||||
secretKey,
|
||||
});
|
||||
const response = await rpc.call({
|
||||
await rpc.call({
|
||||
target: this.target,
|
||||
request: {
|
||||
method: 'connect',
|
||||
params: [getPublicKey(secretKey)],
|
||||
},
|
||||
});
|
||||
if (!response) throw new Error('Invalid response from remote');
|
||||
}, { skipResponse: true });
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -85,29 +78,22 @@ export class ConnectURI {
|
||||
relay: this.relayURL,
|
||||
secretKey,
|
||||
});
|
||||
const response = await rpc.call({
|
||||
await rpc.call({
|
||||
target: this.target,
|
||||
request: {
|
||||
method: 'disconnect',
|
||||
params: [],
|
||||
},
|
||||
});
|
||||
if (!response) throw new Error('Invalid response from remote');
|
||||
}, { skipResponse: true });
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export enum ConnectStatus {
|
||||
Connected = 'connected',
|
||||
Disconnected = 'disconnected',
|
||||
}
|
||||
|
||||
export class Connect {
|
||||
rpc: NostrRPC;
|
||||
target?: string;
|
||||
events = new EventEmitter();
|
||||
status = ConnectStatus.Disconnected;
|
||||
|
||||
constructor({
|
||||
target,
|
||||
@@ -121,7 +107,6 @@ export class Connect {
|
||||
this.rpc = new NostrRPC({ relay, secretKey });
|
||||
if (target) {
|
||||
this.target = target;
|
||||
this.status = ConnectStatus.Connected;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,19 +125,24 @@ export class Connect {
|
||||
} catch (ignore) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore all the events that are not NostrRPCRequest events
|
||||
if (!isValidRequest(payload)) return;
|
||||
|
||||
// ignore all the request that are not connect
|
||||
if (payload.method !== 'connect') return;
|
||||
|
||||
// ignore all the request that are not for us
|
||||
if (!payload.params || payload.params.length !== 1) return;
|
||||
const [pubkey] = payload.params;
|
||||
|
||||
this.status = ConnectStatus.Connected;
|
||||
this.target = pubkey;
|
||||
this.events.emit('connect', pubkey);
|
||||
switch (payload.method) {
|
||||
case 'connect':
|
||||
if (!payload.params || payload.params.length !== 1) return;
|
||||
const [pubkey] = payload.params;
|
||||
this.target = pubkey;
|
||||
this.events.emit('connect', pubkey);
|
||||
return
|
||||
case 'disconnect':
|
||||
this.target = undefined;
|
||||
this.events.emit('disconnect');
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -163,12 +153,8 @@ export class Connect {
|
||||
this.events.off(evt, cb);
|
||||
}
|
||||
|
||||
private isConnected() {
|
||||
return this.status === ConnectStatus.Connected;
|
||||
}
|
||||
|
||||
async getPublicKey(): Promise<string> {
|
||||
if (!this.target || !this.isConnected()) throw new Error('Not connected');
|
||||
if (!this.target) throw new Error('Not connected');
|
||||
|
||||
const response = await this.rpc.call({
|
||||
target: this.target,
|
||||
@@ -180,8 +166,19 @@ export class Connect {
|
||||
return response as string;
|
||||
}
|
||||
|
||||
async signEvent(_event: Event): Promise<Event> {
|
||||
throw new Error('Not implemented');
|
||||
async signEvent(event: Event): Promise<string> {
|
||||
if (!this.target) throw new Error('Not connected');
|
||||
|
||||
const signature = await this.rpc.call({
|
||||
target: this.target,
|
||||
request: {
|
||||
method: 'sign_event',
|
||||
params: [event],
|
||||
},
|
||||
});
|
||||
console.log('signature', signature);
|
||||
|
||||
return signature as string;
|
||||
}
|
||||
|
||||
async getRelays(): Promise<{
|
||||
|
||||
76
src/rpc.ts
76
src/rpc.ts
@@ -9,6 +9,7 @@ import {
|
||||
Event,
|
||||
Sub,
|
||||
Filter,
|
||||
Relay,
|
||||
} from 'nostr-tools';
|
||||
|
||||
export interface NostrRPCRequest {
|
||||
@@ -46,38 +47,19 @@ export class NostrRPC {
|
||||
method: string;
|
||||
params?: any[];
|
||||
};
|
||||
}): Promise<any> {
|
||||
const relay = await relayInit(this.relay);
|
||||
}, opts?: { skipResponse?: boolean, timeout?: number }): Promise<any> {
|
||||
// connect to relay
|
||||
const relay = await connectToRelay(this.relay);
|
||||
|
||||
// prepare request to be sent
|
||||
const request = prepareRequest(id, method, params);
|
||||
const event = await prepareEvent(this.self.secret, target, request);
|
||||
|
||||
// connect to relay
|
||||
await relay.connect();
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
relay.on('connect', () => {
|
||||
resolve();
|
||||
});
|
||||
relay.on('error', () => {
|
||||
reject(`not possible to connect to ${relay.url}`);
|
||||
});
|
||||
});
|
||||
|
||||
// send request via relay
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
relay.on('error', () => {
|
||||
reject(`failed to connect to ${relay.url}`);
|
||||
});
|
||||
const pub = relay.publish(event);
|
||||
pub.on('failed', (reason: any) => {
|
||||
reject(reason);
|
||||
});
|
||||
pub.on('seen', () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
await broadcastToRelay(relay, event);
|
||||
|
||||
// waiting for response from remote
|
||||
if (opts && opts.skipResponse === true) return Promise.resolve();
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
let sub = relay.sub([
|
||||
{
|
||||
@@ -122,16 +104,7 @@ export class NostrRPC {
|
||||
}
|
||||
|
||||
async listen(): Promise<Sub> {
|
||||
const relay = relayInit(this.relay);
|
||||
await relay.connect();
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
relay.on('connect', () => {
|
||||
resolve();
|
||||
});
|
||||
relay.on('error', () => {
|
||||
reject(`not possible to connect to ${relay.url}`);
|
||||
});
|
||||
});
|
||||
const relay = await connectToRelay(this.relay);
|
||||
|
||||
let sub = relay.sub([
|
||||
{
|
||||
@@ -159,6 +132,7 @@ export class NostrRPC {
|
||||
if (!isValidRequest(payload)) return;
|
||||
|
||||
// handle request
|
||||
if (typeof this[payload.method] !== 'function') Promise.resolve();
|
||||
const response = await this.handleRequest(payload);
|
||||
|
||||
const body = prepareResponse(
|
||||
@@ -292,3 +266,33 @@ export function isValidResponse(payload: any): boolean {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function connectToRelay(realayURL: string) {
|
||||
const relay = relayInit(realayURL);
|
||||
await relay.connect();
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
relay.on('connect', () => {
|
||||
resolve();
|
||||
});
|
||||
relay.on('error', () => {
|
||||
reject(`not possible to connect to ${relay.url}`);
|
||||
});
|
||||
});
|
||||
return relay;
|
||||
}
|
||||
export async function broadcastToRelay(relay: Relay, event: Event) {
|
||||
// send request via relay
|
||||
return await new Promise<void>((resolve, reject) => {
|
||||
relay.on('error', () => {
|
||||
reject(`failed to connect to ${relay.url}`);
|
||||
});
|
||||
const pub = relay.publish(event);
|
||||
pub.on('failed', (reason: any) => {
|
||||
reject(reason);
|
||||
});
|
||||
pub.on('seen', () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2984,6 +2984,11 @@ esutils@^2.0.2:
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
|
||||
|
||||
events@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
||||
|
||||
exec-sh@^0.3.2:
|
||||
version "0.3.6"
|
||||
resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc"
|
||||
|
||||
Reference in New Issue
Block a user