Slack上で投稿された内容を集計

# 今回やりたかったこと
slack上で下記を実現したい
1. スラッシュコマンドでlambda呼び出し
1. 呼び出されたチャンネルで全ての投稿を取得
1. 投稿された内容をエクセルで出力し、コマンド実行ユーザに送付

※この後別でやりたいこともあるので、まだ作成中。。できたら追記

# slack側での設定
設定詳細については省略。
特殊なことは実施しておらず、アプリケーションの作成とスコープの設定のみ実施。
スコープについては下記を設定した。
- channels:history
- chat:write
- commands
- files:write
- im:write
- users:read
# lambdaの設定
関数URLを設定。slack側のスラッシュコマンドで呼び出すURLに設定した。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/urls-configuration.html

layerにはexcelJsとslackのAPIをzip化したもの登録した。

また、スラッシュコマンドで呼び出したものについては、3秒以内にレスポンスを返さないとタイムアウトする。

https://api.slack.com/interactivity/slash-commands#responding_to_commands

一旦、何かしらの返却値を返せばよいとのことなので、lambdaの処理先頭でcallbackを呼び出し、その後、引き続き処理を実施するようにした。
```JavaScript
callback(null,202);
```
# 投稿の取得
投稿自体はconversations.historyから取得。
引数でlatestとoldestで投稿の取得対象期間を指定。
```JavaScript
    let params = {
      channel: process.env.DAIRY_REPORT_CHANNEL_ID,
      limit: 200, //公式の推奨値100-200,
      latest: endDate.getTime() / 1000,
      oldest: startDate.getTime() / 1000
    };
    let response = await slack.conversations.history(params);
```
取得自体は1回の呼び出しで全ての投稿を取得できるわけではなく、limitに指定された値か、デフォルトの100研磨で取得される。
期間内の投稿は全て取得したかったので下記で全てが取得されているかを判断。まだ取得できていない残りがある場合、呼び出した際の戻り値に存在するカーソルを使用して、引数に指定して、引き続き同メソッド呼び出し。
```JavaScript
let hasMoreData = response.has_more;
if(hasMoreData){
      cursor = response.response_metadata.next_cursor;
}
```

「○○さんがチャンネルに参加しました」、botが投稿したメッセージなども取得されてくるので、通常のユーザによる投稿だけにしぼる。

```JavaScript   
    let messages = response.messages
    messages = messages.filter(function(message){
      return message.type == 'message' && !('subtype' in message);
    });
    result = result.concat(messages);
```
# 返信の取得
conversations.historyでは返信内容が含まれていない。
そのため、取得した投稿のタイムスタンプをもとに各投稿に対して、スレッドで返信がついているものは、返信も取得した。
```JavaScript  
    let response = await slack.conversations.replies({
      channel: process.env.DAIRY_REPORT_CHANNEL_ID,
      token: process.env.SLAC_API_TOKEN,
      ts: messageTimestamp
    });
```
取得した結果には、投稿元のメッセージも含まれる。あくまでも投稿への返信内容だけ取得したかったの絞り込み
```JavaScript 
    let allReplies = response.messages
    result = allReplies.filter(function(reply){
      //投稿元も含まれるので、返信のみを取得
      return reply.ts != reply.thread_ts;
    });
```
# リアクションの取得
投稿自体へのリアクションはconversations.historyで取得されたメッセージのreactionsが取得できる
```JavaScript 
    let reactions = [];
    for(let message of messages){
      if('reactions' in message){
        let temp = await getReactions(client,message);
        reactions = reactions.concat(temp);
      }
    }
```
取得されるリアクションの情報は下記のような感じだった
```
    { name: 'heart', users: ['xxx','yyy'], count: 2 },
    { name: 'slightly_smiling_face', users: ['xxx','yyy'], count: 2 }

```
# エクセルへの出力
excelJSを用いてエクセルを作成。
投稿、返信、リアクションでシート分けして、出力するようにした。
下記は投稿についてのみの出力時の内容

```JavaScript
let excelBook = new exceljs.Workbook();
  excelBook.addWorksheet(SHEET_NAME_POST);
  let postSheet = excelBook.getWorksheet(SHEET_NAME_POST);
  // 列を定義
  postSheet.columns = [
    { header: '日付', key: 'date' },
    { header: '名前', key: 'name' },
    { header: '投稿内容', key: 'text' },
  ];
  
  for(let post of posts){
    // 行を定義
    postSheet.addRow({
      'date': new Date(post.ts * 1000).toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' }),
      name: getUserName(users,post.user),
      text: post.text
    });
  }

```

# エクセルのユーザへの送信
slack上のDMで作成したエクセルファイルを送信するようにした。
DMのチャンネルIDを取得して、あとはfiles.uploadV2で送信。

```JavaScript
  //DMチャンネルのオープン
  let openResult = await slack.conversations.open({
    users:userId
  });
  let dmChanelId = openResult.channel.id
  //投稿
  let result = await slack.files.uploadV2({
    'file': file,  
    filename: createFileName(startDate,endDate),
    channel_id: dmChanelId,
    initial_comment: '集計結果を添付します',
  });
```
fileの中身自体は下記で用意
```JavaScript
let uint8Array = await excelBook.xlsx.writeBuffer();
```