옵션 번역 수정
This commit is contained in:
parent
fa39c762a2
commit
bf240af991
15
config.ini
15
config.ini
|
|
@ -34,20 +34,25 @@ price_selector_template = '//*[@id="productMainContentContainerId"]/div[1]/div[2
|
||||||
# 옵션 상자
|
# 옵션 상자
|
||||||
; option_box_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc'
|
; option_box_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc'
|
||||||
|
|
||||||
option_box_selector = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[2]/div'
|
; option_box_selector = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[2]/div'
|
||||||
|
option_box_selector = 'div#productMainContentContainerId li > div > div:nth-child(1) > div > div:nth-child(2) > div'
|
||||||
|
|
||||||
; excluded_option_marker = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dfauwV.bXsMpn'
|
; excluded_option_marker = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dfauwV.bXsMpn'
|
||||||
excluded_option_marker = '.bXsMpn.sc-dfauwV'
|
excluded_option_marker = '.bXsMpn.sc-dfauwV'
|
||||||
; delete_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) span:has-text("삭제")'
|
; delete_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) span:has-text("삭제")'
|
||||||
; delete_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) div.sc-igZIGL.kQDmyq'
|
; delete_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) div.sc-igZIGL.kQDmyq'
|
||||||
; delete_button_selector = '.kQDmyq.sc-igZIGL'
|
; delete_button_selector = '.kQDmyq.sc-igZIGL'
|
||||||
|
; delete_button_selector_template = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[2]/div/div[2]/div[1]/div/span'
|
||||||
|
delete_button_selector_template = 'div#productMainContentContainerId li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div.ant-row.ant-row-no-wrap.ant-row-space-between.ant-row-middle.css-1li46mu > div:nth-child(1) > div > span'
|
||||||
|
|
||||||
delete_button_selector_template = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[2]/div/div[2]/div[1]/div/span'
|
|
||||||
fallback1_delete_button_selector_template = 'div#productMainContentContainerId li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div.ant-row.ant-row-no-wrap.ant-row-space-between.ant-row-middle.css-1li46mu > div:nth-child(1) > div > span'
|
fallback1_delete_button_selector_template = 'div#productMainContentContainerId li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div.ant-row.ant-row-no-wrap.ant-row-space-between.ant-row-middle.css-1li46mu > div:nth-child(1) > div > span'
|
||||||
|
|
||||||
|
delete_dialog_selector = 'div.sc-ddjGPC.jbwEYW'
|
||||||
confirm_delete_button_selector = '.ant-modal.css-1li46mu.ant-modal-confirm.ant-modal-confirm-confirm button:has-text("삭제")'
|
confirm_delete_button_selector = 'button.ant-btn-primary.ant-btn-dangerous:has-text('삭제')'
|
||||||
|
; confirm_delete_button_selector = '.ant-modal.css-1li46mu.ant-modal-confirm.ant-modal-confirm-confirm button:has-text("삭제")'
|
||||||
|
; confirm_delete_button_selector = 'xpath=/html/body/div[8]/div/div[2]/div/div[2]/div/div/div/div[2]/button[2]'
|
||||||
add_button_selector2 = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dRGYJT.hmQUGb'
|
add_button_selector2 = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dRGYJT.hmQUGb'
|
||||||
add_button_selector = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[2]/div/div/img'
|
add_button_selector = 'div#productMainContentContainerId li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div > img'
|
||||||
; add_button_selector = 'div.hmQUGb.sc-dRGYJT'
|
; add_button_selector = 'div.hmQUGb.sc-dRGYJT'
|
||||||
; add_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-krITIZ.ckztYT'
|
; add_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-krITIZ.ckztYT'
|
||||||
; file_upload_button_selector = '.ant-modal-content button:has-text("클릭 or 드레그로 파일 업로드")'
|
; file_upload_button_selector = '.ant-modal-content button:has-text("클릭 or 드레그로 파일 업로드")'
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ class LocatorManager:
|
||||||
'excluded_option_marker': self.config.get('OptionLocators', 'excluded_option_marker').strip("'"),
|
'excluded_option_marker': self.config.get('OptionLocators', 'excluded_option_marker').strip("'"),
|
||||||
'delete_button_selector_template': self.config.get('OptionLocators', 'delete_button_selector_template').strip("'"),
|
'delete_button_selector_template': self.config.get('OptionLocators', 'delete_button_selector_template').strip("'"),
|
||||||
'fallback1_delete_button_selector_template': self.config.get('OptionLocators', 'fallback1_delete_button_selector_template').strip("'"),
|
'fallback1_delete_button_selector_template': self.config.get('OptionLocators', 'fallback1_delete_button_selector_template').strip("'"),
|
||||||
|
'delete_dialog_selector': self.config.get('OptionLocators', 'delete_dialog_selector').strip("'"),
|
||||||
'confirm_delete_button_selector': self.config.get('OptionLocators', 'confirm_delete_button_selector').strip("'"),
|
'confirm_delete_button_selector': self.config.get('OptionLocators', 'confirm_delete_button_selector').strip("'"),
|
||||||
'add_button_selector': self.config.get('OptionLocators', 'add_button_selector').strip("'"),
|
'add_button_selector': self.config.get('OptionLocators', 'add_button_selector').strip("'"),
|
||||||
'file_upload_button_selector': self.config.get('OptionLocators', 'file_upload_button_selector').strip("'"),
|
'file_upload_button_selector': self.config.get('OptionLocators', 'file_upload_button_selector').strip("'"),
|
||||||
|
|
|
||||||
59
option.py
59
option.py
|
|
@ -36,6 +36,7 @@ class OptionHandler:
|
||||||
self.excluded_option_marker = self.locator_manager.get_locator('OptionLocators', 'excluded_option_marker')
|
self.excluded_option_marker = self.locator_manager.get_locator('OptionLocators', 'excluded_option_marker')
|
||||||
self.delete_button_selector_template = self.locator_manager.get_locator('OptionLocators', 'delete_button_selector_template')
|
self.delete_button_selector_template = self.locator_manager.get_locator('OptionLocators', 'delete_button_selector_template')
|
||||||
self.fallback1_delete_button_selector_template = self.locator_manager.get_locator('OptionLocators', 'fallback1_delete_button_selector_template')
|
self.fallback1_delete_button_selector_template = self.locator_manager.get_locator('OptionLocators', 'fallback1_delete_button_selector_template')
|
||||||
|
self.delete_dialog_selector = self.locator_manager.get_locator('OptionLocators', 'delete_dialog_selector')
|
||||||
self.confirm_delete_button_selector = self.locator_manager.get_locator('OptionLocators', 'confirm_delete_button_selector')
|
self.confirm_delete_button_selector = self.locator_manager.get_locator('OptionLocators', 'confirm_delete_button_selector')
|
||||||
self.add_button_selector = self.locator_manager.get_locator('OptionLocators', 'add_button_selector')
|
self.add_button_selector = self.locator_manager.get_locator('OptionLocators', 'add_button_selector')
|
||||||
self.file_upload_button_selector = self.locator_manager.get_locator('OptionLocators', 'file_upload_button_selector')
|
self.file_upload_button_selector = self.locator_manager.get_locator('OptionLocators', 'file_upload_button_selector')
|
||||||
|
|
@ -268,6 +269,8 @@ class OptionHandler:
|
||||||
if toggle_states['optionAutoSelect']:
|
if toggle_states['optionAutoSelect']:
|
||||||
self.logger.debug(f"옵션 필터링 및 조정 : {toggle_states['optionAutoSelect']}")
|
self.logger.debug(f"옵션 필터링 및 조정 : {toggle_states['optionAutoSelect']}")
|
||||||
await self.filter_and_adjust_options(max_option_count)
|
await self.filter_and_adjust_options(max_option_count)
|
||||||
|
# 가격 낮은 순 재정렬 클릭
|
||||||
|
await self.low_order_click()
|
||||||
|
|
||||||
# 6. 선택된 옵션정보 재수집
|
# 6. 선택된 옵션정보 재수집
|
||||||
if self.is_percenty_success:
|
if self.is_percenty_success:
|
||||||
|
|
@ -629,6 +632,7 @@ class OptionHandler:
|
||||||
try:
|
try:
|
||||||
selected_count = 0 # 현재까지 선택된 옵션 수
|
selected_count = 0 # 현재까지 선택된 옵션 수
|
||||||
|
|
||||||
|
self.logger.info(f"최대 선택 가능 옵션수 설정값 : {max_option_count}")
|
||||||
for i, name in enumerate(self.option_info['original_names'].values()):
|
for i, name in enumerate(self.option_info['original_names'].values()):
|
||||||
# 최대 옵션 수에 도달하면 더 이상 선택하지 않음
|
# 최대 옵션 수에 도달하면 더 이상 선택하지 않음
|
||||||
if max_option_count > 0 and selected_count >= max_option_count:
|
if max_option_count > 0 and selected_count >= max_option_count:
|
||||||
|
|
@ -716,9 +720,9 @@ class OptionHandler:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 모든 옵션 상자 요소 가져오기
|
# 모든 옵션 상자 요소 가져오기
|
||||||
# option_boxes = await self.page.query_selector_all(self.option_box_selector)
|
option_boxes = await self.page.query_selector_all(self.option_box_selector)
|
||||||
|
|
||||||
option_boxes = await self.page.query_selector_all("div#productMainContentContainerId li > div > div:nth-child(1) > div > div:nth-child(2) > div")
|
# option_boxes = await self.page.query_selector_all("div#productMainContentContainerId li > div > div:nth-child(1) > div > div:nth-child(2) > div")
|
||||||
|
|
||||||
# option_image_element = await self.page.locator("xpath=//*[@id='productMainContentContainerId']/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[1]/div/div[1]/div/div[2]/div/img").element_handle()
|
# option_image_element = await self.page.locator("xpath=//*[@id='productMainContentContainerId']/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[1]/div/div[1]/div/div[2]/div/img").element_handle()
|
||||||
|
|
||||||
|
|
@ -732,11 +736,18 @@ class OptionHandler:
|
||||||
# 선택자에서 인덱스를 반영해 동적 선택자를 생성
|
# 선택자에서 인덱스를 반영해 동적 선택자를 생성
|
||||||
add_button_selector = self.add_button_selector.format(index=index)
|
add_button_selector = self.add_button_selector.format(index=index)
|
||||||
|
|
||||||
|
# 옵션 박스 내부의 텍스트 확인하여 "제외된 옵션" 포함 여부 검사
|
||||||
|
option_text_content = await option_box.inner_text()
|
||||||
|
if "제외된 옵션" in option_text_content:
|
||||||
|
self.logger.debug(f"{index}번째 옵션은 제외된 옵션입니다. 번역을 생략합니다.")
|
||||||
|
continue # 제외된 옵션이므로 다음 옵션으로 이동
|
||||||
|
|
||||||
# 옵션 이미지가 존재하는지 확인
|
# 옵션 이미지가 존재하는지 확인
|
||||||
option_image = await option_box.query_selector("img")
|
option_image = await option_box.query_selector("img")
|
||||||
if option_image is None:
|
if option_image is None:
|
||||||
self.logger.debug(f"{index}번째 옵션에 이미지가 없습니다. 다음 옵션으로 이동합니다.")
|
self.logger.debug(f"{index}번째 옵션에 이미지가 없습니다. 다음 옵션으로 이동합니다.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
option_image_url = await option_image.get_attribute("src")
|
option_image_url = await option_image.get_attribute("src")
|
||||||
self.logger.debug(f"{index}번째 옵션 이미지 URL: {option_image_url}")
|
self.logger.debug(f"{index}번째 옵션 이미지 URL: {option_image_url}")
|
||||||
|
|
||||||
|
|
@ -765,34 +776,47 @@ class OptionHandler:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 기본 선택자로 삭제 버튼 찾기
|
# 기본 선택자로 삭제 버튼 찾기
|
||||||
# delete_button = option_box.query_selector(self.delete_button_selector)
|
|
||||||
delete_button = self.page.locator(f'{self.delete_button_selector_template.format(index=index)}')
|
delete_button = self.page.locator(f'{self.delete_button_selector_template.format(index=index)}')
|
||||||
await delete_button.wait_for(state="attached", timeout=5000) # 타임아웃 설정
|
await delete_button.wait_for(state="attached", timeout=5000) # 타임아웃 설정
|
||||||
|
|
||||||
if not await delete_button.is_visible():
|
if not await delete_button.is_visible():
|
||||||
# fallback으로 재시도
|
# fallback으로 재시도
|
||||||
delete_button = self.page.locator(f'xpath={self.fallback1_delete_button_selector_template.format(index=index)}')
|
delete_button = self.page.locator(f'xpath={self.fallback1_delete_button_selector_template.format(index=index)}')
|
||||||
delete_button.set_timeout(5000)
|
await delete_button.wait_for(state="attached", timeout=5000)
|
||||||
|
|
||||||
if await delete_button.is_visible():
|
if await delete_button.is_visible():
|
||||||
await delete_button.click()
|
await delete_button.click()
|
||||||
self.logger.debug(f"{index}번째 옵션의 삭제 버튼 클릭")
|
self.logger.debug(f"{index}번째 옵션의 삭제 버튼 클릭")
|
||||||
|
|
||||||
|
# 다이알로그 확인 후 삭제 버튼 클릭
|
||||||
|
try:
|
||||||
|
self.logger.debug(f"{index}번째 옵션의 삭제 다이알로그 확인 중...")
|
||||||
|
dialog = await self.page.wait_for_selector(self.delete_dialog_selector, timeout=5000) # 다이알로그 클래스 확인
|
||||||
|
|
||||||
|
if dialog:
|
||||||
|
self.logger.debug(f"{index}번째 옵션의 삭제 다이알로그 확인됨")
|
||||||
|
|
||||||
|
# 삭제 확인 버튼 찾기
|
||||||
|
confirm_delete_button = await dialog.query_selector(self.confirm_delete_button_selector)
|
||||||
|
|
||||||
|
if confirm_delete_button:
|
||||||
|
await confirm_delete_button.click()
|
||||||
|
self.logger.debug(f"{index}번째 옵션의 삭제 확인 버튼 클릭됨")
|
||||||
|
else:
|
||||||
|
self.logger.error(f"{index}번째 옵션의 삭제 확인 버튼이 보이지 않습니다.")
|
||||||
|
else:
|
||||||
|
self.logger.error(f"{index}번째 옵션의 삭제 다이알로그가 나타나지 않았습니다.")
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"{index}번째 옵션의 삭제 다이알로그를 찾는 중 오류 발생: {e}", exc_info=True)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"{index}번째 옵션의 삭제 버튼을 찾는 중 오류 발생: {e}", exc_info=True)
|
self.logger.error(f"{index}번째 옵션의 삭제 버튼을 찾는 중 오류 발생: {e}", exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
if delete_button:
|
try:
|
||||||
# await delete_button.click()
|
|
||||||
# self.logger.debug(f"{index}번째 옵션의 이미지 삭제 버튼 클릭")
|
|
||||||
confirm_delete_button = await self.page.wait_for_selector(self.confirm_delete_button_selector)
|
|
||||||
self.logger.debug(f"{index}번째 옵션의 이미지 삭제확인 버튼 가져오기")
|
|
||||||
if confirm_delete_button:
|
|
||||||
await confirm_delete_button.click()
|
|
||||||
self.logger.debug(f"{index}번째 옵션의 기존 이미지가 삭제되었습니다.")
|
|
||||||
|
|
||||||
# '+ 버튼' 클릭 후 파일 업로드
|
# '+ 버튼' 클릭 후 파일 업로드
|
||||||
self.logger.debug(f"{index}번째 옵션의 이미지추가 버튼 가져오기")
|
self.logger.debug(f"{index}번째 옵션의 이미지추가 버튼 가져오기")
|
||||||
add_button = self.page.locator(add_button_selector)
|
add_button = await self.page.query_selector(add_button_selector)
|
||||||
|
|
||||||
|
|
||||||
if add_button:
|
if add_button:
|
||||||
await add_button.click()
|
await add_button.click()
|
||||||
|
|
@ -812,6 +836,9 @@ class OptionHandler:
|
||||||
self.logger.debug(f"{index}번째 옵션에 이미지가 업로드되었습니다.")
|
self.logger.debug(f"{index}번째 옵션에 이미지가 업로드되었습니다.")
|
||||||
else:
|
else:
|
||||||
self.logger.error(f"{index}번째 옵션의 파일 입력 요소를 찾을 수 없습니다.")
|
self.logger.error(f"{index}번째 옵션의 파일 입력 요소를 찾을 수 없습니다.")
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"{index}번째 옵션의 이미지를 추가하는 중 오류 발생: {e}", exc_info=True)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"{index}번째 옵션 이미지 번역 중 오류 발생: {e}", exc_info=True)
|
self.logger.error(f"{index}번째 옵션 이미지 번역 중 오류 발생: {e}", exc_info=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import KO_EN
|
||||||
import pyperclip # 클립보드 데이터를 확인하기 위한 라이브러리
|
import pyperclip # 클립보드 데이터를 확인하기 위한 라이브러리
|
||||||
from PIL import ImageGrab
|
from PIL import ImageGrab
|
||||||
import re
|
import re
|
||||||
# import psutil
|
|
||||||
|
|
||||||
class WhaleTranslator:
|
class WhaleTranslator:
|
||||||
def __init__(self, app, logger, secret_mode=True, vd_mode=False, pixel_check_interval=0.1, timeout=10, color_tolerance=20):
|
def __init__(self, app, logger, secret_mode=True, vd_mode=False, pixel_check_interval=0.1, timeout=10, color_tolerance=20):
|
||||||
|
|
@ -299,7 +298,7 @@ class WhaleTranslator:
|
||||||
self.logger.debug(f"페이지 로딩 완료 후 웨일 창의 가운데로 마우스 커서 이동")
|
self.logger.debug(f"페이지 로딩 완료 후 웨일 창의 가운데로 마우스 커서 이동")
|
||||||
# pyautogui.moveTo(960,580) # 마우스 센터로 이동
|
# pyautogui.moveTo(960,580) # 마우스 센터로 이동
|
||||||
self.move_mouse_to_center()
|
self.move_mouse_to_center()
|
||||||
time.sleep(1) # 마우스 이동 후 대기
|
time.sleep(0.4) # 마우스 이동 후 대기
|
||||||
|
|
||||||
# original_color = self.get_mouse_position_color() # 현재 색상 가져오기
|
# original_color = self.get_mouse_position_color() # 현재 색상 가져오기
|
||||||
# self.colors['before'] = original_color
|
# self.colors['before'] = original_color
|
||||||
|
|
@ -491,32 +490,7 @@ class WhaleTranslator:
|
||||||
time.sleep(1) # 페이지 로딩 대기
|
time.sleep(1) # 페이지 로딩 대기
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def close_whale_window_if_exists(self):
|
def close_whale_window_if_exists(self):
|
||||||
"""윈도우 핸들을 사용하여 웨일 창을 닫음"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
if not self.whale_hwnd:
|
|
||||||
self.logger.debug("웨일 창 핸들이 설정되지 않았습니다.")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 핸들이 유효한지 확인
|
|
||||||
if win32gui.IsWindow(self.whale_hwnd):
|
|
||||||
self.logger.debug(f"웨일 창 핸들을 찾았습니다: {self.whale_hwnd}. 종료 중...")
|
|
||||||
win32gui.PostMessage(self.whale_hwnd, win32con.WM_CLOSE, 0, 0) # 창을 종료하는 메시지 전송
|
|
||||||
time.sleep(1) # 잠시 대기하여 창 종료 확인
|
|
||||||
if not win32gui.IsWindow(self.whale_hwnd):
|
|
||||||
self.logger.debug("웨일 창이 성공적으로 종료되었습니다.")
|
|
||||||
else:
|
|
||||||
self.logger.debug("웨일 창 종료에 실패했습니다.")
|
|
||||||
else:
|
|
||||||
self.logger.debug("유효하지 않은 웨일 창 핸들입니다.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"핸들을 사용한 웨일 창 종료 중 오류 발생: {e}", exc_info=True)
|
|
||||||
|
|
||||||
|
|
||||||
def close_whale_window_if_exists_ori(self):
|
|
||||||
"""웨일 브라우저 창을 프로세스 ID(pid)로 찾아 종료"""
|
"""웨일 브라우저 창을 프로세스 ID(pid)로 찾아 종료"""
|
||||||
try:
|
try:
|
||||||
if not self.whale_pid:
|
if not self.whale_pid:
|
||||||
|
|
@ -816,7 +790,8 @@ class WhaleTranslator:
|
||||||
max_retries (int): 최대 재시도 횟수.
|
max_retries (int): 최대 재시도 횟수.
|
||||||
"""
|
"""
|
||||||
retry_count = 0
|
retry_count = 0
|
||||||
|
self.move_mouse_to_center()
|
||||||
|
|
||||||
while retry_count < max_retries:
|
while retry_count < max_retries:
|
||||||
self.logger.debug(f"마우스 오른쪽 클릭 시도 #{retry_count + 1}")
|
self.logger.debug(f"마우스 오른쪽 클릭 시도 #{retry_count + 1}")
|
||||||
pyautogui.rightClick()
|
pyautogui.rightClick()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue