UNPKG

slack-edge

Version:

Slack app development framework for edge functions with streamlined TypeScript support

986 lines 49 kB
"use strict"; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _SlackApp_instances, _SlackApp_slashCommands, _SlackApp_events, _SlackApp_globalShorcuts, _SlackApp_messageShorcuts, _SlackApp_blockActions, _SlackApp_blockSuggestions, _SlackApp_viewSubmissions, _SlackApp_viewClosed, _SlackApp_appRateLimited, _SlackApp_assistantEnabled, _SlackApp_assistantEvent, _SlackApp_callAuthorize; Object.defineProperty(exports, "__esModule", { value: true }); exports.noopLazyHandler = exports.SlackApp = void 0; const slack_web_api_client_1 = require("slack-web-api-client"); const assistant_1 = require("./assistant/assistant"); const thread_context_store_1 = require("./assistant/thread-context-store"); const authorize_error_handler_1 = require("./authorization/authorize-error-handler"); const single_team_authorize_1 = require("./authorization/single-team-authorize"); const context_1 = require("./context/context"); const errors_1 = require("./errors"); const execution_context_1 = require("./execution-context"); const built_in_middleware_1 = require("./middleware/built-in-middleware"); const payload_types_1 = require("./request/payload-types"); const request_parser_1 = require("./request/request-parser"); const request_verification_1 = require("./request/request-verification"); const response_1 = require("./response/response"); const socket_mode_client_1 = require("./socket-mode/socket-mode-client"); const function_executed_event_1 = require("./utility/function-executed-event"); const message_events_1 = require("./utility/message-events"); /** * The class representing a Slack app process that handles requests from Slack's API server. */ class SlackApp { // -------------------------- constructor(options) { _SlackApp_instances.add(this); /** * The custom middleware that are called before authorize() function. */ // deno-lint-ignore no-explicit-any this.preAuthorizeMiddleware = [built_in_middleware_1.urlVerification]; /** * The custom middleware that are called after authorize() function. */ // deno-lint-ignore no-explicit-any this.postAuthorizeMiddleware = []; this.eventsToSkipAuthorize = ["app_uninstalled", "tokens_revoked"]; // -------------------------- // Enabled listener functions // -------------------------- _SlackApp_slashCommands.set(this, []); _SlackApp_events.set(this, []); _SlackApp_globalShorcuts.set(this, []); _SlackApp_messageShorcuts.set(this, []); _SlackApp_blockActions.set(this, []); _SlackApp_blockSuggestions.set(this, []); _SlackApp_viewSubmissions.set(this, []); _SlackApp_viewClosed.set(this, []); _SlackApp_appRateLimited.set(this, undefined); _SlackApp_assistantEnabled.set(this, void 0); if (options.env.SLACK_BOT_TOKEN === undefined && (options.authorize === undefined || options.authorize === single_team_authorize_1.singleTeamAuthorize)) { throw new errors_1.ConfigError("When you don't pass env.SLACK_BOT_TOKEN, your own authorize function, which supplies a valid token to use, needs to be passed instead."); } this.env = options.env; // Note that options.env.SLACK_BOT_TOKEN is absent for SlackOAuthApp this.client = new slack_web_api_client_1.SlackAPIClient(options.env.SLACK_BOT_TOKEN, { logLevel: this.env.SLACK_LOGGING_LEVEL, }); // Socket Mode settings this.appLevelToken = options.env.SLACK_APP_TOKEN; this.socketMode = options.socketMode ?? this.appLevelToken !== undefined; if (this.socketMode) { // Socket Mode does not require request signature verification // because the underlying WS connection are securely established. this.signingSecret = ""; } else { if (!this.env.SLACK_SIGNING_SECRET) { throw new errors_1.ConfigError("env.SLACK_SIGNING_SECRET is required to run your app on edge functions!"); } this.signingSecret = this.env.SLACK_SIGNING_SECRET; } this.startLazyListenerAfterAck = options.startLazyListenerAfterAck ?? false; this.ignoreSelfEvents = options.ignoreSelfEvents ?? true; if (this.ignoreSelfEvents) { const middleware = (0, built_in_middleware_1.ignoringSelfEvents)(options.ignoreSelfAssistantMessageEvents ?? true); this.postAuthorizeMiddleware.push(middleware); } this.authorize = options.authorize ?? single_team_authorize_1.singleTeamAuthorize; this.authorizeErrorHandler = options.authorizeErrorHandler ?? (0, authorize_error_handler_1.buildDefaultAuthorizeErrorHanlder)(); this.routes = { events: options.routes?.events }; this.assistantThreadContextStore = options.assistantThreadContextStore; __classPrivateFieldSet(this, _SlackApp_assistantEnabled, options.assistantThreadContextStore !== undefined, "f"); } /** * Registers a pre-authorize middleware. * @param middleware middleware * @returns this instance */ beforeAuthorize(middleware) { this.preAuthorizeMiddleware.push(middleware); return this; } /** * Registers a post-authorize middleware. This naming is for consistency with bolt-js. * @param middleware middleware * @returns this instance */ middleware(middleware) { return this.afterAuthorize(middleware); } /** * Registers a post-authorize middleware. This naming is for consistency with bolt-js. * @param middleware middleware * @returns this instance */ use(middleware) { return this.afterAuthorize(middleware); } /** * Registers a post-authorize middleware. * @param middleware middleware * @returns this instance */ afterAuthorize(middleware) { this.postAuthorizeMiddleware.push(middleware); return this; } /** * Registers a listener that handles slash command executions. * @param pattern the pattern to match slash command name * @param ack ack function that must complete within 3 seconds * @param lazy lazy function that can do anything asynchronously * @returns this instance */ command(pattern, ack, lazy = exports.noopLazyHandler) { const handler = { ack, lazy }; __classPrivateFieldGet(this, _SlackApp_slashCommands, "f").push((body) => { if (body.type || !body.command) { return null; } if (typeof pattern === "string" && body.command === pattern) { return handler; } else if (typeof pattern === "object" && pattern instanceof RegExp && body.command.match(pattern)) { return handler; } return null; }); return this; } /** * Registers a listener that handles custom function calls within Workflow Builder. * Please be aware that this feature is still in beta as of April 2024. * @param callbackId the pattern to match callback_id in a payload * @param lazy lazy function that can do anything asynchronously * @returns this instance */ function(callbackId, lazy) { __classPrivateFieldGet(this, _SlackApp_events, "f").push((body) => { if (body.type !== payload_types_1.PayloadType.EventsAPI || !body.event || body.event.type !== "function_executed") { return null; } if ((0, function_executed_event_1.isFunctionExecutedEvent)(body.event)) { let matched = true; if (callbackId !== undefined) { if (typeof callbackId === "string") { matched = body.event.function.callback_id.includes(callbackId); } if (typeof callbackId === "object") { matched = body.event.function.callback_id.match(callbackId) !== null; } } if (matched) { // deno-lint-ignore require-await return { ack: async (_) => "", lazy }; } } return null; }); return this; } /** * Registers a listener that handles Events API request. * @param event the pattern to match event type in a payload * @param lazy lazy function that can do anything asynchronously * @returns this instance */ event(event, lazy) { __classPrivateFieldGet(this, _SlackApp_events, "f").push((body) => { if (body.type !== payload_types_1.PayloadType.EventsAPI || !body.event) { return null; } if (body.event.type === event) { // deno-lint-ignore require-await return { ack: async () => "", lazy }; } return null; }); return this; } assistant(assistant) { __classPrivateFieldGet(this, _SlackApp_instances, "m", _SlackApp_assistantEvent).call(this, "assistant_thread_started", assistant.threadStartedHandler); __classPrivateFieldGet(this, _SlackApp_instances, "m", _SlackApp_assistantEvent).call(this, "assistant_thread_context_changed", assistant.threadContextChangedHandler); __classPrivateFieldGet(this, _SlackApp_instances, "m", _SlackApp_assistantEvent).call(this, "message", assistant.userMessageHandler, false); __classPrivateFieldGet(this, _SlackApp_instances, "m", _SlackApp_assistantEvent).call(this, "message", assistant.botMessageHandler, true); if (assistant.threadContextStore) { this.assistantThreadContextStore = assistant.threadContextStore; } return this; } assistantThreadStarted(lazy) { return __classPrivateFieldGet(this, _SlackApp_instances, "m", _SlackApp_assistantEvent).call(this, "assistant_thread_started", new assistant_1.Assistant({ threadContextStore: this.assistantThreadContextStore, threadStarted: lazy, }).threadStartedHandler); } assistantThreadContextChanged(lazy) { return __classPrivateFieldGet(this, _SlackApp_instances, "m", _SlackApp_assistantEvent).call(this, "assistant_thread_context_changed", new assistant_1.Assistant({ threadContextStore: this.assistantThreadContextStore, threadContextChanged: lazy, }).threadContextChangedHandler); } assistantUserMessage(lazy) { return __classPrivateFieldGet(this, _SlackApp_instances, "m", _SlackApp_assistantEvent).call(this, "message", new assistant_1.Assistant({ threadContextStore: this.assistantThreadContextStore, userMessage: lazy, }).userMessageHandler, false); } assistantBotMessage(lazy) { return __classPrivateFieldGet(this, _SlackApp_instances, "m", _SlackApp_assistantEvent).call(this, "message", new assistant_1.Assistant({ threadContextStore: this.assistantThreadContextStore, botMessage: lazy, }).botMessageHandler, true); } /** * Registers a listener that handles all newly posted message events. * @param lazy lazy function that can do anything asynchronously * @returns this instance */ anyMessage(lazy) { return this.message(undefined, lazy); } /** * Registers a listener that handles newly posted message events that matches the pattern. * @param pattern the pattern to match a message event's text * @param lazy lazy function that can do anything asynchronously * @returns this instance */ message(pattern, lazy) { __classPrivateFieldGet(this, _SlackApp_events, "f").push((body) => { if (body.type !== payload_types_1.PayloadType.EventsAPI || !body.event || body.event.type !== "message") { return null; } if ((0, message_events_1.isPostedMessageEvent)(body.event)) { let matched = true; if (pattern !== undefined) { if (typeof pattern === "string") { matched = body.event.text.includes(pattern); } if (typeof pattern === "object") { matched = body.event.text.match(pattern) !== null; } } if (matched) { // deno-lint-ignore require-await return { ack: async (_) => "", lazy }; } } return null; }); return this; } /** * Registers a listener that handles global/message shortcut executions. * @param callbackId the pattern to match callback_id in a payload * @param ack ack function that must complete within 3 seconds * @param lazy lazy function that can do anything asynchronously * @returns this instance */ shortcut(callbackId, ack, lazy = exports.noopLazyHandler) { return this.globalShortcut(callbackId, ack, lazy).messageShortcut(callbackId, ack, lazy); } /** * Registers a listener that handles global shortcut executions. * @param callbackId the pattern to match callback_id in a payload * @param ack ack function that must complete within 3 seconds * @param lazy lazy function that can do anything asynchronously * @returns this instance */ globalShortcut(callbackId, ack, lazy = exports.noopLazyHandler) { const handler = { ack, lazy }; __classPrivateFieldGet(this, _SlackApp_globalShorcuts, "f").push((body) => { if (body.type !== payload_types_1.PayloadType.GlobalShortcut || !body.callback_id) { return null; } if (typeof callbackId === "string" && body.callback_id === callbackId) { return handler; } else if (typeof callbackId === "object" && callbackId instanceof RegExp && body.callback_id.match(callbackId)) { return handler; } return null; }); return this; } /** * Registers a listener that handles message shortcut executions. * @param callbackId the pattern to match callback_id in a payload * @param ack ack function that must complete within 3 seconds * @param lazy lazy function that can do anything asynchronously * @returns this instance */ messageShortcut(callbackId, ack, lazy = exports.noopLazyHandler) { const handler = { ack, lazy }; __classPrivateFieldGet(this, _SlackApp_messageShorcuts, "f").push((body) => { if (body.type !== payload_types_1.PayloadType.MessageShortcut || !body.callback_id) { return null; } if (typeof callbackId === "string" && body.callback_id === callbackId) { return handler; } else if (typeof callbackId === "object" && callbackId instanceof RegExp && body.callback_id.match(callbackId)) { return handler; } return null; }); return this; } /** * Registers a listener that handles type: "block_actions" requests. * @param constraints the constraints to match block_id/action_id in a payload * @param ack ack function that must complete within 3 seconds * @param lazy lazy function that can do anything asynchronously * @returns this instance */ action(constraints, ack, lazy = exports.noopLazyHandler) { const handler = { ack, lazy }; __classPrivateFieldGet(this, _SlackApp_blockActions, "f").push((body) => { if (body.type !== payload_types_1.PayloadType.BlockAction || !body.actions || !body.actions[0]) { return null; } const action = body.actions[0]; if (typeof constraints === "string" && action.action_id === constraints) { return handler; } else if (typeof constraints === "object") { if (constraints instanceof RegExp) { if (action.action_id.match(constraints)) { return handler; } } else if (constraints.type) { if (action.type === constraints.type) { if (action.action_id === constraints.action_id) { if (constraints.block_id && action.block_id !== constraints.block_id) { return null; } return handler; } } } } return null; }); return this; } /** * Registers a listener that handles type: "block_suggestion" requests. * Note that your app must return the options/option_groups within 3 seconds, * so slack-edge intentionally does not accept lazy here. * @param constraints the constraints to match block_id/action_id in a payload * @param ack ack function that must complete within 3 seconds * @returns this instance */ options(constraints, ack) { // Note that block_suggestion response must be done within 3 seconds. // So, we don't support the lazy handler for it. const handler = { ack }; __classPrivateFieldGet(this, _SlackApp_blockSuggestions, "f").push((body) => { if (body.type !== payload_types_1.PayloadType.BlockSuggestion || !body.action_id) { return null; } if (typeof constraints === "string" && body.action_id === constraints) { return handler; } else if (typeof constraints === "object") { if (constraints instanceof RegExp) { if (body.action_id.match(constraints)) { return handler; } } else { if (body.action_id === constraints.action_id) { if (body.block_id && body.block_id !== constraints.block_id) { return null; } return handler; } } } return null; }); return this; } /** * Registers a listener that handles type: "view_submission"/"view_closed" requests. * @param callbackId the constraints to match callback_id in a payload * @param ack ack function that must complete within 3 seconds * @param lazy lazy function that can do anything asynchronously * @returns this instance */ view(callbackId, ack, lazy = exports.noopLazyHandler) { return this.viewSubmission(callbackId, ack, lazy).viewClosed(callbackId, ack, lazy); } /** * Registers a listener that handles type: "view_submission" requests. * @param callbackId the constraints to match callback_id in a payload * @param ack ack function that must complete within 3 seconds * @param lazy lazy function that can do anything asynchronously * @returns this instance */ viewSubmission(callbackId, ack, lazy = exports.noopLazyHandler) { const handler = { ack, lazy }; __classPrivateFieldGet(this, _SlackApp_viewSubmissions, "f").push((body) => { if (body.type !== payload_types_1.PayloadType.ViewSubmission || !body.view) { return null; } if (typeof callbackId === "string" && body.view.callback_id === callbackId) { return handler; } else if (typeof callbackId === "object" && callbackId instanceof RegExp && body.view.callback_id.match(callbackId)) { return handler; } return null; }); return this; } /** * Registers a listener that handles type: "view_closed" requests. * @param callbackId the constraints to match callback_id in a payload * @param ack ack function that must complete within 3 seconds * @param lazy lazy function that can do anything asynchronously * @returns this instance */ viewClosed(callbackId, ack, lazy = exports.noopLazyHandler) { const handler = { ack, lazy }; __classPrivateFieldGet(this, _SlackApp_viewClosed, "f").push((body) => { if (body.type !== payload_types_1.PayloadType.ViewClosed || !body.view) { return null; } if (typeof callbackId === "string" && body.view.callback_id === callbackId) { return handler; } else if (typeof callbackId === "object" && callbackId instanceof RegExp && body.view.callback_id.match(callbackId)) { return handler; } return null; }); return this; } /** * Registers a single listener that handles type: "app_rate_limited" requests. * @param lazy lazy function that can do anything asynchronously * @returns this instance */ appRateLimited(lazy) { __classPrivateFieldSet(this, _SlackApp_appRateLimited, (body) => { if (body.type !== payload_types_1.PayloadType.AppRateLimited) { return null; } // deno-lint-ignore require-await return { ack: async () => "", lazy }; }, "f"); return this; } /** * Handles an http request and returns a response to it. * @param request request * @param ctx execution context * @returns response */ async run(request, ctx = new execution_context_1.NoopExecutionContext()) { return await this.handleEventRequest(request, ctx); } /** * Establishes a WebSocket connection for Socket Mode. */ async connect() { if (!this.socketMode) { throw new errors_1.ConfigError("Both env.SLACK_APP_TOKEN and socketMode: true are required to start a Socket Mode connection!"); } this.socketModeClient = new socket_mode_client_1.SocketModeClient(this); await this.socketModeClient.connect(); } /** * Disconnect a WebSocket connection for Socket Mode. */ async disconnect() { if (this.socketModeClient) { await this.socketModeClient.disconnect(); } } /** * Handles an HTTP request from Slack's API server and returns a response to it. * @param request request * @param ctx execution context * @returns response */ async handleEventRequest(request, ctx) { // If the routes.events is missing, any URLs can work for handing requests from Slack if (this.routes.events) { const { pathname } = new URL(request.url); if (pathname !== this.routes.events) { return new Response("Not found", { status: 404 }); } } // To avoid the following warning by Cloudflware, parse the body as Blob first // Called .text() on an HTTP body which does not appear to be text .. const blobRequestBody = await request.blob(); // We can safely assume the incoming request body is always text data const rawBody = await blobRequestBody.text(); // For Request URL verification if (rawBody.includes("ssl_check=")) { // Slack does not send the x-slack-signature header for this pattern. // Thus, we need to check the pattern before verifying a request. const bodyParams = new URLSearchParams(rawBody); if (bodyParams.get("ssl_check") === "1" && bodyParams.get("token")) { return new Response("", { status: 200 }); } } // Verify the request headers and body const isRequestSignatureVerified = this.socketMode || (await (0, request_verification_1.verifySlackRequest)(this.signingSecret, request.headers, rawBody)); if (isRequestSignatureVerified) { // deno-lint-ignore no-explicit-any const body = await (0, request_parser_1.parseRequestBody)(request.headers, rawBody); let retryNum = undefined; try { const retryNumHeader = request.headers.get("x-slack-retry-num"); if (retryNumHeader) { retryNum = Number.parseInt(retryNumHeader); } else if (this.socketMode && body.retry_attempt) { retryNum = Number.parseInt(body.retry_attempt); } // deno-lint-ignore no-unused-vars } catch (e) { // Ignore an exception here } const retryReason = request.headers.get("x-slack-retry-reason") ?? body.retry_reason; const preAuthorizeRequest = { body, rawBody, retryNum, retryReason, context: (0, context_1.builtBaseContext)(body), env: this.env, headers: request.headers, }; if ((0, slack_web_api_client_1.isDebugLogEnabled)(this.env.SLACK_LOGGING_LEVEL)) { console.log(`*** Received request body ***\n ${(0, slack_web_api_client_1.prettyPrint)(body)}`); } for (const middlware of this.preAuthorizeMiddleware) { const response = await middlware(preAuthorizeRequest); if (response) { return (0, response_1.toCompleteResponse)(response); } } let authorizeResult; try { authorizeResult = await __classPrivateFieldGet(this, _SlackApp_instances, "m", _SlackApp_callAuthorize).call(this, preAuthorizeRequest); } catch ( // deno-lint-ignore no-explicit-any error) { if ("name" in error && error.name === "AuthorizeError") { const responseOrError = await this.authorizeErrorHandler({ request: preAuthorizeRequest, error, }); if ("name" in responseOrError && responseOrError.name === "AuthorizeError") { throw responseOrError; // AuthorizeError } else { return responseOrError; // Response } } else { // The authorize() function should not throw any other exceptions than AuthorizeError throw error; } } const primaryToken = preAuthorizeRequest.context.functionBotAccessToken || authorizeResult.botToken; const authorizedContext = { ...preAuthorizeRequest.context, authorizeResult, client: new slack_web_api_client_1.SlackAPIClient(primaryToken, { logLevel: this.env.SLACK_LOGGING_LEVEL, }), botToken: authorizeResult.botToken, botId: authorizeResult.botId, botUserId: authorizeResult.botUserId, userToken: authorizeResult.userToken, }; if (authorizedContext.channelId) { const context = authorizedContext; const primaryToken = context.functionBotAccessToken || context.botToken; const client = new slack_web_api_client_1.SlackAPIClient(primaryToken); if (__classPrivateFieldGet(this, _SlackApp_assistantEnabled, "f") && authorizedContext.isAssistantThreadEvent) { const assistantContext = authorizedContext; const { channelId: channel_id, threadTs: thread_ts } = assistantContext; // setStatus assistantContext.setStatus = async ({ status }) => await client.assistant.threads.setStatus({ channel_id, thread_ts, status }); // setTitle assistantContext.setTitle = async ({ title }) => await client.assistant.threads.setTitle({ channel_id, thread_ts, title }); // setSuggestedPrompts assistantContext.setSuggestedPrompts = async ({ title, prompts }) => { const promptsArgs = []; for (const p of prompts) { if (typeof p === "string") { promptsArgs.push({ message: p, title: p }); } else { promptsArgs.push(p); } } return await client.assistant.threads.setSuggestedPrompts({ channel_id, thread_ts, prompts: promptsArgs, title }); }; // threadContextStore const threadContextStore = this.assistantThreadContextStore ?? new thread_context_store_1.DefaultAssistantThreadContextStore({ client, thisBotUserId: context.botUserId, }); assistantContext.threadContextStore = threadContextStore; // saveThreadContextStore assistantContext.saveThreadContextStore = async (newContext) => { await threadContextStore.save({ channel_id, thread_ts }, newContext); }; // threadContext const threadContext = (await threadContextStore.find({ channel_id, thread_ts })) || (body.event.assistant_thread?.context && Object.keys(body.event.assistant_thread.context).length > 0 ? body.event.assistant_thread?.context : undefined); if (threadContext) { assistantContext.threadContext = threadContext; } // say context.say = async (params) => await client.chat.postMessage({ channel: channel_id, thread_ts, metadata: threadContext ? { event_type: "assistant_thread_context", event_payload: { ...threadContext }, } : undefined, ...params, }); } else { context.say = async (params) => await client.chat.postMessage({ channel: context.channelId, thread_ts: context.threadTs, // for assistant apps ...params, }); } } if (authorizedContext.responseUrl) { const responseUrl = authorizedContext.responseUrl; // deno-lint-ignore require-await authorizedContext.respond = async (params) => { return new slack_web_api_client_1.ResponseUrlSender(responseUrl).call(params); }; } const baseRequest = { ...preAuthorizeRequest, context: authorizedContext, }; for (const middlware of this.postAuthorizeMiddleware) { const response = await middlware(baseRequest); if (response) { return (0, response_1.toCompleteResponse)(response); } } const payload = body; if (body.type === payload_types_1.PayloadType.EventsAPI) { // Events API const slackRequest = { payload: body.event, ...baseRequest, }; // Collect all matching handlers to run them all // This ensures both built-in handlers (e.g., token revocation) and user handlers are invoked const matchedHandlers = []; for (const matcher of __classPrivateFieldGet(this, _SlackApp_events, "f")) { const handler = matcher(payload); if (handler) { matchedHandlers.push(handler); } } if (matchedHandlers.length > 0) { // Run all lazy handlers before ack (if configured) if (!this.startLazyListenerAfterAck) { for (const handler of matchedHandlers) { ctx.waitUntil(handler.lazy(slackRequest)); } } // Run all ack handlers and log each response for (const handler of matchedHandlers) { const slackResponse = await handler.ack(slackRequest); if ((0, slack_web_api_client_1.isDebugLogEnabled)(this.env.SLACK_LOGGING_LEVEL)) { console.log(`*** Slack response ***\n${(0, slack_web_api_client_1.prettyPrint)(slackResponse)}`); } } // Run all lazy handlers after ack (if configured) if (this.startLazyListenerAfterAck) { for (const handler of matchedHandlers) { ctx.waitUntil(handler.lazy(slackRequest)); } } return (0, response_1.toCompleteResponse)(""); } if (payload.event?.type === "assistant_thread_context_changed") { // When a developer does not register their customer listener for this event, // SlackApp automatically calls the built-in one for ease of development. const handler = new assistant_1.Assistant({ threadContextStore: this.assistantThreadContextStore }).threadContextChangedHandler; if (!this.startLazyListenerAfterAck) { const req = slackRequest; ctx.waitUntil(handler(req)); return (0, response_1.toCompleteResponse)(); } } } else if (!body.type && body.command) { // Slash commands const slackRequest = { payload: body, ...baseRequest, }; for (const matcher of __classPrivateFieldGet(this, _SlackApp_slashCommands, "f")) { const handler = matcher(payload); if (handler) { if (!this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } const slackResponse = await handler.ack(slackRequest); if ((0, slack_web_api_client_1.isDebugLogEnabled)(this.env.SLACK_LOGGING_LEVEL)) { console.log(`*** Slack response ***\n${(0, slack_web_api_client_1.prettyPrint)(slackResponse)}`); } if (this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } return (0, response_1.toCompleteResponse)(slackResponse); } } } else if (body.type === payload_types_1.PayloadType.GlobalShortcut) { // Global shortcuts const slackRequest = { payload: body, ...baseRequest, }; for (const matcher of __classPrivateFieldGet(this, _SlackApp_globalShorcuts, "f")) { const handler = matcher(payload); if (handler) { if (!this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } const slackResponse = await handler.ack(slackRequest); if ((0, slack_web_api_client_1.isDebugLogEnabled)(this.env.SLACK_LOGGING_LEVEL)) { console.log(`*** Slack response ***\n${(0, slack_web_api_client_1.prettyPrint)(slackResponse)}`); } if (this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } return (0, response_1.toCompleteResponse)(slackResponse); } } } else if (body.type === payload_types_1.PayloadType.MessageShortcut) { // Message shortcuts const slackRequest = { payload: body, ...baseRequest, }; for (const matcher of __classPrivateFieldGet(this, _SlackApp_messageShorcuts, "f")) { const handler = matcher(payload); if (handler) { if (!this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } const slackResponse = await handler.ack(slackRequest); if ((0, slack_web_api_client_1.isDebugLogEnabled)(this.env.SLACK_LOGGING_LEVEL)) { console.log(`*** Slack response ***\n${(0, slack_web_api_client_1.prettyPrint)(slackResponse)}`); } if (this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } return (0, response_1.toCompleteResponse)(slackResponse); } } } else if (body.type === payload_types_1.PayloadType.BlockAction) { // Block actions // deno-lint-ignore no-explicit-any const slackRequest = { // deno-lint-ignore no-explicit-any payload: body, ...baseRequest, }; for (const matcher of __classPrivateFieldGet(this, _SlackApp_blockActions, "f")) { const handler = matcher(payload); if (handler) { if (!this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } const slackResponse = await handler.ack(slackRequest); if ((0, slack_web_api_client_1.isDebugLogEnabled)(this.env.SLACK_LOGGING_LEVEL)) { console.log(`*** Slack response ***\n${(0, slack_web_api_client_1.prettyPrint)(slackResponse)}`); } if (this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } return (0, response_1.toCompleteResponse)(slackResponse); } } } else if (body.type === payload_types_1.PayloadType.BlockSuggestion) { // Block suggestions const slackRequest = { payload: body, ...baseRequest, }; for (const matcher of __classPrivateFieldGet(this, _SlackApp_blockSuggestions, "f")) { const handler = matcher(payload); if (handler) { // Note that the only way to respond to a block_suggestion request // is to send an HTTP response with options/option_groups. // Thus, we don't support lazy handlers for this pattern. const slackResponse = await handler.ack(slackRequest); if ((0, slack_web_api_client_1.isDebugLogEnabled)(this.env.SLACK_LOGGING_LEVEL)) { console.log(`*** Slack response ***\n${(0, slack_web_api_client_1.prettyPrint)(slackResponse)}`); } return (0, response_1.toCompleteResponse)(slackResponse); } } } else if (body.type === payload_types_1.PayloadType.ViewSubmission) { // View submissions const slackRequest = { payload: body, ...baseRequest, }; for (const matcher of __classPrivateFieldGet(this, _SlackApp_viewSubmissions, "f")) { const handler = matcher(payload); if (handler) { if (!this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } const slackResponse = await handler.ack(slackRequest); if ((0, slack_web_api_client_1.isDebugLogEnabled)(this.env.SLACK_LOGGING_LEVEL)) { console.log(`*** Slack response ***\n${(0, slack_web_api_client_1.prettyPrint)(slackResponse)}`); } if (this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } return (0, response_1.toCompleteResponse)(slackResponse); } } } else if (body.type === payload_types_1.PayloadType.ViewClosed) { // View closed const slackRequest = { payload: body, ...baseRequest, }; for (const matcher of __classPrivateFieldGet(this, _SlackApp_viewClosed, "f")) { const handler = matcher(payload); if (handler) { if (!this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } const slackResponse = await handler.ack(slackRequest); if ((0, slack_web_api_client_1.isDebugLogEnabled)(this.env.SLACK_LOGGING_LEVEL)) { console.log(`*** Slack response ***\n${(0, slack_web_api_client_1.prettyPrint)(slackResponse)}`); } if (this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } return (0, response_1.toCompleteResponse)(slackResponse); } } } else if (body.type === payload_types_1.PayloadType.AppRateLimited) { // App rate limited const slackRequest = { payload: body, ...baseRequest, }; // Only a single appRateLimited handler is supported if (__classPrivateFieldGet(this, _SlackApp_appRateLimited, "f")) { const handler = __classPrivateFieldGet(this, _SlackApp_appRateLimited, "f").call(this, payload); if (handler) { if (!this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } const slackResponse = await handler.ack(slackRequest); if ((0, slack_web_api_client_1.isDebugLogEnabled)(this.env.SLACK_LOGGING_LEVEL)) { console.log(`*** Slack response ***\n${(0, slack_web_api_client_1.prettyPrint)(slackResponse)}`); } if (this.startLazyListenerAfterAck) { ctx.waitUntil(handler.lazy(slackRequest)); } return (0, response_1.toCompleteResponse)(slackResponse); } } } // TODO: Add code suggestion here console.log(`*** No listener found ***\n${JSON.stringify(baseRequest.body)}`); return new Response("No listener found", { status: 404 }); } return new Response("Invalid signature", { status: 401 }); } } exports.SlackApp = SlackApp; _SlackApp_slashCommands = new WeakMap(), _SlackApp_events = new WeakMap(), _SlackApp_globalShorcuts = new WeakMap(), _SlackApp_messageShorcuts = new WeakMap(), _SlackApp_blockActions = new WeakMap(), _SlackApp_blockSuggestions = new WeakMap(), _SlackApp_viewSubmissions = new WeakMap(), _SlackApp_viewClosed = new WeakMap(), _SlackApp_appRateLimited = new WeakMap(), _SlackApp_assistantEnabled = new WeakMap(), _SlackApp_instances = new WeakSet(), _SlackApp_assistantEvent = function _SlackApp_assistantEvent(event, lazy, handleSelfBotMessageEvents = false) { __classPrivateFieldSet(this, _SlackApp_assistantEnabled, true, "f"); __classPrivateFieldGet(this, _SlackApp_events, "f").push((body) => { if (body.type !== payload_types_1.PayloadType.EventsAPI || !body.event) { return null; } if (body.event.type === event && __classPrivateFieldGet(this, _SlackApp_assistantEnabled, "f") && (0, context_1.isAssitantThreadEvent)(body)) { if (event === "message" && "bot_profile" in body.event) { if (handleSelfBotMessageEvents) { // this app's bot message events // The botMessageHandler acknowledges this pattern // Note that ignoreSelfAssistantMessageEvents must be set to false // deno-lint-ignore require-await return { ack: async () => "", lazy }; } else { // userMessageHandler does not acknowledge return null; } } else { // assistant_thread_started events // assistant_thread_context_changed events // user message events // deno-lint-ignore require-await return { ack: async () => "", lazy }; } } return null; }); return this; }, _SlackApp_callAuthorize = async function _SlackApp_callAuthorize(request) { const body = request.body; if (body.type === payload_types_1.PayloadType.EventsAPI && body.event && this.eventsToSkipAuthorize.includes(body.event.type)) { // this pattern does not need AuthorizeResult at all return { enterpriseId: request.context.actorEnterpriseId, teamId: request.context.actorTeamId, team: request.context.actorTeamId, botId: request.context.botId || "N/A", botUserId: request.context.botUserId || "N/A", botToken: "N/A", botScopes: [], userId: request.context.actorUserId, user: request.context.actorUserId, userToken: "N/A", userScopes: [], }; } return await this.authorize(request); }; /** * Singleton lazy listener that does not do anything */ const noopLazyHandler = async () => { }; exports.noopLazyHandler = noopLazyHandler; //# sourceMappingURL=app.js.map