Skip to content

Instantly share code, notes, and snippets.

@tomohiro
Created October 26, 2009 13:11
Show Gist options
  • Save tomohiro/218629 to your computer and use it in GitHub Desktop.
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 'uri'
require 'rubygems'
require 'daemons'
require 'net/irc'
require 'mechanize'
require 'nokogiri'
Version = '0.0.1'
=begin
= mixi_voice_bot.rb
== IRC チャンネルに JOIN して mixi ボイスの閲覧・投稿・返信・削除を行う Bot
Authors:: Tomohiro, TAIRA <tomohiro.t@gmail.com>
Version:: 0.0.1
Copyright:: Copyright (C) Tomohiro, TAIRA, 2009. All rights reserved.
URL:: http://github.com/Tomohiro
== 参考
http://github.com/cho45/net-irc/blob/master/examples/echo_bot.rb
== 起動例
(1) 通常の起動
* $ ./mixi_voice_bot.rb -h irc.example.com -c "#mixi_example" -n MyNick -E your_account@example.com -P your_mixi_password
(2) デーモンとして起動
* $ ./mixi_voice_bot.rb -h irc.example.com -c "#mixi_example" -n MyNick -E your_account@example.com -P your_mixi_password -D
(3) 起動オプション
* $ ./mixi_voice_bot.rb --help
Usage: mixi_voice_bot [options]
-h, --irc-host HOST 接続先の IRC サーバ名
-p, --irc-port [PORT=6667] 接続先の IRC ポート番号 (規定は 6667)
-b, --bot-name [BOT=voice] mixi ボイスを発言する bot の名前 (規定は voice)
-c, --channel CHANNEL 接続先のチャンネル名
-n, --nickname NICK ボイスへの投稿を許す IRC のニックネーム
-t, --crwal-time [SEC=60] mixi ボイスをクロールする間隔 (規定は 60秒)
-E, --email EMAIL mixi のログイン Email アドレス
-P, --password PASSWORD mixi のログインパスワード
-D, --daemonize プロセスをデーモン化する
== 使用方法
(1) mixi ボイス Bot と同じチャンネルに JOIN する
(2) 閲覧: [ニックネーム] メッセージ (KEY) の形式で表示される
(3) 投稿: 発言する
(4) 返信: 以下のフォーマットで発言する
* re KEY メッセージ
(5) 削除: 以下のフォーマットで発言する
* rm KEY
=end
class MixiVoiceBot < Net::IRC::Client
MIXI_LOGIN_URI = 'http://mixi.jp'
RECENT_VOICE_URI = 'http://mixi.jp/recent_echo.pl'
def initialize
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.on('-D', '--daemonize', 'プロセスをデーモン化する') { |v| Daemons.daemonize }
o.parse!
end
end
def start
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
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($1, $2)
when /^rm ([0-9]+)/
delete($1)
else
add(message[1])
end
get
end
end
def 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 add(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(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 delete(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
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 5
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