Fix some bugs with AI
This commit is contained in:
@@ -64,11 +64,11 @@ export const notifyHandle = (handle: Handle) => handleSubscribers.forEach(sub =>
|
|||||||
export const onHandle = (sub: (handle: Handle) => void) => {
|
export const onHandle = (sub: (handle: Handle) => void) => {
|
||||||
handleSubscribers.push(sub)
|
handleSubscribers.push(sub)
|
||||||
|
|
||||||
return () =>
|
return () => {
|
||||||
handleSubscribers.splice(
|
const i = handleSubscribers.findIndex(s => s === sub)
|
||||||
handleSubscribers.findIndex(s => s === sub),
|
|
||||||
1,
|
if (i !== -1) handleSubscribers.splice(i, 1)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchHandle = batcher(800, async (nip05s: string[]) => {
|
export const fetchHandle = batcher(800, async (nip05s: string[]) => {
|
||||||
|
|||||||
@@ -10,16 +10,26 @@ import {
|
|||||||
} from "@welshman/store"
|
} from "@welshman/store"
|
||||||
import {repository} from "./core.js"
|
import {repository} from "./core.js"
|
||||||
import {ensurePlaintext} from "./plaintext.js"
|
import {ensurePlaintext} from "./plaintext.js"
|
||||||
|
import {getSession} from "./session.js"
|
||||||
import {makeOutboxLoader} from "./relayLists.js"
|
import {makeOutboxLoader} from "./relayLists.js"
|
||||||
|
|
||||||
export const muteListsByPubkey = deriveItemsByKey<PublishedList>({
|
export const muteListsByPubkey = deriveItemsByKey<PublishedList>({
|
||||||
repository,
|
repository,
|
||||||
eventToItem: async (event: TrustedEvent) =>
|
eventToItem: async (event: TrustedEvent) => {
|
||||||
readList(
|
const content = await ensurePlaintext(event)
|
||||||
asDecryptedEvent(event, {
|
|
||||||
content: await ensurePlaintext(event),
|
// If this is our own mute list (we have a session for it) but it couldn't be
|
||||||
}),
|
// decrypted yet because no signer is available, don't cache a result with empty
|
||||||
),
|
// private tags — that would get stuck permanently since deriveItemsByKey won't
|
||||||
|
// re-process an already-seen event id. Returning undefined leaves it uncached so it's
|
||||||
|
// retried once a signer is available. For other pubkeys' lists (no session) we fall
|
||||||
|
// through and read just the public tags, as before.
|
||||||
|
if (event.content && content === undefined && getSession(event.pubkey)) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return readList(asDecryptedEvent(event, {content}))
|
||||||
|
},
|
||||||
filters: [{kinds: [MUTES]}],
|
filters: [{kinds: [MUTES]}],
|
||||||
getKey: mute => mute.event.pubkey,
|
getKey: mute => mute.event.pubkey,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ export const setPlaintext = (e: TrustedEvent, content: string) =>
|
|||||||
plaintext.update(assoc(e.id, content))
|
plaintext.update(assoc(e.id, content))
|
||||||
|
|
||||||
export const ensurePlaintext = async (e: TrustedEvent) => {
|
export const ensurePlaintext = async (e: TrustedEvent) => {
|
||||||
if (e.content && !getPlaintext(e)) {
|
// Check for key presence rather than truthiness so a legitimately empty decrypted
|
||||||
|
// result ("") is treated as cached and we don't re-decrypt (and re-hit the signer) on
|
||||||
|
// every call.
|
||||||
|
if (e.content && plaintext.get()[e.id] === undefined) {
|
||||||
const $session = getSession(e.pubkey)
|
const $session = getSession(e.pubkey)
|
||||||
|
|
||||||
if (!$session) return
|
if (!$session) return
|
||||||
@@ -32,7 +35,7 @@ export const ensurePlaintext = async (e: TrustedEvent) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result !== undefined) {
|
||||||
setPlaintext(e, result)
|
setPlaintext(e, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export const loadUsingOutbox = async (kind: number, pubkey: string, filter: Filt
|
|||||||
export const makeOutboxLoader =
|
export const makeOutboxLoader =
|
||||||
(kind: number, filter: Filter = {}, limit = 1) =>
|
(kind: number, filter: Filter = {}, limit = 1) =>
|
||||||
async (pubkey: string, relayHints: string[] = []) => {
|
async (pubkey: string, relayHints: string[] = []) => {
|
||||||
const filters = [{...filter, kinds: [kind], authors: [pubkey]}]
|
const filters = [{...filter, kinds: [kind], authors: [pubkey], limit}]
|
||||||
const relays = Router.get().FromRelays(relayHints).getUrls()
|
const relays = Router.get().FromRelays(relayHints).getUrls()
|
||||||
|
|
||||||
await Promise.all([load({filters, relays}), loadUsingOutbox(kind, pubkey, filter)])
|
await Promise.all([load({filters, relays}), loadUsingOutbox(kind, pubkey, filter)])
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {writable, Subscriber} from "svelte/store"
|
import {writable, Subscriber} from "svelte/store"
|
||||||
import {getter, makeDeriveItem} from "@welshman/store"
|
import {getter, makeDeriveItem} from "@welshman/store"
|
||||||
import {groupBy, batch, now, uniq, ago, DAY, HOUR, MINUTE} from "@welshman/lib"
|
import {groupBy, batch, now, ago, DAY, HOUR, MINUTE} from "@welshman/lib"
|
||||||
import {isOnionUrl, isLocalUrl, isIPAddress, isRelayUrl, getRelaysFromList} from "@welshman/util"
|
import {isOnionUrl, isLocalUrl, isIPAddress, isRelayUrl, getRelaysFromList} from "@welshman/util"
|
||||||
import {Pool, Socket, SocketStatus, SocketEvent, ClientMessage, RelayMessage} from "@welshman/net"
|
import {Pool, Socket, SocketStatus, SocketEvent, ClientMessage, RelayMessage} from "@welshman/net"
|
||||||
import {getBlockedRelayList} from "./blockedRelayLists.js"
|
import {getBlockedRelayList} from "./blockedRelayLists.js"
|
||||||
@@ -124,7 +124,9 @@ const updateRelayStats = batch(1000, (updates: RelayStatsUpdate[]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy so the database gets updated, since we're mutating in updates
|
// Copy so the database gets updated, since we're mutating in updates
|
||||||
$relayStatsByUrl.set(url, {...$relayStatsItem})
|
const next = {...$relayStatsItem}
|
||||||
|
$relayStatsByUrl.set(url, next)
|
||||||
|
notifyRelayStats(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
return $relayStatsByUrl
|
return $relayStatsByUrl
|
||||||
@@ -223,7 +225,7 @@ const onSocketStatus = (status: string, url: string) => {
|
|||||||
url,
|
url,
|
||||||
stats => {
|
stats => {
|
||||||
stats.last_error = now()
|
stats.last_error = now()
|
||||||
stats.recent_errors = uniq(stats.recent_errors.concat(now())).slice(-10)
|
stats.recent_errors = stats.recent_errors.concat(now()).slice(-100)
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ export const notifyRelay = (relay: RelayProfile) => relaySubscribers.forEach(sub
|
|||||||
export const onRelay = (sub: (relay: RelayProfile) => void) => {
|
export const onRelay = (sub: (relay: RelayProfile) => void) => {
|
||||||
relaySubscribers.push(sub)
|
relaySubscribers.push(sub)
|
||||||
|
|
||||||
return () =>
|
return () => {
|
||||||
relaySubscribers.splice(
|
const i = relaySubscribers.findIndex(s => s === sub)
|
||||||
relaySubscribers.findIndex(s => s === sub),
|
|
||||||
1,
|
if (i !== -1) relaySubscribers.splice(i, 1)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchRelay = async (url: string): Promise<Maybe<RelayProfile>> => {
|
export const fetchRelay = async (url: string): Promise<Maybe<RelayProfile>> => {
|
||||||
|
|||||||
@@ -140,18 +140,21 @@ export class Thunk {
|
|||||||
this._notify()
|
this._notify()
|
||||||
},
|
},
|
||||||
onFailure: (result: PublishResult) => {
|
onFailure: (result: PublishResult) => {
|
||||||
|
tracker.removeRelay(event.id, result.relay)
|
||||||
this.options.onFailure?.(result)
|
this.options.onFailure?.(result)
|
||||||
this.results[result.relay] = result
|
this.results[result.relay] = result
|
||||||
this._notify()
|
this._notify()
|
||||||
},
|
},
|
||||||
onPending: this._setPending,
|
onPending: this._setPending,
|
||||||
onTimeout: this._setTimeout,
|
onTimeout: (result: PublishResult) => {
|
||||||
onAborted: this._setAborted,
|
tracker.removeRelay(event.id, result.relay)
|
||||||
|
this._setTimeout(result)
|
||||||
|
},
|
||||||
|
onAborted: (result: PublishResult) => {
|
||||||
|
tracker.removeRelay(event.id, result.relay)
|
||||||
|
this._setAborted(result)
|
||||||
|
},
|
||||||
onComplete: (result: PublishResult) => {
|
onComplete: (result: PublishResult) => {
|
||||||
if (result.status !== PublishStatus.Success) {
|
|
||||||
tracker.removeRelay(event.id, result.relay)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.options.onComplete?.(result)
|
this.options.onComplete?.(result)
|
||||||
this._subs = []
|
this._subs = []
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const topicsByName = call(() => {
|
|||||||
if (topic) {
|
if (topic) {
|
||||||
topic.count++
|
topic.count++
|
||||||
} else {
|
} else {
|
||||||
topicsByName.set(name, {name, count: 0})
|
topicsByName.set(name, {name, count: 1})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-17
@@ -1,5 +1,5 @@
|
|||||||
import {writable, Subscriber} from "svelte/store"
|
import {writable, Subscriber} from "svelte/store"
|
||||||
import {Zapper, TrustedEvent, Zap, getTagValues, getLnUrl, zapFromEvent} from "@welshman/util"
|
import {Zapper, TrustedEvent, Zap, getTagValues, zapFromEvent} from "@welshman/util"
|
||||||
import {
|
import {
|
||||||
removeUndefined,
|
removeUndefined,
|
||||||
fetchJson,
|
fetchJson,
|
||||||
@@ -35,22 +35,17 @@ export const notifyZapper = (zapper: Zapper) => zapperSubscribers.forEach(sub =>
|
|||||||
export const onZapper = (sub: (zapper: Zapper) => void) => {
|
export const onZapper = (sub: (zapper: Zapper) => void) => {
|
||||||
zapperSubscribers.push(sub)
|
zapperSubscribers.push(sub)
|
||||||
|
|
||||||
return () =>
|
return () => {
|
||||||
zapperSubscribers.splice(
|
const i = zapperSubscribers.findIndex(s => s === sub)
|
||||||
zapperSubscribers.findIndex(s => s === sub),
|
|
||||||
1,
|
if (i !== -1) zapperSubscribers.splice(i, 1)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchZapper = batcher(800, async (lnurls: string[]) => {
|
export const fetchZapper = batcher(800, async (lnurls: string[]) => {
|
||||||
const base = appContext.dufflepudUrl
|
const base = appContext.dufflepudUrl
|
||||||
const result = new Map<string, Zapper>()
|
const result = new Map<string, Zapper>()
|
||||||
|
const valid = lnurls.filter(lnurl => lnurl.startsWith("lnurl1"))
|
||||||
for (const lnurl of lnurls) {
|
|
||||||
if (!lnurl.startsWith("lnurl1")) {
|
|
||||||
throw new Error(`Invalid lnurl ${lnurl}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const addZapper = (lnurl: string, info: any) => {
|
const addZapper = (lnurl: string, info: any) => {
|
||||||
if (info) {
|
if (info) {
|
||||||
@@ -64,7 +59,7 @@ export const fetchZapper = batcher(800, async (lnurls: string[]) => {
|
|||||||
|
|
||||||
// Use dufflepud if we it's set up to protect user privacy, otherwise fetch directly
|
// Use dufflepud if we it's set up to protect user privacy, otherwise fetch directly
|
||||||
if (base) {
|
if (base) {
|
||||||
const hexUrls = lnurls.map(bech32ToHex)
|
const hexUrls = valid.map(bech32ToHex)
|
||||||
const res = await tryCatch(() => postJson(`${base}/zapper/info`, {lnurls: hexUrls}))
|
const res = await tryCatch(() => postJson(`${base}/zapper/info`, {lnurls: hexUrls}))
|
||||||
|
|
||||||
for (const {lnurl, info} of res?.data || []) {
|
for (const {lnurl, info} of res?.data || []) {
|
||||||
@@ -72,7 +67,7 @@ export const fetchZapper = batcher(800, async (lnurls: string[]) => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
lnurls.map(async lnurl => {
|
valid.map(async lnurl => {
|
||||||
addZapper(lnurl, await tryCatch(() => fetchJson(bech32ToHex(lnurl))))
|
addZapper(lnurl, await tryCatch(() => fetchJson(bech32ToHex(lnurl))))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -119,10 +114,15 @@ export const deriveZapperForPubkey = (pubkey: string, relays: string[] = []) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getLnUrlsForEvent = async (event: TrustedEvent) => {
|
export const getLnUrlsForEvent = async (event: TrustedEvent) => {
|
||||||
const lnurls = removeUndefined(getTagValues("zap", event.tags).map(getLnUrl))
|
const pubkeys = getTagValues("zap", event.tags)
|
||||||
|
|
||||||
if (lnurls.length > 0) {
|
if (pubkeys.length > 0) {
|
||||||
return lnurls
|
const profiles = await Promise.all(pubkeys.map(pubkey => loadProfile(pubkey)))
|
||||||
|
const lnurls = removeUndefined(profiles.map(profile => profile?.lnurl))
|
||||||
|
|
||||||
|
if (lnurls.length > 0) {
|
||||||
|
return lnurls
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const profile = await loadProfile(event.pubkey)
|
const profile = await loadProfile(event.pubkey)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export const publishOne = (options: PublishOneOptions) =>
|
|||||||
const cleanup = once(() => {
|
const cleanup = once(() => {
|
||||||
options.signal?.removeEventListener("abort", abort)
|
options.signal?.removeEventListener("abort", abort)
|
||||||
options.onComplete?.(result)
|
options.onComplete?.(result)
|
||||||
clearTimeout(timeoutId)
|
if (timeoutId) clearTimeout(timeoutId)
|
||||||
adapter.cleanup()
|
adapter.cleanup()
|
||||||
resolve(result)
|
resolve(result)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -149,8 +149,9 @@ export const requestOne = (options: RequestOneOptions) => {
|
|||||||
|
|
||||||
// Handle abort signal
|
// Handle abort signal
|
||||||
if (options.signal) {
|
if (options.signal) {
|
||||||
options.signal.addEventListener("abort", close)
|
const signal = options.signal
|
||||||
unsubscribers.push(() => options.signal.removeEventListener("abort", close))
|
signal.addEventListener("abort", close)
|
||||||
|
unsubscribers.push(() => signal.removeEventListener("abort", close))
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're auto-closing, make sure it happens even if the relay doesn't send an eose
|
// If we're auto-closing, make sure it happens even if the relay doesn't send an eose
|
||||||
@@ -250,7 +251,7 @@ export const makeLoader = (options: LoaderOptions) =>
|
|||||||
const threshold = options.threshold || 1
|
const threshold = options.threshold || 1
|
||||||
const tracker = new Tracker()
|
const tracker = new Tracker()
|
||||||
|
|
||||||
const abortHandlersByRequest = new Map<LoadOptions, (relay: string) => void>()
|
const abortHandlersByRequest = new Map<LoadOptions, () => void>()
|
||||||
|
|
||||||
const close = (relay: string, request: LoadOptions) => {
|
const close = (relay: string, request: LoadOptions) => {
|
||||||
addToMapKey(closedRequestsByRelay, relay, request)
|
addToMapKey(closedRequestsByRelay, relay, request)
|
||||||
|
|||||||
Reference in New Issue
Block a user