mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 17:50:45 +00:00
fix(msteams): preserve proactive thread replies (#78387)
Co-authored-by: Alex Knight <15041791+amknight@users.noreply.github.com>
This commit is contained in:
@@ -117,6 +117,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- PDF/Codex: include extraction-fallback instructions for `openai-codex/*` PDF tool requests so Codex Responses receives its required system prompt. Fixes #77872. Thanks @anyech.
|
- PDF/Codex: include extraction-fallback instructions for `openai-codex/*` PDF tool requests so Codex Responses receives its required system prompt. Fixes #77872. Thanks @anyech.
|
||||||
- Onboard/channels: recover externalized channel plugins from stale `channels.<id>` config by falling back to `ensureChannelSetupPluginInstalled` via the trusted catalog when the plugin is missing on disk, so leftover `appId`/token entries no longer dead-end onboard with "<channel> plugin not available." (#78328) Thanks @sliverp.
|
- Onboard/channels: recover externalized channel plugins from stale `channels.<id>` config by falling back to `ensureChannelSetupPluginInstalled` via the trusted catalog when the plugin is missing on disk, so leftover `appId`/token entries no longer dead-end onboard with "<channel> plugin not available." (#78328) Thanks @sliverp.
|
||||||
- Codex/app-server: forward the OpenClaw workspace bootstrap block through Codex `developerInstructions` instead of `config.instructions`, so persona/style guidance reaches the behavior-shaping app-server lane. Fixes #77363. Thanks @lonexreb.
|
- Codex/app-server: forward the OpenClaw workspace bootstrap block through Codex `developerInstructions` instead of `config.instructions`, so persona/style guidance reaches the behavior-shaping app-server lane. Fixes #77363. Thanks @lonexreb.
|
||||||
|
- MS Teams: route proactive channel sends with stored thread roots through the configured threaded reply path instead of forcing every CLI/message-tool send into a new top-level post. Fixes #78298. Thanks @amknight.
|
||||||
- CLI/infer: pass minimal instructions to local `openai-codex/*` model probes and surface provider error details when `infer model run` returns no text. Fixes #76464. Thanks @lilesjtu.
|
- CLI/infer: pass minimal instructions to local `openai-codex/*` model probes and surface provider error details when `infer model run` returns no text. Fixes #76464. Thanks @lilesjtu.
|
||||||
- Dependencies: override transitive `ip-address` to `10.2.0` so the runtime lockfile no longer includes the vulnerable `10.1.0` build flagged by Dependabot alert 109. Thanks @vincentkoc.
|
- Dependencies: override transitive `ip-address` to `10.2.0` so the runtime lockfile no longer includes the vulnerable `10.1.0` build flagged by Dependabot alert 109. Thanks @vincentkoc.
|
||||||
- Plugins/install: apply OpenClaw's npm security overrides inside managed external plugin npm roots so hoisted plugin dependencies inherit the host package hardening. Thanks @vincentkoc.
|
- Plugins/install: apply OpenClaw's npm security overrides inside managed external plugin npm roots so hoisted plugin dependencies inherit the host package hardening. Thanks @vincentkoc.
|
||||||
|
|||||||
@@ -574,6 +574,94 @@ describe("msteams messenger", () => {
|
|||||||
expect(reference.conversation?.id).toBe("19:abc@thread.tacv2;messageid=legacy-activity-id");
|
expect(reference.conversation?.id).toBe("19:abc@thread.tacv2;messageid=legacy-activity-id");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("sends no-context thread replies proactively with the channel thread root", async () => {
|
||||||
|
let capturedReference: unknown;
|
||||||
|
const sent: string[] = [];
|
||||||
|
const channelRef: StoredConversationReference = {
|
||||||
|
activityId: "current-msg",
|
||||||
|
user: { id: "user123", name: "User" },
|
||||||
|
agent: { id: "bot123", name: "Bot" },
|
||||||
|
conversation: {
|
||||||
|
id: "19:abc@thread.tacv2",
|
||||||
|
conversationType: "channel",
|
||||||
|
},
|
||||||
|
channelId: "msteams",
|
||||||
|
serviceUrl: "https://service.example.com",
|
||||||
|
threadId: "thread-root-msg-id",
|
||||||
|
};
|
||||||
|
|
||||||
|
const adapter: MSTeamsAdapter = {
|
||||||
|
continueConversation: async (_appId, reference, logic) => {
|
||||||
|
capturedReference = reference;
|
||||||
|
await logic({
|
||||||
|
sendActivity: createRecordedSendActivity(sent),
|
||||||
|
updateActivity: noopUpdateActivity,
|
||||||
|
deleteActivity: noopDeleteActivity,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
process: async () => {},
|
||||||
|
updateActivity: noopUpdateActivity,
|
||||||
|
deleteActivity: noopDeleteActivity,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ids = await sendMSTeamsMessages({
|
||||||
|
replyStyle: "thread",
|
||||||
|
adapter,
|
||||||
|
appId: "app123",
|
||||||
|
conversationRef: channelRef,
|
||||||
|
messages: [{ text: "hello" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sent).toEqual(["hello"]);
|
||||||
|
expect(ids).toEqual(["id:hello"]);
|
||||||
|
const ref = capturedReference as { conversation?: { id?: string }; activityId?: string };
|
||||||
|
expect(ref.conversation?.id).toBe("19:abc@thread.tacv2;messageid=thread-root-msg-id");
|
||||||
|
expect(ref.activityId).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses activityId for no-context thread replies when threadId is absent", async () => {
|
||||||
|
let capturedReference: unknown;
|
||||||
|
const sent: string[] = [];
|
||||||
|
const channelRef: StoredConversationReference = {
|
||||||
|
activityId: "legacy-activity-id",
|
||||||
|
user: { id: "user123", name: "User" },
|
||||||
|
agent: { id: "bot123", name: "Bot" },
|
||||||
|
conversation: {
|
||||||
|
id: "19:abc@thread.tacv2",
|
||||||
|
conversationType: "channel",
|
||||||
|
},
|
||||||
|
channelId: "msteams",
|
||||||
|
serviceUrl: "https://service.example.com",
|
||||||
|
};
|
||||||
|
|
||||||
|
const adapter: MSTeamsAdapter = {
|
||||||
|
continueConversation: async (_appId, reference, logic) => {
|
||||||
|
capturedReference = reference;
|
||||||
|
await logic({
|
||||||
|
sendActivity: createRecordedSendActivity(sent),
|
||||||
|
updateActivity: noopUpdateActivity,
|
||||||
|
deleteActivity: noopDeleteActivity,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
process: async () => {},
|
||||||
|
updateActivity: noopUpdateActivity,
|
||||||
|
deleteActivity: noopDeleteActivity,
|
||||||
|
};
|
||||||
|
|
||||||
|
await sendMSTeamsMessages({
|
||||||
|
replyStyle: "thread",
|
||||||
|
adapter,
|
||||||
|
appId: "app123",
|
||||||
|
conversationRef: channelRef,
|
||||||
|
messages: [{ text: "hello" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const ref = capturedReference as { conversation?: { id?: string }; activityId?: string };
|
||||||
|
expect(sent).toEqual(["hello"]);
|
||||||
|
expect(ref.conversation?.id).toBe("19:abc@thread.tacv2;messageid=legacy-activity-id");
|
||||||
|
expect(ref.activityId).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
it("does not add thread suffix for top-level replyStyle even with threadId set", async () => {
|
it("does not add thread suffix for top-level replyStyle even with threadId set", async () => {
|
||||||
let capturedReference: unknown;
|
let capturedReference: unknown;
|
||||||
const sent: string[] = [];
|
const sent: string[] = [];
|
||||||
|
|||||||
@@ -572,7 +572,7 @@ export async function sendMSTeamsMessages(params: {
|
|||||||
if (params.replyStyle === "thread") {
|
if (params.replyStyle === "thread") {
|
||||||
const ctx = params.context;
|
const ctx = params.context;
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
throw new Error("Missing context for replyStyle=thread");
|
return await sendProactively(messages, 0, resolvedThreadId);
|
||||||
}
|
}
|
||||||
const messageIds: string[] = [];
|
const messageIds: string[] = [];
|
||||||
for (const [idx, message] of messages.entries()) {
|
for (const [idx, message] of messages.entries()) {
|
||||||
|
|||||||
93
extensions/msteams/src/send-context.test.ts
Normal file
93
extensions/msteams/src/send-context.test.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import type { MSTeamsConfig } from "../runtime-api.js";
|
||||||
|
import type { StoredConversationReference } from "./conversation-store.js";
|
||||||
|
import { resolveMSTeamsProactiveReplyStyle } from "./send-context.js";
|
||||||
|
|
||||||
|
function channelRef(params?: Partial<StoredConversationReference>): StoredConversationReference {
|
||||||
|
return {
|
||||||
|
user: { id: "user-1" },
|
||||||
|
agent: { id: "agent-1" },
|
||||||
|
conversation: { id: "19:channel@thread.tacv2", conversationType: "channel" },
|
||||||
|
channelId: "msteams",
|
||||||
|
teamId: "team-1",
|
||||||
|
...params,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("resolveMSTeamsProactiveReplyStyle", () => {
|
||||||
|
it("uses thread for channel conversations with a stored thread root", () => {
|
||||||
|
expect(
|
||||||
|
resolveMSTeamsProactiveReplyStyle({
|
||||||
|
cfg: {},
|
||||||
|
conversationId: "19:channel@thread.tacv2",
|
||||||
|
ref: channelRef({ threadId: "thread-root-1" }),
|
||||||
|
conversationType: "channel",
|
||||||
|
}),
|
||||||
|
).toBe("thread");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to activityId for legacy channel references", () => {
|
||||||
|
expect(
|
||||||
|
resolveMSTeamsProactiveReplyStyle({
|
||||||
|
cfg: {},
|
||||||
|
conversationId: "19:channel@thread.tacv2",
|
||||||
|
ref: channelRef({ activityId: "legacy-root-1" }),
|
||||||
|
conversationType: "channel",
|
||||||
|
}),
|
||||||
|
).toBe("thread");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps configured top-level channel routing", () => {
|
||||||
|
const cfg: MSTeamsConfig = {
|
||||||
|
replyStyle: "thread",
|
||||||
|
teams: {
|
||||||
|
"team-1": {
|
||||||
|
channels: {
|
||||||
|
"19:channel@thread.tacv2": { replyStyle: "top-level" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolveMSTeamsProactiveReplyStyle({
|
||||||
|
cfg,
|
||||||
|
conversationId: "19:channel@thread.tacv2",
|
||||||
|
ref: channelRef({ threadId: "thread-root-1" }),
|
||||||
|
conversationType: "channel",
|
||||||
|
}),
|
||||||
|
).toBe("top-level");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses top-level when a channel has no stored thread root", () => {
|
||||||
|
expect(
|
||||||
|
resolveMSTeamsProactiveReplyStyle({
|
||||||
|
cfg: { replyStyle: "thread" },
|
||||||
|
conversationId: "19:channel@thread.tacv2",
|
||||||
|
ref: channelRef(),
|
||||||
|
conversationType: "channel",
|
||||||
|
}),
|
||||||
|
).toBe("top-level");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses top-level for non-channel conversations", () => {
|
||||||
|
const ref = channelRef({ activityId: "activity-1" });
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolveMSTeamsProactiveReplyStyle({
|
||||||
|
cfg: { replyStyle: "thread" },
|
||||||
|
conversationId: "19:group@thread.v2",
|
||||||
|
ref,
|
||||||
|
conversationType: "groupChat",
|
||||||
|
}),
|
||||||
|
).toBe("top-level");
|
||||||
|
expect(
|
||||||
|
resolveMSTeamsProactiveReplyStyle({
|
||||||
|
cfg: { replyStyle: "thread" },
|
||||||
|
conversationId: "a:personal",
|
||||||
|
ref,
|
||||||
|
conversationType: "personal",
|
||||||
|
}),
|
||||||
|
).toBe("top-level");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||||
import {
|
import {
|
||||||
resolveChannelMediaMaxBytes,
|
resolveChannelMediaMaxBytes,
|
||||||
|
type MSTeamsConfig,
|
||||||
|
type MSTeamsReplyStyle,
|
||||||
type OpenClawConfig,
|
type OpenClawConfig,
|
||||||
type PluginRuntime,
|
type PluginRuntime,
|
||||||
} from "../runtime-api.js";
|
} from "../runtime-api.js";
|
||||||
@@ -13,6 +15,7 @@ import type {
|
|||||||
import { formatUnknownError } from "./errors.js";
|
import { formatUnknownError } from "./errors.js";
|
||||||
import { resolveGraphChatId } from "./graph-upload.js";
|
import { resolveGraphChatId } from "./graph-upload.js";
|
||||||
import type { MSTeamsAdapter } from "./messenger.js";
|
import type { MSTeamsAdapter } from "./messenger.js";
|
||||||
|
import { resolveMSTeamsReplyPolicy, resolveMSTeamsRouteConfig } from "./policy.js";
|
||||||
import { getMSTeamsRuntime } from "./runtime.js";
|
import { getMSTeamsRuntime } from "./runtime.js";
|
||||||
import { createMSTeamsAdapter, createMSTeamsTokenProvider, loadMSTeamsSdkWithAuth } from "./sdk.js";
|
import { createMSTeamsAdapter, createMSTeamsTokenProvider, loadMSTeamsSdkWithAuth } from "./sdk.js";
|
||||||
import { resolveMSTeamsCredentials } from "./token.js";
|
import { resolveMSTeamsCredentials } from "./token.js";
|
||||||
@@ -27,6 +30,8 @@ export type MSTeamsProactiveContext = {
|
|||||||
log: ReturnType<PluginRuntime["logging"]["getChildLogger"]>;
|
log: ReturnType<PluginRuntime["logging"]["getChildLogger"]>;
|
||||||
/** The type of conversation: personal (1:1), groupChat, or channel */
|
/** The type of conversation: personal (1:1), groupChat, or channel */
|
||||||
conversationType: MSTeamsConversationType;
|
conversationType: MSTeamsConversationType;
|
||||||
|
/** Reply style resolved for proactive text/media sends. */
|
||||||
|
replyStyle: MSTeamsReplyStyle;
|
||||||
/** Token provider for Graph API / OneDrive operations */
|
/** Token provider for Graph API / OneDrive operations */
|
||||||
tokenProvider: MSTeamsAccessTokenProvider;
|
tokenProvider: MSTeamsAccessTokenProvider;
|
||||||
/** SharePoint site ID for file uploads in group chats/channels */
|
/** SharePoint site ID for file uploads in group chats/channels */
|
||||||
@@ -42,6 +47,32 @@ export type MSTeamsProactiveContext = {
|
|||||||
graphChatId?: string | null;
|
graphChatId?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function resolveMSTeamsProactiveReplyStyle(params: {
|
||||||
|
cfg?: MSTeamsConfig;
|
||||||
|
conversationId: string;
|
||||||
|
ref: StoredConversationReference;
|
||||||
|
conversationType: MSTeamsConversationType;
|
||||||
|
}): MSTeamsReplyStyle {
|
||||||
|
const threadRootId = params.ref.threadId ?? params.ref.activityId;
|
||||||
|
if (params.conversationType !== "channel" || !threadRootId) {
|
||||||
|
return "top-level";
|
||||||
|
}
|
||||||
|
|
||||||
|
const routeConfig = resolveMSTeamsRouteConfig({
|
||||||
|
cfg: params.cfg,
|
||||||
|
teamId: params.ref.teamId,
|
||||||
|
conversationId: params.conversationId,
|
||||||
|
allowNameMatching: false,
|
||||||
|
});
|
||||||
|
const { replyStyle } = resolveMSTeamsReplyPolicy({
|
||||||
|
isDirectMessage: false,
|
||||||
|
globalConfig: params.cfg,
|
||||||
|
teamConfig: routeConfig.teamConfig,
|
||||||
|
channelConfig: routeConfig.channelConfig,
|
||||||
|
});
|
||||||
|
return replyStyle;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the target value into a conversation reference lookup key.
|
* Parse the target value into a conversation reference lookup key.
|
||||||
* Supported formats:
|
* Supported formats:
|
||||||
@@ -167,6 +198,12 @@ export async function resolveMSTeamsSendContext(params: {
|
|||||||
// groupChat, or unknown defaults to groupChat behavior
|
// groupChat, or unknown defaults to groupChat behavior
|
||||||
conversationType = "groupChat";
|
conversationType = "groupChat";
|
||||||
}
|
}
|
||||||
|
const replyStyle = resolveMSTeamsProactiveReplyStyle({
|
||||||
|
cfg: msteamsCfg,
|
||||||
|
conversationId,
|
||||||
|
ref,
|
||||||
|
conversationType,
|
||||||
|
});
|
||||||
|
|
||||||
// Get SharePoint site ID from config (required for file uploads in group chats/channels)
|
// Get SharePoint site ID from config (required for file uploads in group chats/channels)
|
||||||
const sharePointSiteId = msteamsCfg.sharePointSiteId;
|
const sharePointSiteId = msteamsCfg.sharePointSiteId;
|
||||||
@@ -223,6 +260,7 @@ export async function resolveMSTeamsSendContext(params: {
|
|||||||
adapter: adapter as unknown as MSTeamsAdapter,
|
adapter: adapter as unknown as MSTeamsAdapter,
|
||||||
log,
|
log,
|
||||||
conversationType,
|
conversationType,
|
||||||
|
replyStyle,
|
||||||
tokenProvider,
|
tokenProvider,
|
||||||
sharePointSiteId,
|
sharePointSiteId,
|
||||||
mediaMaxBytes,
|
mediaMaxBytes,
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ function createSharePointSendContext(params: {
|
|||||||
ref: {},
|
ref: {},
|
||||||
log: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
log: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
||||||
conversationType: "groupChat" as const,
|
conversationType: "groupChat" as const,
|
||||||
|
replyStyle: "top-level" as const,
|
||||||
tokenProvider: { getAccessToken: vi.fn(async () => "token") },
|
tokenProvider: { getAccessToken: vi.fn(async () => "token") },
|
||||||
mediaMaxBytes: 8 * 1024 * 1024,
|
mediaMaxBytes: 8 * 1024 * 1024,
|
||||||
sharePointSiteId: params.siteId,
|
sharePointSiteId: params.siteId,
|
||||||
@@ -184,6 +185,7 @@ describe("sendMessageMSTeams", () => {
|
|||||||
ref: {},
|
ref: {},
|
||||||
log: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
log: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
||||||
conversationType: "personal",
|
conversationType: "personal",
|
||||||
|
replyStyle: "top-level",
|
||||||
tokenProvider: { getAccessToken: vi.fn(async () => "token") },
|
tokenProvider: { getAccessToken: vi.fn(async () => "token") },
|
||||||
mediaMaxBytes: 8 * 1024,
|
mediaMaxBytes: 8 * 1024,
|
||||||
sharePointSiteId: undefined,
|
sharePointSiteId: undefined,
|
||||||
@@ -266,6 +268,62 @@ describe("sendMessageMSTeams", () => {
|
|||||||
expect(mockState.convertMarkdownTables).toHaveBeenCalledWith("hello", "off");
|
expect(mockState.convertMarkdownTables).toHaveBeenCalledWith("hello", "off");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("passes the resolved proactive replyStyle to text sends", async () => {
|
||||||
|
mockState.resolveMSTeamsSendContext.mockResolvedValue({
|
||||||
|
adapter: {},
|
||||||
|
appId: "app-id",
|
||||||
|
conversationId: "19:channel@thread.tacv2",
|
||||||
|
ref: {
|
||||||
|
threadId: "thread-root-1",
|
||||||
|
conversation: { id: "19:channel@thread.tacv2", conversationType: "channel" },
|
||||||
|
},
|
||||||
|
log: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
||||||
|
conversationType: "channel",
|
||||||
|
replyStyle: "thread",
|
||||||
|
tokenProvider: { getAccessToken: vi.fn(async () => "token") },
|
||||||
|
mediaMaxBytes: 8 * 1024,
|
||||||
|
sharePointSiteId: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
await sendMessageMSTeams({
|
||||||
|
cfg: {} as OpenClawConfig,
|
||||||
|
to: "conversation:19:channel@thread.tacv2",
|
||||||
|
text: "threaded reply",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockState.sendMSTeamsMessages).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ replyStyle: "thread" }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps top-level proactive replyStyle when resolved for a channel", async () => {
|
||||||
|
mockState.resolveMSTeamsSendContext.mockResolvedValue({
|
||||||
|
adapter: {},
|
||||||
|
appId: "app-id",
|
||||||
|
conversationId: "19:channel@thread.tacv2",
|
||||||
|
ref: {
|
||||||
|
threadId: "thread-root-1",
|
||||||
|
conversation: { id: "19:channel@thread.tacv2", conversationType: "channel" },
|
||||||
|
},
|
||||||
|
log: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
||||||
|
conversationType: "channel",
|
||||||
|
replyStyle: "top-level",
|
||||||
|
tokenProvider: { getAccessToken: vi.fn(async () => "token") },
|
||||||
|
mediaMaxBytes: 8 * 1024,
|
||||||
|
sharePointSiteId: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
await sendMessageMSTeams({
|
||||||
|
cfg: {} as OpenClawConfig,
|
||||||
|
to: "conversation:19:channel@thread.tacv2",
|
||||||
|
text: "top-level reply",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockState.sendMSTeamsMessages).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ replyStyle: "top-level" }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("uses graphChatId instead of conversationId when uploading to SharePoint", async () => {
|
it("uses graphChatId instead of conversationId when uploading to SharePoint", async () => {
|
||||||
// Simulates a group chat where Bot Framework conversationId is valid but we have
|
// Simulates a group chat where Bot Framework conversationId is valid but we have
|
||||||
// a resolved Graph chat ID cached from a prior send.
|
// a resolved Graph chat ID cached from a prior send.
|
||||||
|
|||||||
@@ -391,12 +391,13 @@ async function sendTextWithMedia(
|
|||||||
tokenProvider,
|
tokenProvider,
|
||||||
sharePointSiteId,
|
sharePointSiteId,
|
||||||
mediaMaxBytes,
|
mediaMaxBytes,
|
||||||
|
replyStyle,
|
||||||
} = ctx;
|
} = ctx;
|
||||||
|
|
||||||
let platformMessageIds: string[];
|
let platformMessageIds: string[];
|
||||||
try {
|
try {
|
||||||
platformMessageIds = await sendMSTeamsMessages({
|
platformMessageIds = await sendMSTeamsMessages({
|
||||||
replyStyle: "top-level",
|
replyStyle,
|
||||||
adapter,
|
adapter,
|
||||||
appId,
|
appId,
|
||||||
conversationRef: ref,
|
conversationRef: ref,
|
||||||
|
|||||||
Reference in New Issue
Block a user