Google-api-nodejs-client: Hangout Chat API Documentation

Created on 16 Apr 2018  路  11Comments  路  Source: googleapis/google-api-nodejs-client

Hangout Chat API is supported in the latest client library, but there's no documentation for that. Can we have a documentation for Hangout Chat API too, especially for the asynchronous calls?

docs question

All 11 comments

馃憢 greetings! There are a few good reference docs you can use for Chat. FIrst, here's the developer guide:
https://developers.google.com/hangouts/chat/

And for a reference of how to use the API:
http://google.github.io/google-api-nodejs-client/modules/_apis_chat_v1_.html

Does this get you where you need to go? Or are there are specific things you're looking for? Hope this helps!

Hi Justin, thanks for replying. I have the Chat API working using the REST API, but I was looking for the documentation of the node.js client, just like how I use sheets API. I tried using the nodejs client, but it started giving me some strange errors. Let me recreate them -
Here's my code, just to give you an idea

/* eslint-disable no-console */
const url = require('url');
const request = require('request-promise-native');
const CHAT_ENDPOINT = 'https://chat.googleapis.com';

async function sendMessageUsingRestApi(apiKey, threadName, message) {
  let threadParts = threadName.split('/');
  let spaces = threadParts[1];
  let threadKey = threadParts[3];
  const auth = apiKey;
  let endPointUrl = url.resolve(
    CHAT_ENDPOINT,
    `/v1/spaces/${spaces}/messages?threadKey=${threadKey}&key=${auth}`
  );
  const chatApiResponse = await request.post(endPointUrl, {
    json: { text: message }
  });
  return chatApiResponse;
}

const { google } = require('googleapis');
const chat = google.chat('v1');
const { promisify } = require('util');
chat.spaces.messages.createAsync = promisify(chat.spaces.messages.create);

async function sendMessageUsingClient(apiKey, threadName, message) {
  let threadParts = threadName.split('/');
  let spaces = threadParts[1];
  let threadKey = threadParts[3];
  const auth = apiKey;
  const chatApiResponse = await chat.spaces.messages.createAsync({
    auth: auth,
    parent: `spaces/${spaces}`,
    threadKey: threadKey,
    body: {
      text: message
    }
  });
  return chatApiResponse;
}

sendMessageUsingRestApi(
  'MY_API_KEY',
  'spaces/<spaceID>/threads/<threadKey>',
  'Hi From Rest'
)
  .then(response => console.log(response))
  .catch(e => console.error(e));

sendMessageUsingClient(
  'MY_API_KEY',
  'spaces/<spaceID>/threads/<threadKey>',
  'Hi From Client'
)
  .then(response => console.log(response))
  .catch(e => console.error(e));

So, the REST API works and I get the message Hi From Rest in my chat window, however the nodejs client gives me error, just because it's not clear which parameter will go where. I have tried a lot and finally decided to use the REST API. The problem with that is, I have to use an API key, instead of the Service Account. That's why I think there should be a proper documentation of using Chat API using the Nodejs client.

Here's the error I am getting just in case it helps -

{Error: <h1>Not Found</h1>
<h2>Error 404</h2>

    at createError (/Volumes/Projects/Work/node_modules/axios/lib/core/createError.js:16:15)
    at settle (/Volumes/Projects/Work/node_modules/axios/lib/core/settle.js:18:12)
    at Unzip.handleStreamEnd (/Volumes/Projects/Work/node_modules/axios/lib/adapters/http.js:201:11)
    at emitNone (events.js:111:20)
    at Unzip.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1055:12)}
    at _combinedTickCallback (internal/process/next_tick.js:138:11)
    at process._tickCallback (internal/process/next_tick.js:180:9)

I added the service authentication code to yours and the REST API call works well with it! Just make a service account and download the JSON credentials file and put it in your directory. However, I only got this to work with REST API call, if I make any further progress later, I can update this comment.

(I commented out the token check function because the callback function was giving me an issue)

`const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
const request = require('request-promise-native');

// If modifying these scopes, delete credentials.json.
const SCOPES = ['https://www.googleapis.com/auth/script.projects'];
const TOKEN_PATH = 'credentials.json';

// Load client secrets from a local file.
fs.readFile('client_secret.json', (err, content) => {
  if (err) return console.log('Error loading client secret file:', err);
  // Authorize a client with credentials, then make the REST API call.
  authorize(JSON.parse(content), sendMessageUsingRestApi('<API key>','spaces/<space ID>','This is sent using REST API'));
});

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(credentials, callback) {
  const {client_secret, client_id, redirect_uris} = credentials.installed;
  const oAuth2Client = new google.auth.OAuth2(
      client_id, client_secret, redirect_uris[0]);

     /* 
  // Check if we have previously stored a token.
  fs.readFile(TOKEN_PATH, (err, token) => {
    if (err) {
        return getAccessToken(oAuth2Client, callback);
    }
    oAuth2Client.setCredentials(JSON.parse(token));
    if(callback){
        callback(oAuth2Client);
    }
  });
  */
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 * @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback for the authorized client.
 */
function getAccessToken(oAuth2Client, callback) {
  const authUrl = oAuth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES,
  });
  console.log('Authorize this app by visiting this url:', authUrl);
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  rl.question('Enter the code from that page here: ', (code) => {
    rl.close();
    oAuth2Client.getToken(code, (err, token) => {
      if (err) return callback(err);
      oAuth2Client.setCredentials(token);
      // Store the token to disk for later program executions
      fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
        if (err) console.error(err);
        console.log('Token stored to', TOKEN_PATH);
      });
      callback(oAuth2Client);
    });
  });
}`

Thanks @lcundiff for the code. I would however really appreciate if we can make this work with the nodejs client also. I hope somebody just updates the docs somewhere.

I'm also having issues understanding the docs provided for Chat. Despite the auth issues which I've managed to work out elsewhere, I simply can't create a message and send to a space.

const chat = google.chat({version: 'v1', auth: jwtClient});

chat.spaces.messages.create({
    parent: space.name,
    body: {
        text: 'hello world'
    }
})
.then(res => {
    console.log('Response:', res.data);
})
.catch(err => {
    console.error('Error:', err);
});

Which throws:
Invalid JSON payload received. Unknown name "body[text]": Cannot bind query parameter. Field \'body[text]\' could not be found in request message.

The debug of the error says that the POST request is:
POST /v1/spaces/6WNdlAAAAAE/messages?body%5Btext%5D=hello%20world without any POST body.

An example in a comment above used this body.text item and it was reported to work but I can't see how it ever worked. Also, when I put that body.text as the second argument to the create function, it complains that Message cannot be empty.

There are numerous samples for other Google services, why not Chat?

To update anyone else looking for a solution to this, I dug deep in to the google source and discovered that the following create method signature works:

chat.spaces.messages.create({
    parent: 'spaces/xxxxxx',
    requestBody: {
        text: 'hello world'
    }
})

@jc21 Awesome. Have you done anything using the threadKey query parameter when creating a message? Trying to continuously update a message instead of sending new ones using messages.update (but it requires threadKey.

Found a work around. Just am using the message ID pulled from the message JSON. The thread ID is in the message JSON too, but realized thread ID is used for "replying" to a message while message ID (name attribute in the JSON) is used for updating! So only use threadID for using the chat reply feature.

@JustinBeckwith Close this issue?

What comes after spaces in the Parent key Object @jc21 ? Can you help me please ?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ashishbajaj99 picture ashishbajaj99  路  3Comments

nanu-c picture nanu-c  路  3Comments

lowagner picture lowagner  路  3Comments

JustinBeckwith picture JustinBeckwith  路  3Comments

raapperez picture raapperez  路  3Comments