animiq-nip76-tools
v1.0.5
Published
Tools for handling Private Channel NOSTR Events defined in NIP-76.
Downloads
1
Readme
animiq-nip76-tools
Tools for developing Private Channels Nostr clients.
If NIP-76 Pull Request is accepted, we will rename this package to "nip76-tools".
Depends on @scure, @noble & nostr-tools packages.
Installation
npm install animiq-nip76-tools # or yarn add animiq-nip76-tools
Usage
Creating a nip76 wallet with a private key and a public key.
Here we create the pk
and sk
, but you can use any key pair. Currently only web storage is deomnstrated. More storage types coming soon.
import {generatePrivateKey, getPublicKey} from 'nostr-tools';
import {Nip76WebWalletStorage} from 'animiq-nip76-tools';
let sk = generatePrivateKey() // `sk` is a hex string
let pk = getPublicKey(sk) // `pk` is a hex string
const wallet = await Nip76WebWalletStorage.fromStorage({ ps, sk });
wallet.saveWallet(sk);
Creating New Private Channels
let privateKey = await someMethodToGetTheProfilePrivateKey();
let channel = wallet.createChannel();
channel.content.name = 'My New Channel';
channel.content.about = 'Whatever we want';
const event = await wallet.documentsIndex.createEvent(channel, privateKey);
someMethodToSendTheEventToRelays(event);
Creating a Text Note on a Private Channel
import { PostDocument } from 'animiq-nip76-tools';
let privateKey = await someMethodToGetTheProfilePrivateKey();
let postDocument = new PostDocument();
postDocument.content = {
'Hello World',
pubkey: wallet.ownerPubKey,
kind: nostrTools.Kind.Text
}
let event = await channel.dkxPost.createEvent(postDocument, privateKey);
someMethodToSendTheEventToRelays(event);
Creating a Text Reply or Reaction to Private Channel Note
import { PostDocument } from 'animiq-nip76-tools';
let privateKey = await someMethodToGetTheProfilePrivateKey();
let replyDocument = new PostDocument();
replyDocument.content = {
'Hello Back',
pubkey: wallet.ownerPubKey,
kind: nostrTools.Kind.Text, // or use nostrTools.Kind.Reaction
tags: [['e', post.nostrEvent.id]]
}
let event = await channel.dkxPost.createEvent(replyDocument, privateKey);
someMethodToSendTheEventToRelays(event);
Saving an Invitation for a public key
let privateKey = await someMethodToGetTheProfilePrivateKey();
let invite = new Invitation();
invite.docIndex = channel.dkxInvite.documents.length + 1;
invite.content = {
kind: NostrKinds.PrivateChannelInvitation,
docIndex: invite.docIndex,
for: '(pubkey-hex)',
pubkey: channel.dkxPost.signingParent.nostrPubKey,
signingParent: channel.dkxPost.signingParent,
cryptoParent: channel.dkxPost.cryptoParent,
}
let event = await channel.dkxInvite.createEvent(invite, privateKey);
someMethodToSendTheEventToRelays(event);
let invitationTextToSend = await invite.getPointer();
Saving an Invitation that uses a password
let privateKey = await someMethodToGetTheProfilePrivateKey();
let invite = new Invitation();
invite.docIndex = channel.dkxInvite.documents.length + 1;
invite.content = {
kind: NostrKinds.PrivateChannelInvitation,
docIndex: invite.docIndex,
password: 'the password',
pubkey: channel.dkxPost.signingParent.nostrPubKey,
signingParent: channel.dkxPost.signingParent,
cryptoParent: channel.dkxPost.cryptoParent,
}
let event = await channel.dkxInvite.createEvent(invite, privateKey);
someMethodToSendTheEventToRelays(event);
let invitationTextToSend = await invite.getPointer();
Reading an Invitation
(NOTE: We are working to make this easier to implement.)
import { nip19Extension, HDKIndex } from 'animiq-nip76-tools';
let pointer = await nip19Extension.decode(channelPointer, 'privateKeyHexOrPassword').data;
if ((pointer.type & nip19Extension.PointerType.FullKeySet) === nip19Extension.PointerType.FullKeySet) {
// Unmanaged Invitation
const signingParent = new HDKey({ publicKey: pointer.signingKey, chainCode: pointer.signingChain, version: Versions.nip76API1 });
const cryptoParent = new HDKey({ publicKey: pointer.cryptoKey, chainCode: pointer.cryptoChain, version: Versions.nip76API1 });
const invite = new Invitation();
pointer.docIndex = -1;
invite.pointer = pointer;
invite.content = {
kind: NostrKinds.PrivateChannelInvitation,
pubkey: signingParent.nostrPubKey,
docIndex: pointer.docIndex,
signingParent,
cryptoParent
};
channelIndex = new HDKIndex(HDKIndexType.Singleton, invite.content.signingParent!, invite.content.cryptoParent!);
relayService.subscribe(
[{ authors: [channelIndex.signingParent.nostrPubKey], kinds: [17761], limit: 1 }]
);
// the nostrEvent returned is the channel
} else {
// Managed Invitation
const inviteIndex = HDKIndex.fromChannelPointer(pointer);
relayService.subscribe(
[{ authors: [inviteIndex.signingParent.nostrPubKey], kinds: [17761], limit: 1 }]
);
// the nostrEvent returned is an invitation from which we can read the channel
}
Reading Notes on a Channel
relayService.subscribe([
{ '#e': [channel.dkxPost.eventTag], kinds: [17761], limit: length },
{ '#e': [channel.dkxRsvp.eventTag], kinds: [17761], limit: length },
]);
///events from the stream are then read like:
if (channel.dkxPost.eventTag === nostrEvent.tags[0][1]) {
let post = await channel.dkxPost.readEvent(nostrEvent);
} else if (channel.dkxRsvp.eventTag === nostrEvent.tags[0][1]) {
let rsvp = await channel.dkxRsvp.readEvent(nostrEvent);
}
License
MIT