mod_spox 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. data/CHANGELOG +31 -1
  2. data/LICENSE +674 -0
  3. data/README.rdoc +73 -0
  4. data/bin/mod_spox +28 -28
  5. data/data/mod_spox/extras/AOLSpeak.rb +2 -3
  6. data/data/mod_spox/extras/AutoKick.rb +10 -23
  7. data/data/mod_spox/extras/AutoMode.rb +12 -23
  8. data/data/mod_spox/extras/Bash.rb +55 -0
  9. data/data/mod_spox/extras/Bouncer.rb +85 -57
  10. data/data/mod_spox/extras/Bullshit.rb +1 -1
  11. data/data/mod_spox/extras/Bytes.rb +1 -2
  12. data/data/mod_spox/extras/Confess.rb +27 -29
  13. data/data/mod_spox/extras/DCC.rb +11 -20
  14. data/data/mod_spox/extras/DevWatch.rb +21 -23
  15. data/data/mod_spox/extras/DownForEveryoneOrJustMe.rb +47 -0
  16. data/data/mod_spox/extras/EightBall.rb +1 -1
  17. data/data/mod_spox/extras/FML.rb +35 -0
  18. data/data/mod_spox/extras/Headers.rb +31 -50
  19. data/data/mod_spox/extras/Karma.rb +81 -29
  20. data/data/mod_spox/extras/Logger.rb +2 -2
  21. data/data/mod_spox/extras/LolSpeak.rb +1 -2
  22. data/data/mod_spox/extras/PhpCli.rb +138 -8
  23. data/data/mod_spox/extras/PhpFuncLookup.rb +20 -23
  24. data/data/mod_spox/extras/Pinger.rb +1 -1
  25. data/data/mod_spox/extras/Quotes.rb +8 -10
  26. data/data/mod_spox/extras/RegexTracker.rb +2 -4
  27. data/data/mod_spox/extras/Roulette.rb +20 -27
  28. data/data/mod_spox/extras/RubyCli.rb +93 -0
  29. data/data/mod_spox/extras/Search.rb +17 -3
  30. data/data/mod_spox/extras/Seen.rb +150 -0
  31. data/data/mod_spox/extras/SlashdotHeadlineGenerator.rb +500 -0
  32. data/data/mod_spox/extras/Talk.rb +2 -4
  33. data/data/mod_spox/extras/Topten.rb +10 -12
  34. data/data/mod_spox/extras/TracTicket.rb +3 -5
  35. data/data/mod_spox/extras/Translate.rb +20 -22
  36. data/data/mod_spox/extras/Twitter.rb +118 -33
  37. data/data/mod_spox/extras/UrbanDictionary.rb +8 -17
  38. data/data/mod_spox/extras/Weather.rb +1 -2
  39. data/data/mod_spox/plugins/Authenticator.rb +93 -98
  40. data/data/mod_spox/plugins/Banner.rb +26 -56
  41. data/data/mod_spox/plugins/Helper.rb +5 -6
  42. data/data/mod_spox/plugins/Initializer.rb +4 -14
  43. data/data/mod_spox/plugins/Joiner.rb +1 -1
  44. data/data/mod_spox/plugins/Nicker.rb +13 -0
  45. data/data/mod_spox/plugins/Parter.rb +2 -2
  46. data/data/mod_spox/plugins/Permissions.rb +60 -0
  47. data/data/mod_spox/plugins/PluginLoader.rb +7 -12
  48. data/data/mod_spox/plugins/Ponger.rb +51 -0
  49. data/data/mod_spox/plugins/Quitter.rb +1 -2
  50. data/data/mod_spox/plugins/Servers.rb +57 -0
  51. data/data/mod_spox/plugins/Status.rb +3 -2
  52. data/data/mod_spox/plugins/Triggers.rb +9 -9
  53. data/lib/mod_spox/Bot.rb +109 -33
  54. data/lib/mod_spox/BotConfig.rb +2 -2
  55. data/lib/mod_spox/ConfigurationWizard.rb +12 -12
  56. data/lib/mod_spox/Database.rb +1 -4
  57. data/lib/mod_spox/Exceptions.rb +26 -0
  58. data/lib/mod_spox/Helpers.rb +29 -68
  59. data/lib/mod_spox/Loader.rb +23 -24
  60. data/lib/mod_spox/Logger.rb +19 -17
  61. data/lib/mod_spox/MessageFactory.rb +50 -24
  62. data/lib/mod_spox/Pipeline.rb +21 -7
  63. data/lib/mod_spox/Plugin.rb +27 -3
  64. data/lib/mod_spox/PluginManager.rb +28 -15
  65. data/lib/mod_spox/PriorityQueue.rb +69 -0
  66. data/lib/mod_spox/Socket.rb +93 -51
  67. data/lib/mod_spox/Sockets.rb +76 -63
  68. data/lib/mod_spox/Timer.rb +21 -141
  69. data/lib/mod_spox/Version.rb +14 -0
  70. data/lib/mod_spox/handlers/BadNick.rb +1 -1
  71. data/lib/mod_spox/handlers/Bounce.rb +5 -5
  72. data/lib/mod_spox/handlers/Created.rb +13 -5
  73. data/lib/mod_spox/handlers/Handler.rb +12 -3
  74. data/lib/mod_spox/handlers/Invite.rb +14 -8
  75. data/lib/mod_spox/handlers/Join.rb +24 -20
  76. data/lib/mod_spox/handlers/Kick.rb +22 -13
  77. data/lib/mod_spox/handlers/Mode.rb +42 -36
  78. data/lib/mod_spox/handlers/Motd.rb +4 -0
  79. data/lib/mod_spox/handlers/Names.rb +66 -39
  80. data/lib/mod_spox/handlers/Nick.rb +20 -14
  81. data/lib/mod_spox/handlers/Part.rb +25 -8
  82. data/lib/mod_spox/handlers/Ping.rb +11 -5
  83. data/lib/mod_spox/handlers/Pong.rb +9 -5
  84. data/lib/mod_spox/handlers/Privmsg.rb +25 -17
  85. data/lib/mod_spox/handlers/Quit.rb +13 -8
  86. data/lib/mod_spox/handlers/Topic.rb +4 -0
  87. data/lib/mod_spox/handlers/Welcome.rb +16 -24
  88. data/lib/mod_spox/handlers/Who.rb +64 -48
  89. data/lib/mod_spox/handlers/Whois.rb +92 -60
  90. data/lib/mod_spox/messages/incoming/Nick.rb +2 -2
  91. data/lib/mod_spox/messages/incoming/Privmsg.rb +1 -1
  92. data/lib/mod_spox/messages/incoming/Whois.rb +1 -0
  93. data/lib/mod_spox/messages/internal/EstablishConnection.rb +1 -1
  94. data/lib/mod_spox/messages/internal/PluginsReady.rb +10 -0
  95. data/lib/mod_spox/messages/internal/QueueSocket.rb +8 -0
  96. data/lib/mod_spox/messages/internal/Reconnect.rb +8 -0
  97. data/lib/mod_spox/messages/internal/UnqueueSocket.rb +8 -0
  98. data/lib/mod_spox/migrations/002_persistent_sigs.rb +14 -0
  99. data/lib/mod_spox/migrations/003_auth_restructure.rb +31 -0
  100. data/lib/mod_spox/migrations/004_mode_index_fix.rb +18 -0
  101. data/lib/mod_spox/migrations/005_nick_mode_nopark.rb +18 -0
  102. data/lib/mod_spox/models/Auth.rb +16 -46
  103. data/lib/mod_spox/models/AuthMask.rb +13 -0
  104. data/lib/mod_spox/models/Channel.rb +46 -27
  105. data/lib/mod_spox/models/Config.rb +10 -19
  106. data/lib/mod_spox/models/Group.rb +20 -8
  107. data/lib/mod_spox/models/Models.rb +1 -1
  108. data/lib/mod_spox/models/Nick.rb +105 -113
  109. data/lib/mod_spox/models/NickMode.rb +23 -9
  110. data/lib/mod_spox/models/Server.rb +12 -1
  111. data/lib/mod_spox/models/Setting.rb +12 -16
  112. data/lib/mod_spox/models/Signature.rb +28 -8
  113. data/tests/BotHolder.rb +24 -0
  114. data/tests/handlers/tc_BadNick.rb +21 -0
  115. data/tests/handlers/tc_Created.rb +24 -0
  116. data/tests/handlers/tc_Invite.rb +50 -0
  117. data/tests/handlers/tc_Join.rb +33 -0
  118. data/tests/handlers/tc_Kick.rb +32 -0
  119. data/tests/handlers/tc_Mode.rb +85 -0
  120. data/tests/handlers/tc_Names.rb +35 -0
  121. data/tests/handlers/tc_Nick.rb +55 -0
  122. data/tests/handlers/tc_Part.rb +44 -0
  123. data/tests/handlers/tc_Ping.rb +40 -0
  124. data/tests/handlers/tc_Pong.rb +28 -0
  125. data/tests/handlers/tc_Privmsg.rb +85 -0
  126. data/tests/handlers/tc_Quit.rb +40 -0
  127. data/tests/handlers/tc_Who.rb +50 -0
  128. data/tests/handlers/tc_Whois.rb +61 -0
  129. data/tests/models/tc_Auth.rb +34 -0
  130. data/tests/models/tc_Channel.rb +52 -0
  131. data/tests/models/tc_Config.rb +19 -0
  132. data/tests/models/tc_Nick.rb +142 -0
  133. data/tests/models/tc_NickMode.rb +40 -0
  134. data/tests/models/tc_Setting.rb +21 -0
  135. data/tests/models/tc_Signature.rb +14 -0
  136. data/tests/run_tests.rb +4 -0
  137. metadata +284 -212
  138. data/README +0 -36
  139. data/lib/mod_spox/Cache.rb +0 -57
  140. data/lib/mod_spox/Monitors.rb +0 -84
  141. data/lib/mod_spox/Pool.rb +0 -164
  142. data/lib/mod_spox/models/AuthGroup.rb +0 -16
  143. data/lib/mod_spox/models/ChannelMode.rb +0 -14
  144. data/lib/mod_spox/models/NickChannel.rb +0 -45
  145. data/lib/mod_spox/models/NickGroup.rb +0 -16
@@ -7,18 +7,16 @@ class Karma < ModSpox::Plugin
7
7
  Datatype::Karma.create_table unless Datatype::Karma.table_exists?
8
8
  Datatype::Alias.create_table unless Datatype::Alias.table_exists?
9
9
  alias_group = Models::Group.find_or_create(:name => 'alias')
10
- Models::Signature.find_or_create(:signature => 'karma (?!(fight|alias|dealias|aliases|reset) (\S+|\(.+?\)) ?(\S+|\(.+?\))?$)(\S+|\(.+?\))', :plugin => name, :method => 'score', :description => 'Returns karma for given thing').params = [:crap, :crap2, :crap3, :thing]
11
- Models::Signature.find_or_create(:signature => 'karma reset (\S+|\(.+?\))', :plugin => name, :method => 'reset',
12
- :group_id => Models::Group.filter(:name => 'admin').first.pk, :description => 'Reset a karma score').params = [:thing]
13
- Models::Signature.find_or_create(:signature => 'karma alias (\S+|\(.+?\)) (\S+|\(.+?\))', :plugin => name, :method => 'aka',
14
- :group_id => alias_group.pk, :description => 'Alias a karma object to another karma object').params = [:thing, :thang]
15
- Models::Signature.find_or_create(:signature => 'karma dealias (\S+|\(.+?\)) (\S+|\(.+?\))', :plugin => name, :method => 'dealias',
16
- :group_id => alias_group.pk, :description => 'Remove a karma alias').params = [:thing, :otherthing]
17
- Models::Signature.find_or_create(:signature => 'karma aliases (\S+|\(.+?\))', :plugin => name, :method => 'show_aliases',
18
- :description => 'Show all aliases for given thing').params = [:thing]
19
- Models::Signature.find_or_create(:signature => 'karma fight (\S+|\(.+?\)) (\S+|\(.+?\))', :plugin => name, :method => 'fight',
20
- :description => 'Make two karma objects fight').params = [:thing, :thang]
10
+ add_sig(:sig => 'karma (?!topten|bottomten$)(?!(fight|alias|dealias|aliases|reset) (\S+|\(.+?\)) ?(\S+|\(.+?\))?$)(\S+|\(.+?\))', :method => :score, :desc => 'Returns karma for given thing', :params => [:crap, :crap2, :crap3, :thing])
11
+ add_sig(:sig => 'karma reset (\S+|\(.+?\))', :method => :reset, :group => Models::Group.filter(:name => 'admin').first, :desc => 'Reset a karma score', :params => [:thing])
12
+ add_sig(:sig => 'karma alias (\S+|\(.+?\)) (\S+|\(.+?\))', :method => :aka, :group => alias_group, :desc => 'Alias a karma object to another karma object', :params => [:thing, :thang])
13
+ add_sig(:sig => 'karma dealias (\S+|\(.+?\)) (\S+|\(.+?\))', :method => :dealias, :group => alias_group, :desc => 'Remove a karma alias', :params => [:thing, :otherthing])
14
+ add_sig(:sig => 'karma aliases (\S+|\(.+?\))', :method => :show_aliases, :desc => 'Show all aliases for given thing', :params => [:thing])
15
+ add_sig(:sig => 'karma fight (\S+|\(.+?\)) (\S+|\(.+?\))', :method => :fight, :desc => 'Make two karma objects fight', :params => [:thing, :thang])
21
16
  add_sig(:sig => 'antikarma (\S+|\(.+?\))', :method => :antikarma, :desc => 'Show things antikarma', :params => [:thing])
17
+ add_sig(:sig => 'antikarma fight (\S+|\(.+?\)) (\S+|\(.+?\))', :method => :antifight, :desc => 'Make two antikarma objects fight', :params => [:thing, :thang])
18
+ add_sig(:sig => 'karma topten', :method => :topten, :desc => 'Show top ten highest karma objects')
19
+ add_sig(:sig => 'karma bottomten', :method => :bottomten, :desc => 'Show bottom ten lowest karma objects')
22
20
  @pipeline.hook(self, :check, :Incoming_Privmsg)
23
21
  @thing_maxlen = 32
24
22
  @karma_regex = /(\(.{1,#@thing_maxlen}?\)|\S{1,#@thing_maxlen})([+-]{2})(?:\s|$)/
@@ -32,10 +30,14 @@ class Karma < ModSpox::Plugin
32
30
 
33
31
  def check(message)
34
32
  if(message.is_public?)
33
+ @dupes = {}
35
34
  message.message.scan(@karma_regex) do |match|
36
35
  thing, adj = match
37
36
  thing.downcase!
38
37
  thing = thing[1..-2] if thing[0..0] == '(' && thing[-1..-1] == ')'
38
+ next if @dupes.has_key?(thing) && @dupes[thing].include?(adj)
39
+ @dupes[thing] = [] unless @dupes[thing]
40
+ @dupes[thing] << adj
39
41
  adj = adj == '++' ? +1 : -1
40
42
  things = [thing]
41
43
  karma = Datatype::Karma.find_or_create(:thing => thing, :channel_id => message.target.pk)
@@ -68,6 +70,32 @@ class Karma < ModSpox::Plugin
68
70
  @pipeline << Messages::Internal::TimerAdd.new(self, rand(5) + 1, nil, true){ egg(params[:thing].downcase, message) }
69
71
  end
70
72
  end
73
+
74
+ def bottomten(m, params)
75
+ set = Datatype::Karma.filter(:channel_id => m.target.pk).order(:thing.asc).order(:score.asc).limit(10)
76
+ if(set.count > 0)
77
+ output = []
78
+ set.each do |thing|
79
+ output << "#{thing.thing} (#{thing.score.to_s.slice(0) == '-' ? '' : '+'}#{thing.score})"
80
+ end
81
+ reply m.replyto, "\2Karma bottom ten:\2 #{output.join(', ')}"
82
+ else
83
+ error m.replyto, 'No karma objects stored'
84
+ end
85
+ end
86
+
87
+ def topten(m, params)
88
+ set = Datatype::Karma.filter(:channel_id => m.target.pk).order(:thing.asc).order(:score.desc).limit(10)
89
+ if(set.count > 0)
90
+ output = []
91
+ set.each do |thing|
92
+ output << "#{thing.thing} (#{thing.score.to_s.slice(0) == '-' ? '' : '+'}#{thing.score})"
93
+ end
94
+ reply m.replyto, "\2Karma topten:\2 #{output.join(', ')}"
95
+ else
96
+ error m.replyto, 'No karma objects stored'
97
+ end
98
+ end
71
99
 
72
100
  def antikarma(message, params)
73
101
  return unless message.is_public?
@@ -89,7 +117,7 @@ class Karma < ModSpox::Plugin
89
117
  return unless message.is_public?
90
118
  karma = Datatype::Karma.filter(:thing => sthing, :channel_id => message.target.pk).first
91
119
  if(karma)
92
- karma.update_with_params(:score => 0)
120
+ karma.update(:score => 0)
93
121
  @pipeline << Privmsg.new(message.replyto, "Karma for \2#{karma.thing}\2 has been reset")
94
122
  else
95
123
  @pipeline << Privmsg.new(message.replyto, "\2Error:\2 #{sthing} has no karma")
@@ -108,12 +136,34 @@ class Karma < ModSpox::Plugin
108
136
  winner = thing_score > thang_score ? thing : thang
109
137
  loser = thing_score > thang_score ? thang : thing
110
138
  distance = (thing_score - thang_score).abs
111
- output = "\2KARMA FIGHT RESULTS:\2 "
139
+ output = ["\2KARMA FIGHT RESULTS:\2 "]
112
140
  if(distance > 0)
113
- reply message.replyto, "\2#{winner}\2 #{winner[-1] == 's' || winner[-1] == 115 ? 'have' : 'has'} beaten \2#{loser}\2 #{distance > 50 ? 'like a redheaded step child' : ''} (+#{distance} points)"
141
+ output << "\2#{winner}\2 #{winner[-1] == 's' || winner[-1] == 115 ? 'have' : 'has'} beaten \2#{loser}\2 #{distance > 50 ? 'like a redheaded step child' : ''} (+#{distance} points)"
114
142
  else
115
- reply message.replyto, "\2#{winner}\2 #{winner[-1] == 's' || winner[-1] == 115 ? 'have' : 'has'} tied \2#{loser}\2"
143
+ output << "\2#{winner}\2 #{winner[-1] == 's' || winner[-1] == 115 ? 'have' : 'has'} tied \2#{loser}\2"
116
144
  end
145
+ reply message.replyto, output
146
+ end
147
+
148
+ def antifight(message, params)
149
+ thing = params[:thing]
150
+ thang = params[:thang]
151
+ thing = thing[1..-2] if thing[0..0] == '(' && thing[-1..-1] == ')'
152
+ thang = thang[1..-2] if thang[0..0] == '(' && thang[-1..-1] == ')'
153
+ rthing = Datatype::Karma.find_or_create(:thing => thing.downcase, :channel_id => message.target.pk)
154
+ rthang = Datatype::Karma.find_or_create(:thing => thang.downcase, :channel_id => message.target.pk)
155
+ thing_score = Datatype::Alias.score_object(rthing.pk) * -1
156
+ thang_score = Datatype::Alias.score_object(rthang.pk) * -1
157
+ winner = thing_score > thang_score ? thing : thang
158
+ loser = thing_score > thang_score ? thang : thing
159
+ distance = (thing_score - thang_score).abs
160
+ output = ["\2ANTI-KARMA FIGHT RESULTS:\2 "]
161
+ if(distance > 0)
162
+ output << "\2#{winner}\2 #{winner[-1] == 's' || winner[-1] == 115 ? 'have' : 'has'} beaten \2#{loser}\2 #{distance > 50 ? 'like a redheaded step child' : ''} (+#{distance} points)"
163
+ else
164
+ output << "\2#{winner}\2 #{winner[-1] == 's' || winner[-1] == 115 ? 'have' : 'has'} tied \2#{loser}\2"
165
+ end
166
+ reply message.replyto, output
117
167
  end
118
168
 
119
169
  def aka(message, params)
@@ -140,7 +190,7 @@ class Karma < ModSpox::Plugin
140
190
  otherthing = Datatype::Karma.filter(:thing => sotherthing, :channel_id => message.target.pk).first
141
191
  if(thing && otherthing)
142
192
  set = Datatype::Alias.filter('(thing_id = ? AND aka_id = ?) OR (thing_id = ? AND aka_id = ?)', thing.pk, otherthing.pk, otherthing.pk, thing.pk)
143
- if(set.size < 1)
193
+ if(set.count < 1)
144
194
  reply message.replyto, "\2Error:\2 No alias found between #{thing.thing} and #{otherthing.thing}"
145
195
  else
146
196
  set.destroy
@@ -155,18 +205,22 @@ class Karma < ModSpox::Plugin
155
205
  thing = params[:thing].downcase
156
206
  thing = thing[1..-2] if thing[0..0] == '(' && thing[-1..-1] == ')'
157
207
  thing = Datatype::Karma.filter(:thing => thing, :channel_id => message.target.pk).first
158
- if(thing)
159
- things = []
160
- Datatype::Alias.get_aliases(thing.pk).each do |id|
161
- things << Datatype::Karma[id].thing
162
- end
163
- if(things.empty?)
164
- reply message.replyto, "#{thing.thing} is not currently aliased"
208
+ begin
209
+ if(thing)
210
+ things = []
211
+ Datatype::Alias.get_aliases(thing.pk).each do |id|
212
+ things << Datatype::Karma[id].thing
213
+ end
214
+ if(things.empty?)
215
+ reply message.replyto, "#{thing.thing} is not currently aliased"
216
+ else
217
+ reply message.replyto, "#{thing.thing} is currently aliased to: #{things.sort.join(', ')}"
218
+ end
165
219
  else
166
- reply message.replyto, "#{thing.thing} is currently aliased to: #{things.join(', ')}"
220
+ reply message.replyto, "\2Error:\2 #{params[:thing]} has never been used and has no aliases"
167
221
  end
168
- else
169
- reply message.replyto, "\2Error:\2 #{thing.thing} has never been used and has no aliases"
222
+ rescue Object
223
+ error message.replyto, "No aliases found"
170
224
  end
171
225
  end
172
226
 
@@ -186,9 +240,7 @@ class Karma < ModSpox::Plugin
186
240
  index [:thing, :channel_id], :unique => true
187
241
  end
188
242
 
189
- def channel
190
- ModSpox::Models::Channel[channel_id]
191
- end
243
+ many_to_one :channel, :class => ModSpox::Models::Channel
192
244
  end
193
245
  class Alias < Sequel::Model
194
246
  set_schema do
@@ -22,8 +22,8 @@ class ChatLogger < ModSpox::Plugin
22
22
  @pipeline.hook(self, :log_privmsg, :Incoming_Notice)
23
23
  @pipeline.hook(self, :log_outpriv, :Outgoing_Privmsg)
24
24
  @pipeline.hook(self, :log_outpriv, :Outgoing_Notice)
25
- Signature.find_or_create(:signature => 'seen (\S+)', :plugin => name, :method => 'seen', :description => 'Report last sighting of nick').params = [:nick]
26
- Signature.find_or_create(:signature => 'lastspoke (\S+)', :plugin => name, :method => 'spoke', :description => 'Report last time nick spoke').params = [:nick]
25
+ add_sig(:sig => 'seen (\S+)', :method => :seen, :desc => 'Report last sighting of nick', :params => [:nick])
26
+ add_sig(:sig => 'lastspoke (\S+)', :method => :spoke, :desc => 'Report last time nick spoke', :params => [:nick])
27
27
  end
28
28
 
29
29
  def log_outpriv(message)
@@ -11,8 +11,7 @@ class LolSpeak < ModSpox::Plugin
11
11
  Logger.warn('Error: This plugins requires the lolspeak gem. Please install gem and reload plugin.')
12
12
  raise BotException.new("Failed to initialize plugin. Missing lolspeak gem.")
13
13
  end
14
- Signature.find_or_create(:signature => 'lolspeak (.+)', :plugin => name, :method => 'translate',
15
- :description => 'Translate text to lolspeak').params = [:text]
14
+ add_sig(:sig => 'lolspeak (.+)', :method => :translate, :desc => 'Translate text to lolspeak', :params => [:text])
16
15
  end
17
16
 
18
17
  def translate(message, params)
@@ -6,7 +6,7 @@ class PhpCli < ModSpox::Plugin
6
6
 
7
7
  def initialize(pipeline)
8
8
  super(pipeline)
9
- @path = Config[:plugin_directory] + '/phpcli'
9
+ @path = Config.val(:plugin_directory) + '/phpcli'
10
10
  @botini = @path + '/bot.ini'
11
11
  unless(File.directory?(@path))
12
12
  FileUtils.mkdir_p(@path)
@@ -18,12 +18,20 @@ class PhpCli < ModSpox::Plugin
18
18
  ini.write($ini)
19
19
  ini.close
20
20
  end
21
+ PhpFunction.create_table unless PhpFunction.table_exists?
21
22
  php = Group.find_or_create(:name => 'php')
23
+ phpfunc = Group.find_or_create(:name => 'phpfunc')
22
24
  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]
25
+ add_sig(:sig => 'php (on|off)', :method => :set_channel, :group => admin, :desc => 'Add or remove channel from allowing PHP command', :params => [:action])
26
+ add_sig(:sig => 'php (?!on|off)(.+)', :method => :execute_php, :group => php, :desc => 'Execute PHP code', :params => [:code])
27
+ add_sig(:sig => 'phpq (?!on|off)(.+)', :method => :quiet_php, :group => php, :params => [:code], :desc => 'Execute PHP quietly')
28
+ add_sig(:sig => 'pf add (.+)', :method => :add, :params => [:function], :group => phpfunc, :desc => 'Add a custom PHP function')
29
+ add_sig(:sig => 'pf remove (\d+)', :method => :remove, :params => [:func_id], :group => phpfunc, :desc => 'Remove a custom PHP function')
30
+ add_sig(:sig => 'pf list', :method => :list, :desc => 'List custom PHP functions')
31
+ add_sig(:sig => 'pf edit (.+)', :method => :edit, :params => [:function], :group => phpfunc, :desc => 'Overwrite existing custom PHP function')
32
+ add_sig(:sig => 'pf show (\d+)', :method => :show, :params => [:func_id], :group => phpfunc, :desc => 'Show given PHP function source')
33
+ @customfuncs = []
34
+ populate_customs
27
35
  @channels = Setting.filter(:name => 'phpcli').first
28
36
  @channels = @channels.nil? ? [] : @channels.value
29
37
  end
@@ -51,11 +59,15 @@ class PhpCli < ModSpox::Plugin
51
59
  end
52
60
  end
53
61
 
54
- def execute_php(message, params)
62
+ def quiet_php(message, params)
63
+ execute_php(message, params, true)
64
+ end
65
+
66
+ def execute_php(message, params, shh=false)
55
67
  return unless @channels.include?(message.target.pk)
56
68
  filepath = @path + "/#{rand(99999)}.bot.php"
57
69
  file = File.open(filepath, 'w')
58
- file.write("<? $_SERVER = $_ENV = array(); #{params[:code]} ?>")
70
+ file.write("<? $_SERVER = $_ENV = $GLOBALS = array(); #{@customfuncs.join(' ')} #{params[:code]} ?>")
59
71
  file.close
60
72
  begin
61
73
  output = Helpers.safe_exec("php -c #{@path}/bot.ini -d max_execution_time=10 #{filepath} 2>&1 | head -n 4")
@@ -75,7 +87,7 @@ class PhpCli < ModSpox::Plugin
75
87
  reply message.replyto, "PHP #{type}: "+warning
76
88
  end
77
89
  if(warning.nil? || type !~ /(Fatal|Parse)/)
78
- reply message.replyto, "Result: "+output
90
+ reply message.replyto, "#{shh ? '' : 'Result: '}"+output
79
91
  end
80
92
  File.delete(filepath)
81
93
  rescue Timeout::Error => boom
@@ -85,9 +97,127 @@ class PhpCli < ModSpox::Plugin
85
97
  File.delete(filepath)
86
98
  end
87
99
  end
100
+
101
+ def add(m, params)
102
+ if(params[:function].scan(/function\s+([^\(]+)\(/).size > 1)
103
+ error m.replyto, 'Only one function can be added at a time'
104
+ return
105
+ end
106
+ if(params[:function] =~ /^function\s+([^\(]+)\(/)
107
+ name = $1.downcase
108
+ f = PhpFunction.filter(:name => name).first
109
+ unless(f)
110
+ begin
111
+ parse(params[:function])
112
+ save(params[:function], name, m.source)
113
+ information m.replyto, "New function \2#{name}\2 added to custom PHP functions"
114
+ populate_customs
115
+ rescue Object => boom
116
+ error m.replyto, "Failed to add function #{name}. Error: #{boom}"
117
+ end
118
+ else
119
+ error m.replyto, "Function with name: #{name} already exists"
120
+ end
121
+ else
122
+ error m.replyto, "Function is not in proper format"
123
+ end
124
+ end
125
+
126
+ def remove(m, params)
127
+ f = PhpFunction[params[:func_id].to_i]
128
+ if(f)
129
+ name = f.name
130
+ f.destroy
131
+ information m.replyto, "Function \2#{name}\2 has been removed"
132
+ populate_customs
133
+ else
134
+ error m.replyto, "Failed to locate function with ID: #{params[:func_id]}"
135
+ end
136
+ end
137
+
138
+ def list(m, params)
139
+ output = ["\2Custom PHP functions:\2"]
140
+ PhpFunction.all.each do |f|
141
+ output << "\2ID:\2 #{f.pk} \2Name:\2 #{f.name} \2Author:\2 #{f.nick.nick} \2Added:\2 #{f.added.strftime("%Y/%m/%d-%H:%M:%S")}"
142
+ end
143
+ reply m.replyto, output
144
+ end
145
+
146
+ def edit(m, params)
147
+ if(params[:function] =~ /^function\s+([^\(]+)\(/)
148
+ name = $1.downcase
149
+ begin
150
+ parse(params[:function])
151
+ save(params[:function], name, m.source)
152
+ information m.replyto, "New function \2#{name}\2 added to custom PHP functions"
153
+ populate_customs
154
+ rescue Object => boom
155
+ error m.replyto, "Failed to add function #{name}. Error: #{boom}"
156
+ end
157
+ else
158
+ error m.replyto, "Function is not in proper format"
159
+ end
160
+ end
161
+
162
+ def show(m, params)
163
+ f = PhpFunction[params[:func_id].to_i]
164
+ if(f)
165
+ reply m.replyto, ["Source for function \2#{f.name}\2:", f.php_function]
166
+ else
167
+ error m.replyto, "Failed to find custom PHP function with ID: #{params[:func_id]}"
168
+ end
169
+ end
170
+
171
+ def parse(func)
172
+ filepath = @path + "/#{rand(99999)}.bot.php"
173
+ file = File.open(filepath, 'w')
174
+ file.write("<? #{func} ?>")
175
+ file.close
176
+ output = Helpers.safe_exec("php -l #{filepath} 2>&1 | head -n 4").strip.gsub(/\s{2,}/, ' ').gsub(/[\r\n]+/, ' ')
177
+ File.delete(filepath)
178
+ if(output =~ /(Parse error.+) in/)
179
+ raise "#{$1}"
180
+ end
181
+ end
182
+
183
+ def save(func, name, nick)
184
+ f = PhpFunction.filter(:name => name).first
185
+ f = PhpFunction.new unless f
186
+ f.name = name
187
+ f.php_function = func
188
+ f.added = ::Time.now
189
+ f.nick = nick
190
+ f.save
191
+ end
192
+
193
+ def populate_customs
194
+ @customfuncs = []
195
+ PhpFunction.all.each{|f| @customfuncs << f.php_function }
196
+ end
88
197
 
89
198
  class NoInterpreter < Exceptions::BotException
90
199
  end
200
+
201
+ class PhpFunction < Sequel::Model
202
+ set_schema do
203
+ text :php_function, :null => false
204
+ varchar :name, :null => false, :unique => true
205
+ timestamp :added, :null => false
206
+ foreign_key :nick_id, :null => false
207
+ primary_key :id
208
+ end
209
+
210
+ Sequel::Model.plugin :serialization, :marshal, :php_function
211
+ # serialize(:php_function, :format => :marshal)
212
+
213
+ def nick
214
+ Models::Nick[nick_id]
215
+ end
216
+
217
+ def nick=(n)
218
+ values[:nick_id] = n.pk
219
+ end
220
+ end
91
221
 
92
222
  end
93
223
 
@@ -12,8 +12,8 @@ class PhpFuncLookup < ModSpox::Plugin
12
12
  def initialize(pipeline)
13
13
  super(pipeline)
14
14
  setup_setting
15
- @path = Setting[:phpfunc][:directory]
16
- @trigger = Setting[:phpfunc][:trigger]
15
+ @path = Setting.val(:phpfunc)[:directory]
16
+ @trigger = Setting.val(:phpfunc)[:trigger]
17
17
  @manual = "#{@path}/html"
18
18
  @classlist = []
19
19
  fetch_manual unless File.exists?("#{@path}/manual.tar.gz")
@@ -65,15 +65,12 @@ class PhpFuncLookup < ModSpox::Plugin
65
65
  "->" => [ "?", "Object member accessor thingy","" ],
66
66
  }
67
67
  admin = Group.filter(:name => 'admin').first
68
- Signature.find_or_create(:signature => 'pfunc (\S+)', :plugin => name, :method => 'phpfunc', :description => 'Lookup PHP function').params = [:name]
69
- Signature.find_or_create(:signature => 'fetch php manual', :plugin => name, :method => 'fetch', :group_id => admin.pk,
70
- :description => 'Download and extract PHP manual')
71
- Signature.find_or_create(:signature => 'pfunc trigger (\S+)', :plugin => name, :method => 'set_trigger', :group_id => admin.pk,
72
- :description => 'Set the trigger for auto-lookups').params = [:trigger]
73
- Signature.find_or_create(:signature => 'pfunc show trigger', :plugin => name, :method => 'show_trigger', :description => 'Show current trigger')
74
- Signature.find_or_create(:signature => 'pfunc (add|remove) (\S+)', :plugin => name, :method => 'set_channels', :group_id => admin.pk,
75
- :description => 'Add or remove channels from auto-lookups').params = [:action, :channel]
76
- Signature.find_or_create(:signature => 'pfunc show channels', :plugin => name, :method => 'list_channels', :description => 'Show channels with auto lookup enabled')
68
+ add_sig(:sig => 'pfunc (\S+)', :method => :phpfunc, :desc => 'Lookup PHP function', :params => [:name])
69
+ add_sig(:sig => 'fetch php manual', :method => :fetch, :group => admin, :desc => 'Download and extract PHP manual')
70
+ add_sig(:sig => 'pfunc trigger (\S+)', :method => :set_trigger, :group => admin, :desc => 'Set the trigger for auto-lookups', :params => [:trigger])
71
+ add_sig(:sig => 'pfunc show trigger', :method => :show_trigger, :desc => 'Show current trigger')
72
+ add_sig(:sig => 'pfunc (add|remove) (\S+)', :method => :set_channels, :group => admin, :desc => 'Add or remove channels from auto-lookups', :params => [:action, :channel])
73
+ add_sig(:sig => 'pfunc show channels', :method => :list_channels, :desc => 'Show channels with auto lookup enabled')
77
74
  @pipeline.hook(self, :listen, :Incoming_Privmsg)
78
75
  populate_classes
79
76
  end
@@ -100,7 +97,7 @@ class PhpFuncLookup < ModSpox::Plugin
100
97
  end
101
98
 
102
99
  def listen(m)
103
- if(m.target.is_a?(Channel) && Setting[:phpfunc][:channels].include?(m.target.pk))
100
+ if(m.target.is_a?(Channel) && Setting.val(:phpfunc)[:channels].include?(m.target.pk))
104
101
  if m.message =~ /^#{Regexp.escape(@trigger)}(\S+)$/
105
102
  phpfunc(m, {:name => $1})
106
103
  end
@@ -108,7 +105,7 @@ class PhpFuncLookup < ModSpox::Plugin
108
105
  end
109
106
 
110
107
  def set_trigger(message, params)
111
- vals = Setting[:phpfunc]
108
+ vals = Setting.val(:phpfunc)
112
109
  vals[:trigger] = params[:trigger]
113
110
  Setting.filter(:name => 'phpfunc').first.value = vals
114
111
  @trigger = params[:trigger]
@@ -118,12 +115,12 @@ class PhpFuncLookup < ModSpox::Plugin
118
115
  def set_channels(message, params)
119
116
  channel = Channel.filter(:name => params[:channel]).first
120
117
  if(channel)
121
- vals = Setting[:phpfunc]
118
+ vals = Setting.val(:phpfunc)
122
119
  if(params[:action] == 'add')
123
- vals[:channels] << channel.pk unless Setting[:phpfunc][:channels].include?(channel.pk)
120
+ vals[:channels] << channel.pk unless Setting.val(:phpfunc)[:channels].include?(channel.pk)
124
121
  reply message.replyto, "Channel \2#{params[:channel]}\2 added to PHP auto lookup"
125
122
  else
126
- vals[:channels].delete(channel.pk) if Setting[:phpfunc][:channels].include?(channel.pk)
123
+ vals[:channels].delete(channel.pk) if Setting.val(:phpfunc)[:channels].include?(channel.pk)
127
124
  reply message.replyto, "Channel \2#{params[:channel]}\2 has been removed from PHP auto lookup"
128
125
  end
129
126
  Setting.filter(:name => 'phpfunc').first.value = vals
@@ -133,9 +130,9 @@ class PhpFuncLookup < ModSpox::Plugin
133
130
  end
134
131
 
135
132
  def list_channels(message, params)
136
- if(Setting[:phpfunc][:channels].size > 0)
133
+ if(Setting.val(:phpfunc)[:channels].size > 0)
137
134
  chans = []
138
- Setting[:phpfunc][:channels].each do |id|
135
+ Setting.val(:phpfunc)[:channels].each do |id|
139
136
  chans << Channel[id].name
140
137
  end
141
138
  reply message.replyto, "PHP auto lookup enabled channels: #{chans.join(', ')}"
@@ -145,7 +142,7 @@ class PhpFuncLookup < ModSpox::Plugin
145
142
  end
146
143
 
147
144
  def show_trigger(message, p)
148
- reply message.replyto, "PHP auto lookup trigger: \2#{Setting[:phpfunc][:trigger]}\2"
145
+ reply message.replyto, "PHP auto lookup trigger: \2#{Setting.val(:phpfunc)[:trigger]}\2"
149
146
  end
150
147
 
151
148
  private
@@ -179,10 +176,10 @@ class PhpFuncLookup < ModSpox::Plugin
179
176
  s = Setting.filter(:name => 'phpfunc').first
180
177
  unless(s)
181
178
  s = Setting.find_or_create(:name => 'phpfunc')
182
- s.value = {:directory => Config[:plugin_directory] + '/php', :trigger => '@', :channels => []}
179
+ s.value = {:directory => Config.val(:plugin_directory) + '/php', :trigger => '@', :channels => []}
183
180
  end
184
- unless(File.directory?(Setting[:phpfunc][:directory]))
185
- FileUtils.mkdir_p(Setting[:phpfunc][:directory])
181
+ unless(File.directory?(Setting.val(:phpfunc)[:directory]))
182
+ FileUtils.mkdir_p(Setting.val(:phpfunc)[:directory])
186
183
  end
187
184
  end
188
185
 
@@ -242,7 +239,7 @@ class PhpFuncLookup < ModSpox::Plugin
242
239
  desc = CGI::unescapeHTML(desc.gsub(/<.+?>/, ' ').gsub(/[\s]+/, ' '))
243
240
  output = [versions]
244
241
  output << "\2#{proto}\2"
245
- output << desc
242
+ output << Helpers.convert_entities(desc)
246
243
  output << "http://www.php.net/manual/en/#{filename.gsub(/\.html$/, '.php')}"
247
244
  reply m.replyto, output
248
245
  end