diff --git a/cmd/signaldctl/cmd/message/send/send-message.go b/cmd/signaldctl/cmd/message/send/send-message.go index f8bf02c..e4f705e 100644 --- a/cmd/signaldctl/cmd/message/send/send-message.go +++ b/cmd/signaldctl/cmd/message/send/send-message.go @@ -29,7 +29,6 @@ import ( "gitlab.com/signald/signald-go/cmd/signaldctl/common" "gitlab.com/signald/signald-go/cmd/signaldctl/config" - v0 "gitlab.com/signald/signald-go/signald/client-protocol/v0" "gitlab.com/signald/signald-go/signald/client-protocol/v1" ) @@ -67,7 +66,7 @@ var ( req := v1.SendRequest{ Username: account, MessageBody: strings.Join(args[1:], " "), - Attachments: []*v0.JsonAttachment{}, + Attachments: []*v1.JsonAttachment{}, RecipientAddress: toAddress, RecipientGroupID: toGroup, } @@ -78,7 +77,7 @@ var ( log.Fatal("error resolving attachment", err) } log.Println(path) - req.Attachments = append(req.Attachments, &v0.JsonAttachment{Filename: path}) + req.Attachments = append(req.Attachments, &v1.JsonAttachment{Filename: path}) } resp, err := req.Submit(common.Signald) diff --git a/protocol.json b/protocol.json index 68cb778..a0ded3f 100644 --- a/protocol.json +++ b/protocol.json @@ -2,13 +2,1013 @@ "doc_version": "v1", "version": { "name": "signald", - "version": "0.15.0-23-981b4409", + "version": "0.17.0", "branch": "main", - "commit": "981b44098da8ddd748832597d5f5bde019197902" + "commit": "3d482a6060b7235a18cdb8a4dc60aba61f99da74" }, "info": "This document describes objects that may be used when communicating with signald.", "types": { + "v0": { + "JsonAccountList": { + "fields": { + "accounts": { + "list": true, + "type": "JsonAccount", + "version": "v0" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonMessageEnvelope": { + "fields": { + "username": { + "type": "String", + "example": "\"+12024561414\"" + }, + "uuid": { + "type": "String", + "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"" + }, + "source": { + "type": "JsonAddress", + "version": "v0" + }, + "sourceDevice": { + "type": "int" + }, + "type": { + "type": "String" + }, + "relay": { + "type": "String", + "doc": "this field is no longer available and will never be populated" + }, + "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": "v0" + }, + "syncMessage": { + "type": "JsonSyncMessage", + "version": "v0" + }, + "callMessage": { + "type": "JsonCallMessage", + "version": "v0" + }, + "receipt": { + "type": "JsonReceiptMessage", + "version": "v0" + }, + "typing": { + "type": "JsonTypingMessage", + "version": "v0" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonAccount": { + "fields": { + "deviceId": { + "type": "int" + }, + "username": { + "type": "String" + }, + "filename": { + "type": "String" + }, + "uuid": { + "type": "String" + }, + "registered": { + "type": "boolean" + }, + "has_keys": { + "type": "boolean" + }, + "subscribed": { + "type": "boolean" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonAddress": { + "fields": { + "number": { + "type": "String" + }, + "uuid": { + "type": "UUID" + }, + "relay": { + "type": "String" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonDataMessage": { + "fields": { + "timestamp": { + "type": "long", + "doc": "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.", + "example": "1615576442475" + }, + "attachments": { + "list": true, + "type": "JsonAttachment", + "version": "v0", + "doc": "files attached to the incoming message" + }, + "body": { + "type": "String", + "doc": "the text body of the incoming message.", + "example": "\"hello\"" + }, + "group": { + "type": "JsonGroupInfo", + "version": "v0", + "doc": "if the incoming message was sent to a v1 group, information about that group will be here" + }, + "groupV2": { + "type": "JsonGroupV2Info", + "version": "v0", + "doc": "is the incoming message was sent to a v2 group, basic identifying information about that group will be here. For full information, use list_groups" + }, + "endSession": { + "type": "boolean" + }, + "expiresInSeconds": { + "type": "int", + "doc": "the expiry timer on the incoming message. Clients should delete records of the message within this number of seconds" + }, + "profileKeyUpdate": { + "type": "boolean" + }, + "quote": { + "type": "JsonQuote", + "version": "v0", + "doc": "if the incoming message is a quote or reply to another message, this will contain information about that message" + }, + "contacts": { + "list": true, + "type": "SharedContact", + "version": "v0", + "doc": "if the incoming message has a shared contact, the contact's information will be here" + }, + "previews": { + "list": true, + "type": "JsonPreview", + "version": "v0", + "doc": "if the incoming message has a link preview, information about that preview will be here" + }, + "sticker": { + "type": "JsonSticker", + "version": "v0", + "doc": "if the incoming message is a sticker, information about the sicker will be here" + }, + "viewOnce": { + "type": "boolean", + "doc": "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." + }, + "reaction": { + "type": "JsonReaction", + "version": "v0", + "doc": "if the message adds or removes a reaction to another message, this will indicate what change is being made" + }, + "remoteDelete": { + "type": "RemoteDelete", + "version": "v0", + "doc": "if the inbound message is deleting a previously sent message, indicates which message should be deleted" + }, + "mentions": { + "list": true, + "type": "JsonMention", + "version": "v0", + "doc": "list of mentions in the message" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonSyncMessage": { + "fields": { + "sent": { + "type": "JsonSentTranscriptMessage", + "version": "v0" + }, + "contacts": { + "type": "JsonAttachment", + "version": "v0" + }, + "contactsComplete": { + "type": "boolean" + }, + "groups": { + "type": "JsonAttachment", + "version": "v0" + }, + "blockedList": { + "type": "JsonBlockedListMessage", + "version": "v0" + }, + "request": { + "type": "String" + }, + "readMessages": { + "list": true, + "type": "JsonReadMessage", + "version": "v0" + }, + "viewOnceOpen": { + "type": "JsonViewOnceOpenMessage", + "version": "v0" + }, + "verified": { + "type": "JsonVerifiedMessage", + "version": "v0" + }, + "configuration": { + "type": "ConfigurationMessage", + "version": "v0" + }, + "stickerPackOperations": { + "list": true, + "type": "JsonStickerPackOperationMessage", + "version": "v0" + }, + "fetchType": { + "type": "String" + }, + "messageRequestResponse": { + "type": "JsonMessageRequestResponseMessage", + "version": "v0" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonCallMessage": { + "fields": { + "offerMessage": { + "type": "OfferMessage", + "version": "v0" + }, + "answerMessage": { + "type": "AnswerMessage", + "version": "v0" + }, + "busyMessage": { + "type": "BusyMessage", + "version": "v0" + }, + "hangupMessage": { + "type": "HangupMessage", + "version": "v0" + }, + "iceUpdateMessages": { + "list": true, + "type": "IceUpdateMessage", + "version": "v0" + }, + "destinationDeviceId": { + "type": "int" + }, + "isMultiRing": { + "type": "boolean" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonReceiptMessage": { + "fields": { + "type": { + "type": "String" + }, + "timestamps": { + "list": true, + "type": "Long" + }, + "when": { + "type": "long" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonTypingMessage": { + "fields": { + "action": { + "type": "String" + }, + "timestamp": { + "type": "long" + }, + "groupId": { + "type": "String" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonAttachment": { + "fields": { + "contentType": { + "type": "String" + }, + "id": { + "type": "String" + }, + "size": { + "type": "int" + }, + "storedFilename": { + "type": "String" + }, + "filename": { + "type": "String" + }, + "customFilename": { + "type": "String" + }, + "caption": { + "type": "String" + }, + "width": { + "type": "int" + }, + "height": { + "type": "int" + }, + "voiceNote": { + "type": "boolean" + }, + "key": { + "type": "String" + }, + "digest": { + "type": "String" + }, + "blurhash": { + "type": "String" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonGroupInfo": { + "fields": { + "groupId": { + "type": "String" + }, + "members": { + "list": true, + "type": "JsonAddress", + "version": "v0" + }, + "name": { + "type": "String" + }, + "type": { + "type": "String" + }, + "avatarId": { + "type": "long" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonGroupV2Info": { + "fields": { + "id": { + "type": "String", + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + }, + "revision": { + "type": "int", + "example": "5" + }, + "title": { + "type": "String", + "example": "\"Parkdale Run Club\"" + }, + "description": { + "type": "String" + }, + "avatar": { + "type": "String", + "doc": "path to the group's avatar on local disk, if available", + "example": "\"/var/lib/signald/avatars/group-EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + }, + "timer": { + "type": "int", + "example": "604800" + }, + "members": { + "list": true, + "type": "JsonAddress", + "version": "v0" + }, + "pendingMembers": { + "list": true, + "type": "JsonAddress", + "version": "v0" + }, + "requestingMembers": { + "list": true, + "type": "JsonAddress", + "version": "v0" + }, + "inviteLink": { + "type": "String", + "doc": "the signal.group link, if applicable" + }, + "accessControl": { + "type": "GroupAccessControl", + "version": "v0", + "doc": "current access control settings for this group" + }, + "memberDetail": { + "list": true, + "type": "GroupMember", + "version": "v0", + "doc": "detailed member list" + }, + "pendingMemberDetail": { + "list": true, + "type": "GroupMember", + "version": "v0", + "doc": "detailed pending member list" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonQuote": { + "fields": { + "id": { + "type": "long", + "doc": "the client timestamp of the message being quoted", + "example": "1615576442475" + }, + "author": { + "type": "JsonAddress", + "version": "v0", + "doc": "the author of the message being quoted" + }, + "text": { + "type": "String", + "doc": "the body of the message being quoted", + "example": "\"hey ? what's up?\"" + }, + "attachments": { + "list": true, + "type": "JsonQuotedAttachment", + "version": "v0", + "doc": "list of files attached to the quoted message" + }, + "mentions": { + "list": true, + "type": "JsonMention", + "version": "v0", + "doc": "list of mentions in the quoted message" + } + }, + "doc": "A quote is a reply to a previous message. ID is the sent time of the message being replied to", + "deprecated": true, + "removal_date": 1641027661 + }, + "SharedContact": { + "fields": { + "name": { + "type": "Name", + "version": "v0" + }, + "avatar": { + "type": "Optional", + "version": "v0" + }, + "phone": { + "type": "Optional", + "version": "v0" + }, + "email": { + "type": "Optional", + "version": "v0" + }, + "address": { + "type": "Optional", + "version": "v0" + }, + "organization": { + "type": "Optional", + "version": "v0" + } + } + }, + "JsonPreview": { + "fields": { + "url": { + "type": "String" + }, + "title": { + "type": "String" + }, + "attachment": { + "type": "JsonAttachment", + "version": "v0" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonSticker": { + "fields": { + "packID": { + "type": "String" + }, + "packKey": { + "type": "String" + }, + "stickerID": { + "type": "int" + }, + "attachment": { + "type": "JsonAttachment", + "version": "v0" + }, + "image": { + "type": "String" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonReaction": { + "fields": { + "emoji": { + "type": "String", + "doc": "the emoji to react with", + "example": "\"?\"" + }, + "remove": { + "type": "boolean", + "doc": "set to true to remove the reaction. requires emoji be set to previously reacted emoji" + }, + "targetAuthor": { + "type": "JsonAddress", + "version": "v0", + "doc": "the author of the message being reacted to" + }, + "targetSentTimestamp": { + "type": "long", + "doc": "the client timestamp of the message being reacted to", + "example": "1615576442475" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "RemoteDelete": { + "fields": { + "targetSentTimestamp": { + "type": "long" + } + } + }, + "JsonMention": { + "fields": { + "uuid": { + "type": "String", + "doc": "The UUID of the account being mentioned", + "example": "\"aeed01f0-a234-478e-8cf7-261c283151e7\"" + }, + "start": { + "type": "int", + "doc": "The number of characters in that the mention starts at. Note that due to a quirk of how signald encodes JSON, if this value is 0 (for example if the first character in the message is the mention) the field won't show up.", + "example": "4" + }, + "length": { + "type": "int", + "doc": "The length of the mention represented in the message. Seems to always be 1 but included here in case that changes.", + "example": "1" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonSentTranscriptMessage": { + "fields": { + "destination": { + "type": "JsonAddress", + "version": "v0" + }, + "timestamp": { + "type": "long", + "example": "1615576442475" + }, + "expirationStartTimestamp": { + "type": "long" + }, + "message": { + "type": "JsonDataMessage", + "version": "v0" + }, + "unidentifiedStatus": { + "type": "Map" + }, + "isRecipientUpdate": { + "type": "boolean" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonBlockedListMessage": { + "fields": { + "addresses": { + "list": true, + "type": "JsonAddress", + "version": "v0" + }, + "groupIds": { + "list": true, + "type": "String" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonReadMessage": { + "fields": { + "sender": { + "type": "JsonAddress", + "version": "v0" + }, + "timestamp": { + "type": "long", + "example": "1615576442475" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonViewOnceOpenMessage": { + "fields": { + "sender": { + "type": "JsonAddress", + "version": "v0" + }, + "timestamp": { + "type": "long", + "example": "1615576442475" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonVerifiedMessage": { + "fields": { + "destination": { + "type": "JsonAddress", + "version": "v0" + }, + "identityKey": { + "type": "String" + }, + "verified": { + "type": "String" + }, + "timestamp": { + "type": "long" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "ConfigurationMessage": { + "fields": { + "readReceipts": { + "type": "Optional", + "version": "v0" + }, + "unidentifiedDeliveryIndicators": { + "type": "Optional", + "version": "v0" + }, + "typingIndicators": { + "type": "Optional", + "version": "v0" + }, + "linkPreviews": { + "type": "Optional", + "version": "v0" + } + } + }, + "JsonStickerPackOperationMessage": { + "fields": { + "packID": { + "type": "String" + }, + "packKey": { + "type": "String" + }, + "type": { + "type": "String" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "JsonMessageRequestResponseMessage": { + "fields": { + "person": { + "type": "JsonAddress", + "version": "v0" + }, + "groupId": { + "type": "String" + }, + "type": { + "type": "String" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "OfferMessage": { + "fields": { + "id": { + "type": "long" + }, + "sdp": { + "type": "String" + }, + "type": { + "type": "Type", + "version": "v0" + }, + "opaque": { + "type": "String" + } + } + }, + "AnswerMessage": { + "fields": { + "id": { + "type": "long" + }, + "sdp": { + "type": "String" + }, + "opaque": { + "type": "String" + } + } + }, + "BusyMessage": { + "fields": { + "id": { + "type": "long" + } + } + }, + "HangupMessage": { + "fields": { + "id": { + "type": "long" + }, + "type": { + "type": "Type", + "version": "v0" + }, + "deviceId": { + "type": "int" + }, + "legacy": { + "type": "boolean" + } + } + }, + "IceUpdateMessage": { + "fields": { + "id": { + "type": "long" + }, + "opaque": { + "type": "String" + }, + "sdp": { + "type": "String" + } + } + }, + "JsonQuotedAttachment": { + "fields": { + "contentType": { + "type": "String" + }, + "fileName": { + "type": "String" + }, + "thumbnail": { + "type": "JsonAttachment", + "version": "v0" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "GroupAccessControl": { + "fields": { + "link": { + "type": "String", + "doc": "UNSATISFIABLE when the group link is disabled, ADMINISTRATOR when the group link is enabled but an administrator must approve new members, ANY when the group link is enabled and no approval is required", + "example": "\"ANY\"" + }, + "attributes": { + "type": "String", + "doc": "who can edit group info" + }, + "members": { + "type": "String", + "doc": "who can add members" + } + }, + "doc": "group access control settings. Options for each controlled action are: UNKNOWN, ANY, MEMBER, ADMINISTRATOR, UNSATISFIABLE and UNRECOGNIZED", + "deprecated": true, + "removal_date": 1641027661 + }, + "GroupMember": { + "fields": { + "uuid": { + "type": "String", + "example": "\"aeed01f0-a234-478e-8cf7-261c283151e7\"" + }, + "role": { + "type": "String", + "doc": "possible values are: UNKNOWN, DEFAULT, ADMINISTRATOR and UNRECOGNIZED", + "example": "\"DEFAULT\"" + }, + "joined_revision": { + "type": "int" + } + }, + "deprecated": true, + "removal_date": 1641027661 + }, + "Name": { + "fields": { + "display": { + "type": "Optional", + "version": "v0" + }, + "given": { + "type": "Optional", + "version": "v0" + }, + "family": { + "type": "Optional", + "version": "v0" + }, + "prefix": { + "type": "Optional", + "version": "v0" + }, + "suffix": { + "type": "Optional", + "version": "v0" + }, + "middle": { + "type": "Optional", + "version": "v0" + } + } + }, + "Optional": { + "fields": { + "present": { + "type": "boolean" + } + } + }, + "Type": { + "fields": {} + } + }, "v1": { + "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." + }, + "IncomingMessage": { + "fields": { + "account": { + "type": "String", + "example": "\"+12024561414\"" + }, + "source": { + "type": "JsonAddress", + "version": "v1" + }, + "type": { + "type": "String" + }, + "timestamp": { + "type": "long", + "example": "1615576442475" + }, + "source_device": { + "type": "int" + }, + "server_receiver_timestamp": { + "type": "long", + "example": "1615576442475" + }, + "server_deliver_timestamp": { + "type": "long", + "example": "1615576442475" + }, + "has_legacy_message": { + "type": "boolean" + }, + "has_content": { + "type": "boolean" + }, + "unidentified_sender": { + "type": "boolean" + }, + "data_message": { + "type": "JsonDataMessage", + "version": "v1" + }, + "sync_message": { + "type": "JsonSyncMessage", + "version": "v1" + }, + "call_message": { + "type": "CallMessage", + "version": "v1" + }, + "receipt_message": { + "type": "ReceiptMessage", + "version": "v1" + }, + "typing_message": { + "type": "TypingMessage", + "version": "v1" + }, + "server_guid": { + "type": "String" + } + } + }, + "WebSocketConnectionState": { + "fields": { + "state": { + "type": "String", + "doc": "One of: DISCONNECTED, CONNECTING, CONNECTED, RECONNECTING, DISCONNECTING, AUTHENTICATION_FAILED, FAILED" + }, + "socket": { + "type": "String", + "doc": "One of: UNIDENTIFIED, IDENTIFIED" + } + }, + "doc": "indicates when the websocket connection state to the signal server has changed" + }, "JsonMessageEnvelope": { "fields": { "username": { @@ -77,89 +1077,6 @@ } } }, - "IncomingMessage": { - "fields": { - "account": { - "type": "String", - "example": "\"+12024561414\"" - }, - "source": { - "type": "JsonAddress", - "version": "v1" - }, - "type": { - "type": "String" - }, - "timestamp": { - "type": "long", - "example": "1615576442475" - }, - "source_device": { - "type": "int" - }, - "server_receiver_timestamp": { - "type": "long", - "example": "1615576442475" - }, - "server_deliver_timestamp": { - "type": "long", - "example": "1615576442475" - }, - "has_legacy_message": { - "type": "boolean" - }, - "has_content": { - "type": "boolean" - }, - "unidentified_sender": { - "type": "boolean" - }, - "data_message": { - "type": "JsonDataMessage", - "version": "v1" - }, - "sync_message": { - "type": "JsonSyncMessage", - "version": "v1" - }, - "call_message": { - "type": "CallMessage", - "version": "v1" - }, - "receipt_message": { - "type": "ReceiptMessage", - "version": "v1" - }, - "typing_message": { - "type": "TypingMessage", - "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": { - "type": "String", - "doc": "One of: DISCONNECTED, CONNECTING, CONNECTED, RECONNECTING, DISCONNECTING, AUTHENTICATION_FAILED, FAILED" - }, - "socket": { - "type": "String", - "doc": "One of: UNIDENTIFIED, IDENTIFIED" - } - }, - "doc": "indicates when the websocket connection state to the signal server has changed" - }, "ClientMessageWrapper": { "fields": { "type": { @@ -185,10 +1102,17 @@ }, "doc": "Wraps all incoming messages sent to the client after a v1 subscribe request is issued" }, - "DuplicateMessageError": { + "UntrustedIdentityError": { "fields": { + "identifier": { + "type": "String" + }, "message": { "type": "String" + }, + "identity_key": { + "type": "IdentityKey", + "version": "v1" } }, "error": true @@ -198,6 +1122,9 @@ "sender": { "type": "String" }, + "timestamp": { + "type": "long" + }, "message": { "type": "String" }, @@ -213,17 +1140,13 @@ }, "error": true }, - "UntrustedIdentityError": { + "DuplicateMessageError": { "fields": { - "identifier": { - "type": "String" + "timestamp": { + "type": "long" }, "message": { "type": "String" - }, - "identity_key": { - "type": "IdentityKey", - "version": "v1" } }, "error": true @@ -250,7 +1173,7 @@ "attachments": { "list": true, "type": "JsonAttachment", - "version": "v0" + "version": "v1" }, "quote": { "type": "JsonQuote", @@ -384,6 +1307,17 @@ }, "error": true }, + "AttachmentTooLargeError": { + "fields": { + "filename": { + "type": "String" + }, + "message": { + "type": "String" + } + }, + "error": true + }, "ReactRequest": { "fields": { "username": { @@ -416,6 +1350,17 @@ }, "doc": "react to a previous message" }, + "UnregisteredUserError": { + "fields": { + "message": { + "type": "String" + }, + "e164_number": { + "type": "String" + } + }, + "error": true + }, "VersionRequest": { "fields": {} }, @@ -427,7 +1372,7 @@ }, "version": { "type": "String", - "example": "\"0.15.0-23-981b4409\"" + "example": "\"0.17.0\"" }, "branch": { "type": "String", @@ -435,7 +1380,7 @@ }, "commit": { "type": "String", - "example": "\"981b44098da8ddd748832597d5f5bde019197902\"" + "example": "\"3d482a6060b7235a18cdb8a4dc60aba61f99da74\"" } } }, @@ -520,6 +1465,10 @@ "announcements": { "type": "String", "doc": "indicates if the group is an announcements group. Only admins are allowed to send messages to announcements groups. Options are UNKNOWN, ENABLED or DISABLED" + }, + "removed": { + "type": "boolean", + "doc": "will be set to true for incoming messages to indicate the user has been removed from the group" } }, "doc": "Information about a Signal group" @@ -532,6 +1481,15 @@ }, "error": true }, + "AuthorizationFailedError": { + "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", + "error": true + }, "ApproveMembershipRequest": { "fields": { "account": { @@ -581,7 +1539,7 @@ "doc": "the latest known revision, default value (-1) forces fetch from server" } }, - "doc": "Query the server for the latest state of a known group. If no account in signald is a member of the group (anymore), an error with error_type: 'UnknownGroupError' is returned." + "doc": "Query the server for the latest state of a known group. If the account is not a member of the group, an UnknownGroupError is returned." }, "InvalidGroupStateError": { "fields": { @@ -778,9 +1736,8 @@ }, "name": { "type": "String", - "doc": "New profile name. Set to empty string for no profile name", - "example": "\"signald user\"", - "required": true + "doc": "Change the profile name", + "example": "\"signald user\"" }, "avatarFile": { "type": "String", @@ -789,15 +1746,20 @@ }, "about": { "type": "String", - "doc": "an optional about string. If unset, null or an empty string will unset profile about field" + "doc": "Change the 'about' profile field" }, "emoji": { "type": "String", - "doc": "an optional single emoji character. If unset, null or an empty string will unset profile emoji" + "doc": "Change the profile emoji" }, "mobilecoin_address": { "type": "String", - "doc": "an optional *base64-encoded* MobileCoin address to set in the profile. Note that this is not the traditional MobileCoin address encoding, which is custom. Clients are responsible for converting between MobileCoin's custom base58 on the user-facing side and base64 encoding on the signald side. If unset, null or an empty string, will empty the profile payment address" + "doc": "Change the profile payment address. Payment address must be a *base64-encoded* MobileCoin address. Note that this is not the traditional MobileCoin address encoding, which is custom. Clients are responsible for converting between MobileCoin's custom base58 on the user-facing side and base64 encoding on the signald side." + }, + "visible_badge_ids": { + "list": true, + "type": "String", + "doc": "configure visible badge IDs" } } }, @@ -929,6 +1891,11 @@ "mobilecoin_address": { "type": "String", "doc": "*base64-encoded* mobilecoin address. Note that this is not the traditional MobileCoin address encoding. Clients are responsible for converting between MobileCoin's custom base58 on the user-facing side and base64 encoding on the signald side. If unset, null or an empty string, will empty the profile payment address" + }, + "visible_badge_ids": { + "list": true, + "type": "String", + "doc": "currently unclear how these work, as they are not available in the production Signal apps" } }, "doc": "Information about a Signal user" @@ -1070,7 +2037,7 @@ "type": "String" } }, - "doc": "After a linking URI has been requested, finish_link must be called with the session_id provided with the URI. it will return information about the new account once the linking process is completed by the other device." + "doc": "After a linking URI has been requested, finish_link must be called with the session_id provided with the URI. it will return information about the new account once the linking process is completed by the other device and the new account is setup. Note that the account setup process can sometimes take some time, if rapid userfeedback is required after scanning, use wait_for_scan first, then finish setup with finish_link." }, "Account": { "fields": { @@ -1113,6 +2080,14 @@ }, "error": true }, + "ScanTimeoutError": { + "fields": { + "message": { + "type": "String" + } + }, + "error": true + }, "AddLinkedDeviceRequest": { "fields": { "account": { @@ -1366,12 +2341,6 @@ }, "RequestSyncRequest": { "fields": { - "account": { - "type": "String", - "doc": "The account to use", - "example": "\"+12024561414\"", - "required": true - }, "groups": { "type": "boolean", "doc": "request group sync (default true)" @@ -1387,6 +2356,16 @@ "blocked": { "type": "boolean", "doc": "request block list sync (default true)" + }, + "keys": { + "type": "boolean", + "doc": "request storage service keys" + }, + "account": { + "type": "String", + "doc": "The account to use", + "example": "\"+12024561414\"", + "required": true } }, "doc": "Request other devices on the account send us their group list, syncable config and contact list." @@ -1489,7 +2468,7 @@ "doc": "The device name" } }, - "doc": "set this device's name. This will show up on the mobile device on the same account under " + "doc": "set this device's name. This will show up on the mobile device on the same account under settings -> linked devices" }, "GetAllIdentities": { "fields": { @@ -1676,6 +2655,39 @@ } } }, + "IsIdentifierRegisteredRequest": { + "fields": { + "account": { + "type": "String", + "doc": "The account to use to use", + "example": "\"+12024561414\"", + "required": true + }, + "identifier": { + "type": "String", + "doc": "The UUID of an identifier to check if it is registered on Signal. This UUID is either a Phone Number Identity (PNI) or an Account Identity (ACI).", + "example": "\"aeed01f0-a234-478e-8cf7-261c283151e7\"", + "required": true + } + }, + "doc": "Determine whether an account identifier is registered on the Signal service." + }, + "BooleanMessage": { + "fields": { + "value": { + "type": "boolean" + } + }, + "doc": "A message containing a single boolean, usually as a response" + }, + "WaitForScanRequest": { + "fields": { + "session_id": { + "type": "String" + } + }, + "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" + }, "JsonDataMessage": { "fields": { "timestamp": { @@ -1722,7 +2734,7 @@ "contacts": { "list": true, "type": "SharedContact", - "version": "v0", + "version": "v1", "doc": "if the incoming message has a shared contact, the contact's information will be here" }, "previews": { @@ -1901,6 +2913,53 @@ } } }, + "JsonAttachment": { + "fields": { + "contentType": { + "type": "String" + }, + "id": { + "type": "String" + }, + "size": { + "type": "int" + }, + "storedFilename": { + "type": "String", + "doc": "when receiving, the path that file has been downloaded to" + }, + "filename": { + "type": "String", + "doc": "when sending, the path to the local file to upload" + }, + "customFilename": { + "type": "String", + "doc": "the original name of the file" + }, + "caption": { + "type": "String" + }, + "width": { + "type": "int" + }, + "height": { + "type": "int" + }, + "voiceNote": { + "type": "boolean" + }, + "key": { + "type": "String" + }, + "digest": { + "type": "String" + }, + "blurhash": { + "type": "String" + } + }, + "doc": "represents a file attached to a message. When sending, only `filename` is required." + }, "JsonQuote": { "fields": { "id": { @@ -1916,7 +2975,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, @@ -2006,7 +3065,7 @@ "emoji": { "type": "String", "doc": "the emoji to react with", - "example": "\"👍\"" + "example": "\"?\"" }, "remove": { "type": "boolean", @@ -2106,6 +3165,15 @@ }, "gv1-migration": { "type": "boolean" + }, + "sender_key": { + "type": "boolean" + }, + "announcement_group": { + "type": "boolean" + }, + "change_number": { + "type": "boolean" } } }, @@ -2195,52 +3263,41 @@ }, "doc": "A remote config (feature flag) entry." }, - "JsonAttachment": { + "SharedContact": { "fields": { - "contentType": { - "type": "String" + "name": { + "type": "SharedContactName", + "version": "v1", + "doc": "the name of the shared contact" }, - "id": { - "type": "String" + "email": { + "list": true, + "type": "SharedContactEmail", + "version": "v1", + "doc": "the email addresses of the shared contact" }, - "size": { - "type": "int" + "phone": { + "list": true, + "type": "SharedContactPhone", + "version": "v1", + "doc": "the phone numbers of the shared contact" }, - "storedFilename": { + "address": { + "list": true, + "type": "SharedContactAddress", + "version": "v1", + "doc": "the physical addresses of the shared contact" + }, + "avatar": { + "type": "SharedContactAvatar", + "version": "v1", + "doc": "the profile picture/avatar of the shared contact" + }, + "organization": { "type": "String", - "doc": "when receiving, the path that file has been downloaded to" - }, - "filename": { - "type": "String", - "doc": "when sending, the path to the local file to upload" - }, - "customFilename": { - "type": "String", - "doc": "the original name of the file" - }, - "caption": { - "type": "String" - }, - "width": { - "type": "int" - }, - "height": { - "type": "int" - }, - "voiceNote": { - "type": "boolean" - }, - "key": { - "type": "String" - }, - "digest": { - "type": "String" - }, - "blurhash": { - "type": "String" + "doc": "the organization (e.g. workplace) of the shared contact" } - }, - "doc": "represents a file attached to a message. When seding, only `filename` is required." + } }, "RemoteDelete": { "fields": { @@ -2453,923 +3510,107 @@ "type": "String" } } - } - }, - "v0": { - "JsonAccountList": { - "fields": { - "accounts": { - "list": true, - "type": "JsonAccount", - "version": "v0" - } - }, - "deprecated": true, - "removal_date": 1641027661 }, - "JsonMessageEnvelope": { - "fields": { - "username": { - "type": "String", - "example": "\"+12024561414\"" - }, - "uuid": { - "type": "String", - "example": "\"0cc10e61-d64c-4dbc-b51c-334f7dd45a4a\"" - }, - "source": { - "type": "JsonAddress", - "version": "v0" - }, - "sourceDevice": { - "type": "int" - }, - "type": { - "type": "String" - }, - "relay": { - "type": "String", - "doc": "this field is no longer available and will never be populated" - }, - "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": "v0" - }, - "syncMessage": { - "type": "JsonSyncMessage", - "version": "v0" - }, - "callMessage": { - "type": "JsonCallMessage", - "version": "v0" - }, - "receipt": { - "type": "JsonReceiptMessage", - "version": "v0" - }, - "typing": { - "type": "JsonTypingMessage", - "version": "v0" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonCallMessage": { - "fields": { - "offerMessage": { - "type": "OfferMessage", - "version": "v0" - }, - "answerMessage": { - "type": "AnswerMessage", - "version": "v0" - }, - "busyMessage": { - "type": "BusyMessage", - "version": "v0" - }, - "hangupMessage": { - "type": "HangupMessage", - "version": "v0" - }, - "iceUpdateMessages": { - "list": true, - "type": "IceUpdateMessage", - "version": "v0" - }, - "destinationDeviceId": { - "type": "int" - }, - "isMultiRing": { - "type": "boolean" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonReceiptMessage": { - "fields": { - "type": { - "type": "String" - }, - "timestamps": { - "list": true, - "type": "Long" - }, - "when": { - "type": "long" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonTypingMessage": { - "fields": { - "action": { - "type": "String" - }, - "timestamp": { - "type": "long" - }, - "groupId": { - "type": "String" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonAccount": { - "fields": { - "deviceId": { - "type": "int" - }, - "username": { - "type": "String" - }, - "filename": { - "type": "String" - }, - "uuid": { - "type": "String" - }, - "registered": { - "type": "boolean" - }, - "has_keys": { - "type": "boolean" - }, - "subscribed": { - "type": "boolean" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonAddress": { - "fields": { - "number": { - "type": "String" - }, - "uuid": { - "type": "UUID" - }, - "relay": { - "type": "String" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonDataMessage": { - "fields": { - "timestamp": { - "type": "long", - "doc": "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.", - "example": "1615576442475" - }, - "attachments": { - "list": true, - "type": "JsonAttachment", - "version": "v0", - "doc": "files attached to the incoming message" - }, - "body": { - "type": "String", - "doc": "the text body of the incoming message.", - "example": "\"hello\"" - }, - "group": { - "type": "JsonGroupInfo", - "version": "v0", - "doc": "if the incoming message was sent to a v1 group, information about that group will be here" - }, - "groupV2": { - "type": "JsonGroupV2Info", - "version": "v0", - "doc": "is the incoming message was sent to a v2 group, basic identifying information about that group will be here. For full information, use list_groups" - }, - "endSession": { - "type": "boolean" - }, - "expiresInSeconds": { - "type": "int", - "doc": "the expiry timer on the incoming message. Clients should delete records of the message within this number of seconds" - }, - "profileKeyUpdate": { - "type": "boolean" - }, - "quote": { - "type": "JsonQuote", - "version": "v0", - "doc": "if the incoming message is a quote or reply to another message, this will contain information about that message" - }, - "contacts": { - "list": true, - "type": "SharedContact", - "version": "v0", - "doc": "if the incoming message has a shared contact, the contact's information will be here" - }, - "previews": { - "list": true, - "type": "JsonPreview", - "version": "v0", - "doc": "if the incoming message has a link preview, information about that preview will be here" - }, - "sticker": { - "type": "JsonSticker", - "version": "v0", - "doc": "if the incoming message is a sticker, information about the sicker will be here" - }, - "viewOnce": { - "type": "boolean", - "doc": "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." - }, - "reaction": { - "type": "JsonReaction", - "version": "v0", - "doc": "if the message adds or removes a reaction to another message, this will indicate what change is being made" - }, - "remoteDelete": { - "type": "RemoteDelete", - "version": "v0", - "doc": "if the inbound message is deleting a previously sent message, indicates which message should be deleted" - }, - "mentions": { - "list": true, - "type": "JsonMention", - "version": "v0", - "doc": "list of mentions in the message" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonSyncMessage": { - "fields": { - "sent": { - "type": "JsonSentTranscriptMessage", - "version": "v0" - }, - "contacts": { - "type": "JsonAttachment", - "version": "v0" - }, - "contactsComplete": { - "type": "boolean" - }, - "groups": { - "type": "JsonAttachment", - "version": "v0" - }, - "blockedList": { - "type": "JsonBlockedListMessage", - "version": "v0" - }, - "request": { - "type": "String" - }, - "readMessages": { - "list": true, - "type": "JsonReadMessage", - "version": "v0" - }, - "viewOnceOpen": { - "type": "JsonViewOnceOpenMessage", - "version": "v0" - }, - "verified": { - "type": "JsonVerifiedMessage", - "version": "v0" - }, - "configuration": { - "type": "ConfigurationMessage", - "version": "v0" - }, - "stickerPackOperations": { - "list": true, - "type": "JsonStickerPackOperationMessage", - "version": "v0" - }, - "fetchType": { - "type": "String" - }, - "messageRequestResponse": { - "type": "JsonMessageRequestResponseMessage", - "version": "v0" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonAttachment": { - "fields": { - "contentType": { - "type": "String" - }, - "id": { - "type": "String" - }, - "size": { - "type": "int" - }, - "storedFilename": { - "type": "String" - }, - "filename": { - "type": "String" - }, - "customFilename": { - "type": "String" - }, - "caption": { - "type": "String" - }, - "width": { - "type": "int" - }, - "height": { - "type": "int" - }, - "voiceNote": { - "type": "boolean" - }, - "key": { - "type": "String" - }, - "digest": { - "type": "String" - }, - "blurhash": { - "type": "String" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "SharedContact": { - "fields": { - "name": { - "type": "Name", - "version": "v0" - }, - "avatar": { - "type": "Optional", - "version": "v0" - }, - "phone": { - "type": "Optional", - "version": "v0" - }, - "email": { - "type": "Optional", - "version": "v0" - }, - "address": { - "type": "Optional", - "version": "v0" - }, - "organization": { - "type": "Optional", - "version": "v0" - } - } - }, - "JsonSticker": { - "fields": { - "packID": { - "type": "String" - }, - "packKey": { - "type": "String" - }, - "stickerID": { - "type": "int" - }, - "attachment": { - "type": "JsonAttachment", - "version": "v0" - }, - "image": { - "type": "String" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "ConfigurationMessage": { - "fields": { - "readReceipts": { - "type": "Optional", - "version": "v0" - }, - "unidentifiedDeliveryIndicators": { - "type": "Optional", - "version": "v0" - }, - "typingIndicators": { - "type": "Optional", - "version": "v0" - }, - "linkPreviews": { - "type": "Optional", - "version": "v0" - } - } - }, - "JsonStickerPackOperationMessage": { - "fields": { - "packID": { - "type": "String" - }, - "packKey": { - "type": "String" - }, - "type": { - "type": "String" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "OfferMessage": { - "fields": { - "id": { - "type": "long" - }, - "sdp": { - "type": "String" - }, - "type": { - "type": "Type", - "version": "v0" - }, - "opaque": { - "type": "String" - } - } - }, - "AnswerMessage": { - "fields": { - "id": { - "type": "long" - }, - "sdp": { - "type": "String" - }, - "opaque": { - "type": "String" - } - } - }, - "BusyMessage": { - "fields": { - "id": { - "type": "long" - } - } - }, - "HangupMessage": { - "fields": { - "id": { - "type": "long" - }, - "type": { - "type": "Type", - "version": "v0" - }, - "deviceId": { - "type": "int" - }, - "legacy": { - "type": "boolean" - } - } - }, - "IceUpdateMessage": { - "fields": { - "id": { - "type": "long" - }, - "opaque": { - "type": "String" - }, - "sdp": { - "type": "String" - } - } - }, - "JsonGroupInfo": { - "fields": { - "groupId": { - "type": "String" - }, - "members": { - "list": true, - "type": "JsonAddress", - "version": "v0" - }, - "name": { - "type": "String" - }, - "type": { - "type": "String" - }, - "avatarId": { - "type": "long" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonGroupV2Info": { - "fields": { - "id": { - "type": "String", - "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" - }, - "revision": { - "type": "int", - "example": "5" - }, - "title": { - "type": "String", - "example": "\"Parkdale Run Club\"" - }, - "description": { - "type": "String" - }, - "avatar": { - "type": "String", - "doc": "path to the group's avatar on local disk, if available", - "example": "\"/var/lib/signald/avatars/group-EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" - }, - "timer": { - "type": "int", - "example": "604800" - }, - "members": { - "list": true, - "type": "JsonAddress", - "version": "v0" - }, - "pendingMembers": { - "list": true, - "type": "JsonAddress", - "version": "v0" - }, - "requestingMembers": { - "list": true, - "type": "JsonAddress", - "version": "v0" - }, - "inviteLink": { - "type": "String", - "doc": "the signal.group link, if applicable" - }, - "accessControl": { - "type": "GroupAccessControl", - "version": "v0", - "doc": "current access control settings for this group" - }, - "memberDetail": { - "list": true, - "type": "GroupMember", - "version": "v0", - "doc": "detailed member list" - }, - "pendingMemberDetail": { - "list": true, - "type": "GroupMember", - "version": "v0", - "doc": "detailed pending member list" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonQuote": { - "fields": { - "id": { - "type": "long", - "doc": "the client timestamp of the message being quoted", - "example": "1615576442475" - }, - "author": { - "type": "JsonAddress", - "version": "v0", - "doc": "the author of the message being quoted" - }, - "text": { - "type": "String", - "doc": "the body of the message being quoted", - "example": "\"hey  what's up?\"" - }, - "attachments": { - "list": true, - "type": "JsonQuotedAttachment", - "version": "v0", - "doc": "list of files attached to the quoted message" - }, - "mentions": { - "list": true, - "type": "JsonMention", - "version": "v0", - "doc": "list of mentions in the quoted message" - } - }, - "doc": "A quote is a reply to a previous message. ID is the sent time of the message being replied to", - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonPreview": { - "fields": { - "url": { - "type": "String" - }, - "title": { - "type": "String" - }, - "attachment": { - "type": "JsonAttachment", - "version": "v0" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonReaction": { - "fields": { - "emoji": { - "type": "String", - "doc": "the emoji to react with", - "example": "\"👍\"" - }, - "remove": { - "type": "boolean", - "doc": "set to true to remove the reaction. requires emoji be set to previously reacted emoji" - }, - "targetAuthor": { - "type": "JsonAddress", - "version": "v0", - "doc": "the author of the message being reacted to" - }, - "targetSentTimestamp": { - "type": "long", - "doc": "the client timestamp of the message being reacted to", - "example": "1615576442475" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "RemoteDelete": { - "fields": { - "targetSentTimestamp": { - "type": "long" - } - } - }, - "JsonMention": { - "fields": { - "uuid": { - "type": "String", - "doc": "The UUID of the account being mentioned", - "example": "\"aeed01f0-a234-478e-8cf7-261c283151e7\"" - }, - "start": { - "type": "int", - "doc": "The number of characters in that the mention starts at. Note that due to a quirk of how signald encodes JSON, if this value is 0 (for example if the first character in the message is the mention) the field won't show up.", - "example": "4" - }, - "length": { - "type": "int", - "doc": "The length of the mention represented in the message. Seems to always be 1 but included here in case that changes.", - "example": "1" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonSentTranscriptMessage": { - "fields": { - "destination": { - "type": "JsonAddress", - "version": "v0" - }, - "timestamp": { - "type": "long", - "example": "1615576442475" - }, - "expirationStartTimestamp": { - "type": "long" - }, - "message": { - "type": "JsonDataMessage", - "version": "v0" - }, - "unidentifiedStatus": { - "type": "Map" - }, - "isRecipientUpdate": { - "type": "boolean" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonBlockedListMessage": { - "fields": { - "addresses": { - "list": true, - "type": "JsonAddress", - "version": "v0" - }, - "groupIds": { - "list": true, - "type": "String" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonReadMessage": { - "fields": { - "sender": { - "type": "JsonAddress", - "version": "v0" - }, - "timestamp": { - "type": "long", - "example": "1615576442475" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonViewOnceOpenMessage": { - "fields": { - "sender": { - "type": "JsonAddress", - "version": "v0" - }, - "timestamp": { - "type": "long", - "example": "1615576442475" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonVerifiedMessage": { - "fields": { - "destination": { - "type": "JsonAddress", - "version": "v0" - }, - "identityKey": { - "type": "String" - }, - "verified": { - "type": "String" - }, - "timestamp": { - "type": "long" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonMessageRequestResponseMessage": { - "fields": { - "person": { - "type": "JsonAddress", - "version": "v0" - }, - "groupId": { - "type": "String" - }, - "type": { - "type": "String" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "JsonQuotedAttachment": { - "fields": { - "contentType": { - "type": "String" - }, - "fileName": { - "type": "String" - }, - "thumbnail": { - "type": "JsonAttachment", - "version": "v0" - } - }, - "deprecated": true, - "removal_date": 1641027661 - }, - "Name": { + "SharedContactName": { "fields": { "display": { - "type": "Optional", - "version": "v0" + "type": "String", + "doc": "the full name that should be displayed" }, "given": { - "type": "Optional", - "version": "v0" - }, - "family": { - "type": "Optional", - "version": "v0" - }, - "prefix": { - "type": "Optional", - "version": "v0" - }, - "suffix": { - "type": "Optional", - "version": "v0" + "type": "String", + "doc": "given name" }, "middle": { - "type": "Optional", - "version": "v0" + "type": "String", + "doc": "middle name" + }, + "family": { + "type": "String", + "doc": "family name (surname)" + }, + "prefix": { + "type": "String" + }, + "suffix": { + "type": "String" } } }, - "Optional": { + "SharedContactEmail": { "fields": { - "present": { + "type": { + "type": "String", + "doc": "the type of email (options: HOME, WORK, MOBILE, CUSTOM)" + }, + "value": { + "type": "String", + "doc": "the email address" + }, + "label": { + "type": "String", + "doc": "the type label when type is CUSTOM" + } + } + }, + "SharedContactPhone": { + "fields": { + "type": { + "type": "String", + "doc": "the type of phone (options: HOME, WORK, MOBILE, CUSTOM)" + }, + "value": { + "type": "String", + "doc": "the phone number" + }, + "label": { + "type": "String", + "doc": "the type label when type is CUSTOM" + } + } + }, + "SharedContactAddress": { + "fields": { + "type": { + "type": "String", + "doc": "the type of address (options: HOME, WORK, CUSTOM)" + }, + "label": { + "type": "String" + }, + "street": { + "type": "String" + }, + "pobox": { + "type": "String" + }, + "neighborhood": { + "type": "String" + }, + "city": { + "type": "String" + }, + "region": { + "type": "String" + }, + "postcode": { + "type": "String" + }, + "country": { + "type": "String" + } + } + }, + "SharedContactAvatar": { + "fields": { + "attachment": { + "type": "JsonAttachment", + "version": "v1" + }, + "is_profile": { "type": "boolean" } } - }, - "Type": { - "fields": {} - }, - "GroupAccessControl": { - "fields": { - "link": { - "type": "String", - "doc": "UNSATISFIABLE when the group link is disabled, ADMINISTRATOR when the group link is enabled but an administrator must approve new members, ANY when the group link is enabled and no approval is required", - "example": "\"ANY\"" - }, - "attributes": { - "type": "String", - "doc": "who can edit group info" - }, - "members": { - "type": "String", - "doc": "who can add members" - } - }, - "doc": "group access control settings. Options for each controlled action are: UNKNOWN, ANY, MEMBER, ADMINISTRATOR, UNSATISFIABLE and UNRECOGNIZED", - "deprecated": true, - "removal_date": 1641027661 - }, - "GroupMember": { - "fields": { - "uuid": { - "type": "String", - "example": "\"aeed01f0-a234-478e-8cf7-261c283151e7\"" - }, - "role": { - "type": "String", - "doc": "possible values are: UNKNOWN, DEFAULT, ADMINISTRATOR and UNRECOGNIZED", - "example": "\"DEFAULT\"" - }, - "joined_revision": { - "type": "int" - } - }, - "deprecated": true, - "removal_date": 1641027661 } } }, @@ -3408,6 +3649,9 @@ }, { "name": "InvalidRecipientError" + }, + { + "name": "AttachmentTooLargeError" } ] }, @@ -3442,6 +3686,9 @@ }, { "name": "RateLimitError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -3474,6 +3721,9 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" } ] }, @@ -3502,13 +3752,16 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" } ] }, "get_group": { "request": "GetGroupRequest", "response": "JsonGroupV2Info", - "doc": "Query the server for the latest state of a known group. If no account in signald is a member of the group (anymore), an error with error_type: 'UnknownGroupError' is returned.", + "doc": "Query the server for the latest state of a known group. If the account is not a member of the group, an UnknownGroupError is returned.", "errors": [ { "name": "NoSuchAccountError" @@ -3533,6 +3786,9 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" } ] }, @@ -3616,6 +3872,9 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" } ] }, @@ -3644,6 +3903,12 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -3680,6 +3945,9 @@ }, { "name": "NoSuchAccountError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -3700,6 +3968,9 @@ }, { "name": "UntrustedIdentityError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -3722,6 +3993,9 @@ }, { "name": "ProfileUnavailableError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -3797,6 +4071,9 @@ }, { "name": "UnknownGroupError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -3824,6 +4101,9 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" } ] }, @@ -3843,7 +4123,7 @@ "finish_link": { "request": "FinishLinkRequest", "response": "Account", - "doc": "After a linking URI has been requested, finish_link must be called with the session_id provided with the URI. it will return information about the new account once the linking process is completed by the other device.", + "doc": "After a linking URI has been requested, finish_link must be called with the session_id provided with the URI. it will return information about the new account once the linking process is completed by the other device and the new account is setup. Note that the account setup process can sometimes take some time, if rapid userfeedback is required after scanning, use wait_for_scan first, then finish setup with finish_link.", "errors": [ { "name": "NoSuchSessionError" @@ -3862,6 +4142,9 @@ }, { "name": "UserAlreadyExistsError" + }, + { + "name": "ScanTimeoutError" } ] }, @@ -3947,6 +4230,9 @@ }, { "name": "NoSuchAccountError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -3980,6 +4266,9 @@ }, { "name": "InvalidFingerprintError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -4031,6 +4320,9 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -4065,6 +4357,9 @@ }, { "name": "InvalidRecipientError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -4086,6 +4381,9 @@ }, { "name": "UntrustedIdentityError" + }, + { + "name": "InvalidRequestError" } ] }, @@ -4174,12 +4472,18 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "UnregisteredUserError" } ] }, "set_device_name": { "request": "SetDeviceNameRequest", - "doc": "set this device's name. This will show up on the mobile device on the same account under ", + "doc": "set this device's name. This will show up on the mobile device on the same account under settings -> linked devices", "errors": [ { "name": "InternalError" @@ -4229,6 +4533,9 @@ }, { "name": "InternalError" + }, + { + "name": "AuthorizationFailedError" } ] }, @@ -4275,6 +4582,9 @@ }, { "name": "RateLimitError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -4342,6 +4652,9 @@ }, { "name": "RateLimitError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -4389,6 +4702,12 @@ }, { "name": "InvalidRequestError" + }, + { + "name": "AuthorizationFailedError" + }, + { + "name": "UnregisteredUserError" } ] }, @@ -4411,6 +4730,40 @@ "name": "InternalError" } ] + }, + "is_identifier_registered": { + "request": "IsIdentifierRegisteredRequest", + "response": "BooleanMessage", + "doc": "Determine whether an account identifier is registered on the Signal service.", + "errors": [ + { + "name": "InternalError" + }, + { + "name": "InvalidProxyError" + }, + { + "name": "ServerNotFoundError" + }, + { + "name": "NoSuchAccountError" + } + ] + }, + "wait_for_scan": { + "request": "WaitForScanRequest", + "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", + "errors": [ + { + "name": "NoSuchSessionError" + }, + { + "name": "ScanTimeoutError" + }, + { + "name": "InternalError" + } + ] } } } diff --git a/signald/client-protocol/v1/errors.go b/signald/client-protocol/v1/errors.go index 3081b52..0b3662c 100644 --- a/signald/client-protocol/v1/errors.go +++ b/signald/client-protocol/v1/errors.go @@ -32,6 +32,20 @@ func mkerr(response client_protocol.BasicResponse) error { return err } return result + case "AttachmentTooLargeError": + result := AttachmentTooLargeError{} + err := json.Unmarshal(response.Error, &result) + if err != nil { + return err + } + return result + case "AuthorizationFailedError": + result := AuthorizationFailedError{} + err := json.Unmarshal(response.Error, &result) + if err != nil { + return err + } + return result case "CaptchaRequiredError": result := CaptchaRequiredError{} err := json.Unmarshal(response.Error, &result) @@ -207,6 +221,13 @@ func mkerr(response client_protocol.BasicResponse) error { return err } return result + case "ScanTimeoutError": + result := ScanTimeoutError{} + err := json.Unmarshal(response.Error, &result) + if err != nil { + return err + } + return result case "ServerNotFoundError": result := ServerNotFoundError{} err := json.Unmarshal(response.Error, &result) @@ -228,6 +249,13 @@ func mkerr(response client_protocol.BasicResponse) error { return err } return result + case "UnregisteredUserError": + result := UnregisteredUserError{} + err := json.Unmarshal(response.Error, &result) + if err != nil { + return err + } + return result case "UntrustedIdentityError": result := UntrustedIdentityError{} err := json.Unmarshal(response.Error, &result) @@ -272,6 +300,24 @@ func (e AccountLockedError) Error() string { return e.Message } +type AttachmentTooLargeError struct { + Filename string `json:"filename,omitempty" yaml:"filename,omitempty"` + Message string `json:"message,omitempty" yaml:"message,omitempty"` +} + +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 +type AuthorizationFailedError struct { + Message string `json:"message,omitempty" yaml:"message,omitempty"` +} + +func (e AuthorizationFailedError) Error() string { + return e.Message +} + type CaptchaRequiredError struct { Message string `json:"message,omitempty" yaml:"message,omitempty"` More string `json:"more,omitempty" yaml:"more,omitempty"` @@ -282,7 +328,8 @@ func (e CaptchaRequiredError) Error() string { } type DuplicateMessageError struct { - Message string `json:"message,omitempty" yaml:"message,omitempty"` + Message string `json:"message,omitempty" yaml:"message,omitempty"` + Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` } func (e DuplicateMessageError) Error() string { @@ -470,6 +517,7 @@ type ProtocolInvalidMessageError struct { 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 ProtocolInvalidMessageError) Error() string { @@ -484,6 +532,14 @@ func (e RateLimitError) Error() string { return e.Message } +type ScanTimeoutError struct { + Message string `json:"message,omitempty" yaml:"message,omitempty"` +} + +func (e ScanTimeoutError) Error() string { + return e.Message +} + type ServerNotFoundError struct { Message string `json:"message,omitempty" yaml:"message,omitempty"` UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty"` @@ -509,6 +565,15 @@ func (e UnknownIdentityKeyError) Error() string { return e.Message } +type UnregisteredUserError struct { + E164Number string `json:"e164_number,omitempty" yaml:"e164_number,omitempty"` + Message string `json:"message,omitempty" yaml:"message,omitempty"` +} + +func (e UnregisteredUserError) Error() string { + return e.Message +} + type UntrustedIdentityError struct { Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"` IdentityKey **IdentityKey `json:"identity_key,omitempty" yaml:"identity_key,omitempty"` diff --git a/signald/client-protocol/v1/requests.go b/signald/client-protocol/v1/requests.go index 5a38921..a59691b 100644 --- a/signald/client-protocol/v1/requests.go +++ b/signald/client-protocol/v1/requests.go @@ -217,7 +217,7 @@ func (r *RemoveServerRequest) Submit(conn *signald.Signald) (err error) { } -// Submit: After a linking URI has been requested, finish_link must be called with the session_id provided with the URI. it will return information about the new account once the linking process is completed by the other device. +// Submit: After a linking URI has been requested, finish_link must be called with the session_id provided with the URI. it will return information about the new account once the linking process is completed by the other device and the new account is setup. Note that the account setup process can sometimes take some time, if rapid userfeedback is required after scanning, use wait_for_scan first, then finish setup with finish_link. func (r *FinishLinkRequest) Submit(conn *signald.Signald) (response Account, err error) { r.Version = "v1" r.Type = "finish_link" @@ -316,7 +316,7 @@ func (r *GetAllIdentities) Submit(conn *signald.Signald) (response AllIdentityKe } -// Submit: Query the server for the latest state of a known group. If no account in signald is a member of the group (anymore), an error with error_type: 'UnknownGroupError' is returned. +// Submit: Query the server for the latest state of a known group. If the account is not a member of the group, an UnknownGroupError is returned. func (r *GetGroupRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { r.Version = "v1" r.Type = "get_group" @@ -546,6 +546,39 @@ func (r *GroupLinkInfoRequest) Submit(conn *signald.Signald) (response JsonGroup } +// Submit: Determine whether an account identifier is registered on the Signal service. +func (r *IsIdentifierRegisteredRequest) Submit(conn *signald.Signald) (response BooleanMessage, err error) { + r.Version = "v1" + r.Type = "is_identifier_registered" + if r.ID == "" { + r.ID = signald.GenerateID() + } + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + 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: Join a group using the a signal.group URL. Note that you must have a profile name set to join groups. func (r *JoinGroupRequest) Submit(conn *signald.Signald) (response JsonGroupJoinInfo, err error) { r.Version = "v1" @@ -1048,7 +1081,7 @@ func (r *SendPaymentRequest) Submit(conn *signald.Signald) (response SendRespons } -// Submit: set this device's name. This will show up on the mobile device on the same account under +// 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" r.Type = "set_device_name" @@ -1391,3 +1424,29 @@ func (r *VersionRequest) Submit(conn *signald.Signald) (response JsonVersionMess return response, nil } + +// Submit: 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 +func (r *WaitForScanRequest) Submit(conn *signald.Signald) (err error) { + r.Version = "v1" + r.Type = "wait_for_scan" + if r.ID == "" { + r.ID = signald.GenerateID() + } + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = mkerr(rawResponse) + return + } + + return err + +} diff --git a/signald/client-protocol/v1/structs.go b/signald/client-protocol/v1/structs.go index 38d30ea..e589b8e 100644 --- a/signald/client-protocol/v1/structs.go +++ b/signald/client-protocol/v1/structs.go @@ -62,6 +62,11 @@ type ApproveMembershipRequest struct { Members []*JsonAddress `json:"members,omitempty" yaml:"members,omitempty"` // list of requesting members to approve } +// BooleanMessage: A message containing a single boolean, usually as a response +type BooleanMessage struct { + Value bool `json:"value,omitempty" yaml:"value,omitempty"` +} + type BusyMessage struct { ID int64 `json:"id,omitempty" yaml:"id,omitempty"` } @@ -77,9 +82,12 @@ type CallMessage struct { } type Capabilities struct { - Gv1Migration bool `json:"gv1-migration,omitempty" yaml:"gv1-migration,omitempty"` - Gv2 bool `json:"gv2,omitempty" yaml:"gv2,omitempty"` - Storage bool `json:"storage,omitempty" yaml:"storage,omitempty"` + 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"` + SenderKey bool `json:"sender_key,omitempty" yaml:"sender_key,omitempty"` + Storage bool `json:"storage,omitempty" yaml:"storage,omitempty"` } // ClientMessageWrapper: Wraps all incoming messages sent to the client after a v1 subscribe request is issued @@ -115,7 +123,7 @@ type DeviceInfo struct { Name string `json:"name,omitempty" yaml:"name,omitempty"` } -// FinishLinkRequest: After a linking URI has been requested, finish_link must be called with the session_id provided with the URI. it will return information about the new account once the linking process is completed by the other device. +// FinishLinkRequest: After a linking URI has been requested, finish_link must be called with the session_id provided with the URI. it will return information about the new account once the linking process is completed by the other device and the new account is setup. Note that the account setup process can sometimes take some time, if rapid userfeedback is required after scanning, use wait_for_scan first, then finish setup with finish_link. type FinishLinkRequest struct { Request DeviceName string `json:"device_name,omitempty" yaml:"device_name,omitempty"` @@ -134,7 +142,7 @@ type GetAllIdentities struct { Account string `json:"account,omitempty" yaml:"account,omitempty"` // The account to interact with } -// GetGroupRequest: Query the server for the latest state of a known group. If no account in signald is a member of the group (anymore), an error with error_type: 'UnknownGroupError' is returned. +// GetGroupRequest: Query the server for the latest state of a known group. If the account is not a member of the group, an UnknownGroupError is returned. type GetGroupRequest struct { Request Account string `json:"account,omitempty" yaml:"account,omitempty"` // The account to interact with @@ -243,6 +251,13 @@ type IncomingMessage struct { UnidentifiedSender bool `json:"unidentified_sender,omitempty" yaml:"unidentified_sender,omitempty"` } +// IsIdentifierRegisteredRequest: Determine whether an account identifier is registered on the Signal service. +type IsIdentifierRegisteredRequest struct { + Request + Account string `json:"account,omitempty" yaml:"account,omitempty"` // The account to use to use + Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"` // The UUID of an identifier to check if it is registered on Signal. This UUID is either a Phone Number Identity (PNI) or an Account Identity (ACI). +} + // JoinGroupRequest: Join a group using the a signal.group URL. Note that you must have a profile name set to join groups. type JoinGroupRequest struct { Request @@ -256,7 +271,7 @@ type JsonAddress struct { UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty"` // A UUID, the unique identifier for a particular Signal account. } -// JsonAttachment: represents a file attached to a message. When seding, only `filename` is required. +// JsonAttachment: represents a file attached to a message. When sending, only `filename` is required. type JsonAttachment struct { Blurhash string `json:"blurhash,omitempty" yaml:"blurhash,omitempty"` Caption string `json:"caption,omitempty" yaml:"caption,omitempty"` @@ -279,24 +294,24 @@ 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 []*v0.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 + 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. } // JsonGroupInfo: information about a legacy group @@ -330,6 +345,7 @@ type JsonGroupV2Info struct { 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"` @@ -522,6 +538,7 @@ type Profile struct { MobilecoinAddress string `json:"mobilecoin_address,omitempty" yaml:"mobilecoin_address,omitempty"` // *base64-encoded* mobilecoin address. Note that this is not the traditional MobileCoin address encoding. Clients are responsible for converting between MobileCoin's custom base58 on the user-facing side and base64 encoding on the signald side. If unset, null or an empty string, will empty the profile payment address Name string `json:"name,omitempty" yaml:"name,omitempty"` // The user's name from local contact names if available, or if not in contact list their Signal profile name ProfileName string `json:"profile_name,omitempty" yaml:"profile_name,omitempty"` // The user's Signal profile name + VisibleBadgeIds []string `json:"visible_badge_ids,omitempty" yaml:"visible_badge_ids,omitempty"` // currently unclear how these work, as they are not available in the production Signal apps } type ProfileList struct { @@ -612,6 +629,7 @@ type RequestSyncRequest struct { Configuration bool `json:"configuration,omitempty" yaml:"configuration,omitempty"` // request configuration sync (default true) Contacts bool `json:"contacts,omitempty" yaml:"contacts,omitempty"` // request contact sync (default true) Groups bool `json:"groups,omitempty" yaml:"groups,omitempty"` // request group sync (default true) + Keys bool `json:"keys,omitempty" yaml:"keys,omitempty"` // request storage service keys } // ResetSessionRequest: reset a session with a particular user @@ -640,16 +658,16 @@ type SendPaymentRequest struct { type SendRequest struct { Request - Attachments []*v0.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"` - MessageBody string `json:"messageBody,omitempty" yaml:"messageBody,omitempty"` - Previews []*JsonPreview `json:"previews,omitempty" yaml:"previews,omitempty"` - Quote *JsonQuote `json:"quote,omitempty" yaml:"quote,omitempty"` - RecipientAddress *JsonAddress `json:"recipientAddress,omitempty" yaml:"recipientAddress,omitempty"` - RecipientGroupID string `json:"recipientGroupId,omitempty" yaml:"recipientGroupId,omitempty"` - Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` - Username string `json:"username,omitempty" yaml:"username,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"` + MessageBody string `json:"messageBody,omitempty" yaml:"messageBody,omitempty"` + Previews []*JsonPreview `json:"previews,omitempty" yaml:"previews,omitempty"` + Quote *JsonQuote `json:"quote,omitempty" yaml:"quote,omitempty"` + RecipientAddress *JsonAddress `json:"recipientAddress,omitempty" yaml:"recipientAddress,omitempty"` + RecipientGroupID string `json:"recipientGroupId,omitempty" yaml:"recipientGroupId,omitempty"` + Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` + Username string `json:"username,omitempty" yaml:"username,omitempty"` } type SendResponse struct { @@ -692,7 +710,7 @@ type ServerList struct { Servers []*Server `json:"servers,omitempty" yaml:"servers,omitempty"` } -// SetDeviceNameRequest: set this device's name. This will show up on the mobile device on the same account under +// SetDeviceNameRequest: set this device's name. This will show up on the mobile device on the same account under settings -> linked devices type SetDeviceNameRequest struct { Request Account string `json:"account,omitempty" yaml:"account,omitempty"` // The account to set the device name of @@ -710,12 +728,60 @@ type SetExpirationRequest struct { type SetProfile struct { Request - About string `json:"about,omitempty" yaml:"about,omitempty"` // an optional about string. If unset, null or an empty string will unset profile about field - Account string `json:"account,omitempty" yaml:"account,omitempty"` // The phone number of the account to use - AvatarFile string `json:"avatarFile,omitempty" yaml:"avatarFile,omitempty"` // Path to new profile avatar file. If unset or null, unset the profile avatar - Emoji string `json:"emoji,omitempty" yaml:"emoji,omitempty"` // an optional single emoji character. If unset, null or an empty string will unset profile emoji - MobilecoinAddress string `json:"mobilecoin_address,omitempty" yaml:"mobilecoin_address,omitempty"` // an optional *base64-encoded* MobileCoin address to set in the profile. Note that this is not the traditional MobileCoin address encoding, which is custom. Clients are responsible for converting between MobileCoin's custom base58 on the user-facing side and base64 encoding on the signald side. If unset, null or an empty string, will empty the profile payment address - Name string `json:"name,omitempty" yaml:"name,omitempty"` // New profile name. Set to empty string for no profile name + About string `json:"about,omitempty" yaml:"about,omitempty"` // Change the 'about' profile field + Account string `json:"account,omitempty" yaml:"account,omitempty"` // The phone number of the account to use + AvatarFile string `json:"avatarFile,omitempty" yaml:"avatarFile,omitempty"` // Path to new profile avatar file. If unset or null, unset the profile avatar + Emoji string `json:"emoji,omitempty" yaml:"emoji,omitempty"` // Change the profile emoji + MobilecoinAddress string `json:"mobilecoin_address,omitempty" yaml:"mobilecoin_address,omitempty"` // Change the profile payment address. Payment address must be a *base64-encoded* MobileCoin address. Note that this is not the traditional MobileCoin address encoding, which is custom. Clients are responsible for converting between MobileCoin's custom base58 on the user-facing side and base64 encoding on the signald side. + Name string `json:"name,omitempty" yaml:"name,omitempty"` // Change the profile name + VisibleBadgeIds []string `json:"visible_badge_ids,omitempty" yaml:"visible_badge_ids,omitempty"` // configure visible badge IDs +} + +type SharedContact struct { + Address []*SharedContactAddress `json:"address,omitempty" yaml:"address,omitempty"` // the physical addresses of the shared contact + Avatar *SharedContactAvatar `json:"avatar,omitempty" yaml:"avatar,omitempty"` // the profile picture/avatar of the shared contact + Email []*SharedContactEmail `json:"email,omitempty" yaml:"email,omitempty"` // the email addresses of the shared contact + Name *SharedContactName `json:"name,omitempty" yaml:"name,omitempty"` // the name of the shared contact + Organization string `json:"organization,omitempty" yaml:"organization,omitempty"` // the organization (e.g. workplace) of the shared contact + Phone []*SharedContactPhone `json:"phone,omitempty" yaml:"phone,omitempty"` // the phone numbers of the shared contact +} + +type SharedContactAddress struct { + City string `json:"city,omitempty" yaml:"city,omitempty"` + Country string `json:"country,omitempty" yaml:"country,omitempty"` + Label string `json:"label,omitempty" yaml:"label,omitempty"` + Neighborhood string `json:"neighborhood,omitempty" yaml:"neighborhood,omitempty"` + Pobox string `json:"pobox,omitempty" yaml:"pobox,omitempty"` + Postcode string `json:"postcode,omitempty" yaml:"postcode,omitempty"` + Region string `json:"region,omitempty" yaml:"region,omitempty"` + Street string `json:"street,omitempty" yaml:"street,omitempty"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` // the type of address (options: HOME, WORK, CUSTOM) +} + +type SharedContactAvatar struct { + Attachment *JsonAttachment `json:"attachment,omitempty" yaml:"attachment,omitempty"` + IsProfile bool `json:"is_profile,omitempty" yaml:"is_profile,omitempty"` +} + +type SharedContactEmail struct { + Label string `json:"label,omitempty" yaml:"label,omitempty"` // the type label when type is CUSTOM + Type string `json:"type,omitempty" yaml:"type,omitempty"` // the type of email (options: HOME, WORK, MOBILE, CUSTOM) + Value string `json:"value,omitempty" yaml:"value,omitempty"` // the email address +} + +type SharedContactName struct { + Display string `json:"display,omitempty" yaml:"display,omitempty"` // the full name that should be displayed + Family string `json:"family,omitempty" yaml:"family,omitempty"` // family name (surname) + Given string `json:"given,omitempty" yaml:"given,omitempty"` // given name + Middle string `json:"middle,omitempty" yaml:"middle,omitempty"` // middle name + Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` + Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"` +} + +type SharedContactPhone struct { + Label string `json:"label,omitempty" yaml:"label,omitempty"` // the type label when type is CUSTOM + Type string `json:"type,omitempty" yaml:"type,omitempty"` // the type of phone (options: HOME, WORK, MOBILE, CUSTOM) + Value string `json:"value,omitempty" yaml:"value,omitempty"` // the phone number } type SubmitChallengeRequest struct { @@ -801,6 +867,12 @@ type VersionRequest struct { Request } +// WaitForScanRequest: 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 +type WaitForScanRequest struct { + Request + SessionId string `json:"session_id,omitempty" yaml:"session_id,omitempty"` +} + // WebSocketConnectionState: indicates when the websocket connection state to the signal server has changed type WebSocketConnectionState struct { Socket string `json:"socket,omitempty" yaml:"socket,omitempty"` // One of: UNIDENTIFIED, IDENTIFIED