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
+186
View File
@@ -0,0 +1,186 @@
import { Conversation } from '@botpress/client'
import { AnyIncomingMessage } from '@botpress/sdk/dist/bot'
import * as genenv from '../.genenv'
import * as bp from '.botpress'
const DEFAULT_SLACK_CHANNEL = genenv.SLACKBOX_SLACK_CHANNEL
const FALLBACK_SLACK_CHANNEL = genenv.SLACKBOX_FALLBACK_SLACK_CHANNEL || DEFAULT_SLACK_CHANNEL
const cachedSlackConversationIds: Record<string, string> = {}
const bot = new bp.Bot({
actions: {},
})
bot.on.message('*', async (props) => {
const { conversation, message, client, ctx, logger } = props
if (!conversation.integration.includes('gmail')) {
logger.info('[Slackbox] Not a Gmail message, skipping')
return
}
try {
const shouldForward = await _shouldForwardEmail(client, conversation, logger)
if (!shouldForward) {
logger.info('Email filtered out - no matching Integrations label')
return
}
const subject = (conversation.tags['gmail:subject'] || conversation.tags.subject) as string | undefined
const targetChannel = _getTargetChannel(subject)
const slackConversationId = await _getSlackConversationId(client, logger, targetChannel)
const notificationMessage = _mapGmailToSlack(conversation, message)
await client.createMessage({
type: 'text',
conversationId: slackConversationId,
tags: {},
userId: ctx.botId,
payload: {
text: notificationMessage,
},
})
} catch (error) {
logger.error(`Failed to send email notification: ${error}`)
}
})
const _shouldForwardEmail = async (
client: bp.Client,
conversation: Conversation,
logger: bp.MessageHandlerProps['logger']
): Promise<boolean> => {
const threadId = conversation.tags['gmail:id']
if (!threadId) {
logger.info('[LabelCheck] No threadId, forwarding email')
return true
}
try {
const labelsResponse = await client.callAction({
type: 'gmail:listLabels',
input: {},
})
const labels = labelsResponse.output.labels || []
const threadResponse = await client.callAction({
type: 'gmail:getThread',
input: { id: threadId },
})
const messages = threadResponse.output.messages || []
const allLabelIds = new Set<string>()
messages.forEach((msg) => {
msg.labelIds?.forEach((labelId) => allLabelIds.add(labelId))
})
if (allLabelIds.size === 0) {
logger.info('[LabelCheck] No labels on thread, forwarding email')
return true
}
const labelsById = new Map<string, { type?: string; name?: string }>()
labels.forEach((label) => {
if (label.id) {
labelsById.set(label.id, { type: label.type || undefined, name: label.name || undefined })
}
})
const userLabels = [...allLabelIds].filter((labelId) => {
const label = labelsById.get(labelId)
return label?.type === 'user'
})
if (userLabels.length === 0) {
logger.info('[LabelCheck] No user labels, forwarding email')
return true
}
const hasIntegrationLabel = userLabels.some((labelId) => {
const label = labelsById.get(labelId)
return label?.name === 'Integrations' || label?.name?.startsWith('Integrations/')
})
return hasIntegrationLabel
} catch (error) {
logger.error(`[LabelCheck] Error checking email labels: ${error}`)
return true
}
}
const _resolveSlackChannelId = async (client: bp.Client, channelName: string): Promise<string | undefined> => {
const { output } = await client.callAction({
type: 'slack:findTarget',
input: { query: channelName, channel: 'channel' },
})
const exact = output.targets.find((t) => t.displayName === channelName)
return (exact ?? output.targets[0])?.tags.id
}
const _getSlackConversationId = async (
client: bp.Client,
logger: bp.MessageHandlerProps['logger'],
channelName: string
): Promise<string> => {
if (cachedSlackConversationIds[channelName]) {
return cachedSlackConversationIds[channelName]
}
logger.info(`Fetching Slack conversation ID for channel: ${channelName}`)
const maxRetries = 3
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const channelId = await _resolveSlackChannelId(client, channelName)
if (!channelId) {
throw new Error(`Could not find Slack channel '${channelName}'`)
}
const response = await client.callAction({
type: 'slack:getOrCreateChannelConversation',
input: {
conversation: {
channelId,
},
},
})
cachedSlackConversationIds[channelName] = response.output.conversationId
return cachedSlackConversationIds[channelName] as string
} catch (err) {
logger.warn(`Attempt ${attempt}/${maxRetries} failed: ${err}`)
if (attempt === maxRetries) {
throw err
}
await new Promise((resolve) => setTimeout(resolve, 2000))
}
}
throw new Error('Failed to get Slack conversation ID after retries')
}
const _getTargetChannel = (subject: string | undefined): string => {
if (subject?.toLowerCase().includes('test')) {
return FALLBACK_SLACK_CHANNEL
}
return DEFAULT_SLACK_CHANNEL
}
const _mapGmailToSlack = (conversation: Conversation, message: AnyIncomingMessage<bp.TBot>) => {
const subject = (conversation.tags['gmail:subject'] || conversation.tags.subject) as string | undefined
const fromEmail = (conversation.tags['gmail:email'] || conversation.tags.email) as string | undefined
const messageText =
message.type === 'text' ? (message.payload as { text?: string }).text || 'No content' : 'New email received'
const preview = messageText.length > 200 ? messageText.substring(0, 200) + '...' : messageText
const notificationMessage =
'📦 *New email received!*\n\n' +
`*Subject:* ${subject || '(No subject)'}\n` +
`*From:* ${fromEmail || 'Unknown'}\n` +
`*Body:*\n${preview || 'Preview unavailable'}`
return notificationMessage
}
export default bot