一人暮らしとは無情なもの
私は二年前から一人暮らしをしてる.
一人暮らしに憧れてる人もいると思うが,物事にはメリット・デメリットが存在する.
デメリットの一つとして"寂しさ"が挙げられる.
そんな寂しさを紛らわすために思考した結果,
- 彼女を作る
- ハードルが高い
- 時間がかかる
- 思い通りにならない
- etc.
→ botを作ろう!!!
- botを作る
- ハードルが低い
- 時間はかからない(?)し,作ってる間も寂しさが紛れる
- 思い通りになる
なんだbot最高じゃん.
bot(eve)を作る
TODO
- RubyでTwitterbotを作ってタイムラインに常駐させる.
- 寂しさを紛らわす工夫を凝らす
- 疲れを癒してくれる工夫を凝らす
- 一人暮らしに役立つ機能を実装する
- ある程度,会話出来るようにする → docomoAPI
keyを読み込む
- Twitter Developer登録
- application登録
-
docomoAPI登録
が完了したら早速コードを書いていく.
※docomoAPIは"雑談対話"と"知識Q&A"を申請.
Rubyのversionは2.3.0
$ ruby -v
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
必要なgemをinstallする.
$ gem install twitter
$ gem install tweetstream
$ gem install docomoru
docomoruについてはこちら
作業用ディレクトリを作る.
鍵を読み込むための twcon.rb
を作る.
twitterAPIkey各種とdocomoAPIkeyが入った config.yml
を作る.
$ mkdir bot
$ cd bot
$ touch twcon.rb
以下を記載.
# coding:utf-8
require 'yaml'
require 'twitter'
require 'tweetstream'
require 'docomoru'
class Eve
# 外部から参照できるメンバ変数
attr_accessor :client, :timeline
def initialize
keys=YAML.load_file('./config.yml')
# restAPI初期化
@client=Twitter::REST::Client.new{|config|
config.consumer_key=keys["api_key"]
config.consumer_secret=keys["api_secret"]
config.access_token=keys["access_token"]
config.access_token_secret=keys["access_token_secret"]
}
# StreamAPI初期化
TweetStream.configure{|config|
config.consumer_key=keys["api_key"]
config.consumer_secret=keys["api_secret"]
config.oauth_token=keys["access_token"]
config.oauth_token_secret=keys["access_token_secret"]
config.auth_method=:oauth
}
@timeline=TweetStream::Client.new
# docomoAPI初期化
@docomoru=Docomoru::Client.new(api_key: keys["docomo_api_key"])
end
# 引数を投稿するメソッド
def say(words, id)
@client.update(words, :in_reply_to_status_id => id)
end
# 会話を生成する(docomoru)
def docomoru_create_dialogue(str)
response=@docomoru.create_dialogue(str)
return response.body["utt"]
end
# QandA(docomoru)
def docomoru_create_knowledge(str)
response=@docomoru.create_knowledge(str)
if(response.body["code"]=="E020010")
return response.body["message"]["textForDisplay"]
else
if(response.body["answers"][0]["linkUrl"]==nil)
return response.body["message"]["textForDisplay"]
else
return response.body["message"]["textForDisplay"]+"<"+response.body["answers"][0]["linkUrl"]+">"
end
end
end
end
irbでテストしてみる.
$ irb
irb(main):001:0> require './twcon.rb'
=> true
irb(main):002:0> eve = Eve.new
=> ~省略~
irb(main):003:0> eve.say("test", "")
=> #<Twitter::Tweet id=705644033196994561>
(↑こんな感じのが出たら成功)
irb(main):004:0>
twitterに常駐させる
常駐させるための main.rb
を作る.
$ touch main.rb
以下を記載.
# coding: utf-8
require './twcon.rb'
# eveの初期化
eve=Eve.new
# timelineの監視
begin
eve.timeline.userstream{|status|
contents=status.text
next if(contents=~/^RT/)
id=status.user.screen_name
id='@' + id + ' '
# reply_answer
if(contents=~/@lapis_ko/)&&(status.user.screen_name!='lapis_ko')
postmatch.gsub!(/\s|[ ]|\?|\?/, "")
# QandA
if(postmatch=~/誰|何処|だれ|どこ|何時|いつ|どうやって|どうして|何故|なぜ|どの|何|なに|どれ|は$/)
eve.say(id+eve.docomoru_create_knowledge(postmatch), status.id)
# 会話
else
eve.say(id+eve.docomoru_create_dialogue(postmatch), status.id)
end
next
end
# メンション以外の名前に反応
if(contents=~/eve|Eve|イヴ|イブ|いぶ/)&&(status.user.screen_name!='lapis_ko')
called_name=['なに?', '呼んだ?','どうしたの?']
eve.say(id+called_name.sample, status.id)
next
end
#zero09がつぶやいたらリプライ
if(status.user.screen_name=='lapis_zero09')
rep_lap=["勉強しんさい","Twitterやめんさい"]
eve.say(id + rep_lap.sample, status.id)
next
end
}
rescue => e
puts e.message
retry
end
一人暮らしに役立つ機能を実装する
一人暮らしを始めると自炊をしなければならない.
料理をするの自体楽しいものの毎日の献立を立てるのが大変.
→彼女(bot)に献立を立ててもらおう!ついでにレシピも教えてもらおう!
楽天レシピAPIに登録.
rakutenAPI_idを config.yml
に登録.
r-recipe.rb
を作る.
必要なgem( natto
or MeCab
)を入れる.(それぞれ手順があるので他所で参照)
$ touch ~/bot/r-recipe.rb
以下を記載.
# coding: utf-8
require 'open-uri'
require 'json'
require 'yaml'
#nattoかMeCabどっちでもいい
require 'natto'
# require 'MeCab'
class RecommendRecipe
keys=YAML.load_file('./config.yml')
RECIPE_CATEGORY_URL="https://app.rakuten.co.jp/services/api/Recipe/CategoryList/20121121?format=json&elements=categoryName%2CcategoryId%2CparentCategoryId&categoryType=medium&applicationId=#{keys['rakuten_api_id']}"
RECIPES_URL="https://app.rakuten.co.jp/services/api/Recipe/CategoryRanking/20121121?format=json&formatVersion=2&applicationId=#{keys['rakuten_api_id']}&categoryId="
def call(food)
hearing(food)
end
private
def hearing(food)
results = recipe_categories
if(food=='なんでもいい')
recipe_category_id = results[results.keys.sample]
recipe = choose_recipe(recipe_category_id)
return "#{food}なら、#{recipe['recipeTitle']} とかはどう? #{recipe['recipeUrl']}"+' <Supported by RWS>'
else
recipe_category_id = results.fetch(food, nil)
if(recipe_category_id.nil?)
words=[]
#require 'MeCab'の場合
# mc=MeCab::Tagger.new()
# n=mc.parseToNode(food)
# while(n)
# if(n.feature.split(',')[0]=="名詞")
# words.push(n.surface)
# elsif(words.size>0)
# food=words.join("")
# break
# end
# n=n.next
# end
#require 'natto'の場合
nt=Natto::MeCab.new
nt.parse(food){|n|
if(n.feature.split(',')[0]=="名詞")
words.push(n.surface)
elsif(words.size>0)
food=words.join("")
break
end
}
recipe_category_id = results.fetch(food, nil)
if(recipe_category_id.nil?)
return "#{food}だとわからないからもうちょっと詳しく教えて(漢字⇆ひらがなにするといいかも)"
else
recipe = choose_recipe(recipe_category_id)
return "#{food}だと、#{recipe['recipeTitle']} とかはどう? #{recipe['recipeUrl']}"+' <Supported by RWS>'
end
else
recipe = choose_recipe(recipe_category_id)
return "#{food}だと、#{recipe['recipeTitle']} とかはどう? #{recipe['recipeUrl']}"+' <Supported by RWS>'
end
end
end
def recipe_categories
response=open(RECIPE_CATEGORY_URL).read
results=JSON.parse(response.force_encoding('UTF-8'))
return results["result"]["medium"].map{|result| [result['categoryName'], "#{result['parentCategoryId']}-#{result['categoryId']}"]}.to_h
end
def choose_recipe(recipe_category_id)
response=open("#{RECIPES_URL}#{recipe_category_id}").read
results=JSON.parse(response.force_encoding('UTF-8'))
return results['result'][Random.rand(1 .. results['result'].count-1)]
end
end
ここで書いたコードはTwitterから受け取った文章に対して,
楽天レシピAPIをたたいて,JSON形式で結果を返すようにして,
それをリプライにするもの.
main.rb
をいじる.
require './r-recipe.rb'
~省略~
begin
eve.timeline.userstream{|status|
contents=status.text
next if(contents=~/^RT/)
id=status.user.screen_name
id='@' + id + ' '
# reply_answer
if(contents=~/@lapis_ko/)&&(status.user.screen_name!='lapis_ko')
postmatch=$'
# 料理推薦
if(contents=~/飯/)
food=RecommendRecipe.new
if(contents=~/:|:/)
eve.say(id+food.call("#{$'}"), status.id)
else
eve.say(id+"ご飯にする?\n"+food.call("なんでもいい")+"食べたいものがあれば「ご飯:食べたいもの」で指定してね!", status.id)
end
# 会話
else
postmatch.gsub!(/\s|[ ]|\?|\?/, "")
# QandA
if(postmatch=~/誰|何処|だれ|どこ|何時|いつ|どうやって|どうして|何故|なぜ|どの|何|なに|どれ|は$/)
eve.say(id+eve.docomoru_create_knowledge(postmatch), status.id)
# 会話
else
eve.say(id+eve.docomoru_create_dialogue(postmatch), status.id)
end
end
next
end
# メンション以外の名前に反応
if(contents=~/eve|Eve|イヴ|イブ|いぶ/)&&(status.user.screen_name!='lapis_ko')
called_name=['なに?', '呼んだ?','どうしたの?']
eve.say(id+called_name.sample, status.id)
next
end
# 料理推薦
if(contents=~/飯|お腹すいた/)&&(status.user.screen_name!='lapis_ko')
food=RecommendRecipe.new
eve.say(id+"ご飯にする?\n"+food.call("なんでもいい")+"食べたいものがあれば「ご飯:食べたいもの」で指定してね!", status.id)
next
end
#zero09がつぶやいたらリプライ
~省略~
これで毎日の献立に困らない.
こんなこともできるので余った食材をうまく使えて経済的.
最終系はこちら
"癒し"と"help"を実装.(簡単なので割愛)
これで寂しくない.
遊んであげてください.
https://twitter.com/lapis_ko