import { WebClient } from "@slack/web-api";
import { AuthProvider, setResetSlackClientFn } from "./auth/auth-provider.js";
export class SlackClientError extends Error {
constructor(
message: string,
public code: string,
public statusCode?: number
) {
super(message);
this.name = "SlackClientError";
}
}
let slackClient: WebClient | null = null;
export function resetSlackClient(): void {
slackClient = null;
}
// Register the reset function with auth provider
setResetSlackClientFn(resetSlackClient);
export function getSlackClient(): WebClient {
if (slackClient) {
return slackClient;
}
const authProvider = AuthProvider.getInstance();
const token = authProvider.getToken();
slackClient = new WebClient(token, {
retryConfig: {
retries: 3,
factor: 2,
randomize: true,
},
});
return slackClient;
}
export function handleSlackError(error: unknown): never {
if (error instanceof SlackClientError) {
throw error;
}
if (error && typeof error === "object" && "data" in error) {
const slackError = error as { data?: { error?: string; ok?: boolean } };
if (slackError.data && !slackError.data.ok) {
const errorCode = slackError.data.error || "unknown_error";
const errorMessages: Record<string, string> = {
invalid_auth: "Invalid authentication token.",
account_inactive: "Authentication token is for a deleted user or workspace.",
token_revoked: "Authentication token has been revoked.",
no_permission: "The token does not have the required scopes.",
missing_scope: "The token is missing a required scope for this operation.",
channel_not_found: "The specified channel was not found.",
user_not_found: "The specified user was not found.",
message_not_found: "The specified message was not found.",
not_in_channel: "The user is not a member of the channel.",
is_archived: "The channel has been archived.",
ratelimited: "Rate limit exceeded. Please try again later.",
};
throw new SlackClientError(
errorMessages[errorCode] || `Slack API error: ${errorCode}`,
errorCode
);
}
}
if (error instanceof Error) {
throw new SlackClientError(error.message, "unknown_error");
}
throw new SlackClientError("An unknown error occurred", "unknown_error");
}
export function formatTimestamp(ts: string): string {
const timestamp = parseFloat(ts) * 1000;
return new Date(timestamp).toISOString();
}
export function parseMessageLink(permalink: string): {
team: string;
channel: string;
ts: string;
} | null {
const match = permalink.match(
/https:\/\/([^.]+)\.slack\.com\/archives\/([^/]+)\/p(\d+)/
);
if (!match) return null;
const [, team, channel, tsRaw] = match;
const ts = `${tsRaw.slice(0, -6)}.${tsRaw.slice(-6)}`;
return { team, channel, ts };
}