feat(teamsbot): dedicated bot account support with authenticated join

- New config fields: botAccountEmail, botAccountPassword for dedicated MSFT account
- BrowserBotConnector passes credentials + backgroundImageUrl to bot service
- Service passes config credentials to connector in joinMeeting
- Enables: full language settings, virtual background, no lobby wait
- Fallback: anonymous join when no bot account configured

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
patrick-motsch 2026-02-15 11:56:04 +01:00
parent 91425809c3
commit ad5c9d10cd
3 changed files with 28 additions and 6 deletions

View file

@ -36,21 +36,26 @@ class BrowserBotConnector:
instanceId: str,
gatewayWsUrl: str,
language: str = "de-DE",
botAccountEmail: Optional[str] = None,
botAccountPassword: Optional[str] = None,
backgroundImageUrl: Optional[str] = None,
) -> Dict[str, Any]:
"""
Send join command to the Browser Bot service.
The bot will:
1. Launch a headless browser
2. Navigate to Teams web app
3. Join the meeting
2. If botAccountEmail/Password provided: authenticate with Microsoft first
3. Navigate to Teams web app and join the meeting
4. Enable captions and start scraping
5. Connect back to Gateway via WebSocket using gatewayWsUrl
Args:
gatewayWsUrl: Full WebSocket URL for the bot to connect back to
(e.g. wss://gateway-int.poweron-center.net/api/teamsbot/{instanceId}/bot/ws/{sessionId})
language: BCP-47 language code for captions spoken language (e.g. "de-DE", "en-US")
language: BCP-47 language code for captions spoken language
botAccountEmail: Microsoft account email for authenticated join (None = anonymous)
botAccountPassword: Microsoft account password
backgroundImageUrl: URL to background image for virtual background
Returns:
Dict with 'success' bool and optional 'error' string.
@ -67,10 +72,20 @@ class BrowserBotConnector:
"meetingUrl": meetingUrl,
"botName": botName,
"instanceId": instanceId,
"gatewayWsUrl": gatewayWsUrl, # Full WebSocket URL for bot to connect back
"language": language, # Spoken language for Teams captions
"gatewayWsUrl": gatewayWsUrl,
"language": language,
}
# Add authenticated join credentials if configured
if botAccountEmail and botAccountPassword:
payload["botAccountEmail"] = botAccountEmail
payload["botAccountPassword"] = botAccountPassword
logger.info(f"Bot will join authenticated as {botAccountEmail}")
# Add background image if configured
if backgroundImageUrl:
payload["backgroundImageUrl"] = backgroundImageUrl
try:
async with aiohttp.ClientSession(timeout=_BOT_TIMEOUT) as session:
async with session.post(f"{self.botUrl}/api/bot", json=payload) as resp:

View file

@ -117,6 +117,8 @@ class TeamsbotConfig(BaseModel):
language: str = Field(default="de-DE", description="Primary language for STT/TTS")
voiceId: Optional[str] = Field(default=None, description="Google TTS voice ID (e.g., de-DE-Standard-A)")
browserBotUrl: Optional[str] = Field(default=None, description="URL of the Browser Bot service. Falls back to TEAMSBOT_BROWSER_BOT_URL env variable if not set per-instance.")
botAccountEmail: Optional[str] = Field(default=None, description="Dedicated Microsoft account email for authenticated bot join. Leave empty for anonymous join.")
botAccountPassword: Optional[str] = Field(default=None, description="Dedicated Microsoft account password. MFA must be disabled for this account.")
triggerIntervalSeconds: int = Field(default=10, ge=3, le=60, description="Seconds between periodic AI analysis triggers")
triggerCooldownSeconds: int = Field(default=3, ge=1, le=30, description="Minimum seconds between AI calls")
contextWindowSegments: int = Field(default=20, ge=5, le=100, description="Number of transcript segments to include in AI context")
@ -157,6 +159,8 @@ class TeamsbotConfigUpdateRequest(BaseModel):
language: Optional[str] = None
voiceId: Optional[str] = None
browserBotUrl: Optional[str] = None
botAccountEmail: Optional[str] = None
botAccountPassword: Optional[str] = None
triggerIntervalSeconds: Optional[int] = None
triggerCooldownSeconds: Optional[int] = None
contextWindowSegments: Optional[int] = None

View file

@ -127,6 +127,9 @@ class TeamsbotService:
instanceId=self.instanceId,
gatewayWsUrl=fullGatewayWsUrl,
language=self.config.language,
botAccountEmail=self.config.botAccountEmail,
botAccountPassword=self.config.botAccountPassword,
backgroundImageUrl=session.get("backgroundImageUrl") or self.config.backgroundImageUrl,
)
if result.get("success"):