蔵書管理をやる

Diary日記,蔵書管理

表題の通りです。裏表紙にあるISBNのバーコードをスマホのカメラでスキャンして、読みとった情報から自動で書名や著者名などをスプレッドシートに溜めていってくれるやつをつくりました。

といっても、大したことはしておらず。

AppSheetと25行のGoogle Apps Scriptで作るバーコード蔵書管理アプリ

上記の通りにやったらできた、というだけです。

近いうちに本の整理をする予定があったので、なんかうまいことスプレッドシートに本の情報を溜めていけないかなと考えていたのでした。chatGPTに「作れや」と言ったり、Qiitaの記事を漁りまくったりしましたがどうも上手くいかず、先ほど貼ったリンク先の手順でなんとか動いたので、ほなこれでええかと思いやっています。

こういう感じになる。スマホでバーコードを読みとるだけで記録が完了するのでかなり脳に良い。というか、そのくらい簡略化された作業じゃないと本のデータベース登録なんてやらない。Google Books APIを使っているので、出てこない本はそこそこあって手入力しなきゃいけないが、それでもだいぶ手間が省けている。体感として、2019年以前の漫画は全然ダメ。小説などであればもっと古く遡っても情報を取得できるのだが、漫画はなんかサボってんのか?ってほど情報が引っ張れない。ISBN自体は機能してるし、Google Books APIを直接たたけば情報は出てくるので、なんかバグってうまいこといってないっぽい。しかしそこまで突っ込む技術力はなく。

電撃文庫の情報がタイトルから書影からあらすじまで綺麗に引っ張ってこれたのには感動した。バッカーノ! なんてだいぶ古いのに。

対して、化物語と傷物語と偽物語(上)はうまく処理できず、偽物語(下)からはちゃんとサムネまでヒットした。どういうことなんだ。

…と、そこそこ不具合はありつつも、かなり捗るのでおすすめです。私は何もしていない。全部Qiitaに書いてあるとおりにやりました。先人の知恵に感謝……。一応動く仕組みだけさらっておきます。ISBN, title, subtitle, authors, publisher, publishedDate, description thumbnail, createDateの8項目をヘッダーにこさえたスプレッドシート(シート名をBooksにする)をつくり、App Scriptで「スプレッドシートにISBNが入力されたらGoogle Books APIから書誌情報を引っ張り出してきてスプレッドシートに転記する」というコードをつくります。そのあと、AppSheetから「ISBNをカメラでスキャンして読み込んでスプレッドシートに転記する」アプリを作る、という形です。使うときは、iPhoneのカメラで本の裏表紙をスキャンする→ISBNコードがスプレッドシートに登録される→APIが叩かれ書誌情報が取り込まれる、という手順になります。便利。

あと、これはあったらよさそうだなということで、ついでに蔵書ガチャを作りました。こちらはchatGPTで。珍しくバグも出ずにすんなり上手くいきました。

Slackで「/book」と入力すると、蔵書スプレッドシートの中からランダムで5冊選んで著者名と作品名を教えてくれます。何読めばいいか困ったときにこれに任せるとよさそう。

かなり登録したので比率が漫画寄りになっており、漫画のヒット率が高いですが小説もちゃんと出てきます。

GASのコードは下記。

function doPost(e) {
  const params = parseQuery(e.postData.contents);
  if (params.command === '/book') {
    return postRandomBooksToSlack();
  }
}

function parseQuery(query) {
  const params = {};
  const pairs = query.split('&');
  pairs.forEach(pair => {
    const [key, value] = pair.split('=');
    params[decodeURIComponent(key)] = decodeURIComponent(value.replace(/\+/g, ' '));
  });
  return params;
}


function postRandomBooksToSlack() {
  const spreadsheetUrl = 'https://docs.google.com/spreadsheets/d/蔵書管理スプレッドシートのURL/edit?gid=0#gid=0'; // スプレッドシートのURLを指定
  const sheetName = 'Books'; // シート名を指定

  // スプレッドシート ID を URL から抽出
  const spreadsheetId = getSpreadsheetIdFromUrl(spreadsheetUrl);
  
  // スプレッドシートを取得
  const spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  const sheet = spreadsheet.getSheetByName(sheetName);

  // データの範囲を取得
  const dataRange = sheet.getDataRange();
  const data = dataRange.getValues();

  // ランダムに5つの行を抽出
  const randomRows = getRandomRows(data.length - 1, 5); // ヘッダーを除いた行数を取得

  // 各行のタイトルと著者を取得
  const booksInfo = [];
  randomRows.forEach(row => {
    const title = data[row][1]; // 2列目 (タイトル)
    const author = data[row][3]; // 4列目 (著者)
    booksInfo.push({ title: title, author: author });
  });

  // Slack に投稿
  const message = formatMessageForSlack(booksInfo);
  
  // スラッシュコマンドの応答として返す
  return ContentService.createTextOutput(JSON.stringify({
    response_type: 'in_channel', // チャンネル全体に見えるメッセージ
    text: message
  })).setMimeType(ContentService.MimeType.JSON);
}

// スプレッドシート ID を URL から抽出する関数
function getSpreadsheetIdFromUrl(url) {
  const matches = url.match(/\/d\/([a-zA-Z0-9-_]+)/);
  return matches ? matches[1] : null;
}

// ランダムな行を選択する関数
function getRandomRows(totalRows, numToSelect) {
  const selectedRows = [];
  while (selectedRows.length < numToSelect) {
    const randomRow = Math.floor(Math.random() * totalRows) + 1; // 1行目はヘッダーなので除外
    if (!selectedRows.includes(randomRow)) {
      selectedRows.push(randomRow);
    }
  }
  return selectedRows;
}

// Slack へのメッセージを整形する関数
function formatMessageForSlack(booksInfo) {
  let message = "bookガチャの結果:\n";
  booksInfo.forEach(book => {
    message += `*タイトル:* ${book.title}\n`;
    message += `*著者:* ${book.author}\n\n`;
  });
  return message;
}

デプロイ後、ウェブアプリURLを控えておきます。Slackでアプリをつくって、Slash Commandsで「/book」をつくり、そこにウェブアプリURLをコピペ。スコープは適当に、channels:history、channels:read、chat:write、commands、users:readあたりを付与しておく。多分それでいけるはずです。

バーコードスキャンアプリをつくるのにだいぶ手間取り一日潰しちゃったけど成果物が出たので満足です。