From 3857bd8ded0246c57d81612be7c5c28d86a07747 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 22:41:11 +0330 Subject: [PATCH] Add text chunking and Persian language helpers --- utils/chunker.py | 26 ++++++++++++++++++++++++++ utils/persian.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 utils/chunker.py create mode 100644 utils/persian.py diff --git a/utils/chunker.py b/utils/chunker.py new file mode 100644 index 0000000..12199df --- /dev/null +++ b/utils/chunker.py @@ -0,0 +1,26 @@ +TG_LIMIT = 4096 + + +def chunk_text(text: str, max_length: int = TG_LIMIT) -> list[str]: + """Split text into ≤max_length chunks, preferring newline then space boundaries.""" + if len(text) <= max_length: + return [text] + + chunks: list[str] = [] + while text: + if len(text) <= max_length: + chunks.append(text) + break + + split_at = text.rfind('\n', 0, max_length) + if split_at == -1: + split_at = text.rfind(' ', 0, max_length) + if split_at == -1: + split_at = max_length + + chunk = text[:split_at].strip() + if chunk: + chunks.append(chunk) + text = text[split_at:].strip() + + return chunks diff --git a/utils/persian.py b/utils/persian.py new file mode 100644 index 0000000..7501a03 --- /dev/null +++ b/utils/persian.py @@ -0,0 +1,45 @@ +import re +import unicodedata + +# Normalize Arabic lookalikes to Persian +_ARABIC_TO_PERSIAN = str.maketrans({ + 'ي': 'ی', # Arabic ya → Persian ya + 'ك': 'ک', # Arabic kaf → Persian kaf + 'ى': 'ی', # Alef maqsura → Persian ya +}) + +_PUPPY_RE = re.compile(r'puppy|پاپی|پاپي', re.IGNORECASE | re.UNICODE) + +# Rough threshold: if >15% of chars are Arabic/Persian script → Persian +_PERSIAN_BLOCK = re.compile(r'[؀-ۿ]') + + +def normalize(text: str) -> str: + text = unicodedata.normalize('NFC', text) + text = text.translate(_ARABIC_TO_PERSIAN) + text = text.replace('‌', '').replace('‍', '') # ZWNJ / ZWJ + return text + + +def is_puppy_mentioned(text: str) -> bool: + if not text: + return False + return bool(_PUPPY_RE.search(normalize(text))) + + +def detect_language(text: str) -> str: + if not text: + return 'en' + persian_count = len(_PERSIAN_BLOCK.findall(text)) + return 'fa' if persian_count > len(text) * 0.15 else 'en' + + +def strip_puppy_prefix(text: str) -> str: + """Remove 'Puppy/پاپی' name prefix from a command-style message.""" + cleaned = re.sub( + r'^[\s]*(puppy|پاپی|پاپي)[،,:\s]+', + '', + normalize(text), + flags=re.IGNORECASE | re.UNICODE, + ) + return cleaned.strip() or text.strip()