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を使ってOpenAIの画像生成APIを用いたAI画像生成アプリを作ってみます。前回は画像生成に関わるスクリプトの処理について解説しましたので、今回はチャット画面を作成します。
コード
今回のコードは NCMBMania/monaca-image-gen にアップロードしてあります。実装時の参考にしてください。
チャット画面について
このファイルは 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 }) => {
// この中に処理を記述する
return $render;
}
</script>
$f7
や $onMounted
はFramework7の用意するユーティリティ変数です。何ができるかはFramework7入門 ルーティング・DOM操作・ストアを学ぼう – モナカプレスを参考にしてください。
必要な変数の定義
まずNCMBのデータストアのクラスやチャットメッセージ用の変数を定義します。
// "ImageGen"というデータストアオブジェクトを初期化する。
const ImageGen = ncmb.DataStore("ImageGen");
// メッセージコンテナとメッセージ入力バーを保持する変数を宣言する。
let messages;
let messageBar;
画面がマウントされた際の処理
画面がHTMLとしてマウントされた際の処理でチャット画面の初期化や、過去のメッセージを取得します。
// コンポーネントがマウントされた時に、メッセージコンテナとメッセージ入力バーを作成し、
// 過去のメッセージを表示する。
$onMounted(async () => {
messages = $f7.messages.create({
el: $('.messages'),
});
messageBar = $f7.messagebar.create({
el: $('.messagebar'),
attachments: []
});
// データは昨日以降を対象とする
const date = new Date;
date.setDate(date.getDate() - 1);
// データストアからデータを取得する
const ary = await ImageGen
.greaterThanOrEqualTo('createDate', date)
.fetchAll();
// データをチャット画面に表示する
for (const imageGen of ary) {
await addMessage(imageGen);
}
});
addMessage
関数はチャットデータを messages
に登録し、画像がある場合にはファイルストアからダウンロードしています。ダウンロードされるのは Blob
なので、 URL.createObjectURL
を使って blob://
ではじまるURLに変換しています。
// メッセージをメッセージコンテナに追加する非同期関数。
const addMessage = async (imageGen) => {
// テキスト部分
messages.addMessage({
type: 'sent',
avatar: '/assets/icons/person.png',
name: 'Me',
text: imageGen.content,
});
// 画像がなければ終了
if (!imageGen.image) return;
// 画像のダウンロード
const blob = await ncmb.File.download(imageGen.image, 'blob');
// BlobからURLを作成
const url = URL.createObjectURL(blob);
// 画像を表示
messages.addMessage({
type: 'received',
avatar: '/assets/icons/robot.png',
name: 'Gen',
imageSrc: url,
});
};
チャットを送信・保存する処理
チャットメッセージを書き込んで送信ボタンを押すと send
関数が呼び出されます。
const send = async () => { // メッセージ送信のイベントハンドラ
// 以下はこの中に記述
};
テキストを取得
まず入力されたテキストを取得し、そのデータをNCMBのデータストアインスタンスにします(まだ保存しません)。
// メッセージ入力バーからテキストを取得し、改行を<br />に変換する。
const prompt = messageBar.getValue().replace(/\n/g, '<br />').trim();
// テキストが空なら終了
if (prompt === '') return;
// データストアのオブジェクトを作成
const imageGen = new ImageGen();
// 入力されていた文字列をセット
imageGen.set('content', prompt);
OpenAI APIの呼び出し
OpenAI APIは時間がかかるので、プログレスとしてチャット入力中の表示を出します。
// メッセージを送信中にする
messages.showTyping();
そしてNCMBのスクリプトを実行します。これでOpenAI APIを呼び出して、結果を受け取ります。
// 入力文字から画像を生成する
const res = await ncmb.Script
.data({
prompt,
})
.exec("POST", "image.js");
結果をデータストアに保存
受け取った結果は imageGen
の image
に紐付けて保存します。
// 生成した画像のファイル名を取得
const { fileName } = JSON.parse(res.body);
// 画像のファイル名をセット
imageGen.set('image', fileName);
// データストアに保存
await imageGen.save();
後は表示の更新と、入力中表示を消して完了です。
// メッセージをチャット画面に表示
addMessage(imageGen);
// メッセージ入力バーをクリア
messageBar.clear();
// メッセージの送信中表示をやめる
messages.hideTyping();
これで完成です。
実行例
以下は実行例です。なお、レスポンスが返ってくるまでは10秒以上かかるのでカットしています。
全体のコード
chat.html
のJavaScript部分のコードは以下の通りです。
<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>
<script>
// コンポーネントをエクスポートするデフォルト関数。
// この関数は、"props"と"$f7"と"$onMounted"を含むオブジェクトを引数に取ります。
export default (props, { $f7, $onMounted }) => {
// "ImageGen"というデータストアオブジェクトを初期化する。
const ImageGen = ncmb.DataStore("ImageGen");
// メッセージコンテナとメッセージ入力バーを保持する変数を宣言する。
let messages;
let messageBar;
// コンポーネントがマウントされた時に、メッセージコンテナとメッセージ入力バーを作成し、
// 過去のメッセージを表示する。
$onMounted(async () => {
messages = $f7.messages.create({
el: $('.messages'),
});
messageBar = $f7.messagebar.create({
el: $('.messagebar'),
attachments: []
});
// データは昨日以降を対象とする
const date = new Date;
date.setDate(date.getDate() - 1);
// データストアからデータを取得する
const ary = await ImageGen
.greaterThanOrEqualTo('createDate', date)
.fetchAll();
// データをチャット画面に表示する
for (const imageGen of ary) {
await addMessage(imageGen);
}
});
// メッセージをメッセージコンテナに追加する非同期関数。
const addMessage = async (imageGen) => {
// テキスト部分
messages.addMessage({
type: 'sent',
avatar: '/assets/icons/person.png',
name: 'Me',
text: imageGen.content,
});
// 画像がなければ終了
if (!imageGen.image) return;
// 画像のダウンロード
const blob = await ncmb.File.download(imageGen.image, 'blob');
// BlobからURLを作成
const url = URL.createObjectURL(blob);
// 画像を表示
messages.addMessage({
type: 'received',
avatar: '/assets/icons/robot.png',
name: 'Gen',
imageSrc: url,
});
};
// メッセージを送信するための非同期関数。
const send = async () => {
// メッセージ入力バーからテキストを取得し、改行を<br />に変換する。
const prompt = messageBar.getValue().replace(/\n/g, '<br />').trim();
// テキストが空なら終了
if (prompt === '') return;
// データストアのオブジェクトを作成
const imageGen = new ImageGen();
// 入力されていた文字列をセット
imageGen.set('content', prompt);
// メッセージを送信中にする
messages.showTyping();
// 入力文字から画像を生成する
const res = await ncmb.Script
.data({
prompt,
})
.exec("POST", "image.js");
// 生成した画像のファイル名を取得
const { fileName } = JSON.parse(res.body);
// 画像のファイル名をセット
imageGen.set('image', fileName);
// データストアに保存
await imageGen.save();
// メッセージをチャット画面に表示
addMessage(imageGen);
// メッセージ入力バーをクリア
messageBar.clear();
// メッセージの送信中表示をやめる
messages.hideTyping();
};
return $render;
}
</script>
まとめ
今回のAI画像生成アプリではNCMBの以下の機能を利用しました。
- データストア
- データ保存
- データ検索
- ファイルストア
- ファイルアップロード
- ファイルダウンロード
- スクリプト
- OpenAI APIの実行
NCMBには他にも認証、プッシュ通知などの機能があります。ぜひそれらの機能も利用してアプリを開発してください。