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.
- 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
|