X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Firc.rb;h=0cf70d5a6253e7a086b2f8ad0ac868b7d3ac7cf2;hb=7f4e98a691ba6ee6f220fec982f17c900c929f1d;hp=5d3b01ec1a1139551867b507fafaaa20c62390c3;hpb=0b705e79109fa004bf8956ef65288cf00a9f3312;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git
diff --git a/lib/rbot/irc.rb b/lib/rbot/irc.rb
index 5d3b01ec..0cf70d5a 100644
--- a/lib/rbot/irc.rb
+++ b/lib/rbot/irc.rb
@@ -4,6 +4,8 @@
# 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
+# * Maybe ChannelList and UserList should be HashesOf instead of ArrayOf?
+# See items marked as TODO Ho
#++
# :title: IRC module
#
@@ -17,6 +19,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
@@ -84,11 +96,18 @@ module Irc
@key.to_s
end
+ # Two Casemaps are equal if they have the same upper and lower ranges
+ #
+ def ==(arg)
+ other = arg.to_irc_casemap
+ return self.upper == other.upper && self.lower == other.lower
+ end
+
# Raise an error if _arg_ and self are not the same Casemap
#
def must_be(arg)
other = arg.to_irc_casemap
- raise "Casemap mismatch (#{self} != #{other})" unless self == other
+ raise "Casemap mismatch (#{self.inspect} != #{other.inspect})" unless self == other
return true
end
@@ -176,14 +195,18 @@ module Irc
# @server (if possible) or at the @casemap otherwise
#
def casemap
- @server.casemap rescue @casemap
+ return @server.casemap if defined?(@server) and @server
+ return @casemap
end
# Returns a hash with the current @server and @casemap as values of
# :server and :casemap
#
def server_and_casemap
- {:server => @server, :casemap => @casemap}
+ h = {}
+ h[:server] = @server if defined?(@server) and @server
+ h[:casemap] = @casemap if defined?(@casemap) and @casemap
+ return h
end
# We allow up/downcasing with a different casemap
@@ -531,10 +554,10 @@ module Irc
#
def inspect
str = "<#{self.class}:#{'0x%x' % self.object_id}:"
- str << " @server=#{@server}" if @server
+ str << " @server=#{@server}" if defined?(@server) and @server
str << " @nick=#{@nick.inspect} @user=#{@user.inspect}"
- str << " @host=#{@host.inspect} casemap=#{casemap.inspect}>"
- str
+ str << " @host=#{@host.inspect} casemap=#{casemap.inspect}"
+ str << ">"
end
# Equality: two Netmasks are equal if they downcase to the same thing
@@ -612,10 +635,15 @@ module Irc
#
def matches?(arg)
cmp = arg.to_irc_netmask(:casemap => casemap)
+ debug "Matching #{self.fullform} against #{arg.fullform}"
[:nick, :user, :host].each { |component|
us = self.send(component).irc_downcase(casemap)
them = cmp.send(component).irc_downcase(casemap)
- raise NotImplementedError if us.has_irc_glob? && them.has_irc_glob?
+ if us.has_irc_glob? && them.has_irc_glob?
+ next if us == them
+ warn NotImplementedError
+ return false
+ end
return false if us.has_irc_glob? && !them.has_irc_glob?
return false unless us =~ them.to_irc_regexp
}
@@ -770,14 +798,14 @@ module Irc
def replace(other)
case other
when User
- nick = other.nick
- user = other.user
- host = other.host
+ self.nick = other.nick
+ self.user = other.user
+ self.host = other.host
@server = other.server
@casemap = other.casemap unless @server
- @away = other.away
+ @away = other.away?
else
- replace(other.to_irc_user(server_and_casemap))
+ self.replace(other.to_irc_user(server_and_casemap))
end
end
@@ -835,6 +863,8 @@ module Irc
# Channel modes of type A manipulate lists
#
+ # Example: b (banlist)
+ #
class ModeTypeA < Mode
def initialize(ch)
super
@@ -856,6 +886,8 @@ module Irc
# Channel modes of type B need an argument
#
+ # Example: k (key)
+ #
class ModeTypeB < Mode
def initialize(ch)
super
@@ -900,6 +932,8 @@ 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
@@ -923,6 +957,8 @@ module Irc
# Channel modes of type D are basically booleans
#
+ # Example: m (moderate)
+ #
class ModeTypeD < Mode
def initialize(ch)
super
@@ -1008,8 +1044,8 @@ 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
+ str << " @users=[#{user_nicks.sort.join(', ')}]"
+ str << ">"
end
# Returns self
@@ -1018,6 +1054,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.
#
@@ -1038,7 +1103,7 @@ module Irc
@users = UserList.new
users.each { |u|
- @users << u.to_irc_user(server_and_casemap)
+ add_user(u)
}
# Flags
@@ -1063,25 +1128,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
@@ -1134,10 +1199,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
@@ -1154,8 +1221,8 @@ module Irc
str = "<#{self.class}:#{'0x%x' % self.object_id}:"
str << " @hostname=#{hostname}"
str << " @channels=#{chans}"
- str << " @users=#{users}>"
- str
+ str << " @users=#{users}"
+ str << ">"
end
# Create a new Server, with all instance variables reset to nil (for
@@ -1176,7 +1243,7 @@ module Irc
#
def reset_capabilities
@supports = {
- :casemapping => 'rfc1459',
+ :casemapping => 'rfc1459'.to_irc_casemap,
:chanlimit => {},
:chanmodes => {
:typea => nil, # Type A: address lists
@@ -1269,27 +1336,18 @@ module Irc
key = prekey.downcase.to_sym
end
case key
- when :casemapping, :network
+ when :casemapping
noval_warn(key, val) {
- @supports[key] = val
+ @supports[key] = val.to_irc_casemap
}
when :chanlimit, :idchan, :maxlist, :targmax
noval_warn(key, val) {
groups = val.split(',')
groups.each { |g|
k, v = g.split(':')
- @supports[key][k] = v.to_i
+ @supports[key][k] = v.to_i || 0
}
}
- when :maxchannels
- noval_warn(key, val) {
- reparse += "CHANLIMIT=(chantypes):#{val} "
- }
- when :maxtargets
- noval_warn(key, val) {
- @supports[key]['PRIVMSG'] = val.to_i
- @supports[key]['NOTICE'] = val.to_i
- }
when :chanmodes
noval_warn(key, val) {
groups = val.split(',')
@@ -1312,6 +1370,19 @@ module Irc
when :invex
val ||= 'I'
@supports[key] = val
+ when :maxchannels
+ noval_warn(key, val) {
+ reparse += "CHANLIMIT=(chantypes):#{val} "
+ }
+ when :maxtargets
+ noval_warn(key, val) {
+ @supports[:targmax]['PRIVMSG'] = val.to_i
+ @supports[:targmax]['NOTICE'] = val.to_i
+ }
+ when :network
+ noval_warn(key, val) {
+ @supports[key] = val
+ }
when :nicklen
noval_warn(key, val) {
@supports[key] = val.to_i
@@ -1376,13 +1447,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
@@ -1391,9 +1464,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
@@ -1480,7 +1559,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
@@ -1493,22 +1573,31 @@ 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}"
+ # debug "Old: #{old.inspect}"
if old
# debug "User already existed as #{old.inspect}"
if tmp.known?
if old.known?
+ # debug "Both were known"
# Do not raise an error: things like Freenode change the hostname after identification
warning "User #{tmp.nick} has inconsistent Netmasks! #{self} knows #{old.inspect} but access was tried with #{tmp.inspect}" if old != tmp
raise "User #{tmp} already exists on server #{self}" if fails
end
- if old != tmp
+ if old.fullform.downcase != tmp.fullform.downcase
old.replace(tmp)
- # debug "User improved to #{old.inspect}"
+ # debug "Known user now #{old.inspect}"
end
end
return old
@@ -1559,7 +1648,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