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.
- data/Rakefile +1 -1
- data/bin/botcontrol +1 -1
- data/data/butler/dialogs/create_config.rb +2 -2
- data/data/butler/dialogs/quickcreate.rb +6 -4
- data/data/butler/dialogs/uninstall.rb +4 -3
- data/data/butler/plugins/core/access.rb +10 -10
- data/data/butler/plugins/dev/bleakhouse.rb +19 -8
- data/data/butler/plugins/operator/deop.rb +10 -1
- data/data/butler/plugins/operator/devoice.rb +9 -0
- data/data/butler/plugins/operator/limit.rb +12 -0
- data/data/butler/plugins/operator/op.rb +9 -0
- data/data/butler/plugins/operator/voice.rb +10 -0
- data/data/butler/plugins/util/calculator.rb +11 -0
- data/data/butler/services/org.rubyforge.butler/calculator/1/calculator.rb +68 -0
- data/data/butler/services/org.rubyforge.butler/log/1/service.rb +198 -0
- data/data/butler/services/org.rubyforge.butler/strings/1/data/en/acknowledge.yaml +8 -0
- data/data/butler/services/org.rubyforge.butler/strings/1/data/en/gratitude.yaml +3 -0
- data/data/butler/services/org.rubyforge.butler/strings/1/data/en/hello.yaml +6 -0
- data/data/butler/services/org.rubyforge.butler/strings/1/data/en/ignorance.yaml +7 -0
- data/data/butler/services/org.rubyforge.butler/strings/1/data/en/ignorance_about.yaml +3 -0
- data/data/butler/services/org.rubyforge.butler/strings/1/data/en/insult.yaml +3 -0
- data/data/butler/services/org.rubyforge.butler/strings/1/data/en/rejection.yaml +12 -0
- data/data/butler/services/org.rubyforge.butler/strings/1/service.rb +50 -0
- data/lib/access.rb +6 -3
- data/lib/access/privilege.rb +9 -78
- data/lib/access/privilegelist.rb +75 -0
- data/lib/access/role.rb +14 -94
- data/lib/access/role/base.rb +40 -0
- data/lib/access/rolelist.rb +99 -0
- data/lib/access/savable.rb +6 -3
- data/lib/access/user.rb +21 -19
- data/lib/access/version.rb +17 -0
- data/lib/access/yamlbase.rb +64 -48
- data/lib/butler.rb +1 -0
- data/lib/butler/bot.rb +8 -2
- data/lib/butler/control.rb +3 -1
- data/lib/butler/initialvalues.rb +1 -1
- data/lib/butler/irc/client.rb +6 -0
- data/lib/butler/irc/message.rb +14 -9
- data/lib/butler/irc/parser.rb +8 -5
- data/lib/butler/irc/parser/generic.rb +33 -1
- data/lib/butler/irc/parser/rfc2812.rb +5 -2
- data/lib/butler/plugin.rb +22 -2
- data/lib/butler/plugins.rb +2 -7
- data/lib/butler/service.rb +73 -0
- data/lib/butler/services.rb +65 -0
- data/lib/butler/version.rb +1 -1
- data/lib/ruby/array/random.rb +17 -0
- data/lib/ruby/string/camelcase.rb +14 -0
- data/test/test_access.rb +164 -59
- data/test/test_access/privilege/banners.statistics.yaml +3 -0
- data/test/test_access/privilege/banners.yaml +3 -0
- data/test/test_access/privilege/news.create.yaml +3 -0
- data/test/test_access/privilege/news.delete.yaml +3 -0
- data/test/test_access/privilege/news.edit.yaml +3 -0
- data/test/test_access/privilege/news.read.yaml +3 -0
- data/test/test_access/privilege/news.yaml +3 -0
- data/test/test_access/privilege/paid_content.yaml +3 -0
- data/test/test_access/privilege/statistics.ftp.yaml +3 -0
- data/test/test_access/privilege/statistics.web.yaml +3 -0
- data/test/test_access/privilege/statistics.yaml +3 -0
- data/test/test_access/role/chiefeditor.yaml +7 -0
- data/test/test_access/role/editor.yaml +9 -0
- data/test/test_access/user/test.yaml +12 -0
- metadata +51 -5
- data/data/butler/plugins/core/user.rb +0 -166
- data/data/butler/plugins/dev/onhandlers.rb +0 -93
- 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.
|
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
@@ -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
|
-
|
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
|
-
|
46
|
-
|
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
|
-
|
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
|
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
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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 :
|
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
|
-
|
8
|
-
|
9
|
-
|
7
|
+
|
8
|
+
$BLEAK_HOUSE ||= false
|
9
|
+
|
10
|
+
plugin_attribute :logfile
|
11
|
+
plugin_attribute :active
|
10
12
|
|
11
13
|
def self.on_load
|
12
|
-
|
13
|
-
|
14
|
-
|
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.
|
23
|
-
|
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,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
|