@whitewall/blip-sdk 0.0.136 → 0.0.138

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 (54) hide show
  1. package/dist/cjs/client.js +1 -1
  2. package/dist/cjs/client.js.map +1 -1
  3. package/dist/esm/client.js +1 -1
  4. package/dist/esm/client.js.map +1 -1
  5. package/package.json +2 -2
  6. package/src/client.ts +117 -0
  7. package/src/index.ts +6 -0
  8. package/src/namespaces/account.ts +729 -0
  9. package/src/namespaces/activecampaign.ts +285 -0
  10. package/src/namespaces/analytics.ts +230 -0
  11. package/src/namespaces/billing.ts +17 -0
  12. package/src/namespaces/builder.ts +52 -0
  13. package/src/namespaces/configurations.ts +19 -0
  14. package/src/namespaces/context.ts +67 -0
  15. package/src/namespaces/desk.ts +679 -0
  16. package/src/namespaces/media.ts +39 -0
  17. package/src/namespaces/namespace.ts +125 -0
  18. package/src/namespaces/plugins.ts +223 -0
  19. package/src/namespaces/portal.ts +402 -0
  20. package/src/namespaces/scheduler.ts +88 -0
  21. package/src/namespaces/whatsapp.ts +383 -0
  22. package/src/sender/bliperror.ts +42 -0
  23. package/src/sender/enveloperesolver.ts +148 -0
  24. package/src/sender/gateway/customgatewaysender.ts +43 -0
  25. package/src/sender/http/httpsender.ts +94 -0
  26. package/src/sender/index.ts +7 -0
  27. package/src/sender/plugin/communication.ts +72 -0
  28. package/src/sender/plugin/pluginsender.ts +75 -0
  29. package/src/sender/security.ts +33 -0
  30. package/src/sender/sender.ts +145 -0
  31. package/src/sender/sessionnegotiator.ts +175 -0
  32. package/src/sender/tcp/tcpsender.ts +252 -0
  33. package/src/sender/throttler.ts +36 -0
  34. package/src/sender/websocket/websocketsender.ts +175 -0
  35. package/src/types/account.ts +84 -0
  36. package/src/types/analytics.ts +18 -0
  37. package/src/types/billing.ts +15 -0
  38. package/src/types/command.ts +47 -0
  39. package/src/types/commons.ts +16 -0
  40. package/src/types/desk.ts +51 -0
  41. package/src/types/envelope.ts +9 -0
  42. package/src/types/flow.ts +327 -0
  43. package/src/types/index.ts +13 -0
  44. package/src/types/message.ts +116 -0
  45. package/src/types/node.ts +86 -0
  46. package/src/types/notification.ts +18 -0
  47. package/src/types/plugins.ts +51 -0
  48. package/src/types/portal.ts +39 -0
  49. package/src/types/reason.ts +22 -0
  50. package/src/types/session.ts +22 -0
  51. package/src/types/whatsapp.ts +84 -0
  52. package/src/utils/odata.ts +114 -0
  53. package/src/utils/random.ts +3 -0
  54. package/src/utils/uri.ts +46 -0
@@ -0,0 +1,679 @@
1
+ import type { BlipClient } from '../client.ts'
2
+ import { BlipError } from '../sender/bliperror.ts'
3
+ import {
4
+ type AttendanceHour,
5
+ type DetailedAttendanceHour,
6
+ type Identity,
7
+ Node,
8
+ type ThreadItem,
9
+ type Ticket,
10
+ type TicketStatus,
11
+ } from '../types/index.ts'
12
+ import type { ODataFilter } from '../utils/odata.ts'
13
+ import { uri } from '../utils/uri.ts'
14
+ import { type ConsumeOptions, Namespace, type SendCommandOptions } from './namespace.ts'
15
+
16
+ export class DeskNamespace extends Namespace {
17
+ constructor(blipClient: BlipClient, defaultOptions?: SendCommandOptions) {
18
+ super(blipClient, 'desk', defaultOptions)
19
+ }
20
+
21
+ public async createTicket(contact: Identity, opts?: ConsumeOptions): Promise<Ticket> {
22
+ return await this.sendCommand(
23
+ {
24
+ method: 'set',
25
+ uri: uri`/tickets`,
26
+ type: 'application/vnd.iris.ticket+json',
27
+ resource: {
28
+ customerIdentity: contact,
29
+ },
30
+ },
31
+ opts,
32
+ )
33
+ }
34
+
35
+ public async getTicket(ticket: string, opts?: ConsumeOptions): Promise<Ticket> {
36
+ return await this.sendCommand(
37
+ {
38
+ method: 'get',
39
+ uri: uri`/ticket/${ticket}`,
40
+ },
41
+ opts,
42
+ )
43
+ }
44
+
45
+ public async getTicketMessages(ticket: Ticket, opts?: Omit<ConsumeOptions, 'skip'>): Promise<Array<ThreadItem>>
46
+ public async getTicketMessages(ticket: string, opts?: Omit<ConsumeOptions, 'skip'>): Promise<Array<ThreadItem>>
47
+ public async getTicketMessages(
48
+ ticketOrTicketId: Ticket | string,
49
+ opts?: Omit<ConsumeOptions, 'skip'>,
50
+ ): Promise<Array<ThreadItem>> {
51
+ const ticket =
52
+ typeof ticketOrTicketId === 'string' ? await this.getTicket(ticketOrTicketId, opts) : ticketOrTicketId
53
+
54
+ // Include the message that originated the ticket
55
+ const storageDate = new Date(ticket.storageDate)
56
+ storageDate.setSeconds(storageDate.getSeconds() - 10)
57
+
58
+ const thread = await this.blipClient.account.getThread(
59
+ ticket.customerIdentity,
60
+ {
61
+ referenceDate: storageDate,
62
+ referenceDateDirection: 'after',
63
+ },
64
+ opts,
65
+ )
66
+ return thread
67
+ .filter((item) => new Date(item.date) >= new Date(storageDate))
68
+ .filter((item) => !ticket.closeDate || new Date(item.date) <= new Date(ticket.closeDate))
69
+ }
70
+
71
+ public async getTicketsMetrics(opts?: ConsumeOptions): Promise<{
72
+ maxQueueTime: string
73
+ maxFirstResponseTime: string
74
+ avgQueueTime: string
75
+ avgFirstResponseTime: string
76
+ avgWaitTime: string
77
+ avgResponseTime: string
78
+ avgAttendanceTime: string
79
+ ticketsPerAttendant: number
80
+ }> {
81
+ return await this.sendCommand(
82
+ {
83
+ method: 'get',
84
+ uri: uri`/monitoring/ticket-metrics?${{ version: 2 }}`,
85
+ },
86
+ opts,
87
+ )
88
+ }
89
+
90
+ public async getWaitingTicketsMetrics(
91
+ filters?: {
92
+ teams?: Array<string>
93
+ tags?: Array<string>
94
+ operators?: Array<string>
95
+ },
96
+ opts?: ConsumeOptions,
97
+ ): Promise<
98
+ Array<{
99
+ id: string
100
+ sequentialId: number
101
+ customerIdentity: Identity
102
+ customerName: string
103
+ team: string
104
+ queueTime: string
105
+ priority: number
106
+ agentIdentity?: Identity
107
+ agentName?: string
108
+ }>
109
+ > {
110
+ return await this.sendCommand(
111
+ {
112
+ method: 'get',
113
+ uri: uri`/monitoring/waiting-tickets?${{
114
+ teams: filters?.teams?.join(','),
115
+ tags: filters?.tags?.join(','),
116
+ operators: filters?.operators?.join(','),
117
+ }}`,
118
+ },
119
+ {
120
+ collection: true,
121
+ take: opts?.fetchall ? (opts?.max ?? 1_000_000) : opts?.take,
122
+ fetchall: false,
123
+ ...opts,
124
+ },
125
+ )
126
+ }
127
+
128
+ public async getTickets(filter?: ODataFilter<Ticket>, opts?: ConsumeOptions): Promise<Array<Ticket>> {
129
+ return await this.sendCommand(
130
+ {
131
+ method: 'get',
132
+ uri: uri`/tickets?${{ $filter: filter?.toString() }}`,
133
+ },
134
+ {
135
+ collection: true,
136
+ ...opts,
137
+ },
138
+ )
139
+ }
140
+
141
+ public async getContactTickets(contact: Identity, opts?: ConsumeOptions): Promise<Array<Ticket>> {
142
+ return await this.sendCommand(
143
+ {
144
+ method: 'get',
145
+ uri: uri`/tickets/history-merged/${contact}`,
146
+ },
147
+ {
148
+ collection: true,
149
+ ...opts,
150
+ },
151
+ )
152
+ }
153
+
154
+ public async changeTicketStatus(
155
+ ticket: string,
156
+ status: TicketStatus,
157
+ settings?: {
158
+ agent?: Identity
159
+ closedBy?: Identity
160
+ tags?: Array<string>
161
+ },
162
+ opts?: ConsumeOptions,
163
+ ): Promise<void> {
164
+ try {
165
+ return await this.sendCommand(
166
+ {
167
+ method: 'set',
168
+ uri: uri`/tickets/change-status`,
169
+ type: 'application/vnd.iris.ticket+json',
170
+ resource: {
171
+ id: ticket,
172
+ status,
173
+ tags: settings?.tags,
174
+ agentIdentity: settings?.agent,
175
+ closedBy: settings?.closedBy,
176
+ },
177
+ },
178
+ opts,
179
+ )
180
+ } catch (err) {
181
+ if (err instanceof BlipError && err.code === 64) {
182
+ return
183
+ }
184
+
185
+ throw err
186
+ }
187
+ }
188
+
189
+ public async changeTicketTags(ticket: string, tags: Array<string>, opts?: ConsumeOptions): Promise<void> {
190
+ return await this.sendCommand(
191
+ {
192
+ method: 'set',
193
+ uri: uri`/tickets/${ticket}/change-tags`,
194
+ type: 'application/vnd.iris.ticket+json',
195
+ resource: {
196
+ id: ticket,
197
+ tags,
198
+ },
199
+ },
200
+ opts,
201
+ )
202
+ }
203
+
204
+ public async isOnAttendanceTime(queueIdOrName = 'Default', timezone = -3): Promise<boolean> {
205
+ const attendanceHours: Array<AttendanceHour> = await this.sendCommand(
206
+ {
207
+ method: 'get',
208
+ uri: uri`/attendance-hour`,
209
+ },
210
+ { collection: true },
211
+ )
212
+
213
+ const orderedAttendanceHours = attendanceHours
214
+ // Prioritize non-main attendance hours
215
+ .sort((a, b) => Number(a.isMain) - Number(b.isMain))
216
+
217
+ for (const attendanceHour of orderedAttendanceHours) {
218
+ const detailedAttendanceHour: DetailedAttendanceHour = await this.sendCommand({
219
+ method: 'get',
220
+ uri: uri`/attendance-hour-container/${attendanceHour.id}`,
221
+ })
222
+
223
+ if (
224
+ detailedAttendanceHour.queues.every(
225
+ (queue) => queue.id !== queueIdOrName && queue.description !== queueIdOrName,
226
+ ) &&
227
+ !detailedAttendanceHour.attendanceHour.isMain
228
+ ) {
229
+ // Should keep trying other attendance hours to match the queue
230
+ continue
231
+ }
232
+
233
+ const now = new Date(Date.now() + timezone * 60 * 60 * 1000)
234
+
235
+ const isInsideOffTime = detailedAttendanceHour.attendanceHourOffItems.some((offItem) => {
236
+ const startDate = new Date(offItem.startDate)
237
+ const endDate = new Date(offItem.endDate)
238
+
239
+ return now.getTime() >= startDate.getTime() && now.getTime() <= endDate.getTime()
240
+ })
241
+ if (isInsideOffTime) {
242
+ return false
243
+ }
244
+
245
+ const nowDayOfWeek = now.getDay()
246
+ const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
247
+ const todaySchedule = detailedAttendanceHour.attendanceHourScheduleItems.find(
248
+ (schedule) => schedule.dayOfWeek === daysOfWeek[nowDayOfWeek],
249
+ )
250
+ if (!todaySchedule) {
251
+ return false
252
+ }
253
+
254
+ const startTime = new Date(now)
255
+ const endTime = new Date(now)
256
+
257
+ const [startHours, startMinutes] = todaySchedule.startTime.split(':')
258
+ startTime.setHours(Number(startHours))
259
+ startTime.setMinutes(Number(startMinutes))
260
+
261
+ const [endHours, endMinutes] = todaySchedule.endTime.split(':')
262
+ endTime.setHours(Number(endHours))
263
+ endTime.setMinutes(Number(endMinutes))
264
+
265
+ if (now.getTime() >= startTime.getTime() && now.getTime() <= endTime.getTime()) {
266
+ return true
267
+ }
268
+ }
269
+
270
+ return false
271
+ }
272
+
273
+ public async getApprovedMessageTemplates(opts?: ConsumeOptions): Promise<
274
+ Array<{
275
+ id: string
276
+ is_favorite: boolean
277
+ category: string
278
+ language: string
279
+ last_updated_time: string
280
+ name: string
281
+ status: 'APPROVED' | 'REJECTED' | 'PENDING'
282
+ }>
283
+ > {
284
+ return await this.sendCommand(
285
+ {
286
+ method: 'get',
287
+ uri: uri`/active-message/approved-message-templates`,
288
+ },
289
+ {
290
+ collection: true,
291
+ ...opts,
292
+ },
293
+ )
294
+ }
295
+
296
+ public async getMessageTemplateParams(opts?: ConsumeOptions) {
297
+ const params: Array<{
298
+ params?: {
299
+ paramsId: number
300
+ ownerIdentity: string
301
+ templateId: string
302
+ stateId: string
303
+ isEnabled: boolean
304
+ storageDate: string
305
+ modifyDate: string
306
+ }
307
+ template: {
308
+ Id: string
309
+ Category: string
310
+ Components: Array<
311
+ {
312
+ Type: string
313
+ } & Record<string, unknown>
314
+ >
315
+ Language: string
316
+ LastUpdatedTime: string
317
+ Name: string
318
+ RejectedReason: string
319
+ Status: 'APPROVED' | 'REJECTED' | 'PENDING'
320
+ }
321
+ }> = await this.sendCommand(
322
+ {
323
+ method: 'get',
324
+ uri: uri`/active-message/message-templates-params`,
325
+ },
326
+ {
327
+ collection: true,
328
+ ...opts,
329
+ },
330
+ )
331
+
332
+ return params.map((param) => ({
333
+ template: param.template.Name,
334
+ category: param.template.Category,
335
+ language: param.template.Language,
336
+ status: param.template.Status,
337
+ ownerIdentity: param.params?.ownerIdentity,
338
+ stateId: param.params?.stateId,
339
+ isEnabled: param.params?.isEnabled,
340
+ components: param.template.Components.map((c) => ({
341
+ type: c.Type,
342
+ text: c.Text as string | undefined,
343
+ })),
344
+ }))
345
+ }
346
+
347
+ public async sendIndividualActiveMessage(
348
+ phoneNumber: string,
349
+ message: { template: string; language: string; variables: Array<string> },
350
+ sender: Node,
351
+ opts?: ConsumeOptions,
352
+ ) {
353
+ await this.sendCommand(
354
+ {
355
+ method: 'set',
356
+ uri: uri`/active-message/send-active-message`,
357
+ type: 'application/vnd.iris.activecampaign.full-campaign+json',
358
+ resource: {
359
+ campaign: {
360
+ campaignType: 'Individual',
361
+ CampaignSender: sender,
362
+ attendanceRedirect: sender,
363
+ },
364
+ audience: {
365
+ recipient: phoneNumber,
366
+ recipientType: 'phoneNumber',
367
+ channelType: 'whatsApp',
368
+ messageParams: { ...message.variables },
369
+ },
370
+ message: {
371
+ messageTemplate: message.template,
372
+ channelType: 'whatsapp',
373
+ messageParams: message.variables.length ? Object.keys(message.variables).map(Number) : '',
374
+ messageTemplateLanguage: message.language,
375
+ },
376
+ },
377
+ },
378
+ opts,
379
+ )
380
+ }
381
+
382
+ public async getTeamsAgentsOnline(opts?: ConsumeOptions): Promise<
383
+ Array<{
384
+ name: string
385
+ agentsOnline: number
386
+ }>
387
+ > {
388
+ return await this.sendCommand(
389
+ {
390
+ method: 'get',
391
+ uri: uri`/teams/agents-online`,
392
+ },
393
+ {
394
+ collection: true,
395
+ ...opts,
396
+ },
397
+ )
398
+ }
399
+
400
+ public async getAgents(opts?: ConsumeOptions): Promise<
401
+ Array<{
402
+ email: string
403
+ fullName: string
404
+ identity: Identity
405
+ isEnabled: boolean
406
+ status: 'Offline' | 'Pause' | 'Online' | 'Invisible'
407
+ teams: Array<string>
408
+ lastServiceDate: string
409
+ }>
410
+ > {
411
+ try {
412
+ return await this.sendCommand(
413
+ {
414
+ method: 'get',
415
+ uri: uri`/agents`,
416
+ },
417
+ {
418
+ collection: true,
419
+ ...opts,
420
+ },
421
+ )
422
+ } catch (err) {
423
+ if (err instanceof BlipError && err.code === 67) {
424
+ return []
425
+ }
426
+
427
+ throw err
428
+ }
429
+ }
430
+
431
+ /** @returns The new child ticket created from the transfer */
432
+ public async transferTicket(
433
+ ticket: string,
434
+ teamOrAgentIdentity: Identity | string,
435
+ transferredBy?: Identity,
436
+ opts?: ConsumeOptions,
437
+ ): Promise<Ticket> {
438
+ return await this.sendCommand(
439
+ {
440
+ method: 'set',
441
+ uri: uri`/tickets/${ticket}/transfer`,
442
+ type: 'application/vnd.iris.ticket+json',
443
+ resource: {
444
+ team: Node.isValid(teamOrAgentIdentity) ? 'DIRECT_TRANSFER' : teamOrAgentIdentity,
445
+ agentIdentity: Node.isValid(teamOrAgentIdentity) ? teamOrAgentIdentity : undefined,
446
+ closedBy: transferredBy,
447
+ },
448
+ },
449
+ opts,
450
+ )
451
+ }
452
+
453
+ public async sendTicketSurveyAnswer(
454
+ ticket: string,
455
+ survey: string,
456
+ details: {
457
+ answer: string
458
+ answerScore: number
459
+ comment?: string
460
+ },
461
+ opts?: ConsumeOptions,
462
+ ) {
463
+ return await this.sendCommand(
464
+ {
465
+ method: 'set',
466
+ uri: uri`/attendance-survey-answer`,
467
+ type: 'application/vnd.iris.desk.attendance-survey-answer+json',
468
+ resource: {
469
+ ticketId: ticket,
470
+ survey: survey,
471
+ answer: details.answer,
472
+ answerScore: details.answerScore.toString(),
473
+ comment: details.comment ?? '',
474
+ },
475
+ },
476
+ opts,
477
+ )
478
+ }
479
+
480
+ public async getTags(team: string, opts?: ConsumeOptions): Promise<Array<string>> {
481
+ const result = await this.sendCommand<'get', Array<{ tag: string }>>(
482
+ {
483
+ method: 'get',
484
+ uri: uri`/attendance-queues/name/${team}/tags`,
485
+ },
486
+ {
487
+ collection: true,
488
+ ...opts,
489
+ },
490
+ )
491
+ return result.map((tag) => tag.tag)
492
+ }
493
+
494
+ public async getSummary(
495
+ ticket: string,
496
+ opts?: ConsumeOptions,
497
+ ): Promise<{
498
+ summary: {
499
+ customer_data: {
500
+ name: string
501
+ }
502
+ contact: {
503
+ date: string
504
+ reason: string
505
+ conclusion: string
506
+ sentiment: string
507
+ }
508
+ }
509
+ }> {
510
+ return await this.sendCommand(
511
+ {
512
+ method: 'get',
513
+ uri: uri`/tickets/${ticket}/copilot/thread-end-summary`,
514
+ },
515
+ opts,
516
+ )
517
+ }
518
+
519
+ public async setCustomChannel(
520
+ url: string,
521
+ settings?: {
522
+ token?: string
523
+ },
524
+ opts?: ConsumeOptions,
525
+ ) {
526
+ const configurations = await this.blipClient.account.getConfigurations({
527
+ ...opts,
528
+ ownerIdentity: this.identity,
529
+ })
530
+ if (!configurations || !configurations.DefaultProvider) {
531
+ throw new Error('No default provider found, Blip Desk is not enabled')
532
+ }
533
+
534
+ await this.blipClient.account.setConfigurations(
535
+ {
536
+ DefaultProvider: 'Webhook',
537
+ 'Webhook.ApiEndpoint': url,
538
+ 'Webhook.AuthenticationToken': settings?.token ?? '',
539
+ },
540
+ {
541
+ ...opts,
542
+ ownerIdentity: this.identity,
543
+ },
544
+ )
545
+ }
546
+
547
+ public async getCustomChannel(opts?: ConsumeOptions): Promise<{
548
+ url: string
549
+ token: string
550
+ }> {
551
+ const configurations = await this.blipClient.account.getConfigurations({
552
+ ...opts,
553
+ ownerIdentity: this.identity,
554
+ })
555
+
556
+ if (!configurations || !configurations.DefaultProvider) {
557
+ throw new Error('No default provider found')
558
+ }
559
+
560
+ if (configurations.DefaultProvider !== 'Webhook') {
561
+ throw new Error('Default provider is not Webhook')
562
+ }
563
+
564
+ return {
565
+ url: configurations['Webhook.ApiEndpoint'],
566
+ token: configurations['Webhook.AuthenticationToken'],
567
+ }
568
+ }
569
+
570
+ public async addExtension(
571
+ id: string,
572
+ settings: {
573
+ name: string
574
+ view: 'ticket' | 'agent'
575
+ url: string
576
+ },
577
+ opts?: ConsumeOptions,
578
+ ): Promise<void> {
579
+ const configurations = await this.blipClient.account.getConfigurations({
580
+ ...opts,
581
+ ownerIdentity: this.identity,
582
+ })
583
+
584
+ const extensions = 'Extensions' in configurations ? JSON.parse(configurations.Extensions) : {}
585
+ extensions[id] = settings
586
+
587
+ return await this.blipClient.account.setConfigurations(
588
+ { Extensions: JSON.stringify(extensions) },
589
+ { ...opts, ownerIdentity: this.identity },
590
+ )
591
+ }
592
+
593
+ public async updateExtension(
594
+ id: string,
595
+ settings: Partial<{
596
+ name: string
597
+ view: 'ticket' | 'agent'
598
+ url: string
599
+ }>,
600
+ opts?: ConsumeOptions,
601
+ ): Promise<void> {
602
+ const configurations = await this.blipClient.account.getConfigurations({
603
+ ...opts,
604
+ ownerIdentity: this.identity,
605
+ })
606
+
607
+ if ('Extensions' in configurations) {
608
+ const extensions = JSON.parse(configurations.Extensions) as Record<string, { name: string; url: string }>
609
+ if (id in extensions) {
610
+ extensions[id] = { ...extensions[id], ...settings }
611
+ return await this.blipClient.account.setConfigurations(
612
+ { Extensions: JSON.stringify(extensions) },
613
+ { ...opts, ownerIdentity: this.identity },
614
+ )
615
+ } else {
616
+ throw new Error(`Extension with id "${id}" not found`)
617
+ }
618
+ } else {
619
+ throw new Error('No extensions found')
620
+ }
621
+ }
622
+
623
+ public async deleteExtension(id: string, opts?: ConsumeOptions): Promise<void>
624
+ public async deleteExtension(url: string | RegExp, opts?: ConsumeOptions): Promise<void>
625
+ public async deleteExtension(idOrUrl: string | RegExp, opts?: ConsumeOptions): Promise<void> {
626
+ const configurations = await this.blipClient.account.getConfigurations({
627
+ ...opts,
628
+ ownerIdentity: this.identity,
629
+ })
630
+
631
+ if ('Extensions' in configurations) {
632
+ const extensions = JSON.parse(configurations.Extensions) as Record<string, { name: string; url: string }>
633
+ for (const id in extensions) {
634
+ if (
635
+ (idOrUrl instanceof RegExp && idOrUrl.test(extensions[id].url)) ||
636
+ (typeof idOrUrl === 'string' && (idOrUrl === id || extensions[id].url === idOrUrl))
637
+ ) {
638
+ delete extensions[id]
639
+ }
640
+
641
+ return await this.blipClient.account.setConfigurations(
642
+ { Extensions: JSON.stringify(extensions) },
643
+ { ...opts, ownerIdentity: this.identity },
644
+ )
645
+ }
646
+ }
647
+ }
648
+
649
+ public async createAgent(
650
+ agent: {
651
+ identity: Identity
652
+ teams: Array<string>
653
+ agentSlots?: number
654
+ },
655
+ opts?: ConsumeOptions,
656
+ ): Promise<void> {
657
+ return await this.sendCommand(
658
+ {
659
+ method: 'set',
660
+ uri: uri`/agents`,
661
+ type: 'application/vnd.iris.desk.attendant+json',
662
+ resource: agent,
663
+ },
664
+ opts,
665
+ )
666
+ }
667
+
668
+ public async deleteAgent(identity: Identity, opts?: ConsumeOptions): Promise<void> {
669
+ return await this.sendCommand(
670
+ {
671
+ method: 'delete',
672
+ uri: uri`/agents/${identity}`,
673
+ },
674
+ {
675
+ ...opts,
676
+ },
677
+ )
678
+ }
679
+ }
@@ -0,0 +1,39 @@
1
+ import type { BlipClient } from '../client.ts'
2
+ import { uri } from '../utils/uri.ts'
3
+ import { type ConsumeOptions, Namespace, type SendCommandOptions } from './namespace.ts'
4
+
5
+ export class MediaNamespace extends Namespace {
6
+ constructor(blipClient: BlipClient, defaultOptions?: SendCommandOptions) {
7
+ super(blipClient, 'media', defaultOptions)
8
+ }
9
+
10
+ public async getMediaUploadUrl(opts?: ConsumeOptions): Promise<string> {
11
+ return await this.sendCommand(
12
+ {
13
+ method: 'get',
14
+ uri: uri`/upload-media-uri`,
15
+ },
16
+ opts,
17
+ )
18
+ }
19
+
20
+ public async uploadMedia(
21
+ mimetype: string,
22
+ media: BufferSource | Blob | string | ReadableStream,
23
+ opts?: ConsumeOptions,
24
+ ): Promise<string> {
25
+ const url = await this.getMediaUploadUrl(opts)
26
+ const uploadResponse = await fetch(url, {
27
+ method: 'POST',
28
+ headers: {
29
+ 'Content-Type': mimetype,
30
+ },
31
+ body: media,
32
+ })
33
+ if (!uploadResponse.ok) {
34
+ throw new Error(`Failed to upload media: ${uploadResponse.statusText}`)
35
+ }
36
+ const { mediaUri } = await uploadResponse.json()
37
+ return mediaUri
38
+ }
39
+ }