spox-mod_spox 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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