動機
DiscordのGitHub向けデフォWebhookが使い物にならなかったのでどうにかしたかった
具体的には特定のファイルを含んだコミットを検出したかった
Webhook is 何
初めて触るのでざっと調べた
正体はただのPOSTリクエスト
JSON等のファイルをPOSTで送るだけ
普段オラッデータヨコセッ!っていうところで急にチョコを送り付けるような雰囲気
チョコのことは一般的にpayloadと呼ぶらしい
POSTリクエストを受け付けられて送りつけることができる鯖が欲しい
……それGASでよくね?
つくる
Discord
チャンネルの設定画面からWebhookを受けるURLを作る
ここにJSONでPOSTリクエストを投げるとチャンネルにメッセージが流れる
GitHub
リポジトリ→Setting→Webhook
から新しくWebhookを作る
Discordと繋げる場合通常ならコピーしたURL末尾に/github
を付け加えて送信形式をJSONにする
一度試してみるのもあり
今回はGASを挟むので送信先のURLはあとでGASのものに書き換える
送信イベントはcustomにしておいた方が良い
GAS側でフィルターをかけてもよいが非常に面倒だったうえに
無料プランでの呼び出し制限(doPostに影響するかは分からないが)
をむやみに消費しそうで嫌だったので私はやめた
GAS
GitHub→GAS
GASのdoPostを使う
どうやらJSONを受け取ったときはe.parameter
ではダメな模様
これでJSONをオブジェクトで扱えるようになった
ここから欲しい情報を取り出して整形していく
function doPost(e){
e=JSON.parse(e.postData.getDataAsString());
main(e);
}
本来ならreturnをするところなのだがreturnするとGitHub側のWebhookがリダイレクトに対応していない影響でGAS側の処理は成功しているにもかかわらず302エラーを吐く
(GASはセキュリティ上の理由でレスポンスする際にリダイレクトを挟むらしい)
別にGitHubがエラーを吐こうと全く問題はないのだがログにひたすら赤文字が並ぶのも嫌なのであえてreturnしない
GAS→Discord
DiscordにPOSTでJSONを投げる
UrlFetchApp.fetch()
を使う
使い方は普通のfetchとほぼ同じ
doc
function send(x){
UrlFetchApp.fetch('Webhook送信先URL',{
contentType:'application/json',
method:'post',
payload:JSON.stringify(x)
});
}
整形
GitHubが投げるJSON↓
GitHubのWebhook設定画面からログが見れるのでそれも使うと良い
Discordが受けるJSON↓
上のリファレンスをよく読んでオプションであるかどうかをよく判断してから実装しないと{"embed":["0"]}
エラーが返ってくるので注意
特にfields[][value]
は空文字もnull判定されるので注意
function main(x){
const keys=Object.keys(x),//キーからイベントを判断する
type=//今回はissues, issue_comment, push のみを扱う
keys.includes('head_commit')?
'push':
keys.includes('issue')?
keys.includes('comment')?
'issue_comment':
'issues':
'';
send({
username:'McbeEringi',
avatar_url:'https://mcbeeringi.github.io/img/icon.png',
embeds:[{
color:parseInt('221144',16),//10進数にする必要がある
title:'NULL',
author:{name:x.sender?.login,url:x.sender?.url,icon_url:x.sender?.avatar_url},
footer:{text:x.repository?.full_name,icon_url:x.repository?.owner?.avatar_url},
...(({
push:()=>{
const diff=x.commits.reduce(
(a,y)=>{['added','removed','modified'].forEach((z,i)=>a[i].value=[...a[i].value,...y[z]]);return a;},
['追加','削除','変更'].map(y=>({name:y,value:[]}))
);
if(diff[2].value.includes('skysw.js'))//特定のファイルの更新のキャッチもできる
send({
username:'McbeEringi',
avatar_url:'https://mcbeeringi.github.io/img/icon.png',
content:'変更内容にアップデートが検出されました\n次々回起動時に自動で更新が入ります'
});
return{
color:parseInt('4488ff',16),
title:'ぷっしゅ!',
description:'新しい変更が加えられました',
url:x.compare,
fields:diff.filter(y=>y.value.length).map(y=>Object.assign(y,{value:'>>> '+y.value.join('\n'),inline:true}))
};
},
issues:()=>({
color:parseInt('ff4444',16),
title:`#${x.issue.number} "${x.issue.title}"`,
description:`**バグ報告 ${x.action}**`,
url:x.issue.url,
fields:[
{name:'内容',value:`>>> ** ${x.issue.title} **\n${x.issue.body}`},
...(x.changes?[{name:'変更前',value:`>>> ** ${x.changes.title.from} **\n${x.changes.body.from}`}]:[]),
{name:'ラベル',value:'>>> '+(x.issue.labels.map(y=>y.name).join('\n')||'*なし*')},
]
}),
issue_comment:()=>({
color:parseInt('ff4444',16),
title:`#${x.issue.number} "${x.issue.title}"`,
description:`**バグ報告 コメント ${x.action}**`,
url:x.comment.url,
fields:[
{name:'内容',value:`>>> ${x.comment.body}`},
...(x.changes?[{name:'変更前',value:`>>> ${x.changes.body.from}`}]:[])
]
})
})[type]||Object)()
}]
});
}
デバッグし辛い!
そんなあなたに
const sample={//GitHubのWebhookのログで手に入れたpayloadデータ
action:'edited',
repository:…
…
};
function debug(){doPost({postData:Utilities.newBlob(JSON.stringify(sample))});}
Blob作って投げればデバック可能
公開してGitHubにつなげる
右上の青いデプロイ
→新しいデプロイ
→種類の選択 ウェブアプリ
→自分として実行 全員がアクセス可能
→デプロイ
→ウェブアプリのurl
でdoPostにつながるURLを作れる
これをGitHubのWebhook設定に貼り付けに行く
以後更新する際はデプロイ
→デプロイを管理
→✏
→新しいバージョン
→デプロイ
の手順を踏まないと毎回URLが変わって大変なことになるので注意
つくった
特定のファイルやそれ以外のイベントの監視などそこそこ使えるので重宝することになりそう
少なくとも素でGitHubに繋げるよりかははるかに便利
よきDiscordライフを