Rework group invite workflow: delete cwtchPacket references as no longer needed. Remove more events from being default handled by Peer (but allow them for some usecases still (testing, simple apps).
the build was successful
Информация
the build was successful
Информация
Этот коммит содержится в:
родитель
304a55aa5e
Коммит
15582c7e79
|
@ -558,11 +558,11 @@ func main() {
|
|||
}
|
||||
case "/import-group":
|
||||
if len(commands) == 2 {
|
||||
groupID, err := peer.ImportGroup(commands[1])
|
||||
err := peer.ImportGroup(commands[1])
|
||||
if err != nil {
|
||||
fmt.Printf("Error importing group: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Imported group: %s\n", groupID)
|
||||
fmt.Printf("Imported group!\n")
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("%v", commands)
|
||||
|
|
|
@ -37,7 +37,7 @@ type pipeBridge struct {
|
|||
closedChan chan bool
|
||||
state connections.ConnectionState
|
||||
lock sync.Mutex
|
||||
threeShake func () bool
|
||||
threeShake func() bool
|
||||
|
||||
// For logging / debugging purposes
|
||||
name string
|
||||
|
@ -138,8 +138,8 @@ func (pb *pipeBridge) synLoop(stop chan bool) {
|
|||
return
|
||||
}
|
||||
delay = time.Second
|
||||
case <- stop:
|
||||
return
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ const (
|
|||
// Data [eg "open privacy board"]
|
||||
SetGroupAttribute = Type("SetGroupAttribute")
|
||||
|
||||
// PeerStateChange servers as a new incoming connection message as well, and can/is consumed by frontends to alert of new p2p connections
|
||||
// RemotePeer
|
||||
// ConnectionState
|
||||
PeerStateChange = Type("PeerStateChange")
|
||||
|
|
|
@ -97,10 +97,7 @@ func (g *Group) Invite(initialMessage []byte) ([]byte, error) {
|
|||
InitialMessage: initialMessage[:],
|
||||
}
|
||||
|
||||
cp := &protocol.CwtchPeerPacket{
|
||||
GroupChatInvite: gci,
|
||||
}
|
||||
invite, err := proto.Marshal(cp)
|
||||
invite, err := proto.Marshal(gci)
|
||||
return invite, err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/protocol"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -17,9 +15,8 @@ func TestMessagePadding(t *testing.T) {
|
|||
alice.AddContact(sarah.Onion, &sarah.PublicProfile)
|
||||
|
||||
gid, invite, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
|
||||
gci := &protocol.CwtchPeerPacket{}
|
||||
proto.Unmarshal(invite, gci)
|
||||
sarah.ProcessInvite(gci.GetGroupChatInvite(), alice.Onion)
|
||||
|
||||
sarah.ProcessInvite(string(invite), alice.Onion)
|
||||
|
||||
group := alice.GetGroupByGroupID(gid)
|
||||
|
||||
|
@ -51,9 +48,8 @@ func TestTranscriptConsistency(t *testing.T) {
|
|||
alice.AddContact(sarah.Onion, &sarah.PublicProfile)
|
||||
|
||||
gid, invite, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
|
||||
gci := &protocol.CwtchPeerPacket{}
|
||||
proto.Unmarshal(invite, gci)
|
||||
sarah.ProcessInvite(gci.GetGroupChatInvite(), alice.Onion)
|
||||
|
||||
sarah.ProcessInvite(string(invite), alice.Onion)
|
||||
|
||||
group := alice.GetGroupByGroupID(gid)
|
||||
|
||||
|
|
|
@ -87,20 +87,6 @@ func GenerateNewProfile(name string) *Profile {
|
|||
return p
|
||||
}
|
||||
|
||||
// GetCwtchIdentityPacket returns the wire message for conveying this profiles identity.
|
||||
func (p *Profile) GetCwtchIdentityPacket() (message []byte) {
|
||||
ci := &protocol.CwtchIdentity{
|
||||
Name: p.Name,
|
||||
Ed25519PublicKey: p.Ed25519PublicKey,
|
||||
}
|
||||
cpp := &protocol.CwtchPeerPacket{
|
||||
CwtchIdentify: ci,
|
||||
}
|
||||
message, err := proto.Marshal(cpp)
|
||||
utils.CheckError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// AddContact allows direct manipulation of cwtch contacts
|
||||
func (p *Profile) AddContact(onion string, profile *PublicProfile) {
|
||||
p.lock.Lock()
|
||||
|
@ -317,19 +303,25 @@ func (p *Profile) GetGroupByGroupID(groupID string) (g *Group) {
|
|||
return
|
||||
}
|
||||
|
||||
// ProcessInvite adds a new group invite to the profile.
|
||||
func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite, peerHostname string) {
|
||||
group := new(Group)
|
||||
group.GroupID = gci.GetGroupName()
|
||||
group.LocalID = generateRandomID()
|
||||
group.SignedGroupID = gci.GetSignedGroupId()
|
||||
copy(group.GroupKey[:], gci.GetGroupSharedKey()[:])
|
||||
group.GroupServer = gci.GetServerHost()
|
||||
group.InitialMessage = gci.GetInitialMessage()[:]
|
||||
group.Accepted = false
|
||||
group.Owner = peerHostname
|
||||
group.Attributes = make(map[string]string)
|
||||
p.AddGroup(group)
|
||||
// ProcessInvite adds a new group invite to the profile. returns the new group ID
|
||||
func (p *Profile) ProcessInvite(invite string, peerHostname string) (string, error) {
|
||||
var gci protocol.GroupChatInvite
|
||||
err := proto.Unmarshal([]byte(invite), &gci)
|
||||
if err == nil {
|
||||
group := new(Group)
|
||||
group.GroupID = gci.GetGroupName()
|
||||
group.LocalID = generateRandomID()
|
||||
group.SignedGroupID = gci.GetSignedGroupId()
|
||||
copy(group.GroupKey[:], gci.GetGroupSharedKey()[:])
|
||||
group.GroupServer = gci.GetServerHost()
|
||||
group.InitialMessage = gci.GetInitialMessage()[:]
|
||||
group.Accepted = false
|
||||
group.Owner = peerHostname
|
||||
group.Attributes = make(map[string]string)
|
||||
p.AddGroup(group)
|
||||
return group.GroupID, nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// AddGroup is a convenience method for adding a group to a profile.
|
||||
|
|
|
@ -1,23 +1,14 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/protocol"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestProfileIdentity(t *testing.T) {
|
||||
sarah := GenerateNewProfile("Sarah")
|
||||
alice := GenerateNewProfile("Alice")
|
||||
|
||||
message := sarah.GetCwtchIdentityPacket()
|
||||
|
||||
ci := &protocol.CwtchPeerPacket{}
|
||||
err := proto.Unmarshal(message, ci)
|
||||
if err != nil {
|
||||
t.Errorf("alice should have added sarah as a contact %v", err)
|
||||
}
|
||||
|
||||
alice.AddContact(sarah.Onion, &sarah.PublicProfile)
|
||||
if alice.Contacts[sarah.Onion].Name != "Sarah" {
|
||||
t.Errorf("alice should have added sarah as a contact %v", alice.Contacts)
|
||||
|
@ -78,9 +69,7 @@ func TestRejectGroupInvite(t *testing.T) {
|
|||
alice.AddContact(sarah.Onion, &sarah.PublicProfile)
|
||||
|
||||
gid, invite, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
|
||||
gci := &protocol.CwtchPeerPacket{}
|
||||
proto.Unmarshal(invite, gci)
|
||||
sarah.ProcessInvite(gci.GetGroupChatInvite(), alice.Onion)
|
||||
sarah.ProcessInvite(string(invite), alice.Onion)
|
||||
group := alice.GetGroupByGroupID(gid)
|
||||
if len(sarah.Groups) == 1 {
|
||||
if sarah.GetGroupByGroupID(group.GroupID).Accepted {
|
||||
|
@ -102,9 +91,7 @@ func TestProfileGroup(t *testing.T) {
|
|||
alice.AddContact(sarah.Onion, &sarah.PublicProfile)
|
||||
|
||||
gid, invite, _ := alice.StartGroupWithMessage("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd", []byte("Hello World"))
|
||||
gci := &protocol.CwtchPeerPacket{}
|
||||
proto.Unmarshal(invite, gci)
|
||||
sarah.ProcessInvite(gci.GetGroupChatInvite(), alice.Onion)
|
||||
sarah.ProcessInvite(string(invite), alice.Onion)
|
||||
if len(sarah.GetGroups()) != 1 {
|
||||
t.Errorf("sarah should only be in 1 group instead: %v", sarah.GetGroups())
|
||||
}
|
||||
|
@ -115,9 +102,7 @@ func TestProfileGroup(t *testing.T) {
|
|||
alice.AttemptDecryption(c, s1)
|
||||
|
||||
gid2, invite2, _ := alice.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
|
||||
gci2 := &protocol.CwtchPeerPacket{}
|
||||
proto.Unmarshal(invite2, gci2)
|
||||
sarah.ProcessInvite(gci2.GetGroupChatInvite(), alice.Onion)
|
||||
sarah.ProcessInvite(string(invite2), alice.Onion)
|
||||
group2 := alice.GetGroupByGroupID(gid2)
|
||||
c2, s2, _ := sarah.EncryptMessageToGroup("Hello World", group2.GroupID)
|
||||
alice.AttemptDecryption(c2, s2)
|
||||
|
@ -135,7 +120,7 @@ func TestProfileGroup(t *testing.T) {
|
|||
|
||||
bob := GenerateNewProfile("bob")
|
||||
bob.AddContact(alice.Onion, &alice.PublicProfile)
|
||||
bob.ProcessInvite(gci2.GetGroupChatInvite(), alice.Onion)
|
||||
bob.ProcessInvite(string(invite2), alice.Onion)
|
||||
c3, s3, err := bob.EncryptMessageToGroup("Bobs Message", group2.GroupID)
|
||||
if err == nil {
|
||||
ok, _, message, _ := alice.AttemptDecryption(c3, s3)
|
||||
|
|
|
@ -3,19 +3,19 @@ package peer
|
|||
import (
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/protocol"
|
||||
"cwtch.im/cwtch/protocol/connections"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var autoHandleableEvents = map[event.Type]bool{event.EncryptedGroupMessage: true, event.PeerStateChange: true, event.ServerStateChange: true, event.NewGroupInvite: true}
|
||||
|
||||
// cwtchPeer manages incoming and outgoing connections and all processing for a Cwtch cwtchPeer
|
||||
type cwtchPeer struct {
|
||||
Profile *model.Profile
|
||||
|
@ -30,6 +30,7 @@ type cwtchPeer struct {
|
|||
// directly implement a cwtchPeer.
|
||||
type CwtchPeer interface {
|
||||
Init(event.Manager)
|
||||
AutoHandleEvents(events []event.Type)
|
||||
PeerWithOnion(string)
|
||||
InviteOnionToGroup(string, string) error
|
||||
SendMessageToPeer(string, string) string
|
||||
|
@ -51,7 +52,7 @@ type CwtchPeer interface {
|
|||
|
||||
StartGroup(string) (string, []byte, error)
|
||||
|
||||
ImportGroup(string) (string, error)
|
||||
ImportGroup(string) error
|
||||
ExportGroup(string) (string, error)
|
||||
|
||||
GetGroup(string) *model.Group
|
||||
|
@ -79,6 +80,7 @@ func NewCwtchPeer(name string) CwtchPeer {
|
|||
func FromProfile(profile *model.Profile) CwtchPeer {
|
||||
cp := new(cwtchPeer)
|
||||
cp.Profile = profile
|
||||
cp.shutdown = false
|
||||
return cp
|
||||
}
|
||||
|
||||
|
@ -88,35 +90,34 @@ func (cp *cwtchPeer) Init(eventBus event.Manager) {
|
|||
go cp.eventHandler()
|
||||
|
||||
cp.eventBus = eventBus
|
||||
cp.eventBus.Subscribe(event.EncryptedGroupMessage, cp.queue)
|
||||
cp.eventBus.Subscribe(event.NewGroupInvite, cp.queue)
|
||||
cp.eventBus.Subscribe(event.ServerStateChange, cp.queue)
|
||||
cp.eventBus.Subscribe(event.PeerStateChange, cp.queue)
|
||||
cp.AutoHandleEvents([]event.Type{event.EncryptedGroupMessage})
|
||||
}
|
||||
|
||||
// AutoHandleEvents sets an event (if able) to be handled by this peer
|
||||
func (cp *cwtchPeer) AutoHandleEvents(events []event.Type) {
|
||||
for _, ev := range events {
|
||||
if _, exists := autoHandleableEvents[ev]; exists {
|
||||
cp.eventBus.Subscribe(ev, cp.queue)
|
||||
} else {
|
||||
log.Errorf("Peer asked to autohandle event it cannot: %v\n", ev)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ImportGroup intializes a group from an imported source rather than a peer invite
|
||||
func (cp *cwtchPeer) ImportGroup(exportedInvite string) (groupID string, err error) {
|
||||
func (cp *cwtchPeer) ImportGroup(exportedInvite string) (err error) {
|
||||
if strings.HasPrefix(exportedInvite, "torv3") {
|
||||
data, err := base64.StdEncoding.DecodeString(exportedInvite[5+44:])
|
||||
data, err := base64.StdEncoding.DecodeString(exportedInvite[5:])
|
||||
if err == nil {
|
||||
cpp := &protocol.CwtchPeerPacket{}
|
||||
err = proto.Unmarshal(data, cpp)
|
||||
if err == nil {
|
||||
jsobj, err := proto.Marshal(cpp.GetGroupChatInvite())
|
||||
if err == nil {
|
||||
cp.eventBus.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{
|
||||
event.GroupInvite: string(jsobj),
|
||||
}))
|
||||
} else {
|
||||
log.Errorf("error serializing group: %v", err)
|
||||
}
|
||||
return cpp.GroupChatInvite.GetGroupName(), nil
|
||||
}
|
||||
cp.eventBus.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{
|
||||
event.GroupInvite: string(data),
|
||||
}))
|
||||
} else {
|
||||
log.Errorf("error decoding group invite: %v", err)
|
||||
}
|
||||
} else {
|
||||
err = errors.New("unsupported exported group type")
|
||||
return nil
|
||||
}
|
||||
return
|
||||
return errors.New("unsupported exported group type")
|
||||
}
|
||||
|
||||
// ExportGroup serializes a group invite so it can be given offline
|
||||
|
@ -125,7 +126,7 @@ func (cp *cwtchPeer) ExportGroup(groupID string) (string, error) {
|
|||
if group != nil {
|
||||
invite, err := group.Invite(group.GetInitialMessage())
|
||||
if err == nil {
|
||||
exportedInvite := "torv3" + base64.StdEncoding.EncodeToString(cp.Profile.Ed25519PublicKey) + base64.StdEncoding.EncodeToString(invite)
|
||||
exportedInvite := "torv3" + base64.StdEncoding.EncodeToString(invite)
|
||||
return exportedInvite, err
|
||||
}
|
||||
}
|
||||
|
@ -342,18 +343,18 @@ func (cp *cwtchPeer) eventHandler() {
|
|||
for {
|
||||
ev := cp.queue.Next()
|
||||
switch ev.EventType {
|
||||
/***** Default auto handled events *****/
|
||||
|
||||
case event.EncryptedGroupMessage:
|
||||
ok, groupID, message, seen := cp.Profile.AttemptDecryption([]byte(ev.Data[event.Ciphertext]), []byte(ev.Data[event.Signature]))
|
||||
if ok && !seen {
|
||||
cp.eventBus.Publish(event.NewEvent(event.NewMessageFromGroup, map[event.Field]string{event.TimestampReceived: message.Received.Format(time.RFC3339Nano), event.TimestampSent: message.Timestamp.Format(time.RFC3339Nano), event.Data: message.Message, event.GroupID: groupID, event.Signature: string(message.Signature), event.PreviousSignature: string(message.PreviousMessageSig), event.RemotePeer: message.PeerID}))
|
||||
}
|
||||
|
||||
/***** Non default but requestable handlable events *****/
|
||||
|
||||
case event.NewGroupInvite:
|
||||
var groupInvite protocol.GroupChatInvite
|
||||
err := proto.Unmarshal([]byte(ev.Data[event.GroupInvite]), &groupInvite)
|
||||
if err != nil {
|
||||
log.Errorf("NewGroupInvite could not json decode invite: %v\n", err)
|
||||
}
|
||||
cp.Profile.ProcessInvite(&groupInvite, ev.Data[event.RemotePeer])
|
||||
cp.Profile.ProcessInvite(ev.Data[event.GroupInvite], ev.Data[event.RemotePeer])
|
||||
case event.PeerStateChange:
|
||||
if _, exists := cp.Profile.Contacts[ev.Data[event.RemotePeer]]; exists {
|
||||
cp.Profile.Contacts[ev.Data[event.RemotePeer]].State = ev.Data[event.ConnectionState]
|
||||
|
@ -364,6 +365,7 @@ func (cp *cwtchPeer) eventHandler() {
|
|||
group.State = ev.Data[event.ConnectionState]
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if ev.EventType != "" {
|
||||
log.Errorf("peer event handler received an event it was not subscribed for: %v", ev.EventType)
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -332,12 +331,7 @@ func (e *engine) sendMessageToGroup(server string, ct []byte, sig []byte) {
|
|||
func (e *engine) handlePeerMessage(hostname string, context string, message []byte) {
|
||||
log.Debugf("New message from peer: %v %v", hostname, context)
|
||||
if context == event.ContextInvite {
|
||||
cpp := &protocol.CwtchPeerPacket{}
|
||||
err := proto.Unmarshal(message, cpp)
|
||||
if err == nil && cpp.GetGroupChatInvite() != nil {
|
||||
marshal, _ := proto.Marshal(cpp.GetGroupChatInvite())
|
||||
e.eventManager.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: hostname, event.GroupInvite: string(marshal)}))
|
||||
}
|
||||
e.eventManager.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: hostname, event.GroupInvite: string(message)}))
|
||||
} else {
|
||||
e.eventManager.Publish(event.NewEvent(event.NewMessageFromPeer, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: hostname, event.Data: string(message)}))
|
||||
}
|
||||
|
|
|
@ -3,10 +3,8 @@ package storage
|
|||
import (
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/protocol"
|
||||
"encoding/json"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
@ -212,15 +210,14 @@ func (ps *profileStore) eventHandler() {
|
|||
log.Errorf("error accepting group invite")
|
||||
}
|
||||
case event.NewGroupInvite:
|
||||
var gci protocol.GroupChatInvite
|
||||
err := proto.Unmarshal([]byte(ev.Data[event.GroupInvite]), &gci)
|
||||
gid, err := ps.profile.ProcessInvite(ev.Data[event.GroupInvite], ev.Data[event.RemotePeer])
|
||||
log.Errorf("gid: %v err:%v\n", gid, err)
|
||||
if err == nil {
|
||||
ps.profile.ProcessInvite(&gci, ev.Data[event.RemotePeer])
|
||||
ps.save()
|
||||
group := ps.profile.Groups[gci.GetGroupName()]
|
||||
group := ps.profile.Groups[gid]
|
||||
ps.streamStores[group.GroupID] = NewStreamStore(ps.directory, group.LocalID, ps.password)
|
||||
} else {
|
||||
log.Errorf("error storing new group invite: %v %v", ev, err)
|
||||
log.Errorf("error storing new group invite: %v (%v)", err, ev)
|
||||
}
|
||||
case event.NewMessageFromGroup:
|
||||
groupid := ev.Data[event.GroupID]
|
||||
|
|
|
@ -2,8 +2,6 @@ package storage
|
|||
|
||||
import (
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/cwtch/protocol"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -32,9 +30,6 @@ func TestProfileStoreWriteRead(t *testing.T) {
|
|||
t.Errorf("Creating group invite: %v\n", err)
|
||||
}
|
||||
|
||||
packet := protocol.CwtchPeerPacket{}
|
||||
proto.Unmarshal(invite, &packet)
|
||||
invite, _ = proto.Marshal(packet.GetGroupChatInvite())
|
||||
eventBus.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: ps1.GetProfileCopy(true).Onion, event.GroupInvite: string(invite)}))
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package testing
|
|||
import (
|
||||
app2 "cwtch.im/cwtch/app"
|
||||
"cwtch.im/cwtch/app/utils"
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/cwtch/event/bridge"
|
||||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/peer"
|
||||
|
@ -163,12 +164,15 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
|||
|
||||
alice := utils.WaitGetPeer(app, "alice")
|
||||
fmt.Println("Alice created:", alice.GetProfile().Onion)
|
||||
alice.AutoHandleEvents([]event.Type{event.PeerStateChange, event.ServerStateChange, event.NewGroupInvite})
|
||||
|
||||
bob := utils.WaitGetPeer(app, "bob")
|
||||
fmt.Println("Bob created:", bob.GetProfile().Onion)
|
||||
bob.AutoHandleEvents([]event.Type{event.PeerStateChange, event.ServerStateChange, event.NewGroupInvite})
|
||||
|
||||
carol := utils.WaitGetPeer(appClient, "carol")
|
||||
fmt.Println("Carol created:", carol.GetProfile().Onion)
|
||||
carol.AutoHandleEvents([]event.Type{event.PeerStateChange, event.ServerStateChange, event.NewGroupInvite})
|
||||
|
||||
app.LaunchPeers()
|
||||
appClient.LaunchPeers()
|
||||
|
|
Загрузка…
Ссылка в новой задаче