import re from .constants import BACKEND_TELEGRAM, PREFIXES from .models import Command NAME_RE = re.compile(r"^[a-z][a-z0-9_]{0,31}$") QUOTES = {'"': '"', "“": "”", "„": "“"} def tokenize(text: str) -> list[str]: tokens: list[str] = [] current: list[str] = [] in_token = False closing: str | None = None for char in text: if closing: if char == closing: closing = None else: current.append(char) elif char in QUOTES and not in_token: closing, in_token = QUOTES[char], True elif char.isspace(): if in_token: tokens.append("".join(current)) current, in_token = [], False else: current.append(char) in_token = True if in_token: tokens.append("".join(current)) return tokens def parse(backend: str, text: str, bot_name: str | None = None) -> Command | None: prefix = PREFIXES.get(backend) text = text.strip() if not prefix or not text.startswith(prefix): return None body = text[len(prefix):] if not body or body[0].isspace(): return None parts = body.split(None, 1) head, rest = parts[0], parts[1] if len(parts) > 1 else "" head, _, addressed = head.partition("@") if addressed and (backend != BACKEND_TELEGRAM or (bot_name and addressed.lower() != bot_name.lower().lstrip("@"))): return None name = head.lower() if not NAME_RE.match(name): return None rest = rest.strip() return Command(name, tuple(tokenize(rest)), rest)