Wednesday, 1 June 2022

How to Auto Format Google Form Responses in Google Sheets

When you submit a Google Form, it stores a copy of the form response as a new row in the Google Sheet. The only problem here is that Google Forms will not add any formatting or styles to the new row that you may have applied to the previous rows of the sheet. Let me illustrate this with a small example.

Here’s a Google Sheet that is storing Google Form responses. I’ve changed the default font family to Droid Sans, center-aligned the Country and Age column and also applied a different date format to the Date of Birth column.

Google Forms Response Sheet

Everything looks good but as soon as a new form submissions is made, the new row appended to the Google Sheet via Google Forms will lose all the formatting.

The cell alignment is not preserved, the custom date formats are ignored and so is the default font size and font family. Here’s a screenshot of the same sheet but with a new row added through Google Forms.

Google Forms Auto Formatting

Also see: Automate Google Forms through Workflows

Auto Format New Rows in Google Sheets

Since there’s no way for us to override this Google Forms behavior, we can take the help of Google Apps Script to automatically format new rows in Google Sheets that are added through Google Forms.

The idea is simple. We’ll create an onFormSubmit trigger inside the Google Sheet that will be executed whenever a new form is submitted. This trigger will take whatever formatting that has been applied to the previous row and apply to the current row.

To get started, open the Google Sheet and format the last row with the styles that you would like to apply to incoming form responses.

Next, go to Extensions > Apps Script menu and copy-paste the Google Script below. Run the createTrigger and you are good to go!

/**
 * @OnlyCurrentDoc
 */

const createTrigger = () => {
  ScriptApp.getProjectTriggers().forEach((trigger) => {
    ScriptApp.deleteTrigger(trigger);
  });
  ScriptApp.newTrigger('formRowOnFormSubmit').forSpreadsheet(SpreadsheetApp.getActive()).onFormSubmit().create();
};

const formRowOnFormSubmit = (e) => {
  if (!e) {
    throw new Error('Please do not run this function manually!');
  }
  const { range } = e;
  const row = range.getRowIndex();
  if (row > 2) {
    const sheet = range.getSheet();
    // Select the previous row range
    const sourceRange = sheet.getRange(`${row - 1}:${row - 1}`);
    // Get the recently added row
    const targetRange = sheet.getRange(`${row}:${row}`);
    // Copy the format only from the previous row to the current row
    sourceRange.copyTo(targetRange, SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false);
  }
};

Conditional Formatting in Google Sheets

Learn more about conditional formatting in Google Sheets that allows you to apply automatic formatting to cells in spreadsheets that meet certain criteria.



from Digital Inspiration https://ift.tt/EblptPW

Tuesday, 31 May 2022

The Most Useful Twitter Bots

Useful Twitter Bots

Set reminders with @RemindMe_OfThis

An open-source Twitter bot that lets you easily set reminders for public tweets. Mention @RemindMe_OfThis in the reply of any tweet and specify the time in natural English when you would like to reminded of that tweet.

You could say things like in 2 days or in 12 hours or next week or even in 5 years. Check out the source on Github.

Save Twitter Threads with @ThreadReaderApp

The gurus on Twitter have figured out that threads are the best way to extend the reach of their tweets and @ThreadReaderApp makes is really easy for you read and save these threads.

To get started, reply to any tweet of a thread and mention @threadreaderapp with the “unroll” keyword. and they create a single page with all the tweets arranged in chronologicusefual order. Blog posts anyone?

Also see: Search Twitter Like a Pro

Capture Screenshots with @pikaso_me

Reply to a tweet with the word “screenshot this” and mention @pikaso_me in the reply. You’ll receive a reply tweet with a screenshot image of the original tweet.

The twitter bot capture images in tweets but you can also use Screenshot Guru for that.

Download Videos with @DownloaderBot

You can easily download any video or GIF image from tweets with the help of this Twitter bot.

Mention @DownloaderBot in a reply to any tweet that contains either a video or a gif image, and you will receive a reply with the direct link to download the media.

Twitter, like YouTube, may have a love-hate relationship with bots that allow downloading videos off their platform so it is always a good idea to bookmarks some alternatives. There’s @GetVideoBot, @SendVidBot and @Get_This_V.

Also see: Create your own Twitter Bots



from Digital Inspiration https://ift.tt/CEa3c9H

How to Use Gmail SMTP Server for Sending Email

This step-by-step tutorial describes how you can connect to the Gmail SMTP server for sending emails from a Node.js web application that could be deployed on Google Cloud Functions, AWS Lambda, Cloud Run or running on your local machine.

And unlike most other Node SMTP tutorials that use the username and password combination, this approach uses OAuth and doesn’t require you to turn on less secured apps access in your Google account.

Create Gmail OAuth Credentials

Create a new Google Cloud Project and enable the Gmail API as described in a previous tutorial.

Gmail SMTP OAuth Application

In the APIs & Services section, click on Credentials and click on Create credentials > OAuth Client Id to create a new client ID that will be used to identify your application to Google’s OAuth servers.

Set the application type as Web Application and put the following URL in the Authorized Redirect URI.

https://developers.google.com/oauthplayground

Click the Create button and you’ll be provided the OAuth Client ID and Client Secret values that you’ll need in the next step.

Create Gmail Refresh Token

Gmail Access Refresh Token

Next, we’ll generate a refresh token using the Google Developer OAuth 2.0 playground. The access tokens are valid for an hour but the refresh tokens stay valid forever (unless manually revoked) and can be used to generate a fresh access token.

Go to google.com/oauthplayground, click the Gear Icon and check the option that says Use your own OAuth credentials. Copy-paste the Client ID and Client Secret that you have generated in the previous step.

Inside the Select & Authorize APIs section, enter the scope https://mail.google.com and click the Authorize APIs button to generate the authorization code.

Click the Exchange authorization code for tokens to generate the refresh token that we’ll require in the next step.

Refresh Token

Prepare the Node.js Application

Create a new folder and install the googleapis and nodemailer packages.

mkdir gmail-smtp-sender
cd gmail-smtp-sender
npm init --y
npm install dotenv googleapis nodemailer --save
touch index.js

Create a new .env file in the root folder and add the credentials in the file. Add the file to .gitignore so it is not added to the repository.

// Replace these with your own credentials
CLIENT_ID = 'r2l82l8.apps.googleusercontent.com'
CLIENT_SECRET = 'GOCSPX-5n00Mqm5Jc45p'
REFRESH_TOKEN = '1//04yt8hEatvIr3uyk-ZJSYIhmYqMk4C4EqfPK24w'
REDIRECT_URL = 'https://developers.google.com/oauthplayground'

Open the index.js file and add the following code. You may need to replace the sender’s email with the email address of your own Gmail account that you have authorized to send email.

The Gmail SMTP server name is smtp.gmail.com and the Gmail SMTP port is 465. You can send up to 100 emails per day when the messages sent via SMTP.

const { google } = require('googleapis');
const nodemailer = require('nodemailer');
require('dotenv').config();

const sendEmail = async () => {
  const oauth2Client = new google.auth.OAuth2(
    process.env.CLIENT_ID,
    process.env.CLIENT_SECRET,
    process.env.REDIRECT_URL
  );
  oauth2Client.setCredentials({ refresh_token: process.env.REFRESH_TOKEN });

  const accessToken = await oauth2Client.getAccessToken();
  const myEmail = 'amit@labnol.org';

  const smtpTransport = nodemailer.createTransport({
    service: 'gmail',
    host: 'smtp.gmail.com',
    port: 465,
    secure: true,
    auth: {
      type: 'OAuth2',
      user: myEmail,
      clientId: process.env.CLIENT_ID,
      clientSecret: process.env.CLIENT_SECRET,
      refreshToken: process.env.REFRESH_TOKEN,
      accessToken,
    },
  });

  const mailOptions = {
    from: 'Sender Name <amit@labnol.org>',
    to: 'Receiver Name <hello@example.com>',
    subject: 'Test email 🚀',
    text: 'This is a test email from Node.js 🎉',
    html: 'This is a <b>test email</b> from Node.js 🎉',
  };

  try {
    const response = await smtpTransport.sendMail(mailOptions);
    console.log(`Email sent!`, response);
  } catch (f) {
    console.error(f.message);
  } finally {
    smtpTransport.close();
  }
};

sendEmail().then(() => console.log('Done!'));

Here’s a test email sent by the application. If the email receiver client doesn’t support HTML mail, the plain text version is rendered.

Gmail Sent email

Correct Gmail OAuth Scope

While you can send emails from Gmail using the https://www.googleapis.com/auth/gmail.send scope, you would need to use the restricted https://mail.google.com/ scope for Gmail SMTP. If your OAuth client uses a different scope when requesting permissions for an user, the application will return the 535-5.7.8 Username and Password not accepted error.



from Digital Inspiration https://ift.tt/eLuMlvZ

Monday, 23 May 2022

Send WhatsApp Messages with a Google Sheets Function

In a previous tutorial, you learned how to send WhatsApp messages from Google Sheets using the official WhatsApp API. The first 1,000 messages per month for each WhatsApp Business Account are free and then you pay per use based on the country of the message sender and the message recipient.

WhatsApp API Pricing

For instance, if you are sending a WhatsApp message from the US phone number to a WhatsApp user in France, the cost would be 14¢ per message. However, if you send messages from WhatsApp number in India to another number in India, the cost would be around 0.006¢ per message. The rate cards for WhatsApp API pricing are available here.

In addition to the cost factor, the WhatsApp Business API requires you to have a verified business on Facebook (see verification requirements) and the terms require that you will only send message to WhatsApp users who have opted-in to receive future messages from you on WhatsApp.

The other limitation of WhatsApp API is that you can only send messages that are based on templates pre-approved by WhatsApp. You can however send free-form messages within 24 hours of the last user message in a conversation.

Send WhatsApp Message

WhatsApp Function for Google Sheets

If you are a small business that is looking for an alternate route to message customers on WhatsApp without paying for the API, here’s a semi-automated technique. You can use the Click to Chat feature of WhatsApp to quickly send a personalized message to any phone number that’s registered on WhatsApp.

For this example, we have a sheet that list the customer’s name, amount that they have to pay and the due date for payment. We can use the CONCATENATE or TEXTJOIN function of Google Sheets to create a personalized message for each client in column D.

Create Personalized Message

The column E of the Google Sheet contains the phone numbers of each WhatsApp user. We can use the WHATSAPP custom function to create a personalized chat link for different customers in the Google Sheet. When you click this chat link, it automatically open a WhatsApp conversation with the user and the message is pre-filled in the chat box.

This method does require a few extra click but there’s no cost involved and it works for both WhatsApp Business and WhatsApp personal accounts.

WhatsApp Click to Chat

WhatsApp Function

Here’s the underlying WHATSAPP function that generates the Click to Chat link in Google Sheets. It also supports Array Formulas.

The third parameter determines whether the link should launch the WhatsApp website or the WhatsApp desktop client. You can play with the live sheet here.

/**
 * Create WhatsApp Click to Chat Link
 *
 * @param {string} phone The phone number with country code
 * @param {string} message The text message
 * @param {boolean} web Open the message in WhatsApp web?
 * @return The pre-filled message link for WhatsApp.
 * @customfunction
 */
function WHATSAPP(phone, message, web) {
  if (Array.isArray(phone)) {
    return phone.map((row, index) => WHATSAPP(row[0], message[index][0]), web);
  }
  const phoneNumber = String(phone).replace(/[^\d]/g, '');
  const messageText = encodeURIComponent(message);
  return web === true
    ? `https://web.whatsapp.com/send?phone=${phoneNumber}&text=${messageText}`
    : `https://wa.me/${phoneNumber}?text=${messageText}`;
}


from Digital Inspiration https://ift.tt/QDRHlSm

Friday, 20 May 2022

How to Send WhatsApp Messages from Google Sheets with the WhatsApp API and Apps Script

WhatsApp with Google Sheets

This tutorial describes how you can use the new WhatsApp API with Google Apps Script to send WhatsApp messages from Google Sheets. The same approach would also work for sending WhatsApp messages from Google Forms when new form submissions are received.

Step 1: Create a WhatsApp App

Go to developers.facebook.com and click the Create App button to create a new app that we’ll use for sending WhatsApp messages.

Facebook WhatsApp App

Select Business as the app type.

Give your app a descriptive name (do not use any Facebook trademarks like WhatsApp or Facebook in the app name) and click the Create App button to create the app.

WhatsApp App Name

Once the app has been created, click the WhatsApp button on the next screen to add WhatsApp sending capabilities to your app.

On the next screen, you will be required to link your WhatsApp app to your Facebook business account. You will also have the option to create a new business account if you don’t have one yet.

Add WhatsApp App

Step 2: Add Recipient’s phone number

Facebook will provide you with a test WhatsApp phone number that will be default sending address of your app. For recipients, you’ll have the option to add a maximum of 5 phone numbers during the development phase without having to make any payment.

Your WhatsApp app will provide you with a temporary access token that will be valid for 23 hours. Make a note of this token as we’ll need it in a later step.

WhatsApp Phone Number

Next, click the Recipient Phone Number dropdown to add up to 5 different WhatsApp phone numbers to your app. You’ll receive a verification code on the added numbers and you’ll be able to send WhatsApp messages to only numbers that have been verified with the code.

Verify Phone Number

Step 3: Create WhatsApp Message Template

Switch to the template manager and create a new WhatsApp message template.

For this example, we’ll choose the category as Account Update and give a unique name for your message template. For languages, we’ll choose English for which the code language is en. If you use another language, make a note of the code language as it is required for sending messages.

WhatsApp Message Template

Write the Personalized Message

We are using variable parameters in the message and these will be replaced with the actual values from the Google Sheet. It is very similar to markers that you may have used inside Mail Merge and Document Studio with a small difference that these are positional markers and not named markers.

Here’s our message template where variables 1 and 2 are for customer name and item name respectively.

WhatsApp Message Template

It may take up to a minute for WhatsApp to approve your new message template.

Step 4: Send WhatsApp Messages

Now that all our configuration on the Facebook / WhatsApp side is complete, let’s work on the Google Sheet that will actually send these personalized WhatsApp messages in an automated manner.

Click here to copy the WhatsApp Sheet in your own Google account.

Google sheets WhatsApp

Next, add the phone numbers (with country code) in the Phone number column of the Google Sheet. You should only add numbers that you have verified with your test WhatsApp account in the previous step.

Then go to the Extension menu and choose Apps Script to open the underlying script. Replace WHATSAPP_ACCESS_TOKEN and WHATSAPP_TEMPLATE_NAME with the values that you have copied in the previous steps.

Click the Run button inside the Apps Script editor and it should instantly send the WhatsApp message to your listed phone numbers.

And what you have below is the actual WhatsApp message sent by the WhatsApp API replacing the variable markers in the template with actual values from Google Sheets.

WhatsApp Message Text

The Technical Details

How WhatsApp API works with Google Apps Script

The Google Apps Script connects to Google Sheets and retrieves the details of customers, including phone numbers, that are to be sent messages through WhatsApp.

// Get data from Google Sheets
// for sending messages through WhatsApp
const getSheetData_ = () => {
  const sheet = SpreadsheetApp.getActiveSheet();
  const [header, ...rows] = sheet.getDataRange().getDisplayValues();
  const data = [];
  rows.forEach((row) => {
    const recipient = {};
    header.forEach((title, column) => {
      recipient[title] = row[column];
    });
    data.push(recipient);
  });
  return data;
};

Next, the script iterates through each row of the sheet and sends a WhatsApp message by invoking the WhatsApp Cloud API with the UrlFetch service.

// Send Message with WhatsApp Cloud API

const sendMessage_ = (e) => {
  const apiUrl = 'https://graph.facebook.com/v13.0/114746974570888/messages';
  const request = UrlFetchApp.fetch(apiUrl, {
    muteHttpExceptions: true,
    method: 'POST',
    headers: {
      Authorization: `Bearer ${WHATSAPP_ACCESS_TOKEN}`,
      'Content-Type': 'application/json',
    },
    payload: JSON.stringify({
      type: 'template',
      messaging_product: 'whatsapp',
      to: e.recipient_number,
      template: {
        name: WHATSAPP_TEMPLATE_NAME,
        language: { code: LANGUAGE_CODE },
        components: [
          {
            type: 'body',
            parameters: [
              { type: 'text', text: e.customer_name },
              { type: 'text', text: e.item_name },
              { type: 'text', text: e.delivery_date },
            ],
          },
        ],
      },
    }),
  });

  const { error } = JSON.parse(request);

  if (error) {
    Logger.log(`😞 ${error}`);
  } else {
    Logger.log(`Message sent to ${recipient_number}`);
  }
};

const main = () => {
  getSheetData_().forEach((row) => {
    const status = sendMessage_({
      recipient_number: row['Phone Number'].replace(/[^\d]/g, ''),
      customer_name: row['Customer Name'],
      item_name: row['Item Name'],
      delivery_date: row['Delivery Date'],
    });
  });
};

Also see: Automate Workflows with Google Sheets



from Digital Inspiration https://ift.tt/negSB5C

Tuesday, 17 May 2022

How to Schedule a Meeting in Google Meet with Apps Script

This Apps Script sample shows how you can programmatically schedule video meetings inside Google Meet with one or more participants using the Google Calendar API. It can be useful for teachers who wish to schedule regular meetings with their students but instead of manually creating meeting invites, they can easily automate the whole process for the entire class.

Schedule Google Meeting

Setup Google Meeting with Apps Script

Give your meeting a title, the start date, the meeting duration, the list of attendees and how often you wanted to be reminded of the upcoming Google meeting. A new meeting event will be added to your Google Calendar and you’ll also be provided with a Google Meet link that you share with your students and colleagues through mail merge.

const createGoogleMeeting = () => {
  // The default calendar where this meeting should be created
  const calendarId = 'primary';

  // Schedule a meeting for May 30, 2022 at 1:45 PM
  // January = 0, February = 1, March = 2, and so on
  const eventStartDate = new Date(2022, 5, 30, 13, 45);

  // Set the meeting duration to 45 minutes
  const eventEndDate = new Date(eventStartDate.getTime());
  eventEndDate.setMinutes(eventEndDate.getMinutes() + 45);

  const getEventDate = (eventDate) => {
    // Dates are computed as per the script's default timezone
    const timeZone = Session.getScriptTimeZone();

    // Format the datetime in `full-date T full-time` format
    return {
      timeZone,
      dateTime: Utilities.formatDate(eventDate, timeZone, "yyyy-MM-dd'T'HH:mm:ss"),
    };
  };

  // Email addresses and names (optional) of meeting attendees
  const meetingAttendees = [
    {
      displayName: 'Amit Agarwal',
      email: 'amit@labnol.org',
      responseStatus: 'accepted',
    },
    { email: 'student1@school.edu', responseStatus: 'needsAction' },
    { email: 'student2@school.edu', responseStatus: 'needsAction' },
    {
      displayName: 'Angus McDonald',
      email: 'assistant@school.edu',
      responseStatus: 'tentative',
    },
  ];

  // Generate a random id
  const meetingRequestId = Utilities.getUuid();

  // Send an email reminder a day prior to the meeting and also
  // browser notifications15 minutes before the event start time
  const meetingReminders = [
    {
      method: 'email',
      minutes: 24 * 60,
    },
    {
      method: 'popup',
      minutes: 15,
    },
  ];

  const { hangoutLink, htmlLink } = Calendar.Events.insert(
    {
      summary: 'Maths 101: Trigonometry Lecture',
      description: 'Analyzing the graphs of Trigonometric Functions',
      location: '10 Hanover Square, NY 10005',
      attendees: meetingAttendees,
      conferenceData: {
        createRequest: {
          requestId: meetingRequestId,
          conferenceSolutionKey: {
            type: 'hangoutsMeet',
          },
        },
      },
      start: getEventDate(eventStartDate),
      end: getEventDate(eventEndDate),
      guestsCanInviteOthers: false,
      guestsCanModify: false,
      status: 'confirmed',
      reminders: {
        useDefault: false,
        overrides: meetingReminders,
      },
    },
    calendarId,
    { conferenceDataVersion: 1 }
  );

  Logger.log('Launch meeting in Google Meet: %s', hangoutLink);
  Logger.log('Open event inside Google Calendar: %s', htmlLink);
};

Also see: Generate Add to Calendar Links

Google Meeting with Recurring Schedule

The above code can be extended to create meetings that occur on a recurring schedule.

You need to simply add a recurrence attribute to the meeting event resource that specifies the recurring event in RRULE notation. For instance, the following rule will schedule a recurring video meeting for your Maths lecture every week on Monday, Thursday for 8 times.

{
  ...event,
  recurrence: ["RRULE:FREQ=WEEKLY;COUNT=8;INTERVAL=1;WKST=MO;BYDAY=MO,TH"];
}

Here are some other useful RRULE examples:

  • FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR - Occurs every week except on weekends
  • FREQ=MONTHLY;INTERVAL=2;BYDAY=TU - Occurs every Tuesday, every other month
  • INTERVAL=2;FREQ=WEEKLY - Occurs every other week
  • FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,TH;BYMONTH=12 - Occurs every other week in December on Tuesday and Thursday
  • FREQ=MONTHLY;INTERVAL=2;BYDAY=1SU,-1SU - Occurs every other month on the first and last Sunday of the month


from Digital Inspiration https://ift.tt/J4OITMU