SurfSense/surfsense_backend/app/connectors/teams_history.py
2026-01-09 13:20:30 -08:00

254 lines
8.7 KiB
Python

"""
Microsoft Teams History Module
A module for retrieving conversation history from Microsoft Teams channels.
Allows fetching team lists, channel lists, and message history with date range filtering.
"""
import logging
from datetime import datetime
from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession
from app.connectors.teams_connector import TeamsConnector
from app.schemas.teams_auth_credentials import TeamsAuthCredentialsBase
logger = logging.getLogger(__name__)
class TeamsHistory:
"""Class for retrieving conversation history from Microsoft Teams channels."""
def __init__(
self,
access_token: str | None = None,
session: AsyncSession | None = None,
connector_id: int | None = None,
credentials: TeamsAuthCredentialsBase | None = None,
):
"""
Initialize the TeamsHistory class.
Args:
access_token: Microsoft Graph API access token (optional, for backward compatibility)
session: Database session for token refresh (optional)
connector_id: Connector ID for token refresh (optional)
credentials: Teams OAuth credentials (optional, will be loaded from DB if not provided)
"""
self.connector = TeamsConnector(
access_token=access_token,
session=session,
connector_id=connector_id,
credentials=credentials,
)
async def get_all_teams(self) -> list[dict[str, Any]]:
"""
Get list of all teams the user has access to.
Returns:
List of team objects containing team metadata.
"""
try:
teams = await self.connector.get_joined_teams()
logger.info("Retrieved %s teams", len(teams))
return teams
except Exception as e:
logger.error("Error fetching teams: %s", str(e))
raise
async def get_channels_for_team(self, team_id: str) -> list[dict[str, Any]]:
"""
Get list of all channels in a specific team.
Args:
team_id: The ID of the team
Returns:
List of channel objects containing channel metadata.
"""
try:
channels = await self.connector.get_team_channels(team_id)
logger.info("Retrieved %s channels for team %s", len(channels), team_id)
return channels
except Exception as e:
logger.error("Error fetching channels for team %s: %s", team_id, str(e))
raise
async def get_messages_from_channel(
self,
team_id: str,
channel_id: str,
start_date: datetime | None = None,
end_date: datetime | None = None,
include_replies: bool = True,
) -> list[dict[str, Any]]:
"""
Get messages from a specific channel with optional date filtering.
Args:
team_id: The ID of the team
channel_id: The ID of the channel
start_date: Optional start date for filtering messages
end_date: Optional end date for filtering messages
include_replies: Whether to include reply messages (default: True)
Returns:
List of message objects with content and metadata.
"""
try:
messages = await self.connector.get_channel_messages(
team_id, channel_id, start_date, end_date
)
logger.info(
"Retrieved %s messages from channel %s in team %s",
len(messages),
channel_id,
team_id,
)
# Fetch replies if requested
if include_replies:
all_messages = []
for message in messages:
all_messages.append(message)
# Get replies for this message
try:
replies = await self.connector.get_message_replies(
team_id, channel_id, message.get("id")
)
all_messages.extend(replies)
except Exception:
logger.warning(
"Failed to get replies for message %s",
message.get("id"),
exc_info=True,
)
# Continue without replies for this message
logger.info(
"Total messages including replies: %s for channel %s",
len(all_messages),
channel_id,
)
return all_messages
return messages
except Exception as e:
logger.error(
"Error fetching messages from channel %s in team %s: %s",
channel_id,
team_id,
str(e),
)
raise
async def get_all_messages_from_team(
self,
team_id: str,
start_date: datetime | None = None,
end_date: datetime | None = None,
include_replies: bool = True,
) -> dict[str, list[dict[str, Any]]]:
"""
Get all messages from all channels in a team.
Args:
team_id: The ID of the team
start_date: Optional start date for filtering messages
end_date: Optional end date for filtering messages
include_replies: Whether to include reply messages (default: True)
Returns:
Dictionary mapping channel IDs to lists of messages.
"""
try:
channels = await self.get_channels_for_team(team_id)
all_channel_messages = {}
for channel in channels:
channel_id = channel.get("id")
channel_name = channel.get("displayName", "Unknown")
try:
messages = await self.get_messages_from_channel(
team_id, channel_id, start_date, end_date, include_replies
)
all_channel_messages[channel_id] = messages
logger.info(
"Fetched %s messages from channel '%s' (%s)",
len(messages),
channel_name,
channel_id,
)
except Exception:
logger.error(
"Failed to fetch messages from channel '%s' (%s)",
channel_name,
channel_id,
exc_info=True,
)
all_channel_messages[channel_id] = []
return all_channel_messages
except Exception as e:
logger.error("Error fetching messages from team %s: %s", team_id, str(e))
raise
async def get_all_messages(
self,
start_date: datetime | None = None,
end_date: datetime | None = None,
include_replies: bool = True,
) -> dict[str, dict[str, list[dict[str, Any]]]]:
"""
Get all messages from all teams and channels the user has access to.
Args:
start_date: Optional start date for filtering messages
end_date: Optional end date for filtering messages
include_replies: Whether to include reply messages (default: True)
Returns:
Nested dictionary: team_id -> channel_id -> list of messages.
"""
try:
teams = await self.get_all_teams()
all_messages = {}
for team in teams:
team_id = team.get("id")
team_name = team.get("displayName", "Unknown")
try:
team_messages = await self.get_all_messages_from_team(
team_id, start_date, end_date, include_replies
)
all_messages[team_id] = team_messages
total_messages = sum(
len(messages) for messages in team_messages.values()
)
logger.info(
"Fetched %s total messages from team '%s' (%s)",
total_messages,
team_name,
team_id,
)
except Exception:
logger.error(
"Failed to fetch messages from team '%s' (%s)",
team_name,
team_id,
exc_info=True,
)
all_messages[team_id] = {}
return all_messages
except Exception as e:
logger.error("Error fetching all messages: %s", str(e))
raise