kobot 1.2.1 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/bin/console +1 -0
- data/lib/kobot/config.rb +0 -1
- data/lib/kobot/credential.rb +0 -1
- data/lib/kobot/engine.rb +66 -51
- data/lib/kobot/exception.rb +3 -10
- data/lib/kobot/mailer.rb +0 -2
- data/lib/kobot/option.rb +0 -2
- data/lib/kobot/selector.rb +40 -0
- data/lib/kobot/version.rb +1 -1
- data/lib/kobot.rb +1 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f35251b3ce8549b6b268a9741ecc88398f4fca4e1fbe27c343e2a1c35d456ca0
|
4
|
+
data.tar.gz: 1af276cc34607f075650b5be4df65c9666449c761b410fb5cc63629fe0392ff7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1a76b64eb1aa1019a12b4f1d4a2d34c84d79cf020b0ebc7cee7c1bd4902b180aec70790ee236883100bfd2501bbee9846f61b5b40dd8fb3996159e58b94b0df
|
7
|
+
data.tar.gz: a81c35396d3dde1a74ab9aad3c3535e92699268f49a133f9f43514b192bae808a84f1974c9f9c537690ce3561c942251c044591fc363b54c619b44586ede7670
|
data/CHANGELOG.md
CHANGED
@@ -14,3 +14,17 @@
|
|
14
14
|
- Improved logging for better readability in logs
|
15
15
|
- Switched to builtin Logger#deprecate from Logger#warn for deprecations
|
16
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
|
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
|
data/bin/console
CHANGED
data/lib/kobot/config.rb
CHANGED
data/lib/kobot/credential.rb
CHANGED
data/lib/kobot/engine.rb
CHANGED
@@ -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)
|
@@ -15,7 +14,7 @@ module Kobot
|
|
15
14
|
|
16
15
|
# The entrance where the whole flow starts.
|
17
16
|
#
|
18
|
-
# It exits early if today is weekend or
|
17
|
+
# It exits early if today is weekend or marked as to skip by
|
19
18
|
# the #{Config.skip} specified from command line option --skip.
|
20
19
|
#
|
21
20
|
# Unexpected behavior such as record appearing as holiday on
|
@@ -25,26 +24,12 @@ 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
|
-
|
29
|
-
|
30
|
-
Kobot.logger.info("[Force] should have exited: today=#{@today} is weekend")
|
31
|
-
else
|
32
|
-
Kobot.logger.info("Today=#{@today} is weekend")
|
33
|
-
return
|
34
|
-
end
|
35
|
-
end
|
36
|
-
if skip?
|
37
|
-
Kobot.logger.info("Today=#{@today} is skipped as per: --skip=#{Config.skip}")
|
38
|
-
return
|
39
|
-
end
|
40
|
-
unless %i[in out].include? Config.clock
|
41
|
-
Kobot.logger.warn("Invalid clock operation: #{Config.clock}")
|
42
|
-
return
|
43
|
-
end
|
27
|
+
return unless should_run_today?
|
28
|
+
|
44
29
|
launch_browser
|
45
30
|
login
|
46
31
|
read_today_record
|
47
|
-
|
32
|
+
validate_today_record!
|
48
33
|
if Config.clock == :in
|
49
34
|
clock_in!
|
50
35
|
else
|
@@ -69,12 +54,23 @@ module Kobot
|
|
69
54
|
Mailer.send(clock_notify_message(status: e.message))
|
70
55
|
logout
|
71
56
|
ensure
|
72
|
-
|
73
|
-
@browser&.quit
|
57
|
+
close_browser
|
74
58
|
end
|
75
59
|
|
76
60
|
private
|
77
61
|
|
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
|
+
|
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
|
72
|
+
end
|
73
|
+
|
78
74
|
def launch_browser
|
79
75
|
prefs = {
|
80
76
|
profile: {
|
@@ -90,9 +86,24 @@ module Kobot
|
|
90
86
|
Kobot.logger.info('Launch browser successful')
|
91
87
|
end
|
92
88
|
|
89
|
+
def close_browser
|
90
|
+
return unless @browser
|
91
|
+
|
92
|
+
Kobot.logger.info('Close browser')
|
93
|
+
@browser.quit
|
94
|
+
end
|
95
|
+
|
93
96
|
def login
|
94
97
|
Kobot.logger.info("Navigate to: #{@top_url}")
|
95
98
|
@browser.get @top_url
|
99
|
+
@wait.until { @browser.find_element(id: 'modal_window') }
|
100
|
+
modal_title_element = @wait.until { @browser.find_element(css: '.modal-title') }
|
101
|
+
@selector = if modal_title_element.text.downcase.include? 'password'
|
102
|
+
Selector.en
|
103
|
+
else
|
104
|
+
Selector.ja
|
105
|
+
end
|
106
|
+
Kobot.logger.info "Page title: #{@browser.title}"
|
96
107
|
Kobot.logger.debug do
|
97
108
|
"Login with id=#{Credential.kot_id} and password=#{Credential.kot_password}"
|
98
109
|
end
|
@@ -101,10 +112,14 @@ module Kobot
|
|
101
112
|
@browser.find_element(css: 'div.btn-control-message').click
|
102
113
|
|
103
114
|
Kobot.logger.info 'Login successful'
|
104
|
-
@wait.until
|
115
|
+
@wait.until do
|
116
|
+
@browser.find_element(id: 'notification_content').text.include?(@selector.login_success_notification_text)
|
117
|
+
end
|
105
118
|
if Config.browser_geolocation
|
106
119
|
begin
|
107
|
-
@wait.until
|
120
|
+
@wait.until do
|
121
|
+
@browser.find_element(id: 'location_area').text.include?(@selector.location_area_notification_text)
|
122
|
+
end
|
108
123
|
rescue StandardError => e
|
109
124
|
Kobot.logger.warn "Get geolocation failed: #{e.message}"
|
110
125
|
end
|
@@ -114,21 +129,21 @@ module Kobot
|
|
114
129
|
|
115
130
|
def logout
|
116
131
|
if @browser.current_url.include? 'admin'
|
117
|
-
Kobot.logger.info('Logout from タイムカード page')
|
132
|
+
Kobot.logger.info('Logout from Time Card (タイムカード) page')
|
118
133
|
@browser.find_element(css: 'div.htBlock-header_logoutButton').click
|
119
134
|
else
|
120
|
-
Kobot.logger.info('Logout from Myレコーダー page')
|
135
|
+
Kobot.logger.info('Logout from My Recorder (Myレコーダー) page')
|
121
136
|
@wait.until { @browser.find_element(id: 'menu_icon') }.click
|
122
|
-
@wait.until { @browser.find_element(link:
|
137
|
+
@wait.until { @browser.find_element(link: @selector.logout_menu_link_text) }.click
|
123
138
|
@browser.switch_to.alert.accept
|
124
139
|
end
|
125
140
|
Kobot.logger.info 'Logout successful'
|
126
141
|
end
|
127
142
|
|
128
143
|
def read_today_record
|
129
|
-
Kobot.logger.info('Navigate to タイムカード page')
|
144
|
+
Kobot.logger.info('Navigate to Time Card (タイムカード) page')
|
130
145
|
@wait.until { @browser.find_element(id: 'menu_icon') }.click
|
131
|
-
@wait.until { @browser.find_element(link:
|
146
|
+
@wait.until { @browser.find_element(link: @selector.time_card_menu_link_text) }.click
|
132
147
|
|
133
148
|
time_table = @wait.until { @browser.find_element(css: 'div.htBlock-adjastableTableF_inner > table') }
|
134
149
|
time_table.find_elements(css: 'tbody > tr').each do |tr|
|
@@ -139,8 +154,12 @@ module Kobot
|
|
139
154
|
@kot_today = date_cell.text
|
140
155
|
@kot_today_css_class = date_cell.attribute('class')
|
141
156
|
@kot_today_type = tr.find_element(css: 'td.work_day_type').text
|
142
|
-
@kot_today_clock_in = tr.find_element(
|
143
|
-
|
157
|
+
@kot_today_clock_in = tr.find_element(
|
158
|
+
css: 'td.start_end_timerecord[data-ht-sort-index="START_TIMERECORD"]'
|
159
|
+
).text
|
160
|
+
@kot_today_clock_out = tr.find_element(
|
161
|
+
css: 'td.start_end_timerecord[data-ht-sort-index="END_TIMERECORD"]'
|
162
|
+
).text
|
144
163
|
Kobot.logger.debug do
|
145
164
|
{
|
146
165
|
kot_toay: @kot_today,
|
@@ -154,30 +173,23 @@ module Kobot
|
|
154
173
|
end
|
155
174
|
end
|
156
175
|
|
157
|
-
def
|
176
|
+
def validate_today_record!
|
158
177
|
raise KotRecordError, "Today=#{@today} is not found on kot" if @kot_today.strip.empty?
|
159
178
|
|
160
179
|
if kot_weekend?
|
161
|
-
unless Config.force
|
162
|
-
raise KotRecordError,
|
163
|
-
"Today=#{@today} is marked as weekend on kot: #{@kot_today}"
|
164
|
-
end
|
180
|
+
raise KotRecordError, "Today=#{@today} is marked as weekend on kot: #{@kot_today}" unless Config.force
|
165
181
|
|
166
182
|
Kobot.logger.info(
|
167
183
|
"[Force] should have exited: today=#{@today} is marked as weekend on kot: #{@kot_today}"
|
168
184
|
)
|
169
185
|
end
|
170
186
|
|
171
|
-
|
172
|
-
|
173
|
-
raise KotRecordError,
|
174
|
-
"Today=#{@today} is marked as public holiday on kot: #{@kot_today}"
|
175
|
-
end
|
187
|
+
return unless kot_public_holiday?
|
188
|
+
raise KotRecordError, "Today=#{@today} is marked as public holiday on kot: #{@kot_today}" unless Config.force
|
176
189
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
end
|
190
|
+
Kobot.logger.info(
|
191
|
+
"[Force] should have exited: today=#{@today} is marked as public holiday on kot: #{@kot_today}"
|
192
|
+
)
|
181
193
|
end
|
182
194
|
|
183
195
|
def clock_in!
|
@@ -224,9 +236,9 @@ module Kobot
|
|
224
236
|
@browser.get @top_url
|
225
237
|
clock_in_button = @wait.until { @browser.find_element(css: 'div.record-clock-in') }
|
226
238
|
if Config.dryrun
|
227
|
-
Kobot.logger.info('[Dryrun]
|
239
|
+
Kobot.logger.info('[Dryrun] Clock-in button (出勤) would have been clicked')
|
228
240
|
else
|
229
|
-
Kobot.logger.info('Clicking the
|
241
|
+
Kobot.logger.info('Clicking the Clock-in button (出勤)')
|
230
242
|
clock_in_button.click
|
231
243
|
end
|
232
244
|
end
|
@@ -236,9 +248,9 @@ module Kobot
|
|
236
248
|
@browser.get @top_url
|
237
249
|
clock_out_button = @wait.until { @browser.find_element(css: 'div.record-clock-out') }
|
238
250
|
if Config.dryrun
|
239
|
-
Kobot.logger.info('[Dryrun]
|
251
|
+
Kobot.logger.info('[Dryrun] Clock-out button (退勤) would have been clicked')
|
240
252
|
else
|
241
|
-
Kobot.logger.info('Clicking the
|
253
|
+
Kobot.logger.info('Clicking the Clock-out button (退勤)')
|
242
254
|
clock_out_button.click
|
243
255
|
end
|
244
256
|
end
|
@@ -255,18 +267,21 @@ module Kobot
|
|
255
267
|
end
|
256
268
|
|
257
269
|
def kot_weekend?
|
258
|
-
|
270
|
+
[
|
271
|
+
@selector.kot_date_saturday,
|
272
|
+
@selector.kot_date_sunday
|
273
|
+
].any? { |weekend| @kot_today&.include? weekend }
|
259
274
|
end
|
260
275
|
|
261
276
|
def kot_public_holiday?
|
262
|
-
return true if @kot_today_type&.include?
|
277
|
+
return true if @kot_today_type&.include? @selector.kot_workday_time_off_text
|
263
278
|
|
264
279
|
kot_today_highlighted = %w[sunday saturday].any? do |css|
|
265
280
|
@kot_today_css_class&.include? css
|
266
281
|
end
|
267
282
|
if kot_today_highlighted
|
268
283
|
Kobot.logger.warn(
|
269
|
-
"Today=#{@kot_today} is highlighted (holiday) but not marked as
|
284
|
+
"Today=#{@kot_today} is highlighted (holiday) but not marked as #{@selector.kot_workday_time_off_text}"
|
270
285
|
)
|
271
286
|
end
|
272
287
|
kot_today_highlighted
|
data/lib/kobot/exception.rb
CHANGED
@@ -1,14 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Kobot
|
4
|
-
|
5
|
-
class
|
6
|
-
end
|
7
|
-
|
8
|
-
class KotClockInError < StandardError
|
9
|
-
end
|
10
|
-
|
11
|
-
class KotClockOutError < StandardError
|
12
|
-
end
|
13
|
-
|
4
|
+
class KotRecordError < StandardError; end
|
5
|
+
class KotClockInError < StandardError; end
|
6
|
+
class KotClockOutError < StandardError; end
|
14
7
|
end
|
data/lib/kobot/mailer.rb
CHANGED
@@ -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}.
|
data/lib/kobot/option.rb
CHANGED
@@ -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
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'
|
@@ -14,8 +15,6 @@ require 'kobot/engine'
|
|
14
15
|
# and with Google Gmail service email notification can also be sent to notify the results.
|
15
16
|
module Kobot
|
16
17
|
class << self
|
17
|
-
|
18
|
-
# The entrance to run Kobot.
|
19
18
|
def run
|
20
19
|
configure
|
21
20
|
Engine.new.start
|
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.
|
4
|
+
version: 1.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Jiang
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-12-07 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.
|
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.
|