mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:10:43 +00:00
test: split slack client option imports
This commit is contained in:
95
extensions/slack/src/client-options.ts
Normal file
95
extensions/slack/src/client-options.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import type { RetryOptions, WebClientOptions } from "@slack/web-api";
|
||||
import { HttpsProxyAgent } from "https-proxy-agent";
|
||||
import { resolveEnvHttpProxyUrl } from "openclaw/plugin-sdk/infra-runtime";
|
||||
|
||||
export const SLACK_DEFAULT_RETRY_OPTIONS: RetryOptions = {
|
||||
retries: 2,
|
||||
factor: 2,
|
||||
minTimeout: 500,
|
||||
maxTimeout: 3000,
|
||||
randomize: true,
|
||||
};
|
||||
|
||||
export const SLACK_WRITE_RETRY_OPTIONS: RetryOptions = {
|
||||
retries: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether a hostname is excluded from proxying by `NO_PROXY` / `no_proxy`.
|
||||
* Supports comma-separated entries with optional leading dots (e.g. `.slack.com`).
|
||||
*/
|
||||
function isHostExcludedByNoProxy(hostname: string, env: NodeJS.ProcessEnv = process.env): boolean {
|
||||
const raw = env.no_proxy ?? env.NO_PROXY;
|
||||
if (!raw) {
|
||||
return false;
|
||||
}
|
||||
const entries = raw
|
||||
.split(/[,\s]+/)
|
||||
.map((e) => e.trim().toLowerCase())
|
||||
.filter(Boolean);
|
||||
const lower = hostname.toLowerCase();
|
||||
for (const entry of entries) {
|
||||
if (entry === "*") {
|
||||
return true;
|
||||
}
|
||||
// Strip optional wildcard/leading dot so `*.slack.com` and `.slack.com`
|
||||
// match both `slack.com` (apex) and Slack subdomains.
|
||||
const bare = entry.startsWith("*.")
|
||||
? entry.slice(2)
|
||||
: entry.startsWith(".")
|
||||
? entry.slice(1)
|
||||
: entry;
|
||||
if (lower === bare || lower.endsWith(`.${bare}`)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an HTTPS proxy agent from env vars (HTTPS_PROXY, HTTP_PROXY, etc.)
|
||||
* for use as the `agent` option in Slack WebClient and Socket Mode connections.
|
||||
*
|
||||
* When set, this agent is forwarded through @slack/bolt -> @slack/socket-mode ->
|
||||
* SlackWebSocket as the `httpAgent`, which the `ws` library uses to tunnel the
|
||||
* WebSocket upgrade request through the proxy. This fixes Socket Mode in
|
||||
* environments where outbound traffic must go through an HTTP CONNECT proxy.
|
||||
*
|
||||
* Respects `NO_PROXY` / `no_proxy`; if `*.slack.com` (or a matching pattern)
|
||||
* appears in the exclusion list, returns `undefined` so the connection is direct.
|
||||
*
|
||||
* Returns `undefined` when no proxy env var is configured or when Slack hosts
|
||||
* are excluded by `NO_PROXY`.
|
||||
*/
|
||||
function resolveSlackProxyAgent(): HttpsProxyAgent<string> | undefined {
|
||||
const proxyUrl = resolveEnvHttpProxyUrl("https");
|
||||
if (!proxyUrl) {
|
||||
return undefined;
|
||||
}
|
||||
// Slack Socket Mode connects to these hosts; skip proxy if excluded.
|
||||
if (isHostExcludedByNoProxy("slack.com")) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return new HttpsProxyAgent(proxyUrl);
|
||||
} catch {
|
||||
// Malformed proxy URL; degrade gracefully to direct connection.
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveSlackWebClientOptions(options: WebClientOptions = {}): WebClientOptions {
|
||||
return {
|
||||
...options,
|
||||
agent: options.agent ?? resolveSlackProxyAgent(),
|
||||
retryConfig: options.retryConfig ?? SLACK_DEFAULT_RETRY_OPTIONS,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveSlackWriteClientOptions(options: WebClientOptions = {}): WebClientOptions {
|
||||
return {
|
||||
...options,
|
||||
agent: options.agent ?? resolveSlackProxyAgent(),
|
||||
retryConfig: options.retryConfig ?? SLACK_WRITE_RETRY_OPTIONS,
|
||||
};
|
||||
}
|
||||
@@ -1,98 +1,11 @@
|
||||
import { type RetryOptions, type WebClientOptions, WebClient } from "@slack/web-api";
|
||||
import { HttpsProxyAgent } from "https-proxy-agent";
|
||||
import { resolveEnvHttpProxyUrl } from "openclaw/plugin-sdk/infra-runtime";
|
||||
|
||||
export const SLACK_DEFAULT_RETRY_OPTIONS: RetryOptions = {
|
||||
retries: 2,
|
||||
factor: 2,
|
||||
minTimeout: 500,
|
||||
maxTimeout: 3000,
|
||||
randomize: true,
|
||||
};
|
||||
|
||||
export const SLACK_WRITE_RETRY_OPTIONS: RetryOptions = {
|
||||
retries: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether a hostname is excluded from proxying by `NO_PROXY` / `no_proxy`.
|
||||
* Supports comma-separated entries with optional leading dots (e.g. `.slack.com`).
|
||||
*/
|
||||
function isHostExcludedByNoProxy(hostname: string, env: NodeJS.ProcessEnv = process.env): boolean {
|
||||
const raw = env.no_proxy ?? env.NO_PROXY;
|
||||
if (!raw) {
|
||||
return false;
|
||||
}
|
||||
const entries = raw
|
||||
.split(/[,\s]+/)
|
||||
.map((e) => e.trim().toLowerCase())
|
||||
.filter(Boolean);
|
||||
const lower = hostname.toLowerCase();
|
||||
for (const entry of entries) {
|
||||
if (entry === "*") {
|
||||
return true;
|
||||
}
|
||||
// Strip optional wildcard/leading dot so `*.slack.com` and `.slack.com`
|
||||
// match both `slack.com` (apex) and Slack subdomains.
|
||||
const bare = entry.startsWith("*.")
|
||||
? entry.slice(2)
|
||||
: entry.startsWith(".")
|
||||
? entry.slice(1)
|
||||
: entry;
|
||||
if (lower === bare || lower.endsWith(`.${bare}`)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an HTTPS proxy agent from env vars (HTTPS_PROXY, HTTP_PROXY, etc.)
|
||||
* for use as the `agent` option in Slack WebClient and Socket Mode connections.
|
||||
*
|
||||
* When set, this agent is forwarded through @slack/bolt → @slack/socket-mode →
|
||||
* SlackWebSocket as the `httpAgent`, which the `ws` library uses to tunnel the
|
||||
* WebSocket upgrade request through the proxy. This fixes Socket Mode in
|
||||
* environments where outbound traffic must go through an HTTP CONNECT proxy.
|
||||
*
|
||||
* Respects `NO_PROXY` / `no_proxy` — if `*.slack.com` (or a matching pattern)
|
||||
* appears in the exclusion list, returns `undefined` so the connection is direct.
|
||||
*
|
||||
* Returns `undefined` when no proxy env var is configured or when Slack hosts
|
||||
* are excluded by `NO_PROXY`.
|
||||
*/
|
||||
function resolveSlackProxyAgent(): HttpsProxyAgent<string> | undefined {
|
||||
const proxyUrl = resolveEnvHttpProxyUrl("https");
|
||||
if (!proxyUrl) {
|
||||
return undefined;
|
||||
}
|
||||
// Slack Socket Mode connects to these hosts; skip proxy if excluded.
|
||||
if (isHostExcludedByNoProxy("slack.com")) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return new HttpsProxyAgent(proxyUrl);
|
||||
} catch {
|
||||
// Malformed proxy URL — degrade gracefully to direct connection.
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveSlackWebClientOptions(options: WebClientOptions = {}): WebClientOptions {
|
||||
return {
|
||||
...options,
|
||||
agent: options.agent ?? resolveSlackProxyAgent(),
|
||||
retryConfig: options.retryConfig ?? SLACK_DEFAULT_RETRY_OPTIONS,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveSlackWriteClientOptions(options: WebClientOptions = {}): WebClientOptions {
|
||||
return {
|
||||
...options,
|
||||
agent: options.agent ?? resolveSlackProxyAgent(),
|
||||
retryConfig: options.retryConfig ?? SLACK_WRITE_RETRY_OPTIONS,
|
||||
};
|
||||
}
|
||||
import { type WebClientOptions, WebClient } from "@slack/web-api";
|
||||
import { resolveSlackWebClientOptions, resolveSlackWriteClientOptions } from "./client-options.js";
|
||||
export {
|
||||
resolveSlackWebClientOptions,
|
||||
resolveSlackWriteClientOptions,
|
||||
SLACK_DEFAULT_RETRY_OPTIONS,
|
||||
SLACK_WRITE_RETRY_OPTIONS,
|
||||
} from "./client-options.js";
|
||||
|
||||
export function createSlackWebClient(token: string, options: WebClientOptions = {}) {
|
||||
return new WebClient(token, resolveSlackWebClientOptions(options));
|
||||
|
||||
@@ -23,7 +23,7 @@ import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-i
|
||||
import { normalizeStringEntries } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { installRequestBodyLimitGuard } from "openclaw/plugin-sdk/webhook-request-guards";
|
||||
import { resolveSlackAccount } from "../accounts.js";
|
||||
import { resolveSlackWebClientOptions } from "../client.js";
|
||||
import { resolveSlackWebClientOptions } from "../client-options.js";
|
||||
import { isSlackExecApprovalClientEnabled } from "../exec-approvals.js";
|
||||
import { normalizeSlackWebhookPath, registerSlackHttpHandler } from "../http/index.js";
|
||||
import { SLACK_TEXT_LIMIT } from "../limits.js";
|
||||
|
||||
Reference in New Issue
Block a user