ElixirのPhoenixChannelClientでTwitterを再配信する

　以前の記事「[Phoenix Channelとelm-phoenixについて - Qiita](https://qiita.com/sand/items/f7f6fd8d93e68d651667)」でElmクライアントからPhoenix channelを利用する方法を示しました。この時のexampleはチャットシステムでした。以下のような会話の流れになります。

```
Elmクライアント  <---> Phoenix Channel <---> Elmクライアント
```

　今回はこのチャットシステムをそのまま変更せずに利用して、これにElixirクライアントを加えてみたいと思います。Elixirクライアントは、ブラウザアプリではないので、TwitterからTweetを拾って、そのままchannelに流したいと思います。

```
Elmクライアント  <--- Phoenix Channel <--- Elixirクライアント <---Twitter Stream
```

　ElixirとTwitterについても過去の記事「[ElixirでTwitterを検索する - Qiita](https://qiita.com/sand/items/bd7ea6d3f455ee63fa42)」を参考にします。


　さて、Elixir を Phoenix channelのクライアントとして使うために、[PhoenixChannelClient](https://github.com/ryo33/phoenix-channel-client)というライブラリをを選択しました。同じようなライブラリがいくつかありましたが、ヤマカンで使いやすそうなものを選びました。いい加減です。


　プロジェクトを開始します。

```sh
mix new channel_client
cd channel_client
```

　次にapplication environmentを利用して初期設定を行います。すなわちmix newで作成されたconfig/config.exsファイルにTwitter APIのキーを設定します。以下の項目をファイルの末尾に追加します。

```elixir:config/config.exs
#
config :extwitter, :oauth, [
   consumer_key: "xxxxx",
   consumer_secret: "xxxxx",
   access_token: "xxxxx",
   access_token_secret: "xxxxx"
]
#
```

　さて、mix.exsにphoenixchannelclientと、twitter関連のライブラリを追加します。

```elixir:mix.exs
#
defp deps do
  [
    {:phoenixchannelclient, "~> 0.1.0"},
    {:oauther, "~> 1.1"},
    {:extwitter, "~> 0.8"}
  ]
end
#
```

　次のコマンドでライブラリをインストールします。

```sh
mix deps.get
```


　これで準備が整いました。
　別端末を開いて「[Phoenix Channelとelm-phoenixについて - Qiita](https://qiita.com/sand/items/f7f6fd8d93e68d651667)」のチャットサーバを立ち上げます。

```sh
iex -S mix phx.server
```

　ブラウザからこのサーバにアクセスして、チャットウィンドウを開きます。User1でログインします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/61915/03835cf8-15f1-8236-3384-edd7dad457e7.png)



 さて元のElixirの端末に戻り、シェルを立ち上げます。


```sh
iex -S mix
```

　シェルが立ち上がったら、以下のコマンドを順番に入力します。特にプログラムファイルは書きません。

```elixir
{:ok, pid} = PhoenixChannelClient.start_link()

{:ok, socket} = PhoenixChannelClient.connect(pid,
  host: "localhost",
  port: 4000,
  path: "/socket/websocket",
  secure: false,
  heartbeat_interval: 30_000)

channel = PhoenixChannelClient.channel(socket, "room:lobby", %{user_name: "User9"})

PhoenixChannelClient.join(channel) 

pid = spawn(fn ->
  stream = ExTwitter.stream_filter(track: "ラーメン")
  for tweet <- stream do
    IO.puts tweet.text
    PhoenixChannelClient.push(channel, "new_msg", %{msg: tweet.text})
  end
end)
```

　socketを作り、channelを作り、joinし、twitterのstreamをリッスンし、「ラーメン」というキーワードを含んでいたら、channelにチャットを書き込みます。ポイントは「"room:lobby"」というtopicに「 %{user_name: "User9"}」でjoinしていることです。サーバ側のchannelにインターフェースを合わせています。これだけで既存のチャットシステムに参加できてしまいます。

　以下が、Elixirクライアントの端末のキャプチャーです。IO.puts tweet.text　の出力が流れています。

```
[root@www13134uf channel_client]# iex -S mix
Erlang/OTP 20 [erts-9.1] [source] [64-bit] [smp:3:3] [ds:3:3:10] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.7.0-dev) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> {:ok, pid} = PhoenixChannelClient.start_link()
{:ok, #PID<0.164.0>}
iex(2)> {:ok, socket} = PhoenixChannelClient.connect(pid,
...(2)>   host: "localhost",
...(2)>   port: 4000,
...(2)>   path: "/socket/websocket",
...(2)>   secure: false,
...(2)>   heartbeat_interval: 30_000)
{:ok, %PhoenixChannelClient.Socket{server_name: #PID<0.164.0>}}
iex(3)> channel = PhoenixChannelClient.channel(socket, "room:lobby", %{user_name
: "User9"})
%PhoenixChannelClient.Channel{
  params: %{user_name: "User9"},
  socket: %PhoenixChannelClient.Socket{server_name: #PID<0.164.0>},
  topic: "room:lobby"
}
iex(4)> PhoenixChannelClient.join(channel)
{:ok, %{}}
iex(5)> pid = spawn(fn ->
...(5)>   stream = ExTwitter.stream_filter(track: "ラーメン")
...(5)>   for tweet <- stream do
...(5)>     IO.puts tweet.text
...(5)>     PhoenixChannelClient.push(channel, "new_msg", %{msg: tweet.text})
...(5)>   end
...(5)> end)
#PID<0.206.0>
すき焼き
焼肉
ラーメン
寿司
てりたま
フライドポテト
卵かけご飯
焼き鳥丼
唐揚げ
カツ丼
ピザ

食べるのを控えてる食べ物たちであり今食べたいもの?ゐい陲
ｶﾗｵ皺ﾋｶｵ・邁ﾊｰｰｫｰ蟆ﾄｰﾒｯﾙｱｱ族銧ｻｰｫｰｱｰ・ﾆｰﾕｰｻｰｯ・
ｻ床ｧｰｭｰｰｹﾏｰﾞｷﾆｰﾕｰｻｰｰ・ｴｰﾟｰ擴擴擴・
 きちゃった… (@ ラーメン きずな - @ra_men_kizuna_ in 岡山市, 岡山県) https://t.co/9aPvqSFKPr
RT @purichannel: 初めて煮干しのまぜそば食べてみた！旨すぎ！

https://t.co/NDnFmWldSh

柏のラーメン屋！！

#まぜそば
#ラーメン
#煮干し https://t.co/XOEWRPxUYk
らーめん穀雨＠渋谷・ワンタンめん (3個入り) 。
喧騒から離れた、路地裏にひっそり佇むお店。醤油系の清湯スープに、中細ストレート麺。ワンタンは一口サイズで、味は勿論、見た目も品を感じる一杯。\830。
#ラーメン #ramen https://t.co/x2EgAYaK1Z
iex(6)>
```


　以下が、Elixirクライアントの端末のキャプチャーです。 PhoenixChannelClient.push(channel, "new_msg", %{msg: tweet.text})　の書き込みが自動的に流れています。


![image.png](https://qiita-image-store.s3.amazonaws.com/0/61915/a535e09c-fd19-6534-9dae-a09be91f82af.png)

　これはブラウザクライアントが増えても、すべてにTweetsが配信されます。ブラウザ同士でチャットもできます。チャットシステムにElixirクライアントを加えて、Twetterの書き込みを流しているだけですから。これで完成です。


　最後に、本プログラムには必要ないものですが、channerの双方向性も押さえておきたいと思います。Elixirクライアントでもチャットメッセージを受け取るためには、iexシェル上で以下のコマンドを打つ必要があります。これを設定すれば、ブラウザクライアントから送られたチャットがiex上に流れるようになります。

```elixir
receive do
  {"new_msg", message } -> IO.puts (message["user_name"] <>": "<> message["msg"])
  :close -> IO.puts("closed")
  {:error, error} -> IO.inspect error
end
```

　今回の話はこれで終わりです。今回の記事は、半年以上前に書いた記事「[Meteor and Reactによるリアクティブシステム「ラーメン野郎を追いかけろ！ ＠ Twitter」を作ってみた - Qiita](https://qiita.com/sand/items/02219ec5e42cc406ee5c)」がモティーフになっています。このようなリアルタイムシステムを作るのに、Meteorは素晴らしいプラットフォームを提供してくれました。サーバ側にMongoDB、クライアント側にmini-MongoDBを配して、透過的に同期を取るシステムはシンプルでとても魅力的でした。GoogleのFirebaseを除いては、これに代わるものは知りません。今回は、Phoenix channelを使えば同じようなシステムが構築できるのではないかと思い、試してみました。しかもPhoenix channelはMeteorにない魅力もあります。次回はクライアントをReactに変えて、もっと「ラーメン野郎を追いかけろ」と同じようなものを作りたいと思います。地図とTweetを表示したい。