# adviceprinter2.py import sys import os import platform import subprocess import shutil from pathlib import Path import socket from fastapi import FastAPI,Body import uvicorn from smartcard.System import readers # --------------------------- # Redirect stdout/stderr (for PyInstaller windowed) # --------------------------- if sys.stdout is None: sys.stdout = open(os.devnull, 'w') if sys.stderr is None: sys.stderr = open(os.devnull, 'w') # --------------------------- # Utility for macOS login items # --------------------------- def add_to_login_items(app_path): """Add macOS app to Login Items using AppleScript.""" absolute_app_path = os.path.abspath(app_path) if not os.path.exists(absolute_app_path): print(f"Error: Application not found at '{absolute_app_path}'") return script = f''' tell application "System Events" make new login item at end with properties {{path:"{absolute_app_path}", hidden:false}} end tell ''' try: subprocess.run( ['osascript', '-e', script], check=True, capture_output=True, text=True ) print("✅ Successfully added to Login Items.") except subprocess.CalledProcessError as e: print(f"Error adding to Login Items: {e.stderr}") except FileNotFoundError: print("Error: 'osascript' not found. Must be run on macOS.") # --------------------------- # Thai Smart Card Utilities # --------------------------- def thai2unicode(data): resp = '' if isinstance(data, list): resp = bytes(data).decode('tis-620').replace('#', ' ') return resp.strip() else: return data def getData(connection, cmd, req=[0x00, 0xc0, 0x00, 0x00]): data, sw1, sw2 = connection.transmit(cmd) data, sw1, sw2 = connection.transmit(req + [cmd[-1]]) return [data, sw1, sw2] # Command sets SELECT = [0x00, 0xA4, 0x04, 0x00, 0x08] THAI_CARD = [0xA0, 0x00, 0x00, 0x00, 0x54, 0x48, 0x00, 0x01] CMD_CID = [0x80, 0xb0, 0x00, 0x04, 0x02, 0x00, 0x0d] CMD_THFULLNAME = [0x80, 0xb0, 0x00, 0x11, 0x02, 0x00, 0x64] CMD_ENFULLNAME = [0x80, 0xb0, 0x00, 0x75, 0x02, 0x00, 0x64] CMD_BIRTH = [0x80, 0xb0, 0x00, 0xD9, 0x02, 0x00, 0x08] CMD_GENDER = [0x80, 0xb0, 0x00, 0xE1, 0x02, 0x00, 0x01] CMD_ISSUER = [0x80, 0xb0, 0x00, 0xF6, 0x02, 0x00, 0x64] CMD_ISSUE = [0x80, 0xb0, 0x01, 0x67, 0x02, 0x00, 0x08] CMD_EXPIRE = [0x80, 0xb0, 0x01, 0x6F, 0x02, 0x00, 0x08] CMD_ADDRESS = [0x80, 0xb0, 0x15, 0x79, 0x02, 0x00, 0x64] # --------------------------- # Read Thai ID card data # --------------------------- def read_card_value(): try: readerList = readers() reader = readerList[0] connection = reader.createConnection() connection.connect() except Exception as e: print(e) return False atr = connection.getATR() req = [0x00, 0xc0, 0x00, 0x01] if (atr[0] == 0x3B & atr[1] == 0x67) else [0x00, 0xc0, 0x00, 0x00] connection.transmit(SELECT + THAI_CARD) def read_field(cmd): try: return thai2unicode(getData(connection, cmd, req)[0]) except: return '' cid = read_field(CMD_CID) th_fullname = read_field(CMD_THFULLNAME) en_fullname = read_field(CMD_ENFULLNAME) date_birth = read_field(CMD_BIRTH) gender = read_field(CMD_GENDER) card_issuer = read_field(CMD_ISSUER) issue_date = read_field(CMD_ISSUE) expire_date = read_field(CMD_EXPIRE) address = read_field(CMD_ADDRESS) address_list = address.split(' ') if not address_list or address_list[0] == '': return False val1 = val2 = val3 = val4 = '' for e_addr in address_list: if 'ตำบล' in e_addr: val1 = e_addr.replace('ตำบล', '') elif 'อำเภอ' in e_addr: val2 = e_addr.replace('อำเภอ', '') elif 'จังหวัด' in e_addr: val3 = e_addr.replace('จังหวัด', '') val4 = ' '.join(address_list[:-3]).strip() if cid == '': return False return { 'id13': cid, 'th_fullname': th_fullname, 'en_fullname': en_fullname, 'date_birth': date_birth, 'gender': gender, 'card_issuer': card_issuer, 'issue_date': issue_date, 'expire_date': expire_date, 'address': val4, 'subdistrict': val1, 'district': val2, 'province': val3 } # --------------------------- # Setup for Windows & macOS # --------------------------- def run_initial(): system = platform.system() if system == "Windows": new_folder = Path("C:/Adviceprinter") new_folder.mkdir(parents=True, exist_ok=True) exe_file = Path.home() / "Downloads" / "Adviceprinter2.exe" start_path = Path.home() / "AppData" / "Roaming" / "Microsoft" / "Windows" / "Start Menu" / "Programs" / "Startup" if exe_file.exists(): shutil.copy(exe_file, new_folder) shutil.copy(exe_file, start_path) print(f"✅ Copied {exe_file.name} to {new_folder}") else: print(f"⚠️ {exe_file} not found in Downloads.") elif system == "Darwin": # macOS app_path = "/Applications/Adviceprinter2.app" add_to_login_items(app_path) else: print("Unsupported OS detected. Exiting.") exit() # --------------------------- # FastAPI Application # --------------------------- app = FastAPI(title="Adviceprinter2") @app.get("/") def service_active(): return {"status": "running", "message": "Adviceprinter2 is running."} @app.get("/check_card") def check_card(): try: out = read_card_value() return {"status": "success", "value": out} if out else {"status": "warning", "message": "No smart card inserted."} except Exception as e: return {"status": "error", "message": f"Unexpected error: {str(e)}"} @app.get("/get_computername") def device_hostname(): try: hostname = socket.gethostname() return {"status": "success", "computername": hostname} except Exception as e: return {"status": "error", "message": f"Unexpected error: {str(e)}"} # --------------------------- # Main Entry # --------------------------- if __name__ == "__main__": system = platform.system() if system == "Windows": config_folder = Path("C:/Adviceprinter") elif system == "Darwin": config_folder = Path.home() / "Library/Application Support/Adviceprinter" else: print("Unsupported OS.") sys.exit(0) if not config_folder.exists(): run_initial() else: print("Configuration folder already exists. Skipping setup.") print("🚀 Starting FastAPI server on port 5555...") uvicorn.run(app, port=5555)