# Module for vrobot4 bot modules. module Vrobot4::Module # A command function. Holds extra info such as help and permissions. class Command attr_reader :help_str # @return [String] help string for this command # @param fn [Method] method to be called on run # @param help [String] help string for this command # @param roles [String] list of roles that can use this command def initialize fn, help, roles @function = fn @help_str = help @roles = /[#{roles}]/ end # Checks if this command is usable in a given context. # @param m [Vrobot4::Server::Message] message for context # @return [Boolean] true if command is usable, false otherwise def usable_in? m m.user.roles.scan(@roles).any? and @function.owner.usable_in? m end # Calls the method attached to this command. # @param m [Vrobot4::Server::Message] message for context # @param argv [String] argument string # @return [void] def run m, argv @function.call(m, argv) end end # A bot module. Holds commands and callbacks. class Module # Checks if this module is usable in a given context. # @param m [Vrobot4::Server::Message] message for context # @return [Boolean] true if usable, false otherwise def self.usable_in? m role = m.user.roles mprm = m.serv.mprm retm, retc, retr = true, false, false retm = mprm[:glob][self] if mprm[:glob].key? self retc = mprm[:chan][self][m.chan] if mprm[:chan].key? self retr = mprm[:role][self].scan /[#{role}]/ if mprm[:role].key? self retm or retc or retr end # @param info [Hash] arbitrary extra information for this module def initialize info @commands = {} @info = info Vrobot4.log :DEBUG, "initialized #{self.to_s}" end # Callback for message receiving. # @param m [Vrobot4::Server::Message] message received # @return [Boolean] if this should block further message callbacks def on_message m false end # Callback for command receiving. # @param m [Vrobot4::Server::Message] message that triggered this call # @param cnam [String] name of command to call # @param argv [String] array of string arguments # @return [Boolean] if this should block further command callbacks def on_command m, cnam, argv if (cmd = cmds(m)[cnam]) begin cmd.run(m, argv) rescue m.reply "Error:", $!.to_s end true else false end end # Gets all commands usable in a specified context. # @param m [Vrobot4::Server::Message] message for context # @return [Hash] # a hash of all commands by name def cmds m @commands.select do |name, cmd| cmd.usable_in? m end end protected # Registers a command into this module. # @param fn [Symbol] name of method to add # @param names [String, Array] name(s) to add this command by # @param help [String] help string to use for this command # @param roles [String] roles to allow this command to use # @return [Vrobot4::Module::Command] command added def register fn, names, help = nil, roles: ?v help = "No help available for this command." if help == nil cmd = Command.new(self.method(fn), help, roles).freeze if names.is_a? String @commands[names] = cmd else names.each {|name| @commands[name] = cmd} end cmd end # Check argument validity and count. # # Specifiers include: # [N] A number. # [S] An arbitrary string. # [*] Any amount of arbitrary arguments. Must be the last optional arg. # # @param argv [String, Array] array of arguments or a string # @param req [String] specifier string of required arguments # @param opt [String] specifier string of optional arguments # @return [String, Array] argv def check_args argv, req, opt = "" if argv.is_a? Array then if argv.length < req.length raise ArgumentError, "Not enough arguments" elsif opt[-1] != '*' and argv.length > req.length + opt.length raise ArgumentError, "Too many arguments" end for i in (0 ... req.length) check_arg argv[i], i, req[i] end for i in (0 ... argv.length - req.length) break if opt[i] == '*' argn = i + req.length check_arg argv[argn], argn, opt[i] end else check_arg argv, 0, req[0] end argv end private # Helper function for {#check_args}. # @param arg [String] the argument to check # @param i [Integer] which argument this is # @param req [String] type that the argument should be # @return [void] def check_arg arg, i, req case req when ?N unless Vrobot4.is_num? arg.strip raise ArgumentError, "Expected a number for arg #{i}" end when ?S # Don't need to check anything here. else raise ArgumentError, "Invalid argument specifier #{req}" end end end # Adds a module type to the global list. # @param type [Class] module type to add # @param server [String] server type this module is restricted to (or none) # @param servflags [String] flags the server must have to use this (or none) # @return [void] def self.add_module_type type, server: nil, servflags: nil @@module_types[type.type] = { type: type, server: server, servflags: servflags ? /[#{servflags}]/ : nil } Vrobot4.log :INFO, "added module type #{type.type}" end # Gets a module type by name from the global list. # @param name [String] name of the module type to find # @return [Hash] a hash containing info about the type def self.get_module_type name @@module_types[name] end # The set of all module types. @@module_types = {} end ## EOF