net-yail 1.2.3 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,5 +1,7 @@
1
1
  Rubyforge page: http://rubyforge.org/projects/ruby-irc-yail
2
2
 
3
+ *Want to jump straight to the examples instead of reading this lame "about the project" doc? Just click on Net::YAIL !*
4
+
3
5
  Net::YAIL is a library built for dealing with IRC communications in Ruby.
4
6
  This is a project I've been building for about three years, based
5
7
  originally on the very messy initial release of IRCSocket (back when I first
data/lib/net/yail.rb CHANGED
@@ -9,6 +9,9 @@ require 'net/yail/magic_events'
9
9
  require 'net/yail/default_events'
10
10
  require 'net/yail/output_api'
11
11
 
12
+ # Finally, a real class to include!
13
+ require 'net/yail/message_parser.rb'
14
+
12
15
  # If a thread crashes, I want the app to die. My threads are persistent, not
13
16
  # temporary.
14
17
  Thread.abort_on_exception = true
@@ -36,7 +39,7 @@ module Net
36
39
  # ==Incoming Events
37
40
  #
38
41
  # Current list of incoming events and the parameters sent to the handler:
39
- # * :incoming_any(event) - "global" handler that catches all events and may
42
+ # * :incoming_any(raw) - "global" handler that catches all events and may
40
43
  # modify their data as necessary before the real handler is hit. This
41
44
  # only be used in cases where it's necessary to grab a lot of events that
42
45
  # also need to be processed elsewhere, such as doing input filtering for
@@ -227,6 +230,8 @@ class YAIL
227
230
  include Net::IRCEvents::Defaults
228
231
  include Net::IRCOutputAPI
229
232
 
233
+ VERSION = '1.3.0'
234
+
230
235
  attr_reader(
231
236
  :me, # Nickname on the IRC server
232
237
  :registered, # If true, we've been welcomed
@@ -458,42 +463,71 @@ class YAIL
458
463
 
459
464
  # Gets some input, sends stuff off to a handler. Yay.
460
465
  def process_input(line)
461
- # Allow global handler to break the chain, filter the line, whatever
462
- return if handle :incoming_any, line
463
-
464
- case line
465
- # Numerics must be handled first! Otherwise a numeric message that's
466
- # formatted just right will accidentally match another handler.
467
- when /^:((.+?)(?:!.+?)?) (\d{3})\s+(\S+?) (.+?)$/
468
- handle_numeric($3.to_i, $1, $2, $4, $5)
469
- when /^PING :?(.+?)$/
470
- handle :incoming_ping, $1
471
- when /^:((.+?)(?:!.+?)?) INVITE \S+ :(\S+)/
472
- handle :incoming_invite, $1, $2, $3
473
- when /^:((.+?)(?:!.+?)?) PRIVMSG (\S+) :?\001ACTION (.+?)\001$/
474
- handle :incoming_act, $1, $2, $3, $4
475
- when /^:((.+?)(?:!.+?)?) PRIVMSG (\S+?) :?\001(.+?)\001$/
476
- handle :incoming_ctcp, $1, $2, $3, $4
477
- when /^:((.+?)(?:!.+?)?) PRIVMSG (\S+?) :?(.+?)$/
478
- handle :incoming_msg, $1, $2, $3, $4
479
- when /^:((.+?)(?:!.+?)?) NOTICE (\S+?) :?\001(.+?)\001$/
480
- handle :incoming_ctcpreply, $1, $2, $3, $4
481
- when /^:((.+?)(?:!.+?)?) NOTICE (\S+?) :?(.+?)$/
482
- handle :incoming_notice, $1, $2, $3, $4
483
- when /^:((.+?)(?:!.+?)?) MODE (\S+?) :?(\S+?)(?: (.+?))?$/
484
- handle :incoming_mode, $1, $2, $3, $4, $5
485
- when /^:((.+?)(?:!.+?)?) JOIN :?(\S+?)$/
486
- handle :incoming_join, $1, $2, $3
487
- when /^:((.+?)(?:!.+?)?) PART (\S+?)(?: :?(\S+?)?)?$/
488
- handle :incoming_part, $1, $2, $3, $4
489
- when /^:((.+?)(?:!.+?)?) KICK (\S+?) (\S+?) :?(.+?)$/
490
- handle :incoming_kick, $1, $2, $3, $4, $5
491
- when /^:((.+?)(?:!.+?)?) QUIT :?(.+?)$/
492
- handle :incoming_quit, $1, $2, $3
493
- when /^:((.+?)(?:!.+?)?) NICK :?(\S+?)$/
494
- handle :incoming_nick, $1, $2, $3
466
+ # Allow global handler to break the chain, filter the line, whatever. For
467
+ # this release, it's a hack. 2.0 will be better.
468
+ for handler in @handlers[:incoming_any]
469
+ result = handler.call(line)
470
+ return if result == true
471
+ end
472
+
473
+ # Use the exciting new parser
474
+ msg = Net::YAIL::MessageParser.new(line)
475
+
476
+ # In the legacy system, there are never situations where we want the params
477
+ # separated into array elements. So we convert them here, since so many
478
+ # handlers require a param or two followed by "everything else".
479
+ params = msg.params.dup
480
+ param1 = params.shift
481
+ other1 = params.join(' ') || ''
482
+ param2 = params.shift
483
+ other2 = params.join(' ') || ''
484
+
485
+ case msg.command
486
+ # Ping is important to handle quickly, so it comes first.
487
+ when 'PING'
488
+ handle(:incoming_ping, msg.params.first)
489
+ when /^\d{3}$/
490
+ handle_numeric(msg.command.to_i, msg.prefix, msg.nick, param1, other1)
491
+ when 'INVITE'
492
+ handle(:incoming_invite, msg.prefix, msg.nick, msg.params.last)
493
+
494
+ # This can encompass three possible messages, so further refining happens here - the last param
495
+ # is always the message itself, so we look for patterns there.
496
+ when 'PRIVMSG'
497
+ case msg.params.last
498
+ when /^\001ACTION (.+?)\001$/
499
+ handle(:incoming_act, msg.prefix, msg.nick, msg.params.first, $1)
500
+ when /^\001(.+?)\001$/
501
+ handle(:incoming_ctcp, msg.prefix, msg.nick, msg.params.first, $1)
502
+ else
503
+ handle(:incoming_msg, msg.prefix, msg.nick, param1, other1)
504
+ end
505
+
506
+ # This can encompass two possible messages, again based on final param
507
+ when 'NOTICE'
508
+ case msg.params.last
509
+ when /^\001(.+?)\001$/
510
+ handle(:incoming_ctcpreply, msg.prefix, msg.nick, msg.params.first, $1)
511
+ else
512
+ handle(:incoming_notice, msg.prefix, msg.nick, param1, other1)
513
+ end
514
+
515
+ when 'MODE'
516
+ handle(:incoming_mode, msg.prefix, msg.nick, param1, param2, other2)
517
+ when 'JOIN'
518
+ handle(:incoming_join, msg.prefix, msg.nick, param1)
519
+ when 'PART'
520
+ handle(:incoming_part, msg.prefix, msg.nick, param1, other1)
521
+ when 'KICK'
522
+ handle(:incoming_kick, msg.prefix, msg.nick, param1, param2, other2)
523
+ when 'QUIT'
524
+ handle(:incoming_quit, msg.prefix, msg.nick, param1)
525
+ when 'NICK'
526
+ handle(:incoming_nick, msg.prefix, msg.nick, param1)
527
+
528
+ # Unknown line!
495
529
  else
496
- handle :incoming_miscellany, line
530
+ handle(:incoming_miscellany, line)
497
531
  end
498
532
  end
499
533
 
@@ -0,0 +1,66 @@
1
+ module Net
2
+ class YAIL
3
+
4
+ # This is my lame attempt to convert the BNF-style grammar from RFC 1459 into
5
+ # useable ruby regexes. The hope here is that one can effectively match an
6
+ # incoming message with high accuracy. Usage:
7
+ #
8
+ # line = ':Nerdmaster!jeremy@nerdbucket.com PRIVMSG Nerdminion :Do my bidding!!'
9
+ # message = Net::YAIL::MessageParser.new(line)
10
+ # # hash now has all kinds of useful pieces of the incoming message:
11
+ # puts line.nick # "Nerdmaster"
12
+ # puts line.user # "jeremy"
13
+ # puts line.host # "nerdbucket.com"
14
+ # puts line.prefix # "Nerdmaster!jeremy@nerdbucket.com"
15
+ # puts line.command # "PRIVMSG"
16
+ # puts line.params # ["Nerdminion", "Do my bidding!!"]
17
+ class MessageParser
18
+ attr_reader :nick, :user, :host, :prefix, :command, :params, :servername
19
+
20
+ # Note that all regexes are non-greedy. I'm scared of greedy regexes, sirs.
21
+ USER = /\S+?/
22
+ NICK = /\w[\w\d\\`'^{}\]\[-]+?/
23
+ HOST = /\S+?/
24
+ SERVERNAME = /\S+?/
25
+
26
+ # This is automatically grouped for ease of use in the parsing. Group 1 is
27
+ # the full prefix; 2, 3, and 4 are nick/user/host; 1 is also servername if
28
+ # there was no match to populate 2, 3, and 4.
29
+ PREFIX = /((#{NICK})!(#{USER})@(#{HOST})|#{SERVERNAME})/
30
+ COMMAND = /(\w+|\d{3})/
31
+ TRAILING = /\:\S*?/
32
+ MIDDLE = /(?: +([^ :]\S*))/
33
+
34
+ MESSAGE = /^(?::#{PREFIX} +)?#{COMMAND}(.*)$/
35
+
36
+ def initialize(line)
37
+ @params = []
38
+
39
+ if line =~ MESSAGE
40
+ matches = Regexp.last_match
41
+
42
+ @prefix = matches[1]
43
+ if (matches[2])
44
+ @nick = matches[2]
45
+ @user = matches[3]
46
+ @host = matches[4]
47
+ else
48
+ @servername = matches[1]
49
+ end
50
+
51
+ @command = matches[5]
52
+
53
+ # Args are a bit tricky. First off, we know there must be a single
54
+ # space before the arglist, so we need to strip that. Then we have to
55
+ # separate the trailing arg as it can contain nearly any character. And
56
+ # finally, we split the "middle" args on space.
57
+ arglist = matches[6].sub(/^ +/, '')
58
+ (middle_args, trailing_arg) = arglist.split(/ +:/, 2)
59
+ @params.push(middle_args.split(/ +/), trailing_arg)
60
+ @params.flatten!
61
+ end
62
+ end
63
+ end
64
+
65
+ end
66
+ end
data/tests/net_yail.rb ADDED
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib")
2
+ require 'net/yail'
3
+ require 'net/yail/message_parser'
4
+ puts "VERSION: #{Net::YAIL::VERSION}"
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/net_yail'
3
+ require 'test/unit'
4
+
5
+ class MessageParserText < Test::Unit::TestCase
6
+ # Very simple parsing of easy strings
7
+ def test_parse_basic
8
+ # Basic test of privmsg-type command
9
+ msg = Net::YAIL::MessageParser.new(':Nerdmaster!jeremy@nerdbucket.com PRIVMSG Nerdminion :Do my bidding!!')
10
+ assert_equal 'Nerdmaster', msg.nick
11
+ assert_equal 'jeremy', msg.user
12
+ assert_equal 'nerdbucket.com', msg.host
13
+ assert_equal 'Nerdmaster!jeremy@nerdbucket.com', msg.prefix
14
+ assert_equal 'PRIVMSG', msg.command
15
+ assert_equal 'Nerdminion', msg.params[0]
16
+ assert_equal 'Do my bidding!!', msg.params[1]
17
+
18
+ # Server command of some type
19
+ msg = Net::YAIL::MessageParser.new(':nerdbucket.com SERVERCOMMAND arg1 arg2 :final :trailing :arg, --fd9823')
20
+ assert_equal 'nerdbucket.com', msg.servername
21
+ assert_nil msg.user
22
+ assert_nil msg.nick
23
+ assert_nil msg.host
24
+ assert_equal 'nerdbucket.com', msg.prefix
25
+ assert_equal 'arg1', msg.params[0]
26
+ assert_equal 'arg2', msg.params[1]
27
+ assert_equal 'final :trailing :arg, --fd9823', msg.params[2]
28
+
29
+ # WTF? Well, IRC spec says it's valid
30
+ msg = Net::YAIL::MessageParser.new('MAGICFUNKYFRESHCMD arg1 arg2')
31
+ assert_nil msg.servername
32
+ assert_equal 'MAGICFUNKYFRESHCMD', msg.command
33
+ assert_equal 'arg1', msg.params[0]
34
+ assert_equal 'arg2', msg.params[1]
35
+
36
+ # Action
37
+ msg = Net::YAIL::MessageParser.new(':Nerdmaster!jeremy@nerdbucket.com PRIVMSG #bottest :\001ACTION gives Towelie a joint\001')
38
+ assert_equal 'Nerdmaster', msg.nick
39
+ assert_equal 'PRIVMSG', msg.command
40
+ assert_equal '#bottest', msg.params.first
41
+ assert_equal '\001ACTION gives Towelie a joint\001', msg.params.last
42
+
43
+ # Bot sets mode
44
+ msg = Net::YAIL::MessageParser.new(':Towelie!~x2e521146@towelie.foo.bar MODE Towelie :+i')
45
+ assert_equal 'Towelie', msg.nick
46
+ assert_equal 'towelie.foo.bar', msg.host
47
+ assert_equal 'MODE', msg.command
48
+ assert_equal 'Towelie', msg.params.first
49
+ assert_equal '+i', msg.params.last
50
+
51
+ # Numeric message with a : before final param
52
+ msg = Net::YAIL::MessageParser.new(':someserver.co.uk.fn.bb 366 Towelie #bottest :End of /NAMES list.')
53
+ assert_nil msg.nick
54
+ assert_nil msg.host
55
+ assert_equal '366', msg.command
56
+ assert_equal 'Towelie', msg.params.shift
57
+ assert_equal '#bottest', msg.params.shift
58
+ assert_equal 'End of /NAMES list.', msg.params.shift
59
+ end
60
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-yail
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Echols
@@ -9,7 +9,7 @@ autorequire: net/yail
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-17 00:00:00 -07:00
12
+ date: 2009-04-28 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -31,6 +31,7 @@ files:
31
31
  - lib/net/yail/magic_events.rb
32
32
  - lib/net/yail/eventmap.yml
33
33
  - lib/net/yail/IRCBot.rb
34
+ - lib/net/yail/message_parser.rb
34
35
  - README
35
36
  has_rdoc: true
36
37
  homepage: http://ruby-irc-yail.nerdbucket.com/
@@ -59,5 +60,6 @@ rubygems_version: 1.2.0
59
60
  signing_key:
60
61
  specification_version: 2
61
62
  summary: "Yet Another IRC Library: wrapper for IRC communications in Ruby."
62
- test_files: []
63
-
63
+ test_files:
64
+ - tests/net_yail.rb
65
+ - tests/tc_message_parser.rb