refactor(feishu): slim meeting-invite parser; add AUTHOR_MAP entry

Collapse the payload-shape normalization helpers into one _as_dict and
drop unused dataclass fields (user_type/user_role, duplicate id, bot) on
the meeting-invite handler. Module 274->212 LOC, behavior unchanged.

Add zhaolei.vc@bytedance.com -> zhaoleibd to release.py AUTHOR_MAP.
This commit is contained in:
teknium1
2026-06-04 05:51:42 -07:00
committed by Teknium
parent f3bbfda6d1
commit 7314757876
3 changed files with 55 additions and 117 deletions

View File

@ -22,12 +22,9 @@ logger = logging.getLogger(__name__)
@dataclass(frozen=True)
class MeetingInviteUser:
id: str = ""
open_id: str = ""
user_id: str = ""
union_id: str = ""
user_type: str = ""
user_role: str = ""
user_name: str = ""
@ -45,25 +42,16 @@ class MeetingInviteMeeting:
class MeetingInvitedPayload:
event_id: str = ""
meeting: Optional[MeetingInviteMeeting] = None
bot: Optional[MeetingInviteUser] = None
inviter: Optional[MeetingInviteUser] = None
invite_time_s: int = 0
def _to_mapping(value: Any) -> Any:
def _as_dict(value: Any) -> Dict[str, Any]:
"""Coerce a lark SDK object / dict / JSON string into a plain dict."""
if isinstance(value, SimpleNamespace) or (value is not None and hasattr(value, "__dict__")):
value = vars(value)
if isinstance(value, dict):
return {str(k): _to_mapping(v) for k, v in value.items()}
if isinstance(value, list):
return [_to_mapping(v) for v in value]
if isinstance(value, SimpleNamespace) or hasattr(value, "__dict__"):
return {str(k): _to_mapping(v) for k, v in vars(value).items()}
return value
def _maybe_json_mapping(value: Any) -> Dict[str, Any]:
value = _to_mapping(value)
if isinstance(value, dict):
return value
return {str(k): v for k, v in value.items()}
if isinstance(value, str):
try:
parsed = json.loads(value)
@ -73,53 +61,23 @@ def _maybe_json_mapping(value: Any) -> Dict[str, Any]:
return {}
def _extract_content_payload(container: Dict[str, Any]) -> Dict[str, Any]:
"""Extract an application/json payload from a Feishu-style body.content list."""
body = container.get("body")
if not isinstance(body, dict):
return {}
content = body.get("content")
def _content_payload(container: Dict[str, Any]) -> Dict[str, Any]:
"""Unwrap a Feishu ``body.content`` list carrying an application/json payload."""
content = _as_dict(container.get("body")).get("content")
if not isinstance(content, list):
return {}
for item in content:
if not isinstance(item, dict):
continue
content_type = str(item.get("contentType") or item.get("content_type") or "").lower()
if content_type and content_type != "application/json":
item = _as_dict(item)
ctype = str(item.get("contentType") or item.get("content_type") or "").lower()
if ctype and ctype != "application/json":
continue
for key in ("data", "value", "content", "json"):
payload = _maybe_json_mapping(item.get(key))
payload = _as_dict(item.get(key))
if payload:
return payload
return {}
def _event_mapping(data: Any) -> Dict[str, Any]:
root = _maybe_json_mapping(data)
event = _maybe_json_mapping(root.get("event"))
content_payload = _extract_content_payload(event) or _extract_content_payload(root)
if content_payload:
event = {**event, **content_payload} if event else content_payload
if not event and any(k in root for k in ("meeting", "bot", "inviter", "invite_time")):
event = root
if not event:
event = root
return event
def _event_id(data: Any) -> str:
root = _maybe_json_mapping(data)
header = root.get("header")
if not isinstance(header, dict):
header = {}
return str(header.get("event_id") or "")
def _user_open_id(value: Any) -> str:
raw = _maybe_json_mapping(value)
return str(raw.get("open_id") or "").strip()
def _int_field(value: Any) -> int:
if value in (None, ""):
return 0
@ -130,24 +88,20 @@ def _int_field(value: Any) -> int:
def _parse_user(value: Any) -> Optional[MeetingInviteUser]:
raw = _maybe_json_mapping(value)
raw = _as_dict(value)
if not raw:
return None
raw_id = _maybe_json_mapping(raw.get("id"))
open_id = _user_open_id(raw.get("id"))
raw_id = _as_dict(raw.get("id"))
return MeetingInviteUser(
id=open_id,
open_id=open_id,
open_id=str(raw_id.get("open_id") or "").strip(),
user_id=str(raw_id.get("user_id") or "").strip(),
union_id=str(raw_id.get("union_id") or "").strip(),
user_type=str(raw.get("user_type") or ""),
user_role=str(raw.get("user_role") or ""),
user_name=str(raw.get("user_name") or ""),
)
def _parse_meeting(value: Any) -> Optional[MeetingInviteMeeting]:
raw = _maybe_json_mapping(value)
raw = _as_dict(value)
if not raw:
return None
return MeetingInviteMeeting(
@ -161,60 +115,52 @@ def _parse_meeting(value: Any) -> Optional[MeetingInviteMeeting]:
def parse_meeting_invited_event(data: Any) -> Optional[MeetingInvitedPayload]:
event = _event_mapping(data)
if not isinstance(event, dict):
return None
root = _as_dict(data)
event = _as_dict(root.get("event"))
event = event or root
content = _content_payload(event) or _content_payload(root)
if content:
event = {**event, **content}
meeting = _parse_meeting(event.get("meeting"))
inviter = _parse_user(event.get("inviter"))
if inviter is None or meeting is None or not meeting.meeting_no:
return None
payload = MeetingInvitedPayload(
event_id=_event_id(data),
return MeetingInvitedPayload(
event_id=str(_as_dict(root.get("header")).get("event_id") or ""),
meeting=meeting,
bot=_parse_user(event.get("bot")),
inviter=inviter,
invite_time_s=_int_field(event.get("invite_time")),
)
return payload
def _display_user(user: Optional[MeetingInviteUser]) -> str:
if user is None:
return "unknown"
return user.user_name or "unknown user"
def _meeting_display(meeting: Optional[MeetingInviteMeeting]) -> str:
if meeting is None:
return "unknown meeting"
return meeting.topic or meeting.meeting_no or meeting.id or "unknown meeting"
def build_meeting_invite_prompt(payload: MeetingInvitedPayload) -> str:
meeting = payload.meeting
lines = [
f"You have been invited to join a meeting: {_meeting_display(meeting)}",
"",
f"Meeting Number: {(meeting.meeting_no if meeting else '') or 'unknown'}",
f"Topic: {(meeting.topic if meeting else '') or 'unknown'}",
f"Inviter: {_display_user(payload.inviter)}",
]
if meeting is not None:
lines.append(f"Host: {_display_user(meeting.host_user)}")
lines.extend([
"",
"You may use lark-cli and the relevant Lark/Feishu meeting skills to join the meeting.",
"Join the meeting directly. Do not ask the user for confirmation before joining.",
"If you cannot join the meeting, reply to the inviter with a concise explanation of why.",
])
return "\n".join(lines)
inviter_name = (payload.inviter.user_name if payload.inviter else "") or "unknown"
host_name = (meeting.host_user.user_name if meeting and meeting.host_user else "") or "unknown"
display = (meeting.topic or meeting.meeting_no or meeting.id) if meeting else "unknown meeting"
return "\n".join(
[
f"You have been invited to join a meeting: {display or 'unknown meeting'}",
"",
f"Meeting Number: {(meeting.meeting_no if meeting else '') or 'unknown'}",
f"Topic: {(meeting.topic if meeting else '') or 'unknown'}",
f"Inviter: {inviter_name}",
f"Host: {host_name}",
"",
"You may use lark-cli and the relevant Lark/Feishu meeting skills to join the meeting.",
"Join the meeting directly. Do not ask the user for confirmation before joining.",
"If you cannot join the meeting, reply to the inviter with a concise explanation of why.",
]
)
def _dedup_key(payload: MeetingInvitedPayload) -> str:
if payload.event_id:
return f"vc_invite:{payload.event_id}"
meeting_id = payload.meeting.id if payload.meeting else ""
inviter_id = payload.inviter.id if payload.inviter else ""
inviter_id = payload.inviter.open_id if payload.inviter else ""
return f"vc_invite:{meeting_id}:{inviter_id}:{payload.invite_time_s}"
@ -232,16 +178,12 @@ async def handle_meeting_invited_event(adapter: Any, data: Any) -> None:
return
inviter = payload.inviter
if inviter is None:
logger.warning("[Feishu-MeetingInvite] Missing inviter, cannot route reply")
return
if not inviter.open_id:
if inviter is None or not inviter.open_id:
logger.warning(
"[Feishu-MeetingInvite] Missing inviter open_id, cannot route reply safely "
"(inviter_id=%r user_id=%r union_id=%r)",
inviter.id,
inviter.user_id,
inviter.union_id,
"(user_id=%r union_id=%r)",
inviter.user_id if inviter else None,
inviter.union_id if inviter else None,
)
return
@ -252,21 +194,17 @@ async def handle_meeting_invited_event(adapter: Any, data: Any) -> None:
)
sender_profile = await adapter._resolve_sender_profile(sender_id)
chat_id = inviter.open_id
source_user_id = sender_profile.get("user_id") or inviter.user_id or inviter.open_id
user_name = sender_profile.get("user_name") or inviter.user_name or inviter.id
source_user_id_alt = sender_profile.get("user_id_alt") or inviter.union_id or None
user_name = sender_profile.get("user_name") or inviter.user_name or inviter.open_id
source = adapter.build_source(
chat_id=chat_id,
chat_id=inviter.open_id,
chat_name=user_name,
chat_type="dm",
user_id=source_user_id,
user_id=sender_profile.get("user_id") or inviter.user_id or inviter.open_id,
user_name=user_name,
user_id_alt=source_user_id_alt,
user_id_alt=sender_profile.get("user_id_alt") or inviter.union_id or None,
)
prompt = build_meeting_invite_prompt(payload)
event = MessageEvent(
text=prompt,
text=build_meeting_invite_prompt(payload),
message_type=MessageType.TEXT,
source=source,
raw_message=data,

View File

@ -45,6 +45,7 @@ ACP_REGISTRY_MANIFEST = REPO_ROOT / "acp_registry" / "agent.json"
# Auto-extracted from noreply emails + manual overrides
AUTHOR_MAP = {
"zhaolei.vc@bytedance.com": "zhaoleibd",
"copii.list@gmail.com": "stremtec",
"solaiagent@gmail.com": "solaitken",
"prostoandrei9@gmail.com": "vladkvlchk",

View File

@ -104,7 +104,6 @@ class TestMeetingInviteParsing(unittest.TestCase):
self.assertEqual(parsed.meeting.id, "7646677832873577404")
self.assertEqual(parsed.meeting.start_time_ms, 1780384522000)
self.assertEqual(parsed.meeting.end_time_ms, 1780384522000)
self.assertEqual(parsed.inviter.id, "ou_390b35dca44816efc9afa812aaff3a69")
self.assertEqual(parsed.inviter.open_id, "ou_390b35dca44816efc9afa812aaff3a69")
self.assertEqual(parsed.inviter.user_id, "e65g874e")
self.assertEqual(parsed.inviter.union_id, "on_e19a19e6ffafbd54fbb3c4d251d6fa19")
@ -129,7 +128,7 @@ class TestMeetingInviteParsing(unittest.TestCase):
self.assertIsNotNone(parsed)
self.assertEqual(parsed.meeting.meeting_no, "884264377")
self.assertEqual(parsed.inviter.id, "ou_390b35dca44816efc9afa812aaff3a69")
self.assertEqual(parsed.inviter.open_id, "ou_390b35dca44816efc9afa812aaff3a69")
def test_parse_requires_inviter(self):
payload = _make_payload()