diff --git a/bot/bot.go b/bot/bot.go index c34bf66..de23c68 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -1,6 +1,8 @@ package bot import ( + "regexp" + _ "github.com/mattn/go-sqlite3" "golang.org/x/exp/slog" "maunium.net/go/mautrix" @@ -27,6 +29,7 @@ type Bot struct { cryptoHelper *cryptohelper.CryptoHelper kagiCient *Kagi logger *slog.Logger + messages map[string]string } func NewBot(cfg MatrixConfig, kagi *Kagi, logger *slog.Logger) *Bot { @@ -34,6 +37,7 @@ func NewBot(cfg MatrixConfig, kagi *Kagi, logger *slog.Logger) *Bot { config: cfg, kagiCient: kagi, logger: logger, + messages: make(map[string]string), } } @@ -62,7 +66,8 @@ func (m *Bot) Init() error { if m.config.AcceptInvites { m.AddEventHandler(m.InviteHandler()) } - m.AddEventHandler(m.ResponseHandler()) + m.AddEventHandler(m.ReactionHandler()) + m.AddEventHandler(m.MessageHandler()) return nil } @@ -105,8 +110,8 @@ func (m *Bot) InviteHandler() (event.Type, mautrix.EventHandler) { } } -func (m *Bot) ResponseHandler() (event.Type, mautrix.EventHandler) { - return event.EventReaction, func(source mautrix.EventSource, evt *event.Event) { +func (m *Bot) MessageHandler() (event.Type, mautrix.EventHandler) { + return event.EventMessage, func(source mautrix.EventSource, evt *event.Event) { content := evt.Content.AsMessage() eventID := evt.ID m.logger.Info("received message", slog.String("content", content.Body)) @@ -115,23 +120,53 @@ func (m *Bot) ResponseHandler() (event.Type, mautrix.EventHandler) { m.logger.Info("message sent by bot itself, ignoring", slog.String("event_id", eventID.String())) return } + url, ok := findFirstURL(content.Body) + if !ok { + m.logger.Info("message does not contain url, ignoring", slog.String("event_id", eventID.String())) + return + } + m.messages[eventID.String()] = url + m.logger.Info("saved url", slog.String("event_id", eventID.String()), slog.String("url", url)) + } +} - // get reply from GPT - //reply, err := m.gptClient.Complete(conv) - //if err != nil { - // m.logger.Error("failed to get reply from openai", slog.String("err", err.Error()), slog.String("bot", m.config.UserDisplayName)) - // return - //} +func (m *Bot) ReactionHandler() (event.Type, mautrix.EventHandler) { + return event.EventReaction, func(source mautrix.EventSource, evt *event.Event) { + eventID := evt.ID + // ignore if the message is sent by the bot itself + if evt.Sender == id.UserID(m.config.UserID) { + m.logger.Info("message sent by bot itself, ignoring", slog.String("event_id", eventID.String())) + return + } + rel := evt.Content.AsReaction().GetRelatesTo() + relID := rel.GetAnnotationID() + relKey := rel.GetAnnotationKey() + m.logger.Info("received reaction", slog.String("event_id", eventID.String()), slog.String("rel_id", relID.String()), slog.String("rel_key", relKey)) + if relKey != `🗒️` { + m.logger.Info("reaction is not 🗒️, ignoring", slog.String("event_id", eventID.String())) + return + } - reply := "the summary" + wantedURL, ok := m.messages[relID.String()] + if !ok { + m.logger.Info("referenced message is not known or does not contain url, ignoring", slog.String("event_id", eventID.String())) + return + } + + summary, err := m.kagiCient.Summarize(wantedURL) + if err != nil { + m.logger.Error("failed to summarize", slog.String("err", err.Error())) + return + } + + reply := summary formattedReply := format.RenderMarkdown(reply, true, false) formattedReply.RelatesTo = &event.RelatesTo{ InReplyTo: &event.InReplyTo{ - EventID: eventID, + EventID: relID, }, } - _, err := m.client.SendMessageEvent(evt.RoomID, event.EventMessage, &formattedReply) - if err != nil { + if _, err := m.client.SendMessageEvent(evt.RoomID, event.EventMessage, &formattedReply); err != nil { m.logger.Error("failed to send message", slog.String("err", err.Error())) return } @@ -139,6 +174,15 @@ func (m *Bot) ResponseHandler() (event.Type, mautrix.EventHandler) { if len(reply) > 30 { reply = reply[:30] + "..." } - m.logger.Info("sent reply", slog.String("parent_id", eventID.String()), slog.String("content", reply)) + m.logger.Info("sent reply", slog.String("parent_id", eventID.String()), slog.String("msg", reply)) } } + +func findFirstURL(s string) (string, bool) { + re := regexp.MustCompile(`https?\:\/\/[^ \)]*`) + match := re.FindString(s) + if match == "" { + return "", false + } + return match, true +} diff --git a/bot/kagi.go b/bot/kagi.go index 16d2b1d..4379542 100644 --- a/bot/kagi.go +++ b/bot/kagi.go @@ -1,6 +1,7 @@ package bot import ( + "encoding/json" "fmt" "io" "net/http" @@ -26,7 +27,6 @@ func NewKagi(baseURL, apiKey string) *Kagi { func (k *Kagi) Summarize(sumURL string) (string, error) { queryURL := fmt.Sprintf("%s/summarize?engine=muriel&url=%s", k.baseURL, url.QueryEscape(sumURL)) - //queryURL = "" req, err := http.NewRequest("GET", queryURL, nil) if err != nil { return "", err @@ -37,7 +37,6 @@ func (k *Kagi) Summarize(sumURL string) (string, error) { if err != nil { return "", err } - fmt.Println(res.Status) resBody, err := io.ReadAll(res.Body) if err != nil { @@ -45,7 +44,14 @@ func (k *Kagi) Summarize(sumURL string) (string, error) { } defer res.Body.Close() - fmt.Sprintf("response: %v", string(resBody)) + resp := struct { + Data struct { + Output string + } + }{} + if err := json.Unmarshal(resBody, &resp); err != nil { + return "", err + } - return string(resBody), nil + return resp.Data.Output, nil } diff --git a/go.mod b/go.mod index ba50f9f..9ac40a1 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,15 @@ module ewintr.nl/matrix-kagisum go 1.20 +require ( + github.com/mattn/go-sqlite3 v1.14.17 + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 + maunium.net/go/mautrix v0.15.3 +) + require ( github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mattn/go-sqlite3 v1.14.17 // indirect github.com/rs/zerolog v1.29.1 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -13,9 +18,7 @@ require ( github.com/tidwall/sjson v1.2.5 // indirect github.com/yuin/goldmark v1.5.4 // indirect golang.org/x/crypto v0.10.0 // indirect - golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect golang.org/x/net v0.11.0 // indirect golang.org/x/sys v0.9.0 // indirect maunium.net/go/maulogger/v2 v2.4.1 // indirect - maunium.net/go/mautrix v0.15.3 // indirect ) diff --git a/go.sum b/go.sum index 42e77a0..813166e 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 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= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -10,12 +10,10 @@ github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6 github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -38,7 +36,6 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8= maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho= maunium.net/go/mautrix v0.15.3 h1:C9BHSUM0gYbuZmAtopuLjIcH5XHLb/ZjTEz7nN+0jN0=