fix: auth join via direct URL (skip launcher), sequential audio queue, improve AI prompt (less floskel, stricter response rules)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
efa648d6fe
commit
a483aa6def
3 changed files with 82 additions and 19 deletions
|
|
@ -20,6 +20,8 @@ export class AudioProcedure {
|
|||
private _logger: Logger;
|
||||
private _audioContext: boolean = false;
|
||||
private _initScriptInjected: boolean = false;
|
||||
private _audioQueue: Array<{ audioData: string; format: 'mp3' | 'wav' | 'pcm' }> = [];
|
||||
private _isPlaying: boolean = false;
|
||||
|
||||
constructor(page: Page, logger: Logger) {
|
||||
this._page = page;
|
||||
|
|
@ -113,13 +115,47 @@ export class AudioProcedure {
|
|||
}
|
||||
|
||||
/**
|
||||
* Play audio in the browser.
|
||||
* Audio is piped into the MediaStreamDestination that Teams uses as mic input.
|
||||
* Queue audio for sequential playback.
|
||||
* Audio is never played in parallel -- each clip waits for the previous one to finish.
|
||||
*
|
||||
* @param audioData Base64 encoded audio data
|
||||
* @param format Audio format (mp3, wav, pcm)
|
||||
*/
|
||||
async playAudio(audioData: string, format: 'mp3' | 'wav' | 'pcm'): Promise<void> {
|
||||
// Add to queue
|
||||
this._audioQueue.push({ audioData, format });
|
||||
this._logger.info(`Audio queued (queue size: ${this._audioQueue.length}, playing: ${this._isPlaying})`);
|
||||
|
||||
// If not currently playing, start processing the queue
|
||||
if (!this._isPlaying) {
|
||||
await this._processAudioQueue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the audio queue sequentially.
|
||||
*/
|
||||
private async _processAudioQueue(): Promise<void> {
|
||||
if (this._isPlaying) return;
|
||||
this._isPlaying = true;
|
||||
|
||||
while (this._audioQueue.length > 0) {
|
||||
const item = this._audioQueue.shift()!;
|
||||
try {
|
||||
await this._playAudioInternal(item.audioData, item.format);
|
||||
} catch (error) {
|
||||
this._logger.error('Error playing queued audio:', error);
|
||||
}
|
||||
}
|
||||
|
||||
this._isPlaying = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal: Play audio in the browser (single clip, no queuing).
|
||||
* Audio is piped into the MediaStreamDestination that Teams uses as mic input.
|
||||
*/
|
||||
private async _playAudioInternal(audioData: string, format: 'mp3' | 'wav' | 'pcm'): Promise<void> {
|
||||
if (!this._audioContext) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,24 @@ export class JoinProcedure {
|
|||
await this._handleLauncherDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a launcher dialog is present and handle it.
|
||||
* Used for authenticated joins where we navigate directly to the meeting URL
|
||||
* but Teams may still show the launcher.
|
||||
*/
|
||||
async handleLauncherIfPresent(): Promise<void> {
|
||||
try {
|
||||
const launcherButton = await this._page.$('button[data-tid="joinOnWeb"]');
|
||||
if (launcherButton) {
|
||||
this._logger.info('Launcher dialog found after direct navigation, clicking "Continue on this browser"');
|
||||
await launcherButton.click();
|
||||
await this._page.waitForTimeout(2000);
|
||||
}
|
||||
} catch {
|
||||
// No launcher - that's fine for authenticated joins
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the launcher dialog that asks how to join.
|
||||
* Primary selector: button[data-tid="joinOnWeb"] (confirmed working in Recall.ai).
|
||||
|
|
|
|||
|
|
@ -168,25 +168,34 @@ export class BotOrchestrator {
|
|||
|
||||
this._setState('navigating');
|
||||
|
||||
// Navigate to meeting and handle launcher
|
||||
await this._joinProcedure.startMeetingLauncherFlow(this._meetingUrl);
|
||||
if (authenticate) {
|
||||
// AUTHENTICATED JOIN: Navigate directly to the original meeting URL
|
||||
// within the Teams v2 web app context. The launcher (/dl/launcher/) always
|
||||
// redirects to the "light-meetings" anonymous experience regardless of auth.
|
||||
// Instead, navigate directly to the original meeting URL -- Teams v2 will
|
||||
// recognize the auth session and show the authenticated pre-join screen.
|
||||
this._logger.info(`Authenticated: navigating directly to meeting URL: ${this._meetingUrl}`);
|
||||
await this._page!.goto(this._meetingUrl, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
// After navigation, check if Teams put us on the anonymous page despite auth
|
||||
if (authenticate && this._page) {
|
||||
const currentUrl = this._page.url();
|
||||
if (currentUrl.includes('anon=true') || currentUrl.includes('light-meetings/launch')) {
|
||||
this._logger.warn(`Teams redirected to anonymous mode despite auth. URL: ${currentUrl}`);
|
||||
// Strip anon params and re-navigate
|
||||
const cleanUrl = currentUrl
|
||||
.replace(/[&?]anon=true/gi, '')
|
||||
.replace(/%26anon%3Dtrue/gi, '');
|
||||
this._logger.info(`Re-navigating without anon: ${cleanUrl}`);
|
||||
await this._page.goto(cleanUrl, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 30000,
|
||||
});
|
||||
await this._page.waitForTimeout(3000);
|
||||
// Teams may show the launcher dialog even for direct URLs -- handle it
|
||||
await this._joinProcedure.handleLauncherIfPresent();
|
||||
|
||||
// Wait for the pre-join page to stabilize
|
||||
await this._page!.waitForTimeout(3000);
|
||||
|
||||
// Verify we're on an authenticated page (no "Type your name" input)
|
||||
const pageText = await this._page!.evaluate(() => document.body?.innerText?.substring(0, 500) || '');
|
||||
if (pageText.includes('Enter the name') || pageText.includes('Type your name')) {
|
||||
this._logger.warn('Still on anonymous page after auth navigation - auth session may not have transferred');
|
||||
} else {
|
||||
this._logger.info('On authenticated pre-join page (no name input required)');
|
||||
}
|
||||
} else {
|
||||
// ANONYMOUS JOIN: Use the launcher flow (resolves URL, adds anon params)
|
||||
await this._joinProcedure.startMeetingLauncherFlow(this._meetingUrl);
|
||||
}
|
||||
|
||||
// Set virtual background if configured (must be done on pre-join screen, before "Join now")
|
||||
|
|
|
|||
Loading…
Reference in a new issue