🕑

【GAS】Discord Botをお手軽にスケジューリング

2024/05/13に公開

準備

Discordチャンネルのウェブフックを発行

Botのメッセージを飛ばしたいチャンネルに外部からアクセスためのウェブフックURLを発行する

  1. チャンネル設定
  2. ウェブフックの作成 (連携サービス)
  3. ウェブフックURLをコピー
画像での説明


チャンネル設定

ウェブフックの作成(連携サービス)

ウェブフックURLをコピー

GASの新規プロジェクトを作成

下記のリンクカードをクリックして、空のプロジェクトを作る
https://script.google.com/home/projects/create?template=default

権限の承認

UrlFetchAppScriptAppを使うために下記のコードをコピペしてmyFunctionを実行する

function myFunction() {
  UrlFetchApp.fetch("https://example.com");
  ScriptApp.getProjectTriggers();
}

右下の「許可」を押す

メッセージの送信

定型文を送る(Hello World)

WEBHOOK_URLに発行されたウェブフックURLをコピペしてHelloWorldを実行

function HelloWorld() {
  const message = {
    "username": "お手軽Bot",
    "content": "Hello, World!",
    "tts": false,
  };

  PostMessageToDiscord(message);
}

function PostMessageToDiscord(message) {
  const WEBHOOK_URL = "<URL>"

  const options =
  {
    "method": "POST",
    "headers": { "Content-type": "application/json" },
    "payload": JSON.stringify(message)
  };

  UrlFetchApp.fetch(WEBHOOK_URL, options);
}

ランダムなメッセージを送る

何度かRandomMessageを実行してランダムなメッセージが送られていることを確認

function RandomMessage() {
  const contents = ["Hello, World!", "はろーわーるど!", "ハローワールド!"];

  const message = {
    "username": "お手軽Bot",
    "content": contents[GetRandomInt(0, contents.length)],
    "tts": false,
  };

  PostMessageToDiscord(message);
}

function GetRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min) + min);
}

埋め込みコンテンツを送る

ドキュメントを参照しながら好きなように改造

function EmbedMessage() {
  const message = {
    "username": "お手軽Bot",
    "content": "埋め込みもできるよ",
    "tts": false,
    "embeds": [
      {
        "title": "【GAS】Discord Botのお手軽な作り方【Zenn】",
        "description": "#埋め込みコンテンツを送る",
        "url": "https://zenn.dev/rrisland/articles/9d28cfddb1ffb7",
        "color": 0x3ea8ff,
      }
    ]
  }

  PostMessageToDiscord(message);
}

https://discord.com/developers/docs/resources/webhook#execute-webhook-jsonform-params

スケジューリング

1時間ごとに1回、n分に実行予約する

GASのトリガーを「1時間ごと」にすると1時間のうち00分~59分のランダムなタイミングで実行される
トリガーのたびに1時間後のn分を指定してNewTriggerすることで時間のブレが起こらなくなる

function Scheduler() {
  const date = AddHours(new Date(), 1);

  DeleteTrigger("RandomMessage");
  date.setMinutes(0);
  NewTrigger("RandomMessage", date);

  // DeleteTriggerで他関数の実行予約を消さないようにSchedulerはトリガーの中で一番最後に実行する
  DeleteTrigger("Scheduler");
  date.setMinutes(55);
  NewTrigger("Scheduler", date);
}

function AddHours(date, hours, minutes = 0) {
  date.setTime(date.getTime() + (hours * 60 * 60 * 1000) + (minutes * 60 * 1000));
  return date;
}

function DeleteTrigger(funcName) {
  const triggers = ScriptApp.getProjectTriggers();
  for (const trigger of triggers) {
    if (trigger.getHandlerFunction() !== funcName) continue;
    ScriptApp.deleteTrigger(trigger);
  }
}

function NewTrigger(funcName, date) {
  ScriptApp.newTrigger(funcName).timeBased().at(date).create();
}

トリガーが「無効」になる問題

V8ランタイムのバグでNewTriggerしても正常に動作しないことがある
回避策は2つ

  1. デプロイしたバージョンのSchedulerをトリガーへ登録する
  2. HeadのSchedulerを手動実行せずにトリガーへ登録する

https://zenn.dev/datsukan/articles/f525e0f7af6859
https://qiita.com/YasumiYasumi/items/3a7ca594d197e2aacf52

参考

https://zenn.dev/kentyisapen/articles/78e113f8ca53c3
https://qiita.com/iroha71/items/b2a473898d6c9b4b4ae7

Discussion