Search code examples
javascriptgoogle-apps-scriptgoogle-formsgoogle-hangouts

Google Forms / Apps Script / Hangouts Chat - Bot message content sending individually instead of together


I'm trying to make a Google Hangouts Chat Bot that detects when a form has been filled in, and sends the responses of the most recent form submission to Hangouts Chat using a bot. I have built this off existing code (my JS / GAS knowledge is near zero), mainly based on the GitHub TSFormBot repo. The issue is, it is sending each response invidiually as a different message, instead of 1 single message with all of the content.

For exmaple, a 4 question form causes the bot to send 4 individual replies, with one of the different answers in each. Could you please help me see where I'm going wrong, so I could get the content of all 4 answers in a single response?

Thanks!

Current code:

function postToRoom(e) {
    var formResponses = FormApp.getActiveForm().getResponses();
  var formResponse = formResponses[formResponses.length-1];
  var itemResponses = formResponse.getItemResponses();
    for (var j = 0; j < itemResponses.length; j++) {
    var itemResponse = itemResponses[j];
  var options, options, url; 
  url = PropertiesService.getScriptProperties().getProperty('WEBHOOK_URL');
  if (url) {
    try { 
      payload = {
        "cards": [
          {
            "header": {
              "title": "There is a new request!",
              "imageUrl": "https://images.emojiterra.com/google/android-10/128px/1f916.png",
              "imageStyle": "IMAGE",
            },
                        "sections": [
              {
                "widgets": [
                  {
                    "textParagraph": {
                      "text": '<b>'+ (
                        itemResponse.getItem().getTitle() + ' ' + itemResponse.getResponse())',

                    }
                  }
                ] 
              },
              {

                "widgets": [
                  {
                    "buttons": [
                      {
                        "textButton": {
                          "text": "GO TO RESPONSE",
                          "onClick": {
                            "openLink": {
                              "url": e.response.getEditResponseUrl()
                            }
                          }
                        }
                      },
                      {
                        "textButton": {
                          "text": "GO TO FORM",
                          "onClick": {
                            "openLink": {
                              "url": FormApp.getActiveForm().getEditUrl()
                            }
                          }
                        }
                      }
                    ]
                  }
                ]
              }
            ] 
          } 
        ]
      }    
      options = {
        'method' : 'post',
        'contentType': 'application/json; charset=UTF-8',
        'payload' : JSON.stringify(payload)
      };
      UrlFetchApp.fetch(url, options); 
    } catch(err) {
      Logger.log('FormBot: Error processing Bot. ' + err.message);
    }
  } else {
    Logger.log('FormBot: No Webhook URL specified for Bot');
  }
}


The Form:

The form
The Bot Response

Bot Response


Solution

  • The issue you're having it's because, inside your for loop, you are sending every time your payload using UrlFetchApp.fetch(url, options);, therefore you need to do it in this way:

    // Previous stuff
    for (var j = 0; j < itemResponses.length; j++) {
      // Do something 
    }
    // Do more stuff
    UrlFetchApp.fetch(url, options); 
    

    Knowing that. The first thing I changed in your code, it was to create a payload object, which would possess a sections attribute that later will be populated.

    var payload = {
            "cards": [{
              "header": {
               "title": "TSFormBot",
               "subtitle": "Form Notifications Bot",
               "imageUrl": "https://raw.githubusercontent.com/techstreams/TSFormBot/master/notifications.png",
               "imageStyle": "IMAGE"
               },
            "sections": []
            }
           ]
          };
    

    Having the payload object, I created a function for populating the sections attribute, there you will have the for loop and after filling the object with all the responses, I added the two buttons:

    /*
     * Build Payload 
     * 
     * @param {Object} payload 
     * @param {ItemResponse[]} itemResponses
     * @param {FormResponse} formResponse
     */
    function populateCard(payload, itemResponses, formResponse){
     for (var j = 0; j < itemResponses.length; j++) {
        var itemResponse = itemResponses[j];
         payload["cards"][0]["sections"].push({
           "widgets": [{
             "textParagraph": {
                "text": itemResponse.getItem().getTitle() + ' ' + itemResponse.getResponse()
               }
             }
           ]
        });
      }
      payload["cards"][0]["sections"].push({
                "widgets": [
                  {
                    "buttons": [
                          {
                            "textButton": {
                              "text": "GO TO RESPONSE",
                              "onClick": {
                                "openLink": {
                                  "url": formResponse.getEditResponseUrl()
                                }
                              }
                            }
                          },
                          {
                            "textButton": {
                              "text": "GO TO FORM",
                              "onClick": {
                                "openLink": {
                                  "url": FormApp.getActiveForm().getEditUrl()
                                }
                              }
                            }
                          }
                        ]
                  }
            ]
          }   
      );
      return payload;
    }
    

    After all that, you will able to send the request using UrlFetchApp.fetch(url, options);. Your postToRoom(e) function would look like this:

    /*
     * Process Form Submission
     * 
     * @param {Object} e - form submit event object
     */
    function postToRoom(e) {
      var formResponses = FormApp.getActiveForm().getResponses();
      var formResponse = formResponses[formResponses.length-1];
      var itemResponses = formResponse.getItemResponses();
      formResponse.getEditResponseUrl()
      var options, options, url; 
      url = PropertiesService.getScriptProperties().getProperty('WEBHOOK_URL');
      if (url) {
        try { 
          var payload = {
            "cards": [{
              "header": {
               "title": "TSFormBot",
               "subtitle": "Form Notifications Bot",
               "imageUrl": "https://raw.githubusercontent.com/techstreams/TSFormBot/master/notifications.png",
               "imageStyle": "IMAGE"
               },
            "sections": []
            }
           ]
          };
          // Call this function to populate the card with the responses 
          var PopulatedPayload = populateCard(payload, itemResponses, formResponse);  
          options = {
            'method' : 'post',
            'contentType': 'application/json; charset=UTF-8',
            'payload' : JSON.stringify(payload)
          };
          UrlFetchApp.fetch(url, options); 
        } catch(err) {
          Logger.log('TSFormBot: Error processing Bot. ' + err.message);
        }
      } else {
        Logger.log('TSFormBot: No Webhook URL specified for Bot');
      }
    }
    

    Card Response

    enter image description here

    Docs

    I used these docs to help you: