#あすみかんの上にあすみかん

#たのしいことしかかかないことをここに決意します

GAS+TwitterAPIでChatworkに俺たちのマドンナのツイートを自動投稿

qiita.com

こにゃにゃちわ〜〜〜
Hamee開発部アドカレ3日目担当、あすみです🌼

みなさんは「雑相」という考え方を知っていますでしょうか?
いま「報連相」より「雑相」と言われているくらい、「雑談・相談」が大事と言われています。
私たちのチームはまさに「雑相」で成り立っているチームです。
今回は私たちのチームのマインド共有+自動化・効率化した話を書きます!

僕たちの雑相マドンナ、@shuumaiさん

さっそくなのですが、みなさんは@shuumaiさんという人気ツブヤーカーを知っていますでしょうか?
twitter.com

フォローしている人たちの呟きをマルコフ連鎖で連結し、一つの文にして呟く食べ物です。私たちのチームは全員@shuumaiさんが大好きです。


f:id:asumiso:20191122160937p:plain
8月某日


f:id:asumiso:20191122160946p:plain
10月某日


f:id:asumiso:20191122160957p:plain
11月某日


そう、私たちのチームチャットには「あ、これ面白いな〜」と思った@shuumaiさんのツイートが手動で共有されるのです。笑顔を生むツブヤーカー。私たちもこのようなツイートができるようになりたいものです

@shuumaiくんのツイートを気軽に共有できない

@shuumaiさんの面白ツイートを共有するには、

  1. Twitterアプリを開く
  2. @shuumaiさんのツイートリンクをコピー
  3. Chatworkを開く→ルームを開く
  4. コピペして送信

という長くて煩雑な手動作業が発生します...これが結構めんどくさくのでポンッと@shuumaiさんのツイートが共有できればもっと笑顔が増えるのに、と考えました。

Hamee開発部ではこれまでたくさんの自動化をしてきました。「Redmineチケットステータス遷移・作成の自動化」「検証の自動化」「ユニットテストの自動化」...そう、私たちはいつでも自動化して、隙間時間をクリエイティブな時間に使ってきたんだ.................



@shuumaiさんのツイート共有を自動化しよう!(決意)(天才)



処理の流れ

  1. TwitterAPIで定期的にツイートを数件取得
  2. 人気のツイートの特定
  3. いい感じ〜にフォーマットしてChatworkに送信

今回、「定期的に」ツイートを取得する部分をGASのトリガーにお任せしました。
@shuumaiさんのツイート共有はタイムリーさが重要なので、平日・業務時間中の1時間に1回、3件のツイートを取得することにしました。
@shuumaiさんはXX:00、XX:20、XX:40にツイートするので、「GASで毎XX:10に3件取得し、それぞれのRT+FAVを足して人気と判断したもののみChatWorkに投稿する」という処理で書きました。

f:id:asumiso:20191125171351p:plain
フォーマットまで指定してくるオタク

GASのコード

使用ライブラリは以下です。
ChatWorkClient(1nf253qsOnZ-RcdcFu1Y2v4pGwTuuDxN5EbuvKEZprBWg764tjwA5fLav
TwitterWebService(1rgo8rXsxi1DxI_5Xgo_t3irTw1Y5cxl2mGSkbozKsSXf2E_KBBPC3xTF

// 自分の値入れる
var twitter = TwitterWebService.getInstance(
  'Twitter Consumer Key',
  'Twitter Consumer Secret' 
);

// トリガー設定
function setTrigger() {
  var today = new Date();
  
  // 土日なら送信しない
  var day_of_week = today.getDay();
  if(day_of_week === 0 || day_of_week === 6) return;
  
  // 業務時間内じゃなかったら送信しない
  var hour = today.getHours();
  if(! (hour>8 && hour < 19)) return false;

  // 8時に動いたら→9:10にセットする
  today.setHours(hour+1);
  today.setMinutes(10);
  ScriptApp.newTrigger("myFunction").timeBased().at(today).create();
}

// メイン
function myFunction() {
  // trigger消去
  this.removeTrigger();
  
  // tweet3件取得
  var tweet_array = this.getTimeLine();
  
  // 面白ツイート取得
  var fan_tweet_array = getFanTweets(tweet_array);
  if (fan_tweet_array.length === 0) return;
  
  // チャットに送信
  var format_text = this.formatForPost(fan_tweet_array);
  this.sendChat(format_text);
}

// タイムライン取得
function getTimeLine() {
  var service  = twitter.getService();
  var json = service.fetch("https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=shuumai&count=3");
  var array = JSON.parse(json);
  return array;
}

// トリガー残らないように消去
function removeTrigger() {
  var triggers = ScriptApp.getProjectTriggers();
  for(var ll=0; ll < triggers.length; ll++) {
    if (triggers[ll].getHandlerFunction() == "myFunction") {
      ScriptApp.deleteTrigger(triggers[ll]);
    }
  }
}

// 面白ツイート取得
function getFanTweets(tweet_array) {
  var fan_tweet_array = [];
  for(var ii = 0; ii < tweet_array.length; ii++) {
    var tweet_obj = tweet_array[ii];

    var fan_count;
    if(ii === 0) {
      fan_count = 300;
    } else if (ii === 1) {
      fan_count = 1000;
    } else {
      fan_count = 2000;
    }
    
    var fav_count      = tweet_obj.favorite_count;
    var rt_count       = tweet_obj.retweet_count;
    var real_fan_count = fav_count + rt_count;
    
    if(real_fan_count > fan_count) {
      fan_tweet_array[fan_tweet_array.length] = tweet_obj;
    }
  }
  return fan_tweet_array;
}

// チャットに送る形にフォーマット
function formatForPost(tweet_array) {
  var new_line  = "\n";
  var post_text = '(eat)しゅうまい君さんが面白つぶやきしました(eat)' + new_line;
  for(var jj = 0; jj < tweet_array.length; jj++) {
    var tweet_obj = tweet_array[jj];
    post_text += tweet_obj.text + new_line;
    post_text += '♻️ ' + tweet_obj.retweet_count + ' / ❤ ️' + tweet_obj.favorite_count + ' ';
    post_text += 'https://twitter.com/shuumai/status/'+tweet_obj.id_str + new_line;
  }
  return post_text;
}

// チャット送信
function sendChat(post_text) {
  var client = ChatWorkClient.factory({token: 'Chatwork API Token'}); // 自分の入れる
  client.sendMessage({room_id: 'Chatwork Room ID', body: post_text}); // 自分の入れる
}

コードの処理をする前にauthorizationを通しておきましょう🙇‍♀️
qiita.com
(上記サイトかなり参考にさせていただきました、ありがとうございます...!)

本当にちょっとした解説

setTriggerを使用して時間・分をキッチリ指定した
GASのトリガーは時間指定ができるのですが、ちょうどの時間は指定できません。それを解決したのがこれです。
前準備として「setTriggerを毎時間動かすトリガーにする」を設定しておきます。すると、以下のように動いてくれます。

  1. 15:06(ここの時間が流動的)にsetTriggerが動く
  2. 条件を満たせば、16時10分にmyFunctionを動くように設定する
  3. 16時10分にmyFunctionが動く

こうすることで、GASの時間設定をキッチリさせることができます👏

3件それぞれキッチリ精査してる
ここはこだわりポイントというか荒技しちゃったなポイントなのですが💧
今回、3件それぞれゴリゴリのif文で分岐して判断してます><
本当は@shuumaiさんのツイートの伸び率を見ていい感じに判断するのが適切だと思うのですが、一旦動かすものを作るということでこういう形にしました(綺麗なコードよりまずは運用するのが大事)(そして書き変わらないコード)(THE END...)

f:id:asumiso:20191125170404p:plain
@shuumaiさんのツイートで笑顔になる仲間たち

所感など

TwitterAPIのアカウント申請がめちゃめちゃめんどい....!
なんかめんどくさそ〜なのは知っていましたが、ここまで面倒臭いとは思いませんでした😰私の場合、3年程前にAPIを使用していたのですが、使おうとした時に「オメーなんのためにAPI使う?」「どのエンドポイント使う気なんや?」「なんか統計とか取ったりするんけ?」「英語でOK」みたいな感じでめちゃめちゃ質問攻めにあいました......(全部の質問答えたら、すぐ使えましたが)

f:id:asumiso:20191125171940p:plain
@shuumaiを推す私

TwitterAPI中身は割と使いやすい部類
あんまりAPIを使ったので間違ってたらすみません。。ただ、よく使われそうなエンドポイントは調べればたくさんのQiitaの記事とかが出てきます。ので、実装で困ることはあんまりありませんでした。ドキュメントは....読みづらい!w

@shuumaiは平日より休日の方が面白い
盲点でした..............土日の方がTLが沸いているんでしょうね......土日に面白いツイートばかりしやがるので、かなり悔しい思いをしています。。。半企画倒れやんけ!(怒)

@shuumaiツイートの効果

しばらく回してみました。

f:id:asumiso:20191203104303p:plain
月曜日はあまり面白ツイートしない

f:id:asumiso:20191203104340p:plain
@shuumaiの呟きにワキアイアイとするTL

しかし、ワキアイアイとするだけでなく、

f:id:asumiso:20191203104421p:plain
あったまったTLを和ます@shuumai

ちょっと白熱した会話も和ますことができました!!!奇跡!!!!!
(冷静な会話をすることができる)

みなさんも@shuumaiの呟きを使って、良い雑相LIFEを送りましょう!(?)