Class: IRC::Client

Inherits:
Object
  • Object
show all
Includes:
Loggable, Rhuidean
Defined in:
lib/rhuidean/client.rb,
lib/rhuidean/methods.rb

Overview

These methods are shortcuts for sending data to the IRC server. You can use `raw` to do any of them, or even add a string directly to `@sendq` if you really want. I'm sure I haven't thought of everything here.

Direct Known Subclasses

StatefulClient

Defined Under Namespace

Classes: Error

Constant Summary

IRC_RE =

Note that this doesn't match every IRC message, just the ones we care about. It also doesn't match every IRC message in the way we want. We get what we need. The rest is ignored.

Here's a compact version if you need it:

^(?:\:([^\s]+)\s)?(\w+)\s(?:([^\s\:]+)\s)?(?:\:?(.*))?$
/
^              # beginning of string
(?:            # non-capturing group
    \:         # if we have a ':' then we have an origin
    ([^\s]+)   # get the origin without the ':'
    \s         # space after the origin
)?             # close non-capturing group
(\w+)          # must have a command
\s             # and a space after it
(?:            # non-capturing group
    ([^\s\:]+) # a target for the command
    \s         # and a space after it
)?             # close non-capturing group
(?:            # non-capturing group
    \:?        # if we have a ':' then we have freeform text
    (.*)       # get the rest as one string without the ':'
)?             # close non-capturing group
$              # end of string
/x

Constants included from Rhuidean

Rhuidean::VERSION, Rhuidean::V_MAJOR, Rhuidean::V_MINOR, Rhuidean::V_PATCH

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Loggable

#log, #log_level=, #logger=

Constructor Details

#initialize {|_self| ... } ⇒ Client

Creates a new IRC::Client object. If there is a block given, passes itself to the block for pretty attribute setting.


<tt>client = IRC::Client.new do |c|

c.server   = 'irc.malkier.net'
c.port     = 6667
c.password = 'partypants'
c.nickname = 'rakaur'
c.username = 'rakaur'
c.realname = 'watching the weather change'
c.bind_to  = '10.0.1.20'

c.logger    = Logger.new($stderr)
c.log_level = :debug

end

client.thread = Thread.new { client.io_loop } clients.each { |client| client.thread.join }

...

client.quit("IRC quit message!") client.exit


returns

self

Yields:

  • (_self)

Yield Parameters:

  • _self (IRC::Client)

    the object that the method was called on



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/rhuidean/client.rb', line 58

def initialize
    # Is our socket dead?
    @dead      = false
    @connected = false

    # Received data waiting to be parsed.
    @recvq = []

    # Data waiting to be sent.
    @sendq = []

    # Our event queue.
    @eventq = EventQueue.new

    # Local IP to bind to
    @bind_to = nil

    # Password to login to the server
    @password = nil

    # Our Logger object.
    @logger        = Logger.new($stderr)
    self.log_level = :info

    # If we have a block let it set up our instance attributes.
    yield(self) if block_given?

    # Core events which are needed to work at all.
    on(:read_ready)  { read  }
    on(:write_ready) { write }
    on(:recvq_ready) { parse }

    on(:dead) { self.dead = true }

    on(:exit) do |from|
        log(:fatal, "exiting via #{from}...")
        Thread.exit
    end

    on(:PING) { |m| raw("PONG :#{m.target}") }

    # Set up event handlers. These track some state and such, and can
    # be overridden for other functionality in any child classes.
    set_default_handlers

    # Special method for default CTCP replies
    # I use this so they don't get wiped out when someone overrides
    # the default handlers, but also so that they CAN be wiped out.
    set_ctcp_handlers

    self
end

Instance Attribute Details

#bind_toObject

instance attributes



23
24
25
# File 'lib/rhuidean/client.rb', line 23

def bind_to
  @bind_to
end

#nicknameObject

instance attributes



23
24
25
# File 'lib/rhuidean/client.rb', line 23

def nickname
  @nickname
end

#passwordObject

instance attributes



23
24
25
# File 'lib/rhuidean/client.rb', line 23

def password
  @password
end

#portObject

instance attributes



23
24
25
# File 'lib/rhuidean/client.rb', line 23

def port
  @port
end

#realnameObject

instance attributes



23
24
25
# File 'lib/rhuidean/client.rb', line 23

def realname
  @realname
end

#serverObject

instance attributes



23
24
25
# File 'lib/rhuidean/client.rb', line 23

def server
  @server
end

#socketObject (readonly)

Our TCPSocket.



27
28
29
# File 'lib/rhuidean/client.rb', line 27

def socket
  @socket
end

#threadObject

instance attributes



23
24
25
# File 'lib/rhuidean/client.rb', line 23

def thread
  @thread
end

#usernameObject

instance attributes



23
24
25
# File 'lib/rhuidean/client.rb', line 23

def username
  @username
end

Instance Method Details

#connectObject

Creates and connects our socket.


returns

self



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/rhuidean/client.rb', line 398

def connect
    verify_attributes

    log(:info, "connecting to #@server:#@port")

    begin
        @socket = TCPSocket.new(@server, @port, @bind_to)
    rescue Exception => err
        @eventq.post(:dead)
    end

    @dead      = false
    @connected = true

    pass(@password) if @password
    nick(@nickname)
    user(@username, @server, @server, @realname)
end

#connected?Boolean

Are we connected?


returns

true or false



389
390
391
# File 'lib/rhuidean/client.rb', line 389

def connected?
    @connected
end

#ctcp(to, type, params = nil) ⇒ Object

Sends a CTCP



47
48
49
50
51
52
53
# File 'lib/rhuidean/methods.rb', line 47

def ctcp(to, type, params = nil)
    if params
        @sendq << "PRIVMSG #{to} :\1#{type} #{params}\1"
    else
        @sendq << "PRIVMSG #{to} :\1#{type}\1"
    end
end

#ctcp_reply(to, type, params = '') ⇒ Object

Sends a CTCP reply



61
62
63
# File 'lib/rhuidean/methods.rb', line 61

def ctcp_reply(to, type, params = '')
    @sendq << "NOTICE #{to} :\1#{type} #{params}\1"
end

#dead?Boolean

Is the socket dead?


returns

true or false



380
381
382
# File 'lib/rhuidean/client.rb', line 380

def dead?
    @dead
end

#exit(from = 'exit') ⇒ Object

Forces the Client's Thread to die. If it's the main thread, the application goes with it.


returns

nope!



432
433
434
435
# File 'lib/rhuidean/client.rb', line 432

def exit(from = 'exit')
    @eventq.post(:exit, from)
    @eventq.run
end

#invite(target, channel) ⇒ Object

Sends an IRC INVITE command.



96
97
98
# File 'lib/rhuidean/methods.rb', line 96

def invite(target, channel)
    @senq << "INVITE #{target} #{channel}"
end

#io_loopObject

Schedules input/output and runs the EventQueue.


returns

never, thread dies on :exit



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
# File 'lib/rhuidean/client.rb', line 340

def io_loop
    loop do
        if dead?
            sleep(30)
            connect
            next
        end

        connect unless connected?

        # Run the event loop. These events will add IO, and possibly other
        # events, so we keep running until it's empty.
        @eventq.run while @eventq.needs_ran?

        next if dead?

        writefd = [@socket] unless @sendq.empty?

        # Ruby's threads suck. In theory, the timers should
        # manage themselves in separate threads. Unfortunately,
        # Ruby has a global lock and the scheduler isn't great, so
        # this tells select() to timeout when the next timer needs to run.
        timeout = (Timer.next_time - Time.now.to_f).round
        timeout = 1 if timeout == 0 # Don't want 0, that's forever
        timeout = 60 if timeout < 0 # Less than zero means no timers

        ret = IO.select([@socket], writefd, [], timeout)

        next unless ret

        @eventq.post(:read_ready)  unless ret[0].empty?
        @eventq.post(:write_ready) unless ret[1].empty?
    end
end

#join(channel, key = '') ⇒ Object

Sends an IRC JOIN command.



66
67
68
# File 'lib/rhuidean/methods.rb', line 66

def join(channel, key = '')
    @sendq << "JOIN #{channel} #{key}"
end

#kick(channel, target, reason = '') ⇒ Object

Sends an IRC KICK command.



76
77
78
# File 'lib/rhuidean/methods.rb', line 76

def kick(channel, target, reason = '')
    @sendq << "KICK #{channel} #{target} :#{reason}"
end

#mode(target, mode = '') ⇒ Object

Sends an IRC MODE command.



91
92
93
# File 'lib/rhuidean/methods.rb', line 91

def mode(target, mode = '')
    @sendq << "MODE #{target} #{mode}"
end

#nick(nick) ⇒ Object

Sends an IRC NICK command.



27
28
29
# File 'lib/rhuidean/methods.rb', line 27

def nick(nick)
    @sendq << "NICK #{nick}"
end

#notice(to, message) ⇒ Object

Sends an IRC NOTICE command.



56
57
58
# File 'lib/rhuidean/methods.rb', line 56

def notice(to, message)
    @sendq << "NOTICE #{to} :#{message}"
end

#on(event, &block) ⇒ Object

Registers Event handlers with our EventQueue.


<tt>c.on(:PRIVMSG) do |m|

next if m.params.empty?
if m.params =~ /\.die/ and m.origin == my_master
    c.quit(params)
    c.exit
end</tt>

event

name of the event as a Symbol

block

block to call when Event is posted

returns

self



329
330
331
332
333
# File 'lib/rhuidean/client.rb', line 329

def on(event, &block)
    @eventq.handle(event, &block)

    self
end

#part(channel, message = '') ⇒ Object

Sends an IRC PART command.



71
72
73
# File 'lib/rhuidean/methods.rb', line 71

def part(channel, message = '')
    @sendq << "PART #{channel} :#{message}"
end

#pass(password) ⇒ Object

Sends an IRC PASS command.



37
38
39
# File 'lib/rhuidean/methods.rb', line 37

def pass(password)
    @sendq << "PASS #{password}"
end

#privmsg(to, message) ⇒ Object

Sends an IRC PRIVMSG command.



42
43
44
# File 'lib/rhuidean/methods.rb', line 42

def privmsg(to, message)
    @sendq << "PRIVMSG #{to} :#{message}"
end

#quit(message = '') ⇒ Object

Send an IRC QUIT command.



101
102
103
# File 'lib/rhuidean/methods.rb', line 101

def quit(message = '')
    @sendq << "QUIT :#{message}"
end

#raw(message) ⇒ Object

Sends text directly to the server.



22
23
24
# File 'lib/rhuidean/methods.rb', line 22

def raw(message)
    @sendq << message
end

#to_sObject

Represent ourselves in a string.


returns

our nickname and object ID



422
423
424
# File 'lib/rhuidean/client.rb', line 422

def to_s
    "#{@nickname}:#{self.object_id}"
end

#topic(target, new = '') ⇒ Object

Sends an IRC TOPIC command.



81
82
83
# File 'lib/rhuidean/methods.rb', line 81

def topic(target, new = '')
    @sendq << "TOPIC #{target} :#{new}"
end

#umode(mode) ⇒ Object

Sends an IRC MODE command.



86
87
88
# File 'lib/rhuidean/methods.rb', line 86

def umode(mode)
    @sendq << "MODE #@nickname #{mode}"
end

#user(username, server, host, realname) ⇒ Object

Sends an IRC USER command.



32
33
34
# File 'lib/rhuidean/methods.rb', line 32

def user(username, server, host, realname)
    @sendq << "USER #{username} #{server} #{host} :#{realname}"
end