SpockとLINE BOTでオウム返しを作ってみた

LINE BOT API がトライアル版ですが[公開された](http://linecorp.com/ja/pr/news/ja/2016/1320)ので早速試してみました。
APIの詳細なドキュメントはここに載っています。
<https://developers.line.me/bot-api/overview>

プログラムは
<i class="fa fa-cube"></i>[とりあえずLINE BOT APIでオウムを作ってみた](http://qiita.com/masuidrive/items/1042d93740a7a72242a3)
<i class="fa fa-cube"></i>[LINE BOT をとりあえずタダで Heroku で動かす](http://qiita.com/yuya_takeyama/items/0660a59d13e2cd0b2516)
を参考にしながらオウム返しを作りました。

```hs
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Control.Lens ((^?))
import Control.Monad.IO.Class (liftIO)
import Data.Aeson
import Data.Aeson.Lens (key, _Array)
import qualified Data.ByteString.Char8 as BS
import Data.Foldable (for_)
import System.Environment (lookupEnv)

import Network.HTTP.Conduit
import Web.Spock.Safe

main :: IO ()
main = do
    -- 環境変数の読み込み
    port <- maybe 8080 read <$> lookupEnv "PORT" :: IO Int
    Just fixie_basic         <- lookupEnv "FIXIE_BASIC"
    Just line_channel_id     <- lookupEnv "LINE_CHANNEL_ID"
    Just line_channel_secret <- lookupEnv "LINE_CHANNEL_SECRET"
    Just line_channel_mid    <- lookupEnv "LINE_CHANNEL_MID"
    -- サーバーの起動
    runSpock port . spockT id $ do
       -- POST /callback
       post "callback" $ do
           b <- body
           let Just result = (b ^? key "result" . _Array)
           for_ result $ \msg -> do
               let Just content = msg ^? key "content"
                   Just from    = content ^? key "from"
               liftIO $ do
                   -- メッセージ送信リクエストの作成
                   req <- parseUrl "https://trialbot-api.line.me/v1/events"
                   manager <- newManager tlsManagerSettings
                   let res = object [ "to"        .= Array [from]
                                    , "toChannel" .= Number 1383378250
                                    , "eventType" .= String "138311608800106203"
                                    , "content"   .= content]
                       req' = req { proxy = Just (Proxy {proxyHost = "velodrome.usefixie.com", proxyPort = 80})
                                  , method = "POST"
                                  , requestHeaders = [ ("Content-Type", "application/json; charser=UTF-8")
                                                     , ("X-Line-ChannelID", BS.pack line_channel_id)
                                                     , ("X-Line-ChannelSecret", BS.pack line_channel_secret)
                                                     , ("X-Line-Trusted-User-With-ACL", BS.pack line_channel_mid)
                                                     , ("Proxy-Authorization", BS.pack $ "Basic " ++ fixie_basic)
                                                     ]
                                  , requestBody = RequestBodyLBS (encode res)
                                  }
                   -- 送信
                   httpLbs req' manager
           text ""
```

少し躓いたのは[http-conduit](http://hackage.haskell.org/package/http-conduit)の`Proxy`はプロキシのURLのBasic認証の部分をパースしてくれないので`Proxy-Authorization`のヘッダを自分で作らないといけないというところ。環境変数の`FIXIE_BASIC`にはFixieのBasic認証のユーザー名とパスワードがBase64でエンコードされたものが入っています。

上記コードは実際にHerokuにデプロイして動作確認しました。
HaskellアプリのHerokuへのデプロイ方法は手前味噌ですが以下が参考になります。
[HerokuにHaskellのアプリを公開する](http://qiita.com/lotz/items/e5d0dc85363e590f7e2b)

Haskellはいいぞ！
![](https://i.gyazo.com/69e13c1c1578198850ad7c0d837deaef.png)