Skip to content

Instantly share code, notes, and snippets.

@mseymour
Last active December 10, 2015 10:38
Show Gist options
  • Save mseymour/4422022 to your computer and use it in GitHub Desktop.
Save mseymour/4422022 to your computer and use it in GitHub Desktop.
A very very very basic IRCbot.
require 'socket'
require 'logger'
require 'logger/colors'
require 'thread'
Thread.abort_on_exception = true
class IRC
attr_reader :nick, :user, :gecos
attr_reader :server, :port
attr_reader :socket, :logger, :capabilities, :isupport, :casemapping
def initialize(nick, user, gecos, server, port=6667)
@logger = Logger.new(STDOUT)
@nick = nick
@user = user
@gecos = gecos
@server = server
@port = port
@isupport = {}
@queue = Queue.new
start
end
def nick=(newnick)
@nick = newnick
@socket.puts "NICK #@nick"
@logger.info ">> NICK #@nick"
end
def send_raw(s)
@socket.puts s
end
private
# @!group Basic IRC handling and parsing
# Parses a raw IRC message.
# @param s [String] The raw IRC message to parse.
# @return [Array] The command prefix (nil if none), the command, and an array of arguments.
def parse_message(s)
s = s.chomp
raise StandardError, 'Empty message!' if s.empty?
prefix, s = s[1..-1].split(' ', 2) if s[0].eql? ':'
if s.include? ' :'
s, trailing = s.split(' :', 2)
args = s.split
args << trailing
else
args = s.split(' ')
end
command = args.shift
return prefix, command, args
end
# Starts the connection ping thread in order to let the server know that we are still alive.
def start_ping_thread!
Thread.new do
@logger.debug "Ping thread started."
while true
sleep 120
@socket.puts "PING 0"
@logger.info ">> PING 0"
end
end
end
# Opens the connection, starts the ping thread, and listens for messages from the server.
def start
@socket = TCPSocket.open(@server, @port)
@logger.debug "addr: #{@socket.addr.join(":")}"
@logger.debug "peer: #{@socket.peeraddr.join(":")}"
registered = false
@socket.puts "USER #@user 0 * #@gecos"
@logger.info ">> USER #@user 0 * #@gecos"
self.nick = @nick
@socket.puts "CAP LS"
@logger.info ">> CAP LS"
@socket.puts "CAP END"
@logger.info ">> CAP END"
start_ping_thread!
while msg = @socket.gets do
m = parse_message(msg)
@logger.unknown '<< ' + msg.dump[1..-2]
command = m[1]
self.send("rpl_#{command.downcase}", m);
if ('001'..'004').include?(command) && !registered
registered = true
@socket.puts "JOIN #test"
@logger.info ">> JOIN #test"
@socket.puts "PRIVMSG #test :hi!"
@logger.info ">> PRIVMSG #test :hi!"
end
end
end
# @!group IRC named replies
# Response to client's CAP command, for IRCv3 support.
# See http://ircv3.atheme.org/specification/capability-negotiation-3.1 for more information.
def rpl_cap(m)
case m[2][1]
when 'LS'
@capabilities = m[2][2].split.map(&:intern)
end
end
def rpl_join(m)
@logger.info "Handling join: #{m}"
end
def rpl_part(m)
@logger.info "Handling part: #{m}"
end
def rpl_quit(m)
@logger.info "Handling quit: #{m}"
end
def rpl_notice(m)
@logger.info "Handling notice: #{m}"
end
def rpl_nick(m)
@logger.info "Handling nick: #{m}"
end
def rpl_privmsg(m)
@logger.info "Handling privmsg: #{m}"
end
def rpl_ping(m)
@logger.info ">> PONG :#{m[2][-1]}"
@socket.puts "PONG :#{m[2][-1]}"
end
# rpl_error is called when an error is reported to the client, i.e. the link is closed.
def rpl_error(m)
@logger.warn "Handling error: #{m}"
#raise StandardError, m.inspect
end
# @!group IRC numeric replies
# isupport
def rpl_005(m)
@isupport.update Hash[m[2][1..-2].map {|a| a.split('=') }]
@casemapping = @isupport['CASEMAPPING'].intern if @isupport.has_key?('CASEMAPPING')
end
def rpl_433(m)
@logger.info "Nickname already in use... trying #{@nick}_..."
self.nick = self.nick + "_"
end
# @!group Handling unknown replies
def handle_unknown_command(*m)
#@logger.debug "Handling unknown command: #{m}"
end
def method_missing(method_name, *arguments, &block)
self.send('handle_unknown_command', method_name, *arguments, &block)
end
def respond_to_missing?(method_name, include_private = false)
method_name.to_s.start_with?('rpl_') || super
end
end
IRC.new('nopanerica', 'nopan', 'No pans here!', 'localhost', '6667')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment