diff options
| author | Adrien Hopkins <[email protected]> | 2025-10-18 19:12:28 -0500 |
|---|---|---|
| committer | Adrien Hopkins <[email protected]> | 2025-10-18 19:50:02 -0500 |
| commit | 38a691cd6004adde24c913f69b2bbd01b2541fb9 (patch) | |
| tree | e7125bdec8c03613502955af235bb3f6bd547ee7 | |
| parent | 1f95987dbfefd5c337b32f4b01700f2525f40883 (diff) | |
| download | coinos-ui-38a691cd6004adde24c913f69b2bbd01b2541fb9.tar.gz coinos-ui-38a691cd6004adde24c913f69b2bbd01b2541fb9.zip | |
Add DM expiration functionality
| -rw-r--r-- | src/lib/nip17.ts | 23 | ||||
| -rw-r--r-- | src/routes/(app)/dm/[recipient]/+page.svelte | 23 |
2 files changed, 35 insertions, 11 deletions
diff --git a/src/lib/nip17.ts b/src/lib/nip17.ts index 6cb8f9e3..9dd59f6f 100644 --- a/src/lib/nip17.ts +++ b/src/lib/nip17.ts @@ -10,20 +10,23 @@ const randomTimestamp = () => Math.floor( // If wrapPK is specified and different than receiverPK, // the message will be gift-wrapped to wrapPK // but the rumour will be sent to receiverPK. -export const createNIP17MessageSK = (text: string, senderSK: Uint8Array, receiverPK: string, wrapPK: string = null) => { +export const createNIP17MessageSK = (text: string, senderSK: Uint8Array, receiverPK: string, wrapPK: string = null, expiryDays: number = null) => { const rumour = createRumour(text, getPublicKey(senderSK), receiverPK); const sealed = sealRumourSK(rumour, senderSK, wrapPK || receiverPK); - return giftWrap(sealed, wrapPK || receiverPK); + return giftWrap(sealed, wrapPK || receiverPK, expiryDays); } // Create a NIP-17 message using NIP-07, requiring no sender secret key. // If wrapPK is specified and different than receiverPK, // the message will be gift-wrapped to wrapPK // but the rumour will be sent to receiverPK. -export const createNIP17MessageNIP07 = async (text: string, senderPK: string, receiverPK: string, wrapPK: string = null) => { +// If expiryDays is specified, the message will expire in that many days, +// plus a random amount from 0 to 2 for security. +// (expiration time = random creation time + expiryDays + 2 days) +export const createNIP17MessageNIP07 = async (text: string, senderPK: string, receiverPK: string, wrapPK: string = null, expiryDays: number = null) => { const rumour = createRumour(text, senderPK, receiverPK); const sealed = await sealRumourNip07(rumour, wrapPK || receiverPK); - return giftWrap(sealed, wrapPK || receiverPK); + return giftWrap(sealed, wrapPK || receiverPK, expiryDays); } const createRumour = (text: string, senderPK: string, receiverPK: string) => { @@ -68,16 +71,22 @@ const sealRumourNip07 = async (rumour: object, receiverPK: string) => { return window.nostr.signEvent(sealEvent); } -const giftWrap = (event: object, receiverPK: string) => { +const giftWrap = (event: object, receiverPK: string, expiryDays: number = null) => { const secretKey = generateSecretKey(); const SKString = bytesToHex(secretKey); const conversationKey = u.getConversationKey(secretKey, receiverPK); const encryptedEvent = encrypt(JSON.stringify(event), conversationKey); + const created = randomTimestamp(); + const tags = [["p", receiverPK]]; + if (expiryDays != null) { + const expiration = created + (expiryDays + 2) * 86400; + tags.push(["expiration", expiration.toString()]); + } const giftWrapEvent = { kind: 1059, // gift wrap - created_at: randomTimestamp(), - tags: [["p", receiverPK]], + created_at: created, + tags: tags, pubkey: getPublicKey(secretKey), content: encryptedEvent } diff --git a/src/routes/(app)/dm/[recipient]/+page.svelte b/src/routes/(app)/dm/[recipient]/+page.svelte index 9bebae92..e0702bc3 100644 --- a/src/routes/(app)/dm/[recipient]/+page.svelte +++ b/src/routes/(app)/dm/[recipient]/+page.svelte @@ -61,6 +61,15 @@ return relays; } + const expired = (event: object) => { + for (const tag of event.tags) { + if (tag[0] == "expiration") { + return (Date.now() / 1000) >= Number(tag[1]); + } + } + return false; + } + const loadEvents = async (relays: string[]) => { const wrapped = await pool.querySync( relays, { kinds: [1059], "#p": [user.pubkey], limit: DM_FETCH_LIMIT } @@ -69,9 +78,11 @@ // intentionally decrypting sequentially to avoid having a bunch of popups let decryptedRumours = new Map(); for (event of wrapped) { - const rumour = await decryptMessage(event); - const created = Math.floor(rumour.created_at / 86400); - appendMultimap(decryptedRumours, created, rumour); + if (!expired(event)) { + const rumour = await decryptMessage(event); + const created = Math.floor(rumour.created_at / 86400); + appendMultimap(decryptedRumours, created, rumour); + } else console.log("relay contains expired event", event.id); } for (const [date, event] of decryptedRumours) { event.sort((ev1, ev2) => ev1.created_at - ev2.created_at); @@ -107,11 +118,15 @@ message, user.pubkey, recipient.pubkey, user.pubkey); } else { const sk = await getPrivateKey(user); - event1 = libnip17.createNIP17MessageSK(message, sk, recipient.pubkey); + event1 = libnip17.createNIP17MessageSK( + message, sk, recipient.pubkey); event2 = libnip17.createNIP17MessageSK( message, sk, recipient.pubkey, user.pubkey); } + console.log(Date.now() / 1000); + console.log(event2.tags); + const recipientRelays = await getPreferredRelays(recipient.pubkey); const p1 = Promise.any(pool.publish(recipientRelays, event1)); const senderRelays = await getPreferredRelays(user.pubkey); |
