"""Harness adapter: máy in giấy Brother — CHỈ in (tìm văn bản là tool doc_finder).""" from __future__ import annotations import os import re import shlex import sys from pathlib import Path HERE = Path(__file__).resolve().parent ROOT = HERE.parent.parent for p in (str(HERE), str(ROOT)): if p not in sys.path: sys.path.insert(0, p) from harness.tool_base import Tool, ToolResult def _on_cloud() -> bool: """True nếu đang chạy trên HF Space / server đám mây (không cùng WiFi máy in).""" return bool(os.getenv("SPACE_ID") or os.getenv("SPACE_HOST")) def _no_printer_msg(err: str | None = None) -> str: """Thông báo 'không thấy máy in' rõ ràng + đề xuất, phân biệt online vs offline.""" if _on_cloud(): return ("Không tìm thấy máy in nào — và sẽ KHÔNG thấy được, vì bạn đang dùng bản " "ONLINE (HF Space). Máy chủ chạy trong trung tâm dữ liệu, KHÔNG cùng mạng " "WiFi với máy in ở nhà/văn phòng bạn, nên không quét hay in tới nó được. " "Muốn IN THẬT: chạy bản OFFLINE (tải mã về, chạy web.ps1) trên máy tính " "CÙNG WiFi với máy in, rồi in từ đó.") return ("Không tìm thấy máy in nào trong mạng WiFi hiện tại (đã quét cổng in 631/9100 " "trên toàn dải /24). Kiểm tra giúp mình: (1) máy in đã BẬT và nối CÙNG WiFi/router " "với máy tính này chưa? (2) máy in có bật in qua mạng (IPP/AirPrint) không? " "(3) thử tắt–bật lại máy in. Nếu biết IP máy in, đặt biến môi trường " "PAPER_PRINTER_HOST=<địa chỉ IP> để ghim thẳng, khỏi cần dò." + (f"\n(chi tiết kỹ thuật: {err})" if err else "")) # strip leading print commands so "in cho tôi file X" -> "file X" -> "X" _LEAD = re.compile( r"^\s*(hãy\s+|vui lòng\s+|làm ơn\s+)?(in ra giấy|in giúp( tôi)?|in cho tôi|in dùm( tôi)?|" r"in hộ( tôi)?|in|print)\s+", re.IGNORECASE) # "chỉ dò/tìm máy in" (thiết bị) -> discovery, không in gì _FIND_PRINTER = re.compile( r"(tìm|dò|kiếm|kiểm tra|scan|quét|kết nối|connect).{0,20}(máy in|printer)", re.IGNORECASE) class PaperPrinterTool(Tool): name = "paper_printer" description = ( "IN RA GIẤY thật trên máy in Brother MFC-L3750CDW (WiFi, tự dò tìm & kết nối). " "Dùng khi người dùng muốn IN RA GIẤY: in một ảnh, in PDF, in file có sẵn trên máy " "(tự tìm theo tên trên Desktop), hoặc tả một hình để sinh ảnh rồi in ra giấy A4; " "hoặc chỉ dò xem có máy in trên mạng. Hỗ trợ --size RxC (mm). KHÔNG dùng để TÌM " "văn bản trên web (đó là doc_finder), KHÔNG mô phỏng/in 3D." ) def run(self, task: str, **ctx) -> ToolResult: import paper_agent import paper_print attachment = ctx.get("attachment") on_step = ctx.get("on_progress") low = (task or "").lower() # --- sub-mode: CHỈ dò máy in (không in) --- if not attachment and _FIND_PRINTER.search(low): if callable(on_step): on_step({"kind": "running", "name": "Dò máy in", "machine": "Discovery"}) try: found = paper_print.discover_printer() except Exception as e: found = None err = str(e) else: err = None host = (found or {}).get("host") or (found or {}).get("ip") if found else None if callable(on_step): on_step({"kind": "done", "name": "Dò máy in", "machine": "Discovery", "status": "ok" if found else "warn", "detail": f"Thấy máy in tại {host}." if found else "Không thấy máy in nào trên mạng."}) return ToolResult(bool(found), f"Đã tìm thấy máy in tại {host}." if found else _no_printer_msg(err), {"printer": host}) req, opts = paper_agent.parse_args(shlex.split(task) if task else []) req = _LEAD.sub("", req or task).strip() or (req or task) try: host = paper_print.resolve_host() # discover + connect (or cache) except Exception as e: if callable(on_step): on_step({"kind": "done", "name": "Dò máy in", "machine": "Discovery", "status": "warn", "detail": "Không thấy máy in nào trên mạng."}) return ToolResult(False, _no_printer_msg(str(e)), {"reason": str(e)}) try: if attachment: # user uploaded the exact file to print path, how = attachment, "file tải lên" else: path, how = paper_agent.resolve_source(req, interactive=False) except Exception as e: return ToolResult(False, f"Không lấy được nội dung để in: {e}") result = paper_agent.print_path(Path(path), opts) if result.get("ok"): pages = f", {result['pages']} trang" if result.get("pages") else "" return ToolResult( True, f"Đã gửi in ({result.get('placed_mm')}{pages}) tới {result.get('printer', host)} " f"— nguồn: {how}", result) return ToolResult(False, f"In thất bại: {result}", result) def get_tool() -> Tool: return PaperPrinterTool()