import type { OpenClawConfig } from
"../runtime-api.js" ;
import { resolveConversationPath, resolveGraphConversationId } from
"./graph-messages.js" ;
import {
deleteGraphRequest,
escapeOData,
fetchGraphJson,
patchGraphJson,
postGraphJson,
resolveGraphToken,
} from
"./graph.js" ;
// ---------------------------------------------------------------------------
// Add Participant
// ---------------------------------------------------------------------------
export type AddParticipantMSTeamsParams = {
cfg: OpenClawConfig;
to: string;
userId: string;
role?: string;
};
export type AddParticipantMSTeamsResult = {
added: { userId: string; chatId: string };
};
type ConversationMemberRole =
"member" |
"owner" ;
function normalizeConversationMemberRole(role: string | undefined): ConversationMembe
rRole {
const normalized = role?.trim().toLowerCase() ?? "" ;
if (!normalized) {
return "member" ;
}
if (normalized === "member" || normalized === "owner" ) {
return normalized;
}
throw new Error('MS Teams participant role must be "member" or "owner".' );
}
/**
* Add a user to a chat or channel via Graph API .
*/
export async function addParticipantMSTeams(
params: AddParticipantMSTeamsParams,
): Promise<AddParticipantMSTeamsResult> {
const token = await resolveGraphToken(params.cfg);
const conversationId = await resolveGraphConversationId(params.to);
const conv = resolveConversationPath(conversationId);
const body = {
"@odata.type" : "#microsoft.graph.aadUserConversationMember" ,
roles: [normalizeConversationMemberRole(params.role)],
"user@odata.bind" : `https://graph.microsoft.com/v1.0/users('${escapeOData(params.userId)}')`,
};
await postGraphJson<unknown>({
token,
path: `${conv.basePath}/members`,
body,
});
return { added: { userId: params.userId, chatId: conversationId } };
}
// ---------------------------------------------------------------------------
// Remove Participant
// ---------------------------------------------------------------------------
export type RemoveParticipantMSTeamsParams = {
cfg: OpenClawConfig;
to: string;
userId: string;
};
export type RemoveParticipantMSTeamsResult = {
removed: { userId: string; chatId: string };
};
type GraphConversationMember = {
id?: string;
userId?: string;
};
type GraphConversationMemberResponse = {
value?: GraphConversationMember[];
"@odata.nextLink" ?: string;
};
/**
* Remove a user from a chat or channel via Graph API .
* Lists members first to resolve the membership ID , then deletes .
*/
export async function removeParticipantMSTeams(
params: RemoveParticipantMSTeamsParams,
): Promise<RemoveParticipantMSTeamsResult> {
const token = await resolveGraphToken(params.cfg);
const conversationId = await resolveGraphConversationId(params.to);
const conv = resolveConversationPath(conversationId);
// List members to find the membership ID for the target user. Graph can
// paginate large chats/channels, so walk `@odata.nextLink` before concluding
// the user is missing.
const MAX_PAGES = 10 ;
let nextPath: string | undefined = `${conv.basePath}/members`;
let page = 0 ;
let member: GraphConversationMember | undefined;
while (nextPath && page < MAX_PAGES && !member) {
const membersRes: GraphConversationMemberResponse =
await fetchGraphJson<GraphConversationMemberResponse>({
token,
path: nextPath,
});
member = (membersRes.value ?? []).find(
(candidate: GraphConversationMember) => candidate.userId === params.userId,
);
if (member) {
break ;
}
const nextLink: string | undefined = membersRes["@odata.nextLink" ];
nextPath = nextLink ? nextLink.replace("https://graph.microsoft.com/v1.0 ", "") : undefined;
page++;
}
if (!member?.id) {
throw new Error(`User ${params.userId} is not a member of this conversation`);
}
await deleteGraphRequest({
token,
path: `${conv.basePath}/members/${encodeURIComponent(member.id)}`,
});
return { removed: { userId: params.userId, chatId: conversationId } };
}
// ---------------------------------------------------------------------------
// Rename Group
// ---------------------------------------------------------------------------
export type RenameGroupMSTeamsParams = {
cfg: OpenClawConfig;
to: string;
name: string;
};
export type RenameGroupMSTeamsResult = {
renamed: { chatId: string; newName: string };
};
/**
* Rename a chat ( topic ) or channel ( displayName ) via Graph API .
*/
export async function renameGroupMSTeams(
params: RenameGroupMSTeamsParams,
): Promise<RenameGroupMSTeamsResult> {
const token = await resolveGraphToken(params.cfg);
const conversationId = await resolveGraphConversationId(params.to);
const conv = resolveConversationPath(conversationId);
const body = conv.kind === "chat" ? { topic: params.name } : { displayName: params.name };
await patchGraphJson<unknown>({
token,
path: conv.basePath,
body,
});
return { renamed: { chatId: conversationId, newName: params.name } };
}
Messung V0.5 in Prozent C=98 H=94 G=95
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland