Add nip 29 room utils

This commit is contained in:
Jon Staab
2025-06-13 17:41:11 -07:00
parent c3cd2e3220
commit 09f6db7eae
19 changed files with 121 additions and 30 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "@welshman", "name": "@welshman",
"private": true, "private": true,
"version": "0.3.5", "version": "0.3.6",
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
], ],
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/app", "name": "@welshman/app",
"version": "0.3.5", "version": "0.3.6",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of svelte stores for use in building nostr client applications.", "description": "A collection of svelte stores for use in building nostr client applications.",
+33
View File
@@ -10,6 +10,11 @@ import {
getListTags, getListTags,
getRelayTags, getRelayTags,
makeList, makeList,
makeRoomCreateEvent,
makeRoomDeleteEvent,
makeRoomEditEvent,
makeRoomJoinEvent,
makeRoomLeaveEvent,
RelayMode, RelayMode,
INBOX_RELAYS, INBOX_RELAYS,
FOLLOWS, FOLLOWS,
@@ -17,6 +22,7 @@ import {
MUTES, MUTES,
PINS, PINS,
} from "@welshman/util" } from "@welshman/util"
import type {RoomMeta} from "@welshman/util"
import {Nip59, stamp} from "@welshman/signer" import {Nip59, stamp} from "@welshman/signer"
import {Router, addMaximalFallbacks} from "@welshman/router" import {Router, addMaximalFallbacks} from "@welshman/router"
import { import {
@@ -29,6 +35,8 @@ import {
import {nip44EncryptToSelf, signer} from "./session.js" import {nip44EncryptToSelf, signer} from "./session.js"
import {ThunkOptions, MergedThunk, publishThunk} from "./thunk.js" import {ThunkOptions, MergedThunk, publishThunk} from "./thunk.js"
// NIP 65
export const removeRelay = async (url: string, mode: RelayMode) => { export const removeRelay = async (url: string, mode: RelayMode) => {
const list = get(userRelaySelections) || makeList({kind: RELAYS}) const list = get(userRelaySelections) || makeList({kind: RELAYS})
const dup = getRelayTags(getListTags(list)).find(nthEq(1, url)) const dup = getRelayTags(getListTags(list)).find(nthEq(1, url))
@@ -60,6 +68,8 @@ export const addRelay = async (url: string, mode: RelayMode) => {
return publishThunk({event, relays}) return publishThunk({event, relays})
} }
// NIP 17
export const removeInboxRelay = async (url: string) => { export const removeInboxRelay = async (url: string) => {
const list = get(userInboxRelaySelections) || makeList({kind: INBOX_RELAYS}) const list = get(userInboxRelaySelections) || makeList({kind: INBOX_RELAYS})
const event = await removeFromList(list, url).reconcile(nip44EncryptToSelf) const event = await removeFromList(list, url).reconcile(nip44EncryptToSelf)
@@ -76,6 +86,8 @@ export const addInboxRelay = async (url: string) => {
return publishThunk({event, relays}) return publishThunk({event, relays})
} }
// NIP 02
export const unfollow = async (value: string) => { export const unfollow = async (value: string) => {
const list = get(userFollows) || makeList({kind: FOLLOWS}) const list = get(userFollows) || makeList({kind: FOLLOWS})
const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf) const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf)
@@ -124,6 +136,8 @@ export const pin = async (tag: string[]) => {
return publishThunk({event, relays}) return publishThunk({event, relays})
} }
// NIP 59
export type SendWrappedOptions = Omit<ThunkOptions, "event" | "relays"> & { export type SendWrappedOptions = Omit<ThunkOptions, "event" | "relays"> & {
template: EventTemplate template: EventTemplate
pubkeys: string[] pubkeys: string[]
@@ -145,6 +159,8 @@ export const sendWrapped = async ({template, pubkeys, ...options}: SendWrappedOp
) )
} }
// NIP 86
export const manageRelay = async (url: string, request: ManagementRequest) => { export const manageRelay = async (url: string, request: ManagementRequest) => {
url = url.replace(/^ws/, "http") url = url.replace(/^ws/, "http")
@@ -153,3 +169,20 @@ export const manageRelay = async (url: string, request: ManagementRequest) => {
return sendManagementRequest(url, request, authEvent) return sendManagementRequest(url, request, authEvent)
} }
// NIP 29
export const createRoom = (url: string, room: RoomMeta) =>
publishThunk({event: makeRoomCreateEvent(room), relays: [url]})
export const deleteRoom = (url: string, room: RoomMeta) =>
publishThunk({event: makeRoomDeleteEvent(room), relays: [url]})
export const editRoom = (url: string, room: RoomMeta) =>
publishThunk({event: makeRoomEditEvent(room), relays: [url]})
export const joinRoom = (url: string, room: RoomMeta) =>
publishThunk({event: makeRoomJoinEvent(room), relays: [url]})
export const leaveRoom = (url: string, room: RoomMeta) =>
publishThunk({event: makeRoomLeaveEvent(room), relays: [url]})
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/content", "name": "@welshman/content",
"version": "0.3.5", "version": "0.3.6",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of utilities for parsing nostr note content.", "description": "A collection of utilities for parsing nostr note content.",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/editor", "name": "@welshman/editor",
"version": "0.3.5", "version": "0.3.6",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A batteries-included nostr editor.", "description": "A batteries-included nostr editor.",
+2 -2
View File
@@ -19,7 +19,7 @@ import type {
ImageOptions, ImageOptions,
LinkOptions, LinkOptions,
NSecRejectOptions, NSecRejectOptions,
} from "nostr-editor-coracle-workaround" } from "nostr-editor"
import { import {
NostrExtension, NostrExtension,
Bolt11Extension, Bolt11Extension,
@@ -32,7 +32,7 @@ import {
TagExtension, TagExtension,
VideoExtension, VideoExtension,
NSecRejectExtension, NSecRejectExtension,
} from "nostr-editor-coracle-workaround" } from "nostr-editor"
import {WordCount} from "./WordCount.js" import {WordCount} from "./WordCount.js"
import {CodeInline, type CodeInlineOptions} from "./CodeInline.js" import {CodeInline, type CodeInlineOptions} from "./CodeInline.js"
import {BreakOrSubmit, type BreakOrSubmitOptions} from "./BreakOrSubmit.js" import {BreakOrSubmit, type BreakOrSubmitOptions} from "./BreakOrSubmit.js"
+1 -1
View File
@@ -2,4 +2,4 @@ export * from "./nodeviews/index.js"
export * from "./extensions/index.js" export * from "./extensions/index.js"
export * from "./plugins/index.js" export * from "./plugins/index.js"
export {Editor, NodeViewProps} from "@tiptap/core" export {Editor, NodeViewProps} from "@tiptap/core"
export {UploadTask, FileAttributes} from "nostr-editor-coracle-workaround" export {UploadTask, FileAttributes} from "nostr-editor"
@@ -2,7 +2,7 @@ import type {Instance} from "tippy.js"
import tippy from "tippy.js" import tippy from "tippy.js"
import {nprofileEncode} from "nostr-tools/nip19" import {nprofileEncode} from "nostr-tools/nip19"
import type {Editor} from "@tiptap/core" import type {Editor} from "@tiptap/core"
import {makeNProfileAttrs} from "nostr-editor-coracle-workaround" import {makeNProfileAttrs} from "nostr-editor"
import {PluginKey} from "@tiptap/pm/state" import {PluginKey} from "@tiptap/pm/state"
import Suggestion from "@tiptap/suggestion" import Suggestion from "@tiptap/suggestion"
import {throttle, enumerate, clamp} from "@welshman/lib" import {throttle, enumerate, clamp} from "@welshman/lib"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/feeds", "name": "@welshman/feeds",
"version": "0.3.5", "version": "0.3.6",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "Utilities for building dynamic nostr feeds.", "description": "Utilities for building dynamic nostr feeds.",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/lib", "name": "@welshman/lib",
"version": "0.3.5", "version": "0.3.6",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of utilities.", "description": "A collection of utilities.",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/net", "name": "@welshman/net",
"version": "0.3.5", "version": "0.3.6",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "Utilities for connecting with nostr relays.", "description": "Utilities for connecting with nostr relays.",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/relay", "name": "@welshman/relay",
"version": "0.3.5", "version": "0.3.6",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "An in-memory nostr relay implementation.", "description": "An in-memory nostr relay implementation.",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/router", "name": "@welshman/router",
"version": "0.3.5", "version": "0.3.6",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of utilities for nostr relay selection.", "description": "A collection of utilities for nostr relay selection.",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/signer", "name": "@welshman/signer",
"version": "0.3.5", "version": "0.3.6",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A nostr signer implemenation supporting several login methods.", "description": "A nostr signer implemenation supporting several login methods.",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/store", "name": "@welshman/store",
"version": "0.3.5", "version": "0.3.6",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of utilities based on svelte/store for use with welshman", "description": "A collection of utilities based on svelte/store for use with welshman",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/util", "name": "@welshman/util",
"version": "0.3.5", "version": "0.3.6",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of nostr-related utilities.", "description": "A collection of nostr-related utilities.",
+15 -14
View File
@@ -106,16 +106,17 @@ export const DVM_RESPONSE_OTS = 6900
export const DVM_RESPONSE_OP_RETURN = 6901 export const DVM_RESPONSE_OP_RETURN = 6901
export const DVM_RESPONSE_PUBLISH_SCHEDULE = 6905 export const DVM_RESPONSE_PUBLISH_SCHEDULE = 6905
export const DVM_FEEDBACK = 7000 export const DVM_FEEDBACK = 7000
export const GROUP_ADD_USER = 9000 export const ROOM_ADD_USER = 9000
export const GROUP_REMOVE_USER = 9001 export const ROOM_REMOVE_USER = 9001
export const GROUP_EDIT_META = 9002 export const ROOM_EDIT_META = 9002
export const GROUP_ADD_PERM = 9003 export const ROOM_ADD_PERM = 9003
export const GROUP_REMOVE_PERM = 9004 export const ROOM_REMOVE_PERM = 9004
export const GROUP_DELETE_EVENT = 9005 export const ROOM_DELETE_EVENT = 9005
export const GROUP_EDIT_STATUS = 9006 export const ROOM_EDIT_STATUS = 9006
export const GROUP_CREATE = 9007 export const ROOM_CREATE = 9007
export const GROUP_JOIN = 9021 export const ROOM_DELETE = 9008
export const GROUP_LEAVE = 9022 export const ROOM_JOIN = 9021
export const ROOM_LEAVE = 9022
export const ZAP_GOAL = 9041 export const ZAP_GOAL = 9041
export const ZAP_REQUEST = 9734 export const ZAP_REQUEST = 9734
export const ZAP_RESPONSE = 9735 export const ZAP_RESPONSE = 9735
@@ -128,7 +129,7 @@ export const COMMUNITIES = 10004
export const CHANNELS = 10005 export const CHANNELS = 10005
export const BLOCKED_RELAYS = 10006 export const BLOCKED_RELAYS = 10006
export const SEARCH_RELAYS = 10007 export const SEARCH_RELAYS = 10007
export const GROUPS = 10009 export const ROOMS = 10009
export const FEEDS = 10014 export const FEEDS = 10014
export const TOPICS = 10015 export const TOPICS = 10015
export const EMOJIS = 10030 export const EMOJIS = 10030
@@ -178,9 +179,9 @@ export const EVENT_RSVP = 31925
export const HANDLER_RECOMMENDATION = 31989 export const HANDLER_RECOMMENDATION = 31989
export const HANDLER_INFORMATION = 31990 export const HANDLER_INFORMATION = 31990
export const COMMUNITY = 34550 export const COMMUNITY = 34550
export const GROUP = 35834 export const ROOM = 35834
export const GROUP_META = 39000 export const ROOM_META = 39000
export const GROUP_ADMINS = 39001 export const ROOM_ADMINS = 39001
export const DEPRECATED_RELAY_RECOMMENDATION = 2 export const DEPRECATED_RELAY_RECOMMENDATION = 2
export const DEPRECATED_DIRECT_MESSAGE = 4 export const DEPRECATED_DIRECT_MESSAGE = 4
+56
View File
@@ -0,0 +1,56 @@
import {randomId, nthNe} from "@welshman/lib"
import {
ROOM_META,
ROOM_DELETE,
ROOM_CREATE,
ROOM_EDIT_META,
ROOM_JOIN,
ROOM_LEAVE,
} from "./Kinds.js"
import {makeEvent, TrustedEvent, getIdentifier} from "./Events.js"
export type RoomMeta = {
id: string
tags: string[][]
event?: TrustedEvent
}
export type PublishedRoomMeta = Omit<RoomMeta, "event"> & {
event: TrustedEvent
}
export const makeRoomMeta = (room: Partial<RoomMeta> = {}): RoomMeta => ({
id: randomId(),
tags: [],
...room,
})
export const readRoomMeta = (event: TrustedEvent): PublishedRoomMeta => {
if (event.kind !== ROOM_META) {
throw new Error("Invalid group meta event")
}
const id = getIdentifier(event)
if (!id) {
throw new Error("Group meta event had no d tag")
}
const tags = event.tags.filter(nthNe(0, "d"))
return {id, tags, event}
}
export const makeRoomCreateEvent = (room: RoomMeta) =>
makeEvent(ROOM_CREATE, {tags: [["h", room.id]]})
export const makeRoomDeleteEvent = (room: RoomMeta) =>
makeEvent(ROOM_DELETE, {tags: [["h", room.id]]})
export const makeRoomEditEvent = (room: RoomMeta) =>
makeEvent(ROOM_EDIT_META, {tags: [["h", room.id], ...room.tags]})
export const makeRoomJoinEvent = (room: RoomMeta) => makeEvent(ROOM_JOIN, {tags: [["h", room.id]]})
export const makeRoomLeaveEvent = (room: RoomMeta) =>
makeEvent(ROOM_LEAVE, {tags: [["h", room.id]]})
+1
View File
@@ -12,5 +12,6 @@ export * from "./Nip86.js"
export * from "./Nip98.js" export * from "./Nip98.js"
export * from "./Profile.js" export * from "./Profile.js"
export * from "./Relay.js" export * from "./Relay.js"
export * from "./Room.js"
export * from "./Tags.js" export * from "./Tags.js"
export * from "./Zaps.js" export * from "./Zaps.js"