From 859418b3774250180df497d875671f198a4d4f52 Mon Sep 17 00:00:00 2001 From: Ryan Lahfa Date: Fri, 20 Sep 2024 20:40:08 +0200 Subject: [PATCH] feat(chatops): init takumi Takumi means "artisan" (in the sense of "master") in Japanese. It's an accurate and efficient ChatOps for day-to-day operations of DGNum. Signed-off-by: Ryan Lahfa --- machines/compute01/_configuration.nix | 2 + machines/compute01/takumi.nix | 1 + modules/default.nix | 1 + modules/dgn-chatops/default.nix | 70 +++++++++++++++++++++++++++ modules/dgn-chatops/ircrobots.nix | 56 +++++++++++++++++++++ modules/dgn-chatops/takumi.py | 35 ++++++++++++++ 6 files changed, 165 insertions(+) create mode 100644 machines/compute01/takumi.nix create mode 100644 modules/dgn-chatops/default.nix create mode 100644 modules/dgn-chatops/ircrobots.nix create mode 100644 modules/dgn-chatops/takumi.py diff --git a/machines/compute01/_configuration.nix b/machines/compute01/_configuration.nix index 91fb3bb..0797105 100644 --- a/machines/compute01/_configuration.nix +++ b/machines/compute01/_configuration.nix @@ -5,11 +5,13 @@ lib.extra.mkConfig { # List of modules to enable "dgn-backups" "dgn-web" + "dgn-chatops" ]; enabledServices = [ # List of services to enable "arkheon" + "takumi" "signal-irc-bridge" "ds-fr" "grafana" diff --git a/machines/compute01/takumi.nix b/machines/compute01/takumi.nix new file mode 100644 index 0000000..1d8fdd2 --- /dev/null +++ b/machines/compute01/takumi.nix @@ -0,0 +1 @@ +_: { dgn-chatops.enable = true; } diff --git a/modules/default.nix b/modules/default.nix index 8e5d995..5d2f6a6 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -46,6 +46,7 @@ "dgn-acme" "dgn-backups" "dgn-console" + "dgn-chatops" "dgn-firewall" "dgn-hardware" "dgn-netbox-agent" diff --git a/modules/dgn-chatops/default.nix b/modules/dgn-chatops/default.nix new file mode 100644 index 0000000..2e86d47 --- /dev/null +++ b/modules/dgn-chatops/default.nix @@ -0,0 +1,70 @@ +# Copyright : +# - Ryan Lahfa 2024 +# +# Ce logiciel est un programme informatique servant à déployer des +# configurations de serveurs via NixOS. +# +# Ce logiciel est régi par la licence CeCILL soumise au droit français et +# respectant les principes de diffusion des logiciels libres. Vous pouvez +# utiliser, modifier et/ou redistribuer ce programme sous les conditions +# de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA +# sur le site "http://www.cecill.info". +# +# En contrepartie de l'accessibilité au code source et des droits de copie, +# de modification et de redistribution accordés par cette licence, il n'est +# offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons, +# seule une responsabilité restreinte pèse sur l'auteur du programme, le +# titulaire des droits patrimoniaux et les concédants successifs. +# +# A cet égard l'attention de l'utilisateur est attirée sur les risques +# associés au chargement, à l'utilisation, à la modification et/ou au +# développement et à la reproduction du logiciel par l'utilisateur étant +# donné sa spécificité de logiciel libre, qui peut le rendre complexe à +# manipuler et qui le réserve donc à des développeurs et des professionnels +# avertis possédant des connaissances informatiques approfondies. Les +# utilisateurs sont donc invités à charger et tester l'adéquation du +# logiciel à leurs besoins dans des conditions permettant d'assurer la +# sécurité de leurs systèmes et ou de leurs données et, plus généralement, +# à l'utiliser et l'exploiter dans les mêmes conditions de sécurité. +# +# Le fait que vous puissiez accéder à cet en-tête signifie que vous avez +# pris connaissance de la licence CeCILL, et que vous en avez accepté les +# termes. + +{ + config, + pkgs, + lib, + ... +}: +let + cfg = config.dgn-chatops; + inherit (lib) mkEnableOption mkIf; + python3 = pkgs.python311; + python3Pkgs = python3.pkgs; + ircrobots = python3Pkgs.callPackage ./ircrobots.nix { }; + ps = python3Pkgs.makePythonPath [ ircrobots ]; +in +{ + options.dgn-chatops = { + enable = mkEnableOption "the ChatOps layer"; + }; + + # Our ChatOps bot. + config = mkIf cfg.enable { + systemd.services.irc-takumi = { + description = "DGNum IRC automation bot, Takumi"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + environment = { + PYTHONPATH = ps; + }; + serviceConfig = { + RuntimeDirectory = "takumi"; + StateDirectory = "takumi"; + DynamicUser = true; + ExecStart = "${lib.getExe python3} ${./takumi.py}"; + }; + }; + }; +} diff --git a/modules/dgn-chatops/ircrobots.nix b/modules/dgn-chatops/ircrobots.nix new file mode 100644 index 0000000..c3531fc --- /dev/null +++ b/modules/dgn-chatops/ircrobots.nix @@ -0,0 +1,56 @@ +{ + lib, + buildPythonPackage, + fetchFromGitea, + pythonOlder, + anyio, + asyncio-rlock, + asyncio-throttle, + ircstates, + async-stagger, + async-timeout, + python, +}: + +buildPythonPackage rec { + pname = "ircrobots"; + version = "0.7.0"; + format = "setuptools"; + disabled = pythonOlder "3.7"; + + src = fetchFromGitea { + domain = "git.dgnum.eu"; + owner = "DGNum"; + repo = pname; + # No tag yet :(. + rev = "63aa84b40450bd534fc232eee10e8088028c9f6d"; + hash = "sha256-gXiPy6wjPEtc9v0cG0lb2QVXDlU5Q8ncxJO0lBm2RSE="; + }; + + postPatch = '' + # too specific pins https://github.com/jesopo/ircrobots/issues/3 + sed -iE 's/anyio.*/anyio/' requirements.txt + ''; + + propagatedBuildInputs = [ + anyio + asyncio-rlock + asyncio-throttle + ircstates + async-stagger + async-timeout + ]; + + checkPhase = '' + ${python.interpreter} -m unittest test + ''; + + pythonImportsCheck = [ "ircrobots" ]; + + meta = with lib; { + description = "Asynchronous bare-bones IRC bot framework for python3"; + license = licenses.mit; + homepage = "https://github.com/jesopo/ircrobots"; + maintainers = with maintainers; [ hexa ]; + }; +} diff --git a/modules/dgn-chatops/takumi.py b/modules/dgn-chatops/takumi.py new file mode 100644 index 0000000..4959517 --- /dev/null +++ b/modules/dgn-chatops/takumi.py @@ -0,0 +1,35 @@ +import asyncio + +from irctokens import build, Line +from ircrobots import Bot as BaseBot +from ircrobots import Server as BaseServer +from ircrobots import ConnectionParams + +SERVERS = [ + ("dgnum", "irc.dgnum.eu") +] + +class Server(BaseServer): + async def line_read(self, line: Line): + print(f"{self.name} < {line.format()}") + if line.command == "001": + print(f"connected to {self.isupport.network}") + await self.send(build("JOIN", ["#dgnum-bridge-test"])) + async def line_send(self, line: Line): + print(f"{self.name} > {line.format()}") + +class Bot(BaseBot): + def create_server(self, name: str): + return Server(self, name) + +async def main(): + bot = Bot() + for name, host in SERVERS: + # For IPv4-only connections. + params = ConnectionParams("Takumi", host, 6698) + await bot.add_server(name, params) + + await bot.run() + +if __name__ == "__main__": + asyncio.run(main())