何のために生まれて
特定の日付にDiscordで通知してほしいな、ってことがよくある。ソシャゲのキャラの誕生日とか、TRPGセッションの予定日とか。
サーバー立ててプログラム書いてBotトークン貰ってサーバーに招待して... 面倒くさい。そこまでしたくない。
でも誕生日限定ボイスは聞きたい。キャラ設定は念入りにしたい派。そういう人のための。
何をして生きるのか
- Googleスプレッドシートに日付と予定を入れておく。
- 毎朝GoogleAppsScriptを自動起動、その日の予定を確認する。
- 予定の内容を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の設定画面は頻繁に変わるから正しいことは公式で)
2. Googleスプレッドシートを作る
Googleドライブの適当な場所で右クリ→Googleスプレッドシート
そうしたら名前と誕生日を記入する。
後のために、日付を三列目に、名前はその左隣、シート名はbirthday_list、みたいな感じに決めておく。
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時で保存。
あとがき
Webhookは送信のみの一方通行なので、できることは限られる。でもそっちのほうが、セキュリティ的にも安心だよね。
コードが汚いのは手軽さ重視ってことで、言い訳にならない?無理か。
(2021-07-01) 検索が部分一致になっていたので全体一致検索に修正しました