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
+35
View File
@@ -0,0 +1,35 @@
import * as sdk from '@botpress/sdk'
import * as genenv from './.genenv'
import chat from './bp_modules/chat'
import linear from './bp_modules/linear'
import synchronizer from './bp_modules/synchronizer'
export default new sdk.BotDefinition({})
.addIntegration(linear, {
enabled: true,
configurationType: 'apiKey',
configuration: {
apiKey: genenv.SINLIN_LINEAR_API_KEY,
webhookSigningSecret: genenv.SINLIN_LINEAR_WEBHOOK_SIGNING_SECRET,
},
})
.addIntegration(chat, {
enabled: true,
configuration: {},
})
.addPlugin(synchronizer, {
alias: 'linear',
configuration: {
tableName: 'linearIssuesTable',
},
dependencies: {
listable: {
integrationAlias: 'linear',
integrationInterfaceAlias: 'listable<issue>',
},
deletable: {
integrationAlias: 'linear',
integrationInterfaceAlias: 'deletable<issue>',
},
},
})
+13
View File
@@ -0,0 +1,13 @@
import rootConfig from '../../eslint.config.mjs'
export default [
...rootConfig,
{
languageOptions: {
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: import.meta.dirname,
},
},
},
]
+30
View File
@@ -0,0 +1,30 @@
{
"name": "@bp-bots/sinlin",
"description": "A bot that syncs issues from Linear to Botpress tables",
"scripts": {
"postinstall": "genenv -o ./.genenv/index.ts -e SINLIN_LINEAR_API_KEY -e SINLIN_LINEAR_WEBHOOK_SIGNING_SECRET",
"check:type": "tsc --noEmit",
"check:bplint": "bp lint",
"build": "bp add -y && bp build"
},
"private": true,
"dependencies": {
"@botpress/client": "workspace:*",
"@botpress/sdk": "workspace:*"
},
"devDependencies": {
"@botpress/cli": "workspace:*",
"@botpress/common": "workspace:*",
"@botpress/sdk": "workspace:*",
"@botpresshub/chat": "workspace:*",
"@botpresshub/linear": "workspace:*",
"@botpresshub/synchronizer": "workspace:*",
"@bpinternal/genenv": "0.0.1",
"@types/lodash": "^4.14.191"
},
"bpDependencies": {
"chat": "../../integrations/chat",
"linear": "../../integrations/linear",
"synchronizer": "../../plugins/synchronizer"
}
}
+100
View File
@@ -0,0 +1,100 @@
import { Client } from '@botpress/client'
import * as bp from '.botpress'
const { tableName } = bp.linear.configuration
const getVanillaClient = (props: bp.EventHandlerProps | bp.MessageHandlerProps): Client => props.client._inner
const summarize = (str: string, maxLength: number = 1000): string =>
str.length > maxLength ? str.substring(0, maxLength) + '...' : str
const reply = async (props: bp.MessageHandlerProps, text: string) => {
await props.client.createMessage({
type: 'text',
payload: {
text,
},
conversationId: props.message.conversationId,
userId: props.ctx.botId,
tags: {},
})
}
const bot = new bp.Bot({ actions: {} })
type Command = {
description: string
handler: bp.MessageHandlers['*']
}
const commands: Record<string, Command> = {
'/sync': {
description: 'Sync issues',
handler: async (props: bp.MessageHandlerProps) => {
await bot.actionHandlers['linear#clear']({ ...props, input: {} })
await reply(props, 'Issues synced')
},
},
'/list': {
description: 'List issues',
handler: async (props: bp.MessageHandlerProps) => {
const tableState = await props.client
.getOrSetState({ type: 'bot', id: props.ctx.botId, name: 'linear#table', payload: { tableCreated: false } })
.then((r) => r.state.payload)
if (!tableState.tableCreated) {
await reply(props, 'Table does not exist')
return
}
const { rows } = await getVanillaClient(props).findTableRows({
table: tableName,
filter: {},
limit: 10,
})
const issues: string[] = rows
.map(({ computed: _computed, stale: _stale, similarity: _similarity, ...r }) =>
Object.entries(r)
.map(([k, v]) => `${k}: ${v}`)
.join('; ')
)
.map((r) => `- ${r}`)
const response = issues.join('\n\n') || 'No issues found'
await reply(props, summarize(response))
},
},
'/clear': {
description: 'Clear issues',
handler: async (props: bp.MessageHandlerProps) => {
await bot.actionHandlers['linear#clear']({ ...props, input: {} })
await reply(props, 'Table cleared')
},
},
}
bot.on.message('*', async (props) => {
const { message } = props
if (message.type !== 'text') {
await reply(props, 'I only understand text messages')
return
}
const query = message.payload.text.trim()
const command = commands[query]
if (command) {
const now = Date.now()
props.logger.info(`[${now}:START] command "${query}"`)
await command.handler(props)
props.logger.info(`[${now}:STOP] command "${query}"`)
return
}
const helpMessage = Object.entries(commands)
.map(([cmd, { description }]) => `${cmd}: ${description}`)
.join('\n')
await reply(props, 'Please use one of the following commands:\n' + helpMessage)
})
export default bot
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"paths": { "*": ["./*"] },
"outDir": "dist"
},
"include": [".botpress/**/*", "src/**/*", "*.ts"]
}