import re SECTION_SIGN = "\u00a7" FORMAT_CODE_RE = re.compile(SECTION_SIGN + r"[0-9a-fk-or]", re.IGNORECASE) def strip_mc_format_codes(text: str) -> str: return FORMAT_CODE_RE.sub("", text) _TRANSLATION_TEMPLATES = { "chat.type.text": "<{0}> {1}", "chat.type.announcement": "[{0}] {1}", "multiplayer.player.joined": "{0} joined the game", "multiplayer.player.left": "{0} left the game", "death.attack.player": "{0} was slain by {1}", "death.fell.accident.generic": "{0} fell from a high place", } def json_chat_to_plain(component) -> str: if isinstance(component, str): return component if isinstance(component, dict): parts = [] text = component.get("text", "") if text: parts.append(text) translate = component.get("translate", "") if translate: with_parts = [json_chat_to_plain(c) for c in component.get("with", [])] template = _TRANSLATION_TEMPLATES.get(translate) if template: parts.append(template.format(*with_parts)) else: parts.append(" ".join(with_parts)) extra = component.get("extra", []) for item in extra: parts.append(json_chat_to_plain(item)) return "".join(parts) if isinstance(component, list): return "".join(json_chat_to_plain(item) for item in component) return str(component) def markdown_to_mc_plain(text: str) -> str: if not text: return text ss = SECTION_SIGN text = re.sub(r"\*\*\*(.+?)\*\*\*", ss + "l" + ss + "o\\1" + ss + "r", text) text = re.sub(r"\*\*(.+?)\*\*", ss + "l\\1" + ss + "r", text) text = re.sub(r"\*(.+?)\*", ss + "o\\1" + ss + "r", text) text = re.sub(r"~~(.+?)~~", ss + "m\\1" + ss + "r", text) text = re.sub(r"___(.+?)___", ss + "l" + ss + "o\\1" + ss + "r", text) text = re.sub(r"__(.+?)__", ss + "n\\1" + ss + "r", text) text = re.sub(r"_(.+?)_", ss + "o\\1" + ss + "r", text) text = re.sub(r"\[(.+?)\]\(.+?\)", ss + "9\\1" + ss + "r", text) text = re.sub(r"`(.+?)`", ss + "7\\1" + ss + "r", text) text = re.sub(r"#{1,6}\s*", "", text) text = re.sub(r"[-*+]\s", "• ", text) return text