使用雲端訊息傳遞和雲端函數發送 Web 應用程式的通知

1. 概述

在此 Codelab 中,您將了解如何使用 Cloud Functions for Firebase 透過向聊天應用程式的使用者發送通知來為聊天 Web 應用程式新增功能。

3b1284f5144b54f6.png

你將學到什麼

  • 使用 Firebase SDK 建立 Google Cloud Functions。
  • 基於身份驗證、Cloud Storage 和 Cloud Firestore 事件觸發 Cloud Functions。
  • 為您的 Web 應用程式新增 Firebase 雲端訊息支援。

你需要什麼

  • 一張信用卡。 Cloud Functions for Firebase 需要 Firebase Blaze 計劃,這表示您必須使用信用卡對 Firebase 項目啟用計費。
  • 您選擇的 IDE/文字編輯器,例如WebStormAtomSublime
  • 安裝了 NodeJS v9 後運行 shell 命令的終端。
  • Chrome 等瀏覽器。
  • 範例程式碼。請參閱下一步。

2. 取得範例程式碼

從命令列克隆GitHub 儲存庫

git clone https://github.com/firebase/friendlychat

導入入門應用程式

使用 IDE 開啟或匯入android_studio_folder.png範例程式碼目錄中的cloud-functions-start目錄。此目錄包含 Codelab 的起始程式碼,其中包含功能齊全的聊天 Web 應用程式。

3. 建立 Firebase 專案並設定您的應用

創建專案

Firebase 控制台中,按一下「新增專案」並將其命名為「FriendlyChat」

按一下「建立專案」

升級至 Blaze 計劃

為了使用 Cloud Functions for Firebase,您必須將 Firebase 專案升級到Blaze 計費計畫。這需要您將信用卡或其他結算方式新增至您的 Google Cloud 帳戶。

所有 Firebase 專案(包括 Blaze 計劃中的專案)仍然可以使用 Cloud Functions 的免費使用配額。此 Codelab 中概述的步驟將屬於免費方案的使用限制範圍。但是,您會看到用於託管 Cloud Functions 建置映像的 Cloud Storage 收取少量費用(約 0.03 美元)。

如果您無法使用信用卡或不願意繼續使用 Blaze 計劃,請考慮使用Firebase 模擬器套件,它允許您在本機上免費模擬 Cloud Functions。

啟用Google身份驗證

為了讓使用者登入應用程序,我們將使用需要啟用的 Google 驗證。

在 Firebase 控制台中,開啟「建置」部分 > 「驗證」 > 「登入方法」標籤(或按一下此處前往那裡)。然後,啟用Google Sign-in Provider 並點擊Save 。這將允許用戶使用其 Google 帳戶登入網路應用程式。

另外,請隨意將您的應用程式的面向公眾的名稱設定為Friendly Chat

8290061806aacb46.png

啟用雲端儲存

該應用程式使用雲端儲存上傳圖片。若要在 Firebase 專案上啟用 Cloud Storage,請造訪「儲存」部分並按一下「開始」按鈕。完成那裡的步驟,對於雲端儲存位置,將有一個要使用的預設值。之後點選“完成”

新增網頁應用程式

在 Firebase 控制台上,新增一個 Web 應用程式。為此,請轉到“項目設定”並向下捲動至“新增應用程式” 。選擇 Web 作為平台並選取用於設定 Firebase 託管的複選框,然後註冊應用程式並按一下「下一步」執行其餘步驟,最後按一下「繼續控制台」

4. 安裝 Firebase 命令列介面

Firebase 命令列介面 (CLI) 將允許您在本機提供 Web 應用程式並部署 Web 應用程式和 Cloud Functions。

若要安裝或升級 CLI,請執行以下 npm 命令:

npm -g install firebase-tools

若要驗證 CLI 是否已正確安裝,請開啟控制台並執行:

firebase --version

確保 Firebase CLI 的版本高於4.0.0 ,以便它具有 Cloud Functions 所需的所有最新功能。如果沒有,請執行npm install -g firebase-tools進行升級,如上所示。

透過執行以下命令來授權 Firebase CLI:

firebase login

確保您位於cloud-functions-start目錄中,然後設定 Firebase CLI 以使用您的 Firebase 專案:

firebase use --add

接下來,選擇您的項目 ID 並按照說明進行操作。出現提示時,您可以選擇任何別名,例如codelab

5. 部署並運行Web應用程式

現在您已經匯入並配置了項目,您可以首次執行 Web 應用程式了!開啟終端機窗口,導覽至cloud-functions-start資料夾,然後使用下列命令將 Web 應用程式部署至 Firebase 託管:

firebase deploy --except functions

這是您應該看到的控制台輸出:

i deploying database, storage, hosting
✔  database: rules ready to deploy.
i  storage: checking rules for compilation errors...
✔  storage: rules file compiled successfully
i  hosting: preparing ./ directory for upload...
✔  hosting: ./ folder uploaded successfully
✔ storage: rules file compiled successfully
✔ hosting: 8 files uploaded successfully
i starting release process (may take several minutes)...

✔ Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlychat-1234/overview
Hosting URL: https://friendlychat-1234.firebaseapp.com

開啟網路應用程式

最後一行應顯示託管 URL。現在應該從此 URL 提供 Web 應用程序,其格式應為 https://<project-id>.firebaseapp.com。打開它。您應該會看到聊天應用程式的功能 UI。

使用“使用 GOOGLE 登入”按鈕登入應用程序,並隨意添加一些訊息和發布圖像:

3b1284f5144b54f6.png

如果您首次在新瀏覽器上登入應用程序,請確保在出現提示時允許通知: 8b9d0c66dc36153d.png

我們稍後需要啟用通知。

如果您不小心點擊了「封鎖」 ,則可以透過點擊 Chrome 多功能欄中 URL 左側的「安全性」按鈕並切換「通知」旁的欄位來變更此設定:

e926868b0546ed71.png

現在,我們將使用 Firebase SDK for Cloud Functions 添加一些功能。

6. 函數目錄

Cloud Functions 讓您可以輕鬆擁有在雲端中運行的程式碼,而無需設定伺服器。我們將逐步介紹如何建立對 Firebase Auth、Cloud Storage 和 Firebase Firestore 資料庫事件做出反應的函數。讓我們從 Auth 開始。

使用適用於 Cloud Functions 的 Firebase SDK 時,您的 Functions 程式碼將位於functions目錄下(預設)。您的 Functions 程式碼也是Node.js應用程序,因此需要一個package.json來提供有關您的應用程式的一些資訊並列出依賴項。

為了讓您更輕鬆,我們已經建立了您的程式碼所在的functions/index.js檔案。在繼續之前請隨意檢查此文件。

cd functions
ls

如果您不熟悉Node.js ,在繼續 Codelab 之前了解更多相關資訊將會有所幫助。

package.json檔案已列出兩個必要的依賴: Firebase SDK for Cloud FunctionsFirebase Admin SDK 。要在本地安裝它們,請轉到functions資料夾並運行:

npm install

現在讓我們來看看index.js檔:

索引.js

/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 * ...
 */

// TODO(DEVELOPER): Import the Cloud Functions for Firebase and the Firebase Admin modules here.

// TODO(DEVELOPER): Write the addWelcomeMessage Function here.

// TODO(DEVELOPER): Write the blurImages Function here.

// TODO(DEVELOPER): Write the sendNotification Function here.

我們將導入所需的模組,然後寫三個函數來取代 TODO。讓我們從導入所需的 Node 模組開始。

7. 導入 Cloud Functions 和 Firebase 管理模組

在此 Codelab 期間將需要兩個模組: firebase-functions支援編寫 Cloud Functions 觸發器和日誌,而firebase-admin支援在具有管理員存取權限的伺服器上使用Firebase 平台來執行操作,例如寫入Cloud Firestore 或發送FCM 通知。

index.js檔案中,將第一個TODO替換為以下內容:

索引.js

/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 * ...
 */

// Import the Firebase SDK for Google Cloud Functions.
const functions = require('firebase-functions');
// Import and initialize the Firebase Admin SDK.
const admin = require('firebase-admin');
admin.initializeApp();

// TODO(DEVELOPER): Write the addWelcomeMessage Function here.

// TODO(DEVELOPER): Write the blurImages Function here.

// TODO(DEVELOPER): Write the sendNotification Function here.

當部署到 Cloud Functions 環境或其他 Google Cloud Platform 容器時,Firebase Admin SDK 可以自動配置,當我們不帶參數呼叫admin.initializeApp()時,就會發生這種情況。

現在,讓我們新增一個在用戶首次登入聊天應用程式時運行的函數,並且我們將添加一條聊天訊息來歡迎用戶。

8. 歡迎新用戶

聊天訊息結構

發佈到FriendlyChat聊天來源的訊息儲存在Cloud Firestore中。讓我們看一下用於訊息的資料結構。為此,請在聊天中發布一條新訊息,內容為「Hello World」:

11f5a676fbb1a69a.png

這應該顯示為:

fe6d1c020d0744cf.png

在 Firebase 控制台中,按一下「建置」部分下的「Firestore 資料庫」 。您應該會看到訊息集合和一份包含您所寫訊息的文件:

442c9c10b5e2b245.png

如您所見,聊天訊息會作為文件儲存在 Cloud Firestore 中,並將nameprofilePicUrltexttimestamp屬性新增至messages集合中。

新增歡迎訊息

第一個雲端功能增加了一個歡迎新用戶加入聊天的訊息。為此,我們可以使用觸發器functions.auth().onCreate ,它會在使用者每次在Firebase應用程式中首次登入時執行該函數。將addWelcomeMessages函數新增到您的index.js檔案:

索引.js

// Adds a message that welcomes new users into the chat.
exports.addWelcomeMessages = functions.auth.user().onCreate(async (user) => {
  functions.logger.log('A new user signed in for the first time.');
  const fullName = user.displayName || 'Anonymous';

  // Saves the new welcome message into the database
  // which then displays it in the FriendlyChat clients.
  await admin.firestore().collection('messages').add({
    name: 'Firebase Bot',
    profilePicUrl: '/images/firebase-logo.png', // Firebase logo
    text: `${fullName} signed in for the first time! Welcome!`,
    timestamp: admin.firestore.FieldValue.serverTimestamp(),
  });
  functions.logger.log('Welcome message written to database.');
});

將此函數新增至特殊exports物件是 Node 使函數可在目前檔案外部存取的方法,並且是 Cloud Functions 所必需的。

在上面的函數中,我們將「Firebase Bot」發布的新歡迎訊息新增到聊天訊息清單中。我們透過在 Cloud Firestore 中的messages集合上使用add方法來實現此目的,這是儲存聊天訊息的位置。

由於這是一個非同步操作,我們需要返回Promise來指示 Cloud Firestore 何時完成寫入,以便 Cloud Functions 不會過早執行。

部署雲端函數

Cloud Functions 僅在部署後才會啟動。為此,請在命令列上執行以下命令:

firebase deploy --only functions

這是您應該看到的控制台輸出:

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
⚠  functions: missing necessary APIs. Enabling now...
i  env: ensuring necessary APIs are enabled...
⚠  env: missing necessary APIs. Enabling now...
i  functions: waiting for APIs to activate...
i  env: waiting for APIs to activate...
✔  env: all necessary APIs are enabled
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (X.XX KB) for uploading
✔  functions: functions folder uploaded successfully
i  starting release process (may take several minutes)...
i  functions: creating function addWelcomeMessages...
✔  functions[addWelcomeMessages]: Successful create operation. 
✔  functions: all functions deployed successfully!

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlypchat-1234/overview

測試功能

成功部署該功能後,您將需要有一個首次登入的使用者。

  1. 使用託管 URL(格式為https://<project-id>.firebaseapp.com )在瀏覽器中開啟您的應用程式。
  2. 對於新用戶,請使用「登入」按鈕首次登入您的應用程式。
  • 如果您已經登入應用程序,則可以開啟Firebase 控制台驗證並從使用者清單中刪除您的帳戶。然後,再次登入。

262535d1b1223c65.png

  1. 登入後,應自動顯示歡迎訊息:

1c70e0d64b23525b.png

9. 圖片審核

用戶可以在聊天中上傳所有類型的圖像,因此控制攻擊性圖像始終很重要,尤其是在公共社交平台上。在FriendlyChat中,發佈到聊天的圖像儲存在Google Cloud Storage中。

使用 Cloud Functions,您可以使用functions.storage().onFinalize觸發器來偵測新的映像上傳。每次在 Cloud Storage 中上傳或修改新檔案時,都會執行該命令。

為了審核影像,我們將執行以下程序:

  1. 使用Cloud Vision API檢查圖像是否被標記為成人或暴力。
  2. 如果映像已被標記,請將其下載到正在執行的 Functions 實例上。
  3. 使用ImageMagick模糊影像。
  4. 將模糊影像上傳到雲端儲存。

啟用雲端視覺API

由於我們將在此函數中使用 Google Cloud Vision API,因此您必須在 firebase 專案上啟用該 API。點擊此鏈接,然後選擇您的 Firebase 專案並啟用 API:

5c77fee51ec5de49.png

安裝依賴項

為了審核圖像,我們將使用適用於 Node.js 的 Google Cloud Vision 用戶端庫@google-cloud/vision ,透過 Cloud Vision API 運行圖像以偵測不當圖像。

若要將此套件安裝到您的 Cloud Functions 應用程式中,請執行下列npm install --save命令。確保從functions目錄執行此操作。

npm install --save @google-cloud/vision@2.4.0

這將在本機安裝套件並將它們新增為package.json檔案中聲明的依賴項。

導入並配置依賴項

若要匯入已安裝的依賴項以及我們在本節中需要的一些 Node.js 核心模組( pathosfs ),請將以下行新增至index.js檔案的頂部:

索引.js

const Vision = require('@google-cloud/vision');
const vision = new Vision.ImageAnnotatorClient();
const {promisify} = require('util');
const exec = promisify(require('child_process').exec);

const path = require('path');
const os = require('os');
const fs = require('fs');

由於您的函數將在 Google Cloud 環境中運行,因此無需配置 Cloud Storage 和 Cloud Vision 庫:它們將自動配置為使用您的專案。

檢測不適當的影像

您將使用functions.storage.onChange Cloud Functions 觸發器,該觸發器會在 Cloud Storage 儲存桶中建立或修改檔案或資料夾後立即執行您的程式碼。將blurOffensiveImages函數添加到您的index.js檔案中:

索引.js

// Checks if uploaded images are flagged as Adult or Violence and if so blurs them.
exports.blurOffensiveImages = functions.runWith({memory: '2GB'}).storage.object().onFinalize(
    async (object) => {
      const imageUri = `gs://${object.bucket}/${object.name}`;
      // Check the image content using the Cloud Vision API.
      const batchAnnotateImagesResponse = await vision.safeSearchDetection(imageUri);
      const safeSearchResult = batchAnnotateImagesResponse[0].safeSearchAnnotation;
      const Likelihood = Vision.protos.google.cloud.vision.v1.Likelihood;
      if (Likelihood[safeSearchResult.adult] >= Likelihood.LIKELY ||
          Likelihood[safeSearchResult.violence] >= Likelihood.LIKELY) {
        functions.logger.log('The image', object.name, 'has been detected as inappropriate.');
        return blurImage(object.name);
      }
      functions.logger.log('The image', object.name, 'has been detected as OK.');
    });

請注意,我們新增了將運行該函數的 Cloud Functions 實例的一些配置。使用.runWith({memory: '2GB'}) ,我們請求實例獲得 2GB 內存而不是默認內存,因為該函數是內存密集型的。

當該功能被觸發時,圖像將透過 Cloud Vision API 運行,以偵測其是否被標記為成人或暴力。如果根據這些標準偵測到影像不合適,我們就會對影像進行模糊處理,這在我們接下來將看到的blurImage函數中完成。

影像模糊

在您的index.js檔案中加入以下blurImage函數:

索引.js

// Blurs the given image located in the given bucket using ImageMagick.
async function blurImage(filePath) {
  const tempLocalFile = path.join(os.tmpdir(), path.basename(filePath));
  const messageId = filePath.split(path.sep)[1];
  const bucket = admin.storage().bucket();

  // Download file from bucket.
  await bucket.file(filePath).download({destination: tempLocalFile});
  functions.logger.log('Image has been downloaded to', tempLocalFile);
  // Blur the image using ImageMagick.
  await exec(`convert "${tempLocalFile}" -channel RGBA -blur 0x24 "${tempLocalFile}"`);
  functions.logger.log('Image has been blurred');
  // Uploading the Blurred image back into the bucket.
  await bucket.upload(tempLocalFile, {destination: filePath});
  functions.logger.log('Blurred image has been uploaded to', filePath);
  // Deleting the local file to free up disk space.
  fs.unlinkSync(tempLocalFile);
  functions.logger.log('Deleted local file.');
  // Indicate that the message has been moderated.
  await admin.firestore().collection('messages').doc(messageId).update({moderated: true});
  functions.logger.log('Marked the image as moderated in the database.');
}

在上述函數中,映像二進位檔案是從 Cloud Storage 下載的。然後使用 ImageMagick 的convert工具對影像進行模糊處理,並將模糊版本重新上傳到儲存桶上。接下來,我們刪除 Cloud Functions 實例上的文件以釋放一些磁碟空間,這樣做是因為同一個 Cloud Functions 實例可以重複使用,如果不清理文件,可能會耗盡磁碟空間。最後,我們在聊天訊息中新增一個布林值,指示影像已審核,這將觸發客戶端上訊息的刷新。

部署功能

此功能僅在部署後才會啟動。在命令列上,執行firebase deploy --only functions

firebase deploy --only functions

這是您應該看到的控制台輸出:

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (X.XX KB) for uploading
✔  functions: functions folder uploaded successfully
i  starting release process (may take several minutes)...
i  functions: updating function addWelcomeMessages...
i  functions: creating function blurOffensiveImages...
✔  functions[addWelcomeMessages]: Successful update operation.
✔  functions[blurOffensiveImages]: Successful create operation.
✔  functions: all functions deployed successfully!

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlychat-1234/overview

測試功能

函數部署成功後:

  1. 使用託管 URL(格式為https://<project-id>.firebaseapp.com )在瀏覽器中開啟您的應用程式。
  2. 登入應用程式後,上傳圖像: 4db9fdab56703e4a.png
  3. 選擇要上傳的最佳攻擊性圖像(或者您可以使用這個食肉殭屍!),過了一會兒,您應該會看到您的帖子刷新,並顯示圖像的模糊版本: 83dd904fbaf97d2b.png

10. 新消息通知

在此部分中,您將新增一個雲端函數,用於在發布新訊息時向聊天參與者發送通知。

使用Firebase Cloud Messaging (FCM),您可以跨平台可靠地向使用者發送通知。要向使用者發送通知,您需要他們的 FCM 設備令牌。當使用者在新的瀏覽器或裝置上首次開啟該應用程式時,我們正在使用的聊天 Web 應用程式已經收集了使用者的裝置代幣。這些令牌儲存在 Cloud Firestore 中的fcmTokens集合中。

如果您想了解如何在 Web 應用程式上取得 FCM 裝置令牌,可以存取Firebase Web Codelab

發送通知

若要偵測何時發布新訊息,您將使用functions.firestore.document().onCreate Cloud Functions 觸發器,該觸發器會在 Cloud Firestore 的給定路徑建立新物件時執行您的程式碼。將sendNotifications函數加入到您的index.js檔案中:

索引.js

// Sends a notifications to all users when a new message is posted.
exports.sendNotifications = functions.firestore.document('messages/{messageId}').onCreate(
  async (snapshot) => {
    // Notification details.
    const text = snapshot.data().text;
    const payload = {
      notification: {
        title: `${snapshot.data().name} posted ${text ? 'a message' : 'an image'}`,
        body: text ? (text.length <= 100 ? text : text.substring(0, 97) + '...') : '',
        icon: snapshot.data().profilePicUrl || '/images/profile_placeholder.png',
        click_action: `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com`,
      }
    };

    // Get the list of device tokens.
    const allTokens = await admin.firestore().collection('fcmTokens').get();
    const tokens = [];
    allTokens.forEach((tokenDoc) => {
      tokens.push(tokenDoc.id);
    });

    if (tokens.length > 0) {
      // Send notifications to all tokens.
      const response = await admin.messaging().sendToDevice(tokens, payload);
      await cleanupTokens(response, tokens);
      functions.logger.log('Notifications have been sent and tokens cleaned up.');
    }
  });

在上面的函數中,我們從 Cloud Firestore 資料庫收集所有使用者的裝置令牌,並使用admin.messaging().sendToDevice函數向每個使用者發送通知。

清理令牌

最後,我們要刪除不再有效的令牌。當我們曾經從使用者那裡獲得的令牌不再被瀏覽器或裝置使用時,就會發生這種情況。例如,如果使用者撤銷了瀏覽器會話的通知權限,就會發生這種情況。為此,請在index.js檔案中加入以下cleanupTokens函數:

索引.js

// Cleans up the tokens that are no longer valid.
function cleanupTokens(response, tokens) {
 // For each notification we check if there was an error.
 const tokensDelete = [];
 response.results.forEach((result, index) => {
   const error = result.error;
   if (error) {
     functions.logger.error('Failure sending notification to', tokens[index], error);
     // Cleanup the tokens that are not registered anymore.
     if (error.code === 'messaging/invalid-registration-token' ||
         error.code === 'messaging/registration-token-not-registered') {
       const deleteTask = admin.firestore().collection('fcmTokens').doc(tokens[index]).delete();
       tokensDelete.push(deleteTask);
     }
   }
 });
 return Promise.all(tokensDelete);
}

部署功能

此函數僅在部署後才會激活,要部署它,請在命令列中執行以下命令:

firebase deploy --only functions

這是您應該看到的控制台輸出:

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (X.XX KB) for uploading
✔  functions: functions folder uploaded successfully
i  starting release process (may take several minutes)...
i  functions: updating function addWelcomeMessages...
i  functions: updating function blurOffensiveImages...
i  functions: creating function sendNotifications...
✔  functions[addWelcomeMessages]: Successful update operation.
✔  functions[blurOffensiveImages]: Successful updating operation.
✔  functions[sendNotifications]: Successful create operation.
✔  functions: all functions deployed successfully!

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlychat-1234/overview

測試功能

  1. 函數部署成功後,使用託管 URL(格式為https://<project-id>.firebaseapp.com )在瀏覽器中開啟您的應用程式。
  2. 如果您是第一次登入應用程序,請確保在出現提示時允許通知: 8b9d0c66dc36153d.png
  3. 關閉聊天應用程式標籤或顯示其他標籤:僅當應用程式位於背景時才會顯示通知。如果您想了解如何在應用程式位於前台時接收訊息,請查看我們的文件
  4. 使用不同的瀏覽器(或隱身視窗)登入應用程式並發布訊息。您應該會看到第一個瀏覽器顯示的通知: 45282ab12b28b926.png

11. 恭喜!

您已使用適用於 Cloud Functions 的 Firebase SDK 並向聊天應用程式新增了伺服器端元件。

我們涵蓋的內容

  • 使用 Firebase SDK for Cloud Functions 編寫 Cloud Functions。
  • 根據身份驗證、Cloud Storage 和 Cloud Firestore 事件觸發 Cloud Functions。
  • 為您的 Web 應用程式新增 Firebase 雲端訊息支援。
  • 使用 Firebase CLI 部署 Cloud Functions。

下一步

了解更多