Event-driven CommandService dispatches exact case-sensitive #command matches on COMMAND_CHANNEL (default 2) through a single-registration-point registry; first command #ping -> #pong!. Replies ride COMMAND_CHANNEL: directed DM back to the sender when present, else broadcast. Failing handler/send only logs a warn. DI: CommandModule's factory injects MeshService and attaches itself via MeshService.attachCommands() — a constructor-level cycle leaves one side null in NestJS 12's injector (verified), so no forwardRef is used. Tests 106 -> 121; docs synced (AGENTS.md, MESHTASTIC.md, README).
This commit is contained in:
@@ -11,6 +11,8 @@ import * as net from 'node:net';
|
||||
// type-only import: node:sqlite is only required at runtime by whoever opens a database (mesh.module.ts / tests)
|
||||
import type { DatabaseSync } from 'node:sqlite';
|
||||
import type * as Proto from '@meshtastic/protobufs';
|
||||
// type-only: the instance arrives via attachCommands() in production (command.module.ts) or the constructor in tests — no runtime import cycle with src/commands
|
||||
import type { CommandService } from '../commands/command.service.js';
|
||||
import { OpencodeService } from '../opencode/opencode.service.js';
|
||||
import {
|
||||
BROADCAST_ADDR,
|
||||
@@ -211,11 +213,23 @@ export class MeshService implements OnModuleInit, OnModuleDestroy {
|
||||
private db: DatabaseSync | null = null;
|
||||
/** In-process opencode notifier for rx texts on the notify channel; null in tests that don't wire one. */
|
||||
private readonly opencode: OpencodeService | null;
|
||||
/** Event-driven `#command` auto-responder (COMMAND_CHANNEL); null until wired — via the constructor (tests) or attachCommands() (DI). */
|
||||
private commands: CommandService | null = null;
|
||||
private pollTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
constructor(db?: DatabaseSync, opencode?: OpencodeService) {
|
||||
constructor(
|
||||
db?: DatabaseSync,
|
||||
opencode?: OpencodeService,
|
||||
commands?: CommandService,
|
||||
) {
|
||||
this.db = db ?? null;
|
||||
this.opencode = opencode ?? null;
|
||||
this.commands = commands ?? null;
|
||||
}
|
||||
|
||||
/** Wire the `#command` auto-responder after construction — used by command.module.ts to break the DI cycle (a constructor-level MeshService <-> CommandService cycle leaves one side null in NestJS's injector). */
|
||||
attachCommands(commands: CommandService): void {
|
||||
this.commands = commands;
|
||||
}
|
||||
|
||||
private socket: net.Socket | null = null;
|
||||
@@ -736,6 +750,8 @@ export class MeshService implements OnModuleInit, OnModuleDestroy {
|
||||
at: Date.now(),
|
||||
});
|
||||
this.notifyRx(pkt.channel ?? 0, text, pkt.from, pkt.id, pkt.to);
|
||||
// event-driven #command auto-responder (COMMAND_CHANNEL) — fire-and-forget like the notifier
|
||||
void this.commands?.onRx(pkt.channel ?? 0, text, pkt.from, pkt.id);
|
||||
} else if (decoded && decoded.portnum === PORT_POSITION_APP) {
|
||||
// live position update: the Position is serialized in Data.payload
|
||||
let pos: Proto.Mesh.Position | undefined;
|
||||
|
||||
Reference in New Issue
Block a user