mattermostで通知を受ける

GitLabのビルドの結果を知るのにDiscordを利用していたのだけれども、そもそもチャットとしての完成度はさておき、私のホームネットワークはLANで完結しているものに通知だけwebhookを飛ばすことに抵抗があった。 Discord上では特にそういった制約もなければ、どれだけWebhookを作成できるかは不明瞭であり、特に使用にコストが発生するわけでもないので簡単に使いたいのであれば別にDiscordでも問題ない。

しかし最近になってGitLabを含めホームネットワークを整理したので、通知も含めてすべて完結するようなものを探してみた。かのリチャード・ストールマンもDiscordは使うべきではないと名言しているので私もいつかはDiscordに依存から脱却したいとは思っていた。

もともとGitLabでは様々なインテグレーションが用意されている。Pushoverもあれば、Slack、MSのTeamsなんかもある。しかし、セルフホスティングの選択肢でデスクトップ通知を受けられそうなものといえばTelegramかMattermostだろうか。あるいはメールという選択肢もあるが、今回はMattermostを導入することにしてみた。

Mattermostそのものはすでにバージョン9がリリースされるといったあたりで、実は結構息が長い。もともとDiscordを使い始めた頃にはその存在を気にしてはいた。サーバーのインストールには複数方法があり、モバイルやデスクトップのアプリケーションも提供されているのでなかなかよさそうである。

いざインストールしてみると使い勝手はあまりよろしくない、というかインストールは結構苦労した。そのあたりの話は割愛させていただくとして、Mattermostのサーバーをインストールしたあとに必要な設定を簡単に残しておく。

1. チャンネルを作成する

必須ではないものの、GitLabのリポジトリと紐付けるような運用を行っているため、まずはチャンネルを作成しておく。

2. System Console > Integrations > Integration Management

  • Enable Incoming Webhooks: true

trueであるか確認しておく。デフォルト値なので基本は変更しなくても問題なさそう。

  • Enable integrations to override usernames: true
  • Enable integrations to override profile picture icons: true

この設定はデフォルトでfalseなので変更しておく。

3. Integrations > Incoming Webhooks

ここでWebhookを作成する。URLをコピーしておく。

4. Admin Area > Settings > Network > Outbound requests

ここからはGitLab。ここが重要。

Local IP addresses and domain names that hooks and integrations can accessにMattermostのIPアドレスを追加する。 URLだとHTTPSの設定が正しくないとうまくいかなかったので、IPアドレスにしておくのが無難そうである。

5. Settings > Integrations > Mattermost notifications

Webhookの欄に先程コピーしたURLを貼り付ける。 サーバー自体はNginxのReverse proxyを使用しているので本来HTTPSが指定できるのだが、HTTPで直接Mattermostのポートを指定することにした。

Test settingsをクリックするとテストできる。残念ながらDiscordのようにWebhookごとに画像設定できないのでUsernameのみしか変更できないようだった。

あとはデスクトップの通知設定をFor all activityに変更してあげれば通知を確認することができた。 もしこれを自分で用意しようと思うと、サーバーとデスクトップ通知をするようなアプリケーションを書かなければいけないだろう。

HTTPのAPIも公開されているのでDiscordと同様にうまくこのサーバーを組み合わせられるようにしていきたい。 先程生成したWebhook用のURLを使うことでサーバーから送信することは確認できる:

class Mattermost
  include HTTParty
  base_uri "your-mattermost-server.com"

  def initialize(channel, username, icon_url)
    @channel = channel
    @username = username
    @icon_url = icon_url
  end

  def self.message
    self.class.post(
      "/hooks/xxx-generatedkey-xxx",
      {
        body: {
          "text" => "Hello, this is some text\nThis is more text. 🎉",
          "channel" => @channel,
          "username" => @username,
          "icon_url" => @icon_url,
        }.to_json,
        headers: {
          "Content-Type" => "application/json",
        },
      }
    )
  end
end

生成されるURLはDiscordよりも短いので若干不安になるのだけれども、投稿は確認できた。 ここでは画像のURLをicon_urlで指定できるようだ。 DiscordのようにEmbedsのような概念はなさそうだが、メッセージにMarkdown形式でテーブルやらを表現することができるようだ。