Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env ruby
- #
- # bones v0.03
- # by Jonathan Drain http://d20.jonnydigital.com/roleplaying-tools/dicebot
- # (run "bones-go.rb" first)
- #
- # NB: As a security measure, some IRC networks prevent IRC bots from joining
- # channels too quickly after connecting. Solve with this:
- # /msg bones @@@join #channel
- require 'socket'
- require 'strscan'
- require 'dicebox'
- module Bones
- class Client # an "instance" of bones; generally only one
- def initialize(nick, server, port, channels, admin, player_init_bonuses)
- @running = true
- @nick = nick
- @server = server # one only
- @port = port
- @channels = channels
- @admin = admin
- @player_init_bonuses = player_init_bonuses
- connect()
- run()
- end
- def connect
- @connection = Connection.new(@server, @port)
- @connection.speak "NICK #{@nick}"
- @connection.speak "USER #{@nick} bones * :Bones++ Dicebot: https://github.com/injate/BonesPlusPlus/"
- # This needs some work, faking the client port (54520) right now.
- # http://www.team-clanx.org/articles/socketbot-ident.html
- @connection.speak "IDENT 54520, #{@port} : USERID : UNIX : #{@nick}"
- # TODO: fix join bug
- join(@channels)
- end
- def join(channels)
- channels.each do |channel|
- # join channel
- @connection.speak "JOIN #{channel}"
- puts "Joining #{channel}"
- end
- end
- def join_quietly(channels)
- channels.each do |channel|
- # join channel
- @connection.speak("JOIN #{channel}", true)
- end
- end
- def run # go
- # stay connected
- # handle replies
- while @running
- while @connection.disconnected? # never give up reconnect
- sleep 10
- connect()
- end
- handle_msg (@connection.listen)
- end
- end
- def handle_msg(msg)
- case msg
- when nil
- #nothing
- when /END OF MESSAGE/ # For irc.gamesurge.net joining ASAP, the 1st join happens too early to work on some servers.
- join_quietly(@channels)
- when /^PING (.+)$/
- @connection.speak("PONG #{$1}", true) # PING? PONG!
- # TODO: Check if channels are joined before attempting redundant joins
- join_quietly(@channels)
- when /^:/ # msg
- message = Message.new(msg)
- respond(message)
- else
- puts "RAW>> #{msg}"
- #nothing
- end
- end
- def respond(msg)
- # msg :name, :hostname, :mode, :origin, :privmsg, :text
- if msg.name =~ /#{@admin}/ && msg.text =~ /^#{@nick}, quit/i
- quit(msg.text)
- end
- if msg.text =~ /^#{@nick}(:|,*) ?(\S+)( (.*))?/i
- prefix = @nick
- command = $2.downcase
- unless $4.nil? #optional args, downcase errors on nil
- args = $4.downcase
- end
- # do command - switch statement or use a command handler class
- c = command_handler(prefix, command, args)
- reply(msg, c) if c
- elsif msg.text =~ /^@@@join (#.*)/
- join $1.to_s
- elsif msg.text == "hay" # from here any kind of random text responses can be added
- reply(msg, "hay :v")
- elsif msg.text == "Testing, Testing, 123"
- reply(msg, "everyone has their own learning style")
- elsif msg.text =~ /^(!|@)(\S+)( (.*))?/ # until here
- prefix = $1
- command = $2
- args = $4
- #do command
- c = command_handler(prefix, command, args)
- reply(msg, c) if c
- elsif msg.text =~ /^(\d*#)?(\d+)d(\d+)/
- # DICE HANDLER
- dice = Dicebox::Dice.new(msg.text)
- begin
- d = dice.roll
- if (d.length < 350)
- reply(msg, d)
- else
- reply(msg, "I don't have enough dice to roll that!")
- end
- rescue Exception => e
- puts "ERROR: " + e.to_s
- reply(msg, "I don't understand...")
- end
- end
- end
- def command_handler(prefix, command, args)
- c = CommandHandler.new(prefix, command, args, @player_init_bonuses)
- return c.handle
- end
- def reply(msg, message) # reply to a pm or channel message
- if msg.privmsg
- @connection.speak "#{msg.mode} #{msg.name} :#{message}"
- elsif msg.text =~ /^(\d*#)?(\d+)d(\d+)/ # keeps the name for rolls
- @connection.speak "#{msg.mode} #{msg.origin} :#{msg.name}, #{message}"
- else
- @connection.speak "#{msg.mode} #{msg.origin} :#{message}"
- end
- end
- def pm(person, message)
- @connection.speak "PRIVMSG #{person} :#{message}"
- end
- def say(channel, message)
- pm(channel, message) # they're functionally the same
- end
- def notice(person, message)
- @conection.speak "NOTICE #{person} :#{message}"
- end
- def quit(message)
- @connection.speak "QUIT :#{message}"
- @connection.disconnect
- @running = false;
- end
- end
- class Message
- attr_accessor :name, :hostname, :mode, :origin, :privmsg, :text
- def initialize(msg)
- parse(msg)
- end
- def parse(msg)
- # sample messages:
- # :[email protected] PRIVMSG #bones :hi
- # :[email protected] PRIVMSG bones :hi
- # filter out bold and colour
- # feature suggested by KT
- msg = msg.gsub(/\x02/, '') # bold
- msg = msg.gsub(/\x03(\d)?(\d)?/, '') # colour
- case msg
- when nil
- puts "heard nil? wtf"
- when /^:(\S+)!(\S+) (PRIVMSG|NOTICE) ((#?)\S+) :(.+)/
- @name = $1
- @hostname = $2
- @mode = $3
- @origin = $4
- if ($5 == "#")
- @privmsg = false
- else
- @privmsg = true
- end
- @text = $6.chomp
- print()
- end
- end
- def print
- puts "[#{@origin}|#{@mode}] <#{@name}> #{@text}"
- end
- end
- class Connection # a connection to an IRC server; only one so far
- attr_reader :disconnected
- def initialize(server, port)
- @server = server
- @port = port
- @disconnected = false
- connect()
- end
- def connect
- # do some weird stuff with ports
- @socket = TCPSocket.open(@server, @port)
- puts "hammer connected!"
- @disconnected = false
- end
- def disconnected? # inadvertently disconnected
- return @socket.closed? || @disconnected
- end
- def disconnect
- @socket.close
- end
- def speak(msg,quietly = nil)
- begin
- if quietly != true
- puts("spoke>> " + msg)
- end
- @socket.write(msg + "\n")
- rescue Errno::ECONNRESET
- @disconnected = true;
- end
- end
- def listen # poll socket for lines. luckily, listen is sleepy
- sockets = select([@socket], nil, nil, 1)
- if sockets == nil
- return nil
- else
- begin
- s = sockets[0][0] # read from socket 1
- if s.eof?
- @disconnected = true
- return nil
- end
- msg = s.gets
- rescue Errno::ECONNRESET
- @disconnected = true
- return nil
- end
- end
- end
- end
- class CommandHandler
- def initialize(prefix, command, args, player_init_bonuses)
- @prefix = prefix
- @command = command
- @args = args
- @args.strip if @args
- @player_init_bonuses = player_init_bonuses
- end
- def handle
- case @command
- when "init", "initiative"
- result = handle_init
- when "chargen"
- result = handle_chargen
- when "rules", "rule"
- result = handle_rules
- when "help"
- result = handle_help
- else
- result = nil
- #end
- end
- return result
- end
- def handle_chargen
- set = []
- 6.times do
- roll = []
- 4.times do
- roll << rand(6)+1
- end
- roll = roll.sort
- total = roll[1] + roll[2] + roll[3]
- set << total
- end
- if set.sort[5] < 13
- return handle_chargen
- end
- return set.sort.reverse.join(", ")
- end
- def handle_rules
- case @args
- when "chargen", "pointsbuy", "pointbuy", "point buy", "points buy", "houserules", "house rules"
- result = "Iron Heroes style pointbuy, 26 points. "
- result += "Ability scores start at 10. Increments cost 1pt up to 15, 2pts up to 17, "
- result += "and 4pts up to 18, before racial modifiers. "
- result += "You may drop any one 10 to an 8 and spend the two points elsewhere. "
- result += "You may have up to one flaw and two traits."
- else
- result = nil
- #end
- end
- return result
- end
- def handle_help
- result = "Roll dice in the format '1d20+6'. Multiple sets as so: '2#1d20+6'. "
- result += "Rolls can be followed with a comment as so: '1d20+6 attack roll'. "
- result += "Separate multiple rolls with a semicolon, ';'. "
- result += "Features: Can add and subtract multiple dice, shows original rolls, "
- result += "high degree of randomness (uses a modified Mersenne Twister with a period of 2**19937-1)."
- result += "Bugs: must specify the '1' in '1d20'."
- return result
- end
- def handle_join(client,channel)
- client.join(channel)
- end
- def handle_init()
- players = @player_init_bonuses
- playerRolls = Hash.new
- playerFullRolls = Hash.new
- results = '| '
- resultsFull = '| '
- players.each { |player,bonus|
- init = Dicebox::Dice.new("1d10+"+bonus.to_s())
- initroll = init.roll
- playerFullRolls[player] = initroll
- /^[^:]*: (-?\d+)/ =~ initroll
- playerRolls[player] = Regexp.last_match(1).to_i(10)
- }
- playerRolls = playerRolls.sort {|a,b| a[0]<=>b[0]}
- playerRolls = playerRolls.sort {|a,b| a[1]<=>b[1]}
- playerRolls.each { |player,roll|
- results += "#{player}: #{roll} | "
- resultsFull += "#{player}: " + playerFullRolls[player] +" | "
- }
- if @args =~ /details?$/i || @args =~ /full?$/i
- # Show full rolls
- return resultsFull
- else
- # Just show the final roll
- return results
- end
- end
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement