0
0

More than 1 year has passed since last update.

Webhook GitHub→*GAS*→Discord GASでGitHubの任意のファイルの監視がしたい

Last updated at Posted at 2022-02-13

動機

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が変わって大変なことになるので注意

つくった

一応これで動く
image.png

特定のファイルやそれ以外のイベントの監視などそこそこ使えるので重宝することになりそう
少なくとも素でGitHubに繋げるよりかははるかに便利

よきDiscordライフを

0
0
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
0
0