from datetime import datetime from json import dumps from pydantic import BaseModel, ConfigDict, PositiveInt, PrivateAttr from types import MappingProxyType from typing import Any, Dict, Literal, Mapping, Optional, Self, Sequence from ctp_slack_bot.models.base import Chunk, Content class SlackEventPayload(BaseModel): """Represents a general event payload from Slack.""" type: str event_ts: str model_config = ConfigDict(extra='allow', frozen=True) class SlackEvent(BaseModel): """Represents a general event from Slack.""" token: str team_id: str api_app_id: str event: SlackEventPayload type: str event_id: str event_time: int authed_users: Sequence[str] model_config = ConfigDict(frozen=True) class SlackUserTimestampPair(BaseModel): """Represents a Slack user-timestamp pair.""" user: str ts: str model_config = ConfigDict(frozen=True) class SlackReaction(BaseModel): """Represents a Slack reaction information.""" name: str count: PositiveInt users: Sequence[str] model_config = ConfigDict(frozen=True) class SlackMessage(Content): """Represents a message from Slack after adaptation.""" type: Literal["app_mention", "message"] subtype: Optional[str] = None channel: str channel_type: Optional[str] = None user: Optional[str] = None bot_id: Optional[str] = None thread_ts: Optional[str] = None text: str ts: str edited: Optional[SlackUserTimestampPair] = None event_ts: str deleted_ts: Optional[str] = None hidden: bool = False is_starred: Optional[bool] = None pinned_to: Optional[Sequence[str]] = None reactions: Optional[Sequence[SlackReaction]] = None def get_id(self: Self) -> str: """Unique identifier for this message.""" return f"slack-message:{self.channel}:{self.ts}" def get_chunks(self: Self) -> Sequence[Chunk]: return (Chunk(text=self.text, parent_id=self.get_id(), chunk_id="", metadata=self.get_metadata()), ) def get_metadata(self: Self) -> Mapping[str, Any]: return MappingProxyType({ "modificationTime": datetime.fromtimestamp(float(self.ts)) }) class SlackResponse(BaseModel): # TODO: This should also be based on Content as it is a SlackMessage―just not one for which we know the identity yet. """Represents a response message to be sent to Slack.""" text: str channel: Optional[str] thread_ts: Optional[str] = None