mesh: POST /api/respond + MCP respond tool — reply to a message by packet id

This commit is contained in:
2026-09-25 15:32:34 +00:00
parent 88b617f8b7
commit c33f4613b5
6 changed files with 422 additions and 44 deletions
+29
View File
@@ -29,6 +29,17 @@ export class InjectMessageDto {
from?: number;
}
export class RespondDto {
@ApiProperty({
description:
'Packet id of the earlier message (from GET /api/messages) that this one replies to. Integer in 1..0xffffffff.',
example: 456789,
})
messageId!: number;
@ApiProperty({ description: 'Reply text', example: 'on my way' })
content!: string;
}
export class SendTextDto {
@ApiProperty({ description: 'Message text', example: 'hello' })
text!: string;
@@ -111,6 +122,24 @@ export class MeshController {
return { id: await this.mesh.sendText(to, body.text, replyId) };
}
@Post('respond')
@ApiOperation({
summary: 'Reply to an earlier message by packet id',
description:
"Routing is automatic — the caller never thinks about where it goes: rx records are answered as a DM back to the original sender, tx records go to their destination; broadcast originals (and injected messages without a sender) get a broadcast. The reply always rides the record's channel and carries replyId = messageId, so the recipient app renders it as a threaded quote of that packet. Returns { id } with the new packet id.",
})
@ApiResponse({
status: 200,
description: 'Packet queued',
example: { id: 123456789 },
})
@ApiResponse({ status: 400, description: 'Invalid messageId or content' })
@ApiResponse({ status: 404, description: 'No message with the given id' })
@ApiResponse({ status: 503, description: 'Bridge not connected' })
async respond(@Body() body: RespondDto) {
return this.mesh.respond(body?.messageId, body?.content);
}
@Get('channels')
@ApiOperation({
summary: "The device's Meshtastic channels (keys redacted)",