camper_van 0.0.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/.gitignore +3 -0
- data/.rvmrc +60 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +52 -0
- data/Guardfile +6 -0
- data/LICENSE +19 -0
- data/README.md +73 -0
- data/Rakefile +10 -0
- data/bin/camper_van +60 -0
- data/camper_van.gemspec +27 -0
- data/lib/camper_van/channel.rb +390 -0
- data/lib/camper_van/command_definition.rb +70 -0
- data/lib/camper_van/command_parser.rb +43 -0
- data/lib/camper_van/debug_proxy.rb +62 -0
- data/lib/camper_van/ircd.rb +340 -0
- data/lib/camper_van/logger.rb +10 -0
- data/lib/camper_van/server.rb +105 -0
- data/lib/camper_van/server_reply.rb +94 -0
- data/lib/camper_van/user.rb +59 -0
- data/lib/camper_van/utils.rb +22 -0
- data/lib/camper_van/version.rb +3 -0
- data/lib/camper_van.rb +28 -0
- data/spec/camper_van/channel_spec.rb +433 -0
- data/spec/camper_van/command_definition_spec.rb +54 -0
- data/spec/camper_van/command_parser_spec.rb +40 -0
- data/spec/camper_van/ircd_spec.rb +208 -0
- data/spec/camper_van/server_reply_spec.rb +66 -0
- data/spec/camper_van/server_spec.rb +43 -0
- data/spec/camper_van/user_spec.rb +40 -0
- data/spec/spec_helper.rb +13 -0
- metadata +141 -0
@@ -0,0 +1,390 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module CamperVan
|
6
|
+
class Channel
|
7
|
+
|
8
|
+
# The irc channel name this channel instance is for
|
9
|
+
attr_reader :channel
|
10
|
+
|
11
|
+
# The connected irc client connection
|
12
|
+
attr_reader :client
|
13
|
+
|
14
|
+
# The campfire room to which this channel is connected
|
15
|
+
attr_reader :room
|
16
|
+
|
17
|
+
# Accessor for the EM http request representing the live stream from
|
18
|
+
# the campfire api
|
19
|
+
attr_reader :stream
|
20
|
+
|
21
|
+
# Accessor for hash of known users in the room/channel
|
22
|
+
# Kept up to date by update_users command, as well as Join/Leave
|
23
|
+
# campfire events.
|
24
|
+
attr_reader :users
|
25
|
+
|
26
|
+
include Utils
|
27
|
+
include Logger
|
28
|
+
|
29
|
+
# Public: create a new campfire channel
|
30
|
+
#
|
31
|
+
# channel - name of the channel we're joining
|
32
|
+
# client - the EM::Connection representing the irc client
|
33
|
+
# room - the campfire room we're joining
|
34
|
+
def initialize(channel, client, room)
|
35
|
+
@channel, @client, @room = channel, client, room
|
36
|
+
@users = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: Joins a campfire room and sends the necessary topic
|
40
|
+
# and name list messages back to the IRC client.
|
41
|
+
#
|
42
|
+
# Returns true if the join was successful,
|
43
|
+
# false if the room was full or locked.
|
44
|
+
def join
|
45
|
+
if room.locked?
|
46
|
+
numeric_reply :err_inviteonlychan, "Cannot join #{channel} (locked)"
|
47
|
+
return false
|
48
|
+
|
49
|
+
elsif room.full?
|
50
|
+
numeric_reply :err_channelisfull, "Cannot join #{channel} (full)"
|
51
|
+
return false
|
52
|
+
|
53
|
+
else
|
54
|
+
update_users do
|
55
|
+
# join channel
|
56
|
+
client.user_reply :join, ":#{channel}"
|
57
|
+
|
58
|
+
# current topic
|
59
|
+
client.numeric_reply :rpl_topic, channel, ':' + room.topic
|
60
|
+
|
61
|
+
# List the current users, which must always include myself
|
62
|
+
# (race condition, server may not realize the user has joined yet)
|
63
|
+
nicks = users.values.map { |u| u.nick }
|
64
|
+
nicks.unshift client.nick unless nicks.include? client.nick
|
65
|
+
|
66
|
+
nicks.each_slice(10) do |list|
|
67
|
+
client.numeric_reply :rpl_namereply, "=", channel, ":#{list.join ' '}"
|
68
|
+
end
|
69
|
+
client.numeric_reply :rpl_endofnames, channel, "End of /NAMES list."
|
70
|
+
|
71
|
+
# begin streaming the channel events (joins room implicitly)
|
72
|
+
stream_campfire_to_channel
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
# Public: "leaves" a campfire room, per the PART irc command.
|
80
|
+
# Confirms with the connected client to PART the channel.
|
81
|
+
#
|
82
|
+
# Does not actually leave the campfire room, just closes out the campfire
|
83
|
+
# connections, so the server can idle the connection out. This behavior
|
84
|
+
# was chosen so periodic joins/parts wouldn't spam the campfire rooms
|
85
|
+
# unnecessarily, and also to reflect how Propane et. al. treat open
|
86
|
+
# connections: allowing them to time out rather than leaving explicitly.
|
87
|
+
def part
|
88
|
+
client.user_reply :part, channel
|
89
|
+
stream.close_connection if stream
|
90
|
+
# room.leave # let the timeout do it rather than being explicit!
|
91
|
+
end
|
92
|
+
|
93
|
+
# Public: replies to a WHO command with a list of users for a campfire room,
|
94
|
+
# including their nicks, names, and status.
|
95
|
+
#
|
96
|
+
# For WHO response: http://www.mircscripts.org/forums.php?cid=3&id=159227
|
97
|
+
# In short, H = here, G = away, append @ for chanops (admins)
|
98
|
+
def list_users
|
99
|
+
update_users(:include_joins_and_parts) do
|
100
|
+
users.values.each do |user|
|
101
|
+
status = (user.idle? ? "G" : "H") + (user.admin? ? "@" : "")
|
102
|
+
client.numeric_reply :rpl_whoreply, channel, user.account, user.server,
|
103
|
+
"camper_van", user.nick, status, ":0 #{user.name}"
|
104
|
+
end
|
105
|
+
client.numeric_reply :rpl_endofwho, channel, "End of WHO list"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Public: accepts an IRC PRIVMSG and converts it to an appropriate
|
110
|
+
# campfire text message for the room.
|
111
|
+
#
|
112
|
+
# msg - the IRC PRIVMSG message contents
|
113
|
+
#
|
114
|
+
# TODO: substitute "nick: " with the nick's campfire name instead
|
115
|
+
def privmsg(msg)
|
116
|
+
|
117
|
+
# convert twitter urls to tweets
|
118
|
+
if msg =~ %r(^https://twitter.com/(\w+)/status/(\d+)$)
|
119
|
+
room.tweet(msg) { } # async, no-op callback
|
120
|
+
else
|
121
|
+
# convert ACTIONs
|
122
|
+
msg.sub! /^\01ACTION (.*)\01$/, '*\1*'
|
123
|
+
|
124
|
+
if matched = users.values.detect {|u| msg.start_with?(u.nick + ': ')}
|
125
|
+
msg = msg.sub(/^#{matched.nick}/, matched.name)
|
126
|
+
end
|
127
|
+
|
128
|
+
room.text(msg) { } # async, no-op callback
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
# Public: sends the current channel mode to the client
|
134
|
+
def current_mode
|
135
|
+
n = room.membership_limit
|
136
|
+
client.numeric_reply :rpl_channelmodeis, channel, current_mode_string, n
|
137
|
+
end
|
138
|
+
|
139
|
+
# Public: set the mode on the campfire channel, mapping from the provided
|
140
|
+
# IRC chanmode to the campfire setting.
|
141
|
+
#
|
142
|
+
# mode - the IRC mode flag change. Must be one of:
|
143
|
+
# "+i" - lock room
|
144
|
+
# "-i" - unlock room
|
145
|
+
#
|
146
|
+
# TODO support these when the firering client does:
|
147
|
+
# "+s" - disable guest access
|
148
|
+
# "-s" - enable guest access
|
149
|
+
#
|
150
|
+
# Returns nothing, but lets the client know the results of the call. Sends
|
151
|
+
# an error to the client for an invalid mode string.
|
152
|
+
def set_mode(mode)
|
153
|
+
case mode
|
154
|
+
# when "+s"
|
155
|
+
# when "-s"
|
156
|
+
when "+i"
|
157
|
+
room.lock
|
158
|
+
room.locked = true
|
159
|
+
client.user_reply :mode, channel,
|
160
|
+
current_mode_string, room.membership_limit
|
161
|
+
when "-i"
|
162
|
+
room.unlock
|
163
|
+
room.locked = false
|
164
|
+
client.user_reply :mode, channel,
|
165
|
+
current_mode_string, room.membership_limit
|
166
|
+
else
|
167
|
+
client.numeric_reply :err_unknownmode,
|
168
|
+
"is unknown mode char to me for #{channel}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns the current mode string
|
173
|
+
def current_mode_string
|
174
|
+
n = room.membership_limit
|
175
|
+
s = room.open_to_guests? ? "" : "s"
|
176
|
+
i = room.locked? ? "i" : ""
|
177
|
+
"+#{i}l#{s}"
|
178
|
+
end
|
179
|
+
|
180
|
+
# Public: returns the current topic of the campfire room
|
181
|
+
def current_topic
|
182
|
+
client.numeric_reply :rpl_topic, channel, ':' + room.topic
|
183
|
+
end
|
184
|
+
|
185
|
+
# Public: set the topic of the campfire room to the given string
|
186
|
+
# and lets the irc client know about the change
|
187
|
+
#
|
188
|
+
# topic - the new topic
|
189
|
+
def set_topic(topic)
|
190
|
+
room.update("topic" => topic) do
|
191
|
+
room.topic = topic
|
192
|
+
client.numeric_reply :rpl_topic, channel, ':' + room.topic
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Get the list of users from a room, and update the internal
|
197
|
+
# tracking state as well as the connected client. If the user list
|
198
|
+
# is out of sync, the irc client may receive the associated
|
199
|
+
# JOIN/PART commands.
|
200
|
+
#
|
201
|
+
# include_joins_and_parts - whether or not to include JOIN/PART commands if
|
202
|
+
# the user list has changed since the last update
|
203
|
+
# (defaults to false)
|
204
|
+
# callback - optional callback after the users have been
|
205
|
+
# updated
|
206
|
+
#
|
207
|
+
# Returns nothing, but keeps the users list updated
|
208
|
+
def update_users(include_joins_and_parts=false, &callback)
|
209
|
+
room.users do |user_list|
|
210
|
+
before = users.dup
|
211
|
+
present = {}
|
212
|
+
|
213
|
+
user_list.each do |user|
|
214
|
+
if before[user.id]
|
215
|
+
present[user.id] = before.delete user.id
|
216
|
+
# if present[user.id].nick != nick
|
217
|
+
# # NICK CHANGE
|
218
|
+
# present[user.id].nick = nick
|
219
|
+
# end
|
220
|
+
else
|
221
|
+
new_user = present[user.id] = User.new(user)
|
222
|
+
if include_joins_and_parts
|
223
|
+
client.campfire_reply :join, new_user.nick, channel
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Now that the list of users is updated, the remaining users
|
229
|
+
# in 'before' have left. Let the irc client know.
|
230
|
+
before.each do |id, user|
|
231
|
+
if include_joins_and_parts
|
232
|
+
client.campfire_reply :part, user.nick, channel
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
@users = present
|
237
|
+
|
238
|
+
callback.call if callback
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# Stream messages from campfire and map them to IRC commands for the
|
243
|
+
# connected client.
|
244
|
+
def stream_campfire_to_channel
|
245
|
+
@stream = room.stream do |message|
|
246
|
+
map_message_to_irc message
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Map a campfire message to one or more IRC commands for the client
|
251
|
+
#
|
252
|
+
# message - the campfire message to map to IRC
|
253
|
+
#
|
254
|
+
# Returns nothing, but responds according to the message
|
255
|
+
def map_message_to_irc(message)
|
256
|
+
user_for_message(message) do |message, user|
|
257
|
+
|
258
|
+
# needed in most cases
|
259
|
+
name = user ? irc_name(user.name) : nil
|
260
|
+
|
261
|
+
if %w(Text Tweet Sound Paste Upload).include?(
|
262
|
+
message.type.sub(/Message$/, '')) && name == client.nick
|
263
|
+
logger.debug "skipping message from myself: #{message.type} #{message.body}"
|
264
|
+
return
|
265
|
+
end
|
266
|
+
|
267
|
+
# strip Message off the type to simplify readability
|
268
|
+
case message.type.sub(/Message$/,'')
|
269
|
+
|
270
|
+
when "Timestamp", "Advertisement"
|
271
|
+
# ignore these
|
272
|
+
|
273
|
+
when "Lock"
|
274
|
+
client.campfire_reply :mode, name, channel, "+i"
|
275
|
+
|
276
|
+
when "Unlock"
|
277
|
+
client.campfire_reply :mode, name, channel, "-i"
|
278
|
+
|
279
|
+
when "DisallowGuests"
|
280
|
+
name = irc_name(user.name)
|
281
|
+
client.campfire_reply :mode, name, channel, "+s"
|
282
|
+
|
283
|
+
when "AllowGuests"
|
284
|
+
name = irc_name(user.name)
|
285
|
+
client.campfire_reply :mode, name, channel, "-s"
|
286
|
+
|
287
|
+
when "Idle"
|
288
|
+
if u = users[user.id]
|
289
|
+
u.idle = true
|
290
|
+
end
|
291
|
+
|
292
|
+
when "Unidle"
|
293
|
+
if u = users[user.id]
|
294
|
+
u.idle = false
|
295
|
+
end
|
296
|
+
|
297
|
+
when "Enter"
|
298
|
+
client.campfire_reply :join, name, channel
|
299
|
+
users[user.id] = User.new(user)
|
300
|
+
|
301
|
+
when "Leave", "Kick" # kick is used for idle timeouts
|
302
|
+
client.campfire_reply :part, name, channel, "Leaving..."
|
303
|
+
users.delete user.id
|
304
|
+
|
305
|
+
when "Paste"
|
306
|
+
lines = message.body.split("\n")
|
307
|
+
|
308
|
+
lines[0..2].each do |line|
|
309
|
+
client.campfire_reply :privmsg, name, channel, "> " + line
|
310
|
+
end
|
311
|
+
|
312
|
+
if lines.size > 3
|
313
|
+
client.campfire_reply :privmsg, name, channel, "> more: " +
|
314
|
+
"https://#{client.subdomain}.campfirenow.com/room/#{room.id}/paste/#{message.id}"
|
315
|
+
end
|
316
|
+
|
317
|
+
when "Sound"
|
318
|
+
text = case message.body
|
319
|
+
when "crickets"
|
320
|
+
"hears crickets chirping"
|
321
|
+
when "rimshot"
|
322
|
+
"plays a rimshot"
|
323
|
+
when "trombone"
|
324
|
+
"plays a sad trombone"
|
325
|
+
when "vuvuzela"
|
326
|
+
"======<() ~ ♪ ~♫"
|
327
|
+
else
|
328
|
+
"played a #{message.body} sound"
|
329
|
+
end
|
330
|
+
|
331
|
+
client.campfire_reply :privmsg, name, channel, "\x01ACTION #{text}\x01"
|
332
|
+
|
333
|
+
# when "System"
|
334
|
+
# # NOTICE from :camper_van to channel?
|
335
|
+
|
336
|
+
when "Text"
|
337
|
+
if message.body =~ /^\*.*\*$/
|
338
|
+
client.campfire_reply :privmsg, name, channel, ":\01ACTION " + message.body[1..-2] + "\01"
|
339
|
+
else
|
340
|
+
matched = users.values.detect {|u| message.body.start_with?(u.name + ': ')}
|
341
|
+
if matched
|
342
|
+
body = message.body.sub(/^#{matched.name}/, matched.nick)
|
343
|
+
else
|
344
|
+
body = message.body
|
345
|
+
end
|
346
|
+
client.campfire_reply :privmsg, name, channel, body
|
347
|
+
end
|
348
|
+
|
349
|
+
when "TopicChange"
|
350
|
+
client.campfire_reply :topic, name, channel, message.body
|
351
|
+
room.topic = message.body
|
352
|
+
# client.numeric_reply :rpl_topic, channel, ':' + message.body
|
353
|
+
|
354
|
+
when "Upload"
|
355
|
+
client.campfire_reply :privmsg, name, channel, ":\01ACTION uploaded " +
|
356
|
+
"https://#{client.subdomain}.campfirenow.com/room/#{room.id}/uploads/#{message.id}/#{message.body}"
|
357
|
+
|
358
|
+
when "Tweet"
|
359
|
+
# stringify keys since campfire API is inconsistent about it
|
360
|
+
tweet = stringify_keys(YAML.load(message.body))
|
361
|
+
client.campfire_reply :privmsg, name, channel,
|
362
|
+
"@#{tweet["author_username"]}: #{tweet["message"]}" +
|
363
|
+
" (https://twitter.com/#{tweet["author_username"]}" +
|
364
|
+
"/status/#{tweet["id"]})"
|
365
|
+
|
366
|
+
else
|
367
|
+
logger.warn "unknown message #{message.type}: #{message.body}"
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# Retrieve the user from a message, either by finding it in the current
|
373
|
+
# list of known users, or by asking campfire for the user.
|
374
|
+
#
|
375
|
+
# message - the message for which to look up the user
|
376
|
+
#
|
377
|
+
# Yields the message and the user associated with the message
|
378
|
+
def user_for_message(message)
|
379
|
+
if user = users[message.user_id]
|
380
|
+
yield message, user
|
381
|
+
else
|
382
|
+
message.user do |user|
|
383
|
+
yield message, user
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module CamperVan
|
2
|
+
class HandlerMissing < StandardError
|
3
|
+
attr_reader :command
|
4
|
+
def initialize(command)
|
5
|
+
@command = command
|
6
|
+
@message = "no handler for the #{command.keys.first} command"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module CommandDefinition
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
base.module_eval { include InstanceMethods }
|
14
|
+
base.extend ClassMethods
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
|
19
|
+
# Public: defines a handler for the given irc command
|
20
|
+
#
|
21
|
+
# command - the irc command to define a handler for
|
22
|
+
#
|
23
|
+
# Example:
|
24
|
+
#
|
25
|
+
# handle :nick do |args|
|
26
|
+
# # ... change nickname to ...
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# ```
|
30
|
+
# def handle_nick(*args)
|
31
|
+
# # contents of block
|
32
|
+
# end
|
33
|
+
# ```
|
34
|
+
def handle(command, &block)
|
35
|
+
define_method "handle_#{command}".to_sym, &block
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module InstanceMethods
|
40
|
+
|
41
|
+
# Public: handles the given command using the handler method
|
42
|
+
# defined by the class-level handler metaprogramming, if it
|
43
|
+
# exists.
|
44
|
+
#
|
45
|
+
# command - the Hash command as provided by the irc command parser
|
46
|
+
#
|
47
|
+
# Example:
|
48
|
+
#
|
49
|
+
# handle :nick => ["joe"]
|
50
|
+
#
|
51
|
+
# Raises CamperVan::HandlerMissing if there is no handler method
|
52
|
+
# defined for the given command.
|
53
|
+
def handle(command)
|
54
|
+
name, args = command.to_a.first
|
55
|
+
method_name = "handle_#{name}".to_sym
|
56
|
+
if self.respond_to? method_name
|
57
|
+
m = method(method_name)
|
58
|
+
if m.arity > 0
|
59
|
+
send method_name, args
|
60
|
+
else
|
61
|
+
send method_name
|
62
|
+
end
|
63
|
+
else
|
64
|
+
raise CamperVan::HandlerMissing, command
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module CamperVan
|
2
|
+
|
3
|
+
# simplistic IRC command parser
|
4
|
+
module CommandParser
|
5
|
+
|
6
|
+
# returns hash, e.g.
|
7
|
+
#
|
8
|
+
# malformed # => nil
|
9
|
+
# NICK joe # => # { :nick => ["joe"] }
|
10
|
+
# LIST # => # { :list => [] }
|
11
|
+
# PRIVMSG #foo :test # => { :privmsg => ['#foo', 'test'] }
|
12
|
+
#
|
13
|
+
def parse(line)
|
14
|
+
line = line.dup
|
15
|
+
match = /^([A-Z]+)(\b|$)/.match(line)
|
16
|
+
cmd = match && match[0]
|
17
|
+
|
18
|
+
return nil unless cmd
|
19
|
+
|
20
|
+
# strip off the command and any whitespace
|
21
|
+
line.sub! /^#{cmd}\s*/, ""
|
22
|
+
|
23
|
+
args = []
|
24
|
+
until line.empty? do
|
25
|
+
line =~ /^(\S+)(\s|$)/
|
26
|
+
if $1
|
27
|
+
if $1.start_with?(":")
|
28
|
+
args << line[1..-1]
|
29
|
+
break
|
30
|
+
else
|
31
|
+
args << $1
|
32
|
+
line = line[$1.size..-1]
|
33
|
+
line = line.sub(/^\s+/,"")
|
34
|
+
end
|
35
|
+
else
|
36
|
+
break
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
return {cmd.downcase.to_sym => args }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# debug proxy for dumping all irc traffic between a client and a server
|
2
|
+
module CamperVan
|
3
|
+
class DebugProxy < EM::Connection
|
4
|
+
include EM::Protocols::LineText2
|
5
|
+
|
6
|
+
def self.run(server, server_port=6667)
|
7
|
+
EM.run do
|
8
|
+
EM.start_server "localhost", 6667, DebugProxy, server, server_port
|
9
|
+
puts "* waiting for connections..."
|
10
|
+
|
11
|
+
trap("INT") do
|
12
|
+
puts "* shutting down"
|
13
|
+
EM.stop
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Server < EM::Connection
|
19
|
+
include EM::Protocols::LineText2
|
20
|
+
|
21
|
+
attr_reader :client
|
22
|
+
|
23
|
+
def initialize(client)
|
24
|
+
super
|
25
|
+
@lt2_delimiter = "\r\n"
|
26
|
+
@client = client
|
27
|
+
end
|
28
|
+
|
29
|
+
def post_init
|
30
|
+
puts "* established connection to server"
|
31
|
+
end
|
32
|
+
|
33
|
+
def receive_line(line)
|
34
|
+
puts "> #{line}"
|
35
|
+
client.send_data line + "\r\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
def unbind
|
39
|
+
puts "* server closed connection"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(server, server_port)
|
44
|
+
@server = EM.connect(server, server_port, IrcProxy::Server, self)
|
45
|
+
@lt2_delimiter = "\r\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
def post_init
|
49
|
+
puts "* client connected, establishing connection to server..."
|
50
|
+
end
|
51
|
+
|
52
|
+
def receive_line(line)
|
53
|
+
puts "< #{line}"
|
54
|
+
@server.send_data line + "\r\n"
|
55
|
+
end
|
56
|
+
|
57
|
+
def unbind
|
58
|
+
puts "* client closed connection"
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|