Last active
December 10, 2015 10:38
-
-
Save mseymour/4422022 to your computer and use it in GitHub Desktop.
A very very very basic IRCbot.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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