mirror of
https://github.com/aljazceru/nostr-profile-manager.git
synced 2025-12-20 23:14:21 +01:00
connect to blastr and my relays or multiple default relays
This commit is contained in:
@@ -6,7 +6,7 @@ Only javascript dependancy is [nostr-tools](https://github.com/nbd-wtf/nostr-too
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
Supported profile events: kind `0`, `2`, `10002` and `3`.
|
Supported profile events: kind `0`, `10002` and `3`.
|
||||||
|
|
||||||
##### Backup and Restore
|
##### Backup and Restore
|
||||||
|
|
||||||
@@ -44,7 +44,6 @@ Supported profile events: kind `0`, `2`, `10002` and `3`.
|
|||||||
##### Lightweight and Efficent
|
##### Lightweight and Efficent
|
||||||
- [ ] only javascript dependancy is nostr-tools (TODO: remove timeago)
|
- [ ] only javascript dependancy is nostr-tools (TODO: remove timeago)
|
||||||
- [x] connects to the minimum number of relays
|
- [x] connects to the minimum number of relays
|
||||||
- [ ] connect relays specified in `10002` or `2`
|
- [x] connect relays specified in `10002` or 3 default relays
|
||||||
- [ ] if no `10002` or `2` events are found it crawls through a number of popular relays to ensure it has your latest profile events. (currently it just connects to damus)
|
|
||||||
- [x] minimises the number of open websockets
|
- [x] minimises the number of open websockets
|
||||||
- [ ] use blastr relay to send profile events far and wide
|
- [x] use blastr relay to send profile events far and wide
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
import { Event, Relay, relayInit } from 'nostr-tools';
|
import { Event, SimplePool } from 'nostr-tools';
|
||||||
|
|
||||||
let drelay: Relay;
|
const pool = new SimplePool();
|
||||||
export const setupDefaultRelays = async ():Promise<void> => {
|
let currentrelays = [
|
||||||
if (typeof drelay !== 'undefined') return new Promise((r) => { r(); });
|
'wss://relay.damus.io',
|
||||||
drelay = relayInit('wss://relay.damus.io');
|
'wss://nostr-pub.wellorder.net',
|
||||||
return drelay.connect();
|
'wss://nostr-relay.wlvs.space',
|
||||||
};
|
];
|
||||||
/** setupMyRelays TODO */
|
|
||||||
export const setupMyRelays = async () => setupDefaultRelays();
|
|
||||||
|
|
||||||
export const requestMyProfileFromRelays = async (
|
export const requestMyProfileFromRelays = async (
|
||||||
pubkey:string,
|
pubkey:string,
|
||||||
eventProcesser: (event: Event) => void,
|
eventProcesser: (event: Event) => void,
|
||||||
|
relays?:string[],
|
||||||
) => {
|
) => {
|
||||||
await setupDefaultRelays();
|
if (relays) currentrelays = relays;
|
||||||
const sub = drelay.sub([{
|
const sub = pool.sub(
|
||||||
|
currentrelays,
|
||||||
|
[{
|
||||||
kinds: [0, 2, 10002, 3],
|
kinds: [0, 2, 10002, 3],
|
||||||
authors: [pubkey],
|
authors: [pubkey],
|
||||||
}]);
|
}],
|
||||||
|
);
|
||||||
return new Promise<void>((r) => {
|
return new Promise<void>((r) => {
|
||||||
sub.on('event', (event:Event) => {
|
sub.on('event', (event:Event) => {
|
||||||
if (
|
if (
|
||||||
@@ -28,15 +30,13 @@ export const requestMyProfileFromRelays = async (
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
sub.on('eose', () => {
|
sub.on('eose', () => {
|
||||||
// sub.unsub();
|
|
||||||
r();
|
r();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const publishEventToRelay = async (event:Event):Promise<boolean> => {
|
export const publishEventToRelay = async (event:Event):Promise<boolean> => {
|
||||||
await setupDefaultRelays();
|
const pub = pool.publish(currentrelays, event);
|
||||||
const pub = drelay.publish(event);
|
|
||||||
return new Promise((r) => {
|
return new Promise((r) => {
|
||||||
pub.on('ok', () => r(true));
|
pub.on('ok', () => r(true));
|
||||||
pub.on('failed', () => r(false));
|
pub.on('failed', () => r(false));
|
||||||
|
|||||||
@@ -205,6 +205,122 @@ describe('', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('when isUptodate returns false', () => {
|
describe('when isUptodate returns false', () => {
|
||||||
|
describe('and when cached 10002 event is present', () => {
|
||||||
|
const doBefore = async () => {
|
||||||
|
mockisUptodate.mockReturnValue(false);
|
||||||
|
mockrequestMyProfileFromRelays.mockReset()
|
||||||
|
.mockImplementation(async (_pubkey, eventProcessor) => {
|
||||||
|
eventProcessor({ ...SampleEvents.kind0 });
|
||||||
|
eventProcessor({ ...SampleEvents.kind3 });
|
||||||
|
});
|
||||||
|
await fetchMyProfileEvents(SampleEvents.kind0.pubkey, mockEventProcessor);
|
||||||
|
};
|
||||||
|
test('1 write relays, function called with custom relay and 2 default relays + blaster', async () => {
|
||||||
|
storeMyProfileEvent({
|
||||||
|
...SampleEvents.kind10002,
|
||||||
|
tags: [
|
||||||
|
['r', 'wss://alicerelay.example.com'],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await doBefore();
|
||||||
|
expect(mockrequestMyProfileFromRelays).toBeCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
expect.anything(),
|
||||||
|
[
|
||||||
|
'wss://alicerelay.example.com',
|
||||||
|
'wss://relay.damus.io',
|
||||||
|
'wss://nostr-pub.wellorder.net',
|
||||||
|
'wss://nostr.mutinywallet.com',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('2 write relays function called with custom relays and 1 default relays + blaster', async () => {
|
||||||
|
storeMyProfileEvent({
|
||||||
|
...SampleEvents.kind10002,
|
||||||
|
tags: [
|
||||||
|
['r', 'wss://alicerelay.example.com'],
|
||||||
|
['r', 'wss://expensive-relay.example2.com', 'write'],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await doBefore();
|
||||||
|
expect(mockrequestMyProfileFromRelays).toBeCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
expect.anything(),
|
||||||
|
[
|
||||||
|
'wss://alicerelay.example.com',
|
||||||
|
'wss://expensive-relay.example2.com',
|
||||||
|
'wss://relay.damus.io',
|
||||||
|
'wss://nostr.mutinywallet.com',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('2 write relays including first defauly relay. function called with custom relays and 1 different default relays + blaster', async () => {
|
||||||
|
storeMyProfileEvent({
|
||||||
|
...SampleEvents.kind10002,
|
||||||
|
tags: [
|
||||||
|
['r', 'wss://relay.damus.io'],
|
||||||
|
['r', 'wss://expensive-relay.example2.com', 'write'],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await doBefore();
|
||||||
|
expect(mockrequestMyProfileFromRelays).toBeCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
expect.anything(),
|
||||||
|
[
|
||||||
|
'wss://relay.damus.io',
|
||||||
|
'wss://expensive-relay.example2.com',
|
||||||
|
'wss://nostr-pub.wellorder.net',
|
||||||
|
'wss://nostr.mutinywallet.com',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('with 4 write relays function called with all custom relays + blaster', async () => {
|
||||||
|
storeMyProfileEvent({
|
||||||
|
...SampleEvents.kind10002,
|
||||||
|
tags: [
|
||||||
|
['r', 'wss://alicerelay.example.com'],
|
||||||
|
['r', 'wss://brando-relay.com'],
|
||||||
|
['r', 'wss://expensive-relay.example2.com', 'write'],
|
||||||
|
['r', 'wss://alicerelay.example3.com'],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await doBefore();
|
||||||
|
expect(mockrequestMyProfileFromRelays).toBeCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
expect.anything(),
|
||||||
|
[
|
||||||
|
'wss://alicerelay.example.com',
|
||||||
|
'wss://brando-relay.com',
|
||||||
|
'wss://expensive-relay.example2.com',
|
||||||
|
'wss://alicerelay.example3.com',
|
||||||
|
'wss://nostr.mutinywallet.com',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('custom read relays ignored', async () => {
|
||||||
|
storeMyProfileEvent({
|
||||||
|
...SampleEvents.kind10002,
|
||||||
|
tags: [
|
||||||
|
['r', 'wss://alicerelay.example.com'],
|
||||||
|
['r', 'wss://brando-relay.com'],
|
||||||
|
['r', 'wss://expensive-relay.example2.com', 'write'],
|
||||||
|
['r', 'wss://nostr-relay.example.com', 'read'],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await doBefore();
|
||||||
|
expect(mockrequestMyProfileFromRelays).toBeCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
expect.anything(),
|
||||||
|
[
|
||||||
|
'wss://alicerelay.example.com',
|
||||||
|
'wss://brando-relay.com',
|
||||||
|
'wss://expensive-relay.example2.com',
|
||||||
|
'wss://nostr.mutinywallet.com',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('and when no cached 10002 events are present', () => {
|
||||||
const mockstoreMyProfileEvent = jest.spyOn(FetchEvents, 'storeMyProfileEvent');
|
const mockstoreMyProfileEvent = jest.spyOn(FetchEvents, 'storeMyProfileEvent');
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
mockisUptodate.mockReturnValue(false);
|
mockisUptodate.mockReturnValue(false);
|
||||||
@@ -218,8 +334,8 @@ describe('', () => {
|
|||||||
test('updateLastFetchDate called once', () => {
|
test('updateLastFetchDate called once', () => {
|
||||||
expect(mockupdateLastFetchDate).toBeCalledTimes(1);
|
expect(mockupdateLastFetchDate).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
test('fetchCachedProfileEvent never called', () => {
|
test('fetchCachedProfileEvent only to be called once to getRelays', () => {
|
||||||
expect(fetchCachedProfileEventSpy).toBeCalledTimes(0);
|
expect(fetchCachedProfileEventSpy).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
test('requestMyProfileFromRelays called', () => {
|
test('requestMyProfileFromRelays called', () => {
|
||||||
expect(mockrequestMyProfileFromRelays).toBeCalledTimes(1);
|
expect(mockrequestMyProfileFromRelays).toBeCalledTimes(1);
|
||||||
@@ -228,6 +344,19 @@ describe('', () => {
|
|||||||
expect(mockrequestMyProfileFromRelays).toBeCalledWith(
|
expect(mockrequestMyProfileFromRelays).toBeCalledWith(
|
||||||
SampleEvents.kind0.pubkey,
|
SampleEvents.kind0.pubkey,
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
|
expect.anything(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('mockrequestMyProfileFromRelays called with correct default relays', () => {
|
||||||
|
expect(mockrequestMyProfileFromRelays).toBeCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
expect.anything(),
|
||||||
|
[
|
||||||
|
'wss://relay.damus.io',
|
||||||
|
'wss://nostr-pub.wellorder.net',
|
||||||
|
'wss://nostr-relay.wlvs.space',
|
||||||
|
'wss://nostr.mutinywallet.com',
|
||||||
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
test('eventProcessor called with events passed through by requestMyProfileFromRelays\'s event processor', async () => {
|
test('eventProcessor called with events passed through by requestMyProfileFromRelays\'s event processor', async () => {
|
||||||
@@ -243,4 +372,5 @@ describe('', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -92,6 +92,21 @@ export const fetchCachedProfileEvent = (kind: 0 | 2 | 10002 | 3): null | Event =
|
|||||||
return a[0];
|
return a[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getRelays = () => {
|
||||||
|
const e = fetchCachedProfileEvent(10002);
|
||||||
|
const mywriterelays = !e ? [] : e.tags.filter((r) => !r[2] || r[2] === 'write').map((r) => r[1]);
|
||||||
|
// return minimum of 3 relays + blastr, filling in with default relays (removing duplicates)
|
||||||
|
return [
|
||||||
|
...(mywriterelays.length > 3 ? mywriterelays : [...new Set([
|
||||||
|
...mywriterelays,
|
||||||
|
'wss://relay.damus.io',
|
||||||
|
'wss://nostr-pub.wellorder.net',
|
||||||
|
'wss://nostr-relay.wlvs.space',
|
||||||
|
])].slice(0, 3)),
|
||||||
|
'wss://nostr.mutinywallet.com', // blastr
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
/** get my latest profile events either from cache (if isUptodate) or from relays */
|
/** get my latest profile events either from cache (if isUptodate) or from relays */
|
||||||
export const fetchMyProfileEvents = async (
|
export const fetchMyProfileEvents = async (
|
||||||
pubkey:string,
|
pubkey:string,
|
||||||
@@ -99,14 +114,10 @@ export const fetchMyProfileEvents = async (
|
|||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
// get events from relays, store them and run profileEventProcesser
|
// get events from relays, store them and run profileEventProcesser
|
||||||
if (!isUptodate()) {
|
if (!isUptodate()) {
|
||||||
/**
|
|
||||||
* TODO also run this if we havn't checked for x minutes and we aren't already
|
|
||||||
* listening on my write relays
|
|
||||||
*/
|
|
||||||
await requestMyProfileFromRelays(pubkey, (event: Event) => {
|
await requestMyProfileFromRelays(pubkey, (event: Event) => {
|
||||||
storeMyProfileEvent(event);
|
storeMyProfileEvent(event);
|
||||||
profileEventProcesser(event);
|
profileEventProcesser(event);
|
||||||
});
|
}, getRelays());
|
||||||
// update last-fetch-from-relays date
|
// update last-fetch-from-relays date
|
||||||
updateLastFetchDate();
|
updateLastFetchDate();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
10
src/index.ts
10
src/index.ts
@@ -1,7 +1,6 @@
|
|||||||
import { Event, UnsignedEvent } from 'nostr-tools';
|
import { Event, UnsignedEvent } from 'nostr-tools';
|
||||||
import { generateLogoHero, LoadProfileHome } from './LoadProfileHome';
|
import { generateLogoHero, LoadProfileHome } from './LoadProfileHome';
|
||||||
import { setupDefaultRelays, setupMyRelays } from './RelayManagement';
|
import { fetchMyProfileEvents } from './fetchEvents';
|
||||||
import { fetchCachedProfileEvent, fetchMyProfileEvents } from './fetchEvents';
|
|
||||||
import { localStorageGetItem, localStorageSetItem } from './LocalStorage';
|
import { localStorageGetItem, localStorageSetItem } from './LocalStorage';
|
||||||
import { LoadMetadataPage } from './LoadMetadataPage';
|
import { LoadMetadataPage } from './LoadMetadataPage';
|
||||||
import LoadContactsPage from './LoadContactsPage';
|
import LoadContactsPage from './LoadContactsPage';
|
||||||
@@ -25,13 +24,6 @@ const loadProfile = async () => {
|
|||||||
(document.getElementById('navrelays') as HTMLElement).onclick = LoadRelaysPage;
|
(document.getElementById('navrelays') as HTMLElement).onclick = LoadRelaysPage;
|
||||||
// load profile page (in loading mode)
|
// load profile page (in loading mode)
|
||||||
LoadProfileHome();
|
LoadProfileHome();
|
||||||
// if my relays are known, connect to them
|
|
||||||
if (
|
|
||||||
fetchCachedProfileEvent(10002) !== null
|
|
||||||
|| fetchCachedProfileEvent(2) !== null
|
|
||||||
) await setupMyRelays();
|
|
||||||
// otherwise connect to default relays
|
|
||||||
else await setupDefaultRelays();
|
|
||||||
// load profile data
|
// load profile data
|
||||||
await fetchMyProfileEvents(
|
await fetchMyProfileEvents(
|
||||||
localStorageGetItem('pubkey') as string,
|
localStorageGetItem('pubkey') as string,
|
||||||
|
|||||||
Reference in New Issue
Block a user