mod_spox 0.0.2 → 0.0.3

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 (40) hide show
  1. data/CHANGELOG +1 -1
  2. data/README +6 -2
  3. data/bin/mod_spox +15 -9
  4. data/data/mod_spox/extras/AOLSpeak.rb +271 -0
  5. data/data/mod_spox/extras/AutoKick.rb +119 -0
  6. data/data/mod_spox/extras/AutoRejoin.rb +14 -0
  7. data/data/mod_spox/extras/Bullshit.rb +19 -0
  8. data/data/mod_spox/extras/Confess.rb +166 -0
  9. data/data/mod_spox/extras/DevWatch.rb +154 -0
  10. data/data/mod_spox/extras/EightBall.rb +31 -0
  11. data/data/mod_spox/extras/Headers.rb +50 -0
  12. data/data/mod_spox/extras/Karma.rb +16 -17
  13. data/data/mod_spox/extras/LolSpeak.rb +22 -0
  14. data/data/mod_spox/extras/PhpCli.rb +146 -0
  15. data/data/mod_spox/extras/PhpFuncLookup.rb +261 -0
  16. data/data/mod_spox/extras/Quotes.rb +80 -0
  17. data/data/mod_spox/extras/Roulette.rb +24 -3
  18. data/data/mod_spox/extras/Talk.rb +41 -0
  19. data/data/mod_spox/extras/Translate.rb +95 -0
  20. data/data/mod_spox/extras/Weather.rb +55 -0
  21. data/data/mod_spox/plugins/Authenticator.rb +12 -0
  22. data/data/mod_spox/plugins/Banner.rb +3 -3
  23. data/data/mod_spox/plugins/Helper.rb +37 -0
  24. data/lib/mod_spox/Bot.rb +1 -1
  25. data/lib/mod_spox/ConfigurationWizard.rb +12 -5
  26. data/lib/mod_spox/MessageFactory.rb +1 -2
  27. data/lib/mod_spox/Monitors.rb +26 -8
  28. data/lib/mod_spox/Pipeline.rb +13 -13
  29. data/lib/mod_spox/PluginManager.rb +12 -1
  30. data/lib/mod_spox/Pool.rb +212 -29
  31. data/lib/mod_spox/Socket.rb +6 -6
  32. data/lib/mod_spox/Timer.rb +6 -8
  33. data/lib/mod_spox/handlers/Handler.rb +1 -1
  34. data/lib/mod_spox/handlers/Privmsg.rb +1 -0
  35. data/lib/mod_spox/handlers/Who.rb +1 -0
  36. data/lib/mod_spox/messages/internal/PluginRequest.rb +1 -1
  37. data/lib/mod_spox/messages/outgoing/Privmsg.rb +9 -1
  38. data/lib/mod_spox/models/Setting.rb +3 -3
  39. data/lib/mod_spox/models/Signature.rb +1 -1
  40. metadata +18 -20
@@ -0,0 +1,166 @@
1
+ require 'net/http'
2
+ require 'digest/md5'
3
+ require 'cgi'
4
+
5
+
6
+ # IMPORTANT NOTE: This plugin requires installation of the HTMLEntities gem
7
+ class Confess < ModSpox::Plugin
8
+
9
+ include Models
10
+
11
+ def initialize(pipeline)
12
+ super(pipeline)
13
+ begin
14
+ require 'htmlentities'
15
+ rescue Object => boom
16
+ Logger.log('Error: This plugin requires the HTMLEntities gem. Please install and reload plugin.')
17
+ raise Exceptions::BotException.new("Missing required HTMLEntities library")
18
+ end
19
+ Confession.create_table unless Confession.table_exists?
20
+ Signature.find_or_create(:signature => 'confess ?(.+)?', :plugin => name, :method => 'confess',
21
+ :description => 'Print a confession').params = [:term]
22
+ Signature.find_or_create(:signature => 'confess(\+\+|\-\-) ?(\d+)?', :plugin => name, :method => 'score',
23
+ :description => 'Score a confession').params = [:score, :id]
24
+ Signature.find_or_create(:signature => 'confess score (\d+)', :plugin => name, :method => 'show_score',
25
+ :description => 'Show a confession\'s score').params = [:id]
26
+ Signature.find_or_create(:signature => 'confess count', :plugin => name, :method => 'count',
27
+ :description => 'Current count of cached confessions')
28
+ Signature.find_or_create(:signature => 'confess fetcher (start|stop)', :plugin => name, :method => 'fetcher',
29
+ :description => 'Turn confession fetcher on or off', :group_id => Group.filter(:name => 'admin').first.pk).params = [:status]
30
+ Config[:confess] = 'nofetch' if Config[:confess].nil?
31
+ @last_confession = {}
32
+ @fetch = false
33
+ @mutex = Mutex.new
34
+ @coder = HTMLEntities.new
35
+ start_fetcher if Config[:confess] == 'fetch'
36
+ end
37
+
38
+ def confess(message, params)
39
+ c = nil
40
+ reg = false
41
+ if(params[:term])
42
+ if(params[:term] =~ /^\d+$/)
43
+ c = Confession[params[:term].to_i]
44
+ else
45
+ c = Confession.filter(:confession => Regexp.new(params[:term])).first
46
+ reg = true
47
+ end
48
+ else
49
+ ids = Confession.map(:id)
50
+ c = Confession[ids[rand(ids.size - 1)].to_i]
51
+ end
52
+ if(c)
53
+ reply message.replyto, "\2[#{c.pk}]\2: #{reg ? c.confession.gsub(/(#{params[:term]})/, "\2\\1\2") : c.confession}"
54
+ @last_confession[message.target.pk] = c.pk
55
+ else
56
+ reply message.replyto, "\2Error:\2 Failed to find confession"
57
+ end
58
+ end
59
+
60
+ def show_score(message, params)
61
+ c = Confession[params[:id].to_i]
62
+ if(c)
63
+ reply message.replyto, "\2[#{c.pk}]:\2 #{c.score.to_i}% of raters gave this confession a positive score"
64
+ else
65
+ reply message.replyto, "\2Error:\2 Failed to find confession with ID: #{params[:id]}"
66
+ end
67
+ end
68
+
69
+ def score(message, params)
70
+ if(params[:id])
71
+ c = Confession[params[:id].to_i]
72
+ else
73
+ c = Confession[@last_confession[message.target.pk]] if @last_confession.has_key?(message.target.pk)
74
+ end
75
+ if(c)
76
+ if(params[:score] == '++')
77
+ c.set(:positive => c.positive.to_i + 1)
78
+ else
79
+ c.set(:negative => c.negative.to_i + 1)
80
+ end
81
+ c.set(:score => ((c.positive.to_f) / (c.positive.to_f + c.negative.to_f)) * 100.0)
82
+ else
83
+ reply message.replyto, "\2Error:\2 Failed to find confession to score"
84
+ end
85
+ end
86
+
87
+ def count(message, params)
88
+ reply message.replyto, "Current number of stored confessions: #{Confession.all.size}"
89
+ end
90
+
91
+ def fetcher(message, params)
92
+ if(params[:status] == 'start')
93
+ if(Config[:confess] == 'fetch')
94
+ reply message.replyto, 'Confession fetcher is already running'
95
+ else
96
+ Config[:confess] = 'fetch'
97
+ reply message.replyto, 'Confession fetcher is now running'
98
+ start_fetcher
99
+ end
100
+ else
101
+ if(Config[:confess] == 'fetch')
102
+ Config[:confess] = 'nofetch'
103
+ reply message.replyto, 'Confession fetcher has been stopped'
104
+ else
105
+ reply message.replyto, 'Confession fetcher is not currently running'
106
+ end
107
+ end
108
+ end
109
+
110
+ def grab_page
111
+ begin
112
+ connection = Net::HTTP.new('beta.grouphug.us', 80)
113
+ response = connection.request_get('/random', nil)
114
+ response.value
115
+ page = response.body.gsub(/[\r\n]/, ' ')
116
+ Logger.log("Processing matches")
117
+ page.scan(/<div class="content">\s*<p>\s*(.+?)\s*<\/p>\s*<\/div>/).each{|match|
118
+ Logger.log("Match found: #{match[0]}")
119
+ conf = CGI::unescapeHTML(match[0])
120
+ conf = conf.gsub(/<.+?>/, ' ').gsub(/[\r\n]/, '').gsub(/\s+/, ' ')
121
+ conf = @coder.decode(conf)
122
+ Logger.log("Match turned into: #{conf}")
123
+ if conf.length < 300
124
+ begin
125
+ Confession.new(:confession => conf, :hash => Digest::MD5.hexdigest(conf)).save
126
+ rescue Object => boom
127
+ Logger.log 'Warning: Fetched confession already found in database'
128
+ end
129
+ end
130
+ }
131
+ rescue Object => boom
132
+ Logger.log "Error fetching data: #{boom}"
133
+ end
134
+ if(Config[:confess] == 'fetch')
135
+ @pipeline << Messages::Internal::TimerAdd.new(self, rand(500), nil, true){ grab_page }
136
+ else
137
+ stop_fetcher
138
+ end
139
+ end
140
+
141
+ private
142
+
143
+ def start_fetcher
144
+ @mutex.synchronize do
145
+ grab_page unless @fetch
146
+ end
147
+ end
148
+
149
+ def stop_fetcher
150
+ @mutex.synchronize do
151
+ @fetch = false
152
+ end
153
+ end
154
+
155
+ class Confession < Sequel::Model
156
+ set_schema do
157
+ text :confession, :null => false
158
+ text :hash, :null => false, :unique => true
159
+ integer :positive, :null => false, :default => 0
160
+ integer :negative, :null => false, :default => 0
161
+ float :score, :null => false, :default => 0
162
+ primary_key :id
163
+ end
164
+ end
165
+
166
+ end
@@ -0,0 +1,154 @@
1
+ require 'rexml/document'
2
+ require 'open-uri'
3
+
4
+ class DevWatch < ModSpox::Plugin
5
+
6
+ include ModSpox::Models
7
+
8
+ def initialize(pipeline)
9
+ super(pipeline)
10
+ admin = Group.filter(:name => 'admin').first
11
+ Signature.find_or_create(:signature => 'devwatch (on|off) (\S+)', :plugin => name, :method => 'enable_watch', :group_id => admin.pk,
12
+ :description => 'Turn development watcher on/off in given channel').params = [:status, :channel]
13
+ Signature.find_or_create(:signature => 'devwatch list', :plugin => name, :method => 'watch_list', :group_id => admin.pk,
14
+ :description => 'List all channels on the development watch list')
15
+ Signature.find_or_create(:signature => 'devwatch url ?(\S+)?', :plugin => name, :method => 'set_url', :group_id => admin.pk,
16
+ :description => 'Set URL for development RSS feed').params = [:url]
17
+ Signature.find_or_create(:signature => 'devwatch interval ?(\d+)?', :plugin => name, :method => 'set_interval', :group_id => admin.pk,
18
+ :description => 'Set time interval for notifications').params = [:time]
19
+ if(Setting[:devwatch].nil?)
20
+ Setting.find_or_create(:name => 'devwatch').value = {:channels => [], :interval => 300}
21
+ end
22
+ @pipeline.hook(self, :timer_response, :Internal_TimerResponse)
23
+ @original = nil
24
+ @new = nil
25
+ @timer = nil
26
+ @lock = Mutex.new
27
+ run
28
+ end
29
+
30
+ def enable_watch(message, params)
31
+ channel = Channel.filter(:name => params[:channel]).first
32
+ if(channel)
33
+ vals = Setting[:devwatch]
34
+ if(params[:status] == 'on')
35
+ vals[:channels] << channel.pk unless vals[:channels].include?(channel.pk)
36
+ reply(message.replyto, "#{channel.name} is now on the development watch list")
37
+ else
38
+ vals[:channels].delete(channel.pk) if vals[:channels].include?(channel.pk)
39
+ reply(message.replyto, "#{channel.name} has been removed from the development watch list")
40
+ end
41
+ Setting.filter(:name => 'devwatch').first.value = vals
42
+ run
43
+ else
44
+ reply(message.replyto, "\2Error:\2 I have no record of #{params[:channel]}.")
45
+ end
46
+ end
47
+
48
+ def watch_list(message, params)
49
+ if(Setting[:devwatch][:channels].empty?)
50
+ reply(message.replyto, "No channels currently on the development watch list")
51
+ else
52
+ chans = []
53
+ Setting[:devwatch][:channels].each{|id| chans << Channel[id].name}
54
+ reply(message.replyto, "Channels on development watch list: #{chans.join(', ')}")
55
+ end
56
+ end
57
+
58
+ def set_url(message, params)
59
+ if(params[:url])
60
+ vals = Setting[:devwatch]
61
+ vals[:url] = params[:url]
62
+ reply(message.replyto, "OK")
63
+ Setting.filter(:name => 'devwatch').first.value = vals
64
+ run
65
+ else
66
+ if(Setting[:devwatch].has_key?(:url))
67
+ reply(message.replyto, "\2Devwatch URL:\2 #{Setting[:devwatch][:url]}")
68
+ else
69
+ reply(message.replyto, "\2Error:\2 No URL set for devwatch")
70
+ end
71
+ end
72
+ end
73
+
74
+ def set_interval(message, params)
75
+ if(params[:time])
76
+ vals = Setting[:devwatch]
77
+ vals[:interval] = params[:time].to_i
78
+ Setting.filter(:name => 'devwatch').first.value = vals
79
+ @timer.reset_period(params[:time].to_i) unless @timer.nil?
80
+ reply(message.replyto, "Devwatch announcement interval reset to: #{Helpers.format_seconds(params[:time].to_i)}")
81
+ else
82
+ reply(message.replyto, "Devwatch announcement interval set to: #{Helpers.format_seconds(Setting[:devwatch][:interval].to_i)}")
83
+ end
84
+ end
85
+
86
+ def run
87
+ @lock.synchronize do
88
+ check_updates
89
+ if(Setting[:devwatch].has_key?(:url) && Setting[:devwatch][:channels].size > 0)
90
+ if(@timer.nil?)
91
+ @pipeline << ModSpox::Messages::Internal::TimerAdd.new(self, Setting[:devwatch][:interval].to_i){check_updates}
92
+ sleep(0.01) while @timer.nil?
93
+ end
94
+ else
95
+ if(@timer)
96
+ @pipeline << ModSpox::Messages::Internal::TimerRemove.new(@timer)
97
+ sleep(0.01) until @timer.nil?
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ def timer_response(message)
104
+ if(message.origin == self && message.action_added?)
105
+ @timer = message.action
106
+ elsif(message.action == @timer && message.action_removed?)
107
+ @timer = nil
108
+ end
109
+ end
110
+
111
+ def check_updates
112
+ if(Setting[:devwatch].has_key?(:url) && Setting[:devwatch][:channels].size > 0)
113
+ src = open(Setting[:devwatch][:url])
114
+ doc = REXML::Document.new(src.read)
115
+ if @original.nil?
116
+ doc.elements.each('rss/channel/item') do |item|
117
+ @original = item.elements['title'].text
118
+ break
119
+ end
120
+ Logger.log("Initialized development watch RSS feed: #{@original}")
121
+ else
122
+ @new = doc
123
+ print_new
124
+ end
125
+ end
126
+ end
127
+
128
+ def print_new
129
+ new_items = Array.new
130
+ # run through the list until we hit a duplicate #
131
+ i = 1
132
+ new_orig = nil
133
+ @new.elements.each('rss/channel/item') do |item|
134
+ if item.elements['title'].text == @original
135
+ break
136
+ else
137
+ new_orig = item.elements['title'].text if new_orig.nil?
138
+ new_items << "#{item.elements['title'].text}: #{item.elements['link'].text}"
139
+ end
140
+ i += 1
141
+ end
142
+ @original = new_orig.nil? ? @original : new_orig
143
+ if new_items.size > 0
144
+ new_items.reverse!
145
+ Setting[:devwatch][:channels].each do |id|
146
+ channel = Channel[id]
147
+ new_items.each do |item|
148
+ reply(channel, item)
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ end
@@ -0,0 +1,31 @@
1
+ class EightBall < ModSpox::Plugin
2
+
3
+ include Models
4
+
5
+ def initialize(pipeline)
6
+ super
7
+ Signature.find_or_create(:signature => '8ball .+\?$', :plugin => name, :method => 'eightball', :description => 'Ask the magic eightball a question')
8
+ @responses = ['Ask Again Later',
9
+ 'Better Not Tell You Now',
10
+ 'Concentrate and Ask Again',
11
+ 'Don\'t Count on It',
12
+ 'It Is Certain',
13
+ 'Most Likely',
14
+ 'My Reply is No',
15
+ 'My Sources Say No',
16
+ 'No',
17
+ 'Outlook Good',
18
+ 'Outlook Not So Good',
19
+ 'Reply Hazy, Try Again',
20
+ 'Signs Point to Yes',
21
+ 'Yes',
22
+ 'Yes, Definitely',
23
+ 'You May Rely On It']
24
+ end
25
+
26
+ def eightball(message, params)
27
+ @pipeline << Messages::Outgoing::Privmsg.new(message.replyto, 'shakes magic 8 ball...', true)
28
+ reply message.replyto, "#{message.source.nick}: #{@responses[rand(@responses.size) - 1]}"
29
+ end
30
+
31
+ end
@@ -0,0 +1,50 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+
4
+ class Headers < ModSpox::Plugin
5
+
6
+ def initialize(pipeline)
7
+ super(pipeline)
8
+ Models::Signature.find_or_create(:signature => 'headers (https?:\/\/\S+)', :plugin => name, :method => 'fetch_headers',
9
+ :description => 'Fetch HTTP headers').params = [:url]
10
+ @lock = Mutex.new
11
+ end
12
+
13
+ def fetch_headers(message, params)
14
+ @lock.synchronize do
15
+ secure = false
16
+ if(params[:url] =~ /^http:/)
17
+ port = 80
18
+ else
19
+ port = 443
20
+ secure = true
21
+ end
22
+ params[:url].gsub!(/^https?:\/\//, '')
23
+ if(params[:url] =~ /:(\d+)/)
24
+ port = $1.to_i
25
+ params[:url].gsub!(/:(\d+)/, '')
26
+ end
27
+ if(params[:url] =~ /(.+?[a-zA-Z]{2,4})(\/.+)$/)
28
+ location = $1
29
+ page = $2
30
+ else
31
+ location = params[:url]
32
+ page = '/'
33
+ end
34
+ begin
35
+ reply message.replyto, "Connecting to: #{location} on port: #{port} retrieving: #{page}"
36
+ con = Net::HTTP.new(location, port)
37
+ con.use_ssl = secure
38
+ response = con.get(page, nil)
39
+ reply message.replyto, "Response code: #{response.code}"
40
+ response.each{|key,val|
41
+ reply message.replyto, "#{key}: #{val}"
42
+ }
43
+ reply message.replyto, "Header listing complete"
44
+ rescue Object => boom
45
+ reply message.replyto, "Error retrieving headers: #{boom}"
46
+ end
47
+ end
48
+ end
49
+
50
+ end
@@ -4,7 +4,7 @@ class Karma < ModSpox::Plugin
4
4
 
5
5
  def initialize(pipeline)
6
6
  super(pipeline)
7
- KarmaDatatype::Karma.create_table unless KarmaDatatype::Karma.table_exists?
7
+ Datatype::Karma.create_table unless Datatype::Karma.table_exists?
8
8
  Models::Signature.find_or_create(:signature => 'karma (\S+)', :plugin => name, :method => 'score', :description => 'Returns karma for given thing').params = [:thing]
9
9
  Models::Signature.find_or_create(:signature => 'karma reset (\S+)', :plugin => name, :method => 'reset',
10
10
  :group_id => Models::Group.filter(:name => 'admin').first.pk, :description => 'Reset a karma score').params = [:thing]
@@ -20,7 +20,7 @@ class Karma < ModSpox::Plugin
20
20
  thing.downcase!
21
21
  thing = thing[1..-2] if thing[0..0] == '(' && thing[-1..1] == ')'
22
22
  adj = adj == '++' ? +1 : -1
23
- karma = KarmaDatatype::Karma.find_or_create(:thing => thing, :channel_id => message.target.pk)
23
+ karma = Datatype::Karma.find_or_create(:thing => thing, :channel_id => message.target.pk)
24
24
  karma.set(:score => karma.score + adj)
25
25
  end
26
26
  end
@@ -29,7 +29,7 @@ class Karma < ModSpox::Plugin
29
29
  def score(message, params)
30
30
  params[:thing].downcase!
31
31
  return unless message.is_public?
32
- karma = KarmaDatatype::Karma.filter(:thing => params[:thing], :channel_id => message.target.pk).first
32
+ karma = Datatype::Karma.filter(:thing => params[:thing], :channel_id => message.target.pk).first
33
33
  if(karma)
34
34
  @pipeline << Privmsg.new(message.replyto, "Karma for \2#{karma.thing}\2 is #{karma.score}")
35
35
  else
@@ -40,7 +40,7 @@ class Karma < ModSpox::Plugin
40
40
  def reset(message, params)
41
41
  params[:thing].downcase!
42
42
  return unless message.is_public?
43
- karma = KarmaDatatype::Karma.filter(:thing => params[:thing], :channel_id => message.target.pk).first
43
+ karma = Datatype::Karma.filter(:thing => params[:thing], :channel_id => message.target.pk).first
44
44
  if(karma)
45
45
  karma.set(:score => 0)
46
46
  @pipeline << Privmsg.new(message.replyto, "Karma for \2#{karma.thing}\2 has been reset")
@@ -49,19 +49,18 @@ class Karma < ModSpox::Plugin
49
49
  end
50
50
  end
51
51
 
52
- end
53
-
54
- module KarmaDatatype
55
- class Karma < Sequel::Model
56
- set_schema do
57
- primary_key :id
58
- text :thing, :null => false, :unique => true
59
- integer :score, :null => false, :default => 0
60
- foreign_key :channel_id, :table => :channels
61
- end
62
-
63
- def channel
64
- ModSpox::Models::Channel[channel_id]
52
+ module Datatype
53
+ class Karma < Sequel::Model
54
+ set_schema do
55
+ primary_key :id
56
+ text :thing, :null => false, :unique => true
57
+ integer :score, :null => false, :default => 0
58
+ foreign_key :channel_id, :table => :channels
59
+ end
60
+
61
+ def channel
62
+ ModSpox::Models::Channel[channel_id]
63
+ end
65
64
  end
66
65
  end
67
66
  end
@@ -0,0 +1,22 @@
1
+ # IMPORTANT NOTE: This plugin requires installation of the LOLSpeak gem
2
+ class LolSpeak < ModSpox::Plugin
3
+
4
+ include Models
5
+
6
+ def initialize(pipeline)
7
+ super
8
+ begin
9
+ require 'lolspeak'
10
+ rescue Object => boom
11
+ Logger.log('Error: This plugins requires the lolspeak gem. Please install gem and reload plugin.')
12
+ raise BotException.new("Failed to initialize plugin. Missing lolspeak gem.")
13
+ end
14
+ Signature.find_or_create(:signature => 'lolspeak (.+)', :plugin => name, :method => 'translate',
15
+ :description => 'Translate text to lolspeak').params = [:text]
16
+ end
17
+
18
+ def translate(message, params)
19
+ reply message.replyto, "\2lulz:\2 #{params[:text].to_lolspeak}"
20
+ end
21
+
22
+ end
@@ -0,0 +1,146 @@
1
+ # IMPORTANT NOTE: This plugin will only function if the PHP executable can be located
2
+ class PhpCli < ModSpox::Plugin
3
+
4
+ include Models
5
+ include Messages::Outgoing
6
+
7
+ def initialize(pipeline)
8
+ super(pipeline)
9
+ @path = Config[:plugin_directory] + '/phpcli'
10
+ @botini = @path + '/bot.ini'
11
+ unless(File.directory?(@path))
12
+ FileUtils.mkdir_p(@path)
13
+ end
14
+ result = Helpers.safe_exec('which php')
15
+ raise NoInterpreter.new if result.empty?
16
+ unless File.exists?(@botini)
17
+ ini = File.new(@botini, 'w')
18
+ ini.write($ini)
19
+ ini.close
20
+ end
21
+ php = Group.find_or_create(:name => 'PHP')
22
+ admin = Group.filter(:name => 'admin').first
23
+ Signature.find_or_create(:signature => 'php (on|off)', :plugin => name, :method => 'set_channel', :group_id => admin.pk,
24
+ :description => 'Add or remove channel from allowing PHP command').params = [:action]
25
+ Signature.find_or_create(:signature => 'php (?!on|off)(.+)', :plugin => name, :method => 'execute_php', :group_id => php.pk,
26
+ :description => 'Execute PHP code').params = [:code]
27
+ Setting[:phpcli] = '' if Setting[:phpcli].nil?
28
+ @channels = Setting[:phpcli].split('|')
29
+ end
30
+
31
+ def set_channel(message, params)
32
+ return unless message.is_public?
33
+ if(params[:action] == 'on')
34
+ unless(@channels.include?(message.target.pk))
35
+ @channels << message.target.pk
36
+ Setting[:phpcli] = @channels.join('|')
37
+ end
38
+ reply message.replyto, 'PHP command now active'
39
+ else
40
+ unless(@channels.include?(message.target.pk))
41
+ reply message.replyto, 'PHP command is not currently active in this channel'
42
+ else
43
+ @channels.delete(message.target.pk)
44
+ Setting[:phpcli] = @channels.join('|')
45
+ reply message.replyto, 'PHP command is now disabled'
46
+ end
47
+ end
48
+ end
49
+
50
+ def execute_php(message, params)
51
+ return unless @channels.include?(message.target.pk)
52
+ filepath = @path + "/#{rand(99999)}.bot.php"
53
+ file = File.open(filepath, 'w')
54
+ file.write("<? $_SERVER = $_ENV = array(); #{params[:code]} ?>")
55
+ file.close
56
+ begin
57
+ output = Helpers.safe_exec("php -c #{@path}/bot.ini -d max_execution_time=10 #{filepath} 2>&1 | head -n 4")
58
+ if(output =~ /^sh: line [0-9]+:(.*)$/)
59
+ output = $1
60
+ end
61
+ if(output =~ /^(Fatal error|Warning|Parse error): (.+?) in .*? on line [0-9]+[\n|\r]*(.*)$/)
62
+ warning = $2
63
+ type = $1
64
+ output = $3
65
+ end
66
+ if(output.length > 300)
67
+ reply message.replyto, "#{message.source.nick}: Your result has been truncated. Don't print so much."
68
+ output = output.slice(0, 300)
69
+ end
70
+ if(!warning.nil?)
71
+ reply message.replyto, "PHP #{type}: "+warning
72
+ end
73
+ if(warning.nil? || type !~ /(Fatal|Parse)/)
74
+ reply message.replyto, "Result: "+output
75
+ end
76
+ File.delete(filepath)
77
+ rescue
78
+ reply message.replyto, "\2Error:\2 Timeout reached. Script execution terminated"
79
+ File.delete(filepath)
80
+ end
81
+ end
82
+
83
+ class NoInterpreter < Exceptions::BotException
84
+ end
85
+
86
+ end
87
+
88
+ $ini = <<EOF
89
+ [PHP]
90
+ engine = On
91
+ zend.ze1_compatibility_mode = Off
92
+ short_open_tag = On
93
+ asp_tags = Off
94
+ precision = 12
95
+ y2k_compliance = On
96
+ output_buffering = Off
97
+ zlib.output_compression = Off
98
+ implicit_flush = Off
99
+ unserialize_callback_func=
100
+ serialize_precision = 100
101
+ allow_call_time_pass_reference = On
102
+ safe_mode = On
103
+ safe_mode_gid = On
104
+ safe_mode_include_dir = /tmp/mod_spox/php
105
+ safe_mode_exec_dir = /tmp/mod_spox/php
106
+ safe_mode_allowed_env_vars = PHP_
107
+ safe_mode_protected_env_vars = LD_LIBRARY_PATH
108
+ open_basedir = /tmp/mod_spox
109
+ disable_functions = fscanf fputs chown chmod copy delete fflush file flock ftell glob link fseek lstat move_uploaded_file rename realpath set_file_buffer touch fprintf chgrp fgetss readfile dio_close dio_fnctl dio_open dio_read dio_seek dio_stat dio_tcsetattr dio_truncate dio_write chdir chroot dir closedir getcwd opendir readdir rewinddir scandir posix_kill posix_access posix_ctermid posix_get_last_error posix_getcwd posix_getegid posix_geteuid posix_getgid posix_getgrgid posix_getgrnam posix_getgroups posix_getlogin posix_getpgid posix_getpgrp posix_getpid posix_getppid posix_getpwnam posix_getpwuid posix_getwuid posix_getrlimit posix_getsid posix_getuid posix_isatty posix_mkfifo posix_mknod posix_setegid posix_setgid posix_setpgid posix_setsid posix_setuid posix_strerror posix_times posix_ttyname posix_uname expect_expectl expect_popen sleep time_sleep_until usleep pfsockopen fsockopen openlog debugger_on proc_open pclose popen fsockopen fread set_time_limit ini_set ini_alter ini_restore exec system passthru proc_close proc_nice proc_open proc_terminiate shell_exec sleep usleep pcntl_fork pcntl_exec pcntl_alarm pcntl_getpriority pcntl_setpriority pcntl_waitpid pcntl_wexitstatus pcntl_wifexited pcntl_wifsignaled pcntl_wifstopped pcntl_wstopsig pcntl_wtermsig readline_add_history readline_callback_handler_install readline_callback_handler_remove readline_callback_read_char readline_clear_history readline_completion_function readline_info readline_list_history readline_on_new_line readline_read_history readline_redisplay readline_write_history readline dl set_include_path set_magic_quotes_runtime file_put_contents fwrite fputs copy fputcsv tmpfile symlink tempnam mysql_connect unlink putenv ftp_connect socket_create socket_create socket_close socket_accept socket_bind socket_close socket_connect socket_create_listen socket_create_pair socket_get_option socket_listen socket_read socket_recv socket_select socket_send socket_sendto shmop_close shmop_open shmop_delete shmop_read shmop_size shmop_write msg_get_queue msg_receive msg_remove_queue msg_send msg_set_queue msg_stat_queue msg_acquire sem_aquire sem_release sem_get sem_remove mail time_nanosleep usleep
110
+ disable_classes = dir
111
+ expose_php = On
112
+ max_execution_time = 10
113
+ max_input_time = 20
114
+ memory_limit = 4M
115
+ error_reporting = E_ALL & ~E_NOTICE & ~E_STRICT
116
+ display_errors = On
117
+ display_startup_errors = Off
118
+ log_errors = Off
119
+ log_errors_max_len = 1024
120
+ ignore_repeated_errors = Off
121
+ ignore_repeated_source = Off
122
+ report_memleaks = On
123
+ track_errors = Off
124
+ variables_order = "EGPCS"
125
+ register_globals = Off
126
+ register_long_arrays = On
127
+ register_argc_argv = On
128
+ post_max_size = 8M
129
+ magic_quotes_gpc = On
130
+ magic_quotes_runtime = Off
131
+ magic_quotes_sybase = Off
132
+ auto_prepend_file =
133
+ auto_append_file =
134
+ default_mimetype = "text/html"
135
+ include_path = ".:/usr/share/php"
136
+ doc_root = /tmp/mod_spox/php
137
+ user_dir = /tmp/mod_spox/php
138
+ enable_dl = On
139
+ file_uploads = Off
140
+ allow_url_fopen = Off
141
+ default_socket_timeout = 10
142
+ define_syslog_variables = Off
143
+ sendmail_path = /dev/null
144
+ [Sockets]
145
+ sockets.use_system_read = On
146
+ EOF