Reset Password for SUSI Accounts Using Verification Link

In this blog, I will discuss how the SUSI server interprets the verification link sent to your email id to reset SUSI account password. The email one receives to reset password looks like this :   http://api.susi.ai/apps/resetpass/index.html?token={30 character long token} *Original link contains a token of length of 30 characters. The link has been tampered for security purpose. Taking a close look at the reset link, one would find it easy to decode. It simply contains path to an application on current SUSI Accounts hosting. Name of the application is “resetpass” for Reset Password. But what about the token in the link? As soon as a user clicks on the link, the app is called and token is passed as a GET parameter. The app in background makes a call to the server where the token is evaluated for whether the token is hashed against some user’s email id and has not expired yet. Below is code of first step the client does which is to make a simple ajax call on Ready state. $(document).ready(function() { var passerr = false, confirmerr = false, tokenerr = false; // get password parameters var regex; var urltoken = getParameter('token'); $.ajax( "/aaa/recoverpassword.json", { data: { getParameters: true, token: urltoken }, dataType: 'json', success: function (response) { regex = response.regex; var regexTooltip = response.regexTooltip; $('#pass').tooltip({'trigger':'focus', 'placement': 'left', 'title': regexTooltip}); $('#status-box').text(response.message); tokenerr = false; }, error: function (xhr, ajaxOptions, thrownError) { $('#status-box').text(thrownError); $('#status-box').addClass("error"); $('#pass').prop( "disabled", true ); $('#confirmpass').prop( "disabled", true ); $('#resetbut').prop( "disabled", true ); tokenerr = true; }, }); As you can see the call is made to /aaa/recoverypassword.json end point with token as the request parameter. Now, the client has to just evaluate the JSON response and render the message for user accordingly. If this request returns an error then the error message is shown and the entries are disabled to enter the password. Otherwise, user email id is shown with green text and user can now enter new password and confirm it. In next step, client simply evaluates the password and sends a query to server to reset it. Let us now look at how server functions and processes such requests. //check if a token is already present if (call.get("getParameters", false)) { if (call.get("token", null) != null && !call.get("token", null).isEmpty()) { ClientCredential credentialcheck = new ClientCredential(ClientCredential.Type.resetpass_token, call.get("token", null)); if (DAO.passwordreset.has(credentialcheck.toString())) { Authentication authentication = new Authentication(credentialcheck, DAO.passwordreset); if (authentication.checkExpireTime()) { String passwordPattern = DAO.getConfig("users.password.regex", "^(?=.*\\d).{6,64}$"); String passwordPatternTooltip = DAO.getConfig("users.password.regex.tooltip", "Enter a combination of atleast six characters"); result.put("message", "Email ID: " + authentication.getIdentity().getName()); result.put("regex", passwordPattern); result.put("regexTooltip", passwordPatternTooltip); result.put("accepted", true); return new ServiceResponse(result); } authentication.delete(); throw new APIException(422, "Expired token"); } throw new APIException(422, "Invalid token"); } else { throw new APIException(422, "No token specified"); } } In the above code snippet, server evaluates the received token on the basis of three parameters. First it checks whether the token is provided or not. If not, it throws APIException with error code 422 and a message “No token specified”. If it passes the check, next it checks…

Continue ReadingReset Password for SUSI Accounts Using Verification Link

Implementing the Message Response Status Indicators In SUSI WebChat

SUSI Web Chat now has indicators reflecting the message response status. When a user sends a message, he must be notified that the message has been received and has been delivered to server. SUSI Web Chat implements this by tagging messages with ticks or waiting clock icons and loading gifs to indicate delivery and response status of messages ensuring good UX. This is implemented as: When the user sends a message, the message is tagged with a `clock` icon indicating that the message has been received and delivered to server and is awaiting response from the server When the user is waiting for a response from the server, we display a loading gif Once the response from the server is received, the loading gif is replaced by the server response bubble and the clock icon tagged to the user message is replaced by a tick icon. Lets visit SUSI WebChat and try it out. Query : Hey When the message is sent by the user, we see that the displayed message is tagged with a clock icon and the left side response bubble has a loading gif indicating that the message has been delivered to server and are awaiting response. When the response from server is delivered, the loading gif disappears and the user message tagged with a tick icon.   How was this implemented? The first step is to have a boolean flag indicating the message delivery and response status. let _showLoading = false; getLoadStatus(){ return _showLoading; }, The `showLoading` boolean flag is set to true when the user just sends a message and is waiting for server response.  When the user sends a message, the CREATE_MESSAGE action is triggered. Message Store listens to this action and along with creating the user message, also sets the showLoading flag as true. case ActionTypes.CREATE_MESSAGE: { let message = action.message; _messages[message.id] = message; _showLoading = true; MessageStore.emitChange(); break; } The showLoading flag is used in MessageSection to display the loading gif. We are using a saved gif to display the loading symbol. The loading gif is displayed at the end after displaying all the messages in the message store. Since this loading component must be displayed for every user message, we don’t save this component in MessageStore as a loading message as that would lead to repeated looping thorugh the messages in message store to add and delete loading component. import loadingGIF from '../../images/loading.gif'; function getLoadingGIF() { let messageContainerClasses = 'message-container SUSI'; const LoadingComponent = ( <li className='message-list-item'> <section className={messageContainerClasses}> <img src={loadingGIF} style={{ height: '10px', width: 'auto' }} alt='please wait..' /> </section> </li> ); return LoadingComponent; } We then use this flag in MessageListItem class to tag the user messages with the clock icons. We used Material UI SVG Icons to display the clock and tick messages. We display these beside the time in the messages. import ClockIcon from 'material-ui/svg-icons/action/schedule'; statusIndicator = ( <li className='message-time' style={footerStyle}> <ClockIcon style={indicatorStyle} color={UserPreferencesStore.getTheme()==='light' ? '#90a4ae' : '#7eaaaf'}/> </li> ); When the response from server is received,…

Continue ReadingImplementing the Message Response Status Indicators In SUSI WebChat

Calculation of the Frame Size of the Chat Bubble in SUSI iOS

We receive intelligent responses from the SUSI Server based on our query. Each response contains a different set of actions and the content of the action can be of variable sizes, map, string, table, pie chart, etc. To make the chat bubble size dynamic in the SUSI iOS client, we need to check the action type. For each action, we calculate a different frame size which makes the size of the chat bubble dynamic and hence solving the issue of dynamic size of these bubbles. In order to calculate the frame size, as mentioned above, we need to check the action type of that message. Let’s start by first making the API call sending the query and getting the action types as a response. func queryResponse(_ params: [String : AnyObject], _ completion: @escaping(_ messages: List<Message>?, _ success: Bool, _ error: String?) -> Void) {    let url = getApiUrl(UserDefaults.standard.object(forKey: ControllerConstants.UserDefaultsKeys.ipAddress) as! String, Methods.Chat)        _ = makeRequest(url, .get, [:], parameters: params, completion: { (results, message) in            if let _ = message {                completion(nil, false, ResponseMessages.ServerError)            } else if let results = results {                guard let response = results as? [String : AnyObject] else {                    completion(nil, false, ResponseMessages.InvalidParams)                    return                }                let messages = Message.getAllActions(data: response)                completion(messages, true, nil)            }            return }) } Here, we are sending the query in the params dictionary. The `makeRequest` method makes the actual API call and returns a results object and an error object if any which default to `nil`. First, we check if the error variable is `nil` or not and if it is, we parse the complete response by using a helper method created in the Message object called `getAllActions`. This basically takes the response and gives us a list of messages of all action types returned for that query. In order to display this in the UI, we need to call this method in the View Controller to actually use the result. Here is how we call this method. var params: [String : AnyObject] = [ Client.WebsearchKeys.Query: inputTextView.text! as AnyObject, Client.ChatKeys.TimeZoneOffset: ControllerConstants.timeZone as AnyObject,       Client.ChatKeys.Language: Locale.current.languageCode as AnyObject ] if let location = locationManager.location { params[Client.ChatKeys.Latitude] = location.coordinate.latitude as AnyObject       params[Client.ChatKeys.Longitude] = location.coordinate.longitude as AnyObject } if let userData = UserDefaults.standard.dictionary(forKey: ControllerConstants.UserDefaultsKeys.user) as [String : AnyObject]? { let user = User(dictionary: userData)        params[Client.ChatKeys.AccessToken] = user.accessToken as AnyObject       } Client.sharedInstance.queryResponse(params) { (messages, success, _) in DispatchQueue.main.async { if success { self.collectionView?.performBatchUpdates({                            for message in messages! {                                try! self.realm.write {                                    self.realm.add(message)                                    self.messages.append(message)                                    let indexPath = IndexPath(item: self.messages.count - 1, section: 0)                                    self.collectionView?.insertItems(at: [indexPath])                                }                            }                        }, completion: { (_) in                            self.scrollToLast()                     }) } } } Here, we are creating a params object sending the query and some additional parameters such as time zone, location coordinates and access token identifying the user. After the response is received, which contains a list of messages, we use a method called `performBatchUpdates` on the collection view where we loop through all the messages, writing each one of them to the database and then adding at the end of the collection…

Continue ReadingCalculation of the Frame Size of the Chat Bubble in SUSI iOS

Adding Snackbar to undo recent change in theme of SUSI.AI Web Chat

SUSI.AI Web Chat has two primary themes: Light and Dark. The user can switch between these two from settings, but what if the user does not prefer the theme change. He/She has to repeat the entire process of Going to Settings -> Selecting another theme -> saving it. To enable the user to instantly change the theme, we decided to add snackbar on theme change. What is Snackbar ? Snackbar contains info regarding the operation just performed and it displays them in a visual format. Snackbar can also have a button like “Undo” to revert the recent operation just made. It appears at the bottom of screen by default. A simple snackbar looks like this: Now we needed this to pop-up, whenever the theme is changed. When a user changes theme we run a function named “implementSettings” which checks what the theme is changed to. The method is: implementSettings = (values) => { this.setState({showSettings: false}); if(values.theme!==this.state.currTheme){ this.setState({SnackbarOpen: true}); } this.changeTheme(values.theme); this.changeEnterAsSend(values.enterAsSend); setTimeout(() => { this.setState({ SnackbarOpen: false }); }, 2500); } The argument values is an object that contains all the change that user has made in settings. Here values.theme contains the value of theme user selected from settings. We first check if the theme is same as the current one if so, we do nothing. If the theme is different from current, then we update the theme by calling this.changeTheme(values.theme) and also initiate snackbar by setting SnackbarOpen to open. The snackbar component looks like: <Snackbar open={this.state.SnackbarOpen} message={'Theme Changed'} action="undo" autoHideDuration={4000} onActionTouchTap={this.handleActionTouchTap} onRequestClose={this.handleRequestClose} /> This appears on the screen as follows : Now if a user wants to change the theme instantly he/she can press the undo button. For this undo button, we have a method handleActionTouchTap. In this method, we change the theme back to previous one. The method is implemented in the following manner: handleActionTouchTap = () => { this.setState({ SnackbarOpen: false, }); switch(this.state.currTheme){ case 'light': { this.changeTheme('dark'); break; } case 'dark': { this.changeTheme('light'); break; } default: { // do nothing } } }; The above method is pretty self-explanatory which checks the current theme(“light” or “dark”) and then revert it. We also change the state of SnackbarOpen to “false” so that on clicking “UNDO” button, the theme changes back to previous one and the snackbar closes.Now user is having an option of instantly changing back to previous theme. Resources: Snackbar from material-ui : http://www.material-ui.com/#/components/snackbar States in React: https://facebook.github.io/react-native/docs/state.html Testing Link: http://chat.susi.ai/

Continue ReadingAdding Snackbar to undo recent change in theme of SUSI.AI Web Chat

How SUSI WebChat Implements RSS Action Type

SUSI.AI now has a new action type called RSS. As the name suggests, SUSI is now capable of searching the internet to answer user queries. This web search can be performed either on the client side or the server side. When the web search is to be performed on the client side, it is denoted by websearch action type. When the web search is performed by the server itself, it is denoted by rss action type. The server searches the internet and using RSS feeds, returns an array of objects containing : Title Description Link Count Each object is displayed as a result tile and all the results are rendered as swipeable tiles. Lets visit SUSI WebChat and try it out. Query : Google Response: API response SUSI WebChat uses the same code abstraction to render websearch and rss results as both are results of websearch, only difference being where the search is being performed i.e client side or server side. How does the client know that it is a rss action type response? "actions": [ { "type": "answer", "expression": "I found this on the web:" }, { "type": "rss", "title": "title", "description": "description", "link": "link", "count": 3 } ], The actions attribute in the JSON API response has information about the action type and the keys to be parsed for title, link and description. The type attribute tells the action type is rss. The title attribute tells that title for each result is under the key - title for each object in answers[0].data. Similarly keys to be parsed for description and link are description and link respectively. The count attribute tells the client how many results to display. We then loop through the objects in answers,data[0] and from each object we extract title, description and link. let rssKeys = Object.assign({}, data.answers[0].actions[index]); delete rssKeys.type; let count = -1; if(rssKeys.hasOwnProperty('count')){ count = rssKeys.count; delete rssKeys.count; } let rssTiles = getRSSTiles(rssKeys,data.answers[0].data,count); We use the count attribute and the length of answers[0].data to fix the number of results to be displayed. // Fetch RSS data export function getRSSTiles(rssKeys,rssData,count){ let parseKeys = Object.keys(rssKeys); let rssTiles = []; let tilesLimit = rssData.length; if(count > -1){ tilesLimit = Math.min(count,rssData.length); } for(var i=0; i<tilesLimit; i++){ let respData = rssData[i]; let tileData = {}; parseKeys.forEach((rssKey,j)=>{ tileData[rssKey] = respData[rssKeys[rssKey]]; }); rssTiles.push(tileData); } return rssTiles; } We now have our list of objects with the information parsed from the response.We then pass this list to our renderTiles function where each object in the rssTiles array returned from getRSSTiles function is converted into a Paper tile with the title and description and the entire tile is hyperlinked to the given link using Material UI Paper Component and few CSS attributes. // Draw Tiles for Websearch RSS data export function drawTiles(tilesData){ let resultTiles = tilesData.map((tile,i) => { return( <div key={i}> <MuiThemeProvider> <Paper zDepth={0} className='tile'> <a rel='noopener noreferrer' href={tile.link} target='_blank' className='tile-anchor'> {tile.icon && (<div className='tile-img-container'> <img src={tile.icon} className='tile-img' alt=''/> </div> )} <div className='tile-text'> <p className='tile-title'> <strong> {processText(tile.title,'websearch-rss')} </strong> </p> {processText(tile.description,'websearch-rss')} </div> </a> </Paper>…

Continue ReadingHow SUSI WebChat Implements RSS Action Type

Creating and Maintaining User Sessions Using Universal-Cookies in SUSI Web Chat

If you login to SUSI Web Chat, and come back again after some days, you find that you didn’t have to login and all your previous sent messages are in there in the message pane. To achieve this, SUSI Web Chat uses cookies stored in your browser which is featured in this blog.   In ReactJS, it’s highly misleading and extensive to use the conventional Javascript methodology of saving tokens and deleting them. However, universal-cookie, a node package allows you to store and get cookies with the least possible confusion. In the following examples, I have made use of the get, set and remove functions of the universal-cookie package which have documentations easily available at this link. The basic problem one finds while setting cookies and maintaining sessions is the time through which it should be valid and to secure the routes. We shall look at the steps below to figure out how was it implemented in SUSI Web Chat. 1. The first step is to install the packages by using the following command in your project’s root- npm install universal-cookie --save 2. Import the Cookies Class, where-ever you want to create a Cookie object in your repository. import Cookies from 'universal-cookie'; Create a Cookie object at the same time in the file you want to use it, const cookies = new Cookies(); 3. We make use of the set function of the package first, where we try to set the cookie while the User is trying to login to the account. Note - The cookie value can be set to any value one wants, however, here I am setting it to the access token which is generated by the server so that I can access it throughout the application. AJAX Call to the server Login endpoint for SUSI AI http://api.susi.ai/aaa/login.json?type=access-token&login=EMAIL&password=PASSWORD $.ajax({ options: options, success: function (response) { //Get the response token generated from the server let accessToken = response.access_token; // store the current state let state = this.state; // set the time for which the session needs to be valid let time = response.valid_seconds; //set the access token in the state state.accessToken = accessToken; // set the time in the state state.time = time; // Pass the accessToken and the time through the binded function this.handleOnSubmit(accessToken, time); }.bind(this), error: function ( jqXHR, textStatus, errorThrown) { // Handle errors } }); Function -  handleOnSubmit() // Receive the accessToken and the time for which it needs to be valid handleOnSubmit = (loggedIn, time) => { let state = this.state; if (state.success) { // set the cookie of with the value of the access token at path ‘/’ and set the time using the parameter ‘maxAge’ cookies.set('loggedIn', loggedIn, { path: '/', maxAge: time }); // Redirect the user to logged in state and reload this.props.history.push('/', { showLogin: false }); window.location.reload(); } else { // Handle errors } } 4.  To access the value set to the cookie, we make use of the get function. To check the logged in state of the…

Continue ReadingCreating and Maintaining User Sessions Using Universal-Cookies in SUSI Web Chat

Using SUSI AI Accounting Object to Write User Settings

SUSI Server uses DAO in which accounting object is stored as JSONTray. SUSI clients are using this accounting object for user settings data. In this blogpost we will focus on how to use accounting JSONTray to write the user settings, so that a client can use such endpoint to store the user related settings in Susi server. The Susi server provides the required API endpoints to its web and mobile clients. Before starting with the implementation of servlet let’s take a look at Accounting.java file, to check how Susi server stores the accounting data. public class Accounting { private JsonTray parent; private JSONObject json; private UserRequests requests; private ClientIdentity identity; ... }   The JsonTray is class to hold the volume as <String,JsonObject> pairs as a Json file. The UserRequests  class holds all the user activities. The ClientIdentity class extend the base class Client and provides an Identification String for authentication of users. Now that we have understood about accounting in SUSI server let’s proceed for making an API endpoint to Store Webclient User settings. To make an endpoint we will use the HttpServlet class which provides methods, such as doGet and doPost, for handling HTTP-specific services. We will inherit our ChangeUserSettings class from AbstractAPIHandler yand implement APIhandler interface. In Susi Server the AbsrtactAPI handler extends a HTTPServlet which implements doGet and doPost ,all servlet in SUSI Server extends this class to increase code reusability.   Since a User has to store its setting, set the minimum base role to access this endpoint to User. Apart from ‘User’ there are Admin and Anonymous roles too. @Override public BaseUserRole getMinimalBaseUserRole() { return BaseUserRole.USER; } Next set the path for using this endpoint, by overriding getAPIPath method(). @Override public String getAPIPath() { return "/aaa/changeUserSettings.json"; } We won’t be dealing with getdefault permissions so null can be return. @Override public JSONObject getDefaultPermissions(BaseUserRole baseUserRole) { return null; } Next we implement serviceImpl method which takes four parameters the query, response, authorization and default permissions. @Override public ServiceResponse serviceImpl(Query query, HttpServletResponse response, Authorization authorization, JsonObjectWithDefault permissions) throws APIException { String key = query.get("key", null); String value =query.get("value", null); if (key == null || value == null ) { throw new APIException(400, "Bad Service call, key or value parameters not provided"); } else { if (authorization.getIdentity() == null) { throw new APIException(400, "Specified User Setting not found, ensure you are logged in"); } else { Accounting accounting = DAO.getAccounting(authorization.getIdentity()); JSONObject jsonObject = new JSONObject(); jsonObject.put(key, value); if (accounting.getJSON().has("settings")) { accounting.getJSON().getJSONObject("settings").put(key, value); } else { accounting.getJSON().put("settings", jsonObject); } JSONObject result = new JSONObject(); result.put("message", "You successfully changed settings to your account!"); return new ServiceResponse(result); } } } We will be storing the setting in Json object using key, value pairs. Take the values from user using query.get(“param”,”default value”) and set the default value to null. So that in case the parameters are not present the servlet can return “Bad service call”. To get the accounting object user identity string given by authorization.getIdentity() method is used. Now check if…

Continue ReadingUsing SUSI AI Accounting Object to Write User Settings

Implementing Search Feature In SUSI Web Chat

SUSI WebChat now has a search feature. Users now have an option to filter or find messages. The user can enter a keyword or phrase in the search field and all the matched messages are highlighted with the given keyword and the user can then navigate through the results. Lets visit SUSI WebChat and try it out. Clicking on the search icon on the top right corner of the chat app screen, we’ll see a search field expand to the left from the search icon. Type any word or phrase and you see that all the matches are highlighted in yellow and the currently focused message is highlighted in orange We can use the up and down arrows to navigate between previous and recent messages containing the search string. We can also choose to search case sensitively using the drop down provided by clicking on the vertical dots icon to the right of the search component. Click on the `X` icon or the search icon to exit from the search mode. We again see that the search field contracts to the right, back to its initial state as a search icon. How does the search feature work? We first make our search component with a search field, navigation arrow icon buttons and exit icon button. We then listen to input changes in our search field using onChange function, and on input change, we collect the search string and iterate through all the existing messages checking if the message contains the search string or not, and if present, we mark that message before passing it to MessageListItem to render the message. let match = msgText.indexOf(matchString); if (match !== -1) { msgCopy.mark = { matchText: matchString, isCaseSensitive: isCaseSensitive }; } We alse need to pass the message ID of the currently focused message to MessageListItem as we need to identify that message to highlight it in orange instead of yellow differentiating between all matches and the current match. function getMessageListItem(messages, markID) { if(markID){ return messages.map((message) => { return ( <MessageListItem key={message.id} message={message} markID={markID}        /> ); }); } } We also store the indices of the messages marked in the MessageSection Component state which is later used to iterate through the highlighted results. searchTextChanged = (event) => { let matchString = event.target.value; let messages = this.state.messages; let markingData = searchMsgs(messages, matchString, this.state.searchState.caseSensitive); if(matchString){ let searchState = { markedMsgs: markingData.allmsgs, markedIDs: markingData.markedIDs, markedIndices: markingData.markedIndices, scrollLimit: markingData.markedIDs.length, scrollIndex: 0, scrollID: markingData.markedIDs[0], caseSensitive: this.state.searchState.caseSensitive, open: false, searchText: matchString }; this.setState({ searchState: searchState }); } } After marking the matched messages with the search string, we pass the messages array into MessageListItem Component where the messages are processed and rendered. Here, we check if the message being received from MessageSection is marked or not and if marked, we then highlight the message. To highlight all occurrences of the search string in the message text, I used a module called react-text-highlight. import TextHighlight from 'react-text-highlight'; if(this.props.message.id === markMsgID){ markedText.push( <TextHighlight key={key} highlight={matchString} text={part} markTag='em' caseSensitive={isCaseSensitive} /> ); }…

Continue ReadingImplementing Search Feature In SUSI Web Chat

Processing Text Responses in SUSI Web Chat

SUSI Web Chat client now supports emojis, images, links and special characters. However, these aren’t declared as separate action types i.e the server doesn’t explicitly tell the client that the response contains any of the above features when it sends the JSON response. So the client must parse the text response from server and add support for each of the above mentioned features instead of rendering the plain text as is, to ensure good UX. SUSI Web Chat client parses the text responses to support : HTML Special Entities Images and GIFs URLs and Mail IDs Emojis and Symbols // Proccess the text for HTML Spl Chars, Images, Links and Emojis function processText(text){ if(text){ let htmlText = entities.decode(text); let imgText = imageParse(htmlText); let replacedText = parseAndReplace(imgText); return <Emojify>{replacedText}</Emojify>; }; return text; } Let us write sample skills to test these out. Visit http://dream.susi.ai/ and enter textprocessing. You can then see few sample queries and responses at http://dream.susi.ai/p/textprocessing. Lets visit SUSI WebChat and try it out. Query : dream textprocessing Response: dreaming enabled for textprocessing Query : text with special characters Response:  &para; Here are few “Special Characters&rdquo;! All the special entities notations have been parsed and rendered accordingly! Sometimes we might need to use HTML special characters due to reasons like You need to escape HTML special characters like <, &, or ". Your keyboard does not support the required character. For example, many keyboards do not have em-dash or the copyright symbol. You might be wondering why the client needs to handle this separately as it is generally, automatically converted to relevant HTML character while rendering the HTML. SUSI Web Chat client uses reactjs which has JSX and not HTML. So JSX doesn’t support HTML special characters i.e they aren’t automatically converted to relevant characters while rendering. Hence, the client needs to handle this explicitly. We used the module, html-entities to decode all types of special HTML characters and entities. This module parses the text for HTML entities and replaces them with the relevant character for rendering when used to decode text. import {AllHtmlEntities} from 'html-entities'; const entities = new AllHtmlEntities(); let htmlText = entities.decode(text); Now that the HTML entities are processed, the client then processes the text for image links. Let us now look at how images and gifs are handled. Query : random gif Response: https://media1.giphy.com/media/AAKZ9onKpXog8/200.gif Sometimes, the text contains links for images or gifs and the user would be expecting a media type like image or gif instead of text. So we need to replace those image links with actual images to ensure good UX. This is handled using regular expressions to match image type urls and correspondingly replace them with html img tags so that the response is a image and not URL text. // Parse text for Image URLs function imageParse(stringWithLinks){ let replacePattern = new RegExp([ '((?:https?:\\/\\/)(?:[a-zA-Z]{1}', '(?:[\\w-]+\\.)+(?:[\\w]{2,5}))', '(?::[\\d]{1,5})?\\/(?:[^\\s/]+\\/)', '*(?:[^\\s]+\\.(?:jpe?g|gif|png))', '(?:\\?\\w+=\\w+(?:&\\w+=\\w+)*)?)' ].join(''),'gim'); let splits = stringWithLinks.split(replacePattern); let result = []; splits.forEach((item,key)=>{ let checkmatch = item.match(replacePattern); if(checkmatch){ result.push( <img key={key} src={checkmatch} style={{width:'95%',height:'auto'}} alt=''/>) } else{…

Continue ReadingProcessing Text Responses in SUSI Web Chat

Integration of SUSI AI to Alexa

An Alexa skill which can be used to ask susi for answers like: "Alexa, ask susi chat who are you" or "Alexa, ask susi chat what is the temperature in berlin". If at any point of time, you are unclear about the code in the blog post, you can check the code of the already made SUSI Alexa skill from the susi_alexa_skill repository. Getting Started : Alexa Susi AI Skill Follow the instructions below: Visit the Amazon developer site and Login. Click Alexa, on the top bar. Click Alexa skills kit. Click on add a new skill button on the top right of the page. We will be at the skill information tab.   Write the name of the skill Write the invocation name of the skill i.e. the name that will be used to trigger your skill. Like in our case, if we need to ask anything (as we have 'susi chat' as the invocation name), we will ask with "Alexa, ask susi chat" as a prefix to our question sentence. By clicking next, we will be redirected to the second tab i.e. Interaction model. We need to fill two fields here i.e. intent schema and sample utterances. For intent schema, we need to write all the available intents and the parameters for each of them. Like in our case: { "intents": [ { "slots": [ { "name": "query", "type": "AMAZON.LITERAL" } ], "intent": "callSusiApi" } ] } We have a single intent that is "callSusiApi" and the parameter it accepts is "query" of type "AMAZON.LITERAL" (in simple words, a string type). Parameters are termed as slots here. The different types of slots available, can be seen from here. For sample utterances, we need to tell what utterances by the client will lead to what intent. In our case: We have just one intent and the whole string uttered by the client should be fed to this intent as a "query" slot (parameter). Let's click next now. We will be shifted to the configuration tab. We will be making a lambda function, which will hold the code for our Susi skill, further we need to link that code to this skill. To do the linking we need to get the Amazon resource name i.e. ARN and fill it in the field named endpoint: To get the amazon resource name, in a new tab, visit here. Visit "Lambda" followed by get started button. Click on "Create a lambda function": We need to select a blueprint for our lambda function. Select the "blank function" option for that. Click next. For configure triggers option, click this box and select "Alexa skills kit" option. Click next. In configure function tab, just write the name of the function and its description. Let’s code our lambda function: // basic syntax that should be available in the lambda function var https = require('http'); exports.handler = (event, context) => { try { if (event.session.new) { // New Session console.log("NEW SESSION") } switch (event.request.type) { case "LaunchRequest": //…

Continue ReadingIntegration of SUSI AI to Alexa