{
  description = "Skynet Discord Bot";

  inputs = {
    nixpkgs.url = "nixpkgs/nixos-unstable";
    naersk.url = "github:nix-community/naersk";
    utils.url = "github:numtide/flake-utils";
  };

  nixConfig = {
    extra-substituters = "https://nix-cache.skynet.ie/skynet-cache";
    extra-trusted-public-keys = "skynet-cache:zMFLzcRZPhUpjXUy8SF8Cf7KGAZwo98SKrzeXvdWABo=";
  };

  outputs = {
    self,
    nixpkgs,
    utils,
    naersk,
  }:
    utils.lib.eachDefaultSystem (
      system: let
        pkgs = (import nixpkgs) {inherit system;};
        naersk' = pkgs.callPackage naersk {};
        package_name = "skynet_discord_bot";
        desc = "Skynet Discord Bot";
        buildInputs = with pkgs; [
          openssl
          pkg-config
          rustfmt
        ];
      in rec {
        packages = {
          # For `nix build` & `nix run`:
          default = naersk'.buildPackage {
            pname = "${package_name}";
            src = ./.;
            buildInputs = buildInputs;
          };
          # Run `nix build .#fmt` to run tests
          fmt = naersk'.buildPackage {
            src = ./.;
            mode = "fmt";
            buildInputs = buildInputs;
          };
          # Run `nix build .#clippy` to lint code
          clippy = naersk'.buildPackage {
            src = ./.;
            mode = "clippy";
            buildInputs = buildInputs;
          };
        };

        defaultPackage = packages.default;

        # `nix run`
        apps."${package_name}" = utils.lib.mkApp {
          drv = packages."${package_name}";
        };

        defaultApp = apps."${package_name}";

        # `nix develop`
        devShell = pkgs.mkShell {
          nativeBuildInputs = with pkgs; [rustc cargo pkg-config openssl rustfmt];
        };

        nixosModule = {
          lib,
          pkgs,
          config,
          ...
        }:
          with lib; let
            cfg = config.services."${package_name}";
            # secret options are in the env file(s) loaded separately
            environment_config = {
              DATABASE_HOME = cfg.home;
              DATABASE = "database.db";
            };

            service_name = script: lib.strings.sanitizeDerivationName "${cfg.user}@${script}";

            # oneshot scripts to run
            serviceGenerator = mapAttrs' (script: time:
              nameValuePair (service_name script) {
                description = "Service for ${desc} ${script}";
                wantedBy = [];
                after = ["network-online.target"];
                environment = environment_config;

                serviceConfig = {
                  Type = "oneshot";
                  User = "${cfg.user}";
                  Group = "${cfg.user}";
                  ExecStart = "${self.defaultPackage."${system}"}/bin/${script}";
                  EnvironmentFile = [
                    "${cfg.env.discord}"
                    "${cfg.env.mail}"
                    "${cfg.env.wolves}"
                  ];
                };
              });

            # each timer will run the above service
            timerGenerator = mapAttrs' (script: time:
              nameValuePair (service_name script) {
                description = "Timer for ${desc} ${script}";

                wantedBy = ["timers.target"];
                partOf = ["${service_name script}.service"];
                timerConfig = {
                  OnCalendar = time;
                  Unit = "${service_name script}.service";
                  Persistent = true;
                };
              });

            # modify these
            scripts = {
              # every 20 min
              "update_data" = "*:0,10,20,30,40,50";
              # groups are updated every hour, offset from teh ldap
              "update_users" = "*:05:00";
              # Committee server has its own timer
              "update_committee" = "*:15:00";
              # minecraft stuff is updated at 5am
              "update_minecraft" = "5:10:00";
            };
          in {
            options.services."${package_name}" = {
              enable = mkEnableOption "enable ${package_name}";

              env = {
                discord = mkOption rec {
                  type = types.str;
                  description = "ENV file with DISCORD_TOKEN, DISCORD_TOKEN_MINECRAFT";
                };
                mail = mkOption rec {
                  type = types.str;
                  description = "ENV file with EMAIL_SMTP, EMAIL_USER, EMAIL_PASS";
                };
                wolves = mkOption rec {
                  type = types.str;
                  description = "Mail details, has WOLVES_URL";
                };
              };

              user = mkOption rec {
                type = types.str;
                default = "${package_name}";
                description = "The user to run the service";
              };

              home = mkOption rec {
                type = types.str;
                default = "/etc/${cfg.prefix}${package_name}";
                description = "The home for the user";
              };

              prefix = mkOption rec {
                type = types.str;
                default = "skynet_";
                example = default;
                description = "The prefix used to name service/folders";
              };
            };

            config = mkIf cfg.enable {
              users.groups."${cfg.user}" = {};

              users.users."${cfg.user}" = {
                createHome = true;
                isSystemUser = true;
                home = "${cfg.home}";
                group = "${cfg.user}";
              };

              systemd.services =
                {
                  # main service
                  "${package_name}" = {
                    description = desc;
                    wantedBy = ["multi-user.target"];
                    after = ["network-online.target"];
                    wants = [];
                    environment = environment_config;

                    serviceConfig = {
                      User = "${cfg.user}";
                      Group = "${cfg.user}";
                      Restart = "always";
                      ExecStart = "${self.defaultPackage."${system}"}/bin/${package_name}";
                      # can have multiple env files
                      EnvironmentFile = [
                        "${cfg.env.discord}"
                        "${cfg.env.mail}"
                        "${cfg.env.wolves}"
                      ];
                    };
                    restartTriggers = [
                      "${cfg.env.discord}"
                      "${cfg.env.mail}"
                      "${cfg.env.wolves}"
                    ];
                  };
                }
                // serviceGenerator scripts;

              # timers to run the above services
              systemd.timers = timerGenerator scripts;
            };
          };
      }
    );
}