Nesta página, explicamos como criar um app de chat que recebe eventos do Chat usando o Cloud Pub/Sub. Essa arquitetura é útil se o app de chat estiver atrás de um firewall ou se você quiser enviar ou receber eventos sobre um espaço do Chat ou usuário com a API Google Workspace Events.
O diagrama a seguir mostra a arquitetura de um app de chat criado com o Pub/Sub:
No diagrama anterior, um usuário interagindo com um app de chat do Pub/Sub tem o seguinte fluxo de informações:
Um usuário interage com o app de chat, por exemplo, enviando uma mensagem, emitindo um comando ou adicionando ou removendo o app de um espaço do Chat.
O Chat envia a mensagem para um tópico do Pub/Sub.
Um servidor de aplicativos, que é um sistema na nuvem ou local que contém a lógica do app de chat, assina o tópico do Pub/Sub para receber a mensagem pelo firewall.
Opcionalmente, o app de chat pode chamar a API Chat para postar mensagens de forma assíncrona ou realizar outras operações.
Pré-requisitos
Node.js
- Uma conta do Google Workspace Business ou Enterprise com acesso ao Google Chat.
- Ter um projeto do Google Cloud com o faturamento ativado. Para verificar se um projeto já tem o faturamento ativado, consulte Verificar o status de faturamento dos seus projetos. Para criar um projeto e configurar o faturamento, consulte Criar um projeto do Google Cloud.
- Node.js 14 ou mais recente
- A ferramenta de gerenciamento de pacotes npm
- Um projeto Node.js inicializado. Para inicializar um novo projeto, crie e
mude para uma nova pasta. Em seguida, execute o seguinte comando na interface de linha de comando:
npm init
Python
- Uma conta do Google Workspace Business ou Enterprise com acesso ao Google Chat.
- Ter um projeto do Google Cloud com o faturamento ativado. Para verificar se um projeto já tem o faturamento ativado, consulte Verificar o status de faturamento dos seus projetos. Para criar um projeto e configurar o faturamento, consulte Criar um projeto do Google Cloud.
- Python 3.6 ou mais recente
- A ferramenta de gerenciamento de pacotes pip
Java
- Uma conta do Google Workspace Business ou Enterprise com acesso ao Google Chat.
- Ter um projeto do Google Cloud com o faturamento ativado. Para verificar se um projeto já tem o faturamento ativado, consulte Verificar o status de faturamento dos seus projetos. Para criar um projeto e configurar o faturamento, consulte Criar um projeto do Google Cloud.
- Java 11 ou mais recente
- A ferramenta de gerenciamento de pacotes Maven
Ative as APIs
Antes de usar as APIs do Google, é necessário ativá-las em um projeto do Google Cloud. É possível ativar uma ou mais APIs em um único projeto do Google Cloud.No console do Google Cloud, ative a API Google Chat e a API Pub/Sub.
Configurar o Pub/Sub
Crie um tópico do Pub/Sub para que a API Chat possa enviar mensagens. Recomendamos que você use um único tema por app de chat.
Crie uma conta de serviço para o app de chat autorizar com o Pub/Sub e o Chat e salve o arquivo de chave privada no seu diretório de trabalho.
Crie uma assinatura por pull para o tópico.
Atribua o papel de assinante do Pub/Sub à assinatura da conta de serviço criada anteriormente.
Escrever o script
Nesta seção, você vai definir a lógica do aplicativo para seu app de chat. Você vai escrever um script que faz a autenticação com o Google Cloud e se inscreve no tópico do Pub/Sub para receber eventos do Chat, como quando um usuário envia uma mensagem para seu app de chat.
Quando o script recebe uma mensagem, ele processa os dados de eventos e usa a API do Google Chat para postar uma resposta ao usuário ou espaço. Essa configuração permite que seu app do Chat funcione por trás de um firewall e ainda interaja com os usuários do Chat.
Node.js
Em uma CLI, forneça as credenciais da conta de serviço:
export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATHEm uma CLI, informe o ID do projeto do Google Cloud:
export PROJECT_ID=PROJECT_IDEm uma CLI, forneça o ID da assinatura do Pub/Sub que você criou anteriormente:
export SUBSCRIPTION_ID=SUBSCRIPTION_IDNo diretório de trabalho, crie um arquivo chamado
package.json.No arquivo
package.json, cole o seguinte código:{ "name": "pub-sub-app", "version": "1.0.0", "description": "Google Chat App that listens for messages via Cloud Pub/Sub", "main": "index.js", "scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { "@google-apps/chat": "^0.4.0", "@google-cloud/pubsub": "^4.5.0" }, "license": "Apache-2.0" }No diretório de trabalho, crie um arquivo chamado
index.js.Em
index.js, cole o seguinte código:const {ChatServiceClient} = require('@google-apps/chat'); const {MessageReplyOption} = require('@google-apps/chat').protos.google.chat.v1.CreateMessageRequest; const {PubSub} = require('@google-cloud/pubsub'); const {SubscriberClient} = require('@google-cloud/pubsub/build/src/v1'); // Receives messages from a pull subscription. function receiveMessages() { const chat = new ChatServiceClient({ keyFile: process.env.GOOGLE_APPLICATION_CREDENTIALS, scopes: ['https://www.googleapis.com/auth/chat.bot'], }); const subscriptionPath = new SubscriberClient() .subscriptionPath(process.env.PROJECT_ID, process.env.SUBSCRIPTION_ID) const subscription = new PubSub() .subscription(subscriptionPath); // Handle incoming message, then acknowledge the received message const messageHandler = message => { console.log(`Id : ${message.id}`); const event = JSON.parse(message.data); console.log(`Data : ${JSON.stringify(event)}`); // Post the response to Google Chat. const request = formatRequest(event); if (request != null) { chat.createMessage(request); } // Acknowledge the message. message.ack(); } subscription.on('message', messageHandler); console.log(`Listening for messages on ${subscriptionPath}`); // Keep main thread from exiting while waiting for messages setTimeout(() => { subscription.removeListener('message', messageHandler); console.log(`Stopped listening for messages.`); }, 60 * 1000); } // Send message to Google Chat based on the type of event function formatRequest(event) { const chatEvent = event.chat || {}; // If the app was removed, we don't respond. if (chatEvent.removedFromSpacePayload) { console.log(`App removed from space.`); return null; } const payload = chatEvent.messagePayload || chatEvent.addedToSpacePayload; const spaceName = payload?.space?.name; if (!spaceName) { console.log('No space name in event.'); return null; } if (chatEvent.addedToSpacePayload) { // 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. return { parent: spaceName, message: { text: 'Thank you for adding me!' }, }; } else if (chatEvent.messagePayload) { // In case of message, post the response in the same thread. const message = chatEvent.messagePayload.message; return { parent: spaceName, messageReplyOption: MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD, message: { text: 'You said: `' + message.text + '`', thread: { name: message.thread.name }, }, }; } } if (!process.env.PROJECT_ID) { console.log('Missing PROJECT_ID env var.'); process.exit(1); } if (!process.env.SUBSCRIPTION_ID) { console.log('Missing SUBSCRIPTION_ID env var.'); process.exit(1); } if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) { console.log('Missing GOOGLE_APPLICATION_CREDENTIALS env var.'); process.exit(1); } receiveMessages();
Python
Em uma CLI, forneça as credenciais da conta de serviço:
export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATHEm uma CLI, informe o ID do projeto do Google Cloud:
export PROJECT_ID=PROJECT_IDEm uma CLI, forneça o ID da assinatura do Pub/Sub que você criou anteriormente:
export SUBSCRIPTION_ID=SUBSCRIPTION_IDNo diretório de trabalho, crie um arquivo chamado
requirements.txt.No arquivo
requirements.txt, cole o seguinte código:google-cloud-pubsub>=2.23.0 google-apps-chat==0.1.9No diretório de trabalho, crie um arquivo chamado
app.py.Em
app.py, cole o seguinte código:import json import logging import os import sys import time from google.apps import chat_v1 as google_chat from google.cloud import pubsub_v1 from google.oauth2.service_account import Credentials def receive_messages(): """Receives messages from a pull subscription.""" scopes = ['https://www.googleapis.com/auth/chat.bot'] service_account_key_path = os.environ.get('GOOGLE_APPLICATION_CREDENTIALS') creds = Credentials.from_service_account_file(service_account_key_path) chat = google_chat.ChatServiceClient( credentials=creds, client_options={'scopes': scopes} ) project_id = os.environ.get('PROJECT_ID') subscription_id = os.environ.get('SUBSCRIPTION_ID') subscriber = pubsub_v1.SubscriberClient() subscription_path = subscriber.subscription_path(project_id, subscription_id) # Handle incoming message, then acknowledge the received message def callback(message): event = json.loads(message.data) logging.info('Data : %s', event) # Post the response to Google Chat. request = format_request(event) if request is not None: chat.create_message(request) # Acknowledge the message. message.ack() subscriber.subscribe(subscription_path, callback = callback) logging.info('Listening for messages on %s', subscription_path) # Keep main thread from exiting while waiting for messages while True: time.sleep(60) def format_request(event): """Send message to Google Chat based on the type of event. Args: event: A dictionary with the event data. """ chat_event = event.get('chat', {}) # If the app was removed, we don't respond. if 'removedFromSpacePayload' in chat_event: logging.info('App removed from space.') return payload = chat_event.get('messagePayload') or chat_event.get( 'addedToSpacePayload' ) space_name = payload.get('space', {}).get('name') if payload else None if not space_name: logging.warning('No space name in event.') return if 'addedToSpacePayload' in chat_event: # 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. return google_chat.CreateMessageRequest( parent = space_name, message = { 'text': 'Thank you for adding me!' } ) elif 'messagePayload' in chat_event: # In case of message, post the response in the same thread. message = chat_event['messagePayload']['message'] return google_chat.CreateMessageRequest( parent = space_name, message_reply_option = google_chat.CreateMessageRequest.MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD, message = { 'text': 'You said: `' + message['text'] + '`', 'thread': { 'name': message['thread']['name'] } } ) if __name__ == '__main__': if 'PROJECT_ID' not in os.environ: logging.error('Missing PROJECT_ID env var.') sys.exit(1) if 'SUBSCRIPTION_ID' not in os.environ: logging.error('Missing SUBSCRIPTION_ID env var.') sys.exit(1) if 'GOOGLE_APPLICATION_CREDENTIALS' not in os.environ: logging.error('Missing GOOGLE_APPLICATION_CREDENTIALS env var.') sys.exit(1) logging.basicConfig( level=logging.INFO, style='{', format='{levelname:.1}{asctime} {filename}:{lineno}] {message}') receive_messages()
Java
Em uma CLI, forneça as credenciais da conta de serviço:
export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATHEm uma CLI, informe o ID do projeto do Google Cloud:
export PROJECT_ID=PROJECT_IDEm uma CLI, forneça o ID da assinatura do Pub/Sub que você criou anteriormente:
export SUBSCRIPTION_ID=SUBSCRIPTION_IDNo diretório de trabalho, crie um arquivo chamado
pom.xml.No arquivo
pom.xml, cole o seguinte código:<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.addon</groupId> <artifactId>pubsub-addon-chat-app</artifactId> <version>0.1.0</version> <name>pubsub-addon-chat-app-java</name> <properties> <maven.compiler.release>11</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>com.google.cloud</groupId> <artifactId>libraries-bom</artifactId> <version>26.41.0</version> <!-- Use a recent BOM version --> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- Google Chat GAPIC library --> <dependency> <groupId>com.google.cloud</groupId> <artifactId>google-cloud-chat</artifactId> </dependency> <!-- Google Cloud Pub/Sub library --> <dependency> <groupId>com.google.cloud</groupId> <artifactId>google-cloud-pubsub</artifactId> </dependency> <!-- Google Apps Add-ons Event Object --> <dependency> <groupId>com.google.apps.addons.v1</groupId> <artifactId>google-apps-addons-v1-java</artifactId> <version>0.2.0</version> <!-- Check for latest version --> </dependency> <!-- Protobuf JSON utility --> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java-util</artifactId> </dependency> <!-- Google Auth Library --> <dependency> <groupId>com.google.auth</groupId> <artifactId>google-auth-library-oauth2-http</artifactId> </dependency> <dependency> <groupId>com.google.api</groupId> <artifactId>gax</artifactId> </dependency> <!-- JSON utilities for PubSub message (if needed, though protobuf-java-util is primary for EventObject) --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.2</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>1.7.36</version> <scope>runtime</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.13.0</version> <configuration> <source>11</source> <target>11</target> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.3.0</version> <configuration> <mainClass>Main</mainClass> </configuration> </plugin> </plugins> </build> </project>No diretório de trabalho, crie a estrutura de diretórios
src/main/java.No diretório
src/main/java, crie um arquivo chamadoMain.java.Em
Main.java, cole o seguinte código:import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.api.gax.core.FixedCredentialsProvider; import com.google.auth.oauth2.GoogleCredentials; import com.google.chat.v1.ChatServiceClient; import com.google.chat.v1.ChatServiceSettings; import com.google.chat.v1.CreateMessageRequest; import com.google.chat.v1.CreateMessageRequest.MessageReplyOption; import com.google.chat.v1.Message; import com.google.chat.v1.Thread; 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.ProjectSubscriptionName; import com.google.pubsub.v1.PubsubMessage; import java.io.FileInputStream; import java.util.Collections; public class Main { public static final String PROJECT_ID_ENV_PROPERTY = "PROJECT_ID"; public static final String SUBSCRIPTION_ID_ENV_PROPERTY = "SUBSCRIPTION_ID"; public static final String CREDENTIALS_PATH_ENV_PROPERTY = "GOOGLE_APPLICATION_CREDENTIALS"; public static void main(String[] args) throws Exception { ProjectSubscriptionName subscriptionName = ProjectSubscriptionName.of( System.getenv(Main.PROJECT_ID_ENV_PROPERTY), System.getenv(Main.SUBSCRIPTION_ID_ENV_PROPERTY)); // 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("Subscriber is listening to events..."); subscriber.startAsync(); // Wait for termination subscriber.awaitTerminated(); } } /** * A demo app which implements {@link MessageReceiver} to receive messages. * It echoes 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, 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"; private static final String ADDED_RESPONSE = "Thank you for adding me!"; ChatServiceClient chatServiceClient; EchoApp() throws Exception { GoogleCredentials credential = GoogleCredentials.fromStream(new FileInputStream(SERVICE_ACCOUNT_KEY_PATH)) .createScoped(Collections.singleton(GOOGLE_CHAT_API_SCOPE)); // Create the ChatServiceSettings with the app credentials ChatServiceSettings chatServiceSettings = ChatServiceSettings.newBuilder() .setCredentialsProvider(FixedCredentialsProvider.create(credential)) .build(); // Set the Chat service client chatServiceClient = ChatServiceClient.create(chatServiceSettings); } // 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 acknowledge 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); // Negative acknowledgement makes Pub/Sub redeliver the message. consumer.nack(); } } // Send message to Google Chat based on the type of event. public void handle(JsonNode eventJson) throws Exception { // Google Chat events for add-ons are wrapped in a 'chat' object. if (!eventJson.has("chat")) { System.out.println("Ignored: Not a Chat event (missing 'chat' field)."); return; } JsonNode chatNode = eventJson.get("chat"); CreateMessageRequest createMessageRequest = null; if (chatNode.has("messagePayload")) { // HANDLE MESSAGE JsonNode messagePayload = chatNode.get("messagePayload"); JsonNode message = messagePayload.get("message"); JsonNode space = messagePayload.get("space"); String spaceName = space.get("name").asText(); String userText = message.has("text") ? message.get("text").asText() : ""; String threadName = message.has("thread") ? message.get("thread").get("name").asText() : ""; System.out.println("Received message in " + spaceName + ": " + userText); createMessageRequest = CreateMessageRequest.newBuilder() .setParent(spaceName) .setMessageReplyOption(MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD) .setMessage( Message.newBuilder() .setText("You said: `" + userText + "`") .setThread(Thread.newBuilder().setName(threadName).build()) .build()) .build(); } else if (chatNode.has("addedToSpacePayload")) { // HANDLE ADDED TO SPACE JsonNode addedPayload = chatNode.get("addedToSpacePayload"); JsonNode space = addedPayload.get("space"); String spaceName = space.get("name").asText(); System.out.println("Added to space: " + spaceName); createMessageRequest = CreateMessageRequest.newBuilder() .setParent(spaceName) .setMessage(Message.newBuilder().setText(ADDED_RESPONSE).build()) .build(); } else if (chatNode.has("removedFromSpacePayload")) { System.out.println("Removed from space."); return; } else { System.out.println("Ignored: Unhandled Chat event type."); return; } if (createMessageRequest != null) { // Post the response to Google Chat. chatServiceClient.createMessage(createMessageRequest); System.out.println("Sent reply."); } } }
Configurar o app de chat
Configure o app de chat no console do Google Cloud para fornecer detalhes como nome e avatar e configurar a conexão com o tópico do Pub/Sub.
Ao se conectar ao tópico do Pub/Sub, você permite que o Chat envie eventos para seu app. Seu script, que se inscreve no tópico, pode receber esses eventos e responder aos usuários.
No console do Google Cloud, acesse Menu > APIs e serviços > APIs e serviços ativados > API Google Chat > Configuração.
Configure o app de chat para o Pub/Sub:
- Em Nome do app, insira
Add-on Chat App. - No URL do avatar, digite
https://developers.google.com/workspace/add-ons/images/quickstart-app-avatar.png. - Em Descrição, insira
Quickstart app. - Em Funcionalidade, selecione Participar de espaços e conversas em grupo.
- Em Configurações de conexão, selecione Cloud Pub/Sub e cole o nome do tópico do Pub/Sub criado anteriormente.
- Em Visibilidade, selecione Disponibilizar este app de chat do Google para pessoas e grupos específicos no seu domínio e digite seu endereço de e-mail.
- Em Registros, selecione Registrar erros no Logging.
- Em Nome do app, insira
Clique em Salvar.
Agora que você configurou o app de chat, é necessário atualizar uma configuração do Pub/Sub.
- Na página de configuração da API Chat, em Configurações de conexão, copie o E-mail da conta de serviço, que é um e-mail exclusivo gerado para seu projeto do Google Cloud.
- Conceda permissão ao Chat para publicar no tópico atribuindo o papel de Publicador do Pub/Sub ao e-mail da conta de serviço que você copiou anteriormente.
O app está pronto para receber e responder a mensagens no Chat.
Executar o script
Em uma CLI, mude para o diretório de trabalho e execute o script:
Node.js
npm install
npm start
Python
python -m venv env
source env/bin/activate
pip install -r requirements.txt -U
python app.py
Java
mvn compile exec:java -Dexec.mainClass=Main
Quando você executa o código, o aplicativo começa a detectar mensagens publicadas no tópico do Pub/Sub.
Testar o app de chat
Para testar o app de chat, abra um espaço de mensagem direta com o app de chat e envie uma mensagem:
Abra o Google Chat usando a conta do Google Workspace que você informou ao se tornar um trusted tester.
- Clique em Novo chat.
- No campo Adicionar uma ou mais pessoas, digite o nome do seu app de chat.
Selecione seu app de chat nos resultados. Uma mensagem direta é aberta.
- Na nova mensagem direta com o app, digite
Helloe pressioneenter.
Para adicionar testadores de confiança e saber mais sobre como testar recursos interativos, consulte Testar recursos interativos para apps do Google Chat.
Resolver problemas
Quando um app de chat ou card do Google Chat retorna um erro, a interface do Chat mostra a mensagem "Algo deu errado". ou "Não foi possível processar sua solicitação". Às vezes, a interface do Chat não mostra nenhuma mensagem de erro, mas o app de chat ou card produz um resultado inesperado. Por exemplo, uma mensagem do card pode não aparecer.
Embora uma mensagem de erro não apareça na interface do Chat, mensagens de erro descritivas e dados de registro estão disponíveis para ajudar você a corrigir erros quando o registro de erros para apps do Chat está ativado. Para ajuda com a visualização, depuração e correção de erros, consulte Solucionar e corrigir erros do Google Chat.
Limpar
Para evitar cobranças na sua conta do Google Cloud pelos recursos usados neste tutorial, recomendamos que você exclua o projeto na nuvem.
- No console do Google Cloud, acesse a página Gerenciar recursos. Clique em Menu > IAM e administrador > Gerenciar recursos.
- Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
- Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.
Temas relacionados
Para adicionar mais recursos ao seu app de chat, consulte os seguintes tópicos: