Add support for WeChat bridging

This is based on the PR (https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/3241)
by Tobias Diez (https://github.com/tobiasdiez).

I've refactored some parts, made it more configurable, polished it up,
and it's integrated into the playbook now.

Both the WeChat bridge and WeChat agent appear to be working.
The WeChat bridge joins rooms and responds as expected.

That said, end-to-end testing (actually bridging to a WeChat account) has not been done yet.

Fixes https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/701

Fixes https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/3092

This is sponsored https://etke.cc/ work related to https://gitlab.com/etke.cc/ansible/-/issues/2

Squashed commit of the following:

commit fdd37f02472a0b83d61b4fac80650442f90e7629
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 21:05:53 2024 +0300

    Add documentation for WeChat bridge

commit 8426fc8b95bb160ea7f9659bd45bc59cf1326614
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 20:59:42 2024 +0300

    Rename directory for matrix_wechat_agent_container_src_files_path

commit da200df82bbc9153d307095dd90e4769c400ea1e
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 20:58:26 2024 +0300

    Make WeChat listen_secret configurable and auto-configured via matrix_homeserver_generic_secret_key

commit 4022cb1355828ac16af7d9228cb1066962bb35f5
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 20:54:56 2024 +0300

    Refactor install.yml for WeChat a bit (using blocks, etc.)

commit d07a39b4c4f6b93d04204e13e384086d5a242d52
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 20:52:35 2024 +0300

    Rename WeChat Agent configuration file

    This makes it more clear that it belongs to the agent.
    Otherwise, `config.yaml` and `configure.yaml` make you wonder.

commit ccca72f8d1e602f7c42f4bd552193afa153c9b9d
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 20:49:06 2024 +0300

    Move WeChat agent configuration to a template

commit a4047d94d8877b4095712dfc76ac3082a1edca28
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 20:47:17 2024 +0300

    Mount WeChat config as readonly and instruct bridge to not update it

commit bc0e89f345bf14bbdbfd574bb60d93918c2ac053
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 20:46:33 2024 +0300

    Sync WeChat config with upstream

    Brings up-to-date with:
    https://github.com/duo/matrix-wechat/commits/0.2.4/example-config.yaml

commit a46f5b9cbc8bf16042685a18c77d25a606bc8232
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 19:48:17 2024 +0300

    Rename some files

commit 3877679040cffc4ca6cccfa21a7335f8f796f06e
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 19:47:10 2024 +0300

    Update WeChat logging config

    This brings it up-to-date with what mautrix-go uses.
    Otherwise, on startup we see:

    > Migrating legacy log config

    .. and it gets migrated to what we've done here.

commit e3e95ab234651867c7a975a08455549b31db4172
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 19:43:37 2024 +0300

    Make sure matrix-wechat-agent runs as 1000:1000

    It needs to write stuff to `/home/user/.vnc`.

    `/home/user` is owned by `user:group` (`1000:1000`), so it cannot run
    any other way.

    Previously, if the `matrix` user was uid=1000 by chance, it would work,
    but that's pure luck.

commit 4d5748ae9b84c81d6b48b0a41b790339d9ac4724
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 18:57:09 2024 +0300

    Pin wechat and wechat-agent versions

commit 40d40009f19ebceed4126146cbb510a2c95af671
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 18:53:58 2024 +0300

    docker_image -> container_image for WeChat bridge

commit cc33aff592541913070d13288d17b04ed6243176
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 18:00:25 2024 +0300

    docker_src -> container_src in WeChat bridge

commit 42e6ae9a6483c8ca6d53b8052058d41d90d93797
Author: Slavi Pantaleev <slavi@devture.com>
Date:   Mon Jun 3 17:54:24 2024 +0300

    matrix_go_wechat_ -> matrix_wechat_

    The bridge is written in Go, but does not include Go anywhere in its
    name. As such, it's mostly useless to use `matrix_go_wechat` as the
    prefix.

commit d6662a69d1916d215d5184320c36d2ef73afd3e9
Author: Tobias Diez <code@tobiasdiez.de>
Date:   Mon Mar 25 10:55:16 2024 +0800

    Add wechat bridge
This commit is contained in:
Slavi Pantaleev 2024-06-03 21:06:19 +03:00
parent 16b4389c31
commit 70fd20cef5
13 changed files with 818 additions and 0 deletions

View File

@ -0,0 +1,17 @@
# Setting up the WeChat Bridge (optional)
The playbook can install and configure the [matrix-wechat](https://github.com/duo/matrix-wechat) bridge for you (for bridging to the [WeChat](https://www.wechat.com/) network).
See the project page to learn what it does and why it might be useful to you.
To enable the bridge, use the following playbook configuration and re-run the playbook's [installation](./installing.md) procedure:
```yaml
matrix_wechat_enabled: true
```
## Usage
Once the bridge is installed, start a chat with `@wechatbot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain).
Send `help` to the bot to see the available commands.

View File

@ -178,6 +178,8 @@ When you're done with all the configuration you'd like to do, continue with [Ins
- [Setting up Heisenbridge bouncer-style IRC bridging](configuring-playbook-bridge-heisenbridge.md) (optional)
- [Setting up WeChat bridging](configuring-playbook-bridge-wechat.md) (optional)
### Bots

View File

@ -93,6 +93,8 @@ matrix_homeserver_container_extra_arguments_auto: |
+
(['--mount type=bind,src=' + matrix_go_skype_bridge_config_path + '/registration.yaml,dst=/matrix-go-skype-bridge-registration.yaml,ro'] if matrix_go_skype_bridge_enabled else [])
+
(['--mount type=bind,src=' + matrix_wechat_config_path + '/registration.yaml,dst=/matrix-wechat-registration.yaml,ro'] if matrix_wechat_enabled else [])
+
(['--mount type=bind,src=' + matrix_heisenbridge_base_path + '/registration.yaml,dst=/heisenbridge-registration.yaml,ro'] if matrix_heisenbridge_enabled else [])
+
(['--mount type=bind,src=' + matrix_hookshot_base_path + '/registration.yml,dst=/hookshot-registration.yml,ro'] if matrix_hookshot_enabled else [])
@ -162,6 +164,8 @@ matrix_homeserver_app_service_config_files_auto: |
+
(['/matrix-go-skype-bridge-registration.yaml'] if matrix_go_skype_bridge_enabled else [])
+
(['/matrix-wechat-registration.yaml'] if matrix_wechat_enabled else [])
+
(['/heisenbridge-registration.yaml'] if matrix_heisenbridge_enabled else [])
+
(['/hookshot-registration.yml'] if matrix_hookshot_enabled else [])
@ -298,6 +302,10 @@ devture_systemd_service_manager_services_list_auto: |
+
([{'name': 'matrix-go-skype-bridge.service', 'priority': 2000, 'groups': ['matrix', 'bridges', 'go-skype']}] if matrix_go_skype_bridge_enabled else [])
+
([{'name': 'matrix-wechat.service', 'priority': 2000, 'groups': ['matrix', 'bridges', 'wechat']}] if matrix_wechat_enabled else [])
+
([{'name': 'matrix-wechat-agent.service', 'priority': 2000, 'groups': ['matrix', 'bridges', 'wechat']}] if matrix_wechat_enabled else [])
+
([{'name': 'matrix-heisenbridge.service', 'priority': 2000, 'groups': ['matrix', 'bridges', 'heisenbridge']}] if matrix_heisenbridge_enabled else [])
+
([{'name': 'matrix-hookshot.service', 'priority': 2000, 'groups': ['matrix', 'bridges', 'hookshot', 'bridge-hookshot']}] if matrix_hookshot_enabled else [])
@ -1713,6 +1721,57 @@ matrix_mautrix_wsproxy_syncproxy_database_password: "{{ '%s' | format(matrix_hom
#
######################################################################
######################################################################
#
# matrix-bridge-wechat
#
######################################################################
# We don't enable bridges by default.
matrix_wechat_enabled: false
matrix_wechat_systemd_required_services_list_auto: |
{{
matrix_addons_homeserver_systemd_services_list
+
([devture_postgres_identifier ~ '.service'] if (devture_postgres_enabled and matrix_wechat_database_hostname == devture_postgres_connection_hostname) else [])
}}
matrix_wechat_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm64'] }}"
matrix_wechat_agent_container_image_self_build: "{{ matrix_architecture not in ['amd64'] }}"
matrix_wechat_container_network: "{{ matrix_addons_container_network }}"
matrix_wechat_container_additional_networks_auto: |-
{{
(
([] if matrix_addons_homeserver_container_network == '' else [matrix_addons_homeserver_container_network])
+
([devture_postgres_container_network] if (devture_postgres_enabled and matrix_wechat_database_hostname == devture_postgres_connection_hostname and matrix_wechat_container_network != devture_postgres_container_network) else [])
) | unique
}}
matrix_wechat_appservice_token: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'wechat.as.token', rounds=655555) | to_uuid }}"
matrix_wechat_homeserver_address: "{{ matrix_addons_homeserver_client_api_url }}"
matrix_wechat_homeserver_token: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'wechat.hs.token', rounds=655555) | to_uuid }}"
matrix_wechat_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}"
matrix_wechat_bridge_listen_secret: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'wechat.lstn', rounds=655555) | to_uuid }}"
# Postgres is the default, except if not using internal Postgres server
matrix_wechat_database_engine: "{{ 'postgres' if devture_postgres_enabled else 'sqlite' }}"
matrix_wechat_database_hostname: "{{ devture_postgres_connection_hostname if devture_postgres_enabled else '' }}"
matrix_wechat_database_password: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'gowechat.db', rounds=655555) | to_uuid }}"
######################################################################
#
# /matrix-bridge-wechat
#
######################################################################
######################################################################
#
# matrix-bridge-mautrix-whatsapp
@ -3694,6 +3753,12 @@ devture_postgres_managed_databases_auto: |
'password': matrix_go_skype_bridge_database_password,
}] if (matrix_go_skype_bridge_enabled and matrix_go_skype_bridge_database_engine == 'postgres' and matrix_go_skype_bridge_database_hostname == devture_postgres_connection_hostname) else [])
+
([{
'name': matrix_wechat_database_name,
'username': matrix_wechat_database_username,
'password': matrix_wechat_database_password,
}] if (matrix_wechat_enabled and matrix_wechat_database_engine == 'postgres' and matrix_wechat_database_hostname == devture_postgres_connection_hostname) else [])
+
([{
'name': matrix_mautrix_facebook_database_name,
'username': matrix_mautrix_facebook_database_username,

View File

@ -0,0 +1,152 @@
---
# WeChat Bridge is a Matrix <-> WeChat bridge
# Project source code URL: https://github.com/duo/matrix-wechat
matrix_wechat_enabled: true
# renovate: datasource=docker depName=lxduo/matrix-wechat
matrix_wechat_version: 0.2.4
matrix_wechat_container_image: "{{ matrix_wechat_container_image_name_prefix }}lxduo/matrix-wechat:{{ matrix_wechat_version }}"
matrix_wechat_container_image_name_prefix: "{{ 'localhost/' if matrix_wechat_container_image_self_build else matrix_container_global_registry_prefix }}"
matrix_wechat_container_image_force_pull: "{{ matrix_wechat_container_image.endswith(':latest') }}"
matrix_wechat_container_image_self_build: false
matrix_wechat_container_image_self_build_repo: "https://github.com/duo/matrix-wechat.git"
matrix_wechat_container_image_self_build_branch: "{{ 'master' if matrix_wechat_version == 'latest' else matrix_wechat_version }}"
# renovate: datasource=docker depName=lxduo/matrix-wechat-agent
matrix_wechat_agent_version: 0.0.1
matrix_wechat_agent_container_image: "{{ matrix_wechat_agent_container_image_name_prefix }}lxduo/matrix-wechat-agent:{{ matrix_wechat_agent_version }}"
matrix_wechat_agent_container_image_name_prefix: "{{ 'localhost/' if matrix_wechat_agent_container_image_self_build else matrix_container_global_registry_prefix }}"
matrix_wechat_agent_container_image_force_pull: "{{ matrix_wechat_agent_container_image.endswith(':latest') }}"
# The agent needs to write to /home/user/.vnc.
# `/home/user` is owned by `user:group` (`1000:1000`), so it needs to run with that user/group.
matrix_wechat_agent_container_user_uid: 1000
matrix_wechat_agent_container_user_gid: 1000
matrix_wechat_agent_container_image_self_build: false
matrix_wechat_agent_container_image_self_build_repo: "https://github.com/duo/matrix-wechat-agent.git"
matrix_wechat_agent_container_image_self_build_branch: "{{ 'master' if matrix_wechat_agent_version == 'latest' else matrix_wechat_agent_version }}"
matrix_wechat_base_path: "{{ matrix_base_data_path }}/wechat"
matrix_wechat_config_path: "{{ matrix_wechat_base_path }}/config"
matrix_wechat_data_path: "{{ matrix_wechat_base_path }}/data"
matrix_wechat_container_src_files_path: "{{ matrix_wechat_base_path }}/container-src"
matrix_wechat_agent_container_src_files_path: "{{ matrix_wechat_base_path }}/agent-container-src"
matrix_wechat_homeserver_address: ""
matrix_wechat_homeserver_domain: "{{ matrix_domain }}"
matrix_wechat_appservice_address: 'http://matrix-wechat:8080'
matrix_wechat_container_network: ""
matrix_wechat_container_additional_networks: "{{ matrix_wechat_container_additional_networks_auto + matrix_wechat_container_additional_networks_custom }}"
matrix_wechat_container_additional_networks_auto: []
matrix_wechat_container_additional_networks_custom: []
# A list of extra arguments to pass to the container
matrix_wechat_container_extra_arguments: []
# List of systemd services that matrix-wechat.service depends on.
matrix_wechat_systemd_required_services_list: "{{ matrix_wechat_systemd_required_services_list_default + matrix_wechat_systemd_required_services_list_auto + matrix_wechat_systemd_required_services_list_custom }}"
matrix_wechat_systemd_required_services_list_default: ['docker.service']
matrix_wechat_systemd_required_services_list_auto: []
matrix_wechat_systemd_required_services_list_custom: []
# List of systemd services that matrix-wechat.service wants
matrix_wechat_systemd_wanted_services_list: []
matrix_wechat_appservice_token: ''
matrix_wechat_homeserver_token: ''
matrix_wechat_appservice_bot_username: wechatbot
matrix_wechat_command_prefix: "!wechat"
# Whether or not created rooms should have federation enabled.
# If false, created portal rooms will never be federated.
matrix_wechat_federate_rooms: true
# Database-related configuration fields.
matrix_wechat_database_engine: 'postgres'
matrix_wechat_database_username: 'matrix_wechat'
matrix_wechat_database_password: 'some-password'
matrix_wechat_database_hostname: ''
matrix_wechat_database_port: 5432
matrix_wechat_database_name: 'matrix_wechat'
matrix_wechat_database_sslmode: disable
matrix_wechat_database_connection_string: 'postgresql://{{ matrix_wechat_database_username }}:{{ matrix_wechat_database_password }}@{{ matrix_wechat_database_hostname }}:{{ matrix_wechat_database_port }}/{{ matrix_wechat_database_name }}?sslmode={{ matrix_wechat_database_sslmode }}'
matrix_wechat_bridge_listen_secret: ''
# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth).
matrix_wechat_login_shared_secret: ''
matrix_wechat_login_shared_secret_map:
"{{ {matrix_wechat_homeserver_domain: matrix_wechat_login_shared_secret} if matrix_wechat_login_shared_secret else {} }}"
# Servers to always allow double puppeting from
matrix_wechat_double_puppet_server_map:
"{{ matrix_wechat_homeserver_domain: matrix_wechat_homeserver_address }}"
# Enable End-to-bridge encryption
matrix_wechat_encryption_allow: "{{ matrix_bridges_encryption_enabled }}"
matrix_wechat_encryption_default: "{{ matrix_wechat_encryption_allow }}"
# Minimum severity of journal log messages.
# Options: debug, info, warn, error, fatal
matrix_wechat_log_level: 'warn'
matrix_wechat_permissions: |
{{
{matrix_wechat_homeserver_domain: 'user'}
| combine({matrix_admin: 'admin'} if matrix_admin else {})
}}
# Default Wechat configuration template which covers the generic use case.
# You can customize it by controlling the various variables inside it.
#
# For a more advanced customization, you can extend the default (see `matrix_wechat_configuration_extension_yaml`)
# or completely replace this variable with your own template.
matrix_wechat_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}"
matrix_wechat_configuration_extension_yaml: |
# Your custom YAML configuration goes here.
# This configuration extends the default starting configuration (`matrix_wechat_configuration_yaml`).
#
# You can override individual variables from the default configuration, or introduce new ones.
#
# If you need something more special, you can take full control by
# completely redefining `matrix_wechat_configuration_yaml`.
matrix_wechat_configuration_extension: "{{ matrix_wechat_configuration_extension_yaml | from_yaml if matrix_wechat_configuration_extension_yaml | from_yaml is mapping else {} }}"
# Holds the final configuration (a combination of the default and its extension).
# You most likely don't need to touch this variable. Instead, see `matrix_wechat_configuration_yaml`.
matrix_wechat_configuration: "{{ matrix_wechat_configuration_yaml | from_yaml | combine(matrix_wechat_configuration_extension, recursive=True) }}"
matrix_wechat_registration_yaml: |
id: wechat
url: {{ matrix_wechat_appservice_address }}
as_token: "{{ matrix_wechat_appservice_token }}"
hs_token: "{{ matrix_wechat_homeserver_token }}"
# See https://github.com/mautrix/signal/issues/43
sender_localpart: _bot_{{ matrix_wechat_appservice_bot_username }}
rate_limited: false
namespaces:
users:
- regex: '^@_wechat_(.*):{{ matrix_wechat_homeserver_domain | regex_escape }}$'
exclusive: true
- exclusive: true
regex: '^@{{ matrix_wechat_appservice_bot_username | regex_escape }}:{{ matrix_wechat_homeserver_domain | regex_escape }}$'
de.sorunome.msc2409.push_ephemeral: true
matrix_wechat_registration: "{{ matrix_wechat_registration_yaml | from_yaml }}"
matrix_wechat_agent_service_secret: "{{ matrix_wechat_bridge_listen_secret }}"
matrix_wechat_agent_configuration_yaml: "{{ lookup('template', 'templates/agent-config.yaml.j2') }}"
matrix_wechat_agent_configuration: "{{ matrix_wechat_agent_configuration_yaml | from_yaml }}"

View File

@ -0,0 +1,129 @@
---
- name: Ensure WeChat Bridge paths exists
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
mode: 0750
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"
with_items:
- {path: "{{ matrix_wechat_base_path }}", when: true}
- {path: "{{ matrix_wechat_config_path }}", when: true}
- {path: "{{ matrix_wechat_data_path }}", when: true}
- {path: "{{ matrix_wechat_container_src_files_path }}", when: "{{ matrix_wechat_container_image_self_build }}"}
- {path: "{{ matrix_wechat_agent_container_src_files_path }}", when: "{{ matrix_wechat_agent_container_image_self_build }}"}
when: item.when | bool
- name: Ensure WeChat Bridge image is pulled
community.docker.docker_image:
name: "{{ matrix_wechat_container_image }}"
source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}"
force_source: "{{ matrix_wechat_container_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}"
force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_wechat_container_image_force_pull }}"
when: not matrix_wechat_container_image_self_build
register: result
retries: "{{ devture_playbook_help_container_retries_count }}"
delay: "{{ devture_playbook_help_container_retries_delay }}"
until: result is not failed
- when: matrix_wechat_container_image_self_build | bool
block:
- name: Ensure WeChat Bridge repository is present on self-build
ansible.builtin.git:
repo: "{{ matrix_wechat_container_image_self_build_repo }}"
dest: "{{ matrix_wechat_container_src_files_path }}"
version: "{{ matrix_wechat_container_image_self_build_branch }}"
force: "yes"
become: true
become_user: "{{ matrix_user_username }}"
register: matrix_wechat_git_pull_results
- name: Ensure WeChat Bridge container image is built
community.docker.docker_image:
name: "{{ matrix_wechat_container_image }}"
source: build
force_source: "{{ matrix_wechat_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}"
force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_wechat_git_pull_results.changed }}"
build:
dockerfile: Dockerfile
path: "{{ matrix_wechat_container_src_files_path }}"
pull: true
- name: Ensure WeChat Agent image is pulled
community.docker.docker_image:
name: "{{ matrix_wechat_agent_container_image }}"
source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}"
force_source: "{{ matrix_wechat_agent_container_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}"
force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_wechat_agent_container_image_force_pull }}"
when: not matrix_wechat_agent_container_image_self_build
register: result
retries: "{{ devture_playbook_help_container_retries_count }}"
delay: "{{ devture_playbook_help_container_retries_delay }}"
until: result is not failed
- when: matrix_wechat_agent_container_image_self_build | bool
block:
- name: Ensure WeChat Agent repository is present on self-build
ansible.builtin.git:
repo: "{{ matrix_wechat_agent_container_image_self_build_repo }}"
dest: "{{ matrix_wechat_agent_container_src_files_path }}"
version: "{{ matrix_wechat_agent_container_image_self_build_branch }}"
force: "yes"
become: true
become_user: "{{ matrix_user_username }}"
register: matrix_wechat_agent_git_pull_results
- name: Ensure WeChat Agent container image is built
community.docker.docker_image:
name: "{{ matrix_wechat_agent_container_image }}"
source: build
force_source: "{{ matrix_wechat_agent_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}"
force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_wechat_agent_git_pull_results.changed }}"
build:
dockerfile: Dockerfile
path: "{{ matrix_wechat_agent_container_src_files_path }}"
pull: true
- name: Ensure WeChat configuration installed
ansible.builtin.copy:
content: "{{ matrix_wechat_configuration | to_nice_yaml(indent=2, width=999999) }}"
dest: "{{ matrix_wechat_config_path }}/config.yaml"
mode: 0644
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"
- name: Ensure WeChat registration.yaml installed
ansible.builtin.copy:
content: "{{ matrix_wechat_registration | to_nice_yaml(indent=2, width=999999) }}"
dest: "{{ matrix_wechat_config_path }}/registration.yaml"
mode: 0644
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"
- name: Ensure Wechat Agent configuration installed
ansible.builtin.copy:
content: "{{ matrix_wechat_agent_configuration | to_nice_yaml(indent=2, width=999999) }}"
dest: "{{ matrix_wechat_config_path }}/agent-config.yaml"
mode: 0644
owner: "{{ matrix_user_username }}"
group: "{{ matrix_user_groupname }}"
- name: Ensure matrix-wechat container network is created
community.general.docker_network:
name: "{{ matrix_wechat_container_network }}"
driver: bridge
- name: Ensure matrix-wechat.service installed
ansible.builtin.template:
src: "{{ role_path }}/templates/systemd/matrix-wechat.service.j2"
dest: "{{ devture_systemd_docker_base_systemd_path }}/matrix-wechat.service"
mode: 0644
register: matrix_wechat_systemd_service_result
- name: Ensure matrix-wechat-agent.service installed
ansible.builtin.template:
src: "{{ role_path }}/templates/systemd/matrix-wechat-agent.service.j2"
dest: "{{ devture_systemd_docker_base_systemd_path }}/matrix-wechat-agent.service"
mode: 0644
register: matrix_wechat_agent_systemd_service_result

View File

@ -0,0 +1,20 @@
---
- tags:
- setup-all
- setup-wechat
- install-all
- install-wechat
block:
- when: matrix_wechat_enabled | bool
ansible.builtin.include_tasks: "{{ role_path }}/tasks/validate_config.yml"
- when: matrix_wechat_enabled | bool
ansible.builtin.include_tasks: "{{ role_path }}/tasks/install.yml"
- tags:
- setup-all
- setup-wechat
block:
- when: not matrix_wechat_enabled | bool
ansible.builtin.include_tasks: "{{ role_path }}/tasks/uninstall.yml"

View File

@ -0,0 +1,39 @@
---
- name: Check existence of matrix-wechat service
ansible.builtin.stat:
path: "/etc/systemd/system/matrix-wechat.service"
register: matrix_wechat_service_stat
- when: matrix_wechat_service_stat.stat.exists | bool
block:
- name: Ensure matrix-wechat is stopped
ansible.builtin.service:
name: matrix-wechat
state: stopped
enabled: false
daemon_reload: true
- name: Ensure matrix-wechat.service doesn't exist
ansible.builtin.file:
path: "/etc/systemd/system/matrix-wechat.service"
state: absent
- name: Check existence of matrix-wechat-agent service
ansible.builtin.stat:
path: "/etc/systemd/system/matrix-wechat-agent.service"
register: matrix_wechat_agent_service_stat
- when: matrix_wechat_agent_service_stat.stat.exists | bool
block:
- name: Ensure matrix-wechat-agent is stopped
ansible.builtin.service:
name: matrix-wechat-agent
state: stopped
enabled: false
daemon_reload: true
- name: Ensure matrix-wechat-agent.service doesn't exist
ansible.builtin.file:
path: "/etc/systemd/system/matrix-wechat-agent.service"
state: absent

View File

@ -0,0 +1,19 @@
---
- name: Fail if required WeChat settings not defined
ansible.builtin.fail:
msg: >-
You need to define a required configuration setting (`{{ item.name }}`).
when: "item.when | bool and vars[item.name] == ''"
with_items:
- {'name': 'matrix_wechat_appservice_token', when: true}
- {'name': 'matrix_wechat_homeserver_address', when: true}
- {'name': 'matrix_wechat_homeserver_token', when: true}
- {'name': 'matrix_wechat_database_hostname', when: "{{ matrix_wechat_database_engine == 'postgres' }}"}
- {'name': 'matrix_wechat_container_network', when: true}
- {'name': 'matrix_wechat_bridge_listen_secret', when: true}
- name: Fail if WeChat enabled on ARM64 (not supported by the wechat agent, even with self-building)
when: matrix_architecture not in ['amd64']
ansible.builtin.fail:
msg: "The WeChat Agent does not support the '{{ matrix_architecture }}' architecture yet. Its Dockerfile downloads amd64 binaries and does not work on arm64."

View File

@ -0,0 +1,13 @@
wechat:
version: 3.8.1.26
listen_port: 22222
init_timeout: 10s
request_timeout: 30s
service:
addr: ws://matrix-wechat:20002
secret: {{ matrix_wechat_bridge_listen_secret | to_json }}
ping_interval: 30s
log:
level: info

View File

@ -0,0 +1,265 @@
#jinja2: lstrip_blocks: "True"
# Homeserver details.
homeserver:
# The address that this appservice can use to connect to the homeserver.
address: {{ matrix_wechat_homeserver_address | to_json }}
# The domain of the homeserver (for MXIDs, etc).
domain: {{ matrix_wechat_homeserver_domain | to_json }}
# What software is the homeserver running?
# Standard Matrix homeservers like Synapse, Dendrite and Conduit should just use "standard" here.
software: standard
# The URL to push real-time bridge status to.
# If set, the bridge will make POST requests to this URL whenever a user's 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
# 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: false
# How often should the websocket be pinged? Pinging will be disabled if this is zero.
ping_interval_seconds: 0
# 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: {{ matrix_wechat_appservice_address | to_json }}
# The hostname and port where this appservice should listen.
hostname: 0.0.0.0
port: 8080
# Database config.
database:
# The database type. "sqlite3" and "postgres" are supported.
type: postgres
# The database URI.
# SQLite: File name is enough. 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: {{ matrix_wechat_database_connection_string | to_json }}
# Maximum number of connections. Mostly relevant for Postgres.
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
# The unique ID of this appservice.
id: wechat
# Appservice bot details.
bot:
# Username of the appservice bot.
username: {{ matrix_wechat_appservice_bot_username | to_json }}
# Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty
# to leave display name/avatar as-is.
displayname: WeChat bridge bot
avatar: mxc://matrix.org/rddVQBTjOOmNkNLXWfYJNfPW
# 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: {{ matrix_wechat_appservice_token | to_json }}
hs_token: {{ matrix_wechat_homeserver_token | to_json }}
# Bridge config
bridge:
# Localpart template of MXIDs for WeChat users.
# {% raw %}{{.}}{% endraw %} is replaced with the uin of the WeChat user.
username_template: {% raw %}_wechat_{{.}}{% endraw %}
# Displayname template for WeChat users.
displayname_template: "{% raw %}{{if .Name}}{{.Name}}{{else}}{{.Uin}}{{end}} (WeChat){% endraw %}"
# WeChat listen address (for agent connection)
listen_address: "0.0.0.0:20002"
listen_secret: {{ matrix_wechat_bridge_listen_secret | to_json }}
# 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
# Whether the bridge should send the message status as a custom com.beeper.message_send_status event.
message_status_events: false
# Whether the bridge should send error notices via m.notice events when a message fails to bridge.
message_error_notices: true
portal_message_buffer: 128
# Enable redaction
allow_redaction: false
# Should puppet avatars be fetched from the server even if an avatar is already set?
user_avatar_sync: 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 is bridged. These settings set the default values.
# Existing users won't be affected when these are changed.
default_bridge_presence: false
# Send the presence as "available" to WeChat 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 WeChat user on the other side is typing during a conversation.
send_presence_on_typing: false
# Servers to always allow double puppeting from
double_puppet_server_map:
"{{ matrix_wechat_homeserver_domain }}": {{ matrix_wechat_homeserver_address }}
# 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: {{ matrix_wechat_login_shared_secret_map | to_json }}
# 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: default
# Should group members be synced in parallel? This makes member sync faster
parallel_member_sync: 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
# When using double puppeting, should muted chats be muted in Matrix?
mute_bridging: false
# Allow invite permission for user. User can invite any bots to room with WeChat
# 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
# 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
# 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: {{ matrix_wechat_command_prefix | to_json }}
# 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 WeChat 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: {{ matrix_wechat_encryption_allow | to_json }}
# 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: {{ matrix_wechat_encryption_default | to_json }}
# Whether to use MSC2409/MSC3202 instead of /sync long polling for receiving encryption-related data.
appservice: false
# Require encryption, drop any unencrypted messages.
require: false
# 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: false
# Should users mentions be in the event wire content to enable the server to send push notifications?
plaintext_mentions: false
# 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: false
# Don't store outbound sessions in the inbound table.
dont_store_outbound: false
# Ratchet megolm sessions forward after decrypting messages.
ratchet_on_decrypt: false
# Delete fully used keys (index >= max_messages) after decrypting messages.
delete_fully_used_on_decrypt: false
# Delete previous megolm sessions from same device when receiving a new one.
delete_prev_on_new_session: false
# Delete megolm sessions received from a device when the device is deleted.
delete_on_device_delete: false
# Periodically delete megolm sessions when 2x max_age has passed since receiving the session.
periodically_delete_expired: false
# 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 WeChat to Matrix.
receive: unverified
# Minimum level that the bridge should accept for incoming Matrix messages.
send: unverified
# 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: false
# The maximum number of milliseconds a session should be used
# before changing it. The Matrix spec recommends 604800000 (a week)
# as the default.
milliseconds: 604800000
# 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: 100
# 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: false
# Permissions for using the bridge.
# Permitted values:
# user - Access to use the bridge to chat with a WeChat account.
# admin - User level and some additional administration tools
# Permitted keys:
# * - All Matrix users
# domain - All users on that homeserver
# mxid - Specific user
permissions: {{ matrix_wechat_permissions | to_json }}
# Logging config. See https://github.com/tulir/zeroconfig for details.
logging:
min_level: {{ matrix_wechat_log_level }}
writers:
- type: stdout
format: pretty-colored

View File

@ -0,0 +1,49 @@
#jinja2: lstrip_blocks: "True"
[Unit]
Description=Matrix WeChat Agent
{% for service in matrix_wechat_systemd_required_services_list %}
Requires={{ service }}
After={{ service }}
{% endfor %}
{% for service in matrix_wechat_systemd_wanted_services_list %}
Wants={{ service }}
{% endfor %}
DefaultDependencies=no
[Service]
Type=simple
Environment="HOME={{ devture_systemd_docker_base_systemd_unit_home_path }}"
ExecStartPre=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} stop --time={{ devture_systemd_docker_base_container_stop_grace_time_seconds }} matrix-wechat-agent 2>/dev/null || true'
ExecStartPre=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm matrix-wechat-agent 2>/dev/null || true'
{#
The agent needs to write to /home/user/.vnc.
`/home/user` is owned by `user:group` (`1000:1000`), so it needs to run with that user/group.
#}
ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} create \
--rm \
--name=matrix-wechat-agent \
--log-driver=none \
--user={{ matrix_wechat_agent_container_user_uid }}:{{ matrix_wechat_agent_container_user_gid }} \
--cap-drop=ALL \
--network={{ matrix_wechat_container_network }} \
--mount type=bind,src={{ matrix_wechat_config_path }}/agent-config.yaml,dst=/home/user/matrix-wechat-agent/configure.yaml,ro \
{% for arg in matrix_wechat_container_extra_arguments %}
{{ arg }} \
{% endfor %}
{{ matrix_wechat_agent_container_image }}
{% for network in matrix_wechat_container_additional_networks %}
ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} network connect {{ network }} matrix-wechat-agent
{% endfor %}
ExecStart={{ devture_systemd_docker_base_host_command_docker }} start --attach matrix-wechat-agent
ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} stop --time={{ devture_systemd_docker_base_container_stop_grace_time_seconds }} matrix-wechat-agent 2>/dev/null || true'
ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm matrix-wechat-agent 2>/dev/null || true'
Restart=always
RestartSec=30
SyslogIdentifier=matrix-wechat-agent
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,47 @@
#jinja2: lstrip_blocks: "True"
[Unit]
Description=Matrix WeChat Bridge
{% for service in matrix_wechat_systemd_required_services_list %}
Requires={{ service }}
After={{ service }}
{% endfor %}
{% for service in matrix_wechat_systemd_wanted_services_list %}
Wants={{ service }}
{% endfor %}
DefaultDependencies=no
[Service]
Type=simple
Environment="HOME={{ devture_systemd_docker_base_systemd_unit_home_path }}"
ExecStartPre=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} stop --time={{ devture_systemd_docker_base_container_stop_grace_time_seconds }} matrix-wechat 2>/dev/null || true'
ExecStartPre=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm matrix-wechat 2>/dev/null || true'
ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} create \
--rm \
--name=matrix-wechat \
--log-driver=none \
--user={{ matrix_user_uid }}:{{ matrix_user_gid }} \
--cap-drop=ALL \
--network={{ matrix_wechat_container_network }} \
--mount type=bind,src={{ matrix_wechat_config_path }},dst=/config,ro \
--mount type=bind,src={{ matrix_wechat_data_path }},dst=/data \
--workdir=/data \
{% for arg in matrix_wechat_container_extra_arguments %}
{{ arg }} \
{% endfor %}
{{ matrix_wechat_container_image }} \
/usr/bin/matrix-wechat -c /config/config.yaml -r /config/registration.yaml --no-update
{% for network in matrix_wechat_container_additional_networks %}
ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} network connect {{ network }} matrix-wechat
{% endfor %}
ExecStart={{ devture_systemd_docker_base_host_command_docker }} start --attach matrix-wechat
ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} stop --time={{ devture_systemd_docker_base_container_stop_grace_time_seconds }} matrix-wechat 2>/dev/null || true'
ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm matrix-wechat 2>/dev/null || true'
Restart=always
RestartSec=30
SyslogIdentifier=matrix-wechat
[Install]
WantedBy=multi-user.target

View File

@ -58,6 +58,7 @@
- custom/matrix-bridge-appservice-kakaotalk
- custom/matrix-bridge-beeper-linkedin
- custom/matrix-bridge-go-skype-bridge
- custom/matrix-bridge-wechat
- custom/matrix-bridge-mautrix-facebook
- custom/matrix-bridge-mautrix-twitter
- custom/matrix-bridge-mautrix-hangouts