@ikatec/digisac-api-sdk 4.5.0 → 4.6.0
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/README.md +5 -3
- package/dist/apis/answers/AnswersApi.cjs +38 -0
- package/dist/apis/answers/AnswersApi.cjs.map +1 -1
- package/dist/apis/answers/AnswersApi.d.ts +33 -2
- package/dist/apis/answers/AnswersApi.d.ts.map +1 -1
- package/dist/apis/answers/AnswersApi.mjs +38 -0
- package/dist/apis/answers/AnswersApi.mjs.map +1 -1
- package/dist/apis/answers/types.d.ts +64 -1
- package/dist/apis/answers/types.d.ts.map +1 -1
- package/dist/apis/botSimulators/BotSimulatorsApi.cjs +42 -0
- package/dist/apis/botSimulators/BotSimulatorsApi.cjs.map +1 -0
- package/dist/apis/botSimulators/BotSimulatorsApi.d.ts +31 -0
- package/dist/apis/botSimulators/BotSimulatorsApi.d.ts.map +1 -0
- package/dist/apis/botSimulators/BotSimulatorsApi.mjs +41 -0
- package/dist/apis/botSimulators/BotSimulatorsApi.mjs.map +1 -0
- package/dist/apis/botSimulators/index.cjs +4 -0
- package/dist/apis/botSimulators/index.d.ts +3 -0
- package/dist/apis/botSimulators/index.d.ts.map +1 -0
- package/dist/apis/botSimulators/index.mjs +3 -0
- package/dist/apis/botSimulators/types.cjs +0 -0
- package/dist/apis/botSimulators/types.d.ts +73 -0
- package/dist/apis/botSimulators/types.d.ts.map +1 -0
- package/dist/apis/botSimulators/types.mjs +0 -0
- package/dist/apis/botVersions/BotVersionsApi.cjs +25 -0
- package/dist/apis/botVersions/BotVersionsApi.cjs.map +1 -1
- package/dist/apis/botVersions/BotVersionsApi.d.ts +18 -0
- package/dist/apis/botVersions/BotVersionsApi.d.ts.map +1 -1
- package/dist/apis/botVersions/BotVersionsApi.mjs +25 -0
- package/dist/apis/botVersions/BotVersionsApi.mjs.map +1 -1
- package/dist/apis/botVersions/types.d.ts +4 -0
- package/dist/apis/botVersions/types.d.ts.map +1 -1
- package/dist/apis/bots/BotsApi.cjs +99 -4
- package/dist/apis/bots/BotsApi.cjs.map +1 -1
- package/dist/apis/bots/BotsApi.d.ts +78 -5
- package/dist/apis/bots/BotsApi.d.ts.map +1 -1
- package/dist/apis/bots/BotsApi.mjs +99 -4
- package/dist/apis/bots/BotsApi.mjs.map +1 -1
- package/dist/apis/bots/types.d.ts +266 -16
- package/dist/apis/bots/types.d.ts.map +1 -1
- package/dist/apis/index.cjs +3 -0
- package/dist/apis/index.d.ts +1 -0
- package/dist/apis/index.d.ts.map +1 -1
- package/dist/apis/index.mjs +3 -1
- package/dist/apis/permissions/types.d.ts +1 -1
- package/dist/apis/permissions/types.d.ts.map +1 -1
- package/dist/apis/questions/QuestionsApi.cjs +15 -0
- package/dist/apis/questions/QuestionsApi.cjs.map +1 -1
- package/dist/apis/questions/QuestionsApi.d.ts +15 -1
- package/dist/apis/questions/QuestionsApi.d.ts.map +1 -1
- package/dist/apis/questions/QuestionsApi.mjs +15 -0
- package/dist/apis/questions/QuestionsApi.mjs.map +1 -1
- package/dist/apis/questions/types.d.ts +25 -5
- package/dist/apis/questions/types.d.ts.map +1 -1
- package/dist/apis/quickReplies/QuickRepliesApi.cjs +16 -0
- package/dist/apis/quickReplies/QuickRepliesApi.cjs.map +1 -1
- package/dist/apis/quickReplies/QuickRepliesApi.d.ts +15 -2
- package/dist/apis/quickReplies/QuickRepliesApi.d.ts.map +1 -1
- package/dist/apis/quickReplies/QuickRepliesApi.mjs +16 -0
- package/dist/apis/quickReplies/QuickRepliesApi.mjs.map +1 -1
- package/dist/apis/quickReplies/types.d.ts +30 -2
- package/dist/apis/quickReplies/types.d.ts.map +1 -1
- package/dist/apis/schedule/ScheduleApi.cjs +37 -2
- package/dist/apis/schedule/ScheduleApi.cjs.map +1 -1
- package/dist/apis/schedule/ScheduleApi.d.ts +30 -3
- package/dist/apis/schedule/ScheduleApi.d.ts.map +1 -1
- package/dist/apis/schedule/ScheduleApi.mjs +37 -2
- package/dist/apis/schedule/ScheduleApi.mjs.map +1 -1
- package/dist/apis/schedule/types.d.ts +58 -11
- package/dist/apis/schedule/types.d.ts.map +1 -1
- package/dist/apis/ticketTopics/TicketTopicsApi.cjs +25 -2
- package/dist/apis/ticketTopics/TicketTopicsApi.cjs.map +1 -1
- package/dist/apis/ticketTopics/TicketTopicsApi.d.ts +18 -4
- package/dist/apis/ticketTopics/TicketTopicsApi.d.ts.map +1 -1
- package/dist/apis/ticketTopics/TicketTopicsApi.mjs +25 -2
- package/dist/apis/ticketTopics/TicketTopicsApi.mjs.map +1 -1
- package/dist/apis/ticketTopics/types.d.ts +22 -1
- package/dist/apis/ticketTopics/types.d.ts.map +1 -1
- package/dist/apis/tickets/types.d.ts +37 -1
- package/dist/apis/tickets/types.d.ts.map +1 -1
- package/dist/core/BaseCrudApi.cjs +5 -2
- package/dist/core/BaseCrudApi.cjs.map +1 -1
- package/dist/core/BaseCrudApi.d.ts +7 -4
- package/dist/core/BaseCrudApi.d.ts.map +1 -1
- package/dist/core/BaseCrudApi.mjs +5 -2
- package/dist/core/BaseCrudApi.mjs.map +1 -1
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/incommingWebhooks/index.d.ts +7 -7
- package/dist/incommingWebhooks/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/apis/answers/AnswersApi.test.ts +67 -0
- package/src/apis/answers/AnswersApi.ts +66 -2
- package/src/apis/answers/types.ts +64 -2
- package/src/apis/botSimulators/BotSimulatorsApi.test.ts +58 -0
- package/src/apis/botSimulators/BotSimulatorsApi.ts +57 -0
- package/src/apis/botSimulators/index.ts +2 -0
- package/src/apis/botSimulators/types.ts +72 -0
- package/src/apis/botVersions/BotVersionsApi.test.ts +51 -0
- package/src/apis/botVersions/BotVersionsApi.ts +41 -0
- package/src/apis/botVersions/types.ts +4 -0
- package/src/apis/bots/BotsApi.test.ts +177 -0
- package/src/apis/bots/BotsApi.ts +162 -6
- package/src/apis/bots/types.ts +267 -15
- package/src/apis/index.ts +1 -0
- package/src/apis/payloads.test-d.ts +171 -1
- package/src/apis/permissions/types.ts +10 -4
- package/src/apis/questions/QuestionsApi.test.ts +49 -0
- package/src/apis/questions/QuestionsApi.ts +20 -1
- package/src/apis/questions/types.ts +25 -6
- package/src/apis/quickReplies/QuickRepliesApi.test.ts +69 -0
- package/src/apis/quickReplies/QuickRepliesApi.ts +35 -2
- package/src/apis/quickReplies/types.ts +34 -2
- package/src/apis/schedule/ScheduleApi.test.ts +80 -0
- package/src/apis/schedule/ScheduleApi.ts +59 -6
- package/src/apis/schedule/types.ts +66 -11
- package/src/apis/ticketTopics/TicketTopicsApi.test.ts +40 -0
- package/src/apis/ticketTopics/TicketTopicsApi.ts +35 -4
- package/src/apis/ticketTopics/types.ts +26 -1
- package/src/apis/tickets/types.ts +40 -1
- package/src/core/BaseCrudApi.ts +9 -5
- package/src/core/types.ts +2 -1
- package/src/incommingWebhooks/index.test-d.ts +9 -0
- package/src/incommingWebhooks/index.ts +7 -7
package/src/apis/bots/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BotVersion } from '../botVersions/types'
|
|
2
|
+
import type { Contact } from '../contacts/types'
|
|
2
3
|
import type { Service } from '../services/types'
|
|
3
4
|
|
|
4
5
|
// ─── Settings ─────────────────────────────────────────────────────────────────
|
|
@@ -35,6 +36,10 @@ export type BotEvent =
|
|
|
35
36
|
| 'CONTACT_RECONNECTED'
|
|
36
37
|
| 'API_SIGNAL'
|
|
37
38
|
| 'ANONYMOUS_USER_CREATED_WEBCHAT'
|
|
39
|
+
/** Fired by the worker in `@EVERY` when the contact stops answering the bot. */
|
|
40
|
+
| 'BOT_INACTIVE'
|
|
41
|
+
/** Fired by the worker when an asynchronous `HTTP_REQUEST` action gets its callback. */
|
|
42
|
+
| 'HTTP_REQUEST_CALLBACK'
|
|
38
43
|
|
|
39
44
|
export type BotFlowTriggerNode = {
|
|
40
45
|
id: string
|
|
@@ -69,8 +74,19 @@ export type BotFlowEdge = {
|
|
|
69
74
|
sourceHandle: string
|
|
70
75
|
}
|
|
71
76
|
|
|
72
|
-
/** Visual flow graph used by the flow builder UI
|
|
77
|
+
/** Visual flow graph used by the flow builder UI, as the API answers it. */
|
|
73
78
|
export type BotFlowJson = {
|
|
79
|
+
nodes: BotFlowNode[]
|
|
80
|
+
/** Absent in a bot saved without a flow (`{ nodes: [] }`). */
|
|
81
|
+
edges?: BotFlowEdge[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The flow graph to save. `edges` is required: a bot saved without it (or without `flowJson`)
|
|
86
|
+
* never sends a message — the worker fails reading `flowJson.edges` (SD-2323). Send at least
|
|
87
|
+
* `{ nodes: [], edges: [] }`.
|
|
88
|
+
*/
|
|
89
|
+
export type BotFlowJsonInput = {
|
|
74
90
|
nodes: BotFlowNode[]
|
|
75
91
|
edges: BotFlowEdge[]
|
|
76
92
|
}
|
|
@@ -108,7 +124,7 @@ export type BotActionDataMap = {
|
|
|
108
124
|
actions?: { title: string; value: string }[]
|
|
109
125
|
}
|
|
110
126
|
SEND_TERM: {
|
|
111
|
-
/** Acceptance term UUID. */
|
|
127
|
+
/** Acceptance term UUID (as stored); see `BotActionInput` for how to send it. */
|
|
112
128
|
acceptanceTerms?: string
|
|
113
129
|
}
|
|
114
130
|
CONFIRM_TERM: {
|
|
@@ -170,14 +186,21 @@ export type BotActionDataMap = {
|
|
|
170
186
|
maxAttempts?: number
|
|
171
187
|
/** Actions to take based on AI agent outcomes. */
|
|
172
188
|
actions?: { context?: string; action: { id: string; type: string } }[]
|
|
189
|
+
/** Knowledge base subject UUIDs the agent answers from. */
|
|
190
|
+
knowledgeBase?: string[]
|
|
191
|
+
/** Waits `waitSeconds` to group the contact's messages before answering. */
|
|
192
|
+
messageGrouping?: { enabled?: boolean; waitSeconds?: number }
|
|
173
193
|
}
|
|
174
194
|
HTTP_REQUEST: {
|
|
175
195
|
requestName?: string
|
|
176
196
|
endpointUrl?: string
|
|
177
197
|
httpMethod?: 'get' | 'post' | 'put' | 'patch' | 'delete'
|
|
178
198
|
isAsyncRequest?: boolean
|
|
199
|
+
/** Sends the Digisac payload (contact, ticket, message) along with the request. */
|
|
200
|
+
sendDigisacPayload?: boolean
|
|
179
201
|
bodyType?: 'json' | 'xml' | 'formData' | 'empty'
|
|
180
|
-
|
|
202
|
+
/** A `secret` header comes back from the API with `value: null`. */
|
|
203
|
+
headers?: { key: string; value: string | null; secret?: boolean }[]
|
|
181
204
|
requestBodyFormData?: { key: string; value: string }[]
|
|
182
205
|
requestBodyJson?: string
|
|
183
206
|
requestBodyXml?: string
|
|
@@ -248,40 +271,269 @@ export type BotContexts = {
|
|
|
248
271
|
[contextId: string]: BotContext
|
|
249
272
|
}
|
|
250
273
|
|
|
274
|
+
// ─── Contexts as the /bots routes take and return them ───────────────────────
|
|
275
|
+
//
|
|
276
|
+
// `BotContexts` above is the stored form (a map by context id), served as is by `/bot-versions`
|
|
277
|
+
// and `currentBotVersion`. The `/bots` routes take the contexts as a LIST instead and answer the
|
|
278
|
+
// list again, with each trigger, variable, operator and action described by its option object.
|
|
279
|
+
|
|
280
|
+
/** A variable, operator or action option as the bot builder describes it. */
|
|
281
|
+
export type BotOption<TValue extends string = string> = { value: TValue; name?: string }
|
|
282
|
+
|
|
283
|
+
export type BotConditionTarget =
|
|
284
|
+
| string
|
|
285
|
+
| number
|
|
286
|
+
| string[]
|
|
287
|
+
| { id: string }
|
|
288
|
+
| { id: string }[]
|
|
289
|
+
| { value: string }
|
|
290
|
+
| { value: string }[]
|
|
291
|
+
| { variableName: string; variableValue: string }
|
|
292
|
+
|
|
293
|
+
/** A condition is dropped in silence when `variable`, `operator` or a truthy `target` is missing. */
|
|
294
|
+
export type BotConditionInput = {
|
|
295
|
+
variable: BotOption
|
|
296
|
+
operator: BotOption
|
|
297
|
+
target: BotConditionTarget
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export type BotConditionGroupInput = {
|
|
301
|
+
conditionType: 'and' | 'or'
|
|
302
|
+
conditions: BotConditionInput[]
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Actions the API saves. `SET_VAR` runs in the worker, but saving it answers 500. */
|
|
306
|
+
export type BotSavableActionType = Exclude<BotActionType, 'SET_VAR'>
|
|
307
|
+
|
|
308
|
+
export type BotActionInput = {
|
|
309
|
+
[K in BotSavableActionType]: {
|
|
310
|
+
type: BotOption<K>
|
|
311
|
+
/** Related records go as their id, except the ones below. */
|
|
312
|
+
data: K extends 'SEND_MESSAGE'
|
|
313
|
+
? Omit<BotActionDataMap['SEND_MESSAGE'], 'file'> & {
|
|
314
|
+
/**
|
|
315
|
+
* The file goes as an object: a bare id is dropped. `{ fileName, mimetype, base64Url }`
|
|
316
|
+
* uploads a new one.
|
|
317
|
+
*/
|
|
318
|
+
file?: { id: string } | { fileName: string; mimetype: string; base64Url: string }
|
|
319
|
+
}
|
|
320
|
+
: K extends 'SEND_TERM'
|
|
321
|
+
? {
|
|
322
|
+
/**
|
|
323
|
+
* Send the term as `{ id }`: the bot keeps it as `data.currentDefaultTerm`, and both
|
|
324
|
+
* the "term in use" check of `DELETE /acceptance-terms/:id` and the reset of the
|
|
325
|
+
* contacts' acceptance read its `id`. A bare id is saved, but those checks miss it.
|
|
326
|
+
*/
|
|
327
|
+
acceptanceTerms?: { id: string } | string
|
|
328
|
+
}
|
|
329
|
+
: BotActionDataMap[K]
|
|
330
|
+
}
|
|
331
|
+
}[BotSavableActionType]
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* A `SEND_MESSAGE` without text, interactive or file is dropped, and so is a rule left with no
|
|
335
|
+
* action (except a `BOT_INACTIVE` rule with `data`).
|
|
336
|
+
*/
|
|
337
|
+
export type BotRuleInput = {
|
|
338
|
+
title?: string
|
|
339
|
+
actions: BotActionInput[]
|
|
340
|
+
fallbackActions?: BotActionInput[]
|
|
341
|
+
/** Required: a missing `conditions` answers 500. */
|
|
342
|
+
conditions: BotConditionInput[] | BotConditionGroupInput
|
|
343
|
+
data?: Record<string, unknown>
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export type BotTriggerInput = {
|
|
347
|
+
trigger: { type: BotEvent }
|
|
348
|
+
rules: BotRuleInput[]
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** `@INIT`, `@EVERY` and `@FALLBACK` are the built-in contexts; other ids are user-defined. */
|
|
352
|
+
export type BotContextInput = {
|
|
353
|
+
id: string
|
|
354
|
+
name?: string
|
|
355
|
+
triggers: BotTriggerInput[]
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export type BotParsedCondition =
|
|
359
|
+
| {
|
|
360
|
+
/** `undefined` for a variable the builder does not list. */
|
|
361
|
+
variable?: BotOption & { type?: string; subType?: string }
|
|
362
|
+
operator?: BotOption
|
|
363
|
+
/** Departments and tags come back as their records. */
|
|
364
|
+
target: unknown
|
|
365
|
+
}
|
|
366
|
+
| { $and: BotParsedCondition[] }
|
|
367
|
+
| { $or: BotParsedCondition[] }
|
|
368
|
+
|
|
369
|
+
export type BotParsedAction = {
|
|
370
|
+
type: { value: BotSavableActionType; name: string }
|
|
371
|
+
/** The ids of questions, terms, tags, departments, users etc. come back as their records. */
|
|
372
|
+
data: Record<string, unknown>
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export type BotParsedRule = {
|
|
376
|
+
title: string
|
|
377
|
+
actions: BotParsedAction[]
|
|
378
|
+
fallbackActions: BotParsedAction[]
|
|
379
|
+
conditions: BotParsedCondition[]
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export type BotParsedTrigger = {
|
|
383
|
+
/** `undefined` for the worker events the builder does not list (`LEAVE_CONTEXT`, `BOT_INACTIVE`, `HTTP_REQUEST_CALLBACK`). */
|
|
384
|
+
trigger?: { type: BotEvent; name: string }
|
|
385
|
+
rules: BotParsedRule[]
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export type BotParsedContext = {
|
|
389
|
+
id: string
|
|
390
|
+
name: string
|
|
391
|
+
triggers: BotParsedTrigger[]
|
|
392
|
+
}
|
|
393
|
+
|
|
251
394
|
// ─── Bot ──────────────────────────────────────────────────────────────────────
|
|
252
395
|
|
|
396
|
+
/** Bots are hard-deleted (no `deletedAt`). */
|
|
253
397
|
export type Bot = {
|
|
254
398
|
id: string
|
|
255
399
|
name: string
|
|
256
|
-
|
|
257
|
-
|
|
400
|
+
/** Rewritten on every save: the `SEND_TERM` acceptance term of the contexts, as it was sent. */
|
|
401
|
+
data: { currentDefaultTerm?: { id: string } | string | null } & Record<string, unknown>
|
|
402
|
+
/**
|
|
403
|
+
* The list form (see `BotParsedContext`), not the stored map. Saving it back as is loses the
|
|
404
|
+
* `data` of the rules and the `{{@variable}}` conditions (the list does not carry them).
|
|
405
|
+
*/
|
|
406
|
+
contexts: BotParsedContext[]
|
|
258
407
|
flowJson: BotFlowJson | null
|
|
259
408
|
settings: BotSettings
|
|
260
409
|
currentBotVersionId: string | null
|
|
261
410
|
accountId: string
|
|
262
411
|
createdAt: string
|
|
263
412
|
updatedAt: string
|
|
264
|
-
|
|
413
|
+
/**
|
|
414
|
+
* The stored map of `contexts`: a JSON string in `getById`, an object in `updateById`; absent
|
|
415
|
+
* in `getMany` and `create`.
|
|
416
|
+
*/
|
|
417
|
+
defaultContexts?: string | BotContexts
|
|
265
418
|
// relationships
|
|
266
419
|
currentBotVersion?: BotVersion | null
|
|
267
420
|
services?: Service[]
|
|
421
|
+
/** In `getById`, the first version comes with its contexts in the list form. */
|
|
422
|
+
botVersions?: BotVersion[]
|
|
268
423
|
}
|
|
269
424
|
|
|
425
|
+
export type BotRelationships = 'currentBotVersion' | 'services' | 'botVersions'
|
|
426
|
+
|
|
427
|
+
/** The bot as stored (contexts in the map form), as the `bot.*` webhooks deliver it. */
|
|
428
|
+
export type BotRecord = Omit<
|
|
429
|
+
Bot,
|
|
430
|
+
'contexts' | 'defaultContexts' | 'flowJson' | BotRelationships
|
|
431
|
+
> & {
|
|
432
|
+
contexts: BotContexts
|
|
433
|
+
/**
|
|
434
|
+
* Raw, unlike the API answers: `{}` for a bot saved without a flow, and the bare list of nodes
|
|
435
|
+
* for a bot created with `isDraft`.
|
|
436
|
+
*/
|
|
437
|
+
flowJson: Partial<BotFlowJson> | BotFlowNode[] | null
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** What the `bot.api-signal` webhook delivers (the `triggerSignal` call), not the bot. */
|
|
441
|
+
export type BotApiSignalData = {
|
|
442
|
+
botId: string
|
|
443
|
+
contactId: string
|
|
444
|
+
flag?: string
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/** A duplicate name answers 409, unless the account allows duplicate names. */
|
|
270
448
|
export type CreateBotPayload = {
|
|
271
449
|
name: string
|
|
272
|
-
|
|
273
|
-
contexts
|
|
274
|
-
|
|
450
|
+
/** Required: a missing `contexts` answers 500. */
|
|
451
|
+
contexts: BotContextInput[]
|
|
452
|
+
/** Omitted, the bot never sends a message (SD-2323): send at least `{ nodes: [], edges: [] }`. */
|
|
453
|
+
flowJson?: BotFlowJsonInput
|
|
275
454
|
settings?: BotSettings
|
|
455
|
+
/**
|
|
456
|
+
* Saves the version as a draft instead of publishing it. The bot itself still takes the draft
|
|
457
|
+
* contexts on create, and its `flowJson` is saved as the bare list of nodes.
|
|
458
|
+
*/
|
|
459
|
+
isDraft?: boolean
|
|
460
|
+
/** The id of a bot of the account to duplicate: its secret HTTP headers are kept. */
|
|
461
|
+
id?: string
|
|
462
|
+
/** @deprecated Ignored: the backend rewrites `data` on every save. */
|
|
463
|
+
data?: Record<string, unknown>
|
|
276
464
|
}
|
|
277
465
|
|
|
278
|
-
|
|
466
|
+
/** Renames the bot without touching its contexts or versions. */
|
|
467
|
+
export type RenameBotPayload = {
|
|
468
|
+
name: string
|
|
469
|
+
onlyUpdateBotName: true
|
|
470
|
+
}
|
|
279
471
|
|
|
280
|
-
export type
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
472
|
+
export type SaveBotPayload = {
|
|
473
|
+
/** Required on update too. */
|
|
474
|
+
name: string
|
|
475
|
+
/**
|
|
476
|
+
* The whole flow: it replaces the published one (or the draft, with `isDraft`). An empty list
|
|
477
|
+
* publishes a bot with no contexts.
|
|
478
|
+
*/
|
|
479
|
+
contexts: BotContextInput[]
|
|
480
|
+
/** Omitted or without `edges`, the bot never sends a message (SD-2323). */
|
|
481
|
+
flowJson?: BotFlowJsonInput
|
|
285
482
|
settings?: BotSettings
|
|
483
|
+
/** Saves into the latest draft (or a new one) instead of publishing. */
|
|
484
|
+
isDraft?: boolean
|
|
485
|
+
/** @deprecated Ignored: the backend rewrites `data` on every save. */
|
|
486
|
+
data?: Record<string, unknown>
|
|
487
|
+
/** @deprecated Ignored: every publish points the bot to the version it saves. */
|
|
286
488
|
currentBotVersionId?: string | null
|
|
287
489
|
}
|
|
490
|
+
|
|
491
|
+
export type UpdateBotPayload = RenameBotPayload | SaveBotPayload
|
|
492
|
+
|
|
493
|
+
// ─── Bot errors and extra routes ─────────────────────────────────────────────
|
|
494
|
+
|
|
495
|
+
export type BotHistoryStatus = 'PENDING' | 'SUCCESS' | 'FAILED'
|
|
496
|
+
|
|
497
|
+
/** A step of a bot run, as `getErrors` lists it (always `status: 'FAILED'`). */
|
|
498
|
+
export type BotHistory = {
|
|
499
|
+
id: string
|
|
500
|
+
action: string
|
|
501
|
+
context: string
|
|
502
|
+
payload: Record<string, unknown>
|
|
503
|
+
status: BotHistoryStatus
|
|
504
|
+
errorCode: string | null
|
|
505
|
+
errorMessage: string | null
|
|
506
|
+
createdAt: string
|
|
507
|
+
updatedAt: string
|
|
508
|
+
botId: string
|
|
509
|
+
botSessionId: string
|
|
510
|
+
accountId: string
|
|
511
|
+
contactId: string
|
|
512
|
+
contact?: Contact & { service?: Service }
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export type BotErrorsQuery = {
|
|
516
|
+
page?: number
|
|
517
|
+
/** Defaults to 15, with no upper limit. */
|
|
518
|
+
perPage?: number
|
|
519
|
+
/** A column of the history, or `serviceName` for the connection of the contact. Defaults to `createdAt`. */
|
|
520
|
+
sort?: string
|
|
521
|
+
order?: 'ASC' | 'DESC'
|
|
522
|
+
errorCode?: string
|
|
523
|
+
serviceId?: string
|
|
524
|
+
/** Matches part of the action name. */
|
|
525
|
+
search?: string
|
|
526
|
+
/** ISO date. */
|
|
527
|
+
startDate?: string
|
|
528
|
+
/** ISO date. */
|
|
529
|
+
endDate?: string
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export type SuggestAgentPromptPayload = {
|
|
533
|
+
voiceTone: 'formal' | 'neutral' | 'informal'
|
|
534
|
+
languageType: 'simple' | 'neutral' | 'technical'
|
|
535
|
+
function: 'triage' | 'sales' | 'answer-questions' | 'support'
|
|
536
|
+
companySegment: string
|
|
537
|
+
companySubject: string
|
|
538
|
+
companyServices: string
|
|
539
|
+
}
|
package/src/apis/index.ts
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
import { expectError } from 'tsd'
|
|
1
|
+
import { expectAssignable, expectError, expectNotAssignable, expectType } from 'tsd'
|
|
2
2
|
import type { ApiClient } from '../core/ApiClient'
|
|
3
|
+
import type { Paginated } from '../core/types'
|
|
4
|
+
import { AnswersApi } from './answers/AnswersApi'
|
|
5
|
+
import type { AnswersOverview } from './answers/types'
|
|
6
|
+
import { BotSimulatorsApi } from './botSimulators/BotSimulatorsApi'
|
|
7
|
+
import { BotsApi } from './bots/BotsApi'
|
|
8
|
+
import type { Bot, BotContextInput, BotParsedContext } from './bots/types'
|
|
3
9
|
import { CardsApi } from './cards/CardsApi'
|
|
4
10
|
import { ContactsApi } from './contacts/ContactsApi'
|
|
5
11
|
import { KanbanColumnsApi } from './kanbanColumns/KanbanColumnsApi'
|
|
6
12
|
import { PipelineApi } from './pipeline/PipelineApi'
|
|
13
|
+
import type { PermissionName } from './permissions/types'
|
|
7
14
|
import { PersonalAccessTokensApi } from './personalAccessTokens/PersonalAccessTokensApi'
|
|
15
|
+
import { QuestionsApi } from './questions/QuestionsApi'
|
|
16
|
+
import { QuickRepliesApi } from './quickReplies/QuickRepliesApi'
|
|
8
17
|
import { RolesApi } from './roles/RolesApi'
|
|
18
|
+
import { ScheduleApi } from './schedule/ScheduleApi'
|
|
19
|
+
import type { ScheduleRecord } from './schedule/types'
|
|
9
20
|
import { TagsApi } from './tags/TagsApi'
|
|
21
|
+
import { TicketTopicsApi } from './ticketTopics/TicketTopicsApi'
|
|
22
|
+
import { TicketsApi } from './tickets/TicketsApi'
|
|
23
|
+
import type { Ticket, TicketTransfer } from './tickets/types'
|
|
10
24
|
import { TimetableApi } from './timetable/TimetableApi'
|
|
11
25
|
import { UsersApi } from './users/UsersApi'
|
|
12
26
|
|
|
@@ -93,3 +107,159 @@ expectError(cards.saveComment({ comment: 'no card nor id' }))
|
|
|
93
107
|
const columns = new KanbanColumnsApi(client)
|
|
94
108
|
columns.reorder('col-1', { beforeColumnId: 'col-0', afterColumnId: 'col-2' })
|
|
95
109
|
expectError(columns.reorder('col-1', { position: 'M' }))
|
|
110
|
+
|
|
111
|
+
// ── permissions: the kanban permissions were split by the migration 20260209144630 ──
|
|
112
|
+
|
|
113
|
+
expectAssignable<PermissionName>('kanban.boards.view')
|
|
114
|
+
expectAssignable<PermissionName>('kanban.cards.move')
|
|
115
|
+
expectAssignable<PermissionName>('users.export')
|
|
116
|
+
expectNotAssignable<PermissionName>('kanban.view')
|
|
117
|
+
expectNotAssignable<PermissionName>('kanban.update')
|
|
118
|
+
|
|
119
|
+
// ── tickets: the history of the ticket comes in the ticketTransfers include ──
|
|
120
|
+
|
|
121
|
+
const tickets = new TicketsApi(client)
|
|
122
|
+
tickets.getById('t-1', { include: ['ticketTransfers'] })
|
|
123
|
+
tickets.getById('t-1', { include: [{ model: 'ticketTransfers', include: ['byUser'] }] })
|
|
124
|
+
declare const ticket: Ticket
|
|
125
|
+
expectType<TicketTransfer[] | undefined>(ticket.ticketTransfers)
|
|
126
|
+
declare const transfer: TicketTransfer
|
|
127
|
+
expectType<string | null>(transfer.byUserId)
|
|
128
|
+
expectType<'opened' | 'transferred' | 'closed' | null>(transfer.action)
|
|
129
|
+
|
|
130
|
+
// ── bots: the /bots routes take and return the contexts as a list ───
|
|
131
|
+
|
|
132
|
+
const bots = new BotsApi(client)
|
|
133
|
+
const botContexts: BotContextInput[] = [
|
|
134
|
+
{
|
|
135
|
+
id: '@INIT',
|
|
136
|
+
triggers: [
|
|
137
|
+
{
|
|
138
|
+
trigger: { type: 'MESSAGE_RECEIVED' },
|
|
139
|
+
rules: [
|
|
140
|
+
{ conditions: [], actions: [{ type: { value: 'SEND_MESSAGE' }, data: { text: 'Oi' } }] },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
]
|
|
146
|
+
bots.create({ name: 'Bot', contexts: botContexts, flowJson: { nodes: [], edges: [] } })
|
|
147
|
+
expectError(bots.create({ name: 'Bot', contexts: botContexts, flowJson: { nodes: [] } }))
|
|
148
|
+
expectError(bots.create({ name: 'Bot' }))
|
|
149
|
+
expectError(bots.create({ name: 'Bot', contexts: { '@INIT': { name: 'Início', triggers: {} } } }))
|
|
150
|
+
expectError(
|
|
151
|
+
bots.create({
|
|
152
|
+
name: 'Bot',
|
|
153
|
+
contexts: [
|
|
154
|
+
{
|
|
155
|
+
id: '@INIT',
|
|
156
|
+
triggers: [
|
|
157
|
+
{
|
|
158
|
+
trigger: { type: 'MESSAGE_RECEIVED' },
|
|
159
|
+
rules: [
|
|
160
|
+
{
|
|
161
|
+
conditions: [],
|
|
162
|
+
actions: [{ type: { value: 'SET_VAR' }, data: { key: 'k', value: 'v' } }],
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
}),
|
|
170
|
+
)
|
|
171
|
+
bots.updateById('b-1', { name: 'Bot', contexts: botContexts, isDraft: true })
|
|
172
|
+
bots.updateById('b-1', { name: 'Bot', onlyUpdateBotName: true })
|
|
173
|
+
bots.create({
|
|
174
|
+
name: 'Bot',
|
|
175
|
+
contexts: [
|
|
176
|
+
{
|
|
177
|
+
id: '@INIT',
|
|
178
|
+
triggers: [
|
|
179
|
+
{
|
|
180
|
+
trigger: { type: 'TICKET_OPENED' },
|
|
181
|
+
rules: [
|
|
182
|
+
{
|
|
183
|
+
conditions: [],
|
|
184
|
+
actions: [
|
|
185
|
+
{ type: { value: 'SEND_TERM' }, data: { acceptanceTerms: { id: 'term-1' } } },
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
})
|
|
194
|
+
expectError(bots.updateById('b-1', { contexts: botContexts }))
|
|
195
|
+
expectError(bots.updateById('b-1', { name: 'Bot' }))
|
|
196
|
+
expectType<Promise<Paginated<Bot>>>(bots.getMany({ paginate: false }))
|
|
197
|
+
expectType<Promise<'OK' | false>>(bots.deleteById('b-1'))
|
|
198
|
+
declare const bot: Bot
|
|
199
|
+
expectType<BotParsedContext[]>(bot.contexts)
|
|
200
|
+
|
|
201
|
+
// ── bot simulator: the trigger pick needs the id of the selection message ──
|
|
202
|
+
|
|
203
|
+
const simulator = new BotSimulatorsApi(client)
|
|
204
|
+
simulator.sendEvent('b-1', { type: 'text_contact', text: 'oi' })
|
|
205
|
+
simulator.sendEvent('b-1', { type: 'select_trigger', id: 'm-1', selectedValue: 'TICKET_OPENED' })
|
|
206
|
+
expectError(simulator.sendEvent('b-1', { type: 'select_trigger', selectedValue: 'TICKET_OPENED' }))
|
|
207
|
+
expectError(
|
|
208
|
+
simulator.sendEvent('b-1', { type: 'select_condition', id: 'm-1', selectedValue: 'yes' }),
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
// ── questions: duration goes as a string; update is not partial ─────
|
|
212
|
+
|
|
213
|
+
const questions = new QuestionsApi(client)
|
|
214
|
+
const question = {
|
|
215
|
+
name: 'NPS',
|
|
216
|
+
type: 'nps',
|
|
217
|
+
questionMessage: 'De 0 a 10?',
|
|
218
|
+
duration: '60',
|
|
219
|
+
tries: 3,
|
|
220
|
+
successMessage: 'Obrigado!',
|
|
221
|
+
invalidMessage: 'De 0 a 10, por favor',
|
|
222
|
+
} as const
|
|
223
|
+
questions.create(question)
|
|
224
|
+
expectType<Promise<'OK'>>(questions.deleteById('q-1'))
|
|
225
|
+
expectError(questions.create({ ...question, duration: 60 }))
|
|
226
|
+
questions.updateById('q-1', { type: 'nps', questionMessage: 'x', duration: 60, tries: 3 })
|
|
227
|
+
expectError(questions.updateById('q-1', { name: 'NPS' }))
|
|
228
|
+
|
|
229
|
+
// ── answers: the overview NPS is positional ─────────────────────────
|
|
230
|
+
|
|
231
|
+
const answers = new AnswersApi(client)
|
|
232
|
+
expectType<Promise<AnswersOverview>>(answers.overview({ type: 'nps', periodType: 'closeDate' }))
|
|
233
|
+
expectError(answers.overview({ type: 'custom' }))
|
|
234
|
+
expectType<Promise<'OK'>>(answers.deleteById('a-1'))
|
|
235
|
+
|
|
236
|
+
// ── schedule: create answers no body; update is not partial ─────────
|
|
237
|
+
|
|
238
|
+
const schedule = new ScheduleApi(client)
|
|
239
|
+
const scheduled = {
|
|
240
|
+
scheduledAt: '2026-10-02T12:00:00Z',
|
|
241
|
+
contactId: 'c-1',
|
|
242
|
+
departmentId: 'd-1',
|
|
243
|
+
notes: '',
|
|
244
|
+
}
|
|
245
|
+
expectType<Promise<void>>(schedule.create({ ...scheduled, message: 'Oi' }))
|
|
246
|
+
expectType<Promise<ScheduleRecord>>(schedule.updateById('s-1', scheduled))
|
|
247
|
+
expectError(schedule.updateById('s-1', { message: 'Oi' }))
|
|
248
|
+
expectError(schedule.create({ ...scheduled, files: [{ name: 'a.png' }] }))
|
|
249
|
+
expectError(schedule.create({ ...scheduled, files: [{ id: 'f-1' }] }))
|
|
250
|
+
schedule.updateById('s-1', { ...scheduled, files: [{ id: 'f-1' }] })
|
|
251
|
+
|
|
252
|
+
// ── ticket topics: create needs the departments ─────────────────────
|
|
253
|
+
|
|
254
|
+
const topics = new TicketTopicsApi(client)
|
|
255
|
+
topics.create({ name: 'Dúvida', departments: [] })
|
|
256
|
+
expectError(topics.create({ name: 'Dúvida' }))
|
|
257
|
+
expectError(topics.updateById('t-1', { departments: [] }))
|
|
258
|
+
expectType<Promise<'OK'>>(topics.deleteById('t-1'))
|
|
259
|
+
|
|
260
|
+
// ── quick replies: update fails without the files ───────────────────
|
|
261
|
+
|
|
262
|
+
const quickReplies = new QuickRepliesApi(client)
|
|
263
|
+
quickReplies.updateById('r-1', { title: 'Oi', text: 'Olá', files: [] })
|
|
264
|
+
expectError(quickReplies.updateById('r-1', { title: 'Oi', text: 'Olá' }))
|
|
265
|
+
expectType<Promise<'OK'>>(quickReplies.deleteById('r-1'))
|
|
@@ -47,6 +47,7 @@ export type PermissionName =
|
|
|
47
47
|
| 'users.create'
|
|
48
48
|
| 'users.update'
|
|
49
49
|
| 'users.archive'
|
|
50
|
+
| 'users.export'
|
|
50
51
|
| 'roles.view'
|
|
51
52
|
| 'roles.create'
|
|
52
53
|
| 'roles.update'
|
|
@@ -205,10 +206,15 @@ export type PermissionName =
|
|
|
205
206
|
| 'knowledgeBase.create'
|
|
206
207
|
| 'knowledgeBase.update'
|
|
207
208
|
| 'knowledgeBase.destroy'
|
|
208
|
-
|
|
209
|
-
| 'kanban.
|
|
210
|
-
| 'kanban.
|
|
211
|
-
| 'kanban.
|
|
209
|
+
// The migration 20260209144630 replaced kanban.view/create/update/destroy with the permissions below.
|
|
210
|
+
| 'kanban.boards.view'
|
|
211
|
+
| 'kanban.boards.create'
|
|
212
|
+
| 'kanban.boards.update'
|
|
213
|
+
| 'kanban.boards.destroy'
|
|
214
|
+
| 'kanban.columns.create'
|
|
215
|
+
| 'kanban.columns.update'
|
|
216
|
+
| 'kanban.columns.destroy'
|
|
217
|
+
| 'kanban.cards.move'
|
|
212
218
|
|
|
213
219
|
/**
|
|
214
220
|
* A permission, as `GET /permissions` answers it (the transformer only picks these fields).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import type { ApiClient } from '../../core/ApiClient'
|
|
3
|
+
import { QuestionsApi } from './QuestionsApi'
|
|
4
|
+
|
|
5
|
+
// ── Mock client factory ──────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
function createMockClient(): ApiClient {
|
|
8
|
+
return {
|
|
9
|
+
setAccessToken: vi.fn().mockReturnThis(),
|
|
10
|
+
request: vi.fn(),
|
|
11
|
+
get: vi.fn(),
|
|
12
|
+
post: vi.fn(),
|
|
13
|
+
put: vi.fn(),
|
|
14
|
+
patch: vi.fn(),
|
|
15
|
+
delete: vi.fn(),
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ── Tests ────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
describe('QuestionsApi', () => {
|
|
22
|
+
let client: ReturnType<typeof createMockClient>
|
|
23
|
+
let api: QuestionsApi
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
client = createMockClient()
|
|
27
|
+
api = new QuestionsApi(client)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('create sends duration as a string', async () => {
|
|
31
|
+
const body = {
|
|
32
|
+
name: 'NPS',
|
|
33
|
+
type: 'nps',
|
|
34
|
+
questionMessage: 'De 0 a 10?',
|
|
35
|
+
duration: '60',
|
|
36
|
+
tries: 3,
|
|
37
|
+
successMessage: 'Obrigado!',
|
|
38
|
+
invalidMessage: 'De 0 a 10',
|
|
39
|
+
} as const
|
|
40
|
+
await api.create(body)
|
|
41
|
+
expect(client.post).toHaveBeenCalledWith('/questions', body, undefined)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('deleteById resolves to the OK of the route', async () => {
|
|
45
|
+
vi.mocked(client.delete).mockResolvedValue('OK')
|
|
46
|
+
expect(await api.deleteById('q1')).toBe('OK')
|
|
47
|
+
expect(client.delete).toHaveBeenCalledWith('/questions/q1', undefined)
|
|
48
|
+
})
|
|
49
|
+
})
|
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
import type { ApiClient } from '../../core/ApiClient'
|
|
2
2
|
import { BaseCrudApi } from '../../core/BaseCrudApi'
|
|
3
|
+
import type { ListQuery } from '../../core/types'
|
|
3
4
|
import type { Question, CreateQuestionPayload, UpdateQuestionPayload } from './types'
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Evaluation questions (NPS/CSAT), sent to the contact by the `SEND_QUESTION` bot action. The
|
|
8
|
+
* backend also serves the same routes under the singular `/question` (kept for compatibility).
|
|
9
|
+
*
|
|
10
|
+
* Permissions: `evaluation.view` to read; `evaluation.view` OR `evaluation.create`/`update`/
|
|
11
|
+
* `destroy` to write.
|
|
12
|
+
*/
|
|
5
13
|
export class QuestionsApi extends BaseCrudApi<
|
|
6
14
|
Question,
|
|
7
15
|
CreateQuestionPayload,
|
|
8
|
-
UpdateQuestionPayload
|
|
16
|
+
UpdateQuestionPayload,
|
|
17
|
+
ListQuery<Question>,
|
|
18
|
+
'OK'
|
|
9
19
|
> {
|
|
10
20
|
constructor(client: ApiClient) {
|
|
11
21
|
super(client, '/questions')
|
|
12
22
|
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Deletes a question (soft delete). Resolves to `'OK'`. A question used in a bot answers 403;
|
|
26
|
+
* the answers of a deleted question leave the overview and the exports.
|
|
27
|
+
* @permissions evaluation.view OR evaluation.destroy
|
|
28
|
+
*/
|
|
29
|
+
override deleteById(id: string, headers?: Record<string, string>): Promise<'OK'> {
|
|
30
|
+
return super.deleteById(id, headers)
|
|
31
|
+
}
|
|
13
32
|
}
|