From d3577e574c8ec540dc8bc900794ab27b21e67df2 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Wed, 29 Jan 2020 16:57:42 -0700 Subject: [PATCH] connecting and sending messages working --- .gitignore | 3 + errata.go | 39 +++++++++++++ errata_test.go | 16 ++++++ go.mod | 8 +++ go.sum | 14 +++++ main.go | 153 +++++++++++++++++++++++++++++++++++++++++++++++++ mcchunkie.png | Bin 0 -> 34469 bytes pass_reader.go | 18 ++++++ store.go | 150 ++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 401 insertions(+) create mode 100644 .gitignore create mode 100644 errata.go create mode 100644 errata_test.go create mode 100644 go.sum create mode 100644 main.go create mode 100755 mcchunkie.png create mode 100644 pass_reader.go create mode 100644 store.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39a0253 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.todo +*.db +mcchunkie diff --git a/errata.go b/errata.go new file mode 100644 index 0000000..b320eaf --- /dev/null +++ b/errata.go @@ -0,0 +1,39 @@ +package main + +import ( + "net/http" + + "golang.org/x/net/html" +) + +// ParseErrata grabs all of the OpenBSD errata from an html page +func ParseErrata(s string) ([]string, error) { + var data []string + resp, err := http.Get(s) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + doc, err := html.Parse(resp.Body) + if err != nil { + return nil, err + } + + var f func(*html.Node) + f = func(node *html.Node) { + if node.Type == html.ElementNode && node.Data == "strong" { + if node.FirstChild != nil { + data = append(data, node.FirstChild.Data) + return + } + } + for child := node.FirstChild; child != nil; child = child.NextSibling { + f(child) + } + } + f(doc) + + return data, nil +} diff --git a/errata_test.go b/errata_test.go new file mode 100644 index 0000000..f3379b0 --- /dev/null +++ b/errata_test.go @@ -0,0 +1,16 @@ +package main + +import ( + "testing" +) + +func TestParseErrata(t *testing.T) { + got, err := ParseErrata("https://www.openbsd.org/errata66.html") + if err != nil { + t.Error(err) + } + l := len(got) + if l == 0 { + t.Errorf("errata count %d; want > 0", l) + } +} diff --git a/go.mod b/go.mod index 14474c1..9d3eaa1 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,11 @@ module git.sr.ht/~qbit/mcchunkie go 1.13 + +require ( + github.com/matrix-org/gomatrix v0.0.0-20200128155335-9e7906b6766d + go.etcd.io/bbolt v1.3.3 + golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d + golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 + golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5403a80 --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/matrix-org/gomatrix v0.0.0-20200128155335-9e7906b6766d h1:Vf/EQgAfg8/CBUQv9te7UJreZ9iKKouB2gb8UIRM4jQ= +github.com/matrix-org/gomatrix v0.0.0-20200128155335-9e7906b6766d/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= +golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/main.go b/main.go new file mode 100644 index 0000000..bff1c12 --- /dev/null +++ b/main.go @@ -0,0 +1,153 @@ +package main + +import ( + "flag" + "fmt" + "log" + "net/http" + "time" + + "github.com/matrix-org/gomatrix" +) + +var ( + username string +) + +func sendMessage(c *gomatrix.Client, roomID, message string) error { + _, err := c.UserTyping(roomID, true, 3) + if err != nil { + return err + } + + c.SendText(roomID, message) + + _, err = c.UserTyping(roomID, false, 0) + if err != nil { + return err + } + return nil +} + +func main() { + var store, err = NewStore("mcchunkie.db") + if err != nil { + log.Fatalf("%s\n", err) + } + + var password, userID, accessToken, server string + var setup bool + + flag.StringVar(&username, "user", "", "username to connect to matrix server with") + flag.StringVar(&server, "server", "", "matrix server") + flag.BoolVar(&setup, "s", false, "setup account") + + flag.Parse() + + if server == "" { + server, _ = store.get("config", "server") + if server == "" { + log.Fatalln("please specify a server") + } + + store.set("config", "server", server) + } + + log.Printf("connecting to %s\n", server) + + cli, err := gomatrix.NewClient( + server, + "", + "", + ) + + if setup { + log.Println("requesting access token") + password, err = prompt(fmt.Sprintf("Password for '%s': ", username)) + if err != nil { + log.Fatalln(err) + } + fmt.Println() + + log.Printf("'%s' : '%s'\n", username, password) + + resp, err := cli.Login(&gomatrix.ReqLogin{ + Type: "m.login.password", + User: username, + Password: password, + }) + if err != nil { + log.Fatalln(err) + } + + store.set("account", "username", username) + store.set("account", "access_token", resp.AccessToken) + store.set("account", "user_id", resp.UserID) + } else { + accessToken, _ = store.get("account", "access_token") + userID, _ = store.get("account", "user_id") + } + + cli.SetCredentials(userID, accessToken) + cli.Store = store + syncer := gomatrix.NewDefaultSyncer(username, store) + cli.Client = http.DefaultClient + cli.Syncer = syncer + + /* + if _, err := cli.JoinRoom("!tmCVBJAeuKjCfihUjb:cobryce.com", "", nil); err != nil { + log.Fatalln(err) + } + if _, err := cli.JoinRoom("!sFPUeGfHqjiItcjNIN:matrix.org", "", nil); err != nil { + log.Fatalln(err) + } + if _, err := cli.JoinRoom("!ALCZnrYadLGSySIFZr:matrix.org", "", nil); err != nil { + log.Fatalln(err) + } + */ + if _, err := cli.JoinRoom("!LTxJpLHtShMVmlpwmZ:tapenet.org", "", nil); err != nil { + log.Fatalln(err) + } + + syncer.OnEventType("m.room.message", func(ev *gomatrix.Event) { + fmt.Printf("'%s' == '%s'\n", ev.Sender, username) + if ev.Sender == fmt.Sprintf("@%s", username) { + return + } + + if mtype, ok := ev.MessageType(); ok { + switch mtype { + case "m.text": + if post, ok := ev.Body(); ok { + log.Printf("%s: '%s'", ev.Sender, post) + } + } + } + }) + + //cli.SendText("!tmCVBJAeuKjCfihUjb:cobryce.com", "Butts") + sendMessage(cli, "!LTxJpLHtShMVmlpwmZ:tapenet.org", "Typing hi!") + sendMessage(cli, "!tmCVBJAeuKjCfihUjb:cobryce.com", "Butts") + + avatar := "https://deftly.net/mcchunkie.png" + aurl, err := cli.GetAvatarURL() + + if aurl != avatar { + log.Printf("Setting avatar to: '%s'", avatar) + err = cli.SetAvatarURL(avatar) + if err != nil { + fmt.Println("Unable to set avatar: ", err) + } + } else { + log.Printf("avatar already set") + } + + for { + log.Println("syncing..") + if err := cli.Sync(); err != nil { + fmt.Println("Sync() returned ", err) + } + + time.Sleep(1 * time.Second) + } +} diff --git a/mcchunkie.png b/mcchunkie.png new file mode 100755 index 0000000000000000000000000000000000000000..d6dedf4b26c903bbc4574c40a259cac743ac0c55 GIT binary patch literal 34469 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 Dlez?d literal 0 HcmV?d00001 diff --git a/pass_reader.go b/pass_reader.go new file mode 100644 index 0000000..bda7996 --- /dev/null +++ b/pass_reader.go @@ -0,0 +1,18 @@ +package main + +import ( + "os" + + "golang.org/x/crypto/ssh/terminal" +) + +func prompt(p string) (string, error) { + oldState, err := terminal.MakeRaw(0) + if err != nil { + panic(err) + } + defer terminal.Restore(0, oldState) + + t := terminal.NewTerminal(os.Stdin, p) + return t.ReadPassword(p) +} diff --git a/store.go b/store.go new file mode 100644 index 0000000..7d89ea6 --- /dev/null +++ b/store.go @@ -0,0 +1,150 @@ +package main + +import ( + "bytes" + "encoding/gob" + "log" + + "github.com/matrix-org/gomatrix" + bolt "go.etcd.io/bbolt" +) + +// MCStore implements a gomatrix.Storer and exposes a bbolt db to be used for +// application storage (account info, config info etc). +type MCStore struct { + db *bolt.DB +} + +// NewStore creates a new MCStore instance populated with the proper buckets. +func NewStore(path string) (*MCStore, error) { + db, err := bolt.Open(path, 0666, nil) + if err != nil { + return nil, err + } + + s := &MCStore{db: db} + + err = s.db.Update(func(tx *bolt.Tx) error { + buckets := []string{"filter", "batch", "room", "account", "config", "errata"} + for _, b := range buckets { + if _, err := tx.CreateBucketIfNotExists([]byte(b)); err != nil { + return err + } + } + return nil + }) + + if err != nil { + s.db.Close() + return nil, err + } + return s, nil +} + +func (s *MCStore) set(bucket, key, value string) error { + err := s.db.Update(func(tx *bolt.Tx) error { + bkt, err := tx.CreateBucketIfNotExists([]byte(bucket)) + if err != nil { + return err + } + + err = bkt.Put([]byte(key), []byte(value)) + if err != nil { + return err + } + return nil + }) + + if err != nil { + log.Fatal(err) + } + return nil +} + +func (s *MCStore) get(bucket, key string) (string, error) { + var result string + return result, s.db.View(func(tx *bolt.Tx) error { + bkt := tx.Bucket([]byte(bucket)) + data := bkt.Get([]byte(key)) + + if data == nil { + return nil + } + + result = string(data) + + return nil + }) +} + +func (s *MCStore) getBytes(bucket, key string) ([]byte, error) { + var result []byte + return result, s.db.View(func(tx *bolt.Tx) error { + bkt := tx.Bucket([]byte(bucket)) + data := bkt.Get([]byte(key)) + + if data == nil { + return nil + } + + result = data + return nil + }) +} + +func (s *MCStore) encodeRoom(room *gomatrix.Room) ([]byte, error) { + buf := new(bytes.Buffer) + enc := gob.NewEncoder(buf) + err := enc.Encode(room) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (s *MCStore) decodeRoom(room []byte) (*gomatrix.Room, error) { + var r *gomatrix.Room + buf := bytes.NewBuffer(room) + dec := gob.NewDecoder(buf) + err := dec.Decode(&r) + if err != nil { + return nil, err + } + return r, nil +} + +// SaveFilterID exposed for gomatrix +func (s *MCStore) SaveFilterID(userID, filterID string) { + _ = s.set("filter", userID, filterID) + +} + +// LoadFilterID exposed for gomatrix +func (s *MCStore) LoadFilterID(userID string) string { + filter, _ := s.get("filter", userID) + return string(filter) +} + +// SaveNextBatch exposed for gomatrix +func (s *MCStore) SaveNextBatch(userID, nextBatchToken string) { + _ = s.set("batch", userID, nextBatchToken) +} + +// LoadNextBatch exposed for gomatrix +func (s *MCStore) LoadNextBatch(userID string) string { + batch, _ := s.get("batch", userID) + return string(batch) +} + +// SaveRoom exposed for gomatrix +func (s *MCStore) SaveRoom(room *gomatrix.Room) { + b, _ := s.encodeRoom(room) + _ = s.set("room", room.ID, string(b)) +} + +// LoadRoom exposed for gomatrix +func (s *MCStore) LoadRoom(roomID string) *gomatrix.Room { + b, _ := s.getBytes("room", roomID) + room, _ := s.decodeRoom(b) + return room +}