artaius 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +2 -0
- data/LICENSE +19 -0
- data/README.md +19 -0
- data/Rakefile +3 -0
- data/artaius.gemspec +36 -0
- data/bin/artaius +5 -0
- data/config/locales/en.yml +40 -0
- data/config/locales/ru.yml +47 -0
- data/config/plugins/.gitignore +1 -0
- data/db/.gitignore +1 -0
- data/db/migrations/001_create_players.rb +35 -0
- data/lib/artaius.rb +16 -0
- data/lib/artaius/bot.rb +50 -0
- data/lib/artaius/database.rb +36 -0
- data/lib/artaius/models/player.rb +16 -0
- data/lib/artaius/plugins/archivarius.rb +62 -0
- data/lib/artaius/plugins/identify.rb +56 -0
- data/lib/artaius/plugins/mixer.rb +335 -0
- data/lib/artaius/version.rb +3 -0
- data/spec/lib/artaius/bot_spec.rb +54 -0
- data/spec/lib/artaius/plugins/archivarius_spec.rb +59 -0
- data/spec/lib/artaius/plugins/identify_spec.rb +94 -0
- data/spec/lib/artaius/plugins/mixer_spec.rb +199 -0
- data/spec/lib/artaius/version_spec.rb +0 -0
- data/spec/spec_helper.rb +7 -0
- data/tasks/db.rake +15 -0
- data/tasks/environment.rake +3 -0
- data/tasks/test.rake +6 -0
- metadata +213 -0
@@ -0,0 +1,335 @@
|
|
1
|
+
module Artaius
|
2
|
+
module Plugins
|
3
|
+
# The plugins allows to create game mixes in IRC.
|
4
|
+
class Mixer
|
5
|
+
include Cinch::Plugin
|
6
|
+
|
7
|
+
# Internal: Create a new game.
|
8
|
+
#
|
9
|
+
# players - The Enumerable of players (Set or Array).
|
10
|
+
# limit - The Integer, describes the needed quantity of players, in
|
11
|
+
# order to start the game.
|
12
|
+
# time - The Time, when the game was created.
|
13
|
+
#
|
14
|
+
# Returns Game Struct object.
|
15
|
+
Game = Struct.new(:players, :limit, :time)
|
16
|
+
|
17
|
+
# Internal: Create a new gamer. Gamer is a player, which wants to play a
|
18
|
+
# mix.
|
19
|
+
#
|
20
|
+
# irc_nick - The String, representing gamer's IRC nickname.
|
21
|
+
# irc_authname - The String, representing gamer's IRC authname.
|
22
|
+
# role - The Integer, representing KAG player's role
|
23
|
+
#
|
24
|
+
# Returns Gamer object.
|
25
|
+
Gamer = Struct.new(:irc_nick, :irc_authname, :premium?, :role)
|
26
|
+
|
27
|
+
# Internal: Limit to be used, when the op didn't specify the number of
|
28
|
+
# players in the mix.
|
29
|
+
DEFAULT_LIMIT = 10
|
30
|
+
|
31
|
+
# Internal: Minimal number of slots needed to be able to play the game.
|
32
|
+
MIN_SLOTS = 2
|
33
|
+
|
34
|
+
# Internal: Delay between the beginning of game and its ending (in
|
35
|
+
# seconds).
|
36
|
+
PENDING_DELAY = 300
|
37
|
+
|
38
|
+
|
39
|
+
set react_on: :channel
|
40
|
+
|
41
|
+
match /#{I18n.mixer.m.game}(\s([2-9]|[1-9][0-9]))?$/,
|
42
|
+
method: :start_game,
|
43
|
+
use_suffix: false
|
44
|
+
|
45
|
+
# Internal: Create new game and add the creator to that game, so he/she
|
46
|
+
# will be the first player in that game.
|
47
|
+
#
|
48
|
+
# m - The recieved Message.
|
49
|
+
# limit - The Integer, which sets the limit of the players in the mix.
|
50
|
+
#
|
51
|
+
# Returns nothing.
|
52
|
+
def start_game(m, limit)
|
53
|
+
return if @game or !Player.exists?(m.user.authname)
|
54
|
+
|
55
|
+
@limit = if limit && !limit.empty?
|
56
|
+
limit.to_i
|
57
|
+
else
|
58
|
+
DEFAULT_LIMIT
|
59
|
+
end
|
60
|
+
|
61
|
+
@game = Game.new([], limit, Time.now)
|
62
|
+
@initiator = create_gamer(m)
|
63
|
+
add_player(m)
|
64
|
+
end
|
65
|
+
|
66
|
+
match /#{I18n.mixer.m.play}$/,
|
67
|
+
method: :add_player,
|
68
|
+
use_suffix: false
|
69
|
+
|
70
|
+
# Internal: Add a new player to the game.
|
71
|
+
#
|
72
|
+
# m - The recieved Message.
|
73
|
+
#
|
74
|
+
# Returns nothing.
|
75
|
+
def add_player(m)
|
76
|
+
return if !@game or !Player.exists?(m.user.authname)
|
77
|
+
|
78
|
+
unless @game.players.map(&:irc_authname).include?(m.user.authname)
|
79
|
+
@game.players << create_gamer(m)
|
80
|
+
|
81
|
+
need_players = @limit - @game.players.size
|
82
|
+
|
83
|
+
if ready_to_begin?
|
84
|
+
each_team { |blue, red| begin_game!(m, blue, red) }
|
85
|
+
else
|
86
|
+
m.reply I18n.mixer.players(show_players)
|
87
|
+
m.reply I18n.mixer.need_players(need_players)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
@timer.stop if @timer
|
93
|
+
|
94
|
+
@timer = Timer(PENDING_DELAY, :shots => 1) do
|
95
|
+
@game = nil
|
96
|
+
Channel(m.channel.name).send I18n.mixer.game_cancelled
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
match /#{I18n.mixer.m.cancel}$/,
|
101
|
+
method: :cancel,
|
102
|
+
use_suffix: false
|
103
|
+
|
104
|
+
# Internal: Remove the user from the game. If the user is the last user in
|
105
|
+
# the game, automatically revoke that game.
|
106
|
+
#
|
107
|
+
# m - The recieved Message.
|
108
|
+
#
|
109
|
+
# Returns nothing.
|
110
|
+
def cancel(m)
|
111
|
+
player = @game.players.find { |p| p.irc_authname == m.user.authname}
|
112
|
+
@game.players.delete(player)
|
113
|
+
|
114
|
+
@initiator = @game.players[0]
|
115
|
+
m.reply I18n.mixer.cancel(m.user.nick)
|
116
|
+
|
117
|
+
unless @initiator
|
118
|
+
@timer.stop
|
119
|
+
@game = nil
|
120
|
+
m.reply I18n.mixer.last_left
|
121
|
+
else
|
122
|
+
m.reply I18n.mixer.new_initiator(@initiator.irc_nick)
|
123
|
+
m.reply show_players
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
match /#{I18n.mixer.m.roster}$/,
|
128
|
+
method: :roster,
|
129
|
+
use_suffix: false
|
130
|
+
|
131
|
+
# Internal: Send message about current roster in the mix, if there is any.
|
132
|
+
#
|
133
|
+
# m - The recieved Message.
|
134
|
+
#
|
135
|
+
# Returns nothing.
|
136
|
+
def roster(m)
|
137
|
+
return unless @game
|
138
|
+
|
139
|
+
m.reply I18n.mixer.roster(show_players)
|
140
|
+
end
|
141
|
+
|
142
|
+
match /#{I18n.mixer.m.start}$/,
|
143
|
+
method: :force_start,
|
144
|
+
use_suffix: false
|
145
|
+
|
146
|
+
# Internal: Start the game by all means.
|
147
|
+
#
|
148
|
+
# m - The recieved Message.
|
149
|
+
#
|
150
|
+
# Returns nothing.
|
151
|
+
def force_start(m)
|
152
|
+
return unless @game && m.user.authname == @initiator.irc_authname
|
153
|
+
|
154
|
+
each_team { |blue, red| begin_game!(m, blue, red) }
|
155
|
+
end
|
156
|
+
|
157
|
+
match /#{I18n.mixer.m.slot}(\+|-)(\s([2-9]|[1-9][0-9]))?$/,
|
158
|
+
method: :slot_dispatcher,
|
159
|
+
use_suffix: false
|
160
|
+
|
161
|
+
# Internal: Add or remove slot from the current game.
|
162
|
+
#
|
163
|
+
# m - The recieved Message.
|
164
|
+
# sign - The + or - sign, indicates adding on removing a slot
|
165
|
+
# respectively.
|
166
|
+
# slots - The Integer, reperesenting number of slots to be added or
|
167
|
+
# removed from the game.
|
168
|
+
#
|
169
|
+
def slot_dispatcher(m, sign, slots)
|
170
|
+
return unless @game && m.user.authname == @initiator.irc_authname
|
171
|
+
|
172
|
+
case sign
|
173
|
+
when '+'
|
174
|
+
|
175
|
+
if slots
|
176
|
+
slots.to_i.times { add_slot }
|
177
|
+
m.reply I18n.mixer.n_slots_added(slots, slots_message)
|
178
|
+
else
|
179
|
+
if @limit >= MIN_SLOTS
|
180
|
+
add_slot
|
181
|
+
m.reply I18n.mixer.slot_added(slots_message)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
when '-'
|
186
|
+
|
187
|
+
if slots
|
188
|
+
removed_slots = 0
|
189
|
+
slots.to_i.times { |i|
|
190
|
+
remove_slot
|
191
|
+
removed_slots = i+1
|
192
|
+
break unless @limit > MIN_SLOTS
|
193
|
+
}
|
194
|
+
m.reply I18n.mixer.n_slots_removed(removed_slots, slots_message)
|
195
|
+
else
|
196
|
+
if @limit > MIN_SLOTS
|
197
|
+
remove_slot
|
198
|
+
m.reply I18n.mixer.slot_removed(slots_message)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
if ready_to_begin?
|
205
|
+
each_team { |blue, red| begin_game!(m, blue, red) }
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
match /#{I18n.mixer.m.slots}$/,
|
210
|
+
method: :slots,
|
211
|
+
use_suffix: false
|
212
|
+
|
213
|
+
# Internal: Display information about slots of the current game.
|
214
|
+
#
|
215
|
+
# m - The recieved Message.
|
216
|
+
#
|
217
|
+
# Returns nothing.
|
218
|
+
def slots(m)
|
219
|
+
return unless @game
|
220
|
+
|
221
|
+
m.reply I18n.mixer.slot_stats(@game.players.size, @limit)
|
222
|
+
end
|
223
|
+
|
224
|
+
protected
|
225
|
+
|
226
|
+
# Internal: Show all players in the game. Colorize some nicks.
|
227
|
+
#
|
228
|
+
# Returns nothing.
|
229
|
+
def show_players
|
230
|
+
@game.players.map do |gamer|
|
231
|
+
colorize_nick(gamer)
|
232
|
+
end.join ', '
|
233
|
+
end
|
234
|
+
|
235
|
+
# Internal: Colorize the nick of given gamer. Based on role of the gamer.
|
236
|
+
# Golds are yellow, guards are green and normal are untouched.
|
237
|
+
#
|
238
|
+
# nick - The Gamer, which nick should be colorized.
|
239
|
+
#
|
240
|
+
# Returns colorized nick.
|
241
|
+
def colorize_nick(gamer)
|
242
|
+
if gamer[:premium?]
|
243
|
+
case gamer.role
|
244
|
+
when 2
|
245
|
+
Format(:green, gamer.irc_nick)
|
246
|
+
else
|
247
|
+
Format(:yellow, gamer.irc_nick)
|
248
|
+
end
|
249
|
+
else
|
250
|
+
gamer.irc_nick
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# Internal: Iterate over each team.
|
255
|
+
#
|
256
|
+
# Yields the Array of the Red team and the Array of the Blue team
|
257
|
+
# respectively.
|
258
|
+
#
|
259
|
+
# Returns nothing.
|
260
|
+
def each_team
|
261
|
+
players = @game.players.shuffle
|
262
|
+
|
263
|
+
blue = players.pop(@limit / 2)
|
264
|
+
red = players
|
265
|
+
|
266
|
+
yield blue, red
|
267
|
+
end
|
268
|
+
|
269
|
+
# Internal: Send messages with team rosters and destroy game object.
|
270
|
+
#
|
271
|
+
# m - The recieved Message.
|
272
|
+
# blue - The Array of Blue team.
|
273
|
+
# red - The Array of Red team.
|
274
|
+
#
|
275
|
+
# Returns nil.
|
276
|
+
def begin_game!(m, blue, red)
|
277
|
+
@timer.stop
|
278
|
+
|
279
|
+
blue = blue.map { |gamer| colorize_nick(gamer) }.join(', ')
|
280
|
+
red = red.map { |gamer| colorize_nick(gamer) }.join(', ')
|
281
|
+
|
282
|
+
m.reply I18n.mixer.blue_team(blue)
|
283
|
+
m.reply I18n.mixer.red_team(red)
|
284
|
+
m.reply I18n.mixer.glhf
|
285
|
+
|
286
|
+
@game = nil
|
287
|
+
end
|
288
|
+
|
289
|
+
# Internal: Display slots used/overall number information.
|
290
|
+
#
|
291
|
+
# Returns String.
|
292
|
+
def slots_message
|
293
|
+
I18n.mixer.slots_overall(@limit)
|
294
|
+
end
|
295
|
+
|
296
|
+
# Internal: Increments game slot.
|
297
|
+
#
|
298
|
+
# Returns incremented Integer, representing limit.
|
299
|
+
def add_slot
|
300
|
+
@limit += 1
|
301
|
+
end
|
302
|
+
|
303
|
+
# Internal: Decrements game slot.
|
304
|
+
#
|
305
|
+
# Returns decremented Integer, representing limit.
|
306
|
+
def remove_slot
|
307
|
+
@limit -= 1
|
308
|
+
end
|
309
|
+
|
310
|
+
# Internal: Check if the quantity of players reached the limit. If so,
|
311
|
+
# that means, we can say "Let's get ready to rumble!".
|
312
|
+
#
|
313
|
+
# Returns true if the quantity of players, that are in game, equals to the
|
314
|
+
# slot limit or false otherwise.
|
315
|
+
def ready_to_begin?
|
316
|
+
@game.players.size == @limit
|
317
|
+
end
|
318
|
+
|
319
|
+
# Internal: Create a new gamer. Gamers are persons, that want to play mix.
|
320
|
+
#
|
321
|
+
# Returns Gamer object.
|
322
|
+
def create_gamer(m)
|
323
|
+
player = Player.filter(:irc_authname => m.user.authname).first
|
324
|
+
|
325
|
+
Gamer.new(
|
326
|
+
m.user.nick,
|
327
|
+
m.user.authname,
|
328
|
+
player[:premium],
|
329
|
+
player[:role]
|
330
|
+
)
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
describe Artaius::Bot do
|
4
|
+
let(:bot) { Artaius::Bot.new }
|
5
|
+
|
6
|
+
describe 'bot IRC configuration' do
|
7
|
+
|
8
|
+
it 'must have correct nick' do
|
9
|
+
bot.config.nick.must_equal 'Artaius'
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'must have correct realname' do
|
13
|
+
bot.config.realname.must_equal 'Artaius Lucius'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'must have correct username' do
|
17
|
+
bot.config.user.must_equal 'Artaius Lucius'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'must have correct server to join' do
|
21
|
+
bot.config.server.must_equal 'irc.quakenet.org'
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'must have correct port of a server' do
|
25
|
+
bot.config.port.must_equal 6667
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'must have correct channels to join' do
|
29
|
+
bot.config.channels.must_equal %w{ #kag2d.ru }
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'plugins' do
|
33
|
+
|
34
|
+
describe 'identify plugin' do
|
35
|
+
it 'must contain identify' do
|
36
|
+
bot.config.plugins.plugins.must_include Artaius::Plugins::Identify
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'must have correct username option' do
|
40
|
+
bot.config.plugins
|
41
|
+
.options[Artaius::Plugins::Identify][:username].must_equal 'Artaius'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'must have password option' do
|
45
|
+
bot.config.plugins
|
46
|
+
.options[Artaius::Plugins::Identify][:password]
|
47
|
+
.must_be_instance_of String
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end # plugins
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative '../../../spec_helper'
|
2
|
+
|
3
|
+
describe Artaius::Plugins::Archivarius do
|
4
|
+
|
5
|
+
Archivarius = Artaius::Plugins::Archivarius
|
6
|
+
|
7
|
+
it 'must include required modules' do
|
8
|
+
Archivarius.must_include Cinch::Plugin
|
9
|
+
end
|
10
|
+
|
11
|
+
it "must use correct plugin's name" do
|
12
|
+
Archivarius.plugin_name.must_equal 'archivarius'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'must react on private messages' do
|
16
|
+
Archivarius.react_on.must_equal :private
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'matchers' do
|
20
|
+
let(:reg) { Archivarius.matchers.find { |m| m.method == :sign_up } }
|
21
|
+
|
22
|
+
it 'must have correct number of matchers' do
|
23
|
+
Archivarius.matchers.size.must_equal 1
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'reg matcher' do
|
27
|
+
it 'must exist' do
|
28
|
+
reg.wont_be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'must match the pattern' do
|
32
|
+
reg.pattern.must_match 'reg lol'
|
33
|
+
reg.pattern.must_match 'reg nick'
|
34
|
+
reg.pattern.must_match 'reg nickname'
|
35
|
+
reg.pattern.must_match 'reg awesomenick'
|
36
|
+
reg.pattern.must_match 'reg 10nicks'
|
37
|
+
reg.pattern.must_match 'reg nicks10'
|
38
|
+
reg.pattern.must_match 'reg ni10cks'
|
39
|
+
reg.pattern.must_match 'reg yadda_yadda'
|
40
|
+
reg.pattern.must_match 'reg IaMiDiOt'
|
41
|
+
|
42
|
+
reg.pattern.wont_match 'reg blah'
|
43
|
+
reg.pattern.wont_match 'reg me'
|
44
|
+
reg.pattern.wont_match 'reg i'
|
45
|
+
reg.pattern.wont_match 'reg averylongnicknamereally'
|
46
|
+
reg.pattern.wont_match 'reg supernick '
|
47
|
+
reg.pattern.wont_match 'reg fancy 10'
|
48
|
+
reg.pattern.wont_match 'reg ps[E]udo_player'
|
49
|
+
reg.pattern.wont_match "reg don'ttellmymother"
|
50
|
+
reg.pattern.wont_match 'reg yadda-yadda'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'does not have suffix' do
|
54
|
+
reg.suffix.must_be_nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|