@slack/bolt 3.8.0 → 3.8.1

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.
Files changed (146) hide show
  1. package/dist/App.d.ts +212 -0
  2. package/dist/App.d.ts.map +1 -0
  3. package/dist/App.js +796 -0
  4. package/dist/App.js.map +1 -0
  5. package/dist/WorkflowStep.d.ts +106 -0
  6. package/dist/WorkflowStep.d.ts.map +1 -0
  7. package/dist/WorkflowStep.js +196 -0
  8. package/dist/WorkflowStep.js.map +1 -0
  9. package/dist/conversation-store.d.ts +31 -0
  10. package/dist/conversation-store.d.ts.map +1 -0
  11. package/dist/conversation-store.js +72 -0
  12. package/dist/conversation-store.js.map +1 -0
  13. package/dist/errors.d.ts +79 -0
  14. package/dist/errors.d.ts.map +1 -0
  15. package/dist/errors.js +120 -0
  16. package/dist/errors.js.map +1 -0
  17. package/dist/helpers.d.ts +24 -0
  18. package/dist/helpers.d.ts.map +1 -0
  19. package/dist/helpers.js +100 -0
  20. package/dist/helpers.js.map +1 -0
  21. package/dist/index.d.ts +13 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +43 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/middleware/builtin.d.ts +52 -0
  26. package/dist/middleware/builtin.d.ts.map +1 -0
  27. package/dist/middleware/builtin.js +311 -0
  28. package/dist/middleware/builtin.js.map +1 -0
  29. package/dist/middleware/process.d.ts +5 -0
  30. package/dist/middleware/process.d.ts.map +1 -0
  31. package/dist/middleware/process.js +25 -0
  32. package/dist/middleware/process.js.map +1 -0
  33. package/dist/receivers/AwsLambdaReceiver.d.ts +53 -0
  34. package/dist/receivers/AwsLambdaReceiver.d.ts.map +1 -0
  35. package/dist/receivers/AwsLambdaReceiver.js +188 -0
  36. package/dist/receivers/AwsLambdaReceiver.js.map +1 -0
  37. package/dist/receivers/ExpressReceiver.d.ts +77 -0
  38. package/dist/receivers/ExpressReceiver.d.ts.map +1 -0
  39. package/dist/receivers/ExpressReceiver.js +445 -0
  40. package/dist/receivers/ExpressReceiver.js.map +1 -0
  41. package/dist/receivers/HTTPReceiver.d.ts +109 -0
  42. package/dist/receivers/HTTPReceiver.d.ts.map +1 -0
  43. package/dist/receivers/HTTPReceiver.js +501 -0
  44. package/dist/receivers/HTTPReceiver.js.map +1 -0
  45. package/dist/receivers/SocketModeReceiver.d.ts +61 -0
  46. package/dist/receivers/SocketModeReceiver.d.ts.map +1 -0
  47. package/dist/receivers/SocketModeReceiver.js +175 -0
  48. package/dist/receivers/SocketModeReceiver.js.map +1 -0
  49. package/dist/receivers/custom-routes.d.ts +14 -0
  50. package/dist/receivers/custom-routes.d.ts.map +1 -0
  51. package/dist/receivers/custom-routes.js +33 -0
  52. package/dist/receivers/custom-routes.js.map +1 -0
  53. package/dist/receivers/http-utils.d.ts +5 -0
  54. package/dist/receivers/http-utils.d.ts.map +1 -0
  55. package/dist/receivers/http-utils.js +35 -0
  56. package/dist/receivers/http-utils.js.map +1 -0
  57. package/dist/receivers/render-html-for-install-path.d.ts +3 -0
  58. package/dist/receivers/render-html-for-install-path.d.ts.map +1 -0
  59. package/dist/receivers/render-html-for-install-path.js +25 -0
  60. package/dist/receivers/render-html-for-install-path.js.map +1 -0
  61. package/dist/receivers/verify-redirect-opts.d.ts +7 -0
  62. package/dist/receivers/verify-redirect-opts.d.ts.map +1 -0
  63. package/dist/receivers/verify-redirect-opts.js +23 -0
  64. package/dist/receivers/verify-redirect-opts.js.map +1 -0
  65. package/dist/receivers/verify-request.d.ts +41 -0
  66. package/dist/receivers/verify-request.d.ts.map +1 -0
  67. package/dist/receivers/verify-request.js +98 -0
  68. package/dist/receivers/verify-request.js.map +1 -0
  69. package/dist/types/actions/block-action.d.ts +231 -0
  70. package/dist/types/actions/block-action.d.ts.map +1 -0
  71. package/dist/types/actions/block-action.js +3 -0
  72. package/dist/types/actions/block-action.js.map +1 -0
  73. package/dist/types/actions/dialog-action.d.ts +47 -0
  74. package/dist/types/actions/dialog-action.d.ts.map +1 -0
  75. package/dist/types/actions/dialog-action.js +3 -0
  76. package/dist/types/actions/dialog-action.js.map +1 -0
  77. package/dist/types/actions/index.d.ts +46 -0
  78. package/dist/types/actions/index.d.ts.map +1 -0
  79. package/dist/types/actions/index.js +17 -0
  80. package/dist/types/actions/index.js.map +1 -0
  81. package/dist/types/actions/interactive-message.d.ts +66 -0
  82. package/dist/types/actions/interactive-message.d.ts.map +1 -0
  83. package/dist/types/actions/interactive-message.js +3 -0
  84. package/dist/types/actions/interactive-message.js.map +1 -0
  85. package/dist/types/actions/workflow-step-edit.d.ts +49 -0
  86. package/dist/types/actions/workflow-step-edit.d.ts.map +1 -0
  87. package/dist/types/actions/workflow-step-edit.js +3 -0
  88. package/dist/types/actions/workflow-step-edit.js.map +1 -0
  89. package/dist/types/command/index.d.ts +36 -0
  90. package/dist/types/command/index.d.ts.map +1 -0
  91. package/dist/types/command/index.js +3 -0
  92. package/dist/types/command/index.js.map +1 -0
  93. package/dist/types/events/base-events.d.ts +699 -0
  94. package/dist/types/events/base-events.d.ts.map +1 -0
  95. package/dist/types/events/base-events.js +5 -0
  96. package/dist/types/events/base-events.js.map +1 -0
  97. package/dist/types/events/index.d.ts +63 -0
  98. package/dist/types/events/index.d.ts.map +1 -0
  99. package/dist/types/events/index.js +14 -0
  100. package/dist/types/events/index.js.map +1 -0
  101. package/dist/types/events/message-events.d.ts +312 -0
  102. package/dist/types/events/message-events.d.ts.map +1 -0
  103. package/dist/types/events/message-events.js +3 -0
  104. package/dist/types/events/message-events.js.map +1 -0
  105. package/dist/types/helpers.d.ts +17 -0
  106. package/dist/types/helpers.d.ts.map +1 -0
  107. package/dist/types/helpers.js +3 -0
  108. package/dist/types/helpers.js.map +1 -0
  109. package/dist/types/index.d.ts +10 -0
  110. package/dist/types/index.d.ts.map +1 -0
  111. package/dist/types/index.js +22 -0
  112. package/dist/types/index.js.map +1 -0
  113. package/dist/types/middleware.d.ts +65 -0
  114. package/dist/types/middleware.d.ts.map +1 -0
  115. package/dist/types/middleware.js +14 -0
  116. package/dist/types/middleware.js.map +1 -0
  117. package/dist/types/options/index.d.ts +192 -0
  118. package/dist/types/options/index.d.ts.map +1 -0
  119. package/dist/types/options/index.js +3 -0
  120. package/dist/types/options/index.js.map +1 -0
  121. package/dist/types/receiver.d.ts +16 -0
  122. package/dist/types/receiver.d.ts.map +1 -0
  123. package/dist/types/receiver.js +3 -0
  124. package/dist/types/receiver.js.map +1 -0
  125. package/dist/types/shortcuts/global-shortcut.d.ts +29 -0
  126. package/dist/types/shortcuts/global-shortcut.d.ts.map +1 -0
  127. package/dist/types/shortcuts/global-shortcut.js +3 -0
  128. package/dist/types/shortcuts/global-shortcut.js.map +1 -0
  129. package/dist/types/shortcuts/index.d.ts +23 -0
  130. package/dist/types/shortcuts/index.d.ts.map +1 -0
  131. package/dist/types/shortcuts/index.js +16 -0
  132. package/dist/types/shortcuts/index.js.map +1 -0
  133. package/dist/types/shortcuts/message-shortcut.d.ts +43 -0
  134. package/dist/types/shortcuts/message-shortcut.d.ts.map +1 -0
  135. package/dist/types/shortcuts/message-shortcut.js +3 -0
  136. package/dist/types/shortcuts/message-shortcut.js.map +1 -0
  137. package/dist/types/utilities.d.ts +23 -0
  138. package/dist/types/utilities.d.ts.map +1 -0
  139. package/dist/types/utilities.js +3 -0
  140. package/dist/types/utilities.js.map +1 -0
  141. package/dist/types/view/index.d.ts +175 -0
  142. package/dist/types/view/index.d.ts.map +1 -0
  143. package/dist/types/view/index.js +3 -0
  144. package/dist/types/view/index.js.map +1 -0
  145. package/package.json +1 -1
  146. package/dist/package.json +0 -88
package/dist/App.js ADDED
@@ -0,0 +1,796 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LogLevel = void 0;
7
+ const util_1 = __importDefault(require("util"));
8
+ const web_api_1 = require("@slack/web-api");
9
+ const logger_1 = require("@slack/logger");
10
+ const axios_1 = __importDefault(require("axios"));
11
+ const SocketModeReceiver_1 = __importDefault(require("./receivers/SocketModeReceiver"));
12
+ const HTTPReceiver_1 = __importDefault(require("./receivers/HTTPReceiver"));
13
+ const builtin_1 = require("./middleware/builtin");
14
+ const process_1 = __importDefault(require("./middleware/process"));
15
+ const conversation_store_1 = require("./conversation-store");
16
+ const helpers_1 = require("./helpers");
17
+ const errors_1 = require("./errors");
18
+ const middleware_1 = require("./types/middleware");
19
+ // eslint-disable-next-line import/order
20
+ const allSettled = require("promise.allsettled"); // eslint-disable-line @typescript-eslint/no-require-imports
21
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-commonjs
22
+ const packageJson = require('../package.json'); // eslint-disable-line @typescript-eslint/no-var-requires
23
+ // ----------------------------
24
+ // For listener registration methods
25
+ const validViewTypes = ['view_closed', 'view_submission'];
26
+ // ----------------------------
27
+ // For the constructor
28
+ const tokenUsage = 'Apps used in one workspace should be initialized with a token. Apps used in many workspaces ' +
29
+ 'should be initialized with oauth installer or authorize.';
30
+ var logger_2 = require("@slack/logger");
31
+ Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return logger_2.LogLevel; } });
32
+ class WebClientPool {
33
+ constructor() {
34
+ this.pool = {};
35
+ }
36
+ getOrCreate(token, clientOptions) {
37
+ const cachedClient = this.pool[token];
38
+ if (typeof cachedClient !== 'undefined') {
39
+ return cachedClient;
40
+ }
41
+ const client = new web_api_1.WebClient(token, clientOptions);
42
+ this.pool[token] = client;
43
+ return client;
44
+ }
45
+ }
46
+ /**
47
+ * A Slack App
48
+ */
49
+ class App {
50
+ constructor({ signingSecret = undefined, endpoints = undefined, port = undefined, customRoutes = undefined, agent = undefined, clientTls = undefined, receiver = undefined, convoStore = undefined, token = undefined, appToken = undefined, botId = undefined, botUserId = undefined, authorize = undefined, logger = undefined, logLevel = undefined, ignoreSelf = true, clientOptions = undefined, processBeforeResponse = false, signatureVerification = true, clientId = undefined, clientSecret = undefined, stateSecret = undefined, redirectUri = undefined, installationStore = undefined, scopes = undefined, installerOptions = undefined, socketMode = undefined, developerMode = false, tokenVerificationEnabled = true, extendedErrorHandler = false, } = {}) {
51
+ // Some payloads don't have teamId anymore. So we use EnterpriseId in those scenarios
52
+ this.clients = {};
53
+ // this.logLevel = logLevel;
54
+ this.developerMode = developerMode;
55
+ if (developerMode) {
56
+ // Set logLevel to Debug in Developer Mode if one wasn't passed in
57
+ this.logLevel = logLevel !== null && logLevel !== void 0 ? logLevel : logger_1.LogLevel.DEBUG;
58
+ // Set SocketMode to true if one wasn't passed in
59
+ this.socketMode = socketMode !== null && socketMode !== void 0 ? socketMode : true;
60
+ }
61
+ else {
62
+ // If devs aren't using Developer Mode or Socket Mode, set it to false
63
+ this.socketMode = socketMode !== null && socketMode !== void 0 ? socketMode : false;
64
+ // Set logLevel to Info if one wasn't passed in
65
+ this.logLevel = logLevel !== null && logLevel !== void 0 ? logLevel : logger_1.LogLevel.INFO;
66
+ }
67
+ // Set up logger
68
+ if (typeof logger === 'undefined') {
69
+ // Initialize with the default logger
70
+ const consoleLogger = new logger_1.ConsoleLogger();
71
+ consoleLogger.setName('bolt-app');
72
+ this.logger = consoleLogger;
73
+ }
74
+ else {
75
+ this.logger = logger;
76
+ }
77
+ if (typeof this.logLevel !== 'undefined' && this.logger.getLevel() !== this.logLevel) {
78
+ this.logger.setLevel(this.logLevel);
79
+ }
80
+ // Error-related properties used to later determine args passed into the error handler
81
+ this.hasCustomErrorHandler = false;
82
+ this.errorHandler = defaultErrorHandler(this.logger);
83
+ this.extendedErrorHandler = extendedErrorHandler;
84
+ // Set up client options
85
+ this.clientOptions = clientOptions !== undefined ? clientOptions : {};
86
+ if (agent !== undefined && this.clientOptions.agent === undefined) {
87
+ this.clientOptions.agent = agent;
88
+ }
89
+ if (clientTls !== undefined && this.clientOptions.tls === undefined) {
90
+ this.clientOptions.tls = clientTls;
91
+ }
92
+ if (logLevel !== undefined && logger === undefined) {
93
+ // only logLevel is passed
94
+ this.clientOptions.logLevel = logLevel;
95
+ }
96
+ else {
97
+ // Since v3.4, WebClient starts sharing logger with App
98
+ this.clientOptions.logger = this.logger;
99
+ }
100
+ // The public WebClient instance (app.client)
101
+ // Since v3.4, it can have the passed token in the case of single workspace installation.
102
+ this.client = new web_api_1.WebClient(token, this.clientOptions);
103
+ this.axios = axios_1.default.create({
104
+ httpAgent: agent,
105
+ httpsAgent: agent,
106
+ // disabling axios' automatic proxy support:
107
+ // axios would read from envvars to configure a proxy automatically, but it doesn't support TLS destinations.
108
+ // for compatibility with https://api.slack.com, and for a larger set of possible proxies (SOCKS or other
109
+ // protocols), users of this package should use the `agent` option to configure a proxy.
110
+ proxy: false,
111
+ ...clientTls,
112
+ });
113
+ this.middleware = [];
114
+ this.listeners = [];
115
+ // Add clientOptions to InstallerOptions to pass them to @slack/oauth
116
+ this.installerOptions = {
117
+ clientOptions: this.clientOptions,
118
+ ...installerOptions,
119
+ };
120
+ if (socketMode && port !== undefined && this.installerOptions.port === undefined) {
121
+ // As SocketModeReceiver uses a custom port number to listen on only for the OAuth flow,
122
+ // only installerOptions.port is available in the constructor arguments.
123
+ this.installerOptions.port = port;
124
+ }
125
+ if (this.developerMode &&
126
+ this.installerOptions &&
127
+ (typeof this.installerOptions.callbackOptions === 'undefined' ||
128
+ (typeof this.installerOptions.callbackOptions !== 'undefined' &&
129
+ typeof this.installerOptions.callbackOptions.failure === 'undefined'))) {
130
+ // add a custom failure callback for Developer Mode in case they are using OAuth
131
+ this.logger.debug('adding Developer Mode custom OAuth failure handler');
132
+ this.installerOptions.callbackOptions = {
133
+ failure: (error, _installOptions, _req, res) => {
134
+ this.logger.debug(error);
135
+ res.writeHead(500, { 'Content-Type': 'text/html' });
136
+ res.end(`<html><body><h1>OAuth failed!</h1><div>${error}</div></body></html>`);
137
+ },
138
+ };
139
+ }
140
+ // Initialize receiver
141
+ if (receiver !== undefined) {
142
+ // Custom receiver
143
+ if (this.socketMode) {
144
+ throw new errors_1.AppInitializationError('receiver cannot be passed when socketMode is set to true');
145
+ }
146
+ this.receiver = receiver;
147
+ }
148
+ else if (this.socketMode) {
149
+ if (appToken === undefined) {
150
+ throw new errors_1.AppInitializationError('You must provide an appToken when using Socket Mode');
151
+ }
152
+ this.logger.debug('Initializing SocketModeReceiver');
153
+ // Create default SocketModeReceiver
154
+ this.receiver = new SocketModeReceiver_1.default({
155
+ appToken,
156
+ clientId,
157
+ clientSecret,
158
+ stateSecret,
159
+ redirectUri,
160
+ installationStore,
161
+ scopes,
162
+ logger,
163
+ logLevel: this.logLevel,
164
+ installerOptions: this.installerOptions,
165
+ customRoutes,
166
+ });
167
+ }
168
+ else if (signatureVerification && signingSecret === undefined) {
169
+ // No custom receiver
170
+ throw new errors_1.AppInitializationError('Signing secret not found, so could not initialize the default receiver. Set a signing secret or use a ' +
171
+ 'custom receiver.');
172
+ }
173
+ else {
174
+ this.logger.debug('Initializing HTTPReceiver');
175
+ // Create default HTTPReceiver
176
+ this.receiver = new HTTPReceiver_1.default({
177
+ signingSecret: signingSecret || '',
178
+ endpoints,
179
+ port,
180
+ customRoutes,
181
+ processBeforeResponse,
182
+ signatureVerification,
183
+ clientId,
184
+ clientSecret,
185
+ stateSecret,
186
+ redirectUri,
187
+ installationStore,
188
+ scopes,
189
+ logger,
190
+ logLevel: this.logLevel,
191
+ installerOptions: this.installerOptions,
192
+ });
193
+ }
194
+ let usingOauth = false;
195
+ const httpReceiver = this.receiver;
196
+ if (httpReceiver.installer !== undefined &&
197
+ httpReceiver.installer.authorize !== undefined) {
198
+ // This supports using the built in HTTPReceiver, declaring your own HTTPReceiver
199
+ // and theoretically, doing a fully custom (non express) receiver that implements OAuth
200
+ usingOauth = true;
201
+ }
202
+ if (token !== undefined) {
203
+ if (authorize !== undefined || usingOauth) {
204
+ throw new errors_1.AppInitializationError(`token as well as authorize or oauth installer options were provided. ${tokenUsage}`);
205
+ }
206
+ this.authorize = singleAuthorization(this.client, {
207
+ botId,
208
+ botUserId,
209
+ botToken: token,
210
+ }, tokenVerificationEnabled);
211
+ }
212
+ else if (authorize === undefined && !usingOauth) {
213
+ throw new errors_1.AppInitializationError(`No token, no authorize, and no oauth installer options provided. ${tokenUsage}`);
214
+ }
215
+ else if (authorize !== undefined && usingOauth) {
216
+ throw new errors_1.AppInitializationError(`Both authorize options and oauth installer options provided. ${tokenUsage}`);
217
+ }
218
+ else if (authorize === undefined && usingOauth) {
219
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
220
+ this.authorize = httpReceiver.installer.authorize;
221
+ }
222
+ else if (authorize !== undefined && !usingOauth) {
223
+ this.authorize = authorize;
224
+ }
225
+ else {
226
+ this.logger.error('Never should have reached this point, please report to the team');
227
+ (0, helpers_1.assertNever)();
228
+ }
229
+ // Conditionally use a global middleware that ignores events (including messages) that are sent from this app
230
+ if (ignoreSelf) {
231
+ this.use((0, builtin_1.ignoreSelf)());
232
+ }
233
+ // Use conversation state global middleware
234
+ if (convoStore !== false) {
235
+ // Use the memory store by default, or another store if provided
236
+ const store = convoStore === undefined ? new conversation_store_1.MemoryStore() : convoStore;
237
+ this.use((0, conversation_store_1.conversationContext)(store));
238
+ }
239
+ // Should be last to avoid exposing partially initialized app
240
+ this.receiver.init(this);
241
+ }
242
+ /**
243
+ * Register a new middleware, processed in the order registered.
244
+ *
245
+ * @param m global middleware function
246
+ */
247
+ use(m) {
248
+ this.middleware.push(m);
249
+ return this;
250
+ }
251
+ /**
252
+ * Register WorkflowStep middleware
253
+ *
254
+ * @param workflowStep global workflow step middleware function
255
+ */
256
+ step(workflowStep) {
257
+ const m = workflowStep.getMiddleware();
258
+ this.middleware.push(m);
259
+ return this;
260
+ }
261
+ /**
262
+ * Convenience method to call start on the receiver
263
+ *
264
+ * TODO: should replace HTTPReceiver in type definition with a generic that is constrained to Receiver
265
+ *
266
+ * @param args receiver-specific start arguments
267
+ */
268
+ start(...args) {
269
+ // TODO: HTTPReceiver['start'] should be the actual receiver's return type
270
+ return this.receiver.start(...args);
271
+ }
272
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
273
+ stop(...args) {
274
+ return this.receiver.stop(...args);
275
+ }
276
+ event(eventNameOrPattern, ...listeners) {
277
+ let invalidEventName = false;
278
+ if (typeof eventNameOrPattern === 'string') {
279
+ const name = eventNameOrPattern;
280
+ invalidEventName = name.startsWith('message.');
281
+ }
282
+ else if (eventNameOrPattern instanceof RegExp) {
283
+ const name = eventNameOrPattern.source;
284
+ invalidEventName = name.startsWith('message\\.');
285
+ }
286
+ if (invalidEventName) {
287
+ throw new errors_1.AppInitializationError(`Although the document mentions "${eventNameOrPattern}",` +
288
+ 'it is not a valid event type. Use "message" instead. ' +
289
+ 'If you want to filter message events, you can use event.channel_type for it.');
290
+ }
291
+ this.listeners.push([
292
+ builtin_1.onlyEvents,
293
+ (0, builtin_1.matchEventType)(eventNameOrPattern),
294
+ ...listeners,
295
+ ]);
296
+ }
297
+ message(...patternsOrMiddleware) {
298
+ const messageMiddleware = patternsOrMiddleware.map((patternOrMiddleware) => {
299
+ if (typeof patternOrMiddleware === 'string' || util_1.default.types.isRegExp(patternOrMiddleware)) {
300
+ return (0, builtin_1.matchMessage)(patternOrMiddleware);
301
+ }
302
+ return patternOrMiddleware;
303
+ });
304
+ this.listeners.push([
305
+ builtin_1.onlyEvents,
306
+ (0, builtin_1.matchEventType)('message'),
307
+ ...messageMiddleware,
308
+ ]);
309
+ }
310
+ shortcut(callbackIdOrConstraints, ...listeners) {
311
+ const constraints = typeof callbackIdOrConstraints === 'string' || util_1.default.types.isRegExp(callbackIdOrConstraints) ?
312
+ { callback_id: callbackIdOrConstraints } :
313
+ callbackIdOrConstraints;
314
+ // Fail early if the constraints contain invalid keys
315
+ const unknownConstraintKeys = Object.keys(constraints).filter((k) => k !== 'callback_id' && k !== 'type');
316
+ if (unknownConstraintKeys.length > 0) {
317
+ this.logger.error(`Slack listener cannot be attached using unknown constraint keys: ${unknownConstraintKeys.join(', ')}`);
318
+ return;
319
+ }
320
+ this.listeners.push([
321
+ builtin_1.onlyShortcuts,
322
+ (0, builtin_1.matchConstraints)(constraints),
323
+ ...listeners,
324
+ ]);
325
+ }
326
+ action(actionIdOrConstraints, ...listeners) {
327
+ // Normalize Constraints
328
+ const constraints = typeof actionIdOrConstraints === 'string' || util_1.default.types.isRegExp(actionIdOrConstraints) ?
329
+ { action_id: actionIdOrConstraints } :
330
+ actionIdOrConstraints;
331
+ // Fail early if the constraints contain invalid keys
332
+ const unknownConstraintKeys = Object.keys(constraints).filter((k) => k !== 'action_id' && k !== 'block_id' && k !== 'callback_id' && k !== 'type');
333
+ if (unknownConstraintKeys.length > 0) {
334
+ this.logger.error(`Action listener cannot be attached using unknown constraint keys: ${unknownConstraintKeys.join(', ')}`);
335
+ return;
336
+ }
337
+ this.listeners.push([builtin_1.onlyActions, (0, builtin_1.matchConstraints)(constraints), ...listeners]);
338
+ }
339
+ command(commandName, ...listeners) {
340
+ this.listeners.push([builtin_1.onlyCommands, (0, builtin_1.matchCommandName)(commandName), ...listeners]);
341
+ }
342
+ // TODO: reflect the type in constraits to Source
343
+ options(actionIdOrConstraints, ...listeners) {
344
+ const constraints = typeof actionIdOrConstraints === 'string' || util_1.default.types.isRegExp(actionIdOrConstraints) ?
345
+ { action_id: actionIdOrConstraints } :
346
+ actionIdOrConstraints;
347
+ this.listeners.push([builtin_1.onlyOptions, (0, builtin_1.matchConstraints)(constraints), ...listeners]);
348
+ }
349
+ view(callbackIdOrConstraints, ...listeners) {
350
+ const constraints = typeof callbackIdOrConstraints === 'string' || util_1.default.types.isRegExp(callbackIdOrConstraints) ?
351
+ { callback_id: callbackIdOrConstraints, type: 'view_submission' } :
352
+ callbackIdOrConstraints;
353
+ // Fail early if the constraints contain invalid keys
354
+ const unknownConstraintKeys = Object.keys(constraints).filter((k) => k !== 'callback_id' && k !== 'type');
355
+ if (unknownConstraintKeys.length > 0) {
356
+ this.logger.error(`View listener cannot be attached using unknown constraint keys: ${unknownConstraintKeys.join(', ')}`);
357
+ return;
358
+ }
359
+ if (constraints.type !== undefined && !validViewTypes.includes(constraints.type)) {
360
+ this.logger.error(`View listener cannot be attached using unknown view event type: ${constraints.type}`);
361
+ return;
362
+ }
363
+ this.listeners.push([
364
+ builtin_1.onlyViewActions,
365
+ (0, builtin_1.matchConstraints)(constraints),
366
+ ...listeners,
367
+ ]);
368
+ }
369
+ error(errorHandler) {
370
+ this.errorHandler = errorHandler;
371
+ this.hasCustomErrorHandler = true;
372
+ }
373
+ /**
374
+ * Handles events from the receiver
375
+ */
376
+ async processEvent(event) {
377
+ const { body, ack } = event;
378
+ if (this.developerMode) {
379
+ // log the body of the event
380
+ // this may contain sensitive info like tokens
381
+ this.logger.debug(JSON.stringify(body));
382
+ }
383
+ // TODO: when generating errors (such as in the say utility) it may become useful to capture the current context,
384
+ // or even all of the args, as properties of the error. This would give error handling code some ability to deal
385
+ // with "finally" type error situations.
386
+ // Introspect the body to determine what type of incoming event is being handled, and any channel context
387
+ const { type, conversationId } = (0, helpers_1.getTypeAndConversation)(body);
388
+ // If the type could not be determined, warn and exit
389
+ if (type === undefined) {
390
+ this.logger.warn('Could not determine the type of an incoming event. No listeners will be called.');
391
+ return;
392
+ }
393
+ // From this point on, we assume that body is not just a key-value map, but one of the types of bodies we expect
394
+ const bodyArg = body;
395
+ // Check if type event with the authorizations object or if it has a top level is_enterprise_install property
396
+ const isEnterpriseInstall = isBodyWithTypeEnterpriseInstall(bodyArg, type);
397
+ const source = buildSource(type, conversationId, bodyArg, isEnterpriseInstall);
398
+ let authorizeResult;
399
+ try {
400
+ if (source.isEnterpriseInstall) {
401
+ authorizeResult = await this.authorize(source, bodyArg);
402
+ }
403
+ else {
404
+ authorizeResult = await this.authorize(source, bodyArg);
405
+ }
406
+ }
407
+ catch (error) {
408
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
409
+ const e = error;
410
+ this.logger.warn('Authorization of incoming event did not succeed. No listeners will be called.');
411
+ e.code = errors_1.ErrorCode.AuthorizationError;
412
+ // disabling due to https://github.com/typescript-eslint/typescript-eslint/issues/1277
413
+ // eslint-disable-next-line consistent-return
414
+ return this.handleError({
415
+ error: e,
416
+ logger: this.logger,
417
+ body: bodyArg,
418
+ context: {},
419
+ });
420
+ }
421
+ // Try to set teamId from AuthorizeResult before using one from source
422
+ if (authorizeResult.teamId === undefined && source.teamId !== undefined) {
423
+ authorizeResult.teamId = source.teamId;
424
+ }
425
+ // Try to set enterpriseId from AuthorizeResult before using one from source
426
+ if (authorizeResult.enterpriseId === undefined && source.enterpriseId !== undefined) {
427
+ authorizeResult.enterpriseId = source.enterpriseId;
428
+ }
429
+ if (typeof event.customProperties !== 'undefined') {
430
+ const customProps = event.customProperties;
431
+ const builtinKeyDetected = middleware_1.contextBuiltinKeys.find((key) => key in customProps);
432
+ if (typeof builtinKeyDetected !== 'undefined') {
433
+ throw new errors_1.InvalidCustomPropertyError('customProperties cannot have the same names with the built-in ones');
434
+ }
435
+ }
436
+ const context = {
437
+ ...authorizeResult,
438
+ ...event.customProperties,
439
+ retryNum: event.retryNum,
440
+ retryReason: event.retryReason,
441
+ };
442
+ // Factory for say() utility
443
+ const createSay = (channelId) => {
444
+ const token = selectToken(context);
445
+ return (message) => {
446
+ const postMessageArguments = typeof message === 'string' ?
447
+ { token, text: message, channel: channelId } :
448
+ { ...message, token, channel: channelId };
449
+ return this.client.chat.postMessage(postMessageArguments);
450
+ };
451
+ };
452
+ // Set body and payload
453
+ // TODO: this value should eventually conform to AnyMiddlewareArgs
454
+ let payload;
455
+ switch (type) {
456
+ case helpers_1.IncomingEventType.Event:
457
+ payload = bodyArg.event;
458
+ break;
459
+ case helpers_1.IncomingEventType.ViewAction:
460
+ payload = bodyArg.view;
461
+ break;
462
+ case helpers_1.IncomingEventType.Shortcut:
463
+ payload = bodyArg;
464
+ break;
465
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
466
+ // @ts-ignore: Fallthrough case in switch
467
+ case helpers_1.IncomingEventType.Action:
468
+ if (isBlockActionOrInteractiveMessageBody(bodyArg)) {
469
+ const { actions } = bodyArg;
470
+ [payload] = actions;
471
+ break;
472
+ }
473
+ // If above conditional does not hit, fall through to fallback payload in default block below
474
+ default:
475
+ payload = bodyArg;
476
+ break;
477
+ }
478
+ // NOTE: the following doesn't work because... distributive?
479
+ // const listenerArgs: Partial<AnyMiddlewareArgs> = {
480
+ const listenerArgs = {
481
+ body: bodyArg,
482
+ payload,
483
+ };
484
+ // Set aliases
485
+ if (type === helpers_1.IncomingEventType.Event) {
486
+ const eventListenerArgs = listenerArgs;
487
+ eventListenerArgs.event = eventListenerArgs.payload;
488
+ if (eventListenerArgs.event.type === 'message') {
489
+ const messageEventListenerArgs = eventListenerArgs;
490
+ messageEventListenerArgs.message = messageEventListenerArgs.payload;
491
+ }
492
+ }
493
+ else if (type === helpers_1.IncomingEventType.Action) {
494
+ const actionListenerArgs = listenerArgs;
495
+ actionListenerArgs.action = actionListenerArgs.payload;
496
+ }
497
+ else if (type === helpers_1.IncomingEventType.Command) {
498
+ const commandListenerArgs = listenerArgs;
499
+ commandListenerArgs.command = commandListenerArgs.payload;
500
+ }
501
+ else if (type === helpers_1.IncomingEventType.Options) {
502
+ const optionListenerArgs = listenerArgs;
503
+ optionListenerArgs.options = optionListenerArgs.payload;
504
+ }
505
+ else if (type === helpers_1.IncomingEventType.ViewAction) {
506
+ const viewListenerArgs = listenerArgs;
507
+ viewListenerArgs.view = viewListenerArgs.payload;
508
+ }
509
+ else if (type === helpers_1.IncomingEventType.Shortcut) {
510
+ const shortcutListenerArgs = listenerArgs;
511
+ shortcutListenerArgs.shortcut = shortcutListenerArgs.payload;
512
+ }
513
+ // Set say() utility
514
+ if (conversationId !== undefined && type !== helpers_1.IncomingEventType.Options) {
515
+ listenerArgs.say = createSay(conversationId);
516
+ }
517
+ // Set respond() utility
518
+ if (body.response_url) {
519
+ listenerArgs.respond = buildRespondFn(this.axios, body.response_url);
520
+ }
521
+ else if (typeof body.response_urls !== 'undefined' && body.response_urls.length > 0) {
522
+ // This can exist only when view_submission payloads - response_url_enabled: true
523
+ listenerArgs.respond = buildRespondFn(this.axios, body.response_urls[0].response_url);
524
+ }
525
+ // Set ack() utility
526
+ if (type !== helpers_1.IncomingEventType.Event) {
527
+ listenerArgs.ack = ack;
528
+ }
529
+ else {
530
+ // Events API requests are acknowledged right away, since there's no data expected
531
+ await ack();
532
+ }
533
+ // Get the client arg
534
+ let { client } = this;
535
+ const token = selectToken(context);
536
+ if (token !== undefined) {
537
+ let pool;
538
+ const clientOptionsCopy = { ...this.clientOptions };
539
+ if (authorizeResult.teamId !== undefined) {
540
+ pool = this.clients[authorizeResult.teamId];
541
+ if (pool === undefined) {
542
+ // eslint-disable-next-line no-multi-assign
543
+ pool = this.clients[authorizeResult.teamId] = new WebClientPool();
544
+ }
545
+ // Add teamId to clientOptions so it can be automatically added to web-api calls
546
+ clientOptionsCopy.teamId = authorizeResult.teamId;
547
+ }
548
+ else if (authorizeResult.enterpriseId !== undefined) {
549
+ pool = this.clients[authorizeResult.enterpriseId];
550
+ if (pool === undefined) {
551
+ // eslint-disable-next-line no-multi-assign
552
+ pool = this.clients[authorizeResult.enterpriseId] = new WebClientPool();
553
+ }
554
+ }
555
+ if (pool !== undefined) {
556
+ client = pool.getOrCreate(token, clientOptionsCopy);
557
+ }
558
+ }
559
+ // Dispatch event through the global middleware chain
560
+ try {
561
+ await (0, process_1.default)(this.middleware, listenerArgs, context, client, this.logger, async () => {
562
+ // Dispatch the event through the listener middleware chains and aggregate their results
563
+ // TODO: change the name of this.middleware and this.listeners to help this make more sense
564
+ const listenerResults = this.listeners.map(async (origListenerMiddleware) => {
565
+ // Copy the array so modifications don't affect the original
566
+ const listenerMiddleware = [...origListenerMiddleware];
567
+ // Don't process the last item in the listenerMiddleware array - it shouldn't get a next fn
568
+ const listener = listenerMiddleware.pop();
569
+ if (listener === undefined) {
570
+ return undefined;
571
+ }
572
+ return (0, process_1.default)(listenerMiddleware, listenerArgs, context, client, this.logger,
573
+ // When all of the listener middleware are done processing,
574
+ // `listener` here will be called without a `next` execution
575
+ async () => listener({
576
+ ...listenerArgs,
577
+ context,
578
+ client,
579
+ logger: this.logger,
580
+ // `next` is already set in the outer processMiddleware
581
+ }));
582
+ });
583
+ const settledListenerResults = await allSettled(listenerResults);
584
+ const rejectedListenerResults = settledListenerResults.filter((lr) => lr.status === 'rejected');
585
+ if (rejectedListenerResults.length === 1) {
586
+ throw rejectedListenerResults[0].reason;
587
+ }
588
+ else if (rejectedListenerResults.length > 1) {
589
+ throw new errors_1.MultipleListenerError(rejectedListenerResults.map((rlr) => rlr.reason));
590
+ }
591
+ });
592
+ }
593
+ catch (error) {
594
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
595
+ const e = error;
596
+ // disabling due to https://github.com/typescript-eslint/typescript-eslint/issues/1277
597
+ // eslint-disable-next-line consistent-return
598
+ return this.handleError({
599
+ context,
600
+ error: e,
601
+ logger: this.logger,
602
+ body: bodyArg,
603
+ });
604
+ }
605
+ }
606
+ /**
607
+ * Global error handler. The final destination for all errors (hopefully).
608
+ */
609
+ handleError(args) {
610
+ const { error, ...rest } = args;
611
+ return this.extendedErrorHandler && this.hasCustomErrorHandler ?
612
+ this.errorHandler({ error: (0, errors_1.asCodedError)(error), ...rest }) :
613
+ this.errorHandler((0, errors_1.asCodedError)(error));
614
+ }
615
+ }
616
+ exports.default = App;
617
+ function defaultErrorHandler(logger) {
618
+ return (error) => {
619
+ logger.error(error);
620
+ return Promise.reject(error);
621
+ };
622
+ }
623
+ // -----------
624
+ // singleAuthorization
625
+ function runAuthTestForBotToken(client, authorization) {
626
+ // TODO: warn when something needed isn't found
627
+ return authorization.botUserId !== undefined && authorization.botId !== undefined ?
628
+ Promise.resolve({ botUserId: authorization.botUserId, botId: authorization.botId }) :
629
+ client.auth.test({ token: authorization.botToken }).then((result) => ({
630
+ botUserId: result.user_id,
631
+ botId: result.bot_id,
632
+ }));
633
+ }
634
+ async function buildAuthorizeResult(isEnterpriseInstall, authTestResult, authorization) {
635
+ return { isEnterpriseInstall, botToken: authorization.botToken, ...(await authTestResult) };
636
+ }
637
+ function singleAuthorization(client, authorization, tokenVerificationEnabled) {
638
+ // As Authorize function has a reference to this local variable,
639
+ // this local variable can behave as auth.test call result cache for the function
640
+ let cachedAuthTestResult;
641
+ if (tokenVerificationEnabled) {
642
+ // call auth.test immediately
643
+ cachedAuthTestResult = runAuthTestForBotToken(client, authorization);
644
+ return async ({ isEnterpriseInstall }) => buildAuthorizeResult(isEnterpriseInstall, cachedAuthTestResult, authorization);
645
+ }
646
+ return async ({ isEnterpriseInstall }) => {
647
+ // hold off calling auth.test API until the first access to authorize function
648
+ cachedAuthTestResult = runAuthTestForBotToken(client, authorization);
649
+ return buildAuthorizeResult(isEnterpriseInstall, cachedAuthTestResult, authorization);
650
+ };
651
+ }
652
+ // ----------------------------
653
+ // For processEvent method
654
+ /**
655
+ * Helper which builds the data structure the authorize hook uses to provide tokens for the context.
656
+ */
657
+ function buildSource(type, channelId, body, isEnterpriseInstall) {
658
+ // NOTE: potentially something that can be optimized, so that each of these conditions isn't evaluated more than once.
659
+ // if this makes it prettier, great! but we should probably check perf before committing to any specific optimization.
660
+ const teamId = (() => {
661
+ if (type === helpers_1.IncomingEventType.Event) {
662
+ const bodyAsEvent = body;
663
+ if (Array.isArray(bodyAsEvent.authorizations) &&
664
+ bodyAsEvent.authorizations[0] !== undefined &&
665
+ bodyAsEvent.authorizations[0].team_id !== null) {
666
+ return bodyAsEvent.authorizations[0].team_id;
667
+ }
668
+ return bodyAsEvent.team_id;
669
+ }
670
+ if (type === helpers_1.IncomingEventType.Command) {
671
+ return body.team_id;
672
+ }
673
+ if (type === helpers_1.IncomingEventType.Action ||
674
+ type === helpers_1.IncomingEventType.Options ||
675
+ type === helpers_1.IncomingEventType.ViewAction ||
676
+ type === helpers_1.IncomingEventType.Shortcut) {
677
+ const bodyAsActionOrOptionsOrViewActionOrShortcut = body;
678
+ // When the app is installed using org-wide deployment, team property will be null
679
+ if (typeof bodyAsActionOrOptionsOrViewActionOrShortcut.team !== 'undefined' &&
680
+ bodyAsActionOrOptionsOrViewActionOrShortcut.team !== null) {
681
+ return bodyAsActionOrOptionsOrViewActionOrShortcut.team.id;
682
+ }
683
+ // This is the only place where this function might return undefined
684
+ return bodyAsActionOrOptionsOrViewActionOrShortcut.user.team_id;
685
+ }
686
+ return (0, helpers_1.assertNever)(type);
687
+ })();
688
+ const enterpriseId = (() => {
689
+ if (type === helpers_1.IncomingEventType.Event) {
690
+ const bodyAsEvent = body;
691
+ if (Array.isArray(bodyAsEvent.authorizations) && bodyAsEvent.authorizations[0] !== undefined) {
692
+ // The enteprise_id here can be null when the workspace is not in an Enterprise Grid
693
+ const theId = bodyAsEvent.authorizations[0].enterprise_id;
694
+ return theId !== null ? theId : undefined;
695
+ }
696
+ return bodyAsEvent.enterprise_id;
697
+ }
698
+ if (type === helpers_1.IncomingEventType.Command) {
699
+ return body.enterprise_id;
700
+ }
701
+ if (type === helpers_1.IncomingEventType.Action ||
702
+ type === helpers_1.IncomingEventType.Options ||
703
+ type === helpers_1.IncomingEventType.ViewAction ||
704
+ type === helpers_1.IncomingEventType.Shortcut) {
705
+ // NOTE: no type system backed exhaustiveness check within this group of incoming event types
706
+ const bodyAsActionOrOptionsOrViewActionOrShortcut = body;
707
+ if (typeof bodyAsActionOrOptionsOrViewActionOrShortcut.enterprise !== 'undefined' &&
708
+ bodyAsActionOrOptionsOrViewActionOrShortcut.enterprise !== null) {
709
+ return bodyAsActionOrOptionsOrViewActionOrShortcut.enterprise.id;
710
+ }
711
+ // When the app is installed using org-wide deployment, team property will be null
712
+ if (typeof bodyAsActionOrOptionsOrViewActionOrShortcut.team !== 'undefined' &&
713
+ bodyAsActionOrOptionsOrViewActionOrShortcut.team !== null) {
714
+ return bodyAsActionOrOptionsOrViewActionOrShortcut.team.enterprise_id;
715
+ }
716
+ return undefined;
717
+ }
718
+ return (0, helpers_1.assertNever)(type);
719
+ })();
720
+ const userId = (() => {
721
+ if (type === helpers_1.IncomingEventType.Event) {
722
+ // NOTE: no type system backed exhaustiveness check within this incoming event type
723
+ const { event } = body;
724
+ if ('user' in event) {
725
+ if (typeof event.user === 'string') {
726
+ return event.user;
727
+ }
728
+ if (typeof event.user === 'object') {
729
+ return event.user.id;
730
+ }
731
+ }
732
+ if ('channel' in event && typeof event.channel !== 'string' && 'creator' in event.channel) {
733
+ return event.channel.creator;
734
+ }
735
+ if ('subteam' in event && event.subteam.created_by !== undefined) {
736
+ return event.subteam.created_by;
737
+ }
738
+ return undefined;
739
+ }
740
+ if (type === helpers_1.IncomingEventType.Action ||
741
+ type === helpers_1.IncomingEventType.Options ||
742
+ type === helpers_1.IncomingEventType.ViewAction ||
743
+ type === helpers_1.IncomingEventType.Shortcut) {
744
+ // NOTE: no type system backed exhaustiveness check within this incoming event type
745
+ const bodyAsActionOrOptionsOrViewActionOrShortcut = body;
746
+ return bodyAsActionOrOptionsOrViewActionOrShortcut.user.id;
747
+ }
748
+ if (type === helpers_1.IncomingEventType.Command) {
749
+ return body.user_id;
750
+ }
751
+ return (0, helpers_1.assertNever)(type);
752
+ })();
753
+ return {
754
+ userId,
755
+ isEnterpriseInstall,
756
+ teamId: teamId,
757
+ enterpriseId: enterpriseId,
758
+ conversationId: channelId,
759
+ };
760
+ }
761
+ function isBodyWithTypeEnterpriseInstall(body, type) {
762
+ if (type === helpers_1.IncomingEventType.Event) {
763
+ const bodyAsEvent = body;
764
+ if (Array.isArray(bodyAsEvent.authorizations) && bodyAsEvent.authorizations[0] !== undefined) {
765
+ return !!bodyAsEvent.authorizations[0].is_enterprise_install;
766
+ }
767
+ }
768
+ // command payloads have this property set as a string
769
+ if (typeof body.is_enterprise_install === 'string') {
770
+ return body.is_enterprise_install === 'true';
771
+ }
772
+ // all remaining types have a boolean property
773
+ if (body.is_enterprise_install !== undefined) {
774
+ return body.is_enterprise_install;
775
+ }
776
+ // as a fallback we assume it's a single team installation (but this should never happen)
777
+ return false;
778
+ }
779
+ function isBlockActionOrInteractiveMessageBody(body) {
780
+ return body.actions !== undefined;
781
+ }
782
+ // Returns either a bot token or a user token for client, say()
783
+ function selectToken(context) {
784
+ return context.botToken !== undefined ? context.botToken : context.userToken;
785
+ }
786
+ function buildRespondFn(axiosInstance, responseUrl) {
787
+ return async (message) => {
788
+ const normalizedArgs = typeof message === 'string' ? { text: message } : message;
789
+ return axiosInstance.post(responseUrl, normalizedArgs);
790
+ };
791
+ }
792
+ // ----------------------------
793
+ // Instrumentation
794
+ // Don't change the position of the following code
795
+ (0, web_api_1.addAppMetadata)({ name: packageJson.name, version: packageJson.version });
796
+ //# sourceMappingURL=App.js.map