# Sribi is an IRC interface, designed for use in bots. # Some code modified for use in Sribi from Adrian Hayter - www.adrianhayter.com # # Author:: Thristhart (thristhart@steelwalrus.com) #Changelog: # # v 1.3------------------- #User class #* private message (say) #* notices (notice) #* actions (action) #* private message handling (User.message?) #* commands via private message (User.command?) #* user list (User.list) #* list of channels the user has joined, that you are also in (channels) # #whois info (whois) #* name - Nick #* username - Username #* ip - the IP #* realname - Real Name #* registered - Is the user registered? true/false #* idletime - Time idle #* chans - Channels joined #* signontime - Amount of time user has spent on the server. # # v 1.2--------------------- # debug mode added (Sribi.debug => true or false) # # parse now understands - #* whois #* kick/quit #* nick ##v.1.1 ------------------------ ##place now puts with timestamp ##connection now secured and registered correctly ##bothersome "Log Cleared" message removed # #parse now has - #* join/part message handling #* version message handling # #action message handling (is_action?) # #Nickserv commands #* register #* identify #* change password # #multiple people added or removed from authorised list at once # #Topic setting (set_topic) # #prefix changing (prefix, formerly setVar(prefix)) # #mode commands #* mode #* voice #* devoice #* moderated #* unmoderated #* no_nick_change #* allow_nick_change #* mute # #Channel class #* initialize, replaces Sribi.join_chan #* list #* get #* say, replaces Sribi.say #* notice, replaces Sribi.notice #* action #* users #* ops #* is_op? #* join? #* part? #* command?, replaces Sribi.command #* message? # This is the main Sribi class, holding the different interaction methods. Thread.abort_on_exception = true class Sribi require 'socket' $server, $nick, $port, $command, $sender = "" $prefix = "!" $direct = false $channels = [""] $authnicks = [""] attr_accessor :debug, :nick #Sends a message directly to the server, e.g place("PRIVMSG foo Hello!") def place(s) strx = s.gsub(/\n/, "") strx = strx.gsub(/\r/, "") $con.send strx + "\n", 0 unless strx =~ /^(PONG)/ puts "[#{$nick}] " + strx if @debug end end #Create a connection to the server #* server is the server to connect to #* nick is the nick to use on the server #* port is the port to use connecting to the server #* realname is the real name to use connecting to the server #* host is the host to use connecting to the server # #Example: MyBot = Sribi.new("irc.boomerirc.net", "Botty") def initialize(server, nick, port="6667", realname = "Simple Ruby IRC Bot Interface", host = "www.steelwalrus.com/sribi") @debug = false $server = server $nick = nick $port = port $realname = realname $host = host $con = TCPSocket.new($server,$port) place("PASS SribiFrameworkSecurityProtocol") place("NICK " + $nick) place("USER #{$nick} #{$host} #{server} #{$realname}") Thread.new { while true do $connect_log = $con.recv(512) s = $connect_log.split("\r") for i in s puts i if @debug if i =~ /PING\ / then a = i.split("\:") place("PONG #{$host} :#{a[1]}") end parse(i) end end } Thread.new { while true do place("PONG #{$host} :#{$server}") sleep(30) end } end #parse message. Generally, this command should not be used. def parse(x) upmynick = $nick mynick = upmynick.downcase nick, chan, fullmsg, upfullmsg, msg, upmsg = "" s = x.split(":",3) if s[1] =~ /!/ nick = s[1].split("!")[0] end if s[1] =~ /\s/ chan = s[1].split(" ")[2] end if s[2] != nil fullmsg = s[2].downcase upfullmsg = s[2] if s[3] != nil fullmsg = fullmsg + s[3].downcase upfullmsg = upfullmsg + s[3] end fullmsg = fullmsg.strip upfullmsg = upfullmsg.strip end if upfullmsg == "\001VERSION\001" place("NOTICE #{nick} :\001VERSION Sribi IRC interface:1.1:Ruby\001") version = true end if chan if chan.start_with? "#" puts "#{chan} [#{nick}] #{upfullmsg}" Channel.get(chan).said(nick, upfullmsg) unless s[1].split(" ")[1] == "MODE" or s[1].split(" ")[1] == "PART" end end if s[1] != nil #names list if s[1].split(" ")[1] == "353" Channel.get(s[1].split(" ")[4]).names(s[2].split(" ")) s[2].split(" ").each do |name| unless User.get(name) or name == $nick User.new(name, self) end end end if s[1].split(" ")[1] == "332" $topic = upmsg end #WHOIS #first line containing realname, username and ip if s[1].split(" ")[1] == "311" message = s[1].split(" ") user = User.get(message[3]) user.username = message[4] user.ip = message[5] user.realname = upfullmsg end #second line (is registered or not) if s[1].split(" ")[1] == "307" if upfullmsg == "is a registered nick" User.get(s[1].split(" ")[3]).registered = true else User.get(s[1].split(" ")[3]).registered = false end end #third line (channels joined) if s[1].split(" ")[1] == "319" User.get(s[1].split(" ")[3]).chans = upfullmsg.split(" ") end #fourth line (idle/signon time) if s[1].split(" ")[1] == "317" User.get(s[1].split(" ")[3]).idletime = s[1].split(" ")[4].to_i User.get(s[1].split(" ")[3]).signontime = s[1].split(" ")[5].to_i end #if join/part if s[1].split(" ")[1] =~ /^(JOIN)/ unless nick == $nick Channel.get(s[2].split(" ")[0]).joined(nick) unless User.get(nick) User.new(nick, self) end end end if s[1].split(" ")[1] =~ /^(NICK)/ unless nick == $nick User.get(nick).name = upfullmsg end end if s[1].split(" ")[1] =~ /^(PART)/ unless nick == $nick Channel.get(s[1].split(" ")[2]).parted(User.get(nick)) User.remove(User.get(nick)) end end if s[1].split(" ")[1] =~ /^(KICK)/ if !nick == $nick Channel.get(s[1].split(" ")[2]).parted(s[1].split(" ")[3]) User.remove(User.get(nick)) else Channel.get(s[1].split(" ")[2]).kicked(nick, upfullmsg) end end if s[1].split(" ")[1] =~ /^(QUIT)/ unless nick == $nick Channel.get(s[1].split(" ")[2]).parted(User.get(nick)) User.remove(User.get(nick)) end end #if private message if s[1].split(" ")[1] =~ /^PRIVMSG/ && !chan.start_with?("#") if User.get(nick) User.get(nick).said(upfullmsg) unless version else User.new(nick, self) end end end end #Determines if a message is a CTCP action. def is_action?(message) if message.to_s.start_with?("\001") and message.to_s.end_with?("\001") return true end return false end #Send a CTCP notice to a channel/nick. target defaults to first channel joined. #Registers the current nick with NickServ. Default NickServ name is NickServ. Default email is nick@host. def register(password, email = "#{$nick}@#{$host}", nickserv = "NickServ") place("PRIVMSG #{nickserv} :register #{password} #{email}") puts "Registered with NickServ" end #Identify the current nick with NickServ. def identify(password, nickserv = "NickServ") place("PRIVMSG #{nickserv} : identify #{password}") puts "Sent identification to NickServ." end #Change the NickServ password for the current nick. def change_password(newpass, nickserv = "NickServ") place("PRIVMSG #{nickserv} : set password #{newpass}") puts "Changed NickServ password" end #Manipulate the list of authorised nicks for using the bot. #actions: # #* add adds a name to the list. #* remove removes names from the list. #* list lists authorised nicks. #* check checks if a nick is authorised. def authorised(action, *parameters) if action == "add" parameters.each do |parameter| $authnicks[$authnicks.length] = parameter end end if action == "remove" parameters.each do |parameter| $authnicks[$authnicks.index(parameter)] = "" end end if action == "list" return $authnicks end if action == "check" if $authnicks.include?(parameters[0]) return true end return false end end #USE WITH CAUTION - sets a global variable to a value def set_var(var, value) eval("$#{var} = '#{value}'") return "Succesfully set variable #{var} to #{value}" end #Change the prefix for commands def prefix(new_prefix = nil) if new_prefix $prefix = new_prefix puts "Set $prefix to #{new_prefix}" if @debug return end return $prefix end end #Sribi channel handling class. Each instance is a channel, with it's own variables and methods for parsing. class Channel @@channels = [] @@instances = [] @@gotcommand, @@gotmessage, @@joined, @@partbool = false @@user, @@message, @@join, @@part = "" attr_accessor :name, :rejoin_on_kick #Create a new Channel instance. bot is the Sribi instance associated with the channel. def initialize(channelname, bot) sleep 2 bot.place("JOIN #{channelname}") @@channels << channelname @@instances << self @@me = self @name = channelname @bot = bot @inchan = [] puts "Joined #{@name}" end #List the channels currently joined. def self.list return @@channels end #Get the Channel instance corresponding to the channelname. If no Channel instance has the name, returns nil. def self.get(channel) @@instances.each do |chan| if chan.name == channel return chan end end return end #Send a message to the channel. def say(message) @bot.place("PRIVMSG #{self.name} :#{message}") puts "#{@name} [#{@bot.nick}] #{message}" end #Send a notice to the channel. def notice(message) @bot.place("NOTICE #{self.name} :#{message}") puts "#{@name} -#{@bot.nick}- #{message}" end #Perform a CTCP action - In IRC clients, this is generally /me. def action(action) say "\001ACTION #{action}\001" puts "#{@name} #{@bot.nick} #{action}" end #Get a list of users in the channel. If prefix is false, the list will return without prefixes (~, @ etc.) def users(prefix = false) @usernames = [] @inchan.each do |user| @usernames << user.name end @users = [] unless prefix @usernames.each do |user| if user =~ /^\W/ user[0] = "" end end end @usernames.each do |name| @users << User.get(name) end return @users end #Sribi Functioning Method - Do not use unless you absolutely know what you're doing def joined(user) @inchan << user @@join = user @@joined = true return @inchan puts "#{user.name} has joined channel #{@name}" end #Sribi Functioning Method - Do not use unless you absolutely know what you're doing def parted(user) puts user if @inchan.include?(user) @inchan.delete(user) end @@part = user @@partbool = true puts "#{user.name} has left channel #{@name}" end #Sribi Functioning Method - Do not use unless you absolutely know what you're doing def names(namelist) namelist.each do |name| unless @inchan.include?(User.get(name)) @inchan << User.get(name) end end return @inchan end #Get a list of ops in the channel. def ops ops = [] @inchan.each do |user| if user.name =~ /^~|@|&/ ops << user end end return ops end #call-seq: # Channel.join? => false or [nick, Channel instance] # #Method for checking if a user haas joined the Channel. def self.join? if @@joined @@joined = false return [@@join, @@me] end return false end #call-seq: # Channel.part? => false or [nick, Channel instance] # #Method for checking if a user has left the Channel. def self.part? if @@partbool @@partbool = false return [@@part, @@me] end return false end #Check if a user is an op in the Channel. def is_op?(name) return self.ops.include?(name) end #call-seq: # Channel.command? => false or [command, nick, Channel instance] # #Method for checking if a message starting with the Sribi prefix has been said def self.command? if @@gotcommand @@gotcommand = false return [@@command, @@user, @@me] end return false end #call-seq: # Channel.message? => false or [message, nick, Channel instance] # #Method for checking if a message not starting with the Sribi prefix has been said def self.message? if @@gotmessage @@gotmessage = false return [@@message, @@user, @@me] end return false end #Sribi Functioning Method - Do not use unless you absolutely know what you're doing def said(nick, message) if message.start_with?(@bot.prefix) @@command = message.sub(@bot.prefix, "") @@gotcommand = true else @@gotmessage = true @@message = message end @@user = User.get(nick) end def self.instances return @@instances end def kicked(kicker, reason) if @rejoin_on_kick sleep 2 @bot.place("JOIN #{@name}") notice "I am set to rejoin on kick. If this is a problem, take it up with my creator. In the meantime, #{kicker}, '#{reason}' wasn't a very good reason, dont you think?" else @@instances.delete self g = 0 puts User.list.length puts "------------" User.list.each do |guy| if guy.channels.length == 0 User.remove(guy) else puts "#{guy.name} #{guy.channels.length}" end puts "a" g += 1 end puts g end end def rejoin(delay = 0) @bot.place("PART #{@name}") sleep delay @bot.place("JOIN #{@name}") end #Set the topic the channel def set_topic(new_topic) @bot.place("TOPIC #{self.name} #{topic}") puts "Set topic for #{self.name} to #{topic}" end #Set a mode, ie. mode("+v", "John") to set John +v (voiced) def mode(modes, target = "") @bot.place("MODE #{self.name} #{modes} #{target}") puts "Set mode #{self.name} #{modes} #{target}" end #Give a user voice. (mode +v) def voice(user) mode "+v", user.name end #Remove voice from a user. (mode -v) def devoice(user) mode "-v", user.name end #Make the channel moderated - Only ops and people with voice can talk. (mode +m) def moderated mode "+m" end #Make the channel unmoderated - Everyone can talk. (mode -m) def unmoderated mode "-m" end #Disallow nick change in the channel. (mode +N) def no_nick_change mode "+N" end #Allow nick change in the channel. (mode -N) def allow_nick_change mode "-N" end #Mute users in the channel - Set channel to moderated and remove voice from the users. def mute(*nicks) gotem = false moderated(channel) self.users.each do |user| voice(user) end users.each do |user| devoice(user) end end end #User handling class. Each person to interact with the bot is assigned a User instance. class User @@instances = [] @@gotmsg, @@gotcmd = false @@message, @@command = "" attr_accessor :name, :username, :ip, :realname, :registered, :idletime, :chans, :signontime #Create a new User instance. bot is the Sribi instance to be associated with it. def initialize(nick, bot) puts "Added User #{nick}" if nick =~ /^\W/ @prefix = nick[0...1] nick[0] = "" end @name =nick @bot = bot @@instances << self end #Send a private message to the User. def say(message) @bot.place("PRIVMSG #{self.name} :#{message}") puts ">>#{@name}<< #{message}" end #send a CTCP NOTICE to the user. def notice(message) @bot.place("NOTICE #{self.name} :#{message}") puts ">>-#{@name}-<< #{message}" end #Perform a CTCP action to the user; generally, this is /me def action(act) say("\001ACTION #{act}\001") puts "#{@name} :#{@bot.nick} #{message}" end #Change the nick of a User instance. def self.nick(oldnick, newnick) self.get(oldnick).name = newnick return self.get(newnick) end #Get a User instance with the specified nick. def self.get(nick) if nick =~ /^\W/ nick[0] = "" end @@instances.each do |user| if user.name == nick return user end end return nil end #Method for checking if the User has sent the bot a private message. Returns [message, User instance] def self.message? if @@gotmsg @@gotmsg = false return [@@message, @@me] end return false end #Method for checking if the User has sent a command via private message. Returns [command, User instance] def self.command? if @@gotcmd @@gotcmd = false return [@@command, @@me] end return false end #Sribi functining Method - do not use unless you know what you are doing! Tells the User class that a message has been said. def said(message) puts "[#{@name}(Private Message)] #{message}" if message.start_with?(@bot.prefix) @@command = message.sub(@bot.prefix, "") @@gotcmd = true else @@gotmsg = true @@message = message end @@me = self end #Lists all User instances. def self.list @@instances.each do |inst| puts "#{inst} #{inst.name}" end return @@instances end #Deletes a user. def self.remove(inst) @@instances.delete inst puts "Deleted #{inst} (#{inst.name})" end #Lists the channels the User instance has joined. def channels chans = [] Channel.instances.each do |chan| if chan.users.include? self chans << chan end end return chans end #Gets the whois information about the User. def whois @bot.place("WHOIS #{@name}") sleep 0.5 end end