Skip to content

Commit fdc0fb6

Browse files
committed
feat: feishu_post_message to agentscope-ai#619
1 parent df0523d commit fdc0fb6

1 file changed

Lines changed: 84 additions & 4 deletions

File tree

src/copaw/app/channels/feishu/channel.py

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,9 @@ async def _on_message(self, data: "P2ImMessageReceiveV1") -> None:
532532
text = extract_json_key(content_raw, "text")
533533
if text:
534534
text_parts.append(text)
535+
elif msg_type == "post":
536+
# Handle rich text post messages
537+
text_parts.extend(self._extract_text_from_post(content_raw))
535538
elif msg_type == "image":
536539
image_key = extract_json_key(
537540
content_raw,
@@ -695,6 +698,79 @@ def _add_reaction_sync(self, message_id: str, emoji_type: str) -> None:
695698
except Exception as e:
696699
logger.debug("feishu reaction error: %s", e)
697700

701+
def _extract_text_from_post(
702+
self,
703+
content_raw: str,
704+
) -> List[str]: # pylint: disable=too-many-nested-blocks
705+
"""Extract text content from Feishu post message format.
706+
707+
Post format example:
708+
{
709+
"zh_cn": {
710+
"title": "...",
711+
"content": [[{"tag": "text", "text": "..."}]]
712+
}
713+
}
714+
715+
Returns list of text parts extracted from the post.
716+
"""
717+
# pylint: disable=too-many-nested-blocks
718+
try:
719+
post_content = json.loads(content_raw)
720+
if not isinstance(post_content, dict):
721+
return ["[post: invalid content format]"]
722+
723+
post_text_parts = []
724+
725+
# Get content from any language key (zh_cn, en_us, etc.)
726+
for lang_content in post_content.values():
727+
if not isinstance(lang_content, dict):
728+
continue
729+
if "content" not in lang_content:
730+
continue
731+
732+
content_rows = lang_content["content"]
733+
if not isinstance(content_rows, list):
734+
continue
735+
736+
for row in content_rows:
737+
if not isinstance(row, list):
738+
continue
739+
for element in row:
740+
if not isinstance(element, dict):
741+
continue
742+
743+
# Handle different tag types
744+
tag = element.get("tag")
745+
if tag == "text":
746+
post_text_parts.append(element.get("text", ""))
747+
elif tag == "md":
748+
post_text_parts.append(element.get("text", ""))
749+
elif tag == "at":
750+
# Handle @ mentions
751+
user_name = element.get("user_name", "")
752+
if user_name:
753+
post_text_parts.append(f"@{user_name}")
754+
# Add more tag types as needed
755+
756+
if post_text_parts:
757+
return ["\n".join(post_text_parts)]
758+
else:
759+
return ["[post: no text content]"]
760+
761+
except (json.JSONDecodeError, TypeError, KeyError) as e:
762+
# Truncate content for logging to avoid excessive log length
763+
if isinstance(content_raw, str):
764+
truncated_content = content_raw[:100]
765+
else:
766+
truncated_content = str(content_raw)[:100]
767+
logger.debug(
768+
"feishu post message parsing failed: %s, content=%s",
769+
e,
770+
truncated_content,
771+
)
772+
return ["[post: parse error]"]
773+
698774
async def _download_image_resource(
699775
self,
700776
message_id: str,
@@ -1411,10 +1487,14 @@ async def send_content_parts(
14111487
media_parts: List[OutgoingContentPart] = []
14121488
for p in parts:
14131489
t = getattr(p, "type", None)
1414-
if t == ContentType.TEXT and getattr(p, "text", None):
1415-
text_parts.append(p.text or "")
1416-
elif t == ContentType.REFUSAL and getattr(p, "refusal", None):
1417-
text_parts.append(p.refusal or "")
1490+
if t == ContentType.TEXT:
1491+
text_val = getattr(p, "text", None)
1492+
if text_val:
1493+
text_parts.append(text_val)
1494+
elif t == ContentType.REFUSAL:
1495+
refusal_val = getattr(p, "refusal", None)
1496+
if refusal_val:
1497+
text_parts.append(refusal_val)
14181498
elif t in (
14191499
ContentType.IMAGE,
14201500
ContentType.FILE,

0 commit comments

Comments
 (0)