1.0.5 • Published 1 year ago

animiq-nip76-tools v1.0.5

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

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

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago