package main import ( "crypto/rand" "git.openprivacy.ca/openprivacy/libricochet-go/application" "git.openprivacy.ca/openprivacy/libricochet-go/application/examples/echobot/alicebot" "git.openprivacy.ca/openprivacy/libricochet-go/channels" "git.openprivacy.ca/openprivacy/libricochet-go/identity" "git.openprivacy.ca/openprivacy/log" "time" "git.openprivacy.ca/openprivacy/connectivity/tor" "golang.org/x/crypto/ed25519" "os" ) // EchoBotInstance is an Instance of the EchoBot Application. One is created for every connected peer. type EchoBotInstance struct { rai *application.Instance ra *application.RicochetApplication } // Init establishes an EchoBotInstance func (ebi *EchoBotInstance) Init(rai *application.Instance, ra *application.RicochetApplication) { ebi.rai = rai ebi.ra = ra } // OpenInbound is called when AliceBot opens a ChatChannel. In this case, because we want EchoBot to respond we // need to open a new channel in the other direction. func (ebi *EchoBotInstance) OpenInbound() { log.Debugln("OpenInbound() ChatChannel handler called...") outboutChatChannel := ebi.rai.Connection.Channel("im.ricochet.chat", channels.Outbound) if outboutChatChannel == nil { ebi.rai.Connection.Do(func() error { ebi.rai.Connection.RequestOpenChannel("im.ricochet.chat", &channels.ChatChannel{ Handler: ebi, }) return nil }) } } // ChatMessage is called whenever a connected peer sends a message to EchoBot func (ebi *EchoBotInstance) ChatMessage(messageID uint32, when time.Time, message string) bool { log.Infof("message from %v - %v", ebi.rai.RemoteHostname, message) ebi.SendChatMessage(ebi.rai, ebi.rai.RemoteHostname+" "+message) return true } // ChatMessageAck is called whenever a connected peer acknowledges a message that EchoBot sent. func (ebi *EchoBotInstance) ChatMessageAck(messageID uint32, accepted bool) { } // SendChatMessage sends a chat message to the given echobot instance func (ebi *EchoBotInstance) SendChatMessage(rai *application.Instance, message string) { ebi.rai.Connection.Do(func() error { channel := ebi.rai.Connection.Channel("im.ricochet.chat", channels.Outbound) // We are swallowing the message id and the error here, in reality you will want to handle it. channels.SendMessageOnChatChannel(channel, message) return nil }) } // main() encapsulates an entire ricochet ecosystem, from starting tor, to generating onion service keys to managing // the launching of both the echobot server and the alicebot peer. // In most systems you will only be handling one or two of these subsystems at any given time so this example might seem // bloated, but we have tried to highlight the most interesting aspects to allow easy application to new domains. func main() { // Set up Logging. log.SetLevel(log.LevelInfo) log.AddEverythingFromPattern("connectivity") // Set up Tor acn, err := tor.NewTorACN(".", "") if err != nil { log.Errorf("Unable to start Tor: %v", err) os.Exit(1) } defer acn.Close() // Set up the Echobot Server echobot := new(application.RicochetApplication) cpubk, cprivk, err := ed25519.GenerateKey(rand.Reader) if err != nil { log.Errorf("Error generating keys: %v", err) os.Exit(1) } // Turn on the echobot onion service in Tor. listenService, err := acn.Listen(cprivk, application.RicochetPort) if err != nil { log.Errorf("error setting up onion service: %v", err) os.Exit(1) } // This next section looks complicated (and it is a little), but all it is doing is allowing echobot to handle // im.ricochet.chat type channels. af := application.InstanceFactory{} af.Init() af.AddHandler("im.ricochet.chat", func(rai *application.Instance) func() channels.Handler { ebi := new(EchoBotInstance) ebi.Init(rai, echobot) return func() channels.Handler { chat := new(channels.ChatChannel) chat.Handler = ebi return chat } }) // Thee next few lines turn on echobot and make it available to listen to new connections. // Note that we initialize a V3 identity for echobot. echobot.Init(acn, "echobot", identity.InitializeV3("echobot", &cprivk, &cpubk), af, new(application.AcceptAllContactManager)) log.Infof("echobot listening on %v", listenService.AddressFull()) go echobot.Run(listenService) // Now we wait a little bit for everything to wire itself together. log.Infoln("counting to five ...") time.Sleep(time.Second * 5) // Finally, in these last few lines we setup an AliceBot who simply sends messages to echobot alice := alicebot.NewAliceBot(acn, listenService.AddressIdentity()) alice.SendMessage("be gay") alice.SendMessage("do crime") // stick around and see what happens time.Sleep(time.Second * 30) }