2023-08-08 18:19:31 +00:00
|
|
|
import { bech32 } from 'bech32'
|
Nostr crossposting all item types (#779)
* crosspost-item
* crosspost old items, update with nEventId
* Updating noteId encoding, cleaning up a little
* Fixing item-info condition, cleaning up
* Linting
* Add createdAt variable back
* Change instances of eventId to noteId
* Adding upsertNoteId mutation
* Cleaning up updateItem, using toasts to communivate success/failure in crosspost-item
* Linting
* Move crosspost to share button, make sure only OP can crosspost
* Lint
* Simplify conditions
* user might have no nostr extension installed
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
* change upsertNoteId to updateNoteID for resolver and mutations, change isOp to mine, remove unused noteId params
* Basic setup for crossposting poll / link items
* post rebase fixes and Bounty and job crossposts
* Job crossposting working
* adding back accidentally removed import
* Lint / rebase
* Outsource as much crossposting logic from discussion-form into use-crossposter as possible
* Fix incorrect property for user relays, fix itemId param in updateNoteId
* Fix toast messages / error cases in use-crossposter
* Update item forms to for updated use-crossposter hook
* CrosspostDropdownItem in share updated to accomodate use-crossposter update
* Encode paramaterized replacable event id's in naddress format with nostr-tools, bounty to follw nip-99 spec
* Increase timeout on relay connection / cleaning up
* No longer crossposting job
* Add blastr, fix crosspost button in item-info for polls/discussions, finish removing job crosspostr code
* Fix toaster error, create reusable crossposterror function to surface toaster
* Cleaning up / comments / linting
* Update copy
* Simplify CrosspostdropdownItem, keep replies from being crossposted
* Moved query for missing item fields when crossposting to use-crossposter hook
* Remove unneeded param in CrosspostDropdownItem, lint
* Small fixes post rebase
* Remove unused import
* fix nostr-tools version, fix package-lock.json
* Update components/item-info.js
Co-authored-by: ekzyis <ek@stacker.news>
* Remove unused param, determine poll item type from pollCost field, add mutiny strfry relay to defaults
* Update toaster implementations, use no-cache for item query, restructure crosspostItem to use await with try catch
* crosspost info modal that lives under adv-post-form now has dynamic crossposting info
* Move determineItemType into handleEventCreation, mover item/event handing outside of do ... while loop
* Lint
* Reconcile skip method with onCancel function in toaster
* Handle failedRelays being undefined
* determine item type from router.query.type if available otherwise use item fields
* Initiliaze failerRelays as undefined but handle error explicitly
* Lint
* Fix crosspost default value for link, poll, bounty forms
---------
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
Co-authored-by: ekzyis <ek@stacker.news>
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
2024-02-22 01:18:36 +00:00
|
|
|
import { nip19 } from 'nostr-tools'
|
2023-08-08 18:19:31 +00:00
|
|
|
|
2023-02-08 19:38:04 +00:00
|
|
|
export const NOSTR_PUBKEY_HEX = /^[0-9a-fA-F]{64}$/
|
|
|
|
export const NOSTR_PUBKEY_BECH32 = /^npub1[02-9ac-hj-np-z]+$/
|
|
|
|
export const NOSTR_MAX_RELAY_NUM = 20
|
2023-08-08 18:19:31 +00:00
|
|
|
export const NOSTR_ZAPPLE_PAY_NPUB = 'npub1wxl6njlcgygduct7jkgzrvyvd9fylj4pqvll6p32h59wyetm5fxqjchcan'
|
2023-10-04 18:47:09 +00:00
|
|
|
export const DEFAULT_CROSSPOSTING_RELAYS = [
|
|
|
|
'wss://nostrue.com/',
|
|
|
|
'wss://relay.damus.io/',
|
|
|
|
'wss://relay.nostr.band/',
|
|
|
|
'wss://relay.snort.social/',
|
Nostr crossposting all item types (#779)
* crosspost-item
* crosspost old items, update with nEventId
* Updating noteId encoding, cleaning up a little
* Fixing item-info condition, cleaning up
* Linting
* Add createdAt variable back
* Change instances of eventId to noteId
* Adding upsertNoteId mutation
* Cleaning up updateItem, using toasts to communivate success/failure in crosspost-item
* Linting
* Move crosspost to share button, make sure only OP can crosspost
* Lint
* Simplify conditions
* user might have no nostr extension installed
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
* change upsertNoteId to updateNoteID for resolver and mutations, change isOp to mine, remove unused noteId params
* Basic setup for crossposting poll / link items
* post rebase fixes and Bounty and job crossposts
* Job crossposting working
* adding back accidentally removed import
* Lint / rebase
* Outsource as much crossposting logic from discussion-form into use-crossposter as possible
* Fix incorrect property for user relays, fix itemId param in updateNoteId
* Fix toast messages / error cases in use-crossposter
* Update item forms to for updated use-crossposter hook
* CrosspostDropdownItem in share updated to accomodate use-crossposter update
* Encode paramaterized replacable event id's in naddress format with nostr-tools, bounty to follw nip-99 spec
* Increase timeout on relay connection / cleaning up
* No longer crossposting job
* Add blastr, fix crosspost button in item-info for polls/discussions, finish removing job crosspostr code
* Fix toaster error, create reusable crossposterror function to surface toaster
* Cleaning up / comments / linting
* Update copy
* Simplify CrosspostdropdownItem, keep replies from being crossposted
* Moved query for missing item fields when crossposting to use-crossposter hook
* Remove unneeded param in CrosspostDropdownItem, lint
* Small fixes post rebase
* Remove unused import
* fix nostr-tools version, fix package-lock.json
* Update components/item-info.js
Co-authored-by: ekzyis <ek@stacker.news>
* Remove unused param, determine poll item type from pollCost field, add mutiny strfry relay to defaults
* Update toaster implementations, use no-cache for item query, restructure crosspostItem to use await with try catch
* crosspost info modal that lives under adv-post-form now has dynamic crossposting info
* Move determineItemType into handleEventCreation, mover item/event handing outside of do ... while loop
* Lint
* Reconcile skip method with onCancel function in toaster
* Handle failedRelays being undefined
* determine item type from router.query.type if available otherwise use item fields
* Initiliaze failerRelays as undefined but handle error explicitly
* Lint
* Fix crosspost default value for link, poll, bounty forms
---------
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
Co-authored-by: ekzyis <ek@stacker.news>
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
2024-02-22 01:18:36 +00:00
|
|
|
'wss://nostr21.com/',
|
|
|
|
'wss://nostr.mutinywallet.com/',
|
|
|
|
'wss://relay.mutinywallet.com/'
|
2023-10-04 18:47:09 +00:00
|
|
|
]
|
2023-08-08 18:19:31 +00:00
|
|
|
|
|
|
|
export function hexToBech32 (hex, prefix = 'npub') {
|
|
|
|
return bech32.encode(prefix, bech32.toWords(Buffer.from(hex, 'hex')))
|
|
|
|
}
|
|
|
|
|
|
|
|
export function nostrZapDetails (zap) {
|
|
|
|
let { pubkey, content, tags } = zap
|
|
|
|
let npub = hexToBech32(pubkey)
|
|
|
|
if (npub === NOSTR_ZAPPLE_PAY_NPUB) {
|
|
|
|
const znpub = content.match(/^From: nostr:(npub1[02-9ac-hj-np-z]+)$/)?.[1]
|
|
|
|
if (znpub) {
|
|
|
|
npub = znpub
|
|
|
|
// zapple pay does not support user content
|
|
|
|
content = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const event = tags.filter(t => t?.length >= 2 && t[0] === 'e')?.[0]?.[1]
|
|
|
|
const note = event ? hexToBech32(event, 'note') : null
|
|
|
|
|
|
|
|
return { npub, content, note }
|
|
|
|
}
|
2023-10-04 18:47:09 +00:00
|
|
|
|
|
|
|
async function publishNostrEvent (signedEvent, relay) {
|
|
|
|
return new Promise((resolve, reject) => {
|
Nostr crossposting all item types (#779)
* crosspost-item
* crosspost old items, update with nEventId
* Updating noteId encoding, cleaning up a little
* Fixing item-info condition, cleaning up
* Linting
* Add createdAt variable back
* Change instances of eventId to noteId
* Adding upsertNoteId mutation
* Cleaning up updateItem, using toasts to communivate success/failure in crosspost-item
* Linting
* Move crosspost to share button, make sure only OP can crosspost
* Lint
* Simplify conditions
* user might have no nostr extension installed
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
* change upsertNoteId to updateNoteID for resolver and mutations, change isOp to mine, remove unused noteId params
* Basic setup for crossposting poll / link items
* post rebase fixes and Bounty and job crossposts
* Job crossposting working
* adding back accidentally removed import
* Lint / rebase
* Outsource as much crossposting logic from discussion-form into use-crossposter as possible
* Fix incorrect property for user relays, fix itemId param in updateNoteId
* Fix toast messages / error cases in use-crossposter
* Update item forms to for updated use-crossposter hook
* CrosspostDropdownItem in share updated to accomodate use-crossposter update
* Encode paramaterized replacable event id's in naddress format with nostr-tools, bounty to follw nip-99 spec
* Increase timeout on relay connection / cleaning up
* No longer crossposting job
* Add blastr, fix crosspost button in item-info for polls/discussions, finish removing job crosspostr code
* Fix toaster error, create reusable crossposterror function to surface toaster
* Cleaning up / comments / linting
* Update copy
* Simplify CrosspostdropdownItem, keep replies from being crossposted
* Moved query for missing item fields when crossposting to use-crossposter hook
* Remove unneeded param in CrosspostDropdownItem, lint
* Small fixes post rebase
* Remove unused import
* fix nostr-tools version, fix package-lock.json
* Update components/item-info.js
Co-authored-by: ekzyis <ek@stacker.news>
* Remove unused param, determine poll item type from pollCost field, add mutiny strfry relay to defaults
* Update toaster implementations, use no-cache for item query, restructure crosspostItem to use await with try catch
* crosspost info modal that lives under adv-post-form now has dynamic crossposting info
* Move determineItemType into handleEventCreation, mover item/event handing outside of do ... while loop
* Lint
* Reconcile skip method with onCancel function in toaster
* Handle failedRelays being undefined
* determine item type from router.query.type if available otherwise use item fields
* Initiliaze failerRelays as undefined but handle error explicitly
* Lint
* Fix crosspost default value for link, poll, bounty forms
---------
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
Co-authored-by: ekzyis <ek@stacker.news>
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
2024-02-22 01:18:36 +00:00
|
|
|
const timeout = 3000
|
2023-10-04 18:47:09 +00:00
|
|
|
const wsRelay = new window.WebSocket(relay)
|
|
|
|
let timer
|
|
|
|
let isMessageSentSuccessfully = false
|
|
|
|
|
|
|
|
function timedout () {
|
|
|
|
clearTimeout(timer)
|
|
|
|
wsRelay.close()
|
|
|
|
reject(new Error(`relay timeout for ${relay}`))
|
|
|
|
}
|
|
|
|
|
|
|
|
timer = setTimeout(timedout, timeout)
|
|
|
|
|
|
|
|
wsRelay.onopen = function () {
|
|
|
|
clearTimeout(timer)
|
|
|
|
timer = setTimeout(timedout, timeout)
|
|
|
|
wsRelay.send(JSON.stringify(['EVENT', signedEvent]))
|
|
|
|
}
|
|
|
|
|
|
|
|
wsRelay.onmessage = function (msg) {
|
|
|
|
const m = JSON.parse(msg.data)
|
|
|
|
if (m[0] === 'OK') {
|
|
|
|
isMessageSentSuccessfully = true
|
|
|
|
clearTimeout(timer)
|
|
|
|
wsRelay.close()
|
|
|
|
console.log('Successfully sent event to', relay)
|
|
|
|
resolve()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wsRelay.onerror = function (error) {
|
|
|
|
clearTimeout(timer)
|
|
|
|
console.log(error)
|
|
|
|
reject(new Error(`relay error: Failed to send to ${relay}`))
|
|
|
|
}
|
|
|
|
|
|
|
|
wsRelay.onclose = function () {
|
|
|
|
clearTimeout(timer)
|
|
|
|
if (!isMessageSentSuccessfully) {
|
|
|
|
reject(new Error(`relay error: Failed to send to ${relay}`))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function crosspost (event, relays = DEFAULT_CROSSPOSTING_RELAYS) {
|
|
|
|
try {
|
Nostr crossposting all item types (#779)
* crosspost-item
* crosspost old items, update with nEventId
* Updating noteId encoding, cleaning up a little
* Fixing item-info condition, cleaning up
* Linting
* Add createdAt variable back
* Change instances of eventId to noteId
* Adding upsertNoteId mutation
* Cleaning up updateItem, using toasts to communivate success/failure in crosspost-item
* Linting
* Move crosspost to share button, make sure only OP can crosspost
* Lint
* Simplify conditions
* user might have no nostr extension installed
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
* change upsertNoteId to updateNoteID for resolver and mutations, change isOp to mine, remove unused noteId params
* Basic setup for crossposting poll / link items
* post rebase fixes and Bounty and job crossposts
* Job crossposting working
* adding back accidentally removed import
* Lint / rebase
* Outsource as much crossposting logic from discussion-form into use-crossposter as possible
* Fix incorrect property for user relays, fix itemId param in updateNoteId
* Fix toast messages / error cases in use-crossposter
* Update item forms to for updated use-crossposter hook
* CrosspostDropdownItem in share updated to accomodate use-crossposter update
* Encode paramaterized replacable event id's in naddress format with nostr-tools, bounty to follw nip-99 spec
* Increase timeout on relay connection / cleaning up
* No longer crossposting job
* Add blastr, fix crosspost button in item-info for polls/discussions, finish removing job crosspostr code
* Fix toaster error, create reusable crossposterror function to surface toaster
* Cleaning up / comments / linting
* Update copy
* Simplify CrosspostdropdownItem, keep replies from being crossposted
* Moved query for missing item fields when crossposting to use-crossposter hook
* Remove unneeded param in CrosspostDropdownItem, lint
* Small fixes post rebase
* Remove unused import
* fix nostr-tools version, fix package-lock.json
* Update components/item-info.js
Co-authored-by: ekzyis <ek@stacker.news>
* Remove unused param, determine poll item type from pollCost field, add mutiny strfry relay to defaults
* Update toaster implementations, use no-cache for item query, restructure crosspostItem to use await with try catch
* crosspost info modal that lives under adv-post-form now has dynamic crossposting info
* Move determineItemType into handleEventCreation, mover item/event handing outside of do ... while loop
* Lint
* Reconcile skip method with onCancel function in toaster
* Handle failedRelays being undefined
* determine item type from router.query.type if available otherwise use item fields
* Initiliaze failerRelays as undefined but handle error explicitly
* Lint
* Fix crosspost default value for link, poll, bounty forms
---------
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
Co-authored-by: ekzyis <ek@stacker.news>
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
2024-02-22 01:18:36 +00:00
|
|
|
const signedEvent = await callWithTimeout(() => window.nostr.signEvent(event), 10000)
|
2023-10-04 18:47:09 +00:00
|
|
|
if (!signedEvent) throw new Error('failed to sign event')
|
|
|
|
|
|
|
|
const promises = relays.map(r => publishNostrEvent(signedEvent, r))
|
|
|
|
const results = await Promise.allSettled(promises)
|
|
|
|
const successfulRelays = []
|
|
|
|
const failedRelays = []
|
|
|
|
|
|
|
|
results.forEach((result, index) => {
|
|
|
|
if (result.status === 'fulfilled') {
|
|
|
|
successfulRelays.push(relays[index])
|
|
|
|
} else {
|
|
|
|
failedRelays.push({ relay: relays[index], error: result.reason })
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
Nostr crossposting all item types (#779)
* crosspost-item
* crosspost old items, update with nEventId
* Updating noteId encoding, cleaning up a little
* Fixing item-info condition, cleaning up
* Linting
* Add createdAt variable back
* Change instances of eventId to noteId
* Adding upsertNoteId mutation
* Cleaning up updateItem, using toasts to communivate success/failure in crosspost-item
* Linting
* Move crosspost to share button, make sure only OP can crosspost
* Lint
* Simplify conditions
* user might have no nostr extension installed
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
* change upsertNoteId to updateNoteID for resolver and mutations, change isOp to mine, remove unused noteId params
* Basic setup for crossposting poll / link items
* post rebase fixes and Bounty and job crossposts
* Job crossposting working
* adding back accidentally removed import
* Lint / rebase
* Outsource as much crossposting logic from discussion-form into use-crossposter as possible
* Fix incorrect property for user relays, fix itemId param in updateNoteId
* Fix toast messages / error cases in use-crossposter
* Update item forms to for updated use-crossposter hook
* CrosspostDropdownItem in share updated to accomodate use-crossposter update
* Encode paramaterized replacable event id's in naddress format with nostr-tools, bounty to follw nip-99 spec
* Increase timeout on relay connection / cleaning up
* No longer crossposting job
* Add blastr, fix crosspost button in item-info for polls/discussions, finish removing job crosspostr code
* Fix toaster error, create reusable crossposterror function to surface toaster
* Cleaning up / comments / linting
* Update copy
* Simplify CrosspostdropdownItem, keep replies from being crossposted
* Moved query for missing item fields when crossposting to use-crossposter hook
* Remove unneeded param in CrosspostDropdownItem, lint
* Small fixes post rebase
* Remove unused import
* fix nostr-tools version, fix package-lock.json
* Update components/item-info.js
Co-authored-by: ekzyis <ek@stacker.news>
* Remove unused param, determine poll item type from pollCost field, add mutiny strfry relay to defaults
* Update toaster implementations, use no-cache for item query, restructure crosspostItem to use await with try catch
* crosspost info modal that lives under adv-post-form now has dynamic crossposting info
* Move determineItemType into handleEventCreation, mover item/event handing outside of do ... while loop
* Lint
* Reconcile skip method with onCancel function in toaster
* Handle failedRelays being undefined
* determine item type from router.query.type if available otherwise use item fields
* Initiliaze failerRelays as undefined but handle error explicitly
* Lint
* Fix crosspost default value for link, poll, bounty forms
---------
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
Co-authored-by: ekzyis <ek@stacker.news>
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
2024-02-22 01:18:36 +00:00
|
|
|
let noteId = null
|
|
|
|
if (signedEvent.kind !== 1) {
|
|
|
|
noteId = await nip19.naddrEncode({
|
|
|
|
kind: signedEvent.kind,
|
|
|
|
pubkey: signedEvent.pubkey,
|
|
|
|
identifier: signedEvent.tags[0][1]
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
noteId = hexToBech32(signedEvent.id, 'note')
|
|
|
|
}
|
2023-10-04 18:47:09 +00:00
|
|
|
|
2023-12-19 17:48:48 +00:00
|
|
|
return { successfulRelays, failedRelays, noteId }
|
2023-10-04 18:47:09 +00:00
|
|
|
} catch (error) {
|
Nostr crossposting all item types (#779)
* crosspost-item
* crosspost old items, update with nEventId
* Updating noteId encoding, cleaning up a little
* Fixing item-info condition, cleaning up
* Linting
* Add createdAt variable back
* Change instances of eventId to noteId
* Adding upsertNoteId mutation
* Cleaning up updateItem, using toasts to communivate success/failure in crosspost-item
* Linting
* Move crosspost to share button, make sure only OP can crosspost
* Lint
* Simplify conditions
* user might have no nostr extension installed
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
* change upsertNoteId to updateNoteID for resolver and mutations, change isOp to mine, remove unused noteId params
* Basic setup for crossposting poll / link items
* post rebase fixes and Bounty and job crossposts
* Job crossposting working
* adding back accidentally removed import
* Lint / rebase
* Outsource as much crossposting logic from discussion-form into use-crossposter as possible
* Fix incorrect property for user relays, fix itemId param in updateNoteId
* Fix toast messages / error cases in use-crossposter
* Update item forms to for updated use-crossposter hook
* CrosspostDropdownItem in share updated to accomodate use-crossposter update
* Encode paramaterized replacable event id's in naddress format with nostr-tools, bounty to follw nip-99 spec
* Increase timeout on relay connection / cleaning up
* No longer crossposting job
* Add blastr, fix crosspost button in item-info for polls/discussions, finish removing job crosspostr code
* Fix toaster error, create reusable crossposterror function to surface toaster
* Cleaning up / comments / linting
* Update copy
* Simplify CrosspostdropdownItem, keep replies from being crossposted
* Moved query for missing item fields when crossposting to use-crossposter hook
* Remove unneeded param in CrosspostDropdownItem, lint
* Small fixes post rebase
* Remove unused import
* fix nostr-tools version, fix package-lock.json
* Update components/item-info.js
Co-authored-by: ekzyis <ek@stacker.news>
* Remove unused param, determine poll item type from pollCost field, add mutiny strfry relay to defaults
* Update toaster implementations, use no-cache for item query, restructure crosspostItem to use await with try catch
* crosspost info modal that lives under adv-post-form now has dynamic crossposting info
* Move determineItemType into handleEventCreation, mover item/event handing outside of do ... while loop
* Lint
* Reconcile skip method with onCancel function in toaster
* Handle failedRelays being undefined
* determine item type from router.query.type if available otherwise use item fields
* Initiliaze failerRelays as undefined but handle error explicitly
* Lint
* Fix crosspost default value for link, poll, bounty forms
---------
Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com>
Co-authored-by: ekzyis <ek@stacker.news>
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
2024-02-22 01:18:36 +00:00
|
|
|
console.error('Crosspost error:', error)
|
2023-10-04 18:47:09 +00:00
|
|
|
return { error }
|
|
|
|
}
|
|
|
|
}
|
2023-12-19 22:01:48 +00:00
|
|
|
|
|
|
|
export function callWithTimeout (targetFunction, timeoutMs) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
Promise.race([
|
|
|
|
targetFunction(),
|
|
|
|
new Promise((resolve, reject) => setTimeout(() => reject(new Error('timeouted after ' + timeoutMs + ' ms waiting for extension')), timeoutMs))
|
|
|
|
]).then(resolve)
|
|
|
|
.catch(reject)
|
|
|
|
})
|
|
|
|
}
|