Files
PolyWeather/src/bot/command_parser.py
T

95 lines
2.7 KiB
Python
Raw Normal View History

from __future__ import annotations
import re
import unicodedata
from typing import Any
from typing import Iterable
from typing import Tuple
_SLASH_CHARS = "//⁄∕╱⧸"
_COMMAND_RE = re.compile(
rf"^[\s\u00A0]*[{re.escape(_SLASH_CHARS)}]\s*([A-Za-z0-9_]+)(?:@([A-Za-z0-9_]+))?",
flags=re.ASCII,
)
def _clean_text(text: str | None) -> str:
raw = str(text or "")
if not raw:
return ""
cleaned: list[str] = []
for ch in raw:
code = ord(ch)
if 0xFE00 <= code <= 0xFE0F:
continue
if 0xE0100 <= code <= 0xE01EF:
continue
if unicodedata.category(ch) == "Cf":
continue
cleaned.append(ch)
normalized = "".join(cleaned).strip()
if not normalized:
return ""
for slash in ("", "", "", "", ""):
normalized = normalized.replace(slash, "/")
return normalized
def _parse_command_token(text: str | None) -> Tuple[str, str]:
normalized = _clean_text(text)
if not normalized:
return ("", "")
match = _COMMAND_RE.match(normalized)
if not match:
return ("", "")
command = str(match.group(1) or "").strip().lower()
username = str(match.group(2) or "").strip().lower()
return (command, username)
def extract_command_token(
text: str | None,
entities: Iterable[Any] | None = None,
) -> Tuple[str, str]:
raw = str(text or "")
if entities:
for entity in entities:
if str(getattr(entity, "type", "") or "").strip() != "bot_command":
continue
try:
offset = int(getattr(entity, "offset", 0) or 0)
length = int(getattr(entity, "length", 0) or 0)
except Exception:
continue
if length <= 0:
continue
fragment = raw[offset : offset + length]
command, username = _parse_command_token(fragment)
if command:
return (command, username)
return _parse_command_token(raw)
def extract_command_name(text: str | None, entities: Iterable[Any] | None = None) -> str:
return extract_command_token(text, entities)[0]
def looks_like_slash_command(text: str | None) -> bool:
normalized = _clean_text(text)
if not normalized:
return False
return normalized[:1] == "/"
def split_command_and_args(text: str | None) -> Tuple[str, str]:
normalized = _clean_text(text)
if not normalized:
return ("", "")
match = _COMMAND_RE.match(normalized)
if not match:
return ("", "")
command = str(match.group(1) or "").strip().lower()
rest = normalized[match.end() :].lstrip()
return (command, rest)