From 7594c08018150d71a250b8a283b10d9d369fd0b4 Mon Sep 17 00:00:00 2001 From: Martti Malmi Date: Fri, 4 Aug 2023 13:10:46 +0300 Subject: [PATCH] save latest decrypted dm content --- src/js/LocalState.ts | 2 +- src/js/nostr/Events.ts | 63 ++++++++++++++++++++++++++++++++++------- src/js/nostr/Session.ts | 5 +++- 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/js/LocalState.ts b/src/js/LocalState.ts index bed4a324..6a5a6e1a 100644 --- a/src/js/LocalState.ts +++ b/src/js/LocalState.ts @@ -17,7 +17,7 @@ localForage.config({ /** Our very own implementation of the Gun (https://github.com/amark/gun) API. Used for local state management. */ -class Node { +export class Node { id: string; parent: Node | null; children = new Map(); diff --git a/src/js/nostr/Events.ts b/src/js/nostr/Events.ts index 1a53769c..22fe4b0b 100644 --- a/src/js/nostr/Events.ts +++ b/src/js/nostr/Events.ts @@ -4,7 +4,9 @@ import { Event, Filter, getEventHash, + getPublicKey, matchFilter, + nip04, validateEvent, verifySignature, } from 'nostr-tools'; @@ -12,7 +14,9 @@ import { EventTemplate } from 'nostr-tools'; import FuzzySearch from '../FuzzySearch'; import localState from '../LocalState'; +import { Node } from '../LocalState'; import SortedMap from '../utils/SortedMap'; +import { DecryptedEvent } from '../views/chat/ChatMessages'; import { addGroup, setGroupNameByInvite } from '../views/chat/NewChat'; import EventMetaStore from './EventsMeta'; @@ -377,14 +381,14 @@ const Events = { } this.zapsByNote.get(zappedNote)?.add(event); }, - async saveDMToLocalState(event: Event, chatId: string) { - const latest = localState.get('chats').get(chatId).get('latest'); + async saveDMToLocalState(event: DecryptedEvent, chatNode: Node) { + const latest = chatNode.get('latest'); const e = await latest.once(); if (!e || !e.created_at || e.created_at < event.created_at) { - latest.put({ id: event.id, created_at: event.created_at, text: '' }); + latest.put({ id: event.id, created_at: event.created_at, text: event.text }); } }, - async handleDirectMessage(event: Event) { + async handleDirectMessage(event: DecryptedEvent) { const myPub = Key.getPubKey(); let chatId; let maybeSecretChat = false; @@ -405,23 +409,26 @@ const Events = { try { // TODO decrypting & trying to json parse all dms might be slow, how to avoid? only process new msgs? const decrypted = await Key.decrypt(event.content, event.pubkey); - decrypted && this.decryptedMessages.set(event.id, decrypted); // don't do if maybeSecretChat? + if (decrypted) { + event.text = decrypted; + this.decryptedMessages.set(event.id, decrypted); // don't do if maybeSecretChat? + } // also save to localState, so we don't have to decrypt every time? const innerEvent = JSON.parse(decrypted.slice(decrypted.indexOf('{'))); if (validateEvent(innerEvent) && verifySignature(innerEvent)) { + console.log('innerEvent', innerEvent); // parse nsec from message by regex. nsec is bech32 encoded in the message // no follow distance check here for now const nsec = innerEvent.content.match(/nsec1[023456789acdefghjklmnpqrstuvwxyz]{6,}/gi)?.[0]; if (nsec) { const hexPriv = Key.toNostrHexAddress(nsec); if (hexPriv) { + console.log('nsec & hexpriv'); // TODO browser notification? - addGroup(hexPriv, false, innerEvent.pubkey); + addGroup(hexPriv, false, innerEvent.pubkey); // for some reason, groups don't appear on 1st load after login setGroupNameByInvite(hexPriv, innerEvent.pubkey); localState.get('chatInvites').get(innerEvent.pubkey).put({ priv: hexPriv }); - if (maybeSecretChat) { - return; - } + return; } } } @@ -442,9 +449,45 @@ const Events = { this.insert(event); if (!maybeSecretChat) { - this.saveDMToLocalState(event, chatId); + this.saveDMToLocalState(event, localState.get('chats').get(chatId)); } }, + getEventFromText(text) { + try { + const maybeJson = text.slice(text.indexOf('{')); + console.log('maybeJson', maybeJson); + const e = JSON.parse(maybeJson); + if (validateEvent(e) && verifySignature(e)) { + return e; + } + } catch (e) { + // ignore + } + }, + subscribeGroups() { + localState.get('groups').map((data, groupId) => { + if (data.key) { + const pubKey = getPublicKey(data.key); + if (pubKey) { + console.log('subscribing to group', groupId, pubKey); + PubSub.subscribe({ authors: [pubKey], kinds: [4], '#p': [pubKey] }, async (event) => { + const decrypted = await nip04.decrypt(data.key, pubKey, event.content); + const innerEvent = this.getEventFromText(decrypted); + if ( + innerEvent && + innerEvent.tags.length === 1 && + innerEvent.tags[0][0] === 'p' && + innerEvent.tags[0][1] === pubKey + ) { + innerEvent.text = innerEvent.content; + console.log('saving innerEvent', innerEvent); + this.saveDMToLocalState(innerEvent, localState.get('groups').get(groupId)); + } + }); + } + } + }); + }, handleKeyValue(event: Event) { if (event.pubkey !== Key.getPubKey()) { return; diff --git a/src/js/nostr/Session.ts b/src/js/nostr/Session.ts index e02c4f28..5b8a4a7e 100644 --- a/src/js/nostr/Session.ts +++ b/src/js/nostr/Session.ts @@ -133,7 +133,9 @@ const Session = { }); setTimeout(() => { PubSub.subscribe({ authors: [myPub] }, undefined, true); // our stuff - PubSub.subscribe({ '#p': [myPub], kinds: [1, 3, 4, 6, 7, 9735] }, undefined, true); // mentions, reactions, DMs + PubSub.subscribe({ '#p': [myPub], kinds: [1, 3, 6, 7, 9735] }, undefined, true); // mentions, reactions, DMs + PubSub.subscribe({ '#p': [myPub], kinds: [4] }, undefined, false); // comments, likes + PubSub.subscribe({ authors: [myPub], kinds: [4] }, undefined, false); // comments, likes }, 200); setInterval(() => { console.log('handled msgs per second', Math.round(Events.handledMsgsPerSecond / 5)); @@ -144,6 +146,7 @@ const Session = { Key.getOrCreate(options); localState.get('loggedIn').on(() => this.onLoggedIn()); localState.get('loggedIn').on(() => this.onLoggedIn()); + Events.subscribeGroups(); }, };