Add support for blocked relays

This commit is contained in:
Jon Staab
2026-01-16 14:44:11 -08:00
parent 79a5c0104e
commit e77c4d4b3d
17 changed files with 110 additions and 20 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@welshman",
"private": true,
"version": "0.8.0",
"version": "0.8.1",
"workspaces": [
"packages/*"
],
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/app",
"version": "0.8.0",
"version": "0.8.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of svelte stores for use in building nostr client applications.",
+42
View File
@@ -0,0 +1,42 @@
import {BLOCKED_RELAYS, asDecryptedEvent, readList} from "@welshman/util"
import {TrustedEvent} from "@welshman/util"
import {
deriveItemsByKey,
deriveItems,
makeForceLoadItem,
makeLoadItem,
makeDeriveItem,
getter,
} from "@welshman/store"
import {repository} from "./core.js"
import {makeOutboxLoader} from "./relayLists.js"
export const blockedRelayListsByPubkey = deriveItemsByKey({
repository,
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
filters: [{kinds: [BLOCKED_RELAYS]}],
getKey: blockedRelayLists => blockedRelayLists.event.pubkey,
})
export const blockedRelayLists = deriveItems(blockedRelayListsByPubkey)
export const getBlockedRelayListsByPubkey = getter(blockedRelayListsByPubkey)
export const getBlockedRelayLists = getter(blockedRelayLists)
export const getBlockedRelayList = (pubkey: string) => getBlockedRelayListsByPubkey().get(pubkey)
export const forceLoadBlockedRelayList = makeForceLoadItem(
makeOutboxLoader(BLOCKED_RELAYS),
getBlockedRelayList,
)
export const loadBlockedRelayList = makeLoadItem(
makeOutboxLoader(BLOCKED_RELAYS),
getBlockedRelayList,
)
export const deriveBlockedRelayList = makeDeriveItem(
blockedRelayListsByPubkey,
loadBlockedRelayList,
)
+25
View File
@@ -24,6 +24,7 @@ import {
editProfile,
RelayMode,
MESSAGING_RELAYS,
BLOCKED_RELAYS,
FOLLOWS,
RELAYS,
MUTES,
@@ -36,6 +37,8 @@ import {
forceLoadUserRelayList,
userMessagingRelayList,
forceLoadUserMessagingRelayList,
userBlockedRelayList,
forceLoadUserBlockedRelayList,
userFollowList,
forceLoadUserFollowList,
userMuteList,
@@ -105,6 +108,28 @@ export const addMessagingRelay = async (url: string) => {
return publishThunk({event, relays})
}
// NIP 51
export const removeBlockedRelay = async (url: string) => {
await forceLoadUserBlockedRelayList([])
const list = get(userBlockedRelayList) || makeList({kind: BLOCKED_RELAYS})
const event = await removeFromList(list, url).reconcile(nip44EncryptToSelf)
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
return publishThunk({event, relays})
}
export const addBlockedRelay = async (url: string) => {
await forceLoadUserBlockedRelayList([])
const list = get(userBlockedRelayList) || makeList({kind: BLOCKED_RELAYS})
const event = await addToListPublicly(list, ["relay", url]).reconcile(nip44EncryptToSelf)
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
return publishThunk({event, relays})
}
// NIP 01
export const setProfile = (profile: Profile) => {
+14 -8
View File
@@ -12,6 +12,7 @@ export * from "./pins.js"
export * from "./relays.js"
export * from "./relayStats.js"
export * from "./relayLists.js"
export * from "./blockedRelayLists.js"
export * from "./messagingRelayLists.js"
export * from "./search.js"
export * from "./session.js"
@@ -40,6 +41,7 @@ import {repository, tracker} from "./core.js"
import {getRelays, loadRelay} from "./relays.js"
import {trackRelayStats, getRelayQuality} from "./relayStats.js"
import {deriveRelayList, getRelayList} from "./relayLists.js"
import {deriveBlockedRelayList, getBlockedRelayList} from "./blockedRelayLists.js"
import {deriveMessagingRelayList, getMessagingRelayList} from "./messagingRelayLists.js"
// Sync relays with our database
@@ -84,15 +86,19 @@ const _relayGetter = (fn?: (relay: RelayProfile) => any) =>
.map(r => r.url)
})
export const getPubkeyRelays = (pubkey: string, mode?: RelayMode) =>
mode === RelayMode.Messaging
? getRelaysFromList(getMessagingRelayList(pubkey))
: getRelaysFromList(getRelayList(pubkey), mode)
export const getPubkeyRelays = (pubkey: string, mode?: RelayMode) => {
if (mode === RelayMode.Blocked) return getRelaysFromList(getBlockedRelayList(pubkey))
if (mode === RelayMode.Messaging) return getRelaysFromList(getMessagingRelayList(pubkey))
return getRelaysFromList(getRelayList(pubkey), mode)
}
export const derivePubkeyRelays = (pubkey: string, mode?: RelayMode) =>
mode === RelayMode.Messaging
? derived(deriveMessagingRelayList(pubkey), list => getRelaysFromList(list))
: derived(deriveRelayList(pubkey), list => getRelaysFromList(list, mode))
export const derivePubkeyRelays = (pubkey: string, mode?: RelayMode) => {
if (mode === RelayMode.Blocked)
return derived(deriveBlockedRelayList(pubkey), list => getRelaysFromList(list))
if (mode === RelayMode.Messaging)
return derived(deriveMessagingRelayList(pubkey), list => getRelaysFromList(list))
return derived(deriveRelayList(pubkey), list => getRelaysFromList(list, mode))
}
routerContext.getUserPubkey = () => pubkey.get()
routerContext.getPubkeyRelays = getPubkeyRelays
+7 -1
View File
@@ -1,8 +1,10 @@
import {writable, Subscriber} from "svelte/store"
import {getter, makeDeriveItem} from "@welshman/store"
import {groupBy, batch, now, uniq, ago, DAY, HOUR, MINUTE} from "@welshman/lib"
import {isOnionUrl, isLocalUrl, isIPAddress, isRelayUrl} from "@welshman/util"
import {isOnionUrl, isLocalUrl, isIPAddress, isRelayUrl, getRelaysFromList} from "@welshman/util"
import {Pool, Socket, SocketStatus, SocketEvent, ClientMessage, RelayMessage} from "@welshman/net"
import {getBlockedRelayList} from "./blockedRelayLists.js"
import {pubkey} from "./session.js"
export type RelayStats = {
url: string
@@ -75,6 +77,10 @@ export const getRelayQuality = (url: string) => {
// Skip non-relays entirely
if (!isRelayUrl(url)) return 0
const $pubkey = pubkey.get()
if ($pubkey && getRelaysFromList(getBlockedRelayList($pubkey)).includes(url)) return 0
const relayStats = getRelayStats(url)
// If we have recent errors, skip it
+9
View File
@@ -16,6 +16,11 @@ import {
forceLoadMessagingRelayList,
loadMessagingRelayList,
} from "./messagingRelayLists.js"
import {
blockedRelayListsByPubkey,
forceLoadBlockedRelayList,
loadBlockedRelayList,
} from "./blockedRelayLists.js"
import {wotGraph, getWotGraph} from "./wot.js"
export const makeUserData = <T>(
@@ -67,6 +72,10 @@ export const userMessagingRelayList = makeUserData(
export const forceLoadUserMessagingRelayList = makeUserLoader(forceLoadMessagingRelayList)
export const loadUserMessagingRelayList = makeUserLoader(loadMessagingRelayList)
export const userBlockedRelayList = makeUserData(blockedRelayListsByPubkey, loadBlockedRelayList)
export const forceLoadUserBlockedRelayList = makeUserLoader(forceLoadBlockedRelayList)
export const loadUserBlockedRelayList = makeUserLoader(loadBlockedRelayList)
export const userBlossomServerList = makeUserData(blossomServerListsByPubkey, loadBlossomServerList)
export const forceLoadUserBlossomServerList = makeUserLoader(forceLoadBlossomServerList)
export const loadUserBlossomServerList = makeUserLoader(loadBlossomServerList)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/content",
"version": "0.8.0",
"version": "0.8.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of utilities for parsing nostr note content.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/editor",
"version": "0.8.0",
"version": "0.8.1",
"author": "hodlbod",
"license": "MIT",
"description": "A batteries-included nostr editor.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/feeds",
"version": "0.8.0",
"version": "0.8.1",
"author": "hodlbod",
"license": "MIT",
"description": "Utilities for building dynamic nostr feeds.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/lib",
"version": "0.8.0",
"version": "0.8.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of utilities.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/net",
"version": "0.8.0",
"version": "0.8.1",
"author": "hodlbod",
"license": "MIT",
"description": "Utilities for connecting with nostr relays.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/router",
"version": "0.8.0",
"version": "0.8.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of utilities for nostr relay selection.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/signer",
"version": "0.8.0",
"version": "0.8.1",
"author": "hodlbod",
"license": "MIT",
"description": "A nostr signer implemenation supporting several login methods.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/store",
"version": "0.8.0",
"version": "0.8.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of utilities based on svelte/store for use with welshman",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/util",
"version": "0.8.0",
"version": "0.8.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of nostr-related utilities.",
+2
View File
@@ -5,6 +5,8 @@ import {last, normalizeUrl, stripProtocol} from "@welshman/lib"
export enum RelayMode {
Read = "read",
Write = "write",
Search = "search",
Blocked = "blocked",
Messaging = "messaging",
}