v0.5.59: item counter evidence for fears, catch up Web features to site version.

Ship OpenDota counter-stats reordering for feared items, finalize SITE_VERSION/docs for rankings/streamers/trends/matches/mechanics and draft archetypes, and ignore regenerable Web data caches.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
voson
2026-07-29 02:11:49 +08:00
co-authored by Cursor
parent 37769580f5
commit 3ec8007077
72 changed files with 20669 additions and 2105 deletions
+15 -59
View File
@@ -25,6 +25,7 @@ from pathlib import Path
from common import DATA, ITEM_ICONS
from http_utils import download_icons, http_json, load_itemlist
from loc_format import HAS_PLACEHOLDER, format_loc
from mechanic_tags import apply_mechanic_tags, merge_tag_overrides
ITEMS_URL = (
"https://raw.githubusercontent.com/odota/dotaconstants/master/build/items.json"
@@ -58,6 +59,17 @@ TAG_ORDER = [
"silence",
"hex",
"root",
"stun",
"disarm",
"mute",
"invis",
"sleep",
"fear",
"taunt",
"blind",
"leash",
"ethereal",
"cyclone",
]
# key -> extra tags from curated whitelist (also covered by overrides)
@@ -73,12 +85,6 @@ H1_RE = re.compile(
r"<h1>\s*(主动|被动|使用|开关|升级|Active|Passive|Use|Toggle|Upgrade)\s*[:]?\s*([^<]*)</h1>",
re.I,
)
DISPEL_TYPE_RE = re.compile(
r"(?:驱散类型|Dispel\s*Type)\s*[:]\s*([^<\n]+)",
re.I,
)
def is_core_finished(meta: dict) -> bool:
key = meta.get("key") or ""
if meta.get("tier") is not None:
@@ -174,42 +180,11 @@ def parse_ability_kinds(desc_loc: str, od_abilities: list) -> list[str]:
return sorted(kinds)
def extract_dispel_from_text(text: str) -> set[str]:
tags: set[str] = set()
if not text:
return tags
for m in DISPEL_TYPE_RE.finditer(text):
chunk = m.group(1).lower()
if "" in chunk or "strong" in chunk:
tags.add("strong_dispel")
if "基础" in chunk or "basic" in chunk:
tags.add("basic_dispel")
if "" not in chunk and "strong" not in chunk and ("驱散" in chunk or "dispel" in chunk):
tags.add("basic_dispel")
low = text.lower()
if "strong dispel" in low or "强驱散" in text:
tags.add("strong_dispel")
if "basic dispel" in low or "基础驱散" in text:
tags.add("basic_dispel")
# Explicit purge / 阻止 (diffusal) — avoid bare 「幻象不会」lines
if re.search(
r"(?:施加|应用|造成)?.{0,8}(?:基础)?驱散|applies?\s+(?:a\s+)?(?:basic\s+)?dispel|"
r"\bpurge\b|阻止敌|Inhibit",
text,
re.I,
):
if "strong" in low or "强驱散" in text:
tags.add("strong_dispel")
else:
tags.add("basic_dispel")
return tags
def auto_tags(key: str, desc_zh: str, desc_en: str, en_od: str, od_dispellable: object) -> list[str]:
blob = " ".join([desc_zh or "", desc_en or "", en_od or "", key])
low = blob.lower()
tags: set[str] = set()
tags |= extract_dispel_from_text(blob)
# Query-facing applies-* tags (dispel / CC); shared with ability tagging.
tags |= apply_mechanic_tags(blob, key=key)
if od_dispellable and str(od_dispellable).lower() in ("yes", "both"):
# Item itself being dispellable is not "applies dispel"
@@ -221,8 +196,6 @@ def auto_tags(key: str, desc_zh: str, desc_en: str, en_od: str, od_dispellable:
re.I,
):
tags.add("mana_burn")
if re.search(r"(?:施加|造成|应用).{0,6}破坏|\bbreaks?\b(?:\s+passives?)?|Break\b", blob):
tags.add("break")
if key == "monkey_king_bar" or re.search(
r"true strike|无视闪避|必定命中|attacks? cannot miss",
blob,
@@ -265,12 +238,6 @@ def auto_tags(key: str, desc_zh: str, desc_en: str, en_od: str, od_dispellable:
tags.add("heal_reduce")
if re.search(r"armor reduction|reduce(?:s)? armor|减甲|降低护甲", blob, re.I):
tags.add("armor_reduce")
if re.search(r"\bsilence[sd]?\b|沉默", blob, re.I):
tags.add("silence")
if re.search(r"\bhex(?:es|ed)?\b|变羊|妖术", blob, re.I):
tags.add("hex")
if re.search(r"\broot(?:s|ed)?\b|缠绕|定身", blob, re.I):
tags.add("root")
return [t for t in TAG_ORDER if t in tags]
@@ -287,18 +254,7 @@ def load_overrides() -> dict[str, dict]:
def apply_overrides(key: str, tags: list[str], overrides: dict[str, dict]) -> list[str]:
row = overrides.get(key) or {}
s = set(tags)
for t in row.get("add") or []:
if isinstance(t, str):
s.add(t)
for t in row.get("remove") or []:
if isinstance(t, str):
s.discard(t)
# Keep known order, then any extras
ordered = [t for t in TAG_ORDER if t in s]
extras = sorted(s - set(TAG_ORDER))
return ordered + extras
return merge_tag_overrides(tags, overrides.get(key), TAG_ORDER)
def main() -> None: