Hello anh em. Trong bài Blog lần trước mình đã giới thiệu cho anh em về Shell Script cơ bản. Tuy nhiên, bài Blog kỳ trước chỉ toàn là lý thuyết, mà lý thuyết thì lúc nào cũng CHÁN. Trong bài Blog kỳ này, anh em mình sẽ cùng nhau sử dụng Shell Script để viết nhanh 1 con Batch Cron Jobs đơn giản trong khoảng 40 dòng code, giúp checkin checkout thời gian làm việc 1 cách tự động. Tuy nhiên, anh em cũng lưu ý rằng mục đích chính trong bài Blog lần này KHÔNG PHẢI LÀ viết 1 con batch checkin checkout tự động, MÀ LÀ thông qua việc viết 1 con batch như vậy, chúng ta có thể hiểu hơn về Shell Script và cách mà Shell Scirpt có thể kết hợp với các mảng kiến thức khác như HTTP Request, HTTP Response để tạo ra được 1 con batch hoàn chỉnh.

Bối Cảnh

Sau đại dịch Covid, rất nhiều công ty đã chuyển mình và cho phép nhân viên làm việc Full Remote hoặc Hybrid. Nhưng khi chúng ta không còn đến văn phòng Fulltime 100% như trước, thì phía công ty sẽ phải xây dựng 1 hệ thống chấm công để dễ dàng hơn trong việc management.

Nếu đến văn phòng làm việc, chúng ta sẽ phải tích vân tay, quẹt thẻ để mở cửa đi ra đi vào, và hệ thống cửa của công ty thường sẽ tích hợp hệ thống chấm công luôn nên chúng ta ít khi mà quên được.

Tuy nhiên, khi chúng ta làm việc Remote, chúng ta sẽ phải chấm công bằng hệ thống chấm công riêng của công ty và lúc này thì anh em mình lại rất hay quên. Mặc dù nhiều hôm làm việc từ sáng đến đêm nhưng có thể lại quên checkin checkout cả ngày. Nếu nhớ mà báo với BO thì không sao, chứ nhiều hôm quên báo với BO xong cuối tháng chốt công lại miss communicate với BO lần nữa thì coi như ngày đấy ăn cháo.

Việc chấm công là việc nên làm và phải làm ở bất kỳ công ty nào khi chúng ta làm việc remote, mình hoàn toàn ủng hộ việc đó và không khuyến khích bất kỳ chiêu trò ăn gian nào cả. Tuy nhiên về mặt lý thuyết, chúng ta có thể dễ dàng viết 1 con batch giúp chúng ta checkin checkout tự động trong vòng 5 phút. Đặc biệt là với anh em developer bọn mình, thì việc đó lại càng dễ dàng hơn nữa.

Trong bài Blog kỳ này, mình sẽ chia sẻ với anh em các bước mình đã làm để viết ra 1 con batch checkin checkout tự động trên hệ thống chấm công ERP của công ty mình. Có thể công ty của anh em sẽ dùng 1 hệ thống chấm công khác, tuy nhiên mình nghĩ cách làm cũng sẽ tương tự nhau thôi, quan trọng là anh em hiểu được ý tưởng trong cách làm, sau đó anh em có thể tự thay đổi, điều chỉnh để phù hợp hơn trong trường hợp riêng của anh em.

Thôi ... Mở bài dài quá rồi, anh em mình cùng bắt đầu thôi

Cách thức hoạt động của HTTP Protocol

Như anh em đã biết, tất cả website trên thế giới này đều sử dụng giao thức HTTP. Dù là hệ thống chấm công ERP của công ty mình, hay bất kỳ hệ thống chấm công nào khác thì cũng đều hoạt động theo cơ chế : Client gửi request và Server trả lại response theo các tiêu chuẩn của giao thức HTTP.

Muốn viết được 1 con batch thực hiện chấm công tự động thì đương nhiên chúng ta phải hiểu cách mà hệ thống chấm công đó hoạt động. Mà để hiểu cách hệ thống chấm công hoạt động, thì trước tiên chúng ta phải hiểu cách mà HTTP Protocol hoạt động.

Có rất nhiều điều để nói về HTTP Protocol, nhưng mình nghĩ có 1 điều cực kỳ quan trọng về HTTP Protocol mà anh em phải nhớ, anh em có thể quên mọi thứ về HTTP Protocol nhưng nhất định phải nhớ điều này, đó là : HTTP là một Application Protocol dạng Stateless. Điều này có nghĩa là : các HTTP request hoạt động hoàn toàn độc lập, không phụ thuộc gì vào nhau. Sau khi Client gửi request và Server trả response thì coi như lần giao tiếp đó đã hoàn thành và không còn liên quan gì đến các lần giao tiếp HTTP trước hoặc sau lần đó.

Và điều này có nghĩa là gì ? Nó có nghĩa là chỉ cần chúng ta tìm hiểu được Browser đã làm gì khi chúng ta click vào 1 button trên hệ thống chấm công, thì về cơ bản chúng ta có thể sử dụng 1 công cụ bắn HTTP Request bất kỳ như Postman, REST Client, vv ... để tái hiện lại chính xác HTTP Request khi đó. Mà khi đã tái hiện lại được chính xác HTTP Request khi đó, cũng có nghĩa là chúng ta sẽ nhận được chính xác Response mà Server trả về khi đó ... Ten ten ten tèn ... Các bạn đã thấy có gì đó rạo rực chưa !

Hình bên trên là cấu trúc của 1 HTTP Request, nhìn thì loằng ngoằng phức tạp vậy thôi, chứ thật ra nó chỉ gồm 2 phần chính :

  • Phần đầu : Bao gồm URL và tất cả các param header liên quan
  • Phần thân : Bao gồm dữ liệu được gửi đi cùng với request đó. Hết

Tương tự, bên trên là cấu trúc HTTP Response, cũng bao gồm 2 phần chính :

  • Phần đầu : Bao gồm Response status và tất cả các response header được Server trả về.
  • Phần thân : Bao gồm data tương ứng với request mà client đã yêu cầu.

Như vậy là anh em mình đã tìm hiểu qua về cách hoạt động cơ bản của HTTP Protocol, cũng như cấu trúc của HTTP Request và HTTP Response. Chưa nhiều, nhưng từng đó cũng đủ để chúng ta bắt đầu vọc vạch và tìm hiểu về cách hoạt động của hệ thống chấm công rồi.

Let's gâu, đi tới phần tiếp theo thôi ...

Nghiên cứu hệ thống chấm công : Cơ chế Login

Đây là màn hình Login của hệ thống chấm công của công ty mình. Khi anh em tìm hiểu về cách hoạt động của 1 website, mà cụ thể ở đây là hệ thống chấm công thì auto lúc nào anh em cũng phải bật F12 và mở Tab network nhé.

Giờ mình sẽ nhập đầy đủ thông tin login, sau đó bấm button "Đăng nhập" để xem Browser sẽ thực hiện các Request HTTP như thế nào tới Server nhé.

Sau khi click vào button "Đăng nhập", Browser đã thực hiện khá nhiều Request HTTP, nhưng chúng ta có thể nhận thấy ngay có 1 Request quan trọng nhất là Request login. Ở request này, Browser đã gọi đến URL bên dưới bằng method POST :

https://golang-api-service-dot-microerp-xxxxx.appspot.com/auth/login

Kiểm tra tab Payload của Request login phía trên, chúng ta thấy rằng Browser đã gửi data chứa username và password mà user đã nhập lên Server. Đây chính là data trong phần body của HTTP Request mà chúng ta đã tìm hiểu trước đó.

Chuyển tiếp sang tab Response , chúng ta thấy rằng : Sau khi Browser thực hiện request login thì Server đã trả về cho Browser 1 cục data có chứa 1 mã token gì đó dài ngoằng. Keyword token là 1 keyword rất nhạy cảm, cứ thấy token là chúng ta sẽ cần phải lưu ý và để tâm đến nó, khả năng cao là sẽ cần dùng tới.

"token" : "eyJhbGci0iJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2Nzk4ODQwNzQsImlkIjoxNdfse.N0mezR_6fMgFd9H5tP8jeD0Tae5nL3m9DhT36PTxR18"

Tiếp tục cuộc hành trình, chúng ta sẽ chuyển qua tab Application, click vào Storage/Cookies, rồi click vào tên Domain của hệ thống chấm công để kiểm tra các cookie của hệ thống chấm công đang được lưu trên Browser.

Ở đây cũng có khá nhiều Cookies, nhưng chúng ta sẽ thấy ngay 1 cookie với cái tên khá nhạy cảm auth_token.local

auth_token.local : Bearer%20eyJhbGci0iJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2Nzk4ODQwNzQsImlkIjodsdf.N0mezR_6fMgFd9H5tP8jeD0Tae5nL3m9DhT36PTxR18

Nếu nhìn vào value của cái auth_token.local đó kỹ hơn, anh sẽ thấy nó gồm 3 phần :

auth_token.local : Bearer + %20 + 1 cái mã token dài ngoằng

Và cái mã token dài ngoằng kia lại nhìn quen quen anh em nhỉ. Yes, nó chính là cái mã token mà Server đã trả về khi Browser thực hiện request login. Nếu anh em tìm hiểu 1 chút về URL Encoding thì anh em sẽ biết %20 tương ứng với dấu space. Vì vậy giá trị của Cookies auth_token.local chính là :

auth_token.local : Bearer {{token}}

Vậy là đến đây chúng ta có thể tóm tắt các nội dung chúng ta đã tìm hiểu được về hệ thống chấm công như sau :

  1. Người nhập thông tin đăng nhập, click vào button "Đăng nhập"
  2. Browser thực hiện request login lên Server theo URL bên dưới với method POST : https://golang-api-service-dot-microerp-xxxxx.appspot.com/auth/login
  3. Sau đó, Server trả về cho Browser 1 mã token dài ngoằng
  4. Browser setting token đó vào trong Cookies theo công thức : auth_token.local : Bearer + %20 + token
  5. auth_token.local đã được setting vào trong Cookies, nên mọi Request mà Browser thực hiện lên Server từ nay về sau sẽ đều gửi kèm theo giá trị Cookies này.

Vậy là chúng ta đã tìm hiểu xong cách mà Browser thực hiện request login lên trên Server. Bây giờ chúng ta có thể sử dụng 1 công cụ bắn HTTP Request bất kỳ như Postman, REST Client, vv ... để tái hiện lại request login và get được token y như Browser đã làm.

Trong hình phía trên, mình đã sử dụng REST Client để tái hiện lại request login mà Browser đã được hiện, và như các bạn thấy mình đã lấy được token từ Server trả về. Đến đây là thấy thơm thơm rồi đấy. Ahihi

Nghiên cứu hệ thống chấm công : Cơ chế Checkin

Tiếp theo chúng ta sẽ tìm hiểu: Khi chúng ta click vào button "Checkin" thì Browser đã làm gì, đã gửi request lên server như thế nào. Tương tự như cơ chế Login, nếu chúng ta hiểu cách mà Browser đã làm khi Checkin thì chúng ta cũng có thể tái hiện lại Request Checkin đó bằng tool hoặc bằng code. Hehe

Phía trên là màn hình checkin trong hệ thống chấm công của công ty mình. Như các bạn thấy trong 2 hình phía trên, khi mình click vào button "Giờ vào" thì Browser sẽ thực hiện Request checkin lên Server. Sau khi request checkin success, thì thời gian checkin sẽ được hiện thị lên màn hình và button "Giờ vào" sẽ được đổi wording thành "Giờ ra".

Nếu nhìn kỹ hơn vào trong request checkin, các bạn sẽ thấy Browser đã gọi lên Server với URL bên dưới bằng method POST.

https://golang-api-service-dot-microerp-xxxxx.appspot.com/timekeeping/check-in

Và quan trọng nhất, khi nhìn vào bên trong Request Header chúng ta sẽ thấy Browser đã gửi lên Server 1 mã token authorization như bên dưới :

authorization: Bearer eyJhbGci0iJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2Nzk4ODQwNzQsImlkIjresdfDFe.N0mezR_6fMgFd9H5tP8jeD0Tae5nL3m9DhT36PTxR18

Lại 1 lần nữa, các bạn thấy mã token này có quen không ? Ahihi. Chính xác, nó chính là cái mã token mà chúng ta đã lấy được khi thực hiện request login trước đó. Và đó chính là cơ chế checkin của hệ thống chấm công.

Tương tự như cơ chế Login, sau khi chúng ta đã hiểu về cơ chế Checkin rồi, chúng ta cũng có thể sử dụng 1 công cụ bắn HTTP Request bất kỳ như Postman, REST Client, vv ... để tái hiện lại request checkin và thực hiện checkin vào hệ thống y hệt như Browser đã làm.

Ở hình phía bên trên, mình đã sử dụng REST Client để thực hiện request checkin, khi gửi request mình cũng đính kèm mã token authorization đã lấy được ở request login ... và .... Bùm, mình đã checkin thành công.

Nghiên cứu hệ thống chấm công : Cơ chế Checkout

Hoàn toàn tương tự với lúc Checkin, bây giờ mình sẽ click thử vào button "Giờ ra" và tìm hiểu cách Browser đã thực hiện request tới Server như thế nào để thực hiện checkout.

Hoàn toàn giống lúc Checkin, Browser đã gọi lên Server bằng URL bên dưới với method POST.

https://golang-api-service-dot-microerp-xxxxx.appspot.com/timekeeping/check-out

và gửi kèm theo token authorization y như lúc Checkin

authorization: Bearer eyJhbGci0iJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2Nzk4ODQwNzQsImlkIjdsfsdfFDS.N0mezR_6fMgFd9H5tP8jeD0Tae5nL3m9DhT36PTxR18

Cơ chế checkout hoàn toàn tương tự checkin anh em nhỉ.

Mình lại sử dụng REST Client để tái hiện lại request checkout và ... Bùm, mình lại checkout thành công.

Tóm tắt cơ chế checkin checkout của hệ thống chấm công

Anh em mình đã tìm hiểu được khá nhiều thứ về cơ chế hoạt động của hệ thống chấm công. Trước khi chúng ta thực sự bắt tay vào viết batch checkin checkout, mình sẽ tóm tắt lại 1 số ý chính mà chúng ta đã tìm hiểu được.

Toàn bộ nội dung anh em mình đã tìm hiểu được có thể tóm gọn theo Flow 3 bước bên dưới :

  1. Login : Đầu tiên Browser sẽ gọi lên Server bằng URL Login và đính kèm thông tin đăng nhập của người dùng. Server xác thực thông tin đăng nhập, nếu đúng sẽ trả về cho Browser 1 token xác thực đăng nhập thành công.
  2. Checkin : Browser gọi lên Server bằng URL Checkin và đính kèm mã token trước đó đã nhận từ Server. Sau đó, Server kiểm tra mã token có hợp lệ hay không, nếu mã token hợp lệ thì Server sẽ ghi thời gian Checkin của người dùng vào DB và hiển thị lên màn hình.
  3. Checkout : Tương tự Checkin, Browser gọi lên Server bằng URL Checkout và cũng đính kèm mã token. Server cũng kiểm tra token có hợp lệ hay không, nếu token hợp lệ thì Server sẽ ghi thời gian Checkout của người dùng vào DB và hiển thị lên màn hình.

Vậy là anh em mình đã chuẩn bị xong tất cả nguyên liệu cần thiết để nấu ăn ... à nhầm, để viết ra 1 con batch checkin checkout tự động. Việc còn lại chỉ là sử dụng 1 ngôn ngữ lập trình bất kỳ, để xào nấu cái đống nguyên liệu phía trên thành 1 con batch checkin checkout tự động hoàn chỉnh thôi.

Bắt tay vào viết Batch checkin checkout tự động

Anh em có thể dùng bất kỳ ngôn ngữ lập trình nào anh em thích như PHP, Ruby, Java, Nodejs, ... whatever you want. Trong bài Blog kỳ này mình muốn thực hành về Shell Script nên mình sẽ sử dụng nó để viết Batch nhé anh em.

Nội dung file Shell Scirpt auto_checkin_checkout_batch của mình sẽ như sau :

Trong con batch phía trên, mình sẽ có 2 function là checkin và checkout. Trong mỗi function, đầu tiên mình sẽ thực hiện request login để lấy token authorization, sau đó sử dụng token đó để thực hiện request checkin hoặc checkout tương ứng. Khi run file auto_checkin_checkout_batch này, nếu mình truyền vào param checkin thì function checkin sẽ được chạy, nếu mình truyền vào param khác như checkout thì function checkout sẽ được chạy. Đơn giản chỉ có thể.

Việc còn lại là setting Cron jobs để chạy file auto_checkin_checkout_batch theo 1 thời gian phù hợp trong ngày là xong. Mình đang sử dụng Windows 10, nên mình sẽ dùng WSL2 để giả lập Linux và setting Cron jobs, nếu anh em đang xài Ubuntu hoặc CentOS thì cứ setting Cron jobs như bình thường nhé.

Đầu tiên là setting để file auto_checkin_checkout_batch có quyền excute.

ls -la
chmod +x auto_checkin_checkout_batch

Đảm bảo cronjob đang ở status running.

# Ubuntu
sudo service cron status
sudo service cron start

#WSL2
sudo service cron status
sudo service cron start

Sau đó setting thời gian chạy function checkin checkout cho phù hợp. Công ty mình vào làm lúc 8h sáng và tan làm lúc 17h30 nên mình sẽ setting thời gian checkin là 7h45 và thời gian checkout là 17h45.

crontab -e
# checkin
45 7 * * 1-5 /root/auto_checkin_checkout_batch checkin

# checkout
45 17 * * 1-5 /root/auto_checkin_checkout_batch checkout

Vậy là về cơ bản chúng ta đã viết và setting xong 1 con batch checkin checkout tự động. Từ giờ trở đi, cứ đến đúng 7h45 và 17h45 mỗi ngày, con batch sẽ tự động thực hiện checkin và checkout thay cho chúng ta, 5 ngày 1 tuần, đảm bảo đều răm rắp không quên ngày nào nữa đâu. Ahihi

Độ thêm con batch xí nữa cho xịn xò

Con batch đã chạy và cần mẫn mỗi ngày checkin checkout thay cho chúng ta. Tuy nhiên, sau 1 thời gian anh em sẽ nhận ra 1 sự thật đau lòng đó là : Ngày nào cũng như ngày nào, anh em đều checkin lúc 7h45 và checkout lúc 17h45 đều răm rắp thế này thì thật là không ổn. Thế này thì thật chẳng khác nào vạch áo cho người xem lưng, kiểu nói với cả công ty là : "anh em ơi, tôi đang dùng batch để checkin checkout này, à hú huuuuu". Không ổn, không ổn ....

Chúng ta cần phải tìm cách để random thời gian chạy batch checkin, checkout sao cho nó lộn xộn 1 tý, để cho nó giống 1 người thường, anh em hiểu hông.

Có nhiều cách để làm được việc này, nhưng có 1 cách khá đơn giản đó là viết ra 1 con batch khác để random thời gian chạy cho con batch checkin checkout là xong. Con batch random_time_checkin_checkout_batch có thể viết đơn giản như sau :

crontab -e
# random time checkin checkout
0 6 * * 1-5 /root/random_time_checkin_checkout_batch

# checkin
47 7 * * 1-5 /root/auto_checkin_checkout_batch checkin

# checkout
7 18 * * 1-5 /root/auto_checkin_checkout_batch checkout

Vậy là 6h sáng mỗi ngày, con batch random_time_checkin_checkout_batch sẽ random thời gian checkin trong khoảng 7h00~7h59 và random thời gian checkout trong khoảng 18h00~18h59, sau đó nó sẽ sửa lại file crontab để thay đổi thời gian chạy batch auto_checkin_checkout_batch. Vậy là mỗi ngày thời gian checkin, checkout đều được random mới và thời gian checkin, checkout giữa các ngày sẽ không còn giống nhau nữa. À hú huuuuu hú ....

Tổng kết

Trong khoảng 40 dòng code, anh em mình đã viết xong 1 con Batch Cron Jobs checkin checkout tự động bằng Shell Script hoàn chỉnh. Và như mình đã nói lúc đầu, mục đích chính trong bài Blog lần này KHÔNG PHẢI LÀ viết 1 con batch checkin checkout tự động, MÀ LÀ thông qua việc viết 1 con batch như vậy, chúng ta có thể hiểu hơn về Shell Script và cách mà Shell Scirpt có thể kết hợp với các mảng kiến thức khác như HTTP Request, HTTP Response để tạo ra được 1 con batch hoàn chỉnh.

Trong bài viết này, những thông tin nhạy cảm như password, token, vv.. đã được mình che lại hoặc cố tình sửa cho bị sai, nên đôi khi anh em sẽ thấy mã token ở bước trước và sau không giống nhau là vì như vậy. Tuy nhiên điều đó không quan trọng, mình nghĩ quan trọng là anh em có thể hiểu được ý tưởng trong cách làm để từ đó có thể thay đổi và áp dụng vào trong trường hợp riêng của mình.

Hệ thống chấm công ERP của công ty mình không có Captcha nên viết batch checkin checkout tự động khá đơn giản. Nếu hệ thống được nhúng Captcha vào thì việc viết batch sẽ khó khăn hơn rất nhiều. Tuy nhiên, với công nghệ phát triển mỗi ngày như hiện tại thì có thể vẫn có cách, anh em mình cứ từ từ tìm hiểu thôi. GPT-4 còn có thể giả làm người mù để vượt Captcha nữa mà. Cảm ơn anh em đã đọc bài Blog của mình, hẹn gặp lại anh em trong các bài Blog kỳ sau.

PS/: Mục đích chính mình viết con batch này là để thực hành Shell Script, sau khi thực hành xong, mình sẽ xóa con batch này luôn. Trước giờ mình luôn luôn checkin checkout thủ công bằng tay, mình cũng hay quên và thường xuyên phải liên hệ với BO. Tuy nhiên mình cho rằng thà quên rồi báo lại với BO còn hơn là xài batch tự động, anh em cũng vậy nhé. Ahihi

Tham khảo

Shell Script Document : https://www.shellscript.sh/

Tutorialspoint : https://www.tutorialspoint.com/unix/shell_scripting.htm

freeCodeCamp : https://www.freecodecamp.org/news/shell-scripting-crash-course-how-to-write-bash-scripts-in-linux/