net-yail 1.4.6 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/logger/logger_bot.rb +20 -21
- data/examples/loudbot/bot_runner.rb +114 -0
- data/examples/loudbot/loudbot.rb +87 -0
- data/examples/loudbot/shuffle.rb +17 -0
- data/examples/simple/dumbbot.rb +9 -4
- data/lib/net/yail.rb +437 -383
- data/lib/net/yail/default_events.rb +5 -163
- data/lib/net/yail/event.rb +65 -50
- data/lib/net/yail/irc_bot.rb +7 -7
- data/lib/net/yail/legacy_events.rb +214 -0
- data/lib/net/yail/magic_events.rb +31 -27
- data/lib/net/yail/output_api.rb +82 -262
- data/lib/net/yail/report_events.rb +173 -0
- data/lib/net/yail/yail-version.rb +1 -1
- data/tests/mock_irc.rb +13 -3
- data/tests/tc_event.rb +28 -28
- data/tests/tc_yail.rb +93 -32
- metadata +21 -11
- data/YAIL-RDOC +0 -51
@@ -0,0 +1,214 @@
|
|
1
|
+
module Net
|
2
|
+
module IRCEvents
|
3
|
+
|
4
|
+
# All code here is going to be removed completely at some point, and only exists here to serve the 1.x branch
|
5
|
+
# (and remind me how awful the old system really was)
|
6
|
+
module LegacyEvents
|
7
|
+
|
8
|
+
# DEPRECATED
|
9
|
+
#
|
10
|
+
# Event handler hook. Kinda hacky. Calls your event(s) before the default
|
11
|
+
# event. Default stuff will happen if your handler doesn't return true.
|
12
|
+
def prepend_handler(event, *procs, &block)
|
13
|
+
raise "Cannot change handlers while threads are listening!" if @ioloop_thread
|
14
|
+
|
15
|
+
@log.warn "[DEPRECATED] - Net::YAIL#prepend_handler is deprecated as of 1.5.0 - please see documentation on the new " +
|
16
|
+
"event handling model methods - http://ruby-irc-yail.nerdbucket.com/"
|
17
|
+
|
18
|
+
# Allow blocks as well as procs
|
19
|
+
if block_given?
|
20
|
+
procs.push(block)
|
21
|
+
end
|
22
|
+
|
23
|
+
# See if this is a word for a numeric - only applies to incoming events
|
24
|
+
if (event.to_s =~ /^incoming_(.*)$/)
|
25
|
+
number = @event_number_lookup[$1].to_i
|
26
|
+
event = :"incoming_numeric_#{number}" if number > 0
|
27
|
+
end
|
28
|
+
|
29
|
+
@legacy_handlers[event] ||= Array.new
|
30
|
+
until procs.empty?
|
31
|
+
@legacy_handlers[event].unshift(procs.pop)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Handles the given event (if it's in the @legacy_handlers array) with the
|
36
|
+
# arguments specified.
|
37
|
+
#
|
38
|
+
# The @legacy_handlers must be a hash where key = event to handle and value is
|
39
|
+
# a Proc object (via Class.method(:name) or just proc {...}).
|
40
|
+
# This should be fine if you're setting up handlers with the prepend_handler
|
41
|
+
# method, but if you get "clever," you're on your own.
|
42
|
+
def handle(event, *arguments)
|
43
|
+
# Don't bother with anything if there are no handlers registered.
|
44
|
+
return false unless Array === @legacy_handlers[event]
|
45
|
+
|
46
|
+
@log.debug "+++EVENT HANDLER: Handling event #{event} via #{@legacy_handlers[event].inspect}:"
|
47
|
+
|
48
|
+
# Call all hooks in order until one breaks the chain. For incoming
|
49
|
+
# events, we want something to break the chain or else it'll likely
|
50
|
+
# hit a reporter. For outgoing events, we tend to report them anyway,
|
51
|
+
# so no need to worry about ending the chain except when the bot wants
|
52
|
+
# to take full control over them.
|
53
|
+
result = false
|
54
|
+
for handler in @legacy_handlers[event]
|
55
|
+
result = handler.call(*arguments)
|
56
|
+
break if result == true
|
57
|
+
end
|
58
|
+
|
59
|
+
# Let the new system deal with legacy handlers that wanted to end the chain
|
60
|
+
return result
|
61
|
+
end
|
62
|
+
|
63
|
+
# Since numerics are so many and so varied, this method will auto-fallback
|
64
|
+
# to a simple report if no handler was defined.
|
65
|
+
def handle_numeric(number, fullactor, actor, target, message)
|
66
|
+
# All numerics share the same args, and rarely care about anything but
|
67
|
+
# message, so let's make it easier by passing a hash instead of a list
|
68
|
+
args = {:fullactor => fullactor, :actor => actor, :target => target}
|
69
|
+
base_event = :"incoming_numeric_#{number}"
|
70
|
+
if Array === @legacy_handlers[base_event]
|
71
|
+
return handle(base_event, message, args)
|
72
|
+
else
|
73
|
+
# No handler = report and don't worry about it
|
74
|
+
@log.info "Unknown raw #{number.to_s} from #{fullactor}: #{message}"
|
75
|
+
return false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Gets some input, sends stuff off to a handler. Yay.
|
80
|
+
def legacy_process_event(event)
|
81
|
+
# Allow global handler to break the chain, filter the line, whatever. When we ditch these legacy
|
82
|
+
# events, this code will finally die!
|
83
|
+
if (Net::YAIL::IncomingEvent === event && Array === @legacy_handlers[:incoming_any])
|
84
|
+
for handler in @legacy_handlers[:incoming_any]
|
85
|
+
result = handler.call(event.raw)
|
86
|
+
return true if true == result
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Partial conversion to using events - we still have a horrible case statement, but
|
91
|
+
# we're at least using the event object. Slightly less hacky than before.
|
92
|
+
|
93
|
+
# Except for this - we still have to handle numerics the crappy way until we build the proper
|
94
|
+
# dispatching of events
|
95
|
+
event = event.parent if event.parent && :incoming_numeric == event.parent.type
|
96
|
+
|
97
|
+
case event.type
|
98
|
+
# Ping is important to handle quickly, so it comes first.
|
99
|
+
when :incoming_ping
|
100
|
+
return handle(event.type, event.message)
|
101
|
+
|
102
|
+
when :incoming_numeric
|
103
|
+
# Lovely - I passed in a "nick" - which, according to spec, is NEVER part of a numeric reply
|
104
|
+
handle_numeric(event.numeric, event.servername, nil, event.target, event.message)
|
105
|
+
|
106
|
+
when :incoming_invite
|
107
|
+
return handle(event.type, event.fullname, event.nick, event.channel)
|
108
|
+
|
109
|
+
# Fortunately, the legacy handler for all five "message" types is the same!
|
110
|
+
when :incoming_msg, :incoming_ctcp, :incoming_act, :incoming_notice, :incoming_ctcpreply
|
111
|
+
# Legacy handling requires merger of target and channel....
|
112
|
+
target = event.target if event.pm?
|
113
|
+
target = event.channel if !target
|
114
|
+
|
115
|
+
# Notices come from server sometimes, so... another merger for legacy fun!
|
116
|
+
nick = event.server? ? '' : event.nick
|
117
|
+
return handle(event.type, event.from, nick, target, event.message)
|
118
|
+
|
119
|
+
# This is a bit painful for right now - just use some hacks to make it work semi-nicely,
|
120
|
+
# but let's not put hacks into the core Event object. Modes need reworking soon anyway.
|
121
|
+
#
|
122
|
+
# NOTE: message is currently the mode settings ('+b', for instance) - very bad. TODO: FIX FIX FIX!
|
123
|
+
when :incoming_mode
|
124
|
+
# Modes can come from the server, so legacy system again regularly sent nil data....
|
125
|
+
nick = event.server? ? '' : event.nick
|
126
|
+
return handle(event.type, event.from, nick, event.channel, event.message, event.targets.join(' '))
|
127
|
+
|
128
|
+
when :incoming_topic_change
|
129
|
+
return handle(event.type, event.fullname, event.nick, event.channel, event.message)
|
130
|
+
|
131
|
+
when :incoming_join
|
132
|
+
return handle(event.type, event.fullname, event.nick, event.channel)
|
133
|
+
|
134
|
+
when :incoming_part
|
135
|
+
return handle(event.type, event.fullname, event.nick, event.channel, event.message)
|
136
|
+
|
137
|
+
when :incoming_kick
|
138
|
+
return handle(event.type, event.fullname, event.nick, event.channel, event.target, event.message)
|
139
|
+
|
140
|
+
when :incoming_quit
|
141
|
+
return handle(event.type, event.fullname, event.nick, event.message)
|
142
|
+
|
143
|
+
when :incoming_nick
|
144
|
+
return handle(event.type, event.fullname, event.nick, event.message)
|
145
|
+
|
146
|
+
when :incoming_error
|
147
|
+
return handle(event.type, event.message)
|
148
|
+
|
149
|
+
when :outgoing_privmsg, :outgoing_msg, :outgoing_ctcp, :outgoing_act, :outgoing_notice, :outgoing_ctcpreply
|
150
|
+
return handle(event.type, event.target, event.message)
|
151
|
+
|
152
|
+
when :outgoing_mode
|
153
|
+
return handle(event.type, event.target, event.modes, event.objects)
|
154
|
+
|
155
|
+
when :outgoing_join
|
156
|
+
return handle(event.type, event.channel, event.password)
|
157
|
+
|
158
|
+
when :outgoing_part
|
159
|
+
return handle(event.type, event.channel, event.message)
|
160
|
+
|
161
|
+
when :outgoing_quit
|
162
|
+
return handle(event.type, event.message)
|
163
|
+
|
164
|
+
when :outgoing_nick
|
165
|
+
return handle(event.type, event.nick)
|
166
|
+
|
167
|
+
when :outgoing_user
|
168
|
+
return handle(event.type, event.username, event.hostname, event.servername, event.realname)
|
169
|
+
|
170
|
+
when :outgoing_pass
|
171
|
+
return handle(event.type, event.password)
|
172
|
+
|
173
|
+
when :outgoing_oper
|
174
|
+
return handle(event.type, event.user, event.password)
|
175
|
+
|
176
|
+
when :outgoing_topic
|
177
|
+
return handle(event.type, event.channel, event.topic)
|
178
|
+
|
179
|
+
when :outgoing_names
|
180
|
+
return handle(event.type, event.channel)
|
181
|
+
|
182
|
+
when :outgoing_list
|
183
|
+
return handle(event.type, event.channel, event.server)
|
184
|
+
|
185
|
+
when :outgoing_invite
|
186
|
+
return handle(event.type, event.nick, event.channel)
|
187
|
+
|
188
|
+
when :outgoing_kick
|
189
|
+
return handle(event.type, event.nick, event.channel, event.reason)
|
190
|
+
|
191
|
+
when :outgoing_begin_connection
|
192
|
+
return handle(event.type, event.username, event.address, event.realname)
|
193
|
+
|
194
|
+
# Unknown line - if an incoming event, we need to log it as that shouldn't be able to happen,
|
195
|
+
# but we don't want to kill somebody's app for it. An outgoing event that's part of the
|
196
|
+
# system should NEVER hit this, so we throw an error in that case. Custom events just get
|
197
|
+
# handled with no arguments, to allow for things like :irc_loop.
|
198
|
+
else
|
199
|
+
case event
|
200
|
+
when Net::YAIL::IncomingEvent
|
201
|
+
@log.warn 'Unknown line: %s!' % event.raw.inspect
|
202
|
+
@log.warn "Please report this to the github repo at https://github.com/Nerdmaster/ruby-irc-yail/issues"
|
203
|
+
return handle(:incoming_miscellany, event.raw)
|
204
|
+
when Net::YAIL::OutgoingEvent
|
205
|
+
raise "Unknown outgoing event: #{event.inspect}"
|
206
|
+
else
|
207
|
+
handle(event.type)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
end
|
@@ -1,52 +1,56 @@
|
|
1
1
|
module Net
|
2
2
|
module IRCEvents
|
3
3
|
|
4
|
-
# This module contains all the "magic" methods that need to happen
|
5
|
-
# of
|
4
|
+
# This module contains all the "magic" methods that need to happen by default. User could overwrite
|
5
|
+
# some of these, but really really shouldn't.
|
6
6
|
module Magic
|
7
7
|
private
|
8
8
|
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
# We dun connected to a server! Just sends password (if one is set) and
|
10
|
+
# user/nick. This isn't quite "essential" to a working IRC app, but this data
|
11
|
+
# *must* be sent at some point, so be careful before clobbering this handler.
|
12
|
+
def out_begin_connection(event)
|
13
|
+
pass(@password) if @password
|
14
|
+
user(event.username, '0.0.0.0', event.address, event.realname)
|
15
|
+
nick(@nicknames[0])
|
14
16
|
end
|
15
17
|
|
16
18
|
# We were welcomed, so we need to set up initial nickname and set that we
|
17
19
|
# registered so nick change failure doesn't cause DEATH!
|
18
|
-
def magic_welcome(
|
19
|
-
|
20
|
-
|
20
|
+
def magic_welcome(event)
|
21
|
+
# TODO: Ditch this call to report - move to report lib if necessary
|
22
|
+
report "#{event.from} welcome message: #{event.message}"
|
23
|
+
if (event.message =~ /(\S+)!\S+$/)
|
21
24
|
@me = $1
|
22
|
-
elsif (
|
25
|
+
elsif (event.message =~ /(\S+)$/)
|
23
26
|
@me = $1
|
24
27
|
end
|
25
28
|
|
26
29
|
@registered = true
|
27
30
|
mode @me, 'i'
|
28
|
-
|
29
|
-
# Don't break the chain if user wants their own handler
|
30
|
-
return false
|
31
31
|
end
|
32
32
|
|
33
|
-
# Ping must have a PONG
|
34
|
-
def magic_ping(
|
35
|
-
@socket.puts "PONG :#{text}"
|
33
|
+
# Ping must have a PONG, though crazy user can handle this her own way if she likes
|
34
|
+
def magic_ping(event); @socket.puts "PONG :#{event.message}"; end
|
36
35
|
|
37
|
-
|
38
|
-
|
36
|
+
# If bot changes his name, @me must change - this must be a filter, not the callback!
|
37
|
+
def magic_nick(event)
|
38
|
+
@me = event.message.dup if event.nick.downcase == @me.downcase
|
39
39
|
end
|
40
40
|
|
41
|
-
#
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
# User calls msg, sends a simple message out to the event's target (user or channel)
|
42
|
+
def magic_out_msg(event)
|
43
|
+
privmsg(event.target, event.message)
|
44
|
+
end
|
45
|
+
|
46
|
+
# CTCP
|
47
|
+
def magic_out_ctcp(event)
|
48
|
+
privmsg(event.target, "\001#{event.message}\001")
|
49
|
+
end
|
47
50
|
|
48
|
-
|
49
|
-
|
51
|
+
# CTCP ACTION
|
52
|
+
def magic_out_act(event)
|
53
|
+
privmsg(event.target, "\001ACTION #{event.message}\001")
|
50
54
|
end
|
51
55
|
|
52
56
|
end
|
data/lib/net/yail/output_api.rb
CHANGED
@@ -1,290 +1,110 @@
|
|
1
1
|
module Net
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# ==Argument Duping
|
8
|
-
#
|
9
|
-
# Output APIs dup incoming args before sending them off to handlers. This
|
10
|
-
# is a mechanism that I think could be done better, but I can't figure a good
|
11
|
-
# way to do it at the moment. The reason this is necessary is for a specific
|
12
|
-
# situation where a bot has an array of response messages, and needs to filter
|
13
|
-
# those messages. A call to "msg(messages[rand(10)])" with a handler on :outgoing_msg
|
14
|
-
# that does something like <code>text.gsub!('a', '@')</code> (like a leetspeek
|
15
|
-
# filter) shouldn't destroy the original data in the messages array.
|
16
|
-
#
|
17
|
-
# This could be left up to the programmer, but it seems like something that
|
18
|
-
# a library should own - protecting the programmer for having to remember that
|
19
|
-
# sort of crap, especially if the app is calling msg, act, ctcp, etc. in
|
20
|
-
# various ways from multiple points in the code....
|
21
|
-
#
|
22
|
-
# ==Apologies, good sirs
|
23
|
-
#
|
24
|
-
# If a method exists in this module, and it isn't the +raw+ method, chances
|
25
|
-
# are it's got a handler in the form of :outgoing_<method name>. I am hoping
|
26
|
-
# I document all of those in the main Net::YAIL code, but if I miss one, I
|
27
|
-
# apologize.
|
3
|
+
# This module is responsible for the raw socket output, buffering of all "message" types of
|
4
|
+
# events, and exposing the magic to create a new output command + handler. All output methods
|
5
|
+
# are documented in the main Net::YAIL documentation.
|
28
6
|
module IRCOutputAPI
|
29
7
|
# Spits a raw string out to the server - in case a subclass wants to do
|
30
8
|
# something special on *all* output, please make all output go through this
|
31
9
|
# method. Don't use puts manually. I will kill violaters. Legally
|
32
10
|
# speaking, that is.
|
33
|
-
def raw(line
|
11
|
+
def raw(line)
|
34
12
|
@socket.puts "#{line}\r\n"
|
35
|
-
report "bot: #{line.inspect}" if report
|
36
13
|
end
|
37
14
|
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
|
42
|
-
#
|
43
|
-
# This is sort of the central message output - everything that's based on
|
44
|
-
# PRIVMSG (messages, actions, other ctcp) uses this. Because these messages
|
45
|
-
# aren't insanely important, we actually buffer them instead of sending
|
46
|
-
# straight out to the channel. The output thread has to deal with
|
47
|
-
# sending these out.
|
48
|
-
def privmsg(target, text, report_string)
|
49
|
-
# Dup strings so handler can filter safely
|
50
|
-
target = target.dup
|
51
|
-
text = text.dup
|
52
|
-
|
53
|
-
handle(:outgoing_privmsg, target, text)
|
54
|
-
|
15
|
+
# Buffers the given event to be sent out when we are able to send something out to the given
|
16
|
+
# target. If buffering isn't turned on, the event will be processed in the next loop of outgoing
|
17
|
+
# messages.
|
18
|
+
def buffer_output(event)
|
55
19
|
@privmsg_buffer_mutex.synchronize do
|
56
|
-
@privmsg_buffer[target] ||= Array.new
|
57
|
-
@privmsg_buffer[target].push
|
20
|
+
@privmsg_buffer[event.target] ||= Array.new
|
21
|
+
@privmsg_buffer[event.target].push event
|
58
22
|
end
|
59
23
|
end
|
60
24
|
|
61
|
-
#
|
62
|
-
#
|
63
|
-
# shortcut methods for those types. Target is a channel or username, text
|
25
|
+
# Buffers an :outgoing_msg event. Could be used to send any privmsg, but you're betting off
|
26
|
+
# using act and ctcp shortcut methods for those types. Target is a channel or username, message
|
64
27
|
# is the message.
|
65
|
-
def msg(target,
|
66
|
-
|
67
|
-
target = target.dup
|
68
|
-
text = text.dup
|
69
|
-
|
70
|
-
handle(:outgoing_msg, target, text)
|
71
|
-
|
72
|
-
report_string = @log_silent ? '' : "{#{target}} <#{@me}> #{text}"
|
73
|
-
privmsg(target, text, report_string)
|
74
|
-
end
|
75
|
-
|
76
|
-
# Calls :outgoing_ctcp handler, then sends CTCP to target channel or user
|
77
|
-
def ctcp(target, text)
|
78
|
-
# Dup strings so handler can filter safely
|
79
|
-
target = target.dup
|
80
|
-
text = text.dup
|
81
|
-
|
82
|
-
handle(:outgoing_ctcp, target, text)
|
83
|
-
|
84
|
-
report_string = @log_silent ? '' : "{#{target}} [#{@me} #{text}]"
|
85
|
-
privmsg(target, "\001#{text}\001", report_string)
|
28
|
+
def msg(target, message)
|
29
|
+
buffer_output Net::YAIL::OutgoingEvent.new(:type => :msg, :target => target, :message => message)
|
86
30
|
end
|
87
31
|
|
88
|
-
#
|
89
|
-
|
90
|
-
|
91
|
-
# Dup strings so handler can filter safely
|
92
|
-
target = target.dup
|
93
|
-
text = text.dup
|
94
|
-
|
95
|
-
handle(:outgoing_act, target, text)
|
96
|
-
|
97
|
-
ctcp(target, "ACTION #{text}")
|
32
|
+
# Buffers an :outgoing_ctcp event. Target is user or channel, message is message.
|
33
|
+
def ctcp(target, message)
|
34
|
+
buffer_output Net::YAIL::OutgoingEvent.new(:type => :ctcp, :target => target, :message => message)
|
98
35
|
end
|
99
36
|
|
100
|
-
#
|
101
|
-
def
|
102
|
-
|
103
|
-
target = target.dup
|
104
|
-
text = text.dup
|
105
|
-
|
106
|
-
handle(:outgoing_notice, target, text)
|
107
|
-
|
108
|
-
report "{#{target}} -#{@me}- #{text}" unless @log_silent
|
109
|
-
raw("NOTICE #{target} :#{text}", false)
|
37
|
+
# Buffers an :outgoing_act event. Target is user or channel, message is message.
|
38
|
+
def act(target, message)
|
39
|
+
buffer_output Net::YAIL::OutgoingEvent.new(:type => :act, :target => target, :message => message)
|
110
40
|
end
|
111
41
|
|
112
|
-
#
|
113
|
-
#
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
# Calls :outgoing_join handler and then raw JOIN message for a given channel
|
143
|
-
def join(target, pass = '')
|
144
|
-
# Dup strings so handler can filter safely
|
145
|
-
target = target.dup
|
146
|
-
pass = pass.dup
|
147
|
-
|
148
|
-
handle(:outgoing_join, target, pass)
|
149
|
-
|
150
|
-
text = "JOIN #{target}"
|
151
|
-
text += " #{pass}" unless pass.empty?
|
152
|
-
raw text
|
153
|
-
end
|
154
|
-
|
155
|
-
# Calls :outgoing_part handler and then raw PART for leaving a given channel
|
156
|
-
# (with an optional message)
|
157
|
-
def part(target, text = '')
|
158
|
-
# Dup strings so handler can filter safely
|
159
|
-
target = target.dup
|
160
|
-
text = text.dup
|
161
|
-
|
162
|
-
handle(:outgoing_part, target, text)
|
163
|
-
|
164
|
-
request = "PART #{target}";
|
165
|
-
request += " :#{text}" unless text.to_s.empty?
|
166
|
-
raw request
|
167
|
-
end
|
168
|
-
|
169
|
-
# Calls :outgoing_quit handler and then raw QUIT message with an optional
|
170
|
-
# reason
|
171
|
-
def quit(text = '')
|
172
|
-
# Dup strings so handler can filter safely
|
173
|
-
text = text.dup
|
174
|
-
|
175
|
-
handle(:outgoing_quit, text)
|
176
|
-
|
177
|
-
request = "QUIT";
|
178
|
-
request += " :#{text}" unless text.to_s.empty?
|
179
|
-
raw request
|
180
|
-
end
|
181
|
-
|
182
|
-
# Calls :outgoing_nick handler and then sends raw NICK message to change
|
183
|
-
# nickname.
|
184
|
-
def nick(new_nick)
|
185
|
-
# Dup strings so handler can filter safely
|
186
|
-
new_nick = new_nick.dup
|
187
|
-
|
188
|
-
handle(:outgoing_nick, new_nick)
|
189
|
-
|
190
|
-
raw "NICK :#{new_nick}"
|
191
|
-
end
|
192
|
-
|
193
|
-
# Identifies ourselves to the server. Calls :outgoing_user and sends raw
|
194
|
-
# USER command.
|
195
|
-
def user(username, myaddress, address, realname)
|
196
|
-
# Dup strings so handler can filter safely
|
197
|
-
username = username.dup
|
198
|
-
myaddress = myaddress.dup
|
199
|
-
address = address.dup
|
200
|
-
realname = realname.dup
|
201
|
-
|
202
|
-
handle(:outgoing_user, username, myaddress, address, realname)
|
203
|
-
|
204
|
-
raw "USER #{username} #{myaddress} #{address} :#{realname}"
|
205
|
-
end
|
206
|
-
|
207
|
-
# Sends a password to the server. This *must* be sent before NICK/USER.
|
208
|
-
# Calls :outgoing_pass and sends raw PASS command.
|
209
|
-
def pass(password)
|
210
|
-
# Dupage
|
211
|
-
password = password.dup
|
212
|
-
|
213
|
-
handle(:outgoing_pass, password)
|
214
|
-
raw "PASS #{password}"
|
215
|
-
end
|
216
|
-
|
217
|
-
# Sends an op request. Calls :outgoing_oper and raw OPER command.
|
218
|
-
def oper(user, password)
|
219
|
-
# Dupage
|
220
|
-
user = user.dup
|
221
|
-
password = password.dup
|
222
|
-
|
223
|
-
handle(:outgoing_oper, user, password)
|
224
|
-
raw "OPER #{user} #{password}"
|
225
|
-
end
|
226
|
-
|
227
|
-
# Gets or sets the topic. Calls :outgoing_topic and raw TOPIC command
|
228
|
-
def topic(channel, new_topic = nil)
|
229
|
-
# Dup for filter safety in outgoing handler
|
230
|
-
channel = channel.dup
|
231
|
-
new_topic = new_topic.dup unless new_topic.nil?
|
232
|
-
|
233
|
-
handle(:outgoing_topic, channel, new_topic)
|
234
|
-
output = "TOPIC #{channel}"
|
235
|
-
output += " :#{new_topic}" unless new_topic.to_s.empty?
|
236
|
-
raw output
|
237
|
-
end
|
238
|
-
|
239
|
-
# Gets a list of users and channels if channel isn't specified. If channel
|
240
|
-
# is specified, only shows users in that channel. Will not show invisible
|
241
|
-
# users or channels. Calls :outgoing_names and raw NAMES command.
|
242
|
-
def names(channel = nil)
|
243
|
-
channel = channel.dup unless channel.nil?
|
244
|
-
|
245
|
-
handle(:outgoing_names, channel)
|
246
|
-
output = "NAMES"
|
247
|
-
output += " #{channel}" unless channel.to_s.empty?
|
248
|
-
raw output
|
249
|
-
end
|
250
|
-
|
251
|
-
# I don't know what the server param is for, but it's in the RFC. If
|
252
|
-
# channel is blank, lists all visible, otherwise just lists the channel in
|
253
|
-
# question. Calls :outgoing_list and raw LIST command.
|
254
|
-
def list(channel = nil, server = nil)
|
255
|
-
channel = channel.dup unless channel.nil?
|
256
|
-
server = server.dup unless server.nil?
|
257
|
-
|
258
|
-
handle(:outgoing_list, channel, server)
|
259
|
-
output = "LIST"
|
260
|
-
output += " #{channel}" if channel
|
261
|
-
output += " #{server}" if server
|
262
|
-
raw output
|
263
|
-
end
|
264
|
-
|
265
|
-
# Invites a user to a channel. Calls :outgoing_invite and raw INVITE
|
266
|
-
# command.
|
267
|
-
def invite(nick, channel)
|
268
|
-
channel = channel.dup
|
269
|
-
server = server.dup
|
42
|
+
# Creates an output command and its handler. output_base is a template of the command without
|
43
|
+
# any conditional arguments (for simple commands this is the full template). args is a list of
|
44
|
+
# argument symbols to determine how the event is built and handled. If an argument symbol is
|
45
|
+
# followed by a string, that string is conditionally appended to the output in the handler if the
|
46
|
+
# event has data for that argument.
|
47
|
+
#
|
48
|
+
# I hate the hackiness here, but it's so much easier to build the commands and handlers with an
|
49
|
+
# ugly one-liner than manually, and things like define_method seem to fall short with how much
|
50
|
+
# crap this needs to do.
|
51
|
+
def create_command(command, output_base, *opts)
|
52
|
+
event_opts = lambda {|text| text.gsub(/:(\w+)/, '#{event.\1}') }
|
53
|
+
|
54
|
+
output_base = event_opts.call(output_base)
|
55
|
+
|
56
|
+
# Create a list of actual arg symbols and templates for optional args
|
57
|
+
args = []
|
58
|
+
optional_arg_templates = {}
|
59
|
+
last_symbol = nil
|
60
|
+
for opt in opts
|
61
|
+
case opt
|
62
|
+
when Symbol
|
63
|
+
args.push opt
|
64
|
+
last_symbol = opt
|
65
|
+
when String
|
66
|
+
raise ArgumentError.new("create_command optional argument must have an argument symbol preceding them") unless last_symbol
|
67
|
+
optional_arg_templates[last_symbol] = event_opts.call(opt)
|
68
|
+
last_symbol = nil
|
69
|
+
end
|
70
|
+
end
|
270
71
|
|
271
|
-
|
272
|
-
|
273
|
-
|
72
|
+
# Format strings for command args and event creation
|
73
|
+
event_string = args.collect {|arg| ":#{arg} => #{arg}"}.join(",")
|
74
|
+
event_string = ", #{event_string}" unless event_string.empty?
|
75
|
+
args_string = args.collect {|arg| "#{arg} = ''"}.join(",")
|
76
|
+
|
77
|
+
# Create the command function
|
78
|
+
command_code = %Q|
|
79
|
+
def #{command}(#{args_string})
|
80
|
+
dispatch Net::YAIL::OutgoingEvent.new(:type => #{command.inspect}#{event_string})
|
81
|
+
end
|
82
|
+
|
|
83
|
+
self.class.class_eval command_code
|
84
|
+
|
85
|
+
# Create the handler piece by piece - wow how ugly this is!
|
86
|
+
command_handler = :"magic_out_#{command}"
|
87
|
+
handler_code = %Q|
|
88
|
+
def #{command_handler}(event)
|
89
|
+
output_string = "#{output_base}"
|
90
|
+
|
|
91
|
+
for arg in args
|
92
|
+
if optional_arg_templates[arg]
|
93
|
+
handler_code += %Q|
|
94
|
+
output_string += "#{optional_arg_templates[arg]}" unless event.#{arg}.to_s.empty?
|
95
|
+
|
|
96
|
+
end
|
97
|
+
end
|
98
|
+
handler_code += %Q|
|
99
|
+
raw output_string
|
100
|
+
end
|
101
|
+
|
|
274
102
|
|
275
|
-
|
276
|
-
# :outgoing_kick and issues a raw KICK command.
|
277
|
-
def kick(nick, channel, comment = nil)
|
278
|
-
nick = nick.dup
|
279
|
-
channel = channel.dup
|
280
|
-
comment = comment.dup unless comment.nil?
|
103
|
+
self.class.class_eval handler_code
|
281
104
|
|
282
|
-
|
283
|
-
|
284
|
-
output += " :#{comment}" unless comment.to_s.empty?
|
285
|
-
raw output
|
105
|
+
# At least setting the callback isn't a giant pile of dumb
|
106
|
+
set_callback :"outgoing_#{command}", self.method(command_handler)
|
286
107
|
end
|
287
|
-
|
288
108
|
end
|
289
109
|
|
290
110
|
end
|