camper_van 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,340 @@
|
|
1
|
+
require "socket" # for gethostname
|
2
|
+
|
3
|
+
module CamperVan
|
4
|
+
|
5
|
+
# the IRCD is the server that IRC clients connect to. It handles:
|
6
|
+
#
|
7
|
+
# * irc client registration and validation against campfire
|
8
|
+
# * mapping irc commands to internal Commands
|
9
|
+
# * proxying irc commands to campfire channels
|
10
|
+
class IRCD
|
11
|
+
|
12
|
+
# The IRC client
|
13
|
+
attr_reader :client
|
14
|
+
|
15
|
+
# Registration information for campfire authentication,
|
16
|
+
# comes from the PASS command from the irc client
|
17
|
+
attr_reader :subdomain, :api_key
|
18
|
+
|
19
|
+
# Information for the connected user
|
20
|
+
attr_reader :nick, :user, :host
|
21
|
+
|
22
|
+
# A Hash of connected CampfireChannels
|
23
|
+
attr_reader :channels
|
24
|
+
|
25
|
+
# Whether or not this server is actively sending/receiving data.
|
26
|
+
# Set to false when shutting down so extra commands are ignored.
|
27
|
+
attr_reader :active
|
28
|
+
|
29
|
+
MOTD = <<-motd
|
30
|
+
Welcome to CamperVan.
|
31
|
+
To see what campfire rooms are available to the
|
32
|
+
configured subdomain and api key, use the LIST command.
|
33
|
+
motd
|
34
|
+
|
35
|
+
include CommandDefinition # handle :command { ... }
|
36
|
+
include CommandParser # parses IRC commands
|
37
|
+
include ServerReply # IRC reply helpers
|
38
|
+
include Utils # irc translation helpers
|
39
|
+
include Logger # logging helper
|
40
|
+
|
41
|
+
# Public: initialize an IRC server connection
|
42
|
+
#
|
43
|
+
# client - the EM connection representing the IRC client
|
44
|
+
def initialize(client)
|
45
|
+
@client = client
|
46
|
+
@active = true
|
47
|
+
@channels = {}
|
48
|
+
end
|
49
|
+
|
50
|
+
# The campfire client
|
51
|
+
#
|
52
|
+
# Returns the existing or initializes a new instance of a campfire
|
53
|
+
# client using the configured subdomain and API key.
|
54
|
+
def campfire
|
55
|
+
@campfire ||= Firering::Connection.new(
|
56
|
+
"http://#{subdomain}.campfirenow.com"
|
57
|
+
) do |c|
|
58
|
+
c.token = api_key
|
59
|
+
c.logger = CamperVan.logger
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Handler for when a client sends an IRC command
|
64
|
+
def receive_line(line)
|
65
|
+
if @active
|
66
|
+
cmd = parse(line)
|
67
|
+
handle cmd
|
68
|
+
end
|
69
|
+
rescue HandlerMissing
|
70
|
+
logger.info "ignoring irc command #{cmd.inspect}: no handler"
|
71
|
+
end
|
72
|
+
|
73
|
+
# Send a line back to the irc client
|
74
|
+
def send_line(line)
|
75
|
+
client.send_line line if @active
|
76
|
+
end
|
77
|
+
|
78
|
+
# Shuts down this connection to the server
|
79
|
+
def shutdown
|
80
|
+
@active = false
|
81
|
+
client.close_connection
|
82
|
+
end
|
83
|
+
|
84
|
+
# IRC registration sequence:
|
85
|
+
#
|
86
|
+
# PASS <password> (may not be sent!)
|
87
|
+
# NICK <nickname>
|
88
|
+
# USER <user info>
|
89
|
+
#
|
90
|
+
|
91
|
+
# PASS command handler
|
92
|
+
handle :pass do |args|
|
93
|
+
if args.empty?
|
94
|
+
numeric_reply :err_needmoreparams, ":must specify a password: subdomain:api_key"
|
95
|
+
shutdown
|
96
|
+
else
|
97
|
+
@subdomain, @api_key = *args.first.split(":")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# NICK command handler
|
102
|
+
#
|
103
|
+
# As a part of the registration sequence, sets the nickname.
|
104
|
+
# If sent after the client is registered, responds with an IRC
|
105
|
+
# error, as nick changes with campfire are disallowed (TODO)
|
106
|
+
handle :nick do |args|
|
107
|
+
if args.empty?
|
108
|
+
numeric_reply :err_nonicknamegiven, ":no nickname given"
|
109
|
+
else
|
110
|
+
if @nick
|
111
|
+
# TODO error
|
112
|
+
else
|
113
|
+
@nick = args.first
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# USER command handler
|
119
|
+
#
|
120
|
+
# Final part of the registration sequence.
|
121
|
+
# If registration is successful, sends a welcome reply sequence.
|
122
|
+
handle :user do |args|
|
123
|
+
if args.size < 4
|
124
|
+
numeric_reply :err_needmoreparams, "Need more params"
|
125
|
+
else
|
126
|
+
@user = args.first
|
127
|
+
# grab the remote IP address for the client
|
128
|
+
@host = client.remote_ip
|
129
|
+
|
130
|
+
unless @api_key
|
131
|
+
command_reply :notice, "AUTH", "*** must specify campfire API key as password ***"
|
132
|
+
shutdown
|
133
|
+
return
|
134
|
+
end
|
135
|
+
|
136
|
+
successful_registration
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# PING command handler.
|
141
|
+
#
|
142
|
+
# Responds with a PONG
|
143
|
+
handle :ping do |args|
|
144
|
+
command_reply :pong, *args
|
145
|
+
end
|
146
|
+
|
147
|
+
# LIST command handler
|
148
|
+
#
|
149
|
+
# Sends the list of available campfire channels to the client.
|
150
|
+
handle :list do |args|
|
151
|
+
# hooray async code: have to do gymnastics to make this appear
|
152
|
+
# sequential
|
153
|
+
campfire.rooms do |rooms|
|
154
|
+
sent = 0
|
155
|
+
rooms.each do |room|
|
156
|
+
name = "#" + irc_name(room.name)
|
157
|
+
topic = room.topic
|
158
|
+
room.users do |users|
|
159
|
+
numeric_reply :rpl_list, name, users.size, topic
|
160
|
+
sent += 1
|
161
|
+
if sent == rooms.size
|
162
|
+
numeric_reply :rpl_listend, "End of list"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
handle :who do |args|
|
170
|
+
if channel = channels[args.first]
|
171
|
+
channel.list_users
|
172
|
+
else
|
173
|
+
if args.empty?
|
174
|
+
numeric_reply :rpl_endofwho, "End of WHO list"
|
175
|
+
else
|
176
|
+
numeric_reply :rpl_endofwho, args.first, "End of WHO list"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
handle :join do |args|
|
182
|
+
args.each do |channel|
|
183
|
+
join_channel channel
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
handle :part do |args|
|
188
|
+
name = args.first
|
189
|
+
if channel = channels[name]
|
190
|
+
channel.part
|
191
|
+
else
|
192
|
+
numeric_reply :err_notonchannel, "You're not on that channel"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
handle :topic do |args|
|
197
|
+
name, new_topic = *args
|
198
|
+
if channel = channels[name]
|
199
|
+
if new_topic
|
200
|
+
channel.set_topic new_topic
|
201
|
+
else
|
202
|
+
channel.current_topic
|
203
|
+
end
|
204
|
+
else
|
205
|
+
# TODO topic error
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
handle :privmsg do |args|
|
210
|
+
name, msg = *args
|
211
|
+
if channel = channels[name]
|
212
|
+
channel.privmsg msg
|
213
|
+
else
|
214
|
+
numeric_reply :err_nonicknamegiven, name, "No such nick/channel"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
handle :mode do |args|
|
219
|
+
if channel = channels[args.shift]
|
220
|
+
|
221
|
+
if mode = args.first
|
222
|
+
if mode =~ /^[+-][si]$/
|
223
|
+
channel.set_mode mode
|
224
|
+
else
|
225
|
+
mode = mode.gsub(/\W/,'')
|
226
|
+
numeric_reply :err_unknownmode, mode, "Unknown mode #{mode}"
|
227
|
+
end
|
228
|
+
else
|
229
|
+
channel.current_mode
|
230
|
+
end
|
231
|
+
|
232
|
+
else
|
233
|
+
# no error message for this situation, so ignore it silently
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
handle :away do |args|
|
238
|
+
# ignore silently, there's no campfire API for this
|
239
|
+
end
|
240
|
+
|
241
|
+
handle :quit do |args|
|
242
|
+
channels.values.each do |channel|
|
243
|
+
channel.part
|
244
|
+
end
|
245
|
+
shutdown
|
246
|
+
end
|
247
|
+
|
248
|
+
# Completes a successful registration with the appropriate responses
|
249
|
+
def successful_registration
|
250
|
+
check_campfire_authentication do
|
251
|
+
check_nick_matches_authenticated_user
|
252
|
+
send_welcome
|
253
|
+
send_luser_info
|
254
|
+
send_motd
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Checks that the campfire authentication is successful.
|
259
|
+
#
|
260
|
+
# callback - a block to call if successful.
|
261
|
+
#
|
262
|
+
# Yields to the callback on success (async)
|
263
|
+
#
|
264
|
+
# If it fails, it replies with an error to the client and
|
265
|
+
# disconnects.
|
266
|
+
def check_campfire_authentication(&callback)
|
267
|
+
# invalid user only returns a nil result!
|
268
|
+
campfire.user("me") do |user|
|
269
|
+
if user.name
|
270
|
+
yield
|
271
|
+
else
|
272
|
+
command_reply :notice, "AUTH", "could not connect to campfire: invalid API key"
|
273
|
+
shutdown
|
274
|
+
end
|
275
|
+
end
|
276
|
+
rescue Firering::Connection::HTTPError => e
|
277
|
+
command_reply :notice, "AUTH", "could not connect to campfire: #{e.message}"
|
278
|
+
shutdown
|
279
|
+
end
|
280
|
+
|
281
|
+
# Check to see that the nick as provided during the registration
|
282
|
+
# process matches the authenticated campfire user. If the nicks don't
|
283
|
+
# match, send a nick change to the connected client.
|
284
|
+
def check_nick_matches_authenticated_user
|
285
|
+
campfire.user("me") do |user|
|
286
|
+
name = irc_name user.name
|
287
|
+
if name != nick
|
288
|
+
user_reply :nick, name
|
289
|
+
@nick = name
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def send_welcome
|
295
|
+
hostname = Socket.gethostname
|
296
|
+
numeric_reply :rpl_welcome, "Welcome to CamperVan, #{nick}!#{user}@#{host}"
|
297
|
+
numeric_reply :rpl_yourhost, "Your host is #{hostname}, " +
|
298
|
+
"running CamperVan version #{CamperVan::VERSION}"
|
299
|
+
# using Time.now instead of a global start time since, well, this
|
300
|
+
# particular instance really did just start right now. Give or
|
301
|
+
# take a few seconds.
|
302
|
+
numeric_reply :rpl_created, "This server was created #{Time.now}"
|
303
|
+
numeric_reply :rpl_myinfo, hostname, CamperVan::VERSION,
|
304
|
+
# channel modes: invite-only, secret
|
305
|
+
"is",
|
306
|
+
# user modes: away
|
307
|
+
"a"
|
308
|
+
end
|
309
|
+
|
310
|
+
def send_luser_info
|
311
|
+
numeric_reply :rpl_luserclient, "There is 1 user on 1 channel"
|
312
|
+
numeric_reply :rpl_luserop, 0, "IRC Operators online"
|
313
|
+
numeric_reply :rpl_luserchannels, 0, "channels formed"
|
314
|
+
numeric_reply :rpl_myinfo, "I have 1 client and 0 servers"
|
315
|
+
end
|
316
|
+
|
317
|
+
def send_motd
|
318
|
+
numeric_reply :rpl_motdstart, ":- MOTD for camper_van -"
|
319
|
+
MOTD.split("\n").each do |line|
|
320
|
+
numeric_reply :rpl_motd, ":- #{line.strip}"
|
321
|
+
end
|
322
|
+
numeric_reply :rpl_endofmotd, "END of MOTD"
|
323
|
+
end
|
324
|
+
|
325
|
+
def join_channel(name)
|
326
|
+
campfire.rooms do |rooms|
|
327
|
+
if room = rooms.detect { |r| "#" + irc_name(r.name) == name }
|
328
|
+
channel = Channel.new(name, self, room)
|
329
|
+
if channel.join
|
330
|
+
channels[name] = channel
|
331
|
+
end
|
332
|
+
else
|
333
|
+
numeric_reply :err_nosuchchannel, name, "No such campfire room!"
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module CamperVan
|
2
|
+
|
3
|
+
# The core EventMachine server instance that listens for IRC
|
4
|
+
# connections and maps them to IRCD instances.
|
5
|
+
module Server
|
6
|
+
# Public: start the server
|
7
|
+
#
|
8
|
+
# bind_address - what address to bind to
|
9
|
+
# port - what port to listen on
|
10
|
+
# log_options - an optional hash of additional configuration
|
11
|
+
# options for the logger (see .initialize_logging)
|
12
|
+
def self.run(bind_address="localhost", port=6667, log_options={})
|
13
|
+
|
14
|
+
initialize_logging log_options
|
15
|
+
|
16
|
+
EM.run do
|
17
|
+
logger = Logging.logger[self.name]
|
18
|
+
logger.info "starting server on #{bind_address}:#{port}"
|
19
|
+
EM.start_server bind_address, port, self
|
20
|
+
trap("INT") do
|
21
|
+
logger.info "SIGINT, shutting down"
|
22
|
+
EM.stop
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Initialize the logging system
|
28
|
+
#
|
29
|
+
# opts - Hash of logging options
|
30
|
+
# - :log_level (default :info)
|
31
|
+
# - :log_to - where to log to (default STDOUT), can be IO or
|
32
|
+
# String for log filename
|
33
|
+
def self.initialize_logging(opts={})
|
34
|
+
Logging.consolidate("CamperVan")
|
35
|
+
|
36
|
+
Logging.logger.root.level = opts[:log_level] || :info
|
37
|
+
|
38
|
+
appender = case opts[:log_to]
|
39
|
+
when String
|
40
|
+
Logging.appenders.file(opts[:log_to])
|
41
|
+
when IO
|
42
|
+
Logging.appenders.io(opts[:log_to])
|
43
|
+
when nil
|
44
|
+
Logging.appenders.stdout
|
45
|
+
end
|
46
|
+
|
47
|
+
# YYYY-MM-DDTHH:MM:SS 12345 LEVEL LoggerName : The Log message
|
48
|
+
appender.layout = Logging::Layouts::Pattern.new(:pattern => "%d %5p %5l %c : %m\n")
|
49
|
+
|
50
|
+
Logging.logger.root.add_appenders appender
|
51
|
+
end
|
52
|
+
|
53
|
+
# Using a line-based protocol
|
54
|
+
include EM::Protocols::LineText2
|
55
|
+
|
56
|
+
include Logger
|
57
|
+
|
58
|
+
# Public: returns the instance of the ircd for this connection
|
59
|
+
attr_reader :ircd
|
60
|
+
|
61
|
+
# Public callback once a server connection is established.
|
62
|
+
#
|
63
|
+
# Initializes an IRCD instance for this connection.
|
64
|
+
def post_init(*args)
|
65
|
+
logger.info "got connection from #{remote_ip}"
|
66
|
+
|
67
|
+
# initialize the line-based protocol: IRC is \r\n
|
68
|
+
@lt2_delimiter = "\r\n"
|
69
|
+
|
70
|
+
# start up the IRCD for this connection
|
71
|
+
@ircd = IRCD.new(self)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Public: callback for when a line of the protocol has been
|
75
|
+
# received. Delegates the received line to the ircd instance.
|
76
|
+
#
|
77
|
+
# line - the line received
|
78
|
+
def receive_line(line)
|
79
|
+
logger.debug "irc -> #{line.strip}"
|
80
|
+
ircd.receive_line(line)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Public: send a line to the connected client.
|
84
|
+
#
|
85
|
+
# line - the line to send, sans \r\n delimiter.
|
86
|
+
def send_line(line)
|
87
|
+
logger.debug "irc <- #{line}"
|
88
|
+
send_data line + "\r\n"
|
89
|
+
end
|
90
|
+
|
91
|
+
# Public: callback when a client disconnects
|
92
|
+
def unbind
|
93
|
+
logger.info "closed connection from #{remote_ip}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Public: return the remote ip address of the connected client
|
97
|
+
#
|
98
|
+
# Returns an IP address string
|
99
|
+
def remote_ip
|
100
|
+
@remote_ip ||= get_peername[4,4].unpack("C4").map { |q| q.to_s }.join(".")
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module CamperVan
|
2
|
+
module ServerReply
|
3
|
+
|
4
|
+
# not an exhaustive list, just what i'm using
|
5
|
+
NUMERIC_REPLIES = {
|
6
|
+
|
7
|
+
# successful registration / welcome to the network
|
8
|
+
:rpl_welcome => "001",
|
9
|
+
:rpl_yourhost => "002",
|
10
|
+
:rpl_created => "003",
|
11
|
+
:rpl_myinfo => "004",
|
12
|
+
|
13
|
+
# more welcome messages
|
14
|
+
:rpl_luserclient => "251",
|
15
|
+
:rpl_luserop => "252",
|
16
|
+
:rpl_luserchannels => "254",
|
17
|
+
:rpl_luserme => "255",
|
18
|
+
|
19
|
+
# MOTD
|
20
|
+
:rpl_motdstart => "375",
|
21
|
+
:rpl_motd => "372",
|
22
|
+
:rpl_endofmotd => "376",
|
23
|
+
|
24
|
+
# MODE
|
25
|
+
:rpl_channelmodeis => "324",
|
26
|
+
|
27
|
+
# room listing
|
28
|
+
:rpl_list => "322",
|
29
|
+
:rpl_listend => "323",
|
30
|
+
:rpl_whoreply => "352",
|
31
|
+
:rpl_endofwho => "315",
|
32
|
+
|
33
|
+
# channel joins
|
34
|
+
:rpl_notopic => "331",
|
35
|
+
:rpl_topic => "332",
|
36
|
+
:rpl_namereply => "353",
|
37
|
+
:rpl_endofnames => "366",
|
38
|
+
|
39
|
+
# errors
|
40
|
+
:err_nosuchnick => "401", # no privmsgs to nicks allowed
|
41
|
+
:err_nosuchchannel => "403", # no such channel yo
|
42
|
+
|
43
|
+
:err_nonicknamegiven => "413",
|
44
|
+
|
45
|
+
:err_notonchannel => "442",
|
46
|
+
|
47
|
+
:err_needmoreparams => "461",
|
48
|
+
:err_passwdmismatch => "464",
|
49
|
+
|
50
|
+
:err_channelisfull => "471", # room is full
|
51
|
+
:err_unknownmode => "472",
|
52
|
+
:err_inviteonlychan => "473", # couldn't join the room, it's locked
|
53
|
+
:err_unavailresource => "437" # no such room!
|
54
|
+
|
55
|
+
}
|
56
|
+
|
57
|
+
def numeric_reply(code, *args)
|
58
|
+
number = NUMERIC_REPLIES[code]
|
59
|
+
raise ArgumentError, "unknown code #{code}" unless number
|
60
|
+
send_line ":camper_van #{number} #{nick}" << reply_args(args)
|
61
|
+
end
|
62
|
+
|
63
|
+
def command_reply(command, *args)
|
64
|
+
send_line ":camper_van #{command.to_s.upcase}" << reply_args(args)
|
65
|
+
end
|
66
|
+
|
67
|
+
def user_reply(command, *args)
|
68
|
+
send_line ":#{nick}!#{user}@#{host} #{command.to_s.upcase}" << reply_args(args)
|
69
|
+
end
|
70
|
+
|
71
|
+
def campfire_reply(command, username, *args)
|
72
|
+
# TODO instead of @campfire, use user's email address
|
73
|
+
send_line ":#{username}!#{username}@campfire #{command.to_s.upcase}" << reply_args(args)
|
74
|
+
end
|
75
|
+
|
76
|
+
def error_reply(reason)
|
77
|
+
send_line "ERROR :#{reason}"
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def reply_args(args)
|
83
|
+
reply = ""
|
84
|
+
if args.size > 0
|
85
|
+
if args.last =~ /\s/ && !args.last.start_with?(':')
|
86
|
+
args[-1] = ':' + args.last
|
87
|
+
end
|
88
|
+
reply << " " << args.join(" ")
|
89
|
+
end
|
90
|
+
reply
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module CamperVan
|
2
|
+
class User
|
3
|
+
|
4
|
+
# IRC normalization from names
|
5
|
+
include Utils
|
6
|
+
|
7
|
+
# Public: the user's campfire id
|
8
|
+
attr_reader :id
|
9
|
+
|
10
|
+
# Public: the user's campfire name
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
# Public: the user's irc nick
|
14
|
+
attr_reader :nick
|
15
|
+
|
16
|
+
# Public: the user's unix account name for user@host pairs in irc,
|
17
|
+
# mapped from the user's email address
|
18
|
+
attr_reader :account
|
19
|
+
|
20
|
+
# Public: the user's unix server name for user@host pairs in irc,
|
21
|
+
# mapped from the user's email address
|
22
|
+
attr_reader :server
|
23
|
+
|
24
|
+
# Public: whether the user is idle or not. Updated by campfire
|
25
|
+
# Idle/Unidle messages
|
26
|
+
def idle?
|
27
|
+
@idle
|
28
|
+
end
|
29
|
+
|
30
|
+
# Public: set the user's idle state.
|
31
|
+
#
|
32
|
+
# is_idle - true/false
|
33
|
+
attr_writer :idle
|
34
|
+
|
35
|
+
# Public: whether or not the user is an admin
|
36
|
+
def admin?
|
37
|
+
@admin
|
38
|
+
end
|
39
|
+
|
40
|
+
# Public: set the user's admin state
|
41
|
+
#
|
42
|
+
# admin - true/false
|
43
|
+
attr_writer :admin
|
44
|
+
|
45
|
+
# Public: create a new user from a campfire user definition.
|
46
|
+
#
|
47
|
+
# Initializes the user's fields based on the campfire user info.
|
48
|
+
def initialize(user)
|
49
|
+
@id = user.id
|
50
|
+
@name = user.name
|
51
|
+
@account, @server = user.email_address.split("@")
|
52
|
+
@nick = irc_name user.name
|
53
|
+
@idle = false
|
54
|
+
@admin = user.admin
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module CamperVan
|
2
|
+
module Utils
|
3
|
+
# TODO make irc-safe substitutions, etc.
|
4
|
+
def irc_name(name)
|
5
|
+
name.gsub('/', '-').
|
6
|
+
gsub(/\W/, ' ').
|
7
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
8
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
9
|
+
gsub(/\s+/, "_").
|
10
|
+
tr("-", "_").
|
11
|
+
downcase
|
12
|
+
end
|
13
|
+
|
14
|
+
def stringify_keys(hash)
|
15
|
+
hash.keys.each do |key|
|
16
|
+
hash[key.to_s] = hash.delete(key)
|
17
|
+
end
|
18
|
+
hash
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/lib/camper_van.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "camper_van/version"
|
2
|
+
|
3
|
+
require "eventmachine"
|
4
|
+
require "firering"
|
5
|
+
require "logging"
|
6
|
+
|
7
|
+
module CamperVan
|
8
|
+
require "camper_van/debug_proxy" # debug proxy
|
9
|
+
|
10
|
+
require "camper_van/utils" # utility methods
|
11
|
+
require "camper_van/logger" # logging helper
|
12
|
+
require "camper_van/command_parser" # irc command parser
|
13
|
+
require "camper_van/command_definition" # command definition and processing
|
14
|
+
require "camper_van/server_reply" # ircd responses and helpers
|
15
|
+
require "camper_van/user" # channel/campfire user
|
16
|
+
|
17
|
+
require "camper_van/ircd" # ircd server
|
18
|
+
require "camper_van/channel" # campfire room <-> channel bridge
|
19
|
+
|
20
|
+
require "camper_van/server" # the core campfire EM server
|
21
|
+
|
22
|
+
# Public: return the logger for the module
|
23
|
+
#
|
24
|
+
# Returns a Logging::Logger instance.
|
25
|
+
def self.logger
|
26
|
+
@logger = Logging::Logger[self.name]
|
27
|
+
end
|
28
|
+
end
|