mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-28 16:00:52 +00:00
Improving connection management, add nip05 route calculations
This commit is contained in:
parent
5f1f9f9b69
commit
14e5be129d
@ -73,12 +73,15 @@ If you like Coracle and want to support its development, you can donate sats via
|
||||
# Current
|
||||
|
||||
- [ ] Implement gossip model https://bountsr.org/code/2023/02/03/gossip-model.html
|
||||
- [ ] Add nip 05 to calculation
|
||||
- [ ] Add connection failures to calculation
|
||||
- [_] Add nip 05 to calculation
|
||||
- [ ] Make feeds page customizable. This could potentially use the "lists" NIP
|
||||
- [ ] Show notification at top of feeds: "Showing notes from 3 relays". Click to customize.
|
||||
- [ ] Click through on relays page to view a feed for only that relay.
|
||||
- [ ] Custom views: slider between fast/complete with a warning at either extreme
|
||||
- [ ] Deterministically calculate color for relays, show it on notes. User popper?
|
||||
- [ ] Likes list
|
||||
- [ ] Fix anon/new user experience
|
||||
- [ ] Stream likes rather than load, they're probably what is slowing things down. Figure out how to multiplex them, or store them in the database by count
|
||||
|
||||
# Changelog
|
||||
|
||||
|
@ -1,25 +1,23 @@
|
||||
import type {Relay} from 'nostr-tools'
|
||||
import {relayInit} from 'nostr-tools'
|
||||
import {uniqBy, reject, prop, find, whereEq, is} from 'ramda'
|
||||
import {uniqBy, prop, find, is} from 'ramda'
|
||||
import {ensurePlural} from 'hurdak/lib/hurdak'
|
||||
import {isRelay} from 'src/util/nostr'
|
||||
import {sleep} from 'src/util/misc'
|
||||
import database from 'src/agent/database'
|
||||
|
||||
let connections = []
|
||||
const connections = []
|
||||
|
||||
class Connection {
|
||||
promise: Promise<void>
|
||||
nostr: Relay
|
||||
status: string
|
||||
url: string
|
||||
stats: Record<string, number>
|
||||
lastRequest: number
|
||||
lastConnectionAttempt: number
|
||||
constructor(url) {
|
||||
this.promise = null
|
||||
this.nostr = this.init(url)
|
||||
this.nostr = relayInit(url)
|
||||
this.status = 'new'
|
||||
this.url = url
|
||||
this.stats = {
|
||||
count: 0,
|
||||
timer: 0,
|
||||
@ -29,21 +27,12 @@ class Connection {
|
||||
|
||||
connections.push(this)
|
||||
}
|
||||
init(url) {
|
||||
const nostr = relayInit(url)
|
||||
|
||||
nostr.on('disconnect', () => {
|
||||
connections = reject(whereEq({url}), connections)
|
||||
})
|
||||
|
||||
return nostr
|
||||
}
|
||||
async connect() {
|
||||
const shouldConnect = (
|
||||
this.status === 'new'
|
||||
|| (
|
||||
this.status === 'error'
|
||||
&& Date.now() - this.lastRequest > 30_000
|
||||
&& Date.now() - this.lastConnectionAttempt > 60_000
|
||||
)
|
||||
)
|
||||
|
||||
@ -61,7 +50,7 @@ class Connection {
|
||||
}
|
||||
}
|
||||
|
||||
this.lastRequest = Date.now()
|
||||
this.lastConnectionAttempt = Date.now()
|
||||
|
||||
return this
|
||||
}
|
||||
@ -78,7 +67,7 @@ class Connection {
|
||||
|
||||
const getConnections = () => connections
|
||||
|
||||
const findConnection = url => find(whereEq({url}), connections)
|
||||
const findConnection = url => find(c => c.nostr.url === url, connections)
|
||||
|
||||
const connect = async url => {
|
||||
const conn = findConnection(url) || new Connection(url)
|
||||
@ -148,19 +137,19 @@ const subscribe = async (relays, filters, {onEvent, onEose}: Record<string, (e:
|
||||
if (!seen.has(e.id)) {
|
||||
seen.add(e.id)
|
||||
|
||||
onEvent(Object.assign(e, {seen_on: conn.url}))
|
||||
onEvent(Object.assign(e, {seen_on: conn.nostr.url}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (onEose) {
|
||||
sub.on('eose', () => onEose(conn.url))
|
||||
sub.on('eose', () => onEose(conn.nostr.url))
|
||||
}
|
||||
|
||||
conn.stats.activeCount += 1
|
||||
|
||||
if (conn.stats.activeCount > 10) {
|
||||
console.warn(`Relay ${conn.url} has >10 active subscriptions`)
|
||||
console.warn(`Relay ${conn.nostr.url} has >10 active subscriptions`)
|
||||
}
|
||||
|
||||
return Object.assign(sub, {conn})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {pick, isEmpty} from 'ramda'
|
||||
import {pick, identity, isEmpty} from 'ramda'
|
||||
import {nip05} from 'nostr-tools'
|
||||
import {noop, createMap, ensurePlural, switcherFn} from 'hurdak/lib/hurdak'
|
||||
import {now, timedelta, shuffle, hash} from 'src/util/misc'
|
||||
@ -31,8 +31,10 @@ const processProfileEvents = async events => {
|
||||
const content = JSON.parse(e.content)
|
||||
|
||||
// Fire off a nip05 verification
|
||||
if (content.nip05) {
|
||||
if (content.nip05 && e.created_at > (person.nip05_updated_at || 0)) {
|
||||
verifyNip05(e.pubkey, content.nip05)
|
||||
|
||||
content.nip05_updated_at = e.created_at
|
||||
}
|
||||
|
||||
return content
|
||||
@ -146,41 +148,45 @@ const processMessages = async events => {
|
||||
}
|
||||
}
|
||||
|
||||
// Routes
|
||||
|
||||
const getWeight = type => {
|
||||
if (type === 'kind:10001') return 1
|
||||
if (type === 'kind:3') return 0.8
|
||||
if (type === 'kind:2') return 0.5
|
||||
if (type === 'seen') return 0.2
|
||||
if (type === 'tag') return 0.1
|
||||
}
|
||||
|
||||
const calculateRoute = (pubkey, url, type, mode, created_at) => {
|
||||
if (!isRelay(url)) {
|
||||
return
|
||||
}
|
||||
|
||||
const id = hash([pubkey, url, mode].join('')).toString()
|
||||
const score = getWeight(type) * (1 - (now() - created_at) / timedelta(30, 'days'))
|
||||
const route = database.routes.get(id) || {id, pubkey, url, mode, score: 0, count: 0}
|
||||
const newTotalScore = route.score * route.count + score
|
||||
const newCount = route.count + 1
|
||||
|
||||
if (score > 0) {
|
||||
return {...route, count: newCount, score: newTotalScore / newCount}
|
||||
}
|
||||
}
|
||||
|
||||
const processRoutes = async events => {
|
||||
const updates = []
|
||||
|
||||
// Sample events so we're not burning too many resources
|
||||
events = ensurePlural(shuffle(events)).slice(0, 10)
|
||||
|
||||
const updates = {}
|
||||
|
||||
const getWeight = type => {
|
||||
if (type === 'kind:10001') return 1
|
||||
if (type === 'kind:3') return 0.8
|
||||
if (type === 'kind:2') return 0.5
|
||||
if (type === 'seen') return 0.2
|
||||
if (type === 'tag') return 0.1
|
||||
}
|
||||
|
||||
const putRoute = (pubkey, url, type, mode, created_at) => {
|
||||
if (!isRelay(url)) {
|
||||
return
|
||||
}
|
||||
|
||||
const id = hash([pubkey, url, mode].join('')).toString()
|
||||
const score = getWeight(type) * (1 - (now() - created_at) / timedelta(30, 'days'))
|
||||
const route = database.routes.get(id) || {id, pubkey, url, mode, score: 0, count: 0}
|
||||
const newTotalScore = route.score * route.count + score
|
||||
const newCount = route.count + 1
|
||||
|
||||
if (score > 0) {
|
||||
updates[id] = {...route, count: newCount, score: newTotalScore / newCount}
|
||||
}
|
||||
}
|
||||
|
||||
for (const e of events) {
|
||||
for (const e of ensurePlural(shuffle(events)).slice(0, 10)) {
|
||||
switcherFn(e.kind, {
|
||||
2: () => {
|
||||
putRoute(e.pubkey, e.content, 'kind:2', 'read', e.created_at)
|
||||
putRoute(e.pubkey, e.content, 'kind:2', 'write', e.created_at)
|
||||
updates.push(
|
||||
calculateRoute(e.pubkey, e.content, 'kind:2', 'read', e.created_at)
|
||||
)
|
||||
updates.push(
|
||||
calculateRoute(e.pubkey, e.content, 'kind:2', 'write', e.created_at)
|
||||
)
|
||||
},
|
||||
3: () => {
|
||||
tryJson(() => {
|
||||
@ -189,11 +195,15 @@ const processRoutes = async events => {
|
||||
const {write, read} = conditions as Record<string, boolean|string>
|
||||
|
||||
if (![false, '!'].includes(write)) {
|
||||
putRoute(e.pubkey, url, 'kind:3', 'write', e.created_at)
|
||||
updates.push(
|
||||
calculateRoute(e.pubkey, url, 'kind:3', 'write', e.created_at)
|
||||
)
|
||||
}
|
||||
|
||||
if (![false, '!'].includes(read)) {
|
||||
putRoute(e.pubkey, url, 'kind:3', 'read', e.created_at)
|
||||
updates.push(
|
||||
calculateRoute(e.pubkey, url, 'kind:3', 'read', e.created_at)
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -202,11 +212,15 @@ const processRoutes = async events => {
|
||||
e.tags
|
||||
.forEach(([url, read, write]) => {
|
||||
if (![false, '!'].includes(write)) {
|
||||
putRoute(e.pubkey, url, 'kind:100001', 'write', e.created_at)
|
||||
updates.push(
|
||||
calculateRoute(e.pubkey, url, 'kind:100001', 'write', e.created_at)
|
||||
)
|
||||
}
|
||||
|
||||
if (![false, '!'].includes(read)) {
|
||||
putRoute(e.pubkey, url, 'kind:100001', 'read', e.created_at)
|
||||
updates.push(
|
||||
calculateRoute(e.pubkey, url, 'kind:100001', 'read', e.created_at)
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -216,13 +230,15 @@ const processRoutes = async events => {
|
||||
// Add tag hints
|
||||
events.forEach(e => {
|
||||
Tags.wrap(e.tags).type("p").all().forEach(([_, pubkey, url]) => {
|
||||
putRoute(pubkey, url, 'tag', 'write', e.created_at)
|
||||
updates.push(
|
||||
calculateRoute(pubkey, url, 'tag', 'write', e.created_at)
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (!isEmpty(updates)) {
|
||||
await database.routes.bulkPut(updates)
|
||||
await database.routes.bulkPut(createMap('id', updates.filter(identity)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,6 +260,16 @@ const verifyNip05 = (pubkey, as) =>
|
||||
const person = database.getPersonWithFallback(pubkey)
|
||||
|
||||
database.people.patch({...person, verified_as: as})
|
||||
|
||||
if (result.relays?.length > 0) {
|
||||
console.log('===== NIP05 VERIFICATION RELAYS', result.relays)
|
||||
// database.routes.bulkPut(
|
||||
// createMap('id', result.relays.flatMap(url =>[
|
||||
// calculateRoute(pubkey, url, 'nip05', 'write', now()),
|
||||
// calculateRoute(pubkey, url, 'nip05', 'read', now()),
|
||||
// ]))
|
||||
// )
|
||||
}
|
||||
}
|
||||
}, noop)
|
||||
|
||||
|
@ -25,8 +25,8 @@
|
||||
class="absolute inset-0 mt-20 sm:mt-28 modal-content"
|
||||
transition:fly={{y: 1000, opacity: 1}}
|
||||
style={nested && `padding-top: 1rem`}>
|
||||
<dialog open class="bg-dark border-t border-solid border-medium h-full w-full overflow-auto">
|
||||
<div class="bg-dark border-t border-solid border-medium h-full w-full overflow-auto">
|
||||
<slot />
|
||||
</dialog>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {extractUrls} from "src/util/html"
|
||||
import Preview from 'src/partials/Preview.svelte'
|
||||
import Anchor from 'src/partials/Anchor.svelte'
|
||||
import {settings, modal, renderNote} from "src/app"
|
||||
import {toast, settings, modal, renderNote} from "src/app"
|
||||
import {formatTimestamp} from 'src/util/misc'
|
||||
import Badge from "src/partials/Badge.svelte"
|
||||
import Compose from "src/partials/Compose.svelte"
|
||||
@ -106,12 +106,25 @@
|
||||
replyMentions = getDefaultReplyMentions()
|
||||
}
|
||||
|
||||
const sendReply = () => {
|
||||
const sendReply = async () => {
|
||||
let {content, mentions, topics} = reply.parse()
|
||||
|
||||
if (content) {
|
||||
mentions = uniq(mentions.concat(replyMentions))
|
||||
cmd.createReply(getEventRelays(note), note, content, mentions, topics)
|
||||
|
||||
const relays = getEventRelays(note)
|
||||
const event = await cmd.createReply(relays, note, content, mentions, topics)
|
||||
|
||||
toast.show("info", {
|
||||
text: `Your note has been created!`,
|
||||
link: {
|
||||
text: 'View',
|
||||
href: "/" + nip19.neventEncode({
|
||||
id: event.id,
|
||||
relays: pluck('url', relays.slice(0, 5)),
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
resetReply()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user