From 5d987e72fe2d6c6c2bb048b383e7f2965762eb3e Mon Sep 17 00:00:00 2001
From: patrick-motsch
Date: Sun, 15 Feb 2026 01:26:27 +0100
Subject: [PATCH] fix(teamsbot): Fix Invalid Date + pass language to browser
bot
- Add getIsoTimestamp() to timeUtils for JS-compatible ISO 8601 strings
- Replace getUtcTimestamp() (epoch float) with getIsoTimestamp() for all
teamsbot session/transcript/response timestamp fields (startedAt, endedAt,
creationDate, lastModified, SSE event timestamps)
- Pass config.language to browser bot in join request for captions spoken language
Co-authored-by: Cursor
---
.../features/teamsbot/browserBotConnector.py | 3 +++
.../teamsbot/interfaceFeatureTeamsbot.py | 12 ++++++------
modules/features/teamsbot/service.py | 19 ++++++++++---------
modules/shared/timeUtils.py | 13 +++++++++++++
4 files changed, 32 insertions(+), 15 deletions(-)
diff --git a/modules/features/teamsbot/browserBotConnector.py b/modules/features/teamsbot/browserBotConnector.py
index b0fa5310..1b594a25 100644
--- a/modules/features/teamsbot/browserBotConnector.py
+++ b/modules/features/teamsbot/browserBotConnector.py
@@ -35,6 +35,7 @@ class BrowserBotConnector:
botName: str,
instanceId: str,
gatewayWsUrl: str,
+ language: str = "de-DE",
) -> Dict[str, Any]:
"""
Send join command to the Browser Bot service.
@@ -49,6 +50,7 @@ class BrowserBotConnector:
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")
Returns:
Dict with 'success' bool and optional 'error' string.
@@ -66,6 +68,7 @@ class BrowserBotConnector:
"botName": botName,
"instanceId": instanceId,
"gatewayWsUrl": gatewayWsUrl, # Full WebSocket URL for bot to connect back
+ "language": language, # Spoken language for Teams captions
}
try:
diff --git a/modules/features/teamsbot/interfaceFeatureTeamsbot.py b/modules/features/teamsbot/interfaceFeatureTeamsbot.py
index 1aff28cc..2ce40e18 100644
--- a/modules/features/teamsbot/interfaceFeatureTeamsbot.py
+++ b/modules/features/teamsbot/interfaceFeatureTeamsbot.py
@@ -10,7 +10,7 @@ from typing import Dict, Any, List, Optional
from modules.datamodels.datamodelUam import User
from modules.connectors.connectorDbPostgre import DatabaseConnector
-from modules.shared.timeUtils import getUtcTimestamp
+from modules.shared.timeUtils import getIsoTimestamp
from modules.shared.configuration import APP_CONFIG
from .datamodelTeamsbot import (
@@ -98,13 +98,13 @@ class TeamsbotObjects:
def createSession(self, sessionData: Dict[str, Any]) -> Dict[str, Any]:
"""Create a new session."""
- sessionData["creationDate"] = getUtcTimestamp()
- sessionData["lastModified"] = getUtcTimestamp()
+ sessionData["creationDate"] = getIsoTimestamp()
+ sessionData["lastModified"] = getIsoTimestamp()
return self.db.recordCreate(TeamsbotSession, sessionData)
def updateSession(self, sessionId: str, updates: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Update session fields."""
- updates["lastModified"] = getUtcTimestamp()
+ updates["lastModified"] = getIsoTimestamp()
return self.db.recordModify(TeamsbotSession, sessionId, updates)
def deleteSession(self, sessionId: str) -> bool:
@@ -143,7 +143,7 @@ class TeamsbotObjects:
def createTranscript(self, transcriptData: Dict[str, Any]) -> Dict[str, Any]:
"""Create a new transcript segment."""
- transcriptData["creationDate"] = getUtcTimestamp()
+ transcriptData["creationDate"] = getIsoTimestamp()
return self.db.recordCreate(TeamsbotTranscript, transcriptData)
def _deleteTranscriptsBySession(self, sessionId: str) -> int:
@@ -170,7 +170,7 @@ class TeamsbotObjects:
def createBotResponse(self, responseData: Dict[str, Any]) -> Dict[str, Any]:
"""Create a new bot response record."""
- responseData["creationDate"] = getUtcTimestamp()
+ responseData["creationDate"] = getIsoTimestamp()
return self.db.recordCreate(TeamsbotBotResponse, responseData)
def _deleteResponsesBySession(self, sessionId: str) -> int:
diff --git a/modules/features/teamsbot/service.py b/modules/features/teamsbot/service.py
index 50a938e0..12504847 100644
--- a/modules/features/teamsbot/service.py
+++ b/modules/features/teamsbot/service.py
@@ -16,7 +16,7 @@ from fastapi import WebSocket
from modules.datamodels.datamodelUam import User
from modules.datamodels.datamodelAi import AiCallRequest, AiCallOptions, OperationTypeEnum, PriorityEnum
-from modules.shared.timeUtils import getUtcTimestamp
+from modules.shared.timeUtils import getUtcTimestamp, getIsoTimestamp
from .datamodelTeamsbot import (
TeamsbotSessionStatus,
@@ -57,7 +57,7 @@ async def _emitSessionEvent(sessionId: str, eventType: str, data: Any):
"""Emit an event to the session's SSE stream."""
eventQueue = _sessionEvents.get(sessionId)
if eventQueue:
- await eventQueue.put({"type": eventType, "data": data, "timestamp": getUtcTimestamp()})
+ await eventQueue.put({"type": eventType, "data": data, "timestamp": getIsoTimestamp()})
class TeamsbotService:
@@ -126,6 +126,7 @@ class TeamsbotService:
botName=session.get("botName", self.config.botName),
instanceId=self.instanceId,
gatewayWsUrl=fullGatewayWsUrl,
+ language=self.config.language,
)
if result.get("success"):
@@ -164,7 +165,7 @@ class TeamsbotService:
interface.updateSession(sessionId, {
"status": TeamsbotSessionStatus.ENDED.value,
- "endedAt": getUtcTimestamp(),
+ "endedAt": getIsoTimestamp(),
})
await _emitSessionEvent(sessionId, "statusChange", {"status": "ended"})
@@ -178,7 +179,7 @@ class TeamsbotService:
interface.updateSession(sessionId, {
"status": TeamsbotSessionStatus.ERROR.value,
"errorMessage": str(e),
- "endedAt": getUtcTimestamp(),
+ "endedAt": getIsoTimestamp(),
})
# Cleanup event queue
@@ -269,9 +270,9 @@ class TeamsbotService:
if errorMessage:
updates["errorMessage"] = errorMessage
if dbStatus == TeamsbotSessionStatus.ACTIVE.value:
- updates["startedAt"] = getUtcTimestamp()
+ updates["startedAt"] = getIsoTimestamp()
elif dbStatus in [TeamsbotSessionStatus.ENDED.value, TeamsbotSessionStatus.ERROR.value]:
- updates["endedAt"] = getUtcTimestamp()
+ updates["endedAt"] = getIsoTimestamp()
interface.updateSession(sessionId, updates)
await _emitSessionEvent(sessionId, "statusChange", {"status": status, "errorMessage": errorMessage})
@@ -301,7 +302,7 @@ class TeamsbotService:
sessionId=sessionId,
speaker=speaker,
text=text,
- timestamp=str(getUtcTimestamp()),
+ timestamp=getIsoTimestamp(),
confidence=1.0, # Captions don't have confidence scores
language=self.config.language,
isFinal=isFinal,
@@ -326,7 +327,7 @@ class TeamsbotService:
"speaker": speaker,
"text": text,
"confidence": 1.0,
- "timestamp": getUtcTimestamp(),
+ "timestamp": getIsoTimestamp(),
})
# Update session transcript count
@@ -499,7 +500,7 @@ class TeamsbotService:
modelName=response.modelName,
processingTime=response.processingTime,
priceCHF=response.priceCHF,
- timestamp=str(getUtcTimestamp()),
+ timestamp=getIsoTimestamp(),
).model_dump()
createdResponse = interface.createBotResponse(botResponseData)
diff --git a/modules/shared/timeUtils.py b/modules/shared/timeUtils.py
index 0f54fb8a..4d766579 100644
--- a/modules/shared/timeUtils.py
+++ b/modules/shared/timeUtils.py
@@ -31,6 +31,19 @@ def getUtcTimestamp() -> float:
"""
return time.time()
+def getIsoTimestamp() -> str:
+ """
+ Get current UTC timestamp as ISO 8601 string.
+
+ Use this for fields declared as 'ISO timestamp' strings (e.g. Pydantic str fields
+ that will be parsed by JavaScript's new Date()). JavaScript cannot parse epoch
+ floats from getUtcTimestamp() as dates.
+
+ Returns:
+ str: Current UTC time in ISO 8601 format (e.g. "2026-02-15T00:08:32.070000+00:00")
+ """
+ return datetime.now(timezone.utc).isoformat()
+
def createExpirationTimestamp(expiresInSeconds: int) -> float:
"""
Create a new expiration timestamp from seconds until expiration.