@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.
- package/dist/App.d.ts +212 -0
- package/dist/App.d.ts.map +1 -0
- package/dist/App.js +796 -0
- package/dist/App.js.map +1 -0
- package/dist/WorkflowStep.d.ts +106 -0
- package/dist/WorkflowStep.d.ts.map +1 -0
- package/dist/WorkflowStep.js +196 -0
- package/dist/WorkflowStep.js.map +1 -0
- package/dist/conversation-store.d.ts +31 -0
- package/dist/conversation-store.d.ts.map +1 -0
- package/dist/conversation-store.js +72 -0
- package/dist/conversation-store.js.map +1 -0
- package/dist/errors.d.ts +79 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +120 -0
- package/dist/errors.js.map +1 -0
- package/dist/helpers.d.ts +24 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +100 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/builtin.d.ts +52 -0
- package/dist/middleware/builtin.d.ts.map +1 -0
- package/dist/middleware/builtin.js +311 -0
- package/dist/middleware/builtin.js.map +1 -0
- package/dist/middleware/process.d.ts +5 -0
- package/dist/middleware/process.d.ts.map +1 -0
- package/dist/middleware/process.js +25 -0
- package/dist/middleware/process.js.map +1 -0
- package/dist/receivers/AwsLambdaReceiver.d.ts +53 -0
- package/dist/receivers/AwsLambdaReceiver.d.ts.map +1 -0
- package/dist/receivers/AwsLambdaReceiver.js +188 -0
- package/dist/receivers/AwsLambdaReceiver.js.map +1 -0
- package/dist/receivers/ExpressReceiver.d.ts +77 -0
- package/dist/receivers/ExpressReceiver.d.ts.map +1 -0
- package/dist/receivers/ExpressReceiver.js +445 -0
- package/dist/receivers/ExpressReceiver.js.map +1 -0
- package/dist/receivers/HTTPReceiver.d.ts +109 -0
- package/dist/receivers/HTTPReceiver.d.ts.map +1 -0
- package/dist/receivers/HTTPReceiver.js +501 -0
- package/dist/receivers/HTTPReceiver.js.map +1 -0
- package/dist/receivers/SocketModeReceiver.d.ts +61 -0
- package/dist/receivers/SocketModeReceiver.d.ts.map +1 -0
- package/dist/receivers/SocketModeReceiver.js +175 -0
- package/dist/receivers/SocketModeReceiver.js.map +1 -0
- package/dist/receivers/custom-routes.d.ts +14 -0
- package/dist/receivers/custom-routes.d.ts.map +1 -0
- package/dist/receivers/custom-routes.js +33 -0
- package/dist/receivers/custom-routes.js.map +1 -0
- package/dist/receivers/http-utils.d.ts +5 -0
- package/dist/receivers/http-utils.d.ts.map +1 -0
- package/dist/receivers/http-utils.js +35 -0
- package/dist/receivers/http-utils.js.map +1 -0
- package/dist/receivers/render-html-for-install-path.d.ts +3 -0
- package/dist/receivers/render-html-for-install-path.d.ts.map +1 -0
- package/dist/receivers/render-html-for-install-path.js +25 -0
- package/dist/receivers/render-html-for-install-path.js.map +1 -0
- package/dist/receivers/verify-redirect-opts.d.ts +7 -0
- package/dist/receivers/verify-redirect-opts.d.ts.map +1 -0
- package/dist/receivers/verify-redirect-opts.js +23 -0
- package/dist/receivers/verify-redirect-opts.js.map +1 -0
- package/dist/receivers/verify-request.d.ts +41 -0
- package/dist/receivers/verify-request.d.ts.map +1 -0
- package/dist/receivers/verify-request.js +98 -0
- package/dist/receivers/verify-request.js.map +1 -0
- package/dist/types/actions/block-action.d.ts +231 -0
- package/dist/types/actions/block-action.d.ts.map +1 -0
- package/dist/types/actions/block-action.js +3 -0
- package/dist/types/actions/block-action.js.map +1 -0
- package/dist/types/actions/dialog-action.d.ts +47 -0
- package/dist/types/actions/dialog-action.d.ts.map +1 -0
- package/dist/types/actions/dialog-action.js +3 -0
- package/dist/types/actions/dialog-action.js.map +1 -0
- package/dist/types/actions/index.d.ts +46 -0
- package/dist/types/actions/index.d.ts.map +1 -0
- package/dist/types/actions/index.js +17 -0
- package/dist/types/actions/index.js.map +1 -0
- package/dist/types/actions/interactive-message.d.ts +66 -0
- package/dist/types/actions/interactive-message.d.ts.map +1 -0
- package/dist/types/actions/interactive-message.js +3 -0
- package/dist/types/actions/interactive-message.js.map +1 -0
- package/dist/types/actions/workflow-step-edit.d.ts +49 -0
- package/dist/types/actions/workflow-step-edit.d.ts.map +1 -0
- package/dist/types/actions/workflow-step-edit.js +3 -0
- package/dist/types/actions/workflow-step-edit.js.map +1 -0
- package/dist/types/command/index.d.ts +36 -0
- package/dist/types/command/index.d.ts.map +1 -0
- package/dist/types/command/index.js +3 -0
- package/dist/types/command/index.js.map +1 -0
- package/dist/types/events/base-events.d.ts +699 -0
- package/dist/types/events/base-events.d.ts.map +1 -0
- package/dist/types/events/base-events.js +5 -0
- package/dist/types/events/base-events.js.map +1 -0
- package/dist/types/events/index.d.ts +63 -0
- package/dist/types/events/index.d.ts.map +1 -0
- package/dist/types/events/index.js +14 -0
- package/dist/types/events/index.js.map +1 -0
- package/dist/types/events/message-events.d.ts +312 -0
- package/dist/types/events/message-events.d.ts.map +1 -0
- package/dist/types/events/message-events.js +3 -0
- package/dist/types/events/message-events.js.map +1 -0
- package/dist/types/helpers.d.ts +17 -0
- package/dist/types/helpers.d.ts.map +1 -0
- package/dist/types/helpers.js +3 -0
- package/dist/types/helpers.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +22 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/middleware.d.ts +65 -0
- package/dist/types/middleware.d.ts.map +1 -0
- package/dist/types/middleware.js +14 -0
- package/dist/types/middleware.js.map +1 -0
- package/dist/types/options/index.d.ts +192 -0
- package/dist/types/options/index.d.ts.map +1 -0
- package/dist/types/options/index.js +3 -0
- package/dist/types/options/index.js.map +1 -0
- package/dist/types/receiver.d.ts +16 -0
- package/dist/types/receiver.d.ts.map +1 -0
- package/dist/types/receiver.js +3 -0
- package/dist/types/receiver.js.map +1 -0
- package/dist/types/shortcuts/global-shortcut.d.ts +29 -0
- package/dist/types/shortcuts/global-shortcut.d.ts.map +1 -0
- package/dist/types/shortcuts/global-shortcut.js +3 -0
- package/dist/types/shortcuts/global-shortcut.js.map +1 -0
- package/dist/types/shortcuts/index.d.ts +23 -0
- package/dist/types/shortcuts/index.d.ts.map +1 -0
- package/dist/types/shortcuts/index.js +16 -0
- package/dist/types/shortcuts/index.js.map +1 -0
- package/dist/types/shortcuts/message-shortcut.d.ts +43 -0
- package/dist/types/shortcuts/message-shortcut.d.ts.map +1 -0
- package/dist/types/shortcuts/message-shortcut.js +3 -0
- package/dist/types/shortcuts/message-shortcut.js.map +1 -0
- package/dist/types/utilities.d.ts +23 -0
- package/dist/types/utilities.d.ts.map +1 -0
- package/dist/types/utilities.js +3 -0
- package/dist/types/utilities.js.map +1 -0
- package/dist/types/view/index.d.ts +175 -0
- package/dist/types/view/index.d.ts.map +1 -0
- package/dist/types/view/index.js +3 -0
- package/dist/types/view/index.js.map +1 -0
- package/package.json +1 -1
- 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
|