openvibers/openvibe.live Commits
Live's AI viewers are not evidence that a moment landed (item 151, C07) (#579)
Changes
@@@ -472,8 +472,9 @@ function recentChatText(streamId, sinceSec = 120, limit = 40) {const out = await client.readMessages({ stream_id: id, limit });if (!out || !Array.isArray(out.messages)) throw unavailable('recent messages');const floor = Date.now() - Math.max(1, sinceSec) * 1000;++ // People's chat only: Live's own AI viewers (source_platform 'ai') are not evidence that a moment landed.return out.messages-- .filter((m) => m.message && Date.parse(String(m.timestamp).replace(' ', 'T') + 'Z') >= floor)++ .filter((m) => m.message && m.source_platform !== 'ai' && Date.parse(String(m.timestamp).replace(' ', 'T') + 'Z') >= floor).map((m) => String(m.message)).reverse();}, () => []);@@@ -489,8 +490,9 @@ function recentChatters(streamId, sinceSec = 30, limit = 200) {const out = await client.readMessages({ stream_id: id, limit });if (!out || !Array.isArray(out.messages)) throw unavailable('recent messages');const floor = Date.now() - Math.max(1, sinceSec) * 1000;++ // Live's AI viewers (source_platform 'ai') are not people: they never make "a crowd reacted" (item 151, C07).const people = new Set(out.messages-- .filter((m) => m.message && Date.parse(String(m.timestamp).replace(' ', 'T') + 'Z') >= floor)++ .filter((m) => m.message && m.source_platform !== 'ai' && Date.parse(String(m.timestamp).replace(' ', 'T') + 'Z') >= floor).map((m) => (m.user_id != null ? `u${m.user_id}` : m.username ? `n${String(m.username).toLowerCase()}` : null)).filter(Boolean));return people.size;nnew file mode 100644@@@ -0,0 +1,26 @@++'use strict';++// Live's own AI viewers are not evidence that a moment landed (item 151, C07): they never count as "a crowd reacted"++// (recentChatters) and their lines never reach the AI's clip confirmation (recentChatText). Chat's timeline leaves them++// out of the spike counts on its side (Chat #338).++const assert = require('assert');++console.warn = () => {};++++const client = require('../server/chat/chat-client');++const now = () => new Date().toISOString().replace('T', ' ').slice(0, 19);++client.available = () => true;++client.readMessages = async () => ({ messages: [++ { user_id: 1, username: 'ann', message: 'NO WAY', source_platform: null, timestamp: now() },++ { user_id: 2, username: 'bob', message: 'clip it', source_platform: null, timestamp: now() },++ { user_id: null, username: 'Sora (AI)', message: 'what a play!', source_platform: 'ai', timestamp: now() },++ { user_id: null, username: 'Kai (AI)', message: 'insane', source_platform: 'ai', timestamp: now() },++ { user_id: null, username: 'twitchfan', message: 'LUL', source_platform: 'twitch', timestamp: now() },++] });++const chatReads = require('../server/chat/chat-reads');++++(async () => {++ assert.strictEqual(await chatReads.recentChatters(9001, 60), 3, 'ann, bob and the Twitch viewer; never the AI viewers');++ const text = await chatReads.recentChatText(9002, 120);++ assert.ok(!text.includes('what a play!') && !text.includes('insane'), 'the AI viewers\' lines never reach the confirm step');++ assert.ok(text.includes('NO WAY') && text.includes('LUL'), 'people, relayed ones included, do');++ console.log('ai viewers are not a signal: all checks passed');++})().catch((e) => { console.error(e); process.exit(1); });
@@ -472,8 +472,9 @@ function recentChatText(streamId, sinceSec = 120, limit = 40) {
@@ -472,8 +472,9 @@ function recentChatText(streamId, sinceSec = 120, limit = 40) {
const out = await client.readMessages({ stream_id: id, limit });
const out = await client.readMessages({ stream_id: id, limit });
if (!out || !Array.isArray(out.messages)) throw unavailable('recent messages');
if (!out || !Array.isArray(out.messages)) throw unavailable('recent messages');
const floor = Date.now() - Math.max(1, sinceSec) * 1000;
const floor = Date.now() - Math.max(1, sinceSec) * 1000;
+ // People's chat only: Live's own AI viewers (source_platform 'ai') are not evidence that a moment landed.
return out.messages
return out.messages
- .filter((m) => m.message && Date.parse(String(m.timestamp).replace(' ', 'T') + 'Z') >= floor)
+ .filter((m) => m.message && m.source_platform !== 'ai' && Date.parse(String(m.timestamp).replace(' ', 'T') + 'Z') >= floor)
.map((m) => String(m.message))
.map((m) => String(m.message))
.reverse();
.reverse();
}, () => []);
}, () => []);
@@ -489,8 +490,9 @@ function recentChatters(streamId, sinceSec = 30, limit = 200) {
@@ -489,8 +490,9 @@ function recentChatters(streamId, sinceSec = 30, limit = 200) {
const out = await client.readMessages({ stream_id: id, limit });
const out = await client.readMessages({ stream_id: id, limit });
if (!out || !Array.isArray(out.messages)) throw unavailable('recent messages');
if (!out || !Array.isArray(out.messages)) throw unavailable('recent messages');
const floor = Date.now() - Math.max(1, sinceSec) * 1000;
const floor = Date.now() - Math.max(1, sinceSec) * 1000;
+ // Live's AI viewers (source_platform 'ai') are not people: they never make "a crowd reacted" (item 151, C07).
const people = new Set(out.messages
const people = new Set(out.messages
- .filter((m) => m.message && Date.parse(String(m.timestamp).replace(' ', 'T') + 'Z') >= floor)
+ .filter((m) => m.message && m.source_platform !== 'ai' && Date.parse(String(m.timestamp).replace(' ', 'T') + 'Z') >= floor)
.map((m) => (m.user_id != null ? `u${m.user_id}` : m.username ? `n${String(m.username).toLowerCase()}` : null))
.map((m) => (m.user_id != null ? `u${m.user_id}` : m.username ? `n${String(m.username).toLowerCase()}` : null))
.filter(Boolean));
.filter(Boolean));
return people.size;
return people.size;
new file mode 100644
new file mode 100644
@@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
+'use strict';
+// Live's own AI viewers are not evidence that a moment landed (item 151, C07): they never count as "a crowd reacted"
+// (recentChatters) and their lines never reach the AI's clip confirmation (recentChatText). Chat's timeline leaves them
+// out of the spike counts on its side (Chat #338).
+const assert = require('assert');
+console.warn = () => {};
+
+const client = require('../server/chat/chat-client');
+const now = () => new Date().toISOString().replace('T', ' ').slice(0, 19);
+client.available = () => true;
+client.readMessages = async () => ({ messages: [
+ { user_id: 1, username: 'ann', message: 'NO WAY', source_platform: null, timestamp: now() },
+ { user_id: 2, username: 'bob', message: 'clip it', source_platform: null, timestamp: now() },
+ { user_id: null, username: 'Sora (AI)', message: 'what a play!', source_platform: 'ai', timestamp: now() },
+ { user_id: null, username: 'Kai (AI)', message: 'insane', source_platform: 'ai', timestamp: now() },
+ { user_id: null, username: 'twitchfan', message: 'LUL', source_platform: 'twitch', timestamp: now() },
+] });
+const chatReads = require('../server/chat/chat-reads');
+
+(async () => {
+ assert.strictEqual(await chatReads.recentChatters(9001, 60), 3, 'ann, bob and the Twitch viewer; never the AI viewers');
+ const text = await chatReads.recentChatText(9002, 120);
+ assert.ok(!text.includes('what a play!') && !text.includes('insane'), 'the AI viewers\' lines never reach the confirm step');
+ assert.ok(text.includes('NO WAY') && text.includes('LUL'), 'people, relayed ones included, do');
+ console.log('ai viewers are not a signal: all checks passed');
+})().catch((e) => { console.error(e); process.exit(1); });