mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 03:01:25 +00:00
chore: Enable "curly" rule to avoid single-statement if confusion/errors.
This commit is contained in:
@@ -2,7 +2,9 @@ import type { AllowlistMatch } from "../../channels/allowlist-match.js";
|
||||
|
||||
export function normalizeSlackSlug(raw?: string) {
|
||||
const trimmed = raw?.trim().toLowerCase() ?? "";
|
||||
if (!trimmed) return "";
|
||||
if (!trimmed) {
|
||||
return "";
|
||||
}
|
||||
const dashed = trimmed.replace(/\s+/g, "-");
|
||||
const cleaned = dashed.replace(/[^a-z0-9#@._+-]+/g, "-");
|
||||
return cleaned.replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
||||
@@ -26,7 +28,9 @@ export function resolveSlackAllowListMatch(params: {
|
||||
name?: string;
|
||||
}): SlackAllowListMatch {
|
||||
const allowList = params.allowList;
|
||||
if (allowList.length === 0) return { allowed: false };
|
||||
if (allowList.length === 0) {
|
||||
return { allowed: false };
|
||||
}
|
||||
if (allowList.includes("*")) {
|
||||
return { allowed: true, matchKey: "*", matchSource: "wildcard" };
|
||||
}
|
||||
@@ -42,7 +46,9 @@ export function resolveSlackAllowListMatch(params: {
|
||||
{ value: slug, source: "slug" },
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (!candidate.value) continue;
|
||||
if (!candidate.value) {
|
||||
continue;
|
||||
}
|
||||
if (allowList.includes(candidate.value)) {
|
||||
return {
|
||||
allowed: true,
|
||||
@@ -64,7 +70,9 @@ export function resolveSlackUserAllowed(params: {
|
||||
userName?: string;
|
||||
}) {
|
||||
const allowList = normalizeAllowListLower(params.allowList);
|
||||
if (allowList.length === 0) return true;
|
||||
if (allowList.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return allowListMatches({
|
||||
allowList,
|
||||
id: params.userId,
|
||||
|
||||
@@ -21,7 +21,9 @@ export type SlackChannelConfigResolved = {
|
||||
|
||||
function firstDefined<T>(...values: Array<T | undefined>) {
|
||||
for (const value of values) {
|
||||
if (typeof value !== "undefined") return value;
|
||||
if (typeof value !== "undefined") {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -36,13 +38,19 @@ export function shouldEmitSlackReactionNotification(params: {
|
||||
}) {
|
||||
const { mode, botId, messageAuthorId, userId, userName, allowlist } = params;
|
||||
const effectiveMode = mode ?? "own";
|
||||
if (effectiveMode === "off") return false;
|
||||
if (effectiveMode === "off") {
|
||||
return false;
|
||||
}
|
||||
if (effectiveMode === "own") {
|
||||
if (!botId || !messageAuthorId) return false;
|
||||
if (!botId || !messageAuthorId) {
|
||||
return false;
|
||||
}
|
||||
return messageAuthorId === botId;
|
||||
}
|
||||
if (effectiveMode === "allowlist") {
|
||||
if (!Array.isArray(allowlist) || allowlist.length === 0) return false;
|
||||
if (!Array.isArray(allowlist) || allowlist.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const users = normalizeAllowListLower(allowlist);
|
||||
return allowListMatches({
|
||||
allowList: users,
|
||||
|
||||
@@ -18,10 +18,18 @@ export function inferSlackChannelType(
|
||||
channelId?: string | null,
|
||||
): SlackMessageEvent["channel_type"] | undefined {
|
||||
const trimmed = channelId?.trim();
|
||||
if (!trimmed) return undefined;
|
||||
if (trimmed.startsWith("D")) return "im";
|
||||
if (trimmed.startsWith("C")) return "channel";
|
||||
if (trimmed.startsWith("G")) return "group";
|
||||
if (!trimmed) {
|
||||
return undefined;
|
||||
}
|
||||
if (trimmed.startsWith("D")) {
|
||||
return "im";
|
||||
}
|
||||
if (trimmed.startsWith("C")) {
|
||||
return "channel";
|
||||
}
|
||||
if (trimmed.startsWith("G")) {
|
||||
return "group";
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -169,7 +177,9 @@ export function createSlackMonitorContext(params: {
|
||||
const defaultRequireMention = params.defaultRequireMention ?? true;
|
||||
|
||||
const markMessageSeen = (channelId: string | undefined, ts?: string) => {
|
||||
if (!channelId || !ts) return false;
|
||||
if (!channelId || !ts) {
|
||||
return false;
|
||||
}
|
||||
return seenMessages.check(`${channelId}:${ts}`);
|
||||
};
|
||||
|
||||
@@ -178,7 +188,9 @@ export function createSlackMonitorContext(params: {
|
||||
channelType?: string | null;
|
||||
}) => {
|
||||
const channelId = p.channelId?.trim() ?? "";
|
||||
if (!channelId) return params.mainKey;
|
||||
if (!channelId) {
|
||||
return params.mainKey;
|
||||
}
|
||||
const channelType = normalizeSlackChannelType(p.channelType, channelId);
|
||||
const isDirectMessage = channelType === "im";
|
||||
const isGroup = channelType === "mpim";
|
||||
@@ -197,7 +209,9 @@ export function createSlackMonitorContext(params: {
|
||||
|
||||
const resolveChannelName = async (channelId: string) => {
|
||||
const cached = channelCache.get(channelId);
|
||||
if (cached) return cached;
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
try {
|
||||
const info = await params.app.client.conversations.info({
|
||||
token: params.botToken,
|
||||
@@ -227,7 +241,9 @@ export function createSlackMonitorContext(params: {
|
||||
|
||||
const resolveUserName = async (userId: string) => {
|
||||
const cached = userCache.get(userId);
|
||||
if (cached) return cached;
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
try {
|
||||
const info = await params.app.client.users.info({
|
||||
token: params.botToken,
|
||||
@@ -248,7 +264,9 @@ export function createSlackMonitorContext(params: {
|
||||
threadTs?: string;
|
||||
status: string;
|
||||
}) => {
|
||||
if (!p.threadTs) return;
|
||||
if (!p.threadTs) {
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
token: params.botToken,
|
||||
channel_id: p.channelId,
|
||||
@@ -286,8 +304,12 @@ export function createSlackMonitorContext(params: {
|
||||
const isGroupDm = channelType === "mpim";
|
||||
const isRoom = channelType === "channel" || channelType === "group";
|
||||
|
||||
if (isDirectMessage && !params.dmEnabled) return false;
|
||||
if (isGroupDm && !params.groupDmEnabled) return false;
|
||||
if (isDirectMessage && !params.dmEnabled) {
|
||||
return false;
|
||||
}
|
||||
if (isGroupDm && !params.groupDmEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isGroupDm && groupDmChannels.length > 0) {
|
||||
const allowList = normalizeAllowListLower(groupDmChannels);
|
||||
@@ -301,7 +323,9 @@ export function createSlackMonitorContext(params: {
|
||||
.map((value) => value.toLowerCase());
|
||||
const permitted =
|
||||
allowList.includes("*") || candidates.some((candidate) => allowList.includes(candidate));
|
||||
if (!permitted) return false;
|
||||
if (!permitted) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isRoom && p.channelId) {
|
||||
@@ -342,7 +366,9 @@ export function createSlackMonitorContext(params: {
|
||||
};
|
||||
|
||||
const shouldDropMismatchedSlackEvent = (body: unknown) => {
|
||||
if (!body || typeof body !== "object") return false;
|
||||
if (!body || typeof body !== "object") {
|
||||
return false;
|
||||
}
|
||||
const raw = body as { api_app_id?: unknown; team_id?: unknown };
|
||||
const incomingApiAppId = typeof raw.api_app_id === "string" ? raw.api_app_id : "";
|
||||
const incomingTeamId = typeof raw.team_id === "string" ? raw.team_id : "";
|
||||
|
||||
@@ -21,7 +21,9 @@ export function registerSlackChannelEvents(params: { ctx: SlackMonitorContext })
|
||||
"channel_created",
|
||||
async ({ event, body }: SlackEventMiddlewareArgs<"channel_created">) => {
|
||||
try {
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = event as SlackChannelCreatedEvent;
|
||||
const channelId = payload.channel?.id;
|
||||
@@ -54,7 +56,9 @@ export function registerSlackChannelEvents(params: { ctx: SlackMonitorContext })
|
||||
"channel_rename",
|
||||
async ({ event, body }: SlackEventMiddlewareArgs<"channel_rename">) => {
|
||||
try {
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = event as SlackChannelRenamedEvent;
|
||||
const channelId = payload.channel?.id;
|
||||
@@ -87,12 +91,16 @@ export function registerSlackChannelEvents(params: { ctx: SlackMonitorContext })
|
||||
"channel_id_changed",
|
||||
async ({ event, body }: SlackEventMiddlewareArgs<"channel_id_changed">) => {
|
||||
try {
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = event as SlackChannelIdChangedEvent;
|
||||
const oldChannelId = payload.old_channel_id;
|
||||
const newChannelId = payload.new_channel_id;
|
||||
if (!oldChannelId || !newChannelId) return;
|
||||
if (!oldChannelId || !newChannelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const channelInfo = await ctx.resolveChannelName(newChannelId);
|
||||
const label = resolveSlackChannelLabel({
|
||||
|
||||
@@ -14,7 +14,9 @@ export function registerSlackMemberEvents(params: { ctx: SlackMonitorContext })
|
||||
"member_joined_channel",
|
||||
async ({ event, body }: SlackEventMiddlewareArgs<"member_joined_channel">) => {
|
||||
try {
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) {
|
||||
return;
|
||||
}
|
||||
const payload = event as SlackMemberChannelEvent;
|
||||
const channelId = payload.channel;
|
||||
const channelInfo = channelId ? await ctx.resolveChannelName(channelId) : {};
|
||||
@@ -52,7 +54,9 @@ export function registerSlackMemberEvents(params: { ctx: SlackMonitorContext })
|
||||
"member_left_channel",
|
||||
async ({ event, body }: SlackEventMiddlewareArgs<"member_left_channel">) => {
|
||||
try {
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) {
|
||||
return;
|
||||
}
|
||||
const payload = event as SlackMemberChannelEvent;
|
||||
const channelId = payload.channel;
|
||||
const channelInfo = channelId ? await ctx.resolveChannelName(channelId) : {};
|
||||
|
||||
@@ -21,7 +21,9 @@ export function registerSlackMessageEvents(params: {
|
||||
|
||||
ctx.app.event("message", async ({ event, body }: SlackEventMiddlewareArgs<"message">) => {
|
||||
try {
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = event as SlackMessageEvent;
|
||||
if (message.subtype === "message_changed") {
|
||||
@@ -119,7 +121,9 @@ export function registerSlackMessageEvents(params: {
|
||||
|
||||
ctx.app.event("app_mention", async ({ event, body }: SlackEventMiddlewareArgs<"app_mention">) => {
|
||||
try {
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mention = event as SlackAppMentionEvent;
|
||||
await handleSlackMessage(mention as unknown as SlackMessageEvent, {
|
||||
|
||||
@@ -12,7 +12,9 @@ export function registerSlackPinEvents(params: { ctx: SlackMonitorContext }) {
|
||||
|
||||
ctx.app.event("pin_added", async ({ event, body }: SlackEventMiddlewareArgs<"pin_added">) => {
|
||||
try {
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = event as SlackPinEvent;
|
||||
const channelId = payload.channel_id;
|
||||
@@ -49,7 +51,9 @@ export function registerSlackPinEvents(params: { ctx: SlackMonitorContext }) {
|
||||
|
||||
ctx.app.event("pin_removed", async ({ event, body }: SlackEventMiddlewareArgs<"pin_removed">) => {
|
||||
try {
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = event as SlackPinEvent;
|
||||
const channelId = payload.channel_id;
|
||||
|
||||
@@ -13,7 +13,9 @@ export function registerSlackReactionEvents(params: { ctx: SlackMonitorContext }
|
||||
const handleReactionEvent = async (event: SlackReactionEvent, action: string) => {
|
||||
try {
|
||||
const item = event.item;
|
||||
if (!item || item.type !== "message") return;
|
||||
if (!item || item.type !== "message") {
|
||||
return;
|
||||
}
|
||||
|
||||
const channelInfo = item.channel ? await ctx.resolveChannelName(item.channel) : {};
|
||||
const channelType = channelInfo?.type;
|
||||
@@ -54,7 +56,9 @@ export function registerSlackReactionEvents(params: { ctx: SlackMonitorContext }
|
||||
ctx.app.event(
|
||||
"reaction_added",
|
||||
async ({ event, body }: SlackEventMiddlewareArgs<"reaction_added">) => {
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) {
|
||||
return;
|
||||
}
|
||||
await handleReactionEvent(event as SlackReactionEvent, "added");
|
||||
},
|
||||
);
|
||||
@@ -62,7 +66,9 @@ export function registerSlackReactionEvents(params: { ctx: SlackMonitorContext }
|
||||
ctx.app.event(
|
||||
"reaction_removed",
|
||||
async ({ event, body }: SlackEventMiddlewareArgs<"reaction_removed">) => {
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
||||
if (ctx.shouldDropMismatchedSlackEvent(body)) {
|
||||
return;
|
||||
}
|
||||
await handleReactionEvent(event as SlackReactionEvent, "removed");
|
||||
},
|
||||
);
|
||||
|
||||
@@ -49,7 +49,9 @@ export async function resolveSlackMedia(params: {
|
||||
const files = params.files ?? [];
|
||||
for (const file of files) {
|
||||
const url = file.url_private_download ?? file.url_private;
|
||||
if (!url) continue;
|
||||
if (!url) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// Note: We ignore init options because fetchWithSlackAuth handles
|
||||
// redirect behavior specially. fetchRemoteMedia only passes the URL.
|
||||
@@ -63,7 +65,9 @@ export async function resolveSlackMedia(params: {
|
||||
fetchImpl,
|
||||
filePathHint: file.name,
|
||||
});
|
||||
if (fetched.buffer.byteLength > params.maxBytes) continue;
|
||||
if (fetched.buffer.byteLength > params.maxBytes) {
|
||||
continue;
|
||||
}
|
||||
const saved = await saveMediaBuffer(
|
||||
fetched.buffer,
|
||||
fetched.contentType ?? file.mimetype,
|
||||
@@ -99,7 +103,9 @@ export async function resolveSlackThreadStarter(params: {
|
||||
}): Promise<SlackThreadStarter | null> {
|
||||
const cacheKey = `${params.channelId}:${params.threadTs}`;
|
||||
const cached = THREAD_STARTER_CACHE.get(cacheKey);
|
||||
if (cached) return cached;
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
try {
|
||||
const response = (await params.client.conversations.replies({
|
||||
channel: params.channelId,
|
||||
@@ -109,7 +115,9 @@ export async function resolveSlackThreadStarter(params: {
|
||||
})) as { messages?: Array<{ text?: string; user?: string; ts?: string; files?: SlackFile[] }> };
|
||||
const message = response?.messages?.[0];
|
||||
const text = (message?.text ?? "").trim();
|
||||
if (!message || !text) return null;
|
||||
if (!message || !text) {
|
||||
return null;
|
||||
}
|
||||
const starter: SlackThreadStarter = {
|
||||
text,
|
||||
userId: message.user,
|
||||
|
||||
@@ -30,7 +30,9 @@ export function createSlackMessageHandler(params: {
|
||||
debounceMs,
|
||||
buildKey: (entry) => {
|
||||
const senderId = entry.message.user ?? entry.message.bot_id;
|
||||
if (!senderId) return null;
|
||||
if (!senderId) {
|
||||
return null;
|
||||
}
|
||||
const messageTs = entry.message.ts ?? entry.message.event_ts;
|
||||
// If Slack flags a thread reply but omits thread_ts, isolate it from root debouncing.
|
||||
const threadKey = entry.message.thread_ts
|
||||
@@ -42,13 +44,19 @@ export function createSlackMessageHandler(params: {
|
||||
},
|
||||
shouldDebounce: (entry) => {
|
||||
const text = entry.message.text ?? "";
|
||||
if (!text.trim()) return false;
|
||||
if (entry.message.files && entry.message.files.length > 0) return false;
|
||||
if (!text.trim()) {
|
||||
return false;
|
||||
}
|
||||
if (entry.message.files && entry.message.files.length > 0) {
|
||||
return false;
|
||||
}
|
||||
return !hasControlCommand(text, ctx.cfg);
|
||||
},
|
||||
onFlush: async (entries) => {
|
||||
const last = entries.at(-1);
|
||||
if (!last) return;
|
||||
if (!last) {
|
||||
return;
|
||||
}
|
||||
const combinedText =
|
||||
entries.length === 1
|
||||
? (last.message.text ?? "")
|
||||
@@ -70,7 +78,9 @@ export function createSlackMessageHandler(params: {
|
||||
wasMentioned: combinedMentioned || last.opts.wasMentioned,
|
||||
},
|
||||
});
|
||||
if (!prepared) return;
|
||||
if (!prepared) {
|
||||
return;
|
||||
}
|
||||
if (entries.length > 1) {
|
||||
const ids = entries.map((entry) => entry.message.ts).filter(Boolean) as string[];
|
||||
if (ids.length > 0) {
|
||||
@@ -87,7 +97,9 @@ export function createSlackMessageHandler(params: {
|
||||
});
|
||||
|
||||
return async (message, opts) => {
|
||||
if (opts.source === "message" && message.type !== "message") return;
|
||||
if (opts.source === "message" && message.type !== "message") {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
opts.source === "message" &&
|
||||
message.subtype &&
|
||||
@@ -96,7 +108,9 @@ export function createSlackMessageHandler(params: {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (ctx.markMessageSeen(message.channel, message.ts)) return;
|
||||
if (ctx.markMessageSeen(message.channel, message.ts)) {
|
||||
return;
|
||||
}
|
||||
const resolvedMessage = await threadTsResolver.resolve({ message, source: opts.source });
|
||||
await debouncer.enqueue({ message: resolvedMessage, opts });
|
||||
};
|
||||
|
||||
@@ -67,7 +67,9 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
});
|
||||
},
|
||||
stop: async () => {
|
||||
if (!didSetStatus) return;
|
||||
if (!didSetStatus) {
|
||||
return;
|
||||
}
|
||||
didSetStatus = false;
|
||||
await ctx.setSlackThreadStatus({
|
||||
channelId: message.channel,
|
||||
|
||||
@@ -92,7 +92,9 @@ export async function prepareSlackMessage(params: {
|
||||
|
||||
const isBotMessage = Boolean(message.bot_id);
|
||||
if (isBotMessage) {
|
||||
if (message.user && ctx.botUserId && message.user === ctx.botUserId) return null;
|
||||
if (message.user && ctx.botUserId && message.user === ctx.botUserId) {
|
||||
return null;
|
||||
}
|
||||
if (!allowBots) {
|
||||
logVerbose(`slack: drop bot message ${message.bot_id ?? "unknown"} (allowBots=false)`);
|
||||
return null;
|
||||
@@ -337,7 +339,9 @@ export async function prepareSlackMessage(params: {
|
||||
maxBytes: ctx.mediaMaxBytes,
|
||||
});
|
||||
const rawBody = (message.text ?? "").trim() || media?.placeholder || "";
|
||||
if (!rawBody) return null;
|
||||
if (!rawBody) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ackReaction = resolveAckReaction(cfg, route.agentId);
|
||||
const ackReactionValue = ackReaction ?? "";
|
||||
@@ -552,7 +556,9 @@ export async function prepareSlackMessage(params: {
|
||||
});
|
||||
|
||||
const replyTarget = ctxPayload.To ?? undefined;
|
||||
if (!replyTarget) return null;
|
||||
if (!replyTarget) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (shouldLogVerbose()) {
|
||||
logVerbose(`slack inbound: channel=${message.channel} from=${slackFrom} preview="${preview}"`);
|
||||
|
||||
@@ -4,8 +4,14 @@ export function isSlackChannelAllowedByPolicy(params: {
|
||||
channelAllowed: boolean;
|
||||
}): boolean {
|
||||
const { groupPolicy, channelAllowlistConfigured, channelAllowed } = params;
|
||||
if (groupPolicy === "disabled") return false;
|
||||
if (groupPolicy === "open") return true;
|
||||
if (!channelAllowlistConfigured) return false;
|
||||
if (groupPolicy === "disabled") {
|
||||
return false;
|
||||
}
|
||||
if (groupPolicy === "open") {
|
||||
return true;
|
||||
}
|
||||
if (!channelAllowlistConfigured) {
|
||||
return false;
|
||||
}
|
||||
return channelAllowed;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,9 @@ const slackBolt =
|
||||
const { App, HTTPReceiver } = slackBolt;
|
||||
function parseApiAppIdFromAppToken(raw?: string) {
|
||||
const token = raw?.trim();
|
||||
if (!token) return undefined;
|
||||
if (!token) {
|
||||
return undefined;
|
||||
}
|
||||
const match = /^xapp-\d-([a-z0-9]+)-/i.exec(token);
|
||||
return match?.[1]?.toUpperCase();
|
||||
}
|
||||
@@ -220,7 +222,9 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
||||
|
||||
if (resolveToken) {
|
||||
void (async () => {
|
||||
if (opts.abortSignal?.aborted) return;
|
||||
if (opts.abortSignal?.aborted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (channelsConfig && Object.keys(channelsConfig).length > 0) {
|
||||
try {
|
||||
@@ -235,7 +239,9 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
||||
const unresolved: string[] = [];
|
||||
for (const entry of resolved) {
|
||||
const source = channelsConfig?.[entry.input];
|
||||
if (!source) continue;
|
||||
if (!source) {
|
||||
continue;
|
||||
}
|
||||
if (!entry.resolved || !entry.id) {
|
||||
unresolved.push(entry.input);
|
||||
continue;
|
||||
@@ -284,12 +290,18 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
||||
if (channelsConfig && Object.keys(channelsConfig).length > 0) {
|
||||
const userEntries = new Set<string>();
|
||||
for (const channel of Object.values(channelsConfig)) {
|
||||
if (!channel || typeof channel !== "object") continue;
|
||||
if (!channel || typeof channel !== "object") {
|
||||
continue;
|
||||
}
|
||||
const channelUsers = (channel as { users?: Array<string | number> }).users;
|
||||
if (!Array.isArray(channelUsers)) continue;
|
||||
if (!Array.isArray(channelUsers)) {
|
||||
continue;
|
||||
}
|
||||
for (const entry of channelUsers) {
|
||||
const trimmed = String(entry).trim();
|
||||
if (trimmed && trimmed !== "*") userEntries.add(trimmed);
|
||||
if (trimmed && trimmed !== "*") {
|
||||
userEntries.add(trimmed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,14 +321,20 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
||||
|
||||
const nextChannels = { ...channelsConfig };
|
||||
for (const [channelKey, channelConfig] of Object.entries(channelsConfig)) {
|
||||
if (!channelConfig || typeof channelConfig !== "object") continue;
|
||||
if (!channelConfig || typeof channelConfig !== "object") {
|
||||
continue;
|
||||
}
|
||||
const channelUsers = (channelConfig as { users?: Array<string | number> }).users;
|
||||
if (!Array.isArray(channelUsers) || channelUsers.length === 0) continue;
|
||||
if (!Array.isArray(channelUsers) || channelUsers.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const additions: string[] = [];
|
||||
for (const entry of channelUsers) {
|
||||
const trimmed = String(entry).trim();
|
||||
const resolved = resolvedMap.get(trimmed);
|
||||
if (resolved?.resolved && resolved.id) additions.push(resolved.id);
|
||||
if (resolved?.resolved && resolved.id) {
|
||||
additions.push(resolved.id);
|
||||
}
|
||||
}
|
||||
nextChannels[channelKey] = {
|
||||
...channelConfig,
|
||||
@@ -337,7 +355,9 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
||||
}
|
||||
|
||||
const stopOnAbort = () => {
|
||||
if (opts.abortSignal?.aborted && slackMode === "socket") void app.stop();
|
||||
if (opts.abortSignal?.aborted && slackMode === "socket") {
|
||||
void app.stop();
|
||||
}
|
||||
};
|
||||
opts.abortSignal?.addEventListener("abort", stopOnAbort, { once: true });
|
||||
|
||||
@@ -348,7 +368,9 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
||||
} else {
|
||||
runtime.log?.(`slack http mode listening at ${slackWebhookPath}`);
|
||||
}
|
||||
if (opts.abortSignal?.aborted) return;
|
||||
if (opts.abortSignal?.aborted) {
|
||||
return;
|
||||
}
|
||||
await new Promise<void>((resolve) => {
|
||||
opts.abortSignal?.addEventListener("abort", () => resolve(), {
|
||||
once: true,
|
||||
|
||||
@@ -21,11 +21,15 @@ export async function deliverReplies(params: {
|
||||
const threadTs = payload.replyToId ?? params.replyThreadTs;
|
||||
const mediaList = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
|
||||
const text = payload.text ?? "";
|
||||
if (!text && mediaList.length === 0) continue;
|
||||
if (!text && mediaList.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mediaList.length === 0) {
|
||||
const trimmed = text.trim();
|
||||
if (!trimmed || isSilentReplyText(trimmed, SILENT_REPLY_TOKEN)) continue;
|
||||
if (!trimmed || isSilentReplyText(trimmed, SILENT_REPLY_TOKEN)) {
|
||||
continue;
|
||||
}
|
||||
await sendMessageSlack(params.target, trimmed, {
|
||||
token: params.token,
|
||||
threadTs,
|
||||
@@ -131,7 +135,9 @@ export async function deliverSlackSlashReplies(params: {
|
||||
const combined = [text ?? "", ...mediaList.map((url) => url.trim()).filter(Boolean)]
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
if (!combined) continue;
|
||||
if (!combined) {
|
||||
continue;
|
||||
}
|
||||
const chunkMode = params.chunkMode ?? "length";
|
||||
const markdownChunks =
|
||||
chunkMode === "newline"
|
||||
@@ -140,13 +146,17 @@ export async function deliverSlackSlashReplies(params: {
|
||||
const chunks = markdownChunks.flatMap((markdown) =>
|
||||
markdownToSlackMrkdwnChunks(markdown, chunkLimit, { tableMode: params.tableMode }),
|
||||
);
|
||||
if (!chunks.length && combined) chunks.push(combined);
|
||||
if (!chunks.length && combined) {
|
||||
chunks.push(combined);
|
||||
}
|
||||
for (const chunk of chunks) {
|
||||
messages.push(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
if (messages.length === 0) return;
|
||||
if (messages.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Slack slash command responses can be multi-part by sending follow-ups via response_url.
|
||||
const responseType = params.ephemeral ? "ephemeral" : "in_channel";
|
||||
|
||||
@@ -103,7 +103,9 @@ describe("Slack native command argument menus", () => {
|
||||
registerSlackMonitorSlashCommands({ ctx: ctx as never, account: account as never });
|
||||
|
||||
const handler = commands.get("/usage");
|
||||
if (!handler) throw new Error("Missing /usage handler");
|
||||
if (!handler) {
|
||||
throw new Error("Missing /usage handler");
|
||||
}
|
||||
|
||||
const respond = vi.fn().mockResolvedValue(undefined);
|
||||
const ack = vi.fn().mockResolvedValue(undefined);
|
||||
@@ -132,7 +134,9 @@ describe("Slack native command argument menus", () => {
|
||||
registerSlackMonitorSlashCommands({ ctx: ctx as never, account: account as never });
|
||||
|
||||
const handler = actions.get("openclaw_cmdarg");
|
||||
if (!handler) throw new Error("Missing arg-menu action handler");
|
||||
if (!handler) {
|
||||
throw new Error("Missing arg-menu action handler");
|
||||
}
|
||||
|
||||
const respond = vi.fn().mockResolvedValue(undefined);
|
||||
await handler({
|
||||
@@ -158,7 +162,9 @@ describe("Slack native command argument menus", () => {
|
||||
registerSlackMonitorSlashCommands({ ctx: ctx as never, account: account as never });
|
||||
|
||||
const handler = actions.get("openclaw_cmdarg");
|
||||
if (!handler) throw new Error("Missing arg-menu action handler");
|
||||
if (!handler) {
|
||||
throw new Error("Missing arg-menu action handler");
|
||||
}
|
||||
|
||||
const respond = vi.fn().mockResolvedValue(undefined);
|
||||
await handler({
|
||||
@@ -186,7 +192,9 @@ describe("Slack native command argument menus", () => {
|
||||
registerSlackMonitorSlashCommands({ ctx: ctx as never, account: account as never });
|
||||
|
||||
const handler = actions.get("openclaw_cmdarg");
|
||||
if (!handler) throw new Error("Missing arg-menu action handler");
|
||||
if (!handler) {
|
||||
throw new Error("Missing arg-menu action handler");
|
||||
}
|
||||
|
||||
await handler({
|
||||
ack: vi.fn().mockResolvedValue(undefined),
|
||||
@@ -208,7 +216,9 @@ describe("Slack native command argument menus", () => {
|
||||
registerSlackMonitorSlashCommands({ ctx: ctx as never, account: account as never });
|
||||
|
||||
const handler = actions.get("openclaw_cmdarg");
|
||||
if (!handler) throw new Error("Missing arg-menu action handler");
|
||||
if (!handler) {
|
||||
throw new Error("Missing arg-menu action handler");
|
||||
}
|
||||
|
||||
await handler({
|
||||
ack: vi.fn().mockResolvedValue(undefined),
|
||||
|
||||
@@ -101,7 +101,9 @@ describe("slack slash commands channel policy", () => {
|
||||
registerSlackMonitorSlashCommands({ ctx: ctx as never, account: account as never });
|
||||
|
||||
const handler = [...commands.values()][0];
|
||||
if (!handler) throw new Error("Missing slash handler");
|
||||
if (!handler) {
|
||||
throw new Error("Missing slash handler");
|
||||
}
|
||||
|
||||
const respond = vi.fn().mockResolvedValue(undefined);
|
||||
await handler({
|
||||
@@ -133,7 +135,9 @@ describe("slack slash commands channel policy", () => {
|
||||
registerSlackMonitorSlashCommands({ ctx: ctx as never, account: account as never });
|
||||
|
||||
const handler = [...commands.values()][0];
|
||||
if (!handler) throw new Error("Missing slash handler");
|
||||
if (!handler) {
|
||||
throw new Error("Missing slash handler");
|
||||
}
|
||||
|
||||
const respond = vi.fn().mockResolvedValue(undefined);
|
||||
await handler({
|
||||
@@ -166,7 +170,9 @@ describe("slack slash commands channel policy", () => {
|
||||
registerSlackMonitorSlashCommands({ ctx: ctx as never, account: account as never });
|
||||
|
||||
const handler = [...commands.values()][0];
|
||||
if (!handler) throw new Error("Missing slash handler");
|
||||
if (!handler) {
|
||||
throw new Error("Missing slash handler");
|
||||
}
|
||||
|
||||
const respond = vi.fn().mockResolvedValue(undefined);
|
||||
await handler({
|
||||
|
||||
@@ -45,7 +45,9 @@ const SLACK_COMMAND_ARG_ACTION_ID = "openclaw_cmdarg";
|
||||
const SLACK_COMMAND_ARG_VALUE_PREFIX = "cmdarg";
|
||||
|
||||
function chunkItems<T>(items: T[], size: number): T[][] {
|
||||
if (size <= 0) return [items];
|
||||
if (size <= 0) {
|
||||
return [items];
|
||||
}
|
||||
const rows: T[][] = [];
|
||||
for (let i = 0; i < items.length; i += size) {
|
||||
rows.push(items.slice(i, i + size));
|
||||
@@ -74,11 +76,17 @@ function parseSlackCommandArgValue(raw?: string | null): {
|
||||
value: string;
|
||||
userId: string;
|
||||
} | null {
|
||||
if (!raw) return null;
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const parts = raw.split("|");
|
||||
if (parts.length !== 5 || parts[0] !== SLACK_COMMAND_ARG_VALUE_PREFIX) return null;
|
||||
if (parts.length !== 5 || parts[0] !== SLACK_COMMAND_ARG_VALUE_PREFIX) {
|
||||
return null;
|
||||
}
|
||||
const [, command, arg, value, userId] = parts;
|
||||
if (!command || !arg || !value || !userId) return null;
|
||||
if (!command || !arg || !value || !userId) {
|
||||
return null;
|
||||
}
|
||||
const decode = (text: string) => {
|
||||
try {
|
||||
return decodeURIComponent(text);
|
||||
@@ -90,7 +98,9 @@ function parseSlackCommandArgValue(raw?: string | null): {
|
||||
const decodedArg = decode(arg);
|
||||
const decodedValue = decode(value);
|
||||
const decodedUserId = decode(userId);
|
||||
if (!decodedCommand || !decodedArg || !decodedValue || !decodedUserId) return null;
|
||||
if (!decodedCommand || !decodedArg || !decodedValue || !decodedUserId) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
command: decodedCommand,
|
||||
arg: decodedArg,
|
||||
@@ -163,7 +173,9 @@ export function registerSlackMonitorSlashCommands(params: {
|
||||
}
|
||||
await ack();
|
||||
|
||||
if (ctx.botUserId && command.user_id === ctx.botUserId) return;
|
||||
if (ctx.botUserId && command.user_id === ctx.botUserId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const channelInfo = await ctx.resolveChannelName(command.channel_id);
|
||||
const channelType =
|
||||
@@ -526,7 +538,9 @@ export function registerSlackMonitorSlashCommands(params: {
|
||||
logVerbose("slack: slash commands disabled");
|
||||
}
|
||||
|
||||
if (nativeCommands.length === 0 || !supportsInteractiveArgMenus) return;
|
||||
if (nativeCommands.length === 0 || !supportsInteractiveArgMenus) {
|
||||
return;
|
||||
}
|
||||
|
||||
const registerArgAction = (actionId: string) => {
|
||||
(
|
||||
@@ -540,7 +554,9 @@ export function registerSlackMonitorSlashCommands(params: {
|
||||
const respondFn =
|
||||
respond ??
|
||||
(async (payload: { text: string; blocks?: SlackBlock[]; response_type?: string }) => {
|
||||
if (!body.channel?.id || !body.user?.id) return;
|
||||
if (!body.channel?.id || !body.user?.id) {
|
||||
return;
|
||||
}
|
||||
await ctx.app.client.chat.postEphemeral({
|
||||
token: ctx.botToken,
|
||||
channel: body.channel.id,
|
||||
|
||||
@@ -54,7 +54,9 @@ export function createSlackThreadTsResolver(params: {
|
||||
|
||||
const getCached = (key: string, now: number) => {
|
||||
const entry = cache.get(key);
|
||||
if (!entry) return undefined;
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
if (ttlMs > 0 && now - entry.updatedAt > ttlMs) {
|
||||
cache.delete(key);
|
||||
return undefined;
|
||||
@@ -73,7 +75,9 @@ export function createSlackThreadTsResolver(params: {
|
||||
}
|
||||
while (cache.size > maxSize) {
|
||||
const oldestKey = cache.keys().next().value;
|
||||
if (!oldestKey) break;
|
||||
if (!oldestKey) {
|
||||
break;
|
||||
}
|
||||
cache.delete(oldestKey);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user