├── .gitignore ├── screenshots ├── 1.png ├── 2.png ├── 3.png ├── 4.png └── 5.png ├── client ├── public │ ├── bot.png │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── src │ ├── assets │ │ ├── open-ended.mp3 │ │ └── app.css │ ├── setupProxy.js │ ├── components │ │ ├── chatbot │ │ │ ├── Message.js │ │ │ ├── Card.js │ │ │ └── Chatbot.js │ │ ├── App.js │ │ └── Info.js │ ├── index.js │ └── serviceWorker.js ├── .gitignore └── package.json ├── config ├── keys.js └── prod.js ├── routes ├── fulfillment.js └── dialogFlow.js ├── README.md ├── package.json ├── index.js └── chatbot ├── chatbot.js └── structjson.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | config/dev.js 3 | my-chat-bot-3ca01-e3c8fba8580b.json -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tanish0019/React-Chatbot/HEAD/screenshots/1.png -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tanish0019/React-Chatbot/HEAD/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tanish0019/React-Chatbot/HEAD/screenshots/3.png -------------------------------------------------------------------------------- /screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tanish0019/React-Chatbot/HEAD/screenshots/4.png -------------------------------------------------------------------------------- /screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tanish0019/React-Chatbot/HEAD/screenshots/5.png -------------------------------------------------------------------------------- /client/public/bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tanish0019/React-Chatbot/HEAD/client/public/bot.png -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tanish0019/React-Chatbot/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/src/assets/open-ended.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tanish0019/React-Chatbot/HEAD/client/src/assets/open-ended.mp3 -------------------------------------------------------------------------------- /config/keys.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === "production") { 2 | module.exports = require("./prod"); 3 | } 4 | else { 5 | module.exports = require("./dev"); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /client/src/setupProxy.js: -------------------------------------------------------------------------------- 1 | const proxy = require("http-proxy-middleware"); 2 | 3 | module.exports = function(app) { 4 | app.use(proxy("/api/*", { target: "http://localhost:5000/" })); 5 | }; 6 | -------------------------------------------------------------------------------- /config/prod.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | googleProjectID: process.env.GOOGLE_PROJECT_ID, 3 | dialogFlowSessionID: process.env.DIALOGFLOW_SESSION_ID, 4 | dialogFlowSessionLanguageCode: process.env.DIALOGFLOW_LANGUAGE_CODE, 5 | googleClientEmail: process.env.GOOGLE_CLIENT_EMAIL, 6 | googlePrivateKey: JSON.parse(process.env.GOOGLE_PRIVATE_KEY) 7 | }; 8 | -------------------------------------------------------------------------------- /client/src/components/chatbot/Message.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Message = (props) => { 4 | return(
5 |
6 | {props.text} 7 |
8 |
9 | )}; 10 | 11 | export default Message; 12 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "materialize-css/dist/css/materialize.min.css"; 4 | import App from "./components/App"; 5 | import './assets/app.css'; 6 | import * as serviceWorker from "./serviceWorker"; 7 | 8 | ReactDOM.render(, document.getElementById("root")); 9 | 10 | serviceWorker.unregister(); 11 | -------------------------------------------------------------------------------- /routes/fulfillment.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { WebhookClient } = require("dialogflow-fulfillment"); 3 | 4 | const router = express.Router(); 5 | 6 | router.post('/', (req, res) => { 7 | const agent = new WebhookClient({ request: req, response: res }); 8 | let intentMap = new Map(); 9 | agent.handleRequest(intentMap); 10 | }) 11 | 12 | module.exports = router; 13 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /client/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Chatbot from "./chatbot/Chatbot"; 3 | import Info from "./Info"; 4 | 5 | const App = () => ( 6 |
7 |
8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 |
16 | ); 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "axios": "^0.18.1", 7 | "http-proxy-middleware": "^0.19.1", 8 | "materialize-css": "^1.0.0", 9 | "react": "^16.6.3", 10 | "react-dom": "^16.6.3", 11 | "react-scripts": "2.1.1", 12 | "universal-cookie": "^3.0.7", 13 | "uuid": "^3.3.2" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": "react-app" 23 | }, 24 | "browserslist": [ 25 | ">0.2%", 26 | "not dead", 27 | "not ie <= 11", 28 | "not op_mini all" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /client/src/components/Info.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Info = () => ( 4 |
5 |
6 | bot 7 | Hello! 8 |
9 |
10 |

11 | This is a simple chatbot built using DialogFlow. You can try asking him questions about me like my likes, dislikes and academics. 12 | You can also ask the bot for its name and have small talk with it. I'll add more training phrases whenever I find some time. 13 | Don't forget to say bye before leaving :D 14 |

15 |
16 |
17 | ); 18 | 19 | export default Info; -------------------------------------------------------------------------------- /client/src/components/chatbot/Card.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Card = props => ( 4 |
5 |
6 |
7 | {props.payload.fields.header.stringValue} 11 |
12 |
13 | 14 | {props.payload.fields.description.stringValue} 15 | 16 |
17 |
18 |
19 | ); 20 | 21 | export default Card; 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Chatbot 2 | DialogFlow chatbot which answers a few questions about me in a funny way. 3 | It uses ReactJS on the frontend and nodeJS on the backend. 4 | (With the end of Heroku free tier the site is also down now) 5 | 6 | ## Getting Started 7 | - Get your google credentials and create a dev.js file in the config folder. 8 | - Add all the values in prod.js in dev.js file. 9 | - Run the following commands: 10 | ``` 11 | npm install 12 | npm run start:dev 13 | ``` 14 | - Go to localhost:5000 15 | 16 | 17 | ## Demo 18 | ### The chatbot greets you on opening the website 19 | ![greeting message](./screenshots/1.png) 20 | 21 | --- 22 | 23 | ### Some examples of conversations with the bot 24 | ![conversation demo](./screenshots/2.png) 25 | ![conversation demo](./screenshots/3.png) 26 | ![conversation demo](./screenshots/4.png) 27 | ![conversation demo](./screenshots/5.png) 28 | 29 | 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-chat-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "engines": { 7 | "node": "10.6.0", 8 | "npm": "6.1.0" 9 | }, 10 | "scripts": { 11 | "start": "node index.js", 12 | "backend": "nodemon index.js", 13 | "frontend": "npm run start --prefix client", 14 | "start:dev": "concurrently \"npm run backend\" \"npm run frontend\"", 15 | "heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install --prefix client && npm run build --prefix client" 16 | }, 17 | "author": "Tanish Grover", 18 | "license": "ISC", 19 | "dependencies": { 20 | "actions-on-google": "^2.5.0", 21 | "body-parser": "^1.18.3", 22 | "dialogflow": "^0.7.0", 23 | "dialogflow-fulfillment": "^0.6.1", 24 | "express": "^4.16.4" 25 | }, 26 | "devDependencies": { 27 | "concurrently": "^4.1.0", 28 | "nodemon": "^1.18.7" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const bodyParser = require("body-parser"); 3 | const path = require("path"); 4 | const config = require("./config/keys"); 5 | 6 | const app = express(); 7 | const PORT = process.env.PORT || 5000; 8 | 9 | //Routes 10 | const dialogFlowRoutes = require("./routes/dialogFlow"); 11 | const fulfillmentRoutes = require("./routes/fulfillment"); 12 | 13 | app.use(bodyParser.json()); 14 | 15 | app.get("/api", (req, res) => { 16 | res.send({ hello: "there" }); 17 | }); 18 | 19 | app.use(dialogFlowRoutes); 20 | app.use(fulfillmentRoutes); 21 | 22 | 23 | if (process.env.NODE_ENV === "production") { 24 | // js and css files 25 | app.use(express.static("client/build")); 26 | // index.html 27 | app.get("*", (req, res) => { 28 | res.sendFile(path.resolve(__dirname, "client", "build", "index.html")); 29 | }); 30 | } 31 | 32 | app.listen(PORT, () => { 33 | console.log(`server started at port ${PORT}`); 34 | }); 35 | -------------------------------------------------------------------------------- /routes/dialogFlow.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const chatbot = require("../chatbot/chatbot"); 3 | 4 | const router = express.Router(); 5 | 6 | router.post("/api/df_text_query", async (req, res) => { 7 | const { text, userID, parameters } = req.body; 8 | 9 | const responses = await chatbot 10 | .textQuery(text, userID, parameters) 11 | .catch(err => { 12 | console.log("ERROR:", err); 13 | res.status(400).send("error"); 14 | }); 15 | 16 | const result = responses[0].queryResult; 17 | res.send(result); 18 | }); 19 | 20 | router.post("/api/df_event_query", async (req, res) => { 21 | const { event, userID, parameters } = req.body; 22 | 23 | const responses = await chatbot 24 | .eventQuery(event, userID, parameters) 25 | .catch(err => { 26 | console.log("ERROR:", err); 27 | res.status(400).send("error"); 28 | }); 29 | 30 | const result = responses[0].queryResult; 31 | res.send(result); 32 | }); 33 | 34 | module.exports = router; 35 | -------------------------------------------------------------------------------- /client/src/assets/app.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | html { 8 | font-family: 'Lato', 'Arial', sans-serif; 9 | font-weight: 500; 10 | font-size: 20px; 11 | text-rendering: optimizeLegibility; 12 | } 13 | 14 | body { 15 | background-color: rgb(195, 227, 226); 16 | } 17 | 18 | #chatwindow-nav { 19 | display: flex; 20 | justify-content: space-between; 21 | align-items: center; 22 | padding-right: 12px; 23 | padding-left: 12px; 24 | font-weight: bold; 25 | font-size: 22px; 26 | letter-spacing: 1px; 27 | } 28 | 29 | .close { 30 | cursor: pointer; 31 | } 32 | 33 | #info { 34 | position:absolute; 35 | top:25%; 36 | left:25%; 37 | -ms-transform: translateX(-25%) translateY(-25%); 38 | -webkit-transform: translate(-25%,-25%); 39 | transform: translate(-25%,-25%); 40 | } 41 | 42 | .chatbubble { 43 | max-width: 350px; 44 | border-radius: 30px; 45 | margin: 12px 14px; 46 | width: inherit; 47 | overflow-wrap: break-word; 48 | padding: 7px 20px; 49 | display: inline-block; 50 | font-size: 15px; 51 | word-spacing: 0.3px; 52 | color: white; 53 | text-align: start; 54 | } 55 | 56 | .bot { 57 | border-top-left-radius: 0; 58 | background-color: #f1f0f0; 59 | color: #222; 60 | } 61 | 62 | .me { 63 | border-top-right-radius: 0; 64 | background-color: #ee6e73; 65 | } 66 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 24 | Chatbot 25 | 26 | 27 | 30 |
31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /chatbot/chatbot.js: -------------------------------------------------------------------------------- 1 | const dialogFlow = require("dialogflow"); 2 | const structjson = require("./structjson"); 3 | const config = require("../config/keys"); 4 | 5 | const projectID = config.googleProjectID; 6 | const sessionID = config.dialogFlowSessionID; 7 | const languageCode = config.dialogFlowSessionLanguageCode; 8 | 9 | const credentials = { 10 | client_email: config.googleClientEmail, 11 | private_key: config.googlePrivateKey 12 | }; 13 | 14 | const sessionClient = new dialogFlow.SessionsClient({ projectID, credentials }); 15 | 16 | module.exports = { 17 | textQuery: async function(text, userID, parameters = {}) { 18 | let sessionPath = sessionClient.sessionPath(projectID, sessionID + userID); 19 | 20 | const request = { 21 | session: sessionPath, 22 | queryInput: { 23 | text: { 24 | text: text, 25 | languageCode: languageCode 26 | } 27 | }, 28 | queryParams: { 29 | payload: { 30 | data: parameters 31 | } 32 | } 33 | }; 34 | let responses = await sessionClient.detectIntent(request); 35 | responses = await this.handleAction(responses); 36 | return responses; 37 | }, 38 | 39 | eventQuery: async function(event, userID, parameters = {}) { 40 | let sessionPath = sessionClient.sessionPath(projectID, sessionID + userID); 41 | const request = { 42 | session: sessionPath, 43 | queryInput: { 44 | event: { 45 | name: event, 46 | parameters: structjson.jsonToStructProto(parameters), 47 | languageCode: languageCode 48 | } 49 | } 50 | }; 51 | let responses = await sessionClient.detectIntent(request); 52 | responses = await this.handleAction(responses); 53 | return responses; 54 | }, 55 | 56 | handleAction: function(responses) { 57 | let queryResult = responses[0].queryResult; 58 | switch (queryResult.action) { 59 | case "input.whoAreYou": 60 | 61 | break; 62 | } 63 | return responses; 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /chatbot/structjson.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | /** 17 | * @fileoverview Utilities for converting between JSON and goog.protobuf.Struct 18 | * proto. 19 | */ 20 | 21 | 'use strict'; 22 | 23 | function jsonToStructProto(json) { 24 | const fields = {}; 25 | for (const k in json) { 26 | fields[k] = jsonValueToProto(json[k]); 27 | } 28 | 29 | return {fields}; 30 | } 31 | 32 | const JSON_SIMPLE_TYPE_TO_PROTO_KIND_MAP = { 33 | [typeof 0]: 'numberValue', 34 | [typeof '']: 'stringValue', 35 | [typeof false]: 'boolValue', 36 | }; 37 | 38 | const JSON_SIMPLE_VALUE_KINDS = new Set([ 39 | 'numberValue', 40 | 'stringValue', 41 | 'boolValue', 42 | ]); 43 | 44 | function jsonValueToProto(value) { 45 | const valueProto = {}; 46 | 47 | if (value === null) { 48 | valueProto.kind = 'nullValue'; 49 | valueProto.nullValue = 'NULL_VALUE'; 50 | } else if (value instanceof Array) { 51 | valueProto.kind = 'listValue'; 52 | valueProto.listValue = {values: value.map(jsonValueToProto)}; 53 | } else if (typeof value === 'object') { 54 | valueProto.kind = 'structValue'; 55 | valueProto.structValue = jsonToStructProto(value); 56 | } else if (typeof value in JSON_SIMPLE_TYPE_TO_PROTO_KIND_MAP) { 57 | const kind = JSON_SIMPLE_TYPE_TO_PROTO_KIND_MAP[typeof value]; 58 | valueProto.kind = kind; 59 | valueProto[kind] = value; 60 | } else { 61 | console.warn('Unsupported value type ', typeof value); 62 | } 63 | return valueProto; 64 | } 65 | 66 | function structProtoToJson(proto) { 67 | if (!proto || !proto.fields) { 68 | return {}; 69 | } 70 | const json = {}; 71 | for (const k in proto.fields) { 72 | json[k] = valueProtoToJson(proto.fields[k]); 73 | } 74 | return json; 75 | } 76 | 77 | function valueProtoToJson(proto) { 78 | if (!proto || !proto.kind) { 79 | return null; 80 | } 81 | 82 | if (JSON_SIMPLE_VALUE_KINDS.has(proto.kind)) { 83 | return proto[proto.kind]; 84 | } else if (proto.kind === 'nullValue') { 85 | return null; 86 | } else if (proto.kind === 'listValue') { 87 | if (!proto.listValue || !proto.listValue.values) { 88 | console.warn('Invalid JSON list value proto: ', JSON.stringify(proto)); 89 | } 90 | return proto.listValue.values.map(valueProtoToJson); 91 | } else if (proto.kind === 'structValue') { 92 | return structProtoToJson(proto.structValue); 93 | } else { 94 | console.warn('Unsupported JSON value proto kind: ', proto.kind); 95 | return null; 96 | } 97 | } 98 | 99 | module.exports = { 100 | jsonToStructProto, 101 | structProtoToJson, 102 | }; 103 | -------------------------------------------------------------------------------- /client/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read http://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit http://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /client/src/components/chatbot/Chatbot.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import axios from "axios"; 3 | import Cookies from "universal-cookie"; 4 | import { v4 as uuid } from "uuid"; 5 | import Message from "./Message"; 6 | import Card from "./Card"; 7 | import messageSound from './../../assets/open-ended.mp3'; 8 | 9 | //Creating cookie for unique session for DialogFlow 10 | const cookies = new Cookies(); 11 | 12 | class Chatbot extends Component { 13 | // Creating react references to elements 14 | messagesEnd; 15 | chatInput; 16 | 17 | constructor(props) { 18 | super(props); 19 | this.state = { 20 | messages: [], 21 | showBot: false, 22 | welcomeSent: false, 23 | botName: 'Chatbot' 24 | }; 25 | 26 | this.sound = new Audio(messageSound); 27 | 28 | //Setting the cookie using uuid 29 | if (!cookies.get("userID")) { 30 | cookies.set("userID", uuid(), { path: "/" }); 31 | } 32 | 33 | //Binding event listeners 34 | this.toggleBot = this.toggleBot.bind(this); 35 | this._handleInputKeyPress = this._handleInputKeyPress.bind(this); 36 | } 37 | 38 | resolveAfterXSeconds(time) { 39 | return new Promise(resolve => { 40 | setTimeout(() => { 41 | resolve(time); 42 | }, time * 1000); 43 | }); 44 | } 45 | 46 | async componentDidMount() { 47 | if(!this.state.welcomeSent) { 48 | await this.resolveAfterXSeconds(1.2); 49 | this.df_event_query("WELCOME_TO_SITE"); 50 | this.setState({ welcomeSent: true, showBot: true }); 51 | } 52 | } 53 | 54 | // Scroll to latest message on updation of state 55 | componentDidUpdate() { 56 | if(this.state.showBot) { 57 | this.messagesEnd.scrollIntoView({ behaviour: "smooth" }); 58 | } 59 | if (this.chatInput) { 60 | this.chatInput.focus(); 61 | } 62 | } 63 | 64 | //Function to send text query to server 65 | async df_text_query(text) { 66 | let says = { 67 | speaks: "me", 68 | message: { 69 | text: { 70 | text 71 | } 72 | } 73 | }; 74 | this.setState({ 75 | messages: [...this.state.messages, says] 76 | }); 77 | 78 | const res = await axios.post("/api/df_text_query", { 79 | text, 80 | userID: cookies.get("userID") 81 | }); 82 | 83 | 84 | if (res.data.action === 'input.whoAreYou' && res.data.allRequiredParamsPresent) { 85 | this.setState({botName: res.data.parameters.fields.name.stringValue}); 86 | } 87 | 88 | res.data.fulfillmentMessages.forEach(message => { 89 | says = { 90 | speaks: "bot", 91 | message 92 | }; 93 | this.setState({ 94 | messages: [...this.state.messages, says] 95 | }); 96 | }); 97 | 98 | 99 | this.sound.play(); 100 | } 101 | 102 | // Function to send event query to server 103 | async df_event_query(event) { 104 | const res = await axios.post("/api/df_event_query", { 105 | event, 106 | userID: cookies.get("userID") 107 | }); 108 | // Iterating over all the responeses in the the request response 109 | // because the chatbot can have multiple responses for a single phrase 110 | for (let msg of res.data.fulfillmentMessages) { 111 | let says = { 112 | speaks: "bot", 113 | message: msg 114 | }; 115 | this.setState({ messages: [...this.state.messages, says] }); 116 | } 117 | this.sound.play(); 118 | } 119 | 120 | //Helper functions 121 | isNormalMessage(message) { 122 | return message.message && message.message.text && message.message.text.text; 123 | } 124 | 125 | isMessageCard(message) { 126 | return ( 127 | message.message && 128 | message.message.payload && 129 | message.message.payload.fields && 130 | message.message.payload.fields.cards 131 | ); 132 | } 133 | 134 | // RENDER FUNCTIONS 135 | renderCards(cards) { 136 | return cards.map((card, i) => ); 137 | } 138 | 139 | renderOneMessage(message, i) { 140 | if (this.isNormalMessage(message)) { 141 | return ( 142 | 147 | ); 148 | } else if (this.isMessageCard(message)) { 149 | return ( 150 |
151 |
152 |
160 | {this.renderCards( 161 | message.message.payload.fields.cards.listValue.values 162 | )} 163 |
164 |
165 |
166 | ); 167 | } 168 | } 169 | 170 | //Renders all the messages 171 | renderMessages(stateMessages) { 172 | if (stateMessages) { 173 | return stateMessages.map((message, i) => { 174 | return this.renderOneMessage(message, i); 175 | }); 176 | } 177 | return null; 178 | } 179 | 180 | toggleBot() { 181 | this.setState({ showBot: !this.state.showBot }); 182 | } 183 | 184 | // EVENT LISTENERS 185 | _handleInputKeyPress(e) { 186 | if (e.key === "Enter" && e.target.value !== "") { 187 | this.df_text_query(e.target.value); 188 | e.target.value = ""; 189 | } 190 | } 191 | 192 | render() { 193 | const { showBot, botName } = this.state; 194 | 195 | if (showBot) { 196 | return ( 197 |
207 | 213 |
217 | {this.renderMessages(this.state.messages)} 218 |
{ 220 | this.messagesEnd = el; 221 | }} 222 | style={{ float: "left", clear: "both" }} 223 | /> 224 |
225 | { 228 | this.chatInput = input; 229 | }} 230 | style={{ 231 | paddingLeft: '1%', 232 | paddingRight: '1%', 233 | width: '98%', 234 | backgroundColor: "white", 235 | color: "#222222", 236 | borderTop: '1px solid lightgrey', 237 | marginBottom: 0 238 | }} 239 | placeholder="Start Talking to the bot!" 240 | onKeyPress={this._handleInputKeyPress} 241 | /> 242 | 243 |
244 | ); 245 | } else { 246 | 247 | return ( 248 |
257 | 262 |
263 | ); 264 | } 265 | } 266 | } 267 | 268 | export default Chatbot; 269 | --------------------------------------------------------------------------------