butler 1.8.2 → 1.8.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -1,166 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright 2007 by Stefan Rusterholz.
|
3
|
-
# All rights reserved.
|
4
|
-
# See LICENSE.txt for permissions.
|
5
|
-
#++
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
trigger "user"
|
10
|
-
|
11
|
-
def on_trigger
|
12
|
-
case arguments(1)
|
13
|
-
when "create"
|
14
|
-
name, pass, active = @message.arguments[2,3]
|
15
|
-
active = active && !%(false off).include?(active.downcase)
|
16
|
-
return answer(usage) unless name and pass
|
17
|
-
if @butler.access.user.exists?(name) then
|
18
|
-
answer(:exists, :name => name)
|
19
|
-
else
|
20
|
-
@butler.access.user.create(name, pass, nil, false, :active => active)
|
21
|
-
answer(:created, :name => name)
|
22
|
-
end
|
23
|
-
|
24
|
-
when "delete"
|
25
|
-
return answer(usage) unless name = @message.arguments[2]
|
26
|
-
if @butler.access.user.exists?(name) then
|
27
|
-
@butler.access.user.delete(name)
|
28
|
-
answer(:deleted)
|
29
|
-
else
|
30
|
-
answer(:doesnt_exist, :name => name)
|
31
|
-
end
|
32
|
-
|
33
|
-
when "activate"
|
34
|
-
return answer(usage) unless name = @message.arguments[2]
|
35
|
-
if @butler.access.user.exists?(name) then
|
36
|
-
@butler.access.user[name].activate
|
37
|
-
answer(:activated)
|
38
|
-
else
|
39
|
-
answer(:doesnt_exist, :name => name)
|
40
|
-
end
|
41
|
-
|
42
|
-
when "deactivate"
|
43
|
-
return answer(usage) unless name = @message.arguments[2]
|
44
|
-
if @butler.access.user.exists?(name) then
|
45
|
-
@butler.access.user[name].deactivate
|
46
|
-
answer(:deactivated)
|
47
|
-
else
|
48
|
-
answer(:doesnt_exist, :name => name)
|
49
|
-
end
|
50
|
-
|
51
|
-
when "list"
|
52
|
-
answer(:users, :names => @butler.access.user.keys.columnize(5))
|
53
|
-
|
54
|
-
when "privileges"
|
55
|
-
return answer(usage) unless name = @message.arguments[2]
|
56
|
-
return answer(:doesnt_exist, :name => name) unless user = @butler.access.user[name]
|
57
|
-
answer(:privileges, :name => name, :privileges => user.privileges.list.keys.columnize(5))
|
58
|
-
|
59
|
-
when "roles"
|
60
|
-
return answer(usage) unless name = @message.arguments[2]
|
61
|
-
return answer(:doesnt_exist, :name => name) unless user = @butler.access.user[name]
|
62
|
-
answer(:roles, :name => name, :roles => user.roles.map { |r| r.id }.columnize(5))
|
63
|
-
|
64
|
-
when "add"
|
65
|
-
return answer(usage) unless name = @message.arguments[3] and @message.arguments[4]
|
66
|
-
return answer(:doesnt_exist, :name => name) unless user = @butler.access.user[name]
|
67
|
-
case arguments(2)
|
68
|
-
when "privilege"
|
69
|
-
user.privileges.add(@message.arguments[4])
|
70
|
-
answer(:added_privilege, :name => name, :privilege => @message.arguments[4])
|
71
|
-
|
72
|
-
when "role"
|
73
|
-
role_name = @message.arguments[4]
|
74
|
-
answer(:role_doesnt_exist, :role => role_name) unless role = @butler.access.role[role_name]
|
75
|
-
user.roles.add(role)
|
76
|
-
answer(:added_role, :name => name, :role => role_name)
|
77
|
-
|
78
|
-
else
|
79
|
-
answer(usage)
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
when "remove"
|
84
|
-
return answer(usage) unless name = @message.arguments[3] and @message.arguments[4]
|
85
|
-
return answer(:doesnt_exist, :name => name) unless user = @butler.access.user[name]
|
86
|
-
case arguments(2)
|
87
|
-
when "privilege"
|
88
|
-
user.privileges.remove(@message.arguments[4])
|
89
|
-
answer(:removed_privilege, :name => name, :privilege => @message.arguments[4])
|
90
|
-
|
91
|
-
when "role"
|
92
|
-
role_name = @message.arguments[4]
|
93
|
-
answer(:role_doesnt_exist, :role => role_name) unless role = @butler.access.role[role_name]
|
94
|
-
user.roles.remove(role)
|
95
|
-
answer(:removed_role, :name => name, :role => role_name)
|
96
|
-
|
97
|
-
else
|
98
|
-
answer(usage)
|
99
|
-
|
100
|
-
end
|
101
|
-
|
102
|
-
else
|
103
|
-
answer(usage)
|
104
|
-
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
__END__
|
109
|
-
---
|
110
|
-
:revision:
|
111
|
-
:plugin: 1
|
112
|
-
:summary:
|
113
|
-
en: "Manage your users."
|
114
|
-
:about:
|
115
|
-
:mail: "apeiros@gmx.net"
|
116
|
-
:version: "1.0.0"
|
117
|
-
:author: "Stefan Rusterholz"
|
118
|
-
:strings:
|
119
|
-
:exists:
|
120
|
-
en: |
|
121
|
-
User <%= name %> exists already.
|
122
|
-
:doesnt_exist:
|
123
|
-
en: |
|
124
|
-
User <%= name %> does not exist.
|
125
|
-
:users:
|
126
|
-
en: |
|
127
|
-
Users:
|
128
|
-
<%= names %>
|
129
|
-
:privileges:
|
130
|
-
en: |
|
131
|
-
Privileges of <%= name %>:
|
132
|
-
<%= privileges %>
|
133
|
-
:roles:
|
134
|
-
en: |
|
135
|
-
Roles of <%= name %>:
|
136
|
-
<%= roles %>
|
137
|
-
:created:
|
138
|
-
en: |
|
139
|
-
Created user <%= name %>.
|
140
|
-
:deleted:
|
141
|
-
en: |
|
142
|
-
Deleted user <%= name %>.
|
143
|
-
:activated:
|
144
|
-
en: |
|
145
|
-
Activated user <%= name %>.
|
146
|
-
:deactivated:
|
147
|
-
en: |
|
148
|
-
Deactivated user <%= name %>.
|
149
|
-
:added_privilege:
|
150
|
-
en: |
|
151
|
-
Added privilege <%= privilege %> to <%= name %>.
|
152
|
-
:removed_privilege:
|
153
|
-
en: |
|
154
|
-
Removed privilege <%= privilege %> from <%= name %>.
|
155
|
-
:added_role:
|
156
|
-
en: |
|
157
|
-
Added role <%= role %> to <%= name %>.
|
158
|
-
:removed_role:
|
159
|
-
en: |
|
160
|
-
Removed role <%= role %> from <%= name %>.
|
161
|
-
:usage:
|
162
|
-
en: "![b]user![o] ('create' name password ['active' | 'inactive'] | ('delete' | 'activate' | 'deactivate') username | ('add' | 'remove') privilege)"
|
163
|
-
:help:
|
164
|
-
en:
|
165
|
-
"": |
|
166
|
-
Manage your users.
|
@@ -1,93 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright 2007 by Stefan Rusterholz.
|
3
|
-
# All rights reserved.
|
4
|
-
# See LICENSE.txt for permissions.
|
5
|
-
#++
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
extend OnHandlers
|
10
|
-
|
11
|
-
configuration(
|
12
|
-
"active" => false
|
13
|
-
)
|
14
|
-
|
15
|
-
trigger "onhandlers"
|
16
|
-
|
17
|
-
def on_trigger
|
18
|
-
if @message.arguments[1] && @message.arguments[1].downcase == "on" then
|
19
|
-
plugin.config["active"] = true
|
20
|
-
else
|
21
|
-
plugin.config["active"] = false
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def on_join(listener, user, channel)
|
26
|
-
debug("on_join triggered, #{user} joined #{channel}") if plugin.config["active"]
|
27
|
-
end
|
28
|
-
def on_privmsg(listener, user, text)
|
29
|
-
debug("on_privmsg triggered, #{user} wrote #{text}") if plugin.config["active"]
|
30
|
-
end
|
31
|
-
def on_notice(listener, user, text)
|
32
|
-
debug("on_notice triggered, #{user} wrote #{text}") if plugin.config["active"]
|
33
|
-
end
|
34
|
-
def on_nick(listener, user, old_nick)
|
35
|
-
debug("on_nick triggered, #{user} changed his nick from #{old_nick}") if plugin.config["active"]
|
36
|
-
end
|
37
|
-
def on_topic(listener, user, channel, topic)
|
38
|
-
debug("on_topic triggered, #{user} changed topic of #{channel} to #{topic}") if plugin.config["active"]
|
39
|
-
end
|
40
|
-
def on_part(listener, user, channel)
|
41
|
-
debug("on_part triggered, #{user} parted #{channel}") if plugin.config["active"]
|
42
|
-
end
|
43
|
-
def on_quit(listener, user, reason)
|
44
|
-
debug("on_quit triggered, #{user} has quit with #{reason}") if plugin.config["active"]
|
45
|
-
end
|
46
|
-
def on_kick(listener)
|
47
|
-
debug("on_kick triggered with #{@message}") if plugin.config["active"]
|
48
|
-
end
|
49
|
-
def on_kill(listener)
|
50
|
-
debug("on_kill triggered with #{@message}") if plugin.config["active"]
|
51
|
-
end
|
52
|
-
def on_kline(listener)
|
53
|
-
debug("on_kline triggered with #{@message}") if plugin.config["active"]
|
54
|
-
end
|
55
|
-
def on_invocation(listener)
|
56
|
-
debug("on_invocation triggered with #{@message}") if plugin.config["active"]
|
57
|
-
end
|
58
|
-
def on_ban(listener, *masks)
|
59
|
-
debug("on_ban triggered with #{@message}") if plugin.config["active"]
|
60
|
-
end
|
61
|
-
def on_unban(listener, *masks)
|
62
|
-
debug("on_unban triggered with #{@message}") if plugin.config["active"]
|
63
|
-
end
|
64
|
-
def on_op(listener, *users)
|
65
|
-
debug("on_op triggered with #{@message}") if plugin.config["active"]
|
66
|
-
end
|
67
|
-
def on_deop(listener, *users)
|
68
|
-
debug("on_deop triggered with #{@message}") if plugin.config["active"]
|
69
|
-
end
|
70
|
-
def on_voice(listener, *users)
|
71
|
-
debug("on_voice triggered with #{@message}") if plugin.config["active"]
|
72
|
-
end
|
73
|
-
def on_devoice(listener, *users)
|
74
|
-
debug("on_devoice triggered with #{@message}") if plugin.config["active"]
|
75
|
-
end
|
76
|
-
|
77
|
-
__END__
|
78
|
-
---
|
79
|
-
:revision:
|
80
|
-
:plugin: 1
|
81
|
-
:summary:
|
82
|
-
en: Testing all possible on_handlers
|
83
|
-
:about:
|
84
|
-
:mail: "apeiros@gmx.net"
|
85
|
-
:version: "1.0.0"
|
86
|
-
:author: "Stefan Rusterholz"
|
87
|
-
:strings:
|
88
|
-
:usage:
|
89
|
-
en: "This plugin does not offer a specific interface."
|
90
|
-
:help:
|
91
|
-
en:
|
92
|
-
"": |
|
93
|
-
This plugin is meant for diagnostic purposes only.
|
@@ -1,183 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright 2007 by Stefan Rusterholz.
|
3
|
-
# All rights reserved.
|
4
|
-
# See LICENSE.txt for permissions.
|
5
|
-
#++
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
require 'sequel/sqlite'
|
10
|
-
|
11
|
-
class <<self
|
12
|
-
attr_reader :log_db
|
13
|
-
attr_reader :log_table
|
14
|
-
end
|
15
|
-
|
16
|
-
@log_db = Sequel.sqlite @butler.path.log+'/log.sqlite'
|
17
|
-
|
18
|
-
configuration(
|
19
|
-
"channels" => []
|
20
|
-
)
|
21
|
-
@log_db.create_table(:log) {
|
22
|
-
primary_key :oid, :integer
|
23
|
-
int :time
|
24
|
-
str :channel, :size => 255
|
25
|
-
str :nick, :size => 48
|
26
|
-
int :type
|
27
|
-
text :text
|
28
|
-
} unless @log_db.table_exists?(:log)
|
29
|
-
@log_table = @log_db[:log]
|
30
|
-
|
31
|
-
MessageTypes = [:PRIVMSG, :NOTICE, :JOIN, :PART, :QUIT, :KICK, :KILL]
|
32
|
-
|
33
|
-
trigger "log"
|
34
|
-
|
35
|
-
def on_trigger
|
36
|
-
case @message.arguments[1]
|
37
|
-
when "list"
|
38
|
-
answer(:list, :channels => plugin.config["channels"])
|
39
|
-
when "on"
|
40
|
-
plugin.config["channels"] |= message.arguments[2..-1]
|
41
|
-
answer(:activated, :channels => message.arguments[2..-1])
|
42
|
-
when "off"
|
43
|
-
plugin.config["channels"] -= message.arguments[2..-1]
|
44
|
-
answer(:deactivated, :channels => message.arguments[2..-1])
|
45
|
-
when "search"
|
46
|
-
text, channel, from = @message.text.match(/search (.*)(?: in channel (#\w+))?(?: from (\w+))?$/).captures
|
47
|
-
text = "%#{text}%" if text
|
48
|
-
filtered = plugin.log_table #.filter { :type == 0 || :type == 1 }
|
49
|
-
filtered = filtered.filter { :nick == from } if from
|
50
|
-
filtered = filtered.filter { :nick == channel } if channel
|
51
|
-
filtered = filtered.filter { :text =~ text } if text
|
52
|
-
count = filtered.count
|
53
|
-
if count.zero? then
|
54
|
-
answer(:no_match)
|
55
|
-
else
|
56
|
-
upper = [5,count].min
|
57
|
-
answer("Found (1-#{upper} of #{count}):")
|
58
|
-
filtered.order(:time.DESC).first(5).each_with_index { |row, index|
|
59
|
-
answer(
|
60
|
-
"#{index+1}: " \
|
61
|
-
"[#{Time.at(row[:time]).strftime('%Y-%m-%d %H:%M')}, " \
|
62
|
-
"#{row[:from]} in #{row[:channel]}] " \
|
63
|
-
"#{row[:text]}"
|
64
|
-
)
|
65
|
-
}
|
66
|
-
end
|
67
|
-
when "dump"
|
68
|
-
n = @message.arguments[2].to_i
|
69
|
-
n = 5 if n.zero?
|
70
|
-
data = plugin.log_table.order(:time.DESC).first(n)
|
71
|
-
answer("Dump(#{data.length}):")
|
72
|
-
data.each_with_index { |row, index|
|
73
|
-
answer(
|
74
|
-
"#{index+1}: " \
|
75
|
-
"[#{Time.at(row[:time]).strftime('%Y-%m-%d %H:%M')}, " \
|
76
|
-
"#{row[:from]} in #{row[:channel]}] " \
|
77
|
-
"#{row[:text]}"
|
78
|
-
)
|
79
|
-
}
|
80
|
-
else
|
81
|
-
answer(usage)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
subscribe(:PRIVMSG, -10, nil, MessageTypes.index(:PRIVMSG)) { |listener, message, type|
|
86
|
-
next unless @config["channels"].include?(message.channel.to_str)
|
87
|
-
@log_table << {
|
88
|
-
:time => Time.now.to_i,
|
89
|
-
:channel => message.channel.to_str,
|
90
|
-
:nick => message.from.nick,
|
91
|
-
:type => type,
|
92
|
-
:text => message.text
|
93
|
-
}
|
94
|
-
}
|
95
|
-
subscribe(:NOTICE, -10, nil, MessageTypes.index(:NOTICE)) { |listener, message, type|
|
96
|
-
next unless @config["channels"].include?(message.channel.to_str)
|
97
|
-
@log_table << {
|
98
|
-
:time => Time.now.to_i,
|
99
|
-
:channel => message.channel.to_str,
|
100
|
-
:nick => message.from.nick,
|
101
|
-
:type => type,
|
102
|
-
:text => message.text
|
103
|
-
}
|
104
|
-
}
|
105
|
-
subscribe(:JOIN, -10, nil, MessageTypes.index(:JOIN)) { |listener, message, type|
|
106
|
-
next unless @config["channels"].include?(message.channel.to_str)
|
107
|
-
@log_table << {
|
108
|
-
:time => Time.now.to_i,
|
109
|
-
:channel => message.channel.to_str,
|
110
|
-
:nick => message.from.nick,
|
111
|
-
:type => type,
|
112
|
-
:text => nil
|
113
|
-
}
|
114
|
-
}
|
115
|
-
subscribe(:PART, -10, nil, MessageTypes.index(:PART)) { |listener, message, type|
|
116
|
-
next unless @config["channels"].include?(message.channel.to_str)
|
117
|
-
@log_table << {
|
118
|
-
:time => Time.now.to_i,
|
119
|
-
:channel => message.channel.to_str,
|
120
|
-
:nick => message.from.nick,
|
121
|
-
:type => type,
|
122
|
-
:text => message.text
|
123
|
-
}
|
124
|
-
}
|
125
|
-
subscribe(:QUIT, -10, nil, MessageTypes.index(:QUIT)) { |listener, message, type|
|
126
|
-
next unless @config["channels"].include?(message.channel.to_str)
|
127
|
-
@log_table << {
|
128
|
-
:time => Time.now.to_i,
|
129
|
-
:channel => message.channel.to_str,
|
130
|
-
:nick => message.from.nick,
|
131
|
-
:type => type,
|
132
|
-
:text => message.text
|
133
|
-
}
|
134
|
-
}
|
135
|
-
subscribe(:KICK, -10, nil, MessageTypes.index(:KICK)) { |listener, message, type|
|
136
|
-
next unless @config["channels"].include?(message.channel.to_str)
|
137
|
-
@log_table << {
|
138
|
-
:time => Time.now.to_i,
|
139
|
-
:channel => message.channel.to_str,
|
140
|
-
:nick => message.from.nick,
|
141
|
-
:type => type,
|
142
|
-
:text => message.text
|
143
|
-
}
|
144
|
-
}
|
145
|
-
subscribe(:KILL, -10, nil, MessageTypes.index(:KILL)) { |listener, message, type|
|
146
|
-
next unless @config["channels"].include?(message.channel.to_str)
|
147
|
-
@log_table << {
|
148
|
-
:time => Time.now.to_i,
|
149
|
-
:channel => message.channel.to_str,
|
150
|
-
:nick => message.from.nick,
|
151
|
-
:type => type,
|
152
|
-
:text => message.text
|
153
|
-
}
|
154
|
-
}
|
155
|
-
|
156
|
-
__END__
|
157
|
-
---
|
158
|
-
:revision:
|
159
|
-
:plugin: 1
|
160
|
-
:configuration: 1
|
161
|
-
:summary:
|
162
|
-
en: Log channel messages
|
163
|
-
:about:
|
164
|
-
:mail: "apeiros@gmx.net"
|
165
|
-
:version: "1.0.0"
|
166
|
-
:author: "Stefan Rusterholz"
|
167
|
-
:usage:
|
168
|
-
en: "![b]log![o] 'list'"
|
169
|
-
:help:
|
170
|
-
en:
|
171
|
-
"": Log channel messages.
|
172
|
-
:strings:
|
173
|
-
:no_match:
|
174
|
-
en: "No match found."
|
175
|
-
:list:
|
176
|
-
en: |
|
177
|
-
Logging is active for: <%= channels.join(', ') %>.
|
178
|
-
:activated:
|
179
|
-
en: |
|
180
|
-
Logging activated in: <%= channels.join(', ') %>.
|
181
|
-
:deactivated:
|
182
|
-
en: |
|
183
|
-
Logging deactivated in: <%= channels.join(', ') %>.
|