diff --git a/src/main.rs b/src/main.rs index a5cc584..88188f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ use crossterm::{ execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; -use std::{error::Error, io}; +use std::{env, error::Error, io, process::Command}; use tui::{ backend::{Backend, CrosstermBackend}, layout::{Constraint, Direction, Layout, Rect}, @@ -114,13 +114,14 @@ fn main() -> Result<(), Box> { if !informations.is_empty() { app.messages = informations .iter() - .map(|amessage| amessage.urls.clone()) + .map(|amessage| spider::remove_quotation(amessage.ps.clone())) .collect(); app.stateoflist = true; app.state.select(Some(0)); app.index = Some(0); app.informations = informations; } + app.v2ray_input = utils::start_v2core(); let res = run_app(&mut terminal, app); // restore terminal @@ -160,6 +161,7 @@ fn run_app(terminal: &mut Terminal, mut app: App) -> io::Result<( app.show_popup = true; app.input_mode = InputMode::Popup; } + _ => {} }, InputMode::Editing => match key.code { @@ -171,7 +173,7 @@ fn run_app(terminal: &mut Terminal, mut app: App) -> io::Result<( storge.push('['); storge.push('\n'); if !list[0].is_empty() { - app.messages = list[0].clone(); + //app.messages = list[0].clone(); app.stateoflist = true; app.state.select(Some(0)); app.index = Some(0); @@ -180,15 +182,20 @@ fn run_app(terminal: &mut Terminal, mut app: App) -> io::Result<( app.informations.push(information.clone()); storge.push_str(information.get_the_json_node().as_str()); } + app.messages = app + .informations + .iter() + .map(|ainformation| { + spider::remove_quotation(ainformation.ps.clone()) + }) + .collect(); } storge.pop(); storge.pop(); storge.push('\n'); storge.push(']'); - if let Err(err) = utils::create_json_file(utils::Save::Storage, storge) - { - panic!("err {}", err); - }; + utils::create_json_file(utils::Save::Storage, storge) + .unwrap_or_else(|err| panic!("err {}", err)); } //app.messages.push(app.input.drain(..).collect()); @@ -213,7 +220,29 @@ fn run_app(terminal: &mut Terminal, mut app: App) -> io::Result<( KeyCode::Esc => { app.input_mode = InputMode::Normal; } - + KeyCode::F(5) => { + if let Some(index) = app.index { + let home = env::var("HOME").unwrap(); + utils::create_json_file( + utils::Save::Running, + app.informations[index].clone().running_json(), + ) + .unwrap_or_else(|err| panic!("err {}", err)); + Command::new("pkill").arg("v2ray").output().unwrap_or_else( + |e| panic!("failed to execute process: {}", e), + ); + Command::new("nohup") + .arg(app.v2ray_input.clone()) + .arg("-config") + .arg(home.clone() + "/.config/tv2ray/running.json") + .arg(">") + .arg(home + "/.config/tv2ray/test.log") + .arg("2>&1") + .arg("&") + .spawn() + .expect("failed"); + } + } _ => {} } } else { @@ -228,6 +257,19 @@ fn run_app(terminal: &mut Terminal, mut app: App) -> io::Result<( KeyCode::Char('e') => { app.input_mode = InputMode::PopupEdit; } + KeyCode::Char('s') => { + if let Err(err) = utils::create_json_file( + utils::Save::V2ray, + format!( + "{{ + \"v2core\":\"{}\" +}}", + app.v2ray_input + ), + ) { + panic!("{}", err); + } + } _ => {} }, InputMode::PopupEdit => { @@ -372,18 +414,49 @@ fn ui(f: &mut Frame, app: &mut App) { InputMode::PopupEdit => Style::default().fg(Color::Yellow), _ => Style::default(), }) - .block(Block::default().borders(Borders::ALL).title("Settings")); + .block(Block::default().borders(Borders::ALL).title("v2ray-core")); //f.render_widget(input, chunks[1]); let area = centered_rect(60, 20, f.size()); + //let settings :Vec = app + // .settings + // .iter() + // .map(|asetting| Paragraph::new(app.v2ray_input.as_ref()) + // .style(Style::default()) + // .block(Block::default().borders(Borders::ALL).title(asetting.clone()))) + + // .collect(); + + let chunk = Layout::default() + .direction(Direction::Vertical) + .margin(1) + .constraints( + [ + Constraint::Length(1), + Constraint::Length(3), + Constraint::Min(1), + ] + .as_ref(), + ) + .split(area); + f.render_widget(Clear, area); //this clears out the background - f.render_widget(inputpop, area); + let (msg, style) = ( + vec![Span::raw("Settings, e to edit, s to save")], + Style::default(), + ); + let mut text = Text::from(Spans::from(msg)); + text.patch_style(style); + let title = Paragraph::new(text); + f.render_widget(title, chunk[0]); + f.render_widget(inputpop, chunk[1]); + if let InputMode::PopupEdit = app.input_mode { // Make the cursor visible and ask tui-rs to put it at the specified coordinates after rendering f.set_cursor( // Put cursor past the end of the input text - area.x + app.v2ray_input.width() as u16 + 1, + chunk[1].x + app.v2ray_input.width() as u16 + 1, // Move one line down, from the border to the input line - area.y + 1, + chunk[1].y + 1, ) //InputMode::Normal | InputMode::Select | InputMode::Popup => // Hide the cursor. `Frame` does this by default, so we don't need to do anything here diff --git a/src/spider.rs b/src/spider.rs index 7c0d462..e07c0fb 100644 --- a/src/spider.rs +++ b/src/spider.rs @@ -47,7 +47,7 @@ pub fn get_the_key(paths: Vec) -> Result>> { } Ok(output) } -fn remove_quotation(input: String) -> String { +pub fn remove_quotation(input: String) -> String { let length = input.len(); (&input[1..length - 1]).to_string() } @@ -81,6 +81,340 @@ impl Information { output.push(format!("name: {}", self.ps)); output } + pub fn running_json(&self) -> String { + if self.func == *"\"vmess\"" { + format!( + "{{ + \"inbounds\":[{{ + \"port\":8889, + \"listen\":\"127.0.0.1\", + \"protocol\":\"http\", + \"settings\":{{ + \"udp\": true + }} + }}], + \"outbounds\":[{{ + \"protocol\":{}, + \"sendThrough\": \"0.0.0.0\", + \"settings\":{{ + \"vnext\": [{{ + \"address\": {}, + \"port\":{}, + \"users\":[{{ + \"alterId\": {}, + \"id\":{} + }}] + }}] + }}, + \"streamSettings\":{{ + \"dsSettings\": {{ + \"path\": {} + }}, + \"httpSettings\":{{ + \"host\": [ + ], + \"path\":{} + }}, + \"kcpSettings\": {{ + \"congestion\": false, + \"downlinkCapacity\":20, + \"header\": {{ + \"type\": \"none\" + }}, + \"mtu\": 1350, + \"readBufferSize\": 1, + \"tti\": 20, + \"uplinkCapacity\": 5, + \"writeBufferSize\": 1 + }}, + \"network\": {}, + \"quicSettings\":{{ + \"header\": {{ + \"type\":\"none\" + }}, + \"key\": \"\", + \"security\":\"\" + }}, + \"security\":\"none\", + \"sockopt\":{{ + \"mark\": 255, + \"tcpFastOpen\": false, + \"tproxy\": \"off\" + }}, + \"tcpSettings\": {{ + \"header\": {{ + \"request\" :{{ + \"headers\":{{ + }}, + \"method\": \"GET\", + \"path\":[ + ], + \"version\":\"1.1\" + }}, + \"type\": \"none\" + }} + }}, + \"tlsSettings\": {{ + \"allowInsecure\": true, + \"allowInsecureCiphers\": true, + \"alpn\":[ + ], + \"certificates\":[ + ], + \"disableSessionResumption\":true, + \"disableSystemRoot\":true, + \"serveName\": \"\" + }}, + \"wsSettings\" :{{ + \"headers\" :{{ + }}, + \"path\":{} + }}, + \"xtlsSettings\":{{ + \"allowInsecure\":true, + \"allowInsecureCiphers\":true, + \"alpn\":[ + ], + \"certificates\":[ + ], + \"disableSessionResumption\": false, + \"disableSystemRoot\": true, + \"serveName\":\"\" + }}, + \"tag\":\"outBound_PROXY\" + }} + }}, + {{ + \"protocol\":\"freedom\", + \"tag\": \"direct\", + \"settings\":{{}} + }}], + \"routing\": {{ + \"domainStrategy\": \"IPOnDemand\", + \"rules\":[{{ + \"type\":\"field\", + \"ip\":[\"geoip:private\"], + \"outboundTag\": \"direct\" + }}] + }} +}}", + self.func, self.add, remove_quotation(self.port.clone()), remove_quotation(self.aid.clone()), self.id, self.path, self.path, self.net, self.path + ) + }else{ + format!("{{ + \"api\":{{ + \"service\":[ + \"HandlerService\", + \"LoggerService\", + \"StatsService\" + ], + \"tag\": \"_QV2RAY_API_\" + }}, + \"dns\":{{ + \"service\":[ + \"1.1.1.1\", + \"8.8.8.8\", + \"8.8.4.4\" + ] + }}, + \"inbounds\":[ + {{ + \"listen\":\"127.0.0.1\", + \"protocol\": \"dokodemo-door\", + \"port\": 15490, + \"settings\":{{ + \"address\":\"127.0.0.1\" + }}, + \"sniffing\":{{ + }} + }}, + {{ + \"listen\":\"127.0.0.1\", + \"port\": 8889, + \"protocol\":\"http\", + \"settings\":{{ + \"allowTransparent\":true, + \"timeout\": 300, + \"userLevel\":0 + }}, + \"sniffing\":{{ + \"enabled\":false + }}, + \"tag\":\"http_IN\" + }}, + {{ + \"listen\": \"127.0.0.1\", + \"port\": 1089, + \"protocol\": \"socks\", + \"settings\" :{{ + \"auth\": \"noauth\", + \"ip\": \"127.0.0.1\", + \"udp\": true, + \"userLevel\": 0 + }}, + \"sniffing\":{{ + \"enabled\":false + }}, + \"tag\": \"socks_IN\" + }}, + {{ + \"listen\": \"127.0.0.1\", + \"port\": 12345, + \"protocol\" : \"dokodemo-door\", + \"settings\":{{ + \"address\":\"\", + \"followRediect\": true, + \"network\": \"tcp\", + \"port\":0, + \"timeout\":0, + \"userLevel\":0 + }}, + \"sniffing\":{{ + \"destOverride\":[ + \"http\", + \"tls\" + ], + \"enabled\": true + }}, + \"streamSettings\":{{ + \"sockopt\":{{ + \"tproxy\": \"tproxy\" + }} + }}, + \"tag\": \"tproxy_IN\" + }}, + {{ + \"listen\": \"::1\", + \"port\": 12345, + \"protocol\": \"dokodemo-door\", + \"settings\": {{ + \"address\": \"\", + \"followRediect\": true, + \"network\": \"tcp\", + \"port\": 0, + \"timeout\": 0, + \"userLevel\": 0 + }}, + \"sniffing\": {{ + \"destOverride\": [ + \"http\", + \"tls\" + ], + \"enabled\": true + }}, + \"streamSettings\": {{ + \"sockopt\": {{ + \"tproxy\": \"tproxy\" + }} + }}, + \"tag\": \"tproxy_IN_V6\" + }} + ], + \"log\": {{ + \"loglevel\": \"warning\" + }}, + \"outbounds\":[ + {{ + \"protocol\": \"shadowsocks\", + \"sendThrough\": \"0.0.0.0\", + \"settings\": {{ + \"servers\" :[ + {{ + \"address\":{}, + \"email\": \"\", + \"level\": 0, + \"method\": {}, + \"ota\":false, + \"password\":{}, + \"port\":{} + }} + ] + }}, + \"streamSettings\": {{ + \"sockopt\":{{ + \"mark\": 255 + }} + }}, + \"tag\": \"outBound_PROXY\" + }}, + {{ + \"protocol\": \"freedom\", + \"sendThrough\": \"0.0.0.0\", + \"settings\": {{ + \"domainStrategy\": \"AsIs\", + \"redirect\": \":0\", + \"userLevel\": 0 + }}, + \"streamSettings\": {{ + \"sockopt\": {{ + \"mark\": 255 + }} + }}, + \"tag\": \"outBound_DIRECT\" + }}, + {{ + \"protocol\": \"blackhole\", + \"sendThrough\": \"0.0.0.0\", + \"settings\": {{ + \"response\": {{ + \"type\": \"none\" + }} + }}, + \"streamSettings\": {{ + \"sockopt\": {{ + \"mark\": 255 + }} + }}, + \"tag\": \"outBound_BLACKHOLE\" + }} + ], + \"policy\": {{ + \"system\": {{ + \"statsInboundDownlink\": true, + \"statsInboundUplink\": true, + \"statsOutboundDownlink\": true, + \"statsOutboundUplink\": true + }} + }}, + \"routing\": {{ + \"domainStrategy\": \"AsIs\", + \"rules\": [ + {{ + \"inboundTag\": [ + \"_QV2RAY_API_INBOUND_\" + ], + \"outboundTag\": \"_QV2RAY_API_\", + \"type\": \"field\" + }}, + {{ + \"ip\": [ + \"geoip:private\" + ], + \"outboundTag\": \"outBound_DIRECT\", + \"type\": \"field\" + }}, + {{ + \"ip\": [ + \"geoip:cn\" + ], + \"outboundTag\": \"outBound_DIRECT\", + \"type\": \"field\" + }}, + {{ + \"domain\": [ + \"geosite:cn\" + ], + \"outboundTag\": \"outBound_DIRECT\", + \"type\": \"field\" + }} + ] + }}, + \"stats\": {{ + }} +}}",self.add,self.net,self.id,remove_quotation(self.port.clone())) + } + + } fn get_the_link(&self) -> String { let mut temp = String::new(); if self.func == *"\"vmess\"" { diff --git a/src/utils.rs b/src/utils.rs index c5508c4..cfc27e7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -29,22 +29,40 @@ pub fn create_json_file(save: Save, input: String) -> Result<()> { file.write_all(input.as_bytes())?; Ok(()) } -fn get_json() -> Result { +fn get_json(save: Save) -> Result { let home = env::var("HOME").unwrap(); - let location = format!("{}/.config/tv2ray/storage.json", home); + let location = match save { + Save::Storage => format!("{}/.config/tv2ray/storage.json", home), + Save::Running => format!("{}/.config/tv2ray/running.json", home), + Save::V2ray => format!("{}/.config/tv2ray/v2core.json", home), + }; let mut file = File::open(location)?; let mut output = String::new(); file.read_to_string(&mut output).unwrap(); Ok(output) } +pub fn start_v2core() -> String { + create_storage_before(); + let message = match get_json(Save::V2ray) { + Ok(output) => output, + Err(_) => { + let core = "{\n\"v2core\":\"/usr/bin/v2ray\"\n}".to_string(); + if let Err(err) = create_json_file(Save::V2ray, core.clone()) { + panic!("{}",err); + } + core + } + }; + let v: Value = serde_json::from_str(message.as_str()).unwrap(); + let message_pre = v["v2core"].to_string(); + crate::spider::remove_quotation(message_pre) +} pub fn start() -> Vec { create_storage_before(); - let messages = match get_json() { + let messages = match get_json(Save::Storage) { Ok(output) => output, Err(_) => { - if let Err(err) = create_json_file(Save::Storage, "[]".to_string()) { - panic!("{}", err); - }; + create_json_file(Save::Storage, "[]".to_string()).unwrap_or_else(|err| panic!("{}", err)); "[]".to_string() } };