feat: send simple DM

This commit is contained in:
Vlad Stan 2023-05-05 13:00:09 +03:00
parent 77f7c0b18f
commit 5d906c1fda
4 changed files with 122 additions and 6 deletions

19
helpers.py Normal file
View file

@ -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

View file

@ -58,7 +58,7 @@ class TestMessage(BaseModel):
class TestMessageResponse(BaseModel): class TestMessageResponse(BaseModel):
private_key: str private_key: str
public_key: str public_key: str
event: Event event_json: str
# class nostrKeys(BaseModel): # class nostrKeys(BaseModel):
# pubkey: str # pubkey: str

View file

@ -159,7 +159,7 @@
filled filled
rows="3" rows="3"
type="textarea" type="textarea"
label="Test Message" label="Test Message *"
></q-input> ></q-input>
</div> </div>
</div> </div>
@ -181,7 +181,7 @@
<div class="col-3"></div> <div class="col-3"></div>
<div class="col-9"> <div class="col-9">
<q-badge color="yellow" text-color="black"> <q-badge color="yellow" text-color="black">
<span>This is the recipient of the message</span> <span>This is the recipient of the message. Field required.</span>
</q-badge> </q-badge>
</div> </div>
</div> </div>
@ -189,6 +189,7 @@
<div class="col-12"> <div class="col-12">
<q-btn <q-btn
:disabled="!testData.recieverPublicKey || !testData.message" :disabled="!testData.recieverPublicKey || !testData.message"
@click="sendTestMessage"
unelevated unelevated
color="primary" color="primary"
class="float-right" class="float-right"
@ -197,6 +198,25 @@
</div> </div>
</div> </div>
</q-card-section> </q-card-section>
<q-separator></q-separator>
<q-card-section>
<div class="row q-mt-md">
<div class="col-3">
<span>Sent Data:</span>
</div>
<div class="col-9">
<q-input
outlined
v-model="testData.sentData"
dense
filled
readonly
rows="5"
type="textarea"
></q-input>
</div>
</div>
</q-card-section>
</q-card> </q-card>
</div> </div>
@ -258,9 +278,12 @@
nostrrelayLinks: [], nostrrelayLinks: [],
filter: '', filter: '',
testData: { testData: {
wsConnection: null,
senderPrivateKey: null, senderPrivateKey: null,
recieverPublicKey: null, recieverPublicKey: null,
message: null message: null,
sentData: '',
receivedData: ''
}, },
relayTable: { relayTable: {
columns: [ columns: [
@ -368,6 +391,64 @@
LNbits.utils.notifyApiError(error) 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 () { exportlnurldeviceCSV: function () {
var self = this var self = this
LNbits.utils.exportCSV(self.relayTable.columns, this.nostrLinks) LNbits.utils.exportCSV(self.relayTable.columns, this.nostrLinks)
@ -377,6 +458,7 @@
var self = this var self = this
this.getRelays() this.getRelays()
setInterval(this.getRelays, 5000) setInterval(this.getRelays, 5000)
this.testData.senderPrivateKey = this.$q.localStorage.getItem('lnbits.nostrclient.senderPrivateKey') || ''
} }
}) })
</script> </script>

View file

@ -1,4 +1,5 @@
import asyncio import asyncio
import json
from http import HTTPStatus from http import HTTPStatus
from typing import Optional from typing import Optional
@ -11,7 +12,9 @@ from lnbits.helpers import urlsafe_short_hash
from . import nostrclient_ext, scheduled_tasks from . import nostrclient_ext, scheduled_tasks
from .crud import add_relay, delete_relay, get_relays from .crud import add_relay, delete_relay, get_relays
from .helpers import normalize_public_key
from .models import Relay, RelayList, TestMessage, TestMessageResponse from .models import Relay, RelayList, TestMessage, TestMessageResponse
from .nostr.key import EncryptedDirectMessage, PrivateKey
from .services import NostrRouter, nostr from .services import NostrRouter, nostr
from .tasks import init_relays from .tasks import init_relays
@ -78,9 +81,21 @@ async def api_delete_relay(relay: Relay) -> None:
@nostrclient_ext.put( @nostrclient_ext.put(
"/api/v1/relay/test", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] "/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: 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: except (ValueError, AssertionError) as ex:
raise HTTPException( raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, status_code=HTTPStatus.BAD_REQUEST,