chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:34:48 +08:00
commit 77bb5bf71f
3762 changed files with 353249 additions and 0 deletions
@@ -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) }
}
+120
View File
@@ -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}`)
}
}
+23
View File
@@ -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'}`)
}
}