チャットボット

ここではInputManJSのコメントコンポーネントとタグボックスを組み合わせて、チャットボット風なアプリケーションを作成する方法を紹介します。

InputManJSのコメントコンポーネントとタグボックスコントロールを利用して、チャットボット風なアプリケーションを作成します。このサンプルでは、次のシナリオを確認することができます。 コメントコンポーネント:コメントコンポーネントを利用して、問い合わせ用の回答を表示しています。質問に応じた回答を、リンク、テーブル、画像、リストなどで表現しています。 タグボックスコントロール:タグボックスコントロールを利用して、問い合わせ用の質問を表示しています。
import { InputMan } from '@mescius/inputman.comment'; import '@mescius/inputman.comment/CSS/gc.inputman.comment.css'; import '@mescius/inputman/CSS/gc.inputman-js.css'; import './styles.css'; import * as GcCommonControl from '@mescius/inputman'; import { firstQuery, getMessage, getQueryItems } from './data'; GcCommonControl.InputMan.appearanceStyle = GcCommonControl.InputMan.AppearanceStyle.Modern; const gcComment = new InputMan.GcComment(document.getElementById('gcComment'), { openLinkMode: InputMan.OpenLinkMode.GoToComment, editorConfig: { editorType: InputMan.GcCommentEditorType.GcMultilineTextBox, height: 60, }, userInfo: { id: '1', name: 'testUser', }, showIcon: false, commentMode: InputMan.GcCommentMode.ChatMode, header: [], showZeroReaction: true, showOpenReactionListButton: false, contextToolbar: [] }); const postChatBot = (text) => { const date = new Date(); gcComment.execCommand(InputMan.GcCommentCommand.AddCommentElement, { comment: { id: date.valueOf().toString(), userInfo: { id: '0', name: 'チャットボット', }, content: text, postTime: date, updateTime: date, reactions: text.includes('class="finalAnswer') ? [ { reactionChar: '👍', count: 0, }, { reactionChar: '👎', count: 0, }, ] : [], scrollIntoView: false, }, }); }; //初期コメント postChatBot('ご用件をお伺いいたします。'); gcComment.addEventListener( InputMan.GcCommentEvent.AfterExecuteCommand, (args) => { if ( args.command === InputMan.GcCommentCommand.AddCommentElement && args.value.comment.userInfo.id == '1' ) { let inputMsg = args.value.comment.content; generateChat(inputMsg); } else { let renderedMsgElement = gcComment.getCommentElement( args.value.comment.id ); renderedMsgElement.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest', }); } } ); function generateChat(query) { setTimeout(() => { let message = getMessage(query); postChatBot(message); tagBox1.readOnly = false; tagBox1.value = getQueryItems(message); tagBox1.readOnly = true; }, 800); } var tagBox1 = new GcCommonControl.InputMan.GcTagBox( document.getElementById('tagBox1'), { height: 59, width: 380, value: firstQuery, readOnly: true, displayMode: GcCommonControl.InputMan.TagBoxDisplayMode.Multiline, showDropDown: false, } ); const ele = tagBox1.getElement(); let newContent = ''; //TagBoxの選択されている項目を取得する処理 ele.addEventListener('click', (e) => { if (e.target.classList.contains('tag-content') || e.target.classList.contains('tag')) { newContent = e.target.textContent; setTimeout(() => { const date = new Date(); gcComment.execCommand(InputMan.GcCommentCommand.AddCommentElement, { comment: { id: date.valueOf().toString(), userInfo: { id: '1', name: 'testUser', }, content: newContent, postTime: date, updateTime: date, }, }); }, 50); } });
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <meta name="description" content="実用例 - チャットアプリ" lang="ja" xml:lang="ja" /> <title>実用例 - チャットボット</title> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> window.onload = function () { System.import('./src/app'); }; </script> </head> <body> <div class="container-fluid"> <div class="display"> <div class="header"> <h3 class="title">InputManJS チャットボット</h3> </div> <div class="scroll-area"> <div id="gcComment"></div> </div> <div class="action-area"> <div id="tagBox1"></div> </div> </div> <p>※コメント内のリンクは無効化されています。</p> </div> </body> </html>
body { font-family: sans-serif; font-size: 14px; } .display { margin-top: 16px; height: 465px; width: 380px; margin-left: 16px; border: 1px solid #cfd8df; background-color: #fff; justify-content: center; border-radius: 5px; } .scroll-area { height: 350px; overflow-y: scroll; } .scroll-area::-webkit-scrollbar { display: none; } .header { height: 40px; width: 100%; background-color: rgb(79, 165, 235); display: flex; justify-content: center; align-items: center; color: white; border-radius: 5px; } .gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment[gcim-post-by-current-user='true'] .gcim__comment-info { display: none; } .gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment .gcim__comment-control::before { content: unset; } .gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment[gcim-post-by-current-user='false'] .gcim__comment-info { display: none; } .context-menu { display: none !important; } .gcim__gccomment .gcim__header-footer { display: none !important; } .action-area { margin-top: 15px; } .gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment .gcim__comment-control { max-width: 100% !important; } /* タグボックスのテキスト */ .tag-content { font-size: 90%; } table { display: table; border-collapse: separate; box-sizing: border-box; text-indent: initial; unicode-bidi: isolate; border-spacing: 2px; border-color: gray; } table tr>th { text-align: left; background: #f0f0f0; font-weight: normal; width: 35%; } table tr>* { padding: 0.25rem 0.5rem; border: 1px solid #d2d2d2; background: whitesmoke; } .gcim__gccomment { font-size: 12px; --gcim__gccomment-left-right-padding: 10px; --gcim__gccomment-avatar-width: 25px; --gcim__gccomment-avatar-height: 25px; } .gcim__gccomment[gcim-comment-mode='chatmode'] { --comment-padding: 8px; } .gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment .gcim__comment-control { --comment-balloon-pole-width: 7px; border-radius: 5px; } .gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment .gcim__comment-control::before { top: 7px; } .gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment[gcim-post-by-current-user='true'] .gcim__comment-control::before { right: -7px; } .gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment[gcim-post-by-current-user='false'] .gcim__comment-control::before { left: -7px; } .gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment[gcim-post-by-current-user='true'] { --backcolor: #cfeec7; } .tag { width: 41%; text-align: center; } .finalAnswer { margin-bottom: -5px; } .list { margin-top: -15px; margin-bottom: -30px } .margin { margin-bottom: -20px; } a { pointer-events: none; }
export const firstQuery = [ 'アカウントや購入について', '製品機能について', '価格やライセンスについて', 'その他', ]; export const licenseQuery = [ 'ライセンスの種類を知りたい', '製品の価格を知りたい', '前の質問に戻る', ]; export const productQuery = [ '製品の特長を紹介してほしい', '製品の使い方を知りたい', '前の質問に戻る', ]; export const accountQuery = [ 'アカウントを作成したい', 'パスワードを忘れた', '製品を購入したい', '前の質問に戻る', ]; export const returnQuery = ['最初の質問に戻る']; export const firstMessage = ['ご用件をお伺いいたします。']; export const selectItemeMessage = ['ご質問内容を選択ください。']; const otherMessage = `<p class="finalAnswer">その他のお問い合わせにつきましては、弊社営業部までお問い合わせください。</p> <table style="width:100%;"><tbody><tr><th>専用窓口</th><td>営業部</td></tr><tr><th>営業時間</th><td>月~金 10:00~12:00/13:30~17:00</td></tr><tr><th>電話番号</th><td>050-5490-4661</td></tr><tr><th>FAX番号</th><td>050-3156-3561</td></tr><tr><th>メールアドレス</th><td><a href="mailto:sales@mescius.com">sales@mescius.com</a></td></tr></td></tr></tbody></table>`; const licenseMessage = '製品や価格に関するお問い合わせですね。ご要望をお選びください。'; const productMessage = '製品機能関するお問い合わせですね。ご要望をお選びください。'; const accountMessage = 'アカウントや製品の購入に関するお問い合わせですね。ご要望をお選びください。'; const accountCreateMessage = `<p class="finalAnswer">アカウントの作成についてですね。</p><p>アカウント作成の手順は、弊社ホームページから確認することができます。<br> 製品ページ:<a href="https://developer.mescius.jp/support/user/create" target="_blank" rel="noopener">アカウント作成・変更</a><br>上記のページをご参照下さい。</p> `; const resetPasswordMessage = `<p class="finalAnswer">パスワードの再設定についてですね。</p><p>パスワードの再設定は、以下のパスワードの再設定ページから行ってください。</p> <a href="https://developer.mescius.jp/login/forgot-password" target="_blank" rel="noopener">パスワードの再設定ページ</a> `; const purchaseMessage = `<p class="finalAnswer">製品のご購入についてですね。</p><p class="margin">最新バージョンをお求めの場合には次の方法によりご購入いただけます。購入ルートによってお取引条件が異なりますので、直接お問合せください。</p> <ul class="list"> <li>弊社から直接の購入する場合:<a href="https://developer.mescius.jp/purchase/how-to-buy#directsales" target="_blank" rel="noopener">こちら</a></p>をご覧ください。</li> <li>オンラインから購入する場合:<a href="https://developer.mescius.jp/purchase/how-to-buy#onlinesales" target="_blank" rel="noopener">こちら</a></p>をご覧ください。</li> <li>販売パートナーから購入する場合:<a href="https://developer.mescius.jp/purchase/how-to-buy#partnersales" target="_blank" rel="noopener">こちら</a></p>をご覧ください。</li> </ul> `; const licenseInfoMessage = `<p class="finalAnswer">InputManJSのライセンスの種類についてですね。InputManJSには次のライセンスがあります。</p><ul class="list"> <li>開発ライセンス:InputManJSを組み込んだアプリケーションを開発する際に利用するライセンスです。</li> <li>配布ライセンス:InputManJSを組み込んだアプリケーションを配布する際に利用するライセンスです。</li> </ul> <p>ライセンスに関するより詳細な情報は、弊社製品ページから確認することができます。</p> <p>製品ページ:<a href="https://developer.mescius.jp/license/javascript" target="_blank" rel="noopener">URL</a></p>`; const priceInfoMessage = `<p class="finalAnswer">製品の価格についてですね。</p> <p class="margin">一例として、製品のを開発する際に必要な、開発ライセンス(イニシャル)の価格は、税込みで <b>121,000円</b> となります。<br>他のライセンスの価格は、弊社製品ページから確認することができます。</p> <p>製品ページ:<a href="https://developer.mescius.jp/inputmanjs/pricelist" target="_blank" rel="noopener">URL</a></p>`; const productInfoMessage = `<p class="finalAnswer">製品の特長についてですね。</p> <img src="https://demo.mescius.jp/inputmanjs/demos/resource/logo.png" style="width:100%; height:auto;" alt="InputManJSのロゴ"> <p class="margin">InputManJSは、リッチテキスト、テキスト、マスク、日付時刻、数値、コンボなど用途別に最適化された日本仕様の入力用JavaScriptコントロールセットです。収録されているコントロールの実際の動作は、弊社デモ環境で確認することができます。</p> <p>製品デモページ:<a href="https://demo.mescius.jp/inputmanjs/demos/" target="_blank" rel="noopener">InputManJS デモ</a></p>`; const useInfoMessage = `<p class="finalAnswer">製品の利用方法についてですね。<br>以下の情報をご参考にしていただければと思います。</p> <ul class="list"> <li>製品の実際の動作を確認したい場合:<a href="https://demo.mescius.jp/inputmanjs/demos/" target="_blank" rel="noopener">製品デモ</a>をご覧ください。</li> <li>製品のドキュメントを確認したい場合:<a href="https://demo.mescius.jp/inputmanjs/docs/welcome" target="_blank" rel="noopener">製品ヘルプ</a>をご覧ください。</li> <li>その他の技術情報を確認したい場合:<a href="https://support.mescius.jp/hc/ja/categories/360000686076" target="_blank" rel="noopener">ナレッジベース</a>をご覧ください。</li> </ul>`; export const getMessage = (query) => { let message = ''; switch (query) { //最初の質問の分岐 case firstQuery[2]: message = licenseMessage; break; case firstQuery[0]: message = accountMessage; break; case firstQuery[1]: message = productMessage; break; case firstQuery[3]: message = otherMessage; break; case '前の質問に戻る': message = firstMessage; break; case '': message = selectItemeMessage; break; //ライセンスに関する質問の分岐 case licenseQuery[0]: message = licenseInfoMessage; break; case licenseQuery[1]: message = priceInfoMessage; break; //アカウントに関する質問の分岐 case accountQuery[0]: message = accountCreateMessage; break; //アカウントに関する質問の分岐 case accountQuery[1]: message = resetPasswordMessage; break; case accountQuery[2]: message = purchaseMessage; break; //製品に関する質問の分岐 case productQuery[0]: message = productInfoMessage; break; case productQuery[1]: message = useInfoMessage; break; //最初の質問の分岐 case returnQuery[0]: message = firstMessage; break; default: } return message; }; export const getQueryItems = (message) => { let item = firstQuery; switch (message) { case licenseMessage: item = licenseQuery; break; case productMessage: item = productQuery; break; case accountMessage: item = accountQuery; break; case accountCreateMessage: case resetPasswordMessage: case purchaseMessage: case licenseInfoMessage: case priceInfoMessage: case productInfoMessage: case useInfoMessage: case otherMessage: item = returnQuery; break; default: } return item; };
System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { '@mescius/inputman': 'npm:@mescius/inputman/index.js', '@mescius/inputman/CSS': 'npm:@mescius/inputman/CSS', '@mescius/inputman.richtexteditor': 'npm:@mescius/inputman.richtexteditor/index.js', "@mescius/inputman.richtexteditor/CSS": "npm:@mescius/inputman.richtexteditor/CSS", '@mescius/inputman.richtexteditor/JS/plugins/advlist': 'npm:@mescius/inputman.richtexteditor/JS/plugins/advlist/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/all': 'npm:@mescius/inputman.richtexteditor/JS/plugins/all/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/autosave': 'npm:@mescius/inputman.richtexteditor/JS/plugins/autosave/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/charmap': 'npm:@mescius/inputman.richtexteditor/JS/plugins/charmap/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/directionality': 'npm:@mescius/inputman.richtexteditor/JS/plugins/directionality/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/emoticons': 'npm:@mescius/inputman.richtexteditor/JS/plugins/emoticons/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/fullscreen': 'npm:@mescius/inputman.richtexteditor/JS/plugins/fullscreen/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/htmlcode': 'npm:@mescius/inputman.richtexteditor/JS/plugins/htmlcode/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/image': 'npm:@mescius/inputman.richtexteditor/JS/plugins/image/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/link': 'npm:@mescius/inputman.richtexteditor/JS/plugins/link/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/lists': 'npm:@mescius/inputman.richtexteditor/JS/plugins/lists/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/media': 'npm:@mescius/inputman.richtexteditor/JS/plugins/media/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/pagebreak': 'npm:@mescius/inputman.richtexteditor/JS/plugins/pagebreak/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/preview': 'npm:@mescius/inputman.richtexteditor/JS/plugins/preview/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/save': 'npm:@mescius/inputman.richtexteditor/JS/plugins/save/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/searchreplace': 'npm:@mescius/inputman.richtexteditor/JS/plugins/searchreplace/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/table': 'npm:@mescius/inputman.richtexteditor/JS/plugins/table/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/template': 'npm:@mescius/inputman.richtexteditor/JS/plugins/template/plugin.js', '@mescius/inputman.richtexteditor/JS/plugins/wordcount': 'npm:@mescius/inputman.richtexteditor/JS/plugins/wordcount/plugin.js', '@mescius/inputman.comment': 'npm:@mescius/inputman.comment/index.js', '@mescius/inputman.comment/CSS': 'npm:@mescius/inputman.comment/CSS', '@mescius/wijmo': 'npm:@mescius/wijmo/index.js', '@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles', '@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures', '@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js', '@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js', '@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js', '@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js', '@mescius/spread-sheets-resources-ja': 'npm:@mescius/spread-sheets-resources-ja/index.js', '@mescius/spread-sheets/styles': 'npm:@mescius/spread-sheets/styles', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build': 'npm:systemjs-plugin-babel/systemjs-babel-browser.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, } });