From 4e861c2161d192c3964ede870a2d7cca71457d5b Mon Sep 17 00:00:00 2001 From: Mistyttm Date: Thu, 11 Dec 2025 15:09:24 +1000 Subject: [PATCH] nixos/mautrix-discord: refactor After fixing the registration bug I determined that the module was way too verbose and did not need to provide all of the options provided in the application. I've stopped explicitly declaring all options that aren't needed to make a basic version of the module function. The assertions have also been updated to mirror this fact and will alert a user when the specific required options are unset. A new documnetation file was also added as this module, like other mautrix modules, is quite complex and may need more explaining. --- nixos/doc/manual/redirects.json | 36 ++ .../services/matrix/mautrix-discord.md | 156 +++++++ .../services/matrix/mautrix-discord.nix | 389 ++++-------------- nixos/tests/matrix/mautrix-discord.nix | 67 ++- 4 files changed, 335 insertions(+), 313 deletions(-) create mode 100644 nixos/modules/services/matrix/mautrix-discord.md diff --git a/nixos/doc/manual/redirects.json b/nixos/doc/manual/redirects.json index 5f9265ab667d..264b17c856e8 100644 --- a/nixos/doc/manual/redirects.json +++ b/nixos/doc/manual/redirects.json @@ -65,6 +65,42 @@ "module-services-keycloak-unix-socket": [ "index.html#module-services-keycloak-unix-socket" ], + "module-services-mautrix-discord": [ + "index.html#module-services-mautrix-discord" + ], + "module-services-mautrix-discord-advanced": [ + "index.html#module-services-mautrix-discord-advanced" + ], + "module-services-mautrix-discord-authentication": [ + "index.html#module-services-mautrix-discord-authentication" + ], + "module-services-mautrix-discord-backfill": [ + "index.html#module-services-mautrix-discord-backfill" + ], + "module-services-mautrix-discord-basic-example": [ + "index.html#module-services-mautrix-discord-basic-example" + ], + "module-services-mautrix-discord-basic-usage": [ + "index.html#module-services-mautrix-discord-basic-usage" + ], + "module-services-mautrix-discord-double-puppet": [ + "index.html#module-services-mautrix-discord-double-puppet" + ], + "module-services-mautrix-discord-encryption": [ + "index.html#module-services-mautrix-discord-encryption" + ], + "module-services-mautrix-discord-server-defaults": [ + "index.html#module-services-mautrix-discord-server-defaults" + ], + "module-services-mautrix-discord-setup": [ + "index.html#module-services-mautrix-discord-setup" + ], + "module-services-mautrix-discord-synapse": [ + "index.html#module-services-mautrix-discord-synapse" + ], + "module-services-mautrix-discord-troubleshooting": [ + "index.html#module-services-mautrix-discord-troubleshooting" + ], "module-services-tandoor-recipes-migrating-media-option-move": [ "index.html#module-services-tandoor-recipes-migrating-media-option-move", "index.html#module-services-tandoor-recipes-migrating-media-option-1" diff --git a/nixos/modules/services/matrix/mautrix-discord.md b/nixos/modules/services/matrix/mautrix-discord.md new file mode 100644 index 000000000000..ef356f600da7 --- /dev/null +++ b/nixos/modules/services/matrix/mautrix-discord.md @@ -0,0 +1,156 @@ +# Mautrix-Discord {#module-services-mautrix-discord} + +*Source:* {file}`modules/services/matrix/mautrix-discord` + +*Upstream documentation:* + +[Mautrix-Discord](https://github.com/mautrix/discord) is a Matrix-Discord bridge. + +## Basic Usage {#module-services-mautrix-discord-basic-usage} + +The common setup is to enable the bridge, point it at your homeserver, and set the permissions you want to allow: + +1. Set `services.mautrix-discord.enable` to `true`. +2. Set `services.mautrix-discord.settings.homeserver.address` and `services.mautrix-discord.settings.homeserver.domain`. +3. Override `services.mautrix-discord.settings.bridge.permissions` if the default relay permissions do not fit your deployment. + +The module provides sensible defaults for the appservice listener, registration tokens, and relay permissions. + +### Basic Example {#module-services-mautrix-discord-basic-example} + +```nix +{ + services.mautrix-discord = { + enable = true; + registerToSynapse = true; + settings = { + homeserver = { + address = "http://localhost:8008"; + domain = "example.com"; + }; + bridge.permissions = { + "example.com" = "user"; + "@admin:example.com" = "admin"; + }; + }; + }; +} +``` + +### Server Defaults {#module-services-mautrix-discord-server-defaults} + +By default, the bridge listens on `http://localhost:29334` and generates its appservice tokens automatically. + +## Authentication {#module-services-mautrix-discord-authentication} + +If you want to store the bridge database outside the default SQLite file, set `settings.appservice.database` to use PostgreSQL instead of SQLite: + +```nix +{ + services.mautrix-discord.settings.appservice.database = { + type = "postgres"; + uri = "postgresql:///mautrix-discord?host=/run/postgresql"; + }; +} +``` + +If the connection needs a password, combine it with `services.mautrix-discord.environmentFile`: + +```nix +{ + services.mautrix-discord = { + environmentFile = "/run/secrets/mautrix-discord-env"; + settings.appservice.database.uri = "postgresql://mautrix:$DB_PASSWORD@localhost/mautrix-discord"; + }; +} +``` + +Use `services.mautrix-discord.environmentFile` for any secret you do not want in the Nix store. +This includes database passwords, shared secrets, and similar values. + +Example: + +```nix +{ + services.mautrix-discord = { + environmentFile = "/run/secrets/mautrix-discord-env"; + settings.bridge.login_shared_secret_map = { + "example.com" = "$SHARED_SECRET"; + }; + }; +} +``` + +## Bridge Setup {#module-services-mautrix-discord-setup} + +After the service starts, open a chat with `@discordbot:example.com`, send `login`, and follow the instructions to link your Discord account. + +For more detail, see the [Mautrix-Discord documentation](https://docs.mau.fi/bridges/go/discord/index.html). + +## Advanced Configuration {#module-services-mautrix-discord-advanced} + +The upstream default configuration is available at [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml). To print the generated default configuration from the package, run: + +```bash +nix-shell -p mautrix-discord --run "mautrix-discord -e" +``` + +### Encryption {#module-services-mautrix-discord-encryption} + +```nix +{ + services.mautrix-discord.settings.bridge.encryption = { + allow = true; + default = true; + require = false; + }; +} +``` + +Encryption needs additional bridge-side setup. See the [bridge encryption documentation](https://docs.mau.fi/bridges/general/end-to-bridge-encryption.html) for details. + +### Backfill {#module-services-mautrix-discord-backfill} + +```nix +{ + services.mautrix-discord.settings.bridge.backfill.forward_limits.initial = { + dm = 50; + channel = 50; + thread = 50; + }; +} +``` + +### Double Puppeting {#module-services-mautrix-discord-double-puppet} + +```nix +{ + services.mautrix-discord = { + environmentFile = "/run/secrets/mautrix-discord-env"; + settings.bridge.login_shared_secret_map = { + "example.com" = "$SHARED_SECRET"; + }; + }; +} +``` +where `/run/secrets/mautrix-discord-env` contains +``` +SHARED_SECRET=aProtectedSecret +``` + +## Synapse Integration {#module-services-mautrix-discord-synapse} + +When `services.mautrix-discord.registerToSynapse` is `true`, the bridge writes its registration file automatically and Synapse picks it up. + +If Synapse is enabled, this option defaults to `true`. + +## Troubleshooting {#module-services-mautrix-discord-troubleshooting} + +- View logs with `journalctl -u mautrix-discord.service -f`. +- Check `systemctl status mautrix-discord` if the bridge does not start. +- Verify the homeserver can reach the configured appservice address. +- Ensure the registration file exists and Synapse can read it. + +For more help, see the +[Mautrix-Discord documentation](https://docs.mau.fi/bridges/go/discord/index.html) +or the support room at [#discord:maunium.net](https://matrix.to/#/#discord:maunium.net). diff --git a/nixos/modules/services/matrix/mautrix-discord.nix b/nixos/modules/services/matrix/mautrix-discord.nix index f0796ca1df84..5ed5e4ae54ce 100644 --- a/nixos/modules/services/matrix/mautrix-discord.nix +++ b/nixos/modules/services/matrix/mautrix-discord.nix @@ -5,15 +5,59 @@ ... }: let + defaultDataDir = "/var/lib/mautrix-discord"; cfg = config.services.mautrix-discord; dataDir = cfg.dataDir; format = pkgs.formats.yaml { }; + serviceDependencies = [ + "mautrix-discord-registration.service" + ] + ++ (lib.lists.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit) + ++ (lib.lists.optional config.services.matrix-conduit.enable "matrix-conduit.service") + ++ (lib.lists.optional config.services.dendrite.enable "dendrite.service"); registrationFile = "${dataDir}/discord-registration.yaml"; settingsFile = "${dataDir}/config.yaml"; settingsFileUnformatted = format.generate "discord-config-unsubstituted.yaml" cfg.settings; default_token = "This value is generated when generating the registration"; + settingsDefault = { + homeserver = { + address = ""; + domain = ""; + }; + + appservice = { + address = "http://localhost:29334"; + hostname = "0.0.0.0"; + port = 29334; + database = { + type = "sqlite3"; + uri = "file:${defaultDataDir}/mautrix-discord.db?_txlock=immediate"; + }; + id = "discord"; + bot = { + username = "discordbot"; + displayname = "Discord bridge bot"; + avatar = "mxc://maunium.net/nIdEykemnwdisvHbpxflpDlC"; + }; + as_token = default_token; + hs_token = default_token; + }; + + bridge.permissions."*" = "relay"; + + logging = { + min_level = "info"; + writers = [ + { + type = "stdout"; + format = "pretty-colored"; + time_format = " "; + } + ]; + }; + }; in { options = { @@ -23,238 +67,14 @@ in package = lib.mkPackageOption pkgs "mautrix-discord" { }; settings = lib.mkOption { - type = lib.types.submodule { - freeformType = format.type; - - config = { - _module.args = { inherit cfg lib; }; - }; - - options = { - homeserver = lib.mkOption { - type = lib.types.attrs; - default = { - software = "standard"; - status_endpoint = null; - message_send_checkpoint_endpoint = null; - async_media = false; - websocket = false; - ping_interval_seconds = 0; - }; - description = '' - fullDataDiration. - See [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml) - for more information. - ''; - }; - - appservice = lib.mkOption { - type = lib.types.attrs; - default = { - address = "http://localhost:29334"; - hostname = "0.0.0.0"; - port = 29334; - database = { - type = "sqlite3"; - uri = "file:/var/lib/mautrix-discord/mautrix-discord.db?_txlock=immediate"; - max_open_conns = 20; - max_idle_conns = 2; - max_conn_idle_time = null; - max_conn_lifetime = null; - }; - id = "discord"; - bot = { - username = "discordbot"; - displayname = "Discord bridge bot"; - avatar = "mxc://maunium.net/nIdEykemnwdisvHbpxflpDlC"; - }; - ephemeral_events = true; - async_transactions = false; - as_token = "This value is generated when generating the registration"; - hs_token = "This value is generated when generating the registration"; - }; - defaultText = lib.literalExpression '' - { - address = "http://localhost:29334"; - hostname = "0.0.0.0"; - port = 29334; - database = { - type = "sqlite3"; - uri = "file:''${config.services.mautrix-discord.dataDir}/mautrix-discord.db?_txlock=immediate"; - max_open_conns = 20; - max_idle_conns = 2; - max_conn_idle_time = null; - max_conn_lifetime = null; - }; - id = "discord"; - bot = { - username = "discordbot"; - displayname = "Discord bridge bot"; - avatar = "mxc://maunium.net/nIdEykemnwdisvHbpxflpDlC"; - }; - ephemeral_events = true; - async_transactions = false; - as_token = "This value is generated when generating the registration"; - hs_token = "This value is generated when generating the registration"; - } - ''; - description = '' - Appservice configuration. - See [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml) - for more information. - ''; - }; - - bridge = lib.mkOption { - type = lib.types.attrs; - default = { - username_template = "discord_{{.}}"; - displayname_template = "{{if .Webhook}}Webhook{{else}}{{or .GlobalName .Username}}{{if .Bot}} (bot){{end}}{{end}}"; - channel_name_template = "{{if or (eq .Type 3) (eq .Type 4)}}{{.Name}}{{else}}#{{.Name}}{{end}}"; - guild_name_template = "{{.Name}}"; - private_chat_portal_meta = "default"; - public_address = null; - avatar_proxy_key = "generate"; - portal_message_buffer = 128; - startup_private_channel_create_limit = 5; - delivery_receipts = false; - message_status_events = false; - message_error_notices = true; - restricted_rooms = true; - autojoin_thread_on_open = true; - embed_fields_as_tables = true; - mute_channels_on_create = false; - sync_direct_chat_list = false; - resend_bridge_info = false; - custom_emoji_reactions = true; - delete_portal_on_channel_delete = false; - delete_guild_on_leave = true; - federate_rooms = true; - prefix_webhook_messages = true; - enable_webhook_avatars = false; - use_discord_cdn_upload = true; - #proxy = - cache_media = "unencrypted"; - direct_media = { - enabled = false; - #server_name = "discord-media.example.com"; - #well_known_response = - allow_proxy = true; - server_key = "generate"; - }; - animated_sticker = { - target = "webp"; - args = { - width = 320; - height = 320; - fps = 25; - }; - }; - double_puppet_server_map = { - #"example.com" = "https://example.com"; - }; - double_puppet_allow_discovery = false; - login_shared_secret_map = { - #"example.com" = "foobar"; - }; - command_prefix = "!discord"; - management_room_text = { - welcome = "Hello, I'm a Discord bridge bot."; - welcome_connected = "Use `help` for help."; - welcome_unconnected = "Use `help` for help or `login` to log in."; - additional_help = ""; - }; - backfill = { - forward_limits = { - initial = { - dm = 0; - channel = 0; - thread = 0; - }; - missed = { - dm = 0; - channel = 0; - thread = 0; - }; - max_guild_members = -1; - }; - }; - encryption = { - allow = false; - default = false; - appservice = false; - msc4190 = false; - require = false; - allow_key_sharing = false; - plaintext_mentions = false; - delete_keys = { - delete_outbound_on_ack = false; - dont_store_outbound = false; - ratchet_on_decrypt = false; - delete_fully_used_on_decrypt = false; - delete_prev_on_new_session = false; - delete_on_device_delete = false; - periodically_delete_expired = false; - delete_outdated_inbound = false; - }; - verification_levels = { - receive = "unverified"; - send = "unverified"; - share = "cross-signed-tofu"; - }; - rotation = { - enable_custom = false; - milliseconds = 604800000; - messages = 100; - disable_device_change_key_rotation = false; - }; - }; - provisioning = { - prefix = "/_matrix/provision"; - shared_secret = "generate"; - debug_endpoints = false; - }; - permissions = { - "*" = "relay"; - #"example.com" = "user"; - #"@admin:example.com": "admin"; - }; - }; - description = '' - Bridge configuration. - See [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml) - for more information. - ''; - }; - logging = lib.mkOption { - type = lib.types.attrs; - default = { - min_level = "info"; - writers = lib.singleton { - type = "stdout"; - format = "pretty-colored"; - time_format = " "; - }; - }; - description = '' - Logging configuration. - See [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml) - for more information. - ''; - }; - }; - }; - default = { }; + apply = lib.recursiveUpdate settingsDefault; + type = format.type; + default = settingsDefault; example = lib.literalExpression '' { homeserver = { address = "http://localhost:8008"; - domain = "public-domain.tld"; - }; - - appservice.public = { - prefix = "/public"; - external = "https://public-appservice-address/public"; + domain = "example.com"; }; bridge.permissions = { @@ -265,8 +85,12 @@ in ''; description = '' {file}`config.yaml` configuration as a Nix attribute set. + Configuration options should match those described in [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml). + + Secret tokens should be specified using {option}`environmentFile` + instead of this world-readable attribute set. ''; }; @@ -282,12 +106,9 @@ in dataDir = lib.mkOption { type = lib.types.path; - default = "/var/lib/mautrix-discord"; - defaultText = "/var/lib/mautrix-discord"; - description = '' - Directory to store the bridge's configuration and database files. - This directory will be created if it does not exist. - ''; + default = defaultDataDir; + defaultText = defaultDataDir; + description = "Directory to store the bridge's data."; }; # TODO: Get upstream to add an environment File option. Refer to https://github.com/NixOS/nixpkgs/pull/404871#issuecomment-2895663652 and https://github.com/mautrix/discord/issues/187 @@ -295,92 +116,31 @@ in type = lib.types.nullOr lib.types.path; default = null; description = '' - File containing environment variables to substitute when copying the configuration - out of Nix store to the `services.mautrix-discord.dataDir`. - Can be used for storing the secrets without making them available in the Nix store. - For example, you can set `services.mautrix-discord.settings.appservice.as_token = "$MAUTRIX_DISCORD_APPSERVICE_AS_TOKEN"` - and then specify `MAUTRIX_DISCORD_APPSERVICE_AS_TOKEN="{token}"` in the environment file. - This value will get substituted into the configuration file as a token. + File containing environment variables for secret substitution. + Variables in the config like `$VARIABLE` will be replaced. ''; }; - serviceUnit = lib.mkOption { - type = lib.types.str; - readOnly = true; - default = "mautrix-discord.service"; - description = '' - The systemd unit (a service or a target) for other services to depend on if they - need to be started after matrix-synapse. - This option is useful as the actual parent unit for all matrix-synapse processes - changes when configuring workers. - ''; - }; - - registrationServiceUnit = lib.mkOption { - type = lib.types.str; - readOnly = true; - default = "mautrix-discord-registration.service"; - description = '' - The registration service that generates the registration file. - Systemd unit (a service or a target) for other services to depend on if they - need to be started after mautrix-discord registration service. - This option is useful as the actual parent unit for all matrix-synapse processes - changes when configuring workers. - ''; - }; - - serviceDependencies = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ - cfg.registrationServiceUnit - ] - ++ (lib.lists.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit) - ++ (lib.lists.optional config.services.matrix-conduit.enable "matrix-conduit.service") - ++ (lib.lists.optional config.services.dendrite.enable "dendrite.service"); - - defaultText = '' - [ cfg.registrationServiceUnit ] ++ - (lib.lists.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit) ++ - (lib.lists.optional config.services.matrix-conduit.enable "matrix-conduit.service") ++ - (lib.lists.optional config.services.dendrite.enable "dendrite.service"); - ''; - description = '' - List of Systemd services to require and wait for when starting the application service. - ''; - }; }; }; config = lib.mkIf cfg.enable { + assertions = [ { assertion = - cfg.settings.homeserver.domain or "" != "" && cfg.settings.homeserver.address or "" != ""; - message = '' - The options with information about the homeserver: - `services.mautrix-discord.settings.homeserver.domain` and - `services.mautrix-discord.settings.homeserver.address` have to be set. - ''; - } - { - assertion = cfg.settings.bridge.permissions or { } != { }; - message = '' - The option `services.mautrix-discord.settings.bridge.permissions` has to be set. - ''; + cfg.settings.homeserver.address or "" != "" && cfg.settings.homeserver.domain or "" != ""; + message = "services.mautrix-discord.settings.homeserver.{address,domain} must be set."; } ]; users.users.mautrix-discord = { isSystemUser = true; group = "mautrix-discord"; - extraGroups = [ "mautrix-discord-registration" ]; home = dataDir; description = "Mautrix-Discord bridge user"; }; users.groups.mautrix-discord = { }; - users.groups.mautrix-discord-registration = { - members = lib.lists.optional config.services.matrix-synapse.enable "matrix-synapse"; - }; services.matrix-synapse = lib.mkIf cfg.registerToSynapse { settings.app_service_config_files = [ registrationFile ]; @@ -392,7 +152,9 @@ in systemd.services = { matrix-synapse = lib.mkIf cfg.registerToSynapse { - serviceConfig.SupplementaryGroups = [ "mautrix-discord-registration" ]; + serviceConfig.SupplementaryGroups = [ + "mautrix-discord" + ]; # Make synapse depend on the registration service when auto-registering wants = [ "mautrix-discord-registration.service" ]; after = [ "mautrix-discord-registration.service" ]; @@ -471,15 +233,18 @@ in '${settingsFile}' '${registrationFile}' > '${registrationFile}.tmp' mv '${registrationFile}.tmp' '${registrationFile}' + # Application services should not be rate limited by default. + yq -Y '.rate_limited = false' '${registrationFile}' > '${registrationFile}.tmp' + mv '${registrationFile}.tmp' '${registrationFile}' + umask $old_umask - chown :mautrix-discord-registration '${registrationFile}' chmod 640 '${registrationFile}' ''; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; - UMask = 27; + UMask = "027"; User = "mautrix-discord"; Group = "mautrix-discord"; @@ -501,8 +266,8 @@ in description = "Mautrix-Discord, a Matrix-Discord puppeting/relaybot bridge"; wantedBy = [ "multi-user.target" ]; - wants = [ "network-online.target" ] ++ cfg.serviceDependencies; - after = [ "network-online.target" ] ++ cfg.serviceDependencies; + wants = [ "network-online.target" ] ++ serviceDependencies; + after = [ "network-online.target" ] ++ serviceDependencies; path = [ pkgs.lottieconverter pkgs.ffmpeg-headless @@ -536,6 +301,8 @@ in ProtectHostname = true; ProtectClock = true; + UMask = "027"; + SystemCallArchitectures = "native"; SystemCallErrorNumber = "EPERM"; SystemCallFilter = "@system-service"; @@ -546,10 +313,12 @@ in }; }; - meta = { - maintainers = with lib.maintainers; [ - mistyttm - ]; - }; + }; + + meta = { + maintainers = with lib.maintainers; [ + mistyttm + ]; + doc = ./mautrix-discord.md; }; } diff --git a/nixos/tests/matrix/mautrix-discord.nix b/nixos/tests/matrix/mautrix-discord.nix index 1e53baac7af2..fe27b1db8843 100644 --- a/nixos/tests/matrix/mautrix-discord.nix +++ b/nixos/tests/matrix/mautrix-discord.nix @@ -1,4 +1,4 @@ -{ pkgs, lib, ... }: +{ pkgs, ... }: let homeserverUrl = "http://homeserver:8008"; in @@ -53,6 +53,7 @@ in appservice = { address = "http://homeserver:8009"; + hostname = "0.0.0.0"; port = 8009; id = "discord"; bot = { @@ -60,8 +61,6 @@ in displayname = "Discord bridge bot"; avatar = "mxc://maunium.net/nIdEykemnwdisvHbpxflpDlC"; }; - # Don't override as_token/hs_token - let them use the default placeholder - # which will trigger automatic generation database = { type = "sqlite3"; @@ -75,6 +74,17 @@ in "*" = "relay"; }; }; + + logging = { + min_level = "info"; + writers = [ + { + type = "stdout"; + format = "pretty-colored"; + time_format = " "; + } + ]; + }; }; }; @@ -157,6 +167,57 @@ in with subtest("verify registration file was created"): homeserver.wait_until_succeeds("test -f /var/lib/mautrix-discord/discord-registration.yaml") + # Verify the module wrote the expected bridge configuration. + config_homeserver_address = homeserver.succeed("yq -r '.homeserver.address' /var/lib/mautrix-discord/config.yaml").strip() + config_homeserver_domain = homeserver.succeed("yq -r '.homeserver.domain' /var/lib/mautrix-discord/config.yaml").strip() + config_appservice_address = homeserver.succeed("yq -r '.appservice.address' /var/lib/mautrix-discord/config.yaml").strip() + config_appservice_hostname = homeserver.succeed("yq -r '.appservice.hostname' /var/lib/mautrix-discord/config.yaml").strip() + config_appservice_port = homeserver.succeed("yq -r '.appservice.port' /var/lib/mautrix-discord/config.yaml").strip() + config_appservice_id = homeserver.succeed("yq -r '.appservice.id' /var/lib/mautrix-discord/config.yaml").strip() + config_bot_username = homeserver.succeed("yq -r '.appservice.bot.username' /var/lib/mautrix-discord/config.yaml").strip() + config_bot_displayname = homeserver.succeed("yq -r '.appservice.bot.displayname' /var/lib/mautrix-discord/config.yaml").strip() + config_bot_avatar = homeserver.succeed("yq -r '.appservice.bot.avatar' /var/lib/mautrix-discord/config.yaml").strip() + config_database_type = homeserver.succeed("yq -r '.appservice.database.type' /var/lib/mautrix-discord/config.yaml").strip() + config_database_uri = homeserver.succeed("yq -r '.appservice.database.uri' /var/lib/mautrix-discord/config.yaml").strip() + config_permission = homeserver.succeed("yq -r '.bridge.permissions[\"*\"]' /var/lib/mautrix-discord/config.yaml").strip() + config_logging_min_level = homeserver.succeed("yq -r '.logging.min_level' /var/lib/mautrix-discord/config.yaml").strip() + config_logging_writer_type = homeserver.succeed("yq -r '.logging.writers[0].type' /var/lib/mautrix-discord/config.yaml").strip() + config_logging_writer_format = homeserver.succeed("yq -r '.logging.writers[0].format' /var/lib/mautrix-discord/config.yaml").strip() + reg_rate_limited = homeserver.succeed("yq -r '.rate_limited' /var/lib/mautrix-discord/discord-registration.yaml").strip() + + assert config_homeserver_address == "http://homeserver:8008", \ + f"Unexpected homeserver address: {config_homeserver_address}" + assert config_homeserver_domain == "homeserver", \ + f"Unexpected homeserver domain: {config_homeserver_domain}" + assert config_appservice_address == "http://homeserver:8009", \ + f"Unexpected appservice address: {config_appservice_address}" + assert config_appservice_hostname == "0.0.0.0", \ + f"Unexpected appservice hostname: {config_appservice_hostname}" + assert config_appservice_port == "8009", \ + f"Unexpected appservice port: {config_appservice_port}" + assert config_appservice_id == "discord", \ + f"Unexpected appservice id: {config_appservice_id}" + assert config_bot_username == "discordbot", \ + f"Unexpected bot username: {config_bot_username}" + assert config_bot_displayname == "Discord bridge bot", \ + f"Unexpected bot displayname: {config_bot_displayname}" + assert config_bot_avatar == "mxc://maunium.net/nIdEykemnwdisvHbpxflpDlC", \ + f"Unexpected bot avatar: {config_bot_avatar}" + assert config_database_type == "sqlite3-fk-wal", \ + f"Unexpected database type: {config_database_type}" + assert config_database_uri == "file:/var/lib/mautrix-discord/mautrix-discord.db?_txlock=immediate", \ + f"Unexpected database uri: {config_database_uri}" + assert config_permission == "relay", \ + f"Unexpected default permission mapping: {config_permission}" + assert config_logging_min_level == "info", \ + f"Unexpected logging min_level: {config_logging_min_level}" + assert config_logging_writer_type == "stdout", \ + f"Unexpected logging writer type: {config_logging_writer_type}" + assert config_logging_writer_format == "pretty-colored", \ + f"Unexpected logging writer format: {config_logging_writer_format}" + assert reg_rate_limited == "false", \ + f"Registration file should disable rate limiting by default, got: {reg_rate_limited}" + # Verify tokens were generated and are not default values config_as_token = homeserver.succeed("yq -r '.appservice.as_token' /var/lib/mautrix-discord/config.yaml").strip() config_hs_token = homeserver.succeed("yq -r '.appservice.hs_token' /var/lib/mautrix-discord/config.yaml").strip()