From a5aa0c5788d124341ab1516dfeceea88e6209efe Mon Sep 17 00:00:00 2001 From: Silberengel Date: Sun, 7 Jun 2026 08:53:51 +0200 Subject: [PATCH] fix blocking social kinds --- src/constants.ts | 6 ++- src/features/feed/relay-policy.ts | 4 +- src/lib/social-kind-blocked-relays.test.ts | 34 ++++++++++++++++ src/lib/social-kind-blocked-relays.ts | 47 ++++++++++++++++++++++ src/services/client.service.ts | 20 ++------- 5 files changed, 92 insertions(+), 19 deletions(-) create mode 100644 src/lib/social-kind-blocked-relays.test.ts create mode 100644 src/lib/social-kind-blocked-relays.ts diff --git a/src/constants.ts b/src/constants.ts index 259cb854..cf748d8e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -804,11 +804,15 @@ export const NOTE_STATS_OP_REFERENCE_KINDS_WITHOUT_HIGHLIGHT: readonly number[] /** * When a filter touches these kinds (or omits `kinds`), omit {@link SOCIAL_KIND_BLOCKED_RELAY_URLS} from the relay - * stack — those relays do not carry this note/comment surface (kinds **1** / **1111** / **11** per relay policy). + * stack — those relays do not carry this note/comment/reaction surface (kinds **1** / **7** / **11** / **1111** / **17**). * @see {@link relayFilterIncludesSocialKindBlockedKind} */ const SOCIAL_KIND_BLOCKED_KINDS: readonly number[] = [ kinds.ShortTextNote, + kinds.Reaction, + kinds.Repost, + ExtendedKind.GENERIC_REPOST, + ExtendedKind.EXTERNAL_REACTION, ExtendedKind.DISCUSSION, ExtendedKind.COMMENT ] diff --git a/src/features/feed/relay-policy.ts b/src/features/feed/relay-policy.ts index 0ee1bf00..d965ad48 100644 --- a/src/features/feed/relay-policy.ts +++ b/src/features/feed/relay-policy.ts @@ -1,6 +1,5 @@ import { READ_ONLY_RELAY_URLS, - SOCIAL_KIND_BLOCKED_RELAY_URLS, relayFilterIncludesSocialKindBlockedKind } from '@/constants' import { relayAllowsPublishKind } from '@/lib/relay-publish-filter' @@ -11,6 +10,7 @@ import { relayUrlsStripExtendedTagReqBlocked } from '@/lib/relay-extended-tag-req-blocks' import { isRelayBlockedByUser } from '@/lib/relay-blocked' +import { isSocialKindBlockedRelayUrl } from '@/lib/social-kind-blocked-relays' import { isLocalNetworkUrl, normalizeHttpRelayUrl, normalizeRelayUrlByScheme } from '@/lib/url' import type { TSubRequestFilter } from '@/types' @@ -134,7 +134,7 @@ function isReadOnlyRelay(norm: string): boolean { } function isSocialKindBlockedRelay(norm: string): boolean { - return normalizedSet(SOCIAL_KIND_BLOCKED_RELAY_URLS).has(norm) + return isSocialKindBlockedRelayUrl(norm) } function isExtendedTagBlockedRelay(norm: string): boolean { diff --git a/src/lib/social-kind-blocked-relays.test.ts b/src/lib/social-kind-blocked-relays.test.ts new file mode 100644 index 00000000..5675b1d1 --- /dev/null +++ b/src/lib/social-kind-blocked-relays.test.ts @@ -0,0 +1,34 @@ +import { kinds } from 'nostr-tools' +import { ExtendedKind } from '@/constants' +import { + filterPublishingRelayUrls, + filterRelayUrlsForSocialKindPublish, + isSocialKindBlockedRelayUrl +} from '@/lib/social-kind-blocked-relays' +import { describe, expect, it } from 'vitest' + +describe('social-kind-blocked-relays', () => { + it('matches essayist by hostname even with a path suffix', () => { + expect(isSocialKindBlockedRelayUrl('wss://essayist.decentnewsroom.com/')).toBe(true) + expect(isSocialKindBlockedRelayUrl('wss://essayist.decentnewsroom.com/npub1abc')).toBe(true) + }) + + it('strips essayist for kind 1 and reactions but not long-form', () => { + const urls = ['wss://relay.damus.io/', 'wss://essayist.decentnewsroom.com/'] + expect(filterRelayUrlsForSocialKindPublish(urls, kinds.ShortTextNote)).toEqual([ + 'wss://relay.damus.io/' + ]) + expect(filterRelayUrlsForSocialKindPublish(urls, kinds.Reaction)).toEqual([ + 'wss://relay.damus.io/' + ]) + expect(filterRelayUrlsForSocialKindPublish(urls, kinds.LongFormArticle)).toEqual(urls) + }) + + it('filterPublishingRelayUrls applies read-only and social filters', () => { + const out = filterPublishingRelayUrls( + ['wss://nostr.land/', 'wss://essayist.decentnewsroom.com/', 'wss://relay.damus.io/'], + ExtendedKind.COMMENT + ) + expect(out).toEqual(['wss://relay.damus.io/']) + }) +}) diff --git a/src/lib/social-kind-blocked-relays.ts b/src/lib/social-kind-blocked-relays.ts new file mode 100644 index 00000000..ab7e86af --- /dev/null +++ b/src/lib/social-kind-blocked-relays.ts @@ -0,0 +1,47 @@ +import { SOCIAL_KIND_BLOCKED_RELAY_URLS, isSocialKindBlockedKind } from '@/constants' +import { filterRelaysForEventPublish } from '@/lib/relay-publish-filter' +import { dedupeNormalizeRelayUrlsOrdered } from '@/lib/relay-url-priority' +import { normalizeRelayUrlByScheme } from '@/lib/url' + +function relayHostname(url: string): string | null { + const normalized = normalizeRelayUrlByScheme(url) || url.trim() + if (!normalized) return null + try { + return new URL(normalized).hostname.toLowerCase() + } catch { + return null + } +} + +const blockedExactKeys = new Set( + SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => (normalizeRelayUrlByScheme(u) || u).toLowerCase()).filter(Boolean) +) + +const blockedHostnames = new Set( + SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => relayHostname(u)).filter((h): h is string => !!h) +) + +/** True when `url` is (or is hosted on) a relay in {@link SOCIAL_KIND_BLOCKED_RELAY_URLS}. */ +export function isSocialKindBlockedRelayUrl(url: string): boolean { + const key = (normalizeRelayUrlByScheme(url) || url.trim()).toLowerCase() + if (!key) return false + if (blockedExactKeys.has(key)) return true + const host = relayHostname(url) + return host != null && blockedHostnames.has(host) +} + +/** Strip social-kind-blocked relays for kinds in {@link isSocialKindBlockedKind}. */ +export function filterRelayUrlsForSocialKindPublish( + urls: readonly string[], + eventKind: number +): string[] { + if (!isSocialKindBlockedKind(eventKind)) return [...urls] + return urls.filter((url) => !isSocialKindBlockedRelayUrl(url)) +} + +/** Read-only / profile-index filter + social-kind-blocked strip + dedupe (publish stack). */ +export function filterPublishingRelayUrls(urls: readonly string[], eventKind: number): string[] { + return dedupeNormalizeRelayUrlsOrdered( + filterRelayUrlsForSocialKindPublish(filterRelaysForEventPublish(urls, eventKind), eventKind) + ) +} diff --git a/src/services/client.service.ts b/src/services/client.service.ts index 78b3163a..a2b052b0 100644 --- a/src/services/client.service.ts +++ b/src/services/client.service.ts @@ -168,13 +168,14 @@ import { hexPubkeysEqual, isValidPubkey, pubkeyToNpub, userIdToPubkey } from '@/ import { collectNip05ValuesFromKind0, profileKind0MatchesSearchQuery } from '@/lib/profile-metadata-search' import { decodeProfileSearchQueryToPubkeyHex } from '@/lib/profile-search-query' import { getPubkeysFromPTags, tagNameEquals } from '@/lib/tag' -import { filterRelaysForEventPublish, isReadOnlyRelayUrl } from '@/lib/relay-publish-filter' +import { isReadOnlyRelayUrl } from '@/lib/relay-publish-filter' import { getPaymentAttestationTargetId } from '@/lib/superchat' import { buildPublicMessagePublishRelayUrls, collectRecipientInboxUrls } from '@/lib/public-message-publish-relays' import { buildPrioritizedWriteRelayUrls, dedupeNormalizeRelayUrlsOrdered } from '@/lib/relay-url-priority' +import { filterPublishingRelayUrls } from '@/lib/social-kind-blocked-relays' import { IndexRelayTransportError, isIndexRelayTransportFailure, @@ -961,14 +962,7 @@ class ClientService extends EventTarget { * Normalize, dedupe, then cap at {@link MAX_PUBLISH_RELAYS}. */ private filterPublishingRelays(relays: string[], event: NEvent): string[] { - const socialKindBlockedSet = new Set(SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u)) - return dedupeNormalizeRelayUrlsOrdered( - filterRelaysForEventPublish(relays, event.kind).filter((url) => { - const n = normalizeRelayUrlByScheme(url) || url - if (isSocialKindBlockedKind(event.kind) && socialKindBlockedSet.has(n)) return false - return true - }) - ) + return filterPublishingRelayUrls(relays, event.kind) } /** Kind 31987: always attempt the reviewed relay (`d` tag) first in the publish stack. */ @@ -1760,13 +1754,7 @@ class ClientService extends EventTarget { : relayUrls } - const socialKindBlockedSet = new Set(SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u)) - let filtered = filterRelaysForEventPublish(mergedRelayUrls, event.kind).filter((url) => { - const n = normalizeRelayUrlByScheme(url) || url - if (isSocialKindBlockedKind(event.kind) && socialKindBlockedSet.has(n)) return false - return true - }) - filtered = Array.from(new Set(filtered)) + let filtered = filterPublishingRelayUrls(mergedRelayUrls, event.kind) filtered = Array.from(new Set(filtered)) const countAfterFiltersBeforeCap = filtered.length filtered = await this.capPublishRelayUrlsForPublish(