Toshiko is a horrible IRC Bot. But she is absolutely wonderful, when you understand why I created her. Toshiko is my attempt at learning the Ruby programming language. For me, the best way to learn anything is to apply it toward something that I enjoy. Now before you get too impressed, I’m not starting from ground zero. What little I know about ruby stems from my experience working with RPG Maker’s RGSS, which is essentially a stripped down version of Ruby with custom game libraries stacked in an embedded scripting environment. It’s a good base.
I had contemplated doing this project in python, however, I decided to not push myself so hard and to work with something that was already familiar to me at some level. Advanced Ruby programmers will probably have lot’s of comments about what I’m doing wrong in my code. That’s fine, I know that I’m not an expert in this language. My knowledge of this language is laughable at best. If you see me doing something that’s genuinely bad, or that I could be doing better, let me know. I’m doing this to learn, I accept the fact that I’ll make horrible mistakes and errors along the way. I also appreciate any advice that I can get.
Alright, now that I have gotten the introduction out of the way, let me show you my script. It’s not fully functional yet, it only idles, but the essentials are there. You’ll notice that I’ve included some stuff that I’m not using yet, such as the thread library. That’s because, at the time of posting, I am getting ready to implement threading. I’m doing some research on it now, and only included the dependency so that I wouldn’t forget once I got to actually coding it. Anyway, enjoy, and try to ignore the fact that there is code in there that’s not actually being used, or has variables that aren’t created yet. This is a work in progress after all
#-------------------------------------------------------------
# Project: Toshiko IRC Bot
# Version: 0.5.0-alpha
# Author: Mark LaDoux
# E-mail: mark.ladoux@gmail.com
# Website: http://markladoux.com/
# Copyright: Copyright © 2012, Mark LaDoux. ALL RIGHTS RESERVED
#-------------------------------------------------------------
#----------------------------------------------------------
# include dependencies
#----------------------------------------------------------
require 'socket'
require 'thread'
#-------------------------------------------------------------
# Toshiko Bot Class
#-------------------------------------------------------------
class Toshiko
#-------------------------------------------------------------
# Attribute Accessors ( Read and Write )
#-------------------------------------------------------------
attr_accessor(
:nick, # Use this nick
)
#-------------------------------------------------------------
# Attribute Readers ( Read only, no write access)
#-------------------------------------------------------------
attr_reader(
:version, # Class version
:socket, # Network socket
:dead_socket, # is the network socket dead?
)
#-------------------------------------------------------------
# Attribute Writers ( Write only, no read access )
#-------------------------------------------------------------
attr_writer(
:server, # Connect to this server
:port, # Use this port
:server_password, # Use this password
:nick_password, # Identify with this nick
:real_name, # Use this real name
:use_vhost, # use a vhost
:verbose, # return verbose error messages
)
#-------------------------------------------------------------
# Initialize class!
#-------------------------------------------------------------
def initialize(opts = {})
# Library Version
@version = '0.5.0-alpha'
# Grab user configurations
@server = opts[:server]
@port = opts[:port] || 6667
@server_password = opts[:server_password]
@nick = opts[:nick]
@nick_password = opts[:nick_password]
@real_name = opts[:real_name] || "Toshiko IRCBot #{@version}"
@use_vhost = opts[:use_vhost]
@verbose = opts[:verbose]
# get defaults
defaults
end
#-------------------------------------------------------------
# Defaults
#-------------------------------------------------------------
def defaults
@dead_socket = true
@reconnect = true
end
#----------------------------------------------------------
# Create socket
#
# Creates a TCP connection to the requested server
#----------------------------------------------------------
def create_socket
# set socket dead to false
@dead_socket = false
# create our socket
begin
@socket = TCPSocket.new(@server, @port)
rescue StandardError => e
@dead_socket = true
puts "[ERROR] Unable to create connection to #{@server}"
puts "[ERROR] #{e.inspect}"
raise
end
end
#----------------------------------------------------------
# Run the bot
#----------------------------------------------------------
def run
# create a socket
create_socket
# ident
@socket.puts "NICK #{@nick}"
@socket.puts "USER #{@nick} 0 * :#{@real_name}"
# start main loop
main_loop
end
#----------------------------------------------------------
# Main processing loop
#----------------------------------------------------------
def main_loop
until @socket.eof do
# get our data
@raw = @socket.gets
if @verbose
puts "[RECV] #{@raw}"
end
# check for pings
if @raw.match(/^PING :(.*)/)
@socket.puts "PONG #{$~[1]}"
if @verbose
puts "PONG #{$~[1]}"
end
# we're done here, go to next loop iteration
next
end
# we'll do more later, this is just to test our
# connection before I start implementing the threads
end
@dead_socket = true
run unless @reconnect == false
end
#----------------------------------------------------------
# Missing Method
#
# Checks if method is prefixed with a term that the script
# should ignore, and continues processing gracefully.
# This is for methods that will vary from bot to bot
# and won't necessarily all be there. Non-prefixed methods
# will of course be handled normally, and will cause the
# bot to crash if they are missing. This is necessary, as
# if we don't treat them normally, certain tests will act
# strangly. All elements with values of nil or false, will
# return true. The call to super stops that from happening
# and causes data to remain as expected.
#----------------------------------------------------------
def method_missing(meth, *args, &block)
# missing data parser, we don't want to die on this one
if meth =~ /^process_(.+)$/
if @verbose
puts "[WARN] No parsers #{@command} data defined"
end
# missing trigger case, we don't want to die on this one either
elsif meth =~ /^on_(.+)$/
if @verbose
puts "[WARN] No triggers for #{@command} event defined"
end
# Everything else can go to hell!
else
super
end
end
end