mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 23:21:23 +00:00
feat(feishu): replace built-in SDK with community plugin
Replace the built-in Feishu SDK with the community-maintained clawdbot-feishu plugin by @m1heng. Changes: - Remove src/feishu/ directory (19 files) - Remove src/channels/plugins/outbound/feishu.ts - Remove src/channels/plugins/normalize/feishu.ts - Remove src/config/types.feishu.ts - Remove feishu exports from plugin-sdk/index.ts - Remove FeishuConfig from types.channels.ts New features in community plugin: - Document tools (read/create/edit Feishu docs) - Wiki tools (navigate/manage knowledge base) - Drive tools (folder/file management) - Bitable tools (read/write table records) - Permission tools (collaborator management) - Emoji reactions support - Typing indicators - Rich media support (bidirectional image/file transfer) - @mention handling - Skills for feishu-doc, feishu-wiki, feishu-drive, feishu-perm Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
118
extensions/feishu/src/mention.ts
Normal file
118
extensions/feishu/src/mention.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import type { FeishuMessageEvent } from "./bot.js";
|
||||
|
||||
/**
|
||||
* Mention target user info
|
||||
*/
|
||||
export type MentionTarget = {
|
||||
openId: string;
|
||||
name: string;
|
||||
key: string; // Placeholder in original message, e.g. @_user_1
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract mention targets from message event (excluding the bot itself)
|
||||
*/
|
||||
export function extractMentionTargets(
|
||||
event: FeishuMessageEvent,
|
||||
botOpenId?: string,
|
||||
): MentionTarget[] {
|
||||
const mentions = event.message.mentions ?? [];
|
||||
|
||||
return mentions
|
||||
.filter((m) => {
|
||||
// Exclude the bot itself
|
||||
if (botOpenId && m.id.open_id === botOpenId) return false;
|
||||
// Must have open_id
|
||||
return !!m.id.open_id;
|
||||
})
|
||||
.map((m) => ({
|
||||
openId: m.id.open_id!,
|
||||
name: m.name,
|
||||
key: m.key,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if message is a mention forward request
|
||||
* Rules:
|
||||
* - Group: message mentions bot + at least one other user
|
||||
* - DM: message mentions any user (no need to mention bot)
|
||||
*/
|
||||
export function isMentionForwardRequest(event: FeishuMessageEvent, botOpenId?: string): boolean {
|
||||
const mentions = event.message.mentions ?? [];
|
||||
if (mentions.length === 0) return false;
|
||||
|
||||
const isDirectMessage = event.message.chat_type === "p2p";
|
||||
const hasOtherMention = mentions.some((m) => m.id.open_id !== botOpenId);
|
||||
|
||||
if (isDirectMessage) {
|
||||
// DM: trigger if any non-bot user is mentioned
|
||||
return hasOtherMention;
|
||||
} else {
|
||||
// Group: need to mention both bot and other users
|
||||
const hasBotMention = mentions.some((m) => m.id.open_id === botOpenId);
|
||||
return hasBotMention && hasOtherMention;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract message body from text (remove @ placeholders)
|
||||
*/
|
||||
export function extractMessageBody(text: string, allMentionKeys: string[]): string {
|
||||
let result = text;
|
||||
|
||||
// Remove all @ placeholders
|
||||
for (const key of allMentionKeys) {
|
||||
result = result.replace(new RegExp(key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), "");
|
||||
}
|
||||
|
||||
return result.replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format @mention for text message
|
||||
*/
|
||||
export function formatMentionForText(target: MentionTarget): string {
|
||||
return `<at user_id="${target.openId}">${target.name}</at>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format @everyone for text message
|
||||
*/
|
||||
export function formatMentionAllForText(): string {
|
||||
return `<at user_id="all">Everyone</at>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format @mention for card message (lark_md)
|
||||
*/
|
||||
export function formatMentionForCard(target: MentionTarget): string {
|
||||
return `<at id=${target.openId}></at>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format @everyone for card message
|
||||
*/
|
||||
export function formatMentionAllForCard(): string {
|
||||
return `<at id=all></at>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build complete message with @mentions (text format)
|
||||
*/
|
||||
export function buildMentionedMessage(targets: MentionTarget[], message: string): string {
|
||||
if (targets.length === 0) return message;
|
||||
|
||||
const mentionParts = targets.map((t) => formatMentionForText(t));
|
||||
return `${mentionParts.join(" ")} ${message}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build card content with @mentions (Markdown format)
|
||||
*/
|
||||
export function buildMentionedCardContent(targets: MentionTarget[], message: string): string {
|
||||
if (targets.length === 0) return message;
|
||||
|
||||
const mentionParts = targets.map((t) => formatMentionForCard(t));
|
||||
return `${mentionParts.join(" ")} ${message}`;
|
||||
}
|
||||
Reference in New Issue
Block a user