@whitewall/blip-sdk 0.0.136 → 0.0.137
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/client.ts +117 -0
- package/src/index.ts +6 -0
- package/src/namespaces/account.ts +729 -0
- package/src/namespaces/activecampaign.ts +285 -0
- package/src/namespaces/analytics.ts +230 -0
- package/src/namespaces/billing.ts +17 -0
- package/src/namespaces/builder.ts +52 -0
- package/src/namespaces/configurations.ts +19 -0
- package/src/namespaces/context.ts +67 -0
- package/src/namespaces/desk.ts +679 -0
- package/src/namespaces/media.ts +39 -0
- package/src/namespaces/namespace.ts +125 -0
- package/src/namespaces/plugins.ts +223 -0
- package/src/namespaces/portal.ts +402 -0
- package/src/namespaces/scheduler.ts +88 -0
- package/src/namespaces/whatsapp.ts +383 -0
- package/src/sender/bliperror.ts +42 -0
- package/src/sender/enveloperesolver.ts +148 -0
- package/src/sender/gateway/customgatewaysender.ts +43 -0
- package/src/sender/http/httpsender.ts +94 -0
- package/src/sender/index.ts +7 -0
- package/src/sender/plugin/communication.ts +72 -0
- package/src/sender/plugin/pluginsender.ts +75 -0
- package/src/sender/security.ts +33 -0
- package/src/sender/sender.ts +145 -0
- package/src/sender/sessionnegotiator.ts +175 -0
- package/src/sender/tcp/tcpsender.ts +252 -0
- package/src/sender/throttler.ts +36 -0
- package/src/sender/websocket/websocketsender.ts +175 -0
- package/src/types/account.ts +84 -0
- package/src/types/analytics.ts +18 -0
- package/src/types/billing.ts +15 -0
- package/src/types/command.ts +47 -0
- package/src/types/commons.ts +16 -0
- package/src/types/desk.ts +51 -0
- package/src/types/envelope.ts +9 -0
- package/src/types/flow.ts +327 -0
- package/src/types/index.ts +13 -0
- package/src/types/message.ts +116 -0
- package/src/types/node.ts +86 -0
- package/src/types/notification.ts +18 -0
- package/src/types/plugins.ts +51 -0
- package/src/types/portal.ts +39 -0
- package/src/types/reason.ts +22 -0
- package/src/types/session.ts +22 -0
- package/src/types/whatsapp.ts +84 -0
- package/src/utils/odata.ts +114 -0
- package/src/utils/random.ts +3 -0
- 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
|
+
}
|