昨年の 10 月に Discord Webhook で Elixir からファイルを POST する方法を記事に書きましたが、その Ruby 版です。
テキストをPOSTする
テキストのみの場合、net/http
の Net::HTTP.post_form
メソッドを使うことで簡単に POST できます。
パラメータとして、本文は content 、ユーザ名は username をキーにした Hash を渡します。
このとき content は必須です。
そのほかのフィールドは Discord のドキュメントを参照してください。
ここでは Webhook の URL は次のように環境変数 WEBHOOK_URL
に設定してあることにします。
$ export WEBHOOK_URL=https://discord.com/api/webhooks/****/*******
あとは Webhook の URL 、JSON エンコードした body 、ヘッダにコンテントタイプを指定して POST すれば OK です。
require 'net/http' Net::HTTP.post_form( URI.parse(ENV['WEBHOOK_URL']), { username: 'emattsan', content: 'Hello' } )
ファイルを POST する
前回の Elixir の記事でも書きましたが、ファイルをアップロードするときは JSON でなくて multipart/form-data で POST する必要があります。
multipart-post gem を使う
ありがたいことに multipart を扱うための gem が公開されています。
multipart で POST するためのクラス Net::HTTP::Post::Multipart
と、ファイルを扱うためのヘルパクラス UploadIO
を使います。
まず POST したいファイルから UploadIO
のオブジェクトを作成します。
次に New::HTTP::Post::Multipart
のオブジェクトを作成します。
ここでドキュメントにあるように、パラメータ の Hash にキーを file にして UploadIO
のオブジェクトを渡します。
あとは Net::HTTP::Post
を利用するばあいと同じ手順で POST できます。
require 'json' require 'net/http' require 'net/http/post/multipart' url = URI.parse(ENV['WEBHOOK_URL']) file = UploadIO.new( File.new('./images/image.jpg'), # POST するファイル 'image/jpeg', # ファイルの Content-Type 'image.jpg' # POST されたときのファイル名 ) req = Net::HTTP::Post::Multipart.new( url.path, username: 'emattsan', content: 'Hello', file: file ) http = Net::HTTP.new(url.host, url.port) http.use_ssl = true http.request(req)
自力で multipart を書く
有用な gem があるのでわざわざ自力で書かなくてもよいのですが、multipart の理解のため、また万が一公開されている gem を利用できないケースのため、一例を上げておきます。
multipart ではバウンダリで区切られた複数のコンテンツを POST の body に記述します。
えー…、詳しくは公式の資料とか Multipart messages - MIME - Wikipedia とか参照してみてください。
require 'net/http' boundary = 'DiscordMultipartMessage' url = URI.parse(ENV['WEBHOOK_URL']) req = Net::HTTP::Post.new( url.path, 'Content-Type': "multipart/form-data; boundary=#{boundary}" ) # body にバウンダリで区切ったフィールドを記述する # このときファイルは読み込んだファイルの内容を挿入します req.body = <<~BODY --#{boundary} Content-Disposition: form-data; name="username" emattsan --#{boundary} Content-Disposition: form-data; name="content" Hello --#{boundary} Content-Disposition: form-data; name="file"; filename="image.jpg" #{File.read('./images/image.jpg')} --#{boundary}-- BODY http = Net::HTTP.new(url.host, url.port) http.use_ssl = true http.request(req)
いつか読むはずっと読まない:組合せ数学
組合せ論って面白いですよね。 読んでいるといつの間にか「どう書く」の問題を考えてしまっています。