📬

DenoからS-nailを使ってSMTPメールを送信する

2023/06/24に公開

今回は、XServerで作ったメールアドレスを使ってDenoでメールを送信するのが最終目的です。

S-nailというメール送信ができるコマンドを使ってメールを送信します。

https://manpages.ubuntu.com/manpages/jammy/en/man1/s-nail.1.html

S-nailを使うに至った経緯

DenoでSMTPでメールを送信できるライブラリは多くはありません。2023/06/24現在denomailerが一番安定しているのではないかと思います。しかし、現時点で私が試した限りでは文字化けが多くて日本語を上手く扱えませんでした。他のライブラリはそもそも送信もできず。npmのnodemailerをインポートしても上手くいきませんでした。

というわけで、DenoだけではSMTPでメールを送信する手段が無いという状況になってしまいました。

今回はUbuntuのVPSを使っていましたので、SMTPでメールを送信できるコマンドが無いか調べてみたところS-nailが見つかりました。mailコマンドの拡張版のmailxコマンドが色々と種類があって、その更に後継的なやつのようです(にわか)。

S-nailの準備

S-nailを使うにはMTAが必要になります。今回はPostfixを使用します。

Postfixのインストール

既にインストールしている場合はこの作業は不要です。

sudo apt install postfix -y

PostfixはインストールするときにPostfix Configurationが始まります。対話形式で初期設定を行うのですが、今回は大して設定は必要ありませんのでNo configurationを選択します。

Postfixの設定

設定をスキップしましたが、Postfixを動かすには/etc/postfix/main.cfに最低限の設定が必要になります。

  1. テンプレートから設定ファイルを作成
sudo cp /usr/share/postfix/main.cf.debian /etc/postfix/main.cf
  1. rootのホームディレクトリにメールディレクトリを作成(ディレクトリ名は任意)
sudo mkdir ~/maildir
  1. 設定ファイルにメールディレクトリを追記
sudo postconf -e "home_mailbox= maildir/"
  1. Postfixを再起動
sudo systemctl restart postfix

S-nailのインストール

S-nailはインストールするだけです。設定ファイルを設けることもできますが、今回はDenoから実行しますので、設定情報はDenoから渡すことにします。

sudo apt install s-nail -y

コマンドで送信できるか確かめてみる

以下のコマンドでSMTPでメールを送信します。XServerのメールの場合ですので、他のメールサーバーの場合は認証のやり方が微妙に異なるかもしれません。

echo 本文 | s-nail -:/ -Sv15-compat -Smta=smtps://アカウント名:パスワード@ホスト名:465 -Sfrom=送信元メールアドレス -s件名 -Mtext/html 送信先メールアドレス

アカウント名パスワードホスト名は利用しているメールアドレスのSMTP認証に置き換えます。アカウント名パスワードはURLエンコードが必要です。
(ホスト名もURLエンコードが必要かもしれません。XServerは不要ですが)

-Mtext/htmlはメールをHTMLメールにする設定です。HTMLメールではない場合は外します。

なお、S-nailは標準入力を本文として扱いますので、パイプで本文を渡しています。

DenoからS-nailを実行する

S-nailは次の特徴があるので、コマンドを実行するにはひと工夫必要です。

  • 標準入力を本文として扱う
  • アカウント名とパスワードのURLエンコードが必要

こちらがDenoでS-nailを使ってSMTPメールを送信するコードになります。

send.ts
let host = 'ホスト名'
let user = 'アカウント名'
let password = 'パスワード'

let to = '送信先メールアドレス'
let from = '送信元名 <送信元メールアドレス>'
let subject = '件名'
let body = '本文'

let command = new Deno.Command(
  's-nail',
  {
    args: [
      '-:/',
      '-Sv15-compat',
      `-Smta=smtps://${encodeURIComponent(user)}:${encodeURIComponent(password)}@${host}:465`,
      `-Sfrom=${from}`,
      `-s${subject}`,
      '-Mtext/html',
      to
    ],
    stdin: 'piped'
  }
)
let { stdin } = command.spawn()

let writer = stdin.getWriter()
writer.write(new TextEncoder().encode(body))
writer.close()

URLエンコードにはencodeURIComponent()を使っています。

Deno.Commandでコマンドを実行するときに標準入力を使いたい場合は、stdin: 'piped'オプションを渡して、spawn()メソッドを実行してstdinを得ます。stdinWritableStreamですので、後はWritableStreamの使い方に従って本文を渡します。

複数の送信先やCCなどが必要な場合はS-nailの使い方を見ながら設定が必要になります。

実行するときは--allow-runが必要です。

deno run --allow-run send.ts

Discussion