Switch MusicLink to Matrix-native config

Replace Matterbridge settings with matrix config options.

Generate TOML with proper room list commas.
This commit is contained in:
Dan 2026-01-21 23:10:53 -08:00
parent 15427dddaf
commit eb76cc5ad2
3 changed files with 115 additions and 115 deletions

View file

@ -105,9 +105,17 @@
musiclink = { musiclink = {
enable = true; enable = true;
matterbridge = { matrix = {
enable = true; server = "http://127.0.0.1:8008";
slackChannel = "music"; userId = "@musiclink:clarun.xyz";
rooms = [
"!whU7Geg7JPrBL5wHcW:clarun.xyz"
"!dT40EUcemb8e6bPiig:clarun.xyz"
"!DPQveBnfuDrbgOe6dm:clarun.xyz"
];
shadow = false;
healthAddr = ":8080";
stateStorePath = "data/matrix-state.db";
}; };
}; };
}; };

View file

@ -103,17 +103,47 @@ in
description = "Enable MusicLink bot"; description = "Enable MusicLink bot";
}; };
matterbridge = { matrix = {
enable = mkOption { server = mkOption {
type = types.bool; type = types.str;
default = true; default = "http://127.0.0.1:${toString cfg.matrix.port}";
description = "Enable bundled Matterbridge"; description = "Matrix homeserver base URL";
}; };
slackChannel = mkOption { userId = mkOption {
type = types.str; type = types.str;
default = "music"; default = "@musiclink:${cfg.matrix.serverName}";
description = "Slack channel for MusicLink"; description = "Matrix user ID for MusicLink";
};
rooms = mkOption {
type = types.listOf types.str;
default = [];
description = "Allowlisted Matrix room IDs";
};
tokenFile = mkOption {
type = types.path;
default = "/run/secrets/musiclink-matrix-token";
description = "Path to Matrix access token file";
};
shadow = mkOption {
type = types.bool;
default = false;
description = "Shadow mode (log responses without sending)";
};
healthAddr = mkOption {
type = types.str;
default = "";
description = "Health server listen address (optional)";
};
stateStorePath = mkOption {
type = types.str;
default = "data/matrix-state.db";
description = "Path to Matrix sync state store";
}; };
}; };
}; };
@ -298,9 +328,14 @@ in
# MusicLink Service # MusicLink Service
services.musiclink = mkIf cfg.musiclink.enable { services.musiclink = mkIf cfg.musiclink.enable {
enable = true; enable = true;
matterbridge = { matrix = {
enable = cfg.musiclink.matterbridge.enable; server = cfg.musiclink.matrix.server;
slackChannel = cfg.musiclink.matterbridge.slackChannel; userId = cfg.musiclink.matrix.userId;
rooms = cfg.musiclink.matrix.rooms;
tokenFile = cfg.musiclink.matrix.tokenFile;
shadow = cfg.musiclink.matrix.shadow;
healthAddr = cfg.musiclink.matrix.healthAddr;
stateStorePath = cfg.musiclink.matrix.stateStorePath;
}; };
}; };

View file

@ -4,18 +4,10 @@ with lib;
let let
cfg = config.services.musiclink; cfg = config.services.musiclink;
musiclinkPkg = musiclink; # musiclink input passed via specialArgs is the package set? No, usually it's the flake itself if not mapped.
# But in flake.nix we did: musiclink = inputs.musiclink.packages.x86_64-linux.default;
# So `musiclink` here is the package.
matterbridgePkg = pkgs.matterbridge.overrideAttrs (old: {
patches = (old.patches or []) ++ [
./patches/matterbridge-api-websocket-max-message.patch
];
});
in { in {
options.services.musiclink = { options.services.musiclink = {
enable = mkEnableOption "MusicLink bot with Matterbridge"; enable = mkEnableOption "MusicLink bot";
package = mkOption { package = mkOption {
type = types.package; type = types.package;
@ -23,100 +15,52 @@ in {
description = "The MusicLink bot package"; description = "The MusicLink bot package";
}; };
matterbridge = { matrix = {
enable = mkOption { server = mkOption {
type = types.bool; type = types.str;
default = true; description = "Matrix homeserver base URL";
description = "Enable bundled Matterbridge instance";
}; };
port = mkOption { userId = mkOption {
type = types.port;
default = 4242;
description = "Matterbridge API port";
};
slackChannel = mkOption {
type = types.str; type = types.str;
default = "music"; description = "Matrix user ID for the bot";
description = "Slack channel to bridge"; };
rooms = mkOption {
type = types.listOf types.str;
description = "Allowlisted Matrix room IDs";
};
tokenFile = mkOption {
type = types.path;
default = "/run/secrets/musiclink-matrix-token";
description = "Path to Matrix access token file";
};
shadow = mkOption {
type = types.bool;
default = false;
description = "Shadow mode (log responses without sending)";
};
healthAddr = mkOption {
type = types.str;
default = "";
description = "Health server listen address (optional)";
};
stateStorePath = mkOption {
type = types.str;
default = "data/matrix-state.db";
description = "Path to Matrix sync state store";
}; };
}; };
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
# -------------------------------------------------------------------------
# Matterbridge Service
# -------------------------------------------------------------------------
systemd.services.musiclink-matterbridge = mkIf cfg.matterbridge.enable {
description = "Matterbridge for MusicLink";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
DynamicUser = true;
StateDirectory = "musiclink-matterbridge";
WorkingDirectory = "/var/lib/musiclink-matterbridge";
# Secrets
LoadCredential = [
"musiclink-matrix-token:/run/secrets/musiclink-matrix-token"
];
ExecStartPre = pkgs.writeShellScript "generate-matterbridge-config" ''
set -euo pipefail
MATRIX_TOKEN=$(cat $CREDENTIALS_DIRECTORY/musiclink-matrix-token)
cat > /var/lib/musiclink-matterbridge/matterbridge.toml <<EOF
[api.local]
BindAddress="127.0.0.1:${toString cfg.matterbridge.port}"
Token="musiclink-internal-token"
[matrix.clarun]
Server="http://127.0.0.1:8008"
Token="$MATRIX_TOKEN"
MxID="@musiclink:clarun.xyz"
[[gateway]]
name = "musiclink-gateway"
enable = true
[[gateway.inout]]
account = "api.local"
# Read/Write for bot testing room
[[gateway.inout]]
account = "matrix.clarun"
channel = "!whU7Geg7JPrBL5wHcW:clarun.xyz"
# Additional test room
[[gateway.inout]]
account = "matrix.clarun"
channel = "!dT40EUcemb8e6bPiig:clarun.xyz"
# #music room (bridged from Slack)
[[gateway.inout]]
account = "matrix.clarun"
channel = "!DPQveBnfuDrbgOe6dm:clarun.xyz"
EOF
'';
ExecStart = "${matterbridgePkg}/bin/matterbridge -conf /var/lib/musiclink-matterbridge/matterbridge.toml -debug";
Restart = "always";
RestartSec = "10s";
};
};
# -------------------------------------------------------------------------
# MusicLink Bot Service
# -------------------------------------------------------------------------
systemd.services.musiclink = { systemd.services.musiclink = {
description = "MusicLink Bot"; description = "MusicLink Bot";
after = [ "musiclink-matterbridge.service" ]; after = [ "network.target" ];
wants = [ "musiclink-matterbridge.service" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig = { serviceConfig = {
@ -125,18 +69,31 @@ EOF
StateDirectory = "musiclink"; StateDirectory = "musiclink";
WorkingDirectory = "/var/lib/musiclink"; WorkingDirectory = "/var/lib/musiclink";
LoadCredential = [
"musiclink-matrix-token:${cfg.matrix.tokenFile}"
];
ExecStartPre = pkgs.writeShellScript "generate-musiclink-config" '' ExecStartPre = pkgs.writeShellScript "generate-musiclink-config" ''
cat > /var/lib/musiclink/config.toml <<EOF set -euo pipefail
[matterbridge]
url = "ws://127.0.0.1:${toString cfg.matterbridge.port}/api/websocket" MATRIX_TOKEN=$(cat $CREDENTIALS_DIRECTORY/musiclink-matrix-token)
token = "musiclink-internal-token"
gateway = "musiclink-gateway" cat > /var/lib/musiclink/config.toml <<EOF
username = "MusicLink" [matrix]
shadow = ${lib.boolToString cfg.matrix.shadow}
healthAddr = "${cfg.matrix.healthAddr}"
server = "${cfg.matrix.server}"
accessToken = "$MATRIX_TOKEN"
userId = "${cfg.matrix.userId}"
rooms = [
${lib.concatMapStringsSep ",\n" (room: " \"${room}\"") cfg.matrix.rooms}
]
stateStorePath = "${cfg.matrix.stateStorePath}"
EOF EOF
''; '';
ExecStart = "${cfg.package}/bin/musiclink"; ExecStart = "${cfg.package}/bin/musiclink -config /var/lib/musiclink/config.toml";
Restart = "always"; Restart = "always";
RestartSec = "10s"; RestartSec = "10s";
}; };