commit 0c0aeb29440a04cd570d0b1a086040ffeee732f5 Author: Robert Gerus Date: Mon Jan 3 22:27:52 2022 +0100 initial working version diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..03dcbe1 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module code.hackerspace.pl/ar/notbot + +go 1.16 + +require gopkg.in/irc.v3 v3.1.4 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..12af36d --- /dev/null +++ b/go.sum @@ -0,0 +1,13 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/irc.v3 v3.1.4 h1:DYGMRFbtseXEh+NadmMUFzMraqyuUj4I3iWYFEzDZPc= +gopkg.in/irc.v3 v3.1.4/go.mod h1:shO2gz8+PVeS+4E6GAny88Z0YVVQSxQghdrMVGQsR9s= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/notifier.go b/notifier.go new file mode 100644 index 0000000..de98f39 --- /dev/null +++ b/notifier.go @@ -0,0 +1,179 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net" + "net/http" + "time" + + "gopkg.in/irc.v3" +) + +type atMonitor struct { + previousUserList []string +} + +type atUsers struct { + Login string + Timestamp float64 + PrettyTime string `json:"pretty_time"` +} + +type atResponse struct { + Users []atUsers + Esps uint + Kektops uint + Vms uint + Unknown uint +} + +func (a *atResponse) UserList() (ret []string) { + for _, user := range a.Users { + ret = append(ret, user.Login) + } + + return ret +} + +func listSubstract(a, b []string) (ret []string) { + mb := make(map[string]bool, len(b)) + + for _, x := range b { + mb[x] = true + } + + for _, x := range a { + if _, found := mb[x]; !found { + ret = append(ret, x) + } + } + + return ret +} + +func (a *atMonitor) Run(c *irc.Client, done chan bool) { + ticker := time.NewTicker(10 * time.Second) + + for { + select { + case <-done: + return + case <-ticker.C: + var diffText string + atHS, err := at() + + if err != nil { + log.Println(err) + break + } + + current := atHS.UserList() + + arrived := listSubstract(current, a.previousUserList) + left := listSubstract(a.previousUserList, current) + + if len(arrived) > 0 { + diffText = fmt.Sprint(" +", arrived) + } + + if len(left) > 0 { + diffText = fmt.Sprint(" -", left) + } + + if len(diffText) > 0 { + msg := fmt.Sprintf("NOTICE #hswaw-bottest :%s\n", diffText) + log.println(diffText) + c.Write(msg) + a.previousUserList = current + } + } + } +} + +func genericHandler(c *irc.Client, m *irc.Message) { + log.Println(m) + if m.Command == "001" { + c.Write("JOIN #hswaw-bottest") + } +} + +func main() { + done := make(chan bool) + var a atMonitor + + conn, err := net.Dial("tcp", "irc.libera.chat:6667") + if err != nil { + log.Fatalln(err) + } + + config := irc.ClientConfig{ + Nick: "notbot", + Pass: "***", + User: "bot", + Name: "notbot", + Handler: irc.HandlerFunc(genericHandler), + } + + client := irc.NewClient(conn, config) + go a.Run(client, done) + + err = client.Run() + if err != nil { + done <- true + log.Fatalln(err) + } +} + +func at() (at atResponse, err error) { + var values atResponse = atResponse{} + + data, err := httpGet("https://at.hackerspace.pl/api") + if err != nil { + return values, fmt.Errorf("Unable to access checkinator api:", err) + } + + err = json.Unmarshal(data, &values) + if err != nil { + return values, fmt.Errorf("Unable to decode checkinator response:", err) + } + + return values, nil +} + +func httpGet(link string) ([]byte, error) { + var buf []byte + tr := &http.Transport{ + TLSHandshakeTimeout: 20 * time.Second, + ResponseHeaderTimeout: 20 * time.Second, + } + client := &http.Client{ + Transport: tr, + } + + req, err := http.NewRequest("GET", link, nil) + if err != nil { + return []byte{}, err + } + + resp, err := client.Do(req) + if err != nil { + return []byte{}, err + } + defer resp.Body.Close() + + // Limit response to 5MiB + limitedResponse := http.MaxBytesReader(nil, resp.Body, 5*1024*1024) + buf = make([]byte, 5*1024*1024) + + i, err := io.ReadFull(limitedResponse, buf) + if err == io.ErrUnexpectedEOF { + buf = buf[:i] + } else if err != nil { + return []byte{}, err + } + + return buf, nil +}