diff --git a/src/chrome_debugger.rs b/src/chrome_debugger.rs index 15b2c9e..8c06773 100644 --- a/src/chrome_debugger.rs +++ b/src/chrome_debugger.rs @@ -1,33 +1,51 @@ use std::error::Error; use std::io::{BufRead, BufReader, Read, Write}; use std::net::{Ipv4Addr, TcpStream}; +use std::str::FromStr; use serde::Deserialize; use serde_json::json; use tungstenite::{Message, WebSocket}; +use tungstenite::error::UrlError; +use tungstenite::http::Uri; pub struct ChromeDebugger { + id: u32, ws: WebSocket } impl ChromeDebugger { - pub fn connect(port: u16) -> Result> { + pub fn connect_port(port: u16) -> Result> { let mut stream = TcpStream::connect((Ipv4Addr::LOCALHOST, port))?; let ws_url = get_websocket_url(&mut stream)?; Ok(Self { + id: 1, ws: tungstenite::client(ws_url, stream)?.0 }) } + pub fn connect_url(uri: impl AsRef) -> Result> { + let url = Uri::from_str(uri.as_ref())?; + let host = url.host().ok_or(tungstenite::Error::Url(UrlError::NoHostName))?; + let stream = TcpStream::connect((host, url.port_u16().unwrap_or(80)))?; + + Ok(Self { + id: 1, + ws: tungstenite::client(&url, stream)?.0 + }) + } + pub fn send(&mut self, method: &str, params: serde_json::Value) -> Result<(), Box> { - self.ws.write(Message::Text( + self.ws.send(Message::Text( serde_json::to_string(&json!({ - "id": 1, + "id": self.id, "method": method, "params": params }))? ))?; + self.id += 1; + if cfg!(debug_assertions) { println!("{}", self.ws.read()?); } diff --git a/src/main.rs b/src/main.rs index 1db8509..fbfc2a9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,13 @@ #![feature(try_blocks)] +#![allow(dead_code)] -use std::{env, io}; +use std::{env, io, thread}; use std::error::Error; use std::io::{BufRead, BufReader, ErrorKind}; use std::net::{Ipv4Addr, TcpListener}; -use std::path::Path; +use std::path::{Path}; use std::process::{Child, Command, Stdio}; use std::string::String; -use std::thread::sleep; use std::time::Duration; use serde_json::json; @@ -30,26 +30,31 @@ fn find_lunar_executable() -> Result { format!(r"{localappdata}\Programs\lunarclient\Lunar Client.exe") ] } - "macos" => vec!["/Applications/Lunar Client.app/Contents/MacOS/Lunar Client".into()], + "macos" => vec![ + "/Applications/Lunar Client.app/Contents/MacOS/Lunar Client".into(), + format!( + "{}/Applications/Lunar Client.app/Contents/MacOS/Lunar Client", + env::var("HOME").or(Err("$HOME not defined"))? + ) + ], "linux" => vec!["/usr/bin/lunarclient".into()], _ => Err("unsupported os")? }; - + paths.iter() .find(|p| Path::new(p).exists()) .ok_or(format!("searched in the following locations: [{}]", paths.join(", "))) .map(|p| p.clone()) } -fn wait_for_devtools_server(cmd: &mut Child) -> io::Result<()> { - let reader = BufReader::new(cmd.stderr.take().unwrap()); +fn wait_for_websocket_url(child: &mut Child) -> io::Result { + let reader = BufReader::new(child.stderr.take().unwrap()); for line in reader.lines() { - if line?.starts_with("DevTools listening on ") { - return Ok(()) + if let Some(url) = line?.strip_prefix("Debugger listening on ") { + return Ok(url.into()) } } - - Err(io::Error::new(ErrorKind::UnexpectedEof, "'DevTools listening on ' was never printed")) + Err(io::Error::new(ErrorKind::UnexpectedEof, "'Debugger listening on ' was never printed")) } fn run() -> Result<(), Box> { @@ -62,19 +67,22 @@ fn run() -> Result<(), Box> { let port = free_port()?; - let mut cp = Command::new(lunar_exe) - .arg(format!("--remote-debugging-port={}", port)) + let mut cp = Command::new(&lunar_exe) + .arg(format!("--inspect={}", port)) + .stdin(Stdio::null()) .stderr(Stdio::piped()) .spawn() .map_err(|e| format!("failed to start lunar: {}", e))?; let res = try { - wait_for_devtools_server(&mut cp)?; - // on windows the launcher gets stuck on a black screen if you inject code too early - // no idea why - sleep(Duration::from_millis(1000)); + let url = wait_for_websocket_url(&mut cp)?; + + println!("[LLI] Connecting to {}", url); + let mut debugger = ChromeDebugger::connect_url(url) + .map_err(|e| format!("failed to connect debugger: {}", e))?; - let mut debugger = ChromeDebugger::connect(port).map_err(|e| format!("failed to connect debugger: {}", e))?; + // otherwise you get "ReferenceError: require is not defined" + thread::sleep(Duration::from_millis(1000)); let payload = format!( "{}({})", @@ -83,7 +91,8 @@ fn run() -> Result<(), Box> { ); debugger.send("Runtime.evaluate", json!({ - "expression": payload + "expression": payload, + "includeCommandLineAPI": true }))?; }; diff --git a/src/payload.js b/src/payload.js index f73944d..6b2f955 100644 --- a/src/payload.js +++ b/src/payload.js @@ -23,6 +23,4 @@ opts ); } - - require('electron').remote.getCurrentWindow().webContents.removeAllListeners('devtools-opened') -}) \ No newline at end of file +})