Files
flotilla/src/app/util/wot/getPubkeyRank.ts
T

97 lines
2.4 KiB
TypeScript

import {request} from "@welshman/net"
import {clamp} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
const DEFAULT_ASSERTION_RELAYS =
"wss://nip85.brainstorm.world,wss://nip85.nosfabrica.com,wss://nip85.uid.ovh"
const toAssertionRelayUrl = (url: string) => {
const trimmed = url.trim()
if (!trimmed) return ""
if (trimmed.startsWith("http://")) return trimmed.replace(/^http:/, "ws:")
if (trimmed.startsWith("https://")) return trimmed.replace(/^https:/, "wss:")
if (!/^[a-z]+:\/\//i.test(trimmed)) return `wss://${trimmed}`
return trimmed
}
const ASSERTION_RELAYS = String(import.meta.env.VITE_ASSERTION_RELAYS || DEFAULT_ASSERTION_RELAYS)
.split(",")
.map(toAssertionRelayUrl)
.filter(Boolean)
const rankCache = new Map<string, number | null>()
const pendingRankRequests = new Map<string, Promise<number | null>>()
const getMedian = (values: number[]) => {
const sorted = [...values].sort((a, b) => a - b)
const middle = Math.floor(sorted.length / 2)
if (sorted.length % 2 === 1) {
return sorted[middle]
}
return (sorted[middle - 1] + sorted[middle]) / 2
}
const getRankFromEvent = (event: TrustedEvent) => {
const rankTag = event.tags?.find((tag: string[]) => tag[0] === "rank")
const rankValue = rankTag ? Number(rankTag[1]) : null
if (rankValue === null || !Number.isFinite(rankValue)) {
return null
}
return clamp([0, 100], rankValue)
}
const fetchPubkeyRank = async (pubkey: string): Promise<number | null> => {
if (ASSERTION_RELAYS.length === 0) {
return 50
}
try {
const events = await request({
relays: ASSERTION_RELAYS,
autoClose: true,
filters: [{kinds: [30382], "#d": [pubkey], limit: 8}],
})
const ranks = events.map(getRankFromEvent).filter((rank): rank is number => rank !== null)
if (ranks.length === 0) {
return 50
}
const median = getMedian(ranks)
return median
} catch {
return 50
}
}
export const getPubkeyRank = (pubkey: string): Promise<number | null> => {
if (rankCache.has(pubkey)) {
return Promise.resolve(rankCache.get(pubkey) ?? 50)
}
const pending = pendingRankRequests.get(pubkey)
if (pending) {
return pending
}
const requestPromise = fetchPubkeyRank(pubkey).then(rank => {
rankCache.set(pubkey, rank ?? 50)
pendingRankRequests.delete(pubkey)
return rank ?? 50
})
pendingRankRequests.set(pubkey, requestPromise)
return requestPromise
}