Help us understand the problem. What is going on with this article?

今話題のチャットボットを作ってみた ~スマホ・PC同時対応のチャットUIを爆速で~

More than 1 year has passed since last update.

概要

JavaScriptでチャットボットのUIを爆速で作るためのガイドです。

  • スマートフォンPCブラウザも両方対応
  • scriptタグを1つ入れるだけで既存のコンテンツをジャマしないチャットUI

【作ったチャットボットデモこちらです】

対象読者

  • Java Scriptが書ける人(初心者でもOK)
  • node.js環境がある人(npmまたはyarnが使える)
  • チャットボットに興味がある人

できること

以下のデモのようなPCブラウザとスマホに両対応したチャットボットUIを作ります

スマホの場合は画面にフィットしたチャットUI、PCブラウザの場合はフローティングする小窓にチャットUIを表示します。これを実現するためにどのような技術を使っているかは記事本編にて説明します。

モバイルで表示 PCブラウザで表示

本編

チャットUIは見た目、チャットサーバーは頭脳に相当する。本稿はチャットUI見た目に関する内容がメインとなる。

構成

前述のとおり、本稿で紹介するチャットボットは以下のようにチャットUIの部分チャットサーバー部分の2つのパートで構成されている。

仕組みはシンプルで、ユーザーがチャットUIに入力するとサーバーにテキストが送信され、サーバー側で応答を生成してJSONとして返す。

シーケンス動画
arch2.gif

エコーをかえすだけの簡単なチャットボット

さっそくエコー(自分が書いたテキストをそのまま返す)を返すチャットボットをつくる。

先に完成版デモ。

エコーするだけのチャット
https://riversun.github.io/chatux/ja/echobot/app/chat.html

エコーチャット用のUIを作る

まず、以下の図の左側に相当するチャットUIをさくっと作る

image.png

以下のindex.htmlをローカルに保存して実行するだけで、エコーチャットボットを試すことが可能。

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>エコーボット</title>
</head>
<body>
<script src="https://riversun.github.io/chatux/chatux.min.js"></script>
<script>
    const chatux = new ChatUx();

    //ChatUXの初期化パラメータ
 const initParam =
        {
            renderMode: 'auto',
            api: {
                //echo chat server
                endpoint: 'https://script.google.com/macros/s/AKfycbzro2SWVx5lMKfbLu4cBt16xbqg4lT7xU5wfu4bbSQs-OMcFsQh/exec',
                method: 'GET',
                dataType: 'jsonp'
            },
            bot: {
                botPhoto: 'https://riversun.github.io/chatbot/bot_icon_operator.png',
                humanPhoto: null,
                widget: {
                    sendLabel: '送信',
                    placeHolder: '何か話しかけてみてください'
                }
            },
            window: {
                title: 'エコーボット',
                infoUrl: 'https://github.com/riversun/chatux'
            }
        };
    chatux.init(initParam);
    chatux.start(true);

</script>
</body>
</html>

早速このindex.htmlをブラウザで開いてみるとこの通り実行できる。

chatux_konichia.gif

さて、このコードの重要なところを見ていきたい

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

モバイル表示用に典型的なビューポート制御のmetaタグをいれる。


<script src="https://riversun.github.io/chatux/chatux.min.js"></script>

チャットUI表示用ライブラリChatUxを読み込む。

このライブラリが今回のミソ。

今は手っ取り早くindex.html内にscriptタグでリンクするが、後で1つのjsファイルにバンドルする。


    const chatux = new ChatUx();

まずChatUxインスタンスを作る。
次にChatUxの初期化パラメータ(initParam)を見ていく。

  renderMode: 'auto',
  api: {
    endpoint: 'https://script.google.com/macros/s/AKfycbzro2SWVx5lMKfbLu4cBt16xbqg4lT7xU5wfu4bbSQs-OMcFsQh/exec',
    method: 'GET',
    dataType: 'jsonp'
  }
  • renderMode・・・チャットUIのレンダリングモード

    • auto・・・スマホとPCを自動判定して最適なUIを表示する
    • pc・・・強制的にPC用のレイアウト(小窓)を表示する
    • mobile・・・強制的にスマホ用のレイアウトを表示する
  • api

    • endpoint・・・チャットサーバーのAPIエンドポイント。
      ここではデモ用APIサーバーのURLを指定(次章で自前サーバーの例を示す)
    • method・・・チャットサーバーにアクセスするときのHTTPメソッド。ここではGETメソッドを指定。
    • dataType・・・チャットサーバーにAjaxアクセスするときの方法をjsonまたはjsonpで指定。

初期化パラメータの続きをみていく

  bot: {
    botPhoto: 'https://riversun.github.io/chatbot/bot_icon_operator.png',
    humanPhoto: null,
    widget: {
      sendLabel: '送信',
      placeHolder: '何か話しかけてみてください'
    }
  },
  • bot

    • botPhoto・・・チャットUIに出てくるボット側のアイコン画像のURL
      image.png
    • humanPhoto・・・チャットUIに出てくる人間側のアイコン画像のURL
    • widget
      • sendLabel・・・送信ボタンに表示するテキスト
      • placeHolder・・・ユーザー入力用テキストボックに表示するヒントのテキスト
        image.png
  window: {
    title: 'エコーボット',
    infoUrl: 'https://github.com/riversun/chatux'
  }
  • window・・・PCモード時のウィンドウ設定
    • title・・・ウィンドウのタイトル
    • infoUrl・・・ウィンドウ左上アイコンをクリックしたときのジャンプ先。省略可。
      image.png
chatux.init(initParam);
chatux.start(true);
  • chatux.init(param)で、上の初期化パラメータを適用する

  • chatux.start(true)でチャットUIを有効化する。
     引数にtrueを指定すると、チャットUIが自動的に表示される。
     引数にfalseを指定するか無指定の場合は、画面右下のチャット起動ボタンimage.pngを押すとチャットUIが起動する。

ここまでで、ざっくり、お手軽にチャットボットUIを作れることをみてきた。

今は、デモ用のサーバーを指定したが、次はサーバーを自作してみる。

チャット用のサーバーをつくる

次は、下図の右側に相当するチャットサーバーを作る

image.png

コマンドラインからサーバー用のnpmプロジェクトを準備する

mkdir chatserver
cd chatserver
npm init
(いろいろ聞かれるが、エンターを10回たたけばOK)

npm install express

これでnode環境でサーバーをつくる準備ができたので、いまつくった chatserver ディレクトリserver.jsというファイルを作って、以下のコードを書く。

server.js
const express = require('express');
const app = express();
const port = 8080;
// CORSを有効にする
app.use(function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept');
    next();
});
app.get('/chat', function (req, res) {
    const userInputText = req.query.text;
    const callback = req.query.callback;
    const response = {output: []};
    const msg = response.output;
    msg.push({
        type: 'text',
        value: '' + userInputText + '」ですね!'
    });
    if (callback) {
        const responseText = callback + '(' + JSON.stringify(response) + ')';
        res.set('Content-Type', 'application/javascript');
        res.send(responseText);

    } else {
        res.json(response);
    }
});
app.listen(port, () => {
    console.log('チャットサーバーを開始しました ポート番号:' + port);
});

ソースを簡単に説明する

const express = require('express');
const app = express();
const port = 8080;

チャットサーバーは入力したテキストに応じてJSON応答を返せれば何でもOK。
ここではexpressを使う。


app.get('/chat', function (req, res) {
    const userInputText = req.query.text;
    const callback = req.query.callback;//jsonp対応
    const response = {output: []};
    const msg = response.output;
    msg.push({
        type: 'text',
        value: '' + userInputText + '」ですね!'
    });
    if (callback) {
        const responseText = callback + '(' + JSON.stringify(response) + ')';
        res.set('Content-Type', 'application/javascript');
        res.send(responseText);
    } else {
        res.json(response);
    }
});

ここで /chat?text=こんにちはのようにアクセスがあったときに以下のようなJSONを返すようにする。

{  
   "output":[  
      {  
         "type":"text",
         "value":"「こんにちは」ですね!"
      }
   ]
}

さて、サーバーができたので以下のコマンドでサーバーを起動する。

node server.js
チャットサーバーを開始しました ポート番号:8080

本節のソースコード一式

本節で説明したチャットサーバーのソースコード
https://github.com/riversun/chatux-examples-ja/tree/v1.0.0/step01/chatserver

さきほどのindex.html内のコードを以下のように修正して、チャットサーバーのURLをいま作ったサーバーのURLに変更する

●変更前

index.html(抜粋)
  api: {
    endpoint: 'https://script.google.com/macros/s/AKfycbzro2SWVx5lMKfbLu4cBt16xbqg4lT7xU5wfu4bbSQs-OMcFsQh/exec',
    method: 'GET',
    dataType: 'jsonp'
  }

↓↓↓
●変更前後

index.html(抜粋)
api: {
    endpoint: 'http://localhost:8080/chat',
    method: 'GET',
    dataType: 'json'
},

つまりindex.html全体は以下のようになる

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>エコーボット</title>
</head>
<body>
<script src="https://riversun.github.io/chatux/chatux.min.js"></script>
<script>
    const chatux = new ChatUx();

    //ChatUXの初期化パラメータ
 const initParam =
        {
            renderMode: 'auto',
            api: {
                //echo chat server
                endpoint: 'http://localhost:8080/chat',
                method: 'GET',
                dataType: 'jsonp'
            },
            bot: {
                botPhoto: 'https://riversun.github.io/chatbot/bot_icon_operator.png',
                humanPhoto: null,
                widget: {
                    sendLabel: '送信',
                    placeHolder: '何か話しかけてみてください'
                }
            },
            window: {
                title: 'エコーボット',
                infoUrl: 'https://github.com/riversun/chatux'
            }
        };
    chatux.init(initParam);
    chatux.start(true);

</script>
</body>
</html>

上のindex.htmlをブラウザで開いてみると、このとおりローカルで開いたチャットサーバーに応答している。
gokigen.gif

本節のソースコード一式

本節で説明したindex.htmlのソースコード
https://github.com/riversun/chatux-examples-ja/blob/v1.0.0/step01/chatclient/index.html

【ご参考】チャットサーバーを無料公開する

Google Apps Script(GAS) を使えば無料で上記チャットサーバー相当の機能を公開できる。

GASで動くチャットサーバーのソースコード
function doGet(e) {
    var userInputText = e.parameter.text;
    var callback = e.parameter.callback;
    var response = {output: []};
    var msg = response.output;
    msg.push({
        type: 'text',
        value: '' + userInputText + '」ですね!'
    });
    var responseText = '';
    if (callback) {
        //JSONP
        responseText = callback + '(' + JSON.stringify(response) + ')';
        return send(ContentService.MimeType.JAVASCRIPT, responseText);
    } else {
        //JSON
        return sendJson(response);
    }
}
function send(mimeType, responseText) {
    var textOut = ContentService.createTextOutput();
    textOut.setMimeType(mimeType);
    textOut.setContent(responseText);
    return textOut;
}
function sendJson(response) {
    var textOut = ContentService.createTextOutput();
    var responseText = JSON.stringify(response);
    textOut.setMimeType(ContentService.MimeType.JSON);
    textOut.setContent(responseText);
    return textOut;
}

本節のソースコード一式

本節で説明したGASによるチャットサーバーのソースコード
https://github.com/riversun/chatux-examples-ja/tree/v1.0.0/step01/chatserver4gas

チャットUIのいろんな表現に対応する

ここからは、チャットUIにボタンや画像を表示させてみる。

基本的にチャットUIはサーバーからのレスポンスに応じてレンダリングされるので、チャットサーバー側を変更する。

オプションボタンを表示

前出のチャットサーバーを編集して以下のようなオプションボタン(選択肢)を表示してみる

image.png

コードの全体像はこんな感じで、

server.js
const express = require('express');
const app = express();
const port = 8080;
// CORSを有効にする
app.use(function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept');
    next();
});
app.get('/chat', function (req, res) {
    const userInputText = req.query.text;
    const callback = req.query.callback;
    const response = {output: []};
    const msg = response.output;

    if (userInputText == 'ボタン') {
        msg.push({
            type: 'text',
            value: '好きな動物は?',
            delayMs: 500 //表示ディレイ(ミリ秒)
        });
        //オプションボタンを作る
        const opts = [];
        opts.push({label: 'イヌ', value: ''});
        opts.push({label: 'ネコ', value: ''});
        opts.push({label: 'ウサギ', value: ''});
        msg.push({type: 'option', options: opts});
    } else {
        msg.push({
            type: 'text',
            value: '' + userInputText + '」ですね!'
        });
    }
    if (callback) {
        const responseText = callback + '(' + JSON.stringify(response) + ')';
        res.set('Content-Type', 'application/javascript');
        res.send(responseText);
    } else {
        res.json(response);
    }
});
app.listen(port, () => {
    console.log('チャットサーバーを開始しました ポート番号:' + port);
});

ポイントはここ

    const response = {output: []};
    const msg = response.output;

    if (userInputText == 'ボタン') {
        msg.push({
            type: 'text',
            value: '好きな動物は?',
            delayMs: 500 //表示ディレイ(ミリ秒)
        });
        //オプションボタンを作る
        const opts = [];
        opts.push({label: 'イヌ', value: ''});
        opts.push({label: 'ネコ', value: ''});
        opts.push({label: 'ウサギ', value: ''});
        msg.push({type: 'option', options: opts});
    }

type:'text'として「好きな動物は?」のテキストを表示している
その次に、
type:'option'で、optionsにオプションボタンをコードのように設定する。
delayMs:500で表示の遅延時間(ミリ秒)を設定することができる。複数のメッセージ(たとえば、テキストとボタンと画像)を同時に出したいときは、それぞれに遅延時間を指定すればユーザーがメッセージを読むスピードにあわせて1件ずつ順番にメッセージを表示できる。

opts.push({label: 'イヌ', value: ''});

オプションボタンは何個でも設定可能。
オプションボタンがクリックされると、valueに指定された値がテキストを入力されたのと同じになる。

早速index.htmlを開いて動作を確認してみる。

if (userInputText == 'ボタン') {としているので、
チャットが開いたら「ボタン」と入力する。すると、以下のとおりレンダリングされる。
show_buttons1.gif

画像を表示

次は画像を表示させる。
サーバーのコードに以下を追加する。

 else if (userInputText == '画像') {
            msg.push({
                type: 'text',
                value: '画像を表示します',
                delayMs: 500
            });
            msg.push({
                type: 'image',
                value: 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Kaiserpinguinjunges.jpg/800px-Kaiserpinguinjunges.jpg'
            });

type:'image'とすると、画像を表示できる
value:[画像のURL]で表示したい画像を指定する

server.jsを更新してチャットサーバーを再起動したあと、index.htmlを開いて、「画像」と入力すると、以下のように画像を表示できる。

show_image.gif

本節のソースコード一式

本節で説明したチャットUIの色々な表現に対応したチャットサーバーのソースコード
https://github.com/riversun/chatux-examples-ja/tree/v1.0.0/step02/chatserver


バンドルjsをつくって、チャットUI関連処理を1つのjsファイルにする

さて、いままではindex.htmlの中にChatUXモジュールを<script src="https://riversun.github.io/chatux/chatux.min.js"></script>のようにscriptタグをリンクし、さらにチャットUIのコードも<script><script>に書いてきたが、これをwebpack1つのjsファイルにバンドルする。

さっそく、チャットUIのバンドルjsをつくるためのnpmプロジェクトを作る

mkdir chatclient
cd chatclient
npm init
(エンター10回でOK)

必要モジュールのインストール

まず、チャットUIのコアモジュールChatUxからインストール

npm install --save chatux

次は、モジュールをバンドルするためにwebpackをインストール

npm install --save-dev webpack webpack-cli webpack-dev-server

最後に、ES6をES5に変換するためにbabelをインストール

npm install --save-dev @babel/core @babel/preset-env babel-loader

これで必要なモジュールがインストールできた。

この状態で、package.jsonは以下のようになる

{
  "name": "chatclient",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "chatux": "^1.1.0"
  },
  "devDependencies": {
    "@babel/core": "^7.4.0",
    "@babel/preset-env": "^7.4.2",
    "babel-loader": "^8.0.5",
    "webpack": "^4.29.6",
    "webpack-cli": "^3.3.0",
    "webpack-dev-server": "^3.2.1"
  }
}

次に、srcディレクトリをつくって、そこにindex.jsをつくり、jsのソースコードを書く。

src/index.js
import {ChatUx} from 'chatux';
const chatux = new ChatUx();
const initParam =
    {
        renderMode: 'auto',
        api: {
            //echo chat server
            endpoint: 'http://localhost:8080/chat',
            method: 'GET',
            dataType: 'json'
        },
        bot: {
            botPhoto: 'https://riversun.github.io/chatbot/bot_icon_operator.png',
            humanPhoto: null,
            widget: {
                sendLabel: '送信',
                placeHolder: '何か話しかけてみてください'
            }
        },
        window: {
            title: '新エコーボット',
            infoUrl: 'https://github.com/riversun/chatux'
        }
    };
chatux.init(initParam);
chatux.start(true);

ソースを書いたら、これをコンパイル(トランスパイル)してバンドルjsをつくるためにwebpackの設定をする。

webpack.config.jsをルートディレクトリにつくる。

ちなみに、いまディレクトリはこうなっている。

chatclient
├── src
│   └── index.js
├── node_modules
├── package.json
├── package-lock.json
└── webpack.config.js(いまからここを作業する)

webpack.config.jsは以下のとおり。

webpack.config.js
const path = require('path');
module.exports = (env, argv) => {
    const conf = {
        mode: 'development',
        devServer: {
            open: true,
            openPage: 'index.html',
            contentBase: path.join(__dirname, 'public'),
            watchContentBase: true,
            port: 3000,
            disableHostCheck: true
        },
        entry: {chat: './src/index.js'},
        output: {
            path: path.join(__dirname, 'public'),
            publicPath: '/',
            filename: `[name].js`
        },
        module: {
            rules: [{
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        presets: [['@babel/preset-env',
                            {
                                'modules': 'false',
                                'useBuiltIns': 'usage',
                                "corejs": 3,
                                'targets': '> 0.25%, not dead'
                            }]]
                    }
                }]
            }]
        }
    };
    return conf;
};

次にpackage.jsonにwebpackでindex.jsをコンパイルするための起動スクリプトを追加する

scripts以下にstartstart:webbuild:webをそれぞれ追加する。

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server",
    "build:web": "webpack --config webpack.config.js --mode production"
  },

startはデバッグ用途でwebpack-dev-serverを起動するため、
build:webはindex.jsをコンパイルして必要モジュールが入ったバンドルchat.jsを生成するためのコマンド

さて、最後にpublicディレクトリをつくりその下に、index.htmlを作る。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>新エコーボット</title>
</head>
<body>
<script src="chat.js"></script>
</body>
</html>

ここまでのディレクトリ構成は以下のとおり

chatclient
├── public
│   └── index.html
├── src
│   └── index.js
├── node_modules
├── package.json
├── package-lock.json
└── webpack.config.js

さきほどと同様、チャットサーバーをローカルに起動しておきつつ、いまつくったindex.htmlをwebpack-dev-serverでホストして実行する。

npm start

これでindex.htmlの中に<script src="chat.js"></script>をリンクしただけで無事起動した

afterbunde.gif

バンドルjsを生成したい場合は

npm run build:web

を実行すると、必要モジュールがすべてバンドルされたjsファイルchat.jsが生成される。

chatclient
├── public
│   ├── chat.js (←いま生成されたバンドル)
│   └── index.html
├── src
│   └── index.js
├── node_modules
├── package.json
├── package-lock.json
└── webpack.config.js

ここまでで、チャットサーバーの作り方、チャットUIのバンドルの作り方をみてきたので、これでscriptタグを1ついれるだけで既存のWebコンテンツをジャマしない形でチャットUIを実現できる。

本節のソースコード一式

本節で説明した1つのjsにバンドルするチャットUIのnpmプロジェクトソースコード
https://github.com/riversun/chatux-examples-ja/tree/master/step03/chatclient


カスタマイズと応用

チャットUIの見た目や挙動はカスタマイズ可能。またサーバー側を作り込めばチャットボットやチャットサービスを作成することが可能となる。

styling.gif

チャットUI側の初期化パラメータの設定例

初期化パラメータで、チャットUIのウィンドウの見た目や各種コールバックイベントのハンドリング関数を指定できる。

import {ChatUx} from 'chatux';
const chatux = new ChatUx();

//初期化パラメータ
const initParam = {
    //auto:PCとスマホを自動判定、pc:強制的にPCモード、mobile:強制的にスマホモード
    renderMode: 'auto',
    //true:チャットUIが開いたら、チャット起動ボタンを消す(pcモードのみ有効)
    buttonOffWhenOpenFrame: false,
    bot: {
        //チャットUI起動時に自動的にサーバーに送るテキスト
        wakeupText: null,
        //ボット側のアイコン画像URL
        botPhoto: 'https://riversun.github.io/chatbot/bot_icon_operator.png',
        //ユーザー側のアイコン画像URL
        humanPhoto: null,
        widget: {
            //SENDボタンのラベル
            sendLabel: '送信',
            //テキストボックスのヒント
            placeHolder: ''
        }
    },
    api: {
        //チャットサーバーのURL
        endpoint: 'http://localhost:8080/chat',
        //'GET'または'POST'
        method: 'GET',
        //'json'または'jsonp'
        dataType: 'json',
        errorResponse: {
            output: [
                //ネットワークエラー発生時のエラーメッセージ
                {type: 'text', value: 'ネットワークエラーが発生しました'}
            ]
        }
    },
    //PCモードの場合に表示される小窓(ウィンドウ)の設定
    window: {
        //ウィンドウのタイトル
        title: '私のチャットボット',
        //チャットウィンドウ左上のアイコンをクリックしたときにジャンプするURL
        infoUrl: 'https://github.com/riversun/chatux',
        size: {
            width: 350,//ウィンドウの幅
            height: 500,//ウィンドウの高さ
            minWidth: 300,//ウィンドウの最小幅
            minHeight: 300,//ウィンドウの最小高さ
            titleHeight: 50//ウィンドウのタイトルバー高さ
        },
        appearance: {
            //ウィンドウのボーダースタイル
            border: {
                shadow: '2px 2px 10px  rgba(0, 0, 0, 0.5)',//影
                width: 0,//ボーダーの幅
                radius: 6//ウィンドウの角丸半径
            },
            //ウィンドウのタイトルバーのスタイル
            titleBar: {
                fontSize: 14,
                color: 'white',
                background: 'black',
                leftMargin: 40,
                height: 40,
                buttonWidth: 36,
                buttonHeight: 16,
                buttonColor: 'white',
                buttons: [
                    //閉じるボタン
                    {
                        //閉じるボタンのアイコン(fontawesome)
                        fa: 'fas fa-times',
                        name: 'hideButton',
                        visible: true  //true:表示する
                    }
                ],
                buttonsOnLeft: [
                    //左上のinforUrlボタン
                    {
                        fa: 'fas fa-comment-alt',//specify font awesome icon
                        name: 'info',
                        visible: true  //true:表示する
                    }
                ],
            },
        }
    },
    //チャット起動ボタンの位置
    wakeupButton: {
        right: 20,
        bottom: 20,
        size: 60,
        fontSize: 25//フォントサイズ
    },
    //イベントのコールバックメソッド
    methods: {
        onChatWindowCreate: (win) => {
            //チャットUIが生成された時
        },
        onChatWindowPause: (win) => {
            //チャットUIが閉じられた時
        },
        onChatWindowResume: (win) => {
            //チャットUIが復帰した時
        },
        onUserInput: (userInputText) => {
            //ユーザーがテキストを入力したとき
            //(テキストをインターセプトする)
            console.log('#onUserInput userInputText=' + userInputText);
            if (userInputText === 'おしまい') {
                //チャットUIを終了する
                chatux.dispose();
                //consumed=trueで返すと、テキストはサーバーには送信されない
                const consumed = true;
                return consumed;
            }
        },
        onServerResponse: (response) => {
            //チャットサーバーのレスポンスを受け取った時
            //レンダリング前なのでresponseを編集するとレンダリング結果を変更できる
            console.log('#onServerResponse response=' + JSON.stringify(response));
            return response;
        }
    }
};
chatux.init(initParam);
//trueを指定すると、チャットUIを自動的に開く
chatux.start(true);

見た目、スタイリング

チャット起動ボタンやチャットUI内部はCSSでスタイリングが可能

上の例では、以下のようなCSSを指定している

.chatux-btn-chat {
    color: #fff;
    background-color: black;
    border-color: black;
}
.chatux-btn-chat:hover {
    background-color: black;
    border-color: black;
}

.botui-container {
    font-size: 14px;
    background-color: #7193c1;
    font-family: "Open Sans", sans-serif;
}

.botui-actions-text-input {
    border: 0;
    outline: none;
    border-radius: 0;
    padding: 5px 7px;
    font-family: "Open Sans", sans-serif;
    background-color: transparent;
    color: white;
    width: 70%;
    border-bottom: 2px solid white;
}

CSSのコードはこちら

CSSによるスタイリングのより詳細な設定例
【チャットUIの外側】
https://github.com/riversun/chatux/blob/master/src/app.css

【チャットUIの内側(botUI)】
https://github.com/riversun/chatux/blob/master/src/botui-theme-riversun.css

本節のソースコード一式

本節で説明した見た目スタイリングをカスタムしたnpmプロジェクトのソースコード
(+サーバーとクライアントを1つのnpmプロジェクトに統合)
https://github.com/riversun/chatux-examples-ja/tree/master/step04/chatall

ChatUxを構成している要素技術

これまでみてきたチャットUIをお手軽に実現するChatUxモジュールでは以下のライブラリを内部で利用しているので興味があれば、以下リポジトリが参考になる

チャットサーバー側のカスタムについて

  • 今回はサーバー側は触り程度の説明だったが、セッション機能を利用すればステートフルなチャットボット、チャットサービスをつくることが可能だし、認証機能を入れれば、ユーザーにあったチャットコンテンツを提供することも可能になる。
  • お試し程度であればさきほど紹介したGASも地味に便利

人工無脳からAIチャットサーバーへ

  • 今回の例ではIF文をつかった原始的な対話をサンプルとして紹介した
  • 手続き型の対話であれば Watson などを使ってテキスト→意図分類→対話応答生成という手順で設計すれば本格的な対話アプリは作れるし、WatsonつかわなくてもNLP系のライブラリは最近充実してきているので、それらを活用すればそれっぽいチャットボットを作ることも可能。

ご参考
Watsonをモチーフにして、対話アプリを解説した記事

クエリーパラメータやHTTPヘッダの付加

クライアントからチャットサーバー側に追加のクエリパラメータや、HTTPヘッダを送信する事も可能

HTTPヘッダの追加方法

クライアントからサーバーへの送信時にHTTPヘッダを追加するには以下のように、初期化時に、headersを設定する

HTTPheaderを追加する
chatux.init({
    api: {
        endpoint: 'http://localhost:8080/chat',
        method: 'GET',
        dataType: 'json',
        headers:{
            'Authorization':'Bearer ABCD123ABCD123ABCD123',
            'X-Additional-Headers':'something_value'
        }
    }
});

初期化時ではなく、送信時にHTTPヘッダを追加することも可能

認証情報を付加したい場合などで活用できる

以下のようにする

onPrepareRequestは通信の直前に呼び出されるので、そこでhttpClient.headersに追加したいヘッダ情報を入れてやればOK

chatux.init({
    api: {
        endpoint: 'http://localhost:8080/chat',
        method: 'GET',
        dataType: 'json',
        },
     methods:{
        onPrepareRequest: (httpClient) => {
            httpClient.headers={};
            httpClient.headers['Authorization'] = 'Bearer ABCD123ABCD123ABCD123';
            httpClient.headers['X-Additional-Headers'] = 'something_value';
        }
     }   
});

クエリパラメータ追加方法

現状は送信ボタンをおすと、指定したURLにGETまたはPOSTでhttp://localhost:8080/chat?text=こんにちはのように送信するが、text以外のクエリを追加することが可能。

固定的にクエリパラメータを設定するには初期化時に以下のようにparamsに設定すると

chatux.init({
    api: {
        endpoint: 'http://localhost:8080/chat',
        method: 'GET',
        dataType: 'json',
        params:{
            'param1':'value1',
            'param2':'value2'
        }
    }
});

http://localhost:8080/chat?text=こんにちは&param1=value1&param2=value2のように送信される。

また、送信時にクエリを指定するには以下のように書くことも可能

chatux.init({
    api: {
        endpoint: 'http://localhost:8080/chat',
        method: 'GET',
        dataType: 'json',
        },
     methods:{
        onPrepareRequest: (httpClient) => {
            //intercept http request before sending and set query parameters
            httpClient.params.param1 = 'value1';
            httpClient.params.param2 = 'value2';
        },
        onFinishRequest: (httpClient) => {
            //delete params after sending
            delete httpClient.params.param1;
            delete httpClient.params.param2;
        }
     }   
});

onPrepareRequestは通信の直前に呼び出されるためhttpClient.paramsオブジェクト以下に追加したいクエリパラメータを付加すればOK

httpClient.paramsはチャットを通じて保持されるため、↓のように毎回の通信にparamsをのせたく無い場合はonFinishRequest(通信終了後に呼び出される)でparamsを毎回クリアしてあげればよい

        onFinishRequest: (httpClient) => {
            //delete params after sending
            delete httpClient.params.param1;
            delete httpClient.params.param2;
        }

まとめ

  • スマホもPCブラウザにも両対応したチャットUIの作り方を紹介しました
  • チャットUIの実現のために 拙作ChatUX というモジュールと使い方を説明しました
  • よりカスタムしたい場合は https://github.com/riversun/chatux のREADMEやソースが参考になるとおもいます
  • もし、この記事がお役に立てたら https://github.com/riversun/chatux のほうにもスターを頂けると作者が喜びます★★★

長文お読みいただきありがとうございました。

riversun
UX producer and Full-Stack developer with more than 15 years of experience.
https://github.com/riversun
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした