11 | )
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/custom modules/calendly/calendly/src/views/lite/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | /**
4 | * The lite views are meant to be lightweight. They shouldn't include heavy dependencies.
5 | * Common use case is to add custom components on the web chat. It's also possible to share them to other modules
6 | *
7 | * Even if you don't plan to include a lite view, you must include an empty view that returns 'null'
8 | */
9 | export class LiteView extends React.Component {
10 | render() {
11 | return null
12 | }
13 | }
14 |
15 | export { CalendlyCard } from './CalendlyCard'
16 |
--------------------------------------------------------------------------------
/custom modules/calendly/calendly/src/views/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig_view.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "typeRoots": [
6 | "../../node_modules/@types", "lite/typings.d.ts"]
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/custom modules/calendly/calendly/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom modules/calendly/calendly/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | react-calendly@^2.2.1:
6 | version "2.2.1"
7 | resolved "https://registry.yarnpkg.com/react-calendly/-/react-calendly-2.2.1.tgz#7c35f7747e01045dbd77e18ea1eb18adddf373cf"
8 | integrity sha512-r9WJ2WNr3hjBFnE8UyFCtJiTy7InME0lghw3gA5BqAMOFe70OtLDINDRAKDSS8O1tTSxJJrFdmZ1PpFSL0NAJA==
9 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/custom-multiselect.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/custom-multiselect/custom-multiselect.tgz
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/sendComponent.js:
--------------------------------------------------------------------------------
1 | const options = [
2 | { label: "Test1", value: "Value1" },
3 | { label: "Test2", value: "Value2" },
4 | ];
5 |
6 | //These options will appear to the user
7 | const payloads = [
8 | {
9 | type: "custom",
10 | module: "custom-multiselect",
11 | component: "MultiSelect",
12 | question: "Please select an option",
13 | options,
14 | },
15 | ];
16 |
17 | bp.events.replyToEvent(
18 | {
19 | botId: event.botId,
20 | channel: event.channel,
21 | target: event.target,
22 | threadId: event.threadId,
23 | },
24 | payloads,
25 | event.id
26 | );
27 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/README.md:
--------------------------------------------------------------------------------
1 | ## Overview
2 |
3 | This is the custom module which was showcased during the demonstration.
4 | Please check the [official documentation](https://botpress.com/docs/developers/create-module/) for more information
5 |
6 | ## Quick Start
7 |
8 | 1. Copy the contents of this folder to to `modules/akeed-custom-module`
9 | 2. Open a terminal in the folder `modules/akeed-custom-module` and type `yarn && yarn build`
10 | 3. Edit your `botpress.config.json` and add the module definition so it will be loaded:
11 |
12 | ```js
13 | {
14 | ...
15 | "modules": [
16 | ...
17 | {
18 | "location": "MODULES_ROOT/akeed-custom-module",
19 | "enabled": true
20 | },
21 | }
22 | ```
23 |
24 | 4. Start Botpress: `yarn start`
25 |
26 | ## Continuous Development
27 |
28 | When you make changes to any portion of your module, you need to build it and restart Botpress.
29 |
30 | You can type `yarn watch` which will save you some time, since every time you make a change, the source will be compiled immediately. You will only have to restart Botpress.
31 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/build.extras.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes paths (or glob: https://www.npmjs.com/package/glob) that will be copied in the "dist" folder when the module is built.
3 | *
4 | * Why would you want to put files in the "dist" folder?
5 | * Only files in that folder can be read by Botpress, so if your templates aren't in the "dist" folder, they won't be available.
6 | */
7 | module.exports = {
8 | copyFiles: ['src/bot-templates/**']
9 | }
10 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "custom-multiselect",
3 | "version": "1.0.0",
4 | "description": "Some description on what this module is about",
5 | "private": true,
6 | "main": "dist/backend/index.js",
7 | "author": "Botpress, Inc.",
8 | "license": "AGPL-3.0-only",
9 | "scripts": {
10 | "build": "node ../../build/module-builder/bin/entry build",
11 | "watch": "node ../../build/module-builder/bin/entry watch",
12 | "package": "node ../../build/module-builder/bin/entry package"
13 | },
14 | "dependencies": {}
15 | }
16 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/src/backend/index.ts:
--------------------------------------------------------------------------------
1 | import * as sdk from 'botpress/sdk'
2 |
3 | const entryPoint: sdk.ModuleEntryPoint = {
4 | definition: {
5 | // This must match the name of your module's folder, and the name in package.json
6 | name: 'custom-multiselect',
7 | /**
8 | * When menuIcon is set to `custom`, you need to provide an icon. It must be at that location: `/assets/icon.png`
9 | * Otherwise, use Material icons name: https://material.io/tools/icons/?style=baseline
10 | */
11 | menuIcon: 'flag',
12 | // This is the name of your module which will be displayed in the sidebar
13 | menuText: 'Complete Module',
14 | // When set to `true`, the name and icon of your module won't be displayed in the sidebar
15 | noInterface: false,
16 | // The full name is used in other places, for example when displaying bot templates
17 | fullName: 'Complete Module',
18 | // Not used anywhere, but should be a link to your website or module repository
19 | homepage: 'https://botpress.com'
20 | }
21 | }
22 |
23 | export default entryPoint
24 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/src/config.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Anything that you would like to make configurable to the bot owner would go in this file.
3 | * Botpress handles itself loading the configuration files.
4 | *
5 | * Bot configuration files will override Global configuration when available:
6 | * For example, `data/bots/MY_BOT/config/complete-module.json` will be used by MY_BOT, while `data/global/config/complete-module.json` will be used for all others
7 | */
8 | export interface Config {}
9 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/src/views/full/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default class MyModule extends React.Component {
4 | render() {
5 | return null
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/src/views/lite/components/style.css:
--------------------------------------------------------------------------------
1 | /* Container for whole component */
2 | .multiselect {
3 | display: block;
4 | position: relative;
5 | padding-left: 5px;
6 | margin-bottom: 12px;
7 | }
8 |
9 | /*Grid settings. Add more auto for more columns */
10 | .container{
11 | display: grid;
12 | grid-template-columns: auto auto;
13 | grid-gap: 5px;
14 | padding: 5px;
15 | }
16 |
17 | /* Container for the different options */
18 | .menu-item{
19 | font-size:18px;
20 | display:flex;
21 | }
22 |
23 | /* Checkbox size */
24 | .menu-item input {
25 | height:18px;
26 | width:18px;
27 | }
28 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/src/views/lite/index.jsx:
--------------------------------------------------------------------------------
1 | import { MultiSelect } from './components/MultiSelect'
2 |
3 | export { MultiSelect }
4 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/src/views/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig_view.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "typeRoots": ["../../node_modules/@types", "lite/typings.d.ts"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/src/views/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tslint.json",
3 | "rules": {
4 | "quotemark": [false, "single", "avoid-escape"],
5 | "no-null-keyword": false
6 | },
7 | "linterOptions": {
8 | "exclude": ["**/*.json"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom modules/custom-multiselect/source code/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 |
--------------------------------------------------------------------------------
/custom modules/date-picker/date-picker.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/date-picker/date-picker.tgz
--------------------------------------------------------------------------------
/custom modules/date-picker/source/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "date-picker",
3 | "fullName": "Date Picker Widget",
4 | "version": "1.0.0",
5 | "description": "Date picker widget",
6 | "private": true,
7 | "main": "dist/backend/index.js",
8 | "scripts": {
9 | "build": "node ../../build/module-builder/bin/entry build",
10 | "watch": "node ../../build/module-builder/bin/entry watch",
11 | "package": "node ../../build/module-builder/bin/entry package"
12 | },
13 | "author": "Botpress, Inc.",
14 | "license": "AGPL-3.0-only",
15 | "dependencies": {
16 | "@blueprintjs/datetime": "^3.23.19",
17 | "axios": "0.21.0",
18 | "moment": "^2.29.1",
19 | "query-string": "5.0.1"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/actions/date-compare.js:
--------------------------------------------------------------------------------
1 | const moment = require('moment')
2 |
3 | /**
4 | * Compare two dates.
5 | * The result is stored in temp.dateComparison and can have 3 different values: isBefore, isAfter, isEqual
6 | * @title Date comparison
7 | * @category Date
8 | * @author Botpress
9 | * @param {string|Date|moment} date1 The first date to compare
10 | * @param {string|Date|moment} date2 The date to compare it to
11 | * @param {string} [output=dateComparison] Name of the temporary variable where the result will be saved
12 | */
13 | const compareDates = async (rawDate1, rawDate2, output) => {
14 | if (!rawDate1 || !rawDate2) {
15 | return bp.logger.warn(`Both dates must be configured`)
16 | }
17 |
18 | const date1 = moment(rawDate1)
19 | const date2 = moment(rawDate2)
20 |
21 | if (date1.isBefore(date2)) {
22 | temp[output] = 'isBefore'
23 | } else if (date1.isAfter(date2)) {
24 | temp[output] = 'isAfter'
25 | } else {
26 | temp[output] = 'isEqual'
27 | }
28 | }
29 |
30 | return compareDates(args.date1, args.date2, args.output)
31 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/actions/date-extract.js:
--------------------------------------------------------------------------------
1 | if (event.type === 'datePicker') {
2 | event.state.temp.startDate = event.payload.startDate
3 | event.state.temp.endDate = event.payload.endDate
4 | }
5 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/actions/date-parser.js:
--------------------------------------------------------------------------------
1 | const moment = require('moment')
2 |
3 | /**
4 | * Generic date parser. Leave the date empty to use today's date.
5 | * If the format is empty, it will return a normal Date object.
6 | * Set the format to 'moment' to return the moment object
7 | * Value is stored in {{temp.parsedDate}} by default
8 | * @title Date parser
9 | * @category Date
10 | * @author Botpress
11 | * @param {string|Date|moment} date The date to process (can be a string or a js Date object)
12 | * @param {string} [format=YYYY-MM-DD] Format of the date to output.
13 | * @param {string} [output=parsedDate] Name of the temporary variable where the result will be saved
14 | *
15 | */
16 | const parseDate = async (rawDate, format, output) => {
17 | const date = moment(!rawDate ? undefined : rawDate)
18 |
19 | if (!format) {
20 | temp[output] = date.toDate()
21 | } else if (format === 'moment') {
22 | temp[output]
23 | } else {
24 | temp[output] = date.format(format)
25 | }
26 | }
27 |
28 | return parseDate(args.date, args.format, args.output)
29 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/backend/datePicker.ts:
--------------------------------------------------------------------------------
1 | import * as sdk from 'botpress/sdk'
2 | import { MODULE_NAME } from '.'
3 |
4 | export const generateFlow = async (
5 | data: any,
6 | metadata: sdk.FlowGeneratorMetadata
7 | ): Promise => {
8 | return {
9 | flow: {
10 | nodes: [
11 | {
12 | name: 'entry',
13 | onEnter: [
14 | {
15 | type: sdk.NodeActionType.RunAction,
16 | name: `${MODULE_NAME}/send-date-picker`,
17 | args: data
18 | }
19 | ],
20 | onReceive: [
21 | {
22 | type: sdk.NodeActionType.RunAction,
23 | name: 'date-picker/date-extract',
24 | args: {}
25 | }
26 | ],
27 | next: [{ condition: 'true', node: '#' }]
28 | }
29 | ],
30 | catchAll: {
31 | next: []
32 | }
33 | },
34 | transitions: createTransitions()
35 | }
36 | }
37 |
38 | const createTransitions = (): sdk.NodeTransition[] => {
39 | return [
40 | { caption: 'On success', condition: 'temp.startDate', node: '' },
41 | { caption: 'On cancel', condition: '!temp.startDate', node: '' }
42 | ]
43 | }
44 |
45 | export default { generateFlow }
46 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/config.ts:
--------------------------------------------------------------------------------
1 | export interface Config {
2 | /**
3 | * @default false
4 | */
5 | enabled: boolean
6 | /**
7 | * Configurations specific to messenger channel
8 | */
9 | messenger: {
10 | /**
11 | * When messenger module is enabled, host must be added to whitelist to use the webview.
12 | * Enable this when the messenger channel is enabled
13 | * @default false
14 | */
15 | addToWhitelist: boolean
16 | /**
17 | * Provide custom URL instead of the server's external url (for whitelist & return url)
18 | * @default ""
19 | */
20 | customUrl: string
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/translations/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "cancel": "Cancel",
3 | "submit": "Submit",
4 | "thankYouClose": "Thank you! You can close this window."
5 | }
6 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/translations/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "cancel": "Annuler",
3 | "submit": "Confirmer",
4 | "thankYouClose": "Merci! Vous pouvez fermer cette fenêtre."
5 | }
6 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/views/full/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | export { DatePicker } from './DatePicker'
3 |
4 | export class LiteView extends React.Component {
5 | render() {
6 | return null
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/views/full/typings.d.ts:
--------------------------------------------------------------------------------
1 | // These are properties provided by the studio
2 | export interface SkillProps {
3 | initialData: T
4 | onDataChanged: (data: T) => void
5 | onValidChanged: (canSubmit: boolean) => void
6 | resizeBuilderWindow: (newSize: 'normal' | 'large' | 'small') => void
7 | contentLang: string
8 | defaultLanguage: string
9 | languages: string[]
10 | }
11 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/views/lite/Stylesheet.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default ({ href, onLoad }: { href: string; onLoad?: () => void }) => (
4 |
5 | )
6 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/views/lite/index.tsx:
--------------------------------------------------------------------------------
1 | import MessengerPicker from './MessengerPicker'
2 | import WebPicker from './WebPicker'
3 |
4 | export { WebPicker, MessengerPicker }
5 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/views/lite/style.scss:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0 !important;
3 | width: 250px !important;
4 | }
5 |
6 | :global(.DayPicker-Day--selected) {
7 | background-color: darkgray !important;
8 | }
9 | :global(.DayPicker-Day--selected-range) {
10 | background-color: lightgray !important;
11 | }
12 |
13 | .myDiv {
14 | width: 300px;
15 | }
16 |
17 | .center {
18 | text-align: center;
19 | }
20 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/views/lite/style.scss.d.ts:
--------------------------------------------------------------------------------
1 | // This file is automatically generated.
2 | // Please do not change this file!
3 | interface CssExports {
4 | 'center': string;
5 | 'myDiv': string;
6 | }
7 | declare var cssExports: CssExports;
8 | export = cssExports;
9 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/src/views/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig_view.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom modules/date-picker/source/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/assets/README.md:
--------------------------------------------------------------------------------
1 | ## Assets
2 |
3 | Every time the server is started, all the content of this folder will be copied in the `assets/modules/delegate-bot-conversation/` folder.
4 | They will overwrite existing files.
5 |
6 | Beware: This is a little different from actions and hooks, since they will be replaced even if you edit them.
7 |
8 | ## WARNING
9 |
10 | Every file in this folder will be publicly available for unauthenticated users.
11 | Only front-end bundles, images, css, etc. should be in this folder.
12 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/assets/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Delegate Bot Conversation Example
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Files in the assets folder are publicly available to anyone.
12 | You can link to other files using relative path: /assets/modules/delegate-bot-conversation
13 | When you change assets in your module folder, Botpress needs to be restarted to take new files
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/delegate-bot-conversation/assets/logo.png
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/build.extras.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes paths (or glob: https://www.npmjs.com/package/glob) that will be copied in the "dist" folder when the module is built.
3 | *
4 | * Why would you want to put files in the "dist" folder?
5 | * Only files in that folder can be read by Botpress, so if your templates aren't in the "dist" folder, they won't be available.
6 | */
7 | module.exports = {
8 | copyFiles: ['src/bot-templates/**']
9 | }
10 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/delegate-bot-conversation.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/delegate-bot-conversation/delegate-bot-conversation.tgz
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "delegate-bot-conversation",
3 | "version": "1.0.0",
4 | "description": "Transfer a conversation from one bot to another and back again",
5 | "private": true,
6 | "main": "dist/backend/index.js",
7 | "author": "Botpress, Inc.",
8 | "license": "AGPL-3.0-only",
9 | "webpack": {
10 | "externals": {
11 | "react-select": "ReactSelect",
12 | "reactstrap": "Reactstrap",
13 | "botpress/tooltip": "BotpressTooltip"
14 | }
15 | },
16 | "scripts": {
17 | "build": "node ../../build/module-builder/bin/entry build",
18 | "watch": "node ../../build/module-builder/bin/entry watch",
19 | "package": "node ../../build/module-builder/bin/entry package"
20 | },
21 | "dependencies": {}
22 | }
23 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/src/actions/README.md:
--------------------------------------------------------------------------------
1 | ## Actions
2 |
3 | Every time the server is started, Actions that you put in this folder will be copied in the `data/global/actions/delegate-bot-conversation/` folder.
4 | They will overwrite existing files only if they haven't been edited manually.
5 |
6 | Actions added this way will be available on the flow editor using `delegate-bot-conversation/your-action-name`
7 |
8 | Check the documentation for more information about [Actions](https://botpress.com/docs/build/code#actions)
9 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/src/actions/end-delegation.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash')
2 |
3 | /**
4 | * Send an event back to the main bot so it knows to end delegation.
5 | * @End Delegation
6 | * @category Custom
7 | */
8 | const myAction = async () => {
9 | if (event.channel === 'api') {
10 | await bp.events.sendEvent(
11 | bp.IO.Event({
12 | ..._.pick(event, ['channel', 'target', 'threadId', 'botId']),
13 | type: 'custom',
14 | direction: 'outgoing',
15 | payload: {
16 | type: 'end_delegation'
17 | }
18 | })
19 | )
20 | }
21 | }
22 |
23 | return myAction()
24 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/src/backend/api.ts:
--------------------------------------------------------------------------------
1 | import * as sdk from 'botpress/sdk'
2 |
3 | export default async (bp: typeof sdk) => {
4 | /**
5 | * This is an example route to get you started.
6 | * Your API will be available at `http://localhost:3000/api/v1/bots/BOT_NAME/mod/delegate-bot-conversation`
7 | * Just replace BOT_NAME by your bot ID
8 | */
9 | const router = bp.http.createRouterForBot('delegate-bot-conversation')
10 |
11 | // Link to access this route: http://localhost:3000/api/v1/bots/BOT_NAME/mod/delegate-bot-conversation/my-first-route
12 | router.get('/my-first-route', async (req, res) => {
13 | // Since the bot ID is required to access your module,
14 | const botId = req.params.botId
15 |
16 | /**
17 | * This is how you would get your module configuration for a specific bot.
18 | * If there is no configuration for the bot, global config will be used. Check `config.ts` to set your configurations
19 | */
20 | const config = await bp.config.getModuleConfigForBot('delegate-bot-conversation', botId)
21 |
22 | res.sendStatus(200)
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/src/backend/endDelegation.ts:
--------------------------------------------------------------------------------
1 | import * as sdk from 'botpress/sdk'
2 |
3 | export const generateFlow = async (
4 | data: any,
5 | metadata: sdk.FlowGeneratorMetadata
6 | ): Promise => {
7 | return {
8 | transitions: [],
9 | flow: {
10 | nodes: createNodes(data),
11 | catchAll: {
12 | next: []
13 | }
14 | }
15 | }
16 | }
17 |
18 | const createNodes = data => {
19 | const nodes: sdk.SkillFlowNode[] = [
20 | {
21 | name: 'entry',
22 | onEnter: [
23 | {
24 | type: sdk.NodeActionType.RunAction,
25 | name: 'delegate-bot-conversation/end-delegation',
26 | args: data
27 | }
28 | ],
29 | onReceive: null,
30 | next: [{ condition: 'true', node: 'END' }],
31 | type: 'standard'
32 | }
33 | ]
34 | return nodes
35 | }
36 |
37 | export default { generateFlow }
38 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/src/config.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Anything that you would like to make configurable to the bot owner would go in this file.
3 | * Botpress handles itself loading the configuration files.
4 | *
5 | * Bot configuration files will override Global configuration when available:
6 | * For example, `data/bots/MY_BOT/config/delegate-bot-conversation.json` will be used by MY_BOT, while `data/global/config/delegate-bot-conversation.json` will be used for all others
7 | */
8 | export interface Config {
9 | /**
10 | * @default https://botpress.com
11 | */
12 | someEndpoint: string
13 | /**
14 | * @default 10
15 | */
16 | maxMessages: number
17 | }
18 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/src/content-types/README.md:
--------------------------------------------------------------------------------
1 | ## Content-Types
2 |
3 | Every time the server is started, Content-types that you put in this folder will be copied in the `data/global/content-types/delegate-bot-conversation/` folder.
4 | They will overwrite existing files only if they haven't been edited manually.
5 |
6 | The name of your content types must be unique throughout the server
7 |
8 | Check the documentation for more information about [Content Types](http://localhost:3001/docs/next/build/content)
9 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/src/hooks/README.md:
--------------------------------------------------------------------------------
1 | ## Hooks
2 |
3 | Every time the server is started, Hooks that you put in this folder will be copied in the `data/global/hooks/delegate-bot-conversation/HOOK_TYPE/` folder.
4 | They will overwrite existing files only if they haven't been edited manually.
5 |
6 | Check the documentation for more information about [Hooks](https://botpress.com/docs/build/code#hooks)
7 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/src/views/full/endDelegation.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Row, Col, Label, Input } from 'reactstrap'
3 | import { BotpressTooltip } from 'botpress/tooltip'
4 | import Select from 'react-select'
5 |
6 | import _ from 'lodash'
7 |
8 | export class EndDelegation extends React.Component {
9 | componentDidMount() {
10 | this.props.onValidChanged && this.props.onValidChanged(true)
11 | }
12 |
13 | render() {
14 | return (
15 |
16 | Place the skill on any of the sub bot's flows, where you want to end the delegation. There's no configuration
17 | needed, just drag and drop. Important: At least one message must be sent to the sub Bot in order to end
18 | the delegation.
19 |
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/custom modules/delegate-bot-conversation/src/views/full/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | /**
4 | * This is an example on how you may export multiple components from your view
5 | * If your module offers custom Skills, you would export your skill components here
6 | */
7 |
8 | export { Delegate } from './delegate'
9 | export { EndDelegation } from './endDelegation'
10 |
11 | /**
12 | * This file is the full view of your module. It automatically includes heavy dependencies, like react-bootstrap
13 | * If you want to display an interface for your module, export your principal view as "default"
14 | */
15 | export default class MyMainView extends React.Component {
16 | render() {
17 | return
21 | )
22 | }
23 |
24 | export default Composer
25 |
--------------------------------------------------------------------------------
/custom modules/riva/src/views/lite/Recorder/FileUploader.tsx:
--------------------------------------------------------------------------------
1 | import { FileInput } from '@blueprintjs/core'
2 | import React from 'react'
3 |
4 | const FileUploader = props => {
5 | const handleFileUpload = async event => {
6 | if (!event.target.files) {
7 | return
8 | }
9 |
10 | const fd = new FormData()
11 | fd.append('file', event.target.files[0], 'audio.wav')
12 | fd.append('webSessionId', props.store.api.baseUserPayload.webSessionId)
13 | fd.append('conversationId', props.store.currentConversationId)
14 |
15 | await props.store.bp.axios.post('/mod/riva/sendAudio', fd)
16 | }
17 |
18 | return
19 | }
20 |
21 | export default FileUploader
22 |
--------------------------------------------------------------------------------
/custom modules/riva/src/views/lite/Recorder/Timer.tsx:
--------------------------------------------------------------------------------
1 | import round from 'lodash/round'
2 | import React, { useState, useEffect } from 'react'
3 |
4 | let interval
5 |
6 | const Timer = props => {
7 | const [start, setStart] = useState(0)
8 | const [timer, setTimer] = useState(0)
9 |
10 | useEffect(() => {
11 | if (props.isRecording) {
12 | setStart(Date.now())
13 | interval = setInterval(() => {
14 | setTimer(timer => timer + 100)
15 | }, 100)
16 | }
17 | return () => {
18 | clearInterval(interval)
19 | }
20 | }, [props.isRecording])
21 |
22 | const duration = round((Date.now() - start) / 1000, 1).toFixed(2)
23 | return {duration}
24 | }
25 |
26 | export default Timer
27 |
--------------------------------------------------------------------------------
/custom modules/riva/src/views/lite/index.tsx:
--------------------------------------------------------------------------------
1 | import Composer from './Composer'
2 | import Player from './Player'
3 |
4 | export { Composer, Player }
5 |
--------------------------------------------------------------------------------
/custom modules/riva/src/views/lite/utils.tsx:
--------------------------------------------------------------------------------
1 | export const downloadBlob = (function() {
2 | const a = document.createElement('a')
3 | document.body.appendChild(a)
4 | // @ts-ignore
5 | a.style = 'display: none'
6 | return function(url, fileName) {
7 | a.href = url
8 | a.download = fileName
9 | a.click()
10 | window.URL.revokeObjectURL(url)
11 | }
12 | })()
13 |
--------------------------------------------------------------------------------
/custom modules/riva/src/views/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig_view.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom modules/riva/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom modules/starter-module/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | *.tgz
--------------------------------------------------------------------------------
/custom modules/starter-module/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/starter-module/1.png
--------------------------------------------------------------------------------
/custom modules/starter-module/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/starter-module/2.png
--------------------------------------------------------------------------------
/custom modules/starter-module/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/starter-module/3.png
--------------------------------------------------------------------------------
/custom modules/starter-module/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/starter-module/4.png
--------------------------------------------------------------------------------
/custom modules/starter-module/build.extras.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes paths (or glob: https://www.npmjs.com/package/glob) that will be copied in the "dist" folder when the module is built.
3 | *
4 | * Why would you want to put files in the "dist" folder?
5 | * Only files in that folder can be read by Botpress, so if your templates aren't in the "dist" folder, they won't be available.
6 | */
7 | module.exports = {
8 | copyFiles: ['src/bot-templates/**']
9 | }
10 |
--------------------------------------------------------------------------------
/custom modules/starter-module/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starter-module",
3 | "version": "1.0.0",
4 | "description": "Some description on what this module is about",
5 | "private": true,
6 | "main": "dist/backend/index.js",
7 | "author": "Botpress, Inc.",
8 | "license": "AGPL-3.0-only",
9 | "scripts": {
10 | "build": "node ../../build/module-builder/bin/entry build",
11 | "watch": "node ../../build/module-builder/bin/entry watch",
12 | "package": "node ../../build/module-builder/bin/entry package"
13 | },
14 | "dependencies": {}
15 | }
16 |
--------------------------------------------------------------------------------
/custom modules/starter-module/src/actions/README.md:
--------------------------------------------------------------------------------
1 | ## Actions
2 |
3 | Every time the server is started, Actions that you put in this folder will be copied in the `data/global/actions/starter-module/` folder.
4 | They will overwrite existing files only if they haven't been edited manually.
5 |
6 | Actions added this way will be available on the flow editor using `starter-module/your-action-name`
7 |
8 | Check the documentation for more information about [Actions](https://botpress.com/docs/build/code#actions)
9 |
--------------------------------------------------------------------------------
/custom modules/starter-module/src/backend/api.ts:
--------------------------------------------------------------------------------
1 | import * as sdk from 'botpress/sdk'
2 |
3 | export default async (bp: typeof sdk) => {
4 | /**
5 | * This is an example route to get you started.
6 | * Your API will be available at `http://localhost:3000/api/v1/bots/BOT_NAME/mod/starter-module`
7 | * Just replace BOT_NAME by your bot ID
8 | */
9 | const router = bp.http.createRouterForBot('starter-module')
10 |
11 | // Link to access this route: http://localhost:3000/api/v1/bots/BOT_NAME/mod/starter-module/my-first-route
12 | router.get('/my-first-route', async (req, res) => {
13 | // Since the bot ID is required to access your module,
14 | const botId = req.params.botId
15 |
16 | /**
17 | * This is how you would get your module configuration for a specific bot.
18 | * If there is no configuration for the bot, global config will be used. Check `config.ts` to set your configurations
19 | */
20 | const config = await bp.config.getModuleConfigForBot('starter-module', botId)
21 |
22 | res.sendStatus(200)
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/custom modules/starter-module/src/bot-templates/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/starter-module/src/bot-templates/.gitkeep
--------------------------------------------------------------------------------
/custom modules/starter-module/src/config.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Anything that you would like to make configurable to the bot owner would go in this file.
3 | * Botpress handles itself loading the configuration files.
4 | *
5 | * Bot configuration files will override Global configuration when available:
6 | * For example, `data/bots/MY_BOT/config/starter-module.json` will be used by MY_BOT, while `data/global/config/starter-module.json` will be used for all others
7 | */
8 | export interface Config {
9 | /**
10 | * @default https://botpress.com
11 | */
12 | someEndpoint: string
13 | /**
14 | * @default 10
15 | */
16 | maxMessages: number
17 | }
18 |
--------------------------------------------------------------------------------
/custom modules/starter-module/src/content-types/README.md:
--------------------------------------------------------------------------------
1 | ## Content-Types
2 |
3 | Every time the server is started, Content-types that you put in this folder will be copied in the `data/global/content-types/starter-module/` folder.
4 | They will overwrite existing files only if they haven't been edited manually.
5 |
6 | The name of your content types must be unique throughout the server
7 |
8 | Check the documentation for more information about [Content Types](http://localhost:3001/docs/next/build/content)
9 |
--------------------------------------------------------------------------------
/custom modules/starter-module/src/hooks/README.md:
--------------------------------------------------------------------------------
1 | ## Hooks
2 |
3 | Every time the server is started, Hooks that you put in this folder will be copied in the `data/global/hooks/starter-module/HOOK_TYPE/` folder.
4 | They will overwrite existing files only if they haven't been edited manually.
5 |
6 | Check the documentation for more information about [Hooks](https://botpress.com/docs/building-chatbots/developers/hooks)
7 |
--------------------------------------------------------------------------------
/custom modules/starter-module/src/hooks/after_incoming_middleware/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/starter-module/src/hooks/after_incoming_middleware/.gitkeep
--------------------------------------------------------------------------------
/custom modules/starter-module/src/hooks/before_incoming_middleware/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/starter-module/src/hooks/before_incoming_middleware/.gitkeep
--------------------------------------------------------------------------------
/custom modules/starter-module/src/hooks/before_suggestions_election/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/starter-module/src/hooks/before_suggestions_election/.gitkeep
--------------------------------------------------------------------------------
/custom modules/starter-module/src/views/full/example1.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export class ExampleComponent1 extends React.Component {
4 | render() {
5 | return null
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/custom modules/starter-module/src/views/full/example2.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export class ExampleComponent2 extends React.Component {
4 | render() {
5 | return null
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/custom modules/starter-module/src/views/full/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | /**
4 | * This is an example on how you may export multiple components from your view
5 | * If your module offers custom Skills, you would export your skill components here
6 | */
7 | export { Example1 } from './example1'
8 | export { Example2 } from './example2'
9 |
10 | /**
11 | * This file is the full view of your module. It automatically includes heavy dependencies, like react-bootstrap
12 | * If you want to display an interface for your module, export your principal view as "default"
13 | */
14 | export default class MyMainView extends React.Component {
15 | render() {
16 | return
Some interface
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/custom modules/starter-module/src/views/lite/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | /**
4 | * The lite views are meant to be lightweight. They shouldn't include heavy dependencies.
5 | * Common use case is to add custom components on the web chat. It's also possible to share them to other modules
6 | *
7 | * Even if you don't plan to include a lite view, you must include an empty view that returns 'null'
8 | */
9 | export class LiteView extends React.Component {
10 | render() {
11 | return null
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/custom modules/starter-module/src/views/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig_view.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "typeRoots": ["../../node_modules/@types", "lite/typings.d.ts"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/custom modules/starter-module/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom modules/starter-module/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/.gitignore:
--------------------------------------------------------------------------------
1 | /bin
2 | /node_modules
3 | /node_production_modules
4 | /dist
5 | /assets/web/
6 | /assets/config.schema.json
7 | botpress.d.ts
8 | global.d.ts
9 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/README.md:
--------------------------------------------------------------------------------
1 | # Upload Skill
2 |
3 | ## Overview
4 |
5 | This is aBotpress skill to upload media files to a database or AWS S3. Supported platforms are webchat and messenger.
6 |
7 | ## How to use
8 |
9 | 1. Copy the folder `upload-skill` to `modules/upload-skill`
10 | 2. Open a terminal in the folder `modules/upload-skill` and type `yarn && yarn build`
11 | 3. Edit your `botpress.config.json` and add the module location, like below:
12 |
13 | ```js
14 | "modules": [
15 | ...
16 | {
17 | "location": "MODULES_ROOT/upload-skill",
18 | "enabled": true
19 | }
20 | ]
21 | ```
22 |
23 | 4. Edit `data/bots/your_bot_name/bot.config.json` and add the desired content types in the `contentTypes` section.
24 |
25 | ```
26 | "contentTypes": [
27 | "upload_file",
28 | ...
29 | ]
30 | }
31 | ```
32 |
33 | 5. Start Botpress: `yarn start`
34 | 6. Choose any bots in your workspace, then you should see the module in the sidebar !
35 |
36 | ### Avaliable content types
37 |
38 | - `upload_file`
39 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/assets/README.md:
--------------------------------------------------------------------------------
1 | ## Assets
2 |
3 | Every time the server is started, all the content of this folder will be copied in the `assets/modules/complete-module/` folder.
4 | They will overwrite existing files.
5 |
6 | Beware: This is a little different from actions and hooks, since they will be replaced even if you edit them.
7 |
8 | ## WARNING
9 |
10 | Every file in this folder will be publicly available for unauthenticated users.
11 | Only front-end bundles, images, css, etc. should be in this folder.
12 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/assets/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Complete Module Example
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Files in the assets folder are publicly available to anyone.
12 | You can link to other files using relative path: /assets/modules/complete-module
13 | When you change assets in your module folder, Botpress needs to be restarted to take new files
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/upload-skill/assets/logo.png
--------------------------------------------------------------------------------
/custom modules/upload-skill/build.extras.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes paths (or glob: https://www.npmjs.com/package/glob) that will be copied in the "dist" folder when the module is built.
3 | *
4 | * Why would you want to put files in the "dist" folder?
5 | * Only files in that folder can be read by Botpress, so if your templates aren't in the "dist" folder, they won't be available.
6 | */
7 | module.exports = {
8 | copyFiles: ['src/bot-templates/**']
9 | }
10 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "upload-skill",
3 | "version": "1.0.0",
4 | "description": "Uploads files from end users",
5 | "private": true,
6 | "main": "dist/backend/index.js",
7 | "author": "Botpress, Inc.",
8 | "license": "AGPL-3.0-only",
9 | "webpack": {
10 | "externals": {
11 | "reactstrap": "Reactstrap",
12 | "botpress/tooltip": "BotpressTooltip"
13 | }
14 | },
15 | "scripts": {
16 | "build": "node ../../build/module-builder/bin/entry build",
17 | "watch": "node ../../build/module-builder/bin/entry watch",
18 | "package": "node ../../build/module-builder/bin/entry package"
19 | },
20 | "dependencies": {
21 | "aws-sdk": "^2.1122.0",
22 | "express-fileupload": "^1.4.0",
23 | "uuid": "^8.3.2"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/actions/README.md:
--------------------------------------------------------------------------------
1 | ## Actions
2 |
3 | Every time the server is started, Actions that you put in this folder will be copied in the `data/global/actions/complete-module/` folder.
4 | They will overwrite existing files only if they haven't been edited manually.
5 |
6 | Actions added this way will be available on the flow editor using `complete-module/your-action-name`
7 |
8 | Check the documentation for more information about [Actions](https://botpress.com/docs/build/code#actions)
9 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/backend/UploadFile.ts:
--------------------------------------------------------------------------------
1 | import * as sdk from 'botpress/sdk'
2 |
3 | export const generateFlow = async (
4 | data: any,
5 | metadata: sdk.FlowGeneratorMetadata
6 | ): Promise => {
7 | return {
8 | transitions: createTransitions(data),
9 | flow: {
10 | nodes: createNodes(data),
11 | catchAll: {
12 | next: []
13 | }
14 | }
15 | }
16 | }
17 |
18 | const createNodes = data => {
19 | const nodes: sdk.SkillFlowNode[] = [
20 | {
21 | name: 'entry',
22 | onEnter: [
23 | {
24 | type: sdk.NodeActionType.RunAction,
25 | name: 'upload-skill/display-upload-file',
26 | args: data
27 | }
28 | ],
29 | onReceive: [],
30 | next: [{ condition: 'true', node: '#' }]
31 | }
32 | ]
33 | return nodes
34 | }
35 |
36 | const createTransitions = (data): sdk.NodeTransition[] => {
37 | return [
38 | { caption: 'On success', condition: `temp.${data.reference} !== undefined`, node: '' },
39 | { caption: 'On error', condition: `!temp.${data.reference}`, node: '' }
40 | ]
41 | }
42 |
43 | export default { generateFlow }
44 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/backend/db.ts:
--------------------------------------------------------------------------------
1 | import * as sdk from 'botpress/sdk'
2 | import { TABLE_NAME } from '../constants'
3 | import _ from 'lodash'
4 |
5 | export interface IMediaFile {
6 | id: string
7 | name: string
8 | size: Number
9 | data: any
10 | }
11 |
12 | export default class Db {
13 | constructor(private bp: typeof sdk) {}
14 |
15 | getMediaFile = (id: string) => {
16 | return this.bp
17 | .database(TABLE_NAME)
18 | .select('*')
19 | .where({ id })
20 | .first()
21 | }
22 |
23 | insertMediaFile = async (mediaFile: IMediaFile) => {
24 | return await this.bp.database(TABLE_NAME).insert(mediaFile)
25 | }
26 |
27 | clearFiles = async (threadId: string) => {
28 | return await this.bp
29 | .database(TABLE_NAME)
30 | .where({ threadId })
31 | .del()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/backend/migrate.ts:
--------------------------------------------------------------------------------
1 | import * as sdk from 'botpress/sdk'
2 |
3 | import { TABLE_NAME, MODULE_NAME } from '../constants'
4 |
5 | const debug = DEBUG(MODULE_NAME)
6 |
7 | export default async (bp: typeof sdk) => {
8 | await bp.database.createTableIfNotExists(TABLE_NAME, table => {
9 | debug(`Creating database table '${TABLE_NAME}'`)
10 |
11 | table
12 | .string('id')
13 | .primary()
14 | .notNullable()
15 | table.string('threadId').notNullable()
16 | table.string('botId').notNullable()
17 | table.string('name').notNullable()
18 | table.integer('size').notNullable()
19 | table.string('mimetype').notNullable()
20 | table.binary('data').notNullable()
21 | table.timestamps()
22 | })
23 | }
24 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/backend/utils.ts:
--------------------------------------------------------------------------------
1 | export const asBytes = (size: string) => {
2 | if (typeof size === 'number') {
3 | return size
4 | }
5 |
6 | size = typeof size === 'string' ? size : '0'
7 |
8 | const matches = size
9 | .replace(',', '.')
10 | .toLowerCase()
11 | .match(/(\d+\.?\d{0,})\s{0,}(mb|gb|pt|kb|b)?/i)
12 |
13 | if (!matches || !matches.length) {
14 | return 0
15 | }
16 |
17 | /**/ if (matches[2] === 'b') {
18 | return Number(matches[1]) * Math.pow(1024, 0)
19 | } else if (matches[2] === 'kb') {
20 | return Number(matches[1]) * Math.pow(1024, 1)
21 | } else if (matches[2] === 'mb') {
22 | return Number(matches[1]) * Math.pow(1024, 2)
23 | } else if (matches[2] === 'gb') {
24 | return Number(matches[1]) * Math.pow(1024, 3)
25 | } else if (matches[2] === 'tb') {
26 | return Number(matches[1]) * Math.pow(1024, 4)
27 | }
28 |
29 | return Number(matches[1])
30 | }
31 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/constants.ts:
--------------------------------------------------------------------------------
1 | export const MODULE_NAME = 'upload-skill'
2 | export const TABLE_NAME = 'upload_skill_media'
3 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/content-types/README.md:
--------------------------------------------------------------------------------
1 | ## Content-Types
2 |
3 | Every time the server is started, Content-types that you put in this folder will be copied in the `data/global/content-types/complete-module/` folder.
4 | They will overwrite existing files only if they haven't been edited manually.
5 |
6 | The name of your content types must be unique throughout the server
7 |
8 | Check the documentation for more information about [Content Types](http://localhost:3001/docs/next/build/content)
9 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/content-types/_base.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | typingIndicators: {
3 | typing: {
4 | type: 'boolean',
5 | title: 'module.builtin.typingIndicator',
6 | default: true
7 | }
8 | },
9 | useMarkdown: {
10 | markdown: {
11 | type: 'boolean',
12 | title: 'module.builtin.useMarkdown',
13 | default: true,
14 | $help: {
15 | text: 'module.builtin.markdownHelp',
16 | link: 'https://daringfireball.net/projects/markdown/syntax'
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/content-types/_utils.js:
--------------------------------------------------------------------------------
1 | const URL = require('url').URL
2 | const path = require('path')
3 |
4 | function isBpUrl(str) {
5 | const re = /^\/api\/.*\/bots\/.*\/media\/.*/
6 |
7 | return re.test(str)
8 | }
9 |
10 | function isUrl(str) {
11 | try {
12 | new URL(str)
13 | return true
14 | } catch {
15 | return false
16 | }
17 | }
18 |
19 | function formatURL(baseUrl, url) {
20 | if (isBpUrl(url)) {
21 | return `${baseUrl}${url}`
22 | } else {
23 | return url
24 | }
25 | }
26 |
27 | function extractFileName(file) {
28 | let fileName = path.basename(file)
29 | if (fileName.includes('-')) {
30 | fileName = fileName
31 | .split('-')
32 | .slice(1)
33 | .join('-')
34 | }
35 |
36 | return fileName
37 | }
38 |
39 | function extractPayload(data) {
40 | const payload = {
41 | ...data
42 | }
43 |
44 | delete payload.event
45 | delete payload.temp
46 | delete payload.user
47 | delete payload.session
48 | delete payload.bot
49 | delete payload.BOT_URL
50 |
51 | return payload
52 | }
53 |
54 | module.exports = {
55 | formatURL: formatURL,
56 | isUrl: isUrl,
57 | extractPayload: extractPayload,
58 | extractFileName: extractFileName
59 | }
60 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/content-types/upload-skill-file.js:
--------------------------------------------------------------------------------
1 | const base = require('./_base')
2 | const utils = require('./_utils')
3 |
4 | function renderElement(data, channel) {
5 | return {
6 | type: 'custom',
7 | module: 'upload-skill',
8 | component: 'UploadFile',
9 | ...data
10 | }
11 | }
12 |
13 | module.exports = {
14 | id: 'upload_file',
15 | group: 'upload-skill',
16 | title: 'module.builtin.types.file.title',
17 |
18 | jsonSchema: {
19 | description: 'module.builtin.types.file.description',
20 | type: 'object',
21 | required: ['buttonText'],
22 | properties: {
23 | message: {
24 | type: 'string',
25 | title: 'Description message'
26 | },
27 | buttonText: {
28 | type: 'string',
29 | title: 'Text of the file input button'
30 | },
31 | ...base.typingIndicators
32 | }
33 | },
34 |
35 | uiSchema: {
36 | buttonText: {
37 | 'ui:field': 'i18n_field'
38 | },
39 | description: {
40 | 'ui:field': 'i18n_field'
41 | },
42 | message: {
43 | 'ui:field': 'i18n_field'
44 | }
45 | },
46 |
47 | computePreviewText: formData => {
48 | return `File Upload`
49 | },
50 |
51 | renderElement: renderElement
52 | }
53 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/hooks/README.md:
--------------------------------------------------------------------------------
1 | ## Hooks
2 |
3 | Every time the server is started, Hooks that you put in this folder will be copied in the `data/global/hooks/complete-module/HOOK_TYPE/` folder.
4 | They will overwrite existing files only if they haven't been edited manually.
5 |
6 | Check the documentation for more information about [Hooks](https://botpress.com/docs/build/code#hooks)
7 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/hooks/before_conversation_end/clearDb.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios')
2 | bp.http
3 | .getAxiosConfigForBot(event.botId, { localUrl: true })
4 | .then(axiosConfig => {
5 | return axios.post(
6 | '/mod/upload-skill/clear',
7 | {
8 | threadId: event.threadId
9 | },
10 | axiosConfig
11 | )
12 | })
13 | .catch(e => {
14 | bp.logger.warn('Error clearing the db.')
15 | })
16 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/views/full/example1.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export class ExampleComponent1 extends React.Component {
4 | render() {
5 | return null
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/views/full/example2.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export class ExampleComponent2 extends React.Component {
4 | render() {
5 | return null
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/views/full/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | /**
4 | * This is an example on how you may export multiple components from your view
5 | * If your module offers custom Skills, you would export your skill components here
6 | */
7 |
8 | export { UploadFile } from './UploadFile'
9 |
10 | /**
11 | * This file is the full view of your module. It automatically includes heavy dependencies, like react-bootstrap
12 | * If you want to display an interface for your module, export your principal view as "default"
13 | */
14 | export default class MyMainView extends React.Component {
15 | render() {
16 | return
Some interface
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/views/lite/index.jsx:
--------------------------------------------------------------------------------
1 | export { UploadFile } from './components/UploadFile'
2 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/src/views/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig_view.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "typeRoots": ["../../node_modules/@types", "lite/typings.d.ts"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom modules/upload-skill/upload-skill.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/upload-skill/upload-skill.tgz
--------------------------------------------------------------------------------
/custom modules/validate-email/README.md:
--------------------------------------------------------------------------------
1 | # Validate Email Module
2 | Original Author: bassamtantawi-botpress
3 |
4 | ## Overview
5 | Allows access to a new skill to prompt for, receive and validate email address(es).
6 |
7 | ## How to use
8 |
9 | 1. Upload Module
10 | 2. Go to flow Editor
11 | 3. Select Skill
12 | 
13 | 4. Add in the pertinent information for the skill and click insert
14 | 
15 | 5. Set transitions for success (valid email entered) and failure (invalid email entered)
16 |
17 | ### Quick start option
18 | 1. Copy the folder `validate-email` to `modules/validate-email`
19 | 2. Open a terminal in the folder `modules/validate-email` and type `yarn && yarn build`
20 | 3. Edit your `botpress.config.json` and add the module location, like below:
21 |
22 | ```js
23 | "modules": [
24 | ...
25 | {
26 | "location": "MODULES_ROOT/validate-email",
27 | "enabled": true
28 | }
29 | ]
30 | ```
31 | 4. Start Botpress: `yarn start`
32 | 5. Choose any bots in your workspace, then you should see the module in the sidebar
33 |
--------------------------------------------------------------------------------
/custom modules/validate-email/assets/README.md:
--------------------------------------------------------------------------------
1 | ## Assets
2 |
3 | Every time the server is started, all the content of this folder will be copied in the `assets/modules/complete-module/` folder.
4 | They will overwrite existing files.
5 |
6 | Beware: This is a little different from actions and hooks, since they will be replaced even if you edit them.
7 |
8 | ## WARNING
9 |
10 | Every file in this folder will be publicly available for unauthenticated users.
11 | Only front-end bundles, images, css, etc. should be in this folder.
12 |
--------------------------------------------------------------------------------
/custom modules/validate-email/assets/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Validate Email Example
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Files in the assets folder are publicly available to anyone.
12 | You can link to other files using relative path: /assets/modules/complete-module
13 | When you change assets in your module folder, Botpress needs to be restarted to take new files
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/custom modules/validate-email/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/validate-email/assets/logo.png
--------------------------------------------------------------------------------
/custom modules/validate-email/build.extras.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes paths (or glob: https://www.npmjs.com/package/glob) that will be copied in the "dist" folder when the module is built.
3 | *
4 | * Why would you want to put files in the "dist" folder?
5 | * Only files in that folder can be read by Botpress, so if your templates aren't in the "dist" folder, they won't be available.
6 | */
7 | module.exports = {
8 | copyFiles: ['src/bot-templates/**']
9 | }
10 |
--------------------------------------------------------------------------------
/custom modules/validate-email/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "validate-email",
3 | "version": "1.0.0",
4 | "description": "Validate e-mails entered",
5 | "private": true,
6 | "main": "dist/backend/index.js",
7 | "author": "Botpress, Inc.",
8 | "license": "AGPL-3.0-only",
9 | "webpack": {
10 | "externals": {
11 | "reactstrap": "Reactstrap",
12 | "botpress/tooltip": "BotpressTooltip"
13 | }
14 | },
15 | "scripts": {
16 | "build": "node ../../build/module-builder/bin/entry build",
17 | "watch": "node ../../build/module-builder/bin/entry watch",
18 | "package": "node ../../build/module-builder/bin/entry package"
19 | },
20 | "dependencies": {
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/custom modules/validate-email/src/actions/README.md:
--------------------------------------------------------------------------------
1 | ## Actions
2 |
3 | Every time the server is started, Actions that you put in this folder will be copied in the `data/global/actions/validate-email/` folder.
4 | They will overwrite existing files only if they haven't been edited manually.
5 |
6 | Actions added this way will be available on the flow editor using `validate-email/your-action-name`
7 |
8 | Check the documentation for more information about [Actions](https://botpress.com/docs/build/code#actions)
9 |
--------------------------------------------------------------------------------
/custom modules/validate-email/src/actions/validate-email.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Small description of your action
3 | * @title The title displayed in the flow editor
4 | * @category Custom
5 | * @author Your_Name
6 | * @param {string} reference - An example string variable
7 | * @param {any} multiple - Another Example value
8 | */
9 | const myAction = async (reference, multiple) => {
10 |
11 | const result = event.nlu.entities.filter(entity => entity.name === 'email').map(function (entity) {
12 | return entity.data.value;
13 | });
14 |
15 | if (!result.length) {
16 | return
17 | }
18 | if (multiple) {
19 | temp[reference] = result
20 | } else {
21 | temp[reference] = result[0]
22 | }
23 |
24 |
25 |
26 | }
27 |
28 | return myAction(args.reference, args.multiple)
29 |
--------------------------------------------------------------------------------
/custom modules/validate-email/src/backend/utils.ts:
--------------------------------------------------------------------------------
1 | export const asBytes = (size: string) => {
2 | if (typeof size === 'number') {
3 | return size
4 | }
5 |
6 | size = typeof size === 'string' ? size : '0'
7 |
8 | const matches = size
9 | .replace(',', '.')
10 | .toLowerCase()
11 | .match(/(\d+\.?\d{0,})\s{0,}(mb|gb|pt|kb|b)?/i)
12 |
13 | if (!matches || !matches.length) {
14 | return 0
15 | }
16 |
17 | /**/ if (matches[2] === 'b') {
18 | return Number(matches[1]) * Math.pow(1024, 0)
19 | } else if (matches[2] === 'kb') {
20 | return Number(matches[1]) * Math.pow(1024, 1)
21 | } else if (matches[2] === 'mb') {
22 | return Number(matches[1]) * Math.pow(1024, 2)
23 | } else if (matches[2] === 'gb') {
24 | return Number(matches[1]) * Math.pow(1024, 3)
25 | } else if (matches[2] === 'tb') {
26 | return Number(matches[1]) * Math.pow(1024, 4)
27 | }
28 |
29 | return Number(matches[1])
30 | }
31 |
--------------------------------------------------------------------------------
/custom modules/validate-email/src/translations/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "dashboard": "Dashboard",
3 | "export": "Export",
4 | "exportCsv": "Export as CSV",
5 | "exportJson": "Export as JSON",
6 | "filterChannels": "Filter channels",
7 | "fullName": "Validate email",
8 | "title": "Validate email",
9 | "placeholderText": "Dropdown placeholder text",
10 | "typingIndicator": "Show typing indicators",
11 | "useMarkdown": "Use markdown",
12 | "markdownHelp": "Refer to this documentation for markdown syntax: ",
13 | "disableFreeText": "Disable free text",
14 | "description":"A message showing some file with an optional title",
15 | "changeText":"Change the text",
16 | "pickContent":"Pick content",
17 | "reference":"Reference variable in the temp memory where the result will be stored",
18 | "referenceTitle":"Reference",
19 | "multipleTitle":"Multiple",
20 | "train":"Note: The Bot must be trained for this skill to work properly.",
21 | "contentTitle":"Select the content element to be used to request the user's e-mail address",
22 | "multiple":"Multiple emails, if set to true, an array of all detected emails will be assigned to the reference variable instead of a text string"
23 | }
24 |
--------------------------------------------------------------------------------
/custom modules/validate-email/src/translations/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Email validé",
3 | "dashboard": "Planche",
4 | "export": " Exporter",
5 | "exportCsv": "Exporter en CSV",
6 | "exportJson": "Exporter en JSON",
7 | "filterChannels": "Filtrer les canaux",
8 | "fullName": "Email validé",
9 | "placeholderText": "Texte d'espace réservé déroulant",
10 | "typingIndicator": "Afficher les indicateurs de frappe",
11 | "useMarkdown": "utiliser la démarque",
12 | "markdownHelp": "Consultez cette documentation pour la syntaxe Markdown :",
13 | "disableFreeText": "Désactiver le texte libre",
14 | "description":"Un message montrant un fichier avec un titre optionnel",
15 | "changeText":"Modifier le texte",
16 | "pickContent":"Choisir le contenu",
17 | "reference":"Variable de référence dans la mémoire temporaire où le résultat sera stocké",
18 | "referenceTitle":"Référence",
19 | "multipleTitle":"Multiple",
20 | "train":"Note : Le Bot doit être formé pour que cette compétence fonctionne correctement.",
21 | "contentTitle":"Sélectionnez l'élément de contenu à utiliser pour demander l'adresse e-mail de l'utilisateur",
22 | "multiple":"Emails multiples, si cette option est activée, un tableau de tous les emails détectés sera attribué à la variable de référence au lieu d'une chaîne de texte."
23 | }
24 |
--------------------------------------------------------------------------------
/custom modules/validate-email/src/views/full/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | /**
4 | * This is an example on how you may export multiple components from your view
5 | * If your module offers custom Skills, you would export your skill components here
6 | */
7 |
8 | export { InputEmail } from './InputEmail'
9 |
10 | /**
11 | * This file is the full view of your module. It automatically includes heavy dependencies, like react-bootstrap
12 | * If you want to display an interface for your module, export your principal view as "default"
13 | */
14 | export default class MyMainView extends React.Component {
15 | render() {
16 | return
Some interface
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/custom modules/validate-email/src/views/lite/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Index = () => {
4 | return
5 | }
6 |
7 | export default Index
8 |
--------------------------------------------------------------------------------
/custom modules/validate-email/src/views/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig_view.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "typeRoots": ["../../node_modules/@types", "lite/typings.d.ts"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/custom modules/validate-email/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom modules/validate-email/validate-email.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/validate-email/validate-email.tgz
--------------------------------------------------------------------------------
/custom solutions/Add New Element to Channel/before.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Add New Element to Channel/before.png
--------------------------------------------------------------------------------
/custom solutions/Add New Element to Channel/block-kit-builder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Add New Element to Channel/block-kit-builder.png
--------------------------------------------------------------------------------
/custom solutions/Add New Element to Channel/flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Add New Element to Channel/flow.png
--------------------------------------------------------------------------------
/custom solutions/Add New Element to Channel/incoming.js:
--------------------------------------------------------------------------------
1 | if (event.state.user.sendToTemp) {
2 | event.state.temp = { ...event.state.temp, ...event.state.user.sendToTemp }
3 | delete event.state.user.sendToTemp
4 | }
--------------------------------------------------------------------------------
/custom solutions/Add New Element to Channel/on-unmount.js:
--------------------------------------------------------------------------------
1 | bp.http.deleteRouterForBot('slack') // keep this for testing so that you may easily remount bot and see changes
2 |
--------------------------------------------------------------------------------
/custom solutions/Build Custom Modules Using Module Builder/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Build Custom Modules Using Module Builder/1.png
--------------------------------------------------------------------------------
/custom solutions/Build Custom Modules Using Module Builder/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Build Custom Modules Using Module Builder/2.png
--------------------------------------------------------------------------------
/custom solutions/Build Custom Modules Using Module Builder/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Build Custom Modules Using Module Builder/3.png
--------------------------------------------------------------------------------
/custom solutions/Build Custom Modules Using Module Builder/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Build Custom Modules Using Module Builder/4.png
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Build Custom Web Chat Components/1.png
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Build Custom Web Chat Components/2.png
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Build Custom Web Chat Components/3.png
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Build Custom Web Chat Components/4.png
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/custom-web-component-module.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Build Custom Web Chat Components/custom-web-component-module.zip
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/custom-web-component-module/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | *.tgz
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/custom-web-component-module/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "custom-web-component-module",
3 | "version": "1.0.0",
4 | "description": "Some description on what this module is about",
5 | "private": true,
6 | "main": "dist/backend/index.js",
7 | "author": "Botpress, Inc.",
8 | "license": "AGPL-3.0-only",
9 | "scripts": {
10 | "build": "node ../../build/module-builder/bin/entry build",
11 | "watch": "node ../../build/module-builder/bin/entry watch",
12 | "package": "node ../../build/module-builder/bin/entry package"
13 | },
14 | "dependencies": {}
15 | }
16 |
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/custom-web-component-module/src/backend/index.ts:
--------------------------------------------------------------------------------
1 | import * as sdk from 'botpress/sdk'
2 |
3 | const entryPoint: sdk.ModuleEntryPoint = {
4 | definition: {
5 | // This must match the name of your module's folder, and the name in package.json
6 | name: 'custom-web-component-module',
7 | /**
8 | * By default we are using the https://blueprintjs.com/docs/#icons. Use the corresponding name
9 | * Otherwise, create an icon in the assets module in the following format studio_${module.menuIcon}
10 | */
11 | menuIcon: 'flag',
12 | // This is the name of your module which will be displayed in the sidebar
13 | menuText: 'Custom Web Component',
14 | // When set to `true`, the name and icon of your module won't be displayed in the sidebar
15 | noInterface: false,
16 | // The full name is used in other places, for example when displaying bot templates
17 | fullName: 'Custom Web Component',
18 | // Not used anywhere, but should be a link to your website or module repository
19 | homepage: 'https://botpress.com'
20 | }
21 | }
22 |
23 | export default entryPoint
24 |
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/custom-web-component-module/src/config.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Anything that you would like to make configurable to the bot owner would go in this file.
3 | * Botpress handles itself loading the configuration files.
4 | *
5 | * Bot configuration files will override Global configuration when available:
6 | * For example, `data/bots/MY_BOT/config/custom-web-component-module.json` will be used by MY_BOT, while `data/global/config/custom-web-component-module.json` will be used for all others
7 | */
8 | export interface Config {
9 | /**
10 | * @default https://botpress.com
11 | */
12 | someEndpoint: string
13 | /**
14 | * @default 10
15 | */
16 | maxMessages: number
17 | }
18 |
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/custom-web-component-module/src/views/full/index.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is the full view of your module. It automatically includes heavy dependencies, like react-bootstrap
3 | * If you want to display an interface for your module, export your principal view as "default"
4 | */
5 | export default class MyMainView extends React.Component {
6 | render() {
7 | return
Some interface
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/custom-web-component-module/src/views/lite/before_container.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 |
4 | export class MyBeforeContainer extends React.Component {
5 | interval
6 |
7 | componentDidMount() {
8 | // We need to wait for the current conversation to be loaded before cleaning it
9 | this.interval = setInterval(() => {
10 | if(this.props.store.currentConversation) {
11 | this.props.store.clearMessages()
12 | this.interval && clearInterval(this.interval)
13 | }
14 | }, 50)
15 | }
16 |
17 | componentWillUnmount() {
18 | this.interval && clearInterval(this.interval)
19 | }
20 |
21 | render() {
22 | return null
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/custom-web-component-module/src/views/lite/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export { MyBeforeContainer } from './before_container'
4 | /**
5 | * The lite views are meant to be lightweight. They shouldn't include heavy dependencies.
6 | * Common use case is to add custom components on the web chat. It's also possible to share them to other modules
7 | *
8 | * Even if you don't plan to include a lite view, you must include an empty view that returns 'null'
9 | */
10 | export class LiteView extends React.Component {
11 | render() {
12 | return null
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/custom-web-component-module/src/views/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig_view.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "typeRoots": ["../../node_modules/@types", "lite/typings.d.ts"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/custom-web-component-module/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.shared.json",
3 | "compilerOptions": {
4 | "baseUrl": "./"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom solutions/Build Custom Web Chat Components/custom-web-component-module/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 |
--------------------------------------------------------------------------------
/custom solutions/Create Dynamic Choices/Basic/basic-dynamic-choice.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Create and send a basic dynamic single-choice element
3 | * @title Basic Dynamic Choice
4 | * @category Custom
5 | * @author Botpress
6 | * @param {int} quantity - how many choices to render
7 | */
8 |
9 | const axios = require('axios')
10 |
11 | const dynamicSingleChoice = async quantity => {
12 | var numResponse = await axios.get(
13 | `https://www.random.org/integers/?num=${quantity}&min=1&max=100&col=1&base=10&format=plain&rnd=new`
14 | )
15 | var nums = numResponse.data.split('\n')
16 | let choices = []
17 | nums.forEach(num => {
18 | bp.logger.info(num)
19 | choices.push({ title: num, value: num })
20 | })
21 | choices = choices.filter(n => n)
22 | const payloads = await bp.cms.renderElement(
23 | 'builtin_single-choice',
24 | {
25 | text: 'Pick a number!',
26 | typing: true,
27 | choices: choices
28 | },
29 | event
30 | )
31 |
32 | bp.events.replyToEvent(
33 | {
34 | botId: event.botId,
35 | channel: event.channel,
36 | target: event.target,
37 | threadId: event.threadId
38 | },
39 | payloads,
40 | event.id
41 | )
42 | }
43 |
44 | return dynamicSingleChoice(args.quantity)
45 |
--------------------------------------------------------------------------------
/custom solutions/Create Dynamic Choices/readme.md:
--------------------------------------------------------------------------------
1 | # Create Dynamic Choices
2 |
3 | Original author: @laurentlp
4 |
5 | Last updated by @Gordon-BP on 8 June 2022
6 |
7 | ## Overview
8 | This solution contains basic and advanced example actions for dynamically creating and rendering a single choice element.
9 |
10 |
11 |
12 |
13 | ## Use cases:
14 | There are many cases when users need to choose from a set of choices that is not hard-coded. This solution provides basic and advanced solutions.
15 |
16 | ### Basic Example
17 | This example gets a user-specified quantity of random numbers and renders them as buttons.
18 |
19 | ### Advanced Example
20 | This example also renders a user-specified quantity of random numbers, but has additional configurable options and can process and respond to failures and invalid choices.
21 |
22 | ## How to use
23 | Specific instructions are in the examples' respective folders.
24 |
--------------------------------------------------------------------------------
/custom solutions/Create Link to Download Conversation History/readme.md:
--------------------------------------------------------------------------------
1 | # Create Link to Download Conversation History
2 |
3 | This solution will create a API endpoint that users can access to download their conversation history
4 |
5 | # How to use it
6 |
7 | 1. Create the after_server_start hook available in this folder (createDownloadConversationRouter.js) as a global hook in your server, and restart your server
8 | 2. Use the action from the file SendTranscriptDownloadLink.js, also included in this folder, to send the transcript download link to your user.
9 |
--------------------------------------------------------------------------------
/custom solutions/Create URL from User Query/README.md:
--------------------------------------------------------------------------------
1 | This is an example on how to take information provided by the user to generate a link (or button on Messenger) to redirect a user on a website to finish the transaction.
2 |
--------------------------------------------------------------------------------
/custom solutions/Create URL from User Query/actions/utils.js:
--------------------------------------------------------------------------------
1 | const getUserIdFromTarget = async (eventTarget, bp) => {
2 | const getName = async query => {
3 | const result = await bp.database.raw(query)
4 | return result.rows ? result.rows[0].name : result[0].name
5 | }
6 |
7 | const userId = await getName(
8 | `SELECT name FROM msg_senders s, msg_usermap m WHERE s.id = m."senderId" AND m."userId" = '${eventTarget}'`
9 | )
10 |
11 | return userId
12 | }
13 |
14 | const getMessengerConfig = async (botId, bp) => {
15 | const config = await bp.bots.getBotById(botId)
16 | const channels = config.messaging.channels
17 |
18 | if (!channels || !channels.messenger) {
19 | return bp.logger.warn(`Messenger channel is not configured properly`)
20 | }
21 |
22 | const { accessToken } = channels.messenger
23 | return { accessToken }
24 | }
25 |
26 | module.exports = { getUserIdFromTarget, getMessengerConfig }
27 |
--------------------------------------------------------------------------------
/custom solutions/Create logout link/readme.md:
--------------------------------------------------------------------------------
1 | # Create logout link
2 |
3 | This solution will create a url that can be accessed in the user's browser to logout the current session
4 |
5 | # How to install
6 |
7 | 1. Create the after_server_start hook available in this folder (create_logout_router.js) as a global hook in your server, and restart your server
8 |
9 | # How to use it
10 |
11 | When logged in with a user in the admin panel, access the URL [HOST/api/v1/bots/___/mod/logout/logout](http://localhost:3000/api/v1/bots/___/mod/logout/logout) in the browser
12 |
13 | Additionally, you can specify an environment variable called EXPOSED_LOGOUT_RETURN_URL to specify what URL the browser should be redirect to after the logout.
14 |
--------------------------------------------------------------------------------
/custom solutions/Custom Analytics - User Path/Global Hooks/after_bot_unmount/custom-analytics.js:
--------------------------------------------------------------------------------
1 | function hook(bp: typeof sdk, botId: string) {
2 | /** Your code starts below */
3 |
4 | bp.http.deleteRouterForBot("custom-analytics");
5 |
6 | /** Your code ends here */
7 | }
8 |
--------------------------------------------------------------------------------
/custom solutions/Custom Analytics - User Path/Global Hooks/before_incoming_middleware/generate_session_id.js:
--------------------------------------------------------------------------------
1 | function hook(bp: typeof sdk, event: sdk.IO.IncomingEvent) {
2 | /** Your code starts below */
3 |
4 | const uuid = require("uuid");
5 |
6 | if (event.state && event.state.context && event.state.context.currentFlow == undefined) {
7 | const generated = uuid.v4();
8 | event.state.session.generatedSessionId = generated.substring(0, generated.indexOf("-"));
9 | event.setFlag(bp.IO.WellKnownFlags.FORCE_PERSIST_STATE, true);
10 | }
11 |
12 | /** Your code ends here */
13 | }
14 |
--------------------------------------------------------------------------------
/custom solutions/Custom Analytics - User Path/README.md:
--------------------------------------------------------------------------------
1 | ### What it does
2 |
3 | Sends user path telemetry to a new custom table
4 |
5 | ### How to do it
6 |
7 | 1 - Create global hooks (after_bot_mount, after_bot_unmount, before_incoming_middleware)
8 |
9 | 2 - Create bot hooks for bots that you want to track (after_incoming_middleware)
10 |
11 | 3 - Restart server and talk to the bot
12 |
13 | 4 - Check results by quering the database (table 'custom-analytics') or making Curl request bellow (Change [BOTPRESS_URL], [BOT_ID] and [TOKEN]):
14 |
15 | curl --location --request GET '[BOTPRESS_URL]/api/v1/bots/[BOT_ID]/mod/custom-analytics/aggregate' \
16 | --header 'authorization: Bearer [TOKEN]'
17 |
--------------------------------------------------------------------------------
/custom solutions/Custom amount time typing indicator/README.md:
--------------------------------------------------------------------------------
1 | ### What it does
2 |
3 | Send a typing indicator as long as some async function is processing
4 |
5 | ### How to do it
6 |
7 | Use the code from the 'custom_typing_time.js' file to create an action.
8 |
--------------------------------------------------------------------------------
/custom solutions/Custom amount time typing indicator/custom_typing_time.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Small description of your action
3 | * @title The title displayed in the flow editor
4 | * @category Custom
5 | * @author Your_Name
6 | */
7 | const myAction = async () => {
8 | const intervalMs = 500;
9 | const interval = setInterval(() => {
10 | bp.events.replyToEvent(event, [{ type: "typing", value: intervalMs }]);
11 | }, intervalMs);
12 |
13 | // Make your API call (may take between any amount of time to execute)
14 | // ...
15 |
16 | clearInterval(interval);
17 |
18 | // Do something with your response
19 | // ...
20 | };
21 |
22 | return myAction();
23 |
--------------------------------------------------------------------------------
/custom solutions/Custom oAuth with Converse Api/README.md:
--------------------------------------------------------------------------------
1 | ### About
2 |
3 | This is the hook for custom JWT auth with converse API.
4 |
5 | ### How it works
6 |
7 | Whenever you are hitting the Converse Api endpoint you should put your signed token to the metadata object.
8 | The hook checks if the token is correctly signed with secret and authorizes the request and allows talking to the bot.
9 | Otherwise it redirects to the unauthorized flow which you can configure.
10 |
11 | ### How to use
12 | 1. Add hook to the before_incoming_middleware
13 | 2. Create unauthorized flow and change the name of it at the line 59
14 | 3. Add your secret to the env. variable or to the file system, fetch it and set at the line 4.
15 | 4. Hit the endpoint `/api/v1/bots//converse/`
16 | with the following raw body:
17 | ```json
18 | {
19 | "type": "text",
20 | "text": "Google Stock Price",
21 | "metadata": {
22 | "token": "your-token"
23 | }
24 | }
25 | ```
26 |
--------------------------------------------------------------------------------
/custom solutions/Customize Webchat CSS/README.md:
--------------------------------------------------------------------------------
1 | This solution makes it easier to update the webchat's css code directly from within the code editor.
2 |
3 | 1. Copy the code of the .js file in an "after_server_start" hook
4 | 2. In the Code editor, switch to the Advanced editor
5 | 3. Create a file named `custom.css` in your bot's action folder: `bots/BOT_ID/actions/custom.css`
6 | 4. Add your custom css in that file
7 | 5. Right click on the `channel-web.json` config file, then select `Duplicate to current bot`
8 | 6. Edit the newly created file and set `"extraStylesheet": "/api/v1/bots/welcome-bot/mod/public/custom.css"`
9 |
--------------------------------------------------------------------------------
/custom solutions/Customize Webchat CSS/hooks/after_server_start.js:
--------------------------------------------------------------------------------
1 | const hook = async () => {
2 | const routerPublic = bp.http.createRouterForBot('public', { checkAuthentication: false })
3 | routerPublic.get('/custom.css', async (req, res) => {
4 | const { botId } = req.params
5 |
6 | res.setHeader('content-type', 'text/css')
7 |
8 | if (botId === '___') {
9 | return res.send('')
10 | }
11 |
12 | const ghost = bp.ghost.forBot(botId)
13 |
14 | if (await ghost.fileExists('./actions', 'custom.css')) {
15 | const file = await bp.ghost.forBot(req.params.botId).readFileAsString('./actions', 'custom.css')
16 | return res.send(file)
17 | }
18 |
19 | res.send('')
20 | })
21 | }
22 |
23 | return hook()
24 |
--------------------------------------------------------------------------------
/custom solutions/Delegate conversation/reset_conversation.js:
--------------------------------------------------------------------------------
1 | const exec = (botId) => {
2 | if (session.delegation) {
3 | session.delegation[botId] = undefined;
4 | }
5 | };
6 |
7 | return exec(args.botId);
8 |
--------------------------------------------------------------------------------
/custom solutions/Delete Conversations/README.md:
--------------------------------------------------------------------------------
1 | You can use the script below to delete conversation messages older than 7 days
2 |
3 | There are two different versions because of the [messaging split](https://github.com/botpress/messaging/blob/master/docs/messaging/database.md) that happened in 12.26
4 |
5 | ## Before 12.26.0
6 |
7 | ```
8 | DELETE FROM web_messages wm WHERE wm.sent_on <= (SELECT NOW() - INTERVAL '7 DAY');
9 | DELETE FROM web_conversations wc WHERE wc.created_on <= (SELECT NOW() - INTERVAL '7 DAY');
10 | ```
11 |
12 | ## 12.26.0 or After
13 |
14 | ```
15 | DELETE FROM public.msg_messages WHERE "sentOn" <= (SELECT NOW() - INTERVAL '7 DAY');
16 | ```
17 |
--------------------------------------------------------------------------------
/custom solutions/Dynamic Dropdown with Fuzzy Matching/Bot/bot_example-movie-bot_1622830817286.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Dynamic Dropdown with Fuzzy Matching/Bot/bot_example-movie-bot_1622830817286.tgz
--------------------------------------------------------------------------------
/custom solutions/Dynamic Dropdown with Fuzzy Matching/images/0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Dynamic Dropdown with Fuzzy Matching/images/0.png
--------------------------------------------------------------------------------
/custom solutions/Expose public files/hooks/after_server_start/create_files_router.js:
--------------------------------------------------------------------------------
1 | const router = bp.http.createRouterForBot('files', { checkAuthentication: false })
2 | const mime = require('mime-types')
3 |
4 | router.get('/:file', async (req, res) => {
5 | try {
6 | if (!(await bp.ghost.forGlobal().fileExists('/myfiles', req.params.file))) {
7 | res.status('404')
8 | res.send('File not found')
9 | return
10 | }
11 |
12 | const file = await bp.ghost.forGlobal().readFileAsBuffer('/myfiles', req.params.file)
13 | if (req.query.download == 'true') {
14 | res.setHeader('Content-Disposition', 'attachment; filename=' + req.params.file)
15 | }
16 | res.type(mime.lookup(req.params.file) || 'text/plain')
17 | res.end(file)
18 | } catch (e) {
19 | res.status('500')
20 | res.send('Failed to get file: ' + e.message)
21 | }
22 | })
23 |
--------------------------------------------------------------------------------
/custom solutions/Extract Data Without Slots/README.md:
--------------------------------------------------------------------------------
1 | This is an example on how you can use a hook to extract information and fill the entities instead of using slots
2 |
--------------------------------------------------------------------------------
/custom solutions/Extract Dates with Microsoft Text Recognizers/README.md:
--------------------------------------------------------------------------------
1 | ## How to use
2 |
3 | 1. Add the library `@microsoft/recognizers-text-suite` in the Libraries of your bot
4 | 2. Add the hook as an "after_incoming_middleware" hook
5 | 3. The values are stored in `temp.startDateRange` and `temp.endDateRange`
6 |
--------------------------------------------------------------------------------
/custom solutions/Extract and Validate Informations/README.md:
--------------------------------------------------------------------------------
1 | This is a single action which tries to extract 3 different informations.
2 |
3 | ## How to use
4 |
5 | 1. Add the library `google-libphonenumber` on your bot
6 | 2. Add the action on your node
7 |
8 | ## Output
9 |
10 | If a phone number was extracted, then it will be stored in `temp.phoneNumber`
11 | If an e-mail address is extracted, it will be stored in `temp.emailAddress`
12 |
13 | The other example is how to use a custom regex to extract a specific information
14 |
--------------------------------------------------------------------------------
/custom solutions/Extract time ranges/bot_time-range-extraction_1626801742695.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom solutions/Extract time ranges/bot_time-range-extraction_1626801742695.tgz
--------------------------------------------------------------------------------
/custom solutions/Force Minimum Confidence/mininimum-confidence.js:
--------------------------------------------------------------------------------
1 | async function hook() {
2 | if (event.nlu.intent.confidence < 0.75) {
3 | bp.logger.info('Confidence too low, overriding...')
4 | if (suggestions.length > 0) {
5 | const suggestion = suggestions[0]
6 | const decision = suggestion.decision
7 | decision.status = 'dropped'
8 | decision.reason = 'Overridden due to low confidence'
9 | }
10 | event.nlu.intent = { //This will throw a read-only error, but you can still set the property
11 | name: 'none',
12 | confidence: 1,
13 | context: 'global'
14 | }
15 | }
16 | }
17 | return hook()
18 |
--------------------------------------------------------------------------------
/custom solutions/Force Minimum Confidence/readme.md:
--------------------------------------------------------------------------------
1 | # Force Minimum Confidence
2 |
3 | By default, Botpress NLU sets a minimum confidence threshold of 0.5- this hook allows users to raise that for a particular bot.
4 |
5 | **How to use:**
6 |
7 | 1. Create a `before_suggestions_election` hook in your bot.
8 | 2. Copy/paste the contents of `minimum-confidence.js` into the new hook.
9 | 3. Modify the number on line 2 as needed.'
10 |
11 | Note: This hook is only good for _raising_ the minimum confidence threshold. If you want to lower it, set the environmental variable `BP_DECISION_MIN_CONFIDENCE` as needed.
12 |
--------------------------------------------------------------------------------
/custom solutions/Get user profile from Messenger/README.md:
--------------------------------------------------------------------------------
1 | This is a temporary solution until it is better implemented by Messaging. Since it relies on internal methods, it may break in the future.
2 |
3 | ## How to use
4 |
5 | 1. Configure the messenger channel
6 | 2. Add the action in a node
7 |
8 | Once the action is processed, the user's info (full name, timezone, language) will be stored in the user's attributes, and will be available under `event.state.user`.
9 | There is no way to get the user's e-mail address
10 |
--------------------------------------------------------------------------------
/custom solutions/Get user profile from Messenger/actions/utils.js:
--------------------------------------------------------------------------------
1 | const getUserIdFromTarget = async (eventTarget, bp) => {
2 | const getName = async query => {
3 | const result = await bp.database.raw(query)
4 | return result.rows ? result.rows[0].name : result[0].name
5 | }
6 |
7 | const userId = await getName(
8 | `SELECT name FROM msg_senders s, msg_usermap m WHERE s.id = m."senderId" AND m."userId" = '${eventTarget}'`
9 | )
10 |
11 | return userId
12 | }
13 |
14 | const getMessengerConfig = async (botId, bp) => {
15 | const config = await bp.bots.getBotById(botId)
16 | const channels = config.messaging.channels
17 |
18 | if (!channels || !channels.messenger) {
19 | return bp.logger.warn(`Messenger channel is not configured properly`)
20 | }
21 |
22 | const { accessToken } = channels.messenger
23 | return { accessToken }
24 | }
25 |
26 | module.exports = { getUserIdFromTarget, getMessengerConfig }
27 |
--------------------------------------------------------------------------------
/custom solutions/Get user profile from Slack/README.md:
--------------------------------------------------------------------------------
1 | This is an action that can be used to fetch the user's information from the Slack channel.
2 |
3 |
4 | 1. - Configure the additional scopes needed
5 | - Go to your OAuth configuration page: https://api.slack.com/apps/[APP_ID]/oauth
6 | - Add the following scopes in the Scopes section: users.profile:read
7 | - [Important] Click into Reinstall app at the yellow banner and click on Allow
8 | 2. - Create the action
9 | - Create the action using the script 'get-slack-profile.js' from actions folder
10 | - Call the action to find slack details, it will populate the user.slackUser variable
11 | 3. - The action will
12 | - Query the database for the conversation Id (convId) and user Id (userId)
13 | - Get your token from the bot.config.json
14 | - Use that token to query the Slack API and get the profile using the endpoint: https://api.slack.com/methods/users.profile.get
15 | - Use the response to populate 'user.slackUser'
16 |
--------------------------------------------------------------------------------
/custom solutions/Get user profile from Teams/README.md:
--------------------------------------------------------------------------------
1 | This is an action that can be used to fetch the user's e-mail address on MS Teams channel.
2 |
3 | As long as the Teams channel is configured properly, this action "should" work.
4 | It's possible that some URLs might need to be changed depending on the region or information on Azure.
5 |
--------------------------------------------------------------------------------
/custom solutions/Push ConverseAPI messages to messaging/README.md:
--------------------------------------------------------------------------------
1 | # Push ConverseAPI messages to messaging
2 |
3 | Author: David Vitora
4 |
5 | This solution will push ConverseAPI messages to the messaging tables, in the same way that the channel web currently does (12.27.0), to use it, just include the hooks from the "hooks" folder as global hooks.
6 |
--------------------------------------------------------------------------------
/custom solutions/Push ConverseAPI messages to messaging/hooks/after_server_start/createConverseMessagingMap.js:
--------------------------------------------------------------------------------
1 | const LRUCache = require('lru-cache')
2 | const ms = require('ms')
3 |
4 | async function create() {
5 | await bp.database.createTableIfNotExists('converse_user_map', table => {
6 | table.string('botId')
7 | table.string('converseUserId')
8 | table.uuid('conversationId').unique()
9 | table.uuid('userId').unique()
10 | table.primary(['botId', 'converseUserId'])
11 | })
12 |
13 | if (!bp.converseMappingCache) {
14 | bp.converseMappingCache = new LRUCache({ max: 10000, maxAge: ms('5min') })
15 | }
16 | }
17 | return create()
--------------------------------------------------------------------------------
/custom solutions/Record all Misunderstood/README.MD:
--------------------------------------------------------------------------------
1 | # Record all Misunderstood
2 |
3 | Like Q&As utterances are only recorded as misunderstood if they occur outside of a flow. This hook changes that behavior to record every single utterance that causes the matched intent to be `none`.
4 |
5 | Add as an `after-incoming-middleware` hook.
6 |
--------------------------------------------------------------------------------
/custom solutions/Record final node/createDropoutTable.js:
--------------------------------------------------------------------------------
1 | function createTable() {
2 | bp.database.createTableIfNotExists('last_nodes', function(table) {
3 | table
4 | .increments('id')
5 | .unsigned()
6 | .primary()
7 | table.string('session_id')
8 | table.string('last_flow')
9 | table.string('last_node')
10 | })
11 | }
12 | createTable()
13 |
--------------------------------------------------------------------------------
/custom solutions/Record final node/record-final-node.js:
--------------------------------------------------------------------------------
1 | async function record() {
2 | bp.logger.info('attempting data insert...')
3 | try {
4 | await bp.database.insertAndRetrieve('last_nodes', {
5 | session_id: event.threadId,
6 | last_flow: event.state.context.currentFlow,
7 | last_node: event.state.context.currentNode
8 | })
9 | bp.logger.info('Last node insert successful')
10 | } catch (error) {
11 | bp.logger.info(error)
12 | }
13 | }
14 |
15 | return record()
16 |
--------------------------------------------------------------------------------
/custom solutions/Redirect All Qnas/README.md:
--------------------------------------------------------------------------------
1 | ## Redirect All Q&As
2 |
3 | This hook will enable global redirection for all Q&As in a bot. To use:
4 | 1. Add this hook as a `before_suggestions_election` hook
5 | 2. Add the flow and node where you want to redirect users in lines 7 & 8
6 |
7 | 3. Make sure no Q&As are set to redirect
8 |
9 |
--------------------------------------------------------------------------------
/custom solutions/Redirect All Qnas/Redirect_All_Qnas.js:
--------------------------------------------------------------------------------
1 | // Define the flow and node to redirect to after the Q&A is answered
2 | const nextNode = {
3 | type: 'redirect',
4 | flow: 'main.flow.json',
5 | node: 'entry'
6 | }
7 |
8 | async function hook() {
9 | if (!suggestions.length || !suggestions[0].decision || event.type !== 'text') {
10 | return
11 | }
12 |
13 | const suggestion = suggestions[0]
14 | const decision = suggestion.decision
15 |
16 | if (decision.status === 'elected' && suggestion.source === 'qna') {
17 | bp.logger.info('adding redirect....')
18 | suggestion.payloads = [...suggestion.payloads, nextNode]
19 | }
20 | }
21 | return hook()
22 |
--------------------------------------------------------------------------------
/custom solutions/Redirect user during conversation if URL has a parameter/inject_variable_from_webchat.js:
--------------------------------------------------------------------------------
1 | // We just care if the event comes from web and has the type custom-inject-variable
2 | if (event.channel == 'web' && event.type == 'custom-inject-variable') {
3 | // We add this to prevent the bot from being triggered
4 | event.setFlag(bp.IO.WellKnownFlags.SKIP_DIALOG_ENGINE, true)
5 |
6 | // We add this in order save state variables when SKIP_DIALOG_ENGINE is set
7 | event.setFlag(bp.IO.WellKnownFlags.FORCE_PERSIST_STATE, true)
8 |
9 | // if we don't have a payload in the event, prevent further
10 | // execution
11 | if (!event.payload.payload || !event.payload.payload.payload) {
12 | return
13 | }
14 |
15 | // We save a temporary variable for each property from the payload object
16 | for (const key of Object.keys(event.payload.payload.payload)) {
17 | event.state.temp[key] = event.payload.payload.payload[key]
18 | }
19 | }
--------------------------------------------------------------------------------
/custom solutions/Redirect user during conversation if URL has a parameter/inject_variable_template_v1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
There should be a black button on the right bottom corner
8 |
9 |
10 |
11 |
12 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/custom solutions/Redirection on session reset/README.md:
--------------------------------------------------------------------------------
1 | ### What it does
2 |
3 | Redirect to the beginning of a flow and process the first node when a user resets his session
4 |
5 | ### How to do it
6 |
7 | Use the code from the 'redirect_reset_session.js' file to create a before_incoming_middleware hook
8 |
--------------------------------------------------------------------------------
/custom solutions/Redirection on session reset/redirect_reset_session.js:
--------------------------------------------------------------------------------
1 | async function redirect() {
2 | if (event.type === 'session_reset') {
3 | const sessionId = bp.dialog.createId(event)
4 | await bp.dialog.jumpTo(sessionId, event, 'main.flow.json', 'entry')
5 |
6 | const newEvent = bp.IO.Event({
7 | type: 'any',
8 | payload: {},
9 | direction: event.direction,
10 | channel: event.channel,
11 | target: event.target,
12 | botId: event.botId,
13 | threadId: event.threadId
14 | })
15 |
16 | newEvent.debugger = true
17 | await bp.events.sendEvent(newEvent)
18 | }
19 | }
20 |
21 | return redirect()
--------------------------------------------------------------------------------
/custom solutions/Run Script 30 Minutes Before End of Day/readme.md:
--------------------------------------------------------------------------------
1 | # Routine using hooks
2 |
3 | This After Server Start hook creates a routine that will run every day, 30 minutes before the end of the day, just once. It will consider other Botpress nodes to avoid duplication (using a lock).
4 |
5 | **To use:**
6 |
7 | 1. Create an after_server_start hook using the script attached (routine.js)
8 | 2. Modify the runTask function with your code to create the desired task
9 | 3. Restart server
10 |
--------------------------------------------------------------------------------
/custom solutions/Send Specific QnA to a User/README.md:
--------------------------------------------------------------------------------
1 | This action forces a specific QnA to be sent a user, while ignoring any automatically detected QnAs.
2 |
3 | ## How to Use
4 |
5 | 1. Create your QnAs as normal
6 | 2. Make a choice skill where the values are the IDs for the QnAs you want to trigger
7 |
8 |
9 |
10 | 3. You can easily find the QnA ID by exporting the QnAs as JSON. Just remember to add `__qna__` to the start of the ID
11 |
12 |
13 |
14 | 4. Fill in the parameters:
15 |
16 | **qnaId**: If you're using this after a choice skill, set it to `{{event.payload.payload}}`. Otherwise set it to the Qna ID from step 3
17 |
18 | **collectFeedback**: true to show the thumbs up/down icons, false to hide them
19 |
20 | **followRedirect**: true to follow the qna redirect (if applicable), false to use the node's transition as a follow-up.
21 |
22 |
23 |
--------------------------------------------------------------------------------
/custom solutions/Send adaptive cards using teams/README.md:
--------------------------------------------------------------------------------
1 | This is an action that can be used to send adaptive cards with teams
2 |
3 | As long as the Teams channel is configured properly, this action "should" work.
4 | It's possible that some URLs might need to be changed depending on the region or information on Azure.
5 |
6 | The adaptive cards example was taken from: https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/cards/cards-reference#adaptive-card
7 |
8 | 
9 |
10 | 
11 |
--------------------------------------------------------------------------------
/custom solutions/Set Nested Value/Readme.md:
--------------------------------------------------------------------------------
1 | # Set Nested Value
2 |
3 | Original author: @ptrckbp
4 |
5 | Last updated by @ptrckbp on 4 June 2022
6 |
7 | ## Overview
8 | This custom action extends the builtin `set-variable` action to enable developers to add and modify nth-level fields within an object.
9 |
10 | ## Use cases:
11 | Imagine you have an object like:
12 | ```
13 | temp:{
14 | data:{
15 | resources:{
16 | field1: "Foo"
17 | }
18 | }
19 | }
20 | ```
21 |
22 | and you want to change it to:
23 | ```
24 | temp:{
25 | data:{
26 | resources:{
27 | field1: "Bar"
28 | }
29 | }
30 | }
31 | ```
32 |
33 | Just run this action with the following parameters:
34 |
35 |
36 |
37 | ## How to use
38 | 1. Create a node and add this action to it.
39 | 2. Fill out parameters as desired:
40 |
41 | **Scope:** Either `user`, `session`, or `temp`
42 |
43 | **Key:** The path to the field, separated by periods (".")
44 |
45 | **Value:** The end value to set the field to
46 |
47 | ### Note:
48 | This action will create any fields or object levels that don't exist. If no value is specified, the field will be undefined.
49 |
--------------------------------------------------------------------------------
/custom solutions/Set Nested Value/set-nested-value.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Sets the value of an Nth level field on an object.
3 | * @title Set nested value
4 | * @category Custom
5 | * @author Botpress
6 | * @param {string} scope - either temp, session, or user
7 | * @param {string} key - The field's directory, separated with dots like "data.response.field1"
8 | * @param {any} value - What to set the final value to
9 | */
10 | const setNestedValue = (scope, key, value) => {
11 | const inputKeys = key.split('.')
12 | let movingKey = event.state[scope]
13 | for (let i = 0; i < inputKeys.length; i++) {
14 | const currentKey = inputKeys[i]
15 |
16 | if (i === inputKeys.length - 1) {
17 | console.log(value)
18 | movingKey[currentKey] = value
19 | return
20 | }
21 | console.log(currentKey)
22 | if (!movingKey[currentKey]) {
23 | movingKey[currentKey] = {}
24 | }
25 |
26 | movingKey = movingKey[currentKey]
27 | }
28 | }
29 | return setNestedValue(args.scope, args.key, args.value)
30 |
--------------------------------------------------------------------------------
/custom solutions/Standardize Words Hook/README.md:
--------------------------------------------------------------------------------
1 | ### What it does
2 |
3 | It is a "before-incoming-middleware" hook that will changes words to standardize it
4 |
5 | Example: change "covid-19" or "covid 19" to "covid"
6 |
7 | ### How to do it
8 |
9 | 1 - Create a "before-incoming-middleware" hook using the script "standardise_word.js"
10 |
11 | 2 - Change words in the "maps" object as needed
12 |
--------------------------------------------------------------------------------
/custom solutions/Standardize Words Hook/standardise_word.js:
--------------------------------------------------------------------------------
1 | function hook(bp: typeof sdk, event: sdk.IO.IncomingEvent) {
2 | /** Your code starts below */
3 | if (event.type !== "text") {
4 | return;
5 | }
6 | const _ = require("lodash");
7 | const maps = {
8 | covid: [
9 | /\bcoronavirus\b/gi,
10 | /\bcorona\b/gi,
11 | /\bcoronnavirus\b/gi,
12 | /\bcarona\b/gi,
13 | /\bcovid-19\b/gi,
14 | /\bcovid19\b/gi,
15 | /\bcovid 19\b/gi,
16 | /\sars-cov-2\b/gi,
17 | /\pandemic\b/gi,
18 | ],
19 | };
20 | let phrase = event.preview;
21 | _.entries(maps).forEach(([key, syn]) => {
22 | syn.forEach((e) => {
23 | phrase = phrase.toLowerCase().replace(e, key);
24 | });
25 | });
26 | event.preview = phrase;
27 | event.payload.text = phrase;
28 | /** Your code ends here */
29 | }
30 |
--------------------------------------------------------------------------------
/custom solutions/Success Telemetry/Subviews/README.md:
--------------------------------------------------------------------------------
1 | - Each folder is from a metric, each subfolder has an hook which will create a desired subview in a metric type
2 | - The hook can be created as an 'after_server_start' hook (recommended)
3 | - After creating/modifying the hook, restart the server.
4 |
--------------------------------------------------------------------------------
/custom solutions/Success Telemetry/Subviews/solutions/download_csv_button/Readme.md:
--------------------------------------------------------------------------------
1 | This will create a button to download a CSV file with the results
2 |
3 | # Example
4 | 
5 | 
6 | 
7 |
8 |
9 | OBS: make sure to use the same file name for the hook, since hooks execute in alphabetical order and we need the zz_ to make sure that all metrics are registered before.
10 |
11 |
--------------------------------------------------------------------------------
/custom solutions/Success Telemetry/Subviews/workflow/Separated Workflow/Readme.md:
--------------------------------------------------------------------------------
1 | # Example
2 | 
3 |
--------------------------------------------------------------------------------
/custom solutions/Third party translations/bot.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "translation": {
3 | "google": {
4 | "endpoint": "https://translation.googleapis.com/language/translate/v2",
5 | "token": "YOUR_API_TOKEN"
6 | },
7 | "deepl": {
8 | "endpoint": "https://api-free.deepl.com/v2/translate",
9 | "token": "YOUR_API_TOKEN"
10 | },
11 | "huggingface_fr_en": {
12 | "endpoint": "https://api-inference.huggingface.co/models/Helsinki-NLP/opus-mt-fr-en",
13 | "token": "YOUR_API_TOKEN"
14 | },
15 | "huggingface_en_fr":
16 | {
17 | "endpoint": "https://api-inference.huggingface.co/models/Helsinki-NLP/opus-mt-en-fr",
18 | "token": "YOUR_API_TOKEN"
19 | }
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/custom solutions/UserDropout/DataInsert.js:
--------------------------------------------------------------------------------
1 | function action(bp: typeof sdk, event: sdk.IO.IncomingEvent, args: any, { user, temp, session } = event.state) {
2 | /** Your code starts below */
3 |
4 | /**
5 | * Small description of your action
6 | * @title The title displayed in the flow editor
7 | * @category Custom
8 | * @author Mohsen
9 | * @param {string} first - User first name
10 | * @param {string} last - User last name
11 | * @param {string} Node - Current node in the flow
12 | */
13 |
14 | async function myAction(first, last, Node) {
15 | await bp.database.insertAndRetrieve('Example_DB', {
16 | UserName: first,
17 | LastName: last,
18 | CurrentNode: Node
19 |
20 | //example of what could be inserted in the data table, entries will depend on what is being tracked.
21 | })
22 | }
23 | return myAction(args.first, args.last, args.Node)
24 |
25 | /** Your code ends here */
26 | }
27 |
--------------------------------------------------------------------------------
/custom solutions/UserDropout/Hooks/TableCreation.js:
--------------------------------------------------------------------------------
1 | function hook(bp: typeof sdk) {
2 | /** Your code starts below */
3 |
4 |
5 | function CreateTable(){
6 | const tableName = 'Example_DB'
7 | bp.database.CreateTableIfNotExists(tableName, function(table){
8 | table.increment('id').primary
9 | table.string('FirstName')
10 | table.string('LastName')
11 | table.string('TargetUsers')
12 | })
13 | }
14 | CreateTable()
15 |
16 | /** Your code ends here */
17 | }
18 |
--------------------------------------------------------------------------------
/custom solutions/UserDropout/Hooks/TargetUserEntry.js:
--------------------------------------------------------------------------------
1 | function hook(bp: typeof sdk, event: sdk.IO.IncomingEvent) {
2 | /** Your code starts below */
3 |
4 | //BEFORE INCOMING MIDDLEWARE
5 |
6 | const DATABASE_TABLE = 'Example_DB'
7 |
8 | if(event.type === 'visit'){
9 | bp.logger.info('User Visit' , event)
10 | bp.database.insertAndRetrieve(DATABASE_TABLE, {
11 | TargetUsers: event.target
12 | })
13 | bp.logger.info('Target User has been inserte')
14 | } else {
15 | bp.database(DATABASE_TABLE).delete({
16 | TargetUsers: event.target
17 | //Deleting the Users that actually carried on with the conversation
18 | })
19 | bp.logger.info('User that is not registered as an event.target === visited has been deleted')
20 | }
21 |
22 | /** Your code ends here */
23 | }
24 |
--------------------------------------------------------------------------------
/custom solutions/UserDropout/NodeUpdate.js:
--------------------------------------------------------------------------------
1 | function action(bp: typeof sdk, event: sdk.IO.IncomingEvent, args: any, { user, temp, session } = event.state) {
2 | /** Your code starts below */
3 |
4 | const tableName = 'Example_DB'
5 | this.knex = bp.database
6 |
7 | /**
8 | * Small description of your action
9 | * @title The title displayed in the flow editor
10 | * @category Custom
11 | * @author Your_Name
12 | * @param {string} UserName - Nmae of the User
13 | * @param {any} columnSelector - Column you wish to change
14 | * @param {any} currentNode - The new Value to be stored in that column
15 | */
16 |
17 | const myAction = async (UserName, columnSelector,currentNode) => {
18 | await this.knex(tableName)
19 | .where('Name' , ' = ' , UserName)
20 | //the where condition that allows you to access the desried data entry.
21 | .update(columnSelector,currentNode)
22 | //The update will take in the column chosen, in this case the column that stores the node.
23 | //Second parameter will be the value you want to change it to, this should be the current node.
24 | }
25 |
26 | return myAction(args.UserName, args.columnSelector , args.currentNode)
27 |
28 | /** Your code ends here */
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/custom solutions/Working with Dates/README.md:
--------------------------------------------------------------------------------
1 | Two methods for date manipulation.
2 |
3 | 1. Date Parser
4 | This action takes a parameter (either the user's text, another variable or a date), then it formats it in the requested format, and stores the result in the configured variable.
5 |
6 | If format is left empty, it will return a JS date object
7 | When format is equal to "moment", it will return a moment object
8 | Any other value for format will return the date formatted to the specified format (ex: YYYY-MM-DD for 2022-03-28)
9 |
10 | 2. Date Compare
11 |
12 | This action takes a parameter (either a string, a date object or a moment object) then returns whether the first date `isBefore`, `isAfter` or `isEqual` the second one.
13 |
--------------------------------------------------------------------------------
/custom solutions/Working with Dates/actions/date-compare.js:
--------------------------------------------------------------------------------
1 | const moment = require('moment')
2 |
3 | /**
4 | * Compare two dates.
5 | * The result is stored in temp.dateComparison and can have 3 different values: isBefore, isAfter, isEqual
6 | * @title Date comparison
7 | * @category Date
8 | * @author Botpress
9 | * @param {string|Date|moment} date1 The first date to compare
10 | * @param {string|Date|moment} date2 The date to compare it to
11 | * @param {string} [output=dateComparison] Name of the temporary variable where the result will be saved
12 | */
13 | const compareDates = async (rawDate1, rawDate2, output) => {
14 | if (!rawDate1 || !rawDate2) {
15 | return bp.logger.warn(`Both dates must be configured`)
16 | }
17 |
18 | const date1 = moment(rawDate1)
19 | const date2 = moment(rawDate2)
20 |
21 | if (date1.isBefore(date2)) {
22 | temp[output] = 'isBefore'
23 | } else if (date1.isAfter(date2)) {
24 | temp[output] = 'isAfter'
25 | } else {
26 | temp[output] = 'isEqual'
27 | }
28 | }
29 |
30 | return compareDates(args.date1, args.date2, args.output)
31 |
--------------------------------------------------------------------------------
/custom solutions/Working with Dates/actions/date-parser.js:
--------------------------------------------------------------------------------
1 | const moment = require('moment')
2 |
3 | /**
4 | * Generic date parser. Leave the date empty to use today's date.
5 | * If the format is empty, it will return a normal Date object.
6 | * Set the format to 'moment' to return the moment object
7 | * Value is stored in {{temp.parsedDate}} by default
8 | * @title Date parser
9 | * @category Date
10 | * @author Botpress
11 | * @param {string|Date|moment} date The date to process (can be a string or a js Date object)
12 | * @param {string} [format=YYYY-MM-DD] Format of the date to output.
13 | * @param {string} [output=parsedDate] Name of the temporary variable where the result will be saved
14 | *
15 | */
16 | const parseDate = async (rawDate, format, output) => {
17 | const date = moment(!rawDate ? undefined : rawDate)
18 |
19 | if (!format) {
20 | temp[output] = date.toDate()
21 | } else if (format === 'moment') {
22 | temp[output]
23 | } else {
24 | temp[output] = date.format(format)
25 | }
26 | }
27 |
28 | return parseDate(args.date, args.format, args.output)
29 |
--------------------------------------------------------------------------------
/custom solutions/[HITL-Next] Jump to flow after resolving /README.md:
--------------------------------------------------------------------------------
1 | # [HITL-Next] Jump to flow after resolving
2 |
3 | Original author: @davidvitora
4 |
5 | Last updated by @davidvitora on Jul 11 2022
6 |
7 | ## Overview
8 |
9 | Use this solution if you want your agent using HITL-Next to specify a flow to send the user after the agent resolves the conversation
10 |
11 | ## How to Install
12 |
13 | Simply add the two hooks available in this folder
14 |
15 | ## How to use it
16 |
17 | To specify the flow, during a conversation the agent must type the following command in the chatbox
18 |
19 | [cmd]sendTo:FLOW_NAME:NODE_NAME
20 |
21 | example: [cmd]sendTo:flow2:entry
22 |
23 | If you want to jump to the start node, you can simply use [cmd]sendTo:FLOW_NAME
24 |
25 | example: [cmd]sendTo:flow2
26 |
27 |
--------------------------------------------------------------------------------
/custom solutions/[HITL-Next] Jump to flow after resolving /hooks/before_incoming_middleware/sendTo-in.js:
--------------------------------------------------------------------------------
1 | // Jump to specified flow/node if any
2 | async function send() {
3 | const { sendTo } = event.state.session
4 | if (sendTo) {
5 | const { flowName, nodeName } = sendTo
6 | event.state.session.sendTo = undefined
7 | await bp.dialog.jumpTo(event.threadId, event, flowName, nodeName)
8 | event.setFlag(bp.IO.WellKnownFlags.FORCE_PERSIST_STATE, true)
9 | }
10 | }
11 |
12 | // resolved event from HITL-Next
13 | if (event.type == 'hitlnext' && event.payload.exitType == 'handoffResolved') {
14 | return send()
15 | }
16 |
17 | // Internal event to register the sendTo action
18 | else if (event.type == 'sendTo') {
19 | const { flowName, nodeName } = event.payload
20 | if (flowName) {
21 | event.state.session.sendTo = { flowName, nodeName }
22 | }
23 | event.setFlag(bp.IO.WellKnownFlags.FORCE_PERSIST_STATE, true)
24 | event.setFlag(bp.IO.WellKnownFlags.SKIP_DIALOG_ENGINE, true)
25 | event.setFlag(bp.IO.WellKnownFlags.SKIP_NATIVE_NLU, true)
26 | event.setFlag(bp.IO.WellKnownFlags.SKIP_QNA_PROCESSING, true)
27 | }
28 |
--------------------------------------------------------------------------------
/custom solutions/[HITL-Next] Jump to flow after resolving /hooks/before_outgoing_middleware/sendTo-out.js:
--------------------------------------------------------------------------------
1 | const text = event.payload && event.payload.text
2 | const cmd = text && text.startsWith('[cmd]') && text.substring(5)
3 |
4 | if (cmd) {
5 | const args = cmd.split(':')
6 | const [cmdName, flowName, nodeName] = args
7 |
8 | if (cmdName == 'sendTo') {
9 | if (flowName) {
10 | const { messageId, botId, channel, target, threadId } = event
11 | const internalEvent = bp.IO.Event({
12 | messageId,
13 | botId,
14 | channel,
15 | direction: 'incoming',
16 | payload: { flowName, nodeName },
17 | target,
18 | threadId,
19 | type: 'sendTo'
20 | })
21 | // Send internal incoming event to register desire to jump
22 | bp.events.sendEvent(internalEvent)
23 | }
24 | }
25 | // prevent command outgoing event from being sent to the user
26 | event.channel = 'invalid'
27 | event.setFlag(bp.IO.WellKnownFlags.FORCE_PERSIST_STATE, true)
28 | event.setFlag(bp.IO.WellKnownFlags.SKIP_DIALOG_ENGINE, true)
29 | event.setFlag(bp.IO.WellKnownFlags.SKIP_NATIVE_NLU, true)
30 | event.setFlag(bp.IO.WellKnownFlags.SKIP_QNA_PROCESSING, true)
31 | }
32 |
--------------------------------------------------------------------------------
/custom_tools/NLU_Testing/.env:
--------------------------------------------------------------------------------
1 |
2 | # Endpoint is where your bot is exposed
3 | ENDPOINT=http://localhost:3000
4 |
5 | # These are the credentials used for making API calls
6 | EMAIL=
7 | PASSWORD=
8 |
9 | # This is the ID of the bot you're testing
10 | BOT_ID=
11 |
12 | #Whether or not to record extracted entities in the test set
13 | #Should be either TRUE or FALSE
14 | EXTRACT_ENTITIES=TRUE
15 |
16 | #Whether or not to extract confidence
17 | #Should be either TRUE or FALSE
18 | EXTRACT_CONFIDENCE=TRUE
19 |
20 | # This is your test dataset. Must be a CSV file with two columns like:
21 | # Utterance | Expected
22 | TEST_PATH=
23 |
24 | # This is the name all the results data will be grouped under
25 | COL_NAME=Actual
26 |
27 | # This is your results file. Make it separate if you want, or same as testPath to keep it all in the same file
28 | RESULTS_PATH=
29 |
30 | # This is the folder where all the Q&A .json files are
31 | QNA_FOLDER_PATH=/data/bots/${BOT_ID}/qna/
32 |
33 | #Whether or not to modify the Q&A json files to remove redirects & content elements.
34 | #Should be either TRUE or FALSE
35 | JSON_MOD=TRUE
36 |
37 |
--------------------------------------------------------------------------------
/custom_tools/NLU_Testing/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy>=1.22.2
2 | pandas>=1.2.5
3 | python-dotenv>=0.19.2
4 | requests>=2.22.0
5 | scikit_learn>=1.0.2
6 | matplotlib>=3.5.0
7 |
--------------------------------------------------------------------------------
/custom_tools/bots/export_flow_bot/README.md:
--------------------------------------------------------------------------------
1 | # Export flow bot
2 |
3 | This bot can help you copy reused flows to secondary bots
4 |
5 | ## how to use it?
6 |
7 | 1 - Import the bot_helper-bot.tgz file as a bot inside of botpress
8 |
9 | 
10 |
11 |
12 | 2 - Edit the bot.config.json file from the helper bot in the code editor to include your bots
13 |
14 | 
15 |
16 | 
17 |
18 | 3 - Talk with the helper bot
19 |
20 | 
21 |
22 |
23 |
--------------------------------------------------------------------------------
/custom_tools/bots/export_flow_bot/bot_helper-bot.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom_tools/bots/export_flow_bot/bot_helper-bot.tgz
--------------------------------------------------------------------------------
/custom_tools/bp_image_builder/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | bin/*
4 | out/
--------------------------------------------------------------------------------
/custom_tools/bp_image_builder/Dockerfile:
--------------------------------------------------------------------------------
1 |
2 | FROM node:14-alpine
3 |
4 | WORKDIR /bp_image_builder
5 |
6 | ADD package.json yarn.lock ./
7 | RUN yarn install
8 |
9 | ADD tsconfig.json ./
10 | ADD src ./
11 |
12 | RUN yarn build
13 |
14 | CMD [ "yarn", "start" ]
--------------------------------------------------------------------------------
/custom_tools/bp_image_builder/custom_buildImage_config/README.md:
--------------------------------------------------------------------------------
1 | # Use custom buildImage Config
2 |
3 | If you create a buildImage.config.js file in the same folder as the binary and return a object or function which creates an object, its possible to customize options used in the ImageBuild call.
4 |
5 | Look at the official documentation for possible arguments: https://docs.docker.com/engine/api/v1.37/#operation/ImageBuild
6 |
7 | Use [buildImage.config.js](https://github.com/botpress/solutions/blob/master/custom_tools/bp_image_builder/custom_buildImage_config/buildImage.config.js) as template
8 |
--------------------------------------------------------------------------------
/custom_tools/bp_image_builder/custom_buildImage_config/buildImage.config.js:
--------------------------------------------------------------------------------
1 | module.exports = async () => {
2 | return {
3 | nocache: true,
4 | networkmode: 'host', // recommended as default, so building also works for local deployments
5 | // for all other possible values, see: https://docs.docker.com/engine/api/v1.37/#operation/ImageBuild
6 | }
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/custom_tools/bp_image_builder/custom_dockerfile/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM {{imageTag}}
2 | COPY ./data /botpress/data
3 | ARG BUILD_TOKEN={{BUILD_TOKEN}}
4 | ARG BUILD_ORIGIN_HOST={{BUILD_ORIGIN_HOST}}
5 | RUN echo This is a custom Dockerfile, token is $BUILD_TOKEN and host is $BUILD_ORIGIN_HOST;
6 |
7 | RUN mkdir /botpress/docker_hooks
8 | RUN mkdir /botpress/extra_files
9 |
10 | #Uncomment the line below to copy docker hooks
11 | #COPY ./docker_hooks/ /botpress/docker_hooks/
12 |
13 | #Uncomment the line below to copy custom modules and extract it
14 | #COPY ./custom_modules/ /botpress/modules/
15 | #RUN ./bp extract
16 |
17 | #Uncomment the line below to copy extra files
18 | #COPY ./extra_files/ /botpress/extra_files/
19 |
20 | #Uncomment both lines below to run the after_build docker hook
21 | #RUN chmod -R 777 /botpress/docker_hooks/*
22 | #RUN /botpress/docker_hooks/after_build.sh
23 |
24 | WORKDIR /botpress
25 | CMD ./duckling & ./bp
26 |
27 |
--------------------------------------------------------------------------------
/custom_tools/bp_image_builder/docker_hooks/examples/Download train data from origin/README.md:
--------------------------------------------------------------------------------
1 | # Download train data from origin
2 |
3 | When you bake your image using data from an origin server, your training data is not copied, which means that once your image is running, you will have to train your bot again.
4 |
5 | This after_build hook will connect to your origin server and download the AI models for each of the bots that were copied during the build process, fixing the mentioned issue
6 |
7 | OBS: make sure that the origin server address (host that you suply during login) is reacheable from docker.
8 |
--------------------------------------------------------------------------------
/custom_tools/bp_image_builder/docker_hooks/examples/README.md:
--------------------------------------------------------------------------------
1 | # Docker Hooks Examples
2 |
3 | Each folder will have a custom docker hook (.sh file) and a readme file explaining it
4 |
--------------------------------------------------------------------------------
/custom_tools/bp_image_builder/src/auth/basic.ts:
--------------------------------------------------------------------------------
1 | import fetch from "node-fetch";
2 | import { JWT } from ".";
3 |
4 | export async function loginBasic(
5 | url: string,
6 | payload: { email: string; password: string }
7 | ): Promise {
8 | const endpoint = new URL("/api/v2/admin/auth/login/basic/default", url);
9 |
10 | const res = await fetch(endpoint.href, {
11 | method: "POST",
12 | headers: {
13 | "Content-Type": "application/json",
14 | },
15 | body: JSON.stringify(payload),
16 | });
17 |
18 | const data = await res.json();
19 |
20 | if (!res.ok) {
21 | throw new Error(`Unable to login: ${res.status} ${data.message}`);
22 | }
23 | if (!data.payload) {
24 | throw new Error(`Unable to retrieve token`);
25 | }
26 |
27 | return {
28 | jwt: data.payload.jwt,
29 | exp: Date.now() + data.payload.exp,
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/custom_tools/bp_image_builder/src/build/bppull.ts:
--------------------------------------------------------------------------------
1 | import gunzip from "gunzip-maybe";
2 | import fetch from "node-fetch";
3 | import logger from "loglevel";
4 |
5 | const BP_PULL_ENDPOINT = "/api/v2/admin/management/versioning/export";
6 |
7 | export interface BPPullOpts {
8 | url: string;
9 | authToken: string;
10 | }
11 |
12 | export default async function readBP(
13 | opts: BPPullOpts
14 | ): Promise {
15 | const endpoint = new URL(BP_PULL_ENDPOINT, opts.url);
16 | const res = await fetch(endpoint, {
17 | method: "GET",
18 | headers: { Authorization: `Bearer ${opts.authToken}` },
19 | size: 0,
20 | timeout: 500000,
21 | });
22 | logger
23 | .getLogger("reader")
24 | .info(`Downloading data from Botpress BPFS hosted at ${opts.url}`);
25 | if (!res.ok) {
26 | const error = await res.json();
27 |
28 | throw new Error(
29 | `Archive download failed with error code ${res.status}: ${error.message}`
30 | );
31 | }
32 | return res.body.pipe(gunzip());
33 | }
34 |
--------------------------------------------------------------------------------
/custom_tools/bp_image_builder/src/multistrategy.ts:
--------------------------------------------------------------------------------
1 | export class AbstractMultiStrategy {
2 | protected _strategies = new Map();
3 | registerStrategy(name: string, handler: T): void {
4 | this._strategies.set(name, handler);
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom_tools/bp_image_builder/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "out",
4 | "target": "ESNext",
5 | "baseUrl": "src",
6 | "esModuleInterop": true,
7 | "moduleResolution": "node",
8 | "module": "commonjs",
9 | "sourceMap": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/custom_tools/bp_uploader/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | bin/*
4 | out/
--------------------------------------------------------------------------------
/custom_tools/bp_uploader/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bp_uploader",
3 | "version": "0.1.0",
4 | "license": "MIT",
5 | "engines": {
6 | "node": ">=12.13"
7 | },
8 | "scripts": {
9 | "build": "tsc",
10 | "dev": "ts-node src/index.ts",
11 | "start": "node ./out/index.js",
12 | "package": "yarn build && pkg ."
13 | },
14 | "dependencies": {
15 | "chalk": "^4.1.1",
16 | "commander": "^8.0.0",
17 | "fast-glob": "^3.2.7",
18 | "fs-extra": "^10.0.0",
19 | "loglevel": "^1.7.1",
20 | "node-json-db": "^1.3.0",
21 | "promptly": "^3.2.0",
22 | "request": "^2.88.2"
23 | },
24 | "devDependencies": {
25 | "@types/fs-extra": "^9.0.12",
26 | "@types/promptly": "^3.0.2",
27 | "@types/request": "^2.48.7",
28 | "@types/tar-stream": "^2.2.1",
29 | "pkg": "^5.2.1",
30 | "ts-node": "^10.0.0",
31 | "typescript": "^4.3.4"
32 | },
33 | "pkg": {
34 | "scripts": "out/**/*.js",
35 | "targets": [
36 | "node12-linux-x64",
37 | "node12-macos-x64",
38 | "node12-windows-x64"
39 | ],
40 | "outputPath": "bin"
41 | },
42 | "bin": "./out/index.js"
43 | }
44 |
--------------------------------------------------------------------------------
/custom_tools/bp_uploader/src/auth/README.md:
--------------------------------------------------------------------------------
1 | Code copied from `bp_image_builder`
--------------------------------------------------------------------------------
/custom_tools/bp_uploader/src/auth/basic.ts:
--------------------------------------------------------------------------------
1 | import fetch from "node-fetch";
2 | import { JWT } from ".";
3 |
4 | export async function loginBasic(
5 | url: string,
6 | payload: { email: string; password: string }
7 | ): Promise {
8 | const endpoint = new URL("/api/v2/admin/auth/login/basic/default", url);
9 |
10 | const res = await fetch(endpoint.href, {
11 | method: "POST",
12 | headers: {
13 | "Content-Type": "application/json",
14 | },
15 | body: JSON.stringify(payload),
16 | });
17 |
18 | const data = await res.json();
19 |
20 | if (!res.ok) {
21 | throw new Error(`Unable to login: ${res.status} ${data.message}`);
22 | }
23 | if (!data.payload) {
24 | throw new Error(`Unable to retrieve token`);
25 | }
26 |
27 | return {
28 | jwt: data.payload.jwt,
29 | exp: Date.now() + data.payload.exp,
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/custom_tools/bp_uploader/src/auth/multistrategy.ts:
--------------------------------------------------------------------------------
1 | export class AbstractMultiStrategy {
2 | protected _strategies = new Map();
3 | registerStrategy(name: string, handler: T): void {
4 | this._strategies.set(name, handler);
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/custom_tools/bp_uploader/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "out",
4 | "target": "ESNext",
5 | "baseUrl": "src",
6 | "esModuleInterop": true,
7 | "moduleResolution": "node",
8 | "module": "commonjs",
9 | "sourceMap": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/custom_tools/count messages without HITL included/README.md:
--------------------------------------------------------------------------------
1 | # Count User Messages without HITL Messages Included
2 |
3 | Original author: @bassamtantawi-botpress
4 |
5 | Last updated by @bassamtantawi-botpress July 19 2022
6 |
7 | ## Overview
8 | This is SQL that can be used directly with Postgress to count the number of messages sent by the user to the bot excluding messages sent to agents via HITL Next.
9 |
--------------------------------------------------------------------------------
/custom_tools/readme-template.md:
--------------------------------------------------------------------------------
1 | # Module / Tool / Solution Title
2 |
3 | Original author: @author OR First name
4 |
5 | Last updated by @author on date
6 |
7 | ## Overview
8 | A brief summary of what the thing does, written for a non-technical audience.
9 |
10 | ## Use cases:
11 | Briefly summarize any specific use cases, with screenshots.
12 |
13 | ## How to use
14 | - If it's an action, what parameters does it take?
15 | - If it's a hook, what kind of hook?
16 | - Does it require any config.json files to be modified?
17 | - If skills are added, put screenshots
18 | - Does it create any specialized variables?
19 |
20 | ## Guidelines (Remove before posting)
21 | 1. Ensure a non-technical reader can get a basic understanding of the tool / module / solution from reading this file
22 | 2. Installation instructions should be easy to follow
23 | 3. Please add as many screenshots as posssible! 1 picture == 1000 words
24 | 4. Run **all** writeups through [grammarly](https://demo.grammarly.com/)
25 |
--------------------------------------------------------------------------------