All checks were successful
Open PR on Staging / audit_and_check (pull_request) Successful in 34s
Also remove the getMostPopularRelays function and use a hardcoded list of relays Further, if metadata event is not found from relays cache an empty metadata for that pubkey
246 lines
6.3 KiB
TypeScript
246 lines
6.3 KiB
TypeScript
import { Event, Filter, kinds, UnsignedEvent } from 'nostr-tools'
|
|
import { RelayList } from 'nostr-tools/kinds'
|
|
import { getRelayInfo, unixNow } from '.'
|
|
import { NostrController, relayController } from '../controllers'
|
|
import { localCache } from '../services'
|
|
import { RelayMap, RelaySet } from '../types'
|
|
import {
|
|
DEFAULT_LOOK_UP_RELAY_LIST,
|
|
ONE_WEEK_IN_MS,
|
|
SIGIT_RELAY
|
|
} from './const'
|
|
|
|
const READ_MARKER = 'read'
|
|
const WRITE_MARKER = 'write'
|
|
|
|
/**
|
|
* Attempts to find a relay list from the provided lookUpRelays.
|
|
* If the relay list is found, it will be added to the user relay list metadata.
|
|
* @param lookUpRelays
|
|
* @param hexKey
|
|
* @return found relay list or null
|
|
*/
|
|
const findRelayListAndUpdateCache = async (
|
|
lookUpRelays: string[],
|
|
hexKey: string
|
|
): Promise<Event | null> => {
|
|
try {
|
|
const eventFilter: Filter = {
|
|
kinds: [RelayList],
|
|
authors: [hexKey]
|
|
}
|
|
|
|
console.count('findRelayListAndUpdateCache')
|
|
const event = await relayController.fetchEvent(eventFilter, lookUpRelays)
|
|
if (event) {
|
|
await localCache.addUserRelayListMetadata(event)
|
|
}
|
|
return event
|
|
} catch (error) {
|
|
console.error(error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempts to find a relay list in cache. If it is present, it will check that the cached event is not
|
|
* older than one week.
|
|
* @param hexKey
|
|
* @return RelayList event if it's not older than a week; otherwise null
|
|
*/
|
|
const findRelayListInCache = async (hexKey: string): Promise<Event | null> => {
|
|
try {
|
|
// Attempt to retrieve the metadata event from the local cache
|
|
const cachedRelayListMetadataEvent =
|
|
await localCache.getUserRelayListMetadata(hexKey)
|
|
|
|
// Check if the cached event is not older than one week
|
|
if (
|
|
cachedRelayListMetadataEvent &&
|
|
isOlderThanOneWeek(cachedRelayListMetadataEvent.cachedAt)
|
|
) {
|
|
return cachedRelayListMetadataEvent.event
|
|
}
|
|
|
|
return null
|
|
} catch (error) {
|
|
console.error(error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Transforms a list of relay tags from a Nostr Event to a RelaySet.
|
|
* @param tags
|
|
*/
|
|
const getUserRelaySet = (tags: string[][]): RelaySet => {
|
|
return tags
|
|
.filter(isRelayTag)
|
|
.reduce<RelaySet>(toRelaySet, getDefaultRelaySet())
|
|
}
|
|
|
|
const getDefaultRelaySet = (): RelaySet => ({
|
|
read: [SIGIT_RELAY],
|
|
write: [SIGIT_RELAY]
|
|
})
|
|
|
|
const getDefaultRelayMap = (): RelayMap => ({
|
|
[SIGIT_RELAY]: { write: true, read: true }
|
|
})
|
|
|
|
const isOlderThanOneWeek = (cachedAt: number) => {
|
|
return Date.now() - cachedAt < ONE_WEEK_IN_MS
|
|
}
|
|
|
|
const isOlderThanOneDay = (cachedAt: number) => {
|
|
return Date.now() - cachedAt < ONE_WEEK_IN_MS
|
|
}
|
|
|
|
const isRelayTag = (tag: string[]): boolean => tag[0] === 'r'
|
|
|
|
const toRelaySet = (obj: RelaySet, tag: string[]): RelaySet => {
|
|
if (tag.length >= 3) {
|
|
const marker = tag[2]
|
|
|
|
if (marker === READ_MARKER) {
|
|
obj.read.push(tag[1])
|
|
} else if (marker === WRITE_MARKER) {
|
|
obj.write.push(tag[1])
|
|
}
|
|
}
|
|
if (tag.length === 2) {
|
|
obj.read.push(tag[1])
|
|
obj.write.push(tag[1])
|
|
}
|
|
|
|
return obj
|
|
}
|
|
|
|
/**
|
|
* Provides relay map.
|
|
* @param npub - user's npub
|
|
* @returns - promise that resolves into relay map and a timestamp when it has been updated.
|
|
*/
|
|
const getRelayMap = async (
|
|
npub: string
|
|
): Promise<{ map: RelayMap; mapUpdated?: number }> => {
|
|
// More info about this kind of event available https://github.com/nostr-protocol/nips/blob/master/65.md
|
|
const eventFilter: Filter = {
|
|
kinds: [kinds.RelayList],
|
|
authors: [npub]
|
|
}
|
|
|
|
console.count('getRelayMap')
|
|
const event = await relayController
|
|
.fetchEvent(eventFilter, DEFAULT_LOOK_UP_RELAY_LIST)
|
|
.catch((err) => {
|
|
return Promise.reject(err)
|
|
})
|
|
|
|
if (event) {
|
|
// Handle founded 10002 event
|
|
const relaysMap: RelayMap = {}
|
|
|
|
// 'r' stands for 'relay'
|
|
const relayTags = event.tags.filter((tag) => tag[0] === 'r')
|
|
|
|
relayTags.forEach((tag) => {
|
|
const uri = tag[1]
|
|
const relayType = tag[2]
|
|
|
|
// if 3rd element of relay tag is undefined, relay is WRITE and READ
|
|
relaysMap[uri] = {
|
|
write: relayType ? relayType === 'write' : true,
|
|
read: relayType ? relayType === 'read' : true
|
|
}
|
|
})
|
|
|
|
Object.keys(relaysMap).forEach((relayUrl) => {
|
|
console.log('being called from getRelayMap')
|
|
relayController.connectRelay(relayUrl)
|
|
})
|
|
|
|
getRelayInfo(Object.keys(relaysMap))
|
|
|
|
return Promise.resolve({ map: relaysMap, mapUpdated: event.created_at })
|
|
} else {
|
|
return Promise.resolve({ map: getDefaultRelayMap() })
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Publishes relay map.
|
|
* @param relayMap - relay map.
|
|
* @param npub - user's npub.
|
|
* @param extraRelaysToPublish - optional relays to publish relay map.
|
|
* @returns - promise that resolves into a string representing publishing result.
|
|
*/
|
|
const publishRelayMap = async (
|
|
relayMap: RelayMap,
|
|
npub: string,
|
|
extraRelaysToPublish?: string[]
|
|
): Promise<string> => {
|
|
const timestamp = unixNow()
|
|
const relayURIs = Object.keys(relayMap)
|
|
|
|
// More info about this kind of event available https://github.com/nostr-protocol/nips/blob/master/65.md
|
|
const tags: string[][] = relayURIs.map((relayURI) =>
|
|
[
|
|
'r',
|
|
relayURI,
|
|
relayMap[relayURI].read && relayMap[relayURI].write
|
|
? ''
|
|
: relayMap[relayURI].write
|
|
? 'write'
|
|
: 'read'
|
|
].filter((value) => value !== '')
|
|
)
|
|
|
|
const newRelayMapEvent: UnsignedEvent = {
|
|
kind: kinds.RelayList,
|
|
tags,
|
|
content: '',
|
|
pubkey: npub,
|
|
created_at: timestamp
|
|
}
|
|
|
|
const nostrController = NostrController.getInstance()
|
|
const signedEvent = await nostrController.signEvent(newRelayMapEvent)
|
|
|
|
let relaysToPublish = relayURIs
|
|
|
|
// Add extra relays if provided
|
|
if (extraRelaysToPublish) {
|
|
relaysToPublish = [...relaysToPublish, ...extraRelaysToPublish]
|
|
}
|
|
|
|
// If relay map is empty, use most popular relay URIs
|
|
if (!relaysToPublish.length) {
|
|
relaysToPublish = DEFAULT_LOOK_UP_RELAY_LIST
|
|
}
|
|
const publishResult = await relayController.publish(
|
|
signedEvent,
|
|
relaysToPublish
|
|
)
|
|
|
|
if (publishResult && publishResult.length) {
|
|
return Promise.resolve(
|
|
`Relay Map published on: ${publishResult.join('\n')}`
|
|
)
|
|
}
|
|
|
|
return Promise.reject('Publishing updated relay map was unsuccessful.')
|
|
}
|
|
|
|
export {
|
|
findRelayListAndUpdateCache,
|
|
findRelayListInCache,
|
|
getDefaultRelayMap,
|
|
getDefaultRelaySet,
|
|
getRelayMap,
|
|
getUserRelaySet,
|
|
isOlderThanOneDay,
|
|
isOlderThanOneWeek,
|
|
publishRelayMap
|
|
}
|