1{
2 config,
3 options,
4 pkgs,
5 lib,
6 ...
7}:
8let
9 dataDir = "/var/lib/matrix-appservice-discord";
10 registrationFile = "${dataDir}/discord-registration.yaml";
11 cfg = config.services.matrix-appservice-discord;
12 opt = options.services.matrix-appservice-discord;
13 # TODO: switch to configGen.json once RFC42 is implemented
14 settingsFile = pkgs.writeText "matrix-appservice-discord-settings.json" (
15 builtins.toJSON cfg.settings
16 );
17
18in
19{
20 options = {
21 services.matrix-appservice-discord = {
22 enable = lib.mkEnableOption "a bridge between Matrix and Discord";
23
24 package = lib.mkPackageOption pkgs "matrix-appservice-discord" { };
25
26 settings = lib.mkOption rec {
27 # TODO: switch to lib.types.config.json as prescribed by RFC42 once it's implemented
28 type = lib.types.attrs;
29 apply = lib.recursiveUpdate default;
30 default = {
31 database = {
32 filename = "${dataDir}/discord.db";
33 };
34
35 # empty values necessary for registration file generation
36 # actual values defined in environmentFile
37 auth = {
38 clientID = "";
39 botToken = "";
40 };
41 };
42 example = lib.literalExpression ''
43 {
44 bridge = {
45 domain = "public-domain.tld";
46 homeserverUrl = "http://public-domain.tld:8008";
47 };
48 }
49 '';
50 description = ''
51 {file}`config.yaml` configuration as a Nix attribute set.
52
53 Configuration options should match those described in
54 [config.sample.yaml](https://github.com/Half-Shot/matrix-appservice-discord/blob/master/config/config.sample.yaml).
55
56 {option}`config.bridge.domain` and {option}`config.bridge.homeserverUrl`
57 should be set to match the public host name of the Matrix homeserver for webhooks and avatars to work.
58
59 Secret tokens should be specified using {option}`environmentFile`
60 instead of this world-readable attribute set.
61 '';
62 };
63
64 environmentFile = lib.mkOption {
65 type = lib.types.nullOr lib.types.path;
66 default = null;
67 description = ''
68 File containing environment variables to be passed to the matrix-appservice-discord service,
69 in which secret tokens can be specified securely by defining values for
70 `APPSERVICE_DISCORD_AUTH_CLIENT_I_D` and
71 `APPSERVICE_DISCORD_AUTH_BOT_TOKEN`.
72 '';
73 };
74
75 url = lib.mkOption {
76 type = lib.types.str;
77 default = "http://localhost:${toString cfg.port}";
78 defaultText = lib.literalExpression ''"http://localhost:''${toString config.${opt.port}}"'';
79 description = ''
80 The URL where the application service is listening for HS requests.
81 '';
82 };
83
84 port = lib.mkOption {
85 type = lib.types.port;
86 default = 9005; # from https://github.com/Half-Shot/matrix-appservice-discord/blob/master/package.json#L11
87 description = ''
88 Port number on which the bridge should listen for internal communication with the Matrix homeserver.
89 '';
90 };
91
92 localpart = lib.mkOption {
93 type = with lib.types; nullOr str;
94 default = null;
95 description = ''
96 The user_id localpart to assign to the AS.
97 '';
98 };
99
100 serviceDependencies = lib.mkOption {
101 type = with lib.types; listOf str;
102 default = lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit;
103 defaultText = lib.literalExpression ''
104 lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit
105 '';
106 description = ''
107 List of Systemd services to require and wait for when starting the application service,
108 such as the Matrix homeserver if it's running on the same host.
109 '';
110 };
111 };
112 };
113
114 config = lib.mkIf cfg.enable {
115 systemd.services.matrix-appservice-discord = {
116 description = "A bridge between Matrix and Discord.";
117
118 wantedBy = [ "multi-user.target" ];
119 wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
120 after = [ "network-online.target" ] ++ cfg.serviceDependencies;
121
122 preStart = ''
123 if [ ! -f '${registrationFile}' ]; then
124 ${cfg.package}/bin/matrix-appservice-discord \
125 --generate-registration \
126 --url=${lib.escapeShellArg cfg.url} \
127 ${
128 lib.optionalString (cfg.localpart != null) "--localpart=${lib.escapeShellArg cfg.localpart}"
129 } \
130 --config='${settingsFile}' \
131 --file='${registrationFile}'
132 fi
133 '';
134
135 serviceConfig = {
136 Type = "simple";
137 Restart = "always";
138
139 ProtectSystem = "strict";
140 ProtectHome = true;
141 ProtectKernelTunables = true;
142 ProtectKernelModules = true;
143 ProtectControlGroups = true;
144
145 DynamicUser = true;
146 PrivateTmp = true;
147 WorkingDirectory = "${cfg.package}/${cfg.package.passthru.nodeAppDir}";
148 StateDirectory = baseNameOf dataDir;
149 UMask = "0027";
150 EnvironmentFile = cfg.environmentFile;
151
152 ExecStart = ''
153 ${cfg.package}/bin/matrix-appservice-discord \
154 --file='${registrationFile}' \
155 --config='${settingsFile}' \
156 --port='${toString cfg.port}'
157 '';
158 };
159 };
160 };
161
162 meta.maintainers = with lib.maintainers; [ euxane ];
163}