12 SLEEP_TABLE = [1, 2, 5, 15, 30, 60, 120, 300]
14 RUBYBOT_VERSION = "0.1"
16 DCProtocol.registerClientVersion("RubyBot", RUBYBOT_VERSION)
21 options[:config_file] = "dcbot.conf"
22 OptionParser.new do |opts|
23 opts.banner = "Usage: dcbot.rb [options]"
25 opts.on("--config filename", "Use filename as the config file", "[default: dcbot.conf]") do |filename|
26 options[:config_file] = filename
28 opts.on("--[no-]debug", "Sets the debug flag") { |flag| options[:debug] = flag }
29 opts.on("--[no-]peer-debug", "Sets the debug flag for peer connections") { |flag| options[:peer_debug] = flag }
32 config = IniReader.read(options[:config_file])
34 global_config = config.find { |section| section[0] == "global" }
36 if options.has_key? :debug then
37 debug = options[:debug]
38 elsif global_config and global_config[1].has_key? "debug" then
39 debug = global_config[1]["debug"].downcase
40 if debug == "true" then
45 connections = config.select { |section| section[0] == "connection" }
46 # FIXME: use all config sections
47 connection = connections[0][1]
48 host = connection["host"]
49 port = connection["port"].to_i
50 nickname = connection["nickname"]
51 description = connection["description"]
53 if connection.has_key? "prefix" then
54 PluginBase.cmd_prefix = connection["prefix"]
58 EventMachine::open_keyboard KeyboardInput
59 sockopts = { :description => description,
61 :peer_debug => options[:peer_debug] }
62 setupConnection(host, port, nickname, sockopts, 0)
67 def setupConnection(host, port, nickname, sockopts, sleep)
68 $socket = DCClientProtocol.connect(host, port, nickname, sockopts) do |c|
69 c.registerCallback :message do |socket, sender, message, isprivate|
70 if isprivate or sender == "*Dtella" then
71 puts "<#{sender}> #{message}"
73 if message[0,1] == PluginBase::CMD_PREFIX then
74 cmd, args = message[1..-1].split(" ", 2)
75 args = "" if args.nil?
76 if cmd == "reload" and isprivate then
77 # special sekrit reload command
78 PluginBase.loadPlugins
79 socket.sendPrivateMessage(sender, "Plugins have been reloaded")
80 elsif cmd == "quit" and isprivate then
81 # super-sekrit quit command
83 elsif PluginBase.has_command?(cmd) then
85 PluginBase.dispatch(socket, cmd, sender, isprivate, args)
86 rescue StandardError => e
87 socket.sendPrivateMessage(sender, "An error occurred executing your command: #{e.to_s}")
88 STDERR.puts "ERROR: #{e.to_s}"
89 PP.pp(e.backtrace, STDERR)
92 socket.sendPrivateMessage(sender, "Unknown command: #{PluginBase::CMD_PREFIX}#{cmd}")
96 c.registerCallback :error do |socket, message|
97 STDERR.puts "! #{message}"
99 c.registerCallback :peer_error do |socket, peer, message|
100 STDERR.puts "! Peer #{peer.host}:#{peer.port}: #{message}"
102 c.registerCallback :unbind do |socket|
104 # this is our only socket for the moment
105 EventMachine.stop_event_loop
107 EventMachine::add_timer(SLEEP_TABLE[sleep]) do
108 sleep += 1 unless sleep == SLEEP_TABLE.size - 1
109 setupConnection(host, port, nickname, sockopts, sleep)
113 c.registerCallback :connected do |socket|
115 socket.registerCallback :unbind do |socket|
117 # this is our only socket for the moment
118 EventMachine.stop_event_loop
120 setupConnection(host, port, nickname, sockopts, 0)
124 c.registerCallback :reverse_connection do |socket, user|
125 puts "* Bouncing RevConnectToMe back to user: #{user.nickname}"
127 c.registerCallback :reverse_connection_ignored do |socket, user|
128 puts "* Ignoring RevConnectToMe from user: #{user.nickname}"
130 c.registerCallback :peer_initialized do |socket, peer|
131 puts "* Connecting to peer: #{peer.host}:#{peer.port}"
133 c.registerCallback :peer_unbind do |socket, peer|
134 peer_id = "#{peer.host}:#{peer.port}"
135 peer_id << " (#{peer.remote_nick})" if peer.remote_nick
136 puts "* Connection to peer #{peer_id} closed"
138 c.registerCallback :peer_get do |socket,peer,filename|
139 peer_id = "#{peer.host}:#{peer.port}"
140 peer_id << " (#{peer.remote_nick})" if peer.remote_nick
141 puts "* Peer #{peer_id} requested: #{filename}"
147 Signal.trap 'INT', 'DEFAULT'
148 STDERR.puts "\nShutting down..."
149 # TODO: cancel all client2client connections that are in a cancel-able state
150 EventMachine.stop_event_loop