commit
9b278cff31
11 changed files with 206 additions and 46 deletions
|
|
@ -897,6 +897,7 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
### Product
|
||||
- __id__: _string_
|
||||
- __name__: _string_
|
||||
- __noffer__: _string_
|
||||
- __price_sats__: _number_
|
||||
|
||||
### RelaysMigration
|
||||
|
|
@ -964,6 +965,7 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __max_withdrawable__: _number_
|
||||
- __network_max_fee_bps__: _number_
|
||||
- __network_max_fee_fixed__: _number_
|
||||
- __noffer__: _string_
|
||||
- __service_fee_bps__: _number_
|
||||
- __userId__: _string_
|
||||
- __user_identifier__: _string_
|
||||
|
|
|
|||
|
|
@ -1977,6 +1977,7 @@ export const PaymentStateValidate = (o?: PaymentState, opts: PaymentStateOptions
|
|||
export type Product = {
|
||||
id: string
|
||||
name: string
|
||||
noffer: string
|
||||
price_sats: number
|
||||
}
|
||||
export const ProductOptionalFields: [] = []
|
||||
|
|
@ -1984,6 +1985,7 @@ export type ProductOptions = OptionsBaseMessage & {
|
|||
checkOptionalsAreSet?: []
|
||||
id_CustomCheck?: (v: string) => boolean
|
||||
name_CustomCheck?: (v: string) => boolean
|
||||
noffer_CustomCheck?: (v: string) => boolean
|
||||
price_sats_CustomCheck?: (v: number) => boolean
|
||||
}
|
||||
export const ProductValidate = (o?: Product, opts: ProductOptions = {}, path: string = 'Product::root.'): Error | null => {
|
||||
|
|
@ -1996,6 +1998,9 @@ export const ProductValidate = (o?: Product, opts: ProductOptions = {}, path: st
|
|||
if (typeof o.name !== 'string') return new Error(`${path}.name: is not a string`)
|
||||
if (opts.name_CustomCheck && !opts.name_CustomCheck(o.name)) return new Error(`${path}.name: custom check failed`)
|
||||
|
||||
if (typeof o.noffer !== 'string') return new Error(`${path}.noffer: is not a string`)
|
||||
if (opts.noffer_CustomCheck && !opts.noffer_CustomCheck(o.noffer)) return new Error(`${path}.noffer: custom check failed`)
|
||||
|
||||
if (typeof o.price_sats !== 'number') return new Error(`${path}.price_sats: is not a number`)
|
||||
if (opts.price_sats_CustomCheck && !opts.price_sats_CustomCheck(o.price_sats)) return new Error(`${path}.price_sats: custom check failed`)
|
||||
|
||||
|
|
@ -2351,6 +2356,7 @@ export type UserInfo = {
|
|||
max_withdrawable: number
|
||||
network_max_fee_bps: number
|
||||
network_max_fee_fixed: number
|
||||
noffer: string
|
||||
service_fee_bps: number
|
||||
userId: string
|
||||
user_identifier: string
|
||||
|
|
@ -2362,6 +2368,7 @@ export type UserInfoOptions = OptionsBaseMessage & {
|
|||
max_withdrawable_CustomCheck?: (v: number) => boolean
|
||||
network_max_fee_bps_CustomCheck?: (v: number) => boolean
|
||||
network_max_fee_fixed_CustomCheck?: (v: number) => boolean
|
||||
noffer_CustomCheck?: (v: string) => boolean
|
||||
service_fee_bps_CustomCheck?: (v: number) => boolean
|
||||
userId_CustomCheck?: (v: string) => boolean
|
||||
user_identifier_CustomCheck?: (v: string) => boolean
|
||||
|
|
@ -2382,6 +2389,9 @@ export const UserInfoValidate = (o?: UserInfo, opts: UserInfoOptions = {}, path:
|
|||
if (typeof o.network_max_fee_fixed !== 'number') return new Error(`${path}.network_max_fee_fixed: is not a number`)
|
||||
if (opts.network_max_fee_fixed_CustomCheck && !opts.network_max_fee_fixed_CustomCheck(o.network_max_fee_fixed)) return new Error(`${path}.network_max_fee_fixed: custom check failed`)
|
||||
|
||||
if (typeof o.noffer !== 'string') return new Error(`${path}.noffer: is not a string`)
|
||||
if (opts.noffer_CustomCheck && !opts.noffer_CustomCheck(o.noffer)) return new Error(`${path}.noffer: custom check failed`)
|
||||
|
||||
if (typeof o.service_fee_bps !== 'number') return new Error(`${path}.service_fee_bps: is not a number`)
|
||||
if (opts.service_fee_bps_CustomCheck && !opts.service_fee_bps_CustomCheck(o.service_fee_bps)) return new Error(`${path}.service_fee_bps: custom check failed`)
|
||||
|
||||
|
|
|
|||
|
|
@ -353,6 +353,7 @@ message UserInfo{
|
|||
int64 service_fee_bps = 5;
|
||||
int64 network_max_fee_bps = 6;
|
||||
int64 network_max_fee_fixed = 7;
|
||||
string noffer = 8;
|
||||
}
|
||||
|
||||
message GetUserOperationsRequest{
|
||||
|
|
@ -409,6 +410,7 @@ message Product {
|
|||
string id = 1;
|
||||
string name = 2;
|
||||
int64 price_sats = 3;
|
||||
string noffer = 4;
|
||||
}
|
||||
|
||||
message GetProductBuyLinkResponse {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils';
|
||||
import { bech32 } from 'bech32';
|
||||
import { LoadNosrtSettingsFromEnv } from './services/nostr/index.js';
|
||||
|
||||
export const utf8Decoder = new TextDecoder('utf-8')
|
||||
export const utf8Encoder = new TextEncoder()
|
||||
|
|
@ -17,6 +18,18 @@ export type CustomProfilePointer = {
|
|||
bridge?: string[] // one bridge
|
||||
}
|
||||
|
||||
export type OfferPointer = {
|
||||
pubkey: string,
|
||||
relay: string,
|
||||
offer: string
|
||||
priceType: PriceType,
|
||||
price?: number
|
||||
}
|
||||
export enum PriceType {
|
||||
fixed = 0,
|
||||
variable = 1,
|
||||
spontaneous = 2,
|
||||
}
|
||||
|
||||
|
||||
type TLV = { [t: number]: Uint8Array[] }
|
||||
|
|
@ -54,6 +67,26 @@ export const encodeNprofile = (profile: CustomProfilePointer): string => {
|
|||
return bech32.encode("nprofile", words, 5000);
|
||||
}
|
||||
|
||||
export const encodeNoffer = (offer: OfferPointer): string => {
|
||||
let relay = offer.relay
|
||||
if (!relay) {
|
||||
const settings = LoadNosrtSettingsFromEnv()
|
||||
relay = settings.relays[0]
|
||||
}
|
||||
const o: TLV = {
|
||||
0: [hexToBytes(offer.pubkey)],
|
||||
1: [utf8Encoder.encode(relay)],
|
||||
2: [utf8Encoder.encode(offer.offer)],
|
||||
3: [new Uint8Array([Number(offer.priceType)])],
|
||||
}
|
||||
if (offer.price) {
|
||||
o[4] = [new Uint8Array(new BigUint64Array([BigInt(offer.price)]).buffer)]
|
||||
}
|
||||
const data = encodeTLV(o);
|
||||
const words = bech32.toWords(data)
|
||||
return bech32.encode("noffer", words, 5000);
|
||||
}
|
||||
|
||||
const parseTLV = (data: Uint8Array): TLV => {
|
||||
const result: TLV = {}
|
||||
let rest = data
|
||||
|
|
@ -69,10 +102,32 @@ const parseTLV = (data: Uint8Array): TLV => {
|
|||
return result
|
||||
}
|
||||
|
||||
export const decodeNoffer = (noffer: string): OfferPointer => {
|
||||
const { prefix, words } = bech32.decode(noffer, 5000)
|
||||
if (prefix !== "noffer") {
|
||||
throw new Error("Expected nprofile prefix");
|
||||
}
|
||||
const data = new Uint8Array(bech32.fromWords(words))
|
||||
|
||||
const tlv = parseTLV(data);
|
||||
if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for noffer')
|
||||
if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')
|
||||
if (!tlv[1]?.[0]) throw new Error('missing TLV 1 for noffer')
|
||||
if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for noffer')
|
||||
if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for noffer')
|
||||
return {
|
||||
pubkey: bytesToHex(tlv[0][0]),
|
||||
relay: utf8Decoder.decode(tlv[1][0]),
|
||||
offer: utf8Decoder.decode(tlv[2][0]),
|
||||
priceType: tlv[3][0][0],
|
||||
price: tlv[4] ? Number(new BigUint64Array(tlv[4][0])[0]) : undefined
|
||||
}
|
||||
}
|
||||
|
||||
export const decodeNprofile = (nprofile: string): CustomProfilePointer => {
|
||||
const { prefix, words } = bech32.decode(nprofile, 5000)
|
||||
if (prefix !== "nprofile") {
|
||||
throw new Error ("Expected nprofile prefix");
|
||||
throw new Error("Expected nprofile prefix");
|
||||
}
|
||||
const data = new Uint8Array(bech32.fromWords(words))
|
||||
|
||||
|
|
@ -83,6 +138,6 @@ export const decodeNprofile = (nprofile: string): CustomProfilePointer => {
|
|||
return {
|
||||
pubkey: bytesToHex(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],
|
||||
bridge: tlv[2] ? tlv[2].map(d => utf8Decoder.decode(d)): []
|
||||
bridge: tlv[2] ? tlv[2].map(d => utf8Decoder.decode(d)) : []
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
import Main from "./services/main/index.js"
|
||||
import Main, { NofferData } from "./services/main/index.js"
|
||||
import Nostr from "./services/nostr/index.js"
|
||||
import { NostrSend, NostrSettings } from "./services/nostr/handler.js"
|
||||
import { NostrEvent, NostrSend, NostrSettings } from "./services/nostr/handler.js"
|
||||
import * as Types from '../proto/autogenerated/ts/types.js'
|
||||
import NewNostrTransport, { NostrRequest } from '../proto/autogenerated/ts/nostr_transport.js';
|
||||
import { ERROR, getLogger } from "./services/helpers/logger.js";
|
||||
import { UnsignedEvent } from "./services/nostr/tools/event.js";
|
||||
import { defaultInvoiceExpiry } from "./services/storage/paymentStorage.js";
|
||||
import { Application } from "./services/storage/entity/Application.js";
|
||||
|
||||
export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSettings: NostrSettings, onClientEvent: (e: { requestId: string }, fromPub: string) => void): { Stop: () => void, Send: NostrSend } => {
|
||||
const log = getLogger({})
|
||||
|
|
@ -45,6 +48,11 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
|
|||
log(ERROR, "invalid json event received", event.content)
|
||||
return
|
||||
}
|
||||
if (event.kind === 21001) {
|
||||
const offerReq = j as NofferData
|
||||
mainHandler.handleNip69Noffer(offerReq, event)
|
||||
return
|
||||
}
|
||||
if (!j.rpcName) {
|
||||
onClientEvent(j as { requestId: string }, event.pub)
|
||||
return
|
||||
|
|
@ -59,3 +67,5 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
|
|||
})
|
||||
return { Stop: () => nostr.Stop, Send: (...args) => nostr.Send(...args) }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import * as Types from '../../../proto/autogenerated/ts/types.js'
|
|||
|
||||
import { MainSettings } from './settings.js'
|
||||
import ApplicationManager from './applicationManager.js'
|
||||
import { encodeNoffer, PriceType } from '../../custom-nip19.js'
|
||||
export default class {
|
||||
storage: Storage
|
||||
settings: MainSettings
|
||||
|
|
@ -59,7 +60,8 @@ export default class {
|
|||
user_identifier: appUser.identifier,
|
||||
network_max_fee_bps: this.settings.lndSettings.feeRateBps,
|
||||
network_max_fee_fixed: this.settings.lndSettings.feeFixedLimit,
|
||||
service_fee_bps: this.settings.outgoingAppUserInvoiceFeeBps
|
||||
service_fee_bps: this.settings.outgoingAppUserInvoiceFeeBps,
|
||||
noffer: encodeNoffer({ pubkey: app.nostr_public_key!, offer: appUser.identifier, priceType: PriceType.spontaneous, relay: "" })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { ApplicationUser } from '../storage/entity/ApplicationUser.js'
|
|||
import { PubLogger, getLogger } from '../helpers/logger.js'
|
||||
import crypto from 'crypto'
|
||||
import { Application } from '../storage/entity/Application.js'
|
||||
import { encodeNoffer, PriceType } from '../../custom-nip19.js'
|
||||
|
||||
const TOKEN_EXPIRY_TIME = 2 * 60 * 1000 // 2 minutes, in milliseconds
|
||||
|
||||
|
|
@ -157,7 +158,8 @@ export default class {
|
|||
user_identifier: u.identifier,
|
||||
network_max_fee_bps: this.settings.lndSettings.feeRateBps,
|
||||
network_max_fee_fixed: this.settings.lndSettings.feeFixedLimit,
|
||||
service_fee_bps: this.settings.outgoingAppUserInvoiceFeeBps
|
||||
service_fee_bps: this.settings.outgoingAppUserInvoiceFeeBps,
|
||||
noffer: encodeNoffer({ pubkey: app.nostr_public_key!, offer: u.identifier, priceType: PriceType.spontaneous, relay: "" })
|
||||
|
||||
},
|
||||
max_withdrawable: this.paymentManager.GetMaxPayableInvoice(u.user.balance_sats, true)
|
||||
|
|
@ -196,8 +198,9 @@ export default class {
|
|||
user_identifier: user.identifier,
|
||||
network_max_fee_bps: this.settings.lndSettings.feeRateBps,
|
||||
network_max_fee_fixed: this.settings.lndSettings.feeFixedLimit,
|
||||
service_fee_bps: this.settings.outgoingAppUserInvoiceFeeBps
|
||||
}
|
||||
service_fee_bps: this.settings.outgoingAppUserInvoiceFeeBps,
|
||||
noffer: encodeNoffer({ pubkey: app.nostr_public_key!, offer: user.identifier, priceType: PriceType.spontaneous, relay: "" })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import AppUserManager from "./appUserManager.js"
|
|||
import { Application } from '../storage/entity/Application.js'
|
||||
import { UserReceivingInvoice } from '../storage/entity/UserReceivingInvoice.js'
|
||||
import { UnsignedEvent } from '../nostr/tools/event.js'
|
||||
import { NostrSend } from '../nostr/handler.js'
|
||||
import { NostrEvent, NostrSend } from '../nostr/handler.js'
|
||||
import MetricsManager from '../metrics/index.js'
|
||||
import { LoggedEvent } from '../storage/eventsLog.js'
|
||||
import { LiquidityProvider } from "./liquidityProvider.js"
|
||||
|
|
@ -21,6 +21,7 @@ import { Utils } from "../helpers/utilsWrapper.js"
|
|||
import { RugPullTracker } from "./rugPullTracker.js"
|
||||
import { AdminManager } from "./adminManager.js"
|
||||
import { Unlocker } from "./unlocker.js"
|
||||
import { defaultInvoiceExpiry } from "../storage/paymentStorage.js"
|
||||
|
||||
type UserOperationsSub = {
|
||||
id: string
|
||||
|
|
@ -30,6 +31,7 @@ type UserOperationsSub = {
|
|||
newOutgoingTx: (operation: Types.UserOperation) => void
|
||||
}
|
||||
const appTag = "Lightning.Pub"
|
||||
export type NofferData = { offer: string, amount?: number }
|
||||
export default class {
|
||||
storage: Storage
|
||||
lnd: LND
|
||||
|
|
@ -46,9 +48,9 @@ export default class {
|
|||
liquidityProvider: LiquidityProvider
|
||||
utils: Utils
|
||||
rugPullTracker: RugPullTracker
|
||||
unlocker:Unlocker
|
||||
unlocker: Unlocker
|
||||
nostrSend: NostrSend = () => { getLogger({})("nostr send not initialized yet") }
|
||||
constructor(settings: MainSettings, storage: Storage, adminManager: AdminManager, utils: Utils,unlocker:Unlocker) {
|
||||
constructor(settings: MainSettings, storage: Storage, adminManager: AdminManager, utils: Utils, unlocker: Unlocker) {
|
||||
this.settings = settings
|
||||
this.storage = storage
|
||||
this.utils = utils
|
||||
|
|
@ -272,4 +274,67 @@ export default class {
|
|||
log({ unsigned: event })
|
||||
this.nostrSend({ type: 'app', appId: invoice.linkedApplication.app_id }, { type: 'event', event })
|
||||
}
|
||||
|
||||
async getNofferInvoice(offerReq: NofferData, appId: string): Promise<{ success: true, invoice: string } | { success: false, code: number, max: number }> {
|
||||
try {
|
||||
const { remote } = await this.lnd.ChannelBalance()
|
||||
const { offer, amount } = offerReq
|
||||
const split = offer.split(':')
|
||||
if (split.length === 1) {
|
||||
if (!amount || isNaN(amount) || amount < 10 || amount > remote) {
|
||||
return { success: false, code: 5, max: remote }
|
||||
}
|
||||
const res = await this.applicationManager.AddAppUserInvoice(appId, {
|
||||
http_callback_url: "", payer_identifier: split[0], receiver_identifier: split[0],
|
||||
invoice_req: { amountSats: amount, memo: "free offer" }
|
||||
})
|
||||
return { success: true, invoice: res.invoice }
|
||||
} else if (split[0] === 'p') {
|
||||
const product = await this.productManager.NewProductInvoice(split[1])
|
||||
return { success: true, invoice: product.invoice }
|
||||
} else {
|
||||
return { success: false, code: 1, max: remote }
|
||||
}
|
||||
} catch (e: any) {
|
||||
getLogger({ component: "noffer" })(ERROR, e.message || e)
|
||||
return { success: false, code: 1, max: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
async handleNip69Noffer(offerReq: NofferData, event: NostrEvent) {
|
||||
const offerInvoice = await this.getNofferInvoice(offerReq, event.appId)
|
||||
if (!offerInvoice.success) {
|
||||
const code = offerInvoice.code
|
||||
const e = newNofferResponse(JSON.stringify({ code, error: codeToMessage(code), range: { min: 10, max: offerInvoice.max } }), event)
|
||||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
||||
return
|
||||
}
|
||||
const e = newNofferResponse(JSON.stringify({ bolt11: offerInvoice.invoice }), event)
|
||||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const codeToMessage = (code: number) => {
|
||||
switch (code) {
|
||||
case 1: return 'Invalid Offer'
|
||||
case 2: return 'Temporary Failure'
|
||||
case 3: return 'Expired Offer'
|
||||
case 4: return 'Unsupported Feature'
|
||||
case 5: return 'Invalid Amount'
|
||||
default: throw new Error("unknown error code" + code)
|
||||
}
|
||||
}
|
||||
|
||||
const newNofferResponse = (content: string, event: NostrEvent): UnsignedEvent => {
|
||||
return {
|
||||
content,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
kind: 21001,
|
||||
pubkey: "",
|
||||
tags: [
|
||||
['p', event.pub],
|
||||
['e', event.id],
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import * as Types from '../../../proto/autogenerated/ts/types.js'
|
|||
import { MainSettings } from './settings.js'
|
||||
import PaymentManager from './paymentManager.js'
|
||||
import { defaultInvoiceExpiry } from '../storage/paymentStorage.js'
|
||||
import { encodeNoffer, PriceType } from '../../custom-nip19.js'
|
||||
|
||||
export default class {
|
||||
storage: Storage
|
||||
|
|
@ -20,10 +21,12 @@ export default class {
|
|||
async AddProduct(userId: string, req: Types.AddProductRequest): Promise<Types.Product> {
|
||||
const user = await this.storage.userStorage.GetUser(userId)
|
||||
const newProduct = await this.storage.productStorage.AddProduct(req.name, req.price_sats, user)
|
||||
const offer = `p:${newProduct.product_id}`
|
||||
return {
|
||||
id: newProduct.product_id,
|
||||
name: newProduct.name,
|
||||
price_sats: newProduct.price_sats,
|
||||
noffer: encodeNoffer({ pubkey: user.user_id, offer: offer, priceType: PriceType.fixed, price: newProduct.price_sats, relay: "" })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { encodeNprofile } from '../../custom-nip19.js'
|
|||
const handledEvents: string[] = [] // TODO: - big memory leak here, add TTL
|
||||
type AppInfo = { appId: string, publicKey: string, privateKey: string, name: string }
|
||||
type ClientInfo = { clientId: string, publicKey: string, privateKey: string, name: string }
|
||||
export type SendData = { type: "content", content: string, pub: string } | { type: "event", event: UnsignedEvent }
|
||||
export type SendData = { type: "content", content: string, pub: string } | { type: "event", event: UnsignedEvent, encrypt?: { toPub: string } }
|
||||
export type SendInitiator = { type: 'app', appId: string } | { type: 'client', clientId: string }
|
||||
export type NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => void
|
||||
|
||||
|
|
@ -22,6 +22,7 @@ export type NostrEvent = {
|
|||
appId: string
|
||||
startAtNano: string
|
||||
startAtMs: number
|
||||
kind: number
|
||||
}
|
||||
|
||||
type SettingsRequest = {
|
||||
|
|
@ -89,7 +90,7 @@ const sendToNostr: NostrSend = (initiator, data, relays) => {
|
|||
subProcessHandler.Send(initiator, data, relays)
|
||||
}
|
||||
send({ type: 'ready' })
|
||||
|
||||
const supportedKinds = [21000, 21001]
|
||||
export default class Handler {
|
||||
pool = new SimplePool()
|
||||
settings: NostrSettings
|
||||
|
|
@ -132,7 +133,7 @@ export default class Handler {
|
|||
const sub = relay.sub([
|
||||
{
|
||||
since: Math.ceil(Date.now() / 1000),
|
||||
kinds: [21000],
|
||||
kinds: supportedKinds,
|
||||
'#p': Object.keys(this.apps),
|
||||
}
|
||||
])
|
||||
|
|
@ -140,7 +141,7 @@ export default class Handler {
|
|||
log("up to date with nostr events")
|
||||
})
|
||||
sub.on('event', async (e) => {
|
||||
if (e.kind !== 21000 || !e.pubkey) {
|
||||
if (!supportedKinds.includes(e.kind) || !e.pubkey) {
|
||||
return
|
||||
}
|
||||
const pubTags = e.tags.find(tags => tags && tags.length > 1 && tags[0] === 'p')
|
||||
|
|
@ -155,7 +156,7 @@ export default class Handler {
|
|||
})
|
||||
}
|
||||
|
||||
async processEvent(e: Event<21000>, app: AppInfo) {
|
||||
async processEvent(e: Event, app: AppInfo) {
|
||||
const eventId = e.id
|
||||
if (handledEvents.includes(eventId)) {
|
||||
this.log("event already handled")
|
||||
|
|
@ -166,7 +167,7 @@ export default class Handler {
|
|||
const startAtNano = process.hrtime.bigint().toString()
|
||||
const decoded = decodePayload(e.content)
|
||||
const content = await decryptData(decoded, getSharedSecret(app.privateKey, e.pubkey))
|
||||
this.eventCallback({ id: eventId, content, pub: e.pubkey, appId: app.appId, startAtNano, startAtMs })
|
||||
this.eventCallback({ id: eventId, content, pub: e.pubkey, appId: app.appId, startAtNano, startAtMs, kind: e.kind })
|
||||
}
|
||||
|
||||
async Send(initiator: SendInitiator, data: SendData, relays?: string[]) {
|
||||
|
|
@ -184,6 +185,13 @@ export default class Handler {
|
|||
}
|
||||
} else {
|
||||
toSign = data.event
|
||||
if (data.encrypt) {
|
||||
const content = await encryptData(data.event.content, getSharedSecret(keys.privateKey, data.encrypt.toPub))
|
||||
toSign.content = encodePayload(content)
|
||||
}
|
||||
if (!toSign.pubkey) {
|
||||
toSign.pubkey = keys.publicKey
|
||||
}
|
||||
}
|
||||
|
||||
const signed = finishEvent(toSign, keys.privateKey)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue