diff --git a/protocol.json b/protocol.json index e967a99..b6a679c 100644 --- a/protocol.json +++ b/protocol.json @@ -2,9 +2,9 @@ "doc_version": "v1", "version": { "name": "signald", - "version": "0.11.1+git2021-01-26r209ba8f1.66", + "version": "0.11.1+git2021-01-30r350d7b78.75", "branch": "main", - "commit": "209ba8f1b1f472ca1fb2b0f1790f7cd1fad0658b" + "commit": "350d7b78e6d8579866da07b9dc91035663e0b073" }, "info": "This document describes objects that may be used when communicating with signald.", "types": { @@ -34,7 +34,7 @@ }, "timestamp": { "type": "long", - "example": "1611698481651" + "example": "1611966817808" }, "timestampISO": { "type": "String" @@ -44,7 +44,7 @@ }, "serverDeliveredTimestamp": { "type": "long", - "example": "161169848165180" + "example": "161196681780880" }, "hasLegacyMessage": { "type": "boolean" @@ -81,7 +81,8 @@ "fields": { "username": { "type": "String", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "recipientAddress": { "type": "JsonAddress", @@ -123,7 +124,7 @@ }, "timestamp": { "type": "long", - "example": "1611698481651" + "example": "1611966817808" } } }, @@ -131,7 +132,8 @@ "fields": { "username": { "type": "String", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "recipientAddress": { "type": "JsonAddress", @@ -143,7 +145,8 @@ }, "reaction": { "type": "JsonReaction", - "version": "v1" + "version": "v1", + "required": true }, "timestamp": { "type": "long" @@ -162,7 +165,7 @@ }, "version": { "type": "String", - "example": "\"0.11.1+git2021-01-26r209ba8f1.66\"" + "example": "\"0.11.1+git2021-01-30r350d7b78.75\"" }, "branch": { "type": "String", @@ -170,7 +173,7 @@ }, "commit": { "type": "String", - "example": "\"209ba8f1b1f472ca1fb2b0f1790f7cd1fad0658b\"" + "example": "\"350d7b78e6d8579866da07b9dc91035663e0b073\"" } } }, @@ -179,11 +182,13 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "groupID": { "type": "String", - "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true } }, "doc": "Accept a v2 group invitation. Note that you must have a profile name set to join groups." @@ -231,17 +236,20 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "groupID": { "type": "String", - "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true }, "members": { "list": true, "type": "JsonAddress", "version": "v1", - "doc": "list of requesting members to approve" + "doc": "list of requesting members to approve", + "required": true } }, "doc": "approve a request to join a group" @@ -251,11 +259,13 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "groupID": { "type": "String", - "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true }, "revision": { "type": "int", @@ -269,7 +279,8 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true } }, "doc": "list all linked devices on a Signal account" @@ -288,12 +299,14 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "uri": { "type": "String", "doc": "The signal.group URL", - "example": "\"https://signal.group/#CjQKINH_GZhXhfifTcnBkaKTNRxW-hHKnGSq-cJNyPVqHRp8EhDUB7zjKNEl0NaULhsqJCX3\"" + "example": "\"https://signal.group/#CjQKINH_GZhXhfifTcnBkaKTNRxW-hHKnGSq-cJNyPVqHRp8EhDUB7zjKNEl0NaULhsqJCX3\"", + "required": true } }, "doc": "Join a group using the a signal.group URL. Note that you must have a profile name set to join groups." @@ -332,12 +345,14 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "deviceId": { "type": "long", "doc": "the ID of the device to unlink", - "example": "3" + "example": "3", + "required": true } }, "doc": "Remove a linked device from the Signal account. Only allowed when the local device id is 1" @@ -347,11 +362,13 @@ "account": { "type": "String", "doc": "The identifier of the account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "groupID": { "type": "String", - "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true }, "title": { "type": "String", @@ -392,12 +409,14 @@ "account": { "type": "String", "doc": "The phone number of the account to use", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "name": { "type": "String", "doc": "New profile name. Set to empty string for no profile name", - "example": "\"signald user\"" + "example": "\"signald user\"", + "required": true }, "avatarFile": { "type": "String", @@ -411,12 +430,14 @@ "account": { "type": "String", "doc": "The signal account to use", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "partial": { "type": "JsonAddress", "version": "v1", - "doc": "The partial address, missing fields" + "doc": "The partial address, missing fields", + "required": true } }, "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" @@ -442,27 +463,79 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "to": { "type": "JsonAddress", "version": "v1", - "doc": "The address that sent the message being marked as read" + "doc": "The address that sent the message being marked as read", + "required": true }, "timestamps": { "list": true, "type": "Long", "doc": "List of messages to mark as read", - "example": "1611698481651" + "example": "1611966817808", + "required": true } } }, + "GetProfileRequest": { + "fields": { + "account": { + "type": "String", + "doc": "the signald account to use", + "required": true + }, + "address": { + "type": "JsonAddress", + "version": "v1", + "doc": "the address to look up", + "required": true + } + }, + "doc": "Get all information available about a user" + }, + "Profile": { + "fields": { + "name": { + "type": "String", + "doc": "The user's name from local contact names if available, or if not in contact list their Signal profile name" + }, + "avatar": { + "type": "String" + }, + "address": { + "type": "JsonAddress", + "version": "v1" + }, + "capabilities": { + "type": "Capabilities", + "version": "v1" + }, + "color": { + "type": "String" + }, + "profile_name": { + "type": "String", + "doc": "The user's Signal profile name" + }, + "inbox_position": { + "type": "Integer" + }, + "expiration_time": { + "type": "int" + } + }, + "doc": "Information about a Signal user" + }, "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" + "example": "1611966817808" }, "attachments": { "list": true, @@ -599,7 +672,7 @@ "id": { "type": "long", "doc": "the client timestamp of the message being quoted", - "example": "1611698481651" + "example": "1611966817808" }, "author": { "type": "JsonAddress", @@ -687,7 +760,7 @@ "targetSentTimestamp": { "type": "long", "doc": "the client timestamp of the message being reacted to", - "example": "1611698481651" + "example": "1611966817808" } } }, @@ -712,6 +785,19 @@ } } }, + "Capabilities": { + "fields": { + "gv2": { + "type": "boolean" + }, + "storage": { + "type": "boolean" + }, + "gv1-migration": { + "type": "boolean" + } + } + }, "JsonSentTranscriptMessage": { "fields": { "destination": { @@ -720,7 +806,7 @@ }, "timestamp": { "type": "long", - "example": "1611698481651" + "example": "1611966817808" }, "expirationStartTimestamp": { "type": "long" @@ -758,7 +844,7 @@ }, "timestamp": { "type": "long", - "example": "1611698481651" + "example": "1611966817808" } } }, @@ -770,7 +856,7 @@ }, "timestamp": { "type": "long", - "example": "1611698481651" + "example": "1611966817808" } } }, @@ -1200,7 +1286,8 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true } }, "doc": "list all linked devices on a Signal account", @@ -1220,12 +1307,14 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "deviceId": { "type": "long", "doc": "the ID of the device to unlink", - "example": "3" + "example": "3", + "required": true } }, "doc": "Remove a linked device from the Signal account. Only allowed when the local device id is 1" @@ -1235,12 +1324,14 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "uri": { "type": "String", "doc": "The signal.group URL", - "example": "\"https://signal.group/#CjQKINH_GZhXhfifTcnBkaKTNRxW-hHKnGSq-cJNyPVqHRp8EhDUB7zjKNEl0NaULhsqJCX3\"" + "example": "\"https://signal.group/#CjQKINH_GZhXhfifTcnBkaKTNRxW-hHKnGSq-cJNyPVqHRp8EhDUB7zjKNEl0NaULhsqJCX3\"", + "required": true } }, "doc": "Join a group using the a signal.group URL. Note that you must have a profile name set to join groups.", @@ -1277,11 +1368,13 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "groupID": { "type": "String", - "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true }, "title": { "type": "String", @@ -1344,11 +1437,13 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "groupID": { "type": "String", - "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true } }, "doc": "Accept a v2 group invitation. Note that you must have a profile name set to join groups.", @@ -1359,17 +1454,20 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "groupID": { "type": "String", - "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true }, "members": { "list": true, "type": "JsonAddress", "version": "v1", - "doc": "list of requesting members to approve" + "doc": "list of requesting members to approve", + "required": true } }, "doc": "approve a request to join a group", @@ -1380,11 +1478,13 @@ "account": { "type": "String", "doc": "The account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "groupID": { "type": "String", - "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true }, "revision": { "type": "int", @@ -1401,11 +1501,13 @@ "account": { "type": "String", "doc": "The identifier of the account to interact with", - "example": "\"+12024561414\"" + "example": "\"+12024561414\"", + "required": true }, "groupID": { "type": "String", - "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", + "required": true }, "title": { "type": "String", @@ -1506,6 +1608,11 @@ }, "mark_read": { "request": "MarkReadRequest" + }, + "get_profile": { + "request": "GetProfileRequest", + "response": "Profile", + "doc": "Get all information available about a user" } }, "v1alpha1": { diff --git a/signald/client-protocol/v1/requests.go b/signald/client-protocol/v1/requests.go index 065ba8c..052c7a9 100644 --- a/signald/client-protocol/v1/requests.go +++ b/signald/client-protocol/v1/requests.go @@ -143,6 +143,39 @@ func (r *GetLinkedDevicesRequest) Submit(conn *signald.Signald) (response Linked } +// Submit: Get all information available about a user +func (r *GetProfileRequest) Submit(conn *signald.Signald) (response Profile, err error) { + r.Version = "v1" + r.Type = "get_profile" + 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" diff --git a/signald/client-protocol/v1/structs.go b/signald/client-protocol/v1/structs.go index dbff7f9..3a23417 100644 --- a/signald/client-protocol/v1/structs.go +++ b/signald/client-protocol/v1/structs.go @@ -27,6 +27,12 @@ type ApproveMembershipRequest struct { Members []*JsonAddress `json:"members,omitempty"` // list of requesting members to approve } +type Capabilities struct { + Gv1Migration bool `json:"gv1-migration,omitempty"` + Gv2 bool `json:"gv2,omitempty"` + Storage bool `json:"storage,omitempty"` +} + // GetGroupRequest: Query the server for the latest state of a known group type GetGroupRequest struct { Request @@ -41,6 +47,13 @@ type GetLinkedDevicesRequest struct { Account string `json:"account,omitempty"` // The account to interact with } +// GetProfileRequest: Get all information available about a user +type GetProfileRequest struct { + Request + Account string `json:"account,omitempty"` // the signald account to use + Address *JsonAddress `json:"address,omitempty"` // the address to look up +} + // GroupInfo: A generic type that is used when the group version is not known type GroupInfo struct { V1 *JsonGroupInfo `json:"v1,omitempty"` @@ -229,6 +242,18 @@ type MarkReadRequest struct { To *JsonAddress `json:"to,omitempty"` // The address that sent the message being marked as read } +// Profile: Information about a Signal user +type Profile struct { + Address *JsonAddress `json:"address,omitempty"` + Avatar string `json:"avatar,omitempty"` + Capabilities *Capabilities `json:"capabilities,omitempty"` + Color string `json:"color,omitempty"` + ExpirationTime int32 `json:"expiration_time,omitempty"` + InboxPosition int32 `json:"inbox_position,omitempty"` + Name string `json:"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"` // The user's Signal profile name +} + type ProtocolRequest struct { Request } diff --git a/tools/generator/main.go b/tools/generator/main.go index 03dac27..ad10640 100644 --- a/tools/generator/main.go +++ b/tools/generator/main.go @@ -64,6 +64,10 @@ var fieldNameMap = map[string]string{ "id": "ID", "recipientGroupId": "RecipientGroupID", "uuid": "UUID", + "expiration_time": "ExpirationTime", + "inbox_position": "InboxPosition", + "profile_name": "ProfileName", + "gv1-migration": "Gv1Migration", } func (d *DataType) fixForVersion(field, version string) {