From 8c7e5711889f87c32b9bcd676c92ddb2a89c4ea1 Mon Sep 17 00:00:00 2001 From: Finn Date: Sun, 7 Feb 2021 19:59:35 -0800 Subject: [PATCH] Update protocol --- protocol.json | 115 +++++++++++++++++++++---- signald/client-protocol/v1/requests.go | 2 +- signald/client-protocol/v1/structs.go | 55 ++++++++---- 3 files changed, 135 insertions(+), 37 deletions(-) diff --git a/protocol.json b/protocol.json index 5e7dbd9..ec08625 100644 --- a/protocol.json +++ b/protocol.json @@ -2,9 +2,9 @@ "doc_version": "v1", "version": { "name": "signald", - "version": "0.12.0+git2021-01-30rce7ef396.5", + "version": "0.12.0+git2021-02-08r98148393.17", "branch": "main", - "commit": "ce7ef39669fc7482b2efe6555bc2962cca8e1ca6" + "commit": "9814839376b0aceed8329fecc7477a5da7d0cae8" }, "info": "This document describes objects that may be used when communicating with signald.", "types": { @@ -34,7 +34,7 @@ }, "timestamp": { "type": "long", - "example": "1612005072095" + "example": "1612746778552" }, "timestampISO": { "type": "String" @@ -44,7 +44,7 @@ }, "serverDeliveredTimestamp": { "type": "long", - "example": "161200507209580" + "example": "161274677855280" }, "hasLegacyMessage": { "type": "boolean" @@ -124,7 +124,7 @@ }, "timestamp": { "type": "long", - "example": "1612005072095" + "example": "1612746778552" } } }, @@ -165,7 +165,7 @@ }, "version": { "type": "String", - "example": "\"0.12.0+git2021-01-30rce7ef396.5\"" + "example": "\"0.12.0+git2021-02-08r98148393.17\"" }, "branch": { "type": "String", @@ -173,7 +173,7 @@ }, "commit": { "type": "String", - "example": "\"ce7ef39669fc7482b2efe6555bc2962cca8e1ca6\"" + "example": "\"9814839376b0aceed8329fecc7477a5da7d0cae8\"" } } }, @@ -207,6 +207,11 @@ "type": "String", "example": "\"Parkdale Run Club\"" }, + "avatar": { + "type": "String", + "doc": "path to the group's avatar on local disk, if available", + "example": "\"/var/lib/signald/avatars/group-EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"" + }, "timer": { "type": "int", "example": "604800" @@ -227,7 +232,25 @@ "version": "v1" }, "inviteLink": { - "type": "String" + "type": "String", + "doc": "the signal.group link, if applicable" + }, + "accessControl": { + "type": "GroupAccessControl", + "version": "v1", + "doc": "current access control settings for this group" + }, + "memberDetail": { + "list": true, + "type": "GroupMember", + "version": "v1", + "doc": "detailed member list" + }, + "pendingMemberDetail": { + "list": true, + "type": "GroupMember", + "version": "v1", + "doc": "detailed pending member list" } } }, @@ -367,6 +390,7 @@ }, "groupID": { "type": "String", + "doc": "the ID of the group to update", "example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"", "required": true }, @@ -378,6 +402,10 @@ "type": "String", "example": "\"/tmp/image.jpg\"" }, + "updateTimer": { + "type": "int", + "doc": "update the group timer." + }, "addMembers": { "list": true, "type": "JsonAddress", @@ -387,9 +415,22 @@ "list": true, "type": "JsonAddress", "version": "v1" + }, + "updateRole": { + "type": "GroupMember", + "version": "v1" + }, + "updateAccessControl": { + "type": "GroupAccessControl", + "version": "v1", + "doc": "note that only one of the access controls may be updated per request" + }, + "resetLink": { + "type": "boolean", + "doc": "regenerate the group link password, invalidating the old one" } }, - "doc": "modify a group" + "doc": "modify a group. Note that only one modification action may be preformed at once" }, "GroupInfo": { "fields": { @@ -476,7 +517,7 @@ "list": true, "type": "Long", "doc": "List of messages to mark as read", - "example": "1612005072095", + "example": "1612746778552", "required": true } } @@ -508,7 +549,8 @@ "doc": "The user's name from local contact names if available, or if not in contact list their Signal profile name" }, "avatar": { - "type": "String" + "type": "String", + "doc": "path to avatar on local disk" }, "address": { "type": "JsonAddress", @@ -519,7 +561,8 @@ "version": "v1" }, "color": { - "type": "String" + "type": "String", + "doc": "color of the chat with this user" }, "profile_name": { "type": "String", @@ -582,7 +625,7 @@ "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": "1612005072095" + "example": "1612746778552" }, "attachments": { "list": true, @@ -719,7 +762,7 @@ "id": { "type": "long", "doc": "the client timestamp of the message being quoted", - "example": "1612005072095" + "example": "1612746778552" }, "author": { "type": "JsonAddress", @@ -807,7 +850,7 @@ "targetSentTimestamp": { "type": "long", "doc": "the client timestamp of the message being reacted to", - "example": "1612005072095" + "example": "1612746778552" } } }, @@ -832,6 +875,40 @@ } } }, + "GroupAccessControl": { + "fields": { + "link": { + "type": "String", + "doc": "UNSATISFIABLE when the group link is disabled, ADMINISTRATOR when the group link is enabled but an administrator must approve new members, ANY when the group link is enabled and no approval is required", + "example": "\"ANY\"" + }, + "attributes": { + "type": "String", + "doc": "who can edit group info" + }, + "members": { + "type": "String", + "doc": "who can add members" + } + }, + "doc": "group access control settings. Options for each controlled action are: UNKNOWN, ANY, MEMBER, ADMINISTRATOR, UNSATISFIABLE and UNRECOGNIZED" + }, + "GroupMember": { + "fields": { + "uuid": { + "type": "String", + "example": "\"aeed01f0-a234-478e-8cf7-261c283151e7\"" + }, + "role": { + "type": "String", + "doc": "possible values are: UNKNOWN, DEFAULT, ADMINISTRATOR and UNRECOGNIZED", + "example": "\"DEFAULT\"" + }, + "joined_revision": { + "type": "int" + } + } + }, "Capabilities": { "fields": { "gv2": { @@ -853,7 +930,7 @@ }, "timestamp": { "type": "long", - "example": "1612005072095" + "example": "1612746778552" }, "expirationStartTimestamp": { "type": "long" @@ -891,7 +968,7 @@ }, "timestamp": { "type": "long", - "example": "1612005072095" + "example": "1612746778552" } } }, @@ -903,7 +980,7 @@ }, "timestamp": { "type": "long", - "example": "1612005072095" + "example": "1612746778552" } } }, @@ -1644,7 +1721,7 @@ "update_group": { "request": "UpdateGroupRequest", "response": "GroupInfo", - "doc": "modify a group" + "doc": "modify a group. Note that only one modification action may be preformed at once" }, "set_profile": { "request": "SetProfile" diff --git a/signald/client-protocol/v1/requests.go b/signald/client-protocol/v1/requests.go index f9d19cb..ba78fe1 100644 --- a/signald/client-protocol/v1/requests.go +++ b/signald/client-protocol/v1/requests.go @@ -471,7 +471,7 @@ func (r *SetProfile) Submit(conn *signald.Signald) (err error) { } -// Submit: modify a group +// Submit: modify a group. Note that only one modification action may be preformed at once func (r *UpdateGroupRequest) Submit(conn *signald.Signald) (response GroupInfo, err error) { r.Version = "v1" r.Type = "update_group" diff --git a/signald/client-protocol/v1/structs.go b/signald/client-protocol/v1/structs.go index 83adc87..f4e6e5d 100644 --- a/signald/client-protocol/v1/structs.go +++ b/signald/client-protocol/v1/structs.go @@ -55,6 +55,13 @@ type GetProfileRequest struct { Async bool `json:"async,omitempty" yaml:"async,omitempty"` // return results from local store immediately, refreshing from server if needed. If false (default), block until all pending profiles have been retrieved. } +// GroupAccessControl: group access control settings. Options for each controlled action are: UNKNOWN, ANY, MEMBER, ADMINISTRATOR, UNSATISFIABLE and UNRECOGNIZED +type GroupAccessControl struct { + Attributes string `json:"attributes,omitempty" yaml:"attributes,omitempty"` // who can edit group info + Link string `json:"link,omitempty" yaml:"link,omitempty"` // UNSATISFIABLE when the group link is disabled, ADMINISTRATOR when the group link is enabled but an administrator must approve new members, ANY when the group link is enabled and no approval is required + Members string `json:"members,omitempty" yaml:"members,omitempty"` // who can add members +} + // GroupInfo: A generic type that is used when the group version is not known type GroupInfo struct { V1 *JsonGroupInfo `json:"v1,omitempty" yaml:"v1,omitempty"` @@ -66,6 +73,12 @@ type GroupList struct { LegacyGroups []*JsonGroupInfo `json:"legacyGroups,omitempty" yaml:"legacyGroups,omitempty"` } +type GroupMember struct { + Joined_revision int32 `json:"joined_revision,omitempty" yaml:"joined_revision,omitempty"` + Role string `json:"role,omitempty" yaml:"role,omitempty"` // possible values are: UNKNOWN, DEFAULT, ADMINISTRATOR and UNRECOGNIZED + UUID string `json:"uuid,omitempty" yaml:"uuid,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 @@ -121,14 +134,18 @@ type JsonGroupJoinInfo struct { } type JsonGroupV2Info struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - InviteLink string `json:"inviteLink,omitempty" yaml:"inviteLink,omitempty"` - Members []*JsonAddress `json:"members,omitempty" yaml:"members,omitempty"` - PendingMembers []*JsonAddress `json:"pendingMembers,omitempty" yaml:"pendingMembers,omitempty"` - RequestingMembers []*JsonAddress `json:"requestingMembers,omitempty" yaml:"requestingMembers,omitempty"` - Revision int32 `json:"revision,omitempty" yaml:"revision,omitempty"` - Timer int32 `json:"timer,omitempty" yaml:"timer,omitempty"` - Title string `json:"title,omitempty" yaml:"title,omitempty"` + AccessControl *GroupAccessControl `json:"accessControl,omitempty" yaml:"accessControl,omitempty"` // current access control settings for this group + Avatar string `json:"avatar,omitempty" yaml:"avatar,omitempty"` // path to the group's avatar on local disk, if available + ID string `json:"id,omitempty" yaml:"id,omitempty"` + InviteLink string `json:"inviteLink,omitempty" yaml:"inviteLink,omitempty"` // the signal.group link, if applicable + MemberDetail []*GroupMember `json:"memberDetail,omitempty" yaml:"memberDetail,omitempty"` // detailed member list + Members []*JsonAddress `json:"members,omitempty" yaml:"members,omitempty"` + PendingMemberDetail []*GroupMember `json:"pendingMemberDetail,omitempty" yaml:"pendingMemberDetail,omitempty"` // detailed pending member list + PendingMembers []*JsonAddress `json:"pendingMembers,omitempty" yaml:"pendingMembers,omitempty"` + RequestingMembers []*JsonAddress `json:"requestingMembers,omitempty" yaml:"requestingMembers,omitempty"` + Revision int32 `json:"revision,omitempty" yaml:"revision,omitempty"` + Timer int32 `json:"timer,omitempty" yaml:"timer,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` } type JsonMention struct { @@ -262,9 +279,9 @@ type MarkReadRequest struct { // Profile: Information about a Signal user type Profile struct { Address *JsonAddress `json:"address,omitempty" yaml:"address,omitempty"` - Avatar string `json:"avatar,omitempty" yaml:"avatar,omitempty"` + Avatar string `json:"avatar,omitempty" yaml:"avatar,omitempty"` // path to avatar on local disk Capabilities *Capabilities `json:"capabilities,omitempty" yaml:"capabilities,omitempty"` - Color string `json:"color,omitempty" yaml:"color,omitempty"` + Color string `json:"color,omitempty" yaml:"color,omitempty"` // color of the chat with this user ExpirationTime int32 `json:"expiration_time,omitempty" yaml:"expiration_time,omitempty"` InboxPosition int32 `json:"inbox_position,omitempty" yaml:"inbox_position,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` // The user's name from local contact names if available, or if not in contact list their Signal profile name @@ -327,15 +344,19 @@ type SetProfile struct { Name string `json:"name,omitempty" yaml:"name,omitempty"` // New profile name. Set to empty string for no profile name } -// UpdateGroupRequest: modify a group +// UpdateGroupRequest: modify a group. Note that only one modification action may be preformed at once type UpdateGroupRequest struct { Request - Account string `json:"account,omitempty" yaml:"account,omitempty"` // The identifier of the account to interact with - AddMembers []*JsonAddress `json:"addMembers,omitempty" yaml:"addMembers,omitempty"` - Avatar string `json:"avatar,omitempty" yaml:"avatar,omitempty"` - GroupID string `json:"groupID,omitempty" yaml:"groupID,omitempty"` - RemoveMembers []*JsonAddress `json:"removeMembers,omitempty" yaml:"removeMembers,omitempty"` - Title string `json:"title,omitempty" yaml:"title,omitempty"` + Account string `json:"account,omitempty" yaml:"account,omitempty"` // The identifier of the account to interact with + AddMembers []*JsonAddress `json:"addMembers,omitempty" yaml:"addMembers,omitempty"` + Avatar string `json:"avatar,omitempty" yaml:"avatar,omitempty"` + GroupID string `json:"groupID,omitempty" yaml:"groupID,omitempty"` // the ID of the group to update + RemoveMembers []*JsonAddress `json:"removeMembers,omitempty" yaml:"removeMembers,omitempty"` + ResetLink bool `json:"resetLink,omitempty" yaml:"resetLink,omitempty"` // regenerate the group link password, invalidating the old one + Title string `json:"title,omitempty" yaml:"title,omitempty"` + UpdateAccessControl *GroupAccessControl `json:"updateAccessControl,omitempty" yaml:"updateAccessControl,omitempty"` // note that only one of the access controls may be updated per request + UpdateRole *GroupMember `json:"updateRole,omitempty" yaml:"updateRole,omitempty"` + UpdateTimer int32 `json:"updateTimer,omitempty" yaml:"updateTimer,omitempty"` // update the group timer. } type VersionRequest struct {