mirror of
https://github.com/Colorado-Mesh/meshcore-bot-firmware.git
synced 2026-08-11 08:10:29 +00:00
forge: add patch 14 — stabilize bot response tokens
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Fri, 15 May 2026 11:44:21 -0600
|
Date: Fri, 15 May 2026 11:44:21 -0600
|
||||||
Subject: [PATCH 01/13] Add companion radio firmware bot command parity
|
Subject: [PATCH 01/14] Add companion radio firmware bot command parity
|
||||||
|
|
||||||
---
|
---
|
||||||
.../companion_radio/BotCommandRegistry.cpp | 121 ++
|
.../companion_radio/BotCommandRegistry.cpp | 121 ++
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Fri, 15 May 2026 19:58:42 -0600
|
Date: Fri, 15 May 2026 19:58:42 -0600
|
||||||
Subject: [PATCH 02/13] Harden firmware bot response coordination
|
Subject: [PATCH 02/14] Harden firmware bot response coordination
|
||||||
|
|
||||||
---
|
---
|
||||||
examples/companion_radio/BotCommands.cpp | 2 +-
|
examples/companion_radio/BotCommands.cpp | 2 +-
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Fri, 15 May 2026 20:09:58 -0600
|
Date: Fri, 15 May 2026 20:09:58 -0600
|
||||||
Subject: [PATCH 03/13] Allow prefixless firmware bot DMs
|
Subject: [PATCH 03/14] Allow prefixless firmware bot DMs
|
||||||
|
|
||||||
---
|
---
|
||||||
examples/companion_radio/BotPolicy.cpp | 2 +-
|
examples/companion_radio/BotPolicy.cpp | 2 +-
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Sat, 16 May 2026 04:12:02 +0000
|
Date: Sat, 16 May 2026 04:12:02 +0000
|
||||||
Subject: [PATCH 04/13] Align firmware bot commands with upstream behavior
|
Subject: [PATCH 04/14] Align firmware bot commands with upstream behavior
|
||||||
|
|
||||||
---
|
---
|
||||||
.../companion_radio/BotCommandRegistry.cpp | 25 +-
|
.../companion_radio/BotCommandRegistry.cpp | 25 +-
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Sat, 16 May 2026 12:38:13 -0600
|
Date: Sat, 16 May 2026 12:38:13 -0600
|
||||||
Subject: [PATCH 05/13] Add hop-aware bot coordination, utility commands, and
|
Subject: [PATCH 05/14] Add hop-aware bot coordination, utility commands, and
|
||||||
bounded delays
|
bounded delays
|
||||||
|
|
||||||
Adds new utility commands (TIME, LORA, ID, NEIGHBORS), neighbor tracking,
|
Adds new utility commands (TIME, LORA, ID, NEIGHBORS), neighbor tracking,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Sat, 16 May 2026 18:58:28 -0600
|
Date: Sat, 16 May 2026 18:58:28 -0600
|
||||||
Subject: [PATCH 06/13] Mark bot adverts, speed up initial advert, default
|
Subject: [PATCH 06/14] Mark bot adverts, speed up initial advert, default
|
||||||
contacts auto-overwrite
|
contacts auto-overwrite
|
||||||
|
|
||||||
Three coordination/discoverability fixes:
|
Three coordination/discoverability fixes:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Sat, 16 May 2026 19:33:04 -0600
|
Date: Sat, 16 May 2026 19:33:04 -0600
|
||||||
Subject: [PATCH 07/13] Prefix bot responses with request token for inter-bot
|
Subject: [PATCH 07/14] Prefix bot responses with request token for inter-bot
|
||||||
suppression
|
suppression
|
||||||
|
|
||||||
Adds a 4-hex request token (low 16 bits of the request fingerprint) as a
|
Adds a 4-hex request token (low 16 bits of the request fingerprint) as a
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Sat, 16 May 2026 19:55:36 -0600
|
Date: Sat, 16 May 2026 19:55:36 -0600
|
||||||
Subject: [PATCH 08/13] Drop [MCBOT] advert suffix and auto-learn known-bot
|
Subject: [PATCH 08/14] Drop [MCBOT] advert suffix and auto-learn known-bot
|
||||||
path
|
path
|
||||||
|
|
||||||
Removes BOT_ADVERT_MARKER / writeBotAdvertName / isBotAdvertName from
|
Removes BOT_ADVERT_MARKER / writeBotAdvertName / isBotAdvertName from
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Sat, 16 May 2026 20:05:19 -0600
|
Date: Sat, 16 May 2026 20:05:19 -0600
|
||||||
Subject: [PATCH 09/13] Widen hop-step + drop "ack " prefix from test response
|
Subject: [PATCH 09/14] Widen hop-step + drop "ack " prefix from test response
|
||||||
|
|
||||||
Two changes:
|
Two changes:
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Sat, 16 May 2026 20:31:12 -0600
|
Date: Sat, 16 May 2026 20:31:12 -0600
|
||||||
Subject: [PATCH 10/13] Widen coordinator delays a tiny bit more
|
Subject: [PATCH 10/14] Widen coordinator delays a tiny bit more
|
||||||
|
|
||||||
~25% bump across the main coordination knobs:
|
~25% bump across the main coordination knobs:
|
||||||
- BOT_RESPONSE_DELAY_BASE_MILLIS 1200 -> 1500
|
- BOT_RESPONSE_DELAY_BASE_MILLIS 1200 -> 1500
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Sat, 16 May 2026 20:49:51 -0600
|
Date: Sat, 16 May 2026 20:49:51 -0600
|
||||||
Subject: [PATCH 11/13] Auto-advert on startup (initial flood at 5s instead of
|
Subject: [PATCH 11/14] Auto-advert on startup (initial flood at 5s instead of
|
||||||
120s)
|
120s)
|
||||||
|
|
||||||
BOT_PREFS_INITIAL_FLOOD_ADVERT_MILLIS 120000 -> 5000. The bot now sends
|
BOT_PREFS_INITIAL_FLOOD_ADVERT_MILLIS 120000 -> 5000. The bot now sends
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Sat, 16 May 2026 21:32:04 -0600
|
Date: Sat, 16 May 2026 21:32:04 -0600
|
||||||
Subject: [PATCH 12/13] Spell out hops/byte in test+path; drop channel name
|
Subject: [PATCH 12/14] Spell out hops/byte in test+path; drop channel name
|
||||||
from test ack
|
from test ack
|
||||||
|
|
||||||
Test response (writeAckResponse):
|
Test response (writeAckResponse):
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: cj-vana <cj@depth23.online>
|
From: cj-vana <cj@depth23.online>
|
||||||
Date: Sat, 16 May 2026 22:18:02 -0600
|
Date: Sat, 16 May 2026 22:18:02 -0600
|
||||||
Subject: [PATCH 13/13] =?UTF-8?q?Hop=20delay=20grows=20quadratically=20?=
|
Subject: [PATCH 13/14] =?UTF-8?q?Hop=20delay=20grows=20quadratically=20?=
|
||||||
=?UTF-8?q?=E2=80=94=20wider=20gaps=20at=20higher=20hop=20counts?=
|
=?UTF-8?q?=E2=80=94=20wider=20gaps=20at=20higher=20hop=20counts?=
|
||||||
MIME-Version: 1.0
|
MIME-Version: 1.0
|
||||||
Content-Type: text/plain; charset=UTF-8
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
|||||||
@@ -0,0 +1,342 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: cj-vana <cj@depth23.online>
|
||||||
|
Date: Sun, 17 May 2026 12:06:00 -0600
|
||||||
|
Subject: [PATCH 14/14] Stabilize firmware bot response tokens
|
||||||
|
|
||||||
|
---
|
||||||
|
examples/companion_radio/FirmwareBot.cpp | 52 ++++++++++++------
|
||||||
|
examples/companion_radio/FirmwareBot.h | 1 +
|
||||||
|
examples/companion_radio/MyMesh.cpp | 67 +++++++++++++++++++++---
|
||||||
|
examples/companion_radio/MyMesh.h | 1 +
|
||||||
|
4 files changed, 96 insertions(+), 25 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/examples/companion_radio/FirmwareBot.cpp b/examples/companion_radio/FirmwareBot.cpp
|
||||||
|
index afd44d41..21312c55 100644
|
||||||
|
--- a/examples/companion_radio/FirmwareBot.cpp
|
||||||
|
+++ b/examples/companion_radio/FirmwareBot.cpp
|
||||||
|
@@ -366,8 +366,17 @@ BotWriteResult writeAckResponse(const BotMessage& message, const BotCommand& com
|
||||||
|
}
|
||||||
|
|
||||||
|
BotFingerprint fingerprintFor(const BotMessage& message) {
|
||||||
|
+ return fingerprintFor(message, NULL, 0);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+BotFingerprint fingerprintFor(const BotMessage& message, const uint8_t* channel_id, size_t channel_id_len) {
|
||||||
|
uint64_t hash = 1469598103934665603ULL;
|
||||||
|
- hash = fnv1aUpdateChannel(hash, message);
|
||||||
|
+ if (message.channel_kind == BOT_CHANNEL_DM || !channel_id || channel_id_len == 0) {
|
||||||
|
+ hash = fnv1aUpdateChannel(hash, message);
|
||||||
|
+ } else {
|
||||||
|
+ hash = fnv1aUpdate(hash, 0xC7);
|
||||||
|
+ hash = fnv1aUpdateBytes(hash, channel_id, channel_id_len);
|
||||||
|
+ }
|
||||||
|
hash = fnv1aUpdateBytes(hash, message.sender_key_prefix, sizeof(message.sender_key_prefix));
|
||||||
|
hash = fnv1aUpdateTextLower(hash, message.sender_name, boundedStrLen(message.sender_name, sizeof(message.sender_name)));
|
||||||
|
hash = fnv1aUpdateU32(hash, message.sender_timestamp);
|
||||||
|
@@ -381,6 +390,24 @@ BotFingerprint fingerprintFor(const BotMessage& message) {
|
||||||
|
return fingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static int hexNibble(char c);
|
||||||
|
+
|
||||||
|
+bool parseRequestTokenPrefix(const char* text, size_t text_len, uint16_t* token, size_t* prefix_len) {
|
||||||
|
+ if (token) *token = 0;
|
||||||
|
+ if (prefix_len) *prefix_len = 0;
|
||||||
|
+ if (!text || text_len < 7) return false;
|
||||||
|
+ if (text[0] != '[' || text[5] != ']' || text[6] != ' ') return false;
|
||||||
|
+ uint16_t value = 0;
|
||||||
|
+ for (int i = 0; i < 4; i++) {
|
||||||
|
+ int n = hexNibble(text[1 + i]);
|
||||||
|
+ if (n < 0) return false;
|
||||||
|
+ value = (uint16_t)((value << 4) | (uint16_t)n);
|
||||||
|
+ }
|
||||||
|
+ if (token) *token = value;
|
||||||
|
+ if (prefix_len) *prefix_len = 7;
|
||||||
|
+ return true;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
BotFingerprint responseFingerprintFor(const BotMessage& message, const char* response_text, size_t response_text_len) {
|
||||||
|
uint64_t hash = 1469598103934665603ULL;
|
||||||
|
hash = fnv1aUpdateChannel(hash, message);
|
||||||
|
@@ -389,6 +416,13 @@ BotFingerprint responseFingerprintFor(const BotMessage& message, const char* res
|
||||||
|
hash = fnv1aUpdateBytes(hash, message.sender_key_prefix, message.sender_key_prefix_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ uint16_t token = 0;
|
||||||
|
+ size_t prefix_len = 0;
|
||||||
|
+ if (parseRequestTokenPrefix(response_text, response_text_len, &token, &prefix_len)) {
|
||||||
|
+ response_text += prefix_len;
|
||||||
|
+ response_text_len -= prefix_len;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
char normalized[BOT_MAX_RESPONSE_LEN + 1];
|
||||||
|
size_t normalized_len = 0;
|
||||||
|
normalizeText(response_text, response_text_len, normalized, sizeof(normalized), &normalized_len);
|
||||||
|
@@ -423,22 +457,6 @@ static int hexNibble(char c) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
-bool parseRequestTokenPrefix(const char* text, size_t text_len, uint16_t* token, size_t* prefix_len) {
|
||||||
|
- if (token) *token = 0;
|
||||||
|
- if (prefix_len) *prefix_len = 0;
|
||||||
|
- if (!text || text_len < 7) return false; // "[XXXX] " = 7 chars minimum
|
||||||
|
- if (text[0] != '[' || text[5] != ']' || text[6] != ' ') return false;
|
||||||
|
- uint16_t value = 0;
|
||||||
|
- for (int i = 0; i < 4; i++) {
|
||||||
|
- int n = hexNibble(text[1 + i]);
|
||||||
|
- if (n < 0) return false;
|
||||||
|
- value = (uint16_t)((value << 4) | (uint16_t)n);
|
||||||
|
- }
|
||||||
|
- if (token) *token = value;
|
||||||
|
- if (prefix_len) *prefix_len = 7;
|
||||||
|
- return true;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
BotWriteResult prependRequestToken(BotFingerprint request_fingerprint, char* text, size_t text_len, size_t buf_len,
|
||||||
|
size_t* new_len) {
|
||||||
|
if (new_len) *new_len = text_len;
|
||||||
|
diff --git a/examples/companion_radio/FirmwareBot.h b/examples/companion_radio/FirmwareBot.h
|
||||||
|
index 708432a2..f1726712 100644
|
||||||
|
--- a/examples/companion_radio/FirmwareBot.h
|
||||||
|
+++ b/examples/companion_radio/FirmwareBot.h
|
||||||
|
@@ -17,6 +17,7 @@ BotWriteResult writeResponseForChannel(BotChannelKind channel_kind, bool allow_p
|
||||||
|
BotWriteResult writeAckResponse(const BotMessage& message, const BotCommand& command, char* output, size_t output_len,
|
||||||
|
size_t* written);
|
||||||
|
BotFingerprint fingerprintFor(const BotMessage& message);
|
||||||
|
+BotFingerprint fingerprintFor(const BotMessage& message, const uint8_t* channel_id, size_t channel_id_len);
|
||||||
|
BotFingerprint responseFingerprintFor(const BotMessage& message, const char* response_text, size_t response_text_len);
|
||||||
|
uint16_t requestToken(BotFingerprint request_fingerprint);
|
||||||
|
void formatRequestToken(uint16_t token, char out[5]);
|
||||||
|
diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp
|
||||||
|
index 9121d42c..1a7ddb7a 100644
|
||||||
|
--- a/examples/companion_radio/MyMesh.cpp
|
||||||
|
+++ b/examples/companion_radio/MyMesh.cpp
|
||||||
|
@@ -121,6 +121,16 @@
|
||||||
|
#define LAZY_CONTACTS_WRITE_DELAY 5000
|
||||||
|
|
||||||
|
#if CMESH_BOT_ENABLED
|
||||||
|
+#ifndef CMESH_BOT_DEBUG
|
||||||
|
+#define CMESH_BOT_DEBUG 0
|
||||||
|
+#endif
|
||||||
|
+#if CMESH_BOT_DEBUG
|
||||||
|
+#define BOT_DEBUG_PRINTF(...) Serial.printf(__VA_ARGS__)
|
||||||
|
+#define BOT_DEBUG_PRINTLN(value) Serial.println(value)
|
||||||
|
+#else
|
||||||
|
+#define BOT_DEBUG_PRINTF(...)
|
||||||
|
+#define BOT_DEBUG_PRINTLN(value)
|
||||||
|
+#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg=="
|
||||||
|
@@ -1055,6 +1065,7 @@ bool MyMesh::handleBotCLI(const char *args) {
|
||||||
|
|
||||||
|
void MyMesh::observeBotDirectMessage(const ContactInfo &from, uint32_t sender_timestamp, const uint8_t *sender_prefix,
|
||||||
|
size_t sender_prefix_len, const char *text, const mesh::Packet *packet) {
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: DM rx from=%s text=%.40s enabled=%d\r\n", from.name, text, bot_prefs.enabled);
|
||||||
|
BotMessage message;
|
||||||
|
memset(&message, 0, sizeof(message));
|
||||||
|
message.channel_kind = BotPolicy::classifyChannel(NULL, 0, true, bot_prefs);
|
||||||
|
@@ -1090,6 +1101,7 @@ void MyMesh::observeBotDirectMessage(const ContactInfo &from, uint32_t sender_ti
|
||||||
|
|
||||||
|
void MyMesh::observeBotChannelMessage(uint8_t channel_idx, const char *channel_name, const char *text,
|
||||||
|
uint32_t sender_timestamp, const mesh::Packet *packet) {
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: CH rx ch=%s text=%.40s enabled=%d\r\n", channel_name ? channel_name : "?", text, bot_prefs.enabled);
|
||||||
|
BotMessage message;
|
||||||
|
memset(&message, 0, sizeof(message));
|
||||||
|
size_t channel_len = botBoundedStrLen(channel_name, BOT_MAX_CHANNEL_NAME_LEN);
|
||||||
|
@@ -1429,7 +1441,7 @@ bool MyMesh::dispatchBotTraceDirectLink(const BotMessage &message, const Contact
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
- BotFingerprint request_fingerprint = FirmwareBot::fingerprintFor(message);
|
||||||
|
+ BotFingerprint request_fingerprint = botRequestFingerprintFor(message, channel_idx);
|
||||||
|
BotFingerprint response_fingerprint =
|
||||||
|
FirmwareBot::responseFingerprintFor(message, final_response, final_response_len);
|
||||||
|
BotFingerprint fingerprint;
|
||||||
|
@@ -1503,7 +1515,7 @@ bool MyMesh::handleBotTraceCommand(const BotMessage &message, const ContactInfo
|
||||||
|
}
|
||||||
|
if (!have_path || !botTracePathShapeValid(path_len, flags)) return false;
|
||||||
|
|
||||||
|
- BotFingerprint request_fingerprint = FirmwareBot::fingerprintFor(message);
|
||||||
|
+ BotFingerprint request_fingerprint = botRequestFingerprintFor(message, channel_idx);
|
||||||
|
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||||
|
size_t response_len = botFormatTraceSent(response, sizeof(response), (uint8_t)(path_len / hash_size));
|
||||||
|
char final_response[BOT_MAX_RESPONSE_LEN + 1];
|
||||||
|
@@ -1737,6 +1749,16 @@ bool MyMesh::observeKnownBotResponse(const BotMessage &message, bool authoritati
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
+BotFingerprint MyMesh::botRequestFingerprintFor(const BotMessage &message, uint8_t channel_idx) {
|
||||||
|
+ if (message.channel_kind != BOT_CHANNEL_DM && channel_idx != 0xFF) {
|
||||||
|
+ ChannelDetails channel;
|
||||||
|
+ if (getChannel(channel_idx, channel)) {
|
||||||
|
+ return FirmwareBot::fingerprintFor(message, channel.channel.hash, sizeof(channel.channel.hash));
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return FirmwareBot::fingerprintFor(message);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
bool MyMesh::observeBotGroupResponse(const BotMessage &message) {
|
||||||
|
if (!BotPolicy::isPrefixlessCommandAllowed(message.channel_kind)) return false;
|
||||||
|
|
||||||
|
@@ -1749,6 +1771,9 @@ bool MyMesh::observeBotGroupResponse(const BotMessage &message) {
|
||||||
|
if (FirmwareBot::parseRequestTokenPrefix(message.text, message.text_len, &token, &token_prefix_len)) {
|
||||||
|
if (ResponseCoordinator::suppressByRequestToken(bot_coordinator_pending, BOT_COORDINATOR_PENDING_SLOTS, token)) {
|
||||||
|
token_suppressed = true;
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: token suppress %04x\r\n", (unsigned)token);
|
||||||
|
+ } else {
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: token miss %04x\r\n", (unsigned)token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1763,9 +1788,12 @@ bool MyMesh::observeBotGroupResponse(const BotMessage &message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx) {
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: record kind=%d direct=%d enabled=%d\r\n", (int)message.channel_kind, direct_recipient != NULL, bot_prefs.enabled);
|
||||||
|
bot_stats.observed_messages++;
|
||||||
|
BotPolicyDecision decision = BotPolicy::decide(message.channel_kind);
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: policy decision=%d\r\n", (int)decision);
|
||||||
|
if (decision == BOT_POLICY_IGNORE) {
|
||||||
|
+ BOT_DEBUG_PRINTLN("CBOT-DBG: IGNORE -> drop");
|
||||||
|
bot_stats.ignored_messages++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
@@ -1776,24 +1804,28 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bot_prefs.enabled) {
|
||||||
|
+ BOT_DEBUG_PRINTLN("CBOT-DBG: bot disabled -> drop");
|
||||||
|
bot_stats.ignored_messages++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (observeKnownBotResponse(message, direct_recipient != NULL)) return;
|
||||||
|
- if (!direct_recipient && observeBotGroupResponse(message)) return;
|
||||||
|
+ if (observeKnownBotResponse(message, direct_recipient != NULL)) { BOT_DEBUG_PRINTLN("CBOT-DBG: known-bot suppress"); return; }
|
||||||
|
+ if (!direct_recipient && observeBotGroupResponse(message)) { BOT_DEBUG_PRINTLN("CBOT-DBG: group-response suppress"); return; }
|
||||||
|
sendQueuedBotResponses();
|
||||||
|
|
||||||
|
BotCommand command;
|
||||||
|
if (!FirmwareBot::parseCommand(message.text, message.text_len, &command,
|
||||||
|
BotPolicy::isPrefixlessCommandAllowed(message.channel_kind))) {
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: parseCommand FAILED text=%.40s prefixless=%d\r\n", message.text, BotPolicy::isPrefixlessCommandAllowed(message.channel_kind));
|
||||||
|
if (message.text_len > 0 && (message.text[0] == '!' || message.text[0] == '/')) bot_stats.parse_errors++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: parsed cmd.id=%d name=%s args=%.30s\r\n", (int)command.id, command.name, command.args);
|
||||||
|
|
||||||
|
if ((command.id != BOT_COMMAND_UNKNOWN && command.id != BOT_COMMAND_UNSUPPORTED &&
|
||||||
|
!BotPrefsCodec::commandEnabled(bot_prefs, command.id)) ||
|
||||||
|
FirmwareBot::isCommandOnCooldown(bot_command_cooldowns, BOT_COMMAND_COOLDOWN_SLOTS, command.id, _ms->getMillis())) {
|
||||||
|
+ BOT_DEBUG_PRINTLN("CBOT-DBG: cmd disabled or on cooldown -> drop");
|
||||||
|
bot_stats.ignored_messages++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
@@ -1868,7 +1900,7 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
- BotFingerprint request_fingerprint = FirmwareBot::fingerprintFor(message);
|
||||||
|
+ BotFingerprint request_fingerprint = botRequestFingerprintFor(message, channel_idx);
|
||||||
|
BotFingerprint response_fingerprint = FirmwareBot::responseFingerprintFor(message, final_response, final_response_len);
|
||||||
|
BotFingerprint fingerprint;
|
||||||
|
uint32_t due_at_millis = 0;
|
||||||
|
@@ -1884,7 +1916,11 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||||
|
queue_depth, bot_prefs.normal_delay_ms,
|
||||||
|
bot_prefs.normal_jitter_ms, bot_prefs.hop_step_ms,
|
||||||
|
&fingerprint, &due_at_millis);
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: schedule result=%d due=%lu now=%lu reqfp=%08lx%08lx\r\n",
|
||||||
|
+ (int)schedule, (unsigned long)due_at_millis, (unsigned long)_ms->getMillis(),
|
||||||
|
+ (unsigned long)(request_fingerprint.value >> 32), (unsigned long)(request_fingerprint.value & 0xFFFFFFFFul));
|
||||||
|
if (schedule == BOT_COORDINATOR_NO_SPACE || schedule == BOT_COORDINATOR_NOT_NORMAL) {
|
||||||
|
+ BOT_DEBUG_PRINTLN("CBOT-DBG: schedule FAILED -> drop");
|
||||||
|
bot_stats.send_failures++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
@@ -1894,8 +1930,11 @@ void MyMesh::recordBotObservation(const BotMessage &message, const ContactInfo *
|
||||||
|
FirmwareBot::recordCommandCooldown(bot_command_cooldowns, BOT_COMMAND_COOLDOWN_SLOTS, command.id, _ms->getMillis(),
|
||||||
|
BOT_COMMAND_COOLDOWN_MILLIS);
|
||||||
|
if (!enqueueBotResponse(message, direct_recipient, channel_idx, final_response, final_response_len, fingerprint, response_fingerprint)) {
|
||||||
|
+ BOT_DEBUG_PRINTLN("CBOT-DBG: enqueueBotResponse FAILED -> drop");
|
||||||
|
ResponseCoordinator::cancel(bot_coordinator_pending, BOT_COORDINATOR_PENDING_SLOTS, fingerprint);
|
||||||
|
bot_stats.send_failures++;
|
||||||
|
+ } else {
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: queued response, text=%.40s\r\n", final_response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1904,6 +1943,8 @@ void MyMesh::sendQueuedBotResponses() {
|
||||||
|
while (true) {
|
||||||
|
BotCoordinatorReady ready = ResponseCoordinator::poll(bot_coordinator_pending, BOT_COORDINATOR_PENDING_SLOTS, now);
|
||||||
|
if (ready.result == BOT_COORDINATOR_READY_NONE) return;
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: poll result=%d reqfp=%08lx\r\n", (int)ready.result,
|
||||||
|
+ (unsigned long)(ready.request_fingerprint.value & 0xFFFFFFFFul));
|
||||||
|
|
||||||
|
if (ready.result == BOT_COORDINATOR_READY_SUPPRESSED) {
|
||||||
|
bot_stats.suppressed_responses++;
|
||||||
|
@@ -1945,12 +1986,14 @@ void MyMesh::sendQueuedBotResponses() {
|
||||||
|
bool success = false;
|
||||||
|
if (pending->direct) {
|
||||||
|
ContactInfo *recipient = lookupContactByPubKey(pending->recipient_pub_key, PUB_KEY_SIZE);
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: send DM lookup=%s text=%.40s\r\n", recipient ? "FOUND" : "NULL", pending->text);
|
||||||
|
if (recipient) {
|
||||||
|
uint32_t expected_ack = 0;
|
||||||
|
uint32_t est_timeout = 0;
|
||||||
|
uint32_t timestamp = getRTCClock()->getCurrentTimeUnique();
|
||||||
|
int result = sendMessage(*recipient, timestamp, 0, pending->text, expected_ack, est_timeout);
|
||||||
|
success = result != MSG_SEND_FAILED;
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: sendMessage result=%d success=%d\r\n", result, success);
|
||||||
|
if (success && expected_ack) {
|
||||||
|
expected_ack_table[next_ack_idx].msg_sent = _ms->getMillis();
|
||||||
|
expected_ack_table[next_ack_idx].ack = expected_ack;
|
||||||
|
@@ -1960,9 +2003,11 @@ void MyMesh::sendQueuedBotResponses() {
|
||||||
|
}
|
||||||
|
} else if (pending->channel_idx != 0xFF) {
|
||||||
|
ChannelDetails channel;
|
||||||
|
+ bool have_ch = getChannel(pending->channel_idx, channel);
|
||||||
|
uint32_t timestamp = getRTCClock()->getCurrentTimeUnique();
|
||||||
|
- success = getChannel(pending->channel_idx, channel) &&
|
||||||
|
- sendGroupMessage(timestamp, channel.channel, _prefs.node_name, pending->text, pending->text_len);
|
||||||
|
+ success = have_ch && sendGroupMessage(timestamp, channel.channel, _prefs.node_name, pending->text, pending->text_len);
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: send CH idx=%u have=%d success=%d text=%.40s\r\n",
|
||||||
|
+ (unsigned)pending->channel_idx, have_ch, success, pending->text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
@@ -1970,6 +2015,7 @@ void MyMesh::sendQueuedBotResponses() {
|
||||||
|
ResponseCoordinator::recordRecent(bot_coordinator_recent, BOT_COORDINATOR_RECENT_SLOTS,
|
||||||
|
pending->response_fingerprint, now);
|
||||||
|
} else {
|
||||||
|
+ BOT_DEBUG_PRINTLN("CBOT-DBG: send FAILED -> bot stays silent");
|
||||||
|
bot_stats.send_failures++;
|
||||||
|
}
|
||||||
|
pending->active = false;
|
||||||
|
@@ -2521,9 +2567,14 @@ void MyMesh::begin(bool has_display) {
|
||||||
|
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
|
||||||
|
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
|
||||||
|
#if CMESH_BOT_ENABLED
|
||||||
|
- if (!_store->loadBotPrefs(bot_prefs)) bot_prefs.prefs_load_failures++;
|
||||||
|
+ bool loaded = _store->loadBotPrefs(bot_prefs);
|
||||||
|
+ if (!loaded) bot_prefs.prefs_load_failures++;
|
||||||
|
BotPrefsCodec::validate(bot_prefs);
|
||||||
|
applyBotPrefs();
|
||||||
|
+ BOT_DEBUG_PRINTF("CBOT-DBG: boot: loaded=%d enabled=%d bot_ch=%s testing_ch=%s mask=0x%08lx delay=%u jitter=%u hop_step=%u\r\n",
|
||||||
|
+ loaded, bot_prefs.enabled, bot_prefs.bot_channel, bot_prefs.testing_channel,
|
||||||
|
+ (unsigned long)bot_prefs.command_mask, bot_prefs.normal_delay_ms, bot_prefs.normal_jitter_ms,
|
||||||
|
+ bot_prefs.hop_step_ms);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h
|
||||||
|
index 46c3ff3c..cfd402df 100644
|
||||||
|
--- a/examples/companion_radio/MyMesh.h
|
||||||
|
+++ b/examples/companion_radio/MyMesh.h
|
||||||
|
@@ -215,6 +215,7 @@ private:
|
||||||
|
void recordBotObservation(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx);
|
||||||
|
bool observeKnownBotResponse(const BotMessage &message, bool authoritative_sender);
|
||||||
|
bool observeBotGroupResponse(const BotMessage &message);
|
||||||
|
+ BotFingerprint botRequestFingerprintFor(const BotMessage &message, uint8_t channel_idx);
|
||||||
|
void buildBotCommandContext(BotCommandContext &context, BotCommandId command_id);
|
||||||
|
bool enqueueBotResponse(const BotMessage &message, const ContactInfo *direct_recipient, uint8_t channel_idx,
|
||||||
|
const char *text, size_t text_len, BotFingerprint request_fingerprint,
|
||||||
@@ -1138,6 +1138,26 @@ static void test_fingerprint() {
|
|||||||
assert(fa.value != FirmwareBot::fingerprintFor(b).value);
|
assert(fa.value != FirmwareBot::fingerprintFor(b).value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_channel_id_fingerprint() {
|
||||||
|
uint8_t channel_id_a[4] = { 0x10, 0x20, 0x30, 0x40 };
|
||||||
|
uint8_t channel_id_b[4] = { 0x10, 0x20, 0x30, 0x41 };
|
||||||
|
BotMessage a = make_message("#bot", "!PING");
|
||||||
|
BotMessage b = make_message("#testing", "!ping");
|
||||||
|
BotMessage c = make_message("#ops", "!ping");
|
||||||
|
c.channel_kind = BOT_CHANNEL_BOT;
|
||||||
|
BotFingerprint fa = FirmwareBot::fingerprintFor(a, channel_id_a, sizeof(channel_id_a));
|
||||||
|
BotFingerprint fb = FirmwareBot::fingerprintFor(b, channel_id_a, sizeof(channel_id_a));
|
||||||
|
BotFingerprint fc = FirmwareBot::fingerprintFor(c, channel_id_a, sizeof(channel_id_a));
|
||||||
|
assert(fa.value == fb.value);
|
||||||
|
assert(fa.value == fc.value);
|
||||||
|
assert(fa.value != FirmwareBot::fingerprintFor(a, channel_id_b, sizeof(channel_id_b)).value);
|
||||||
|
|
||||||
|
BotMessage dm = a;
|
||||||
|
dm.channel_kind = BOT_CHANNEL_DM;
|
||||||
|
dm.channel_name[0] = 0;
|
||||||
|
assert(FirmwareBot::fingerprintFor(dm, channel_id_a, sizeof(channel_id_a)).value == FirmwareBot::fingerprintFor(dm).value);
|
||||||
|
}
|
||||||
|
|
||||||
static BotFingerprint final_response_fingerprint_for(const BotMessage& message, const char* text) {
|
static BotFingerprint final_response_fingerprint_for(const BotMessage& message, const char* text) {
|
||||||
char response[BOT_MAX_RESPONSE_LEN + 1];
|
char response[BOT_MAX_RESPONSE_LEN + 1];
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
@@ -1171,6 +1191,21 @@ static void test_response_fingerprint() {
|
|||||||
FirmwareBot::responseFingerprintFor(other_dm, "pong!", 5).value);
|
FirmwareBot::responseFingerprintFor(other_dm, "pong!", 5).value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_response_fingerprint_ignores_request_token() {
|
||||||
|
BotMessage message = make_message("#bot", "ping");
|
||||||
|
BotFingerprint bare = FirmwareBot::responseFingerprintFor(message, "Pong!", 5);
|
||||||
|
assert(bare.value == FirmwareBot::responseFingerprintFor(message, "[1a2b] Pong!", 12).value);
|
||||||
|
assert(bare.value == FirmwareBot::responseFingerprintFor(message, "[1A2B] Pong!", 12).value);
|
||||||
|
assert(bare.value != FirmwareBot::responseFingerprintFor(message, "[bad] Pong!", 11).value);
|
||||||
|
|
||||||
|
BotMessage dm = message;
|
||||||
|
dm.channel_kind = BOT_CHANNEL_DM;
|
||||||
|
dm.channel_name[0] = 0;
|
||||||
|
dm.sender_key_prefix_len = BOT_SENDER_KEY_PREFIX_LEN;
|
||||||
|
assert(FirmwareBot::responseFingerprintFor(dm, "Pong!", 5).value ==
|
||||||
|
FirmwareBot::responseFingerprintFor(dm, "[1a2b] Pong!", 12).value);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_authoritative_suppression_flow() {
|
static void test_authoritative_suppression_flow() {
|
||||||
BotKnownBotEntry entries[1];
|
BotKnownBotEntry entries[1];
|
||||||
uint8_t known_key[BOT_SENDER_KEY_PREFIX_LEN] = { 2, 4, 6, 8, 10, 12 };
|
uint8_t known_key[BOT_SENDER_KEY_PREFIX_LEN] = { 2, 4, 6, 8, 10, 12 };
|
||||||
@@ -1665,7 +1700,9 @@ int main() {
|
|||||||
test_known_bot_registry();
|
test_known_bot_registry();
|
||||||
test_known_bot_registry_ambiguous_short_prefix();
|
test_known_bot_registry_ambiguous_short_prefix();
|
||||||
test_fingerprint();
|
test_fingerprint();
|
||||||
|
test_channel_id_fingerprint();
|
||||||
test_response_fingerprint();
|
test_response_fingerprint();
|
||||||
|
test_response_fingerprint_ignores_request_token();
|
||||||
test_authoritative_suppression_flow();
|
test_authoritative_suppression_flow();
|
||||||
test_response_coordinator_schedule_poll();
|
test_response_coordinator_schedule_poll();
|
||||||
test_response_coordinator_distinct_requests_same_response();
|
test_response_coordinator_distinct_requests_same_response();
|
||||||
|
|||||||
Reference in New Issue
Block a user