Compare commits

...

9 Commits

Author SHA1 Message Date
kinoshitakenta 86bd790f1c
feat: added the function to open the booking meeting room page 2025-06-20 14:30:33 +08:00
kinoshitakenta ccd5dee807
Merge branch 'env/migrate-to-uv' 2025-06-20 13:52:19 +08:00
kinoshitakenta bee7927ef9
chore: use uv to manage the environment
Refactors the project to use `uv` for dependency management
instead of `poetry` and `requirements.txt`.

Updates `pyproject.toml` to remove unnecessary fields, and adds
`uv.lock` to specify exact versions of dependencies.

Updates the README with `uv` command to execute the application.
2025-06-20 13:37:38 +08:00
kinoshitakenta 8579b135cf
Merge branch 'env/migrate-to-poetry' 2025-06-19 16:52:21 +08:00
kinoshitakenta 07e01554d0
chore: updates author info and dependencies
Updates the author information and adjusts the dependency versions in the project configuration.
2025-06-19 16:47:22 +08:00
kinoshitakenta 953b6ab909
feat: improves element click handling
Adds retry logic to handle `ElementClickInterceptedException`.

This change enhances the robustness of element clicking by implementing a retry mechanism with a timeout.
It addresses the issue where clicks are sometimes intercepted (such as scrolling up and down on the page), preventing the action from succeeding.
2025-06-12 16:29:28 +08:00
kinoshitakenta 77054f1d84
docs: adds documentation to login function
Adds a detailed docstring to the `keep_login_status` function,
clarifying its purpose, arguments, return value, and behavior
during login attempts.
2025-06-12 16:26:41 +08:00
kinoshitakenta 6523fecb5a
feat: improves login status handling
Ability to further handle pop-up windows when login attempts are unsuccessful.

Modified the return value of the `keep_login_status` function to indicate whether the operation was successful or failed.
In case of a failure, no further command actions will be executed.
2025-06-12 16:25:26 +08:00
insleker 73f0e62301 chore: add poetry dependency manager 2025-05-20 12:43:11 +08:00
7 changed files with 108 additions and 29 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ __pycache__/
# config file
config.toml
uv.lock

View File

@ -1 +1,7 @@
# auto_login_EIP
- 執行
```shell
# uv run python main.py --config_path .\config.toml.example
```

View File

@ -92,8 +92,8 @@ def main(opt):
continue
# * run command
keep_login_status(driver, login_info)
action_agent.run(action_code)
if keep_login_status(driver, login_info):
action_agent.run(action_code)
except KeyboardInterrupt:
pass

20
pyproject.toml Normal file
View File

@ -0,0 +1,20 @@
[project]
name = "auto-login-eip"
version = "0.1.0"
description = ""
authors = [{ name = "Kinoshita Kenta", email = "ybs0306748@gmail.com" }]
requires-python = ">=3.9"
readme = "README.md"
dependencies = [
"selenium (>=4.27.1,<5.0.0)",
"tomli (>=2.2.1,<3.0.0)",
"webdriver-manager (>=4.0.2,<5.0.0)",
"rich (>=13.9.4,<15.0.0)",
]
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.uv]
package = false

View File

@ -1,9 +0,0 @@
# for parsing toml file
tomli
# `webdriver_manager` manages webdriver versions
webdriver_manager
selenium
# better output
rich

View File

@ -14,6 +14,7 @@ class ActionType(IntEnum):
請假 = 1
補卡 = 2
訂便當 = 3
訂會議室 = 4
@classmethod
def has_value(cls, value):
@ -25,7 +26,8 @@ class Action():
self.action_list = {ActionType.登入: self.__登入,
ActionType.請假: self.__請假,
ActionType.補卡: self.__補卡,
ActionType.訂便當: self.__訂便當
ActionType.訂便當: self.__訂便當,
ActionType.訂會議室: self.__訂會議室
}
self.driver = driver
@ -125,9 +127,17 @@ class Action():
order_title, img, order_status = a_tag
if order_status.text != "【已訂購】":
order_title.click()
open_page_num += 1
time.sleep(0.05)
for attempt in range(5):
try:
order_title.click()
open_page_num += 1
time.sleep(0.05)
break # 點成功就跳出 retry
except ElementClickInterceptedException:
print("點擊被遮蔽,重試中...")
time.sleep(0.2) # 等一下再重試
else:
print("點擊失敗:可能被遮蔽或其他原因")
if open_page_num > 0:
print(f"已開啟 {open_page_num} 個訂購頁面")
@ -135,3 +145,16 @@ class Action():
print("沒有尚未訂購的團購訂單")
self.driver.switch_to.default_content()
def __訂會議室(self):
"""訂會議室"""
self.driver.switch_to.frame(self.driver.find_element(By.ID, "main"))
all_meeting_room_tag = self.driver.find_element(By.ID, "WPPublicResource_TreeTagt0") # 會議室
ActionChains(self.driver).move_to_element(all_meeting_room_tag).move_to_element(all_meeting_room_tag).click(all_meeting_room_tag).perform()
Zhubei_tag = self.driver.find_element(By.ID, "WPPublicResource_TreeTagt1") # 竹北
ActionChains(self.driver).move_to_element(Zhubei_tag).move_to_element(Zhubei_tag).click(Zhubei_tag).perform()
meeting_room_500_tag = self.driver.find_element(By.ID, "WPPublicResource_TreeTagt2") # 500會議室
ActionChains(self.driver).move_to_element(meeting_room_500_tag).move_to_element(meeting_room_500_tag).click(meeting_room_500_tag).perform()

View File

@ -8,6 +8,7 @@ if sys.version_info >= (3, 11):
else:
import tomli as tomllib
from selenium import webdriver
from selenium.common.exceptions import NoAlertPresentException, UnexpectedAlertPresentException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
@ -44,7 +45,22 @@ class LoginInfo():
"please enter your passwd: ")
def keep_login_status(driver: webdriver.Chrome, login_info: LoginInfo):
def keep_login_status(driver: webdriver.Chrome, login_info: LoginInfo) -> bool:
"""
Attempt to log into the CHI MotorWeb ERP system using the provided login information.
This function navigates to the login page, fills in the login form with the users credentials,
and attempts to log in. It handles unexpected alert pop-ups that indicate login failure,
and manages browser windows to ensure only the relevant page remains open.
Args:
driver (webdriver.Chrome): An instance of Selenium WebDriver controlling a Chrome browser.
login_info (LoginInfo): A data object containing login credentials and preferences.
Returns:
bool: True if login is successful or already logged in; False if login failed (e.g., due to incorrect credentials).
"""
# * Close all windows except the main window.
while len(driver.window_handles) > 1:
driver.switch_to.window(driver.window_handles[1])
@ -77,33 +93,55 @@ def keep_login_status(driver: webdriver.Chrome, login_info: LoginInfo):
# * Press the submit button.
submit_btn = driver.find_element(By.ID, LOGIN_BUTTON_ID)
submit_btn.click()
time.sleep(1)
time.sleep(3)
# * Check if login failed (alert popup)
try:
alert = driver.switch_to.alert
print(f"Login error message: {alert.text}")
alert.accept()
return False # Skip remaining logic, login failed
except NoAlertPresentException:
pass # No alert, proceed
time.sleep(2)
# * If login has pop a new window, switch main window to the new one.
login_page_handle = ""
main_page_handle = ""
for handle in driver.window_handles:
driver.switch_to.window(handle)
if "CHI MotorWeb - " in driver.title:
try:
title = driver.title
except UnexpectedAlertPresentException:
try:
alert = driver.switch_to.alert
print(f"Unexpected alert: {alert.text}")
alert.accept()
continue
except NoAlertPresentException:
continue
if "CHI MotorWeb - " in title:
main_page_handle = handle
elif "CHI MOTOR WEB ERP 登入" in driver.title:
elif "CHI MOTOR WEB ERP 登入" in title:
login_page_handle = handle
# * get the page handle that should be stay
if main_page_handle:
stay_page_handle = main_page_handle
elif login_page_handle:
stay_page_handle = login_page_handle
else:
stay_page_handle = top_page
stay_page_handle = main_page_handle or login_page_handle or top_page
# * close unnecessary pages
for handle in driver.window_handles:
if handle != stay_page_handle:
driver.switch_to.window(handle)
driver.close()
try:
driver.switch_to.window(handle)
driver.close()
except Exception as e:
print(f"Error closing window {handle}: {e}")
driver.switch_to.window(stay_page_handle)
driver.maximize_window()
return True
elif "CHI MotorWeb - " in driver.title:
return True