From 7601734651ee2de9767246fd58bd3598fdfdd8ca Mon Sep 17 00:00:00 2001 From: h7x4 Date: Sat, 21 Jun 2025 19:54:10 +0200 Subject: [PATCH] modules/ooye: init --- flake.nix | 1 + modules/matrix-ooye.nix | 211 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 modules/matrix-ooye.nix diff --git a/flake.nix b/flake.nix index f510e99..f8b1389 100644 --- a/flake.nix +++ b/flake.nix @@ -166,6 +166,7 @@ snappymail = ./modules/snappymail.nix; robots-txt = ./modules/robots-txt.nix; gickup = ./modules/gickup; + ooye = ./modules/matrix-ooye.nix; }; devShells = forAllSystems (system: { diff --git a/modules/matrix-ooye.nix b/modules/matrix-ooye.nix new file mode 100644 index 0000000..9953bd3 --- /dev/null +++ b/modules/matrix-ooye.nix @@ -0,0 +1,211 @@ +# Original from: https://cgit.rory.gay/nix/OOYE-module.git/ + +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.services.matrix-ooye; + mkStringOption = + name: default: + lib.mkOption { + type = lib.types.str; + default = default; + }; +in +{ + options = { + services.matrix-ooye = { + enable = lib.mkEnableOption "Enable OOYE service"; + package = lib.mkOption { + type = lib.types.package; + default = pkgs.out-of-your-element; + }; + appserviceId = mkStringOption "The ID of the appservice." "ooye"; + homeserver = mkStringOption "The homeserver to connect to." "http://localhost:8006"; + homeserverName = mkStringOption "The name of the homeserver to connect to." "localhost"; + namespace = mkStringOption "The prefix to use for the MXIDs/aliases of bridged users/rooms. Should end with a _!" "_ooye_"; + discordTokenPath = mkStringOption "The path to the discord token file." "/etc/ooye-discord-token"; + discordClientSecretPath = mkStringOption "The path to the discord token file." "/etc/ooye-discord-client-secret"; + socket = mkStringOption "The socket to listen on, can either be a port number or a unix socket path." "6693"; + bridgeOrigin = mkStringOption "The web frontend URL for the bridge, defaults to http://localhost:{socket}" ""; + + enableSynapseIntegration = lib.mkEnableOption "Enable Synapse integration"; + }; + }; + config = lib.mkIf cfg.enable ( + let + baseConfig = pkgs.writeText "matrix-ooye-config.json" ( + builtins.toJSON { + id = cfg.appserviceId; + namespaces = { + users = [ + { + exclusive = true; + regex = "@${cfg.namespace}.*:${cfg.homeserverName}"; + } + ]; + aliases = [ + { + exclusive = true; + regex = "#${cfg.namespace}.*:${cfg.homeserverName}"; + } + ]; + }; + protocols = [ "discord" ]; + sender_localpart = "${cfg.namespace}bot"; + rate_limited = false; + socket = cfg.socket; # Can either be a TCP port or a unix socket path + url = if (lib.hasPrefix "/" cfg.socket) then "unix:${cfg.socket}" else "http://localhost:${cfg.socket}"; + ooye = { + server_name = cfg.homeserverName; + namespace_prefix = cfg.namespace; + max_file_size = 5000000; + content_length_workaround = false; + include_user_id_in_mxid = true; + server_origin = cfg.homeserver; + bridge_origin = if (cfg.bridgeOrigin == "") then "http://localhost:${cfg.socket}" else cfg.bridgeOrigin; + }; + } + ); + + script = pkgs.writeScript "matrix-ooye-pre-start.sh" '' + #!${lib.getExe pkgs.bash} + REGISTRATION_FILE=registration.yaml + + id + echo "Before if statement" + stat ''${REGISTRATION_FILE} + + if [[ ! -f ''${REGISTRATION_FILE} ]]; then + echo "No registration file found at '$REGISTRATION_FILE'" + cp --no-preserve=mode,ownership ${baseConfig} ''${REGISTRATION_FILE} + fi + + echo "After if statement" + stat ''${REGISTRATION_FILE} + + AS_TOKEN=$(${lib.getExe pkgs.jq} -r .as_token ''${REGISTRATION_FILE}) + HS_TOKEN=$(${lib.getExe pkgs.jq} -r .hs_token ''${REGISTRATION_FILE}) + DISCORD_TOKEN=$(cat /run/credentials/matrix-ooye-pre-start.service/discord_token) + DISCORD_CLIENT_SECRET=$(cat /run/credentials/matrix-ooye-pre-start.service/discord_client_secret) + + # Check if we have all required tokens + if [[ -z "$AS_TOKEN" || "$AS_TOKEN" == "null" ]]; then + AS_TOKEN=$(${lib.getExe pkgs.openssl} rand -hex 64) + echo "Generated new AS token: ''${AS_TOKEN}" + fi + + if [[ -z "$HS_TOKEN" || "$HS_TOKEN" == "null" ]]; then + HS_TOKEN=$(${lib.getExe pkgs.openssl} rand -hex 64) + echo "Generated new HS token: ''${HS_TOKEN}" + fi + + if [[ -z "$DISCORD_TOKEN" ]]; then + echo "No Discord token found at '${cfg.discordTokenPath}'" + echo "You can find this on the 'Bot' tab of your Discord application." + exit 1 + fi + + if [[ -z "$DISCORD_CLIENT_SECRET" ]]; then + echo "No Discord client secret found at '${cfg.discordTokenPath}'" + echo "You can find this on the 'OAuth2' tab of your Discord application." + exit 1 + fi + + shred -u ''${REGISTRATION_FILE} + cp --no-preserve=mode,ownership ${baseConfig} ''${REGISTRATION_FILE} + + ${lib.getExe pkgs.jq} '.as_token = "'$AS_TOKEN'" | .hs_token = "'$HS_TOKEN'" | .ooye.discord_token = "'$DISCORD_TOKEN'" | .ooye.discord_client_secret = "'$DISCORD_CLIENT_SECRET'"' ''${REGISTRATION_FILE} > ''${REGISTRATION_FILE}.tmp + + shred -u ''${REGISTRATION_FILE} + mv ''${REGISTRATION_FILE}.tmp ''${REGISTRATION_FILE} + ''; + + in + { + warnings = + lib.optionals ((builtins.substring (lib.stringLength cfg.namespace - 1) 1 cfg.namespace) != "_") [ + "OOYE namespace does not end with an underscore! This is recommended to have better ID formatting. Provided: '${cfg.namespace}'" + ] + ++ lib.optionals ((builtins.substring 0 1 cfg.namespace) != "_") [ + "OOYE namespace does not start with an underscore! This is recommended to avoid conflicts with registered users. Provided: '${cfg.namespace}'" + ]; + + environment.systemPackages = [ cfg.package ]; + + systemd.services."matrix-ooye-pre-start" = { + enable = true; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = script; + WorkingDirectory = "/var/lib/matrix-ooye"; + StateDirectory = "matrix-ooye"; + DynamicUser = true; + RemainAfterExit = true; + Type = "oneshot"; + + LoadCredential = [ + "discord_token:${cfg.discordTokenPath}" + "discord_client_secret:${cfg.discordClientSecretPath}" + ]; + }; + }; + + systemd.services."matrix-ooye" = { + enable = true; + description = "Out of Your Element - a Discord bridge for Matrix."; + + wants = [ + "network-online.target" + "matrix-synapse.service" + "conduit.service" + "dendrite.service" + ]; + after = [ + "matrix-ooye-pre-start.service" + "network-online.target" + ]; + requires = [ "matrix-ooye-pre-start.service" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ExecStart = lib.getExe config.services.matrix-ooye.package; + WorkingDirectory = "/var/lib/matrix-ooye"; + StateDirectory = "matrix-ooye"; + #ProtectSystem = "strict"; + #ProtectHome = true; + #PrivateTmp = true; + #NoNewPrivileges = true; + #PrivateDevices = true; + Restart = "on-failure"; + DynamicUser = true; + }; + }; + + systemd.services."matrix-synapse" = lib.mkIf cfg.enableSynapseIntegration { + + after = [ + "matrix-ooye-pre-start.service" + "network-online.target" + ]; + requires = [ "matrix-ooye-pre-start.service" ]; + serviceConfig = { + LoadCredential = [ + "matrix-ooye-registration:/var/lib/matrix-ooye/registration.yaml" + ]; + ExecStartPre = [ + "+${pkgs.coreutils}/bin/cp /run/credentials/matrix-synapse.service/matrix-ooye-registration ${config.services.matrix-synapse.dataDir}/ooye-registration.yaml" + "+${pkgs.coreutils}/bin/chown matrix-synapse:matrix-synapse ${config.services.matrix-synapse.dataDir}/ooye-registration.yaml" + ]; + }; + }; + + services.matrix-synapse.settings.app_service_config_files = lib.mkIf cfg.enableSynapseIntegration [ + "${config.services.matrix-synapse.dataDir}/ooye-registration.yaml" + ]; + } + ); +}