626 lines
20 KiB
Go
626 lines
20 KiB
Go
package main
|
|
|
|
import (
|
|
app2 "cwtch.im/cwtch/app"
|
|
"cwtch.im/cwtch/event"
|
|
peer2 "cwtch.im/cwtch/peer"
|
|
|
|
"bytes"
|
|
"cwtch.im/cwtch/model"
|
|
"fmt"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
|
"github.com/c-bata/go-prompt"
|
|
"golang.org/x/crypto/ssh/terminal"
|
|
"os"
|
|
"os/user"
|
|
"path"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
var app app2.Application
|
|
var peer peer2.CwtchPeer
|
|
var group *model.Group
|
|
var groupFollowBreakChan chan bool
|
|
var prmpt string
|
|
|
|
var suggestionsBase = []prompt.Suggest{
|
|
{Text: "/new-profile", Description: "create a new profile"},
|
|
{Text: "/load-profiles", Description: "loads profiles with a password"},
|
|
{Text: "/list-profiles", Description: "list active profiles"},
|
|
{Text: "/select-profile", Description: "selects an active profile to use"},
|
|
{Text: "/help", Description: "print list of commands"},
|
|
{Text: "/quit", Description: "quit cwtch"},
|
|
}
|
|
|
|
var suggestionsSelectedProfile = []prompt.Suggest{
|
|
{Text: "/info", Description: "show user info"},
|
|
{Text: "/list-contacts", Description: "retrieve a list of contacts"},
|
|
{Text: "/list-groups", Description: "retrieve a list of groups"},
|
|
{Text: "/new-group", Description: "create a new group on a server"},
|
|
{Text: "/select-group", Description: "selects a group to follow"},
|
|
{Text: "/unselect-group", Description: "stop following the current group"},
|
|
{Text: "/invite", Description: "invite a new contact"},
|
|
{Text: "/invite-to-group", Description: "invite an existing contact to join an existing group"},
|
|
{Text: "/accept-invite", Description: "accept the invite of a group"},
|
|
/*{Text: "/list-servers", Description: "retrieve a list of servers and their connection status"},
|
|
{Text: "/list-peers", Description: "retrieve a list of peers and their connection status"},*/
|
|
{Text: "/export-group", Description: "export a group invite: prints as a string"},
|
|
{Text: "/trust", Description: "trust a peer"},
|
|
{Text: "/block", Description: "block a peer - you will no longer see messages or connect to this peer"},
|
|
}
|
|
|
|
var suggestions = suggestionsBase
|
|
|
|
var usages = map[string]string{
|
|
"/new-profile": "/new-profile [name]",
|
|
"/load-profiles": "/load-profiles",
|
|
"/list-profiles": "",
|
|
"/select-profile": "/select-profile [onion]",
|
|
"/quit": "",
|
|
/* "/list-servers": "",
|
|
"/list-peers": "",*/
|
|
"/list-contacts": "",
|
|
"/list-groups": "",
|
|
"/select-group": "/select-group [groupid]",
|
|
"/unselect-group": "",
|
|
"/export-group": "/export-group [groupid]",
|
|
"/info": "",
|
|
"/send": "/send [groupid] [message]",
|
|
"/timeline": "/timeline [groupid]",
|
|
"/accept-invite": "/accept-invite [groupid]",
|
|
"/invite": "/invite [peerid]",
|
|
"/invite-to-group": "/invite-to-group [groupid] [peerid]",
|
|
"/new-group": "/new-group [server]",
|
|
"/help": "",
|
|
"/trust": "/trust [peerid]",
|
|
"/block": "/block [peerid]",
|
|
}
|
|
|
|
func printMessage(m model.Message) {
|
|
p := peer.GetContact(m.PeerID)
|
|
name := "unknown"
|
|
if p != nil {
|
|
name = p.Name
|
|
} else if peer.GetProfile().Onion == m.PeerID {
|
|
name = peer.GetProfile().Name
|
|
}
|
|
|
|
fmt.Printf("%v %v (%v): %v\n", m.Timestamp, name, m.PeerID, m.Message)
|
|
}
|
|
|
|
func startGroupFollow() {
|
|
groupFollowBreakChan = make(chan bool)
|
|
go func() {
|
|
for {
|
|
l := len(group.Timeline.GetMessages())
|
|
select {
|
|
case <-time.After(1 * time.Second):
|
|
if group == nil {
|
|
return
|
|
}
|
|
gms := group.Timeline.GetMessages()
|
|
if len(gms) != l {
|
|
fmt.Printf("\n")
|
|
for ; l < len(gms); l++ {
|
|
printMessage(gms[l])
|
|
}
|
|
fmt.Printf(prmpt)
|
|
}
|
|
case <-groupFollowBreakChan:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func stopGroupFollow() {
|
|
if group != nil {
|
|
groupFollowBreakChan <- true
|
|
group = nil
|
|
}
|
|
}
|
|
|
|
func completer(d prompt.Document) []prompt.Suggest {
|
|
|
|
var s []prompt.Suggest
|
|
|
|
if d.FindStartOfPreviousWord() == 0 {
|
|
return prompt.FilterHasPrefix(suggestions, d.GetWordBeforeCursor(), true)
|
|
}
|
|
|
|
w := d.CurrentLine()
|
|
|
|
// Suggest a profile id
|
|
if strings.HasPrefix(w, "/select-profile") {
|
|
s = []prompt.Suggest{}
|
|
peerlist := app.ListPeers()
|
|
for onion, peername := range peerlist {
|
|
s = append(s, prompt.Suggest{Text: onion, Description: peername})
|
|
}
|
|
}
|
|
|
|
if peer == nil {
|
|
return s
|
|
}
|
|
|
|
// Suggest groupid
|
|
if /*strings.HasPrefix(w, "send") || strings.HasPrefix(w, "timeline") ||*/ strings.HasPrefix(w, "/export-group") || strings.HasPrefix(w, "/select-group") {
|
|
s = []prompt.Suggest{}
|
|
groups := peer.GetGroups()
|
|
for _, groupID := range groups {
|
|
group := peer.GetGroup(groupID)
|
|
s = append(s, prompt.Suggest{Text: group.GroupID, Description: "Group owned by " + group.Owner + " on " + group.GroupServer})
|
|
}
|
|
return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true)
|
|
}
|
|
|
|
// Suggest unaccepted group
|
|
if strings.HasPrefix(w, "/accept-invite") {
|
|
s = []prompt.Suggest{}
|
|
groups := peer.GetGroups()
|
|
for _, groupID := range groups {
|
|
group := peer.GetGroup(groupID)
|
|
if group.Accepted == false {
|
|
s = append(s, prompt.Suggest{Text: group.GroupID, Description: "Group owned by " + group.Owner + " on " + group.GroupServer})
|
|
}
|
|
}
|
|
return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true)
|
|
}
|
|
|
|
// suggest groupid AND peerid
|
|
if strings.HasPrefix(w, "/invite-to-group") {
|
|
|
|
if d.FindStartOfPreviousWordWithSpace() == 0 {
|
|
s = []prompt.Suggest{}
|
|
groups := peer.GetGroups()
|
|
for _, groupID := range groups {
|
|
group := peer.GetGroup(groupID)
|
|
if group.Owner == "self" {
|
|
s = append(s, prompt.Suggest{Text: group.GroupID, Description: "Group owned by " + group.Owner + " on " + group.GroupServer})
|
|
}
|
|
}
|
|
return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true)
|
|
}
|
|
|
|
s = []prompt.Suggest{}
|
|
contacts := peer.GetContacts()
|
|
for _, onion := range contacts {
|
|
contact := peer.GetContact(onion)
|
|
s = append(s, prompt.Suggest{Text: contact.Onion, Description: contact.Name})
|
|
}
|
|
return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true)
|
|
}
|
|
|
|
// Suggest contact onion / peerid
|
|
if strings.HasPrefix(w, "/block") || strings.HasPrefix(w, "/trust") || strings.HasPrefix(w, "/invite") {
|
|
s = []prompt.Suggest{}
|
|
contacts := peer.GetContacts()
|
|
for _, onion := range contacts {
|
|
contact := peer.GetContact(onion)
|
|
s = append(s, prompt.Suggest{Text: contact.Onion, Description: contact.Name})
|
|
}
|
|
return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true)
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func handleAppEvents(em event.Manager) {
|
|
queue := event.NewQueue()
|
|
em.Subscribe(event.NewPeer, queue)
|
|
em.Subscribe(event.PeerError, queue)
|
|
|
|
for {
|
|
ev := queue.Next()
|
|
switch ev.EventType {
|
|
case event.NewPeer:
|
|
onion := ev.Data[event.Identity]
|
|
p := app.GetPeer(onion)
|
|
app.LaunchPeers()
|
|
|
|
fmt.Printf("\nLoaded profile %v (%v)\n", p.GetProfile().Name, p.GetProfile().Onion)
|
|
suggestions = append(suggestionsBase, suggestionsSelectedProfile...)
|
|
|
|
profiles := app.ListPeers()
|
|
fmt.Printf("\n%v profiles active now\n", len(profiles))
|
|
fmt.Printf("You should run `select-profile` to use a profile or `list-profiles` to view loaded profiles\n")
|
|
case event.PeerError:
|
|
err := ev.Data[event.Error]
|
|
fmt.Printf("\nError creating profile: %v\n", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
|
|
cwtch :=
|
|
`
|
|
#, #'
|
|
@@@@@@:
|
|
@@@@@@.
|
|
@'@@+#' @@@@+
|
|
''''''@ #+@ :
|
|
@''''+;+' . '
|
|
@''@' :+' , ; ##, +'
|
|
,@@ ;' #'#@''. #''@''#
|
|
# ''''''#:,,#'''''@
|
|
: @''''@ :+'''@
|
|
' @;+'@ @'#
|
|
.:# '#..# '# @
|
|
@@@@@@
|
|
@@@@@@
|
|
'@@@@
|
|
@# . .
|
|
+++, #'@+'@
|
|
''', ''''''#
|
|
.#+# ''', @'''+,
|
|
@''# ''', .#@
|
|
:; '@''# .;. ''', ' : ;. ,
|
|
@+'''@ '+'+ @++ @+'@+''''+@ #+'''#: ''';#''+@ @@@@ @@@@@@@@@ :@@@@#
|
|
#''''''# +''. +'': +'''''''''+ @'''''''# '''+'''''@ @@@@ @@@@@@@@@@@@@@@@:
|
|
@'''@@'''@ @''# ,'''@ ''+ @@''+#+ :'''@@+''' ''''@@'''' @@@@ @@@@@@@@@@@@@@@@@
|
|
'''# @''# +''@ @'''# ;''@ +''+ @''@ ,+'', '''@ #'''. @@@@ @@@@ '@@@# @@@@
|
|
;''' @@; '''# #'@'' @''@ @''+ +''# .@@ ''', '''. @@@@ @@@ @@@ .@@@
|
|
@''# #'' ''#''#@''. #''# '''. '''. +'', @@@@ @@@ @@@ @@@
|
|
@''# @''@'' #'@+'+ #''# '''. ''', +'', +@@@.@@@ @@@@ @@@, @@@ ,@@@
|
|
;''+ @, +''@'# @'+''@ @''# +''; '+ ''', +'', @@@@@@@@# @@@@ @@@. .@@@ .@@@
|
|
'''# ++'+ ''''@ ,''''# #''' @''@ '@''+ ''', ''', @@@@@@@@: @@@@ @@@; .@@@' ;@@@
|
|
@'''@@'''@ #'''. +'''' ;'''#@ :'''#@+''+ ''', ''', @@@@@@# @@@@ @@@+ ,@@@. @@@@
|
|
#''''''# @''+ @''+ +'''' @'''''''# ''', ''', #@@@. @@@@ @@@+ @@@ @@@@
|
|
@+''+@ '++@ ;++@ '#''@ ##'''@: +++, +++, :@ @@@@ @@@' @@@ '@@@
|
|
:' ' '`
|
|
fmt.Printf("%v\n\n", cwtch)
|
|
quit := false
|
|
|
|
usr, err := user.Current()
|
|
if err != nil {
|
|
log.Errorf("\nError: could not load current user: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
acn, err := connectivity.StartTor(path.Join(usr.HomeDir, ".cwtch"), "")
|
|
if err != nil {
|
|
log.Errorf("\nError connecting to Tor: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
app = app2.NewApp(acn, path.Join(usr.HomeDir, ".cwtch"))
|
|
go handleAppEvents(app.GetPrimaryBus())
|
|
if err != nil {
|
|
log.Errorf("Error initializing application: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
log.SetLevel(log.LevelDebug)
|
|
fmt.Printf("\nWelcome to Cwtch!\n")
|
|
fmt.Printf("If this if your first time you should create a profile by running `/new-profile`\n")
|
|
fmt.Printf("`/load-profiles` will prompt you for a password and load profiles from storage\n")
|
|
fmt.Printf("`/help` will show you other available commands\n")
|
|
fmt.Printf("There is full [TAB] completion support\n\n")
|
|
|
|
var history []string
|
|
for !quit {
|
|
|
|
prmpt = "cwtch> "
|
|
if group != nil {
|
|
prmpt = fmt.Sprintf("cwtch %v (%v) [%v] say> ", peer.GetProfile().Name, peer.GetProfile().Onion, group.GroupID)
|
|
} else if peer != nil {
|
|
prmpt = fmt.Sprintf("cwtch %v (%v)> ", peer.GetProfile().Name, peer.GetProfile().Onion)
|
|
}
|
|
|
|
text := prompt.Input(prmpt, completer, prompt.OptionSuggestionBGColor(prompt.Purple),
|
|
prompt.OptionDescriptionBGColor(prompt.White),
|
|
prompt.OptionPrefixTextColor(prompt.White),
|
|
prompt.OptionInputTextColor(prompt.Purple),
|
|
prompt.OptionHistory(history))
|
|
|
|
commands := strings.Split(text[0:], " ")
|
|
history = append(history, text)
|
|
|
|
if peer == nil {
|
|
if commands[0] != "/help" && commands[0] != "/quit" && commands[0] != "/new-profile" && commands[0] != "/load-profiles" && commands[0] != "/select-profile" && commands[0] != "/list-profiles" {
|
|
fmt.Printf("Profile needs to be set\n")
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Send
|
|
if group != nil && !strings.HasPrefix(commands[0], "/") {
|
|
err := peer.SendMessageToGroup(group.GroupID, text)
|
|
if err != nil {
|
|
fmt.Printf("Error sending message: %v\n", err)
|
|
}
|
|
}
|
|
|
|
switch commands[0] {
|
|
case "/quit":
|
|
quit = true
|
|
case "/new-profile":
|
|
if len(commands) == 2 {
|
|
name := strings.Trim(commands[1], " ")
|
|
if name == "" {
|
|
fmt.Printf("Error creating profile, usage: %v\n", usages[commands[0]])
|
|
break
|
|
}
|
|
|
|
fmt.Print("** WARNING: PASSWORDS CANNOT BE RECOVERED! **\n")
|
|
|
|
password := ""
|
|
failcount := 0
|
|
for ; failcount < 3; failcount++ {
|
|
fmt.Print("Enter a password to encrypt the profile: ")
|
|
bytePassword, _ := terminal.ReadPassword(int(syscall.Stdin))
|
|
if string(bytePassword) == "" {
|
|
fmt.Print("\nBlank password not allowed.")
|
|
continue
|
|
}
|
|
fmt.Print("\nRe-enter password: ")
|
|
bytePassword2, _ := terminal.ReadPassword(int(syscall.Stdin))
|
|
if bytes.Equal(bytePassword, bytePassword2) {
|
|
password = string(bytePassword)
|
|
break
|
|
} else {
|
|
fmt.Print("\nPASSWORDS DIDN'T MATCH! Try again.\n")
|
|
}
|
|
}
|
|
|
|
if failcount >= 3 {
|
|
fmt.Printf("Error creating profile for %v: Your password entries must match!\n", name)
|
|
} else {
|
|
app.CreatePeer(name, password)
|
|
}
|
|
} else {
|
|
fmt.Printf("Error creating New Profile, usage: %s\n", usages[commands[0]])
|
|
}
|
|
case "/load-profiles":
|
|
fmt.Print("Enter a password to decrypt the profile: ")
|
|
bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
|
|
|
|
if err != nil {
|
|
fmt.Printf("\nError loading profiles: %v\n", err)
|
|
continue
|
|
}
|
|
|
|
app.LoadProfiles(string(bytePassword))
|
|
|
|
if err == nil {
|
|
|
|
} else {
|
|
fmt.Printf("\nError loading profiles: %v\n", err)
|
|
}
|
|
|
|
case "/list-profiles":
|
|
peerlist := app.ListPeers()
|
|
for onion, peername := range peerlist {
|
|
fmt.Printf(" %v\t%v\n", onion, peername)
|
|
}
|
|
case "/select-profile":
|
|
if len(commands) == 2 {
|
|
p := app.GetPeer(commands[1])
|
|
if p == nil {
|
|
fmt.Printf("Error: profile '%v' does not exist\n", commands[1])
|
|
} else {
|
|
stopGroupFollow()
|
|
peer = p
|
|
suggestions = append(suggestionsBase, suggestionsSelectedProfile...)
|
|
}
|
|
|
|
// Auto cwtchPeer / Join Server
|
|
// TODO There are some privacy implications with this that we should
|
|
// think over.
|
|
for _, name := range p.GetProfile().GetContacts() {
|
|
profile := p.GetContact(name)
|
|
if profile.Trusted && !profile.Blocked {
|
|
p.PeerWithOnion(profile.Onion)
|
|
}
|
|
}
|
|
|
|
for _, groupid := range p.GetGroups() {
|
|
group := p.GetGroup(groupid)
|
|
if group.Accepted || group.Owner == "self" {
|
|
p.JoinServer(group.GroupServer)
|
|
}
|
|
}
|
|
|
|
} else {
|
|
fmt.Printf("Error selecting profile, usage: %s\n", usages[commands[0]])
|
|
}
|
|
case "/info":
|
|
if peer != nil {
|
|
fmt.Printf("Address cwtch:%v\n", peer.GetProfile().Onion)
|
|
} else {
|
|
fmt.Printf("Profile needs to be set\n")
|
|
}
|
|
case "/invite":
|
|
if len(commands) == 2 {
|
|
fmt.Printf("Inviting cwtch:%v\n", commands[1])
|
|
peer.PeerWithOnion(commands[1])
|
|
} else {
|
|
fmt.Printf("Error inviting peer, usage: %s\n", usages[commands[0]])
|
|
}
|
|
/*case "/list-peers":
|
|
peers := peer.GetPeers()
|
|
for p, s := range peers {
|
|
fmt.Printf("Name: %v Status: %v\n", p, connections.ConnectionStateName[s])
|
|
}
|
|
case "/list-servers":
|
|
servers := peer.GetServers()
|
|
for s, st := range servers {
|
|
fmt.Printf("Name: %v Status: %v\n", s, connections.ConnectionStateName[st])
|
|
}*/
|
|
case "/list-contacts":
|
|
contacts := peer.GetContacts()
|
|
for _, onion := range contacts {
|
|
c := peer.GetContact(onion)
|
|
fmt.Printf("Name: %v Onion: %v Trusted: %v\n", c.Name, c.Onion, c.Trusted)
|
|
}
|
|
case "/list-groups":
|
|
for _, gid := range peer.GetGroups() {
|
|
g := peer.GetGroup(gid)
|
|
fmt.Printf("Group Id: %v Owner: %v Accepted:%v\n", gid, g.Owner, g.Accepted)
|
|
}
|
|
case "/trust":
|
|
if len(commands) == 2 {
|
|
peer.TrustPeer(commands[1])
|
|
} else {
|
|
fmt.Printf("Error trusting peer, usage: %s\n", usages[commands[0]])
|
|
}
|
|
case "/block":
|
|
if len(commands) == 2 {
|
|
peer.BlockPeer(commands[1])
|
|
} else {
|
|
fmt.Printf("Error blocking peer, usage: %s\n", usages[commands[0]])
|
|
}
|
|
case "/accept-invite":
|
|
if len(commands) == 2 {
|
|
groupID := commands[1]
|
|
err := peer.AcceptInvite(groupID)
|
|
if err != nil {
|
|
fmt.Printf("Error: %v\n", err)
|
|
} else {
|
|
group := peer.GetGroup(groupID)
|
|
if group == nil {
|
|
fmt.Printf("Error: group does not exist\n")
|
|
} else {
|
|
peer.JoinServer(group.GroupServer)
|
|
}
|
|
}
|
|
} else {
|
|
fmt.Printf("Error accepting invite, usage: %s\n", usages[commands[0]])
|
|
}
|
|
case "/invite-to-group":
|
|
if len(commands) == 3 {
|
|
fmt.Printf("Inviting %v to %v\n", commands[1], commands[2])
|
|
err := peer.InviteOnionToGroup(commands[2], commands[1])
|
|
if err != nil {
|
|
fmt.Printf("Error: %v\n", err)
|
|
}
|
|
} else {
|
|
fmt.Printf("Error inviting peer to group, usage: %s\n", usages[commands[0]])
|
|
}
|
|
case "/new-group":
|
|
if len(commands) == 2 && commands[1] != "" {
|
|
fmt.Printf("Setting up a new group on server:%v\n", commands[1])
|
|
id, _, err := peer.StartGroup(commands[1])
|
|
if err == nil {
|
|
fmt.Printf("New Group [%v] created for server %v\n", id, commands[1])
|
|
group := peer.GetGroup(id)
|
|
if group == nil {
|
|
fmt.Printf("Error: group does not exist\n")
|
|
} else {
|
|
peer.JoinServer(group.GroupServer)
|
|
}
|
|
} else {
|
|
fmt.Printf("Error creating new group: %v", err)
|
|
}
|
|
} else {
|
|
fmt.Printf("Error creating a new group, usage: %s\n", usages[commands[0]])
|
|
}
|
|
case "/select-group":
|
|
if len(commands) == 2 {
|
|
g := peer.GetGroup(commands[1])
|
|
if g == nil {
|
|
fmt.Printf("Error: group %s not found!\n", commands[1])
|
|
} else {
|
|
stopGroupFollow()
|
|
group = g
|
|
|
|
fmt.Printf("--------------- %v ---------------\n", group.GroupID)
|
|
gms := group.Timeline.GetMessages()
|
|
max := 20
|
|
if len(gms) < max {
|
|
max = len(gms)
|
|
}
|
|
for i := len(gms) - max; i < len(gms); i++ {
|
|
printMessage(gms[i])
|
|
}
|
|
fmt.Printf("------------------------------\n")
|
|
|
|
startGroupFollow()
|
|
}
|
|
} else {
|
|
fmt.Printf("Error selecting a group, usage: %s\n", usages[commands[0]])
|
|
}
|
|
case "/unselect-group":
|
|
stopGroupFollow()
|
|
case "/export-group":
|
|
if len(commands) == 2 {
|
|
group := peer.GetGroup(commands[1])
|
|
if group == nil {
|
|
fmt.Printf("Error: group does not exist\n")
|
|
} else {
|
|
invite, _ := peer.ExportGroup(commands[1])
|
|
fmt.Printf("Invite: %v\n", invite)
|
|
}
|
|
} else {
|
|
fmt.Printf("Error exporting group, usage: %s\n", usages[commands[0]])
|
|
}
|
|
case "/import-group":
|
|
if len(commands) == 2 {
|
|
err := peer.ImportGroup(commands[1])
|
|
if err != nil {
|
|
fmt.Printf("Error importing group: %v\n", err)
|
|
} else {
|
|
fmt.Printf("Imported group!\n")
|
|
}
|
|
} else {
|
|
fmt.Printf("%v", commands)
|
|
fmt.Printf("Error importing group, usage: %s\n", usages[commands[0]])
|
|
}
|
|
case "/help":
|
|
for _, command := range suggestions {
|
|
fmt.Printf("%-18s%-56s%s\n", command.Text, command.Description, usages[command.Text])
|
|
}
|
|
case "/sendlots":
|
|
if len(commands) == 2 {
|
|
group := peer.GetGroup(commands[1])
|
|
if group == nil {
|
|
fmt.Printf("Error: group does not exist\n")
|
|
} else {
|
|
for i := 0; i < 100; i++ {
|
|
fmt.Printf("Sending message: %v\n", i)
|
|
err := peer.SendMessageToGroup(commands[1], fmt.Sprintf("this is message %v", i))
|
|
if err != nil {
|
|
fmt.Printf("could not send message %v because %v\n", i, err)
|
|
}
|
|
}
|
|
fmt.Printf("Waiting 5 seconds for message to process...\n")
|
|
time.Sleep(time.Second * 5)
|
|
timeline := group.GetTimeline()
|
|
totalLatency := time.Duration(0)
|
|
maxLatency := time.Duration(0)
|
|
totalMessages := 0
|
|
for i := 0; i < 100; i++ {
|
|
found := false
|
|
for _, m := range timeline {
|
|
if m.Message == fmt.Sprintf("this is message %v", i) && m.PeerID == peer.GetProfile().Onion {
|
|
found = true
|
|
latency := m.Received.Sub(m.Timestamp)
|
|
fmt.Printf("Latency for Message %v was %v\n", i, latency)
|
|
totalLatency = totalLatency + latency
|
|
if maxLatency < latency {
|
|
maxLatency = latency
|
|
}
|
|
totalMessages++
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
fmt.Printf("message %v was never received\n", i)
|
|
}
|
|
}
|
|
|
|
fmt.Printf("Average Latency for %v messages was: %vms\n", totalMessages, time.Duration(int64(totalLatency)/int64(totalMessages)))
|
|
fmt.Printf("Max Latency for %v messages was: %vms\n", totalMessages, maxLatency)
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
app.Shutdown()
|
|
acn.Close()
|
|
os.Exit(0)
|
|
}
|