ストックしたQiita記事をSlackへランダムに通知するHubotスクリプト

:warning:建てっぱなしのHubot環境を使っているので環境が非常に古いです。

# 問題点

ストックを整理しようとしたら800件近くあり、手をつける気分にもなれなかった。→ストックの機能不全。

これはよくある
**「目の前の問題が多すぎてどこから手を付ければいいかわからない」**[^1]
状態だと思ったので、ひとまず一日ひとつ、ストックを確認していく日課を作ろうと思った。

# スクリーンショット

![image.png](https://qiita-image-store.s3.amazonaws.com/0/62354/3a45664b-292e-458b-b5b6-7792d317dea5.png)


# 動作環境

* Heroku
* Node.js v0.12(Promiseが動かなかったのでv0.10から上げた)
* hubot 2.11.1
 * coffee-script 1.6.3
 * hubot-slack 3.3.0


# コード

```coffeescript:qiita-stock.coffee
# Description:
# Qiitaのストックをランダムに表示する
#
# Commands:
# 

QIITA_TOKEN = 'your token' # memo herokuの環境変数でもいい
USER_ID = 'khsk'

cron   = require('cron').CronJob
random = require('hubot').Response::random

qiita = (msg, stock) ->
    tags = Object.keys(stock.tags).map((v) ->
        stock.tags[v].name
    ).join(', ')
    # memo bodyから https?://.+\.(jpeg|jpg|png|gif)などを抜き出してimage_urlまたはthumb_urlとして使う (要る？)
    {
        username:'Qiita-Stock',
        icon_emoji:':books:',
        text:'Qiita記事を紹介します',
        message:msg,
        attachments:[
            {
                color: "#55c500",
                author_icon: stock.user.profile_image_url,
                author_name: stock.user.name,
                author_link: 'https://qiita.com/' + stock.user.id,
                title: stock.title,
                title_link: stock.url,
                text: stock.body.slice(0, 117),
                footer: tags,
                ts: new Date(stock.updated_at) / 1000,
            }
        ]
    }

getAllStock = (robot) ->
    page = 1
    done = stockLoop(robot, page)
    done.then (stocks) ->
        console.log(stocks.length)
        console.log('完了')
        stock = random(stocks)
        robot.adapter.customMessage qiita({room: 'random'}, stock)

# ストック取得が0件になるまでループし、取得結果を結合していく
stockLoop = (robot ,page, stocks = []) ->
    return getStock(robot, page)
    .then (result) ->
        if (result && result.length != 0)
            stocks = stocks.concat(result)
            page += 1
            stockLoop(robot, page, stocks)
        else
            return stocks
    .catch (result) ->
        console.log(result)
        return stocks

# 100件ストックを取得する
getStock = (robot, page) ->
    new Promise (resolve, reject) ->
        robot.http('https://qiita.com/api/v2/users/' + USER_ID + '/stocks?per_page=100&page=' + page)
        .header('Authorization', 'Bearer ' + QIITA_TOKEN)
        .get() (err, res, body) ->
            resolve JSON.parse body

module.exports = (robot) ->
    new cron('0 0 10 * * 1-5'
        , () ->
            getAllStock robot
        , null
        , true
    )

    # デバッグ用
    robot.respond /qs/i, (msg) ->
        msg.send 'ストックを表示します。しばらくお待ちください。'
        getAllStock robot
```

久々のHubot,久々のCoffeeScript,コールバックにプロミスにループ(再帰)に。
難しいですね…。
やっぱりasync/awaitは最高！

バージョン上げや生jsで書く手間を省いた代わりにCoffScript構文学習と難コーディング、読みにくいコードになったのはあまりペイできていない。

concatがrubyと違い非破壊なことでかなり躓いた。

ブラウザで見るとストックページ数が膨大に見えるが、
実際は1ページ20件なので、一度に100件とるスクリプトではページ数が5分の一まで減る。
ブラウザでは20件で40ページ[^2]あったので勢いトークンを使っているが、実際は8ページしか取得しないのでトークン無しでも十分動く。

[^2]: トークンなしでは一時間に60リクエスト制限だったはず

# 参考

* [Promiseでループ処理をする - Qiita](https://qiita.com/niusounds/items/339496f58aed9c6dabce)
* [Slack API attachmentsチートシート - Qiita](https://qiita.com/daikiojm/items/759ea40c00f9b539a4c8)
* [Promiseと仲良くなって気持ち良く非同期処理を書こう - Qiita#必要な環境](https://qiita.com/progre/items/03626b7f4655007d8cb2#%E5%BF%85%E8%A6%81%E3%81%AA%E7%92%B0%E5%A2%83)  
 ローカルで動くのにHerokuで動かない問題。nodejsが0.10だった。
* [Scripting | HUBOT#Making HTTP calls](https://hubot.github.com/docs/scripting/#making-http-calls)

* [Date/Timestamp変換のまとめ - Web就活日記](http://yut.hatenablog.com/entry/20111015/1318633937)

***

* [【要望】ストックした記事をランダムで表示してほしい - Qiita](https://qiita.com/suin/items/9fdc92a92438c9dba1f9)

* [SlackとQiitaの117文字な関係 - Qiita](https://qiita.com/suin/items/c2cdf9d51198bfb714b2)  
 本文表示文字数にたいてい使うが、こちらはmd形式なので等価でない

***

* [qiitaから自分のストックを全て削除したいスクリプト - Qiita](https://qiita.com/positrium/items/b552bfa1a3659e653e33)  
  一度リセットもアリかも

***

* [SlackでHubotを使い回し別人(BOT)として発言する - Qiita](https://qiita.com/khsk/items/a78f34439bbc457ac8cf)

[^1]: レガシーコード改善ガイドで見た記憶