diff options
| -rw-r--r-- | src/features/messages.js | 23 | ||||
| -rw-r--r-- | src/nostr.js | 9 |
2 files changed, 28 insertions, 4 deletions
diff --git a/src/features/messages.js b/src/features/messages.js index bfdabd1..695cba2 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -63,7 +63,10 @@ import { COMMUNITY, EPOCH } from '../community.js'; import COMMUNITY_DIRECTORY from '../community-directory.json'; import { assetUrl } from '../asset-url.js'; import { mergeFeeds } from '../feed-sync.js'; -const DM_RELAYS = ['wss://relay.coinos.io', 'wss://nos.lol']; +// coinos's own relay and primal: both take DM gift wraps and pass call +// messages (ephemeral 21059) on live. nos.lol was here until 2026-10-19 — it +// refused or ignored the household IP's publishes, call answers among them. +const DM_RELAYS = ['wss://relay.coinos.io', 'wss://relay.primal.net']; const NOTIFIER = 'https://nwcpush.coinos.io'; const APP_BASE = 'https://v3.coinos.io'; const CACHE_MAX = 50; // messages kept per channel / per DM thread in feature state @@ -9257,6 +9260,21 @@ export function messagesFeature(ctx) { }; let calls = null; const callInbox = new Map(); // peer -> their inbox relays, for this session + // Relays that turned a call message away are left out of the next ones: a + // relay that won't take the kind (nip17.com: "only kind 1059") for a day, + // one rate-limiting or banning us for ten minutes. Kept on this device. + const CALL_SHUN_KEY = 'coinos-call-shun'; + let callShun = (() => { try { return JSON.parse(localStorage.getItem(CALL_SHUN_KEY) || '{}'); } catch { return {}; } })(); // relay -> until + function callRefused(url, msg) { + const kindRule = /only kind|not (accepted|allowed)|blocked|unsupported|restricted|kind \d+/i.test(msg) && !/rate|too many|banned|slow down/i.test(msg); + callShun[url] = Date.now() + (kindRule ? 86400_000 : 600_000); + try { localStorage.setItem(CALL_SHUN_KEY, JSON.stringify(callShun)); } catch {} + } + function callRelaysOf(relays) { + const now = Date.now(); + const ok = relays.filter((r) => !(callShun[r] > now)); + return ok.length ? ok : relays; // all of them shunned: try them anyway + } async function inboxFor(pk) { if (callInbox.has(pk)) return callInbox.get(pk); const p = (isMe(pk) ? Promise.resolve(ownInbox) : fetchInboxRelays(pk).catch(() => [])) @@ -9313,7 +9331,8 @@ export function messagesFeature(ctx) { const tries = signed.kind === CALL.ICE ? 1 : 4; for (let i = 0; i < tries; i++) { if (i) await new Promise((r) => setTimeout(r, 1500 * 2 ** (i - 1))); - if (await publishOn(await inboxFor(pk), wrapCall(signed, pk), { onauth: relayAuth })) return; + const relays = callRelaysOf(await inboxFor(pk)); + if (await publishOn(relays, wrapCall(signed, pk), { onauth: relayAuth, onrefused: callRefused })) return; } throw new Error('not delivered'); }, diff --git a/src/nostr.js b/src/nostr.js index 8dcab01..c77fc27 100644 --- a/src/nostr.js +++ b/src/nostr.js @@ -498,7 +498,7 @@ export async function queryStreamOn(relays, filter, onEvent, maxWait = 1500) { }); } catch {} finally { await Promise.all(pending); } } -export async function publishOn(relays, evt, { onauth = null } = {}) { +export async function publishOn(relays, evt, { onauth = null, onrefused = null } = {}) { try { const live = liveRelays(relays); // A rejected publish is a relay refusing the event (policy, rate limit, @@ -520,7 +520,12 @@ export async function publishOn(relays, evt, { onauth = null } = {}) { // "relay connection errored/closed" means the socket never came up — // that's a sick relay, not a rejected event. if (/connect|closed|errored|timeout|network/i.test(msg)) markRelaySick(live[i], msg); - else console.warn(`nostr: publish kind ${evt.kind} refused by ${live[i]}:`, msg); + else { + console.warn(`nostr: publish kind ${evt.kind} refused by ${live[i]}:`, msg); + // the relay said no to this event (policy, a rate limit, a ban): a + // caller can stop asking it + if (onrefused) try { onrefused(live[i], msg); } catch {} + } return false; })); // The first acceptance IS delivery — one durable copy is enough, and |
