Add option to query message from phone when send times out

This commit is contained in:
Tulir Asokan 2020-05-24 17:28:30 +03:00
parent bb883e1262
commit 5a62982a56
3 changed files with 61 additions and 33 deletions

View file

@ -36,6 +36,7 @@ type BridgeConfig struct {
CommunityTemplate string `yaml:"community_template"` CommunityTemplate string `yaml:"community_template"`
ConnectionTimeout int `yaml:"connection_timeout"` ConnectionTimeout int `yaml:"connection_timeout"`
FetchMessageOnTimeout bool `yaml:"fetch_message_on_timeout"`
LoginQRRegenCount int `yaml:"login_qr_regen_count"` LoginQRRegenCount int `yaml:"login_qr_regen_count"`
MaxConnectionAttempts int `yaml:"max_connection_attempts"` MaxConnectionAttempts int `yaml:"max_connection_attempts"`
ConnectionRetryDelay int `yaml:"connection_retry_delay"` ConnectionRetryDelay int `yaml:"connection_retry_delay"`

View file

@ -73,6 +73,10 @@ bridge:
# WhatsApp connection timeout in seconds. # WhatsApp connection timeout in seconds.
connection_timeout: 20 connection_timeout: 20
# If WhatsApp doesn't respond within connection_timeout, should the bridge try to fetch the message
# to see if it was actually bridged? Use this if you have problems with sends timing out but actually
# succeeding.
fetch_message_on_timeout: false
# Number of times to regenerate QR code when logging in. # Number of times to regenerate QR code when logging in.
# The regenerated QR code is sent as an edit and essentially multiplies the login timeout (20 seconds) # The regenerated QR code is sent as an edit and essentially multiplies the login timeout (20 seconds)
login_qr_regen_count: 2 login_qr_regen_count: 2

View file

@ -1263,17 +1263,11 @@ func (portal *Portal) addRelaybotFormat(sender *User, content *event.MessageEven
return true return true
} }
func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) { func (portal *Portal) convertMatrixMessage(sender *User, evt *event.Event) *waProto.WebMessageInfo {
if !portal.HasRelaybot() && (
(portal.IsPrivateChat() && sender.JID != portal.Key.Receiver) ||
portal.sendMatrixConnectionError(sender, evt.ID)) {
return
}
content := evt.Content.AsMessage() content := evt.Content.AsMessage()
if content == nil { if content == nil {
return return nil
} }
portal.log.Debugfln("Received event %s", evt.ID)
ts := uint64(evt.Timestamp / 1000) ts := uint64(evt.Timestamp / 1000)
status := waProto.WebMessageInfo_ERROR status := waProto.WebMessageInfo_ERROR
@ -1306,7 +1300,7 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
portal.log.Debugln("Database says", sender.MXID, "not in chat and no relaybot, but trying to send anyway") portal.log.Debugln("Database says", sender.MXID, "not in chat and no relaybot, but trying to send anyway")
} else { } else {
portal.log.Debugln("Ignoring message from", sender.MXID, "in chat with no relaybot") portal.log.Debugln("Ignoring message from", sender.MXID, "in chat with no relaybot")
return return nil
} }
} else { } else {
relaybotFormatted = portal.addRelaybotFormat(sender, content) relaybotFormatted = portal.addRelaybotFormat(sender, content)
@ -1316,7 +1310,7 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
if evt.Type == event.EventSticker { if evt.Type == event.EventSticker {
content.MsgType = event.MsgImage content.MsgType = event.MsgImage
} }
var err error
switch content.MsgType { switch content.MsgType {
case event.MsgText, event.MsgEmote, event.MsgNotice: case event.MsgText, event.MsgEmote, event.MsgNotice:
text := content.Body text := content.Body
@ -1341,7 +1335,7 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
case event.MsgImage: case event.MsgImage:
media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaImage) media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaImage)
if media == nil { if media == nil {
return return nil
} }
info.Message.ImageMessage = &waProto.ImageMessage{ info.Message.ImageMessage = &waProto.ImageMessage{
Caption: &media.Caption, Caption: &media.Caption,
@ -1356,7 +1350,7 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
case event.MsgVideo: case event.MsgVideo:
media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaVideo) media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaVideo)
if media == nil { if media == nil {
return return nil
} }
duration := uint32(content.GetInfo().Duration) duration := uint32(content.GetInfo().Duration)
info.Message.VideoMessage = &waProto.VideoMessage{ info.Message.VideoMessage = &waProto.VideoMessage{
@ -1373,7 +1367,7 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
case event.MsgAudio: case event.MsgAudio:
media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaAudio) media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaAudio)
if media == nil { if media == nil {
return return nil
} }
duration := uint32(content.GetInfo().Duration) duration := uint32(content.GetInfo().Duration)
info.Message.AudioMessage = &waProto.AudioMessage{ info.Message.AudioMessage = &waProto.AudioMessage{
@ -1388,7 +1382,7 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
case event.MsgFile: case event.MsgFile:
media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaDocument) media := portal.preprocessMatrixMedia(sender, relaybotFormatted, content, evt.ID, whatsapp.MediaDocument)
if media == nil { if media == nil {
return return nil
} }
info.Message.DocumentMessage = &waProto.DocumentMessage{ info.Message.DocumentMessage = &waProto.DocumentMessage{
Url: &media.URL, Url: &media.URL,
@ -1401,44 +1395,73 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event) {
} }
default: default:
portal.log.Debugln("Unhandled Matrix event:", evt) portal.log.Debugln("Unhandled Matrix event:", evt)
return nil
}
return info
}
func (portal *Portal) wasMessageSent(sender *User, id string) bool {
_, err := sender.Conn.LoadMessagesAfter(portal.Key.JID, id, true, 0)
if err != nil {
if err != whatsapp.ErrServerRespondedWith404 {
portal.log.Warnfln("Failed to check if message was bridged without response: %v", err)
}
return false
}
return true
}
func (portal *Portal) sendErrorMessage(sendErr error) id.EventID {
resp, err := portal.sendMainIntentMessage(event.MessageEventContent{
MsgType: event.MsgNotice,
Body: fmt.Sprintf("\u26a0 Your message may not have been bridged: %v", sendErr),
})
if err != nil {
portal.log.Warnfln("Failed to send bridging error message:", err)
return ""
}
return resp.EventID
}
var timeout = errors.New("message sending timed out")
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 return
} }
portal.log.Debugfln("Received event %s", evt.ID)
info := portal.convertMatrixMessage(sender, evt)
portal.markHandled(sender, info, evt.ID) portal.markHandled(sender, info, evt.ID)
portal.log.Debugln("Sending event", evt.ID, "to WhatsApp") portal.log.Debugln("Sending event", evt.ID, "to WhatsApp")
errChan := make(chan error, 1) errChan := make(chan error, 1)
go sender.Conn.SendRaw(info, errChan) go sender.Conn.SendRaw(info, errChan)
var errorSendResp *mautrix.RespSendEvent var err error
var errorEventID id.EventID
select { select {
case err = <-errChan: case err = <-errChan:
case <-time.After(time.Duration(portal.bridge.Config.Bridge.ConnectionTimeout) * time.Second): case <-time.After(time.Duration(portal.bridge.Config.Bridge.ConnectionTimeout) * time.Second):
portal.log.Warnfln("Response when bridging Matrix event %s is taking long to arrive", evt.ID) if portal.bridge.Config.Bridge.FetchMessageOnTimeout && portal.wasMessageSent(sender, info.Key.GetId()) {
errorSendResp, err = portal.sendMainIntentMessage(event.MessageEventContent{ portal.log.Debugln("Matrix event %s was bridged, but response didn't arrive within timeout")
MsgType: event.MsgNotice, } else {
Body: fmt.Sprintf("\u26a0 Your message may not have been bridged: message sending timed out"), portal.log.Warnfln("Response when bridging Matrix event %s is taking long to arrive", evt.ID)
}) errorEventID = portal.sendErrorMessage(timeout)
if err != nil {
portal.log.Warnfln("Failed to send bridging timeout message:", err)
} }
err = <-errChan err = <-errChan
} }
if err != nil { if err != nil {
portal.log.Errorfln("Error handling Matrix event %s: %v", evt.ID, err) portal.log.Errorfln("Error handling Matrix event %s: %v", evt.ID, err)
_, err = portal.sendMainIntentMessage(event.MessageEventContent{ portal.sendErrorMessage(err)
MsgType: event.MsgNotice,
Body: fmt.Sprintf("\u26a0 Your message may not have been bridged: %v", err),
})
if err != nil {
portal.log.Warnfln("Failed to send bridging failure message:", err)
}
} else { } else {
portal.log.Debugln("Handled Matrix event %s", evt.ID) portal.log.Debugfln("Handled Matrix event %s", evt.ID)
} }
if errorSendResp != nil { if errorEventID != "" {
_, err = portal.MainIntent().RedactEvent(portal.MXID, errorSendResp.EventID) _, err = portal.MainIntent().RedactEvent(portal.MXID, errorEventID)
if err != nil { if err != nil {
portal.log.Warnfln("Failed to redact timeout warning message %s: %v", errorSendResp.EventID, err) portal.log.Warnfln("Failed to redact timeout warning message %s: %v", errorEventID, err)
} }
} }
} }