diff --git a/Makefile b/Makefile index 1333aeb..badfcd2 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ signaldctl: signald/client-protocol go build -o signaldctl ./cmd/signaldctl protocol.json: - echo '{"type": "protocol", "version": "v1alpha1"}' | nc -q0 -U /var/run/signald/signald.sock | jq 'select(.type == "protocol").data' > protocol.json + signald --dump-protocol > protocol.json signald/client-protocol: protocol.json tools/generator/* go run ./tools/generator < protocol.json diff --git a/protocol.json b/protocol.json index 60d9868..2ec06d6 100644 --- a/protocol.json +++ b/protocol.json @@ -1,945 +1 @@ -{ - "doc_version": "v1alpha1", - "version": { - "name": "signald", - "version": "0.10.0+git2020-12-06rcea5cb72.51", - "branch": "refactor-client-request-handling", - "commit": "cea5cb720355afdca460aa30257bdd1317494c7b" - }, - "info": "This document describes objects that may be used when communicating with signald. If this document lacks something you need to generate a client, please open an issue (https://gitlab.com/thefinn93/signald/-/issues/new). This is an initial proposal for the format, and I expect to change it before finalizing it. If it workswell, I hope to slowly move things out of the legacy request types.", - "types": { - "v1": { - "JsonMessageEnvelope": { - "fields": { - "username": { - "type": "String" - }, - "uuid": { - "type": "String" - }, - "source": { - "type": "JsonAddress", - "version": "v1" - }, - "sourceDevice": { - "type": "int" - }, - "type": { - "type": "String" - }, - "relay": { - "type": "String" - }, - "timestamp": { - "type": "long" - }, - "timestampISO": { - "type": "String" - }, - "serverTimestamp": { - "type": "long" - }, - "serverDeliveredTimestamp": { - "type": "long" - }, - "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" - } - } - }, - "SendRequest": { - "fields": { - "username": { - "type": "String" - }, - "recipientAddress": { - "type": "JsonAddress", - "version": "v1" - }, - "recipientGroupId": { - "type": "String" - }, - "messageBody": { - "type": "String" - }, - "attachments": { - "list": true, - "type": "JsonAttachment", - "version": "v0" - }, - "quote": { - "type": "JsonQuote", - "version": "v1" - }, - "timestamp": { - "type": "Long" - }, - "mentions": { - "list": true, - "type": "JsonMention", - "version": "v1" - } - } - }, - "SendResponse": { - "fields": { - "results": { - "list": true, - "type": "JsonSendMessageResult", - "version": "v1" - }, - "timestamp": { - "type": "long" - } - } - }, - "ReactRequest": { - "fields": { - "username": { - "type": "String" - }, - "recipientAddress": { - "type": "JsonAddress", - "version": "v1" - }, - "recipientGroupId": { - "type": "String" - }, - "reaction": { - "type": "JsonReaction", - "version": "v1" - }, - "timestamp": { - "type": "long" - } - }, - "doc": "react to a previous message" - }, - "VersionRequest": { - "fields": {} - }, - "JsonVersionMessage": { - "fields": { - "name": { - "type": "String" - }, - "version": { - "type": "String" - }, - "branch": { - "type": "String" - }, - "commit": { - "type": "String" - } - } - }, - "JsonAddress": { - "fields": { - "number": { - "type": "String", - "doc": "An e164 phone number, starting with +. Currently the only available user-facing Signal identifier." - }, - "uuid": { - "type": "UUID", - "doc": "A UUID, the unique identifier for a particular Signal account." - }, - "relay": { - "type": "String" - } - } - }, - "JsonDataMessage": { - "fields": { - "timestamp": { - "type": "long", - "doc": "the (unix) 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." - }, - "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." - }, - "group": { - "type": "JsonGroupInfo", - "version": "v1", - "doc": "if the incoming message was sent to a v1 group, information about that group will be here" - }, - "groupV2": { - "type": "JsonGroupV2Info", - "version": "v1", - "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": "v1", - "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": "v1", - "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" - } - } - }, - "JsonSyncMessage": { - "fields": { - "sent": { - "type": "JsonSentTranscriptMessage", - "version": "v1" - }, - "contacts": { - "type": "JsonAttachment", - "version": "v0" - }, - "contactsComplete": { - "type": "boolean" - }, - "groups": { - "type": "JsonAttachment", - "version": "v0" - }, - "blockedList": { - "type": "JsonBlockedListMessage", - "version": "v1" - }, - "request": { - "type": "String" - }, - "readMessages": { - "list": true, - "type": "JsonReadMessage", - "version": "v1" - }, - "viewOnceOpen": { - "type": "JsonViewOnceOpenMessage", - "version": "v1" - }, - "verified": { - "type": "JsonVerifiedMessage", - "version": "v1" - }, - "configuration": { - "type": "ConfigurationMessage", - "version": "v0" - }, - "stickerPackOperations": { - "list": true, - "type": "JsonStickerPackOperationMessage", - "version": "v0" - }, - "fetchType": { - "type": "String" - }, - "messageRequestResponse": { - "type": "JsonMessageRequestResponseMessage", - "version": "v1" - } - } - }, - "JsonQuote": { - "fields": { - "id": { - "type": "long" - }, - "author": { - "type": "JsonAddress", - "version": "v1" - }, - "text": { - "type": "String" - }, - "attachments": { - "list": true, - "type": "JsonQuotedAttachment", - "version": "v0" - }, - "mentions": { - "list": true, - "type": "Mention", - "version": "v0" - } - }, - "doc": "A quote is a reply to a previous message. ID is the sent time of the message being replied to" - }, - "JsonMention": { - "fields": { - "uuid": { - "type": "String" - }, - "start": { - "type": "int" - }, - "length": { - "type": "int" - } - } - }, - "JsonSendMessageResult": { - "fields": { - "address": { - "type": "JsonAddress", - "version": "v1" - }, - "success": { - "type": "Success", - "version": "v0" - }, - "networkFailure": { - "type": "boolean" - }, - "unregisteredFailure": { - "type": "boolean" - }, - "identityFailure": { - "type": "String" - } - } - }, - "JsonReaction": { - "fields": { - "emoji": { - "type": "String", - "doc": "the emoji to react with" - }, - "remove": { - "type": "boolean", - "doc": "set to true to remove the reaction. requires emoji be set to previously reacted emoji" - }, - "targetAuthor": { - "type": "JsonAddress", - "version": "v1", - "doc": "the author of the message being reacted to" - }, - "targetSentTimestamp": { - "type": "long", - "doc": "the client timestamp of the message being reacted to" - } - } - }, - "JsonGroupInfo": { - "fields": { - "groupId": { - "type": "String" - }, - "members": { - "list": true, - "type": "JsonAddress", - "version": "v1" - }, - "name": { - "type": "String" - }, - "type": { - "type": "String" - }, - "avatarId": { - "type": "long" - } - } - }, - "JsonGroupV2Info": { - "fields": { - "id": { - "type": "String" - }, - "masterKey": { - "type": "String" - }, - "revision": { - "type": "int" - }, - "title": { - "type": "String" - }, - "timer": { - "type": "int" - }, - "members": { - "list": true, - "type": "JsonAddress", - "version": "v1" - }, - "pendingMembers": { - "list": true, - "type": "JsonAddress", - "version": "v1" - }, - "requestingMembers": { - "list": true, - "type": "JsonAddress", - "version": "v1" - }, - "inviteLinkPassword": { - "type": "String" - } - } - }, - "JsonSentTranscriptMessage": { - "fields": { - "destination": { - "type": "JsonAddress", - "version": "v1" - }, - "timestamp": { - "type": "long" - }, - "expirationStartTimestamp": { - "type": "long" - }, - "message": { - "type": "JsonDataMessage", - "version": "v1" - }, - "unidentifiedStatus": { - "type": "Map" - }, - "isRecipientUpdate": { - "type": "boolean" - } - } - }, - "JsonBlockedListMessage": { - "fields": { - "addresses": { - "list": true, - "type": "JsonAddress", - "version": "v1" - }, - "groupIds": { - "list": true, - "type": "String" - } - } - }, - "JsonReadMessage": { - "fields": { - "sender": { - "type": "JsonAddress", - "version": "v1" - }, - "timestamp": { - "type": "long" - } - } - }, - "JsonViewOnceOpenMessage": { - "fields": { - "sender": { - "type": "JsonAddress", - "version": "v1" - }, - "timestamp": { - "type": "long" - } - } - }, - "JsonVerifiedMessage": { - "fields": { - "destination": { - "type": "JsonAddress", - "version": "v1" - }, - "identityKey": { - "type": "String" - }, - "verified": { - "type": "String" - }, - "timestamp": { - "type": "long" - } - } - }, - "JsonMessageRequestResponseMessage": { - "fields": { - "person": { - "type": "JsonAddress", - "version": "v1" - }, - "groupId": { - "type": "String" - }, - "type": { - "type": "String" - } - } - } - }, - "v0": { - "JsonAccountList": { - "fields": { - "accounts": { - "list": true, - "type": "JsonAccount", - "version": "v0" - } - } - }, - "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" - } - } - }, - "JsonReceiptMessage": { - "fields": { - "type": { - "type": "String" - }, - "timestamps": { - "list": true, - "type": "Long" - }, - "when": { - "type": "long" - } - } - }, - "JsonTypingMessage": { - "fields": { - "action": { - "type": "String" - }, - "timestamp": { - "type": "long" - }, - "groupId": { - "type": "String" - } - } - }, - "JsonAccount": { - "fields": { - "deviceId": { - "type": "int" - }, - "username": { - "type": "String" - }, - "filename": { - "type": "String" - }, - "uuid": { - "type": "String" - }, - "registered": { - "type": "boolean" - }, - "has_keys": { - "type": "boolean" - }, - "subscribed": { - "type": "boolean" - } - } - }, - "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" - } - } - }, - "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" - } - } - }, - "JsonSticker": { - "fields": { - "packID": { - "type": "String" - }, - "packKey": { - "type": "String" - }, - "stickerID": { - "type": "int" - }, - "attachment": { - "type": "JsonAttachment", - "version": "v0" - } - } - }, - "RemoteDelete": { - "fields": { - "targetSentTimestamp": { - "type": "long" - } - } - }, - "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" - } - } - }, - "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" - } - } - }, - "Mention": { - "fields": { - "uuid": { - "type": "UUID" - }, - "start": { - "type": "int" - }, - "length": { - "type": "int" - } - } - }, - "Success": { - "fields": { - "unidentified": { - "type": "boolean" - }, - "needsSync": { - "type": "boolean" - }, - "duration": { - "type": "long" - } - } - }, - "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": {} - } - }, - "v1alpha1": { - "ProtocolRequest": { - "fields": {} - } - } - }, - "actions": { - "v1": { - "send": { - "request": "SendRequest", - "response": "SendResponse" - }, - "react": { - "request": "ReactRequest", - "response": "SendResponse", - "doc": "react to a previous message" - }, - "version": { - "request": "VersionRequest", - "response": "JsonVersionMessage" - } - }, - "v1alpha1": { - "protocol": { - "request": "ProtocolRequest" - } - } - } -} +{"doc_version":"v1","version":{"name":"signald","version":"0.11.1+git2021-01-26r209ba8f1.66","branch":"main","commit":"209ba8f1b1f472ca1fb2b0f1790f7cd1fad0658b"},"info":"This document describes objects that may be used when communicating with signald.","types":{"v1":{"JsonMessageEnvelope":{"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":"1611698481651"},"timestampISO":{"type":"String"},"serverTimestamp":{"type":"long"},"serverDeliveredTimestamp":{"type":"long","example":"161169848165180"},"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"}}},"SendRequest":{"fields":{"username":{"type":"String","example":"\"+12024561414\""},"recipientAddress":{"type":"JsonAddress","version":"v1"},"recipientGroupId":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"messageBody":{"type":"String","example":"\"hello\""},"attachments":{"list":true,"type":"JsonAttachment","version":"v0"},"quote":{"type":"JsonQuote","version":"v1"},"timestamp":{"type":"Long"},"mentions":{"list":true,"type":"JsonMention","version":"v1"}}},"SendResponse":{"fields":{"results":{"list":true,"type":"JsonSendMessageResult","version":"v1"},"timestamp":{"type":"long","example":"1611698481651"}}},"ReactRequest":{"fields":{"username":{"type":"String","example":"\"+12024561414\""},"recipientAddress":{"type":"JsonAddress","version":"v1"},"recipientGroupId":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"reaction":{"type":"JsonReaction","version":"v1"},"timestamp":{"type":"long"}},"doc":"react to a previous message"},"VersionRequest":{"fields":{}},"JsonVersionMessage":{"fields":{"name":{"type":"String","example":"\"signald\""},"version":{"type":"String","example":"\"0.11.1+git2021-01-26r209ba8f1.66\""},"branch":{"type":"String","example":"\"main\""},"commit":{"type":"String","example":"\"209ba8f1b1f472ca1fb2b0f1790f7cd1fad0658b\""}}},"AcceptInvitationRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"groupID":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""}},"doc":"Accept a v2 group invitation. Note that you must have a profile name set to join groups."},"JsonGroupV2Info":{"fields":{"id":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"revision":{"type":"int","example":"5"},"title":{"type":"String","example":"\"Parkdale Run Club\""},"timer":{"type":"int","example":"604800"},"members":{"list":true,"type":"JsonAddress","version":"v1"},"pendingMembers":{"list":true,"type":"JsonAddress","version":"v1"},"requestingMembers":{"list":true,"type":"JsonAddress","version":"v1"},"inviteLink":{"type":"String"}}},"ApproveMembershipRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"groupID":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"members":{"list":true,"type":"JsonAddress","version":"v1","doc":"list of requesting members to approve"}},"doc":"approve a request to join a group"},"GetGroupRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"groupID":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"revision":{"type":"int","doc":"the latest known revision, default value (-1) forces fetch from server"}},"doc":"Query the server for the latest state of a known group"},"GetLinkedDevicesRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""}},"doc":"list all linked devices on a Signal account"},"LinkedDevices":{"fields":{"devices":{"list":true,"type":"DeviceInfo","version":"v0"}}},"JoinGroupRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"uri":{"type":"String","doc":"The signal.group URL","example":"\"https://signal.group/#CjQKINH_GZhXhfifTcnBkaKTNRxW-hHKnGSq-cJNyPVqHRp8EhDUB7zjKNEl0NaULhsqJCX3\""}},"doc":"Join a group using the a signal.group URL. Note that you must have a profile name set to join groups."},"JsonGroupJoinInfo":{"fields":{"groupID":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"title":{"type":"String","example":"\"Parkdale Run Club\""},"memberCount":{"type":"int","example":"3"},"addFromInviteLink":{"type":"int"},"revision":{"type":"int","example":"5"},"pendingAdminApproval":{"type":"boolean"}}},"ProtocolRequest":{"fields":{}},"RemoveLinkedDeviceRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"deviceId":{"type":"long","doc":"the ID of the device to unlink","example":"3"}},"doc":"Remove a linked device from the Signal account. Only allowed when the local device id is 1"},"UpdateGroupRequest":{"fields":{"account":{"type":"String","doc":"The identifier of the account to interact with","example":"\"+12024561414\""},"groupID":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"title":{"type":"String","example":"\"Parkdale Run Club\""},"avatar":{"type":"String","example":"\"/tmp/image.jpg\""},"addMembers":{"list":true,"type":"JsonAddress","version":"v1"},"removeMembers":{"list":true,"type":"JsonAddress","version":"v1"}},"doc":"modify a group"},"GroupInfo":{"fields":{"v1":{"type":"JsonGroupInfo","version":"v1"},"v2":{"type":"JsonGroupV2Info","version":"v1"}},"doc":"A generic type that is used when the group version is not known"},"SetProfile":{"fields":{"account":{"type":"String","doc":"The phone number of the account to use","example":"\"+12024561414\""},"name":{"type":"String","doc":"New profile name. Set to empty string for no profile name","example":"\"signald user\""},"avatarFile":{"type":"String","doc":"Path to new profile avatar file, if the avatar should be updated","example":"\"/tmp/image.jpg\""}}},"ResolveAddressRequest":{"fields":{"account":{"type":"String","doc":"The signal account to use","example":"\"+12024561414\""},"partial":{"type":"JsonAddress","version":"v1","doc":"The partial address, missing fields"}},"doc":"Resolve a partial JsonAddress with only a number or UUID to one with both. Anywhere that signald accepts a JsonAddress will except a partial, this is a convenience function for client authors, mostly because signald doesn't resolve all the partials it returns"},"JsonAddress":{"fields":{"number":{"type":"String","doc":"An e164 phone number, starting with +. Currently the only available user-facing Signal identifier.","example":"\"+13215551234\""},"uuid":{"type":"UUID","doc":"A UUID, the unique identifier for a particular Signal account."},"relay":{"type":"String"}}},"MarkReadRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"to":{"type":"JsonAddress","version":"v1","doc":"The address that sent the message being marked as read"},"timestamps":{"list":true,"type":"Long","doc":"List of messages to mark as read","example":"1611698481651"}}},"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":"1611698481651"},"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":"v1","doc":"if the incoming message was sent to a v1 group, information about that group will be here"},"groupV2":{"type":"JsonGroupV2Info","version":"v1","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":"v1","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":"v1","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":"v1","doc":"list of mentions in the message"}}},"JsonSyncMessage":{"fields":{"sent":{"type":"JsonSentTranscriptMessage","version":"v1"},"contacts":{"type":"JsonAttachment","version":"v0"},"contactsComplete":{"type":"boolean"},"groups":{"type":"JsonAttachment","version":"v0"},"blockedList":{"type":"JsonBlockedListMessage","version":"v1"},"request":{"type":"String"},"readMessages":{"list":true,"type":"JsonReadMessage","version":"v1"},"viewOnceOpen":{"type":"JsonViewOnceOpenMessage","version":"v1"},"verified":{"type":"JsonVerifiedMessage","version":"v1"},"configuration":{"type":"ConfigurationMessage","version":"v0"},"stickerPackOperations":{"list":true,"type":"JsonStickerPackOperationMessage","version":"v0"},"fetchType":{"type":"String"},"messageRequestResponse":{"type":"JsonMessageRequestResponseMessage","version":"v1"}}},"JsonQuote":{"fields":{"id":{"type":"long","doc":"the client timestamp of the message being quoted","example":"1611698481651"},"author":{"type":"JsonAddress","version":"v1","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":"v1","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"},"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"}}},"JsonSendMessageResult":{"fields":{"address":{"type":"JsonAddress","version":"v1"},"success":{"type":"Success","version":"v0"},"networkFailure":{"type":"boolean","example":"false"},"unregisteredFailure":{"type":"boolean","example":"false"},"identityFailure":{"type":"String"}}},"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":"v1","doc":"the author of the message being reacted to"},"targetSentTimestamp":{"type":"long","doc":"the client timestamp of the message being reacted to","example":"1611698481651"}}},"JsonGroupInfo":{"fields":{"groupId":{"type":"String"},"members":{"list":true,"type":"JsonAddress","version":"v1"},"name":{"type":"String"},"type":{"type":"String"},"avatarId":{"type":"long"}}},"JsonSentTranscriptMessage":{"fields":{"destination":{"type":"JsonAddress","version":"v1"},"timestamp":{"type":"long","example":"1611698481651"},"expirationStartTimestamp":{"type":"long"},"message":{"type":"JsonDataMessage","version":"v1"},"unidentifiedStatus":{"type":"Map"},"isRecipientUpdate":{"type":"boolean"}}},"JsonBlockedListMessage":{"fields":{"addresses":{"list":true,"type":"JsonAddress","version":"v1"},"groupIds":{"list":true,"type":"String"}}},"JsonReadMessage":{"fields":{"sender":{"type":"JsonAddress","version":"v1"},"timestamp":{"type":"long","example":"1611698481651"}}},"JsonViewOnceOpenMessage":{"fields":{"sender":{"type":"JsonAddress","version":"v1"},"timestamp":{"type":"long","example":"1611698481651"}}},"JsonVerifiedMessage":{"fields":{"destination":{"type":"JsonAddress","version":"v1"},"identityKey":{"type":"String"},"verified":{"type":"String"},"timestamp":{"type":"long"}}},"JsonMessageRequestResponseMessage":{"fields":{"person":{"type":"JsonAddress","version":"v1"},"groupId":{"type":"String"},"type":{"type":"String"}}}},"v0":{"JsonAccountList":{"fields":{"accounts":{"list":true,"type":"JsonAccount","version":"v0"}}},"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"}}},"JsonReceiptMessage":{"fields":{"type":{"type":"String"},"timestamps":{"list":true,"type":"Long"},"when":{"type":"long"}}},"JsonTypingMessage":{"fields":{"action":{"type":"String"},"timestamp":{"type":"long"},"groupId":{"type":"String"}}},"JsonAccount":{"fields":{"deviceId":{"type":"int"},"username":{"type":"String"},"filename":{"type":"String"},"uuid":{"type":"String"},"registered":{"type":"boolean"},"has_keys":{"type":"boolean"},"subscribed":{"type":"boolean"}}},"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"}}},"DeviceInfo":{"fields":{"id":{"type":"long"},"name":{"type":"String"},"created":{"type":"long"},"lastSeen":{"type":"long"}}},"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"}}},"JsonSticker":{"fields":{"packID":{"type":"String"},"packKey":{"type":"String"},"stickerID":{"type":"int"},"attachment":{"type":"JsonAttachment","version":"v0"}}},"RemoteDelete":{"fields":{"targetSentTimestamp":{"type":"long"}}},"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"}}},"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"}}},"Success":{"fields":{"unidentified":{"type":"boolean"},"needsSync":{"type":"boolean"},"duration":{"type":"long"}}},"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":{}}},"v1alpha1":{"ProtocolRequest":{"fields":{},"deprecated":true},"GetLinkedDevicesRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""}},"doc":"list all linked devices on a Signal account","deprecated":true},"LinkedDevices":{"fields":{"devices":{"list":true,"type":"DeviceInfo","version":"v0"}}},"RemoveLinkedDeviceRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"deviceId":{"type":"long","doc":"the ID of the device to unlink","example":"3"}},"doc":"Remove a linked device from the Signal account. Only allowed when the local device id is 1"},"JoinGroupRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"uri":{"type":"String","doc":"The signal.group URL","example":"\"https://signal.group/#CjQKINH_GZhXhfifTcnBkaKTNRxW-hHKnGSq-cJNyPVqHRp8EhDUB7zjKNEl0NaULhsqJCX3\""}},"doc":"Join a group using the a signal.group URL. Note that you must have a profile name set to join groups.","deprecated":true},"JsonGroupJoinInfo":{"fields":{"groupID":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"title":{"type":"String","example":"\"Parkdale Run Club\""},"memberCount":{"type":"int","example":"3"},"addFromInviteLink":{"type":"int"},"revision":{"type":"int","example":"5"},"pendingAdminApproval":{"type":"boolean"}}},"UpdateGroupRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"groupID":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"title":{"type":"String","example":"\"Parkdale Run Club\""},"addMembers":{"list":true,"type":"JsonAddress","version":"v1"},"removeMembers":{"list":true,"type":"JsonAddress","version":"v1"}},"doc":"modify a group. only v2 groups for now","deprecated":true},"JsonGroupV2Info":{"fields":{"id":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"revision":{"type":"int","example":"5"},"title":{"type":"String","example":"\"Parkdale Run Club\""},"timer":{"type":"int","example":"604800"},"members":{"list":true,"type":"JsonAddress","version":"v1"},"pendingMembers":{"list":true,"type":"JsonAddress","version":"v1"},"requestingMembers":{"list":true,"type":"JsonAddress","version":"v1"},"inviteLink":{"type":"String"}}},"AcceptInvitationRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"groupID":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""}},"doc":"Accept a v2 group invitation. Note that you must have a profile name set to join groups.","deprecated":true},"ApproveMembershipRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"groupID":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"members":{"list":true,"type":"JsonAddress","version":"v1","doc":"list of requesting members to approve"}},"doc":"approve a request to join a group","deprecated":true},"GetGroupRequest":{"fields":{"account":{"type":"String","doc":"The account to interact with","example":"\"+12024561414\""},"groupID":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"revision":{"type":"int","doc":"the latest known revision, default value (-1) forces fetch from server"}},"doc":"Query the server for the latest state of a known group","deprecated":true}},"v1alpha2":{"UpdateGroupRequest":{"fields":{"account":{"type":"String","doc":"The identifier of the account to interact with","example":"\"+12024561414\""},"groupID":{"type":"String","example":"\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\""},"title":{"type":"String","example":"\"Parkdale Run Club\""},"avatar":{"type":"String","example":"\"/tmp/image.jpg\""},"addMembers":{"list":true,"type":"JsonAddress","version":"v1"},"removeMembers":{"list":true,"type":"JsonAddress","version":"v1"}},"doc":"modify a group","deprecated":true},"GroupInfo":{"fields":{"v1":{"type":"JsonGroupInfo","version":"v1"},"v2":{"type":"JsonGroupV2Info","version":"v1"}},"doc":"A generic type that is used when the group version is not known"}}},"actions":{"v1":{"send":{"request":"SendRequest","response":"SendResponse"},"react":{"request":"ReactRequest","response":"SendResponse","doc":"react to a previous message"},"version":{"request":"VersionRequest","response":"JsonVersionMessage"},"accept_invitation":{"request":"AcceptInvitationRequest","response":"JsonGroupV2Info","doc":"Accept a v2 group invitation. Note that you must have a profile name set to join groups."},"approve_membership":{"request":"ApproveMembershipRequest","response":"JsonGroupV2Info","doc":"approve a request to join a group"},"get_group":{"request":"GetGroupRequest","response":"JsonGroupV2Info","doc":"Query the server for the latest state of a known group"},"get_linked_devices":{"request":"GetLinkedDevicesRequest","response":"LinkedDevices","doc":"list all linked devices on a Signal account"},"join_group":{"request":"JoinGroupRequest","response":"JsonGroupJoinInfo","doc":"Join a group using the a signal.group URL. Note that you must have a profile name set to join groups."},"protocol":{"request":"ProtocolRequest"},"remove_linked_device":{"request":"RemoveLinkedDeviceRequest","doc":"Remove a linked device from the Signal account. Only allowed when the local device id is 1"},"update_group":{"request":"UpdateGroupRequest","response":"GroupInfo","doc":"modify a group"},"set_profile":{"request":"SetProfile"},"resolve_address":{"request":"ResolveAddressRequest","response":"JsonAddress","doc":"Resolve a partial JsonAddress with only a number or UUID to one with both. Anywhere that signald accepts a JsonAddress will except a partial, this is a convenience function for client authors, mostly because signald doesn't resolve all the partials it returns"},"mark_read":{"request":"MarkReadRequest"}},"v1alpha1":{"protocol":{"request":"ProtocolRequest","deprecated":true},"get_linked_devices":{"request":"GetLinkedDevicesRequest","response":"LinkedDevices","doc":"list all linked devices on a Signal account","deprecated":true},"remove_linked_device":{"request":"RemoveLinkedDeviceRequest","doc":"Remove a linked device from the Signal account. Only allowed when the local device id is 1"},"join_group":{"request":"JoinGroupRequest","response":"JsonGroupJoinInfo","doc":"Join a group using the a signal.group URL. Note that you must have a profile name set to join groups.","deprecated":true},"update_group":{"request":"UpdateGroupRequest","response":"JsonGroupV2Info","doc":"modify a group. only v2 groups for now","deprecated":true},"accept_invitation":{"request":"AcceptInvitationRequest","response":"JsonGroupV2Info","doc":"Accept a v2 group invitation. Note that you must have a profile name set to join groups.","deprecated":true},"approve_membership":{"request":"ApproveMembershipRequest","response":"JsonGroupV2Info","doc":"approve a request to join a group","deprecated":true},"get_group":{"request":"GetGroupRequest","response":"JsonGroupV2Info","doc":"Query the server for the latest state of a known group","deprecated":true}},"v1alpha2":{"update_group":{"request":"UpdateGroupRequest","response":"GroupInfo","doc":"modify a group","deprecated":true}}}} diff --git a/signald/client-protocol/v0/structs.go b/signald/client-protocol/v0/structs.go index 288339c..aab9077 100644 --- a/signald/client-protocol/v0/structs.go +++ b/signald/client-protocol/v0/structs.go @@ -25,6 +25,13 @@ type ConfigurationMessage struct { UnidentifiedDeliveryIndicators *Optional `json:"unidentifiedDeliveryIndicators,omitempty"` } +type DeviceInfo struct { + Created int64 `json:"created,omitempty"` + ID int64 `json:"id,omitempty"` + LastSeen int64 `json:"lastSeen,omitempty"` + Name string `json:"name,omitempty"` +} + type HangupMessage struct { DeviceId int32 `json:"deviceId,omitempty"` ID int64 `json:"id,omitempty"` @@ -115,12 +122,6 @@ type JsonTypingMessage struct { Timestamp int64 `json:"timestamp,omitempty"` } -type Mention struct { - Length int32 `json:"length,omitempty"` - Start int32 `json:"start,omitempty"` - UUID string `json:"uuid,omitempty"` -} - type Name struct { Display *Optional `json:"display,omitempty"` Family *Optional `json:"family,omitempty"` diff --git a/signald/client-protocol/v1/requests.go b/signald/client-protocol/v1/requests.go index f648cc9..065ba8c 100644 --- a/signald/client-protocol/v1/requests.go +++ b/signald/client-protocol/v1/requests.go @@ -11,6 +11,193 @@ import ( "gitlab.com/signald/signald-go/signald" ) +// Submit: Accept a v2 group invitation. Note that you must have a profile name set to join groups. +func (r *AcceptInvitationRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { + r.Version = "v1" + r.Type = "accept_invitation" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + +// Submit: approve a request to join a group +func (r *ApproveMembershipRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { + r.Version = "v1" + r.Type = "approve_membership" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + +// Submit: Query the server for the latest state of a known group +func (r *GetGroupRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { + r.Version = "v1" + r.Type = "get_group" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + +// Submit: list all linked devices on a Signal account +func (r *GetLinkedDevicesRequest) Submit(conn *signald.Signald) (response LinkedDevices, err error) { + r.Version = "v1" + r.Type = "get_linked_devices" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + 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" + r.Type = "join_group" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + +func (r *MarkReadRequest) Submit(conn *signald.Signald) error { + r.Version = "v1" + r.Type = "mark_read" + if r.ID == "" { + r.ID = generateID() + } + + return conn.RawRequest(r) + +} + +func (r *ProtocolRequest) Submit(conn *signald.Signald) error { + r.Version = "v1" + r.Type = "protocol" + if r.ID == "" { + r.ID = generateID() + } + + return conn.RawRequest(r) + +} + // Submit: react to a previous message func (r *ReactRequest) Submit(conn *signald.Signald) (response SendResponse, err error) { r.Version = "v1" @@ -44,6 +231,51 @@ func (r *ReactRequest) Submit(conn *signald.Signald) (response SendResponse, err } +// Submit: Remove a linked device from the Signal account. Only allowed when the local device id is 1 +func (r *RemoveLinkedDeviceRequest) Submit(conn *signald.Signald) error { + r.Version = "v1" + r.Type = "remove_linked_device" + if r.ID == "" { + r.ID = generateID() + } + + return conn.RawRequest(r) + +} + +// Submit: Resolve a partial JsonAddress with only a number or UUID to one with both. Anywhere that signald accepts a JsonAddress will except a partial, this is a convenience function for client authors, mostly because signald doesn't resolve all the partials it returns +func (r *ResolveAddressRequest) Submit(conn *signald.Signald) (response JsonAddress, err error) { + r.Version = "v1" + r.Type = "resolve_address" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + func (r *SendRequest) Submit(conn *signald.Signald) (response SendResponse, err error) { r.Version = "v1" r.Type = "send" @@ -76,6 +308,50 @@ func (r *SendRequest) Submit(conn *signald.Signald) (response SendResponse, err } +func (r *SetProfile) Submit(conn *signald.Signald) error { + r.Version = "v1" + r.Type = "set_profile" + if r.ID == "" { + r.ID = generateID() + } + + return conn.RawRequest(r) + +} + +// Submit: modify a group +func (r *UpdateGroupRequest) Submit(conn *signald.Signald) (response GroupInfo, err error) { + r.Version = "v1" + r.Type = "update_group" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + func (r *VersionRequest) Submit(conn *signald.Signald) (response JsonVersionMessage, err error) { r.Version = "v1" r.Type = "version" diff --git a/signald/client-protocol/v1/structs.go b/signald/client-protocol/v1/structs.go index 88659fd..dbff7f9 100644 --- a/signald/client-protocol/v1/structs.go +++ b/signald/client-protocol/v1/structs.go @@ -12,6 +12,48 @@ type Request struct { Type string `json:"type"` } +// AcceptInvitationRequest: Accept a v2 group invitation. Note that you must have a profile name set to join groups. +type AcceptInvitationRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with + GroupID string `json:"groupID,omitempty"` +} + +// ApproveMembershipRequest: approve a request to join a group +type ApproveMembershipRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with + GroupID string `json:"groupID,omitempty"` + Members []*JsonAddress `json:"members,omitempty"` // list of requesting members to approve +} + +// GetGroupRequest: Query the server for the latest state of a known group +type GetGroupRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with + GroupID string `json:"groupID,omitempty"` + Revision int32 `json:"revision,omitempty"` // the latest known revision, default value (-1) forces fetch from server +} + +// GetLinkedDevicesRequest: list all linked devices on a Signal account +type GetLinkedDevicesRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with +} + +// GroupInfo: A generic type that is used when the group version is not known +type GroupInfo struct { + V1 *JsonGroupInfo `json:"v1,omitempty"` + V2 *JsonGroupV2Info `json:"v2,omitempty"` +} + +// 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 + Account string `json:"account,omitempty"` // The account to interact with + Uri string `json:"uri,omitempty"` // The signal.group URL +} + type JsonAddress struct { Number string `json:"number,omitempty"` // An e164 phone number, starting with +. Currently the only available user-facing Signal identifier. Relay string `json:"relay,omitempty"` @@ -31,13 +73,14 @@ type JsonDataMessage struct { ExpiresInSeconds int32 `json:"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"` // if the incoming message was sent to a v1 group, information about that group will be here GroupV2 *JsonGroupV2Info `json:"groupV2,omitempty"` // 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 + Mentions []*JsonMention `json:"mentions,omitempty"` // list of mentions in the message Previews []*v0.JsonPreview `json:"previews,omitempty"` // if the incoming message has a link preview, information about that preview will be here ProfileKeyUpdate bool `json:"profileKeyUpdate,omitempty"` Quote *JsonQuote `json:"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"` // if the message adds or removes a reaction to another message, this will indicate what change is being made RemoteDelete *v0.RemoteDelete `json:"remoteDelete,omitempty"` // if the inbound message is deleting a previously sent message, indicates which message should be deleted Sticker *v0.JsonSticker `json:"sticker,omitempty"` // if the incoming message is a sticker, information about the sicker will be here - Timestamp int64 `json:"timestamp,omitempty"` // the (unix) 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. + Timestamp int64 `json:"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"` // 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. } @@ -49,22 +92,30 @@ type JsonGroupInfo struct { Type string `json:"type,omitempty"` } +type JsonGroupJoinInfo struct { + AddFromInviteLink int32 `json:"addFromInviteLink,omitempty"` + GroupID string `json:"groupID,omitempty"` + MemberCount int32 `json:"memberCount,omitempty"` + PendingAdminApproval bool `json:"pendingAdminApproval,omitempty"` + Revision int32 `json:"revision,omitempty"` + Title string `json:"title,omitempty"` +} + type JsonGroupV2Info struct { - ID string `json:"id,omitempty"` - InviteLinkPassword string `json:"inviteLinkPassword,omitempty"` - MasterKey string `json:"masterKey,omitempty"` - Members []*JsonAddress `json:"members,omitempty"` - PendingMembers []*JsonAddress `json:"pendingMembers,omitempty"` - RequestingMembers []*JsonAddress `json:"requestingMembers,omitempty"` - Revision int32 `json:"revision,omitempty"` - Timer int32 `json:"timer,omitempty"` - Title string `json:"title,omitempty"` + ID string `json:"id,omitempty"` + InviteLink string `json:"inviteLink,omitempty"` + Members []*JsonAddress `json:"members,omitempty"` + PendingMembers []*JsonAddress `json:"pendingMembers,omitempty"` + RequestingMembers []*JsonAddress `json:"requestingMembers,omitempty"` + Revision int32 `json:"revision,omitempty"` + Timer int32 `json:"timer,omitempty"` + Title string `json:"title,omitempty"` } type JsonMention struct { - Length int32 `json:"length,omitempty"` - Start int32 `json:"start,omitempty"` - UUID string `json:"uuid,omitempty"` + Length int32 `json:"length,omitempty"` // The length of the mention represented in the message. Seems to always be 1 but included here in case that changes. + Start int32 `json:"start,omitempty"` // 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. + UUID string `json:"uuid,omitempty"` // The UUID of the account being mentioned } type JsonMessageEnvelope struct { @@ -96,11 +147,11 @@ type JsonMessageRequestResponseMessage struct { // JsonQuote: A quote is a reply to a previous message. ID is the sent time of the message being replied to type JsonQuote struct { - Attachments []*v0.JsonQuotedAttachment `json:"attachments,omitempty"` - Author *JsonAddress `json:"author,omitempty"` - ID int64 `json:"id,omitempty"` - Mentions []*v0.Mention `json:"mentions,omitempty"` - Text string `json:"text,omitempty"` + Attachments []*v0.JsonQuotedAttachment `json:"attachments,omitempty"` // list of files attached to the quoted message + Author *JsonAddress `json:"author,omitempty"` // the author of the message being quoted + ID int64 `json:"id,omitempty"` // the client timestamp of the message being quoted + Mentions []*JsonMention `json:"mentions,omitempty"` // list of mentions in the quoted message + Text string `json:"text,omitempty"` // the body of the message being quoted } type JsonReaction struct { @@ -167,6 +218,21 @@ type JsonViewOnceOpenMessage struct { Timestamp int64 `json:"timestamp,omitempty"` } +type LinkedDevices struct { + Devices []*v0.DeviceInfo `json:"devices,omitempty"` +} + +type MarkReadRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with + Timestamps []int64 `json:"timestamps,omitempty"` // List of messages to mark as read + To *JsonAddress `json:"to,omitempty"` // The address that sent the message being marked as read +} + +type ProtocolRequest struct { + Request +} + // ReactRequest: react to a previous message type ReactRequest struct { Request @@ -177,6 +243,20 @@ type ReactRequest struct { Username string `json:"username,omitempty"` } +// RemoveLinkedDeviceRequest: Remove a linked device from the Signal account. Only allowed when the local device id is 1 +type RemoveLinkedDeviceRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with + DeviceId int64 `json:"deviceId,omitempty"` // the ID of the device to unlink +} + +// ResolveAddressRequest: Resolve a partial JsonAddress with only a number or UUID to one with both. Anywhere that signald accepts a JsonAddress will except a partial, this is a convenience function for client authors, mostly because signald doesn't resolve all the partials it returns +type ResolveAddressRequest struct { + Request + Account string `json:"account,omitempty"` // The signal account to use + Partial *JsonAddress `json:"partial,omitempty"` // The partial address, missing fields +} + type SendRequest struct { Request Attachments []*v0.JsonAttachment `json:"attachments,omitempty"` @@ -194,6 +274,24 @@ type SendResponse struct { Timestamp int64 `json:"timestamp,omitempty"` } +type SetProfile struct { + Request + Account string `json:"account,omitempty"` // The phone number of the account to use + AvatarFile string `json:"avatarFile,omitempty"` // Path to new profile avatar file, if the avatar should be updated + Name string `json:"name,omitempty"` // New profile name. Set to empty string for no profile name +} + +// UpdateGroupRequest: modify a group +type UpdateGroupRequest struct { + Request + Account string `json:"account,omitempty"` // The identifier of the account to interact with + AddMembers []*JsonAddress `json:"addMembers,omitempty"` + Avatar string `json:"avatar,omitempty"` + GroupID string `json:"groupID,omitempty"` + RemoveMembers []*JsonAddress `json:"removeMembers,omitempty"` + Title string `json:"title,omitempty"` +} + type VersionRequest struct { Request } diff --git a/signald/client-protocol/v1alpha1/requests.go b/signald/client-protocol/v1alpha1/requests.go index b2bd38a..4f8e6f5 100644 --- a/signald/client-protocol/v1alpha1/requests.go +++ b/signald/client-protocol/v1alpha1/requests.go @@ -3,11 +3,179 @@ package v1alpha1 // DO NOT EDIT: this file is automatically generated by ./tools/generator in this repo import ( + "encoding/json" + "fmt" + "log" "math/rand" "gitlab.com/signald/signald-go/signald" ) +// Submit: Accept a v2 group invitation. Note that you must have a profile name set to join groups. +func (r *AcceptInvitationRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { + r.Version = "v1alpha1" + r.Type = "accept_invitation" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + +// Submit: approve a request to join a group +func (r *ApproveMembershipRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { + r.Version = "v1alpha1" + r.Type = "approve_membership" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + +// Submit: Query the server for the latest state of a known group +func (r *GetGroupRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { + r.Version = "v1alpha1" + r.Type = "get_group" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + +// Submit: list all linked devices on a Signal account +func (r *GetLinkedDevicesRequest) Submit(conn *signald.Signald) (response LinkedDevices, err error) { + r.Version = "v1alpha1" + r.Type = "get_linked_devices" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + 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 = "v1alpha1" + r.Type = "join_group" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + func (r *ProtocolRequest) Submit(conn *signald.Signald) error { r.Version = "v1alpha1" r.Type = "protocol" @@ -19,6 +187,51 @@ func (r *ProtocolRequest) Submit(conn *signald.Signald) error { } +// Submit: Remove a linked device from the Signal account. Only allowed when the local device id is 1 +func (r *RemoveLinkedDeviceRequest) Submit(conn *signald.Signald) error { + r.Version = "v1alpha1" + r.Type = "remove_linked_device" + if r.ID == "" { + r.ID = generateID() + } + + return conn.RawRequest(r) + +} + +// Submit: modify a group. only v2 groups for now +func (r *UpdateGroupRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { + r.Version = "v1alpha1" + r.Type = "update_group" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + const idsize = 10 var charset = []rune("abcdefghijklmnopqrstuvwxyz0123456789") diff --git a/signald/client-protocol/v1alpha1/structs.go b/signald/client-protocol/v1alpha1/structs.go index 2fdf815..497e89d 100644 --- a/signald/client-protocol/v1alpha1/structs.go +++ b/signald/client-protocol/v1alpha1/structs.go @@ -2,12 +2,94 @@ package v1alpha1 // DO NOT EDIT: this file is automatically generated by ./tools/generator in this repo +import ( + "gitlab.com/signald/signald-go/signald/client-protocol/v0" + "gitlab.com/signald/signald-go/signald/client-protocol/v1" +) + type Request struct { ID string `json:"id"` Version string `json:"version"` Type string `json:"type"` } +// AcceptInvitationRequest: Accept a v2 group invitation. Note that you must have a profile name set to join groups. +type AcceptInvitationRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with + GroupID string `json:"groupID,omitempty"` +} + +// ApproveMembershipRequest: approve a request to join a group +type ApproveMembershipRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with + GroupID string `json:"groupID,omitempty"` + Members []*v1.JsonAddress `json:"members,omitempty"` // list of requesting members to approve +} + +// GetGroupRequest: Query the server for the latest state of a known group +type GetGroupRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with + GroupID string `json:"groupID,omitempty"` + Revision int32 `json:"revision,omitempty"` // the latest known revision, default value (-1) forces fetch from server +} + +// GetLinkedDevicesRequest: list all linked devices on a Signal account +type GetLinkedDevicesRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with +} + +// 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 + Account string `json:"account,omitempty"` // The account to interact with + Uri string `json:"uri,omitempty"` // The signal.group URL +} + +type JsonGroupJoinInfo struct { + AddFromInviteLink int32 `json:"addFromInviteLink,omitempty"` + GroupID string `json:"groupID,omitempty"` + MemberCount int32 `json:"memberCount,omitempty"` + PendingAdminApproval bool `json:"pendingAdminApproval,omitempty"` + Revision int32 `json:"revision,omitempty"` + Title string `json:"title,omitempty"` +} + +type JsonGroupV2Info struct { + ID string `json:"id,omitempty"` + InviteLink string `json:"inviteLink,omitempty"` + Members []*v1.JsonAddress `json:"members,omitempty"` + PendingMembers []*v1.JsonAddress `json:"pendingMembers,omitempty"` + RequestingMembers []*v1.JsonAddress `json:"requestingMembers,omitempty"` + Revision int32 `json:"revision,omitempty"` + Timer int32 `json:"timer,omitempty"` + Title string `json:"title,omitempty"` +} + +type LinkedDevices struct { + Devices []*v0.DeviceInfo `json:"devices,omitempty"` +} + type ProtocolRequest struct { Request } + +// RemoveLinkedDeviceRequest: Remove a linked device from the Signal account. Only allowed when the local device id is 1 +type RemoveLinkedDeviceRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with + DeviceId int64 `json:"deviceId,omitempty"` // the ID of the device to unlink +} + +// UpdateGroupRequest: modify a group. only v2 groups for now +type UpdateGroupRequest struct { + Request + Account string `json:"account,omitempty"` // The account to interact with + AddMembers []*v1.JsonAddress `json:"addMembers,omitempty"` + GroupID string `json:"groupID,omitempty"` + RemoveMembers []*v1.JsonAddress `json:"removeMembers,omitempty"` + Title string `json:"title,omitempty"` +} diff --git a/signald/client-protocol/v1alpha2/requests.go b/signald/client-protocol/v1alpha2/requests.go new file mode 100644 index 0000000..8f237a5 --- /dev/null +++ b/signald/client-protocol/v1alpha2/requests.go @@ -0,0 +1,57 @@ +package v1alpha2 + +// DO NOT EDIT: this file is automatically generated by ./tools/generator in this repo + +import ( + "encoding/json" + "fmt" + "log" + "math/rand" + + "gitlab.com/signald/signald-go/signald" +) + +// Submit: modify a group +func (r *UpdateGroupRequest) Submit(conn *signald.Signald) (response GroupInfo, err error) { + r.Version = "v1alpha2" + r.Type = "update_group" + if r.ID == "" { + r.ID = generateID() + } + + err = conn.RawRequest(r) + if err != nil { + log.Println("signald-go: error submitting request to signald") + return response, err + } + + responseChannel := conn.GetResponseListener(r.ID) + defer conn.CloseResponseListener(r.ID) + + rawResponse := <-responseChannel + if rawResponse.Error != nil { + err = fmt.Errorf("signald error: %s", string(rawResponse.Error)) + 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 response, err + } + + return response, nil + +} + +const idsize = 10 + +var charset = []rune("abcdefghijklmnopqrstuvwxyz0123456789") + +func generateID() string { + id := make([]rune, idsize) + for i := range id { + id[i] = charset[rand.Intn(len(charset))] + } + return string(id) +} diff --git a/signald/client-protocol/v1alpha2/structs.go b/signald/client-protocol/v1alpha2/structs.go new file mode 100644 index 0000000..69ae9bd --- /dev/null +++ b/signald/client-protocol/v1alpha2/structs.go @@ -0,0 +1,30 @@ +package v1alpha2 + +// DO NOT EDIT: this file is automatically generated by ./tools/generator in this repo + +import ( + "gitlab.com/signald/signald-go/signald/client-protocol/v1" +) + +type Request struct { + ID string `json:"id"` + Version string `json:"version"` + Type string `json:"type"` +} + +// GroupInfo: A generic type that is used when the group version is not known +type GroupInfo struct { + V1 *v1.JsonGroupInfo `json:"v1,omitempty"` + V2 *v1.JsonGroupV2Info `json:"v2,omitempty"` +} + +// UpdateGroupRequest: modify a group +type UpdateGroupRequest struct { + Request + Account string `json:"account,omitempty"` // The identifier of the account to interact with + AddMembers []*v1.JsonAddress `json:"addMembers,omitempty"` + Avatar string `json:"avatar,omitempty"` + GroupID string `json:"groupID,omitempty"` + RemoveMembers []*v1.JsonAddress `json:"removeMembers,omitempty"` + Title string `json:"title,omitempty"` +}