@openclaw/slack 2026.7.2-beta.7 → 2026.8.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{account-inspect-DEKpg9Ip.js → account-inspect-YJvFBbCR.js} +1 -1
- package/dist/account-inspect-api.js +1 -1
- package/dist/{accounts-CHAeNZOX.js → accounts-BlzxuYle.js} +7 -3
- package/dist/{accounts.runtime-BHD4Ma2R.js → accounts.runtime-CbabBL8x.js} +1 -1
- package/dist/{action-runtime-DfuYoMjG.js → action-runtime-D6jg2CQd.js} +193 -125
- package/dist/action-runtime.runtime-JtoOK9l5.js +2 -0
- package/dist/{actions-D3NuJvva.js → actions-DFk0ANVB.js} +116 -235
- package/dist/actions.runtime-CYLsYRai.js +2 -0
- package/dist/api.js +17 -17
- package/dist/{approval-handler.runtime-BA05P0QT.js → approval-handler.runtime-BNvAoKdK.js} +53 -14
- package/dist/{channel-B9fVdW3s.js → channel-CA5w7qDF.js} +209 -110
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-type-DXyos1d2.js → channel-type-CZWv55Wu.js} +9 -12
- package/dist/{config-adapter-CxBQH8fq.js → channel.setup-CpyYsEXH.js} +83 -25
- package/dist/{config-schema-SgZT6nXV.js → config-schema-CN3XZRX5.js} +27 -17
- package/dist/conversation-binding-route-DFVLUdYF.js +115 -0
- package/dist/{directory-config-CnCIAktf.js → directory-config-BGYyuzWh.js} +3 -3
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-DKunWh3A.js → directory-live-DjUJQLbq.js} +13 -22
- package/dist/{doctor-contract-CsGH1Dfc.js → doctor-contract-BAy7-EUQ.js} +30 -3
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{group-policy-NQZti9t-.js → group-policy-DyfB_ZIE.js} +282 -183
- package/dist/index.js +4 -1
- package/dist/{message-tool-api-DkhFpMoy.js → message-tool-api-CqMr5Rmc.js} +14 -3
- package/dist/message-tool-api.js +1 -1
- package/dist/{monitor-CmKz0wtI.js → monitor-Chrecrb9.js} +3 -3
- package/dist/{outbound-adapter-BkuMp2Vs.js → outbound-adapter-DuRqAQXK.js} +35 -23
- package/dist/{pipeline.runtime-DgtmuYCP.js → pipeline.runtime-Bbfd5VFW.js} +1386 -929
- package/dist/{policy-PRysPn23.js → policy-BKXSxrIS.js} +18 -14
- package/dist/{probe-CgDsSops.js → probe-BVA65leh.js} +38 -18
- package/dist/{provider-DmRizMZt.js → provider-CUUiwsg2.js} +3454 -2601
- package/dist/{relay-source-Blxj8xb4.js → relay-source-mhpMMRG-.js} +11 -21
- package/dist/{replies-4s_Vq_z9.js → replies-SnWyXcsL.js} +84 -27
- package/dist/{resolve-channels-yEqmM-3I.js → resolve-channels-BSpqC8jU.js} +39 -12
- package/dist/{resolve-users-BOrt5K-5.js → resolve-users-Dvz9pDe0.js} +28 -5
- package/dist/{runtime-api-QMb-Q5Ko.js → runtime-api-DBi7xc_g.js} +3 -3
- package/dist/runtime-api.js +11 -11
- package/dist/{scopes-BJSrkAp6.js → scopes-BSk4GZvZ.js} +1 -1
- package/dist/security-contract-api.js +1 -1
- package/dist/{send-DsLeZpST.js → send-CtnOxKCc.js} +249 -163
- package/dist/{send.runtime-gUdK6V3h.js → send.runtime-kYctMHSd.js} +1 -1
- package/dist/setup-entry.js +1 -4
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-BeNzrMrJ.js → setup-surface-BPFfp6So.js} +6 -6
- package/dist/{shared-DgIN3Dcr.js → shared-DJBaslN7.js} +45 -45
- package/dist/{block-text-DkHaoGem.js → slack-client-kind-BXyOOICI.js} +17 -2
- package/dist/{slash-dispatch.runtime-zPkyl7dB.js → slash-dispatch.runtime-Cf2FTrnf.js} +2 -2
- package/dist/{streaming-compat-3_X-21wW.js → streaming-compat-B-e0mqM0.js} +3 -8
- package/dist/{target-parsing-N6lHogYZ.js → target-parsing-BnMD2ZqZ.js} +40 -2
- package/dist/{targets-KmjP2E8s.js → targets-Cx5W_n3W.js} +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/crypto.d.ts +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/socket-mode/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/socket-mode/node_modules/@types/node/crypto.d.ts +1 -1
- package/node_modules/@slack/socket-mode/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/web-api/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/crypto.d.ts +1 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/package.json +2 -2
- package/node_modules/undici/lib/cache/memory-cache-store.js +54 -9
- package/node_modules/undici/lib/cache/sqlite-cache-store.js +7 -1
- package/node_modules/undici/lib/core/request.js +12 -1
- package/node_modules/undici/lib/dispatcher/client-h1.js +11 -2
- package/node_modules/undici/lib/handler/cache-handler.js +246 -64
- package/node_modules/undici/lib/handler/cache-revalidation-handler.js +3 -3
- package/node_modules/undici/lib/handler/retry-handler.js +24 -0
- package/node_modules/undici/lib/interceptor/cache.js +159 -52
- package/node_modules/undici/lib/util/cache.js +324 -62
- package/node_modules/undici/lib/util/date.js +23 -6
- package/node_modules/undici/lib/web/cookies/util.js +79 -9
- package/node_modules/undici/package.json +1 -1
- package/openclaw.plugin.json +280 -33
- package/package.json +9 -11
- package/skills/slack/SKILL.md +10 -68
- package/dist/account-reply-mode-DrUXogvO.js +0 -14
- package/dist/action-runtime.runtime-DQErZYpz.js +0 -2
- package/dist/actions.runtime-BIRU_G4M.js +0 -2
- package/dist/channel.setup-CLP5oTvh.js +0 -56
- package/dist/preflight-audio.runtime-D5Bgags0.js +0 -3
- package/dist/slash-plugin-commands.runtime-Dosfcx3X.js +0 -2
- package/node_modules/p-map/index.d.ts +0 -155
- package/node_modules/p-map/index.js +0 -285
- package/node_modules/p-map/license +0 -9
- package/node_modules/p-map/package.json +0 -57
- package/node_modules/p-map/readme.md +0 -190
- package/dist/{security-audit-DR097212.js → security-audit-B_yKr7MW.js} +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as resolveDefaultSlackAccountId, r as mergeSlackAccountConfig } from "./accounts-
|
|
1
|
+
import { i as resolveDefaultSlackAccountId, r as mergeSlackAccountConfig } from "./accounts-BlzxuYle.js";
|
|
2
2
|
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-resolution";
|
|
3
3
|
import { hasConfiguredSecretInput, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
4
4
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as inspectSlackAccount } from "./account-inspect-
|
|
1
|
+
import { t as inspectSlackAccount } from "./account-inspect-YJvFBbCR.js";
|
|
2
2
|
//#region extensions/slack/account-inspect-api.ts
|
|
3
3
|
function inspectSlackReadOnlyAccount(cfg, accountId) {
|
|
4
4
|
return inspectSlackAccount({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_ACCOUNT_ID, createAccountListHelpers, hasConfiguredAccountValue, normalizeAccountId } from "openclaw/plugin-sdk/account-resolution";
|
|
2
2
|
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
3
|
-
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
+
import { asOptionalRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
4
|
import { mapAllowFromEntries, normalizeChannelDmPolicy } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
5
5
|
import { resolveAccountEntry } from "openclaw/plugin-sdk/routing";
|
|
6
6
|
//#region extensions/slack/src/token.ts
|
|
@@ -39,7 +39,11 @@ function resolveSlackOperationToken(account, operation) {
|
|
|
39
39
|
return account.config.userTokenReadOnly === false ? botToken ?? userToken : botToken;
|
|
40
40
|
}
|
|
41
41
|
const { listAccountIds, resolveDefaultAccountId, resolveAccountConfig: resolveMergedSlackAccountConfig } = createAccountListHelpers("slack", {
|
|
42
|
-
nestedObjectKeys: [
|
|
42
|
+
nestedObjectKeys: [
|
|
43
|
+
"botLoopProtection",
|
|
44
|
+
"presenceEvents",
|
|
45
|
+
"relay"
|
|
46
|
+
],
|
|
43
47
|
hasImplicitDefaultAccount: (cfg) => {
|
|
44
48
|
const slack = cfg.channels?.slack;
|
|
45
49
|
if (slack?.postAs === "user") {
|
|
@@ -60,7 +64,7 @@ function resolveSlackAccountConfig(cfg, accountId) {
|
|
|
60
64
|
return resolveAccountEntry(cfg.channels?.slack?.accounts, accountId);
|
|
61
65
|
}
|
|
62
66
|
function asStreamingConfigObject(value) {
|
|
63
|
-
return
|
|
67
|
+
return asOptionalRecord(value);
|
|
64
68
|
}
|
|
65
69
|
function asLegacyStreamingScalar(value) {
|
|
66
70
|
return typeof value === "boolean" || typeof value === "string" ? value : void 0;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as resolveSlackAccount, l as resolveSlackOperationToken, o as resolveSlackAccountAllowFrom } from "./accounts-
|
|
1
|
+
import { a as resolveSlackAccount, l as resolveSlackOperationToken, o as resolveSlackAccountAllowFrom } from "./accounts-BlzxuYle.js";
|
|
2
2
|
export { resolveSlackAccount, resolveSlackAccountAllowFrom, resolveSlackOperationToken };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { t as slackContextTargetsMatch } from "./targets-
|
|
3
|
-
import {
|
|
4
|
-
import { a as readPositiveIntegerParam, c as withNormalizedTimestamp, n as imageResultFromFile, o as readReactionParams, r as jsonResult, s as readStringParam, t as createActionGate } from "./runtime-api-
|
|
5
|
-
import { n as resolveSlackChannelConfig, t as isSlackChannelAllowedByPolicy } from "./policy-
|
|
1
|
+
import { a as parseSlackTarget, n as formatSlackTarget, o as resolveSlackChannelId } from "./target-parsing-BnMD2ZqZ.js";
|
|
2
|
+
import { t as slackContextTargetsMatch } from "./targets-Cx5W_n3W.js";
|
|
3
|
+
import { Et as getSlackInstallationKind, Lt as SLACK_PRIVATE_ACTION_DELIVERY_RESULT, O as hasSlackNativeDataBlock, Rt as resolveSlackAutoThreadId, g as assertSlackDetachedTargetAllowed, pt as parseSlackBlocksInput, t as buildSlackChannelIdCandidates } from "./group-policy-DyfB_ZIE.js";
|
|
4
|
+
import { a as readPositiveIntegerParam, c as withNormalizedTimestamp, n as imageResultFromFile, o as readReactionParams, r as jsonResult, s as readStringParam, t as createActionGate } from "./runtime-api-DBi7xc_g.js";
|
|
5
|
+
import { n as resolveSlackChannelConfig, t as isSlackChannelAllowedByPolicy } from "./policy-BKXSxrIS.js";
|
|
6
6
|
import { normalizeAccountId } from "openclaw/plugin-sdk/account-resolution";
|
|
7
7
|
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
8
8
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
@@ -24,9 +24,10 @@ const pinActions = /* @__PURE__ */ new Set([
|
|
|
24
24
|
"unpinMessage",
|
|
25
25
|
"listPins"
|
|
26
26
|
]);
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
27
|
+
const SLACK_REACTION_USER_LIMIT = 100;
|
|
28
|
+
const loadSlackActionsRuntime = createLazyRuntimeModule(() => import("./actions.runtime-CYLsYRai.js"));
|
|
29
|
+
const loadSlackAccountsRuntime = createLazyRuntimeModule(() => import("./accounts.runtime-CbabBL8x.js"));
|
|
30
|
+
const loadSlackChannelTypeRuntime = createLazyRuntimeModule(() => import("./channel-type-CZWv55Wu.js").then((n) => n.t));
|
|
30
31
|
function createLazySlackAction(key) {
|
|
31
32
|
return (async (...args) => {
|
|
32
33
|
const action = (await loadSlackActionsRuntime())[key];
|
|
@@ -53,26 +54,15 @@ const slackActionRuntime = {
|
|
|
53
54
|
sendSlackMessage: createLazySlackAction("sendSlackMessage"),
|
|
54
55
|
unpinSlackMessage: createLazySlackAction("unpinSlackMessage")
|
|
55
56
|
};
|
|
56
|
-
/**
|
|
57
|
-
* Resolve threadTs for a Slack message based on context and replyToMode.
|
|
58
|
-
* - "all": always inject threadTs
|
|
59
|
-
* - "first"/"batched": inject only for the first eligible message (updates hasRepliedRef)
|
|
60
|
-
* - "off": never auto-inject
|
|
61
|
-
*/
|
|
62
57
|
function resolveThreadTsFromContext(explicitThreadTs, targetChannel, context, opts) {
|
|
63
58
|
if (explicitThreadTs) return explicitThreadTs;
|
|
64
59
|
if (opts?.suppressImplicitThread) return;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (context.replyToMode === "all") return context.currentThreadTs;
|
|
72
|
-
if (isSingleUseReplyToMode(context.replyToMode ?? "off") && context.hasRepliedRef && !context.hasRepliedRef.value) {
|
|
73
|
-
context.hasRepliedRef.value = true;
|
|
74
|
-
return context.currentThreadTs;
|
|
75
|
-
}
|
|
60
|
+
const threadTs = resolveSlackAutoThreadId({
|
|
61
|
+
to: targetChannel,
|
|
62
|
+
toolContext: context
|
|
63
|
+
});
|
|
64
|
+
if (isSingleUseReplyToMode(context?.replyToMode ?? "off") && !context?.hasRepliedRef) return;
|
|
65
|
+
return threadTs;
|
|
76
66
|
}
|
|
77
67
|
function readSlackBlocksParam(params) {
|
|
78
68
|
return slackActionRuntime.parseSlackBlocksInput(params.blocks);
|
|
@@ -116,7 +106,7 @@ async function isSlackDmTargetConfigured(params) {
|
|
|
116
106
|
}
|
|
117
107
|
function isCurrentSlackReadTarget(params) {
|
|
118
108
|
const requesterAccountId = params.context?.requesterAccountId?.trim();
|
|
119
|
-
return Boolean(normalizeOptionalLowercaseString(params.context?.currentChannelProvider) === "slack" && requesterAccountId && normalizeAccountId(requesterAccountId) === normalizeAccountId(params.account.accountId) && params.context && slackContextTargetsMatch(params.
|
|
109
|
+
return Boolean(normalizeOptionalLowercaseString(params.context?.currentChannelProvider) === "slack" && requesterAccountId && normalizeAccountId(requesterAccountId) === normalizeAccountId(params.account.accountId) && params.context && slackContextTargetsMatch(params.target, params.context));
|
|
120
110
|
}
|
|
121
111
|
function assertSlackMemberInfoAllowed(params) {
|
|
122
112
|
if (params.context?.conversationReadOrigin === "direct-operator") return;
|
|
@@ -128,6 +118,8 @@ function resolveSlackChannelReadPolicy(params) {
|
|
|
128
118
|
const channels = params.account.config.channels;
|
|
129
119
|
const channelKeys = Object.keys(channels ?? {});
|
|
130
120
|
const channelConfig = resolveSlackChannelConfig({
|
|
121
|
+
teamId: params.teamId,
|
|
122
|
+
allowUnscoped: getSlackInstallationKind(params.account.accountId) !== "enterprise",
|
|
131
123
|
channelId: params.channelId,
|
|
132
124
|
channelName: params.channelName,
|
|
133
125
|
channels,
|
|
@@ -163,7 +155,7 @@ function resolveSlackChannelReadPolicy(params) {
|
|
|
163
155
|
return {
|
|
164
156
|
channelAllowed: baseChannelAllowed,
|
|
165
157
|
channelExplicitlyDisabled,
|
|
166
|
-
groupDmAllowed: params.account.config.dm?.enabled !== false && params.account.config.dm?.groupEnabled === true && (params.currentConversation || isSlackGroupDmTargetConfigured(params.account, params.channelId)),
|
|
158
|
+
groupDmAllowed: params.account.config.dm?.enabled !== false && params.account.config.dm?.groupEnabled === true && (params.currentConversation || isSlackGroupDmTargetConfigured(params.account, params.channelId, params.teamId)),
|
|
167
159
|
shouldResolveName
|
|
168
160
|
};
|
|
169
161
|
}
|
|
@@ -173,7 +165,11 @@ async function assertSlackReadTargetAllowed(params) {
|
|
|
173
165
|
};
|
|
174
166
|
const currentConversation = isCurrentSlackReadTarget({
|
|
175
167
|
account: params.account,
|
|
176
|
-
|
|
168
|
+
target: formatSlackTarget({
|
|
169
|
+
teamId: params.teamId,
|
|
170
|
+
kind: "channel",
|
|
171
|
+
id: params.channelId
|
|
172
|
+
}),
|
|
177
173
|
context: params.context
|
|
178
174
|
});
|
|
179
175
|
const directOperator = params.conversationReadOrigin === "direct-operator";
|
|
@@ -184,6 +180,7 @@ async function assertSlackReadTargetAllowed(params) {
|
|
|
184
180
|
cfg: params.cfg,
|
|
185
181
|
accountId: params.account.accountId,
|
|
186
182
|
channelId: params.channelId,
|
|
183
|
+
teamId: params.teamId,
|
|
187
184
|
operation: "read"
|
|
188
185
|
});
|
|
189
186
|
if (info.type !== "dm" || !await isSlackDmTargetConfigured({
|
|
@@ -205,6 +202,7 @@ async function assertSlackReadTargetAllowed(params) {
|
|
|
205
202
|
cfg: params.cfg,
|
|
206
203
|
accountId: params.account.accountId,
|
|
207
204
|
channelId: params.channelId,
|
|
205
|
+
teamId: params.teamId,
|
|
208
206
|
operation: "read",
|
|
209
207
|
...preliminary.shouldResolveName ? { requireFreshName: true } : {}
|
|
210
208
|
});
|
|
@@ -225,17 +223,53 @@ async function assertSlackReadTargetAllowed(params) {
|
|
|
225
223
|
}
|
|
226
224
|
if (!(info.type === "channel" ? resolved.channelAllowed : info.type === "group" ? resolved.groupDmAllowed : resolved.channelAllowed && resolved.groupDmAllowed)) deny();
|
|
227
225
|
}
|
|
228
|
-
function isSlackGroupDmTargetConfigured(account, channelId) {
|
|
226
|
+
function isSlackGroupDmTargetConfigured(account, channelId, teamId) {
|
|
229
227
|
const entries = account.config.dm?.groupChannels ?? [];
|
|
230
228
|
if (entries.length === 0) return true;
|
|
229
|
+
const candidates = new Set(buildSlackChannelIdCandidates(channelId, teamId, { allowUnscoped: getSlackInstallationKind(account.accountId) !== "enterprise" }).map((candidate) => candidate.toLowerCase()));
|
|
231
230
|
const target = channelId.trim().toLowerCase();
|
|
232
231
|
return entries.some((entry) => {
|
|
233
232
|
const candidate = String(entry).trim().toLowerCase();
|
|
234
|
-
return candidate === "*" || candidate === target || candidate === `slack:${target}` || candidate === `channel:${target}` || candidate === `group:${target}` || candidate === `mpim:${target}`;
|
|
233
|
+
return candidate === "*" || candidates.has(candidate) || candidate === target || candidate === `slack:${target}` || candidate === `channel:${target}` || candidate === `group:${target}` || candidate === `mpim:${target}`;
|
|
235
234
|
});
|
|
236
235
|
}
|
|
236
|
+
function resolveTrustedCurrentSlackTeamId(params) {
|
|
237
|
+
const requesterAccountId = params.context?.requesterAccountId?.trim();
|
|
238
|
+
if (normalizeOptionalLowercaseString(params.context?.currentChannelProvider) !== "slack" || !requesterAccountId || normalizeAccountId(requesterAccountId) !== normalizeAccountId(params.account.accountId)) return;
|
|
239
|
+
const matchingTeams = /* @__PURE__ */ new Map();
|
|
240
|
+
for (const raw of [params.context?.currentChannelId, params.context?.currentMessagingTarget]) {
|
|
241
|
+
if (!raw) continue;
|
|
242
|
+
const current = parseSlackTarget(raw);
|
|
243
|
+
if (current?.teamId && (!params.target || current.kind === params.target.kind && current.id.toLowerCase() === params.target.id.toLowerCase())) matchingTeams.set(current.teamId.toLowerCase(), current.teamId);
|
|
244
|
+
}
|
|
245
|
+
return matchingTeams.size === 1 ? matchingTeams.values().next().value : void 0;
|
|
246
|
+
}
|
|
247
|
+
function resolveSlackActionTarget(account, raw, context) {
|
|
248
|
+
const parsed = parseSlackTarget(raw, { defaultKind: "channel" });
|
|
249
|
+
if (!parsed) throw new Error("Slack target is required.");
|
|
250
|
+
const teamId = parsed.teamId ?? resolveTrustedCurrentSlackTeamId({
|
|
251
|
+
account,
|
|
252
|
+
target: parsed,
|
|
253
|
+
context
|
|
254
|
+
});
|
|
255
|
+
assertSlackDetachedTargetAllowed(account.accountId, teamId);
|
|
256
|
+
return {
|
|
257
|
+
routingTarget: teamId ? formatSlackTarget({
|
|
258
|
+
teamId,
|
|
259
|
+
kind: parsed.kind,
|
|
260
|
+
id: parsed.id
|
|
261
|
+
}) : raw,
|
|
262
|
+
teamId
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function resolveSlackActionChannelTarget(account, raw, context) {
|
|
266
|
+
const resolved = resolveSlackActionTarget(account, raw, context);
|
|
267
|
+
return {
|
|
268
|
+
channelId: resolveSlackChannelId(raw),
|
|
269
|
+
teamId: resolved.teamId
|
|
270
|
+
};
|
|
271
|
+
}
|
|
237
272
|
async function handleSlackAction(params, cfg, context) {
|
|
238
|
-
const resolveChannelId = () => resolveSlackChannelId(readStringParam(params, "channelId", { required: true }));
|
|
239
273
|
const action = readStringParam(params, "action", { required: true });
|
|
240
274
|
const accountId = readStringParam(params, "accountId");
|
|
241
275
|
const { resolveSlackAccount, resolveSlackOperationToken } = await loadSlackAccountsRuntime();
|
|
@@ -243,38 +277,42 @@ async function handleSlackAction(params, cfg, context) {
|
|
|
243
277
|
cfg,
|
|
244
278
|
accountId
|
|
245
279
|
});
|
|
246
|
-
|
|
280
|
+
const resolveChannelTarget = () => resolveSlackActionChannelTarget(account, readStringParam(params, "channelId", { required: true }), context);
|
|
247
281
|
const isActionEnabled = createActionGate(account.actions ?? cfg.channels?.slack?.actions);
|
|
248
282
|
const botToken = account.botToken?.trim();
|
|
249
|
-
const buildActionOpts = (operation) => {
|
|
283
|
+
const buildActionOpts = (operation, teamId) => {
|
|
250
284
|
const token = resolveSlackOperationToken(account, operation);
|
|
251
285
|
if (!token && account.identity === "user") throw new Error(`Slack operation token missing for account "${account.accountId}".`);
|
|
252
286
|
const tokenOverride = token && token !== botToken ? token : void 0;
|
|
253
287
|
return {
|
|
254
288
|
cfg,
|
|
255
289
|
...accountId ? { accountId } : {},
|
|
256
|
-
...tokenOverride ? { token: tokenOverride } : {}
|
|
290
|
+
...tokenOverride ? { token: tokenOverride } : {},
|
|
291
|
+
teamId
|
|
257
292
|
};
|
|
258
293
|
};
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
294
|
+
const assertReadTargetAllowed = async (target) => {
|
|
295
|
+
await assertSlackReadTargetAllowed({
|
|
296
|
+
account,
|
|
297
|
+
cfg,
|
|
298
|
+
channelId: target.channelId,
|
|
299
|
+
teamId: target.teamId,
|
|
300
|
+
conversationReadOrigin: context?.conversationReadOrigin,
|
|
301
|
+
context
|
|
302
|
+
});
|
|
303
|
+
};
|
|
268
304
|
if (reactionsActions.has(action)) {
|
|
269
305
|
if (!isActionEnabled("reactions")) throw new Error("Slack reactions are disabled.");
|
|
270
|
-
const
|
|
306
|
+
const target = resolveChannelTarget();
|
|
307
|
+
const { channelId } = target;
|
|
308
|
+
const readOpts = buildActionOpts("read", target.teamId);
|
|
309
|
+
const writeOpts = buildActionOpts("write", target.teamId);
|
|
271
310
|
const messageId = readStringParam(params, "messageId", { required: true });
|
|
272
311
|
if (action === "react") {
|
|
273
312
|
const { emoji, remove, isEmpty } = readReactionParams(params, { removeErrorMessage: "Emoji is required to remove a Slack reaction." });
|
|
274
|
-
await assertReadTargetAllowed(
|
|
313
|
+
await assertReadTargetAllowed(target);
|
|
275
314
|
if (remove) {
|
|
276
|
-
|
|
277
|
-
else await slackActionRuntime.removeSlackReaction(channelId, messageId, emoji);
|
|
315
|
+
await slackActionRuntime.removeSlackReaction(channelId, messageId, emoji, writeOpts);
|
|
278
316
|
return jsonResult({
|
|
279
317
|
ok: true,
|
|
280
318
|
removed: emoji
|
|
@@ -282,44 +320,58 @@ async function handleSlackAction(params, cfg, context) {
|
|
|
282
320
|
}
|
|
283
321
|
if (isEmpty) return jsonResult({
|
|
284
322
|
ok: true,
|
|
285
|
-
removed:
|
|
323
|
+
removed: await slackActionRuntime.removeOwnSlackReactions(channelId, messageId, writeOpts)
|
|
286
324
|
});
|
|
287
|
-
|
|
288
|
-
else await slackActionRuntime.reactSlackMessage(channelId, messageId, emoji);
|
|
325
|
+
await slackActionRuntime.reactSlackMessage(channelId, messageId, emoji, writeOpts);
|
|
289
326
|
return jsonResult({
|
|
290
327
|
ok: true,
|
|
291
328
|
added: emoji
|
|
292
329
|
});
|
|
293
330
|
}
|
|
294
|
-
await assertReadTargetAllowed(
|
|
331
|
+
await assertReadTargetAllowed(target);
|
|
332
|
+
const limit = Math.min(readPositiveIntegerParam(params, "limit", { message: "limit must be a positive integer." }) ?? SLACK_REACTION_USER_LIMIT, SLACK_REACTION_USER_LIMIT);
|
|
295
333
|
return jsonResult({
|
|
296
334
|
ok: true,
|
|
297
|
-
reactions:
|
|
335
|
+
reactions: (await slackActionRuntime.listSlackReactions(channelId, messageId, readOpts))?.map((reaction) => reaction.users ? Object.assign({}, reaction, { users: reaction.users.slice(0, limit) }) : reaction)
|
|
298
336
|
});
|
|
299
337
|
}
|
|
300
338
|
if (messagingActions.has(action)) {
|
|
301
339
|
if (!isActionEnabled("messages")) throw new Error("Slack messages are disabled.");
|
|
340
|
+
const sendSlackMessage = async (target, content, options) => {
|
|
341
|
+
const replyReference = context?.hasRepliedRef && slackContextTargetsMatch(target, context) ? context.hasRepliedRef : void 0;
|
|
342
|
+
const result = await slackActionRuntime.sendSlackMessage(target, content, {
|
|
343
|
+
...options,
|
|
344
|
+
...replyReference ? { [SLACK_PRIVATE_ACTION_DELIVERY_RESULT]: () => {
|
|
345
|
+
replyReference.value = true;
|
|
346
|
+
} } : {}
|
|
347
|
+
});
|
|
348
|
+
if (replyReference) replyReference.value = true;
|
|
349
|
+
return result;
|
|
350
|
+
};
|
|
302
351
|
switch (action) {
|
|
303
352
|
case "sendMessage": {
|
|
304
|
-
const
|
|
353
|
+
const destination = resolveSlackActionTarget(account, readStringParam(params, "to", { required: true }), context).routingTarget;
|
|
305
354
|
const content = readStringParam(params, "content", { allowEmpty: true });
|
|
306
355
|
const mediaUrl = readStringParam(params, "mediaUrl");
|
|
307
356
|
const blocks = readSlackBlocksParam(params);
|
|
308
357
|
const replyBroadcast = readBooleanParam(params, "replyBroadcast");
|
|
309
358
|
const textIsSlackMrkdwn = readBooleanParam(params, "textIsSlackMrkdwn");
|
|
310
359
|
const textIsSlackPlainText = readBooleanParam(params, "textIsSlackPlainText");
|
|
360
|
+
const forceDocument = readBooleanParam(params, "forceDocument") === true;
|
|
311
361
|
const preparedMessages = context?.preparedMessages;
|
|
312
362
|
const authoredTextPlacement = readStringParam(params, "authoredTextPlacement");
|
|
313
363
|
if (authoredTextPlacement && authoredTextPlacement !== "none" && authoredTextPlacement !== "blocks" && authoredTextPlacement !== "outside-blocks") throw new Error("Slack authoredTextPlacement is invalid.");
|
|
314
364
|
const nativeDataFallbackBaseText = readStringParam(params, "nativeDataFallbackBaseText", { allowEmpty: true });
|
|
315
365
|
if (!content && !mediaUrl && !blocks && !preparedMessages?.length) throw new Error("Slack sendMessage requires content, blocks, or mediaUrl.");
|
|
316
366
|
if (replyBroadcast && mediaUrl) throw new Error("Slack replyBroadcast is only supported for text or block thread replies.");
|
|
317
|
-
const threadTs = resolveThreadTsFromContext(readStringParam(params, "threadTs"),
|
|
367
|
+
const threadTs = resolveThreadTsFromContext(readStringParam(params, "threadTs"), destination, context, { suppressImplicitThread: params.topLevel === true || params.threadTs === null });
|
|
318
368
|
const baseSendOpts = {
|
|
319
|
-
...
|
|
369
|
+
...buildActionOpts("write"),
|
|
370
|
+
mediaAccess: context?.mediaAccess,
|
|
320
371
|
mediaLocalRoots: context?.mediaLocalRoots,
|
|
321
372
|
mediaReadFile: context?.mediaReadFile,
|
|
322
|
-
threadTs: threadTs ?? void 0
|
|
373
|
+
threadTs: threadTs ?? void 0,
|
|
374
|
+
...forceDocument ? { forceDocument: true } : {}
|
|
323
375
|
};
|
|
324
376
|
const sendOpts = {
|
|
325
377
|
...baseSendOpts,
|
|
@@ -333,52 +385,50 @@ async function handleSlackAction(params, cfg, context) {
|
|
|
333
385
|
const shouldSplitLongContent = content && content.length > 8e3 && !hasSlackNativeDataBlock(blocks);
|
|
334
386
|
if (content && shouldSplitLongContent) {
|
|
335
387
|
const { replyBroadcast: _replyBroadcast, ...blockSendOpts } = sendOpts;
|
|
336
|
-
await
|
|
388
|
+
await sendSlackMessage(destination, "", {
|
|
337
389
|
...blockSendOpts,
|
|
338
390
|
blocks
|
|
339
391
|
});
|
|
340
|
-
return await
|
|
392
|
+
return await sendSlackMessage(destination, content, sendOpts);
|
|
341
393
|
}
|
|
342
|
-
return await
|
|
394
|
+
return await sendSlackMessage(destination, content ?? "", {
|
|
343
395
|
...sendOpts,
|
|
344
396
|
blocks
|
|
345
397
|
});
|
|
346
398
|
};
|
|
347
|
-
const result = preparedMessages?.length ? await (async () => {
|
|
348
|
-
let lastResult;
|
|
349
|
-
if (mediaUrl) lastResult = await slackActionRuntime.sendSlackMessage(to, "", {
|
|
350
|
-
...baseSendOpts,
|
|
351
|
-
mediaUrl
|
|
352
|
-
});
|
|
353
|
-
for (const [index, message] of preparedMessages.entries()) lastResult = await slackActionRuntime.sendSlackMessage(to, message.text, {
|
|
354
|
-
...baseSendOpts,
|
|
355
|
-
...index === 0 && replyBroadcast ? { replyBroadcast: true } : {},
|
|
356
|
-
...message.blocks ? { blocks: message.blocks } : {},
|
|
357
|
-
...message.authoredTextPlacement ? { authoredTextPlacement: message.authoredTextPlacement } : {},
|
|
358
|
-
...Object.hasOwn(message, "nativeDataFallbackBaseText") ? { nativeDataFallbackBaseText: message.nativeDataFallbackBaseText } : {},
|
|
359
|
-
...message.textIsSlackPlainText ? { textIsSlackPlainText: true } : {}
|
|
360
|
-
});
|
|
361
|
-
if (!lastResult) throw new Error("Slack prepared message plan produced no delivery.");
|
|
362
|
-
return lastResult;
|
|
363
|
-
})() : blocks ? await (async () => {
|
|
364
|
-
if (mediaUrl) await slackActionRuntime.sendSlackMessage(to, "", {
|
|
365
|
-
...sendOpts,
|
|
366
|
-
mediaUrl
|
|
367
|
-
});
|
|
368
|
-
return await sendContentAndBlocks();
|
|
369
|
-
})() : await slackActionRuntime.sendSlackMessage(to, content ?? "", {
|
|
370
|
-
...sendOpts,
|
|
371
|
-
mediaUrl: mediaUrl ?? void 0,
|
|
372
|
-
blocks
|
|
373
|
-
});
|
|
374
|
-
if (context?.hasRepliedRef && slackContextTargetsMatch(to, context)) context.hasRepliedRef.value = true;
|
|
375
399
|
return jsonResult({
|
|
376
400
|
ok: true,
|
|
377
|
-
result
|
|
401
|
+
result: preparedMessages?.length ? await (async () => {
|
|
402
|
+
let lastResult;
|
|
403
|
+
if (mediaUrl) lastResult = await sendSlackMessage(destination, "", {
|
|
404
|
+
...baseSendOpts,
|
|
405
|
+
mediaUrl
|
|
406
|
+
});
|
|
407
|
+
for (const [index, message] of preparedMessages.entries()) lastResult = await sendSlackMessage(destination, message.text, {
|
|
408
|
+
...baseSendOpts,
|
|
409
|
+
...index === 0 && replyBroadcast ? { replyBroadcast: true } : {},
|
|
410
|
+
...message.blocks ? { blocks: message.blocks } : {},
|
|
411
|
+
...message.authoredTextPlacement ? { authoredTextPlacement: message.authoredTextPlacement } : {},
|
|
412
|
+
...Object.hasOwn(message, "nativeDataFallbackBaseText") ? { nativeDataFallbackBaseText: message.nativeDataFallbackBaseText } : {},
|
|
413
|
+
...message.textIsSlackPlainText ? { textIsSlackPlainText: true } : {}
|
|
414
|
+
});
|
|
415
|
+
if (!lastResult) throw new Error("Slack prepared message plan produced no delivery.");
|
|
416
|
+
return lastResult;
|
|
417
|
+
})() : blocks ? await (async () => {
|
|
418
|
+
if (mediaUrl) await sendSlackMessage(destination, "", {
|
|
419
|
+
...sendOpts,
|
|
420
|
+
mediaUrl
|
|
421
|
+
});
|
|
422
|
+
return await sendContentAndBlocks();
|
|
423
|
+
})() : await sendSlackMessage(destination, content ?? "", {
|
|
424
|
+
...sendOpts,
|
|
425
|
+
mediaUrl: mediaUrl ?? void 0,
|
|
426
|
+
blocks
|
|
427
|
+
})
|
|
378
428
|
});
|
|
379
429
|
}
|
|
380
430
|
case "uploadFile": {
|
|
381
|
-
const
|
|
431
|
+
const destination = resolveSlackActionTarget(account, readStringParam(params, "to", { required: true }), context).routingTarget;
|
|
382
432
|
const filePath = readStringParam(params, "filePath", {
|
|
383
433
|
required: true,
|
|
384
434
|
trim: false
|
|
@@ -386,48 +436,53 @@ async function handleSlackAction(params, cfg, context) {
|
|
|
386
436
|
const initialComment = readStringParam(params, "initialComment", { allowEmpty: true });
|
|
387
437
|
const filename = readStringParam(params, "filename");
|
|
388
438
|
const title = readStringParam(params, "title");
|
|
439
|
+
const forceDocument = readBooleanParam(params, "forceDocument") === true;
|
|
389
440
|
if (readBooleanParam(params, "replyBroadcast")) throw new Error("Slack replyBroadcast is only supported for text or block thread replies.");
|
|
390
|
-
const threadTs = resolveThreadTsFromContext(readStringParam(params, "threadTs"),
|
|
391
|
-
const result = await slackActionRuntime.sendSlackMessage(to, initialComment ?? "", {
|
|
392
|
-
...writeOpts,
|
|
393
|
-
mediaUrl: filePath,
|
|
394
|
-
mediaLocalRoots: context?.mediaLocalRoots,
|
|
395
|
-
mediaReadFile: context?.mediaReadFile,
|
|
396
|
-
threadTs: threadTs ?? void 0,
|
|
397
|
-
...filename ? { uploadFileName: filename } : {},
|
|
398
|
-
...title ? { uploadTitle: title } : {}
|
|
399
|
-
});
|
|
400
|
-
if (context?.hasRepliedRef && slackContextTargetsMatch(to, context)) context.hasRepliedRef.value = true;
|
|
441
|
+
const threadTs = resolveThreadTsFromContext(readStringParam(params, "threadTs"), destination, context, { suppressImplicitThread: params.topLevel === true || params.threadTs === null });
|
|
401
442
|
return jsonResult({
|
|
402
443
|
ok: true,
|
|
403
|
-
result
|
|
444
|
+
result: await sendSlackMessage(destination, initialComment ?? "", {
|
|
445
|
+
...buildActionOpts("write"),
|
|
446
|
+
mediaUrl: filePath,
|
|
447
|
+
mediaAccess: context?.mediaAccess,
|
|
448
|
+
mediaLocalRoots: context?.mediaLocalRoots,
|
|
449
|
+
mediaReadFile: context?.mediaReadFile,
|
|
450
|
+
threadTs: threadTs ?? void 0,
|
|
451
|
+
...forceDocument ? { forceDocument: true } : {},
|
|
452
|
+
...filename ? { uploadFileName: filename } : {},
|
|
453
|
+
...title ? { uploadTitle: title } : {}
|
|
454
|
+
})
|
|
404
455
|
});
|
|
405
456
|
}
|
|
406
457
|
case "editMessage": {
|
|
407
|
-
const
|
|
458
|
+
const target = resolveChannelTarget();
|
|
459
|
+
const { channelId } = target;
|
|
408
460
|
const messageId = readStringParam(params, "messageId", { required: true });
|
|
409
461
|
const content = readStringParam(params, "content", { allowEmpty: true });
|
|
410
462
|
const blocks = readSlackBlocksParam(params);
|
|
411
463
|
if (!content && !blocks) throw new Error("Slack editMessage requires content or blocks.");
|
|
412
|
-
await assertReadTargetAllowed(
|
|
413
|
-
|
|
464
|
+
await assertReadTargetAllowed(target);
|
|
465
|
+
const writeOpts = buildActionOpts("write", target.teamId);
|
|
466
|
+
await slackActionRuntime.editSlackMessage(channelId, messageId, content ?? "", {
|
|
414
467
|
...writeOpts,
|
|
415
468
|
blocks
|
|
416
469
|
});
|
|
417
|
-
else await slackActionRuntime.editSlackMessage(channelId, messageId, content ?? "", { blocks });
|
|
418
470
|
return jsonResult({ ok: true });
|
|
419
471
|
}
|
|
420
472
|
case "deleteMessage": {
|
|
421
|
-
const
|
|
473
|
+
const target = resolveChannelTarget();
|
|
474
|
+
const { channelId } = target;
|
|
422
475
|
const messageId = readStringParam(params, "messageId", { required: true });
|
|
423
|
-
await assertReadTargetAllowed(
|
|
424
|
-
|
|
425
|
-
|
|
476
|
+
await assertReadTargetAllowed(target);
|
|
477
|
+
const writeOpts = buildActionOpts("write", target.teamId);
|
|
478
|
+
await slackActionRuntime.deleteSlackMessage(channelId, messageId, writeOpts);
|
|
426
479
|
return jsonResult({ ok: true });
|
|
427
480
|
}
|
|
428
481
|
case "readMessages": {
|
|
429
|
-
const
|
|
430
|
-
|
|
482
|
+
const target = resolveChannelTarget();
|
|
483
|
+
const { channelId } = target;
|
|
484
|
+
await assertReadTargetAllowed(target);
|
|
485
|
+
const readOpts = buildActionOpts("read", target.teamId);
|
|
431
486
|
const limit = readPositiveIntegerParam(params, "limit", { message: "limit must be a positive integer." });
|
|
432
487
|
const before = readStringParam(params, "before");
|
|
433
488
|
const after = readStringParam(params, "after");
|
|
@@ -454,14 +509,16 @@ async function handleSlackAction(params, cfg, context) {
|
|
|
454
509
|
const fileId = readStringParam(params, "fileId", { required: true });
|
|
455
510
|
const channelTarget = readStringParam(params, "channelId") ?? readStringParam(params, "to") ?? context?.currentChannelId;
|
|
456
511
|
if (!channelTarget) throw new Error("Slack file download requires channelId or to so the read target can be authorized.");
|
|
457
|
-
const
|
|
458
|
-
|
|
512
|
+
const target = resolveSlackActionChannelTarget(account, channelTarget, context);
|
|
513
|
+
const { channelId } = target;
|
|
514
|
+
await assertReadTargetAllowed(target);
|
|
459
515
|
const threadId = readStringParam(params, "threadId") ?? readStringParam(params, "replyTo");
|
|
460
516
|
const maxBytes = account.config?.mediaMaxMb ? account.config.mediaMaxMb * 1024 * 1024 : 20 * 1024 * 1024;
|
|
461
517
|
const readToken = resolveSlackOperationToken(account, "read");
|
|
518
|
+
const readOpts = buildActionOpts("read", target.teamId);
|
|
462
519
|
const downloaded = await slackActionRuntime.downloadSlackFile(fileId, {
|
|
463
520
|
...readOpts,
|
|
464
|
-
...readToken && !readOpts
|
|
521
|
+
...readToken && !readOpts.token ? { token: readToken } : {},
|
|
465
522
|
maxBytes,
|
|
466
523
|
channelId,
|
|
467
524
|
threadId: threadId ?? void 0
|
|
@@ -499,25 +556,26 @@ async function handleSlackAction(params, cfg, context) {
|
|
|
499
556
|
}
|
|
500
557
|
if (pinActions.has(action)) {
|
|
501
558
|
if (!isActionEnabled("pins")) throw new Error("Slack pins are disabled.");
|
|
502
|
-
const
|
|
559
|
+
const target = resolveChannelTarget();
|
|
560
|
+
const { channelId } = target;
|
|
561
|
+
const readOpts = buildActionOpts("read", target.teamId);
|
|
562
|
+
const writeOpts = buildActionOpts("write", target.teamId);
|
|
503
563
|
if (action === "pinMessage") {
|
|
504
564
|
const messageId = readStringParam(params, "messageId", { required: true });
|
|
505
|
-
await assertReadTargetAllowed(
|
|
506
|
-
|
|
507
|
-
else await slackActionRuntime.pinSlackMessage(channelId, messageId);
|
|
565
|
+
await assertReadTargetAllowed(target);
|
|
566
|
+
await slackActionRuntime.pinSlackMessage(channelId, messageId, writeOpts);
|
|
508
567
|
return jsonResult({ ok: true });
|
|
509
568
|
}
|
|
510
569
|
if (action === "unpinMessage") {
|
|
511
570
|
const messageId = readStringParam(params, "messageId", { required: true });
|
|
512
|
-
await assertReadTargetAllowed(
|
|
513
|
-
|
|
514
|
-
else await slackActionRuntime.unpinSlackMessage(channelId, messageId);
|
|
571
|
+
await assertReadTargetAllowed(target);
|
|
572
|
+
await slackActionRuntime.unpinSlackMessage(channelId, messageId, writeOpts);
|
|
515
573
|
return jsonResult({ ok: true });
|
|
516
574
|
}
|
|
517
|
-
await assertReadTargetAllowed(
|
|
575
|
+
await assertReadTargetAllowed(target);
|
|
518
576
|
return jsonResult({
|
|
519
577
|
ok: true,
|
|
520
|
-
pins: (
|
|
578
|
+
pins: (await slackActionRuntime.listSlackPins(channelId, readOpts)).map((pin) => {
|
|
521
579
|
const message = pin.message ? withNormalizedTimestamp(pin.message, pin.message.ts) : pin.message;
|
|
522
580
|
return message ? Object.assign({}, pin, { message }) : pin;
|
|
523
581
|
})
|
|
@@ -531,15 +589,25 @@ async function handleSlackAction(params, cfg, context) {
|
|
|
531
589
|
context,
|
|
532
590
|
userId
|
|
533
591
|
});
|
|
592
|
+
const teamId = resolveTrustedCurrentSlackTeamId({
|
|
593
|
+
account,
|
|
594
|
+
context
|
|
595
|
+
});
|
|
596
|
+
assertSlackDetachedTargetAllowed(account.accountId, teamId);
|
|
534
597
|
return jsonResult({
|
|
535
598
|
ok: true,
|
|
536
|
-
info:
|
|
599
|
+
info: await slackActionRuntime.getSlackMemberInfo(userId, buildActionOpts("read", teamId))
|
|
537
600
|
});
|
|
538
601
|
}
|
|
539
602
|
if (action === "emojiList") {
|
|
540
603
|
if (!isActionEnabled("emojiList")) throw new Error("Slack emoji list is disabled.");
|
|
541
604
|
const limit = readPositiveIntegerParam(params, "limit", { message: "limit must be a positive integer." });
|
|
542
|
-
const
|
|
605
|
+
const teamId = resolveTrustedCurrentSlackTeamId({
|
|
606
|
+
account,
|
|
607
|
+
context
|
|
608
|
+
});
|
|
609
|
+
assertSlackDetachedTargetAllowed(account.accountId, teamId);
|
|
610
|
+
const result = await slackActionRuntime.listSlackEmojis(buildActionOpts("read", teamId));
|
|
543
611
|
if (limit != null && limit > 0 && result.emoji != null) {
|
|
544
612
|
const entries = Object.entries(result.emoji).toSorted(([a], [b]) => a.localeCompare(b));
|
|
545
613
|
if (entries.length > limit) return jsonResult({
|