仕事中でも株価を見るために株価チャートを投稿するSlack botを作ってみた

株価チャートって見てて面白いですよね！(株やってないですが)

googleで `企業名 株価`って検索すると
その企業の株価チャートを表示してくれるのに気づいたので、
puppeteerを使ってその部分のスクリーンショットを取得して
Slackに投稿してくれるBotを作ってみました

## つくったもの

Github: https://github.com/tamanugi/slack_bot_stock_chart

|<img width="522" alt="スクリーンショット 2017-10-18 12.39.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/55219/138ba5dc-5f27-44e7-a91f-74d852cd6b27.png">|<img width="538" alt="スクリーンショット 2017-10-18 12.39.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/55219/d24b9349-7514-6b9d-4d3c-5685dee0ce88.png">|
|:--:|:--:|

キャプチャのようにbotにメンションつけてメッセージ送ると
`{メッセージ} 株価` でGoogle検索を実行して
出てきた株価の部分のスクリーンショットをslackにアップロードしてくれます。

また Google の株価チャートは期間指定ができるのですが、そちらも選択できるようにしています

## つかったもの

- botkit
- puppeteer

## 中身とか

```js:bot.js
const StockCapture = require('./lib/stock_capture.js') 

if (!process.env.token) {
  console.log('Error: Specify token in environment');
  process.exit(1);
}

var Botkit = require('botkit');
let fs = require('fs')

var controller = Botkit.slackbot({
  debug: false
});

var bot = controller.spawn({
  token: process.env.token
}).startRTM();

controller.hears(['(.*)'],['direct_message', 'direct_mention', 'mention'], function(bot, message) {
  let arg = message.match[1]
  let company, period;

  let match = arg.match(/(.*)(1日|5日|1か月|3か月|1年|5年|最長)$/)
  if(match) {
    company = match[1]
    period = match[2]
  }else{
    company = arg
  }

  StockCapture.capture(company, period, () => {
    bot.api.files.upload({
      file: fs.createReadStream('stock-chart.png'),
      filename: 'stock-chart.png',
      channels: message.channel
    },(err,res) => {
        if (err) console.log(err)
    })
  })

});

```

botkitを使ってファイルをアップロードする方法については
以前書いた自分の記事を参考にしました

参考: [Botkitで画像ファイルをslackにアップロードする ](https://qiita.com/tamanugi/items/4773b00a629c301fb55a)

```js:lib/stock_capture.js
const puppeteer = require('puppeteer');

const periods = {
  '1日': '1d',
  '5日': '5d',
  '1か月': '1M',
  '3か月': '3M',
  '1年': '1Y',
  '5年': '5Y',
  '最長': '40Y',
}

const capture = async (company, period ,cb) => {
  const browser = await puppeteer.launch({headless: true});
  const page = await browser.newPage();

  const targetElementSelector = '#fac-ut'

  await page.goto('https://google.co.jp')
  await page.type('#lst-ib', `${company} 株価`)
  await page.click('#tsf > div.tsf-p > div.jsb > center > input[type="submit"]:nth-child(1)')
  await page.waitFor(targetElementSelector)

  if(period){
    const dataPeriod = periods[period]
    await page.click(`#fac-sbtns > ol > li[data-period='${dataPeriod}']`)
    await page.waitFor(1000)
  }

  const clip = await page.evaluate(s => {
    const el = document.querySelector(s)

    // エレメントの高さと位置を取得
    let { width, height, top: y, left: x } = el.getBoundingClientRect()

    // padding分調整
    width += 32
    height += 36
    x -= 16
    y += 4


    return { width, height, x , y}
  }, targetElementSelector)

  // スクリーンショットに位置と大きさを指定してclipする
  await page.screenshot({ clip, path: 'stock-chart.png' })

  cb()

  browser.close();
};

module.exports.capture = capture
```

株価を表示している部分は `div#fac-ut` になりますので
位置と大きさを取得してスクリーンショット取る際に引数に指定しています。

取得できる位置と大きさと実際に欲しい部分がずれていたのでそこは調整しています。(padding分だけずれた?)

puppeteerを使ってあるエレメントのスクリーンショットを取得する方法は
過去にまとめたので、そちらもご参考いただけると幸いです

[Puppeteerを使って指定したDOMのみのスクリーンショットを取得する](https://qiita.com/tamanugi/items/8cc1266265457f13b9ea)

## まとめとか

puppeteerもbotkitもほんとにお手軽に使えるので
ちょっとしたChat Bot作る際に役立ちますね~~
もっと色々と作ってみようと思います

~~よーしこれで仕事中に株価ウォッチできるぞー~~