Loading pkgs/applications/networking/instant-messengers/signal-desktop/copy-noto-emoji.py 0 → 100644 +118 −0 Original line number Diff line number Diff line """Copy Noto Color Emoji PNGs into an extracted Signal ASAR archive. Signal loads small Apple emoji PNGs directly from `node_modules/emoji-datasource-apple/img/apple/64`, and downloads and caches large Apple emoji WebP files in `.proto` bundles on the fly. The latter are not a copyright concern for the Nixpkgs cache, but would result in inconsistent presentation between small and large emoji. We skip the complexity and buy some additional privacy by replacing the `emoji://jumbo?emoji=` URL prefix with a `file://` path to the copied PNGs inside the ASAR archive, and linking the `node_modules` PNG paths directly to them. """ import json import shutil import sys from pathlib import Path def signal_name_to_emoji(signal_emoji_name: str) -> str: r"""Return the emoji corresponding to a Signal emoji name. Signal emoji names are concatenations of UTF‐16 code units, represented in lowercase big‐endian hex padded to four digits. >>> signal_name_to_emoji("d83dde36200dd83cdf2bfe0f") '😶🌫️' >>> b"\xd8\x3d\xde\x36\x20\x0d\xd8\x3c\xdf\x2b\xfe\x0f".decode("utf-16-be") '😶🌫️' """ hex_bytes = zip(signal_emoji_name[::2], signal_emoji_name[1::2]) emoji_utf_16_be = bytes( int("".join(hex_pair), 16) for hex_pair in hex_bytes ) return emoji_utf_16_be.decode("utf-16-be") def emoji_to_noto_name(emoji: str) -> str: r"""Return the Noto emoji name of an emoji. Noto emoji names are underscore‐separated Unicode scalar values, represented in lowercase big‐endian hex padded to at least four digits. Any U+FE0F variant selectors are omitted. >>> emoji_to_noto_name("😶🌫️") '1f636_200d_1f32b' >>> emoji_to_noto_name("\U0001f636\u200d\U0001f32b\ufe0f") '1f636_200d_1f32b' """ return "_".join( f"{ord(scalar_value):04x}" for scalar_value in emoji if scalar_value != "\ufe0f" ) def emoji_to_emoji_data_name(emoji: str) -> str: r"""Return the npm emoji-data emoji name of an emoji. emoji-data emoji names are hyphen‐minus‐separated Unicode scalar values, represented in lowercase big‐endian hex padded to at least four digits. >>> emoji_to_emoji_data_name("😶🌫️") '1f636-200d-1f32b-fe0f' >>> emoji_to_emoji_data_name("\U0001f636\u200d\U0001f32b\ufe0f") '1f636-200d-1f32b-fe0f' """ return "-".join(f"{ord(scalar_value):04x}" for scalar_value in emoji) def _main() -> None: noto_png_path, asar_root = (Path(arg) for arg in sys.argv[1:]) asar_root = asar_root.absolute() out_path = asar_root / "images" / "nixpkgs-emoji" out_path.mkdir(parents=True) emoji_data_out_path = ( asar_root / "node_modules" / "emoji-datasource-apple" / "img" / "apple" / "64" ) emoji_data_out_path.mkdir(parents=True) jumbomoji_json_path = asar_root / "build" / "jumbomoji.json" with jumbomoji_json_path.open() as jumbomoji_json_file: jumbomoji_packs = json.load(jumbomoji_json_file) for signal_emoji_names in jumbomoji_packs.values(): for signal_emoji_name in signal_emoji_names: emoji = signal_name_to_emoji(signal_emoji_name) try: shutil.copy( noto_png_path / f"emoji_u{emoji_to_noto_name(emoji)}.png", out_path / emoji, ) except FileNotFoundError: print( f"Missing Noto emoji: {emoji} {signal_emoji_name}", file=sys.stderr, ) continue ( emoji_data_out_path / f"{emoji_to_emoji_data_name(emoji)}.png" ).symlink_to(out_path / emoji) print(out_path.relative_to(asar_root)) if __name__ == "__main__": _main() pkgs/applications/networking/instant-messengers/signal-desktop/generic.nix +95 −5 Original line number Diff line number Diff line { stdenv , lib , callPackage , fetchurl , autoPatchelfHook , noto-fonts-color-emoji , dpkg , asar , rsync , python3 , wrapGAppsHook3 , makeWrapper , nixosTests Loading Loading @@ -57,6 +62,27 @@ let inherit (stdenv) targetPlatform; ARCH = if targetPlatform.isAarch64 then "arm64" else "x64"; # Noto Color Emoji PNG files for emoji replacement; see below. noto-fonts-color-emoji-png = noto-fonts-color-emoji.overrideAttrs (prevAttrs: { pname = "noto-fonts-color-emoji-png"; # The build produces 136×128 PNGs by default for arcane font # reasons, but we want square PNGs. buildFlags = prevAttrs.buildFlags or [ ] ++ [ "BODY_DIMENSIONS=128x128" ]; makeTargets = [ "compressed" ]; installPhase = '' runHook preInstall mkdir -p $out/share mv build/compressed_pngs $out/share/noto-fonts-color-emoji-png python3 add_aliases.py --srcdir=$out/share/noto-fonts-color-emoji-png runHook postInstall ''; }); in stdenv.mkDerivation rec { inherit pname version; Loading @@ -71,11 +97,36 @@ stdenv.mkDerivation rec { src = fetchurl { inherit url hash; recursiveHash = true; downloadToTemp = true; nativeBuildInputs = [ dpkg asar ]; # Signal ships the Apple emoji set without a licence via an npm # package and upstream does not seem terribly interested in fixing # this; see: # # * <https://github.com/signalapp/Signal-Android/issues/5862> # * <https://whispersystems.discoursehosting.net/t/signal-is-likely-violating-apple-license-terms-by-using-apple-emoji-in-the-sticker-creator-and-android-and-desktop-apps/52883> # # We work around this by replacing it with the Noto Color Emoji # set, which is available under a FOSS licence and more likely to # be used on a NixOS machine anyway. The Apple emoji are removed # during `fetchurl` to ensure that the build doesn’t cache the # unlicensed emoji files, but the rest of the work is done in the # main derivation. postFetch = '' dpkg-deb -x $downloadedFile $out asar extract "$out/opt/${dir}/resources/app.asar" $out/asar-contents rm -r \ "$out/opt/${dir}/resources/app.asar"{,.unpacked} \ $out/asar-contents/node_modules/emoji-datasource-apple ''; }; nativeBuildInputs = [ rsync asar python3 autoPatchelfHook dpkg (wrapGAppsHook3.override { inherit makeWrapper; }) ]; Loading Loading @@ -127,11 +178,13 @@ stdenv.mkDerivation rec { wayland ]; unpackPhase = "dpkg-deb -x $src ."; dontBuild = true; dontConfigure = true; unpackPhase = '' rsync -a --chmod=+w $src/ . ''; installPhase = '' runHook preInstall Loading @@ -147,6 +200,30 @@ stdenv.mkDerivation rec { # Create required symlinks: ln -s libGLESv2.so "$out/lib/${dir}/libGLESv2.so.2" # Copy the Noto Color Emoji PNGs into the ASAR contents. See `src` # for the motivation, and the script for the technical details. emojiPrefix=$( python3 ${./copy-noto-emoji.py} \ ${noto-fonts-color-emoji-png}/share/noto-fonts-color-emoji-png \ asar-contents ) # Replace the URL used for fetching large versions of emoji with # the local path to our copied PNGs. substituteInPlace asar-contents/preload.bundle.js \ --replace-fail \ 'emoji://jumbo?emoji=' \ "file://$out/lib/${lib.escapeURL dir}/resources/app.asar/$emojiPrefix/" # `asar(1)` copies files from the corresponding `.unpacked` # directory when extracting, and will put them back in the modified # archive if you don’t specify them again when repacking. Signal # leaves their native `.node` libraries unpacked, so we match that. asar pack \ --unpack '*.node' \ asar-contents \ "$out/lib/${dir}/resources/app.asar" runHook postInstall ''; Loading Loading @@ -180,8 +257,21 @@ stdenv.mkDerivation rec { ''; homepage = "https://signal.org/"; changelog = "https://github.com/signalapp/Signal-Desktop/releases/tag/v${version}"; license = lib.licenses.agpl3Only; maintainers = with lib.maintainers; [ eclairevoyant mic92 equirosa urandom bkchr teutat3s ]; license = [ lib.licenses.agpl3Only # Various npm packages lib.licenses.free ]; maintainers = with lib.maintainers; [ eclairevoyant mic92 equirosa urandom bkchr teutat3s emily ]; mainProgram = pname; platforms = [ "x86_64-linux" "aarch64-linux" ]; sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ]; Loading pkgs/applications/networking/instant-messengers/signal-desktop/pyproject.toml 0 → 100644 +15 −0 Original line number Diff line number Diff line [tool.mypy] files = ["*.py"] strict = true [tool.ruff] line-length = 80 [tool.ruff.lint] select = ["ALL"] ignore = ["COM812", "D203", "D213", "ISC001", "T201"] allowed-confusables = ["‐"] [tool.ruff.format] docstring-code-format = true docstring-code-line-length = "dynamic" pkgs/applications/networking/instant-messengers/signal-desktop/signal-desktop-aarch64.nix +1 −1 Original line number Diff line number Diff line Loading @@ -4,5 +4,5 @@ callPackage ./generic.nix { } rec { dir = "Signal"; version = "7.19.0"; url = "https://github.com/0mniteck/Signal-Desktop-Mobian/raw/${version}/builds/release/signal-desktop_${version}_arm64.deb"; hash = "sha256-L5Wj1ofMR+QJezd4V6pAhkINLF6y9EB5VNFAIOZE5PU="; hash = "sha256-wyXVZUuY1TDGAVq7Gx9r/cuBuoMmSk9KQttTJlIN+k8="; } pkgs/applications/networking/instant-messengers/signal-desktop/signal-desktop-beta.nix +1 −1 Original line number Diff line number Diff line Loading @@ -4,5 +4,5 @@ callPackage ./generic.nix { } rec { dir = "Signal Beta"; version = "7.19.0-beta.1"; url = "https://updates.signal.org/desktop/apt/pool/s/signal-desktop-beta/signal-desktop-beta_${version}_amd64.deb"; hash = "sha256-kD08xke+HYhwAZG7jmU1ILo013556vNcvAFc/+9BTjg="; hash = "sha256-dIZvzJ45c5kL+2HEaKrtbck5Zz572pQAj3YTenzz6Zs="; } Loading
pkgs/applications/networking/instant-messengers/signal-desktop/copy-noto-emoji.py 0 → 100644 +118 −0 Original line number Diff line number Diff line """Copy Noto Color Emoji PNGs into an extracted Signal ASAR archive. Signal loads small Apple emoji PNGs directly from `node_modules/emoji-datasource-apple/img/apple/64`, and downloads and caches large Apple emoji WebP files in `.proto` bundles on the fly. The latter are not a copyright concern for the Nixpkgs cache, but would result in inconsistent presentation between small and large emoji. We skip the complexity and buy some additional privacy by replacing the `emoji://jumbo?emoji=` URL prefix with a `file://` path to the copied PNGs inside the ASAR archive, and linking the `node_modules` PNG paths directly to them. """ import json import shutil import sys from pathlib import Path def signal_name_to_emoji(signal_emoji_name: str) -> str: r"""Return the emoji corresponding to a Signal emoji name. Signal emoji names are concatenations of UTF‐16 code units, represented in lowercase big‐endian hex padded to four digits. >>> signal_name_to_emoji("d83dde36200dd83cdf2bfe0f") '😶🌫️' >>> b"\xd8\x3d\xde\x36\x20\x0d\xd8\x3c\xdf\x2b\xfe\x0f".decode("utf-16-be") '😶🌫️' """ hex_bytes = zip(signal_emoji_name[::2], signal_emoji_name[1::2]) emoji_utf_16_be = bytes( int("".join(hex_pair), 16) for hex_pair in hex_bytes ) return emoji_utf_16_be.decode("utf-16-be") def emoji_to_noto_name(emoji: str) -> str: r"""Return the Noto emoji name of an emoji. Noto emoji names are underscore‐separated Unicode scalar values, represented in lowercase big‐endian hex padded to at least four digits. Any U+FE0F variant selectors are omitted. >>> emoji_to_noto_name("😶🌫️") '1f636_200d_1f32b' >>> emoji_to_noto_name("\U0001f636\u200d\U0001f32b\ufe0f") '1f636_200d_1f32b' """ return "_".join( f"{ord(scalar_value):04x}" for scalar_value in emoji if scalar_value != "\ufe0f" ) def emoji_to_emoji_data_name(emoji: str) -> str: r"""Return the npm emoji-data emoji name of an emoji. emoji-data emoji names are hyphen‐minus‐separated Unicode scalar values, represented in lowercase big‐endian hex padded to at least four digits. >>> emoji_to_emoji_data_name("😶🌫️") '1f636-200d-1f32b-fe0f' >>> emoji_to_emoji_data_name("\U0001f636\u200d\U0001f32b\ufe0f") '1f636-200d-1f32b-fe0f' """ return "-".join(f"{ord(scalar_value):04x}" for scalar_value in emoji) def _main() -> None: noto_png_path, asar_root = (Path(arg) for arg in sys.argv[1:]) asar_root = asar_root.absolute() out_path = asar_root / "images" / "nixpkgs-emoji" out_path.mkdir(parents=True) emoji_data_out_path = ( asar_root / "node_modules" / "emoji-datasource-apple" / "img" / "apple" / "64" ) emoji_data_out_path.mkdir(parents=True) jumbomoji_json_path = asar_root / "build" / "jumbomoji.json" with jumbomoji_json_path.open() as jumbomoji_json_file: jumbomoji_packs = json.load(jumbomoji_json_file) for signal_emoji_names in jumbomoji_packs.values(): for signal_emoji_name in signal_emoji_names: emoji = signal_name_to_emoji(signal_emoji_name) try: shutil.copy( noto_png_path / f"emoji_u{emoji_to_noto_name(emoji)}.png", out_path / emoji, ) except FileNotFoundError: print( f"Missing Noto emoji: {emoji} {signal_emoji_name}", file=sys.stderr, ) continue ( emoji_data_out_path / f"{emoji_to_emoji_data_name(emoji)}.png" ).symlink_to(out_path / emoji) print(out_path.relative_to(asar_root)) if __name__ == "__main__": _main()
pkgs/applications/networking/instant-messengers/signal-desktop/generic.nix +95 −5 Original line number Diff line number Diff line { stdenv , lib , callPackage , fetchurl , autoPatchelfHook , noto-fonts-color-emoji , dpkg , asar , rsync , python3 , wrapGAppsHook3 , makeWrapper , nixosTests Loading Loading @@ -57,6 +62,27 @@ let inherit (stdenv) targetPlatform; ARCH = if targetPlatform.isAarch64 then "arm64" else "x64"; # Noto Color Emoji PNG files for emoji replacement; see below. noto-fonts-color-emoji-png = noto-fonts-color-emoji.overrideAttrs (prevAttrs: { pname = "noto-fonts-color-emoji-png"; # The build produces 136×128 PNGs by default for arcane font # reasons, but we want square PNGs. buildFlags = prevAttrs.buildFlags or [ ] ++ [ "BODY_DIMENSIONS=128x128" ]; makeTargets = [ "compressed" ]; installPhase = '' runHook preInstall mkdir -p $out/share mv build/compressed_pngs $out/share/noto-fonts-color-emoji-png python3 add_aliases.py --srcdir=$out/share/noto-fonts-color-emoji-png runHook postInstall ''; }); in stdenv.mkDerivation rec { inherit pname version; Loading @@ -71,11 +97,36 @@ stdenv.mkDerivation rec { src = fetchurl { inherit url hash; recursiveHash = true; downloadToTemp = true; nativeBuildInputs = [ dpkg asar ]; # Signal ships the Apple emoji set without a licence via an npm # package and upstream does not seem terribly interested in fixing # this; see: # # * <https://github.com/signalapp/Signal-Android/issues/5862> # * <https://whispersystems.discoursehosting.net/t/signal-is-likely-violating-apple-license-terms-by-using-apple-emoji-in-the-sticker-creator-and-android-and-desktop-apps/52883> # # We work around this by replacing it with the Noto Color Emoji # set, which is available under a FOSS licence and more likely to # be used on a NixOS machine anyway. The Apple emoji are removed # during `fetchurl` to ensure that the build doesn’t cache the # unlicensed emoji files, but the rest of the work is done in the # main derivation. postFetch = '' dpkg-deb -x $downloadedFile $out asar extract "$out/opt/${dir}/resources/app.asar" $out/asar-contents rm -r \ "$out/opt/${dir}/resources/app.asar"{,.unpacked} \ $out/asar-contents/node_modules/emoji-datasource-apple ''; }; nativeBuildInputs = [ rsync asar python3 autoPatchelfHook dpkg (wrapGAppsHook3.override { inherit makeWrapper; }) ]; Loading Loading @@ -127,11 +178,13 @@ stdenv.mkDerivation rec { wayland ]; unpackPhase = "dpkg-deb -x $src ."; dontBuild = true; dontConfigure = true; unpackPhase = '' rsync -a --chmod=+w $src/ . ''; installPhase = '' runHook preInstall Loading @@ -147,6 +200,30 @@ stdenv.mkDerivation rec { # Create required symlinks: ln -s libGLESv2.so "$out/lib/${dir}/libGLESv2.so.2" # Copy the Noto Color Emoji PNGs into the ASAR contents. See `src` # for the motivation, and the script for the technical details. emojiPrefix=$( python3 ${./copy-noto-emoji.py} \ ${noto-fonts-color-emoji-png}/share/noto-fonts-color-emoji-png \ asar-contents ) # Replace the URL used for fetching large versions of emoji with # the local path to our copied PNGs. substituteInPlace asar-contents/preload.bundle.js \ --replace-fail \ 'emoji://jumbo?emoji=' \ "file://$out/lib/${lib.escapeURL dir}/resources/app.asar/$emojiPrefix/" # `asar(1)` copies files from the corresponding `.unpacked` # directory when extracting, and will put them back in the modified # archive if you don’t specify them again when repacking. Signal # leaves their native `.node` libraries unpacked, so we match that. asar pack \ --unpack '*.node' \ asar-contents \ "$out/lib/${dir}/resources/app.asar" runHook postInstall ''; Loading Loading @@ -180,8 +257,21 @@ stdenv.mkDerivation rec { ''; homepage = "https://signal.org/"; changelog = "https://github.com/signalapp/Signal-Desktop/releases/tag/v${version}"; license = lib.licenses.agpl3Only; maintainers = with lib.maintainers; [ eclairevoyant mic92 equirosa urandom bkchr teutat3s ]; license = [ lib.licenses.agpl3Only # Various npm packages lib.licenses.free ]; maintainers = with lib.maintainers; [ eclairevoyant mic92 equirosa urandom bkchr teutat3s emily ]; mainProgram = pname; platforms = [ "x86_64-linux" "aarch64-linux" ]; sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ]; Loading
pkgs/applications/networking/instant-messengers/signal-desktop/pyproject.toml 0 → 100644 +15 −0 Original line number Diff line number Diff line [tool.mypy] files = ["*.py"] strict = true [tool.ruff] line-length = 80 [tool.ruff.lint] select = ["ALL"] ignore = ["COM812", "D203", "D213", "ISC001", "T201"] allowed-confusables = ["‐"] [tool.ruff.format] docstring-code-format = true docstring-code-line-length = "dynamic"
pkgs/applications/networking/instant-messengers/signal-desktop/signal-desktop-aarch64.nix +1 −1 Original line number Diff line number Diff line Loading @@ -4,5 +4,5 @@ callPackage ./generic.nix { } rec { dir = "Signal"; version = "7.19.0"; url = "https://github.com/0mniteck/Signal-Desktop-Mobian/raw/${version}/builds/release/signal-desktop_${version}_arm64.deb"; hash = "sha256-L5Wj1ofMR+QJezd4V6pAhkINLF6y9EB5VNFAIOZE5PU="; hash = "sha256-wyXVZUuY1TDGAVq7Gx9r/cuBuoMmSk9KQttTJlIN+k8="; }
pkgs/applications/networking/instant-messengers/signal-desktop/signal-desktop-beta.nix +1 −1 Original line number Diff line number Diff line Loading @@ -4,5 +4,5 @@ callPackage ./generic.nix { } rec { dir = "Signal Beta"; version = "7.19.0-beta.1"; url = "https://updates.signal.org/desktop/apt/pool/s/signal-desktop-beta/signal-desktop-beta_${version}_amd64.deb"; hash = "sha256-kD08xke+HYhwAZG7jmU1ILo013556vNcvAFc/+9BTjg="; hash = "sha256-dIZvzJ45c5kL+2HEaKrtbck5Zz572pQAj3YTenzz6Zs="; }