Merge pull request #282 from pdxlocations:ping-reply

Enhance bot response to include triggering packet ID for native replies
This commit is contained in:
pdxlocations
2026-07-24 09:20:43 -07:00
committed by GitHub
2 changed files with 35 additions and 4 deletions
+7 -4
View File
@@ -40,7 +40,8 @@ def bot_respond(packet: Dict[str, Any], message: str, send_channel: int) -> bool
return False
snr = packet.get('rxSnr', -128)
rssi = packet.get('rxRssi', -128)
replyIDset = (packet.get('decoded') or {}).get('replyId', False)
incoming_reply_id = (packet.get('decoded') or {}).get('replyId')
packet_id = packet.get("id")
hop_start = packet.get('hopStart', 0)
hop_limit = packet.get('hopLimit', 0)
transport_type = packet.get('transportMechanism', None)
@@ -53,8 +54,8 @@ def bot_respond(packet: Dict[str, Any], message: str, send_channel: int) -> bool
details.append(f"RSSI: {rssi}")
if hops != 0:
details.append(f"Hops: {hops}")
if replyIDset:
details.append(f"Relay: {replyIDset}")
if incoming_reply_id:
details.append(f"Relay: {incoming_reply_id}")
transport_text = str(transport_type).upper() if transport_type is not None else ""
for transport_name in ("UDP", "MQTT"):
if transport_name in transport_text:
@@ -72,7 +73,9 @@ def bot_respond(packet: Dict[str, Any], message: str, send_channel: int) -> bool
if not ui_state.bot_mode_enabled:
return
send_message(response_data_string,channel=send_channel)
# Use the triggering packet's ID so Meshtastic clients render this
# automatic response as a native reply when that ID is available.
send_message(response_data_string, channel=send_channel, reply_id=packet_id)
# Import locally to avoid circular import at module import time.
from contact.ui.contact_ui import request_ui_redraw
+28
View File
@@ -5,6 +5,8 @@ import types
from unittest import mock
import contact.ui.default_config as config
from contact.utilities.singleton import interface_state, ui_state
from tests.test_support import reset_singletons
class BotHandlerTests(unittest.TestCase):
@@ -16,6 +18,13 @@ class BotHandlerTests(unittest.TestCase):
)
cls.bot_handler = importlib.import_module("contact.message_handlers.bot_handler")
def setUp(self) -> None:
reset_singletons()
self.bot_handler.send_message.reset_mock()
def tearDown(self) -> None:
reset_singletons()
def test_is_bot_message_uses_configured_catch_words(self) -> None:
with mock.patch.object(config, "ping_bot_catch_words", "ping; test; pong"):
self.assertTrue(self.bot_handler.is_bot_message("PING"))
@@ -26,6 +35,25 @@ class BotHandlerTests(unittest.TestCase):
with mock.patch.object(config, "ping_bot_catch_words", " ; ; "):
self.assertTrue(self.bot_handler.is_bot_message("ping"))
def test_bot_response_replies_to_triggering_packet(self) -> None:
ui_state.bot_mode_enabled = True
interface_state.myNodeNum = 111
packet = {"id": 900, "from": 222, "decoded": {}}
with mock.patch.object(self.bot_handler.threading, "Thread") as thread:
self.assertTrue(self.bot_handler.bot_respond(packet, "ping", 0))
send_response = thread.call_args.kwargs["target"]
with mock.patch.object(self.bot_handler.time, "sleep"):
with mock.patch.dict(
sys.modules,
{"contact.ui.contact_ui": types.SimpleNamespace(request_ui_redraw=mock.Mock())},
):
send_response()
self.bot_handler.send_message.assert_called_once_with(
"Pong!", channel=0, reply_id=900
)
if __name__ == "__main__":
unittest.main()