From e912898dd02dc8b048fadf0878d122e37e37ba22 Mon Sep 17 00:00:00 2001 From: Marrub Date: Fri, 11 Aug 2017 22:40:43 -0400 Subject: [PATCH] Document all code with YARDoc --- .gitignore | 1 + .yardopts | 1 + README | 7 +++- source/common.rb | 20 +++++++++-- source/main.rb | 4 +++ source/module.rb | 75 ++++++++++++++++++++++++++++++++++------ source/server.rb | 81 +++++++++++++++++++++++++++++++++++++------- source/sv_discord.rb | 25 ++++++++++++-- 8 files changed, 183 insertions(+), 31 deletions(-) create mode 100644 .yardopts diff --git a/.gitignore b/.gitignore index 62de81d..7b7d573 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*doc working *.yml *.swp diff --git a/.yardopts b/.yardopts new file mode 100644 index 0000000..13122f7 --- /dev/null +++ b/.yardopts @@ -0,0 +1 @@ +--protected source/*.rb - README diff --git a/README b/README index 71af1a9..f24ee70 100644 --- a/README +++ b/README @@ -1 +1,6 @@ -words +Mod_Audio requires: +- youtube-dl.rb +- streamio-ffmpeg + +Sv_Discord requires: +- discordrb diff --git a/source/common.rb b/source/common.rb index 7e44f9c..e528e93 100644 --- a/source/common.rb +++ b/source/common.rb @@ -1,10 +1,19 @@ +# Module for the main vrobot4 program and global info. module Vrobot4 + # The current program version. Version = "4.00".freeze - @@debug = 0 - def self.debug= set; @@debug = set; end - def self.debug ; @@debug; end + def self.debug=(set) @@debug = set end # Sets the current debug level. + def self.debug ( ) @@debug end # Gets the current debug level. + # Logs to the console. + # + # @param lv [Symbol] + # - If +:DEBUG+, this message will not be printed if the global debug + # level is less than 1. + # - If +:DEBUGV+, this message will not be printed if the global debug + # level is less than 2. + # @return [Boolean] true if the message was printed, false otherwise def self.log lv, *text if (lv != :DEBUG || @@debug >= 1) && (lv != :DEBUGV || @@debug >= 2) @@ -15,9 +24,14 @@ module Vrobot4 end end + # Checks if the argument +s+ is a numeric string. + # @param str [String] the string to check def self.is_num? str /\A[-+]?[0-9]*\.?[0-9]+\Z/ === str end + + private + @@debug = 0 end ## EOF diff --git a/source/main.rb b/source/main.rb index e9985f1..546fc51 100644 --- a/source/main.rb +++ b/source/main.rb @@ -5,6 +5,7 @@ require './server.rb' require 'yaml' module Vrobot4 + private def self.loadServer servinfo type = servinfo["type"] @@ -30,6 +31,9 @@ module Vrobot4 thrds.each {|th| th.join} end + public + # Runs the program. + # @param cfg [IO] configuration file to load def self.main cfg log :INFO, "vrobot version", Version diff --git a/source/module.rb b/source/module.rb index 46ae249..3d1763e 100644 --- a/source/module.rb +++ b/source/module.rb @@ -1,13 +1,20 @@ +# Module for vrobot4 bot modules. module Vrobot4::Module + # A command function. Holds extra info for i.e. permissions. class Command - attr_reader :help_str + attr_reader :help_str # 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 def usable_in? m type = @function.owner role = m.user.roles @@ -19,27 +26,33 @@ module Vrobot4::Module role.scan(@roles).any? and (retm or retc or retr) end + # Calls the method attached to this command. def run m, argv @function.call(m, argv) end end + # A bot module. Holds commands and callbacks. class Module + # @param info [Hash] arbitrary extra information for this module def initialize info @commands = {} @info = info Vrobot4.log :DEBUG, "initialized", self.to_s end - def register fn, cnam, help = nil, roles: "v" - help = "No help available for this command." if help == nil - @commands[cnam] = Command.new(self.method(fn), help, roles).freeze - 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 [Array] array of string arguments + # @return [Boolean] if this should block further command callbacks def on_command m, cnam, argv if (cmd = get_cmd m, cnam) begin; cmd.run(m, argv) @@ -50,16 +63,48 @@ module Vrobot4::Module 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 all_cmds m @commands.select {|name, cmd| cmd.usable_in? m} end + # Gets a command by name in a specified context. + # @param m [Vrobot4::Server::Message] message for context + # @return [Vrobot4::Module::Command] command if found, nil if not def get_cmd m, name cmds = all_cmds(m) cmds[name] if cmds.key? name 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. + # + # @param argv [Array] array of arguments + # @param req [String] specifier string of required arguments + # @param opt [String] specifier string of optional arguments def check_args argv, req, opt = "" if argv.length < req.length raise ArgumentError, "Not enough arguments" @@ -80,15 +125,17 @@ module Vrobot4::Module def check_arg arg, i, req case req when 'N' - unless Vrobot4.is_num? arg + unless Vrobot4.is_num? arg.strip raise ArgumentError, "Expected a number for arg " + i.to_s end end end end - @@module_types = {} - + # 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) def self.add_module_type type, server: nil, servflags: nil @@module_types[type.type] = { type: type, @@ -98,9 +145,15 @@ module Vrobot4::Module Vrobot4.log :INFO, "added module type:", type.type end - def self.get_module_type s - @@module_types[s] + # 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 + + private + @@module_types = {} end ## EOF diff --git a/source/server.rb b/source/server.rb index ab1a274..6eac16e 100644 --- a/source/server.rb +++ b/source/server.rb @@ -1,3 +1,4 @@ +# Module for vrobot4 server interfaces. module Vrobot4::Server class Mod_Base < Vrobot4::Module::Module def initialize info @@ -10,6 +11,7 @@ module Vrobot4::Server register :c_dbg, "dbg", "Sets the debug level.", roles: "o" end + # @!visibility private def c_help m, argv check_args argv, "", "S" if argv.length == 0 @@ -28,6 +30,7 @@ module Vrobot4::Server end end + # @!visibility private def c_die m, argv m.serv.voice_quit m if m.serv.flags.include? "A" m.reply \ @@ -42,6 +45,7 @@ module Vrobot4::Server exit end + # @!visibility private def c_modr m, argv check_args argv, "S", "S" m.serv.drop_mod argv[0] @@ -49,38 +53,57 @@ module Vrobot4::Server m.serv.load_mod argv[0] end + # @!visibility private def c_modl m, argv check_args argv, "S" m.serv.load_mod argv[0] end + # @!visibility private def c_modu m, argv check_args argv, "S" m.serv.drop_mod argv[0] end + # @!visibility private def c_dbg m, argv check_args argv, "N" Vrobot4.debug = argv[0].to_i end + # @!visibility private def on_command m, cnam, argv Vrobot4.log :DEBUGV, "command", cnam.to_s, argv.to_s super end end + private_constant :Mod_Base + # Generic user information. May be extended. class User - attr_reader :name, :roles + attr_reader :name # Plaintext name of the user. + attr_reader :roles # List of user's roles. end + # Generic channel information. May be extended. class Channel - attr_reader :name + attr_reader :name # Plaintext name of the channel. end + # Generic event information. May not be extended. class Message - attr_reader :msg, :user, :chan, :serv + attr_reader :msg # Plaintext of message (if any.) + attr_reader :user # User that triggered this message (if any.) + attr_reader :chan # Channel this message was sent to (if any.) + attr_reader :serv # Server this message was sent to. + # @param info [Hash] A hash containing message info. Keys may be omitted. + # [:msg] Plaintext of message. + # [:user] User that triggered this message. + # [:chan] Channel that this message was sent to. + # [:serv] Server this message was sent to. + # [:reply] Method that sends a message to the specified channel. + # [:reply_b] Method that sends a large message to the specified channel. def initialize(**info) @msg = info[:msg] if info.key? :msg @user = info[:user] if info.key? :user @@ -90,24 +113,29 @@ module Vrobot4::Server @reply_b = info[:reply_b] if info.key? :reply_b end + # Sends a message to the channel this message originated from. def reply *args @reply.call args.join(" ") end + # Sends a large message to the channel this message originated from. def reply_b *args @reply_b.call args.join(" ") end end + # Generic server interface. class Server - attr_reader :mprm + attr_reader :mprm # Module permissions for this server. + # @param info [Hash] arbitrary extra information for this server def initialize info @info = info @modules = [Mod_Base.new(nil)] load_permissions info["permissions"] if info.key? "permissions" end + # Loads and initializes a module into the load list. def load_mod mod mt = Vrobot4::Module.get_module_type(mod) if mt[:server] and mt[:server] != self.class.type or @@ -117,6 +145,7 @@ module Vrobot4::Server @modules << mt[:type].new(@info.key?(mod) ? @info[mod] : nil) end + # Drops a module from the load list. def drop_mod mod mt = Vrobot4::Module.get_module_type(mod) @modules.each_index do |i| @@ -124,60 +153,86 @@ module Vrobot4::Server end end + # Yields for every module loaded in the server. def each_mod @modules.each {|mod| yield mod} end + # (see Vrobot4::Module::Module#on_message) + # @note Passes information to all modules. def on_message m @modules.each {|mod| break if mod.on_message m} end + # (see Vrobot4::Module::Module#on_command) + # @note Passes information to all modules. def on_command m, cnam, argv @modules.each {|mod| break if mod.on_command m, cnam, argv} end + # Connect to the server. + def connect + raise NotImplementedError, "Server#connect not implemented" + end + + # Flags for this server. + # @return [String] def flags "" end protected + # Implementation defined permission loader. + # Loads information into +@mprm+. def load_permissions pinf - raise NotImplementedError, "Server::#load_permissions not implemented" + raise NotImplementedError, "Server#load_permissions not implemented" end end + # Basis for an audio-enabled server interface. class AudioServer < Server + # Joins a voice channel, using a message for context. def voice_join m - raise NotImplementedError, "AudioServer::#voice_join not implemented" + raise NotImplementedError, "AudioServer#voice_join not implemented" end + # Quits a voice channel, using a message for context. def voice_quit m - raise NotImplementedError, "AudioServer::#voice_quit not implemented" + raise NotImplementedError, "AudioServer#voice_quit not implemented" end + # Plays an arbitrary audio file in a given context. def play m, io - raise NotImplementedError, "AudioServer::#play not implemented" + raise NotImplementedError, "AudioServer#play not implemented" end + # Check if the bot is playing audio in a given context. def is_playing? m - raise NotImplementedError, "AudioServer::#is_playing? not implemented" + raise NotImplementedError, "AudioServer#is_playing? not implemented" end + # (see Vrobot4::Server::Server#flags) def flags "A" end end - @@server_types = {} - + # Adds a server type to the global list. + # @param type [Class] server type to add def self.add_server_type type @@server_types[type.type] = type Vrobot4.log :INFO, "added server type:", type.type end - def self.get_server_type s - @@server_types[s] + # Gets a server type by name from the global list. + # @param name [String] name of the server type to find + # @return [Vrobot4::Server::Server] the server type + def self.get_server_type name + @@server_types[name] end + + private + @@server_types = {} end ## EOF diff --git a/source/sv_discord.rb b/source/sv_discord.rb index 9942b0c..9e336e8 100644 --- a/source/sv_discord.rb +++ b/source/sv_discord.rb @@ -1,14 +1,17 @@ require 'discordrb' +# A server implementation for Discord using discordrb. class Sv_Discord < Vrobot4::Server::AudioServer + # The server type name. def self.type "Discord" end Vrobot4::Server.add_server_type self - attr_reader :bot + attr_reader :bot # The Discordrb::Bot instance. + # (see Vrobot4::Server::Server#initialize) def initialize info super @@ -41,16 +44,19 @@ class Sv_Discord < Vrobot4::Server::AudioServer end end + # (see Vrobot4::Server::AudioServer#voice_join) def voice_join m chan = m.user.real.voice_channel raise RuntimeError, "You're not in a voice channel" unless chan @bot.voice_connect chan end + # (see Vrobot4::Server::AudioServer#voice_join) def voice_quit m @bot.voice_destroy m.chan.real.server.id end + # (see Vrobot4::Server::AudioServer#play) def play m, io v = @bot.voice(m.chan.real) raise ArgumentError, "Invalid i/o stream" unless io @@ -58,20 +64,24 @@ class Sv_Discord < Vrobot4::Server::AudioServer v.play_stream io end + # (see Vrobot4::Server::AudioServer#is_playing?) def is_playing? m v = @bot.voice(m.chan.real) v != nil and v.playing? end + # (see Vrobot4::Server::Server#connect) def connect @bot.run end + # (see Vrobot4::Server::Server#flags) def flags "AD" end protected + # (see Vrobot4::Server::Server#load_permissions) def load_permissions pinf @mprm = {chan: {}, role: {}, glob: {}} pinf.each do |perm| @@ -92,19 +102,26 @@ class Sv_Discord < Vrobot4::Server::AudioServer @cprm = {} end + # Returns true if the channel is enabled, false otherwise. def [] chan (@cprm.key? chan.name and @cprm[chan.name]) or (@cprm.key? chan.real.id and @cprm[chan.real.id]) end + # Sets a channel's permission on/off. def []= chan, set @cprm[chan] = set end end + private_constant :ChannelPerms + # A Discord user. class User < Vrobot4::Server::User - attr_reader :real + attr_reader :real # The Discordrb::User instance. + # @param user [Discordrb::User] the discord user + # @param ops [Array] list of operator role IDs + # @param hop [Array] list of half-operator role IDs def initialize user, ops, hop @real = user @name = user.name @@ -121,9 +138,11 @@ class Sv_Discord < Vrobot4::Server::AudioServer end end + # A Discord channel. class Channel < Vrobot4::Server::Channel - attr_reader :real + attr_reader :real # The Discordrb::Channel instance. + # @param chan [Discordrb::Channel] the discord channel def initialize chan @real = chan @name = "#" + chan.name