import type { ClawdbotConfig } from "../../../src/config/config.js"; import type { DmPolicy } from "../../../src/config/types.js"; import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js"; import { formatDocsLink } from "../../../src/terminal/links.js"; import type { WizardPrompter } from "../../../src/wizard/prompts.js"; import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy, } from "../../../src/channels/plugins/onboarding-types.js"; import { addWildcardAllowFrom } from "../../../src/channels/plugins/onboarding/helpers.js"; import { resolveMSTeamsCredentials } from "./token.js"; const channel = "msteams" as const; function setMSTeamsDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy) { const allowFrom = dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.msteams?.allowFrom)?.map((entry) => String(entry)) : undefined; return { ...cfg, channels: { ...cfg.channels, msteams: { ...cfg.channels?.msteams, dmPolicy, ...(allowFrom ? { allowFrom } : {}), }, }, }; } async function noteMSTeamsCredentialHelp(prompter: WizardPrompter): Promise { await prompter.note( [ "1) Azure Bot registration → get App ID + Tenant ID", "2) Add a client secret (App Password)", "3) Set webhook URL + messaging endpoint", "Tip: you can also set MSTEAMS_APP_ID / MSTEAMS_APP_PASSWORD / MSTEAMS_TENANT_ID.", `Docs: ${formatDocsLink("/channels/msteams", "msteams")}`, ].join("\n"), "MS Teams credentials", ); } const dmPolicy: ChannelOnboardingDmPolicy = { label: "MS Teams", channel, policyKey: "channels.msteams.dmPolicy", allowFromKey: "channels.msteams.allowFrom", getCurrent: (cfg) => cfg.channels?.msteams?.dmPolicy ?? "pairing", setPolicy: (cfg, policy) => setMSTeamsDmPolicy(cfg, policy), }; export const msteamsOnboardingAdapter: ChannelOnboardingAdapter = { channel, getStatus: async ({ cfg }) => { const configured = Boolean(resolveMSTeamsCredentials(cfg.channels?.msteams)); return { channel, configured, statusLines: [`MS Teams: ${configured ? "configured" : "needs app credentials"}`], selectionHint: configured ? "configured" : "needs app creds", quickstartScore: configured ? 2 : 0, }; }, configure: async ({ cfg, prompter }) => { const resolved = resolveMSTeamsCredentials(cfg.channels?.msteams); const hasConfigCreds = Boolean( cfg.channels?.msteams?.appId?.trim() && cfg.channels?.msteams?.appPassword?.trim() && cfg.channels?.msteams?.tenantId?.trim(), ); const canUseEnv = Boolean( !hasConfigCreds && process.env.MSTEAMS_APP_ID?.trim() && process.env.MSTEAMS_APP_PASSWORD?.trim() && process.env.MSTEAMS_TENANT_ID?.trim(), ); let next = cfg; let appId: string | null = null; let appPassword: string | null = null; let tenantId: string | null = null; if (!resolved) { await noteMSTeamsCredentialHelp(prompter); } if (canUseEnv) { const keepEnv = await prompter.confirm({ message: "MSTEAMS_APP_ID + MSTEAMS_APP_PASSWORD + MSTEAMS_TENANT_ID detected. Use env vars?", initialValue: true, }); if (keepEnv) { next = { ...next, channels: { ...next.channels, msteams: { ...next.channels?.msteams, enabled: true }, }, }; } else { appId = String( await prompter.text({ message: "Enter MS Teams App ID", validate: (value) => (value?.trim() ? undefined : "Required"), }), ).trim(); appPassword = String( await prompter.text({ message: "Enter MS Teams App Password", validate: (value) => (value?.trim() ? undefined : "Required"), }), ).trim(); tenantId = String( await prompter.text({ message: "Enter MS Teams Tenant ID", validate: (value) => (value?.trim() ? undefined : "Required"), }), ).trim(); } } else if (hasConfigCreds) { const keep = await prompter.confirm({ message: "MS Teams credentials already configured. Keep them?", initialValue: true, }); if (!keep) { appId = String( await prompter.text({ message: "Enter MS Teams App ID", validate: (value) => (value?.trim() ? undefined : "Required"), }), ).trim(); appPassword = String( await prompter.text({ message: "Enter MS Teams App Password", validate: (value) => (value?.trim() ? undefined : "Required"), }), ).trim(); tenantId = String( await prompter.text({ message: "Enter MS Teams Tenant ID", validate: (value) => (value?.trim() ? undefined : "Required"), }), ).trim(); } } else { appId = String( await prompter.text({ message: "Enter MS Teams App ID", validate: (value) => (value?.trim() ? undefined : "Required"), }), ).trim(); appPassword = String( await prompter.text({ message: "Enter MS Teams App Password", validate: (value) => (value?.trim() ? undefined : "Required"), }), ).trim(); tenantId = String( await prompter.text({ message: "Enter MS Teams Tenant ID", validate: (value) => (value?.trim() ? undefined : "Required"), }), ).trim(); } if (appId && appPassword && tenantId) { next = { ...next, channels: { ...next.channels, msteams: { ...next.channels?.msteams, enabled: true, appId, appPassword, tenantId, }, }, }; } return { cfg: next, accountId: DEFAULT_ACCOUNT_ID }; }, dmPolicy, disable: (cfg) => ({ ...cfg, channels: { ...cfg.channels, msteams: { ...cfg.channels?.msteams, enabled: false }, }, }), };