Serverless chat bot by Google Apps Script - step/step
Tags: googleappsscript, javascript, chatwork, line
Published: 2020-11-04 15:27:09

---
title: Serverless chat bot by Google Apps Script - step/step
published: true
description: step by step Google Apps Script
tags: googleappsscript, javascript, chatwork, LINE
//cover_image: https://direct_url_to_image.jpg
---

## TL;DR
By using "GAS(Google Apps Script)", notify today's events on "Google Calendar" to "Google Chat", "Chatwork" and "LINE Messenger" every morning.

All you need is

- Web browser
- Accounts (Google, Chatwork, LINE, LINE Developer)

and it's free.

source code: [kurab/notifyEvents](https://github.com/kurab/notifyEvents)

## What's Google Apps Script?
It's a JavaScript-based programming language provided by Google that allows you to do a lot of things, and it's easy to integrate with Google's services.

Nowadays, even non-engineers can let Python do all the routine work for them. But with Python, it's a bit more difficult to get started in terms of having to install this and that. With Google Apps Script (GAS), on the other hand, all you need is a browser and a Google account, so even non-engineers can easily get started. Furthermore, there are many companies that use GSuite, so routine work may be relatively related to Google's services.

GAS is also basically JavaScript, so you can solve most of the problems by just Google it.


## Hello, world
First of all, access to Google Drive. I'd like to work in "My Drive > bot" directory for this time.

Select "Google Apps Script" from "+New" menu on top left or "Right click" menu > more.

![addScript](https://dev-to-uploads.s3.amazonaws.com/i/9k770t1473rndu41biqw.png)
 
Then, you will move to the script editor like this.

![gasEdit](https://dev-to-uploads.s3.amazonaws.com/i/9td2seabt9t938hdn530.png)
 
Let name helloWorld as project name and update code like this:

```JavaScript
function myFunction() {
  console.log('Hello, World');
}
```

After saving your code(Ctrl+s or save button), click Run button(▶) on menu. It seems nothing happens but you can find log from menu "View > Logs".

![gasLog](https://dev-to-uploads.s3.amazonaws.com/i/vd51xkscpgv6ych4k17u.png)

Congrats!! Now you have done your first Google Apps Script. This is the basic working flow of GAS. 


## Get Calendar Events

Here is the reference of Google Calendar API.

> [Calendar Service](https://developers.google.com/apps-script/reference/calendar)

This time, I'd like to get today's events information.

Let's create GAS file and name "notifyCalendar" or anything you want.

To start, access your calendar from script and get the calendar name.
Find Calendar ID in "Settings and sharing" of calendar > Integrate calendar > Calendar ID and paste it to {Calendar ID} in following code.

```javascript
function notifyEvents() {
  const calendarId = '{Calendar ID}';
  const calendar = CalendarApp.getCalendarById(calenderId);
  const calendarName = calendar.getName();

  console.log(calendarName);
}
```

When you run this, there will be a review of your calendar access and you will be prompted to grant it. Then wait for the execution to complete, open the log and you will see the calendar name. If there is no log yet, wait for a while.

Now, let's continue to get today's events in the calendar and put them in the log.

```javascript
function notifyEvents() {
  const calendarId = '{Calendar ID}';
  const calendar = CalendarApp.getCalendarById(calenderId);
  const calendarName = calendar.getName();

  const today = new Date();
  const calendarEvents = calendar.getEventsForDay(today);

  console.log(calendarEvents);
}
```

If you have events today, you will get data like [{}, {}, {}]. If you don't have event, you will get empty array like [].

List up events if you have, and display "no event" if you don't have. Well, if you want to specify a period of time, not just today, use the [getEvents](https://developers.google.com/apps-script/reference/calendar/calendar#getEvents(Date,Date,Object)) method.

```javascript
function notifyEvents() {
  const calendarId = '{Calendar ID}';
  const calendar = CalendarApp.getCalendarById(calendarId);
  const calendarName = calendar.getName();

  const today = new Date();
  const calendarEvents = calendar.getEventsForDay(today);

  if (calendarEvents.length) {
    for (var event of calendarEvents) {
      var eventTitle = event.getTitle() ? event.getTitle() : '(no title)';
      console.log(eventTitle);
    }
  } else {
    console.log('no event');
  }
}
```

getTitle method doesn't return "(no title)" for no titled events. You need to set it by yourself.

Some events on your calendar are timed and some are not, so if you have a timed event, show it as well. At the same time, you don't want to get sent just the name of the event, so add a thoughtful message as well.

```javascript
function notifyEvents() {
  const calendarId = '{Calendar ID}';
  const calendar = CalendarApp.getCalendarById(calendarId);
  const calendarName = calendar.getName();

  const today = new Date();
  const calendarEvents = calendar.getEventsForDay(today);

  var message = 'Good morning!\n' +
                'Notification from ' + calendarName + 'at' +
                 Utilities.formatDate(today, 'GMT+0900', 'MM/dd') +
                '\n-----------------------------------------';

  if (calendarEvents.length) {
    for (var event of calendarEvents) {
      var eventTitle = event.getTitle() ? event.getTitle() : '(no title)';
      message = message + '\n- ' + eventTitle;
      if (!event.isAllDayEvent()) {
        message = message + ' at '+ 
                  Utilities.formatDate(event.getStartTime(), 'GMT+0900', 'HH:mm') + '~' + 
                  Utilities.formatDate(event.getEndTime(), 'GMT+0900', 'HH:mm') + '(JST)';
      }
    }
  } else {
    message = message + '\nno event';
  }

  console.log(message);
}
```

We have successfully get calendar events now.
You can get more information by the API, check documents like getXX method or isXX.

> [Class CalendarEvent](https://developers.google.com/apps-script/reference/calendar/calendar-event)


## Post to chat

### Google Chat

Issue webhook url on chat room that you want to post calendar events.

![chatWebhook](https://dev-to-uploads.s3.amazonaws.com/i/uw5ary4x23z8o5p0iepf.png)

and paste it to {Webhook URL}.

```javascript
function notifyEvents() {
    ...
  } else {
    message = message + '\nno evnet';
  }

  postToChat(message);
}

function postToChat(message) {
  const chatWebhook = '{Webhook URL}';
  const messageText = { 'text': message };
  const options = {
    'method': 'POST',
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8'
    },
    'payload': JSON.stringify(messageText)
  };
  var result = UrlFetchApp.fetch(chatWebhook, options);
  Logger.log(result);
}
``` 
You will ask to review access, so grant it.

### Chatwork

Issue Chatwork API Token on Chatwork settings and memo chat room ID that you want to post. xxxxxx(number) of #!ridxxxxxx in URL.

Now on GAS editor, from menu > Resources > Libraries, add:

> M6TcEyniCs1xb3sdXFF_FhI-MNonZQ_sT

I set the Latest version, 18.

![chatworkLib](https://dev-to-uploads.s3.amazonaws.com/i/r44ujm4iujdjk1uygvxv.png)

Paste your Chatwork API Token and Room ID.

```javascript
function notifyEvents() {
  ...
  //postToChat(message);
  postToChatwork(message);
}

function postToChat(message) {...}

function postToChatwork(message) {
  const chatworkToken = '{Chatwork API Token}';
  const chatworkRoomId = '{Chatwork Room ID}';
  const chatworkClient = ChatWorkClient.factory({token: chatworkToken});
  chatworkClient.sendMessage({room_id: chatworkRoomId, body: message});
}
```

### LINE Messenger

This time, you can send a message only to yourself, so you can send the message to a fixed ID.

Flow is:

1. on LINE Developers Create "Provider" and "Messaging API Channel"
2. In Messaging API settings, issue "Channel access token (long-lived)"
3. In Messaging API settings, disable "Auto-reply messages" and "Greeting messages" (Cuz we don't need them)
4. In Basic settings, copy "Your user ID".
5. On GAS, publish as Web App and copy URL (menu > Publish > Deploy as web app)
6. On LINE Developers, in Messaging API settings, set URL that you copied at step5 as Webhook
7. On your LINE Smartphone app, scan QR code to become a friend
8. Run GAS

For a detailed explanation of LINE Developers, I'll leave you to look at other articles and so on, but the GAS code will be following. Put in the Token and user ID that you have copied in steps 2 and 4 above.

```javascript
...
function postToLine(message) {
  const lineToken = '{LINE Token}';
  const lineMessagePushUrl = 'https://api.line.me/v2/bot/message/push';
  const lineHeaders = {
    "Content-Type": "application/json; charset=UTF-8",
    "Authorization": "Bearer " + lineToken
  };
  const linePayload = {
    "to" : "{User ID}",
    "messages" : [{
      "type": "text",
      "text": message
    }]
  };
  const options = {
    "method": "POST",
    "headers": lineHeaders,
    "payload": JSON.stringify(linePayload)
  };
  var result = UrlFetchApp.fetch(lineMessagePushUrl, options);
  Logger.log(result);
}
```

## Set Trigger and execute it every morning

Finally, let's set up automatic execution. When you click "Trigger" button next to "Run" button in the menu, you will be redirected to "Trigger Settings" page. Create a new Trigger.

In this case, I want this applet to notify only once every morning. About the mysterious range from 9am to 10am, I tried to run it for a few days and it's 9:46am and every day is different. I don't feel uncomfortable, but in my case, I thought it's good as long as the notification comes before the start of work.

![gasTrigger](https://dev-to-uploads.s3.amazonaws.com/i/pbsq63vb9hcvqwh3qigd.png)
 
That's all, but... I'll get notifications on Saturday and Sunday too, which will ruin my weekend feeling. So I'll add a process to GAS to not send messages on Saturdays and Sundays. Holidays...I can do that, but this time, it's okay.

```javascript
function notifyEvents() {
  const calendarId = 'kura@indigames.net';
  const calendar = CalendarApp.getCalendarById(calendarId);
  const calendarName = calendar.getName();

  const today = new Date();
  const dayOfWeek = today.getDay();
  if (dayOfWeek === 0 || dayOfWeek === 6) return;

  const calendarEvents = calendar.getEventsForDay(today);
  ...
}
...
```

## PS
When I googled "Google Apps Script", there are a lot of articles start from Spreadsheet..., and I had the impression that it is equivalent to the Macro of Excel, and it reminds me like the secret Excel Macro that no one can maintain.

Well, anyways, I think it's good to have the option of using Google Apps Script for small things.

Sample script for this article is here.

> [kurab/notifyEvents](https://github.com/kurab/notifyEvents)