@@ -99,6 +99,7 @@ class LarkTopicEventDecisionReason(str, Enum):
9999 BINDING_UNAVAILABLE = "binding_unavailable"
100100 CHAT_MISMATCH = "chat_mismatch"
101101 TOPIC_MISMATCH = "topic_mismatch"
102+ ROUTE_AMBIGUOUS = "route_ambiguous"
102103 SELF_MESSAGE = "self_message"
103104 INVALID_ROUTING_STATE = "invalid_routing_state"
104105 NOT_ADDRESSED = "not_addressed"
@@ -825,6 +826,7 @@ def list_lark_connections(
825826 "binding_unavailable" ,
826827 "chat_mismatch" ,
827828 "topic_mismatch" ,
829+ "route_ambiguous" ,
828830 }
829831 ):
830832 event_blocker = "lark_event_route_mismatch"
@@ -1034,7 +1036,7 @@ def decide_lark_topic_event(
10341036 }
10351037 has_binding = False
10361038 matched_chat = False
1037- matched_topic = False
1039+ candidates : list [ dict [ str , Any ]] = []
10381040 for goal_id , payload in binding_payloads .items ():
10391041 binding = binding_for_goal (payload , goal_id )
10401042 if not binding or binding .get ("enabled" ) is not True :
@@ -1045,27 +1047,14 @@ def decide_lark_topic_event(
10451047 if target is None :
10461048 continue
10471049 channel = target .get ("channel" ) if isinstance (target .get ("channel" ), Mapping ) else {}
1050+ if str (channel .get ("chat_id" ) or "" ) != chat_id :
1051+ continue
1052+ matched_chat = True
10481053 identity = target .get ("identity" ) if isinstance (target .get ("identity" ), Mapping ) else {}
10491054 topic = binding .get ("topic" ) if isinstance (binding .get ("topic" ), Mapping ) else {}
10501055 binding_channel = binding .get ("channel" ) if isinstance (binding .get ("channel" ), Mapping ) else {}
10511056 topic_root = str (topic .get ("root_message_id" ) or binding_channel .get ("pinned_message_id" ) or "" )
10521057 routing = binding .get ("routing" ) if isinstance (binding .get ("routing" ), Mapping ) else {}
1053- if str (channel .get ("chat_id" ) or "" ) != chat_id :
1054- continue
1055- matched_chat = True
1056- if topic_root != root_id :
1057- continue
1058- matched_topic = True
1059- sender_id = str (event .get ("sender_id" ) or "" )
1060- if sender_id and sender_id in {
1061- str (identity .get ("bot_app_id" ) or "" ),
1062- str (identity .get ("bot_open_id" ) or "" ),
1063- }:
1064- return {
1065- "matched" : False ,
1066- "reason" : LarkTopicEventDecisionReason .SELF_MESSAGE .value ,
1067- "route" : None ,
1068- }
10691058 try :
10701059 capture_scope = _routing_value (
10711060 CaptureScope ,
@@ -1108,57 +1097,103 @@ def decide_lark_topic_event(
11081097 "reason" : LarkTopicEventDecisionReason .INVALID_ROUTING_STATE .value ,
11091098 "route" : None ,
11101099 }
1111- if capture_scope != "configured_chat_all" and not is_event_addressed_to_bot (
1112- event , identity
1113- ):
1114- return {
1115- "matched" : False ,
1116- "reason" : LarkTopicEventDecisionReason .NOT_ADDRESSED .value ,
1117- "route" : None ,
1100+ candidates .append (
1101+ {
1102+ "goal_id" : goal_id ,
1103+ "binding" : binding ,
1104+ "identity" : identity ,
1105+ "target_ref" : target_ref ,
1106+ "topic_root" : topic_root ,
1107+ "capture_scope" : capture_scope ,
1108+ "ingress_mode" : ingress_mode ,
1109+ "reply_mode" : reply_mode ,
1110+ "routing" : routing ,
1111+ "connector" : connector ,
11181112 }
1119- route = {
1120- "app_ref" : str (identity .get ("sender_profile" ) or "default" ),
1121- "goal_id" : goal_id ,
1122- "message_id" : message_id ,
1123- "reply_mode" : reply_mode ,
1124- "target_ref" : target_ref ,
1125- "topic_root_message_id" : topic_root ,
1113+ )
1114+
1115+ exact_topic_candidates = [
1116+ candidate for candidate in candidates if candidate ["topic_root" ] == root_id
1117+ ]
1118+ eligible = exact_topic_candidates or [
1119+ candidate
1120+ for candidate in candidates
1121+ if candidate ["capture_scope" ] == CaptureScope .CONFIGURED_CHAT_ALL .value
1122+ ]
1123+ if len (eligible ) > 1 :
1124+ return {
1125+ "matched" : False ,
1126+ "reason" : LarkTopicEventDecisionReason .ROUTE_AMBIGUOUS .value ,
1127+ "route" : None ,
11261128 }
1127- if ingress_mode != "direct_session" or binding .get ("agent_id" ):
1128- route .update (
1129- {
1130- "agent_id" : str (binding .get ("agent_id" ) or "" ),
1131- "session_id" : str (binding .get ("session_id" ) or "" ),
1132- "capture_scope" : capture_scope ,
1133- "ingress_mode" : ingress_mode ,
1134- "inbox_config_ref" : str (routing .get ("inbox_config_ref" ) or "" ),
1135- ** (
1136- {"connector" : dict (connector )}
1137- if isinstance (connector , Mapping )
1138- else {}
1139- ),
1140- ** (
1141- {"event_id" : str (event .get ("event_id" ) or message_id )}
1142- if isinstance (connector , Mapping )
1143- else {}
1144- ),
1145- }
1146- )
1129+ if not eligible :
1130+ reason = (
1131+ LarkTopicEventDecisionReason .BINDING_UNAVAILABLE
1132+ if not has_binding
1133+ else LarkTopicEventDecisionReason .CHAT_MISMATCH
1134+ if not matched_chat
1135+ else LarkTopicEventDecisionReason .TOPIC_MISMATCH
1136+ )
1137+ return {"matched" : False , "reason" : reason .value , "route" : None }
1138+
1139+ selected = eligible [0 ]
1140+ identity = selected ["identity" ]
1141+ sender_id = str (event .get ("sender_id" ) or "" )
1142+ if sender_id and sender_id in {
1143+ str (identity .get ("bot_app_id" ) or "" ),
1144+ str (identity .get ("bot_open_id" ) or "" ),
1145+ }:
1146+ return {
1147+ "matched" : False ,
1148+ "reason" : LarkTopicEventDecisionReason .SELF_MESSAGE .value ,
1149+ "route" : None ,
1150+ }
1151+ capture_scope = str (selected ["capture_scope" ])
1152+ if capture_scope != CaptureScope .CONFIGURED_CHAT_ALL .value and not is_event_addressed_to_bot (
1153+ event , identity
1154+ ):
11471155 return {
1148- "matched" : True ,
1149- "reason" : LarkTopicEventDecisionReason .MATCHED .value ,
1150- "route" : route ,
1156+ "matched" : False ,
1157+ "reason" : LarkTopicEventDecisionReason .NOT_ADDRESSED .value ,
1158+ "route" : None ,
11511159 }
1152- reason = (
1153- LarkTopicEventDecisionReason .BINDING_UNAVAILABLE
1154- if not has_binding
1155- else LarkTopicEventDecisionReason .CHAT_MISMATCH
1156- if not matched_chat
1157- else LarkTopicEventDecisionReason .TOPIC_MISMATCH
1158- if not matched_topic
1159- else LarkTopicEventDecisionReason .BINDING_UNAVAILABLE
1160- )
1161- return {"matched" : False , "reason" : reason .value , "route" : None }
1160+ binding = selected ["binding" ]
1161+ ingress_mode = str (selected ["ingress_mode" ])
1162+ routing = selected ["routing" ]
1163+ connector = selected ["connector" ]
1164+ route = {
1165+ "app_ref" : str (identity .get ("sender_profile" ) or "default" ),
1166+ "goal_id" : str (selected ["goal_id" ]),
1167+ "message_id" : message_id ,
1168+ "reply_mode" : str (selected ["reply_mode" ]),
1169+ "target_ref" : str (selected ["target_ref" ]),
1170+ "topic_root_message_id" : str (selected ["topic_root" ]),
1171+ }
1172+ if ingress_mode != IngressMode .DIRECT_SESSION .value or binding .get ("agent_id" ):
1173+ route .update (
1174+ {
1175+ "agent_id" : str (binding .get ("agent_id" ) or "" ),
1176+ "session_id" : str (binding .get ("session_id" ) or "" ),
1177+ "capture_scope" : capture_scope ,
1178+ "ingress_mode" : ingress_mode ,
1179+ "inbox_config_ref" : str (routing .get ("inbox_config_ref" ) or "" ),
1180+ ** (
1181+ {"connector" : dict (connector )}
1182+ if isinstance (connector , Mapping )
1183+ else {}
1184+ ),
1185+ ** (
1186+ {"event_id" : str (event .get ("event_id" ) or message_id )}
1187+ if isinstance (connector , Mapping )
1188+ else {}
1189+ ),
1190+ }
1191+ )
1192+ return {
1193+ "matched" : True ,
1194+ "reason" : LarkTopicEventDecisionReason .MATCHED .value ,
1195+ "route" : route ,
1196+ }
11621197
11631198
11641199def route_lark_topic_event (
0 commit comments