Skip to content

Instantly share code, notes, and snippets.

@tomohiro
Created October 26, 2009 13:11
Show Gist options
  • Select an option

  • Save tomohiro/218629 to your computer and use it in GitHub Desktop.

Select an option

Save tomohiro/218629 to your computer and use it in GitHub Desktop.
IRC チャンネルに JOIN して mixi ボイスの閲覧・投稿・返信・削除を行う Bot
#!/usr/bin/env ruby
$LOAD_PATH << 'lib'
$LOAD_PATH << '../lib'
require 'optparse'
require 'rubygems'
require 'daemons'
require 'net/irc'
require 'uri'
require 'mechanize'
require 'nokogiri'
Version = '0.0.1'
class MixiVoiceBot < Net::IRC::Client
MIXI_LOGIN_URI = 'http://mixi.jp'
RECENT_VOICE_URI = 'http://mixi.jp/recent_echo.pl'
def initialize
Daemons.daemonize
setup_options
super(@irc_host, @irc_port, {
:nick => @bot_name,
:user => @bot_name,
:real => @bot_name,
:channel => @channel,
})
@agent = WWW::Mechanize.new
if ENV['http_proxy']
proxy = URI.parse(ENV['http_proxy'])
@agent.set_proxy(proxy.host, proxy.port)
end
@caches = []
@identity = nil
end
def setup_options
@irc_port = 6667
@bot_name = 'voice'
@crawl_time = 60
ARGV.options do |o|
o.on('-h', '--irc-host HOST', '接続先の IRC サーバ名') { |v| @irc_host = v }
o.on('-p', "--irc-port [PORT=#{@irc_port}]", '接続先の IRC ポート番号 (規定は 6667)') { |v| @irc_port = v }
o.on('-b', "--bot-name [BOT=#{@bot_name}]", 'mixi ボイスを発言する bot の名前 (規定は voice)') { |v| @bot_name = v }
o.on('-c', '--channel CHANNEL', '接続先のチャンネル名') { |v| @channel = v }
o.on('-n', '--nickname NICK', 'ボイスへの投稿を許す IRC のニックネーム') { |v| @nickname = v }
o.on('-t', "--crwal-time [SEC=#{@crawl_time}]", 'mixi ボイスをクロールする間隔 (規定は 60秒)') { |v| @crawl_time = v }
o.on('-E', '--email EMAIL', 'mixi のログイン Email アドレス') { |v| @email = v }
o.on('-P', '--password PASSWORD', 'mixi のログインパスワード') { |v| @password = v }
o.parse!
end
end
def start
mixi_login
@identity = get_identity
@socket = TCPSocket.open(@host, @port)
@socket.gets
post(NICK, @opts.nick)
post(USER, @opts.user, '0', '*', @opts.real)
post(JOIN, @opts.channel)
post_thread = Thread.new do
while line = @socket.gets
message = Message.parse(line)
puts message
name = "on_#{(COMMANDS[message.command.upcase] || message.command).downcase}"
send(name, message) if respond_to? name
end
end
get_thread = Thread.new do
loop do
get_voice
sleep @crawl_time
end
end
post_thread.join
get_thread.join
rescue IOError => e
@log.error 'IOError' + e.to_s
ensure
finish
end
def on_privmsg(message)
if message.prefix =~ Regexp.new(@nickname)
case message[1]
when /^re ([0-9]+) (.+)/
reply_voice($1, $2)
when /^rm ([0-9]+)/
remove_voice($1)
else
post_voice(message[1])
end
get_voice
end
end
def mixi_login
@agent.get MIXI_LOGIN_URI do |login_page|
login_page.form 'login_form' do |form|
form.email = @email
form.password = @password
end.submit
end
end
def post_voice(voice)
@agent.get RECENT_VOICE_URI do |post_page|
post_page.form_with(:action => 'add_echo.pl') do |form|
form.body = voice
end.submit
end
end
def reply_voice(key, voice)
if @caches.has_key? key
member_id = @caches[key][:member_id]
post_time = @caches[key][:post_time]
@agent.get RECENT_VOICE_URI do |post_page|
post_page.form_with(:action => '/add_echo.pl') do |form|
form.body = voice
form.parent_member_id = member_id
form.parent_post_time = post_time
end.submit
end
else
post(NOTICE, @opts.channel, '指定された返信先が見つかりません')
end
end
def remove_voice(post_time)
@agent.post "http://mixi.jp/delete_echo.pl?post_time=#{post_time}&post_key=#{@identity}&redirect=recent_echo"
@caches = crawl_recent_voice
end
def get_voice
voices = crawl_recent_voice
voices.sort.each do |key, voice|
if @caches.empty? or !@caches.has_key? key
post(PRIVMSG, @opts.channel, "[#{voice[:nickname]}]#{voice[:reply]} #{voice[:comment]} (#{key})")
sleep 2
end
end
@caches = voices
end
def get_identity
recent_page = @agent.get RECENT_VOICE_URI
identity = (Nokogiri::HTML(recent_page.body)/'input#post_key').first['value']
end
def crawl_recent_voice
recent_page = @agent.get RECENT_VOICE_URI
voices = {}
(Nokogiri::HTML(recent_page.body)/'td.comment').each do |comment|
key = timestamp(comment)
voices[key] = build_voice(comment)
end
voices
end
def timestamp(comment)
comment.at('div.echo_post_time').text
end
def build_voice(comment)
{
:member_id => comment.at('div.echo_member_id').text,
:post_time => comment.at('div.echo_post_time').text,
:nickname => comment.at('div.echo_nickname').text,
:reply => ((' ' + comment.at('a').text) if comment.at('a').text =~ /^>/),
:comment => comment.at('div.echo_body').text
}
end
end
MixiVoiceBot.new.start
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment