net-yail 1.2.0 → 1.2.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/CHANGELOG +11 -0
- data/README +1 -2
- data/lib/net/yail.rb +49 -26
- data/lib/net/yail/IRCBot.rb +7 -1
- data/lib/net/yail/default_events.rb +6 -5
- data/lib/net/yail/output_api.rb +91 -1
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -34,3 +34,14 @@
|
|
34
34
|
* Fixed logger's handler to not call the channel logging code on private message
|
35
35
|
* Figured it's about time for a new gem, 1.2.0. Change to join handler breaks
|
36
36
|
existing code in a minor way - don't forget to fix your handlers!
|
37
|
+
|
38
|
+
2008-07-22:
|
39
|
+
* Added a bunch of new output APIs and corresponding outgoing handlers
|
40
|
+
* Auto-support for server password with new arg to constructor
|
41
|
+
* Moved TODO stuff into a separate file outside the gem's evil influence!
|
42
|
+
|
43
|
+
2008-07-??:
|
44
|
+
* Fixed up a bunch of code to add a bit more stability for dead sockets and
|
45
|
+
such.
|
46
|
+
* IRCBot can now properly reconnect when the socket does end up dying.
|
47
|
+
* Bumped version to 1.2.1. Probably a good time to release another gem.
|
data/README
CHANGED
@@ -56,8 +56,7 @@ Features of YAIL:
|
|
56
56
|
for the most basic working examples.
|
57
57
|
|
58
58
|
I still have a lot to do, though. The output API is definitely not fully
|
59
|
-
fleshed out
|
60
|
-
as KICK or TOPIC. I believe that the library is also missing a lot for people
|
59
|
+
fleshed out. I believe that the library is also missing a lot for people
|
61
60
|
who just have a different approach than me, since this was purely designed for
|
62
61
|
my own benefit, and then released almost exclusively to piss off the people
|
63
62
|
whose work I stole to get where I'm at today. (Just kiddin', Pope)
|
data/lib/net/yail.rb
CHANGED
@@ -9,21 +9,6 @@ require 'net/yail/magic_events'
|
|
9
9
|
require 'net/yail/default_events'
|
10
10
|
require 'net/yail/output_api'
|
11
11
|
|
12
|
-
# TODO:
|
13
|
-
# * Extract all pattern matching into an external file - store both the
|
14
|
-
# pattern and the event handle's symbol.
|
15
|
-
# * Build a system to allow numeric events to get sent post-processed data
|
16
|
-
# if it makes sense (converting the text to specific parts instead of all
|
17
|
-
# handlers having to regex it themselves, for instance)
|
18
|
-
# * To deal with the required post-handler logic in the output API, and
|
19
|
-
# "nicely" deal with filtering output, it may be better to have a separate
|
20
|
-
# hash of YAIL-required post-handlers. This would allow all logic to be
|
21
|
-
# done as handlers (makes things more consistent) as well as getting rid of
|
22
|
-
# the funky arg duping on a per-API-method basis (putting the auto-dupe back
|
23
|
-
# in the handle method)
|
24
|
-
# * Handle this:
|
25
|
-
# (14:45.25) +++INCOMING: :Nerdmaster!xxx@yyyy INVITE botname :#channel_name
|
26
|
-
|
27
12
|
# If a thread crashes, I want the app to die. My threads are persistent, not
|
28
13
|
# temporary.
|
29
14
|
Thread.abort_on_exception = true
|
@@ -82,10 +67,14 @@ module Net
|
|
82
67
|
#
|
83
68
|
# Generally speaking, you won't need these very often, but they're here for
|
84
69
|
# the edge cases all the same. Note that the socket output cannot be skipped
|
85
|
-
# (see Return value from events below), so this is truly just to allow
|
70
|
+
# (see "Return value from events" below), so this is truly just to allow
|
86
71
|
# modifying things before they go out (filtering speech, converting or
|
87
72
|
# stripping markup, etc) or just general stats-type logic.
|
88
73
|
#
|
74
|
+
# Note that in all cases below, the client is *about* to perform an action.
|
75
|
+
# Text can be filtered, things can be logged, but keep in mind that the action
|
76
|
+
# has not yet happened.
|
77
|
+
#
|
89
78
|
# Events:
|
90
79
|
# * :outgoing_begin_connection(username, address, realname) - called when the
|
91
80
|
# start_listening method has set up all threading and such. Default behavior
|
@@ -111,6 +100,21 @@ module Net
|
|
111
100
|
# * :outgoing_nick(new_nick) - The client is about to change nickname
|
112
101
|
# * :outgoing_user(username, myaddress, address, realname) - We're about to
|
113
102
|
# send a USER command.
|
103
|
+
# * :outgoing_pass(password) - The client is about to send a password to the
|
104
|
+
# server via PASS.
|
105
|
+
# * :outgoing_oper(user, password) - The client is about to request ops from
|
106
|
+
# the server via OPER.
|
107
|
+
# * :outgoing_topic(channel, new_topic) - If new_topic is blank (nil or ''),
|
108
|
+
# the client is requesting the channel's topic, otherwise setting it.
|
109
|
+
# * :outgoing_names(channel) - Client is requesting a list of names in the
|
110
|
+
# given channel, or all channels and names if channel is blank.
|
111
|
+
# * :outgoing_list(channel, server) - Client is querying channel information.
|
112
|
+
# I honestly don't know what server is for from RFC, but asking for a
|
113
|
+
# specific channel gives just data on that channel.
|
114
|
+
# * :outgoing_invite(nick, channel) - Client is sending an INVITE message to
|
115
|
+
# nick for channel.
|
116
|
+
# * :outgoing_kick(nick, channel, comment) - Client is about to kick the user
|
117
|
+
# from the channel with an optional comment.
|
114
118
|
#
|
115
119
|
# Note that a single output call can hit multiple handlers, so you must plan
|
116
120
|
# carefully. A call to act() will hit the act handler, then ctcp (since act
|
@@ -221,7 +225,7 @@ class YAIL
|
|
221
225
|
:me, # Nickname on the IRC server
|
222
226
|
:registered, # If true, we've been welcomed
|
223
227
|
:nicknames, # Array of nicknames to try when logging on to server
|
224
|
-
:dead_socket, # True if
|
228
|
+
:dead_socket, # True if @socket.eof? or read/connect fail
|
225
229
|
:socket # TCPSocket instance
|
226
230
|
)
|
227
231
|
attr_accessor(
|
@@ -248,6 +252,8 @@ class YAIL
|
|
248
252
|
# * <tt>:throttle_seconds</tt>: Seconds between a cycle of privmsg sends.
|
249
253
|
# Defaults to 1. One "cycle" is defined as sending one line of output to
|
250
254
|
# *all* targets that have output buffered.
|
255
|
+
# * <tt>:server_password</tt>: Very optional. If set, this is the password
|
256
|
+
# sent out to the server before USER and NICK messages.
|
251
257
|
def initialize(options = {})
|
252
258
|
@me = ''
|
253
259
|
@nicknames = options[:nicknames]
|
@@ -259,6 +265,7 @@ class YAIL
|
|
259
265
|
@silent = options[:silent] || false
|
260
266
|
@loud = options[:loud] || false
|
261
267
|
@throttle_seconds = options[:throttle_seconds] || 1
|
268
|
+
@password = options[:server_password]
|
262
269
|
|
263
270
|
# Read in map of event numbers and names. Yes, I stole this event map
|
264
271
|
# file from RubyIRC and made very minor changes.... They stole it from
|
@@ -266,12 +273,17 @@ class YAIL
|
|
266
273
|
eventmap = "#{File.dirname(__FILE__)}/yail/eventmap.yml"
|
267
274
|
@event_number_lookup = File.open(eventmap) { |file| YAML::load(file) }.invert
|
268
275
|
|
269
|
-
# Build our socket
|
270
|
-
@socket = TCPSocket.new(@address, @port)
|
271
|
-
|
272
276
|
# We're not dead... yet...
|
273
277
|
@dead_socket = false
|
274
278
|
|
279
|
+
# Build our socket - if something goes wrong, it's immediately a dead socket.
|
280
|
+
begin
|
281
|
+
@socket = TCPSocket.new(@address, @port)
|
282
|
+
rescue StandardError => boom
|
283
|
+
report "+++ERROR: Unable to open socket connection in Net::YAIL.initialize: #{boom.inspect}"
|
284
|
+
@dead_socket = true
|
285
|
+
end
|
286
|
+
|
275
287
|
# Shared resources for threads to try and coordinate.... I know very
|
276
288
|
# little about thread safety, so this stuff may be a terrible disaster.
|
277
289
|
# Please send me better approaches if you are less stupid than I.
|
@@ -294,6 +306,9 @@ class YAIL
|
|
294
306
|
# We don't want to spawn an extra listener
|
295
307
|
return if Thread === @ioloop_thread
|
296
308
|
|
309
|
+
# Don't listen if socket is dead
|
310
|
+
return if @dead_socket
|
311
|
+
|
297
312
|
# Build forced / magic logic - welcome setting @me, ping response, etc.
|
298
313
|
# Since we do these here, nobody can skip them and they're always first.
|
299
314
|
setup_magic_handlers
|
@@ -312,10 +327,10 @@ class YAIL
|
|
312
327
|
# regarding threads. Please help me if you know how to make this system
|
313
328
|
# better. DEAR LORD HELP ME IF YOU CAN!
|
314
329
|
def stop_listening
|
315
|
-
|
316
|
-
@input_processor.terminate
|
317
|
-
@privmsg_processor.terminate
|
330
|
+
# Kill all threads if they're really threads
|
331
|
+
[@ioloop_thread, @input_processor, @privmsg_processor].each {|thread| thread.terminate if Thread === thread}
|
318
332
|
|
333
|
+
# Just for safety, set everything to nil
|
319
334
|
@ioloop_thread = nil
|
320
335
|
@input_processor = nil
|
321
336
|
@privmsg_processor = nil
|
@@ -326,10 +341,14 @@ class YAIL
|
|
326
341
|
# Reads incoming data - should only be called by io_loop, and only when
|
327
342
|
# we've already ensured that data is, in fact, available.
|
328
343
|
def read_incoming_data
|
329
|
-
|
344
|
+
begin
|
345
|
+
line = @socket.gets
|
346
|
+
rescue StandardError => boom
|
347
|
+
@dead_socket = true
|
348
|
+
report "+++ERROR in read_incoming_data -> @socket.gets: #{boom.inspect}"
|
349
|
+
end
|
330
350
|
|
331
|
-
# If we
|
332
|
-
# status and return
|
351
|
+
# If we somehow got no data here, the socket is closed. Run away!!!
|
333
352
|
if !line
|
334
353
|
@dead_socket = true
|
335
354
|
return
|
@@ -352,6 +371,10 @@ class YAIL
|
|
352
371
|
while true
|
353
372
|
# if no data is coming in, don't block the socket!
|
354
373
|
read_incoming_data if Kernel.select([@socket], nil, nil, 0)
|
374
|
+
|
375
|
+
# Check for dead socket
|
376
|
+
@dead_socket = true if @socket.eof?
|
377
|
+
|
355
378
|
sleep 0.05
|
356
379
|
end
|
357
380
|
end
|
data/lib/net/yail/IRCBot.rb
CHANGED
@@ -80,6 +80,12 @@ class IRCBot
|
|
80
80
|
|
81
81
|
# Enters the socket's listening loop(s)
|
82
82
|
def start_listening
|
83
|
+
# If socket's already dead (probably couldn't connect to server), don't
|
84
|
+
# try to listen!
|
85
|
+
if @irc.dead_socket
|
86
|
+
$stderr.puts "Dead socket, can't start listening!"
|
87
|
+
end
|
88
|
+
|
83
89
|
@irc.start_listening
|
84
90
|
end
|
85
91
|
|
@@ -90,7 +96,7 @@ class IRCBot
|
|
90
96
|
# processing.
|
91
97
|
def irc_loop
|
92
98
|
while true
|
93
|
-
until @irc.dead_socket
|
99
|
+
until @irc.dead_socket
|
94
100
|
sleep 15
|
95
101
|
@irc.handle(:irc_loop)
|
96
102
|
Thread.pass
|
@@ -185,12 +185,13 @@ module Defaults
|
|
185
185
|
report "*MOTD* End of MOTD"
|
186
186
|
end
|
187
187
|
|
188
|
-
# We dun connected to a server! Just sends
|
189
|
-
# quite "essential" to a working IRC app, but this data
|
190
|
-
# some point, so be careful before skipping this handler.
|
188
|
+
# We dun connected to a server! Just sends password (if one is set) and
|
189
|
+
# user/nick. This isn't quite "essential" to a working IRC app, but this data
|
190
|
+
# *must* be sent at some point, so be careful before skipping this handler.
|
191
191
|
def out_begin_connection(username, address, realname)
|
192
|
-
|
193
|
-
|
192
|
+
pass(@password) if @password
|
193
|
+
user(username, '0.0.0.0', address, realname)
|
194
|
+
nick(@nicknames[0])
|
194
195
|
end
|
195
196
|
|
196
197
|
# Incoming invitation
|
data/lib/net/yail/output_api.rb
CHANGED
@@ -18,6 +18,13 @@ module Net
|
|
18
18
|
# a library should own - protecting the programmer for having to remember that
|
19
19
|
# sort of crap, especially if the app is calling msg, act, ctcp, etc. in
|
20
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.
|
21
28
|
module IRCOutputAPI
|
22
29
|
# Spits a raw string out to the server - in case a subclass wants to do
|
23
30
|
# something special on *all* output, please make all output go through this
|
@@ -31,7 +38,7 @@ module IRCOutputAPI
|
|
31
38
|
# Calls :outgoing_privmsg handler, then sends a message (text) out to the
|
32
39
|
# given channel/user (target), and reports itself with the given string.
|
33
40
|
# This method shouldn't be called directly most of the time - just use msg,
|
34
|
-
# act,
|
41
|
+
# act, ctcp, etc.
|
35
42
|
#
|
36
43
|
# This is sort of the central message output - everything that's based on
|
37
44
|
# PRIVMSG (messages, actions, other ctcp) uses this. Because these messages
|
@@ -183,6 +190,8 @@ module IRCOutputAPI
|
|
183
190
|
raw "NICK :#{new_nick}"
|
184
191
|
end
|
185
192
|
|
193
|
+
# Identifies ourselves to the server. Calls :outgoing_user and sends raw
|
194
|
+
# USER command.
|
186
195
|
def user(username, myaddress, address, realname)
|
187
196
|
# Dup strings so handler can filter safely
|
188
197
|
username = username.dup
|
@@ -195,6 +204,87 @@ module IRCOutputAPI
|
|
195
204
|
raw "USER #{username} #{myaddress} #{address} :#{realname}"
|
196
205
|
end
|
197
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
|
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
|
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
|
256
|
+
server = server.dup
|
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
|
270
|
+
|
271
|
+
handle(:outgoing_invite, nick, channel)
|
272
|
+
raw "INVITE #{nick} #{channel}"
|
273
|
+
end
|
274
|
+
|
275
|
+
# Kicks the given user from the channel with the optional comment. Calls
|
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
|
281
|
+
|
282
|
+
handle(:outgoing_kick, nick, channel, comment)
|
283
|
+
output = "KICK #{channel} #{nick}"
|
284
|
+
output += " :#{comment}" unless comment.to_s.empty?
|
285
|
+
raw output
|
286
|
+
end
|
287
|
+
|
198
288
|
end
|
199
289
|
|
200
290
|
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.
|
4
|
+
version: 1.2.1
|
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: 2008-
|
12
|
+
date: 2008-08-01 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|