vrobot4/source/module.rb

188 lines
6.3 KiB
Ruby

# 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<String, Vrobot4::Module::Command>]
# 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