├── .github └── workflows │ ├── README.md │ ├── publish-image-builder.yml │ └── release_bp_image_builder.yml ├── .gitignore ├── .prettierrc ├── README.md ├── custom modules ├── README.MD ├── calendly │ ├── README.md │ ├── calendly.tgz │ └── calendly │ │ ├── README.md │ │ ├── build.extras.js │ │ ├── package.json │ │ ├── src │ │ ├── backend │ │ │ └── index.ts │ │ ├── config.ts │ │ ├── content-types │ │ │ ├── _base.js │ │ │ └── calendlyCard.js │ │ ├── translations │ │ │ └── en.json │ │ └── views │ │ │ ├── full │ │ │ └── index.jsx │ │ │ ├── lite │ │ │ ├── CalendlyCard.jsx │ │ │ └── index.jsx │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ └── yarn.lock ├── custom-multiselect │ ├── README.md │ ├── custom-multiselect.tgz │ ├── sendComponent.js │ └── source code │ │ ├── README.md │ │ ├── build.extras.js │ │ ├── package.json │ │ ├── src │ │ ├── backend │ │ │ └── index.ts │ │ ├── config.ts │ │ └── views │ │ │ ├── full │ │ │ └── index.jsx │ │ │ ├── lite │ │ │ ├── components │ │ │ │ ├── MultiSelect.jsx │ │ │ │ └── style.css │ │ │ └── index.jsx │ │ │ ├── tsconfig.json │ │ │ └── tslint.json │ │ ├── tsconfig.json │ │ └── yarn.lock ├── date-picker │ ├── README.md │ ├── date-picker.tgz │ └── source │ │ ├── package.json │ │ ├── src │ │ ├── actions │ │ │ ├── date-compare.js │ │ │ ├── date-extract.js │ │ │ ├── date-parser.js │ │ │ └── send-date-picker.js │ │ ├── backend │ │ │ ├── api.ts │ │ │ ├── datePicker.ts │ │ │ └── index.ts │ │ ├── config.ts │ │ ├── translations │ │ │ ├── en.json │ │ │ └── fr.json │ │ └── views │ │ │ ├── full │ │ │ ├── DatePicker.tsx │ │ │ ├── index.tsx │ │ │ └── typings.d.ts │ │ │ ├── lite │ │ │ ├── MessengerPicker.tsx │ │ │ ├── Picker.tsx │ │ │ ├── Stylesheet.tsx │ │ │ ├── WebPicker.tsx │ │ │ ├── index.tsx │ │ │ ├── style.scss │ │ │ └── style.scss.d.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ └── yarn.lock ├── delegate-bot-conversation │ ├── README.md │ ├── assets │ │ ├── README.md │ │ ├── index.html │ │ └── logo.png │ ├── build.extras.js │ ├── delegate-bot-conversation.tgz │ ├── package.json │ ├── src │ │ ├── actions │ │ │ ├── README.md │ │ │ ├── delegate-to-sub-bot.js │ │ │ └── end-delegation.js │ │ ├── backend │ │ │ ├── api.ts │ │ │ ├── delegate.ts │ │ │ ├── endDelegation.ts │ │ │ └── index.ts │ │ ├── config.ts │ │ ├── content-types │ │ │ └── README.md │ │ ├── hooks │ │ │ └── README.md │ │ └── views │ │ │ ├── full │ │ │ ├── delegate.jsx │ │ │ ├── endDelegation.jsx │ │ │ └── index.jsx │ │ │ ├── lite │ │ │ └── index.jsx │ │ │ └── tsconfig.json │ ├── tsconfig.json │ └── yarn.lock ├── download-transcript-button │ ├── README.md │ ├── download-transcript-button.tgz │ └── source code │ │ ├── README.md │ │ ├── package.json │ │ ├── src │ │ ├── actions │ │ │ └── sendDownloadTranscriptButton.js │ │ ├── backend │ │ │ └── index.ts │ │ └── views │ │ │ ├── full │ │ │ └── index.jsx │ │ │ ├── lite │ │ │ ├── components │ │ │ │ └── DownloadTranscriptButton.jsx │ │ │ └── index.jsx │ │ │ └── tsconfig.json │ │ └── tsconfig.json ├── flow-utils │ ├── README.md │ ├── bot_demo.tgz │ ├── build.extras.js │ ├── flow-utils.tgz │ ├── package.json │ ├── src │ │ ├── actions │ │ │ ├── add_to_array.js │ │ │ ├── create_array.js │ │ │ ├── increment_variable.js │ │ │ ├── iterate_array.js │ │ │ ├── operation.js │ │ │ ├── reply.js │ │ │ └── setVariableValue.js │ │ ├── backend │ │ │ ├── AddToArray.ts │ │ │ ├── CreateArray.ts │ │ │ ├── ForEach.ts │ │ │ ├── RawReply.ts │ │ │ └── index.ts │ │ ├── translations │ │ │ └── en.json │ │ └── views │ │ │ ├── full │ │ │ ├── AddToArray.jsx │ │ │ ├── CreateArray.jsx │ │ │ ├── ForEach.jsx │ │ │ ├── RawReply.jsx │ │ │ ├── index.jsx │ │ │ ├── lib │ │ │ │ ├── ArgumentsList.tsx │ │ │ │ ├── parameters.scss │ │ │ │ └── parameters.scss.d.ts │ │ │ ├── style.scss │ │ │ └── style.scss.d.ts │ │ │ ├── lite │ │ │ └── index.jsx │ │ │ └── tsconfig.json │ ├── tsconfig.json │ └── yarn.lock ├── form-module │ ├── README.md │ ├── form-module.tgz │ └── source │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src │ │ ├── backend │ │ │ └── index.ts │ │ ├── config.ts │ │ ├── examples │ │ │ └── actions │ │ │ │ └── display-form.js │ │ └── views │ │ │ ├── full │ │ │ └── index.jsx │ │ │ ├── lite │ │ │ ├── components │ │ │ │ └── FormComponent │ │ │ │ │ ├── FormComponent.scss │ │ │ │ │ ├── FormComponent.scss.d.ts │ │ │ │ │ └── FormComponent.tsx │ │ │ └── index.tsx │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ └── yarn.lock ├── google-spreadsheet │ ├── README.md │ ├── google-spreadsheet.tgz │ └── source code │ │ ├── README.md │ │ ├── package.json │ │ ├── src │ │ ├── backend │ │ │ ├── index.ts │ │ │ └── sheet.ts │ │ ├── config.ts │ │ └── views │ │ │ ├── full │ │ │ └── index.jsx │ │ │ └── lite │ │ │ └── index.jsx │ │ ├── tsconfig.json │ │ └── yarn.lock ├── hide-chat │ ├── README.md │ ├── hide-chat_3_0_3.tgz │ └── source code │ │ ├── build.extras.js │ │ ├── package.json │ │ ├── src │ │ ├── actions │ │ │ └── hide_chat.js │ │ ├── backend │ │ │ └── index.ts │ │ ├── bot-templates │ │ │ └── hide-chat-demo │ │ │ │ ├── bot.config.json │ │ │ │ ├── content-elements │ │ │ │ ├── builtin_single-choice.json │ │ │ │ ├── builtin_text.json │ │ │ │ └── custom_hide-chat.json │ │ │ │ └── flows │ │ │ │ ├── main.flow.json │ │ │ │ ├── main.ui.json │ │ │ │ └── skills │ │ │ │ ├── choice-14b859.flow.json │ │ │ │ ├── choice-14b859.ui.json │ │ │ │ ├── choice-a14123.flow.json │ │ │ │ └── choice-a14123.ui.json │ │ └── views │ │ │ ├── full │ │ │ └── index.jsx │ │ │ ├── lite │ │ │ ├── components │ │ │ │ ├── HideChat.jsx │ │ │ │ └── yarn.lock │ │ │ └── index.jsx │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ └── yarn.lock ├── number-picker │ ├── README.md │ ├── number-picker.tgz │ └── source │ │ └── number-picker │ │ ├── package.json │ │ ├── src │ │ ├── actions │ │ │ ├── number-invalid-answer.js │ │ │ ├── number-parse-answer.js │ │ │ └── send-number-picker.js │ │ ├── backend │ │ │ ├── api.ts │ │ │ ├── index.ts │ │ │ └── numberPicker.ts │ │ ├── translations │ │ │ ├── en.json │ │ │ └── fr.json │ │ └── views │ │ │ ├── full │ │ │ ├── NumberPicker.tsx │ │ │ ├── index.tsx │ │ │ └── typings.d.ts │ │ │ ├── lite │ │ │ ├── MessengerPicker.tsx │ │ │ ├── Picker.tsx │ │ │ ├── WebPicker.tsx │ │ │ ├── index.tsx │ │ │ ├── style.scss │ │ │ └── style.scss.d.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ └── yarn.lock ├── riva │ ├── README.md │ ├── package.json │ ├── riva.tgz │ ├── riva_protos │ │ ├── riva_asr.proto │ │ ├── riva_audio.proto │ │ ├── riva_nlp.proto │ │ └── riva_tts.proto │ ├── src │ │ ├── backend │ │ │ ├── api.ts │ │ │ ├── index.ts │ │ │ ├── middleware.ts │ │ │ ├── riva-service.ts │ │ │ ├── service.ts │ │ │ └── utils.ts │ │ ├── config.ts │ │ ├── riva_api │ │ │ ├── riva_asr_grpc_pb.d.ts │ │ │ ├── riva_asr_grpc_pb.js │ │ │ ├── riva_asr_pb.d.ts │ │ │ ├── riva_asr_pb.js │ │ │ ├── riva_audio_grpc_pb.js │ │ │ ├── riva_audio_pb.d.ts │ │ │ ├── riva_audio_pb.js │ │ │ ├── riva_nlp_grpc_pb.d.ts │ │ │ ├── riva_nlp_grpc_pb.js │ │ │ ├── riva_nlp_pb.d.ts │ │ │ ├── riva_nlp_pb.js │ │ │ ├── riva_tts_grpc_pb.d.ts │ │ │ ├── riva_tts_grpc_pb.js │ │ │ ├── riva_tts_pb.d.ts │ │ │ └── riva_tts_pb.js │ │ └── views │ │ │ ├── full │ │ │ └── index.tsx │ │ │ ├── lite │ │ │ ├── Composer.css │ │ │ ├── Composer.tsx │ │ │ ├── Player.tsx │ │ │ ├── Recorder │ │ │ │ ├── FileUploader.tsx │ │ │ │ ├── Microphone.tsx │ │ │ │ ├── Timer.tsx │ │ │ │ ├── VoiceRecorder.tsx │ │ │ │ └── webToWav.ts │ │ │ ├── index.tsx │ │ │ └── utils.tsx │ │ │ └── tsconfig.json │ ├── tsconfig.json │ └── yarn.lock ├── starter-module │ ├── .gitignore │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── README.md │ ├── build.extras.js │ ├── package.json │ ├── src │ │ ├── actions │ │ │ └── README.md │ │ ├── backend │ │ │ ├── api.ts │ │ │ └── index.ts │ │ ├── bot-templates │ │ │ └── .gitkeep │ │ ├── config.ts │ │ ├── content-types │ │ │ └── README.md │ │ ├── hooks │ │ │ ├── README.md │ │ │ ├── after_incoming_middleware │ │ │ │ └── .gitkeep │ │ │ ├── before_incoming_middleware │ │ │ │ └── .gitkeep │ │ │ └── before_suggestions_election │ │ │ │ └── .gitkeep │ │ └── views │ │ │ ├── full │ │ │ ├── example1.jsx │ │ │ ├── example2.jsx │ │ │ └── index.jsx │ │ │ ├── lite │ │ │ └── index.jsx │ │ │ └── tsconfig.json │ ├── tsconfig.json │ └── yarn.lock ├── upload-skill │ ├── .gitignore │ ├── README.md │ ├── assets │ │ ├── README.md │ │ ├── index.html │ │ └── logo.png │ ├── build.extras.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── actions │ │ │ ├── README.md │ │ │ └── display-upload-file.js │ │ ├── backend │ │ │ ├── UploadFile.ts │ │ │ ├── api.ts │ │ │ ├── db.ts │ │ │ ├── index.ts │ │ │ ├── migrate.ts │ │ │ └── utils.ts │ │ ├── config.ts │ │ ├── constants.ts │ │ ├── content-types │ │ │ ├── README.md │ │ │ ├── _base.js │ │ │ ├── _utils.js │ │ │ └── upload-skill-file.js │ │ ├── hooks │ │ │ ├── README.md │ │ │ ├── before_conversation_end │ │ │ │ └── clearDb.js │ │ │ └── before_incoming_middleware │ │ │ │ └── handleUploadFilePayload.js │ │ └── views │ │ │ ├── full │ │ │ ├── UploadFile.jsx │ │ │ ├── example1.jsx │ │ │ ├── example2.jsx │ │ │ └── index.jsx │ │ │ ├── lite │ │ │ ├── components │ │ │ │ ├── FileInput.tsx │ │ │ │ └── UploadFile.tsx │ │ │ └── index.jsx │ │ │ └── tsconfig.json │ ├── tsconfig.json │ ├── upload-skill.tgz │ └── yarn.lock └── validate-email │ ├── README.md │ ├── assets │ ├── README.md │ ├── index.html │ └── logo.png │ ├── build.extras.js │ ├── package.json │ ├── src │ ├── actions │ │ ├── README.md │ │ └── validate-email.js │ ├── backend │ │ ├── InputEmail.ts │ │ ├── index.ts │ │ └── utils.ts │ ├── config.ts │ ├── translations │ │ ├── en.json │ │ ├── es.json │ │ └── fr.json │ └── views │ │ ├── full │ │ ├── InputEmail.jsx │ │ └── index.jsx │ │ ├── lite │ │ └── index.jsx │ │ └── tsconfig.json │ ├── tsconfig.json │ ├── validate-email.tgz │ └── yarn.lock ├── custom solutions ├── Add New Element to Channel │ ├── README.md │ ├── before.png │ ├── block-kit-builder.png │ ├── flow.png │ ├── incoming.js │ ├── on-unmount.js │ ├── sendform.js │ └── server.js ├── Adding new Channel │ ├── README.md │ └── new-channel.svg ├── Break out of flow when matching a QNA │ ├── README.md │ ├── break-out-qna-hook.js │ └── break-out-qna-v2 ├── Build Custom Modules Using Module Builder │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ └── README.md ├── Build Custom Web Chat Components │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── README.md │ ├── custom-web-component-module.zip │ └── custom-web-component-module │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src │ │ ├── backend │ │ │ └── index.ts │ │ ├── config.ts │ │ └── views │ │ │ ├── full │ │ │ └── index.jsx │ │ │ ├── lite │ │ │ ├── before_container.jsx │ │ │ └── index.jsx │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ └── yarn.lock ├── Conversation Events Webhook │ ├── conversation_events_webhook.js │ └── readme.md ├── Create Dynamic Carousel │ ├── README.md │ └── send-carousel.js ├── Create Dynamic Choices │ ├── Advanced │ │ ├── README.md │ │ ├── advanced-dynamic-choice.js │ │ └── process-dynamic-option.js │ ├── Basic │ │ ├── README.md │ │ └── basic-dynamic-choice.js │ └── readme.md ├── Create Link to Download Conversation History │ ├── actions │ │ └── sendTranscriptDownloadLink.js │ ├── hooks │ │ └── after_server_start │ │ │ └── createDownloadConversationRouter.js │ └── readme.md ├── Create URL from User Query │ ├── README.md │ └── actions │ │ ├── send-to-website.js │ │ └── utils.js ├── Create logout link │ ├── hooks │ │ └── after_server_start │ │ │ └── create_logout_router.js │ └── readme.md ├── Custom Analytics - User Path │ ├── Bot Hooks (add to bots you want to track) │ │ └── after_incoming_middleware │ │ │ └── capture-custom-analytics.js │ ├── Global Hooks │ │ ├── after_bot_mount │ │ │ └── custom-analytics.js │ │ ├── after_bot_unmount │ │ │ └── custom-analytics.js │ │ └── before_incoming_middleware │ │ │ └── generate_session_id.js │ └── README.md ├── Custom amount time typing indicator │ ├── README.md │ └── custom_typing_time.js ├── Custom oAuth with Converse Api │ ├── README.md │ └── before_incoming_middleware │ │ └── work.js ├── Customize Webchat CSS │ ├── README.md │ └── hooks │ │ └── after_server_start.js ├── Delegate conversation │ ├── README.md │ ├── delegate.js │ └── reset_conversation.js ├── Delete Conversations │ └── README.md ├── Dynamic Dropdown with Fuzzy Matching │ ├── Bot │ │ └── bot_example-movie-bot_1622830817286.tgz │ ├── README.md │ └── images │ │ └── 0.png ├── Expose public files │ ├── hooks │ │ └── after_server_start │ │ │ └── create_files_router.js │ └── readme.md ├── Extract Data Without Slots │ ├── README.md │ └── hooks │ │ └── after_incoming_middleware.js ├── Extract Dates with Microsoft Text Recognizers │ ├── README.md │ └── hooks │ │ └── after-incoming-middleware.js ├── Extract and Validate Informations │ ├── README.md │ └── actions │ │ └── data-extractor.js ├── Extract time ranges │ ├── README.md │ ├── bot_time-range-extraction_1626801742695.tgz │ └── time-extract-hook.js ├── Force Minimum Confidence │ ├── mininimum-confidence.js │ └── readme.md ├── Get user profile from Messenger │ ├── README.md │ └── actions │ │ ├── fb-extract-info.js │ │ └── utils.js ├── Get user profile from Slack │ ├── README.md │ └── actions │ │ └── get-slack-profile.js ├── Get user profile from Teams │ ├── README.md │ └── actions │ │ └── get-teams-email.js ├── HITL Details │ ├── hooks │ │ └── after_server_start │ │ │ └── HITLDetails.js │ └── readme.md ├── HITL Reject API │ └── readme.md ├── HITL Use Events Webhook │ └── readme.md ├── Increment Custom Metric │ ├── README.md │ └── increment-custom-metric.js ├── Increment Variable By N │ ├── increment-variable-by-n.js │ └── readme.md ├── Push ConverseAPI messages to messaging │ ├── README.md │ └── hooks │ │ ├── after_incoming_middleware │ │ └── insertConverseIntoMessagingIncoming.js │ │ ├── after_server_start │ │ └── createConverseMessagingMap.js │ │ └── before_outgoing_middleware │ │ └── insertConverseIntoMessagingOutgoing.js ├── Record all Misunderstood │ ├── README.MD │ └── record-all-misunderstood.js ├── Record final node │ ├── README.md │ ├── createDropoutTable.js │ └── record-final-node.js ├── Redirect All Qnas │ ├── README.md │ └── Redirect_All_Qnas.js ├── Redirect user during conversation if URL has a parameter │ ├── README.md │ ├── inject_variable_from_webchat.js │ ├── inject_variable_template_final.html │ └── inject_variable_template_v1.html ├── Redirection on exact intent │ └── redirection-hook.js ├── Redirection on session reset │ ├── README.md │ └── redirect_reset_session.js ├── Run Script 30 Minutes Before End of Day │ ├── readme.md │ └── routine.js ├── Send Specific QnA to a User │ ├── README.md │ └── actions │ │ └── send-qna.js ├── Send adaptive cards using teams │ ├── README.md │ └── actions │ │ └── sendAdaptativeCard.js ├── Send message to user using API [Intersection API] │ ├── README.md │ └── hooks │ │ └── after_server_start │ │ └── createIntersectionAPI.js ├── Set Nested Value │ ├── Readme.md │ └── set-nested-value.js ├── Standardize Words Hook │ ├── README.md │ └── standardise_word.js ├── Success Telemetry │ └── Subviews │ │ ├── README.md │ │ ├── solutions │ │ └── download_csv_button │ │ │ ├── Readme.md │ │ │ └── zz_create_download_csv_button.js │ │ └── workflow │ │ └── Separated Workflow │ │ ├── Readme.md │ │ └── zst_subview_workflow_sepated.js ├── Third party translations │ ├── Readme.md │ ├── bot.config.json │ ├── translate-bot-messages.js │ └── translate-user-messages.js ├── Timely Greeting │ ├── Timely-Greeting.js │ └── readme.md ├── UserDropout │ ├── DataInsert.js │ ├── Hooks │ │ ├── TableCreation.js │ │ └── TargetUserEntry.js │ └── NodeUpdate.js ├── Working with Dates │ ├── README.md │ └── actions │ │ ├── date-compare.js │ │ └── date-parser.js └── [HITL-Next] Jump to flow after resolving │ ├── README.md │ └── hooks │ ├── before_incoming_middleware │ └── sendTo-in.js │ └── before_outgoing_middleware │ └── sendTo-out.js └── custom_tools ├── NLU_Testing ├── .env ├── LICENSE ├── NLU-Testing.py ├── NLU_Testing.ipynb ├── README.md └── requirements.txt ├── bots └── export_flow_bot │ ├── README.md │ └── bot_helper-bot.tgz ├── bp_image_builder ├── .gitignore ├── Dockerfile ├── README.md ├── custom_buildImage_config │ ├── README.md │ └── buildImage.config.js ├── custom_dockerfile │ ├── Dockerfile │ └── README.md ├── docker_hooks │ ├── README.md │ └── examples │ │ ├── Download train data from origin │ │ ├── README.md │ │ └── after_build.sh │ │ └── README.md ├── package.json ├── src │ ├── auth │ │ ├── basic.ts │ │ └── index.ts │ ├── build │ │ ├── bppull.ts │ │ ├── fs.ts │ │ └── index.ts │ ├── index.ts │ ├── multistrategy.ts │ └── utils.ts ├── tsconfig.json └── yarn.lock ├── bp_uploader ├── .gitignore ├── README.md ├── package.json ├── src │ ├── auth │ │ ├── README.md │ │ ├── basic.ts │ │ ├── index.ts │ │ └── multistrategy.ts │ ├── index.ts │ └── uploader.ts ├── tsconfig.json └── yarn.lock ├── count messages without HITL included ├── README.md └── count user messages without HITL Messages └── readme-template.md /.github/workflows/README.md: -------------------------------------------------------------------------------- 1 | # GitHub Action 2 | GitHub Actions must be in the root of the repository. 3 | 4 | ## About 5 | This builds and releases the image builder in custom tools when it is updated. Do not download this file. 6 | -------------------------------------------------------------------------------- /.github/workflows/release_bp_image_builder.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - "bpib-v*" # Push events to matching bpib-v* 5 | 6 | name: Release BP Image Builder 7 | 8 | jobs: 9 | build: 10 | name: Create Release 11 | runs-on: ubuntu-latest 12 | defaults: 13 | run: 14 | working-directory: custom_tools/bp_image_builder/ 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v2 18 | - name: Setup NodeJS 19 | uses: actions/setup-node@v2 20 | with: 21 | node-version: "12.13" 22 | - name: Build 23 | working-directory: ${{ github.workspace }}/custom_tools/bp_image_builder/ 24 | run: yarn && yarn package 25 | - name: Get Package Version 26 | id: package-version 27 | uses: notiz-dev/github-action-json-property@release 28 | with: 29 | path: "${{ github.workspace }}/custom_tools/bp_image_builder/package.json" 30 | prop_path: "version" 31 | - name: Create release 32 | uses: softprops/action-gh-release@v1 33 | with: 34 | name: v${{ steps.package-version.outputs.prop }} 35 | files: | 36 | ${{ github.workspace }}/custom_tools/bp_image_builder/bin/* 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | GITHUB_REPOSITORY: botpress/solutions 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | assets 4 | .DS_Store 5 | .idea/ 6 | .idea 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "semi": false, 6 | "bracketSpacing": true, 7 | "requirePragma": false 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # solutions for Botpress V12 2 | 3 | ## Purpose 4 | The public repository provides the Botpress community with several resources to assist in building Chatbots in Botpress V12. 5 | 6 | ## Categories 7 | Below is a list of the different categories in this repository. There are brief explanations of each category provided. 8 | 9 | ### Modules 10 | These are custom modules that provide additional functionality to Botpress v12 Studio. 11 | 12 | ### Solutions 13 | These solutions are a compilation of .js files used for hooks or actions. 14 | 15 | ### Tools 16 | These tools are additional programs provided to assist with your Botpress V12 bot. 17 | -------------------------------------------------------------------------------- /custom modules/calendly/README.md: -------------------------------------------------------------------------------- 1 | # Calendly Module 2 | 3 | Original author: @sadda11asm 4 | 5 | Last updated by @sadda11asm on 17 August 2021 6 | 7 | ## Overview 8 | This custom module creates a new skill to integrate Botpress with [calendly](https://calendly.com/). The new skill lets users select a date on a calendar UI, then a time, and then pass those values to calendly to make an appointment. 9 | 10 | ## Use cases: 11 | 1. Empower end users to schedule meetings using your existing calendly solution. 12 | 13 | ## How to use 14 | 1. Upload module 15 | 2. Add `calendly-card` content type to your bot.config.json 16 | 17 | ![image](https://user-images.githubusercontent.com/28860442/128021196-7d07fa29-4304-4228-8bb2-0fb47d87fece.png) 18 | 19 | 3. Restart Botpress again 20 | 4. Now, you can go and create a node to send the calendly widget invitation: 21 | 22 | ![image](https://user-images.githubusercontent.com/28860442/128021455-d84d2777-afcc-4898-a74a-d7073ba47949.png) 23 | 5. Add the calendly widget URL and other settings to your card 24 | 25 | ![image](https://user-images.githubusercontent.com/28860442/128021650-e1be9818-ca44-4cfe-a887-446397b8387c.png) 26 | 27 | 6. Test your Card in action! 28 | 29 | ![image](https://user-images.githubusercontent.com/28860442/128021897-70bb0ac7-9444-4f20-993f-72473029bb5a.png) 30 | 31 | -------------------------------------------------------------------------------- /custom modules/calendly/calendly.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/calendly/calendly.tgz -------------------------------------------------------------------------------- /custom modules/calendly/calendly/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/calendly/calendly/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "calendly", 3 | "version": "1.0.0", 4 | "description": "Calendly module", 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 | "react-calendly": "^2.2.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /custom modules/calendly/calendly/src/backend/index.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | 3 | import en from '../translations/en.json' 4 | 5 | const entryPoint: sdk.ModuleEntryPoint = { 6 | translations: { en }, 7 | definition: { 8 | // This must match the name of your module's folder, and the name in package.json 9 | name: 'calendly', 10 | /** 11 | * When menuIcon is set to `custom`, you need to provide an icon. It must be at that location: `/assets/icon.png` 12 | * Otherwise, use Material icons name: https://material.io/tools/icons/?style=baseline 13 | */ 14 | menuIcon: 'custom', 15 | // This is the name of your module which will be displayed in the sidebar 16 | menuText: 'Calendly', 17 | // When set to `true`, the name and icon of your module won't be displayed in the sidebar 18 | noInterface: true, 19 | // The full name is used in other places, for example when displaying bot templates 20 | fullName: 'Calendly Module', 21 | // Not used anywhere, but should be a link to your website or module repository 22 | homepage: 'https://botpress.com' 23 | } 24 | } 25 | 26 | export default entryPoint 27 | -------------------------------------------------------------------------------- /custom modules/calendly/calendly/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 | /** 10 | * @default https://botpress.com 11 | */ 12 | someEndpoint: string 13 | /** 14 | * @default 10 15 | */ 16 | maxMessages: number 17 | } 18 | -------------------------------------------------------------------------------- /custom modules/calendly/calendly/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 | } 10 | -------------------------------------------------------------------------------- /custom modules/calendly/calendly/src/translations/en.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /custom modules/calendly/calendly/src/views/full/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | /** 3 | * This file is the full view of your module. It automatically includes heavy dependencies, like react-bootstrap 4 | * If you want to display an interface for your module, export your principal view as "default" 5 | */ 6 | const MyMainView = ({ bp }) => { 7 | return null 8 | } 9 | 10 | export default MyMainView 11 | -------------------------------------------------------------------------------- /custom modules/calendly/calendly/src/views/lite/CalendlyCard.jsx: -------------------------------------------------------------------------------- 1 | import {InlineWidget} from 'react-calendly' 2 | import React from 'react' 3 | 4 | export class CalendlyCard extends React.Component { //bp, hideEventTypeDetails, hideLandingPageDetails, height, width 5 | render() { 6 | console.log("URL", this.props.url) 7 | return ( 8 |
9 | 10 |
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
Some interface
18 | } 19 | } 20 | -------------------------------------------------------------------------------- /custom modules/delegate-bot-conversation/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/delegate-bot-conversation/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/delegate-bot-conversation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.shared.json", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /custom modules/delegate-bot-conversation/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | -------------------------------------------------------------------------------- /custom modules/download-transcript-button/download-transcript-button.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/download-transcript-button/download-transcript-button.tgz -------------------------------------------------------------------------------- /custom modules/download-transcript-button/source code/README.md: -------------------------------------------------------------------------------- 1 | ### How to modify/develop new features to the module 2 | 3 | 1. Copy this folder to the `modules` folder of your botpress repository 4 | 2. Change the folder name to 'download-transcript-button' 5 | 3. Download dependencies and build the module: `yarn && yarn build` 6 | 4. Edit your `data/global/botpress.config.json` and add the module location, like below: 7 | 5. Run Botpress to see the module being used 8 | 9 | ```js 10 | "modules": [ 11 | ... 12 | { 13 | "location": "MODULES_ROOT/download-transcript-button", 14 | "enabled": true 15 | } 16 | ] 17 | ``` 18 | 19 | 6. Change the source code 20 | 7. Build the module: `yarn build` 21 | 8. To package it and create a '.tgz' file, run `yarn && yarn build` 22 | -------------------------------------------------------------------------------- /custom modules/download-transcript-button/source code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "download-transcript-button", 3 | "version": "1.0.0", 4 | "description": "Download Transcript Button", 5 | "private": true, 6 | "main": "dist/backend/index.js", 7 | "scripts": { 8 | "build": "node ../../build/module-builder/bin/entry build", 9 | "watch": "node ../../build/module-builder/bin/entry watch", 10 | "package": "node ../../build/module-builder/bin/entry package" 11 | }, 12 | "author": "Botpress, Inc.", 13 | "license": "AGPL-3.0-only", 14 | "dependencies": { 15 | "react-select": "^2.4.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /custom modules/download-transcript-button/source code/src/actions/sendDownloadTranscriptButton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @title Send Download Transcript Button 4 | * @category Module Download Transcription Button 5 | * @author Botpress, Inc. 6 | */ 7 | const sendDownloadTranscriptButton = async () => { 8 | if (event.channel != 'web') { 9 | return 10 | } 11 | 12 | const postbackEvent = bp.IO.Event({ 13 | type: 'custom', 14 | channel: 'web', 15 | direction: 'outgoing', 16 | target: event.target, 17 | botId: event.botId, 18 | payload: { type: 'custom', component: 'DownloadTranscriptButton', module: 'download-transcript-button' } 19 | }) 20 | 21 | await bp.events.sendEvent(postbackEvent) 22 | } 23 | 24 | return sendDownloadTranscriptButton() 25 | -------------------------------------------------------------------------------- /custom modules/download-transcript-button/source code/src/backend/index.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | 3 | const entryPoint: sdk.ModuleEntryPoint = { 4 | definition: { 5 | name: 'download-transcript-button', 6 | menuIcon: 'none', 7 | menuText: 'Download Transcript Button', 8 | fullName: 'Download Transcript Button', 9 | noInterface: true, // This prevents your module from being displayed in the menu, since we only add custom components here 10 | homepage: 'https://botpress.com' 11 | } 12 | } 13 | 14 | export default entryPoint 15 | -------------------------------------------------------------------------------- /custom modules/download-transcript-button/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/download-transcript-button/source code/src/views/lite/components/DownloadTranscriptButton.jsx: -------------------------------------------------------------------------------- 1 | export class DownloadTranscriptButton extends React.Component { 2 | onClick = () => { 3 | this.props.store.downloadConversation() 4 | } 5 | 6 | render() { 7 | return ( 8 | 11 | ) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /custom modules/download-transcript-button/source code/src/views/lite/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export { DownloadTranscriptButton } from './components/DownloadTranscriptButton' 4 | 5 | export class InjectedBelow extends React.Component { 6 | render() { 7 | return null 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /custom modules/download-transcript-button/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/download-transcript-button/source code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.shared.json", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /custom modules/flow-utils/README.md: -------------------------------------------------------------------------------- 1 | # Flow Utils 2 | Original Author: David Vitora 3 | 4 | ## Overview 5 | This module gives many of the fuctionalities of arrays to the flow editor. 6 | - For Each Skill 7 | - iterate through array 8 | - Add To Array Skill 9 | - Add and item on array at temp/user/session memory 10 | - Create Array Skill 11 | - Create an array on temp/user/session memory 12 | - Raw Reply Skill 13 | - Provides details that can be helpful for debugging and desinging dynamic content for skills 14 | 15 | 16 | ## Use Cases 17 | - processing array data 18 | - creating/editing array objects 19 | - debugging 20 | 21 | 22 | ## How to Use 23 | 1. Add Module (flow-utils.tgz) 24 | 2. Go to Flow editor 25 | 3. Select Desired Skill 26 | - For Each 27 | - ![image](https://user-images.githubusercontent.com/104075132/191105285-2b66b750-e877-4229-939f-2c500a566102.png) 28 | 29 | - Add to Array 30 | - ![image](https://user-images.githubusercontent.com/104075132/191105423-07668195-9bec-4f98-ad37-1d8ab79ff6f8.png) 31 | 32 | - Create Array 33 | - ![image](https://user-images.githubusercontent.com/104075132/191105506-56cec936-bf97-40f8-b3a7-43bd82752794.png) 34 | 35 | - Raw Reply 36 | - ![image](https://user-images.githubusercontent.com/104075132/191105569-4cacb244-0c15-4349-8ef3-1882d9913e3e.png) 37 | 38 | 4. Add to flows 39 | 40 | -------------------------------------------------------------------------------- /custom modules/flow-utils/bot_demo.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/flow-utils/bot_demo.tgz -------------------------------------------------------------------------------- /custom modules/flow-utils/build.extras.js: -------------------------------------------------------------------------------- 1 | // This is only to make sure that the module builder exports the demo bot template. 2 | // If you don't include bot templates with your module, this file can be excluded. 3 | module.exports = { 4 | copyFiles: ['src/bot-templates/**'] 5 | } 6 | -------------------------------------------------------------------------------- /custom modules/flow-utils/flow-utils.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/flow-utils/flow-utils.tgz -------------------------------------------------------------------------------- /custom modules/flow-utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flow-utils", 3 | "version": "1.0.0", 4 | "description": "Flow Utils", 5 | "private": true, 6 | "main": "dist/backend/index.js", 7 | "webpack": { 8 | "externals": { 9 | "reactstrap": "Reactstrap", 10 | "botpress/tooltip": "BotpressTooltip" 11 | } 12 | }, 13 | "scripts": { 14 | "build": "node ../../build/module-builder/bin/entry build", 15 | "watch": "node ../../build/module-builder/bin/entry watch", 16 | "package": "node ../../build/module-builder/bin/entry package" 17 | }, 18 | "author": "Botpress, Inc.", 19 | "license": "AGPL-3.0-only", 20 | "dependencies": { 21 | "he": "^1.2.0", 22 | "nanoid": "^2.1.6", 23 | "react-bootstrap": "^0.33.1", 24 | "react-select": "^2.4.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/actions/add_to_array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @hidden true 3 | */ 4 | 5 | const _ = require('lodash') 6 | var he = require('he') 7 | 8 | const add_to_array = async (variableArrayName, variableArrayScope, value) => { 9 | const context = { 10 | event, 11 | user: event.state.user, 12 | temp: event.state.temp, 13 | session: event.state.session 14 | } 15 | 16 | if (value.trim().match(/^\${.*}$/g)) { 17 | // bp.cms.renderTemplate will use mustache, which is not good to assign complex values 18 | value = _.get(context, value.replace(/\${|}/g, '')) 19 | } else { 20 | value = he.decode(value) 21 | } 22 | 23 | try { 24 | value = JSON.parse(value) 25 | } catch (e) { 26 | bp.logger.attachError(e).error('[AddToArray] Error parsing item') 27 | } 28 | 29 | const scope = event.state[variableArrayScope] 30 | const array = _.get(event.state, `${variableArrayScope}.${variableArrayName}`) 31 | if (!scope || !array || !Array.isArray(array)) { 32 | throw new Error(`Array with variableName ${variableArrayName} and scope ${variableArrayScope} doesn't exist`) 33 | } 34 | 35 | array.push(value) 36 | } 37 | 38 | return add_to_array(args.variableName, args.variableScope, args.value) 39 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/actions/create_array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @hidden true 3 | */ 4 | 5 | const _ = require('lodash') 6 | 7 | const create_array = async (variableArrayName, variableArrayScope, value) => { 8 | try { 9 | value = JSON.parse(value) 10 | } catch (e) { 11 | value = undefined 12 | bp.logger.attachError(e).error('[CreateArray] Error parsing array') 13 | } 14 | 15 | if (!event.state[variableArrayScope]) { 16 | throw new Error(`Invalid scope ${variableArrayScope}`) 17 | } 18 | 19 | _.set(event.state, `${variableArrayScope}.${variableArrayName}`, value || []) 20 | } 21 | 22 | return create_array(args.variableName, args.variableScope, args.value) 23 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/actions/increment_variable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Increments a variable by an amount 3 | * @title Increment Variable 4 | * @category Flow-Utils 5 | * @author David 6 | * @param {string} variableType - temp/session/user 7 | * @param {string} variableName - Variable Name 8 | * @param {number} amountToIncrement - Amount to Increment in the number variable 9 | */ 10 | 11 | const _ = require('lodash') 12 | 13 | const myAction = async (variableType, variableName, amountToIncrement) => { 14 | _.set( 15 | event.state, 16 | `${variableType}.${variableName}`, 17 | (Number.parseFloat(_.get(event.state, `${variableType}.${variableName}`)) || 0) + 18 | Number.parseFloat(amountToIncrement) 19 | ) 20 | } 21 | return myAction(args.variableType, args.variableName, args.amountToIncrement) 22 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/actions/reply.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @hidden true 3 | */ 4 | 5 | const _ = require('lodash') 6 | 7 | const rawReply = async (contentType, payload) => { 8 | const context = { 9 | event, 10 | user: event.state.user, 11 | temp: event.state.temp, 12 | session: event.state.session 13 | } 14 | try { 15 | _.forEach(payload, (value, key) => { 16 | try { 17 | if (value.trim().match(/^\${.*}$/g)) { 18 | // bp.cms.renderTemplate will use mustache, which is not good to assign complex values 19 | payload[key] = _.get(context, value.replace(/\${|}/g, '')) 20 | } else if (value.match(/{{|}}/g)) { 21 | // only use it if the string has lots of variables to fiil 22 | payload[key] = bp.cms.renderTemplate(value, context) 23 | } 24 | payload[key] = JSON.parse(payload[key]) 25 | } catch (e) {} 26 | }) 27 | const payloads = await bp.cms.renderElement(contentType, payload, event) 28 | bp.events.replyToEvent( 29 | { 30 | botId: event.botId, 31 | channel: event.channel, 32 | target: event.target, 33 | threadId: event.threadId 34 | }, 35 | payloads, 36 | event.id 37 | ) 38 | } catch (e) { 39 | bp.logger.attachError(e).error('[Reply] Error') 40 | } 41 | } 42 | 43 | return rawReply(args.contentType, args.payload) 44 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/backend/AddToArray.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | import _ from 'lodash' 3 | 4 | const generateFlow = async (data: any, metadata: sdk.FlowGeneratorMetadata): Promise => { 5 | return { 6 | transitions: createTransitions(data), 7 | flow: { 8 | nodes: createNodes(data), 9 | catchAll: { 10 | next: [] 11 | } 12 | } 13 | } 14 | } 15 | 16 | const createNodes = data => { 17 | const nodes: sdk.SkillFlowNode[] = [ 18 | { 19 | name: 'entry', 20 | onEnter: [ 21 | { 22 | type: sdk.NodeActionType.RunAction, 23 | name: 'flow-utils/add_to_array', 24 | args: { 25 | variableName: data.variableName, 26 | variableScope: data.variableScope, 27 | value: data.value 28 | } 29 | } 30 | ], 31 | next: [{ condition: 'true', node: '#' }] 32 | } 33 | ] 34 | return nodes 35 | } 36 | 37 | const createTransitions = (data): sdk.NodeTransition[] => { 38 | return [ 39 | { 40 | caption: 'On End', 41 | condition: 'true', 42 | node: '' 43 | } 44 | ] 45 | } 46 | 47 | export default { generateFlow } 48 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/backend/CreateArray.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | import _ from 'lodash' 3 | 4 | const generateFlow = async (data: any, metadata: sdk.FlowGeneratorMetadata): Promise => { 5 | return { 6 | transitions: createTransitions(data), 7 | flow: { 8 | nodes: createNodes(data), 9 | catchAll: { 10 | next: [] 11 | } 12 | } 13 | } 14 | } 15 | 16 | const createNodes = data => { 17 | const nodes: sdk.SkillFlowNode[] = [ 18 | { 19 | name: 'entry', 20 | onEnter: [ 21 | { 22 | type: sdk.NodeActionType.RunAction, 23 | name: 'flow-utils/create_array', 24 | args: { 25 | variableName: data.variableName, 26 | variableScope: data.variableScope, 27 | value: data.value 28 | } 29 | } 30 | ], 31 | next: [{ condition: 'true', node: '#' }] 32 | } 33 | ] 34 | return nodes 35 | } 36 | 37 | const createTransitions = (data): sdk.NodeTransition[] => { 38 | return [ 39 | { 40 | caption: 'On Create', 41 | condition: 'true', 42 | node: '' 43 | } 44 | ] 45 | } 46 | 47 | export default { generateFlow } 48 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/backend/RawReply.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | import _ from 'lodash' 3 | 4 | const generateFlow = async (data: any, metadata: sdk.FlowGeneratorMetadata): Promise => { 5 | return { 6 | transitions: createTransitions(data), 7 | flow: { 8 | nodes: createNodes(data), 9 | catchAll: { 10 | next: [] 11 | } 12 | } 13 | } 14 | } 15 | 16 | const createNodes = data => { 17 | const nodes: sdk.SkillFlowNode[] = [ 18 | { 19 | name: 'entry', 20 | onEnter: [ 21 | { 22 | type: sdk.NodeActionType.RunAction, 23 | name: 'flow-utils/reply', 24 | args: { 25 | contentType: data.contentType, 26 | payload: data.payload 27 | } 28 | } 29 | ], 30 | next: [{ condition: 'true', node: '#' }] 31 | } 32 | ] 33 | return nodes 34 | } 35 | 36 | const createTransitions = (data): sdk.NodeTransition[] => { 37 | return [ 38 | { 39 | caption: 'On End', 40 | condition: 'true', 41 | node: '' 42 | } 43 | ] 44 | } 45 | 46 | export default { generateFlow } 47 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/backend/index.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | 3 | import en from '../translations/en.json' 4 | 5 | import addToArray from './AddToArray' 6 | import createArray from './CreateArray' 7 | import forEach from './ForEach' 8 | import rawReply from './RawReply' 9 | 10 | const skills: sdk.Skill[] = [ 11 | { 12 | id: 'ForEach', 13 | name: 'module.flow-utils.forEach', 14 | icon: 'refresh', 15 | flowGenerator: forEach.generateFlow 16 | }, 17 | { 18 | id: 'AddToArray', 19 | name: 'module.flow-utils.addToArray', 20 | icon: 'add', 21 | flowGenerator: addToArray.generateFlow 22 | }, 23 | { 24 | id: 'CreateArray', 25 | name: 'module.flow-utils.createArray', 26 | icon: 'array', 27 | flowGenerator: createArray.generateFlow 28 | }, 29 | { 30 | id: 'RawReply', 31 | name: 'module.flow-utils.rawReply', 32 | icon: 'send-message', 33 | flowGenerator: rawReply.generateFlow 34 | } 35 | ] 36 | 37 | const entryPoint: sdk.ModuleEntryPoint = { 38 | definition: { 39 | name: 'flow-utils', 40 | menuIcon: 'none', 41 | menuText: 'Flow Utils', 42 | fullName: 'Flow Utils', 43 | noInterface: true 44 | }, 45 | translations: { en }, 46 | skills 47 | } 48 | 49 | export default entryPoint 50 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "forEach": "For Each", 3 | "createArray": "Create Array", 4 | "addToArray": "Add to Array", 5 | "rawReply": "Raw Reply" 6 | } 7 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/views/full/index.jsx: -------------------------------------------------------------------------------- 1 | export { ForEach } from './ForEach' 2 | export { AddToArray } from './AddToArray' 3 | export { CreateArray } from './CreateArray' 4 | export { RawReply } from './RawReply' 5 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/views/full/lib/parameters.scss: -------------------------------------------------------------------------------- 1 | .key input { 2 | padding: 5px; 3 | border: solid 1px #ddd; 4 | height: 32px; 5 | } 6 | 7 | .invalid input { 8 | border: 1px solid red; 9 | } 10 | 11 | .table { 12 | td { 13 | padding: 0; 14 | position: relative; 15 | } 16 | 17 | input { 18 | width: 100%; 19 | } 20 | 21 | tbody { 22 | overflow-y: overlay; 23 | } 24 | 25 | margin-bottom: 0; 26 | table-layout: fixed; 27 | } 28 | 29 | .keyTip { 30 | position: absolute; 31 | right: 15px; 32 | font-size: 20px; 33 | top: 11px; 34 | color: green; 35 | } 36 | 37 | .mandatory { 38 | input { 39 | font-weight: bold; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/views/full/lib/parameters.scss.d.ts: -------------------------------------------------------------------------------- 1 | // This file is automatically generated. 2 | // Please do not change this file! 3 | interface CssExports { 4 | 'invalid': string; 5 | 'key': string; 6 | 'keyTip': string; 7 | 'mandatory': string; 8 | 'table': string; 9 | } 10 | declare var cssExports: CssExports; 11 | export = cssExports; 12 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/views/full/style.scss: -------------------------------------------------------------------------------- 1 | .modalContent { 2 | padding: 15px; 3 | } 4 | 5 | .warning { 6 | color: red; 7 | } 8 | 9 | .note { 10 | margin: 10px 0 10px 0; 11 | } 12 | 13 | .section { 14 | margin-top: 10px; 15 | } 16 | 17 | .warning { 18 | color: darkred; 19 | 20 | i { 21 | font-size: 16px; 22 | vertical-align: baseline; 23 | } 24 | } 25 | 26 | .padded { 27 | padding: 10px; 28 | } 29 | 30 | .errorContainer { 31 | height: 50px; 32 | margin-top: 5px; 33 | } 34 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/views/full/style.scss.d.ts: -------------------------------------------------------------------------------- 1 | // This file is automatically generated. 2 | // Please do not change this file! 3 | interface CssExports { 4 | 'errorContainer': string; 5 | 'modalContent': string; 6 | 'note': string; 7 | 'padded': string; 8 | 'section': string; 9 | 'warning': string; 10 | } 11 | declare var cssExports: CssExports; 12 | export = cssExports; 13 | -------------------------------------------------------------------------------- /custom modules/flow-utils/src/views/lite/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export class InjectedBelow extends React.Component { 4 | render() { 5 | return null 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /custom modules/flow-utils/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/flow-utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.shared.json", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /custom modules/form-module/README.md: -------------------------------------------------------------------------------- 1 | # Form Module 2 | Original author: allardy 3 | 4 | ## Overview 5 | 6 | This custom module allows a form to be displayed to a user in a webchat. A template action is provided that can be used to build different forms. 7 | 8 | ## Use Cases 9 | If you need to provide access to a form as part of your flows within your chatbot. 10 | 11 | ## How to use 12 | 1. Upload Module 13 | 2. Open the code editor 14 | 3. In actions in the **Examples** folder there will be a sub folder called **form-module with** a sub-folder called **actions** it will contain **display-form.js** 15 | ![image](https://user-images.githubusercontent.com/104075132/200040310-ca6f52da-611b-4c40-a60e-d06228a59198.png) 16 | 4. Copy the content of the action 17 | 5. Create a new action and paste content from the display-form.js file 18 | 6. Adjust schema as required 19 | 7. Add action to appropriate node in flow 20 | 8. Add a **after_incoming_middleware** hook that catches event of type **form-data** and use that hook to dictate what happens with the form data. 21 | 22 | ```js 23 | if (event.type === "form-data" && event.payload.data) { 24 | const { firstName, lastName, businessEmail, phoneNumber } = event.payload.data; 25 | // do something 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /custom modules/form-module/form-module.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/form-module/form-module.tgz -------------------------------------------------------------------------------- /custom modules/form-module/source/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | assets/web/ 3 | assets/config.schema.json 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /custom modules/form-module/source/README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | This is a simple module that shows a form to a user on the webchat 4 | 5 | ## Quick Start 6 | 7 | 1. Add this module on your server 8 | 2. Open the code editor 9 | 3. Under `Examples`, expand `form-module`, then `actions`, and open `display-form.js` 10 | 4. Copy the content of the file 11 | 5. Create a new action, then paste the content in that new file 12 | 6. Adjust the fields & the schema as required, then add the action on your flow 13 | 14 | ## Processing responses 15 | 16 | 1. Add a `after_incoming_middleware` hook 17 | 2. Catch event of type `form-data` 18 | 19 | ```js 20 | if (event.type === 'form-data' && event.payload.data) { 21 | const { firstName, lastName, businessEmail, phoneNumber } = event.payload.data 22 | // do something 23 | } 24 | ``` 25 | -------------------------------------------------------------------------------- /custom modules/form-module/source/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "form-module", 3 | "version": "1.0.0", 4 | "description": "A module allowing the bot to display and gather data via a form component", 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 | "serialize-javascript": "^6.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /custom modules/form-module/source/src/backend/index.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | 3 | const entryPoint: sdk.ModuleEntryPoint = { 4 | definition: { 5 | name: 'form-module', 6 | menuIcon: 'flag', 7 | menuText: 'Form Module', 8 | noInterface: true, 9 | fullName: 'Form Module' 10 | } 11 | } 12 | 13 | export default entryPoint 14 | -------------------------------------------------------------------------------- /custom modules/form-module/source/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 | /** 10 | * @default https://botpress.com 11 | */ 12 | someEndpoint: string 13 | /** 14 | * @default 10 15 | */ 16 | maxMessages: number 17 | } 18 | -------------------------------------------------------------------------------- /custom modules/form-module/source/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 | /** 9 | * This file is the full view of your module. It automatically includes heavy dependencies, like react-bootstrap 10 | * If you want to display an interface for your module, export your principal view as "default" 11 | */ 12 | export default class MyMainView extends React.Component { 13 | render() { 14 | return null 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /custom modules/form-module/source/src/views/lite/components/FormComponent/FormComponent.scss.d.ts: -------------------------------------------------------------------------------- 1 | // This file is automatically generated. 2 | // Please do not change this file! 3 | interface CssExports { 4 | 'Button': string; 5 | 'ButtonDisabled': string; 6 | 'Disabled': string; 7 | 'Error': string; 8 | 'Form': string; 9 | 'Group': string; 10 | 'Input': string; 11 | 'InputError': string; 12 | 'Logo': string; 13 | 'P': string; 14 | 'Privacy': string; 15 | 'form__field': string; 16 | 'form__label': string; 17 | 'form__label_error': string; 18 | } 19 | declare var cssExports: CssExports; 20 | export = cssExports; 21 | -------------------------------------------------------------------------------- /custom modules/form-module/source/src/views/lite/index.tsx: -------------------------------------------------------------------------------- 1 | export { FormComponent } from './components/FormComponent/FormComponent' 2 | -------------------------------------------------------------------------------- /custom modules/form-module/source/src/views/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig_view.shared.json", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /custom modules/form-module/source/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.shared.json", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /custom modules/form-module/source/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | randombytes@^2.1.0: 6 | version "2.1.0" 7 | resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" 8 | integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== 9 | dependencies: 10 | safe-buffer "^5.1.0" 11 | 12 | safe-buffer@^5.1.0: 13 | version "5.2.1" 14 | resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 15 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 16 | 17 | serialize-javascript@^6.0.0: 18 | version "6.0.0" 19 | resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" 20 | integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== 21 | dependencies: 22 | randombytes "^2.1.0" 23 | -------------------------------------------------------------------------------- /custom modules/google-spreadsheet/README.md: -------------------------------------------------------------------------------- 1 | # Google Spreadsheet Module 2 | Original author: @davidvitora 3 | 4 | ## Overview 5 | Allows you to integrate your bot to send data to a googlesheet. 6 | 7 | ## Use cases 8 | Allows you to write to a google sheet. 9 | 10 | Provides Google Sheets Api access within Botpress 11 | 12 | ## How to use 13 | 1. Upload Module 14 | 2. [Set up Service Account for Google Sheets](https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication) 15 | 3. Go to Code Editor **Module Configurations**->**google-spreadsheet.json** 16 | 4. below schema add clientEmail and Private key as pictured below with the correct values and save 17 | ![image](https://user-images.githubusercontent.com/104075132/200061739-59616c1a-324d-4777-a0e0-03e918b8e9ab.png) 18 | 5. Access the object from the bp.kvs namespace 19 | const doc = await bp.kvs.googleSpreadSheet.getGoogleSheet('DOCUMENT_ID_FROM_URL') // https://docs.google.com/spreadsheets/d/DOCUMENT_ID_FROM_URL/edit#gid=0 20 | 21 | **Tip**: Make sure you have shared the google sheet document with the email generated for the Service Account 22 | 23 | **Additional Docs**: https://theoephraim.github.io/node-google-spreadsheet/#/?id=the-basics 24 | -------------------------------------------------------------------------------- /custom modules/google-spreadsheet/google-spreadsheet.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/google-spreadsheet/google-spreadsheet.tgz -------------------------------------------------------------------------------- /custom modules/google-spreadsheet/source code/README.md: -------------------------------------------------------------------------------- 1 | Provides Google Sheets Api access within Botpress 2 | 3 | How to use 4 | 5 | Set up a Service Account 6 | 7 | https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication 8 | 9 | Configure the email and private key in the module configuration 10 | 11 | Now you can access the object from bp.kvs namespace 12 | 13 | const doc = await bp.kvs.googleSpreadSheet.getGoogleSheet('DOCUMENT_ID_FROM_URL') // https://docs.google.com/spreadsheets/d/DOCUMENT_ID_FROM_URL/edit#gid=0 14 | 15 | OBS: You need to share the google sheet document with the email generated for the Service Account 16 | 17 | DOCS: https://theoephraim.github.io/node-google-spreadsheet/#/?id=the-basics 18 | -------------------------------------------------------------------------------- /custom modules/google-spreadsheet/source code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-spreadsheet", 3 | "version": "1.0.0", 4 | "description": "Google Spread Sheet", 5 | "private": true, 6 | "main": "dist/backend/index.js", 7 | "scripts": { 8 | "build": "node ../../build/module-builder/bin/entry build", 9 | "watch": "node ../../build/module-builder/bin/entry watch", 10 | "package": "node ../../build/module-builder/bin/entry package" 11 | }, 12 | "author": "Botpress, Inc.", 13 | "license": "AGPL-3.0-only", 14 | "dependencies": { 15 | "google-spreadsheet": "^3.1.15", 16 | "react-select": "^2.4.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /custom modules/google-spreadsheet/source code/src/backend/index.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | import { getGoogleSheet } from './sheet' 3 | 4 | export interface Extension { 5 | googleSpreadSheet: { 6 | getGoogleSheet: any 7 | } 8 | } 9 | 10 | export type SDK = typeof sdk & Extension 11 | 12 | const onServerReady = async (bp: typeof sdk & Extension) => { 13 | // @ts-ignore 14 | bp.kvs.googleSpreadSheet = { 15 | getGoogleSheet: (URLId) => { 16 | return getGoogleSheet(bp, URLId) 17 | } 18 | } 19 | } 20 | 21 | const entryPoint: sdk.ModuleEntryPoint = { 22 | onServerReady, 23 | definition: { 24 | name: 'google-spreadsheet', 25 | menuIcon: 'none', 26 | menuText: 'Google Spread Sheet', 27 | fullName: 'Google Spread Sheet', 28 | noInterface: true, // This prevents your module from being displayed in the menu, since we only add custom components here 29 | homepage: 'https://botpress.com' 30 | } 31 | } 32 | 33 | export default entryPoint 34 | -------------------------------------------------------------------------------- /custom modules/google-spreadsheet/source code/src/backend/sheet.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash' 2 | import { Config } from '../config' 3 | const { GoogleSpreadsheet } = require('google-spreadsheet') 4 | 5 | 6 | 7 | export const getGoogleSheet = async (bp, URLId) => { 8 | const sheet = new GoogleSpreadsheet(URLId) 9 | const globalConfig = (await bp.config.getModuleConfig('google-spreadsheet')) as Config 10 | 11 | await sheet.useServiceAccountAuth({ 12 | client_email: globalConfig.clientEmail, 13 | private_key: globalConfig.private_key, 14 | }) 15 | 16 | return sheet 17 | } 18 | -------------------------------------------------------------------------------- /custom modules/google-spreadsheet/source code/src/config.ts: -------------------------------------------------------------------------------- 1 | export interface Config { 2 | /** 3 | * Email generated for the service account 4 | * https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication 5 | */ 6 | clientEmail?: string 7 | 8 | /** 9 | * Private key generated for the service account 10 | * https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication 11 | */ 12 | private_key?: string 13 | } 14 | -------------------------------------------------------------------------------- /custom modules/google-spreadsheet/source code/src/views/full/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export class FullView extends React.Component { 4 | render() { 5 | return null 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /custom modules/google-spreadsheet/source code/src/views/lite/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export class LiteView extends React.Component { 4 | render() { 5 | return null 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /custom modules/google-spreadsheet/source code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.shared.json", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /custom modules/hide-chat/hide-chat_3_0_3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/hide-chat/hide-chat_3_0_3.tgz -------------------------------------------------------------------------------- /custom modules/hide-chat/source code/build.extras.js: -------------------------------------------------------------------------------- 1 | // This is only to make sure that the module builder exports the demo bot template. 2 | // If you don't include bot templates with your module, this file can be excluded. 3 | module.exports = { 4 | copyFiles: ['src/bot-templates/**'] 5 | } 6 | -------------------------------------------------------------------------------- /custom modules/hide-chat/source code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hide-chat", 3 | "version": "3.0.3", 4 | "description": "Hide Chat v3.0.3", 5 | "private": true, 6 | "main": "dist/backend/index.js", 7 | "scripts": { 8 | "build": "node ../../build/module-builder/bin/entry build", 9 | "watch": "node ../../build/module-builder/bin/entry watch", 10 | "package": "node ../../build/module-builder/bin/entry package" 11 | }, 12 | "author": "Botpress, Inc.", 13 | "license": "AGPL-3.0-only", 14 | "dependencies": { 15 | "react-select": "^2.4.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /custom modules/hide-chat/source code/src/actions/hide_chat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Hide/Show Composer 3 | * @title Hide or shows the text input field (composer) 4 | * @category Hide Chat 5 | * @author David Vitora 6 | * @param {boolean} hide - 'true' or 'false' 7 | */ 8 | 9 | const yn = require('yn') 10 | const hideChat = async hide => { 11 | const payload = [ 12 | { 13 | type: 'custom', 14 | module: 'hide-chat', 15 | component: 'HideChat', 16 | hidden: yn(hide), 17 | noBubble: true 18 | } 19 | ] 20 | await bp.events.replyToEvent( 21 | { 22 | botId: event.botId, 23 | channel: event.channel, 24 | target: event.target, 25 | threadId: event.threadId 26 | }, 27 | payload, 28 | event.id 29 | ) 30 | } 31 | 32 | return hideChat(args.hide) 33 | -------------------------------------------------------------------------------- /custom modules/hide-chat/source code/src/backend/index.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | 3 | const botTemplates: sdk.BotTemplate[] = [ 4 | { 5 | id: 'hide-chat-demo', 6 | name: 'Demo - Hide Chat', 7 | desc: 'This demo bot shows how the chat can be hidden' 8 | } 9 | ] 10 | 11 | const entryPoint: sdk.ModuleEntryPoint = { 12 | botTemplates, 13 | definition: { 14 | name: 'hide-chat', 15 | menuIcon: 'none', 16 | menuText: 'Hide Chat', 17 | fullName: 'Hide Chat', 18 | noInterface: true, // This prevents your module from being displayed in the menu, since we only add custom components here 19 | homepage: 'https://botpress.com' 20 | } 21 | } 22 | 23 | export default entryPoint 24 | -------------------------------------------------------------------------------- /custom modules/hide-chat/source code/src/bot-templates/hide-chat-demo/bot.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "locked": false, 3 | "disabled": false, 4 | "private": false, 5 | "details": {}, 6 | "$schema": "../../bot.config.schema.json", 7 | "description": "This demo bot shows how the chat can be hidden", 8 | "active": true, 9 | "version": "12.19.0", 10 | "author": "Botpress, Inc.", 11 | "license": "AGPL-3.0", 12 | "imports": { 13 | "modules": [], 14 | "incomingMiddleware": [], 15 | "outgoingMiddleware": [], 16 | "contentTypes": ["builtin_text", "builtin_single-choice", "builtin_image", "builtin_carousel", "builtin_card"] 17 | }, 18 | "dialog": { 19 | "timeoutInterval": "5m" 20 | }, 21 | "logs": { 22 | "expiration": "1 week" 23 | }, 24 | "languages": ["en"], 25 | "defaultLanguage": "en" 26 | } 27 | -------------------------------------------------------------------------------- /custom modules/hide-chat/source code/src/bot-templates/hide-chat-demo/content-elements/custom_hide-chat.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "custom_hide-chat-0faErK", 4 | "formData": { 5 | "hidden$en": true 6 | }, 7 | "createdBy": "admin", 8 | "createdOn": "2021-03-24T13:55:03.384Z", 9 | "modifiedOn": "2021-03-24T13:55:03.384Z" 10 | }, 11 | { 12 | "id": "custom_hide-chat-3pNcRH", 13 | "formData": { 14 | "hidden$en": false 15 | }, 16 | "createdBy": "admin", 17 | "createdOn": "2021-03-24T13:57:45.196Z", 18 | "modifiedOn": "2021-03-24T13:57:45.196Z" 19 | } 20 | ] -------------------------------------------------------------------------------- /custom modules/hide-chat/source code/src/bot-templates/hide-chat-demo/flows/skills/choice-14b859.ui.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "360188", 5 | "position": {} 6 | }, 7 | { 8 | "id": "465079", 9 | "position": {} 10 | }, 11 | { 12 | "id": "579550", 13 | "position": {} 14 | }, 15 | { 16 | "id": "600666", 17 | "position": {} 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /custom modules/hide-chat/source code/src/bot-templates/hide-chat-demo/flows/skills/choice-a14123.ui.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "476710", 5 | "position": {} 6 | }, 7 | { 8 | "id": "308388", 9 | "position": {} 10 | }, 11 | { 12 | "id": "996895", 13 | "position": {} 14 | }, 15 | { 16 | "id": "744037", 17 | "position": {} 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /custom modules/hide-chat/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/hide-chat/source code/src/views/lite/components/HideChat.jsx: -------------------------------------------------------------------------------- 1 | export class HideChat extends React.Component { 2 | componentDidMount() { 3 | const { 4 | store: { composer }, 5 | hidden, 6 | isBotMessage, 7 | isLastGroup, 8 | isLastOfGroup 9 | } = this.props 10 | 11 | if (isBotMessage && ( isLastGroup || isLastOfGroup )) { 12 | composer.setHidden(hidden) 13 | } 14 | } 15 | 16 | render() { 17 | return null 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /custom modules/hide-chat/source code/src/views/lite/components/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | -------------------------------------------------------------------------------- /custom modules/hide-chat/source code/src/views/lite/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export { HideChat } from './components/HideChat' 4 | 5 | export class InjectedBelow extends React.Component { 6 | render() { 7 | return null 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /custom modules/hide-chat/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/hide-chat/source code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.shared.json", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /custom modules/number-picker/number-picker.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/number-picker/number-picker.tgz -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "number-picker", 3 | "fullName": "Number Picker Widget", 4 | "version": "1.0.0", 5 | "description": "Number 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 | } 17 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/src/actions/number-invalid-answer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @hidden true 3 | */ 4 | 5 | const key = args.randomId ? `skill-number-picker-invalid-count-${args.randomId}` : `skill-number-picker-invalid-count` 6 | const value = (temp[key] || 0) + 1 7 | temp[key] = value 8 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/src/backend/api.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | import { asyncMiddleware as asyncMw, BPRequest } from 'common/http' 3 | import { MODULE_NAME } from '.' 4 | 5 | export default async (bp: typeof sdk) => { 6 | const asyncMiddleware = asyncMw(bp.logger) 7 | 8 | const router = bp.http.createRouterForBot(MODULE_NAME, { checkAuthentication: false }) 9 | 10 | router.post( 11 | '/messenger', 12 | asyncMiddleware(async (req, res) => { 13 | const { botId } = req.params 14 | 15 | const moduleConfig = await bp.config.getModuleConfigForBot(MODULE_NAME, botId) 16 | if (!moduleConfig.enabled) { 17 | bp.logger.warn('Module is not enabled for bot') 18 | return res.sendStatus(200) 19 | } 20 | 21 | const { userId, conversationId, payload } = req.body 22 | await bp.users.getOrCreateUser('messenger', userId, botId) 23 | 24 | const event = bp.IO.Event({ 25 | botId, 26 | channel: 'messenger', 27 | direction: 'incoming', 28 | target: userId, 29 | threadId: conversationId.toString(), 30 | type: payload?.type, 31 | payload 32 | }) 33 | 34 | await bp.events.sendEvent(event) 35 | res.sendStatus(200) 36 | }) 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/src/backend/index.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | import api from './api' 3 | import numberPicker from './numberPicker' 4 | 5 | export const MODULE_NAME = 'number-picker' 6 | 7 | const onServerReady = async (bp: typeof sdk) => { 8 | await api(bp) 9 | } 10 | 11 | const skills: sdk.Skill[] = [ 12 | { 13 | id: 'NumberPicker', 14 | name: 'Number Picker', 15 | icon: 'numerical', 16 | flowGenerator: numberPicker.generateFlow 17 | } 18 | ] 19 | 20 | const entryPoint: sdk.ModuleEntryPoint = { 21 | onServerReady, 22 | skills, 23 | definition: { 24 | name: MODULE_NAME, 25 | menuIcon: 'numerical', 26 | fullName: 'Number Picker', 27 | homepage: 'https://botpress.io', 28 | noInterface: true, 29 | experimental: true 30 | } 31 | } 32 | 33 | export default entryPoint 34 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/src/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "cancel": "Cancel", 3 | "submit": "Submit", 4 | "thankYouClose": "Thank you! You can close this window." 5 | } 6 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/src/translations/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "cancel": "Annuler", 3 | "submit": "Confirmer", 4 | "thankYouClose": "Merci! Vous pouvez fermer cette fenêtre." 5 | } 6 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/src/views/full/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | export { NumberPicker } from './NumberPicker' 3 | 4 | export class LiteView extends React.Component { 5 | render() { 6 | return null 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/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/number-picker/source/number-picker/src/views/lite/WebPicker.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import Picker from './Picker' 3 | 4 | const WebPicker = props => { 5 | const [isHidden, setHidden] = useState(false) 6 | const { isLastGroup, isLastOfGroup, onSendData, minValue, maxValue, locale } = props 7 | 8 | useEffect(() => { 9 | if (isLastOfGroup && isLastGroup) { 10 | props.store.composer.setLocked(true) 11 | } 12 | }, []) 13 | 14 | const submit = async (value: string) => { 15 | hidePicker() 16 | onSendData?.({ type: 'numberPicker', text: value }) 17 | } 18 | 19 | const cancel = () => { 20 | hidePicker() 21 | onSendData?.({ type: 'numberPicker' }) 22 | } 23 | 24 | const hidePicker = () => { 25 | setHidden(true) 26 | props.store.composer.setLocked(false) 27 | } 28 | 29 | if (!(isLastGroup && isLastOfGroup) || isHidden) { 30 | return null 31 | } 32 | 33 | if (!locale) { 34 | return
35 | } 36 | 37 | return ( 38 | <> 39 | 40 | 41 | ) 42 | } 43 | 44 | export default WebPicker 45 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/src/views/lite/index.tsx: -------------------------------------------------------------------------------- 1 | import MessengerPicker from './MessengerPicker' 2 | import WebPicker from './WebPicker' 3 | 4 | export { WebPicker, MessengerPicker } 5 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/src/views/lite/style.scss: -------------------------------------------------------------------------------- 1 | /* Chrome, Safari, Edge, Opera */ 2 | input::-webkit-outer-spin-button, 3 | input::-webkit-inner-spin-button { 4 | -webkit-appearance: none; 5 | margin: 0; 6 | } 7 | 8 | /* Firefox */ 9 | input[type='number'] { 10 | -moz-appearance: textfield; 11 | } 12 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/src/views/lite/style.scss.d.ts: -------------------------------------------------------------------------------- 1 | // This file is automatically generated. 2 | // Please do not change this file! 3 | interface CssExports { 4 | 5 | } 6 | declare var cssExports: CssExports; 7 | export = cssExports; 8 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/src/views/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig_view.shared.json", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.shared.json", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /custom modules/number-picker/source/number-picker/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | -------------------------------------------------------------------------------- /custom modules/riva/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "riva", 3 | "fullName": "Riva", 4 | "version": "1.0.0", 5 | "description": "Leverage Riva Skills to handle ASR and TTS on your bot", 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 | "buildJs": "grpc_tools_node_protoc --proto_path=./riva_protos --js_out=import_style=commonjs,binary:src/riva_api --grpc_out=grpc_js:src/riva_api protos/riva_*.proto", 13 | "buildTs": "grpc_tools_node_protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --ts_out=./src/riva_api -I ./riva_protos ./riva_protos/*.proto" 14 | }, 15 | "author": "Botpress, Inc.", 16 | "license": "AGPL-3.0-only", 17 | "devDependencies": { 18 | "@types/node": "^11.0.1", 19 | "grpc-tools": "^1.11.2", 20 | "grpc_tools_node_protoc_ts": "^5.3.2" 21 | }, 22 | "dependencies": { 23 | "@botpress/messaging-client": "^1.2.0", 24 | "@grpc/grpc-js": "^1.6.6", 25 | "bluebird-global": "^1.0.1", 26 | "fs-extra": "^10.1.0", 27 | "google-protobuf": "^3.20.1-rc.1", 28 | "lodash": "^4.17.21", 29 | "multer": "^1.4.4", 30 | "tmp": "^0.2.1", 31 | "wav": "^1.0.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /custom modules/riva/riva.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botpress/solutions/af5a8cd42211ba79b415cf9974c39c054e38b430/custom modules/riva/riva.tgz -------------------------------------------------------------------------------- /custom modules/riva/src/backend/index.ts: -------------------------------------------------------------------------------- 1 | import 'bluebird-global' 2 | import * as sdk from 'botpress/sdk' 3 | import _ from 'lodash' 4 | import api from './api' 5 | import middleware from './middleware' 6 | 7 | const onServerReady = async (bp: typeof sdk) => { 8 | await api(bp) 9 | await middleware(bp) 10 | } 11 | 12 | const entryPoint: sdk.ModuleEntryPoint = { 13 | onServerReady, 14 | definition: { 15 | name: 'riva', 16 | fullName: 'Riva', 17 | homepage: 'https://botpress.com', 18 | noInterface: true 19 | } 20 | } 21 | 22 | export default entryPoint 23 | -------------------------------------------------------------------------------- /custom modules/riva/src/backend/middleware.ts: -------------------------------------------------------------------------------- 1 | import * as sdk from 'botpress/sdk' 2 | 3 | import { sendToUser } from './service' 4 | 5 | export default async (bp: typeof sdk) => { 6 | bp.events.registerMiddleware({ 7 | name: 'riva.outgoing', 8 | direction: 'outgoing', 9 | handler: async (event, next) => { 10 | if (event.channel !== 'riva') { 11 | return next(undefined, undefined, true) 12 | } 13 | 14 | await sendToUser(event, bp) 15 | 16 | return next(undefined, true) 17 | }, 18 | order: 5, 19 | description: 'Send audio messages' 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /custom modules/riva/src/config.ts: -------------------------------------------------------------------------------- 1 | export interface Config { 2 | /** 3 | * Server address, ex: 4 | * @default 127.0.0.1:50051 5 | */ 6 | serverAddress: string 7 | } 8 | -------------------------------------------------------------------------------- /custom modules/riva/src/riva_api/riva_audio_grpc_pb.js: -------------------------------------------------------------------------------- 1 | // GENERATED CODE -- NO SERVICES IN PROTO 2 | -------------------------------------------------------------------------------- /custom modules/riva/src/riva_api/riva_audio_pb.d.ts: -------------------------------------------------------------------------------- 1 | // package: nvidia.riva 2 | // file: riva_audio.proto 3 | 4 | /* tslint:disable */ 5 | /* eslint-disable */ 6 | 7 | import * as jspb from 'google-protobuf' 8 | 9 | export enum AudioEncoding { 10 | ENCODING_UNSPECIFIED = 0, 11 | LINEAR_PCM = 1, 12 | FLAC = 2, 13 | MULAW = 3, 14 | ALAW = 20 15 | } 16 | -------------------------------------------------------------------------------- /custom modules/riva/src/riva_api/riva_audio_pb.js: -------------------------------------------------------------------------------- 1 | // source: riva_audio.proto 2 | /** 3 | * @fileoverview 4 | * @enhanceable 5 | * @suppress {missingRequire} reports error on implicit type usages. 6 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 7 | * field starts with 'MSG_' and isn't a translatable message. 8 | * @public 9 | */ 10 | // GENERATED CODE -- DO NOT EDIT! 11 | /* eslint-disable */ 12 | // @ts-nocheck 13 | 14 | var jspb = require('google-protobuf') 15 | var goog = jspb 16 | var global = Function('return this')() 17 | 18 | goog.exportSymbol('proto.nvidia.riva.AudioEncoding', null, global) 19 | /** 20 | * @enum {number} 21 | */ 22 | proto.nvidia.riva.AudioEncoding = { 23 | ENCODING_UNSPECIFIED: 0, 24 | LINEAR_PCM: 1, 25 | FLAC: 2, 26 | MULAW: 3, 27 | ALAW: 20 28 | } 29 | 30 | goog.object.extend(exports, proto.nvidia.riva) 31 | -------------------------------------------------------------------------------- /custom modules/riva/src/views/full/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const View = () => { 4 | return
5 | } 6 | 7 | export default View 8 | -------------------------------------------------------------------------------- /custom modules/riva/src/views/lite/Composer.css: -------------------------------------------------------------------------------- 1 | .bpw-recorder-container { 2 | position: absolute; 3 | bottom: 47px; 4 | right: 60px; 5 | } 6 | 7 | #recorder { 8 | box-shadow: none; 9 | background-color: transparent; 10 | background-image: none; 11 | } 12 | 13 | .bpw-recorder-button-cancel { 14 | display: none; 15 | } 16 | 17 | .bpw-composer-textarea { 18 | background: #f0f0f0cc; 19 | border-radius: 33px; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .bpw-send-button { 25 | color: black; 26 | } 27 | 28 | .bpw-send-button:hover, 29 | .bpw-send-button:focus { 30 | background: none !important; 31 | color: gray; 32 | } 33 | 34 | .bpw-composer textarea::-webkit-scrollbar { 35 | display: none; 36 | } 37 | 38 | .bpw-composer textarea { 39 | -ms-overflow-style: none; 40 | scrollbar-width: none; 41 | padding-right: 82px; 42 | } 43 | 44 | #botpress-tooltip-1 { 45 | display: none; 46 | } 47 | 48 | #botpress-tooltip-37 { 49 | display: none; 50 | } 51 | -------------------------------------------------------------------------------- /custom modules/riva/src/views/lite/Composer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './Composer.css' 3 | 4 | import VoiceRecorder from './Recorder/VoiceRecorder' 5 | 6 | const Composer = props => { 7 | return ( 8 |
9 | { 11 | const blob = new Blob([buff], { type: 'audio/wav' }) 12 | const fd = new FormData() 13 | fd.append('file', blob, 'audio.wav') 14 | fd.append('webSessionId', props.store.api.baseUserPayload.webSessionId) 15 | fd.append('conversationId', props.store.currentConversationId) 16 | 17 | await props.store.bp.axios.post('/mod/riva/sendAudio', fd) 18 | }} 19 | > 20 |
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 | ![image](https://user-images.githubusercontent.com/104075132/209175855-f69c74ca-c5fa-41c3-bd59-0fab58ed05bf.png) 13 | 4. Add in the pertinent information for the skill and click insert 14 | ![image](https://user-images.githubusercontent.com/104075132/209175500-25c11650-b479-449e-8dc8-a2bac3366e09.png) 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 | image 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 | image 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 | image 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 | image 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 | image 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 | ![image](https://user-images.githubusercontent.com/13484138/205094343-6ba9dd23-1c06-4b68-86a5-d822d7aa0dd3.png) 9 | 10 | ![image](https://user-images.githubusercontent.com/13484138/205094240-95f6b7b0-6a69-4758-bbbd-4acc640fadc9.png) 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 | image 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 | ![image](https://user-images.githubusercontent.com/13484138/207772352-ab71a2ad-e88c-49ef-8a30-edb6f12055ed.png) 5 | ![image](https://user-images.githubusercontent.com/13484138/207772425-3ace40e0-9eb3-4917-9ab4-4aeb4098b793.png) 6 | ![image](https://user-images.githubusercontent.com/13484138/208509944-4ffe5053-dcea-445a-84ad-34a2e3f929d2.png) 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 | ![image](https://user-images.githubusercontent.com/13484138/150584573-96546ef9-d141-4649-948b-9139b4ba392b.png) 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 | ![image](https://user-images.githubusercontent.com/13484138/193856917-d50d2199-2e79-4955-a5ed-969c382887a3.png) 10 | 11 | 12 | 2 - Edit the bot.config.json file from the helper bot in the code editor to include your bots 13 | 14 | ![image](https://user-images.githubusercontent.com/13484138/193856180-9533d179-bee3-43c5-87bf-383fff4da02c.png) 15 | 16 | ![image](https://user-images.githubusercontent.com/13484138/193856380-c892366e-9ded-4a56-ba96-31552da95059.png) 17 | 18 | 3 - Talk with the helper bot 19 | 20 | ![image](https://user-images.githubusercontent.com/13484138/193856644-2aed8283-165e-47e8-a952-cf2535ccdc4c.png) 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 | --------------------------------------------------------------------------------