diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index d741e40..0000000 --- a/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*] -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -indent_style = space -indent_size = 4 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eca8b9b..6e08691 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,13 +1,19 @@ variables: - IMAGE_NAME: thallian/matrix-appservice-whatsapp + CONTAINER_NAME: thallian/matrix-appservice-whatsapp build: stage: build + image: + name: gcr.io/kaniko-project/executor:debug + entrypoint: [""] script: - - docker login -u $DOCKER_USER -p $DOCKER_PASSWORD $DOCKER_REGISTRY - - docker build -t $IMAGE_NAME:$CI_COMMIT_SHA . - - docker build -t $IMAGE_NAME:$CI_COMMIT_REF_NAME . - - docker build -t $IMAGE_NAME:latest . - - docker push $IMAGE_NAME:$CI_COMMIT_SHA - - docker push $IMAGE_NAME:$CI_COMMIT_REF_NAME - - docker push $IMAGE_NAME:latest + - mkdir -p /kaniko/.docker + - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(printf "%s:%s" "$CI_REGISTRY_USER" "$CI_REGISTRY_PASSWORD" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json + - >- + /kaniko/executor + --cache=false + --context "$CI_PROJECT_DIR" + --dockerfile "$CI_PROJECT_DIR/Dockerfile" + --destination "$CONTAINER_NAME:$CI_COMMIT_SHA" + --destination "$CONTAINER_NAME:$CI_COMMIT_REF_NAME" + --destination "$CONTAINER_NAME:latest" diff --git a/Dockerfile b/Dockerfile index 46408f4..d87758d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM golang:1-alpine3.12 as builder +FROM golang:1.18.0-alpine3.15 as builder -ENV VERSION=v0.1.5 +ENV VERSION=v0.3.0 RUN apk --no-cache add \ git \ @@ -27,10 +27,8 @@ COPY --from=builder /build/bin/mautrix-whatsapp /bin/mautrix-whatsapp RUN apk --no-cache add \ ca-certificates \ - libstdc++ - -RUN apk --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community add \ - olm + libstdc++ \ + olm WORKDIR /var/lib/matrix-bridge diff --git a/rootfs/etc/confd/templates/config.yaml.tmpl b/rootfs/etc/confd/templates/config.yaml.tmpl index fb824ec..6adc887 100644 --- a/rootfs/etc/confd/templates/config.yaml.tmpl +++ b/rootfs/etc/confd/templates/config.yaml.tmpl @@ -5,15 +5,26 @@ homeserver: # The domain of the homeserver (for MXIDs, etc). domain: "{{ getenv "SERVER_DOMAIN" }}" + # Is the homeserver actually mautrix-asmux? + asmux: false + # The URL to push real-time bridge status to. + # If set, the bridge will make POST requests to this URL whenever a user's whatsapp 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: false + # 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: http://{{getenv "HOSTNAME"}}:8080 + address: http://{{getenv "HOSTNAME"}}:29318 # The hostname and port where this appservice should listen. hostname: 0.0.0.0 - port: 8080 + port: 29318 # Database config. database: @@ -21,40 +32,229 @@ appservice: type: postgres # The database URI. Usually file name. https://github.com/mattn/go-sqlite3#connection-string uri: {{ getenv "DATABASE_DATASOURCE" }} - # Path to the Matrix room state store. - state_store_path: /var/lib/matrix-bridge/data/mx-state.json + max_open_conns: 20 + 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 + + + # 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: generate + # Segment API key to enable analytics tracking for web server + # endpoints. Set to null to disable. + # Currently the only events are login start, QR code retrieve, and login + # success/failure. + segment_key: null # The unique ID of this appservice. id: whatsapp # Appservice bot details. bot: # Username of the appservice bot. - username: _whatsapp_bot + username: whatsapp_ot # Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty # to leave display name/avatar as-is. - displayname: WhatsApp Bridge Bot + displayname: WhatsApp bridge bot avatar: mxc://maunium.net/NeXNQarUbrlYBiPCpprYsRqr + + # Whether or not to receive ephemeral events via appservice transactions. + # Requires MSC2409 support (i.e. Synapse 1.22+). + # You should disable bridge -> sync_with_custom_puppets when this is enabled. + ephemeral_events: false + # Authentication tokens for AS <-> HS communication. Autogenerated; do not modify. as_token: "{{ getenv "AS_TOKEN" }}" hs_token: "{{ getenv "HS_TOKEN" }}" -# Bridge config. Currently unused. +# 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:8001 + +# Config for things that are directly sent to WhatsApp. +whatsapp: + # Device name that's shown in the "WhatsApp Web" section in the mobile app. + os_name: Mautrix-WhatsApp bridge + # Browser name that determines the logo shown in the mobile app. + # Must be "unknown" for a generic icon or a valid browser name if you want a specific icon. + # List of valid browser names: https://github.com/tulir/whatsmeow/blob/2a72655ef600a7fd7a2e98d53ec6da029759c4b8/binary/proto/def.proto#L1582-L1594 + browser_name: unknown + +# Bridge config bridge: # Localpart template of MXIDs for WhatsApp users. # {{.}} is replaced with the phone number of the WhatsApp user. username_template: whatsapp_{{"{{"}}.{{"}}"}} # Displayname template for WhatsApp users. - # {{.Notify}} - nickname set by the WhatsApp user - # {{.Jid}} - phone number (international format) + # {{.PushName}} - nickname set by the WhatsApp user + # {{.BusinessName}} - validated WhatsApp business name + # {{.Phone}} - phone number (international format) # The following variables are also available, but will cause problems on multi-user instances: - # {{.Name}} - display name from contact list - # {{.Short}} - short display name from contact list - displayname_template: "{{"{{"}}if .Notify{{"}}"}}{{"{{"}}.Notify{{"}}"}}{{"{{"}}else{{"}}"}}{{"{{"}}.Jid{{"}}"}}{{"{{"}}end{{"}}"}} (WA)" + # {{.FullName}} - full name from contact list + # {{.FirstName}} - first name from contact list + displayname_template: "{{"{{"}}if .PushName{{"}}"}}{{"{{"}}.PushName{{"}}"}}{{"{{"}}else if .BusinessName{{"}}"}}{{"{{"}}.BusinessName{{"}}"}}{{"{{"}}else{{"}}"}}{{"{{"}}.JID{{"}}"}}{{"{{"}}end{{"}}"}} (WA)" + # 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 `!wa sync space` to create and fill the space for the first time. + personal_filtering_spaces: false + # Should the bridge send a read receipt from the bridge bot when a message has been sent to WhatsApp? + delivery_receipts: false + # Should incoming calls send a message to the Matrix room? + call_start_notices: true + # Should another user's cryptographic identity changing send a message to Matrix? + identity_change_notices: false + portal_message_buffer: 128 + # Settings for handling history sync payloads. These settings only apply right after login, + # because the phone only sends the history sync data once, and there's no way to re-request it + # (other than logging out and back in again). + history_sync: + # Should the bridge create portals for chats in the history sync payload? + create_portals: true + # Maximum age of chats in seconds to create portals for. Set to 0 to create portals for all chats in sync payload. + max_age: 604800 + # Enable backfilling history sync payloads from WhatsApp using batch sending? + # This requires a server with MSC2716 support, which is currently an experimental feature in synapse. + # It can be enabled by setting experimental_features -> msc2716_enabled to true in homeserver.yaml. + # Note that prior to Synapse 1.49, there were some bugs with the implementation, especially if using event persistence workers. + # There are also still some issues in Synapse's federation implementation. + backfill: false + # Use double puppets for backfilling? + # In order to use this, the double puppets must be in the appservice's user ID namespace + # (because the bridge can't use the double puppet access token with batch sending). + # This only affects double puppets on the local server, double puppets on other servers will never be used. + double_puppet_backfill: false + # Should the bridge request a full sync from the phone when logging in? + # This bumps the size of history syncs from 3 months to 1 year. + request_full_sync: false + # Should puppet avatars be fetched from the server even if an avatar is already set? + user_avatar_sync: true + # Should Matrix users leaving groups be bridged to WhatsApp? + bridge_matrix_leave: true + # Should the bridge sync with double puppeting to receive EDUs that aren't normally sent to appservices. + sync_with_custom_puppets: true + # 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 + # When double puppeting is enabled, users can use `!wa toggle` to change whether + # presence and read receipts are bridged. These settings set the default values. + # Existing users won't be affected when these are changed. + default_bridge_receipts: true + default_bridge_presence: true + # Send the presence as "available" to whatsapp when users start typing on a portal. + # This works as a workaround for homeservers that do not support presence, and allows + # users to see when the whatsapp user on the other side is typing during a conversation. + send_presence_on_typing: false + # Should the bridge always send "active" delivery receipts (two gray ticks on WhatsApp) + # even if the user isn't marked as online (e.g. when presence bridging isn't enabled)? + # + # By default, the bridge acts like WhatsApp web, which only sends active delivery + # receipts when it's in the foreground. + force_active_delivery_receipts: false + # Servers to always allow double puppeting from + double_puppet_server_map: + example.com: https://example.com + # 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: + example.com: foobar + # Should the bridge explicitly set the avatar and room name for private chat portal rooms? + private_chat_portal_meta: false + # Should Matrix m.notice-type messages be bridged? + bridge_notices: true + # 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 + # When using double puppeting, should muted chats be muted in Matrix? + mute_bridging: false + # When using double puppeting, should archived chats be moved to a specific tag in Matrix? + # Note that WhatsApp unarchives chats when a message is received, which will also be mirrored to Matrix. + # This can be set to a tag (e.g. m.lowpriority), or null to disable. + archive_tag: null + # Same as above, but for pinned chats. The favorite tag is called m.favourite + pinned_tag: null + # Should mute status and tags only be bridged when the portal room is created? + tag_only_on_create: true + # Should WhatsApp status messages be bridged into a Matrix room? + # Disabling this won't affect already created status broadcast rooms. + enable_status_broadcast: true + # Should the status broadcast room be muted and moved into low priority by default? + # This is only applied when creating the room, the user can unmute/untag it later. + mute_status_broadcast: true + # Should the bridge use thumbnails from WhatsApp? + # They're disabled by default due to very low resolution. + whatsapp_thumbnail: false + # Allow invite permission for user. User can invite any bots to room with whatsapp + # users (private chat and groups) + allow_user_invite: false + # Whether or not created rooms should have federation enabled. + # If false, created portal rooms will never be federated. + federate_rooms: true + # Whether to enable disappearing messages in groups. If enabled, then the expiration time of + # the messages will be determined by the first user to read the message, rather than individually. + # If the bridge only has a single user, this can be turned on safely. + disappearing_messages_in_groups: false + # Should the bridge never send alerts to the bridge management room? + # These are mostly things like the user being logged out. + disable_bridge_alerts: false + # Should the bridge detect URLs in outgoing messages, ask the homeserver to generate a preview, + # and send it to WhatsApp? URL previews can always be sent using the `com.beeper.linkpreviews` + # key in the event content even if this is disabled. + url_previews: false # The prefix for commands. Only required in non-management rooms. command_prefix: "!wa" + + # 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 a WhatsApp 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. + # It is recommended to also set private_chat_portal_meta to true when using this. + default: true + # Options for automatic key sharing. + key_sharing: + # 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: true + # Require the requesting device to have a valid cross-signing signature? + # This doesn't require that the bridge has verified the device, only that the user has verified it. + # Not yet implemented. + require_cross_signing: false + # Require devices to be verified by the bridge? + # Verification by the bridge is not yet implemented. + require_verification: true + # Permissions for using the bridge. # Permitted values: # user - Access to use the bridge to chat with a WhatsApp account. @@ -64,21 +264,42 @@ bridge: # domain - All users on that homeserver # mxid - Specific user permissions: + "*": relay "{{ getenv "SERVER_DOMAIN" }}": user "{{ getenv "ADMIN_USER" }}": admin + # Settings for relay mode + relay: + # Whether relay mode should be allowed. If allowed, `!wa set-relay` can be used to turn any + # authenticated user into a relaybot for that chat. + enabled: false + # Should only admins be allowed to set themselves as relay users? + admin_only: true + # The formats to use when sending messages to WhatsApp via the relaybot. + message_formats: + m.text: "{{"{{"}} .Sender.Displayname {{"}}"}}: {{"{{"}} .Message {{"}}"}}" + m.notice: "{{"{{"}} .Sender.Displayname {{"}}"}}: {{"{{"}} .Message {{"}}"}}" + m.emote: "* {{"{{"}} .Sender.Displayname {{"}}"}} {{"{{"}} .Message {{"}}"}}" + m.file: "{{"{{"}} .Sender.Displayname {{"}}"}} sent a file" + m.image: "{{"{{"}} .Sender.Displayname {{"}}"}} sent an image" + m.audio: "{{"{{"}} .Sender.Displayname {{"}}"}} sent an audio file" + m.video: "{{"{{"}} .Sender.Displayname {{"}}"}} sent a video" + m.location: "{{"{{"}} .Sender.Displayname {{"}}"}} sent a location" + # Logging config. logging: - # The directory for log files. Will be created if not found. - directory: /var/lib/matrix-bridge/logs - # Available variables: .Date for the file date and .Index for different log files on the same day. - file_name_format: "{{"{{"}}.Date{{"}}"}}-{{"{{"}}.Index{{"}}"}}.log" - # Date format for file names in the Go time format: https://golang.org/pkg/time/#pkg-constants - file_date_format: 2006-01-02 - # Log file permissions. - file_mode: 0600 - # Timestamp format for log entries in the Go time format. - timestamp_format: Jan _2, 2006 15:04:05 - # Minimum severity for log messages. - # Options: debug, info, warn, error, fatal - print_level: debug + # The directory for log files. Will be created if not found. + directory: ./logs + # Available variables: .Date for the file date and .Index for different log files on the same day. + # Set this to null to disable logging to file. + file_name_format: "" + # Date format for file names in the Go time format: https://golang.org/pkg/time/#pkg-constants + file_date_format: "2006-01-02" + # Log file permissions. + file_mode: 0o600 + # Timestamp format for log entries in the Go time format. + timestamp_format: "Jan _2, 2006 15:04:05" + # Minimum severity for log messages printed to stdout/stderr. This doesn't affect the log file. + # Options: debug, info, warn, error, fatal + print_level: debug + diff --git a/rootfs/etc/fix-attrs.d/01-bridge b/rootfs/etc/fix-attrs.d/01-bridge deleted file mode 100644 index 39e31e9..0000000 --- a/rootfs/etc/fix-attrs.d/01-bridge +++ /dev/null @@ -1,2 +0,0 @@ -/var/lib/matrix-bridge/data/ true matrix-bridge 0640 0750 - diff --git a/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/whatsapp-bridge b/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/whatsapp-bridge new file mode 100644 index 0000000..e69de29 diff --git a/rootfs/etc/s6-overlay/s6-rc.d/whatsapp-bridge/dependencies b/rootfs/etc/s6-overlay/s6-rc.d/whatsapp-bridge/dependencies new file mode 100644 index 0000000..7ba7514 --- /dev/null +++ b/rootfs/etc/s6-overlay/s6-rc.d/whatsapp-bridge/dependencies @@ -0,0 +1 @@ +confd diff --git a/rootfs/etc/services.d/whatsapp-bridge/run b/rootfs/etc/s6-overlay/s6-rc.d/whatsapp-bridge/run similarity index 83% rename from rootfs/etc/services.d/whatsapp-bridge/run rename to rootfs/etc/s6-overlay/s6-rc.d/whatsapp-bridge/run index 429c926..439b8fd 100644 --- a/rootfs/etc/services.d/whatsapp-bridge/run +++ b/rootfs/etc/s6-overlay/s6-rc.d/whatsapp-bridge/run @@ -1,3 +1,3 @@ -#!/usr/bin/with-contenv sh +#!/bin/sh s6-setuidgid matrix-bridge mautrix-whatsapp -c /var/lib/matrix-bridge/config.yaml -r /var/lib/matrix-bridge/whatsapp-registration.yaml diff --git a/rootfs/etc/s6-overlay/s6-rc.d/whatsapp-bridge/type b/rootfs/etc/s6-overlay/s6-rc.d/whatsapp-bridge/type new file mode 100644 index 0000000..5883cff --- /dev/null +++ b/rootfs/etc/s6-overlay/s6-rc.d/whatsapp-bridge/type @@ -0,0 +1 @@ +longrun