第12回北海道開発オフに参加した
今日は第12回北海道開発オフでした。
開発オフはいつも通り、勉強会というよりはサークルのようなものなので、やってる事はいつも通り「各個人が黙々と開発して、ランチして、成果発表する」なのですが、今回はなんと過去最多の20人の参加!すごい!人が増えてもやることは変わりませんけど(笑)でもこの「もくもくやる」という事の楽しさが、少しでも多くの人に伝わってきたのかなぁ、なんて思います。
さて、今日の僕の成果ですが、しばらく中途半端になっていた、我が愚息@dabesaの教育です。結構作り込みを変えたのですが、大きくは以下のような構造をしています。
---
・Twitter絡みの部分は全てTwitter4Rを使用。
・@smokeymonkeyのFriend Timelineから最新200ポストを取得。
・元となるデータファイルに、最新200ポストをマージ。このデータファイルは常に新しいほうから5000個のポストを保持。
・5000個のポストをMeCabで形態素解析して、マルコフテーブルを生成。このとき、各ポストの一番最初の単語にフラグをつけておく。
・フラグをつけられた「各ポストの一番最初の単語」をランダムに抽出し、そこから文章を生成。
・生成された文章のうち、一部を北海道弁に置換。
・Twitterにポスト。
・Replyは、Replyでもらったポストから名詞・動詞を抽出し、その単語を元に文章を生成して返す。ただし文章を生成出来なかった場合は複数パターンの応答を返す。
・一日2回(9時・21時)、Google TrendsからHottrendキーワードをポストする。
---
ソース自体は素人の日曜大工なので腐ってるのですが、まぁ以前よりはちゃんと日本語っぽい言葉を発することが出来てるんじゃないかな、と思います。今後も改善していきたいですね。
人様にさらせるほど立派なものでは無いですが、一応ソースは以下に。
開発オフはいつも通り、勉強会というよりはサークルのようなものなので、やってる事はいつも通り「各個人が黙々と開発して、ランチして、成果発表する」なのですが、今回はなんと過去最多の20人の参加!すごい!人が増えてもやることは変わりませんけど(笑)でもこの「もくもくやる」という事の楽しさが、少しでも多くの人に伝わってきたのかなぁ、なんて思います。
さて、今日の僕の成果ですが、しばらく中途半端になっていた、我が愚息@dabesaの教育です。結構作り込みを変えたのですが、大きくは以下のような構造をしています。
---
・Twitter絡みの部分は全てTwitter4Rを使用。
・@smokeymonkeyのFriend Timelineから最新200ポストを取得。
・元となるデータファイルに、最新200ポストをマージ。このデータファイルは常に新しいほうから5000個のポストを保持。
・5000個のポストをMeCabで形態素解析して、マルコフテーブルを生成。このとき、各ポストの一番最初の単語にフラグをつけておく。
・フラグをつけられた「各ポストの一番最初の単語」をランダムに抽出し、そこから文章を生成。
・生成された文章のうち、一部を北海道弁に置換。
・Twitterにポスト。
・Replyは、Replyでもらったポストから名詞・動詞を抽出し、その単語を元に文章を生成して返す。ただし文章を生成出来なかった場合は複数パターンの応答を返す。
・一日2回(9時・21時)、Google TrendsからHottrendキーワードをポストする。
---
ソース自体は素人の日曜大工なので腐ってるのですが、まぁ以前よりはちゃんと日本語っぽい言葉を発することが出来てるんじゃないかな、と思います。今後も改善していきたいですね。
Ruby 逆引きレシピ すぐに美味しいサンプル&テクニック 232 (PROGRAMMER’S RECIPE)
posted with amazlet at 09.09.13
島田 浩二 設樂 洋爾 村田 賢太 前田 智樹 谷口 文威
翔泳社
売り上げランキング: 168012
翔泳社
売り上げランキング: 168012
人様にさらせるほど立派なものでは無いですが、一応ソースは以下に。
#! /usr/bin/ruby -Ku require 'rubygems' gem 'twitter4r' require 'time' require 'twitter' require 'pit' require 'MeCab' require 'kconv' require 'uri' require 'net/http' require 'rexml/document' require 'cgi' class Hash def to_http_str self.map{|key,value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&") end end # ************************************************ # データを取得するところ # ************************************************ def getData data = Array.new fdata = Array.new ndata = Array.new # smokeymonkeyのアカウントを使う config = Pit.get("Twitter") cl = Twitter::Client.new(config) # 元となるデータを取得 timeline = cl.timeline_for(:friend, :count=>200) do |status| post = Kconv.kconv("#{status.text}",Kconv::UTF8) if /[!-~]/ !~ post then data.push(post) end end # データファイル読み込み f = File.open("data.txt",'r') fdata = f.readlines f.close # データファイルの内容に最新データを追加 fdata.concat(data) # データファイルの最新5000行を取得 i=0 fdata.reverse_each do |post| ndata.unshift post if i > 5000 then break end i = i+1 end # データファイルに書き込み f = File.open("data.txt",'a') f.puts ndata f.close # 分析用データを返す return ndata end # ************************************************ # マルコフしちゃうところ # ************************************************ def makeMarkov(data) ary = Array.new tbl = Array.new c = MeCab::Tagger.new(ARGV.join(" ")) # データからマルコフテーブル作成 data.each do |line| line.chop! node = c.parseToNode(line) node = node.next i = 0 while node do hash = Hash.new hash["0"] = i hash["1"] = node.surface node = node.next if node then hash["2"] = node.surface else hash["2"] = nil end ary.push(hash) # 各文章の最初の単語だけのArrayを作成 if hash["0"] == 0 then tbl.push hash end i = i+1 end end return ary,tbl end # ************************************************ # マルコフしちゃうところ # ************************************************ def execMarkov(ary,tbl) mess = "" # 連鎖する post = tbl[rand(tbl.size)] mess.concat(post["1"]) # 継続する単語が無くなるまでループ while post["2"] != nil do # 候補を抽出 rndline = Array.new i = 0 ary.each do |line| if post["2"] == line["1"] then rndline.push(line) i = i + 1 end end # 候補からランダムに接続 post = rndline[rand(i)] mess.concat(post["1"]) # 候補が以下3種類だった場合はEnd s1 = Kconv.kconv("。",Kconv::UTF8) s2 = Kconv.kconv("!",Kconv::UTF8) s3 = Kconv.kconv("?",Kconv::UTF8) if post["1"] == s1 or post["1"] == s2 or post["1"] == s3 then post = Hash.new end end # ポストするメッセージを戻す return mess end # ************************************************ # 北海道弁変換するところ # ************************************************ def subDabesa(mess) mess.gsub!(/すごく/,'なまら') mess.gsub!(/すげえ/,'なまら') mess.gsub!(/とても/,'なまら') mess.gsub!(/バカ/,'はんかくさ') mess.gsub!(/酷い/,'わやい') mess.gsub!(/ひどい/,'わやい') mess.gsub!(/落ち着かない/,'あずましくない') mess.gsub!(/交換した/,'バクった') mess.gsub!(/いじった/,'ちょした') mess.gsub!(/バイバイ/,'したっけ') mess.gsub!(/冷たい/,'しゃっこい') mess.gsub!(/疲れた/,'こわい') mess.gsub!(/大変だ/,'ゆるくない') mess.gsub!(/痒い/,'いずい') mess.gsub!(/ヤバイ/,'やくい') mess.gsub!(/ヤバい/,'やくい') mess.gsub!(/やばい/,'やくい') mess.gsub!(/格好悪い/,'がさい') mess.gsub!(/カッコ悪い/,'がさい') mess.gsub!(/頑張/,'けっぱ') mess.gsub!(/捨て/,'なげ') mess.gsub!(/本州/,'内地') mess.gsub!(/小さい/,'ちゃんこい') mess.gsub!(/ちいさい/,'ちゃんこい') mess.gsub!(/止めろ/,'止めれ') mess.gsub!(/やめろ/,'やめれ') mess.gsub!(/だな/,'だべな') mess.gsub!(/だね/,'だべね') return mess end # ************************************************ # ポストするところ # ************************************************ def putPost(mess) # Twitterにポスト config = Pit.get("dabesa") cl = Twitter::Client.new(config) cl.status(:post,mess) # Wassrにポスト wmess = 'status=' + URI.escape(mess) req = Net::HTTP::Post.new("/statuses/update.json?") req.basic_auth(config["login"],config["password"]) Net::HTTP.start('api.wassr.jp',80) {|http| response = http.request(req,wmess) } end # ************************************************ # RepliesからReplyを返信する # ************************************************ def SendReplies(ary,tbl) data = Array.new c = MeCab::Tagger.new(ARGV.join(" ")) config = Pit.get("dabesa") cl = Twitter::Client.new(config) flg = 0 sid = nil idfile = open("repid.txt",'r') idfile.each do |id| sid = id.chop end idfile.close # Twitterのreplies APIを取得 req = Net::HTTP::Get.new("/statuses/replies.xml") req.basic_auth(config["login"],config["password"]) xml = Net::HTTP.start('twitter.com',80) {|http| http.request(req).body } # API(XML)を解析しstatusがある間ループ doc=nil doc=REXML::Document.new xml doc.elements.each('/statuses/status') do |elm| scname = elm.elements['user/screen_name'].text.to_s # dabesa自身のポストなら無視 if scname == "dabesa" then next end # 最新Replyのidをファイルに書き込み if flg == 0 and elm.elements['id'].text.to_i >= sid.to_i then idfile = open("repid.txt",'w') idfile.puts elm.elements['id'].text.to_i idfile.close flg = 1 end # 取得していた最新Replyまで到達したらBreak(既にRelyしたポストは無視) if elm.elements['id'].text.to_i <= sid.to_i then break end #Replyのテキストを取得し、名詞と動詞を探す post = elm.elements['text'].text # 半角全角を削除 while /^\.*\/*@[A-Za-z0-9_]*/ =~ post do post.sub!(/^\.*\/*@[A-Za-z0-9_]*/,'') post.sub!(/\s*/,'') end post.chop! n = c.parseToNode(post) n = n.next word = Array.new while n do ftr = n.feature.split(",") if ftr[0] == "名詞" or ftr[0] == "動詞" then word.push n.surface end n = n.next end # Replyの名詞or動詞がマルコフテーブルにあればそこから文章を作る。 # マルコフテーブルに無ければ疑問を返す。 wflg = 0 ary.each do |line| if word[0] == line["1"] then post = line wflg = 1 end end mess = String.new if wflg == 1 then mess.concat(post["1"]) else mflg = rand(10) case mflg when 0,5,10 mess = word[0].to_s + "ってなんだべか。" when 1,3 mess = word[0].to_s + word[0].to_s + "はんかくさい。" when 2,6 mess = "そったら" + word[0].to_s + "とか興味無いし。" when 4,7,8,9 post = tbl[rand(tbl.size)] mess.concat(post["1"]) end post = Hash.new end # 継続する単語が無くなるまでループ while post["2"] != nil do # 候補を抽出 rndline = Array.new i = 0 ary.each do |line| if post["2"] == line["1"] then rndline.push(line) i = i + 1 end end # 候補からランダムに接続 post = rndline[rand(i)] mess.concat(post["1"]) # 候補が以下3種類だった場合はEnd s1 = Kconv.kconv("。",Kconv::UTF8) s2 = Kconv.kconv("!",Kconv::UTF8) s3 = Kconv.kconv("?",Kconv::UTF8) if post["1"] == s1 or post["1"] == s2 or post["1"] == s3 then post = Hash.new end end pmess = "@" + scname + " " + mess cl.status(:post,pmess) end end # 実行部 data = Array.new ary = Array.new tbl = Array.new data = getData() ary,tbl = makeMarkov(data) mess = execMarkov(ary,tbl) pmess = subDabesa(mess) putPost(pmess) SendReplies(ary,tbl) exit