GitHubActionsでQiitaとSlackを繋いで、エンジニアにとっての良い習慣をつくる。(追記あり)

## よりよい技術者を目指すための習慣
私が感銘を受けた[佐藤太一](https://github.com/taichi)さんの講演でエンジニアが身につけたい習慣についてのお話があります。

[デブサミ2016レポート「今日の習慣が明日をつくる～よりよい技術者を目指して～」](https://dev.classmethod.jp/articles/devsumi2016-19-c-3/)
この中で特に、そこまで敷居が高くなく毎日実践できそうなことが書かれていました。（スライドお借りします。）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164405/7cf6abdc-0117-8663-ea6f-bc454d30b1f4.png" width="70%">

###惰性でトレンドを見る習慣をつけるのが大事
>毎日5分Trending repositories on GitHub todayを見よう
>どんな言語が流行り、開発者が注目されているかを見る
>最初はリポジトリまで見なくてもいい。__惰性でトレンドを見る習慣をつけるのが大事__
>言語を絞らずにトレンドに上がるリポジトリを見てみると、誰かが学習成果を公開しているリポジトリが多い
>だれかがムリのないやり方で続けて、上手くいったものを、皆が見にいっていることがわかる

この方がおっしゃているのはGitHubでのことですが、私たちにはQiitaがというプラットホームもありQiitaのトレンドを毎日見ることにもあてはめれるのではないかと思い、最近学んだ__自動化__を活用してエンジニアにとっての良い習慣を身に着ける仕組みが作れないか考案してみました。

## GitHubActionsを活用する。
今回考案したものはこのようなものです。
Qiitaのトレンドの情報を取得して、GithubActionsのScheduleで設定した時間にSlackに通知します。
イメージはこんな感じです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164405/f987708a-4b85-65c2-c674-bfed8c638979.png" width="70%">
トレンドをみたければQiitaにアクセスすれば済む話ですが、人間はいかんせん面倒くさがりです。
また、Qiitaからはメルマガが来ますが、私の知る範囲ではトレンド一覧だけの内容はなかった気がします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164405/ba539d5d-46e8-2fc0-d387-691b36325bd0.png" width="30%">
なので通勤中や朝起きた瞬間にLINEなどでスマホをいじっているタイミングでSlackから通知があれば、ついつい読んでしまうんじゃないかという仮説をたてました。
それが毎日続けば、プロラグラムを書く人（__エンジニアに限らず__）がどんな技術に興味を持っているか知れたり、同僚の方と「今朝のトレンドみた？」などのコミュニケーションが生まれたりするのではないかと思いました。

## 実際に作ったもの
時刻になったらトレンド一覧のURLとタイトルがSlackの画面に通知されます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">GitHub Actionsを使って指定時間にQiitaのトレンド取得するスクリプトを実行→Slackに通知するデモ<a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://twitter.com/hashtag/githubactions?src=hash&amp;ref_src=twsrc%5Etfw">#githubactions</a> <a href="https://twitter.com/hashtag/slack?src=hash&amp;ref_src=twsrc%5Etfw">#slack</a> <a href="https://twitter.com/hashtag/qiita?src=hash&amp;ref_src=twsrc%5Etfw">#qiita</a> <a href="https://t.co/NaeoQopbN5">https://t.co/NaeoQopbN5</a> <a href="https://t.co/L2xQv4bjuT">pic.twitter.com/L2xQv4bjuT</a></p>&mdash; ウーリズム (@Uh_rhythm) <a href="https://twitter.com/Uh_rhythm/status/1311298208665141251?ref_src=twsrc%5Etfw">September 30, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

__ですが、こちらは手動実行したものです。後述します。__

## Node.jsでQiitaの情報取得する。
トレンドを取得するAPIがなかったためスクレイピングしました。
1日に1回の定期実行なので迷惑はかけないと思うのですが、どうなんでしょうか・・・
またトレンドは1回で無理のない量がどれくらいかわからなかったのでひとまず上位１０件の取得にしました。

```javascript
const rp = require('request-promise');
const cheerio = require('cheerio');
const request = require('request');

const options = {
  transform: (body) => {
    return cheerio.load(body);
  }
};

const urls = [
    'https://qiita.com/'
];

const promises = urls.map((url)=> {
  return (async () => {
    try {
      const $ = await rp.get(url, options);
      const elm = $('div[data-hyperapp-app="Trend"]')[0]['attribs']['data-hyperapp-props']
      return elm;
    } catch(error) {
      console.error('Error:', error);
    }
  })();
});
Promise.all(promises).then((result) => {
    const list = JSON.parse(result[0]);
    const nodeList = list.trend.edges;
    for (var i = 0; i < 10; i++) {
        var title = nodeList[i].node.title;
        var author = nodeList[i].node.author.urlName;
        var id = nodeList[i].node.uuid;
        var url = `https://qiita.com/${author}/items/${id}`;
        var message =
`----------------
タイトル:${title}
URL:${url}`;
       postToSlack(message)
    }
})

function postToSlack(msg) {
  request({
      url: '{{ WebhookURL}}',
      method: 'POST',
      headers: {'Content-Type': 'application/json' },
      body: JSON.stringify({
      text: `@channel\n${msg}`,
      link_names: 1 // @がメンションと解釈されるためのフラグ
    })
  }, (error, response, body) => {
    if (error) {
      return console.error(error);
    }
  });
}

```
コードはこちらからお借りしました。
ありがとうございます。
[Node.jsでスクレイピングするならこれが本命（たぶん）
](https://qiita.com/ksato9700/items/026f2c1d93facdf6976f)

## GithubActionsで定期実行する
cronの設定はUTC（協定世界時）なので、人がスマホを眺めていそうな、通勤時間帯の日本時刻の朝8時にスクリプトを起動したかったので日本時間から9時間を引いた値を設定します。

```ym
on:
  schedule:
  - cron:  '00 23 * * *'
  # 日本時間で午前8時00分になる
```
こちらはcronの設定になるのですが下記のサイトを参考にしました。

https://www.jisakeisan.com/?y=2020&m=9&d=30&hh=5&mm=35&t1=utc&t2=jst

日本時間の設定ありそうですが、どうなんでしょう。

### タイムラグの謎
時間指定に関しては何度かテストをしたのですが、毎回五分前後のラグがありました。
21時に時間指定をして本番用の動画を撮ろうとしたのですが、スクリプトが起動したのは21時20分でした。
仕様なのか、理解が足りないのか調査が必要そうです。
また、リポジトリの設定をPublicにした状態でWebhookを載せたコードをGithubにあげてしまい、GithubとSlackの両方に怒られてしまいましたので、Privateを設定しましょう。


## Slackに通知する
WebhookのURLを取得しますが、下記の記事を参考にさせていただきました。
ありがとうございます。

[SlackのIncoming WebhooksでWebhook URLを取得する方法
](https://qiita.com/twtjudy1128/items/1fa1b4d6494fae2d8259)
画面にきちんとトレンド一覧が通知されています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164405/e72cfc9c-1741-0bdd-e664-f6437bcff1c1.png" width="50%">


## 自動化と仲良くしてとにかく積み重ねて行こう
自動化をテーマにこの記事ともう一本記事を書きました。

[IFTTTでTwitterのハッシュタグ投稿をSpreadSheetで管理する](https://qiita.com/UhRhythm/items/320c32e3cbd314222391)

記録や情報収集など能動的にやらなきゃいけないことも、システムにやってもらって
ストレスを減らせることがわかりました。
手間を減らして、インプットとアウトプットを積み重ねることでより良い自分を目指すことができそう。
さらにいろいろなシステムを組み合わせればより世界が広がることがわかりました。

とある識者の方も同意してくれました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164405/b58ceee9-2e37-b068-5376-3e3cd00c2c15.png" src="50%">

## のびすけ校長に感謝
Qiitaに書くのが適切かはわかりませんが。
今回の制作は私がプロトアウトスタジオに入学するに至った[のびすけ校長](https://twitter.com/n0bisuke)の授業での課題でした。
憧れのエンジニアの方に教わった内容で自分の生活習慣を少し変えることができました。
これってすごくエポックな出来事です。
あらためて、ありがとうございました。

##追記(2020年10月7日)
@yumetodo さんのからご指摘いただき非推奨のrequestの使用をやめaxiosに書き換えました
@dodonki1223 さんに情報をいただき、Qiitaのトレンド情報の取得方法が変わった変わったため書き換えました。

ありがとうございます！

```javascript
'use strict'
const rp = require('request-promise');
const cheerio = require('cheerio');
const axios = require('axios');

const options = {
  transform: (body) => {
    return cheerio.load(body);
  }
};

const urls = [
    'https://qiita.com/'
];

const promises = urls.map((url)=> {
  return (async () => {
    try {
      const $ = await rp.get(url, options);
      const elm = $('script[data-component-name="HomeArticleTrendFeed"]')[0]['children']
      return elm;
    } catch(error) {
      console.error('Error:', error);
    }
  })();
});
Promise.all(promises).then((result) => {
  const list = JSON.parse(result[0][0].data);
  const nodeList = list.trend.edges;
  nodeList.map(function(item, index) {
    const title = item.node.title
    const url = item.node.linkUrl
    const message =
`----------------
タイトル:${title}
URL:${url}`;
    if (index < 10) {
      postToSlack({username: 'slack-bot',channel: '#qiita-trend-top10',text: `${message}`}).then(console.log);
    }
  });
})

async function postToSlack(payload) {
  const webhookUrl = '{{ WebhookURL }}'
  const res = await axios.post(webhookUrl, JSON.stringify(payload))
  return res.data
}

```