kobot 1.2.3 → 2.0.0

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: 3eba14b1e3b076a368dd700c2ac46a9db4fd13bf57a58e29797de7db7eb16fc3
4
- data.tar.gz: 1926333cefbaf4272ae117cc7afb12284ed85d6ff41aa96a02094904599b5312
3
+ metadata.gz: 0e2452d15afce1b0ec273b240ab062cc526adedcf7f11ecaf98e7cbe3590d393
4
+ data.tar.gz: 16f3f7fefbf4f30c621148841ba93aa57b8859cc5adb092edad02b3e6970e95c
5
5
  SHA512:
6
- metadata.gz: '08ad9cb2d55c2931a34ed4243884a7e16c189d1d914e5aa935cfd4d32fc3d3d7567cdaecdb53de54e980e84ec4c6e696761b7f9b15fb65e3a0eef11f829370e5'
7
- data.tar.gz: f8662e41fe53d35df04b1b530f946621330124684d6bd955e6b18e53830efaef74c22db66818a8a3958aa8a4745b4d37090419f538962c3a4876e8dbb9b6c1d7
6
+ metadata.gz: 4ec4604ff3d7486b48bc574f3130f2856787d6cb46644ba11cf1832569590dfe5a6159518a8474390a28500f77cd506da58531981df855db8e0d220627bb6486
7
+ data.tar.gz: 6f57f5fdf3e1f5186ca8ae60b6a482d555ca5a6e23004fec8f5bef324942001f7008fddfc96e93151c8ad88a9d17a99440bfef912540c2715a622433ea67b1a1
data/CHANGELOG.md CHANGED
@@ -22,3 +22,18 @@
22
22
  ### v1.2.3
23
23
  - Improved validation logic to skip running due to weekend or intentional skips
24
24
  - Refactored engine by reducing methods length based on reports by Rubocop
25
+
26
+ ### v1.2.4
27
+ - Changed to use boolean values for validation instead of raising exceptions
28
+
29
+ ### v1.2.5
30
+ - Added I18n support for both English and Japanese KOT UI
31
+
32
+ ### v1.3.0
33
+ - Added config option (-a, --auto-skip-without) to enable automatic skipping based on schedule
34
+ - Replaced deprecated options with capabilities option in Selenium driver initialization
35
+ - Refactored scattered attr_accessors and reduced instance variables count reported by Rubocop
36
+
37
+ ### v2.0.0
38
+ - Added -a, --auto-skip flag to support the enabling and disabling of auto-skipping feature
39
+ - Renamed the schedule config option from -a, --auto-skip-without to -w, --auto-skip-without
data/README.md CHANGED
@@ -62,6 +62,10 @@ Usage: kobot [options]
62
62
  -s, --skip [D1,D2,D3] Specify dates to skip clock in/out with date format YYYY-MM-DD and
63
63
  multiple values separated by comma, such as: 2020-05-01,2020-12-31;
64
64
  weekends and public holidays in Japan will be skipped by default
65
+ -a, --auto-skip Enable auto-skipping by reading schedule from the Time Card page
66
+ -w [SCHEDULE], Skip today if the provided text is not contained in the schedule
67
+ --auto-skip-without column on the Time Card page of KOT; by default 裁量労働 is used;
68
+ effective only if --auto-skip is passed to enable auto-skipping.
65
69
  -t, --to [TO] Email address to send notification to; by default it is sent to
66
70
  the same self email account used in SMTP config as the sender
67
71
  -n, --notify Enable email notification
data/lib/kobot/config.rb CHANGED
@@ -8,24 +8,22 @@ module Kobot
8
8
  attr_accessor :clock,
9
9
  :loglevel,
10
10
  :skip,
11
+ :auto_skip,
12
+ :auto_skip_without,
11
13
  :dryrun,
12
- :force
13
-
14
- attr_accessor :kot_url,
14
+ :force,
15
+ :kot_url,
15
16
  :kot_timezone_offset,
16
- :kot_date_format
17
-
18
- attr_accessor :gmail_notify_enabled,
17
+ :kot_date_format,
18
+ :gmail_notify_enabled,
19
19
  :gmail_notify_subject,
20
20
  :gmail_notify_to,
21
21
  :gmail_smtp_address,
22
- :gmail_smtp_port
23
-
24
- attr_accessor :browser_headless,
22
+ :gmail_smtp_port,
23
+ :browser_headless,
25
24
  :browser_geolocation,
26
- :browser_wait_timeout
27
-
28
- attr_accessor :credentials_file
25
+ :browser_wait_timeout,
26
+ :credentials_file
29
27
 
30
28
  def configure
31
29
  yield self
data/lib/kobot/engine.rb CHANGED
@@ -14,7 +14,7 @@ module Kobot
14
14
 
15
15
  # The entrance where the whole flow starts.
16
16
  #
17
- # It exits early if today is weekend or treated as holiday by
17
+ # It exits early if today is weekend or marked as to skip by
18
18
  # the #{Config.skip} specified from command line option --skip.
19
19
  #
20
20
  # Unexpected behavior such as record appearing as holiday on
@@ -24,7 +24,8 @@ module Kobot
24
24
  # System errors or any unknown exceptions occurred if any are
25
25
  # to be popped up and should be handled by the outside caller.
26
26
  def start
27
- validate_today!
27
+ return unless should_run_today?
28
+
28
29
  launch_browser
29
30
  login
30
31
  read_today_record
@@ -35,8 +36,6 @@ module Kobot
35
36
  clock_out!
36
37
  end
37
38
  logout
38
- rescue KotSkip => e
39
- Kobot.logger.warn(e.message)
40
39
  rescue KotRecordError => e
41
40
  Kobot.logger.warn(e.message)
42
41
  Mailer.send(clock_notify_message(status: e.message))
@@ -60,13 +59,16 @@ module Kobot
60
59
 
61
60
  private
62
61
 
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
62
+ def should_run_today?
63
+ if skip?
64
+ Kobot.logger.warn("Today=#{@today} is skipped as per: --skip=#{Config.skip}")
65
+ return false
66
+ end
67
+ return true unless weekend?
68
68
 
69
- Kobot.logger.info("[Force] should have exited: today=#{@today} is weekend")
69
+ Kobot.logger.info("[Force] should have exited: today=#{@today} is weekend") if Config.force
70
+ Kobot.logger.warn("Today=#{@today} is weekend") unless Config.force
71
+ Config.force
70
72
  end
71
73
 
72
74
  def launch_browser
@@ -79,7 +81,10 @@ module Kobot
79
81
  }
80
82
  options = Selenium::WebDriver::Chrome::Options.new(prefs: prefs)
81
83
  options.headless! if Config.browser_headless
82
- @browser = Selenium::WebDriver.for(:chrome, options: options)
84
+ caps = [
85
+ options
86
+ ]
87
+ @browser = Selenium::WebDriver.for(:chrome, capabilities: caps)
83
88
  @wait = Selenium::WebDriver::Wait.new(timeout: Config.browser_wait_timeout)
84
89
  Kobot.logger.info('Launch browser successful')
85
90
  end
@@ -95,6 +100,12 @@ module Kobot
95
100
  Kobot.logger.info("Navigate to: #{@top_url}")
96
101
  @browser.get @top_url
97
102
  @wait.until { @browser.find_element(id: 'modal_window') }
103
+ modal_title_element = @wait.until { @browser.find_element(css: '.modal-title') }
104
+ @selector = if modal_title_element.text.downcase.include? 'password'
105
+ Selector.en
106
+ else
107
+ Selector.ja
108
+ end
98
109
  Kobot.logger.info "Page title: #{@browser.title}"
99
110
  Kobot.logger.debug do
100
111
  "Login with id=#{Credential.kot_id} and password=#{Credential.kot_password}"
@@ -104,10 +115,14 @@ module Kobot
104
115
  @browser.find_element(css: 'div.btn-control-message').click
105
116
 
106
117
  Kobot.logger.info 'Login successful'
107
- @wait.until { @browser.find_element(id: 'notification_content').text.include?('データを取得しました') }
118
+ @wait.until do
119
+ @browser.find_element(id: 'notification_content').text.include?(@selector.login_success_notification_text)
120
+ end
108
121
  if Config.browser_geolocation
109
122
  begin
110
- @wait.until { @browser.find_element(id: 'location_area').text.include?('位置情報取得済み') }
123
+ @wait.until do
124
+ @browser.find_element(id: 'location_area').text.include?(@selector.location_area_notification_text)
125
+ end
111
126
  rescue StandardError => e
112
127
  Kobot.logger.warn "Get geolocation failed: #{e.message}"
113
128
  end
@@ -117,21 +132,21 @@ module Kobot
117
132
 
118
133
  def logout
119
134
  if @browser.current_url.include? 'admin'
120
- Kobot.logger.info('Logout from タイムカード page')
135
+ Kobot.logger.info('Logout from Time Card (タイムカード) page')
121
136
  @browser.find_element(css: 'div.htBlock-header_logoutButton').click
122
137
  else
123
- Kobot.logger.info('Logout from Myレコーダー page')
138
+ Kobot.logger.info('Logout from My Recorder (Myレコーダー) page')
124
139
  @wait.until { @browser.find_element(id: 'menu_icon') }.click
125
- @wait.until { @browser.find_element(link: 'ログアウト') }.click
140
+ @wait.until { @browser.find_element(link: @selector.logout_menu_link_text) }.click
126
141
  @browser.switch_to.alert.accept
127
142
  end
128
143
  Kobot.logger.info 'Logout successful'
129
144
  end
130
145
 
131
146
  def read_today_record
132
- Kobot.logger.info('Navigate to タイムカード page')
147
+ Kobot.logger.info('Navigate to Time Card (タイムカード) page')
133
148
  @wait.until { @browser.find_element(id: 'menu_icon') }.click
134
- @wait.until { @browser.find_element(link: 'タイムカード') }.click
149
+ @wait.until { @browser.find_element(link: @selector.time_card_menu_link_text) }.click
135
150
 
136
151
  time_table = @wait.until { @browser.find_element(css: 'div.htBlock-adjastableTableF_inner > table') }
137
152
  time_table.find_elements(css: 'tbody > tr').each do |tr|
@@ -139,22 +154,24 @@ module Kobot
139
154
  next unless date_cell.text.include? @today
140
155
 
141
156
  Kobot.logger.info('Reading today record')
142
- @kot_today = date_cell.text
143
- @kot_today_css_class = date_cell.attribute('class')
144
- @kot_today_type = tr.find_element(css: 'td.work_day_type').text
145
- @kot_today_clock_in = tr.find_element(
157
+ @kot_data = {}
158
+ @kot_data[:today] = date_cell.text
159
+ @kot_data[:today_css_class] = date_cell.attribute('class')
160
+ @kot_data[:today_type] = tr.find_element(css: 'td.work_day_type').text
161
+ @kot_data[:today_schedule] = tr.find_element(css: 'td.schedule').text
162
+ @kot_data[:today_clock_in] = tr.find_element(
146
163
  css: 'td.start_end_timerecord[data-ht-sort-index="START_TIMERECORD"]'
147
164
  ).text
148
- @kot_today_clock_out = tr.find_element(
165
+ @kot_data[:today_clock_out] = tr.find_element(
149
166
  css: 'td.start_end_timerecord[data-ht-sort-index="END_TIMERECORD"]'
150
167
  ).text
151
168
  Kobot.logger.debug do
152
169
  {
153
- kot_toay: @kot_today,
154
- kot_today_css_class: @kot_today_css_class,
155
- kot_today_type: @kot_today_type,
156
- kot_today_clock_in: @kot_today_clock_in,
157
- kot_today_clock_out: @kot_today_clock_out
170
+ kot_toay: @kot_data[:today],
171
+ kot_today_css_class: @kot_data[:today_css_class],
172
+ kot_today_type: @kot_data[:today_type],
173
+ kot_today_clock_in: @kot_data[:today_clock_in],
174
+ kot_today_clock_out: @kot_data[:today_clock_out]
158
175
  }
159
176
  end
160
177
  break
@@ -162,60 +179,71 @@ module Kobot
162
179
  end
163
180
 
164
181
  def validate_today_record!
165
- raise KotRecordError, "Today=#{@today} is not found on kot" if @kot_today.strip.empty?
182
+ raise KotRecordError, "Today=#{@today} is not found on kot" if @kot_data[:today].strip.empty?
166
183
 
167
184
  if kot_weekend?
168
- raise KotRecordError, "Today=#{@today} is marked as weekend on kot: #{@kot_today}" unless Config.force
185
+ raise KotRecordError, "Today=#{@today} is marked as weekend: #{@kot_data[:today]}" unless Config.force
169
186
 
170
187
  Kobot.logger.info(
171
- "[Force] should have exited: today=#{@today} is marked as weekend on kot: #{@kot_today}"
188
+ "[Force] should have exited: today=#{@today} is marked as weekend: #{@kot_data[:today]}"
172
189
  )
173
190
  end
174
191
 
175
- return unless kot_public_holiday?
176
- raise KotRecordError, "Today=#{@today} is marked as public holiday on kot: #{@kot_today}" unless Config.force
192
+ if kot_public_holiday?
193
+ raise KotRecordError, "Today=#{@today} is marked as public holiday: #{@kot_data[:today]}" unless Config.force
177
194
 
178
- Kobot.logger.info(
179
- "[Force] should have exited: today=#{@today} is marked as public holiday on kot: #{@kot_today}"
180
- )
195
+ Kobot.logger.info(
196
+ "[Force] should have exited: today=#{@today} is marked as public holiday: #{@kot_data[:today]}"
197
+ )
198
+ end
199
+
200
+ return unless Config.auto_skip
201
+
202
+ if kot_non_work_schedule?
203
+ raise KotRecordError, "Today=#{@today} is non-work schedule: #{@kot_data[:today_schedule]}" unless Config.force
204
+
205
+ Kobot.logger.info(
206
+ "[Force] should have exited: today=#{@today} is non-work schedule: #{@kot_data[:today_schedule]}"
207
+ )
208
+ end
181
209
  end
182
210
 
183
211
  def clock_in!
184
212
  Kobot.logger.warn("Clock in during the afternoon: #{@now}") if @now.hour > 12
185
- if @kot_today_clock_in.strip.empty?
213
+ if @kot_data[:today_clock_in].strip.empty?
186
214
  click_clock_in_button
187
215
  return if Config.dryrun
188
216
 
189
217
  read_today_record
190
- raise KotClockInError, 'Clock in operation seems to have failed' if @kot_today_clock_in.strip.empty?
218
+ raise KotClockInError, 'Clock in operation seems to have failed' if @kot_data[:today_clock_in].strip.empty?
191
219
 
192
- Kobot.logger.info("Clock in successful: #{@kot_today_clock_in}")
220
+ Kobot.logger.info("Clock in successful: #{@kot_data[:today_clock_in]}")
193
221
  Mailer.send(clock_notify_message(clock: :in))
194
222
  else
195
- Kobot.logger.warn("Clock in done already: #{@kot_today_clock_in}")
223
+ Kobot.logger.warn("Clock in done already: #{@kot_data[:today_clock_in]}")
196
224
  end
197
225
  end
198
226
 
199
227
  def clock_out!
200
228
  Kobot.logger.warn("Clock out during the morning: #{@now}") if @now.hour <= 12
201
229
  unless Config.dryrun
202
- if @kot_today_clock_in.strip.empty?
230
+ if @kot_data[:today_clock_in].strip.empty?
203
231
  raise KotClockOutError,
204
- "!!!No clock in record for today=#{@kot_today}!!!"
232
+ "!!!No clock in record for today=#{@kot_data[:today]}!!!"
205
233
  end
206
234
  end
207
235
 
208
- if @kot_today_clock_out.strip.empty?
236
+ if @kot_data[:today_clock_out].strip.empty?
209
237
  click_clock_out_button
210
238
  return if Config.dryrun
211
239
 
212
240
  read_today_record
213
- raise KotClockOutError, 'Clock out operation seems to have failed' if @kot_today_clock_out.strip.empty?
241
+ raise KotClockOutError, 'Clock out operation seems to have failed' if @kot_data[:today_clock_out].strip.empty?
214
242
 
215
- Kobot.logger.info("Clock out successful: #{@kot_today_clock_out}")
243
+ Kobot.logger.info("Clock out successful: #{@kot_data[:today_clock_out]}")
216
244
  Mailer.send(clock_notify_message(clock: :out))
217
245
  else
218
- Kobot.logger.warn("Clock out done already: #{@kot_today_clock_out}")
246
+ Kobot.logger.warn("Clock out done already: #{@kot_data[:today_clock_out]}")
219
247
  end
220
248
  end
221
249
 
@@ -224,9 +252,9 @@ module Kobot
224
252
  @browser.get @top_url
225
253
  clock_in_button = @wait.until { @browser.find_element(css: 'div.record-clock-in') }
226
254
  if Config.dryrun
227
- Kobot.logger.info('[Dryrun] clock in button (出勤) would have been clicked')
255
+ Kobot.logger.info('[Dryrun] Clock-in button (出勤) would have been clicked')
228
256
  else
229
- Kobot.logger.info('Clicking the clock in button (出勤)')
257
+ Kobot.logger.info('Clicking the Clock-in button (出勤)')
230
258
  clock_in_button.click
231
259
  end
232
260
  end
@@ -236,9 +264,9 @@ module Kobot
236
264
  @browser.get @top_url
237
265
  clock_out_button = @wait.until { @browser.find_element(css: 'div.record-clock-out') }
238
266
  if Config.dryrun
239
- Kobot.logger.info('[Dryrun] clock out button (退勤) would have been clicked')
267
+ Kobot.logger.info('[Dryrun] Clock-out button (退勤) would have been clicked')
240
268
  else
241
- Kobot.logger.info('Clicking the clock in button (退勤)')
269
+ Kobot.logger.info('Clicking the Clock-out button (退勤)')
242
270
  clock_out_button.click
243
271
  end
244
272
  end
@@ -255,18 +283,25 @@ module Kobot
255
283
  end
256
284
 
257
285
  def kot_weekend?
258
- %w[土 日].any? { |kanji| @kot_today&.include? kanji }
286
+ [
287
+ @selector.kot_date_saturday,
288
+ @selector.kot_date_sunday
289
+ ].any? { |weekend| @kot_data[:today]&.include? weekend }
290
+ end
291
+
292
+ def kot_non_work_schedule?
293
+ !@kot_data[:today_schedule]&.include?(Config.auto_skip_without)
259
294
  end
260
295
 
261
296
  def kot_public_holiday?
262
- return true if @kot_today_type&.include? '休日'
297
+ return true if @kot_data[:today_type]&.include? @selector.kot_workday_time_off_text
263
298
 
264
299
  kot_today_highlighted = %w[sunday saturday].any? do |css|
265
- @kot_today_css_class&.include? css
300
+ @kot_data[:today_css_class]&.include? css
266
301
  end
267
302
  if kot_today_highlighted
268
303
  Kobot.logger.warn(
269
- "Today=#{@kot_today} is highlighted (holiday) but not marked as 休日"
304
+ "Today=#{@kot_data[:today]} is highlighted (holiday) but not marked as #{@selector.kot_workday_time_off_text}"
270
305
  )
271
306
  end
272
307
  kot_today_highlighted
@@ -278,8 +313,8 @@ module Kobot
278
313
  "<b>Date:</b> #{@today}",
279
314
  "<b>Status:</b> <span style='color:#{color}'>#{status}</span>"
280
315
  ]
281
- message << "<b>Clock_in:</b> #{@kot_today_clock_in}" if clock
282
- message << "<b>Clock_out:</b> #{@kot_today_clock_out}" if clock == :out
316
+ message << "<b>Clock_in:</b> #{@kot_data[:today_clock_in]}" if clock
317
+ message << "<b>Clock_out:</b> #{@kot_data[:today_clock_out]}" if clock == :out
283
318
  message.join('<br>')
284
319
  end
285
320
  end
@@ -1,15 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobot
4
- class KotSkip < StandardError
5
- end
6
-
7
- class KotRecordError < StandardError
8
- end
9
-
10
- class KotClockInError < StandardError
11
- end
12
-
13
- class KotClockOutError < StandardError
14
- end
4
+ class KotRecordError < StandardError; end
5
+ class KotClockInError < StandardError; end
6
+ class KotClockOutError < StandardError; end
15
7
  end
data/lib/kobot/mailer.rb CHANGED
@@ -47,7 +47,7 @@ module Kobot
47
47
  To: <#{to}>
48
48
  MIME-Version: 1.0
49
49
  Content-type: text/html
50
- Subject: #{subject}
50
+ Subject: #{subject}
51
51
  Date: #{Time.now.getlocal(Config.kot_timezone_offset)}
52
52
 
53
53
  #{body}
data/lib/kobot/option.rb CHANGED
@@ -27,6 +27,17 @@ module Kobot
27
27
  options[:skip] = skip
28
28
  end
29
29
 
30
+ opt.on('-a', '--auto-skip', 'Enable auto-skipping by reading schedule from the Time Card page') do |auto_skip|
31
+ options[:auto_skip] = auto_skip
32
+ end
33
+
34
+ opt.on('-w', '--auto-skip-without [SCHEDULE]',
35
+ 'Skip today if the provided text is not contained in the schedule',
36
+ 'column on the Time Card page of KOT; by default 裁量労働 is used;',
37
+ 'effective only if --auto-skip is passed to enable auto-skipping.') do |schedule|
38
+ options[:auto_skip_without] = schedule
39
+ end
40
+
30
41
  opt.on('-t', '--to [TO]',
31
42
  'Email address to send notification to; by default it is sent to',
32
43
  'the same self email account used in SMTP config as the sender') do |to|
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kobot
4
+ # The texts used in selectors to identify the elements on KOT UI.
5
+ class Selector
6
+ attr_accessor :login_success_notification_text,
7
+ :location_area_notification_text,
8
+ :time_card_menu_link_text,
9
+ :kot_date_saturday,
10
+ :kot_date_sunday,
11
+ :kot_workday_time_off_text,
12
+ :logout_menu_link_text
13
+
14
+ class << self
15
+ def en
16
+ selector = Selector.new
17
+ selector.login_success_notification_text = 'Data has been obtained'
18
+ selector.location_area_notification_text = 'Obtained location'
19
+ selector.time_card_menu_link_text = 'Time Card'
20
+ selector.kot_date_saturday = 'Sat'
21
+ selector.kot_date_sunday = 'Sun'
22
+ selector.kot_workday_time_off_text = 'time-off'
23
+ selector.logout_menu_link_text = 'Sign out'
24
+ selector
25
+ end
26
+
27
+ def ja
28
+ selector = Selector.new
29
+ selector.login_success_notification_text = 'データを取得しました'
30
+ selector.location_area_notification_text = '位置情報取得済み'
31
+ selector.time_card_menu_link_text = 'タイムカード'
32
+ selector.kot_date_saturday = '土'
33
+ selector.kot_date_sunday = '日'
34
+ selector.kot_workday_time_off_text = '休日'
35
+ selector.logout_menu_link_text = 'ログアウト'
36
+ selector
37
+ end
38
+ end
39
+ end
40
+ end
data/lib/kobot/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobot
4
- VERSION = '1.2.3'
4
+ VERSION = '2.0.0'
5
5
  end
data/lib/kobot.rb CHANGED
@@ -4,6 +4,7 @@ require 'kobot/version'
4
4
  require 'kobot/exception'
5
5
  require 'kobot/option'
6
6
  require 'kobot/config'
7
+ require 'kobot/selector'
7
8
  require 'kobot/credential'
8
9
  require 'kobot/logger'
9
10
  require 'kobot/mailer'
@@ -33,6 +34,8 @@ module Kobot
33
34
  config.dryrun = options[:dryrun]
34
35
  config.force = options[:force]
35
36
  config.skip = options[:skip] || []
37
+ config.auto_skip = options[:auto_skip]
38
+ config.auto_skip_without = options[:auto_skip_without] || '裁量労働'
36
39
 
37
40
  config.kot_url = 'https://s2.kingtime.jp/independent/recorder/personal/'
38
41
  config.kot_timezone_offset = '+09:00'
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.2.3
4
+ version: 2.0.0
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-09-05 00:00:00.000000000 Z
11
+ date: 2021-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: webdrivers
@@ -83,6 +83,7 @@ files:
83
83
  - lib/kobot/logger.rb
84
84
  - lib/kobot/mailer.rb
85
85
  - lib/kobot/option.rb
86
+ - lib/kobot/selector.rb
86
87
  - lib/kobot/version.rb
87
88
  homepage: https://github.com/yuan-jiang/kobot
88
89
  licenses:
@@ -107,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
108
  - !ruby/object:Gem::Version
108
109
  version: '0'
109
110
  requirements: []
110
- rubygems_version: 3.0.3
111
+ rubygems_version: 3.1.2
111
112
  signing_key:
112
113
  specification_version: 4
113
114
  summary: Kobot automates the clock in/out of KING OF TIME.