Skip to content

Commit

Permalink
Broader match for channel names. Bug fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
david committed Jun 21, 2008
1 parent 0188393 commit ed31a20
Show file tree
Hide file tree
Showing 7 changed files with 355 additions and 22 deletions.
6 changes: 2 additions & 4 deletions lib/minibot/commands.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ module Commands
include Events::Constants include Events::Constants


def join(*channels) def join(*channels)
channels.each { |channel| write "JOIN #{channel}" } channels.each { |channel| server.write "JOIN #{channel}" }
end end


TOPIC_REPLIES = [ RPL_NOTOPIC, [ RPL_TOPIC, RPL_TOPIC_META ], RPL_TOPIC ]

def topic(channel) def topic(channel)
topic = author = timestamp = nil topic = author = timestamp = nil
server.write "TOPIC #{channel}", *TOPIC_REPLIES do |code, reply| server.write "TOPIC #{channel}", *EXPECTED_REPLIES_TOPIC do |code, reply|
if code == RPL_TOPIC if code == RPL_TOPIC
topic = reply topic = reply
elsif code == RPL_TOPIC_META elsif code == RPL_TOPIC_META
Expand Down
2 changes: 2 additions & 0 deletions lib/minibot/constants.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ module Constants
RPL_TOPIC = "332" RPL_TOPIC = "332"
RPL_TOPIC_META = "333" RPL_TOPIC_META = "333"
ERR_NICKNAMEINUSE = "433" ERR_NICKNAMEINUSE = "433"

EXPECTED_REPLIES_TOPIC = [ RPL_NOTOPIC, [ RPL_TOPIC, RPL_TOPIC_META ], RPL_TOPIC ]
end end
end end
end end
2 changes: 1 addition & 1 deletion lib/minibot/daemon.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def initialize(config)
end end


def main_loop def main_loop
while msg = @server.next_message while msg = @server.read
dispatch msg dispatch msg
end end
end end
Expand Down
23 changes: 14 additions & 9 deletions lib/minibot/events.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -44,25 +44,30 @@ def error(num, message)
private private


def dispatch(srv_msg) def dispatch(srv_msg)
if match = /^:(\w+)!.+ PRIVMSG (#\w+) :([^\001].+)/.match(srv_msg) if match = /^:(\w+)!.+ PRIVMSG (\S+) :([^\001].+)/.match(srv_msg)
message *match.values_at(2, 1, 3) target, origin, message = *match.values_at(2, 1, 3)
elsif match = /^:(\w+)!.+ JOIN :(#\w+)/.match(srv_msg) unless target == @nick
message target, origin, message
else
private_message origin, message
end
elsif match = /^:(\w+)!.+ JOIN :(\S+)/.match(srv_msg)
user_joined *match.values_at(2, 1) user_joined *match.values_at(2, 1)
elsif match = /^:(\w+)!.+ PART (#\w+)/.match(srv_msg) elsif match = /^:(\w+)!.+ PART (\S+)/.match(srv_msg)
user_parted *match.values_at(2, 1) user_parted *match.values_at(2, 1)
elsif match = /^:(\w+)!.+ PRIVMSG (#\w+) :\001ACTION (.+)\001/.match(srv_msg) elsif match = /^:(\w+)!.+ PRIVMSG (\S+) :\001ACTION (.+)\001/.match(srv_msg)
user_action *match.values_at(2, 1, 3) user_action *match.values_at(2, 1, 3)
elsif match = /^:(\w+)!.+ PRIVMSG #{@nick} :(.+)/.match(srv_msg) elsif match = /^:(\w+)!.+ PRIVMSG #{@nick} :(.+)/.match(srv_msg)
private_message *match.values_at(1, 2) private_message *match.values_at(1, 2)
elsif match = /^:(\w+)!.+ INVITE \w+ :(#\w+)/.match(srv_msg) elsif match = /^:(\w+)!.+ INVITE \w+ :(\S+)/.match(srv_msg)
invited *match.values_at(2, 1) invited *match.values_at(2, 1)
elsif match = /^PING/.match(srv_msg) elsif match = /^PING/.match(srv_msg)
pinged pinged
elsif match = /^:(\w+)!.+ TOPIC (#\w+) :(.+)/.match(srv_msg) elsif match = /^:(\w+)!.+ TOPIC (\S+) :(.+)/.match(srv_msg)
topic_changed *match.values_at(2, 1, 3) topic_changed *match.values_at(2, 1, 3)
elsif match = /^:(\w+)!.+ KICK (#\w+) #{@nick} :(.+)/.match(srv_msg) elsif match = /^:(\w+)!.+ KICK (\S+) #{@nick} :(.+)/.match(srv_msg)
kicked *match.values_at(2, 1, 3) kicked *match.values_at(2, 1, 3)
elsif match = /^:(\w+)!.+ KICK (#\w+) (\w+) :(.+)/.match(srv_msg) elsif match = /^:(\w+)!.+ KICK (\S+) (\w+) :(.+)/.match(srv_msg)
user_kicked *match.values_at(2, 1, 3, 4) user_kicked *match.values_at(2, 1, 3, 4)
elsif match = /^:\S+ #{RPL_WELCOME} .*?(:.*)?$/.match(srv_msg) elsif match = /^:\S+ #{RPL_WELCOME} .*?(:.*)?$/.match(srv_msg)
ready ready
Expand Down
121 changes: 121 additions & 0 deletions lib/minibot/server.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,121 @@
module MiniBot
class Server
REPLY_RE = /:\S+ (\d{3}) \S+ :?(.+)/

def self.connect(server, port)
s = new(server, port)
s.connect
s
end

def connect
@socket = TCPSocket.new(@server, @port)
end

def read
if @messages.first && message_complete?(@messages.first)
to_message(@messages.shift)
else
read_messages
read
end
end

def match_code(code, message)
if (match = reply?(message)) && match[1] == code
match[1, 2]
else
nil
end
end

def write(msg, *replies)
@socket.print "#{msg}\r\n"

unless replies.empty?
index = 0
matches, messages = catch :halted do
loop do
index, message = next_message(index)
replies.each do |r|
case r
when String
if match = /:\S+ (#{r}) \S+ :?(.+)/.match(message)
throw :halted, [[ match ], [ message ]]
end
when Array
matched = []
messages = []
r.each do |ri|
if match = /:\S+ (#{ri}) \S+ :?(.+)/.match(message)
matched << match
messages << message
index, message = next_message(index)
elsif !matched.empty?
index -= matched.length
message = matched.first
break
end
end

throw :halted, [matched, messages] unless matched.empty?
else
raise ArgumentError, "Unknown reply argument type: #{r.inspect}", caller
end
end
end
end

messages.each { |m| delete_message(m) }
matches.each { |m| yield m[1, 2] }
end
end

def extract(regexp)

end

def disconnect
@socket.close if @socket
end

private

def delete_message(m)
@messages.delete("#{m}\r")
end

def message_complete?(message)
message[-1] == ?\r
end

def to_message(raw)
raw.chomp
end

def next_message(index)
read_messages if !@messages[index] || !message_complete?(@messages[index])

[index + 1, to_message(@messages[index])]
end

def reply?(msg)
REPLY_RE.match msg
end

def initialize(server, port)
@server = server
@port = port
@messages = []
end

def read_messages
buffer = @socket.recvfrom(512).first
messages = buffer.split /\n/

@messages.last << messages.shift if @messages.last && @messages.last[-1] != ?\r

@messages += messages
end
end
end
16 changes: 8 additions & 8 deletions spec/commands_spec.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ def initialize(server = nil)


describe "#join" do describe "#join" do
it "should join a channel" do it "should join a channel" do
bot = CommandBot.new bot = CommandBot.new(mock "server")
bot.should_receive(:write).with("JOIN #testchannel") bot.server.should_receive(:write).with("JOIN #testchannel")
bot.join "#testchannel" bot.join "#testchannel"
end end


it "should join multiple channels" do it "should join multiple channels" do
bot = CommandBot.new bot = CommandBot.new(mock "server")
bot.should_receive(:write).with("JOIN #testchannel") bot.server.should_receive(:write).with("JOIN #testchannel")
bot.should_receive(:write).with("JOIN #anotherchannel") bot.server.should_receive(:write).with("JOIN #anotherchannel")
bot.join "#testchannel", "#anotherchannel" bot.join "#testchannel", "#anotherchannel"
end end
end end
Expand All @@ -30,7 +30,7 @@ def initialize(server = nil)
it "should return the topic data" do it "should return the topic data" do
bot = CommandBot.new(mock "server") bot = CommandBot.new(mock "server")
bot.server.should_receive(:write). bot.server.should_receive(:write).
with("TOPIC #datamapper", *MiniBot::Commands::TOPIC_REPLIES). with("TOPIC #datamapper", *MiniBot::Commands::EXPECTED_REPLIES_TOPIC).
and_yield("332", "Documentation! http://datamapper.rubyforge.org/"). and_yield("332", "Documentation! http://datamapper.rubyforge.org/").
and_yield("333", "ssmoot 1212697142") and_yield("333", "ssmoot 1212697142")
Time.should_receive(:at).with(1212697142).and_return("whoa") Time.should_receive(:at).with(1212697142).and_return("whoa")
Expand All @@ -44,7 +44,7 @@ def initialize(server = nil)
it "should return nil for no topic" do it "should return nil for no topic" do
bot = CommandBot.new(mock "server") bot = CommandBot.new(mock "server")
bot.server.should_receive(:write). bot.server.should_receive(:write).
with("TOPIC #datamapper", *MiniBot::Commands::TOPIC_REPLIES). with("TOPIC #datamapper", *MiniBot::Commands::EXPECTED_REPLIES_TOPIC).
and_yield("331", "There isn't a topic.") and_yield("331", "There isn't a topic.")


topic, author, time = bot.topic "#datamapper" topic, author, time = bot.topic "#datamapper"
Expand All @@ -56,7 +56,7 @@ def initialize(server = nil)
it "should return only the topic for servers that don't send the metadata" do it "should return only the topic for servers that don't send the metadata" do
bot = CommandBot.new(mock "server") bot = CommandBot.new(mock "server")
bot.server.should_receive(:write). bot.server.should_receive(:write).
with("TOPIC #datamapper", *MiniBot::Commands::TOPIC_REPLIES). with("TOPIC #datamapper", *MiniBot::Commands::EXPECTED_REPLIES_TOPIC).
and_yield("332", "TOPIC!"). and_yield("332", "TOPIC!").
and_yield("400", "Mary had a little lamb") and_yield("400", "Mary had a little lamb")


Expand Down
Loading

0 comments on commit ed31a20

Please sign in to comment.