bifurqué depuis cwtch.im/cwtch
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).
Cette révision appartient à :
Parent
304a55aa5e
révision
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()
|
||||
|
|
Chargement…
Référencer dans un nouveau ticket