LoginSignup
29
18

More than 5 years have passed since last update.

Cloud Functionsでチャットのメッセージを監視&プッシュ通知

Last updated at Posted at 2018-05-09

やりたいこと

アプリからメッセージを送信→DB(Firestore)にメッセージが保存される→それを監視していたCloudFunctionsがプッシュ通知を送る

Cloud Functions で可能な処理

関心事が発生したときにユーザーに通知する

notify.png

これと同じようなイメージ。

Firestore設計

chatrooms(コレクション)
 ∟chatroom1(ドキュメント)
  ∟messages(コレクション)
   ∟message1(ドキュメント)
   ∟message2(ドキュメント)
   ∟message3(ドキュメント)

 ∟chatroom2(ドキュメント)
  ∟messages(コレクション)
   ∟message1(ドキュメント)
   ∟message2(ドキュメント)

  • ルートレベルにchatroomsコレクションがある
  • chatroomsドキュメント配下にmessagesサブコレクションがある

このように設計しました。

アプリ設計

設計というほどではないのですが、以下のように事前にトピックを購読させます。
タイミングとしてはLINEでいうところの「グループチャットに参加する」のようなものを契機にすればよいのではないでしょうか。

その際トピック名としてチャットルームのドキュメントIDを指定します。

Messaging.messaging().subscribe(toTopic: "/topics/\(chatroomドキュメントのid)")

こうすることで/topics/{chatroomドキュメントのid}宛てにFCMメッセージが送られた際にプッシュ通知を受け取れるようになります。

CloudFunctionsの設計

CLIツールをインストールしていない場合はインストールしてください。
$ npm install -g firebase-tools

適当にディレクトリを作ってそこでプロジェクトを初期化します。
$ firebase init functions

image.png

プロジェクトを選択し、JavascriptかTypescriptを選択します。
今回はJavascriptを選択した前提で話を進めます。
そのあと色々聞かれますが全てEnterでOKです。

image.png
こんなファイル群が自動で生成されるはずです。

index.jsに以下を追記します。(公式の手順通りですが)
これで

functions/index.js
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

これでFirestoreにアクセスできるようになります。
続いて肝心のプッシュ通知を行う関数を定義します。

Firestoreへの追加をトリガーにプッシュ通知を送っています。
こちらを参考にしました。
Cloud Functions による Cloud Firestore の拡張

functions/index.js
exports.sendNotifications = functions.firestore
    .document('/chatrooms/{chatroomId}/messages/{messageId}')
    .onCreate((event, snapshot) => { // 公式ではeventとなっていたが、そうするとevent.params.chatroomIdのように値を取ってこれなかった。

        var topic = snapshot.params.chatroomId; // こうすることで上で指定したドキュメントのパスの{chatroomId}部分が取得できる。

        var payload = {
          notification: {
            title: "新着メッセージ", // この辺りを動的に取ってくるケースが多いかな?
            body: "新着メッセージがあります"
          }
        };

        var options = {
          priority: "high"
        };

        // 指定したtopic宛てにメッセージを送る
        return admin.messaging().sendToTopic(topic, payload, options)
          .then(function(response) {
            return console.log("Successfully sent message:", response);
          })
          .catch(function(error) {
            return console.log("Error sending message:", error);
          });
    });

そしてデプロイします。

$ firebase deploy --only functions

上記のコードであればエラーは出ないと思いますが、当初自分はsendToTopic内のconsole.logにreturnをつけていなかったので以下のようにLintで怒られました。
Each then() should return a value or throw promise/always-return

うまくデプロイが済めばこうなるはずです。

✔  functions: Finished running predeploy script.
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (49.67 KB) for uploading
✔  functions: functions folder uploaded successfully
i  functions: creating function sendNotifications...
✔  functions[generateThumbnail]: Successful delete operation. 
✔  functions[sendNotifications]: Successful create operation. 

✔  Deploy complete!

そしてアプリからメッセージを送信します。(当記事ではメッセージ送信箇所について言及していませんが、Firestoreのmessageドキュメントを生成する処理を実行すればOKです。アプリでそれを実装するのが面倒な場合はFirestoreのコンソールから直接messagesドキュメントを作っても動作確認は可能です。)
すると、そのチャットルームを購読している端末に通知がいくと思います。

Cloud Functionsのログにも結果が出力されているはずです。

image.png

もっといい方法があれば教えていただきたいです。

29
18
1

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
29
18