mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-21 16:05:19 +08:00
fix(acceptance): repair official account replies and processor navigation
This commit is contained in:
@@ -465,7 +465,11 @@ def build_platform_tool_resources(
|
||||
reason = None
|
||||
if definition is None:
|
||||
reason = 'unknown_tool'
|
||||
elif definition.api not in supported_apis:
|
||||
elif definition.api not in supported_apis and not (
|
||||
definition.name == 'event_reply'
|
||||
and {'reply_message', 'get_message'} <= supported_apis
|
||||
and (event.delivery.reply_target or {}).get('message_id')
|
||||
):
|
||||
reason = 'adapter_api_unsupported'
|
||||
elif definition.scope == 'event' and not _event_matches(event.event_type, definition.event_patterns):
|
||||
reason = 'event_incompatible'
|
||||
@@ -727,9 +731,24 @@ async def execute_platform_tool(
|
||||
bot = await ap.platform_mgr.get_bot_by_uuid(execution_context, bot_id)
|
||||
if bot is None:
|
||||
raise ValueError(f'Bot {bot_id} is not running')
|
||||
if definition.api not in set(bot.adapter.get_supported_apis() or []):
|
||||
supported_apis = set(bot.adapter.get_supported_apis() or [])
|
||||
reply_via_source = (
|
||||
definition.name == 'event_reply'
|
||||
and 'send_message' not in supported_apis
|
||||
and {'reply_message', 'get_message'} <= supported_apis
|
||||
and (delivery.get('reply_target') or {}).get('message_id')
|
||||
)
|
||||
if definition.api not in supported_apis and not reply_via_source:
|
||||
raise ValueError(f'Platform API {definition.api} is no longer supported by bot {bot_id}')
|
||||
api_func = getattr(bot.adapter, definition.api, None)
|
||||
if reply_via_source:
|
||||
async def api_func(target_type, target_id, message):
|
||||
source = await bot.adapter.get_message(
|
||||
chat_type=target_type,
|
||||
chat_id=target_id,
|
||||
message_id=delivery['reply_target']['message_id'],
|
||||
)
|
||||
return await bot.adapter.reply_message(source, message)
|
||||
if not callable(api_func):
|
||||
raise ValueError(f'Platform API {definition.api} is declared but not implemented')
|
||||
if definition.api == 'send_message':
|
||||
|
||||
@@ -704,7 +704,8 @@ class BotService:
|
||||
bot = await self.get_bot(context, bot_data['uuid'], include_secret=True)
|
||||
|
||||
try:
|
||||
await self.ap.platform_mgr.load_bot(context, bot)
|
||||
if bot.get('enable'):
|
||||
await self.ap.platform_mgr.load_bot(context, bot)
|
||||
except Exception as exc:
|
||||
raise BotApplyError(bot_error_message(exc, bot), bot['uuid']) from exc
|
||||
|
||||
@@ -740,8 +741,8 @@ class BotService:
|
||||
bot = await self.get_bot(context, bot_uuid, include_secret=True)
|
||||
try:
|
||||
await self.ap.platform_mgr.remove_bot(context, bot_uuid)
|
||||
runtime_bot = await self.ap.platform_mgr.load_bot(context, bot)
|
||||
if runtime_bot.enable:
|
||||
if bot.get('enable'):
|
||||
runtime_bot = await self.ap.platform_mgr.load_bot(context, bot)
|
||||
await runtime_bot.run()
|
||||
except Exception as exc:
|
||||
raise BotApplyError(bot_error_message(exc, bot), bot['uuid']) from exc
|
||||
|
||||
@@ -56,13 +56,14 @@ class OfficialAccountAdapter(OfficialAccountAPIMixin, abstract_platform_adapter.
|
||||
}
|
||||
if mode == 'drop':
|
||||
bot = OAClient(**common_kwargs)
|
||||
elif mode == 'passive':
|
||||
elif mode in ('passive', 'auto'):
|
||||
bot = OAClientForLongerResponse(
|
||||
**common_kwargs,
|
||||
LoadingMessage=config.get('LoadingMessage', ''),
|
||||
customer_service=mode == 'auto',
|
||||
)
|
||||
else:
|
||||
raise KeyError('OfficialAccount Mode must be "drop" or "passive"')
|
||||
raise KeyError('OfficialAccount Mode must be "drop", "passive" or "auto"')
|
||||
|
||||
super().__init__(
|
||||
config=config,
|
||||
@@ -112,11 +113,12 @@ class OfficialAccountAdapter(OfficialAccountAPIMixin, abstract_platform_adapter.
|
||||
if not isinstance(source, OAEvent):
|
||||
raise ValueError('OfficialAccount reply_message requires an OAEvent source object')
|
||||
content = await OfficialAccountMessageConverter.yiri2target(message)
|
||||
if self.config.get('Mode') == 'passive':
|
||||
await self.bot.set_message(source.user_id, source.message_id, content)
|
||||
if self.config.get('Mode') in ('passive', 'auto'):
|
||||
queued = await self.bot.set_message(source.user_id, source.message_id, content)
|
||||
else:
|
||||
await self.bot.set_message(source.message_id, content)
|
||||
return platform_events.MessageResult(message_id=source.message_id, raw={'queued': True})
|
||||
queued = True
|
||||
return platform_events.MessageResult(message_id=source.message_id, raw={'queued': queued})
|
||||
|
||||
async def call_platform_api(self, action: str, params: dict = {}) -> dict:
|
||||
handler = PLATFORM_API_MAP.get(action)
|
||||
|
||||
@@ -34,6 +34,22 @@ spec:
|
||||
type: array[string]
|
||||
required: false
|
||||
default: []
|
||||
- name: AppID
|
||||
label:
|
||||
en_US: App ID
|
||||
zh_Hans: 应用 ID
|
||||
zh_Hant: 應用 ID
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: AppSecret
|
||||
label:
|
||||
en_US: App Secret
|
||||
zh_Hans: 应用密钥
|
||||
zh_Hant: 應用密鑰
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: webhook_url
|
||||
label:
|
||||
en_US: Webhook Callback URL
|
||||
@@ -62,32 +78,32 @@ spec:
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: AppID
|
||||
label:
|
||||
en_US: App ID
|
||||
zh_Hans: 应用 ID
|
||||
zh_Hant: 應用 ID
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: AppSecret
|
||||
label:
|
||||
en_US: App Secret
|
||||
zh_Hans: 应用密钥
|
||||
zh_Hant: 應用密鑰
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: Mode
|
||||
label:
|
||||
en_US: Mode
|
||||
zh_Hans: 接入模式
|
||||
zh_Hant: 接入模式
|
||||
description:
|
||||
en_US: "drop replies within the current callback; passive returns a loading message first and queues the real reply for the user's next message."
|
||||
zh_Hans: "drop 会在当前回调内等待回复;passive 会先返回加载提示,并将真实回复排队到用户下一条消息。"
|
||||
zh_Hant: "drop 會在目前回調內等待回覆;passive 會先回傳載入提示,並將真實回覆排隊到使用者下一則訊息。"
|
||||
type: string
|
||||
en_US: "Auto uses customer service messaging when available, falling back to next-message retrieval for permission, window or quota limits. Passive always waits for the next message. Drop requires a reply within 5 seconds."
|
||||
zh_Hans: "自动模式在生成完成后通过客服接口回复;无权限、超出回复窗口或额度不足时,改为下一条消息领取。passive 始终由下一条消息领取;drop 需要在 5 秒内完成回复。客服接口通常需企业主体认证,以微信实际权限为准。"
|
||||
zh_Hant: "自動模式在生成完成後透過客服介面回覆;無權限、超出回覆時限或額度不足時,改為下一則訊息領取。passive 始終由下一則訊息領取;drop 需要在 5 秒內完成回覆。客服介面通常需企業主體認證,以微信實際權限為準。"
|
||||
type: select
|
||||
options:
|
||||
- name: auto
|
||||
label:
|
||||
en_US: Automatic reply with fallback (auto)
|
||||
zh_Hans: 自动回复,受限时转为领取(auto)
|
||||
zh_Hant: 自動回覆,受限時轉為領取(auto)
|
||||
- name: drop
|
||||
label:
|
||||
en_US: Reply in current callback (drop)
|
||||
zh_Hans: 当前回调内回复(drop)
|
||||
zh_Hant: 目前回調內回覆(drop)
|
||||
- name: passive
|
||||
label:
|
||||
en_US: Queue reply for next message (passive)
|
||||
zh_Hans: 下一条消息时返回回复(passive)
|
||||
zh_Hant: 下一則訊息時傳回回覆(passive)
|
||||
required: true
|
||||
default: "drop"
|
||||
- name: LoadingMessage
|
||||
|
||||
@@ -450,10 +450,13 @@ class RuntimeBot:
|
||||
return {}
|
||||
if hasattr(model, 'model_dump'):
|
||||
try:
|
||||
return model.model_dump(mode='json')
|
||||
# Native adapter objects are for replying, not event logging.
|
||||
# Exclude them before JSON serialization, since some (OAEvent,
|
||||
# for example) cannot be serialized by Pydantic.
|
||||
return model.model_dump(mode='json', exclude={'source_platform_object'})
|
||||
except TypeError:
|
||||
try:
|
||||
return model.model_dump()
|
||||
return model.model_dump(exclude={'source_platform_object'})
|
||||
except Exception:
|
||||
return {}
|
||||
except Exception:
|
||||
@@ -2231,6 +2234,10 @@ class PlatformManager:
|
||||
)
|
||||
binding = _binding
|
||||
for bot in result.all():
|
||||
# Disabled drafts may not have the credentials needed to construct
|
||||
# an adapter. They become runtime bots when explicitly enabled.
|
||||
if not bot.enable:
|
||||
continue
|
||||
try:
|
||||
if binding is None:
|
||||
binding = await self.ap.workspace_service.get_execution_binding(workspace_uuid)
|
||||
|
||||
Reference in New Issue
Block a user