lita-karma 0.0.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/lita/handlers/karma.rb +64 -60
- data/lita-karma.gemspec +2 -2
- data/spec/lita/handlers/karma_spec.rb +71 -77
- data/spec/spec_helper.rb +1 -1
- metadata +4 -5
- data/CHANGELOG.md +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 666d042af696f1eb6b735cd531c3ea31a8485987
|
4
|
+
data.tar.gz: c445dfd3e021e96dda584674ba75953fdf28335f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0863e9bd71df183c70fa99ef8f6070839016f5b174496913c41406ea38ccf3ff6219bf63b79746748e294020accd09e553c2475b74890cb58b32a4845d039e39
|
7
|
+
data.tar.gz: 0c39702837bd97f9cabca8fb076e97a9db599d940007fff164690d8fe1c2f5d3993e79004f35f7355ab329fb8d92b5172a48d27903572cd576c95ddb22462cbf
|
data/lib/lita/handlers/karma.rb
CHANGED
@@ -3,43 +3,42 @@ require "lita"
|
|
3
3
|
module Lita
|
4
4
|
module Handlers
|
5
5
|
class Karma < Handler
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
6
|
+
route %r{([^\s]{2,})\+\+}, :increment, help: { "TERM++" => "Increments TERM by one." }
|
7
|
+
route %r{([^\s]{2,})\-\-}, :decrement, help: { "TERM--" => "Decrements TERM by one." }
|
8
|
+
route %r{([^\s]{2,})~~}, :check, help: { "TERM~~" => "Shows the current karma of TERM." }
|
9
|
+
route %r{^karma\s+worst}, :list_worst, command: true, help: {
|
10
|
+
"karma worst [N]" => "Lists the bottom N terms by karma. N defaults to 5."
|
11
|
+
}
|
12
|
+
route %r{^karma\s+best}, :list_best, command: true, help: {
|
13
|
+
"karma best [N]" => "Lists the top N terms by karma. N defaults to 5."
|
14
|
+
}
|
15
|
+
route %r{^karma\s+modified}, :modified, command: true, help: {
|
16
|
+
"karma modified TERM" => "Lists the names of users who have upvoted or downvoted TERM."
|
17
|
+
}
|
18
|
+
route %r{^karma\s*$}, :list_best, command: true
|
19
|
+
route %r{^([^\s]{2,})\s*\+=\s*([^\s]{2,})}, :link, command: true, help: {
|
20
|
+
"TERM1 += TERM2" => "Links TERM2 to TERM1. TERM1's karma will then be displayed as the sum of its own and TERM2's karma."
|
21
|
+
}
|
22
|
+
route %r{^([^\s]{2,})\s*-=\s*([^\s]{2,})}, :unlink, command: true, help: {
|
23
|
+
"TERM1 -= TERM2" => "Unlinks TERM2 from TERM1. TERM1's karma will no longer be displayed as the sum of its own and TERM2's karma."
|
24
|
+
}
|
25
|
+
|
26
|
+
def self.default_config(config)
|
27
|
+
config.cooldown = 300
|
19
28
|
end
|
20
29
|
|
21
|
-
|
22
|
-
|
23
|
-
route %r{([^\s]{2,})~~}, to: :check
|
24
|
-
route %r{^karma\s+worst}, to: :list_worst, command: true
|
25
|
-
route %r{^karma\s+best}, to: :list_best, command: true
|
26
|
-
route %r{^karma\s+modified}, to: :modified, command: true
|
27
|
-
route %r{^karma\s*$}, to: :list_best, command: true
|
28
|
-
route %r{^([^\s]{2,})\s*\+=\s*([^\s]{2,})}, to: :link, command: true
|
29
|
-
route %r{^([^\s]{2,})\s*-=\s*([^\s]{2,})}, to: :unlink, command: true
|
30
|
-
|
31
|
-
def increment(matches)
|
32
|
-
modify(matches, 1)
|
30
|
+
def increment(response)
|
31
|
+
modify(response, 1)
|
33
32
|
end
|
34
33
|
|
35
|
-
def decrement(
|
36
|
-
modify(
|
34
|
+
def decrement(response)
|
35
|
+
modify(response, -1)
|
37
36
|
end
|
38
37
|
|
39
|
-
def check(
|
38
|
+
def check(response)
|
40
39
|
output = []
|
41
40
|
|
42
|
-
matches.each do |match|
|
41
|
+
response.matches.each do |match|
|
43
42
|
term = match[0]
|
44
43
|
own_score = score = redis.zscore("terms", term).to_i
|
45
44
|
links = []
|
@@ -57,86 +56,93 @@ module Lita
|
|
57
56
|
output << string
|
58
57
|
end
|
59
58
|
|
60
|
-
reply *output
|
59
|
+
response.reply *output
|
61
60
|
end
|
62
61
|
|
63
|
-
def list_best(
|
64
|
-
list(:zrevrange)
|
62
|
+
def list_best(response)
|
63
|
+
list(response, :zrevrange)
|
65
64
|
end
|
66
65
|
|
67
|
-
def list_worst(
|
68
|
-
list(:zrange)
|
66
|
+
def list_worst(response)
|
67
|
+
list(response, :zrange)
|
69
68
|
end
|
70
69
|
|
71
|
-
def link(
|
72
|
-
matches.each do |match|
|
70
|
+
def link(response)
|
71
|
+
response.matches.each do |match|
|
73
72
|
term1, term2 = match
|
74
73
|
|
75
74
|
if redis.sadd("links:#{term1}", term2)
|
76
|
-
reply "#{term2} has been linked to #{term1}."
|
75
|
+
response.reply "#{term2} has been linked to #{term1}."
|
77
76
|
else
|
78
|
-
reply "#{term2} is already linked to #{term1}."
|
77
|
+
response.reply "#{term2} is already linked to #{term1}."
|
79
78
|
end
|
80
79
|
end
|
81
80
|
end
|
82
81
|
|
83
|
-
def unlink(
|
84
|
-
matches.each do |match|
|
82
|
+
def unlink(response)
|
83
|
+
response.matches.each do |match|
|
85
84
|
term1, term2 = match
|
86
85
|
|
87
86
|
if redis.srem("links:#{term1}", term2)
|
88
|
-
reply "#{term2} has been unlinked from #{term1}."
|
87
|
+
response.reply "#{term2} has been unlinked from #{term1}."
|
89
88
|
else
|
90
|
-
reply "#{term2} is not linked to #{term1}."
|
89
|
+
response.reply "#{term2} is not linked to #{term1}."
|
91
90
|
end
|
92
91
|
end
|
93
92
|
end
|
94
93
|
|
95
|
-
def modified(
|
96
|
-
term = args[1]
|
94
|
+
def modified(response)
|
95
|
+
term = response.args[1]
|
97
96
|
|
98
97
|
if term.nil? || term.strip.empty?
|
99
|
-
reply "Format: #{robot.name}: karma modified TERM"
|
98
|
+
response.reply "Format: #{robot.name}: karma modified TERM"
|
100
99
|
return
|
101
100
|
end
|
102
101
|
|
103
102
|
user_ids = redis.smembers("modified:#{term}")
|
104
103
|
|
105
104
|
if user_ids.empty?
|
106
|
-
reply "#{term} has never been modified."
|
105
|
+
response.reply "#{term} has never been modified."
|
107
106
|
else
|
108
|
-
|
107
|
+
output = user_ids.map do |id|
|
108
|
+
User.find_by_id(id).name
|
109
|
+
end.join(", ")
|
110
|
+
response.reply output
|
109
111
|
end
|
110
112
|
end
|
111
113
|
|
112
114
|
private
|
113
115
|
|
114
|
-
def modify(
|
115
|
-
matches.each do |match|
|
116
|
+
def modify(response, delta)
|
117
|
+
response.matches.each do |match|
|
116
118
|
term = match[0]
|
117
119
|
|
118
|
-
ttl = redis.ttl("cooldown:#{user.id}:#{term}")
|
120
|
+
ttl = redis.ttl("cooldown:#{response.user.id}:#{term}")
|
119
121
|
if ttl >= 0
|
120
122
|
cooldown_message =
|
121
123
|
"You cannot modify #{term} for another #{ttl} second"
|
122
124
|
cooldown_message << (ttl == 1 ? "." : "s.")
|
123
|
-
reply cooldown_message
|
125
|
+
response.reply cooldown_message
|
124
126
|
return
|
125
127
|
else
|
126
128
|
redis.zincrby("terms", delta, term)
|
127
|
-
redis.sadd("modified:#{term}", user.id)
|
129
|
+
redis.sadd("modified:#{term}", response.user.id)
|
128
130
|
cooldown = Lita.config.handlers.karma.cooldown
|
129
131
|
if cooldown
|
130
|
-
redis.setex(
|
132
|
+
redis.setex(
|
133
|
+
"cooldown:#{response.user.id}:#{term}",
|
134
|
+
cooldown.to_i,
|
135
|
+
1
|
136
|
+
)
|
131
137
|
end
|
132
138
|
end
|
133
139
|
end
|
134
140
|
|
135
|
-
check(
|
141
|
+
check(response)
|
136
142
|
end
|
137
143
|
|
138
|
-
def list(redis_command)
|
139
|
-
n = (args[1] || 5).to_i - 1
|
144
|
+
def list(response, redis_command)
|
145
|
+
n = (response.args[1] || 5).to_i - 1
|
140
146
|
|
141
147
|
terms_scores = redis.public_send(
|
142
148
|
redis_command, "terms", 0, n, with_scores: true
|
@@ -147,15 +153,13 @@ module Lita
|
|
147
153
|
end.join("\n")
|
148
154
|
|
149
155
|
if output.length == 0
|
150
|
-
reply "There are no terms being tracked yet."
|
156
|
+
response.reply "There are no terms being tracked yet."
|
151
157
|
else
|
152
|
-
reply output
|
158
|
+
response.reply output
|
153
159
|
end
|
154
160
|
end
|
155
161
|
end
|
156
162
|
|
157
|
-
Lita.config.handlers.karma = Config.new
|
158
|
-
Lita.config.handlers.karma.cooldown = 300
|
159
163
|
Lita.register_handler(Karma)
|
160
164
|
end
|
161
165
|
end
|
data/lita-karma.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "lita-karma"
|
3
|
-
spec.version = "0.0
|
3
|
+
spec.version = "1.0.0"
|
4
4
|
spec.authors = ["Jimmy Cuadra"]
|
5
5
|
spec.email = ["jimmy@jimmycuadra.com"]
|
6
6
|
spec.description = %q{A Lita handler for tracking karma points for arbitrary terms.}
|
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
14
14
|
spec.require_paths = ["lib"]
|
15
15
|
|
16
|
-
spec.add_runtime_dependency "lita", "~>
|
16
|
+
spec.add_runtime_dependency "lita", "~> 2.0"
|
17
17
|
|
18
18
|
spec.add_development_dependency "bundler", "~> 1.3"
|
19
19
|
spec.add_development_dependency "rake"
|
@@ -1,196 +1,190 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe Lita::Handlers::Karma,
|
3
|
+
describe Lita::Handlers::Karma, lita_handler: true do
|
4
4
|
before { Lita.config.handlers.karma.cooldown = nil }
|
5
5
|
|
6
6
|
it { routes("foo++").to(:increment) }
|
7
7
|
it { routes("foo--").to(:decrement) }
|
8
8
|
it { routes("foo~~").to(:check) }
|
9
|
-
it {
|
10
|
-
it {
|
11
|
-
it {
|
12
|
-
it {
|
13
|
-
it {
|
14
|
-
it {
|
15
|
-
|
16
|
-
describe ".help" do
|
17
|
-
it "returns a hash of command help" do
|
18
|
-
expect(described_class.help).to be_a(Hash)
|
19
|
-
end
|
20
|
-
end
|
9
|
+
it { routes_command("karma best").to(:list_best) }
|
10
|
+
it { routes_command("karma worst").to(:list_worst) }
|
11
|
+
it { routes_command("karma modified").to(:modified) }
|
12
|
+
it { routes_command("karma").to(:list_best) }
|
13
|
+
it { routes_command("foo += bar").to(:link) }
|
14
|
+
it { routes_command("foo -= bar").to(:unlink) }
|
21
15
|
|
22
16
|
describe "#increment" do
|
23
17
|
it "increases the term's score by one and says the new score" do
|
24
|
-
|
25
|
-
|
18
|
+
send_message("foo++")
|
19
|
+
expect(replies.last).to eq("foo: 1")
|
26
20
|
end
|
27
21
|
|
28
22
|
it "matches multiple terms in one message" do
|
29
|
-
|
30
|
-
|
23
|
+
send_message("foo++ bar++")
|
24
|
+
expect(replies).to eq(["foo: 1", "bar: 1"])
|
31
25
|
end
|
32
26
|
|
33
27
|
it "doesn't start from zero if the term already has a positive score" do
|
34
|
-
|
35
|
-
|
36
|
-
|
28
|
+
send_message("foo++")
|
29
|
+
send_message("foo++")
|
30
|
+
expect(replies.last).to eq("foo: 2")
|
37
31
|
end
|
38
32
|
|
39
33
|
it "replies with a warning if term increment is on cooldown" do
|
40
34
|
Lita.config.handlers.karma.cooldown = 10
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
send_test_message("foo++")
|
35
|
+
send_message("foo++")
|
36
|
+
send_message("foo++")
|
37
|
+
expect(replies.last).to match(/cannot modify foo/)
|
45
38
|
end
|
46
39
|
end
|
47
40
|
|
48
41
|
describe "#decrement" do
|
49
42
|
it "decreases the term's score by one and says the new score" do
|
50
|
-
|
51
|
-
|
43
|
+
send_message("foo--")
|
44
|
+
expect(replies.last).to eq("foo: -1")
|
52
45
|
end
|
53
46
|
|
54
47
|
it "matches multiple terms in one message" do
|
55
|
-
|
56
|
-
|
48
|
+
send_message("foo-- bar--")
|
49
|
+
expect(replies).to eq(["foo: -1", "bar: -1"])
|
57
50
|
end
|
58
51
|
|
59
52
|
it "doesn't start from zero if the term already has a positive score" do
|
60
|
-
|
61
|
-
|
62
|
-
|
53
|
+
send_message("foo++")
|
54
|
+
send_message("foo--")
|
55
|
+
expect(replies.last).to eq("foo: 0")
|
63
56
|
end
|
64
57
|
|
65
58
|
it "replies with a warning if term increment is on cooldown" do
|
66
59
|
Lita.config.handlers.karma.cooldown = 10
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
send_test_message("foo--")
|
60
|
+
send_message("foo--")
|
61
|
+
send_message("foo--")
|
62
|
+
expect(replies.last).to match(/cannot modify foo/)
|
71
63
|
end
|
72
64
|
end
|
73
65
|
|
74
66
|
describe "#check" do
|
75
67
|
it "says the term's current score" do
|
76
|
-
|
77
|
-
|
68
|
+
send_message("foo~~")
|
69
|
+
expect(replies.last).to eq("foo: 0")
|
78
70
|
end
|
79
71
|
|
80
72
|
it "matches multiple terms in one message" do
|
81
|
-
|
82
|
-
|
73
|
+
send_message("foo~~ bar~~")
|
74
|
+
expect(replies).to eq(["foo: 0", "bar: 0"])
|
83
75
|
end
|
84
76
|
end
|
85
77
|
|
86
78
|
describe "#list" do
|
87
79
|
it "replies with a warning if there are no terms" do
|
88
|
-
|
89
|
-
|
80
|
+
send_command("karma")
|
81
|
+
expect(replies.last).to match(/no terms being tracked/)
|
90
82
|
end
|
91
83
|
|
92
84
|
context "with modified terms" do
|
93
85
|
before do
|
94
|
-
|
86
|
+
send_message(
|
95
87
|
"one++ one++ one++ two++ two++ three++ four++ four-- five--"
|
96
88
|
)
|
97
89
|
end
|
98
90
|
|
99
91
|
it "lists the top 5 terms by default" do
|
100
|
-
|
92
|
+
send_command("karma")
|
93
|
+
expect(replies.last).to eq <<-MSG.chomp
|
101
94
|
1. one (3)
|
102
95
|
2. two (2)
|
103
96
|
3. three (1)
|
104
97
|
4. four (0)
|
105
98
|
5. five (-1)
|
106
99
|
MSG
|
107
|
-
send_test_message("#{robot.name}: karma")
|
108
100
|
end
|
109
101
|
|
110
102
|
it 'lists the bottom 5 terms when passed "worst"' do
|
111
|
-
|
103
|
+
send_command("karma worst")
|
104
|
+
expect(replies.last).to eq <<-MSG.chomp
|
112
105
|
1. five (-1)
|
113
106
|
2. four (0)
|
114
107
|
3. three (1)
|
115
108
|
4. two (2)
|
116
109
|
5. one (3)
|
117
110
|
MSG
|
118
|
-
send_test_message("#{robot.name}: karma worst")
|
119
111
|
end
|
120
112
|
|
121
113
|
it "limits the list to the count passed as the second argument" do
|
122
|
-
|
114
|
+
send_command("karma best 2")
|
115
|
+
expect(replies.last).to eq <<-MSG.chomp
|
123
116
|
1. one (3)
|
124
117
|
2. two (2)
|
125
118
|
MSG
|
126
|
-
send_test_message("#{robot.name}: karma best 2")
|
127
119
|
end
|
128
120
|
end
|
129
121
|
end
|
130
122
|
|
131
123
|
describe "#link" do
|
132
124
|
it "says that it's linked term 2 to term 1" do
|
133
|
-
|
134
|
-
|
125
|
+
send_command("foo += bar")
|
126
|
+
expect(replies.last).to eq("bar has been linked to foo.")
|
135
127
|
end
|
136
128
|
|
137
129
|
it "says that term 2 was already linked to term 1 if it was" do
|
138
|
-
|
139
|
-
|
140
|
-
|
130
|
+
send_command("foo += bar")
|
131
|
+
send_command("foo += bar")
|
132
|
+
expect(replies.last).to eq("bar is already linked to foo.")
|
141
133
|
end
|
142
134
|
|
143
135
|
it "causes term 1's score to be modified by term 2's" do
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
136
|
+
send_message("foo++ bar++ baz++")
|
137
|
+
send_command("foo += bar")
|
138
|
+
send_command("foo += baz")
|
139
|
+
send_message("foo~~")
|
140
|
+
expect(replies.last).to match(
|
141
|
+
/foo: 3 \(1\), linked to: ba[rz]: 1, ba[rz]: 1/
|
142
|
+
)
|
149
143
|
end
|
150
144
|
end
|
151
145
|
|
152
146
|
describe "#unlink" do
|
153
147
|
it "says that it's unlinked term 2 from term 1" do
|
154
|
-
|
155
|
-
|
156
|
-
|
148
|
+
send_command("foo += bar")
|
149
|
+
send_command("foo -= bar")
|
150
|
+
expect(replies.last).to eq("bar has been unlinked from foo.")
|
157
151
|
end
|
158
152
|
|
159
153
|
it "says that term 2 was not linked to term 1 if it wasn't" do
|
160
|
-
|
161
|
-
|
154
|
+
send_command("foo -= bar")
|
155
|
+
expect(replies.last).to eq("bar is not linked to foo.")
|
162
156
|
end
|
163
157
|
|
164
158
|
it "causes term 1's score to stop being modified by term 2's" do
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
159
|
+
send_message("foo++ bar++")
|
160
|
+
send_command("foo += bar")
|
161
|
+
send_command("foo -= bar")
|
162
|
+
send_message("foo~~")
|
163
|
+
expect(replies.last).to eq("foo: 1")
|
170
164
|
end
|
171
165
|
end
|
172
166
|
|
173
167
|
describe "#modified" do
|
174
168
|
it "replies with the required format if a term is not provided" do
|
175
|
-
|
176
|
-
|
169
|
+
send_command("karma modified")
|
170
|
+
expect(replies.last).to match(/^Format:/)
|
177
171
|
end
|
178
172
|
|
179
173
|
it "replies with the required format if the term is an empty string" do
|
180
|
-
|
181
|
-
|
174
|
+
send_command("karma modified ' '")
|
175
|
+
expect(replies.last).to match(/^Format:/)
|
182
176
|
end
|
183
177
|
|
184
178
|
it "replies with a message if the term hasn't been modified" do
|
185
|
-
|
186
|
-
|
179
|
+
send_command("karma modified foo")
|
180
|
+
expect(replies.last).to match(/never been modified/)
|
187
181
|
end
|
188
182
|
|
189
183
|
it "lists users who have modified the given term" do
|
190
184
|
allow(Lita::User).to receive(:find_by_id).and_return(user)
|
191
|
-
|
192
|
-
|
193
|
-
|
185
|
+
send_message("foo++")
|
186
|
+
send_command("karma modified foo")
|
187
|
+
expect(replies.last).to eq(user.name)
|
194
188
|
end
|
195
189
|
end
|
196
190
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lita-karma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jimmy Cuadra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06
|
11
|
+
date: 2013-07-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lita
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -103,7 +103,6 @@ extra_rdoc_files: []
|
|
103
103
|
files:
|
104
104
|
- .gitignore
|
105
105
|
- .travis.yml
|
106
|
-
- CHANGELOG.md
|
107
106
|
- Gemfile
|
108
107
|
- README.md
|
109
108
|
- Rakefile
|
data/CHANGELOG.md
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
# Changelog
|
2
|
-
|
3
|
-
## 0.0.3 (June 23, 2013)
|
4
|
-
|
5
|
-
* Added command help.
|
6
|
-
* When a user attempts to modify a term and is rate limited, Lita no longer also displays the term's current karma.
|
7
|
-
|
8
|
-
## 0.0.2 (June 22, 2013)
|
9
|
-
|
10
|
-
* Added term linking.
|
11
|
-
* Added `karma modified` command.
|
12
|
-
* Added rate limiting via `config.handlers.karma.cooldown`.
|
13
|
-
|
14
|
-
## 0.0.1 (June 15, 2003)
|
15
|
-
|
16
|
-
Initial release.
|