openvibers/openvibe.chat Commits

Arena on stream: !1, !2, !tie reach Live as chat effects (#240)

OP OpenVibers committed 16 days ago · 6bdd521cbd · 4 files, +35 −1

Changes

ddiff --git a/server/chat/chat-server.js b/server/chat/chat-server.js
iindex cb9bc5d..cebdc99 100644
---- a/server/chat/chat-server.js
++++ b/server/chat/chat-server.js
@@@ -1355,6 +1355,17 @@ class ChatServer {
return;
}
++ if (cmd === '!1' || cmd === '!2' || cmd === '!tie') {
++ if (!client.user?.id) { this.sendTo(ws, { type: 'system', message: 'Sign in to vote in Bench.' }); return; }
++ if (!client.streamId) { this.sendTo(ws, { type: 'system', message: 'Join a live stream to vote in Bench.' }); return; }
++ const choice = cmd === '!1' ? 'left' : cmd === '!2' ? 'right' : 'tie';
++ try {
++ const result = await ctx.effects.benchVote(client.streamId, client.user.id, choice);
++ this.sendTo(ws, { type: 'system', message: result.message || 'Bench could not count that vote.' });
++ } catch (err) { this.sendTo(ws, { type: 'system', message: 'Bench could not count that vote.' }); }
++ return;
++ }
++
if (cmd === '!gotti') {
const username = client.user ? client.user.display_name : client.anonId;
const coreUsername = client.user ? client.user.username : null;
ddiff --git a/server/live-context.js b/server/live-context.js
iindex d9d1a45..5155a11 100644
---- a/server/live-context.js
++++ b/server/live-context.js
@@@ -4,7 +4,7 @@
* Every piece of data Chat does not own (accounts and roles, streams and live state, channels,
* channel moderation policy, bans and IP rules, follows, cosmetics and tags, site settings,
* anon numbers) and every side effect chat triggers in Live (coins, AI viewers, arena, media
-- * queue, hardware, pastes, translation, PowerChat, notifications, viewer counts, IP log) goes
++ * queue, hardware, Bench votes, pastes, translation, PowerChat, notifications, viewer counts, IP log) goes
* through this module. Nothing else in Chat talks to Live.
*
* Transport: Live's internal endpoints, service token from OpenVibe.Network for audience
@@@ -944,6 +944,7 @@ const effects = {
hardwareCommand: async (streamerUserId, command, fromUser, { subject = null, userId = null } = {}) => await effect('hardware', {
streamer_user_id: streamerUserId, command, from_user: fromUser, ...(subject ? { from_subject: subject } : {}), ...(userId ? { from_user_id: userId } : {}),
}),
++ benchVote: (streamId, userId, choice) => effect('bench-vote', { stream_id: streamId, viewer_user_id: userId, choice }),
createPaste: (body) => effect('paste', body).then((r) => r.paste),
translate: (text, channelUserId) => effect('translate', { text, channel_user_id: channelUserId }).then((r) => r.translation || null),
notifyDm: (body) => fire('notify/dm', body),
ddiff --git a/test/helpers.js b/test/helpers.js
iindex d2b7ac6..8736644 100644
---- a/test/helpers.js
++++ b/test/helpers.js
@@@ -262,6 +262,7 @@ async function boot({ env = {} } = {}) {
return send(400, { error: 'unknown op' });
// Live forwards a robot command to the channel's OpenVibe.Bot robot as the viewer: the stub says who it was asked as.
case 'hardware': return send(200, { ok: false, reason: 'no_robot', message: `No robot here (asked as ${body.from_subject || 'nobody'}, user ${body.from_user_id || '-'}).` });
++ case 'bench-vote': return send(200, { ok: true, message: 'Your Bench vote is counted.' });
case 'ensure-channel': {
let ch = [...live.channels.values()].find((c) => c.user_id === body.user_id);
if (!ch) { ch = { id: 100 + live.channels.size, user_id: body.user_id, title: 'New Channel' }; live.channels.set(ch.id, ch); }
ddiff --git a/test/ws-protocol.test.js b/test/ws-protocol.test.js
iindex 7de37c8..009e0fd 100644
---- a/test/ws-protocol.test.js
++++ b/test/ws-protocol.test.js
@@@ -334,6 +334,27 @@ t('media commands go to Live’s queue with the same replies', async () => {
await aliceWs.next((x) => x.type === 'system' && x.message === `No robot here (asked as usr_01J9AAAAAAAAAAAAAAAAAAAAAA, user ${alice.id}).`);
});
++t('Bench votes reach Live once with signed-in viewer and stream ids', async () => {
++ await h.sleep(1100);
++ // Bob is still timed out by the moderation test. Use a viewer with no moderation history.
++ const viewer = h.addUser('benchvoter');
++ await h.ctx.sync();
++ const viewerWs = await h.ws({ ip: '198.51.100.75', token: viewer.token, stream: streamId });
++ try {
++ viewerWs.sendJson({ type: 'join', streamId, token: viewer.token });
++ await viewerWs.next((m) => m.type === 'auth');
++ const before = h.live.effects.length;
++ aliceWs.sendJson({ type: 'chat', message: '!1' });
++ await aliceWs.next((x) => x.type === 'system' && x.message === 'Your Bench vote is counted.');
++ viewerWs.sendJson({ type: 'chat', message: '!tie' });
++ await viewerWs.next((x) => x.type === 'system' && x.message === 'Your Bench vote is counted.');
++ assert.deepStrictEqual(h.live.effects.slice(before).filter((e) => e.name === 'bench-vote').map((e) => e.body), [
++ { stream_id: streamId, viewer_user_id: alice.id, choice: 'left' },
++ { stream_id: streamId, viewer_user_id: viewer.id, choice: 'tie' },
++ ]);
++ } finally { viewerWs.close(); }
++});
++
t('a !word that is not a channel sound is pressed as the robot button of that name, quietly', async () => {
await h.sleep(1100);
const before = h.live.effects.length;