butler 1.8.2 → 1.8.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 (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