352 lines
17 KiB
Python
352 lines
17 KiB
Python
# main.py
|
|
import sys
|
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QPushButton, QTextBrowser, QLineEdit, QLabel, QCheckBox,
|
|
QComboBox, QProgressBar, QWidget, QMessageBox, QTabWidget)
|
|
from PyQt5.QtCore import Qt, QThread, pyqtSignal
|
|
from playwright.async_api import async_playwright
|
|
import configparser
|
|
import logging
|
|
import json
|
|
from cryptography.fernet import Fernet
|
|
import asyncio
|
|
|
|
# 설정 로드 및 저장
|
|
def load_config():
|
|
config = configparser.ConfigParser()
|
|
config.read('config.ini')
|
|
return config
|
|
|
|
def save_config(config):
|
|
with open('config.ini', 'w') as configfile:
|
|
config.write(configfile)
|
|
|
|
# 암호화 키 생성 및 로드
|
|
def generate_key():
|
|
return Fernet.generate_key()
|
|
|
|
def load_key():
|
|
try:
|
|
with open("secret.key", "rb") as key_file:
|
|
return key_file.read()
|
|
except FileNotFoundError:
|
|
key = generate_key()
|
|
with open("secret.key", "wb") as key_file:
|
|
key_file.write(key)
|
|
return key
|
|
|
|
# 암호화 및 복호화 함수
|
|
def encrypt_data(data, key):
|
|
fernet = Fernet(key)
|
|
return fernet.encrypt(data.encode())
|
|
|
|
def decrypt_data(data, key):
|
|
fernet = Fernet(key)
|
|
return fernet.decrypt(data).decode()
|
|
|
|
# 로그 설정
|
|
def setup_logging():
|
|
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
logging.info("Logging setup complete.")
|
|
|
|
class LicenseAgreement(QWidget):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.initUI()
|
|
|
|
def initUI(self):
|
|
self.setWindowTitle('License Agreement')
|
|
layout = QVBoxLayout()
|
|
|
|
self.licenseText = QTextBrowser()
|
|
self.licenseText.setPlainText("License Agreement Text Here...")
|
|
layout.addWidget(self.licenseText)
|
|
|
|
self.acceptCheckbox = QCheckBox("I accept the terms and conditions.")
|
|
self.acceptCheckbox.stateChanged.connect(self.toggleAcceptButton)
|
|
layout.addWidget(self.acceptCheckbox)
|
|
|
|
self.acceptButton = QPushButton("Accept")
|
|
self.acceptButton.setEnabled(False)
|
|
self.acceptButton.clicked.connect(self.accept)
|
|
layout.addWidget(self.acceptButton)
|
|
|
|
self.cancelButton = QPushButton("Cancel")
|
|
self.cancelButton.clicked.connect(self.cancel)
|
|
layout.addWidget(self.cancelButton)
|
|
|
|
self.setLayout(layout)
|
|
|
|
def toggleAcceptButton(self, state):
|
|
self.acceptButton.setEnabled(state == Qt.Checked)
|
|
|
|
def accept(self):
|
|
self.accepted = True
|
|
self.close()
|
|
|
|
def cancel(self):
|
|
self.accepted = False
|
|
self.close()
|
|
|
|
class FetchSettingsThread(QThread):
|
|
result_ready = pyqtSignal(dict)
|
|
|
|
def run(self):
|
|
asyncio.run(self.fetch_market_settings())
|
|
|
|
async def fetch_market_settings(self):
|
|
async with async_playwright() as p:
|
|
browser = await p.chromium.launch(headless=False)
|
|
page = await browser.new_page()
|
|
await page.goto("https://percenty.co.kr")
|
|
await page.click(".signList > .ant-btn-default > span")
|
|
await page.fill(".ant-input:nth-child(4)", "your_username")
|
|
await page.fill(".ant-input:nth-child(1)", "your_password")
|
|
await page.click(".ant-btn-primary")
|
|
|
|
# 팝업 다이얼로그 닫기
|
|
try:
|
|
await page.click('xpath=body > div:nth-child(10) > div > div.ant-modal-wrap.ant-modal-centered > div > div.ant-modal-content > div.ant-modal-footer > button.ant-btn.css-1li46mu.ant-btn-primary')
|
|
except:
|
|
pass
|
|
|
|
await page.click('xpath=/html/body/div[1]/div/div/div/div/aside/div/ul/li[7]/ul/li[2]')
|
|
|
|
market_data = {}
|
|
# 각 마켓의 API key를 가져오는 로직
|
|
markets = {
|
|
"쿠팡": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[1]",
|
|
"스마트스토어": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[2]",
|
|
"옥션지마켓": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[3]",
|
|
"11번가-일반": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[4]",
|
|
"11번가-글로벌": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[5]",
|
|
"롯데온": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[6]",
|
|
}
|
|
|
|
for market, xpath in markets.items():
|
|
await page.click(f'xpath={xpath}')
|
|
if market == "쿠팡":
|
|
market_data[market] = {
|
|
"쿠팡ID": await page.get_attribute('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div/div/div[1]/div/div[2]/div/div[2]/div[1]/input', 'value'),
|
|
"업체코드": await page.get_attribute('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div/div/div[1]/div/div[2]/div/div[2]/div[2]/input', 'value'),
|
|
"Access Key": await page.get_attribute('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div/div/div[1]/div/div[2]/div/div[2]/div[3]/input', 'value'),
|
|
"Secret Key": await page.get_attribute('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div/div/div[1]/div/div[2]/div/div[2]/div[4]/input', 'value')
|
|
}
|
|
elif market == "스마트스토어":
|
|
market_data[market] = {
|
|
"애플리케이션 ID": await page.get_attribute('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[2]/div/div[1]/div/div[2]/div/div/div[2]/input', 'value'),
|
|
"애플리케이션 시크릿": await page.get_attribute('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[2]/div/div[1]/div/div[2]/div/div/div[3]/input', 'value')
|
|
}
|
|
elif market == "옥션지마켓":
|
|
market_data[market] = {
|
|
"옥션ID": await page.get_attribute('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[3]/div/div[1]/div/div[2]/div/div/div[1]/input', 'value'),
|
|
"G마켓ID": await page.get_attribute('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[3]/div/div[1]/div/div[2]/div/div/div[2]/input', 'value')
|
|
}
|
|
elif market == "11번가-일반":
|
|
market_data[market] = {
|
|
"API KEY": await page.get_attribute('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[4]/div/div[1]/div/div[2]/div/div/div[1]/input', 'value')
|
|
}
|
|
elif market == "11번가-글로벌":
|
|
market_data[market] = {
|
|
"API KEY": await page.get_attribute('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[5]/div/div[1]/div/div[2]/div/div/div[1]/input', 'value')
|
|
}
|
|
elif market == "롯데온":
|
|
market_data[market] = {
|
|
"API KEY": await page.get_attribute('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[6]/div/div[1]/div/div[2]/div/div/div[1]/input', 'value')
|
|
}
|
|
|
|
await browser.close()
|
|
self.result_ready.emit(market_data)
|
|
|
|
class ChangeBusinessThread(QThread):
|
|
progress_update = pyqtSignal(int)
|
|
def __init__(self, market_data):
|
|
super().__init__()
|
|
self.market_data = market_data
|
|
|
|
def run(self):
|
|
asyncio.run(self.change_business())
|
|
|
|
async def change_business(self):
|
|
async with async_playwright() as p:
|
|
browser = await p.chromium.launch(headless=False)
|
|
page = await browser.new_page()
|
|
await page.goto("https://percenty.co.kr")
|
|
await page.click(".signList > .ant-btn-default > span")
|
|
await page.fill(".ant-input:nth-child(4)", "your_username")
|
|
await page.fill(".ant-input:nth-child(1)", "your_password")
|
|
await page.click(".ant-btn-primary")
|
|
|
|
# 팝업 다이얼로그 닫기
|
|
try:
|
|
await page.click('xpath=body > div:nth-child(10) > div > div.ant-modal-wrap.ant-modal-centered > div > div.ant-modal-content > div.ant-modal-footer > button.ant-btn.css-1li46mu.ant-btn-primary')
|
|
except:
|
|
pass
|
|
|
|
await page.click('xpath=/html/body/div[1]/div/div/div/div/aside/div/ul/li[7]/ul/li[2]')
|
|
|
|
markets = self.market_data.keys()
|
|
progress_step = 100 // len(markets)
|
|
progress = 0
|
|
|
|
for market in markets:
|
|
market_xpath = {
|
|
"쿠팡": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[1]",
|
|
"스마트스토어": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[2]",
|
|
"옥션지마켓": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[3]",
|
|
"11번가-일반": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[4]",
|
|
"11번가-글로벌": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[5]",
|
|
"롯데온": "/html/body/div[1]/div/div/div/div/main/div[2]/div/div[1]/div[1]/div/div[6]"
|
|
}[market]
|
|
|
|
await page.click(f'xpath={market_xpath}')
|
|
data = self.market_data[market]
|
|
if market == "쿠팡":
|
|
await page.fill('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div/div/div[1]/div/div[2]/div/div[2]/div[1]/input', data["쿠팡ID"])
|
|
await page.fill('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div/div/div[1]/div/div[2]/div/div[2]/div[2]/input', data["업체코드"])
|
|
await page.fill('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div/div/div[1]/div/div[2]/div/div[2]/div[3]/input', data["Access Key"])
|
|
await page.fill('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div/div/div[1]/div/div[2]/div/div[2]/div[4]/input', data["Secret Key"])
|
|
elif market == "스마트스토어":
|
|
await page.fill('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[2]/div/div[1]/div/div[2]/div/div/div[2]/input', data["애플리케이션 ID"])
|
|
await page.fill('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[2]/div/div[1]/div/div[2]/div/div/div[3]/input', data["애플리케이션 시크릿"])
|
|
elif market == "옥션지마켓":
|
|
await page.fill('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[3]/div/div[1]/div/div[2]/div/div/div[1]/input', data["옥션ID"])
|
|
await page.fill('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[3]/div/div[1]/div/div[2]/div/div/div[2]/input', data["G마켓ID"])
|
|
elif market == "11번가-일반":
|
|
await page.fill('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[4]/div/div[1]/div/div[2]/div/div/div[1]/input', data["API KEY"])
|
|
elif market == "11번가-글로벌":
|
|
await page.fill('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[5]/div/div[1]/div/div[2]/div/div/div[1]/input', data["API KEY"])
|
|
elif market == "롯데온":
|
|
await page.fill('xpath=/html/body/div[1]/div/div/div/div/main/div[2]/div/div[2]/div/div[6]/div/div[1]/div/div[2]/div/div/div[1]/input', data["API KEY"])
|
|
|
|
progress += progress_step
|
|
self.progress_update.emit(progress)
|
|
|
|
await browser.close()
|
|
self.progress_update.emit(100)
|
|
|
|
class MainWindow(QMainWindow):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.initUI()
|
|
self.config = load_config()
|
|
self.key = load_key()
|
|
self.fetch_thread = FetchSettingsThread()
|
|
self.fetch_thread.result_ready.connect(self.updateStatus)
|
|
self.load_business_data()
|
|
|
|
def initUI(self):
|
|
self.setWindowTitle('Percenty API Manager')
|
|
|
|
mainLayout = QVBoxLayout()
|
|
|
|
self.settingButtonLayout = QHBoxLayout()
|
|
self.currentSettingButton = QPushButton("현재 설정 가져오기")
|
|
self.saveSettingButton = QPushButton("현재 설정 저장하기")
|
|
self.currentSettingButton.clicked.connect(self.fetch_current_settings)
|
|
self.saveSettingButton.clicked.connect(self.save_current_settings)
|
|
self.settingButtonLayout.addWidget(self.currentSettingButton)
|
|
self.settingButtonLayout.addWidget(self.saveSettingButton)
|
|
mainLayout.addLayout(self.settingButtonLayout)
|
|
|
|
self.currentStatusLayout = QHBoxLayout()
|
|
self.statusBox1 = QLabel("사업자 현황")
|
|
self.statusBox2 = QLabel("마켓 현황")
|
|
self.statusBox3 = QLabel("빈 박스")
|
|
self.currentStatusLayout.addWidget(self.statusBox1)
|
|
self.currentStatusLayout.addWidget(self.statusBox2)
|
|
self.currentStatusLayout.addWidget(self.statusBox3)
|
|
mainLayout.addLayout(self.currentStatusLayout)
|
|
|
|
self.selectSettingLayout = QVBoxLayout()
|
|
self.businessDropdown = QComboBox()
|
|
self.businessDropdown.currentIndexChanged.connect(self.update_market_checkboxes)
|
|
self.marketCheckboxLayout = QVBoxLayout()
|
|
self.changeBusinessButton = QPushButton("사업자 바꾸기")
|
|
self.changeBusinessButton.clicked.connect(self.change_business)
|
|
self.progressBar = QProgressBar()
|
|
self.selectSettingLayout.addWidget(self.businessDropdown)
|
|
self.selectSettingLayout.addLayout(self.marketCheckboxLayout)
|
|
self.selectSettingLayout.addWidget(self.changeBusinessButton)
|
|
self.selectSettingLayout.addWidget(self.progressBar)
|
|
mainLayout.addLayout(self.selectSettingLayout)
|
|
|
|
widget = QWidget()
|
|
widget.setLayout(mainLayout)
|
|
self.setCentralWidget(widget)
|
|
|
|
def load_business_data(self):
|
|
try:
|
|
loaded_data = self.config['BUSINESS_DATA']['data']
|
|
decrypted_data = decrypt_data(loaded_data, self.key)
|
|
self.business_data = json.loads(decrypted_data)
|
|
self.update_business_dropdown()
|
|
except KeyError:
|
|
self.business_data = {}
|
|
|
|
def update_business_dropdown(self):
|
|
self.businessDropdown.clear()
|
|
for business_name in self.business_data.keys():
|
|
self.businessDropdown.addItem(business_name)
|
|
self.update_market_checkboxes()
|
|
|
|
def fetch_current_settings(self):
|
|
self.fetch_thread.start()
|
|
|
|
def save_current_settings(self):
|
|
encrypted_data = encrypt_data(json.dumps(self.business_data), self.key)
|
|
self.config['BUSINESS_DATA'] = {'data': encrypted_data}
|
|
save_config(self.config)
|
|
QMessageBox.information(self, "Success", "Current settings have been saved.")
|
|
|
|
def updateStatus(self, market_data):
|
|
business_name = self.businessDropdown.currentText()
|
|
if business_name:
|
|
self.business_data[business_name]["markets"] = market_data
|
|
self.update_market_checkboxes()
|
|
self.statusBox1.setText(f"사업자 현황: {business_name}")
|
|
self.statusBox2.setText(json.dumps(market_data, indent=4, ensure_ascii=False))
|
|
|
|
def update_market_checkboxes(self):
|
|
business_name = self.businessDropdown.currentText()
|
|
if not business_name:
|
|
return
|
|
|
|
self.clear_layout(self.marketCheckboxLayout)
|
|
|
|
markets = self.business_data[business_name]["markets"]
|
|
for market, details in markets.items():
|
|
market_checkbox = QCheckBox(market)
|
|
market_checkbox.setChecked(details["api_key"] != "")
|
|
self.marketCheckboxLayout.addWidget(market_checkbox)
|
|
|
|
def clear_layout(self, layout):
|
|
while layout.count():
|
|
child = layout.takeAt(0)
|
|
if child.widget():
|
|
child.widget().deleteLater()
|
|
|
|
def change_business(self):
|
|
business_name = self.businessDropdown.currentText()
|
|
selected_markets = [checkbox.text() for checkbox in self.findChildren(QCheckBox) if checkbox.isChecked()]
|
|
|
|
if business_name:
|
|
market_data = {market: self.business_data[business_name]["markets"][market] for market in selected_markets}
|
|
self.change_thread = ChangeBusinessThread(market_data)
|
|
self.change_thread.progress_update.connect(self.update_progress)
|
|
self.change_thread.start()
|
|
|
|
def update_progress(self, value):
|
|
self.progressBar.setValue(value)
|
|
|
|
if __name__ == "__main__":
|
|
setup_logging()
|
|
app = QApplication(sys.argv)
|
|
licenseWindow = LicenseAgreement()
|
|
licenseWindow.show()
|
|
app.exec_()
|
|
|
|
if licenseWindow.accepted:
|
|
mainWindow = MainWindow()
|
|
mainWindow.show()
|
|
sys.exit(app.exec_())
|