3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

image.png

はじめに

こちらは Qiita全国学生対抗戦 Advent Calendar 2025 の10日目の記事です!
(空いてたので埋めさせていただきました🙏)

兼、株式会社ビザスク - VisasQ Inc. Advent Calendar 2025 の10日目の記事です!
(tips: 東京通信大学 では会社員をしながら学位を取得できます!)

ご注意

仕様上、キビキビ最新情報を取得したい用途には不向きです🙏
就活などで気になってる会社の株価を程よく把握する程度の用途などでオススメかもです!

※余談: 作りすぎにもご注意
高校時代から使っている友だち同士で作った Slack Workspace は↑の例のように通知まみれ
(院進した方はともかく殆ど就職したのでそろそろ止めても良いかなと思いつつ備忘録)

最終的にできたコード

  • 使い方 (忙しい人向け)
    1. スプレッドシートを用意する
    2. A1セルGoogleFinance関数 を使って投稿したい情報(株価や為替)が表示されるようにする
    3. そのまま Google Apps Script のエディタを開き下記コードをコピペ
    4. payload に入ってきた値を投稿するだけの Slack Workflow の Webhook URL を用意する
      1. Webhook URL イベントの Slack Workflow 作成手順例(ご参加までに!): すぐはじめられるSlackチャンネル・メンショングループ招待の自動化 の step2
    5. properties.getProperty('SLACK_WEBHOOK_URL'); が動くよう前のステップで得た URL を SLACK_WEBHOOK_URL という key でセット
    6. postPriceToSlack を実行し動作確認
      1. 土日祝に通知されても微妙なので Google Calendar で休日情報を把握するなどの都合上いろいろアクセスきかれますので必要に応じて承認してください :pray:
    7. 動作問題なければ createAndManageTriggers を実行し概ね1時間間隔で自動投稿されるよう自動セットアップ
/**
 * スプレッドシートのA1セルの値を、指定された日時にSlackに投稿します。
 * - 平日の指定時間のみ動作します。
 * - 土日、祝日、年末年始(12/29~1/3)は自動的にスキップします。
 */
function postPriceToSlack() {
  const now = new Date();
  const timeZone = 'Asia/Tokyo'; // タイムゾーンを日本に設定
  const formattedDate = Utilities.formatDate(now, timeZone, 'yyyy-MM-dd');
  
  // --- 実行日時のバリデーション ---
  
  // 1. 土日かどうかを判定
  const dayOfWeek = now.getDay();
  if (dayOfWeek === 0 || dayOfWeek === 6) { // 0:日曜, 6:土曜
    console.log(`本日は土日(${formattedDate})のため、処理をスキップしました。`);
    return;
  }

  // 2. 年末年始(12/29~1/3)かどうかを判定
  const month = now.getMonth() + 1; // getMonth()は0-11を返すため+1
  const date = now.getDate();
  if ((month === 12 && date >= 29) || (month === 1 && date <= 3)) {
    console.log(`本日は年末年始(${formattedDate})のため、処理をスキップしました。`);
    return;
  }

  // 3. 祝日かどうかを判定
  const calendarId = 'ja.japanese#holiday@group.v.calendar.google.com';
  const calendar = CalendarApp.getCalendarById(calendarId);
  const events = calendar.getEventsForDay(now);
  if (events.length > 0) {
    console.log(`本日は祝日「${events[0].getTitle()}」(${formattedDate})のため、処理をスキップしました。`);
    return;
  }
  
  // --- ここからSlackへの投稿処理 ---

  console.log(`営業日のため、Slackへの投稿処理を実行します。(${formattedDate})`);

  const properties = PropertiesService.getScriptProperties();
  const slackWebhookUrl = properties.getProperty('SLACK_WEBHOOK_URL');

  if (!slackWebhookUrl) {
    console.error('エラー: SlackのWebhook URLがスクリプトプロパティに設定されていません。');
    return;
  }

  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const price = sheet.getRange('A1').getValue();

  const payload = {
    'price': String(price)
  };

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

  try {
    const response = UrlFetchApp.fetch(slackWebhookUrl, options);
    console.log('Slackへの投稿に成功しました。レスポンスコード: ' + response.getResponseCode());
  } catch (e) {
    console.error('Slackへの投稿に失敗しました: ' + e.toString());
  }
}

/**
 * 投稿用のトリガーを自動で設定する管理用の関数です。
 * 実行すると、既存のトリガーをすべて削除し、指定された時間に新しいトリガーを再設定します。
 * この関数は、設定を変更したい場合に手動で一度だけ実行してください。
 */
function createAndManageTriggers() {
  // 1. このプロジェクトに設定されている既存のトリガーをすべて削除する
  const allTriggers = ScriptApp.getProjectTriggers();
  for (const trigger of allTriggers) {
    ScriptApp.deleteTrigger(trigger);
  }
  console.log(`${allTriggers.length}件の既存トリガーを削除しました。`);

  // 2. 投稿を実行したい時間(時と分)を配列で指定する
  const executionTimes = [
    // --- 午前 ---
    { hour: 9, minute: 0 },   // 9:00頃
    { hour: 10, minute: 0 },  // 10:00頃
    { hour: 11, minute: 0 },  // 11:00頃
    { hour: 11, minute: 30 }, // 11:30頃 ★追加
    // --- 午後 ---
    { hour: 12, minute: 30 }, // 12:30頃
    { hour: 13, minute: 30 }, // 13:30頃
    { hour: 14, minute: 30 }, // 14:30頃
    { hour: 15, minute: 30 }  // 15:30頃
  ];

  // 3. 指定された時間ごとにトリガーを新規作成する
  for (const time of executionTimes) {
    ScriptApp.newTrigger('postPriceToSlack')
      .timeBased()
      .atHour(time.hour)
      .nearMinute(time.minute) // 指定した「分」の前後15分の範囲で実行
      .everyDays(1)
      .create();
    console.log(`${time.hour}${time.minute}分頃のトリガーを作成しました。`);
  }
  
  SpreadsheetApp.getUi().alert('トリガーの更新が完了しました。毎日、平日の指定時間に実行されます。');
}

解説:情報源

上記のとおり、スプレッドシートの便利な関数で株価や為替を取得し A1 セルに入れて、その内容を Google Apps Script で取得してます :chart:

株価

例: Google

=GOOGLEFINANCE("NASDAQ:GOOG")

※ご注意: 日本株の場合は少し異なります (↓ご参考までに)

例: ビザスク

=IMPORTXML("https://www.google.com/finance/quote/4490:TYO","//*[@class=""YMlKec fxKbKc""]")

為替

例: ドル円

=GOOGLEFINANCE("CURRENCY:USDJPY")

解説:自動投稿の仕組み

以下のとおり、Slack Workflow の Webhook URL を用いた仕組みとなっております!
わざわざ Slack アプリを作ったり細かい権限の設定が不要です✨️

  const payload = {
    'price': String(price)
  };

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

  try {
    const response = UrlFetchApp.fetch(slackWebhookUrl, options);
    console.log('Slackへの投稿に成功しました。レスポンスコード: ' + response.getResponseCode());
  } catch (e) {
    console.error('Slackへの投稿に失敗しました: ' + e.toString());
  }

解説:投稿日時の制御

土日かどうかや年末年始かの判定は、曜日を取得し分岐したり 12月29日 ~ 01月03日 かどうかで分岐すれば良いものの祝日の判断方法が課題として出てきました :zzz:
今回は Google Apps Script の利点を活かし Google Calendar から祝日情報を取得することで、祝日に通知させない要件を達成しました 💪

  // 3. 祝日かどうかを判定
  const calendarId = 'ja.japanese#holiday@group.v.calendar.google.com';
  const calendar = CalendarApp.getCalendarById(calendarId);
  const events = calendar.getEventsForDay(now);
  if (events.length > 0) {
    console.log(`本日は祝日「${events[0].getTitle()}」(${formattedDate})のため、処理をスキップしました。`);
    return;
  }

さいごに

投稿頻度に制約があるものの Web API やサーバの準備等は不要で手軽に「株価や為替を1時間おきにSlackへ自動で投稿する」ことができる方法の紹介でした!
このくらい手軽にできる方法が意外と見当たらなかったので(調べ方悪かったかも)、取り急ぎまとめることができて良かったです!参考になれば幸いです!
ここまでご覧くださりありがとうございました!

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?