CLOVA Chatbot Custom API
    • PDF

    CLOVA Chatbot Custom API

    • PDF

    Article Summary

    To integrate with a custom chatbot, copy and store the invoke URL and secret key of the stage created.

    Requests

    Sending a request to CLOVA Chatbot from a custom channel.

    • Http Method: POST

    • Headers

    namevalue
    Content-Type"application/json;UTF-8"
    X-NCP-CHATBOT_SIGNATUREsignature of request body

    Request signature headers

    • Signature definition (X-NCP-CHATBOT_SIGNATURE)

      • Algorithm: HmacSHA256

      • SecretKey: it is the SecretKey created by the CLOVA Chatbot domain builder.

      • Sign content: it means the request body string.

    • X-NCP-CHATBOT_SIGNATURE: X-NCP-CHATBOT_SIGNATURE is a message that sends a request to the chatbot.
      That is, when a request is sent to the chatbot, the request header requires content-type, X-NCP-CHATBOT_SIGNATURE, and
      X-NCP-CHATBOT_SIGNATURE is set by encoding the sign content (request body) in Base64.

    Sample code for creating the Signature (Sign content) for each language

    It describes in detail the X-NCP-CHATBOT_SIGNATURE setting and example.

    • js code sample
    const HmacSHA256 = require('crypto-js/hmac-sha256');
    const EncBase64 = require('crypto-js/enc-base64');
    signatureHeader = HmacSHA256(requestBodyString, secretKey).toString(EncBase64);
    
    • java code sample
    byte[] secretKeyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, "HmacSHA256");
    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(secretKeySpec);
    byte[] signature = mac.doFinal(body.getBytes(StandardCharsets.UTF_8));
    String signatureHeader = Base64.getEncoder().encodeToString(signature);
    

    Request body

    Open

    • Response to welcome message

    • trigger when open messenger, will response welcome message if have set in "Messenger Connection" -> "Custom"

    ClassificationJson Model - Example
    Open{
    "version": "v2",
    "userId": "U47b00b58c90f8e47428af8b7bddcda3d",
    "userIp": "8.8.8.8",
    "timestamp": 12345678,
    "bubbles": [ {
    "type": "text",
    "data" : { "description" : "postback text of welcome action" } } ],
    "event": "open"
    }

    Open request field details

    Field nameData typeRequirement statusDescription
    versionstringfalseVersion is "v2" by default and set to "v1" if no value is entered.
    userIdstringtrueUnique ID of a user chatting with the chatbot. Must not exceed 256 characters.
    A unique userId must be set for each user.
    userIpstringfalseUser’s IP address. Not required.
    timestamplongtrueTime offset value (January 1, 1970, 00:00:00 GMT)
    bubblesarraytrueempty array "[]" or only one Text component caused by welcome action
    eventstringtrueSets event value to "open"

    Send

    Sends a user’s question to CLOVA Chatbot.

    When invoking send, the userId value can be set to anything as long as it is a unique key value. (e.g., member number)

    • userid: unique key value, description: user query
    ClassificationJson Model - Example
    Send{
    "version": "v2",
    "userId": "U47b00b58c90f8e47428af8b7bddcda3d",
    "userIp": "8.8.8.8",
    "timestamp": 12345678,
    "bubbles": [ {
    "type": "text",
    "data" : { "description" : "text content which is user input" } } ],
    "event": "send"
    }

    Send request field details

    Field nameData typeRequirement statusDescription
    versionstringfalseVersion is "v2" by default and set to "v1" if no value is entered.
    userIdstringtrueUnique ID of a user chatting with the chatbot. Must not exceed 256 characters.
    A unique userId must be set for each user.
    userIpstringfalseUser’s IP address. Not required.
    timestamplongtrueTime offset value (January 1, 1970, 00:00:00 GMT)
    bubblesarray[Text]trueOnly one text component is supported.
    If there is more than one text component, the last component uses the user request.
    eventstringtrueSets event value to "send"

    getPersistentMenu

    Calls persistent menu list.

    if need show persistent menu but local cache not exists, could request PersistentMenu, will response persistentMenu field if fixed menu have set in messengers custom tab.

    PersistentMenu can be requested if a persistent menu needs to be displayed but no local cache exists. Responds to the persistMenu field if persistent. The menu is set in the messenger customization tab.

    ClassificationJson Model - Example
    getPersistentMenu{
    "version": "v2",
    "userId": "U47b00b58c90f8e47428af8b7bddcda3d",
    "userIp": "8.8.8.8",
    "timestamp": 12345678,
    "bubbles": [],
    "event": "getPersistentMenu"
    }

    getPersistentMenu request field details

    Field nameData typeRequirement statusDescription
    versionstringfalseVersion is "v2" by default and set to "v1" if no value is entered.
    userIdstringtrueUnique ID of a user chatting with the chatbot. Must not exceed 256 characters.
    A unique userId must be set for each user.
    userIpstringfalseUser’s IP address. Not required.
    timestamplongtrueTime offset value (January 1, 1970, 00:00:00 GMT)
    bubblesarraytrueSets empty Array value "[]"
    eventstringtrueSets event value to "getPersistentMenu"

    Responses

    Response result status (success/failure)

    Success

    • HTTP Status Code returns 200 if a chatbot query is successful, and the response fields are as follows:
    ClassificationJson Model - Example
    Success{
    "version": "v2",
    "userId": "U47b00b58c90f8e47428af8b7bddcda3d",
    "sessionId": "34a59946-5dcb-4b72-9b63-a773c659702e",
    "timestamp": 12345678,
    "bubbles": [ // each component is a bubble ],
    "quickButtons": [ // some buttons ],
    "scenario": {
    "name": "analyzedScenarioName",
    "intent": [ // some scenario intent ] },
    "entities": [ {
    "word": "userInputWord",
    "name": "analyzedEntityName" } ],
    "keywords": [ {
    "keyword": "userInputKeyword",
    "group": "analyzedKeywordGroupName",
    "type": "analyzedKeywordType" } ],
    "persistentMenu": { // one template component },
    "event": "send"
    }

    Response result status field details (Success)

    Field nameData typeRequirement statusDescription
    versionstringfalseVersion is "v2" by default. Responds with "v1" if no value is entered for requests.
    userIdstringtrueSame as the userId value set at the time of request.
    sessionIdstringfalseCurrent session id. Value managed by CLOVA Chatbot.
    timestamplongtrueResponse time offset value (milliseconds, January 1, 1970, 00:00:00 GMT)
    bubblesarray[Component]falseArray of response components. Each component matches the chatbot's response bubble.
    quickButtonsarray[Component]falseInformation on the quickButton set at the bottom of the chatbot.
    scenariojsonObjectfalseScenario analysis result matched to user query. Provides scenario name and intent (interaction type) information.
    entitiesarray[jsonObject]falseEntity analysis result matched to user query.
    keywordsarray[jsonObject]falseThe following keyword matches are possible during user chat: "exactMatch" or "contain", "exactMatch"
    "exactMatch" means the user input matches the keyword exactly, and "contain" means the user input contains the keyword.
    persistentMenuTemplateComponentfalseSets a persistent menu. PersistentMenu
    eventstringtrueFixed response of "send."
    slotNormalizerarray[jsonObject]falseSystem entities that match user query. (Date, time, people, name)
    normalizerstringfalseSystem entities that match user query.

    chatbot-03-clip_image002_ko

    Error

    Error

    • HTTP Status Code returns 500 if a chatbot query fails, and the response fields are as follows

    (Errors other than 500 are detailed at the bottom of the article):

    HttpStatusCodeDescription
    500Internal server error

    Error Response Body:

    {
        "code": "500",
        "message": "Internal server error",
        "timestamp": 12345678
    }
    

    Component

    The chatbot’s response component follows the following json structure:

    {
      "type": "...",
      "title": "optional, short bold text",
      "subTitle": "optional, short gray text",
      "data" : {
        ...
      }
    }
    

    There are three types of components:

    • Basic Component
    • Composite Component
    • Flex Component

    Basic Component

    Three basic components are provided: text, image, and button.

    Text

    Text-type chatbot response component. Title, subtitle, detailed description, and URL can be set.

    • Text component structure

    chatbot-03-clip_image003_ko

    • Text component JSON
    ClassificationJson Model - Example
    Text{ "type": "text", "title": "optional, short bold text", "subTitle": "optional, short gray text","data" : {"description" : "optional, a long text content","url" : "optional, a hyperlink at the bottom of description","urlAlias" :"optional, hyperlink show this alias","action": {Action Data} }}
    • Detailed description of text component
    Field nameData typeRequirement statusDescription
    typestringtruetext
    titlestringfalseshort bold text
    subTitlestringfalseshort gray text
    data.descriptionstringfalsea long text content
    data.urlstringfalsethe hyperlink jump url
    data.urlAliasstringfalsethe hyperlink show text
    data.actionActionfalsethe action of click on text or title

    Image

    Image-type chatbot response component. Title, subtitle, detailed description, and URL can be set along with the image.

    • Image component structure

    chatbot-03-clip_image004_ko

    • Image component Json
    ClassificationJson Model - Example
    Image{
    "type": "image",
    "title": "optional, short bold text",
    "subTitle": "optional, short gray text",
    "data" : {
    "imageUrl" : "https://ssl.pstatic.net/CloudFunctions.png",
    "alt" : "optional, short hint show when hover on image",
    "imagePosition" : "top",
    "description" : "optional, details info of image",
    "url" : "optional, a hyperlink at the bottom of description",
    "urlAlias" : "optional, hyperlink show this alias",
    "action": {Action Data}
    }
    • Detailed description of image component
    Field nameData typeRequirement statusDescription
    typestringtrueimage
    titlestringfalseshort bold text
    subTitlestringfalseshort gray text
    data.imageUrlstringtrueimage url, must be https url
    data.altstringfalseshort hint text show hover on image
    data.imagePositionstringfalsetop / bottom / left / right, default is top
    data.descriptionstringfalsedetails info of image
    data.urlstringfalsethe hyperlink jump url
    data.urlAliasstringfalsethe hyperlink show text
    data.actionActionfalsethe action of click on image or title

    Button

    Image-type chatbot response component. Title, subtitle, detailed description, and URL can be set along with the image.

    • Button component structure

    chatbot-03-clip_image005_ko

    • Button component JSON
    ClassificationJson Model - Example
    Button (Basic Button){
    "type": "button",
    "title": "optional, text show on button",
    "subTitle": "optional, short gray text",
    "data" : {
    "type": "basic",
    "iconUrl" : "https://ssl.pstatic.net/CloudFunctions.png",
    "action": {Action Data}
    }
    }
    Button (Image Button){
    "type": "button",
    "title": "optional, text show on button",
    "subTitle": "optional, short gray text",
    "data" : {
    "type": "imageButton",
    "iconUrl" : "https://ssl.pstatic.net/CloudFunctions.png",
    "action": {Action Data}
    }
    }
    • Detailed description of button component
    Field nameData typeRequirement statusDescription
    typestringtruebutton
    titlestringfalsetext show on button
    subTitlestringfalseshort gray text
    data.typestringtruebasic or imageButton
    data.iconUrlstringfalsebutton icon url, must be https url
    data.actionActiontruethe action of click on button

    Composite Component

    A composite component consists of template and carousel components.

    The chatbot response can be set in Chatbot Builder according to specific characteristics. The response result set via Chatbot Builder is returned in the following JSON format:

    Template Component

    The template consists of basic building blocks. The template has three parts: cover, contentTable, and footTable. The cover is the main content. contentTable and footTable are table layouts.

    • Template component structure

    chatbot-03-clip_image006_ko

    • Template component Json
    ClassificationJson Model - Example
    Template{
    "type": "template",
    "title": "optional, short bold text",
    "subTitle": "optional, short gray text",
    "data":{
    "cover":{ // any basic component },
    "contentTableShowRows": 3, // if row count more than 3, should be fold "contentBackgroundImage":"https://ssl.pstatic.net/CloudFunctions.png", // optinal
    "contentTable":[ // table layout [ // first row {
    "colSpan": 1,
    "rowSpan": 2,
    "data":{ // any basic component type } },
    // other cells in first row ], // another rows ],
    "footTableShowRows":3, // if row count more than 3, should be fold
    "footBackgroundImage":"https://ssl.pstatic.net/CloudFunctions.png", // optinal
    "footTable":[ // table layout same as contentTable ] }
    }
    • Template component details
    Field nameData typeRequirement statusDescription
    typestringtruetemplate
    titlestringfalseshort bold text
    subTitlestringfalseshort gray text
    data.coverBasic ComponentfalseText / Image / Button
    data.contentTableShowRowsintegerfalseSets the maximum number of rows.
    If the number of rows exceeds the max value, the maximum number is displayed and the rest are collapsed, requiring the addition of a scaling button.
    All rows are displayed if the value is not set.
    data.contentBackgroundImagestringfalseDisplays background image in content table zone.
    data.contentTablearray[][Cell]falseIt is a table layout with a two-dimensional cell array where cell data are the basic building blocks.
    data.footTableShowRowsintegerfalseSets the maximum number of rows.
    If the number of rows exceeds the max value, the maximum number is displayed and the rest are collapsed, requiring the addition of a scaling button.
    All rows are displayed if the value is not set.
    data.footBackgroundImagestringfalseDisplays background image in foot table zone.
    data.footTablearray[][Cell]falseSame as contentTable, but is not always present unless contentTable is unable to support the content.
    • Table layout details
    Field nameData typeRequirement statusDescription
    rowSpanintegertruespan row count
    colSpanintegertruespan column count
    dataBasic ComponenttrueText / Image / Button

    Carousel Component

    You can set carousel responses.

    chatbot-03-clip_image007_ko

    • Carousel component JSON
    ClassificationJson Model - Example
    Carousel{
    "type": "carousel",
    "title": "optional, short bold text",
    "subTitle": "optional, short gray text",
    "data" : {
    "cards": [ { // any component except carousel self and flex }, // more components
    ] }
    }
    • Carousel component details
    Field nameData typeRequirement statusDescription
    typestringtruecarousel
    titlestringfalseshort bold text
    subTitlestringfalseshort gray text
    data.cardsarray[Component]trueComponent array
    Components other than carousel and flex.

    Flex Component

    Supports all Json object formats. JSON specifications can be defined as needed.

    Example: FlexMessageContainerObject JSON can be used.

    • Json in Flex
    assortmentJson Model - Example
    Flex{
    "type": "flex",
    "title": "not used",
    "subTitle": "required, alternative text",
    "data" : { // any json object }
    }
    • Flex Component Details
    Field namesData typeRequiredExplanation
    typestringtrueflex
    titlestringtruealternative text, show in chat list and push alert
    subTitlestringfalsenot used
    datajson objecttrueany json object. Example: line flex messsage could copy json from Flex Message Simulator

    Special Messenger Component

    1. LineFlex

      The LineFlex function supported in Line messenger has been changed to a common Flex component format, but the specifications remain the same.

    2. LineSticker

      The LineSticker function supported in LINE messenger can set sticker messages according to the sticker list format.

      • LineSticker Json
    ClassificationJson Model - Example
    LineSticker{
    "type": "line_sticker",
    "data" : {
    "packageId": "packageId of LINE",
    "stickerId": "stickerId of LINE"
    }
    }
    • LineSticker component details
    Field nameData typeRequirement statusDescription
    typestringtrueline_sticker
    data.packageIdstringtruepackageId of a sticker for LINE. See sticker list.
    data.stickerIdstringtruestickerId of a sticker for LINE. See sticker list.
    1. LineWorksSticker

      The LineWorksSticker function supported in LINE WORKS can set sticker messages according to the corresponding format of the sticker list.

      • LineWorksSticker Json
    ClassificationJson Model - Example
    LineWorksSticker{
    "type": "lineworks_sticker",
    "data" : {
    "packageId": "packageId of LINEWORKS",
    "stickerId": "stickerId of LINEWORKS"
    }
    }
    • LineWorksSticker component details
    Field nameData typeRequirement statusDescription
    typestringtruelineworks_sticker
    data.packageIdstringtruepackageId of a sticker for LINE WORKS. See sticker list.
    data.stickerIdstringtruestickerId of a sticker for LINE WORKS. See sticker list.

    Action

    An action is common data for all components. When a component is clicked, it defines the operation to be performed.

    • Postback

      Clicking on the component will postback the postbackText to the chatbot and display the postback as user chat.

      • Postback Json
    ClassificationJson Model - Example
    Postback{ "type": "line_sticker", "data" : { "packageId": "packageId of LINE", "stickerId": "stickerId of LINE" } }
    • Postback component details
    Field nameData typeRequirement statusDescription
    typestringtruepostback
    data.postbackstringtruetext show as user chat, if send this field to chatbot, not affect previous features, but will not support some new features
    data.postbackFullstringtruepostback full content send to chatbot
    • Utterance

      Clicking on the building block will postback the text to the chatbot and display the text as the user’s chat.

      • Utterance Json
    ClassificationJson Model - Example
    Utterance{
    "type": "utterance",
    "data" : {
    "utteranceId" : 1,
    "text" : "text show in chat window",
    "postback" : "postback text"
    }
    }
    • Utterance component details
    Field nameData typeRequirement statusDescription
    typestringtrueutterance
    data.utteranceIdstringtrue
    data.textstringtruetext show in chat window as user input
    data.postbackstringtruepostback text send to chatbot
    • Link

      Clicking the component will redirect the user to the URL.

      • Link Json
    ClassificationJson Model - Example
    Link{
    "type": "link",
    "data" : {
    "url" : "http://www.ncloud.com",
    "mobileUrl" : "http://m.ncloud.com"
    }
    }
    • Link component details
    Field nameData typeRequirement statusDescription
    typestringtruepostback
    data.urlstringtrueopen url
    data.mobileUrlstringfalseurl for mobile device
    • Phone

      Clicking the component will redirect the user to the dial page. This function is only supported on mobile devices.

      • Phone Component JSON
    ClassificationJson Model - Example
    Phone{
    "type": "phone",
    "data" : { "number" : "400-1111-1111", "name" : "Customer service" }
    }
    • Phone component details
    Field nameData typeRequirement statusDescription
    typestringtruephone
    data.numberstringtruephone number
    data.namestringfalsecontact name
    • Welcome

      Clicking the component will send an open event.

      • Welcome component Json
    ClassificationJson Model - Example
    Welcome{ "type": "welcome", "data" : { "postback" : "postback text, optional" } }
    • Welcome component details
    Field nameData typeRequirement statusDescription
    typestringtruewelcome
    data.postbackstringfalsePostback text sent to chatbot from an open event.

    Quick Button

    Group-pinning button at the bottom of the chat window.

    Persistent Menu

    The persistent menu is displayed when the user touches the menu button in the chat bar. It is always included in the welcome response. permanentMenu contents are included in responses without changes to utils.

    chatbot-03-clip_image023_ko

    Persistent menu is a Template Component , with these definition:

    • title will show on the chat bar.
    • no cover, cover should be discard.
    • no foot table, fields relate to foot area should be discard.
    • contentBackgroundImage is the background, if the components in the contentTable have image, will cover the background.

    Errors

    An explanation of error responses.

    HttpStatusCodeDescription
    500Internal server error
    • Error Response Body:
     {
        "code": "1001",
        "message": "domain code test not found",
        "timestamp": 12345678
      }
    
    • Body Introduce
    FieldTypeMust ExistsDescription
    codestringtrueerror code
    eventstringtruefixed string value "send"
    timestamplongtrueresponse time milliseconds since January 1, 1970, 00:00:00 GMT
    • Errors
    ErrorCodeDescription
    4000request param invalid
    4010Unauthorized
    4030Forbidden to access
    4031Signature validate failed
    4032timestamp exceeded time window(10000ms)
    1000version not support
    1001Not found domain code
    1002check url param is invalid
    5000Unknown service error
    5010Current protocol version not support this reply structure
    5020Calls to this api have exceeded the rate limit

    API examples

    The following are example implementations of the CLOVA Custom API for each language:

    • JAVA - Chatbot Custom API call source
    package com.ncp.ai.demo.process;
    
    import android.media.MediaPlayer;
    import android.os.Environment;
    import android.util.Base64;
    
    import java.io.BufferedReader;
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.net.URLEncoder;
    import java.security.Timestamp;
    import java.util.Date;
    
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import android.util.Base64;
    
    import org.json.JSONArray;
    import org.json.JSONObject;
    
    public class ChatbotProc {
    
      public static String main(String voiceMessage, String apiURL, String secretKey) {
    
    
            String chatbotMessage = "";
    
            try {
                //String apiURL = "https://ex9av8bv0e.apigw.ntruss.com/custom_chatbot/prod/";
    
                URL url = new URL(apiURL);
    
                String message = getReqMessage(voiceMessage);
                System.out.println("##" + message);
    
                String encodeBase64String = makeSignature(message, secretKey);
    
                HttpURLConnection con = (HttpURLConnection)url.openConnection();
                con.setRequestMethod("POST");
                con.setRequestProperty("Content-Type", "application/json;UTF-8");
                con.setRequestProperty("X-NCP-CHATBOT_SIGNATURE", encodeBase64String);
    
                // post request
                con.setDoOutput(true);
                DataOutputStream wr = new DataOutputStream(con.getOutputStream());
                wr.write(message.getBytes("UTF-8"));
                wr.flush();
                wr.close();
                int responseCode = con.getResponseCode();
    
                BufferedReader br;
    
                if(responseCode==200) { // Normal call
                    System.out.println(con.getResponseMessage());
    
                    BufferedReader in = new BufferedReader(
                            new InputStreamReader(
                                    con.getInputStream()));
                    String decodedString;
                    while ((decodedString = in.readLine()) != null) {
                        chatbotMessage = decodedString;
                    }
                    //chatbotMessage = decodedString;
                    in.close();
    
                } else {  // Error occurred
                    chatbotMessage = con.getResponseMessage();
                }
            } catch (Exception e) {
                System.out.println(e);
            }
    
            return chatbotMessage;
        }
    
        public static String makeSignature(String message, String secretKey) {
    
            String encodeBase64String = "";
    
            try {
                byte[] secrete_key_bytes = secretKey.getBytes("UTF-8");
    
                SecretKeySpec signingKey = new SecretKeySpec(secrete_key_bytes, "HmacSHA256");
                Mac mac = Mac.getInstance("HmacSHA256");
                mac.init(signingKey);
    
                byte[] rawHmac = mac.doFinal(message.getBytes("UTF-8"));
                encodeBase64String = Base64.encodeToString(rawHmac, Base64.NO_WRAP);
    
                return encodeBase64String;
    
            } catch (Exception e){
                System.out.println(e);
            }
    
            return encodeBase64String;
    
        }
    
        public static String getReqMessage(String voiceMessage) {
    
            String requestBody = "";
    
            try {
    
                JSONObject obj = new JSONObject();
    
                long timestamp = new Date().getTime();
    
                System.out.println("##"+timestamp);
    
                obj.put("version", "v2");
                obj.put("userId", "U47b00b58c90f8e47428af8b7bddc1231heo2");
    //=> userId is a unique code for each chat user, not a fixed value, recommend use UUID. use different id for each user could help you to split chat history for users.
    
                obj.put("timestamp", timestamp);
    
                JSONObject bubbles_obj = new JSONObject();
    
                bubbles_obj.put("type", "text");
    
                JSONObject data_obj = new JSONObject();
                data_obj.put("description", voiceMessage);
    
                bubbles_obj.put("type", "text");
                bubbles_obj.put("data", data_obj);
    
                JSONArray bubbles_array = new JSONArray();
                bubbles_array.put(bubbles_obj);
    
                obj.put("bubbles", bubbles_array);
                obj.put("event", "send");
    
                requestBody = obj.toString();
    
            } catch (Exception e){
                System.out.println("## Exception : " + e);
            }
    
            return requestBody;
    
        }
    }
    
    • Example of calling ChatbotProc.java above in UI stage
    package com.example.user.ncpaidemo;
    
    import android.content.Context;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.speech.tts.Voice;
    import android.util.Log;
    import android.view.MenuItem;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    
    import com.naver.speech.clientapi.SpeechRecognitionResult;
    import com.ncp.ai.demo.process.ChatbotProc;
    import com.ncp.ai.demo.process.CsrProc;
    import com.ncp.ai.demo.process.CssProc;
    import com.ncp.ai.utils.AudioWriterPCM;
    
    import org.json.JSONArray;
    import org.json.JSONObject;
    
    import java.lang.ref.WeakReference;
    import java.util.List;
    
    public class VoiceChatbotActivity extends BaseActivity {
    
        private static final String TAG = VoiceChatbotActivity.class.getSimpleName();
        private RecognitionHandler handler;
        private CsrProc naverRecognizer;
        private TextView txtResult;
        private Button btnStart;
        private String mResult;
        private AudioWriterPCM writer;
        private String clientId;
        private String clientSecret;
        // Handle speech recognition Messages.
        private void handleMessage(Message msg) {
            switch (msg.what) {
                case R.id.clientReady: // Voice recognition ready
                    txtResult.setText("Connected");
                    writer = new AudioWriterPCM(Environment.getExternalStorageDirectory().getAbsolutePath() + "/NaverSpeechTest");
                    writer.open("Test");
                    break;
                case R.id.audioRecording:
                    writer.write((short[]) msg.obj);
                    break;
                case R.id.partialResult:
                    mResult = (String) (msg.obj);
                    mResult += mResult;
                    txtResult.setText(mResult);
                    break;
                case R.id.finalResult: // Final recognition result
                    SpeechRecognitionResult speechRecognitionResult1 = (SpeechRecognitionResult) msg.obj;
                    List<String> results1 = speechRecognitionResult1.getResults();
                    StringBuilder strBuf1 = new StringBuilder();
                    for(String result : results1) {
                        strBuf1.append(result);
                        //strBuf.append("\n");
                        break;
                    }
                    mResult = strBuf1.toString();
                    txtResult.setText(mResult);
    
                    requestChatbot();
    
                    break;
                case R.id.recognitionError:
                    if (writer != null) {
                        writer.close();
                    }
                    mResult = "Error code : " + msg.obj.toString();
                    txtResult.setText(mResult);
                    btnStart.setText(R.string.str_start);
                    btnStart.setEnabled(true);
                    break;
                case R.id.clientInactive:
                    if (writer != null) {
                        writer.close();
                    }
                    btnStart.setText(R.string.str_start);
                    btnStart.setEnabled(true);
                    break;
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_voice_chatbot);
            SharedPreferences sharedPref = getSharedPreferences("PREF", Context.MODE_PRIVATE);
    
            clientId = sharedPref.getString("application_client_id", "");
            clientSecret = sharedPref.getString("application_client_secret", "");
    
            txtResult = (TextView) findViewById(R.id.textViewVoiceChatbotResult);
            btnStart = (Button) findViewById(R.id.btn_voice_chatbot1);
            handler = new RecognitionHandler(this);
            //naverRecognizer = new CsrProc(this, handler, clientId);
            naverRecognizer = CsrProc.getCsrProc(this, clientId);
            naverRecognizer.setHandler(handler);
            btnStart.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    if (!naverRecognizer.getSpeechRecognizer().isRunning()) {
    
                        mResult = "";
                        txtResult.setText("Connecting...");
                        btnStart.setText(R.string.str_stop);
                        naverRecognizer.recognize();
                    } else {
                        Log.d(TAG, "stop and wait Final Result");
                        btnStart.setEnabled(false);
                        naverRecognizer.getSpeechRecognizer().stop();
                    }
                }
            });
    
            Button voiceChatbotReplay;
    
            voiceChatbotReplay = (Button) findViewById(R.id.btn_voice_chatbot_replay);
            voiceChatbotReplay.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
    
                    TextView txtResult = (TextView) findViewById(R.id.text_voice_chatbot_replay);
                    CSSExecute(txtResult.getText().toString());
    
                }
            });
        }
    
        private void requestChatbot() {
    
            SharedPreferences sharedPref = getSharedPreferences("PREF", Context.MODE_PRIVATE);
    
            String chatbotApiGwUrl = sharedPref.getString("chatbot_api_gw_url", "");
            String chatbotSecretKey = sharedPref.getString("chatbot_secret_key", "");
    
            TextView csrSourceText = (TextView)findViewById(R.id.textViewVoiceChatbotResult);
            String text = csrSourceText.getText().toString();
    
            VoiceChatbotActivity.VoiceChatbotTask task = new VoiceChatbotActivity.VoiceChatbotTask();
            task.execute(text, chatbotApiGwUrl, chatbotSecretKey);
        }
    
        @Override
        protected void onStart() {
            super.onStart(); // Voice recognition server initialization is here
            naverRecognizer.getSpeechRecognizer().initialize();
        }
        @Override
        protected void onResume() {
            super.onResume();
            mResult = "";
            txtResult.setText("");
            btnStart.setText(R.string.str_start);
            btnStart.setEnabled(true);
        }
    //    @Override
    //    protected void onStop() {
    //        System.out.println("voice chatbot End!!!");
    //        super.onStop(); // terminate voice recognition server
    //        naverRecognizer.getSpeechRecognizer().release();
    //    }
        // Declare handler for handling SpeechRecognizer thread's Messages.
        static class RecognitionHandler extends Handler {
            private final WeakReference<VoiceChatbotActivity> mActivity;
            RecognitionHandler(VoiceChatbotActivity activity) {
                mActivity = new WeakReference<VoiceChatbotActivity>(activity);
            }
            @Override
            public void handleMessage(Message msg) {
                VoiceChatbotActivity activity = mActivity.get();
                if (activity != null) {
                    activity.handleMessage(msg);
                }
            }
        }
    
        public class VoiceChatbotTask extends AsyncTask<String, String, String> {
    
            @Override
            public String doInBackground(String... strings) {
    
                return ChatbotProc.main(strings[0], strings[1], strings[2]);
            }
    
            @Override
            protected void onPostExecute(String result) {
    
                ReturnThreadResult(result);
            }
        }
    
        public String ReturnThreadResult(String result) {
    
            //{"version":"v2","userId":"U47b00b58c90f8e47428af8b7bddc1231heo2","sessionId":"617666","timestamp":1546593912020,
            // "bubbles":[{"type":"template","data":{"cover":{"type":"text","data":{"description":"b"}},"contentTable":[[{"rowSpan":1,"colSpan":1,"data":{"type":"button","title":"b","data":{"type":"basic","action":{"type":"link","data":{"url":"https://www.ncloud.com/product"}}}}}],[{"rowSpan":1,"colSpan":1,"data":{"type":"button","title": "b","data":{"type":"basic","action":{"type":"link","data":{"url":"https://www.ncloud.com/product"}}}}}]]}}],"event":"send"}
    
            //{"version":"v2","userId":"U47b00b58c90f8e47428af8b7bddc1231heo2","sessionId":"641799","timestamp":1546777198124,
            // "bubbles":[{"type":"text","data":{"description":"b"}}],"event":"send"}
            String chatbotMessage =  "";
            String rlt = result;
            try{
                JSONObject jsonObject = new JSONObject(rlt);
                JSONArray bubbles = jsonObject.getJSONArray("bubbles");
    
                for (int i =0; i < bubbles.length(); i++){
    
                    JSONObject bubble = bubbles.getJSONObject(i);
    
                    String chatType = bubble.getString("type");
    
                    if (chatType.equals("text")){
    
                        chatbotMessage = bubble.getJSONObject("data").getString("description");
    
                    }else if (chatType.equals("template")) {
    
                        chatbotMessage = bubble.getJSONObject("data").getJSONObject("cover").getJSONObject("data").getString("description");
    
                    }else {
                        chatbotMessage = "";
                    }
    
                    TextView txtResult = (TextView) findViewById(R.id.text_voice_chatbot_replay);
                    txtResult.setText(chatbotMessage);
    
                    break;
                }
    
            } catch (Exception e) {
                System.out.println(e);
            }
    
            CSSExecute(chatbotMessage);
    
            return chatbotMessage;
        }
    
        private void CSSExecute(String message) {
    
            VoiceChatbotActivity.NaverTTSTask tts = new VoiceChatbotActivity.NaverTTSTask();
            tts.execute(message, "mijin", clientId, clientSecret);
        }
    
        public class NaverTTSTask extends AsyncTask<String, String, String> {
    
            @Override
            public String doInBackground(String... strings) {
                System.out.println(strings[1]);
                CssProc.main(strings[0], strings[1], strings[2], strings[3]);
                return null;
            }
        }
    }
    
    
    • Python - CLOVA Chatbot Custom API v2
    import hashlib
    import hmac
    import base64
    import time
    import requests
    import json
    
    
    class ChatbotMessageSender:
    
        # chatbot api gateway url
        ep_path = ''
        # chatbot custom secret key
        secret_key = ''
    
        def req_message_send(self):
    
            timestamp = self.get_timestamp()
            request_body = {
                'version': 'v2',
                'userId': 'U47b00b58c90f8e47428af8b7bddcda3d1111111',
                'timestamp': timestamp,
                'bubbles': [
                    {
                        'type': 'text',
                        'data': {
                            'description': 'About Me'
                        }
                    }
                ],
                'event': 'send'
            }
    
            ## Request body
            encode_request_body = json.dumps(request_body).encode('UTF-8')
    
            ## make signature
            signature = self.make_signature(self.secret_key, encode_request_body)
    
            ## headers
            custom_headers = {
                'Content-Type': 'application/json;UTF-8',
                'X-NCP-CHATBOT_SIGNATURE': signature
            }
    
            print("## Timestamp : ", timestamp)
            print("## Signature : ", signature)
            print("## headers ", custom_headers)
            print("## Request Body : ", encode_request_body)
    
            ## POST Request
            response = requests.post(headers=custom_headers, url=self.ep_path, data=encode_request_body)
    
            return response
    
        @staticmethod
        def get_timestamp():
            timestamp = int(time.time() * 1000)
            return timestamp
    
        @staticmethod
        def make_signature(secret_key, request_body):
    
            secret_key_bytes = bytes(secret_key, 'UTF-8')
    
            signing_key = base64.b64encode(hmac.new(secret_key_bytes, request_body, digestmod=hashlib.sha256).digest())
    
            return signing_key
    
    
    if __name__ == '__main__':
    
        res = ChatbotMessageSender().req_message_send()
    
        print(res.status_code)
        if(res.status_code == 200):
            print(res.text)
            #print(res.read().decode("UTF-8"))
    
    • php
    
    function makeSignature($secretKey, $requestBody) {
      $signautue = base64_encode(hash_hmac('sha256', $requestBody, $secretKey,true));
      //echo "this is signiture : ".$signautue."\n";
      return $signautue;
    }
    
    
    try {
      // User IP
      if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR']) {
          $clientIpAddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
      } else {
          $clientIpAddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
      }
    
    
      $timestamp = "";
      $microtime = "";
      list($microtime,$timestamp) = explode(' ',microtime());
      $timestamp = $timestamp.substr($microtime, 2, 3);
    
      $url = "https://xi3mfpym2x.apigw.ntruss.com/send/beta/";
    
      $requestBody=  '{
        "version": "v2",
        "userId": "U47b00b58c90f8e47428af8b7bddcda3d231",
        "userIp": "'.$clientIpAddress.'",
        "timestamp": '.$timestamp.',
        "bubbles": [
          {
            "type": "text",
            "data" : {
              "description" : "postback text of welcome action"
            }
          }
        ],
        "event": "open"
      }';
    
      $secretKey = "YnVFTWZDemt3bUZhaEJlalN1Z3ZNY2pzeVp0aVRjd04=12";
      $signautue = makeSignature($secretKey,$requestBody);
    
      $is_post = true;
    
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_POST, true);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
      curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
      curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
      $headers = array();
      $headers[] = "Content-Type:application/json; charset=utf-8";
      $headers[] = "X-NCP-CHATBOT_SIGNATURE: " .$signautue;
      curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    
      $response = curl_exec($ch);
      $err = curl_error($ch);
      curl_close ($ch);
      if ($err) {
        echo "cURL Error #:" . $err;
      } else {
        echo $response;
      }
    
    } catch(Exception $E) {
        echo "Response: ". $E->lastResponse . "\n";
    }
    ?>
    
    • swift
    import UIKit
    import CommonCrypto
    
    
    // chatbot custom secret key
    let secret_key = "";
    
    // chatbot api gateway url
    let invoke_url = "";
    
    class ViewController: UIViewController, URLSessionDelegate {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            demo()
        }
    
        func demo() {
            let timeInterval: TimeInterval = NSDate().timeIntervalSince1970
            let millisecond = CLongLong(round(timeInterval*1000))
            
            let body = ["bubbles":[["data": ["description":"test"],
                                    "type":"text"]],
                        "event":"send",
                        "timestamp":millisecond,
                        "userId":"test",
                        "version":"v2"] as NSDictionary
           
            do {
                let jsonDataFromBody = try JSONSerialization.data(withJSONObject: body, options: JSONSerialization.WritingOptions.prettyPrinted)
                let jsonBytes = [CUnsignedChar](jsonDataFromBody)
                let jsonBytesPointer = UnsafePointer<CUnsignedChar>(jsonBytes) //UnsafeRawPointer
                
                let cKey = secret_key.cString(using: String.Encoding.utf8)
                let digestLen = Int(CC_SHA256_DIGEST_LENGTH)
                let sha256 = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
                CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), cKey, secret_key.lengthOfBytes(using: String.Encoding.utf8), jsonBytesPointer, jsonBytes.count, sha256)
                
                let base64_sha256 = Data.init(bytes: sha256, count: Int(CC_SHA256_DIGEST_LENGTH)).base64EncodedString()
                
                httpsRequest("X-NCP-CHATBOT_SIGNATURE", base64_sha256, body: jsonDataFromBody)
                sha256.deallocate()
                
            } catch {
                print(error)
            }
            
        }
        
        func httpsRequest(_ signHeaderKey:String, _ signature:String, body:Data) {
            let url = URL.init(string: invoke_url)!
            var request = URLRequest.init(url: url)
            request.addValue(signature, forHTTPHeaderField: signHeaderKey)
            request.addValue("application/json", forHTTPHeaderField: "Content-Type")
            request.httpMethod = "POST"
            request.httpBody = body
         
            let session = URLSession.shared
            let task = session.dataTask(with: request) { (data:Data?, response:URLResponse?, error:Error?) in
                print(String.init(data:data!, encoding: .utf8)!)
            }
            task.resume()
        }
        
        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
            if challenge.protectionSpace.authenticationMethod != NSURLAuthenticationMethodServerTrust {
                return
            }
            let credential = URLCredential.init(trust: challenge.protectionSpace.serverTrust!)
            completionHandler(URLSession.AuthChallengeDisposition.useCredential, credential)
        }
    
    }
    
    • objective-c
    #import "ViewController.h"
    #import <UIKit/UIKit.h>
    #import <CommonCrypto/CommonHMAC.h>
    
    // chatbot custom secret key
    static NSString *secret_key = @"";
    
    // chatbot api gateway url
    static NSString *invoke_url = @"";
    
    @interface ViewController () <NSURLSessionDelegate>
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self demo];
    }
    
    - (void)demo {
        NSTimeInterval time = [[NSDate date] timeIntervalSince1970]*1000;
        long long millisecond = [[NSNumber numberWithDouble:time] longLongValue];
        NSDictionary *body = @{@"bubbles":@[@{@"data":@{@"description":@"test"},
                                              @"type":@"text"}],
                               @"event":@"send",
                               @"timestamp":millisecond,
                               @"userId":@"U47b00b58c90f8e47428af8b7bddcda3d1111111",
                               @"version":@"v2"
                               };
        NSError *error;
    
        NSData *jsondataFrombody = [NSJSONSerialization dataWithJSONObject:body
                                                               options:0
                                                                 error:&error];
        
        const char *cKey = [secret_key cStringUsingEncoding:NSUTF8StringEncoding];
        NSMutableData* sha256 = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
        CCHmac(kCCHmacAlgSHA256, cKey, secret_key.length, jsondataFrombody.bytes, jsondataFrombody.length, sha256.mutableBytes);
        
        NSString *base64_sha256 = [sha256 base64EncodedStringWithOptions:0];
        
        [self httpsRequest:@"X-NCP-CHATBOT_SIGNATURE" signature:base64_sha256 bodyData:jsondataFrombody];
    }
    
    - (void)httpsRequest:(NSString *)signHeaderKey signature:(NSString *)sign bodyData:(NSData *)bodyData  {
        NSURL *url = [NSURL URLWithString:invoke_url];
        NSMutableURLRequest *mRequest = [NSMutableURLRequest requestWithURL:url];
        [mRequest setValue:sign forHTTPHeaderField:signHeaderKey];
        [mRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        mRequest.HTTPMethod = @"POST";
        mRequest.HTTPBody = bodyData;
        
        NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                                              delegate:self
                                                         delegateQueue:[NSOperationQueue mainQueue]];//replace delegateQueue with your workingQueue
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:mRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            NSLog(@"response: %@", [response description]);
            NSLog(@"response data:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        }];
        [dataTask resume];
    
    }
    
    - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
        if (![challenge.protectionSpace.authenticationMethod isEqualToString:@"NSURLAuthenticationMethodServerTrust"]) {
            return;
        }
        NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
    
    }
    
    @end
    

    Was this article helpful?

    Changing your password will log you out immediately. Use the new password to log back in.
    First name must have atleast 2 characters. Numbers and special characters are not allowed.
    Last name must have atleast 1 characters. Numbers and special characters are not allowed.
    Enter a valid email
    Enter a valid password
    Your profile has been successfully updated.