chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import * as sdk from '@botpress/sdk'
|
||||
import { AxiosRequestConfig } from 'axios'
|
||||
import { getZendeskClient } from '../client'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export const callApi: bp.IntegrationProps['actions']['callApi'] = async (
|
||||
props
|
||||
): Promise<bp.actions.callApi.output.Output> => {
|
||||
const { client, ctx, input, logger } = props
|
||||
const { method, path, headers, params, requestBody } = input
|
||||
const zendeskClient = await getZendeskClient(client, ctx, logger)
|
||||
|
||||
try {
|
||||
const requestConfig: AxiosRequestConfig = {
|
||||
method,
|
||||
url: `/api/v2/${path}`,
|
||||
headers: headers ? JSON.parse(headers) : {},
|
||||
params: params ? JSON.parse(params) : {},
|
||||
validateStatus: () => true,
|
||||
}
|
||||
|
||||
if (method !== 'GET') {
|
||||
requestConfig.data = requestBody ? JSON.parse(requestBody) : {}
|
||||
}
|
||||
|
||||
return await zendeskClient.makeRequest(requestConfig)
|
||||
} catch (error) {
|
||||
throw new sdk.RuntimeError(`Error: ${error instanceof Error ? error.message : 'An unknown error occurred'}`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { transformTicket } from 'src/definitions/schemas'
|
||||
import { getZendeskClient } from '../client'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export const closeTicket: bp.IntegrationProps['actions']['closeTicket'] = async (props) => {
|
||||
const { client: bpClient, ctx, input, logger } = props
|
||||
const zendeskClient = await getZendeskClient(bpClient, ctx, logger)
|
||||
const originalTicket = await zendeskClient.getTicket(input.ticketId)
|
||||
|
||||
const { ticket } = await zendeskClient.updateTicket(input.ticketId, {
|
||||
comment: {
|
||||
body: input.comment,
|
||||
author_id: originalTicket.requester_id,
|
||||
},
|
||||
status: 'closed',
|
||||
})
|
||||
|
||||
return {
|
||||
ticket: transformTicket(ticket),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { transformTicket } from 'src/definitions/schemas'
|
||||
import { getZendeskClient } from '../client'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export const createTicket: bp.IntegrationProps['actions']['createTicket'] = async (props) => {
|
||||
const { client: bpClient, ctx, input, logger } = props
|
||||
const zendeskClient = await getZendeskClient(bpClient, ctx, logger)
|
||||
const ticket = await zendeskClient.createTicket(
|
||||
input.subject,
|
||||
input.comment,
|
||||
{
|
||||
name: input.requesterName,
|
||||
email: input.requesterEmail,
|
||||
},
|
||||
input.ticketFormId ? { ticket_form_id: parseInt(input.ticketFormId, 10) } : {}
|
||||
)
|
||||
|
||||
return {
|
||||
ticket: transformTicket(ticket),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { getZendeskClient } from '../client'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export const createUser: bp.IntegrationProps['actions']['createUser'] = async (props) => {
|
||||
const { client: bpClient, ctx, input, logger } = props
|
||||
const zendeskClient = await getZendeskClient(bpClient, ctx, logger)
|
||||
|
||||
const { name, email, pictureUrl } = input
|
||||
|
||||
const { user } = await bpClient.getOrCreateUser({
|
||||
name,
|
||||
pictureUrl,
|
||||
tags: {
|
||||
email,
|
||||
role: 'end-user',
|
||||
},
|
||||
discriminateByTags: ['email'],
|
||||
})
|
||||
|
||||
let remote_photo_url = pictureUrl
|
||||
|
||||
const urlLength = _getEncodedLength(pictureUrl)
|
||||
const maxUrlLength = 255
|
||||
if (urlLength && urlLength > maxUrlLength) {
|
||||
// Zendesk has a limit of 255 characters for the remote_photo_url
|
||||
logger
|
||||
.forBot()
|
||||
.warn(
|
||||
`Picture URL is too long (${urlLength} characters, but max is ${maxUrlLength}). It will not be set in Zendesk.`
|
||||
)
|
||||
remote_photo_url = ''
|
||||
}
|
||||
|
||||
const zendeskUser = await zendeskClient.createOrUpdateUser({
|
||||
role: 'end-user',
|
||||
external_id: user.id,
|
||||
name,
|
||||
remote_photo_url,
|
||||
email,
|
||||
})
|
||||
|
||||
await bpClient.updateUser({
|
||||
id: user.id,
|
||||
name,
|
||||
pictureUrl,
|
||||
tags: {
|
||||
id: `${zendeskUser.id}`,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
userId: user.id,
|
||||
}
|
||||
}
|
||||
|
||||
const _getEncodedLength = (url: string | undefined): number | undefined => (url ? encodeURI(url).length : undefined)
|
||||
@@ -0,0 +1,10 @@
|
||||
import { transformUser } from 'src/definitions/schemas'
|
||||
import { getZendeskClient } from '../client'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export const findCustomer: bp.IntegrationProps['actions']['findCustomer'] = async (props) => {
|
||||
const { client: bpClient, ctx, input, logger } = props
|
||||
const zendeskClient = await getZendeskClient(bpClient, ctx, logger)
|
||||
const customers = await zendeskClient.findCustomers(input.query)
|
||||
return { customers: customers.map(transformUser) }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { transformTicket } from 'src/definitions/schemas'
|
||||
import { getZendeskClient } from '../client'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export const getTicket: bp.IntegrationProps['actions']['getTicket'] = async (props) => {
|
||||
const { client: bpClient, ctx, input, logger } = props
|
||||
const zendeskClient = await getZendeskClient(bpClient, ctx, logger)
|
||||
const ticket = await zendeskClient.getTicket(input.ticketId)
|
||||
return { ticket: transformTicket(ticket) }
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import { buildConversationTranscript } from '@botpress/common'
|
||||
import * as sdk from '@botpress/sdk'
|
||||
import { getZendeskClient, type ZendeskClient } from '../client'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export const startHitl: bp.IntegrationProps['actions']['startHitl'] = async (props) => {
|
||||
const { ctx, input, client: bpClient, logger } = props
|
||||
|
||||
const downstreamBotpressUser = await bpClient.getUser({ id: ctx.botUserId })
|
||||
const chatbotName = input.hitlSession?.chatbotName ?? downstreamBotpressUser.user.name ?? 'Botpress'
|
||||
const chatbotPhotoUrl =
|
||||
input.hitlSession?.chatbotPhotoUrl ??
|
||||
downstreamBotpressUser.user.pictureUrl ??
|
||||
'https://app.botpress.dev/favicon/bp.svg'
|
||||
|
||||
const { user } = await bpClient.getUser({
|
||||
id: input.userId,
|
||||
})
|
||||
|
||||
const zendeskAuthorId = user.tags.id
|
||||
|
||||
if (!zendeskAuthorId) {
|
||||
throw new sdk.RuntimeError(`User ${user.id} not linked in Zendesk`)
|
||||
}
|
||||
|
||||
const zendeskClient = await getZendeskClient(bpClient, ctx, logger)
|
||||
await _updateZendeskBotpressUser(props, {
|
||||
zendeskClient,
|
||||
chatbotName,
|
||||
chatbotPhotoUrl,
|
||||
})
|
||||
|
||||
const requester = input.hitlSession?.requesterName
|
||||
? {
|
||||
name: input.hitlSession?.requesterName,
|
||||
...(input.hitlSession?.requesterEmail ? { email: input.hitlSession?.requesterEmail } : {}),
|
||||
}
|
||||
: { id: zendeskAuthorId }
|
||||
|
||||
const ticket = await zendeskClient.createTicket(
|
||||
input.title ?? 'Untitled Ticket',
|
||||
await _buildTicketBody(props, { chatbotName }),
|
||||
requester,
|
||||
{
|
||||
priority: input.hitlSession?.priority,
|
||||
...(input.hitlSession?.ticketFormId ? { ticket_form_id: parseInt(input.hitlSession.ticketFormId, 10) } : {}),
|
||||
}
|
||||
)
|
||||
|
||||
const zendeskTicketId = `${ticket.id}`
|
||||
const { conversation } = await bpClient.getOrCreateConversation({
|
||||
channel: 'hitl',
|
||||
tags: {
|
||||
id: zendeskTicketId,
|
||||
},
|
||||
})
|
||||
|
||||
await zendeskClient.updateTicket(zendeskTicketId, {
|
||||
external_id: conversation.id,
|
||||
})
|
||||
|
||||
return {
|
||||
conversationId: conversation.id,
|
||||
}
|
||||
}
|
||||
|
||||
const _updateZendeskBotpressUser = async (
|
||||
{ client, ctx }: bp.ActionProps['startHitl'],
|
||||
{
|
||||
zendeskClient,
|
||||
chatbotName,
|
||||
chatbotPhotoUrl,
|
||||
}: { zendeskClient: ZendeskClient; chatbotName: string; chatbotPhotoUrl: string }
|
||||
) => {
|
||||
await client.updateUser({
|
||||
id: ctx.botUserId,
|
||||
pictureUrl: chatbotPhotoUrl,
|
||||
name: chatbotName,
|
||||
})
|
||||
|
||||
await zendeskClient.createOrUpdateUser({
|
||||
external_id: ctx.botUserId,
|
||||
name: chatbotName,
|
||||
remote_photo_url: chatbotPhotoUrl,
|
||||
})
|
||||
}
|
||||
|
||||
const _buildTicketBody = async (
|
||||
{ input, client, ctx }: bp.ActionProps['startHitl'],
|
||||
{ chatbotName }: { chatbotName: string }
|
||||
) => {
|
||||
const description = input.description?.trim() || `Someone opened a ticket using your ${chatbotName} chatbot.`
|
||||
const messageHistory = await buildConversationTranscript({ client, ctx, messages: input.messageHistory })
|
||||
|
||||
return description + (messageHistory.length ? `\n\n---\n\n${messageHistory}` : '')
|
||||
}
|
||||
|
||||
export const stopHitl: bp.IntegrationProps['actions']['stopHitl'] = async (props) => {
|
||||
const { client: bpClient, ctx, input, logger } = props
|
||||
const { conversation } = await bpClient.getConversation({
|
||||
id: input.conversationId,
|
||||
})
|
||||
|
||||
const ticketId: string | undefined = conversation.tags.id
|
||||
if (!ticketId) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const zendeskClient = await getZendeskClient(bpClient, ctx, logger)
|
||||
|
||||
try {
|
||||
await zendeskClient.updateTicket(ticketId, {
|
||||
status: 'closed',
|
||||
})
|
||||
return {}
|
||||
} catch (err) {
|
||||
console.error('Could not close ticket', err)
|
||||
throw new sdk.RuntimeError(`Failed to close ticket: ${err}`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { callApi } from './call-api'
|
||||
import { closeTicket } from './close-ticket'
|
||||
import { createTicket } from './create-ticket'
|
||||
import { createUser } from './create-user'
|
||||
import { findCustomer } from './find-customer'
|
||||
import { getTicket } from './get-ticket'
|
||||
import { startHitl, stopHitl } from './hitl'
|
||||
import { listAgents } from './list-agents'
|
||||
import { syncKb } from './sync-kb'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export default {
|
||||
getTicket,
|
||||
findCustomer,
|
||||
createTicket,
|
||||
createUser,
|
||||
closeTicket,
|
||||
listAgents,
|
||||
callApi,
|
||||
startHitl,
|
||||
stopHitl,
|
||||
syncKb,
|
||||
} satisfies bp.IntegrationProps['actions']
|
||||
@@ -0,0 +1,13 @@
|
||||
import { transformUser } from 'src/definitions/schemas'
|
||||
import { getZendeskClient } from '../client'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export const listAgents: bp.IntegrationProps['actions']['listAgents'] = async (props) => {
|
||||
const { client: bpClient, ctx, input, logger } = props
|
||||
const zendeskClient = await getZendeskClient(bpClient, ctx, logger)
|
||||
const agents = await zendeskClient.getAgents(input.isOnline)
|
||||
|
||||
return {
|
||||
agents: agents.map(transformUser),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as sdk from '@botpress/sdk'
|
||||
import { uploadArticlesToKb } from 'src/misc/upload-articles-to-kb'
|
||||
import { deleteKbArticles } from 'src/misc/utils'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export const syncKb: bp.IntegrationProps['actions']['syncKb'] = async (props) => {
|
||||
try {
|
||||
const { client, ctx, input, logger } = props
|
||||
const kbId = input.knowledgeBaseId
|
||||
|
||||
await deleteKbArticles(kbId, client)
|
||||
|
||||
await uploadArticlesToKb({ ctx, client, logger, kbId })
|
||||
|
||||
return {
|
||||
success: true,
|
||||
}
|
||||
} catch (error) {
|
||||
throw new sdk.RuntimeError(`Error: ${error instanceof Error ? error.message : 'An unknown error occurred'}`)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user