net-toc 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/net/toc.rb +570 -0
- metadata +38 -0
data/net/toc.rb
ADDED
@@ -0,0 +1,570 @@
|
|
1
|
+
# A small library that connects to AOL Instant Messenger using the TOC v2.0 protocol.
|
2
|
+
#
|
3
|
+
# Author:: Ian Henderson (mailto:ian@ianhenderson.org)
|
4
|
+
# Copyright:: Copyright (c) 2006 Ian Henderson
|
5
|
+
# License:: revised BSD license (http://www.opensource.org/licenses/bsd-license.php)
|
6
|
+
# Version:: 0.2
|
7
|
+
#
|
8
|
+
# See Net::TOC for documentation.
|
9
|
+
|
10
|
+
|
11
|
+
require 'socket'
|
12
|
+
|
13
|
+
module Net
|
14
|
+
# == Overview
|
15
|
+
# === Opening a Connection
|
16
|
+
# Pass Net::Toc.new your screenname and password to create a new connection.
|
17
|
+
# It will return a Client object, which is used to communicate with the server.
|
18
|
+
#
|
19
|
+
# client = Net::TOC.new("screenname", "p455w0rd")
|
20
|
+
#
|
21
|
+
# To actually connect, use Client#connect.
|
22
|
+
#
|
23
|
+
# client.connect
|
24
|
+
#
|
25
|
+
# If your program uses an input loop (e.g., reading from stdin), you can start it here.
|
26
|
+
# Otherwise, you must use Client#wait to prevent the program from exiting immediately.
|
27
|
+
#
|
28
|
+
# client.wait
|
29
|
+
#
|
30
|
+
# === Opening a Connection - The Shortcut
|
31
|
+
# If your program only sends IMs in response to received IMs, you can save yourself some code.
|
32
|
+
# Net::TOC.new takes an optional block argument, to be called each time a message arrives (it is passed to Client#on_im).
|
33
|
+
# Client#connect and Client#wait are automatically called.
|
34
|
+
#
|
35
|
+
# Net::TOC.new("screenname", "p455w0rd") do | message, buddy |
|
36
|
+
# # handle the im
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# === Receiving Events
|
40
|
+
# Client supports two kinds of event handlers: Client#on_im and Client#on_error.
|
41
|
+
#
|
42
|
+
# The given block will be called every time the event occurs.
|
43
|
+
# client.on_im do | message, buddy |
|
44
|
+
# puts "#{buddy.screen_name}: #{message}"
|
45
|
+
# end
|
46
|
+
# client.on_error do | error |
|
47
|
+
# puts "!! #{error}"
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# You can also receive events using Buddy#on_status.
|
51
|
+
# Pass it any number of statuses (e.g., :away, :offline, :available, :idle) and a block;
|
52
|
+
# the block will be called each time the buddy's status changes to one of the statuses.
|
53
|
+
#
|
54
|
+
# friend = client.buddy_list.buddy_named("friend")
|
55
|
+
# friend.on_status(:available) do
|
56
|
+
# friend.send_im "Hi!"
|
57
|
+
# end
|
58
|
+
# friend.on_status(:idle, :away) do
|
59
|
+
# friend.send_im "Bye!"
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# === Sending IMs
|
63
|
+
# To send an instant message, call Buddy#send_im.
|
64
|
+
#
|
65
|
+
# friend.send_im "Hello, #{friend.screen_name}!"
|
66
|
+
#
|
67
|
+
# === Status Changes
|
68
|
+
# You can modify your state using these Client methods: Client#go_away, Client#come_back, and Client#idle_time=.
|
69
|
+
#
|
70
|
+
# client.go_away "Away"
|
71
|
+
# client.idle_time = 600 # ten minutes
|
72
|
+
# client.come_back
|
73
|
+
# client.idle_time = 0 # stop being idle
|
74
|
+
#
|
75
|
+
# It is not necessary to call Client#idle_time= continuously; the server will automatically keep track.
|
76
|
+
#
|
77
|
+
# == Examples
|
78
|
+
# === Simple Bot
|
79
|
+
# This bot lets you run ruby commands remotely, but only if your screenname is in the authorized list.
|
80
|
+
#
|
81
|
+
# require 'net/toc'
|
82
|
+
# authorized = ["admin_screenname"]
|
83
|
+
# Net::TOC.new("screenname", "p455w0rd") do | message, buddy |
|
84
|
+
# if authorized.member? buddy.screen_name
|
85
|
+
# begin
|
86
|
+
# result = eval(message.chomp.gsub(/<[^>]+>/,"")) # remove html formatting
|
87
|
+
# buddy.send_im result.to_s if result.respond_to? :to_s
|
88
|
+
# rescue Exception => e
|
89
|
+
# buddy.send_im "#{e.class}: #{e}"
|
90
|
+
# end
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
# === (Slightly) More Complicated and Contrived Bot
|
94
|
+
# If you message this bot when you're available, you get a greeting and the date you logged in.
|
95
|
+
# If you message it when you're away, you get scolded, and then pestered each time you become available.
|
96
|
+
#
|
97
|
+
# require 'net/toc'
|
98
|
+
# client = Net::TOC.new("screenname", "p455w0rd")
|
99
|
+
# client.on_error do | error |
|
100
|
+
# admin = client.buddy_list.buddy_named("admin_screenname")
|
101
|
+
# admin.send_im("Error: #{error}")
|
102
|
+
# end
|
103
|
+
# client.on_im do | message, buddy, auto_response |
|
104
|
+
# return if auto_response
|
105
|
+
# if buddy.available?
|
106
|
+
# buddy.send_im("Hello, #{buddy.screen_name}. You have been logged in since #{buddy.last_signon}.")
|
107
|
+
# else
|
108
|
+
# buddy.send_im("Liar!")
|
109
|
+
# buddy.on_status(:available) { buddy.send_im("Welcome back, liar.") }
|
110
|
+
# end
|
111
|
+
# end
|
112
|
+
# client.connect
|
113
|
+
# client.wait
|
114
|
+
# === Simple Interactive Client
|
115
|
+
# Use screenname<<message to send message.
|
116
|
+
# <<message sends message to the last buddy you messaged.
|
117
|
+
# When somebody sends you a message, it is displayed as screenname>>message.
|
118
|
+
#
|
119
|
+
# require 'net/toc'
|
120
|
+
# print "screen name: "
|
121
|
+
# screen_name = gets.chomp
|
122
|
+
# print "password: "
|
123
|
+
# password = gets.chomp
|
124
|
+
#
|
125
|
+
# client = Net::TOC.new(screen_name, password)
|
126
|
+
#
|
127
|
+
# client.on_im do | message, buddy |
|
128
|
+
# puts "#{buddy}>>#{message}"
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# client.connect
|
132
|
+
#
|
133
|
+
# puts "connected"
|
134
|
+
#
|
135
|
+
# last_buddy = ""
|
136
|
+
# loop do
|
137
|
+
# buddy_name, message = *gets.chomp.split("<<",2)
|
138
|
+
#
|
139
|
+
# buddy_name = last_buddy if buddy_name == ""
|
140
|
+
#
|
141
|
+
# unless buddy_name.nil? or message.nil?
|
142
|
+
# last_buddy = buddy_name
|
143
|
+
# client.buddy_list.buddy_named(buddy_name).send_im(message)
|
144
|
+
# end
|
145
|
+
# end
|
146
|
+
module TOC
|
147
|
+
class CommunicationError < RuntimeError # :nodoc:
|
148
|
+
end
|
149
|
+
|
150
|
+
# Converts a screen name into its canonical form - lowercase, with no spaces.
|
151
|
+
def format_screen_name(screen_name)
|
152
|
+
screen_name.downcase.gsub(/\s+/, '')
|
153
|
+
end
|
154
|
+
|
155
|
+
# Escapes a message so it doesn't confuse the server. You should never have to call this directly.
|
156
|
+
def format_message(message) # :nodoc:
|
157
|
+
msg = message.gsub(/(\r|\n|\r\n)/, '<br>')
|
158
|
+
msg.gsub(/[{}\\"]/, "\\\\\\0") # oh dear
|
159
|
+
end
|
160
|
+
|
161
|
+
# Creates a new Client. See the Client.new method for details.
|
162
|
+
def self.new(screen_name, password, &optional_block) # :yields: message, buddy, auto_response, client
|
163
|
+
Client.new(screen_name, password, &optional_block)
|
164
|
+
end
|
165
|
+
|
166
|
+
Debug = false # :nodoc:
|
167
|
+
|
168
|
+
ErrorCode = {
|
169
|
+
901 => "<param> is not available.",
|
170
|
+
902 => "Warning <param> is not allowed.",
|
171
|
+
903 => "Message dropped; you are exceeding the server speed limit",
|
172
|
+
980 => "Incorrect screen name or password.",
|
173
|
+
981 => "The service is temporarily unavailable.",
|
174
|
+
982 => "Your warning level is too high to sign on.",
|
175
|
+
983 => "You have been connecting and disconnecting too frequently. Wait 10 minutes and try again.",
|
176
|
+
989 => "An unknown error has occurred in the signon process."
|
177
|
+
}
|
178
|
+
|
179
|
+
# The Connection class handles low-level communication using the TOC protocol. You shouldn't use it directly.
|
180
|
+
class Connection # :nodoc:
|
181
|
+
include TOC
|
182
|
+
|
183
|
+
def initialize(screen_name)
|
184
|
+
@user = format_screen_name screen_name
|
185
|
+
@msgseq = rand(100000)
|
186
|
+
end
|
187
|
+
|
188
|
+
def open(server="toc.oscar.aol.com", port=9898)
|
189
|
+
close
|
190
|
+
@sock = TCPSocket.new(server, port)
|
191
|
+
|
192
|
+
@sock.send "FLAPON\r\n\r\n", 0
|
193
|
+
|
194
|
+
toc_version = *recv.unpack("N")
|
195
|
+
|
196
|
+
send [1, 1, @user.length, @user].pack("Nnna*"), :sign_on
|
197
|
+
end
|
198
|
+
|
199
|
+
def close
|
200
|
+
@sock.close unless @sock.nil?
|
201
|
+
end
|
202
|
+
|
203
|
+
FrameType = {
|
204
|
+
:sign_on => 1,
|
205
|
+
:data => 2
|
206
|
+
}
|
207
|
+
|
208
|
+
def send(message, type=:data)
|
209
|
+
message << "\0"
|
210
|
+
puts " send: #{message}" if Debug
|
211
|
+
@msgseq = @msgseq.next
|
212
|
+
header = ['*', FrameType[type], @msgseq, message.length].pack("aCnn")
|
213
|
+
packet = header + message
|
214
|
+
@sock.send packet, 0
|
215
|
+
end
|
216
|
+
|
217
|
+
def recv
|
218
|
+
header = @sock.recv 6
|
219
|
+
raise CommunicationError, "Server didn't send full header." if header.length < 6
|
220
|
+
|
221
|
+
asterisk, type, serverseq, length = header.unpack "aCnn"
|
222
|
+
|
223
|
+
response = @sock.recv length
|
224
|
+
puts " recv: #{response}" if Debug
|
225
|
+
unless type == FrameType[:sign_on]
|
226
|
+
message, value = response.split(":", 2)
|
227
|
+
unless message.nil? or value.nil?
|
228
|
+
msg_sym = message.downcase.to_sym
|
229
|
+
yield msg_sym, value if block_given?
|
230
|
+
end
|
231
|
+
end
|
232
|
+
response
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
# Any unknown methods are assumed to be messages for the server.
|
238
|
+
def method_missing(command, *args)
|
239
|
+
send(([command] + args).join(" "))
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
class Buddy
|
244
|
+
include TOC
|
245
|
+
include Comparable
|
246
|
+
|
247
|
+
attr_reader :screen_name, :status, :warning_level, :last_signon, :idle_time
|
248
|
+
|
249
|
+
def initialize(screen_name, conn) # :nodoc:
|
250
|
+
@screen_name = screen_name
|
251
|
+
@conn = conn
|
252
|
+
@status = :offline
|
253
|
+
@warning_level = 0
|
254
|
+
@on_status = {}
|
255
|
+
@last_signon = :never
|
256
|
+
@idle_time = 0
|
257
|
+
end
|
258
|
+
|
259
|
+
def <=>(other) # :nodoc:
|
260
|
+
format_screen_name(@screen_name) <=> format_screen_name(other.screen_name)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Pass a block to be called when status changes to any of +statuses+. This replaces any previously set on_status block for these statuses.
|
264
|
+
def on_status(*statuses, &callback) #:yields:
|
265
|
+
statuses.each { | status | @on_status[status] = callback }
|
266
|
+
end
|
267
|
+
|
268
|
+
# Returns +true+ unless status == :offline.
|
269
|
+
def online?
|
270
|
+
status != :offline
|
271
|
+
end
|
272
|
+
|
273
|
+
# Returns +true+ if status == :available.
|
274
|
+
def available?
|
275
|
+
status == :available
|
276
|
+
end
|
277
|
+
|
278
|
+
# Returns +true+ if status == :away.
|
279
|
+
def away?
|
280
|
+
status == :away
|
281
|
+
end
|
282
|
+
|
283
|
+
# Returns +true+ if buddy is idle.
|
284
|
+
def idle?
|
285
|
+
@idle_time > 0
|
286
|
+
end
|
287
|
+
|
288
|
+
# Sends the instant message +message+ to the buddy. If +auto_response+ is true, the message is marked as an automated response.
|
289
|
+
def send_im(message, auto_response=false)
|
290
|
+
args = [format_screen_name(@screen_name), "\"" + format_message(message) + "\""]
|
291
|
+
args << "auto" if auto_response
|
292
|
+
@conn.toc_send_im *args
|
293
|
+
end
|
294
|
+
|
295
|
+
# Warns the buddy. If the argument is :anonymous, the buddy is warned anonymously. Otherwise, your name is sent with the warning.
|
296
|
+
# You may only warn buddies who have recently IMed you.
|
297
|
+
def warn(anon=:named)
|
298
|
+
@conn.toc_evil(format_screen_name(@screen_name), anon == :anonymous ? "anon" : "norm")
|
299
|
+
end
|
300
|
+
|
301
|
+
# The string representation of a buddy; equivalent to Buddy#screen_name.
|
302
|
+
def to_s
|
303
|
+
screen_name
|
304
|
+
end
|
305
|
+
|
306
|
+
def raw_update(val) # :nodoc:
|
307
|
+
# TODO: Support user types properly.
|
308
|
+
name, online, warning, signon_time, idle, user_type = *val.split(":")
|
309
|
+
@warning_level = warning.to_i
|
310
|
+
@last_signon = Time.at(signon_time.to_i)
|
311
|
+
@idle_time = idle.to_i
|
312
|
+
if online == "F"
|
313
|
+
update_status :offline
|
314
|
+
elsif user_type[2...3] and user_type[2...3] == "U"
|
315
|
+
update_status :away
|
316
|
+
elsif @idle_time > 0
|
317
|
+
update_status :idle
|
318
|
+
else
|
319
|
+
update_status :available
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
private
|
324
|
+
|
325
|
+
def update_status(status)
|
326
|
+
if @on_status[status] and status != @status
|
327
|
+
@status = status
|
328
|
+
@on_status[status].call
|
329
|
+
else
|
330
|
+
@status = status
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
# Manages groups and buddies. Don't create one yourself - get one using Client#buddy_list.
|
336
|
+
class BuddyList
|
337
|
+
include TOC
|
338
|
+
|
339
|
+
def initialize(conn) # :nodoc:
|
340
|
+
@conn = conn
|
341
|
+
@buddies = {}
|
342
|
+
@groups = {}
|
343
|
+
@group_order = []
|
344
|
+
end
|
345
|
+
|
346
|
+
# Constructs a printable string representation of the buddy list.
|
347
|
+
def to_s
|
348
|
+
s = ""
|
349
|
+
each_group do | group, buddies |
|
350
|
+
s << "== #{group} ==\n"
|
351
|
+
buddies.each do | buddy |
|
352
|
+
s << " * #{buddy}\n"
|
353
|
+
end
|
354
|
+
end
|
355
|
+
s
|
356
|
+
end
|
357
|
+
|
358
|
+
# Calls the passed block once for each group, passing the group name and the list of buddies as parameters.
|
359
|
+
def each_group
|
360
|
+
@group_order.each do | group |
|
361
|
+
buddies = @groups[group]
|
362
|
+
yield group, buddies
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
# Adds a new group named +group_name+.
|
367
|
+
# Setting +sync+ to :dont_sync will prevent this change from being sent to the server.
|
368
|
+
def add_group(group_name, sync=:sync)
|
369
|
+
if @groups[group_name].nil?
|
370
|
+
@groups[group_name] = []
|
371
|
+
@group_order << group_name
|
372
|
+
@conn.toc2_new_group group_name if sync == :sync
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
# Adds the buddy named +buddy_name+ to the group named +group+. If this group does not exist, it is created.
|
377
|
+
# Setting +sync+ to :dont_sync will prevent this change from being sent to the server.
|
378
|
+
def add_buddy(group, buddy_name, sync=:sync)
|
379
|
+
add_group(group, sync) if @groups[group].nil?
|
380
|
+
@groups[group] << buddy_named(buddy_name)
|
381
|
+
@conn.toc2_new_buddies("{g:#{group}\nb:#{format_screen_name(buddy_name)}\n}") if sync == :sync
|
382
|
+
end
|
383
|
+
|
384
|
+
# Removes the buddy named +buddy_name+ from the group named +group+.
|
385
|
+
# Setting +sync+ to :dont_sync will prevent this change from being sent to the server.
|
386
|
+
def remove_buddy(group, buddy_name, sync=:sync)
|
387
|
+
unless @groups[group].nil?
|
388
|
+
buddy = buddy_named(buddy_name)
|
389
|
+
@groups[group].reject! { | b | b == buddy }
|
390
|
+
@conn.toc2_remove_buddy(format_screen_name(buddy_name), group) if sync == :sync
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
# Returns the buddy named +name+. If the buddy does not exist, it is created. +name+ is not case- or whitespace-sensitive.
|
395
|
+
def buddy_named(name)
|
396
|
+
formatted_name = format_screen_name(name)
|
397
|
+
buddy = @buddies[formatted_name]
|
398
|
+
if buddy.nil?
|
399
|
+
buddy = Buddy.new(name, @conn)
|
400
|
+
@buddies[formatted_name] = buddy
|
401
|
+
end
|
402
|
+
buddy
|
403
|
+
end
|
404
|
+
|
405
|
+
# Decodes the buddy list from raw CONFIG data.
|
406
|
+
def decode_toc(val) # :nodoc:
|
407
|
+
current_group = nil
|
408
|
+
val.each_line do | line |
|
409
|
+
letter, name = *line.split(":")
|
410
|
+
name = name.chomp
|
411
|
+
case letter
|
412
|
+
when "g"
|
413
|
+
add_group(name, :dont_sync)
|
414
|
+
current_group = name
|
415
|
+
when "b"
|
416
|
+
add_buddy(current_group, name, :dont_sync)
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
# A high-level interface to TOC. It supports asynchronous message handling through the use of threads, and maintains a list of buddies.
|
423
|
+
class Client
|
424
|
+
include TOC
|
425
|
+
|
426
|
+
attr_reader :buddy_list, :screen_name
|
427
|
+
|
428
|
+
# You must initialize the client with your screen name and password.
|
429
|
+
# If a block is given, Client#listen will be invoked with the block after initialization.
|
430
|
+
def initialize(screen_name, password, &optional_block) # :yields: message, buddy, auto_response, client
|
431
|
+
@conn = Connection.new(screen_name)
|
432
|
+
@screen_name = format_screen_name(screen_name)
|
433
|
+
@password = password
|
434
|
+
@callbacks = {}
|
435
|
+
@buddy_list = BuddyList.new(@conn)
|
436
|
+
add_callback(:config, :config2) { |v| @buddy_list.decode_toc v }
|
437
|
+
add_callback(:update_buddy, :update_buddy2) { |v| update_buddy v }
|
438
|
+
on_error do | error |
|
439
|
+
$stderr.puts "Error: #{error}"
|
440
|
+
end
|
441
|
+
listen(&optional_block) if block_given?
|
442
|
+
end
|
443
|
+
|
444
|
+
# Connects to the server and starts an event-handling thread.
|
445
|
+
def connect(server="toc.oscar.aol.com", port=9898, oscar_server="login.oscar.aol.com", oscar_port=5190)
|
446
|
+
@conn.open(server, port)
|
447
|
+
code = 7696 * @screen_name[0] * @password[0]
|
448
|
+
@conn.toc2_signon(oscar_server, oscar_port, @screen_name, roasted_pass, "english", "\"TIC:toc.rb\"", 160, code)
|
449
|
+
|
450
|
+
@conn.recv do |msg, val|
|
451
|
+
if msg == :sign_on
|
452
|
+
@conn.toc_add_buddy(@screen_name)
|
453
|
+
@conn.toc_init_done
|
454
|
+
capabilities.each do |capability|
|
455
|
+
@conn.toc_set_caps(capability)
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
@thread.kill unless @thread.nil? # ha
|
460
|
+
@thread = Thread.new { loop { event_loop } }
|
461
|
+
end
|
462
|
+
|
463
|
+
# Disconnects and kills the event-handling thread. You may still add callbacks while disconnected.
|
464
|
+
def disconnect
|
465
|
+
@thread.kill unless @thread.nil?
|
466
|
+
@thread = nil
|
467
|
+
@conn.close
|
468
|
+
end
|
469
|
+
|
470
|
+
# Connects to the server and forwards received IMs to the given block. See Client#connect for the arguments.
|
471
|
+
def listen(*args) # :yields: message, buddy, auto_response, client
|
472
|
+
on_im do | message, buddy, auto_response |
|
473
|
+
yield message, buddy, auto_response, self
|
474
|
+
end
|
475
|
+
connect(*args)
|
476
|
+
wait
|
477
|
+
end
|
478
|
+
|
479
|
+
# Pass a block to be called every time an IM is received. This will replace any previous on_im handler.
|
480
|
+
def on_im
|
481
|
+
raise ArgumentException, "on_im requires a block argument" unless block_given?
|
482
|
+
add_callback(:im_in, :im_in2) do |val|
|
483
|
+
screen_name, auto, f2, *message = *val.split(":")
|
484
|
+
message = message.join(":")
|
485
|
+
buddy = @buddy_list.buddy_named(screen_name)
|
486
|
+
auto_response = auto == "T"
|
487
|
+
yield message, buddy, auto_response
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
# Pass a block to be called every time an error occurs. This will replace any previous on_error handler, including the default exception-raising behavior.
|
492
|
+
def on_error
|
493
|
+
raise ArgumentException, "on_error requires a block argument" unless block_given?
|
494
|
+
add_callback(:error) do |val|
|
495
|
+
code, param = *val.split(":")
|
496
|
+
error = ErrorCode[code.to_i]
|
497
|
+
error = "An unknown error occurred." if error.nil?
|
498
|
+
error.gsub!("<param>", param) unless param.nil?
|
499
|
+
yield error
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
# Sets your status to away and +away_message+ as your away message.
|
504
|
+
def go_away(away_message)
|
505
|
+
@conn.toc_set_away "\"#{away_message.gsub("\"","\\\"")}\""
|
506
|
+
end
|
507
|
+
|
508
|
+
# Sets your status to available.
|
509
|
+
def come_back
|
510
|
+
@conn.toc_set_away
|
511
|
+
end
|
512
|
+
|
513
|
+
# Sets your idle time in seconds. You only need to set this once; afterwards, the server will keep track itself.
|
514
|
+
# Set to 0 to stop being idle.
|
515
|
+
def idle_time=(seconds)
|
516
|
+
@conn.toc_set_idle seconds
|
517
|
+
end
|
518
|
+
|
519
|
+
# Waits for the event-handling thread for +limit+ seconds, or indefinitely if no argument is given. Use this to prevent your program from exiting prematurely.
|
520
|
+
# For example, the following script will exit right after connecting:
|
521
|
+
# client = Net::TOC.new("screenname", "p455w0rd")
|
522
|
+
# client.connect
|
523
|
+
# To prevent this, use wait:
|
524
|
+
# client = Net::TOC.new("screenname", "p455w0rd")
|
525
|
+
# client.connect
|
526
|
+
# client.wait
|
527
|
+
# Now the program will wait until the client has disconnected before exiting.
|
528
|
+
def wait(limit=nil)
|
529
|
+
@thread.join limit
|
530
|
+
end
|
531
|
+
|
532
|
+
# Returns a list of this client's capabilities. Not yet implemented.
|
533
|
+
def capabilities
|
534
|
+
[] # TODO
|
535
|
+
end
|
536
|
+
|
537
|
+
private
|
538
|
+
|
539
|
+
# Returns an "encrypted" version of the password to be sent across the internet.
|
540
|
+
# Decrypting it is trivial, though.
|
541
|
+
def roasted_pass
|
542
|
+
tictoc = "Tic/Toc".unpack "c*"
|
543
|
+
pass = @password.unpack "c*"
|
544
|
+
roasted = "0x"
|
545
|
+
pass.each_index do |i|
|
546
|
+
roasted << sprintf("%02x", pass[i] ^ tictoc[i % tictoc.length])
|
547
|
+
end
|
548
|
+
roasted
|
549
|
+
end
|
550
|
+
|
551
|
+
def update_buddy(val)
|
552
|
+
screen_name = val.split(":").first.chomp
|
553
|
+
buddy = @buddy_list.buddy_named(screen_name)
|
554
|
+
buddy.raw_update(val)
|
555
|
+
end
|
556
|
+
|
557
|
+
def add_callback(*callbacks, &block)
|
558
|
+
callbacks.each do |callback|
|
559
|
+
@callbacks[callback] = block;
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
def event_loop
|
564
|
+
@conn.recv do |msg, val|
|
565
|
+
@callbacks[msg].call(val) unless @callbacks[msg].nil?
|
566
|
+
end
|
567
|
+
end
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end
|
metadata
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.10
|
3
|
+
specification_version: 1
|
4
|
+
name: net-toc
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.2"
|
7
|
+
date: 2006-08-02
|
8
|
+
summary: "A ruby library which uses the TOC protocol to connect to AOL's instant messaging
|
9
|
+
network."
|
10
|
+
require_paths:
|
11
|
+
- "."
|
12
|
+
email: ian@ianhenderson.org
|
13
|
+
homepage: http://net-toc.rubyforge.org
|
14
|
+
rubyforge_project: net-toc
|
15
|
+
description:
|
16
|
+
autorequire:
|
17
|
+
default_executable:
|
18
|
+
bindir: bin
|
19
|
+
has_rdoc: true
|
20
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
21
|
+
requirements:
|
22
|
+
-
|
23
|
+
- ">"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 0.0.0
|
26
|
+
version:
|
27
|
+
platform: ruby
|
28
|
+
authors:
|
29
|
+
- Ian Henderson
|
30
|
+
files:
|
31
|
+
- net/toc.rb
|
32
|
+
test_files: []
|
33
|
+
rdoc_options: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
requirements: []
|
38
|
+
dependencies: []
|