@soimy/dingtalk 3.5.1 → 3.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -24
- package/openclaw.plugin.json +695 -0
- package/package.json +12 -7
- package/src/ack-reaction-service.ts +1 -1
- package/src/auth.ts +1 -1
- package/src/card/card-action-handler.ts +1 -1
- package/src/card/card-stop-handler.ts +1 -1
- package/src/card/card-streaming-mode.ts +30 -0
- package/src/card/reasoning-answer-split.ts +162 -0
- package/src/card/reasoning-block-assembler.ts +157 -0
- package/src/card-callback-service.ts +1 -1
- package/src/card-draft-controller.ts +117 -6
- package/src/card-service.ts +112 -1
- package/src/channel.ts +131 -96
- package/src/command/card-stop-command.ts +4 -22
- package/src/command/inbound-command-dispatch-service.ts +464 -0
- package/src/config-schema.ts +62 -38
- package/src/config.ts +25 -3
- package/src/docs-service.ts +5 -5
- package/src/http-client.ts +20 -0
- package/src/inbound-handler.ts +475 -501
- package/src/logger-context.ts +16 -2
- package/src/media-utils.ts +166 -10
- package/src/message-utils.ts +33 -5
- package/src/{attachment-text-extractor.ts → messaging/attachment-text-extractor.ts} +1 -1
- package/src/{quoted-file-service.ts → messaging/quoted-file-service.ts} +14 -9
- package/src/onboarding.ts +29 -0
- package/src/plugin-sdk-channel-actions-augment.ts +11 -0
- package/src/reply-strategy-card.ts +294 -28
- package/src/reply-strategy-markdown.ts +124 -19
- package/src/reply-strategy.ts +22 -2
- package/src/send-service.ts +178 -7
- package/src/targeting/agent-routing.ts +55 -32
- package/src/{group-members-store.ts → targeting/group-members-store.ts} +1 -1
- package/src/types.ts +60 -4
- package/src/utils.ts +190 -0
package/src/card-service.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import axios from "
|
|
4
|
+
import axios from "./http-client";
|
|
5
5
|
import { getAccessToken } from "./auth";
|
|
6
6
|
import { updateCardVariables } from "./card-callback-service";
|
|
7
7
|
import { DINGTALK_CARD_TEMPLATE, STOP_ACTION_VISIBLE, STOP_ACTION_HIDDEN } from "./card/card-template";
|
|
@@ -904,6 +904,117 @@ export async function finishAICard(
|
|
|
904
904
|
}
|
|
905
905
|
}
|
|
906
906
|
|
|
907
|
+
function getCardRecallTarget(card: AICardInstance): {
|
|
908
|
+
isGroup: boolean;
|
|
909
|
+
conversationId?: string;
|
|
910
|
+
} {
|
|
911
|
+
const { targetId, isExplicitUser } = stripTargetPrefix(card.conversationId);
|
|
912
|
+
const resolvedTarget = resolveOriginalPeerId(targetId);
|
|
913
|
+
const isGroup = !isExplicitUser && resolvedTarget.startsWith("cid");
|
|
914
|
+
return {
|
|
915
|
+
isGroup,
|
|
916
|
+
conversationId: resolvedTarget || undefined,
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
function parseRecallFailureEntries(payload: unknown): Array<[string, string]> {
|
|
921
|
+
if (!payload || typeof payload !== "object") {
|
|
922
|
+
return [];
|
|
923
|
+
}
|
|
924
|
+
return Object.entries(payload as Record<string, unknown>)
|
|
925
|
+
.map(([key, value]) => [String(key), String(value ?? "")] as [string, string]);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
export async function recallAICardMessage(
|
|
929
|
+
card: AICardInstance,
|
|
930
|
+
log?: Logger,
|
|
931
|
+
): Promise<boolean> {
|
|
932
|
+
const config = card.config;
|
|
933
|
+
const processQueryKey = card.processQueryKey?.trim();
|
|
934
|
+
const robotCode = config ? resolveRobotCode(config) : "";
|
|
935
|
+
|
|
936
|
+
if (!config || !processQueryKey || !robotCode) {
|
|
937
|
+
log?.warn?.(
|
|
938
|
+
`[DingTalk][AICard] Skip recall because required metadata is missing: ` +
|
|
939
|
+
`card=${card.cardInstanceId} hasConfig=${Boolean(config)} ` +
|
|
940
|
+
`processQueryKey=${processQueryKey || "(none)"} robotCode=${robotCode || "(none)"}`,
|
|
941
|
+
);
|
|
942
|
+
return false;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
const target = getCardRecallTarget(card);
|
|
946
|
+
if (!target.conversationId) {
|
|
947
|
+
log?.warn?.(
|
|
948
|
+
`[DingTalk][AICard] Skip recall because conversationId is invalid: card=${card.cardInstanceId} conversationId=${card.conversationId}`,
|
|
949
|
+
);
|
|
950
|
+
return false;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
const url = target.isGroup
|
|
954
|
+
? `${DINGTALK_API}/v1.0/robot/groupMessages/recall`
|
|
955
|
+
: `${DINGTALK_API}/v1.0/robot/otoMessages/batchRecall`;
|
|
956
|
+
const body: Record<string, unknown> = {
|
|
957
|
+
robotCode,
|
|
958
|
+
processQueryKeys: [processQueryKey],
|
|
959
|
+
};
|
|
960
|
+
if (target.isGroup) {
|
|
961
|
+
body.openConversationId = target.conversationId;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
try {
|
|
965
|
+
const token = await getAccessToken(config, log);
|
|
966
|
+
const response = await axios.post(url, body, {
|
|
967
|
+
headers: {
|
|
968
|
+
"x-acs-dingtalk-access-token": token,
|
|
969
|
+
"Content-Type": "application/json",
|
|
970
|
+
},
|
|
971
|
+
...getProxyBypassOption(config),
|
|
972
|
+
});
|
|
973
|
+
const successResults = Array.isArray((response.data as Record<string, unknown> | undefined)?.successResult)
|
|
974
|
+
? ((response.data as Record<string, unknown>).successResult as unknown[])
|
|
975
|
+
.map((item) => String(item))
|
|
976
|
+
: [];
|
|
977
|
+
const failedEntries = parseRecallFailureEntries(
|
|
978
|
+
(response.data as Record<string, unknown> | undefined)?.failedResult,
|
|
979
|
+
);
|
|
980
|
+
if (failedEntries.length > 0) {
|
|
981
|
+
log?.warn?.(
|
|
982
|
+
`[DingTalk][AICard] Recall reported failedResult: card=${card.cardInstanceId} ` +
|
|
983
|
+
`processQueryKey=${processQueryKey} failed=${JSON.stringify(failedEntries)}`,
|
|
984
|
+
);
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
if (!successResults.includes(processQueryKey)) {
|
|
988
|
+
log?.warn?.(
|
|
989
|
+
`[DingTalk][AICard] Recall response missing successResult for processQueryKey=${processQueryKey} ` +
|
|
990
|
+
`payload=${JSON.stringify(response.data)}`,
|
|
991
|
+
);
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
card.state = AICardStatus.FINISHED;
|
|
996
|
+
card.lastUpdated = Date.now();
|
|
997
|
+
removePendingCard(card, log);
|
|
998
|
+
log?.info?.(
|
|
999
|
+
`[DingTalk][AICard] Recalled empty card message: card=${card.cardInstanceId} ` +
|
|
1000
|
+
`conversationId=${target.conversationId} processQueryKey=${processQueryKey} mode=${target.isGroup ? "group" : "direct"}`,
|
|
1001
|
+
);
|
|
1002
|
+
return true;
|
|
1003
|
+
} catch (err: any) {
|
|
1004
|
+
log?.warn?.(`[DingTalk][AICard] Recall failed for card=${card.cardInstanceId}: ${err.message}`);
|
|
1005
|
+
if (err.response?.data !== undefined) {
|
|
1006
|
+
log?.warn?.(
|
|
1007
|
+
formatDingTalkErrorPayloadLog(
|
|
1008
|
+
target.isGroup ? "card.groupRecall" : "card.directRecall",
|
|
1009
|
+
err.response.data,
|
|
1010
|
+
"[DingTalk][AICard]",
|
|
1011
|
+
),
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
return false;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
907
1018
|
export async function finishStoppedAICard(
|
|
908
1019
|
card: AICardInstance,
|
|
909
1020
|
content: string,
|