Use numerical acks

This commit is contained in:
Jack Kingsman
2026-01-10 00:51:54 -08:00
parent e262bd677a
commit 2798b551f8
17 changed files with 79 additions and 58 deletions
+10 -2
View File
@@ -258,6 +258,11 @@ KeyStore.clear_private_key()
## ACK and Repeat Detection
The `acked` field is an integer count, not a boolean:
- `0` = not acked
- `1` = one ACK/echo received
- `2+` = multiple flood echoes received
### Direct Message ACKs
When sending a direct message, an expected ACK code is tracked:
@@ -267,7 +272,7 @@ from app.event_handlers import track_pending_ack
track_pending_ack(expected_ack="abc123", message_id=42, timeout_ms=30000)
```
When ACK event arrives, the message is marked as acked.
When ACK event arrives, the message's ack count is incremented.
### Channel Message Repeats
@@ -276,7 +281,10 @@ Flood messages echo back through repeaters. Detection uses:
- Text hash
- Timestamp (±5 second window)
When a repeat is detected, the original outgoing message is marked as "acked".
Each repeat increments the ack count. The frontend displays:
- `?` = no acks
- `✓` = 1 echo
- `✓2`, `✓3`, etc. = multiple echoes (real-time updates via WebSocket)
### Auto-Contact Sync to Radio
+2 -2
View File
@@ -180,8 +180,8 @@ async def on_ack(event: "Event") -> None:
message_id, _, _ = _pending_acks.pop(ack_code)
logger.info("ACK received for message %d", message_id)
await MessageRepository.mark_acked(message_id)
broadcast_event("message_acked", {"message_id": message_id})
ack_count = await MessageRepository.increment_ack_count(message_id)
broadcast_event("message_acked", {"message_id": message_id, "ack_count": ack_count})
else:
logger.debug("ACK code %s does not match any pending messages", ack_code)
+1 -1
View File
@@ -76,7 +76,7 @@ class Message(BaseModel):
txt_type: int = 0
signature: str | None = None
outgoing: bool = False
acked: bool = False
acked: int = 0
class RawPacket(BaseModel):
+2 -2
View File
@@ -230,8 +230,8 @@ async def _process_group_text(
# Don't pop - let it expire naturally so subsequent repeats via
# different radio paths are also caught as duplicates
logger.info("Repeat detected for channel message %d", message_id)
await MessageRepository.mark_acked(message_id)
broadcast_event("message_acked", {"message_id": message_id})
ack_count = await MessageRepository.increment_ack_count(message_id)
broadcast_event("message_acked", {"message_id": message_id, "ack_count": ack_count})
is_repeat = True
break
+10 -4
View File
@@ -313,17 +313,23 @@ class MessageRepository:
txt_type=row["txt_type"],
signature=row["signature"],
outgoing=bool(row["outgoing"]),
acked=bool(row["acked"]),
acked=row["acked"],
)
for row in rows
]
@staticmethod
async def mark_acked(message_id: int) -> None:
async def increment_ack_count(message_id: int) -> int:
"""Increment ack count and return the new value."""
await db.conn.execute(
"UPDATE messages SET acked = 1 WHERE id = ?", (message_id,)
"UPDATE messages SET acked = acked + 1 WHERE id = ?", (message_id,)
)
await db.conn.commit()
cursor = await db.conn.execute(
"SELECT acked FROM messages WHERE id = ?", (message_id,)
)
row = await cursor.fetchone()
return row["acked"] if row else 1
@staticmethod
async def find_duplicate(
@@ -398,7 +404,7 @@ class MessageRepository:
txt_type=row["txt_type"],
signature=row["signature"],
outgoing=bool(row["outgoing"]),
acked=bool(row["acked"]),
acked=row["acked"],
)
for row in rows
]