Files
simplex-chat/apps/simplex-support-bot-light/src/support_bot_light/text.py
T
sh 68916aa90a feat: add simplex-support-bot-light (#7378)
* feat: add simplex-support-bot-light

* feat: package simplex-support-bot-light for docker

* docs: document simplex-support-bot-light

* fix: make a missing config actionable and bounded

* docs: note the attached compose Ctrl+C behaviour

* refactor: use the library for startup and custom data

* refactor: drop the display-name workaround

* style: shorten the startup comment

* build: build the core and the library into the image

* docs: correct where the image type rule comes from

* fix: log what the core said a command got wrong

* build: run the container as the operator's uid

* docs: keep the container uid in .env

* docs: tighten the README
2026-08-17 09:12:47 +01:00

49 lines
1.8 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Sanitising peer-controlled text before it is rendered."""
from __future__ import annotations
import unicodedata
# mkValidName in src/Simplex/Chat/Library/Commands.hs caps a locally entered
# name at 50 characters. It is not applied to inbound profiles.
MAX_NAME = 50
UNNAMED = "(unnamed)"
# Characters that render as nothing but are neither whitespace nor a control
# category, so `str.split` and `str.isprintable` both let them through. A stock
# client accepts them in a profile name, which makes "Alice" a
# working impersonation of "Alice".
# Separators the bot's own messages use. A customer chooses their display name,
# and the roster group is the operator's only record of who was added.
SEPARATORS = frozenset("→")
INVISIBLE = frozenset(
"" # Hangul fillers
"" # Braille pattern blank
"" # Khmer inherent vowels
"" # word joiner, zero-width no-break space
)
def safe_name(name: str) -> str:
"""Collapse and truncate a display name for rendering.
The core does not sanitise inbound profiles: a peer's display name reaches
us verbatim and may contain newlines or run to kilobytes. Rendered as-is it
forges lines in the roster group and in the log, and can push a message past
the size the core will send.
"""
# NFKC folds compatibility forms, so a name cannot hide behind an exotic
# encoding of an ordinary character.
collapsed = " ".join(unicodedata.normalize("NFKC", name).split())
printable = "".join(
c for c in collapsed if c.isprintable() and c not in INVISIBLE and c not in SEPARATORS
)
stripped = printable.strip()
if not stripped:
return UNNAMED
if len(stripped) > MAX_NAME:
return stripped[: MAX_NAME - 1] + "…"
return stripped