rubot 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -0
- data/Rakefile +12 -0
- data/lib/core.rb +8 -0
- data/lib/core/command.rb +70 -0
- data/lib/core/dispatcher.rb +84 -0
- data/lib/core/listener.rb +13 -0
- data/lib/core/runner.rb +13 -0
- data/lib/extensions.rb +4 -0
- data/lib/extensions/kernel.rb +6 -0
- data/lib/extensions/string.rb +9 -0
- data/lib/init/bundler.rb +9 -0
- data/lib/irc.rb +7 -0
- data/lib/irc/constants.rb +10 -0
- data/lib/irc/message.rb +14 -0
- data/lib/irc/server.rb +149 -0
- data/lib/rubot.rb +12 -0
- metadata +83 -0
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# todo Add documentation here
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('rubot', '0.0.1') do |p|
|
6
|
+
p.description = "A Ruby Bot framwork for IRC"
|
7
|
+
p.url = "http://github.com/tombombadil/hello_world"
|
8
|
+
p.author = "Chris Thorn"
|
9
|
+
p.email = "thorncp @nospam@ gmail.com"
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
|
+
p.development_dependencies = []
|
12
|
+
end
|
data/lib/core.rb
ADDED
data/lib/core/command.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require "shellwords"
|
2
|
+
module Rubot
|
3
|
+
module Core
|
4
|
+
class Command
|
5
|
+
attr_reader :protected
|
6
|
+
|
7
|
+
def initialize(dispatcher)
|
8
|
+
@dispatcher = dispatcher
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(server, message)
|
12
|
+
args = Shellwords.shellwords(message.body.gsub(/(')/n, "\\\\\'"))
|
13
|
+
options = parse(args)
|
14
|
+
|
15
|
+
if options.help
|
16
|
+
# this to_s.lines.to_a business is a hack to not display the -h, --help line
|
17
|
+
# there's got to be an easier/better way to do this. but it'll work for now
|
18
|
+
server.msg(message.destination, options.help.to_s.lines.to_a[0..-2])
|
19
|
+
else
|
20
|
+
message.body = args.join(" ")
|
21
|
+
execute(server, message, options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def is_protected?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# commands override this
|
32
|
+
def execute(server, message, options)
|
33
|
+
server.msg(message.destincation, "unimplemented")
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.acts_as_protected
|
37
|
+
define_method(:is_protected?) do
|
38
|
+
true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.aliases(*aliases)
|
43
|
+
raise ArgumentError, 'only symbols allowed' unless aliases.all? {|a| a.is_a? Symbol}
|
44
|
+
define_method(:aliases) do
|
45
|
+
aliases
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse(args)
|
50
|
+
options = OpenStruct.new
|
51
|
+
@parser = OptionParser.new do |parser|
|
52
|
+
parser.banner = "Usage: !#{self.class.to_s.downcase} [options]"
|
53
|
+
|
54
|
+
options(parser, options)
|
55
|
+
|
56
|
+
parser.on_tail("-h", "--help", "Show this message") do
|
57
|
+
options.help = parser
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
@parser.parse!(args)
|
62
|
+
options
|
63
|
+
end
|
64
|
+
|
65
|
+
# override this to add more options in a command class
|
66
|
+
def options(parser, options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "thread"
|
2
|
+
|
3
|
+
module Rubot
|
4
|
+
module Core
|
5
|
+
class Dispatcher
|
6
|
+
|
7
|
+
attr_reader :commands, :listeners, :function_character, :config, :resource_lock
|
8
|
+
|
9
|
+
def initialize(config)
|
10
|
+
@config = config
|
11
|
+
@function_character = @config["function_character"]
|
12
|
+
|
13
|
+
@auth_list = config["auth_list"].split(',')
|
14
|
+
|
15
|
+
@resource_lock = Mutex.new
|
16
|
+
reload
|
17
|
+
# runners are only run on server connection, so there's no need them to be in reload
|
18
|
+
load_dir "runners", @runners = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def connected(server)
|
22
|
+
run_runners(server)
|
23
|
+
end
|
24
|
+
|
25
|
+
def reload
|
26
|
+
load_dir "commands", @commands = {}
|
27
|
+
load_dir "listeners", @listeners = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def handle_message(server, message)
|
31
|
+
if message.body =~ /^#{@function_character}([a-z_]+)( .+)?$/i
|
32
|
+
message.body = $2.nil? ? "" : $2.strip # remove the function name from the message
|
33
|
+
command = @commands[$1.underscore.to_sym]
|
34
|
+
|
35
|
+
if command.nil?
|
36
|
+
puts "#{$1} does not yield a command"
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
#if command is protected and user is not authenticated, return
|
41
|
+
if command.is_protected? && !@auth_list.include?(message.from)
|
42
|
+
server.msg(message.destination, "unauthorized")
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
command.run(server, message)
|
47
|
+
elsif message.from != server.nick
|
48
|
+
@listeners.each_value do |listener|
|
49
|
+
listener.execute(server, message)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_auth(nick)
|
55
|
+
unless @auth_list.include? nick
|
56
|
+
@auth_list.push nick
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def remove_auth(nick)
|
61
|
+
@auth_list.delete nick
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def run_runners(server)
|
67
|
+
@runners.each_value {|r| r.run(server)}
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_dir(dir, set = nil)
|
71
|
+
Dir["#{dir}/*.rb"].each do |file|
|
72
|
+
load file
|
73
|
+
|
74
|
+
next unless set
|
75
|
+
|
76
|
+
name = File.basename(file, ".rb")
|
77
|
+
clazz = eval(name.camelize).new(self)
|
78
|
+
set[name.to_sym] = clazz
|
79
|
+
clazz.aliases.each{|a| set[a] = clazz} if clazz.respond_to? :aliases
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/core/runner.rb
ADDED
data/lib/extensions.rb
ADDED
data/lib/init/bundler.rb
ADDED
data/lib/irc.rb
ADDED
data/lib/irc/message.rb
ADDED
data/lib/irc/server.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require "socket"
|
2
|
+
|
3
|
+
module Rubot
|
4
|
+
module Irc
|
5
|
+
class Server
|
6
|
+
include Rubot::Irc::Constants
|
7
|
+
attr_reader :nick, :connected_at
|
8
|
+
|
9
|
+
def initialize(dispatcher)
|
10
|
+
dispatcher.config["server"].each_pair do |key, value|
|
11
|
+
instance_variable_set("@#{key}".to_sym, value)
|
12
|
+
end
|
13
|
+
@channels = @channels.split(",").collect(&:strip)
|
14
|
+
@dispatcher = dispatcher
|
15
|
+
end
|
16
|
+
|
17
|
+
def connect
|
18
|
+
return if @is_connected
|
19
|
+
|
20
|
+
@conn = TCPSocket.open(@host, @port, @vhost)
|
21
|
+
raw "USER #{@nick} #{@nick} #{@nick} :#{@real_name}"
|
22
|
+
change_nick @nick
|
23
|
+
join @channels
|
24
|
+
|
25
|
+
begin
|
26
|
+
main_loop()
|
27
|
+
rescue Interrupt
|
28
|
+
rescue Exception => detail
|
29
|
+
puts detail.message()
|
30
|
+
print detail.backtrace.join("\n")
|
31
|
+
retry
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def quit
|
36
|
+
raw "QUIT :#{@quit_message}"
|
37
|
+
@conn.close
|
38
|
+
end
|
39
|
+
|
40
|
+
def change_nick(new_nick)
|
41
|
+
raw "NICK #{new_nick}"
|
42
|
+
@nick = new_nick
|
43
|
+
end
|
44
|
+
|
45
|
+
def join(channels)
|
46
|
+
channels = channels.split(',') if channels.is_a? String
|
47
|
+
@channels.concat(channels).uniq!
|
48
|
+
bulk_command("JOIN %s", channels)
|
49
|
+
end
|
50
|
+
|
51
|
+
def part(channels)
|
52
|
+
channels = channels.split(',') if channels.is_a? String
|
53
|
+
@channels.reject! { |channel| channels.include?(channel) }
|
54
|
+
bulk_command("PART %s :#{@quit_message}", channels)
|
55
|
+
end
|
56
|
+
|
57
|
+
def msg(destination, message)
|
58
|
+
message = message.to_s.split("\n") unless message.is_a? Array
|
59
|
+
build_message_array(message).each do |l|
|
60
|
+
raw "PRIVMSG #{destination} :#{l}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def action(destination, message)
|
65
|
+
msg(destination, "\001ACTION #{message}\001")
|
66
|
+
end
|
67
|
+
|
68
|
+
def raw(message)
|
69
|
+
puts "--> #{message}"
|
70
|
+
@conn.puts "#{message}\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
def names(channel)
|
74
|
+
raw "NAMES #{channel}"
|
75
|
+
@conn.gets.split(":")[2].split(" ")
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def main_loop
|
81
|
+
while true
|
82
|
+
ready = select([@conn])
|
83
|
+
next unless ready
|
84
|
+
|
85
|
+
return if @conn.eof
|
86
|
+
s = @conn.gets
|
87
|
+
handle_server_input(s)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def handle_server_input(s)
|
92
|
+
puts s
|
93
|
+
|
94
|
+
case s.strip
|
95
|
+
when /^PING :(.+)$/i
|
96
|
+
raw "PONG :#{$1}"
|
97
|
+
when /^:([-.0-9a-z]+)\s([0-9]+)\s(.+)\s(.*)$/i
|
98
|
+
handle_meta($1, $2.to_i, $4)
|
99
|
+
when /^:(.+?)!(.+?)@(.+?)\sPRIVMSG\s(.+)\s:(.+)$/i
|
100
|
+
message = Rubot::Irc::Message.new($1, $4 == @nick ? $1 : $4, $5)
|
101
|
+
# TODO add ability to pass events other than privmsg to dispatcher. ie, nick changes, parts, joins, quits, bans, etc, etc
|
102
|
+
@dispatcher.handle_message(self, message)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# performs the same command on each element in the given collection, separated by comma
|
107
|
+
def bulk_command(formatted_string, elements)
|
108
|
+
if elements.is_a? String
|
109
|
+
elements = elements.split(',')
|
110
|
+
end
|
111
|
+
|
112
|
+
elements.each do |e|
|
113
|
+
raw sprintf(formatted_string, e.to_s.strip)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def handle_meta(server, code, message)
|
118
|
+
case code
|
119
|
+
when ERR_NICK_IN_USE
|
120
|
+
if @nick == @alt_nick
|
121
|
+
puts "all nicks used, don't know how to name myself."
|
122
|
+
quit
|
123
|
+
exit!
|
124
|
+
end
|
125
|
+
change_nick @alt_nick
|
126
|
+
join @channels
|
127
|
+
when WELLCOME
|
128
|
+
@dispatcher.connected(self)
|
129
|
+
@is_connected = true
|
130
|
+
@connected_at = Time.now
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def string_to_irc_lines(str)
|
135
|
+
str.split(" ").inject([""]) do |arr, word|
|
136
|
+
arr.push("") if arr.last.size > MAX_MESSAGE_LENGTH
|
137
|
+
arr.last << "#{word} "
|
138
|
+
arr
|
139
|
+
end.map(&:strip)
|
140
|
+
end
|
141
|
+
|
142
|
+
def build_message_array(arr)
|
143
|
+
arr.each_with_index.map do |message, index|
|
144
|
+
message.size > MAX_MESSAGE_LENGTH ? string_to_irc_lines(message) : arr[index]
|
145
|
+
end.flatten
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/rubot.rb
ADDED
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Chris Thorn
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-03-04 00:00:00 -09:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: A Ruby Bot framwork for IRC featuring reloadable commands and listeners.
|
22
|
+
email: thorncp@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README
|
29
|
+
files:
|
30
|
+
- lib/core/command.rb
|
31
|
+
- lib/core/dispatcher.rb
|
32
|
+
- lib/core/listener.rb
|
33
|
+
- lib/core/runner.rb
|
34
|
+
- lib/core.rb
|
35
|
+
- lib/extensions/kernel.rb
|
36
|
+
- lib/extensions/string.rb
|
37
|
+
- lib/extensions.rb
|
38
|
+
- lib/init/bundler.rb
|
39
|
+
- lib/irc/constants.rb
|
40
|
+
- lib/irc/message.rb
|
41
|
+
- lib/irc/server.rb
|
42
|
+
- lib/irc.rb
|
43
|
+
- lib/rubot.rb
|
44
|
+
- README
|
45
|
+
- Rakefile
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://github.com/thorncp/rubot
|
48
|
+
licenses: []
|
49
|
+
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options:
|
52
|
+
- --line-numbers
|
53
|
+
- --inline-source
|
54
|
+
- --title
|
55
|
+
- NiftyGenerators
|
56
|
+
- --main
|
57
|
+
- README.rdoc
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 1
|
73
|
+
- 2
|
74
|
+
version: "1.2"
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.3.6
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: A Ruby Bot framwork for IRC
|
82
|
+
test_files: []
|
83
|
+
|