Update protocol and add group leave command

This commit is contained in:
Finn 2021-02-11 17:33:02 -08:00
parent 8c7e571188
commit 2f84cb4f3c
4 changed files with 239 additions and 14 deletions

View file

@ -0,0 +1,88 @@
// Copyright © 2021 Finn Herzfeld <finn@janky.solutions>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package leave
import (
"encoding/json"
"log"
"os"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
"gitlab.com/signald/signald-go/cmd/signaldctl/common"
"gitlab.com/signald/signald-go/cmd/signaldctl/config"
"gitlab.com/signald/signald-go/signald/client-protocol/v1"
)
var (
account string
LeaveGroupCmd = &cobra.Command{
Use: "leave <group id>",
Short: "leave a group (or decline an invitation to a group)",
PreRun: func(cmd *cobra.Command, args []string) {
if account == "" {
account = config.Config.DefaultAccount
}
if account == "" {
log.Fatal("No account specified. Please specify with --account or set a default")
}
if len(args) == 0 {
common.Must(cmd.Help())
log.Fatal("must specify a group ID")
}
},
Run: func(_ *cobra.Command, args []string) {
go common.Signald.Listen(nil)
req := v1.LeaveGroupRequest{Account: account, GroupID: args[0]}
resp, err := req.Submit(common.Signald)
if err != nil {
log.Fatal(err, "error communicating with signald")
}
switch common.OutputFormat {
case common.OutputFormatJSON:
err := json.NewEncoder(os.Stdout).Encode(resp)
if err != nil {
log.Fatal(err, "error encoding response to stdout")
}
case common.OutputFormatYAML:
err := yaml.NewEncoder(os.Stdout).Encode(resp)
if err != nil {
log.Fatal(err, "error encoding response to stdout")
}
case common.OutputFormatCSV, common.OutputFormatTable, common.OutputFormatDefault:
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"ID", "Title", "Members"})
t.AppendRow(table.Row{resp.V2.ID, resp.V2.Title, len(resp.V2.Members)})
if common.OutputFormat == common.OutputFormatCSV {
t.RenderCSV()
} else {
common.StylizeTable(t)
t.Render()
}
default:
log.Fatal("Unsupported output format")
}
},
}
)
func init() {
AcceptGroupInvitationCmd.Flags().StringVarP(&account, "account", "a", "", "the signald account to use")
}

View file

@ -2,9 +2,9 @@
"doc_version": "v1", "doc_version": "v1",
"version": { "version": {
"name": "signald", "name": "signald",
"version": "0.12.0+git2021-02-08r98148393.17", "version": "0.12.0+git2021-02-12r1f18d539.24",
"branch": "main", "branch": "main",
"commit": "9814839376b0aceed8329fecc7477a5da7d0cae8" "commit": "1f18d539bb0d09ea51948e9559ab5e04ca9359a5"
}, },
"info": "This document describes objects that may be used when communicating with signald.", "info": "This document describes objects that may be used when communicating with signald.",
"types": { "types": {
@ -34,7 +34,7 @@
}, },
"timestamp": { "timestamp": {
"type": "long", "type": "long",
"example": "1612746778552" "example": "1613092537706"
}, },
"timestampISO": { "timestampISO": {
"type": "String" "type": "String"
@ -44,7 +44,7 @@
}, },
"serverDeliveredTimestamp": { "serverDeliveredTimestamp": {
"type": "long", "type": "long",
"example": "161274677855280" "example": "161309253770680"
}, },
"hasLegacyMessage": { "hasLegacyMessage": {
"type": "boolean" "type": "boolean"
@ -124,7 +124,7 @@
}, },
"timestamp": { "timestamp": {
"type": "long", "type": "long",
"example": "1612746778552" "example": "1613092537706"
} }
} }
}, },
@ -165,7 +165,7 @@
}, },
"version": { "version": {
"type": "String", "type": "String",
"example": "\"0.12.0+git2021-02-08r98148393.17\"" "example": "\"0.12.0+git2021-02-12r1f18d539.24\""
}, },
"branch": { "branch": {
"type": "String", "type": "String",
@ -173,7 +173,7 @@
}, },
"commit": { "commit": {
"type": "String", "type": "String",
"example": "\"9814839376b0aceed8329fecc7477a5da7d0cae8\"" "example": "\"1f18d539bb0d09ea51948e9559ab5e04ca9359a5\""
} }
} }
}, },
@ -517,7 +517,7 @@
"list": true, "list": true,
"type": "Long", "type": "Long",
"doc": "List of messages to mark as read", "doc": "List of messages to mark as read",
"example": "1612746778552", "example": "1613092537706",
"required": true "required": true
} }
} }
@ -620,12 +620,61 @@
} }
} }
}, },
"CreateGroupRequest": {
"fields": {
"account": {
"type": "String",
"doc": "The account to interact with",
"example": "\"+12024561414\"",
"required": true
},
"title": {
"type": "String",
"example": "\"Parkdale Run Club\"",
"required": true
},
"avatar": {
"type": "String",
"example": "\"/tmp/image.jpg\""
},
"members": {
"list": true,
"type": "JsonAddress",
"version": "v1"
},
"timer": {
"type": "int",
"doc": "the message expiration timer"
},
"member_role": {
"type": "String",
"doc": "The role of all members other than the group creator. Options are ADMINISTRATOR or DEFAULT (case insensitive)",
"example": "\"ADMINISTRATOR\""
}
}
},
"LeaveGroupRequest": {
"fields": {
"account": {
"type": "String",
"doc": "The account to use",
"example": "\"+12024561414\"",
"required": true
},
"groupID": {
"type": "String",
"doc": "The group to leave",
"example": "\"EdSqI90cS0UomDpgUXOlCoObWvQOXlH5G3Z2d3f4ayE=\"",
"required": true
}
}
},
"JsonDataMessage": { "JsonDataMessage": {
"fields": { "fields": {
"timestamp": { "timestamp": {
"type": "long", "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.", "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": "1612746778552" "example": "1613092537706"
}, },
"attachments": { "attachments": {
"list": true, "list": true,
@ -762,7 +811,7 @@
"id": { "id": {
"type": "long", "type": "long",
"doc": "the client timestamp of the message being quoted", "doc": "the client timestamp of the message being quoted",
"example": "1612746778552" "example": "1613092537706"
}, },
"author": { "author": {
"type": "JsonAddress", "type": "JsonAddress",
@ -850,7 +899,7 @@
"targetSentTimestamp": { "targetSentTimestamp": {
"type": "long", "type": "long",
"doc": "the client timestamp of the message being reacted to", "doc": "the client timestamp of the message being reacted to",
"example": "1612746778552" "example": "1613092537706"
} }
} }
}, },
@ -930,7 +979,7 @@
}, },
"timestamp": { "timestamp": {
"type": "long", "type": "long",
"example": "1612746778552" "example": "1613092537706"
}, },
"expirationStartTimestamp": { "expirationStartTimestamp": {
"type": "long" "type": "long"
@ -968,7 +1017,7 @@
}, },
"timestamp": { "timestamp": {
"type": "long", "type": "long",
"example": "1612746778552" "example": "1613092537706"
} }
} }
}, },
@ -980,7 +1029,7 @@
}, },
"timestamp": { "timestamp": {
"type": "long", "type": "long",
"example": "1612746778552" "example": "1613092537706"
} }
} }
}, },
@ -1746,6 +1795,14 @@
"list_contacts": { "list_contacts": {
"request": "ListContactsRequest", "request": "ListContactsRequest",
"response": "ProfileList" "response": "ProfileList"
},
"create_group": {
"request": "CreateGroupRequest",
"response": "JsonGroupV2Info"
},
"leave_group": {
"request": "LeaveGroupRequest",
"response": "GroupInfo"
} }
}, },
"v1alpha1": { "v1alpha1": {

View file

@ -76,6 +76,38 @@ func (r *ApproveMembershipRequest) Submit(conn *signald.Signald) (response JsonG
} }
func (r *CreateGroupRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) {
r.Version = "v1"
r.Type = "create_group"
if r.ID == "" {
r.ID = signald.GenerateID()
}
err = conn.RawRequest(r)
if err != nil {
log.Println("signald-go: error submitting request to signald")
return
}
responseChannel := conn.GetResponseListener(r.ID)
defer conn.CloseResponseListener(r.ID)
rawResponse := <-responseChannel
if rawResponse.Error != nil {
err = 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
}
return response, nil
}
// Submit: Query the server for the latest state of a known group // Submit: Query the server for the latest state of a known group
func (r *GetGroupRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) { func (r *GetGroupRequest) Submit(conn *signald.Signald) (response JsonGroupV2Info, err error) {
r.Version = "v1" r.Version = "v1"
@ -208,6 +240,38 @@ func (r *JoinGroupRequest) Submit(conn *signald.Signald) (response JsonGroupJoin
} }
func (r *LeaveGroupRequest) Submit(conn *signald.Signald) (response GroupInfo, err error) {
r.Version = "v1"
r.Type = "leave_group"
if r.ID == "" {
r.ID = signald.GenerateID()
}
err = conn.RawRequest(r)
if err != nil {
log.Println("signald-go: error submitting request to signald")
return
}
responseChannel := conn.GetResponseListener(r.ID)
defer conn.CloseResponseListener(r.ID)
rawResponse := <-responseChannel
if rawResponse.Error != nil {
err = 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
}
return response, nil
}
func (r *ListContactsRequest) Submit(conn *signald.Signald) (response ProfileList, err error) { func (r *ListContactsRequest) Submit(conn *signald.Signald) (response ProfileList, err error) {
r.Version = "v1" r.Version = "v1"
r.Type = "list_contacts" r.Type = "list_contacts"

View file

@ -33,6 +33,16 @@ type Capabilities struct {
Storage bool `json:"storage,omitempty" yaml:"storage,omitempty"` Storage bool `json:"storage,omitempty" yaml:"storage,omitempty"`
} }
type CreateGroupRequest struct {
Request
Account string `json:"account,omitempty" yaml:"account,omitempty"` // The account to interact with
Avatar string `json:"avatar,omitempty" yaml:"avatar,omitempty"`
Member_role string `json:"member_role,omitempty" yaml:"member_role,omitempty"` // The role of all members other than the group creator. Options are ADMINISTRATOR or DEFAULT (case insensitive)
Members []*JsonAddress `json:"members,omitempty" yaml:"members,omitempty"`
Timer int32 `json:"timer,omitempty" yaml:"timer,omitempty"` // the message expiration timer
Title string `json:"title,omitempty" yaml:"title,omitempty"`
}
// GetGroupRequest: Query the server for the latest state of a known group // GetGroupRequest: Query the server for the latest state of a known group
type GetGroupRequest struct { type GetGroupRequest struct {
Request Request
@ -254,6 +264,12 @@ type JsonViewOnceOpenMessage struct {
Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"` Timestamp int64 `json:"timestamp,omitempty" yaml:"timestamp,omitempty"`
} }
type LeaveGroupRequest struct {
Request
Account string `json:"account,omitempty" yaml:"account,omitempty"` // The account to use
GroupID string `json:"groupID,omitempty" yaml:"groupID,omitempty"` // The group to leave
}
type LinkedDevices struct { type LinkedDevices struct {
Devices []*v0.DeviceInfo `json:"devices,omitempty" yaml:"devices,omitempty"` Devices []*v0.DeviceInfo `json:"devices,omitempty" yaml:"devices,omitempty"`
} }