From 45b50e47339726195d90d273fd642516b9a48bc7 Mon Sep 17 00:00:00 2001 From: Steve Date: Sun, 17 Jul 2022 18:15:29 -0400 Subject: [PATCH] switch to botlib, waitgroups --- client.go | 48 ------------------------- config.go | 50 -------------------------- go.mod | 7 ++-- go.sum | 12 ++++--- lazystore.go | 85 -------------------------------------------- main.go | 99 ++++++++++++++++++++++++++-------------------------- nun.go | 57 ++++++++++++++++++++++++++++++ reddit.go | 13 +++---- 8 files changed, 124 insertions(+), 247 deletions(-) delete mode 100644 client.go delete mode 100644 config.go delete mode 100644 lazystore.go create mode 100644 nun.go diff --git a/client.go b/client.go deleted file mode 100644 index 916ab40..0000000 --- a/client.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "fmt" - "log" - "strings" - - "maunium.net/go/mautrix" - "maunium.net/go/mautrix/id" -) - -func newMatrixClient(config *botConfig) *mautrix.Client { - fmt.Println("Logging into", config.Homeserver, "as", config.Username) - var client *mautrix.Client - var err error - //make sure username is lower case otherwise token login breaks - uname := strings.ToLower(config.Username) - if config.Token == "" { - client, err = mautrix.NewClient(config.Homeserver, "", "") - if err != nil { - panic(err) - } - } else { - log.Println("using token login") - client, err = mautrix.NewClient(config.Homeserver, id.NewUserID(uname, config.Domain), config.Token) - if err != nil { - panic(err) - } - } - client.Store = NewLazyMemStore(config.Statefile) - if config.Token == "" { - loginRes, err := client.Login(&mautrix.ReqLogin{ - Type: "m.login.password", - Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: uname}, - Password: config.Password, - StoreCredentials: true, - }) - if err != nil { - panic(err) - } - config.Token = loginRes.AccessToken - log.Println("Login succesful, saving access_token to config file") - writeConfig(config) - } else { - log.Println("skipping login since token provided") - } - return client -} diff --git a/config.go b/config.go deleted file mode 100644 index 2c91b5a..0000000 --- a/config.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "io/ioutil" - "log" - "os" - - "gopkg.in/yaml.v2" -) - -type botConfig struct { - Homeserver string `yaml:"homeserver"` - Domain string `yaml:"domain"` - Dimension string `yaml:"dimension"` - Username string `yaml:"username"` - Password string `yaml:"password"` - Statefile string `yaml:"statefile"` - Token string `yaml:"token"` - filename string -} - -func loadConfig(filename string) *botConfig { - yamlFile, err := ioutil.ReadFile(filename) - cnf := &botConfig{} - if err == nil { - err = yaml.Unmarshal(yamlFile, cnf) - } else { - panic(err) - } - if err != nil { - panic(err) - } - cnf.filename = filename - return cnf -} - -func writeConfig(cnf *botConfig) { - file, err := os.OpenFile(cnf.filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - log.Fatalf("error opening/creating file: %v", err) - } - defer file.Close() - - enc := yaml.NewEncoder(file) - - err = enc.Encode(cnf) - if err != nil { - log.Fatalf("error encoding: %v", err) - } -} diff --git a/go.mod b/go.mod index 88a2deb..ab6787f 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,13 @@ module git.saintnet.tech/stryan/nunbot go 1.18 require ( - gopkg.in/yaml.v2 v2.4.0 + git.saintnet.tech/stryan/matrixbotlib v0.1.2 maunium.net/go/mautrix v0.11.0 ) require ( github.com/stretchr/testify v1.8.0 // indirect - golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 // indirect - golang.org/x/net v0.0.0-20220513224357-95641704303c // indirect + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect + golang.org/x/net v0.0.0-20220708220712-1185a9018129 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index b954c48..a9877c4 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +git.saintnet.tech/stryan/matrixbotlib v0.1.1 h1:8TbWEtgmt6GH2MjZZa9938DClJgvh6aEdY7Knj4Cpmk= +git.saintnet.tech/stryan/matrixbotlib v0.1.1/go.mod h1:Id9JBCt3YqOViUwSVXQomGysaWPVyoDk2fbmOf9L264= +git.saintnet.tech/stryan/matrixbotlib v0.1.2 h1:kmldNm1xyKm2t8vPUqyQ+7AvhQ0CeT/mpIhTCOvehh8= +git.saintnet.tech/stryan/matrixbotlib v0.1.2/go.mod h1:Id9JBCt3YqOViUwSVXQomGysaWPVyoDk2fbmOf9L264= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -8,10 +12,10 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 h1:NUzdAbFtCJSXU20AOXgeqaUwg8Ypg4MPYmL+d+rsB5c= -golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/net v0.0.0-20220513224357-95641704303c h1:nF9mHSvoKBLkQNQhJZNsc66z2UzAMUbLGjC95CF3pU0= -golang.org/x/net v0.0.0-20220513224357-95641704303c/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= +golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/lazystore.go b/lazystore.go deleted file mode 100644 index 713e5fc..0000000 --- a/lazystore.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "bytes" - "encoding/gob" - - "os" - - "maunium.net/go/mautrix" - "maunium.net/go/mautrix/id" -) - -type LazyMemStore struct { - mem *mautrix.InMemoryStore - NextBatch map[id.UserID]string - saveFile string -} - -func NewLazyMemStore(fileloc string) *LazyMemStore { - return &LazyMemStore{ - mem: mautrix.NewInMemoryStore(), - NextBatch: make(map[id.UserID]string), - saveFile: fileloc, - } -} - -func (l *LazyMemStore) SaveFilterID(userID id.UserID, filterID string) { - l.mem.SaveFilterID(userID, filterID) -} - -func (l *LazyMemStore) LoadFilterID(userID id.UserID) string { - return l.mem.LoadFilterID(userID) -} - -func (l *LazyMemStore) SaveNextBatch(userID id.UserID, nextBatchToken string) { - b := new(bytes.Buffer) - l.NextBatch[userID] = nextBatchToken - e := gob.NewEncoder(b) - err := e.Encode(l.NextBatch) - if err != nil { - panic(err) - } - if err := os.WriteFile(l.saveFile, b.Bytes(), 0666); err != nil { - panic(err) - } - -} - -func (l *LazyMemStore) LoadNextBatch(userID id.UserID) string { - - dat, err := os.ReadFile(l.saveFile) - if err != nil { - if os.IsNotExist(err) { - b := new(bytes.Buffer) - e := gob.NewEncoder(b) - err := e.Encode(l.NextBatch) - if err != nil { - panic(err) - } - if err := os.WriteFile(l.saveFile, b.Bytes(), 0666); err != nil { - panic(err) - } - dat, err = os.ReadFile(l.saveFile) - if err != nil { - panic(err) - } - } else { - panic(err) - } - } - d := gob.NewDecoder(bytes.NewBuffer(dat)) - err = d.Decode(&l.NextBatch) - if err != nil { - panic(err) - } - return l.NextBatch[userID] -} - -func (l *LazyMemStore) SaveRoom(room *mautrix.Room) { - l.mem.SaveRoom(room) -} - -func (l *LazyMemStore) LoadRoom(roomID id.RoomID) *mautrix.Room { - return l.mem.LoadRoom(roomID) -} diff --git a/main.go b/main.go index e81caa3..501ddc9 100644 --- a/main.go +++ b/main.go @@ -1,11 +1,12 @@ package main import ( - "fmt" "log" - "strings" - "time" + "os" + "os/signal" + "sync" + mbl "git.saintnet.tech/stryan/matrixbotlib" "maunium.net/go/mautrix" "maunium.net/go/mautrix/event" ) @@ -17,24 +18,29 @@ var GitTag string var GitCommit string func main() { - conf := loadConfig("config.yaml") - matrixClient := newMatrixClient(conf) - //redditClient := newRedditClient(conf) + conf, err := mbl.LoadMatrixClientConfig("config.yaml") + if err != nil { + panic(err) + } + store := mbl.NewLazyMemStore(conf.Statefile) + matrixClient, err := mbl.NewMatrixClient(conf, store) + if err != nil { + panic(err) + } syncer := matrixClient.Syncer.(*mautrix.DefaultSyncer) + mbl.AcceptAllRoomInvites(matrixClient) syncer.OnEventType(event.EventMessage, func(source mautrix.EventSource, evt *event.Event) { if evt.Sender == matrixClient.UserID { return //ignore events from self } - fmt.Printf("<%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body) - body := evt.Content.AsMessage().Body - bodyS := strings.Split(body, " ") - if bodyS[0] != "!nun" { + cmd, err := mbl.ParseCommand(evt, "nun") + if err != nil { + if err != mbl.ErrCmdParseNoPrefix { + log.Printf("invalid command: %v", err) + } return } - if len(bodyS) < 2 { - return //nothing to parse - } - switch bodyS[1] { + switch cmd[1] { case "version": // print version if GitTag != "" { @@ -43,48 +49,43 @@ func main() { matrixClient.SendText(evt.RoomID, "NunBot version "+GitCommit) } case "help": - matrixClient.SendText(evt.RoomID, "Supported commands: version,stats") + matrixClient.SendText(evt.RoomID, "Supported commands: version, help") default: //command not found matrixClient.SendText(evt.RoomID, "command not recognized") } }) - syncer.OnEventType(event.StateMember, func(source mautrix.EventSource, evt *event.Event) { - fmt.Printf("<%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body) - if evt.Content.AsMember().Membership.IsInviteOrJoin() { - _, err := matrixClient.JoinRoomByID(evt.RoomID) - if err != nil { - fmt.Printf("error joining room %v", evt.RoomID) - } else { - fmt.Printf("joined room %v", evt.RoomID) - } - } - }) - - var curPost post + var wg sync.WaitGroup + stop := make(chan bool) + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) go func() { - for { - time.Sleep(30 * time.Second) - newPost := getNewestPost("LittleNuns") - if curPost.Title != newPost.Title { - curPost = newPost - roomResp, err := matrixClient.JoinedRooms() - if err != nil { - log.Printf("error getting joined rooms: %v", err) - continue - } - rooms := roomResp.JoinedRooms - for _, room := range rooms { - matrixClient.SendText(room, fmt.Sprintf("%v\n%v", curPost.Title, curPost.Link)) - } - - } - } + <-c + log.Println("trying to shutdown cleanly") + matrixClient.StopSync() + stop <- true }() - err := matrixClient.Sync() - if err != nil { - panic(err) - } + nun := newNunWatch(stop, matrixClient) + wg.Add(1) + go func() { + err = matrixClient.Sync() + if err != nil { + log.Println(err) + } + wg.Done() + log.Println("matrix client shutdown") + + }() + + wg.Add(1) + go func() { + nun.Main() + wg.Done() + log.Println("nun watch shutdown") + }() + log.Println("nunbot running") + wg.Wait() + log.Println("shutting down") } diff --git a/nun.go b/nun.go new file mode 100644 index 0000000..6a14ba4 --- /dev/null +++ b/nun.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + "log" + "time" + + "maunium.net/go/mautrix" +) + +type nunWatch struct { + fail int + curPost post + stop chan bool + client *mautrix.Client +} + +func newNunWatch(stop chan bool, c *mautrix.Client) *nunWatch { + return &nunWatch{0, post{}, stop, c} +} + +func (n *nunWatch) Main() { + ticker := time.NewTicker(30 * time.Second) + + for { + select { + case <-n.stop: + return + case <-ticker.C: + if n.fail > 5 { + log.Fatal("fail count too high; ending loop") + return + } + newPost, err := getNewestPost("LittleNuns") + if err != nil { + log.Printf("error getting newest post: %v", err) + log.Println("skipping this cycle, incrementing fail count") + n.fail++ + continue + } + n.fail = 0 + if n.curPost.Title != newPost.Title { + n.curPost = newPost + roomResp, err := n.client.JoinedRooms() + if err != nil { + log.Printf("error getting joined rooms: %v", err) + continue + } + rooms := roomResp.JoinedRooms + for _, room := range rooms { + n.client.SendText(room, fmt.Sprintf("%v\n%v", n.curPost.Title, n.curPost.Link)) + } + + } + } + } +} diff --git a/reddit.go b/reddit.go index 4042179..d9a37b6 100644 --- a/reddit.go +++ b/reddit.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "log" "net/http" "time" ) @@ -15,7 +14,7 @@ type post struct { Link string } -func getNewestPost(subreddit string) post { +func getNewestPost(subreddit string) (post, error) { var resp redditResp //building request from scratch because reddit api is weird url := fmt.Sprintf("https://www.reddit.com/r/%v/new.json?sort=new&limit=1", subreddit) @@ -29,26 +28,24 @@ func getNewestPost(subreddit string) post { } res, err := defaultClient.Do(req) if err != nil { - log.Printf("Got %v, retrying in 5s", err) time.Sleep(5 * time.Second) res, err = http.Get(url) if err != nil { - log.Printf("Got %v, not trying again", err) - return post{} + return post{}, err } } defer res.Body.Close() body, readErr := ioutil.ReadAll(res.Body) if readErr != nil { - log.Fatalf("error reading reddit resp: %v", readErr) + return post{}, err } err = json.Unmarshal(body, &resp) if err != nil { - log.Fatalf("unmarshal error: %v", err) + return post{}, err } return post{ Title: resp.Data.Children[0].Data.Title, Link: resp.Data.Children[0].Data.URL, - } + }, nil } -- 2.45.2