/** * nip-29 (managed relay-based groups) event kinds and tag helpers. * managed groups: the relay keeps membership/metadata and enforces access. */ export const NIP29_KINDS = { /** chat message posted to a group. */ chat: 9, /** threaded reply (we show it like chat for the mvp). */ threadReply: 11, /** admin adds a user to a group (kind 9000). */ putUser: 9000, /** admin removes a user from a group (kind 9001). */ removeUser: 9001, /** admin edits group metadata — name/about (kind 9002). */ editMetadata: 9002, /** join request (kind 9021). */ joinRequest: 9021, /** leave request (kind 9022). */ leaveRequest: 9022, /** group metadata (name/about/picture). */ metadata: 39000, /** group admins list. */ admins: 39001, /** group members list. */ members: 39002 } as const; /** addressable nip-29 list kinds the client subscribes to for group state. */ export const GROUP_META_KINDS: number[] = [ NIP29_KINDS.metadata, NIP29_KINDS.admins, NIP29_KINDS.members ]; /** pull all `p` tag pubkeys out of an event's tags. */ export function pubkeysFromTags(tags: string[][]): string[] { return tags.filter((t) => t[0] === 'p' && typeof t[1] === 'string').map((t) => t[1] as string); } /** value of the first tag with the given name, or undefined. */ export function tagValue(tags: string[][], name: string): string | undefined { return tags.find((t) => t[0] === name)?.[1]; } /** kinds we treat as showable chat messages in a room timeline. */ export const CHAT_KINDS: number[] = [NIP29_KINDS.chat, NIP29_KINDS.threadReply]; /** the `h` tag carries the group id on every nip-29 event. */ export function groupTag(groupId: string): string[] { return ['h', groupId]; } /** pull the group id out of an event's tags, if it's there. */ export function groupIdFromTags(tags: string[][]): string | undefined { const h = tags.find((t) => t[0] === 'h'); return h?.[1]; }