fix: import errors

This commit is contained in:
Junyan Qin
2025-11-21 23:39:19 +08:00
parent 442c93193c
commit 2412e080b4
6 changed files with 96 additions and 101 deletions

View File

@@ -13,9 +13,9 @@ import httpx
from Crypto.Cipher import AES
from quart import Quart, request, Response, jsonify
from libs.wecom_ai_bot_api import wecombotevent
from libs.wecom_ai_bot_api.WXBizMsgCrypt3 import WXBizMsgCrypt
from pkg.platform.logger import EventLogger
from langbot.libs.wecom_ai_bot_api import wecombotevent
from langbot.libs.wecom_ai_bot_api.WXBizMsgCrypt3 import WXBizMsgCrypt
from langbot.pkg.platform.logger import EventLogger
@dataclass
@@ -224,10 +224,7 @@ class WecomBotClient:
# 只有在非统一模式下才注册独立路由
if not self.unified_mode:
self.app.add_url_rule(
'/callback/command',
'handle_callback',
self.handle_callback_request,
methods=['POST', 'GET']
'/callback/command', 'handle_callback', self.handle_callback_request, methods=['POST', 'GET']
)
self._message_handlers = {
@@ -445,7 +442,7 @@ class WecomBotClient:
await self.logger.error("请求体中缺少 'encrypt' 字段")
return Response('Bad Request', status=400)
xml_post_data = f"<xml><Encrypt><![CDATA[{encrypted_msg}]]></Encrypt></xml>"
xml_post_data = f'<xml><Encrypt><![CDATA[{encrypted_msg}]]></Encrypt></xml>'
ret, decrypted_xml = self.wxcpt.DecryptMsg(xml_post_data, msg_signature, timestamp, nonce)
if ret != 0:
await self.logger.error('解密失败')
@@ -483,7 +480,7 @@ class WecomBotClient:
picurl = item.get('image', {}).get('url')
if texts:
message_data['content'] = "".join(texts) # 拼接所有 text
message_data['content'] = ''.join(texts) # 拼接所有 text
if picurl:
base64 = await self.download_url_to_base64(picurl, self.EnCodingAESKey)
message_data['picurl'] = base64 # 只保留第一个 image
@@ -491,7 +488,9 @@ class WecomBotClient:
# Extract user information
from_info = msg_json.get('from', {})
message_data['userid'] = from_info.get('userid', '')
message_data['username'] = from_info.get('alias', '') or from_info.get('name', '') or from_info.get('userid', '')
message_data['username'] = (
from_info.get('alias', '') or from_info.get('name', '') or from_info.get('userid', '')
)
# Extract chat/group information
if msg_json.get('chattype', '') == 'group':
@@ -580,7 +579,7 @@ class WecomBotClient:
encrypted_bytes = response.content
aes_key = base64.b64decode(encoding_aes_key + "=") # base64 补齐
aes_key = base64.b64decode(encoding_aes_key + '=') # base64 补齐
iv = aes_key[:16]
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
@@ -589,22 +588,22 @@ class WecomBotClient:
pad_len = decrypted[-1]
decrypted = decrypted[:-pad_len]
if decrypted.startswith(b"\xff\xd8"): # JPEG
mime_type = "image/jpeg"
elif decrypted.startswith(b"\x89PNG"): # PNG
mime_type = "image/png"
elif decrypted.startswith((b"GIF87a", b"GIF89a")): # GIF
mime_type = "image/gif"
elif decrypted.startswith(b"BM"): # BMP
mime_type = "image/bmp"
elif decrypted.startswith(b"II*\x00") or decrypted.startswith(b"MM\x00*"): # TIFF
mime_type = "image/tiff"
if decrypted.startswith(b'\xff\xd8'): # JPEG
mime_type = 'image/jpeg'
elif decrypted.startswith(b'\x89PNG'): # PNG
mime_type = 'image/png'
elif decrypted.startswith((b'GIF87a', b'GIF89a')): # GIF
mime_type = 'image/gif'
elif decrypted.startswith(b'BM'): # BMP
mime_type = 'image/bmp'
elif decrypted.startswith(b'II*\x00') or decrypted.startswith(b'MM\x00*'): # TIFF
mime_type = 'image/tiff'
else:
mime_type = "application/octet-stream"
mime_type = 'application/octet-stream'
# 转 base64
base64_str = base64.b64encode(decrypted).decode("utf-8")
return f"data:{mime_type};base64,{base64_str}"
base64_str = base64.b64encode(decrypted).decode('utf-8')
return f'data:{mime_type};base64,{base64_str}'
async def run_task(self, host: str, port: int, *args, **kwargs):
"""

View File

@@ -58,11 +58,11 @@ class BotService:
if runtime_bot is not None:
adapter_runtime_values['bot_account_id'] = runtime_bot.adapter.bot_account_id
if persistence_bot['adapter'] in ['wecom', 'wecombot', 'officialaccount', 'qqofficial', 'slack','wecomcs']:
if persistence_bot['adapter'] in ['wecom', 'wecombot', 'officialaccount', 'qqofficial', 'slack', 'wecomcs']:
api_port = self.ap.instance_config.data['api']['port']
webhook_url = f"/bots/{bot_uuid}"
webhook_url = f'/bots/{bot_uuid}'
adapter_runtime_values['webhook_url'] = webhook_url
adapter_runtime_values['webhook_full_url'] = f"http://<Your-Server-IP>:{api_port}{webhook_url}"
adapter_runtime_values['webhook_full_url'] = f'http://<Your-Server-IP>:{api_port}{webhook_url}'
else:
adapter_runtime_values['webhook_url'] = None
adapter_runtime_values['webhook_full_url'] = None

View File

@@ -5,13 +5,12 @@ import traceback
import pydantic
import datetime
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
from libs.official_account_api.oaevent import OAEvent
from libs.official_account_api.api import OAClient
from libs.official_account_api.api import OAClientForLongerResponse
from langbot.libs.official_account_api.oaevent import OAEvent
from langbot.libs.official_account_api.api import OAClient
from langbot.libs.official_account_api.api import OAClientForLongerResponse
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.platform.events as platform_events
from langbot_plugin.api.entities.builtin.command import errors as command_errors
from ..logger import EventLogger
@@ -93,7 +92,6 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd
bot_account_id = config.get('AppID', '')
super().__init__(
bot=bot,
bot_account_id=bot_account_id,
@@ -101,10 +99,6 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd
logger=logger,
)
async def reply_message(
self,
message_source: platform_events.FriendMessage,
@@ -165,19 +159,20 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd
if self.bot_uuid and hasattr(self.logger, 'ap'):
try:
api_port = self.logger.ap.instance_config.data['api']['port']
webhook_url = f"http://127.0.0.1:{api_port}/bots/{self.bot_uuid}"
webhook_url_public = f"http://<Your-Public-IP>:{api_port}/bots/{self.bot_uuid}"
webhook_url = f'http://127.0.0.1:{api_port}/bots/{self.bot_uuid}'
webhook_url_public = f'http://<Your-Public-IP>:{api_port}/bots/{self.bot_uuid}'
await self.logger.info(f"微信公众号 Webhook 回调地址:")
await self.logger.info(f" 本地地址: {webhook_url}")
await self.logger.info(f" 公网地址: {webhook_url_public}")
await self.logger.info(f"请在微信公众号后台配置此回调地址")
await self.logger.info('微信公众号 Webhook 回调地址:')
await self.logger.info(f' 本地地址: {webhook_url}')
await self.logger.info(f' 公网地址: {webhook_url_public}')
await self.logger.info('请在微信公众号后台配置此回调地址')
except Exception as e:
await self.logger.warning(f"无法生成 webhook URL: {e}")
await self.logger.warning(f'无法生成 webhook URL: {e}')
async def keep_alive():
while True:
await asyncio.sleep(1)
await keep_alive()
async def kill(self) -> bool:
@@ -192,5 +187,8 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd
):
return super().unregister_listener(event_type, callback)
async def is_muted(self, group_id: str, ) -> bool:
async def is_muted(
self,
group_id: str,
) -> bool:
pass

View File

@@ -9,9 +9,8 @@ import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platf
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.platform.events as platform_events
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
from langbot_plugin.api.entities.builtin.command import errors as command_errors
from libs.qq_official_api.api import QQOfficialClient
from libs.qq_official_api.qqofficialevent import QQOfficialEvent
from langbot.libs.qq_official_api.api import QQOfficialClient
from langbot.libs.qq_official_api.qqofficialevent import QQOfficialEvent
from ...utils import image
from ..logger import EventLogger
@@ -141,11 +140,7 @@ class QQOfficialAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter
def __init__(self, config: dict, logger: EventLogger):
bot = QQOfficialClient(
app_id=config['appid'],
secret=config['secret'],
token=config['token'],
logger=logger,
unified_mode=True
app_id=config['appid'], secret=config['secret'], token=config['token'], logger=logger, unified_mode=True
)
super().__init__(
@@ -256,19 +251,20 @@ class QQOfficialAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter
if self.bot_uuid and hasattr(self.logger, 'ap'):
try:
api_port = self.logger.ap.instance_config.data['api']['port']
webhook_url = f"http://127.0.0.1:{api_port}/bots/{self.bot_uuid}"
webhook_url_public = f"http://<Your-Public-IP>:{api_port}/bots/{self.bot_uuid}"
webhook_url = f'http://127.0.0.1:{api_port}/bots/{self.bot_uuid}'
webhook_url_public = f'http://<Your-Public-IP>:{api_port}/bots/{self.bot_uuid}'
await self.logger.info(f"QQ 官方机器人 Webhook 回调地址:")
await self.logger.info(f" 本地地址: {webhook_url}")
await self.logger.info(f" 公网地址: {webhook_url_public}")
await self.logger.info(f"请在 QQ 官方机器人后台配置此回调地址")
await self.logger.info('QQ 官方机器人 Webhook 回调地址:')
await self.logger.info(f' 本地地址: {webhook_url}')
await self.logger.info(f' 公网地址: {webhook_url_public}')
await self.logger.info('请在 QQ 官方机器人后台配置此回调地址')
except Exception as e:
await self.logger.warning(f"无法生成 webhook URL: {e}")
await self.logger.warning(f'无法生成 webhook URL: {e}')
async def keep_alive():
while True:
await asyncio.sleep(1)
await keep_alive()
async def kill(self) -> bool:

View File

@@ -5,10 +5,9 @@ import traceback
import datetime
from libs.wecom_api.api import WecomClient
from langbot.libs.wecom_api.api import WecomClient
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
from libs.wecom_api.wecomevent import WecomEvent
from langbot_plugin.api.entities.builtin.command import errors as command_errors
from langbot.libs.wecom_api.wecomevent import WecomEvent
from ...utils import image
from ..logger import EventLogger
import langbot_plugin.api.entities.builtin.platform.message as platform_message
@@ -159,12 +158,11 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
unified_mode=True,
)
super().__init__(
config=config,
logger=logger,
bot=bot,
bot_account_id="",
bot_account_id='',
)
def set_bot_uuid(self, bot_uuid: str):
@@ -189,7 +187,6 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
await self.bot.send_image(fixed_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)
parts = target_id.split('|')
user_id = parts[0]
@@ -235,23 +232,23 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
return await self.bot.handle_unified_webhook(request)
async def run_async(self):
if self.bot_uuid and hasattr(self.logger, 'ap'):
try:
api_port = self.logger.ap.instance_config.data['api']['port']
webhook_url = f"http://127.0.0.1:{api_port}/bots/{self.bot_uuid}"
webhook_url_public = f"http://<Your-Public-IP>:{api_port}/bots/{self.bot_uuid}"
webhook_url = f'http://127.0.0.1:{api_port}/bots/{self.bot_uuid}'
webhook_url_public = f'http://<Your-Public-IP>:{api_port}/bots/{self.bot_uuid}'
await self.logger.info(f"企业微信 Webhook 回调地址:")
await self.logger.info(f" 本地地址: {webhook_url}")
await self.logger.info(f" 公网地址: {webhook_url_public}")
await self.logger.info(f"请在企业微信后台配置此回调地址")
await self.logger.info('企业微信 Webhook 回调地址:')
await self.logger.info(f' 本地地址: {webhook_url}')
await self.logger.info(f' 公网地址: {webhook_url_public}')
await self.logger.info('请在企业微信后台配置此回调地址')
except Exception as e:
await self.logger.warning(f"无法生成 webhook URL: {e}")
await self.logger.warning(f'无法生成 webhook URL: {e}')
async def keep_alive():
while True:
await asyncio.sleep(1)
await keep_alive()
async def kill(self) -> bool:
@@ -265,6 +262,6 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
],
):
return super().unregister_listener(event_type, callback)
async def is_muted(self, group_id: int) -> bool:
pass

View File

@@ -8,11 +8,10 @@ import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platf
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.platform.events as platform_events
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
import pydantic
from ..logger import EventLogger
from libs.wecom_ai_bot_api.wecombotevent import WecomBotEvent
from libs.wecom_ai_bot_api.api import WecomBotClient
from ...core import app
from ..logger import EventLogger
from langbot.libs.wecom_ai_bot_api.wecombotevent import WecomBotEvent
from langbot.libs.wecom_ai_bot_api.api import WecomBotClient
class WecomBotMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
@staticmethod
@@ -36,14 +35,14 @@ class WecomBotMessageConverter(abstract_platform_adapter.AbstractMessageConverte
return chain
class WecomBotEventConverter(abstract_platform_adapter.AbstractEventConverter):
@staticmethod
async def yiri2target(event: platform_events.MessageEvent):
return event.source_platform_object
@staticmethod
async def yiri2target(event:platform_events.MessageEvent):
return event.source_platform_object
@staticmethod
async def target2yiri(event:WecomBotEvent):
async def target2yiri(event: WecomBotEvent):
message_chain = await WecomBotMessageConverter.target2yiri(event)
if event.type == 'single':
return platform_events.FriendMessage(
@@ -82,6 +81,7 @@ class WecomBotEventConverter(abstract_platform_adapter.AbstractEventConverter):
except Exception:
print(traceback.format_exc())
class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
bot: WecomBotClient
bot_account_id: str
@@ -91,13 +91,11 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
bot_uuid: str = None
def __init__(self, config: dict, logger: EventLogger):
required_keys = ['Token', 'EncodingAESKey', 'Corpid', 'BotId']
missing_keys = [key for key in required_keys if key not in config]
if missing_keys:
raise Exception(f'WecomBot 缺少配置项: {missing_keys}')
bot = WecomBotClient(
Token=config['Token'],
EnCodingAESKey=config['EncodingAESKey'],
@@ -114,9 +112,12 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
bot_account_id=bot_account_id,
)
async def reply_message(self, message_source:platform_events.MessageEvent, message:platform_message.MessageChain,quote_origin: bool = False):
async def reply_message(
self,
message_source: platform_events.MessageEvent,
message: platform_message.MessageChain,
quote_origin: bool = False,
):
content = await self.message_converter.yiri2target(message)
await self.bot.set_message(message_source.source_platform_object.message_id, content)
@@ -170,7 +171,9 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
def register_listener(
self,
event_type: typing.Type[platform_events.Event],
callback: typing.Callable[[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None],
callback: typing.Callable[
[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None
],
):
async def on_message(event: WecomBotEvent):
try:
@@ -178,6 +181,7 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
except Exception:
await self.logger.error(f'Error in wecombot callback: {traceback.format_exc()}')
print(traceback.format_exc())
try:
if event_type == platform_events.FriendMessage:
self.bot.on_message('single')(on_message)
@@ -211,19 +215,20 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
if self.bot_uuid and hasattr(self.logger, 'ap'):
try:
api_port = self.logger.ap.instance_config.data['api']['port']
webhook_url = f"http://127.0.0.1:{api_port}/bots/{self.bot_uuid}"
webhook_url_public = f"http://<Your-Public-IP>:{api_port}/bots/{self.bot_uuid}"
webhook_url = f'http://127.0.0.1:{api_port}/bots/{self.bot_uuid}'
webhook_url_public = f'http://<Your-Public-IP>:{api_port}/bots/{self.bot_uuid}'
await self.logger.info(f"企业微信机器人 Webhook 回调地址:")
await self.logger.info(f" 本地地址: {webhook_url}")
await self.logger.info(f" 公网地址: {webhook_url_public}")
await self.logger.info(f"请在企业微信后台配置此回调地址")
await self.logger.info('企业微信机器人 Webhook 回调地址:')
await self.logger.info(f' 本地地址: {webhook_url}')
await self.logger.info(f' 公网地址: {webhook_url_public}')
await self.logger.info('请在企业微信后台配置此回调地址')
except Exception as e:
await self.logger.warning(f"无法生成 webhook URL: {e}")
await self.logger.warning(f'无法生成 webhook URL: {e}')
async def keep_alive():
while True:
await asyncio.sleep(1)
await keep_alive()
async def kill(self) -> bool:
@@ -232,11 +237,11 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
async def unregister_listener(
self,
event_type: type,
callback: typing.Callable[[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None],
callback: typing.Callable[
[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None
],
):
return super().unregister_listener(event_type, callback)
async def is_muted(self, group_id: int) -> bool:
pass