our/our.rb

115 lines
2.4 KiB
Ruby
Executable File

#!/usr/bin/env ruby
require 'open3'
require 'socket'
require 'timeout'
# configurable environment variables
nick = ENV['OUR_NICK'] || 'our'
channels = ENV['OUR_CHANNELS'] || '#tildetown,#bots'
prefix = ENV['OUR_PREFIX'] || "#{nick}/"
cmds_dir = ENV['OUR_CMDS_DIR'] || '/town/our'
module IRC
class User
attr_accessor :s
def initialize addr, port, nick
@hooks = []
@s = TCPSocket.open addr, port.to_s
s.puts "USER #{nick} fakehost whatevenisaservername :beep boop"
s.puts "NICK #{nick}"
hook do |m|
next unless m.cmd == 'PING'
raw "PING #{nick}"
end
end
def raw msg
@s.puts msg
end
def join chan
raw "JOIN #{chan}"
end
def privmsg target, msg
raw "PRIVMSG #{target} :#{msg}"
end
def hook &h
@hooks << h
end
def loop
while line = s.gets
msg = Message.new line
@hooks.each{|h| h.call(msg)}
end
end
end
class Message
attr_accessor :prefix, :cmd, :args, :raw
# TODO custom constructor
def initialize msg
msg = msg.delete_suffix "\r\n"
@raw = msg
@prefix = nil
@prefix, msg = msg[1..].split(' ', 2) if msg[0] == ':'
@cmd, msg = msg.split(' ', 2)
@args = []
while msg and not msg.empty?
if msg[0] == ':'
@args << msg[1..]
break
end
s, msg = msg.split(' ', 2)
@args << s
end
end
end
end
puts "starting"
i = IRC::User.new 'localhost', 6667, nick
channels.split(',').each { |channel| i.join channel }
i.hook do |msg|
next unless msg.cmd == 'PRIVMSG'
target, content = msg.args
next unless content.delete_prefix! prefix
cmd, args = content.split(' ', 2)
cmd = "#{cmds_dir}/#{cmd}"
args ||= ''
next unless File.exists? cmd
if not File.executable? cmd
i.privmsg target, "#{cmd} isn't executable. try chmod +x"
next
end
begin
Open3.popen2e(cmd, args, msg.prefix, target) do |_, stdout, wait_thread|
out = nil
Timeout::timeout(3) do
out = stdout.gets # only interested in the first line of output
stdout.gets until stdout.eof? # make sure process finishes in time allotted
end
i.privmsg target, out if out
rescue Timeout::Error
Process.kill("KILL", wait_thread.pid)
i.privmsg target, "[our.rb] command timed out"
end
rescue Exception => e
i.privmsg target, "[our.rb] #{e.to_s}"
end
next true
end
i.loop