Files
botpress--botpress/integrations/slack/src/misc/utils.ts
T
2026-07-13 13:34:48 +08:00

165 lines
4.6 KiB
TypeScript

import { RuntimeError } from '@botpress/sdk'
import axios from 'axios'
import { SlackClient } from 'src/slack-api'
import * as bp from '.botpress'
const _extractBotpressFileId = (url: string): string | undefined => {
try {
const pathname = new URL(url).pathname
const last = pathname.split('/').filter(Boolean).pop()
return last ? decodeURIComponent(last) : undefined
} catch {
return undefined
}
}
export const downloadBotpressFile = async (
url: string,
client: bp.Client,
logger: bp.Logger
): Promise<{ buffer: Buffer; filename: string }> => {
let downloadUrl = url
let filename = _extractBotpressFileId(url) ?? 'file'
const fileId = _extractBotpressFileId(url)
if (fileId) {
try {
const { file } = await client.getFile({ id: fileId })
downloadUrl = file.url
filename = file.key.split('/').pop() ?? filename
} catch (error) {
logger
.forBot()
.debug('Could not resolve file through Botpress Files API, falling back to direct URL download', { error })
}
}
try {
const response = await axios.get<ArrayBuffer>(downloadUrl, { responseType: 'arraybuffer' })
return { buffer: Buffer.from(response.data), filename }
} catch (error) {
logger.forBot().error('Error while downloading file from URL:', error)
throw new RuntimeError(error instanceof Error ? error.message : String(error))
}
}
export const isValidUrl = (str: string) => {
try {
new URL(str)
return true
} catch {
return false
}
}
export const getBotpressUserFromSlackUser = async (props: { slackUserId: string }, client: bp.Client) => {
const { user } = await client.getOrCreateUser({
tags: { id: props.slackUserId },
})
return {
botpressUser: user,
botpressUserId: user.id,
}
}
export const updateBotpressUserFromSlackUser = async (
slackUserId: string,
botpressUser: Awaited<ReturnType<bp.Client['getOrCreateUser']>>['user'],
client: bp.Client,
ctx: bp.Context,
logger: bp.Logger
) => {
if (botpressUser.pictureUrl && botpressUser.name) {
return
}
try {
const slackClient = await SlackClient.createFromStates({ ctx, client, logger })
const userProfile = await slackClient.getUserProfile({ userId: slackUserId })
const fieldsToUpdate = {
pictureUrl: userProfile?.image_192,
name: userProfile?.real_name,
}
logger.forBot().debug('Fetched latest Slack user profile: ', fieldsToUpdate)
if (fieldsToUpdate.pictureUrl || fieldsToUpdate.name) {
await client.updateUser({ ...botpressUser, ...fieldsToUpdate })
}
} catch (error) {
logger.forBot().error('Error while fetching user profile from Slack:', error)
}
}
export const getBotpressConversationFromSlackThread = async (
props: { slackChannelId: string; slackThreadId?: string },
client: bp.Client
) => {
let conversation: bp.ClientResponses['getConversation']['conversation']
if (props.slackThreadId) {
const resp = await client.getOrCreateConversation({
channel: 'thread',
tags: { id: props.slackChannelId, thread: props.slackThreadId },
})
conversation = resp.conversation
} else {
const channel = props.slackChannelId.startsWith('D') ? 'dm' : 'channel'
const resp = await client.getOrCreateConversation({
channel,
tags: { id: props.slackChannelId },
})
conversation = resp.conversation
}
return {
botpressConversation: conversation,
botpressConversationId: conversation.id,
}
}
export const getMessageFromSlackEvent = async (
client: bp.Client,
event: { item: { type: string; channel?: string; ts?: string } }
) => {
if (event.item.type !== 'message' || !event.item.channel || !event.item.ts) {
return undefined
}
const { messages } = await client.listMessages({
tags: { ts: event.item.ts, channelId: event.item.channel },
})
return messages[0]
}
export const safeParseBody = (body: string) => {
const parseResult = _safeParseJson(body)
return parseResult.success ? parseResult : _safeDecodeURIComponent(body)
}
const _safeDecodeURIComponent = (component: string) => {
try {
const decoded = decodeURIComponent(component)
return _safeParseJson(decoded.startsWith('payload=') ? decoded.slice('payload='.length) : decoded)
} catch (thrown: unknown) {
return {
success: false,
error: thrown instanceof Error ? thrown : new Error(String(thrown)),
}
}
}
const _safeParseJson = (json: string) => {
try {
return {
success: true,
data: JSON.parse(json),
}
} catch (thrown: unknown) {
return {
success: false,
error: thrown instanceof Error ? thrown : new Error(String(thrown)),
}
}
}