class ProtonBot::Plug
Plug
. This class includes socket, writer and reader threads, user&channel-data, API for interacting with server etc. @!attribute [r] bot
@return [ProtonBot::Bot] Plug's bot.
@!attribute [r] db
@return [Heliodor::DB] Plug's Heliodor-powered database. @see http://www.rubydoc.info/gems/heliodor
@!attribute [r] sock
@return [TCPSocket,SSLSocket] Plug's socket. May be SSL-socket.
@!attribute [r] rsock
@return [TCPSocket] Always non-SSL socket.
@!attribute [r] is_ssl
@return [Boolean] True if connection is SSL-powered
@!attribute [r] name
@return [String] Plug's name.
@!attribute [r] conf
@return [Hash<String>] Config.
@!attribute [r] rloop
@return [Thread] Reader-loop-thread.
@!attribute [r] wloop
@return [Thread] Writer-loop-thread.
@!attribute [r] log
@return [ProtonBot::LogWrapper] Log wrapper.
@!attribute [r] queue
@return [Queue] Queue.
@!attribute [r] chans
@return [Hash<String>] Channel data.
@!attribute [r] users
@return [Hash<String>] User data (by nick).
@!attribute [r] event_locks
@return [Array<EventLock>] Event locks.
@!attribute [rw] running
@return [Boolean] Returns `true` if bot still runs and processes messages from server.
@!attribute [r] user
@return [String] Plug's username.
@!attribute [rw] nick
@return [String] Plug's nickname.
@!attribute [r] rnam
@return [String] Plug's realname.
Attributes
Public Class Methods
@param bot [Bot] @param name [String] @param conf [Hash<String>]
# File lib/protonbot/plug.rb, line 49 def initialize(bot, name, conf) @bot = bot @name = name @db = @bot.dbs[@name] @log = @bot._log.wrap("!#{@name}") @conf = conf @nick = conf['nick'] @user = conf['user'] @rnam = conf['rnam'] @sock = nil @running = false @queue = Queue.new @chans = {} @users = {} @event_locks = [] if @conf['sasl'] and @conf['sasl_user'] and @conf['pass'] @use_sasl = true else @use_sasl = false end end
Colorizes given string. @param string [String] @return [String] Output
# File lib/protonbot/plug_io.rb, line 74 def self.process_colors(string) string .gsub("%C%", "%C?") .gsub(",%", ",?") .gsub("%C", "\x03") .gsub("%B", "\x02") .gsub("%I", "\x10") .gsub("%U", "\x1F") .gsub("%N", "\x0F") .gsub("?WHITE", "0") .gsub("?BLACK", "1") .gsub("?BLUE", "2") .gsub("?GREEN", "3") .gsub("?RED", "4") .gsub("?BROWN", "5") .gsub("?PURPLE", "6") .gsub("?ORANGE", "7") .gsub("?YELLOW", "8") .gsub("?LGREEN", "9") .gsub("?CYAN" , "10") .gsub("?LCYAN", "11") .gsub("?LBLUE", "12") .gsub("?PINK", "13") .gsub("?GREY", "14") .gsub("?LGREY", "15") end
Splits given string each 399 bytes and on newlines @param string [String] @return [String] Output
# File lib/protonbot/plug_io.rb, line 58 def self.ssplit(string) out = [] arr = string.split("\n\r") arr.each do |i| items = i.scan(/.{,399}/) items.delete('') items.each do |i2| out << i2 end end out end
Public Instance Methods
Sends CTCP ACTION to given target @param target [String] @param msg [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 102 def action(target, msg) ctcp(target, "ACTION #{msg}") self end
Sets ban on given user at given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 166 def ban(chan, nick) usermode(chan, nick, '+b') self end
Changes current nick to given. If successful, `unick` event will be emitted @param to [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 45 def change_nick(to) write("NICK #{to}") self end
Sets mode on given channel @param chan [String] @param mode [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 139 def chanmode(chan, mode) write("MODE #{chan} #{mode}") self end
Connects to server, introduces and starts reader and writer threads. @return [Plug] self
# File lib/protonbot/plug.rb, line 73 def connect! if @conf['ssl'] == nil or @conf['ssl'] == false @sock = @rsock = TCPSocket.new(@conf['host'], @conf['port']) @is_ssl = false else @rsock = TCPSocket.new(@conf['host'], @conf['port']) @ssl_ctx = OpenSSL::SSL::SSLContext.new @ssl_ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE @ssl_ctx.ssl_version = :SSLv23 @ssl_ctx.cert = OpenSSL::X509::Certificate.new(File.read(File.expand_path(@conf['ssl_crt']))) if @conf['ssl_crt'] @ssl_ctx.key = OpenSSL::PKey::RSA.new(File.read(File.expand_path(@conf['ssl_key']))) if @conf['ssl_key'] @sock = OpenSSL::SSL::SSLSocket.new(@rsock, @ssl_ctx) @sock.sync = true @sock.connect @is_ssl = true end @running = true @rloop = Thread.new { readloop } @wloop = Thread.new { writeloop } log.info("Started plug `#{name}` successfully!") introduce self end
Sends CTCP to given target. @param target [String] @param msg [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 93 def ctcp(target, msg) write("PRIVMSG #{target} :\x01#{self.class.process_colors(msg)}\x01") self end
Deops given user at given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 211 def deop(chan, nick) usermode(chan, nick, '-o') self end
Devoices on given user at given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 229 def devoice(chan, nick) usermode(chan, nick, '-v') self end
Emits passed event - calls first matching hook from each plugin @param dat [Hash] Event hash @return [Plug] self
# File lib/protonbot/plug_events.rb, line 5 def emit(dat = {}) hooks = [] bot.plugins.each do |_, p| hooks += p.hooks end hooks = hooks.keep_if do |hook| dat >= hook.pattern end hooks.each do |h| canrun = true h.chain.each do |l| next unless canrun canrun = l.call(dat, h) end h.block.call(dat) if canrun end event_locks.each_with_index do |el, k| if dat >= el.pattern event_locks.delete_at(k) el.unlock end end self end
Emits passed event in new thread @param dat [Hash] Event hash @return [Plug] self
# File lib/protonbot/plug_events.rb, line 33 def emitt(dat = {}) d = dat.clone Thread.new do begin emit(d) rescue => e log_err(e) end end self end
Sets excempt on given user at given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 184 def excempt(chan, nick) usermode(chan, nick, '+e') self end
Gets hostname for given nickname using WHOIS and event lock @param nick [String] @return [String] host
# File lib/protonbot/plug_utils.rb, line 13 def gethost(nick) if @users[nick] and @users[nick][:host] @users[nick][:host] else Thread.new do sleep(1) whois(nick) end wait_for(type: :code_whoisuser, nick: nick) end @users[nick][:host] end
Gets username for given nickname using WHOIS and event lock @param nick [String] @return [String] user
# File lib/protonbot/plug_utils.rb, line 29 def getuser(nick) if @users[nick] and @users[nick][:user] @users[nick][:user] else Thread.new do sleep(1) whois(nick) end wait_for(type: :code, code: ProtonBot::Numeric::WHOISUSER) @users[nick][:user] end end
@return [String] out
# File lib/protonbot/plug.rb, line 119 def inspect %(<#ProtonBot::Plug:#{object_id.to_s(16)} @name=#{name} @bot=#{bot}>) end
Sends credentials to server (PASS, NICK, USER).
# File lib/protonbot/plug_io.rb, line 48 def introduce write_("PASS #{@conf['pass']}") if @conf['pass'] and !@use_sasl write_("CAP REQ :sasl") if @use_sasl write_("NICK #{@conf['nick']}") write_("USER #{@conf['user']} 0 * :#{@conf['rnam']}") end
Invites user to given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 120 def invite(chan, nick) write("INVITE #{nick} #{chan}") self end
Joins given channel and uses password if it's provided @param chan [String] @param pass [String] Password @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 54 def join(chan, pass = '') write("JOIN #{chan} #{pass}") self end
Kicks given user from given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 238 def kick(chan, nick, reason = 'Default Kick Reason') write("KICK #{chan} #{nick} :#{reason}") self end
Logs given error object to cosole @param e [Exception] @return [Plug] self
# File lib/protonbot/plug.rb, line 107 def log_err(e) @log.error('Error!') @log.error("> Inspect: #{e.inspect}") @log.error("> Message: #{e.message}") @log.error('> Backtrace:') e.backtrace.each do |i| @log.error(">> #{i}") end self end
Sends NCTCP to given target. @param target [String] @param msg [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 111 def nctcp(target, msg) write("NOTICE #{target} :\x01#{self.class.process_colors(msg)}\x01") self end
Sends notice to given target. Splits it each 399 bytes. @param target [String] @param msg [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 82 def notice(target, msg) self.class.ssplit(self.class.process_colors(msg)).each do |m| write("NOTICE #{target} :\u200B#{m}") end self end
Ops given user at given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 202 def op(chan, nick) usermode(chan, nick, '+o') self end
Parts given channel with given reason @param chan [String] @param reason [String]
# File lib/protonbot/plug_utils.rb, line 62 def part(chan, reason = 'Parting...') write("PART #{chan} :#{reason}") self end
Sends message to given target. Splits it each 399 bytes. @param target [String] @param msg [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 71 def privmsg(target, msg) self.class.ssplit(self.class.process_colors(msg)).each do |m| write("PRIVMSG #{target} :\u200B#{m}") end self end
Sets quiet on given user at given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 148 def quiet(chan, nick) usermode(chan, nick, '+q') self end
Main read-loop. Reads data from socket and emits event `raw`
# File lib/protonbot/plug_io.rb, line 3 def readloop while @running if s = @sock.gets s = s.force_encoding(@conf['encoding']) s = s[0..-3] log.info "R > #{s}" begin emit(type: :raw, raw_data: s.clone, plug: self, db: db, bot: bot) rescue => e log_err(e) end else @running = false end end log.info 'Connection closed.' end
Removes given user from given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 247 def remove(chan, nick, reason = 'Default Remove Reason') write("REMOVE #{chan} #{nick} :#{reason}") self end
@!api private
# File lib/protonbot/plug.rb, line 124 def thrjoin until @rloop && @rloop.status == 'run' sleep(0.1) end until @wloop && @wloop.status == 'run' sleep(0.1) end @bot.plugthrs[@name].join @rloop.join @wloop.join end
Removes ban on given user at given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 175 def unban(chan, nick) usermode(chan, nick, '-b') self end
Removes excempt on given user at given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 193 def unexcempt(chan, nick) usermode(chan, nick, '-e') self end
Removes quiet on given user at given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 157 def unquiet(chan, nick) usermode(chan, nick, '-q') self end
Sets mode on given user at given channel @param chan [String] @param nick [String] @param mode [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 130 def usermode(chan, nick, mode) write("MODE #{chan} #{mode} #{nick}") self end
Voices given user at given channel @param chan [String] @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 220 def voice(chan, nick) usermode(chan, nick, '+v') self end
Creates EventLock with given pattern. @param pattern [Hash] @return [Plug] self
# File lib/protonbot/plug_events.rb, line 48 def wait_for(pattern) ProtonBot::EventLock.new(self, pattern) self end
Sends WHOIS message to the server @param nick [String] @return [Plug] self
# File lib/protonbot/plug_utils.rb, line 5 def whois(nick) write("WHOIS #{nick}") self end
Adds given message to the queue @param s [String] Message @return [Plug] self
# File lib/protonbot/plug_io.rb, line 33 def write(s) @queue << s self end
Sends message to server without using queue. @param s [String] Message @return [Plug] self
# File lib/protonbot/plug_io.rb, line 41 def write_(s) s = s.encode(@conf['encoding'], s.encoding) @sock.puts s log.info "W > #{s}" end
Main write-loop. Reads data from queue and sends it to server
# File lib/protonbot/plug_io.rb, line 22 def writeloop while @running || !@wqueue.empty? s = @queue.pop write_(s) sleep(@conf['queue_delay']) end end