Skip to content

Commit

Permalink
Added draw to, closes #169.
Browse files Browse the repository at this point in the history
  • Loading branch information
dblock committed Aug 19, 2018
1 parent 47c5928 commit 66274c1
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 69 deletions.
5 changes: 2 additions & 3 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2018-08-11 15:57:07 -0400 using RuboCop version 0.55.0.
# on 2018-08-19 18:30:30 -0400 using RuboCop version 0.55.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 4
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyleAlignWith, AutoCorrect, Severity.
# SupportedStylesAlignWith: keyword, variable, start_of_line
Layout/EndAlignment:
Exclude:
- 'slack-gamebot/commands/register.rb'
- 'slack-gamebot/commands/subscription.rb'
- 'slack-gamebot/commands/team.rb'
- 'slack-gamebot/models/user.rb'

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Change Log

* [#169](https://github.com/dblock/slack-gamebot/issues/169): Added `draw to`, similar to `lost to` - [@dblock](https://github.com/dblock).
* [#154](https://github.com/dblock/slack-gamebot/issues/154): Added `unsubscribe` to cancel a paid subscription - [@dblock](https://github.com/dblock).
* [#168](https://github.com/dblock/slack-gamebot/issues/168): Added `challenge?` to display elo stake - [@dblock](https://github.com/dblock).
* [#161](https://github.com/dblock/slack-gamebot/pull/161): Added `MONGODB_URI` env as production mongoid client uri - [@dsantosmerino](https://github.com/dsantosmerino).
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ You can record a loss without a challenge.

![](screenshots/lost_to.gif)

You can also record scores and record lost matches with multiple players.
You can also record scores and record lost matches without a challenge including with multiple players.

```
gamebot lost to @WangHoe @ZhangJike with @DengYaping 5:21
Expand All @@ -171,6 +171,20 @@ gamebot draw 2:2
Match has been recorded. Victor Barna tied with Zhang Jike with a score of 2:2.
```

You can also record scores and ties without a challenge.

```
gamebot draw to @VictorBarna
Match is a draw, waiting to hear from Victor Barna.
gamebot draw 2:2
Match is a draw! Wang Hoe tied with Victor Barna with the score of 2:2.
```

You can also record scores and have multiple players.

#### gamebot resigned [to <opponent> [with <teammate>]]

Records your resignation, which is a special kind of `lost` without a score.
Expand Down
6 changes: 5 additions & 1 deletion slack-gamebot/commands/challenges.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ class Challenges < SlackRubyBot::Commands::Base
subscribed_command 'challenges' do |client, data, _match|
challenges = ::Challenge.where(
channel: data.channel,
:state.in => [ChallengeState::PROPOSED, ChallengeState::ACCEPTED]
:state.in => [
ChallengeState::PROPOSED,
ChallengeState::ACCEPTED,
ChallengeState::DRAWN
]
).asc(:created_at)

if challenges.any?
Expand Down
57 changes: 54 additions & 3 deletions slack-gamebot/commands/draw.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,60 @@ class Draw < SlackRubyBot::Commands::Base

subscribed_command 'draw' do |client, data, match|
challenger = ::User.find_create_or_update_by_slack_id!(client, data.user)
challenge = ::Challenge.find_by_user(client.owner, data.channel, challenger, [ChallengeState::PROPOSED, ChallengeState::ACCEPTED])
scores = Score.parse(match['expression']) if match['expression']
if challenge
expression = match['expression'] if match['expression']
arguments = expression.split.reject(&:blank?) if expression

scores = nil
opponents = []
teammates = [challenger]
multi_player = expression&.include?(' with ')

current = :scores
while arguments&.any?
argument = arguments.shift
case argument
when 'to' then
current = :opponents
when 'with' then
current = :teammates
else
if current == :opponents
opponents << ::User.find_by_slack_mention!(client, argument)
current = :scores unless multi_player
elsif current == :teammates
teammates << ::User.find_by_slack_mention!(client, argument)
current = :scores if opponents.count == teammates.count
else
scores ||= []
scores << Score.check(argument)
end
end
end

challenge = ::Challenge.find_by_user(client.owner, data.channel, challenger, [
ChallengeState::PROPOSED,
ChallengeState::ACCEPTED,
ChallengeState::DRAWN
])

if !(teammates & opponents).empty?
client.say(channel: data.channel, text: 'You cannot draw to yourself!', gif: 'loser')
logger.info "Cannot draw to yourself: #{client.owner} - #{match}"
elsif opponents.any? && (challenge.nil? || (challenge.challengers != opponents && challenge.challenged != opponents))
challenge = ::Challenge.create!(
team: client.owner, channel: data.channel,
created_by: challenger, updated_by: challenger,
challengers: teammates, challenged: opponents,
draw: [challenger], draw_scores: scores,
state: ChallengeState::DRAWN
)
messages = [
"Match is a draw, waiting to hear from #{(challenge.challengers + challenge.challenged - challenge.draw).map(&:user_name).and}.",
challenge.draw_scores? ? "Recorded #{Score.scores_to_string(challenge.draw_scores)}." : nil
].compact
client.say(channel: data.channel, text: messages.join(' '), gif: 'tie')
logger.info "DRAW TO: #{client.owner} - #{challenge}"
elsif challenge
if challenge.draw.include?(challenger)
challenge.update_attributes!(draw_scores: scores) if scores
messages = [
Expand Down
2 changes: 1 addition & 1 deletion slack-gamebot/commands/help.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Help < SlackRubyBot::Commands::Base
cancel: cancel a previous challenge
lost [to <opponent>] [score, ...]: record your loss
resigned [to <opponent>]: record a resignation
draw: record a tie
draw [to <opponent>] [score, ...]: record a tie
taunt <opponent> [<opponent> ...]: taunt players
rank [<player> ...]: rank a player or a list of players
matches [number|infinity]: show this season's matches
Expand Down
3 changes: 2 additions & 1 deletion slack-gamebot/models/challenge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,10 @@ def winners_and_losers_for_resigned(loser)

def draw!(player, scores = nil)
raise SlackGamebot::Error, 'Challenge must first be accepted.' if state == ChallengeState::PROPOSED
raise SlackGamebot::Error, "Challenge has already been #{state}." unless state == ChallengeState::ACCEPTED
raise SlackGamebot::Error, "Challenge has already been #{state}." unless state == ChallengeState::ACCEPTED || state == ChallengeState::DRAWN
raise SlackGamebot::Error, "Already recorded a draw from #{player.user_name}." if draw.include?(player)
draw << player
update_attributes!(state: ChallengeState::DRAWN)
update_attributes!(draw_scores: scores) if scores
return if draw.count != (challenged.count + challengers.count)
# in a draw, winners have a lower original elo
Expand Down
1 change: 1 addition & 0 deletions slack-gamebot/models/challenge_state.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ class ChallengeState
define :ACCEPTED, 'accepted'
define :DECLINED, 'declined'
define :CANCELED, 'canceled'
define :DRAWN, 'drawing'
define :PLAYED, 'played'
end
6 changes: 5 additions & 1 deletion slack-gamebot/models/season.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ def create_user_ranks

def archive_challenges!
team.challenges.where(
:state.in => [ChallengeState::PROPOSED, ChallengeState::ACCEPTED]
:state.in => [
ChallengeState::PROPOSED,
ChallengeState::ACCEPTED,
ChallengeState::DRAWN
]
).set(
state: ChallengeState::CANCELED,
updated_by_id: created_by&.id
Expand Down
194 changes: 136 additions & 58 deletions spec/slack-gamebot/commands/draw_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,77 +4,155 @@
let!(:team) { Fabricate(:team) }
let(:app) { SlackGamebot::Server.new(team: team) }
let(:client) { app.send(:client) }
let(:challenged) { Fabricate(:user, user_name: 'username') }
let!(:challenge) { Fabricate(:challenge, challenged: [challenged]) }
before do
challenge.accept!(challenged)
end
it 'draw' do
expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match is a draw, waiting to hear from #{challenge.challengers.map(&:user_name).and}."
)
challenge.reload
expect(challenge.state).to eq ChallengeState::ACCEPTED
expect(challenge.draw).to eq challenge.challenged
end
it 'draw with a score' do
expect(message: "#{SlackRubyBot.config.user} draw 2:2", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match is a draw, waiting to hear from #{challenge.challengers.map(&:user_name).and}. Recorded the score of 2:2."
)
challenge.reload
expect(challenge.state).to eq ChallengeState::ACCEPTED
expect(challenge.draw).to eq challenge.challenged
expect(challenge.draw_scores?).to be true
expect(challenge.draw_scores).to eq [[2, 2]]
end
context 'confirmation' do
context 'with a challenge' do
let(:challenged) { Fabricate(:user, user_name: 'username') }
let!(:challenge) { Fabricate(:challenge, challenged: [challenged]) }
before do
challenge.draw!(challenge.challengers.first)
challenge.accept!(challenged)
end
it 'confirmed' do
it 'draw' do
expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match has been recorded! #{challenge.challengers.map(&:user_name).and} tied with #{challenge.challenged.map(&:user_name).and}."
"Match is a draw, waiting to hear from #{challenge.challengers.map(&:user_name).and}."
)
challenge.reload
expect(challenge.state).to eq ChallengeState::PLAYED
expect(challenge.draw).to eq challenge.challenged + challenge.challengers
expect(challenge.state).to eq ChallengeState::DRAWN
expect(challenge.draw).to eq challenge.challenged
end
it 'with score' do
expect(message: "#{SlackRubyBot.config.user} draw 3:3", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match has been recorded! #{challenge.challengers.map(&:user_name).and} tied with #{challenge.challenged.map(&:user_name).and} with the score of 3:3."
it 'draw with a score' do
expect(message: "#{SlackRubyBot.config.user} draw 2:2", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match is a draw, waiting to hear from #{challenge.challengers.map(&:user_name).and}. Recorded the score of 2:2."
)
challenge.reload
expect(challenge.match.scores).to eq [[3, 3]]
expect(challenge.state).to eq ChallengeState::DRAWN
expect(challenge.draw).to eq challenge.challenged
expect(challenge.draw_scores?).to be true
expect(challenge.draw_scores).to eq [[2, 2]]
end
context 'confirmation' do
before do
challenge.draw!(challenge.challengers.first)
end
it 'confirmed' do
expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match has been recorded! #{challenge.challengers.map(&:user_name).and} tied with #{challenge.challenged.map(&:user_name).and}."
)
challenge.reload
expect(challenge.state).to eq ChallengeState::PLAYED
expect(challenge.draw).to eq challenge.challenged + challenge.challengers
end
it 'with score' do
expect(message: "#{SlackRubyBot.config.user} draw 3:3", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match has been recorded! #{challenge.challengers.map(&:user_name).and} tied with #{challenge.challenged.map(&:user_name).and} with the score of 3:3."
)
challenge.reload
expect(challenge.match.scores).to eq [[3, 3]]
end
it 'with invalid score' do
expect(message: "#{SlackRubyBot.config.user} draw 21:15", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
'In a tie both sides must score the same number of points.'
)
end
it 'draw with scores' do
expect(message: "#{SlackRubyBot.config.user} draw 21:15 15:21", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match has been recorded! #{challenge.challengers.map(&:user_name).and} tied with #{challenge.challenged.map(&:user_name).and} with the scores of 15:21 21:15."
)
challenge.reload
expect(challenge.match.scores).to eq [[21, 15], [15, 21]]
end
end
it 'with invalid score' do
expect(message: "#{SlackRubyBot.config.user} draw 21:15", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
'In a tie both sides must score the same number of points.'
it 'draw already confirmed' do
challenge.draw!(challenge.challenged.first)
expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match is a draw, still waiting to hear from #{challenge.challengers.map(&:user_name).and}."
)
end
it 'draw with scores' do
expect(message: "#{SlackRubyBot.config.user} draw 21:15 15:21", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match has been recorded! #{challenge.challengers.map(&:user_name).and} tied with #{challenge.challenged.map(&:user_name).and} with the scores of 15:21 21:15."
it 'does not update a previously lost match' do
challenge.lose!(challenge.challenged.first)
expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
'No challenge to draw!'
)
end
it 'does not update a previously won match' do
challenge.lose!(challenge.challengers.first)
expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
'No challenge to draw!'
)
challenge.reload
expect(challenge.match.scores).to eq [[21, 15], [15, 21]]
end
end
it 'draw already confirmed' do
challenge.draw!(challenge.challenged.first)
expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match is a draw, still waiting to hear from #{challenge.challengers.map(&:user_name).and}."
)
end
it 'does not update a previously lost match' do
challenge.lose!(challenge.challenged.first)
expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
'No challenge to draw!'
)
end
it 'does not update a previously won match' do
challenge.lose!(challenge.challengers.first)
expect(message: "#{SlackRubyBot.config.user} draw", user: challenged.user_id, channel: challenge.channel).to respond_with_slack_message(
'No challenge to draw!'
)
context 'without a challenge' do
let(:winner) { Fabricate(:user) }
let(:loser) { Fabricate(:user, user_name: 'username') }
it 'draw to' do
expect do
expect do
expect(message: "#{SlackRubyBot.config.user} draw to #{winner.user_name}", user: loser.user_id, channel: 'channel').to respond_with_slack_message(
"Match is a draw, waiting to hear from #{winner.user_name}."
)
end.to change(Challenge, :count).by(1)
end.to_not change(Match, :count)
challenge = Challenge.desc(:_id).first
expect(challenge.state).to eq ChallengeState::DRAWN
expect(challenge.draw).to eq [loser]
end
it 'draw with a score' do
expect do
expect do
expect(message: "#{SlackRubyBot.config.user} draw to #{winner.user_name} 2:2", user: loser.user_id, channel: 'channel').to respond_with_slack_message(
"Match is a draw, waiting to hear from #{winner.user_name}. Recorded the score of 2:2."
)
end.to change(Challenge, :count).by(1)
end.to_not change(Match, :count)
challenge = Challenge.desc(:_id).first
expect(challenge.state).to eq ChallengeState::DRAWN
expect(challenge.draw).to eq [loser]
expect(challenge.draw_scores?).to be true
expect(challenge.draw_scores).to eq [[2, 2]]
end
context 'confirmation' do
before do
allow_any_instance_of(Slack::Web::Client).to receive(:users_info).and_return(nil)
end
let!(:challenge) do
Challenge.create!(
team: loser.team, channel: 'channel',
created_by: loser, updated_by: loser,
challengers: [loser], challenged: [winner],
draw: [loser], draw_scores: [],
state: ChallengeState::DRAWN
)
end
it 'still waiting' do
expect(message: "#{SlackRubyBot.config.user} draw", user: loser.user_id, channel: 'channel').to respond_with_slack_message(
"Match is a draw, still waiting to hear from #{winner.user_name}."
)
end
it 'confirmed' do
expect(message: "#{SlackRubyBot.config.user} draw", user: winner.user_id, channel: 'channel').to respond_with_slack_message(
"Match has been recorded! #{loser.user_name} tied with #{winner.user_name}."
)
challenge.reload
expect(challenge.state).to eq ChallengeState::PLAYED
expect(challenge.draw).to eq [loser, winner]
end
it 'with score' do
expect(message: "#{SlackRubyBot.config.user} draw 3:3", user: winner.user_id, channel: 'channel').to respond_with_slack_message(
"Match has been recorded! #{loser.user_name} tied with #{winner.user_name} with the score of 3:3."
)
challenge.reload
expect(challenge.match.scores).to eq [[3, 3]]
end
it 'with invalid score' do
expect(message: "#{SlackRubyBot.config.user} draw 21:15", user: winner.user_id, channel: 'channel').to respond_with_slack_message(
'In a tie both sides must score the same number of points.'
)
end
it 'draw with scores' do
expect(message: "#{SlackRubyBot.config.user} draw 21:15 15:21", user: winner.user_id, channel: challenge.channel).to respond_with_slack_message(
"Match has been recorded! #{loser.user_name} tied with #{winner.user_name} with the scores of 15:21 21:15."
)
challenge.reload
expect(challenge.match.scores).to eq [[21, 15], [15, 21]]
end
end
end
end

0 comments on commit 66274c1

Please sign in to comment.