125 lines
3.9 KiB
TypeScript
125 lines
3.9 KiB
TypeScript
import type { ClawdbotConfig } from "../../../src/config/types.js";
|
|
import type { getChildLogger as getChildLoggerFn } from "../../../src/logging.js";
|
|
import type {
|
|
MSTeamsConversationStore,
|
|
StoredConversationReference,
|
|
} from "./conversation-store.js";
|
|
import { createMSTeamsConversationStoreFs } from "./conversation-store-fs.js";
|
|
import type { MSTeamsAdapter } from "./messenger.js";
|
|
import { createMSTeamsAdapter, loadMSTeamsSdkWithAuth } from "./sdk.js";
|
|
import { resolveMSTeamsCredentials } from "./token.js";
|
|
|
|
let _log: ReturnType<typeof getChildLoggerFn> | undefined;
|
|
const getLog = async (): Promise<ReturnType<typeof getChildLoggerFn>> => {
|
|
if (_log) return _log;
|
|
const { getChildLogger } = await import("../logging.js");
|
|
_log = getChildLogger({ name: "msteams:send" });
|
|
return _log;
|
|
};
|
|
|
|
export type MSTeamsProactiveContext = {
|
|
appId: string;
|
|
conversationId: string;
|
|
ref: StoredConversationReference;
|
|
adapter: MSTeamsAdapter;
|
|
log: Awaited<ReturnType<typeof getLog>>;
|
|
};
|
|
|
|
/**
|
|
* Parse the --to argument into a conversation reference lookup key.
|
|
* Supported formats:
|
|
* - conversation:19:abc@thread.tacv2 → lookup by conversation ID
|
|
* - user:aad-object-id → lookup by user AAD object ID
|
|
* - 19:abc@thread.tacv2 → direct conversation ID
|
|
*/
|
|
function parseRecipient(to: string): {
|
|
type: "conversation" | "user";
|
|
id: string;
|
|
} {
|
|
const trimmed = to.trim();
|
|
const finalize = (type: "conversation" | "user", id: string) => {
|
|
const normalized = id.trim();
|
|
if (!normalized) {
|
|
throw new Error(`Invalid --to value: missing ${type} id`);
|
|
}
|
|
return { type, id: normalized };
|
|
};
|
|
if (trimmed.startsWith("conversation:")) {
|
|
return finalize("conversation", trimmed.slice("conversation:".length));
|
|
}
|
|
if (trimmed.startsWith("user:")) {
|
|
return finalize("user", trimmed.slice("user:".length));
|
|
}
|
|
// Assume it's a conversation ID if it looks like one
|
|
if (trimmed.startsWith("19:") || trimmed.includes("@thread")) {
|
|
return finalize("conversation", trimmed);
|
|
}
|
|
// Otherwise treat as user ID
|
|
return finalize("user", trimmed);
|
|
}
|
|
|
|
/**
|
|
* Find a stored conversation reference for the given recipient.
|
|
*/
|
|
async function findConversationReference(recipient: {
|
|
type: "conversation" | "user";
|
|
id: string;
|
|
store: MSTeamsConversationStore;
|
|
}): Promise<{
|
|
conversationId: string;
|
|
ref: StoredConversationReference;
|
|
} | null> {
|
|
if (recipient.type === "conversation") {
|
|
const ref = await recipient.store.get(recipient.id);
|
|
if (ref) return { conversationId: recipient.id, ref };
|
|
return null;
|
|
}
|
|
|
|
const found = await recipient.store.findByUserId(recipient.id);
|
|
if (!found) return null;
|
|
return { conversationId: found.conversationId, ref: found.reference };
|
|
}
|
|
|
|
export async function resolveMSTeamsSendContext(params: {
|
|
cfg: ClawdbotConfig;
|
|
to: string;
|
|
}): Promise<MSTeamsProactiveContext> {
|
|
const msteamsCfg = params.cfg.channels?.msteams;
|
|
|
|
if (!msteamsCfg?.enabled) {
|
|
throw new Error("msteams provider is not enabled");
|
|
}
|
|
|
|
const creds = resolveMSTeamsCredentials(msteamsCfg);
|
|
if (!creds) {
|
|
throw new Error("msteams credentials not configured");
|
|
}
|
|
|
|
const store = createMSTeamsConversationStoreFs();
|
|
|
|
// Parse recipient and find conversation reference
|
|
const recipient = parseRecipient(params.to);
|
|
const found = await findConversationReference({ ...recipient, store });
|
|
|
|
if (!found) {
|
|
throw new Error(
|
|
`No conversation reference found for ${recipient.type}:${recipient.id}. ` +
|
|
`The bot must receive a message from this conversation before it can send proactively.`,
|
|
);
|
|
}
|
|
|
|
const { conversationId, ref } = found;
|
|
const log = await getLog();
|
|
|
|
const { sdk, authConfig } = await loadMSTeamsSdkWithAuth(creds);
|
|
const adapter = createMSTeamsAdapter(authConfig, sdk);
|
|
|
|
return {
|
|
appId: creds.appId,
|
|
conversationId,
|
|
ref,
|
|
adapter: adapter as unknown as MSTeamsAdapter,
|
|
log,
|
|
};
|
|
}
|