feat(fava.accounts.minion): Add truelayer importer

Truelayer is a platform that connects to many European financial
institutions, including all the ones I care about! I plan to make this
generic over accounts in the future, but for now it's awesome to have a
proof-of-concept for importers working on a single account...

Change-Id: I1b51da2952a666316a68c4dae913b5cdfae2718a
Reviewed-on: https://git.clicks.codes/c/Infra/NixFiles/+/805
Tested-by: Skyler Grey <minion@clicks.codes>
Reviewed-by: Skyler Grey <minion@clicks.codes>
diff --git a/systems/x86_64-linux/teal/default.nix b/systems/x86_64-linux/teal/default.nix
index a97ccd8..0bd7bd4 100644
--- a/systems/x86_64-linux/teal/default.nix
+++ b/systems/x86_64-linux/teal/default.nix
@@ -176,7 +176,14 @@
       };
       fava = {
         enable = true;
+        extraPythonPackages = [
+          pkgs.clicks.beancount-autobean
+          pkgs.clicks.beancount-smart_importer
+        ];
         tailscaleAuth = true;
+        credentials = {
+          truelayer_client_secret = config.age.secrets."clicks.services.fava.credentials.truelayer_client_secret".path;
+        };
         accounts = {
           "clicks" = lib.home-manager.hm.dag.entryAnywhere {
             name = "Clicks Codes";
@@ -189,6 +196,34 @@
           "minion" = lib.home-manager.hm.dag.entryBetween [ "testing" ] [ "clicks" ] {
             name = "Skyler Grey";
             beancountExtraOptions.operating_currency = "GBP";
+            favaExtraOptions = {
+              invert-income-liabilities-equity = "true";
+              auto-reload = "true";
+              import-config = builtins.toString (pkgs.writeText "minion-imports.py" ''
+                import autobean.truelayer
+                from smart_importer import apply_hooks, PredictPayees, PredictPostings
+
+                import os
+                import pathlib
+
+                with open(pathlib.Path(os.environ["CREDENTIALS_DIRECTORY"]) / pathlib.Path("truelayer_client_secret")) as f:
+                  truelayer_client_secret = f.read().strip()
+
+                CONFIG = [
+                  apply_hooks(
+                    autobean.truelayer.Importer(
+                      "fava-228732",
+                      truelayer_client_secret
+                    ),
+                    [
+                      PredictPayees(),
+                      PredictPostings(),
+                    ]
+                  )
+                ]
+              '');
+              import-dirs = "/var/lib/private/fava/minion/";
+            };
           };
           "testing" = lib.home-manager.hm.dag.entryAfter [ "clicks" ] {
             name = "Test Data - May Be Wiped At Any Time";
@@ -250,8 +285,12 @@
 
   system.stateVersion = "24.05";
 
+  age.secrets."clicks.networking.tailscale.authKeyFile".rekeyFile = ./clicks.networking.tailscale.authKeyFile.age;
+
   age.secrets."clicks.security.acme.defaults.environmentFile".rekeyFile = ./clicks.security.acme.defaults.environmentFile.age;
 
+  age.secrets."clicks.services.fava.credentials.truelayer_client_secret".rekeyFile = ./clicks.services.fava.credentials.truelayer_client_secret.age;
+
   age.secrets."clicks.services.headscale.oidc.client_secret_path" = {
     rekeyFile = ./clicks.services.headscale.oidc.client_secret_path.age;
     group = "headscale";
@@ -272,6 +311,4 @@
     group = "headscale";
     mode = "440";
   };
-
-  age.secrets."clicks.networking.tailscale.authKeyFile".rekeyFile = ./clicks.networking.tailscale.authKeyFile.age;
 }