From 5290834b8b0df6734f01ba787b110b64216cdd49 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 17:48:12 +0000 Subject: [PATCH 1/3] Initial plan From def798bf1fa135f60bafcab96666997566fd82e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 17:52:43 +0000 Subject: [PATCH 2/3] fix: WeCom sender_name shows user ID instead of actual username - Add get_user_info() to WecomClient to fetch user name via /user/get API - Update WecomEventConverter.target2yiri to accept bot param and fetch real user name - Update register_listener call to pass self.bot for user name lookup - URL-encode userid parameter for safety Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> --- src/langbot/libs/wecom_api/api.py | 26 +++++++++++++++++++++++ src/langbot/pkg/platform/sources/wecom.py | 19 +++++++++++++---- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/langbot/libs/wecom_api/api.py b/src/langbot/libs/wecom_api/api.py index c3f1b76a..9e911a80 100644 --- a/src/langbot/libs/wecom_api/api.py +++ b/src/langbot/libs/wecom_api/api.py @@ -4,6 +4,7 @@ import base64 import binascii import httpx import traceback +from urllib.parse import quote from quart import Quart import xml.etree.ElementTree as ET from typing import Callable, Dict, Any @@ -67,6 +68,31 @@ class WecomClient: await self.logger.error(f'获取accesstoken失败:{response.json()}') raise Exception(f'未获取access token: {data}') + async def get_user_info(self, userid: str) -> dict: + """ + Get user information by user ID using the contacts secret. + + Args: + userid: The user ID to look up. + + Returns: + dict: User information including 'name' field. + """ + if not await self.check_access_token_for_contacts(): + self.access_token_for_contacts = await self.get_access_token(self.secret_for_contacts) + + url = self.base_url + '/user/get?access_token=' + self.access_token_for_contacts + '&userid=' + quote(userid) + async with httpx.AsyncClient() as client: + response = await client.get(url) + data = response.json() + if data.get('errcode') == 40014 or data.get('errcode') == 42001: + self.access_token_for_contacts = await self.get_access_token(self.secret_for_contacts) + return await self.get_user_info(userid) + if data.get('errcode', 0) != 0: + await self.logger.error(f'获取用户信息失败:{data}') + return {} + return data + async def get_users(self): if not self.check_access_token_for_contacts(): self.access_token_for_contacts = await self.get_access_token(self.secret_for_contacts) diff --git a/src/langbot/pkg/platform/sources/wecom.py b/src/langbot/pkg/platform/sources/wecom.py index 7bed676f..777118d7 100644 --- a/src/langbot/pkg/platform/sources/wecom.py +++ b/src/langbot/pkg/platform/sources/wecom.py @@ -163,22 +163,33 @@ class WecomEventConverter(abstract_platform_adapter.AbstractEventConverter): return wecom_event @staticmethod - async def target2yiri(event: WecomEvent): + async def target2yiri(event: WecomEvent, bot: WecomClient = None): """ 将 WecomEvent 转换为平台的 FriendMessage 对象。 Args: event (WecomEvent): 企业微信事件。 + bot (WecomClient): 企业微信客户端,用于获取用户信息。 Returns: platform_events.FriendMessage: 转换后的 FriendMessage 对象。 """ + # Try to get the user's real name from the WeCom API + nickname = str(event.user_id) + if bot and event.user_id: + try: + user_info = await bot.get_user_info(event.user_id) + if user_info and user_info.get('name'): + nickname = user_info.get('name') + except Exception: + pass # Fall back to user_id as nickname + # 转换消息链 if event.type == 'text': yiri_chain = await WecomMessageConverter.target2yiri(event.message, event.message_id) friend = platform_entities.Friend( id=f'u{event.user_id}', - nickname=str(event.agent_id), + nickname=nickname, remark='', ) @@ -186,7 +197,7 @@ class WecomEventConverter(abstract_platform_adapter.AbstractEventConverter): elif event.type == 'image': friend = platform_entities.Friend( id=f'u{event.user_id}', - nickname=str(event.agent_id), + nickname=nickname, remark='', ) @@ -287,7 +298,7 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): async def on_message(event: WecomEvent): self.bot_account_id = event.receiver_id try: - return await callback(await self.event_converter.target2yiri(event), self) + return await callback(await self.event_converter.target2yiri(event, self.bot), self) except Exception: await self.logger.error(f'Error in wecom callback: {traceback.format_exc()}') From bbe019f0c66a24ac43011634299d35b22fb0cbcf Mon Sep 17 00:00:00 2001 From: WangCham <651122857@qq.com> Date: Mon, 23 Mar 2026 13:52:57 +0800 Subject: [PATCH 3/3] fix: wrong agentid --- src/langbot/libs/wecom_api/api.py | 10 +++--- src/langbot/pkg/platform/sources/wecom.py | 38 ++++++++------------- src/langbot/pkg/platform/sources/wecom.yaml | 7 ---- 3 files changed, 19 insertions(+), 36 deletions(-) diff --git a/src/langbot/libs/wecom_api/api.py b/src/langbot/libs/wecom_api/api.py index 9e911a80..b6c7ef0b 100644 --- a/src/langbot/libs/wecom_api/api.py +++ b/src/langbot/libs/wecom_api/api.py @@ -70,7 +70,7 @@ class WecomClient: async def get_user_info(self, userid: str) -> dict: """ - Get user information by user ID using the contacts secret. + Get user information by user ID using the application secret. Args: userid: The user ID to look up. @@ -78,15 +78,15 @@ class WecomClient: Returns: dict: User information including 'name' field. """ - if not await self.check_access_token_for_contacts(): - self.access_token_for_contacts = await self.get_access_token(self.secret_for_contacts) + if not await self.check_access_token(): + self.access_token = await self.get_access_token(self.secret) - url = self.base_url + '/user/get?access_token=' + self.access_token_for_contacts + '&userid=' + quote(userid) + url = self.base_url + '/user/get?access_token=' + self.access_token + '&userid=' + quote(userid) async with httpx.AsyncClient() as client: response = await client.get(url) data = response.json() if data.get('errcode') == 40014 or data.get('errcode') == 42001: - self.access_token_for_contacts = await self.get_access_token(self.secret_for_contacts) + self.access_token = await self.get_access_token(self.secret) return await self.get_user_info(userid) if data.get('errcode', 0) != 0: await self.logger.error(f'获取用户信息失败:{data}') diff --git a/src/langbot/pkg/platform/sources/wecom.py b/src/langbot/pkg/platform/sources/wecom.py index 777118d7..27c77ad3 100644 --- a/src/langbot/pkg/platform/sources/wecom.py +++ b/src/langbot/pkg/platform/sources/wecom.py @@ -148,19 +148,7 @@ class WecomEventConverter(abstract_platform_adapter.AbstractEventConverter): pass if type(event) is platform_events.FriendMessage: - payload = { - 'MsgType': 'text', - 'Content': '', - 'FromUserName': event.sender.id, - 'ToUserName': bot_account_id, - 'CreateTime': int(datetime.datetime.now().timestamp()), - 'AgentID': event.sender.nickname, - } - wecom_event = WecomEvent.from_payload(payload=payload) - if not wecom_event: - raise ValueError('无法从 message_data 构造 WecomEvent 对象') - - return wecom_event + return event.source_platform_object @staticmethod async def target2yiri(event: WecomEvent, bot: WecomClient = None): @@ -193,7 +181,9 @@ class WecomEventConverter(abstract_platform_adapter.AbstractEventConverter): remark='', ) - return platform_events.FriendMessage(sender=friend, message_chain=yiri_chain, time=event.timestamp) + return platform_events.FriendMessage( + sender=friend, message_chain=yiri_chain, time=event.timestamp, source_platform_object=event + ) elif event.type == 'image': friend = platform_entities.Friend( id=f'u{event.user_id}', @@ -203,7 +193,9 @@ class WecomEventConverter(abstract_platform_adapter.AbstractEventConverter): yiri_chain = await WecomMessageConverter.target2yiri_image(picurl=event.picurl, message_id=event.message_id) - return platform_events.FriendMessage(sender=friend, message_chain=yiri_chain, time=event.timestamp) + return platform_events.FriendMessage( + sender=friend, message_chain=yiri_chain, time=event.timestamp, source_platform_object=event + ) class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): @@ -221,7 +213,6 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): 'secret', 'token', 'EncodingAESKey', - 'contacts_secret', ] missing_keys = [key for key in required_keys if key not in config] @@ -234,7 +225,7 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): secret=config['secret'], token=config['token'], EncodingAESKey=config['EncodingAESKey'], - contacts_secret=config['contacts_secret'], + contacts_secret=config.get('contacts_secret', ''), # Optional, kept for backward compatibility logger=logger, unified_mode=True, api_base_url=config.get('api_base_url', 'https://qyapi.weixin.qq.com/cgi-bin'), @@ -259,18 +250,17 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): ): Wecom_event = await WecomEventConverter.yiri2target(message_source, self.bot_account_id, self.bot) content_list = await WecomMessageConverter.yiri2target(message, self.bot) - fixed_user_id = Wecom_event.user_id - # 删掉开头的u - fixed_user_id = fixed_user_id[1:] + # user_id is the original FromUserName from WecomEvent + user_id = Wecom_event.user_id for content in content_list: if content['type'] == 'text': - await self.bot.send_private_msg(fixed_user_id, Wecom_event.agent_id, content['content']) + await self.bot.send_private_msg(user_id, Wecom_event.agent_id, content['content']) elif content['type'] == 'image': - await self.bot.send_image(fixed_user_id, Wecom_event.agent_id, content['media_id']) + await self.bot.send_image(user_id, Wecom_event.agent_id, content['media_id']) elif content['type'] == 'voice': - await self.bot.send_voice(fixed_user_id, Wecom_event.agent_id, content['media_id']) + await self.bot.send_voice(user_id, Wecom_event.agent_id, content['media_id']) elif content['type'] == 'file': - await self.bot.send_file(fixed_user_id, Wecom_event.agent_id, content['media_id']) + await self.bot.send_file(user_id, Wecom_event.agent_id, content['media_id']) async def send_message(self, target_type: str, target_id: str, message: platform_message.MessageChain): content_list = await WecomMessageConverter.yiri2target(message, self.bot) diff --git a/src/langbot/pkg/platform/sources/wecom.yaml b/src/langbot/pkg/platform/sources/wecom.yaml index 1d547c29..c732f699 100644 --- a/src/langbot/pkg/platform/sources/wecom.yaml +++ b/src/langbot/pkg/platform/sources/wecom.yaml @@ -39,13 +39,6 @@ spec: type: string required: true default: "" - - name: contacts_secret - label: - en_US: Contacts Secret - zh_Hans: 通讯录密钥 - type: string - required: true - default: "" - name: api_base_url label: en_US: API Base URL