最初にまとめ
- 「小説家になろう」に掲載されている小説の更新通知をLineに送ります。
- 同じテーマはQiita、他サイトで見かけられはしたものの、実現手段が同じでソースコードを公開しているものが無かったため記事にしました。
- 散々言われていますが、「自分個人用に何かを開発したい際にGoogle Apps Script(GAS)はかなり手軽に使えて良い」ということを実感しました。
はじめに
- 現在放送中のアニメ「蜘蛛ですが、なにか?」、かなり人を選ぶと思いますが面白いですよね
- 原作は「小説家になろう」で連載中! (おそらく)最終局面の真っ只中なので、次回更新が待ち遠しくて仕方ない
- ただ、更新通知をメール等で送ってくれる機能がないので、サイトを覗きに行っては、まだ更新されて無かった…と落ち込む毎日…。
- そこで更新を自動チェックする手段を構築します。
- 通知インタフェースとしてはLineを
- 更新確認および通知送信にはGAS + Line Nofityを使います
- 選定理由:お手軽かつ無料
アウトプットイメージ
制作
事前準備
- Line Notifyのアクセストークンを取得します。検索すればいくらでも出てくるので割愛します。
- Google Spread SheetをDB代わりに使うので、下記のようなフォーマットを作成しておきます。
- 通知対象:購読したい小説のNコード(URLの末尾です)を記載
- 最終更新日、最新話数はブランクで大丈夫です
- 複数の小説を購読したい場合には、どんどん下に追加していけばOKです
- GASのタイムゾーンがデフォルトだと北米になっているので、以下手順を参考に日本時間にしておくと良いです。リンク
実装
- 事前準備で用意したスプレッドシートの「ツール」→「スクリプトエディタ」と進んで、以下のコードを貼り付けます。
- 事前準備で取得したLine Notifyのトークンを
YOUR_TOKEN
の部分に貼り付けます。 - その後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);
}
}
}
簡単に解説
- 公式の「なろう小説API」で読みたい小説の情報(最終更新日・話数)を取得
- スプレッドシート上に最終更新日が記録されていない or 記録されているが日付が古い場合、更新されたと判断する
- 更新ありの場合、最終既読~最新話までの話数リストを生成して、各話数までのURLを添えてLineに通知(例えば前回更新で1話まで更新されていて、今回のチェックで3話が見つかったら、2話・3話のURLを送る)
- スプレッドシート上の情報を更新
おわりに
GASを触ってみたいと思っていたので、趣味と実益を兼ねた良い経験になりました。
ちなみに、これが完成してからまだ一度も更新通知は届いていません
参考
同じような機能で稼働中のサービス
ネット小説更新チェック 様:
メールアドレスを登録すると更新通知を送ってくれます
先達の試み
一番最後の記事がやりたいことそのままだったのですが、残念ながらソースコードを見つけられず…。