Class: CamperVan::Channel

Inherits:
Object
  • Object
show all
Includes:
Logger, Utils
Defined in:
lib/camper_van/channel.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logger

#logger

Methods included from Utils

#irc_name, #stringify_keys

Constructor Details

#initialize(channel, client, room) ⇒ Channel

Public: create a new campfire channel

channel - name of the channel we’re joining client - the EM::Connection representing the irc client room - the campfire room we’re joining



34
35
36
37
# File 'lib/camper_van/channel.rb', line 34

def initialize(channel, client, room)
  @channel, @client, @room = channel, client, room
  @users = {}
end

Instance Attribute Details

#channelObject (readonly)

The irc channel name this channel instance is for



9
10
11
# File 'lib/camper_van/channel.rb', line 9

def channel
  @channel
end

#clientObject (readonly)

The connected irc client connection



12
13
14
# File 'lib/camper_van/channel.rb', line 12

def client
  @client
end

#roomObject (readonly)

The campfire room to which this channel is connected



15
16
17
# File 'lib/camper_van/channel.rb', line 15

def room
  @room
end

#streamObject (readonly)

Accessor for the EM http request representing the live stream from the campfire api



19
20
21
# File 'lib/camper_van/channel.rb', line 19

def stream
  @stream
end

#usersObject (readonly)

Accessor for hash of known users in the room/channel Kept up to date by update_users command, as well as Join/Leave campfire events.



24
25
26
# File 'lib/camper_van/channel.rb', line 24

def users
  @users
end

Instance Method Details

#current_modeObject

Public: sends the current channel mode to the client



132
133
134
135
# File 'lib/camper_van/channel.rb', line 132

def current_mode
  n = room.membership_limit
  client.numeric_reply :rpl_channelmodeis, channel, current_mode_string, n
end

#current_mode_stringObject

Returns the current mode string



171
172
173
174
175
176
# File 'lib/camper_van/channel.rb', line 171

def current_mode_string
  n = room.membership_limit
  s = room.open_to_guests? ? "" : "s"
  i = room.locked? ? "i" : ""
  "+#{i}l#{s}"
end

#current_topicObject

Public: returns the current topic of the campfire room



179
180
181
# File 'lib/camper_van/channel.rb', line 179

def current_topic
  client.numeric_reply :rpl_topic, channel, ':' + room.topic
end

#joinObject

Public: Joins a campfire room and sends the necessary topic and name list messages back to the IRC client.

Returns true if the join was successful,

false if the room was full or locked.


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/camper_van/channel.rb', line 44

def join
  if room.locked?
    client.numeric_reply :err_inviteonlychan, "Cannot join #{channel} (locked)"
    return false

  elsif room.full?
    client.numeric_reply :err_channelisfull, "Cannot join #{channel} (full)"
    return false

  else
    update_users do
      # join channel
      client.user_reply :join, ":#{channel}"

      # current topic
      client.numeric_reply :rpl_topic, channel, ':' + (room.topic || "")

      # List the current users, which must always include myself
      # (race condition, server may not realize the user has joined yet)
      nicks = users.values.map { |u| u.nick }
      nicks.unshift client.nick unless nicks.include? client.nick

      nicks.each_slice(10) do |list|
        client.numeric_reply :rpl_namereply, "=", channel, ":#{list.join ' '}"
      end
      client.numeric_reply :rpl_endofnames, channel, "End of /NAMES list."

      # begin streaming the channel events (joins room implicitly)
      stream_campfire_to_channel
    end
  end

  true
end

#list_usersObject

Public: replies to a WHO command with a list of users for a campfire room, including their nicks, names, and status.

For WHO response: www.mircscripts.org/forums.php?cid=3&id=159227 In short, H = here, G = away, append @ for chanops (admins)



95
96
97
98
99
100
101
102
103
104
# File 'lib/camper_van/channel.rb', line 95

def list_users
  update_users(:include_joins_and_parts) do
    users.values.each do |user|
      status = (user.idle? ? "G" : "H") + (user.admin? ? "@" : "")
      client.numeric_reply :rpl_whoreply, channel, user., user.server,
        "camper_van", user.nick, status, ":0 #{user.name}"
    end
    client.numeric_reply :rpl_endofwho, channel, "End of WHO list"
  end
end

#map_message_to_irc(message) ⇒ Object

Map a campfire message to one or more IRC commands for the client

message - the campfire message to map to IRC

Returns nothing, but responds according to the message



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/camper_van/channel.rb', line 255

def map_message_to_irc(message)
  user_for_message(message) do |message, user|

    # needed in most cases
    name = user ? irc_name(user.name) : nil

    # strip Message off the type to simplify readability
    type = message.type.sub(/Message$/, '')

    if %w(Text Tweet Sound Paste Upload).include?(type) && name == client.nick
        logger.debug "skipping message from myself: #{message.type} #{message.body}"
      next
    end

    case type
    when "Timestamp", "Advertisement"
      # ignore these

    when "Lock"
      client.campfire_reply :mode, name, channel, "+i"

    when "Unlock"
      client.campfire_reply :mode, name, channel, "-i"

    when "DisallowGuests"
      name = irc_name(user.name)
      client.campfire_reply :mode, name, channel, "+s"

    when "AllowGuests"
      name = irc_name(user.name)
      client.campfire_reply :mode, name, channel, "-s"

    when "Idle"
      if u = users[user.id]
        u.idle = true
      end

    when "Unidle"
      if u = users[user.id]
        u.idle = false
      end

    when "Enter"
      unless users[user.id]
        client.campfire_reply :join, name, channel
        users[user.id] = User.new(user)
      end

    when "Leave", "Kick" # kick is used for idle timeouts
      client.campfire_reply :part, name, channel, "Leaving..."
      users.delete user.id

    when "Paste"
      lines = message.body.split(/\n|\r\n|\r/)

      lines[0..2].each do |line|
        client.campfire_reply :privmsg, name, channel, ":> " + line
      end

      if lines.size > 3
        client.campfire_reply :privmsg, name, channel, ":> more: " +
          "https://#{client.subdomain}.campfirenow.com/room/#{room.id}/paste/#{message.id}"
      end

    when "Sound"
      text = case message.body
      when "crickets"
        "hears crickets chirping"
      when "rimshot"
        "plays a rimshot"
      when "trombone"
        "plays a sad trombone"
      when "vuvuzela"
        "======<() ~ ♪ ~♫"
      else
        "played a #{message.body} sound"
      end

      client.campfire_reply :privmsg, name, channel, "\x01ACTION #{text}\x01"

    # when "System"
    #   # NOTICE from :camper_van to channel?

    when "Text"
      if message.body =~ /^\*.*\*$/
        client.campfire_reply :privmsg, name, channel, ":\01ACTION " + message.body[1..-2] + "\01"
      else
        matched = users.values.detect do |user|
          message.body =~ /^#{Regexp.escape(user.name)}(\W+(\s|$)|$)/
        end

        if matched
          body = message.body.sub(/^#{matched.name}/, matched.nick)
        else
          body = message.body
        end
        body.split(/[\r\n]+/).each do |line|
          client.campfire_reply :privmsg, name, channel, ":" + line
        end
      end

    when "TopicChange"
      client.campfire_reply :topic, name, channel, message.body
      room.topic = message.body
      # client.numeric_reply :rpl_topic, channel, ':' + message.body

    when "Upload"
      room.connection.http(:get, "/room/#{message.room_id}/messages/#{message.id}/upload.json") do |data|
        client.campfire_reply :privmsg, name, channel, ":\01ACTION uploaded " + data[:upload][:full_url]
      end

    when "Tweet"
      message.body.split(/\n|\r\n|\r/).each do |line|
        client.campfire_reply :privmsg, name, channel, line
      end

    else
      logger.warn "unknown message #{message.type}: #{message.body}"
    end
  end
end

#partObject

Public: “leaves” a campfire room, per the PART irc command. Confirms with the connected client to PART the channel.



81
82
83
84
85
86
87
88
# File 'lib/camper_van/channel.rb', line 81

def part
  client.user_reply :part, channel
  if stream
    stream.close if stream.respond_to?(:close) # EM/em-http-request gem is installed and uses .close
    stream.close_connection if stream.respond_to?(:close_connection)
  end
  room.leave
end

#privmsg(msg) ⇒ Object

Public: accepts an IRC PRIVMSG and converts it to an appropriate campfire text message for the room.

msg - the IRC PRIVMSG message contents



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/camper_van/channel.rb', line 111

def privmsg(msg)

  # convert twitter urls to tweets
  if msg =~ %r(^https://twitter.com/(\w+)/status/(\d+)$)
    room.tweet(msg) { } # async, no-op callback
  else
    # convert ACTIONs
    msg.sub! /^\01ACTION (.*)\01$/, '*\1*'

    matched = users.values.detect do |user|
      msg =~ /^#{Regexp.escape(user.nick)}($|\W+(\s|$))/
    end

    msg = msg.sub(/^#{matched.nick}/, matched.name) if matched

    room.text(msg) { } # async, no-op callback
  end

end

#set_mode(mode) ⇒ Object

Public: set the mode on the campfire channel, mapping from the provided IRC chanmode to the campfire setting.

mode - the IRC mode flag change. Must be one of:

"+i" - lock room
"-i" - unlock room

TODO support these when the firering client does:

"+s" - disable guest access
"-s" - enable guest access

Returns nothing, but lets the client know the results of the call. Sends

an error to the client for an invalid mode string.


150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/camper_van/channel.rb', line 150

def set_mode(mode)
  case mode
  # when "+s"
  # when "-s"
  when "+i"
    room.lock
    room.locked = true
    client.user_reply :mode, channel,
      current_mode_string, room.membership_limit
  when "-i"
    room.unlock
    room.locked = false
    client.user_reply :mode, channel,
      current_mode_string, room.membership_limit
  else
    client.numeric_reply :err_unknownmode,
      "is unknown mode char to me for #{channel}"
  end
end

#set_topic(topic) ⇒ Object

Public: set the topic of the campfire room to the given string and lets the irc client know about the change

topic - the new topic



187
188
189
190
191
192
# File 'lib/camper_van/channel.rb', line 187

def set_topic(topic)
  room.update("topic" => topic) do
    room.topic = topic
    client.numeric_reply :rpl_topic, channel, ':' + room.topic
  end
end

#stream_campfire_to_channelObject

Stream messages from campfire and map them to IRC commands for the connected client.

Only starts the stream once.



244
245
246
247
248
# File 'lib/camper_van/channel.rb', line 244

def stream_campfire_to_channel
  @stream ||= room.stream do |message|
    map_message_to_irc message
  end
end

#update_users(include_joins_and_parts = false, &callback) ⇒ Object

Get the list of users from a room, and update the internal tracking state as well as the connected client. If the user list is out of sync, the irc client may receive the associated JOIN/PART commands.

include_joins_and_parts - whether or not to include JOIN/PART commands if

the user list has changed since the last update
(defaults to false)

callback - optional callback after the users have been

updated

Returns nothing, but keeps the users list updated



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/camper_van/channel.rb', line 206

def update_users(include_joins_and_parts=false, &callback)
  room.users do |user_list|
    before = users.dup
    present = {}

    user_list.each do |user|
      if before[user.id]
        present[user.id] = before.delete user.id
        # if present[user.id].nick != nick
        #   # NICK CHANGE
        #   present[user.id].nick = nick
        # end
      else
        new_user = present[user.id] = User.new(user)
        if include_joins_and_parts
          client.campfire_reply :join, new_user.nick, channel
        end
      end
    end

    # Now that the list of users is updated, the remaining users
    # in 'before' have left. Let the irc client know.
    before.each do |id, user|
      if include_joins_and_parts
        client.campfire_reply :part, user.nick, channel
      end
    end

    @users = present

    callback.call if callback
  end
end

#user_for_message(message) ⇒ Object

Retrieve the user from a message, either by finding it in the current list of known users, or by asking campfire for the user.

message - the message for which to look up the user

Yields the message and the user associated with the message



383
384
385
386
387
388
389
390
391
# File 'lib/camper_van/channel.rb', line 383

def user_for_message(message)
  if user = users[message.user_id]
    yield message, user
  else
    message.user do |user|
      yield message, user
    end
  end
end