fix: verify camera is ON after joining meeting using in-meeting video-button
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6e712858dc
commit
aef99057b2
1 changed files with 83 additions and 0 deletions
|
|
@ -156,6 +156,9 @@ export class BotOrchestrator {
|
||||||
// Dismiss any post-join permission modals (e.g. "Manage windows on all displays")
|
// Dismiss any post-join permission modals (e.g. "Manage windows on all displays")
|
||||||
await this._joinProcedure!.dismissBrowserPermissionModals();
|
await this._joinProcedure!.dismissBrowserPermissionModals();
|
||||||
|
|
||||||
|
// Verify camera is on in the meeting
|
||||||
|
await this._ensureCameraOnInMeeting();
|
||||||
|
|
||||||
// Initialize audio playback
|
// Initialize audio playback
|
||||||
await this._audioProcedure!.initialize();
|
await this._audioProcedure!.initialize();
|
||||||
|
|
||||||
|
|
@ -311,6 +314,9 @@ export class BotOrchestrator {
|
||||||
// Start keepalive to prevent idle disconnect
|
// Start keepalive to prevent idle disconnect
|
||||||
this._startKeepAlive();
|
this._startKeepAlive();
|
||||||
|
|
||||||
|
// Verify camera is on in the meeting
|
||||||
|
await this._ensureCameraOnInMeeting();
|
||||||
|
|
||||||
// Initialize audio playback
|
// Initialize audio playback
|
||||||
await this._audioProcedure!.initialize();
|
await this._audioProcedure!.initialize();
|
||||||
|
|
||||||
|
|
@ -384,6 +390,83 @@ export class BotOrchestrator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify camera is on after joining the meeting, and turn it on if not.
|
||||||
|
*
|
||||||
|
* In-meeting camera button (from Teams DOM):
|
||||||
|
* <button id="video-button" data-state="call-video-off" aria-label="Turn camera on">
|
||||||
|
* <button id="video-button" data-state="call-video-on" aria-label="Turn camera off">
|
||||||
|
*/
|
||||||
|
private async _ensureCameraOnInMeeting(): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Wait a moment for meeting controls to render
|
||||||
|
await this._page!.waitForTimeout(2000);
|
||||||
|
|
||||||
|
// Find the in-meeting video button
|
||||||
|
let videoBtn = await this._page!.$('button#video-button');
|
||||||
|
|
||||||
|
if (!videoBtn) {
|
||||||
|
// Fallback selectors
|
||||||
|
const fallbacks = [
|
||||||
|
'button[data-inp="video-button"]',
|
||||||
|
'button[id="video-button"]',
|
||||||
|
'button[aria-label*="camera" i]',
|
||||||
|
'button[aria-label*="Camera" i]',
|
||||||
|
'button[aria-label*="Video" i]',
|
||||||
|
];
|
||||||
|
for (const sel of fallbacks) {
|
||||||
|
videoBtn = await this._page!.$(sel);
|
||||||
|
if (videoBtn) {
|
||||||
|
this._logger.info(`In-meeting video button found via fallback: ${sel}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!videoBtn) {
|
||||||
|
this._logger.warn('In-meeting video button not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read current state
|
||||||
|
const state = await videoBtn.evaluate((el) => ({
|
||||||
|
dataState: el.getAttribute('data-state') || '',
|
||||||
|
ariaLabel: el.getAttribute('aria-label') || '',
|
||||||
|
id: el.id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._logger.info(
|
||||||
|
`In-meeting camera: data-state="${state.dataState}", ` +
|
||||||
|
`aria-label="${state.ariaLabel}", id="${state.id}"`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Camera is off if data-state is "call-video-off"
|
||||||
|
const isOff = state.dataState === 'call-video-off'
|
||||||
|
|| state.ariaLabel.toLowerCase().includes('turn camera on')
|
||||||
|
|| state.ariaLabel.toLowerCase().includes('kamera einschalten');
|
||||||
|
|
||||||
|
if (isOff) {
|
||||||
|
await videoBtn.click();
|
||||||
|
this._logger.info('In-meeting camera was OFF — clicked to turn ON');
|
||||||
|
await this._page!.waitForTimeout(2000);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
const afterState = await videoBtn.evaluate((el) => ({
|
||||||
|
dataState: el.getAttribute('data-state') || '',
|
||||||
|
ariaLabel: el.getAttribute('aria-label') || '',
|
||||||
|
}));
|
||||||
|
this._logger.info(
|
||||||
|
`In-meeting camera after toggle: data-state="${afterState.dataState}", ` +
|
||||||
|
`aria-label="${afterState.ariaLabel}"`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this._logger.info('In-meeting camera already ON');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this._logger.warn(`Could not verify in-meeting camera: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a keepalive timer that periodically moves the mouse and sends
|
* Start a keepalive timer that periodically moves the mouse and sends
|
||||||
* a WebSocket ping. Prevents Teams from detecting the bot as idle
|
* a WebSocket ping. Prevents Teams from detecting the bot as idle
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue