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