rubot 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,16 +5,25 @@ require "optparse"
5
5
  module Rubot
6
6
  module Core
7
7
  # Base class that handles the dirty work for IRC commands.
8
- # All commands belong in the /commands directory and
9
- # inherit this class.
10
- #
11
- # Since:: 0.0.1
12
8
  class Command
13
9
 
10
+ # Takes an instance of Rubot::Core::Dispatcher. Any
11
+ # child class that needs a constructor should override
12
+ # this method.
13
+ #
14
+ # ==== Parameters
15
+ # dispatcher<Rubot::Core::Dispatcher>:: The dispatcher that was used to create
16
+ # the instance of the command.
14
17
  def initialize(dispatcher)
15
18
  @dispatcher = dispatcher
16
19
  end
17
20
 
21
+ # Runs the command with the given server and message.
22
+ #
23
+ # ==== Parameters
24
+ # server<Rubot::Irc::Server>:: Server instance the command should use for
25
+ # messaging and information.
26
+ # message<Rubot::Irc::Message>:: The message that invoked the command.
18
27
  def run(server, message)
19
28
  if protected? && !message.authenticated
20
29
  server.msg(message.destination, "unauthorized")
@@ -34,29 +43,64 @@ module Rubot
34
43
  end
35
44
  end
36
45
 
46
+ # Whether the command is marked as protected or not (using Command::acts_as_protected).
37
47
  def protected?
38
48
  false
39
49
  end
40
50
 
41
51
  private
42
52
 
43
- # commands override this
53
+ # The internal execution of a command, called by #run. This is the method all
54
+ # children classes should override.
55
+ #
56
+ # ==== Paramters
57
+ # server<Rubot::Irc::Server>:: Server instance the command should use for messaging and information.
58
+ # message<Rubot::Irc:Message>:: The message that invoked the command.
59
+ # options<OpenStruct>:: The options that were parsed from the message that invoked the command. The
60
+ # parsing is handled in #run
44
61
  def execute(server, message, options)
45
62
  server.msg(message.destincation, "unimplemented")
46
63
  end
47
64
 
65
+ # Marks the command as protected. Commands that are protected can only be accessed by users who
66
+ # are authenticated. If you only need pieces of the command to be protected, this is not the method
67
+ # you're looking for. In that case, use Message#authenticated, which is populated automatically
68
+ # depending on the invoking user.
69
+ #
70
+ # ==== Example
71
+ # class Quit < Rubot::Core::Command
72
+ # acts_as_protected
73
+ #
74
+ # def execute(server, message, options)
75
+ # server.quit
76
+ # exit
77
+ # end
78
+ # end
48
79
  def self.acts_as_protected
49
80
  define_method(:protected?) do
50
81
  true
51
82
  end
52
83
  end
53
84
 
85
+ # Allows a command to be called by different names (aliases).
86
+ #
87
+ # ==== Example
88
+ # This command can be invoked by <em>!hi</em>, <em>!hello</em>, or <em>!whats_up</em>.
89
+ # class Hi < Rubot::Core::Command
90
+ # aliases :hello, :whats_up
91
+ #
92
+ # def execute(server, message, options)
93
+ # server.msg(message.destination, "hi everybody!")
94
+ # end
95
+ # end
54
96
  def self.aliases(*aliases)
55
97
  define_method(:aliases) do
56
98
  aliases
57
99
  end
58
100
  end
59
101
 
102
+ # Parses the given array using the default options (help) and any options specified
103
+ # by the child class by overriding #options.
60
104
  def parse(args)
61
105
  options = OpenStruct.new
62
106
  @parser = OptionParser.new do |parser|
@@ -73,7 +117,25 @@ module Rubot
73
117
  options
74
118
  end
75
119
 
76
- # override this to add more options in a command class
120
+ # Method to be overriden by child class if extra options are to be used. Options are
121
+ # parsed with OptParse.
122
+ #
123
+ # ==== Example
124
+ # class Hi < Rubot::Core::Command
125
+ #
126
+ # def execute(server, message, options)
127
+ # if options.bye
128
+ # server.msg(message.destination, "bye everybody!")
129
+ # else
130
+ # server.msg(message.destination, "hi everybody!")
131
+ # end
132
+ # end
133
+ #
134
+ # def options(parser, options)
135
+ # parser.on("-b", "--bye", "Instead of saying hi, say goodbye") do |bye|
136
+ # options.bye = bye
137
+ # end
138
+ # end
77
139
  def options(parser, options)
78
140
  end
79
141
  end
@@ -2,14 +2,36 @@ require "thread"
2
2
 
3
3
  module Rubot
4
4
  module Core
5
- # The middle man. The Dispatcher takes incomming messages
6
- # from the server and determines the appropriate action to
7
- # take, handing the messages off to commands or listeners.
8
- #
9
- # Since:: 0.0.1
5
+ # The middle man. The Dispatcher takes incomming messages from the server and
6
+ # determines the appropriate action to take, handing the messages off to commands
7
+ # and listeners.
10
8
  class Dispatcher
11
- attr_reader :commands, :listeners, :function_character, :config, :resource_lock
9
+ # Hash that holds instances of commands registered with the dispatcher. There
10
+ # is a single instance of each command, with its name, and aliases, as keys
11
+ # to that instance.
12
+ attr_reader :commands
13
+
14
+ # Hash that holds instances of listeners registered with the dispatcher. There
15
+ # is a single instance of each listener, with its name as the key to that instance.
16
+ attr_reader :listeners
17
+
18
+ # The value used to denote a command, this is pulled from config.
19
+ attr_reader :function_character
20
+
21
+ # The config hash that was used to create the Dispatcher instance.
22
+ attr_reader :config
23
+
24
+ # Mutex that should be used when accessing anything in <em>/resources</em>. This
25
+ # will most likely be moved to the resource manager when it is implemented.
26
+ attr_reader :resource_lock
12
27
 
28
+ # Creates an instance of Dispatcher using the given config hash. Values expected to
29
+ # be in this hash are:
30
+ # * function_character - The character used to denote a command
31
+ # * auth_list - Comma separated string of authenticated users
32
+ #
33
+ # ==== Parameters
34
+ # config<Hash>:: Hash containing config values
13
35
  def initialize(config)
14
36
  @config = config
15
37
  @function_character = @config["function_character"]
@@ -22,15 +44,28 @@ module Rubot
22
44
  load_dir "runners", @runners = {}
23
45
  end
24
46
 
47
+ # Called when successful connection is made to a server. This is when the runners are
48
+ # executed.
49
+ #
50
+ # ==== Parameters
51
+ # server<Rubot::Irc::Server>:: The server instance that has successfully connected
25
52
  def connected(server)
26
53
  run_runners(server)
27
54
  end
28
55
 
56
+ # Exposed method to reload all commands and listeners.
29
57
  def reload
30
58
  load_dir "commands", @commands = {}
31
59
  load_dir "listeners", @listeners = {}
32
60
  end
33
61
 
62
+ # Determines how to handle a message from the server. If the message fits the format
63
+ # of a command, an attempt is made to find and execute that command. Otherwise, the
64
+ # message is passed to the listeners.
65
+ #
66
+ # ==== Parameters
67
+ # server<Rubot::Irc::Server>:: The server where the messge was received
68
+ # message<Rubot:Irc::Message>:: The message to handle
34
69
  def handle_message(server, message)
35
70
  if message.body =~ /^#{@function_character}([a-z_]+)( .+)?$/i
36
71
  message.body = $2.nil? ? "" : $2.strip # remove the function name from the message
@@ -45,30 +80,57 @@ module Rubot
45
80
  end
46
81
  end
47
82
 
83
+ # Finds a command based on the message alias, and determines if the invoking user
84
+ # is authenticated.
85
+ #
86
+ # ==== Parameters
87
+ # message<Rubot::Irc::Message>:: The message to be used
48
88
  def command_from_message(message)
49
- command = @commands[message.alias]
89
+ command = @commands[message.alias]
50
90
  message.authenticated = authenticated?(message.from) unless command.nil?
51
91
  command
52
92
  end
53
93
 
94
+ # Determines if the given nick has authenticated with the bot.
95
+ #
96
+ # ==== Parameters
97
+ # nick<String>:: The nick to check for auth privledges
54
98
  def authenticated?(nick)
55
99
  @auth_list.include?(nick)
56
100
  end
57
101
 
102
+ # Adds nick to the authenticated list.
103
+ #
104
+ # ==== Parameters
105
+ # nick<String>:: The nick to add to the authenticated list
58
106
  def add_auth(nick)
59
107
  @auth_list << nick unless authenticated?(nick)
60
108
  end
61
109
 
110
+ # Removes nick from the authenticated list.
111
+ #
112
+ # ==== Parameters
113
+ # nick<String>:: The nick to remove from the authenticated list
62
114
  def remove_auth(nick)
63
115
  @auth_list.delete nick
64
116
  end
65
117
 
66
118
  private
67
119
 
120
+ # Runs all runners using the given server instance.
121
+ #
122
+ # ==== Parameters
123
+ # server<Rubot::Irc::Server>:: Server instance
68
124
  def run_runners(server)
69
125
  @runners.each_value {|runner| runner.run(server)}
70
126
  end
71
127
 
128
+ # Loads all files in the given directory and stores the class instances
129
+ # in the given set. This is used to load commands, listeners, and runners.
130
+ #
131
+ # ==== Parameters
132
+ # dir<String>:: Directory of files to load
133
+ # set<Hash>:: Hash to store class instances in
72
134
  def load_dir(dir, set)
73
135
  Dir["#{dir}/*.rb"].each do |file|
74
136
  load file
@@ -76,6 +138,14 @@ module Rubot
76
138
  end
77
139
  end
78
140
 
141
+ # Takes a file and creates an instance of the class within the file and stores it
142
+ # in hash, with the base filename as the key. If the class instance responds to
143
+ # :aliases, these are used as keys to the instance as well.
144
+ #
145
+ # ==== Parameters
146
+ # file<String>:: Filename to use for instantiation
147
+ # hash<Hash>:: Hash to store the instance in
148
+ # ext<String>:: File extension of the file to load
79
149
  def file_to_instance_hash(file, hash, ext = ".rb")
80
150
  name = File.basename(file, ext)
81
151
  clazz = eval(name.camelize).new(self)
@@ -1,10 +1,33 @@
1
1
  module Rubot
2
2
  module Core
3
+ # Base class for all listeners. A listener cannot be called directly, but
4
+ # <em>listens</em> to messages on a server.
5
+ #
6
+ # ==== Example
7
+ # This listener responds when the bot is greeted.
8
+ # class Greet < Rubot::Core::Listener
9
+ # def execute(server, message)
10
+ # server.msg(message.destination, "hi #{message.from}") if message.body == "hi #{server.nick}"
11
+ # end
12
+ # end
3
13
  class Listener
14
+ # Takes an instance of Rubot::Core::Dispatcher. Any
15
+ # child class that needs a constructor should override
16
+ # this method.
17
+ #
18
+ # ==== Parameters
19
+ # dispatcher<Rubot::Core::Dispatcher>:: The dispatcher that was used to create
20
+ # the instance of the listener.
4
21
  def initialize(dispatcher)
5
22
  @dispatcher = dispatcher
6
23
  end
7
24
 
25
+ # Runs the listener with the given server and message.
26
+ #
27
+ # ==== Paramters
28
+ # server<Rubot::Irc::Server>:: Server instance the listener should use for
29
+ # messaging and information.
30
+ # message<Rubot::Irc::Message>:: The message that invoked the command.
8
31
  def execute(server, message)
9
32
  puts "#{self} listener does not implement execute method"
10
33
  end
@@ -1,11 +1,35 @@
1
1
  module Rubot
2
2
  module Core
3
+ # Base class for all runners. Runners are intended to be executed upon
4
+ # successful connection to a server, and cannot be invoked from a user.
5
+ #
6
+ # ==== Example
7
+ # This runner greets every channel the bot is in upon server connection.
8
+ # class Greet < Rubot::Core::Runner
9
+ # def run(server)
10
+ # server.channels.each do |channel|
11
+ # server.msg(channel, "hi everybody!")
12
+ # end
13
+ # end
14
+ # end
3
15
  class Runner
4
16
 
17
+ # Takes an instance of Rubot::Core::Dispatcher. Any
18
+ # child class that needs a constructor should override
19
+ # this method.
20
+ #
21
+ # ==== Parameters
22
+ # dispatcher<Rubot::Core::Dispatcher>:: The dispatcher that was used to create
23
+ # the instance of the runner.
5
24
  def initialize(dispatcher)
6
25
  @dispatcher = dispatcher
7
26
  end
8
27
 
28
+ # Runs the runner with the given server.
29
+ #
30
+ # ==== Paramters
31
+ # server<Rubot::Irc::Server>:: Server instance the runner should use for
32
+ # messaging and information.
9
33
  def run(server)
10
34
  end
11
35
  end
@@ -1,4 +1,5 @@
1
1
  class Object
2
+ # Returns a reference to the obkect's eigen (singleton) class.
2
3
  def eigen_class
3
4
  class << self
4
5
  self
@@ -1,8 +1,10 @@
1
1
  class String
2
+ # Converts a string camel case. Taken from Rails source.
2
3
  def camelize
3
4
  self.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
4
5
  end
5
6
 
7
+ # Converts a string to underscore form. Taken from Rails source.
6
8
  def underscore
7
9
  self.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
8
10
  end
@@ -1,9 +1,29 @@
1
1
  module Rubot
2
2
  module Irc
3
+ # Represents an IRC message.
3
4
  class Message
4
- attr_accessor :destination, :body, :alias, :authenticated
5
+ # The location where the reply to this message should be sent. This defaults
6
+ # to the source of the message
7
+ attr_accessor :destination
8
+
9
+ # The body of the message.
10
+ attr_accessor :body
11
+
12
+ # If the message was used to invoke a command, this will be the command name.
13
+ attr_accessor :alias
14
+
15
+ # If the user that sent this message is authenticated, this will be true.
16
+ attr_accessor :authenticated
17
+
18
+ # The user that sent this message.
5
19
  attr_reader :from
6
20
 
21
+ # Initializes a new object with the given from, destination, and body.
22
+ #
23
+ # ==== Parameters
24
+ # from<String>:: The nick who sent the message
25
+ # destination<String>:: The destination where the reply to this message should be sent.
26
+ # body<String>:: The body of the message
7
27
  def initialize(from, destination, body)
8
28
  @from = from
9
29
  @destination = destination
@@ -1,14 +1,96 @@
1
1
  module Rubot
2
2
  module Irc
3
+ # Used to queue outgoing messages with a delay, so we don't get excess flood
4
+ # kicked for spamming. The actual actions taken can be whatever they need to
5
+ # be. Currently they're just sending a message and sending an action to the
6
+ # server.
7
+ #
8
+ # I'll rehash the Rubot::Irc::Server code with a few modifications to
9
+ # demonstrate how it works.
10
+ #
11
+ # ==== Example
12
+ # This code initializes a message queue with a two second delay, then defines
13
+ # two methods with different actions.
14
+ #
15
+ # @message_queue = MessageQueue.new(2)
16
+ #
17
+ # @message_queue.message do |destination, message|
18
+ # puts "MESSAGE to #{destination} with body #{message}"
19
+ # end
20
+ #
21
+ # @message_queue.action do |destination, action|
22
+ # puts "ACTION to #{destination} with body #{action}"
23
+ # end
24
+ #
25
+ # After executing this code, the :message and :action methods are now available
26
+ # for this queue. Each call to these methods adds another action to the queue
27
+ # with the appropriate action.
28
+ #
29
+ # To use our newly created queue:
30
+ #
31
+ # @message_queue.message "destination_one", "i'm a message"
32
+ # @message_queue.action "destination_two", "i'm an action"
33
+ # @message_queue.message "destination_one", "i'm anoter message"
34
+ #
35
+ # This will yield the following output:
36
+ #
37
+ # MESSAGE to destination_one with body i'm a message
38
+ # # two second delay
39
+ # ACTION to destination_two with body i'm an action
40
+ # # two second delay
41
+ # MESSAGE to destination_one with body i'm another message
42
+ #
43
+ # These created methods are tailored for messages, and can accept string, arrays,
44
+ # or exploded arrays as parameters, as long as the first parameter is the destination.
45
+ # In the case of (exploded) arrays, each element in the array will be treated
46
+ # as a separate call to the block used to create the method.
47
+ #
48
+ # ==== Fancy Pants Paraters
49
+ # Assuming the code above has been executed, we can run the following:
50
+ #
51
+ # @message_queue.message "destination_three", "message one", "message two", "message three"
52
+ # actions = ["action one", "action_two", "action_three"]
53
+ # @message_queue.action "destination_two", actions
54
+ #
55
+ # which will produce:
56
+ #
57
+ # MESSAGE to destination_three with body message one
58
+ # # two second delay
59
+ # MESSAGE to destination_three with body message two
60
+ # # two second delay
61
+ # MESSAGE to destination_three with body message three
62
+ # # two second delay
63
+ # ACTION to destination_two with body action one
64
+ # # two second delay
65
+ # ACTION to destination_two with body action two
66
+ # # two second delay
67
+ # ACTION to destination_two with body action three
3
68
  class MessageQueue
69
+ # The delay in seconds to be used.
4
70
  attr_accessor :delay
5
71
 
72
+ # Initiliazes a new queue with the given delay.
73
+ #
74
+ # ==== Parameters
75
+ # delay<Fixnum>:: Delay in seconds
6
76
  def initialize(delay)
7
77
  @delay = delay
8
78
  @lock = Mutex.new
9
79
  @queue = []
10
80
  end
11
81
 
82
+ # This is where the magic happens. If we're sent a method we don't respond to,
83
+ # and it contains a block, we create a new method using that block. If no block
84
+ # is given, we forward to the base method_missing.
85
+ #
86
+ # This method makes an assumption about the block that's given. It is assumed
87
+ # that the block takes two arguments, a destination and a message (This is a
88
+ # message queue after all).
89
+ #
90
+ # ==== Parameters
91
+ # method<Symbol>:: Method name
92
+ # args<Array>:: Parameters given to the method
93
+ # block<Proc>:: The block that was passed
12
94
  def method_missing(method, *args, &block)
13
95
  if block
14
96
  eigen_class.instance_eval do
@@ -28,6 +110,7 @@ module Rubot
28
110
  end
29
111
 
30
112
  private
113
+ # Flushes the queue, executing each action it contains
31
114
  def flush
32
115
  until @queue.empty?
33
116
  element = @queue.shift
@@ -2,10 +2,26 @@ require "socket"
2
2
 
3
3
  module Rubot
4
4
  module Irc
5
+ # Performs the dirty work of communicating with an IRC server, passing messages
6
+ # back and forth between the server and a dispatcher.
5
7
  class Server
6
8
  include Rubot::Irc::Constants
7
- attr_reader :nick, :connected_at, :channels
8
-
9
+ # Our current nick
10
+ attr_reader :nick
11
+
12
+ # When a successful connection to the server was made
13
+ attr_reader :connected_at
14
+
15
+ # The list of channels we're currently in
16
+ attr_reader :channels
17
+
18
+ # Initializes a Server using the given dispatcher.
19
+ #
20
+ # The connection info to be used is extracted from the dispatcher's config method. (This
21
+ # needs to be changed)
22
+ #
23
+ # ==== Parameters
24
+ # dispatcher<Rubot::Core::Dispatcher>:: The dispatcher to handle the messages we receive
9
25
  def initialize(dispatcher)
10
26
  dispatcher.config["server"].each_pair do |key, value|
11
27
  instance_variable_set("@#{key}".to_sym, value)
@@ -24,6 +40,7 @@ module Rubot
24
40
  end
25
41
  end
26
42
 
43
+ # Attempts to make a connection to the server
27
44
  def connect
28
45
  return if @is_connected
29
46
 
@@ -42,55 +59,88 @@ module Rubot
42
59
  end
43
60
  end
44
61
 
62
+ # Sends the quit command to the IRC server, then closes the connection.
45
63
  def quit
46
64
  raw "QUIT :#{@quit_message}"
47
65
  @conn.close
48
66
  end
49
67
 
68
+ # Changes our nick.
50
69
  def change_nick(new_nick)
51
70
  raw "NICK #{new_nick}"
52
71
  @nick = new_nick
53
72
  end
54
73
 
74
+ # Joins the specified channels, which can be comma separated string or an array.
75
+ #
76
+ # ==== Parameters
77
+ # channels<String,Array>:: The channels to join
55
78
  def join(channels)
56
79
  channels = channels.split(',') if channels.is_a? String
57
80
  @channels.concat(channels).uniq!
58
81
  bulk_command("JOIN %s", channels)
59
82
  end
60
83
 
84
+ # Parts the specified channels, which can be comma separated string or an array.
85
+ #
86
+ # ==== Parameters
87
+ # channels<String,Array>:: The channels to part
61
88
  def part(channels)
62
89
  channels = channels.split(',') if channels.is_a? String
63
90
  @channels.reject! { |channel| channels.include?(channel) }
64
91
  bulk_command("PART %s :#{@quit_message}", channels)
65
92
  end
66
93
 
94
+ # Adds message(s) to the outgoing queue
95
+ #
96
+ # ==== Parameters
97
+ # destination<String>:: Where to send the message
98
+ # message<String,Array>:: The message(s) to be sent
67
99
  def msg(destination, message)
68
100
  @message_queue.message(destination, message)
101
+ # TODO use build_message_array with the queue
69
102
  #message = message.to_s.split("\n") unless message.is_a? Array
70
103
  #build_message_array(message).each do |l|
71
104
  # raw "PRIVMSG #{destination} :#{l}"
72
105
  #end
73
106
  end
74
-
75
- def action(destination, message)
76
- @message_queue.action(destination, message)
77
- #msg(destination, "\001ACTION #{message}\001")
78
- end
79
-
107
+
108
+ # Adds action(s) to the outgoing queue
109
+ #
110
+ # ==== Parameters
111
+ # destination<String>:: Where to send the message
112
+ # action<String,Array>:: The actions(s) to be sent
113
+ def action(destination, action)
114
+ @message_queue.action(destination, action)
115
+ end
116
+
117
+ # Sends a raw line to the server and dumps it the console (to be replaced with a logger)
118
+ #
119
+ # ==== Parameters
120
+ # message<String>:: The raw line to send
80
121
  def raw(message)
81
122
  puts "--> #{message}"
82
123
  @conn.puts "#{message}\n"
83
124
  end
84
125
 
126
+ # Get the list of nicks in the given channel. I'm not sure if the current implementation
127
+ # is really how it should be. TODO investigate
128
+ #
129
+ # ==== Parameters
130
+ # channel<String>:: The channel whose nicks we want
85
131
  def names(channel)
86
132
  raw "NAMES #{channel}"
87
133
  @conn.gets.split(":")[2].split(" ")
88
134
  end
89
135
 
90
136
  private
91
-
137
+
138
+ # The loop that goes on forever, reading input from the server.
92
139
  def main_loop
93
- while true
140
+ loop do
141
+ # something about this doesn't seem right. why are we using select and creating
142
+ # the ready object if we're never using it. TODO investigate wtf is going on here
143
+ # and find a better solution
94
144
  ready = select([@conn])
95
145
  next unless ready
96
146
 
@@ -100,8 +150,9 @@ module Rubot
100
150
  end
101
151
  end
102
152
 
153
+ # Determines what to do with the input from the server
103
154
  def handle_server_input(s)
104
- puts s
155
+ puts s # TODO logger
105
156
 
106
157
  case s.strip
107
158
  when /^PING :(.+)$/i
@@ -126,6 +177,7 @@ module Rubot
126
177
  end
127
178
  end
128
179
 
180
+ # Handles predefined events from the server.
129
181
  def handle_meta(server, code, message)
130
182
  case code
131
183
  when ERR_NICK_IN_USE
@@ -143,6 +195,8 @@ module Rubot
143
195
  end
144
196
  end
145
197
 
198
+ # Preps the string for sending to the server by breaking it down into
199
+ # an array if it's too long
146
200
  def string_to_irc_lines(str)
147
201
  str.split(" ").inject([""]) do |arr, word|
148
202
  arr.push("") if arr.last.size > MAX_MESSAGE_LENGTH
@@ -151,6 +205,8 @@ module Rubot
151
205
  end.map(&:strip)
152
206
  end
153
207
 
208
+ # Ensures each string in the array is shorter than the max allowed message
209
+ # length, breaking the string if necessary
154
210
  def build_message_array(arr)
155
211
  arr.each_with_index.map do |message, index|
156
212
  message.size > MAX_MESSAGE_LENGTH ? string_to_irc_lines(message) : arr[index]
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 0
9
- version: 0.1.0
8
+ - 1
9
+ version: 0.1.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Chris Thorn
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-03-23 00:00:00 -08:00
17
+ date: 2010-04-07 00:00:00 -08:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -28,42 +28,34 @@ extra_rdoc_files:
28
28
  - README.rdoc
29
29
  files:
30
30
  - bin/rubot
31
- - bin/rubot~
31
+ - lib/core/command.rb
32
+ - lib/core/dispatcher.rb
33
+ - lib/core/listener.rb
34
+ - lib/core/runner.rb
32
35
  - lib/core.rb
33
- - lib/irc/message.rb
34
- - lib/irc/server.rb
35
- - lib/irc/constants.rb
36
- - lib/irc/message_queue.rb
37
- - lib/irc/server.rb~
38
- - lib/generators/command.rb~
39
- - lib/generators/listener.template~
40
- - lib/generators/listeners.template
41
- - lib/generators/commands.template
42
- - lib/generators/command.template~
43
- - lib/generators/runner.template~
44
- - lib/generators/runners.template
45
36
  - lib/extensions/object.rb
46
37
  - lib/extensions/string.rb
47
- - lib/template/commands/reload.rb
48
- - lib/template/commands/quit.rb
49
- - lib/template/commands/part.rb
50
- - lib/template/commands/msg.rb
51
- - lib/template/commands/up_time.rb
52
- - lib/template/commands/join.rb
38
+ - lib/extensions.rb
39
+ - lib/generators/commands.template
40
+ - lib/generators/listeners.template
41
+ - lib/generators/runners.template
42
+ - lib/irc/constants.rb
43
+ - lib/irc/message.rb
44
+ - lib/irc/message_queue.rb
45
+ - lib/irc/server.rb
46
+ - lib/irc.rb
47
+ - lib/rubot.rb
53
48
  - lib/template/commands/auth.rb
54
49
  - lib/template/commands/help.rb
50
+ - lib/template/commands/join.rb
51
+ - lib/template/commands/msg.rb
52
+ - lib/template/commands/part.rb
53
+ - lib/template/commands/quit.rb
54
+ - lib/template/commands/reload.rb
55
+ - lib/template/commands/up_time.rb
55
56
  - lib/template/main.rb
56
57
  - lib/template/resources/config.yml
57
- - lib/core/dispatcher.rb
58
- - lib/core/listener.rb
59
- - lib/core/command.rb
60
- - lib/core/runner.rb
61
- - lib/core/runner.rb~
62
- - lib/irc.rb
63
58
  - lib/template.rb
64
- - lib/extensions.rb
65
- - lib/rubot.rb
66
- - lib/template.rb~
67
59
  - README.rdoc
68
60
  has_rdoc: true
69
61
  homepage: http://github.com/thorncp/rubot
data/bin/rubot~ DELETED
@@ -1,48 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "optparse"
4
- require "ostruct"
5
- require "template"
6
-
7
- options = OpenStruct.new
8
- parser = OptionParser.new do |parser|
9
- parser.banner = "Usage: #{__FILE__} <options>"
10
-
11
- parser.separator ""
12
- parser.separator "Initialization"
13
-
14
- parser.on("-i", "--init ProjectName", "Initializes a new rubot project in the ProjectName directory") do |name|
15
- init name
16
- exit
17
- end
18
-
19
- parser.separator ""
20
- parser.separator "Generators"
21
-
22
- parser.on("-c", "--command Name", "Generates a new command named Name") do |name|
23
- generate_command(name)
24
- exit
25
- end
26
-
27
- parser.on("-l", "--listener Name", "Generates a new listener named Name") do |listener|
28
- generate_listener(name)
29
- exit
30
- end
31
-
32
- parser.on("-r", "--runner Name", "Generates a new listener named Name") do |runner|
33
- generate_runner(name)
34
- exit
35
- end
36
-
37
- parser.separator ""
38
- parser.separator "Common options:"
39
-
40
- parser.on_tail("-h", "--help", "Show this message") do
41
- puts parser
42
- exit
43
- end
44
- end
45
-
46
- parser.parse!(ARGV)
47
- puts parser
48
-
@@ -1,13 +0,0 @@
1
- module Rubot
2
- module Core
3
- class BaseRunner
4
-
5
- def initialize(dispatcher)
6
- @dispatcher = dispatcher
7
- end
8
-
9
- def run(server)
10
- end
11
- end
12
- end
13
- end
@@ -1,5 +0,0 @@
1
- class < Rubot::Core::Command
2
- def execute(server, message, options)
3
- server.msg(message.destination, "unimplemented")
4
- end
5
- end
@@ -1,5 +0,0 @@
1
- class $NAME$ < Rubot::Core::Command
2
- def execute(server, message, options)
3
- server.msg(message.destination, "unimplemented")
4
- end
5
- end
@@ -1,7 +0,0 @@
1
- class --NAME-- < Rubot::Core::Listener
2
- def execute(server, message)
3
- if message.from == server.nick
4
- server.msg(message.destination, "hi #{message.from}")
5
- end
6
- end
7
- end
@@ -1,5 +0,0 @@
1
- class --NAME-- < Rubot::Core::Runner
2
- def run(server)
3
- server.msg(message.destination, "unimplemented")
4
- end
5
- end
@@ -1,162 +0,0 @@
1
- require "socket"
2
-
3
- module Rubot
4
- module Irc
5
- class Server
6
- include Rubot::Irc::Constants
7
- attr_reader :nick, :connected_at, :channels
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
-
16
- @message_queue = MessageQueue.new(@message_delay)
17
-
18
- @message_queue.message do |destination, message|
19
- raw "PRIVMSG #{destination} :#{message}"
20
- end
21
-
22
- @message_queue.action do |destination, action|
23
- raw "PRIVMSG #{destination} :\001ACTION #{action}\001"
24
- end
25
- end
26
-
27
- def connect
28
- return if @is_connected
29
-
30
- @conn = TCPSocket.open(@host, @port, @vhost)
31
- raw "USER #{@nick} #{@nick} #{@nick} :#{@real_name}"
32
- change_nick @nick
33
- join @channels
34
-
35
- begin
36
- main_loop()
37
- rescue Interrupt
38
- rescue Exception => detail
39
- puts detail.message()
40
- print detail.backtrace.join("\n")
41
- retry
42
- end
43
- end
44
-
45
- def quit
46
- raw "QUIT :#{@quit_message}"
47
- @conn.close
48
- end
49
-
50
- def change_nick(new_nick)
51
- raw "NICK #{new_nick}"
52
- @nick = new_nick
53
- end
54
-
55
- def join(channels)
56
- channels = channels.split(',') if channels.is_a? String
57
- @channels.concat(channels).uniq!
58
- bulk_command("JOIN %s", channels)
59
- end
60
-
61
- def part(channels)
62
- channels = channels.split(',') if channels.is_a? String
63
- @channels.reject! { |channel| channels.include?(channel) }
64
- bulk_command("PART %s :#{@quit_message}", channels)
65
- end
66
-
67
- def msg(destination, message)
68
- @message_queue.message(destination, message)
69
- #message = message.to_s.split("\n") unless message.is_a? Array
70
- #build_message_array(message).each do |l|
71
- # raw "PRIVMSG #{destination} :#{l}"
72
- #end
73
- end
74
-
75
- def action(destination, message)
76
- @message_queue.action(destination, message)
77
- #msg(destination, "\001ACTION #{message}\001")
78
- end
79
-
80
- def raw(message)
81
- puts "--> #{message}"
82
- @conn.puts "#{message}\n"
83
- end
84
-
85
- def names(channel)
86
- raw "NAMES #{channel}"
87
- @conn.gets.split(":")[2].split(" ")
88
- end
89
-
90
- private
91
-
92
- def main_loop
93
- while true
94
- ready = select([@conn])
95
- next unless ready
96
-
97
- return if @conn.eof
98
- s = @conn.gets
99
- handle_server_input(s)
100
- end
101
- end
102
-
103
- def handle_server_input(s)
104
- puts s
105
-
106
- case s.strip
107
- when /^PING :(.+)$/i
108
- raw "PONG :#{$1}"
109
- when /^:([-.0-9a-z]+)\s([0-9]+)\s(.+)\s(.*)$/i
110
- handle_meta($1, $2.to_i, $4)
111
- when /^:(.+?)!(.+?)@(.+?)\sPRIVMSG\s(.+)\s:(.+)$/i
112
- message = Rubot::Irc::Message.new($1, $4 == @nick ? $1 : $4, $5)
113
- # TODO add ability to pass events other than privmsg to dispatcher. ie, nick changes, parts, joins, quits, bans, etc, etc
114
- @dispatcher.handle_message(self, message)
115
- end
116
- end
117
-
118
- # performs the same command on each element in the given collection, separated by comma
119
- def bulk_command(formatted_string, elements)
120
- if elements.is_a? String
121
- elements = elements.split(',')
122
- end
123
-
124
- elements.each do |e|
125
- raw sprintf(formatted_string, e.to_s.strip)
126
- end
127
- end
128
-
129
- def handle_meta(server, code, message)
130
- case code
131
- when ERR_NICK_IN_USE
132
- if @nick == @alt_nick
133
- puts "all nicks used, don't know how to name myself."
134
- quit
135
- exit!
136
- end
137
- change_nick @alt_nick
138
- join @channels
139
- when WELLCOME
140
- @dispatcher.connected(self)
141
- @is_connected = true
142
- @connected_at = Time.now
143
- end
144
- end
145
-
146
- def string_to_irc_lines(str)
147
- str.split(" ").inject([""]) do |arr, word|
148
- arr.push("") if arr.last.size > MAX_MESSAGE_LENGTH
149
- arr.last << "#{word} "
150
- arr
151
- end.map(&:strip)
152
- end
153
-
154
- def build_message_array(arr)
155
- arr.each_with_index.map do |message, index|
156
- message.size > MAX_MESSAGE_LENGTH ? string_to_irc_lines(message) : arr[index]
157
- end.flatten
158
- end
159
- end
160
- end
161
- end
162
-
@@ -1,52 +0,0 @@
1
- require "fileutils"
2
- require "extensions/string"
3
-
4
- def init(name)
5
- if Dir.exist? name
6
- puts "directory '#{name}' already exists"
7
- return
8
- end
9
-
10
- template_dir = File.join(File.dirname(__FILE__), "template")
11
-
12
- FileUtils.cp_r(template_dir, name)
13
-
14
- dir_structure = %w{resources commands listeners runners}
15
-
16
- # for some reason gem doesn't include empty folders during the install.
17
- # this is to make sure these directories exist
18
- dir_structure.each do |dir|
19
- FileUtils.mkdir File.join(name, dir) unless File.exist? File.join(name, dir)
20
- end
21
- end
22
-
23
- def generate_command(name)
24
- #command = File.join("commands", "#{name.underscore}.rb")
25
- #template_file = File.join(File.dirname(__FILE__), "generators", "command.template")
26
- #source = IO.read(template_file).gsub(/--NAME--/, name.camelize)
27
- #File.open(command, "w") {|file| file.write(source)}
28
- generate("commands", name)
29
- end
30
-
31
- def generate_listener(name)
32
- #template_file = File.join(File.dirname(__FILE__), "generators", "listener.template")
33
- #command = IO.read(template_file).gsub(/--NAME--/, name.camelize)
34
- #File.open("listeners/#{name.underscore}.rb", "w") {|file| file.write(command)}
35
- generate("listeners", name)
36
- end
37
-
38
- def generate_runner(name)
39
- #template_file = File.join(File.dirname(__FILE__), "generators", "runner.template")
40
- #command = IO.read(template_file).gsub(/--NAME--/, name.camelize)
41
- #File.open("runners/#{name.underscore}.rb", "w") {|file| file.write(command)}
42
- generate("runners", name)
43
- end
44
-
45
- def generate(template, name)
46
- filename = File.join(template, "#{name.underscore}.rb")
47
- puts "file '#{filename}' already exists" and return if File.exist?(filename)
48
-
49
- template_file = File.join(File.dirname(__FILE__), "generators", "#{template}.template")
50
- source = IO.read(template_file).gsub(/--NAME--/, name.camelize)
51
- File.open(filename, "w") {|file| file.write(source)}
52
- end