diff --git a/source/common.rb b/source/common.rb index ce39f63..0e9f1a9 100644 --- a/source/common.rb +++ b/source/common.rb @@ -11,6 +11,10 @@ module Vrobot4 puts "[" + lv.to_s.ljust(6) + "] " + text.join(" ") end end + + def self.is_num? s + /\A[-+]?[0-9]*\.?[0-9]+\Z/ === s + end end ## EOF diff --git a/source/main.rb b/source/main.rb index d60d0df..b7d73ca 100644 --- a/source/main.rb +++ b/source/main.rb @@ -1,6 +1,6 @@ require './common.rb' -require './server.rb' require './module.rb' +require './server.rb' require 'yaml' @@ -10,8 +10,8 @@ module Vrobot4 serv = Server::get_server_type(type).new(servinfo) - servinfo.has_key?("modules") && servinfo["modules"].each \ - {|mod| serv.loadMod Module::get_module_type(mod).new} + servinfo["modules"].each \ + {|mod| serv.load_mod mod} if servinfo.key? "modules" serv end @@ -38,7 +38,7 @@ module Vrobot4 begin; cfg = YAML.load IO.read(cfgname) rescue; log :ERROR, "error reading bot config:", $!; exit; end - Vrobot4.debug = cfg["debug"] if cfg.has_key? "debug" + Vrobot4.debug = cfg["debug"] if cfg.key? "debug" cfg["loadmods"].each {|mod| load mod, true} diff --git a/source/mod_base.rb b/source/mod_base.rb new file mode 100644 index 0000000..31548ce --- /dev/null +++ b/source/mod_base.rb @@ -0,0 +1,67 @@ +module Vrobot4::Server + class Mod_Base < Vrobot4::Module::Module + def initialize + super + register :c_help, "help", "Prints documentation for commands." + register :c_die, "die", "Kills all bot instances.", roles: "o" + register :c_modr, "modr", "Reloads a module.", roles: "o" + register :c_modl, "modl", "Loads a module.", roles: "o" + register :c_modu, "modu", "Unloads a module.", roles: "o" + register :c_dbg, "dbg", "Sets the debug level.", roles: "o" + end + + def c_help m, argv + check_args argv, "", "S" + if argv.length == 0 + cmds = [] + m.serv.each_mod {|mod| cmds << mod.all_cmds(m).keys.join(", ")} + cmds.delete "" + m.reply "Commands:", cmds.join(", ") + else + name = argv[0] + m.serv.each_mod do |mod| + if (cmd = mod.get_cmd m, name) + return m.reply name + ":", cmd.help_str + end + end + m.reply "Command not found:", name + end + end + + def c_die m, argv + m.reply \ + ["STATUS: DYING", + "ded", + "proceeding to die", + "bye", + "dedededed"].sample + exit + end + + def c_modr m, argv + check_args argv, "S" + m.serv.drop_mod argv[0] + m.serv.load_mod argv[0] + end + + def c_modl m, argv + check_args argv, "S" + m.serv.load_mod argv[0] + end + + def c_modu m, argv + check_args argv, "S" + m.serv.drop_mod argv[0] + end + + def c_dbg m, argv + check_args argv, "N" + Vrobot4.debug = argv[0].to_i + end + + def onCommand m, cnam, argv + Vrobot4.log :DEBUG, "command", cnam.to_s, argv.to_s + super + end + end +end diff --git a/source/mod_discord.rb b/source/mod_discord.rb new file mode 100644 index 0000000..7c1c17b --- /dev/null +++ b/source/mod_discord.rb @@ -0,0 +1,25 @@ +class Mod_Discord < Vrobot4::Module::Module + Vrobot4::Module.add_module_type self, "Discord", server: "Discord" + + def initialize + super + register :c_roles, "roles", "Gets a list of roles on your user." + register :c_purge, "purge", "Prunes messages from a channel.", roles: "o" + end + + def c_roles m, argv + roles = [] + m.user.real.roles.each {|role| roles << role.name + ":" + role.id.to_s} + m.reply roles.join("\n") + end + + def c_purge m, argv + check_args argv, "N" + n = argv[0].to_i + n = 2 if n < 2 + n = 100 if n > 100 + m.chan.real.prune n + end +end + +## EOF diff --git a/source/mod_fun.rb b/source/mod_fun.rb new file mode 100644 index 0000000..ccf0d59 --- /dev/null +++ b/source/mod_fun.rb @@ -0,0 +1,58 @@ +class Mod_Fun < Vrobot4::Module::Module + Vrobot4::Module.add_module_type self, "Fun" + + def initialize + super + register :c_carmack, "carmack", "mmmmmmmmmm" + register :c_revenant, "revenant", "AAAAAAAAA" + register :c_wan, "wan", "wan!" + register :c_nyan, "nyan", "nyan~" + register :c_box, "box", "S H I T P O S T I N G" + register :c_zdoom, "zdoom", "ZDoom™" + register :c_marble, "marble", "mararuaruaruaruarb" + register :c_what, "what", "What the fuck did you just fucking say about" + end + + def c_carmack m, argv; m.reply "m" * (rand(24) + 5) ; end + def c_revenant m, argv; m.reply "A" * (rand(24) + 3) ; end + def c_wan m, argv; m.reply "wan! " * (rand(10) + 1) ; end + def c_nyan m, argv; m.reply "nyan " * (rand(10) + 1) + "~"; end + + def c_box m, argv + str = argv.join(" ") + text = str + "\n" + if str.length > 2 + for i in (1..str.length - 2) + text << str[i] + " " * (str.length - 2) + str.reverse[i] + "\n" + end + end + text << str.reverse + m.reply_spaced text + end + + def c_zdoom m, argv + m.reply "ZDoom" + "™" * (rand(15) + 1) + end + + def c_marble m, argv + check_args argv, "", "N" + + if argv.length == 0; n = 20 + else; n = argv[0].to_i; end + n = 1000 if n > 1000 + + text = "m" + for i in (0..rand(n) + 1) + text << ["a", "r", "u"].sample + end + text << "b" + + m.reply text + end + + def c_what m, argv + m.reply "What the fuck did you just fucking say about me, you little bitch? I'll have you know I graduated top of my class in the Navy Seals, and I've been involved in numerous secret raids on Al-Quaeda, and I have over 300 confirmed kills. I am trained in gorilla warfare and I'm the top sniper in the entire US armed forces. You are nothing to me but just another target. I will wipe you the fuck out with precision the likes of which has never been seen before on this Earth, mark my fucking words. You think you can get away with saying that shit to me over the Internet? Think again, fucker. As we speak I am contacting my secret network of spies across the USA and your IP is being traced right now so you better prepare for the storm, maggot. The storm that wipes out the pathetic little thing you call your life. You're fucking dead, kid. I can be anywhere, anytime, and I can kill you in over seven hundred ways, and that's just with my bare hands. Not only am I extensively trained in unarmed combat, but I have access to the entire arsenal of the United States Marine Corps and I will use it to its full extent to wipe your miserable ass off the face of the continent, you little shit. If only you could have known what unholy retribution your little \"clever\" comment was about to bring down upon you, maybe you would have held your fucking tongue. But you couldn't, you didn't, and now you're paying the price, you goddamn idiot. I will shit fury all over you and you will drown in it. You're fucking dead, kiddo." + end +end + +## EOF diff --git a/source/mod_util.rb b/source/mod_util.rb index dd36d01..2b6ef6b 100644 --- a/source/mod_util.rb +++ b/source/mod_util.rb @@ -2,12 +2,93 @@ class Mod_Util < Vrobot4::Module::Module Vrobot4::Module.add_module_type self, "Utilities" def initialize - super() - register :testf, "Test function." + super + register :c_rand, "rand", "Returns a random number. Example: .rand 500" + register :c_decide, "decide", "Decides between one or more choices." + register :c_eightball, "eightball", "Peer into the magic 8-ball." + register :c_mystery, "mystery", "it is a mystery" end - def testf m, argv - puts "test function run", m, argv + def c_rand m, argv + check_args argv, "N", "N" + min, max = 0, argv[0].to_f + min, max = max, argv[1].to_f if argv.length >= 2 + max, min = min, max if max < min + n = rand(max - min) + min + m.reply n.round + end + + def c_decide m, argv + argv = argv.join(" ").split(",").each {|arg| arg.chomp!} + m.reply argv.sample + end + + def c_eightball m, argv + m.reply \ + ["Yes.", + "No.", + "Try again later.", + "Reply hazy.", + "Perhaps...", + "Maybe not...", + "Definitely.", + "Never.", + "system error [0xfded] try again later", + "Can you repeat the question?", + "Most certainly.", + "Nope.", + "Without a doubt.", + "Not at all.", + "Better not tell you now.", + "Concentrate and ask again.", + "It is decidedly so.", + "My reply is \"no\".", + "You may rely on it.", + "Don't count on it.", + "The answer is uncertain.", + "Sorry, I wasn't paying attention. What'd you say?", + "As I see it, yes.", + "My sources say \"no\".", + "Most likely.", + "Very doubtful.", + "I don't know. Ask your mom.", + "The demon runes are quaking again. One moment.", + "Outlook good.", + "Outlook is not so good.", + "Indeed.", + "Absolutely not.", + "Yeah, we'll go with that.", + "That works.", + "Of course. What are you, stupid?", + "No. Hell no.", + "Signs say yes.", + "Aren't you a little too old to be believing that?", + "Looks good to me.", + "Sure, why not?", + "It is certain.", + "Please no. Please no. Please no.", + "Yes, please.", + "Nah.", + "Go for it!", + "Negative.", + "Obviously, dumbass.", + "I doubt it.", + "Eeeh...it's likely?", + "Forget about it.", + "Chances aren't good.", + "Ahahahahahahaha no.", + "Maybe? I think.", + "No. Die.", + "Huh? Oh, sure.", + "Yeah, right...", + "How about no.", + "Doesn't look good to me.", + "Probably.", + "Obviously not, dumbass.", + "#"].sample + end + + def c_mystery m, argv end end diff --git a/source/module.rb b/source/module.rb index 9372dcd..d683988 100644 --- a/source/module.rb +++ b/source/module.rb @@ -2,9 +2,23 @@ module Vrobot4::Module class Command attr_reader :help_str - def initialize fn, help + def initialize fn, help, roles @function = fn @help_str = help + @roles = /[#{roles}]/ + end + + def usable_in? m, type + chan = m.chan.name + role = m.user.roles + mprm = m.serv.mprm + retm = true + retc = false + retr = false + retm = mprm[:glob][type] if mprm[:glob].key? type + retc = mprm[:chan][type].key? chan if mprm[:chan].key? type + retr = mprm[:role][type].scan /[#{role}]/ if mprm[:role].key? type + role.scan(@roles).any? && (retm || retc || retr) end def run m, argv @@ -15,25 +29,68 @@ module Vrobot4::Module class Module def initialize @commands = {} + Vrobot4.log :DEBUG, "initialized", self.to_s end - def register fn, help - @commands[fn.to_s] = Command.new(self.method(fn), help) + def register fn, cnam, help, roles: "v" + @commands[cnam] = Command.new(self.method(fn), help, roles).freeze end - def eachCmd - @commands.each {|cmd| yield cmd} + def onMessage m + false end def onCommand m, cnam, argv - @commands[cnam].run(m, argv) if @commands.has_key? cnam + if (cmd = get_cmd m, cnam) + begin; cmd.run(m, argv) + rescue; m.reply "error:", $!.to_s; end + return true + else + return false + end + end + + def all_cmds m + @commands.select {|name, cmd| cmd.usable_in? m, self.class} + end + + def get_cmd m, name + cmds = all_cmds(m) + cmds[name] if cmds.key? name + end + + protected + def check_args argv, req, opt = "" + if argv.length < req.length + raise ArgumentError, "not enough arguments" + elsif argv.length > req.length + opt.length + raise ArgumentError, "too many arguments" + else + (0...req.length).each do |i| + check_arg argv[i], i, req[i] + end + + (0...argv.length - req.length).each do |i| + check_arg argv[i + req.length], i + req.length, opt[i] + end + end + end + + private + def check_arg arg, i, req + case req + when 'N' + unless Vrobot4.is_num? arg + raise ArgumentError, "expected a number for arg " + i.to_s + end + end end end @@module_types = {} - def self.add_module_type t, name - @@module_types[name] = t + def self.add_module_type t, name, server: nil + @@module_types[name] = {:type => t, :server => server} Vrobot4.log :INFO, "added module type:", name end diff --git a/source/server.rb b/source/server.rb index bb61866..4fa01ec 100644 --- a/source/server.rb +++ b/source/server.rb @@ -1,34 +1,95 @@ +require './mod_base.rb' + module Vrobot4::Server class User - end - - class Message - attr_reader :msg - - def initialize msg - @msg = msg - end + attr_reader :name, :roles end class Channel + attr_reader :name + end + + class Message + attr_reader :msg, :user, :chan, :serv + + def initialize( + msg: nil, + user: nil, + chan: nil, + serv: nil, + reply: nil, + reply_spaced: nil) + @msg = msg + @user = user + @chan = chan + @serv = serv + @reply = reply + @reply_spaced = reply_spaced + end + + def reply *args + @reply.call args.join(" ") + end + + def reply_spaced *args + @reply_spaced.call args.join(" ") + end end class Server - def initialize - @modules = [] + attr_reader :mprm + + def initialize info + @modules = [Mod_Base.new] + load_permissions info["permissions"] if info.key? "permissions" end - def loadMod mod - @modules << mod + def load_mod mod + mt = Vrobot4::Module.get_module_type(mod) + if mt[:server] and mt[:server] != type + raise ArgumentError, "Module " + mod + " not valid for this server" + end + @modules << mt[:type].new + end + + def drop_mod mod + mt = Vrobot4::Module.get_module_type(mod) + @modules.each_index do |i| + @modules.delete_at i if @modules[i].is_a? mt[:type] + end + end + + def each_mod + @modules.each {|mod| yield mod} end def onMessage m - Vrobot4.log :DEBUG, "message:", m.to_s + @modules.each {|mod| break if mod.onMessage m} end def onCommand m, cnam, argv - Vrobot4.log :DEBUG, "command:", m.to_s, cnam.to_s, argv.to_s - @modules.each {|mod| mod.onCommand m, cnam, argv} + @modules.each {|mod| break if mod.onCommand m, cnam, argv} + end + + def type + "Unknown" + end + + private + def load_permissions pinf + @mprm = {:chan => {}, :role => {}, :glob => {}} + pinf.each do |perm| + mod = Vrobot4::Module.get_module_type(perm["module"])[:type] + if perm.key? "channel" + chan = perm["channel"] + @mprm[:chan][mod] = {} unless @mprm[:chan].key? chan + @mprm[:chan][mod][chan] = true + elsif perm.key? "roles" + @mprm[:role][mod] = perm["roles"] + else + @mprm[:glob][mod] = perm["enable"] + end + end end end diff --git a/source/sv_discord.rb b/source/sv_discord.rb index 2a8568a..7969c05 100644 --- a/source/sv_discord.rb +++ b/source/sv_discord.rb @@ -3,14 +3,48 @@ require 'discordrb' class Sv_Discord < Vrobot4::Server::Server Vrobot4::Server.add_server_type self, "Discord" + attr_reader :bot + + class User < Vrobot4::Server::User + attr_reader :real + + def initialize user, admins + @real = user + @name = user.name + @roles = "v" + @roles += "o" if admins.select {|role| user.role? role}.any? + @roles += "O" if user.owner? + end + end + + class Channel < Vrobot4::Server::Channel + attr_reader :real + + def initialize chan + @real = chan + @name = "#" + chan.name + end + end + def initialize info - super() + super + + if info.key? "admins"; @admins = info["admins"] + else; @admins = []; end + @bot = Discordrb::Bot.new \ token: info["apikey"], client_id: info["client"] @bot.message do |evt| - m = Vrobot4::Server::Message.new(evt.message.content) + m = Vrobot4::Server::Message.new \ + msg: evt.message.content, + user: User.new(evt.user, @admins), + chan: Channel.new(evt.channel), + serv: self, + reply: -> (text) {evt.respond text}, + reply_spaced: -> (text) {evt.respond "```\n" + text + "```"} + if m.msg.start_with? '.' argv = m.msg.split cnam = argv.shift[1..-1] @@ -24,6 +58,10 @@ class Sv_Discord < Vrobot4::Server::Server def connect @bot.run end + + def type + "Discord" + end end ## EOF