メールの内容をMattermostに送る方法

Tips
投稿日2019年08月21日 10:00

概要

MattermostというSlackライクなコミュニケーションツールがあります。
Slackでは、メールの内容をSlackのチャンネルに表示することができるのですが、Mattermostではそのような機能がないため、自作する必要があります。

ここでは、メールの内容をMattermostに送る方法について紹介します。

<h2>流れ</h2>

Mattermostには、Incoming Webhookというものがあり、これを使えばプログラム上からMattermostの特定のチャンネルにメッセージを送ることができます。
そのため、Mattermostにメッセージを送る方法はこれを使います。

次に、メッセージを作る部分ですが、これはメールサーバからメールを取得してそれを元にメッセージを作ることになります。
そのため、プログラムを作成し、そこでメールサーバにアクセスしてメッセージを作成する必要があります。
今回は使い慣れているということで、Rubyを採用しました。
また、メッセージの作成後はそれをMattermostに送ります。

Incoming Webhookの作成

Incoming Webhookの作成は、メニューの統合機能から行います。
もしかすると、統合機能は管理者によって機能が無効化されている可能性もあり、そのような場合は管理者に機能を有効にしてもらいましょう。

統合機能では画面では、Incoming Webhookとして、内向きのWebhookを作成します。

内向きのWebhookの作成は指示に従って作成します。作成を終えると、URLが表示されるので、そこに適切な内容のデータを送ることでメッセージを投稿することができます。

<h2>メッセージの作成と送信</h2>

それでは一番難しいメッセージの作成と送信を行うプログラムの作成をします。

プログラムはRubyを使用します。

# frozen_string_literal: true

require 'net/imap'
require 'uri'
require 'net/http'
require 'mail'
require 'json'

def get_body(mail)
  if mail.multipart?
    if mail.text_part
      mail.text_part.decoded
    elsif mail.html_part
      mail.html_part.decoded
    end
  else
    mail.body.decoded
  end
end

def create_message(from, subject, date, body)
  data = {
    text: "From: #{from[0]}
Subject: #{subject}
Date: #{date}\n
#{body.force_encoding('UTF-8').encode('UTF-8', invalid: :replace)}"
  }
end

def send_message(server_uri, message)
  uri = URI.parse(server_uri)

  response = Net::HTTP.post_form(uri, payload: message.to_json)

  return if response.code == '200'

  warn "Error webhook request #{response.code} #{response.message}"
  exit!
end

def archive_mail(imap, id)
  imap.uid_copy(id, 'INBOX.Archive')
  imap.uid_store(id, '+FLAGS', [:Deleted])
  imap.expunge
end

webhook_uri = 'MattermostのWebhookのURL'

imap_host = ENV['IMAP_HOST']
imap_usessl = true
imap_port = 993
imap = Net::IMAP.new(imap_host, imap_port, imap_usessl)

imap_user = ENV['IMAP_USER']
imap_passwd = ENV['IMAP_PASS']
imap.login(imap_user, imap_passwd)

search_str = 'example@example.com'

imap.select('INBOX')
ids = imap.search(['FROM', search_str])
ids.each do |id|
  mail = imap.fetch(id, %w[RFC822 UID]).first

  m = Mail.new(mail.attr['RFC822'])

  next unless m.from.include? search_str

  from = m.from
  subject = m.subject
  date = m.date
  body = get_body(m)

  message = create_message(from, subject, date, body)

  send_message('webhook_url', message)

  archive_mail(imap, mail.attr['UID'])
end

imap.logout
imap.disconnect

基本的には、Rubyに標準で添付されているimapというライブラリを使いメールサーバにIMAPでアクセスして操作しています。
また、今回はメールサーバのアドレスやユーザ名やパスワードは環境変数から取得しています。

このプログラムでは、example@exampleというメールアドレスから送られてきたメールをsearchメソッドによって取得し、それを元にMattermostのメッセージを作っています。
searchメソッドで検索するのは、selectで指定したINBOXという一般的に送られてきたメールが置かれる場所を検索します。検索場所を変える場合は、selectメソッドの引数の文字列を変えます。
メッセージの作成はcreate_message関数で行っています。create_message関数のデータを作成するために必要なデータはmailというgemを使用し取得しています。
mailというgemは、メールのデータを扱いやすくするためのgemで、gem install mailなどのコマンドを実行してインストールする必要があります。
m = Mail.new(mail.attr['RFC822'])はメールを扱いやすくしたオブジェクトを作成しています。
ちなみに、m.bodyにはメールの本文が代入されているのですが、メールにはテキストのものとHTMLのもの、さらに添付ファイルがあるかどうかなどでかなり複雑な構造になっています。
この構造に対応して本文を取り出しているのが、独自に作成したget_bodyメソッドです。
create_message関数で作成しているデータの構造はMattermostのWebhookの構造に対応しており、実際にメッセージとして表示される内容は、textで指定しているものになります。textで指定している文字列のうち、body.force_encoding('UTF-8').encode('UTF-8', invalid: :replace)というものがありますが、これは文字コードがUTF-8でないとto_jsonという処理でエラーが発生するため、文字コードの変換を行っています。

私の環境だけかもしれませんが、searchメソッドで検索したメールの内容になぜか検索には引っかかるはずのないメールが引っかかることがありました。
そのため、next unless m.from.include? search_strという処理でそのメールが本当に正しいかどうかを確認しています。

メッセージを、Mattermostに送っているのは、独自に作成したsend_message関数です。
send_message関数は、作成したメッセージを元に、Mattermostで作成したWebhookのURLに向けてメッセージの送信を行なっています。

このプログラムは定期的に実行するつもりなのですが、このままではひとつひとつのメールについて、Mattermostに送ったのか送っていないのか分かりません。そのため、同じメールの内容がなんども送られることなります。
そこで、すでにMattermostにメッセージとして送ったメールについては、そのメールをアーカイブします。
アーカイブは、archive_mail関数で行っています。

実行

プログラムを作成してそこでMattermostに送るメッセージの作成と送信を行うのはいいのですが、この処理は定期的に行う必要があります。
ここでは、cronを使うことにします。

cronの設定ファイルは、/etc/crontabなどにあるので、そこに次のような内容を追記します。

*/5 * * * *  root root ruby /opt/send-mail-content-to-mattermost.rb

これは、ファイル名をsend-mail-content-to-mattermost.rbとして、/optディレクトリに置いている例です。
ファイル名や場所は適当に変えてください。

このcronの設定の場合、5分おきに実行されます。

<h2>さいごに</h2>

これで、Mattermostにメールの内容を送ることができるようになりました。