net-yail 1.4.6 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,113 +1,10 @@
1
1
  module Net
2
2
  module IRCEvents
3
3
 
4
- # This module contains all the default events handling - mainly for
5
- # reporting things or simple logic. Users can put in their own event handlers
6
- # that return true and ignore these, so nothing in here should be truly
7
- # essential to a healthy IRC app.
4
+ # This module contains all the default events handling that hasn't yet been cleaned up for 2.0
8
5
  module Defaults
9
6
  private
10
7
 
11
- # Sets up all the default handlers for events - just reporting things others
12
- # don't handle in all but a few cases
13
- def setup_default_handlers
14
- # Incoming events
15
- prepend_handler :incoming_msg, self.method(:r_msg)
16
- prepend_handler :incoming_act, self.method(:r_act)
17
- prepend_handler :incoming_notice, self.method(:r_notice)
18
- prepend_handler :incoming_ctcp, self.method(:r_ctcp)
19
- prepend_handler :incoming_ctcpreply, self.method(:r_ctcpreply)
20
- prepend_handler :incoming_mode, self.method(:r_mode)
21
- prepend_handler :incoming_join, self.method(:r_join)
22
- prepend_handler :incoming_part, self.method(:r_part)
23
- prepend_handler :incoming_kick, self.method(:r_kick)
24
- prepend_handler :incoming_quit, self.method(:r_quit)
25
- prepend_handler :incoming_nick, self.method(:r_nick)
26
- prepend_handler :incoming_miscellany, self.method(:r_miscellany)
27
-
28
- # Incoming numeric events here
29
- prepend_handler :incoming_welcome, self.method(:r_welcome)
30
- prepend_handler :incoming_bannedfromchan, self.method(:r_bannedfromchan)
31
- prepend_handler :incoming_badchannelkey, self.method(:r_badchannelkey)
32
- prepend_handler :incoming_nicknameinuse, self.method(:_nicknameinuse)
33
- prepend_handler :incoming_channelurl, self.method(:r_channelurl)
34
- prepend_handler :incoming_topic, self.method(:r_topic)
35
- prepend_handler :incoming_topicinfo, self.method(:r_topicinfo)
36
- prepend_handler :incoming_namreply, self.method(:_namreply)
37
- prepend_handler :incoming_endofnames, self.method(:r_endofnames)
38
- prepend_handler :incoming_motd, self.method(:r_motd)
39
- prepend_handler :incoming_motdstart, self.method(:r_motdstart)
40
- prepend_handler :incoming_endofmotd, self.method(:r_endofmotd)
41
- prepend_handler :incoming_invite, self.method(:r_invite)
42
-
43
- # Outgoing events
44
- prepend_handler :outgoing_begin_connection, self.method(:out_begin_connection)
45
- end
46
-
47
- def r_msg(fullactor, actor, target, text)
48
- report "{#{target}} <#{actor}> #{text}"
49
- end
50
-
51
- def r_act(fullactor, actor, target, text)
52
- report "{#{target}} * #{actor} #{text}"
53
- end
54
-
55
- def r_notice(fullactor, actor, target, text)
56
- report "{#{target}} -#{actor}- #{text}"
57
- end
58
-
59
- def r_ctcp(fullactor, actor, target, text)
60
- report "{#{target}} [#{actor} #{text}]"
61
- end
62
-
63
- def r_ctcpreply(fullactor, actor, target, text)
64
- report "{#{target}} [Reply: #{actor} #{text}]"
65
- end
66
-
67
- def r_mode(fullactor, actor, target, modes, objects)
68
- report "{#{target}} #{actor} sets mode #{modes} #{objects}"
69
- end
70
-
71
- def r_join(fullactor, actor, target)
72
- report "{#{target}} #{actor} joins"
73
- end
74
-
75
- def r_part(fullactor, actor, target, text)
76
- report "{#{target}} #{actor} parts (#{text})"
77
- end
78
-
79
- def r_kick(fullactor, actor, target, object, text)
80
- report "{#{target}} #{actor} kicked #{object} (#{text})"
81
- end
82
-
83
- def r_quit(fullactor, actor, text)
84
- report "#{actor} quit (#{text})"
85
- end
86
-
87
- # Reports nick change unless nickname is us - we check nickname here since
88
- # the magic method changes @me to the new nickname.
89
- def r_nick(fullactor, actor, nickname)
90
- report "#{actor} changed nick to #{nickname}" unless nickname == @me
91
- end
92
-
93
- def r_bannedfromchan(text, args)
94
- text =~ /^(\S*) :Cannot join channel/
95
- report "Banned from channel #{$1}"
96
- end
97
-
98
- def r_badchannelkey(text, args)
99
- text =~ /^(\S*) :Cannot join channel/
100
- report "Bad channel key (password) for #{$1}"
101
- end
102
-
103
- def r_welcome(*args)
104
- report "*** Logged in as #{@me}. ***"
105
- end
106
-
107
- def r_miscellany(line)
108
- report "serv: #{line}"
109
- end
110
-
111
8
  # Nickname change failed: already in use. This needs a rewrite to at
112
9
  # least hit a "failed too many times" handler of some kind - for a bot,
113
10
  # quitting may be fine, but for something else, we may want to prompt a
@@ -117,8 +14,8 @@ module Defaults
117
14
  #
118
15
  # TODO: This should really not even be here. Client should have full control over whether or not
119
16
  # they want this. Base IRC bot class should have this, but not the core YAIL lib.
120
- def _nicknameinuse(text, args)
121
- text =~ /^(\S+)/
17
+ def _nicknameinuse(event)
18
+ event.message =~ /^(\S+)/
122
19
  report "Nickname #{$1} is already in use."
123
20
 
124
21
  if (!@registered)
@@ -137,29 +34,11 @@ module Defaults
137
34
  end
138
35
  end
139
36
 
140
- # Channel URL
141
- def r_channelurl(text, args)
142
- text =~ /^(\S+) :?(.+)$/
143
- report "{#{$1}} URL is #{$2}"
144
- end
145
-
146
- # Channel topic
147
- def r_topic(text, args)
148
- text =~ /^(\S+) :?(.+)$/
149
- report "{#{$1}} Topic is: #{$2}"
150
- end
151
-
152
- # Channel topic setter
153
- def r_topicinfo(text, args)
154
- text =~ /^(\S+) (\S+) (\d+)$/
155
- report "{#{$1}} Topic set by #{$2} on #{Time.at($3.to_i).asctime}"
156
- end
157
-
158
37
  # Names line
159
38
  #
160
39
  # TODO: Either store this data silently or ditch this code - this verbosity doesn't belong in a core lib
161
- def _namreply(text, args)
162
- text =~ /^(@|\*|=) (\S+) :?(.+)$/
40
+ def _namreply(event)
41
+ event.message =~ /^(@|\*|=) (\S+) :?(.+)$/
163
42
  channeltype = {'@' => 'Secret', '*' => 'Private', '=' => 'Normal'}[$1]
164
43
  report "{#{$2}} #{channeltype} channel nickname list: #{$3}"
165
44
  @nicklist = $3.split(' ')
@@ -167,43 +46,6 @@ module Defaults
167
46
  report "First nick: #{@nicklist[0]}"
168
47
  end
169
48
 
170
- # End of names
171
- def r_endofnames(text, args)
172
- text =~ /^(\S+)/
173
- report "{#{$1}} Nickname list complete"
174
- end
175
-
176
- # MOTD line
177
- def r_motd(text, args)
178
- text =~ /^:?(.+)$/
179
- report "*MOTD* #{$1}"
180
- end
181
-
182
- # Beginning of MOTD
183
- def r_motdstart(text, args)
184
- text =~ /^:?(.+)$/
185
- report "*MOTD* #{$1}"
186
- end
187
-
188
- # End of MOTD
189
- def r_endofmotd(text, args)
190
- report "*MOTD* End of MOTD"
191
- end
192
-
193
- # We dun connected to a server! Just sends password (if one is set) and
194
- # user/nick. This isn't quite "essential" to a working IRC app, but this data
195
- # *must* be sent at some point, so be careful before skipping this handler.
196
- def out_begin_connection(username, address, realname)
197
- pass(@password) if @password
198
- user(username, '0.0.0.0', address, realname)
199
- nick(@nicknames[0])
200
- end
201
-
202
- # Incoming invitation
203
- def r_invite(fullactor, actor, target)
204
- report "[#{actor}] INVITE to #{target}"
205
- end
206
-
207
49
  end
208
50
 
209
51
  end
@@ -6,16 +6,35 @@ class YAIL
6
6
  # Base event class for stuff shared by any type of event. Note that :type and :handled
7
7
  # apply to *all* events, so they're explicitly defined here.
8
8
  class BaseEvent
9
- attr_reader :type, :handled
10
-
11
- # Creates an event object and sets up some sane defaults for common elements
12
- def initialize()
9
+ # Creates an event object and sets up some sane defaults for common elements. Any elements
10
+ # in the data hash are converted to "magic" methods.
11
+ def initialize(data = {})
12
+ # Don't modify incoming data!
13
+ @data = data.dup
13
14
  @handled = false
14
- @type = nil
15
+ @type = @data.delete(:type)
16
+
17
+ # All events have the capacity for a parent
18
+ @data[:parent] ||= nil
19
+
20
+ # Give useful accessors in a hacky but fun way! I can't decide if I prefer the pain of
21
+ # using method_missing or the pain of not knowing how to do this without a string eval....
22
+ for key in @data.keys
23
+ key = key.to_s
24
+ self.instance_eval("def #{key}; return @data[:#{key}]; end")
25
+ end
26
+
27
+ raise "BaseEvent not usable - please subclass" if BaseEvent == self.class
28
+ end
29
+
30
+ # Helps us debug
31
+ def to_s
32
+ return super().gsub(self.class.name, "%s [%s]" % [self.class.name, @type.to_s])
15
33
  end
16
34
 
17
- # Sets this event as having been handled (i.e., no further handling should occur)
18
- def handle; @handled = true; end
35
+ # Slightly unintuitive name to avoid accidental use - we don't want it to be the norm to stop
36
+ # the event handling chain anymore! Filters + callback should make that a rarity.
37
+ def handled!; @handled = true; end
19
38
 
20
39
  # Cheesy shortcut to @handled in "boolean" form
21
40
  def handled?; return @handled; end
@@ -30,6 +49,11 @@ class YAIL
30
49
  end
31
50
  end
32
51
 
52
+ # Custom event is just a base event that doesn't crash when accessing type :)
53
+ class CustomEvent < BaseEvent
54
+ def type; return @type; end
55
+ end
56
+
33
57
  # This is the incoming event class. For all situations where the server
34
58
  # sent us some kind of event, this class handles all the data.
35
59
  #
@@ -37,9 +61,10 @@ class YAIL
37
61
  # the IRC server. Other possible pieces of data are as follows:
38
62
  # * fullname: Rarely needed, full text of origin of an action
39
63
  # * nick: Nickname of originator of an event
64
+ # * from: Nickname *or* server name, should be on every event
40
65
  # * channel: Where applicable, the name of the channel in which the event
41
66
  # happened.
42
- # * text: Actual message/emote/notice/etc
67
+ # * message: Actual message/emote/notice/etc
43
68
  # * target: User targeted for various commands - PRIVMSG/NOTICE recipient, KICK victim, etc
44
69
  # * pm?: Set to true if the event is a "private" event (not sent to the
45
70
  # channel). Useful primarily for message types of events (PRIVMSG).
@@ -59,27 +84,14 @@ class YAIL
59
84
  attr_reader :raw, :msg
60
85
  private_class_method :new
61
86
 
62
- # Sets up data and accessors
87
+ # Incoming events always have :raw and :msg in the data hash
63
88
  def initialize(data = {})
64
- super()
65
-
66
89
  # Don't modify incoming element!
67
90
  @data = data.dup
68
91
  @raw = @data.delete(:raw)
69
92
  @msg = @data.delete(:msg)
70
- @type = @data.delete(:type)
71
-
72
- # Give useful accessors in a hacky but fun way! I can't decide if I prefer the pain of
73
- # using method_missing or the pain of not knowing how to do this without a string eval....
74
- for key in @data.keys
75
- key = key.to_s
76
- self.instance_eval("def #{key}; return @data[:#{key}]; end")
77
- end
78
- end
79
93
 
80
- # Helps us debug
81
- def to_s
82
- return super().gsub('IncomingEvent', "IncomingEvent[#{@type.to_s}]")
94
+ super(data)
83
95
  end
84
96
 
85
97
  # Incoming events in our system are always :incoming_xxx
@@ -92,7 +104,15 @@ class YAIL
92
104
  # Parse with MessageParser to get raw IRC info
93
105
  raw = line.dup
94
106
  msg = Net::YAIL::MessageParser.new(line)
95
- data = { :raw => raw, :msg => msg, :parent => nil }
107
+
108
+ # All incoming events need .raw and .msg in addition to any base event attributes.
109
+ #
110
+ # "from" is a tricky case as it isn't used on all messages - but because it's something of
111
+ # a standard we rely on for so many messages, it has a default so that at the least one can
112
+ # rely on not getting a crash for some of the edge cases (like "NOTICE :ERROR from foo.bar.com"
113
+ # or a server-less "NOTICE AUTH :xxxx"). Maybe more elements should have defaults... not
114
+ # real sure yet.
115
+ data = { :raw => raw, :msg => msg, :from => nil }
96
116
 
97
117
  # Not all messages from the server identify themselves as such, so we just assume it's from
98
118
  # the server unless we explicitly see a nick
@@ -102,47 +122,42 @@ class YAIL
102
122
  if msg.servername
103
123
  data[:from] = data[:servername] = msg.servername
104
124
  elsif msg.prefix && msg.nick
105
- data[:from] = data[:fullname] = msg.prefix
106
- data[:nick] = msg.nick
125
+ data[:fullname] = msg.prefix
126
+ data[:from] = data[:nick] = msg.nick
107
127
  data[:server?] = false
108
128
  end
109
129
 
110
- # Since "from" is something of a standard we rely on for all kinds of messages, we default
111
- # to an empty string so that at the least one can rely on not getting a crash for some of
112
- # the edge cases (like "NOTICE :ERROR from foo.bar.com")
113
- data[:from] ||= ""
114
-
115
130
  case msg.command
116
131
  when 'ERROR'
117
132
  data[:type] = :error
118
- data[:text] = msg.params.last
133
+ data[:message] = msg.params.last
119
134
  event = new(data)
120
135
 
121
136
  when 'PING'
122
137
  data[:type] = :ping
123
- data[:text] = msg.params.last
138
+ data[:message] = msg.params.last
124
139
  event = new(data)
125
140
 
126
141
  when 'TOPIC'
127
142
  data[:type] = :topic_change
128
143
  data[:channel] = msg.params.first
129
- data[:text] = msg.params.last
144
+ data[:message] = msg.params.last
130
145
  event = new(data)
131
146
 
132
147
  when /^\d{3}$/
133
148
  # Get base event for the "numeric" type - so many of these exist, and so few are likely
134
- # to be handled directly. Sadly, some hackery has to happen here to make "text" backward-
149
+ # to be handled directly. Sadly, some hackery has to happen here to make "message" backward-
135
150
  # compatible since old YAIL auto-joined all parameters into one string.
136
151
  data[:type] = :numeric
137
152
  params = msg.params.dup
138
153
  data[:target] = params.shift
139
154
  data[:parameters] = params
140
- data[:text] = params.join(' ')
155
+ data[:message] = params.join(' ')
141
156
  data[:numeric] = msg.command.to_i
142
157
  event = new(data)
143
158
 
144
159
  # Create child event for the specific numeric
145
- data[:type] = msg.command.to_sym
160
+ data[:type] = :"numeric_#{msg.command.to_i}"
146
161
  data[:parent] = event
147
162
  event = new(data)
148
163
 
@@ -174,24 +189,24 @@ class YAIL
174
189
  when 'PART'
175
190
  data[:type] = :part
176
191
  data[:channel] = msg.params.first
177
- data[:text] = msg.params.last
192
+ data[:message] = msg.params.last
178
193
  event = new(data)
179
194
 
180
195
  when 'KICK'
181
196
  data[:type] = :kick
182
197
  data[:channel] = msg.params[0]
183
198
  data[:target] = msg.params[1]
184
- data[:text] = msg.params[2]
199
+ data[:message] = msg.params[2]
185
200
  event = new(data)
186
201
 
187
202
  when 'QUIT'
188
203
  data[:type] = :quit
189
- data[:text] = msg.params.first
204
+ data[:message] = msg.params.first
190
205
  event = new(data)
191
206
 
192
207
  when 'NICK'
193
208
  data[:type] = :nick
194
- data[:text] = msg.params.first
209
+ data[:message] = msg.params.first
195
210
  event = new(data)
196
211
 
197
212
  # Unknown line! If this library is complete, we should *never* see this situation occur,
@@ -211,7 +226,7 @@ class YAIL
211
226
  def self.mode_events(msg, data)
212
227
  data[:type] = :mode
213
228
  data[:channel] = msg.params.shift
214
- data[:text] = msg.params.shift
229
+ data[:message] = msg.params.shift
215
230
  data[:targets] = msg.params
216
231
  event = new(data)
217
232
  end
@@ -240,22 +255,22 @@ class YAIL
240
255
 
241
256
  # Get base event
242
257
  data[:type] = :msg
243
- data[:text] = msg.params.last
258
+ data[:message] = msg.params.last
244
259
  event = new(data)
245
260
 
246
261
  # Is this CTCP?
247
- if event.text =~ /^\001(.+?)\001$/
262
+ if event.message =~ /^\001(.+?)\001$/
248
263
  data[:type] = :ctcp
249
- data[:text] = $1
264
+ data[:message] = $1
250
265
  data[:parent] = event
251
266
 
252
267
  event = new(data)
253
268
  end
254
269
 
255
270
  # CTCP action?
256
- if :ctcp == data[:type] && event.text =~ /^ACTION (.+)$/
271
+ if :ctcp == data[:type] && event.message =~ /^ACTION (.+)$/
257
272
  data[:type] = :act
258
- data[:text] = $1
273
+ data[:message] = $1
259
274
  data[:parent] = event
260
275
 
261
276
  event = new(data)
@@ -271,12 +286,12 @@ class YAIL
271
286
 
272
287
  # Get base event
273
288
  data[:type] = :notice
274
- data[:text] = msg.params.last
289
+ data[:message] = msg.params.last
275
290
  event = new(data)
276
291
 
277
- if event.text =~ /^\001(.+?)\001$/
292
+ if event.message =~ /^\001(.+?)\001$/
278
293
  data[:type] = :ctcp_reply
279
- data[:text] = $1
294
+ data[:message] = $1
280
295
  data[:parent] = event
281
296
 
282
297
  event = new(data)
@@ -1,11 +1,12 @@
1
1
  require 'rubygems'
2
2
  require 'net/yail'
3
+ require 'net/yail/report_events'
3
4
 
4
5
  # My abstraction from adapter to a real bot.
5
6
  class IRCBot
6
- attr_reader :irc
7
+ include Net::IRCEvents::Reports
7
8
 
8
- public
9
+ attr_reader :irc
9
10
 
10
11
  # Creates a new bot. Options are anything you can pass to the Net::YAIL constructor:
11
12
  # * <tt>:irc_network</tt>: Name/IP of the IRC server - backward-compatibility hack, and is
@@ -60,9 +61,10 @@ class IRCBot
60
61
  # add_custom_handlers to allow auto-creation in case of a restart.
61
62
  def connect_socket
62
63
  @irc = Net::YAIL.new(@options)
64
+ setup_reporting(@irc)
63
65
 
64
66
  # Simple hook for welcome to allow auto-joining of the channel
65
- @irc.prepend_handler :incoming_welcome, self.method(:welcome)
67
+ @irc.on_welcome self.method(:welcome)
66
68
 
67
69
  add_custom_handlers
68
70
  end
@@ -95,7 +97,7 @@ class IRCBot
95
97
  while true
96
98
  until @irc.dead_socket
97
99
  sleep 15
98
- @irc.handle(:irc_loop)
100
+ @irc.dispatch Net::YAIL::CustomEvent.new(:type => :irc_loop)
99
101
  Thread.pass
100
102
  end
101
103
 
@@ -109,10 +111,8 @@ class IRCBot
109
111
 
110
112
  private
111
113
  # Basic handler for joining our channels upon successful registration
112
- def welcome(text, args)
114
+ def welcome(event)
113
115
  @options[:channels].each {|channel| @irc.join(channel) }
114
- # Let the default welcome stuff still happen
115
- return false
116
116
  end
117
117
 
118
118
  ################