Add Matrix->WhatsApp media bridging

This commit is contained in:
Tulir Asokan 2018-08-24 22:31:18 +03:00
parent 5eacaafc93
commit 5f6955d36f
2 changed files with 82 additions and 12 deletions

View File

@ -1,9 +1,9 @@
# Features & roadmap # Features & roadmap
* Matrix → WhatsApp * Matrix → WhatsApp
* [ ] Message content * [x] Message content
* [x] Plain text * [x] Plain text
* [x] Formatted messages * [x] Formatted messages
* [ ] Media/files * [x] Media/files
* [ ] Message redactions * [ ] Message redactions
* [ ] Presence * [ ] Presence
* [ ] Typing notifications * [ ] Typing notifications

View File

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"html" "html"
"image" "image"
"io"
"math/rand" "math/rand"
"mime" "mime"
"net/http" "net/http"
@ -309,9 +310,9 @@ var boldRegex = regexp.MustCompile("([\\s>_~]|^)\\*(.+?)\\*([^a-zA-Z\\d]|$)")
var strikethroughRegex = regexp.MustCompile("([\\s>_*]|^)~(.+?)~([^a-zA-Z\\d]|$)") var strikethroughRegex = regexp.MustCompile("([\\s>_*]|^)~(.+?)~([^a-zA-Z\\d]|$)")
var whatsAppFormat = map[*regexp.Regexp]string{ var whatsAppFormat = map[*regexp.Regexp]string{
codeBlockRegex: "<pre>$1</pre>", codeBlockRegex: "<pre>$1</pre>",
italicRegex: "$1<em>$2</em>$3", italicRegex: "$1<em>$2</em>$3",
boldRegex: "$1<strong>$2</strong>$3", boldRegex: "$1<strong>$2</strong>$3",
strikethroughRegex: "$1<del>$2</del>$3", strikethroughRegex: "$1<del>$2</del>$3",
} }
@ -475,27 +476,96 @@ func makeMessageID() string {
return strings.ToUpper(hex.EncodeToString(b)) return strings.ToUpper(hex.EncodeToString(b))
} }
func (portal *Portal) PreprocessMatrixMedia(evt *gomatrix.Event) (string, io.ReadCloser, []byte) {
if evt.Content.Info == nil {
evt.Content.Info = &gomatrix.FileInfo{}
}
caption := evt.Content.Body
exts, err := mime.ExtensionsByType(evt.Content.Info.MimeType)
for _, ext := range exts {
if strings.HasSuffix(caption, ext) {
caption = ""
break
}
}
content, err := portal.MainIntent().Download(evt.Content.URL)
if err != nil {
portal.log.Errorln("Failed to download media in %s: %v", evt.ID, err)
return "", nil, nil
}
thumbnail, err := portal.MainIntent().DownloadBytes(evt.Content.Info.ThumbnailURL)
return caption, content, thumbnail
}
func (portal *Portal) HandleMatrixMessage(evt *gomatrix.Event) { func (portal *Portal) HandleMatrixMessage(evt *gomatrix.Event) {
info := whatsapp.MessageInfo{
Id: makeMessageID(),
RemoteJid: portal.JID,
}
var err error var err error
switch evt.Content.MsgType { switch evt.Content.MsgType {
case gomatrix.MsgText: case gomatrix.MsgText, gomatrix.MsgEmote:
text := evt.Content.Body text := evt.Content.Body
if evt.Content.Format == gomatrix.FormatHTML { if evt.Content.Format == gomatrix.FormatHTML {
text = htmlParser.Parse(evt.Content.FormattedBody) text = htmlParser.Parse(evt.Content.FormattedBody)
} }
id := makeMessageID() if evt.Content.MsgType == gomatrix.MsgEmote {
text = "/me " + text
}
err = portal.user.Conn.Send(whatsapp.TextMessage{ err = portal.user.Conn.Send(whatsapp.TextMessage{
Text: text, Text: text,
Info: whatsapp.MessageInfo{ Info: info,
Id: id, })
RemoteJid: portal.JID, case gomatrix.MsgImage:
}, caption, content, thumbnail := portal.PreprocessMatrixMedia(evt)
if content == nil {
return
}
err = portal.user.Conn.Send(whatsapp.ImageMessage{
Caption: caption,
Content: content,
Thumbnail: thumbnail,
Type: evt.Content.Info.MimeType,
Info: info,
})
case gomatrix.MsgVideo:
caption, content, thumbnail := portal.PreprocessMatrixMedia(evt)
if content == nil {
return
}
err = portal.user.Conn.Send(whatsapp.VideoMessage{
Caption: caption,
Content: content,
Thumbnail: thumbnail,
Type: evt.Content.Info.MimeType,
Info: info,
})
case gomatrix.MsgAudio:
_, content, _ := portal.PreprocessMatrixMedia(evt)
if content == nil {
return
}
err = portal.user.Conn.Send(whatsapp.AudioMessage{
Content: content,
Type: evt.Content.Info.MimeType,
Info: info,
})
case gomatrix.MsgFile:
_, content, thumbnail := portal.PreprocessMatrixMedia(evt)
if content == nil {
return
}
err = portal.user.Conn.Send(whatsapp.DocumentMessage{
Content: content,
Thumbnail: thumbnail,
Type: evt.Content.Info.MimeType,
Info: info,
}) })
portal.MarkHandled(id, evt.ID)
default: default:
portal.log.Debugln("Unhandled Matrix event:", evt) portal.log.Debugln("Unhandled Matrix event:", evt)
return return
} }
portal.MarkHandled(info.Id, evt.ID)
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)
} else { } else {