Twitter見るとき、情報量多すぎてめんどくさい。 フィルタリングかけて、slackとかメールに投げてほしい。

要件

  • UserStreamをみてほしい発言が来たら、通知
  • 定期的に検索回して、ほしい発言が来たら、通知

npmのパッケージのコピペでUserStreamが簡単に組めた。 飛んでくるJSON見ながら、レーン分け。 ここまでは早かった。

UserStream部分

function streamfunc(){

var stream = bot.stream('user');
        stream.on('data', function(data) {
                var id        = ('user' in data && 'screen_name' in data.user) ? data.user.screen_name : null;
                var text      = ('text' in data) ? data.text.replace(new RegExp('^@' + BOT_ID + ' '), '') : '';
                var ifMention = ('in_reply_to_user_id' in data) ? (data.in_reply_to_user_id !== null) : false;
                if(data.text === undefined){
                }else{
                    //このへんに分岐条件を書く。
                }
        });
  stream.on('error', function(error) {
    console.log(error);
    throw error;
    
  });
}

検索部分

function searchfunc(sendflg){//sendflg 初回起動時に0 その後は1を入れる。

        bot.get('search/tweets', {q: 'けんさくしたいもじ exclude:retweets'}, function(error,data, response){ //exclude:retweetsでRT除外
            data.statuses.forEach(function(val,index,ar) {
                      var flg=0;
                      for(var i = 0; i < idar.length; i++){ //配列の中身とチェックして既に入ってたら通知しない。
                         if(idar[i] ===  val.id_str){
                             flg = 1;
                         }
                      }
                      if(flg == 0){
                          if(sendflg==1){
                            slackpost("https://twitter.com/"+val.user.screen_name+"/status/"+val.id_str,"random");
                          }
                          idar.push(val.id_str);  //同じ通知しないように配列にいれる。
                      }
            });
        });
});

ひとまずこれで満足はしたけど、ほっとくとUserStream接続が切れるということが往々にある。

しばらく定期的に再起動かけてやり過ごしてたけど、apiのドキュメントには、再接続を実装しろって書いてあるので おとなしく作る。

UserStream再接続にハマる

npm見てもチュートリアルない。 ひとまず、error吐いたらfunctionを再起してみる。

setIntervalかけて再起動

var timer = setInterval(function(clearInterval(timer);streamfunc),60000);

うまく起動した。

と、思いきや。

[Error: Status Code: 420]
{ [SyntaxError: Unexpected token E] source: 'Exceeded connection limit for user' }

http://westplain.sakuraweb.com/translate/twitter/API-Overview/Error-Codes-and-Responses.cgi

420:速度制限に引っかかった場合に、検索APIとトレンドAPI バージョン1によって返されます。

どうやら多重起動してるっぽい。

errorイベントを通るたびにログ吐いてみたら、一回の接続に対して複数回発火してたので、倍々で増えちゃう。

よくよく考えると、コネクション貼ってなくてもイベントは残るなら複数回発火は起こりえる気がしてきたので、 落ちたかどうかをフラグ管理することにした。

setInterval(streamchk,60000);

function streamchk(){
    if(resflg==1){
        resflg=0;
        streamfunc();
    }else{
        console.log("test");
    }
}

function streamfunc(){

var stream = bot.stream('user');
        stream.on('data', function(data) {
                var id        = ('user' in data && 'screen_name' in data.user) ? data.user.screen_name : null;
                var text      = ('text' in data) ? data.text.replace(new RegExp('^@' + BOT_ID + ' '), '') : '';
                var ifMention = ('in_reply_to_user_id' in data) ? (data.in_reply_to_user_id !== null) : false;
                if(data.text === undefined){
                }else{
                 if(data.text.match(/【ていき】/)){
                 }else{
                    if(data.user.screen_name == 'plsfive'){
                     slackpost("https://twitter.com/"+data.user.screen_name+"/status/"+data.id_str,"tw")
                    }else{
                     slackpost("https://twitter.com/"+data.user.screen_name+"/status/"+data.id_str,"general");
                    }
                 }
                }
        });
stream.on('error', function(error) {
  console.log(error);
  resflg = 1;    
});

なんか、泥臭い。 イベント初期化のタイミングが微妙なので、これでも多重起動は100%防げるわけじゃないなあ…

テスト繰り返してたらAPI蹴られて面倒なので一旦切りあげ。

あえてthrow errorさせて、foreverなり、プログラムごと再起動させても解決できるけど、 長時間ダウンしたら延々再起動しまくって負荷かけそう…。


追記

once

onceっていう、一回発火したら消えるイベント作れることを今更知った。 ので、多重起動の問題はこれでなんとかなりそう。

main();

function main(){
    streamfunc(function(raidjs){
              setTimeout(main,10000);
      
    });


}
function streamfunc(acallback){

var stream = bot.stream('user',function(stream) {
        stream.on('data', function(data) {
                var id        = ('user' in data && 'screen_name' in data.user) ? data.user.screen_name : null;
                var text      = ('text' in data) ? data.text.replace(new RegExp('^@' + BOT_ID + ' '), '') : '';
                var ifMention = ('in_reply_to_user_id' in data) ? (data.in_reply_to_user_id !== null) : false;
                console.log(data);
               //省略
        });
    stream.once('error', function(error) {
      acallback();
    });
    stream.on('error', function(error) {
      console.log(error);
    });
});

ある程度はこれで対応できるんじゃないかな。

フィルタ

検索オプション、公式に書いてないのがあったのでちょっとまとめる。

exclude:retweets リツイート除外
exclude:nativeretweets 公式リツイート除外
min_replies:[num] リプライ数下限
min_retweets:[num] RT数下限
min_faves:[num] fav数下限