syntax = "proto3";

package model;

option java_package = "io.lantern.messaging";

enum MessageDirection {
  OUT = 0;
  IN = 1;
}

enum ContactType {
  DIRECT = 0; // e.g. a person
  GROUP = 1;
}

// A globally unique identifier for a Contact
message ContactId {
  ContactType type = 1;
  string id = 2; // the public IdentityKey for direct contacts, the globally unique group id (type 4 UUID) for groups, both base32 encoded
}

// A numeric version of the IdentityKey encoded in a phone-number like encoding
message ChatNumber {
  string number = 1; // the full contact number (82 or more digits)
  string shortNumber = 2; // the short version of the number by which the contact is registered with tassis
  string domain = 3; // the domain on which the short number is registered (e.g. lantern.io)
}

enum ContactSource {
  UNKNOWN = 0; // source of contact is unknown
  INTRODUCTION = 1; // contact was added through introduction
  APP1 = 2; // application-specific source 1
  APP2 = 3; // application-specific source 2
  APP3 = 4; // application-specific source 3
  APP4 = 5; // application-specific source 4
  APP5 = 6; // application-specific source 5
  UNSOLICITED = 7; // contact was added upon receipt of unsolicited message
}

// How well do we know a contact. Lower numbers equate to a lower verification level.
enum VerificationLevel {
  UNACCEPTED = 0; // contact hasn't even been accepted into contact list yet
  UNVERIFIED = 1; // we think we know who this is, but we haven't verified their key fingerprint
  VERIFIED = 2; // we've verified their key fingerprint
}

// A typed datum
message Datum {
  oneof value {
    string string = 1;
    double float = 2;
    int64 int = 3;
    bool bool = 4;
    bytes bytes = 5;
  }
}

// A contact in the address book
message Contact {
  ContactId contactId = 1;
  map<int32, string> applicationIds = 14; // application-specific contacts ids like usernames, phone numbers, etc. keyed to an integer type (type is application-defined)
  repeated string memberIds = 2; // the ids of group member Contacts, base32 encoded (only applies to Contacts of type GROUP)
  string displayName = 3; // the display name shown for this Contact (specific to our current device, the contact may have different display names in other people's address books)
  ContactSource source = 13; // source from which the Contact was added
  int64 createdTs = 4; // the millisecond unix timestamp when this contact was created
  int64 mostRecentMessageTs = 5; // the millisecond unix timestamp of the most recent message with this contact (excluding control messages)
  MessageDirection mostRecentMessageDirection = 6; // direction of the most recent message with this contact
  string mostRecentMessageText = 7; // the text of the most recent message with this contact
  string mostRecentAttachmentMimeType = 8; // the mime type of the most recent attachment with this contact
  int32 messagesDisappearAfterSeconds = 9; // the number of seconds after which new messages will automatically disappear. <=0 means they won't disappear.
  int64 firstReceivedMessageTs = 10; // the timestamp of when the first message was received from this contact (excluding control messages)
  bool hasReceivedMessage = 11; // boolean indicating whether or not the contact has received any message (including control messages)
  int64 mostRecentHelloTs = 12; // the timestamp of the most recent hello received from this Contact (used for bidirectional contact adding)
  VerificationLevel verificationLevel = 15; // how well do we know this contact
  string numericFingerprint = 16; // a numeric fingerprint of this contact (depends on our own messenger ID)
  bool blocked = 17; // flag indicating of the contact is blocked
  map<string, Datum> applicationData = 18; // place for applications to store app-specific data associated with the Contact
  ChatNumber chatNumber = 19; // for direct contacts, this is their chat number (looks like a phone number)
  bool isMe = 20; // if true, this contact is the "me" contact (i.e. the contact representing the current user)
  int32 numUnviewedMessages = 21; // the number of messages from this contact that have not been viewed
}

// A provisional direct contact that is not yet in the address book. If we receive a Hello from that
// Contact before the ProvisionalContact expires, then the contact is added to the address book.
// Otherwise, the provisional contact is deleted and the contactId is deleted from our Signal
// protocol store, ensuring no trace is left of this contact.
message ProvisionalContact {
  string contactId = 1; // the ID of the contact (base32 encoded)
  int64 expiresAt = 2; // the unix timestamp in milliseconds of when this provisional contact expires
  ContactSource source = 3; // source from which the Contact was added
  VerificationLevel verificationLevel = 4; // how well do we know this contact
}

// An attachment to a Message
message Attachment {
  string mimeType = 1; // the MIME type for this attachment
  bytes keyMaterial = 2; // 64 bytes of key material (32 bytes for AES256 encryption, 32 bytes for HmacSHA256 authentication)
  bytes digest = 3; // secure digest of the attachment
  int64 plaintextLength = 4; // the content length of the plain text
  map<string, string> metadata = 5;
  string downloadUrl = 6; // the URL from which the encrypted attachment can be downloaded
}

// A locally stored Attachment
message StoredAttachment {
  enum Status {
    PENDING = 0; // attachment has not yet been encrypted
    PENDING_UPLOAD = 1; // attachment has not yet been uploaded
    DONE = 2; // attachment has been uploaded/downloaded
    FAILED = 3; // attachment permanently failed to encrypt or upload/download
  }

  string guid = 1; // a globally unique ID for the attachment
  Attachment attachment = 2; // the Attachment
  string plainTextFilePath = 5; // path to the on-disk location of the plain text version of this attachment
  string encryptedFilePath = 3; // path to the on-disk location of encrypted version of this attachment
  Status status = 4; // the status of the upload/download
  StoredAttachment thumbnail = 6; // an optional thumbnail of this attachment
}

// An attachment with optional associated Thumbnail
message AttachmentWithThumbnail {
  Attachment attachment = 1;
  Attachment thumbnail = 2;
}

// Introduction is an introduction to a potential direct Contact
message Introduction {
  bytes id = 1; // the id of the person to whom you're being introduced
  string displayName = 2; // the display name of person to whom you're being introduced
  VerificationLevel verificationLevel = 3; // how well does the introducer know this contact
}

// Details of an Introduction (attached to a StoredMessage)
message IntroductionDetails {
  enum IntroductionStatus {
    PENDING = 0; // introduction has been made but has not yet been accepted/rejected
    ACCEPTED = 1; // introduction has been accepted
  }

  ContactId to = 1; // the id of the contact to which we're being introduced
  string displayName = 2; // the display name of the contact to whom we're being introduced. This
  // may change when accepting an Introduction to the same contact made by
  // someone else.
  string originalDisplayName = 3; // the original display name of the introduction, this will never change
  IntroductionStatus status = 4; // the status of the introduction
  VerificationLevel verificationLevel = 5; // how well does the introducer know this contact
  VerificationLevel constrainedVerificationLevel = 6; // verificationLevel constrained by introducer's verification level
}

// A text message with attachments, the primary type of message exchanged by users.
message Message {
  bytes id = 1; // the id of the message, unique under the given sender
  bytes replyToSenderId = 2; // if this message is a reply to another message, this contains the ID of the sender of that message
  bytes replyToId = 3; // if this message is a reply to another message, this contains the ID of that message
  string text = 4;
  map<int32, AttachmentWithThumbnail> attachments = 5; // attachments keyed to unique IDs for each attachment within this message
  int32 disappearAfterSeconds = 6; // if > 0, message should disappear within this many seconds of first being viewed
  Introduction introduction = 7; // an introduction to another Contact
}

// A locally stored Message
message StoredMessage {
  enum DeliveryStatus {
    SENDING = 0; // message is currently pending send but hasn't successfully finished yet
    PARTIALLY_SENT = 1; // message has been successfully sent to some of the recipients, will retry other recipients
    COMPLETELY_SENT = 2; // message has been successfully sent to all recipients
    PARTIALLY_FAILED = 3; // message failed to send to a subset of recipients, will not retry
    COMPLETELY_FAILED = 4; // message failed to send to any recipients, will not retry
  }

  ContactId contactId = 1; // the ContactId that identifies the conversation to which this message belongs (either a direct conversation with a person, or messages in a group)
  string senderId = 2; // the id of the person who sent this message (will match the conversation.id for direct conversations)
  string id = 3; // the id of the message (same as on Message), encoded in base32
  int64 ts = 4; // The unix timestamp in milliseconds for when the message was sent (for OUT) or received (for IN)
  string replyToSenderId = 5; // if this message is a reply to another message, this contains the ID of the sender of that message
  string replyToId = 6; // if this message is a reply to another message, this contains the ID of that message
  string text = 7;
  int32 disappearAfterSeconds = 8;
  map<int32, StoredAttachment> attachments = 9; // attachments keyed to unique IDs for each attachment within this message (excluding thumbnails)
  map<int32, int32> thumbnails = 15; // maps the id of a thumbnail attachment to the attachment for which it is a thumbnail
  MessageDirection direction = 10; // direction in which the message is going/came
  map<string, Reaction> reactions = 11; // reactions by the id of the reacter
  DeliveryStatus status = 12; // for outgoing messages, the status of its sending and delivery
  int64 firstViewedAt = 13; // the unix timestamp in milliseconds of when this message was first viewed on the current device
  int64 disappearAt = 14; // the unix timestamp in milliseconds of when this message will automatically disappear
  int64 remotelyDeletedAt = 16; // the unix timestamp in milliseconds of when an authorized party remotely deleted this message (will be 0 if the message hasn't been remotely deleted)
  ContactId remotelyDeletedBy = 17; // the id of the contact who remotely deleted this message
  IntroductionDetails introduction = 18; // if this message was an introduction, this stores details of that introduction
}

// A reaction to a message
message Reaction {
  bytes reactingToSenderId = 1; // the id of the sender of the message to which this is reacting
  bytes reactingToMessageId = 2; // the id of the message to which this is reacting
  string emoticon = 3; // a 2 byte emoticon representing the reaction
}

// An update to disappearing message settings
message DisappearSettings {
  int32 messagesDisappearAfterSeconds = 1; // the number of seconds after which new messages will automatically disappear. <=0 means they won't disappear.
}

// A Hello from a Contact. If this Hello is not marked as final, we'll respond with a final Hello of
// our own. Hellos are only processed in conjunction with ProvisionalContacts.
message Hello {
  bool final = 2; // indicates whether this is a final hello
}

// An envelope for messages to be transferred via the message broker
message TransferMessage {
  oneof content {
    bytes message = 1; // serialized Message
    bytes reaction = 2; // serialized Reaction
    bytes deleteMessageId = 3; // the id of a message from this Sender to delete
    bytes disappearSettings = 4; // serialized DisappearSettings
    bytes hello = 5; // serialized Hello
    bytes webRTCSignal = 6; // the content of a WebRTCSignal
  }

  int64 sent = 10000; // the unix timestamp in milliseconds of when this TransferMessage was sent
}

// An outbound Message
message OutboundMessage {
  enum SubDeliveryStatus {
    SENDING = 0; // message is currently in the process of sending to specific deviceId but hasn't successfully finished yet
    SENT = 1; // message has been sent to specific deviceId
  }

  string id = 1; // a unique identifier for this message
  string senderId = 2; // the id of who sent this message (same as on StoredMessage)
  string recipientId = 3; // the id of the recipient
  int64 sent = 4; // The unix timestamp in millisecond for when the message was sent
  map<string, SubDeliveryStatus> subDeliveryStatuses = 5; // Statuses of subdeliveries to specific deviceIds
  oneof content {
    string messageId = 31; // the id of the message (same as on Message), encoded in base32
    bytes reaction = 32; // serialized Reaction
    bytes deleteMessageId = 33; // the id of a message from this Sender to delete
    bytes disappearSettings = 34; // serialized DisappearSettings
    bytes hello = 35; // serialized Hello
  }
}

// An inbound Attachment
message InboundAttachment {
  string senderId = 1; // the id of who sent the message associated to the attachment
  string messageId = 2; // the id of the StoredMessage, encoded in base32
  int64 ts = 3; // The unix timestamp in milliseconds for when the message was received (has to match what's on the StoredMessage
  int32 attachmentId = 4; // The id of the attachment within the Message
  bool isThumbnail = 5; // indicates if this is a download of a thumbnail
}

// An audio waveform
message AudioWaveform {
  repeated int32 bars = 1; // amplitude bars of the waveform, encoded as 32 bit integers. 0 is the lowest amplitude, 255 is the highest.
}