From b5af9d176b18a1a1abbc2c7831402457de40d84c Mon Sep 17 00:00:00 2001 From: finn Date: Tue, 17 May 2022 18:14:09 -0700 Subject: [PATCH] bump protocol --- protocol.json | 1306 +++++++++++++++++++----- signald/client-protocol/v0/structs.go | 1 + signald/client-protocol/v1/errors.go | 73 +- signald/client-protocol/v1/requests.go | 128 +++ signald/client-protocol/v1/structs.go | 225 +++- 5 files changed, 1400 insertions(+), 333 deletions(-) diff --git a/protocol.json b/protocol.json index a0ded3f..edf0bcb 100644 --- a/protocol.json +++ b/protocol.json @@ -2,9 +2,9 @@ "doc_version": "v1", "version": { "name": "signald", - "version": "0.17.0", + "version": "0.18.3", "branch": "main", - "commit": "3d482a6060b7235a18cdb8a4dc60aba61f99da74" + "commit": "2d898baa70a20c44ae918d2ce6e1f108579ee271" }, "info": "This document describes objects that may be used when communicating with signald.", "types": { @@ -487,7 +487,7 @@ "text": { "type": "String", "doc": "the body of the message being quoted", - "example": "\"hey ? what's up?\"" + "example": "\"hey  what's up?\"" }, "attachments": { "list": true, @@ -577,7 +577,7 @@ "emoji": { "type": "String", "doc": "the emoji to react with", - "example": "\"?\"" + "example": "\"👍\"" }, "remove": { "type": "boolean", @@ -916,6 +916,9 @@ }, "Optional": { "fields": { + "empty": { + "type": "boolean" + }, "present": { "type": "boolean" } @@ -926,13 +929,125 @@ } }, "v1": { - "ListenerState": { + "ClientMessageWrapper": { "fields": { - "connected": { - "type": "boolean" + "type": { + "type": "String", + "doc": "the type of object to expect in the `data` field" + }, + "version": { + "type": "String", + "doc": "the version of the object in the `data` field" + }, + "data": { + "type": "Object", + "doc": "the incoming object. The structure will vary from message to message, see `type` and `version` fields" + }, + "error": { + "type": "Boolean", + "doc": "true if the incoming message represents an error" + }, + "account": { + "type": "String", + "doc": "the account this message is from" } }, - "doc": "prior attempt to indicate signald connectivity state. WebSocketConnectionState messages will be delivered at the same time as well as in other parts of the websocket lifecycle." + "doc": "Wraps all incoming messages sent to the client after a v1 subscribe request is issued" + }, + "ProtocolInvalidMessageError": { + "fields": { + "sender": { + "type": "String" + }, + "timestamp": { + "type": "long" + }, + "message": { + "type": "String" + }, + "sender_device": { + "type": "int" + }, + "content_hint": { + "type": "int" + }, + "group_id": { + "type": "String" + } + }, + "error": true + }, + "UntrustedIdentityError": { + "fields": { + "identifier": { + "type": "String" + }, + "message": { + "type": "String" + }, + "identity_key": { + "type": "IdentityKey", + "version": "v1" + } + }, + "error": true + }, + "ProtocolNoSessionError": { + "fields": { + "sender": { + "type": "String" + }, + "timestamp": { + "type": "long" + }, + "message": { + "type": "String" + }, + "sender_device": { + "type": "int" + }, + "content_hint": { + "type": "int" + }, + "group_id": { + "type": "String" + } + }, + "error": true + }, + "ProtocolInvalidKeyIdError": { + "fields": { + "sender": { + "type": "String" + }, + "timestamp": { + "type": "long" + }, + "message": { + "type": "String" + }, + "sender_device": { + "type": "int" + }, + "content_hint": { + "type": "int" + }, + "group_id": { + "type": "String" + } + }, + "error": true + }, + "DuplicateMessageError": { + "fields": { + "timestamp": { + "type": "long" + }, + "message": { + "type": "String" + } + }, + "error": true }, "IncomingMessage": { "fields": { @@ -991,11 +1106,23 @@ "type": "TypingMessage", "version": "v1" }, + "story_message": { + "type": "StoryMessage", + "version": "v1" + }, "server_guid": { "type": "String" } } }, + "ListenerState": { + "fields": { + "connected": { + "type": "boolean" + } + }, + "doc": "prior attempt to indicate signald connectivity state. WebSocketConnectionState messages will be delivered at the same time as well as in other parts of the websocket lifecycle." + }, "WebSocketConnectionState": { "fields": { "state": { @@ -1009,154 +1136,24 @@ }, "doc": "indicates when the websocket connection state to the signal server has changed" }, - "JsonMessageEnvelope": { + "StorageChange": { "fields": { - "username": { - "type": "String", - "example": "\"+12024561414\"" - }, - "uuid": { - "type": "String", - "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"" - }, - "source": { - "type": "JsonAddress", - "version": "v1" - }, - "sourceDevice": { - "type": "int" - }, - "type": { - "type": "String" - }, - "relay": { - "type": "String" - }, - "timestamp": { - "type": "long", - "example": "1615576442475" - }, - "timestampISO": { - "type": "String" - }, - "serverTimestamp": { - "type": "long" - }, - "serverDeliveredTimestamp": { - "type": "long", - "example": "161557644247580" - }, - "hasLegacyMessage": { - "type": "boolean" - }, - "hasContent": { - "type": "boolean" - }, - "isUnidentifiedSender": { - "type": "boolean" - }, - "dataMessage": { - "type": "JsonDataMessage", - "version": "v1" - }, - "syncMessage": { - "type": "JsonSyncMessage", - "version": "v1" - }, - "callMessage": { - "type": "JsonCallMessage", - "version": "v0" - }, - "receipt": { - "type": "JsonReceiptMessage", - "version": "v0" - }, - "typing": { - "type": "JsonTypingMessage", - "version": "v0" - } - } - }, - "ClientMessageWrapper": { - "fields": { - "type": { - "type": "String", - "doc": "the type of object to expect in the `data` field" - }, "version": { - "type": "String", - "doc": "the version of the object in the `data` field" - }, - "data": { - "type": "Object", - "doc": "the incoming object. The structure will vary from message to message, see `type` and `version` fields" - }, - "error": { - "type": "Boolean", - "doc": "true if the incoming message represents an error" - }, - "account": { - "type": "String", - "doc": "the account this message is from" + "type": "long", + "doc": "Seems to behave like the group version numbers and increments every time the state changes" } }, - "doc": "Wraps all incoming messages sent to the client after a v1 subscribe request is issued" - }, - "UntrustedIdentityError": { - "fields": { - "identifier": { - "type": "String" - }, - "message": { - "type": "String" - }, - "identity_key": { - "type": "IdentityKey", - "version": "v1" - } - }, - "error": true - }, - "ProtocolInvalidMessageError": { - "fields": { - "sender": { - "type": "String" - }, - "timestamp": { - "type": "long" - }, - "message": { - "type": "String" - }, - "sender_device": { - "type": "int" - }, - "content_hint": { - "type": "int" - }, - "group_id": { - "type": "String" - } - }, - "error": true - }, - "DuplicateMessageError": { - "fields": { - "timestamp": { - "type": "long" - }, - "message": { - "type": "String" - } - }, - "error": true + "doc": "Broadcast to subscribed clients when there is a state change from the storage service" }, "SendRequest": { "fields": { "username": { "type": "String", - "example": "\"+12024561414\"", - "required": true + "example": "\"+12024561414\"" + }, + "account": { + "type": "String", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"" }, "recipientAddress": { "type": "JsonAddress", @@ -1318,6 +1315,23 @@ }, "error": true }, + "AuthorizationFailedError": { + "fields": { + "message": { + "type": "String" + } + }, + "doc": "Indicates the server rejected our credentials or a failed group update. Typically means the linked device was removed by the primary device, or that the account was re-registered. For group updates, this can indicate that we lack permissions.", + "error": true + }, + "SQLError": { + "fields": { + "message": { + "type": "String" + } + }, + "error": true + }, "ReactRequest": { "fields": { "username": { @@ -1372,7 +1386,7 @@ }, "version": { "type": "String", - "example": "\"0.17.0\"" + "example": "\"0.18.3\"" }, "branch": { "type": "String", @@ -1380,7 +1394,7 @@ }, "commit": { "type": "String", - "example": "\"3d482a6060b7235a18cdb8a4dc60aba61f99da74\"" + "example": "\"2d898baa70a20c44ae918d2ce6e1f108579ee271\"" } } }, @@ -1389,7 +1403,7 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "groupID": { @@ -1469,6 +1483,16 @@ "removed": { "type": "boolean", "doc": "will be set to true for incoming messages to indicate the user has been removed from the group" + }, + "banned_members": { + "list": true, + "type": "BannedGroupMember", + "version": "v1" + }, + "group_change": { + "type": "GroupChange", + "version": "v1", + "doc": "Represents a peer-to-peer group change done by a user. Will not be set if the group change signature fails verification. This is usually only set inside of incoming messages." } }, "doc": "Information about a Signal group" @@ -1481,13 +1505,13 @@ }, "error": true }, - "AuthorizationFailedError": { + "GroupPatchNotAcceptedError": { "fields": { "message": { "type": "String" } }, - "doc": "indicates the server rejected our credentials. Typically means the linked device was removed by the primary device, or that the account was re-registered", + "doc": "Indicates the server rejected our group update. This can be due to errors such as trying to add a user that's already in the group.", "error": true }, "ApproveMembershipRequest": { @@ -1495,7 +1519,7 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "groupID": { @@ -1526,7 +1550,7 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "groupID": { @@ -1554,7 +1578,7 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true } }, @@ -1574,7 +1598,7 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "uri": { @@ -1657,7 +1681,7 @@ "account": { "type": "String", "doc": "The identifier of the account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "groupID": { @@ -1776,7 +1800,7 @@ "account": { "type": "String", "doc": "The signal account to use", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "partial": { @@ -1809,7 +1833,7 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "to": { @@ -1835,6 +1859,7 @@ "account": { "type": "String", "doc": "the signald account to use", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "async": { @@ -1912,6 +1937,7 @@ "fields": { "account": { "type": "String", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true } } @@ -1934,6 +1960,7 @@ "fields": { "account": { "type": "String", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "async": { @@ -1956,7 +1983,7 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "title": { @@ -1998,7 +2025,7 @@ "account": { "type": "String", "doc": "The account to use", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "groupID": { @@ -2030,6 +2057,10 @@ }, "FinishLinkRequest": { "fields": { + "overwrite": { + "type": "boolean", + "doc": "overwrite existing account data if the phone number conflicts. false by default" + }, "device_name": { "type": "String" }, @@ -2050,6 +2081,9 @@ "type": "Boolean", "doc": "indicates the account has not completed registration" }, + "pni": { + "type": "String" + }, "device_id": { "type": "int", "doc": "The Signal device ID. Official Signal mobile clients (iPhone and Android) have device ID = 1, while linked devices such as Signal Desktop or Signal iPad have higher device IDs." @@ -2093,7 +2127,7 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "uri": { @@ -2188,7 +2222,7 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "address": { @@ -2219,7 +2253,7 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "address": { @@ -2274,7 +2308,7 @@ "account": { "type": "String", "doc": "The account to delete", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "server": { @@ -2289,7 +2323,7 @@ "account": { "type": "String", "doc": "The account to use", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "address": { @@ -2324,7 +2358,7 @@ "account": { "type": "String", "doc": "The account to use", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "address": { @@ -2364,7 +2398,7 @@ "account": { "type": "String", "doc": "The account to use", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true } }, @@ -2388,7 +2422,7 @@ "account": { "type": "String", "doc": "The account to use", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "uri": { @@ -2412,6 +2446,7 @@ "fields": { "account": { "type": "String", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "address": { @@ -2436,7 +2471,7 @@ "account": { "type": "String", "doc": "The account to use", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "address": { @@ -2460,7 +2495,7 @@ "account": { "type": "String", "doc": "The account to set the device name of", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "device_name": { @@ -2495,7 +2530,7 @@ "account": { "type": "String", "doc": "The account to subscribe to incoming message for", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true } }, @@ -2506,7 +2541,7 @@ "account": { "type": "String", "doc": "The account to unsubscribe from", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true } }, @@ -2517,7 +2552,7 @@ "account": { "type": "String", "doc": "the account to use", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "address": { @@ -2577,7 +2612,7 @@ "account": { "type": "String", "doc": "the account to use", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "address": { @@ -2602,7 +2637,7 @@ "account": { "type": "String", "doc": "The account to use to retrieve the remote config", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true } }, @@ -2622,7 +2657,7 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "members": { @@ -2636,6 +2671,9 @@ "type": "String", "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", "required": true + }, + "also_ban": { + "type": "boolean" } }, "doc": "deny a request to join a group" @@ -2644,6 +2682,7 @@ "fields": { "account": { "type": "String", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "challenge": { @@ -2660,7 +2699,7 @@ "account": { "type": "String", "doc": "The account to use to use", - "example": "\"+12024561414\"", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", "required": true }, "identifier": { @@ -2688,6 +2727,158 @@ }, "doc": "An optional part of the linking process. Intended to be called after displaying the QR code, will return quickly after the user scans the QR code. finish_link must be called after wait_for_scan returns a non-error" }, + "GetGroupRevisionPagesRequest": { + "fields": { + "account": { + "type": "String", + "doc": "The account to interact with", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", + "required": true + }, + "group_id": { + "type": "String", + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true + }, + "from_revision": { + "type": "int", + "doc": "The revision to start the pages from. Note that if this is lower than the revision you joined the group, an AuthorizationFailedError is returned.", + "required": true + }, + "include_first_revision": { + "type": "boolean", + "doc": "Whether to include the first state in the returned pages (default false)" + } + }, + "doc": "Query the server for group revision history. The history contains information about the changes between each revision and the user that made the change." + }, + "GroupHistoryPage": { + "fields": { + "results": { + "list": true, + "type": "GroupHistoryEntry", + "version": "v1" + }, + "paging_data": { + "type": "PagingData", + "version": "v1" + } + }, + "doc": "The result of fetching a group's history along with paging data." + }, + "SendSyncMessageRequest": { + "fields": { + "account": { + "type": "String", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", + "required": true + }, + "view_once_open_message": { + "type": "JsonViewOnceOpenMessage", + "version": "v1", + "doc": "This can be set to indicate to other devices about having viewed a view-once message." + }, + "message_request_response": { + "type": "JsonMessageRequestResponseMessage", + "version": "v1", + "doc": "This can be set to indicate to other devices about a response to an incoming message request from an unknown user or group. Warning: Using the BLOCK and BLOCK_AND_DELETE options relies on other devices to do the blocking, and it does not make you leave the group!" + } + }, + "doc": "Sends a sync message to the account's devices" + }, + "JsonSendMessageResult": { + "fields": { + "address": { + "type": "JsonAddress", + "version": "v1" + }, + "success": { + "type": "SendSuccess", + "version": "v1" + }, + "networkFailure": { + "type": "boolean", + "example": "false" + }, + "unregisteredFailure": { + "type": "boolean", + "example": "false" + }, + "identityFailure": { + "type": "String" + }, + "proof_required_failure": { + "type": "ProofRequiredError", + "version": "v1" + } + } + }, + "BanUserRequest": { + "fields": { + "account": { + "type": "String", + "doc": "The account to interact with", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", + "required": true + }, + "group_id": { + "type": "String", + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true + }, + "users": { + "list": true, + "type": "JsonAddress", + "version": "v1", + "doc": "List of users to ban", + "required": true + } + }, + "doc": "Bans users from a group. This works even if the users aren't in the group. If they are currently in the group, they will also be removed." + }, + "UnbanUserRequest": { + "fields": { + "account": { + "type": "String", + "doc": "The account to interact with", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"", + "required": true + }, + "group_id": { + "type": "String", + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true + }, + "users": { + "list": true, + "type": "JsonAddress", + "version": "v1", + "doc": "List of users to unban", + "required": true + } + }, + "doc": "Unbans users from a group." + }, + "IdentityKey": { + "fields": { + "added": { + "type": "long", + "doc": "the first time this identity key was seen" + }, + "safety_number": { + "type": "String", + "example": "\"373453558586758076680580548714989751943247272727416091564451\"" + }, + "qr_code_data": { + "type": "String", + "doc": "base64-encoded QR code data" + }, + "trust_level": { + "type": "String", + "doc": "One of TRUSTED_UNVERIFIED, TRUSTED_VERIFIED or UNTRUSTED" + } + } + }, "JsonDataMessage": { "fields": { "timestamp": { @@ -2773,9 +2964,17 @@ "version": "v1", "doc": "details about the MobileCoin payment attached to the message, if present" }, + "is_expiration_update": { + "type": "boolean", + "doc": "whether or not this message changes the expiresInSeconds value for the whole chat. Some messages (remote deletes, reactions, etc) will have expiresInSeconds=0 even though the chat has disappearing messages enabled." + }, "group_call_update": { "type": "String", "doc": "the eraId string from a group call message update" + }, + "story_context": { + "type": "StoryContext", + "version": "v1" } } }, @@ -2893,23 +3092,22 @@ } } }, - "IdentityKey": { + "StoryMessage": { "fields": { - "added": { - "type": "long", - "doc": "the first time this identity key was seen" + "group": { + "type": "JsonGroupV2Info", + "version": "v1" }, - "safety_number": { - "type": "String", - "example": "\"373453558586758076680580548714989751943247272727416091564451\"" + "file": { + "type": "JsonAttachment", + "version": "v1" }, - "qr_code_data": { - "type": "String", - "doc": "base64-encoded QR code data" + "text": { + "type": "TextAttachment", + "version": "v1" }, - "trust_level": { - "type": "String", - "doc": "One of TRUSTED_UNVERIFIED, TRUSTED_VERIFIED or UNTRUSTED" + "allow_replies": { + "type": "Boolean" } } }, @@ -2975,7 +3173,7 @@ "text": { "type": "String", "doc": "the body of the message being quoted", - "example": "\"hey ? what's up?\"" + "example": "\"hey  what's up?\"" }, "attachments": { "list": true, @@ -3033,39 +3231,12 @@ }, "doc": "metadata about one of the links in a message" }, - "JsonSendMessageResult": { - "fields": { - "address": { - "type": "JsonAddress", - "version": "v1" - }, - "success": { - "type": "SendSuccess", - "version": "v1" - }, - "networkFailure": { - "type": "boolean", - "example": "false" - }, - "unregisteredFailure": { - "type": "boolean", - "example": "false" - }, - "identityFailure": { - "type": "String" - }, - "proof_required_failure": { - "type": "ProofRequiredError", - "version": "v1" - } - } - }, "JsonReaction": { "fields": { "emoji": { "type": "String", "doc": "the emoji to react with", - "example": "\"?\"" + "example": "\"👍\"" }, "remove": { "type": "boolean", @@ -3117,6 +3288,125 @@ } } }, + "BannedGroupMember": { + "fields": { + "uuid": { + "type": "String", + "example": "\"aeed01f0-a234-478e-8cf7-261c283151e7\"" + }, + "timestamp": { + "type": "long", + "doc": "Timestamp as milliseconds since Unix epoch of when the user was banned. This field is set by the server." + } + } + }, + "GroupChange": { + "fields": { + "editor": { + "type": "JsonAddress", + "version": "v1", + "doc": "The user that made the change." + }, + "revision": { + "type": "Integer", + "doc": "The group revision that this change brings the group to." + }, + "new_members": { + "list": true, + "type": "GroupMember", + "version": "v1", + "doc": "Represents users have been added to the group. This can be from group members adding users, or a users joining via a group link that required no approval." + }, + "delete_members": { + "list": true, + "type": "JsonAddress", + "version": "v1", + "doc": "Represents users that have been removed from the group. This can be from admins removing users, or users choosing to leave the group" + }, + "modify_member_roles": { + "list": true, + "type": "GroupMember", + "version": "v1", + "doc": "Represents users with their new, modified role." + }, + "modified_profile_keys": { + "list": true, + "type": "GroupMember", + "version": "v1", + "doc": "Represents users that have rotated their profile key. Note that signald currently does not expose profile keys to clients. The joined revision property will always be 0 in this list." + }, + "new_pending_members": { + "list": true, + "type": "GroupPendingMember", + "version": "v1", + "doc": "Represents a user that has been invited to the group by another user." + }, + "delete_pending_members": { + "list": true, + "type": "JsonAddress", + "version": "v1" + }, + "promote_pending_members": { + "list": true, + "type": "GroupMember", + "version": "v1" + }, + "new_banned_members": { + "list": true, + "type": "BannedGroupMember", + "version": "v1" + }, + "new_unbanned_members": { + "list": true, + "type": "BannedGroupMember", + "version": "v1" + }, + "new_title": { + "type": "String" + }, + "new_avatar": { + "type": "Boolean", + "doc": "Whether this group change changed the avatar." + }, + "new_timer": { + "type": "Integer", + "doc": "New disappearing messages timer value." + }, + "new_access_control": { + "type": "GroupAccessControl", + "version": "v1", + "doc": "If not null, then this group change modified one of the access controls. Some of the properties in here will be null." + }, + "new_requesting_members": { + "list": true, + "type": "GroupRequestingMember", + "version": "v1", + "doc": "Represents users that have requested to join the group via the group link. Note that members requesting to join might not necessarily have the list of users in the group, so they won't be able to send a peer-to-peer group update message to inform users of their request to join. Other users in the group may inform us that the revision has increased, but the members requesting access will have to be obtained from the server instead (which signald will handle). For now, a get_group request has to be made to get the users that have requested to join the group." + }, + "delete_requesting_members": { + "list": true, + "type": "JsonAddress", + "version": "v1" + }, + "promote_requesting_members": { + "list": true, + "type": "GroupMember", + "version": "v1" + }, + "new_invite_link_password": { + "type": "Boolean", + "doc": "Whether this group change involved resetting the group invite link." + }, + "new_description": { + "type": "String" + }, + "new_is_announcement_group": { + "type": "String", + "doc": "Whether this change affected the announcement group setting. Possible values are UNKNOWN, ENABLED or DISABLED" + } + }, + "doc": "Represents a group change made by a user. This can also represent request link invites. Only the fields relevant to the group change performed will be set. Note that in signald, group changes are currently only received from incoming messages from a message subscription." + }, "DeviceInfo": { "fields": { "id": { @@ -3158,11 +3448,15 @@ "Capabilities": { "fields": { "gv2": { - "type": "boolean" + "type": "boolean", + "doc": "this capability is deprecated and will always be true" }, "storage": { "type": "boolean" }, + "stories": { + "type": "boolean" + }, "gv1-migration": { "type": "boolean" }, @@ -3263,6 +3557,93 @@ }, "doc": "A remote config (feature flag) entry." }, + "GroupHistoryEntry": { + "fields": { + "group": { + "type": "JsonGroupV2Info", + "version": "v1" + }, + "change": { + "type": "GroupChange", + "version": "v1" + } + } + }, + "PagingData": { + "fields": { + "has_more_pages": { + "type": "boolean" + }, + "next_page_revision": { + "type": "int" + } + } + }, + "JsonViewOnceOpenMessage": { + "fields": { + "sender": { + "type": "JsonAddress", + "version": "v1" + }, + "timestamp": { + "type": "long", + "example": "1615576442475" + } + } + }, + "JsonMessageRequestResponseMessage": { + "fields": { + "person": { + "type": "JsonAddress", + "version": "v1" + }, + "groupId": { + "type": "String" + }, + "type": { + "type": "String", + "doc": "One of UNKNOWN, ACCEPT, DELETE, BLOCK, BLOCK_AND_DELETE, UNBLOCK_AND_ACCEPT" + } + }, + "doc": "Responses to message requests from unknown users or groups" + }, + "SendSuccess": { + "fields": { + "unidentified": { + "type": "boolean" + }, + "needsSync": { + "type": "boolean" + }, + "duration": { + "type": "long" + }, + "devices": { + "list": true, + "type": "Integer" + } + } + }, + "ProofRequiredError": { + "fields": { + "token": { + "type": "String" + }, + "options": { + "list": true, + "type": "String", + "doc": "possible list values are RECAPTCHA and PUSH_CHALLENGE" + }, + "message": { + "type": "String" + }, + "retry_after": { + "type": "long", + "doc": "value in seconds" + } + }, + "error": true + }, "SharedContact": { "fields": { "name": { @@ -3306,6 +3687,16 @@ } } }, + "StoryContext": { + "fields": { + "author": { + "type": "String" + }, + "sent_timestamp": { + "type": "long" + } + } + }, "JsonSentTranscriptMessage": { "fields": { "destination": { @@ -3356,18 +3747,6 @@ } } }, - "JsonViewOnceOpenMessage": { - "fields": { - "sender": { - "type": "JsonAddress", - "version": "v1" - }, - "timestamp": { - "type": "long", - "example": "1615576442475" - } - } - }, "JsonVerifiedMessage": { "fields": { "destination": { @@ -3385,20 +3764,6 @@ } } }, - "JsonMessageRequestResponseMessage": { - "fields": { - "person": { - "type": "JsonAddress", - "version": "v1" - }, - "groupId": { - "type": "String" - }, - "type": { - "type": "String" - } - } - }, "OfferMessage": { "fields": { "id": { @@ -3464,42 +3829,63 @@ } } }, - "SendSuccess": { + "TextAttachment": { "fields": { - "unidentified": { - "type": "boolean" + "text": { + "type": "String" }, - "needsSync": { - "type": "boolean" + "style": { + "type": "String" }, - "duration": { - "type": "long" + "preview": { + "type": "JsonPreview", + "version": "v1" }, - "devices": { - "list": true, - "type": "Integer" + "text_foreground_color": { + "type": "String" + }, + "text_background_color": { + "type": "String" + }, + "background_gradient": { + "type": "Gradient", + "version": "v1" + }, + "background_color": { + "type": "String" } } }, - "ProofRequiredError": { + "GroupPendingMember": { "fields": { - "token": { - "type": "String" - }, - "options": { - "list": true, + "uuid": { "type": "String", - "doc": "possible list values are RECAPTCHA and PUSH_CHALLENGE" + "example": "\"aeed01f0-a234-478e-8cf7-261c283151e7\"" }, - "message": { - "type": "String" + "role": { + "type": "String", + "doc": "possible values are: UNKNOWN, DEFAULT, ADMINISTRATOR and UNRECOGNIZED", + "example": "\"DEFAULT\"" }, - "retry_after": { - "type": "long", - "doc": "value in seconds" + "timestamp": { + "type": "long" + }, + "added_by_uuid": { + "type": "String", + "example": "\"aeed01f0-a234-478e-8cf7-261c283151e7\"" } - }, - "error": true + } + }, + "GroupRequestingMember": { + "fields": { + "uuid": { + "type": "String", + "example": "\"aeed01f0-a234-478e-8cf7-261c283151e7\"" + }, + "timestamp": { + "type": "long" + } + } }, "ServerCDN": { "fields": { @@ -3611,6 +3997,16 @@ "type": "boolean" } } + }, + "Gradient": { + "fields": { + "start_color": { + "type": "String" + }, + "end_color": { + "type": "String" + } + } } } }, @@ -3652,6 +4048,12 @@ }, { "name": "AttachmentTooLargeError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -3689,6 +4091,12 @@ }, { "name": "UnregisteredUserError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -3723,7 +4131,15 @@ "name": "InvalidRequestError" }, { - "name": "AuthorizationFailedError" + "name": "AuthorizationFailedError", + "doc": "Can be caused if signald is setup as a linked device that has been removed by the primary device. If trying to update a group, this can also be caused if group permissions don't allow the update (e.g. current role insufficient or not a member)." + }, + { + "name": "SQLError" + }, + { + "name": "GroupPatchNotAcceptedError", + "doc": "Caused when server rejects the group update." } ] }, @@ -3754,7 +4170,15 @@ "name": "InvalidRequestError" }, { - "name": "AuthorizationFailedError" + "name": "AuthorizationFailedError", + "doc": "Can be caused if signald is setup as a linked device that has been removed by the primary device. If trying to update a group, this can also be caused if group permissions don't allow the update (e.g. current role insufficient or not a member)." + }, + { + "name": "SQLError" + }, + { + "name": "GroupPatchNotAcceptedError", + "doc": "Caused when server rejects the group update." } ] }, @@ -3789,6 +4213,9 @@ }, { "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -3811,6 +4238,12 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -3851,6 +4284,12 @@ }, { "name": "InvalidGroupStateError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -3875,6 +4314,9 @@ }, { "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -3905,10 +4347,18 @@ "name": "InvalidRequestError" }, { - "name": "AuthorizationFailedError" + "name": "AuthorizationFailedError", + "doc": "Can be caused if signald is setup as a linked device that has been removed by the primary device. If trying to update a group, this can also be caused if group permissions don't allow the update (e.g. current role insufficient or not a member)." }, { "name": "UnregisteredUserError" + }, + { + "name": "SQLError" + }, + { + "name": "GroupPatchNotAcceptedError", + "doc": "Caused when server rejects the group update, e.g. trying to add a user that's already in the group" } ] }, @@ -3932,6 +4382,12 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -3948,6 +4404,12 @@ }, { "name": "UnregisteredUserError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -3971,6 +4433,12 @@ }, { "name": "UnregisteredUserError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -3996,6 +4464,12 @@ }, { "name": "UnregisteredUserError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4017,6 +4491,12 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4035,6 +4515,12 @@ }, { "name": "NoSuchAccountError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4074,6 +4560,12 @@ }, { "name": "UnregisteredUserError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4104,6 +4596,9 @@ }, { "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4167,6 +4662,12 @@ }, { "name": "InternalError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4233,6 +4734,12 @@ }, { "name": "UnregisteredUserError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4269,6 +4776,12 @@ }, { "name": "UnregisteredUserError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4287,6 +4800,12 @@ }, { "name": "NoSuchAccountError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4323,6 +4842,12 @@ }, { "name": "UnregisteredUserError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4360,6 +4885,12 @@ }, { "name": "UnregisteredUserError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4384,6 +4915,12 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4425,6 +4962,9 @@ }, { "name": "GroupVerificationError" + }, + { + "name": "SQLError" } ] }, @@ -4444,6 +4984,12 @@ }, { "name": "InternalError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4474,10 +5020,18 @@ "name": "InvalidRequestError" }, { - "name": "AuthorizationFailedError" + "name": "AuthorizationFailedError", + "doc": "Can be caused if signald is setup as a linked device that has been removed by the primary device. If trying to update a group, this can also be caused if group permissions don't allow the update (e.g. current role insufficient or not a member)." }, { "name": "UnregisteredUserError" + }, + { + "name": "SQLError" + }, + { + "name": "GroupPatchNotAcceptedError", + "doc": "If updating a group, caused when server rejects the group update." } ] }, @@ -4496,6 +5050,12 @@ }, { "name": "NoSuchAccountError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4515,6 +5075,12 @@ }, { "name": "InternalError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4536,6 +5102,9 @@ }, { "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4548,6 +5117,9 @@ }, { "name": "InternalError" + }, + { + "name": "SQLError" } ] }, @@ -4585,6 +5157,12 @@ }, { "name": "UnregisteredUserError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4655,6 +5233,12 @@ }, { "name": "UnregisteredUserError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" } ] }, @@ -4674,6 +5258,9 @@ }, { "name": "NoSuchAccountError" + }, + { + "name": "SQLError" } ] }, @@ -4704,10 +5291,18 @@ "name": "InvalidRequestError" }, { - "name": "AuthorizationFailedError" + "name": "AuthorizationFailedError", + "doc": "Can be caused if signald is setup as a linked device that has been removed by the primary device. If trying to update a group, this can also be caused if group permissions don't allow the update (e.g. current role insufficient or not a member)." }, { "name": "UnregisteredUserError" + }, + { + "name": "SQLError" + }, + { + "name": "GroupPatchNotAcceptedError", + "doc": "Caused when server rejects the group update." } ] }, @@ -4728,6 +5323,9 @@ }, { "name": "InternalError" + }, + { + "name": "SQLError" } ] }, @@ -4747,6 +5345,9 @@ }, { "name": "NoSuchAccountError" + }, + { + "name": "SQLError" } ] }, @@ -4764,6 +5365,159 @@ "name": "InternalError" } ] + }, + "get_group_revision_pages": { + "request": "GetGroupRevisionPagesRequest", + "response": "GroupHistoryPage", + "doc": "Query the server for group revision history. The history contains information about the changes between each revision and the user that made the change.", + "errors": [ + { + "name": "NoSuchAccountError" + }, + { + "name": "UnknownGroupError" + }, + { + "name": "ServerNotFoundError" + }, + { + "name": "InvalidProxyError" + }, + { + "name": "InternalError" + }, + { + "name": "GroupVerificationError" + }, + { + "name": "InvalidGroupStateError" + }, + { + "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError", + "doc": "caused when not a member of the group, when requesting logs from a revision lower than your joinedAtVersion, etc." + }, + { + "name": "RateLimitError" + }, + { + "name": "SQLError" + } + ] + }, + "send_sync_message": { + "request": "SendSyncMessageRequest", + "response": "JsonSendMessageResult", + "doc": "Sends a sync message to the account's devices", + "errors": [ + { + "name": "InvalidRequestError" + }, + { + "name": "RateLimitError" + }, + { + "name": "InternalError" + }, + { + "name": "UnregisteredUserError" + }, + { + "name": "NoSuchAccountError" + }, + { + "name": "ServerNotFoundError" + }, + { + "name": "InvalidProxyError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "SQLError" + } + ] + }, + "ban_user": { + "request": "BanUserRequest", + "response": "JsonGroupV2Info", + "doc": "Bans users from a group. This works even if the users aren't in the group. If they are currently in the group, they will also be removed.", + "errors": [ + { + "name": "NoSuchAccountError" + }, + { + "name": "ServerNotFoundError" + }, + { + "name": "InvalidProxyError" + }, + { + "name": "UnknownGroupError" + }, + { + "name": "GroupVerificationError" + }, + { + "name": "InternalError" + }, + { + "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError", + "doc": "Can be caused if signald is setup as a linked device that has been removed by the primary device. If trying to update a group, this can also be caused if group permissions don't allow the update (e.g. current role insufficient or not a member)." + }, + { + "name": "SQLError" + }, + { + "name": "GroupPatchNotAcceptedError", + "doc": "Caused when server rejects the group update." + } + ] + }, + "unban_user": { + "request": "UnbanUserRequest", + "response": "JsonGroupV2Info", + "doc": "Unbans users from a group.", + "errors": [ + { + "name": "NoSuchAccountError" + }, + { + "name": "ServerNotFoundError" + }, + { + "name": "InvalidProxyError" + }, + { + "name": "UnknownGroupError" + }, + { + "name": "GroupVerificationError" + }, + { + "name": "InternalError" + }, + { + "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError", + "doc": "Can be caused if signald is setup as a linked device that has been removed by the primary device. If trying to update a group, this can also be caused if group permissions don't allow the update (e.g. current role insufficient or not a member)." + }, + { + "name": "SQLError" + }, + { + "name": "GroupPatchNotAcceptedError", + "doc": "Caused when server rejects the group update." + } + ] } } } diff --git a/signald/client-protocol/v0/structs.go b/signald/client-protocol/v0/structs.go index 92de9ec..1afd428 100644 --- a/signald/client-protocol/v0/structs.go +++ b/signald/client-protocol/v0/structs.go @@ -291,6 +291,7 @@ type OfferMessage struct { } type Optional struct { + Empty bool `json:"empty,omitempty" yaml:"empty,omitempty"` Present bool `json:"present,omitempty" yaml:"present,omitempty"` } diff --git a/signald/client-protocol/v1/errors.go b/signald/client-protocol/v1/errors.go index 0b3662c..c7a4841 100644 --- a/signald/client-protocol/v1/errors.go +++ b/signald/client-protocol/v1/errors.go @@ -81,6 +81,13 @@ func mkerr(response client_protocol.BasicResponse) error { return err } return result + case "GroupPatchNotAcceptedError": + result := GroupPatchNotAcceptedError{} + err := json.Unmarshal(response.Error, &result) + if err != nil { + return err + } + return result case "GroupVerificationError": result := GroupVerificationError{} err := json.Unmarshal(response.Error, &result) @@ -207,6 +214,13 @@ func mkerr(response client_protocol.BasicResponse) error { return err } return result + case "ProtocolInvalidKeyIdError": + result := ProtocolInvalidKeyIdError{} + err := json.Unmarshal(response.Error, &result) + if err != nil { + return err + } + return result case "ProtocolInvalidMessageError": result := ProtocolInvalidMessageError{} err := json.Unmarshal(response.Error, &result) @@ -214,6 +228,13 @@ func mkerr(response client_protocol.BasicResponse) error { return err } return result + case "ProtocolNoSessionError": + result := ProtocolNoSessionError{} + err := json.Unmarshal(response.Error, &result) + if err != nil { + return err + } + return result case "RateLimitError": result := RateLimitError{} err := json.Unmarshal(response.Error, &result) @@ -221,6 +242,13 @@ func mkerr(response client_protocol.BasicResponse) error { return err } return result + case "SQLError": + result := SQLError{} + err := json.Unmarshal(response.Error, &result) + if err != nil { + return err + } + return result case "ScanTimeoutError": result := ScanTimeoutError{} err := json.Unmarshal(response.Error, &result) @@ -309,7 +337,7 @@ func (e AttachmentTooLargeError) Error() string { return e.Message } -// AuthorizationFailedError: indicates the server rejected our credentials. Typically means the linked device was removed by the primary device, or that the account was re-registered +// AuthorizationFailedError: Indicates the server rejected our credentials or a failed group update. Typically means the linked device was removed by the primary device, or that the account was re-registered. For group updates, this can indicate that we lack permissions. type AuthorizationFailedError struct { Message string `json:"message,omitempty" yaml:"message,omitempty"` } @@ -360,6 +388,15 @@ func (e GroupNotActiveError) Error() string { return e.Message } +// GroupPatchNotAcceptedError: Indicates the server rejected our group update. This can be due to errors such as trying to add a user that's already in the group. +type GroupPatchNotAcceptedError struct { + Message string `json:"message,omitempty" yaml:"message,omitempty"` +} + +func (e GroupPatchNotAcceptedError) Error() string { + return e.Message +} + type GroupVerificationError struct { Message string `json:"message,omitempty" yaml:"message,omitempty"` } @@ -511,6 +548,19 @@ func (e ProofRequiredError) Error() string { return e.Message } +type ProtocolInvalidKeyIdError struct { + ContentHint int32 `json:"content_hint,omitempty" yaml:"content_hint,omitempty"` + GroupId string `json:"group_id,omitempty" yaml:"group_id,omitempty"` + Message string `json:"message,omitempty" yaml:"message,omitempty"` + Sender string `json:"sender,omitempty" yaml:"sender,omitempty"` + SenderDevice int32 `json:"sender_device,omitempty" yaml:"sender_device,omitempty"` + Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` +} + +func (e ProtocolInvalidKeyIdError) Error() string { + return e.Message +} + type ProtocolInvalidMessageError struct { ContentHint int32 `json:"content_hint,omitempty" yaml:"content_hint,omitempty"` GroupId string `json:"group_id,omitempty" yaml:"group_id,omitempty"` @@ -524,6 +574,19 @@ func (e ProtocolInvalidMessageError) Error() string { return e.Message } +type ProtocolNoSessionError struct { + ContentHint int32 `json:"content_hint,omitempty" yaml:"content_hint,omitempty"` + GroupId string `json:"group_id,omitempty" yaml:"group_id,omitempty"` + Message string `json:"message,omitempty" yaml:"message,omitempty"` + Sender string `json:"sender,omitempty" yaml:"sender,omitempty"` + SenderDevice int32 `json:"sender_device,omitempty" yaml:"sender_device,omitempty"` + Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` +} + +func (e ProtocolNoSessionError) Error() string { + return e.Message +} + type RateLimitError struct { Message string `json:"message,omitempty" yaml:"message,omitempty"` } @@ -532,6 +595,14 @@ func (e RateLimitError) Error() string { return e.Message } +type SQLError struct { + Message string `json:"message,omitempty" yaml:"message,omitempty"` +} + +func (e SQLError) Error() string { + return e.Message +} + type ScanTimeoutError struct { Message string `json:"message,omitempty" yaml:"message,omitempty"` } diff --git a/signald/client-protocol/v1/requests.go b/signald/client-protocol/v1/requests.go index 03388b9..ac5d93b 100644 --- a/signald/client-protocol/v1/requests.go +++ b/signald/client-protocol/v1/requests.go @@ -130,6 +130,38 @@ func (r *ApproveMembershipRequest) Submit(conn *signald.Signald) (response JsonG } +// Submit: Bans users from a group. This works even if the users aren't in the group. If they are currently in the group, they will also be removed. +func (r *BanUserRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { + r.Version = "v1" + r.Type = "ban_user" + if r.ID == "" { + r.ID = signald.GenerateID() + } + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return + } + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = mkerr(rawResponse) + return + } + + err = json.Unmarshal(rawResponse.Data, &response) + if err != nil { + rawResponseJson, _ := rawResponse.Data.MarshalJSON() + log.Println("signald-go: error unmarshalling response from signald of type", rawResponse.Type, string(rawResponseJson)) + return + } + + return response, nil + +} + func (r *CreateGroupRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { r.Version = "v1" r.Type = "create_group" @@ -338,6 +370,38 @@ func (r *GetGroupRequest) Submit(conn *signald.Signald) (response JsonGroupV2Inf } +// Submit: Query the server for group revision history. The history contains information about the changes between each revision and the user that made the change. +func (r *GetGroupRevisionPagesRequest) Submit(conn *signald.Signald) (response GroupHistoryPage, err error) { + r.Version = "v1" + r.Type = "get_group_revision_pages" + if r.ID == "" { + r.ID = signald.GenerateID() + } + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return + } + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = mkerr(rawResponse) + return + } + + err = json.Unmarshal(rawResponse.Data, &response) + if err != nil { + rawResponseJson, _ := rawResponse.Data.MarshalJSON() + log.Println("signald-go: error unmarshalling response from signald of type", rawResponse.Type, string(rawResponseJson)) + return + } + + return response, nil + +} + // Submit: Get information about a known keys for a particular address func (r *GetIdentitiesRequest) Submit(conn *signald.Signald) (response IdentityKeyList, err error) { r.Version = "v1" @@ -1047,6 +1111,38 @@ func (r *SendPaymentRequest) Submit(conn *signald.Signald) (response SendRespons } +// Submit: Sends a sync message to the account's devices +func (r *SendSyncMessageRequest) Submit(conn *signald.Signald) (response JsonSendMessageResult, err error) { + r.Version = "v1" + r.Type = "send_sync_message" + if r.ID == "" { + r.ID = signald.GenerateID() + } + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return + } + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = mkerr(rawResponse) + return + } + + err = json.Unmarshal(rawResponse.Data, &response) + if err != nil { + rawResponseJson, _ := rawResponse.Data.MarshalJSON() + log.Println("signald-go: error unmarshalling response from signald of type", rawResponse.Type, string(rawResponseJson)) + return + } + + return response, nil + +} + // Submit: set this device's name. This will show up on the mobile device on the same account under settings -> linked devices func (r *SetDeviceNameRequest) Submit(conn *signald.Signald) (err error) { r.Version = "v1" @@ -1227,6 +1323,38 @@ func (r *TypingRequest) Submit(conn *signald.Signald) (err error) { } +// Submit: Unbans users from a group. +func (r *UnbanUserRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { + r.Version = "v1" + r.Type = "unban_user" + if r.ID == "" { + r.ID = signald.GenerateID() + } + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return + } + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = mkerr(rawResponse) + return + } + + err = json.Unmarshal(rawResponse.Data, &response) + if err != nil { + rawResponseJson, _ := rawResponse.Data.MarshalJSON() + log.Println("signald-go: error unmarshalling response from signald of type", rawResponse.Type, string(rawResponseJson)) + return + } + + return response, nil + +} + // Submit: See subscribe for more info func (r *UnsubscribeRequest) Submit(conn *signald.Signald) (err error) { r.Version = "v1" diff --git a/signald/client-protocol/v1/structs.go b/signald/client-protocol/v1/structs.go index e589b8e..cf4845f 100644 --- a/signald/client-protocol/v1/structs.go +++ b/signald/client-protocol/v1/structs.go @@ -25,6 +25,7 @@ type Account struct { Address *JsonAddress `json:"address,omitempty" yaml:"address,omitempty"` // The address of this account DeviceId int32 `json:"device_id,omitempty" yaml:"device_id,omitempty"` // The Signal device ID. Official Signal mobile clients (iPhone and Android) have device ID = 1, while linked devices such as Signal Desktop or Signal iPad have higher device IDs. Pending bool `json:"pending,omitempty" yaml:"pending,omitempty"` // indicates the account has not completed registration + Pni string `json:"pni,omitempty" yaml:"pni,omitempty"` } type AccountList struct { @@ -62,6 +63,19 @@ type ApproveMembershipRequest struct { Members []*JsonAddress `json:"members,omitempty" yaml:"members,omitempty"` // list of requesting members to approve } +// BanUserRequest: Bans users from a group. This works even if the users aren't in the group. If they are currently in the group, they will also be removed. +type BanUserRequest struct { + Request + Account string `json:"account,omitempty" yaml:"account,omitempty"` // The account to interact with + GroupId string `json:"group_id,omitempty" yaml:"group_id,omitempty"` + Users []*JsonAddress `json:"users,omitempty" yaml:"users,omitempty"` // List of users to ban +} + +type BannedGroupMember struct { + Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` // Timestamp as milliseconds since Unix epoch of when the user was banned. This field is set by the server. + UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty"` +} + // BooleanMessage: A message containing a single boolean, usually as a response type BooleanMessage struct { Value bool `json:"value,omitempty" yaml:"value,omitempty"` @@ -85,9 +99,10 @@ type Capabilities struct { AnnouncementGroup bool `json:"announcement_group,omitempty" yaml:"announcement_group,omitempty"` ChangeNumber bool `json:"change_number,omitempty" yaml:"change_number,omitempty"` Gv1Migration bool `json:"gv1-migration,omitempty" yaml:"gv1-migration,omitempty"` - Gv2 bool `json:"gv2,omitempty" yaml:"gv2,omitempty"` + Gv2 bool `json:"gv2,omitempty" yaml:"gv2,omitempty"` // this capability is deprecated and will always be true SenderKey bool `json:"sender_key,omitempty" yaml:"sender_key,omitempty"` Storage bool `json:"storage,omitempty" yaml:"storage,omitempty"` + Stories bool `json:"stories,omitempty" yaml:"stories,omitempty"` } // ClientMessageWrapper: Wraps all incoming messages sent to the client after a v1 subscribe request is issued @@ -127,6 +142,7 @@ type DeviceInfo struct { type FinishLinkRequest struct { Request DeviceName string `json:"device_name,omitempty" yaml:"device_name,omitempty"` + Overwrite bool `json:"overwrite,omitempty" yaml:"overwrite,omitempty"` // overwrite existing account data if the phone number conflicts. false by default SessionId string `json:"session_id,omitempty" yaml:"session_id,omitempty"` } @@ -150,6 +166,15 @@ type GetGroupRequest struct { Revision int32 `json:"revision,omitempty" yaml:"revision,omitempty"` // the latest known revision, default value (-1) forces fetch from server } +// GetGroupRevisionPagesRequest: Query the server for group revision history. The history contains information about the changes between each revision and the user that made the change. +type GetGroupRevisionPagesRequest struct { + Request + Account string `json:"account,omitempty" yaml:"account,omitempty"` // The account to interact with + FromRevision int32 `json:"from_revision,omitempty" yaml:"from_revision,omitempty"` // The revision to start the pages from. Note that if this is lower than the revision you joined the group, an AuthorizationFailedError is returned. + GroupId string `json:"group_id,omitempty" yaml:"group_id,omitempty"` + IncludeFirstRevision bool `json:"include_first_revision,omitempty" yaml:"include_first_revision,omitempty"` // Whether to include the first state in the returned pages (default false) +} + // GetIdentitiesRequest: Get information about a known keys for a particular address type GetIdentitiesRequest struct { Request @@ -175,6 +200,11 @@ type GetServersRequest struct { Request } +type Gradient struct { + EndColor string `json:"end_color,omitempty" yaml:"end_color,omitempty"` + StartColor string `json:"start_color,omitempty" yaml:"start_color,omitempty"` +} + // GroupAccessControl: group access control settings. Options for each controlled action are: UNKNOWN, ANY, MEMBER, ADMINISTRATOR, UNSATISFIABLE and UNRECOGNIZED type GroupAccessControl struct { Attributes string `json:"attributes,omitempty" yaml:"attributes,omitempty"` // who can edit group info @@ -182,6 +212,42 @@ type GroupAccessControl struct { Members string `json:"members,omitempty" yaml:"members,omitempty"` // who can add members } +// GroupChange: Represents a group change made by a user. This can also represent request link invites. Only the fields relevant to the group change performed will be set. Note that in signald, group changes are currently only received from incoming messages from a message subscription. +type GroupChange struct { + DeleteMembers []*JsonAddress `json:"delete_members,omitempty" yaml:"delete_members,omitempty"` // Represents users that have been removed from the group. This can be from admins removing users, or users choosing to leave the group + DeletePendingMembers []*JsonAddress `json:"delete_pending_members,omitempty" yaml:"delete_pending_members,omitempty"` + DeleteRequestingMembers []*JsonAddress `json:"delete_requesting_members,omitempty" yaml:"delete_requesting_members,omitempty"` + Editor *JsonAddress `json:"editor,omitempty" yaml:"editor,omitempty"` // The user that made the change. + ModifiedProfileKeys []*GroupMember `json:"modified_profile_keys,omitempty" yaml:"modified_profile_keys,omitempty"` // Represents users that have rotated their profile key. Note that signald currently does not expose profile keys to clients. The joined revision property will always be 0 in this list. + ModifyMemberRoles []*GroupMember `json:"modify_member_roles,omitempty" yaml:"modify_member_roles,omitempty"` // Represents users with their new, modified role. + NewAccessControl *GroupAccessControl `json:"new_access_control,omitempty" yaml:"new_access_control,omitempty"` // If not null, then this group change modified one of the access controls. Some of the properties in here will be null. + NewAvatar bool `json:"new_avatar,omitempty" yaml:"new_avatar,omitempty"` // Whether this group change changed the avatar. + NewBannedMembers []*BannedGroupMember `json:"new_banned_members,omitempty" yaml:"new_banned_members,omitempty"` + NewDescription string `json:"new_description,omitempty" yaml:"new_description,omitempty"` + NewInviteLinkPassword bool `json:"new_invite_link_password,omitempty" yaml:"new_invite_link_password,omitempty"` // Whether this group change involved resetting the group invite link. + NewIsAnnouncementGroup string `json:"new_is_announcement_group,omitempty" yaml:"new_is_announcement_group,omitempty"` // Whether this change affected the announcement group setting. Possible values are UNKNOWN, ENABLED or DISABLED + NewMembers []*GroupMember `json:"new_members,omitempty" yaml:"new_members,omitempty"` // Represents users have been added to the group. This can be from group members adding users, or a users joining via a group link that required no approval. + NewPendingMembers []*GroupPendingMember `json:"new_pending_members,omitempty" yaml:"new_pending_members,omitempty"` // Represents a user that has been invited to the group by another user. + NewRequestingMembers []*GroupRequestingMember `json:"new_requesting_members,omitempty" yaml:"new_requesting_members,omitempty"` // Represents users that have requested to join the group via the group link. Note that members requesting to join might not necessarily have the list of users in the group, so they won't be able to send a peer-to-peer group update message to inform users of their request to join. Other users in the group may inform us that the revision has increased, but the members requesting access will have to be obtained from the server instead (which signald will handle). For now, a get_group request has to be made to get the users that have requested to join the group. + NewTimer int32 `json:"new_timer,omitempty" yaml:"new_timer,omitempty"` // New disappearing messages timer value. + NewTitle string `json:"new_title,omitempty" yaml:"new_title,omitempty"` + NewUnbannedMembers []*BannedGroupMember `json:"new_unbanned_members,omitempty" yaml:"new_unbanned_members,omitempty"` + PromotePendingMembers []*GroupMember `json:"promote_pending_members,omitempty" yaml:"promote_pending_members,omitempty"` + PromoteRequestingMembers []*GroupMember `json:"promote_requesting_members,omitempty" yaml:"promote_requesting_members,omitempty"` + Revision int32 `json:"revision,omitempty" yaml:"revision,omitempty"` // The group revision that this change brings the group to. +} + +type GroupHistoryEntry struct { + Change *GroupChange `json:"change,omitempty" yaml:"change,omitempty"` + Group *JsonGroupV2Info `json:"group,omitempty" yaml:"group,omitempty"` +} + +// GroupHistoryPage: The result of fetching a group's history along with paging data. +type GroupHistoryPage struct { + PagingData *PagingData `json:"paging_data,omitempty" yaml:"paging_data,omitempty"` + Results []*GroupHistoryEntry `json:"results,omitempty" yaml:"results,omitempty"` +} + // GroupInfo: A generic type that is used when the group version is not known type GroupInfo struct { V1 *JsonGroupInfo `json:"v1,omitempty" yaml:"v1,omitempty"` @@ -206,6 +272,18 @@ type GroupMember struct { UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty"` } +type GroupPendingMember struct { + AddedByUuid string `json:"added_by_uuid,omitempty" yaml:"added_by_uuid,omitempty"` + Role string `json:"role,omitempty" yaml:"role,omitempty"` // possible values are: UNKNOWN, DEFAULT, ADMINISTRATOR and UNRECOGNIZED + Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` + UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty"` +} + +type GroupRequestingMember struct { + Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` + UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty"` +} + type HangupMessage struct { DeviceId int32 `json:"device_id,omitempty" yaml:"device_id,omitempty"` ID int64 `json:"id,omitempty" yaml:"id,omitempty"` @@ -244,6 +322,7 @@ type IncomingMessage struct { ServerReceiverTimestamp int64 `json:"server_receiver_timestamp,omitempty" yaml:"server_receiver_timestamp,omitempty"` Source *JsonAddress `json:"source,omitempty" yaml:"source,omitempty"` SourceDevice int32 `json:"source_device,omitempty" yaml:"source_device,omitempty"` + StoryMessage *StoryMessage `json:"story_message,omitempty" yaml:"story_message,omitempty"` SyncMessage *JsonSyncMessage `json:"sync_message,omitempty" yaml:"sync_message,omitempty"` Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` Type string `json:"type,omitempty" yaml:"type,omitempty"` @@ -294,24 +373,26 @@ type JsonBlockedListMessage struct { } type JsonDataMessage struct { - Attachments []*JsonAttachment `json:"attachments,omitempty" yaml:"attachments,omitempty"` // files attached to the incoming message - Body string `json:"body,omitempty" yaml:"body,omitempty"` // the text body of the incoming message. - Contacts []*SharedContact `json:"contacts,omitempty" yaml:"contacts,omitempty"` // if the incoming message has a shared contact, the contact's information will be here - EndSession bool `json:"endSession,omitempty" yaml:"endSession,omitempty"` - ExpiresInSeconds int32 `json:"expiresInSeconds,omitempty" yaml:"expiresInSeconds,omitempty"` // the expiry timer on the incoming message. Clients should delete records of the message within this number of seconds - Group *JsonGroupInfo `json:"group,omitempty" yaml:"group,omitempty"` // if the incoming message was sent to a v1 group, information about that group will be here - GroupV2 *JsonGroupV2Info `json:"groupV2,omitempty" yaml:"groupV2,omitempty"` // if the incoming message was sent to a v2 group, basic identifying information about that group will be here. If group information changes, JsonGroupV2Info.revision is incremented. If the group revision is higher than previously seen, a client can retrieve the group information by calling get_group. - GroupCallUpdate string `json:"group_call_update,omitempty" yaml:"group_call_update,omitempty"` // the eraId string from a group call message update - Mentions []*JsonMention `json:"mentions,omitempty" yaml:"mentions,omitempty"` // list of mentions in the message - Payment *Payment `json:"payment,omitempty" yaml:"payment,omitempty"` // details about the MobileCoin payment attached to the message, if present - Previews []*JsonPreview `json:"previews,omitempty" yaml:"previews,omitempty"` // if the incoming message has a link preview, information about that preview will be here - ProfileKeyUpdate bool `json:"profileKeyUpdate,omitempty" yaml:"profileKeyUpdate,omitempty"` - Quote *JsonQuote `json:"quote,omitempty" yaml:"quote,omitempty"` // if the incoming message is a quote or reply to another message, this will contain information about that message - Reaction *JsonReaction `json:"reaction,omitempty" yaml:"reaction,omitempty"` // if the message adds or removes a reaction to another message, this will indicate what change is being made - RemoteDelete *RemoteDelete `json:"remoteDelete,omitempty" yaml:"remoteDelete,omitempty"` // if the inbound message is deleting a previously sent message, indicates which message should be deleted - Sticker *v0.JsonSticker `json:"sticker,omitempty" yaml:"sticker,omitempty"` // if the incoming message is a sticker, information about the sicker will be here - Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` // the timestamp that the message was sent at, according to the sender's device. This is used to uniquely identify this message for things like reactions and quotes. - ViewOnce bool `json:"viewOnce,omitempty" yaml:"viewOnce,omitempty"` // indicates the message is a view once message. View once messages typically include no body and a single image attachment. Official Signal clients will prevent the user from saving the image, and once the user has viewed the image once they will destroy the image. + Attachments []*JsonAttachment `json:"attachments,omitempty" yaml:"attachments,omitempty"` // files attached to the incoming message + Body string `json:"body,omitempty" yaml:"body,omitempty"` // the text body of the incoming message. + Contacts []*SharedContact `json:"contacts,omitempty" yaml:"contacts,omitempty"` // if the incoming message has a shared contact, the contact's information will be here + EndSession bool `json:"endSession,omitempty" yaml:"endSession,omitempty"` + ExpiresInSeconds int32 `json:"expiresInSeconds,omitempty" yaml:"expiresInSeconds,omitempty"` // the expiry timer on the incoming message. Clients should delete records of the message within this number of seconds + Group *JsonGroupInfo `json:"group,omitempty" yaml:"group,omitempty"` // if the incoming message was sent to a v1 group, information about that group will be here + GroupV2 *JsonGroupV2Info `json:"groupV2,omitempty" yaml:"groupV2,omitempty"` // if the incoming message was sent to a v2 group, basic identifying information about that group will be here. If group information changes, JsonGroupV2Info.revision is incremented. If the group revision is higher than previously seen, a client can retrieve the group information by calling get_group. + GroupCallUpdate string `json:"group_call_update,omitempty" yaml:"group_call_update,omitempty"` // the eraId string from a group call message update + IsExpirationUpdate bool `json:"is_expiration_update,omitempty" yaml:"is_expiration_update,omitempty"` // whether or not this message changes the expiresInSeconds value for the whole chat. Some messages (remote deletes, reactions, etc) will have expiresInSeconds=0 even though the chat has disappearing messages enabled. + Mentions []*JsonMention `json:"mentions,omitempty" yaml:"mentions,omitempty"` // list of mentions in the message + Payment *Payment `json:"payment,omitempty" yaml:"payment,omitempty"` // details about the MobileCoin payment attached to the message, if present + Previews []*JsonPreview `json:"previews,omitempty" yaml:"previews,omitempty"` // if the incoming message has a link preview, information about that preview will be here + ProfileKeyUpdate bool `json:"profileKeyUpdate,omitempty" yaml:"profileKeyUpdate,omitempty"` + Quote *JsonQuote `json:"quote,omitempty" yaml:"quote,omitempty"` // if the incoming message is a quote or reply to another message, this will contain information about that message + Reaction *JsonReaction `json:"reaction,omitempty" yaml:"reaction,omitempty"` // if the message adds or removes a reaction to another message, this will indicate what change is being made + RemoteDelete *RemoteDelete `json:"remoteDelete,omitempty" yaml:"remoteDelete,omitempty"` // if the inbound message is deleting a previously sent message, indicates which message should be deleted + Sticker *v0.JsonSticker `json:"sticker,omitempty" yaml:"sticker,omitempty"` // if the incoming message is a sticker, information about the sicker will be here + StoryContext *StoryContext `json:"story_context,omitempty" yaml:"story_context,omitempty"` + Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` // the timestamp that the message was sent at, according to the sender's device. This is used to uniquely identify this message for things like reactions and quotes. + ViewOnce bool `json:"viewOnce,omitempty" yaml:"viewOnce,omitempty"` // indicates the message is a view once message. View once messages typically include no body and a single image attachment. Official Signal clients will prevent the user from saving the image, and once the user has viewed the image once they will destroy the image. } // JsonGroupInfo: information about a legacy group @@ -335,21 +416,23 @@ type JsonGroupJoinInfo struct { // JsonGroupV2Info: Information about a Signal group type JsonGroupV2Info struct { - AccessControl *GroupAccessControl `json:"accessControl,omitempty" yaml:"accessControl,omitempty"` // current access control settings for this group - Announcements string `json:"announcements,omitempty" yaml:"announcements,omitempty"` // indicates if the group is an announcements group. Only admins are allowed to send messages to announcements groups. Options are UNKNOWN, ENABLED or DISABLED - Avatar string `json:"avatar,omitempty" yaml:"avatar,omitempty"` // path to the group's avatar on local disk, if available - Description string `json:"description,omitempty" yaml:"description,omitempty"` - ID string `json:"id,omitempty" yaml:"id,omitempty"` - InviteLink string `json:"inviteLink,omitempty" yaml:"inviteLink,omitempty"` // the signal.group link, if applicable - MemberDetail []*GroupMember `json:"memberDetail,omitempty" yaml:"memberDetail,omitempty"` // detailed member list - Members []*JsonAddress `json:"members,omitempty" yaml:"members,omitempty"` - PendingMemberDetail []*GroupMember `json:"pendingMemberDetail,omitempty" yaml:"pendingMemberDetail,omitempty"` // detailed pending member list - PendingMembers []*JsonAddress `json:"pendingMembers,omitempty" yaml:"pendingMembers,omitempty"` - Removed bool `json:"removed,omitempty" yaml:"removed,omitempty"` // will be set to true for incoming messages to indicate the user has been removed from the group - RequestingMembers []*JsonAddress `json:"requestingMembers,omitempty" yaml:"requestingMembers,omitempty"` - Revision int32 `json:"revision,omitempty" yaml:"revision,omitempty"` - Timer int32 `json:"timer,omitempty" yaml:"timer,omitempty"` - Title string `json:"title,omitempty" yaml:"title,omitempty"` + AccessControl *GroupAccessControl `json:"accessControl,omitempty" yaml:"accessControl,omitempty"` // current access control settings for this group + Announcements string `json:"announcements,omitempty" yaml:"announcements,omitempty"` // indicates if the group is an announcements group. Only admins are allowed to send messages to announcements groups. Options are UNKNOWN, ENABLED or DISABLED + Avatar string `json:"avatar,omitempty" yaml:"avatar,omitempty"` // path to the group's avatar on local disk, if available + BannedMembers []*BannedGroupMember `json:"banned_members,omitempty" yaml:"banned_members,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + GroupChange *GroupChange `json:"group_change,omitempty" yaml:"group_change,omitempty"` // Represents a peer-to-peer group change done by a user. Will not be set if the group change signature fails verification. This is usually only set inside of incoming messages. + ID string `json:"id,omitempty" yaml:"id,omitempty"` + InviteLink string `json:"inviteLink,omitempty" yaml:"inviteLink,omitempty"` // the signal.group link, if applicable + MemberDetail []*GroupMember `json:"memberDetail,omitempty" yaml:"memberDetail,omitempty"` // detailed member list + Members []*JsonAddress `json:"members,omitempty" yaml:"members,omitempty"` + PendingMemberDetail []*GroupMember `json:"pendingMemberDetail,omitempty" yaml:"pendingMemberDetail,omitempty"` // detailed pending member list + PendingMembers []*JsonAddress `json:"pendingMembers,omitempty" yaml:"pendingMembers,omitempty"` + Removed bool `json:"removed,omitempty" yaml:"removed,omitempty"` // will be set to true for incoming messages to indicate the user has been removed from the group + RequestingMembers []*JsonAddress `json:"requestingMembers,omitempty" yaml:"requestingMembers,omitempty"` + Revision int32 `json:"revision,omitempty" yaml:"revision,omitempty"` + Timer int32 `json:"timer,omitempty" yaml:"timer,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` } type JsonMention struct { @@ -358,31 +441,11 @@ type JsonMention struct { UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty"` // The UUID of the account being mentioned } -type JsonMessageEnvelope struct { - CallMessage *v0.JsonCallMessage `json:"callMessage,omitempty" yaml:"callMessage,omitempty"` - DataMessage *JsonDataMessage `json:"dataMessage,omitempty" yaml:"dataMessage,omitempty"` - HasContent bool `json:"hasContent,omitempty" yaml:"hasContent,omitempty"` - HasLegacyMessage bool `json:"hasLegacyMessage,omitempty" yaml:"hasLegacyMessage,omitempty"` - IsUnidentifiedSender bool `json:"isUnidentifiedSender,omitempty" yaml:"isUnidentifiedSender,omitempty"` - Receipt *v0.JsonReceiptMessage `json:"receipt,omitempty" yaml:"receipt,omitempty"` - Relay string `json:"relay,omitempty" yaml:"relay,omitempty"` - ServerDeliveredTimestamp int64 `json:"serverDeliveredTimestamp,omitempty" yaml:"serverDeliveredTimestamp,omitempty"` - ServerTimestamp int64 `json:"serverTimestamp,omitempty" yaml:"serverTimestamp,omitempty"` - Source *JsonAddress `json:"source,omitempty" yaml:"source,omitempty"` - SourceDevice int32 `json:"sourceDevice,omitempty" yaml:"sourceDevice,omitempty"` - SyncMessage *JsonSyncMessage `json:"syncMessage,omitempty" yaml:"syncMessage,omitempty"` - Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` - TimestampISO string `json:"timestampISO,omitempty" yaml:"timestampISO,omitempty"` - Type string `json:"type,omitempty" yaml:"type,omitempty"` - Typing *v0.JsonTypingMessage `json:"typing,omitempty" yaml:"typing,omitempty"` - Username string `json:"username,omitempty" yaml:"username,omitempty"` - UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty"` -} - +// JsonMessageRequestResponseMessage: Responses to message requests from unknown users or groups type JsonMessageRequestResponseMessage struct { GroupId string `json:"groupId,omitempty" yaml:"groupId,omitempty"` Person *JsonAddress `json:"person,omitempty" yaml:"person,omitempty"` - Type string `json:"type,omitempty" yaml:"type,omitempty"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` // One of UNKNOWN, ACCEPT, DELETE, BLOCK, BLOCK_AND_DELETE, UNBLOCK_AND_ACCEPT } // JsonPreview: metadata about one of the links in a message @@ -519,6 +582,11 @@ type OfferMessage struct { Type string `json:"type,omitempty" yaml:"type,omitempty"` } +type PagingData struct { + HasMorePages bool `json:"has_more_pages,omitempty" yaml:"has_more_pages,omitempty"` + NextPageRevision int32 `json:"next_page_revision,omitempty" yaml:"next_page_revision,omitempty"` +} + // Payment: details about a MobileCoin payment type Payment struct { Note string `json:"note,omitempty" yaml:"note,omitempty"` // note attached to the payment @@ -566,6 +634,7 @@ type ReceiptMessage struct { type RefuseMembershipRequest struct { Request Account string `json:"account,omitempty" yaml:"account,omitempty"` // The account to interact with + AlsoBan bool `json:"also_ban,omitempty" yaml:"also_ban,omitempty"` GroupId string `json:"group_id,omitempty" yaml:"group_id,omitempty"` Members []*JsonAddress `json:"members,omitempty" yaml:"members,omitempty"` // list of requesting members to refuse } @@ -658,6 +727,7 @@ type SendPaymentRequest struct { type SendRequest struct { Request + Account string `json:"account,omitempty" yaml:"account,omitempty"` Attachments []*JsonAttachment `json:"attachments,omitempty" yaml:"attachments,omitempty"` Members []*JsonAddress `json:"members,omitempty" yaml:"members,omitempty"` // Optionally set to a sub-set of group members. Ignored if recipientGroupId isn't specified Mentions []*JsonMention `json:"mentions,omitempty" yaml:"mentions,omitempty"` @@ -682,6 +752,14 @@ type SendSuccess struct { Unidentified bool `json:"unidentified,omitempty" yaml:"unidentified,omitempty"` } +// SendSyncMessageRequest: Sends a sync message to the account's devices +type SendSyncMessageRequest struct { + Request + Account string `json:"account,omitempty" yaml:"account,omitempty"` + MessageRequestResponse *JsonMessageRequestResponseMessage `json:"message_request_response,omitempty" yaml:"message_request_response,omitempty"` // This can be set to indicate to other devices about a response to an incoming message request from an unknown user or group. Warning: Using the BLOCK and BLOCK_AND_DELETE options relies on other devices to do the blocking, and it does not make you leave the group! + ViewOnceOpenMessage *JsonViewOnceOpenMessage `json:"view_once_open_message,omitempty" yaml:"view_once_open_message,omitempty"` // This can be set to indicate to other devices about having viewed a view-once message. +} + // Server: a Signal server type Server struct { Ca string `json:"ca,omitempty" yaml:"ca,omitempty"` // base64 encoded trust store, password must be 'whisper' @@ -784,6 +862,23 @@ type SharedContactPhone struct { Value string `json:"value,omitempty" yaml:"value,omitempty"` // the phone number } +// StorageChange: Broadcast to subscribed clients when there is a state change from the storage service +type StorageChange struct { + Version int64 `json:"version,omitempty" yaml:"version,omitempty"` // Seems to behave like the group version numbers and increments every time the state changes +} + +type StoryContext struct { + Author string `json:"author,omitempty" yaml:"author,omitempty"` + SentTimestamp int64 `json:"sent_timestamp,omitempty" yaml:"sent_timestamp,omitempty"` +} + +type StoryMessage struct { + AllowReplies bool `json:"allow_replies,omitempty" yaml:"allow_replies,omitempty"` + File *JsonAttachment `json:"file,omitempty" yaml:"file,omitempty"` + Group *JsonGroupV2Info `json:"group,omitempty" yaml:"group,omitempty"` + Text *TextAttachment `json:"text,omitempty" yaml:"text,omitempty"` +} + type SubmitChallengeRequest struct { Request Account string `json:"account,omitempty" yaml:"account,omitempty"` @@ -797,6 +892,16 @@ type SubscribeRequest struct { Account string `json:"account,omitempty" yaml:"account,omitempty"` // The account to subscribe to incoming message for } +type TextAttachment struct { + BackgroundColor string `json:"background_color,omitempty" yaml:"background_color,omitempty"` + BackgroundGradient *Gradient `json:"background_gradient,omitempty" yaml:"background_gradient,omitempty"` + Preview *JsonPreview `json:"preview,omitempty" yaml:"preview,omitempty"` + Style string `json:"style,omitempty" yaml:"style,omitempty"` + Text string `json:"text,omitempty" yaml:"text,omitempty"` + TextBackgroundColor string `json:"text_background_color,omitempty" yaml:"text_background_color,omitempty"` + TextForegroundColor string `json:"text_foreground_color,omitempty" yaml:"text_foreground_color,omitempty"` +} + // TrustRequest: Trust another user's safety number using either the QR code data or the safety number text type TrustRequest struct { Request @@ -823,6 +928,14 @@ type TypingRequest struct { When int64 `json:"when,omitempty" yaml:"when,omitempty"` } +// UnbanUserRequest: Unbans users from a group. +type UnbanUserRequest struct { + Request + Account string `json:"account,omitempty" yaml:"account,omitempty"` // The account to interact with + GroupId string `json:"group_id,omitempty" yaml:"group_id,omitempty"` + Users []*JsonAddress `json:"users,omitempty" yaml:"users,omitempty"` // List of users to unban +} + // UnsubscribeRequest: See subscribe for more info type UnsubscribeRequest struct { Request