0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Monaca + NCMB + ChatGPTでAIチャットアプリを作る(その3: チャット画面の作成)

Last updated at Posted at 2023-04-06

NCMBでは公式SDKとしてSwift/Objective-C/Kotlin/Java/Unity/JavaScript SDKを用意しています。また、それ以外にもコミュニティSDKとして、非公式ながらFlutter/React Native/Google Apps Script/C#/Ruby/Python/PHPなど幅広い言語向けにSDKが開発されています。

今回は公式SDKの一つ、JavaScript SDKとFramework7を使ってChatGPTを用いたチャットアプリを作ってみます。前回はスクリプトでの処理について解説しましたので、今回はチャット画面を作成します。

コード

今回のコードは NCMBMania/monaca-chatgpt にアップロードしてあります。実装時の参考にしてください。

チャット画面について

このファイルは www/pages/chat.html として作成しています。チャット画面はFramework7のメッセージコンポーネントを用いるので、HTMLはとても簡単です。

<template>
  <div class="page" data-name="chat"> <!-- ページ全体のコンテナ -->
    <div class="navbar"> <!-- ナビゲーションバー -->
      <div class="navbar-bg"></div>
      <div class="navbar-inner sliding">
        <div class="title">AIチャット</div> <!-- タイトル -->
      </div>
    </div>
    <div class="toolbar messagebar"> <!-- メッセージツールバー -->
      <div class="toolbar-inner">
        <div class="messagebar-area">
          <textarea class="resizable" placeholder="Message"></textarea> <!-- テキストエリア -->
        </div>
        <a class="link icon-only demo-send-message-link" @click=${send}> <!-- メッセージ送信ボタン -->
          <i class="icon f7-icons if-not-md">arrow_up_circle_fill</i>
          <i class="icon material-icons md-only">send</i>
        </a>
      </div>
      <div class="messagebar-sheet">
      </div>
    </div>
    <div class="page-content messages-content"> <!-- メッセージ表示エリア -->
      <div class="messages"> <!-- メッセージ表示用の要素 -->
      </div>
    </div>
  </div>
</template>

JavaScriptの実装

JavaScriptは同じ chat.html の中に記述します。まず、スクリプトタグと基本となる関数のエクスポートを定義します。

<script>
	export default (props, { $f7, $onMounted, $store, $update, $tick }) => {
	  // この中に処理を記述する
	  return $render;
	}
</script>

$f7$onMounted はFramework7の用意するユーティリティ変数です。何ができるかはFramework7入門 ルーティング・DOM操作・ストアを学ぼう – モナカプレスを参考にしてください。

必要な変数の定義

まずNCMBのデータストアのクラスやチャットメッセージ用の変数を定義します。

const Chat = ncmb.DataStore("Chat"); // データストア"Chat"の生成
let messages; // メッセージを表示するための変数
let messageBar; // メッセージを入力するための変数
const chats = []; // チャット履歴を保持するための配列

画面がマウントされた際の処理

画面がHTMLとしてマウントされた際の処理でチャット画面の初期化や、過去のメッセージを取得します。

$onMounted(async () => { // コンポーネントがマウントされたときに実行される関数
  messages = $f7.messages.create({ // messagesの初期化
    el: $('.messages'), // 表示する要素
  });
  messageBar = $f7.messagebar.create({ // messageBarの初期化
    el: $('.messagebar'), // 入力する要素
    attachments: [] // 添付ファイルの初期値
  });
  const date = new Date;
  date.setDate(date.getDate() - 1);
  const ary = await Chat
    .greaterThanOrEqualTo('createDate', date) // createDateが指定日以降のデータを取得
    .fetchAll(); // 取得した全てのデータを配列で取得
  ary.forEach(chat => addMessage(chat)); // 取得したデータをメッセージ表示エリアに追加
  $update(); // レンダリングの更新
});

addMessage 関数はチャットデータを配列に登録し、チャット画面用のデータを作成します。

const addMessage = (chat) => { // メッセージ表示エリアにメッセージを追加する関数
  messages.addMessage({ // メッセージを追加
    text: chat.content, // メッセージの内容
    type: chat.role === 'user' ? 'sent' : 'received', // メッセージの送信元
    name: chat.role === 'user' ? 'Me': 'Chat GPT', // メッセージの送信先
    avatar: chat.role === 'user' ? '/assets/icons/person.png' : '/assets/icons/robot.png', // アバター画像
  });
  chats.push(chat); // チャット履歴に追加
};

チャットを送信・保存する処理

チャットメッセージを書き込んで送信ボタンを押すと send 関数が呼び出されます。

const send = async () => { // メッセージ送信のイベントハンドラ
  // 以下はこの中に記述
};

テキストを取得

まず入力されたテキストを取得し、そのデータをNCMBに保存します。

const content = messageBar.getValue().replace(/\n/g, '<br />').trim(); // 入力されたメッセージを取得
if (content === '') return; // メッセージが空の場合は送信しない
const chat = await saveChat('user', content); // チャットを保存

saveChat 関数は以下の通りです。これだけでクラウドのデータベースにチャットメッセージが保存されます。

const saveChat = (role, content) => { // チャットを保存する関数
  const chat = new Chat(); // 新しいチャットを生成
  return chat
    .set('content', content) // チャットの内容を設定
    .set('role', role) // チャットの送信元を設定
    .save(); // チャットを保存
};

保存されたチャットは先ほどの addMessage 関数を使って配列に追加します。追加したら表示を更新し、さらにその画面更新が完了するのを $tick で待ちます。

addMessage(chat); // メッセージ表示エリアにメッセージを追加
$update(); // レンダリングの更新
await $tick(); // レンダリングの完了を待つ

これまでのメッセージ履歴を作成

ここからOpenAI API部分の処理になります。まずデータストアから取得したメッセージ履歴を使って、過去のやり取りを作成します。

// ここからAIチャット
const pastMessages = chats.map(chat => { // チャット履歴をAIに送信するための配列を作成
  return {
    role: chat.role,
    content: chat.content,
  };
});

OpenAI APIの呼び出し

OpenAI APIは時間がかかるので、プログレスとしてチャット入力中の表示を出します。

messages.showTyping(); // タイピング中の表示

そしてNCMBのスクリプトを実行します。これでOpenAI APIを呼び出して、結果を受け取ります。

const res = await ncmb.Script // テキスト生成スクリプトを実行
  .data({
    content,
    messages: pastMessages,
  })
  .exec("POST", "text.js");

結果をデータストアに保存

受け取った結果はボット(assistant)のメッセージとしてデータストアに保存します。

const { text } = JSON.parse(res.body); // スクリプトの結果を取得
const aiChat = await saveChat('assistant', text); // AIの返答を保存
addMessage(aiChat); // メッセージ表示エリアにAIの返答を追加

後は表示の更新と、入力中表示を消して完了です。

messages.hideTyping(); // タイピング中の表示を非表示にする
messageBar.clear(); // 入力欄をクリア
$update(); // レンダリングの更新

これで完成です。

0987 localhost - 0406171301.jpg

全体のコード

chat.html のJavaScript部分のコードは以下の通りです。

export default (props, { $f7, $onMounted, $store, $update, $tick }) => {
  const Chat = ncmb.DataStore("Chat"); // データストア"Chat"の生成
  let messages; // メッセージを表示するための変数
  let messageBar; // メッセージを入力するための変数
  const chats = []; // チャット履歴を保持するための配列
  $onMounted(async () => { // コンポーネントがマウントされたときに実行される関数
    messages = $f7.messages.create({ // messagesの初期化
      el: $('.messages'), // 表示する要素
    });
    messageBar = $f7.messagebar.create({ // messageBarの初期化
      el: $('.messagebar'), // 入力する要素
      attachments: [] // 添付ファイルの初期値
    });
    const date = new Date;
    date.setDate(date.getDate() - 1);
    const ary = await Chat
      .greaterThanOrEqualTo('createDate', date) // createDateが指定日以降のデータを取得
      .fetchAll(); // 取得した全てのデータを配列で取得
    ary.forEach(chat => addMessage(chat)); // 取得したデータをメッセージ表示エリアに追加
    $update(); // レンダリングの更新
  });

  const addMessage = (chat) => { // メッセージ表示エリアにメッセージを追加する関数
    messages.addMessage({ // メッセージを追加
      text: chat.content, // メッセージの内容
      type: chat.role === 'user' ? 'sent' : 'received', // メッセージの送信元
      name: chat.role === 'user' ? 'Me': 'Chat GPT', // メッセージの送信先
      avatar: chat.role === 'user' ? '/assets/icons/person.png' : '/assets/icons/robot.png', // アバター画像
    });
    chats.push(chat); // チャット履歴に追加
  };
  const saveChat = (role, content) => { // チャットを保存する関数
    const chat = new Chat(); // 新しいチャットを生成
    return chat
      .set('content', content) // チャットの内容を設定
      .set('role', role) // チャットの送信元を設定
      .save(); // チャットを保存
  };
  const send = async () => { // メッセージ送信のイベントハンドラ
    const content = messageBar.getValue().replace(/\n/g, '<br />').trim(); // 入力されたメッセージを取得
    if (content === '') return; // メッセージが空の場合は送信しない
    const chat = await saveChat('user', content); // チャットを保存
    addMessage(chat); // メッセージ表示エリアにメッセージを追加
    $update(); // レンダリングの更新
    await $tick(); // レンダリングの完了を待つ
    // ここからAIチャット
    const pastMessages = chats.map(chat => { // チャット履歴をAIに送信するための配列を作成
      return {
        role: chat.role,
        content: chat.content,
      };
    });
    messages.showTyping(); // タイピング中の表示
    const res = await ncmb.Script // テキスト生成スクリプトを実行
      .data({
        content,
        messages: pastMessages,
      })
      .exec("POST", "text.js");
    const { text } = JSON.parse(res.body); // スクリプトの結果を取得
    const aiChat = await saveChat('assistant', text); // AIの返答を保存
    addMessage(aiChat); // メッセージ表示エリアにAIの返答を追加
    messages.hideTyping(); // タイピング中の表示を非表示にする
    messageBar.clear(); // 入力欄をクリア
    $update(); // レンダリングの更新
  };
  return $render;
}

まとめ

今回のAIチャットアプリではNCMBの以下の機能を利用しました。

  • データストア
    • データ保存
    • データ検索
  • スクリプト
    • OpenAI APIの実行

NCMBには他にもファイルストアや認証、プッシュ通知などの機能があります。ぜひそれらの機能も利用してアプリを開発してください。

mBaaSでサーバー開発不要! | ニフクラ mobile backend

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?