diff --git a/helpers.py b/helpers.py
new file mode 100644
index 0000000..bcf5c02
--- /dev/null
+++ b/helpers.py
@@ -0,0 +1,19 @@
+from bech32 import bech32_decode, convertbits
+
+
+def normalize_public_key(pubkey: str) -> str:
+ if pubkey.startswith("npub1"):
+ _, decoded_data = bech32_decode(pubkey)
+ if not decoded_data:
+ raise ValueError("Public Key is not valid npub")
+
+ decoded_data_bits = convertbits(decoded_data, 5, 8, False)
+ if not decoded_data_bits:
+ raise ValueError("Public Key is not valid npub")
+ return bytes(decoded_data_bits).hex()
+
+ # check if valid hex
+ if len(pubkey) != 64:
+ raise ValueError("Public Key is not valid hex")
+ int(pubkey, 16)
+ return pubkey
diff --git a/models.py b/models.py
index 899359f..1456d83 100644
--- a/models.py
+++ b/models.py
@@ -58,7 +58,7 @@ class TestMessage(BaseModel):
class TestMessageResponse(BaseModel):
private_key: str
public_key: str
- event: Event
+ event_json: str
# class nostrKeys(BaseModel):
# pubkey: str
diff --git a/templates/nostrclient/index.html b/templates/nostrclient/index.html
index 593419a..90859ae 100644
--- a/templates/nostrclient/index.html
+++ b/templates/nostrclient/index.html
@@ -159,7 +159,7 @@
filled
rows="3"
type="textarea"
- label="Test Message"
+ label="Test Message *"
>
@@ -181,7 +181,7 @@
- This is the recipient of the message
+ This is the recipient of the message. Field required.
@@ -189,6 +189,7 @@
+
+
+
+
@@ -258,9 +278,12 @@
nostrrelayLinks: [],
filter: '',
testData: {
+ wsConnection: null,
senderPrivateKey: null,
recieverPublicKey: null,
- message: null
+ message: null,
+ sentData: '',
+ receivedData: ''
},
relayTable: {
columns: [
@@ -368,6 +391,64 @@
LNbits.utils.notifyApiError(error)
})
},
+ sendTestMessage: async function(){
+ try {
+ const {data} = await LNbits.api.request(
+ 'PUT',
+ '/nostrclient/api/v1/relay/test?usr=' + this.g.user.id,
+ this.g.user.wallets[0].adminkey,
+ {
+ sender_private_key: this.testData.senderPrivateKey,
+ reciever_public_key: this.testData.recieverPublicKey,
+ message: this.testData.message
+ }
+ )
+ console.log('### data', data)
+ this.testData.senderPrivateKey = data.private_key
+ this.$q.localStorage.set('lnbits.nostrclient.senderPrivateKey', data.private_key || '')
+ const event = JSON.parse(data.event_json)[1]
+ console.log('### event', event)
+ this.sendDataToWebSocket(data.event_json)
+ } catch (error) {
+ LNbits.utils.notifyApiError(error)
+ }
+ },
+
+ sendDataToWebSocket: async function (data){
+ try {
+ if (!this.testData.wsConnection) {
+ this.connectToWebsocket()
+ }
+ this.testData.wsConnection.send(data)
+ this.testData.sentData = data + '\n' + this.testData.sentData
+ } catch (error) {
+ this.$q.notify({
+ timeout: 5000,
+ type: 'warning',
+ message: 'Failed to connect to websocket',
+ caption: `${error}`
+ })
+ }
+ },
+ connectToWebsocket: function () {
+ const scheme = location.protocol === 'http:' ? 'ws' : 'wss'
+ const port = location.port ? `:${location.port}` : ''
+ const wsUrl = `${scheme}://${document.domain}${port}/nostrclient/api/v1/relay`
+ this.testData.wsConnection = new WebSocket(wsUrl)
+ wsConnection.onmessage = async e => {
+ // const data = JSON.parse(e.data)
+ console.log('### onmessage', e.data)
+ }
+ wsConnection.onerror = async e => {
+ // const data = JSON.parse(e.data)
+ console.log('### onerror', e.data)
+ }
+ wsConnection.onclose = async e => {
+ // const data = JSON.parse(e.data)
+ console.log('### onclose', e.data)
+ }
+
+ },
exportlnurldeviceCSV: function () {
var self = this
LNbits.utils.exportCSV(self.relayTable.columns, this.nostrLinks)
@@ -377,6 +458,7 @@
var self = this
this.getRelays()
setInterval(this.getRelays, 5000)
+ this.testData.senderPrivateKey = this.$q.localStorage.getItem('lnbits.nostrclient.senderPrivateKey') || ''
}
})
diff --git a/views_api.py b/views_api.py
index 3287351..e7c5f06 100644
--- a/views_api.py
+++ b/views_api.py
@@ -1,4 +1,5 @@
import asyncio
+import json
from http import HTTPStatus
from typing import Optional
@@ -11,7 +12,9 @@ from lnbits.helpers import urlsafe_short_hash
from . import nostrclient_ext, scheduled_tasks
from .crud import add_relay, delete_relay, get_relays
+from .helpers import normalize_public_key
from .models import Relay, RelayList, TestMessage, TestMessageResponse
+from .nostr.key import EncryptedDirectMessage, PrivateKey
from .services import NostrRouter, nostr
from .tasks import init_relays
@@ -78,9 +81,21 @@ async def api_delete_relay(relay: Relay) -> None:
@nostrclient_ext.put(
"/api/v1/relay/test", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
)
-async def api_test_endpoint(test_message: TestMessage) -> TestMessageResponse:
+async def api_test_endpoint(data: TestMessage) -> TestMessageResponse:
try:
- print("### api_test_endpoint", test_message)
+ to_public_key = normalize_public_key(data.reciever_public_key)
+
+ pk = bytes.fromhex(data.sender_private_key) if data.sender_private_key else None
+ private_key = PrivateKey(pk)
+
+ dm = EncryptedDirectMessage(
+ recipient_pubkey=to_public_key, cleartext_content=data.message
+ )
+ private_key.sign_event(dm)
+
+ print("### api_test_endpoint", data)
+
+ return TestMessageResponse(private_key=private_key.hex(), public_key=to_public_key, event_json=dm.to_message())
except (ValueError, AssertionError) as ex:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,