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
+1
View File
@@ -0,0 +1 @@
.botpress
+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,
},
},
},
]
+1
View File
@@ -0,0 +1 @@
The Vonage integration enables seamless communication between your AI-powered chatbot and Vonage, a leading cloud communications platform. Connect your chatbot to Vonage and leverage a variety of communication channels, including SMS, voice calls, and messaging apps. With this integration, you can automate interactions, send notifications, handle inquiries, and provide customer support directly through Vonage. Utilize Vonage's robust features such as SMS delivery, voice call routing, messaging app integration, and more to create reliable and effective conversational experiences. Enhance your communication capabilities and engage with users effortlessly with the Vonage Integration for Botpress.
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 229.59 200">
<path fill="#000" d="M45.3408,0 L-0.0002,0 L64.6808,146.958 C65.1748,148.081 66.7718,148.07 67.2508,146.942 L88.7628,96.337 L45.3408,0 Z"/>
<path fill="#000" d="M183.4502,0 C183.4502,0 113.9562,159.156 104.6482,173.833 C93.8292,190.896 86.6592,197.409 73.3912,199.496 C73.2682,199.515 73.1772,199.621 73.1772,199.746 C73.1772,199.886 73.2912,200 73.4312,200 L114.9552,200 C132.9432,200 145.9152,184.979 153.1042,171.714 C161.2742,156.637 229.5902,0 229.5902,0 L183.4502,0 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 567 B

@@ -0,0 +1,89 @@
import { z, IntegrationDefinition, messages } from '@botpress/sdk'
import proactiveConversation from 'bp_modules/proactive-conversation'
import proactiveUser from 'bp_modules/proactive-user'
export default new IntegrationDefinition({
name: 'vonage',
version: '1.0.5',
title: 'Vonage',
description: 'Send and receive SMS messages.',
icon: 'icon.svg',
readme: 'hub.md',
configuration: {
schema: z.object({
apiKey: z.string().min(1).describe('The API Key').title('API Key'),
apiSecret: z.string().min(1).describe('The API Secret').title('API Secret'),
signatureSecret: z.string().min(1).describe('The signature secret').title('Signature Secret'),
useTestingApi: z.boolean().describe('Chooses if we should use the test api').title('Use Testing API'),
}),
},
channels: {
channel: {
title: 'Channel',
description: 'The vonage Channel',
messages: { ...messages.defaults, bloc: messages.markdownBloc },
message: {
tags: {
id: { title: 'ID', description: 'The id of the message' },
},
},
conversation: {
tags: {
userId: { title: 'User ID', description: 'The User id' },
channel: { title: 'Channel', description: 'The conversation channel' },
channelId: { title: 'Channel ID', description: 'The channel id' },
},
},
},
},
events: {},
user: {
tags: {
userId: { title: 'User ID', description: 'The user id' },
channel: { title: 'Channel', description: 'The channel of the user' },
},
},
entities: {
conversation: {
schema: z.object({
userId: z.string(),
channelId: z.string(),
channel: z.string(),
}),
},
user: {
schema: z.object({
channel: z.string().describe('The channel of the user').title('Channel'),
userId: z.string().describe('The user id').title('User ID'),
}),
},
},
attributes: {
category: 'Communication & Channels',
guideSlug: 'vonage',
repo: 'botpress',
},
})
.extend(proactiveConversation, ({ entities }) => ({
entities: {
conversation: entities.conversation,
},
actions: {
getOrCreateConversation: {
name: 'startConversation',
title: 'Start proactive conversation',
description: 'Start a proactive conversation given a user',
},
},
}))
.extend(proactiveUser, ({ entities }) => ({
entities: { user: entities.user },
actions: {
getOrCreateUser: {
name: 'getOrCreateUser',
title: 'Get or create user',
description: 'Get or create a user in the Vonage channel',
},
},
}))
+26
View File
@@ -0,0 +1,26 @@
{
"name": "@botpresshub/vonage",
"description": "Vonage integration for Botpress",
"private": true,
"scripts": {
"build": "bp add -y && bp build",
"check:type": "tsc --noEmit",
"check:bplint": "bp lint",
"test": "vitest --run"
},
"dependencies": {
"@botpress/client": "workspace:*",
"@botpress/sdk": "workspace:*",
"@botpress/sdk-addons": "workspace:*",
"axios": "^0.27.2"
},
"devDependencies": {
"@botpress/cli": "workspace:*",
"@botpress/common": "workspace:*",
"@botpress/sdk": "workspace:*"
},
"bpDependencies": {
"proactive-user": "../../interfaces/proactive-user",
"proactive-conversation": "../../interfaces/proactive-conversation"
}
}
+144
View File
@@ -0,0 +1,144 @@
import { RuntimeError } from '@botpress/client'
import * as sdk from '@botpress/sdk'
import * as common from '@botpress/sdk-addons'
import * as formatter from './payloadFormatter'
import * as vonage from './vonage'
import * as bp from '.botpress'
const integration = new bp.Integration({
register: async () => {},
unregister: async () => {},
actions: {
async startConversation(props) {
const vonageChannel = props.input.conversation.channel
const channelId = props.input.conversation.channelId
const userId = props.input.conversation.userId
if (!(vonageChannel && channelId && userId)) {
throw new sdk.RuntimeError('Could not create conversation: missing channel, channelId or userId')
}
const { conversation } = await props.client.getOrCreateConversation({
tags: {
channel: vonageChannel,
channelId,
userId,
},
channel: 'channel',
})
return {
conversationId: conversation.id,
}
},
async getOrCreateUser(props) {
const vonageChannel = props.input.user.channel
const userId = props.input.user.userId
if (!(vonageChannel && userId)) {
throw new sdk.RuntimeError('Could not create a user: missing channel or userId')
}
const { user } = await props.client.getOrCreateUser({
tags: {
channel: vonageChannel,
userId,
},
})
return {
userId: user.id,
}
},
},
channels: {
channel: {
messages: {
text: async (props) => {
const payload = { message_type: 'text', text: props.payload.text }
await vonage.sendMessage(props, payload)
},
image: async (props) => {
const payload = { message_type: 'image', image: { url: props.payload.imageUrl } }
await vonage.sendMessage(props, payload)
},
audio: async (props) => {
const payload = { message_type: 'audio', audio: { url: props.payload.audioUrl } }
await vonage.sendMessage(props, payload)
},
video: async (props) => {
const payload = { message_type: 'video', video: { url: props.payload.videoUrl } }
await vonage.sendMessage(props, payload)
},
file: async (props) => {
const payload = { message_type: 'file', file: { url: props.payload.fileUrl } }
await vonage.sendMessage(props, payload)
},
location: async (props) => {
const payload = formatter.formatLocationPayload(props.payload)
await vonage.sendMessage(props, payload)
},
carousel: async (props) => {
const payloads = formatter.formatCarouselPayload(props.payload)
for (const payload of payloads) {
await vonage.sendMessage(props, payload)
}
},
card: async (props) => {
const payload = formatter.formatCardPayload(props.payload)
await vonage.sendMessage(props, payload)
},
dropdown: async (props) => {
const payload = formatter.formatDropdownPayload(props.payload)
await vonage.sendMessage(props, payload)
},
choice: async (props) => {
const payload = formatter.formatChoicePayload(props.payload)
await vonage.sendMessage(props, payload)
},
bloc: () => {
throw new RuntimeError('Not implemented')
},
},
},
},
handler: async ({ req, client }) => {
if (!req.body) {
console.warn('Handler received an empty body')
return
}
const data = JSON.parse(req.body)
console.info(`Handler received request of type ${data.message_type}`)
if (data.message_type !== 'text') {
throw new Error('Handler received an invalid message type')
}
const { conversation } = await client.getOrCreateConversation({
channel: 'channel',
tags: {
channel: data.channel,
channelId: data.to,
userId: data.from,
},
})
const { user } = await client.getOrCreateUser({
tags: {
channel: data.channel,
userId: data.from,
},
})
await client.createMessage({
tags: { id: data.message_uuid },
type: 'text',
userId: user.id,
conversationId: conversation.id,
payload: { text: data.text },
})
},
})
export default common.reporting.wrapIntegration(integration)
+133
View File
@@ -0,0 +1,133 @@
import * as bp from '.botpress'
export function formatLocationPayload(payload: bp.channels.channel.location.Location) {
return {
message_type: 'custom',
custom: {
type: 'location',
location: {
latitude: payload.latitude,
longitude: payload.longitude,
},
},
}
}
export function formatDropdownPayload(payload: bp.channels.channel.dropdown.Dropdown) {
return {
message_type: 'custom',
custom: {
type: 'interactive',
interactive: {
type: 'list',
body: {
text: payload.text,
},
action: {
button: 'Select an option',
sections: [
{
rows: payload.options.map((x, i) => ({ id: `slot-${i}::${x.value}`, title: x.label })),
},
],
},
},
},
}
}
export function formatChoicePayload(payload: bp.channels.channel.choice.Choice) {
if (payload.options.length < 3) {
return {
message_type: 'custom',
custom: {
type: 'interactive',
interactive: {
type: 'button',
body: {
text: payload.text,
},
action: {
buttons: payload.options.map((x, i) => ({
type: 'reply',
reply: { id: `slot-${i}::${x.value}`, title: x.label },
})),
},
},
},
}
}
if (payload.options.length <= 10) {
return {
message_type: 'custom',
custom: {
type: 'interactive',
interactive: {
type: 'list',
body: {
text: payload.text,
},
action: {
button: 'Select an option',
sections: [
{
rows: payload.options.map((x, i) => ({ id: `slot-${i}::${x.value}`, title: x.label })),
},
],
},
},
},
}
}
return {
message_type: 'text',
text: `${payload.text}\n\n${payload.options.map(({ label }, idx) => `*(${idx + 1})* ${label}`).join('\n')}`,
}
}
export function formatCarouselPayload(payload: bp.channels.channel.carousel.Carousel) {
let count = 0
return payload.items.map((card) => {
const cardPayload = formatCardPayload(card, count)
count += card.actions.length
return cardPayload
})
}
type CardOption = CardSay | CardPostback | CardUrl
type CardSay = { title: string; type: 'say'; value: string }
type CardPostback = { title: string; type: 'postback'; value: string }
type CardUrl = { title: string; type: 'url' }
export function formatCardPayload(payload: bp.channels.channel.card.Card, count: number = 0) {
const options: CardOption[] = []
payload.actions.forEach((action) => {
if (action.action === 'say') {
options.push({ title: action.label, type: 'say', value: action.value })
} else if (action.action === 'url') {
options.push({ title: `${action.label} : ${action.value}`, type: 'url' })
} else if (action.action === 'postback') {
options.push({ title: action.label, type: 'postback', value: action.value })
}
})
const body = `*${payload.title}*\n\n${payload.subtitle ? `${payload.subtitle}\n\n` : ''}${options
.map(({ title }, idx) => `*(${idx + count + 1})* ${title}`)
.join('\n')}`
if (payload.imageUrl) {
return {
message_type: 'image',
image: {
url: payload.imageUrl,
caption: body,
},
}
}
return { message_type: 'text', text: body }
}
+41
View File
@@ -0,0 +1,41 @@
import axios from 'axios'
import * as bp from '.botpress'
function getRequestMetadata(conversation: SendMessageProps['conversation']) {
const channel = conversation.tags?.channel
const channelId = conversation.tags?.channelId
const userId = conversation.tags?.userId
if (!channelId) {
throw new Error('Invalid channel id')
}
if (!userId) {
throw new Error('Invalid user id')
}
if (!channel) {
throw new Error('Invalid channel')
}
return { to: userId, from: channelId, channel }
}
type SendMessageProps = Pick<bp.AnyMessageProps, 'ctx' | 'conversation' | 'ack'>
export async function sendMessage({ conversation, ctx, ack }: SendMessageProps, payload: any) {
const { to, from, channel } = getRequestMetadata(conversation)
const response = await axios.post(
'https://api.nexmo.com/v1/messages',
{
...payload,
from,
to,
channel,
},
{
headers: { 'Content-Type': 'application/json' },
auth: { username: ctx.configuration.apiKey, password: ctx.configuration.apiSecret },
}
)
await ack({ tags: { id: response.data.message_uuid } })
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"paths": { "*": ["./*"] },
"outDir": "dist"
},
"include": [".botpress/**/*", "definitions/**/*", "src/**/*", "*.ts"]
}
+2
View File
@@ -0,0 +1,2 @@
import config from '../../vitest.config'
export default config