kobot 1.0.0 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a9d8a789566c49cffabd4006d13502a6222a211d9193cdf58f0523c6dd89937
4
- data.tar.gz: 993fed11502b5149ece85da3d46d69f74169cf75c479221e56becf6b195d6d97
3
+ metadata.gz: 3eba14b1e3b076a368dd700c2ac46a9db4fd13bf57a58e29797de7db7eb16fc3
4
+ data.tar.gz: 1926333cefbaf4272ae117cc7afb12284ed85d6ff41aa96a02094904599b5312
5
5
  SHA512:
6
- metadata.gz: 3fe31ae275852246a0ba1656f829352ef204278d55f4578bc160d1917b8c07a8586b621eee42ed80e1c7535c0650de0cd8a7edcbb5b7da9f7359f2fa42a8a435
7
- data.tar.gz: e904e08fe88b5c616e5c7287e77a6de4d0e7b85dba1306e4c5dd8ab9ff24938437cd69dcfed4e2da0c6904ddbfb561a96a4aea92f0d61a268b2460591d950efe
6
+ metadata.gz: '08ad9cb2d55c2931a34ed4243884a7e16c189d1d914e5aa935cfd4d32fc3d3d7567cdaecdb53de54e980e84ec4c6e696761b7f9b15fb65e3a0eef11f829370e5'
7
+ data.tar.gz: f8662e41fe53d35df04b1b530f946621330124684d6bd955e6b18e53830efaef74c22db66818a8a3958aa8a4745b4d37090419f538962c3a4876e8dbb9b6c1d7
@@ -1,2 +1,24 @@
1
1
  ### v1.0.0
2
2
  - Initial release
3
+
4
+ ### v1.1.0
5
+ - Deprecated lower-case environment variables for credentials configuration
6
+ - Updated option help doc to indicate weekends and public holidays are skipped by default
7
+ - Updated required_ruby_version to be >= 2.4.0 to align with the webdrivers dependency
8
+
9
+ ### v1.2.0
10
+ - Added an option to allow forcibly running regardless of weekends or public holidays
11
+ - Added an ENV flag to allow requiring lib in local workspace for development purpose
12
+
13
+ ### v1.2.1
14
+ - Improved logging for better readability in logs
15
+ - Switched to builtin Logger#deprecate from Logger#warn for deprecations
16
+ - Renamed internal method to skip? from holiday? as it was meant for skipping any specified date
17
+
18
+ ### v1.2.2
19
+ - Improved login screen wait and logging
20
+ - Applied fix for offenses about empty lines and long lines reported by Rubocop
21
+
22
+ ### v1.2.3
23
+ - Improved validation logic to skip running due to weekend or intentional skips
24
+ - Refactored engine by reducing methods length based on reports by Rubocop
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
- # Kobot
1
+ # Kobot [![Gem Version](https://badge.fury.io/rb/kobot.svg)](https://badge.fury.io/rb/kobot)
2
2
 
3
3
  Kobot is a simple tool to automate the clock in or clock out operation on the web service
4
- provided by [KING OF TIME](kingtime.jp) by leveraging [Selenium WebDriver](selenium.dev),
5
- and with Google Gmail service email notification can also be sent to notify the results.
4
+ provided by [KING OF TIME](https://www.kingtime.jp/) by leveraging [Selenium WebDriver](
5
+ https://www.selenium.dev/), and with Google Gmail service email notifications can also be
6
+ sent to notify the results.
6
7
 
7
8
  It is meant for use only by one working under the discretionary labor system or flexible
8
9
  hours where the daily record is still required regardless of the actual start or end time
@@ -21,10 +22,11 @@ Tested on macOS Catelina and runs on Heroku platform so it works on unix-like sy
21
22
  #### Configuration
22
23
 
23
24
  By default it uses `~/.kobot` file locally to persist credentials for reuse, but all credentials
24
- can be overridden by setting environment variables, which is the recommended way of running the
25
- job on platforms like Heroku. When running for the first time, if none of the configuration file
26
- and ENV satisfies all required credentials, an interactive prompt will be displayed for setting,
27
- so there is no need to manually prepare the file beforehand. The content looks something like:
25
+ can also be supplied or overridden by runtime environment variables, which is the recommended way
26
+ of running as scheduled jobs on platforms like Heroku. When running for the first time, if neither
27
+ the configuration file nor ENV satisfies all required credentials, `an interactive prompt will be
28
+ displayed for initial setting, so there is no need to manually prepare the file beforehand`. The
29
+ content looks something like:
28
30
  ```property
29
31
  kot_id=xxx
30
32
  kot_password=xxx
@@ -34,6 +36,7 @@ Gmail account and password (or `app` password if MFA is on) are asked when notif
34
36
  gmail_id=xxx
35
37
  gmail_password=xxx
36
38
  ```
39
+ Note: use all upper-case letters for the above variables if using environment variables, e.g. `KOT_ID`.
37
40
 
38
41
  #### Google Chrome browser
39
42
 
@@ -51,17 +54,20 @@ $ gem install kobot
51
54
  ## Usage
52
55
 
53
56
  Get help doc:
54
- ```bash
57
+ ```
55
58
  $ kobot -h
56
59
  Usage: kobot [options]
57
- -c, --clock CLOCK The clock action: in, out
58
- -l, --loglevel [LEVEL] Specify log level: debug, info, warn, error. Default is info
60
+ -c, --clock CLOCK Required; the clock action option: in, out
61
+ -l, --loglevel [LEVEL] Specify log level: debug, info, warn, error; default is info
59
62
  -s, --skip [D1,D2,D3] Specify dates to skip clock in/out with date format YYYY-MM-DD and
60
- multiple values separated by comma, such as: 2020-05-01,2020-12-31
61
- -t, --to [TO] Email address to send notification to. By default it is sent to
63
+ multiple values separated by comma, such as: 2020-05-01,2020-12-31;
64
+ weekends and public holidays in Japan will be skipped by default
65
+ -t, --to [TO] Email address to send notification to; by default it is sent to
62
66
  the same self email account used in SMTP config as the sender
63
67
  -n, --notify Enable email notification
64
68
  -d, --dryrun Run the process without actual clock in/out
69
+ -f, --force Run the process forcibly regardless of weekends or public holidays;
70
+ must be used together with --dryrun to prevent mistaken operations
65
71
  -x, --headless Start browser in headless mode
66
72
  -g, --geolocation Allow browser to use geolocation
67
73
  -h, --help Show this help message
@@ -69,20 +75,25 @@ Usage: kobot [options]
69
75
  ```
70
76
 
71
77
  Dryrun to try out:
72
- ```bash
73
- $ kobot --clock in --dryrun
78
+ ```
79
+ $ kobot --clock in --notify --dryrun
74
80
  ```
75
81
 
76
82
  Clock in/out with email notification
77
- ```bash
78
- $ kobot --clock in --notify
79
- $ kobot --clock out --notify
83
+ ```
84
+ $ kobot --clock in --notify --headless
85
+ $ kobot --clock out --notify --headless
80
86
  ```
81
87
 
82
88
  Run the task with crontab
83
89
  ```cron
84
- 30 09 * * * user kobot --clock in --notify
85
- 30 18 * * * user kobot --clock out --notify
90
+ # Weekend and public holidays will be skipped by default
91
+ 30 09 * * * user kobot --cin -n -x
92
+ 30 18 * * * user kobot --cout -n -x
93
+
94
+ # Skip for weekdays planned to take leave for example
95
+ 30 09 * * * user kobot --cin -n -x -s 2020-09-01,2020-11-11
96
+ 30 18 * * * user kobot --cout -n -x -s 2020-09-01,2020-11-11
86
97
  ```
87
98
  On platforms like Heroku, an add-on called [Heroku Scheduler](https://elements.heroku.com/addons/scheduler) makes
88
99
  running scheduled tasks much easier. Tips: either clock in or clock out task can be scheduled multiple times in
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'bundler/setup'
4
5
  require 'kobot'
data/exe/kobot CHANGED
@@ -1,6 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'kobot'
4
+ begin
5
+ # For quickly tryout in local development
6
+ # without installing to system gems path
7
+ raise LoadError if ENV['KOBOT_DEV']
8
+
9
+ require 'kobot'
10
+ rescue LoadError
11
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
12
+ require 'kobot'
13
+ end
5
14
 
6
15
  Kobot.run
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
 
18
18
  spec.homepage = 'https://github.com/yuan-jiang/kobot'
19
19
  spec.license = 'MIT'
20
- spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
20
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
21
21
 
22
22
  spec.metadata['allowed_push_host'] = 'https://rubygems.org/'
23
23
  spec.metadata['homepage_uri'] = spec.homepage
@@ -14,8 +14,6 @@ require 'kobot/engine'
14
14
  # and with Google Gmail service email notification can also be sent to notify the results.
15
15
  module Kobot
16
16
  class << self
17
-
18
- # The entrance to run Kobot.
19
17
  def run
20
18
  configure
21
19
  Engine.new.start
@@ -33,6 +31,7 @@ module Kobot
33
31
  config.clock = options[:clock].to_sym
34
32
  config.loglevel = options[:loglevel]&.to_sym || :info
35
33
  config.dryrun = options[:dryrun]
34
+ config.force = options[:force]
36
35
  config.skip = options[:skip] || []
37
36
 
38
37
  config.kot_url = 'https://s2.kingtime.jp/independent/recorder/personal/'
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobot
4
-
5
4
  # Configuration definition includes static ones hardcoded and
6
5
  # dynamic ones that can be specified by command line options.
7
6
  class Config
@@ -9,7 +8,8 @@ module Kobot
9
8
  attr_accessor :clock,
10
9
  :loglevel,
11
10
  :skip,
12
- :dryrun
11
+ :dryrun,
12
+ :force
13
13
 
14
14
  attr_accessor :kot_url,
15
15
  :kot_timezone_offset,
@@ -4,7 +4,6 @@ module Kobot
4
4
  # Credentials include id and password to login to KOT and
5
5
  # Gmail SMTP id and password to send email notifications.
6
6
  class Credential
7
-
8
7
  class << self
9
8
  attr_accessor :kot_id,
10
9
  :kot_password,
@@ -30,7 +29,7 @@ module Kobot
30
29
  @credentials.each do |attr, value|
31
30
  send("#{attr}=".to_sym, value)
32
31
  end
33
- Kobot.logger.info('Credentials load successful')
32
+ Kobot.logger.info('Load credentials successful')
34
33
  Kobot.logger.debug(@credentials)
35
34
  end
36
35
 
@@ -41,18 +40,18 @@ module Kobot
41
40
  if File.exist? Config.credentials_file
42
41
  File.open(Config.credentials_file) do |file|
43
42
  file.each do |line|
44
- attr, value = line.chomp.split('=')
43
+ attr, value = line.strip.split('=')
45
44
  @credentials[attr] = value
46
45
  end
47
46
  end
48
47
  end
49
- @credentials['kot_id'] = ENV['kot_id'] if ENV['kot_id']
50
- @credentials['kot_password'] = ENV['kot_password'] if ENV['kot_password']
51
- @credentials['gmail_id'] = ENV['gmail_id'] if ENV['gmail_id']
52
- @credentials['gmail_password'] = ENV['gmail_password'] if ENV['gmail_password']
53
-
54
48
  required_credentials = %w[kot_id kot_password]
55
49
  required_credentials.concat %w[gmail_id gmail_password] if Config.gmail_notify_enabled
50
+ required_credentials.each do |attr|
51
+ Kobot.logger.deprecate(attr, attr.upcase) if ENV[attr]
52
+ env_attr_value = ENV[attr.upcase] || ENV[attr]
53
+ @credentials[attr] = env_attr_value if env_attr_value
54
+ end
56
55
  required_credentials.none? do |attr|
57
56
  credential = @credentials[attr]
58
57
  !credential || credential.strip.empty?
@@ -6,7 +6,6 @@ module Kobot
6
6
  # The core class that launches browser, logins to KOT, reads today
7
7
  # record, and conducts clock in or clock out action based on config.
8
8
  class Engine
9
-
10
9
  def initialize
11
10
  @now = Time.now.getlocal(Config.kot_timezone_offset)
12
11
  @today = @now.strftime(Config.kot_date_format)
@@ -25,28 +24,19 @@ module Kobot
25
24
  # System errors or any unknown exceptions occurred if any are
26
25
  # to be popped up and should be handled by the outside caller.
27
26
  def start
28
- if weekend?
29
- Kobot.logger.info("Today=#{@today} is weekend.")
30
- return
31
- end
32
- if holiday?
33
- Kobot.logger.info("Today=#{@today} is holiday.")
34
- return
35
- end
36
- unless %i[in out].include? Config.clock
37
- Kobot.logger.warn("Invalid clock operation: #{Config.clock}")
38
- return
39
- end
27
+ validate_today!
40
28
  launch_browser
41
29
  login
42
30
  read_today_record
43
- verify_today_record!
31
+ validate_today_record!
44
32
  if Config.clock == :in
45
33
  clock_in!
46
34
  else
47
35
  clock_out!
48
36
  end
49
37
  logout
38
+ rescue KotSkip => e
39
+ Kobot.logger.warn(e.message)
50
40
  rescue KotRecordError => e
51
41
  Kobot.logger.warn(e.message)
52
42
  Mailer.send(clock_notify_message(status: e.message))
@@ -65,11 +55,20 @@ module Kobot
65
55
  Mailer.send(clock_notify_message(status: e.message))
66
56
  logout
67
57
  ensure
68
- @browser&.quit
58
+ close_browser
69
59
  end
70
60
 
71
61
  private
72
62
 
63
+ def validate_today!
64
+ raise KotSkip, "Today=#{@today} is skipped as per: --skip=#{Config.skip}" if skip?
65
+
66
+ return unless weekend?
67
+ raise KotSkip, "Today=#{@today} is weekend" unless Config.force
68
+
69
+ Kobot.logger.info("[Force] should have exited: today=#{@today} is weekend")
70
+ end
71
+
73
72
  def launch_browser
74
73
  prefs = {
75
74
  profile: {
@@ -85,9 +84,18 @@ module Kobot
85
84
  Kobot.logger.info('Launch browser successful')
86
85
  end
87
86
 
87
+ def close_browser
88
+ return unless @browser
89
+
90
+ Kobot.logger.info('Close browser')
91
+ @browser.quit
92
+ end
93
+
88
94
  def login
89
- @browser.get @top_url
90
95
  Kobot.logger.info("Navigate to: #{@top_url}")
96
+ @browser.get @top_url
97
+ @wait.until { @browser.find_element(id: 'modal_window') }
98
+ Kobot.logger.info "Page title: #{@browser.title}"
91
99
  Kobot.logger.debug do
92
100
  "Login with id=#{Credential.kot_id} and password=#{Credential.kot_password}"
93
101
  end
@@ -104,13 +112,15 @@ module Kobot
104
112
  Kobot.logger.warn "Get geolocation failed: #{e.message}"
105
113
  end
106
114
  end
107
- Kobot.logger.info @browser.title
115
+ Kobot.logger.info "Page title: #{@browser.title}"
108
116
  end
109
117
 
110
118
  def logout
111
119
  if @browser.current_url.include? 'admin'
120
+ Kobot.logger.info('Logout from タイムカード page')
112
121
  @browser.find_element(css: 'div.htBlock-header_logoutButton').click
113
122
  else
123
+ Kobot.logger.info('Logout from Myレコーダー page')
114
124
  @wait.until { @browser.find_element(id: 'menu_icon') }.click
115
125
  @wait.until { @browser.find_element(link: 'ログアウト') }.click
116
126
  @browser.switch_to.alert.accept
@@ -119,6 +129,7 @@ module Kobot
119
129
  end
120
130
 
121
131
  def read_today_record
132
+ Kobot.logger.info('Navigate to タイムカード page')
122
133
  @wait.until { @browser.find_element(id: 'menu_icon') }.click
123
134
  @wait.until { @browser.find_element(link: 'タイムカード') }.click
124
135
 
@@ -131,8 +142,12 @@ module Kobot
131
142
  @kot_today = date_cell.text
132
143
  @kot_today_css_class = date_cell.attribute('class')
133
144
  @kot_today_type = tr.find_element(css: 'td.work_day_type').text
134
- @kot_today_clock_in = tr.find_element(css: 'td.start_end_timerecord[data-ht-sort-index="START_TIMERECORD"]').text
135
- @kot_today_clock_out = tr.find_element(css: 'td.start_end_timerecord[data-ht-sort-index="END_TIMERECORD"]').text
145
+ @kot_today_clock_in = tr.find_element(
146
+ css: 'td.start_end_timerecord[data-ht-sort-index="START_TIMERECORD"]'
147
+ ).text
148
+ @kot_today_clock_out = tr.find_element(
149
+ css: 'td.start_end_timerecord[data-ht-sort-index="END_TIMERECORD"]'
150
+ ).text
136
151
  Kobot.logger.debug do
137
152
  {
138
153
  kot_toay: @kot_today,
@@ -146,10 +161,23 @@ module Kobot
146
161
  end
147
162
  end
148
163
 
149
- def verify_today_record!
150
- raise KotRecordError, "Today=#{@today} is not found on kot." if @kot_today.strip.empty?
151
- raise KotRecordError, "Today=#{@today} is marked as weekend on kot: #{@kot_today}" if kot_weekend?
152
- raise KotRecordError, "Today=#{@today} is marked as public holiday on kot: #{@kot_today}" if kot_public_holiday?
164
+ def validate_today_record!
165
+ raise KotRecordError, "Today=#{@today} is not found on kot" if @kot_today.strip.empty?
166
+
167
+ if kot_weekend?
168
+ raise KotRecordError, "Today=#{@today} is marked as weekend on kot: #{@kot_today}" unless Config.force
169
+
170
+ Kobot.logger.info(
171
+ "[Force] should have exited: today=#{@today} is marked as weekend on kot: #{@kot_today}"
172
+ )
173
+ end
174
+
175
+ return unless kot_public_holiday?
176
+ raise KotRecordError, "Today=#{@today} is marked as public holiday on kot: #{@kot_today}" unless Config.force
177
+
178
+ Kobot.logger.info(
179
+ "[Force] should have exited: today=#{@today} is marked as public holiday on kot: #{@kot_today}"
180
+ )
153
181
  end
154
182
 
155
183
  def clock_in!
@@ -192,21 +220,25 @@ module Kobot
192
220
  end
193
221
 
194
222
  def click_clock_in_button
223
+ Kobot.logger.info("Navigate to: #{@top_url}")
195
224
  @browser.get @top_url
196
225
  clock_in_button = @wait.until { @browser.find_element(css: 'div.record-clock-in') }
197
226
  if Config.dryrun
198
227
  Kobot.logger.info('[Dryrun] clock in button (出勤) would have been clicked')
199
228
  else
229
+ Kobot.logger.info('Clicking the clock in button (出勤)')
200
230
  clock_in_button.click
201
231
  end
202
232
  end
203
233
 
204
234
  def click_clock_out_button
235
+ Kobot.logger.info("Navigate to: #{@top_url}")
205
236
  @browser.get @top_url
206
237
  clock_out_button = @wait.until { @browser.find_element(css: 'div.record-clock-out') }
207
238
  if Config.dryrun
208
239
  Kobot.logger.info('[Dryrun] clock out button (退勤) would have been clicked')
209
240
  else
241
+ Kobot.logger.info('Clicking the clock in button (退勤)')
210
242
  clock_out_button.click
211
243
  end
212
244
  end
@@ -215,7 +247,7 @@ module Kobot
215
247
  @now.saturday? || @now.sunday?
216
248
  end
217
249
 
218
- def holiday?
250
+ def skip?
219
251
  return false unless Config.skip
220
252
  return false unless Config.skip.respond_to? :include?
221
253
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobot
4
+ class KotSkip < StandardError
5
+ end
4
6
 
5
7
  class KotRecordError < StandardError
6
8
  end
@@ -10,5 +12,4 @@ module Kobot
10
12
 
11
13
  class KotClockOutError < StandardError
12
14
  end
13
-
14
15
  end
@@ -3,11 +3,9 @@
3
3
  require 'net/smtp'
4
4
 
5
5
  module Kobot
6
-
7
6
  # Responsible for sending email notifications in SMTP with Gmail
8
7
  class Mailer
9
8
  class << self
10
-
11
9
  # Sends email in preconfigured Gmail SMTP credential and to the recipient
12
10
  # configured by #{Config.gmail_notify_to} or self if not configured, with
13
11
  # email subject set by #{Config.gmail_notify_subject}.
@@ -3,33 +3,32 @@
3
3
  require 'optparse'
4
4
 
5
5
  module Kobot
6
-
7
6
  # Responsible for parsing the command line options for custom execution.
8
7
  class Option
9
8
  class << self
10
-
11
9
  # Parses command line options and returns a hash containing the options.
12
10
  def parse!
13
11
  options = {}
14
12
  opt_parser = OptionParser.new do |opt|
15
13
  opt.banner = "Usage: #{$PROGRAM_NAME} [options]"
16
14
 
17
- opt.on('-c', '--clock CLOCK', 'The clock action: in, out') do |clock|
15
+ opt.on('-c', '--clock CLOCK', 'Required; the clock action option: in, out') do |clock|
18
16
  options[:clock] = clock
19
17
  end
20
18
 
21
- opt.on('-l', '--loglevel [LEVEL]', 'Specify log level: debug, info, warn, error. Default is info') do |level|
19
+ opt.on('-l', '--loglevel [LEVEL]', 'Specify log level: debug, info, warn, error; default is info') do |level|
22
20
  options[:loglevel] = level
23
21
  end
24
22
 
25
23
  opt.on('-s', '--skip [D1,D2,D3]', Array,
26
24
  'Specify dates to skip clock in/out with date format YYYY-MM-DD and',
27
- 'multiple values separated by comma, such as: 2020-05-01,2020-12-31') do |skip|
25
+ 'multiple values separated by comma, such as: 2020-05-01,2020-12-31;',
26
+ 'weekends and public holidays in Japan will be skipped by default') do |skip|
28
27
  options[:skip] = skip
29
28
  end
30
29
 
31
30
  opt.on('-t', '--to [TO]',
32
- 'Email address to send notification to. By default it is sent to',
31
+ 'Email address to send notification to; by default it is sent to',
33
32
  'the same self email account used in SMTP config as the sender') do |to|
34
33
  options[:to] = to
35
34
  end
@@ -42,6 +41,12 @@ module Kobot
42
41
  options[:dryrun] = dryrun
43
42
  end
44
43
 
44
+ opt.on('-f', '--force',
45
+ 'Run the process forcibly regardless of weekends or public holidays;',
46
+ 'must be used together with --dryrun to prevent mistaken operations') do |force|
47
+ options[:force] = force
48
+ end
49
+
45
50
  opt.on('-x', '--headless', 'Start browser in headless mode') do |headless|
46
51
  options[:headless] = headless
47
52
  end
@@ -62,7 +67,12 @@ module Kobot
62
67
  end
63
68
  opt_parser.parse! ARGV
64
69
  raise OptionParser::MissingArgument, 'The clock option is required' if options[:clock].nil?
65
- raise OptionParser::InvalidArgument, 'The clock option must be either: in, out' unless %w[in out].include? options[:clock]
70
+ unless %w[in out].include? options[:clock]
71
+ raise OptionParser::InvalidArgument, 'The clock option must be either: in, out'
72
+ end
73
+ if options[:force] && !options[:dryrun]
74
+ raise OptionParser::InvalidArgument, 'Must specify --dryrun for forcibly running'
75
+ end
66
76
 
67
77
  options
68
78
  rescue OptionParser::MissingArgument, OptionParser::InvalidArgument => e
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobot
4
- VERSION = '1.0.0'
4
+ VERSION = '1.2.3'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kobot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Jiang
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-10 00:00:00.000000000 Z
11
+ date: 2020-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: webdrivers
@@ -100,7 +100,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: 2.3.0
103
+ version: 2.4.0
104
104
  required_rubygems_version: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - ">="