@openclaw/msteams 2026.2.15 → 2026.2.19
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/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/src/attachments/download.ts +5 -5
- package/src/attachments/graph.ts +6 -6
- package/src/attachments/html.ts +1 -1
- package/src/attachments.test.ts +3 -2
- package/src/channel.directory.test.ts +21 -3
- package/src/conversation-store-fs.test.ts +3 -16
- package/src/messenger.test.ts +6 -0
- package/src/monitor-handler/inbound-media.ts +1 -1
- package/src/monitor-handler/message-handler.ts +3 -3
- package/src/monitor-handler.ts +3 -3
- package/src/monitor.ts +2 -2
- package/src/onboarding.ts +3 -4
- package/src/polls.test.ts +2 -15
- package/src/reply-dispatcher.ts +2 -2
- package/src/send-context.ts +1 -1
- package/src/send.ts +53 -42
- package/src/store-fs.ts +3 -25
- package/src/test-runtime.ts +16 -0
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
MSTeamsAccessTokenProvider,
|
|
3
|
-
MSTeamsAttachmentLike,
|
|
4
|
-
MSTeamsInboundMedia,
|
|
5
|
-
} from "./types.js";
|
|
6
1
|
import { getMSTeamsRuntime } from "../runtime.js";
|
|
7
2
|
import {
|
|
8
3
|
extractInlineImageCandidates,
|
|
@@ -14,6 +9,11 @@ import {
|
|
|
14
9
|
resolveAuthAllowedHosts,
|
|
15
10
|
resolveAllowedHosts,
|
|
16
11
|
} from "./shared.js";
|
|
12
|
+
import type {
|
|
13
|
+
MSTeamsAccessTokenProvider,
|
|
14
|
+
MSTeamsAttachmentLike,
|
|
15
|
+
MSTeamsInboundMedia,
|
|
16
|
+
} from "./types.js";
|
|
17
17
|
|
|
18
18
|
type DownloadCandidate = {
|
|
19
19
|
url: string;
|
package/src/attachments/graph.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
MSTeamsAccessTokenProvider,
|
|
3
|
-
MSTeamsAttachmentLike,
|
|
4
|
-
MSTeamsGraphMediaResult,
|
|
5
|
-
MSTeamsInboundMedia,
|
|
6
|
-
} from "./types.js";
|
|
7
1
|
import { getMSTeamsRuntime } from "../runtime.js";
|
|
8
2
|
import { downloadMSTeamsAttachments } from "./download.js";
|
|
9
3
|
import {
|
|
@@ -13,6 +7,12 @@ import {
|
|
|
13
7
|
normalizeContentType,
|
|
14
8
|
resolveAllowedHosts,
|
|
15
9
|
} from "./shared.js";
|
|
10
|
+
import type {
|
|
11
|
+
MSTeamsAccessTokenProvider,
|
|
12
|
+
MSTeamsAttachmentLike,
|
|
13
|
+
MSTeamsGraphMediaResult,
|
|
14
|
+
MSTeamsInboundMedia,
|
|
15
|
+
} from "./types.js";
|
|
16
16
|
|
|
17
17
|
type GraphHostedContent = {
|
|
18
18
|
id?: string | null;
|
package/src/attachments/html.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { MSTeamsAttachmentLike, MSTeamsHtmlAttachmentSummary } from "./types.js";
|
|
2
1
|
import {
|
|
3
2
|
ATTACHMENT_TAG_RE,
|
|
4
3
|
extractHtmlFromAttachment,
|
|
@@ -7,6 +6,7 @@ import {
|
|
|
7
6
|
isLikelyImageAttachment,
|
|
8
7
|
safeHostForUrl,
|
|
9
8
|
} from "./shared.js";
|
|
9
|
+
import type { MSTeamsAttachmentLike, MSTeamsHtmlAttachmentSummary } from "./types.js";
|
|
10
10
|
|
|
11
11
|
export function summarizeMSTeamsHtmlAttachments(
|
|
12
12
|
attachments: MSTeamsAttachmentLike[] | undefined,
|
package/src/attachments.test.ts
CHANGED
|
@@ -10,11 +10,12 @@ const saveMediaBufferMock = vi.fn(async () => ({
|
|
|
10
10
|
|
|
11
11
|
const runtimeStub = {
|
|
12
12
|
media: {
|
|
13
|
-
detectMime:
|
|
13
|
+
detectMime: detectMimeMock as unknown as PluginRuntime["media"]["detectMime"],
|
|
14
14
|
},
|
|
15
15
|
channel: {
|
|
16
16
|
media: {
|
|
17
|
-
saveMediaBuffer:
|
|
17
|
+
saveMediaBuffer:
|
|
18
|
+
saveMediaBufferMock as unknown as PluginRuntime["channel"]["media"]["saveMediaBuffer"],
|
|
18
19
|
},
|
|
19
20
|
},
|
|
20
21
|
} as unknown as PluginRuntime;
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
3
|
import { msteamsPlugin } from "./channel.js";
|
|
4
4
|
|
|
5
5
|
describe("msteams directory", () => {
|
|
6
|
+
const runtimeEnv: RuntimeEnv = {
|
|
7
|
+
log: () => {},
|
|
8
|
+
error: () => {},
|
|
9
|
+
exit: (code: number): never => {
|
|
10
|
+
throw new Error(`exit ${code}`);
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
6
14
|
it("lists peers and groups from config", async () => {
|
|
7
15
|
const cfg = {
|
|
8
16
|
channels: {
|
|
@@ -26,7 +34,12 @@ describe("msteams directory", () => {
|
|
|
26
34
|
expect(msteamsPlugin.directory?.listGroups).toBeTruthy();
|
|
27
35
|
|
|
28
36
|
await expect(
|
|
29
|
-
msteamsPlugin.directory!.listPeers({
|
|
37
|
+
msteamsPlugin.directory!.listPeers!({
|
|
38
|
+
cfg,
|
|
39
|
+
query: undefined,
|
|
40
|
+
limit: undefined,
|
|
41
|
+
runtime: runtimeEnv,
|
|
42
|
+
}),
|
|
30
43
|
).resolves.toEqual(
|
|
31
44
|
expect.arrayContaining([
|
|
32
45
|
{ kind: "user", id: "user:alice" },
|
|
@@ -37,7 +50,12 @@ describe("msteams directory", () => {
|
|
|
37
50
|
);
|
|
38
51
|
|
|
39
52
|
await expect(
|
|
40
|
-
msteamsPlugin.directory!.listGroups({
|
|
53
|
+
msteamsPlugin.directory!.listGroups!({
|
|
54
|
+
cfg,
|
|
55
|
+
query: undefined,
|
|
56
|
+
limit: undefined,
|
|
57
|
+
runtime: runtimeEnv,
|
|
58
|
+
}),
|
|
41
59
|
).resolves.toEqual(
|
|
42
60
|
expect.arrayContaining([
|
|
43
61
|
{ kind: "group", id: "conversation:chan1" },
|
|
@@ -1,28 +1,15 @@
|
|
|
1
|
-
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import os from "node:os";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
6
|
-
import type { StoredConversationReference } from "./conversation-store.js";
|
|
7
5
|
import { createMSTeamsConversationStoreFs } from "./conversation-store-fs.js";
|
|
6
|
+
import type { StoredConversationReference } from "./conversation-store.js";
|
|
8
7
|
import { setMSTeamsRuntime } from "./runtime.js";
|
|
9
|
-
|
|
10
|
-
const runtimeStub = {
|
|
11
|
-
state: {
|
|
12
|
-
resolveStateDir: (env: NodeJS.ProcessEnv = process.env, homedir?: () => string) => {
|
|
13
|
-
const override = env.OPENCLAW_STATE_DIR?.trim() || env.OPENCLAW_STATE_DIR?.trim();
|
|
14
|
-
if (override) {
|
|
15
|
-
return override;
|
|
16
|
-
}
|
|
17
|
-
const resolvedHome = homedir ? homedir() : os.homedir();
|
|
18
|
-
return path.join(resolvedHome, ".openclaw");
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
} as unknown as PluginRuntime;
|
|
8
|
+
import { msteamsRuntimeStub } from "./test-runtime.js";
|
|
22
9
|
|
|
23
10
|
describe("msteams conversation store (fs)", () => {
|
|
24
11
|
beforeEach(() => {
|
|
25
|
-
setMSTeamsRuntime(
|
|
12
|
+
setMSTeamsRuntime(msteamsRuntimeStub);
|
|
26
13
|
});
|
|
27
14
|
|
|
28
15
|
it("filters and prunes expired entries (but keeps legacy ones)", async () => {
|
package/src/messenger.test.ts
CHANGED
|
@@ -125,6 +125,7 @@ describe("msteams messenger", () => {
|
|
|
125
125
|
|
|
126
126
|
const adapter: MSTeamsAdapter = {
|
|
127
127
|
continueConversation: async () => {},
|
|
128
|
+
process: async () => {},
|
|
128
129
|
};
|
|
129
130
|
|
|
130
131
|
const ids = await sendMSTeamsMessages({
|
|
@@ -154,6 +155,7 @@ describe("msteams messenger", () => {
|
|
|
154
155
|
},
|
|
155
156
|
});
|
|
156
157
|
},
|
|
158
|
+
process: async () => {},
|
|
157
159
|
};
|
|
158
160
|
|
|
159
161
|
const ids = await sendMSTeamsMessages({
|
|
@@ -191,6 +193,7 @@ describe("msteams messenger", () => {
|
|
|
191
193
|
|
|
192
194
|
const adapter: MSTeamsAdapter = {
|
|
193
195
|
continueConversation: async () => {},
|
|
196
|
+
process: async () => {},
|
|
194
197
|
};
|
|
195
198
|
|
|
196
199
|
const ids = await sendMSTeamsMessages({
|
|
@@ -250,6 +253,7 @@ describe("msteams messenger", () => {
|
|
|
250
253
|
|
|
251
254
|
const adapter: MSTeamsAdapter = {
|
|
252
255
|
continueConversation: async () => {},
|
|
256
|
+
process: async () => {},
|
|
253
257
|
};
|
|
254
258
|
|
|
255
259
|
const ids = await sendMSTeamsMessages({
|
|
@@ -277,6 +281,7 @@ describe("msteams messenger", () => {
|
|
|
277
281
|
|
|
278
282
|
const adapter: MSTeamsAdapter = {
|
|
279
283
|
continueConversation: async () => {},
|
|
284
|
+
process: async () => {},
|
|
280
285
|
};
|
|
281
286
|
|
|
282
287
|
await expect(
|
|
@@ -310,6 +315,7 @@ describe("msteams messenger", () => {
|
|
|
310
315
|
},
|
|
311
316
|
});
|
|
312
317
|
},
|
|
318
|
+
process: async () => {},
|
|
313
319
|
};
|
|
314
320
|
|
|
315
321
|
const ids = await sendMSTeamsMessages({
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { MSTeamsTurnContext } from "../sdk-types.js";
|
|
2
1
|
import {
|
|
3
2
|
buildMSTeamsGraphMessageUrls,
|
|
4
3
|
downloadMSTeamsAttachments,
|
|
@@ -8,6 +7,7 @@ import {
|
|
|
8
7
|
type MSTeamsHtmlAttachmentSummary,
|
|
9
8
|
type MSTeamsInboundMedia,
|
|
10
9
|
} from "../attachments.js";
|
|
10
|
+
import type { MSTeamsTurnContext } from "../sdk-types.js";
|
|
11
11
|
|
|
12
12
|
type MSTeamsLogger = {
|
|
13
13
|
debug?: (message: string, meta?: Record<string, unknown>) => void;
|
|
@@ -9,15 +9,13 @@ import {
|
|
|
9
9
|
formatAllowlistMatchMeta,
|
|
10
10
|
type HistoryEntry,
|
|
11
11
|
} from "openclaw/plugin-sdk";
|
|
12
|
-
import type { StoredConversationReference } from "../conversation-store.js";
|
|
13
|
-
import type { MSTeamsMessageHandlerDeps } from "../monitor-handler.js";
|
|
14
|
-
import type { MSTeamsTurnContext } from "../sdk-types.js";
|
|
15
12
|
import {
|
|
16
13
|
buildMSTeamsAttachmentPlaceholder,
|
|
17
14
|
buildMSTeamsMediaPayload,
|
|
18
15
|
type MSTeamsAttachmentLike,
|
|
19
16
|
summarizeMSTeamsHtmlAttachments,
|
|
20
17
|
} from "../attachments.js";
|
|
18
|
+
import type { StoredConversationReference } from "../conversation-store.js";
|
|
21
19
|
import { formatUnknownError } from "../errors.js";
|
|
22
20
|
import {
|
|
23
21
|
extractMSTeamsConversationMessageId,
|
|
@@ -26,6 +24,7 @@ import {
|
|
|
26
24
|
stripMSTeamsMentionTags,
|
|
27
25
|
wasMSTeamsBotMentioned,
|
|
28
26
|
} from "../inbound.js";
|
|
27
|
+
import type { MSTeamsMessageHandlerDeps } from "../monitor-handler.js";
|
|
29
28
|
import {
|
|
30
29
|
isMSTeamsGroupAllowed,
|
|
31
30
|
resolveMSTeamsAllowlistMatch,
|
|
@@ -35,6 +34,7 @@ import {
|
|
|
35
34
|
import { extractMSTeamsPollVote } from "../polls.js";
|
|
36
35
|
import { createMSTeamsReplyDispatcher } from "../reply-dispatcher.js";
|
|
37
36
|
import { getMSTeamsRuntime } from "../runtime.js";
|
|
37
|
+
import type { MSTeamsTurnContext } from "../sdk-types.js";
|
|
38
38
|
import { recordMSTeamsSentMessage, wasMSTeamsMessageSent } from "../sent-message-cache.js";
|
|
39
39
|
import { resolveMSTeamsInboundMedia } from "./inbound-media.js";
|
|
40
40
|
|
package/src/monitor-handler.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
2
2
|
import type { MSTeamsConversationStore } from "./conversation-store.js";
|
|
3
|
+
import { buildFileInfoCard, parseFileConsentInvoke, uploadToConsentUrl } from "./file-consent.js";
|
|
3
4
|
import type { MSTeamsAdapter } from "./messenger.js";
|
|
5
|
+
import { createMSTeamsMessageHandler } from "./monitor-handler/message-handler.js";
|
|
4
6
|
import type { MSTeamsMonitorLogger } from "./monitor-types.js";
|
|
7
|
+
import { getPendingUpload, removePendingUpload } from "./pending-uploads.js";
|
|
5
8
|
import type { MSTeamsPollStore } from "./polls.js";
|
|
6
9
|
import type { MSTeamsTurnContext } from "./sdk-types.js";
|
|
7
|
-
import { buildFileInfoCard, parseFileConsentInvoke, uploadToConsentUrl } from "./file-consent.js";
|
|
8
|
-
import { createMSTeamsMessageHandler } from "./monitor-handler/message-handler.js";
|
|
9
|
-
import { getPendingUpload, removePendingUpload } from "./pending-uploads.js";
|
|
10
10
|
|
|
11
11
|
export type MSTeamsAccessTokenProvider = {
|
|
12
12
|
getAccessToken: (scope: string) => Promise<string>;
|
package/src/monitor.ts
CHANGED
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
type OpenClawConfig,
|
|
7
7
|
type RuntimeEnv,
|
|
8
8
|
} from "openclaw/plugin-sdk";
|
|
9
|
-
import type { MSTeamsConversationStore } from "./conversation-store.js";
|
|
10
|
-
import type { MSTeamsAdapter } from "./messenger.js";
|
|
11
9
|
import { createMSTeamsConversationStoreFs } from "./conversation-store-fs.js";
|
|
10
|
+
import type { MSTeamsConversationStore } from "./conversation-store.js";
|
|
12
11
|
import { formatUnknownError } from "./errors.js";
|
|
12
|
+
import type { MSTeamsAdapter } from "./messenger.js";
|
|
13
13
|
import { registerMSTeamsHandlers, type MSTeamsActivityHandler } from "./monitor-handler.js";
|
|
14
14
|
import { createMSTeamsPollStoreFs, type MSTeamsPollStore } from "./polls.js";
|
|
15
15
|
import {
|
package/src/onboarding.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
addWildcardAllowFrom,
|
|
11
11
|
DEFAULT_ACCOUNT_ID,
|
|
12
12
|
formatDocsLink,
|
|
13
|
+
mergeAllowFromEntries,
|
|
13
14
|
promptChannelAccessConfig,
|
|
14
15
|
} from "openclaw/plugin-sdk";
|
|
15
16
|
import {
|
|
@@ -133,9 +134,7 @@ async function promptMSTeamsAllowFrom(params: {
|
|
|
133
134
|
);
|
|
134
135
|
continue;
|
|
135
136
|
}
|
|
136
|
-
const unique =
|
|
137
|
-
...new Set([...existing.map((v) => String(v).trim()).filter(Boolean), ...ids]),
|
|
138
|
-
];
|
|
137
|
+
const unique = mergeAllowFromEntries(existing, ids);
|
|
139
138
|
return setMSTeamsAllowFrom(params.cfg, unique);
|
|
140
139
|
}
|
|
141
140
|
|
|
@@ -149,7 +148,7 @@ async function promptMSTeamsAllowFrom(params: {
|
|
|
149
148
|
}
|
|
150
149
|
|
|
151
150
|
const ids = resolved.map((item) => item.id as string);
|
|
152
|
-
const unique =
|
|
151
|
+
const unique = mergeAllowFromEntries(existing, ids);
|
|
153
152
|
return setMSTeamsAllowFrom(params.cfg, unique);
|
|
154
153
|
}
|
|
155
154
|
}
|
package/src/polls.test.ts
CHANGED
|
@@ -1,27 +1,14 @@
|
|
|
1
|
-
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import os from "node:os";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
6
5
|
import { buildMSTeamsPollCard, createMSTeamsPollStoreFs, extractMSTeamsPollVote } from "./polls.js";
|
|
7
6
|
import { setMSTeamsRuntime } from "./runtime.js";
|
|
8
|
-
|
|
9
|
-
const runtimeStub = {
|
|
10
|
-
state: {
|
|
11
|
-
resolveStateDir: (env: NodeJS.ProcessEnv = process.env, homedir?: () => string) => {
|
|
12
|
-
const override = env.OPENCLAW_STATE_DIR?.trim() || env.OPENCLAW_STATE_DIR?.trim();
|
|
13
|
-
if (override) {
|
|
14
|
-
return override;
|
|
15
|
-
}
|
|
16
|
-
const resolvedHome = homedir ? homedir() : os.homedir();
|
|
17
|
-
return path.join(resolvedHome, ".openclaw");
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
} as unknown as PluginRuntime;
|
|
7
|
+
import { msteamsRuntimeStub } from "./test-runtime.js";
|
|
21
8
|
|
|
22
9
|
describe("msteams polls", () => {
|
|
23
10
|
beforeEach(() => {
|
|
24
|
-
setMSTeamsRuntime(
|
|
11
|
+
setMSTeamsRuntime(msteamsRuntimeStub);
|
|
25
12
|
});
|
|
26
13
|
|
|
27
14
|
it("builds poll cards with fallback text", () => {
|
package/src/reply-dispatcher.ts
CHANGED
|
@@ -9,8 +9,6 @@ import {
|
|
|
9
9
|
} from "openclaw/plugin-sdk";
|
|
10
10
|
import type { MSTeamsAccessTokenProvider } from "./attachments/types.js";
|
|
11
11
|
import type { StoredConversationReference } from "./conversation-store.js";
|
|
12
|
-
import type { MSTeamsMonitorLogger } from "./monitor-types.js";
|
|
13
|
-
import type { MSTeamsTurnContext } from "./sdk-types.js";
|
|
14
12
|
import {
|
|
15
13
|
classifyMSTeamsSendError,
|
|
16
14
|
formatMSTeamsSendErrorHint,
|
|
@@ -21,7 +19,9 @@ import {
|
|
|
21
19
|
renderReplyPayloadsToMessages,
|
|
22
20
|
sendMSTeamsMessages,
|
|
23
21
|
} from "./messenger.js";
|
|
22
|
+
import type { MSTeamsMonitorLogger } from "./monitor-types.js";
|
|
24
23
|
import { getMSTeamsRuntime } from "./runtime.js";
|
|
24
|
+
import type { MSTeamsTurnContext } from "./sdk-types.js";
|
|
25
25
|
|
|
26
26
|
export function createMSTeamsReplyDispatcher(params: {
|
|
27
27
|
cfg: OpenClawConfig;
|
package/src/send-context.ts
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
type PluginRuntime,
|
|
5
5
|
} from "openclaw/plugin-sdk";
|
|
6
6
|
import type { MSTeamsAccessTokenProvider } from "./attachments/types.js";
|
|
7
|
+
import { createMSTeamsConversationStoreFs } from "./conversation-store-fs.js";
|
|
7
8
|
import type {
|
|
8
9
|
MSTeamsConversationStore,
|
|
9
10
|
StoredConversationReference,
|
|
10
11
|
} from "./conversation-store.js";
|
|
11
12
|
import type { MSTeamsAdapter } from "./messenger.js";
|
|
12
|
-
import { createMSTeamsConversationStoreFs } from "./conversation-store-fs.js";
|
|
13
13
|
import { getMSTeamsRuntime } from "./runtime.js";
|
|
14
14
|
import { createMSTeamsAdapter, loadMSTeamsSdkWithAuth } from "./sdk.js";
|
|
15
15
|
import { resolveMSTeamsCredentials } from "./token.js";
|
package/src/send.ts
CHANGED
|
@@ -374,6 +374,45 @@ async function sendTextWithMedia(
|
|
|
374
374
|
};
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
type ProactiveActivityParams = {
|
|
378
|
+
adapter: MSTeamsProactiveContext["adapter"];
|
|
379
|
+
appId: string;
|
|
380
|
+
ref: MSTeamsProactiveContext["ref"];
|
|
381
|
+
activity: Record<string, unknown>;
|
|
382
|
+
errorPrefix: string;
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
async function sendProactiveActivity({
|
|
386
|
+
adapter,
|
|
387
|
+
appId,
|
|
388
|
+
ref,
|
|
389
|
+
activity,
|
|
390
|
+
errorPrefix,
|
|
391
|
+
}: ProactiveActivityParams): Promise<string> {
|
|
392
|
+
const baseRef = buildConversationReference(ref);
|
|
393
|
+
const proactiveRef = {
|
|
394
|
+
...baseRef,
|
|
395
|
+
activityId: undefined,
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
let messageId = "unknown";
|
|
399
|
+
try {
|
|
400
|
+
await adapter.continueConversation(appId, proactiveRef, async (ctx) => {
|
|
401
|
+
const response = await ctx.sendActivity(activity);
|
|
402
|
+
messageId = extractMessageId(response) ?? "unknown";
|
|
403
|
+
});
|
|
404
|
+
return messageId;
|
|
405
|
+
} catch (err) {
|
|
406
|
+
const classification = classifyMSTeamsSendError(err);
|
|
407
|
+
const hint = formatMSTeamsSendErrorHint(classification);
|
|
408
|
+
const status = classification.statusCode ? ` (HTTP ${classification.statusCode})` : "";
|
|
409
|
+
throw new Error(
|
|
410
|
+
`${errorPrefix} failed${status}: ${formatUnknownError(err)}${hint ? ` (${hint})` : ""}`,
|
|
411
|
+
{ cause: err },
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
377
416
|
/**
|
|
378
417
|
* Send a poll (Adaptive Card) to a Teams conversation or user.
|
|
379
418
|
*/
|
|
@@ -409,27 +448,13 @@ export async function sendPollMSTeams(
|
|
|
409
448
|
};
|
|
410
449
|
|
|
411
450
|
// Send poll via proactive conversation (Adaptive Cards require direct activity send)
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
try {
|
|
420
|
-
await adapter.continueConversation(appId, proactiveRef, async (ctx) => {
|
|
421
|
-
const response = await ctx.sendActivity(activity);
|
|
422
|
-
messageId = extractMessageId(response) ?? "unknown";
|
|
423
|
-
});
|
|
424
|
-
} catch (err) {
|
|
425
|
-
const classification = classifyMSTeamsSendError(err);
|
|
426
|
-
const hint = formatMSTeamsSendErrorHint(classification);
|
|
427
|
-
const status = classification.statusCode ? ` (HTTP ${classification.statusCode})` : "";
|
|
428
|
-
throw new Error(
|
|
429
|
-
`msteams poll send failed${status}: ${formatUnknownError(err)}${hint ? ` (${hint})` : ""}`,
|
|
430
|
-
{ cause: err },
|
|
431
|
-
);
|
|
432
|
-
}
|
|
451
|
+
const messageId = await sendProactiveActivity({
|
|
452
|
+
adapter,
|
|
453
|
+
appId,
|
|
454
|
+
ref,
|
|
455
|
+
activity,
|
|
456
|
+
errorPrefix: "msteams poll send",
|
|
457
|
+
});
|
|
433
458
|
|
|
434
459
|
log.info("sent poll", { conversationId, pollId: pollCard.pollId, messageId });
|
|
435
460
|
|
|
@@ -469,27 +494,13 @@ export async function sendAdaptiveCardMSTeams(
|
|
|
469
494
|
};
|
|
470
495
|
|
|
471
496
|
// Send card via proactive conversation
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
try {
|
|
480
|
-
await adapter.continueConversation(appId, proactiveRef, async (ctx) => {
|
|
481
|
-
const response = await ctx.sendActivity(activity);
|
|
482
|
-
messageId = extractMessageId(response) ?? "unknown";
|
|
483
|
-
});
|
|
484
|
-
} catch (err) {
|
|
485
|
-
const classification = classifyMSTeamsSendError(err);
|
|
486
|
-
const hint = formatMSTeamsSendErrorHint(classification);
|
|
487
|
-
const status = classification.statusCode ? ` (HTTP ${classification.statusCode})` : "";
|
|
488
|
-
throw new Error(
|
|
489
|
-
`msteams card send failed${status}: ${formatUnknownError(err)}${hint ? ` (${hint})` : ""}`,
|
|
490
|
-
{ cause: err },
|
|
491
|
-
);
|
|
492
|
-
}
|
|
497
|
+
const messageId = await sendProactiveActivity({
|
|
498
|
+
adapter,
|
|
499
|
+
appId,
|
|
500
|
+
ref,
|
|
501
|
+
activity,
|
|
502
|
+
errorPrefix: "msteams card send",
|
|
503
|
+
});
|
|
493
504
|
|
|
494
505
|
log.info("sent adaptive card", { conversationId, messageId });
|
|
495
506
|
|
package/src/store-fs.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
1
|
import fs from "node:fs";
|
|
3
|
-
import
|
|
4
|
-
import { safeParseJson } from "openclaw/plugin-sdk";
|
|
2
|
+
import { readJsonFileWithFallback, writeJsonFileAtomically } from "openclaw/plugin-sdk";
|
|
5
3
|
import { withFileLock as withPathLock } from "./file-lock.js";
|
|
6
4
|
|
|
7
5
|
const STORE_LOCK_OPTIONS = {
|
|
@@ -19,31 +17,11 @@ export async function readJsonFile<T>(
|
|
|
19
17
|
filePath: string,
|
|
20
18
|
fallback: T,
|
|
21
19
|
): Promise<{ value: T; exists: boolean }> {
|
|
22
|
-
|
|
23
|
-
const raw = await fs.promises.readFile(filePath, "utf-8");
|
|
24
|
-
const parsed = safeParseJson<T>(raw);
|
|
25
|
-
if (parsed == null) {
|
|
26
|
-
return { value: fallback, exists: true };
|
|
27
|
-
}
|
|
28
|
-
return { value: parsed, exists: true };
|
|
29
|
-
} catch (err) {
|
|
30
|
-
const code = (err as { code?: string }).code;
|
|
31
|
-
if (code === "ENOENT") {
|
|
32
|
-
return { value: fallback, exists: false };
|
|
33
|
-
}
|
|
34
|
-
return { value: fallback, exists: false };
|
|
35
|
-
}
|
|
20
|
+
return await readJsonFileWithFallback(filePath, fallback);
|
|
36
21
|
}
|
|
37
22
|
|
|
38
23
|
export async function writeJsonFile(filePath: string, value: unknown): Promise<void> {
|
|
39
|
-
|
|
40
|
-
await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
41
|
-
const tmp = path.join(dir, `${path.basename(filePath)}.${crypto.randomUUID()}.tmp`);
|
|
42
|
-
await fs.promises.writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, {
|
|
43
|
-
encoding: "utf-8",
|
|
44
|
-
});
|
|
45
|
-
await fs.promises.chmod(tmp, 0o600);
|
|
46
|
-
await fs.promises.rename(tmp, filePath);
|
|
24
|
+
await writeJsonFileAtomically(filePath, value);
|
|
47
25
|
}
|
|
48
26
|
|
|
49
27
|
async function ensureJsonFile(filePath: string, fallback: unknown) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
4
|
+
|
|
5
|
+
export const msteamsRuntimeStub = {
|
|
6
|
+
state: {
|
|
7
|
+
resolveStateDir: (env: NodeJS.ProcessEnv = process.env, homedir?: () => string) => {
|
|
8
|
+
const override = env.OPENCLAW_STATE_DIR?.trim() || env.OPENCLAW_STATE_DIR?.trim();
|
|
9
|
+
if (override) {
|
|
10
|
+
return override;
|
|
11
|
+
}
|
|
12
|
+
const resolvedHome = homedir ? homedir() : os.homedir();
|
|
13
|
+
return path.join(resolvedHome, ".openclaw");
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
} as unknown as PluginRuntime;
|