Prevent historical Teams chat replay as fresh transcript input.
Add chat warmup guard and key-based deduplication so old thread messages are not re-emitted with new timestamps after join. Made-with: Cursor
This commit is contained in:
parent
abf38eada3
commit
5caa5a047e
1 changed files with 22 additions and 1 deletions
|
|
@ -20,6 +20,8 @@ export class ChatProcedure {
|
|||
private _onChatMessage: (entry: ChatMessageEntry) => void;
|
||||
private _isSubscribed: boolean = false;
|
||||
private _lastMessageText: string = '';
|
||||
private _chatReadyAtMs: number = 0;
|
||||
private _recentMessageKeyTimestamps: Map<string, number> = new Map();
|
||||
|
||||
constructor(
|
||||
page: Page,
|
||||
|
|
@ -119,6 +121,7 @@ export class ChatProcedure {
|
|||
speaker: string;
|
||||
text: string;
|
||||
timestamp: string;
|
||||
messageKey?: string;
|
||||
}) => {
|
||||
this._handleChatMessage(msg);
|
||||
});
|
||||
|
|
@ -204,6 +207,7 @@ export class ChatProcedure {
|
|||
speaker: author,
|
||||
text,
|
||||
timestamp: new Date().toISOString(),
|
||||
messageKey: `${author}::${text}`,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
|
@ -239,6 +243,7 @@ export class ChatProcedure {
|
|||
speaker: candidateName,
|
||||
text: candidateBody,
|
||||
timestamp: new Date().toISOString(),
|
||||
messageKey: `${candidateName}::${candidateBody}`,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
|
@ -320,16 +325,32 @@ export class ChatProcedure {
|
|||
});
|
||||
|
||||
this._logger.info(`Chat MutationObserver set up (target: ${chatObserverTarget})`);
|
||||
// Guard against Teams replaying historical thread entries as fresh mutations.
|
||||
this._chatReadyAtMs = Date.now() + 12000;
|
||||
this._logger.info('Chat monitor warmup active for 12s');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a chat message event from the browser.
|
||||
*/
|
||||
private _handleChatMessage(msg: { speaker: string; text: string; timestamp: string }): void {
|
||||
private _handleChatMessage(msg: { speaker: string; text: string; timestamp: string; messageKey?: string }): void {
|
||||
if (!this._isSubscribed || !msg.text) return;
|
||||
|
||||
const nowMs = Date.now();
|
||||
if (this._chatReadyAtMs > 0 && nowMs < this._chatReadyAtMs) return;
|
||||
|
||||
// Dedup
|
||||
if (msg.text === this._lastMessageText) return;
|
||||
const key = msg.messageKey || `${msg.speaker}::${msg.text}`;
|
||||
const lastSeen = this._recentMessageKeyTimestamps.get(key);
|
||||
if (lastSeen && (nowMs - lastSeen) < 120000) return;
|
||||
this._recentMessageKeyTimestamps.set(key, nowMs);
|
||||
if (this._recentMessageKeyTimestamps.size > 400) {
|
||||
const cutoff = nowMs - 10 * 60 * 1000;
|
||||
for (const [k, ts] of this._recentMessageKeyTimestamps.entries()) {
|
||||
if (ts < cutoff) this._recentMessageKeyTimestamps.delete(k);
|
||||
}
|
||||
}
|
||||
this._lastMessageText = msg.text;
|
||||
|
||||
this._logger.info(`Chat: [${msg.speaker}] ${msg.text}`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue