How to customize user dialog in botpress ver 11.9.5 [Updated to v12.0.1]?

I am trying to add timestamp to every dialog in botpress chat. So far I am able to add this timestamp in bot’s dialog, but I need some pointers in adding the same to user’s dialog.

Screenshot from chat showing timestamp in bot’s dialog
chatbot%20screenshot
Custom component

export class InfaText extends React.Component {
  message = this.props.text

  getTimestamp = () => {
	let date = new Date();
	let options = {
	  month: "short",
	  day: "numeric", hour: "2-digit", minute: "2-digit"
	};
	return date.toLocaleTimeString("en-us", options);
  }
  render() {
	return (<div className="infaTextMain">
	  <p className="infaTextMessage">{this.message}</p>
	  <small className="infaTextTimestamp">{this.getTimestamp()}</small>
	</div>)
  }
}

Also, is there a generic way to add a timestamp to all dialogs?

@allardy, @DigiSenseiZim can you please help with this?

Hi @abhisheksimon , it’s not possible in 11.9.5, but it’s a lot easier with 12.0 which was just released yesterday. We have completely revamped the possibilities of customization on the webchat, and the new debugger was developed as a Custom Component.

That means that anything achievable with the debugger can be achieve by your componnet. This might be interesting for you:

 this.props.store.setMessageWrapper({ module: 'extensions', component: 'Wrapper' })

(it wraps EVERY messages with that component, so it could be your time wrapper)

Check the debugger source code to see how it was implemented.

And check here how it was embedded on the webchat:

Thanks for your reply

I upgraded to v12.0.1 and tried with a fresh botpress source code and bot. Below are the changes that I made.

  1. Created InfaMessageWrapper.jsx class here modules\infa-module\src\views\lite\components\InfaMessageWrapper.jsx
    image
  2. Made changes modules\infa-module\src\views\lite\index.jsx to include InfaMessageWrapper
    image
  3. Added entry for InfaMessageWrapper in modules\channel-web\src\views\full\index.tsx file
    image

Even then I do not see this wrapper in effect, what am I missing here?
Note: In below screenshot, the dialogs do not have any timestamp. This wrapper should add a timestamp to each dialog.

You need two different components. One is the wrapper itself, the other one is the “controller”.

The component you specify in the “overrides” section is the controller. When it is mounted, it will call the this.props.store.setMessageWrapper() to define the wrapper that will be used for all messages.

The wrapper itself should be lightweight (and should not call the “setMessageWrapper”).

Logic looks good, just split your actual InfraMessageWrapper and you should be good to go !

Here is what I did

  1. I created a controller modules\infa-module\src\views\lite\components\InfaMessageController.jsx and
  2. a wrapper modules\infa-module\src\views\lite\components\wrapper\MessageWrapper.tsx and
  3. updated controller name accordingly in modules\infa-module\src\views\lite\index.jsx and modules\channel-web\src\views\full\index.tsx file, please check screenshot below for the directory structure.

image

but during the build process, it fails on my custom module infa-module with below exception also I have following 2 questions apart from the given exception

  1. What should I return from InfaMessageController controller in the render block, since MessageWrapper is already returning the wrapped dialog with timestamp :crossed_fingers:.
  2. What are this.props and Props interface in InfaMessageController, is Props referred by this.props.store.setMessageWrapper?
    a. In MessageWrapper should I use {this.props.children} or {this.wrapperprops.children} ?
=======================================
Error building module C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module
=======================================
Status:
There was a breaking change in how module views are handled in Botpress 11.6
Web bundles and liteViews were replaced by a more standardized method.

Please check our migration guide here: https://botpress.io/docs/developers/migrate/

error Command failed with exit code 1.

Output: [1/4] Resolving packages...
success Already up-to-date.
$ ./node_modules/.bin/module-builder build
[module-builder] Build completed
[module-builder] Child
       3 modules
Child
       8 modules

    ERROR in ./src/views/lite/components/InfaMessageController.jsx
    Module build failed (from ./node_modules/babel-loader/lib/index.js):
    SyntaxError: C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module\src\views\lite\components\InfaMessageController.jsx: Unexpected token, expected "{" (2:58)

      1 |
    > 2 | export class InfaMessageController extends React.Component<Props> {
        |                                                           ^
      3 |
      4 |   componentDidUpdate(_prevProps, prevState) {
      5 |     this.props.store.setMessageWrapper({ module: 'infa-module', component: './wrapper/MessageWrapper' })
        at Object.raise (C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module\node_modules\@babel\parser\lib\index.js:6344:17)
        at Object.unexpected (C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module\node_modules\@babel\parser\lib\index.js:7659:16)
        at Object.expect (C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module\node_modules\@babel\parser\lib\index.js:7645:28)
        at Object.parseClassBody (C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module\node_modules\@babel\parser\lib\index.js:10562:10)
        at Object.parseClass (C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module\node_modules\@babel\parser\lib\index.js:10537:22)
        at Object.parseStatementContent (C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module\node_modules\@babel\parser\lib\index.js:9830:21)
        at Object.parseStatement (C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module\node_modules\@babel\parser\lib\index.js:9788:17)
        at Object.parseExportDeclaration (C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module\node_modules\@babel\parser\lib\index.js:10980:17)
        at Object.maybeParseExportDeclaration (C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module\node_modules\@babel\parser\lib\index.js:10930:31)
        at Object.parseExport (C:\Tools\Workspaces\Botpress\12x\1201\botpress-master\modules\infa-module\node_modules\@babel\parser\lib\index.js:10859:29)
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

[15:36:13] 'build-module infa-module' errored after 16 s
[15:36:13] Error: Command failed: cross-env npm_config_target_platform=darwin yarn  && yarn build

InfaMessageController.jsx


export class InfaMessageController extends React.Component<Props> {
  componentDidUpdate(_prevProps, prevState) {
    this.props.store.setMessageWrapper({ module: 'infa-module', component: './wrapper/MessageWrapper' })
  }
  render() {
    return (
      { this.props.children }
    )
  }
}
interface Props {
  store: any
}

MessageWrapper.tsx

import React from 'react'
export class MessageWrapper extends React.Component<WrapperProps> {
  getTimestamp = () => {
    let date = new Date();
    let options = {
      month: "short",
      day: "numeric", hour: "2-digit", minute: "2-digit"
    };
    return date.toLocaleTimeString("en-us", options);
  }

  render() {
    return (
      <div>
        <p>--{this.props.children}</p>
        <small className="infaTextTimestamp">{this.getTimestamp()}</small>
      </div>
    )
  }
}

interface WrapperProps {
  incomingEventId: string
  store: any
}

index.jsx

...
export { InfaMessageController } from './components/InfaMessageController'
...

index.tsx

...
overrides: {
          before_container: [
            {
              module: 'extensions',
              component: 'Debugger'
            },
            {
              module: 'infa-module',
              component: 'InfaMessageController'
            }
...

@allardy can you please provide some input.
@EFF please provide some input, I tried exactly mentioned in your stackoverflow answer, but still no luck https://stackoverflow.com/questions/56818568/how-to-customize-user-dialog-in-botpress-ver-11-9-5

@abhisheksimon Your file is still jsx and not tsx, so is not allowed.

InfraMessageController:

  • Remove
  • I suggest setting your message wrapper like thos: ({ module: infra-module’, component: ‘MessageWrapper’}, and make sure MessageWrapper is exported from lite/index.jsx
  • You can simply return render(){ return null } for the controller, it’s only used to contact the store

Other than that, it looks good !

I renamed modules\infa-module\src\views\lite\components\InfaMessageController.jsx to
modules\infa-module\src\views\lite\components\InfaMessageController.tsx

now it says

    ERROR in ./src/views/lite/index.jsx
    Module not found: Error: Can't resolve './components/InfaMessageController' in 'C:\Tools\Workspaces\Botpress\12x\1202\botpress-master\modules\infa-module\src\views\lite'
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

[18:03:30] 'build-module infa-module' errored after 14 s

When using typescript in your module view, you need to add tsconfig.json and tslint.json in your src/views folder (see an example in channel-web/nlu modules). Also, restart the build of the module (if you watch you will get this error because it still expects the .jsx file).

If you still have the issue, please provide screenshots of your work directory and the controller file

Is there any document w.r.t understanding when jsx and tsx should be used.

It is more than 20 days I am struggling to get this working. It would be really great, if you can point me to a proper document or help me with this issue.

Here are the details of my controller with relevant screenshots

modules\infa-module\src\views\lite\components\InfaMessageController.tsx
image

modules\infa-module\src\views\lite\components\MessageWrapper.tsx
image

modules\infa-module\src\views\lite\index.jsx

import React from 'react'
export class LiteView extends React.Component {
  render() {
    return null
  }
}
export { InfaMessageController } from './components/InfaMessageController'

modules\infa-module\src\views\tslint.json - copied from channel-web/nlu modules

{
  "extends": "../../../../tslint.json",
  "rules": {
    "quotemark": [false, "single", "avoid-escape"],
    "no-null-keyword": false
  },
  "linterOptions": {
    "exclude": ["**/*.json"]
  }
}

modules\infa-module\src\views\tsconfig.json - copied from channel-web/nlu modules

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowJs": true,
    "target": "es5",
    "lib": ["es7", "dom", "esnext"],
    "jsx": "react",
    "typeRoots": ["../../node_modules/@types"],
    "baseUrl": "./",
    "paths": {
      "botpress/ui": ["../../../../src/bp/ui-studio/src/web/components/Shared/Interface/typings.d.ts"],
      "botpress/sdk": ["../botpress.d.ts"]
    }
  }
}

Screenshot of infa-modules directory
image

Build error with above changes

If you struggle with typescript typed files (files ending in .tsx), you can just stick to jsx, which doesn’t include typing and is more permissive. Just change your files to .jsx and remove typings, it’s gonna be easier to get started. Then if you want in the future, start converting them to tsx to get typings.

  1. In InfraMessageController, you should have component: 'MessageWrapper. That component must be exported directly from your lite/index.jsx file. It can’t be relative, because the webchat doesn’t know your module structure. It needs only to know “Load component X from module Y”.

  2. You are also missing imports for react

  3. Your message wrapper looks all good

  4. In lite/index.jsx, you also need to export the message wrapper: export { MessageWrapper } from './components/MessageWrapper

  5. All good for the copied files

If you still can get it to work, send me your module zipped (without node_modules) at yann@botpress.io and i’ll have a look

@allardy I followed your steps but still no luck, so I have sent infa-module.zip to your email id. Also since you have my module can you please look at this question as well?

Thanks

@abhisheksimon Found the problem. I missed it when I checked your transcripts earlier, but if you don’t render anything (like in InfraMessageController), your componentDIdUpdate will never be called. Just move your message wrapper to componentDidMount and you’ll be good to go !

Also, you should add any dependency you use in your module inside your package.json (eg: react-select, axios). This way it’s guaranteed they will be available for your module and you won’t get weird errors.

@allardy I tried componentDidMount and other suggestions in your earlier posts, but nothing is working for me.

Also, dependencies are already present in my package.json

Current InfaMessageController.jsx

import React from 'react'

export class InfaMessageController extends React.Component {
  componentDidMount() {
    console.log("componentDidMount called");
    this.props.store.setMessageWrapper({ module: 'infa-module', component: 'MessageWrapper' });
    console.log("componentDidMount messageWrapped");
  }
  /*componentDidUpdate(_prevProps, prevState) {
    console.log("componentDidUpdate called");
    this.props.store.setMessageWrapper({ module: 'infa-module', component: 'MessageWrapper' });
  }*/

  render() {
    return null
  }
}

componentDidMount() is also getting called

MessageWrapper.jsx

import React from 'react'

export class MessageWrapper extends React.Component {
  getTimestamp = () => {
    let date = new Date();
    let options = {
      month: "short",
      day: "numeric", hour: "2-digit", minute: "2-digit"
    };
    return date.toLocaleTimeString("en-us", options);
  }

  render() {
    return (
      <div>
        {this.props.children}
        <small className="infaTextTimestamp">{this.getTimestamp()}</small>
      </div>
    )
  }
}

and lite\index.jsx

import React from 'react'
/**
 * The lite views are meant to be lightweight. They shouldn't include heavy dependencies.
 * Common use case is to add custom components on the web chat. It's also possible to share them to other modules
 *
 * Even if you don't plan to include a lite view, you must include an empty view that returns 'null'
 */
export class LiteView extends React.Component {
  render() {
    return null
  }
}

/* Also tried solution by eff_it, from stackoverflow
 * https://stackoverflow.com/a/56962582/707414
  export const MessageWrapper = props => (
  <div>
    <p style={{ color: 'blue' }}>sent on: {new Date(props.sentOn).toDateString()}</p>
    <div style={{ background: 'black', color: 'white' }}>{props.children}</div>
  </div>
)

export const MySuperOverride = props => {
  props.store.setMessageWrapper({ module: 'infa-module', component: 'MessageWrapper' })
  return null
}*/

export { InfaDropdownMenu } from './components/InfaDropdownMenu'
export { InfaText } from './components/InfaText'
export { InfaLinkPreview } from './components/InfaLinkPreview'

export { MessageWrapper } from './components/MessageWrapper'
export { InfaMessageController } from './components/InfaMessageController'

Will it be possible for you to look at the infa-module which is sent over mail, make changes which works and send it back to me?

I sent you what I used. Works like a charm on my side. Put embedded-webchat.html in assets/channel-web/examples and open it:

@allardy Thank you for looking into it, is there something else (like a configuration) out of the infa-module module that is missing?

Since I did a diff of my latest infa-module and the one you just sent, everything seems to match, except the console logs.

External configuration where in overrides InfaMessageController is getting used: modules\channel-web\src\views\full\index.tsx

overrides: {
          before_container: [
            {
              module: 'infa-module',
              component: 'InfaMessageController'
            },
            {
              module: 'extensions',
              component: 'Debugger'
            }
            /* Disabled for now until we get a proper UX with that
            {
              module: 'testing',
              component: 'ScenarioBuilder'
            }*/
          ]
        }

I also tried creating a new bot with an empty bot template, still no luck.

Update
Do I need to create a InfaMessageController content type under modules\infa-module\src\content-types ?

If you edit modules/channel-web/src/views/full/index.tsx to add your module, then you will only see your custom component on the studio.

You have loaded the embedded webchat on the screenshot. Have you replaced it with the one I sent you (which include your controller) ?

Also, please note that the debugger already uses the MessageWrapper, and only one can be set at a time. If you add your message controller on the studio (in the channelweb index.tsx file), you also need to remove the debugger.

I recommend using the embedded-webchat file and only load your module.

@allardy I tried replacing this but it did not work.

Thanks, this helped. For anyone who faces similar issues I have created a step by step instructions here.

Glad that helped! Just to add a bit more clarification, there can only be one message wrapper set at a time for the moment. When you open the debugger (the bug icon), it displays the panel and sets the message wrapper. When you hide the panel, it removes it.

You could uncheck “Always show debugger” and set your message wrapper on mount, but it would be removed when you toggle the debugger.

Also, you could add a button next to the debugger to toggle your message wrapper for different use cases

1 Like