mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-10-18 02:47:15 +00:00
visual check
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
// Capture only the tested process's window; a desktop-only image is not evidence.
|
||||
import AppKit
|
||||
import CoreGraphics
|
||||
import Foundation
|
||||
|
||||
func fail(_ message: String) -> Never {
|
||||
FileHandle.standardError.write(Data((message + "\n").utf8))
|
||||
exit(1)
|
||||
}
|
||||
|
||||
guard CommandLine.arguments.count == 3,
|
||||
let pid = Int32(CommandLine.arguments[1]) else {
|
||||
fail("Usage: capture-window PID OUTPUT.png")
|
||||
}
|
||||
let output = URL(fileURLWithPath: CommandLine.arguments[2])
|
||||
let deadline = Date().addingTimeInterval(30)
|
||||
var chosen: [String: Any]?
|
||||
while Date() < deadline {
|
||||
guard let app = NSRunningApplication(processIdentifier: pid), !app.isTerminated else {
|
||||
fail("ZoiteChat is not running")
|
||||
}
|
||||
app.activate(options: [])
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.2))
|
||||
let windows = CGWindowListCopyWindowInfo([.optionOnScreenOnly, .excludeDesktopElements],
|
||||
kCGNullWindowID) as? [[String: Any]] ?? []
|
||||
let owned = windows.filter { window in
|
||||
guard (window[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value == pid,
|
||||
(window[kCGWindowLayer as String] as? NSNumber)?.intValue == 0,
|
||||
let bounds = window[kCGWindowBounds as String] as? [String: Any],
|
||||
let width = bounds["Width"] as? NSNumber,
|
||||
let height = bounds["Height"] as? NSNumber else { return false }
|
||||
return width.doubleValue >= 600 && height.doubleValue >= 300
|
||||
}
|
||||
chosen = owned.max { left, right in
|
||||
func area(_ window: [String: Any]) -> Double {
|
||||
let bounds = window[kCGWindowBounds as String] as? [String: NSNumber] ?? [:]
|
||||
return (bounds["Width"]?.doubleValue ?? 0) * (bounds["Height"]?.doubleValue ?? 0)
|
||||
}
|
||||
return area(left) < area(right)
|
||||
}
|
||||
if chosen != nil { break }
|
||||
}
|
||||
guard let window = chosen,
|
||||
let identifier = window[kCGWindowNumber as String] as? NSNumber else {
|
||||
fail("No visible ZoiteChat window found in this runner's desktop session")
|
||||
}
|
||||
let capture = Process()
|
||||
capture.executableURL = URL(fileURLWithPath: "/usr/sbin/screencapture")
|
||||
capture.arguments = ["-x", "-o", "-t", "png", "-l", identifier.stringValue, output.path]
|
||||
do { try capture.run() } catch { fail("Cannot start screencapture: \(error)") }
|
||||
capture.waitUntilExit()
|
||||
guard capture.terminationStatus == 0,
|
||||
let png = try? Data(contentsOf: output),
|
||||
let bitmap = NSBitmapImageRep(data: png),
|
||||
bitmap.pixelsWide >= 600, bitmap.pixelsHigh >= 300 else {
|
||||
fail("Application window capture failed; check runner Screen Recording permissions")
|
||||
}
|
||||
var colors = Set<String>()
|
||||
for y in stride(from: 0, to: bitmap.pixelsHigh, by: max(1, bitmap.pixelsHigh / 60)) {
|
||||
for x in stride(from: 0, to: bitmap.pixelsWide, by: max(1, bitmap.pixelsWide / 80)) {
|
||||
if let color = bitmap.colorAt(x: x, y: y)?.usingColorSpace(.deviceRGB) {
|
||||
colors.insert("\(Int(color.redComponent * 255)),\(Int(color.greenComponent * 255)),\(Int(color.blueComponent * 255))")
|
||||
}
|
||||
}
|
||||
}
|
||||
guard colors.count >= 8 else { fail("Window capture is blank or nearly uniform") }
|
||||
let evidence: [String: Any] = ["pid": pid, "window_id": identifier.intValue,
|
||||
"width": bitmap.pixelsWide, "height": bitmap.pixelsHigh, "sampled_colors": colors.count]
|
||||
do {
|
||||
let data = try JSONSerialization.data(withJSONObject: evidence, options: [.sortedKeys])
|
||||
print(String(decoding: data, as: UTF8.self))
|
||||
} catch { fail("Cannot serialize screenshot evidence: \(error)") }
|
||||
Reference in New Issue
Block a user