From 8c6cc5dbde169e3a4b9e486d7968292e4856c5dc Mon Sep 17 00:00:00 2001 From: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com> Date: Sun, 19 Jul 2026 07:32:34 -0400 Subject: [PATCH] Don't show messages from Ignored nodes (#11068) * Honor Node Ignore messages * Screenless node fix --- src/MessageStore.cpp | 129 +++++++++++++++--- src/MessageStore.h | 8 +- src/graphics/Screen.cpp | 12 +- src/graphics/draw/MenuHandler.cpp | 11 +- src/graphics/draw/MessageRenderer.cpp | 2 + .../Notification/NotificationApplet.cpp | 5 +- .../User/AllMessage/AllMessageApplet.cpp | 4 +- .../niche/InkHUD/Applets/User/DM/DMApplet.cpp | 4 +- .../ThreadedMessage/ThreadedMessageApplet.cpp | 3 +- src/graphics/niche/InkHUD/Events.cpp | 8 +- src/graphics/niche/InkHUD/Persistence.cpp | 4 + src/mesh/MeshService.cpp | 4 +- src/mesh/NodeDB.cpp | 3 + src/modules/AdminModule.cpp | 8 ++ src/modules/TextMessageModule.cpp | 6 +- src/mqtt/MQTT.cpp | 29 ++++ 16 files changed, 195 insertions(+), 45 deletions(-) diff --git a/src/MessageStore.cpp b/src/MessageStore.cpp index 6332c0e82..6284ea007 100644 --- a/src/MessageStore.cpp +++ b/src/MessageStore.cpp @@ -7,6 +7,7 @@ #include "SafeFile.h" #include "gps/RTC.h" #include "memory/MemAudit.h" +#include #include // memcpy #ifndef MESSAGE_TEXT_POOL_SIZE @@ -65,6 +66,15 @@ static inline const char *getTextFromPool(uint16_t offset) return &g_messagePool[offset]; } +static inline bool isIgnoredNodeNum(uint32_t nodeNum) +{ + if (nodeNum == 0 || nodeNum == NODENUM_BROADCAST) + return false; + + const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum); + return nodeInfoLiteIsIgnored(node); +} + // Helper: assign a timestamp (RTC if available, else boot-relative) static inline void assignTimestamp(StoredMessage &sm) { @@ -163,9 +173,44 @@ static inline void autosaveTick(MessageStore *store) } #endif -// Add from incoming/outgoing packet -const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &packet) +bool MessageStore::shouldStorePacket(const meshtastic_MeshPacket &packet) const { + const uint32_t localNode = nodeDB->getNodeNum(); + const bool isDM = packet.to != 0 && packet.to != NODENUM_BROADCAST; + if (isDM) { + const bool outgoing = packet.from == 0 || packet.from == localNode; + const uint32_t peer = outgoing ? packet.to : packet.from; + return !isIgnoredNodeNum(peer); + } + + if (packet.from != 0 && packet.from != localNode) + return !isIgnoredNodeNum(packet.from); + + return true; +} + +bool MessageStore::isMessageVisible(const StoredMessage &msg) const +{ + const uint32_t localNode = nodeDB->getNodeNum(); + if (msg.type == MessageType::DM_TO_US) { + const uint32_t peer = (msg.sender == localNode) ? msg.dest : msg.sender; + return !isIgnoredNodeNum(peer); + } + + if (msg.sender != 0 && msg.sender != localNode) + return !isIgnoredNodeNum(msg.sender); + + return true; +} + +// Add from incoming/outgoing packet +const StoredMessage *MessageStore::tryAddFromPacket(const meshtastic_MeshPacket &packet) +{ + if (!shouldStorePacket(packet)) { + LOG_DEBUG("Drop store 0x%08x", packet.from); + return nullptr; + } + StoredMessage sm; assignTimestamp(sm); sm.channelIndex = packet.channel; @@ -196,7 +241,14 @@ const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &pa markMessageStoreUnsaved(); #endif - return liveMessages.back(); + return &liveMessages.back(); +} + +const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &packet) +{ + const StoredMessage *stored = tryAddFromPacket(packet); + assert(stored); + return *stored; } // Outgoing/manual message @@ -323,28 +375,33 @@ void MessageStore::loadFromFlash() resetMessagePool(); // reset pool when loading #ifdef FSCom - concurrency::LockGuard guard(spiLock); + { + concurrency::LockGuard guard(spiLock); - if (!FSCom.exists(filename.c_str())) - return; + if (!FSCom.exists(filename.c_str())) + return; - auto f = FSCom.open(filename.c_str(), FILE_O_READ); - if (!f) - return; + auto f = FSCom.open(filename.c_str(), FILE_O_READ); + if (!f) + return; - uint8_t count = 0; - f.readBytes(reinterpret_cast(&count), 1); - if (count > MAX_MESSAGES_SAVED) - count = MAX_MESSAGES_SAVED; + uint8_t count = 0; + f.readBytes(reinterpret_cast(&count), 1); + if (count > MAX_MESSAGES_SAVED) + count = MAX_MESSAGES_SAVED; - for (uint8_t i = 0; i < count; ++i) { - StoredMessage m; - if (!readMessageRecord(f, m)) - break; - liveMessages.push_back(m); + for (uint8_t i = 0; i < count; ++i) { + StoredMessage m; + if (!readMessageRecord(f, m)) + break; + liveMessages.push_back(m); + } + + f.close(); } - f.close(); + if (pruneHiddenMessages()) + saveToFlash(); #endif // Loading messages does not trigger an autosave g_messageStoreHasUnsavedChanges = false; @@ -406,6 +463,13 @@ template static void eraseAllMatches(std::dequegetNodeNum(); + auto pred = [&](const StoredMessage &m) { + if (m.sender == nodeNum) + return true; + if (m.type != MessageType::DM_TO_US) + return false; + return m.sender == local ? m.dest == nodeNum : m.sender == nodeNum; + }; + eraseAllMatches(liveMessages, pred); + saveToFlash(); +} + // Delete oldest message in a direct chat with a node void MessageStore::deleteOldestMessageWithPeer(uint32_t peer) { @@ -460,7 +538,7 @@ std::deque MessageStore::getChannelMessages(uint8_t channel) cons { std::deque result; for (const auto &m : liveMessages) { - if (m.type == MessageType::BROADCAST && m.channelIndex == channel) { + if (isMessageVisible(m) && m.type == MessageType::BROADCAST && m.channelIndex == channel) { result.push_back(m); } } @@ -471,13 +549,22 @@ std::deque MessageStore::getDirectMessages() const { std::deque result; for (const auto &m : liveMessages) { - if (m.type == MessageType::DM_TO_US) { + if (isMessageVisible(m) && m.type == MessageType::DM_TO_US) { result.push_back(m); } } return result; } +bool MessageStore::hasVisibleMessages() const +{ + for (const auto &m : liveMessages) { + if (isMessageVisible(m)) + return true; + } + return false; +} + // Upgrade boot-relative timestamps once RTC is valid // Only same-boot boot-relative messages are healed. // Persisted boot-relative messages from old boots stay ??? forever. diff --git a/src/MessageStore.h b/src/MessageStore.h index 040806197..724e10bc6 100644 --- a/src/MessageStore.h +++ b/src/MessageStore.h @@ -93,7 +93,7 @@ class MessageStore void addLiveMessage(StoredMessage &&msg); void addLiveMessage(const StoredMessage &msg); // convenience overload const std::deque &getLiveMessages() const { return liveMessages; } - + const StoredMessage *tryAddFromPacket(const meshtastic_MeshPacket &mp); // Incoming/outgoing -> RAM only // Add new messages from packets or manual input const StoredMessage &addFromPacket(const meshtastic_MeshPacket &mp); // Incoming/outgoing → RAM only void addFromString(uint32_t sender, uint8_t channelIndex, const std::string &text); // Manual add @@ -111,13 +111,16 @@ class MessageStore void deleteOldestMessageWithPeer(uint32_t peer); void deleteAllMessagesInChannel(uint8_t channel); void deleteAllMessagesWithPeer(uint32_t peer); - + void deleteAllMessagesFromNode(uint32_t nodeNum); // Unified accessor (for UI code, defaults to RAM buffer) const std::deque &getMessages() const { return liveMessages; } + bool hasVisibleMessages() const; // Helper filters for future use std::deque getChannelMessages(uint8_t channel) const; // Only broadcast messages on a channel std::deque getDirectMessages() const; // Only direct messages + bool shouldStorePacket(const meshtastic_MeshPacket &mp) const; + bool isMessageVisible(const StoredMessage &msg) const; // Upgrade boot-relative timestamps once RTC is valid void upgradeBootRelativeTimestamps(); @@ -129,6 +132,7 @@ class MessageStore static uint16_t storeText(const char *src, size_t len); private: + bool pruneHiddenMessages(); std::deque liveMessages; // Single in-RAM message buffer (also used for persistence) std::string filename; // Flash filename for persistence }; diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 73bf04b6d..95883038b 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -2110,7 +2110,7 @@ int Screen::handleInputEvent(const InputEvent *event) if (ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) { if (event->inputEvent == INPUT_BROKER_UP) { - if (messageStore.getMessages().empty()) { + if (!messageStore.hasVisibleMessages()) { cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST); } else { graphics::MessageRenderer::scrollUp(); @@ -2120,7 +2120,7 @@ int Screen::handleInputEvent(const InputEvent *event) } if (event->inputEvent == INPUT_BROKER_DOWN) { - if (messageStore.getMessages().empty()) { + if (!messageStore.hasVisibleMessages()) { cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST); } else { graphics::MessageRenderer::scrollDown(); @@ -2169,9 +2169,9 @@ int Screen::handleInputEvent(const InputEvent *event) if (!inputIntercepted) { #if defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2 bool handledEncoderScroll = false; - const bool isTextMessageFrame = (framesetInfo.positions.textMessage != 255 && - this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage && - !messageStore.getMessages().empty()); + const bool isTextMessageFrame = + (framesetInfo.positions.textMessage != 255 && + this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage && messageStore.hasVisibleMessages()); if (isTextMessageFrame) { if (event->inputEvent == INPUT_BROKER_UP_LONG) { graphics::MessageRenderer::nudgeScroll(-1); @@ -2254,7 +2254,7 @@ int Screen::handleInputEvent(const InputEvent *event) } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) { menuHandler::loraMenu(); } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) { - if (!messageStore.getMessages().empty()) { + if (messageStore.hasVisibleMessages()) { menuHandler::messageResponseMenu(); } else { if (currentResolution == ScreenResolution::UltraLow) { diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 349fe9c4a..ccb447399 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -70,12 +70,15 @@ const StoredMessage *getNewestMessageForActiveThread() const uint32_t peer = graphics::MessageRenderer::getThreadPeer(); const uint32_t localNode = nodeDB->getNodeNum(); - if (mode == graphics::MessageRenderer::ThreadMode::ALL) { - return &messages.back(); - } - for (auto it = messages.rbegin(); it != messages.rend(); ++it) { const StoredMessage &m = *it; + if (!messageStore.isMessageVisible(m)) { + continue; + } + + if (mode == graphics::MessageRenderer::ThreadMode::ALL) { + return &m; + } if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) { if (m.type == MessageType::BROADCAST && static_cast(m.channelIndex) == channel) { diff --git a/src/graphics/draw/MessageRenderer.cpp b/src/graphics/draw/MessageRenderer.cpp index bc5e027fb..6c202cb6d 100644 --- a/src/graphics/draw/MessageRenderer.cpp +++ b/src/graphics/draw/MessageRenderer.cpp @@ -403,6 +403,8 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 // Filter messages based on thread mode std::deque filtered; for (const auto &m : messageStore.getLiveMessages()) { + if (!messageStore.isMessageVisible(m)) + continue; bool include = false; switch (currentMode) { case ThreadMode::ALL: diff --git a/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp index 682c4de5e..e2090269e 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp @@ -234,7 +234,8 @@ std::string InkHUD::NotificationApplet::getNotificationText(uint16_t widthAvaila // Pick source of message const StoredMessage *message = msgIsBroadcast ? &inkhud->persistence->latestMessage.broadcast : &inkhud->persistence->latestMessage.dm; - + if (!message->sender || !messageStore.isMessageVisible(*message)) + return parse(text); // Find info about the sender meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(message->sender); @@ -270,4 +271,4 @@ std::string InkHUD::NotificationApplet::getNotificationText(uint16_t widthAvaila return parse(text); } -#endif \ No newline at end of file +#endif diff --git a/src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.cpp index ec266771e..740af32a7 100644 --- a/src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.cpp @@ -46,7 +46,7 @@ void InkHUD::AllMessageApplet::onRender(bool full) message = &latestMessage->dm; // Short circuit: no text message - if (!message->sender) { + if (!message->sender || !messageStore.isMessageVisible(*message)) { printAt(X(0.5), Y(0.5), "No Message", CENTER, MIDDLE); return; } @@ -138,4 +138,4 @@ bool InkHUD::AllMessageApplet::approveNotification(Notification &n) return true; } -#endif \ No newline at end of file +#endif diff --git a/src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.cpp index 4940e69bf..9f1471c90 100644 --- a/src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.cpp @@ -42,7 +42,7 @@ int InkHUD::DMApplet::onReceiveTextMessage(const meshtastic_MeshPacket *p) void InkHUD::DMApplet::onRender(bool full) { // Abort if no text message - if (!latestMessage->dm.sender) { + if (!latestMessage->dm.sender || !messageStore.isMessageVisible(latestMessage->dm)) { printAt(X(0.5), Y(0.5), "No DMs", CENTER, MIDDLE); return; } @@ -131,4 +131,4 @@ bool InkHUD::DMApplet::approveNotification(Notification &n) return true; } -#endif \ No newline at end of file +#endif diff --git a/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp index 31aeaa814..ef5902a24 100644 --- a/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp @@ -191,7 +191,8 @@ ProcessMessage InkHUD::ThreadedMessageApplet::handleReceived(const meshtastic_Me return ProcessMessage::CONTINUE; // Store in the global messageStore - this handles sender, timestamp, channel, text, and ack status - messageStore.addFromPacket(mp); + if (!messageStore.tryAddFromPacket(mp)) + return ProcessMessage::CONTINUE; // If this was an incoming message, suggest that our applet becomes foreground, if permitted if (getFrom(&mp) != nodeDB->getNodeNum()) diff --git a/src/graphics/niche/InkHUD/Events.cpp b/src/graphics/niche/InkHUD/Events.cpp index ddb4a57b7..1a5e438d9 100644 --- a/src/graphics/niche/InkHUD/Events.cpp +++ b/src/graphics/niche/InkHUD/Events.cpp @@ -534,13 +534,19 @@ int InkHUD::Events::onReceiveTextMessage(const meshtastic_MeshPacket *packet) if (getFrom(packet) == nodeDB->getNodeNum()) return 0; + if (!messageStore.shouldStorePacket(*packet)) + return 0; + bool isBroadcastMsg = isBroadcast(packet->to); inkhud->persistence->latestMessage.wasBroadcast = isBroadcastMsg; if (!isBroadcastMsg) { // DMs never pass through ThreadedMessageApplet, so add them to the global store here // so they survive reboots. Derive the latestMessage cache entry from the stored result. - inkhud->persistence->latestMessage.dm = messageStore.addFromPacket(*packet); + const StoredMessage *stored = messageStore.tryAddFromPacket(*packet); + if (!stored) + return 0; + inkhud->persistence->latestMessage.dm = *stored; } else { // Broadcasts are added to the global store by ThreadedMessageApplet::handleReceived(). // Here we only update the latestMessage cache used by AllMessageApplet / NotificationApplet. diff --git a/src/graphics/niche/InkHUD/Persistence.cpp b/src/graphics/niche/InkHUD/Persistence.cpp index 8a8140bb3..f588060b4 100644 --- a/src/graphics/niche/InkHUD/Persistence.cpp +++ b/src/graphics/niche/InkHUD/Persistence.cpp @@ -26,6 +26,10 @@ void InkHUD::Persistence::loadLatestMessage() int lastBroadcastPos = -1, lastDMPos = -1, pos = 0; for (const StoredMessage &m : messageStore.getLiveMessages()) { + if (!messageStore.isMessageVisible(m)) { + pos++; + continue; + } if (m.type == MessageType::BROADCAST) { latestMessage.broadcast = m; lastBroadcastPos = pos; diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index ce356d7cd..c7148c59f 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -257,8 +257,8 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p) p.to != NODENUM_BROADCAST && p.to != 0) // DM only { perhapsDecode(&p); - const StoredMessage &sm = messageStore.addFromPacket(p); - graphics::MessageRenderer::handleNewMessage(nullptr, sm, p); // notify UI + if (const StoredMessage *sm = messageStore.tryAddFromPacket(p)) + graphics::MessageRenderer::handleNewMessage(nullptr, *sm, p); // notify UI }) #if !MESHTASTIC_EXCLUDE_ADMIN // Note admin requests on their way out: AdminModule only accepts a response from a remote we diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 4c2879adc..a33f08389 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -3268,6 +3268,9 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact) LOG_WARN(PROTECTED_CAP_WARN_FMT, "ignore", contact.node_num, MAX_NUM_NODES - 2); nodeInfoLiteSetBit(info, NODEINFO_BITFIELD_IS_FAVORITE_MASK, false); eraseNodeSatellites(contact.node_num); +#if HAS_SCREEN || defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) + messageStore.deleteAllMessagesFromNode(contact.node_num); +#endif } else { /* Clients are sending add_contact before every text message DM (because clients may hold a larger node database with * public keys than the radio holds). However, we don't want to update last_heard just because we sent someone a DM! diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index e7990a5a5..bdfd4bb51 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -29,6 +29,7 @@ #include "Default.h" #include "MeshRadio.h" +#include "MessageStore.h" #include "RadioInterface.h" #include "TypeConversions.h" #include "mesh/RadioLibInterface.h" @@ -537,7 +538,14 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta if (node != NULL) { if (nodeDB->setProtectedFlag(node, NODEINFO_BITFIELD_IS_IGNORED_MASK, true)) { nodeDB->eraseNodeSatellites(node->num); +#if HAS_SCREEN || defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) + messageStore.deleteAllMessagesFromNode(node->num); +#endif saveChanges(SEGMENT_NODEDATABASE, false); +#if HAS_SCREEN + if (screen) + screen->setFrames(graphics::Screen::FOCUS_PRESERVE); +#endif } else if (mp.from == 0) { // local request from the phone - tell the user why it didn't take sendWarning(NodeDB::PROTECTED_CAP_WARN_FMT, "ignore", r->set_ignored_node, MAX_NUM_NODES - 2); } else { diff --git a/src/modules/TextMessageModule.cpp b/src/modules/TextMessageModule.cpp index 1d0912311..818e39a94 100644 --- a/src/modules/TextMessageModule.cpp +++ b/src/modules/TextMessageModule.cpp @@ -25,12 +25,14 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp // Guard against running in MeshtasticUI or with no screen if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { // Store in the central message history - const StoredMessage &sm = messageStore.addFromPacket(mp); + const StoredMessage *sm = messageStore.tryAddFromPacket(mp); + if (!sm) + return ProcessMessage::CONTINUE; // Pass message to renderer (banner + thread switching + scroll reset) // Use the global Screen singleton to retrieve the current OLED display auto *display = screen ? screen->getDisplayDevice() : nullptr; - graphics::MessageRenderer::handleNewMessage(display, sm, mp); + graphics::MessageRenderer::handleNewMessage(display, *sm, mp); }) // Only trigger screen wake if configuration allows it if (shouldWakeOnReceivedMessage()) { diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 907888bff..f8ab7fc07 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -55,6 +55,32 @@ static bool isConnected = false; static uint32_t lastPositionUnavailableWarning = 0; static const uint32_t POSITION_UNAVAILABLE_WARNING_INTERVAL_MS = 15000; // 15 seconds +inline bool shouldDropMqttDownlink(const meshtastic_MeshPacket &packet) +{ + if (is_in_repeated(config.lora.ignore_incoming, packet.from)) { + LOG_INFO("Drop MQTT ignored 0x%08x", packet.from); + return true; + } + + const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet.from); + if (nodeInfoLiteIsIgnored(node)) { + LOG_INFO("Drop MQTT node 0x%08x", packet.from); + return true; + } + + if (packet.from == NODENUM_BROADCAST) { + LOG_INFO("Drop MQTT broadcast src"); + return true; + } + + if (config.lora.ignore_mqtt) { + LOG_INFO("Drop MQTT ignore_mqtt"); + return true; + } + + return false; +} + inline void onReceiveProto(char *topic, byte *payload, size_t length) { const DecodedServiceEnvelope e(payload, length); @@ -122,6 +148,9 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length) p->which_payload_variant = e.packet->which_payload_variant; memcpy(&p->decoded, &e.packet->decoded, std::max(sizeof(p->decoded), sizeof(p->encrypted))); + if (shouldDropMqttDownlink(*p)) + return; + if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { if (moduleConfig.mqtt.encryption_enabled) { LOG_INFO("Ignore decoded message on MQTT, encryption is enabled");