Tech Racho エンジニアの「?」を「!」に。
  • 開発

HaskellでTwitterのタイムライン取得とツイート投稿をやってみた

はじめまして、jhondaです。
入社したばかりでまだまだ勉強することだらけですが、今回はお仕事以外のテーマで書きたいと思います。
以前 Haskell の勉強を兼ねて、おうちで TwitterのBot を作りました。
Twitter の API を 用いて Haskell でタイムラインの取得とツイートの投稿ができるプログラムを作成するところまでまとめてみます。

準備

  • stack (Haskell のビルドツール) を入れる。

  • Twitter のアカウントを作成する。

    • ホームタイムライン確認のために適当にフォローしておく。
  • Application Management で App を登録し API Key を取得する。以下の4つをあとで使用する。
    • Consumer Key
    • Consumer Secret
    • Access Token
    • Access Token Secret

プロジェクトの作成

stack new {プロジェクト名}でプロジェクトの雛形をつくる。

% stack new twitter-bot # プロジェクト作成
% tree twitter-bot # ディレクトリ構造はこんな感じ
twitter-bot
├── ChangeLog.md
├── LICENSE
├── README.md
├── Setup.hs
├── app
│   └── Main.hs
├── package.yaml
├── src
│   └── Lib.hs
├── stack.yaml
├── test
│   └── Spec.hs
└── twitter-bot.cabal

いくつかパッケージを利用するので、package.yamlのdependenciesに記述を追加する。

library:
  source-dirs: src
  dependencies:
  - http-conduit       # HTTPリクエストを投げるのに使う
  - authenticate-oauth # OAuth認証に使う
  - text               # Haskellでは文字列の扱いがやや面倒なので、楽にする
  - aeson              # JSONパーザ

executables:
  twitter-bot-exe:
    main:                Main.hs
    source-dirs:         app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - twitter-bot
    - text # こっちにも追加

コーディング

まずは Lib.hs に。

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}

module Lib
    ( Tweet (..)
    , getHomeTL
    , tweet
    ) where

import Data.Text
import Data.Text.Encoding
import Data.Aeson
import GHC.Generics
import Network.HTTP.Conduit
import Web.Authenticate.OAuth

newtype Tweet  = Tweet { text :: Text
                       } deriving (Show, Generic)

instance FromJSON Tweet
instance ToJSON Tweet

myName = "jhonda_bot" -- botのTwitterアカウント名

myOAuth = newOAuth
    { oauthServerName     = "api.twitter.com"
    , oauthConsumerKey    = "your consumer key"     -- https://apps.twitter.com/ で取得したやつ
    , oauthConsumerSecret = "your consumer secret"  -- https://apps.twitter.com/ で取得したやつ
    }

myCredential = newCredential
    "your access token"        -- https://apps.twitter.com/ で取得したやつ
    "your access token secret" -- https://apps.twitter.com/ で取得したやつ

getHomeTL :: IO (Either String [Tweet])
getHomeTL = do
    response <- do
        req <-
            parseRequest
            $ "https://api.twitter.com/1.1/statuses/home_timeline.json?screen_name="
            ++ myName
        signedReq <- signOAuth myOAuth myCredential req
        manager   <- newManager tlsManagerSettings
        httpLbs signedReq manager
    return $ eitherDecode $ responseBody response

tweet :: Text -> IO ()
tweet tw = do
    req     <- parseRequest "https://api.twitter.com/1.1/statuses/update.json"
    manager <- newManager tlsManagerSettings
    let postReq = urlEncodedBody [("status", encodeUtf8 tw)] req
    signedReq <- signOAuth myOAuth myCredential postReq
    httpLbs signedReq manager
    return ()

Main.hs も書く。

{-# LANGUAGE OverloadedStrings #-}

module Main where

import Lib
import qualified Data.Text.IO as T

main :: IO ()
main = do
  -- ツイートを投稿する
  tweet "5000兆円欲しい!"
  -- とりあえずホームタイムラインの最新10件を表示する
  homeTL <- getHomeTL
  case homeTL of
    Left err -> error err
    Right tl -> mapM_ (T.putStrLn . text) $ take 10 tl

実行

% stack build # ビルドする
% stack exec twitter-bot-exe # 実行する

これで "5000兆円欲しい!" のツイートとホームタイムラインの最新10件が出力されるはずです。

最後に

これでHaskellでツイートの取得と投稿ができるようになりました。

ここまでできれば、ツイートを拾って加工して投稿する、という風にしてcronとかで定期実行させてやればBotらしくすることもできると思います。


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。