






















@@ -70,32 +70,96 @@ type SlackSendOpts = {
7070blocks?: (Block | KnownBlock)[];
7171};
727273+type SlackWebApiErrorData = {
74+error?: unknown;
75+needed?: unknown;
76+response_metadata?: {
77+scopes?: unknown;
78+acceptedScopes?: unknown;
79+};
80+};
81+82+type SlackWebApiError = Error & {
83+data?: SlackWebApiErrorData;
84+};
85+7386function hasCustomIdentity(identity?: SlackSendIdentity): boolean {
7487return Boolean(identity?.username || identity?.iconUrl || identity?.iconEmoji);
7588}
768977-function isSlackCustomizeScopeError(err: unknown): boolean {
90+function normalizeSlackApiString(value: unknown): string | undefined {
91+return typeof value === "string" && value.trim() ? value.trim() : undefined;
92+}
93+94+function normalizeSlackScopeList(value: unknown): string[] {
95+if (!Array.isArray(value)) {
96+return [];
97+}
98+return value.flatMap((scope) => {
99+const normalized = normalizeSlackApiString(scope);
100+return normalized ? [normalized] : [];
101+});
102+}
103+104+function getSlackWebApiErrorData(err: unknown): SlackWebApiErrorData | undefined {
78105if (!(err instanceof Error)) {
79-return false;
106+return undefined;
80107}
81-const maybeData = err as Error & {
82-data?: {
83-error?: string;
84-needed?: string;
85-response_metadata?: { scopes?: string[]; acceptedScopes?: string[] };
86-};
87-};
88-const code = normalizeLowercaseStringOrEmpty(maybeData.data?.error);
108+const data = (err as SlackWebApiError).data;
109+if (!data || typeof data !== "object") {
110+return undefined;
111+}
112+return data;
113+}
114+115+function formatSlackWebApiErrorMessage(err: unknown): string | undefined {
116+if (!(err instanceof Error)) {
117+return undefined;
118+}
119+const data = getSlackWebApiErrorData(err);
120+const code = normalizeSlackApiString(data?.error);
121+if (!code) {
122+return undefined;
123+}
124+const details: string[] = [];
125+const needed = normalizeSlackApiString(data?.needed);
126+if (needed) {
127+details.push(`needed: ${needed}`);
128+}
129+const scopes = normalizeSlackScopeList(data?.response_metadata?.scopes);
130+if (scopes.length) {
131+details.push(`granted: ${scopes.join(", ")}`);
132+}
133+const acceptedScopes = normalizeSlackScopeList(data?.response_metadata?.acceptedScopes);
134+if (acceptedScopes.length) {
135+details.push(`accepted: ${acceptedScopes.join(", ")}`);
136+}
137+return `${err.message || `An API error occurred: ${code}`}${
138+ details.length ? ` (${details.join("; ")})` : ""
139+ }`;
140+}
141+142+function enrichSlackWebApiError(err: unknown): unknown {
143+const message = formatSlackWebApiErrorMessage(err);
144+if (!message || !(err instanceof Error) || message === err.message) {
145+return err;
146+}
147+return new Error(message);
148+}
149+150+function isSlackCustomizeScopeError(err: unknown): boolean {
151+const data = getSlackWebApiErrorData(err);
152+const code = normalizeLowercaseStringOrEmpty(normalizeSlackApiString(data?.error));
89153if (code !== "missing_scope") {
90154return false;
91155}
92-const needed = normalizeLowercaseStringOrEmpty(maybeData.data?.needed);
156+const needed = normalizeLowercaseStringOrEmpty(normalizeSlackApiString(data?.needed));
93157if (needed?.includes("chat:write.customize")) {
94158return true;
95159}
96160const scopes = [
97- ...(maybeData.data?.response_metadata?.scopes ?? []),
98- ...(maybeData.data?.response_metadata?.acceptedScopes ?? []),
161+ ...normalizeSlackScopeList(data?.response_metadata?.scopes),
162+ ...normalizeSlackScopeList(data?.response_metadata?.acceptedScopes),
99163].map((scope) => normalizeLowercaseStringOrEmpty(scope));
100164return scopes.includes("chat:write.customize");
101165}
@@ -401,6 +465,22 @@ async function sendMessageSlackQueued(params: {
401465token: string;
402466recipient: SlackRecipient;
403467blocks?: (Block | KnownBlock)[];
468+}): Promise<SlackSendResult> {
469+try {
470+return await sendMessageSlackQueuedInner(params);
471+} catch (err) {
472+throw enrichSlackWebApiError(err);
473+}
474+}
475+476+async function sendMessageSlackQueuedInner(params: {
477+trimmedMessage: string;
478+opts: SlackSendOpts;
479+cfg: OpenClawConfig;
480+account: ReturnType<typeof resolveSlackAccount>;
481+token: string;
482+recipient: SlackRecipient;
483+blocks?: (Block | KnownBlock)[];
404484}): Promise<SlackSendResult> {
405485const { opts, cfg, account, token, recipient, blocks, trimmedMessage } = params;
406486const client = opts.client ?? getSlackWriteClient(token);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。