butler 1.8.2 → 1.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/Rakefile +1 -1
  2. data/bin/botcontrol +1 -1
  3. data/data/butler/dialogs/create_config.rb +2 -2
  4. data/data/butler/dialogs/quickcreate.rb +6 -4
  5. data/data/butler/dialogs/uninstall.rb +4 -3
  6. data/data/butler/plugins/core/access.rb +10 -10
  7. data/data/butler/plugins/dev/bleakhouse.rb +19 -8
  8. data/data/butler/plugins/operator/deop.rb +10 -1
  9. data/data/butler/plugins/operator/devoice.rb +9 -0
  10. data/data/butler/plugins/operator/limit.rb +12 -0
  11. data/data/butler/plugins/operator/op.rb +9 -0
  12. data/data/butler/plugins/operator/voice.rb +10 -0
  13. data/data/butler/plugins/util/calculator.rb +11 -0
  14. data/data/butler/services/org.rubyforge.butler/calculator/1/calculator.rb +68 -0
  15. data/data/butler/services/org.rubyforge.butler/log/1/service.rb +198 -0
  16. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/acknowledge.yaml +8 -0
  17. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/gratitude.yaml +3 -0
  18. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/hello.yaml +6 -0
  19. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/ignorance.yaml +7 -0
  20. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/ignorance_about.yaml +3 -0
  21. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/insult.yaml +3 -0
  22. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/rejection.yaml +12 -0
  23. data/data/butler/services/org.rubyforge.butler/strings/1/service.rb +50 -0
  24. data/lib/access.rb +6 -3
  25. data/lib/access/privilege.rb +9 -78
  26. data/lib/access/privilegelist.rb +75 -0
  27. data/lib/access/role.rb +14 -94
  28. data/lib/access/role/base.rb +40 -0
  29. data/lib/access/rolelist.rb +99 -0
  30. data/lib/access/savable.rb +6 -3
  31. data/lib/access/user.rb +21 -19
  32. data/lib/access/version.rb +17 -0
  33. data/lib/access/yamlbase.rb +64 -48
  34. data/lib/butler.rb +1 -0
  35. data/lib/butler/bot.rb +8 -2
  36. data/lib/butler/control.rb +3 -1
  37. data/lib/butler/initialvalues.rb +1 -1
  38. data/lib/butler/irc/client.rb +6 -0
  39. data/lib/butler/irc/message.rb +14 -9
  40. data/lib/butler/irc/parser.rb +8 -5
  41. data/lib/butler/irc/parser/generic.rb +33 -1
  42. data/lib/butler/irc/parser/rfc2812.rb +5 -2
  43. data/lib/butler/plugin.rb +22 -2
  44. data/lib/butler/plugins.rb +2 -7
  45. data/lib/butler/service.rb +73 -0
  46. data/lib/butler/services.rb +65 -0
  47. data/lib/butler/version.rb +1 -1
  48. data/lib/ruby/array/random.rb +17 -0
  49. data/lib/ruby/string/camelcase.rb +14 -0
  50. data/test/test_access.rb +164 -59
  51. data/test/test_access/privilege/banners.statistics.yaml +3 -0
  52. data/test/test_access/privilege/banners.yaml +3 -0
  53. data/test/test_access/privilege/news.create.yaml +3 -0
  54. data/test/test_access/privilege/news.delete.yaml +3 -0
  55. data/test/test_access/privilege/news.edit.yaml +3 -0
  56. data/test/test_access/privilege/news.read.yaml +3 -0
  57. data/test/test_access/privilege/news.yaml +3 -0
  58. data/test/test_access/privilege/paid_content.yaml +3 -0
  59. data/test/test_access/privilege/statistics.ftp.yaml +3 -0
  60. data/test/test_access/privilege/statistics.web.yaml +3 -0
  61. data/test/test_access/privilege/statistics.yaml +3 -0
  62. data/test/test_access/role/chiefeditor.yaml +7 -0
  63. data/test/test_access/role/editor.yaml +9 -0
  64. data/test/test_access/user/test.yaml +12 -0
  65. metadata +51 -5
  66. data/data/butler/plugins/core/user.rb +0 -166
  67. data/data/butler/plugins/dev/onhandlers.rb +0 -93
  68. data/data/butler/plugins/service/log.rb +0 -183
data/Rakefile CHANGED
@@ -62,7 +62,7 @@ LOCAL_PATHS['plugins'] = "#{LOCAL_PATHS['data']}/plugins"
62
62
  #LOCAL_PATHS['config'] = "#{Config::CONFIG['sysconfdir']}/butler"
63
63
  LOCAL_PATHS['pids'] = "#{Config::CONFIG['localstatedir']}/run/butler"
64
64
  CHANGES = "See CHANGELOG.txt"
65
- DESC = "Butler 1.8.2, 3rd alpha release of butler2."
65
+ DESC = "Butler 1.8.3, 4th alpha release of butler2."
66
66
 
67
67
  GemSpec = Gem::Specification.new do |s|
68
68
  s.homepage = HOMEPATH
data/bin/botcontrol CHANGED
@@ -249,7 +249,7 @@ case command
249
249
  )
250
250
 
251
251
  when "uninstall"
252
- botcontrol.discuss("uninstall", false, :path => path, :config => config)
252
+ botcontrol.discuss("uninstall", false)
253
253
 
254
254
  else
255
255
  botcontrol.discuss("unknown_command", false, :command => command)
@@ -1,7 +1,7 @@
1
1
  begin
2
- user = variables.user || variables.botcontrol.user
2
+ user = variables.user || variables.botcontrol.user # can be nil
3
3
  variables.botcontrol.configure_user(user) unless variables.botcontrol.configured?(user)
4
- Butler.path = variables.botcontrol.butler_path
4
+ Butler.path = variables.botcontrol.butler_path(user)
5
5
  rescue Errno::EACCES
6
6
  say :cant_configure, :user => user, :app => $0
7
7
  exit
@@ -35,15 +35,17 @@ user = ask(:user, nil, String, :min => 3)
35
35
  pass = ask(:pass, nil, String, :min => 3)
36
36
  admin = bot.access.user.create(user, pass, nil, true, :active => true)
37
37
  role = bot.access.role.create("default_role", "This role is applied to every user")
38
- role.privileges.add(%w[
38
+ %w[
39
39
  plugin/public
40
40
  plugin/core/login
41
41
  plugin/core/logout
42
42
  plugin/core/help
43
43
  plugin/core/usage
44
- ])
45
- user = bot.access.user.create("default_user", nil, nil, false, :active => true)
46
- user.roles.add(%w[default_role])
44
+ ].each { |priv| role.privileges.add_oid(priv) }
45
+ default_user = bot.access.user.create("default_user", nil, nil, false, :active => true)
46
+ %w[default_role].each { |role|
47
+ default_user.roles.add_oid(role)
48
+ }
47
49
 
48
50
  puts
49
51
  say(:how_to_start, :botname => nick, :user => user, :pass => pass)
@@ -1,7 +1,8 @@
1
- unless ENV["SUDO_USER"]
1
+ # botcontrol must run as sudo in order to uninstall all stuff
2
+ unless ENV["SUDO_USER"] || Process.uid.zero? # *NIX
2
3
  say(:must_run_as_sudo)
3
4
  else
4
- variables.config.users.sort.each { |user, conf|
5
+ variables.botcontrol.config[:users].sort.each { |user, conf|
5
6
  if prompt(:uninstall_user, false, :name => user) then
6
7
  FileUtils.rm_r(conf[:bots])
7
8
  FileUtils.rm_r(conf[:run])
@@ -12,6 +13,6 @@ else
12
13
  else
13
14
  say(:uninstalling_botcontrol)
14
15
  File.delete($0)
15
- File.delete(variables.path.config)
16
+ File.delete(variables.botcontrol.path[:config])
16
17
  end
17
18
  end
@@ -35,7 +35,7 @@ def on_create_user(params)
35
35
  false,
36
36
  :active => !!params.active
37
37
  )
38
- user[params.username].roles.add("default_role")
38
+ user[params.username].roles.add_oid("default_role")
39
39
  answer(:user_created, :username => params.username)
40
40
  end
41
41
  end
@@ -44,7 +44,7 @@ end
44
44
  def on_delete_user(params)
45
45
  params.username.downcase!
46
46
  if user.exists?(params.username) then
47
- user.delete(params.username)
47
+ user.delete_oid(params.username)
48
48
  answer(:user_deleted, :username => params.username)
49
49
  else
50
50
  answer(:user_doesnt_exist, :username => params.username)
@@ -61,7 +61,7 @@ end
61
61
  def on_delete_role(params)
62
62
  params.rolename.downcase!
63
63
  return answer(:role_doesnt_exist, :rolename => params.rolename) unless role.exists?(params.rolename)
64
- role.delete(params.rolename)
64
+ role.delete_oid(params.rolename)
65
65
  answer(:role_deleted, :rolename => params.rolename)
66
66
  end
67
67
 
@@ -69,7 +69,7 @@ end
69
69
  def on_grant_privilege_to_user(params)
70
70
  params.username.downcase!
71
71
  return answer(:user_doesnt_exist, :username => params.username) unless user.exists?(params.username)
72
- user[params.username].privileges.add(params.privilege)
72
+ user[params.username].privileges.add_oid(params.privilege)
73
73
  answer(:privilege_granted_to_user, :username => params.username, :privilege => params.privilege)
74
74
  end
75
75
 
@@ -77,7 +77,7 @@ end
77
77
  def on_revoke_privilege_from_user(params)
78
78
  params.username.downcase!
79
79
  return answer(:user_doesnt_exist, :username => params.username) unless user.exists?(params.username)
80
- user[params.username].privileges.remove(params.privilege)
80
+ user[params.username].privileges.remove_oid(params.privilege)
81
81
  answer(:privilege_revoked_from_user, :username => params.username, :privilege => params.privilege)
82
82
  end
83
83
 
@@ -85,7 +85,7 @@ end
85
85
  def on_grant_privilege_to_role(params)
86
86
  params.rolename.downcase!
87
87
  return answer(:role_doesnt_exist, :rolename => params.rolename) unless role.exists?(params.rolename)
88
- role[params.rolename].privileges.add(role[params.privilege])
88
+ role[params.rolename].privileges.add_oid(params.privilege)
89
89
  answer(:privilege_granted_to_role, :rolename => params.rolename, :privilege => params.privilege)
90
90
  end
91
91
 
@@ -93,7 +93,7 @@ end
93
93
  def on_revoke_privilege_from_role(params)
94
94
  params.rolename.downcase!
95
95
  return answer(:role_doesnt_exist, :rolename => params.rolename) unless role.exists?(params.rolename)
96
- role[params.rolename].privileges.remove(role[params.privilege])
96
+ role[params.rolename].privileges.remove_oid(params.privilege)
97
97
  answer(:privilege_revoked_from_role, :rolename => params.rolename, :privilege => params.privilege)
98
98
  end
99
99
 
@@ -103,7 +103,7 @@ def on_add_role_to_user(params)
103
103
  params.rolename.downcase!
104
104
  return answer(:user_doesnt_exist, :username => params.username) unless user.exists?(params.username)
105
105
  return answer(:role_doesnt_exist, :username => params.rolename) unless role.exists?(params.rolename)
106
- user[params.username].roles.add(role[params.rolename])
106
+ user[params.username].roles.add_oid(params.rolename)
107
107
  answer(:role_added_to_user, :username => params.username, :rolename => params.rolename)
108
108
  end
109
109
 
@@ -113,7 +113,7 @@ def on_remove_role_from_user(params)
113
113
  params.rolename.downcase!
114
114
  return answer(:user_doesnt_exist, :username => params.username) unless user.exists?(params.username)
115
115
  return answer(:role_doesnt_exist, :username => params.rolename) unless role.exists?(params.rolename)
116
- user[params.username].roles.remove(role[params.rolename])
116
+ user[params.username].roles.remove_oid(params.rolename)
117
117
  answer(:role_removed_from_user, :username => params.username, :rolename => params.rolename)
118
118
  end
119
119
 
@@ -139,7 +139,7 @@ __END__
139
139
  :on_create_user:
140
140
  en: "create user :username [with password :password] [:active{active}]"
141
141
  :on_delete_user:
142
- en: "delete user :rolename"
142
+ en: "delete user :username"
143
143
  :on_create_role:
144
144
  en: "create role :rolename [with description +description]"
145
145
  :on_delete_role:
@@ -4,14 +4,19 @@
4
4
  # See LICENSE.txt for permissions.
5
5
  #++
6
6
 
7
- def self.logfile
8
- @logfile
9
- end
7
+
8
+ $BLEAK_HOUSE ||= false
9
+
10
+ plugin_attribute :logfile
11
+ plugin_attribute :active
10
12
 
11
13
  def self.on_load
12
- @logfile = @butler.path.log+'/bleakhouse.log'
13
- @bl_logger = BleakHouse::Logger.new
14
- File.delete(@logfile) if File.exist?(@logfile)
14
+ if $BLEAK_HOUSE then
15
+ @logfile = @butler.path.log+'/bleakhouse.log'
16
+ @bl_logger = BleakHouse::Logger.new
17
+ @active = true
18
+ File.delete(@logfile) if File.exist?(@logfile)
19
+ end
15
20
  end
16
21
 
17
22
  def self.snapshot(*args)
@@ -19,8 +24,12 @@ def self.snapshot(*args)
19
24
  end
20
25
 
21
26
  def on_trigger
22
- plugin.snapshot('plugin', false)
23
- answer(:done, :file => plugin.logfile)
27
+ if plugin.active then
28
+ plugin.snapshot('plugin', false)
29
+ answer(:done, :file => plugin.logfile)
30
+ else
31
+ answer(:inactive)
32
+ end
24
33
  end
25
34
 
26
35
  __END__
@@ -37,6 +46,8 @@ __END__
37
46
  :strings:
38
47
  :done:
39
48
  en: "Snapshot taken. The file is in <%= file %>."
49
+ :inactive:
50
+ en: "Bleak house is not activated for this bot."
40
51
  :summary:
41
52
  en: Inspect butlers memory usage.
42
53
  :trigger:
@@ -20,17 +20,26 @@ __END__
20
20
  :mail: "apeiros@gmx.net"
21
21
  :version: "1.0.0"
22
22
  :author: "Stefan Rusterholz"
23
+ :translation:
24
+ es: "Rafael George, <george.rafael@gmail.com>"
23
25
  :help:
24
26
  en:
25
27
  "": |
26
- Revoke a users op via butler.
28
+ Revoke a users op via butler
29
+ es:
30
+ "": |
31
+ Revoca a un usuario los privilegios de operador atravez de butler
27
32
  :map:
28
33
  :on_deop:
29
34
  en: "deop [*user@Nick] [in :channel@Channel]"
35
+ es: "deop [*user@Nick] [en :channel@Channel]"
30
36
  :revision:
31
37
  :plugin: 1
32
38
  :summary:
33
39
  en: Revoke a users op via butler.
40
+ es: Revoca a un usuario los privilegios de operador atravez de butler
34
41
  :usage:
35
42
  en: |
36
43
  ![b]deop![o] [![c(green)]nick![o] ...] [in ![c(green)]channel![o]]
44
+ es: |
45
+ ![b]deop![o] [![c(green)]nick![o] ...] [en ![c(green)]canal![o]]
@@ -19,17 +19,26 @@ __END__
19
19
  :mail: "apeiros@gmx.net"
20
20
  :version: "1.0.0"
21
21
  :author: "Stefan Rusterholz"
22
+ :translation:
23
+ es: "Rafael George, <george.rafael@gmail.com>"
22
24
  :help:
23
25
  en:
24
26
  "": |
25
27
  Revoke a users voice via butler.
28
+ es:
29
+ "": |
30
+ Revoca la voz de un usuario atravez de butler.
26
31
  :map:
27
32
  :on_devoice:
28
33
  en: "devoice [*user@Nick] [in :channel@Channel]"
34
+ es: "desvoz [*user@Nick] [en :channel@Channel]"
29
35
  :revision:
30
36
  :plugin: 1
31
37
  :summary:
32
38
  en: Revoke a users voice via butler.
39
+ es: Revoca la voz de un usuario atravez de butler.
33
40
  :usage:
34
41
  en: |
35
42
  ![b]devoice![o] [![c(green)]nick![o] ...] [![c(green)]channel![o] ...]
43
+ es: |
44
+ ![b]desvoz![o] [![c(green)]nick![o] ...] [![c(green)]canal![o] ...]
@@ -59,24 +59,36 @@ __END__
59
59
  :mail: "apeiros@gmx.net"
60
60
  :version: "1.0.0"
61
61
  :author: "Stefan Rusterholz"
62
+ :translation:
63
+ es: "Rafael George, <george.rafael@gmail.com>"
62
64
  :help:
63
65
  en:
64
66
  "": |
65
67
  A slowly shifting channel-limit to avoid join-floods.
68
+ es:
69
+ "": |
70
+ Maneja los excesos de ingresos al canal.
66
71
  :map:
67
72
  :on_limit:
68
73
  en: limit *channels@Channel
74
+ es: limita *channels@Channel
69
75
  :on_unlimit:
70
76
  en: unlimit *channels@Channel
77
+ es: deslimita *channels@Channel
71
78
  :revision:
72
79
  :plugin: 1
73
80
  :strings:
74
81
  :limiting:
75
82
  en: Limiting channels <%= channels.join(', ') %>.
83
+ es: Limita canales <%= channels.join(', ') %>.
76
84
  :unlimiting:
77
85
  en: No longer limiting channels <%= channels.join(', ') %>.
86
+ es: No sigue limitando canales <%= channels.join(', ') %>.
78
87
  :summary:
79
88
  en: A slowly shifting channel-limit to avoid join-floods.
89
+ es: Maneja los excesos de ingresos al canal.
80
90
  :usage:
81
91
  en: |
82
92
  (![b]limit![o]|![b]unlimit![o]) ![c(green)]channel![o] ...
93
+ es: |
94
+ (![b]limita![o]|![b]deslimita![o]) ![c(green)]canales![o] ...
@@ -19,18 +19,27 @@ __END__
19
19
  :mail: "apeiros@gmx.net"
20
20
  :version: "1.0.0"
21
21
  :author: "Stefan Rusterholz"
22
+ :translation:
23
+ es: "Rafael George, <george.rafael@gmail.com>"
22
24
  :help:
23
25
  en:
24
26
  "": |
25
27
  Grant a user op via butler.
28
+ es:
29
+ "": |
30
+ Otorga privilegios de operador a un usuario atravez de butler.
26
31
  :map:
27
32
  :on_op:
28
33
  en: "op [*user@Nick] [in :channel@Channel]"
34
+ es: "op [*user@Nick] [en :channel@Channel]"
29
35
  :revision:
30
36
  :plugin: 1
31
37
  :strings:
32
38
  :summary:
33
39
  en: Grant users op via butler.
40
+ es: Otorga privilegios de operador a un usuario atravez de butler.
34
41
  :usage:
35
42
  en: |
36
43
  ![b]op![o] [![c(green)]nick![o] ...] [in ![c(green)]channel![o]]
44
+ es: |
45
+ ![b]op![o] [![c(green)]nick![o] ...] [in ![c(green)]canal![o]]
@@ -19,18 +19,28 @@ __END__
19
19
  :mail: "apeiros@gmx.net"
20
20
  :version: "1.0.0"
21
21
  :author: "Stefan Rusterholz"
22
+ :translation:
23
+ es: "Rafael George, <george.rafael@gmail.com>"
22
24
  :help:
23
25
  en:
24
26
  "": |
25
27
  Grant a user voice via butler.
28
+ es:
29
+ "": |
30
+ Otorga voz a un usuario atravez de butler.
26
31
  :map:
27
32
  :on_voice:
28
33
  en:
29
34
  "voice [*user@Nick] [in :channel@Channel]"
35
+ es:
36
+ "voz [*user@Nick] [en :channel@Channel]"
30
37
  :revision:
31
38
  :plugin: 1
32
39
  :summary:
33
40
  en: Grant a user voice via butler.
41
+ es: Otorga voz a un usuario atravez de butler.
34
42
  :usage:
35
43
  en: |
36
44
  ![b]voice![o] [![c(green)]nick![o] ...] [in ![c(green)]channel![o]]
45
+ es: |
46
+ ![b]voice![o] [![c(green)]nick![o] ...] [in ![c(green)]canal![o]]
@@ -0,0 +1,11 @@
1
+ def on_define_function(params)
2
+ if match = params.proto.match(/^([a-z]+)(?:\((.*?)\))?$/) then
3
+ end
4
+ end
5
+
6
+ __END__
7
+ :map:
8
+ :on_define_function:
9
+ en: "define function +proto := +definition"
10
+ :on_calculate:
11
+ en: "calculate +term"
@@ -0,0 +1,68 @@
1
+ require 'blank'
2
+
3
+ class Calculator < Blank('inspect', 'binding', 'method_missing')
4
+ class Functions
5
+ def initialize
6
+ @functions = {}
7
+ end
8
+
9
+ def [](name)
10
+ @functions[name.to_s]
11
+ end
12
+
13
+ def []=(name, definition)
14
+ @functions[name.to_s] = definition
15
+ end
16
+
17
+ def call(m, *args)
18
+ @functions[m.to_s].call(*args)
19
+ end
20
+
21
+ def respond_to?(m, *a)
22
+ super || @functions.has_key?(m.to_s)
23
+ end
24
+
25
+ def method_missing(m, *args)
26
+ super unless @functions.has_key?(m.to_s)
27
+ @functions[m.to_s].call(*args)
28
+ end
29
+ end
30
+
31
+ include Math
32
+
33
+ attr_reader :term
34
+
35
+ def initialize(term, delegate=nil)
36
+ @term = term.scan(/\s+|,|[a-z]*\(|\)|-?\d(?:\.\d+)?|[a-z]+|[*\/%+-]/).join
37
+ @variables = {}
38
+ @delegate = delegate
39
+ @results = []
40
+ end
41
+
42
+ def result(variables={})
43
+ @variables=variables
44
+ @results.unshift Thread.new {
45
+ $SAFE = 3
46
+ Kernel.eval(@term, binding)
47
+ }.value
48
+ @results.pop if @results.length > 10
49
+ @results.first
50
+ end
51
+ alias call result
52
+
53
+ def load(result)
54
+ @results[result]
55
+ end
56
+
57
+ def method_missing(m, *a)
58
+ if a.empty? && @variables.has_key?(m) then
59
+ @variables[m]
60
+ elsif @delegate.respond_to?(m) then
61
+ @delegate.call(m, *a)
62
+ else
63
+ super
64
+ end
65
+ end
66
+ end
67
+
68
+ register_service("org.rubyforge.butler.calculator", Calculator)
@@ -0,0 +1,198 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'sqlite3'
10
+ require 'thread'
11
+ require 'ruby/file/write'
12
+
13
+
14
+
15
+ MessageTypes = [:PRIVMSG, :NOTICE, :JOIN, :PART, :QUIT, :KICK, :KILL]
16
+ Row = Struct.new(:oid, :time, :channel, :nick, :type, :text)
17
+ def Row.from_row(row)
18
+ oid, time, channel, nick, type, text = *row
19
+ new(oid.to_i, Time.at(oid.to_i), channel, nick, MessageTypes[type.to_i], text)
20
+ end
21
+
22
+ class IRCLogger
23
+ def initialize(db, channels)
24
+ @db_file = db
25
+ @db = SQLite3::Database.new(db)
26
+ @db.execute %q{
27
+ CREATE TABLE IF NOT EXISTS messages(
28
+ oid INTEGER PRIMARY KEY AUTOINCREMENT,
29
+ time INTEGER,
30
+ channel TEXT,
31
+ nick TEXT,
32
+ type INTEGER,
33
+ text TEXT
34
+ )
35
+ }
36
+ @insert = @db.prepare %q{
37
+ INSERT INTO messages
38
+ (time, channel, nick, type, text)
39
+ VALUES (:time, :channel, :nick, :type, :text)
40
+ }
41
+ File.write(channels, {}.to_yaml) unless File.exist?(channels)
42
+ @channels = YAML.load_file(channels)
43
+ @config_file = channels
44
+ @lock = Mutex.new
45
+ end
46
+
47
+ def start_logging(channel)
48
+ @lock.synchronize {
49
+ @channels[channel] = true
50
+ File.write(@config_file, @channels.to_yaml)
51
+ }
52
+ end
53
+
54
+ def stop_logging(channel)
55
+ @lock.synchronize {
56
+ @channels.delete(channel)
57
+ File.write(@config_file, @channels.to_yaml)
58
+ }
59
+ end
60
+
61
+ def logged?(channel)
62
+ @channels.has_key?(channel)
63
+ end
64
+
65
+ def insert(values)
66
+ @insert.execute(values)
67
+ end
68
+
69
+ def select(statement=nil, *values)
70
+ query = "SELECT * FROM messages #{statement}"
71
+ if block_given? then
72
+ @db.execute(query, *values) { |row|
73
+ yield(Row.from_row(row))
74
+ }
75
+ else
76
+ @db.execute(query, *values).map { |row|
77
+ Row.from_row(row)
78
+ }
79
+ end
80
+ end
81
+
82
+ def find(args={}, &block)
83
+ where = []
84
+ if nick = args[:nick] then
85
+ where << field_equals_values('nick', nick)
86
+ end
87
+ if channel = args[:channel] then
88
+ where << field_equals_values('channel', channel)
89
+ end
90
+ if time = args[:time] then
91
+ where << "time >= #{SQLite3::Database.quote(time.begin.to_i)} AND time <= #{SQLite3::Database.quote(time.end.to_i)}"
92
+ end
93
+ if types = args[:type] then
94
+ where << field_equals_values('type', types)
95
+ end
96
+ if text = args[:text] then
97
+ field_contains_all_values('text', text)
98
+ end
99
+ select(where.empty? ? nil : 'WHERE '+where.join(' AND '), &block)
100
+ end
101
+
102
+ def field_equals_values(field, values)
103
+ if Array === values then
104
+ values.map { |e| "#{field} LIKE '#{SQLite3::Database.quote(e)}'" }.join(" AND ")
105
+ else
106
+ "#{field} LIKE '#{SQLite3::Database.quote(values)}'"
107
+ end
108
+ end
109
+
110
+ def field_equals_values(field, values)
111
+ if Array === values then
112
+ is_in(field, values)
113
+ else
114
+ "#{field} = '#{SQLite3::Database.quote(values)}'"
115
+ end
116
+ end
117
+
118
+ def is_in(field, values)
119
+ "#{field} IN ('#{values.map { |e| SQLite3::Database.quote(text) }.join('\', \'') }')"
120
+ end
121
+ end
122
+
123
+ def self.on_load
124
+ logger = IRCLogger.new(@path.base+'/log.sqlite', @path.base+'/channels.yaml')
125
+
126
+ subscribe(:PRIVMSG, -10, MessageTypes.index(:PRIVMSG)) { |listener, message, type|
127
+ next unless logger.logged?(message.channel.to_str)
128
+ logger.insert(
129
+ :time => Time.now.to_i,
130
+ :channel => message.channel.to_str,
131
+ :nick => message.from.nick,
132
+ :type => type,
133
+ :text => message.text
134
+ )
135
+ }
136
+ subscribe(:NOTICE, -10, MessageTypes.index(:NOTICE)) { |listener, message, type|
137
+ next unless logger.logged?(message.channel.to_str)
138
+ logger.insert(
139
+ :time => Time.now.to_i,
140
+ :channel => message.channel.to_str,
141
+ :nick => message.from.nick,
142
+ :type => type,
143
+ :text => message.text
144
+ )
145
+ }
146
+ subscribe(:JOIN, -10, MessageTypes.index(:JOIN)) { |listener, message, type|
147
+ next unless logger.logged?(message.channel.to_str)
148
+ logger.insert(
149
+ :time => Time.now.to_i,
150
+ :channel => message.channel.to_str,
151
+ :nick => message.from.nick,
152
+ :type => type,
153
+ :text => nil
154
+ )
155
+ }
156
+ subscribe(:PART, -10, MessageTypes.index(:PART)) { |listener, message, type|
157
+ next unless logger.logged?(message.channel.to_str)
158
+ logger.insert(
159
+ :time => Time.now.to_i,
160
+ :channel => message.channel.to_str,
161
+ :nick => message.from.nick,
162
+ :type => type,
163
+ :text => message.text
164
+ )
165
+ }
166
+ subscribe(:QUIT, -10, MessageTypes.index(:QUIT)) { |listener, message, type|
167
+ next unless logger.logged?(message.channel.to_str)
168
+ logger.insert(
169
+ :time => Time.now.to_i,
170
+ :channel => message.channel.to_str,
171
+ :nick => message.from.nick,
172
+ :type => type,
173
+ :text => message.text
174
+ )
175
+ }
176
+ subscribe(:KICK, -10, MessageTypes.index(:KICK)) { |listener, message, type|
177
+ next unless logger.logged?(message.channel.to_str)
178
+ logger.insert(
179
+ :time => Time.now.to_i,
180
+ :channel => message.channel.to_str,
181
+ :nick => message.from.nick,
182
+ :type => type,
183
+ :text => message.text
184
+ )
185
+ }
186
+ subscribe(:KILL, -10, MessageTypes.index(:KILL)) { |listener, message, type|
187
+ next unless logger.logged?(message.channel.to_str)
188
+ logger.insert(
189
+ :time => Time.now.to_i,
190
+ :channel => message.channel.to_str,
191
+ :nick => message.from.nick,
192
+ :type => type,
193
+ :text => message.text
194
+ )
195
+ }
196
+
197
+ register(logger)
198
+ end