class TinyIRC::Bot
Attributes
log[RW]
config[RW]
config_file[RW]
config_mtx[RW]
db[RW]
groups[RW]
log[RW]
plugins[RW]
prefix[RW]
sockets[RW]
Public Class Methods
new(config_file, db_file)
click to toggle source
# File lib/tinyirc/bot.rb, line 18 def initialize(config_file, db_file) @config_file = config_file @sockets = {} @plugins = {} @groups = {} @log = ParticleLog.new 'bot', ParticleLog::INFO @log.important 'Hello, IRC!' @db = SQLite3::Database.open db_file @db.execute <<~SQL CREATE TABLE IF NOT EXISTS groupinfo ( server VARCHAR(32), host VARCHAR(64), name VARCHAR(32) ); SQL @log.info "db = #{db_file}" @config_mtx = Mutex.new load_config end
Public Instance Methods
_read_line(socket)
click to toggle source
# File lib/tinyirc/bot.rb, line 293 def _read_line(socket) res = IO.select([socket.sock], nil, nil, 0.001) return nil unless res begin return socket.sock.readline("\r\n", chomp: true).force_encoding("UTF-8") rescue => e socket.log.error "#{e.class.name} - #{e.message}" socket.mtx.synchronize do socket.running = false end handle_event(type: :disconnect, socket: socket) return nil end end
add(full, name)
click to toggle source
# File lib/tinyirc/bot.rb, line 73 def add(full, name) n = File.basename(name) if @plugins.include? n @log.error "Cannot have multiple plugins with same basename (#{n})" else @plugins[n] = TinyIRC::Plugin.new self, full end end
add_group(name, group_config)
click to toggle source
# File lib/tinyirc/bot.rb, line 118 def add_group(name, group_config) raise RuntimeError, 'User-defined group names cannot contain slashes!' if name.index '/' g = TinyIRC::Group.new name (group_config[name]['include'] || []).each do |g2| if g2.index '/' g2p, _ = *g2.split('/', 2) unless @plugins.include? g2p @log.error "There's no plugin called `#{g2p}`" next end unless @plugins[g2p].groups.include? g2 @log.error "Plugin `#{g2p}` does not have group `#{g2}`" next end @plugins[g2p].groups[g2].perms.each do |perm| g.perm(perm.plugin, perm.command, perm.branch) end else unless @groups.include? g2 @log.error "There's no group called `#{g2}`" next end @groups[g2].perms.each do |perm| g.perm(perm.plugin, perm.command, perm.branch) end end end (group_config[name]['perms'] || []).each do |perm| g.perm *TinyIRC::Permission.parse(perm) end @groups[name] = g end
handle_command(h, cmd_info)
click to toggle source
# File lib/tinyirc/bot.rb, line 269 def handle_command(h, cmd_info) @plugins.each_pair do |name, plugin| return true if plugin.handle_command(h, cmd_info) end false end
handle_event(e)
click to toggle source
# File lib/tinyirc/bot.rb, line 276 def handle_event(e) TinyIRC.define_event_methods(e) Thread.new do @plugins['core'].handle_event(e) end end
inspect()
click to toggle source
# File lib/tinyirc/bot.rb, line 360 def inspect '#<TinyIRC::Bot>' end
ioloop()
click to toggle source
# File lib/tinyirc/bot.rb, line 292 def ioloop def _read_line(socket) res = IO.select([socket.sock], nil, nil, 0.001) return nil unless res begin return socket.sock.readline("\r\n", chomp: true).force_encoding("UTF-8") rescue => e socket.log.error "#{e.class.name} - #{e.message}" socket.mtx.synchronize do socket.running = false end handle_event(type: :disconnect, socket: socket) return nil end end def read_line(socket) return unless socket.running msg = _read_line(socket) return unless msg socket.log.io "R> #{msg}" handle_event(type: :raw, raw_data: msg, socket: socket, bot: self) end def write_line(socket) return unless socket.running previous = socket.last_write current = Time.now diff = current - previous return if diff < 0.7 && diff > 0 # todo - implement bursts msg = begin socket.queue.pop true rescue ThreadError nil end return unless msg begin socket.direct_write msg.force_encoding("UTF-8") rescue => e socket.log.error "#{e.class.name} - #{e.message}" socket.mtx.synchronize do socket.running = false end handle_event(type: :disconnect, socket: socket) return nil end socket.last_write = current end while true do sleep 0.001 @sockets.each_pair do |sname, socket| sleep 0.001 next unless socket.running read_line(socket) write_line(socket) end end end
load_config()
click to toggle source
# File lib/tinyirc/bot.rb, line 43 def load_config @config_mtx.synchronize do @config = YAML.parse_file(@config_file).to_ruby @prefix = @config['prefix'] || '!' @log.info "prefix = #{@prefix}" load_plugin_config load_group_config load_cooldown_config load_server_config end end
load_cooldown_config()
click to toggle source
# File lib/tinyirc/bot.rb, line 172 def load_cooldown_config cooldown_config = @config['cooldowns'] || {} l = ParticleLog.new('cooldowns', ParticleLog::INFO) cooldown_config.each_pair do |cmd, cld| cld = cld.to_i a = cmd.split('/', 3) if !a[0] || !a[1] && a[2] l.error "Invalid command entry: #{cld}" next end pname = a[0] command = a[1] || :all branch = a[2] || :all unless @plugins.include? pname l.error "There's no plugin called `#{pname}`" next end plugin = @plugins[pname] upd = lambda do |command| if branch == :all command.branches.each_pair do |_, b| b.cooldown = cld l.info "#{pname}/#{command.name}/#{b.id} <- #{cld}s" end else unless command.branches.include? branch l.error "`#{pname}/#{command.name}` command does not have branch called `#{branch}`" next end command.branches[branch].cooldown = cld l.info "#{pname}/#{command.name}/#{branch} <- #{cld}s" end end if command == :all plugin.commands.each_pair do |_, cmd| upd.(cmd) end else unless plugin.commands.include? command l.error "`#{pname}/#{command}` command does not have branch called `#{branch}`" next end upd.(plugin.commands[command]) end end end
load_group_config()
click to toggle source
# File lib/tinyirc/bot.rb, line 110 def load_group_config @groups = {} group_config = { "world" => {}, "admin" => {} }.merge(@config['groups'] || {}) def add_group(name, group_config) raise RuntimeError, 'User-defined group names cannot contain slashes!' if name.index '/' g = TinyIRC::Group.new name (group_config[name]['include'] || []).each do |g2| if g2.index '/' g2p, _ = *g2.split('/', 2) unless @plugins.include? g2p @log.error "There's no plugin called `#{g2p}`" next end unless @plugins[g2p].groups.include? g2 @log.error "Plugin `#{g2p}` does not have group `#{g2}`" next end @plugins[g2p].groups[g2].perms.each do |perm| g.perm(perm.plugin, perm.command, perm.branch) end else unless @groups.include? g2 @log.error "There's no group called `#{g2}`" next end @groups[g2].perms.each do |perm| g.perm(perm.plugin, perm.command, perm.branch) end end end (group_config[name]['perms'] || []).each do |perm| g.perm *TinyIRC::Permission.parse(perm) end @groups[name] = g end def add(name, group_config) add_group(name, group_config) end def remove(name) @groups.delete name end (@groups.keys & group_config.keys).each do |k| add_group(k, group_config) end (@groups.keys - group_config.keys).each do |k| remove k end (group_config.keys - @groups.keys).each do |k| add(k, group_config) end end
load_plugin_config()
click to toggle source
# File lib/tinyirc/bot.rb, line 70 def load_plugin_config plugin_config = { "tinyirc/plugins/core" => nil }.merge(@config['plugins'] || {}) def add(full, name) n = File.basename(name) if @plugins.include? n @log.error "Cannot have multiple plugins with same basename (#{n})" else @plugins[n] = TinyIRC::Plugin.new self, full end end def remove(name) @plugins.delete name end pkeys = {} plugin_config.keys.each do |k| pkeys[File.basename(k)] = k end (@plugins.keys - pkeys.keys).each do |k| @plugins[k].log.info 'Destroying...' remove k end (pkeys.keys - @plugins.keys).each do |k| add pkeys[k], k end @plugins.each_pair do |k, v| cfg = plugin_config[k] || {} raise RuntimeError, 'Invalid config type' if cfg.class != Hash v._l_config(cfg) unless v.loaded v._l_postinit end end end
load_server_config()
click to toggle source
# File lib/tinyirc/bot.rb, line 221 def load_server_config server_config = @config['servers'] || {} def add(name, cfg) s = TinyIRC::IRCSocket.new(self, name, cfg) Thread.new { s.connect } @sockets[name] = s end def remove(name) @sockets[name].mtx.synchronize do @sockets.delete(name).disconnect end end (@sockets.keys & server_config.keys).each do |k| s = @sockets[k] c = server_config[k] Thread.new do #s.mtx.synchronize do should_restart = false should_restart = true if s.host != c['host'] should_restart = true if s.port != c['port'] should_restart = true if s.user != c['user'] should_restart = true if s.rnam != c['rnam'] s.disconnect if should_restart s.host = c['host'] s.port = c['port'] s.nick = c['nick'] s.user = c['user'] s.pass = c['pass'] s.rnam = c['rnam'] s.connect if should_restart end end (@sockets.keys - server_config.keys).each do |k| remove k end (server_config.keys - @sockets.keys).each do |k| add(k, server_config[k]) end end
read_line(socket)
click to toggle source
# File lib/tinyirc/bot.rb, line 308 def read_line(socket) return unless socket.running msg = _read_line(socket) return unless msg socket.log.io "R> #{msg}" handle_event(type: :raw, raw_data: msg, socket: socket, bot: self) end
reload()
click to toggle source
# File lib/tinyirc/bot.rb, line 56 def reload @config_mtx.synchronize do @plugins = {} @config = YAML.parse_file(@config_file).to_ruby @prefix = @config['prefix'] || '!' @log.info "prefix = #{@prefix}" load_plugin_config load_group_config load_cooldown_config load_server_config end end
remove(name)
click to toggle source
# File lib/tinyirc/bot.rb, line 82 def remove(name) @plugins.delete name end
start()
click to toggle source
# File lib/tinyirc/bot.rb, line 283 def start Thread.new do ioloop end TinyIRC::App.cfg self TinyIRC::App.run! end
write_line(socket)
click to toggle source
# File lib/tinyirc/bot.rb, line 316 def write_line(socket) return unless socket.running previous = socket.last_write current = Time.now diff = current - previous return if diff < 0.7 && diff > 0 # todo - implement bursts msg = begin socket.queue.pop true rescue ThreadError nil end return unless msg begin socket.direct_write msg.force_encoding("UTF-8") rescue => e socket.log.error "#{e.class.name} - #{e.message}" socket.mtx.synchronize do socket.running = false end handle_event(type: :disconnect, socket: socket) return nil end socket.last_write = current end