# Copyright:: Copyright (c) 2006 Giuseppe Bilotta\r
# License:: GPLv2\r
\r
-#--\r
-#####\r
-####\r
-### Discussion on IRC on how to implement it\r
-##\r
-#\r
-# <tango_> a. do we want user groups together with users?\r
-# <markey> hmm\r
-# <markey> let me think about it\r
-# <markey> generally I would say: as simple as possible while keeping it as flexible as need be\r
-# <tango_> I think we can put user groups in place afterwards if we build the structure right\r
-# <markey> prolly, yes\r
-# <tango_> so\r
-# <tango_> each plugin registers a name\r
-# <tango_> so rather than auth level we have +name -name\r
-# <markey> yes\r
-# <markey> much better\r
-# <tango_> the default is +name for every plugin, except when the plugin tells otherwise\r
-# <markey> although.. \r
-# <markey> if I only want to allow you access to one plugin\r
-# <markey> I have lots of typing to do\r
-# <tango_> nope\r
-# <tango_> we allow things like -*\r
-# <markey> ok\r
-# <tango_> and + has precedence\r
-# <tango_> hm no, not good either\r
-# <tango_> because we want bot -* +onething and +* -onething to work\r
-# <markey> but then: one plugin currently can have several levels, no?\r
-# <tango_> of course\r
-# <markey> commandedit, commanddel, commandfoo\r
-# <tango_> name.command ?\r
-# <markey> yep\r
-# <tango_> (then you can't have dots in commands\r
-# <tango_> maybe name:command\r
-# <markey> or name::comand\r
-# <markey> like a namespace\r
-# <tango_> ehehehe yeah I like it :)\r
-# <tango_> tel\r
-# <tango_> brb\r
-# <markey> usermod setcaps eean -*\r
-# <markey> usermod setcaps eean +quiz::edit\r
-# <markey> great\r
-# <markey> or even\r
-# <markey> auth eean -*, +quiz::edit\r
-# <markey> awesome\r
-# <markey> auth eean -*, +quiz::edit, +command, -command::del\r
-# <tango_> yes\r
-# <markey> you know, the default should be -*\r
-# <markey> because\r
-# <markey> in the time between adding the user and changing auth\r
-# <markey> it's insecure\r
-# <markey> user could do havoc\r
-# <markey> useradd eean, then eean does "~quit", before I change auth\r
-# <tango_> nope\r
-# <markey> perhaps we should allow combining useradd with auth\r
-# <tango_> the default should be +* -important stuff\r
-# <markey> ok\r
-# <tango_> how to specify channel stuff?\r
-# <markey> for one, when you issue the command on the channel itself\r
-# <markey> then it's channel relative\r
-# <markey> perhaps\r
-# <markey> or\r
-# <tango_> yes but I was thinking more about the syntax\r
-# <markey> auth eean #rbot -quiz\r
-# <tango_> hm\r
-# <markey> or maybe: treat channels like users: auth #rbot -quiz\r
-# <markey> would shut up quiz in #rbot\r
-# <markey> hm\r
-# <markey> heh\r
-# <tango_> auth * #rbot -quiz\r
-# <markey> not sure I'm making sense here ;)\r
-# <tango_> I think syntax should be auth [usermask] [channelmask] [modes]\r
-# <markey> yes\r
-# <markey> modes separated by comma?\r
-# <tango_> where channelmask is implied to be *\r
-# <tango_> no we can have it spacesplit\r
-# <markey> great\r
-# <markey> ok\r
-# <tango_> modes are detected by +-\r
-# <tango_> so you can do something like auth markey #rbot -quiz #amarok -chuck\r
-# <markey> also I like "auth" a lot more than "usermod foo"\r
-# <markey> yep\r
-# <tango_> I don't understand why the 'mod'\r
-# <tango_> we could have all auth commands start with use\r
-# <tango_> user\r
-# <tango_> user add\r
-# <tango_> user list\r
-# <tango_> user del\r
-# <markey> yes\r
-# <tango_> user auth\r
-# <tango_> hm\r
-# <tango_> and maybe auth as a synonym for user auth\r
-# <markey> this is also uncomfortable: usermod wants the full user mask\r
-# <markey> you have to copy/paste it\r
-# <tango_> no\r
-# <tango_> can't you use *?\r
-# <markey> sorry not sure\r
-# <markey> but this shows, it's not inuitive\r
-# <markey> I've read the docs\r
-# <markey> but didn't know how to use it really\r
-# <tango_> markey!*@*\r
-# <markey> that's not very intuitive\r
-# <tango_> we could use nick as a synonym for nick!*@* if it's too much for you :D\r
-# <markey> usermod markey foo should suffice\r
-# <markey> rememember: you're a hacker. when rbot gets many new users, they will often be noobs\r
-# <markey> gotta make things simple to use\r
-# <tango_> but the hostmask is only needed for the user creation\r
-# <markey> really? then forget what I said, sorry\r
-# <tango_> I think so\r
-# <tango_> ,help auth\r
-# <testbot> Auth module (User authentication) topics: setlevel, useradd, userdel, usermod, auth, levels, users, whoami, identify\r
-# <tango_> ,help usermod\r
-# <testbot> no help for topic usermod\r
-# <tango_> ,help auth usermod\r
-# <testbot> usermod <username> <item> <value> => Modify <username>s settings. Valid <item>s are: hostmask, (+|-)hostmask, password, level (private addressing only)\r
-# <tango_> see? it's username, not nick :D\r
-# <markey> btw, help usermod should also work\r
-# <tango_> ,help auth useradd\r
-# <testbot> useradd <username> => Add user <mask>, you still need to set him up correctly (private addressing only)\r
-# <markey> instead of help auth usermode\r
-# <markey> when it's not ambiguous\r
-# <tango_> and the help for useradd is wrong\r
-# <markey> for the website, we could make a logo contest :) the current logo looks like giblet made it in 5 minutes ;)\r
-# <markey> ah well, for 1.0 maybe\r
-# <tango_> so a user on rbot is given by\r
-# <tango_> username, password, hostmasks, permissions\r
-# <markey> yup\r
-# <tango_> the default permission is +* -importantstuff\r
-# <markey> how defines importantstuff?\r
-# <markey> you mean like core and auth?\r
-# <tango_> yes\r
-# <markey> ok\r
-# <tango_> but we can decide about this :)\r
-# <markey> some plugins are dangerous by default\r
-# <markey> like command plugin\r
-# <markey> you can do all sorts of nasty shit with it\r
-# <tango_> then command plugin will do something like: command.defaultperm("-command")\r
-# <markey> yes, good point\r
-# <tango_> this is then added to the default permissions (user * channel *)\r
-# <tango_> when checking for auth, we go like this:\r
-# <tango_> hm\r
-# <tango_> check user * channel *\r
-# <tango_> then user name channel *\r
-# <tango_> then user * channel name\r
-# <tango_> then user name channel name\r
-# <tango_> for each of these combinations we match against * first, then against command, and then against command::subcommand\r
-# <markey> yup\r
-# <tango_> setting or resetting it depending on wether it's + or -\r
-# <tango_> the final result gives us the permission\r
-# <tango_> implementation detail\r
-# <tango_> username and passwords are strings\r
-# <markey> (I might rename the command plugin, the name is somewhat confusing)\r
-# <tango_> yeah\r
-# <tango_> hostmasks are hostmasks\r
-# <markey> also I'm pondering to restrict it more: disallow access to @bot\r
-# <tango_> permissions are in the form [ [channel, {command => bool, ...}] ...]\r
-#++\r
-\r
require 'singleton'\r
\r
module Irc\r
#\r
module Auth\r
\r
+ BotConfig.register BotConfigStringValue.new( 'auth.password',\r
+ :default => 'rbotauth', :wizard => true,\r
+ :desc => 'Password for the bot owner' )\r
+ # BotConfig.register BotConfigIntegerValue.new( 'auth.default_level',\r
+ # :default => 10, :wizard => true,\r
+ # :desc => 'The default level for new/unknown users' )\r
+\r
# Generate a random password of length _l_\r
#\r
def random_password(l=8)\r
@command = path.last\r
debug "Created command #{@command.inspect} with path #{@path.join(', ')}"\r
end\r
+\r
end\r
\r
# This method raises a TypeError if _user_ is not of class User\r
}\r
return allow\r
end\r
+\r
end\r
\r
\r
@perm = {}\r
end\r
\r
+ # Inspection\r
+ def inspect\r
+ str = "<#{self.class}:#{'0x%08x' % self.object_id}:"\r
+ str << " @username=#{@username.inspect}"\r
+ str << " @netmasks=#{@netmasks.inspect}"\r
+ str << " @perm=#{@perm.inspect}"\r
+ str\r
+ end\r
+\r
+ # Convert into a hash\r
+ def to_hash\r
+ {\r
+ :username => @username,\r
+ :password => @password,\r
+ :netmasks => @netmasks,\r
+ :perm => @perm\r
+ }\r
+ end\r
+\r
+ # Restore from hash\r
+ def from_hash(h)\r
+ @username = h[:username] if h.has_key?(:username)\r
+ @password = h[:password] if h.has_key?(:password)\r
+ @netmasks = h[:netmasks] if h.has_key?(:netmasks)\r
+ @perm = h[:perm] if h.has_key?(:perm)\r
+ end\r
+\r
+ # This method sets the password if the proposed new password\r
+ # is valid\r
+ def password=(pwd=nil)\r
+ if pwd\r
+ begin\r
+ raise InvalidPassword, "#{pwd} contains invalid characters" if pwd !~ /^[A-Za-z0-9]+$/\r
+ raise InvalidPassword, "#{pwd} too short" if pwd.length < 4\r
+ @password = pwd\r
+ rescue InvalidPassword => e\r
+ raise e\r
+ rescue => e\r
+ raise InvalidPassword, "Exception #{e.inspect} while checking #{pwd}"\r
+ end\r
+ else\r
+ reset_password\r
+ end\r
+ end\r
+\r
# Resets the password by creating a new onw\r
def reset_password\r
@password = random_password\r
when Netmask\r
@netmasks << mask\r
else\r
- @netmasks << Netmask(mask)\r
+ @netmasks << Netmask.new(mask)\r
end\r
end\r
\r
when Netmask\r
m = mask\r
else\r
- m << Netmask(mask)\r
+ m << Netmask.new(mask)\r
end\r
@netmasks.delete(m)\r
end\r
def login(user, password)\r
if password == @password\r
add_netmask(user) unless knows?(user)\r
+ debug "#{user} logged in as #{self.inspect}"\r
return true\r
else\r
return false\r
return name.to_s.chomp.downcase.gsub(/[^a-z0-9]/,"_")\r
end\r
\r
- # This method sets the password if the proposed new password\r
- # is valid\r
- def password=(pwd=nil)\r
- if pwd\r
- begin\r
- raise InvalidPassword, "#{pwd} contains invalid characters" if pwd !~ /^[A-Za-z0-9]+$/\r
- raise InvalidPassword, "#{pwd} too short" if pwd.length < 4\r
- @password = pwd\r
- rescue InvalidPassword => e\r
- raise e\r
- rescue => e\r
- raise InvalidPassword, "Exception #{e.inspect} while checking #{pwd}"\r
- end\r
- else\r
- reset_password\r
- end\r
- end\r
end\r
\r
\r
# identified with the bot\r
#\r
class DefaultBotUserClass < BotUser\r
+\r
+ private :login, :add_netmask, :delete_netmask\r
+\r
include Singleton\r
+\r
def initialize\r
super("everyone")\r
@default_perm = PermissionSet.new\r
end\r
- private :login, :add_netmask, :delete_netmask\r
\r
# Sets the default permission for the default user (i.e. the ones\r
# set by the BotModule writers) on all channels\r
end\r
return allow\r
end\r
+\r
end\r
\r
# Returns the only instance of DefaultBotUserClass\r
# This is the BotOwner: he can do everything\r
#\r
class BotOwnerClass < BotUser\r
+\r
include Singleton\r
+\r
def initialize\r
super("owner")\r
end\r
def permit?(cmd, chan=nil)\r
return true\r
end\r
+\r
end\r
\r
# Returns the only instance of BotOwnerClass\r
# everything\r
#\r
class AuthManagerClass\r
+\r
include Singleton\r
\r
attr_reader :everyone\r
@has_changes = false\r
end\r
\r
+ def set_changed\r
+ @has_changes = true\r
+ end\r
+\r
+ def reset_changed\r
+ @has_changes = false\r
+ end\r
+\r
+ def changed?\r
+ @has_changes\r
+ end\r
+\r
# resets the hashes\r
def reset_hashes\r
@botusers = Hash.new\r
}\r
end\r
\r
- # load botlist from userfile\r
- def load_merge(filename=nil)\r
- # TODO\r
- raise NotImplementedError\r
- @has_changes = true\r
- end\r
-\r
- def load(filename=nil)\r
+ def load_array(ary, forced)\r
+ raise "Won't load with unsaved changes" if @has_changes and not forced\r
reset_hashes\r
- load_merge(filename)\r
+ ary.each { |x|\r
+ raise TypeError, "#{x} should be a Hash" unless x.class <= Hash\r
+ u = x[:username]\r
+ unless include?(u)\r
+ create_botuser(u)\r
+ end\r
+ get_botuser(u).from_hash(x)\r
+ }\r
+ @has_changes=false\r
end\r
\r
- # save botlist to userfile\r
- def save(filename=nil)\r
- return unless @has_changes\r
- # TODO\r
- raise NotImplementedError\r
+ def save_array\r
+ @allbotusers.values.map { |x|\r
+ x.to_hash\r
+ }\r
end\r
\r
# checks if we know about a certain BotUser username\r
@allbotusers[k] = bu\r
end\r
\r
+ # returns the botuser with name _name_\r
+ def get_botuser(name)\r
+ @allbotusers.fetch(BotUser.sanitize_username(name).to_sym)\r
+ end\r
+\r
# Logs Irc::User _ircuser_ in to BotUser _botusername_ with password _pwd_\r
#\r
# raises an error if _botusername_ is not a known BotUser username\r
#\r
def login(ircuser, botusername, pwd, bymask = false)\r
Irc::error_if_not_user(ircuser)\r
- n = BotUser.sanitize_username(name)\r
+ n = BotUser.sanitize_username(botusername)\r
k = n.to_sym\r
raise "No such BotUser #{n}" unless include?(k)\r
if @botusers.has_key?(ircuser)\r
# @botusers[ircuser].logout(ircuser)\r
end\r
bu = @allbotusers[k]\r
- if bymask && bu.knows?(user)\r
+ if bymask && bu.knows?(ircuser)\r
@botusers[ircuser] = bu\r
return true\r
elsif bu.login(ircuser, pwd)\r
def allow?(cmdtxt, user, chan=nil)\r
permit?(user, cmdtxt, chan)\r
end\r
+\r
end\r
\r
# Returns the only instance of AuthManagerClass\r
def Auth.authmanager\r
return AuthManagerClass.instance\r
end\r
+\r
end\r
+\r
end\r
\r
class ConfigModule < CoreBotModule\r
\r
+ def save\r
+ @bot.config.save\r
+ end\r
+\r
def handle_list(m, params)\r
modules = []\r
if params[:module]\r
m.reply modules.join(", ")\r
end\r
else\r
- @bot.configitems.each_key do |key|\r
+ @bot.config.items.each_key do |key|\r
name = key.to_s.split('.').first\r
modules.push name unless modules.include?(name)\r
end\r
@bot.save\r
m.reply "rescanning ..."\r
@bot.rescan\r
- m.reply "done. #{@plugins.status(true)}"\r
+ m.reply "done. #{@bot.plugins.status(true)}"\r
end\r
\r
def bot_nick(m, param)\r
# globs is not handled yet.\r
# \r
def matches?(arg)\r
- cmp = Netmask(arg)\r
+ cmp = Netmask.new(arg)\r
raise TypeError, "#{arg} and #{self} have different casemaps" if @casemap != cmp.casemap\r
raise TypeError, "#{arg} is not a valid Netmask" unless cmp.class <= Netmask\r
[:nick, :user, :host].each { |component|\r
- us = self.send(:component)\r
- them = cmp.send(:component)\r
+ us = self.send(component)\r
+ them = cmp.send(component)\r
raise NotImplementedError if us.has_irc_glob? && them.has_irc_glob?\r
return false if us.has_irc_glob? && !them.has_irc_glob?\r
return false unless us =~ them.to_irc_regexp\r
# Case equality. Checks if arg matches self\r
#\r
def ===(arg)\r
- Netmask(arg).matches?(self)\r
+ Netmask.new(arg).matches?(self)\r
end\r
end\r
\r
# a name of\r
#\r
def user_or_channel?(name)\r
- if supports[:chantypes].include?(name[0].chr)\r
+ if supports[:chantypes].include?(name[0])\r
return Channel\r
else\r
return User\r
# Returns the actual User or Channel object matching _name_\r
#\r
def user_or_channel(name)\r
- if supports[:chantypes].include?(name[0].chr)\r
+ if supports[:chantypes].include?(name[0])\r
return channel(name)\r
else\r
return user(name)\r
# new_user(_str_, +false+)\r
#\r
def user(str)\r
- # This method can get called before server has been initialized (e.g. on\r
- # Freenode there is a NOTICE from AUTH on connect). In this case we just\r
- # return the string\r
- #\r
- if defined?(@supports)\r
- new_user(str, false)\r
- else\r
- str\r
- end\r
+ new_user(str, false)\r
end\r
\r
# Remove User _someuser_ from the list of <code>User</code>s.\r
# by default)
attr_reader :timer
+ # synchronize with this mutex while touching permanent data files:
+ # saving, flushing, cleaning up ...
+ attr_reader :save_mutex
+
# bot's Language data
attr_reader :lang
- # server the bot is connected to
- # TODO multiserver
- attr_reader :server
-
- # the client personality of the bot
- # TODO multiserver
- attr_reader :client
-
# bot's irc socket
# TODO multiserver
attr_reader :socket
# proxies etc as defined by the bot configuration/environment
attr_reader :httputil
+ # server we are connected to
+ # TODO multiserver
+ def server
+ @client.server
+ end
+
# bot User in the client/server connection
- attr_reader :myself
+ # TODO multiserver
+ def myself
+ @client.client
+ end
# bot User in the client/server connection
def nick
@registry = BotRegistry.new self
@timer = Timer::Timer.new(1.0) # only need per-second granularity
+ @save_mutex = Mutex.new
@timer.add(@config['core.save_every']) { save } if @config['core.save_every']
@logs = Hash.new
exit 2
end
@auth.everyone.set_default_permission("*", true)
+ @auth.botowner.password= @config['auth.password']
Dir.mkdir("#{botclass}/plugins") unless File.exist?("#{botclass}/plugins")
@plugins = Plugins::pluginmanager
@socket = IrcSocket.new(@config['server.name'], @config['server.port'], @config['server.bindhost'], @config['server.sendq_delay'], @config['server.sendq_burst'])
@client = IrcClient.new
- @server = @client.server
- @myself = @client.client
- @myself.nick = @config['irc.nick']
+ myself.nick = @config['irc.nick']
# Channels where we are quiet
# It's nil when we are not quiet, an empty list when we are quiet
# TODO this needs to go into rfc2812.rb
# Since capabs are two-steps processes, server.supports[:capab]
# should be a three-state: nil, [], [....]
- sendq "CAPAB IDENTIFY-MSG" if @server.supports[:capab]
+ sendq "CAPAB IDENTIFY-MSG" if server.supports[:capab]
}
@client[:datastr] = proc { |data|
# TODO this needs to go into rfc2812.rb
if data[:text] == "IDENTIFY-MSG"
- @server.capabilities["identify-msg".to_sym] = true
+ server.capabilities["identify-msg".to_sym] = true
else
debug "Not handling RPL_DATASTR #{data[:servermessage]}"
end
}
@client[:privmsg] = proc { |data|
- m = PrivMessage.new(self, @server, data[:source], data[:target], data[:message])
+ m = PrivMessage.new(self, server, data[:source], data[:target], data[:message])
+ debug "Message target is #{data[:target].inspect}"
+ debug "Bot is #{myself.inspect}"
# TODO use the new Netmask class
# @config['irc.ignore_users'].each { |mask| return if Irc.netmaskmatch(mask,m.source) }
@plugins.privmsg(m) if m.address?
}
@client[:notice] = proc { |data|
- message = NoticeMessage.new(self, @server, data[:source], data[:target], data[:message])
+ message = NoticeMessage.new(self, server, data[:source], data[:target], data[:message])
# pass it off to plugins that want to hear everything
@plugins.delegate "listen", message
}
source = data[:source]
old = data[:oldnick]
new = data[:newnick]
- m = NickMessage.new(self, @server, source, old, new)
+ m = NickMessage.new(self, server, source, old, new)
if source == myself
debug "my nick is now #{new}"
end
@client[:quit] = proc {|data|
source = data[:source]
message = data[:message]
- m = QuitMessage.new(self, @server, source, source, message)
+ m = QuitMessage.new(self, server, source, source, message)
data[:was_on].each { |ch|
irclog "@ Quit: #{source}: #{message}", ch
}
irclog "@ Mode #{data[:modestring]} by #{data[:source]}", data[:channel]
}
@client[:join] = proc {|data|
- m = JoinMessage.new(self, @server, data[:source], data[:channel], data[:message])
+ m = JoinMessage.new(self, server, data[:source], data[:channel], data[:message])
irclogjoin(m)
@plugins.delegate("listen", m)
@plugins.delegate("join", m)
}
@client[:part] = proc {|data|
- m = PartMessage.new(self, @server, data[:source], data[:channel], data[:message])
+ m = PartMessage.new(self, server, data[:source], data[:channel], data[:message])
irclogpart(m)
@plugins.delegate("listen", m)
@plugins.delegate("part", m)
}
@client[:kick] = proc {|data|
- m = KickMessage.new(self, @server, data[:source], data[:target], data[:channel],data[:message])
+ m = KickMessage.new(self, server, data[:source], data[:target], data[:channel],data[:message])
irclogkick(m)
@plugins.delegate("listen", m)
end
}
@client[:changetopic] = proc {|data|
- m = TopicMessage.new(self, @server, data[:source], data[:channel], data[:topic])
+ m = TopicMessage.new(self, server, data[:source], data[:channel], data[:topic])
irclogtopic(m)
@plugins.delegate("listen", m)
channel = data[:channel]
topic = channel.topic
irclog "@ Topic set by #{topic.set_by} on #{topic.set_on}", channel
- m = TopicMessage.new(self, @server, data[:source], channel, topic)
+ m = TopicMessage.new(self, server, data[:source], channel, topic)
@plugins.delegate("listen", m)
@plugins.delegate("topic", m)
end
stop_server_pings
- @server.clear
+ server.clear
if @socket.connected?
@socket.clearq
@socket.shutdown
@socket.shutdown
end
debug "Logging quits"
- @server.channels.each { |ch|
+ server.channels.each { |ch|
irclog "@ quit (#{message})", ch
}
debug "Saving"
# call the save method for bot's config, keywords, auth and all plugins
def save
- @config.save
- # @keywords.save
- @auth.save
- @plugins.save
- DBTree.cleanup_logs
+ @save_mutex.synchronize do
+ # @config.save
+ # @keywords.save
+ # @auth.save
+ @plugins.save
+ DBTree.cleanup_logs
+ end
end
# call the rescan method for the bot's lang, keywords and all plugins
case where
when Channel
irclog "-=#{myself}=- #{message}", where
- when User
- irclog "[-=#{where}=-] #{message}", $1
else
irclog "[-=#{where}=-] #{message}", where
end
case where
when Channel
irclog "<#{myself}> #{message}", where
- when User
- irclog "[msg(#{where})] #{message}", $1
else
irclog "[msg(#{where})] #{message}", where
end
else
raise ArgumentError, "Can't find auth base in #{botmodule.inspect}"
end
- post = items.reject{ |x|
+ words = items.reject{ |x|
x == pre || x.kind_of?(Symbol)
}
- if post.empty?
+ if words.empty?
post = nil
else
- post = post.first
+ post = words.first
end
if hash.has_key?(:auth_path)
extra = hash[:auth_path]
+ if extra.sub!(/^:/, "")
+ pre += post
+ post = nil
+ end
+ if extra.sub!(/:$/, "")
+ post = [post,words[1]].compact.join("::") if words.length > 1
+ end
pre = nil if extra.sub!(/^!/, "")
post = nil if extra.sub!(/!$/, "")
else
module Irc
BotConfig.register BotConfigArrayValue.new('plugins.blacklist',
- :default => [], :wizard => false, :requires_restart => true,
+ :default => [], :wizard => false, :requires_rescan => true,
:desc => "Plugins that should not be loaded")
module Plugins
require 'rbot/messagemapper'
# call the cleanup method for each active plugin
def cleanup
- delegate 'cleanup'
+ @bot.save_mutex.synchronize do
+ delegate 'cleanup'
+ end
reset_botmodule_lists
end
# debug "#{p.botmodule_class} #{p.name} responds"
p.send method, *args
rescue Exception => err
+ raise if err.class <= SystemExit
error report_error("#{p.botmodule_class} #{p.name} #{method}() failed:", err)
raise if err.class <= BDB::Fatal
end
def privmsg(m)
# debug "Delegating privmsg #{m.message.inspect} from #{m.source} to #{m.replyto} with pluginkey #{m.plugin.inspect}"
return unless m.plugin
- begin
- [core_commands, plugin_commands].each { |pl|
- # We do it this way to skip creating spurious keys
- # FIXME use fetch?
- k = m.plugin.to_sym
- if pl.has_key?(k)
- p = pl[k][:botmodule]
- a = pl[k][:auth]
- else
- p = nil
- a = nil
- end
- if p
- # We check here for things that don't check themselves
- # (e.g. mapped things)
- # debug "Checking auth ..."
- if a.nil? || @bot.auth.allow?(a, m.source, m.replyto)
- # debug "Checking response ..."
- if p.respond_to?("privmsg")
- begin
- # debug "#{p.botmodule_class} #{p.name} responds"
- p.privmsg(m)
- rescue Exception => err
- error report_error("#{p.botmodule_class} #{p.name} privmsg() failed:", err)
- raise if err.class <= BDB::Fatal
- end
- # debug "Successfully delegated #{m.message}"
- return true
- else
- # debug "#{p.botmodule_class} #{p.name} is registered, but it doesn't respond to privmsg()"
+ [core_commands, plugin_commands].each { |pl|
+ # We do it this way to skip creating spurious keys
+ # FIXME use fetch?
+ k = m.plugin.to_sym
+ if pl.has_key?(k)
+ p = pl[k][:botmodule]
+ a = pl[k][:auth]
+ else
+ p = nil
+ a = nil
+ end
+ if p
+ # We check here for things that don't check themselves
+ # (e.g. mapped things)
+ # debug "Checking auth ..."
+ if a.nil? || @bot.auth.allow?(a, m.source, m.replyto)
+ # debug "Checking response ..."
+ if p.respond_to?("privmsg")
+ begin
+ # debug "#{p.botmodule_class} #{p.name} responds"
+ p.privmsg(m)
+ rescue Exception => err
+ raise if err.class <= SystemExit
+ error report_error("#{p.botmodule_class} #{p.name} privmsg() failed:", err)
+ raise if err.class <= BDB::Fatal
end
+ # debug "Successfully delegated #{m.message}"
+ return true
else
- # debug "#{p.botmodule_class} #{p.name} is registered, but #{m.source} isn't allowed to call #{m.plugin.inspect} on #{m.replyto}"
+ # debug "#{p.botmodule_class} #{p.name} is registered, but it doesn't respond to privmsg()"
end
else
- # debug "No #{pl.values.first[:botmodule].botmodule_class} registered #{m.plugin.inspect}" unless pl.empty?
+ # debug "#{p.botmodule_class} #{p.name} is registered, but #{m.source} isn't allowed to call #{m.plugin.inspect} on #{m.replyto}"
end
- # debug "Finished delegating privmsg with key #{m.plugin.inspect}" + ( pl.empty? ? "" : " to #{pl.values.first[:botmodule].botmodule_class}s" )
- }
- return false
- rescue Exception => e
- error report_error("couldn't delegate #{m.message.inspect}", e)
- end
+ else
+ # debug "No #{pl.values.first[:botmodule].botmodule_class} registered #{m.plugin.inspect}" unless pl.empty?
+ end
+ # debug "Finished delegating privmsg with key #{m.plugin.inspect}" + ( pl.empty? ? "" : " to #{pl.values.first[:botmodule].botmodule_class}s" )
+ }
+ return false
# debug "Finished delegating privmsg with key #{m.plugin.inspect}"
end
end
end
def flush
+ # debug "fushing registry #{@registry}"
@registry.flush
@registry.sync
end
def close
+ # debug "closing registry #{@registry}"
@registry.close
end
data[:nick] = $2
data[:address] = $3
@client = @server.user(data[:netmask])
+ set = true
when /Welcome to the Internet Relay Network\s(\S+)/
data[:nick] = $1
when /Welcome.*\s+(\S+)$/
when /^(\S+)$/
data[:nick] = $1
end
- @user ||= @server.user(data[:nick])
+ @client = @server.user(data[:nick]) unless set
handle(:welcome, data)
when RPL_YOURHOST
# "Your host is <servername>, running version <ver>"
users.each { |ar|
u = @server.user(ar[0])
- chan.users << u
+ chan.users << u unless chan.users.include?(u)
if ar[1]
m = @server.supports[:prefix][:prefixes].index(ar[1].to_sym)
m = @server.supports[:prefix][:modes][m]