Zenn
🤖

DifyでLINEにCursorの障害情報を通知するBotを作った

2025/03/28に公開

https://x.com/kinopee_ai/status/1905482269164241339

Cursorの障害通知はSlackにも送れるのですが、会社全体としてCursorを使っているわけではないので追加するのもなぁと思っていたら、「RSS読んでLINEに通知するやつ作ってみたら?」とアドバイスもらったのでやってみることにしました。(LINE Botを作る練習しようかなという話をしていたので)

ちなみに開発練習は我らがChatGPTにサポートしてもらいます。

なお、私はLINE Bot初体験+Difyも自分でつくるのは初めてです。

LINE Developersにログイン

https://developers.line.biz/ja/

まずはなにはともあれログインが必要です。私は今回はじめてログインするので、ビジネスIDも作りました。

プロバイダーをつくる

なんぞ?と思ったら

LINE Developersサイトでは、サービスを提供し、ユーザーの情報を取得する開発者個人、企業、または団体等をサービス提供者(LINEミニアプリではサービス事業主)と呼びます。

https://developers.line.biz/ja/docs/line-developers-console/overview/#provider

つまりここは自分の名前。

Messaging APIを作成

コンソールから作れなくなってる………!

と言うわけで、公式アカウントを作ります。
作れたら、右上の歯車マークから設定画面に遷移し、Messaging APIを開いて、こちらも連携しておきます。

結構ここわかりづらかった………。

Difyで新規アプリを作成する

Difyで何かつくるのも初めてなので、まずは「おうむ返しをするBot」からやってみます。
チャットボット型で新規に「おうむ返しBot」を作ってみます。

あなたはユーザーの発言をそのまま繰り返す、おうむ返しBotです。
ユーザーが何か話しかけたら、必ず「おうむ返し: ○○」のように繰り返してください。

プロンプトはこれ。では話しかけてみます。

おお………簡単………。
では、次はRSSを読み込ませてみます。

APIキーを取得する

ChatGPTはこう言ってるけど、

  1. 左サイドバーの「API アクセス」をクリック
  2. 「APIキーを作成」を押す
  3. 好きな名前(例:RSS通知用)でキーを発行
  4. コピー

APIキーを作成ってドコ???って思ったら右上にいました。ちっちゃいの見落としがちなんだよぉ………。

あと好きな名前とかもなかったです。まあ作れたらなんでもOK。

GASで通知Botの作成

コードはChatGPTがこれを使いなさいと出してくれたやつ。
何してるか把握したかったので、「何してるか理解しておきたいから解説して」って頼んで、コメントつけてもらいました。

// 1. 定数設定(RSSとDifyのキー)
const RSS_URL = 'https://status.cursor.com/history.rss';
const DIFY_API_KEY = '<ここにAPIキー>';
const CACHE_KEY = 'lastPubDate'; // 前回通知したRSSの日時を保存するキー

// 2. メイン処理:RSSをチェックして通知する
function checkAndNotifyNewRSS() {
  // RSSデータを取得
  const response = UrlFetchApp.fetch(RSS_URL);
  const xml = response.getContentText();

  // XMLをパースして、RSSの一番新しい投稿を取り出す
  const document = XmlService.parse(xml);
  const items = document.getRootElement()
    .getChild('channel')
    .getChildren('item');
  const latest = items[0];

  // 最新の情報を取り出す
  const title = latest.getChildText('title');
  const link = latest.getChildText('link');
  const pubDate = latest.getChildText('pubDate'); // 公開日時(例:Fri, 28 Mar 2025 06:00:00 GMT)

  // 前回の通知日時と比較(キャッシュに保存してある)
  const cache = CacheService.getScriptCache();
  const lastPubDate = cache.get(CACHE_KEY);

  if (lastPubDate === pubDate) {
    Logger.log('新着なし');
    return; // 同じなら何もしないで終了!
  }

  // 新着ならメッセージを作って通知
  const message = `RSS通知:\n${title}\n${pubDate}\n${link}`;
  sendToDify(message);

  // 今回通知した日時をキャッシュに保存(6時間)
  cache.put(CACHE_KEY, pubDate, 21600);
}

// 3. Difyに通知を送る関数
function sendToDify(queryText) {
  const url = 'https://api.dify.ai/v1/chat-messages';

  const payload = {
    inputs: {}, // 入力パラメータ(今回は使ってない)
    query: queryText, // ユーザーからの質問のようなもの
    response_mode: 'blocking', // 「streaming」じゃなく一括で返してもらう
    conversation_id: null // 会話を新しく始める(履歴なし)
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    headers: {
      Authorization: 'Bearer ' + DIFY_API_KEY,
    },
    payload: JSON.stringify(payload),
  };

  const res = UrlFetchApp.fetch(url, options);
  Logger.log(res.getContentText()); // レスポンスをログに出す(開発用)
}

コードを追加したら、定期実行を設定。

これで実行してみると、以下のエラーが出ました。

Exception: Request failed for https://api.dify.ai returned code 400. Truncated server response: {"code": "invalid_param", "message": "Arg user must be provided.", "status": 400}
 (use muteHttpExceptions option to examine full response)

Difyのユーザー情報がない。追加方法もChatGPTがサポートしてくれました。

function sendToDify(queryText) {
  const url = 'https://api.dify.ai/v1/chat-messages';

  const payload = {
    user: 'rss-bot', // ←追加!任意の識別子でOK
    inputs: {},
    query: queryText,
    response_mode: 'blocking',
    conversation_id: null
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    headers: {
      Authorization: 'Bearer ' + DIFY_API_KEY,
    },
    payload: JSON.stringify(payload),
  };

  const res = UrlFetchApp.fetch(url, options);
  Logger.log(res.getContentText());
}

user って何?

  • Difyの中で「誰が話しかけたか?」を識別するための値
  • 実際のユーザーIDじゃなくてもOK(今回は rss-bot とかでOK!)

とのこと。これを追加したら無事GASは動作しました!

これができたので、Difyのログを確認すると………

見れてる!新着がないとここに出てこないので、GAS側でキャッシュクリアの関数入れておきました。

function resetCache() {
  CacheService.getScriptCache().remove('lastPubDate');
}

LINEで通知する仕組みをつくる

ついにLINEと連携!まずは自分のユーザーIDをとってくる。

function doPost(e) {
  const json = JSON.parse(e.postData.contents);
  const userId = json.events[0].source.userId;
  Logger.log('User ID: ' + userId);

  // 確認用におうむ返ししてみる(任意)
  const replyToken = json.events[0].replyToken;
  const message = {
    replyToken: replyToken,
    messages: [{
      type: 'text',
      text: 'こんにちは!User ID をログに記録しました 🙌',
    }],
  };

  UrlFetchApp.fetch('https://api.line.me/v2/bot/message/reply', {
    method: 'post',
    contentType: 'application/json',
    headers: {
      Authorization: 'Bearer <LINEのアクセストークン>',
    },
    payload: JSON.stringify(message),
  });
}

これを入れて、ウェブアプリとしてデプロイ。今は実行する関数選ばなくてもdoPost(e) が実行されるらしい。(そもそも実行する関数を選ぶセレクトボックスみたいなのがなかった)

デプロイ設定したら、次はLINEのWebhook URLにGASのURLを追加します。

「検証」をクリックして成功だったらOK。今回は「Webhookの利用」だけ有効化しました。

しかし…LINEでメッセージしてもログが見れない

メッセージしたら「こんにちは!User ID をログに記録しました 🙌」が表示されて、ログからユーザーIDが確認できるはずでした。

でも、肝心のGASにログがない!そもそも表示のさせ方がわからない。実行ログはあるけど、それはGAS上で実行させたログ。
どうもUIが変わってから表示ができないっぽい………………?

と言うわけで、もうLINEのメッセージでユーザーIDをとることにしました。

ついにLINEに障害情報を届ける!

最終的なGASのコードはこちら。

// 1. 定数設定(RSSとDifyのキー)
const RSS_URL = 'https://status.cursor.com/history.rss';
const DIFY_API_KEY = '<DIFY_API_KEY>';
const CACHE_KEY = 'lastPubDate'; // 前回通知したRSSの日時を保存するキー

// 2. メイン処理:RSSをチェックして通知する
function checkAndNotifyNewRSS() {
  // RSSデータを取得
  const response = UrlFetchApp.fetch(RSS_URL);
  const xml = response.getContentText();

  // XMLをパースして、RSSの一番新しい投稿を取り出す
  const document = XmlService.parse(xml);
  const items = document.getRootElement()
    .getChild('channel')
    .getChildren('item');
  const latest = items[0];

  // 最新の情報を取り出す
  const title = latest.getChildText('title');
  const link = latest.getChildText('link');
  const pubDate = latest.getChildText('pubDate'); // 公開日時(例:Fri, 28 Mar 2025 06:00:00 GMT)

  // 前回の通知日時と比較(キャッシュに保存してある)
  const cache = CacheService.getScriptCache();
  const lastPubDate = cache.get(CACHE_KEY);

  if (lastPubDate === pubDate) {
    Logger.log('新着なし');
    return; // 同じなら何もしないで終了!
  }

  // 新着ならメッセージを作って通知
  const message = `RSS通知:\n${title}\n${pubDate}\n${link}`;
  sendToDify(message);

  // 今回通知した日時をキャッシュに保存(6時間)
  cache.put(CACHE_KEY, pubDate, 21600);
}

// 3. Difyに通知を送る関数
function sendToDify(queryText) {
  const url = 'https://api.dify.ai/v1/chat-messages';

  const payload = {
    user: 'rss-bot',
    inputs: {}, // 入力パラメータ(今回は使ってない)
    query: queryText, // ユーザーからの質問のようなもの
    response_mode: 'blocking', // 「streaming」じゃなく一括で返してもらう
    conversation_id: null // 会話を新しく始める(履歴なし)
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    headers: {
      Authorization: 'Bearer ' + DIFY_API_KEY,
    },
    payload: JSON.stringify(payload),
  };

  const res = UrlFetchApp.fetch(url, options);
  Logger.log(res.getContentText()); // レスポンスをログに出す(開発用)
  const data = JSON.parse(res.getContentText());

  const replyText = data.answer;
  sendToLine(replyText);
}

function sendToLine(text) {
  const LINE_TOKEN = '<LINE_TOKEN>';
  const USER_ID = '<USER_ID>';

  const payload = {
    to: USER_ID,
    messages: [{
      type: 'text',
      text: text,
    }],
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    headers: {
      Authorization: 'Bearer ' + LINE_TOKEN,
    },
    payload: JSON.stringify(payload),
  };

  const res = UrlFetchApp.fetch('https://api.line.me/v2/bot/message/push', options);
  Logger.log('LINE通知結果: ' + res.getContentText());
}

function resetCache() {
  CacheService.getScriptCache().remove('lastPubDate');
}

function doPost(e) {}

キャッシュを消し、GAS上で障害情報を取得する関数を実行!

できた〜〜〜〜〜〜〜〜〜!!!!なんか文面おかしいけど!


ChatGPTもお祝い。ちなみにこんな陽キャなプロンプトは仕込んでません

通知のテキストが気に入らないのでプロンプトを変える

あなたはCursorの障害情報をLINEで自動通知するアシスタントです。

ユーザーが何かを発言したわけではなく、RSSから自動取得した情報を元に、  
Cursorで障害が発生したことを簡潔に、わかりやすく日本語で伝えてください。

次の要素を含めて構成してください:

・障害の概要(title)  
・発生日時(pubDate)  
・詳細リンク(link)

導入文として「Cursorで障害が発生しました。」などをつけて、  
全体を1つのまとまった通知文にしてください。

文体は落ち着いたトーンで、必要以上に謝罪などは含めず、情報を正確に伝えてください。

プロンプト整えるだけでずいぶんかわりました。すごいなぁ………。

「障害情報ある?」って聞いたら答えてくれるようにする

今は一方的な通知にしてるけど、自分が聞いた時にも答えてくれるようにしたい!
と言うわけで、またプロンプトを変更します。(ChatGPTによる添削あり)

あなたはCursorの障害情報をLINEで自動通知するアシスタントです。

これはユーザーからの質問ではなく、GASから送られてきたRSSデータを元に自動で通知するためのメッセージです。

シナリオは以下のどちらかです:

1. RSSに新しい障害情報がある場合  
→ 以下の要素を含めた簡潔な通知文を作成してください:

・障害の概要(title)  
・発生日時(pubDate)  
・詳細リンク(link)

文の冒頭に「Cursorで障害が発生しました。」などの導入文を入れてください。

2. RSSに新着情報がない場合  
→ 「現在、新しい障害は発生していません」といった自然な文にしてください。

全体として落ち着いた丁寧なトーンで、必要以上の謝罪や敬語は不要です。  
GASから送られたテキストに応じて適切な返答を生成してください。

完成!!!!!!!!!!!!!!!!!!!!!!
これ、作業してない時間も含めたら6時間くらいでできました。やば。

Dify+LINE Botなかなか面白いですね。次は食事管理Botでも作ってみようかな〜。

Discussion

ログインするとコメントできます