LoginSignup
7
1

More than 3 years have passed since last update.

GAS活用:小説家になろう更新通知を受け取る

Posted at

最初にまとめ

  • 「小説家になろう」に掲載されている小説の更新通知をLineに送ります。
  • 同じテーマはQiita、他サイトで見かけられはしたものの、実現手段が同じでソースコードを公開しているものが無かったため記事にしました。
  • 散々言われていますが、「自分個人用に何かを開発したい際にGoogle Apps Script(GAS)はかなり手軽に使えて良い」ということを実感しました。

はじめに

  • 現在放送中のアニメ「蜘蛛ですが、なにか?」、かなり人を選ぶと思いますが面白いですよね :spider:
  • 原作は「小説家になろう」で連載中! (おそらく)最終局面の真っ只中なので、次回更新が待ち遠しくて仕方ない
  • ただ、更新通知をメール等で送ってくれる機能がないので、サイトを覗きに行っては、まだ更新されて無かった…と落ち込む毎日…。
  • そこで更新を自動チェックする手段を構築します。
    • 通知インタフェースとしてはLineを
    • 更新確認および通知送信にはGAS + Line Nofityを使います
    • 選定理由:お手軽かつ無料 :moneybag:

アウトプットイメージ

Line画面イメージ

制作

事前準備

  • Line Notifyのアクセストークンを取得します。検索すればいくらでも出てくるので割愛します。
  • Google Spread SheetをDB代わりに使うので、下記のようなフォーマットを作成しておきます。
    • 通知対象:購読したい小説のNコード(URLの末尾です)を記載
    • 最終更新日、最新話数はブランクで大丈夫です
    • 複数の小説を購読したい場合には、どんどん下に追加していけばOKです
  • GASのタイムゾーンがデフォルトだと北米になっているので、以下手順を参考に日本時間にしておくと良いです。リンク

image.png

実装

  1. 事前準備で用意したスプレッドシートの「ツール」→「スクリプトエディタ」と進んで、以下のコードを貼り付けます。
  2. 事前準備で取得したLine NotifyのトークンをYOUR_TOKENの部分に貼り付けます。
  3. その後GAS上のトリガー機能で、実行する関数「main」、イベント「時間ベース」として、お好みの時間間隔を設定すれば完成。お手軽ですね。
const NAROU_ENDPOINT = "https://api.syosetu.com/novelapi/api/?out=json&ncode=";
const LINE_ENDPOINT = "https://notify-api.line.me/api/notify";
const LINE_TOKEN = "YOUR_TOKEN";

// スプレッドシートの設定
const COL_ID = 1;
const COL_UPDATE = 2;
const COL_TOTAL = 3;
const ROW_FIRST_CONTENT = 2;

const sheet = SpreadsheetApp.getActiveSheet();

function getNovelInfo(id) {
  const resp = UrlFetchApp.fetch(NAROU_ENDPOINT + id);
  const parsed_resp = JSON.parse(resp.getContentText());
  // [0]には取得した総ノベル数、[1]以降に各ノベル情報が返ってくる。必要なのは[1]だけ。
  return parseNovel(parsed_resp[1])
}

function parseNovel(novel) {
  const title = novel.title;
  const lastUpdate = new Date(novel.general_lastup.replace("-", "/"));
  const total = novel.general_all_no;
  return { "title": title, "lastUpdate": lastUpdate, "total": total }
}

function overwriteSheet(idx, novel) {
  sheet.getRange(idx, COL_UPDATE).setValue(novel.lastUpdate);
  sheet.getRange(idx, COL_TOTAL).setValue(novel.total);
}

function getUnread(idx, novel) {
  // 最終既読~最新話までの話数リストを返す。
  // ただし最終既読の記録が無かったり、記録値が不正だった場合のエラー処理を含む。
  try {
    const end = novel.total;

    // 最終既読が記録されていなければ、最新話だけを通知する
    if (sheet.getRange(idx, COL_TOTAL).isBlank()) {
      return [end]
    }
    const start = sheet.getRange(idx, COL_TOTAL).getValue() + 1;

    // 最終既読記録値が不正なら、最新話だけを通知する
    if (start >= end) {
      console.warn("Saved total would not be correct. Overwrote it by the new value.");
      return [end]
    }

    return [...Array((end - start + 1))].map(function (_, i) {
      return start + i
    })
  }
  catch (error) {
    console.error(error);
  }
}

function makePlainMailBody(idx, novel) {
  const id = sheet.getRange(idx, COL_ID).getValue();
  const url = "https://ncode.syosetu.com/" + id + "/"
  const unread = getUnread(idx, novel);

  let mailBody = `\n${novel.title} が更新されました。\n\n`;
  for (let i of unread) {
    mailBody += `第${i}話\n${url}${String(i)}\n`;
  }
  return mailBody
}

function lineNotify(message) {
  const req = {
    "method": "post",
    "headers": { "Authorization": "Bearer " + LINE_TOKEN },
    "payload": { "message": message }
  };
  UrlFetchApp.fetch(LINE_ENDPOINT, req);
}

function notifyUpdate(idx, novel) {
  lineNotify(makePlainMailBody(idx, novel));
  overwriteSheet(idx, novel);
}

function main() {
  const lastRow = sheet.getLastRow();

  for (let i = ROW_FIRST_CONTENT; i <= lastRow; i++) {
    const id = sheet.getRange(i, COL_ID).getValue()

    try {
      const novel = getNovelInfo(id);
      const isFirstCheck = sheet.getRange(i, COL_UPDATE).isBlank();

      if (isFirstCheck) {
        console.log("First check");
        notifyUpdate(i, novel);
        continue
      }

      const prevUpdate = new Date(sheet.getRange(i, COL_UPDATE).getValue());
      const isUpdated = (prevUpdate.getTime() - novel.lastUpdate.getTime() < 0 ? true : false);

      if (isUpdated) {
        console.log("found update");
        notifyUpdate(i, novel);
      } else {
        console.log("no update")
      }

    }
    catch (error) {
      console.error(error);
    }
  }
}

簡単に解説

  1. 公式の「なろう小説API」で読みたい小説の情報(最終更新日・話数)を取得
  2. スプレッドシート上に最終更新日が記録されていない or 記録されているが日付が古い場合、更新されたと判断する
  3. 更新ありの場合、最終既読~最新話までの話数リストを生成して、各話数までのURLを添えてLineに通知(例えば前回更新で1話まで更新されていて、今回のチェックで3話が見つかったら、2話・3話のURLを送る)
  4. スプレッドシート上の情報を更新

おわりに

GASを触ってみたいと思っていたので、趣味と実益を兼ねた良い経験になりました。
ちなみに、これが完成してからまだ一度も更新通知は届いていません :innocent:

参考

同じような機能で稼働中のサービス

ネット小説更新チェック 様:
メールアドレスを登録すると更新通知を送ってくれます

先達の試み

一番最後の記事がやりたいことそのままだったのですが、残念ながらソースコードを見つけられず…。

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1