diff --git a/modules/features/teamsbot/bridgeConnector.py b/modules/features/teamsbot/bridgeConnector.py index 8e43ee1f..f97f4103 100644 --- a/modules/features/teamsbot/bridgeConnector.py +++ b/modules/features/teamsbot/bridgeConnector.py @@ -29,7 +29,6 @@ class BridgeConnector: self, meetingLink: str, botName: str, - backgroundImageUrl: Optional[str], sessionId: str, gatewayCallbackUrl: str, gatewayWsUrl: str, @@ -56,7 +55,6 @@ class BridgeConnector: payload = { "meetingLink": meetingLink, "botName": botName, - "backgroundImageUrl": backgroundImageUrl, "sessionId": sessionId, "gatewayCallbackUrl": gatewayCallbackUrl, "gatewayWsUrl": gatewayWsUrl, diff --git a/modules/features/teamsbot/browserBotConnector.py b/modules/features/teamsbot/browserBotConnector.py index 210f09f1..173d2e5e 100644 --- a/modules/features/teamsbot/browserBotConnector.py +++ b/modules/features/teamsbot/browserBotConnector.py @@ -38,16 +38,16 @@ class BrowserBotConnector: language: str = "de-DE", botAccountEmail: Optional[str] = None, botAccountPassword: Optional[str] = None, - backgroundImageUrl: Optional[str] = None, + transferMode: str = "auto", ) -> Dict[str, Any]: """ Send join command to the Browser Bot service. The bot will: - 1. Launch a headless browser + 1. Launch a browser (headful for auth, headless for anonymous) 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 + 4. Enable captions/audio capture based on transferMode 5. Connect back to Gateway via WebSocket using gatewayWsUrl Args: @@ -55,7 +55,7 @@ class BrowserBotConnector: 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 + transferMode: How to capture meeting content: caption, audio, or auto Returns: Dict with 'success' bool and optional 'error' string. @@ -74,6 +74,7 @@ class BrowserBotConnector: "instanceId": instanceId, "gatewayWsUrl": gatewayWsUrl, "language": language, + "transferMode": transferMode, } # Add authenticated join credentials if configured @@ -82,10 +83,6 @@ class BrowserBotConnector: 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: diff --git a/modules/features/teamsbot/datamodelTeamsbot.py b/modules/features/teamsbot/datamodelTeamsbot.py index 81625b90..737f360e 100644 --- a/modules/features/teamsbot/datamodelTeamsbot.py +++ b/modules/features/teamsbot/datamodelTeamsbot.py @@ -61,6 +61,13 @@ class TeamsbotJoinMode(str, Enum): USER_ACCOUNT = "userAccount" # Join with user's own Microsoft account (OAuth) +class TeamsbotTransferMode(str, Enum): + """How meeting audio/transcript is transferred from bot to gateway.""" + CAPTION = "caption" # Use Teams live captions (text scraping from DOM) + AUDIO = "audio" # Capture meeting audio and stream to gateway for STT + AUTO = "auto" # Automatic: anonymous → audio, authenticated → caption + + # ============================================================================ # Database Models (stored in PostgreSQL) # ============================================================================ @@ -72,7 +79,6 @@ class TeamsbotSession(BaseModel): mandateId: str = Field(description="Mandate ID (FK)") meetingLink: str = Field(description="Teams meeting join link") botName: str = Field(default="AI Assistant", description="Display name of the bot in the meeting") - backgroundImageUrl: Optional[str] = Field(default=None, description="Background image URL for the bot's video feed") status: TeamsbotSessionStatus = Field(default=TeamsbotSessionStatus.PENDING, description="Current session status") startedAt: Optional[str] = Field(default=None, description="ISO timestamp when session started") endedAt: Optional[str] = Field(default=None, description="ISO timestamp when session ended") @@ -147,10 +153,10 @@ class TeamsbotUserSettings(BaseModel): userId: str = Field(description="User ID (FK)") instanceId: str = Field(description="Feature instance ID (FK)") botName: Optional[str] = Field(default=None, description="Bot display name override") - backgroundImageUrl: Optional[str] = Field(default=None, description="Background image URL override") aiSystemPrompt: Optional[str] = Field(default=None, description="AI system prompt override") responseMode: Optional[str] = Field(default=None, description="Response mode override: auto, manual, transcribeOnly") responseChannel: Optional[str] = Field(default=None, description="Response channel override: voice, chat, both") + transferMode: Optional[str] = Field(default=None, description="Transfer mode override: caption, audio, auto") language: Optional[str] = Field(default=None, description="Language override (e.g. de-DE)") voiceId: Optional[str] = Field(default=None, description="TTS voice ID override") triggerIntervalSeconds: Optional[int] = Field(default=None, description="Trigger interval override") @@ -167,18 +173,16 @@ class TeamsbotUserSettings(BaseModel): class TeamsbotConfig(BaseModel): """Configuration for a Teams Bot feature instance (serves as default template for new users).""" botName: str = Field(default="AI Assistant", description="Default bot display name") - backgroundImageUrl: Optional[str] = Field(default=None, description="Default background image URL") aiSystemPrompt: str = Field( default="Du bist ein hilfreicher Meeting-Assistent. Fasse wichtige Punkte zusammen und beantworte Fragen sachlich.", description="Custom system prompt for the AI analysis" ) responseMode: TeamsbotResponseMode = Field(default=TeamsbotResponseMode.AUTO, description="How the bot responds") responseChannel: TeamsbotResponseChannel = Field(default=TeamsbotResponseChannel.VOICE, description="Channel for bot responses: voice, chat, or both") + transferMode: TeamsbotTransferMode = Field(default=TeamsbotTransferMode.AUTO, description="How meeting content is captured: caption (Teams captions), audio (stream to gateway STT), or auto") 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") @@ -199,7 +203,6 @@ class TeamsbotStartSessionRequest(BaseModel): """Request to start a new Teams Bot session.""" meetingLink: str = Field(description="Teams meeting join link (e.g., https://teams.microsoft.com/l/meetup-join/...)") botName: Optional[str] = Field(default=None, description="Override bot name for this session") - backgroundImageUrl: Optional[str] = Field(default=None, description="Override background image for this session") connectionId: Optional[str] = Field(default=None, description="Microsoft connection ID for Graph API access") joinMode: Optional[TeamsbotJoinMode] = Field(default=None, description="How the bot joins: systemBot, anonymous, or userAccount. Defaults to systemBot if credentials configured, else anonymous.") sessionContext: Optional[str] = Field(default=None, description="Custom context/knowledge to provide to the bot for this session (e.g. meeting agenda, documents, background info)") @@ -215,15 +218,13 @@ class TeamsbotSessionResponse(BaseModel): class TeamsbotConfigUpdateRequest(BaseModel): """Request to update teamsbot configuration.""" botName: Optional[str] = None - backgroundImageUrl: Optional[str] = None aiSystemPrompt: Optional[str] = None responseMode: Optional[TeamsbotResponseMode] = None responseChannel: Optional[TeamsbotResponseChannel] = None + transferMode: Optional[TeamsbotTransferMode] = None 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 @@ -249,7 +250,6 @@ class BridgeJoinRequest(BaseModel): """Request sent to .NET Media Bridge to join a meeting.""" meetingLink: str = Field(description="Teams meeting join link") botName: str = Field(description="Bot display name") - backgroundImageUrl: Optional[str] = Field(default=None, description="Background image URL") gatewayCallbackUrl: str = Field(description="Gateway URL for bridge callbacks") gatewayWsUrl: str = Field(description="Gateway WebSocket URL for audio streaming") sessionId: str = Field(description="Session ID for correlation") diff --git a/modules/features/teamsbot/routeFeatureTeamsbot.py b/modules/features/teamsbot/routeFeatureTeamsbot.py index 94683cfc..34dfd7f3 100644 --- a/modules/features/teamsbot/routeFeatureTeamsbot.py +++ b/modules/features/teamsbot/routeFeatureTeamsbot.py @@ -177,7 +177,6 @@ async def startSession( mandateId=mandateId, meetingLink=cleanMeetingUrl, botName=body.botName or config.botName, - backgroundImageUrl=body.backgroundImageUrl or config.backgroundImageUrl, sessionContext=body.sessionContext, status=TeamsbotSessionStatus.PENDING, startedByUserId=str(context.user.id), @@ -449,8 +448,8 @@ def _getEffectiveConfig(instanceId: str, userId: str, interface) -> TeamsbotConf # Merge: user settings override instance defaults (only non-None values) overrides = {} - for field in ["botName", "backgroundImageUrl", "aiSystemPrompt", "responseMode", - "responseChannel", "language", "voiceId", + for field in ["botName", "aiSystemPrompt", "responseMode", + "responseChannel", "transferMode", "language", "voiceId", "triggerIntervalSeconds", "triggerCooldownSeconds", "contextWindowSegments"]: value = userSettings.get(field) if value is not None: diff --git a/modules/features/teamsbot/service.py b/modules/features/teamsbot/service.py index 9c3c537c..0cd17bde 100644 --- a/modules/features/teamsbot/service.py +++ b/modules/features/teamsbot/service.py @@ -130,9 +130,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, + botAccountEmail=self.config.botAccountEmail if hasattr(self.config, 'botAccountEmail') else None, + botAccountPassword=self.config.botAccountPassword if hasattr(self.config, 'botAccountPassword') else None, + transferMode=self.config.transferMode if hasattr(self.config, 'transferMode') else "auto", ) if result.get("success"):