scoootscooob 8746362f5e
refactor(slack): move Slack channel code to extensions/slack/src/ (#45621)
Move all Slack channel implementation files from src/slack/ to
extensions/slack/src/ and replace originals with shim re-exports.
This follows the extension migration pattern for channel plugins.

- Copy all .ts files to extensions/slack/src/ (preserving directory
  structure: monitor/, http/, monitor/events/, monitor/message-handler/)
- Transform import paths: external src/ imports use relative paths
  back to src/, internal slack imports stay relative within extension
- Replace all src/slack/ files with shim re-exports pointing to
  the extension copies
- Update tsconfig.plugin-sdk.dts.json rootDir from "src" to "." so
  the DTS build can follow shim chains into extensions/
- Update write-plugin-sdk-entry-dts.ts re-export path accordingly
- Preserve extensions/slack/index.ts, package.json, openclaw.plugin.json,
  src/channel.ts, src/runtime.ts, src/channel.test.ts (untouched)
2026-03-14 02:47:04 -07:00

108 lines
3.2 KiB
TypeScript

import {
compileAllowlist,
resolveCompiledAllowlistMatch,
type AllowlistMatch,
} from "../../../../src/channels/allowlist-match.js";
import {
normalizeHyphenSlug,
normalizeStringEntries,
normalizeStringEntriesLower,
} from "../../../../src/shared/string-normalization.js";
const SLACK_SLUG_CACHE_MAX = 512;
const slackSlugCache = new Map<string, string>();
export function normalizeSlackSlug(raw?: string) {
const key = raw ?? "";
const cached = slackSlugCache.get(key);
if (cached !== undefined) {
return cached;
}
const normalized = normalizeHyphenSlug(raw);
slackSlugCache.set(key, normalized);
if (slackSlugCache.size > SLACK_SLUG_CACHE_MAX) {
const oldest = slackSlugCache.keys().next();
if (!oldest.done) {
slackSlugCache.delete(oldest.value);
}
}
return normalized;
}
export function normalizeAllowList(list?: Array<string | number>) {
return normalizeStringEntries(list);
}
export function normalizeAllowListLower(list?: Array<string | number>) {
return normalizeStringEntriesLower(list);
}
export function normalizeSlackAllowOwnerEntry(entry: string): string | undefined {
const trimmed = entry.trim().toLowerCase();
if (!trimmed || trimmed === "*") {
return undefined;
}
const withoutPrefix = trimmed.replace(/^(slack:|user:)/, "");
return /^u[a-z0-9]+$/.test(withoutPrefix) ? withoutPrefix : undefined;
}
export type SlackAllowListMatch = AllowlistMatch<
"wildcard" | "id" | "prefixed-id" | "prefixed-user" | "name" | "prefixed-name" | "slug"
>;
type SlackAllowListSource = Exclude<SlackAllowListMatch["matchSource"], undefined>;
export function resolveSlackAllowListMatch(params: {
allowList: string[];
id?: string;
name?: string;
allowNameMatching?: boolean;
}): SlackAllowListMatch {
const compiledAllowList = compileAllowlist(params.allowList);
const id = params.id?.toLowerCase();
const name = params.name?.toLowerCase();
const slug = normalizeSlackSlug(name);
const candidates: Array<{ value?: string; source: SlackAllowListSource }> = [
{ value: id, source: "id" },
{ value: id ? `slack:${id}` : undefined, source: "prefixed-id" },
{ value: id ? `user:${id}` : undefined, source: "prefixed-user" },
...(params.allowNameMatching === true
? ([
{ value: name, source: "name" as const },
{ value: name ? `slack:${name}` : undefined, source: "prefixed-name" as const },
{ value: slug, source: "slug" as const },
] satisfies Array<{ value?: string; source: SlackAllowListSource }>)
: []),
];
return resolveCompiledAllowlistMatch({
compiledAllowlist: compiledAllowList,
candidates,
});
}
export function allowListMatches(params: {
allowList: string[];
id?: string;
name?: string;
allowNameMatching?: boolean;
}) {
return resolveSlackAllowListMatch(params).allowed;
}
export function resolveSlackUserAllowed(params: {
allowList?: Array<string | number>;
userId?: string;
userName?: string;
allowNameMatching?: boolean;
}) {
const allowList = normalizeAllowListLower(params.allowList);
if (allowList.length === 0) {
return true;
}
return allowListMatches({
allowList,
id: params.userId,
name: params.userName,
allowNameMatching: params.allowNameMatching,
});
}