@openclaw/msteams 2026.3.2 → 2026.3.7
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/index.ts +2 -2
- package/package.json +1 -1
- package/src/attachments/graph.ts +1 -1
- package/src/attachments/payload.ts +1 -1
- package/src/attachments/remote-media.ts +1 -1
- package/src/attachments/shared.ts +2 -2
- package/src/attachments.test.ts +1 -1
- package/src/channel.directory.test.ts +1 -1
- package/src/channel.ts +95 -101
- package/src/directory-live.ts +1 -1
- package/src/file-lock.ts +1 -1
- package/src/graph.ts +1 -1
- package/src/media-helpers.ts +1 -1
- package/src/messenger.test.ts +16 -19
- package/src/messenger.ts +1 -1
- package/src/monitor-handler/message-handler.authz.test.ts +1 -1
- package/src/monitor-handler/message-handler.ts +59 -53
- package/src/monitor-handler.file-consent.test.ts +1 -1
- package/src/monitor-handler.ts +1 -1
- package/src/monitor.lifecycle.test.ts +9 -3
- package/src/monitor.ts +1 -1
- package/src/onboarding.ts +23 -47
- package/src/outbound.test.ts +131 -0
- package/src/outbound.ts +1 -1
- package/src/policy.test.ts +1 -1
- package/src/policy.ts +9 -10
- package/src/probe.test.ts +1 -1
- package/src/probe.ts +6 -2
- package/src/reply-dispatcher.ts +1 -1
- package/src/resolve-allowlist.test.ts +78 -0
- package/src/resolve-allowlist.ts +70 -79
- package/src/runtime.ts +1 -1
- package/src/secret-input.ts +1 -1
- package/src/send-context.ts +1 -1
- package/src/send.test.ts +2 -2
- package/src/send.ts +42 -43
- package/src/store-fs.ts +1 -1
- package/src/test-runtime.ts +1 -1
- package/src/token.test.ts +1 -1
- package/src/token.ts +1 -1
package/CHANGELOG.md
CHANGED
package/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
-
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/msteams";
|
|
2
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/msteams";
|
|
3
3
|
import { msteamsPlugin } from "./src/channel.js";
|
|
4
4
|
import { setMSTeamsRuntime } from "./src/runtime.js";
|
|
5
5
|
|
package/package.json
CHANGED
package/src/attachments/graph.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fetchWithSsrFGuard, type SsrFPolicy } from "openclaw/plugin-sdk";
|
|
1
|
+
import { fetchWithSsrFGuard, type SsrFPolicy } from "openclaw/plugin-sdk/msteams";
|
|
2
2
|
import { getMSTeamsRuntime } from "../runtime.js";
|
|
3
3
|
import { downloadMSTeamsAttachments } from "./download.js";
|
|
4
4
|
import { downloadAndStoreMSTeamsRemoteMedia } from "./remote-media.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SsrFPolicy } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { SsrFPolicy } from "openclaw/plugin-sdk/msteams";
|
|
2
2
|
import { getMSTeamsRuntime } from "../runtime.js";
|
|
3
3
|
import { inferPlaceholder } from "./shared.js";
|
|
4
4
|
import type { MSTeamsInboundMedia } from "./types.js";
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
isHttpsUrlAllowedByHostnameSuffixAllowlist,
|
|
5
5
|
isPrivateIpAddress,
|
|
6
6
|
normalizeHostnameSuffixAllowlist,
|
|
7
|
-
} from "openclaw/plugin-sdk";
|
|
8
|
-
import type { SsrFPolicy } from "openclaw/plugin-sdk";
|
|
7
|
+
} from "openclaw/plugin-sdk/msteams";
|
|
8
|
+
import type { SsrFPolicy } from "openclaw/plugin-sdk/msteams";
|
|
9
9
|
import type { MSTeamsAttachmentLike } from "./types.js";
|
|
10
10
|
|
|
11
11
|
type InlineImageCandidate =
|
package/src/attachments.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PluginRuntime, SsrFPolicy } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { PluginRuntime, SsrFPolicy } from "openclaw/plugin-sdk/msteams";
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
|
|
4
4
|
import {
|
package/src/channel.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import type { ChannelMessageActionName, ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
1
|
import {
|
|
3
|
-
|
|
2
|
+
collectAllowlistProviderRestrictSendersWarnings,
|
|
3
|
+
formatAllowFromLowercase,
|
|
4
|
+
} from "openclaw/plugin-sdk/compat";
|
|
5
|
+
import type {
|
|
6
|
+
ChannelMessageActionName,
|
|
7
|
+
ChannelPlugin,
|
|
8
|
+
OpenClawConfig,
|
|
9
|
+
} from "openclaw/plugin-sdk/msteams";
|
|
10
|
+
import {
|
|
11
|
+
buildProbeChannelStatusSummary,
|
|
12
|
+
buildRuntimeAccountStatusSnapshot,
|
|
4
13
|
buildChannelConfigSchema,
|
|
5
14
|
createDefaultChannelRuntimeState,
|
|
6
15
|
DEFAULT_ACCOUNT_ID,
|
|
7
16
|
MSTeamsConfigSchema,
|
|
8
17
|
PAIRING_APPROVED_MESSAGE,
|
|
9
|
-
|
|
10
|
-
resolveDefaultGroupPolicy,
|
|
11
|
-
} from "openclaw/plugin-sdk";
|
|
18
|
+
} from "openclaw/plugin-sdk/msteams";
|
|
12
19
|
import { listMSTeamsDirectoryGroupsLive, listMSTeamsDirectoryPeersLive } from "./directory-live.js";
|
|
13
20
|
import { msteamsOnboardingAdapter } from "./onboarding.js";
|
|
14
21
|
import { msteamsOutbound } from "./outbound.js";
|
|
@@ -120,27 +127,20 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
|
|
|
120
127
|
configured: account.configured,
|
|
121
128
|
}),
|
|
122
129
|
resolveAllowFrom: ({ cfg }) => cfg.channels?.msteams?.allowFrom ?? [],
|
|
123
|
-
formatAllowFrom: ({ allowFrom }) =>
|
|
124
|
-
allowFrom
|
|
125
|
-
.map((entry) => String(entry).trim())
|
|
126
|
-
.filter(Boolean)
|
|
127
|
-
.map((entry) => entry.toLowerCase()),
|
|
130
|
+
formatAllowFrom: ({ allowFrom }) => formatAllowFromLowercase({ allowFrom }),
|
|
128
131
|
resolveDefaultTo: ({ cfg }) => cfg.channels?.msteams?.defaultTo?.trim() || undefined,
|
|
129
132
|
},
|
|
130
133
|
security: {
|
|
131
134
|
collectWarnings: ({ cfg }) => {
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
return collectAllowlistProviderRestrictSendersWarnings({
|
|
136
|
+
cfg,
|
|
134
137
|
providerConfigPresent: cfg.channels?.msteams !== undefined,
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
configuredGroupPolicy: cfg.channels?.msteams?.groupPolicy,
|
|
139
|
+
surface: "MS Teams groups",
|
|
140
|
+
openScope: "any member",
|
|
141
|
+
groupPolicyPath: "channels.msteams.groupPolicy",
|
|
142
|
+
groupAllowFromPath: "channels.msteams.groupAllowFrom",
|
|
137
143
|
});
|
|
138
|
-
if (groupPolicy !== "open") {
|
|
139
|
-
return [];
|
|
140
|
-
}
|
|
141
|
-
return [
|
|
142
|
-
`- MS Teams groups: groupPolicy="open" allows any member to trigger (mention-gated). Set channels.msteams.groupPolicy="allowlist" + channels.msteams.groupAllowFrom to restrict senders.`,
|
|
143
|
-
];
|
|
144
144
|
},
|
|
145
145
|
},
|
|
146
146
|
setup: {
|
|
@@ -246,11 +246,43 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
|
|
|
246
246
|
name: undefined as string | undefined,
|
|
247
247
|
note: undefined as string | undefined,
|
|
248
248
|
}));
|
|
249
|
+
type ResolveTargetResultEntry = (typeof results)[number];
|
|
250
|
+
type PendingTargetEntry = { input: string; query: string; index: number };
|
|
249
251
|
|
|
250
252
|
const stripPrefix = (value: string) => normalizeMSTeamsUserInput(value);
|
|
253
|
+
const markPendingLookupFailed = (pending: PendingTargetEntry[]) => {
|
|
254
|
+
pending.forEach(({ index }) => {
|
|
255
|
+
const entry = results[index];
|
|
256
|
+
if (entry) {
|
|
257
|
+
entry.note = "lookup failed";
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
const resolvePending = async <T>(
|
|
262
|
+
pending: PendingTargetEntry[],
|
|
263
|
+
resolveEntries: (entries: string[]) => Promise<T[]>,
|
|
264
|
+
applyResolvedEntry: (target: ResolveTargetResultEntry, entry: T) => void,
|
|
265
|
+
) => {
|
|
266
|
+
if (pending.length === 0) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
const resolved = await resolveEntries(pending.map((entry) => entry.query));
|
|
271
|
+
resolved.forEach((entry, idx) => {
|
|
272
|
+
const target = results[pending[idx]?.index ?? -1];
|
|
273
|
+
if (!target) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
applyResolvedEntry(target, entry);
|
|
277
|
+
});
|
|
278
|
+
} catch (err) {
|
|
279
|
+
runtime.error?.(`msteams resolve failed: ${String(err)}`);
|
|
280
|
+
markPendingLookupFailed(pending);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
251
283
|
|
|
252
284
|
if (kind === "user") {
|
|
253
|
-
const pending:
|
|
285
|
+
const pending: PendingTargetEntry[] = [];
|
|
254
286
|
results.forEach((entry, index) => {
|
|
255
287
|
const trimmed = entry.input.trim();
|
|
256
288
|
if (!trimmed) {
|
|
@@ -266,37 +298,21 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
|
|
|
266
298
|
pending.push({ input: entry.input, query: cleaned, index });
|
|
267
299
|
});
|
|
268
300
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
target.resolved = entry.resolved;
|
|
281
|
-
target.id = entry.id;
|
|
282
|
-
target.name = entry.name;
|
|
283
|
-
target.note = entry.note;
|
|
284
|
-
});
|
|
285
|
-
} catch (err) {
|
|
286
|
-
runtime.error?.(`msteams resolve failed: ${String(err)}`);
|
|
287
|
-
pending.forEach(({ index }) => {
|
|
288
|
-
const entry = results[index];
|
|
289
|
-
if (entry) {
|
|
290
|
-
entry.note = "lookup failed";
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
}
|
|
301
|
+
await resolvePending(
|
|
302
|
+
pending,
|
|
303
|
+
(entries) => resolveMSTeamsUserAllowlist({ cfg, entries }),
|
|
304
|
+
(target, entry) => {
|
|
305
|
+
target.resolved = entry.resolved;
|
|
306
|
+
target.id = entry.id;
|
|
307
|
+
target.name = entry.name;
|
|
308
|
+
target.note = entry.note;
|
|
309
|
+
},
|
|
310
|
+
);
|
|
295
311
|
|
|
296
312
|
return results;
|
|
297
313
|
}
|
|
298
314
|
|
|
299
|
-
const pending:
|
|
315
|
+
const pending: PendingTargetEntry[] = [];
|
|
300
316
|
results.forEach((entry, index) => {
|
|
301
317
|
const trimmed = entry.input.trim();
|
|
302
318
|
if (!trimmed) {
|
|
@@ -319,48 +335,32 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
|
|
|
319
335
|
pending.push({ input: entry.input, query, index });
|
|
320
336
|
});
|
|
321
337
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
target.note = "team id";
|
|
349
|
-
}
|
|
350
|
-
if (entry.note) {
|
|
351
|
-
target.note = entry.note;
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
} catch (err) {
|
|
355
|
-
runtime.error?.(`msteams resolve failed: ${String(err)}`);
|
|
356
|
-
pending.forEach(({ index }) => {
|
|
357
|
-
const entry = results[index];
|
|
358
|
-
if (entry) {
|
|
359
|
-
entry.note = "lookup failed";
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
}
|
|
338
|
+
await resolvePending(
|
|
339
|
+
pending,
|
|
340
|
+
(entries) => resolveMSTeamsChannelAllowlist({ cfg, entries }),
|
|
341
|
+
(target, entry) => {
|
|
342
|
+
if (!entry.resolved || !entry.teamId) {
|
|
343
|
+
target.resolved = false;
|
|
344
|
+
target.note = entry.note;
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
target.resolved = true;
|
|
348
|
+
if (entry.channelId) {
|
|
349
|
+
target.id = `${entry.teamId}/${entry.channelId}`;
|
|
350
|
+
target.name =
|
|
351
|
+
entry.channelName && entry.teamName
|
|
352
|
+
? `${entry.teamName}/${entry.channelName}`
|
|
353
|
+
: (entry.channelName ?? entry.teamName);
|
|
354
|
+
} else {
|
|
355
|
+
target.id = entry.teamId;
|
|
356
|
+
target.name = entry.teamName;
|
|
357
|
+
target.note = "team id";
|
|
358
|
+
}
|
|
359
|
+
if (entry.note) {
|
|
360
|
+
target.note = entry.note;
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
);
|
|
364
364
|
|
|
365
365
|
return results;
|
|
366
366
|
},
|
|
@@ -425,23 +425,17 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
|
|
|
425
425
|
outbound: msteamsOutbound,
|
|
426
426
|
status: {
|
|
427
427
|
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID, { port: null }),
|
|
428
|
-
buildChannelSummary: ({ snapshot }) =>
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
lastProbeAt: snapshot.lastProbeAt ?? null,
|
|
433
|
-
}),
|
|
428
|
+
buildChannelSummary: ({ snapshot }) =>
|
|
429
|
+
buildProbeChannelStatusSummary(snapshot, {
|
|
430
|
+
port: snapshot.port ?? null,
|
|
431
|
+
}),
|
|
434
432
|
probeAccount: async ({ cfg }) => await probeMSTeams(cfg.channels?.msteams),
|
|
435
433
|
buildAccountSnapshot: ({ account, runtime, probe }) => ({
|
|
436
434
|
accountId: account.accountId,
|
|
437
435
|
enabled: account.enabled,
|
|
438
436
|
configured: account.configured,
|
|
439
|
-
|
|
440
|
-
lastStartAt: runtime?.lastStartAt ?? null,
|
|
441
|
-
lastStopAt: runtime?.lastStopAt ?? null,
|
|
442
|
-
lastError: runtime?.lastError ?? null,
|
|
437
|
+
...buildRuntimeAccountStatusSnapshot({ runtime, probe }),
|
|
443
438
|
port: runtime?.port ?? null,
|
|
444
|
-
probe,
|
|
445
439
|
}),
|
|
446
440
|
},
|
|
447
441
|
gateway: {
|
package/src/directory-live.ts
CHANGED
package/src/file-lock.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { withFileLock } from "openclaw/plugin-sdk";
|
|
1
|
+
export { withFileLock } from "openclaw/plugin-sdk/msteams";
|
package/src/graph.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MSTeamsConfig } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { MSTeamsConfig } from "openclaw/plugin-sdk/msteams";
|
|
2
2
|
import { GRAPH_ROOT } from "./attachments/shared.js";
|
|
3
3
|
import { loadMSTeamsSdkWithAuth } from "./sdk.js";
|
|
4
4
|
import { readAccessToken } from "./token-response.js";
|
package/src/media-helpers.ts
CHANGED
package/src/messenger.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { SILENT_REPLY_TOKEN, type PluginRuntime } from "openclaw/plugin-sdk";
|
|
4
|
+
import { SILENT_REPLY_TOKEN, type PluginRuntime } from "openclaw/plugin-sdk/msteams";
|
|
5
5
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
6
|
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
|
|
7
7
|
import type { StoredConversationReference } from "./conversation-store.js";
|
|
@@ -72,6 +72,17 @@ const createRecordedSendActivity = (
|
|
|
72
72
|
};
|
|
73
73
|
};
|
|
74
74
|
|
|
75
|
+
const REVOCATION_ERROR = "Cannot perform 'set' on a proxy that has been revoked";
|
|
76
|
+
|
|
77
|
+
const createFallbackAdapter = (proactiveSent: string[]): MSTeamsAdapter => ({
|
|
78
|
+
continueConversation: async (_appId, _reference, logic) => {
|
|
79
|
+
await logic({
|
|
80
|
+
sendActivity: createRecordedSendActivity(proactiveSent),
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
process: async () => {},
|
|
84
|
+
});
|
|
85
|
+
|
|
75
86
|
describe("msteams messenger", () => {
|
|
76
87
|
beforeEach(() => {
|
|
77
88
|
setMSTeamsRuntime(runtimeStub);
|
|
@@ -297,18 +308,11 @@ describe("msteams messenger", () => {
|
|
|
297
308
|
|
|
298
309
|
const ctx = {
|
|
299
310
|
sendActivity: async () => {
|
|
300
|
-
throw new TypeError(
|
|
311
|
+
throw new TypeError(REVOCATION_ERROR);
|
|
301
312
|
},
|
|
302
313
|
};
|
|
303
314
|
|
|
304
|
-
const adapter
|
|
305
|
-
continueConversation: async (_appId, _reference, logic) => {
|
|
306
|
-
await logic({
|
|
307
|
-
sendActivity: createRecordedSendActivity(proactiveSent),
|
|
308
|
-
});
|
|
309
|
-
},
|
|
310
|
-
process: async () => {},
|
|
311
|
-
};
|
|
315
|
+
const adapter = createFallbackAdapter(proactiveSent);
|
|
312
316
|
|
|
313
317
|
const ids = await sendMSTeamsMessages({
|
|
314
318
|
replyStyle: "thread",
|
|
@@ -338,18 +342,11 @@ describe("msteams messenger", () => {
|
|
|
338
342
|
threadSent.push(content);
|
|
339
343
|
return { id: `id:${content}` };
|
|
340
344
|
}
|
|
341
|
-
throw new TypeError(
|
|
345
|
+
throw new TypeError(REVOCATION_ERROR);
|
|
342
346
|
},
|
|
343
347
|
};
|
|
344
348
|
|
|
345
|
-
const adapter
|
|
346
|
-
continueConversation: async (_appId, _reference, logic) => {
|
|
347
|
-
await logic({
|
|
348
|
-
sendActivity: createRecordedSendActivity(proactiveSent),
|
|
349
|
-
});
|
|
350
|
-
},
|
|
351
|
-
process: async () => {},
|
|
352
|
-
};
|
|
349
|
+
const adapter = createFallbackAdapter(proactiveSent);
|
|
353
350
|
|
|
354
351
|
const ids = await sendMSTeamsMessages({
|
|
355
352
|
replyStyle: "thread",
|
package/src/messenger.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type ReplyPayload,
|
|
8
8
|
SILENT_REPLY_TOKEN,
|
|
9
9
|
sleep,
|
|
10
|
-
} from "openclaw/plugin-sdk";
|
|
10
|
+
} from "openclaw/plugin-sdk/msteams";
|
|
11
11
|
import type { MSTeamsAccessTokenProvider } from "./attachments/types.js";
|
|
12
12
|
import type { StoredConversationReference } from "./conversation-store.js";
|
|
13
13
|
import { classifyMSTeamsSendError } from "./errors.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpenClawConfig, PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { OpenClawConfig, PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/msteams";
|
|
2
2
|
import { describe, expect, it, vi } from "vitest";
|
|
3
3
|
import type { MSTeamsMessageHandlerDeps } from "../monitor-handler.js";
|
|
4
4
|
import { setMSTeamsRuntime } from "../runtime.js";
|
|
@@ -2,20 +2,24 @@ import {
|
|
|
2
2
|
DEFAULT_ACCOUNT_ID,
|
|
3
3
|
buildPendingHistoryContextFromMap,
|
|
4
4
|
clearHistoryEntriesIfEnabled,
|
|
5
|
+
dispatchReplyFromConfigWithSettledDispatcher,
|
|
5
6
|
DEFAULT_GROUP_HISTORY_LIMIT,
|
|
6
7
|
createScopedPairingAccess,
|
|
7
8
|
logInboundDrop,
|
|
9
|
+
evaluateSenderGroupAccessForPolicy,
|
|
10
|
+
resolveSenderScopedGroupPolicy,
|
|
8
11
|
recordPendingHistoryEntryIfEnabled,
|
|
9
12
|
resolveControlCommandGate,
|
|
10
13
|
resolveDefaultGroupPolicy,
|
|
11
14
|
isDangerousNameMatchingEnabled,
|
|
12
15
|
readStoreAllowFromForDmPolicy,
|
|
13
16
|
resolveMentionGating,
|
|
17
|
+
resolveInboundSessionEnvelopeContext,
|
|
14
18
|
formatAllowlistMatchMeta,
|
|
15
19
|
resolveEffectiveAllowFromLists,
|
|
16
20
|
resolveDmGroupAccessWithLists,
|
|
17
21
|
type HistoryEntry,
|
|
18
|
-
} from "openclaw/plugin-sdk";
|
|
22
|
+
} from "openclaw/plugin-sdk/msteams";
|
|
19
23
|
import {
|
|
20
24
|
buildMSTeamsAttachmentPlaceholder,
|
|
21
25
|
buildMSTeamsMediaPayload,
|
|
@@ -172,12 +176,10 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
|
|
172
176
|
conversationId,
|
|
173
177
|
channelName,
|
|
174
178
|
});
|
|
175
|
-
const senderGroupPolicy =
|
|
176
|
-
groupPolicy
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
? "allowlist"
|
|
180
|
-
: "open";
|
|
179
|
+
const senderGroupPolicy = resolveSenderScopedGroupPolicy({
|
|
180
|
+
groupPolicy,
|
|
181
|
+
groupAllowFrom: effectiveGroupAllowFrom,
|
|
182
|
+
});
|
|
181
183
|
const access = resolveDmGroupAccessWithLists({
|
|
182
184
|
isGroup: !isDirectMessage,
|
|
183
185
|
dmPolicy,
|
|
@@ -228,46 +230,57 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
if (!isDirectMessage && msteamsCfg) {
|
|
231
|
-
if (
|
|
232
|
-
log.debug?.("dropping group message (
|
|
233
|
+
if (channelGate.allowlistConfigured && !channelGate.allowed) {
|
|
234
|
+
log.debug?.("dropping group message (not in team/channel allowlist)", {
|
|
233
235
|
conversationId,
|
|
236
|
+
teamKey: channelGate.teamKey ?? "none",
|
|
237
|
+
channelKey: channelGate.channelKey ?? "none",
|
|
238
|
+
channelMatchKey: channelGate.channelMatchKey ?? "none",
|
|
239
|
+
channelMatchSource: channelGate.channelMatchSource ?? "none",
|
|
234
240
|
});
|
|
235
241
|
return;
|
|
236
242
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
if (effectiveGroupAllowFrom.length === 0 && !channelGate.allowlistConfigured) {
|
|
250
|
-
log.debug?.("dropping group message (groupPolicy: allowlist, no allowlist)", {
|
|
251
|
-
conversationId,
|
|
252
|
-
});
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
if (effectiveGroupAllowFrom.length > 0 && access.decision !== "allow") {
|
|
256
|
-
const allowMatch = resolveMSTeamsAllowlistMatch({
|
|
257
|
-
allowFrom: effectiveGroupAllowFrom,
|
|
243
|
+
const senderGroupAccess = evaluateSenderGroupAccessForPolicy({
|
|
244
|
+
groupPolicy,
|
|
245
|
+
groupAllowFrom:
|
|
246
|
+
effectiveGroupAllowFrom.length > 0 || !channelGate.allowlistConfigured
|
|
247
|
+
? effectiveGroupAllowFrom
|
|
248
|
+
: ["*"],
|
|
249
|
+
senderId,
|
|
250
|
+
isSenderAllowed: (_senderId, allowFrom) =>
|
|
251
|
+
resolveMSTeamsAllowlistMatch({
|
|
252
|
+
allowFrom,
|
|
258
253
|
senderId,
|
|
259
254
|
senderName,
|
|
260
255
|
allowNameMatching: isDangerousNameMatchingEnabled(msteamsCfg),
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
256
|
+
}).allowed,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (!senderGroupAccess.allowed && senderGroupAccess.reason === "disabled") {
|
|
260
|
+
log.debug?.("dropping group message (groupPolicy: disabled)", {
|
|
261
|
+
conversationId,
|
|
262
|
+
});
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (!senderGroupAccess.allowed && senderGroupAccess.reason === "empty_allowlist") {
|
|
266
|
+
log.debug?.("dropping group message (groupPolicy: allowlist, no allowlist)", {
|
|
267
|
+
conversationId,
|
|
268
|
+
});
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (!senderGroupAccess.allowed && senderGroupAccess.reason === "sender_not_allowlisted") {
|
|
272
|
+
const allowMatch = resolveMSTeamsAllowlistMatch({
|
|
273
|
+
allowFrom: effectiveGroupAllowFrom,
|
|
274
|
+
senderId,
|
|
275
|
+
senderName,
|
|
276
|
+
allowNameMatching: isDangerousNameMatchingEnabled(msteamsCfg),
|
|
277
|
+
});
|
|
278
|
+
log.debug?.("dropping group message (not in groupAllowFrom)", {
|
|
279
|
+
sender: senderId,
|
|
280
|
+
label: senderName,
|
|
281
|
+
allowlistMatch: formatAllowlistMatchMeta(allowMatch),
|
|
282
|
+
});
|
|
283
|
+
return;
|
|
271
284
|
}
|
|
272
285
|
}
|
|
273
286
|
|
|
@@ -451,12 +464,9 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
|
|
451
464
|
|
|
452
465
|
const mediaPayload = buildMSTeamsMediaPayload(mediaList);
|
|
453
466
|
const envelopeFrom = isDirectMessage ? senderName : conversationType;
|
|
454
|
-
const storePath =
|
|
467
|
+
const { storePath, envelopeOptions, previousTimestamp } = resolveInboundSessionEnvelopeContext({
|
|
468
|
+
cfg,
|
|
455
469
|
agentId: route.agentId,
|
|
456
|
-
});
|
|
457
|
-
const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg);
|
|
458
|
-
const previousTimestamp = core.channel.session.readSessionUpdatedAt({
|
|
459
|
-
storePath,
|
|
460
470
|
sessionKey: route.sessionKey,
|
|
461
471
|
});
|
|
462
472
|
const body = core.channel.reply.formatAgentEnvelope({
|
|
@@ -559,18 +569,14 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
|
|
559
569
|
|
|
560
570
|
log.info("dispatching to agent", { sessionKey: route.sessionKey });
|
|
561
571
|
try {
|
|
562
|
-
const { queuedFinal, counts } = await
|
|
572
|
+
const { queuedFinal, counts } = await dispatchReplyFromConfigWithSettledDispatcher({
|
|
573
|
+
cfg,
|
|
574
|
+
ctxPayload,
|
|
563
575
|
dispatcher,
|
|
564
576
|
onSettled: () => {
|
|
565
577
|
markDispatchIdle();
|
|
566
578
|
},
|
|
567
|
-
|
|
568
|
-
core.channel.reply.dispatchReplyFromConfig({
|
|
569
|
-
ctx: ctxPayload,
|
|
570
|
-
cfg,
|
|
571
|
-
dispatcher,
|
|
572
|
-
replyOptions,
|
|
573
|
-
}),
|
|
579
|
+
replyOptions,
|
|
574
580
|
});
|
|
575
581
|
|
|
576
582
|
log.info("dispatch complete", { queuedFinal, counts });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpenClawConfig, PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { OpenClawConfig, PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/msteams";
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import type { MSTeamsConversationStore } from "./conversation-store.js";
|
|
4
4
|
import type { MSTeamsAdapter } from "./messenger.js";
|
package/src/monitor-handler.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk/msteams";
|
|
2
2
|
import type { MSTeamsConversationStore } from "./conversation-store.js";
|
|
3
3
|
import { buildFileInfoCard, parseFileConsentInvoke, uploadToConsentUrl } from "./file-consent.js";
|
|
4
4
|
import { normalizeMSTeamsConversationId } from "./inbound.js";
|