chat: periodischer Scan fuer Teilnehmer-Nachrichten, Chat-Panel-Reopen wenn geschlossen
Made-with: Cursor
This commit is contained in:
parent
cb337ec377
commit
b552ccd547
1 changed files with 72 additions and 0 deletions
|
|
@ -23,6 +23,7 @@ export class ChatProcedure {
|
|||
private _lastMessageText: string = '';
|
||||
private _botJoinedAtMs: number = 0;
|
||||
private _recentMessageKeyTimestamps: Map<string, number> = new Map();
|
||||
private _scanIntervalId: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
constructor(
|
||||
page: Page,
|
||||
|
|
@ -357,6 +358,72 @@ export class ChatProcedure {
|
|||
});
|
||||
|
||||
this._logger.info(`Chat MutationObserver set up (target: ${chatObserverTarget})`);
|
||||
|
||||
this._startPeriodicChatScan();
|
||||
}
|
||||
|
||||
/**
|
||||
* Periodic scan as fallback: participant messages may load async and miss
|
||||
* the MutationObserver (addedNode with empty placeholder, then content update).
|
||||
* Also reopens the chat panel if it was closed (e.g. by user or Teams).
|
||||
*/
|
||||
private _startPeriodicChatScan(): void {
|
||||
if (this._scanIntervalId) return;
|
||||
const intervalMs = 2500;
|
||||
this._scanIntervalId = setInterval(async () => {
|
||||
if (!this._isSubscribed || !this._page) return;
|
||||
try {
|
||||
const chatPanelOpen = await this._page.evaluate(() => {
|
||||
const chatBtn = document.querySelector('button[id="chat-button"], button[data-tid="chat-button"]') as HTMLElement | null;
|
||||
if (chatBtn?.getAttribute('aria-pressed') === 'true') return true;
|
||||
const messageList = document.querySelector('[data-tid="message-pane-list"], [data-tid="chat-pane-list"], [data-tid="chat-pane"]') as HTMLElement | null;
|
||||
return !!(messageList && messageList.offsetHeight > 50);
|
||||
});
|
||||
if (!chatPanelOpen) {
|
||||
this._logger.info('Chat panel closed, reopening');
|
||||
await this._openChatPanel();
|
||||
}
|
||||
|
||||
const knownKeys = Array.from(this._recentMessageKeyTimestamps.keys());
|
||||
const messages = await this._page.evaluate((knownKeysArr: string[]) => {
|
||||
const known = new Set(knownKeysArr);
|
||||
const noisePatterns = [
|
||||
'meeting ended', 'meeting started', 'was invited', 'left the chat',
|
||||
'joined the meeting', 'left the meeting', 'doesn\'t have a teams account',
|
||||
];
|
||||
function isNoise(t: string) {
|
||||
const l = t.toLowerCase();
|
||||
return noisePatterns.some(p => l.includes(p));
|
||||
}
|
||||
const results: Array<{ speaker: string; text: string; timestamp: string; teamsTimestamp?: string; messageKey: string }> = [];
|
||||
const seenThisScan = new Set<string>();
|
||||
const container = document.querySelector('[data-tid="message-pane-list"], [data-tid="chat-pane-list"], [data-tid="chat-pane"]') || document.body;
|
||||
const candidates = container.querySelectorAll('[data-tid="chat-message"], .fui-ChatMessage, [data-tid*="chat-pane-message"]');
|
||||
for (const el of Array.from(candidates) as HTMLElement[]) {
|
||||
const messageEl = el.closest?.('[data-tid="chat-message"], .fui-ChatMessage') || el;
|
||||
let author = 'Unknown';
|
||||
const authorEl = messageEl.querySelector('[data-tid="message-author"], [data-tid="message-author-name"], .fui-ChatMessage__author, [data-tid*="author"]');
|
||||
if (authorEl?.textContent) author = authorEl.textContent.trim();
|
||||
const bodyEl = messageEl.querySelector('[data-tid="message-body"], .fui-ChatMessage__body, [data-tid*="message-body"]');
|
||||
const text = (bodyEl as HTMLElement)?.innerText?.trim() || '';
|
||||
if (!text || text.length < 2 || isNoise(text)) continue;
|
||||
const key = `${author}::${text}`;
|
||||
if (known.has(key) || seenThisScan.has(key)) continue;
|
||||
seenThisScan.add(key);
|
||||
const timeEl = messageEl.querySelector('time[datetime], [data-tid*="timestamp"] time');
|
||||
const ts = timeEl?.getAttribute?.('datetime') || new Date().toISOString();
|
||||
results.push({ speaker: author, text, timestamp: ts, teamsTimestamp: ts, messageKey: key });
|
||||
}
|
||||
return results;
|
||||
}, knownKeys);
|
||||
for (const msg of messages) {
|
||||
this._handleChatMessage(msg);
|
||||
}
|
||||
} catch {
|
||||
// Page may be closed
|
||||
}
|
||||
}, intervalMs);
|
||||
this._logger.info(`Chat periodic scan started (interval: ${intervalMs}ms)`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -477,6 +544,11 @@ export class ChatProcedure {
|
|||
async unsubscribe(): Promise<void> {
|
||||
this._isSubscribed = false;
|
||||
|
||||
if (this._scanIntervalId) {
|
||||
clearInterval(this._scanIntervalId);
|
||||
this._scanIntervalId = null;
|
||||
}
|
||||
|
||||
try {
|
||||
await this._page.evaluate(() => {
|
||||
if ((window as any).__chatObserver) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue