aboutsummaryrefslogtreecommitdiffstats
path: root/src/notify-text.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/notify-text.js')
-rw-r--r--src/notify-text.js79
1 files changed, 79 insertions, 0 deletions
diff --git a/src/notify-text.js b/src/notify-text.js
new file mode 100644
index 0000000..a88ab14
--- /dev/null
+++ b/src/notify-text.js
@@ -0,0 +1,79 @@
+// The words of every notification the worker shows while the app is closed.
+//
+// Chrome on Android (and, quietly, on desktop) scores each web notification's
+// title and body with an on-device "unwanted notification" model before it
+// shows it, and above 90 of 100 the notification is replaced by a "Possible
+// spam" warning — hidden until the user taps "Show notification", and nine
+// such in a week revoke the site's permission outright on a phone that
+// doesn't visit much (Chromium kAutoRevokeSuspiciousNotification). Our old
+// wording scored 92–99 across the board: "Payment received", "Incoming
+// payment", "Payment request", "alice is calling", "New message", "alice
+// sent you a message" — the exact shape of the scam notifications the model
+// was trained (on synthetic data) to catch. What it lets through is less a
+// rule than a measurement; the scores here come from Chrome 156's model via
+// tools/notif-spam-score.js, which re-scores SAMPLES in a throwaway Chrome.
+// Measured 2026-11-03: "sats" reads as money without reading as a payment
+// scam (9–14), "payment" does not (95–97); a person's NAME in the body of a
+// message or reaction line pushes it over (80–99), "New chat" / "<name>"
+// does not (8–32); "reply" in any title is 96–99, "Someone replied" /
+// "Open coinos to see it." is 13; a reply's TEXT is uncontrollable (an
+// "@name" or a link in it scores 97+), so none rides along; "Calling" /
+// "<name>" is 11–29 where every "<name> is calling" shape is 80–99.
+// Re-run the scorer before changing any of these.
+
+export const OPEN = 'Open coinos to see it.';
+
+function sats(n) {
+ const v = Number(n);
+ return v > 0 ? `+${v.toLocaleString()} sats. ${OPEN}` : OPEN;
+}
+
+// A user-facing notification by reason. `d` is the push payload (amountSat,
+// emoji) plus what the device adds (a sender's name).
+export function notifyText(reason, d = {}) {
+ switch (reason) {
+ case 'payment': return ['Sats received', sats(d.amountSat)];
+ case 'zap': return ['Zap received', sats(d.amountSat)];
+ case 'dm': return ['New chat', d.name || 'Someone wrote to you'];
+ case 'chat': return ['New chat activity', 'There are new messages in your communities.'];
+ case 'mention': return [d.reply ? 'Someone replied' : 'You were mentioned', OPEN];
+ case 'reaction': {
+ // a unicode emoji in the title is fine (17); a :shortcode: is not (85)
+ const e = d.emoji && !/^:[\w-]+:$/.test(d.emoji) ? ' ' + d.emoji : '';
+ return ['New reaction' + e, `to your post. ${OPEN}`];
+ }
+ case 'call': return ['Calling', d.name || 'Someone'];
+ // the wallet was asked for an invoice while closed (someone paying its name)
+ case 'incoming': return ['Someone is sending sats', 'Open coinos to receive them.'];
+ // a connected app asked the closed wallet to pay
+ case 'spend': return ['Spend request', 'A connected app asked to spend sats. Open coinos to approve.'];
+ default: return notifyText('chat');
+ }
+}
+
+// What tools/notif-spam-score.js scores: one line per shape a user can see.
+export const SAMPLES = [
+ notifyText('payment', { amountSat: 1000 }),
+ notifyText('payment', { amountSat: 21 }),
+ notifyText('payment'),
+ notifyText('zap', { amountSat: 1000 }),
+ notifyText('zap'),
+ notifyText('dm', { name: 'alice' }),
+ notifyText('dm', { name: 'Bitcoin Bob' }),
+ notifyText('dm', { name: 'Jarryd' }),
+ notifyText('dm'),
+ notifyText('chat'),
+ notifyText('mention'),
+ notifyText('mention', { reply: true }),
+ notifyText('reaction', { emoji: '❤️' }),
+ notifyText('reaction', { emoji: '🤙' }),
+ notifyText('reaction', { emoji: ':pepe:' }),
+ notifyText('call', { name: 'alice' }),
+ notifyText('call', { name: 'Bitcoin Bob' }),
+ notifyText('call', { name: 'Alice Caller' }),
+ notifyText('call'),
+ notifyText('incoming'),
+ notifyText('spend'),
+ ['Payment sent', '-1,000 sats via Damus'],
+ ['Spending balance', 'Receive or spend sats before 11/20/2026 to keep it alive.'],
+];