Botcopy Documentation
Now we're talking.
Dialogflow chatbots for websites, made easy & powerful.
Create a Dialogflow chatbot for web that has both brains & beauty.
What's below?
• Learn how Google Assistant responses work with Botcopy
• Learn how to use our features to boost engagement
You can count on us.
The Basics
Bot Prompts
Display relevant messaging to boost engagement.
Bot prompts make your website conversational by prompting your user to engage with your bot. Simply create your intents in Dialogflow and build prompts in Botcopy that trigger based on event or training phrase and the chosen URL.
Additional triggers coming soon!
*Make sure to remove https:// from the url input
Branding
White-labeled bots with advanced customization.
Chat window's colors, fonts, size, and full-screen functionality (CUI mode) can be found within a bot's branding tab.
CUI stands for Conversational User Interface. If you would like a full-screen chat experience when the users enter a specific URL make sure this toggle is on. The ability to minimize the screen will be removed. This behavior can be controlled at a bot level and prompt level.
AppendChild guide
The below allows you to customize the chat window with css by editing your existing snippet.
Note: In order to use this the Botcopy Snippet must be at the end of your <body>, not in <head>.
//Botcopy Snippet <script type="text/javascript" id="botcopy-embedder-d7lcfheammjct" class="botcopy-embedder-d7lcfheammjct" data-botId="BOT_ID_HERE" > var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = 'https://widget.botcopy.com/js/injection.js'; document.getElementById('botcopy-embedder-d7lcfheammjct').appendChild(s); //Attach new styles when shadow DOM is ready: window.addEventListener('load', function() { var botStyles = document.createElement('style'); // add custom styles, dont forget ";" botStyles.innerHTML = '.botcopy-class { background-color: rgba(0,0,0,0.0) !important; } .botcopy--avatar-image { margin-right: 40px !important; }'; // make sure everything has loaded, otherwise you'll get an undefined exception document.getElementById('botcopy-widget-root').shadowRoot.appendChild(botStyles); }); </script>
Select from our pre-designed color schemes or select your own colors within the greeter advanced section. Botcopy accepts both images and gif files for greeters.
Greeter vs Avatar
The greater is the chat icon displayed before clicking into the chat window.
The avatar is the icon within the chat that represents your company's agent.
Botcopy components support most unicode characters including emoji 📱. We also support the following markdown formatting:
Headers
# H1
## H2
### H3
#### H4
##### H5
###### H6
Alternatively, for H1 and H2, an underline-ish style:
Alt-H1
======
Alt-H2
------
H1
H2
H3
H4
H5
H6
Alternatively, for H1 and H2, an underline-ish style:
Alt-H1
Alt-H2
Emphasis
Emphasis, aka italics, with *asterisks* or _underscores_.
Strong emphasis, aka bold, with **asterisks** or __underscores__.
Combined emphasis with **asterisks and _underscores_**.
Strikethrough uses two tildes. ~~Scratch this.~~
Emphasis, aka italics, with asterisks or underscores.
Strong emphasis, aka bold, with asterisks or underscores.
Combined emphasis with asterisks and underscores.
Strikethrough uses two tildes. Scratch this.
Lists
Inline HTML should be used to display lists.
Ordered List
<ol> <li>First ordered list item</li> <li>Another item</li> </ol>
Unordered List
<ul> <li>Unordered item 1</li> <li>item 2</li> </ul>
Description List
dl> <dt>Coffee</dt> <dd>- black hot drink</dd> <dt>Milk</dt> <dd>- white cold drink</dd> </dl>
- First ordered list item
- Another item
- Unordered item 1
- item 2
- Coffee
- - black hot drink
- Milk
- - white cold drink
Links
There are two ways to create links.
[I'm an inline-style link](https://www.google.com)
[I'm an inline-style link to a phone number](tel:1112223333)
[I'm an inline-style link with title](https://www.google.com "Google's Homepage")
[I'm a reference-style link][Arbitrary case-insensitive reference text]
[I'm a relative reference to a repository file](../blob/master/LICENSE)
[You can use numbers for reference-style link definitions][1]
Or leave it empty and use the [link text itself].
URLs and URLs in angle brackets will automatically get turned into links.
http://www.example.com or <http://www.example.com> and sometimes
example.com (but not on Github, for example).
Some text to show that the reference links can follow later.
[arbitrary case-insensitive reference text]: https://www.mozilla.org
[1]: http://slashdot.org
[link text itself]: http://www.reddit.com
I'm an inline-style link to a phone number
I'm an inline-style link with title
I'm a relative reference to a repository file
You can use numbers for reference-style link definitions
Or leave it empty and use the link text itself.
URLs and URLs in angle brackets will automatically get turned into links. http://www.example.com or http://www.example.com and sometimes example.com (but not on Github, for example).
Some text to show that the reference links can follow later.
Images
Here's our logo (hover to see the title text):
Inline-style:

Reference-style:
![alt text][logo]
[logo]: https://www.botcopy.com/wp-content/uploads/2019/06/150x150purple.png "Logo Title Text 2"
Here's our logo (hover to see the title text):
Inline-style:
Reference-style:
Blockquotes
> Blockquotes can be used to emulate reply text.
> This line is part of the same quote.
Quote break.
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
Blockquotes can be used to emulate reply text. This line is part of the same quote.
Quote break.
This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can put Markdown into a blockquote.
Code and Syntax Highlighting
Inline `code` has `back-ticks around` it.
Inline code
has back-ticks around
it.
Blocks of code are fenced by lines with three back-ticks ```
.
Inline HTML
You can also use raw HTML in your Markdown, and it'll mostly work pretty well.
<div>
<div>The div with no name</div>
<div>Markdown in HTML</div>
<div>Does *not* work **very** well. Use HTML <em>tags</em>.</div>
</div>
- The div with no name
- Markdown in HTML
- Does *not* work **very** well. Use HTML tags.
Line Breaks
Our recommendation for learning how line breaks work is to experiment and discover -- hit <Enter> once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. Or use <br>. You'll soon learn to get what you want.
Here are some things to try out:
Here's a line for us to start with.
This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
This line is also a separate paragraph, but...
This line is only separated by a single newline, so it's a separate line in the *same paragraph*.
Here's a line for us to start with.
This line is separated from the one above by two newlines, so it will be a separate paragraph.
This line is also begins a separate paragraph, but...
This line is only separated by a single newline, so it's a separate line in the same paragraph.
Note: \n has legacy support for simple responses, cards and carousels but not for other components. It's recommended to use the two options above.
Menus provide options to your users. It can be used to cover the main flows of your bot and get your users back on track.
Build your menu in Dialogflow. Create an intent, set one of its training phrases to "menu" and add your chosen response. When a user clicks on menu on the window, this will trigger your menu intent.
Note: You may hide the menu by toggling it off in Window and Messages within Branding.
Connect
Integrate analytics, text-to-speech and live chat.
- Copy the generated embed snippet* from the connect page on your portal.
- Paste the embed snippet between <body> and </body> of your website.
- Your branded widget will appear in the bottom right of your website
*Botcopy generates a different embed snippet to work with Google's Tag Manager.
1. Connecting Botcopy to Janis
- Sign in to your Janis account
- Go to your account and click on Add Bot
- Name your bot
- Choose Botcopy from Bot Builder / Framework
- Select which Dialogflow agent to connect to
- Your completed setup should look something like this:
2. Copying your keys to Botcopy
- Copy your Janis API Key 🔑 and Janis Client Key 🔑
- Paste and enter keys into the Live chat fields found on the Connect page
3. Add Janis to Slack
- Add the Janis app to your Slack channel
- Follow the Janis onboarding to connect your AI from Dialogflow
4. Setting up your intents in Dialogflow
When will Janis notify my live chat agents in Slack?
Janis will notify you when:
- the fallback intent is triggered
- botcopy_human_takeover output context is set (lifespan: 1) on an intent
By providing a privacy policy url, Botcopy will automatically request consent from your users before they are able to engage with your bot. If you do not want this to occur, simply leave the field empty.
Note: Text-to-Speech (TTS) is currently only supported on Chrome & Edge due to limitations with other available browsers.
Step One: Select Project
- Go to your Google Cloud Console and click the circled drop down. We will be using ‘TESTME-’ for this example.
- Select your chosen Dialogflow Project from the list and click open at the bottom of the Dialog.
Note I’ve selected the ‘All’ tab.
Step Two: Billing
- Enable Billing for the project
Step Three: Credentials
- Set up the Text-to-Speech API
- Select your project ID and click Continue
- Then, click ‘Go to credentials’
- Ensure that you’ve selected ‘Cloud Text-to-Speech API’ in the drop down, then click the
blue hyperlink to ‘API key’
- Name the API key
- Whitelist your website and Botcopy under Application restrictions.
- *.botcopy.com/* - must be listed for TTS to display and work on the Botcopy portal
Here are some examples of URLs that you can allow to set up a referrer
-
- A specific URL with an exact path: www.example.com/path
- Any URL in a single domain with no subdomains, using a wildcard asterisk (*): example.com/*
- Any URL in a single subdomain, using a wildcard asterisk (*): sub.example.com/*
- Any subdomain or path URLs in a single domain, using wildcard asterisks (*): *.example.com/*
- A URL with a non-standard port: www.example.com:8000/*
- Set the API restriction to Cloud Text-to-Speech API only, then click ‘Create
- Copy the key to your clipboard
Step Four: Copy key to Botcopy
- Head to portal.botcopy.com and select the bot associated with the Dialogflow agent
project ID you set up the billing and API key for - Paste the copied API key into the field labeled ‘Google TTS API Key’ and press Enter
- A drop down will appear with Gender options for the voice
Additional notes:
- The TTS language will be the agent's language already conversing with the end user.
- Google's Cloud Text-to-Speech allows 4 million characters free / month, and it's $4.00 USD for each one million characters after.
Customize Audio Output (Only available in Simple Responses)
- Use customize audio output to offer emphasis and alter spoken words.
- If there is no customize audio output, all text received in a simple response will be spoken by your agent.
Components
Modular control for custom chat window experiences.
To disable the input bar and force users to make a selection (suggestion chips, card buttons etc), add botcopy-disable-inputbar as a context to an intent.
The Feedback component is used to collect feedback from a user. Positive/Negative feedback with attached comments are displayed on the Feedback page of the Portal.
Use the following triggers to display the Feedback component:
Context
botcopy-feedback
Set End of Conversation
When 'Set this intent as end of conversation' is toggled on in an intent
Window Trigger
Botcopy.showFeedback()
Note: Can be triggered through your bot or website.
Below is an example of how to trigger a webview from an intent through fulfillment and providing a response in case the user closes the webview. Uses the actions-on-google
package.
// this example uses a Basic Card from 'actions-on-google' // you'll need to deconstruct the BasicCard and Button classes from the actions-on-google package const { BasicCard, Button } = require('actions-on-google') function LitePackageButtonFunction(agent) { // set your agent's requestSource to 'ACTIONS_ON_GOOGLE' agent.requestSource = 'ACTIONS_ON_GOOGLE'; // set a context that is passed to Botcopy to trigger your webview const urlContext1 = { name: 'openWebview', lifespan: 1, parameters: { hideBrowserTab: false, webViewTarget: 'self', webViewUrl: 'https://yourwebsite.com/page' } }; //If using multiple webviews, set a unique context per intent agent.context.set(urlContext1); // declare a variable conv (this can be a global variable or within an intent response function) const conv = agent.conv(); conv.ask("Opening a webview!"); conv.ask(new BasicCard({ buttons: [new Button({ title: 'Button Title', url: 'https://yourwebsite.com/page' })], title: 'title text' })); // add your conv variable to your agent to send your responses to your botcopy widget agent.add(conv); }
Google Assistant Responses
Built within intents.
A simple response displays as a regular text file triggered from a users intent. If you would like to add buttons to this text add a new response type 'suggestion chips'.
Note: If your agent is setup with default responses, you can toggle on "Use Response from the DEFAULT tab as the first response. *Be careful as it will only accept the first response. It is recommended to use Google Assistant responses for rich responses and a consistent experience if additional channels are added at a later stage.
Suggestion chips are suggested options for your users to choose from. Add them strategically to help your customers navigate your chat more easily.
Link-out Suggestions display a button that links out to a URL.
Basic cards can be used for images, gifs, videos and files by placing the URL in the image URL field. Cards typically contain a title, text, and a button.
- Default - open link in chat window
- To open the link in a new tab, add ?nt=t to the end of your url: https://botcopy.com?nt=t
- To open the link in the same tab, add ?nt=f to the end of your url: https://botcopy.com?nt=f
Images: 16:9 ratio
Videos:
Paste the video URL into the top input field where it says "Enter Image URL.". Currently, Botcopy accepts Dropbox URLs.
IMPORTANT: You must change the "www" to "dl". Please see an example of this change below.
Original URL
https://www.dropbox.com/s/49cj0a442kxig1d/FRont%20page%20ad%20%2887060D21-08F8-4195-9F09-340CA2A64C3F%29.mp4?dl=0
Botcopy accepted
https://dl.dropbox.com/s/49cj0a442kxig1d/FRont%20page%20ad%20%2887060D21-08F8-4195-9F09-340CA2A64C3F%29.mp4?dl=0
For a carousel with selections that link to web pages, a Browse carousel card is recommended. These let users click on a selection in the carousel to link out to a url in their browser. Carousel cards only link to other intents within your Dialogflow agent.
Browse Carousel Cards as a Google Assistant response are under development by Dialogflow. However, it is possible to display Browse Carousel Cards through fulfillment.
Dialogflow requires you to add a simple response* and a minimum of 2 list items to Lists. Botcopy supports a List header, List item titles, List item descriptions, and images for each List item. Below is an image of what they look like within our chat.
*You can display a List by itself by toggling the ON option next to 'Use responses from the DEFAULT tab as the first responses' in your Dialogflow console.
Table cards are still under development by Dialogflow. We will update these docs when this component has been updated.
Media Content was designed mainly for audio files. However, we are working on making them work for images and video as well. Don't forget to convert the url to mp3 or mp4.
If you'd like to send just the video, image or file without simple response or suggestion chips you can use a custom payload.
Fulfillment Responses
Talk with systems and customize responses with code.
Dialogflow Requirements:
agent.requestSource = agent.ACTIONS_ON_GOOGLE; is required to precede const conv = agent.conv(); for your response payloads.
When using the actions of google package, ensure your dependencies are up to date. "dialogflow-fulfillment" should be at least version ^0.6.1.
Note: When creating a new agent on Dialogflow, the package.json that is generated does not use the latest dependencies.
package.json example with updated dependencies:
{ "name": "dialogflowFirebaseFulfillment", "description": "This is a Botcopy example for dependencies for a Dialogflow agents using Cloud Functions for Firebase", "version": "0.0.1", "private": true, "license": "Apache Version 2.0", "author": "Google Inc.", "engines": { "node": "6" }, "scripts": { "start": "firebase serve --only functions:dialogflowFirebaseFulfillment", "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment" }, "dependencies": { "actions-on-google": "^2.5.0", "firebase-admin": "^6.4.0", "firebase-functions": "^2.1.0", "dialogflow": "^4.0.3", "dialogflow-fulfillment": "^0.6.1" } }
For reference, here is the default fulfillment package.json generated by Dialogflow.
{ "name": "dialogflowFirebaseFulfillment", "description": "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase", "version": "0.0.1", "private": true, "license": "Apache Version 2.0", "author": "Google Inc.", "engines": { "node": "8" }, "scripts": { "start": "firebase serve --only functions:dialogflowFirebaseFulfillment", "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment" }, "dependencies": { "actions-on-google": "^2.2.0", "firebase-admin": "^5.13.1", "firebase-functions": "^2.0.2", "dialogflow": "^0.6.0", "dialogflow-fulfillment": "^0.5.0" } }
Simple Responses are text responses from your agent. They are required by Google to precede a rich response (Suggestion Chips, Carousels, Browse Carousels etc)
function simpleResponse(agent) { agent.requestSource = "ACTIONS_ON_GOOGLE"; let conv = agent.conv(); conv.ask('This is a simple response from fulfillment.'); agent.add(conv); }
Suggestion Chips offer options to users.
Here is a working example for a Suggestion Chips using the actions-on-google
package.
// for this example, you must require the 'actions-on-google' library // deconstruct the Suggestions class from the library at the top of your document const { Suggestions } = require('actions-on-google') function suggestions(agent) { agent.requestSource = "ACTIONS_ON_GOOGLE"; let conv = agent.conv(); conv.ask('This is a simple response from fulfillment.'); //adds a single suggestion chip conv.ask(new Suggestions('Suggestion Chips')); //adds multiple suggestion chips conv.ask(new Suggestions(['suggestion 1', 'suggestion 2'])); agent.add(conv); }
Link-out Suggestions display a button that links out to a URL.
Here is a working example for a Link Out Suggestion using the actions-on-google
package.
// for this example, you must require the 'actions-on-google' library // deconstruct the LinkOutSuggestion class from the library at the top of your document const { LinkOutSuggestion } = require('actions-on-google') function linkOut(agent) { agent.requestSource = agent.ACTIONS_ON_GOOGLE; const conv = agent.conv(); conv.ask('Simple responses are required before a rich response'); conv.ask( new LinkOutSuggestion({ name: 'Title of Link Out', url: 'https://botcopy.com' }) ); agent.add(conv); }
Here is an example on adding a Basic Card in fulfillment with the actions-on-google
package
// deconstruct the Basic Card and Button classes from the library at the top of your document const { BasicCard, Button, Image } = require('actions-on-google') function aogBasicCard(agent) { agent.requestSource = agent.ACTIONS_ON_GOOGLE; const conv = agent.conv(); conv.ask('Here is a basic card through fulfillment'); conv.ask( new BasicCard({ buttons: [ new Button({ title: 'Test Button', url: 'https://botcopy.com' }), new Button({ title: 'Test Button 2', url: 'https://botcopy.com' }) ], text: 'Test formatted text', image: new Image({ url: 'https://cdn-images-1.medium.com/max/1200/1*s-JvYKXGEnYPdyIgT0w7gw.png', alt: 'Bot banner' }), subtitle: 'Test subtitle', title: 'Test title' }) ); agent.add(conv); }
Below is an example of how to add a carousel as a response in fulfillment using the actions-on-google
package. We recommend using this package over the dialogflow-fulfillment
package.
// for this example, you must require the 'actions-on-google' library // deconstruct the Carousel and Image classes from the library at the top of your document // example: const { Carousel, Image } = require('actions-on-google') function fulfillmentCarousel(agent) { // set your agent's requestSource to 'ACTIONS_ON_GOOGLE' agent.requestSource = agent.ACTIONS_ON_GOOGLE; // declare a variable conv (this can be a global variable or within an intent response function) let conv = agent.conv(); // conv.ask your responses // this is a simple text response conv.ask('This is a carousel example.'); // this is a carousel response conv.ask( new Carousel({ // items in your carousel items: { // option key name - sent as a response when a user clicks on this card SELECTION_KEY_ONE: { synonyms: ['synonym 1', 'synonym 2', 'synonym 3'], // title of your carousel item title: 'Title of First Carousel Item', // description of your item description: 'This is a description of a carousel item.', // an image associated with a card. note the new 'Image' class we imported image: new Image({ url: 'IMG_URL_AOG.com', alt: 'Image alternate text' }) }, // second carousel item SELECTION_KEY_GOOGLE_HOME: { synonyms: ['Google Home Assistant', 'Assistant on the Google Home'], title: 'Google Home', description: 'Google Home is a voice-activated speaker powered by ' + 'the Google Assistant.', image: new Image({ url: 'IMG_URL_GOOGLE_HOME.com', alt: 'Google Home' }) }, // third carousel item SELECTION_KEY_GOOGLE_PIXEL: { synonyms: ['Google Pixel XL', 'Pixel', 'Pixel XL'], title: 'Google Pixel', description: 'Pixel. Phone by Google.', image: new Image({ url: 'IMG_URL_GOOGLE_PIXEL.com', alt: 'Google Pixel' }) } } }) ); // add your conv variable to your agent // this sends all your responses to your botcopy widget agent.add(conv); }
Below is an example of how to add a carousel as a response in fulfillment using the dialogflow-fulfillment package.
// for this example, you must require the 'dialogflow-fulfillment' library // deconstruct the Payload class from the library at the top of your document // example: const { Payload } = require('dialogflow-fulfillment') function fulfillmentPayload(agent) { // set your agent's requestSource to 'ACTIONS_ON_GOOGLE' agent.requestSource = 'ACTIONS_ON_GOOGLE'; // use agent.add and the new Payload class to send a raw JSON Carousel to botcopy agent.add( new Payload(agent.ACTIONS_ON_GOOGLE, { expectUserResponse: true, richResponse: { items: [ { simpleResponse: { textToSpeech: 'Choose a item' } } ] }, systemIntent: { intent: 'actions.intent.OPTION', data: { '@type': 'type.googleapis.com/google.actions.v2.OptionValueSpec', carouselSelect: { items: [ { optionInfo: { key: 'first title' }, description: 'first description', image: { url: 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png', accessibilityText: 'first alt' }, title: 'first title' }, { optionInfo: { key: 'second' }, description: 'second description', image: { url: 'https://lh3.googleusercontent.com/Nu3a6F80WfixUqf_ec_vgXy_c0-0r4VLJRXjVFF_X_CIilEu8B9fT35qyTEj_PEsKw', accessibilityText: 'second alt' }, title: 'second title' } ] } } } }) ); }
Here is an example on adding a Browse Carousel Card in fulfillment with the actions-on-google
package
Note: To open links in a new tab versus within the chat window, add ?nt=t at the end of your url: https://botcopy.com?nt=t
// deconstruct the BrowseCarousel, BrowseCarouselItem and Image classes from the library at the top of your document const { BrowseCarousel, BrowseCarouselItem, Image } = require('actions-on-google') function browseCarousel(agent) { agent.requestSource = "ACTIONS_ON_GOOGLE"; let conv = agent.conv(); conv.ask('This is a browse carousel example from fulfillment.'); // Create a browse carousel conv.ask(new BrowseCarousel({ items: [ new BrowseCarouselItem({ title: 'Title of item 1', url: 'https://botcopy.com', description: 'Description of item 1', image: new Image({ url: 'https://www.botcopy.com/wp-content/uploads/2019/01/Screen-Shot-2019-01-27-at-3.37.28-PM.png', alt: 'Image alternate text', }), footer: 'Item 1 footer', //Supported in next update }), new BrowseCarouselItem({ title: 'Title of Item 2', url: 'https://botcopy.com', description: 'Description of Item 2', image: new Image({ url: 'https://www.botcopy.com/wp-content/uploads/2019/01/Screen-Shot-2019-01-25-at-10.31.54-AM.png', alt: 'Image alternate text', }), footer: 'Item 2 footer', }), ], })); agent.add(conv); }
Below is an example of how to add a List in fulfillment using the actions-on-google
package.
// this example uses the List class from 'actions-on-google' // the structure of a list is similar to a carousel // you'll need to deconstruct the List and Image classes from the actions-on-google package const { List, Image } = require('actions-on-google') function list(agent) { // set your agent's requestSource to 'ACTIONS_ON_GOOGLE' agent.requestSource = agent.ACTIONS_ON_GOOGLE; // declare a variable conv (this can be a global variable or within an intent response function) let conv = agent.conv(); // conv.ask your responses // this is a simple text response conv.ask('This is a list example.'); // this is a list response conv.ask( new List({ // one main difference between a list and a carousel is the title // this title is optional. if you choose to include one, it will be rendered as a header over the list title: 'List Title', items: { // the fields within a list mirror those in a carousel SELECTION_KEY_ONE: { synonyms: ['synonym 1', 'synonym 2', 'synonym 3'], title: 'Title of First List Item', description: 'This is a description of a list item.', // note the image field uses the Image class like carousels image: new Image({ url: 'IMG_URL_AOG.com', alt: 'Image alternate text' }) }, // Add the second item to the list SELECTION_KEY_GOOGLE_HOME: { synonyms: ['Google Home Assistant', 'Assistant on the Google Home'], title: 'Google Home', description: 'Google Home is a voice-activated speaker powered by ' + 'the Google Assistant.', image: new Image({ url: 'IMG_URL_GOOGLE_HOME.com', alt: 'Google Home' }) }, // Add the third item to the list SELECTION_KEY_GOOGLE_PIXEL: { synonyms: ['Google Pixel XL', 'Pixel', 'Pixel XL'], title: 'Google Pixel', description: 'Pixel. Phone by Google.', image: new Image({ url: 'IMG_URL_GOOGLE_PIXEL.com', alt: 'Google Pixel' }) } } }) ); // add your conv variable to your agent // this sends all your responses to your botcopy widget agent.add(conv); }
Fulfillment examples
These are just a few we use there are plenty more. Enjoy.
Dialogflow Requirements:
agent.requestSource = agent.ACTIONS_ON_GOOGLE; is required to precede const conv = agent.conv(); for your response payloads.
When using the actions of google package, ensure your dependencies are up to date. "dialogflow-fulfillment" should be at least version ^0.6.1.
Note: When creating a new agent on Dialogflow, the package.json that is generated does not use the latest dependencies.
package.json example with updated dependencies:
{ "name": "dialogflowFirebaseFulfillment", "description": "This is a Botcopy example for dependencies for a Dialogflow agents using Cloud Functions for Firebase", "version": "0.0.1", "private": true, "license": "Apache Version 2.0", "author": "Google Inc.", "engines": { "node": "6" }, "scripts": { "start": "firebase serve --only functions:dialogflowFirebaseFulfillment", "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment" }, "dependencies": { "actions-on-google": "^2.5.0", "firebase-admin": "^6.4.0", "firebase-functions": "^2.1.0", "dialogflow": "^4.0.3", "dialogflow-fulfillment": "^0.6.1" } }
For reference, here is the default fulfillment package.json generated by Dialogflow.
{ "name": "dialogflowFirebaseFulfillment", "description": "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase", "version": "0.0.1", "private": true, "license": "Apache Version 2.0", "author": "Google Inc.", "engines": { "node": "8" }, "scripts": { "start": "firebase serve --only functions:dialogflowFirebaseFulfillment", "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment" }, "dependencies": { "actions-on-google": "^2.2.0", "firebase-admin": "^5.13.1", "firebase-functions": "^2.0.2", "dialogflow": "^0.6.0", "dialogflow-fulfillment": "^0.5.0" } }
const sgMail = require('@sendgrid/mail'); process.env.DEBUG = 'dialogflow:debug'; process.env.SENDGRID_API_KEY = 'INSERT SENDGRID API KEY HERE', //Create NodeJS API Key in Sendgrid function sendEmail(agent) { sgMail.setApiKey(process.env.SENDGRID_API_KEY); const emailParam = agent.parameters.email; const msg = { to: emailParam, from: 'email.botcopytest@gmail.com', subject: 'Looks like we missed you. You probably caught us sleeping.', text: 'Hi, Keystroke (our bot) just sent us your email to follow up. How can we help you today?', //html: '', }; sgMail.send(msg); agent.add('Got it. Thank you! I will make sure our team reaches out to you soon. You are free to continue to hold. Feel free to ask me any questions while you wait.'); } let intentMap = new Map(); intentMap.set('email_Capture', sendEmail); agent.handleRequest(intentMap); }); }
Language Settings
Speak the user's language.
Botcopy’s widget supports multilingual agents based on the preferred language in each of your end users' browser. To enable localization in Dialogflow, find the “Languages” tab in your agent's settings and add the languages you desire from the ones Google currently makes available. Once the alternate intents, entities, training phrases and fulfillment responses are built out in your Dialogflow agent, the language on your widget will display the appropriate language.
See video for details:
Augment your Website
Contextualize the bot
Understand and route users to their end goals, and yours.
Use case example
You manage a real estate company and own 5 apartment buildings.
Note: Both features can be used in parallel. If used in parallel, the botcopy-ref-context will have two parameters.
Ref parameter detection
The url ref parameter feature is for customizing a single bot for different individual users. So let's say you have an email campaign with buttons that leads to each available apartment depending on your user's preferences.
Each button's url has a unique ref parameter of www.realestatehomes.com?ref=customer1
Botcopy's widget will recognize this ref and pass it as a parameter on the botcopy-ref-context for a lifespan of 2. customer1 can then be used to trigger the flow with apartment1's details as well as customer1's first and last name.
See implementation in Ref Parameter Detection
data-snippetRef (Custom Snippet Parameter)
When you add a data-snippetRef field to your snippet, the value is static per page.
Using the example above, imagine you have a few pages listed as below:
www.realestatehomes.com/apartment1
www.realestatehomes.com/apartment2
www.realestatehomes.com/apartment3
A user of yours is on www.realestatehomes.com/apartment1 and would like to request a maintenance check.
The chatbot will respond knowing that the maintenance check refers to apartment 1. That's because you've copied your Botcopy snippet into this particular page in the BODY, and added a data-snippetRef field to the snippet.
In this case, the field is data-snippetRef="apartment1"
The widget detects this and passes the value of apartment1 to your agent. Now your agent can know which apartment the user is inquiring about without asking!
This is what the snippet should look like once you've manually added it to the page for Apartment 1 in the example above:
<script type="text/javascript" id="botcopy-embedder-d7lcfheammjct" class="botcopy-embedder-d7lcfheammjct" data-botId="yourBotID" data-snippetRef="apartment1" > var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = 'https://widget.botcopy.com/js/injection.js'; document.getElementById('botcopy-embedder-d7lcfheammjct').appendChild(s); </script>
You'd paste this same snippet into any page that requires a custom snippet parameter, but you'd change the data-snipperRef value per page, e.g., apartment2, apartment3, etc.
See implementation in Custom Snippet Parameters
To pass data from URLs, you can use our ref parameter detection feature. The Botcopy widget will detect the value from your ref key and set a context on your agent.
Typical use cases:
- Linking marketing campaign to specific users
- Referencing specific products
- Displaying relevant prompt based on user
Example Flow:
- User receives email
- User clicks email with link www.botcopy.com?ref=customer1
- Widget detects ref=customer1
- Assigns customer1 as a parameter to context 'botcopy-ref-context' (the name will always be botcopy-ref-context and has a lifespan of 2)
In the example below we setup a google sheet api using SheetLabs.
Example function using actions-on-google
package:
function refTestConv(agent) { const contextName = 'botcopy-ref-context'; // 'botcopy-ref-context' is the context we assign the value from your ref key agent.requestSource = 'ACTIONS_ON_GOOGLE'; const conv = agent.conv(); conv.ask('Testing the Conv ref'); const botcopyRefContext = agent.context.get(contextName); console.log('botcopy-ref-context', botcopyRefContext); if (!botcopyRefContext || !botcopyRefContext.parameters || !botcopyRefContext.parameters.botcopyRefValue) { conv.ask('botcopyRefContext.parameters.botcopyRefValue is empty!'); return; // If the ref is undefined show a different response } const { parameters: { botcopyRef, botcopyRefValue } } = botcopyRefContext; // Optional to peel off a part of the value const botcopyRefValueCleaned = botcopyRefValue.replace('customer', ''); conv.ask(`sent ref was: ${botcopyRefValue} and cleaned: ${botcopyRefValueCleaned}`); return pullFromSheetlabs(botcopyRefValueCleaned) .then((parameters) => { // once we have pulled the information, set a new context // new userContext has information pulled from db conv.contexts.set('userContext', 100, parameters); const userContext = conv.contexts.output.userContext; console.log('userContext', userContext); if (!userContext) { throw new Error('userContext not found'); } const { parameters: { firstName, lastName, email } } = userContext; console.log('firstlastemail', firstName, lastName, email); conv.ask(`Hi, ${firstName} ${lastName}, is ${email} still your current email address?`); }) .catch((e) => { conv.ask(`could not load data from sheets! error: ${e.message}`); }) .then(() => { // end conversation and send to user agent.add(conv); }); } //Function referencing context parameters and pulling data from sheet to display function pullFromSheetlabs(id) { // pull data from salesforce / crm / database with ref variable // we used sheetlabs to create a google sheet api // the following refs correlate with records in the sheet // 5024 - Matthew Porter matt@matt.com // 7482 - Alex Seegers alex@alex.com // 1337 - Phil Holly phil@phil.com const url = `https://sheetlabs.com/SUNS/reftest?ref=${id}`; // const url = `https://sheetlabs.com/SUNS/reftest?ref=5024`; return axios.get(url).then((res) => { console.log('res.data', res.data); const parameters = {}; parameters.firstName = res.data[0].firstName; parameters.lastName = res.data[0].lastName; parameters.email = res.data[0].email; console.log('parameters post axios', parameters); return parameters; }).catch((e) => { console.log('axios error', e); throw e; }); }
This feature enables you to pass a parameter from your Botcopy snippet on a specific page, to your agent so that you can trigger the relevant intent.
If you need help getting your head around this concept, please see "What's the difference between ref parameter and custom snippet parameters?" above for an in-depth example use case.
This is what your snippet should look like once you've manually added data-snippetRef="yourCustomValue":
<script type="text/javascript" id="botcopy-embedder-d7lcfheammjct" class="botcopy-embedder-d7lcfheammjct" data-botId="yourBotID" data-snippetRef="yourCustomValue" > var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = 'https://widget.botcopy.com/js/injection.js'; document.getElementById('botcopy-embedder-d7lcfheammjct').appendChild(s); </script>
Below is an example function to be used in Dialogflow's fulfillment.
Here we are peeling off the parameters from botcopy-ref-context
function snippetTest(agent) { agent.requestSource = agent.ACTIONS_ON_GOOGLE // 'botcopy-ref-context' has the passed values // it has a lifespan of 2 intents const context = agent.context.get('botcopy-ref-context') // We're destructuring the value of botcopySnippetRefValue off the context const { parameters: { botcopySnippetRefValue } } = context // Here you would use botcopySnippetRefValue within an HTTP call, // create a new context with the response, // and reference that context throughout the conversation, // but for this example we're just displaying the value sent. // See the query ref implementation for an example of using the HTTP call. agent.add(`Snippet value = ${botcopySnippetRefValue}`) }
Window Triggers
Website control of the bot.
Below is a button that opens the chat window
<button onclick="Botcopy.openWindow()">Opens Chat Window</button>
Below is a button that closes the chat window
<button onclick="Botcopy.closeWindow()">Closes Chat Window</button>
Below is a button that sends an event and opens the prompt
<button onclick="triggerPrompt()">Opens Prompt</button>
Here is the function referenced in onclick
function triggerPrompt() { Botcopy.sendEvent('sendEvent'); Botcopy.openPrompt(); }
Note: This is intended to be used with a simple response. Rich media is currently under development.
Below is a button that closes the bot prompt.
<button onclick="Botcopy.closePrompt()">Closes Prompt</button>
Below is a button that sends an event and opens the window
<button onclick="eventOpen()">Send event and open chat window</button>
Here is the eventOpen function referenced in onclick:
function eventOpen () { Botcopy.sendEvent('sendEvent'); Botcopy.openWindow(); }
@param eventName: string - will be sent as an event to Dialogflow
@param eventContext (optional)
{
name: string
lifespanCount: number
parameters: { key: value }
}
Botcopy.sendEvent(...)
Here is an example of sending a context with sendEvent:
Botcopy.sendEvent("set-id", { name: "context-set-id", lifespanCount: 4, parameters: { id: "1234" } })
Note: The event name and context name must be different.
Intent Setup
Below is a button that silently (does not remove previous suggestion chips) sends an event and opens the window.
<button onclick="eventSilentOpen()">Silently send event and open chat window</button>
Here is the eventSilentOpen function referenced in onclick:
function eventSilentOpen () { Botcopy.sendEventSilent('sendEventSilent'); Botcopy.openWindow(); }
@param eventName: string - will be sent as an event to Dialogflow
@param eventContext (optional)
{
name: string
lifespanCount: number
parameters: { key: value }
}
Botcopy.sendEventSilent(...)
Here is an example of sending a context with sendEventSilent:
Botcopy.sendEvent("set-id", { name: "context-set-id", lifespanCount: 4, parameters: { id: "1234" } }) // Note: The event name and context name must be different.
Below is a button that sends text and opens the window
<button onclick="textOpen()">Send text & open</button>
Here is the function referenced in onclick:
function textOpen () { Botcopy.sendText('Text from button', true); Botcopy.openWindow(); }
@param text: string - will be sent as text to Dialogflow
@param isUserMessage: boolean - default: false;
if true: the text will appear as a user message, otherwise it will be invisible
@param eventContext (optional)
{
name: string
lifespanCount: number
parameters?: [{ key: value }]
}
Botcopy.sendText(...)
Intent Setup
Below is a button that opens the window and shows the feedback box
<button onclick="showFeedbackBox()">Show Feedback</button>
Here is the function referenced in onclick
function showFeedbackBox() { Botcopy.openWindow(); Botcopy.showFeedback(); }
Below is a button that hides the feedback box.
<button onclick="Botcopy.hideFeedback()">Hide Feedback</button>
Below is a button that opens the window and clears the chat history
<button onclick="clearHistoryTrigger()">Open window & clear history</button>
Here is the function referenced in onclick:
function clearHistoryTrigger () { Botcopy.openWindow(); Botcopy.clearHistory(); }
Window Events
Detect chat window activity.
The chat window triggers a variety of events that you can create event listeners for.
To add an event listener for the Chat Window, add the following Javascript code:
window.addEventListener('botcopy-events', function (e) { // Handle event });
To add a switch for different botcopy events you can do the following:
window.addEventListener('botcopy-events', function (e) { switch(e.detail.type) { case 'bc-tts-on': // Handle event break; } });
This event occurs when the chat window has initialized. The event structure looks like the following:
type: "bc-initialized", payload: {}
This event occurs when a live agent enters the chat. The event structure looks like the following:
type: "bc-agent-entered-chat", payload: { avatarURL: "string", humanName: "string" }
This event occurs when a live agent leaves the chat. The event structure looks like the following:
type: "bc-agent-left-chat", payload: { avatarURL: "string", humanName: "string" }
This event occurs when a message is sent from a live agent to a user. The event structure looks like the following:
type: "bc-agent-message-sent", payload: { message: "string" }
This event occurs when a button on a card is clicked by the user. The event structure looks like the following:
type: "bc-button-clicked", payload: { input: "string", url: "string" }
This event occurs when a card in a carousel or browse carousel is clicked by the user. The event structure looks like the following:
type: "bc-card-clicked", payload: { input: "string" }
This event occurs when a suggestion chip is clicked by the user. The event structure looks like the following:
type: "bc-chip-clicked", payload: { input: "string" }
This event occurs when link-out suggestion is clicked by the user. The event structure looks like the following:
type: "bc-link-out-clicked", payload: { url: "string" }
This event occurs when an element on the list is clicked by the user. The event structure looks like the following:
type: "bc-list-element-clicked", payload: { input: "string" }
This event occurs when the chat history is cleared. The event structure looks like the following:
type: "bc-history-cleared", payload: {}
This event occurs when text-to-speech is turned on by the user. The event structure looks like the following:
type: "bc-tts-on", payload: {}
This event occurs when text-to-speech is turned off by the user. The event structure looks like the following:
type: "bc-tts-off", payload: {}
This event occurs when the feedback box is opened. The event structure looks like the following:
type: "bc-feedback-open", payload: {}
This event occurs when the feedback box is closed. The event structure looks like the following:
type: "bc-feedback-close", payload: {}
This event occurs when the webview component is open. The event structure looks like the following:
type: "bc-webview-open", payload: {}
This event occurs when the webview component is closed and the user returns to the chat. The event structure looks like the following:
type: "bc-webview-close", payload: {}
This event occurs when the chat window is opened. The event structure looks like the following:
type: "bc-window-open", payload: {}
This event occurs when the chat window is closed. The event structure looks like the following:
type: "bc-window-close", payload: {}