Merge pull request #113988 from piegamesde/matrix-appservice-irc
This commit is contained in:
commit
b2ebc881cf
|
@ -7485,6 +7485,12 @@
|
||||||
githubId = 627831;
|
githubId = 627831;
|
||||||
name = "Hoang Xuan Phu";
|
name = "Hoang Xuan Phu";
|
||||||
};
|
};
|
||||||
|
piegames = {
|
||||||
|
name = "piegames";
|
||||||
|
email = "nix@piegames.de";
|
||||||
|
github = "piegamesde";
|
||||||
|
githubId = 14054505;
|
||||||
|
};
|
||||||
pierrechevalier83 = {
|
pierrechevalier83 = {
|
||||||
email = "pierrechevalier83@gmail.com";
|
email = "pierrechevalier83@gmail.com";
|
||||||
github = "pierrechevalier83";
|
github = "pierrechevalier83";
|
||||||
|
|
|
@ -499,6 +499,7 @@
|
||||||
./services/misc/lifecycled.nix
|
./services/misc/lifecycled.nix
|
||||||
./services/misc/mame.nix
|
./services/misc/mame.nix
|
||||||
./services/misc/matrix-appservice-discord.nix
|
./services/misc/matrix-appservice-discord.nix
|
||||||
|
./services/misc/matrix-appservice-irc.nix
|
||||||
./services/misc/matrix-synapse.nix
|
./services/misc/matrix-synapse.nix
|
||||||
./services/misc/mautrix-telegram.nix
|
./services/misc/mautrix-telegram.nix
|
||||||
./services/misc/mbpfan.nix
|
./services/misc/mbpfan.nix
|
||||||
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.matrix-appservice-irc;
|
||||||
|
|
||||||
|
pkg = pkgs.matrix-appservice-irc;
|
||||||
|
bin = "${pkg}/bin/matrix-appservice-irc";
|
||||||
|
|
||||||
|
jsonType = (pkgs.formats.json {}).type;
|
||||||
|
|
||||||
|
configFile = pkgs.runCommandNoCC "matrix-appservice-irc.yml" {
|
||||||
|
# Because this program will be run at build time, we need `nativeBuildInputs`
|
||||||
|
nativeBuildInputs = [ (pkgs.python3.withPackages (ps: [ ps.pyyaml ps.jsonschema ])) ];
|
||||||
|
preferLocalBuild = true;
|
||||||
|
|
||||||
|
config = builtins.toJSON cfg.settings;
|
||||||
|
passAsFile = [ "config" ];
|
||||||
|
} ''
|
||||||
|
# The schema is given as yaml, we need to convert it to json
|
||||||
|
python -c 'import json; import yaml; import sys; json.dump(yaml.safe_load(sys.stdin), sys.stdout)' \
|
||||||
|
< ${pkg}/lib/node_modules/matrix-appservice-irc/config.schema.yml \
|
||||||
|
> config.schema.json
|
||||||
|
python -m jsonschema config.schema.json -i $configPath
|
||||||
|
cp "$configPath" "$out"
|
||||||
|
'';
|
||||||
|
registrationFile = "/var/lib/matrix-appservice-irc/registration.yml";
|
||||||
|
in {
|
||||||
|
options.services.matrix-appservice-irc = with types; {
|
||||||
|
enable = mkEnableOption "the Matrix/IRC bridge";
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = port;
|
||||||
|
description = "The port to listen on";
|
||||||
|
default = 8009;
|
||||||
|
};
|
||||||
|
|
||||||
|
needBindingCap = mkOption {
|
||||||
|
type = bool;
|
||||||
|
description = "Whether the daemon needs to bind to ports below 1024 (e.g. for the ident service)";
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordEncryptionKeyLength = mkOption {
|
||||||
|
type = ints.unsigned;
|
||||||
|
description = "Length of the key to encrypt IRC passwords with";
|
||||||
|
default = 4096;
|
||||||
|
example = 8192;
|
||||||
|
};
|
||||||
|
|
||||||
|
registrationUrl = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = ''
|
||||||
|
The URL where the application service is listening for homeserver requests,
|
||||||
|
from the Matrix homeserver perspective.
|
||||||
|
'';
|
||||||
|
example = "http://localhost:8009";
|
||||||
|
};
|
||||||
|
|
||||||
|
localpart = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "The user_id localpart to assign to the appservice";
|
||||||
|
default = "appservice-irc";
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = mkOption {
|
||||||
|
description = ''
|
||||||
|
Configuration for the appservice, see
|
||||||
|
<link xlink:href="https://github.com/matrix-org/matrix-appservice-irc/blob/${pkgs.matrix-appservice-irc.version}/config.sample.yaml"/>
|
||||||
|
for supported values
|
||||||
|
'';
|
||||||
|
default = {};
|
||||||
|
type = submodule {
|
||||||
|
freeformType = jsonType;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
homeserver = mkOption {
|
||||||
|
description = "Homeserver configuration";
|
||||||
|
default = {};
|
||||||
|
type = submodule {
|
||||||
|
freeformType = jsonType;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
url = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "The URL to the home server for client-server API calls";
|
||||||
|
};
|
||||||
|
|
||||||
|
domain = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = ''
|
||||||
|
The 'domain' part for user IDs on this home server. Usually
|
||||||
|
(but not always) is the "domain name" part of the homeserver URL.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
database = mkOption {
|
||||||
|
default = {};
|
||||||
|
description = "Configuration for the database";
|
||||||
|
type = submodule {
|
||||||
|
freeformType = jsonType;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
engine = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Which database engine to use";
|
||||||
|
default = "nedb";
|
||||||
|
example = "postgres";
|
||||||
|
};
|
||||||
|
|
||||||
|
connectionString = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "The database connection string";
|
||||||
|
default = "nedb://var/lib/matrix-appservice-irc/data";
|
||||||
|
example = "postgres://username:password@host:port/databasename";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
ircService = mkOption {
|
||||||
|
default = {};
|
||||||
|
description = "IRC bridge configuration";
|
||||||
|
type = submodule {
|
||||||
|
freeformType = jsonType;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
passwordEncryptionKeyPath = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = ''
|
||||||
|
Location of the key with which IRC passwords are encrypted
|
||||||
|
for storage. Will be generated on first run if not present.
|
||||||
|
'';
|
||||||
|
default = "/var/lib/matrix-appservice-irc/passkey.pem";
|
||||||
|
};
|
||||||
|
|
||||||
|
servers = mkOption {
|
||||||
|
type = submodule { freeformType = jsonType; };
|
||||||
|
description = "IRC servers to connect to";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
systemd.services.matrix-appservice-irc = {
|
||||||
|
description = "Matrix-IRC bridge";
|
||||||
|
before = [ "matrix-synapse.service" ]; # So the registration can be used by Synapse
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
preStart = ''
|
||||||
|
umask 077
|
||||||
|
# Generate key for crypting passwords
|
||||||
|
if ! [ -f "${cfg.settings.ircService.passwordEncryptionKeyPath}" ]; then
|
||||||
|
${pkgs.openssl}/bin/openssl genpkey \
|
||||||
|
-out "${cfg.settings.ircService.passwordEncryptionKeyPath}" \
|
||||||
|
-outform PEM \
|
||||||
|
-algorithm RSA \
|
||||||
|
-pkeyopt "rsa_keygen_bits:${toString cfg.passwordEncryptionKeyLength}"
|
||||||
|
fi
|
||||||
|
# Generate registration file
|
||||||
|
if ! [ -f "${registrationFile}" ]; then
|
||||||
|
# The easy case: the file has not been generated yet
|
||||||
|
${bin} --generate-registration --file ${registrationFile} --config ${configFile} --url ${cfg.registrationUrl} --localpart ${cfg.localpart}
|
||||||
|
else
|
||||||
|
# The tricky case: we already have a generation file. Because the NixOS configuration might have changed, we need to
|
||||||
|
# regenerate it. But this would give the service a new random ID and tokens, so we need to back up and restore them.
|
||||||
|
# 1. Backup
|
||||||
|
id=$(grep "^id:.*$" ${registrationFile})
|
||||||
|
hs_token=$(grep "^hs_token:.*$" ${registrationFile})
|
||||||
|
as_token=$(grep "^as_token:.*$" ${registrationFile})
|
||||||
|
# 2. Regenerate
|
||||||
|
${bin} --generate-registration --file ${registrationFile} --config ${configFile} --url ${cfg.registrationUrl} --localpart ${cfg.localpart}
|
||||||
|
# 3. Restore
|
||||||
|
sed -i "s/^id:.*$/$id/g" ${registrationFile}
|
||||||
|
sed -i "s/^hs_token:.*$/$hs_token/g" ${registrationFile}
|
||||||
|
sed -i "s/^as_token:.*$/$as_token/g" ${registrationFile}
|
||||||
|
fi
|
||||||
|
# Allow synapse access to the registration
|
||||||
|
if ${getBin pkgs.glibc}/bin/getent group matrix-synapse > /dev/null; then
|
||||||
|
chgrp matrix-synapse ${registrationFile}
|
||||||
|
chmod g+r ${registrationFile}
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
|
serviceConfig = rec {
|
||||||
|
Type = "simple";
|
||||||
|
ExecStart = "${bin} --config ${configFile} --file ${registrationFile} --port ${toString cfg.port}";
|
||||||
|
|
||||||
|
ProtectHome = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
StateDirectory = "matrix-appservice-irc";
|
||||||
|
StateDirectoryMode = "755";
|
||||||
|
|
||||||
|
User = "matrix-appservice-irc";
|
||||||
|
Group = "matrix-appservice-irc";
|
||||||
|
|
||||||
|
CapabilityBoundingSet = [ "CAP_CHOWN" ] ++ optional (cfg.needBindingCap) "CAP_NET_BIND_SERVICE";
|
||||||
|
AmbientCapabilities = CapabilityBoundingSet;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
|
||||||
|
LockPersonality = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
PrivateMounts = true;
|
||||||
|
SystemCallFilter = "~@aio @clock @cpu-emulation @debug @keyring @memlock @module @mount @obsolete @raw-io @setuid @swap";
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
RestrictAddressFamilies = "AF_INET AF_INET6";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups.matrix-appservice-irc = {};
|
||||||
|
users.users.matrix-appservice-irc = {
|
||||||
|
description = "Service user for the Matrix-IRC bridge";
|
||||||
|
group = "matrix-appservice-irc";
|
||||||
|
isSystemUser = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -227,6 +227,7 @@ in
|
||||||
mariadb-galera-mariabackup = handleTest ./mysql/mariadb-galera-mariabackup.nix {};
|
mariadb-galera-mariabackup = handleTest ./mysql/mariadb-galera-mariabackup.nix {};
|
||||||
mariadb-galera-rsync = handleTest ./mysql/mariadb-galera-rsync.nix {};
|
mariadb-galera-rsync = handleTest ./mysql/mariadb-galera-rsync.nix {};
|
||||||
matomo = handleTest ./matomo.nix {};
|
matomo = handleTest ./matomo.nix {};
|
||||||
|
matrix-appservice-irc = handleTest ./matrix-appservice-irc.nix {};
|
||||||
matrix-synapse = handleTest ./matrix-synapse.nix {};
|
matrix-synapse = handleTest ./matrix-synapse.nix {};
|
||||||
mediawiki = handleTest ./mediawiki.nix {};
|
mediawiki = handleTest ./mediawiki.nix {};
|
||||||
memcached = handleTest ./memcached.nix {};
|
memcached = handleTest ./memcached.nix {};
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
import ./make-test-python.nix ({ pkgs, ... }:
|
||||||
|
let
|
||||||
|
homeserverUrl = "http://homeserver:8448";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = "matrix-appservice-irc";
|
||||||
|
meta = {
|
||||||
|
maintainers = pkgs.matrix-appservice-irc.meta.maintainers;
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
homeserver = { pkgs, ... }: {
|
||||||
|
# We'll switch to this once the config is copied into place
|
||||||
|
specialisation.running.configuration = {
|
||||||
|
services.matrix-synapse = {
|
||||||
|
enable = true;
|
||||||
|
database_type = "sqlite3";
|
||||||
|
app_service_config_files = [ "/registration.yml" ];
|
||||||
|
|
||||||
|
enable_registration = true;
|
||||||
|
|
||||||
|
listeners = [
|
||||||
|
# The default but tls=false
|
||||||
|
{
|
||||||
|
"bind_address" = "";
|
||||||
|
"port" = 8448;
|
||||||
|
"resources" = [
|
||||||
|
{ "compress" = true; "names" = [ "client" "webclient" ]; }
|
||||||
|
{ "compress" = false; "names" = [ "federation" ]; }
|
||||||
|
];
|
||||||
|
"tls" = false;
|
||||||
|
"type" = "http";
|
||||||
|
"x_forwarded" = false;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ 8448 ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
ircd = { pkgs, ... }: {
|
||||||
|
services.ngircd = {
|
||||||
|
enable = true;
|
||||||
|
config = ''
|
||||||
|
[Global]
|
||||||
|
Name = ircd.ircd
|
||||||
|
Info = Server Info Text
|
||||||
|
AdminInfo1 = _
|
||||||
|
|
||||||
|
[Channel]
|
||||||
|
Name = #test
|
||||||
|
Topic = a cool place
|
||||||
|
|
||||||
|
[Options]
|
||||||
|
PAM = no
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
networking.firewall.allowedTCPPorts = [ 6667 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
appservice = { pkgs, ... }: {
|
||||||
|
services.matrix-appservice-irc = {
|
||||||
|
enable = true;
|
||||||
|
registrationUrl = "http://appservice:8009";
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
homeserver.url = homeserverUrl;
|
||||||
|
homeserver.domain = "homeserver";
|
||||||
|
|
||||||
|
ircService.servers."ircd" = {
|
||||||
|
name = "IRCd";
|
||||||
|
port = 6667;
|
||||||
|
dynamicChannels = {
|
||||||
|
enabled = true;
|
||||||
|
aliasTemplate = "#irc_$CHANNEL";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ 8009 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
client = { pkgs, ... }: {
|
||||||
|
environment.systemPackages = [
|
||||||
|
(pkgs.writers.writePython3Bin "do_test"
|
||||||
|
{ libraries = [ pkgs.python3Packages.matrix-client ]; } ''
|
||||||
|
import socket
|
||||||
|
from matrix_client.client import MatrixClient
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
matrix = MatrixClient("${homeserverUrl}")
|
||||||
|
matrix.register_with_password(username="alice", password="foobar")
|
||||||
|
|
||||||
|
irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
irc.connect(("ircd", 6667))
|
||||||
|
irc.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
irc.send(b"USER bob bob bob :bob\n")
|
||||||
|
irc.send(b"NICK bob\n")
|
||||||
|
|
||||||
|
m_room = matrix.join_room("#irc_#test:homeserver")
|
||||||
|
irc.send(b"JOIN #test\n")
|
||||||
|
|
||||||
|
# plenty of time for the joins to happen
|
||||||
|
sleep(10)
|
||||||
|
|
||||||
|
m_room.send_text("hi from matrix")
|
||||||
|
irc.send(b"PRIVMSG #test :hi from irc \r\n")
|
||||||
|
|
||||||
|
print("Waiting for irc message...")
|
||||||
|
while True:
|
||||||
|
buf = irc.recv(10000)
|
||||||
|
if b"hi from matrix" in buf:
|
||||||
|
break
|
||||||
|
|
||||||
|
print("Waiting for matrix message...")
|
||||||
|
|
||||||
|
|
||||||
|
def callback(room, e):
|
||||||
|
if "hi from irc" in e['content']['body']:
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
m_room.add_listener(callback, "m.room.message")
|
||||||
|
matrix.listen_forever()
|
||||||
|
''
|
||||||
|
)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
ircd.wait_for_unit("ngircd.service")
|
||||||
|
ircd.wait_for_open_port(6667)
|
||||||
|
|
||||||
|
with subtest("start the appservice"):
|
||||||
|
appservice.wait_for_unit("matrix-appservice-irc.service")
|
||||||
|
appservice.wait_for_open_port(8009)
|
||||||
|
|
||||||
|
with subtest("copy the registration file"):
|
||||||
|
appservice.copy_from_vm("/var/lib/matrix-appservice-irc/registration.yml")
|
||||||
|
homeserver.copy_from_host(
|
||||||
|
pathlib.Path(os.environ.get("out", os.getcwd())) / "registration.yml", "/"
|
||||||
|
)
|
||||||
|
homeserver.succeed("chmod 444 /registration.yml")
|
||||||
|
|
||||||
|
with subtest("start the homeserver"):
|
||||||
|
homeserver.succeed(
|
||||||
|
"/run/current-system/specialisation/running/bin/switch-to-configuration test >&2"
|
||||||
|
)
|
||||||
|
|
||||||
|
homeserver.wait_for_unit("matrix-synapse.service")
|
||||||
|
homeserver.wait_for_open_port(8448)
|
||||||
|
|
||||||
|
with subtest("ensure messages can be exchanged"):
|
||||||
|
client.succeed("do_test")
|
||||||
|
'';
|
||||||
|
|
||||||
|
})
|
|
@ -0,0 +1,28 @@
|
||||||
|
{ pkgs, nodePackages, makeWrapper, nixosTests, nodejs, stdenv, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
packageName = with lib; concatStrings (map (entry: (concatStrings (mapAttrsToList (key: value: "${key}-${value}") entry))) (importJSON ./package.json));
|
||||||
|
|
||||||
|
ourNodePackages = import ./node-composition.nix {
|
||||||
|
inherit pkgs nodejs;
|
||||||
|
inherit (stdenv.hostPlatform) system;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
ourNodePackages."${packageName}".override {
|
||||||
|
nativeBuildInputs = [ makeWrapper nodePackages.node-gyp-build ];
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
makeWrapper '${nodejs}/bin/node' "$out/bin/matrix-appservice-irc" \
|
||||||
|
--add-flags "$out/lib/node_modules/matrix-appservice-irc/app.js"
|
||||||
|
'';
|
||||||
|
|
||||||
|
passthru.tests.matrix-appservice-irc = nixosTests.matrix-appservice-irc;
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "Node.js IRC bridge for Matrix";
|
||||||
|
maintainers = with maintainers; [ piegames ];
|
||||||
|
homepage = "https://github.com/matrix-org/matrix-appservice-irc";
|
||||||
|
license = licenses.asl20;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
ROOT="$(realpath "$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")"/../../../..)"
|
||||||
|
|
||||||
|
$(nix-build $ROOT -A nodePackages.node2nix --no-out-link)/bin/node2nix \
|
||||||
|
--nodejs-12 \
|
||||||
|
--node-env ../../../development/node-packages/node-env.nix \
|
||||||
|
--development \
|
||||||
|
--input package.json \
|
||||||
|
--output node-packages.nix \
|
||||||
|
--composition node-composition.nix
|
|
@ -0,0 +1,17 @@
|
||||||
|
# This file has been generated by node2nix 1.9.0. Do not edit!
|
||||||
|
|
||||||
|
{pkgs ? import <nixpkgs> {
|
||||||
|
inherit system;
|
||||||
|
}, system ? builtins.currentSystem, nodejs ? pkgs."nodejs-12_x"}:
|
||||||
|
|
||||||
|
let
|
||||||
|
nodeEnv = import ../../../development/node-packages/node-env.nix {
|
||||||
|
inherit (pkgs) stdenv lib python2 runCommand writeTextFile;
|
||||||
|
inherit pkgs nodejs;
|
||||||
|
libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
import ./node-packages.nix {
|
||||||
|
inherit (pkgs) fetchurl nix-gitignore stdenv lib fetchgit;
|
||||||
|
inherit nodeEnv;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
||||||
|
[
|
||||||
|
{"matrix-appservice-irc": "git+https://github.com/matrix-org/matrix-appservice-irc.git#0.25.0" }
|
||||||
|
]
|
|
@ -5933,6 +5933,8 @@ in
|
||||||
|
|
||||||
matrix-synapse-tools = recurseIntoAttrs matrix-synapse.tools;
|
matrix-synapse-tools = recurseIntoAttrs matrix-synapse.tools;
|
||||||
|
|
||||||
|
matrix-appservice-irc = callPackage ../servers/matrix-synapse/matrix-appservice-irc { };
|
||||||
|
|
||||||
matrix-appservice-slack = callPackage ../servers/matrix-synapse/matrix-appservice-slack {};
|
matrix-appservice-slack = callPackage ../servers/matrix-synapse/matrix-appservice-slack {};
|
||||||
|
|
||||||
matrix-appservice-discord = callPackage ../servers/matrix-appservice-discord { };
|
matrix-appservice-discord = callPackage ../servers/matrix-appservice-discord { };
|
||||||
|
|
Loading…
Reference in New Issue