Add background-hash-mark addition for region routing

Per https://buymeacoffee.com/ripplebiz/region-filtering:

> After some discussions, and that there is some confusion
around #channels and #regions, it's been decided to drop
the requirement to have the '#' prefix. So, region names
will just be plain alphanumeric (and '-'), with no # prefix.

> For backwards compatibility, the names will internally have
a '#' prepended, but for all client GUI's and command lines,
you generally won't see mention of '#' prefixes. The next
firmware release (v1.12.0) and subsequent Ripple firmware
and Liam's app will have modified UI to remove the '#' requirement.

So, silently add, but don't duplicate, for users who have already
added hashmarks.
This commit is contained in:
Jack Kingsman
2026-03-09 15:24:23 -07:00
parent e03ddcaaa7
commit b157ee14e4
13 changed files with 107 additions and 24 deletions
+2 -1
View File
@@ -262,10 +262,11 @@ class RadioManager:
# Apply flood scope from settings (best-effort; older firmware
# may not support set_flood_scope)
from app.region_scope import normalize_region_scope
from app.repository import AppSettingsRepository
app_settings = await AppSettingsRepository.get()
scope = app_settings.flood_scope
scope = normalize_region_scope(app_settings.flood_scope)
try:
await mc.commands.set_flood_scope(scope if scope else "")
logger.info("Applied flood_scope=%r", scope or "(disabled)")
+20
View File
@@ -0,0 +1,20 @@
"""Helpers for normalizing MeshCore flood-scope / region names."""
def normalize_region_scope(scope: str | None) -> str:
"""Normalize a user-facing region scope into MeshCore's internal form.
Region names are now user-facing plain strings like ``Esperance``.
Internally, MeshCore still expects hashtag-style names like ``#Esperance``.
Backward compatibility:
- blank/None stays disabled (`""`)
- existing leading ``#`` is preserved
"""
stripped = (scope or "").strip()
if not stripped:
return ""
if stripped.startswith("#"):
return stripped
return f"#{stripped}"
+2 -1
View File
@@ -9,6 +9,7 @@ from app.dependencies import require_connected
from app.models import Channel, ChannelDetail, ChannelMessageCounts, ChannelTopSender
from app.radio import radio_manager
from app.radio_sync import upsert_channel_from_radio_slot
from app.region_scope import normalize_region_scope
from app.repository import ChannelRepository, MessageRepository
from app.websocket import broadcast_event
@@ -153,7 +154,7 @@ async def set_channel_flood_scope_override(
if not channel:
raise HTTPException(status_code=404, detail="Channel not found")
override = request.flood_scope_override.strip() or None
override = normalize_region_scope(request.flood_scope_override) or None
updated = await ChannelRepository.update_flood_scope_override(channel.key, override)
if not updated:
raise HTTPException(status_code=500, detail="Failed to update flood-scope override")
+3 -2
View File
@@ -14,6 +14,7 @@ from app.models import (
SendDirectMessageRequest,
)
from app.radio import radio_manager
from app.region_scope import normalize_region_scope
from app.repository import AmbiguousPublicKeyPrefixError, AppSettingsRepository, MessageRepository
from app.websocket import broadcast_error, broadcast_event
@@ -31,12 +32,12 @@ async def _send_channel_message_with_effective_scope(
action_label: str,
) -> Any:
"""Send a channel message, temporarily overriding flood scope when configured."""
override_scope = (channel.flood_scope_override or "").strip()
override_scope = normalize_region_scope(channel.flood_scope_override)
baseline_scope = ""
if override_scope:
settings = await AppSettingsRepository.get()
baseline_scope = settings.flood_scope
baseline_scope = normalize_region_scope(settings.flood_scope)
if override_scope and override_scope != baseline_scope:
logger.info(
+2 -2
View File
@@ -6,6 +6,7 @@ from fastapi import APIRouter
from pydantic import BaseModel, Field
from app.models import AppSettings
from app.region_scope import normalize_region_scope
from app.repository import AppSettingsRepository
logger = logging.getLogger(__name__)
@@ -123,8 +124,7 @@ async def update_settings(update: AppSettingsUpdate) -> AppSettings:
# Flood scope
flood_scope_changed = False
if update.flood_scope is not None:
stripped = update.flood_scope.strip()
kwargs["flood_scope"] = stripped
kwargs["flood_scope"] = normalize_region_scope(update.flood_scope)
flood_scope_changed = True
if kwargs: