ASown's picture
Consolidate project history under ASown
f368481
Raw History Blame Contribute Delete
5.75 kB
"""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()