with import <stockholm/lib>; { config, pkgs, ... }: let #for shared state directory stateDir = config.krebs.reaktor2.r.stateDir; generators = pkgs.reaktor2-plugins.generators; hooks = pkgs.reaktor2-plugins.hooks; commands = pkgs.reaktor2-plugins.commands; taskRcFile = builtins.toFile "taskrc" '' confirmation=no ''; task = name: { pattern = "^${name}-([a-z]+)(?::?\\s*(.*))?"; activate = "match"; command = 1; arguments = [2]; env.TASKDATA = "${stateDir}/${name}"; commands = { add.filename = pkgs.writeDash "${name}-task-add" '' ${pkgs.taskwarrior}/bin/task rc:${taskRcFile} add "$1" ''; list.filename = pkgs.writeDash "${name}-task-list" '' ${pkgs.taskwarrior}/bin/task rc:${taskRcFile} export \ | ${pkgs.jq}/bin/jq -r ' .[] | select(.id != 0) | "\(.id) \(.description)" ' ''; delete.filename = pkgs.writeDash "${name}-task-delete" '' ${pkgs.taskwarrior}/bin/task rc:${taskRcFile} delete "$1" ''; done.filename = pkgs.writeDash "${name}-task-done" '' ${pkgs.taskwarrior}/bin/task rc:${taskRcFile} done "$1" ''; }; }; systemPlugin = { plugin = "system"; config = { workdir = stateDir; hooks.JOIN = [ { activate = "always"; command = { filename = <stockholm/krebs/5pkgs/simple/Reaktor/scripts/tell-on_join.sh>; env = { PATH = makeBinPath [ pkgs.coreutils # XXX env, touch pkgs.jq # XXX sed pkgs.utillinux # XXX flock ]; state_file = "${stateDir}/tell.json"; }; }; } ]; hooks.PRIVMSG = [ { pattern = "^bier bal(an(ce)?)?$"; activate = "match"; command = { env = { state_file = "${stateDir}/ledger"; }; filename = pkgs.writeDash "bier-balance" '' ${pkgs.hledger}/bin/hledger -f $state_file bal -N -O csv \ | ${pkgs.coreutils}/bin/tail +2 \ | ${pkgs.miller}/bin/mlr --icsv --opprint cat \ | ${pkgs.gnused}/bin/sed 's/^/the_/' ''; }; } { pattern = ''^([\H-]*):?\s+([+-][1-9][0-9]*)\s+(\S+)$''; activate = "match"; arguments = [1 2 3]; command = { env = { # TODO; get state as argument state_file = "${stateDir}/ledger"; }; filename = pkgs.writeDash "ledger-add" '' set -x tonick=$1 amt=$2 unit=$3 printf '%s\n %s %d %s\n %s %d %s\n' "$(date -Id)" "$tonick" "$amt" "$unit" "$_from" "$(expr 0 - "''${amt#+}")" "$unit" >> $state_file ''; }; } hooks.sed (generators.command_hook { inherit (commands) dance random-emoji nixos-version; tell = { filename = <stockholm/krebs/5pkgs/simple/Reaktor/scripts/tell-on_privmsg.sh>; env = { PATH = makeBinPath [ pkgs.coreutils # XXX date, env pkgs.jq # XXX sed pkgs.utillinux # XXX flock ]; state_file = "${stateDir}/tell.txt"; }; }; }) (task "agenda") ]; }; }; in { users.users.reaktor2 = { uid = genid_uint31 "reaktor2"; home = stateDir; isSystemUser = true; extraGroups = [ "reaktor2" ]; }; users.groups.reaktor2 = {}; systemd.services.htgen-agenda.serviceConfig.StateDirectory = "reaktor2"; krebs.htgen.agenda = { port = 8009; user = { name = "reaktor2"; home = stateDir; }; script = ''. ${pkgs.writeDash "agenda" '' echo "$Method $Request_URI" >&2 case "$Method" in "GET") printf 'HTTP/1.1 200 OK\r\n' printf 'Connection: close\r\n' printf '\r\n' TASKDATA=/var/lib/reaktor2/agenda ${pkgs.taskwarrior}/bin/task rc:${taskRcFile} export exit ;; esac ''}''; }; services.nginx = { virtualHosts."agenda.r" = { serverAliases = [ "kri.r" ]; locations."= /index.html".extraConfig = '' alias ${pkgs.writeText "agenda.html" '' <!DOCTYPE html> <html> <head> <title>Agenda</title> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <style> html { font-family: monospace; } dt { float: left; clear: left; width: 30px; text-align: right; font-weight: bold; } dd { margin: 0 0 0 40px; padding: 0 0 0.5em 0; } .date { color: grey; font-style: italic; } </style> </head> <body> <dl id="agenda"></dl> <script> const urlSearchParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlSearchParams.entries()); if (params.hasOwnProperty("style")) { const cssUrls = params["style"].split(" ").filter((x) => x.length > 0); for (const cssUrl of cssUrls) fetch(cssUrl) .then((response) => response.text().then((css) => { const title = document.getElementsByTagName("head")[0]; const style = document.createElement("style"); style.appendChild(document.createTextNode(css)); title.appendChild(style); }) ) .catch(console.log); } fetch("/agenda.json") .then((response) => { response.json().then((agenda) => { const dl = document.getElementById("agenda"); for (const agendaItem of agenda) { if (agendaItem.status !== "pending") continue; // task warrior date format to ISO const entryDate = agendaItem.entry.replace( /(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z/, "$1-$2-$3T$4:$5:$6Z" ); const dt = document.createElement("dt"); dt.className = "id"; dt.appendChild(document.createTextNode(agendaItem.id.toString())); dl.appendChild(dt); const spanDate = document.createElement("span"); spanDate.className = "date"; spanDate.title = new Date(entryDate).toString(); spanDate.appendChild(document.createTextNode(entryDate)); const link = document.createElement("a"); link.href = "http://wiki.r/agenda/" + encodeURIComponent(agendaItem.description.replaceAll("/", "\u29F8")); // we use big solidus instead of slash because gollum will create directories link.appendChild(document.createTextNode(agendaItem.description)); const dd = document.createElement("dd"); dd.className = "description"; dd.appendChild(link); dd.appendChild(document.createTextNode(" ")); dd.appendChild(spanDate); dl.appendChild(dd); } }); }) .then((data) => console.log(data)); </script> </body> </html> ''}; ''; locations."/agenda.json".extraConfig = '' proxy_set_header Host $host; proxy_pass http://localhost:8009; ''; extraConfig = '' add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; ''; }; }; systemd.services.reaktor2-r.serviceConfig.DynamicUser = mkForce false; systemd.services.reaktor2-hackint.serviceConfig.DynamicUser = mkForce false; krebs.reaktor2 = { hackint = { hostname = "irc.hackint.org"; nick = "reaktor2|krebs"; plugins = [ { plugin = "register"; config = { channels = [ "#krebs" ]; }; } systemPlugin ]; username = "reaktor2"; port = "6697"; }; r = { nick = "reaktor2|krebs"; sendDelaySec = null; plugins = [ { plugin = "register"; config = { channels = [ "#noise" "#xxx" ]; }; } systemPlugin ]; username = "reaktor2"; }; }; }