GAS で席替えをするLINEBotを作成した

## 成果物
LINE上で「席替え！」とチャットすると、席替え結果を返すBot。
席替えは毎日実施される。
アイコンは拾い物。
Thanks!(https://iconbu.com/illust/4427)
![席替え成果.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1073381/1cb2fc88-8320-0b76-ba4e-08de37c2085d.jpeg)

## 背景
会社の研修中、交流のため「毎回同じところに座らないように。」とのことで作成。
### もう少し詳しく
初めは、ＬＩＮＥのあみだくじで行っていたが、あみだくじの作成が面倒に。
LINEBOTのが楽？！と思いつき、作成。
事前に同期に相談していなかったが、スムーズに導入してもらえる。

## 作成時間
前日の晩に、「（LINEのあみだくじが）面倒くさい」と聞き、翌朝作成。
作業時間。1時間強。（褒めたい）

以前、飲食店向けのLINEBotを作成していたこともあり、早くできた。
GASはお仕事で少しやっていたので問題なし。




## シート
| シート名 | 内容 |
|:-:|:-:|
| name  | 1行目、席替えしたい人の名前。  |
| recode  | 席替えの履歴。A列、日付。B~列、席替え結果。  |
| log  | エラーの記録。  |

この3つのシートを作ればOK。

## やり方
後日追記
1．スプレッドシートを準備する。
2．名前を記入する。
3．ソースコードをコピペする。
<dd>スプレッドシートから「拡張機能」>「Apps Script」>コピペ。>Ctrl + s で保存。</dd>
4．「create」をトリガーで「毎日」「9時」に実行する。
<dd>左側のバーから「トリガー」>「トリガーを追加」</dd>

※このままでは動きません。LINE、トークンについてはまた。

## ソースコード

### 全体
| 関数名 | 説明 |
|:-:|:-:|
| create  | スプレッドシートから名前を取得して席替え。プロパティに保存。トリガーで毎朝9時に実施する。  |
|  getMessages | プロパティの席替えリストをLINEメッセージに変換し返却。  |
| doPost  | LINEからメッセージを受信すると起動。「席替え！」を受信すると席替え内容を返す。  | 

### 工夫
BOT動作時に、シートから直接取得すると時間がかかるので、propertyServiceを利用して時短。


```javascript: コード.js
/**
 * @description スプレッドシートから名前を取得して席替え。プロパティに保存。トリガーで毎朝9時に実施する。
 */
function create() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const [names] = ss.getSheetByName('name').getDataRange().getValues();
  /**
   * @description 席替え参加者リスト
   * @param {string[]}
   */
  console.log(names)
  //配列をランダムに入れ替える
  for (let i = names.length - 1; i > 0; i--) {
    let r = Math.floor(Math.random() * (i + 1));
    let tmp = names[i]
    names[i] = names[r]
    names[r] = tmp
  }
  console.log(names)
  const d = new Date()
  d.setDate(d.getDate() + 1)
  console.log(d)

  //明日の日付とともにシートに保存する。
  const date = Utilities.formatDate(d, Session.getScriptTimeZone(), "yyyy/MM/dd")
  const values = [[date, ...names]]
  const sh = ss.getSheetByName('recode')
  const lr = sh.getLastRow();//スプレッドシートの最終行を取得
  sh.getRange(lr + 1, 1, values.length, values[0].length).setValues(values);//貼付

  //データをプロパティに保存する
  const json = JSON.stringify(values);
  PropertiesService.getScriptProperties().setProperty('seki', json);
}
/**
 * @description プロパティの席替えリストをLINEメッセージに変換し返却。
 */
function getMessages() {
  //プロパティから名前のランダムリスト+日付を取得
  const json = PropertiesService.getScriptProperties().getProperty('seki');
  const [values] = JSON.parse(json);
  const v = values.reduce((a, x, i) => {
    if(i ===0) return [...a, x]
    const nn = i+". " +x
    return[...a, nn]
  }, [])
  //ラインに投下される形
  const text = v.join('\n');
  const messages = [
    {
      type: "text",
      text: "せきがえんぬ~"
    },
    {
      type: "text",
      text,
    }]
  console.log(messages)
  return messages;
}
/**
 * @description LINEからメッセージを受信すると起動。「席替え！」を受信すると席替え内容を返す。
 */
function doPost(e) {
  try {
    const event = JSON.parse(e.postData.contents).events[0];
    console.log(event)
    if(event["type"] !== 'message' || event["message"]["type"] !== "text"){
      return;
    }
    const sendedText = event["message"]["text"]; //送信テキスト
    console.log(sendedText)
    const t = ["席替え！", "せきがえ！"]
    if(!t.includes(sendedText)) {
      return;
    }
    const url = 'https://api.line.me/v2/bot/message/reply';
    const ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty('token');
    if (!ACCESS_TOKEN) {
      console.error('no token')
      return;
    }
    const messages = getMessages();
    const replyToken = event["replyToken"];
    const options = {
      "headers": {
        "Content-Type": "application/json; charset=UTF-8",
        "Authorization": 'Bearer ' + ACCESS_TOKEN
      },
      "method": "post",
      "payload": JSON.stringify({ replyToken, messages }),
      muteHttpExceptions: true
    };
    var response = UrlFetchApp.fetch(url, options);
    if (response.getResponseCode !== 200) {
      console.error("error!")
      return
    }
  } catch (e) {
    console.log(e)
    const s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('log')
    const lr = s.getLastRow()
    s.getRange(lr + 1, 1).setValue(JSON.stringify(e))
  }
  return ContentService
    .createTextOutput(JSON.stringify({ 'content': 'post ok' }))
    .setMimeType(ContentService.MimeType.JSON);
}


```

## 感想
名前順をシャッフルしてくれるので、席替え以外の用途にも使ってもらえました。
嬉しき。

研修中のインフラとして当然のように使われる存在になったので、作ってよかったです。

### 改善
席を見やすくすることも考えたりしましたが、研修を乗り切ったのでひとまず終了。


## 参考
https://zenn.dev/yoheimuta/articles/89e9b85e01fc4f