feat: add slack user scopes and teams graph hints

This commit is contained in:
Peter Steinberger
2026-01-17 19:32:38 +00:00
parent c32ad19377
commit 727c07bd88
3 changed files with 58 additions and 25 deletions

View File

@@ -45,13 +45,26 @@ type ChannelCapabilitiesReport = {
support?: ChannelCapabilities;
actions?: string[];
probe?: unknown;
scopes?: SlackScopesResult;
slackScopes?: Array<{
tokenType: "bot" | "user";
result: SlackScopesResult;
}>;
target?: DiscordTargetSummary;
channelPermissions?: DiscordPermissionsReport;
};
const REQUIRED_DISCORD_PERMISSIONS = ["ViewChannel", "SendMessages"] as const;
const TEAMS_GRAPH_PERMISSION_HINTS: Record<string, string> = {
"ChannelMessage.Read.All": "channel history",
"Chat.Read.All": "chat history",
"Channel.ReadBasic.All": "channel list",
"Team.ReadBasic.All": "team list",
"TeamsActivity.Read.All": "teams activity",
"Sites.Read.All": "files (SharePoint)",
"Files.Read.All": "files (OneDrive)",
};
function normalizeTimeout(raw: unknown, fallback = 10_000) {
const value = typeof raw === "string" ? Number(raw) : Number(raw);
if (!Number.isFinite(value) || value <= 0) return fallback;
@@ -185,8 +198,16 @@ function formatProbeLines(channelId: string, probe: unknown): string[] {
if (graph.ok === false) {
lines.push(`Graph: ${theme.error(graph.error ?? "failed")}`);
} else if (roles.length > 0 || scopes.length > 0) {
if (roles.length > 0) lines.push(`Graph roles: ${roles.join(", ")}`);
if (scopes.length > 0) lines.push(`Graph scopes: ${scopes.join(", ")}`);
const formatPermission = (permission: string) => {
const hint = TEAMS_GRAPH_PERMISSION_HINTS[permission];
return hint ? `${permission} (${hint})` : permission;
};
if (roles.length > 0) {
lines.push(`Graph roles: ${roles.map(formatPermission).join(", ")}`);
}
if (scopes.length > 0) {
lines.push(`Graph scopes: ${scopes.map(formatPermission).join(", ")}`);
}
} else if (graph.ok === true) {
lines.push("Graph: ok");
}
@@ -302,14 +323,31 @@ async function resolveChannelReports(params: {
}
}
let scopes: SlackScopesResult | undefined;
let slackScopes: ChannelCapabilitiesReport["slackScopes"];
if (plugin.id === "slack" && configured && enabled) {
const token = (resolvedAccount as { botToken?: string }).botToken?.trim();
if (!token) {
scopes = { ok: false, error: "Slack bot token missing." };
const botToken = (resolvedAccount as { botToken?: string }).botToken?.trim();
const userToken = (
resolvedAccount as { config?: { userToken?: string } }
).config?.userToken?.trim();
const scopeReports: NonNullable<ChannelCapabilitiesReport["slackScopes"]> = [];
if (botToken) {
scopeReports.push({
tokenType: "bot",
result: await fetchSlackScopes(botToken, timeoutMs),
});
} else {
scopes = await fetchSlackScopes(token, timeoutMs);
scopeReports.push({
tokenType: "bot",
result: { ok: false, error: "Slack bot token missing." },
});
}
if (userToken) {
scopeReports.push({
tokenType: "user",
result: await fetchSlackScopes(userToken, timeoutMs),
});
}
slackScopes = scopeReports;
}
let discordTarget: DiscordTargetSummary | undefined;
@@ -337,7 +375,7 @@ async function resolveChannelReports(params: {
target: discordTarget,
channelPermissions: discordPermissions,
actions,
scopes,
slackScopes,
});
}
return reports;
@@ -425,12 +463,15 @@ export async function channelsCapabilitiesCommand(
} else if (report.configured && report.enabled) {
lines.push(theme.muted("Probe: unavailable"));
}
if (report.channel === "slack" && report.scopes) {
if (report.scopes.ok && report.scopes.scopes?.length) {
const source = report.scopes.source ? ` (${report.scopes.source})` : "";
lines.push(`Scopes${source}: ${report.scopes.scopes.join(", ")}`);
} else if (report.scopes.error) {
lines.push(`Scopes: ${theme.error(report.scopes.error)}`);
if (report.channel === "slack" && report.slackScopes) {
for (const entry of report.slackScopes) {
const source = entry.result.source ? ` (${entry.result.source})` : "";
const label = entry.tokenType === "user" ? "User scopes" : "Bot scopes";
if (entry.result.ok && entry.result.scopes?.length) {
lines.push(`${label}${source}: ${entry.result.scopes.join(", ")}`);
} else if (entry.result.error) {
lines.push(`${label}: ${theme.error(entry.result.error)}`);
}
}
}
if (report.channel === "discord" && report.channelPermissions) {