mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 00:33:20 +08:00
Merge pull request #76 from divingleee/feat/dingtalk-at-person
dingtalk add @person function
This commit is contained in:
commit
799391552e
2 changed files with 32 additions and 19 deletions
|
|
@ -1008,13 +1008,18 @@ export function createDingtalkApi({
|
||||||
|
|
||||||
failAiCard: failCard,
|
failAiCard: failCard,
|
||||||
|
|
||||||
async sendText({ clientId, clientSecret, sessionWebhook, text, signal }) {
|
async sendText({ clientId, clientSecret, sessionWebhook, text, at, signal }) {
|
||||||
const content = nonEmptyString(text);
|
const content = nonEmptyString(text);
|
||||||
if (!content) throw new TypeError('text is required');
|
if (!content) throw new TypeError('text is required');
|
||||||
const webhook = normalizeDingtalkSessionWebhook(sessionWebhook);
|
const webhook = normalizeDingtalkSessionWebhook(sessionWebhook);
|
||||||
const token = await accessToken({ clientId, clientSecret, signal });
|
const token = await accessToken({ clientId, clientSecret, signal });
|
||||||
|
const replyAt = at && typeof at === 'object'
|
||||||
|
&& ((Array.isArray(at.atUserIds) && at.atUserIds.length > 0)
|
||||||
|
|| (Array.isArray(at.atMobiles) && at.atMobiles.length > 0)
|
||||||
|
|| at.isAtAll === true)
|
||||||
|
? { at } : {};
|
||||||
const response = await requestJson(fetchImpl, webhook, {
|
const response = await requestJson(fetchImpl, webhook, {
|
||||||
body: { msgtype: 'text', text: { content } },
|
body: { msgtype: 'text', text: { content }, ...replyAt },
|
||||||
headers: { 'x-acs-dingtalk-access-token': token },
|
headers: { 'x-acs-dingtalk-access-token': token },
|
||||||
signal,
|
signal,
|
||||||
action: '消息回复',
|
action: '消息回复',
|
||||||
|
|
|
||||||
|
|
@ -551,7 +551,7 @@ export class DingtalkHarnessBridge {
|
||||||
`[dsh-dingtalk] failed to process a command [${failure.referenceId}]`,
|
`[dsh-dingtalk] failed to process a command [${failure.referenceId}]`,
|
||||||
safeErrorDiagnostic(error),
|
safeErrorDiagnostic(error),
|
||||||
);
|
);
|
||||||
return this.#send(sessionWebhook, messageFailureText(failure)).catch(() => undefined);
|
return this.#send(sessionWebhook, messageFailureText(failure), this.#atUsersFor(message)).catch(() => undefined);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.#acceptedMessageIds.delete(messageId);
|
this.#acceptedMessageIds.delete(messageId);
|
||||||
this.#commandTasks.delete(task);
|
this.#commandTasks.delete(task);
|
||||||
|
|
@ -573,7 +573,7 @@ export class DingtalkHarnessBridge {
|
||||||
: null,
|
: null,
|
||||||
isQuestionPending: () => this.#pendingInteractions.has(key),
|
isQuestionPending: () => this.#pendingInteractions.has(key),
|
||||||
send: sessionWebhook
|
send: sessionWebhook
|
||||||
? (reply) => this.#send(sessionWebhook, reply)
|
? (reply) => this.#send(sessionWebhook, reply, this.#atUsersFor(message))
|
||||||
: async () => undefined,
|
: async () => undefined,
|
||||||
});
|
});
|
||||||
if (approvalReply) {
|
if (approvalReply) {
|
||||||
|
|
@ -807,7 +807,7 @@ export class DingtalkHarnessBridge {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
for (const reply of result?.messages ?? [result?.message]) {
|
for (const reply of result?.messages ?? [result?.message]) {
|
||||||
if (reply) await this.#send(sessionWebhook, reply);
|
if (reply) await this.#send(sessionWebhook, reply, this.#atUsersFor(message));
|
||||||
}
|
}
|
||||||
this.#status.lastError = null;
|
this.#status.lastError = null;
|
||||||
}
|
}
|
||||||
|
|
@ -885,23 +885,23 @@ export class DingtalkHarnessBridge {
|
||||||
let batchSettled = batchSubmission === null;
|
let batchSettled = batchSubmission === null;
|
||||||
try {
|
try {
|
||||||
if (!text && !hasImages && !hasFiles) {
|
if (!text && !hasImages && !hasFiles) {
|
||||||
await this.#send(sessionWebhook, t('目前支持文字、图片和文件消息。'));
|
await this.#send(sessionWebhook, t('目前支持文字、图片和文件消息。'), this.#atUsersFor(message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const command = text.toLowerCase();
|
const command = text.toLowerCase();
|
||||||
if (isPlainText && !hasImages && !hasFiles && command === '/help') {
|
if (isPlainText && !hasImages && !hasFiles && command === '/help') {
|
||||||
await this.#send(sessionWebhook, helpText());
|
await this.#send(sessionWebhook, helpText(), this.#atUsersFor(message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isPlainText && !hasImages && !hasFiles && command === '/status') {
|
if (isPlainText && !hasImages && !hasFiles && command === '/status') {
|
||||||
await this.#harness.ensureRunning({ signal: this.#signal });
|
await this.#harness.ensureRunning({ signal: this.#signal });
|
||||||
await this.#send(sessionWebhook, t('钉钉机器人与 DeepSeek Harness 连接正常。'));
|
await this.#send(sessionWebhook, t('钉钉机器人与 DeepSeek Harness 连接正常。'), this.#atUsersFor(message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isPlainText && !hasImages && !hasFiles && command === '/new') {
|
if (isPlainText && !hasImages && !hasFiles && command === '/new') {
|
||||||
await this.#state.clearSession(key);
|
await this.#state.clearSession(key);
|
||||||
await this.#send(sessionWebhook, t('已开启新会话。请发送你的问题。'));
|
await this.#send(sessionWebhook, t('已开启新会话。请发送你的问题。'), this.#atUsersFor(message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const workspaceCommand = isPlainText && !hasImages && !hasFiles
|
const workspaceCommand = isPlainText && !hasImages && !hasFiles
|
||||||
|
|
@ -909,7 +909,7 @@ export class DingtalkHarnessBridge {
|
||||||
: null;
|
: null;
|
||||||
if (workspaceCommand) {
|
if (workspaceCommand) {
|
||||||
for (const reply of workspaceCommand.messages ?? [workspaceCommand.message]) {
|
for (const reply of workspaceCommand.messages ?? [workspaceCommand.message]) {
|
||||||
await this.#send(sessionWebhook, reply);
|
await this.#send(sessionWebhook, reply, this.#atUsersFor(message));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -923,7 +923,7 @@ export class DingtalkHarnessBridge {
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
if (compactCommand) {
|
if (compactCommand) {
|
||||||
await this.#send(sessionWebhook, compactCommand.message);
|
await this.#send(sessionWebhook, compactCommand.message, this.#atUsersFor(message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -988,7 +988,7 @@ export class DingtalkHarnessBridge {
|
||||||
textReceipt = createDeliveryReceipt({
|
textReceipt = createDeliveryReceipt({
|
||||||
deliveryId: messageId,
|
deliveryId: messageId,
|
||||||
presentation: 'dingtalk-text',
|
presentation: 'dingtalk-text',
|
||||||
providerMessageIds: await this.#send(sessionWebhook, answerText),
|
providerMessageIds: await this.#send(sessionWebhook, answerText, this.#atUsersFor(message)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -1049,7 +1049,7 @@ export class DingtalkHarnessBridge {
|
||||||
? `${errorText}\n\n${batchFailureMessage}`
|
? `${errorText}\n\n${batchFailureMessage}`
|
||||||
: errorText;
|
: errorText;
|
||||||
const streamed = cardStarted && await cardStream.finish(visibleError);
|
const streamed = cardStarted && await cardStream.finish(visibleError);
|
||||||
if (!streamed) await this.#send(sessionWebhook, visibleError);
|
if (!streamed) await this.#send(sessionWebhook, visibleError, this.#atUsersFor(message));
|
||||||
} catch {
|
} catch {
|
||||||
this.#logger.error?.('[dsh-dingtalk] failed to send the safe error reply');
|
this.#logger.error?.('[dsh-dingtalk] failed to send the safe error reply');
|
||||||
}
|
}
|
||||||
|
|
@ -1102,7 +1102,7 @@ export class DingtalkHarnessBridge {
|
||||||
const text = message?.msgtype === 'text' ? nonEmptyString(message?.text?.content) : null;
|
const text = message?.msgtype === 'text' ? nonEmptyString(message?.text?.content) : null;
|
||||||
if (!text) {
|
if (!text) {
|
||||||
try {
|
try {
|
||||||
await this.#send(sessionWebhook, t('请用文字回答当前问题。'));
|
await this.#send(sessionWebhook, t('请用文字回答当前问题。'), this.#atUsersFor(message));
|
||||||
} catch {
|
} catch {
|
||||||
this.#logger.error?.('[dsh-dingtalk] failed to reject a non-text interaction reply');
|
this.#logger.error?.('[dsh-dingtalk] failed to reject a non-text interaction reply');
|
||||||
}
|
}
|
||||||
|
|
@ -1113,7 +1113,7 @@ export class DingtalkHarnessBridge {
|
||||||
if (!pending || pending !== expected || pending.submitting) {
|
if (!pending || pending !== expected || pending.submitting) {
|
||||||
if (claimed && (!pending || pending !== expected)) {
|
if (claimed && (!pending || pending !== expected)) {
|
||||||
try {
|
try {
|
||||||
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT));
|
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT), this.#atUsersFor(message));
|
||||||
} catch {
|
} catch {
|
||||||
this.#logger.error?.('[dsh-dingtalk] failed to send an expired interaction notice');
|
this.#logger.error?.('[dsh-dingtalk] failed to send an expired interaction notice');
|
||||||
}
|
}
|
||||||
|
|
@ -1178,7 +1178,7 @@ export class DingtalkHarnessBridge {
|
||||||
if (error?.code === 'interaction-not-pending') {
|
if (error?.code === 'interaction-not-pending') {
|
||||||
this.#clearPendingInteraction(key, pending.interactionId);
|
this.#clearPendingInteraction(key, pending.interactionId);
|
||||||
try {
|
try {
|
||||||
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT));
|
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT), this.#atUsersFor(message));
|
||||||
} catch {
|
} catch {
|
||||||
this.#logger.error?.('[dsh-dingtalk] failed to send an expired interaction notice');
|
this.#logger.error?.('[dsh-dingtalk] failed to send an expired interaction notice');
|
||||||
}
|
}
|
||||||
|
|
@ -1191,7 +1191,7 @@ export class DingtalkHarnessBridge {
|
||||||
this.#status.lastError = t('回答提交失败。');
|
this.#status.lastError = t('回答提交失败。');
|
||||||
this.#logger.error?.('[dsh-dingtalk] failed to answer a Harness interaction');
|
this.#logger.error?.('[dsh-dingtalk] failed to answer a Harness interaction');
|
||||||
try {
|
try {
|
||||||
await this.#send(sessionWebhook, t('回答提交失败,请重新发送当前问题的答案。'));
|
await this.#send(sessionWebhook, t('回答提交失败,请重新发送当前问题的答案。'), this.#atUsersFor(message));
|
||||||
} catch {
|
} catch {
|
||||||
this.#logger.error?.('[dsh-dingtalk] failed to send an interaction retry notice');
|
this.#logger.error?.('[dsh-dingtalk] failed to send an interaction retry notice');
|
||||||
}
|
}
|
||||||
|
|
@ -1323,7 +1323,7 @@ export class DingtalkHarnessBridge {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT));
|
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT), this.#atUsersFor(message));
|
||||||
} catch {
|
} catch {
|
||||||
this.#logger.error?.('[dsh-dingtalk] failed to send an expired interaction notice');
|
this.#logger.error?.('[dsh-dingtalk] failed to send an expired interaction notice');
|
||||||
}
|
}
|
||||||
|
|
@ -1367,7 +1367,14 @@ export class DingtalkHarnessBridge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async #send(sessionWebhook, text) {
|
#atUsersFor(message) {
|
||||||
|
const sender = senderStaffId(message);
|
||||||
|
return String(message?.conversationType) === '2' && sender
|
||||||
|
? { atUserIds: [sender] }
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async #send(sessionWebhook, text, at) {
|
||||||
const providerMessageIds = [];
|
const providerMessageIds = [];
|
||||||
for (const chunk of splitDingtalkText(text, this.#maxMessageChars)) {
|
for (const chunk of splitDingtalkText(text, this.#maxMessageChars)) {
|
||||||
this.#signal?.throwIfAborted();
|
this.#signal?.throwIfAborted();
|
||||||
|
|
@ -1376,6 +1383,7 @@ export class DingtalkHarnessBridge {
|
||||||
clientSecret: this.#clientSecret,
|
clientSecret: this.#clientSecret,
|
||||||
sessionWebhook,
|
sessionWebhook,
|
||||||
text: chunk,
|
text: chunk,
|
||||||
|
at,
|
||||||
signal: this.#signal,
|
signal: this.#signal,
|
||||||
});
|
});
|
||||||
providerMessageIds.push(...providerMessageIdsFor(result));
|
providerMessageIds.push(...providerMessageIdsFor(result));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue