types.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. # Copyright (c) 2020 Tulir Asokan
  2. #
  3. # This Source Code Form is subject to the terms of the Mozilla Public
  4. # License, v. 2.0. If a copy of the MPL was not distributed with this
  5. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. from typing import Optional, Dict, Any, List, NewType
  7. from uuid import UUID
  8. from attr import dataclass
  9. import attr
  10. from mautrix.types import SerializableAttrs, SerializableEnum
  11. GroupID = NewType('GroupID', str)
  12. @dataclass
  13. class Account(SerializableAttrs['Account']):
  14. device_id: int = attr.ib(metadata={"json": "deviceId"})
  15. username: str
  16. filename: str
  17. registered: bool
  18. has_keys: bool
  19. subscribed: bool
  20. uuid: Optional[UUID] = None
  21. @dataclass(frozen=True, eq=False)
  22. class Address(SerializableAttrs['Address']):
  23. number: Optional[str] = None
  24. uuid: Optional[UUID] = None
  25. @property
  26. def is_valid(self) -> bool:
  27. return bool(self.number) or bool(self.uuid)
  28. @property
  29. def best_identifier(self) -> str:
  30. return str(self.uuid) if self.uuid else self.number
  31. def __eq__(self, other: 'Address') -> bool:
  32. if not isinstance(other, Address):
  33. return False
  34. if self.uuid and other.uuid:
  35. return self.uuid == other.uuid
  36. elif self.number and other.number:
  37. return self.number == other.number
  38. return False
  39. def __hash__(self) -> int:
  40. if self.uuid:
  41. return hash(self.uuid)
  42. return hash(self.number)
  43. @classmethod
  44. def parse(cls, value: str) -> 'Address':
  45. return Address(number=value) if value.startswith("+") else Address(uuid=UUID(value))
  46. @dataclass
  47. class TrustLevel(SerializableEnum):
  48. TRUSTED_UNVERIFIED = "TRUSTED_UNVERIFIED"
  49. @dataclass
  50. class Identity(SerializableAttrs['Identity']):
  51. trust_level: TrustLevel
  52. added: int
  53. fingerprint: str
  54. safety_number: str
  55. qr_code_data: str
  56. address: Address
  57. @dataclass
  58. class GetIdentitiesResponse(SerializableAttrs['GetIdentitiesResponse']):
  59. identities: List[Identity]
  60. @dataclass
  61. class Contact(SerializableAttrs['Contact']):
  62. address: Address
  63. name: Optional[str] = None
  64. color: Optional[str] = None
  65. profile_key: Optional[str] = attr.ib(default=None, metadata={"json": "profileKey"})
  66. message_expiration_time: int = attr.ib(default=0, metadata={"json": "messageExpirationTime"})
  67. @dataclass
  68. class Profile(SerializableAttrs['Profile']):
  69. name: str
  70. avatar: str = ""
  71. identity_key: str = ""
  72. unidentified_access: str = ""
  73. unrestricted_unidentified_access: bool = False
  74. @dataclass
  75. class Group(SerializableAttrs['Group']):
  76. group_id: GroupID = attr.ib(metadata={"json": "groupId"})
  77. name: str = "Unknown group"
  78. # Sometimes "UPDATE"
  79. type: Optional[str] = None
  80. # Not always present
  81. members: List[Address] = attr.ib(factory=lambda: [])
  82. avatar_id: int = attr.ib(default=0, metadata={"json": "avatarId"})
  83. @dataclass
  84. class Attachment(SerializableAttrs['Attachment']):
  85. width: int = 0
  86. height: int = 0
  87. caption: Optional[str] = None
  88. preview: Optional[str] = None
  89. blurhash: Optional[str] = None
  90. voice_note: bool = attr.ib(default=False, metadata={"json": "voiceNote"})
  91. content_type: Optional[str] = attr.ib(default=None, metadata={"json": "contentType"})
  92. custom_filename: Optional[str] = attr.ib(default=None, metadata={"json": "customFilename"})
  93. # Only for incoming
  94. id: Optional[str] = None
  95. incoming_filename: Optional[str] = attr.ib(default=None, metadata={"json": "storedFilename"})
  96. digest: Optional[str] = None
  97. # Only for outgoing
  98. outgoing_filename: Optional[str] = attr.ib(default=None, metadata={"json": "filename"})
  99. @dataclass
  100. class Quote(SerializableAttrs['Quote']):
  101. id: int
  102. author: Address
  103. text: str
  104. # TODO: attachments, mentions
  105. @dataclass
  106. class Reaction(SerializableAttrs['Reaction']):
  107. emoji: str
  108. remove: bool
  109. target_author: Address = attr.ib(metadata={"json": "targetAuthor"})
  110. target_sent_timestamp: int = attr.ib(metadata={"json": "targetSentTimestamp"})
  111. @dataclass
  112. class Sticker(SerializableAttrs['Sticker']):
  113. attachment: Attachment
  114. pack_id: str = attr.ib(metadata={"json": "packID"})
  115. pack_key: str = attr.ib(metadata={"json": "packKey"})
  116. sticker_id: int = attr.ib(metadata={"json": "stickerID"})
  117. @dataclass
  118. class MessageData(SerializableAttrs['MessageData']):
  119. timestamp: int
  120. body: Optional[str] = None
  121. quote: Optional[Quote] = None
  122. reaction: Optional[Reaction] = None
  123. attachments: List[Attachment] = attr.ib(factory=lambda: [])
  124. sticker: Optional[Sticker] = None
  125. # TODO mentions (although signald doesn't support group v2 yet)
  126. group: Optional[Group] = None
  127. end_session: bool = attr.ib(default=False, metadata={"json": "endSession"})
  128. expires_in_seconds: int = attr.ib(default=0, metadata={"json": "expiresInSeconds"})
  129. profile_key_update: bool = attr.ib(default=False, metadata={"json": "profileKeyUpdate"})
  130. view_once: bool = attr.ib(default=False, metadata={"json": "viewOnce"})
  131. @dataclass
  132. class SentSyncMessage(SerializableAttrs['SentSyncMessage']):
  133. message: MessageData
  134. timestamp: int
  135. expiration_start_timestamp: int = attr.ib(metadata={"json": "expirationStartTimestamp"})
  136. is_recipient_update: bool = attr.ib(default=False, metadata={"json": "isRecipientUpdate"})
  137. unidentified_status: Dict[str, bool] = attr.ib(factory=lambda: {})
  138. destination: Optional[Address] = None
  139. class TypingAction(SerializableEnum):
  140. UNKNOWN = "UNKNOWN"
  141. STARTED = "STARTED"
  142. STOPPED = "STOPPED"
  143. @dataclass
  144. class TypingNotification(SerializableAttrs['TypingNotification']):
  145. action: TypingAction
  146. timestamp: int
  147. group_id: Optional[GroupID] = attr.ib(default=None, metadata={"json": "groupId"})
  148. @dataclass
  149. class OwnReadReceipt(SerializableAttrs['OwnReadReceipt']):
  150. sender: Address
  151. timestamp: int
  152. class ReceiptType(SerializableEnum):
  153. UNKNOWN = "UNKNOWN"
  154. DELIVERY = "DELIVERY"
  155. READ = "READ"
  156. @dataclass
  157. class Receipt(SerializableAttrs['Receipt']):
  158. type: ReceiptType
  159. timestamps: List[int]
  160. when: int
  161. @dataclass
  162. class SyncMessage(SerializableAttrs['SyncMessage']):
  163. sent: Optional[SentSyncMessage] = None
  164. typing: Optional[TypingNotification] = None
  165. read_messages: Optional[List[OwnReadReceipt]] = attr.ib(default=None,
  166. metadata={"json": "readMessages"})
  167. contacts: Optional[Dict[str, Any]] = None
  168. contacts_complete: bool = attr.ib(default=False, metadata={"json": "contactsComplete"})
  169. class MessageType(SerializableEnum):
  170. CIPHERTEXT = "CIPHERTEXT"
  171. UNIDENTIFIED_SENDER = "UNIDENTIFIED_SENDER"
  172. RECEIPT = "RECEIPT"
  173. PREKEY_BUNDLE = "PREKEY_BUNDLE"
  174. KEY_EXCHANGE = "KEY_EXCHANGE"
  175. UNKNOWN = "UNKNOWN"
  176. @dataclass
  177. class Message(SerializableAttrs['Message']):
  178. username: str
  179. source: Address
  180. timestamp: int
  181. timestamp_iso: str = attr.ib(metadata={"json": "timestampISO"})
  182. type: MessageType
  183. source_device: int = attr.ib(metadata={"json": "sourceDevice"})
  184. server_timestamp: int = attr.ib(metadata={"json": "serverTimestamp"})
  185. server_delivered_timestamp: int = attr.ib(metadata={"json": "serverDeliveredTimestamp"})
  186. has_content: bool = attr.ib(metadata={"json": "hasContent"})
  187. is_unidentified_sender: bool = attr.ib(metadata={"json": "isUnidentifiedSender"})
  188. has_legacy_message: bool = attr.ib(default=False, metadata={"json": "hasLegacyMessage"})
  189. data_message: Optional[MessageData] = attr.ib(default=None, metadata={"json": "dataMessage"})
  190. sync_message: Optional[SyncMessage] = attr.ib(default=None, metadata={"json": "syncMessage"})
  191. typing: Optional[TypingNotification] = None
  192. receipt: Optional[Receipt] = None