mirror of
https://github.com/nbd-wtf/nostr-tools.git
synced 2025-12-08 16:28:49 +00:00
Add NIP-25 utils
This commit is contained in:
1
index.ts
1
index.ts
@@ -12,6 +12,7 @@ export * as nip10 from './nip10'
|
||||
export * as nip13 from './nip13'
|
||||
export * as nip19 from './nip19'
|
||||
export * as nip21 from './nip21'
|
||||
export * as nip25 from './nip25'
|
||||
export * as nip26 from './nip26'
|
||||
export * as nip27 from './nip27'
|
||||
export * as nip39 from './nip39'
|
||||
|
||||
104
nip25.test.js
Normal file
104
nip25.test.js
Normal file
@@ -0,0 +1,104 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
const {nip25, finishEvent, getPublicKey, Kind} = require('./lib/nostr.cjs')
|
||||
|
||||
describe('finishReactionEvent + getReactedEventPointer', () => {
|
||||
const privateKey =
|
||||
'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf'
|
||||
|
||||
const publicKey = getPublicKey(privateKey)
|
||||
|
||||
const reactedEvent = finishEvent({
|
||||
kind: Kind.Text,
|
||||
tags: [
|
||||
['e', 'replied event id'],
|
||||
['p', 'replied event pubkey'],
|
||||
],
|
||||
content: 'Replied to a post',
|
||||
created_at: 1617932115
|
||||
}, privateKey)
|
||||
|
||||
it('should create a signed event from a minimal template', () => {
|
||||
const template = {
|
||||
created_at: 1617932115
|
||||
}
|
||||
|
||||
const event = nip25.finishReactionEvent(template, reactedEvent, privateKey)
|
||||
|
||||
expect(event.kind).toEqual(Kind.Reaction)
|
||||
expect(event.tags).toEqual([
|
||||
[
|
||||
'e',
|
||||
'replied event id',
|
||||
],
|
||||
[
|
||||
'p',
|
||||
'replied event pubkey',
|
||||
],
|
||||
[
|
||||
'e',
|
||||
'0ecdbd4dba0652afb19e5f638257a41552a37995a4438ef63de658443f8d16b1',
|
||||
],
|
||||
[
|
||||
'p',
|
||||
'6af0f9de588f2c53cedcba26c5e2402e0d0aa64ec7b47c9f8d97b5bc562bab5f',
|
||||
],
|
||||
])
|
||||
expect(event.content).toEqual('+')
|
||||
expect(event.created_at).toEqual(template.created_at)
|
||||
expect(event.pubkey).toEqual(publicKey)
|
||||
expect(typeof event.id).toEqual('string')
|
||||
expect(typeof event.sig).toEqual('string')
|
||||
|
||||
const reactedEventPointer = nip25.getReactedEventPointer(event)
|
||||
|
||||
expect(reactedEventPointer.id).toEqual(reactedEvent.id)
|
||||
expect(reactedEventPointer.author).toEqual(reactedEvent.pubkey)
|
||||
})
|
||||
|
||||
it('should create a signed event from a filled template', () => {
|
||||
const template = {
|
||||
tags: [
|
||||
['nonstandard', 'tag'],
|
||||
],
|
||||
content: '👍',
|
||||
created_at: 1617932115
|
||||
}
|
||||
|
||||
const event = nip25.finishReactionEvent(template, reactedEvent, privateKey)
|
||||
|
||||
expect(event.kind).toEqual(Kind.Reaction)
|
||||
expect(event.tags).toEqual([
|
||||
[
|
||||
'nonstandard',
|
||||
'tag',
|
||||
],
|
||||
[
|
||||
'e',
|
||||
'replied event id',
|
||||
],
|
||||
[
|
||||
'p',
|
||||
'replied event pubkey',
|
||||
],
|
||||
[
|
||||
'e',
|
||||
'0ecdbd4dba0652afb19e5f638257a41552a37995a4438ef63de658443f8d16b1',
|
||||
],
|
||||
[
|
||||
'p',
|
||||
'6af0f9de588f2c53cedcba26c5e2402e0d0aa64ec7b47c9f8d97b5bc562bab5f',
|
||||
],
|
||||
])
|
||||
expect(event.content).toEqual('👍')
|
||||
expect(event.created_at).toEqual(template.created_at)
|
||||
expect(event.pubkey).toEqual(publicKey)
|
||||
expect(typeof event.id).toEqual('string')
|
||||
expect(typeof event.sig).toEqual('string')
|
||||
|
||||
const reactedEventPointer = nip25.getReactedEventPointer(event)
|
||||
|
||||
expect(reactedEventPointer.id).toEqual(reactedEvent.id)
|
||||
expect(reactedEventPointer.author).toEqual(reactedEvent.pubkey)
|
||||
})
|
||||
})
|
||||
68
nip25.ts
Normal file
68
nip25.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { Event, finishEvent, Kind } from './event'
|
||||
import { EventPointer } from './nip19'
|
||||
|
||||
export type ReactionEventTemplate = {
|
||||
/**
|
||||
* Pass only non-nip25 tags if you have to. Nip25 tags ('e' and 'p' tags from reacted event) will be added automatically.
|
||||
*/
|
||||
tags?: string[][]
|
||||
|
||||
/**
|
||||
* @default '+'
|
||||
*/
|
||||
content?: string
|
||||
|
||||
created_at: number
|
||||
}
|
||||
|
||||
export function finishReactionEvent(
|
||||
t: ReactionEventTemplate,
|
||||
reacted: Event,
|
||||
privateKey: string,
|
||||
): Event {
|
||||
const inheritedTags = reacted.tags.filter(
|
||||
(tag) => tag.length >= 2 && (tag[0] === 'e' || tag[0] === 'p'),
|
||||
)
|
||||
|
||||
return finishEvent({
|
||||
...t,
|
||||
kind: Kind.Reaction,
|
||||
tags: [
|
||||
...(t.tags ?? []),
|
||||
...inheritedTags,
|
||||
['e', reacted.id],
|
||||
['p', reacted.pubkey],
|
||||
],
|
||||
content: t.content ?? '+',
|
||||
}, privateKey)
|
||||
}
|
||||
|
||||
export function getReactedEventPointer(event: Event): undefined | EventPointer {
|
||||
if (event.kind !== Kind.Reaction) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
let lastETag: undefined | string[]
|
||||
let lastPTag: undefined | string[]
|
||||
|
||||
for (let i = event.tags.length - 1; i >= 0 && (lastETag === undefined || lastPTag === undefined); i--) {
|
||||
const tag = event.tags[i]
|
||||
if (tag.length >= 2) {
|
||||
if (tag[0] === 'e' && lastETag === undefined) {
|
||||
lastETag = tag
|
||||
} else if (tag[0] === 'p' && lastPTag === undefined) {
|
||||
lastPTag = tag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lastETag === undefined || lastPTag === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
id: lastETag[1],
|
||||
relays: [ lastETag[2], lastPTag[2] ].filter((x) => x !== undefined),
|
||||
author: lastPTag[1],
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user