إنشاء تطبيق Google Chat خلف جدار ناري باستخدام Pub/Sub

توضّح هذه الصفحة كيفية إنشاء تطبيق Chat باستخدام ميزة Pub/Sub. ويكون هذا النوع من البنية الأساسية لتطبيق Chat مفيدًا إذا كان لدى مؤسستك جدار حماية، والذي قد يمنع Chat من إرسال رسائل إلى تطبيق Chat، أو إذا كان تطبيق Chat يستخدم Google Workspace Events API. ومع ذلك، يتم فرض القيود التالية على هذه البنية لأنّ تطبيقات Chat هذه يمكنها إرسال رسائل غير متزامنة واستلامها فقط:

  • لا يمكن استخدام مربعات الحوار في الرسائل. بدلاً من ذلك، استخدِم رسالة بطاقة.
  • لا يمكن تعديل بطاقات فردية من خلال استجابة متزامنة. بدلاً من ذلك، يمكنك تعديل الرسالة بأكملها من خلال استدعاء الطريقة patch.

يوضح المخطّط التالي بنية تطبيق Chat الذي تم إنشاؤه باستخدام Pub/Sub:

بنية تطبيق Chat تم تنفيذها من خلال ميزة النشر/الاشتراك

في الرسم البياني السابق، يتضمّن المستخدم الذي يتفاعل مع تطبيق Pub/Sub Chat تدفق المعلومات التالي:

  1. يرسل المستخدم رسالة في Chat إلى تطبيق Chat، إما في رسالة مباشرة أو في مساحة Chat، أو في حدث في مساحة Chat يحتوي تطبيق Chat على اشتراك نشط لها.

  2. يرسل Chat الرسالة إلى موضوع النشر/الاشتراك.

  3. يشترك خادم التطبيقات، الذي يكون إما سحابة إلكترونية أو نظامًا داخل المؤسسة يحتوي على منطق تطبيق Chat، في موضوع النشر/الاشتراك من أجل تلقّي الرسالة عبر جدار الحماية.

  4. اختياريًا، يمكن لتطبيق Chat استدعاء Chat API لنشر الرسائل بشكل غير متزامن أو تنفيذ عمليات أخرى.

المتطلبات الأساسية

Java

إعداد البيئة

قبل استخدام Google APIs، عليك تفعيلها في مشروع على Google Cloud. يمكنك تفعيل واجهة برمجة تطبيقات واحدة أو أكثر في مشروع واحد على Google Cloud.

إعداد ميزة النشر/الاشتراك

  1. أنشِئ موضوع نشر/اشتراك يمكن لواجهة Chat API إرسال الرسائل إليه. وننصحك باستخدام موضوع واحد لكل تطبيق في Chat

  2. امنح الإذن في Chat للنشر في الموضوع من خلال منح دور ناشر النشر/الاشتراك لحساب الخدمة التالي:

    chat-api-push@system.gserviceaccount.com
    
  3. أنشِئ حساب خدمة لتطبيق Chat للتفويض من خلال Pub/Sub وChat واحفظ ملف المفتاح الخاص في دليل العمل.

  4. أنشئ اشتراكًا في سحب للموضوع.

  5. عيّن دور مشترك النشر/الاشتراك في الاشتراك لحساب الخدمة الذي أنشأته في السابق.

كتابة النص

Java

  1. في واجهة سطر الأوامر، يجب تقديم بيانات اعتماد حساب الخدمة:

    export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATH
    
  2. في دليل العمل، أنشِئ ملفًا باسم pom.xml.

  3. في ملف pom.xml، الصق الرمز التالي:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.google.chat.pubsub</groupId>
    <artifactId>java-pubsub-app</artifactId>
    <version>0.1.0</version>
    
    <name>java-pubsub-app</name>
    
    <properties>
      <maven.compiler.target>11</maven.compiler.target>
      <maven.compiler.source>11</maven.compiler.source>
    </properties>
    
    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>com.google.cloud</groupId>
          <artifactId>libraries-bom</artifactId>
          <version>26.26.0</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
      </dependencies>
    </dependencyManagement>
    
    <dependencies>
      <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.9.1</version>
      </dependency>
      <dependency>
        <groupId>com.google.api-client</groupId>
        <artifactId>google-api-client</artifactId>
        <version>1.32.1</version>
      </dependency>
      <dependency>
        <groupId>com.google.cloud</groupId>
        <artifactId>google-cloud-pubsub</artifactId>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.14.2</version>
      </dependency>
    </dependencies>
    
    <build>
      <pluginManagement>
        <plugins>
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
          </plugin>
        </plugins>
      </pluginManagement>
    </build>
    </project>
    
  4. في دليل العمل، أنشِئ بنية الدليل src/main/java.

  5. في دليل src/main/java، أنشِئ ملفًا باسم Main.java.

  6. في Main.java، الصق الرمز التالي:

    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.node.JsonNodeFactory;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
    import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
    import com.google.api.client.http.ByteArrayContent;
    import com.google.api.client.http.GenericUrl;
    import com.google.api.client.http.HttpContent;
    import com.google.api.client.http.HttpRequest;
    import com.google.api.client.http.HttpRequestFactory;
    import com.google.api.client.http.HttpTransport;
    import com.google.cloud.pubsub.v1.AckReplyConsumer;
    import com.google.cloud.pubsub.v1.MessageReceiver;
    import com.google.cloud.pubsub.v1.Subscriber;
    import com.google.pubsub.v1.PubsubMessage;
    import com.google.pubsub.v1.ProjectSubscriptionName;
    import java.io.FileInputStream;
    import java.util.Collections;
    
    public class Main {
    
      public static final String CREDENTIALS_PATH_ENV_PROPERTY = "GOOGLE_APPLICATION_CREDENTIALS";
    
      // Google Cloud Project ID
      public static final String PROJECT_ID = PROJECT_ID;
    
      // Cloud Pub/Sub Subscription ID
      public static final String SUBSCRIPTION_ID = SUBSCRIPTION_ID
    
      public static void main(String[] args) throws Exception {
        ProjectSubscriptionName subscriptionName =
            ProjectSubscriptionName.of(PROJECT_ID, SUBSCRIPTION_ID);
    
        // Instantiate app, which implements an asynchronous message receiver.
        EchoApp echoApp = new EchoApp();
    
        // Create a subscriber for <var>SUBSCRIPTION_ID</var> bound to the message receiver
        final Subscriber subscriber =
            Subscriber.newBuilder(subscriptionName, echoApp).build();
        System.out.println("Starting subscriber...");
        subscriber.startAsync();
    
        // Wait for termination
        subscriber.awaitTerminated();
      }
    }
    
    / **
    * A demo app which implements {@link MessageReceiver} to receive messages. It simply echoes the
    * incoming messages.
    */
    class EchoApp implements MessageReceiver {
    
      // Path to the private key JSON file of the service account to be used for posting response
      // messages to Google Chat.
      // In this demo, we are using the same service account for authorizing with Cloud Pub/Sub to
      // receive messages and authorizing with Google Chat to post messages. If you are using
      // different service accounts, please set the path to the private key JSON file of the service
      // account used to post messages to Google Chat here.
      private static final String SERVICE_ACCOUNT_KEY_PATH =
          System.getenv(Main.CREDENTIALS_PATH_ENV_PROPERTY);
    
      // Developer code for Google Chat API scope.
      private static final String GOOGLE_CHAT_API_SCOPE = "https://www.googleapis.com/auth/chat.bot";
    
      // Response URL Template with placeholders for space id.
      private static final String RESPONSE_URL_TEMPLATE =
          "https://chat.googleapis.com/v1/__SPACE_ID__/messages";
    
      // Response echo message template.
      private static final String RESPONSE_TEMPLATE = "You said: `__MESSAGE__`";
    
      private static final String ADDED_RESPONSE = "Thank you for adding me!";
    
      GoogleCredential credential;
      HttpTransport httpTransport;
      HttpRequestFactory requestFactory;
    
      EchoApp() throws Exception {
        credential =
            GoogleCredential.fromStream(new FileInputStream(SERVICE_ACCOUNT_KEY_PATH))
                .createScoped(Collections.singleton(GOOGLE_CHAT_API_SCOPE));
        httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        requestFactory = httpTransport.createRequestFactory(credential);
      }
    
      // Called when a message is received by the subscriber.
      @Override
      public void receiveMessage(PubsubMessage pubsubMessage, AckReplyConsumer consumer) {
        System.out.println("Id : " + pubsubMessage.getMessageId());
        // handle incoming message, then ack/nack the received message
        try {
          ObjectMapper mapper = new ObjectMapper();
          JsonNode dataJson = mapper.readTree(pubsubMessage.getData().toStringUtf8());
          System.out.println("Data : " + dataJson.toString());
          handle(dataJson);
          consumer.ack();
        } catch (Exception e) {
          System.out.println(e);
          consumer.nack();
        }
      }
    
      public void handle(JsonNode eventJson) throws Exception {
        JsonNodeFactory jsonNodeFactory = new JsonNodeFactory(false);
        ObjectNode responseNode = jsonNodeFactory.objectNode();
    
        // Construct the response depending on the event received.
    
        String eventType = eventJson.get("type").asText();
        switch (eventType) {
          case "ADDED_TO_SPACE":
            responseNode.put("text", ADDED_RESPONSE);
            // An app can also be added to a space by @mentioning it in a message. In that case, we fall
            // through to the MESSAGE case and let the app respond. If the app was added using the
            // invite flow, we just post a thank you message in the space.
            if(!eventJson.has("message")) {
              break;
            }
          case "MESSAGE":
            responseNode.put("text",
                RESPONSE_TEMPLATE.replaceFirst(
                    "__MESSAGE__", eventJson.get("message").get("text").asText()));
            // In case of message, post the response in the same thread.
            ObjectNode threadNode = jsonNodeFactory.objectNode();
            threadNode.put("name", eventJson.get("message").get("thread").get("name").asText());
            responseNode.put("thread", threadNode);
            break;
          case "REMOVED_FROM_SPACE":
          default:
            // Do nothing
            return;
        }
    
        // Post the response to Google Chat.
    
        String URI =
            RESPONSE_URL_TEMPLATE.replaceFirst(
                "__SPACE_ID__", eventJson.get("space").get("name").asText());
        GenericUrl url = new GenericUrl(URI);
    
        HttpContent content =
            new ByteArrayContent("application/json", responseNode.toString().getBytes("UTF-8"));
        HttpRequest request = requestFactory.buildPostRequest(url, content);
        com.google.api.client.http.HttpResponse response = request.execute();
      }
    }
    

    استبدل ما يلي:

    • PROJECT_ID: رقم تعريف مشروع Google Cloud
    • SUBSCRIPTION_ID: رقم تعريف الاشتراك الخاص باشتراك النشر/الاشتراك الذي أنشأته في السابق.

نشر التطبيق في Chat

  1. في Google Cloud Console، انتقِل إلى القائمة > واجهات برمجة التطبيقات والخدمات > واجهات برمجة التطبيقات والخدمات التي تم تفعيلها > واجهة برمجة تطبيقات Google Chat > الضبط.

    الانتقال إلى الإعدادات

  2. اضبط تطبيق Chat للنشر/الاشتراك:

    1. في اسم التطبيق، أدخِل Quickstart App.
    2. في عنوان URL للصورة الرمزية، أدخِل https://developers.google.com/chat/images/quickstart-app-avatar.png.
    3. في الوصف، أدخِل Quickstart app.
    4. ضمن الوظائف، اختَر تلقّي رسائل بين شخصَين والانضمام إلى المساحات والمحادثات الجماعية.
    5. ضِمن إعدادات الاتصال، اختَر Cloud Pub/Sub والصِق اسم موضوع النشر/الاشتراك الذي أنشأته في السابق.
    6. ضمن إذن الوصول، اختَر إتاحة تطبيق Google Chat هذا لمستخدمين محدَّدين ومجموعات محدَّدة في نطاقك وأدخِل عنوان بريدك الإلكتروني.
    7. ضمن السجلات، اختَر تسجيل الأخطاء إلى التسجيل.
  3. انقر على حفظ.

أصبح التطبيق جاهزًا لتلقّي الرسائل والردّ عليها من خلال Chat.

تشغيل النص البرمجي

في واجهة سطر الأوامر، بدّل إلى دليل العمل وشغِّل النص البرمجي:

Java

mvn compile exec:java -Dexec.mainClass=Main

عند تشغيل الرمز، يبدأ التطبيق في الاستماع إلى الرسائل المنشورة في موضوع النشر/الاشتراك.

اختبار تطبيق Chat

لاختبار تطبيق Chat، أرسِل رسالة مباشرة إلى التطبيق باتّباع الخطوات التالية:

  1. افتح Google Chat.
  2. لإرسال رسالة مباشرة إلى التطبيق، انقر على بدء محادثة ، وفي النافذة التي تظهر، انقر على البحث عن تطبيقات.
  3. في مربّع الحوار البحث عن التطبيقات، ابحث عن "تطبيق Quickstart".
  4. لفتح رسالة مباشرة باستخدام التطبيق، ابحث عن تطبيق Quickstart وانقر على إضافة > Chat.
  5. في الرسالة المباشرة، اكتب Hello واضغط على enter. ويردّ تطبيق Chat الرسالة إليك.

لإضافة مختبِرين موثوق بهم ومعرفة مزيد من المعلومات عن اختبار الميزات التفاعلية، يمكنك الاطّلاع على مقالة اختبار الميزات التفاعلية لتطبيقات Google Chat.

تحديد المشاكل وحلّها

عندما يعرض أحد تطبيقات Google Chat أو بطاقة رسالة خطأ، تعرض واجهة Chat رسالة مفادها "حدث خطأ" أو "تعذّرت معالجة طلبك". في بعض الأحيان، لا تعرض واجهة مستخدم Chat أي رسالة خطأ، ولكن يعرض تطبيق Chat أو البطاقة نتيجة غير متوقعة. على سبيل المثال، قد لا تظهر رسالة بطاقة.

على الرغم من أنّه قد لا تظهر رسالة الخطأ في واجهة مستخدم Chat، تتوفّر رسائل الخطأ الوصفية وبيانات السجلّ لمساعدتك في إصلاح الأخطاء عند تفعيل تسجيل الأخطاء في تطبيقات Chat. للحصول على مساعدة في عرض الأخطاء وتصحيحها وتصحيحها، يُرجى الاطّلاع على مقالة تحديد وحلّ مشاكل Google Chat.

تَنظيم

لتجنّب دفع رسوم من حسابك على Google Cloud مقابل الموارد المستخدمة في هذا البرنامج التعليمي، ننصحك بحذف المشروع على Google Cloud.

  1. في Google Cloud Console، انتقِل إلى صفحة إدارة الموارد. انقر على القائمة > إدارة الهوية وإمكانية الوصول والمشرف > إدارة الموارد.

    الانتقال إلى "مدير الموارد"

  2. في قائمة المشاريع، اختَر المشروع الذي تريد حذفه، ثم انقر على حذف .
  3. في مربّع الحوار، اكتب معرّف المشروع، ثم انقر على إيقاف التشغيل لحذف المشروع.