import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/config-runtime"; import { convertMarkdownTables } from "openclaw/plugin-sdk/text-runtime"; import { loadOutboundMediaFromUrl, type OpenClawConfig } from "../runtime-api.js"; import { createMSTeamsConversationStoreFs } from "./conversation-store-fs.js"; import {
classifyMSTeamsSendError,
formatMSTeamsSendErrorHint,
formatUnknownError,
} from "./errors.js"; import { prepareFileConsentActivityFs, requiresFileConsent } from "./file-consent-helpers.js"; import { buildTeamsFileInfoCard } from "./graph-chat.js"; import {
getDriveItemProperties,
uploadAndShareOneDrive,
uploadAndShareSharePoint,
} from "./graph-upload.js"; import { extractFilename, extractMessageId } from "./media-helpers.js"; import { buildConversationReference, sendMSTeamsMessages } from "./messenger.js"; import { setPendingUploadActivityIdFs } from "./pending-uploads-fs.js"; import { setPendingUploadActivityId } from "./pending-uploads.js"; import { buildMSTeamsPollCard } from "./polls.js"; import { resolveMSTeamsSendContext, type MSTeamsProactiveContext } from "./send-context.js";
export type SendMSTeamsMessageParams = { /** Full config (for credentials) */
cfg: OpenClawConfig; /** Conversation ID or user ID to send to */
to: string; /** Message text */
text: string; /** Optional media URL */
mediaUrl?: string; /** Optional filename override for uploaded media/files */
filename?: string;
mediaLocalRoots?: readonly string[];
mediaReadFile?: (filePath: string) => Promise<Buffer>;
};
export type SendMSTeamsMessageResult = {
messageId: string;
conversationId: string; /** If a FileConsentCard was sent instead of the file, this contains the upload ID */
pendingUploadId?: string;
};
/** Threshold for large files that require FileConsentCard flow in personal chats */ const FILE_CONSENT_THRESHOLD_BYTES = 4 * 1024 * 1024; // 4MB
export type SendMSTeamsPollParams = { /** Full config (for credentials) */
cfg: OpenClawConfig; /** Conversation ID or user ID to send to */
to: string; /** Poll question */
question: string; /** Poll options */
options: string[]; /** Max selections (defaults to 1) */
maxSelections?: number;
};
export type SendMSTeamsCardParams = { /** Full config (for credentials) */
cfg: OpenClawConfig; /** Conversation ID or user ID to send to */
to: string; /** Adaptive Card JSON object */
card: Record<string, unknown>;
};
export type SendMSTeamsCardResult = {
messageId: string;
conversationId: string;
};
// Personal chats: base64 only works for images; use FileConsentCard for large files or non-images if (
requiresFileConsent({
conversationType,
contentType: media.contentType,
bufferSize: media.buffer.length,
thresholdBytes: FILE_CONSENT_THRESHOLD_BYTES,
})
) { // Proactive CLI sends run in a different process from the gateway's // monitor that receives the fileConsent/invoke callback. Use the FS- // backed helper so the invoke handler can find the pending upload when // the user clicks "Allow". const { activity, uploadId } = await prepareFileConsentActivityFs({
media: { buffer: media.buffer, filename: fileName, contentType: media.contentType },
conversationId,
description: messageText || undefined,
});
// Store the activity ID so the accept handler can replace the consent // card in-place. Mirror it into the FS store too because the invoke // callback may be delivered to a different process than the CLI send.
setPendingUploadActivityId(uploadId, messageId);
await setPendingUploadActivityIdFs(uploadId, messageId);
// Personal chat with small image: use base64 (only works for images) if (conversationType === "personal") { // Small image in personal chat: use base64 (only works for images) const base64 = media.buffer.toString("base64"); const finalMediaUrl = `data:${media.contentType};base64,${base64}`;
if (isImage && !sharePointSiteId) { // Group chat/channel without SharePoint: send image inline (avoids OneDrive failures) const base64 = media.buffer.toString("base64"); const finalMediaUrl = `data:${media.contentType};base64,${base64}`; return sendTextWithMedia(ctx, messageText, finalMediaUrl);
}
// Group chat or channel: upload to SharePoint (if siteId configured) or OneDrive try { if (sharePointSiteId) { // Use SharePoint upload + Graph API for native file card
log.debug?.("uploading to SharePoint for native file card", {
fileName,
conversationType,
siteId: sharePointSiteId,
});
const uploaded = await uploadAndShareSharePoint({
buffer: media.buffer,
filename: fileName,
contentType: media.contentType,
tokenProvider,
siteId: sharePointSiteId, // Use the Graph-native chat ID (19:xxx format) — the Bot Framework conversationId // for personal DMs uses a different format that Graph API rejects.
chatId: ctx.graphChatId ?? conversationId,
usePerUserSharing: conversationType === "groupChat",
});
// Fallback: no SharePoint site configured, use OneDrive with markdown link
log.debug?.("uploading to OneDrive (no SharePoint site configured)", {
fileName,
conversationType,
});
export type EditMSTeamsMessageParams = { /** Full config (for credentials) */
cfg: OpenClawConfig; /** Conversation ID or user ID */
to: string; /** Activity ID of the message to edit */
activityId: string; /** New message text */
text: string;
};
export type EditMSTeamsMessageResult = {
conversationId: string;
};
export type DeleteMSTeamsMessageParams = { /** Full config (for credentials) */
cfg: OpenClawConfig; /** Conversation ID or user ID */
to: string; /** Activity ID of the message to delete */
activityId: string;
};
export type DeleteMSTeamsMessageResult = {
conversationId: string;
};
/** *Edit(update)apreviouslysentmessageinaTeamsconversation. * *UsestheBotFramework`continueConversation`→`updateActivity`flow *forproactiveeditsoutsideoftheoriginalturncontext.
*/
export async function editMessageMSTeams(
params: EditMSTeamsMessageParams,
): Promise<EditMSTeamsMessageResult> { const { cfg, to, activityId, text } = params; const { adapter, appId, conversationId, ref, log } = await resolveMSTeamsSendContext({
cfg,
to,
});
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.