Posted at

ruby で Mastodon の Streaming API を受信する

More than 1 year has passed since last update.

Mastodon の bot 等を作るために Streaming API を取得したかったのですが mastodon-api gem には REST API しかなかったのと、具体的なコード例がなかったのとでいくらか調査が必要でした。

その調べた結果を残しておきます。

以下の記事も参考にしました

「PHPでMastodonのStreaming APIを受信する」

http://qiita.com/yyano/items/841f79266faf2dc8b6dc


Streaming API 概要

タイムラインや通知を Websocket で受け取ることができます。

URL は https://(インスタンスのURL)/api/v1/streaming

アプリケーションの API アクセストークンとタイムライン種類をクエリで渡すことができます https://(インスタンスのURL)/api/v1/streaming?access_token=(アクセストークン)&stream=[user|public|hashtag]

アクセストークンの取得については他の Mastodon API 解説記事を参照してください。


faye-websocket gem を使う例

以下の gem を使うのでインストールする。


  • eventmachine

  • faye-websocket

require "eventmachine"

require "faye/websocket"

INSTANCE="(インスタンスのURL)"
TOKEN='(アクセストークン)'
TL='user'

EM.run do
conn = Faye::WebSocket::Client.new(
"wss://#{INSTANCE}/api/v1/streaming?access_token=#{TOKEN}&stream=#{TL}",
)

conn.on :open do |e|
puts "connection success."
end

conn.on :error do |e|
puts "error occured."
end

conn.on :close do |e|
puts "connection close."
end

conn.on :message do |msg|
puts "message receive."
puts msg.data
end
end


websocket-client-simple gem を使う例

必要なのは client だけだとか、eventmachine でネイティブエクステンションをインストールしたくないとかいった場合 websocket-client-simple を使うのが良いです。

以下の gem を使うのでインストールする。


  • websocket-client-simple

require 'websocket-client-simple'

INSTANCE="(インスタンスのURL)"
TOKEN='(アクセストークン)'
TL='user'

url = "https://#{INSTANCE}/api/v1/streaming?access_token=#{TOKEN}&stream=#{TL}"

# --- streaming receiver
begin
ws = WebSocket::Client::Simple.connect(url)
rescue => e
puts "error: #{e}"
else
ws.on :message do |msg|
puts "!message"
p msg
end

ws.on :open do
puts "streaming open"
end

ws.on :close do |e|
puts "close"
p e
exit 1
end

ws.on :error do |e|
p e
end
end

loop do
sleep 1
end


簡単な bot のサンプル

メンションで「にゃーん」と言われたら、相手に「にゃーん」と返す簡単な応答 bot です。

Streaming API で通知を監視し、該当のメンションを見つけたら REST API で応答しています。

以下の gem を使用しています


  • mastodon-api

  • websocket-client-simple

  • nokogiri

# coding: utf-8

require 'mastodon'
require 'websocket-client-simple'
require 'nokogiri'
require 'json'

# --- debug switch
VERB = true

# --- config
INSTANCE="(インスタンスのURL)"
TOKEN='(アクセストークン)'
TL='user'

BASEURL = "https://#{INSTANCE}"
STREAMURL = "#{BASEURL}/api/v1/streaming?access_token=#{TOKEN}&stream=#{TL}"

queue = Queue.new
mastodon_client = Mastodon::REST::Client.new(base_url: BASEURL, bearer_token: TOKEN)

# --- action
def answer(request)
result = {}
p request if VERB
contents = Nokogiri::HTML.parse(request["status"]["content"])
text = ''
contents.search('p').children.each do |item|
text += item.text.strip if item.text?
end
#
if text == "にゃーん"
result[:username] = request["account"]["username"]
result[:answer] = "にゃーん"
end
#
p result
return result
end

# --- streaming receiver
begin
ws = WebSocket::Client::Simple.connect(STREAMURL)
rescue => e
puts "error: #{e}"
else
ws.on :message do |msg|
if msg.data.size > 0
begin
toot = JSON.parse(msg.data)
if toot["event"] == "notification"
body = JSON.parse(toot["payload"])
if body["type"] == "mention"
puts "!message" if VERB
res = answer(body)
if res.size > 0
queue.push(res)
end
end
end
rescue => e
puts "content parse error."
puts e
end
end
end

ws.on :open do
puts "streaming open"
end

ws.on :close do |e|
puts "close"
p e
exit 1
end

ws.on :error do |e|
p e
end
end

# --- wait loop and response sender
loop do
action = queue.pop
if action.key?(:username) && action.key?(:answer)
toot = "@#{action[:username]} #{action[:answer]}"
begin
result = mastodon_client.create_status(toot)
p result if VERB
rescue => e
p e
exit 1
end
end
sleep 0.2
end