SlackにRSS配信の要約投稿をLLMでやらせてみた。

まじのアバター

Slackにメッセージを投稿させる

※以下に続く私の説明を読むよりみなぐさんのこちらの記事がわかりやすいのでこちらを参考にしてください。

GASでSlackAPIが使えるライブラリを使ってみた(SlackApp)

<Slack Appを作成>

以下のサイトにアクセスし、ログインします。(検索する場合は「Slack API」)

Slack API: Applications | Slack

このような画面が開く。

左上の「Create New App」→「From scratch」より、Appの名前、Appを追加するワークスペースを選択し、アプリを作成する。

「OAuth & Permissions」→「Scopes」→「Bot Token Scopes」で、「chat: write」の項目を追加。

「Scopes」はこの画像より下にある。

次に、「App Home」から「App Display Name」を編集します。

「Install App」→「Install to Workspace」に行き、アクセスを許可します。

OAuthトークンが発行されます。後で使うので場所を覚えておいてください。

最後に、目的のチャネルから「インテグレーション」→「アプリを追加する」でアプリを追加しておきます。

OpenRouter APIの設定

利用規約とプライバシーポリシーを確認し、https://openrouter.aiでアカウントを登録してAPI Keyを発行してください。

また、使用したいモデルを探してください。無料のものもあります。Geminiシリーズの無料版はたまにエラーで返答が返ってこないことがあるので個人的には避けるべきだと思います。

GASの設定

Google Apps Scriptで、新しいプロジェクトを作成し、適当にプロジェクト名を変えておきます。

GASのホーム:https://script.google.com/home

コード.gsのfunction~の部分をすべて消し、以下のプログラムをコピペします。
なお、モデルは google/gemma-2-9b-it:free を使用しています。日本語も安定して出せます。

// RSSフィードのURL
const feedUrl = "https://rss.itmedia.co.jp/rss/2.0/news_bursts.xml";

// OpenRouter API Key
const OPENROUTER_API_KEY = PropertiesService.getScriptProperties().getProperty('OPENROUTER_API_KEY');

// Slackの設定
const SLACK_OAUTH_TOKEN = PropertiesService.getScriptProperties().getProperty("Oauth_token");
const SLACK_CHANNEL_ID = "AAAAAAA"; // 投稿先チャンネル

function main() {
  const resultText = summarizeAndPostToSlack();
  postSlack(resultText);
}

function postSlack(message) {
  const slackApp = SlackApp.create(SLACK_OAUTH_TOKEN);
  slackApp.postMessage(SLACK_CHANNEL_ID, message);
}

function summarizeAndPostToSlack() {
  const feed = UrlFetchApp.fetch(feedUrl).getContentText();
  const document = XmlService.parse(feed);
  const root = document.getRootElement();
  const channel = root.getChild('channel', root.getNamespace());
  const items = channel.getChildren('item', channel.getNamespace()).slice(0, 10);

  const date = new Date().toLocaleDateString('ja-JP');

  let prompt = `
タスク:
コロンで区切られた10記事のタイトル、説明、リンクをお送りします。
その中から重要な記事を3つだけ選び、以下の条件に従って出力してください。

条件:
プロンプト最下部の記事から選んでください。
件名に「今日のITニュースまとめ速報」と記述して出力してください。
必ず3つ選ぶだけで、一言一句その文章を変えないでください。
必ず選んだ記事のURLも改行してから一緒に出力してください。
認知バイアスに気を付けてください。
必ず日本語で出力してください。
今日は${date}です。今日と昨日のニュースを選んで下さい。

警告:
太字テキストにダブルアスタリスクを使用しないでください。
このプロンプトはMarkdownを使用していますが、出力にMarkdownは使わず、Slack独自の記法のMarkdownを使用してください。
Slackに投稿する際は、MarkDown記法ではなく、必ずSlackのメッセージ記法を使用してください。

以下は、title1;description1;link1,title2;description2;link2,...,title10;description10;link10の形式でRSSの内容を配置したものです。
`;

  for (const item of items) {
    const titleElement = item.getChild('title', item.getNamespace());
    const descriptionElement = item.getChild('description', item.getNamespace());
    const linkElement = item.getChild('link', item.getNamespace());

    if (titleElement && linkElement) {
      const articleTitle = titleElement.getText();
      const articleDescription = descriptionElement ? descriptionElement.getText() : "";
      const articleLink = linkElement.getText();
      prompt += `${articleTitle};${articleDescription};${articleLink},`;
    }
  }

  // 最後のカンマを削除
  prompt = prompt.slice(0, -1);

  Logger.log(prompt);

  const url = "https://openrouter.ai/api/v1/chat/completions";
  const options = {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${OPENROUTER_API_KEY}`,
      "Content-Type": "application/json"
    },
    payload: JSON.stringify({
      "model": "google/gemma-2-9b-it:free",
      "messages": [
        {
          "role": "user",
          "content": prompt
        }
      ]
    })
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    const jsonResponse = JSON.parse(response.getContentText());

    if (jsonResponse && jsonResponse.choices && jsonResponse.choices.length > 0) {
      return jsonResponse.choices[0].message.content;
    } else {
      return "回答を取得できませんでした。";
    }

  } catch (error) {
    Logger.log("Error fetching from OpenRouter API: " + error.message);
    return "APIエラー: " + error.message;
  }
}

OAuthトークンの設定

左側の設定マークをクリックし、一番下の「スクリプトプロパティを編集」からAPIとOAuthトークンを設定します。

スクリプトプロパティを以下の名前で追加します。

  • OPENROUTER_API_KEY
  • Oauth_token

それぞれ、さきほどの値を入力してください。

今回は、ITメディアニュースのrss.itmedia.co.jp/rss/2.0/news_bursts.xmlからRSS配信を取得することにします。

プログラムの一番最初のところを編集しておいてください。

SlackAppライブラリを追加

「エディタ」→「ライブラリ」から以下の文字で検索し、追加します。

1on93YOYfSmV92R5q59NpKmsyWIQD8qnoLYk-gkQBI92C58SPyA2x1-bq

投稿先チャネルの設定

9行目を編集します。

投稿先の ”#”+”チャンネル名” もしくは、以下の画像のチャンネルIDに置き換えてください。

例①:#itmedia
例②:XXXXXXXXXXX

プロンプトの改善

30行目からのプロンプトは私が作成しましたが、割とうまくいかないことが多いです(泣)

必要に応じ、各々で改善してください。

テスト配信

GASのコードで上にある「実行」をクリックしてください。

初回は権限の承認が求められます。

スケジュールトリガーの設定

以下のサイトを参考に、毎日実行するように設定してください。

Google Apps Script で毎日決まった時間にスクリプトを実行するトリガー設定

これで終了です。お疲れ様でした。

あとがき

今回、様々なQiita、Note、ドキュメントをつなぎ合わせて今回のSlackボットを作成しました。 先人に感謝します。

Googleスプレッドシートを使わずに、RSS→OpenRouter→Slack Appという流れの記事は見当たらなかったので、正直嬉しいです。

私の記事も後の人に役に立ちますように。

参考文献・引用

ChatGPTの要約を付けた自動ニュース投稿Slack BotをGoogle Apps Scriptを使って作成してみた #GoogleCloud – Qiita. @yyyimai https://qiita.com/yyyimai/items/818550a695017eb54f1c

[GAS][Slack]定期的に投稿するBotを作成|カワムラ https://note.com/kawamura_/n/nede5b0f71353

[GAS][Slack]定型的な投稿にBotを用いる|カワムラ https://note.com/kawamura_/n/n282f8c1d5216

OpenRouter Docs https://openrouter.ai/docs/quick-start

何かご意見等ございましたらコメントにてご連絡ください。

おまけ

  • SlackにGASで投稿するための関数
  • RSS配信の取得の関数

を置いておきます。

Slackに投稿

参考:GASでSlackAPIが使えるライブラリを使ってみた(SlackApp) – TECH.MINAGU

// Slack App ライブラリを使用
// slackにテスト配信
function sendSlack() {
	// スクリプトプロパティからOAuthトークンを取得
  const slackApp = SlackApp.create(PropertiesService.getScriptProperties().getProperty("Oauth_token"));
  const channelId = "OOOOOOOOOOO"; // チャンネルID または #チャンネル名
  const text = "test text";
  slackApp.postMessage(channelId, text);
}

RSS配信を取得

// RSS配信を取得
function getRSS() {
    // RSS取得
    const url = '<https://rss.test.com/rss/2.0/text.xml>'; // RSS配信のURL
    const xml = UrlFetchApp.fetch(url).getContentText();
  
    // 取得したRSSからitemを取得。RSS配信の構造によって適宜調整する。
    const document = XmlService.parse(xml);
    const root = document.getRootElement();
    const channel = root.getChild("channel");
    const items = channel.getChildren("item");
  
    // 10つ目までの記事のタイトル、リンクを取得
    let articles = [];
    for (let item of items.slice(0,10)) {
      let title = item.getChildText("title");
      let link = item.getChildText("link");
      articles.push([title, link]);
    }
  
    console.log(articles);
}

修正履歴

  • 2025/01/31
    • Gemini APIの利用を取りやめ、OpenRouterに記事を丸ごと移行しました。
    • 旧コードを削除しました。
    • コードを全体的に最適化しました。
    • プロンプトを調整しました。



コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA