In Files

Parent

Cinch::Bot

Attributes

channel_manager[R]

@return [ChannelManager]

channels[R]

@return [Array<Channel>] All channels the bot currently is in

config[R]

@return [Config]

handler_threads[R]

@return [Array<Thread>] @api private

host[R]

@return [String] the bot's hostname

irc[R]

@return [IRC]

last_connection_was_successful[RW]

@return [Boolean] @api private

logger[RW]

@return [Logger]

mask[R]

@return [Mask]

nick[RW]

The bot's nickname. @overload nick=(new_nick)

@raise [Exceptions::NickTooLong] Raised if the bot is
  operating in {#strict? strict mode} and the new nickname is
  too long
@return [String]

@overload nick

@return [String]

@return [String]

plugins[R]

@return [Array<Plugin>] All registered plugins

quitting[R]

@return [Boolean] whether the bot is in the process of disconnecting

realname[R]

@return [String]

signed_on_at[R]

@return [Time]

user[R]

@return [String]

user_manager[R]

@return [UserManager]

Public Class Methods

new(&b) click to toggle source

@yield

# File lib/cinch/bot.rb, line 480
def initialize(&b)
  @logger = Logger::FormattedLogger.new($stderr)
  @events = {}
  @config = OpenStruct.new({
                             :server => "localhost",
                             :port   => 6667,
                             :ssl    => OpenStruct.new({
                                                         :use => false,
                                                         :verify => false,
                                                         :client_cert => nil,
                                                         :ca_path => "/etc/ssl/certs",
                                                       }),
                             :password => nil,
                             :nick   => "cinch",
                             :nicks  => nil,
                             :realname => "cinch",
                             :user => "cinch",
                             :verbose => true,
                             :messages_per_second => 0.5,
                             :server_queue_size => 10,
                             :strictness => :forgiving,
                             :message_split_start => '... ',
                             :message_split_end   => ' ...',
                             :max_messages => nil,
                             :plugins => OpenStruct.new({
                                                          :plugins => [],
                                                          :prefix  => /^!/,
                                                          :suffix  => nil,
                                                          :options => Hash.new {|h,k| h[k] = {}},
                                                        }),
                             :channels => [],
                             :encoding => :irc,
                             :reconnect => true,
                             :local_host => nil,
                             :timeouts => OpenStruct.new({
                                                           :read => 240,
                                                           :connect => 10,
                                                         }),
                             :ping_interval => 120,
                           })

  @semaphores_mutex = Mutex.new
  @semaphores = Hash.new { |h,k| h[k] = Mutex.new }
  @plugins = []
  @callback = Callback.new(self)
  @channels = []
  @handler_threads = []
  @quitting = false

  @user_manager = UserManager.new(self)
  @channel_manager = ChannelManager.new(self)

  instance_eval(&b) if block_given?

  on :connect do
    bot.config.channels.each do |channel|
      bot.join channel
    end
  end
end

Public Instance Methods

Channel(channel) click to toggle source

Helper method for turning a String into a {Channel} object.

@param [String] channel a channel name @return [Channel] a {Channel} object @example

on :message, /^please join (#.+)$/ do |m, target|
  Channel(target).join
end
# File lib/cinch/bot.rb, line 79
def Channel(channel)
  return channel if channel.is_a?(Channel)
  @channel_manager.find_ensured(channel)
end
User(user) click to toggle source

Helper method for turning a String into an {User} object.

@param [String] user a user's nickname @return [User] an {User} object @example

on :message, /^tell me everything about (.+)$/ do |m, target|
  user = User(target)
  m.reply "%s is named %s and connects from %s" % [user.nick, user.name, user.host]
end
# File lib/cinch/bot.rb, line 93
def User(user)
  return user if user.is_a?(User)
  @user_manager.find_ensured(user)
end
action(recipient, text) click to toggle source

Invoke an action (/me) in/to a recipient (a channel or user). You should be using {Channel#action} and {User#action} instead.

@param [String] recipient the recipient @param [String] text the message to send @return [void] @see Channel#action @see User#action @see safe_action

# File lib/cinch/bot.rb, line 261
def action(recipient, text)
  raw("PRIVMSG #{recipient} :\0001ACTION #{text}\0001")
end
configure(&block) click to toggle source

This method is used to set a bot's options. It indeed does nothing else but yielding {Bot#config}, but it makes for a nice DSL.

@yieldparam [Struct] config the bot's config @return [void]

# File lib/cinch/bot.rb, line 382
def configure(&block)
  @callback.instance_exec(@config, &block)
end
debug(msg) click to toggle source

(see Logger::Logger#debug) @see Logger::Logger#debug

# File lib/cinch/bot.rb, line 469
def debug(msg)
  @logger.debug(msg)
end
dispatch(event, msg = nil, *arguments) click to toggle source

@param [Symbol] event The event type @param [Message, nil] msg The message which is responsible for

and attached to the event, or nil.

@param [Array] *arguments A list of additional arguments to pass

to event handlers

@return [void]

# File lib/cinch/bot.rb, line 337
def dispatch(event, msg = nil, *arguments)
  if handlers = find(event, msg)
    handlers.each do |handler|
      regexps, args, block = *handler
      # calling Message#match multiple times is not a problem
      # because we cache the result
      if msg
        regexp = regexps.find { |rx|
          msg.match(rx.to_r(msg), event)
        }
        captures = msg.match(regexp.to_r(msg), event).captures
      else
        captures = []
      end

      invoke(block, args, msg, captures, arguments)
    end
  end
end
generate_next_nick(base = nil) click to toggle source

Try to create a free nick, first by cycling through all available alternatives and then by appending underscores.

@param [String] base The base nick to start trying from @api private @return String

# File lib/cinch/bot.rb, line 571
def generate_next_nick(base = nil)
  nicks = @config.nicks || []

  if base
    # if `base` is not in our list of nicks to try, assume that it's
    # custom and just append an underscore
    if !nicks.include?(base)
      return base + "_"
    else
      # if we have a base, try the next nick or append an
      # underscore if no more nicks are left
      new_index = nicks.index(base) + 1
      if nicks[new_index]
        return nicks[new_index]
      else
        return base + "_"
      end
    end
  else
    # if we have no base, try the first possible nick
    new_nick = @config.nicks ? @config.nicks.first : @config.nick
  end
end
halt() click to toggle source

Stop execution of the current {on} handler.

@return [void]

# File lib/cinch/bot.rb, line 147
def halt
  throw :halt
end
helpers(&b) click to toggle source

Define helper methods in the context of the bot.

@yield Expects a block containing method definitions @return [void]

# File lib/cinch/bot.rb, line 102
def helpers(&b)
  Callback.class_eval(&b)
end
join(channel, key = nil) click to toggle source

Join a channel.

@param [String, Channel] channel either the name of a channel or a {Channel} object @param [String] key optionally the key of the channel @return [void] @see Channel#join

# File lib/cinch/bot.rb, line 451
def join(channel, key = nil)
  Channel(channel).join(key)
end
msg(recipient, text, notice = false) click to toggle source

Sends a PRIVMSG to a recipient (a channel or user). You should be using {Channel#send} and {User#send} instead.

@param [String] recipient the recipient @param [String] text the message to send @param [Boolean] notice Use NOTICE instead of PRIVMSG? @return [void] @see Channel#send @see User#send @see safe_msg

# File lib/cinch/bot.rb, line 173
def msg(recipient, text, notice = false)
  text = text.to_s
  split_start = @config.message_split_start || ""
  split_end   = @config.message_split_end   || ""
  command = notice ? "NOTICE" : "PRIVMSG"

  text.split(/\r\n|\r|\n/).each do |line|
    maxlength = 510 - (":" + " #{command} " + " :").size
    maxlength = maxlength - self.mask.to_s.length - recipient.to_s.length
    maxlength_without_end = maxlength - split_end.bytesize

    if line.bytesize > maxlength
      splitted = []

      while line.bytesize > maxlength_without_end
        pos = line.rindex(/\s/, maxlength_without_end)
        r = pos || maxlength_without_end
        splitted << line.slice!(0, r) + split_end.tr(" ", "\u00A0")
        line = split_start.tr(" ", "\u00A0") + line.lstrip
      end

      splitted << line
      splitted[0, (@config.max_messages || splitted.size)].each do |string|
        string.tr!("\u00A0", " ") # clean string from any non-breaking spaces
        raw("#{command} #{recipient} :#{string}")
      end
    else
      raw("#{command} #{recipient} :#{line}")
    end
  end
end
Also aliased as: privmsg, send
nick=(new_nick) click to toggle source
# File lib/cinch/bot.rb, line 557
def nick=(new_nick)
  if new_nick.size > @irc.isupport["NICKLEN"] && strict?
    raise Exceptions::NickTooLong, new_nick
  end
  @config.nick = new_nick
  raw "NICK #{new_nick}"
end
notice(recipient, text) click to toggle source

Sends a NOTICE to a recipient (a channel or user). You should be using {Channel#notice} and {User#notice} instead.

@param [String] recipient the recipient @param [String] text the message to send @return [void] @see Channel#notice @see User#notice @see safe_notice

# File lib/cinch/bot.rb, line 216
def notice(recipient, text)
  msg(recipient, text, true)
end
on(event, regexps = [], *args, &block) click to toggle source

Registers a handler.

@param [String, Symbol, Integer] event the event to match. Available

events are all IRC commands in lowercase as symbols, all numeric
replies, and the following:

  - :channel (a channel message)
  - :private (a private message)
  - :message (both channel and private messages)
  - :error   (handling errors, use a numeric error code as `match`)
  - :ctcp    (ctcp requests, use a ctcp command as `match`)

@param [Regexp, String, Integer] match every message of the

right event will be checked against this argument and the event
will only be called if it matches

@yieldparam [String] *args each capture group of the regex will

be one argument to the block. It is optional to accept them,
though

@return [void]

# File lib/cinch/bot.rb, line 306
def on(event, regexps = [], *args, &block)
  regexps = [*regexps]
  regexps = [//] if regexps.empty?

  event = event.to_sym

  regexps.map! do |regexp|
    pattern = case regexp
             when Pattern
               regexp
             when Regexp
               Pattern.new(nil, regexp, nil)
             else
               if event == :ctcp
                 Pattern.new(/^/, /#{Regexp.escape(regexp.to_s)}(?:$| .+)/, nil)
               else
                 Pattern.new(/^/, /#{Regexp.escape(regexp.to_s)}/, /$/)
               end
             end
    debug "[on handler] Registering handler with pattern `#{pattern.inspect}`, reacting on `#{event}`"
    pattern
  end
  (@events[event] ||= []) << [regexps, args, block]
end
part(channel, reason = nil) click to toggle source

Part a channel.

@param [String, Channel] channel either the name of a channel or a {Channel} object @param [String] reason an optional reason/part message @return [void] @see Channel#part

# File lib/cinch/bot.rb, line 461
def part(channel, reason = nil)
  Channel(channel).part(reason)
end
privmsg(recipient, text, notice = false) click to toggle source
Alias for: msg
quit(message = nil) click to toggle source

Disconnects from the server.

@param [String] message The quit message to send while quitting @return [void]

# File lib/cinch/bot.rb, line 390
def quit(message = nil)
  @quitting = true
  command = message ? "QUIT :#{message}" : "QUIT"
  raw command
end
raw(command) click to toggle source

Sends a raw message to the server.

@param [String] command The message to send. @return [void] @see IRC#message

# File lib/cinch/bot.rb, line 159
def raw(command)
  @irc.message(command)
end
register_plugin(plugin) click to toggle source

Registers a plugin.

@param [Class<Plugin>] plugin The plugin class to register @return [void]

# File lib/cinch/bot.rb, line 370
def register_plugin(plugin)
  @plugins << plugin.new(self)
end
register_plugins() click to toggle source

Register all plugins from `@config.plugins.plugins`.

@return [void]

# File lib/cinch/bot.rb, line 360
def register_plugins
  @config.plugins.plugins.each do |plugin|
    register_plugin(plugin)
  end
end
safe_action(recipient, text) click to toggle source

Like {action}, but remove any non-printable characters from `text`. The purpose of this method is to send text from untrusted sources, like other users or feeds.

Note: this will *break* any mIRC color codes embedded in the string.

@param (see action) @return (see action) @see action @see Channel#safe_action @see User#safe_action @todo Handle mIRC color codes more gracefully.

# File lib/cinch/bot.rb, line 278
def safe_action(recipient, text)
  action(recipient, Cinch.filter_string(text))
end
safe_msg(recipient, text) click to toggle source

Like {msg}, but remove any non-printable characters from `text`. The purpose of this method is to send text of untrusted sources, like other users or feeds.

Note: this will *break* any mIRC color codes embedded in the string.

@return (see msg) @param (see msg) @see msg @see User#safe_send @see Channel#safe_send @todo Handle mIRC color codes more gracefully.

# File lib/cinch/bot.rb, line 233
def safe_msg(recipient, text)
  msg(recipient, Cinch.filter_string(text))
end
Also aliased as: safe_privmsg, safe_send
safe_notice(recipient, text) click to toggle source

Like {safe_msg} but for notices.

@return (see safe_msg) @param (see safe_msg) @see safe_notice @see notice @see User#safe_notice @see Channel#safe_notice @todo (see safe_msg)

# File lib/cinch/bot.rb, line 248
def safe_notice(recipient, text)
  msg(recipient, Cinch.filter_string(text), true)
end
safe_privmsg(recipient, text) click to toggle source
Alias for: safe_msg
safe_send(recipient, text) click to toggle source
Alias for: safe_msg
secure?() click to toggle source

@return [Boolean] True if the bot is using SSL to connect to the

server.
# File lib/cinch/bot.rb, line 597
def secure?
  @config[:ssl] == true || (@config[:ssl].is_a?(Hash) && @config[:ssl][:use])
end
send(recipient, text, notice = false) click to toggle source
Alias for: msg
start(plugins = true) click to toggle source

Connects the bot to a server.

@param [Boolean] plugins Automatically register plugins from

`@config.plugins.plugins`?

@return [void] Connects the bot to a server.

@param [Boolean] plugins Automatically register plugins from

`@config.plugins.plugins`?

@return [void]

# File lib/cinch/bot.rb, line 406
def start(plugins = true)
  @reconnects = 0
  register_plugins if plugins

  begin
    @user_manager.each do |user|
      user.in_whois = false
      user.unsync_all
    end # reset state of all users

    @channel_manager.each do |channel|
      channel.unsync_all
    end # reset state of all channels

    @logger.debug "Connecting to #{@config.server}:#{@config.port}"
    @irc = IRC.new(self)
    @irc.connect

    if @config.reconnect && !@quitting
      # double the delay for each unsuccesful reconnection attempt
      if @last_connection_was_successful
        @reconnects = 0
        @last_connection_was_successful = false
      else
        @reconnects += 1
      end

      # Sleep for a few seconds before reconnecting to prevent being
      # throttled by the IRC server
      wait = 2**@reconnects
      @logger.debug "Waiting #{wait} seconds before reconnecting"
      sleep wait
    end
  end while @config.reconnect and not @quitting
end
strict?() click to toggle source

@return [Boolean] True if the bot reports ISUPPORT violations as

exceptions.
# File lib/cinch/bot.rb, line 475
def strict?
  @config.strictness == :strict
end
synchronize(name, &block) click to toggle source

Since Cinch uses threads, all handlers can be run simultaneously, even the same handler multiple times. This also means, that your code has to be thread-safe. Most of the time, this is not a problem, but if you are accessing stored data, you will most likely have to synchronize access to it. Instead of managing all mutexes yourself, Cinch provides a synchronize method, which takes a name and block.

Synchronize blocks with the same name share the same mutex, which means that only one of them will be executed at a time.

@param [String, Symbol] name a name for the synchronize block. @return [void] @yield

@example

configure do |c|
  …
  @i = 0
end

on :channel, /^start counting!/ do
  synchronize(:my_counter) do
    10.times do
      val = @i
      # at this point, another thread might've incremented :i already.
      # this thread wouldn't know about it, though.
      @i = val + 1
    end
  end
end
# File lib/cinch/bot.rb, line 137
def synchronize(name, &block)
  # Must run the default block +/ fetch in a thread safe way in order to
  # ensure we always get the same mutex for a given name.
  semaphore = @semaphores_mutex.synchronize { @semaphores[name] }
  semaphore.synchronize(&block)
end
unknown?() click to toggle source

This method is only provided in order to give Bot and User a common interface.

@return [false] Always returns `false`. @see User#unknown? See User#unknown? for the method's real use.

# File lib/cinch/bot.rb, line 606
def unknown?
  false
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.