0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Golang: agoutiを使用してAKASHIの勤怠ボタンを押せるようにしてみた

Posted at

概要

ブラウザ操作をやってみたかったので、
毎日勤怠管理で使用しているAKASHIのボタンを自動ブラウザで押せるようなアプリケーション作ってみました。

Goを使用した背景は、Goで書きたかったからです笑
実際のコード見たい方がいらっしゃいましたら、別途GitHubのリポジトリ公開しようと思います。

実装環境

  • macOS Monterey(12.2.1)
  • Goland
  • go version go1.20 darwin/arm64

使用フレームワーク

1. agouti

Golangでブラウザ操作してくれるやつ。

% go get https://pkg.go.dev/github.com/sclevine/agouti 

今回はChromeを使用するので、事前にChrome Driverもインストールしておきます。

% brew install chromedriver

2. godotenv

Golangで環境変数が使用できるようになるやつ

% go get github.com/joho/godotenv

ツリー構造

.
├── .gitignore
├── .env
├── akashi.go
├── go.mod
├── go.sum
└── main.go

コード

main.go
package main

import (
	"fmt"
	"github.com/sclevine/agouti"
	"log"
)

func main() {
	// オプションの選択
	option, err := selectOption()
	if err != nil {
		fmt.Printf("select option error: %s\n", err)
		return
	}

	// chromeドライバを使って、Google Chromeを使用できるようにする。
	agoutiDriver := agouti.ChromeDriver()
	if err := agoutiDriver.Start(); err != nil {
		fmt.Printf("Failed to start driver. %s\n", err)
		return
	}
	defer agoutiDriver.Stop()

	// agoutiを使用して新規ブラウザを立ち上げる。開いたブラウザを使って色々操作します。
	page, err := agoutiDriver.NewPage()
	if err != nil {
		fmt.Printf("Failed to open a new page. %s\n", err)
		return
	}

	// オプションに応じて勤怠処理の実行
	if err := executeAkashiTimeClock(page, option); err != nil {
		log.Fatalln(err)
	}

	fmt.Println("success")
}
akashi.go
package main

import (
	"fmt"
	"github.com/joho/godotenv"
	"github.com/sclevine/agouti"
	"os"
)

const (
	attendanceOptionNumber = 1
	leavingOptionNumber    = 2
)

func selectOption() (int, error) {
	var optionMap = map[int]string{
		attendanceOptionNumber: "出勤",
		leavingOptionNumber:    "退勤",
	}

	// 実行したいオプションを入力してもらう
	var option int
	fmt.Printf("select options \n")
	for k, v := range optionMap {
		fmt.Printf("%v: %v\n", k, v)
	}
	fmt.Printf(">")
	fmt.Scan(&option)

	// 入力されたオプションが正しいか判定
	if _, ok := optionMap[option]; !ok {
		return 0, fmt.Errorf("not the correct option value")
	}

	return option, nil
}

func executeAkashiTimeClock(page *agouti.Page, option int) error {
	// AKASHIログインページ移動
	if err := openAkashiLoginPage(page); err != nil {
		return err
	}

	// AKASHIログイン
	if err := loginAkashi(page); err != nil {
		return err
	}

	// 音声ミュート
	if err := muteAkashi(page); err != nil {
		return err
	}

	// オプションに応じて勤怠処理の変更
	var xPath string
	switch option {
	case attendanceOptionNumber:
		xPath = "/html/body/div[1]/div/section/form/div[2]/div/div[2]/ul[1]/li[1]/a[@data-punch-type='attendance']"
	case leavingOptionNumber:
		xPath = "/html/body/div[1]/div/section/form/div[2]/div/div[2]/ul[1]/li[2]/a[@data-punch-type='leaving']"
	}

	button := page.FindByXPath(xPath)
	if err := button.Click(); err != nil {
		return fmt.Errorf("Failed to click button. %s\n", err)
	}

	return nil
}

func openAkashiLoginPage(page *agouti.Page) error {
	// 次に勤怠ページを指定したAKASHIのページを開きます。
	if err := page.Navigate("https://atnd.ak4.jp/login?next=%2Fmypage%2Fpunch"); err != nil {
		return fmt.Errorf("Failed to navigate to AKASHI login page. %s\n", err)
	}
	return nil
}

func loginAkashi(page *agouti.Page) error {
	// フォーム情報を取得
	formCompanyId := page.FindByID("form_company_id")
	formLoginId := page.FindByID("form_login_id")
	formPassword := page.FindByID("form_password")

	// 環境変数セット
	if err := godotenv.Load(); err != nil {
		return fmt.Errorf("Failed loading .env file: %s\n", err)
	}

	companyId := os.Getenv("FORM_COMPANY_ID")
	loginId := os.Getenv("FORM_LOGIN_ID")
	password := os.Getenv("FORM_PASSWORD")

	// フォームにログイン情報を入力
	formCompanyId.Fill(companyId)
	formLoginId.Fill(loginId)
	formPassword.Fill(password)

	// submit
	formSubmitButton := page.FindByID("submit-button")
	if err := formSubmitButton.Click(); err != nil {
		return fmt.Errorf("Failed to click form button. %s\n", err)
	}

	return nil
}

func muteAkashi(page *agouti.Page) error {
	// muteボタンのXPathを指定して音声ミュート
	muteButton := page.FindByXPath("/html/body/div[1]/div/section/form/div[1]/div[2]/ul/li[2]")
	if err := muteButton.Click(); err != nil {
		return fmt.Errorf("Failed to click mute button. %s\n", err)
	}
	return nil
}
.gitignore
.env
.idea
.env
# AKASHIログイン情報
FORM_COMPANY_ID=""
FORM_LOGIN_ID=""
FORM_PASSWORD=""

使用方法

% go build FILE_NAME

% go run BUILD_FILE_NAME
select options
2: 退勤
1: 出勤
> // 実行したいオプションを入力 → chromeが起動してオプションに応じたブラウザ操作実行

success

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?