from __future__ import annotations DEFAULT_STATUS_LABELS = { "open": "исполняет роль", "backstage": "в закулисье", "delay": "задержки", "rest": "антракт", } MARKDOWN_V2_SPECIALS = r"_*[]()~`>#+-=|{}.!" def escape_markdown_v2(value: str) -> str: return "".join(f"\\{char}" if char in MARKDOWN_V2_SPECIALS else char for char in value) def escape_markdown_v2_url(value: str) -> str: return value.replace("\\", "\\\\").replace(")", "\\)") def build_hidden_link(config: dict) -> str: url = config.get("hidden_link_url", "").strip() if not url: return "" invisible = config.get("hidden_link_char", "\u2063") return f"[{invisible}]({escape_markdown_v2_url(url)})" def build_actor_lines(config: dict, state: dict) -> str: actor_lines: list[str] = [] actor_state = state.get("actors", {}) status_labels = {**DEFAULT_STATUS_LABELS, **config.get("status_labels", {})} for actor in config["actors"]: current = actor_state.get(actor["key"], {}) status = current.get("status", actor.get("default_status", "backstage")) phrase = current.get("phrase", actor.get("phrases", {}).get(status, "")) label = status_labels.get(status, status) display_name = actor.get("display_md", actor["display_name"]) meta = actor.get("meta_md", actor["pronouns"]) emoji = actor.get("emoji_md", actor.get("emoji", "")) line = ( f"{emoji} " f"[{escape_markdown_v2(display_name)}]({escape_markdown_v2_url(actor['link'])}) " f"{escape_markdown_v2(meta)} " f"{escape_markdown_v2(label)}\\." ) if phrase: line = f"{line}\n {escape_markdown_v2(phrase)}" actor_lines.append(line) actor_lines.extend(config.get("static_actor_lines_md", [])) return "\n".join(actor_lines) def build_default_template(config: dict) -> str: blocks = [] hidden = build_hidden_link(config) if hidden: blocks.append(hidden) for key in ("header", "intro_links", "projects_block", "actors_title"): value = config.get(key, "").strip() if value: blocks.append(escape_markdown_v2(value)) blocks.append("{{actors}}") for key in ("legend", "footer"): value = config.get(key, "").strip() if value: blocks.append(escape_markdown_v2(value)) return "\n\n".join(blocks) def build_channel_text(config: dict, state: dict) -> str: template = state.get("template", {}).get("text") or config.get("template_text") or build_default_template(config) actors_block = build_actor_lines(config, state) hidden_link = build_hidden_link(config) text = template.replace("{{actors}}", actors_block) text = text.replace("{{hidden_link}}", hidden_link) if "{{hidden_link}}" not in template and hidden_link: text = f"{hidden_link}\n{text}" return text