MicrosoftTeamsにGoogleNewsBotを入れてみた

# MicrosoftTeamsにGoogleNewsBotを入れてみた
## ゴールイメージ
<img width="870" alt="スクリーンショット 2019-04-15 13.11.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164563/7d10366b-0b65-62a2-39a1-9c4e4ecf3cf9.png">

## 動機
* 会社の公認コミュニケーションツールがSlackじゃなくてTeamsになりそう
* Teamsのwebhookを使ってみたい
* [お友達](https://twitter.com/belltree_gon)の作ったGoogleNewsBot for SlackをTeamsに改造してみよう

## 実行環境とフロー
* AWS
    * CloudWatch　ルールでLambda(Node.js)を定周期起動
    * GoogleNewsRSSを利用して、指定したキーワドのNewsを取得して、Teamsに投げ込む
    
## Teams側の設定
* Newsを表示したいチャンネルにIncoming Webhookコネクタを作成する必要がある。
* 詳しい解説記事が既にQiitaにあるので[こちら](https://qiita.com/miyamiya/items/ccae505b8e97b5b532a7)参照して作成してください（`チャネルの画面に戻ると 3 で付けた名前とアイコンの何者かが参加しています`まで実行すれば大丈夫）

## ローカルで試してみよう
### npmインストール
* request-promise
* request
* rss-parser
* さくっとやりたいので`serverless`は使わない

```
$ mkdir newsbot-teams
$ cd newsbot-teams
$ npm install request-promise request rss-parser
```

### ソース
* 下記構成でそれぞれ作成してください

#### tree
```
.
├── index.js
├── google-news-service.js
├── main.js
└── package.json
```

#### index.js
```javascript:index.js
'use strict';

const rp = require('request-promise');
const lodash = require('lodash');
const GoogleNewsService = require('./google-news-service.js');

function dateTimeFilter(articles, fromDate) {
    return articles.filter((article) => {
      const pubDate = new Date(article.pubDate);
      return (pubDate.getTime() - fromDate.getTime()) > 1;  
    });
  }

function post2Slack(teams_webhook,message) {
  return rp.post(teams_webhook, {
    headers: { "Content-type": "application/json" },
    json: { text: message } ,
    uri : teams_webhook
  });
}

async function main(fromDate,teams_webhook,keywords) {
  try {
    const googleNews = await (new GoogleNewsService()).exec(keywords);
    const news = lodash.uniqBy(dateTimeFilter(lodash.concat([], googleNews), fromDate), 'link');
    const titleList = news.map((article) => {
      return `・[${ article.title }](${ article.link })<br>`;
    }).join('');
    const message = 'ニュースとってきた。<br>' + titleList;
    await post2Slack(teams_webhook,message);
  } catch(error) {
    console.error(error);
  }
}

exports.handler = (event, context, callback) => {
    const fromDate = new Date();
    const teams_webhook = event['teams_webhook'];
    const keywords = event['keywords'];
    fromDate.setHours(fromDate.getHours() - event['fromHour']);
    main(fromDate,teams_webhook,keywords);
};

```

#### google-news-service.js
```javascript:google-news-service.js
const Parser = require('rss-parser');
module.exports = class GoogleNewsService {
  constructor(){
  }
  createGoogleNewsUrl(keyword) {
    return 'https://news.google.com/_/rss/search?q='
      + encodeURIComponent(keyword)
      + '&hl=ja&gl=JP&ceid=JP:ja';
  }
  async exec(keywords) {
    const splitkeywords = keywords.split(',');
    const parser = new Parser();
    const feeds = await Promise.all(splitkeywords.map((keyword) => {
      return parser.parseURL(this.createGoogleNewsUrl(keyword));
    }));
    const articles = [];
    feeds.forEach((feed) => {
      feed.items.forEach((item) => {
        articles.push(item);
      });
    });
    return articles;
  }
}
```

#### main.js
* ローカルでlambdaっぽく呼ぶやつ

```javascript:main.js
var event = {
    version: '0',
    id: 'XXXXXXXX-XXXXX-XXXX-XXXXX-XXXXXXXX',
    'detail-type': 'Scheduled Event',
    source: 'aws.events',
    account: 'XXXXXXXXXX',
    time: '2019-01-30T06:27:04Z',
    region: 'ap-northeast-1',
    resources:
        [ 'arn:aws:events:ap-northeast-1:XXXXXXXXXX:rule/box-auditlog-exec' ],
    detail: {},
    fromHour : '10',
    teams_webhook : 'https://outlook.office.com/webhook/XXXXXXXXXXXXXXXXXXXX/IncomingWebhook/XXXXXXXXXXX/XXXXXXXXXXXXXX',
    keywords : 'AWS,GCP,Azure,CentOS,IDaaS,okta,onelogin,ゼロトラスト,zero trust,Nginx,Microsoft,Windows Server,脆弱性,ゼロデイ'
};

var context = {
    invokeid: 'invokeid',
    done: function(err,message){
        return;
    }
};

var lambda = require("./index.js");
lambda.handler(event,context);
```
##### 適宜変更してください
* fromHour : 過去何時間分のニュースを取得するか設定
* teams_webhook : Teams webhooksのurl.
* keywords : 取得したいNewsキーワード

### ローカルで実行
```
$ node main.js
```
指定したTeamsにニュースがポストされたら成功
<img width="870" alt="スクリーンショット 2019-04-15 13.11.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164563/d66eac5d-046e-f2eb-eb87-e8e3943f90f3.png">

## AWSにアップして動かしてみよう
### 事前準備
* lambda関数作成
    * ランタイム:Node.js V8.10
    * タイムアウト:3分
   
    <img width="564" alt="qiita3.png" src="https://qiita-image-store.s3.amazonaws.com/0/164563/6d3a57cf-080f-6d81-904b-32e9c0c078fe.png">

### AWSへデプロイ
* 今回はS3を経由しているが、S3を経由する必要は特に無い

```
$ zip -r newsbot-teams.zip index.js node_modules
$ aws s3 cp ./newsbot-teams.zip s3://[mybucket]/newsbot-teams.zip --profile [myprofile]
$ aws lambda update-function-code --function-name newsbot-teams --s3-bucket [mybucket] --s3-key newsbot-teams.zip --publish --profile [myprofile]
```

### 実行
* CloudWatch　ルールに、lambdaを指定して引数を設定して起動する。
    * 周期はお好きなように `GMTだから気をつけて`
    * 引数サンプル：`入力の設定`-`定数(JSONテキスト)`
    
````$xslt
{"fromHour": "15", "teams_webhook": "https://outlook.office.com/webhook/xxxxxxx/IncomingWebhook/xxxxxxxxxxx/xxxxxxxx", "keywords": "AWS,GCP,Azure,CentOS,IDaaS,okta,onelogin,ゼロトラスト,zero trust,Nginx,Microsoft,Windows Server,脆弱性,ゼロデイ"}
````
<img width="1107" alt="スクリーンショット_2019-04-15_13_30_17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164563/2b4605cf-01b4-7009-7210-1ce3c630ae97.png">

部単位のチャンネルトとかに、部で共有したいNewsキーワードを設定して毎日１回とか数回ポストすると良いと思う。日常業務に忙殺されて、情報をキャッチアップできてない人多いし。

## ソース置き場
[こちら](https://github.com/kenji-toforone/newsbot-teams)にソース置いておきます。（ソース汚かったらPullReqください）