【GAS】Qiitaオーガナイゼーション内の新着記事を社内チャットに通知する〜その2


前回に引き続き、
社内の誰かが記事を投稿したら、
社内チャットに通知が送るアプリケーションを作ります。

※本記事は下記の記事の続きとなります

https://qiita.com/YoTAIMA/items/d5e25214c9a2a6b43c4f


## 作成するもの

1. オーガナイゼーションに登録されているユーザーの投稿を検知する
1. 検知した投稿を社内チャットツールに通知する（本記事）


## 下調べ

今回、前回の記事で取得した新着記事一覧を、
社内で使用しているChatWorkへの通知する処理を書きたいと思います。

ちらっと調べたところ下記の記事が見つかりました。

https://creators-note.chatwork.com/entry/gas-reservation-post

どうやら、GAS用のライブラリがあるようで、
それを使うと簡単に実装することができそうです。

## 実装

今回は上記の記事にサンプルコードもあるので、早速実装に移ります。

実装する内容は、

1. スプレッドシートから、未通知の投稿を習得する
1. 1件ずつ取り出し、通知済みの状態にし、チャットを送信する

となります。

おまけですが、動作確認用にスクリプトプロパティで
チャット送付の可否を切り替えるというのもやってみます。

### 準備

まずは、ChatWorkのライブラリを登録します。
GAS編集画面左にあるライブラリの「+」をクリックし、
スクリプトID　　に以下のIDを入力して「検索」をクリックし、表示内容を確認して「追加」をクリックすれば完了です。

```
スクリプトID:
1nf253qsOnZ-RcdcFu1Y2v4pGwTuuDxN5EbuvKEZprBWg764tjwA5fLav
```
※スクリプトIDは公式をご参照ください

あとは、ChatWorkのAPIキーと、投稿先のRoomIDが必要となります。


### スプレッドシートの操作

つづいて、スプレッドシートの操作をします。
まず、スプレッドシートは下記のようになっています。

![スクリーンショット 2022-06-19 午後21.02.00 午後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1215071/5b64803b-b6d8-9474-a952-5a075e8bf2c6.png)

通知済みが「0：未通知」「1:通知済み」となるので、
「0」の行を取得し、「1」に変更するということを実装します。


```javascript:PostChat
function postArticleToChat() {
  // プロパティを取得
  const prop = PropertiesService.getScriptProperties();
  const spreadSheet = SpreadsheetApp.openById(prop.getProperty("SHEET_ID"));
  const articleSheet = spreadSheet.getSheetByName(prop.getProperty("SHEET_NAME_ARTICLE"));

  /**
   * 対象記事一覧を取得する
   */
  const fetchNewArticle = function(){
    return articleSheet.getDataRange().getValues()
    .map((row,index) => ([...row, index + 1]))
    .filter(row => row[6] == 0)
    .map(row => ({no: row[0], title: row[1], url:row[2], author:row[3], created:row[4], rowNum:row[6]}));
  }

  /**
   * 記事一覧を通知済みに変更する
   */
  const markSendArticle = function(article){
    articleSheet.getRange("F" + article.rowNum).setValue(1);
  }

  const sendArticleToChat = function(client, article){
    markSendArticle(article);
    // ここに送信処理を書く

  }

  // 対象記事一覧を取得する
  newArticles = fetchNewArticle();
  if(newArticles.length　==　0){
    // 対象が0件のときは通知しない
    Logger.log("通知対象:0件");
    return;
  }

  // チャットで通知する
  const client = "");
  newArticles.forEach(article => sendArticleToChat(client, article));
}
```

このあたりは前記事で触ったのとほとんど同じなので、慣れたものですね。
~~相変わらずコードが中途半端ですが~~

一応実行すると、下記のとおりに。

![スクリーンショット 2022-06-20 9.18.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1215071/a35591aa-522f-b9b0-5aee-5530c79816ee.png)

通知済みのセルが1となっていることがわかります。

### チャットの送信処理

つづいて、チャットの送信処理です。
こちらはChatWorkの記事を参考に実装していきます。

※トークンなど、必要な情報はスクリプトプロパティに事前に登録した上で使用します。

1. tokenからchatWorkClientを生成
1. sendMessageに送信先のroom_idとメッセージ内容を渡す

で実装できそうです。


```javascript:PostChat
function postArticleToChat() {
  // プロパティを取得
  const prop = PropertiesService.getScriptProperties();
  const spreadSheet = SpreadsheetApp.openById(prop.getProperty("SHEET_ID"));
  const articleSheet = spreadSheet.getSheetByName(prop.getProperty("SHEET_NAME_ARTICLE"));

  /**
   * 対象記事一覧を取得する
   */
  const fetchNewArticle = function(){
    return articleSheet.getDataRange().getValues()
    .map((row,index) => ([...row, index + 1]))
    .filter(row => row[6] == 0)
    .map(row => ({no: row[0], title: row[1], url:row[2], author:row[3], created:row[4], rowNum:row[6]}));
  }

  /**
   * 記事一覧を通知済みに変更する
   */
  const markSendArticle = function(article){
    articleSheet.getRange("F" + article.rowNum).setValue(1);
  }

  /**
   * Chat送信用のクライアントを取得する
   */
  const createChatClient = function(){
    if(prop.getProperty("ENABLE_SEND_CHAT") == 1){
      const token = prop.getProperty("CHATWORK_TOKEN");
      return ChatWorkClient.factory({token: token});
    }else{
      // チャット通知が有効でない場合用のモッククライアントを返却
      const mockClient = class{
      // チャットには送らず、ログ出力するだけ
        sendMessage({room_id, body}){ Logger.log(body);}
      };
      return new mockClient();
    }
  }

  const sendArticleToChat = function(client, article){
    markSendArticle(article);

    const message =  `
お疲れさまです。
Qiitaに新規記事が投稿されました。

タイトル：${article.title}
URL：${article.url}

※本メッセージは自動投稿です。`;
    const room_id = prop.getProperty("CHATWORK_ROOM_ID");
    // チャットに送信
    client.sendMessage({
          room_id: room_id,
          body: message
        });

  }

  // 対象記事一覧を取得する
  newArticles = fetchNewArticle();
  if(newArticles.length　==　0){
    // 対象が0件のときは通知しない
    Logger.log("通知対象:0件");
    return;
  }

  // チャットで通知する
  const client = createChatClient();
  newArticles.forEach(article => sendArticleToChat(client, article));
}

```

諸事情（~~APIトークンを準備出来ていなかった~~）ため、
スクリプトプロパティでチャットに送るか否かを制御し、送らない場合にはログに出力するようにしました。
（createChatClient()でChatWorkのクライアントを作成するときに、
スクリプトプロパティにセットした、ENABLE_SEND_CHATが1でない場合には、
簡易的にモックのクライアントを返すようにしています。
モックのクライアントは、メッセージ送信処理では、チャットの本文をログに出力します。）


以上で、実装終了なはず・・・・です。
後は実際に動かして確かめてみましょう。

![スクリーンショット 2022-06-20 11.17.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1215071/8142a959-e4d0-b3ad-a996-41bce1a0d7c6.png)


無事、通知ができました。めでたしめでたし。

## トリガーのセット

あとは、GASのトリガーでタイマーをセットすれば自動通知の処理が完了です。
（このあたりも、ChatWorkさんの記事にかかれていますね）

GASエディタの左部、「トリガー」から、「トリガーを追加」をクリックします。
今回は毎日起動したいので、また、厳密な時刻でのコントロールは不要なので、
下記の通り設定します。

- イベントのソース： 時間主導型
- 時間ベースのトリガータイプ: 日付ベースのタイマー
- 時刻を選択: 記事取得：8-9時、 通知:10-11時

これで準備は完了です。
あとは待っていれば、社内の投稿を、勝手に検知して投稿くれる・・・・はず！

## まとめ

２回に分けて投稿となりましたが、以上で無事、実装することができました。
GASを使ったり、公開されているライブラリやAPIを活用すると、
本当に簡単に実装することができますね。

もちろん、本来であれば、例外処理などもう少し工夫して書くべきなのですが、
少し触っただけでも、これくらいのアプリなら実装できる、というのは非常にありがたいを思いました。

各々現場に出ていると、今どういうことに興味あるの？というのが中々見えにくくなってしまいますが、
少しでも、社内のメンバーに興味を持つ切っ掛けになれば嬉しいですね。