LoginSignup
3
4

More than 1 year has passed since last update.

Discord用に誕生日通知botを作る

Last updated at Posted at 2021-06-20

何のために生まれて

特定の日付にDiscordで通知してほしいな、ってことがよくある。ソシャゲのキャラの誕生日とか、TRPGセッションの予定日とか。
サーバー立ててプログラム書いてBotトークン貰ってサーバーに招待して... 面倒くさい。そこまでしたくない。
でも誕生日限定ボイスは聞きたい。キャラ設定は念入りにしたい派。そういう人のための。

何をして生きるのか

  1. Googleスプレッドシートに日付と予定を入れておく。
  2. 毎朝GoogleAppsScriptを自動起動、その日の予定を確認する。
  3. 予定の内容をwebhook経由でDiscordへ送信

各サービスの詳細

Googleスプレッドシート

Google版Excelみたいなやつ。ブラウザで編集、Googleドライブに保存される。無料だよ。

GoogleAppsScript

略してGAS。自動処理を書くための言語で、ExcelでいうところのVBA、つまりマクロ。
一日一回実行、みたいな設定ができるので今回はそれを使う。

DiscordのWebhook

Discordの用意してくれるURLに引数付きでアクセスするとbotの投稿として処理してくれる。(正確に言うとPOSTでJSONを投げるのだけど)
"ピザを食べるために生産ラインなんて必要ない お電話一本ですぐにお届け" 的な最高のサービス

さて、やってみようか

以下はソシャゲ(ブルアカ)の誕生日botを例にやってます。

1. DiscordのWebhookを作る

Discordで「ウェブフックの管理」って権限がいるので注意。
サーバー設定 > 連携サービス > ウェブフック で新しいWebhookを追加できる。urlは後で使う。
(Discordの設定画面は頻繁に変わるから正しいことは公式で)
image.png

2. Googleスプレッドシートを作る

Googleドライブの適当な場所で右クリ→Googleスプレッドシート
そうしたら名前と誕生日を記入する。
後のために、日付を三列目に、名前はその左隣、シート名はbirthday_list、みたいな感じに決めておく。
image.png

3. GASを書く

Googleスプレッドシートのメニューバーから、ツール > スクリプトエディタでGASの編集画面が出てくる。
コードはこんな感じ。

// ウェブフックのURLリスト(さっきコピーした奴)
const Birthday_Notice_Webhook_List = [
  "https://discord.com/api/webhooks/???????????/?????????_?????????????????????????????????"
];
// botメッセージの設定
const Birthday_Notice_Webhook_Payload = {
  "username": "誕生日bot", // メッセージの送信者名
  "content": "今日%sは%sの誕生日です!" // メッセージ本文
  // "avatar_url": でアイコン設定とか、他にもいろいろできる。
  // (https://discord.com/developers/docs/resources/webhook#execute-webhook)
};


// この関数を毎日0-1時(JST)に起動
function main_everyday() {
  checkBirthday();
}


function checkBirthday() {
  // 検索対象の列番号(3列目=C列)
  const Day_Column = 3;

  // "birthday_list"シート
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("birthday_list");

  // 今日の日付
  today = Utilities.formatDate(new Date(), "JST", "M/d");
  Logger.log(`today: ${today}`);

  // 日付を検索
  var textFinder = sheet.createTextFinder(today);
  textFinder.matchEntireCell(true) // 全体一致
  var ranges = textFinder.findAll();
  Logger.log(`検索結果: ${ranges.length}件`);

  // 該当セルをひとつづつ
  for (var i = 0; i < ranges.length; i++) {
    if (ranges[i].getColumn() == Day_Column) {
      var webhook_payload = Object.assign({}, Birthday_Notice_Webhook_Payload); // オブジェクトの参照渡しを回避
      // メッセージに日付と名前を埋め込み
      webhook_payload["content"] = Utilities.formatString(
        Birthday_Notice_Webhook_Payload["content"],
        today, ranges[i].offset(0, -1).getValue() // 左隣の値=名前
      );
      // メッセージを送信
      postWebhook(Birthday_Notice_Webhook_List, webhook_payload);
    }
  }
}

function postWebhook(url_list, payload) {
  var options = {
    "method": "POST",
    'contentType': 'application/json', // これ忘れがち
    "payload": JSON.stringify(payload)
  };
  Logger.log(`Payload for webhook: ${options["payload"]}`);
  for (var i = 0; i < url_list.length; i++) {
    // HTTPリクエストを送る
    var r = UrlFetchApp.fetch(url_list[i], options);
    Logger.log(`Response from webhook: (${r.getResponseCode()})${r.getContentText("UTF-8")}`);
  }
}

たぶん途中でアクセス許可みたいな画面が出るので、スクリプトが信用できそうなら許可。
コードができたら左の時計マークからトリガーを設定する。
実行はmain_everyday、イベントのソースを時間主導型、日付ベースの午前0時~1時で保存。
image.png

これでもう、推しの誕生日ボイスを聞き逃す心配はない。
image.png

あとがき

Webhookは送信のみの一方通行なので、できることは限られる。でもそっちのほうが、セキュリティ的にも安心だよね。
コードが汚いのは手軽さ重視ってことで、言い訳にならない?無理か。
(2021-07-01) 検索が部分一致になっていたので全体一致検索に修正しました

3
4
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
4