dmon
dmon

Reputation: 30168

Google Chat Bot: Returning a threaded reply in onMessage() creates a new message in Space

We've been trying to update our ChatBots to use threaded replies, but we're running into issues, specifically when responding to a brand new message in the Space. For example, if a user says:

@TheChatBot help!

We want the bot to create a threaded reply to that message. Instead, it creates a new message at the top level of the space.

Interestingly, if we mention the bot from WITHIN a thread, it works correctly.

We tried many combinations of message.thread values (e.g. { threadKey: ...} , {name: ...}) and none of the combinations worked for the message outside of a thread.

Here's a snippet of the ChatBot gs code:

function onMessage(event) {
...
  var message = event.message;
  var threadName = message.thread ? message.thread.name : null;
...
  return {
    text: ...,
    threadReply: true,
    thread: {
      name: threadName
    }
  };
}

Upvotes: 1

Views: 465

Answers (2)

ayush-t02
ayush-t02

Reputation: 11

function sendThreadedReply(messageUrl, replyText) {
  // Input validation
  if (!messageUrl || !replyText) {
    throw new Error('Message URL and reply text are required');
  }
  
  // Extract space name and message ID from the URL
  let spaceName, messageId;
  
  try {
    // Remove any query parameters
    messageUrl = messageUrl.split('?')[0];
    const parts = messageUrl.split('/');
    
    if (parts.includes('room')) {
      const roomIndex = parts.indexOf('room');
      spaceName = parts[roomIndex + 1];
      messageId = parts[parts.length - 1];
    } else if (parts.includes('spaces')) {
      const spacesIndex = parts.indexOf('spaces');
      spaceName = parts[spacesIndex + 1];
      messageId = parts[parts.length - 1];
    } else {
      throw new Error('Unable to parse space name and message ID from URL');
    }
    
  } catch (error) {
    Logger.log('Error parsing URL: ' + error.toString());
    throw new Error('Invalid message URL format: ' + error.message);
  }

  // Construct the API endpoint with thread parameter
  const endpoint = `https://chat.googleapis.com/v1/spaces/${spaceName}/messages?messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD`;

  // Prepare the message payload with thread name
  const payload = {
    text: replyText,
    thread: {
      name: `spaces/${spaceName}/threads/${messageId}`
    }
  };

  // Get OAuth token
  let accessToken;
  try {
    accessToken = ScriptApp.getOAuthToken();
    Logger.log('Successfully obtained OAuth token');
  } catch (error) {
    Logger.log('Error getting OAuth token: ' + error.toString());
    throw new Error('Failed to obtain OAuth token. Make sure you have the necessary permissions.');
  }

  // Prepare request options
  const options = {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

  // Send the request
  try {
    const response = UrlFetchApp.fetch(endpoint, options);
    const responseCode = response.getResponseCode();
    const responseText = response.getContentText();
    
    Logger.log('Response code: ' + responseCode);
    Logger.log('Response body: ' + responseText);
    
    if (responseCode === 200) {
      const responseBody = JSON.parse(responseText);
      return responseBody;
    } else {
      throw new Error(`API returned status code ${responseCode}: ${responseText}`);
    }
  } catch (error) {
    Logger.log('Error sending request: ' + error.toString());
    throw new Error('Failed to send reply: ' + error.message);
  }
}

this code worked for me. You can use service account authentication or oauth2 depending on your usecase. I have used oauth2.

Threaded reply using webhooks and no oauth

function sendThreadedMessageByWebhook(webhookUrl, message, messageUrl) {
  // Extract thread name from message URL
  function getThreadNameFromUrl(url) {
    // Remove any query parameters
    url = url.split('?')[0];
    const parts = url.split('/');
    
    let spaceName, messageId;
    
    if (parts.includes('room')) {
      const roomIndex = parts.indexOf('room');
      spaceName = parts[roomIndex + 1];
      messageId = parts[parts.length - 1];
    } else if (parts.includes('spaces')) {
      const spacesIndex = parts.indexOf('spaces');
      spaceName = parts[spacesIndex + 1];
      messageId = parts[parts.length - 1];
    } else {
      throw new Error('Unable to parse space name and message ID from URL');
    }
    
    return `spaces/${spaceName}/threads/${messageId}`;
  }

  // Add messageReplyOption parameter to webhook URL
  webhookUrl = webhookUrl + "&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD";
  
  // Get thread name from message URL
  const threadName = getThreadNameFromUrl(messageUrl);
  
  // Prepare the message payload
  const payload = {
    text: message,
    thread: {
      name: threadName
    }
  };

  // Prepare request options
  const options = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json; charset=UTF-8'
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

  // Send the request
  try {
    const response = UrlFetchApp.fetch(webhookUrl, options);
    const responseCode = response.getResponseCode();
    const responseText = response.getContentText();
    
    Logger.log('Response code: ' + responseCode);
    Logger.log('Response body: ' + responseText);
    
    return {
      responseCode: responseCode,
      response: responseText
    };
  } catch (error) {
    Logger.log('Error sending request: ' + error.toString());
    throw new Error('Failed to send reply: ' + error.message);
  }
}

Upvotes: 1

SputnikDrunk2
SputnikDrunk2

Reputation: 4048

My investigation revealed that Google Chat currently restricts response placement to the top-level space name, even though individual thread names (messages where the chatbot was mentioned) are readily available within the onMessage response (e.g.,spaces/SPACE_ID/threads/THREAD_ID).


Current Behavior

Official documentation on Processing or Responding to Interaction Events confirms that Chat bots are currently limited to "Insert content to space" (see image below) for their replies, suggesting this behavior is intentional:

enter image description here

What I Have Tried

I attempted to formulate a workaround following the guide spaces.message.create. In this process, I intended for the chatbot to create a message on the thread where it has been mentioned, with use of the thread name. However, it appears there is a limitation in the Chat API. Specifically, one of the required parameters is a top-level Space name instead of a thread name.

Last Resort

Having said that, I would recommend that you consider submitting a feature idea on Google Issue Tracker. I believe this would be an ideal request for the chatbot, given its use cases such as the primary goal mentioned in your post.

Upvotes: 1

Related Questions