You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

221 lines
5.6 KiB

import { COMMENT_EVENT_KIND, PICTURE_EVENT_KIND } from '@/constants'
import { TDraftEvent, TMailboxRelay, TRelaySet } from '@/types'
import dayjs from 'dayjs'
import { Event, kinds } from 'nostr-tools'
import {
extractCommentMentions,
extractHashtags,
extractImagesFromContent,
extractMentions,
getEventCoordinate,
isReplaceable
} from './event'
// https://github.com/nostr-protocol/nips/blob/master/25.md
export function createReactionDraftEvent(event: Event): TDraftEvent {
const tags = event.tags.filter((tag) => tag.length >= 2 && ['e', 'p'].includes(tag[0]))
tags.push(['e', event.id])
tags.push(['p', event.pubkey])
tags.push(['k', event.kind.toString()])
if (isReplaceable(event.kind)) {
tags.push(['a', getEventCoordinate(event)])
}
return {
kind: kinds.Reaction,
content: '+',
tags,
created_at: dayjs().unix()
}
}
// https://github.com/nostr-protocol/nips/blob/master/18.md
export function createRepostDraftEvent(event: Event): TDraftEvent {
const tags = [
['e', event.id], // TODO: url
['p', event.pubkey]
]
return {
kind: kinds.Repost,
content: JSON.stringify(event),
tags,
created_at: dayjs().unix()
}
}
export async function createShortTextNoteDraftEvent(
content: string,
pictureInfos: { url: string; tags: string[][] }[],
options: {
parentEvent?: Event
addClientTag?: boolean
} = {}
): Promise<TDraftEvent> {
const { pubkeys, otherRelatedEventIds, quoteEventIds, rootEventId, parentEventId } =
await extractMentions(content, options.parentEvent)
const hashtags = extractHashtags(content)
const tags = pubkeys
.map((pubkey) => ['p', pubkey])
.concat(otherRelatedEventIds.map((eventId) => ['e', eventId]))
.concat(quoteEventIds.map((eventId) => ['q', eventId]))
.concat(hashtags.map((hashtag) => ['t', hashtag]))
if (rootEventId) {
tags.push(['e', rootEventId, '', 'root'])
}
if (parentEventId) {
tags.push(['e', parentEventId, '', 'reply'])
}
const { images } = extractImagesFromContent(content)
if (images && images.length) {
tags.push(...generateImetaTags(images, pictureInfos))
}
if (options.addClientTag) {
tags.push(['client', 'jumble'])
}
return {
kind: kinds.ShortTextNote,
content,
tags,
created_at: dayjs().unix()
}
}
// https://github.com/nostr-protocol/nips/blob/master/51.md
export function createRelaySetDraftEvent(relaySet: TRelaySet): TDraftEvent {
return {
kind: kinds.Relaysets,
content: '',
tags: [
['d', relaySet.id],
['title', relaySet.name],
...relaySet.relayUrls.map((url) => ['relay', url])
],
created_at: dayjs().unix()
}
}
export async function createPictureNoteDraftEvent(
content: string,
pictureInfos: { url: string; tags: string[][] }[],
options: {
addClientTag?: boolean
} = {}
): Promise<TDraftEvent> {
const { pubkeys, quoteEventIds } = await extractMentions(content)
const hashtags = extractHashtags(content)
if (!pictureInfos.length) {
throw new Error('No images found in content')
}
const tags = pictureInfos
.map((info) => ['imeta', ...info.tags.map(([n, v]) => `${n} ${v}`)])
.concat(pubkeys.map((pubkey) => ['p', pubkey]))
.concat(quoteEventIds.map((eventId) => ['q', eventId]))
.concat(hashtags.map((hashtag) => ['t', hashtag]))
if (options.addClientTag) {
tags.push(['client', 'jumble'])
}
return {
kind: PICTURE_EVENT_KIND,
content,
tags,
created_at: dayjs().unix()
}
}
export async function createCommentDraftEvent(
content: string,
parentEvent: Event,
pictureInfos: { url: string; tags: string[][] }[],
options: {
addClientTag?: boolean
} = {}
): Promise<TDraftEvent> {
const {
pubkeys,
quoteEventIds,
rootEventId,
rootEventKind,
rootEventPubkey,
parentEventId,
parentEventKind,
parentEventPubkey
} = await extractCommentMentions(content, parentEvent)
const hashtags = extractHashtags(content)
const tags = [
['E', rootEventId],
['K', rootEventKind.toString()],
['P', rootEventPubkey],
['e', parentEventId],
['k', parentEventKind.toString()],
['p', parentEventPubkey]
]
.concat(pubkeys.map((pubkey) => ['p', pubkey]))
.concat(quoteEventIds.map((eventId) => ['q', eventId]))
.concat(hashtags.map((hashtag) => ['t', hashtag]))
const { images } = extractImagesFromContent(content)
if (images && images.length) {
tags.push(...generateImetaTags(images, pictureInfos))
}
if (options.addClientTag) {
tags.push(['client', 'jumble'])
}
return {
kind: COMMENT_EVENT_KIND,
content,
tags,
created_at: dayjs().unix()
}
}
export function createRelayListDraftEvent(mailboxRelays: TMailboxRelay[]): TDraftEvent {
return {
kind: kinds.RelayList,
content: '',
tags: mailboxRelays.map(({ url, scope }) =>
scope === 'both' ? ['r', url] : ['r', url, scope]
),
created_at: dayjs().unix()
}
}
export function createFollowListDraftEvent(tags: string[][], content?: string): TDraftEvent {
return {
kind: kinds.Contacts,
content: content ?? '',
created_at: dayjs().unix(),
tags
}
}
export function createProfileDraftEvent(content: string, tags: string[][] = []): TDraftEvent {
return {
kind: kinds.Metadata,
content,
tags,
created_at: dayjs().unix()
}
}
function generateImetaTags(imageUrls: string[], pictureInfos: { url: string; tags: string[][] }[]) {
return imageUrls.map((imageUrl) => {
const pictureInfo = pictureInfos.find((info) => info.url === imageUrl)
return pictureInfo
? ['imeta', ...pictureInfo.tags.map(([n, v]) => `${n} ${v}`)]
: ['imeta', `url ${imageUrl}`]
})
}