package alicebot import ( "crypto/rand" "git.openprivacy.ca/openprivacy/libricochet-go" "git.openprivacy.ca/openprivacy/libricochet-go/channels" "git.openprivacy.ca/openprivacy/libricochet-go/connection" "git.openprivacy.ca/openprivacy/libricochet-go/connectivity" "git.openprivacy.ca/openprivacy/libricochet-go/identity" "git.openprivacy.ca/openprivacy/libricochet-go/log" "golang.org/x/crypto/ed25519" "os" "time" ) // NewAliceBot creates a new AliceBot and establishes a connection to the given onion server. func NewAliceBot(acn connectivity.ACN, onion string) AliceBot { alice := new(alicebot) alice.messages = make(map[uint32]string) var err error alice.pub, alice.priv, err = ed25519.GenerateKey(rand.Reader) if err != nil { log.Errorf("[alice] error generating key: %v", err) os.Exit(1) } rc, err := goricochet.Open(acn, onion) if err != nil { log.Errorf("[alice] error connecting to echobot: %v", err) os.Exit(1) } _, err = connection.HandleOutboundConnection(rc).ProcessAuthAsV3Client(identity.InitializeV3("alice", &alice.priv, &alice.pub)) if err != nil { log.Errorf("[alice] failed to authenticate connection: %v", err) os.Exit(1) } alice.rc = rc ach := connection.AutoConnectionHandler{} ach.Init() ach.RegisterChannelHandler("im.ricochet.chat", func() channels.Handler { chat := new(channels.ChatChannel) chat.Handler = alice return chat }) go alice.rc.Process(&ach) log.Infof("[alice] requesting channel...") alice.rc.Do(func() error { chatchannel := channels.ChatChannel{} chatchannel.Handler = alice _, err := alice.rc.RequestOpenChannel("im.ricochet.chat", &chatchannel) if err != nil { log.Errorf("failed requestopenchannel: %v", err) os.Exit(1) } return nil }) return alice } type alicebot struct { messages map[uint32]string pub ed25519.PublicKey priv ed25519.PrivateKey mID int rc *connection.Connection } // AliceBot is an interface for alicebot, allowing callers to send and receive messages. type AliceBot interface { channels.ChatChannelHandler SendMessage(string) } // SendMessage can be called to send a message to EchoBot func (ab *alicebot) SendMessage(message string) { // The following code opens (or creates) a new im.ricochet.chat channel to the connected service // and sends a message. log.Infof("[alice] sending...") ab.rc.Do(func() error { channel := ab.rc.Channel("im.ricochet.chat", channels.Outbound) id, err := channels.SendMessageOnChatChannel(channel, message) if err == nil { ab.messages[id] = message } return err }) } // OpenInbound is called when EchoBot attempts to open a channel with AliceBot func (ab *alicebot) OpenInbound() { log.Infof("[alice] inbound connection established") } // ChatMessage is called whenever AliceBot receives a message from EchoBot func (ab *alicebot) ChatMessage(messageID uint32, when time.Time, message string) bool { log.Infof("[alice] got message from echobot: %s", message) return true } // ChatMessageAck is called whenever AliceBot received an acknowledgement of a previously sent message. func (ab *alicebot) ChatMessageAck(messageID uint32, accepted bool) { log.Infof("[alice] message \"%s\" ack'd", ab.messages[messageID]) }