Skip to content

Commit 08a0708

Browse files
committed
solves review comments
1 parent 099503c commit 08a0708

8 files changed

Lines changed: 39 additions & 7 deletions

File tree

radioshaq/radioshaq/api/routes/messages.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ async def inject_and_store(
369369
)
370370
transcript_id = None
371371
storage = get_transcript_storage(request)
372-
if storage and getattr(storage, "_db", None):
372+
if storage and getattr(storage, "db", None):
373373
transcript_id = await storage.store(
374374
session_id=f"inject-store-{uuid.uuid4().hex[:12]}",
375375
source_callsign=src,

radioshaq/radioshaq/database/models.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,11 @@ class CoordinationEvent(Base):
310310
)
311311

312312
def to_dict(self) -> dict[str, Any]:
313-
"""Convert to dictionary."""
313+
"""Convert to dictionary. Redacts emergency_contact_phone in extra_data for privacy."""
314+
extra = dict(self.extra_data) if self.extra_data else {}
315+
if "emergency_contact_phone" in extra and extra["emergency_contact_phone"]:
316+
raw = str(extra["emergency_contact_phone"])
317+
extra["emergency_contact_phone"] = "****" + raw[-4:] if len(raw) >= 4 else "****"
314318
return {
315319
"id": self.id,
316320
"event_type": self.event_type,
@@ -323,7 +327,7 @@ def to_dict(self) -> dict[str, Any]:
323327
"priority": self.priority,
324328
"notes": self.notes,
325329
"created_at": self.created_at.isoformat() if self.created_at else None,
326-
"extra_data": self.extra_data,
330+
"extra_data": extra,
327331
}
328332

329333

radioshaq/radioshaq/orchestrator/factory.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,12 +408,14 @@ def create_tool_registry(config: Config, db: Any = None, *, app: Any = None) ->
408408
app.state.agent_registry.get_agent("radio_tx")
409409
if getattr(app.state, "agent_registry", None) else None
410410
)
411+
message_bus = getattr(app.state, "message_bus", None) if app else None
411412
relay_tool = RelayMessageTool(
412413
storage=storage,
413414
injection_queue=injection_queue,
414415
get_radio_tx=get_radio_tx,
415416
config=config,
416417
callsign_repository=callsign_repo,
418+
message_bus=message_bus,
417419
)
418420
registry.register(relay_tool)
419421
logger.debug("Registered relay tool: %s", relay_tool.name)

radioshaq/radioshaq/specialized/radio_rx_audio.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ async def _on_segment_ready(self, segment: Any) -> None:
392392
return
393393

394394
# Optionally store voice transcript (band, source=voice_listener) for GET /transcripts and relay
395-
if getattr(self._radio_config, "voice_store_transcript", False) and self._transcript_storage and getattr(self._transcript_storage, "_db", None):
395+
if getattr(self._radio_config, "voice_store_transcript", False) and self._transcript_storage and getattr(self._transcript_storage, "db", None):
396396
min_len = getattr(self._radio_config, "voice_store_min_length", 0) or 0
397397
keywords = getattr(self._radio_config, "voice_store_keywords", None) or []
398398
stripped = transcript.strip()

radioshaq/radioshaq/specialized/relay_tools.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
from typing import Any
77

88
from radioshaq.compliance_plugin import get_band_plan_source_for_config
9+
from radioshaq.constants import E164_PATTERN
910
from radioshaq.radio.bands import BAND_PLANS
1011
from radioshaq.relay.service import relay_message_between_bands_service
12+
from radioshaq.utils.phone import normalize_e164
1113

1214
# Optional ISO datetime for deliver_at (lenient)
1315
DELIVER_AT_PATTERN = re.compile(
@@ -34,12 +36,14 @@ def __init__(
3436
get_radio_tx: Any = None,
3537
config: Any = None,
3638
callsign_repository: Any = None,
39+
message_bus: Any = None,
3740
) -> None:
3841
self._storage = storage
3942
self._injection_queue = injection_queue
4043
self._get_radio_tx = get_radio_tx
4144
self._config = config
4245
self._callsign_repository = callsign_repository
46+
self._message_bus = message_bus
4347

4448
def to_schema(self) -> dict[str, Any]:
4549
return {
@@ -119,6 +123,12 @@ def validate_params(self, params: dict[str, Any]) -> list[str]:
119123
errors.append("target_channel must be radio, sms, or whatsapp")
120124
if target_channel in ("sms", "whatsapp") and not destination_phone:
121125
errors.append("destination_phone is required when target_channel is sms or whatsapp")
126+
if target_channel in ("sms", "whatsapp") and destination_phone:
127+
normalised = normalize_e164(destination_phone)
128+
if not E164_PATTERN.match(normalised):
129+
errors.append(
130+
f"destination_phone must be E.164 (e.g. +14155552671); got: {destination_phone!r}"
131+
)
122132
if params.get("emergency") is True and target_channel not in ("sms", "whatsapp"):
123133
errors.append("emergency only applies when target_channel is sms or whatsapp")
124134
config = self._config
@@ -165,7 +175,7 @@ async def execute(
165175
emergency: bool = False,
166176
**kwargs: Any,
167177
) -> str:
168-
if self._storage is None or not getattr(self._storage, "_db", None):
178+
if self._storage is None or getattr(self._storage, "db", None) is None:
169179
return "Error: Relay not available (no storage)."
170180

171181
config = self._config
@@ -223,6 +233,7 @@ async def execute(
223233
target_channel=(target_channel or "radio").strip().lower(),
224234
destination_phone=(destination_phone or "").strip() or None,
225235
emergency=emergency,
236+
message_bus=self._message_bus,
226237
)
227238

228239
if not result.get("ok"):

radioshaq/radioshaq/specialized/sms_agent.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,20 @@ async def _send(
6464
"notes": "Twilio client or from_number not configured",
6565
"reason": "twilio_not_configured",
6666
}
67+
from_e164 = normalize_e164(self.from_number)
68+
if not from_e164:
69+
return {
70+
"success": False,
71+
"to": to,
72+
"notes": "from_number normalizes to empty string; check Twilio sender config",
73+
"reason": "invalid_from",
74+
}
6775

6876
try:
6977
msg = await asyncio.to_thread(
7078
self.twilio_client.messages.create,
7179
body=body,
72-
from_=normalize_e164(self.from_number),
80+
from_=from_e164,
7381
to=to,
7482
)
7583
result = {

radioshaq/radioshaq/specialized/whatsapp_agent.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ async def _do_send(self, to: str, message: str) -> dict[str, Any]:
8383
"to": to,
8484
"notes": "to (phone number) is required",
8585
}
86+
if not from_e164:
87+
return {
88+
"success": False,
89+
"to": to,
90+
"notes": "from_number normalizes to empty string; check Twilio WhatsApp sender config",
91+
}
8692
try:
8793
msg = await asyncio.to_thread(
8894
self.client.messages.create,

radioshaq/tests/unit/specialized/test_relay_tools.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ async def test_relay_tool_execute_no_storage_returns_error() -> None:
9494
@pytest.mark.asyncio
9595
async def test_relay_tool_execute_calls_service_and_formats_result() -> None:
9696
"""Execute calls relay_message_between_bands_service and returns string with Relayed and ids."""
97-
storage = MagicMock(_db=MagicMock())
97+
storage = MagicMock()
98+
storage.db = MagicMock()
9899
storage.store = AsyncMock(side_effect=[101, 102])
99100
queue = MagicMock()
100101
get_radio_tx = MagicMock(return_value=None)

0 commit comments

Comments
 (0)