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,76 @@
import axios from 'axios'
import http from 'http'
import * as bp from '.botpress'
type AxiosSummaryErrorProps = {
request: {
method: string
url: string
} | null
response: {
status: number
statusText: string
data: unknown
} | null
}
class AxiosSummaryError extends Error {
public constructor(props: AxiosSummaryErrorProps) {
const { request, response } = props
const messageLines = ['Zendesk API error']
if (request) {
messageLines.push(`Request: ${request.method} ${request.url}`)
}
if (response) {
messageLines.push(`Response: ${response.status} (${response.statusText}) ${JSON.stringify(response.data)}`)
}
const message = messageLines.map((l) => `\n ${l}`).join('')
super(message)
}
}
/**
* Axios Requests are too verbose and can pollute logs.
* This function summarizes the error for better readability.
*/
export const summarizeAxiosError = (thrown: unknown, logger: bp.Logger): never => {
if (!axios.isAxiosError(thrown)) {
throw thrown
}
const { request, response } = thrown
let parsedRequest: AxiosSummaryErrorProps['request'] = null
try {
if (request) {
// for some reason request is not properly typed in axios error
const req = request as http.ClientRequest
const method = req.method
const url = `${req.protocol}//${req.host}${req.path}`
parsedRequest = {
method,
url,
}
}
} catch {}
let parsedResponse: AxiosSummaryErrorProps['response'] = null
if (response) {
const status = response.status
const statusText = response.statusText
const data = response.data
parsedResponse = {
status,
statusText,
data,
}
}
logger.forBotOnly().error('Zendesk API request failed', { request: parsedRequest, response: parsedResponse })
throw new AxiosSummaryError({
request: parsedRequest,
response: parsedResponse,
})
}
@@ -0,0 +1,61 @@
import { getZendeskClient } from 'src/client'
import { ZendeskArticle } from 'src/definitions/schemas'
import { getUploadArticlePayload } from 'src/misc/utils'
import { Client, Context, Logger } from '.botpress'
export const uploadArticlesToKb = async (props: { ctx: Context; client: Client; logger: Logger; kbId: string }) => {
const { ctx, client: bpClient, logger, kbId } = props
logger.forBot().info('Attempting to sync Zendesk KB to BP KB')
const fetchedArticles: ZendeskArticle[] = []
try {
const fetchArticles = async (url: string) => {
const zendeskClient = await getZendeskClient(bpClient, ctx, logger)
const response: { data: { articles: ZendeskArticle[]; next_page?: string } } = await zendeskClient.makeRequest({
method: 'get',
url,
})
const { articles, next_page } = response.data
fetchedArticles.push(...articles)
if (next_page) {
await fetchArticles(next_page)
}
}
await fetchArticles('/api/v2/help_center/articles')
} catch (error) {
logger
.forBot()
.error(`Error fetching Zendesk articles: ${error instanceof Error ? error.message : 'An unknown error occurred'}`)
return
}
try {
for (const article of fetchedArticles) {
if (article.draft || !article.body) {
logger.forBot().info(`Article "${article.title}" is either unpublished or empty. Skipping...`)
continue
}
const payload = getUploadArticlePayload({ kbId, article })
await bpClient.uploadFile(payload)
logger.forBot().info(`Successfully uploaded article ${article.title} to BP KB`)
}
} catch (error) {
logger
.forBot()
.error(
`Error uploading article to BP KB: ${error instanceof Error ? error.message : 'An unknown error occurred'}`
)
logger.forBot().error(JSON.stringify(error))
return
}
logger.forBot().info('Successfully synced Zendesk KB to BP KB')
}
+40
View File
@@ -0,0 +1,40 @@
import { ZendeskArticle } from 'src/definitions/schemas'
import * as bp from '.botpress'
export const stringifyProperties = (obj: Record<string, any>) => {
const result: Record<string, string> = {}
for (const [key, value] of Object.entries(obj)) {
result[key] = JSON.stringify(value)
}
return result
}
export const getUploadArticlePayload = ({ kbId, article }: { kbId: string; article: ZendeskArticle }) => {
const { body, id, title, locale, label_names } = article
return {
key: `${kbId}/${id}.html`,
accessPolicies: [],
content: body,
index: true,
tags: {
source: 'knowledge-base',
kbId,
title,
locale,
labels: label_names.join(' '),
zendeskId: `${id}`,
},
}
}
export const deleteKbArticles = async (kbId: string, client: bp.Client): Promise<void> => {
const { files } = await client.listFiles({
tags: {
kbId,
},
})
for (const file of files) {
await client.deleteFile({ id: file.id })
}
}