npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

spam-score

v1.1.0

Published

Score any message or email for spam probability using weighted rule analysis. No API key needed. Works on email, Discord messages, comments, and more.

Readme

spam-score

Score any message for spam probability using weighted rule analysis. No API key. No dependencies.

npm Node.js TypeScript License: MIT


What it catches

| Category | Examples | |---|---| | 📣 Caps & punctuation | ALL CAPS text, triple exclamation marks | | 💰 Money / free offers | "Earn $500/day", "FREE gift", "guaranteed profit" | | ⏰ Urgency tactics | "Act now", "Expires today", "Last chance" | | 🔗 Suspicious links | IP-address links, URL shorteners, link display-text mismatches | | 🎭 Obfuscation | Zero-width chars, Cyrillic lookalikes, word salad | | 📧 Email header anomalies | Missing DKIM, SPF fail, Reply-To mismatch | | 📝 Known spam phrases | Nigerian prince, prize winner, pharmacy spam, investment alerts | | ✅ Ham signals (negative score) | DKIM present, SPF pass, plain-text format |


Installation

npm install spam-score

Node ≥ 18 required. Zero runtime dependencies.


Quick Start

import { scoreSpam } from 'spam-score';

const result = scoreSpam(
  'CONGRATULATIONS!!! You have WON $1000!!! Click here NOW to claim your prize before it EXPIRES!!!'
);

console.log(result.score);          // 9.5
console.log(result.probability);    // 0.927
console.log(result.verdict);        // 'definitely_spam'
console.log(result.triggeredRules); // [{id:'WINNER_CLAIM', score:3}, {id:'EXCESSIVE_CAPS',...}, ...]

API

scoreSpam(body, options?)

Score a message body for spam.

import { scoreSpam } from 'spam-score';

const r = scoreSpam(messageBody, {
  headers: { from: '[email protected]', subject: 'Hello', 'dkim-signature': '...' },
  isHtml: false,
  whitelistDomains: ['mycompany.com'],
});

Returns SpamResult:

{
  score: number;            // Total weighted score (higher = more spam)
  probability: number;      // 0.0–1.0 spam probability
  verdict: SpamVerdict;     // 'ham' | 'maybe_spam' | 'spam' | 'definitely_spam'
  triggeredRules: SpamRule[]; // Rules that fired
  allRules: SpamRule[];       // All rules evaluated
  categories: SpamCategory[]; // High-level categories triggered
  poweredBy: string;          // "@tanmaahy's spam-score package"
}

SpamRule:

{
  id: string;          // e.g. 'EXCESSIVE_CAPS'
  description: string; // human-readable
  score: number;       // contribution (negative = ham signal)
  triggered: boolean;
}

Verdict thresholds

| Score | Verdict | |---|---| | < 2.5 | ham — clean message | | 2.5 – 5.0 | maybe_spam — review recommended | | 5.0 – 8.0 | spam — block or quarantine | | > 8.0 | definitely_spam — high confidence spam |


Options

| Option | Type | Description | |---|---|---| | headers | EmailHeaders | Email headers for header-based rules (DKIM, SPF, From, Subject, etc.) | | isHtml | boolean | Force HTML mode (auto-detected if omitted) | | whitelistDomains | string[] | Sender domains to always treat as ham |

EmailHeaders accepts any of: from, to, replyTo, subject, date, receivedSpf, dkimSignature, xMailer, contentType — or any raw header key.


Rule list

| Rule ID | Score | Description | |---|---|---| | EXCESSIVE_CAPS | +2.5 | >40% uppercase characters | | ALL_CAPS_SUBJECT | +2.0 | Subject line entirely uppercase | | EXCESSIVE_EXCLAMATION | +1.5 | 3+ exclamation marks | | FREE_OFFER | +2.0 | "free" + offer keyword | | MONEY_PROMISE | +2.5 | Guaranteed income/profit | | WINNER_CLAIM | +3.0 | Congratulations + winner | | CASINO_SPAM | +2.5 | Casino/jackpot promotion | | ACT_NOW | +2.0 | "Act now", "limited time" | | URGENT_SUBJECT | +1.5 | Urgent/action-required in subject | | WORD_SALAD | +1.5 | Obfuscating nonsense words | | ZERO_WIDTH_CHARS | +3.0 | Invisible Unicode characters | | LOOKALIKE_CHARS | +2.5 | Mixed Cyrillic + Latin | | MANY_LINKS | +1.5 | More than 5 links | | LINK_MISMATCH | +2.5 | Display URL ≠ actual href | | IP_LINK | +3.0 | Raw IP address in link | | URL_SHORTENER | +1.5 | bit.ly, tinyurl, etc. | | NIGERIAN_PRINCE | +4.0 | Advance-fee fraud patterns | | CLICK_BELOW | +1.0 | "Click here to claim/verify" | | UNSUBSCRIBE_DECEPTIVE | +1.5 | Commercial email without unsubscribe | | PHARMACY_SPAM | +3.5 | Viagra/Cialis/online pharmacy | | INVESTMENT_SPAM | +2.5 | Hot stock pick / trading signal | | VERY_SHORT_BODY | +1.0 | <20 words with a link | | DECEPTIVE_SUBJECT | +1.5 | Fake Re:/Fwd: | | HAS_DKIM | -1.5 | Valid DKIM signature (ham) | | SPF_PASS | -1.0 | SPF record passes (ham) | | HAS_PLAIN_TEXT | -0.5 | Plain text (not HTML-only) (ham) |


Real-world examples

Express middleware — block spam comments

import { scoreSpam } from 'spam-score';

app.post('/comments', (req, res, next) => {
  const r = scoreSpam(req.body.text);
  if (r.verdict === 'spam' || r.verdict === 'definitely_spam') {
    return res.status(400).json({ error: 'Message blocked as spam', score: r.score });
  }
  next();
});

Discord bot — auto-delete spam

import { scoreSpam } from 'spam-score';

client.on('messageCreate', async (msg) => {
  if (msg.author.bot) return;
  const r = scoreSpam(msg.content);
  if (r.probability > 0.85) {
    await msg.delete();
    await msg.channel.send(`🚫 Message removed (spam score: ${r.score.toFixed(1)})`);
  }
});

Email processing pipeline

import { scoreSpam } from 'spam-score';

function processEmail(raw: { body: string; headers: Record<string, string> }) {
  const r = scoreSpam(raw.body, {
    headers: raw.headers,
    whitelistDomains: ['mycompany.com', 'trusted-partner.com'],
  });

  if (r.verdict === 'ham') return 'inbox';
  if (r.verdict === 'maybe_spam') return 'review';
  return 'spam';
}

Get a breakdown of why something was flagged

const r = scoreSpam(suspiciousEmail);
console.log('Triggered rules:');
for (const rule of r.triggeredRules) {
  console.log(`  [${rule.score > 0 ? '+' : ''}${rule.score}] ${rule.id}: ${rule.description}`);
}

Privacy

100% local — no data leaves your machine. No network requests, no telemetry.


License

MIT © spam-score contributors