This commit is contained in:
9700X_PC 2025-02-11 01:49:56 +09:00
parent 4520763b90
commit 1db59535fb
249 changed files with 1241 additions and 277 deletions

BIN
1orders.db Normal file

Binary file not shown.

15
app.log
View File

@ -0,0 +1,15 @@
[2025-02-11 01:38:20,160] [INFO] 시작 중...
[2025-02-11 01:42:17,322] [INFO] 시작 중...
[2025-02-11 01:43:32,874] [INFO] 시작 중...
[2025-02-11 01:45:43,657] [INFO] 시작 중...
[2025-02-11 01:45:44,158] [INFO] 브라우저 연결 완료.
[2025-02-11 01:45:46,397] [INFO] QR 코드가 감지됨. 사용자 로그인 필요.
[2025-02-11 01:47:38,717] [INFO] 시작 중...
[2025-02-11 01:47:39,284] [INFO] 브라우저 연결 완료.
[2025-02-11 01:47:40,574] [INFO] QR 코드가 감지됨. 사용자 로그인 필요.
[2025-02-11 01:47:50,568] [INFO] 이전 로그인 세션이 유지됨. QR 코드 표시 없이 진행.
[2025-02-11 01:49:08,562] [INFO] 시작 중...
[2025-02-11 01:49:09,080] [INFO] 브라우저 연결 완료.
[2025-02-11 01:49:10,197] [INFO] QR 코드가 감지됨. 사용자 로그인 필요.
[2025-02-11 01:49:27,823] [INFO] 수신자 입력란을 찾지 못했습니다: Page.wait_for_selector: Target page, context or browser has been closed
[2025-02-11 01:49:30,406] [INFO] 수신자 입력란을 찾지 못했습니다: Page.wait_for_selector: Target page, context or browser has been closed

View File

@ -1,28 +1,49 @@
# gui/main_window.py
from PySide6.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QTextEdit, QMenuBar, QMenu, QLabel, QTableWidget, QTableWidgetItem, QMessageBox)
import asyncio
from datetime import datetime
from PySide6.QtWidgets import (
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QTextEdit, QMenuBar, QMenu, QLabel,
QTableWidget, QTableWidgetItem, QMessageBox
)
from PySide6.QtGui import QAction
from PySide6.QtCore import Qt, Slot
from PySide6.QtCore import Qt, Slot, QSettings, QTimer
from gui.order_input_dialog import OrderInputDialog
from gui.template_management_dialog import TemplateManagementDialog
from gui.help_dialog import HelpDialog
from gui.settings_dialog import SettingsDialog
from src.database_module import DatabaseManager
from src.sms_module import SMSMessenger # SMS 전송 모듈
# 메인 창 클래스
class MainWindow(QMainWindow):
def __init__(self, logger):
super().__init__()
self.logger = logger
self.settings = QSettings("When_Ride_Mycar", "SMS_Sender")
self.db_manager = DatabaseManager() # SQLite, SQLAlchemy 기반 DB 관리자
self.setWindowTitle("주문 알림 SMS 전송 프로그램")
self.resize(900, 700)
self.setup_menu()
self.setup_ui()
self.apply_styles()
# SMSMessenger를 생성합니다.
self.sms_messenger = SMSMessenger(self.logger, headless=False, delay=1)
# 이벤트 루프가 실행된 후 (0ms 후) connect()를 실행하도록 예약합니다.
QTimer.singleShot(0, self.start_sms_connection)
# 매달 1일(또는 저장된 월과 현재 월이 다르면) SMS 카운트 초기화
self.check_and_reset_sms_count()
self.update_sms_count_label()
self.refresh_order_list()
def start_sms_connection(self):
asyncio.create_task(self.sms_messenger.connect())
def setup_menu(self):
menu_bar = self.menuBar()
file_menu = menu_bar.addMenu("파일")
@ -57,10 +78,17 @@ class MainWindow(QMainWindow):
main_layout.addLayout(button_layout)
# 주문 목록 테이블
# SMS 발송 건수 표시 (예: "이번달 총 발송건수: X 건")
self.sms_count_label = QLabel()
self.sms_count_label.setToolTip("이번달 총 SMS 발송 건수")
main_layout.addWidget(self.sms_count_label)
# 주문 목록 테이블 (열 수를 8로 설정)
self.order_table = QTableWidget()
self.order_table.setColumnCount(6)
self.order_table.setHorizontalHeaderLabels(["ID", "상품명", "고객명", "전화번호", "현재단계", "SMS발송여부", "최종 업데이트"])
self.order_table.setColumnCount(8)
self.order_table.setHorizontalHeaderLabels([
"ID", "상품명", "고객명", "전화번호", "현재단계", "SMS발송여부", "지금발송", "최종 업데이트"
])
self.order_table.setToolTip("현재 진행 중인 주문 목록을 표시합니다.")
main_layout.addWidget(QLabel("주문 목록:"))
main_layout.addWidget(self.order_table)
@ -73,7 +101,6 @@ class MainWindow(QMainWindow):
main_layout.addWidget(self.log_display)
self.logger.log_signal.connect(self.append_log)
self.order_table.itemChanged.connect(self.item_changed_slot)
@Slot(str)
@ -81,89 +108,140 @@ class MainWindow(QMainWindow):
self.log_display.append(message)
def open_order_input_dialog(self):
dialog = OrderInputDialog(self.logger, parent=self)
dialog = OrderInputDialog(self.logger, self.db_manager, parent=self)
if dialog.exec():
# 주문 입력 완료 후 주문 목록 새로고침
self.refresh_order_list()
def open_template_management_dialog(self):
dialog = TemplateManagementDialog(self.logger, parent=self)
dialog = TemplateManagementDialog(self.logger, self.db_manager, parent=self)
dialog.exec()
def open_settings_dialog(self):
dialog = SettingsDialog(self)
dialog = SettingsDialog(self.logger, self.db_manager)
dialog.exec()
def show_help(self):
dialog = HelpDialog(parent=self)
dialog.exec()
def check_and_reset_sms_count(self):
"""현재 월과 저장된 월을 비교하여, 달이 변경되었으면 SMS 카운트를 초기화"""
current_month = datetime.now().strftime("%Y-%m")
stored_month = self.settings.value("sms_count_month", "")
if stored_month != current_month:
# 새로운 달이 시작되었으므로 카운트를 0으로 초기화하고 저장
self.settings.setValue("sms_count", 0)
self.settings.setValue("sms_count_month", current_month)
def update_sms_count_label(self):
"""SMS 카운트 라벨 업데이트"""
count = self.settings.value("sms_count", 0, type=int)
self.sms_count_label.setText(f"이번달 총 발송건수: {count}")
def increment_sms_count(self):
"""SMS 전송 성공 시 카운트를 1 증가시키고 라벨 업데이트"""
count = self.settings.value("sms_count", 0, type=int)
count += 1
self.settings.setValue("sms_count", count)
self.update_sms_count_label()
def refresh_order_list(self):
orders = self.db_manager.get_all_orders()
self.order_table.blockSignals(True) # 초기화 시 itemChanged 시그널 방지
self.order_table.setRowCount(len(orders))
for row, order in enumerate(orders):
# 각 셀을 편집 가능하게 설정
# ID (편집 불가)
id_item = QTableWidgetItem(str(order.id))
id_item.setFlags(id_item.flags() & ~Qt.ItemIsEditable)
self.order_table.setItem(row, 0, id_item)
name_item = QTableWidgetItem(order.customer_name or "")
name_item.setFlags(name_item.flags() | Qt.ItemIsEditable)
self.order_table.setItem(row, 1, name_item)
# 상품명 (편집 가능)
product_item = QTableWidgetItem(order.product_name or "")
product_item.setFlags(product_item.flags() | Qt.ItemIsEditable)
self.order_table.setItem(row, 2, product_item)
self.order_table.setItem(row, 1, product_item)
# 고객명 (편집 가능)
name_item = QTableWidgetItem(order.customer_name or "")
name_item.setFlags(name_item.flags() | Qt.ItemIsEditable)
self.order_table.setItem(row, 2, name_item)
# 전화번호 (편집 가능)
phone_item = QTableWidgetItem(order.customer_phone)
phone_item.setFlags(phone_item.flags() | Qt.ItemIsEditable)
self.order_table.setItem(row, 3, phone_item)
# 현재단계 (편집 가능)
step_item = QTableWidgetItem(str(order.order_step))
step_item.setFlags(step_item.flags() & ~Qt.ItemIsEditable)
step_item.setFlags(step_item.flags() | Qt.ItemIsEditable)
self.order_table.setItem(row, 4, step_item)
sms_item = QTableWidgetItem("전송 완료" if order.domestic_tracking else "미전송")
# SMS발송여부 (편집 불가)
sms_status = "전송 완료" if order.domestic_tracking else "미전송"
sms_item = QTableWidgetItem(sms_status)
sms_item.setFlags(sms_item.flags() & ~Qt.ItemIsEditable)
self.order_table.setItem(row, 5, sms_item)
# "지금발송" 버튼 (셀 위젯)
send_btn = QPushButton("지금발송")
send_btn.setToolTip("SMS를 즉시 전송합니다.")
# SMS 미전송인 경우에만 버튼 활성화
send_btn.setEnabled(False if order.domestic_tracking else True)
# 버튼 클릭 시 해당 주문의 SMS를 전송하는 비동기 함수 호출
send_btn.clicked.connect(lambda _, o=order: asyncio.create_task(self.send_sms_for_order(o)))
self.order_table.setCellWidget(row, 6, send_btn)
# 최종 업데이트 (편집 불가)
updated_at_str = order.updated_at.strftime("%Y-%m-%d %H:%M:%S") if order.updated_at else ""
update_item = QTableWidgetItem(updated_at_str)
update_item.setFlags(update_item.flags() & ~Qt.ItemIsEditable)
self.order_table.setItem(row, 6, update_item)
self.order_table.setItem(row, 7, update_item)
self.order_table.blockSignals(False)
async def send_sms_for_order(self, order):
"""주문 객체를 인자로 받아 SMS를 전송하는 비동기 함수"""
recipient = order.customer_phone
# 실제 템플릿과 주문 데이터를 결합하여 메시지 작성 (여기서는 예시 메시지 사용)
message = "주문 접수 메시지 예시"
self.logger.log(f"Order {order.id}: SMS 전송 시도 (받는 사람: {recipient})", level=1)
result = await self.sms_messenger.send_sms(recipient, message)
self.logger.log(f"Order {order.id}: SMS 전송 결과: {result}", level=1)
if result.get("success"):
# 전송 성공 시 DB 업데이트 및 SMS 발송 카운트 증가
self.db_manager.update_order(order.id, domestic_tracking="전송 완료")
self.increment_sms_count()
self.refresh_order_list()
@Slot()
def item_changed_slot(self):
# 사용자가 셀을 수정한 경우, 수정 내용을 DB에 업데이트하고 확인 메시지를 보여줌
def item_changed_slot(self, item):
# 사용자가 셀을 수정한 경우, 수정 내용을 DB에 업데이트합니다.
row = self.order_table.currentRow()
if row < 0:
return
try:
order_id = int(self.order_table.item(row, 0).text())
customer_name = self.order_table.item(row, 1).text()
product_name = self.order_table.item(row, 2).text()
except Exception:
return
customer_name = self.order_table.item(row, 2).text()
product_name = self.order_table.item(row, 1).text()
customer_phone = self.order_table.item(row, 3).text()
# 기타 수정 가능한 필드는 필요에 따라 추가
# 수정 내용을 데이터베이스에 업데이트 (예시)
updated_order = self.db_manager.update_order(order_id,
order_step_text = self.order_table.item(row, 4).text()
try:
order_step = int(order_step_text)
except ValueError:
QMessageBox.warning(self, "입력 오류", "진행 단계는 정수 값으로 입력해주세요.")
return
updated_order = self.db_manager.update_order(
order_id,
customer_name=customer_name,
product_name=product_name,
customer_phone=customer_phone)
# 확인 메시지 표시
customer_phone=customer_phone,
order_step=order_step
)
QMessageBox.information(self, "저장 확인", f"주문서(ID {order_id})가 수정되었습니다.")
self.logger.log(f"주문서(ID {order_id}) 수정: 고객명={customer_name}, 상품명={product_name}, 전화번호={customer_phone}", level=1)
def open_settings_dialog(self):
from gui.settings_dialog import SettingsDialog
dialog = SettingsDialog(self)
dialog.exec()
def show_help(self):
from gui.help_dialog import HelpDialog
dialog = HelpDialog(parent=self)
dialog.exec()
self.logger.log(
f"주문서(ID {order_id}) 수정: 고객명={customer_name}, 상품명={product_name}, 전화번호={customer_phone}, 진행단계={order_step}",
level=1
)
def apply_styles(self):
style = """

View File

@ -1,145 +1,268 @@
# gui/order_input_dialog.py (일부 발췌)
from PySide6.QtWidgets import QDialog, QFormLayout, QLineEdit, QComboBox, QPushButton, QHBoxLayout, QVBoxLayout, QLabel
from PySide6.QtCore import QSettings
# gui/order_input_dialog.py
import re
import sys
import asyncio
from PySide6.QtWidgets import (
QDialog, QFormLayout, QLineEdit, QComboBox, QPushButton, QHBoxLayout,
QVBoxLayout, QLabel, QMessageBox
)
from src.sms_module import SMSMessenger # SMS 전송 모듈 (테스트 코드 참조)
class OrderInputDialog(QDialog):
def __init__(self, logger, parent=None):
def __init__(self, logger, db_manager, parent=None):
super().__init__(parent)
self.logger = logger
self.db_manager = db_manager
self.setWindowTitle("주문정보 입력")
self.resize(600, 500)
self.sms_messenger = SMSMessenger(headless=False, delay=1) # SMS 테스트 모듈 (필요에 따라 설정)
self.setup_ui()
self.apply_styles()
order_data = {
"customer_name": self.customer_name_edit.text().strip(),
"product_name": self.product_name_edit.text().strip(), # 추가
"order_market": self.order_market_edit.text().strip(),
"customer_phone": self.customer_phone_edit.text().strip(),
"shop_name": self.shop_combo.currentText(),
"taobao_tracking": self.taobao_tracking_edit.text().strip(),
"delivery_agent": self.delivery_agent_edit.text().strip(),
"pre_carrier": self.pre_carrier_edit.text().strip(),
"domestic_tracking": self.tracking_edit.text().strip(),
"freight_tracking": self.freight_tracking_edit.text().strip(),
"cs_memo1": self.cs_memo1_edit.text().strip(),
"cs_memo2": self.cs_memo2_edit.text().strip(),
"order_step": None # '선택된 단계번호", # 예: 1 ~ 5
# 기타 필요한 필드...
}
def setup_ui(self):
layout = QVBoxLayout(self)
form_layout = QFormLayout()
# 샵 이름 선택 ComboBox 추가
# 1. 샵 이름 선택 ComboBox: DB에서 shop_names를 가져옴
self.shop_combo = QComboBox()
self.shop_combo.setToolTip("주문서에 사용할 샵 이름을 선택하세요")
settings = QSettings("MyCompany", "MySMSApp")
shop_names = settings.value("shop_names", [])
if not isinstance(shop_names, list):
shop_names = [shop_names] if shop_names else []
self.shop_combo.addItems(shop_names)
shop_names = self.db_manager.get_settings_by_category("shop_names")
formatted_shop_names = [f"[{shop.tag}사업자]-{shop.value}" for shop in shop_names]
self.shop_combo.addItems(formatted_shop_names)
form_layout.addRow("샵 이름:", self.shop_combo)
# 2. 고객 이름
self.customer_name_edit = QLineEdit()
self.customer_name_edit.setPlaceholderText("고객 이름 입력")
self.customer_name_edit.setToolTip("고객의 이름을 입력하세요")
form_layout.addRow("고객 이름:", self.customer_name_edit)
# 3. 상품명
self.product_name_edit = QLineEdit()
self.product_name_edit.setPlaceholderText("상품명")
self.product_name_edit.setToolTip("상품명을 입력하세요")
self.product_name_edit.setPlaceholderText("상품명 입력")
self.product_name_edit.setToolTip("주문하실 상품명을 입력하세요")
form_layout.addRow("상품명:", self.product_name_edit)
self.order_market_edit = QLineEdit()
self.order_market_edit.setPlaceholderText("주문 마켓 입력 (예: 쿠팡, 11번가 등)")
self.order_market_edit.setToolTip("주문이 발생한 국내 마켓을 입력하세요")
form_layout.addRow("주문 마켓:", self.order_market_edit)
# 4. 주문 마켓 선택 ComboBox: DB에서 order_markets를 가져옴
self.order_market_combo = QComboBox()
self.order_market_combo.setToolTip("주문이 발생한 국내 마켓을 선택하세요")
order_markets = self.db_manager.get_settings_by_category("order_markets")
self.order_market_combo.addItems(order_markets)
form_layout.addRow("주문 마켓:", self.order_market_combo)
# 5. 고객 전화번호 (자동 하이픈 삽입)
self.customer_phone_edit = QLineEdit()
self.customer_phone_edit.setPlaceholderText("010-1234-5678")
self.customer_phone_edit.setToolTip("고객 전화번호를 입력하세요")
self.customer_phone_edit.setToolTip("고객 전화번호를 입력하세요. 입력 중 자동 하이픈이 삽입됩니다.")
self.customer_phone_edit.textChanged.connect(self.format_phone_number_slot)
form_layout.addRow("고객 전화번호:", self.customer_phone_edit)
# 6. 타오바오 트래킹 번호
self.taobao_tracking_edit = QLineEdit()
self.taobao_tracking_edit.setPlaceholderText("타오바오 트래킹 번호 입력")
self.taobao_tracking_edit.setToolTip("타오바오에서 발행된 트래킹 번호")
form_layout.addRow("타오바오 트래킹:", self.taobao_tracking_edit)
# 7. 배대지 이름
self.delivery_agent_edit = QLineEdit()
self.delivery_agent_edit.setPlaceholderText("배대지 이름 입력")
self.delivery_agent_edit.setToolTip("배송대행지(배대지)의 이름을 입력하세요")
form_layout.addRow("배대지 이름:", self.delivery_agent_edit)
self.pre_carrier_edit = QLineEdit()
self.pre_carrier_edit.setPlaceholderText("선송장 택배사 입력")
self.pre_carrier_edit.setToolTip("주문 발송 시 사용한 택배사를 입력하세요")
form_layout.addRow("선송장 택배사:", self.pre_carrier_edit)
# 8. 국내 택배사 선택 ComboBox: DB에서 domestic_couriers를 가져옴
# (기존 '선송장 택배사' 항목을 국내 택배사로 변경)
self.domestic_courier_combo = QComboBox()
self.domestic_courier_combo.setToolTip("주문 발송 시 사용한 국내 택배사를 선택하세요")
domestic_couriers = self.db_manager.get_settings_by_category("domestic_couriers")
self.domestic_courier_combo.addItems(domestic_couriers)
form_layout.addRow("국내 택배사:", self.domestic_courier_combo)
# 9. 국내 트래킹번호
self.tracking_edit = QLineEdit()
self.tracking_edit.setPlaceholderText("배송대행지 주문 트래킹 번호 입력")
self.tracking_edit.setToolTip("배송대행지에서 발행한 국내 트래킹 번호")
form_layout.addRow("국내 트래킹번호:", self.tracking_edit)
self.freight_carrier_edit = QLineEdit()
self.freight_carrier_edit.setPlaceholderText("화물택배사 입력")
self.freight_carrier_edit.setToolTip("물품이 무거울 경우 사용한 화물택배사를 입력")
form_layout.addRow("화물택배사:", self.freight_carrier_edit)
# 10. 화물택배사 선택 ComboBox: DB에서 cargo_couriers를 가져옴
self.freight_carrier_combo = QComboBox()
self.freight_carrier_combo.setToolTip("물품이 무거울 경우 사용한 화물택배사를 선택하세요")
cargo_couriers = self.db_manager.get_settings_by_category("cargo_couriers")
self.freight_carrier_combo.addItems(cargo_couriers)
form_layout.addRow("화물택배사:", self.freight_carrier_combo)
# 11. 화물 트래킹번호
self.freight_tracking_edit = QLineEdit()
self.freight_tracking_edit.setPlaceholderText("화물택배 트래킹 번호 입력")
self.freight_tracking_edit.setToolTip("화물택배 전환 시 발행된 트래킹 번호")
form_layout.addRow("화물 트래킹번호:", self.freight_tracking_edit)
# 12. CS 메모1
self.cs_memo1_edit = QLineEdit()
self.cs_memo1_edit.setPlaceholderText("고객 서비스 메모1 입력")
self.cs_memo1_edit.setToolTip("주문 관련 CS 메모를 입력하세요")
form_layout.addRow("CS 메모1:", self.cs_memo1_edit)
# 13. CS 메모2
self.cs_memo2_edit = QLineEdit()
self.cs_memo2_edit.setPlaceholderText("고객 서비스 메모2 입력")
self.cs_memo2_edit.setToolTip("추가 CS 메모를 입력하세요")
form_layout.addRow("CS 메모2:", self.cs_memo2_edit)
self.order_step_combo = QComboBox()
self.order_step_combo.setToolTip("현재 주문의 진행단계")
# order_steps = self.db_manager.get_settings_by_category("cargo_couriers")
order_steps = ["1단계-통관부호 요청", "2단계-통관부호 재요청", "3단계-주문접수", "4단계-배대지도착", "5단계-통관시작", "6단계-국내배송 시작", "7단계-화물택배전환"]
self.order_step_combo.addItems(order_steps)
form_layout.addRow("진행단계:", self.order_step_combo)
layout.addLayout(form_layout)
# 버튼 영역
button_layout = QHBoxLayout()
self.complete_button = QPushButton("입력완료")
self.complete_button.setToolTip("주문 정보를 입력하고 저장합니다.")
self.complete_send_button = QPushButton("입력완료 및 1단계 문자발송")
self.complete_send_button.setToolTip("주문 정보를 입력 후 1단계 SMS를 전송합니다.")
self.complete_send_button.setToolTip("주문 정보를 저장 후 1단계 SMS를 전송합니다.")
self.temp_save_button = QPushButton("임시저장")
self.temp_save_button.setToolTip("주문 정보를 임시 저장합니다.")
self.cancel_button = QPushButton("취소")
self.cancel_button.setToolTip("입력을 취소합니다.")
self.complete_button.clicked.connect(self.on_complete)
self.complete_send_button.clicked.connect(self.on_complete_and_send)
self.temp_save_button.clicked.connect(self.on_temp_save)
self.cancel_button.clicked.connect(self.on_cancel)
button_layout.addWidget(self.complete_button)
button_layout.addWidget(self.complete_send_button)
button_layout.addWidget(self.temp_save_button)
button_layout.addWidget(self.cancel_button)
layout.addLayout(button_layout)
def apply_styles(self):
style = """
QDialog {
background-color: #ffffff;
}
QLineEdit, QComboBox {
padding: 4px;
border: 1px solid #ccc;
border-radius: 4px;
}
QPushButton {
background-color: #1976D2;
color: white;
border-radius: 4px;
padding: 6px 12px;
}
QPushButton:hover {
background-color: #1565C0;
}
QDialog { background-color: #ffffff; }
QLineEdit, QComboBox { padding: 4px; border: 1px solid #ccc; border-radius: 4px; }
QPushButton { background-color: #1976D2; color: white; border-radius: 4px; padding: 6px 12px; }
QPushButton:hover { background-color: #1565C0; }
"""
self.setStyleSheet(style)
def format_phone_number(self, text):
# 간단한 규칙 적용: 010으로 시작하면 010-0000-0000, 05로 시작하면 0504-0000-0000 형태로 포맷팅
digits = re.sub(r'\D', '', text)
if digits.startswith("010") and len(digits) >= 10:
return f"{digits[:3]}-{digits[3:7]}-{digits[7:11]}"
elif digits.startswith("05") and len(digits) >= 11:
return f"{digits[:4]}-{digits[4:8]}-{digits[8:12]}"
else:
return text
def format_phone_number_slot(self, text):
formatted = self.format_phone_number(text)
if formatted != text:
self.customer_phone_edit.blockSignals(True)
self.customer_phone_edit.setText(formatted)
self.customer_phone_edit.blockSignals(False)
def validate_phone_number_input(self):
"""
전화번호 입력란의 값을 검증합니다.
- 010으로 시작하면 전체 숫자 10자리여야 합니다. (, "010" + 7자리)
- 05 시작하면 전체 숫자 10자리여야 합니다. (, "05" + 8자리)
"""
text = self.customer_phone_edit.text().strip()
digits = re.sub(r'\D', '', text)
if digits.startswith("010"):
expected_length = 11 # 010(3자리) + 7자리 = 10자리
if len(digits) != expected_length:
QMessageBox.warning(self, "입력 오류", "010으로 시작하는 전화번호는 7자리 숫자(총 10자리)를 입력해야 합니다.")
return False
elif digits.startswith("05"):
expected_length = 12 # 05(2자리) + 8자리 = 10자리
if len(digits) != expected_length:
QMessageBox.warning(self, "입력 오류", "05로 시작하는 전화번호는 8자리 숫자(총 10자리)를 입력해야 합니다.")
return False
else:
QMessageBox.warning(self, "입력 오류", "전화번호는 010 또는 05로 시작해야 합니다.")
return False
return True
def get_order_data(self):
# 주문 진행 단계는 order_step_combo에서 선택한 문자열에서 앞쪽 숫자를 추출합니다.
order_step_text = self.order_step_combo.currentText()
try:
# 예: "3단계-주문접수" → 3
order_step = int(order_step_text.split("단계")[0])
except Exception:
order_step = 1 # 변환 실패 시 기본값 1 사용
data = {
"shop_name": self.shop_combo.currentText(),
"customer_name": self.customer_name_edit.text().strip(),
"product_name": self.product_name_edit.text().strip(),
"order_market": self.order_market_combo.currentText(),
"customer_phone": self.customer_phone_edit.text().strip(),
"taobao_tracking": self.taobao_tracking_edit.text().strip(),
"delivery_agent": self.delivery_agent_edit.text().strip(),
"domestic_courier": self.domestic_courier_combo.currentText(), # 국내 택배사
"domestic_tracking": self.tracking_edit.text().strip(),
"freight_carrier": self.freight_carrier_combo.currentText(),
"freight_tracking": self.freight_tracking_edit.text().strip(),
"cs_memo1": self.cs_memo1_edit.text().strip(),
"cs_memo2": self.cs_memo2_edit.text().strip(),
"order_step": order_step
}
return data
def on_complete(self):
if not self.validate_phone_number_input():
return
data = self.get_order_data()
if not data["customer_phone"]:
QMessageBox.warning(self, "입력 오류", "고객 전화번호를 입력하세요.")
return
# DB 저장: 주문서를 DatabaseManager.insert_order()로 저장
order = self.db_manager.insert_order(**data)
self.order_data = data
self.logger.log(f"주문 정보 저장됨: {data}", level=1)
QMessageBox.information(self, "저장 확인", f"주문 정보(ID {order.id})가 저장되었습니다.")
self.accept()
def on_complete_and_send(self):
if not self.validate_phone_number_input():
return
# 입력완료와 SMS 전송을 동시에 진행: DB에 저장한 후 SMS 모듈 호출
self.on_complete() # 저장 처리
async def send_sms():
result = await self.sms_messenger.send_sms(
recipient=self.customer_phone_edit.text().strip(),
message="주문 접수 메시지 예시" # 실제 템플릿과 주문 데이터를 결합하여 사용
)
self.logger.log(f"SMS 전송 결과: {result}", level=1)
asyncio.create_task(send_sms())
self.accept()
def on_temp_save(self):
data = self.get_order_data()
self.order_data = data
self.logger.log(f"주문 정보 임시 저장됨: {data}", level=1)
QMessageBox.information(self, "임시 저장", "주문 정보가 임시 저장되었습니다.")
self.accept()
def on_cancel(self):
reply = QMessageBox.question(self, "취소 확인",
"입력 중인 주문 정보를 저장하지 않고 취소하시겠습니까?",
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
self.reject()
def showEvent(self, event):
# 전화번호 입력 시 자동 포맷팅을 위한 슬롯 연결
self.customer_phone_edit.textChanged.connect(self.format_phone_number_slot)
super().showEvent(event)

View File

@ -1,78 +1,283 @@
# gui/settings_dialog.py
from PySide6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QListWidget, QLabel, QMessageBox
from PySide6.QtCore import QSettings
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton,
QListWidget, QLabel, QMessageBox, QTabWidget, QWidget, QComboBox
)
from PySide6.QtCore import Qt
class SettingsDialog(QDialog):
def __init__(self, parent=None):
class ShopTagDialog(QDialog):
"""
이름 추가 , 사용 가능한 태그(숫자) 하나를 선택하는 다이얼로그.
available_tags에는 사용되지 않은 태그 숫자 리스트가 전달됩니다.
"""
def __init__(self, available_tags, parent=None):
super().__init__(parent)
self.setWindowTitle("사용자 설정")
self.resize(400, 300)
self.settings = QSettings("MyCompany", "MySMSApp")
self.setup_ui()
self.load_shop_names()
def setup_ui(self):
self.setWindowTitle("샵 태그 선택")
self.selected_tag = None
layout = QVBoxLayout(self)
label = QLabel("이 샵의 태그 번호를 선택하세요:")
layout.addWidget(label)
self.shop_list = QListWidget()
self.shop_list.setToolTip("저장된 샵 이름 목록을 확인하세요.")
layout.addWidget(QLabel("저장된 샵 이름:"))
layout.addWidget(self.shop_list)
self.combo = QComboBox()
# available_tags가 예: [3, 4, 5, 6, 7]라면 콤보박스에는 "3사", "4사", ... 로 표시
for tag in available_tags:
self.combo.addItem(f"{tag}", tag)
layout.addWidget(self.combo)
input_layout = QHBoxLayout()
self.shop_input = QLineEdit()
self.shop_input.setPlaceholderText("새 샵 이름 입력")
self.shop_input.setToolTip("새로운 샵 이름을 입력하세요.")
input_layout.addWidget(self.shop_input)
self.add_button = QPushButton("추가")
self.add_button.setToolTip("새 샵 이름을 추가합니다.")
self.add_button.clicked.connect(self.add_shop_name)
input_layout.addWidget(self.add_button)
layout.addLayout(input_layout)
# 엔터키를 누르면 다이얼로그를 수락하도록 연결합니다.
self.combo.lineEdit().returnPressed.connect(self.accept)
button_layout = QHBoxLayout()
self.delete_button = QPushButton("삭제")
self.delete_button.setToolTip("선택한 샵 이름을 삭제합니다.")
self.delete_button.clicked.connect(self.delete_shop_name)
self.close_button = QPushButton("닫기")
self.close_button.clicked.connect(self.accept)
button_layout.addWidget(self.delete_button)
button_layout.addWidget(self.close_button)
ok_button = QPushButton("확인")
cancel_button = QPushButton("취소")
ok_button.clicked.connect(self.accept)
cancel_button.clicked.connect(self.reject)
button_layout.addWidget(ok_button)
button_layout.addWidget(cancel_button)
layout.addLayout(button_layout)
def load_shop_names(self):
shop_names = self.settings.value("shop_names", [])
if not isinstance(shop_names, list):
shop_names = [shop_names] if shop_names else []
self.shop_list.clear()
for name in shop_names:
self.shop_list.addItem(name)
def exec_(self):
result = super().exec_()
if result == QDialog.Accepted:
self.selected_tag = self.combo.currentData()
return result
def add_shop_name(self):
name = self.shop_input.text().strip()
if not name:
class SettingsDialog(QDialog):
def __init__(self, logger, db_manager, parent=None):
"""
:param logger: 로깅 객체
:param db_manager: DatabaseManager 인스턴스 (database_module.py의 DatabaseManager)
"""
super().__init__(parent)
self.setWindowTitle("사용자 설정")
self.resize(500, 500)
self.logger = logger
self.db_manager = db_manager
# 관리할 항목: key는 DB상의 카테고리명, value는 표시될 이름
self.categories = {
"shop_names": "샵 이름",
"order_markets": "주문마켓",
"domestic_couriers": "국내 택배사",
"cargo_couriers": "화물 택배사"
}
# 탭 순서를 보존하기 위해 key 목록 저장
self.tab_keys = list(self.categories.keys())
self.widgets = {}
self.setup_ui()
self.load_all_settings()
self.setStyleSheet(self.load_style_sheet())
# 탭 변경 시 해당 탭의 입력창에 포커스하도록 연결
self.tab_widget.currentChanged.connect(self.on_tab_changed)
def setup_ui(self):
main_layout = QVBoxLayout(self)
self.tab_widget = QTabWidget()
for key, display_name in self.categories.items():
tab = QWidget()
tab_layout = QVBoxLayout(tab)
# 저장된 항목을 표시하는 리스트 위젯
list_widget = QListWidget()
list_widget.setToolTip(f"저장된 {display_name} 목록을 확인하세요.")
tab_layout.addWidget(list_widget)
# 입력창과 추가 버튼을 담는 수평 레이아웃
input_layout = QHBoxLayout()
line_edit = QLineEdit()
line_edit.setPlaceholderText(f"{display_name} 입력")
line_edit.returnPressed.connect(lambda cat=key: self.add_item(cat))
line_edit.setToolTip(f"새로운 {display_name}을(를) 입력하세요.")
input_layout.addWidget(line_edit)
add_button = QPushButton("추가")
add_button.setToolTip(f"{display_name}을(를) 추가합니다.")
add_button.clicked.connect(lambda _, cat=key: self.add_item(cat))
input_layout.addWidget(add_button)
tab_layout.addLayout(input_layout)
# 엔터키로도 추가 (샵 이름의 경우)
if key == "shop_names":
line_edit.returnPressed.connect(lambda cat=key: self.add_item(cat))
# 삭제 버튼
delete_button = QPushButton("삭제")
delete_button.setToolTip(f"선택한 {display_name}을(를) 삭제합니다.")
delete_button.clicked.connect(lambda _, cat=key: self.delete_item(cat))
tab_layout.addWidget(delete_button, alignment=Qt.AlignRight)
self.tab_widget.addTab(tab, display_name)
index = self.tab_widget.indexOf(tab)
self.tab_widget.setTabToolTip(index, f"{display_name} 설정을 관리합니다.")
self.widgets[key] = {
"list": list_widget,
"input": line_edit,
"add": add_button,
"delete": delete_button
}
main_layout.addWidget(self.tab_widget)
# 닫기 버튼
button_layout = QHBoxLayout()
close_button = QPushButton("닫기")
close_button.setToolTip("설정 창을 닫습니다.")
close_button.clicked.connect(self.accept)
button_layout.addWidget(close_button)
main_layout.addLayout(button_layout)
def on_tab_changed(self, index):
key = self.tab_keys[index]
if key in self.widgets and "input" in self.widgets[key]:
self.widgets[key]["input"].setFocus()
def load_all_settings(self):
"""
카테고리별로 DB에 저장된 값을 읽어와 리스트 위젯에 표시합니다.
이름의 경우, DB에 별도 저장된 tag와 value를 "[tag사업자] shop_name" 형식으로 표시합니다.
"""
for category in self.categories.keys():
self.widgets[category]["list"].clear()
if category == "shop_names":
records = self.db_manager.get_settings_by_category(category)
# records는 각 레코드가 tag와 value 속성을 가지고 있다고 가정합니다.
for rec in records:
display_text = f"[{rec.tag}사업자] {rec.value}"
self.widgets[category]["list"].addItem(display_text)
else:
values = self.db_manager.get_settings_by_category(category)
for item in values:
self.widgets[category]["list"].addItem(item)
def get_available_shop_tags(self, used):
"""
이미 사용 중인 태그 집합을 받아, 사용되지 않은 숫자 _최소 5개_를 반환합니다.
예를 들어 used가 {1, 2}라면 [3, 4, 5, 6, 7] 반환합니다.
:param used: set(int) 이미 사용된 태그 번호
:return: list(int) 사용 가능한 태그 번호 (최소 5)
"""
available = []
num = 1
while len(available) < 5:
if num not in used:
available.append(num)
num += 1
return available
def add_item(self, category):
"""
지정된 카테고리에 항목을 추가합니다.
이름의 경우 태그 선택 다이얼로그를 통해 tag를 선택하고, DB에는 별도의 tag 필드와 함께 저장합니다.
"""
display_name = self.categories.get(category, category)
text = self.widgets[category]["input"].text().strip()
if not text:
return
current_names = self.settings.value("shop_names", [])
if not isinstance(current_names, list):
current_names = [current_names] if current_names else []
if name in current_names:
if category == "shop_names":
# 이미 등록된 샵 이름(및 태그)을 조회 (DB에서 tag와 value로 관리)
existing = self.db_manager.get_settings_by_category("shop_names")
used_tags = set()
for rec in existing:
if rec.tag is not None:
used_tags.add(rec.tag)
if rec.value.strip().lower() == text.lower():
QMessageBox.warning(self, "중복", "이미 존재하는 샵 이름입니다.")
return
current_names.append(name)
self.settings.setValue("shop_names", current_names)
self.load_shop_names()
self.shop_input.clear()
available = self.get_available_shop_tags(used_tags)
tag_dialog = ShopTagDialog(available, self)
if tag_dialog.exec_() == QDialog.Accepted:
chosen_tag = tag_dialog.selected_tag
else:
return # 취소 시 중단
# DB에 추가: add_shop_setting(category, value, tag)
success = self.db_manager.add_shop_setting("shop_names", text, chosen_tag)
if not success:
QMessageBox.warning(self, "중복", f"이미 존재하는 {display_name}입니다.")
return
else:
final_value = text
success = self.db_manager.add_setting(category, final_value)
if not success:
QMessageBox.warning(self, "중복", f"이미 존재하는 {display_name}입니다.")
return
def delete_shop_name(self):
selected_items = self.shop_list.selectedItems()
self.load_all_settings()
self.widgets[category]["input"].clear()
def delete_item(self, category):
"""
지정된 카테고리에서 선택한 항목을 삭제합니다.
이름의 경우 "[tag사] shop_name"에서 tag와 shop_name을 추출하여 삭제합니다.
"""
selected_items = self.widgets[category]["list"].selectedItems()
if not selected_items:
return
current_names = self.settings.value("shop_names", [])
if not isinstance(current_names, list):
current_names = [current_names] if current_names else []
for item in selected_items:
name = item.text()
if name in current_names:
current_names.remove(name)
self.settings.setValue("shop_names", current_names)
self.load_shop_names()
if category == "shop_names":
text = item.text()
if text.startswith("[") and "]" in text:
try:
tag_str = text.split("]")[0][1:] # 예: "[3사" -> "3사"
# tag_str이 "3사"이면 tag를 정수로 변환
tag_num = int(tag_str.replace("", ""))
shop_name = text.split("]", 1)[1].strip()
self.db_manager.delete_shop_setting(category, shop_name, tag_num)
except Exception:
pass
else:
self.db_manager.delete_setting(category, item.text())
self.load_all_settings()
def load_style_sheet(self):
"""
현대적이고 모던한 느낌의 스타일 시트를 반환합니다.
"""
return """
QDialog {
background-color: #f7f9fc;
font-family: 'Segoe UI', sans-serif;
font-size: 10pt;
}
QTabWidget::pane {
border: 1px solid #dcdcdc;
background: #ffffff;
}
QTabBar::tab {
background: #e8eff7;
border: 1px solid #dcdcdc;
padding: 8px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
margin-right: 2px;
min-width: 100px;
}
QTabBar::tab:selected {
background: #ffffff;
border-bottom: 2px solid #3498db;
font-weight: bold;
}
QLineEdit {
border: 1px solid #dcdcdc;
padding: 6px;
border-radius: 4px;
background-color: #ffffff;
}
QPushButton {
background-color: #3498db;
color: #ffffff;
border: none;
padding: 6px 12px;
border-radius: 4px;
}
QPushButton:hover {
background-color: #2980b9;
}
QListWidget {
background: #ffffff;
border: 1px solid #dcdcdc;
padding: 4px;
border-radius: 4px;
}
"""

View File

@ -7,13 +7,13 @@ from PySide6.QtGui import QMouseEvent, QKeyEvent
# 주문 단계 목록 (하드코딩; 추후 config 등으로 분리 가능)
ORDER_STEPS = {
1: "주문 접수",
2: "배송대행지 도착",
3: "국내 도착 및 통관 시작",
4: "통관 완료 및 국내 배송 시작",
5: "화물 전환",
6: "통관번호 요청",
7: "통관번호 재요청",
1: "통관번호 요청",
2: "통관번호 재요청",
3: "주문 접수",
4: "배송대행지 도착",
5: "국내 도착 및 통관 시작",
6: "통관 완료 및 국내 배송 시작",
7: "화물 전환",
}
@ -48,7 +48,7 @@ class TemplateCard(QFrame):
self.layout.setSpacing(5)
# 상단: 번호 필드 또는 플러스 표시
if not self.is_plus:
self.top_label = QLabel(f"#{self.template_id} - {self.name}")
self.top_label = QLabel(f"# {self.name}")
else:
self.top_label = QLabel("+ 추가")
self.top_label.setFixedHeight(30)

View File

@ -1,20 +1,18 @@
# gui/template_management_dialog.py
from PySide6.QtWidgets import (QDialog, QHBoxLayout, QVBoxLayout, QListWidget, QScrollArea, QWidget, QMessageBox)
from PySide6.QtCore import Qt, QSettings, Slot
from PySide6.QtCore import Qt, Slot
from gui.template_card import TemplateCard
from src.database_module import DatabaseManager
from gui.template_card import ORDER_STEPS # 또는 별도로 ORDER_STEPS 상수를 정의
class TemplateManagementDialog(QDialog):
def __init__(self, logger, parent=None):
def __init__(self, logger, db_manager, parent=None):
super().__init__(parent)
self.logger = logger
self.setWindowTitle("템플릿 관리")
self.resize(800, 600)
self.settings = QSettings("MyCompany", "MySMSApp")
self.current_stage = 1
self.selected_template_id = None
self.db_manager = DatabaseManager()
self.db_manager = db_manager
self.plus_card = None # 플러스 카드는 한 번만 생성하도록 함
self.setup_ui()
self.apply_styles()
@ -80,8 +78,10 @@ class TemplateManagementDialog(QDialog):
@Slot(int)
def on_step_changed(self, row: int):
"""주문 단계가 변경될 때 이전에 선택한 템플릿을 자동으로 선택"""
stage = row + 1
# 현재 수정 모드에 있는 카드가 있으면 수정 취소를 시도
# 현재 수정 모드에 있는 카드가 있으면 취소
if TemplateCard.currently_editing is not None:
editing_card = TemplateCard.currently_editing
try:
@ -90,13 +90,19 @@ class TemplateManagementDialog(QDialog):
self.logger.log(f"수정 모드 취소 중 오류 발생: {e}", level=1)
finally:
TemplateCard.currently_editing = None
self.logger.log(f"주문 단계 변경: {stage} - {ORDER_STEPS.get(stage)}", level=1)
# UI에 해당 단계의 템플릿 목록을 불러오기
self.load_templates_for_stage(stage)
last_template = self.settings.value(f"selected_template_stage_{stage}", None)
# 해당 단계에서 마지막으로 선택한 템플릿 불러오기
last_template = self.db_manager.get_template_settings(f"selected_template_stage_{stage}")
if last_template is not None:
try:
last_template = int(last_template)
self.select_template(last_template)
self.select_template(last_template) # 선택된 상태로 유지
except ValueError:
pass
@ -143,7 +149,7 @@ class TemplateManagementDialog(QDialog):
except RuntimeError:
pass
self.selected_template_id = template_id
self.settings.setValue(f"selected_template_stage_{self.current_stage}", template_id)
self.db_manager.set_template_settings(f"selected_template_stage_{self.current_stage}", str(template_id))
self.logger.log(f"템플릿 선택됨: ID {template_id}", level=1)
@Slot()
@ -172,8 +178,17 @@ class TemplateManagementDialog(QDialog):
self.on_template_selected(template_id)
def restore_settings(self):
last_step = self.settings.value("last_selected_step", 1)
self.step_list.setCurrentRow(int(last_step) - 1)
"""각 단계별 마지막으로 선택한 템플릿을 불러와 복원"""
for stage in ORDER_STEPS.keys(): # ORDER_STEPS에 있는 단계별로 조회
last_template = self.db_manager.get_template_settings(f"selected_template_stage_{stage}")
if last_template is not None:
try:
last_template = int(last_template)
if stage == self.current_stage:
self.select_template(last_template)
except ValueError:
pass
self.logger.log("템플릿 설정 복원 완료", level=1)
def closeEvent(self, event):

19
main.py
View File

@ -1,20 +1,25 @@
#!/usr/bin/env python3
# main.py
import sys
import asyncio
from PySide6.QtWidgets import QApplication
from qasync import QEventLoop # qasync 라이브러리 설치: pip install qasync
from gui.main_window import MainWindow
from src.logger_module import Logger
def main():
app = QApplication(sys.argv)
# 로거 초기화: GUI 로그 콜백은 메인창의 로그 창에 append하도록 연결 (MainWindow에서 설정)
logger = Logger(log_file="app.log")
# QApplication 생성
app = QApplication(sys.argv)
# qasync 이벤트 루프 생성 및 QApplication과 통합
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
# 메인 창 생성 (여기서 logger를 전달하여 GUI 로그 출력)
window = MainWindow(logger)
window.show()
sys.exit(app.exec())
with loop:
loop.run_forever()
if __name__ == '__main__':
if __name__ == "__main__":
main()

BIN
orders.db

Binary file not shown.

BIN
qr_code.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -1,6 +1,6 @@
{
"epochs": [ {
"calculation_time": "13378378446134919",
"calculation_time": "13383679415520368",
"config_version": 0,
"model_version": "0",
"padded_top_topics_start_index": 0,
@ -8,5 +8,5 @@
"top_topics_and_observing_domains": [ ]
} ],
"hex_encoded_hmac_key": "D9AF344532865CF91C853ED77B8B69345D464E12284F8E417E2AA35C759352CF",
"next_scheduled_calculation_time": "13378983246134979"
"next_scheduled_calculation_time": "13384284215520418"
}

Binary file not shown.

View File

@ -1,3 +1,3 @@
2024/12/11-17:21:28.575 61b0 Reusing MANIFEST D:\py\AUtoTao2\src\browsers\user_data\Default\Extension State/MANIFEST-000001
2024/12/11-17:21:28.575 61b0 Recovering log #3
2024/12/11-17:21:28.575 61b0 Reusing old log D:\py\AUtoTao2\src\browsers\user_data\Default\Extension State/000003.log
2025/02/11-01:49:08.970 269c Reusing MANIFEST D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\Extension State/MANIFEST-000001
2025/02/11-01:49:08.970 269c Recovering log #3
2025/02/11-01:49:08.970 269c Reusing old log D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\Extension State/000003.log

View File

@ -1,3 +1,3 @@
2024/12/11-17:16:24.345 4878 Reusing MANIFEST D:\py\AUtoTao2\src\browsers\user_data\Default\Extension State/MANIFEST-000001
2024/12/11-17:16:24.346 4878 Recovering log #3
2024/12/11-17:16:24.346 4878 Reusing old log D:\py\AUtoTao2\src\browsers\user_data\Default\Extension State/000003.log
2025/02/11-01:47:39.208 45ac Reusing MANIFEST D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\Extension State/MANIFEST-000001
2025/02/11-01:47:39.209 45ac Recovering log #3
2025/02/11-01:47:39.209 45ac Reusing old log D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\Extension State/000003.log

View File

@ -1,3 +1,3 @@
2024/12/11-17:21:30.685 6604 Reusing MANIFEST D:\py\AUtoTao2\src\browsers\user_data\Default\GCM Store\Encryption/MANIFEST-000001
2024/12/11-17:21:30.685 6604 Recovering log #3
2024/12/11-17:21:30.685 6604 Reusing old log D:\py\AUtoTao2\src\browsers\user_data\Default\GCM Store\Encryption/000003.log
2025/02/11-01:49:11.619 7110 Reusing MANIFEST D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\GCM Store\Encryption/MANIFEST-000001
2025/02/11-01:49:11.619 7110 Recovering log #3
2025/02/11-01:49:11.619 7110 Reusing old log D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\GCM Store\Encryption/000003.log

View File

@ -1,3 +1,3 @@
2024/12/11-17:16:26.426 5efc Reusing MANIFEST D:\py\AUtoTao2\src\browsers\user_data\Default\GCM Store\Encryption/MANIFEST-000001
2024/12/11-17:16:26.426 5efc Recovering log #3
2024/12/11-17:16:26.426 5efc Reusing old log D:\py\AUtoTao2\src\browsers\user_data\Default\GCM Store\Encryption/000003.log
2025/02/11-01:47:41.821 1dc0 Reusing MANIFEST D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\GCM Store\Encryption/MANIFEST-000001
2025/02/11-01:47:41.821 1dc0 Recovering log #3
2025/02/11-01:47:41.821 1dc0 Reusing old log D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\GCM Store\Encryption/000003.log

View File

@ -0,0 +1,3 @@
2025/02/11-01:49:09.123 269c Reusing MANIFEST D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\IndexedDB\https_messages.google.com_0.indexeddb.leveldb/MANIFEST-000001
2025/02/11-01:49:09.123 269c Recovering log #3
2025/02/11-01:49:09.124 269c Reusing old log D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\IndexedDB\https_messages.google.com_0.indexeddb.leveldb/000003.log

View File

@ -0,0 +1,3 @@
2025/02/11-01:47:39.335 45ac Reusing MANIFEST D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\IndexedDB\https_messages.google.com_0.indexeddb.leveldb/MANIFEST-000001
2025/02/11-01:47:39.335 45ac Recovering log #3
2025/02/11-01:47:39.335 45ac Reusing old log D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\IndexedDB\https_messages.google.com_0.indexeddb.leveldb/000003.log

View File

@ -1,3 +1,3 @@
2025/01/09-12:06:40.151 5a70 Reusing MANIFEST D:\py\AUtoTao2\src\browsers\user_data\Default\Local Storage\leveldb/MANIFEST-000001
2025/01/09-12:06:40.152 5a70 Recovering log #3
2025/01/09-12:06:40.152 5a70 Reusing old log D:\py\AUtoTao2\src\browsers\user_data\Default\Local Storage\leveldb/000003.log
2025/02/11-01:49:08.907 4174 Reusing MANIFEST D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\Local Storage\leveldb/MANIFEST-000001
2025/02/11-01:49:08.911 4174 Recovering log #3
2025/02/11-01:49:08.913 4174 Reusing old log D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\Local Storage\leveldb/000003.log

View File

@ -0,0 +1,3 @@
2025/02/11-01:47:39.138 3cb0 Reusing MANIFEST D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\Local Storage\leveldb/MANIFEST-000001
2025/02/11-01:47:39.141 3cb0 Recovering log #3
2025/02/11-01:47:39.142 3cb0 Reusing old log D:\py\Mycar_SMS_Sender2\src\browsers\user_data\Default\Local Storage\leveldb/000003.log

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"sts":[{"expiry":1765440867.111811,"host":"ObTElBeSjmuk/whOMl2S5VhuM0kKnK0J1EktqFzQAJ4=","mode":"force-https","sts_include_subdomains":false,"sts_observed":1733904867.111812},{"expiry":1765440867.130256,"host":"aso6vR2q0VkqJKMxOB1wM/2xD6W32U9MRzbLsX5k/BA=","mode":"force-https","sts_include_subdomains":false,"sts_observed":1733904867.130258},{"expiry":1765440916.810704,"host":"nAuqgR4iEWti7SOdT3UHPl6rmZU/DeaIm38P2O2OkgA=","mode":"force-https","sts_include_subdomains":false,"sts_observed":1733904916.810707},{"expiry":1765441288.822726,"host":"8/RrMmQlCD2Gsp14wUCE1P8r7B2C5+yE0+g79IPyRsc=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1733905288.822728},{"expiry":1765440867.106641,"host":"+loO+DGmT6DTr59JZFAnGSlBAwPkO5M/R9ec1Sw/9KA=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1733904867.106643}],"version":2}
{"sts":[{"expiry":1765440867.111811,"host":"ObTElBeSjmuk/whOMl2S5VhuM0kKnK0J1EktqFzQAJ4=","mode":"force-https","sts_include_subdomains":false,"sts_observed":1733904867.111812},{"expiry":1765440867.130256,"host":"aso6vR2q0VkqJKMxOB1wM/2xD6W32U9MRzbLsX5k/BA=","mode":"force-https","sts_include_subdomains":false,"sts_observed":1733904867.130258},{"expiry":1770741945.03296,"host":"nAuqgR4iEWti7SOdT3UHPl6rmZU/DeaIm38P2O2OkgA=","mode":"force-https","sts_include_subdomains":false,"sts_observed":1739205945.032963},{"expiry":1770742149.168543,"host":"8/RrMmQlCD2Gsp14wUCE1P8r7B2C5+yE0+g79IPyRsc=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1739206149.168546},{"expiry":1765440867.106641,"host":"+loO+DGmT6DTr59JZFAnGSlBAwPkO5M/R9ec1Sw/9KA=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1733904867.106643}],"version":2}

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More