diff --git a/api/nostr.py b/api/nostr.py index c46e75ca..c0334317 100644 --- a/api/nostr.py +++ b/api/nostr.py @@ -53,10 +53,10 @@ class Nostr: Tag.parse( [ "order_id", - f"{config("COORDINATOR_ALIAS", cast=str)}#{order.id}", + f"{config("COORDINATOR_ALIAS", cast=str).lower()}#{order.id}", ] ), - Tag.parse(["status", Order.Status(order.status).label]), + Tag.parse(["status", order.status]), ] await client.send_private_msg(PublicKey.parse(robot.nostr_pubkey), text, tags) diff --git a/api/notifications.py b/api/notifications.py index d4c93b0d..c07be63d 100644 --- a/api/notifications.py +++ b/api/notifications.py @@ -37,7 +37,7 @@ class Notifications: self.save_message(order, robot, title, description) if robot.nostr_pubkey: nostr_send_notification_event.delay( - robot_id=robot.id, order_id=order.id, text=description + robot_id=robot.id, order_id=order.id, text=title ) if robot.telegram_enabled: self.send_telegram_message(robot.telegram_chat_id, title, description) diff --git a/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx b/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx index 47163bd6..f6b9c317 100644 --- a/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx +++ b/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx @@ -3,6 +3,8 @@ import { Box, Drawer, List, ListItem, Typography, useTheme } from '@mui/material import { AppContext, type UseAppStoreType } from '../../../contexts/AppContext'; import { RoboSatsTextIcon } from '../../../components/Icons'; import { useTranslation } from 'react-i18next'; +import { UseFederationStoreType, FederationContext } from '../../../contexts/FederationContext'; +import { GarageContext, UseGarageStoreType } from '../../../contexts/GarageContext'; interface NotificationsDrawerProps { show: boolean; @@ -12,11 +14,25 @@ interface NotificationsDrawerProps { const NotificationsDrawer = ({ show, setShow }: NotificationsDrawerProps): React.JSX.Element => { const { t } = useTranslation(); const theme = useTheme(); - const { page } = useContext(AppContext); + const { page, settings } = useContext(AppContext); + const { federation } = useContext(FederationContext); + const { garage } = useContext(GarageContext); + useEffect(() => { setShow(false); }, [page]); + useEffect(() => { + if (settings.connection === 'nostr' && !federation.loading) loadNotifciationsNostr(); + }, [settings.connection, federation.loading]); + + const loadNotifciationsNostr = (): void => { + federation.roboPool.subscribeNotifications(garage, { + onevent: (_event) => {}, + oneose: () => {}, + }); + }; + return ( setShow(false)}> diff --git a/frontend/src/services/RoboPool/index.ts b/frontend/src/services/RoboPool/index.ts index 7e4dbb05..dc014ca8 100644 --- a/frontend/src/services/RoboPool/index.ts +++ b/frontend/src/services/RoboPool/index.ts @@ -1,5 +1,5 @@ -import { type Event } from 'nostr-tools'; -import { type Coordinator, type Settings } from '../../models'; +import { nip17, type Event } from 'nostr-tools'; +import { Garage, type Coordinator, type Settings } from '../../models'; import defaultFederation from '../../../static/federation.json'; import { websocketClient, type WebsocketConnection, WebsocketState } from '../Websocket'; import thirdParties from '../../../static/thirdparties.json'; @@ -100,14 +100,17 @@ class RoboPool { } const authors = scope.map((f) => f.nostrHexPubkey).filter((item) => item !== undefined); + const subscribeBookPending = 'subscribeBookPending'; + const subscribeBookSuccess = 'subscribeBookPending'; + const requestPending = [ 'REQ', - 'subscribeBookPending', + subscribeBookPending, { authors, kinds: [38383], '#s': ['pending'] }, ]; const requestSuccess = [ 'REQ', - 'subscribeBookSuccess', + subscribeBookSuccess, { authors, kinds: [38383], @@ -118,6 +121,9 @@ class RoboPool { this.messageHandlers.push((_url: string, messageEvent: MessageEvent) => { const jsonMessage = JSON.parse(messageEvent.data); + + if (![subscribeBookPending, subscribeBookSuccess].includes(jsonMessage[1])) return; + if (jsonMessage[0] === 'EVENT') { const event: Event = jsonMessage[2]; const network = event.tags.find((e) => e[0] === 'network'); @@ -135,14 +141,18 @@ class RoboPool { .map((f) => f.nostrHexPubkey) .filter((item) => item !== undefined); + const subscribeRatings = `subscribeRatings${id ?? ''}`; const requestRatings = [ 'REQ', - `subscribeRatings${id ?? ''}`, + subscribeRatings, { kinds: [31986], '#p': pubkeys ?? defaultPubkeys, since: 1746316800 }, ]; this.messageHandlers.push((_url: string, messageEvent: MessageEvent) => { const jsonMessage = JSON.parse(messageEvent.data); + + if (subscribeRatings !== jsonMessage[1]) return; + if (jsonMessage[0] === 'EVENT') { events.onevent(jsonMessage[2]); } else if (jsonMessage[0] === 'EOSE') { @@ -152,18 +162,38 @@ class RoboPool { this.sendMessage(JSON.stringify(requestRatings)); }; - subscribeChat = (hexPubKeys: string[], since: number, events: RoboPoolEvents): void => { - const requestRatings = ['REQ', 'subscribeChat', { kinds: [1059], '#p': hexPubKeys, since }]; + subscribeNotifications = (garage: Garage, events: RoboPoolEvents): void => { + const hexPubKeys = Object.values(garage.slots).map((s) => s.nostrPubKey); + + if (hexPubKeys.length === 0) return; + + const subscribeChat = 'subscribeChat'; + const requestNotifications = ['REQ', subscribeChat, { kinds: [1059], '#p': hexPubKeys }]; this.messageHandlers.push((_url: string, messageEvent: MessageEvent) => { const jsonMessage = JSON.parse(messageEvent.data); + + if (subscribeChat !== jsonMessage[1]) return; + if (jsonMessage[0] === 'EVENT') { - events.onevent(jsonMessage[2]); + const wrappedEvent: Event = jsonMessage[2]; + + console.log('wrappedEvent', wrappedEvent); + + const hexPubKey = wrappedEvent.tags.find((t) => t[0] == 'p')?.[1]; + + const slot = Object.values(garage.slots).find((s) => s.nostrPubKey == hexPubKey); + + if (slot?.nostrSecKey) { + const unwrappedEvent = nip17.unwrapEvent(wrappedEvent, slot.nostrSecKey); + events.onevent(unwrappedEvent as Event); + } } else if (jsonMessage[0] === 'EOSE') { events.oneose(); } }); - this.sendMessage(JSON.stringify(requestRatings)); + + this.sendMessage(JSON.stringify(requestNotifications)); }; sendEvent = (event: Event): void => {