diff --git a/api/gitlab/build.go b/api/gitlab/build.go index 64a3df7..d65eed8 100644 --- a/api/gitlab/build.go +++ b/api/gitlab/build.go @@ -69,7 +69,7 @@ func getRefFromBridge(bridge string) (string, error) { switch bridge { case "imessage", "whatsapp": return "master", nil - case "discord", "slack", "gmessages", "signal", "imessagego": + case "discord", "slack", "gmessages", "signal", "imessagego", "meta": return "main", nil default: return "", fmt.Errorf("unknown bridge %s", bridge) @@ -164,7 +164,7 @@ func downloadFile(ctx context.Context, artifactURL, path string) error { func needsLibolmDylib(bridge string) bool { switch bridge { - case "imessage", "whatsapp", "discord", "slack", "gmessages", "signal", "imessagego": + case "imessage", "whatsapp", "discord", "slack", "gmessages", "signal", "imessagego", "meta": return runtime.GOOS == "darwin" default: return false diff --git a/bridgeconfig/meta.tpl.yaml b/bridgeconfig/meta.tpl.yaml new file mode 100644 index 0000000..f0a6e4f --- /dev/null +++ b/bridgeconfig/meta.tpl.yaml @@ -0,0 +1,271 @@ +# Homeserver details. +homeserver: + # The address that this appservice can use to connect to the homeserver. + address: {{ .HungryAddress }} + # The domain of the homeserver (also known as server_name, used for MXIDs, etc). + domain: beeper.local + + # What software is the homeserver running? + # Standard Matrix homeservers like Synapse, Dendrite and Conduit should just use "standard" here. + software: hungry + # The URL to push real-time bridge status to. + # If set, the bridge will make POST requests to this URL whenever a user's FB/IG connection state changes. + # The bridge will use the appservice as_token to authorize requests. + status_endpoint: null + # Endpoint for reporting per-message status. + message_send_checkpoint_endpoint: null + # Does the homeserver support https://github.com/matrix-org/matrix-spec-proposals/pull/2246? + async_media: true + + # Should the bridge use a websocket for connecting to the homeserver? + # The server side is currently not documented anywhere and is only implemented by mautrix-wsproxy, + # mautrix-asmux (deprecated), and hungryserv (proprietary). + websocket: {{ .Websocket }} + # How often should the websocket be pinged? Pinging will be disabled if this is zero. + ping_interval_seconds: 180 + +# Application service host/registration related details. +# Changing these values requires regeneration of the registration. +appservice: + # The address that the homeserver can use to connect to this appservice. + address: null + + # The hostname and port where this appservice should listen. + hostname: {{ if .Websocket }}null{{ else }}{{ .ListenAddr }}{{ end }} + port: {{ if .Websocket }}null{{ else }}{{ .ListenPort }}{{ end }} + + # Database config. + database: + # The database type. "sqlite3-fk-wal" and "postgres" are supported. + type: sqlite3-fk-wal + # The database URI. + # SQLite: A raw file path is supported, but `file:?_txlock=immediate` is recommended. + # https://github.com/mattn/go-sqlite3#connection-string + # Postgres: Connection string. For example, postgres://user:password@host/database?sslmode=disable + # To connect via Unix socket, use something like postgres:///dbname?host=/var/run/postgresql + uri: file:{{.DatabasePrefix}}mautrix-meta.db?_txlock=immediate + # Maximum number of connections. Mostly relevant for Postgres. + max_open_conns: 5 + max_idle_conns: 2 + # Maximum connection idle time and lifetime before they're closed. Disabled if null. + # Parsed with https://pkg.go.dev/time#ParseDuration + max_conn_idle_time: null + max_conn_lifetime: null + + # The unique ID of this appservice. + id: {{ .AppserviceID }} + # Appservice bot details. + bot: + # Username of the appservice bot. + username: {{ .BridgeName }}bot + # Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty + # to leave display name/avatar as-is. + displayname: {{ if eq .Params.meta_platform "facebook" }}Facebook{{ else }}Instagram{{ end }} bridge bot + avatar: mxc://maunium.net/{{ if eq .Params.meta_platform "facebook" }}ygtkteZsXnGJLJHRchUwYWak{{ else }}JxjlbZUlCPULEeHZSwleUXQv{{ end }} + + # Whether or not to receive ephemeral events via appservice transactions. + # Requires MSC2409 support (i.e. Synapse 1.22+). + ephemeral_events: true + + # Should incoming events be handled asynchronously? + # This may be necessary for large public instances with lots of messages going through. + # However, messages will not be guaranteed to be bridged in the same order they were sent in. + async_transactions: false + + # Authentication tokens for AS <-> HS communication. Autogenerated; do not modify. + as_token: {{ .ASToken }} + hs_token: {{ .HSToken }} + +# Prometheus config. +metrics: + # Enable prometheus metrics? + enabled: false + # IP and port where the metrics listener should be. The path is always /metrics + listen: 127.0.0.1:8000 + +meta: + # Which service is this bridge for? Either "facebook" or "instagram". + # Remember to change the appservice id, bot profile info and bridge username_template too. + mode: {{ .Params.meta_platform }} + +# Bridge config +bridge: + # Localpart template of MXIDs for FB/IG users. + username_template: {{ .BridgeName }}_{{ "{{.}}" }} + # Displayname template for FB/IG users. This is also used as the room name in DMs if private_chat_portal_meta is enabled. + displayname_template: {{ `"{{or .ContactName .ProfileName .PhoneNumber "Unknown user" }}"` }} + # Whether to explicitly set the avatar and room name for private chat portal rooms. + # If set to `default`, this will be enabled in encrypted rooms and disabled in unencrypted rooms. + # If set to `always`, all DM rooms will have explicit names and avatars set. + # If set to `never`, DM rooms will never have names and avatars set. + private_chat_portal_meta: never + + portal_message_buffer: 128 + + # Should the bridge create a space for each logged-in user and add bridged rooms to it? + # Users who logged in before turning this on should run `!meta sync-space` to create and fill the space for the first time. + personal_filtering_spaces: true + # Should Matrix m.notice-type messages be bridged? + bridge_notices: true + # Should the bridge send a read receipt from the bridge bot when a message has been sent to FB/IG? + delivery_receipts: false + # Whether the bridge should send the message status as a custom com.beeper.message_send_status event. + message_status_events: true + # Whether the bridge should send error notices via m.notice events when a message fails to bridge. + message_error_notices: false + # Should the bridge update the m.direct account data event when double puppeting is enabled. + # Note that updating the m.direct event is not atomic (except with mautrix-asmux) + # and is therefore prone to race conditions. + sync_direct_chat_list: false + # Set this to true to tell the bridge to re-send m.bridge events to all rooms on the next run. + # This field will automatically be changed back to false after it, except if the config file is not writable. + resend_bridge_info: false + # Send captions in the same message as images. This will send data compatible with both MSC2530. + # This is currently not supported in most clients. + caption_in_message: true + # Servers to always allow double puppeting from + double_puppet_server_map: + {{ .BeeperDomain }}: {{ .HungryAddress }} + # Allow using double puppeting from any server with a valid client .well-known file. + double_puppet_allow_discovery: false + # Shared secrets for https://github.com/devture/matrix-synapse-shared-secret-auth + # + # If set, double puppeting will be enabled automatically for local users + # instead of users having to find an access token and run `login-matrix` + # manually. + login_shared_secret_map: + {{ .BeeperDomain }}: "as_token:{{ .ASToken }}" + + # Maximum time for handling Matrix events. Duration strings formatted for https://pkg.go.dev/time#ParseDuration + # Null means there's no enforced timeout. + message_handling_timeout: + # Send an error message after this timeout, but keep waiting for the response until the deadline. + # This is counted from the origin_server_ts, so the warning time is consistent regardless of the source of delay. + # If the message is older than this when it reaches the bridge, the message won't be handled at all. + error_after: null + # Drop messages after this timeout. They may still go through if the message got sent to the servers. + # This is counted from the time the bridge starts handling the message. + deadline: 120s + + # The prefix for commands. Only required in non-management rooms. + command_prefix: default + # Messages sent upon joining a management room. + # Markdown is supported. The defaults are listed below. + management_room_text: + # Sent when joining a room. + welcome: "Hello, I'm {{ if eq .Params.meta_platform "facebook" }}a Facebook{{ else }}an Instagram{{ end }} bridge bot." + # Sent when joining a management room and the user is already logged in. + welcome_connected: "Use `help` for help." + # Sent when joining a management room and the user is not logged in. + welcome_unconnected: "Use `help` for help or `login` to log in." + # Optional extra text sent when joining a management room. + additional_help: "" + + # End-to-bridge encryption support options. + # + # See https://docs.mau.fi/bridges/general/end-to-bridge-encryption.html for more info. + encryption: + # Allow encryption, work in group chat rooms with e2ee enabled + allow: true + # Default to encryption, force-enable encryption in all portals the bridge creates + # This will cause the bridge bot to be in private chats for the encryption to work properly. + default: true + # Whether to use MSC2409/MSC3202 instead of /sync long polling for receiving encryption-related data. + appservice: true + # Require encryption, drop any unencrypted messages. + require: true + # Enable key sharing? If enabled, key requests for rooms where users are in will be fulfilled. + # You must use a client that supports requesting keys from other users to use this feature. + allow_key_sharing: true + # Options for deleting megolm sessions from the bridge. + delete_keys: + # Beeper-specific: delete outbound sessions when hungryserv confirms + # that the user has uploaded the key to key backup. + delete_outbound_on_ack: true + # Don't store outbound sessions in the inbound table. + dont_store_outbound: false + # Ratchet megolm sessions forward after decrypting messages. + ratchet_on_decrypt: true + # Delete fully used keys (index >= max_messages) after decrypting messages. + delete_fully_used_on_decrypt: true + # Delete previous megolm sessions from same device when receiving a new one. + delete_prev_on_new_session: true + # Delete megolm sessions received from a device when the device is deleted. + delete_on_device_delete: true + # Periodically delete megolm sessions when 2x max_age has passed since receiving the session. + periodically_delete_expired: true + # Delete inbound megolm sessions that don't have the received_at field used for + # automatic ratcheting and expired session deletion. This is meant as a migration + # to delete old keys prior to the bridge update. + delete_outdated_inbound: false + # What level of device verification should be required from users? + # + # Valid levels: + # unverified - Send keys to all device in the room. + # cross-signed-untrusted - Require valid cross-signing, but trust all cross-signing keys. + # cross-signed-tofu - Require valid cross-signing, trust cross-signing keys on first use (and reject changes). + # cross-signed-verified - Require valid cross-signing, plus a valid user signature from the bridge bot. + # Note that creating user signatures from the bridge bot is not currently possible. + # verified - Require manual per-device verification + # (currently only possible by modifying the `trust` column in the `crypto_device` database table). + verification_levels: + # Minimum level for which the bridge should send keys to when bridging messages from FB/IG to Matrix. + receive: cross-signed-tofu + # Minimum level that the bridge should accept for incoming Matrix messages. + send: cross-signed-tofu + # Minimum level that the bridge should require for accepting key requests. + share: cross-signed-tofu + # Options for Megolm room key rotation. These options allow you to + # configure the m.room.encryption event content. See: + # https://spec.matrix.org/v1.3/client-server-api/#mroomencryption for + # more information about that event. + rotation: + # Enable custom Megolm room key rotation settings. Note that these + # settings will only apply to rooms created after this option is + # set. + enable_custom: true + # The maximum number of milliseconds a session should be used + # before changing it. The Matrix spec recommends 604800000 (a week) + # as the default. + milliseconds: 2592000000 + # The maximum number of messages that should be sent with a given a + # session before changing it. The Matrix spec recommends 100 as the + # default. + messages: 10000 + + # Disable rotating keys when a user's devices change? + # You should not enable this option unless you understand all the implications. + disable_device_change_key_rotation: true + + # Settings for provisioning API + provisioning: + # Prefix for the provisioning API paths. + prefix: /_matrix/provision + # Shared secret for authentication. If set to "generate", a random secret will be generated, + # or if set to "disable", the provisioning API will be disabled. + shared_secret: {{ .ProvisioningSecret }} + + # Permissions for using the bridge. + # Permitted values: + # relay - Talk through the relaybot (if enabled), no access otherwise + # user - Access to use the bridge to chat with a Meta account. + # admin - User level and some additional administration tools + # Permitted keys: + # * - All Matrix users + # domain - All users on that homeserver + # mxid - Specific user + permissions: + "{{ .UserID }}": admin + +# Logging config. See https://github.com/tulir/zeroconfig for details. +logging: + min_level: debug + writers: + - type: stdout + format: pretty-colored + - type: file + format: json + filename: ./logs/mautrix-meta.log + max_size: 100 + max_backups: 10 + compress: false diff --git a/cmd/bbctl/bridgeutil.go b/cmd/bbctl/bridgeutil.go index e4e8999..c0a1d49 100644 --- a/cmd/bbctl/bridgeutil.go +++ b/cmd/bbctl/bridgeutil.go @@ -14,27 +14,28 @@ import ( ) var allowedBridgeRegex = regexp.MustCompile("^[a-z0-9-]{1,32}$") -var officialBridges = map[string]string{ - "discord": "discord", - "facebook": "facebook", - "googlechat": "googlechat", - "gchat": "googlechat", - "imessage": "imessage", - "imessagego": "imessagego", - "instagram": "instagram", - "linkedin": "linkedin", - "signal": "signal", - "slack": "slack", - "telegram": "telegram", - "twitter": "twitter", - "whatsapp": "whatsapp", - "irc": "heisenbridge", - "heisenbridge": "heisenbridge", - "androidsms": "androidsms", - "gmessages": "gmessages", - "sms": "gmessages", - "rcs": "gmessages", - "googlemessages": "gmessages", + +type bridgeTypeToNames struct { + typeName string + names []string +} + +var officialBridges = []bridgeTypeToNames{ + {"discord", []string{"discord"}}, + {"meta", []string{"meta", "instagramgo", "facebookgo"}}, + {"facebook", []string{"facebook"}}, + {"googlechat", []string{"googlechat", "gchat"}}, + {"imessagego", []string{"imessagego"}}, + {"imessage", []string{"imessage"}}, + {"instagram", []string{"instagram"}}, + {"linkedin", []string{"linkedin"}}, + {"signal", []string{"signal"}}, + {"slack", []string{"slack"}}, + {"telegram", []string{"telegram"}}, + {"twitter", []string{"twitter"}}, + {"whatsapp", []string{"whatsapp"}}, + {"heisenbridge", []string{"irc", "heisenbridge"}}, + {"gmessages", []string{"gmessages", "googlemessages", "rcs", "sms"}}, } var websocketBridges = map[string]bool{ @@ -46,6 +47,7 @@ var websocketBridges = map[string]bool{ "imessage": true, "imessagego": true, "signal": true, + "meta": true, } func doOutputFile(ctx *cli.Context, name, data string) error { @@ -77,19 +79,14 @@ func validateBridgeName(ctx *cli.Context, bridge string) error { } func guessOrAskBridgeType(bridge, bridgeType string) (string, error) { - // Hack because the officialBridges map is unordered - if bridgeType == "" { - if strings.Contains(bridge, "imessagego") { - bridgeType = "imessagego" - } else if strings.Contains(bridge, "imessage") { - bridgeType = "imessage" - } - } if bridgeType == "" { - for key, value := range officialBridges { - if strings.Contains(bridge, key) { - bridgeType = value - break + Outer: + for _, br := range officialBridges { + for _, name := range br.names { + if strings.Contains(bridge, name) { + bridgeType = br.typeName + break Outer + } } } } diff --git a/cmd/bbctl/config.go b/cmd/bbctl/config.go index de7c469..3132fe6 100644 --- a/cmd/bbctl/config.go +++ b/cmd/bbctl/config.go @@ -67,6 +67,23 @@ func simpleDescriptions(descs map[string]string) func(string, int) string { } var askParams = map[string]func(map[string]string) (bool, error){ + "meta": func(extraParams map[string]string) (bool, error) { + metaPlatform := extraParams["meta_platform"] + if metaPlatform == "" { + err := survey.AskOne(&survey.Select{ + Message: "Which Meta platform do you want to bridge?", + Options: []string{"instagram", "facebook"}, + }, &metaPlatform) + if err != nil { + return false, err + } + extraParams["meta_platform"] = metaPlatform + return true, nil + } else if metaPlatform != "instagram" && metaPlatform != "facebook" { + return false, UserError{"Invalid Meta platform specified"} + } + return false, nil + }, "imessagego": func(extraParams map[string]string) (bool, error) { nacToken := extraParams["nac_token"] var didAddParams bool @@ -174,6 +191,7 @@ var bridgeIPSuffix = map[string]string{ "telegram": "17", "whatsapp": "18", "facebook": "19", + "meta": "19", "googlechat": "20", "twitter": "27", "signal": "28", @@ -303,7 +321,7 @@ func generateBridgeConfig(ctx *cli.Context) error { } var startupCommand, installInstructions string switch cfg.BridgeType { - case "imessage", "whatsapp", "discord", "slack", "gmessages", "signal": + case "imessage", "whatsapp", "discord", "slack", "gmessages", "signal", "meta": startupCommand = fmt.Sprintf("mautrix-%s", cfg.BridgeType) if outputPath != "config.yaml" && outputPath != "" { startupCommand += " -c " + outputPath diff --git a/cmd/bbctl/run.go b/cmd/bbctl/run.go index 46316fd..f3fda72 100644 --- a/cmd/bbctl/run.go +++ b/cmd/bbctl/run.go @@ -288,7 +288,7 @@ func runBridge(ctx *cli.Context) error { var bridgeArgs []string var needsWebsocketProxy bool switch cfg.BridgeType { - case "imessage", "imessagego", "whatsapp", "discord", "slack", "gmessages", "signal": + case "imessage", "imessagego", "whatsapp", "discord", "slack", "gmessages", "signal", "meta": binaryName := fmt.Sprintf("mautrix-%s", cfg.BridgeType) if cfg.BridgeType == "imessagego" { binaryName = "beeper-imessage"