truckstop/message/matrix.go

255 lines
6.1 KiB
Go

package message
import (
"bytes"
"fmt"
"io/ioutil"
"local/truckstop/config"
"local/truckstop/logtr"
"net/http"
"regexp"
"strings"
"time"
"github.com/matrix-org/gomatrix"
)
type Matrix struct {
mock bool
homeserver string
username string
token string
room string
continuation string
}
func NewSOSMatrix() logtr.SOSer {
conf := config.Get().Log.SOSMatrix
return newMatrix(conf, "0")
}
func NewMatrix() Matrix {
conf := config.Get().Message.Matrix
return newMatrix(conf, GetMatrixContinuation())
}
func newMatrix(conf config.Matrix, cont string) Matrix {
return Matrix{
homeserver: conf.Homeserver,
username: conf.Username,
token: conf.Token,
room: conf.Room,
mock: conf.Mock,
continuation: cont,
}
}
func (m Matrix) getclient() (*gomatrix.Client, error) {
return gomatrix.NewClient(m.homeserver, m.username, m.token)
}
func (m Matrix) Continuation() string {
return m.continuation
}
func (m *Matrix) Receive() ([]Message, error) {
if m.mock {
logtr.Infof("matrix.Receive()")
messages := make([]Message, 0)
for k := range config.Get().Clients {
messages = append(messages, Message{Timestamp: time.Now(), Sender: k, Content: "!state nc"})
if k == "bel" {
messages = append(messages, Message{Timestamp: time.Now(), Sender: k, Content: "!help"})
}
if k == "broc" {
messages = append(messages, Message{Timestamp: time.Now(), Sender: k, Content: "!available 2148-10-" + fmt.Sprint(time.Now().Unix()%28)})
}
}
return messages, nil
}
clients := config.Get().Clients
matrixIDs := map[string]struct{}{}
for k := range clients {
matrixIDs[clients[k].IDs.Matrix] = struct{}{}
}
if len(matrixIDs) == 0 {
return nil, nil
}
c, err := m.getclient()
if err != nil {
return nil, err
}
messages := make([]Message, 0)
result, err := c.Messages(m.room, "999999999999999999", m.Continuation(), 'b', 50)
if err != nil {
return nil, err
}
logtr.Debugf("%s => {Start:%s End:%v};; %v, (%d)", m.Continuation(), result.Start, result.End, err, len(result.Chunk))
m.continuation = result.End
for _, event := range result.Chunk {
logtr.Verbosef("matrix event: %+v", event)
if _, ok := matrixIDs[event.Sender]; !ok {
continue
}
switch event.Type {
case "m.room.message":
b, ok := event.Body()
if ok {
messages = append(messages, Message{Timestamp: time.Unix(0, event.Timestamp*int64(time.Millisecond)), Sender: event.Sender, Content: strings.TrimSpace(b)})
}
}
}
clientChange := regexp.MustCompile("@[a-z]+$")
logtr.Debugf("rewriting messages based on @abc")
for i := range messages {
if found := clientChange.FindString(messages[i].Content); found != "" {
messages[i].Content = strings.TrimSpace(strings.ReplaceAll(messages[i].Content, found, ""))
messages[i].Sender = found[1:]
} else {
for k, v := range config.Get().Clients {
if v.IDs.Matrix == messages[i].Sender {
messages[i].Sender = k
}
}
}
messages[i].Content = strings.TrimSpace(messages[i].Content)
}
logtr.Debugf("rewriting messages based on ! CoMmAnD ...")
for i := range messages {
if strings.HasPrefix(messages[i].Content, "!") {
messages[i].Content = "!" + strings.TrimSpace(messages[i].Content[1:])
splits := strings.Split(messages[i].Content, " ")
messages[i].Content = strings.ToLower(splits[0]) + " " + strings.Join(splits, " ")
}
}
return messages, nil
}
func (m Matrix) Remove(id string) error {
if m.mock {
logtr.Infof("matrix.Remove(%s)", id)
return nil
}
c, err := m.getclient()
if err != nil {
return err
}
_, err = c.RedactEvent(m.room, id, &gomatrix.ReqRedact{Reason: "stale"})
return err
}
func (m Matrix) Update(id, text string) error {
if m.mock {
logtr.Infof("matrix.Update(%s)", text)
return nil
}
c, err := m.getclient()
if err != nil {
return err
}
type MRelatesTo struct {
EventID string `json:"event_id"`
RelType string `json:"rel_type"`
}
type NewContent struct {
Body string `json:"body"`
MsgType string `json:"msgtype"`
}
type RelatesToRoomMessage struct {
Body string `json:"body"`
MsgType string `json:"msgtype"`
MRelatesTo MRelatesTo `json:"m.relates_to"`
MNewContent NewContent `json:"m.new_content"`
}
_, err = c.SendMessageEvent(m.room, "m.room.message", RelatesToRoomMessage{
Body: "",
MsgType: "m.text",
MNewContent: NewContent{
Body: text,
MsgType: "m.text",
},
MRelatesTo: MRelatesTo{
EventID: id,
RelType: "m.replace",
},
})
return err
}
func (m Matrix) Send(text string) error {
_, err := m.SendTracked(text)
return err
}
func (m Matrix) SendTracked(text string) (string, error) {
if m.mock {
logtr.Infof("matrix.SendTracked(%s)", text)
return "", nil
}
c, err := m.getclient()
if err != nil {
return "", err
}
resp, err := c.SendText(m.room, text)
if err != nil {
return "", err
}
return resp.EventID, nil
}
func (m Matrix) SendImage(uri string) error {
_, err := m.SendImageTracked(uri)
return err
}
func (m Matrix) SendImageTracked(uri string) (string, error) {
if m.mock {
logtr.Infof("matrix.SendImage(%s)", uri)
return "", nil
}
response, err := http.Get(uri)
if err != nil {
return "", err
}
if response.StatusCode != http.StatusOK {
b, _ := ioutil.ReadAll(response.Body)
response.Body.Close()
return "", fmt.Errorf("failed to get %s: (%d) %s", uri, response.StatusCode, b)
}
b, err := ioutil.ReadAll(response.Body)
response.Body.Close()
if err != nil {
return "", err
}
c, err := m.getclient()
if err != nil {
return "", err
}
mediaUpload, err := c.UploadToContentRepo(bytes.NewReader(b), "image/jpeg", int64(len(b)))
if err != nil {
return "", err
}
publicURI := mediaUpload.ContentURI
resp, err := c.SendImage(m.room, "img", publicURI)
logtr.Debugf("sent image %s => %s: %+v", uri, publicURI, resp)
return resp.EventID, err
}
func SetMatrixContinuation(continuation string) {
db := config.Get().DB()
db.Set(getMatrixContinuationKey(), []byte(continuation))
}
func GetMatrixContinuation() string {
db := config.Get().DB()
b, _ := db.Get(getMatrixContinuationKey())
if b == nil {
return "0"
}
return string(b)
}
func getMatrixContinuationKey() string {
return "matrix_continuation"
}