PerlでGitHub webhookを受けるbotを作ってみた話

こちらは[Perl Advent Calendar 2020](https://qiita.com/advent-calendar/2020/perl)の20日目の記事になります。
昨日は、utgwkkさんによる、[現状の癖を反映した .perltidyrc](https://blog.utgw.net/entry/2020/12/19/233443) でした。

今年も軽い気持ちでPerl Advent Calendarに投稿してみようと思ったのですが、
何か流行り物をやってみようと思いつつ例によって前日までネタが浮かばなかったので、
以前作成したGitHub のwebhookを受けるbotを若干作り直してみたので紹介してみることにしました。

# 何を作ったのか？
https://github.com/teckl/p5-GitHub-Webhook-Bot

こういうやつです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28477/9714cf75-c4e1-2d3a-d716-f3685dab9276.png" width=45%> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28477/04320086-2923-6a24-34da-38413415b451.png" width=45%>
GitHubのwebhookイベントに応じて、Slack, Chatworkにクロスポストする感じのbotになります。


# GitHub webhook botを作った経緯
弊社ではGitHub Enterpriseを使っているのですが、
プルリクエスト時のレビュー依頼やissueでのやり取りなどを円滑にするために3年前くらいにbotによる運用で楽がしたいということになりました。

例えばGitHubとSlackを連携させるのは、普通であれば
[GitHub と Slack を連携させる](https://slack.com/intl/ja-jp/help/articles/232289568-GitHub-%E3%81%A8-Slack-%E3%82%92%E9%80%A3%E6%90%BA%E3%81%95%E3%81%9B%E3%82%8B)
[SlackとGitHubを連携してissueコメントやプルリク通知をチャンネルに流す方法（2020年版）](https://qiita.com/unsoluble_sugar/items/e42ac352400598a24915)

↑この公式のSlack 用 GitHub アプリを使う方法が一番初めに浮かぶと思います。

が、ある深遠な理由により、当時はChatworkをメインにしており、Slackのbotなどを使うことが出来ませんでした。。

また、公式のSlack 用 GitHub アプリだと、GitHubのイベントをチャットに流すことは出来ますが、
複数のリポジトリ・複数のチャンネルが入れ乱れている場合は流量が大量になり、
いちいち流れを追うのも非効率ということもありました。
自分に関係あるissue, PullRequestだけを見逃さないようにしたいというのがあり、
公式botとかでもそういった事はできなさそうだったのでPerlで作ってみることにしました。

# やりたかったこと
## @記法
@記法でコールされた人や、issueやPRにアサインされている人だけにメンション（TO）を飛ばしたい。
（皆様のところではissue時のコメントや、プルリクエスト時のレビュー依頼などはどうされているのでしょうか…？）

## リポジトリとチャンネル毎に出し分けしたい
リポジトリ単位で、関係のある案件ごとのチャンネルだけにwebhookを流したいと考えました。

## コード記法
調査した際のログやスニペットなどを気軽にissueなどに貼りたいが、チャットログがあまり汚れないようにしたい

## 画像付きLGTMしたい
当時流行っていた [仕事にあそびごころを、LGTMコメントに使いたいGif画像](https://qiita.com/tos-miyake/items/7e33b67fc62620d4e87b) みたいな、
LGTM画像を貼ってワイワイしたい。

## Slackにも移れるようにしておきたい
深遠な理由が状況によって変わる可能性もあったため、場合によってChatworkからSlackに移動しても同じように使えるようにしておきたいと思っていました。

---

# Chatwork 特有の事情
## Chatwork絵文字のタグが独特

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">某ChatWork氏、<br>「Class::DBI」って書くと<br>「Class:😄BI」って絵文字になっちゃうので<br>「Class::\DBI」ってエスケープするフィルタが必要な気がしてきた…🦆</p>&mdash; teckl (@teckl) <a href="https://twitter.com/teckl/status/1011488727464472576?ref_src=twsrc%5Etfw">June 26, 2018</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

これはPerl使いにとってなかなか辛い感じで、
`:D` -> `😄` になってしまうのを防ぐために置換リストを用意するなどしました。
https://github.com/teckl/p5-GitHub-Webhook-Bot/blob/main/etc/chatwork_emoticon_list.txt


## LGTM画像の実装
Slackしか使ってない方はご存知ないと思いますので説明させていただきますと、
当時のChatworkでは画像URLなどを貼ってもプレビュー（インライン展開）してくれず、人力で画像をアップロードするしか方法がありませんでした。
無いのであれば、Perlの得意分野である正規表現を使って、画像URLっぽいものを抜いてきて、
自前で別途チャンネルにアップロードするような感じにしました。
https://github.com/teckl/p5-GitHub-Webhook-Bot/blob/main/lib/GitHub/Webhook/Bot/Web.pm#L200

（※この機能はChatwork公式で今年春から使えるようになったようです…！：[「リンクプレビュー」機能をリリースしました！ ](https://blog-ja.chatwork.com/2020/05/linkpreview.html)）

---

# というわけでその後
このbotを作ってから、いい感じにChatworkで運用して平和に暮らしていたのですが、
その後深遠な理由が無くなり、ChatworkからSlackに移行する事になりました。
当時Slackになっても使えるようにということで中身空っぽのSlack用分岐だけを用意していましたが、
中身をちょろっと書き換えてSlack移行ができました。
（ベタベタな分岐が汚いものの）Chatwork専用のロジックはせっかくなのでそのまま残しております。

今もChatworkとGitHubを使われている方は普通に使えるのではないかと思いますので、
もしよろしければお試しいただけるとありがたいです。。

---

# 技術的な事

## ウェブアプリケーションフレームワーク：Kossy
今回初めて試してみましたが、使用しているフレームワークはkazeburoさんのKossyになります。
https://github.com/kazeburo/Kossy
sinatra風のとても軽量なWAFで、今回のような単機能のWEBアプリを作るにはとてもいい感じです。

作った当時、Kossyが使われているisucon 6のアプリをいろいろと参考にさせていただきました。。
https://github.com/isucon/isucon6-final/tree/master/webapp/perl

## Githubのwebhookについて
GitHubのwebhookについては、
[GithubのHookについてのまとめとソリューション | おそらくはそれさえも平凡な日々](https://songmu.jp/riji/entry/2013-12-05-github-hooks.html)
がとても参考になりました。
こちらの記事をもとに肉付けした感じのが今回のbotになります。

webhookのバリデーションについては、 
[GithubのWebhookのバリデーションとPlack::Middleware::HubSignature](https://songmu.jp/riji/entry/2014-12-05-plack-middleware-hubsignature.html)
の `Plack::Middleware::HubSignature` を利用させていただいています。 

## webhookのテスト環境について
[ペイロードを受信するようサーバーを設定する - GitHub Docs](https://docs.github.com/ja/free-pro-team@latest/developers/webhooks-and-events/configuring-your-server-to-receive-payloads#ngrok%E3%81%AE%E5%88%A9%E7%94%A8)
↑のGitHub公式ページに書いてありますが、このwebhookをテストするには [ngrok](https://ngrok.com/) を使うのが超便利でした。
外部のリクエストを受ける環境で毎回デプロイとかするのは面倒だったりするので、テスト中はローカルのアプリケーションをngrokで一時的に公開すると楽にデバッグできました。

## yamlの読み込み
もともとはjsonで作っていたのですが、人間が手動で編集するにはケツカンマ問題などが辛かったので今回yamlに変更しました。

## 環境変数の読み込み
ハードコードしちゃってましたが、 `.env` を使って読み込むようにしてみました。

---

# 感想
今回、Perlネタを考えるにあたってネタが無かったのもありますが、
このまま社内に埋もれさせるのももったいなと思ってこの機会に公開できるようにしてみました。。

特にLGTM画像のあたりなど、不便だからこそ、なんとかして頑張ってみよう、的なモチベーションになったのを覚えています。
正直なところ、元からSlackを使っていたらわざわざこれを作るようなモチベーションは出なかっただろうなぁ、と今になって思ったりもしますが、いい勉強になりました。

明日は、[kolukuさんによる Perlでズンドコ問題を短くしてみる - koluku&#39;s blog](https://koluku.com/entry/2020/12/21/101709) です！