Re-work address
This commit is contained in:
+56
-80
@@ -1,85 +1,61 @@
|
||||
import type {UnsignedEvent} from 'nostr-tools'
|
||||
import {nip19} from 'nostr-tools'
|
||||
import {GROUP_DEFINITION, COMMUNITY_DEFINITION} from './Kinds'
|
||||
import {Tags} from './Tags'
|
||||
|
||||
export const isGroupAddress = (a: string) => a.startsWith(`${GROUP_DEFINITION}:`)
|
||||
|
||||
export const isCommunityAddress = (a: string) => a.startsWith(`${COMMUNITY_DEFINITION}:`)
|
||||
|
||||
export const isContextAddress = (a: string) => isCommunityAddress(a) || isGroupAddress(a)
|
||||
|
||||
export class Address {
|
||||
readonly kind: number
|
||||
|
||||
constructor(
|
||||
kind: string | number,
|
||||
readonly pubkey: string,
|
||||
readonly identifier: string,
|
||||
readonly relays: string[],
|
||||
) {
|
||||
this.kind = parseInt(kind as string)
|
||||
this.identifier = identifier || ""
|
||||
}
|
||||
|
||||
static getKind = (a: string) => Address.fromRaw(a, []).kind
|
||||
|
||||
static getPubkey = (a: string) => Address.fromRaw(a, []).pubkey
|
||||
|
||||
static getIdentifier = (a: string) => Address.fromRaw(a, []).identifier
|
||||
|
||||
static fromEvent = (e: UnsignedEvent, relays: string[]) =>
|
||||
new Address(e.kind, e.pubkey, Tags.fromEvent(e).get("d")?.value() || "", relays)
|
||||
|
||||
static fromRaw = (a: string, relays: string[]) => {
|
||||
const [kind, pubkey, identifier] = a.split(":")
|
||||
|
||||
return new Address(kind, pubkey, identifier, relays)
|
||||
}
|
||||
|
||||
static fromTag = (tag: string[]) => {
|
||||
const [a, hint] = tag.slice(1)
|
||||
const relays = hint ? [hint] : []
|
||||
|
||||
return this.fromRaw(a, relays)
|
||||
}
|
||||
|
||||
static fromNaddr = (naddr: string) => {
|
||||
let type
|
||||
let data = {} as any
|
||||
try {
|
||||
({type, data} = nip19.decode(naddr) as {
|
||||
type: "naddr"
|
||||
data: any
|
||||
})
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
|
||||
if (type !== "naddr") {
|
||||
throw new Error(`Invalid naddr ${naddr}`)
|
||||
}
|
||||
|
||||
return new Address(data.kind, data.pubkey, data.identifier, data.relays)
|
||||
}
|
||||
|
||||
asRaw = () => [this.kind, this.pubkey, this.identifier].join(":")
|
||||
|
||||
asTag = (mark?: string) => {
|
||||
const tag = ["a", this.asRaw(), this.relays[0] || ""]
|
||||
|
||||
if (mark) {
|
||||
tag.push(mark)
|
||||
}
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
asNaddr = () => nip19.naddrEncode(this)
|
||||
|
||||
asFilter = () => ({
|
||||
kinds: [this.kind],
|
||||
authors: [this.pubkey],
|
||||
"#d": [this.identifier],
|
||||
})
|
||||
export type Address = {
|
||||
kind: number,
|
||||
pubkey: string
|
||||
identifier: string
|
||||
relays: string[]
|
||||
}
|
||||
|
||||
// Plain text format
|
||||
|
||||
export const decodeAddress = (a: string, relays: string[] = []): Address => {
|
||||
const [kind, pubkey, identifier = ""] = a.split(":")
|
||||
|
||||
return {kind: parseInt(kind), pubkey, identifier, relays}
|
||||
}
|
||||
|
||||
export const encodeAddress = (a: Address) => [a.kind, a.pubkey, a.identifier].join(":")
|
||||
|
||||
// Naddr encoding
|
||||
|
||||
export const addressFromNaddr = (naddr: string): Address => {
|
||||
let type
|
||||
let data = {} as any
|
||||
try {
|
||||
({type, data} = nip19.decode(naddr) as {
|
||||
type: "naddr"
|
||||
data: any
|
||||
})
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
|
||||
if (type !== "naddr") {
|
||||
throw new Error(`Invalid naddr ${naddr}`)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
export const addressToNaddr = (a: Address): string => nip19.naddrEncode(a)
|
||||
|
||||
// Get from event, encode to filter
|
||||
|
||||
export const getIdentifier = (e: UnsignedEvent) => e.tags.find(t => t[0] === "d")?.[1] || ""
|
||||
|
||||
export const addressFromEvent = (e: UnsignedEvent, relays: string[] = []) =>
|
||||
({kind: e.kind, pubkey: e.pubkey, identifier: getIdentifier(e), relays})
|
||||
|
||||
export const addressToFilter = (a: Address) =>
|
||||
({kinds: [a.kind], authors: [a.pubkey], "#d": [a.identifier]})
|
||||
|
||||
// Utils
|
||||
|
||||
export const isGroupAddress = (a: Address) => a.kind === GROUP_DEFINITION
|
||||
|
||||
export const isCommunityAddress = (a: Address) => a.kind === COMMUNITY_DEFINITION
|
||||
|
||||
export const isContextAddress = (a: Address) => [GROUP_DEFINITION, COMMUNITY_DEFINITION].includes(a.kind)
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ import type {Event, EventTemplate, UnsignedEvent} from 'nostr-tools'
|
||||
import {verifyEvent, getEventHash} from 'nostr-tools'
|
||||
import {cached} from "./LRUCache"
|
||||
import {now} from './Tools'
|
||||
import {Address} from './Address'
|
||||
import {addressFromEvent, encodeAddress} from './Address'
|
||||
import {isEphemeralKind, isReplaceableKind, isPlainReplaceableKind, isParameterizedReplaceableKind} from './Kinds'
|
||||
|
||||
export type Rumor = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey' | 'id'>
|
||||
@@ -43,7 +43,7 @@ export const hasValidSignature = cached<string, boolean, [Event]>({
|
||||
},
|
||||
})
|
||||
|
||||
export const getAddress = (e: UnsignedEvent) => Address.fromEvent(e, []).asRaw()
|
||||
export const getAddress = (e: UnsignedEvent) => encodeAddress(addressFromEvent(e))
|
||||
|
||||
export const getIdOrAddress = (e: Rumor) => isReplaceable(e) ? getAddress(e) : e.id
|
||||
|
||||
|
||||
+7
-2
@@ -3,7 +3,12 @@ import type {Rumor} from './Events'
|
||||
import {getAddress, isReplaceable} from './Events'
|
||||
import {Tag, Tags} from './Tags'
|
||||
import {first, uniq, shuffle} from './Tools'
|
||||
import {Address, isGroupAddress, isCommunityAddress} from './Address'
|
||||
import {GROUP_DEFINITION, COMMUNITY_DEFINITION} from './Kinds'
|
||||
import {addressFromEvent, decodeAddress} from './Address'
|
||||
|
||||
const isGroupAddress = (a: string) => decodeAddress(a).kind === GROUP_DEFINITION
|
||||
|
||||
const isCommunityAddress = (a: string) => decodeAddress(a).kind === COMMUNITY_DEFINITION
|
||||
|
||||
export enum RelayMode {
|
||||
Inbox = "inbox",
|
||||
@@ -205,7 +210,7 @@ export class Router {
|
||||
}
|
||||
|
||||
address = (event: UnsignedEvent) =>
|
||||
Address.fromEvent(event, this.Event(event).limit(3).getUrls())
|
||||
addressFromEvent(event, this.Event(event).limit(3).getUrls())
|
||||
}
|
||||
|
||||
// Router Scenario
|
||||
|
||||
+20
-6
@@ -3,13 +3,17 @@ import {Fluent} from './Fluent'
|
||||
import type {OmitStatics} from './Tools'
|
||||
import {last} from './Tools'
|
||||
import {isShareableRelayUrl} from './Relays'
|
||||
import {isCommunityAddress, isGroupAddress, isContextAddress} from './Address'
|
||||
import type {Address} from './Address'
|
||||
import {encodeAddress, decodeAddress} from './Address'
|
||||
import {GROUP_DEFINITION, COMMUNITY_DEFINITION} from './Kinds'
|
||||
|
||||
export class Tag extends (Fluent<string> as OmitStatics<typeof Fluent<string>, 'from'>) {
|
||||
static from(xs: Iterable<string>) {
|
||||
return new Tag(Array.from(xs))
|
||||
}
|
||||
|
||||
static fromAddress = (a: Address) => new Tag(["a", encodeAddress(a), a.relays[0] || ""])
|
||||
|
||||
valueOf = () => this.xs
|
||||
|
||||
key = () => this.xs[0]
|
||||
@@ -25,6 +29,16 @@ export class Tag extends (Fluent<string> as OmitStatics<typeof Fluent<string>, '
|
||||
setValue = (v: string) => this.set(1, v)
|
||||
|
||||
setMark = (m: string) => this.xs.length > 2 ? this.set(this.xs.length - 2, m) : this.append(m)
|
||||
|
||||
asAddress = () => decodeAddress(this.value())
|
||||
|
||||
isAddress = (kind?: number) => this.key() === "a" && this.value()?.startsWith(`${kind}:`)
|
||||
|
||||
isGroup = () => this.isAddress(GROUP_DEFINITION)
|
||||
|
||||
isCommunity = () => this.isAddress(COMMUNITY_DEFINITION)
|
||||
|
||||
isContext = () => this.isAddress(GROUP_DEFINITION) || this.isAddress(COMMUNITY_DEFINITION)
|
||||
}
|
||||
|
||||
export class Tags extends (Fluent<Tag> as OmitStatics<typeof Fluent<Tag>, 'from'>) {
|
||||
@@ -70,7 +84,7 @@ export class Tags extends (Fluent<Tag> as OmitStatics<typeof Fluent<Tag>, 'from'
|
||||
topics = () => this.whereKey("t").values().map((t: string) => t.replace(/^#/, ""))
|
||||
|
||||
ancestors = () => {
|
||||
const tags = this.filter(t => ["a", "e", "q"].includes(t.key()) && !isContextAddress(t.value()))
|
||||
const tags = this.filter(t => ["a", "e", "q"].includes(t.key()) && !t.isContext())
|
||||
const roots: string[][] = []
|
||||
const replies: string[][] = []
|
||||
const mentions: string[][] = []
|
||||
@@ -129,11 +143,11 @@ export class Tags extends (Fluent<Tag> as OmitStatics<typeof Fluent<Tag>, 'from'
|
||||
return parents.get("e") || parents.get("a")
|
||||
}
|
||||
|
||||
groups = () => this.whereKey("a").filter(t => isGroupAddress(t.value()))
|
||||
groups = () => this.whereKey("a").filter(t => t.isGroup())
|
||||
|
||||
communities = () => this.whereKey("a").filter(t => isCommunityAddress(t.value()))
|
||||
communities = () => this.whereKey("a").filter(t => t.isCommunity())
|
||||
|
||||
context = () => this.whereKey("a").filter(t => isContextAddress(t.value()))
|
||||
context = () => this.whereKey("a").filter(t => t.isContext())
|
||||
|
||||
asObject = () => {
|
||||
const result: Record<string, string> = {}
|
||||
@@ -167,7 +181,7 @@ export class Tags extends (Fluent<Tag> as OmitStatics<typeof Fluent<Tag>, 'from'
|
||||
|
||||
addContext = (addresses: string[]) => this.concat(addresses.map(a => Tag.from(["a", a])))
|
||||
|
||||
removeContext = () => this.reject(t => t.key() === "a" && isContextAddress(t.value()))
|
||||
removeContext = () => this.reject(t => t.isContext())
|
||||
|
||||
setContext = (addresses: string[]) => this.removeContext().addContext(addresses)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user