diff --git a/protocol.json b/protocol.json index b6a679c..5e7dbd9 100644 --- a/protocol.json +++ b/protocol.json @@ -2,9 +2,9 @@ "doc_version": "v1", "version": { "name": "signald", - "version": "0.11.1+git2021-01-30r350d7b78.75", + "version": "0.12.0+git2021-01-30rce7ef396.5", "branch": "main", - "commit": "350d7b78e6d8579866da07b9dc91035663e0b073" + "commit": "ce7ef39669fc7482b2efe6555bc2962cca8e1ca6" }, "info": "This document describes objects that may be used when communicating with signald.", "types": { @@ -34,7 +34,7 @@ }, "timestamp": { "type": "long", - "example": "1611966817808" + "example": "1612005072095" }, "timestampISO": { "type": "String" @@ -44,7 +44,7 @@ }, "serverDeliveredTimestamp": { "type": "long", - "example": "161196681780880" + "example": "161200507209580" }, "hasLegacyMessage": { "type": "boolean" @@ -124,7 +124,7 @@ }, "timestamp": { "type": "long", - "example": "1611966817808" + "example": "1612005072095" } } }, @@ -165,7 +165,7 @@ }, "version": { "type": "String", - "example": "\"0.11.1+git2021-01-30r350d7b78.75\"" + "example": "\"0.12.0+git2021-01-30rce7ef396.5\"" }, "branch": { "type": "String", @@ -173,7 +173,7 @@ }, "commit": { "type": "String", - "example": "\"350d7b78e6d8579866da07b9dc91035663e0b073\"" + "example": "\"ce7ef39669fc7482b2efe6555bc2962cca8e1ca6\"" } } }, @@ -476,7 +476,7 @@ "list": true, "type": "Long", "doc": "List of messages to mark as read", - "example": "1611966817808", + "example": "1612005072095", "required": true } } @@ -488,6 +488,10 @@ "doc": "the signald account to use", "required": true }, + "async": { + "type": "boolean", + "doc": "return results from local store immediately, refreshing from server if needed. If false (default), block until all pending profiles have been retrieved." + }, "address": { "type": "JsonAddress", "version": "v1", @@ -530,12 +534,55 @@ }, "doc": "Information about a Signal user" }, + "ListGroupsRequest": { + "fields": { + "account": { + "type": "String", + "required": true + } + } + }, + "GroupList": { + "fields": { + "groups": { + "list": true, + "type": "JsonGroupV2Info", + "version": "v1" + }, + "legacyGroups": { + "list": true, + "type": "JsonGroupInfo", + "version": "v1" + } + } + }, + "ListContactsRequest": { + "fields": { + "account": { + "type": "String", + "required": true + }, + "async": { + "type": "boolean", + "doc": "return results from local store immediately, refreshing from server if needed. If false (default), block until all pending profiles have been retrieved." + } + } + }, + "ProfileList": { + "fields": { + "profiles": { + "list": true, + "type": "Profile", + "version": "v1" + } + } + }, "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": "1611966817808" + "example": "1612005072095" }, "attachments": { "list": true, @@ -672,7 +719,7 @@ "id": { "type": "long", "doc": "the client timestamp of the message being quoted", - "example": "1611966817808" + "example": "1612005072095" }, "author": { "type": "JsonAddress", @@ -760,7 +807,7 @@ "targetSentTimestamp": { "type": "long", "doc": "the client timestamp of the message being reacted to", - "example": "1611966817808" + "example": "1612005072095" } } }, @@ -806,7 +853,7 @@ }, "timestamp": { "type": "long", - "example": "1611966817808" + "example": "1612005072095" }, "expirationStartTimestamp": { "type": "long" @@ -844,7 +891,7 @@ }, "timestamp": { "type": "long", - "example": "1611966817808" + "example": "1612005072095" } } }, @@ -856,7 +903,7 @@ }, "timestamp": { "type": "long", - "example": "1611966817808" + "example": "1612005072095" } } }, @@ -1317,7 +1364,8 @@ "required": true } }, - "doc": "Remove a linked device from the Signal account. Only allowed when the local device id is 1" + "doc": "Remove a linked device from the Signal account. Only allowed when the local device id is 1", + "deprecated": true }, "JoinGroupRequest": { "fields": { @@ -1613,6 +1661,14 @@ "request": "GetProfileRequest", "response": "Profile", "doc": "Get all information available about a user" + }, + "list_groups": { + "request": "ListGroupsRequest", + "response": "GroupList" + }, + "list_contacts": { + "request": "ListContactsRequest", + "response": "ProfileList" } }, "v1alpha1": { @@ -1628,7 +1684,8 @@ }, "remove_linked_device": { "request": "RemoveLinkedDeviceRequest", - "doc": "Remove a linked device from the Signal account. Only allowed when the local device id is 1" + "doc": "Remove a linked device from the Signal account. Only allowed when the local device id is 1", + "deprecated": true }, "join_group": { "request": "JoinGroupRequest", diff --git a/signald/client-protocol/v1/requests.go b/signald/client-protocol/v1/requests.go index 052c7a9..d0edb38 100644 --- a/signald/client-protocol/v1/requests.go +++ b/signald/client-protocol/v1/requests.go @@ -209,6 +209,70 @@ func (r *JoinGroupRequest) Submit(conn *signald.Signald) (response JsonGroupJoin } +func (r *ListContactsRequest) Submit(conn *signald.Signald) (response ProfileList, err error) { + r.Version = "v1" + r.Type = "list_contacts" + 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 *ListGroupsRequest) Submit(conn *signald.Signald) (response GroupList, err error) { + r.Version = "v1" + r.Type = "list_groups" + 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" diff --git a/signald/client-protocol/v1/structs.go b/signald/client-protocol/v1/structs.go index 3a23417..1601545 100644 --- a/signald/client-protocol/v1/structs.go +++ b/signald/client-protocol/v1/structs.go @@ -52,6 +52,7 @@ type GetProfileRequest struct { Request Account string `json:"account,omitempty"` // the signald account to use Address *JsonAddress `json:"address,omitempty"` // the address to look up + Async bool `json:"async,omitempty"` // return results from local store immediately, refreshing from server if needed. If false (default), block until all pending profiles have been retrieved. } // GroupInfo: A generic type that is used when the group version is not known @@ -60,6 +61,11 @@ type GroupInfo struct { V2 *JsonGroupV2Info `json:"v2,omitempty"` } +type GroupList struct { + Groups []*JsonGroupV2Info `json:"groups,omitempty"` + LegacyGroups []*JsonGroupInfo `json:"legacyGroups,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 @@ -235,6 +241,17 @@ type LinkedDevices struct { Devices []*v0.DeviceInfo `json:"devices,omitempty"` } +type ListContactsRequest struct { + Request + Account string `json:"account,omitempty"` + Async bool `json:"async,omitempty"` // return results from local store immediately, refreshing from server if needed. If false (default), block until all pending profiles have been retrieved. +} + +type ListGroupsRequest struct { + Request + Account string `json:"account,omitempty"` +} + type MarkReadRequest struct { Request Account string `json:"account,omitempty"` // The account to interact with @@ -254,6 +271,10 @@ type Profile struct { ProfileName string `json:"profile_name,omitempty"` // The user's Signal profile name } +type ProfileList struct { + Profiles []*Profile `json:"profiles,omitempty"` +} + type ProtocolRequest struct { Request }