【 Qiita x Slack 】社内メンバーがQiitaに投稿をすると、Slackに通知を送る仕組みを作ってみた！

#はじめに
同じチームで働いている__「メンバーのQiita記事 - 更新通知」__が欲しいなー。 _と、
ふと思ったのがこの記事を書こうと考えたキッカケ！早速作ってみる。_

_通勤電車の中でひらめいた！:bulb:_

__[ ※注意次項 ]__

昨年末にPrivate Itemとして記事を書いていて、
最後まで書ききっていなかったのを、今ふと思い出したので、
修正も加えながら公開したいと思う。ので、若干内容が古い可能性あり。


##というか、もうあるよね？そういう記事(笑)

<font color="red">__絶対誰かやってるじゃん！__</font>_と思いながら、
電車の中でおそるおそる_ 「Qiita」 _で調べてみると、、。_

##「Qiita更新通知」で検索してみた！

__「 Qiita更新通知 」__で、Qiitaってみた！
_　→ ググってみた的なやつ_

>_・実際に撮ったスクリーンショット_

>![image.png](https://qiita-image-store.s3.amazonaws.com/0/180432/29be61cb-38bc-2ecc-2825-9011c7849e39.png)

>__Title : [Qiitaの更新をslackに通知したい](http://qiita.com/forest1/items/06f542f15c8b3ce5785e)__

うわー、もうあるじゃん！

__「やっぱみんな考えること同じだよなー！」__

と、思いながら記事内容を確認してみると、

###<font color="teal">__[ 以下、記事全文抜粋 ]__</font>

>（調べ中）

>とりあえず、
https://dotjun.slack.com/apps
このページの右上のmanageから入る
Qiita:Teamじゃないと無理っぽい感じ


##【 結論 】Qiita:Teamでないと無理っぽい！

どうやら __<font color="red">「Qiita:Team」</font>__ じゃないと無理っぽい。
と、諦めるのならこの記事を書く意味はありません。

####<font color="black">" 創造性は制約を好む "</font>


_いや、、。やったこともないし、
全然できるか分からないけどw_

_とりあえずなんかプラグラムを組めばできるでしょという
軽いノリでできるかどうか分からないことにチャレンジしてみる!!_

#### <font color="red">__Qiita個人ページのRSS(Rich Site Summary)を取得__</font>したらイケるんじゃね？

_という訳で、早速、試しにやってみる。_

#1.「Qiita」個人ページのRSSを取得してみる！

さっそく、__Qiita個人ページのRSS(フィード)を取得__してみる。

_そもそもRSSってどうやって取得するんだ？笑_

>_Webページの```<head>```タグ内に、下記のタグ(```rss.html```)が記載されていれば、RSSフィードが存在する。
上記のタグがなければ、RSSフィードが存在しない。_

>__Title : [WebページからRSSのURLを取得する。](http://qiita.com/masaki_iwahara/items/ed99c34bac5d3cd7e2cd)__


```rss.html
<link rel="alternate" type="application/rss+xml" title="RSSのタイトル" href="RSSフィードのIRL"> 
```

さっそく、Qiita個人ページを開いて、
ソースコード内に「```rss+xml```」があるかどうか検索してみる。

_な、、ないじゃん。
<font color="red">RSSフィード</font>見つからないじゃんｗ_

Qiitaには、RSSフィード存在しないのか。。。

## 「Qiita」個人ページには、RSSフィードが存在しない？

_まぁここまで記事書いてるし、ここでやめるってのもあれなので、
RSSフィードについてもう少し調べてみた。_


RSSにはフォーマットが複数存在するので、そのフォーマットによって追加すべきタグ内容が変わってくるようです。

- フォーマットが__「RSS2.0」__の場合  

```rss.html
<link rel="alternate" type="application/rss+xml" href="フィードURL" title="RSS2.0" />
```

- フォーマットが__「Atom」__の場合

```atom.html
<link rel="alternate" type="application/atom+xml" href="フィードURL" title="Atom" />
```

### 上記のコードは、、

>__<font color="red">「RSSフィードがココにありますよ！」</font>__と拡張機能等にお知らせするために、追加するコードです。

_※ RSSについて、もっと詳しい情報が欲しい方は下記記事をご参考ください。_

__Title : [RSSフィードが存在することを知らせるためのlinkタグ](http://techmemo.biz/html/rss-feed-link-head-html/)__

## もしかして、もしかするとQiitaのRSSフォーマットは、Atom?

再度、「```atom+xml```」で、ソースコードを検索してみる。

_あ、あったーーーーーーーーーーー！！！！笑_

## 「Qiita」個人ページにRSSフィードが存在することを発見。

```myQiitaRss.html
<link rel="alternate" type="application/atom+xml" title="Atom Feed" href="http://qiita.com/Futo23/feed" />
```

URL : ```http://qiita.com/〇〇〇〇〇(アカウント名)/feed``` へアクセスすると、
下記のようなxmlが返ってくることが分かると思います。

```rss.xml
<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="ja-JP" xmlns="http://www.w3.org/2005/Atom">
  <id>tag:qiita.com,2005:/Futo23/feed</id>
  <link rel="alternate" type="text/html" href="http://qiita.com"/>
  <link rel="self" type="application/atom+xml" href="http://qiita.com/Futo23/feed"/>
  <title>Futo23の投稿 - Qiita</title>
  <description>QiitaでユーザーFuto23による最近の投稿</description>
  <updated>2017-05-31T16:52:58+09:00</updated>
  <link>http://qiita.com/Futo23</link>
  <entry>
    <id>tag:qiita.com,2005:PublicArticle/497543</id>
    <published>2017-05-31T16:52:58+09:00</published>
    <updated>2017-06-01T19:40:22+09:00</updated>
    <link rel="alternate" type="text/html" href="http://qiita.com/Futo23/items/be35639bf73a3330c971"/>
    <url>http://qiita.com/Futo23/items/be35639bf73a3330c971</url>
    <title>【※JS初心者必見】JS未経験者がJavaScript(JS)でつまずくポイントについてまとめてみた！ - 「var」と「let」と「const」と、</title>
    <content type="html">
```

_さて、次は何をしたら良いのか？
上記で取得したXMLを元に、更新の有無を判断する必要があります。_

# 2. 取得したXMLをパースする。

_今回は、GAS(Google Apps Script)を用いて、
上記のxmlをパースしていきたいと思います。_

####1. まず、[Googleドライブ](https://www.google.com/intl/ja_ALL/drive/)にアクセスします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/180432/e22849e8-b557-037d-de2f-d71afcc3df00.png)

上記の手順で、__「Google Apps Script」__を新規作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/180432/dd6e2554-e1b1-df06-742e-02f9d4ed090b.png)

####2. 次に、XMLパーサーを作成していきます。

>__[パーサーとは？]__
プログラムのソースコードやXML文書など、一定の文法に従って記述された複雑な構造のテキスト文章を解析し、プログラムで扱えるようなデータ構造の集合体に変換するプログラムのこと。
[[e-word - パーサー(parser) ] より抜粋。](http://e-words.jp/w/%E3%83%91%E3%83%BC%E3%82%B5.html)

<font color="red">_XMLのパースには、GAS(Google Apps Script)の「XML Service Service(誤字ではないｗ)」が使えそう！_</font>

### とにかく、サンプルコードを動かしてみる。

[GAS (Google Apps Script) - REFERENCE](https://developers.google.com/apps-script/reference/xml-service/)

_こちらのGAS公式リファレンスに記載のあるソースコードを実際に動かしてみた。_

```js:parseSample.gs
function parseXml() {
  // Qiita個人ページのフィードURL
  var url = "http://qiita.com/〇〇〇〇〇(ユーザー名)/feed";
  var xml = UrlFetchApp.fetch(url).getContentText();
  var document = XmlService.parse(xml);
  var root = document.getRootElement();
  var atom = XmlService.getNamespace("http://www.w3.org/2005/Atom");

  var entries = document.getRootElement().getChildren("entry", atom);
  for (var i = 0; i < entries.length; i++) {
    var title = entries[i].getChild("title", atom).getText();
    var categoryElements = entries[i].getChildren("category", atom);
    var labels = [];
    for (var j = 0; j < categoryElements.length; j++) {
      labels.push(categoryElements[j].getAttribute("term").getValue());
    }
    Logger.log("%s (%s)", title, labels.join(", "));
  }
}
```

####1. コードをGASに保存、実行する。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180432/ded60167-7f1f-5f4e-a071-f1480bd27d00.png)

_※ 画面中央上の再生マークを押すと、上記のソースコードが実行されます。_

####2. ログを確認する。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180432/307e6b2b-14c3-7cea-50cf-ca2f624a6968.png)

_画面中央のメニューより[ 表示 ] > [ ログ ] を実行すると、上記ソースコードのログ出力を確認することができます。
ショートカットは、「 ```Ctrl + Enter``` 」_

####3. ログ出力の結果が表示される。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/180432/2d1d22f0-0a21-04fb-6d14-ca799a3edb59.png)

_ざっと、こんな感じｗ_

_後は、このソースコードをいい感じにしてやるだけ！_

```js:parseXml.gs
function parseXml() {
  
  // Qiita個人ページのフィードURL
  var url = "http://qiita.com/futo23/feed";
  
  // 上記URLにGETリクエスト
  // getContentText()で、xmlを取得
  var xml = UrlFetchApp.fetch(url).getContentText();
  
  // 取得したxmlを文書化
  var document = XmlService.parse(xml);
  
  // ドキュメントのルート要素を抽出
  var root = document.getRootElement();
  
  // 引数のURLを用いてNameSpaceを生成します。（おそらくフォーマット指定？）
  var atom = XmlService.getNamespace("http://www.w3.org/2005/Atom");

  // エントリーの記事を一つずつ取得
  var entries = document.getRootElement().getChildren("entry", atom);
  for (var i = 0; i < entries.length; i++) {
    
    // 記事タイトルの取得
    var title = entries[i].getChild("title", atom).getText();
    
    // 記事の更新日時を取得
    var UpdateTime = entries[i].getChild("updated", atom).getText();
        
    Logger.log(title + " : " + UpdateTime);
  }
}
```

ユーザーオブジェクト作って、newして実態を生成したまでは良いのだけど、
上記の値を保持することはできないので、ロジックを変更する必要がある。


#### Slack投稿ロジックを変更する。
_(旧) 既存のエントリIDと最新のエントリIDを比較し、差異があればSlackに投稿する。_
<font color="red">_(新) 直近1時間以内に更新があれば、Slackに投稿する。_</font>

# 3. Slackの特定のChannelに投稿する。

```js:parseXml2.gs

// 更新通知を行うユーザー名
var names = ["xxxxxxx","Futo23"];

function main() {  
  for (var k = 0; k < names.length; k++) {
     parseXml(names[k]);
  }
}

function parseXml(user) {
  
  // Qiita個人ページのフィードURL
  var url = "http://qiita.com/" + user + "/feed";
  
  // 上記URLにGETリクエスト
  // getContentText()で、xmlを取得
  var xml = UrlFetchApp.fetch(url).getContentText();
  
  // 取得したxmlを文書化
  var document = XmlService.parse(xml);
  
  // ドキュメントのルート要素を抽出
  var root = document.getRootElement();
  
  // 引数のURLを用いてNameSpaceを生成します。（おそらくフォーマット指定？）
  var atom = XmlService.getNamespace("http://www.w3.org/2005/Atom");

  // エントリー記事を取得
  var entries = document.getRootElement().getChildren("entry", atom);
  
  // Logger.log(entries);
  
  // 最新記事のidを取得
  var id = entries[0].getChild("id", atom).getText();
  var pos = id.indexOf("/");
  var entrieId = id.substr(pos+1);
  
  // 記事タイトルの取得
  var title = entries[0].getChild("title", atom).getText();
  
  // 記事の更新日時を取得
  var publishTime = entries[0].getChild("published", atom).getText();
  var nowTime = Utilities.formatDate(new Date(), "Asia/Tokyo", "yyyy-MM-dd'T'HH:mm:ss");
  // Logger.log("publishTime : " + Date.parse(publishTime));
  // Logger.log("nowTime : " + Date.parse(nowTime));
  
  // 1時間をミリ秒へ変換したもの
  var TimeLag = 3600000;
  
  // 更新があった場合[1時間以内に更新があったもの]
  if (Date.parse(nowTime) - Date.parse(publishTime) <= TimeLag) {
    
    // Slackに投稿を行う。
    var postUrl = "https://slack.com/api/chat.postMessage";
    // 送りたいSlackTeamのトークンを指定
    // [テストページ] https://api.slack.com/methods/chat.postMessage/test
    var token = "xoxp-[対象のSlackチームのToken]";
    // Qiita Channel
    var channel = "C5NHYUMN0"; // Channel ID
    var text = "@" + user + " さんが【 " + title + " 】を投稿しました。";
　　
    var fixUrl = postUrl + "?token=" + token + "&channel=" + channel + "&text=" + text;
　　 UrlFetchApp.fetch(fixUrl);
    // Logger.log(fixUrl);
  }
  
  // [参考ソース] 実際には、必要ないです。
  for (var i = 0; i < entries.length; i++) {
    // 記事タイトルの取得
    var title = entries[i].getChild("title", atom).getText();
    // 記事IDの取得
    var id = entries[i].getChild("id", atom).getText();
    // 記事の更新日時を取得
    var UpdateTime = entries[i].getChild("updated", atom).getText();
  }
}
```

こんな感じ。

このGASを1時間置きに実行してやれば、
最新のQiita記事がSlackの特定チャネルに投稿されます！

編集 > 現在のプロジェクトのトリガー > 実行する関数と時間タイマーをセットし、トリガーを追加する。

こんな感じで保存してやると、1時間置きにSlackに更新通知が来るかもしれないし、こないかもしれないｗ

![image.png](https://qiita-image-store.s3.amazonaws.com/0/180432/a088a501-d2a8-05e7-7b90-38d92297488c.png)


興味がある方は是非お試しください！

# 最後に
なんとなく作ってみたら出来ました。
何でもSlackにつなげたいという安直な考えですが、
少しでもみなさんのお役に立てればと思います。

最後までお読みいただき、ありがとうございました。