Meteorで作ったチャットアプリにbot機能をつける

## 仕様

- 外部サイトをクローリングして更新情報を取得
- twitterのつぶやきを取得
- それらを取得してチャットアプリに投稿する
- cronを使って自動でbot機能を実行

## 必要なもの

- node
- npm
- cheerio(外部サイトをクローリングするパッケージ)
- twitterAPIの「Consumer Key」「Consumer Secret」 
- percolate:synced-cron(meteorのcronパッケージ)

## 外部サイトをクローリング

requireを使うのでnpmをインストール

``` command:command
$ meteor npm install
```

外部サイトをスクレイピングするためのパッケージインストール

``` command:command
$ npm install cheerio
```

外部サイトを指定してクローリング

``` javascript:server
import { Meteor } from 'meteor/meteor';
const cheerio = require('cheerio');
Meteor.http.get('https://hogehoge.com', (err, result) => {
  $ = cheerio.load(result.content);
  const newsText = $('.hoge').eq(0).find('a').text();
  if (!newsText) return;
  Meteor.call('scrapingPost', newsText,);
});
```

## Twitter Application Only Authentication

twitterのAPIを使って、指定したアカウントのツイートを取得

### BEARER TOKENを取得

``` javascript:server
const request = require('request');
const consumerKey = 'CONSUMER_KEY';
const consumerSecret = 'CONSUMER_SECRET';
const encodeSecret = new Buffer(consumerKey + ':' + consumerSecret).toString('base64');

const options = {
  url: 'https://api.twitter.com/oauth2/token',
  headers: {
    'Authorization': 'Basic ' + encodeSecret,
    'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'},
  body: 'grant_type=client_credentials'
};

request.post(options, function(error, response, body) {
  console.log(body); // <<<< This is your BEARER TOKEN !!!
});
```

### 指定したアカウントのツイートを取得

``` javascript:server
const twitterBot = (url, userName) => {
  const twitterApi = 'https://api.twitter.com/1.1/search/tweets.json?q=from:' + userName; // 取得したツイッターアカウントを指定
  const bearerToken = 'BEARER TOKEN';

  const options = {
    method: 'GET',
    url: twitterApi,
    qs: {
      'screen_name': 'twitterapi'
    },
    json: true,
    headers: {
      'Authorization': 'Bearer ' + bearerToken
    },
  };

  request(options, (error, response, body) => {
    if (!body.statuses[0]) return;          // twitterのデータが取得できないときの対応
    const newsText = body.statuses[0].text; // 最新のツイートを取得
    if (!newsText) return;
    twitterBotPost(newsText);
  });
};

// “Meteor code must always run within a Fiber” errorの対応
const twitterBotPost = Meteor.bindEnvironment((newsText) => {
  if (Posts.findOne({ text: newsText })) return;
  Meteor.call('scrapingPost', newsText);
});

twitterBot('https://twitter.com/hogehoge', 'hogehoge');
```

### “Meteor code must always run within a Fiber” error

参考サイトを元にtwitterBot機能をつけたら「“Meteor code must always run within a Fiber”」エラーが発生
その対応として下記を追加

``` javascript:sample
Meteor.bindEnvironment
```

## cronで自動処理

指定した時間で自動で投稿するためのパッケージインストール

``` command:command
$ meteor add percolate:synced-cron
```

``` javascript:server
Meteor.startup(() => {
  SyncedCron.start();
});

SyncedCron.add({
  name: 'BOT Function',
  schedule: (parser) => {
    return parser.text('every 2 hours'); // cronする時間を指定
  },
  job: () => {
    botFnc(); // bot機能をまとめた関数を指定
  }
});
```

## 参考サイト

- [cheerio公式サイト](https://github.com/cheeriojs/cheerio)
- [Meteor: サーバサイドMethod内で非同期処理を行う方法](https://trycatchand.blogspot.jp/2016/06/how-to-use-async-function-in-meteor-serverside-method.html)
- [Twitter Application Only Authentication Demo](https://github.com/m-coding/twitter-application-only-auth/blob/master/README.md)
- [“Meteor code must always run within a Fiber” error](https://forums.meteor.com/t/meteor-code-must-always-run-within-a-fiber-error/16872)
- [synced-cron公式サイト](https://github.com/percolatestudio/meteor-synced-cron)