X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Firc.rb;h=b8ca459304d453b6e8e183ed656b684a1e7da882;hb=9c50738a84bec26402902513a4cd21b54dcc0a80;hp=8ef55712cf48affeb815482241a1635b3f26d51c;hpb=624c16b4547b2ffa7f47ca77d725658c104809ac;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git
diff --git a/lib/rbot/irc.rb b/lib/rbot/irc.rb
index 8ef55712..b8ca4593 100644
--- a/lib/rbot/irc.rb
+++ b/lib/rbot/irc.rb
@@ -3,7 +3,11 @@
# * do we want to handle a Channel list for each User telling which
# Channels is the User on (of those the client is on too)?
# We may want this so that when a User leaves all Channels and he hasn't
-# sent us privmsgs, we know remove him from the Server @users list
+# sent us privmsgs, we know we can remove him from the Server @users list
+# * Maybe ChannelList and UserList should be HashesOf instead of ArrayOf?
+# See items marked as TODO Ho.
+# The framework to do this is now in place, thanks to the new [] method
+# for NetmaskList, which allows retrieval by Netmask or String
#++
# :title: IRC module
#
@@ -17,6 +21,16 @@
require 'singleton'
+class Object
+
+ # We extend the Object class with a method that
+ # checks if the receiver is nil or empty
+ def nil_or_empty?
+ return true unless self
+ return true if self.respond_to? :empty and self.empty?
+ return false
+ end
+end
# The Irc module is used to keep all IRC-related classes
# in the same namespace
@@ -447,6 +461,13 @@ class ArrayOf < Array
}
end
+ # We introduce the 'downcase' method, which maps downcase() to all the Array
+ # elements, properly failing when the elements don't have a downcase method
+ #
+ def downcase
+ self.map { |el| el.downcase }
+ end
+
# Modifying methods which we don't handle yet are made private
#
private :[]=, :collect!, :map!, :fill, :flatten!
@@ -526,7 +547,7 @@ module Irc
if self.class == Netmask
return self if fits_with_server_and_casemap?(opts)
end
- return self.fullform.to_irc_netmask(server_and_casemap.merge(opts))
+ return self.downcase.to_irc_netmask(opts)
end
# Converts the receiver into a User with the given (optional)
@@ -669,6 +690,35 @@ module Irc
super(Netmask, ar)
end
+ # We enhance the [] method by allowing it to pick an element that matches
+ # a given Netmask, a String or a Regexp
+ # TODO take into consideration the opportunity to use select() instead of
+ # find(), and/or a way to let the user choose which one to take (second
+ # argument?)
+ #
+ def [](*args)
+ if args.length == 1
+ case args[0]
+ when Netmask
+ self.find { |mask|
+ mask.matches?(args[0])
+ }
+ when String
+ self.find { |mask|
+ mask.matches?(args[0].to_irc_netmask(:casemap => mask.casemap))
+ }
+ when Regexp
+ self.find { |mask|
+ mask.fullform =~ args[0]
+ }
+ else
+ super(*args)
+ end
+ else
+ super(*args)
+ end
+ end
+
end
end
@@ -771,6 +821,21 @@ module Irc
end
end
+ # Users can be either simply downcased (their nick only)
+ # or fully downcased: this will return the fullform downcased
+ # according to the given casemap.
+ #
+ def full_irc_downcase(cmap=casemap)
+ self.fullform.irc_downcase(cmap)
+ end
+
+ # full_downcase() will return the fullform downcased according to the
+ # User's own casemap
+ #
+ def full_downcase
+ self.full_irc_downcase
+ end
+
# Since to_irc_user runs the same checks on server and channel as
# to_irc_netmask, we just try that and return self if it works.
#
@@ -778,7 +843,7 @@ module Irc
#
def to_irc_user(opts={})
return self if fits_with_server_and_casemap?(opts)
- return self.fullform.to_irc_user(server_and_casemap(opts))
+ return self.full_downcase.to_irc_user(opts)
end
# We can replace everything at once with data from another User
@@ -801,14 +866,24 @@ module Irc
# A UserList is an ArrayOf User
s
+ # We derive it from NetmaskList, which allows us to inherit any special
+ # NetmaskList method
#
- class UserList < ArrayOf
+ class UserList < NetmaskList
# Create a new UserList, optionally filling it with the elements from
# the Array argument fed to it.
#
def initialize(ar=[])
- super(User, ar)
+ super(ar)
+ @element_class = User
+ end
+
+ # Convenience method: convert the UserList to a list of nicks. The indices
+ # are preserved
+ #
+ def nicks
+ self.map { |user| user.nick }
end
end
@@ -842,6 +917,7 @@ module Irc
# Mode on a Channel
#
class Mode
+ attr_reader :channel
def initialize(ch)
@channel = ch
end
@@ -851,7 +927,10 @@ module Irc
# Channel modes of type A manipulate lists
#
+ # Example: b (banlist)
+ #
class ModeTypeA < Mode
+ attr_reader :list
def initialize(ch)
super
@list = NetmaskList.new
@@ -872,12 +951,19 @@ module Irc
# Channel modes of type B need an argument
#
+ # Example: k (key)
+ #
class ModeTypeB < Mode
def initialize(ch)
super
@arg = nil
end
+ def status
+ @arg
+ end
+ alias :value :status
+
def set(val)
@arg = val
end
@@ -895,6 +981,8 @@ module Irc
# modes of type A
#
class UserMode < ModeTypeB
+ attr_reader :list
+ alias :users :list
def initialize(ch)
super
@list = UserList.new
@@ -916,22 +1004,25 @@ module Irc
# Channel modes of type C need an argument when set,
# but not when they get reset
#
+ # Example: l (limit)
+ #
class ModeTypeC < Mode
def initialize(ch)
super
- @arg = false
+ @arg = nil
end
def status
@arg
end
+ alias :value :status
def set(val)
@arg = val
end
def reset
- @arg = false
+ @arg = nil
end
end
@@ -939,6 +1030,8 @@ module Irc
# Channel modes of type D are basically booleans
#
+ # Example: m (moderate)
+ #
class ModeTypeD < Mode
def initialize(ch)
super
@@ -1024,7 +1117,7 @@ module Irc
str = "<#{self.class}:#{'0x%x' % self.object_id}:"
str << " on server #{server}" if server
str << " @name=#{@name.inspect} @topic=#{@topic.text.inspect}"
- str << " @users=[#{@users.sort.join(', ')}]"
+ str << " @users=[#{user_nicks.sort.join(', ')}]"
str << ">"
end
@@ -1034,6 +1127,35 @@ module Irc
self
end
+ # TODO Ho
+ def user_nicks
+ @users.map { |u| u.downcase }
+ end
+
+ # Checks if the receiver already has a user with the given _nick_
+ #
+ def has_user?(nick)
+ user_nicks.index(nick.irc_downcase(casemap))
+ end
+
+ # Returns the user with nick _nick_, if available
+ #
+ def get_user(nick)
+ idx = has_user?(nick)
+ @users[idx] if idx
+ end
+
+ # Adds a user to the channel
+ #
+ def add_user(user, opts={})
+ silent = opts.fetch(:silent, false)
+ if has_user?(user) && !silent
+ warn "Trying to add user #{user} to channel #{self} again"
+ else
+ @users << user.to_irc_user(server_and_casemap)
+ end
+ end
+
# Creates a new channel with the given name, optionally setting the topic
# and an initial users list.
#
@@ -1054,7 +1176,7 @@ module Irc
@users = UserList.new
users.each { |u|
- @users << u.to_irc_user(server_and_casemap)
+ add_user(u)
}
# Flags
@@ -1079,25 +1201,25 @@ module Irc
# A channel is local to a server if it has the '&' prefix
#
def local?
- name[0] = 0x26
+ name[0] == 0x26
end
# A channel is modeless if it has the '+' prefix
#
def modeless?
- name[0] = 0x2b
+ name[0] == 0x2b
end
# A channel is safe if it has the '!' prefix
#
def safe?
- name[0] = 0x21
+ name[0] == 0x21
end
# A channel is normal if it has the '#' prefix
#
def normal?
- name[0] = 0x23
+ name[0] == 0x23
end
# Create a new mode
@@ -1120,6 +1242,13 @@ module Irc
super(Channel, ar)
end
+ # Convenience method: convert the ChannelList to a list of channel names.
+ # The indices are preserved
+ #
+ def names
+ self.map { |chan| chan.name }
+ end
+
end
end
@@ -1150,10 +1279,12 @@ module Irc
attr_reader :channels, :users
+ # TODO Ho
def channel_names
@channels.map { |ch| ch.downcase }
end
+ # TODO Ho
def user_nicks
@users.map { |u| u.downcase }
end
@@ -1294,7 +1425,7 @@ module Irc
groups = val.split(',')
groups.each { |g|
k, v = g.split(':')
- @supports[key][k] = v.to_i
+ @supports[key][k] = v.to_i || 0
}
}
when :chanmodes
@@ -1325,8 +1456,8 @@ module Irc
}
when :maxtargets
noval_warn(key, val) {
- @supports[key]['PRIVMSG'] = val.to_i
- @supports[key]['NOTICE'] = val.to_i
+ @supports[:targmax]['PRIVMSG'] = val.to_i
+ @supports[:targmax]['NOTICE'] = val.to_i
}
when :network
noval_warn(key, val) {
@@ -1396,13 +1527,15 @@ module Irc
# Checks if the receiver already has a channel with the given _name_
#
def has_channel?(name)
- channel_names.index(name.downcase)
+ return false if name.nil_or_empty?
+ channel_names.index(name.irc_downcase(casemap))
end
alias :has_chan? :has_channel?
# Returns the channel with name _name_, if available
#
def get_channel(name)
+ return nil if name.nil_or_empty?
idx = has_channel?(name)
channels[idx] if idx
end
@@ -1411,9 +1544,15 @@ module Irc
# Create a new Channel object bound to the receiver and add it to the
# list of Channel
s on the receiver, unless the channel was
# present already. In this case, the default action is to raise an
- # exception, unless _fails_ is set to false
+ # exception, unless _fails_ is set to false. An exception can also be
+ # raised if _str_ is nil or empty, again only if _fails_ is set to true;
+ # otherwise, the method just returns nil
#
def new_channel(name, topic=nil, users=[], fails=true)
+ if name.nil_or_empty?
+ raise "Tried to look for empty or nil channel name #{name.inspect}" if fails
+ return nil
+ end
ex = get_chan(name)
if ex
raise "Channel #{name} already exists on server #{self}" if fails
@@ -1500,7 +1639,8 @@ module Irc
# Checks if the receiver already has a user with the given _nick_
#
def has_user?(nick)
- user_nicks.index(nick.downcase)
+ return false if nick.nil_or_empty?
+ user_nicks.index(nick.irc_downcase(casemap))
end
# Returns the user with nick _nick_, if available
@@ -1513,9 +1653,15 @@ module Irc
# Create a new User object bound to the receiver and add it to the list
# of User
s on the receiver, unless the User was present
# already. In this case, the default action is to raise an exception,
- # unless _fails_ is set to false
+ # unless _fails_ is set to false. An exception can also be raised
+ # if _str_ is nil or empty, again only if _fails_ is set to true;
+ # otherwise, the method just returns nil
#
def new_user(str, fails=true)
+ if str.nil_or_empty?
+ raise "Tried to look for empty or nil user name #{str.inspect}" if fails
+ return nil
+ end
tmp = str.to_irc_user(:server => self)
old = get_user(tmp.nick)
# debug "Tmp: #{tmp.inspect}"
@@ -1582,7 +1728,7 @@ module Irc
@users.inject(UserList.new) {
|list, user|
if user.user == "*" or user.host == "*"
- list << user if user.nick.downcase =~ nm.nick.downcase.to_irc_regexp
+ list << user if user.nick.irc_downcase(casemap) =~ nm.nick.irc_downcase(casemap).to_irc_regexp
else
list << user if user.matches?(nm)
end