@arnaudw38/nodebb-plugin-spam-be-gone 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [1.0.4] - 2026-02-25
6
+
7
+ ### Fixed
8
+ - Fixed login UX regression where the Turnstile widget was resetting while typing in the username/password fields.
9
+ - Turnstile now resets only after a login submission retry flow (failed login without page reload), not on every keystroke.
10
+
11
+ ## [1.0.3] - 2026-02-25
12
+
13
+ ### Fixed
14
+ - Fixed Cloudflare Turnstile failure on login retry without page reload.
15
+ - Reset Turnstile widget automatically after a failed login attempt (Turnstile tokens are single-use).
16
+ - Improved login flow reliability when users retry authentication on the same page.
17
+
18
+ ### Improved
19
+ - Added safer Turnstile widget state handling on the login form.
20
+ - Added callbacks handling for expired/timeout token states.
21
+ - Better UX during repeated login attempts without manual refresh.
22
+ - Removed deprecated `stopforumspam` npm dependency and replaced it with direct StopForumSpam API requests using native `fetch` (eliminates `q` / `node-domexception` install warnings).
23
+
24
+ ## [1.0.2] - 2026-02-25
25
+
26
+ ### UI
27
+ - Replaced visible 'Turnstile' label with a more user-friendly label:
28
+ - French: `Vérification de sécurité`
29
+
30
+ ## [1.0.1] - 2026-02-25
31
+
32
+ ### Changed
33
+ - Refactored plugin for NodeBB 4.x compatibility.
34
+ - Removed legacy reCAPTCHA and hCaptcha integrations.
35
+ - Added Cloudflare Turnstile support (register + optional login protection).
36
+ - Simplified package/tooling for a minimal runtime-focused plugin setup.
37
+
38
+ ### Docs
39
+ - Rewrote README in English (Turnstile-only, no images).
40
+ - Added npm-ready/publish-ready package metadata and documentation.
package/library.js CHANGED
@@ -4,7 +4,6 @@ const util = require('util');
4
4
  const https = require('https');
5
5
  const querystring = require('querystring');
6
6
  const Honeypot = require('project-honeypot');
7
- const stopforumspam = require('stopforumspam');
8
7
 
9
8
  const winston = require.main.require('winston');
10
9
  const nconf = require.main.require('nconf');
@@ -100,9 +99,6 @@ Plugin.load = async function (params) {
100
99
  if (!settings.akismetMinReputationHam) {
101
100
  settings.akismetMinReputationHam = 10;
102
101
  }
103
- if (settings.stopforumspamApiKey) {
104
- stopforumspam.Key(settings.stopforumspamApiKey);
105
- }
106
102
 
107
103
  pluginSettings = settings;
108
104
 
@@ -137,7 +133,7 @@ Plugin.report = async function (req, res, next) {
137
133
  if (isAdmin) {
138
134
  return res.status(403).send({ message: '[[spam-be-gone:cant-report-admin]]' });
139
135
  }
140
- await stopforumspam.submit({ ip: ips[0], email: fields.email, username: fields.username }, `Manual submission from user: ${req.uid} to user: ${fields.uid} via ${pluginData.id}`);
136
+ await stopForumSpamSubmit({ ip: ips[0], email: fields.email, username: fields.username }, `Manual submission from user: ${req.uid} to user: ${fields.uid} via ${pluginData.id}`);
141
137
  res.status(200).json({ message: '[[spam-be-gone:user-reported]]' });
142
138
  } catch (err) {
143
139
  winston.error(`[plugins/${pluginData.nbbId}][report-error] ${err.message}`);
@@ -152,7 +148,7 @@ Plugin.reportFromQueue = async (req, res) => {
152
148
  }
153
149
  const submitData = { ip: data.ip, email: data.email, username: data.username };
154
150
  try {
155
- await stopforumspam.submit(submitData, `Manual submission from user: ${req.uid} to user: ${data.username} via ${pluginData.id}`);
151
+ await stopForumSpamSubmit(submitData, `Manual submission from user: ${req.uid} to user: ${data.username} via ${pluginData.id}`);
156
152
  res.status(200).json({ message: '[[spam-be-gone:user-reported]]' });
157
153
  } catch (err) {
158
154
  winston.error(`[plugins/${pluginData.nbbId}][report-error] ${err.message}\n${JSON.stringify(submitData, null, 4)}`);
@@ -283,7 +279,7 @@ Plugin.getRegistrationQueue = async function (data) {
283
279
  async function augmentWitSpamData(user) {
284
280
  try {
285
281
  user.ip = user.ip.replace('::ffff:', '');
286
- let body = await stopforumspam.isSpammer({ ip: user.ip, email: user.email, username: user.username, f: 'json' });
282
+ let body = await stopForumSpamLookup({ ip: user.ip, email: user.email, username: user.username });
287
283
  if (!body) {
288
284
  body = { success: 1, username: { frequency: 0, appears: 0 }, email: { frequency: 0, appears: 0 }, ip: { frequency: 0, appears: 0, asn: null } };
289
285
  }
@@ -396,6 +392,71 @@ Plugin._turnstileCheck = async function (req) {
396
392
  });
397
393
  };
398
394
 
395
+
396
+ async function stopForumSpamLookup({ ip, email, username }) {
397
+ const params = new URLSearchParams();
398
+ params.set('f', 'json');
399
+ if (ip) params.set('ip', ip);
400
+ if (email) params.set('email', email);
401
+ if (username) params.set('username', username);
402
+
403
+ const res = await fetch(`https://api.stopforumspam.org/api?${params.toString()}`, {
404
+ headers: {
405
+ accept: 'application/json',
406
+ 'user-agent': pluginData.id,
407
+ },
408
+ });
409
+
410
+ if (!res.ok) {
411
+ throw new Error(`StopForumSpam lookup failed (${res.status})`);
412
+ }
413
+
414
+ return await res.json();
415
+ }
416
+
417
+ async function stopForumSpamSubmit({ ip, email, username }, evidence) {
418
+ if (!pluginSettings.stopforumspamApiKey) {
419
+ throw new Error('[[spam-be-gone:sfs-api-key-not-set]]');
420
+ }
421
+
422
+ const body = new URLSearchParams();
423
+ body.set('api_key', pluginSettings.stopforumspamApiKey);
424
+ body.set('api', 'json');
425
+ if (ip) body.set('ip_addr', ip);
426
+ if (email) body.set('email', email);
427
+ if (username) body.set('username', username);
428
+ if (evidence) body.set('evidence', evidence);
429
+
430
+ const res = await fetch('https://www.stopforumspam.com/add.php', {
431
+ method: 'POST',
432
+ headers: {
433
+ 'content-type': 'application/x-www-form-urlencoded',
434
+ accept: 'application/json, text/plain;q=0.9, */*;q=0.8',
435
+ 'user-agent': pluginData.id,
436
+ },
437
+ body: body.toString(),
438
+ });
439
+
440
+ const text = await res.text();
441
+ if (!res.ok) {
442
+ throw new Error(`StopForumSpam submit failed (${res.status})`);
443
+ }
444
+
445
+ try {
446
+ const parsed = JSON.parse(text);
447
+ if (parsed.success === 0 || parsed.error) {
448
+ throw new Error(parsed.error || 'StopForumSpam submit failed');
449
+ }
450
+ return parsed;
451
+ } catch (err) {
452
+ // Some SFS responses can be plain text; consider HTTP 200 success as accepted.
453
+ if (/error/i.test(text)) {
454
+ throw err instanceof Error ? err : new Error('StopForumSpam submit failed');
455
+ }
456
+ return { success: 1, raw: text };
457
+ }
458
+ }
459
+
399
460
  Plugin.admin = {
400
461
  menu: function (custom_header, callback) {
401
462
  custom_header.plugins.push({ route: `/plugins/${pluginData.nbbId}`, icon: pluginData.faIcon, name: pluginData.name });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arnaudw38/nodebb-plugin-spam-be-gone",
3
- "version": "1.0.2",
4
- "description": "Anti-spam plugin for NodeBB 4.x using Akismet, StopForumSpam, ProjectHoneyPot, and Cloudflare Turnstile (Turnstile-only fork)",
3
+ "version": "1.0.4",
4
+ "description": "Anti-spam plugin for NodeBB 4.x using Akismet, StopForumSpam API, ProjectHoneyPot, and Cloudflare Turnstile (Turnstile-only fork)",
5
5
  "main": "library.js",
6
6
  "scripts": {},
7
7
  "repository": {
@@ -33,8 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "async": "^3.2.0",
36
- "project-honeypot": "~0.0.0",
37
- "stopforumspam": "^1.3.8"
36
+ "project-honeypot": "~0.0.0"
38
37
  },
39
38
  "nbbpm": {
40
39
  "compatibility": "^4.0.0"
@@ -50,7 +49,8 @@
50
49
  "upgrades/",
51
50
  "plugin.json",
52
51
  "README.md",
53
- "LICENSE"
52
+ "LICENSE",
53
+ "CHANGELOG.md"
54
54
  ],
55
55
  "publishConfig": {
56
56
  "access": "public"
@@ -82,12 +82,7 @@ $(function () {
82
82
  .on('click.spamBeGoneTurnstileLogin', '[component="login/submit"]', function () {
83
83
  scheduleLoginTurnstileReset(targetId);
84
84
  })
85
- .off('input.spamBeGoneTurnstileLogin change.spamBeGoneTurnstileLogin')
86
- .on('input.spamBeGoneTurnstileLogin change.spamBeGoneTurnstileLogin', 'input[name="username"], input[name="password"]', function () {
87
- if (document.getElementById(targetId) && getCurrentTurnstileResponse()) {
88
- resetTurnstileWidget(targetId);
89
- }
90
- });
85
+ .off('input.spamBeGoneTurnstileLogin change.spamBeGoneTurnstileLogin');
91
86
  }
92
87
 
93
88
  function renderTurnstileIfNeeded(isLoginPage) {