I will check tomorrow, but I only got the nip04.

Also, you should ask for the nprofile in the login page. In that way, you will have the relay info to get the DM relays and send the DMs. :)

Reply to this note

Please Login to reply.

Discussion

Is the code open somewhere?

😅 the code that does the sending is a complete mess and has a non-public info in it. I’ll try to clean it up and open the source for it. Need to parameterize some of the non-public (but not a high value secret or anything) info.

t-y Fishcake

This took me way longer than I would have liked, but here is the working Typescript code that makes those god damn GW DMs.

// Create DM event

export async function creatNIP17DMEvent(

fromSk: string,

toNpub: string,

dm: string[],

): Promise> {

const skBuff = hexToBytes(fromSk)

const events: Array = []

while (dm.length > 0) {

const msg = dm.shift()

if (msg)

events.push(createNip17Wraps(msg, toNpub, skBuff))

}

return events

}

function getNow(): number {

return Math.floor(Date.now() / 1000)

}

function getRandomNow(): number {

return getNow() - Math.floor(Math.random() * 172800)

}

function nip44Encrypt(rumor: Rumor, privateKey: Uint8Array, publicKey: string): string {

const key = nip44.v2.utils.getConversationKey(privateKey, publicKey)

return nip44.v2.encrypt(JSON.stringify(rumor), key)

}

function createWrap(event: Event, recipientPublicKey: string): Event {

// Get random secret key to wrap the event

const randomSK = generateSecretKey()

return finalizeEvent({

kind: 1059, // Gift wrapped event

content: nip44Encrypt(event, randomSK, recipientPublicKey),

created_at: getRandomNow(),

tags: [["p", recipientPublicKey]]

}, randomSK) as Event

}

function createSeal(event: Rumor, privateKey: Uint8Array, recipientPublicKey: string): Event {

return finalizeEvent({

kind: 13, // Seal

content: nip44Encrypt(event, privateKey, recipientPublicKey),

created_at: getRandomNow(),

tags: []

}, privateKey)

}

function createRumor(message: string, senderPubkey: string, receiverPubkey: string): Rumor {

const rumor = {

kind: 14, // As per specs, should be Kind 14 event

content: message,

pubkey: senderPubkey,

created_at: getNow(),

tags: [["p", receiverPubkey]],

} as Rumor

rumor.id = getEventHash(rumor)

return rumor

}

function createNip17Wraps(message: string, recipientPublicKey: string, senderSk: Uint8Array): Event {

const senderPubkey = getPublicKey(senderSk)

return createWrap(

createSeal(

createRumor(message, senderPubkey, recipientPublicKey),

senderSk, recipientPublicKey),

recipientPublicKey)

}

One fix for types:

function nip44Encrypt(rumor: Rumor | VerifiedEvent, privateKey: Uint8Array, publicKey: string): string {

const key = nip44.v2.utils.getConversationKey(privateKey, publicKey)

return nip44.v2.encrypt(JSON.stringify(rumor), key)

}

It does not have any impact, just cosmetics

It works! :)

Yeah! I do suggest to make a clear documentation about how it should be done, I will contribute if needed. It is super confusing now and no single NIP to look at 🫂😭

Maybe send a PR with some of this code to nostr-tools?

I should add nip40 expiration to the wrap too I think, if the event is OTP

Great idea

I had some other issues, and didn’t have the messages actually sent anywhere, now they are but none of the clients see them only 04. Hence my point, if it is complex and close to impossible to implement even with examples, it will not be used or will be misused or misimplemented 😭😭😭