From 6a12e3d0af30e18dc232e3c9218aa2e80c6a4454 Mon Sep 17 00:00:00 2001 From: "Brian (bex) Exelbierd" Date: Sun, 18 Jan 2026 22:58:17 +0100 Subject: [PATCH] Add --ignore-avatars and --ignore-stickers CLI flags Implement two new CLI flags to disable downloading avatars and sticker packs during message reception, following the existing pattern of --ignore-attachments and --ignore-stories flags. Changes: - Add --ignore-avatars and --ignore-stickers flags to ReceiveCommand, DaemonCommand, and JsonRpcDispatcherCommand - Extend ReceiveConfig record with ignoreAvatars and ignoreStickers fields - Add ReceiveHelper.getReceiveConfig() getter to expose config to other helpers - Gate avatar downloads in ProfileHelper (profile avatars), SyncHelper (contact avatars), and GroupHelper (group avatars for V1 and V2) - Gate sticker pack downloads in IncomingMessageHandler for both direct sticker messages and sync sticker pack operations - Update handleSignalServiceDataMessage and handleSyncMessage to pass full ReceiveConfig instead of individual boolean flags - Update man page (signal-cli.1.adoc) with flag documentation - Add entries to CHANGELOG.md When these flags are set, the respective content is not downloaded during message reception. Metadata (avatar paths, sticker pack IDs) is still stored, and existing FileNotFoundException handling will surface if content is later requested but wasn't downloaded. Fixes #1903 --- CHANGELOG.md | 5 +++++ .../signal/manager/api/ReceiveConfig.java | 2 +- .../signal/manager/helper/GroupHelper.java | 5 ++++- .../helper/IncomingMessageHandler.java | 20 ++++++++++--------- .../signal/manager/helper/ProfileHelper.java | 7 ++++++- .../signal/manager/helper/ReceiveHelper.java | 6 +++++- .../signal/manager/helper/SyncHelper.java | 2 +- man/signal-cli.1.adoc | 18 +++++++++++++++++ .../asamk/signal/commands/DaemonCommand.java | 6 ++++++ .../commands/JsonRpcDispatcherCommand.java | 6 ++++++ .../asamk/signal/commands/ReceiveCommand.java | 10 +++++++++- .../org/asamk/signal/util/CommandUtil.java | 4 +++- 12 files changed, 75 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b22bcebc..ab612b12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased] +### Added + +- Add --ignore-avatars flag to prevent downloading avatars +- Add --ignore-stickers flag to prevent downloading sticker packs + ## [0.13.22] - 2025-11-14 Requires libsignal-client version 0.86.1. diff --git a/lib/src/main/java/org/asamk/signal/manager/api/ReceiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/api/ReceiveConfig.java index e9108d12..f7b64e43 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/ReceiveConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/ReceiveConfig.java @@ -1,3 +1,3 @@ package org.asamk.signal.manager.api; -public record ReceiveConfig(boolean ignoreAttachments, boolean ignoreStories, boolean sendReadReceipts) {} +public record ReceiveConfig(boolean ignoreAttachments, boolean ignoreStories, boolean ignoreAvatars, boolean ignoreStickers, boolean sendReadReceipts) {} diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index f6bc9344..558501b2 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -108,6 +108,9 @@ public class GroupHelper { } public void downloadGroupAvatar(GroupIdV1 groupId, SignalServiceAttachment avatar) { + if (context.getReceiveHelper().getReceiveConfig().ignoreAvatars()) { + return; + } try { context.getAvatarStore() .storeGroupAvatar(groupId, @@ -505,7 +508,7 @@ public class GroupHelper { } storeProfileKeysFromMembers(decryptedGroup); final var avatar = decryptedGroup.avatar; - if (!avatar.isEmpty()) { + if (!avatar.isEmpty() && !context.getReceiveHelper().getReceiveConfig().ignoreAvatars()) { downloadGroupAvatar(groupInfoV2.getGroupId(), groupSecretParams, avatar); } groupInfoV2.setGroup(decryptedGroup); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java index 79d24b2a..28684d72 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java @@ -368,7 +368,7 @@ public final class IncomingMessageHandler { false, senderDeviceAddress, destination, - receiveConfig.ignoreAttachments())); + receiveConfig)); } if (content.getStoryMessage().isPresent()) { @@ -381,7 +381,7 @@ public final class IncomingMessageHandler { actions.addAll(handleSyncMessage(envelope, syncMessage, senderDeviceAddress, - receiveConfig.ignoreAttachments())); + receiveConfig)); } return actions; @@ -474,7 +474,7 @@ public final class IncomingMessageHandler { final SignalServiceEnvelope envelope, final SignalServiceSyncMessage syncMessage, final DeviceAddress sender, - final boolean ignoreAttachments + final ReceiveConfig receiveConfig ) { var actions = new ArrayList(); account.setMultiDevice(true); @@ -490,12 +490,12 @@ public final class IncomingMessageHandler { : new DeviceAddress(account.getRecipientResolver().resolveRecipient(destination), destination.getServiceId(), 0), - ignoreAttachments)); + receiveConfig)); } if (message.getStoryMessage().isPresent()) { actions.addAll(handleSignalServiceStoryMessage(message.getStoryMessage().get(), sender.recipientId(), - ignoreAttachments)); + receiveConfig.ignoreAttachments())); } } if (syncMessage.getRequest().isPresent() && account.isPrimaryDevice()) { @@ -575,7 +575,7 @@ public final class IncomingMessageHandler { final var sticker = context.getStickerHelper() .addOrUpdateStickerPack(stickerPackId, stickerPackKey, installed); - if (sticker != null && installed) { + if (sticker != null && installed && !receiveConfig.ignoreStickers()) { context.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId, sticker.packKey())); } } @@ -731,7 +731,7 @@ public final class IncomingMessageHandler { boolean isSync, DeviceAddress source, DeviceAddress destination, - boolean ignoreAttachments + ReceiveConfig receiveConfig ) { var actions = new ArrayList(); if (message.getGroupContext().isPresent()) { @@ -823,7 +823,7 @@ public final class IncomingMessageHandler { message.getExpireTimerVersion()); } } - if (!ignoreAttachments) { + if (!receiveConfig.ignoreAttachments()) { if (message.getAttachments().isPresent()) { for (var attachment : message.getAttachments().get()) { context.getAttachmentHelper().downloadAttachment(attachment); @@ -871,7 +871,9 @@ public final class IncomingMessageHandler { sticker = new StickerPack(stickerPackId, messageSticker.getPackKey()); account.getStickerStore().addStickerPack(sticker); } - context.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId, messageSticker.getPackKey())); + if (!receiveConfig.ignoreStickers()) { + context.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId, messageSticker.getPackKey())); + } } return actions; } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java index af16ed1d..507b0b14 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java @@ -278,7 +278,9 @@ public final class ProfileHelper { final SignalServiceProfile encryptedProfile ) { final var avatarPath = encryptedProfile.getAvatar(); - downloadProfileAvatar(recipientId, avatarPath, profileKey); + if (!context.getReceiveHelper().getReceiveConfig().ignoreAvatars()) { + downloadProfileAvatar(recipientId, avatarPath, profileKey); + } return ProfileUtils.decryptProfile(profileKey, encryptedProfile); } @@ -288,6 +290,9 @@ public final class ProfileHelper { final String avatarPath, final ProfileKey profileKey ) { + if (context.getReceiveHelper().getReceiveConfig().ignoreAvatars()) { + return; + } var profile = account.getProfileStore().getProfile(recipientId); if (profile == null || !Objects.equals(avatarPath, profile.getAvatarUrlPath())) { logger.trace("Downloading profile avatar for {}", recipientId); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java index add4cd82..917daccc 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java @@ -40,7 +40,7 @@ public class ReceiveHelper { private final SignalDependencies dependencies; private final Context context; - private ReceiveConfig receiveConfig = new ReceiveConfig(false, false, false); + private ReceiveConfig receiveConfig = new ReceiveConfig(false, false, false, false, false); private boolean hasCaughtUpWithOldMessages = false; private boolean isWaitingForMessage = false; private boolean shouldStop = false; @@ -58,6 +58,10 @@ public class ReceiveHelper { dependencies.setAllowStories(!receiveConfig.ignoreStories()); } + public ReceiveConfig getReceiveConfig() { + return receiveConfig; + } + public void setAuthenticationFailureListener(final Callable authenticationFailureListener) { this.authenticationFailureListener = authenticationFailureListener; } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java index 1911b8bb..74cbd6f4 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java @@ -378,7 +378,7 @@ public class SyncHelper { } account.getContactStore().storeContact(recipientId, builder.build()); - if (c.getAvatar().isPresent()) { + if (c.getAvatar().isPresent() && !context.getReceiveHelper().getReceiveConfig().ignoreAvatars()) { storeContactAvatar(c.getAvatar().get(), address); } } diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 0ae7a8bc..28a468fe 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -587,6 +587,12 @@ Don’t download attachments of received messages. *--ignore-stories*:: Don’t receive story messages from the server. +*--ignore-avatars*:: +Don't download avatars of received messages. + +*--ignore-stickers*:: +Don't download sticker packs of received messages. + *--send-read-receipts*:: Send read receipts for all incoming data messages (in addition to the default delivery receipts) @@ -950,6 +956,12 @@ Don’t download attachments of received messages. *--ignore-stories*:: Don’t receive story messages from the server. +*--ignore-avatars*:: +Don't download avatars of received messages. + +*--ignore-stickers*:: +Don't download sticker packs of received messages. + *--send-read-receipts*:: Send read receipts for all incoming data messages (in addition to the default delivery receipts) @@ -971,6 +983,12 @@ Don’t download attachments of received messages. *--ignore-stories*:: Don’t receive story messages from the server. +*--ignore-avatars*:: +Don't download avatars of received messages. + +*--ignore-stickers*:: +Don't download sticker packs of received messages. + *--send-read-receipts*:: Send read receipts for all incoming data messages (in addition to the default delivery receipts) diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 6790c65d..b0da5303 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -80,6 +80,12 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { subparser.addArgument("--ignore-stories") .help("Don’t receive story messages from the server.") .action(Arguments.storeTrue()); + subparser.addArgument("--ignore-avatars") + .help("Don't download avatars of received messages.") + .action(Arguments.storeTrue()); + subparser.addArgument("--ignore-stickers") + .help("Don't download sticker packs of received messages.") + .action(Arguments.storeTrue()); subparser.addArgument("--send-read-receipts") .help("Send read receipts for all incoming data messages (in addition to the default delivery receipts)") .action(Arguments.storeTrue()); diff --git a/src/main/java/org/asamk/signal/commands/JsonRpcDispatcherCommand.java b/src/main/java/org/asamk/signal/commands/JsonRpcDispatcherCommand.java index 5ce9daf4..fc6c9ae1 100644 --- a/src/main/java/org/asamk/signal/commands/JsonRpcDispatcherCommand.java +++ b/src/main/java/org/asamk/signal/commands/JsonRpcDispatcherCommand.java @@ -43,6 +43,12 @@ public class JsonRpcDispatcherCommand implements LocalCommand, MultiLocalCommand subparser.addArgument("--ignore-stories") .help("Don’t receive story messages from the server.") .action(Arguments.storeTrue()); + subparser.addArgument("--ignore-avatars") + .help("Don't download avatars of received messages.") + .action(Arguments.storeTrue()); + subparser.addArgument("--ignore-stickers") + .help("Don't download sticker packs of received messages.") + .action(Arguments.storeTrue()); subparser.addArgument("--send-read-receipts") .help("Send read receipts for all incoming data messages (in addition to the default delivery receipts)") .action(Arguments.storeTrue()); diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 37da35e1..6456cbb8 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -54,6 +54,12 @@ public class ReceiveCommand implements LocalCommand, JsonRpcSingleCommand new JsonReceiveMessageHandler(m, writer); diff --git a/src/main/java/org/asamk/signal/util/CommandUtil.java b/src/main/java/org/asamk/signal/util/CommandUtil.java index 61f5013e..1875429f 100644 --- a/src/main/java/org/asamk/signal/util/CommandUtil.java +++ b/src/main/java/org/asamk/signal/util/CommandUtil.java @@ -146,8 +146,10 @@ public class CommandUtil { public static ReceiveConfig getReceiveConfig(final Namespace ns) { final var ignoreAttachments = Boolean.TRUE.equals(ns.getBoolean("ignore-attachments")); final var ignoreStories = Boolean.TRUE.equals(ns.getBoolean("ignore-stories")); + final var ignoreAvatars = Boolean.TRUE.equals(ns.getBoolean("ignore-avatars")); + final var ignoreStickers = Boolean.TRUE.equals(ns.getBoolean("ignore-stickers")); final var sendReadReceipts = Boolean.TRUE.equals(ns.getBoolean("send-read-receipts")); - return new ReceiveConfig(ignoreAttachments, ignoreStories, sendReadReceipts); + return new ReceiveConfig(ignoreAttachments, ignoreStories, ignoreAvatars, ignoreStickers, sendReadReceipts); } }