では、シェルスクリプトを作成してみましょう。今回のシェルスクリプト(important_message.sh)は、次のようになります。「Googleアカウントのユーザー名」と「Googleアカウントのパスワード」は重要なメッセージが届くGmailのユーザー名とパスワードに、「トークン」は、Slack Appを作成したときに生成された認証アクセス用トークンに書き換えてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#!/bin/sh # Gmailに関する設定 pop_server="pop.gmail.com" user_id="GoogleアカウントのID" password="Googleアカウントのパスワード" # 「【重要】」というタイトルをエンコード subject_base64="44CQ6YeN6KaB44CR" # Slackに関する設定 channel_urlencoding=$(echo "シェルスクリプト連載" | python3 -c "import sys, urllib.parse; print (urllib.parse.quote(sys.stdin.read().rstrip()));") token="トークン" # 受信メール数の取得 expect -c " set timeout 30 spawn openssl s_client -connect ${pop_server}:995 expect \"+OK Gpop ready\" send \"user ${user_id}\n\" expect \"+OK send PASS\" send \"pass ${password}\n\" expect \"+OK Welcome.\" send \"stat\n\" expect \"+OK\" send \"quit\n\" expect \"+OK Farewell.\" exit 0 " > receive.log receive_count=$(grep +OK receive.log | tail -n2 |head -n 1 | cut -d " " -f 2) # メッセージ受信 for i in $(seq {receive_count}) do expect -c " set timeout 30 spawn openssl s_client -connect ${pop_server}:995 expect \"+OK Gpop ready\" send \"user ${user_id}\n\" expect \"+OK send PASS\" send \"pass ${password}\n\" expect \"+OK Welcome.\" send \"retr 1\n\" expect \".\" send \"quit\n\" expect \"+OK Farewell.\" exit 0 " > message.log # タイトルの先頭部分を抽出 subject=$(cat message.log | grep "Subject: =" | sed "s/Subject: =?UTF-8?B?//g "| cut -c 1-16) # 重要なメッセージをSlackに転送 if [ ${subject} = ${subject_base64} ] ; then cat message.log | awk '/Content-Transfer-Encoding\: 8bit/,/^\./' | head -n -1 | tail -n +2 > message.txt message_urlencoding=$(cat message.txt | python3 -c "import sys, urllib.parse; print (urllib.parse.quote(sys.stdin.read().rstrip()));") curl "https://slack.com/api/chat.postMessage?token=${token}&channel={channel_urlencoding}&text=${message_urlencoding}" fi done |
※実際のスクリプトから一部の文字($など)が抜け落ちています(原因は調査中)。正しいスクリプトを「 https://github.com/shellscript-magazine/rensai_shellscript1 」から入手してください。
※受信数を取得する部分が不安定だったので、31行目のコードを修正しました(2019年11月9日)。
それでは、シェルスクリプトを1行ずつ見ていきましょう。1行目は、第1回で説明した「シェバン」です。3行目の「#」で始まる行はコメントです。8、11、15、33行目、51行目、54行目のコメント行になります。
4~6行目は、Gmailにアクセスするための「受信メールサーバーのホスト名」「Googleアカウントのユーザー名」「Googleアカウントのパスワード」を、それぞれ「pop_server」「user_id」「password」の変数に代入しています。
9行目は「【重要】」というBase64でエンコードした文字列の一部を変数「subject_base64」に代入しています。日本語で書かれた、メールのタイトルは、
1 |
Subject: =?文字コード?B?Base64でエンコード |
のように受信されます。この文字列は、受信したメールのタイトルの先頭が「【重要】」であるかどうかのチェック(55行目)に利用しています。
12行目は、Slackのチャンネル名(ここでは「シェルスクリプト連載」)をURLエンコードして変数「channel_urlencoding」に代入しています。URLエンコードの部分は、「Slack Appを生成する」の章で実行した最初のコマンドと同じです。URLエンコードに適したコマンドがないので、Pythonの「urllib.parse」ライブラリを用いてエンコードしています。チャンネル名は、関数「sys.stdin.read()」を使ってパイプライン(|)経由で標準入力から取り込んでいます。標準入力から受け取ったチャンネル名には改行コード(LF)が含まれているので「rstrip()」関数で末尾の改行を削除しています。URLエンコードする関数は「urllib.parse.quote()」です。
13行目は、認証アクセス用のトークンを変数「token」に代入しています。この変数は、58行目でSlackにメッセージを転送するために利用します。
16~31行目では、受信メールサーバーにアクセスして受信メッセージの件数を取得しています。第7回と同様に、expectコマンドを利用しています。18行目の「spawn」の後ろにあるコマンドで、受信メールサーバーへアクセスしています。「expect」が期待する応答、「send」が入力する文字列です。期待する応答が返ってこないときのタイムアウトは、17行目の「set timeout」で設定しています。ここでは「30」秒という長いタイムアウトにしています。タイムアウトを長くしないと、29行目のリダイレクト(>)で書き込んだログが「receive.log」ファイルに正しく残りませんでした。
24行目の「send \”stat\n\”」で受信メッセージ数を取得しています。応答は、次のように返ってきます。
1 |
+OK 受信メッセージ数 受信メッセージ総容量(バイト) |
この応答はログファイルの下部に書き込まれています。31行目の「grep +OK receive.log | tail -n 2 | head -n 1」で、その行を切り出します。「grep」コマンドで「+OK」で始まる応答部分を抽出して「tail」コマンドで末尾2行だけ切り出し、「head」コマンドでその中から先頭の1行を取り出しています。その後ろの「cut -d ” ” -f 2」で、2列目にある受信メッセージ数を抽出して変数「receive_count」に代入しています。
34~59行目で「for」文の処理を受信メッセージ数分実行します。for文に関しては、第1回を参照してください。
36~49行目は、受信メッセージを1通ずつ読み出す処理です。44行目の「retr 1」が読み出すコマンドで、受信メッセージの最後が「.」で終わるので、45行目で応答を確認しています。一度受信したメッセージはサーバーから削除されます。
49行目で受信メッセージを含んだログを「message.log」ファイルに取り込んでいます。52行目でBase64でエンコードされたタイトルの先頭から16文字分を取り込んで変数「subject」に代入しています。
55行目でこの16文字からタイトルの先頭が「【重要】」であるかどうかを判断して、「【重要】」であれば「if」文内の処理を実行します。if文に関しては、第1回を参照してください。
56行目でログからメッセージ部分を切り出しています。プログラミング言語「AWK」を使うと、次のように開始部分と終了部分を指定するだけでメッセージを切り出せます。
1 |
awk '/開始部分/',/終了部分/' |
ここでの切り出しには、受信メッセージヘッダーの最後にある「Content-Transfer-Encoding: 8bit」を開始部分として、受信メッセージの終わりにある「.」を終了部分として利用しています。受信メッセージヘッダーの最後は、メールを送信するクライアント(エージェント)ソフトによって異なります。実際に、message.logファイルを確認して自分の環境に合わせて変更してください。
なお、「:」「.」は特殊文字なのでエスケープ(手前に「\」を付ける)しています。また、行頭を表す正規表現の「^」も利用しています。
開始部分と終了部分が切り出した受信メッセージに含まれているので「head -n -1 | tail -n +2」で取り除いて「message.txt」ファイルに格納しています。
57行目は受信メッセージをURLエンコードする処理です。Slackに転送するメッセージをファイルから読み出すために「cat」コマンドを使っていますが、「Slack Appを生成する」の章で実行した、「echo」コマンドを使っている2番目のコマンドと同じです。URLエンコードの処理は、前述したPythonの「urllib.parse」ライブラリを用いたものと同様です。
Slackに転送するURLエンコーディングした受信メッセージが用意できたので、58行目の「curl」コマンドでSlackに対してメッセージを送信しています。これは「Slack Appを生成する」の章で実行した3番目のコマンドです。