arXivの最新論文をSlackに自動通知する!Google Apps Scriptで作るシンプルBot2

IT・プログラミング

重複投稿を防ぐしくみ

前回までで、Google Apps Script(GAS)とSlackのWebhookを連携し、arXivの最新論文情報を自動投稿するBotが完成しました。

ただし、このままだと同じ論文を何度もSlackに投稿してしまう可能性があります。

今回はこの問題を解決するために、「投稿済みの論文IDを記録し、次回以降はスキップする」という仕組みを実装します。

投稿済みの論文をどうやって判別する?

arXivのフィードには、すべての論文に固有の id(例:http://arxiv.org/abs/2405.12345)があります。

この idスプレッドシートに保存しておけば、「前回までに投稿済みかどうか」を判別できます。

スプレッドシートに「Posted」シートを追加

シート名:Posted(列は1列のみ)

A列(arXiv ID)
http://arxiv.org/abs/2405
http://arxiv.org/abs/2405

このシートに投稿済みの論文IDを蓄積していきます。

getPostedIds() ― 投稿済みID一覧を取得

まずは記録されているIDを配列で取得する関数です。
ただし、シートが空の場合はエラーになるので、例外を防ぐチェックを入れます。

function getPostedIds() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Posted');
  if (!sheet) return [];

  const lastRow = sheet.getLastRow();
  if (lastRow === 0) return [];

  return sheet.getRange(1, 1, lastRow, 1)
              .getValues()
              .map(row => row[0]);
}

savePostedId() ― 投稿が終わったらIDを記録

新しい論文を投稿した後は、Posted シートにIDを追加しておきましょう。

function savePostedId(arxivId) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Posted');
  if (!sheet) return;

  sheet.appendRow([arxivId]);
}

postArxivToSlack() に統合する

メインの投稿関数を以下のように改良しましょう。

function postArxivToSlack() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();

  // Posted シートがなければ作る
  if (!ss.getSheetByName('Posted')) {
    ss.insertSheet('Posted');
  }

  const settings = getSettings();
  const SLACK_WEBHOOK_URL = settings['SLACK_WEBHOOK_URL'];
  const CATEGORY = settings['CATEGORY'] || 'cs.CL';
  const MAX_RESULTS = parseInt(settings['MAX_RESULTS'], 10) || 5;

  const postedIds = getPostedIds();

  const url = `http://export.arxiv.org/api/query?search_query=cat:${CATEGORY}&sortBy=submittedDate&sortOrder=descending&max_results=${MAX_RESULTS}`;
  const response = UrlFetchApp.fetch(url);
  const xml = response.getContentText();
  const doc = XmlService.parse(xml);
  const entries = doc.getRootElement().getChildren('entry', doc.getRootElement().getNamespace());

  entries.forEach(entry => {
    const ns = doc.getRootElement().getNamespace();
    const arxivId = entry.getChildText('id', ns).trim();

    if (postedIds.includes(arxivId)) {
      Logger.log(`Skip duplicate: ${arxivId}`);
      return;
    }

    const title = entry.getChildText('title', ns).trim();
    const summary = entry.getChildText('summary', ns).trim();

    const payload = {
      text: `*${title}*\n${summary}\n<${arxivId}|Read more>`
    };

    UrlFetchApp.fetch(SLACK_WEBHOOK_URL, {
      method: 'post',
      contentType: 'application/json',
      payload: JSON.stringify(payload)
    });

    savePostedId(arxivId); // 記録!
  });
}

動作確認

  1. スクリプトを実行(初回はすべて投稿される)
  2. Posted シートにIDが記録される
  3. 次回以降、同じIDは投稿されずスキップされる

おまけ:投稿済みデータをリセットしたいときは?

Posted シートの中身を手動で削除すればOKです。空になった状態でスクリプトを動かすと、再度すべて投稿されます。

まとめ

このパートでは、**同じ論文を何度もSlackに投稿しないための「重複防止機能」**を実装しました。

  • Posted シートに投稿済みIDを記録
  • getPostedIds() で過去投稿をチェック
  • savePostedId() で新規投稿を保存
  • シートが空でも落ちない安全設計に!

次回は、投稿メッセージを翻訳付きにしたり、キーワードフィルターをつけたり、より便利な機能を紹介していきます。

タイトルとURLをコピーしました