spox-mod_spox 0.3.1 → 0.3.2

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.
Files changed (73) hide show
  1. data/CHANGELOG +5 -0
  2. data/README.rdoc +61 -1
  3. data/bin/mod_spox +1 -7
  4. data/data/mod_spox/extras/AutoKick.rb +3 -2
  5. data/data/mod_spox/extras/AutoMode.rb +2 -1
  6. data/data/mod_spox/extras/AutoRejoin.rb +5 -4
  7. data/data/mod_spox/extras/Bouncer.rb +243 -131
  8. data/data/mod_spox/extras/FloodKicker.rb +2 -1
  9. data/data/mod_spox/extras/Fortune.rb +5 -1
  10. data/data/mod_spox/extras/Karma.rb +2 -1
  11. data/data/mod_spox/extras/Logger.rb +11 -9
  12. data/data/mod_spox/extras/NickServ.rb +2 -1
  13. data/data/mod_spox/extras/PhpCli.rb +1 -1
  14. data/data/mod_spox/extras/PhpFuncLookup.rb +2 -1
  15. data/data/mod_spox/extras/RegexTracker.rb +2 -1
  16. data/data/mod_spox/extras/Roulette.rb +2 -1
  17. data/data/mod_spox/extras/Seen.rb +10 -8
  18. data/data/mod_spox/extras/Topten.rb +2 -1
  19. data/data/mod_spox/extras/Translate.rb +2 -1
  20. data/data/mod_spox/extras/Twitter.rb +1 -1
  21. data/data/mod_spox/plugins/Authenticator.rb +7 -7
  22. data/data/mod_spox/plugins/Banner.rb +6 -6
  23. data/data/mod_spox/plugins/BotNick.rb +2 -1
  24. data/data/mod_spox/plugins/Initializer.rb +4 -4
  25. data/data/mod_spox/plugins/Joiner.rb +1 -1
  26. data/data/mod_spox/plugins/PluginLoader.rb +1 -1
  27. data/data/mod_spox/plugins/Ponger.rb +8 -8
  28. data/data/mod_spox/plugins/Status.rb +1 -1
  29. data/lib/mod_spox/Bot.rb +27 -27
  30. data/lib/mod_spox/BotConfig.rb +17 -21
  31. data/lib/mod_spox/Filter.rb +29 -0
  32. data/lib/mod_spox/FilterManager.rb +63 -0
  33. data/lib/mod_spox/Helpers.rb +120 -14
  34. data/lib/mod_spox/Loader.rb +1 -1
  35. data/lib/mod_spox/MessageFactory.rb +10 -2
  36. data/lib/mod_spox/Pipeline.rb +66 -61
  37. data/lib/mod_spox/PluginManager.rb +5 -5
  38. data/lib/mod_spox/PriorityQueue.rb +21 -8
  39. data/lib/mod_spox/Sockets.rb +6 -6
  40. data/lib/mod_spox/Timer.rb +3 -3
  41. data/lib/mod_spox/Version.rb +2 -2
  42. data/lib/mod_spox/handlers/UserHost.rb +32 -0
  43. data/lib/mod_spox/handlers/Whois.rb +7 -7
  44. data/lib/mod_spox/messages/incoming/Pong.rb +11 -2
  45. data/lib/mod_spox/messages/incoming/UserHost.rb +24 -0
  46. data/lib/mod_spox/messages/internal/FilterAdd.rb +20 -0
  47. data/lib/mod_spox/messages/internal/FilterList.rb +18 -0
  48. data/lib/mod_spox/messages/internal/FilterListing.rb +22 -0
  49. data/lib/mod_spox/messages/internal/FilterRemove.rb +20 -0
  50. data/lib/mod_spox/messages/internal/Incoming.rb +15 -0
  51. data/lib/mod_spox/migrations/006_ignore.rb +21 -0
  52. data/lib/mod_spox/models/Nick.rb +11 -0
  53. data/lib/mod_spox/rfc2812.rb +2 -1
  54. data/lib/mod_spox/rfc2812_full.rb +185 -0
  55. data/tests/BotHolder.rb +7 -1
  56. data/tests/handlers/tc_Created.rb +28 -8
  57. data/tests/handlers/tc_Invite.rb +11 -9
  58. data/tests/handlers/tc_Join.rb +36 -16
  59. data/tests/handlers/tc_Kick.rb +27 -6
  60. data/tests/handlers/tc_Mode.rb +23 -13
  61. data/tests/handlers/tc_Names.rb +29 -9
  62. data/tests/handlers/tc_Nick.rb +30 -8
  63. data/tests/handlers/tc_Part.rb +30 -20
  64. data/tests/handlers/tc_Ping.rb +32 -19
  65. data/tests/handlers/tc_Pong.rb +28 -8
  66. data/tests/handlers/tc_Privmsg.rb +33 -13
  67. data/tests/handlers/tc_Quit.rb +30 -17
  68. data/tests/handlers/tc_Who.rb +8 -3
  69. data/tests/handlers/tc_Whois.rb +7 -3
  70. data/tests/lib/tc_BotConfig.rb +35 -0
  71. data/tests/lib/tc_Helpers.rb +139 -0
  72. data/tests/lib/tc_PriorityQueue.rb +31 -0
  73. metadata +17 -2
@@ -0,0 +1,63 @@
1
+ module ModSpox
2
+ class FilterManager
3
+
4
+ def initialize(pipeline)
5
+ @pipeline = pipeline
6
+ @filters = {}
7
+ [:FilterAdd, :FilterRemove, :FilterList, :FilterListing].each{|f| Helpers.load_message(:internal, f)}
8
+ @pipeline.hook(self, :add_filter, ModSpox::Messages::Internal::FilterAdd)
9
+ @pipeline.hook(self, :remove_filter, ModSpox::Messages::Internal::FilterRemove)
10
+ @pipeline.hook(self, :list_filter, ModSpox::Messages::Internal::FilterList)
11
+ end
12
+
13
+ # filter:: ModSpox::Filter object
14
+ # type:: type of message to filter
15
+ def add(filter, type)
16
+ type = Helpers.find_const(type)
17
+ @filters[type] ||= []
18
+ @filters[type] << filter
19
+ end
20
+
21
+ # filter:: ModSpox::Filter object
22
+ # type:: type of message filter is associated to
23
+ # Remove a filter. If a type is defined, the filter
24
+ # will only be removed from that type. If no type is
25
+ # defined, the filter will be removed completely
26
+ def remove(filter, type=nil)
27
+ if(type.nil?)
28
+ @filters.each do |ar|
29
+ ar.delete_if do |key, value|
30
+ value == filter
31
+ end
32
+ end
33
+ else
34
+ type = Helpers.find_const(type)
35
+ if(@filters[type])
36
+ key = @filters[type].index(filter)
37
+ @filters[type].delete(key) if key
38
+ end
39
+ end
40
+ end
41
+
42
+ # type:: type of message
43
+ # Return array of filters. Will only return
44
+ def filters(type=nil)
45
+ if(type.nil?)
46
+ return @filters.dup
47
+ else
48
+ type = Helpers.find_const(type)
49
+ return @filters[type] ? @filters[type].dup : nil
50
+ end
51
+ end
52
+
53
+ # m:: message from pipeline
54
+ # Applies filters to messages from pipeline
55
+ def apply_filters(m)
56
+ @filters.keys.each do |type|
57
+ if(Helpers.type_of?(m, type))
58
+ @filters[type].each{|f| f.filter(m)}
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -11,8 +11,8 @@ module ModSpox
11
11
  # secs:: number of seconds
12
12
  # Converts seconds into a human readable string
13
13
  def Helpers.format_seconds(secs)
14
- arg = {:year => 29030400,
15
- :month => 2419200,
14
+ arg = {:year => 31536000,
15
+ :month => 2678400,
16
16
  :week => 604800,
17
17
  :day => 86400,
18
18
  :hour => 3600,
@@ -44,29 +44,37 @@ module ModSpox
44
44
  "Yotta" # 1024^8
45
45
  ]
46
46
  def Helpers.format_size(bytes)
47
+ return "0 bytes" if bytes == 0
47
48
  mag = (Math.log(bytes) / Math.log(1024)).floor
48
49
  mag = [ Suff.length - 1, mag ].min
49
50
  val = bytes.to_f / (1024 ** mag)
50
- "%7.3f %sbyte%s" % [ val, Suff[mag], val == 1 ? "" : "s" ]
51
+ ("%.3f %sbyte%s" % [ val, Suff[mag], val == 1 ? "" : "s" ]).strip
51
52
  end
52
53
 
53
54
  # command:: command to execute
54
55
  # timeout:: maximum number of seconds to run
56
+ # maxbytes:: maximum number of result bytes to accept
55
57
  # Execute a system command (use with care)
56
- def Helpers.safe_exec(command, timeout=10)
58
+ def Helpers.safe_exec(command, timeout=10, maxbytes=500)
59
+ output = []
60
+ pro = nil
57
61
  begin
58
62
  Timeout::timeout(timeout) do
59
- result = `#{command}`
63
+ pro = IO.popen(command)
64
+ until(pro.closed? || pro.eof?)
65
+ output << pro.getc
66
+ raise IOError.new("Maximum allowed output bytes exceeded. (#{maxbytes} bytes)") unless output.count <= maxbytes
67
+ end
60
68
  end
61
- rescue Timeout::Error => boom
62
- Logger.warn("Command execution exceeded allowed time (command: #{command} | timeout: #{timeout})")
63
- raise boom
69
+ output = output.join('')
64
70
  rescue Object => boom
65
- Logger.warn("Command generated an exception (command: #{command} | error: #{boom})")
66
71
  raise boom
72
+ ensure
73
+ Process.kill('KILL', pro.pid) if Process.waitpid2(pro.pid, Process::WNOHANG).nil? # make sure the process is dead
67
74
  end
75
+ return output
68
76
  end
69
-
77
+
70
78
  # url:: URL to shorten
71
79
  # Gets a tinyurl for given URL
72
80
  def Helpers.tinyurl(url)
@@ -87,13 +95,13 @@ module ModSpox
87
95
  # or channel name. If the string given does not match the required
88
96
  # pattern for a channel or nick, the string is returned.
89
97
  def Helpers.find_model(string, create=true)
90
- result = nil
98
+ result = string
91
99
  if(string =~ /^[A-Za-z\|\\\{\}\[\]\^\`~\_\-]+[A-Za-z0-9\|\\\{\}\[\]\^\`~\_\-]*$/)
92
100
  result = Models::Nick.find_or_create(:nick => string.downcase)
93
101
  elsif(['&', '#', '+', '!'].include?(string[0]))
94
102
  result = Models::Channel.find_or_create(:name => string.downcase)
95
- elsif(Models::Server.filter(:host => string, :connected => true).count > 0)
96
- result = Models::Server.filter(:host => string, :connected => true).first
103
+ elsif(Models::Server.filter(:host => string).count > 0)
104
+ result = Models::Server.filter(:host => string).first
97
105
  else
98
106
  Logger.warn("Failed to match string to model: #{string} -> No match")
99
107
  end
@@ -115,8 +123,106 @@ module ModSpox
115
123
  # kind:: (:internal|:incoming|:outgoing)
116
124
  # type:: message type (Example: :Privmsg)
117
125
  # Easy loader for messages
118
- def load_message(kind, type)
126
+ def Helpers.load_message(kind, type)
127
+ raise ArgumentError.new('Valid kind types: :internal, :incoming, :outgoing') unless [:internal, :incoming, :outgoing].include?(kind)
119
128
  require "mod_spox/messages/#{kind}/#{type}"
120
129
  end
130
+
131
+ # a:: object
132
+ # b:: type constant or string
133
+ # symbolize:: Symbolize and check (deprecated)
134
+ # Determines if a is a type of b. For example:
135
+ #
136
+ # a = Foo::Bar.new
137
+ # Helpers.type_of?(a, Foo) -> true
138
+ def Helpers.type_of?(a, b, symbolize=false)
139
+ return true if (b.is_a?(Class) || b.is_a?(Module)) && a.is_a?(b) # if only it were always this easy
140
+ checks = []
141
+ # first, we strip the front down
142
+ t = a.class.to_s
143
+ unless(t.index('ModSpox::Messages::').nil?)
144
+ t.slice!(t.index('ModSpox::Messages::'), 19)
145
+ checks << t
146
+ end
147
+ t = a.class.to_s
148
+ if(t.slice(0) == '<')
149
+ t.slice!(0, t.rindex('>'))
150
+ checks << t
151
+ end
152
+ checks << a.class.to_s
153
+ checks.each do |s|
154
+ until(s.index('::').nil?) do
155
+ s.slice!(s.rindex('::'), s.length - s.rindex('::'))
156
+ return true if s =~ /#{b}.*/
157
+ end
158
+ end
159
+ # one last check if we are allowed to symbolize
160
+ if(symbolize && b.is_a?(Symbol))
161
+ sym = a.class.to_s
162
+ sym.gsub!('::', '_')
163
+ return true if sym == b || b =~ /#{sym}.*/
164
+ sym.slice!(0, 17) if sym.index('ModSpox_Messages') == 0
165
+ sym.slice!(0, sym.index('>')+1) if sym.index('<') == 0 # this is for dynamic objects from plugins
166
+ return true if sym == b || b =~ /#{sym}.*/
167
+ end
168
+ return false
169
+ end
170
+
171
+ # c:: constant name (String)
172
+ # Finds a constant if it exists
173
+ # Example:: Foo::Bar
174
+ def Helpers.find_const(c)
175
+ return c unless c.is_a?(String)
176
+ const = nil
177
+ [Kernel, ModSpox, Messages].each do |base|
178
+ begin
179
+ c.split('::').each do |part|
180
+ const = const.nil? ? base.const_get(part) : const.const_get(part)
181
+ end
182
+ rescue NameError
183
+ const = nil
184
+ end
185
+ end
186
+ return const.nil? ? c : const
187
+ end
188
+
189
+ # IdealHumanRandomIterator - select "random" members of a population, favoring
190
+ # those least-recently selected, to appease silly humans who hate repeats
191
+ #
192
+ # Abstract:
193
+ # given a decently-sized set of items (say, 100 famous quotes), an
194
+ # average persons's idea of N "random" entries is not actually random.
195
+ # people don't want items to appear twice in a row, or too frequently
196
+ # (even though true randomness means this is just as likely as any other order).
197
+ #
198
+ # instead, design a scheme whereby LRU items are weighted more heavily,
199
+ # to "encourage" subsequent selections to not repeat.
200
+ #
201
+ # Author: Ryan "pizza_" Flynn
202
+ # - pulled from the algodict project
203
+ # - - http://github.com/pizza/algodict
204
+ class IdealHumanRandomIterator
205
+
206
+ def initialize(list)
207
+ raise ArgumentError.new("Array type required") unless list.is_a?(Array)
208
+ @items = list
209
+ end
210
+
211
+ # Given length L, generate a random number in the range [0,len-1), heavily
212
+ # weighted towards the low end.
213
+ def self.nexti(len)
214
+ len += 1 if len % 2 == 1
215
+ index = len > 2 ? rand(len/2) : 0
216
+ return index
217
+ end
218
+
219
+ # return a psuedo-random member of items. subsequent calls should never
220
+ # return the same item.
221
+ def next()
222
+ index = IdealHumanRandomIterator.nexti(@items.length)
223
+ @items.push @items.delete_at(index)
224
+ return @items.last
225
+ end
226
+ end
121
227
  end
122
228
  end
@@ -38,7 +38,7 @@ module ModSpox
38
38
  Database.db = Sequel.connect("#{ModSpox.jdbc ? 'jdbc:' : ''}postgres://#{config[:db_username]}:#{config[:db_password]}@#{config[:db_host]}/#{config[:db_database]}")
39
39
  Database.type = :pgsql
40
40
  when 'sqlite'
41
- Database.db = Sequel.sqlite("#{BotConfig[:userpath]}/mod_spox.db", :pool_timeout => 20, :timeout => 5000)
41
+ Database.db = Sequel.connect("#{ModSpox.jdbc ? 'jdbc:' : ''}sqlite://#{BotConfig[:userpath]}/mod_spox.db", :pool_timeout => 20, :timeout => 5000)
42
42
  Database.type = :sqlite
43
43
  end
44
44
  end
@@ -21,6 +21,13 @@ module ModSpox
21
21
  @sync = [:RPL_MOTDSTART, :RPL_MOTD, :RPL_ENDOFMOTD, :RPL_WHOREPLY, :RPL_ENDOFWHO,
22
22
  :RPL_NAMREPLY, :RPL_ENDOFNAMES, :RPL_WHOISUSER, :RPL_WHOISSERVER, :RPL_WHOISOPERATOR,
23
23
  :RPL_WHOISIDLE, :RPL_WHOISCHANNELS, :RPL_WHOISIDENTIFIED, :RPL_ENDOFWHOIS].map{|s| RFC[s][:value]}
24
+ @pipeline.hook(self, :proc_internal, ModSpox::Messages::Internal::Incoming)
25
+ end
26
+
27
+ # m:: ModSpox::Messages::Internal::Incoming
28
+ # Process internal raw message
29
+ def proc_internal(m)
30
+ self << m.message
24
31
  end
25
32
 
26
33
  # string:: server message to be parsed
@@ -76,10 +83,10 @@ module ModSpox
76
83
  end
77
84
  end
78
85
  else
79
- Logger.error("No handler was found to process message of type: #{key} Message: #{message}")
86
+ Logger.warn("No handler was found to process message of type: #{key} Message: #{message}")
80
87
  raise Exceptions::HandlerNotFound.new(key)
81
88
  end
82
- rescue Exceptions::HandlerNotFound => boom
89
+ rescue ModSpox::Exceptions::HandlerNotFound => boom
83
90
  unless(loaded)
84
91
  if(@available[boom.message_type])
85
92
  @available[boom.message_type].each do|f|
@@ -96,6 +103,7 @@ module ModSpox
96
103
  retry
97
104
  end
98
105
  end
106
+ Logger.error("Failed to find a handler for: #{boom.message_type}")
99
107
  raise boom
100
108
  rescue Object => boom
101
109
  if(boom.class.to_s == 'SQLite3::BusyException' || boom.class.to_s == 'PGError')
@@ -2,7 +2,8 @@
2
2
  'mod_spox/Logger',
3
3
  'mod_spox/Exceptions',
4
4
  'mod_spox/messages/incoming/Privmsg',
5
- 'mod_spox/messages/incoming/Notice'].each{|f|require f}
5
+ 'mod_spox/messages/incoming/Notice',
6
+ 'mod_spox/FilterManager'].each{|f|require f}
6
7
  module ModSpox
7
8
 
8
9
  class Pipeline
@@ -16,8 +17,9 @@ module ModSpox
16
17
  @populate_lock = Mutex.new
17
18
  populate_triggers
18
19
  populate_signatures
19
- hook(self, :populate_triggers, :Internal_TriggersUpdate)
20
- hook(self, :populate_signatures, :Internal_SignaturesUpdate)
20
+ hook(self, :populate_triggers, ModSpox::Messages::Internal::TriggersUpdate)
21
+ hook(self, :populate_signatures, ModSpox::Messages::Internal::SignaturesUpdate)
22
+ @filters = FilterManager.new(self)
21
23
  end
22
24
 
23
25
  # message:: Message to send down pipeline
@@ -52,12 +54,12 @@ module ModSpox
52
54
  # Hooks a plugin into the pipeline for a specific type of message
53
55
  def hook(object, method, type)
54
56
  Logger.info("Object #{object.class.to_s} hooking into messages of type: #{type}")
55
- type = type.gsub(/::/, '_').to_sym unless type.is_a?(Symbol)
57
+ type = Helpers.find_const(type)
56
58
  method = method.to_sym unless method.is_a?(Symbol)
57
- name = object.class.to_s.gsub(/^.+:/, '')
58
- @hooks[type] = Hash.new unless @hooks.has_key?(type)
59
- @hooks[type][name.to_sym] = Array.new unless @hooks[type][name.to_sym].is_a?(Array)
60
- @hooks[type][name.to_sym] << {:object => object, :method => method}
59
+ name = object.class
60
+ @hooks[type] ||= Hash.new
61
+ @hooks[type][name] ||= Array.new
62
+ @hooks[type][name] << {:object => object, :method => method}
61
63
  end
62
64
 
63
65
  # plugin:: Plugin to unhook from pipeline
@@ -65,8 +67,8 @@ module ModSpox
65
67
  # This will remove the hook a plugin has for a specific message type
66
68
  def unhook(object, method, type)
67
69
  Logger.info("Object #{object.class.to_s} unhooking from messages of type: #{type}")
68
- type = type.gsub(/::/, '_').to_sym unless type.is_a?(Symbol)
69
- name = object.class.to_s.gsub(/^.+:/, '').to_sym
70
+ type = Helpers.find_const(type)
71
+ name = object.class
70
72
  raise Exceptions::InvalidValue.new("Unknown hook type given: #{type.to_s}") unless @hooks.has_key?(type)
71
73
  raise Exceptions::InvalidValue.new("Unknown object hooked: #{name.to_s}") unless @hooks[type].has_key?(name)
72
74
  @hooks[type][name].each{|hook|
@@ -100,7 +102,7 @@ module ModSpox
100
102
  a.destroy
101
103
  Models::Signature.all.each do |s|
102
104
  c = s.signature[0].chr.downcase
103
- if(c =~ /^[a-z]$/)
105
+ if(c =~ /^[A-Za-z]$/)
104
106
  type = c.to_sym
105
107
  elsif(c =~ /^[0-9]$/)
106
108
  type = :digit
@@ -119,39 +121,26 @@ module ModSpox
119
121
 
120
122
  # Processes messages
121
123
  def message_processor(message)
122
- begin
123
- Logger.info("Pipeline is processing a message: #{message}")
124
- parse(message)
125
- type = message.class.to_s.gsub(/^(ModSpox::Messages::|#<.+?>::)/, '').gsub('::', '_').to_sym
126
- mod = type.to_s.gsub(/_.+$/, '').to_sym
127
- Logger.info("Pipeline determines that #{message} is of type: #{type}")
128
- [type, mod, :all].each do |type|
129
- if(@hooks.has_key?(type))
130
- @hooks[type].each_value do |objects|
124
+ @filters.apply_filters(message)
125
+ return if message.nil?
126
+ @hooks.keys.each do |type|
127
+ next unless Helpers.type_of?(message, type, true)
128
+ @hooks[type].each_value do |objects|
129
+ objects.each do |v|
130
+ @pool.process do
131
131
  begin
132
- objects.each do |v|
133
- @pool.process do
134
- begin
135
- v[:object].send(v[:method].to_s, message)
136
- rescue Object => boom
137
- if(boom.class.to_s == 'SQLite3::BusyException')
138
- Database.reset_connections
139
- retry
140
- else
141
- raise boom
142
- end
143
- end
144
- end
145
- end
132
+ v[:object].send(v[:method], message)
146
133
  rescue Object => boom
147
- Logger.warn("Plugin threw exception while attempting to process message: #{boom}\n#{boom.backtrace.join("\n")}")
134
+ if(boom.class.to_s == 'SQLite3::BusyException')
135
+ Database.reset_connections
136
+ end
137
+ raise boom
148
138
  end
149
139
  end
150
140
  end
151
141
  end
152
- rescue Object => boom
153
- Logger.warn("Pipeline encountered an exception while processing a message: #{boom}\n#{boom.backtrace.join("\n")}")
154
142
  end
143
+ parse(message)
155
144
  end
156
145
 
157
146
  # message:: Message to parse
@@ -159,49 +148,65 @@ module ModSpox
159
148
  # trigger signatures. If matches are found, they will be sent
160
149
  # to the proper plugin for processing
161
150
  def parse(message)
162
- return unless message.kind_of?(Messages::Incoming::Privmsg) || message.kind_of?(Messages::Incoming::Notice)
151
+ return unless message.is_a?(Messages::Incoming::Privmsg) || message.is_a?(Messages::Incoming::Notice)
163
152
  trigger = nil
164
153
  @triggers.each{|t| trigger = t if message.message[0..t.size-1] == t}
165
- if(!trigger.nil? || message.addressed?)
166
- return if !trigger.nil? && message.message.length == trigger.length
167
- Logger.info("Message has matched against a known trigger")
168
- c = (message.addressed? && trigger.nil?) ? message.message[0].chr.downcase : message.message[trigger.length].chr.downcase
169
- if(c =~ /^[a-z]$/)
170
- type = c.to_sym
171
- elsif(c =~ /^[0-9]$/)
172
- type = :digit
173
- else
174
- type = :other
154
+ unless(trigger.nil? && !message.addressed?)
155
+ return if trigger && message.message.length == trigger.length
156
+ Logger.info('Messages has matched a known trigger')
157
+ # okay, so now that we know we are being asked to do something, lets find
158
+ # a signature that might match. Signatures are sorted by first character
159
+ # so once we have that we can get rolling
160
+ c = (message.addressed? && trigger.nil?) ? message.message[0].chr : message.message[trigger.length].chr
161
+ case c
162
+ when /[A-Za-z]/
163
+ type = c.to_sym
164
+ when /\d/
165
+ type = :digit
166
+ else
167
+ type = :other
175
168
  end
176
- sig_check = @signatures.has_key?(type) ? @signatures[type] : []
177
- sig_check = sig_check + @signatures[:other] if type != :other && @signatures.has_key?(:other)
169
+ sig_check = @signatures[type] ? @signatures[type] : []
170
+ sig_check += @signatures[:other] if @signatures[:other] && type != :other # others are always checked because they are fickle shells of what they once were
178
171
  sig_check.each do |sig|
179
172
  Logger.info("Matching against: #{trigger}#{sig.signature}")
180
173
  esc_trig = trigger.nil? ? '' : Regexp.escape(trigger)
181
- res = message.message.scan(/^#{esc_trig}#{sig.signature}$/)
182
- if(res.size > 0)
183
- next unless message.source.in_group?(sig.group) || message.source.in_group?(@admin) || sig.group.nil?
184
- next if sig.requirement == 'private' && message.is_public?
185
- next if sig.requirement == 'public' && message.is_private?
174
+ result = message.message.scan(/^#{esc_trig}#{sig.signature}$/)
175
+ if(result.size > 0 && @plugins[sig.plugin.to_sym])
176
+ next unless allowed?(message, sig)
186
177
  params = Hash.new
178
+ # symbolize up the parameters for symbolic symbolism
187
179
  sig.params.size.times do |i|
188
- params[sig.params[i].to_sym] = res[0][i]
189
- Logger.info("Signature params: #{sig.params[i].to_sym} = #{res[0][i]}")
180
+ params[sig.params[i].to_sym] = result[0][i]
181
+ Logger.info("Signature params: #{sig.params[i]} = #{result[0][i]}")
190
182
  end
191
- if(@plugins.has_key?(sig.plugin.to_sym))
183
+ # throw it in the pool for processing
184
+ @pool.process do
192
185
  begin
193
- @pool.process{ @plugins[sig.plugin.to_sym].send(sig.values[:method], message, params) }
186
+ @plugins[sig.plugin.to_sym].send(sig.values[:method], message, params)
194
187
  rescue Object => boom
195
- Logger.warn("Plugin threw exception while attempting to process message: #{boom}\n#{boom.backtrace.join("\n")}")
188
+ if(boom.class.to_s == 'SQLite3::BusyException')
189
+ Database.reset_connections
190
+ end
191
+ raise boom
196
192
  end
197
193
  end
198
194
  end
199
195
  end
200
196
  else
201
- Logger.info("Message failed to match any known trigger")
197
+ Logger.info('Message failed to match any known trigger')
202
198
  end
203
199
  end
204
200
 
201
+ # message:: ModSpox::Messages::Incoming
202
+ # sig:: ModSpox::Models::Signature
203
+ # Check if the given message is allowed to be processed
204
+ def allowed?(message, sig)
205
+ return false if sig.requirement == 'private' && message.is_public?
206
+ return false if sig.requirement == 'public' && message.is_private?
207
+ return (message.source.in_group?(sig.group) || message.source.in_group?(@admin) || sig.group.nil?)
208
+ end
209
+
205
210
  end
206
211
 
207
212
  end