ほわいとぼーど

ぷろぐらまのメモ帳

(ネタ)Slack+Hubotでファイルアップロードする話

深遠な理由*1によりファイルアップロードをSlack経由で行いたい。
アップロード先はAWS S3なのですが、ここではAWSの世界にファイルを持ち込むまでを書きます。
普通、ファイルアップロードしたいとなれば入り口としてWebサーバ的なものが必要ですが、
ちょっとした要件にはセキュリティコストが高くついて面倒。
SlackやGithubといった既に利用している外部サービスに乗っかればその辺のコストを省略できる、
というのが後付の狙い。

どうしたかというと、Slackのファイルアップロード機能でアップロードします。
その際にコメントに特定のキーワードを付与すると、
Hubotが裏でファイルダウンロードしてS3にアップロードします。
HubotはAWSの中にいる前提です。簡単・強引な仕様。

hubot-slackにはそういう機能はないので、生のAdapterで書きます。
要点抜粋のソースは以下。

request = require 'request'
fs = require 'fs'

module.exports = (robot) ->
  robot.adapter.client?.on? 'raw_message', (msg) ->
    if msg.type is 'message' and msg.subtype is 'file_share' and msg.text.match(/uploaded a file: \<\S+\> and commented: !fileupload/)

      filename = msg.file.url_download.split('/').pop()
      filepath = "work/#{filename}"
      file = fs.createWriteStream(filepath)
      channel = robot.adapter.client.getChannelByID msg.channel
      request
        .get(msg.file.url_download)
        .on 'error', (err) ->
          console.log "#{err}"
        .pipe(file)
        .on 'close', (resp) ->
          # post process
          robot.send {room: channel.name},  "<@#{msg.user}>: Complete file download: #{filename}"

アップロードの際のコメントは、
「uploaded a file: <filename> and commented: (comment)」
という形になるので注意が必要。
上記の例ではコメントに「!fileupload」と付ければ実行されます。
工夫すれば複数のアップロード先も制御できそう。

mgs.file.url_download でSlackにアップロードされたURLが取れるので、ダウンロードします。
この例ではrequest使ってます。後は似るなり焼くなり。
adapter.clientを使うのでchannelとかuserは自分で取得する必要があります。

欠点として、ファイルアップロードを別の場所(S3とか)にしたいのにSlackにも残ってしまう。
終わったら削除したかったのだがBotだと削除できないらしい。
ファイルサイズとかも危険なので上限をチェックするようにはしてます。
後はファイルが重複しないようにtmpdir作ったり終わったら消したり、
細かい所は割愛してますがケア必要です。

*1:「Slackからサクッとファイルアップロードできたりしないの?」と言われたとか