#序章 日常の崩壊
Twitter上のあるアカウントが「チノちゃんチノちゃん」と、日々繰り返す。
何も変わらない、いつも通りのタイムライン。
そこにTweetDeckを劈く緑の波動。
今、TLを、「パセリ農家」が横切った。
#第一章 チノちゃんをパセリ農家に。
あれから一週間が経ったであろうか。ぼくはパセリ農家に囚われ続けていた。
「パセリ。農家。」
HIGH LOW LOW。HIGH LOW LOW。
分けて口にしてみれば何の面白みも無い文字列だ。
「パセリ農家。...ドュフフ」
LOW HIGH HIGH HIGH LOW LOW。...LOW HIGH HIGH
ぼくは遂にubuntu14.04 LTSを開き、ターミナル上に文字を書き連ねた。
# -*- coding: utf-8 -*-
require 'tweetstream'
require 'twitter'
CONSUMER_KEY = "**********"
CONSUMER_SECRET = "**********"
ACCESS_TOKEN = "**********"
ACCESS_TOKEN_SECRET = "**********"
client = Twitter::REST::Client.new do |config|
config.consumer_key = CONSUMER_KEY
config.consumer_secret = CONSUMER_SECRET
config.access_token = ACCESS_TOKEN
config.access_token_secret = ACCESS_TOKEN_SECRET
end
TweetStream.configure do |config|
config.consumer_key = CONSUMER_KEY
config.consumer_secret = CONSUMER_SECRET
config.oauth_token = ACCESS_TOKEN
config.oauth_token_secret = ACCESS_TOKEN_SECRET
config.auth_method = :oauth
end
client_stream = TweetStream::Client.new
client_stream.userstream do |status|
word = /チノちゃん/
if word =~ status.text
tweet_content = status.text.gsub(word, 'パセリ農家')
client.update(tweet_content)
end
end
解説
client_stream.userstream do |status|
からend
に挟まれる部分が実質のコードである。新しくツイートが来ると、status
にそのツイートの情報を入れ込んでこの中をループするようになっている。例えばstatus.text
にはツイートされた文章が入っている。
それまでの諸々はただの準備なので、本記事最後に参照を付けた。逆にstreamingAPIを使ってtwitter-botを作ったことが無い人は是非この実質のコード以外をコピペしてbotを作り始めて欲しい。
コードは解説するまでも無いとは思う。
- wordに /チノちゃん/ を代入
- /チノちゃん/ が入っていたら処理をする。
- ツイートテキスト内の /チノちゃん/ を
.gsub()
を利用して 'パセリ農家' に置換したテキストを生成。 - それをツイート。
#第二章 チノちゃん大発生
2015年11月25日、朝、パセリ農家botがその姿を現した。
パセリ農家bot「いやいやいやいやいやいや情報可視化の皆さんなんで自分の声録音してるんですかパセリ農家の声使えばいいでしょやり方は書いたんだから✋w」
ヤツが気づく。
堰を切ったようにタイムラインに「チノちゃん」が大量発生した。
パセリ農家bot「パセリ農家をパセリ農家にするな。」
パセリ農家bot「パセリ農家!!」
パセリ農家bot「パセリ農家botおもしろすぎるでしょパセリ農家」
パセリ農家bot「パセリ農家は」
パセリ農家bot「僕はパセリ農家!」
パセリ農家botはあらゆる「チノちゃん」を収穫し、加工し、そして出荷していく。
このままではこいつが過労死してしまう。危機感に背中を押されたぼくは、また、パソコンに向かうこととなった。
data = {
"friend_id_01" => [ [/オタク/], [] ],
"friend_id_02" => [ [/フォノン/], [] ],
"friend_id_03" => [ [/やよい/], [/高槻/,/軒/] ],
"friend_id_04" => [ [/イショティ/], [/イショティハドゥス/] ],
"friend_id_05" => [ [/天ぷら/], [] ],
"friend_id_06" => [ [/布団/], [] ],
"friend_id_07" => [ [/前川/], [/みく/] ],
"friend_id_08" => [ [/ウィルベル/], [] ]
}
def check_word(text, word_list)
result = 0
if word_list == []
return [result, text]
end
word_list.each {|word|
if word =~ text
text = text.gsub(word, 'パセリ農家')
result = 1
break
end
}
return [result, text]
end
count = 0
client_stream.userstream do |status|
name = status.user.screen_name
#対象人物のツイートかチェック
target = 0
data.each_key{|key|
if key == name
target = 1
end
}
if target == 1
#対象のNG単語があるかチェック
check_ng = check_word(status.text, data[name][1])
if check_ng[0] == 0
#対象の単語があるかチェック
check_result = check_word(status.text, data[name][0])
if check_result[0] == 1
#3割ツイート
if count < 3
sleep(count*5 + 15)
client.update(check_result[1])
end
count = (count + 3) % 10
end
end
end
end
解説
- まずはツイートする回数を減らした。当初はrubyの
class Random
を用いていたのだが、動作しているのかどうかが分かりにくかったため、変数count
を用いて一定の間隔でツイートするように変更した。 - そして、人によって反応する単語を分ける仕様に変更。そのための情報は最初のハッシュ型の
data
に込められている。そのdata
内では、"対象の人物のid"と"その人用の単語リスト"が関連付けされており、また、単語リストも更に、"変換対象の単語のリスト"と"非変換対象の単語のリスト"に分かれている。 - 非変換対象の単語は、例えば「やよい軒」を「パセリ農家軒」に置換してしまうという過去の悲劇を避ける為のものである。
- この
data
を処理するために関数word_check()
を作成。対象単語があるかないかの判断、ついでに置換した文章もここで作成している。 - 変換対象の単語までついでにリストにしてあるのは、関数
check_word()
を変換対象リスト、非変換対象リスト、両方に使用する為である。
#第三章 秋の空
ぼくはパセリは好きではない。
とある機会に、綺麗に盛りつけられたサンドイッチと共にパセリが出てきたことがあった。
パセリ農家botに毒された人々はぼくに見せつけるようにしてそれを口へと運ぶ。
「ほらwww ほらwwwww」
何も煽らなくても良いではないか。
食べなくて良いものは食べなくて良いではないか。
ぼくは刺し身のツマと、その友達のたんぽぽの花を頭に浮かべながら、パセリを一つ、口にした。
満足。
もう結構。
一個で十分。
例え好きなものであったとしても、ものには必ず限度というものがある。
#[target_word_list, exclusive_word_list]
word_list = [
[ [/オタク/], [] ],
[ [/やよい/], [/高槻/,/軒/] ],
[ [/チノちゃん/], [] ],
[ [/天ぷら/], [] ],
[ [/藤宮さん/], [] ],
[ [/布団/], [] ],
[ [/前川/], [/みく/] ]
]
def check_word(text, word_list)
#上記paseri_ver2.rb参照
end
random = Random.new
count = 0
change = 0
word_to_detect = word_list[random.rand(word_list.length)]
client.create_direct_message('your_own_id', "begin #{word_to_detect}")
client_stream.userstream do |status|
count += 1
#change target word
if status.user.screen_name == "your_own_id"
if /change/ =~ status.text
change = 1
end
end
if count > 200
change = 1
end
if change == 1
count = 0
change = 0
word_to_detect = word_list[random.rand(word_list.length)]
client.create_direct_message('your_own_id', "change #{word_to_detect}")
end
#check target word
check = check_word(status.text, word_to_detect[0])
#check NG words
check_ng = check_word(status.text, word_to_detect[1])
#all check
if check[0] == 1 and check_ng[0] == 0
client.update(check[1])
change = 1
end
end
解説
- paseri_ver2.rbではbotが反応する単語を只管連呼すればいつか必ず反応される仕組みであった。なので反応する単語さえわかってしまえば後は全て興醒めなのである。
- そこで、反応する単語を変化するようにした。一回反応したら、若しくは反応しないまま200ツイートが流れたら、若しくは特定のidのアカウントが"change"と呟いたら、単語を変える。ついでにどの単語に変えたかはDMをしてくれる仕様になっている。
- 単語の決定は今度こそランダム。どれになったかはDMで確認すればいいので問題ない。
- 単語変化に伴って人物を絞る必要が無くなった。idごとの処理を無くしたためコードがすっきりした気がする。
#第四章 諸行無常
はっきり言おう。
ぼくはもうこれから1年はパセリを食べたくない。
踏みつけて歩く落ち葉の頭上には、まだ青さの残るイチョウの葉が、年に一度だけ輝くためにその準備を進めていた。
思い返せば少し前までは辺り一面に銀杏の実が罠を張り、ぼくらを脅かしていたこともあった。
年に一度。
黄金に。
これだけはほとんどの人が是として迎え入れるだろう銀杏の良い所である。
上を見上げながら僕は歩く。
通りではおじさんおばさんがカメラを構えている。
寒くなったとは言え、これはこれで良い季節だ。
しかし、不意に、ぼくは足裏に抵抗を感じた。
ー嫌な予感しかしない。
ぼくを嘲笑う銀杏の実が、靴を通して透けて見えた。
#あとがき
こんな記事で良いのかどうかわかりませんが折角の機会ということで書かせていただいた次第です。
一応の目的が、botを作ったことが無い人に興味を持ってもらうことでして、それゆえ汚いながらもソースコードを全面に載っけることにしました。ええ、汚いです。(しかもこのために少しコードをいじってるのでコピペでそのまま動くかは保証しかねます。) もし興味を持った人がいれば、是非少し調べて作ってみてください。難しくは無いと思います。
また、bot作ったことある人とか、そんなのつまんないって人でも楽しめるようにと、適当に散文を書きました。これがつまらなかった人はここまで読んでないでしょうから、まあいいでしょう。
正直僕自身パセリ農家botには完全に飽きているので、今後はどうすればより飽きないかを考えるしかないです。一応yahooのapiは触ってみようかなぁとか。
まぁ結論としては、良かったらパセリ農家bot、フォローしてみてください。
→@parsley_farmer
###botを作ったことがない人が本当に作ってくれる際の参考
Twitter APIの使い方まとめ
Ruby - tweetstream で Twitter Streaming API を利用!
rubyでtwitterのbotを実装する(第5世代のやり方)