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.
- data/CHANGELOG +5 -0
- data/README.rdoc +61 -1
- data/bin/mod_spox +1 -7
- data/data/mod_spox/extras/AutoKick.rb +3 -2
- data/data/mod_spox/extras/AutoMode.rb +2 -1
- data/data/mod_spox/extras/AutoRejoin.rb +5 -4
- data/data/mod_spox/extras/Bouncer.rb +243 -131
- data/data/mod_spox/extras/FloodKicker.rb +2 -1
- data/data/mod_spox/extras/Fortune.rb +5 -1
- data/data/mod_spox/extras/Karma.rb +2 -1
- data/data/mod_spox/extras/Logger.rb +11 -9
- data/data/mod_spox/extras/NickServ.rb +2 -1
- data/data/mod_spox/extras/PhpCli.rb +1 -1
- data/data/mod_spox/extras/PhpFuncLookup.rb +2 -1
- data/data/mod_spox/extras/RegexTracker.rb +2 -1
- data/data/mod_spox/extras/Roulette.rb +2 -1
- data/data/mod_spox/extras/Seen.rb +10 -8
- data/data/mod_spox/extras/Topten.rb +2 -1
- data/data/mod_spox/extras/Translate.rb +2 -1
- data/data/mod_spox/extras/Twitter.rb +1 -1
- data/data/mod_spox/plugins/Authenticator.rb +7 -7
- data/data/mod_spox/plugins/Banner.rb +6 -6
- data/data/mod_spox/plugins/BotNick.rb +2 -1
- data/data/mod_spox/plugins/Initializer.rb +4 -4
- data/data/mod_spox/plugins/Joiner.rb +1 -1
- data/data/mod_spox/plugins/PluginLoader.rb +1 -1
- data/data/mod_spox/plugins/Ponger.rb +8 -8
- data/data/mod_spox/plugins/Status.rb +1 -1
- data/lib/mod_spox/Bot.rb +27 -27
- data/lib/mod_spox/BotConfig.rb +17 -21
- data/lib/mod_spox/Filter.rb +29 -0
- data/lib/mod_spox/FilterManager.rb +63 -0
- data/lib/mod_spox/Helpers.rb +120 -14
- data/lib/mod_spox/Loader.rb +1 -1
- data/lib/mod_spox/MessageFactory.rb +10 -2
- data/lib/mod_spox/Pipeline.rb +66 -61
- data/lib/mod_spox/PluginManager.rb +5 -5
- data/lib/mod_spox/PriorityQueue.rb +21 -8
- data/lib/mod_spox/Sockets.rb +6 -6
- data/lib/mod_spox/Timer.rb +3 -3
- data/lib/mod_spox/Version.rb +2 -2
- data/lib/mod_spox/handlers/UserHost.rb +32 -0
- data/lib/mod_spox/handlers/Whois.rb +7 -7
- data/lib/mod_spox/messages/incoming/Pong.rb +11 -2
- data/lib/mod_spox/messages/incoming/UserHost.rb +24 -0
- data/lib/mod_spox/messages/internal/FilterAdd.rb +20 -0
- data/lib/mod_spox/messages/internal/FilterList.rb +18 -0
- data/lib/mod_spox/messages/internal/FilterListing.rb +22 -0
- data/lib/mod_spox/messages/internal/FilterRemove.rb +20 -0
- data/lib/mod_spox/messages/internal/Incoming.rb +15 -0
- data/lib/mod_spox/migrations/006_ignore.rb +21 -0
- data/lib/mod_spox/models/Nick.rb +11 -0
- data/lib/mod_spox/rfc2812.rb +2 -1
- data/lib/mod_spox/rfc2812_full.rb +185 -0
- data/tests/BotHolder.rb +7 -1
- data/tests/handlers/tc_Created.rb +28 -8
- data/tests/handlers/tc_Invite.rb +11 -9
- data/tests/handlers/tc_Join.rb +36 -16
- data/tests/handlers/tc_Kick.rb +27 -6
- data/tests/handlers/tc_Mode.rb +23 -13
- data/tests/handlers/tc_Names.rb +29 -9
- data/tests/handlers/tc_Nick.rb +30 -8
- data/tests/handlers/tc_Part.rb +30 -20
- data/tests/handlers/tc_Ping.rb +32 -19
- data/tests/handlers/tc_Pong.rb +28 -8
- data/tests/handlers/tc_Privmsg.rb +33 -13
- data/tests/handlers/tc_Quit.rb +30 -17
- data/tests/handlers/tc_Who.rb +8 -3
- data/tests/handlers/tc_Whois.rb +7 -3
- data/tests/lib/tc_BotConfig.rb +35 -0
- data/tests/lib/tc_Helpers.rb +139 -0
- data/tests/lib/tc_PriorityQueue.rb +31 -0
- 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
|
data/lib/mod_spox/Helpers.rb
CHANGED
@@ -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 =>
|
15
|
-
:month =>
|
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
|
-
"
|
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
|
-
|
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
|
-
|
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 =
|
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
|
96
|
-
result = Models::Server.filter(:host => string
|
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
|
data/lib/mod_spox/Loader.rb
CHANGED
@@ -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.
|
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.
|
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')
|
data/lib/mod_spox/Pipeline.rb
CHANGED
@@ -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'
|
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,
|
20
|
-
hook(self, :populate_signatures,
|
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 =
|
57
|
+
type = Helpers.find_const(type)
|
56
58
|
method = method.to_sym unless method.is_a?(Symbol)
|
57
|
-
name = object.class
|
58
|
-
@hooks[type]
|
59
|
-
@hooks[type][name
|
60
|
-
@hooks[type][name
|
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 =
|
69
|
-
name = object.class
|
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 =~ /^[
|
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
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
|
166
|
-
return if
|
167
|
-
Logger.info(
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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
|
177
|
-
sig_check
|
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
|
-
|
182
|
-
if(
|
183
|
-
next unless
|
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] =
|
189
|
-
Logger.info("Signature params: #{sig.params[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
|
-
|
183
|
+
# throw it in the pool for processing
|
184
|
+
@pool.process do
|
192
185
|
begin
|
193
|
-
@
|
186
|
+
@plugins[sig.plugin.to_sym].send(sig.values[:method], message, params)
|
194
187
|
rescue Object => boom
|
195
|
-
|
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(
|
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
|