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
23 changes: 23 additions & 0 deletions bridgeconfig/imessage-v2.tpl.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Network-specific config options for iMessage (rustpush / bridgev2)
network:
# Displayname template for iMessage users.
displayname_template: '{{`{{if .FirstName}}{{.FirstName}}{{if .LastName}} {{.LastName}}{{end}}{{else if .Nickname}}{{.Nickname}}{{else if .Phone}}{{.Phone}}{{else if .Email}}{{.Email}}{{else}}{{.ID}}{{end}}`}}'
# Should SMS chats always be in the same room as iMessage chats with the same phone number?
disable_sms_portals: false
# Send captions in the same message as images using MSC2530?
caption_in_message: true
# Should we convert heif images to jpeg before re-uploading?
convert_heif: false
# Should we convert tiff images to jpeg before re-uploading?
convert_tiff: true
# How many days back to look for chats during initial sync (default: 365)
initial_sync_days: 365

{{ setfield . "CommandPrefix" "!im" -}}
{{ setfield . "DatabaseFileName" "mautrix-imessage" -}}
{{ setfield . "BridgeTypeName" "iMessage" -}}
{{ setfield . "BridgeTypeIcon" "mxc://maunium.net/tManJEpANASZvDVzvRvhILdX" -}}
{{ setfield . "DefaultPickleKey" "mautrix.bridge.e2ee" -}}
{{ setfield . "MaxInitialMessages" 100 -}}
{{ setfield . "MaxBackwardMessages" 100 -}}
{{ template "bridgev2.tpl.yaml" . }}
2 changes: 2 additions & 0 deletions cmd/bbctl/bridgeutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var officialBridges = []bridgeTypeToNames{
{"discord", []string{"discord"}},
{"meta", []string{"meta", "instagram", "facebook"}},
{"googlechat", []string{"googlechat", "gchat"}},
{"imessage-v2", []string{"imessage-v2"}},
{"imessagego", []string{"imessagego"}},
{"imessage", []string{"imessage"}},
{"linkedin", []string{"linkedin"}},
Expand All @@ -46,6 +47,7 @@ var websocketBridges = map[string]bool{
"gvoice": true,
"heisenbridge": true,
"imessage": true,
"imessage-v2": true,
"imessagego": true,
"signal": true,
"bridgev2": true,
Expand Down
27 changes: 21 additions & 6 deletions cmd/bbctl/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ var askParams = map[string]func(string, map[string]string) (bool, error){
}
return didAddParams, nil
},
"imessage-v2": func(bridgeName string, extraParams map[string]string) (bool, error) {
// imessage-v2 is a bridgev2 bridge with rustpush, no connector selection needed
return false, nil
},
"imessage": func(bridgeName string, extraParams map[string]string) (bool, error) {
platform := extraParams["imessage_platform"]
barcelonaPath := extraParams["barcelona_path"]
Expand All @@ -131,13 +135,14 @@ var askParams = map[string]func(string, map[string]string) (bool, error){
if platform == "" {
err := survey.AskOne(&survey.Select{
Message: "Select iMessage connector:",
Options: []string{"mac", "mac-nosip", "bluebubbles"},
Options: []string{"rustpush", "mac", "mac-nosip", "bluebubbles"},
Description: simpleDescriptions(map[string]string{
"rustpush": "Connect directly to Apple's iMessage servers via rustpush (macOS 14.2+, recommended)",
"mac": "Use AppleScript to send messages and read chat.db for incoming data - only requires Full Disk Access (from system settings)",
"mac-nosip": "Use Barcelona to interact with private APIs - requires disabling SIP and AMFI",
"bluebubbles": "Connect to a BlueBubbles instance",
}),
Default: "mac",
Default: "rustpush",
}, &platform)
if err != nil {
return didAddParams, err
Expand Down Expand Up @@ -233,7 +238,9 @@ func doGenerateBridgeConfig(ctx *cli.Context, bridge string) (*generatedBridgeCo
}
existingBridge, ok := whoami.User.Bridges[bridge]
var bridgeType string
if ok && existingBridge.BridgeState.BridgeType != "" {
if cliType := ctx.String("type"); cliType != "" {
bridgeType = cliType
} else if ok && existingBridge.BridgeState.BridgeType != "" {
bridgeType = existingBridge.BridgeState.BridgeType
} else {
bridgeType, err = guessOrAskBridgeType(bridge, ctx.String("type"))
Expand Down Expand Up @@ -277,13 +284,18 @@ func doGenerateBridgeConfig(ctx *cli.Context, bridge string) (*generatedBridgeCo
if dbPrefix != "" {
dbPrefix = filepath.Join(dbPrefix, bridge+"-")
}
websocket := websocketBridges[bridgeType]
// When rustpush is selected under the imessage type, use the imessage-v2 template.
configTemplate := bridgeType
if bridgeType == "imessage" && extraParams["imessage_platform"] == "rustpush" {
configTemplate = "imessage-v2"
}
websocket := websocketBridges[bridgeType] || configTemplate == "imessage-v2"
var listenAddress string
var listenPort uint16
if !websocket {
listenAddress, listenPort, reg.Registration.URL = getBridgeWebsocketProxyConfig(bridge, bridgeType)
}
cfg, err := bridgeconfig.Generate(bridgeType, bridgeconfig.Params{
cfg, err := bridgeconfig.Generate(configTemplate, bridgeconfig.Params{
HungryAddress: reg.HomeserverURL,
BeeperDomain: ctx.String("homeserver"),
Websocket: websocket,
Expand All @@ -302,7 +314,7 @@ func doGenerateBridgeConfig(ctx *cli.Context, bridge string) (*generatedBridgeCo
ProvisioningSecret: whoami.User.AsmuxData.LoginToken,
})
return &generatedBridgeConfig{
BridgeType: bridgeType,
BridgeType: configTemplate,
Config: cfg,
RegisterJSON: reg,
}, err
Expand Down Expand Up @@ -346,6 +358,9 @@ func generateBridgeConfig(ctx *cli.Context) error {
}
var startupCommand, installInstructions string
switch cfg.BridgeType {
case "imessage-v2":
startupCommand = "mautrix-imessage-v2 -c " + outputPath
installInstructions = "https://github.com/lrhodin/imessage"
case "imessage", "whatsapp", "discord", "slack", "gmessages", "gvoice", "signal", "meta", "twitter", "bluesky", "linkedin":
startupCommand = fmt.Sprintf("mautrix-%s", cfg.BridgeType)
if outputPath != "config.yaml" && outputPath != "<config file>" {
Expand Down
8 changes: 7 additions & 1 deletion cmd/bbctl/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,18 @@ func doRegisterBridge(ctx *cli.Context, bridge, bridgeType string, onlyGet bool)
state = status.StateStarting
}

// Beeper's UI recognizes "imessage" — map imessage-v2 so it displays correctly.
displayBridgeType := bridgeType
if bridgeType == "imessage-v2" {
displayBridgeType = "imessage"
}

if !ctx.Bool("no-state") {
err = beeperapi.PostBridgeState(ctx.String("homeserver"), GetEnvConfig(ctx).Username, bridge, resp.AppToken, beeperapi.ReqPostBridgeState{
StateEvent: state,
Reason: "SELF_HOST_REGISTERED",
IsSelfHosted: true,
BridgeType: bridgeType,
BridgeType: displayBridgeType,
})
if err != nil {
return nil, fmt.Errorf("failed to mark bridge as RUNNING: %w", err)
Expand Down
7 changes: 6 additions & 1 deletion cmd/bbctl/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ func compileGoBridge(ctx context.Context, buildDir, binaryPath, bridgeType strin
repo := fmt.Sprintf("https://github.com/mautrix/%s.git", bridgeType)
if bridgeType == "imessagego" {
repo = "https://github.com/beeper/imessage.git"
} else if bridgeType == "imessage-v2" {
repo = "https://github.com/lrhodin/imessage.git"
}
log.Printf("Cloning [cyan]%s[reset] to [cyan]%s[reset]", repo, buildDir)
err = makeCmd(ctx, buildDirParent, "git", "clone", repo, buildDir).Run()
Expand Down Expand Up @@ -308,7 +310,7 @@ func runBridge(ctx *cli.Context) error {
var bridgeArgs []string
var needsWebsocketProxy bool
switch cfg.BridgeType {
case "imessage", "imessagego", "whatsapp", "discord", "slack", "gmessages", "gvoice",
case "imessage", "imessagego", "imessage-v2", "whatsapp", "discord", "slack", "gmessages", "gvoice",
"signal", "meta", "twitter", "bluesky", "linkedin", "telegram":
ciBridgeType := cfg.BridgeType
binaryName := fmt.Sprintf("mautrix-%s", cfg.BridgeType)
Expand Down Expand Up @@ -337,6 +339,9 @@ func runBridge(ctx *cli.Context) error {
return fmt.Errorf("failed to compile bridge: %w", err)
}
} else if overrideBridgeCmd == "" {
if cfg.BridgeType == "imessage-v2" {
return UserError{"imessage-v2 does not have prebuilt binaries. Use --compile to build locally, or see https://github.com/lrhodin/imessage"}
}
err = updateGoBridge(ctx.Context, bridgeCmd, ciBridgeType, ciV2, ctx.Bool("no-update"))
if errors.Is(err, gitlab.ErrNotBuiltInCI) {
return UserError{fmt.Sprintf("Binaries for %s are not built in the CI. Use --compile to tell bbctl to build the bridge locally.", binaryName)}
Expand Down