From 266395ab4c1e9c672ff041f25e226f74fe6e2547 Mon Sep 17 00:00:00 2001 From: Nikaidou Haruki Date: Wed, 24 Dec 2025 02:51:06 +0900 Subject: [PATCH] Add Nix flake support with NixOS and Home Manager modules for Abaddon - Introduced `flake.nix` for flake-based configuration. - Created `flake.lock` to manage dependencies. - Added NixOS and Home Manager modules for system-wide and user-level configurations. - Updated `README.md` with installation instructions for Nix flake. - Implemented necessary Nix module files for configuration management. --- README.md | 105 ++++++++++++ flake.lock | 146 ++++++++++++++++ flake.nix | 173 +++++++++++++++++++ nix/hm-module.nix | 8 + nix/module.nix | 391 +++++++++++++++++++++++++++++++++++++++++++ nix/nixos-module.nix | 22 +++ 6 files changed, 845 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 nix/hm-module.nix create mode 100644 nix/module.nix create mode 100644 nix/nixos-module.nix diff --git a/README.md b/README.md index 44a00558..f981c08c 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,111 @@ the result of fundamental issues with Discord's thread implementation. 5. `make` 6. [Copy resources](#resources) +#### Nix Flakes: + +Abaddon provides a Nix flake with NixOS and Home Manager modules. + +
+ Show Nix installation options + +##### Using the NixOS Module (system-wide) + +Add abaddon to your flake inputs and import the NixOS module: + +```nix +# flake.nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + abaddon.url = "github:uowuo/abaddon"; + }; + + outputs = { self, nixpkgs, abaddon, ... }: { + nixosConfigurations.yourhost = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + abaddon.nixosModules.default + { + programs.abaddon.enable = true; + } + ]; + }; + }; +} +``` + +##### Using the Home Manager Module (per-user) + +For user-level configuration with Home Manager: + +```nix +# flake.nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + home-manager.url = "github:nix-community/home-manager"; + abaddon.url = "github:uowuo/abaddon"; + }; + + outputs = { self, nixpkgs, home-manager, abaddon, ... }: { + homeConfigurations.youruser = home-manager.lib.homeManagerConfiguration { + pkgs = nixpkgs.legacyPackages.x86_64-linux; + modules = [ + abaddon.homeManagerModules.default + { + programs.abaddon = { + enable = true; + settings = { + discord = { + autoconnect = true; + }; + gui = { + animations = true; + font_scale = 1.0; + }; + }; + # Optional: custom CSS + css = '' + .embed { + border-radius: 10px; + } + ''; + }; + } + ]; + }; + }; +} +``` + +##### Using the Overlay + +You can also use the overlay to add abaddon to your pkgs: + +```nix +{ + inputs.abaddon.url = "github:uowuo/abaddon"; + + outputs = { self, nixpkgs, abaddon, ... }: { + nixosConfigurations.yourhost = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [{ + nixpkgs.overlays = [ abaddon.overlays.default ]; + environment.systemPackages = [ pkgs.abaddon ]; + }]; + }; + }; +} +``` + +##### Run directly with Nix + +```bash +nix run github:uowuo/abaddon +``` + +
+ ### Downloads: Latest release version: https://github.com/uowuo/abaddon/releases/latest diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..72831580 --- /dev/null +++ b/flake.lock @@ -0,0 +1,146 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "ixwebsocket-src": { + "flake": false, + "locked": { + "lastModified": 1754436497, + "narHash": "sha256-RBeCuWHPOsOgLJV+0dhweXdd/TvLWXCoR/y2jjDWcug=", + "owner": "machinezone", + "repo": "ixwebsocket", + "rev": "150e3d83b5f6a2a47f456b79330a9afe87cd379c", + "type": "github" + }, + "original": { + "owner": "machinezone", + "repo": "ixwebsocket", + "type": "github" + } + }, + "keychain-src": { + "flake": false, + "locked": { + "lastModified": 1747647713, + "narHash": "sha256-qDpDfS5z8z+zKWYvBooHsP/fccR243oOfRdJwciNRZk=", + "owner": "hrantzsch", + "repo": "keychain", + "rev": "8846e78a1ea72a06a5da17b7f3493dd1c76c0a88", + "type": "github" + }, + "original": { + "owner": "hrantzsch", + "repo": "keychain", + "type": "github" + } + }, + "miniaudio-src": { + "flake": false, + "locked": { + "lastModified": 1757537205, + "narHash": "sha256-ZrfKw5a3AtIER2btCKWhuvygasNaHNf9EURf1Kv96Vc=", + "owner": "mackron", + "repo": "miniaudio", + "rev": "f40cf03f80cdb7e741d43e53b7e706e8c1394bcf", + "type": "github" + }, + "original": { + "owner": "mackron", + "repo": "miniaudio", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1766309749, + "narHash": "sha256-3xY8CZ4rSnQ0NqGhMKAy5vgC+2IVK0NoVEzDoOh4DA4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a6531044f6d0bef691ea18d4d4ce44d0daa6e816", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "qrcodegen-src": { + "flake": false, + "locked": { + "lastModified": 1737616857, + "narHash": "sha256-6SugPt0lp1Gz7nV23FLmsmpfzgFItkSw7jpGftsDPWc=", + "owner": "nayuki", + "repo": "QR-Code-generator", + "rev": "2c9044de6b049ca25cb3cd1649ed7e27aa055138", + "type": "github" + }, + "original": { + "owner": "nayuki", + "repo": "QR-Code-generator", + "type": "github" + } + }, + "rnnoise-src": { + "flake": false, + "locked": { + "lastModified": 1740268698, + "narHash": "sha256-fkSy7Sqnx0yLfGLciHf8PaptzFVzFAeRrhE4R5z8hSw=", + "owner": "xiph", + "repo": "rnnoise", + "rev": "70f1d256acd4b34a572f999a05c87bf00b67730d", + "type": "github" + }, + "original": { + "owner": "xiph", + "repo": "rnnoise", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "ixwebsocket-src": "ixwebsocket-src", + "keychain-src": "keychain-src", + "miniaudio-src": "miniaudio-src", + "nixpkgs": "nixpkgs", + "qrcodegen-src": "qrcodegen-src", + "rnnoise-src": "rnnoise-src" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..e3feee3c --- /dev/null +++ b/flake.nix @@ -0,0 +1,173 @@ +{ + description = "Abaddon - A Discord client built with GTK"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + + # Submodule sources + ixwebsocket-src = { + url = "github:machinezone/ixwebsocket"; + flake = false; + }; + miniaudio-src = { + url = "github:mackron/miniaudio"; + flake = false; + }; + keychain-src = { + url = "github:hrantzsch/keychain"; + flake = false; + }; + rnnoise-src = { + url = "github:xiph/rnnoise"; + flake = false; + }; + qrcodegen-src = { + url = "github:nayuki/QR-Code-generator"; + flake = false; + }; + }; + + outputs = { self, nixpkgs, flake-utils, ixwebsocket-src, miniaudio-src, keychain-src, rnnoise-src, qrcodegen-src }: + let + # System-independent outputs + systemIndependentOutputs = { + # NixOS module (system-wide installation) + nixosModules.default = { config, lib, pkgs, ... }: { + imports = [ ./nix/nixos-module.nix ]; + programs.abaddon.package = lib.mkDefault self.packages.${pkgs.system}.abaddon; + }; + nixosModules.abaddon = self.nixosModules.default; + + # Home Manager module (user-level configuration) + homeManagerModules.default = { config, lib, pkgs, ... }: { + imports = [ ./nix/module.nix ]; + programs.abaddon.package = lib.mkDefault self.packages.${pkgs.system}.abaddon; + }; + homeManagerModules.abaddon = self.homeManagerModules.default; + + # Overlay for adding abaddon to pkgs + overlays.default = final: prev: { + abaddon = self.packages.${prev.system}.abaddon; + }; + }; + + # Per-system outputs + perSystemOutputs = flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + + abaddon = pkgs.stdenv.mkDerivation rec { + pname = "abaddon"; + version = "0.2.1"; + + src = ./.; + + postPatch = '' + # Create subprojects directory and copy submodule sources + mkdir -p subprojects + + cp -r ${ixwebsocket-src} subprojects/ixwebsocket + chmod -R u+w subprojects/ixwebsocket + + cp -r ${miniaudio-src} subprojects/miniaudio + chmod -R u+w subprojects/miniaudio + + cp -r ${keychain-src} subprojects/keychain + chmod -R u+w subprojects/keychain + + cp -r ${rnnoise-src} subprojects/rnnoise + chmod -R u+w subprojects/rnnoise + + cp -r ${qrcodegen-src} subprojects/qrcodegen + chmod -R u+w subprojects/qrcodegen + ''; + + nativeBuildInputs = with pkgs; [ + cmake + pkg-config + wrapGAppsHook3 + ]; + + buildInputs = with pkgs; [ + # Required + gtkmm3 + nlohmann_json + curl + zlib + sqlite + spdlog + openssl + + # Optional but enabled by default + libhandy + libopus + libsodium + libsecret # Required for keychain + fontconfig + rnnoise # For voice activity detection + + # GTK/GLib ecosystem + glib + gtk3 + glibmm + cairomm + pangomm + atkmm + harfbuzz + sysprof # For sysprof-capture-4 pkg-config + + # X11 dependencies + xorg.libXdmcp + + # Icons + hicolor-icon-theme + adwaita-icon-theme + ]; + + cmakeFlags = [ + "-DCMAKE_BUILD_TYPE=Release" + "-DABADDON_RESOURCE_DIR=${placeholder "out"}/share/abaddon" + "-DUSE_LIBHANDY=ON" + "-DENABLE_VOICE=ON" + "-DUSE_KEYCHAIN=ON" + "-DENABLE_NOTIFICATION_SOUNDS=ON" + "-DENABLE_RNNOISE=ON" + "-DENABLE_QRCODE_LOGIN=ON" + ]; + + postInstall = '' + # Install desktop file and icon + install -Dm644 $src/res/desktop/io.github.uowuo.abaddon.desktop $out/share/applications/io.github.uowuo.abaddon.desktop + install -Dm644 $src/res/desktop/icon.svg $out/share/icons/hicolor/scalable/apps/io.github.uowuo.abaddon.svg + install -Dm644 $src/res/desktop/io.github.uowuo.abaddon.metainfo.xml $out/share/metainfo/io.github.uowuo.abaddon.metainfo.xml + ''; + + meta = with pkgs.lib; { + description = "Alternative Discord client made in C++ with GTK"; + homepage = "https://github.com/uowuo/abaddon"; + license = licenses.gpl3Plus; + platforms = platforms.linux; + mainProgram = "abaddon"; + }; + }; + in + { + packages = { + default = abaddon; + abaddon = abaddon; + }; + + devShells.default = pkgs.mkShell { + inputsFrom = [ abaddon ]; + packages = with pkgs; [ + gdb + valgrind + ]; + }; + } + ); + in + # Merge system-independent and per-system outputs + perSystemOutputs // systemIndependentOutputs; +} diff --git a/nix/hm-module.nix b/nix/hm-module.nix new file mode 100644 index 00000000..5ccf2baa --- /dev/null +++ b/nix/hm-module.nix @@ -0,0 +1,8 @@ +# Home Manager module wrapper +# This file imports the main module for use with Home Manager +{ config, lib, pkgs, ... }: + +{ + imports = [ ./module.nix ]; +} + diff --git a/nix/module.nix b/nix/module.nix new file mode 100644 index 00000000..89f9e560 --- /dev/null +++ b/nix/module.nix @@ -0,0 +1,391 @@ +# NixOS/Home Manager module for Abaddon Discord client +{ config, lib, pkgs, ... }: + +let + cfg = config.programs.abaddon; + # Merge user settings with empty defaults for each section + mergedSettings = lib.recursiveUpdate { + discord = { }; + gui = { }; + http = { }; + style = { }; + notifications = { }; + voice = { }; + } cfg.settings; + + # Generate the INI content + configContent = lib.generators.toINI {} mergedSettings; + + # Generate CSS content + cssContent = + if cfg.css != null then cfg.css + else if cfg.cssFile != null then builtins.readFile cfg.cssFile + else null; + +in +{ + options.programs.abaddon = { + enable = lib.mkEnableOption "Abaddon Discord client"; + + package = lib.mkOption { + type = lib.types.package; + description = "The abaddon package to use."; + }; + + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = lib.types.attrsOf (lib.types.attrsOf (lib.types.oneOf [ + lib.types.str + lib.types.bool + lib.types.int + lib.types.float + ])); + + options = { + discord = lib.mkOption { + type = lib.types.submodule { + freeformType = lib.types.attrsOf (lib.types.oneOf [ + lib.types.str + lib.types.bool + lib.types.int + ]); + + options = { + api_base = lib.mkOption { + type = lib.types.str; + default = "https://discord.com/api/v9"; + description = "Discord API base URL."; + }; + + gateway = lib.mkOption { + type = lib.types.str; + default = "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream"; + description = "Discord gateway WebSocket URL."; + }; + + memory_db = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Use in-memory database instead of file."; + }; + + prefetch = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Prefetch messages on startup."; + }; + + autoconnect = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Automatically connect on startup."; + }; + + keychain = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Store token in system keychain."; + }; + }; + }; + default = { }; + description = "Discord connection settings."; + }; + + gui = lib.mkOption { + type = lib.types.submodule { + freeformType = lib.types.attrsOf (lib.types.oneOf [ + lib.types.str + lib.types.bool + lib.types.int + lib.types.float + ]); + + options = { + css = lib.mkOption { + type = lib.types.str; + default = "main.css"; + description = "CSS file to use for styling (relative to resource dir)."; + }; + + animated_guild_hover_only = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Only animate guild icons on hover."; + }; + + animations = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Enable animations."; + }; + + custom_emojis = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Show custom emojis."; + }; + + owner_crown = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Show crown icon for server owner."; + }; + + save_state = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Save window state between sessions."; + }; + + stock_emojis = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Show stock emojis."; + }; + + unreads = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Show unread indicators."; + }; + + alt_menu = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Use alternative menu style."; + }; + + hide_to_tray = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Hide to system tray on close."; + }; + + show_deleted_indicator = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Show indicator for deleted messages."; + }; + + font_scale = lib.mkOption { + type = lib.types.float; + default = -1.0; + description = "Font scale factor (-1 for default)."; + }; + + folder_icon_only = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Show only icons for folders."; + }; + + classic_change_guild_on_open = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Change guild when opening in classic mode."; + }; + + image_embed_clamp_width = lib.mkOption { + type = lib.types.int; + default = 400; + description = "Maximum width for image embeds."; + }; + + image_embed_clamp_height = lib.mkOption { + type = lib.types.int; + default = 300; + description = "Maximum height for image embeds."; + }; + + classic_channels = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Use classic channel list style."; + }; + }; + }; + default = { }; + description = "GUI settings."; + }; + + http = lib.mkOption { + type = lib.types.submodule { + freeformType = lib.types.attrsOf (lib.types.oneOf [ + lib.types.str + lib.types.int + ]); + + options = { + concurrent = lib.mkOption { + type = lib.types.int; + default = 20; + description = "Number of concurrent HTTP connections."; + }; + + user_agent = lib.mkOption { + type = lib.types.str; + default = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"; + description = "User agent string for HTTP requests."; + }; + }; + }; + default = { }; + description = "HTTP client settings."; + }; + + style = lib.mkOption { + type = lib.types.submodule { + freeformType = lib.types.attrsOf lib.types.str; + + options = { + expandercolor = lib.mkOption { + type = lib.types.str; + default = "rgba(255, 83, 112, 0)"; + description = "Color for channel expanders."; + }; + + nsfwchannelcolor = lib.mkOption { + type = lib.types.str; + default = "#970d0d"; + description = "Color for NSFW channels."; + }; + + mentionbadgecolor = lib.mkOption { + type = lib.types.str; + default = "rgba(184, 37, 37, 0)"; + description = "Background color for mention badges."; + }; + + mentionbadgetextcolor = lib.mkOption { + type = lib.types.str; + default = "rgba(251, 251, 251, 0)"; + description = "Text color for mention badges."; + }; + + unreadcolor = lib.mkOption { + type = lib.types.str; + default = "rgba(255, 255, 255, 0)"; + description = "Color for unread indicator."; + }; + }; + }; + default = { }; + description = "Style/color settings."; + }; + + notifications = lib.mkOption { + type = lib.types.submodule { + freeformType = lib.types.attrsOf lib.types.bool; + + options = { + enabled = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Enable desktop notifications."; + }; + + playsound = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Play sound on notifications."; + }; + }; + }; + default = { }; + description = "Notification settings."; + }; + + voice = lib.mkOption { + type = lib.types.submodule { + freeformType = lib.types.attrsOf lib.types.str; + + options = { + vad = lib.mkOption { + type = lib.types.str; + default = "rnnoise"; + description = "Voice activity detection method (rnnoise or gate)."; + }; + + backends = lib.mkOption { + type = lib.types.str; + default = ""; + description = "Audio backends to use."; + }; + }; + }; + default = { }; + description = "Voice chat settings."; + }; + }; + }; + default = { }; + description = '' + Configuration for Abaddon. Will be written to ~/.config/abaddon/abaddon.ini. + + Example: + ```nix + programs.abaddon.settings = { + discord = { + autoconnect = true; + }; + gui = { + animations = false; + font_scale = 1.2; + }; + style = { + nsfwchannelcolor = "#ff0000"; + }; + }; + ``` + ''; + }; + + css = lib.mkOption { + type = lib.types.nullOr lib.types.lines; + default = null; + description = '' + Custom CSS to use for styling Abaddon. + This will be written to a file and referenced in the config. + + Example: + ```nix + programs.abaddon.css = ''' + .embed { + border-radius: 10px; + background-color: rgba(0, 0, 0, 0.2); + } + '''; + ``` + ''; + }; + + cssFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = '' + Path to a custom CSS file for styling Abaddon. + Alternative to the `css` option. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + home.packages = [ cfg.package ]; + + # Create the config directory and INI file + xdg.configFile."abaddon/abaddon.ini" = lib.mkIf (cfg.settings != { }) { + text = configContent; + }; + + # Create custom CSS file if provided + xdg.configFile."abaddon/css/custom.css" = lib.mkIf (cssContent != null) { + text = cssContent; + }; + + # Override CSS path in settings if custom CSS is provided + programs.abaddon.settings.gui.css = lib.mkIf (cssContent != null) + (lib.mkDefault "custom.css"); + }; +} + diff --git a/nix/nixos-module.nix b/nix/nixos-module.nix new file mode 100644 index 00000000..75cdfa38 --- /dev/null +++ b/nix/nixos-module.nix @@ -0,0 +1,22 @@ +# NixOS system-wide module for Abaddon +# This provides system-level installation (not per-user configuration) +{ config, lib, pkgs, ... }: + +let + cfg = config.programs.abaddon; +in +{ + options.programs.abaddon = { + enable = lib.mkEnableOption "Abaddon Discord client"; + + package = lib.mkOption { + type = lib.types.package; + description = "The abaddon package to use."; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + }; +} +