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/lib/crisp.js DELETED
@@ -1,1168 +0,0 @@
1
- /*
2
- * node-crisp-api
3
- *
4
- * Copyright 2022, Crisp IM SAS
5
- * Author: Baptiste Jamin <baptiste@crisp.chat>
6
- */
7
-
8
-
9
- "use strict";
10
-
11
-
12
- // Imports
13
- var pkg = require("../package.json");
14
- var got = require("got");
15
-
16
- var URL = require("url").URL;
17
- var Crypto = require("crypto");
18
- var EventEmitter = require("fbemitter").EventEmitter;
19
-
20
-
21
- // RTM modes available
22
- Crisp.RTM_MODES = {
23
- WebSockets : "websockets",
24
- WebHooks : "webhooks"
25
- };
26
-
27
- Crisp.AVAILABLE_RTM_MODES = [
28
- Crisp.RTM_MODES.WebSockets,
29
- Crisp.RTM_MODES.WebHooks
30
- ];
31
-
32
-
33
- // Base configuration
34
- Crisp.DEFAULT_REQUEST_TIMEOUT = 10000;
35
- Crisp.DEFAULT_SOCKET_TIMEOUT = 10000;
36
- Crisp.DEFAULT_SOCKET_RECONNECT_DELAY = 5000;
37
- Crisp.DEFAULT_SOCKET_RECONNECT_DELAY_MAX = 10000;
38
- Crisp.DEFAULT_SOCKET_RECONNECT_FACTOR = 0.75;
39
- Crisp.DEFAULT_BROKER_SCHEDULE = 500;
40
- Crisp.DEFAULT_EVENT_REBIND_INTERVAL_MIN = 2500;
41
- Crisp.DEFAULT_USERAGENT_PREFIX = "node-crisp-api/";
42
-
43
-
44
- // REST API defaults
45
- Crisp.DEFAULT_REST_HOST = "https://api.crisp.chat";
46
- Crisp.DEFAULT_REST_BASE_PATH = "/v1/";
47
-
48
-
49
- // RTM API defaults
50
- Crisp.DEFAULT_RTM_MODE = Crisp.RTM_MODES.WebSockets;
51
-
52
- Crisp.DEFAULT_RTM_EVENTS = [
53
- // Session Events
54
- "session:update_availability",
55
- "session:update_verify",
56
- "session:request:initiated",
57
- "session:set_email",
58
- "session:set_phone",
59
- "session:set_address",
60
- "session:set_subject",
61
- "session:set_avatar",
62
- "session:set_nickname",
63
- "session:set_origin",
64
- "session:set_data",
65
- "session:sync:pages",
66
- "session:sync:events",
67
- "session:sync:capabilities",
68
- "session:sync:geolocation",
69
- "session:sync:system",
70
- "session:sync:network",
71
- "session:sync:timezone",
72
- "session:sync:locales",
73
- "session:sync:rating",
74
- "session:sync:topic",
75
- "session:set_state",
76
- "session:set_block",
77
- "session:set_segments",
78
- "session:set_opened",
79
- "session:set_closed",
80
- "session:set_participants",
81
- "session:set_mentions",
82
- "session:set_routing",
83
- "session:set_inbox",
84
- "session:removed",
85
- "session:error",
86
-
87
- // Message Events
88
- "message:updated",
89
- "message:send",
90
- "message:received",
91
- "message:removed",
92
- "message:compose:send",
93
- "message:compose:receive",
94
- "message:acknowledge:read:send",
95
- "message:acknowledge:read:received",
96
- "message:acknowledge:unread:send",
97
- "message:acknowledge:delivered",
98
- "message:acknowledge:ignored",
99
- "message:notify:unread:send",
100
- "message:notify:unread:received",
101
-
102
- // Spam Events
103
- "spam:message",
104
- "spam:decision",
105
-
106
- // People Events
107
- "people:profile:created",
108
- "people:profile:updated",
109
- "people:profile:removed",
110
- "people:bind:session",
111
- "people:sync:profile",
112
- "people:import:progress",
113
- "people:import:done",
114
-
115
- // Campaign Events
116
- "campaign:progress",
117
- "campaign:dispatched",
118
- "campaign:running",
119
-
120
- // Browsing Events
121
- "browsing:request:initiated",
122
- "browsing:request:rejected",
123
-
124
- // Call Events
125
- "call:request:initiated",
126
- "call:request:rejected",
127
-
128
- // Status Events
129
- "status:health:changed",
130
-
131
- // Website Event
132
- "website:update_visitors_count",
133
- "website:update_operators_availability",
134
- "website:users:available",
135
-
136
- // Bucket Events
137
- "bucket:url:upload:generated",
138
- "bucket:url:avatar:generated",
139
- "bucket:url:website:generated",
140
- "bucket:url:campaign:generated",
141
- "bucket:url:helpdesk:generated",
142
- "bucket:url:status:generated",
143
- "bucket:url:processing:generated",
144
- "bucket:url:crawler:generated",
145
-
146
- // Media Events
147
- "media:animation:listed",
148
-
149
- // Email Event
150
- "email:subscribe",
151
- "email:track:view",
152
-
153
- // Plugin Events
154
- "plugin:channel",
155
- "plugin:event",
156
- "plugin:settings:saved",
157
- ];
158
-
159
-
160
- // REST API services
161
- var services = {
162
- Bucket : require("./services/Bucket"),
163
- Media : require("./services/Media"),
164
- Plugin : require("./services/Plugin"),
165
- Website : require("./services/Website")
166
- };
167
-
168
-
169
- /**
170
- * Crisp API Library
171
- * @class
172
- * @classdesc This is the Crisp Library. Handles REST and RTM operations
173
- */
174
- function Crisp() {
175
- /**
176
- * @public
177
- * @type {*}
178
- */
179
- this.bucket = {};
180
-
181
- /**
182
- * @public
183
- * @type {*}
184
- */
185
- this.media = {};
186
-
187
- /**
188
- * @public
189
- * @type {*}
190
- */
191
- this.plugin = {};
192
-
193
- /**
194
- * @public
195
- * @type {*}
196
- */
197
- this.website = {};
198
-
199
- /**
200
- * @public
201
- * @type {object}
202
- */
203
- this.auth = {
204
- tier : "user",
205
- identifier : null,
206
- key : null,
207
- token : null
208
- };
209
-
210
- /**
211
- * @private
212
- * @type {object}
213
- */
214
- this._rest = {
215
- host : Crisp.DEFAULT_REST_HOST,
216
- basePath : Crisp.DEFAULT_REST_BASE_PATH
217
- };
218
-
219
- /**
220
- * @private
221
- * @type {object}
222
- */
223
- this._rtm = {
224
- host : null,
225
- mode : Crisp.DEFAULT_RTM_MODE
226
- };
227
-
228
- /**
229
- * @private
230
- * @type {string}
231
- */
232
- this._useragent = (Crisp.DEFAULT_USERAGENT_PREFIX + pkg.version);
233
-
234
- /**
235
- * @private
236
- * @type {object}
237
- */
238
- this._emitter = new EventEmitter();
239
-
240
- /**
241
- * @private
242
- * @type {object|null}
243
- */
244
- this._socket = null;
245
-
246
- /**
247
- * @private
248
- * @type {object|null}
249
- */
250
- this._loopback = null;
251
-
252
- /**
253
- * @private
254
- * @type {number|null}
255
- */
256
- this._lastEventRebind = null;
257
-
258
- /**
259
- * @private
260
- * @type {object|null}
261
- */
262
- this._brokerScheduler = null;
263
-
264
- /**
265
- * @private
266
- * @type {Array}
267
- */
268
- this._brokerBindHooks = [];
269
-
270
- /**
271
- * @private
272
- * @type {object}
273
- */
274
- this._boundEvents = {};
275
-
276
- // Prepare
277
- this._prepareServices();
278
- }
279
-
280
-
281
- Crisp.prototype = {
282
- /**
283
- * Sets the REST API host
284
- * @memberof Crisp
285
- * @public
286
- * @method setRestHost
287
- * @param {string} host - Hostname
288
- * @return {undefined}
289
- */
290
- setRestHost : function(host) {
291
- if (typeof host === "string") {
292
- this._rest.host = host;
293
- } else {
294
- throw new Error("[Crisp] setRestHost: parameter host should be a string");
295
- }
296
- },
297
-
298
- /**
299
- * Sets the RTM API host
300
- * @memberof Crisp
301
- * @public
302
- * @method setRtmHost
303
- * @param {string} host - Hostname
304
- * @return {undefined}
305
- */
306
- setRtmHost : function(host) {
307
- if (typeof host === "string") {
308
- this._rtm.host = host;
309
- } else {
310
- throw new Error("[Crisp] setRtmHost: parameter host should be a string");
311
- }
312
- },
313
-
314
- /**
315
- * Sets the RTM channel mode (ie. WebSockets or Web Hooks)
316
- * @memberof Crisp
317
- * @public
318
- * @method setRtmMode
319
- * @param {string} mode - RTM mode ('websockets' or 'webhooks')
320
- * @return {undefined}
321
- */
322
- setRtmMode : function(mode) {
323
- if (Crisp.AVAILABLE_RTM_MODES.indexOf(mode) !== -1) {
324
- this._rtm.mode = mode;
325
- } else {
326
- throw new Error(
327
- "[Crisp] setRtmMode: parameter mode value should be one of: " +
328
- Crisp.AVAILABLE_RTM_MODES.join(", ")
329
- );
330
- }
331
- },
332
-
333
- /**
334
- * Sets the authentication tier
335
- * @memberof Crisp
336
- * @public
337
- * @method setTier
338
- * @param {string} tier
339
- * @return {undefined}
340
- */
341
- setTier : function(tier) {
342
- this.auth.tier = (tier || "user");
343
- },
344
-
345
- /**
346
- * Authenticates
347
- * @memberof Crisp
348
- * @public
349
- * @method authenticate
350
- * @param {string} identifier
351
- * @param {string} key
352
- * @return {undefined}
353
- */
354
- authenticate : function(identifier, key) {
355
- var auth = this.auth;
356
-
357
- // Store credentials
358
- auth.identifier = identifier;
359
- auth.key = key;
360
-
361
- // Assign pre-computed authentication token
362
- auth.token = Buffer.from(identifier + ":" + key).toString("base64");
363
- },
364
-
365
- /**
366
- * Authenticates (with tier)
367
- * @memberof Crisp
368
- * @public
369
- * @method authenticateTier
370
- * @param {string} tier
371
- * @param {string} identifier
372
- * @param {string} key
373
- * @return {undefined}
374
- */
375
- authenticateTier : function(tier, identifier, key) {
376
- this.setTier(tier);
377
- this.authenticate(identifier, key);
378
- },
379
-
380
- /**
381
- * Method wrapper to HEAD a resource
382
- * @memberof Crisp
383
- * @public
384
- * @method head
385
- * @param {string} resource
386
- * @param {object} query
387
- * @param {object} body
388
- * @return {Promise}
389
- */
390
- head : function(resource, query, body) {
391
- var self = this;
392
-
393
- return new Promise(function(resolve, reject) {
394
- self._request(
395
- resource, "head", (query || {}), null, resolve, reject
396
- );
397
- });
398
- },
399
-
400
- /**
401
- * Method wrapper to GET a resource
402
- * @memberof Crisp
403
- * @public
404
- * @method get
405
- * @param {string} resource
406
- * @param {object} query
407
- * @param {object} body
408
- * @return {Promise}
409
- */
410
- get : function(resource, query) {
411
- var self = this;
412
-
413
- return new Promise(function(resolve, reject) {
414
- self._request(
415
- resource, "get", (query || {}), null, resolve, reject
416
- );
417
- });
418
- },
419
-
420
- /**
421
- * Method wrapper to POST a resource
422
- * @memberof Crisp
423
- * @public
424
- * @method post
425
- * @param {string} resource
426
- * @param {object} query
427
- * @param {object} body
428
- * @return {Promise}
429
- */
430
- post : function(resource, query, body) {
431
- var self = this;
432
-
433
- return new Promise(function(resolve, reject) {
434
- self._request(
435
- resource, "post", (query || {}), (body || {}), resolve, reject
436
- );
437
- });
438
- },
439
-
440
- /**
441
- * Method wrapper to PATCH a resource
442
- * @memberof Crisp
443
- * @public
444
- * @method patch
445
- * @param {string} resource
446
- * @param {object} query
447
- * @param {object} body
448
- * @return {Promise}
449
- */
450
- patch : function(resource, query, body) {
451
- var self = this;
452
-
453
- return new Promise(function(resolve, reject) {
454
- self._request(
455
- resource, "patch", (query || {}), (body || {}), resolve, reject
456
- );
457
- });
458
- },
459
-
460
- /**
461
- * Method wrapper to PUT a resource
462
- * @memberof Crisp
463
- * @public
464
- * @method put
465
- * @param {string} resource
466
- * @param {object} query
467
- * @param {object} body
468
- * @return {Promise}
469
- */
470
- put : function(resource, query, body) {
471
- var self = this;
472
-
473
- return new Promise(function(resolve, reject) {
474
- self._request(
475
- resource, "put", (query || {}), (body || {}), resolve, reject
476
- );
477
- });
478
- },
479
-
480
- /**
481
- * Method wrapper to DELETE a resource
482
- * @memberof Crisp
483
- * @public
484
- * @method delete
485
- * @param {string} resource
486
- * @param {object} query
487
- * @param {object} body
488
- * @return {Promise}
489
- */
490
- delete : function(resource, query, body) {
491
- var self = this;
492
-
493
- return new Promise(function(resolve, reject) {
494
- self._request(
495
- resource, "delete", (query || {}), (body || null), resolve, reject
496
- );
497
- });
498
- },
499
-
500
- /**
501
- * Binds RTM event
502
- * @memberof Crisp
503
- * @public
504
- * @method on
505
- * @param {string} event
506
- * @param {function} callback
507
- * @return {Promise}
508
- */
509
- on : function(event, callback) {
510
- // Ensure all input arguments are set
511
- if (typeof event !== "string") {
512
- throw new Error("[Crisp] on: parameter event should be a string");
513
- }
514
- if (typeof callback !== "function") {
515
- throw new Error("[Crisp] on: parameter callback should be a function");
516
- }
517
-
518
- // Disallow unrecognized event names
519
- if (Crisp.DEFAULT_RTM_EVENTS.indexOf(event) === -1) {
520
- throw new Error(
521
- "[Crisp] on: parameter event value is not recognized: '" + event + "'"
522
- );
523
- }
524
-
525
- // Important: we do not allow .on() to be called once socket is connected, \
526
- // or loopback is bound as we consider event listeners must be bound \
527
- // once all together. This prevents bogous integrations from sending \
528
- // flood of 'socket:bind'` to the RTM API, if using WebSockets. Web \
529
- // Hooks follows the same scheme for consistency's sake.
530
- if (this._socket || this._loopback) {
531
- throw new Error(
532
- "[Crisp] on: connector is already bound, please listen to event " +
533
- "earlier on: '" + event + "'"
534
- );
535
- }
536
-
537
- // Add listener to emitter
538
- this._emitter.addListener(event, callback);
539
-
540
- // Subscribe event on the broker
541
- if (this._boundEvents[event] !== true) {
542
- var rtmMode = this._rtm.mode;
543
-
544
- // Mark event as bound
545
- this._boundEvents[event] = true;
546
-
547
- // Broker not connected? Connect now.
548
- return this._prepareBroker(
549
- function(instance, emitter) {
550
- // Listen for event? (once instance is bound)
551
- switch (rtmMode) {
552
- case Crisp.RTM_MODES.WebSockets: {
553
- // Listen on socket event
554
- instance.on(event, function(data) {
555
- emitter.emit(event, data);
556
- });
557
-
558
- break;
559
- }
560
- }
561
- }
562
- );
563
- }
564
-
565
- return Promise.resolve();
566
- },
567
-
568
- /**
569
- * Receives a raw event and dispatches it to the listener (used for Web Hooks)
570
- * @memberof Crisp
571
- * @public
572
- * @method receiveHook
573
- * @param {object} body
574
- * @return {undefined}
575
- */
576
- receiveHook : function(body) {
577
- var self = this;
578
-
579
- if (self._loopback) {
580
- // Ensure payload is readable
581
- if (!body || typeof body !== "object") {
582
- return new Error("[Crisp] receiveHook: empty hook payload");
583
- }
584
-
585
- // Ensure payload is properly formatted
586
- if (!body.event || !body.data ||
587
- typeof body.event !== "string" || typeof body.data !== "object") {
588
- return new Error("[Crisp] receiveHook: malformatted hook payload");
589
- }
590
-
591
- // Check if event is subscribed to? (in routing table)
592
- // Notice: if not in routing table, then silently discard the event w/o \
593
- // any error, as we do not want an HTTP failure status to be sent in \
594
- // response by the implementor.
595
- if (self._boundEvents[body.event] !== true) {
596
- return null;
597
- }
598
-
599
- // Dispatch event to event bus
600
- // Notice: go asynchronous, so that the event is processed ASAP and \
601
- // dispatched on the event bus later, as the hook might be received \
602
- // synchronously over HTTP.
603
- process.nextTick(function() {
604
- self._loopback.emit(body.event, body.data);
605
- });
606
-
607
- return null;
608
- }
609
-
610
- return new Error("[Crisp] receiveHook: hook loopback not bound");
611
- },
612
-
613
- /**
614
- * Verifies an event string and checks that signatures match (used for Web \
615
- * Hooks)
616
- * @memberof Crisp
617
- * @public
618
- * @method verifyHook
619
- * @param {string} secret
620
- * @param {object} body
621
- * @param {string} timestamp
622
- * @param {string} signature
623
- * @return {boolean}
624
- */
625
- verifyHook : function(secret, body, timestamp, signature) {
626
- if (this._loopback) {
627
- return this._verifySignature(secret, body, timestamp, signature);
628
- }
629
-
630
- // Default: not verified (loopback not /yet?/ bound)
631
- return false;
632
- },
633
-
634
- /**
635
- * Verifies an event string and checks that signatures match (used for \
636
- * Widgets)
637
- * @memberof Crisp
638
- * @public
639
- * @method verifyWidget
640
- * @param {string} secret
641
- * @param {object} body
642
- * @param {string} timestamp
643
- * @param {string} signature
644
- * @return {boolean}
645
- */
646
- verifyWidget : function(secret, body, timestamp, signature) {
647
- return this._verifySignature(secret, body, timestamp, signature);
648
- },
649
-
650
- /**
651
- * Rebinds socket events (used for WebSockets)
652
- * @memberof Crisp
653
- * @public
654
- * @method rebind
655
- * @return {Promise}
656
- */
657
- rebindSocket : function() {
658
- var self = this;
659
-
660
- if (!self._socket) {
661
- throw new Error(
662
- "[Crisp] rebindSocket: cannot rebind a socket that is not yet bound"
663
- );
664
- }
665
-
666
- // Make sure that the library user is not rebinding too frequently (which \
667
- // is illegal)
668
- var nowTime = Date.now();
669
-
670
- if (self._lastEventRebind !== null &&
671
- ((nowTime - self._lastEventRebind) <
672
- Crisp.DEFAULT_EVENT_REBIND_INTERVAL_MIN)) {
673
- throw new Error(
674
- "[Crisp] rebindSocket: cannot rebind, last rebind was requested too " +
675
- "recently"
676
- );
677
- }
678
-
679
- return Promise.resolve()
680
- .then(function() {
681
- // Rebind to socket events (eg. newly bound websites)
682
- self._lastEventRebind = nowTime;
683
-
684
- self._socket.emit("socket:bind", {});
685
-
686
- return Promise.resolve();
687
- });
688
- },
689
-
690
- /**
691
- * Prepares a URI based from path segments
692
- * @memberof Crisp
693
- * @private
694
- * @method _prepareRestUrl
695
- * @param {Array} paths - List of paths ['session', 'login']
696
- * @return {string}
697
- */
698
- _prepareRestUrl : function(paths) {
699
- if (Array.isArray(paths) === true) {
700
- var output = this._rest.host + this._rest.basePath;
701
-
702
- output += paths.join("/");
703
-
704
- return output;
705
- }
706
-
707
- throw new Error(
708
- "[Crisp] prepareRestUrl: parameter host should be an Array"
709
- );
710
- },
711
-
712
- /**
713
- * Binds services to the main object
714
- * @memberof Crisp
715
- * @private
716
- * @method _prepareServices
717
- * @return {undefined}
718
- */
719
- _prepareServices : function() {
720
- // Bind services
721
- for (var name in services) {
722
- var serviceInstance = new services[name]();
723
-
724
- // Acquire service map
725
- var serviceMap = this[(name[0].toLowerCase() + name.substring(1))];
726
-
727
- // No service map available?
728
- if (!serviceMap) {
729
- throw new Error(
730
- "[Crisp] prepareServices: service '" + name + "' has no map available"
731
- );
732
- }
733
-
734
- // No resources defined in service?
735
- if (!serviceInstance._resources ||
736
- serviceInstance._resources.length === 0) {
737
- throw new Error(
738
- "[Crisp] prepareServices: service '" + name + "' has no resources " +
739
- "defined"
740
- );
741
- }
742
-
743
- // Prepare all resources (for service)
744
- this._prepareResources(
745
- serviceMap, serviceInstance._resources
746
- );
747
- }
748
- },
749
-
750
- /**
751
- * Binds resources to the service object
752
- * @memberof Crisp
753
- * @private
754
- * @method _prepareResources
755
- * @param {object} serviceMap
756
- * @param {Array} resources
757
- * @return {undefined}
758
- */
759
- _prepareResources : function(serviceMap, resources) {
760
- for (var i = 0; i < resources.length; i++) {
761
- var resourceConstructor = require("./resources/" + resources[i]);
762
-
763
- // Instanciate resource, which will auto-bind itself to service prototype
764
- new resourceConstructor(serviceMap, this);
765
- }
766
- },
767
-
768
- /**
769
- * Binds broker to the main object
770
- * @memberof Crisp
771
- * @private
772
- * @method _prepareBroker
773
- * @param {function} fnBindHook
774
- * @return {Promise}
775
- */
776
- _prepareBroker : function(fnBindHook) {
777
- var self = this;
778
-
779
- return new Promise(function(resolve, reject) {
780
- var rtmMode = self._rtm.mode,
781
- rtmHostOverride = self._rtm.host;
782
-
783
- // Append bind hook to pending stack
784
- self._brokerBindHooks.push(fnBindHook);
785
-
786
- // Make sure to prepare broker once? (defer broker binding, waiting that \
787
- // all listeners have been bound, that way we submit the list of \
788
- // filtered events to the RTM API once, and never again in the future)
789
- if (self._brokerScheduler === null) {
790
- // Socket or loopback already set? We should not even have entered \
791
- // there.
792
- if (self._socket || self._loopback) {
793
- throw new Error(
794
- "[Crisp] prepareBroker: illegal call to prepare broker (tie break)"
795
- );
796
- }
797
-
798
- self._brokerScheduler = setTimeout(function() {
799
- switch (rtmMode) {
800
- case Crisp.RTM_MODES.WebSockets: {
801
- // Connect to socket now
802
- // Notice: will unstack broker bind hooks once ready
803
- self._connectSocket(rtmHostOverride)
804
- .then(resolve)
805
- .catch(reject);
806
-
807
- break;
808
- }
809
-
810
- case Crisp.RTM_MODES.WebHooks: {
811
- // Connect to loopback now
812
- self._connectLoopback()
813
- .then(resolve)
814
- .catch(reject);
815
-
816
- break;
817
- }
818
-
819
- default: {
820
- var unsupportedError = new Error(
821
- "[Crisp] prepareBroker: mode of RTM broker unsupported " +
822
- "('" + rtmMode + "')"
823
- );
824
-
825
- reject(unsupportedError);
826
- }
827
- }
828
- }, Crisp.DEFAULT_BROKER_SCHEDULE);
829
- } else {
830
- // Pass-through
831
- resolve();
832
- }
833
- });
834
- },
835
-
836
- /**
837
- * Connects loopback (used for Web Hooks)
838
- * @memberof Crisp
839
- * @private
840
- * @method _connectLoopback
841
- * @return {Promise}
842
- */
843
- _connectLoopback : function() {
844
- var self = this;
845
-
846
- return Promise.resolve()
847
- .then(function() {
848
- // Assign emitter to loopback
849
- self._loopback = self._emitter;
850
-
851
- // Unstack broker bind hooks immediately
852
- self._unstackBrokerBindHooks(self._loopback);
853
-
854
- return Promise.resolve();
855
- });
856
- },
857
-
858
- /**
859
- * Connects socket, using preferred RTM API host (used for WebSockets)
860
- * @memberof Crisp
861
- * @private
862
- * @method _connectSocket
863
- * @param {string} rtmHostOverride
864
- * @return {Promise}
865
- */
866
- _connectSocket : function(rtmHostOverride) {
867
- var self = this;
868
-
869
- return Promise.resolve()
870
- .then(function() {
871
- // Any override RTM API host?
872
- if (rtmHostOverride) {
873
- return Promise.resolve({
874
- socket : {
875
- app : rtmHostOverride
876
- }
877
- });
878
- }
879
-
880
- // Acquire RTM API URL from remote
881
- var restUrlSegments;
882
-
883
- switch (self.auth.tier) {
884
- case "plugin": {
885
- restUrlSegments = ["plugin", "connect", "endpoints"];
886
-
887
- break;
888
- }
889
-
890
- default: {
891
- restUrlSegments = ["user", "connect", "endpoints"];
892
- }
893
- }
894
-
895
- return self.get(
896
- self._prepareRestUrl(restUrlSegments)
897
- )
898
- .catch(function() {
899
- // Void error (consider as empty response)
900
- return Promise.resolve({});
901
- });
902
- })
903
- .then(function(endpoints) {
904
- var rtmHostAffinity = ((endpoints.socket || {}).app || null);
905
-
906
- // No RTM API host acquired?
907
- if (rtmHostAffinity === null) {
908
- throw new Error(
909
- "[Crisp] connectSocket: could not acquire target host to " +
910
- "connect to, is your session valid for tier?"
911
- );
912
- }
913
-
914
- // Parse target RTM API host as an URL object
915
- var rtmHostUrl = new URL(rtmHostAffinity);
916
-
917
- // Connect to socket
918
- self._socket = require("socket.io-client")(rtmHostUrl.origin, {
919
- path : (rtmHostUrl.pathname || "/"),
920
- transports : ["websocket"],
921
- timeout : Crisp.DEFAULT_SOCKET_TIMEOUT,
922
- reconnection : true,
923
- reconnectionDelay : Crisp.DEFAULT_SOCKET_RECONNECT_DELAY,
924
- reconnectionDelayMax : Crisp.DEFAULT_SOCKET_RECONNECT_DELAY_MAX,
925
- randomizationFactor : Crisp.DEFAULT_SOCKET_RECONNECT_FACTOR
926
- });
927
-
928
- self._emitAuthenticateSocket();
929
-
930
- // Setup base socket event listeners
931
- self._socket.io.on("reconnect", function() {
932
- self._emitAuthenticateSocket();
933
- });
934
-
935
- self._socket.on("unauthorized", function() {
936
- throw new Error(
937
- "[Crisp] connectSocket: cannot listen for events as " +
938
- "authentication is invalid"
939
- );
940
- });
941
-
942
- // Setup user socket event listeners
943
- self._unstackBrokerBindHooks(self._socket);
944
-
945
- return Promise.resolve();
946
- });
947
- },
948
-
949
- /**
950
- * Authenticates client (used for WebSockets)
951
- * @memberof Crisp
952
- * @private
953
- * @method _emitAuthenticateSocket
954
- * @return {undefined}
955
- */
956
- _emitAuthenticateSocket : function() {
957
- var auth = this.auth,
958
- boundEvents = Object.keys(this._boundEvents);
959
-
960
- if (!this._socket) {
961
- throw new Error(
962
- "[Crisp] emitAuthenticateSocket: cannot listen for events as socket " +
963
- "is not yet bound"
964
- );
965
- }
966
- if (!auth.identifier || !auth.key) {
967
- throw new Error(
968
- "[Crisp] emitAuthenticateSocket: cannot listen for events as you " +
969
- "did not authenticate"
970
- );
971
- }
972
- if (boundEvents.length === 0) {
973
- throw new Error(
974
- "[Crisp] emitAuthenticateSocket: cannot listen for events as no " +
975
- "event is being listened to"
976
- );
977
- }
978
-
979
- this._socket.emit("authentication", {
980
- username : auth.identifier,
981
- password : auth.key,
982
- tier : auth.tier,
983
- events : boundEvents
984
- });
985
- },
986
-
987
- /**
988
- * Unstacks pending broker bind hooks
989
- * @memberof Crisp
990
- * @private
991
- * @method _unstackBrokerBindHooks
992
- * @param {object} modeInstance
993
- * @return {undefined}
994
- */
995
- _unstackBrokerBindHooks : function(modeInstance) {
996
- // Setup user socket event listeners
997
- while (this._brokerBindHooks.length > 0) {
998
- this._brokerBindHooks.shift()(
999
- modeInstance, this._emitter
1000
- );
1001
- }
1002
- },
1003
-
1004
- /**
1005
- * Performs a request to REST API
1006
- * @memberof Crisp
1007
- * @private
1008
- * @method _request
1009
- * @param {string} resource
1010
- * @param {string} method
1011
- * @param {object} query
1012
- * @param {object} body
1013
- * @param {function} resolve
1014
- * @param {function} reject
1015
- * @return {undefined}
1016
- */
1017
- _request : function(resource, method, query, body, resolve, reject) {
1018
- var self = this;
1019
-
1020
- var requestParameters = {
1021
- responseType : "json",
1022
- timeout : Crisp.DEFAULT_REQUEST_TIMEOUT,
1023
-
1024
- headers : {
1025
- "User-Agent" : self._useragent,
1026
- "X-Crisp-Tier" : self.auth.tier
1027
- },
1028
-
1029
- throwHttpErrors : false
1030
- };
1031
-
1032
- // Add authorization?
1033
- if (self.auth.token) {
1034
- requestParameters.headers.Authorization = ("Basic " + self.auth.token);
1035
- }
1036
-
1037
- // Add body?
1038
- if (body) {
1039
- requestParameters.json = body;
1040
- }
1041
-
1042
- // Add query?
1043
- if (query) {
1044
- requestParameters.searchParams = query;
1045
- }
1046
-
1047
- // Proceed request
1048
- got[method](resource, requestParameters)
1049
- .catch(function(error) {
1050
- return Promise.resolve(error);
1051
- })
1052
- .then(function(response, error) {
1053
- var data = response.body;
1054
-
1055
- // Request error?
1056
- if (!response.statusCode) {
1057
- return reject({
1058
- reason : "error",
1059
- message : "internal_error",
1060
- code : 500,
1061
-
1062
- data : {
1063
- namespace : "request",
1064
-
1065
- message : (
1066
- "Got request error: " + (response.name || "Unknown")
1067
- )
1068
- }
1069
- });
1070
- }
1071
-
1072
- // Response error?
1073
- if (response.statusCode >= 400) {
1074
- var reason_message = self._readErrorResponseReason(
1075
- method, response.statusCode, response
1076
- );
1077
- var data_message = ((response.body || {}).data || {}).message;
1078
-
1079
- return reject({
1080
- reason : "error",
1081
- message : reason_message,
1082
- code : response.statusCode,
1083
-
1084
- data : {
1085
- namespace : "response",
1086
-
1087
- message : (
1088
- "Got response error: " + (data_message || reason_message)
1089
- )
1090
- }
1091
- });
1092
- }
1093
-
1094
- // Regular response
1095
- return resolve(
1096
- (response.body || {}).data || {}
1097
- );
1098
- });
1099
- },
1100
-
1101
- /**
1102
- * Reads reason for error response
1103
- * @memberof Crisp
1104
- * @private
1105
- * @method _readErrorResponseReason
1106
- * @param {string} method
1107
- * @param {number} statusCode
1108
- * @param {object} response
1109
- * @return {string}
1110
- */
1111
- _readErrorResponseReason : function(method, statusCode, response) {
1112
- // HEAD method? As HEAD requests do not expect any response body, then we \
1113
- // cannot map a reason from the response.
1114
- if (method === "head") {
1115
- // 5xx errors?
1116
- if (statusCode >= 500) {
1117
- return "server_error";
1118
- }
1119
-
1120
- // 4xx errors?
1121
- if (statusCode >= 400) {
1122
- return "route_error";
1123
- }
1124
- }
1125
-
1126
- // Other methods must hold a response body, therefore we can fallback on \
1127
- // an HTTP error if we fail to acquire any reason at all.
1128
- return ((response.body || {}).reason || "http_error");
1129
- },
1130
-
1131
- /**
1132
- * Verifies an event string and checks that signatures match
1133
- * @memberof Crisp
1134
- * @private
1135
- * @method verifyHook
1136
- * @param {string} secret
1137
- * @param {object} body
1138
- * @param {string} timestamp
1139
- * @param {string} signature
1140
- * @return {boolean}
1141
- */
1142
- _verifySignature : function(secret, body, timestamp, signature) {
1143
- // Ensure all provided data is valid
1144
- if (!secret || !signature || !body || typeof body !== "object" ||
1145
- !timestamp || isNaN(timestamp) === true) {
1146
- return false;
1147
- }
1148
-
1149
- // Compute local trace
1150
- var localTrace = ("[" + timestamp + ";" + JSON.stringify(body) + "]");
1151
-
1152
- // Create local HMAC
1153
- var localMac = Crypto.createHmac("sha256", secret);
1154
-
1155
- localMac.update(localTrace);
1156
-
1157
- // Compute local signature, and compare
1158
- var localSignature = localMac.digest("hex");
1159
-
1160
- return (
1161
- (signature === localSignature) ? true : false
1162
- );
1163
- }
1164
- };
1165
-
1166
-
1167
- module.exports = Crisp;
1168
- module.exports.Crisp = Crisp;