Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@botpress/cli",
"version": "6.6.1",
"version": "6.6.2",
"description": "Botpress CLI",
"scripts": {
"build": "pnpm run build:types && pnpm run bundle && pnpm run template:gen",
Expand Down Expand Up @@ -28,7 +28,7 @@
"@apidevtools/json-schema-ref-parser": "^11.7.0",
"@botpress/chat": "0.5.5",
"@botpress/client": "1.43.0",
"@botpress/sdk": "6.8.0",
"@botpress/sdk": "6.9.0",
"@bpinternal/const": "^0.1.0",
"@bpinternal/tunnel": "^0.1.1",
"@bpinternal/verel": "^0.2.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/templates/empty-bot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"private": true,
"dependencies": {
"@botpress/client": "1.43.0",
"@botpress/sdk": "6.8.0"
"@botpress/sdk": "6.9.0"
},
"devDependencies": {
"@types/node": "^22.16.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/templates/empty-integration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"private": true,
"dependencies": {
"@botpress/client": "1.43.0",
"@botpress/sdk": "6.8.0"
"@botpress/sdk": "6.9.0"
},
"devDependencies": {
"@types/node": "^22.16.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/templates/empty-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"private": true,
"dependencies": {
"@botpress/sdk": "6.8.0"
"@botpress/sdk": "6.9.0"
},
"devDependencies": {
"@types/node": "^22.16.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/templates/hello-world/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"private": true,
"dependencies": {
"@botpress/client": "1.43.0",
"@botpress/sdk": "6.8.0"
"@botpress/sdk": "6.9.0"
},
"devDependencies": {
"@types/node": "^22.16.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/templates/webhook-message/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"private": true,
"dependencies": {
"@botpress/client": "1.43.0",
"@botpress/sdk": "6.8.0",
"@botpress/sdk": "6.9.0",
"axios": "^1.6.8"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@botpress/sdk",
"version": "6.8.0",
"version": "6.9.0",
"description": "Botpress SDK",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
Expand Down
12 changes: 9 additions & 3 deletions packages/sdk/src/bot/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isApiError, Client, RuntimeError, Message, State } from '@botpress/client'
import { isApiError, Client, RuntimeError, Message, State, User, Conversation } from '@botpress/client'
import { retryConfig } from '../../retry'
import { Request, Response, parseBody } from '../../serve'
import * as utils from '../../utils/type-utils'
Expand Down Expand Up @@ -171,6 +171,8 @@ const onEventReceived = async (serverProps: types.ServerProps): Promise<Response
if (ctx.type === 'message_created') {
const event = body.event
let message: Message = event.payload.message
const user: User = event.payload.user
const conversation: Conversation = event.payload.conversation

common.logger = common.logger.with({
messageId: message.id,
Expand All @@ -182,6 +184,8 @@ const onEventReceived = async (serverProps: types.ServerProps): Promise<Response
for (const handler of beforeIncomingMessageHooks) {
const hookOutput = await handler({
...common,
user,
conversation,
data: message,
})
message = hookOutput?.data ?? message
Expand All @@ -192,8 +196,8 @@ const onEventReceived = async (serverProps: types.ServerProps): Promise<Response

const messagePayload: Parameters<(typeof messageHandlers)[number]>[0] = {
...common,
user: event.payload.user,
conversation: event.payload.conversation,
user,
conversation,
message,
event,
}
Expand All @@ -206,6 +210,8 @@ const onEventReceived = async (serverProps: types.ServerProps): Promise<Response
for (const handler of afterIncomingMessageHooks) {
const hookOutput = await handler({
...common,
user,
conversation,
data: message,
})
message = hookOutput?.data ?? message
Expand Down
34 changes: 30 additions & 4 deletions packages/sdk/src/bot/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,16 @@ export type WorkflowHandlers<TBot extends common.BaseBot> = {
) => Promise<void>
}

type BaseHookDefinition = { stoppable?: boolean; data: any }
type BaseHookDefinition = {
stoppable?: boolean
data: any
/**
* Per-hook-type extra props injected into the hook handler input. Only set
* for hooks where the bot runtime already has the relevant context (e.g.
* incoming message hooks).
*/
extraInputs?: object
}
type HookDefinition<THookDef extends BaseHookDefinition = BaseHookDefinition> = THookDef

/**
Expand All @@ -254,6 +263,10 @@ export type HookDefinitions<TBot extends common.BaseBot> = {
before_incoming_message: HookDefinition<{
stoppable: true
data: _IncomingMessages<TBot> & { '*': AnyIncomingMessage<TBot> }
extraInputs: {
user?: client.User
conversation?: client.Conversation
}
}>
before_outgoing_message: HookDefinition<{
stoppable: false
Expand All @@ -274,6 +287,10 @@ export type HookDefinitions<TBot extends common.BaseBot> = {
after_incoming_message: HookDefinition<{
stoppable: true
data: _IncomingMessages<TBot> & { '*': AnyIncomingMessage<TBot> }
extraInputs: {
user?: client.User
conversation?: client.Conversation
}
}>
after_outgoing_message: HookDefinition<{
stoppable: false
Expand All @@ -297,11 +314,20 @@ export type HookData<TBot extends common.BaseBot> = {
}
}

type _HookExtraInputs<TBot extends common.BaseBot> = {
[THookType in utils.StringKeys<HookDefinitions<TBot>>]: HookDefinitions<TBot>[THookType] extends {
extraInputs: infer T
}
? T
: {}
}

export type HookInputs<TBot extends common.BaseBot> = {
[THookType in utils.StringKeys<HookData<TBot>>]: {
[THookDataName in utils.StringKeys<HookData<TBot>[THookType]>]: ExtendedHandlerProps<TBot> & {
data: HookData<TBot>[THookType][THookDataName]
}
[THookDataName in utils.StringKeys<HookData<TBot>[THookType]>]: ExtendedHandlerProps<TBot> &
_HookExtraInputs<TBot>[THookType] & {
data: HookData<TBot>[THookType][THookDataName]
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/plugin/conversation-proxy/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export type ActionableConversation<
Omit<client.ClientInputs['updateConversation'], 'id'>,
{ tags?: commonTypes.ToTags<typeUtils.StringKeys<TPlugin['conversation']['tags']>> }
>
) => Promise<ActionableConversation<TPlugin>>
) => Promise<ActionableConversation<TPlugin, TChannelName, TMessage>>
getMessage: (props: { id: string }) => Promise<messageProxy.ActionableMessage<TPlugin, TMessage>>
getOrCreateMessage: (
props: Omit<client.ClientInputs['getOrCreateMessage'], 'conversationId' | 'tags' | 'type' | 'payload'> &
Expand Down
51 changes: 40 additions & 11 deletions packages/sdk/src/plugin/implementation.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as client from '@botpress/client'
import type {
MessageHandlersMap as BotMessageHandlersMap,
EventHandlersMap as BotEventHandlersMap,
Expand Down Expand Up @@ -46,16 +47,21 @@ export type PluginImplementationProps<TPlugin extends BasePlugin = BasePlugin> =
actions: ActionHandlers<TPlugin>
}

type WithMessageContext<T> = T & { user: client.User; conversation: client.Conversation }

const _hookInputHasMessageContext = <T extends object>(input: T): input is WithMessageContext<T> =>
'user' in input && 'conversation' in input

type Tools<TPlugin extends BasePlugin = BasePlugin> = InjectedHandlerProps<TPlugin>

export class PluginImplementation<TPlugin extends BasePlugin = BasePlugin> implements BotHandlers<TPlugin> {
private _runtimeProps: PluginRuntimeProps<TPlugin> | undefined

private _actionHandlers: ActionHandlers<any>
private _messageHandlers: OrderedMessageHandlersMap<any> = {}
private _eventHandlers: OrderedEventHandlersMap<any> = {}
private _stateExpiredHandlers: OrderedStateExpiredHandlersMap<any> = {}
private _hookHandlers: OrderedHookHandlersMap<any> = {
private _messageHandlers: OrderedMessageHandlersMap = {}
private _eventHandlers: OrderedEventHandlersMap = {}
private _stateExpiredHandlers: OrderedStateExpiredHandlersMap = {}
private _hookHandlers: OrderedHookHandlersMap = {
before_incoming_event: {},
before_incoming_message: {},
before_outgoing_message: {},
Expand All @@ -67,7 +73,7 @@ export class PluginImplementation<TPlugin extends BasePlugin = BasePlugin> imple
after_outgoing_call_action: {},
after_incoming_call_action: {},
}
private _workflowHandlers: OrderedWorkflowHandlersMap<any> = {
private _workflowHandlers: OrderedWorkflowHandlersMap = {
started: {},
continued: {},
timed_out: {},
Expand Down Expand Up @@ -264,12 +270,35 @@ export class PluginImplementation<TPlugin extends BasePlugin = BasePlugin> imple

return handlers.map(({ handler }) =>
utils.functions.setName(
(input: utils.types.ValueOf<HookInputsWithoutInjectedProps<TPlugin>>['*']) =>
handler({
...input,
data: unprefixTagsOwnedByPlugin(input.data, { alias: this._runtime.alias }),
...this._getTools(input.client),
}),
(input: utils.types.ValueOf<HookInputsWithoutInjectedProps<TPlugin>>['*']) => {
const data = unprefixTagsOwnedByPlugin(input.data, { alias: this._runtime.alias })
const tools = this._getTools(input.client)

let extraProps: Record<string, any> = {}

// `before_incoming_message` and `after_incoming_message` carry the raw
// `user` and `conversation` from the incoming event payload; wrap them
// as proxies so handlers see the same shape as `plugin.on.message(...)`.
// Other hook types don't carry these fields, so we just forward.
if (_hookInputHasMessageContext(input)) {
const user = proxyUser({
client: input.client,
user: input.user,
conversationId: input.conversation.id,
pluginAlias: this._runtime.alias,
})

const conversation = proxyConversation({
client: input.client,
plugin: this._runtime,
conversation: input.conversation,
})

extraProps = { user, conversation }
}

return handler({ ...input, data, ...extraProps, ...tools })
},
handler.name
)
)
Expand Down
18 changes: 18 additions & 0 deletions packages/sdk/src/plugin/server/types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,21 @@ test('MessageRequest with base plugin should be loose type', () => {
]
>
})

test('HookHandlers with implemented plugin should always extend AnyHookHandler', () => {
type Actual = types.HookHandlers<FooBarBazPlugin>
type Expected = Record<types.HookDefinitionType, Record<string, types.AnyHookHandler<FooBarBazPlugin>>>
type _assertion = utils.AssertExtends<Actual, Expected>
})

test('HookHandlers with empty plugin should always extend AnyHookHandler', () => {
type Actual = types.HookHandlers<EmptyPlugin>
type Expected = Record<types.HookDefinitionType, Record<string, types.AnyHookHandler<EmptyPlugin>>>
type _assertion = utils.AssertExtends<Actual, Expected>
})

test('HookHandlers with base plugin should always extend AnyHookHandler', () => {
type Actual = types.HookHandlers<common.BasePlugin>
type Expected = Record<types.HookDefinitionType, Record<string, types.AnyHookHandler<common.BasePlugin>>>
type _assertion = utils.AssertExtends<Actual, Expected>
})
Loading
Loading