crisp-api 9.12.1 → 10.0.2

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 (116) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +6 -2
  3. package/dist/crisp.d.ts +167 -0
  4. package/dist/crisp.js +764 -0
  5. package/dist/resources/BaseResource.d.ts +15 -0
  6. package/dist/resources/BaseResource.js +20 -0
  7. package/dist/resources/BucketURL.d.ts +28 -0
  8. package/dist/resources/BucketURL.js +29 -0
  9. package/dist/resources/MediaAnimation.d.ts +14 -0
  10. package/dist/resources/MediaAnimation.js +32 -0
  11. package/dist/resources/PluginConnect.d.ts +50 -0
  12. package/dist/resources/PluginConnect.js +73 -0
  13. package/dist/resources/PluginSubscription.d.ts +103 -0
  14. package/dist/resources/PluginSubscription.js +122 -0
  15. package/dist/resources/WebsiteAnalytics.d.ts +14 -0
  16. package/dist/resources/WebsiteAnalytics.js +29 -0
  17. package/dist/resources/WebsiteAvailability.d.ts +31 -0
  18. package/dist/resources/WebsiteAvailability.js +36 -0
  19. package/dist/resources/WebsiteBase.d.ts +60 -0
  20. package/dist/resources/WebsiteBase.js +71 -0
  21. package/dist/resources/WebsiteBatch.d.ts +52 -0
  22. package/dist/resources/WebsiteBatch.js +70 -0
  23. package/dist/resources/WebsiteCampaign.d.ts +199 -0
  24. package/dist/resources/WebsiteCampaign.js +194 -0
  25. package/dist/resources/WebsiteConversation.d.ts +701 -0
  26. package/dist/resources/WebsiteConversation.js +595 -0
  27. package/dist/resources/WebsiteHelpdesk.d.ts +347 -0
  28. package/dist/resources/WebsiteHelpdesk.js +587 -0
  29. package/dist/resources/WebsiteOperator.d.ts +79 -0
  30. package/dist/resources/WebsiteOperator.js +93 -0
  31. package/dist/resources/WebsitePeople.d.ts +248 -0
  32. package/dist/resources/WebsitePeople.js +276 -0
  33. package/dist/resources/WebsiteSettings.d.ts +159 -0
  34. package/dist/resources/WebsiteSettings.js +36 -0
  35. package/dist/resources/WebsiteVerify.d.ts +38 -0
  36. package/dist/resources/WebsiteVerify.js +50 -0
  37. package/dist/resources/WebsiteVisitors.d.ts +113 -0
  38. package/dist/resources/WebsiteVisitors.js +88 -0
  39. package/dist/resources/index.d.ts +17 -0
  40. package/dist/resources/index.js +40 -0
  41. package/dist/services/bucket.d.ts +13 -0
  42. package/dist/services/bucket.js +28 -0
  43. package/dist/services/media.d.ts +13 -0
  44. package/dist/services/media.js +28 -0
  45. package/dist/services/plugin.d.ts +14 -0
  46. package/dist/services/plugin.js +30 -0
  47. package/dist/services/website.d.ts +24 -0
  48. package/dist/services/website.js +50 -0
  49. package/eslint.config.mjs +208 -0
  50. package/lib/crisp.ts +957 -0
  51. package/lib/resources/BaseResource.ts +29 -0
  52. package/lib/resources/BucketURL.ts +49 -0
  53. package/lib/resources/MediaAnimation.ts +34 -0
  54. package/lib/resources/PluginConnect.ts +128 -0
  55. package/lib/resources/PluginSubscription.ts +208 -0
  56. package/lib/resources/WebsiteAnalytics.ts +31 -0
  57. package/lib/resources/WebsiteAvailability.ts +54 -0
  58. package/lib/resources/WebsiteBase.ts +108 -0
  59. package/lib/resources/WebsiteBatch.ts +96 -0
  60. package/lib/resources/WebsiteCampaign.ts +399 -0
  61. package/lib/resources/WebsiteConversation.ts +1416 -0
  62. package/lib/resources/WebsiteHelpdesk.ts +982 -0
  63. package/lib/resources/WebsiteOperator.ts +161 -0
  64. package/lib/resources/WebsitePeople.ts +527 -0
  65. package/lib/resources/WebsiteSettings.ts +192 -0
  66. package/lib/resources/WebsiteVerify.ts +76 -0
  67. package/lib/resources/WebsiteVisitors.ts +196 -0
  68. package/lib/resources/index.ts +25 -0
  69. package/lib/services/bucket.ts +28 -0
  70. package/lib/services/media.ts +28 -0
  71. package/lib/services/plugin.ts +32 -0
  72. package/lib/services/website.ts +62 -0
  73. package/package.json +16 -11
  74. package/tsconfig.json +12 -5
  75. package/lib/crisp.js +0 -1168
  76. package/lib/resources/BucketURL.js +0 -34
  77. package/lib/resources/MediaAnimation.js +0 -41
  78. package/lib/resources/PluginConnect.js +0 -119
  79. package/lib/resources/PluginSubscription.js +0 -234
  80. package/lib/resources/WebsiteAnalytics.js +0 -37
  81. package/lib/resources/WebsiteAvailability.js +0 -48
  82. package/lib/resources/WebsiteBase.js +0 -100
  83. package/lib/resources/WebsiteBatch.js +0 -92
  84. package/lib/resources/WebsiteCampaign.js +0 -396
  85. package/lib/resources/WebsiteConversation.js +0 -1261
  86. package/lib/resources/WebsiteHelpdesk.js +0 -1198
  87. package/lib/resources/WebsiteOperator.js +0 -167
  88. package/lib/resources/WebsitePeople.js +0 -516
  89. package/lib/resources/WebsiteSettings.js +0 -50
  90. package/lib/resources/WebsiteVerify.js +0 -79
  91. package/lib/resources/WebsiteVisitors.js +0 -148
  92. package/lib/services/Bucket.js +0 -28
  93. package/lib/services/Media.js +0 -28
  94. package/lib/services/Plugin.js +0 -29
  95. package/lib/services/Website.js +0 -39
  96. package/types/crisp.d.ts +0 -151
  97. package/types/resources/BucketURL.d.ts +0 -15
  98. package/types/resources/MediaAnimation.d.ts +0 -15
  99. package/types/resources/PluginConnect.d.ts +0 -15
  100. package/types/resources/PluginSubscription.d.ts +0 -15
  101. package/types/resources/WebsiteAnalytics.d.ts +0 -15
  102. package/types/resources/WebsiteAvailability.d.ts +0 -15
  103. package/types/resources/WebsiteBase.d.ts +0 -15
  104. package/types/resources/WebsiteBatch.d.ts +0 -15
  105. package/types/resources/WebsiteCampaign.d.ts +0 -15
  106. package/types/resources/WebsiteConversation.d.ts +0 -15
  107. package/types/resources/WebsiteHelpdesk.d.ts +0 -15
  108. package/types/resources/WebsiteOperator.d.ts +0 -15
  109. package/types/resources/WebsitePeople.d.ts +0 -15
  110. package/types/resources/WebsiteSettings.d.ts +0 -15
  111. package/types/resources/WebsiteVerify.d.ts +0 -15
  112. package/types/resources/WebsiteVisitors.d.ts +0 -15
  113. package/types/services/Bucket.d.ts +0 -14
  114. package/types/services/Media.d.ts +0 -14
  115. package/types/services/Plugin.d.ts +0 -14
  116. package/types/services/Website.d.ts +0 -14
package/dist/crisp.js ADDED
@@ -0,0 +1,764 @@
1
+ "use strict";
2
+ /*
3
+ * node-crisp-api
4
+ *
5
+ * Copyright 2022, Crisp IM SAS
6
+ * Author: Baptiste Jamin <baptiste@crisp.chat>
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
20
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
+ };
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.Crisp = void 0;
27
+ /**************************************************************************
28
+ * IMPORTS
29
+ ***************************************************************************/
30
+ // NPM
31
+ const got_1 = __importDefault(require("got"));
32
+ const socket_io_client_1 = require("socket.io-client");
33
+ const url_1 = require("url");
34
+ const crypto_1 = __importDefault(require("crypto"));
35
+ const mitt_1 = __importDefault(require("mitt"));
36
+ // PROJECT: SERVICES
37
+ const bucket_1 = __importDefault(require("./services/bucket"));
38
+ const media_1 = __importDefault(require("./services/media"));
39
+ const plugin_1 = __importDefault(require("./services/plugin"));
40
+ const website_1 = __importDefault(require("./services/website"));
41
+ // PROJECT: MAIN
42
+ // Export all types from resources
43
+ __exportStar(require("./resources"), exports);
44
+ const AVAILABLE_RTM_MODES = [
45
+ "websockets",
46
+ "webhooks"
47
+ ];
48
+ const VERSION = "10.0.2";
49
+ // Base configuration
50
+ const DEFAULT_REQUEST_TIMEOUT = 10000;
51
+ const DEFAULT_SOCKET_TIMEOUT = 10000;
52
+ const DEFAULT_SOCKET_RECONNECT_DELAY = 5000;
53
+ const DEFAULT_SOCKET_RECONNECT_DELAY_MAX = 10000;
54
+ const DEFAULT_SOCKET_RECONNECT_FACTOR = 0.75;
55
+ const DEFAULT_BROKER_SCHEDULE = 500;
56
+ const DEFAULT_EVENT_REBIND_INTERVAL_MIN = 2500;
57
+ const DEFAULT_USERAGENT_PREFIX = "node-crisp-api/";
58
+ // REST API defaults
59
+ const DEFAULT_REST_HOST = "https://api.crisp.chat";
60
+ const DEFAULT_REST_BASE_PATH = "/v1/";
61
+ // RTM API defaults
62
+ const DEFAULT_RTM_MODE = "websockets";
63
+ const DEFAULT_RTM_EVENTS = [
64
+ // Session Events
65
+ "session:update_availability",
66
+ "session:update_verify",
67
+ "session:request:initiated",
68
+ "session:set_email",
69
+ "session:set_phone",
70
+ "session:set_address",
71
+ "session:set_subject",
72
+ "session:set_avatar",
73
+ "session:set_nickname",
74
+ "session:set_origin",
75
+ "session:set_data",
76
+ "session:sync:pages",
77
+ "session:sync:events",
78
+ "session:sync:capabilities",
79
+ "session:sync:geolocation",
80
+ "session:sync:system",
81
+ "session:sync:network",
82
+ "session:sync:timezone",
83
+ "session:sync:locales",
84
+ "session:sync:rating",
85
+ "session:sync:topic",
86
+ "session:set_state",
87
+ "session:set_block",
88
+ "session:set_segments",
89
+ "session:set_opened",
90
+ "session:set_closed",
91
+ "session:set_participants",
92
+ "session:set_mentions",
93
+ "session:set_routing",
94
+ "session:set_inbox",
95
+ "session:removed",
96
+ "session:error",
97
+ // Message Events
98
+ "message:updated",
99
+ "message:send",
100
+ "message:received",
101
+ "message:removed",
102
+ "message:compose:send",
103
+ "message:compose:receive",
104
+ "message:acknowledge:read:send",
105
+ "message:acknowledge:read:received",
106
+ "message:acknowledge:unread:send",
107
+ "message:acknowledge:delivered",
108
+ "message:acknowledge:ignored",
109
+ "message:notify:unread:send",
110
+ "message:notify:unread:received",
111
+ // Spam Events
112
+ "spam:message",
113
+ "spam:decision",
114
+ // People Events
115
+ "people:profile:created",
116
+ "people:profile:updated",
117
+ "people:profile:removed",
118
+ "people:bind:session",
119
+ "people:sync:profile",
120
+ "people:import:progress",
121
+ "people:import:done",
122
+ // Campaign Events
123
+ "campaign:progress",
124
+ "campaign:dispatched",
125
+ "campaign:running",
126
+ // Browsing Events
127
+ "browsing:request:initiated",
128
+ "browsing:request:rejected",
129
+ // Call Events
130
+ "call:request:initiated",
131
+ "call:request:rejected",
132
+ // Identity Events
133
+ "identity:verify:request",
134
+ // Status Events
135
+ "status:health:changed",
136
+ // Website Event
137
+ "website:update_visitors_count",
138
+ "website:update_operators_availability",
139
+ "website:users:available",
140
+ // Bucket Events
141
+ "bucket:url:upload:generated",
142
+ "bucket:url:avatar:generated",
143
+ "bucket:url:website:generated",
144
+ "bucket:url:campaign:generated",
145
+ "bucket:url:helpdesk:generated",
146
+ "bucket:url:status:generated",
147
+ "bucket:url:processing:generated",
148
+ "bucket:url:crawler:generated",
149
+ // Media Events
150
+ "media:animation:listed",
151
+ // Email Event
152
+ "email:subscribe",
153
+ "email:track:view",
154
+ // Plugin Events
155
+ "plugin:channel",
156
+ "plugin:event",
157
+ "plugin:settings:saved"
158
+ ];
159
+ // REST API services
160
+ const services = {
161
+ Bucket: bucket_1.default,
162
+ Media: media_1.default,
163
+ Plugin: plugin_1.default,
164
+ Website: website_1.default
165
+ };
166
+ /**
167
+ * Crisp API Library
168
+ */
169
+ class Crisp {
170
+ /**
171
+ * Constructor
172
+ */
173
+ constructor() {
174
+ this.bucket = new bucket_1.default();
175
+ this.media = new media_1.default();
176
+ this.plugin = new plugin_1.default();
177
+ this.website = new website_1.default();
178
+ this.auth = {
179
+ tier: "user",
180
+ identifier: null,
181
+ key: null,
182
+ token: null
183
+ };
184
+ this._rest = {
185
+ host: DEFAULT_REST_HOST,
186
+ basePath: DEFAULT_REST_BASE_PATH
187
+ };
188
+ this._rtm = {
189
+ host: "",
190
+ mode: DEFAULT_RTM_MODE
191
+ };
192
+ this._useragent = (DEFAULT_USERAGENT_PREFIX + VERSION);
193
+ this._emitter = (0, mitt_1.default)();
194
+ this._socket = null;
195
+ this._loopback = null;
196
+ this._lastEventRebind = null;
197
+ this._brokerScheduler = null;
198
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, no-unused-vars
199
+ this._brokerBindHooks = [];
200
+ this._boundEvents = {};
201
+ this._prepareServices();
202
+ }
203
+ /**
204
+ * Sets the REST API host
205
+ */
206
+ setRestHost(host) {
207
+ if (typeof host === "string") {
208
+ this._rest.host = host;
209
+ }
210
+ else {
211
+ throw new Error("[Crisp] setRestHost: parameter host should be a string");
212
+ }
213
+ }
214
+ /**
215
+ * Sets the RTM API host
216
+ */
217
+ setRtmHost(host) {
218
+ if (typeof host === "string") {
219
+ this._rtm.host = host;
220
+ }
221
+ else {
222
+ throw new Error("[Crisp] setRtmHost: parameter host should be a string");
223
+ }
224
+ }
225
+ /**
226
+ * Sets the RTM channel mode (ie. WebSockets or Web Hooks)
227
+ */
228
+ setRtmMode(mode) {
229
+ if (AVAILABLE_RTM_MODES.indexOf(mode) !== -1) {
230
+ this._rtm.mode = mode;
231
+ }
232
+ else {
233
+ throw new Error("[Crisp] setRtmMode: parameter mode value should be one of: " +
234
+ AVAILABLE_RTM_MODES.join(", "));
235
+ }
236
+ }
237
+ /**
238
+ * Sets the authentication tier
239
+ */
240
+ setTier(tier) {
241
+ this.auth.tier = (tier || "user");
242
+ }
243
+ /**
244
+ * Authenticates
245
+ */
246
+ authenticate(identifier, key) {
247
+ // Store credentials
248
+ this.auth.identifier = identifier;
249
+ this.auth.key = key;
250
+ // Assign pre-computed authentication token
251
+ this.auth.token = Buffer.from(identifier + ":" + key).toString("base64");
252
+ }
253
+ /**
254
+ * Authenticates (with tier)
255
+ */
256
+ authenticateTier(tier, identifier, key) {
257
+ this.setTier(tier);
258
+ this.authenticate(identifier, key);
259
+ }
260
+ /* eslint-disable @typescript-eslint/no-explicit-any */
261
+ /**
262
+ * Method wrapper to HEAD a resource
263
+ */
264
+ head(resource, query) {
265
+ return new Promise((resolve, reject) => {
266
+ this.request(resource, "head", (query || {}), null, resolve, reject);
267
+ });
268
+ }
269
+ /**
270
+ * Method wrapper to GET a resource
271
+ */
272
+ get(resource, query) {
273
+ return new Promise((resolve, reject) => {
274
+ this.request(resource, "get", (query || {}), null, resolve, reject);
275
+ });
276
+ }
277
+ /**
278
+ * Method wrapper to POST a resource
279
+ */
280
+ post(resource, query, body) {
281
+ return new Promise((resolve, reject) => {
282
+ this.request(resource, "post", (query || {}), (body || {}), resolve, reject);
283
+ });
284
+ }
285
+ /**
286
+ * Method wrapper to PATCH a resource
287
+ */
288
+ patch(resource, query, body) {
289
+ return new Promise((resolve, reject) => {
290
+ this.request(resource, "patch", (query || {}), (body || {}), resolve, reject);
291
+ });
292
+ }
293
+ /**
294
+ * Method wrapper to PUT a resource
295
+ */
296
+ put(resource, query, body) {
297
+ return new Promise((resolve, reject) => {
298
+ this.request(resource, "put", (query || {}), (body || {}), resolve, reject);
299
+ });
300
+ }
301
+ /**
302
+ * Method wrapper to DELETE a resource
303
+ */
304
+ delete(resource, query, body) {
305
+ return new Promise((resolve, reject) => {
306
+ this.request(resource, "delete", (query || {}), (body || null), resolve, reject);
307
+ });
308
+ }
309
+ /* eslint-enable @typescript-eslint/no-explicit-any */
310
+ /**
311
+ * Binds RTM event
312
+ */
313
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, no-unused-vars
314
+ on(event, callback) {
315
+ // Ensure all input arguments are set
316
+ if (typeof event !== "string") {
317
+ throw new Error("[Crisp] on: parameter event should be a string");
318
+ }
319
+ if (typeof callback !== "function") {
320
+ throw new Error("[Crisp] on: parameter callback should be a function");
321
+ }
322
+ // Disallow unrecognized event names
323
+ if (DEFAULT_RTM_EVENTS.indexOf(event) === -1) {
324
+ throw new Error("[Crisp] on: parameter event value is not recognized: '" + event + "'");
325
+ }
326
+ // Important: we do not allow .on() to be called once socket is connected, \
327
+ // or loopback is bound as we consider event listeners must be bound \
328
+ // once all together. This prevents bogous integrations from sending \
329
+ // flood of 'socket:bind'` to the RTM API, if using WebSockets. Web \
330
+ // Hooks follows the same scheme for consistency's sake.
331
+ if (this._socket || this._loopback) {
332
+ throw new Error("[Crisp] on: connector is already bound, please listen to event " +
333
+ "earlier on: '" + event + "'");
334
+ }
335
+ // Add listener to emitter
336
+ this._emitter.on(event, callback);
337
+ // Subscribe event on the broker
338
+ if (this._boundEvents[event] !== true) {
339
+ let rtmMode = this._rtm.mode;
340
+ // Mark event as bound
341
+ this._boundEvents[event] = true;
342
+ // Broker not connected? Connect now.
343
+ return this.prepareBroker((instance, emitter) => {
344
+ // Listen for event? (once instance is bound)
345
+ switch (rtmMode) {
346
+ case "websockets": {
347
+ // Listen on socket event
348
+ instance.on(event, (data) => {
349
+ emitter.emit(event, data);
350
+ });
351
+ break;
352
+ }
353
+ }
354
+ });
355
+ }
356
+ return Promise.resolve();
357
+ }
358
+ /**
359
+ * Receives a raw event and dispatches it to the listener (used for Web Hooks)
360
+ */
361
+ receiveHook(body) {
362
+ if (this._loopback) {
363
+ // Ensure payload is readable
364
+ if (!body || typeof body !== "object") {
365
+ return new Error("[Crisp] receiveHook: empty hook payload");
366
+ }
367
+ // Ensure payload is properly formatted
368
+ if (!body.event || !body.data ||
369
+ typeof body.event !== "string" || typeof body.data !== "object") {
370
+ return new Error("[Crisp] receiveHook: malformatted hook payload");
371
+ }
372
+ // Check if event is subscribed to? (in routing table)
373
+ // Notice: if not in routing table, then silently discard the event w/o \
374
+ // any error, as we do not want an HTTP failure status to be sent in \
375
+ // response by the implementor.
376
+ if (this._boundEvents[body.event] !== true) {
377
+ return null;
378
+ }
379
+ // Dispatch event to event bus
380
+ // Notice: go asynchronous, so that the event is processed ASAP and \
381
+ // dispatched on the event bus later, as the hook might be received \
382
+ // synchronously over HTTP.
383
+ process.nextTick(() => {
384
+ this._loopback.emit(body.event, body.data);
385
+ });
386
+ return null;
387
+ }
388
+ return new Error("[Crisp] receiveHook: hook loopback not bound");
389
+ }
390
+ /**
391
+ * Verifies an event string and checks that signatures match (used for Web \
392
+ * Hooks)
393
+ */
394
+ verifyHook(secret, body, timestamp, signature) {
395
+ if (this._loopback) {
396
+ return this.verifySignature(secret, body, timestamp, signature);
397
+ }
398
+ // Default: not verified (loopback not /yet?/ bound)
399
+ return false;
400
+ }
401
+ /**
402
+ * Verifies an event string and checks that signatures match (used for \
403
+ * Widgets)
404
+ */
405
+ verifyWidget(secret, body, timestamp, signature) {
406
+ return this.verifySignature(secret, body, timestamp, signature);
407
+ }
408
+ /**
409
+ * Rebinds socket events (used for WebSockets)
410
+ */
411
+ rebindSocket() {
412
+ if (!this._socket) {
413
+ throw new Error("[Crisp] rebindSocket: cannot rebind a socket that is not yet bound");
414
+ }
415
+ // Make sure that the library user is not rebinding too frequently (which \
416
+ // is illegal)
417
+ const nowTime = Date.now();
418
+ if (this._lastEventRebind !== null &&
419
+ ((nowTime - this._lastEventRebind) <
420
+ DEFAULT_EVENT_REBIND_INTERVAL_MIN)) {
421
+ throw new Error("[Crisp] rebindSocket: cannot rebind, last rebind was requested too " +
422
+ "recently");
423
+ }
424
+ return Promise.resolve()
425
+ .then(() => {
426
+ // Rebind to socket events (eg. newly bound websites)
427
+ this._lastEventRebind = nowTime;
428
+ this._socket.emit("socket:bind", {});
429
+ return Promise.resolve();
430
+ });
431
+ }
432
+ /**
433
+ * Prepares a URI based from path segments
434
+ */
435
+ prepareRestUrl(paths) {
436
+ if (Array.isArray(paths) === true) {
437
+ let output = this._rest.host + this._rest.basePath;
438
+ output += paths.join("/");
439
+ return output;
440
+ }
441
+ throw new Error("[Crisp] prepareRestUrl: parameter host should be an Array");
442
+ }
443
+ /**
444
+ * Binds services to the main object
445
+ */
446
+ _prepareServices() {
447
+ // Bind services
448
+ for (const name in services) {
449
+ const serviceInstance = new services[name]();
450
+ // Acquire service map
451
+ const serviceMap = this[(name[0].toLowerCase() + name.substring(1))];
452
+ // No resources defined in service?
453
+ if (!serviceInstance.__resources ||
454
+ serviceInstance.__resources.length === 0) {
455
+ throw new Error("[Crisp] prepareServices: service '" + name + "' has no resources " +
456
+ "defined");
457
+ }
458
+ // Prepare all resources (for service)
459
+ this.prepareResources(serviceMap, serviceInstance.__resources);
460
+ }
461
+ }
462
+ /**
463
+ * Binds resources to the service object
464
+ */
465
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
466
+ prepareResources(serviceMap, resources) {
467
+ for (let i = 0; i < resources.length; i++) {
468
+ const resourceConstructor = resources[i];
469
+ const resourceInstance = new resourceConstructor(this);
470
+ // Bind each method of the resource instance to the service map
471
+ for (const methodName of Object.getOwnPropertyNames(Object.getPrototypeOf(resourceInstance))) {
472
+ if (methodName !== "constructor") {
473
+ serviceMap[methodName] = resourceInstance[methodName].bind(resourceInstance);
474
+ }
475
+ }
476
+ }
477
+ }
478
+ /**
479
+ * Binds broker to the main object
480
+ */
481
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, no-unused-vars
482
+ prepareBroker(fnBindHook) {
483
+ return new Promise((resolve, reject) => {
484
+ const rtmMode = this._rtm.mode;
485
+ const rtmHostOverride = this._rtm.host;
486
+ // Append bind hook to pending stack
487
+ this._brokerBindHooks.push(fnBindHook);
488
+ // Make sure to prepare broker once? (defer broker binding, waiting that \
489
+ // all listeners have been bound, that way we submit the list of \
490
+ // filtered events to the RTM API once, and never again in the future)
491
+ if (this._brokerScheduler === null) {
492
+ // Socket or loopback already set? We should not even have entered \
493
+ // there.
494
+ if (this._socket || this._loopback) {
495
+ throw new Error("[Crisp] prepareBroker: illegal call to prepare broker (tie break)");
496
+ }
497
+ // @ts-ignore
498
+ this._brokerScheduler = setTimeout(() => {
499
+ switch (rtmMode) {
500
+ case "websockets": {
501
+ // Connect to socket now
502
+ // Notice: will unstack broker bind hooks once ready
503
+ this.connectSocket(rtmHostOverride)
504
+ .then(resolve)
505
+ .catch(reject);
506
+ break;
507
+ }
508
+ case "webhooks": {
509
+ // Connect to loopback now
510
+ this.connectLoopback()
511
+ .then(resolve)
512
+ .catch(reject);
513
+ break;
514
+ }
515
+ default: {
516
+ const unsupportedError = new Error("[Crisp] prepareBroker: mode of RTM broker unsupported " +
517
+ "('" + rtmMode + "')");
518
+ reject(unsupportedError);
519
+ }
520
+ }
521
+ }, DEFAULT_BROKER_SCHEDULE);
522
+ }
523
+ else {
524
+ // Pass-through
525
+ resolve(true);
526
+ }
527
+ });
528
+ }
529
+ /**
530
+ * Connects loopback (used for Web Hooks)
531
+ */
532
+ connectLoopback() {
533
+ return Promise.resolve()
534
+ .then(() => {
535
+ // Assign emitter to loopback
536
+ this._loopback = this._emitter;
537
+ // Unstack broker bind hooks immediately
538
+ this.unstackBrokerBindHooks(this._loopback);
539
+ return Promise.resolve();
540
+ });
541
+ }
542
+ /**
543
+ * Connects socket, using preferred RTM API host (used for WebSockets)
544
+ */
545
+ connectSocket(rtmHostOverride) {
546
+ return Promise.resolve()
547
+ .then(() => {
548
+ // Any override RTM API host?
549
+ if (rtmHostOverride) {
550
+ return Promise.resolve({
551
+ socket: {
552
+ app: rtmHostOverride
553
+ }
554
+ });
555
+ }
556
+ // Acquire RTM API URL from remote
557
+ let restUrlSegments;
558
+ switch (this.auth.tier) {
559
+ case "plugin": {
560
+ restUrlSegments = ["plugin", "connect", "endpoints"];
561
+ break;
562
+ }
563
+ default: {
564
+ restUrlSegments = ["user", "connect", "endpoints"];
565
+ }
566
+ }
567
+ return this.get(this.prepareRestUrl(restUrlSegments))
568
+ .catch(() => {
569
+ // Void error (consider as empty response)
570
+ return Promise.resolve({});
571
+ });
572
+ })
573
+ .then((endpoints) => {
574
+ var _a, _b, _c;
575
+ // @ts-ignore
576
+ const rtmHostAffinity = (((_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.socket) === null || _a === void 0 ? void 0 : _a.app) || null);
577
+ // No RTM API host acquired?
578
+ if (rtmHostAffinity === null) {
579
+ throw new Error("[Crisp] connectSocket: could not acquire target host to " +
580
+ "connect to, is your session valid for tier?");
581
+ }
582
+ // Parse target RTM API host as an URL object
583
+ const rtmHostUrl = new url_1.URL(rtmHostAffinity);
584
+ // Connect to socket
585
+ // @ts-ignore
586
+ this._socket = (0, socket_io_client_1.io)(rtmHostUrl.origin, {
587
+ path: (rtmHostUrl.pathname || "/"),
588
+ transports: ["websocket"],
589
+ timeout: DEFAULT_SOCKET_TIMEOUT,
590
+ reconnection: true,
591
+ reconnectionDelay: DEFAULT_SOCKET_RECONNECT_DELAY,
592
+ reconnectionDelayMax: DEFAULT_SOCKET_RECONNECT_DELAY_MAX,
593
+ randomizationFactor: DEFAULT_SOCKET_RECONNECT_FACTOR
594
+ });
595
+ this.emitAuthenticateSocket();
596
+ // Setup base socket event listeners
597
+ (_b = this._socket) === null || _b === void 0 ? void 0 : _b.io.on("reconnect", () => {
598
+ this.emitAuthenticateSocket();
599
+ });
600
+ (_c = this._socket) === null || _c === void 0 ? void 0 : _c.on("unauthorized", () => {
601
+ throw new Error("[Crisp] connectSocket: cannot listen for events as " +
602
+ "authentication is invalid");
603
+ });
604
+ // Setup user socket event listeners
605
+ this.unstackBrokerBindHooks(this._socket);
606
+ return Promise.resolve();
607
+ });
608
+ }
609
+ /**
610
+ * Authenticates client (used for WebSockets)
611
+ */
612
+ emitAuthenticateSocket() {
613
+ const auth = this.auth;
614
+ const boundEvents = Object.keys(this._boundEvents);
615
+ if (!this._socket) {
616
+ throw new Error("[Crisp] emitAuthenticateSocket: cannot listen for events as socket " +
617
+ "is not yet bound");
618
+ }
619
+ if (!auth.identifier || !auth.key) {
620
+ throw new Error("[Crisp] emitAuthenticateSocket: cannot listen for events as you " +
621
+ "did not authenticate");
622
+ }
623
+ if (boundEvents.length === 0) {
624
+ throw new Error("[Crisp] emitAuthenticateSocket: cannot listen for events as no " +
625
+ "event is being listened to");
626
+ }
627
+ this._socket.emit("authentication", {
628
+ username: auth.identifier,
629
+ password: auth.key,
630
+ tier: auth.tier,
631
+ events: boundEvents
632
+ });
633
+ }
634
+ /**
635
+ * Unstacks pending broker bind hooks
636
+ */
637
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
638
+ unstackBrokerBindHooks(modeInstance) {
639
+ var _a;
640
+ // Setup user socket event listeners
641
+ while (this._brokerBindHooks.length > 0) {
642
+ (_a = this._brokerBindHooks.shift()) === null || _a === void 0 ? void 0 : _a(modeInstance, this._emitter);
643
+ }
644
+ }
645
+ /**
646
+ * Performs a request to REST API
647
+ */
648
+ request(resource, method, query, body,
649
+ // eslint-disable-next-line no-unused-vars
650
+ resolve,
651
+ // eslint-disable-next-line no-unused-vars
652
+ reject) {
653
+ let requestParameters = {
654
+ responseType: "json",
655
+ timeout: DEFAULT_REQUEST_TIMEOUT,
656
+ headers: {
657
+ "User-Agent": this._useragent,
658
+ "X-Crisp-Tier": this.auth.tier
659
+ },
660
+ throwHttpErrors: false
661
+ };
662
+ // Add authorization?
663
+ if (this.auth.token) {
664
+ // @ts-ignore
665
+ requestParameters.headers.Authorization = ("Basic " + this.auth.token);
666
+ }
667
+ // Add body?
668
+ if (body) {
669
+ // @ts-ignore
670
+ requestParameters.json = body;
671
+ }
672
+ // Add query?
673
+ if (query) {
674
+ // @ts-ignore
675
+ requestParameters.searchParams = query;
676
+ }
677
+ // Proceed request
678
+ got_1.default[method](resource, requestParameters)
679
+ .catch((error) => {
680
+ return Promise.resolve(error);
681
+ })
682
+ .then((response) => {
683
+ var _a, _b, _c;
684
+ // Request error?
685
+ if (!response.statusCode) {
686
+ return reject({
687
+ reason: "error",
688
+ message: "internal_error",
689
+ code: 500,
690
+ data: {
691
+ namespace: "request",
692
+ message: ("Got request error: " + (response.name || "Unknown"))
693
+ }
694
+ });
695
+ }
696
+ // Response error?
697
+ if (response.statusCode >= 400) {
698
+ let reasonMessage = this.readErrorResponseReason(method, response.statusCode, response);
699
+ const dataMessage = (((_b = (_a = response === null || response === void 0 ? void 0 : response.body) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) || "");
700
+ return reject({
701
+ reason: "error",
702
+ message: reasonMessage,
703
+ code: response.statusCode,
704
+ data: {
705
+ namespace: "response",
706
+ message: ("Got response error: " + (dataMessage || reasonMessage))
707
+ }
708
+ });
709
+ }
710
+ // Regular response
711
+ return resolve((((_c = response === null || response === void 0 ? void 0 : response.body) === null || _c === void 0 ? void 0 : _c.data) || {}));
712
+ });
713
+ }
714
+ /**
715
+ * Reads reason for error response
716
+ */
717
+ readErrorResponseReason(method, statusCode, response) {
718
+ var _a;
719
+ // HEAD method? As HEAD requests do not expect any response body, then we \
720
+ // cannot map a reason from the response.
721
+ if (method === "head") {
722
+ // 5xx errors?
723
+ if (statusCode >= 500) {
724
+ return "server_error";
725
+ }
726
+ // 4xx errors?
727
+ if (statusCode >= 400) {
728
+ return "route_error";
729
+ }
730
+ }
731
+ // Other methods must hold a response body, therefore we can fallback on \
732
+ // an HTTP error if we fail to acquire any reason at all.
733
+ // @ts-ignore
734
+ return ((((_a = response === null || response === void 0 ? void 0 : response.body) === null || _a === void 0 ? void 0 : _a.reason) || "http_error"));
735
+ }
736
+ /**
737
+ * Verifies an event string and checks that signatures match
738
+ */
739
+ verifySignature(secret, body, timestamp, signature) {
740
+ // Ensure all provided data is valid
741
+ if (!secret || !signature || !body || typeof body !== "object" ||
742
+ !timestamp || isNaN(timestamp) === true) {
743
+ return false;
744
+ }
745
+ // Compute local trace
746
+ let localTrace = ("[" + timestamp + ";" + JSON.stringify(body) + "]");
747
+ // Create local HMAC
748
+ let localMac = crypto_1.default.createHmac("sha256", secret);
749
+ localMac.update(localTrace);
750
+ // Compute local signature, and compare
751
+ let localSignature = localMac.digest("hex");
752
+ return ((signature === localSignature) ? true : false);
753
+ }
754
+ }
755
+ exports.Crisp = Crisp;
756
+ /**
757
+ * @deprecated Use import { RTM_MODES } instead
758
+ */
759
+ Crisp.RTM_MODES = {
760
+ WebSockets: "websockets",
761
+ WebHooks: "webhooks"
762
+ };
763
+ ;
764
+ exports.default = Crisp;