openvibers/openvibe.chat Commits
The core slur list catches every inflection of the n-word family (an -erin' form passed a channel's nudge and the global feed); real words that share its first letters still pass (#199)
Changes
@@@ -42,7 +42,10 @@ const CORE_SLUR_CATEGORIES = [label: 'N-word and variants',patterns: [// Base + plural forms: ****a, ****as, ****az, ****er, ****ers, ****az-- '\\bn+i+g+g+(?:a+[sz]?|e+r+[sz]?)\\b',++ // ...and every inflection of the -er form (-ers, -ering, -erin, -ered, -erish) and -ah/-uh: a line that used one++ // passed both a channel's nudge and the global feed (2026-10-25). Real words that share the first letters++ // (niggle, niggard) are not matched: neither continues with a or er.++ '\\bn+i+g+g+(?:a+[hsz]?|u+h+|e+r+(?:[sz]|i+n+g*[sz]?|e+d+|i+s+h+)?)\\b',// Alternate root family: nick, knick, etc.'\\b[kn]*n+h?i+c?k+e+r+s?\\b',],@@@ -53,4 +53,18 @@ assert.deepStrictEqual(guard.check({ id: 201, message: 'y', user_id: 5, mirroredassert.strictEqual(guard.check({ id: 202, message: 'a whole sentence', user_id: 5, mirrored: true }).ok, true);assert.strictEqual(guard.check({ id: 203, message: '#'.repeat(300), user_id: 5, mirrored: true }).ok, false, 'a mirrored wall');++// Every inflection of the n-word family is a core slur, in any room and in the global feed (2026-10-25: an '-erin'++// form passed both). Spelled out at run time from its parts; real words that share the first letters are not.++{++ const { containsCoreSlur } = require('../server/chat/moderation-utils');++ const stem = ['n', 'i', 'g', 'g'].join('');++ for (const tail of ['er', 'ers', 'a', 'as', 'az', 'ah', 'uh', 'ering', 'erin', 'erings', 'ered', 'erish']) {++ assert.strictEqual(containsCoreSlur(`who is ${stem}${tail} your eggs`), true, `${stem.slice(0, 1)}…${tail}`);++ assert.strictEqual(containsCoreSlur(`who is ${stem}${tail}\u2019 your eggs`), true, `${stem.slice(0, 1)}…${tail} with a curly apostrophe`);++ assert.strictEqual(lineOk(`who is ${stem}${tail} your eggs`, { mirrored: true }), false);++ }++ for (const word of ['niggle', 'niggling', 'niggles', 'niggard', 'niggardly', 'snigger', 'sniggering'])++ assert.strictEqual(containsCoreSlur(`a small ${word} about the stream`), false, word);++}++console.log('global-standard: all checks passed');
@@ -42,7 +42,10 @@ const CORE_SLUR_CATEGORIES = [
@@ -42,7 +42,10 @@ const CORE_SLUR_CATEGORIES = [
label: 'N-word and variants',
label: 'N-word and variants',
patterns: [
patterns: [
// Base + plural forms: ****a, ****as, ****az, ****er, ****ers, ****az
// Base + plural forms: ****a, ****as, ****az, ****er, ****ers, ****az
- '\\bn+i+g+g+(?:a+[sz]?|e+r+[sz]?)\\b',
+ // ...and every inflection of the -er form (-ers, -ering, -erin, -ered, -erish) and -ah/-uh: a line that used one
+ // passed both a channel's nudge and the global feed (2026-10-25). Real words that share the first letters
+ // (niggle, niggard) are not matched: neither continues with a or er.
+ '\\bn+i+g+g+(?:a+[hsz]?|u+h+|e+r+(?:[sz]|i+n+g*[sz]?|e+d+|i+s+h+)?)\\b',
// Alternate root family: nick, knick, etc.
// Alternate root family: nick, knick, etc.
'\\b[kn]*n+h?i+c?k+e+r+s?\\b',
'\\b[kn]*n+h?i+c?k+e+r+s?\\b',
],
],
@@ -53,4 +53,18 @@ assert.deepStrictEqual(guard.check({ id: 201, message: 'y', user_id: 5, mirrored
@@ -53,4 +53,18 @@ assert.deepStrictEqual(guard.check({ id: 201, message: 'y', user_id: 5, mirrored
assert.strictEqual(guard.check({ id: 202, message: 'a whole sentence', user_id: 5, mirrored: true }).ok, true);
assert.strictEqual(guard.check({ id: 202, message: 'a whole sentence', user_id: 5, mirrored: true }).ok, true);
assert.strictEqual(guard.check({ id: 203, message: '#'.repeat(300), user_id: 5, mirrored: true }).ok, false, 'a mirrored wall');
assert.strictEqual(guard.check({ id: 203, message: '#'.repeat(300), user_id: 5, mirrored: true }).ok, false, 'a mirrored wall');
+// Every inflection of the n-word family is a core slur, in any room and in the global feed (2026-10-25: an '-erin'
+// form passed both). Spelled out at run time from its parts; real words that share the first letters are not.
+{
+ const { containsCoreSlur } = require('../server/chat/moderation-utils');
+ const stem = ['n', 'i', 'g', 'g'].join('');
+ for (const tail of ['er', 'ers', 'a', 'as', 'az', 'ah', 'uh', 'ering', 'erin', 'erings', 'ered', 'erish']) {
+ assert.strictEqual(containsCoreSlur(`who is ${stem}${tail} your eggs`), true, `${stem.slice(0, 1)}…${tail}`);
+ assert.strictEqual(containsCoreSlur(`who is ${stem}${tail}\u2019 your eggs`), true, `${stem.slice(0, 1)}…${tail} with a curly apostrophe`);
+ assert.strictEqual(lineOk(`who is ${stem}${tail} your eggs`, { mirrored: true }), false);
+ }
+ for (const word of ['niggle', 'niggling', 'niggles', 'niggard', 'niggardly', 'snigger', 'sniggering'])
+ assert.strictEqual(containsCoreSlur(`a small ${word} about the stream`), false, word);
+}
+
console.log('global-standard: all checks passed');
console.log('global-standard: all checks passed');