fixup! treewide: upgrading to latest mautrix standards
Signed-off-by: Sumner Evans <sumner@beeper.com>
This commit is contained in:
208
messagetracking.go
Normal file
208
messagetracking.go
Normal file
@@ -0,0 +1,208 @@
|
||||
// mautrix-groupme - A Matrix-GroupMe puppeting bridge.
|
||||
// Copyright (C) 2022 Sumner Evans, Karmanyaah Malhotra
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
log "maunium.net/go/maulogger/v2"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/bridge/status"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
var (
|
||||
errMessageTakingLong = errors.New("bridging the message is taking longer than usual")
|
||||
)
|
||||
|
||||
func errorToStatusReason(err error) (reason event.MessageStatusReason, status event.MessageStatus, isCertain, sendNotice bool, humanMessage string) {
|
||||
switch {
|
||||
case errors.Is(err, errMessageTakingLong):
|
||||
return event.MessageStatusTooOld, event.MessageStatusPending, false, true, err.Error()
|
||||
default:
|
||||
return event.MessageStatusGenericError, event.MessageStatusRetriable, false, true, ""
|
||||
}
|
||||
}
|
||||
|
||||
func (portal *Portal) sendErrorMessage(evt *event.Event, err error, msgType string, confirmed bool, editID id.EventID) id.EventID {
|
||||
if !portal.bridge.Config.Bridge.MessageErrorNotices {
|
||||
return ""
|
||||
}
|
||||
certainty := "may not have been"
|
||||
if confirmed {
|
||||
certainty = "was not"
|
||||
}
|
||||
msg := fmt.Sprintf("\u26a0 Your %s %s bridged: %v", msgType, certainty, err)
|
||||
if errors.Is(err, errMessageTakingLong) {
|
||||
msg = fmt.Sprintf("\u26a0 Bridging your %s is taking longer than usual", msgType)
|
||||
}
|
||||
content := &event.MessageEventContent{
|
||||
MsgType: event.MsgNotice,
|
||||
Body: msg,
|
||||
}
|
||||
if editID != "" {
|
||||
content.SetEdit(editID)
|
||||
} else {
|
||||
content.SetReply(evt)
|
||||
}
|
||||
resp, err := portal.sendMainIntentMessage(content)
|
||||
if err != nil {
|
||||
portal.log.Warnfln("Failed to send bridging error message:", err)
|
||||
return ""
|
||||
}
|
||||
return resp.EventID
|
||||
}
|
||||
|
||||
func (portal *Portal) sendStatusEvent(evtID, lastRetry id.EventID, err error) {
|
||||
if !portal.bridge.Config.Bridge.MessageStatusEvents {
|
||||
return
|
||||
}
|
||||
if lastRetry == evtID {
|
||||
lastRetry = ""
|
||||
}
|
||||
intent := portal.bridge.Bot
|
||||
if !portal.Encrypted {
|
||||
// Bridge bot isn't present in unencrypted DMs
|
||||
intent = portal.MainIntent()
|
||||
}
|
||||
content := event.BeeperMessageStatusEventContent{
|
||||
Network: portal.getBridgeInfoStateKey(),
|
||||
RelatesTo: event.RelatesTo{
|
||||
Type: event.RelReference,
|
||||
EventID: evtID,
|
||||
},
|
||||
LastRetry: lastRetry,
|
||||
}
|
||||
if err == nil {
|
||||
content.Status = event.MessageStatusSuccess
|
||||
} else {
|
||||
content.Reason, content.Status, _, _, content.Message = errorToStatusReason(err)
|
||||
content.Error = err.Error()
|
||||
}
|
||||
content.FillLegacyBooleans()
|
||||
_, err = intent.SendMessageEvent(portal.MXID, event.BeeperMessageStatus, &content)
|
||||
if err != nil {
|
||||
portal.log.Warnln("Failed to send message status event:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (portal *Portal) sendDeliveryReceipt(eventID id.EventID) {
|
||||
if portal.bridge.Config.Bridge.DeliveryReceipts {
|
||||
err := portal.bridge.Bot.MarkRead(portal.MXID, eventID)
|
||||
if err != nil {
|
||||
portal.log.Debugfln("Failed to send delivery receipt for %s: %v", eventID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (portal *Portal) sendMessageMetrics(evt *event.Event, err error, part string, ms *metricSender) {
|
||||
var msgType string
|
||||
switch evt.Type {
|
||||
case event.EventMessage:
|
||||
msgType = "message"
|
||||
case event.EventReaction:
|
||||
msgType = "reaction"
|
||||
case event.EventRedaction:
|
||||
msgType = "redaction"
|
||||
default:
|
||||
msgType = "unknown event"
|
||||
}
|
||||
evtDescription := evt.ID.String()
|
||||
if evt.Type == event.EventRedaction {
|
||||
evtDescription += fmt.Sprintf(" of %s", evt.Redacts)
|
||||
}
|
||||
origEvtID := evt.ID
|
||||
if retryMeta := evt.Content.AsMessage().MessageSendRetry; retryMeta != nil {
|
||||
origEvtID = retryMeta.OriginalEventID
|
||||
}
|
||||
if err != nil {
|
||||
level := log.LevelError
|
||||
if part == "Ignoring" {
|
||||
level = log.LevelDebug
|
||||
}
|
||||
portal.log.Logfln(level, "%s %s %s from %s: %v", part, msgType, evtDescription, evt.Sender, err)
|
||||
reason, statusCode, isCertain, sendNotice, _ := errorToStatusReason(err)
|
||||
checkpointStatus := status.ReasonToCheckpointStatus(reason, statusCode)
|
||||
portal.bridge.SendMessageCheckpoint(evt, status.MsgStepRemote, err, checkpointStatus, ms.getRetryNum())
|
||||
if sendNotice {
|
||||
ms.setNoticeID(portal.sendErrorMessage(evt, err, msgType, isCertain, ms.getNoticeID()))
|
||||
}
|
||||
portal.sendStatusEvent(origEvtID, evt.ID, err)
|
||||
} else {
|
||||
portal.log.Debugfln("Handled Matrix %s %s", msgType, evtDescription)
|
||||
portal.sendDeliveryReceipt(evt.ID)
|
||||
portal.bridge.SendMessageSuccessCheckpoint(evt, status.MsgStepRemote, ms.getRetryNum())
|
||||
portal.sendStatusEvent(origEvtID, evt.ID, nil)
|
||||
if prevNotice := ms.popNoticeID(); prevNotice != "" {
|
||||
_, _ = portal.MainIntent().RedactEvent(portal.MXID, prevNotice, mautrix.ReqRedact{
|
||||
Reason: "error resolved",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type metricSender struct {
|
||||
portal *Portal
|
||||
previousNotice id.EventID
|
||||
lock sync.Mutex
|
||||
completed bool
|
||||
retryNum int
|
||||
}
|
||||
|
||||
func (ms *metricSender) getRetryNum() int {
|
||||
if ms != nil {
|
||||
return ms.retryNum
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ms *metricSender) getNoticeID() id.EventID {
|
||||
if ms == nil {
|
||||
return ""
|
||||
}
|
||||
return ms.previousNotice
|
||||
}
|
||||
|
||||
func (ms *metricSender) popNoticeID() id.EventID {
|
||||
if ms == nil {
|
||||
return ""
|
||||
}
|
||||
evtID := ms.previousNotice
|
||||
ms.previousNotice = ""
|
||||
return evtID
|
||||
}
|
||||
|
||||
func (ms *metricSender) setNoticeID(evtID id.EventID) {
|
||||
if ms != nil && ms.previousNotice == "" {
|
||||
ms.previousNotice = evtID
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *metricSender) sendMessageMetrics(evt *event.Event, err error, part string, completed bool) {
|
||||
ms.lock.Lock()
|
||||
defer ms.lock.Unlock()
|
||||
if !completed && ms.completed {
|
||||
return
|
||||
}
|
||||
ms.portal.sendMessageMetrics(evt, err, part, ms)
|
||||
ms.retryNum++
|
||||
ms.completed = completed
|
||||
}
|
||||
Reference in New Issue
Block a user