{"_id":"@agentmarketing/payload-spam-filtering","_rev":"8-e8e90d3ca832fba6cf940bbf4b92d96d","name":"@agentmarketing/payload-spam-filtering","dist-tags":{"latest":"2.0.3"},"versions":{"1.0.0":{"name":"@agentmarketing/payload-spam-filtering","version":"1.0.0","keywords":["payload","payloadcms","payload-cms","plugin","spam","detection","gemini","google-ai","ai","form","security"],"author":{"url":"Agent Marketing","name":"Shane Farmer","email":"digital@agentmarketing.co.uk"},"license":"MIT","_id":"@agentmarketing/payload-spam-filtering@1.0.0","maintainers":[{"name":"agent-marketing","email":"digital@helloagent.co.uk"},{"name":"shane-farmer","email":"shane@helloagent.co.uk"}],"homepage":"https://www.agentmarketing.co.uk","bugs":{"url":"https://www.agentmarketing.co.uk","email":"digital@agentmarketing.co.uk"},"dist":{"shasum":"7d91bd1eec6005217ed64a677715f2846c5965e2","tarball":"https://registry.npmjs.org/@agentmarketing/payload-spam-filtering/-/payload-spam-filtering-1.0.0.tgz","fileCount":26,"integrity":"sha512-IOaEI7AAJgBnY2RxDqtx35z4FLOB7QrtraspvPI+0V4gQTNmQLEa2rNQOwXMKSruBledNpWkgBnZtTbbqPgglQ==","signatures":[{"sig":"MEUCIQCZtzrHoced8wIDEhNVSxMK60iYe0bnW50sQh2xlM1oxgIgYYrTrkhR341WQ8pqpOiXyWgB0mDG0dvN4qb8C94fVn8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":63361},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"d400539afcb2835e54947fbff6c6742ae1dbcf59","scripts":{"dev":"tsc --watch","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"shane-farmer","email":"shane@helloagent.co.uk"},"_npmVersion":"11.2.0","description":"Payload spam filtering using Google Gemini (free tier)","directories":{},"_nodeVersion":"22.11.0","dependencies":{"fs-extra":"^11.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.0.0","@types/node":"^20.0.0","@types/fs-extra":"^11.0.4"},"peerDependencies":{"payload":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/payload-spam-filtering_1.0.0_1771494051718_0.6081581903092226","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@agentmarketing/payload-spam-filtering","version":"2.0.0","keywords":["payload","payloadcms","plugin","spam","detection","gemini","ai","form","security"],"author":{"name":"Agent Systems"},"license":"MIT","_id":"@agentmarketing/payload-spam-filtering@2.0.0","maintainers":[{"name":"agent-marketing","email":"digital@helloagent.co.uk"},{"name":"jake-holcroft","email":"jake@agentmarketing.co.uk"},{"name":"shane-farmer","email":"shane@helloagent.co.uk"}],"homepage":"https://git.agentsystems.co.uk/agent/libs/agent-payload-spam-filter-plugin#readme","bugs":{"url":"https://git.agentsystems.co.uk/agent/libs/agent-payload-spam-filter-plugin/-/issues"},"bin":{"agent-spam-filter":"bin/cli.mjs"},"dist":{"shasum":"506de6c04cbad4dd6479ea6ffb4920bb24df1bee","tarball":"https://registry.npmjs.org/@agentmarketing/payload-spam-filtering/-/payload-spam-filtering-2.0.0.tgz","fileCount":26,"integrity":"sha512-qoq6nBuYHMIZVx2UXX1tp8YKuorH0TyJ4GiA26r3AiOEboO9TrdhlLAB5fDtsxufOREL16L3+GnfuPqE2LjpyA==","signatures":[{"sig":"MEYCIQDwy0d2GxNOPeAVhZ/VGQyY1v7qDxC4QS2eyU8Qbg1YMgIhAJj1S1D9JuyBuq1gfTRdvYE3f6SUFyMjBD07r07wukih","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":75933},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"9c2d691346c6f249121ee4cf9326af39e00c7655","scripts":{"dev":"tsc --watch","build":"node ./scripts/build.mjs","prepublishOnly":"node ./scripts/build.mjs"},"_npmUser":{"name":"agent-marketing","email":"digital@helloagent.co.uk"},"deprecated":"Security: contained private repo URL, upgrade to 2.0.2","repository":{"url":"https://git.agentsystems.co.uk/agent/libs/agent-payload-spam-filter-plugin.git","type":"git"},"_npmVersion":"11.11.0","description":"Payload CMS plugin for spam detection using Google Gemini AI. Includes init CLI, local pre-filter, result caching, and a Captured Spam admin collection.","directories":{},"_nodeVersion":"22.11.0","dependencies":{"fs-extra":"^11.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.0.0","@types/node":"^20.0.0","@types/fs-extra":"^11.0.4"},"peerDependencies":{"payload":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/payload-spam-filtering_2.0.0_1777541740586_0.8027089784682537","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@agentmarketing/payload-spam-filtering","version":"2.0.1","keywords":["payload","payloadcms","plugin","spam","detection","gemini","ai","form","security"],"author":{"name":"Agent Systems"},"license":"MIT","_id":"@agentmarketing/payload-spam-filtering@2.0.1","maintainers":[{"name":"agent-marketing","email":"digital@helloagent.co.uk"},{"name":"jake-holcroft","email":"jake@agentmarketing.co.uk"},{"name":"shane-farmer","email":"shane@helloagent.co.uk"}],"homepage":"https://git.agentsystems.co.uk/agent/libs/agent-payload-spam-filter-plugin#readme","bugs":{"url":"https://git.agentsystems.co.uk/agent/libs/agent-payload-spam-filter-plugin/-/issues"},"bin":{"agent-spam-filter":"bin/cli.mjs"},"dist":{"shasum":"3e1827563b7cd9815b192ba90ae6c37a121508a6","tarball":"https://registry.npmjs.org/@agentmarketing/payload-spam-filtering/-/payload-spam-filtering-2.0.1.tgz","fileCount":26,"integrity":"sha512-Lcqds4PhFIMJU3CdDqgJQ4C5rqYpblKiL+OGye/xIG1Jxz730MkK9YvrnBxFykDhVH3+4uOhspWQsE/eRUD4FQ==","signatures":[{"sig":"MEUCIE7rmzvr1agBGYXfvazGcIA+vPfp1cdN0FWewQaVyrxcAiEAg9T4mD/CeSL75Fj7tkNAZVgCU770tcT+yoSSfjnBZvo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":76921},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"9c2d691346c6f249121ee4cf9326af39e00c7655","scripts":{"dev":"tsc --watch","build":"node ./scripts/build.mjs","prepublishOnly":"node ./scripts/build.mjs"},"_npmUser":{"name":"agent-marketing","email":"digital@helloagent.co.uk"},"deprecated":"Security: contained private repo URL, upgrade to 2.0.2","repository":{"url":"https://git.agentsystems.co.uk/agent/libs/agent-payload-spam-filter-plugin.git","type":"git"},"_npmVersion":"11.11.0","description":"Payload CMS plugin for spam detection using Google Gemini AI. Includes init CLI, local pre-filter, result caching, and a Captured Spam admin collection.","directories":{},"_nodeVersion":"22.11.0","dependencies":{"fs-extra":"^11.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.0.0","@types/node":"^20.0.0","@types/fs-extra":"^11.0.4"},"peerDependencies":{"payload":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/payload-spam-filtering_2.0.1_1778058523967_0.0684470230705767","host":"s3://npm-registry-packages-npm-production"}},"2.0.2":{"name":"@agentmarketing/payload-spam-filtering","version":"2.0.2","keywords":["payload","payloadcms","plugin","spam","detection","gemini","ai","form","security"],"author":{"name":"Agent Systems"},"license":"MIT","_id":"@agentmarketing/payload-spam-filtering@2.0.2","maintainers":[{"name":"agent-marketing","email":"digital@helloagent.co.uk"},{"name":"jake-holcroft","email":"jake@agentmarketing.co.uk"},{"name":"shane-farmer","email":"shane@helloagent.co.uk"}],"bin":{"agent-spam-filter":"bin/cli.mjs"},"dist":{"shasum":"a92952738ceb655334ec01525260dfde8affc1ff","tarball":"https://registry.npmjs.org/@agentmarketing/payload-spam-filtering/-/payload-spam-filtering-2.0.2.tgz","fileCount":26,"integrity":"sha512-/rRpHXD5KhynFRIJmcjCWjCRgY+f/P06dngHK162pvVlOGTG25o2ii8pfyUH0UX7V18k7oZdgJiwzYgfy6Lq6Q==","signatures":[{"sig":"MEUCIHn3CLa0QFpcDJ6UdmubYuW/8l9Il7zlKLSe7sqsMtLWAiEAxfnIyf5DdbIuIDr80Vg65sCEiBbRGfLdRO08zMHhQzM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":76347},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"9c2d691346c6f249121ee4cf9326af39e00c7655","scripts":{"dev":"tsc --watch","build":"node ./scripts/build.mjs","prepublishOnly":"node ./scripts/build.mjs"},"_npmUser":{"name":"agent-marketing","email":"digital@helloagent.co.uk"},"_npmVersion":"11.11.0","description":"Payload CMS plugin for spam detection using Google Gemini AI. Includes init CLI, local pre-filter, result caching, and a Captured Spam admin collection.","directories":{},"_nodeVersion":"22.11.0","dependencies":{"fs-extra":"^11.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.0.0","@types/node":"^20.0.0","@types/fs-extra":"^11.0.4"},"peerDependencies":{"payload":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/payload-spam-filtering_2.0.2_1778058719145_0.717762410408761","host":"s3://npm-registry-packages-npm-production"}},"2.0.3":{"name":"@agentmarketing/payload-spam-filtering","version":"2.0.3","description":"Payload CMS plugin for spam detection using Google Gemini AI. Includes init CLI, local pre-filter, result caching, and a Captured Spam admin collection.","main":"dist/index.js","types":"dist/index.d.ts","bin":{"agent-spam-filter":"bin/cli.mjs"},"scripts":{"build":"node ./scripts/build.mjs","dev":"tsc --watch","prepublishOnly":"node ./scripts/build.mjs"},"publishConfig":{"access":"public"},"keywords":["payload","payloadcms","plugin","spam","detection","gemini","ai","form","security"],"author":{"name":"Agent Systems"},"license":"MIT","peerDependencies":{"payload":"^3.0.0"},"dependencies":{"fs-extra":"^11.1.1"},"devDependencies":{"@types/fs-extra":"^11.0.4","@types/node":"^20.0.0","typescript":"^5.0.0"},"gitHead":"9c2d691346c6f249121ee4cf9326af39e00c7655","_id":"@agentmarketing/payload-spam-filtering@2.0.3","_nodeVersion":"22.11.0","_npmVersion":"11.11.0","dist":{"integrity":"sha512-JVxRE9wulqU9tiD+iiG4fJEQNw5M3UQZQNMxsQS8xL/PAUuUDlKi10Hju5FjF+/3xrx43+P+bBx3QHkmJNJ1IA==","shasum":"afbb122df1fb0d224b41ec5bfd13848443671cb4","tarball":"https://registry.npmjs.org/@agentmarketing/payload-spam-filtering/-/payload-spam-filtering-2.0.3.tgz","fileCount":26,"unpackedSize":75295,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIH+Yl1dLs9NshfGkEdeMpy8JAdlZb5RiMsp/zlrd0rv7AiEA/myPnJ8Qs2/PgvsDStuzmSbZVxM3HQjK9GQkoJCn5JU="}]},"_npmUser":{"name":"agent-marketing","email":"digital@helloagent.co.uk"},"directories":{},"maintainers":[{"name":"agent-marketing","email":"digital@helloagent.co.uk"},{"name":"jake-holcroft","email":"jake@agentmarketing.co.uk"},{"name":"shane-farmer","email":"shane@helloagent.co.uk"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/payload-spam-filtering_2.0.3_1778059169681_0.828129878080752"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-19T09:40:51.540Z","modified":"2026-05-06T09:19:30.101Z","1.0.0":"2026-02-19T09:40:51.877Z","2.0.0":"2026-04-30T09:35:40.717Z","2.0.1":"2026-05-06T09:08:44.135Z","2.0.2":"2026-05-06T09:11:59.293Z","2.0.3":"2026-05-06T09:19:29.917Z"},"author":{"name":"Agent Systems"},"license":"MIT","keywords":["payload","payloadcms","plugin","spam","detection","gemini","ai","form","security"],"description":"Payload CMS plugin for spam detection using Google Gemini AI. Includes init CLI, local pre-filter, result caching, and a Captured Spam admin collection.","maintainers":[{"name":"agent-marketing","email":"digital@helloagent.co.uk"},{"name":"jake-holcroft","email":"jake@agentmarketing.co.uk"},{"name":"shane-farmer","email":"shane@helloagent.co.uk"}],"readme":"# Payload Spam Filtering\r\n\r\nA spam detection plugin for **Payload 3** that hooks into both `site-form-submissions` (Site Forms) and `form-submissions` (Payload form-builder). Catches obvious spam locally before any API call, caches Gemini results for repeated content, saves blocked submissions to a **Captured Spam** admin collection, and returns a friendly message to the end user.\r\n\r\n**By Shane Farmer / [Agent Marketing](https://agentmarketing.co.uk/)**  \r\nQuestions or issues? Email [digital@agentmarketing.co.uk](mailto:digital@agentmarketing.co.uk).\r\n\r\n## Install\r\n\r\n```bash\r\npnpm add @agentmarketing/payload-spam-filtering\r\n# or: npm install @agentmarketing/payload-spam-filtering\r\n```\r\n\r\n## Quick setup\r\n\r\n### 1. Set environment variables\r\n\r\nAdd to your `.env`:\r\n\r\n```env\r\nGEMINI_API_KEY=your_gemini_api_key_here\r\nGEMINI_MODEL=gemini-2.5-flash\r\nSPAM_DETECTION_BUSINESS_CONTEXT=Describe your business here\r\nSPAM_DETECTION_STRICTNESS=2\r\n```\r\n\r\n### 2. Run init\r\n\r\nFrom your Payload project root:\r\n\r\n```bash\r\npnpm exec agent-spam-filter init\r\n# or: npx agent-spam-filter init\r\n```\r\n\r\nThis copies `spamFilterPlugin.ts` into `src/plugins/` and patches `src/plugins/index.ts` to import and register it. Restart your dev server and you're done.\r\n\r\nRe-running is safe and idempotent (skips files that already exist). Use `--force` to overwrite — a `.bak` copy is kept.\r\n\r\n## What `init` installs\r\n\r\n### `src/plugins/spamFilterPlugin.ts`\r\n\r\nA Payload `Plugin` function that:\r\n\r\n- Adds a `capture-spam` collection to your Payload config (visible in admin under **Spam Detection**)\r\n- Registers a `beforeChange` hook on both `site-form-submissions` and `form-submissions` via `onInit`, so plugin order in your config does not matter\r\n\r\nThe hook runs checks in this order on every complete submission:\r\n\r\n| Step | Cost | What happens |\r\n| --- | --- | --- |\r\n| 1. Disabled / no API key | — | Fail open, submission passes |\r\n| 2. Content hash cache hit | Zero | Return stored result, no Gemini call |\r\n| 3. Local pre-filter match | Zero | Block immediately, save to Captured Spam |\r\n| 4. Gemini AI | API call | Classify, cache result, save to Captured Spam if spam |\r\n\r\nWhen spam is detected the user sees a friendly 400 message instead of a silent failure.\r\n\r\n### `capture-spam` collection\r\n\r\nSaved to the Payload admin under the **Spam Detection** group. Each entry records:\r\n\r\n| Field | Description |\r\n| --- | --- |\r\n| Form type | `site-form-submissions` or `form-submissions` |\r\n| Form ID | The ID of the form that was submitted |\r\n| Detected by | `pre-filter` or `gemini` |\r\n| Confidence | 0.0 to 1.0 score |\r\n| Reasoning | Gemini explanation or pre-filter rule name |\r\n| Detected at | Timestamp |\r\n| Submission data | Full field/value array as JSON |\r\n\r\n## How it hooks into Payload\r\n\r\nThe plugin uses `onInit` to attach hooks after all plugins have loaded, so it works regardless of plugin registration order.\r\n\r\n- `site-form-submissions` — gated on `data.status === 'complete'` to skip drafts and partial multi-step sequences\r\n- `form-submissions` — gated on `operation === 'create'`\r\n\r\n## Local pre-filter\r\n\r\nCatches unambiguous spam without any Gemini API call:\r\n\r\n| Pattern | Examples |\r\n| --- | --- |\r\n| Nigerian prince / inheritance scams | \"nigerian bank\", \"inheritance fund\" |\r\n| Lottery / prize scams | \"you have won\", \"claim your prize\" |\r\n| Medical spam | \"viagra\", \"cialis\" |\r\n| Generic scam greetings | \"dear sir\", \"dear friend\", \"dear beneficiary\" |\r\n| Work-from-home scams | \"earn\" + \"work from home\" |\r\n| Guaranteed profit / crypto | \"guaranteed returns\", \"crypto guaranteed profit\" |\r\n| Excessive URLs | 4+ links in a single message |\r\n| Excessive all-caps | Over 50% uppercase alpha chars, message over 40 chars |\r\n\r\nPre-filter results are saved to Captured Spam with `source: pre-filter`.\r\n\r\n## Content hash cache\r\n\r\nResults are cached by SHA-256 hash of the submission content. Identical submissions return the cached decision instantly with no API call.\r\n\r\n- TTL: `SPAM_DETECTION_CACHE_TTL_HOURS` (default 24 hours)\r\n- Max entries: 500 (oldest evicted)\r\n- Saved to: `{SPAM_DETECTION_STORAGE_PATH}/spam-detection-cache.json`\r\n\r\n## Strictness levels\r\n\r\nSet via `SPAM_DETECTION_STRICTNESS` env var:\r\n\r\n| Level | Value | Behaviour |\r\n| --- | --- | --- |\r\n| Low | `1` | Only obvious spam — scams, lottery, medical. Very conservative. |\r\n| Medium | `2` | Balanced — also blocks promotional language and generic greetings. Default. |\r\n| High | `3` | Strict — only passes content that is clearly a specific business inquiry. Requires `SPAM_DETECTION_BUSINESS_CONTEXT`. |\r\n\r\n## Configurable Gemini model\r\n\r\nThe model is read from `GEMINI_MODEL` (default `gemini-2.5-flash`). Change it in `.env` without a code deploy.\r\n\r\n## Upgrading from 1.x\r\n\r\n1. Update the package: `npm update @agentmarketing/payload-spam-filtering`\r\n2. Add to `.env`: `GEMINI_MODEL=gemini-2.5-flash`\r\n3. Re-run init to get the `capture-spam` collection and updated hook behaviour:\r\n\r\n```bash\r\npnpm exec agent-spam-filter init --force\r\n```\r\n\r\nA `.bak` copy of your existing `spamFilterPlugin.ts` is kept before overwriting.\r\n\r\n## CLI reference\r\n\r\n```\r\nagent-spam-filter init [--force] [--skip-plugins] [--package name]\r\n\r\n  --force          Overwrite existing scaffold file (keeps a .bak copy).\r\n  --skip-plugins   Do not patch src/plugins/index.ts.\r\n  --package name   Override the import path used in the patch.\r\n```\r\n\r\n## Using `checkForSpam` directly\r\n\r\nThe core function is still available for custom integrations:\r\n\r\n```typescript\r\nimport { checkForSpam } from '@agentmarketing/payload-spam-filtering'\r\n\r\nconst result = await checkForSpam(\r\n  [{ field: 'message', value: 'Hello, I need a quote.' }],\r\n  { strictness: 2 }\r\n)\r\n\r\nif (result.isSpam) {\r\n  // result.confidence  -- 0.0 to 1.0\r\n  // result.reasoning   -- explanation\r\n}\r\n```\r\n\r\nAll env vars are read automatically. Pass `options` to override per-call.\r\n\r\n## Environment variable reference\r\n\r\n| Variable | Required | Default | Description |\r\n| --- | --- | --- | --- |\r\n| `GEMINI_API_KEY` | Yes | — | Google AI Studio API key |\r\n| `GEMINI_MODEL` | No | `gemini-2.5-flash` | Gemini model slug |\r\n| `SPAM_DETECTION_BUSINESS_CONTEXT` | No | `General business inquiries` | Used at strictness 3 |\r\n| `SPAM_DETECTION_STRICTNESS` | No | `2` | `1`, `2`, or `3` |\r\n| `SPAM_DETECTION_ENABLED` | No | `true` | Set to `false` to disable |\r\n| `SPAM_DETECTION_CACHE_TTL_HOURS` | No | `24` | Hours to keep cached results |\r\n| `SPAM_DETECTION_STORAGE_PATH` | No | `./public/spam-detection` | Folder for JSON result files |\r\n\r\n## License\r\n\r\nMIT.\r\n\r\n## Contact\r\n\r\n**[Agent Marketing](https://agentmarketing.co.uk/)** · [digital@agentmarketing.co.uk](mailto:digital@agentmarketing.co.uk)\r\n","readmeFilename":"README.md"}