mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-28 11:41:04 +00:00
feat(channels): add DingTalk reply/quote message context support
- Add interfaces for DingTalkRepliedMsg and DingTalkRichTextPart types - Support both newer text.repliedMsg and legacy quoteMessage formats - Extract quoted message context with isReplyToBot detection - Summarize replied content (text, richText, media placeholders) This enables the bot to understand when users are replying to specific messages and provides context about what message is being referenced. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
a61189b232
commit
9f4dd53198
1 changed files with 117 additions and 8 deletions
|
|
@ -17,7 +17,26 @@ import type {
|
|||
* Raw DingTalk message data — the SDK's RobotMessage type only covers text,
|
||||
* but DingTalk sends richer payloads for richText, picture, file, etc.
|
||||
*/
|
||||
|
||||
|
||||
interface DingTalkRichTextPart {
|
||||
type?: string;
|
||||
text?: string;
|
||||
downloadCode?: string;
|
||||
atName?: string;
|
||||
}
|
||||
|
||||
interface DingTalkRepliedMsg {
|
||||
msgId?: string;
|
||||
msgType?: string;
|
||||
senderId?: string;
|
||||
content?: {
|
||||
text?: string;
|
||||
richText?: DingTalkRichTextPart[];
|
||||
downloadCode?: string;
|
||||
fileName?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface DingTalkMessageData {
|
||||
msgId?: string;
|
||||
msgtype?: string;
|
||||
|
|
@ -27,14 +46,21 @@ interface DingTalkMessageData {
|
|||
senderId?: string;
|
||||
senderStaffId?: string;
|
||||
senderNick?: string;
|
||||
chatbotUserId?: string;
|
||||
isInAtList?: boolean;
|
||||
text?: { content?: string };
|
||||
text?: {
|
||||
content?: string;
|
||||
isReplyMsg?: boolean;
|
||||
repliedMsg?: DingTalkRepliedMsg;
|
||||
};
|
||||
quoteMessage?: {
|
||||
msgId?: string;
|
||||
senderId?: string;
|
||||
text?: { content?: string };
|
||||
msgtype?: string;
|
||||
};
|
||||
content?: {
|
||||
richText?: Array<{
|
||||
type?: string;
|
||||
text?: string;
|
||||
downloadCode?: string;
|
||||
}>;
|
||||
richText?: DingTalkRichTextPart[];
|
||||
downloadCode?: string;
|
||||
fileName?: string;
|
||||
recognition?: string;
|
||||
|
|
@ -210,6 +236,85 @@ export class DingtalkChannel extends ChannelBase {
|
|||
process.stderr.write(`[DingTalk:${this.name}] Disconnected.\n`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract quoted/referenced message context from a reply.
|
||||
* DingTalk provides this via text.repliedMsg (newer) or quoteMessage (legacy).
|
||||
*/
|
||||
private extractQuotedContext(data: DingTalkMessageData): {
|
||||
referencedText?: string;
|
||||
isReplyToBot: boolean;
|
||||
} {
|
||||
// Newer format: text.repliedMsg
|
||||
if (data.text?.isReplyMsg && data.text.repliedMsg) {
|
||||
const replied = data.text.repliedMsg;
|
||||
const isReplyToBot =
|
||||
!!data.chatbotUserId && replied.senderId === data.chatbotUserId;
|
||||
|
||||
// Note: DingTalk doesn't include content for interactiveCard replies
|
||||
// (bot responses sent via webhook). Only user message quotes have text.
|
||||
const text = this.summarizeRepliedContent(replied);
|
||||
return { referencedText: text || undefined, isReplyToBot };
|
||||
}
|
||||
|
||||
// Legacy format: quoteMessage
|
||||
if (data.quoteMessage) {
|
||||
const quote = data.quoteMessage;
|
||||
const isReplyToBot =
|
||||
!!data.chatbotUserId && quote.senderId === data.chatbotUserId;
|
||||
const text = quote.text?.content?.trim();
|
||||
return { referencedText: text || undefined, isReplyToBot };
|
||||
}
|
||||
|
||||
return { isReplyToBot: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a text summary from a repliedMsg, handling text, richText, and
|
||||
* media message types with placeholders.
|
||||
*/
|
||||
private summarizeRepliedContent(replied: DingTalkRepliedMsg): string {
|
||||
const msgType = replied.msgType;
|
||||
const content = replied.content;
|
||||
|
||||
// Direct text content
|
||||
if (content?.text?.trim()) {
|
||||
return content.text.trim();
|
||||
}
|
||||
|
||||
// RichText: concatenate text parts, placeholder for images
|
||||
if (content?.richText && Array.isArray(content.richText)) {
|
||||
const parts: string[] = [];
|
||||
for (const part of content.richText) {
|
||||
const partType = part.type || 'text';
|
||||
if (partType === 'text' && part.text) {
|
||||
parts.push(part.text);
|
||||
} else if (partType === 'picture') {
|
||||
parts.push('[image]');
|
||||
} else if (partType === 'at' && part.atName) {
|
||||
parts.push(`@${part.atName}`);
|
||||
}
|
||||
}
|
||||
const summary = parts.join('').trim();
|
||||
if (summary) return summary;
|
||||
}
|
||||
|
||||
// Media type placeholders
|
||||
switch (msgType) {
|
||||
case 'picture':
|
||||
return '[image]';
|
||||
case 'file':
|
||||
return `[file: ${content?.fileName || 'file'}]`;
|
||||
case 'audio':
|
||||
return '[audio]';
|
||||
case 'video':
|
||||
return '[video]';
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract text and media download codes from an incoming DingTalk message.
|
||||
* Handles text, richText, picture, file, audio, and video message types.
|
||||
|
|
@ -377,6 +482,9 @@ export class DingtalkChannel extends ChannelBase {
|
|||
cleanText = cleanText.replace(/@\S+/g, '').trim();
|
||||
}
|
||||
|
||||
// Extract quoted message context
|
||||
const quoted = this.extractQuotedContext(data);
|
||||
|
||||
const chatId = conversationId || sessionWebhook;
|
||||
|
||||
const envelope: Envelope = {
|
||||
|
|
@ -387,7 +495,8 @@ export class DingtalkChannel extends ChannelBase {
|
|||
text: cleanText || content.text,
|
||||
isGroup,
|
||||
isMentioned,
|
||||
isReplyToBot: false,
|
||||
isReplyToBot: quoted.isReplyToBot,
|
||||
referencedText: quoted.referencedText,
|
||||
};
|
||||
|
||||
// Attach 👀 reaction, process message, then recall reaction
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue