from datetime import datetime from typing import Any, List, Optional, Self from uuid import uuid4 class AudioFile: uri: str creation_timestamp: int def __init__(self, uri: str, creation_timestamp: int): self.uri = uri self.creation_timestamp = creation_timestamp @classmethod def from_dict(cls, data: dict) -> Self: return cls( uri=data['uri'], creation_timestamp=data['creation_timestamp'] ) class Gif: uri: str def __init__(self, uri: str): self.uri = uri @classmethod def from_dict(cls, data: dict) -> Self: return cls( uri=data['uri'] ) class Photo: uri: str creation_timestamp: int def __init__(self, uri: str, creation_timestamp: int): self.uri = uri self.creation_timestamp = creation_timestamp @classmethod def from_dict(cls, data: dict) -> Self: return cls( uri=data['uri'], creation_timestamp=data['creation_timestamp'] ) class Reaction: reaction: str actor: str def __init__(self, reaction: str, actor: str): self.reaction = reaction self.actor = actor @classmethod def from_dict(cls, data: dict) -> Self: return cls( reaction=data['reaction'], actor=data['actor'] ) class Share: link: str share_text: str def __init__(self, link: str, share_text: str): self.link = link self.share_text = share_text @classmethod def from_dict(cls, data: dict) -> Self: return cls( link=data['link'], share_text=data['share_text'] ) class Sticker: uri: str ai_stickers: Optional[List[Any]] def __init__(self, uri: str, ai_stickers: Optional[List[Any]] = None): self.uri = uri self.ai_stickers = ai_stickers if ai_stickers is not None else [] @classmethod def from_dict(cls, data: dict) -> Self: return cls( uri=data['uri'], ai_stickers=data.get('ai_stickers', []) ) class Video: uri: str creation_timestamp: int def __init__(self, uri: str, creation_timestamp: int): self.uri = uri self.creation_timestamp = creation_timestamp @classmethod def from_dict(cls, data: dict) -> Self: return cls( uri=data['uri'], creation_timestamp=data['creation_timestamp'] ) class Message: sender_name: str date_time: datetime content: Optional[str] sticker: Optional[Sticker] share: Optional[Share] photos: Optional[List[Photo]] videos: Optional[List[Video]] gifs: Optional[List[Gif]] audio_files: Optional[List[AudioFile]] call_duration: Optional[int] reactions: Optional[List[Reaction]] is_unsent: Optional[bool] is_geoblocked_for_viewer: bool _id: str def __init__(self, sender_name: str, date_time: datetime, is_geoblocked_for_viewer: bool, content: Optional[str] = None, sticker: Optional[Sticker] = None, share: Optional[Share] = None, photos: Optional[List[Photo]] = None, videos: Optional[List[Video]] = None, gifs: Optional[List[Gif]] = None, audio_files: Optional[List[AudioFile]] = None, call_duration: Optional[int] = None, reactions: Optional[List[Reaction]] = None, is_unsent: Optional[bool] = None ): self.sender_name = sender_name self.date_time = date_time self.content = content self.sticker = sticker self.share = share self.photos = photos self.videos = videos self.gifs = gifs self.audio_files = audio_files self.call_duration = call_duration self.reactions = reactions self.is_unsent = is_unsent self.is_geoblocked_for_viewer = is_geoblocked_for_viewer self._id = str(uuid4()) @property def message_id(self) -> str: return self._id @classmethod def from_dict(cls, data: dict) -> Self: _timestamp_ms = data.get('timestamp_ms') _sender_name = data.get('sender_name') _is_geoblocked_for_viewer = data.get('is_geoblocked_for_viewer') _content = data.get('content') _sticker_dict = data.get('sticker') _share_dict = data.get('share') _photos_dict = data.get('photos') _videos_dict = data.get('videos') _gifs_dict = data.get('gifs') _audio_files_dict = data.get('audio_files') _call_duration = data.get('call_duration') _reactions_dict = data.get('reactions') _is_unsent = data.get('is_unsent') _date_time = datetime.fromtimestamp(_timestamp_ms / 1000) _sticker = Sticker.from_dict(_sticker_dict) if _sticker_dict else None _share = Share.from_dict(_share_dict) if _share_dict else None _photos = [Photo.from_dict(photo) for photo in _photos_dict] if _photos_dict else None _videos = [Video.from_dict(video) for video in _videos_dict] if _videos_dict else None _gifs = [Gif.from_dict(gif) for gif in _gifs_dict] if _gifs_dict else None _audio_files = [AudioFile.from_dict(audio_file) for audio_file in _audio_files_dict] if _audio_files_dict else None _reactions = [Reaction.from_dict(reaction) for reaction in _reactions_dict] if _reactions_dict else None return cls( sender_name=_sender_name, date_time=_date_time, is_geoblocked_for_viewer=_is_geoblocked_for_viewer, content=_content, sticker=_sticker, share=_share, photos=_photos, videos=_videos, gifs=_gifs, audio_files=_audio_files, call_duration=_call_duration, reactions=_reactions, is_unsent=_is_unsent ) class Image: creation_timestamp: int uri: str def __init__(self, creation_timestamp: int, uri: str): self.creation_timestamp = creation_timestamp self.uri = uri @classmethod def from_dict(cls, data: dict) -> Self: return cls( creation_timestamp=data['creation_timestamp'], uri=data['uri'] ) class JoinableMode: mode: int link: str def __init__(self, mode: int, link: str): self.mode = mode self.link = link @classmethod def from_dict(cls, data: dict) -> Self: return cls( mode=data['mode'], link=data['link'] ) class Participant: name: str def __init__(self, name: str) -> None: self.name = name @classmethod def from_dict(cls, data: dict) -> Self: return cls( name = data.get('name') ) class FacebookExport: messages: List[Message] participants: List[Participant] title: str is_still_participant: bool thread_path: str magic_words: List[Any] image: Image joinable_mode: JoinableMode def __init__(self, messages: List[Message], participants: List[Participant], title: str, is_still_participant: bool, thread_path: str, magic_words: List[Any], image: Image, joinable_mode: JoinableMode): self.messages = messages self.participants = participants self.title = title self.is_still_participant = is_still_participant self.thread_path = thread_path self.magic_words = magic_words self.image = image self.joinable_mode = joinable_mode @classmethod def from_dict(cls, data: dict) -> Self: messages_data = data.get('messages', []) participants_data = data.get('participants', []) title = data['title'] is_still_participant = data['is_still_participant'] thread_path = data['thread_path'] magic_words = data.get('magic_words', []) image = Image.from_dict(data['image']) joinable_mode = JoinableMode.from_dict(data['joinable_mode']) messages = [Message.from_dict(m) for m in messages_data] participants = {Participant.from_dict(p) for p in participants_data} return cls( messages=messages, participants=participants, title=title, is_still_participant=is_still_participant, thread_path=thread_path, magic_words=magic_words, image=image, joinable_mode=joinable_mode )