diff --git a/.ruby-version b/.ruby-version index 8e8299d..e70b452 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.4.2 +2.6.0 diff --git a/.travis.yml b/.travis.yml index 6585fb6..3fc4783 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,15 @@ rvm: -- 2.5.1 +- 2.6.0 bundler_args: "--without debug development" cache: bundler +addons: + apt: + sources: + - ubuntu-sdk-team + packages: + - libqt5webkit5-dev + - qtdeclarative5-dev + chrome: stable before_script: - export SECRET_KEY_BASE="$(bundle exec rake secret)" - cp .sample.env .env @@ -9,5 +17,12 @@ before_script: - bundle exec rake db:create - bundle exec rake db:migrate - export DISPLAY=:99.0 -- sh -e /etc/init.d/xvfb start -env: SECRET_KEY_BASE="$(bundle exec rake secret)" +env: + global: + - QMAKE=/usr/lib/x86_64-linux-gnu/qt5/bin/qmake + - SECRET_KEY_BASE="$(bundle exec rake secret)" +services: + - postgresql + - xvfb +script: bundle exec rake + diff --git a/Gemfile b/Gemfile index 6a00bca..e4b8176 100644 --- a/Gemfile +++ b/Gemfile @@ -1,59 +1,62 @@ -source "https://rubygems.org" +# frozen_string_literal: true -ruby "2.5.1" +source 'https://rubygems.org' -gem "autoprefixer-rails" -gem "bourbon", "~> 4.2.0" -gem "coffee-rails", "~> 4.1.0" -gem "delayed_job_active_record" -gem "flutie" -gem "high_voltage" -gem "jquery-rails" -gem "neat", "~> 1.7.0" -gem "newrelic_rpm", ">= 3.9.8" -gem "normalize-rails", "~> 3.0.0" -gem "pg" -gem "puma" -gem "rack-canonical-host" -gem "rails", "~> 4.2" -gem "recipient_interceptor" -gem "sass-rails", "~> 5.0" -gem "simple_form" -gem "title" -gem "uglifier" -gem "telegram-bot-ruby" -gem "leaflet-rails" +ruby '2.6.0' + +gem 'autoprefixer-rails' +gem 'bourbon', '~> 4.2.0' +gem 'chronic' +gem 'coffee-rails', '~> 4.1.0' +gem 'delayed_job_active_record' +gem 'flutie' +gem 'high_voltage' +gem 'jquery-rails' +gem 'leaflet-rails' +gem 'neat', '~> 1.7.0' +gem 'newrelic_rpm', '>= 3.9.8' +gem 'normalize-rails', '~> 3.0.0' +gem 'pg' +gem 'puma' +gem 'rack-canonical-host' +gem 'rails', '~> 4.2' +gem 'recipient_interceptor' +gem 'sass-rails', '~> 5.0' +gem 'simple_form' +gem 'telegram-bot-ruby' +gem 'title' +gem 'uglifier' group :development do - gem "quiet_assets" - gem "refills" - gem "spring" - gem "spring-commands-rspec" - gem "web-console" + gem 'quiet_assets' + gem 'refills' + gem 'spring' + gem 'spring-commands-rspec' + gem 'web-console' end group :development, :test do - gem "awesome_print" - gem "bullet" - gem "bundler-audit", require: false - gem "dotenv-rails" - gem "factory_girl_rails" - gem "pry-byebug" - gem "pry-rails" - gem "rspec-rails", "~> 3.4.0" + gem 'awesome_print' + gem 'bullet' + gem 'bundler-audit', require: false + gem 'dotenv-rails' + gem 'factory_girl_rails' + gem 'pry-byebug' + gem 'pry-rails' + gem 'rspec-rails', '~> 3.4.0' end group :test do - gem "capybara-webkit", "~> 1.14.0" - gem "database_cleaner" - gem "formulaic" - gem "launchy" - gem "shoulda-matchers" - gem "simplecov", require: false - gem "timecop" - gem "webmock" + gem 'capybara-webkit' + gem 'database_cleaner' + gem 'formulaic' + gem 'launchy' + gem 'shoulda-matchers' + gem 'simplecov', require: false + gem 'timecop' + gem 'webmock' end group :staging, :production do - gem "rack-timeout" + gem 'rack-timeout' end diff --git a/Gemfile.lock b/Gemfile.lock index dee58e9..1f955df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -35,7 +35,7 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - addressable (2.5.2) + addressable (2.6.0) public_suffix (>= 2.0.2, < 4.0) arel (6.0.4) autoprefixer-rails (7.1.1) @@ -56,16 +56,18 @@ GEM bundler (~> 1.2) thor (~> 0.18) byebug (9.0.6) - capybara (2.13.0) + capybara (3.25.0) addressable - mime-types (>= 1.16) - nokogiri (>= 1.3.3) - rack (>= 1.0.0) - rack-test (>= 0.5.4) - xpath (~> 2.0) - capybara-webkit (1.14.0) - capybara (>= 2.3.0, < 2.14.0) + mini_mime (>= 0.1.3) + nokogiri (~> 1.8) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (~> 1.5) + xpath (~> 3.2) + capybara-webkit (1.15.1) + capybara (>= 2.3, < 4.0) json + chronic (0.10.2) coderay (1.1.1) coercible (1.0.0) descendants_tracker (~> 0.0.1) @@ -121,7 +123,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.1.0) + json (2.2.0) launchy (2.4.3) addressable (~> 2.3) leaflet-rails (1.0.3) @@ -132,9 +134,6 @@ GEM mail (2.7.1) mini_mime (>= 0.1.1) method_source (0.8.2) - mime-types (3.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2016.0521) mini_mime (1.0.1) mini_portile2 (2.4.0) minitest (5.11.3) @@ -143,7 +142,7 @@ GEM bourbon (>= 4.0) sass (>= 3.3) newrelic_rpm (4.2.0.334) - nokogiri (1.10.1) + nokogiri (1.10.3) mini_portile2 (~> 2.4.0) normalize-rails (3.0.3) pg (0.20.0) @@ -156,7 +155,7 @@ GEM pry (~> 0.10) pry-rails (0.3.6) pry (>= 0.10.4) - public_suffix (3.0.1) + public_suffix (3.1.1) puma (3.9.1) quiet_assets (1.1.0) railties (>= 3.1, < 5.0) @@ -195,6 +194,7 @@ GEM recipient_interceptor (0.1.2) mail refills (0.2.0) + regexp_parser (1.5.1) rspec-core (3.4.4) rspec-support (~> 3.4.0) rspec-expectations (3.4.0) @@ -270,8 +270,8 @@ GEM addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff - xpath (2.1.0) - nokogiri (~> 1.3) + xpath (3.2.0) + nokogiri (~> 1.8) PLATFORMS ruby @@ -282,7 +282,8 @@ DEPENDENCIES bourbon (~> 4.2.0) bullet bundler-audit - capybara-webkit (~> 1.14.0) + capybara-webkit + chronic coffee-rails (~> 4.1.0) database_cleaner delayed_job_active_record @@ -322,7 +323,7 @@ DEPENDENCIES webmock RUBY VERSION - ruby 2.5.1p57 + ruby 2.6.0p0 BUNDLED WITH - 1.16.2 + 1.17.2 diff --git a/config/locales/en.yml b/config/locales/en.yml index cc23466..0ed3415 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -51,8 +51,10 @@ en: /create_game to start a new game /archive_game to archive the current game /leave to leave a game that you joined + /set_time to add a time to the current game whatever: "Whatever, @%{username}" no_game: "There's no active game, @%{username}" + date: "The date is set to %{date}" game_created: | Game has been created, @%{username} You can join this game with /join @@ -62,6 +64,13 @@ en: Type /create_game to create a new game. You can specify the required number of players: */create_game 8*. + no_time: | + The chosen time could not be parsed to choose a time use + this command with a selected time, like: + /set_time at 7pm + /set_time next_tuesday + /set_time next thursday at 8pm + game_status: "There's [a game](%{game_url}) with *%{players} players*. Type /join to *join*." joined_game: "@%{username} joined the game. There are now %{players} players." left_game: "@%{username} left the game. There are now *%{players}* players." diff --git a/db/migrate/20190702161748_add_hour_and_minutes_to_game.rb b/db/migrate/20190702161748_add_hour_and_minutes_to_game.rb new file mode 100644 index 0000000..f59e07c --- /dev/null +++ b/db/migrate/20190702161748_add_hour_and_minutes_to_game.rb @@ -0,0 +1,5 @@ +class AddHourAndMinutesToGame < ActiveRecord::Migration + def change + add_column :games, :datetime, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index 7223bd8..b6ce7ee 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160422153232) do +ActiveRecord::Schema.define(version: 20190702161748) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -52,6 +52,7 @@ t.datetime "updated_at", null: false t.uuid "uuid", default: "uuid_generate_v4()" t.datetime "archived_at" + t.datetime "datetime" end create_table "locations", force: :cascade do |t| diff --git a/lib/pickup_bot.rb b/lib/pickup_bot.rb index b8552dd..9b9ba5d 100644 --- a/lib/pickup_bot.rb +++ b/lib/pickup_bot.rb @@ -35,6 +35,8 @@ def handle_text(message) PickupBot::Commands::Join.run(telegram_bot, message) when /leave/ PickupBot::Commands::Leave.run(telegram_bot, message) + when /set_time/ + PickupBot::Commands::SetTime.run(telegram_bot, message) else PickupBot::Commands::Error.run(telegram_bot, message, exception) end diff --git a/lib/pickup_bot/commands.rb b/lib/pickup_bot/commands.rb index e6fe071..d94029d 100644 --- a/lib/pickup_bot/commands.rb +++ b/lib/pickup_bot/commands.rb @@ -9,3 +9,4 @@ module PickupBot::Commands require 'pickup_bot/commands/leave' require 'pickup_bot/commands/set_location' require 'pickup_bot/commands/status' +require 'pickup_bot/commands/set_time' diff --git a/lib/pickup_bot/commands/set_time.rb b/lib/pickup_bot/commands/set_time.rb new file mode 100644 index 0000000..21b865f --- /dev/null +++ b/lib/pickup_bot/commands/set_time.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module PickupBot::Commands + class SetTime + def self.run(telegram_bot, message) + new(telegram_bot, message).run + end + + def initialize(telegram_bot, message) + @telegram_bot = telegram_bot + @message = message + end + + def run + return no_game_message(message) unless game_exists? + + if datetime && current_game.update(datetime: datetime) + telegram_bot.api.send_message( + chat_id: message.chat.id, + text: I18n.t('bot.date', date: I18n.l(datetime, format: :long)) + ) + else + telegram_bot.api.send_message( + chat_id: message.chat.id, + text: I18n.t('bot.no_time') + ) + end + end + + private + + attr_reader :telegram_bot, :message + + def no_game_message(message) + telegram_bot.api.send_message( + chat_id: message.chat.id, + text: I18n.t('bot.no_game', username: username) + ) + end + + def game_exists? + !!current_game + end + + def current_game + @game ||= Game.active.find_by_chat_id(@message.chat.id) + end + + def players + if current_game.required_players > 0 + "#{current_game.players.count} / #{current_game.required_players}" + else + current_game.players.count.to_s + end + end + + def username + message.from.username || message.from.first_name + end + + def game_date? + current_game.blank? + end + + def datetime + @datetime ||= @message.text + .then { |x| x.split(' ', 2).last.strip } + .then { |x| Chronic.parse(x) } + end + end +end diff --git a/spec/lib/pickup_bot/commands/set_time_spec.rb b/spec/lib/pickup_bot/commands/set_time_spec.rb new file mode 100644 index 0000000..babab71 --- /dev/null +++ b/spec/lib/pickup_bot/commands/set_time_spec.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'telegram/bot' +require 'pickup_bot' + +feature 'sit time to a game' do + let(:bot) { Telegram::Bot::Client.new('fake-token') } + let(:pickup_bot) { PickupBot.new(bot) } + let(:telegram_user) { Telegram::Bot::Types::User.new(user_params) } + let(:telegram_chat) { Telegram::Bot::Types::Chat.new(chat_params) } + let(:existing_game) { Game.new(chat_id: fake_chat_id) } + + before :each do + stub_request(:any, /api.telegram.org/).to_return(status: 200, body: '[]', headers: {}) + end + + context 'no game currently exists' do + scenario 'player tries to add a time' do + message = Telegram::Bot::Types::Message.new(message_params('/set_time 06:00')) + + pickup_bot.run(message) + + expect(a_request(:post, 'https://api.telegram.org/botfake-token/sendMessage') + .with(body: { + 'chat_id' => '123', + 'text' => I18n.t( + 'bot.no_game', + username: user_params[:username] + ) + })).to have_been_made.times(1) + end + end + + context 'an active game exists' do + before do + Game.create(chat_id: fake_chat_id, required_players: 5) + end + + scenario 'set the time to next week' do + message = Telegram::Bot::Types::Message.new(message_params('/set_time next tuesday at 7pm')) + + pickup_bot.run(message) + + expect(a_request(:post, 'https://api.telegram.org/botfake-token/sendMessage') + .with(body: { + 'chat_id' => '123', + 'text' => "The date is set to #{next_tuesday} 19:00" + })).to have_been_made.times(1) + end + + scenario 'set the time to 7pm' do + date = Date.today + message = Telegram::Bot::Types::Message.new(message_params('/set_time 7pm')) + + pickup_bot.run(message) + + expect(a_request(:post, 'https://api.telegram.org/botfake-token/sendMessage') + .with(body: { + 'chat_id' => '123', + 'text' => "The date is set to #{output_date(date)} 19:00" + })).to have_been_made.times(1) + end + + scenario 'return a message when the date could not be formatted right' do + message = Telegram::Bot::Types::Message.new(message_params('/set_time ')) + + pickup_bot.run(message) + + expect(a_request(:post, 'https://api.telegram.org/botfake-token/sendMessage') + .with(body: { + 'chat_id' => '123', + 'text' => <<~HEREDOC + The chosen time could not be parsed to choose a time use + this command with a selected time, like: + /set_time at 7pm + /set_time next_tuesday + /set_time next thursday at 8pm + HEREDOC + })).to have_been_made.times(1) + end + end + + def next_tuesday + today = Date.today + + (1..7).find { |t| (today + t).tuesday? } + .then { |days| today + days } + .then { |date| output_date(date) } + end + + def output_date(date) + "#{Date::MONTHNAMES[date.month]} #{date.mday}, #{date.year}" + end +end