openvibers/openvibe.chat Commits
A chat line that could not be saved is not broadcast: the sender gets an error and nobody sees a message that history, moderation and replays never have (the insert failure used to be swallowed as non-critical). ws-protocol test
Changes
@@@ -809,7 +809,13 @@ class ChatServer {metadata: chatMsg.vip_badge ? { vip_badge: chatMsg.vip_badge } : undefined,});if (result.lastInsertRowid) chatMsg.id = Number(result.lastInsertRowid);-- } catch { /* non-critical */ }++ } catch (err) {++ // Not saved means not sent: a line everyone saw but history, moderation and replays never++ // have would be a ghost. Tell the sender and stop here.++ console.warn('[Chat] message not saved, not broadcast:', err && err.message);++ this.sendTo(ws, { type: 'error', message: 'Your message could not be sent. Please try again.' });++ return;++ }// Attach reply context to broadcastif (replyTo) chatMsg.reply_to = replyTo;@@@ -108,6 +108,18 @@ t('chat into a stream room: room delivery, global cross-feed, persistence, outboassert.strictEqual(reaction.body.powerchat_chat.externalChatterId, `u${alice.id}`);});++t('a message that could not be saved is not broadcast; the sender is told', async () => {++ const orig = h.db.saveChatMessage;++ h.db.saveChatMessage = () => { throw new Error('disk full'); };++ try {++ bobWs.sendJson({ type: 'chat', message: 'ghost line' }); // bob has not spoken yet (no rate limit)++ const err = await bobWs.next((m) => m.type === 'error' && /could not be sent/.test(m.message));++ assert.ok(err);++ await new Promise((r) => setTimeout(r, 200));++ assert.ok(!aliceWs.all.some((m) => m.type === 'chat' && m.message === 'ghost line'), 'nobody else received it');++ } finally { h.db.saveChatMessage = orig; }++});++t('stream history and channel history return the message like Live did', async () => {const hist = await h.http('GET', `/api/chat/${streamId}/history`);assert.strictEqual(hist.status, 200);
@@ -809,7 +809,13 @@ class ChatServer {
@@ -809,7 +809,13 @@ class ChatServer {
metadata: chatMsg.vip_badge ? { vip_badge: chatMsg.vip_badge } : undefined,
metadata: chatMsg.vip_badge ? { vip_badge: chatMsg.vip_badge } : undefined,
});
});
if (result.lastInsertRowid) chatMsg.id = Number(result.lastInsertRowid);
if (result.lastInsertRowid) chatMsg.id = Number(result.lastInsertRowid);
- } catch { /* non-critical */ }
+ } catch (err) {
+ // Not saved means not sent: a line everyone saw but history, moderation and replays never
+ // have would be a ghost. Tell the sender and stop here.
+ console.warn('[Chat] message not saved, not broadcast:', err && err.message);
+ this.sendTo(ws, { type: 'error', message: 'Your message could not be sent. Please try again.' });
+ return;
+ }
// Attach reply context to broadcast
// Attach reply context to broadcast
if (replyTo) chatMsg.reply_to = replyTo;
if (replyTo) chatMsg.reply_to = replyTo;
@@ -108,6 +108,18 @@ t('chat into a stream room: room delivery, global cross-feed, persistence, outbo
@@ -108,6 +108,18 @@ t('chat into a stream room: room delivery, global cross-feed, persistence, outbo
assert.strictEqual(reaction.body.powerchat_chat.externalChatterId, `u${alice.id}`);
assert.strictEqual(reaction.body.powerchat_chat.externalChatterId, `u${alice.id}`);
});
});
+t('a message that could not be saved is not broadcast; the sender is told', async () => {
+ const orig = h.db.saveChatMessage;
+ h.db.saveChatMessage = () => { throw new Error('disk full'); };
+ try {
+ bobWs.sendJson({ type: 'chat', message: 'ghost line' }); // bob has not spoken yet (no rate limit)
+ const err = await bobWs.next((m) => m.type === 'error' && /could not be sent/.test(m.message));
+ assert.ok(err);
+ await new Promise((r) => setTimeout(r, 200));
+ assert.ok(!aliceWs.all.some((m) => m.type === 'chat' && m.message === 'ghost line'), 'nobody else received it');
+ } finally { h.db.saveChatMessage = orig; }
+});
+
t('stream history and channel history return the message like Live did', async () => {
t('stream history and channel history return the message like Live did', async () => {
const hist = await h.http('GET', `/api/chat/${streamId}/history`);
const hist = await h.http('GET', `/api/chat/${streamId}/history`);
assert.strictEqual(hist.status, 200);
assert.strictEqual(hist.status, 200);