Space multi-line command replies MESH_MULTI_SEND_DELAY_MS apart (v0.21.5)
CI / build + unit (pull_request) Failing after 21s

Production incident: '#rss get lublin' returned an array of lines and
CommandService sent each one back-to-back within the same second — on the real
mesh 3 of 4 items were dropped in RF (the owner saw only the last one).

CommandService.onRx now waits MESH_MULTI_SEND_DELAY_MS (new env, default 5000 =
5 s, read at construction like the other timing knobs) between consecutive
lines of an array reply — no delay before the first line. The delay sits in the
shared per-line loop, so both the respond() path and the legacy sendText
fallback are covered by one await.
This commit is contained in:
2026-09-26 10:29:21 +00:00
parent 3f6c02d71e
commit 1b768b14a7
5 changed files with 47 additions and 4 deletions
+14 -2
View File
@@ -27,6 +27,11 @@ export class CommandService {
process.env.COMMAND_CHANNEL ?? '2',
10,
);
// spacing between consecutive multi-line reply messages — back-to-back packets get dropped in RF on a real mesh; each packet needs time to land. Read at construction like the other env config.
private readonly multiSendDelayMs = parseInt(
process.env.MESH_MULTI_SEND_DELAY_MS ?? '5000',
10,
);
// single registration point: the constructor registers every built-in command — add the next one there (or via register())
private readonly commands = new Map<string, CommandHandler>();
// prefix handlers own a whole family (`#rss`, `#rss add ...`) instead of one exact name
@@ -83,8 +88,15 @@ export class CommandService {
try {
const reply = await handler({ text: trimmed, from, id });
if (reply === undefined || reply === null) return; // void = no reply
// an array reply sends one message per line, all threaded to the same trigger packet id
for (const line of Array.isArray(reply) ? reply : [reply]) {
// an array reply sends one message per line, all threaded to the same trigger packet id — spaced so each packet has time to land in RF (no delay before the first line)
const lines = Array.isArray(reply) ? reply : [reply];
for (let i = 0; i < lines.length; i++) {
if (i > 0 && this.multiSendDelayMs > 0) {
await new Promise((resolve) =>
setTimeout(resolve, this.multiSendDelayMs),
);
}
const line = lines[i];
if (id !== undefined && this.mesh) {
// respond() auto-routes each line: rides the original record's channel with replyId = id (threaded quote)
await this.mesh.respond(id, line);