mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 00:33:20 +08:00
dingtalk add @person function
This commit is contained in:
parent
d251bc280d
commit
7d5bc94f64
2 changed files with 33 additions and 20 deletions
|
|
@ -1008,13 +1008,18 @@ export function createDingtalkApi({
|
|||
|
||||
failAiCard: failCard,
|
||||
|
||||
async sendText({ clientId, clientSecret, sessionWebhook, text, signal }) {
|
||||
async sendText({ clientId, clientSecret, sessionWebhook, text, at, signal }) {
|
||||
const content = nonEmptyString(text);
|
||||
if (!content) throw new TypeError('text is required');
|
||||
const webhook = normalizeDingtalkSessionWebhook(sessionWebhook);
|
||||
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, {
|
||||
body: { msgtype: 'text', text: { content } },
|
||||
body: { msgtype: 'text', text: { content }, ...replyAt },
|
||||
headers: { 'x-acs-dingtalk-access-token': token },
|
||||
signal,
|
||||
action: '消息回复',
|
||||
|
|
|
|||
|
|
@ -548,7 +548,7 @@ export class DingtalkHarnessBridge {
|
|||
`[dsh-dingtalk] failed to process a command [${failure.referenceId}]`,
|
||||
safeErrorDiagnostic(error),
|
||||
);
|
||||
return this.#send(sessionWebhook, messageFailureText(failure)).catch(() => undefined);
|
||||
return this.#send(sessionWebhook, messageFailureText(failure), this.#atUsersFor(message)).catch(() => undefined);
|
||||
}).finally(() => {
|
||||
this.#acceptedMessageIds.delete(messageId);
|
||||
this.#commandTasks.delete(task);
|
||||
|
|
@ -570,7 +570,7 @@ export class DingtalkHarnessBridge {
|
|||
: null,
|
||||
isQuestionPending: () => this.#pendingInteractions.has(key),
|
||||
send: sessionWebhook
|
||||
? (reply) => this.#send(sessionWebhook, reply)
|
||||
? (reply) => this.#send(sessionWebhook, reply, this.#atUsersFor(message))
|
||||
: async () => undefined,
|
||||
});
|
||||
if (approvalReply) {
|
||||
|
|
@ -803,7 +803,7 @@ export class DingtalkHarnessBridge {
|
|||
]);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
@ -829,7 +829,7 @@ export class DingtalkHarnessBridge {
|
|||
`[dsh-dingtalk] failed to process a batch input message [${failure.referenceId}]`,
|
||||
safeErrorDiagnostic(error),
|
||||
);
|
||||
await this.#send(sessionWebhook, messageFailureText(failure)).catch(() => undefined);
|
||||
await this.#send(sessionWebhook, messageFailureText(failure), this.#atUsersFor(message)).catch(() => undefined);
|
||||
}).finally(() => {
|
||||
this.#acceptedMessageIds.delete(messageId);
|
||||
this.#commandTasks.delete(task);
|
||||
|
|
@ -881,23 +881,23 @@ export class DingtalkHarnessBridge {
|
|||
let batchSettled = batchSubmission === null;
|
||||
try {
|
||||
if (!text && !hasImages && !hasFiles) {
|
||||
await this.#send(sessionWebhook, t('目前支持文字、图片和文件消息。'));
|
||||
await this.#send(sessionWebhook, t('目前支持文字、图片和文件消息。'), this.#atUsersFor(message));
|
||||
return;
|
||||
}
|
||||
|
||||
const command = text.toLowerCase();
|
||||
if (isPlainText && !hasImages && !hasFiles && command === '/help') {
|
||||
await this.#send(sessionWebhook, helpText());
|
||||
await this.#send(sessionWebhook, helpText(), this.#atUsersFor(message));
|
||||
return;
|
||||
}
|
||||
if (isPlainText && !hasImages && !hasFiles && command === '/status') {
|
||||
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;
|
||||
}
|
||||
if (isPlainText && !hasImages && !hasFiles && command === '/new') {
|
||||
await this.#state.clearSession(key);
|
||||
await this.#send(sessionWebhook, t('已开启新会话。请发送你的问题。'));
|
||||
await this.#send(sessionWebhook, t('已开启新会话。请发送你的问题。'), this.#atUsersFor(message));
|
||||
return;
|
||||
}
|
||||
const workspaceCommand = isPlainText && !hasImages && !hasFiles
|
||||
|
|
@ -905,7 +905,7 @@ export class DingtalkHarnessBridge {
|
|||
: null;
|
||||
if (workspaceCommand) {
|
||||
for (const reply of workspaceCommand.messages ?? [workspaceCommand.message]) {
|
||||
await this.#send(sessionWebhook, reply);
|
||||
await this.#send(sessionWebhook, reply, this.#atUsersFor(message));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -919,7 +919,7 @@ export class DingtalkHarnessBridge {
|
|||
)
|
||||
: null;
|
||||
if (compactCommand) {
|
||||
await this.#send(sessionWebhook, compactCommand.message);
|
||||
await this.#send(sessionWebhook, compactCommand.message, this.#atUsersFor(message));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -984,7 +984,7 @@ export class DingtalkHarnessBridge {
|
|||
textReceipt = createDeliveryReceipt({
|
||||
deliveryId: messageId,
|
||||
presentation: 'dingtalk-text',
|
||||
providerMessageIds: await this.#send(sessionWebhook, answerText),
|
||||
providerMessageIds: await this.#send(sessionWebhook, answerText, this.#atUsersFor(message)),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -1045,7 +1045,7 @@ export class DingtalkHarnessBridge {
|
|||
? `${errorText}\n\n${batchFailureMessage}`
|
||||
: errorText;
|
||||
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 {
|
||||
this.#logger.error?.('[dsh-dingtalk] failed to send the safe error reply');
|
||||
}
|
||||
|
|
@ -1098,7 +1098,7 @@ export class DingtalkHarnessBridge {
|
|||
const text = message?.msgtype === 'text' ? nonEmptyString(message?.text?.content) : null;
|
||||
if (!text) {
|
||||
try {
|
||||
await this.#send(sessionWebhook, t('请用文字回答当前问题。'));
|
||||
await this.#send(sessionWebhook, t('请用文字回答当前问题。'), this.#atUsersFor(message));
|
||||
} catch {
|
||||
this.#logger.error?.('[dsh-dingtalk] failed to reject a non-text interaction reply');
|
||||
}
|
||||
|
|
@ -1109,7 +1109,7 @@ export class DingtalkHarnessBridge {
|
|||
if (!pending || pending !== expected || pending.submitting) {
|
||||
if (claimed && (!pending || pending !== expected)) {
|
||||
try {
|
||||
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT));
|
||||
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT), this.#atUsersFor(message));
|
||||
} catch {
|
||||
this.#logger.error?.('[dsh-dingtalk] failed to send an expired interaction notice');
|
||||
}
|
||||
|
|
@ -1174,7 +1174,7 @@ export class DingtalkHarnessBridge {
|
|||
if (error?.code === 'interaction-not-pending') {
|
||||
this.#clearPendingInteraction(key, pending.interactionId);
|
||||
try {
|
||||
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT));
|
||||
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT), this.#atUsersFor(message));
|
||||
} catch {
|
||||
this.#logger.error?.('[dsh-dingtalk] failed to send an expired interaction notice');
|
||||
}
|
||||
|
|
@ -1187,7 +1187,7 @@ export class DingtalkHarnessBridge {
|
|||
this.#status.lastError = t('回答提交失败。');
|
||||
this.#logger.error?.('[dsh-dingtalk] failed to answer a Harness interaction');
|
||||
try {
|
||||
await this.#send(sessionWebhook, t('回答提交失败,请重新发送当前问题的答案。'));
|
||||
await this.#send(sessionWebhook, t('回答提交失败,请重新发送当前问题的答案。'), this.#atUsersFor(message));
|
||||
} catch {
|
||||
this.#logger.error?.('[dsh-dingtalk] failed to send an interaction retry notice');
|
||||
}
|
||||
|
|
@ -1319,7 +1319,7 @@ export class DingtalkHarnessBridge {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT));
|
||||
await this.#send(sessionWebhook, t(INTERACTION_RESOLVED_TEXT), this.#atUsersFor(message));
|
||||
} catch {
|
||||
this.#logger.error?.('[dsh-dingtalk] failed to send an expired interaction notice');
|
||||
}
|
||||
|
|
@ -1363,7 +1363,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 = [];
|
||||
for (const chunk of splitDingtalkText(text, this.#maxMessageChars)) {
|
||||
this.#signal?.throwIfAborted();
|
||||
|
|
@ -1372,6 +1379,7 @@ export class DingtalkHarnessBridge {
|
|||
clientSecret: this.#clientSecret,
|
||||
sessionWebhook,
|
||||
text: chunk,
|
||||
at,
|
||||
signal: this.#signal,
|
||||
});
|
||||
providerMessageIds.push(...providerMessageIdsFor(result));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue