crisp-api 9.13.0 → 10.0.3

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