Michaelです。
「雑コラBOT」の作成の過程からAzure Functionsの設定方法を紹介する「雑コラBotを作ろう」の5回目です。
今回は、Azure FunctionsからSlackにメッセージを投稿する関数を作成し、Blob Storageにアップロードされた画像のSAS URLを投稿する仕組みを作ります。
構成
Queue Storageに格納されたメッセージをトリガーに関数を開始し、Blob Storageにアップロードした画像パスからSAS URLを生成して、Slackにメッセージとして投稿します。
投稿されたURLをもとにSlackが自動で画像を読み込み、メッセージに画像が表示されます。
関数の作成
Azure Functionsの関数をJavaScriptで作成します。
関数のテンプレートの一覧から「Queue trigger」を選択してJavaScriptで作成を開始します。
関数の作成ペインで関数名とトリガーバインドを設定します。
キューは画像処理関数でメッセージを出力したキューを指定します。
Slackカスタムインテグレーションの設定
Azure Functionsからメッセージを投稿できるようにSlackの設定をします。
SlackのAppディレクトリから「着信Webフック」を検索し、カスタムインテグレーションに追加します。
「着信Webフック」の設定を開き、メッセージを投稿するチャンネルを設定します。その他の項目は任意で設定をします。
「Webhook URL」は、Azure Functionsからメッセージを投稿する際に使用するため、メモにひかえておきます。
スクリプト
スクリプトは以下のように作成しました。
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
// 1. モジュールの読み込み const querystring = require("querystring"); const crypto = require("crypto"); const https = require("https") const url = require('url'); // 2. Slack着信WebフックのURL設定 const slack_url = "https://hooks.slack.com/services/****/****/********" // 3. SASトークン発行関数 function generateSasToken(conn_string, container, blobName, permissions, effective_hours) { const x_ms_version = "2017-04-17" var service = "blob" var sr = service.slice(0 , 1) // SASトークンクエリの設定 var query_obj = { "sp": permissions, "sv": x_ms_version, "sr": sr }; // SAS開始日時 var startDate = new Date(); startDate.setMinutes(startDate.getMinutes() - 5); query_obj.st = to_utc_datetime(startDate); // SAS終了日時 if (!effective_hours) { effective_hours = 24 } var expiryDate = new Date(startDate); expiryDate.setHours(startDate.getHours() + effective_hours); query_obj.se = to_utc_datetime(expiryDate); // 接続文字列からアカウント名・キーを取得 var connection_info = querystring.parse(conn_string, ";"); connection_info.AccountKey = connection_info.AccountKey.replace(/\s/g, "+"); //スペースを+に置換 const account_name = connection_info.AccountName; const account_key = connection_info.AccountKey; // Blob serviceパス生成 if (container.slice(0 , 1) != "/"){ container = "/" + container }; var canonicalized_resource = '/' + service + '/' + account_name + container + "/" + blobName // 認証文字列生成 var string_to_sign = query_obj.sp + "\n" + //PERMISSION query_obj.st + "\n" + //START query_obj.se + "\n" + //EXPIRY canonicalized_resource + "\n" + "" + "\n" + //IDENTIFIER "" + "\n" + //IP "" + "\n" + //PROTOCOL query_obj.sv //VERSION); if (service == 'blob' || service == 'file'){ string_to_sign += "\n" string_to_sign += "" + "\n" + //CACHE_CONTROL "" + "\n" + //CONTENT_DISPOSITION "" + "\n" + //CONTENT_ENCODING "" + "\n" + //CONTENT_LANGUAGE "" //CONTENT_TYPE; }; // Signature生成 var key = new Buffer(account_key, 'base64') var hmac = crypto.createHmac('sha256', key); hmac.update(string_to_sign); query_obj.sig = hmac.digest("base64"); // SASトークン生成 var sas_token = querystring.stringify(query_obj) var sas_url = "https://" + account_name + ".blob.core.windows.net" + container + "/" + blobName + "?" + sas_token return { token: sas_token, sasUrl: sas_url }; } // ISO 8601 format function to_utc_datetime(date){ var Y = date.getFullYear() + ''; var m = ('0' + (date.getMonth() + 1)).slice(-2); var d = ('0' + date.getDate()).slice(-2); var H = ('0' + date.getHours()).slice(-2); var M = ('0' + date.getMinutes()).slice(-2); //var S = ('0' + date.getSeconds()).slice(-2); var S = "00"; var utc_date = Y + "-" + m + "-" + d + "T" + H + ":" + M + ":" + S + "Z"; return utc_date; } // 4. HTTPリクエスト関数 function https_request(context, options, callback){ // URL解析 var url_parse = url.parse(options.url); const host = url_parse.host; var path = url_parse.path; // メソッド設定 var method = "GET" if ("method" in options){ method = options.method; }; // 本文設定 if ("body" in options && typeof(options.body) == "object"){ options.body = JSON.stringify(options.body) }; // ヘッダ設定 var headers = {} if ("headers" in options){ headers = options.headers }; if (options.json){ headers["Content-Type"] = "application/json" }; headers["Content-Length"] = Buffer.byteLength(options.body) // オプション var req_options = { host: host, port: 443, path: path, method: method, headers: headers }; // リクエスト let req = https.request(req_options, (res) => { res.setEncoding('utf8'); res.on('data', (chunk) => { if (options.json && typeof(chunk) == "object") { chunk = JSON.parse(chunk) } callback(null, res, chunk); }); }); req.on('error', (e) => { callback(e, "", null); }); if ("body" in options) { req.write(options.body); } req.end(); }; module.exports = function (context, myQueueItem) { var payload = {} if (myQueueItem.status == "ok") { // 5. SAS URLの生成 var conn_string = process.env.recipeteststorage_STORAGE var sas = generateSasToken(conn_string, myQueueItem.container, myQueueItem.blob_path, "r", 24) var sas_url = sas.sasUrl // 6. 投稿用メッセージの作成 payload.text = myQueueItem.message + "\n<" + sas_url + "|" + myQueueItem.filename + ">" } else { // 6. 投稿用メッセージの作成 payload.text = myQueueItem.message } // 7. HTTPリクエスト var options = { url: slack_url, method: "POST", json: true, body: payload }; https_request(context, options, function(error, response, res_body){ if (!error && response.statusCode == 200){ context.log("Send Message: Succeeded"); context.done(); } else{ context.log("Send Message: Failed"); context.done(); }; }); }; |
スクリプトの説明
- モジュールの読み込み
処理に必要なパッケージを読み込みます。 - Slack着信WebフックのURL設定
Slackに追加した着信WebフックのURLを設定します。 - SASトークン発行関数
Blob Storageに保存された画像のパスからSASトークンを生成します。
SASトークン発行に関しては、npm の「azure-storage」というパッケージでもっと簡単に記述できますが、パフォーマンスの観点からBlob Storage APIの使用に沿った関数で実装しています。 - HTTPリクエスト関数
URLにHTTPリクエストする関数です。
npm の「request」パッケージがNode.jsのHTTPクライアントとして一般的ですが、こちらもパフォーマンスの観点から標準パッケージの「https」でリクエスト用の関数を作成しています。 - SAS URLの生成
SASトークン発行関数でBlobのSAS URLを生成します。 - 投稿用メッセージの作成
Slackの着信Webフックのリクエスト形式に合わせてペイロードを作成します。 - HTTPリクエスト
Slackにメッセージを投稿します。
実行結果
Slackのチャンネルに画像のURLを投稿すると、投稿時刻とほぼ同時に関数のログが表示され、Queue Storageとトリガーにより関数が開始されていることが確認できました。
SlackをみるとBotからメッセージが投稿され、URLの画像と同じ画像が表示されることが確認できました。
まとめ
今回は、Azure FunctionsからSlackにメッセージを投稿する仕組みを作成しました。これで、Slackに対してAzure Functionsから疑似的に画像を投稿する仕組みが出来上がりました。
あとは、顔に好きな画像を貼り付けてコラ画像を作成する仕組みをすれば雑コラBotの完成です。
次回は、画像処理関数にFace APIを付け加えてコラ画像作成に必要な顔検出の仕組みを追加します。