BOTを作るときに用意しておくと（個人的に）便利なメソッド集

たとえば [Google Apps Script](https://developers.google.com/apps-script/) でBOTを作るときなど、ゼロから構築すると大変なのであらかじめテンプレート化しておくとスムーズに開発が始められる。

ここでは、私が自分の備忘録も兼ねて、ふだんテンプレートに含めているメソッドを紹介する。ただし、私はフロントエンドエンジニアで、BOTは上記のGASを使って開発するので、言語はJavaScript (ES5) を用いる。

## `@channel` や `@here` する

ときどき「あれ、どうやって書くんだっけ」と忘れてしまうので、最初からメソッド化しておく。（メソッドではなく定数でもよいだろう。）

```javascript
/**
 * チャンネル全体にメンションする
 * @params ()
 * return string
 **/
function channelMention() {
  return `<!channel>`;
}
```

## メンションする

上記のchannel / hereなどをさらにメソッド化したもの。

```javascript
/**
 * ユーザにメンションする
 * @params (userName: string)
 * return string
 **/
function mention(userName) {
  if (!userName) return;
  
  return '<!' + userName + '>';
}
```

## 任意の配列からランダムにひとつを選択して返す

これを用意しておくと、語尾や挨拶などを複数用意していかにも人間がしゃべっているように自然に見せかけることなどに役立つ。

```javascript
/**
 * 与えられた配列からランダムにひとつ返すメソッド
 * @param (array: Array<string | number>)
 * return string | number;
 **/
function getRandom(array) {
  if (!array) return;

  return array[Math.floor(Math.random() * array.length)];
}
```

上記のようなメソッドがひとつあれば、 `getRandom(['私','わたし','ワタシ']) + 'はBOT' + getRandom(['です','デス']) + getRandom(['よ','ヨ',''])` のように、よりバリエーションに富んだメッセージングを実装できる。

## 任意の配列の配列を組み合わせてテキストを返す（上記の応用）

上記の実装が終わったら、さらに文字列生成を簡素化するために以下のようなメソッドが用意できる。

```javascript
/**
 * 与えられた配列の配列を組み合わせて文章を返すメソッド
 * @param (arrayArray: Array<Array<string | number>>)
 * return string;
 **/
function createRandomText(arrayArray) {
  if (!arrayArray) return;

  var message = '';

  for (var i = 0; i < arrayArray.length; i++) {
    message += getRandom(arrayArray[i]);
  }

  return message;
}
```

これで、上記のサンプルは `createRandomText(['私','わたし','ワタシ'], ['はBOT'], ['です','デス'], ['よ','ヨ',''])` とよりシンプルに書けるようになった。

## 配列の特定のキーでのソート

これはBOTに限った話ではないが、配列をソートする用意をしておけば実装がスムーズになるのは言うまでもない。

```javascript
/**
 * 配列の特定のキーでのソート
 * @params (array: Array<Object>, key: string, order: 'ASC' | 'DESC')
 * return Array<Object>
 **/
function sortByKey(array, key, order) {
  if (!array) return;
  if (!key) return array;

  var isDescending = order === 'DESC';
  
  array.sort(function(a, b) {
    if (a[key] > b[key]) return (isDescending ? 1 : -1);
    if (a[key] < b[key]) return (isDescending ? -1 : 1);
    return 0;
  }
}
```

ただし、当該キーの値がnumberではなくstringの場合、ひらがなやカタカナ・漢字のソートも考慮しなければならないためより複雑になるが、それが必要な場合は [JS：配列の正しいソート方法](https://goma.pw/article/2015-11-18-0/) などを参考にしてもっと複雑な判定をしなければいけない。

## 土日祝祭日かどうかを判定する

祝日にBOTが一人で稼働しているのは虚しいものだ。今日が土日祝祭日かどうかを判定することを容易にしておけば、if文をかませるだけで処理を中止することができる。以下は冗長な例だが、Node.jsが使える環境であれば [japanese-holidays](https://www.npmjs.com/package/japanese-holidays) のようなライブラリを用いればメンテナンスもしなくていいし楽チンである。

```javascript
/**
 * 今日が土日祝祭日かどうかを判定する
 * @params ();
 * return boolean;
 **/
function isHoliday() {
  var today = new Date();
  var now = new Date(today.getFullYear(), today.getMonth(), today.getDate());

  var holidays = [
    new Date(2017, 1 - 1, 1),
    new Date(2017, 1 - 1, 9),
    new Date(2017, 2 - 1, 11),
    new Date(2017, 3 - 1, 20),
    new Date(2017, 4 - 1, 29),
    new Date(2017, 5 - 1, 3),
    new Date(2017, 5 - 1, 4),
    new Date(2017, 5 - 1, 5),
    new Date(2017, 7 - 1, 17),
    new Date(2017, 8 - 1, 11),
    new Date(2017, 9 - 1, 18),
    new Date(2017, 9 - 1, 23),
    new Date(2017, 10 - 1, 9),
    new Date(2017, 11 - 1, 3),
    new Date(2017, 11 - 1, 23),
    new Date(2017, 12 - 1, 23),
    new Date(2018, 1 - 1, 1),
    new Date(2018, 1 - 1, 8),
    new Date(2018, 2 - 1, 11),
    new Date(2018, 3 - 1, 21),
    new Date(2018, 4 - 1, 29),
    new Date(2018, 5 - 1, 3),
    new Date(2018, 5 - 1, 4),
    new Date(2018, 5 - 1, 5),
    new Date(2018, 7 - 1, 16),
    new Date(2018, 8 - 1, 11),
    new Date(2018, 9 - 1, 17),
    new Date(2018, 9 - 1, 23),
    new Date(2018, 10 - 1, 8),
    new Date(2018, 11 - 1, 3),
    new Date(2018, 11 - 1, 23),
    new Date(2018, 12 - 1, 23)
  ];

  return now.getDay() === 0 || now.getDay() === 6 || holidays.indexOf(now) >= 0;
}
```

> 20171216追記:
> GASで日本の祝日を取得するもっとも簡単な方法が、Google公式にあった。すなわち、Googleカレンダーの祝日データを取得してしまえばいいのだ。以下、[GoogleAppsScriptで日本の祝日を取得する
](https://qiita.com/kamatama_41/items/be40e05524530920a9d9)を参考にした。

```javascript
/**
 * 今日が土日祝祭日かどうかを判定する
 **/
function isHoliday(year, month, day) {
  var startDate = new Date();
  startDate.setFullYear(year || new Date().getFullYear(), month ? month - 1 : new Date().getMonth(), day || new Date().getDate());
  startDate.setHours(0, 0, 0, 0);

  var endDate = new Date();
  endDate.setFullYear(year || new Date().getFullYear(), month ? month - 1 : new Date().getMonth(), day || new Date().getDate());
  endDate.setHours(23, 59, 59, 999);

  var cal = CalendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com');
  var holidays = cal.getEvents(startDate, endDate);

  return startDate.getDay() === 0 || startDate.getDay() === 6 || holidays.length > 0;
}
```

## 時間帯を判定する

BOTの挨拶は基本だが、夜中に「こんにちは」なんて言っていたら情けない。いつでも分岐できるようにあらかじめルールを決めておき、判定できるようにしておくと効率がよい。

応用すれば、季節ごとに適切なメッセージで応答したりなど、できることの幅が拡がるだろう。

```javascript
/**
 * 時間帯を判定し、時間帯キーを返す
 * @params()
 * return string;
 **/
function getTimePeriod() {
  var now = new Date();
  var timePeriodKey;

  if (now.getHours() >= 5 && now.getHours() < 11) {
    timePeriodKey = 'MORNING';
  } else if (now.getHours() >= 12 && now.getHours() < 17) {
    timePeriodKey = 'EVENING';
  } else if (now.getHours() >= 17 && now.getHours() < 22) {
    timePeriodKey = 'NIGHT';
  } else {
    timePeriodKey = 'MIDNIGHT';
  }
  
  return timePeriodKey;
}
```

## スプレッドシートのデータを配列で取得する

スプレッドシートとのデータ連携が必要になったときに、ゼロから準備していたら大変だ。あらかじめFileIdとSheetNameを渡せば配列で返してもらえるようにしておけば、すぐに実装が始められる。

以下は、Google Apps Scriptでの例である。詳しくは、 [Google SpreadSheet のデータを JSON 形式で取得する Web API をサクッと作る](https://qiita.com/takatama/items/7aa1097aac453fff1d53) も参照されたい。

```javascript
/**
 * スプレッドシートのデータを配列として取得する
 * @params(fileId: string, sheetName: string)
 * return Array<Object>
 **/
function getSpreadSheet(fileId, sheetName) {
  var spreadSheet = SpreadsheetApp.openById(fileId).getSheetByName(sheetName);
  var rows = spreadSheet.getDataRange().getValues();
  var keys = rows.splice(0, 1)[0];

  return rows.map(function(row) {
    var obj = {}
    row.map(function(item, index) {
      obj[keys[index]] = item;
    });
    return obj;
  });
}
```

あとは、随時更新していきたいと思う。