Guard flood scope and be better about blocking

This commit is contained in:
Jack Kingsman
2026-03-04 20:15:44 -08:00
parent e439bc913a
commit 03f4963966
12 changed files with 35 additions and 5 deletions

View File

@@ -192,6 +192,7 @@ class Message(BaseModel):
)
txt_type: int = 0
signature: str | None = None
sender_key: str | None = None
outgoing: bool = False
acked: int = 0
sender_name: str | None = None

View File

@@ -206,6 +206,8 @@ async def create_message_from_decrypted(
sender_timestamp=timestamp,
received_at=received,
paths=paths,
sender_name=sender,
sender_key=resolved_sender_key,
).model_dump(),
)

View File

@@ -258,13 +258,19 @@ class RadioManager:
# Sync radio clock with system time
await sync_radio_time(mc)
# Apply flood scope from settings
# Apply flood scope from settings (best-effort; older firmware
# may not support set_flood_scope)
from app.repository import AppSettingsRepository
app_settings = await AppSettingsRepository.get()
scope = app_settings.flood_scope
await mc.commands.set_flood_scope(scope if scope else "")
logger.info("Applied flood_scope=%r", scope or "(disabled)")
try:
await mc.commands.set_flood_scope(scope if scope else "")
logger.info("Applied flood_scope=%r", scope or "(disabled)")
except Exception as exc:
logger.warning(
"set_flood_scope failed (firmware may not support it): %s", exc
)
# Sync contacts/channels from radio to DB and clear radio
logger.info("Syncing and offloading radio data...")

View File

@@ -154,6 +154,7 @@ class MessageRepository:
paths=MessageRepository._parse_paths(row["paths"]),
txt_type=row["txt_type"],
signature=row["signature"],
sender_key=row["sender_key"],
outgoing=bool(row["outgoing"]),
acked=row["acked"],
sender_name=row["sender_name"],

View File

@@ -270,14 +270,22 @@ export function App() {
if (!msg.outgoing) {
const bKeys = blockedKeysRef.current;
const bNames = blockedNamesRef.current;
// Block DMs by key
// Block DMs by sender key
if (
bKeys.length > 0 &&
msg.type === 'PRIV' &&
bKeys.includes(msg.conversation_key.toLowerCase())
)
return;
// Block by sender name (works for channel messages)
// Block channel messages by sender key
if (
bKeys.length > 0 &&
msg.type === 'CHAN' &&
msg.sender_key &&
bKeys.includes(msg.sender_key.toLowerCase())
)
return;
// Block by sender name (works for both DMs and channel messages)
if (bNames.length > 0 && msg.sender_name && bNames.includes(msg.sender_name)) return;
}

View File

@@ -193,6 +193,7 @@ describe('Integration: No phantom unreads from mesh echoes (hitlist #8 regressio
paths: null,
txt_type: 0,
signature: null,
sender_key: null,
outgoing: false,
acked: 0,
sender_name: null,
@@ -214,6 +215,7 @@ describe('Integration: No phantom unreads from mesh echoes (hitlist #8 regressio
paths: null,
txt_type: 0,
signature: null,
sender_key: null,
outgoing: false,
acked: 0,
sender_name: null,
@@ -354,6 +356,7 @@ describe('Integration: ACK + messageCache propagation', () => {
paths: null,
txt_type: 0,
signature: null,
sender_key: null,
outgoing: true,
acked: 0,
sender_name: null,
@@ -378,6 +381,7 @@ describe('Integration: ACK + messageCache propagation', () => {
paths: [{ path: 'aa', received_at: 1700000001 }],
txt_type: 0,
signature: null,
sender_key: null,
outgoing: true,
acked: 1,
sender_name: null,
@@ -406,6 +410,7 @@ describe('Integration: ACK + messageCache propagation', () => {
paths: null,
txt_type: 0,
signature: null,
sender_key: null,
outgoing: true,
acked: 5,
sender_name: null,
@@ -430,6 +435,7 @@ describe('Integration: ACK + messageCache propagation', () => {
paths: null,
txt_type: 0,
signature: null,
sender_key: null,
outgoing: true,
acked: 0,
sender_name: null,

View File

@@ -18,6 +18,7 @@ function createMessage(overrides: Partial<Message> = {}): Message {
paths: null,
txt_type: 0,
signature: null,
sender_key: null,
outgoing: false,
acked: 0,
sender_name: null,

View File

@@ -24,6 +24,7 @@ function createSearchResult(overrides: Partial<Message> = {}): Message {
paths: null,
txt_type: 0,
signature: null,
sender_key: null,
outgoing: false,
acked: 0,
sender_name: 'Alice',

View File

@@ -35,6 +35,7 @@ function createMessage(overrides: Partial<Message> = {}): Message {
paths: null,
txt_type: 0,
signature: null,
sender_key: null,
outgoing: true,
acked: 0,
sender_name: null,

View File

@@ -19,6 +19,7 @@ function createMessage(overrides: Partial<Message> = {}): Message {
paths: null,
txt_type: 0,
signature: null,
sender_key: null,
outgoing: false,
acked: 0,
sender_name: null,

View File

@@ -151,6 +151,7 @@ export interface Message {
paths: MessagePath[] | null;
txt_type: number;
signature: string | null;
sender_key: string | null;
outgoing: boolean;
/** ACK count: 0 = not acked, 1+ = number of acks/flood echoes received */
acked: number;

View File

@@ -320,6 +320,7 @@ class TestContactMessageCLIFiltering:
"paths",
"txt_type",
"signature",
"sender_key",
"outgoing",
"acked",
"sender_name",