fixup! treewide: upgrading to latest mautrix standards
Signed-off-by: Sumner Evans <sumner@beeper.com>
This commit is contained in:
parent
853fad9576
commit
5b3be968d2
@ -26,6 +26,8 @@ import (
|
|||||||
"maunium.net/go/mautrix/bridge/bridgeconfig"
|
"maunium.net/go/mautrix/bridge/bridgeconfig"
|
||||||
"maunium.net/go/mautrix/event"
|
"maunium.net/go/mautrix/event"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
|
"github.com/beeper/groupme-lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeferredConfig struct {
|
type DeferredConfig struct {
|
||||||
@ -184,6 +186,15 @@ type UsernameTemplateArgs struct {
|
|||||||
UserID id.UserID
|
UserID id.UserID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bc BridgeConfig) FormatDisplayname(gmid groupme.ID, member groupme.Member) string {
|
||||||
|
var buf strings.Builder
|
||||||
|
_ = bc.displaynameTemplate.Execute(&buf, map[string]string{
|
||||||
|
"Name": member.Nickname,
|
||||||
|
"GMID": gmid.String(),
|
||||||
|
})
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (bc BridgeConfig) FormatUsername(username string) string {
|
func (bc BridgeConfig) FormatUsername(username string) string {
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
_ = bc.ParsedUsernameTemplate.Execute(&buf, username)
|
_ = bc.ParsedUsernameTemplate.Execute(&buf, username)
|
||||||
|
1
main.go
1
main.go
@ -17,6 +17,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"maunium.net/go/mautrix/bridge"
|
"maunium.net/go/mautrix/bridge"
|
||||||
|
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
|
||||||
|
}
|
18
no-crypto.go
18
no-crypto.go
@ -1,18 +0,0 @@
|
|||||||
//go:build !cgo || nocrypto
|
|
||||||
// +build !cgo nocrypto
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewCryptoHelper(bridge *Bridge) Crypto {
|
|
||||||
if !bridge.Config.Bridge.Encryption.Allow {
|
|
||||||
bridge.Log.Warnln("Bridge built without end-to-bridge encryption, but encryption is enabled in config")
|
|
||||||
}
|
|
||||||
bridge.Log.Debugln("Bridge built without end-to-bridge encryption")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var NoSessionFound = errors.New("nil")
|
|
239
portal.go
239
portal.go
@ -366,7 +366,7 @@ func (portal *Portal) SyncParticipants(metadata *groupme.Group) {
|
|||||||
if user != nil {
|
if user != nil {
|
||||||
changed = levels.EnsureUserLevel(user.MXID, expectedLevel) || changed
|
changed = levels.EnsureUserLevel(user.MXID, expectedLevel) || changed
|
||||||
}
|
}
|
||||||
puppet.Sync(nil, portal.MXID, *participant) //why nil whynot
|
puppet.Sync(nil, participant, false, false)
|
||||||
}
|
}
|
||||||
if changed {
|
if changed {
|
||||||
_, err = portal.MainIntent().SetPowerLevels(portal.MXID, levels)
|
_, err = portal.MainIntent().SetPowerLevels(portal.MXID, levels)
|
||||||
@ -396,6 +396,10 @@ func (portal *Portal) SyncParticipants(metadata *groupme.Group) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user *User) updateAvatar(gmdi groupme.ID, avatarID *string, avatarURL *id.ContentURI, avatarSet *bool, log log.Logger, intent *appservice.IntentAPI) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (portal *Portal) UpdateAvatar(user *User, avatar string, updateInfo bool) bool {
|
func (portal *Portal) UpdateAvatar(user *User, avatar string, updateInfo bool) bool {
|
||||||
// if len(avatar) == 0 {
|
// if len(avatar) == 0 {
|
||||||
// var err error
|
// var err error
|
||||||
@ -670,29 +674,27 @@ type BridgeInfoContent struct {
|
|||||||
Channel BridgeInfoSection `json:"channel"`
|
Channel BridgeInfoSection `json:"channel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func (portal *Portal) getBridgeInfoStateKey() string {
|
||||||
StateBridgeInfo = event.Type{Type: "m.bridge", Class: event.StateEventType}
|
return fmt.Sprintf("com.beeper.groupme://groupme/%s", portal.Key.GMID)
|
||||||
StateHalfShotBridgeInfo = event.Type{Type: "uk.half-shot.bridge", Class: event.StateEventType}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
func (portal *Portal) getBridgeInfo() (string, BridgeInfoContent) {
|
func (portal *Portal) getBridgeInfo() (string, event.BridgeEventContent) {
|
||||||
bridgeInfo := BridgeInfoContent{
|
bridgeInfo := event.BridgeEventContent{
|
||||||
BridgeBot: portal.bridge.Bot.UserID,
|
BridgeBot: portal.bridge.Bot.UserID,
|
||||||
Creator: portal.MainIntent().UserID,
|
Creator: portal.MainIntent().UserID,
|
||||||
Protocol: BridgeInfoSection{
|
Protocol: event.BridgeInfoSection{
|
||||||
ID: "whatsapp",
|
ID: "groupme",
|
||||||
DisplayName: "WhatsApp",
|
DisplayName: "GroupMe",
|
||||||
AvatarURL: id.ContentURIString(portal.bridge.Config.AppService.Bot.Avatar),
|
AvatarURL: portal.bridge.Config.AppService.Bot.ParsedAvatar.CUString(),
|
||||||
ExternalURL: "https://www.whatsapp.com/",
|
ExternalURL: "https://www.groupme.com/",
|
||||||
},
|
},
|
||||||
Channel: BridgeInfoSection{
|
Channel: event.BridgeInfoSection{
|
||||||
ID: portal.Key.GMID.String(),
|
ID: portal.Key.GMID.String(),
|
||||||
DisplayName: portal.Name,
|
DisplayName: portal.Name,
|
||||||
AvatarURL: portal.AvatarURL.CUString(),
|
AvatarURL: portal.AvatarURL.CUString(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
bridgeInfoStateKey := fmt.Sprintf("net.maunium.whatsapp://whatsapp/%s", portal.Key.GMID)
|
return portal.getBridgeInfoStateKey(), bridgeInfo
|
||||||
return bridgeInfoStateKey, bridgeInfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) UpdateBridgeInfo() {
|
func (portal *Portal) UpdateBridgeInfo() {
|
||||||
@ -702,11 +704,12 @@ func (portal *Portal) UpdateBridgeInfo() {
|
|||||||
}
|
}
|
||||||
portal.log.Debugln("Updating bridge info...")
|
portal.log.Debugln("Updating bridge info...")
|
||||||
stateKey, content := portal.getBridgeInfo()
|
stateKey, content := portal.getBridgeInfo()
|
||||||
_, err := portal.MainIntent().SendStateEvent(portal.MXID, StateBridgeInfo, stateKey, content)
|
_, err := portal.MainIntent().SendStateEvent(portal.MXID, event.StateBridge, stateKey, content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
portal.log.Warnln("Failed to update m.bridge:", err)
|
portal.log.Warnln("Failed to update m.bridge:", err)
|
||||||
}
|
}
|
||||||
_, err = portal.MainIntent().SendStateEvent(portal.MXID, StateHalfShotBridgeInfo, stateKey, content)
|
// TODO remove this once https://github.com/matrix-org/matrix-doc/pull/2346 is in spec
|
||||||
|
_, err = portal.MainIntent().SendStateEvent(portal.MXID, event.StateHalfShotBridge, stateKey, content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
portal.log.Warnln("Failed to update uk.half-shot.bridge:", err)
|
portal.log.Warnln("Failed to update uk.half-shot.bridge:", err)
|
||||||
}
|
}
|
||||||
@ -764,12 +767,12 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
|
|||||||
Parsed: portal.GetBasePowerLevels(),
|
Parsed: portal.GetBasePowerLevels(),
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Type: StateBridgeInfo,
|
Type: event.StateBridge,
|
||||||
Content: event.Content{Parsed: bridgeInfo},
|
Content: event.Content{Parsed: bridgeInfo},
|
||||||
StateKey: &bridgeInfoStateKey,
|
StateKey: &bridgeInfoStateKey,
|
||||||
}, {
|
}, {
|
||||||
// TODO remove this once https://github.com/matrix-org/matrix-doc/pull/2346 is in spec
|
// TODO remove this once https://github.com/matrix-org/matrix-doc/pull/2346 is in spec
|
||||||
Type: StateHalfShotBridgeInfo,
|
Type: event.StateHalfShotBridge,
|
||||||
Content: event.Content{Parsed: bridgeInfo},
|
Content: event.Content{Parsed: bridgeInfo},
|
||||||
StateKey: &bridgeInfoStateKey,
|
StateKey: &bridgeInfoStateKey,
|
||||||
}}
|
}}
|
||||||
@ -911,6 +914,10 @@ func isGatewayError(err error) bool {
|
|||||||
return errors.As(err, &httpErr) && (httpErr.IsStatus(http.StatusBadGateway) || httpErr.IsStatus(http.StatusGatewayTimeout))
|
return errors.As(err, &httpErr) && (httpErr.IsStatus(http.StatusBadGateway) || httpErr.IsStatus(http.StatusGatewayTimeout))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (portal *Portal) sendMainIntentMessage(content *event.MessageEventContent) (*mautrix.RespSendEvent, error) {
|
||||||
|
return portal.sendMessage(portal.MainIntent(), event.EventMessage, content, nil, 0)
|
||||||
|
}
|
||||||
|
|
||||||
func (portal *Portal) encrypt(intent *appservice.IntentAPI, content *event.Content, eventType event.Type) (event.Type, error) {
|
func (portal *Portal) encrypt(intent *appservice.IntentAPI, content *event.Content, eventType event.Type) (event.Type, error) {
|
||||||
if !portal.Encrypted || portal.bridge.Crypto == nil {
|
if !portal.Encrypted || portal.bridge.Crypto == nil {
|
||||||
return eventType, nil
|
return eventType, nil
|
||||||
@ -958,7 +965,7 @@ func (portal *Portal) handleAttachment(intent *appservice.IntentAPI, attachment
|
|||||||
}
|
}
|
||||||
data, uploadMimeType, file := portal.encryptFile(*imgData, mime)
|
data, uploadMimeType, file := portal.encryptFile(*imgData, mime)
|
||||||
|
|
||||||
uploaded, err := portal.uploadWithRetry(intent, data, uploadMimeType, MediaUploadRetries)
|
uploaded, err := intent.UploadBytes(data, uploadMimeType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, mautrix.MTooLarge) {
|
if errors.Is(err, mautrix.MTooLarge) {
|
||||||
err = errors.New("homeserver rejected too large file")
|
err = errors.New("homeserver rejected too large file")
|
||||||
@ -1006,7 +1013,7 @@ func (portal *Portal) handleAttachment(intent *appservice.IntentAPI, attachment
|
|||||||
}
|
}
|
||||||
|
|
||||||
data, uploadMimeType, file := portal.encryptFile(vidContents, mime)
|
data, uploadMimeType, file := portal.encryptFile(vidContents, mime)
|
||||||
uploaded, err := portal.uploadWithRetry(intent, data, uploadMimeType, MediaUploadRetries)
|
uploaded, err := intent.UploadBytes(data, uploadMimeType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, mautrix.MTooLarge) {
|
if errors.Is(err, mautrix.MTooLarge) {
|
||||||
err = errors.New("homeserver rejected too large file")
|
err = errors.New("homeserver rejected too large file")
|
||||||
@ -1046,7 +1053,7 @@ func (portal *Portal) handleAttachment(intent *appservice.IntentAPI, attachment
|
|||||||
}
|
}
|
||||||
data, uploadMimeType, file := portal.encryptFile(fileData, fmime)
|
data, uploadMimeType, file := portal.encryptFile(fileData, fmime)
|
||||||
|
|
||||||
uploaded, err := portal.uploadWithRetry(intent, data, uploadMimeType, MediaUploadRetries)
|
uploaded, err := intent.UploadBytes(data, uploadMimeType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, mautrix.MTooLarge) {
|
if errors.Is(err, mautrix.MTooLarge) {
|
||||||
err = errors.New("homeserver rejected too large file")
|
err = errors.New("homeserver rejected too large file")
|
||||||
@ -1120,149 +1127,6 @@ func (portal *Portal) handleAttachment(intent *appservice.IntentAPI, attachment
|
|||||||
}
|
}
|
||||||
// return nil, true, errors.New("Unknown type")
|
// return nil, true, errors.New("Unknown type")
|
||||||
}
|
}
|
||||||
func (portal *Portal) HandleMediaMessage(source *User, msg mediaMessage) {
|
|
||||||
// intent := portal.startHandling(source, msg.info)
|
|
||||||
// if intent == nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// data, err := msg.download()
|
|
||||||
// if err == whatsapp.ErrMediaDownloadFailedWith404 || err == whatsapp.ErrMediaDownloadFailedWith410 {
|
|
||||||
// portal.log.Warnfln("Failed to download media for %s: %v. Calling LoadMediaInfo and retrying download...", msg.info.Id, err)
|
|
||||||
// _, err = source.Conn.LoadMediaInfo(msg.info.RemoteJid, msg.info.Id, msg.info.FromMe)
|
|
||||||
// if err != nil {
|
|
||||||
// portal.sendMediaBridgeFailure(source, intent, msg.info, fmt.Errorf("failed to load media info: %w", err))
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// data, err = msg.download()
|
|
||||||
// }
|
|
||||||
// if err == whatsapp.ErrNoURLPresent {
|
|
||||||
// portal.log.Debugfln("No URL present error for media message %s, ignoring...", msg.info.Id)
|
|
||||||
// return
|
|
||||||
// } else if err != nil {
|
|
||||||
// portal.sendMediaBridgeFailure(source, intent, msg.info, err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var width, height int
|
|
||||||
// if strings.HasPrefix(msg.mimeType, "image/") {
|
|
||||||
// cfg, _, _ := image.DecodeConfig(bytes.NewReader(data))
|
|
||||||
// width, height = cfg.Width, cfg.Height
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// data, uploadMimeType, file := portal.encryptFile(data, msg.mimeType)
|
|
||||||
//
|
|
||||||
// uploaded, err := portal.uploadWithRetry(intent, data, uploadMimeType, MediaUploadRetries)
|
|
||||||
// if err != nil {
|
|
||||||
// if errors.Is(err, mautrix.MTooLarge) {
|
|
||||||
// portal.sendMediaBridgeFailure(source, intent, msg.info, errors.New("homeserver rejected too large file"))
|
|
||||||
// } else if httpErr := err.(mautrix.HTTPError); httpErr.IsStatus(413) {
|
|
||||||
// portal.sendMediaBridgeFailure(source, intent, msg.info, errors.New("proxy rejected too large file"))
|
|
||||||
// } else {
|
|
||||||
// portal.sendMediaBridgeFailure(source, intent, msg.info, fmt.Errorf("failed to upload media: %w", err))
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if msg.fileName == "" {
|
|
||||||
// mimeClass := strings.Split(msg.mimeType, "/")[0]
|
|
||||||
// switch mimeClass {
|
|
||||||
// case "application":
|
|
||||||
// msg.fileName = "file"
|
|
||||||
// default:
|
|
||||||
// msg.fileName = mimeClass
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// exts, _ := mime.ExtensionsByType(msg.mimeType)
|
|
||||||
// if exts != nil && len(exts) > 0 {
|
|
||||||
// msg.fileName += exts[0]
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// content := &event.MessageEventContent{
|
|
||||||
// Body: msg.fileName,
|
|
||||||
// File: file,
|
|
||||||
// Info: &event.FileInfo{
|
|
||||||
// Size: len(data),
|
|
||||||
// MimeType: msg.mimeType,
|
|
||||||
// Width: width,
|
|
||||||
// Height: height,
|
|
||||||
// Duration: int(msg.length),
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// if content.File != nil {
|
|
||||||
// content.File.URL = uploaded.ContentURI.CUString()
|
|
||||||
// } else {
|
|
||||||
// content.URL = uploaded.ContentURI.CUString()
|
|
||||||
// }
|
|
||||||
// portal.SetReply(content, msg.context)
|
|
||||||
//
|
|
||||||
// if msg.thumbnail != nil && portal.bridge.Config.Bridge.WhatsappThumbnail {
|
|
||||||
// thumbnailMime := http.DetectContentType(msg.thumbnail)
|
|
||||||
// thumbnailCfg, _, _ := image.DecodeConfig(bytes.NewReader(msg.thumbnail))
|
|
||||||
// thumbnailSize := len(msg.thumbnail)
|
|
||||||
// thumbnail, thumbnailUploadMime, thumbnailFile := portal.encryptFile(msg.thumbnail, thumbnailMime)
|
|
||||||
// uploadedThumbnail, err := intent.UploadBytes(thumbnail, thumbnailUploadMime)
|
|
||||||
// if err != nil {
|
|
||||||
// portal.log.Warnfln("Failed to upload thumbnail for %s: %v", msg.info.Id, err)
|
|
||||||
// } else if uploadedThumbnail != nil {
|
|
||||||
// if thumbnailFile != nil {
|
|
||||||
// thumbnailFile.URL = uploadedThumbnail.ContentURI.CUString()
|
|
||||||
// content.Info.ThumbnailFile = thumbnailFile
|
|
||||||
// } else {
|
|
||||||
// content.Info.ThumbnailURL = uploadedThumbnail.ContentURI.CUString()
|
|
||||||
// }
|
|
||||||
// content.Info.ThumbnailInfo = &event.FileInfo{
|
|
||||||
// Size: thumbnailSize,
|
|
||||||
// Width: thumbnailCfg.Width,
|
|
||||||
// Height: thumbnailCfg.Height,
|
|
||||||
// MimeType: thumbnailMime,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// switch strings.ToLower(strings.Split(msg.mimeType, "/")[0]) {
|
|
||||||
// case "image":
|
|
||||||
// if !msg.sendAsSticker {
|
|
||||||
// content.MsgType = event.MsgImage
|
|
||||||
// }
|
|
||||||
// case "video":
|
|
||||||
// content.MsgType = event.MsgVideo
|
|
||||||
// case "audio":
|
|
||||||
// content.MsgType = event.MsgAudio
|
|
||||||
// default:
|
|
||||||
// content.MsgType = event.MsgFile
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// _, _ = intent.UserTyping(portal.MXID, false, 0)
|
|
||||||
// ts := int64(msg.info.Timestamp * 1000)
|
|
||||||
// eventType := event.EventMessage
|
|
||||||
// if msg.sendAsSticker {
|
|
||||||
// eventType = event.EventSticker
|
|
||||||
// }
|
|
||||||
// resp, err := portal.sendMessage(intent, eventType, content, ts)
|
|
||||||
// if err != nil {
|
|
||||||
// portal.log.Errorfln("Failed to handle message %s: %v", msg.info.Id, err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if len(msg.caption) > 0 {
|
|
||||||
// captionContent := &event.MessageEventContent{
|
|
||||||
// Body: msg.caption,
|
|
||||||
// MsgType: event.MsgNotice,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// portal.bridge.Formatter.ParseWhatsApp(captionContent, msg.context.MentionedJID)
|
|
||||||
//
|
|
||||||
// _, err := portal.sendMessage(intent, event.EventMessage, captionContent, ts)
|
|
||||||
// if err != nil {
|
|
||||||
// portal.log.Warnfln("Failed to handle caption of message %s: %v", msg.info.Id, err)
|
|
||||||
// }
|
|
||||||
// // TODO store caption mxid?
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// portal.finishHandling(source, msg.info.Source, resp.EventID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (portal *Portal) HandleTextMessage(source *User, message *groupme.Message) {
|
func (portal *Portal) HandleTextMessage(source *User, message *groupme.Message) {
|
||||||
intent := portal.startHandling(source, message)
|
intent := portal.startHandling(source, message)
|
||||||
@ -1592,19 +1456,7 @@ func (portal *Portal) convertMatrixMessage(sender *User, evt *event.Event) ([]*g
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
relaybotFormatted := false
|
relaybotFormatted := false
|
||||||
if sender.NeedsRelaybot(portal) {
|
|
||||||
if !portal.HasRelaybot() {
|
|
||||||
if sender.HasSession() {
|
|
||||||
portal.log.Debugln("Database says", sender.MXID, "not in chat and no relaybot, but trying to send anyway")
|
|
||||||
} else {
|
|
||||||
portal.log.Debugln("Ignoring message from", sender.MXID, "in chat with no relaybot")
|
|
||||||
return nil, sender
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
relaybotFormatted = portal.addRelaybotFormat(sender, content)
|
|
||||||
sender = portal.bridge.Relaybot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if evt.Type == event.EventSticker {
|
if evt.Type == event.EventSticker {
|
||||||
content.MsgType = event.MsgImage
|
content.MsgType = event.MsgImage
|
||||||
} else if content.MsgType == event.MsgImage && content.GetInfo().MimeType == "image/gif" {
|
} else if content.MsgType == event.MsgImage && content.GetInfo().MimeType == "image/gif" {
|
||||||
@ -1720,34 +1572,9 @@ func (portal *Portal) wasMessageSent(sender *User, id string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) sendErrorMessage(message string) id.EventID {
|
|
||||||
resp, err := portal.sendMainIntentMessage(event.MessageEventContent{
|
|
||||||
MsgType: event.MsgNotice,
|
|
||||||
Body: fmt.Sprintf("\u26a0 Your message may not have been bridged: %v", message),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
portal.log.Warnfln("Failed to send bridging error message:", err)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return resp.EventID
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var timeout = errors.New("message sending timed out")
|
var timeout = errors.New("message sending timed out")
|
||||||
|
|
||||||
func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
|
func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
|
||||||
if !portal.HasRelaybot() && ((portal.IsPrivateChat() && sender.JID != portal.Key.Receiver) ||
|
|
||||||
portal.sendMatrixConnectionError(sender, evt.ID)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
portal.log.Debugfln("Received event %s", evt.ID)
|
portal.log.Debugfln("Received event %s", evt.ID)
|
||||||
info, sender := portal.convertMatrixMessage(sender, evt)
|
info, sender := portal.convertMatrixMessage(sender, evt)
|
||||||
if info == nil {
|
if info == nil {
|
||||||
@ -1762,11 +1589,9 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
|
|||||||
portal.log.Warnln("Unable to handle message from Matrix", evt.ID)
|
portal.log.Warnln("Unable to handle message from Matrix", evt.ID)
|
||||||
//TODO handle deleted room and such
|
//TODO handle deleted room and such
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
portal.markHandled(sender, i, evt.ID)
|
portal.markHandled(sender, i, evt.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) sendRaw(sender *User, evt *event.Event, info *groupme.Message, retries int) (*groupme.Message, error) {
|
func (portal *Portal) sendRaw(sender *User, evt *event.Event, info *groupme.Message, retries int) (*groupme.Message, error) {
|
||||||
@ -1892,7 +1717,7 @@ func (portal *Portal) HandleMatrixRedaction(sender *User, evt *event.Event) {
|
|||||||
func (portal *Portal) Delete() {
|
func (portal *Portal) Delete() {
|
||||||
portal.Portal.Delete()
|
portal.Portal.Delete()
|
||||||
portal.bridge.portalsLock.Lock()
|
portal.bridge.portalsLock.Lock()
|
||||||
delete(portal.bridge.portalsByJID, portal.Key)
|
delete(portal.bridge.portalsByGMID, portal.Key)
|
||||||
if len(portal.MXID) > 0 {
|
if len(portal.MXID) > 0 {
|
||||||
delete(portal.bridge.portalsByMXID, portal.MXID)
|
delete(portal.bridge.portalsByMXID, portal.MXID)
|
||||||
}
|
}
|
||||||
@ -1976,7 +1801,7 @@ func (portal *Portal) HandleMatrixLeave(sender *User) {
|
|||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
// TODO should we somehow deduplicate this call if this leave was sent by the bridge?
|
// TODO should we somehow deduplicate this call if this leave was sent by the bridge?
|
||||||
err := sender.Client.RemoveFromGroup(sender.JID, portal.Key.GMID)
|
err := sender.Client.RemoveFromGroup(sender.GMID, portal.Key.GMID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
portal.log.Errorfln("Failed to leave group as %s: %v", sender.MXID, err)
|
portal.log.Errorfln("Failed to leave group as %s: %v", sender.MXID, err)
|
||||||
return
|
return
|
||||||
|
@ -34,8 +34,8 @@ type ProvisioningAPI struct {
|
|||||||
|
|
||||||
func (prov *ProvisioningAPI) Init() {
|
func (prov *ProvisioningAPI) Init() {
|
||||||
prov.log = prov.bridge.Log.Sub("Provisioning")
|
prov.log = prov.bridge.Log.Sub("Provisioning")
|
||||||
prov.log.Debugln("Enabling provisioning API at", prov.bridge.Config.AppService.Provisioning.Prefix)
|
prov.log.Debugln("Enabling provisioning API at", prov.bridge.Config.Bridge.Provisioning.Prefix)
|
||||||
r := prov.bridge.AS.Router.PathPrefix(prov.bridge.Config.AppService.Provisioning.Prefix).Subrouter()
|
r := prov.bridge.AS.Router.PathPrefix(prov.bridge.Config.Bridge.Provisioning.Prefix).Subrouter()
|
||||||
r.Use(prov.AuthMiddleware)
|
r.Use(prov.AuthMiddleware)
|
||||||
r.HandleFunc("/ping", prov.Ping).Methods(http.MethodGet)
|
r.HandleFunc("/ping", prov.Ping).Methods(http.MethodGet)
|
||||||
r.HandleFunc("/login", prov.Login)
|
r.HandleFunc("/login", prov.Login)
|
||||||
@ -61,7 +61,7 @@ func (prov *ProvisioningAPI) AuthMiddleware(h http.Handler) http.Handler {
|
|||||||
} else if strings.HasPrefix(auth, "Bearer ") {
|
} else if strings.HasPrefix(auth, "Bearer ") {
|
||||||
auth = auth[len("Bearer "):]
|
auth = auth[len("Bearer "):]
|
||||||
}
|
}
|
||||||
if auth != prov.bridge.Config.AppService.Provisioning.SharedSecret {
|
if auth != prov.bridge.Config.Bridge.Provisioning.SharedSecret {
|
||||||
jsonResponse(w, http.StatusForbidden, map[string]interface{}{
|
jsonResponse(w, http.StatusForbidden, map[string]interface{}{
|
||||||
"error": "Invalid auth token",
|
"error": "Invalid auth token",
|
||||||
"errcode": "M_FORBIDDEN",
|
"errcode": "M_FORBIDDEN",
|
||||||
@ -150,7 +150,7 @@ func (prov *ProvisioningAPI) Disconnect(w http.ResponseWriter, r *http.Request)
|
|||||||
// } else if len(sess.Wid) > 0 {
|
// } else if len(sess.Wid) > 0 {
|
||||||
// user.SetSession(&sess)
|
// user.SetSession(&sess)
|
||||||
// }
|
// }
|
||||||
user.bridge.Metrics.TrackConnectionState(user.JID, false)
|
user.bridge.Metrics.TrackConnectionState(user.GMID, false)
|
||||||
jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp"})
|
jsonResponse(w, http.StatusOK, Response{true, "Disconnected from WhatsApp"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
145
puppet.go
145
puppet.go
@ -18,9 +18,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"sync"
|
||||||
|
|
||||||
log "maunium.net/go/maulogger/v2"
|
log "maunium.net/go/maulogger/v2"
|
||||||
|
|
||||||
@ -31,7 +30,6 @@ import (
|
|||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
"github.com/beeper/groupme/database"
|
"github.com/beeper/groupme/database"
|
||||||
"github.com/beeper/groupme/groupmeext"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var userIDRegex *regexp.Regexp
|
var userIDRegex *regexp.Regexp
|
||||||
@ -162,8 +160,7 @@ func (bridge *GMBridge) dbPuppetsToPuppets(dbPuppets []*database.Puppet) []*Pupp
|
|||||||
|
|
||||||
func (bridge *GMBridge) FormatPuppetMXID(gmid groupme.ID) id.UserID {
|
func (bridge *GMBridge) FormatPuppetMXID(gmid groupme.ID) id.UserID {
|
||||||
return id.NewUserID(
|
return id.NewUserID(
|
||||||
bridge.Config.Bridge.FormatUsername(
|
bridge.Config.Bridge.FormatUsername(gmid.String()),
|
||||||
gmid),
|
|
||||||
bridge.Config.Homeserver.Domain)
|
bridge.Config.Homeserver.Domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,17 +188,16 @@ type Puppet struct {
|
|||||||
customIntent *appservice.IntentAPI
|
customIntent *appservice.IntentAPI
|
||||||
customTypingIn map[id.RoomID]bool
|
customTypingIn map[id.RoomID]bool
|
||||||
customUser *User
|
customUser *User
|
||||||
|
|
||||||
|
syncLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (puppet *Puppet) PhoneNumber() string {
|
func (puppet *Puppet) PhoneNumber() string {
|
||||||
println("phone num")
|
return puppet.GMID.String()
|
||||||
return strings.Replace(puppet.GMID, whatsappExt.NewUserSuffix, "", 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (puppet *Puppet) IntentFor(portal *Portal) *appservice.IntentAPI {
|
func (puppet *Puppet) IntentFor(portal *Portal) *appservice.IntentAPI {
|
||||||
if (!portal.IsPrivateChat() && puppet.customIntent == nil) ||
|
if puppet.customIntent == nil || portal.Key.GMID == puppet.GMID {
|
||||||
(portal.backfilling && portal.bridge.Config.Bridge.InviteOwnPuppetForBackfilling) ||
|
|
||||||
portal.Key.GMID == puppet.GMID {
|
|
||||||
return puppet.DefaultIntent()
|
return puppet.DefaultIntent()
|
||||||
}
|
}
|
||||||
return puppet.customIntent
|
return puppet.customIntent
|
||||||
@ -219,132 +215,95 @@ func (puppet *Puppet) DefaultIntent() *appservice.IntentAPI {
|
|||||||
//
|
//
|
||||||
//}
|
//}
|
||||||
|
|
||||||
func (puppet *Puppet) UpdateAvatar(source *User, portalMXID id.RoomID, avatar string) bool {
|
func (puppet *Puppet) UpdateAvatar(source *User, forcePortalSync bool) bool {
|
||||||
memberRaw, _ := puppet.bridge.StateStore.TryGetMemberRaw(portalMXID, puppet.MXID) //TODO Handle
|
changed := source.updateAvatar(puppet.GMID, &puppet.Avatar, &puppet.AvatarURL, &puppet.AvatarSet, puppet.log, puppet.DefaultIntent())
|
||||||
|
if !changed || puppet.Avatar == "unauthorized" {
|
||||||
if memberRaw.Avatar == avatar {
|
if forcePortalSync {
|
||||||
return false // up to date
|
go puppet.updatePortalAvatar()
|
||||||
}
|
|
||||||
|
|
||||||
if len(avatar) == 0 {
|
|
||||||
var err error
|
|
||||||
// err = puppet.DefaultIntent().SetRoomAvatarURL(portalMXID, id.ContentURI{})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
puppet.log.Warnln("Failed to remove avatar:", err, puppet.MXID)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
memberRaw.Avatar = avatar
|
return changed
|
||||||
memberRaw.AvatarURL = ""
|
|
||||||
|
|
||||||
go puppet.updatePortalAvatar()
|
|
||||||
|
|
||||||
puppet.bridge.StateStore.SetMemberRaw(&memberRaw) //TODO handle
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
err := puppet.DefaultIntent().SetAvatarURL(puppet.AvatarURL)
|
||||||
//TODO check its actually groupme?
|
|
||||||
image, mime, err := groupmeext.DownloadImage(avatar + ".large")
|
|
||||||
if err != nil {
|
|
||||||
puppet.log.Warnln(err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := puppet.DefaultIntent().UploadBytes(*image, mime)
|
|
||||||
if err != nil {
|
|
||||||
puppet.log.Warnln("Failed to upload avatar:", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// err = puppet.DefaultIntent().SetRoomAvatarURL(portalMXID, resp.ContentURI)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
puppet.log.Warnln("Failed to set avatar:", err)
|
puppet.log.Warnln("Failed to set avatar:", err)
|
||||||
|
} else {
|
||||||
|
puppet.AvatarSet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
memberRaw.AvatarURL = resp.ContentURI.String()
|
|
||||||
memberRaw.Avatar = avatar
|
|
||||||
|
|
||||||
puppet.bridge.StateStore.SetMemberRaw(&memberRaw) //TODO handle
|
|
||||||
|
|
||||||
go puppet.updatePortalAvatar()
|
go puppet.updatePortalAvatar()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (puppet *Puppet) UpdateName(source *User, portalMXID id.RoomID, contact groupme.Member) bool {
|
func (puppet *Puppet) UpdateName(member groupme.Member, forcePortalSync bool) bool {
|
||||||
newName, _ := puppet.bridge.Config.Bridge.FormatDisplayname(contact)
|
newName := puppet.bridge.Config.Bridge.FormatDisplayname(puppet.GMID, member)
|
||||||
|
if puppet.Displayname != newName || !puppet.NameSet {
|
||||||
memberRaw, _ := puppet.bridge.StateStore.TryGetMemberRaw(portalMXID, puppet.MXID) //TODO Handle
|
oldName := puppet.Displayname
|
||||||
|
puppet.Displayname = newName
|
||||||
if memberRaw.DisplayName != newName { //&& quality >= puppet.NameQuality[portalMXID] {
|
puppet.NameSet = false
|
||||||
var err error
|
err := puppet.DefaultIntent().SetDisplayName(newName)
|
||||||
// err = puppet.DefaultIntent().SetRoomDisplayName(portalMXID, newName)
|
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
memberRaw.DisplayName = newName
|
puppet.log.Debugln("Updated name", oldName, "->", newName)
|
||||||
// puppet.NameQuality[portalMXID] = quality
|
puppet.NameSet = true
|
||||||
|
|
||||||
puppet.bridge.StateStore.SetMemberRaw(&memberRaw) //TODO handle; maybe .Update() ?
|
|
||||||
go puppet.updatePortalName()
|
go puppet.updatePortalName()
|
||||||
} else {
|
} else {
|
||||||
puppet.log.Warnln("Failed to set display name:", err)
|
puppet.log.Warnln("Failed to set display name:", err)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
} else if forcePortalSync {
|
||||||
|
go puppet.updatePortalName()
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (puppet *Puppet) updatePortalMeta(meta func(portal *Portal)) {
|
func (puppet *Puppet) updatePortalMeta(meta func(portal *Portal)) {
|
||||||
if puppet.bridge.Config.Bridge.PrivateChatPortalMeta {
|
if puppet.bridge.Config.Bridge.PrivateChatPortalMeta || puppet.bridge.Config.Bridge.Encryption.Allow {
|
||||||
for _, portal := range puppet.bridge.GetAllPortalsByJID(puppet.JID) {
|
for _, portal := range puppet.bridge.GetAllPortalsByGMID(puppet.GMID) {
|
||||||
|
if !puppet.bridge.Config.Bridge.PrivateChatPortalMeta && !portal.Encrypted {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Get room create lock to prevent races between receiving contact info and room creation.
|
||||||
|
portal.roomCreateLock.Lock()
|
||||||
meta(portal)
|
meta(portal)
|
||||||
|
portal.roomCreateLock.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (puppet *Puppet) updatePortalAvatar() {
|
func (puppet *Puppet) updatePortalAvatar() {
|
||||||
puppet.updatePortalMeta(func(portal *Portal) {
|
puppet.updatePortalMeta(func(portal *Portal) {
|
||||||
|
if portal.Avatar == puppet.Avatar && portal.AvatarURL == puppet.AvatarURL && portal.AvatarSet {
|
||||||
m, _ := puppet.bridge.StateStore.TryGetMemberRaw(portal.MXID, puppet.MXID)
|
return
|
||||||
|
}
|
||||||
|
portal.AvatarURL = puppet.AvatarURL
|
||||||
|
portal.Avatar = puppet.Avatar
|
||||||
|
portal.AvatarSet = false
|
||||||
|
defer portal.Update(nil)
|
||||||
if len(portal.MXID) > 0 {
|
if len(portal.MXID) > 0 {
|
||||||
_, err := portal.MainIntent().SetRoomAvatar(portal.MXID, id.MustParseContentURI(m.AvatarURL))
|
_, err := portal.MainIntent().SetRoomAvatar(portal.MXID, puppet.AvatarURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
portal.log.Warnln("Failed to set avatar:", err)
|
portal.log.Warnln("Failed to set avatar:", err)
|
||||||
|
} else {
|
||||||
|
portal.AvatarSet = true
|
||||||
|
portal.UpdateBridgeInfo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
portal.AvatarURL = id.MustParseContentURI(m.AvatarURL)
|
|
||||||
portal.Avatar = m.Avatar
|
|
||||||
portal.Update()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (puppet *Puppet) updatePortalName() {
|
func (puppet *Puppet) updatePortalName() {
|
||||||
puppet.updatePortalMeta(func(portal *Portal) {
|
puppet.updatePortalMeta(func(portal *Portal) {
|
||||||
m, _ := puppet.bridge.StateStore.TryGetMemberRaw(portal.MXID, puppet.MXID)
|
portal.UpdateName(puppet.Displayname, groupme.ID(""), true)
|
||||||
if len(portal.MXID) > 0 {
|
|
||||||
_, err := portal.MainIntent().SetRoomName(portal.MXID, m.DisplayName)
|
|
||||||
if err != nil {
|
|
||||||
portal.log.Warnln("Failed to set name:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
portal.Name = m.DisplayName
|
|
||||||
portal.Update()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (puppet *Puppet) Sync(source *User, portalMXID id.RoomID, contact groupme.Member) {
|
func (puppet *Puppet) Sync(source *User, member *groupme.Member, forceAvatarSync, forcePortalSync bool) {
|
||||||
if contact.UserID.String() == "system" {
|
puppet.syncLock.Lock()
|
||||||
puppet.log.Warnln("Trying to sync system puppet")
|
defer puppet.syncLock.Unlock()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := puppet.DefaultIntent().EnsureRegistered()
|
err := puppet.DefaultIntent().EnsureRegistered()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
puppet.log.Errorln("Failed to ensure registered:", err)
|
puppet.log.Errorln("Failed to ensure registered:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
update := false
|
puppet.log.Debugfln("Syncing info through %s", source.GMID)
|
||||||
update = puppet.UpdateName(source, portalMXID, contact) || update
|
|
||||||
update = puppet.UpdateAvatar(source, portalMXID, contact.ImageURL) || update
|
// TODO
|
||||||
if update {
|
|
||||||
puppet.Update()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
284
user.go
284
user.go
@ -21,7 +21,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -200,9 +199,8 @@ func (br *GMBridge) NewUser(dbUser *database.User) *User {
|
|||||||
|
|
||||||
chatListReceived: make(chan struct{}, 1),
|
chatListReceived: make(chan struct{}, 1),
|
||||||
syncPortalsDone: make(chan struct{}, 1),
|
syncPortalsDone: make(chan struct{}, 1),
|
||||||
syncStart: make(chan struct{}, 1),
|
|
||||||
messageInput: make(chan PortalMessage),
|
messageInput: make(chan PortalMessage),
|
||||||
messageOutput: make(chan PortalMessage, br.Config.Bridge.UserMessageBuffer),
|
messageOutput: make(chan PortalMessage, br.Config.Bridge.PortalMessageBuffer),
|
||||||
}
|
}
|
||||||
|
|
||||||
user.PermissionLevel = user.bridge.Config.Bridge.Permissions.Get(user.MXID)
|
user.PermissionLevel = user.bridge.Config.Bridge.Permissions.Get(user.MXID)
|
||||||
@ -349,7 +347,7 @@ func (user *User) Connect() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
user.log.Debugln("Connecting to WhatsApp")
|
user.log.Debugln("Connecting to GroupMe")
|
||||||
timeout := time.Duration(user.bridge.Config.GroupMe.ConnectionTimeout)
|
timeout := time.Duration(user.bridge.Config.GroupMe.ConnectionTimeout)
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
timeout = 20
|
timeout = 20
|
||||||
@ -365,7 +363,7 @@ func (user *User) Connect() bool {
|
|||||||
|
|
||||||
func (user *User) RestoreSession() bool {
|
func (user *User) RestoreSession() bool {
|
||||||
if len(user.Token) > 0 {
|
if len(user.Token) > 0 {
|
||||||
err := user.Conn.SubscribeToUser(context.TODO(), groupme.ID(user.GMID), user.Token.String())
|
err := user.Conn.SubscribeToUser(context.TODO(), groupme.ID(user.GMID), user.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
@ -447,19 +445,15 @@ func (user *User) PostLogin() {
|
|||||||
user.bridge.Metrics.TrackConnectionState(user.GMID, true)
|
user.bridge.Metrics.TrackConnectionState(user.GMID, true)
|
||||||
user.bridge.Metrics.TrackLoginState(user.GMID, true)
|
user.bridge.Metrics.TrackLoginState(user.GMID, true)
|
||||||
user.bridge.Metrics.TrackBufferLength(user.MXID, 0)
|
user.bridge.Metrics.TrackBufferLength(user.MXID, 0)
|
||||||
go user.intPostLogin()
|
// go user.intPostLogin()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) tryAutomaticDoublePuppeting() {
|
func (user *User) tryAutomaticDoublePuppeting() {
|
||||||
if len(user.bridge.Config.Bridge.LoginSharedSecret) == 0 {
|
if !user.bridge.Config.CanAutoDoublePuppet(user.MXID) {
|
||||||
// Automatic login not enabled
|
|
||||||
return
|
|
||||||
} else if _, homeserver, _ := user.MXID.Parse(); homeserver != user.bridge.Config.Homeserver.Domain {
|
|
||||||
// user is on another homeserver
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user.log.Debugln("Checking if double puppeting needs to be enabled")
|
user.log.Debugln("Checking if double puppeting needs to be enabled")
|
||||||
puppet := user.bridge.GetPuppetByGMID(user.JID)
|
puppet := user.bridge.GetPuppetByGMID(user.GMID)
|
||||||
if len(puppet.CustomMXID) > 0 {
|
if len(puppet.CustomMXID) > 0 {
|
||||||
user.log.Debugln("User already has double-puppeting enabled")
|
user.log.Debugln("User already has double-puppeting enabled")
|
||||||
// Custom puppet already enabled
|
// Custom puppet already enabled
|
||||||
@ -519,85 +513,84 @@ func (user *User) postConnPing() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) intPostLogin() {
|
// func (user *User) intPostLogin() {
|
||||||
defer user.syncWait.Done()
|
// defer user.syncWait.Done()
|
||||||
user.lastReconnection = time.Now().Unix()
|
// user.lastReconnection = time.Now().Unix()
|
||||||
user.Client = groupmeext.NewClient(user.Token)
|
// user.Client = groupmeext.NewClient(user.Token)
|
||||||
if len(user.JID) == 0 {
|
// if len(user.JID) == 0 {
|
||||||
myuser, err := user.Client.MyUser(context.TODO())
|
// myuser, err := user.Client.MyUser(context.TODO())
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err) //TODO
|
// log.Fatal(err) //TODO
|
||||||
}
|
// }
|
||||||
user.JID = myuser.ID.String()
|
// user.JID = myuser.ID.String()
|
||||||
}
|
// }
|
||||||
user.Update()
|
// user.Update()
|
||||||
|
|
||||||
user.createCommunity()
|
// user.tryAutomaticDoublePuppeting()
|
||||||
user.tryAutomaticDoublePuppeting()
|
|
||||||
|
|
||||||
user.log.Debugln("Waiting for chat list receive confirmation")
|
// user.log.Debugln("Waiting for chat list receive confirmation")
|
||||||
user.HandleChatList()
|
// user.HandleChatList()
|
||||||
select {
|
// select {
|
||||||
case <-user.chatListReceived:
|
// case <-user.chatListReceived:
|
||||||
user.log.Debugln("Chat list receive confirmation received in PostLogin")
|
// user.log.Debugln("Chat list receive confirmation received in PostLogin")
|
||||||
case <-time.After(time.Duration(user.bridge.Config.Bridge.ChatListWait) * time.Second):
|
// case <-time.After(time.Duration(user.bridge.Config.Bridge.ChatListWait) * time.Second):
|
||||||
user.log.Warnln("Timed out waiting for chat list to arrive!")
|
// user.log.Warnln("Timed out waiting for chat list to arrive!")
|
||||||
user.postConnPing()
|
// user.postConnPing()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
if !user.postConnPing() {
|
// if !user.postConnPing() {
|
||||||
user.log.Debugln("Post-connection ping failed, unlocking processing of incoming messages.")
|
// user.log.Debugln("Post-connection ping failed, unlocking processing of incoming messages.")
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
user.log.Debugln("Waiting for portal sync complete confirmation")
|
// user.log.Debugln("Waiting for portal sync complete confirmation")
|
||||||
select {
|
// select {
|
||||||
case <-user.syncPortalsDone:
|
// case <-user.syncPortalsDone:
|
||||||
user.log.Debugln("Post-connection portal sync complete, unlocking processing of incoming messages.")
|
// user.log.Debugln("Post-connection portal sync complete, unlocking processing of incoming messages.")
|
||||||
// TODO this is too short, maybe a per-portal duration?
|
// // TODO this is too short, maybe a per-portal duration?
|
||||||
case <-time.After(time.Duration(user.bridge.Config.Bridge.PortalSyncWait) * time.Second):
|
// case <-time.After(time.Duration(user.bridge.Config.Bridge.PortalSyncWait) * time.Second):
|
||||||
user.log.Warnln("Timed out waiting for portal sync to complete! Unlocking processing of incoming messages.")
|
// user.log.Warnln("Timed out waiting for portal sync to complete! Unlocking processing of incoming messages.")
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (user *User) HandleChatList() {
|
func (user *User) HandleChatList() {
|
||||||
chatMap := make(map[string]groupme.Group)
|
chatMap := map[groupme.ID]groupme.Group{}
|
||||||
chats, err := user.Client.IndexAllGroups()
|
chats, err := user.Client.IndexAllGroups()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user.log.Errorln("chat sync error", err) //TODO: handle
|
user.log.Errorln("chat sync error", err) //TODO: handle
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, chat := range chats {
|
for _, chat := range chats {
|
||||||
chatMap[chat.ID.String()] = *chat
|
chatMap[chat.ID] = *chat
|
||||||
}
|
}
|
||||||
user.GroupList = chatMap
|
user.GroupList = chatMap
|
||||||
|
|
||||||
dmMap := make(map[string]groupme.Chat)
|
dmMap := map[groupme.ID]groupme.Chat{}
|
||||||
dms, err := user.Client.IndexAllChats()
|
dms, err := user.Client.IndexAllChats()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user.log.Errorln("chat sync error", err) //TODO: handle
|
user.log.Errorln("chat sync error", err) //TODO: handle
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, dm := range dms {
|
for _, dm := range dms {
|
||||||
dmMap[dm.OtherUser.ID.String()] = *dm
|
dmMap[dm.OtherUser.ID] = *dm
|
||||||
}
|
}
|
||||||
user.ChatList = dmMap
|
user.ChatList = dmMap
|
||||||
|
|
||||||
userMap := make(map[string]groupme.User)
|
userMap := map[groupme.ID]groupme.User{}
|
||||||
users, err := user.Client.IndexAllRelations()
|
users, err := user.Client.IndexAllRelations()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user.log.Errorln("Error syncing user list, continuing sync", err)
|
user.log.Errorln("Error syncing user list, continuing sync", err)
|
||||||
}
|
}
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
puppet := user.bridge.GetPuppetByJID(u.ID.String())
|
puppet := user.bridge.GetPuppetByGMID(u.ID)
|
||||||
// "" for overall user not related to one group
|
// "" for overall user not related to one group
|
||||||
puppet.Sync(nil, "", groupme.Member{
|
puppet.Sync(nil, &groupme.Member{
|
||||||
UserID: u.ID,
|
UserID: u.ID,
|
||||||
Nickname: u.Name,
|
Nickname: u.Name,
|
||||||
ImageURL: u.AvatarURL,
|
ImageURL: u.AvatarURL,
|
||||||
})
|
}, false, false)
|
||||||
userMap[u.ID.String()] = *u
|
userMap[u.ID] = *u
|
||||||
}
|
}
|
||||||
user.RelationList = userMap
|
user.RelationList = userMap
|
||||||
|
|
||||||
@ -607,90 +600,88 @@ func (user *User) HandleChatList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) syncPortals(createAll bool) {
|
func (user *User) syncPortals(createAll bool) {
|
||||||
|
// user.log.Infoln("Reading chat list")
|
||||||
|
|
||||||
user.log.Infoln("Reading chat list")
|
// chats := make(ChatList, 0, len(user.GroupList)+len(user.ChatList))
|
||||||
|
// portalKeys := make([]database.PortalKeyWithMeta, 0, cap(chats))
|
||||||
|
|
||||||
chats := make(ChatList, 0, len(user.GroupList)+len(user.ChatList))
|
// for _, group := range user.GroupList {
|
||||||
existingKeys := user.GetInCommunityMap()
|
|
||||||
portalKeys := make([]database.PortalKeyWithMeta, 0, cap(chats))
|
|
||||||
|
|
||||||
for _, group := range user.GroupList {
|
// portal := user.bridge.GetPortalByJID(database.GroupPortalKey(group.ID.String()))
|
||||||
|
|
||||||
portal := user.bridge.GetPortalByJID(database.GroupPortalKey(group.ID.String()))
|
// chats = append(chats, Chat{
|
||||||
|
// Portal: portal,
|
||||||
|
// LastMessageTime: uint64(group.UpdatedAt.ToTime().Unix()),
|
||||||
|
// Group: &group,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// for _, dm := range user.ChatList {
|
||||||
|
// portal := user.bridge.GetPortalByJID(database.NewPortalKey(dm.OtherUser.ID.String(), user.JID))
|
||||||
|
// chats = append(chats, Chat{
|
||||||
|
// Portal: portal,
|
||||||
|
// LastMessageTime: uint64(dm.UpdatedAt.ToTime().Unix()),
|
||||||
|
// DM: &dm,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
chats = append(chats, Chat{
|
// for _, chat := range chats {
|
||||||
Portal: portal,
|
// var inCommunity, ok bool
|
||||||
LastMessageTime: uint64(group.UpdatedAt.ToTime().Unix()),
|
// if inCommunity, ok = existingKeys[chat.Portal.Key]; !ok || !inCommunity {
|
||||||
Group: &group,
|
// inCommunity = user.addPortalToCommunity(chat.Portal)
|
||||||
})
|
// if chat.Portal.IsPrivateChat() {
|
||||||
}
|
// puppet := user.bridge.GetPuppetByJID(chat.Portal.Key.GMID)
|
||||||
for _, dm := range user.ChatList {
|
// user.addPuppetToCommunity(puppet)
|
||||||
portal := user.bridge.GetPortalByJID(database.NewPortalKey(dm.OtherUser.ID.String(), user.JID))
|
// }
|
||||||
chats = append(chats, Chat{
|
// }
|
||||||
Portal: portal,
|
// portalKeys = append(portalKeys, database.PortalKeyWithMeta{PortalKey: chat.Portal.Key, InCommunity: inCommunity})
|
||||||
LastMessageTime: uint64(dm.UpdatedAt.ToTime().Unix()),
|
// }
|
||||||
DM: &dm,
|
// user.log.Infoln("Read chat list, updating user-portal mapping")
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, chat := range chats {
|
// err := user.SetPortalKeys(portalKeys)
|
||||||
var inCommunity, ok bool
|
// if err != nil {
|
||||||
if inCommunity, ok = existingKeys[chat.Portal.Key]; !ok || !inCommunity {
|
// user.log.Warnln("Failed to update user-portal mapping:", err)
|
||||||
inCommunity = user.addPortalToCommunity(chat.Portal)
|
// }
|
||||||
if chat.Portal.IsPrivateChat() {
|
// sort.Sort(chats)
|
||||||
puppet := user.bridge.GetPuppetByJID(chat.Portal.Key.GMID)
|
// limit := user.bridge.Config.Bridge.InitialChatSync
|
||||||
user.addPuppetToCommunity(puppet)
|
// if limit < 0 {
|
||||||
}
|
// limit = len(chats)
|
||||||
}
|
// }
|
||||||
portalKeys = append(portalKeys, database.PortalKeyWithMeta{PortalKey: chat.Portal.Key, InCommunity: inCommunity})
|
// now := uint64(time.Now().Unix())
|
||||||
}
|
// user.log.Infoln("Syncing portals")
|
||||||
user.log.Infoln("Read chat list, updating user-portal mapping")
|
|
||||||
|
|
||||||
err := user.SetPortalKeys(portalKeys)
|
// wg := sync.WaitGroup{}
|
||||||
if err != nil {
|
// for i, chat := range chats {
|
||||||
user.log.Warnln("Failed to update user-portal mapping:", err)
|
// if chat.LastMessageTime+user.bridge.Config.Bridge.SyncChatMaxAge < now {
|
||||||
}
|
// break
|
||||||
sort.Sort(chats)
|
// }
|
||||||
limit := user.bridge.Config.Bridge.InitialChatSync
|
// wg.Add(1)
|
||||||
if limit < 0 {
|
// go func(chat Chat, i int) {
|
||||||
limit = len(chats)
|
// create := (chat.LastMessageTime >= user.LastConnection && user.LastConnection > 0) || i < limit
|
||||||
}
|
// if len(chat.Portal.MXID) > 0 || create || createAll {
|
||||||
now := uint64(time.Now().Unix())
|
// chat.Portal.Sync(user, chat.Group)
|
||||||
user.log.Infoln("Syncing portals")
|
// err := chat.Portal.BackfillHistory(user, chat.LastMessageTime)
|
||||||
|
// if err != nil {
|
||||||
|
// chat.Portal.log.Errorln("Error backfilling history:", err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
// wg.Done()
|
||||||
for i, chat := range chats {
|
// }(chat, i)
|
||||||
if chat.LastMessageTime+user.bridge.Config.Bridge.SyncChatMaxAge < now {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
wg.Add(1)
|
|
||||||
go func(chat Chat, i int) {
|
|
||||||
create := (chat.LastMessageTime >= user.LastConnection && user.LastConnection > 0) || i < limit
|
|
||||||
if len(chat.Portal.MXID) > 0 || create || createAll {
|
|
||||||
chat.Portal.Sync(user, chat.Group)
|
|
||||||
err := chat.Portal.BackfillHistory(user, chat.LastMessageTime)
|
|
||||||
if err != nil {
|
|
||||||
chat.Portal.log.Errorln("Error backfilling history:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Done()
|
// }
|
||||||
}(chat, i)
|
// wg.Wait()
|
||||||
|
// //TODO: handle leave from groupme side
|
||||||
}
|
// user.UpdateDirectChats(nil)
|
||||||
wg.Wait()
|
// user.log.Infoln("Finished syncing portals")
|
||||||
//TODO: handle leave from groupme side
|
// select {
|
||||||
user.UpdateDirectChats(nil)
|
// case user.syncPortalsDone <- struct{}{}:
|
||||||
user.log.Infoln("Finished syncing portals")
|
// default:
|
||||||
select {
|
// }
|
||||||
case user.syncPortalsDone <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) getDirectChats() map[id.UserID][]id.RoomID {
|
func (user *User) getDirectChats() map[id.UserID][]id.RoomID {
|
||||||
res := make(map[id.UserID][]id.RoomID)
|
res := make(map[id.UserID][]id.RoomID)
|
||||||
privateChats := user.bridge.DB.Portal.FindPrivateChats(user.JID)
|
privateChats := user.bridge.DB.Portal.FindPrivateChats(user.GMID)
|
||||||
for _, portal := range privateChats {
|
for _, portal := range privateChats {
|
||||||
if len(portal.MXID) > 0 {
|
if len(portal.MXID) > 0 {
|
||||||
res[user.bridge.FormatPuppetMXID(portal.Key.GMID)] = []id.RoomID{portal.MXID}
|
res[user.bridge.FormatPuppetMXID(portal.Key.GMID)] = []id.RoomID{portal.MXID}
|
||||||
@ -715,11 +706,8 @@ func (user *User) UpdateDirectChats(chats map[id.UserID][]id.RoomID) {
|
|||||||
}
|
}
|
||||||
user.log.Debugln("Updating m.direct list on homeserver")
|
user.log.Debugln("Updating m.direct list on homeserver")
|
||||||
var err error
|
var err error
|
||||||
if user.bridge.Config.Homeserver.Asmux {
|
if user.bridge.Config.Homeserver.Software == bridgeconfig.SoftwareAsmux {
|
||||||
urlPath := intent.BuildBaseURL("_matrix", "client", "unstable", "net.maunium.asmux", "dms")
|
urlPath := intent.BuildClientURL("unstable", "com.beeper.asmux", "dms")
|
||||||
// _, err = intent.MakeFullRequest(method, urlPath, http.Header{
|
|
||||||
// "X-Asmux-Auth": {user.bridge.AS.Registration.AppToken},
|
|
||||||
// }, chats, nil)
|
|
||||||
_, err = intent.MakeFullRequest(mautrix.FullRequest{
|
_, err = intent.MakeFullRequest(mautrix.FullRequest{
|
||||||
Method: method,
|
Method: method,
|
||||||
URL: urlPath,
|
URL: urlPath,
|
||||||
@ -772,9 +760,9 @@ func (user *User) syncPuppets(contacts map[string]whatsapp.Contact) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) updateLastConnectionIfNecessary() {
|
func (user *User) updateLastConnectionIfNecessary() {
|
||||||
if user.LastConnection+60 < uint64(time.Now().Unix()) {
|
// if user.LastConnection+60 < uint64(time.Now().Unix()) {
|
||||||
user.UpdateLastConnection()
|
// user.UpdateLastConnection()
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleError(err error) {
|
func (user *User) HandleError(err error) {
|
||||||
@ -784,7 +772,7 @@ func (user *User) HandleError(err error) {
|
|||||||
if closed, ok := err.(*whatsapp.ErrConnectionClosed); ok {
|
if closed, ok := err.(*whatsapp.ErrConnectionClosed); ok {
|
||||||
user.bridge.Metrics.TrackDisconnection(user.MXID)
|
user.bridge.Metrics.TrackDisconnection(user.MXID)
|
||||||
if closed.Code == 1000 && user.cleanDisconnection {
|
if closed.Code == 1000 && user.cleanDisconnection {
|
||||||
user.bridge.Metrics.TrackConnectionState(user.JID, false)
|
user.bridge.Metrics.TrackConnectionState(user.GMID, false)
|
||||||
user.cleanDisconnection = false
|
user.cleanDisconnection = false
|
||||||
user.log.Infoln("Clean disconnection by server")
|
user.log.Infoln("Clean disconnection by server")
|
||||||
return
|
return
|
||||||
@ -869,7 +857,7 @@ func (user *User) PortalKey(gmid groupme.ID) database.PortalKey {
|
|||||||
return database.NewPortalKey(gmid, user.GMID)
|
return database.NewPortalKey(gmid, user.GMID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) GetPortalByJID(gmid groupme.ID) *Portal {
|
func (user *User) GetPortalByGMID(gmid groupme.ID) *Portal {
|
||||||
return user.bridge.GetPortalByGMID(user.PortalKey(gmid))
|
return user.bridge.GetPortalByGMID(user.PortalKey(gmid))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -894,11 +882,11 @@ func (user *User) handleMessageLoop() {
|
|||||||
puppet := user.bridge.GetPuppetByGMID(msg.data.UserID)
|
puppet := user.bridge.GetPuppetByGMID(msg.data.UserID)
|
||||||
portal := user.bridge.GetPortalByGMID(msg.chat)
|
portal := user.bridge.GetPortalByGMID(msg.chat)
|
||||||
if puppet != nil {
|
if puppet != nil {
|
||||||
puppet.Sync(user, portal.MXID, groupme.Member{
|
puppet.Sync(user, &groupme.Member{
|
||||||
UserID: msg.data.UserID,
|
UserID: msg.data.UserID,
|
||||||
Nickname: msg.data.Name,
|
Nickname: msg.data.Name,
|
||||||
ImageURL: msg.data.AvatarURL,
|
ImageURL: msg.data.AvatarURL,
|
||||||
}) //TODO: add params or docs?
|
}, false, false)
|
||||||
}
|
}
|
||||||
portal.messages <- msg
|
portal.messages <- msg
|
||||||
}
|
}
|
||||||
@ -984,19 +972,19 @@ func (user *User) HandleLikeIcon(_ groupme.ID, _, _ int, _ string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleNewNickname(groupID, userID groupme.ID, name string) {
|
func (user *User) HandleNewNickname(groupID, userID groupme.ID, name string) {
|
||||||
puppet := user.bridge.GetPuppetByJID(userID.String())
|
puppet := user.bridge.GetPuppetByGMID(userID)
|
||||||
if puppet != nil {
|
if puppet != nil {
|
||||||
puppet.UpdateName(user, user.GetPortalByJID(groupID.String()).MXID, groupme.Member{
|
puppet.UpdateName(groupme.Member{
|
||||||
Nickname: name,
|
Nickname: name,
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
})
|
}, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HandleNewAvatarInGroup(groupID, userID groupme.ID, url string) {
|
func (user *User) HandleNewAvatarInGroup(groupID, userID groupme.ID, url string) {
|
||||||
puppet := user.bridge.GetPuppetByJID(userID.String())
|
puppet := user.bridge.GetPuppetByGMID(userID)
|
||||||
if puppet != nil {
|
if puppet != nil {
|
||||||
puppet.UpdateAvatar(user, user.GetPortalByJID(groupID.String()).MXID, url)
|
puppet.UpdateAvatar(user, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1262,7 +1250,3 @@ type FakeMessage struct {
|
|||||||
//func (user *User) HandleUnknownBinaryNode(node *waBinary.Node) {
|
//func (user *User) HandleUnknownBinaryNode(node *waBinary.Node) {
|
||||||
// user.log.Debugfln("Unknown binary message: %+v", node)
|
// user.log.Debugfln("Unknown binary message: %+v", node)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
func (user *User) NeedsRelaybot(portal *Portal) bool {
|
|
||||||
return !user.HasSession() || !user.IsInPortal(portal.Key)
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user