mirror of
https://github.com/ilyakooo0/nixpkgs.git
synced 2024-09-20 03:59:30 +03:00
nixos/maubot: init
This commit is contained in:
parent
e96b8fd970
commit
00070cf866
@ -14,7 +14,7 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
||||
- Create the first release note entry in this section!
|
||||
- [maubot](https://github.com/maubot/maubot), a plugin-based Matrix bot framework. Available as [services.maubot](#opt-services.maubot.enable).
|
||||
|
||||
## Backward Incompatibilities {#sec-release-24.05-incompatibilities}
|
||||
|
||||
|
@ -621,6 +621,7 @@
|
||||
./services/matrix/appservice-irc.nix
|
||||
./services/matrix/conduit.nix
|
||||
./services/matrix/dendrite.nix
|
||||
./services/matrix/maubot.nix
|
||||
./services/matrix/mautrix-facebook.nix
|
||||
./services/matrix/mautrix-telegram.nix
|
||||
./services/matrix/mautrix-whatsapp.nix
|
||||
|
103
nixos/modules/services/matrix/maubot.md
Normal file
103
nixos/modules/services/matrix/maubot.md
Normal file
@ -0,0 +1,103 @@
|
||||
# Maubot {#module-services-maubot}
|
||||
|
||||
[Maubot](https://github.com/maubot/maubot) is a plugin-based bot
|
||||
framework for Matrix.
|
||||
|
||||
## Configuration {#module-services-maubot-configuration}
|
||||
|
||||
1. Set [](#opt-services.maubot.enable) to `true`. The service will use
|
||||
SQLite by default.
|
||||
2. If you want to use PostgreSQL instead of SQLite, do this:
|
||||
|
||||
```nix
|
||||
services.maubot.settings.database = "postgresql://maubot@localhost/maubot";
|
||||
```
|
||||
|
||||
If the PostgreSQL connection requires a password, you will have to
|
||||
add it later on step 8.
|
||||
3. If you plan to expose your Maubot interface to the web, do something
|
||||
like this:
|
||||
```nix
|
||||
services.nginx.virtualHosts."matrix.example.org".locations = {
|
||||
"/_matrix/maubot/" = {
|
||||
proxyPass = "http://127.0.0.1:${toString config.services.maubot.settings.server.port}";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
};
|
||||
services.maubot.settings.server.public_url = "matrix.example.org";
|
||||
# do the following only if you want to use something other than /_matrix/maubot...
|
||||
services.maubot.settings.server.ui_base_path = "/another/base/path";
|
||||
```
|
||||
4. Optionally, set `services.maubot.pythonPackages` to a list of python3
|
||||
packages to make available for Maubot plugins.
|
||||
5. Optionally, set `services.maubot.plugins` to a list of Maubot
|
||||
plugins (full list available at https://plugins.maubot.xyz/):
|
||||
```nix
|
||||
services.maubot.plugins = with config.services.maubot.package.plugins; [
|
||||
reactbot
|
||||
# This will only change the default config! After you create a
|
||||
# plugin instance, the default config will be copied into that
|
||||
# instance's config in Maubot's database, and further base config
|
||||
# changes won't affect the running plugin.
|
||||
(rss.override {
|
||||
base_config = {
|
||||
update_interval = 60;
|
||||
max_backoff = 7200;
|
||||
spam_sleep = 2;
|
||||
command_prefix = "rss";
|
||||
admins = [ "@chayleaf:pavluk.org" ];
|
||||
};
|
||||
})
|
||||
];
|
||||
# ...or...
|
||||
services.maubot.plugins = config.services.maubot.package.plugins.allOfficialPlugins;
|
||||
# ...or...
|
||||
services.maubot.plugins = config.services.maubot.package.plugins.allPlugins;
|
||||
# ...or...
|
||||
services.maubot.plugins = with config.services.maubot.package.plugins; [
|
||||
(weather.override {
|
||||
# you can pass base_config as a string
|
||||
base_config = ''
|
||||
default_location: New York
|
||||
default_units: M
|
||||
default_language:
|
||||
show_link: true
|
||||
show_image: false
|
||||
'';
|
||||
})
|
||||
];
|
||||
```
|
||||
6. Start Maubot at least once before doing the following steps (it's
|
||||
necessary to generate the initial config).
|
||||
7. If your PostgreSQL connection requires a password, add
|
||||
`database: postgresql://user:password@localhost/maubot`
|
||||
to `/var/lib/maubot/config.yaml`. This overrides the Nix-provided
|
||||
config. Even then, don't remove the `database` line from Nix config
|
||||
so the module knows you use PostgreSQL!
|
||||
8. To create a user account for logging into Maubot web UI and
|
||||
configuring it, generate a password using the shell command
|
||||
`mkpasswd -R 12 -m bcrypt`, and edit `/var/lib/maubot/config.yaml`
|
||||
with the following:
|
||||
|
||||
```yaml
|
||||
admins:
|
||||
admin_username: $2b$12$g.oIStUeUCvI58ebYoVMtO/vb9QZJo81PsmVOomHiNCFbh0dJpZVa
|
||||
```
|
||||
|
||||
Where `admin_username` is your username, and `$2b...` is the bcrypted
|
||||
password.
|
||||
9. Optional: if you want to be able to register new users with the
|
||||
Maubot CLI (`mbc`), and your homeserver is private, add your
|
||||
homeserver's registration key to `/var/lib/maubot/config.yaml`:
|
||||
|
||||
```yaml
|
||||
homeservers:
|
||||
matrix.example.org:
|
||||
url: https://matrix.example.org
|
||||
secret: your-very-secret-key
|
||||
```
|
||||
10. Restart Maubot after editing `/var/lib/maubot/config.yaml`,and
|
||||
Maubot will be available at
|
||||
`https://matrix.example.org/_matrix/maubot`. If you want to use the
|
||||
`mbc` CLI, it's available using the `maubot` package (`nix-shell -p
|
||||
maubot`).
|
459
nixos/modules/services/matrix/maubot.nix
Normal file
459
nixos/modules/services/matrix/maubot.nix
Normal file
@ -0,0 +1,459 @@
|
||||
{ lib
|
||||
, config
|
||||
, pkgs
|
||||
, ...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.services.maubot;
|
||||
|
||||
wrapper1 =
|
||||
if cfg.plugins == [ ]
|
||||
then cfg.package
|
||||
else cfg.package.withPlugins (_: cfg.plugins);
|
||||
|
||||
wrapper2 =
|
||||
if cfg.pythonPackages == [ ]
|
||||
then wrapper1
|
||||
else wrapper1.withPythonPackages (_: cfg.pythonPackages);
|
||||
|
||||
settings = lib.recursiveUpdate cfg.settings {
|
||||
plugin_directories.trash =
|
||||
if cfg.settings.plugin_directories.trash == null
|
||||
then "delete"
|
||||
else cfg.settings.plugin_directories.trash;
|
||||
server.unshared_secret = "generate";
|
||||
};
|
||||
|
||||
finalPackage = wrapper2.withBaseConfig settings;
|
||||
|
||||
isPostgresql = db: builtins.isString db && lib.hasPrefix "postgresql://" db;
|
||||
isLocalPostgresDB = db: isPostgresql db && builtins.any (x: lib.hasInfix x db) [
|
||||
"@127.0.0.1/"
|
||||
"@::1/"
|
||||
"@[::1]/"
|
||||
"@localhost/"
|
||||
];
|
||||
parsePostgresDB = db:
|
||||
let
|
||||
noSchema = lib.removePrefix "postgresql://" db;
|
||||
in {
|
||||
username = builtins.head (lib.splitString "@" noSchema);
|
||||
database = lib.last (lib.splitString "/" noSchema);
|
||||
};
|
||||
|
||||
postgresDBs = [
|
||||
cfg.settings.database
|
||||
cfg.settings.crypto_database
|
||||
cfg.settings.plugin_databases.postgres
|
||||
];
|
||||
|
||||
localPostgresDBs = builtins.filter isLocalPostgresDB postgresDBs;
|
||||
|
||||
parsedLocalPostgresDBs = map parsePostgresDB localPostgresDBs;
|
||||
parsedPostgresDBs = map parsePostgresDB postgresDBs;
|
||||
|
||||
hasLocalPostgresDB = localPostgresDBs != [ ];
|
||||
in
|
||||
{
|
||||
options.services.maubot = with lib; {
|
||||
enable = mkEnableOption (mdDoc "maubot");
|
||||
|
||||
package = lib.mkPackageOptionMD pkgs "maubot" { };
|
||||
|
||||
plugins = mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [ ];
|
||||
example = literalExpression ''
|
||||
with config.services.maubot.package.plugins; [
|
||||
xyz.maubot.reactbot
|
||||
xyz.maubot.rss
|
||||
];
|
||||
'';
|
||||
description = mdDoc ''
|
||||
List of additional maubot plugins to make available.
|
||||
'';
|
||||
};
|
||||
|
||||
pythonPackages = mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [ ];
|
||||
example = literalExpression ''
|
||||
with pkgs.python3Packages; [
|
||||
aiohttp
|
||||
];
|
||||
'';
|
||||
description = mdDoc ''
|
||||
List of additional Python packages to make available for maubot.
|
||||
'';
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/lib/maubot";
|
||||
description = mdDoc ''
|
||||
The directory where maubot stores its stateful data.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfigFile = mkOption {
|
||||
type = types.str;
|
||||
default = "./config.yaml";
|
||||
defaultText = literalExpression ''"''${config.services.maubot.dataDir}/config.yaml"'';
|
||||
description = mdDoc ''
|
||||
A file for storing secrets. You can pass homeserver registration keys here.
|
||||
If it already exists, **it must contain `server.unshared_secret`** which is used for signing API keys.
|
||||
If `configMutable` is not set to true, **maubot user must have write access to this file**.
|
||||
'';
|
||||
};
|
||||
|
||||
configMutable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = mdDoc ''
|
||||
Whether maubot should write updated config into `extraConfigFile`. **This will make your Nix module settings have no effect besides the initial config, as extraConfigFile takes precedence over NixOS settings!**
|
||||
'';
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
default = { };
|
||||
description = mdDoc ''
|
||||
YAML settings for maubot. See the
|
||||
[example configuration](https://github.com/maubot/maubot/blob/master/maubot/example-config.yaml)
|
||||
for more info.
|
||||
|
||||
Secrets should be passed in by using `extraConfigFile`.
|
||||
'';
|
||||
type = with types; submodule {
|
||||
options = {
|
||||
database = mkOption {
|
||||
type = str;
|
||||
default = "sqlite:maubot.db";
|
||||
example = "postgresql://username:password@hostname/dbname";
|
||||
description = mdDoc ''
|
||||
The full URI to the database. SQLite and Postgres are fully supported.
|
||||
Other DBMSes supported by SQLAlchemy may or may not work.
|
||||
'';
|
||||
};
|
||||
|
||||
crypto_database = mkOption {
|
||||
type = str;
|
||||
default = "default";
|
||||
example = "postgresql://username:password@hostname/dbname";
|
||||
description = mdDoc ''
|
||||
Separate database URL for the crypto database. By default, the regular database is also used for crypto.
|
||||
'';
|
||||
};
|
||||
|
||||
database_opts = mkOption {
|
||||
type = types.attrs;
|
||||
default = { };
|
||||
description = mdDoc ''
|
||||
Additional arguments for asyncpg.create_pool() or sqlite3.connect()
|
||||
'';
|
||||
};
|
||||
|
||||
plugin_directories = mkOption {
|
||||
default = { };
|
||||
description = mdDoc "Plugin directory paths";
|
||||
type = submodule {
|
||||
options = {
|
||||
upload = mkOption {
|
||||
type = types.str;
|
||||
default = "./plugins";
|
||||
defaultText = literalExpression ''"''${config.services.maubot.dataDir}/plugins"'';
|
||||
description = mdDoc ''
|
||||
The directory where uploaded new plugins should be stored.
|
||||
'';
|
||||
};
|
||||
load = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "./plugins" ];
|
||||
defaultText = literalExpression ''[ "''${config.services.maubot.dataDir}/plugins" ]'';
|
||||
description = mdDoc ''
|
||||
The directories from which plugins should be loaded. Duplicate plugin IDs will be moved to the trash.
|
||||
'';
|
||||
};
|
||||
trash = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = "./trash";
|
||||
defaultText = literalExpression ''"''${config.services.maubot.dataDir}/trash"'';
|
||||
description = mdDoc ''
|
||||
The directory where old plugin versions and conflicting plugins should be moved. Set to null to delete files immediately.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
plugin_databases = mkOption {
|
||||
description = mdDoc "Plugin database settings";
|
||||
default = { };
|
||||
type = submodule {
|
||||
options = {
|
||||
sqlite = mkOption {
|
||||
type = types.str;
|
||||
default = "./plugins";
|
||||
defaultText = literalExpression ''"''${config.services.maubot.dataDir}/plugins"'';
|
||||
description = mdDoc ''
|
||||
The directory where SQLite plugin databases should be stored.
|
||||
'';
|
||||
};
|
||||
|
||||
postgres = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = if isPostgresql cfg.settings.database then "default" else null;
|
||||
defaultText = literalExpression ''if isPostgresql config.services.maubot.settings.database then "default" else null'';
|
||||
description = mdDoc ''
|
||||
The connection URL for plugin database. See [example config](https://github.com/maubot/maubot/blob/master/maubot/example-config.yaml) for exact format.
|
||||
'';
|
||||
};
|
||||
|
||||
postgres_max_conns_per_plugin = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = 3;
|
||||
description = mdDoc ''
|
||||
Maximum number of connections per plugin instance.
|
||||
'';
|
||||
};
|
||||
|
||||
postgres_opts = mkOption {
|
||||
type = types.attrs;
|
||||
default = { };
|
||||
description = mdDoc ''
|
||||
Overrides for the default database_opts when using a non-default postgres connection URL.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
server = mkOption {
|
||||
default = { };
|
||||
description = mdDoc "Listener config";
|
||||
type = submodule {
|
||||
options = {
|
||||
hostname = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = mdDoc ''
|
||||
The IP to listen on
|
||||
'';
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 29316;
|
||||
description = mdDoc ''
|
||||
The port to listen on
|
||||
'';
|
||||
};
|
||||
public_url = mkOption {
|
||||
type = types.str;
|
||||
default = "http://${cfg.settings.server.hostname}:${toString cfg.settings.server.port}";
|
||||
defaultText = literalExpression ''"http://''${config.services.maubot.settings.server.hostname}:''${toString config.services.maubot.settings.server.port}"'';
|
||||
description = mdDoc ''
|
||||
Public base URL where the server is visible.
|
||||
'';
|
||||
};
|
||||
ui_base_path = mkOption {
|
||||
type = types.str;
|
||||
default = "/_matrix/maubot";
|
||||
description = mdDoc ''
|
||||
The base path for the UI.
|
||||
'';
|
||||
};
|
||||
plugin_base_path = mkOption {
|
||||
type = types.str;
|
||||
default = "${config.services.maubot.settings.server.ui_base_path}/plugin/";
|
||||
defaultText = literalExpression ''
|
||||
"''${config.services.maubot.settings.server.ui_base_path}/plugin/"
|
||||
'';
|
||||
description = mdDoc ''
|
||||
The base path for plugin endpoints. The instance ID will be appended directly.
|
||||
'';
|
||||
};
|
||||
override_resource_path = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = mdDoc ''
|
||||
Override path from where to load UI resources.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
homeservers = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
url = mkOption {
|
||||
type = types.str;
|
||||
description = mdDoc ''
|
||||
Client-server API URL
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
default = {
|
||||
"matrix.org" = {
|
||||
url = "https://matrix-client.matrix.org";
|
||||
};
|
||||
};
|
||||
description = mdDoc ''
|
||||
Known homeservers. This is required for the `mbc auth` command and also allows more convenient access from the management UI.
|
||||
If you want to specify registration secrets, pass this via extraConfigFile instead.
|
||||
'';
|
||||
};
|
||||
|
||||
admins = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = { root = ""; };
|
||||
description = mdDoc ''
|
||||
List of administrator users. Plaintext passwords will be bcrypted on startup. Set empty password
|
||||
to prevent normal login. Root is a special user that can't have a password and will always exist.
|
||||
'';
|
||||
};
|
||||
|
||||
api_features = mkOption {
|
||||
type = types.attrsOf bool;
|
||||
default = {
|
||||
login = true;
|
||||
plugin = true;
|
||||
plugin_upload = true;
|
||||
instance = true;
|
||||
instance_database = true;
|
||||
client = true;
|
||||
client_proxy = true;
|
||||
client_auth = true;
|
||||
dev_open = true;
|
||||
log = true;
|
||||
};
|
||||
description = mdDoc ''
|
||||
API feature switches.
|
||||
'';
|
||||
};
|
||||
|
||||
logging = mkOption {
|
||||
type = types.attrs;
|
||||
description = mdDoc ''
|
||||
Python logging configuration. See [section 16.7.2 of the Python
|
||||
documentation](https://docs.python.org/3.6/library/logging.config.html#configuration-dictionary-schema)
|
||||
for more info.
|
||||
'';
|
||||
default = {
|
||||
version = 1;
|
||||
formatters = {
|
||||
colored = {
|
||||
"()" = "maubot.lib.color_log.ColorFormatter";
|
||||
format = "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s";
|
||||
};
|
||||
normal = {
|
||||
format = "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s";
|
||||
};
|
||||
};
|
||||
handlers = {
|
||||
file = {
|
||||
class = "logging.handlers.RotatingFileHandler";
|
||||
formatter = "normal";
|
||||
filename = "./maubot.log";
|
||||
maxBytes = 10485760;
|
||||
backupCount = 10;
|
||||
};
|
||||
console = {
|
||||
class = "logging.StreamHandler";
|
||||
formatter = "colored";
|
||||
};
|
||||
};
|
||||
loggers = {
|
||||
maubot = {
|
||||
level = "DEBUG";
|
||||
};
|
||||
mau = {
|
||||
level = "DEBUG";
|
||||
};
|
||||
aiohttp = {
|
||||
level = "INFO";
|
||||
};
|
||||
};
|
||||
root = {
|
||||
level = "DEBUG";
|
||||
handlers = [ "file" "console" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
warnings = lib.optional (builtins.any (x: x.username != x.database) parsedLocalPostgresDBs) ''
|
||||
The Maubot database username doesn't match the database name! This means the user won't be automatically
|
||||
granted ownership of the database. Consider changing either the username or the database name.
|
||||
'';
|
||||
assertions = [
|
||||
{
|
||||
assertion = builtins.all (x: !lib.hasInfix ":" x.username) parsedPostgresDBs;
|
||||
message = ''
|
||||
Putting database passwords in your Nix config makes them world-readable. To securely put passwords
|
||||
in your Maubot config, change /var/lib/maubot/config.yaml after running Maubot at least once as
|
||||
described in the NixOS manual.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion = hasLocalPostgresDB -> config.services.postgresql.enable;
|
||||
message = ''
|
||||
Cannot deploy maubot with a configuration for a local postgresql database and a missing postgresql service.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
services.postgresql = lib.mkIf hasLocalPostgresDB {
|
||||
enable = true;
|
||||
ensureDatabases = map (x: x.database) parsedLocalPostgresDBs;
|
||||
ensureUsers = lib.flip map parsedLocalPostgresDBs (x: {
|
||||
name = x.username;
|
||||
ensureDBOwnership = lib.mkIf (x.username == x.database) true;
|
||||
});
|
||||
};
|
||||
|
||||
users.users.maubot = {
|
||||
group = "maubot";
|
||||
home = cfg.dataDir;
|
||||
# otherwise StateDirectory is enough
|
||||
createHome = lib.mkIf (cfg.dataDir != "/var/lib/maubot") true;
|
||||
isSystemUser = true;
|
||||
};
|
||||
|
||||
users.groups.maubot = { };
|
||||
|
||||
systemd.services.maubot = rec {
|
||||
description = "maubot - a plugin-based Matrix bot system written in Python";
|
||||
after = [ "network.target" ] ++ wants ++ lib.optional hasLocalPostgresDB "postgresql.service";
|
||||
# all plugins get automatically disabled if maubot starts before synapse
|
||||
wants = lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit;
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
preStart = ''
|
||||
if [ ! -f "${cfg.extraConfigFile}" ]; then
|
||||
echo "server:" > "${cfg.extraConfigFile}"
|
||||
echo " unshared_secret: $(head -c40 /dev/random | base32 | ${pkgs.gawk}/bin/awk '{print tolower($0)}')" > "${cfg.extraConfigFile}"
|
||||
chmod 640 "${cfg.extraConfigFile}"
|
||||
fi
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = "${finalPackage}/bin/maubot --config ${cfg.extraConfigFile}" + lib.optionalString (!cfg.configMutable) " --no-update";
|
||||
User = "maubot";
|
||||
Group = "maubot";
|
||||
Restart = "on-failure";
|
||||
RestartSec = "10s";
|
||||
StateDirectory = lib.mkIf (cfg.dataDir == "/var/lib/maubot") "maubot";
|
||||
WorkingDirectory = cfg.dataDir;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ chayleaf ];
|
||||
meta.doc = ./maubot.md;
|
||||
}
|
Loading…
Reference in New Issue
Block a user