openvibers/openvibe.fun Commits

AI text may describe people; only sex acts and porn stay gated (#21)

OP OpenVibers committed 11 days ago · 3c9c114818 · 11 files, +30 −60

* AI text may describe people; only sex acts and porn stay gated

* Fun: drop the last looks bans from quest comments

* openvibe-sdk v0.81.0 (was v0.74.0)

Opened by scripts/pin-bump.js in OpenVibers/OpenVibe.Shared after the v0.81.0 release.

* Merge remote-tracking branch 'origin/main' into refresh-21

# Conflicts:
#	STATUS.json
#	package-lock.json
#	package.json

* Merge remote-tracking branch 'origin/pin-bump/openvibe-sdk-v0.81.0' into refresh-21

Changes

ddiff --git a/README.md b/README.md
iindex 58dc6dc..88343ad 100644
---- a/README.md
++++ b/README.md
@@@ -233,6 +233,6 @@ AGPL-3.0-only. See [LICENSE](LICENSE).
<!-- versions:start -->
- openvibe-contracts: v0.236.0
--- openvibe-sdk: v0.74.0
++- openvibe-sdk: v0.81.0
- openvibe-shared: v4.93.2
<!-- versions:end -->
ddiff --git a/STATUS.json b/STATUS.json
iindex cc72f99..965e399 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -9,11 +9,11 @@
"port": "5140",
"domain": "openvibe.fun",
"contracts": "openvibe-contracts v0.236.0",
-- "sdk": "openvibe-sdk v0.74.0",
++ "sdk": "openvibe-sdk v0.81.0",
"shared": "openvibe-shared v4.93.2",
"packages": [
"openvibe-contracts v0.236.0",
-- "openvibe-sdk v0.74.0",
++ "openvibe-sdk v0.81.0",
"openvibe-shared v4.93.2"
],
"summary": "OpenVibe.Fun is the Fun room. A streamer opens an IRL quest board on their public Live channel, viewers suggest safe challenges and chat votes with !quest vote, the streamer accepts one and marks it live/completed, and the chat or a moderator verifies it; a local badge is paid and OpenCoins are credited only when the wallet is configured. The local guide reads OpenStreetMap (Nominatim/Overpass with a contact User-Agent, one request a second per host, a seven-day cache and a bounded around: query) and Ticketmaster only when a key is configured. Group plans have options, one vote each, a tie refused, and a winner the viewer can add to their own Today with their own token. No route needs a service token; Fun never posts to Chat or Live.",
ddiff --git a/package-lock.json b/package-lock.json
iindex 2589482..a2d45d6 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -16,7 +16,7 @@
"helmet": "^8.1.0",
"iovalkey": "^0.4.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.236.0",
-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0",
++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.81.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.93.2",
"pg": "^8.23.0"
},
@@@ -1166,9 +1166,9 @@
}
},
"node_modules/openvibe-sdk": {
-- "version": "0.74.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0",
-- "integrity": "sha512-DO9KVPnUriY/I4lX6ZEJtXaUvRDbJyqnqJxvlpl+zkNGFILpmuBXVJogNOCfK071Qci1gg7C4Iw1Xrseklcajg==",
++ "version": "0.81.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.81.0",
++ "integrity": "sha512-GWD+qkkKiOyer1SATAaVs8qdWR1Z4WIRvtcYQU0NijzoL9BDRmkaXzLHjwwkRezdkHmdOgtvNIU8GAFaUaS8Ow==",
"license": "MIT",
"bin": {
"openvibe-download": "bin/openvibe-download.js"
ddiff --git a/package.json b/package.json
iindex 0da91da..ad9d7a7 100644
---- a/package.json
++++ b/package.json
@@@ -23,7 +23,7 @@
"helmet": "^8.1.0",
"iovalkey": "^0.4.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.236.0",
-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0",
++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.81.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.93.2",
"pg": "^8.23.0"
},
ddiff --git a/server/quests/ideas.js b/server/quests/ideas.js
iindex 433ad9b..738f5fc 100644
---- a/server/quests/ideas.js
++++ b/server/quests/ideas.js
@@@ -3,7 +3,7 @@
/**
* The safe starter quests. These are a small catalogue in code, not an AI generation: the only "generation" in v1
* is choosing one of these for a streamer (POST /quests/:slug/idea), so no model call and no new AI capability is
-- * involved. Every one passes server/quests/safety.js, and they are written without describing anyone's looks.
++ * involved. Every one passes server/quests/safety.js.
*/
const IDEAS = Object.freeze([
{ title: 'Find the oldest bar on this street', brief: 'Ask a local or look at the date over the door; tell chat what you found.', category: 'near_me', accessible: false },
ddiff --git a/server/quests/safety.js b/server/quests/safety.js
iindex 8015e65..00e4a12 100644
---- a/server/quests/safety.js
++++ b/server/quests/safety.js
@@@ -33,7 +33,7 @@ const RULES = Object.freeze([
{
code: 'adult',
why: 'That challenge is not suitable for a public stream.',
-- re: /\b(naked|nude|strip(?:ping|tease)?|porn(?:ography)?|sex(?:ual)?|escort)\b/i,
++ re: /\b(porn(?:ography)?|sex(?:\s+acts?)?|escort)\b/i,
},
]);
ddiff --git a/server/search.js b/server/search.js
iindex cd993ce..118caeb 100644
---- a/server/search.js
++++ b/server/search.js
@@@ -11,7 +11,6 @@
* author or token is ever part of a document.
*/
const { ids } = require('openvibe-contracts');
--const { cleanSummary } = require('./text/no-looks');
const OWNER = 'fun';
const EVENT_UPSERTED = 'fun.index_document.upserted';
@@@ -26,7 +25,7 @@ function questDocument(q, { baseUrl = 'https://openvibe.fun' } = {}) {
if (!q || q.visibility !== 'public' || !QUEST_STATUSES.has(q.status)) return { owner: OWNER, type: 'quest', id: q.id, revision, deleted: true };
// The curated idea deck is the one place Fun writes public text for a quest; a look-naming summary would
// become the title. A person's own suggestion is never rewritten here.
-- const summary = q.source === 'idea' ? cleanSummary(q.title, q.brief) : q.brief;
++ const summary = q.brief;
const body = [
q.title,
q.brief,
ddiff --git a/server/store.js b/server/store.js
iindex 39e48a7..87574e0 100644
---- a/server/store.js
++++ b/server/store.js
@@@ -6,12 +6,11 @@
*
* Every write that makes a public quest or plan writes its Search document (or tombstone) in the same transaction
* (server/search.js). A suggestion is born unlisted and only the streamer (or staff) can accept it; a plan's owner
-- * (or staff) decides its winner. No function here stores a token, a person's looks or a private message.
++ * (or staff) decides its winner. No function here stores a token or a private message.
*/
const crypto = require('crypto');
const { ids } = require('openvibe-contracts');
const safety = require('./quests/safety');
--const { cleanSummary } = require('./text/no-looks');
const SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
const USERNAME_RE = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,63}$/;
ddiff --git a/server/text/no-looks.js b/server/text/no-looks.js
ddeleted file mode 100644
iindex c18f2f8..0000000
---- a/server/text/no-looks.js
++++ /dev/null
@@@ -1,22 +0,0 @@
--'use strict';
--
--/**
-- * No looks in public AI/curated text (the network rule, openvibe-sdk/no-looks, shared with Live, Community and
-- * MediaHub): a summary we write never describes a person's body, clothing, hair, face or guessed gender. A summary
-- * that names looks at all is not repaired word by word — it is replaced by the item's title. A person's own
-- * suggestion or plan text is not passed through this (that would rewrite what they wrote); the safety filter and
-- * reports handle those.
-- */
--const { noLooks, hasLooks } = require('openvibe-sdk/no-looks');
--
--/** The summary to store for text Fun generated or curated. */
--function cleanSummary(title, summary) {
-- const t = String(title || '').trim() || 'Something to do';
-- const s = String(summary == null ? '' : summary).trim();
-- if (!s) return t;
-- if (hasLooks(s)) return t;
-- const cleaned = String(noLooks(s) || '').trim();
-- return cleaned || t;
--}
--
--module.exports = { cleanSummary, noLooks, hasLooks };
ddiff --git a/test/no-looks-guard.test.js b/test/no-looks-guard.test.js
nnew file mode 100644
iindex 0000000..5587d93
---- /dev/null
++++ b/test/no-looks-guard.test.js
@@@ -0,0 +1,18 @@
++'use strict';
++// Owner, 2026-10-31: AI may describe people; only sex acts and porn stay gated. No server file may call the old
++// looks filter again.
++const assert = require('assert');
++const fs = require('fs');
++const path = require('path');
++
++const root = path.join(__dirname, '..');
++const offenders = [];
++(function walk(dir) {
++ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
++ const p = path.join(dir, e.name);
++ if (e.isDirectory()) walk(p);
++ else if (e.name.endsWith('.js') && /\b(noLooks|noLooksTags|hasLooks)\s*\(|openvibe-sdk\/no-looks/.test(fs.readFileSync(p, 'utf8'))) offenders.push(path.relative(root, p));
++ }
++})(path.join(root, 'server'));
++assert.deepStrictEqual(offenders, [], 'no looks filter in server/');
++console.log('No looks filter: AI text describes people as written.');
ddiff --git a/test/no-looks.test.js b/test/no-looks.test.js
ddeleted file mode 100644
iindex 6a92d62..0000000
---- a/test/no-looks.test.js
++++ /dev/null
@@@ -1,24 +0,0 @@
--'use strict';
--/**
-- * No looks in public AI/curated text: a summary that names looks becomes the item's title; an ordinary summary is
-- * kept. A person's own suggestion is not rewritten by this module — the safety filter and reports handle it.
-- */
--const assert = require('assert');
--const { cleanSummary, hasLooks } = require('../server/text/no-looks');
--const { check, done } = require('./helpers/boot');
--
--(async () => {
-- await check('a summary that names looks becomes the title', async () => {
-- assert.strictEqual(cleanSummary('Find the best bench', 'A shirtless man in a kitchen laughs.'), 'Find the best bench');
-- assert.ok(hasLooks('A woman with long red hair smiles.'));
-- });
--
-- await check('an ordinary summary survives and the empty case becomes the title', async () => {
-- const s = cleanSummary('Find the best bench', 'A quiet bench above the river, ten minutes from the station.');
-- assert.strictEqual(s, 'A quiet bench above the river, ten minutes from the station.');
-- assert.strictEqual(cleanSummary('Find the best bench', ''), 'Find the best bench');
-- assert.strictEqual(cleanSummary(null, null), 'Something to do');
-- });
--
-- done();
--})();