@max1874/feishu 0.2.17 → 0.2.18
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/package.json +1 -1
- package/src/bot.ts +10 -7
- package/src/docx.ts +34 -2
- package/src/runtime.ts +19 -0
package/package.json
CHANGED
package/src/bot.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type HistoryEntry,
|
|
8
8
|
} from "openclaw/plugin-sdk";
|
|
9
9
|
import type { FeishuConfig, FeishuMessageContext, FeishuMediaInfo } from "./types.js";
|
|
10
|
-
import { getFeishuRuntime } from "./runtime.js";
|
|
10
|
+
import { getFeishuRuntime, runWithConversationContext } from "./runtime.js";
|
|
11
11
|
import { createFeishuClient } from "./client.js";
|
|
12
12
|
import {
|
|
13
13
|
resolveFeishuGroupConfig,
|
|
@@ -763,12 +763,15 @@ export async function handleFeishuMessage(params: {
|
|
|
763
763
|
|
|
764
764
|
log(`feishu: dispatching to agent (session=${route.sessionKey})`);
|
|
765
765
|
|
|
766
|
-
const { queuedFinal, counts } = await
|
|
767
|
-
ctx:
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
766
|
+
const { queuedFinal, counts } = await runWithConversationContext(
|
|
767
|
+
{ senderOpenId: ctx.senderOpenId, chatId: ctx.chatId, chatType: isGroup ? "group" : "p2p" },
|
|
768
|
+
() => core.channel.reply.dispatchReplyFromConfig({
|
|
769
|
+
ctx: ctxPayload,
|
|
770
|
+
cfg,
|
|
771
|
+
dispatcher,
|
|
772
|
+
replyOptions,
|
|
773
|
+
}),
|
|
774
|
+
);
|
|
772
775
|
|
|
773
776
|
markDispatchIdle();
|
|
774
777
|
|
package/src/docx.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
3
3
|
import { createFeishuClient } from "./client.js";
|
|
4
|
+
import { getConversationContext } from "./runtime.js";
|
|
4
5
|
import type { FeishuConfig } from "./types.js";
|
|
5
6
|
import type * as Lark from "@larksuiteoapi/node-sdk";
|
|
6
7
|
import { Readable } from "stream";
|
|
@@ -490,6 +491,23 @@ async function setDocPermissionTenantEditable(client: Lark.Client, docToken: str
|
|
|
490
491
|
|
|
491
492
|
export type DocPermission = "private" | "tenant_editable";
|
|
492
493
|
|
|
494
|
+
/** Grant full_access to a user (ou_*) or chat group (oc_*) */
|
|
495
|
+
async function grantFullAccess(client: Lark.Client, docToken: string, memberId: string) {
|
|
496
|
+
const isChat = memberId.startsWith("oc_");
|
|
497
|
+
const res = await client.drive.permissionMember.create({
|
|
498
|
+
path: { token: docToken },
|
|
499
|
+
params: { type: "docx", need_notification: false },
|
|
500
|
+
data: {
|
|
501
|
+
member_type: isChat ? "openchat" : "openid",
|
|
502
|
+
member_id: memberId,
|
|
503
|
+
perm: "full_access",
|
|
504
|
+
type: isChat ? "chat" : "user",
|
|
505
|
+
},
|
|
506
|
+
});
|
|
507
|
+
if (res.code !== 0) throw new Error(res.msg);
|
|
508
|
+
return res.data;
|
|
509
|
+
}
|
|
510
|
+
|
|
493
511
|
async function createDoc(
|
|
494
512
|
client: Lark.Client,
|
|
495
513
|
title: string,
|
|
@@ -503,16 +521,30 @@ async function createDoc(
|
|
|
503
521
|
const doc = res.data?.document;
|
|
504
522
|
const docId = doc?.document_id;
|
|
505
523
|
|
|
506
|
-
// Set permission if requested
|
|
524
|
+
// Set link sharing permission if requested
|
|
507
525
|
if (permission === "tenant_editable" && docId) {
|
|
508
526
|
await setDocPermissionTenantEditable(client, docId);
|
|
509
527
|
}
|
|
510
528
|
|
|
529
|
+
// Auto-grant full_access to conversation participant (user in DM, chat group in group)
|
|
530
|
+
let grantedTo: string | undefined;
|
|
531
|
+
const convCtx = getConversationContext();
|
|
532
|
+
if (docId && convCtx) {
|
|
533
|
+
const memberId = convCtx.chatType === "group" ? convCtx.chatId : convCtx.senderOpenId;
|
|
534
|
+
try {
|
|
535
|
+
await grantFullAccess(client, docId, memberId);
|
|
536
|
+
grantedTo = memberId;
|
|
537
|
+
} catch {
|
|
538
|
+
// Non-fatal: doc is created, permission grant failed
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
511
542
|
return {
|
|
512
543
|
document_id: docId,
|
|
513
544
|
title: doc?.title,
|
|
514
545
|
url: `https://feishu.cn/docx/${docId}`,
|
|
515
546
|
permission: permission ?? "private",
|
|
547
|
+
...(grantedTo && { granted_full_access_to: grantedTo }),
|
|
516
548
|
};
|
|
517
549
|
}
|
|
518
550
|
|
|
@@ -854,7 +886,7 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
|
|
|
854
886
|
name: "feishu_doc_create",
|
|
855
887
|
label: "Feishu Doc Create",
|
|
856
888
|
description:
|
|
857
|
-
"Create a new empty Feishu document.
|
|
889
|
+
"Create a new empty Feishu document. Automatically grants full_access (manage) permission to the current conversation participant.",
|
|
858
890
|
parameters: CreateDocSchema,
|
|
859
891
|
async execute(_toolCallId, params) {
|
|
860
892
|
const { title, folder_token, permission } = params as {
|
package/src/runtime.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
3
|
|
|
3
4
|
let runtime: PluginRuntime | null = null;
|
|
4
5
|
|
|
@@ -12,3 +13,21 @@ export function getFeishuRuntime(): PluginRuntime {
|
|
|
12
13
|
}
|
|
13
14
|
return runtime;
|
|
14
15
|
}
|
|
16
|
+
|
|
17
|
+
// --- Conversation context (tracked via AsyncLocalStorage for concurrency safety) ---
|
|
18
|
+
|
|
19
|
+
export interface FeishuConversationContext {
|
|
20
|
+
senderOpenId: string;
|
|
21
|
+
chatId: string;
|
|
22
|
+
chatType: "group" | "p2p";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const conversationStore = new AsyncLocalStorage<FeishuConversationContext>();
|
|
26
|
+
|
|
27
|
+
export function runWithConversationContext<T>(ctx: FeishuConversationContext, fn: () => T): T {
|
|
28
|
+
return conversationStore.run(ctx, fn);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getConversationContext(): FeishuConversationContext | undefined {
|
|
32
|
+
return conversationStore.getStore();
|
|
33
|
+
}
|