Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions custompuppet.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"strings"

"maunium.net/go/mautrix"
"maunium.net/go/mautrix/appservice"
Expand Down Expand Up @@ -86,6 +87,9 @@ func (user *User) loginWithSharedSecret() error {
if loginSecret == "appservice" {
client.AccessToken = user.bridge.AS.Registration.AppToken
req.Type = mautrix.AuthTypeAppservice
} else if strings.HasPrefix(loginSecret, "as_token:") {
client.AccessToken = loginSecret[9:]
req.Type = mautrix.AuthTypeAppservice
} else {
mac := hmac.New(sha512.New, []byte(loginSecret))
mac.Write([]byte(user.MXID))
Expand Down
25 changes: 19 additions & 6 deletions portal.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"crypto/sha256"
"errors"
"fmt"
"os/exec"
"path/filepath"
"runtime/debug"
"sort"
Expand Down Expand Up @@ -1410,16 +1411,28 @@ func (portal *Portal) handleMatrixMediaDirect(url id.ContentURI, file *event.Enc
_, isMSC3245Voice := evt.Content.Raw["org.matrix.msc3245.voice"]

// Only convert when sending to iMessage. SMS users probably don't want CAF.
// iMessage voice memos use CAF with Opus codec at 24000 Hz (since iOS 12.2).
// ffmpeg cannot mux Opus into CAF, so we use a two-stage conversion:
// 1. ffmpeg decodes OGG to AIFF (afconvert cannot read OGG)
// 2. afconvert encodes AIFF to CAF/Opus
if portal.Identifier.Service == "iMessage" && isMSC3245Voice && strings.HasPrefix(mimeType, "audio/") {
filePath, err = ffmpeg.ConvertPath(context.TODO(), filePath, ".caf", []string{}, []string{"-c:a", "libopus"}, false)
basePath := strings.TrimSuffix(filePath, filepath.Ext(filePath))
aiffPath := basePath + ".aiff"
cafPath := basePath + ".caf"
if out, convErr := exec.Command("ffmpeg", "-i", filePath, "-f", "aiff", aiffPath).CombinedOutput(); convErr != nil {
log.Errorfln("Failed to decode voice message to AIFF: %v\nffmpeg output: %s", convErr, out)
err = convErr
return
}
if out, convErr := exec.Command("afconvert", "-f", "caff", "-d", "opus", aiffPath, cafPath).CombinedOutput(); convErr != nil {
log.Errorfln("Failed to encode voice message to CAF/Opus: %v\nafconvert output: %s", convErr, out)
err = convErr
return
}
filePath = cafPath
mimeType = "audio/x-caf"
isVoiceMemo = true
filename = filepath.Base(filePath)

if err != nil {
log.Errorfln("Failed to transcode voice message to CAF. Error: %w", err)
return
}
}

resp, err = portal.bridge.IM.SendFile(portal.getTargetGUID("media message", evt.ID, ""), caption, filename, filePath, messageReplyID, messageReplyPart, mimeType, isVoiceMemo, metadata)
Expand Down