lark-oapi 2.0.0.dev2__py3-none-any.whl → 2.0.0.dev4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -70,6 +70,7 @@ from .config import (
70
70
  from .driver import LarkClientDriver
71
71
  from .errors import FeishuChannelError, FeishuChannelErrorCode
72
72
  from .identity import IdentityResolver, NameCache
73
+ from .normalize.comment import normalize_comment
73
74
  from .normalize.dedup import Deduper, InMemoryDedupStore
74
75
  from .normalize.pipeline import InboundPipeline, PipelineConfig, PipelineDeps
75
76
  from .outbound.routing import infer_receive_id_type
@@ -95,6 +96,22 @@ EventHandler = Callable[..., Any]
95
96
  Unsubscribe = Callable[[], None]
96
97
 
97
98
 
99
+ def _card_action_identity(action: Any) -> str:
100
+ """Stable dedup fragment for a card action.
101
+
102
+ Node-SDK aligned: different buttons on the same card click to different
103
+ ``(tag, value)`` pairs, and those must dedup-distinctly; a genuine WS
104
+ redelivery of the *same* click hashes identically and is suppressed.
105
+ """
106
+ tag = getattr(action, "tag", "") or ""
107
+ value = getattr(action, "value", None)
108
+ try:
109
+ value_repr = json.dumps(value, sort_keys=True, ensure_ascii=False)
110
+ except (TypeError, ValueError):
111
+ value_repr = repr(value)
112
+ return f"{tag}:{value_repr}"
113
+
114
+
98
115
  # ---------------------------------------------------------------------------
99
116
  # FeishuChannel
100
117
  # ---------------------------------------------------------------------------
@@ -771,6 +788,18 @@ class FeishuChannel:
771
788
  b = getattr(b, register)(handler)
772
789
  except Exception: # pragma: no cover
773
790
  pass
791
+ # drive.notice.comment_add_v1 has no typed processor in the
792
+ # generated SDK. The wire payload may arrive under either schema:
793
+ # the legacy callback channel uses p1 (event has ``uuid``), but
794
+ # the modern WS frontier wraps the same event in a p2 envelope
795
+ # (``schema=2.0``). Register the customized-event handler under
796
+ # both so neither path logs "processor not found".
797
+ b = b.register_p1_customized_event(
798
+ "drive.notice.comment_add_v1", self._on_p1_comment_add
799
+ )
800
+ b = b.register_p2_customized_event(
801
+ "drive.notice.comment_add_v1", self._on_p1_comment_add
802
+ )
774
803
  return b.build()
775
804
 
776
805
  # ------------------------------------------------------------------
@@ -804,6 +833,9 @@ class FeishuChannel:
804
833
  def _on_p2_message_read(self, data: Any) -> None:
805
834
  self.schedule(self._handle_message_read_event(data))
806
835
 
836
+ def _on_p1_comment_add(self, data: Any) -> None:
837
+ self.schedule(self._handle_comment_event(data))
838
+
807
839
  # ------------------------------------------------------------------
808
840
  # Async event handlers
809
841
  # ------------------------------------------------------------------
@@ -866,25 +898,80 @@ class FeishuChannel:
866
898
  operator_open_id = getattr(
867
899
  getattr(event, "operator", None), "open_id", None
868
900
  )
869
- await self._invoke(
870
- "cardAction",
871
- CardActionEvent(
872
- message_id=message_id or "",
873
- chat_id=chat_id or "",
874
- operator=EventOperator(open_id=operator_open_id or ""),
875
- action=CardActionPayload(
876
- tag=tag or "",
877
- value=raw_value,
878
- name=getattr(raw_value, "name", None)
879
- if hasattr(raw_value, "name")
880
- else None,
881
- ),
882
- raw=_coerce.obj_to_dict(data) or {},
901
+ payload = CardActionEvent(
902
+ message_id=message_id or "",
903
+ chat_id=chat_id or "",
904
+ operator=EventOperator(open_id=operator_open_id or ""),
905
+ action=CardActionPayload(
906
+ tag=tag or "",
907
+ value=raw_value,
908
+ name=getattr(raw_value, "name", None)
909
+ if hasattr(raw_value, "name")
910
+ else None,
883
911
  ),
912
+ raw=_coerce.obj_to_dict(data) or {},
913
+ )
914
+ # Route through safety.push_action (tier 2): dedup on a stable
915
+ # action identity (tag + value payload) so Feishu's at-least-once
916
+ # WS redelivery can't double-invoke the handler, and serialize by
917
+ # chat_id so two fast clicks in the same chat are processed in
918
+ # order. Node-SDK aligned.
919
+ await self._through_action_safety(
920
+ event_id=f"card:{payload.message_id}:{payload.operator.open_id}:"
921
+ f"{_card_action_identity(payload.action)}",
922
+ queue_scope=payload.chat_id or payload.message_id or "",
923
+ handler=lambda: self._invoke("cardAction", payload),
884
924
  )
885
925
  except Exception as e:
886
926
  logger.exception("FeishuChannel cardAction dispatch failed: %s", e)
887
927
 
928
+ async def _through_action_safety(
929
+ self,
930
+ *,
931
+ event_id: str,
932
+ queue_scope: str,
933
+ handler: Callable[[], Any],
934
+ ) -> None:
935
+ """Run ``handler`` through the safety tier-2 gate (dedup + lock +
936
+ per-scope serial queue) when the pipeline exists; fall back to a
937
+ direct invocation when it hasn't been built yet (early events during
938
+ startup, unit tests that bypass ``connect``)."""
939
+ safety = self._safety
940
+ if safety is None:
941
+ result = handler()
942
+ if inspect.isawaitable(result):
943
+ await result
944
+ return
945
+
946
+ async def _run() -> None:
947
+ result = handler()
948
+ if inspect.isawaitable(result):
949
+ await result
950
+
951
+ await safety.push_action(event_id, queue_scope or event_id, _run)
952
+
953
+ async def _through_light_safety(
954
+ self,
955
+ *,
956
+ event_id: str,
957
+ handler: Callable[[], Any],
958
+ ) -> None:
959
+ """Tier-3 variant: dedup only (reaction add/remove). Same fallback
960
+ semantics as :meth:`_through_action_safety`."""
961
+ safety = self._safety
962
+ if safety is None:
963
+ result = handler()
964
+ if inspect.isawaitable(result):
965
+ await result
966
+ return
967
+
968
+ async def _run() -> None:
969
+ result = handler()
970
+ if inspect.isawaitable(result):
971
+ await result
972
+
973
+ await safety.push_light(event_id, _run)
974
+
888
975
  async def _handle_reaction_event(self, data: Any, *, action: str) -> None:
889
976
  cfg = self._config.inbound.reaction_notifications
890
977
  if cfg == "off":
@@ -905,16 +992,25 @@ class FeishuChannel:
905
992
  if message_id and message_id not in self._sent_messages:
906
993
  return
907
994
 
908
- await self._invoke(
909
- "reaction",
910
- ReactionEvent(
911
- message_id=message_id,
912
- operator=EventOperator(open_id=operator_open_id or ""),
913
- emoji_type=emoji_type or "",
914
- action="added" if action == "create" else "removed",
915
- action_time=action_time,
916
- raw=_coerce.obj_to_dict(data) or {},
995
+ direction = "added" if action == "create" else "removed"
996
+ payload = ReactionEvent(
997
+ message_id=message_id,
998
+ operator=EventOperator(open_id=operator_open_id or ""),
999
+ emoji_type=emoji_type or "",
1000
+ action=direction,
1001
+ action_time=action_time,
1002
+ raw=_coerce.obj_to_dict(data) or {},
1003
+ )
1004
+ # Tier 3: dedup only. Reactions are idempotent state changes so
1005
+ # lock / serial queue would add latency for no benefit, but
1006
+ # WS redelivery would double-invoke without this guard.
1007
+ # Node-SDK aligned (pushLight).
1008
+ await self._through_light_safety(
1009
+ event_id=(
1010
+ f"reaction:{message_id}:{operator_open_id or ''}:"
1011
+ f"{emoji_type or ''}:{direction}"
917
1012
  ),
1013
+ handler=lambda: self._invoke("reaction", payload),
918
1014
  )
919
1015
  except Exception as e:
920
1016
  logger.exception("FeishuChannel reaction dispatch failed: %s", e)
@@ -961,6 +1057,36 @@ class FeishuChannel:
961
1057
  except Exception as e:
962
1058
  logger.exception("FeishuChannel messageRead dispatch failed: %s", e)
963
1059
 
1060
+ async def _handle_comment_event(self, data: Any) -> None:
1061
+ try:
1062
+ # ``CustomizedEvent.event`` is the raw inner event payload as a
1063
+ # plain dict; the per-event timestamp lives on the envelope
1064
+ # (``header.create_time`` for p2, ``ts`` for p1) — not in the
1065
+ # inner dict — so pass it explicitly.
1066
+ raw_event = getattr(data, "event", None)
1067
+ header = getattr(data, "header", None)
1068
+ envelope_ts = (
1069
+ getattr(header, "create_time", None) if header is not None
1070
+ else getattr(data, "ts", None)
1071
+ )
1072
+ normalized = normalize_comment(
1073
+ raw_event if raw_event is not None else data,
1074
+ bot_open_id=self._bot_open_id,
1075
+ envelope_timestamp=envelope_ts,
1076
+ )
1077
+ if normalized is None:
1078
+ return
1079
+ # Tier 2: dedup + lock + per-file_token serial queue. Multiple
1080
+ # comments on the same document are ordered; redeliveries of the
1081
+ # same comment event are dropped. Node-SDK aligned.
1082
+ await self._through_action_safety(
1083
+ event_id=f"comment:{normalized.file_token}:{normalized.comment_id}",
1084
+ queue_scope=normalized.file_token,
1085
+ handler=lambda: self._invoke("comment", normalized),
1086
+ )
1087
+ except Exception as e:
1088
+ logger.exception("FeishuChannel comment dispatch failed: %s", e)
1089
+
964
1090
  def _notify_reconnecting(self) -> None:
965
1091
  for h in list(self._handlers.get("reconnecting", [])):
966
1092
  try:
@@ -1,7 +1,33 @@
1
- """Normalize `drive.notice.comment_add_v1` events.
1
+ """Normalize ``drive.notice.comment_add_v1`` events.
2
2
 
3
- Produces a `CommentEvent` with `file_token` / `file_type` / `comment_id` /
4
- `operator` fields extracted from the raw event payload.
3
+ Wire payload (node-aligned, observed end-to-end via channel-test-harness
4
+ TC-317 against a real Feishu tenant 2026-04-27)::
5
+
6
+ {
7
+ "file_token": "...",
8
+ "file_type": "docx",
9
+ "comment_id": "...",
10
+ "reply_id": "...",
11
+ "is_mentioned": true,
12
+ "create_time": "1700000000000", # ms, string
13
+ "notice_meta": {
14
+ "from_user_id": { "open_id": "...", "user_id": "...", "union_id": "..." },
15
+ "to_user_id": { ... },
16
+ "file_token": "...",
17
+ "file_type": "docx",
18
+ "timestamp": "1700000000000",
19
+ "is_mentioned": true,
20
+ "notice_type": "comment_add"
21
+ },
22
+ // Legacy fallbacks (older p1 callbacks):
23
+ "user_id": { "open_id": "...", ... },
24
+ "is_mention": true,
25
+ "action_time": "1700000000000"
26
+ }
27
+
28
+ The operator lives at ``notice_meta.from_user_id`` (top-level
29
+ ``user_id`` is the legacy fallback). Whether the bot was mentioned is a
30
+ boolean flag (``is_mentioned``), not a probe of the mentions array.
5
31
  """
6
32
 
7
33
  from dataclasses import dataclass, field
@@ -31,10 +57,23 @@ def normalize_comment(
31
57
  data: Any,
32
58
  *,
33
59
  bot_open_id: Optional[str] = None,
60
+ envelope_timestamp: Optional[str] = None,
34
61
  ) -> Optional[CommentEvent]:
35
- """Flatten the raw drive.notice.comment_add_v1 payload.
62
+ """Flatten the raw ``drive.notice.comment_add_v1`` payload.
63
+
64
+ Accepts either the whole envelope (with ``event`` key) or the inner
65
+ event dict. Returns ``None`` when the payload is malformed or missing
66
+ one of the required fields (``file_token`` / ``file_type`` /
67
+ ``comment_id`` / operator open_id).
36
68
 
37
- Accepts either the whole envelope (with `event` key) or the inner event dict.
69
+ ``envelope_timestamp`` is the ``header.create_time`` (p2) or top-level
70
+ ``ts`` (p1) carried by the WS/HTTP envelope. The inner event payload
71
+ omits a per-event timestamp on the wire, so the envelope is the only
72
+ reliable source — pass it from the dispatcher callback.
73
+
74
+ ``bot_open_id`` is unused — the bot-mention signal is sourced from the
75
+ payload's ``is_mentioned`` flag instead. The parameter is kept for
76
+ backward compatibility with the previous (broken) implementation.
38
77
  """
39
78
  if not isinstance(data, dict):
40
79
  data = _try_dict(data)
@@ -46,30 +85,50 @@ def normalize_comment(
46
85
  notice_meta = event.get("notice_meta") if isinstance(event.get("notice_meta"), dict) else {}
47
86
 
48
87
  file_token = event.get("file_token") or notice_meta.get("file_token") or ""
49
- if not file_token:
50
- return None
51
88
  file_type = event.get("file_type") or notice_meta.get("file_type") or ""
52
89
  comment_id = event.get("comment_id") or notice_meta.get("comment_id") or ""
53
- if not comment_id:
90
+
91
+ # Operator: prefer notice_meta.from_user_id (current p2 wire format),
92
+ # fall back to top-level user_id (legacy p1 callback shape). The old
93
+ # path looked at ``event.operator`` / ``event.operator_id`` — neither
94
+ # is in the actual payload, so operator came back null.
95
+ user_id_obj = notice_meta.get("from_user_id") or event.get("user_id") or {}
96
+ if not isinstance(user_id_obj, dict):
97
+ user_id_obj = {}
98
+ operator_open_id = user_id_obj.get("open_id")
99
+
100
+ # Required-field gate (node-aligned). Missing operator open_id is a
101
+ # malformed payload — drop rather than deliver a half-populated event.
102
+ if not (file_token and file_type and comment_id and operator_open_id):
54
103
  return None
104
+
55
105
  reply_id = event.get("reply_id") or notice_meta.get("reply_id") or None
56
106
 
57
- operator_raw = event.get("operator") or event.get("operator_id") or notice_meta.get("operator") or {}
58
107
  op = CommentOperator(
59
- open_id=(operator_raw or {}).get("open_id"),
60
- user_id=(operator_raw or {}).get("user_id"),
61
- union_id=(operator_raw or {}).get("union_id"),
108
+ open_id=operator_open_id,
109
+ user_id=user_id_obj.get("user_id"),
110
+ union_id=user_id_obj.get("union_id"),
62
111
  )
112
+
63
113
  mentioned_bot_flag = bool(
64
- bot_open_id and any(
65
- isinstance(m, dict) and m.get("user_id") == bot_open_id
66
- for m in (event.get("mentions") or [])
67
- )
114
+ event.get("is_mentioned")
115
+ if event.get("is_mentioned") is not None
116
+ else (notice_meta.get("is_mentioned") or event.get("is_mention"))
117
+ )
118
+
119
+ ts_str = (
120
+ event.get("create_time")
121
+ or notice_meta.get("timestamp")
122
+ or event.get("action_time")
123
+ or event.get("event_create_time")
124
+ or event.get("timestamp")
125
+ or envelope_timestamp
68
126
  )
69
127
  try:
70
- ts = int(event.get("event_create_time") or event.get("timestamp") or 0)
128
+ ts = int(ts_str) if ts_str is not None else 0
71
129
  except (TypeError, ValueError):
72
130
  ts = 0
131
+
73
132
  return CommentEvent(
74
133
  file_token=file_token,
75
134
  file_type=file_type,
@@ -20,6 +20,10 @@ from typing import Any, Dict, Iterable, List, Optional, Tuple
20
20
  from ..types import Mention
21
21
 
22
22
  _PLACEHOLDER_RE = re.compile(r"@_user_\d+")
23
+ # Matches the ``@_all`` mention-all placeholder Feishu embeds in text content
24
+ # for "@所有人" messages. Needs a non-word-char (or end-of-string) boundary
25
+ # after ``@_all`` so we don't misfire on, say, ``@_all_employees``.
26
+ _AT_ALL_RE = re.compile(r"@_all(?![A-Za-z0-9_])")
23
27
  _AT_TAG_RE = re.compile(
24
28
  r"<at\s+([^>]*?)>(?P<name>.*?)</at>",
25
29
  re.IGNORECASE | re.DOTALL,
@@ -27,6 +31,20 @@ _AT_TAG_RE = re.compile(
27
31
  _ATTR_RE = re.compile(r'([\w_-]+)\s*=\s*"([^"]*)"')
28
32
 
29
33
 
34
+ def text_has_mention_all(text: Optional[str]) -> bool:
35
+ """True if ``text`` contains the ``@_all`` placeholder.
36
+
37
+ Feishu does NOT populate ``event.message.mentions`` for ``@所有人``
38
+ messages in all cases — the only signal can be an ``@_all`` token
39
+ inside ``content.text``. Without detecting that, downstream policy
40
+ code sees ``mentioned_all=False`` and the ``mention_all_blocked``
41
+ gate never fires.
42
+ """
43
+ if not text:
44
+ return False
45
+ return _AT_ALL_RE.search(text) is not None
46
+
47
+
30
48
  # ---------------------------------------------------------------------------
31
49
  # Data shape
32
50
  # ---------------------------------------------------------------------------
@@ -155,6 +173,9 @@ def resolve_mentions(
155
173
  return f"@{m.name}"
156
174
 
157
175
  result = _PLACEHOLDER_RE.sub(_replace, content)
176
+ # Rewrite ``@_all`` placeholder to the human-visible form; without this
177
+ # user-visible content carries the raw token.
178
+ result = _AT_ALL_RE.sub("@所有人", result)
158
179
  if strip_bot_mentions:
159
180
  result = re.sub(r"\s{2,}", " ", result).strip()
160
181
  return result
@@ -25,7 +25,12 @@ from ..types import (
25
25
  from .dedup import Deduper
26
26
  from .flatten import flatten
27
27
  from .interactive import fetch_interactive
28
- from .mentions import extract_mentions, parse_at_tags, resolve_mentions
28
+ from .mentions import (
29
+ extract_mentions,
30
+ parse_at_tags,
31
+ resolve_mentions,
32
+ text_has_mention_all,
33
+ )
29
34
  from .merge_forward import MergeForwardExpander
30
35
  from .registry import parse_message_content
31
36
 
@@ -155,8 +160,18 @@ class InboundPipeline:
155
160
  mentions: List[Mention] = list(ext.mention_list)
156
161
  mentioned_all = ext.mentioned_all
157
162
  if isinstance(content, TextContent):
163
+ # Feishu frequently ships ``@所有人`` messages with
164
+ # ``mentions = null`` — the only signal is an ``@_all``
165
+ # placeholder in ``content.text``. Without this probe the
166
+ # policy gate never sees ``mentioned_all=True`` so
167
+ # ``respond_to_mention_all`` / ``mention_all_blocked`` go
168
+ # silently skipped (TC-505).
169
+ if not mentioned_all and text_has_mention_all(content.text):
170
+ mentioned_all = True
158
171
  content.text = resolve_mentions(content.text, ext)
159
172
  elif isinstance(content, PostContent):
173
+ if not mentioned_all and text_has_mention_all(content.text):
174
+ mentioned_all = True
160
175
  content.text = resolve_mentions(content.text, ext)
161
176
  at_mentions, at_all, stripped = parse_at_tags(content.text)
162
177
  content.text = stripped
lark_oapi/core/const.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # Info
2
2
  PROJECT = "oapi-sdk-python"
3
- VERSION = "2.0.0.dev2"
3
+ VERSION = "2.0.0.dev4"
4
4
 
5
5
  # Domain
6
6
  FEISHU_DOMAIN = "https://open.feishu.cn"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lark-oapi
3
- Version: 2.0.0.dev2
3
+ Version: 2.0.0.dev4
4
4
  Summary: Lark OpenAPI SDK for Python
5
5
  Home-page: https://github.com/larksuite/oapi-sdk-python
6
6
  Author: Wenbo Mao
@@ -10003,7 +10003,7 @@ lark_oapi/channel/__init__.py,sha256=yZeVV8Zeyv53YufjfX6lHrDblyeMmhMS2I40sHRv0kM
10003
10003
  lark_oapi/channel/_api_helpers.py,sha256=mv0pNmf1X2gG0gSCNfFXOFf_Y2Bl08FhOQCATc_vzqM,8874
10004
10004
  lark_oapi/channel/_coerce.py,sha256=nHdt-y6iHuo_Fp7ZdLFkydG1pTMM-_wQJGJbOzcUiEI,7490
10005
10005
  lark_oapi/channel/bot_identity.py,sha256=E2zMl1AYFCENi-HXtXH47WgZrWRpPMXiXyM38cvMdrw,5488
10006
- lark_oapi/channel/channel.py,sha256=KNvvf6-y3eTUr1Hn08lNbPnG_8dMx_2UFP6ofi_BHnI,54225
10006
+ lark_oapi/channel/channel.py,sha256=uZJXERef6N9QYqUXzt2PmLBTNY3ZK_gwJZeczJHB8C8,59676
10007
10007
  lark_oapi/channel/config.py,sha256=PEYUDqG_r3sOqAF7acrA3pDgVPmk16ChWVB71l1xd_U,9877
10008
10008
  lark_oapi/channel/driver.py,sha256=5Ij0WaQP3kEhUqZqUoREgfwv052118lyj2A6bCiIg7Y,11863
10009
10009
  lark_oapi/channel/errors.py,sha256=MbQIH95dxt3m99ofaMp_3oM1RJVvAMxW1A3SKiavAfk,7179
@@ -10017,13 +10017,13 @@ lark_oapi/channel/auth/uat_runner.py,sha256=pt4c99AJnIJzmPwNxngI6PrsrBK5Xpt5WDh8
10017
10017
  lark_oapi/channel/card/__init__.py,sha256=NqmfsU_1sWIubZ-QtrhWTlEcHY5H_THNaVPSakJsjl0,277
10018
10018
  lark_oapi/channel/card/builder.py,sha256=KbxbIBQTd0ojN0wWplxz1DdM-BK9Twllf_tUEXqw94w,11932
10019
10019
  lark_oapi/channel/normalize/__init__.py,sha256=8VPAoSV7LHKLKQAS7LnqgDC0jHwldykhRxnvlng70cw,940
10020
- lark_oapi/channel/normalize/comment.py,sha256=4DuUL1hfxsJhpvtAbQXydFL_ScJzssRljp06hvtdP7Q,2755
10020
+ lark_oapi/channel/normalize/comment.py,sha256=vmAKhCKXyzNSrB0WBvHDDDDtSKhO_zxyZSby1R-k1KQ,5054
10021
10021
  lark_oapi/channel/normalize/dedup.py,sha256=T8c2eUynJkOc83nEsxBxTSmaj0BFw8crjjVVGOuoY5I,3250
10022
10022
  lark_oapi/channel/normalize/flatten.py,sha256=vvvKhFFF_HXHC9fBZr_hhN6eV0DCq-vsfHXlHJrYku4,804
10023
10023
  lark_oapi/channel/normalize/interactive.py,sha256=EqwBy5EEW-wdfze4EQpBXQ7UIqzzxJNiBj8fSdWqSbM,2705
10024
- lark_oapi/channel/normalize/mentions.py,sha256=aQy4dKAEYBND17Ra1tZiQFgQtcc-bpgd7n5eCjKQfsk,6744
10024
+ lark_oapi/channel/normalize/mentions.py,sha256=w-hoYYw8QoiFP6Gf30l1MLSMJVfm4lpRURmGbOXufYU,7716
10025
10025
  lark_oapi/channel/normalize/merge_forward.py,sha256=F88wE2dNywQOUpPeoEJNiM4l3U5we1EtP_Cnrpa8YCs,8513
10026
- lark_oapi/channel/normalize/pipeline.py,sha256=VZ9h-F6RzkTP7I7uoS0lCz6s3Lbl80nVmlE3XUgJeUU,8979
10026
+ lark_oapi/channel/normalize/pipeline.py,sha256=s2UU3-BvBQaAPKHhK5nFbZzBHTVUbkdHqfWb9Td0Rpk,9623
10027
10027
  lark_oapi/channel/normalize/registry.py,sha256=xF-35gSSxwLjoHsWS3Cjz7n7K4rDMOIFvlXfLgqrEyg,11260
10028
10028
  lark_oapi/channel/normalize/converters/__init__.py,sha256=7hIl1hfFXkQ78EkY1WzVt4vmWThGJP2ll7ozp5jCCck,2202
10029
10029
  lark_oapi/channel/normalize/converters/_utils.py,sha256=VvDj6v6NwTSIY39Gsudsl4s6Hcq0inuAyfR87bYGrM8,1853
@@ -10073,7 +10073,7 @@ lark_oapi/channel/safety/processing_lock.py,sha256=G4Kcm8-aRCfrIKD_6aXu-inWG28tB
10073
10073
  lark_oapi/channel/safety/stale_detector.py,sha256=FKVBdYwRddpFynpRZxTyznxL3gkJVaoTXnn_iQ9uY04,691
10074
10074
  lark_oapi/channel/safety/types.py,sha256=WaWhUleLe21-57HTFH4cOwJpZb2OiwKuDxWxPAE0OYA,2124
10075
10075
  lark_oapi/core/__init__.py,sha256=z286YdAEQ4kRDGuKsZb0Osua5KMcg4yqBsLmKsdW1Hw,263
10076
- lark_oapi/core/const.py,sha256=Nzn_gukWAtdLCrjRSojYlIlYsdALTOBHU102XRWqUuA,608
10076
+ lark_oapi/core/const.py,sha256=6t5WIv00UT_LdpXnzL0WEpzYp1vhpQP0p2TMAwgJmnI,608
10077
10077
  lark_oapi/core/construct.py,sha256=CKsC-5bza7R01PDE2pWbyThlXi1oLAtjR0YD3Lex_sM,1980
10078
10078
  lark_oapi/core/enum.py,sha256=ehxuylOjD3btFHTRtpA--QvEcvUlWimuqXIqglcg4Kw,471
10079
10079
  lark_oapi/core/env_var.py,sha256=6xonyZzC_6Jyaotazj-ip0_yzB4wpZ4xArXIdPdPTck,169
@@ -10174,8 +10174,8 @@ lark_oapi/ws/pb/google/protobuf/pyext/cpp_message.py,sha256=GCWPkLCXd0QxY3yzNN-G
10174
10174
  lark_oapi/ws/pb/google/protobuf/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10175
10175
  lark_oapi/ws/pb/google/protobuf/util/json_format_pb2.py,sha256=4QRyr6gfGO_e2MNpt3z3MT1ZHfoR5SXRpmIs4aWrv6g,6375
10176
10176
  lark_oapi/ws/pb/google/protobuf/util/json_format_proto3_pb2.py,sha256=uA8FCLxKYYUrFOlpjSlyWQWbtpodzBMZrIxM25dyJhM,14610
10177
- lark_oapi-2.0.0.dev2.dist-info/licenses/LICENSE,sha256=N2ZgITrKZ1yufqVdfmr4ixMPp4qJ_ly-1_YeCs3w-Uk,1083
10178
- lark_oapi-2.0.0.dev2.dist-info/METADATA,sha256=Rslg-_LRvQwuubEyhu17sS60bTx-pSEfDiu_tlVslxo,4743
10179
- lark_oapi-2.0.0.dev2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
10180
- lark_oapi-2.0.0.dev2.dist-info/top_level.txt,sha256=sDloiIa3R4quQp19_e1mf206wAxH0Wg1bvNnKzV8QJg,10
10181
- lark_oapi-2.0.0.dev2.dist-info/RECORD,,
10177
+ lark_oapi-2.0.0.dev4.dist-info/licenses/LICENSE,sha256=N2ZgITrKZ1yufqVdfmr4ixMPp4qJ_ly-1_YeCs3w-Uk,1083
10178
+ lark_oapi-2.0.0.dev4.dist-info/METADATA,sha256=o25IQ1h_x-Q-Od785pKtEtIRmyxdvpIka9q5gqcN3SY,4743
10179
+ lark_oapi-2.0.0.dev4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
10180
+ lark_oapi-2.0.0.dev4.dist-info/top_level.txt,sha256=sDloiIa3R4quQp19_e1mf206wAxH0Wg1bvNnKzV8QJg,10
10181
+ lark_oapi-2.0.0.dev4.dist-info/RECORD,,