from typing import Optional, Literal, TYPE_CHECKING from datetime import datetime, timedelta from pydantic import BaseModel import re from bs4 import BeautifulSoup if TYPE_CHECKING: from vobject.icalendar import RecurringComponent from masto_bridges.repo import Commit from masto_bridges.models import Account from masto_bridges.caldav import Event class Status(BaseModel): """ Model of a toot on mastodon See: https://mastodonpy.readthedocs.io/en/stable/#toot-dicts """ id: int url: str account: Account content: str visibility: Literal['public', 'unlisted', 'private', 'direct'] created_at:datetime in_reply_to_id: Optional[int] = None in_reply_to_account_id: Optional[int] = None spoiler_text: Optional[str] = None reblog: Optional['Status'] = None def __init__(self, **kwargs): if kwargs.get('url', None) is None: # try using the uri kwargs['url'] = kwargs['uri'] super(Status, self).__init__(**kwargs) class Config: extra='ignore' class Post(BaseModel): #timestamp: Optional[datetime] = None text:str status:Optional[Status] = None commit:Optional[Commit] = None cal_user:Optional[str] = None cal_url:Optional[str] = None @classmethod def from_commit(cls, commit:Commit) -> 'Post': text = '\n'.join([commit.subject, commit.body]) return Post(text=text, commit=commit) @classmethod def from_status(cls, status:Status) -> 'Post': # if this is a boost, get the original status if status.reblog: date = status.created_at status = status.reblog status.created_at = date soup = BeautifulSoup(status.content, 'lxml') # replace with double line breaks pars = [p.text for p in soup.find_all('p')] text = '\n\n'.join(pars) return Post(text=text, status=status) @classmethod def from_vevent(cls, event:'RecurringComponent'): user=event.summary.value text = event.description.value try: url = event.location.value except AttributeError: url = None return Post(text=text, cal_user=user, cal_url=url) def format_masto(self) -> str: """ Format a post to go from git -> masto. Needs to have a :attr:`.commit` attribute! Does not split the body text into multiple toots. That should be handled in the posting action Example: git-social: https://{repo_url}/commits/{hash} {subject line} {body} """ if self.commit is not None: return f"xpost from git-social: {self.commit.url}\n---\n{self.text}""" elif self.cal_user is not None: return f"xpost from a calendar\n---\n{self.text}""" else: return self.text def format_commit(self) -> str: """ Add a link back to original masto post split by double lines """ return f"xpost from mastodon: {self.status.url}\n\n{self.text}" def format_caldav(self) -> Event: """ Format a mastodon post for posting to CALDAV """ return Event( dtstart=self.status.created_at, dtend = self.status.created_at + timedelta(minutes=5), location = self.status.url, summary= self.status.account.acct, description = self.text )