Files
yunke/yunke-server/app/scraper/parsers.py
T

52 lines
2.2 KiB
Python
Raw Normal View History

2026-08-01 11:39:55 +08:00
"""Field / type-code normalization helpers for upstream record parsers.
The yunke CRM uses opaque integer type codes (``type`` / ``messageType``) on
WeChat message records. The storage layer prefers stable enum strings so that
media dispatch (image / voice / video / file) and presentation (text vs.
date divider) can be keyed off a single normalized value rather than the
raw code, whose exact mapping for non-text codes is a Phase-0 unknown.
This module is intentionally tiny: one total function that maps the
documented codes and falls through to ``"unknown"`` for everything else.
It MUST NOT raise — every record pulled during a scrape passes through here,
and one unexpected code must not abort the whole pull. The raw code is
preserved on the record's ``raw_json`` so the persistence layer can recover
it when needed.
"""
from __future__ import annotations
from typing import Any
__all__ = ["normalize_wechat_type"]
# Documented WeChat message type codes (see Task 1.6 spec).
# * ``1`` — plain text (and sticker text). The only non-media type we map
# today; everything else falls through to ``"unknown"``.
# * ``-2`` — date divider. NOT a real message; the scraper uses this label as
# its drop signal (filtered out, never yielded, never persisted).
# Image / voice / video / file / transfer / redpacket / system codes are
# observed in the wild but their exact integer values are a Phase-0 unknown.
# When confirmed, extend this table — for now they pass through as
# ``"unknown"`` so the message is still ingested rather than dropped.
_WECHAT_TYPE_NAMES: dict[int, str] = {
1: "text",
-2: "divider",
}
def normalize_wechat_type(raw: Any) -> str:
"""Map an upstream WeChat ``type`` / ``messageType`` code to a stable string.
Returns ``"text"`` for code ``1``, ``"divider"`` for code ``-2``, and
``"unknown"`` for any other / missing / unparsable value.
Never raises: an unexpected type must not break the scrape. The raw code
is preserved on ``raw_json`` so the persistence layer can recover it.
"""
try:
code = int(raw)
except (TypeError, ValueError):
return "unknown"
return _WECHAT_TYPE_NAMES.get(code, "unknown")