個人的に読んでるWeb漫画の更新通知botをGAS+TwitterAPIで作りました


完成したbotはこちら


Table of Contents



モチベーション

僕はめちゃくちゃ異世界転生漫画を読んでます。多分100本くらいあると思います。

見てるやつは片っ端からブックマークに入れて、毎月25日くらいに全部読んでいくのですが、流石に100本をまとめて読むのは辛い。(まあ逆に良い感じにしょうもないやつ切っていけるから良いという説もあるが…)

できれば毎晩寝る前に数本読むのが一番最高。

ということで更新されたときに通知が来るようにしたい。

もちろんそれぞれの漫画サイト毎で通知は何かしらあると思うが、いろんなサービスをまとめて、取捨選択してとなると自分で作るしかない。

そしてできればサーバーを立てたくない。


方法

通知はGmailでもいいが、Twitter APIを用いてbotでツイートして、その通知はTwitter公式クライアントで拾えば公開されてなんかまあ良い感じなのでそうします。

前回の記事同様に、更新情報の取得やAPI処理をサーバーを立ててそこでpythonプログラムを自動実行させるとかだったら言語の学習はほとんど必要ないのですが、なんせサーバーを立てたくないのでGASでやることにしました。

GAS(Google Apps Script)

Google Apps Script(略称GAS/ガス)とは、Googleによって開発されたスクリプトプラットフォームである軽量のアプリケーション開発のためのGoogleのワークスペースプラットフォーム。 主にGoogleのサービスを自動化するスクリプト言語である。JavaScriptがもとになっているため汎用性が高く、開発環境はGoogle Chromeだけでいいのでプログラミング初心者が始めやすい言語の1つである。

​ 引用 wikipedia

例えばGoogleスプレッドシートを作って、そのシートに対して何か操作をしたりするスクリプトを簡単に作れて、当然紐付けも簡単に行え、自動定期実行も無料でできる。

もちろん無料枠だと 制限があるので、その範囲でできるように作っていく。

とはいえそこは流石天下のGoogle、引っかかる可能性のある制限は6分制限くらいでした。


fig1

なるべく簡単に作りたかったので、上図のように読んでる漫画とURLのリストをサイト別に用意して、

これを上から1行ずつURLからアクセスし、最新話の更新日をスクレイピングで取得、その日が前日であればツイートする。1


当然サイトによって更新日の位置が違うので、それぞれに違う処理を書く必要がある。

上図の下部のシート名を見ると分かるように、サイト毎にシートを分けて同じようにリスト化しておく。

また、前述の6分制限もあるので、スクリプト自体を分けて6分を超えないようにしつつ、どれがどのサイトのスクリプトかも分かりやすくする。


スクリプト本体

// @ts-nocheck
// 認証用URL取得
function getOAuthURL() {
  Logger.log(getService().authorize());
}
 
// サービス取得
function getService() {
  return OAuth1.createService('Twitter')
      .setAccessTokenUrl('https://api.twitter.com/oauth/access_token')
      .setRequestTokenUrl('https://api.twitter.com/oauth/request_token')
      .setAuthorizationUrl('https://api.twitter.com/oauth/authorize')
      // 設定した認証情報をセット
      .setConsumerKey(PropertiesService.getScriptProperties().getProperty("CONSUMER_API_KEY"))
      .setConsumerSecret(PropertiesService.getScriptProperties().getProperty("CONSUMER_API_SECRET"))
      .setCallbackFunction('authCallback')
      // 認証情報をプロパティストアにセット(これにより認証解除するまで再認証が不要になる)
      .setPropertyStore(PropertiesService.getUserProperties());
}
 
//  認証成功時に呼び出される処理を定義
function authCallback(request) {
  var service = getService();
  var authorized = service.handleCallback(request);
  if (authorized) {
    return HtmlService.createHtmlOutput('success!!');
  } else {
    return HtmlService.createHtmlOutput('failed');
  }    
}

// ツイート用のAPIを起動する関数
function toTweet(text) {
  var twitterService = getService();
  
  if (twitterService.hasAccess()) {
    // 投稿
    var twMethod = { method:"POST" };
    twMethod.payload = { status: text };
    var response = twitterService.fetch("https://api.twitter.com/1.1/statuses/update.json", twMethod);
    
    Logger.log(response.getContentText());
 
  } else {
    Logger.log(service.getLastError());
  }
}


function notify_update_cw() {
  var spreadsheet = SpreadsheetApp.openById('スプレッドシートのIDを入れる');
  // var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); //これだとスプレッドシートを閉じてるときに使えない
  var sheet = spreadsheet.getSheetByName('comic_walker');
  var lastrow = sheet.getLastRow();
  // スプレッドシートの全行取得
  const range = sheet.getRange('A1:B' + lastrow);
  //昨日の日付を取得
  var now = new Date();
  var yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
  var year_str = yesterday.getFullYear();
  //月は0~11なので+1する
  var month_str = 1 + yesterday.getMonth();
  var day_str = yesterday.getDate();
  // comic_walkerは0埋めが必要
  const yesterday_str = year_str + "/" + ('0' + month_str).slice(-2) + "/" + ('0' + day_str).slice(-2)

  for(var i = 1; i <= lastrow - 1; i++){
    var l = range.getValues()[i];
    var getUrl = l[1];
    var html = UrlFetchApp.fetch(getUrl).getContentText('UTF-8');
    var update_date = Parser.data(html).from('<span class="comicIndex-date">').to('</span>').iterate()[0].split(" ")[0];
    if (update_date == yesterday_str) {
      toTweet("昨日 " + update_date + " に更新されました。\n" + l[0] + " " + l[1]);
    }
    Utilities.sleep(10000);
  }
}

こちらはComic Walker版のスクリプト、他のサイト用のスクリプトもシートの指定と日付の形式とスクレイピングの要素を指定する部分以外は共通。

notify_update_cwより前は 参考の通りにTwitterAPI周りの処理を記述。

Utilities.sleep(10000);は10秒間何もせずに止める操作で、6分制限のことを考えると一番きつい処理だが、 Comic Walkerのrobot.txtを見ると(見方が合ってるか分からないが)、クローラは10秒間Delayしろって書いているので、それに従っています。

とりあえず数の多いComic Walkerと更新日が分かりやすいヤングエースUPを実装したが、ガルドコミックス等は更新日には無料公開せず、公開約1ヶ月後の無料公開予定日を拾う必要がある。

しかし、無料公開予定日は無料公開されると消えてしまうので、前日に「明日無料公開される予定です」といった感じに通知する必要がある。(実装しても当分はテスト版という扱いをすると思う)

その他ガンガンONLINEもなかなか読んでるので、実装しなければという感じ。(実装よりもスプレッドシートに書くのが面倒で作業が進んでいない、実装はコピペしてちょっと変えるだけなので簡単)

あんまり本数読んでいないところはいつも通り25日にまとめ読みしても良いかもしれん。


参考



  1. 実行時間が17時前後なので、当日をキーにすると18時以降に更新された場合に拾い漏らす。 ↩︎

Avatar
ゲッタ〜
機械学習エンジニア

プログラミングが好き