├── .build ├── default.Manifest.xml ├── default.init.aardio └── default.main.aardio ├── .gitignore ├── LICENSE ├── README.md ├── app.ico ├── default.aproj ├── forms └── main │ ├── json.aardio │ ├── pac.aardio │ ├── tools │ ├── github.aardio │ ├── tools.aardio │ └── youtube.aardio │ └── v2ray.aardio ├── lib ├── config.aardio ├── style.aardio └── v2ray │ ├── config.aardio │ └── core.aardio ├── main.aardio ├── pac.txt ├── screenshots ├── config.json.png ├── pac.jpg └── win2ray.png ├── v2ray-core ├── LICENSE ├── v2ctl.exe ├── v2ray.exe └── win2ray-default-servers.json └── win2ray.exe /.build/default.Manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | win2ray 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 52 | 53 | 54 | 55 | 56 | 57 | True/PM 58 | 59 | 60 | -------------------------------------------------------------------------------- /.build/default.init.aardio: -------------------------------------------------------------------------------- 1 | //发布前触发 2 | import ide; -------------------------------------------------------------------------------- /.build/default.main.aardio: -------------------------------------------------------------------------------- 1 | //此触发器在生成EXE以后执行 2 | import ide; 3 | import fsys; 4 | 5 | //获取生成的EXE文件路径 6 | var publishFile = ide.getPublishPath(); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | v2ray-core/config.json 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # win2ray 2 | 简洁稳定的 V2Ray(兼容Shadowsocks) Windows 客户端, 3 | 支持自动检测可用的代理服务器、断线自动更换服务器。本程序使用 [aardio](http://www.aardio.com) 编写,不需要.Net运行库,生成的EXE非常小。 4 | 5 | win2ray 可自动并选择响应最快的代理服务器尝试连接,连接成功立即模拟浏览器检测访问国外网站是否异常,异常则自动更换代理服务器: 6 | ![win2ray](./screenshots/win2ray.png) 7 | 8 | 可导入导出各种格式的服务器配置,支持json、vmess链接、ss链接、文本表格格式服务器列表、订阅链接等。 9 | 可选在 ["/v2ray-core/win2ray-default-servers.json"](./v2ray-core/win2ray-default-servers.json) 文件中添加默认服务器配置(生成EXE后默认配置自动嵌入到EXE文件,可选删除该文件,也可以继续使用该文件覆盖EXE自带的默认服务器列表)。 10 | 11 | ![json格式服务器列表](./screenshots/config.json.png) 12 | 13 | ![PAC配置](./screenshots/pac.jpg) 14 | 15 | 软件首次运行时会在当前目录查找 "./v2ray-core/v2ray.exe" 16 | 发行文件仅需要 "./win2ray.exe",可选带上 "./v2ray-core/" 目录( 如果没有找到会自动到v2ray官网下载,不过没有代理服务器下载有时候非常慢 )。 17 | 18 | 19 | -------------------------------------------------------------------------------- /app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuuniang/win2ray/fb32f0e8f71c7d353cbb8c721b6a022f4ef350f3/app.ico -------------------------------------------------------------------------------- /default.aproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /forms/main/json.aardio: -------------------------------------------------------------------------------- 1 | import fonts.fontAwesome; 2 | import win.ui; 3 | /*DSG{{*/ 4 | var winform = win.form(text="aardio form";right=912;bottom=793;bgcolor=16777215) 5 | winform.add( 6 | btnUpdate={cls="plus";text="更新设置";left=731;top=750;right=863;bottom=781;align="left";bgcolor=11580047;db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=22}};iconText='\uF0C7';notify=1;textPadding={left=40};z=7}; 7 | editCoreConfig={cls="edit";left=10;top=34;right=903;bottom=744;db=1;dl=1;dr=1;dt=1;edge=1;hide=1;hscroll=1;multiline=1;vscroll=1;z=6}; 8 | editOutbounds={cls="edit";left=9;top=32;right=902;bottom=743;db=1;dl=1;dr=1;dt=1;edge=1;hscroll=1;multiline=1;vscroll=1;z=1}; 9 | lbTip={cls="static";text="可在下面输入JSON数组、或每行一个服务器链接、或每行以空格分开的服务器配置文本:";left=11;top=9;right=899;bottom=29;color=3947580;dl=1;dr=1;dt=1;font=LOGFONT(h=-13);transparent=1;z=8}; 10 | navCoreConfig={cls="plus";text="高级配置( JSON )";left=184;top=750;right=315;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=3}; 11 | navOutbounds={cls="plus";text="服务器配置( JSON )";left=53;top=750;right=184;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=2}; 12 | navRight={cls="plus";left=314;top=750;right=604;bottom=751;bgcolor=16777215;border={color=-16744448};db=1;dl=1;dr=1;forecolor=32768;linearGradient=180;z=4}; 13 | plus={cls="plus";left=9;top=750;right=53;bottom=751;border={color=-16744448};db=1;dl=1;forecolor=32768;z=5} 14 | ) 15 | /*}}*/ 16 | 17 | import config; 18 | import web.json; 19 | import win.ui.tabs; 20 | var tbs = win.ui.tabs(winform.navOutbounds,winform.navCoreConfig,winform.nav06,winform.nav091,winform.navZhengMa,winform.navTxt) 21 | tbs.margin = 0; 22 | 23 | tbs.skin({ 24 | foreground={ 25 | active=0xFFFFFFFF; 26 | default=0x00FFFFFF; 27 | hover=0xFFCCCCCC; 28 | }; 29 | checked={ 30 | foreground={ 31 | default=0x00FFFFFF; 32 | }; 33 | border = { 34 | default = {left=1;right=1;bottom=1;color=0xFF008000} 35 | }; 36 | } 37 | }); 38 | 39 | tbs.onSelchange = function(idx,strip,form){ 40 | if(idx==1){ 41 | winform.editOutbounds.hide = false; 42 | winform.editCoreConfig.hide = true; 43 | winform.lbTip.text = `可在下面输入JSON数组、或每行一个服务器链接、或每行以空格分开的服务器配置文本:` 44 | } 45 | else { 46 | winform.editOutbounds.hide = true; 47 | winform.editCoreConfig.hide = false; 48 | winform.lbTip.text = `outbounds[1]会被自动替换为服务器数组,请勿修改: ` 49 | } 50 | } 51 | tbs.selIndex = 1; 52 | 53 | import style; 54 | winform.btnUpdate.skin(style.primaryButton); 55 | winform.btnUpdate.oncommand = function(id,event){ 56 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}; 57 | 58 | if(tbs.selIndex==1){ 59 | if(..string.match(winform.editOutbounds.text,"%\[\]")){ 60 | var cfg,err = web.json.tryParse(winform.editOutbounds.text); 61 | if(!cfg){ 62 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 63 | winform.btnUpdate.disabledText = null; 64 | return; 65 | } 66 | config.proxy.outbounds = cfg : ..table.array(); 67 | } 68 | else { 69 | var outbounds = v2ray.config.importFromString(winform.editOutbounds.text); 70 | config.proxy.outbounds = outbounds : ..table.array(); 71 | winform.editOutbounds.text = web.json.stringify(outbounds,true); 72 | } 73 | 74 | config.proxy.save(); 75 | winform.editOutbounds.modified = false; 76 | publish("config.proxy.outbounds.changed",cfg) 77 | } 78 | elseif(tbs.selIndex==2) { 79 | var cfg,err = web.json.tryParse(winform.editCoreConfig.text); 80 | if(type(cfg)!==type.table){ 81 | winform.editCoreConfig.showErrorTip(,string.concat("JSON语法错误 ",err)); 82 | winform.btnUpdate.disabledText = null; 83 | return; 84 | } 85 | 86 | if(cfg.outbounds[[1]][["tag"]]!="proxy"){ 87 | winform.msgErr(`outbounds[1]必须指定为{"tag":"proxy"}`); 88 | winform.btnUpdate.disabledText = null; 89 | return; 90 | } 91 | 92 | config.core.default = cfg; 93 | config.core.save(); 94 | publish("config.proxy.outbounds.changed",cfg) 95 | } 96 | 97 | win.delay(500); 98 | winform.btnUpdate.disabledText = null; 99 | winform.msgOk("配置已更新"); 100 | } 101 | 102 | 103 | import v2ray.config; 104 | winform.editOutbounds.enablePopMenu(function(){ 105 | if(tbs.selIndex==1){ 106 | var strEditBounds = string.trim(winform.editOutbounds.text); 107 | var menu = { 108 | { /*---分隔线---*/ }; 109 | { "自剪贴板批量导入vmess、ss链接、或订阅源"; 110 | function(id){ 111 | 112 | var outbounds = v2ray.config.importFromClip(); 113 | if(#outbounds){ 114 | var json = strEditBounds; 115 | if( (!#json) || ..string.match(json,"%\[\]")){ 116 | var cfg,err = ..table.array(); 117 | if(#json){ 118 | cfg,err= web.json.tryParse(strEditBounds); 119 | if(!cfg){ 120 | winform.editOutbounds.showErrorTip(,string.concat("当前配置存在JSON语法错误 ",err)); 121 | return; 122 | } 123 | } 124 | 125 | ..table.append(cfg,outbounds); 126 | winform.editOutbounds.text = ..web.json.stringify(cfg,true); 127 | } 128 | else { 129 | var str = v2ray.config.exportToString(outbounds); 130 | if(#strEditBounds){ 131 | winform.editOutbounds.text = strEditBounds+ '\r\n' + str; 132 | } 133 | else { 134 | winform.editOutbounds.text = str; 135 | } 136 | } 137 | 138 | winform.msgOk("已成功导入" + #outbounds + "个服务器"); 139 | winform.editOutbounds.modified = true; 140 | winform.editOutbounds.setFocus(); 141 | return; 142 | } 143 | 144 | winform.msgFrown("未导入服务器, 请先复制订阅源、或vmess、ss链接!") 145 | } 146 | }; 147 | } 148 | 149 | if( (!#strEditBounds) || ..string.match(strEditBounds,"%\[\]")){ 150 | menu[3] = { "更新订阅源"; 151 | function(id){ 152 | var cfg,err = ..table.array(); 153 | 154 | var json = strEditBounds; 155 | if(#json){ 156 | cfg,err = web.json.tryParse(strEditBounds); 157 | if(!cfg){ 158 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 159 | return; 160 | } 161 | } 162 | 163 | var subscribeUrls = {} 164 | for(i=#cfg;1;-1){ 165 | var outbound = cfg[i] 166 | if(outbound.subscribeUrl){ 167 | subscribeUrls[outbound.subscribeUrl] = true; 168 | } 169 | } 170 | 171 | if(!table.count(subscribeUrls)){ 172 | import win.clip; 173 | var clibStr = win.clip.read(); 174 | if( ..string.startWith(clibStr,"http://") || ..string.startWith(clibStr,"https://") ){ 175 | subscribeUrls[clibStr] = true; 176 | } 177 | else { 178 | winform.msgWarn('当前配置不包含来自订阅源的服务器,\n请先复制订阅源网址到剪贴板!'); 179 | return; 180 | } 181 | } 182 | 183 | var count = 0; 184 | for(url,v in subscribeUrls){ 185 | var outbounds = v2ray.config.importFromString(url); 186 | if(#outbounds){ 187 | for(i=#cfg;1;-1){ 188 | var outbound = cfg[i] 189 | if(outbound.subscribeUrl==url){ 190 | ..table.remove( cfg,i ); 191 | } 192 | } 193 | 194 | count = count + #outbounds; 195 | ..table.append(cfg,outbounds); 196 | } 197 | } 198 | 199 | winform.editOutbounds.text = ..web.json.stringify(cfg,true); 200 | 201 | winform.msgOk("已成功刷新" + count + "个服务器"); 202 | winform.editOutbounds.modified = true; 203 | winform.editOutbounds.setFocus(); 204 | return; 205 | } 206 | }; 207 | } 208 | 209 | if(..string.match(strEditBounds,"%\[\]")){ 210 | menu[4] = {} 211 | menu[5] = { "转换为 vmess:// 或 ss:// 链接"; 212 | function(id){ 213 | var cfg,err = web.json.tryParse(strEditBounds); 214 | if(!cfg){ 215 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 216 | return; 217 | } 218 | 219 | var str = v2ray.config.exportToString(cfg); 220 | if(str){ 221 | winform.editOutbounds.text = str; 222 | winform.editOutbounds.modified = true; 223 | } 224 | } 225 | }; 226 | menu[6] = { "转换为 Base64 订阅源"; 227 | function(id){ 228 | var cfg,err = web.json.tryParse(strEditBounds); 229 | if(!cfg){ 230 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 231 | return; 232 | } 233 | 234 | var str = v2ray.config.exportToString(cfg); 235 | if(str){ 236 | import crypt; 237 | winform.editOutbounds.text = ..crypt.encodeBin(str); 238 | winform.editOutbounds.modified = true; 239 | } 240 | } 241 | }; 242 | menu[7] = { "转换为文本格式"; 243 | function(id){ 244 | var cfg,err = web.json.tryParse(strEditBounds); 245 | if(!cfg){ 246 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 247 | return; 248 | } 249 | 250 | var outstr = {}; 251 | for i,outbound in table.eachIndex(cfg){ 252 | if(outbound.protocol != "shadowsocks"){ 253 | var path = outbound.path; 254 | if( outbound.host){ 255 | path = ( (#outbound.type && outbound.type!="none") ? outbound.type : outbound.network : "") 256 | + "://" + outbound.host ++ path; 257 | } 258 | 259 | table.push(outstr, string.concat( outbound.address,'\t' 260 | ,outbound.port,'\t' 261 | ,outbound.id,,'\t' 262 | ,outbound.network,'\t' 263 | ,path,'\t' 264 | ,outbound.tls ) ); 265 | } 266 | else { 267 | table.push(outstr, string.concat( outbound.address,'\t' 268 | ,outbound.port,'\t' 269 | ,outbound.id,,'\t' 270 | ,'shadowsocks\t' 271 | ,outbound.security) ); 272 | } 273 | } 274 | 275 | winform.editOutbounds.text = string.join(outstr,'\r\n'); 276 | winform.editOutbounds.modified = true; 277 | } 278 | }; 279 | } 280 | elseif( #strEditBounds ) { 281 | menu[3] = {} 282 | 283 | menu[4] = { "转换为 JSON"; 284 | function(id){ 285 | import web.json; 286 | var outbounds = v2ray.config.importFromString(strEditBounds); 287 | if(outbounds){ 288 | winform.editOutbounds.text = web.json.stringify(outbounds,true); 289 | winform.editOutbounds.modified = true; 290 | } 291 | } 292 | }; 293 | } 294 | 295 | table.push(menu,{ /*---分隔线---*/ }); 296 | if(winform.editOutbounds.modified){ 297 | table.push(menu,{ "恢复为当前应用的服务器配置(JSON)"; 298 | function(id){ 299 | winform.editOutbounds.text = web.json.stringify( 300 | config.proxy.outbounds,true 301 | ) 302 | winform.editOutbounds.modified = false; 303 | } 304 | }); 305 | } 306 | 307 | table.push(menu,{ "恢复为默认服务器配置(JSON)"; 308 | function(id){ 309 | ..config.__loadDefaultOutbounds(); 310 | publish("config.proxy.outbounds.changed",cfg); 311 | } 312 | }); 313 | 314 | return menu; 315 | } 316 | }) 317 | 318 | subscribe("config.proxy.outbounds.config.changed",function(){ 319 | if(winform.editOutbounds.modified){ 320 | return; 321 | } 322 | 323 | winform.editOutbounds.text = web.json.stringify( 324 | config.proxy.outbounds,true 325 | ) 326 | 327 | winform.editOutbounds.modified = false; 328 | } ) 329 | 330 | winform.editOutbounds.text = #config.proxy.outbounds ? web.json.stringify( 331 | config.proxy.outbounds ,true 332 | ) : "[]" 333 | winform.editOutbounds.modified = false; 334 | 335 | winform.editCoreConfig.text = web.json.stringify( 336 | config.core.default,true 337 | ) 338 | 339 | winform.enableDpiScaling(); 340 | winform.show(); 341 | win.loopMessage(); 342 | return winform; -------------------------------------------------------------------------------- /forms/main/pac.aardio: -------------------------------------------------------------------------------- 1 | 2 | import win.ui; 3 | import win.ui.atom; 4 | import fonts.fontAwesome; 5 | /*DSG{{*/ 6 | var winform = win.form(text="win2ray - 代理自动配置文件(PAC)";right=912;bottom=802;bgcolor=16777215) 7 | winform.add( 8 | btnReset={cls="plus";text="恢复PAC";left=609;top=763;right=741;bottom=794;align="left";bgcolor=14935259;db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=22}};iconText='\uF0C7';notify=1;textPadding={left=40};z=6}; 9 | btnSave={cls="plus";text="保存PAC";left=751;top=763;right=883;bottom=794;align="left";bgcolor=11580047;db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=22}};iconText='\uF0C7';notify=1;textPadding={left=40};z=7}; 10 | btnUpdate={cls="plus";text="同步到网络最新PAC";left=402;top=763;right=599;bottom=794;align="left";bgcolor=14935259;db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=22}};iconText='\uF1C4';notify=1;textPadding={left=40};z=4}; 11 | editPacDirect={cls="richedit";left=9;top=37;right=902;bottom=743;db=1;dl=1;dr=1;dt=1;edge=1;hidesel=false;hscroll=1;multiline=1;vscroll=1;z=1}; 12 | editPacProxy={cls="richedit";left=9;top=37;right=902;bottom=751;db=1;dl=1;dr=1;dt=1;edge=1;hidesel=false;hscroll=1;multiline=1;vscroll=1;z=2}; 13 | lbTip={cls="plus";text="以下域名通过代理连接。";left=11;top=9;right=899;bottom=35;align="left";color=3947580;dl=1;dr=1;dt=1;font=LOGFONT(h=-13);notify=1;transparent=1;z=9}; 14 | navDirect={cls="plus";text="直连域名( JSON )";left=184;top=750;right=315;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=5}; 15 | navProxy={cls="plus";text="代理域名( JSON )";left=53;top=750;right=184;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=3}; 16 | navRight={cls="plus";left=314;top=750;right=604;bottom=751;bgcolor=16777215;border={color=-16744448};db=1;dl=1;dr=1;forecolor=32768;linearGradient=180;z=8}; 17 | plus={cls="plus";left=9;top=750;right=53;bottom=751;border={color=-16744448};db=1;dl=1;forecolor=32768;z=10} 18 | ) 19 | /*}}*/ 20 | 21 | import config; 22 | import web.json; 23 | import win.ui.tabs; 24 | var tbs = win.ui.tabs(winform.navProxy,winform.navDirect,winform.nav06,winform.nav091,winform.navZhengMa,winform.navTxt) 25 | tbs.margin = 0; 26 | 27 | tbs.skin({ 28 | foreground={ 29 | active=0xFFFFFFFF; 30 | default=0x00FFFFFF; 31 | hover=0xFFCCCCCC; 32 | }; 33 | checked={ 34 | foreground={ 35 | default=0x00FFFFFF; 36 | }; 37 | border = { 38 | default = {left=1;right=1;bottom=1;color=0xFF008000} 39 | }; 40 | } 41 | }); 42 | 43 | tbs.onSelchange = function(idx,strip,form){ 44 | if(idx==1){ 45 | winform.currentEditPac = winform.editPacProxy; 46 | winform.editPacProxy.hide = false; 47 | winform.editPacDirect.hide = true; 48 | winform.lbTip.text = `以下域名通过代理连接:` 49 | } 50 | else { 51 | winform.currentEditPac = winform.editPacDirect; 52 | winform.editPacProxy.hide = true; 53 | winform.editPacDirect.hide = false; 54 | winform.lbTip.text = `以下域名不使用代理: ` 55 | } 56 | } 57 | tbs.selIndex = 1; 58 | 59 | import win.dlg.message; 60 | win.dlg.message.install(); 61 | 62 | winform.loadPacToEdit = function(str){ 63 | var rules = string.match(str,"var\s+rules\s*=\s*(%\[\])") 64 | if(!rules) return winform.msgErr("PAC文件格式错误"); 65 | 66 | rules = web.json.parse(rules); 67 | if(!(rules && table.isArray(rules) && type(rules[1][1])==type.table)) return winform.msgErr("PAC文件格式错误"); 68 | 69 | import win.clip; 70 | 71 | 72 | var proxy,direct = table.array(),table.array() 73 | for(i=1;#rules){ 74 | win.clip.write(table.tostring(rules[i][1])) 75 | table.append(direct,rules[i][1]); 76 | table.append(proxy,rules[i][2]); 77 | } 78 | 79 | winform.editPacProxy.text = web.json.stringify(proxy,true) 80 | winform.editPacDirect.text = web.json.stringify(direct,true) 81 | } 82 | 83 | import string.conv; 84 | winform.savePacToFile = function(){ 85 | var proxy = web.json.tryParse(string.conv.fromWide(winform.editPacProxy.text)) 86 | var direct = web.json.tryParse(string.conv.fromWide(winform.editPacDirect.text)) 87 | if(!(proxy)) return winform.msgErr("代理域名配置格式错误"); 88 | if(!(direct)) return winform.msgErr("直连域名配置格式错误"); 89 | 90 | str = string.replace($"/pac.txt","var\s+rules\s*=\s*%\[\]","var rules = " + web.json.stringify({{direct;proxy}},true)) 91 | string.save(io.appData("/win2ray-v2/proxy.pac.txt"),str ); 92 | } 93 | 94 | import config; 95 | import style; 96 | winform.btnSave.skin(style.primaryButton); 97 | winform.btnUpdate.skin(style.button); 98 | winform.btnReset.skin(style.button); 99 | winform.lbTip.skin(style.link); 100 | 101 | winform.btnUpdate.oncommand = function(id,event){ 102 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}; 103 | 104 | thread.invoke( 105 | function(winform){ 106 | import crypt; 107 | import web.rest.jsonLiteClient; 108 | 109 | var http = web.rest.jsonLiteClient(); 110 | var github = http.api("https://api.github.com"); 111 | 112 | var result = github.repos["petronny"]["gfwlist2pac"].contents["gfwlist.pac"].get(); 113 | if(result[["content"]] && result[["encoding"]]=="base64"){ 114 | winform.loadPacToEdit( crypt.decodeBin(result[["content"]]) ); 115 | winform.editPacDirect.modified = true; 116 | winform.btnSave.disabled = false; 117 | winform.btnReset.disabled = false; 118 | winform.msgOk("PAC已同步到最新版本,请点击「保存PAC」按钮"); 119 | } 120 | else { 121 | winform.msgFrown("PAC更新失败,建议开启全局代理后重试。") 122 | } 123 | winform.btnUpdate.disabledText = null; 124 | },winform 125 | ) 126 | } 127 | 128 | 129 | winform.btnSave.oncommand = function(id,event){ 130 | winform.btnSave.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}; 131 | 132 | thread.invoke( 133 | function(winform){ 134 | winform.savePacToFile(); 135 | winform.updateProxy(); 136 | 137 | sleep(500); 138 | winform.btnSave.disabledText = null; 139 | winform.btnSave.disabled = true; 140 | winform.btnReset.disabled = true; 141 | winform.msgOk("已保存PAC文件") 142 | },winform 143 | ) 144 | } 145 | 146 | winform.btnReset.oncommand = function(id,event){ 147 | winform.btnReset.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}; 148 | 149 | thread.invoke( 150 | function(winform){ 151 | var pacPath = io.appData("/win2ray-v2/proxy.pac.txt"); 152 | var str = io.exist(pacPath) ? string.load(pacPath) || $"/pac.txt"; 153 | if(str) winform.loadPacToEdit(str); 154 | 155 | sleep(500); 156 | winform.btnReset.disabledText = null; 157 | winform.btnSave.disabled = true; 158 | winform.btnReset.disabled = true; 159 | winform.msgOk("已恢复到当前PAC配置") 160 | },winform 161 | ) 162 | } 163 | 164 | import config; 165 | import v2ray.core; 166 | winform.updateProxy = function(){ 167 | if(config.proxy.mode === "pac"){ 168 | inet.conn.setProxy(,"SOCKS=127.0.0.1:"+v2ray.core.getPort()); 169 | inet.conn.setProxyAutoConfig(,v2ray.core.getPacUrl() ) 170 | } 171 | elseif(..config.proxy.mode === "proxy"){ 172 | inet.conn.setProxyAutoConfig(,v2ray.core.getPacUrl() ) 173 | inet.conn.setProxy(,"SOCKS=127.0.0.1:"+v2ray.core.getPort()); 174 | } 175 | } 176 | 177 | var pacPath = io.appData("/win2ray-v2/proxy.pac.txt"); 178 | var str = io.exist(pacPath) ? string.load(pacPath) || $"/pac.txt"; 179 | if(str) winform.loadPacToEdit(str); 180 | 181 | winform.editPacProxy.modified = false; 182 | winform.editPacDirect.modified = false; 183 | winform.btnSave.disabled = true; 184 | winform.btnReset.disabled = true; 185 | 186 | winform.editPacDirect.limit = -1; 187 | winform.editPacDirect.onChange = function(){ 188 | winform.btnSave.disabled = false; 189 | winform.btnReset.disabled = false; 190 | } 191 | 192 | winform.editPacProxy.limit = -1; 193 | winform.editPacProxy.onChange = function(){ 194 | winform.btnSave.disabled = false; 195 | winform.btnReset.disabled = false; 196 | } 197 | 198 | import process; 199 | winform.lbTip.oncommand = function(id,event){ 200 | process.openUrl("https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_(PAC)_file") 201 | } 202 | 203 | import win.dlg.findReplace; 204 | var findDlgDirect = win.dlg.findReplace(winform); 205 | 206 | findDlgDirect.onSearch = function(findWhat,down,matchCase,wholeWord){ 207 | winform.currentEditPac.findText(findWhat,findDlgDirect.flags); 208 | } 209 | 210 | findDlgDirect.onReplace = function(findWhat,replaceWith,down,matchCase,wholeWord){ 211 | winform.currentEditPac.replaceText(findWhat,replaceWith,findDlgDirect.flags); 212 | } 213 | 214 | findDlgDirect.onReplaceAll = function(findWhat,replaceWith,down,matchCase,wholeWord){ 215 | winform.currentEditPac.replaceAll(findWhat,replaceWith,findDlgDirect.flags) 216 | } 217 | 218 | winform.editPacDirect.enablePopMenu(function(){ 219 | var items = { 220 | { "查找 Ctrl + F"; 221 | function(id){ 222 | findDlgDirect.find(winform.editPacDirect.selText) 223 | } 224 | }; 225 | { "替换 Ctrl + H"; 226 | function(id){ 227 | findDlgDirect.replace(winform.editPacDirect.selText); 228 | } 229 | } 230 | } 231 | return items; 232 | }); 233 | 234 | winform.editPacProxy.enablePopMenu(function(){ 235 | var items = { 236 | { "查找 Ctrl + F"; 237 | function(id){ 238 | findDlgDirect.find(winform.editPacProxy.selText) 239 | } 240 | }; 241 | { "替换 Ctrl + H"; 242 | function(id){ 243 | findDlgDirect.replace(winform.editPacProxy.selText); 244 | } 245 | } 246 | } 247 | return items; 248 | }); 249 | 250 | 251 | import win.ui.accelerator; 252 | var accelerator = win.ui.accelerator({ 253 | 254 | { 255 | ctrl = true; vkey = 'F'#; 256 | oncommand = function() findDlgDirect.find(winform.currentEditPac.selText); 257 | }; 258 | 259 | { 260 | ctrl = true; vkey = 'H'#; 261 | oncommand = function() findDlgDirect.replace(winform.currentEditPac.selText); 262 | }; 263 | 264 | },winform ); 265 | 266 | winform.enableDpiScaling(); 267 | winform.show(); 268 | win.loopMessage(); 269 | 270 | return winform; -------------------------------------------------------------------------------- /forms/main/tools/github.aardio: -------------------------------------------------------------------------------- 1 | //RUNAS//github优化 2 | import fonts.fontAwesome; 3 | import win.ui; 4 | /*DSG{{*/ 5 | var winform = win.form(text="github.com 网速优化工具";right=655;bottom=281;bgcolor=16777215;border="none";max=false) 6 | winform.add( 7 | bk={cls="bk";left=0;top=0;right=659;bottom=34;bgcolor=12632256;z=8}; 8 | cmbIpCidrs={cls="combobox";left=49;top=111;right=328;bottom=137;edge=1;items={};mode="dropdown";z=1}; 9 | editIpCurrent={cls="edit";left=118;top=206;right=366;bottom=230;edge=1;z=5}; 10 | plusExploreHosts={cls="plus";text='\uF07C 浏览hosts文件';left=450;top=204;right=607;bottom=235;bgcolor=-6371181;font=LOGFONT(h=-15;name='FontAwesome';charset=0);notify=1;z=7}; 11 | plusUpdateDns={cls="plus";text='\uF0AD 更改github.com解析到选定IP / 并更新图像等服务器IP';left=77;top=158;right=563;bottom=189;bgcolor=-6371181;font=LOGFONT(h=-15;name='FontAwesome';charset=0);notify=1;z=3}; 12 | plusUpdateIps={cls="plus";text='\uF021 获取最新IP列表 / 测速';left=345;top=108;right=607;bottom=139;bgcolor=-6371181;font=LOGFONT(h=-15;name='FontAwesome';charset=0);notify=1;z=2}; 13 | static={cls="static";text="本工具使用github官网接口获取最新IP列表,从github下载zip的速度与此无关。";left=49;top=50;right=583;bottom=86;font=LOGFONT(h=-13);transparent=1;z=4}; 14 | static2={cls="static";text="当前指向IP:";left=27;top=208;right=111;bottom=228;align="right";transparent=1;z=6} 15 | ) 16 | /*}}*/ 17 | 18 | winform.cmbIpCidrs.onListChange = function(){ 19 | winform.setTimeout( 20 | function(){ 21 | var ip = string.match(winform.cmbIpCidrs.selText,"\d+\.\d+\.\d+\.\d+"); 22 | if(!ip){ 23 | return ; 24 | } 25 | winform.plusUpdateDns.text = "更改 github.com 域名解析到:" + ip 26 | } 27 | ); 28 | } 29 | 30 | import wsock; 31 | import fsys.config; 32 | config = fsys.config( io.appData("/aardio/github-ip-tools/") ); 33 | winform.plusUpdateIps.oncommand = function(id,event){ 34 | winform.plusUpdateIps.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 35 | winform.text = "请稍候,正在测试github.com所有可用IP的响应速度" 36 | 37 | var metaInfo = win.invoke( 38 | function(){ 39 | import win; 40 | import web.rest.jsonClient; 41 | var http = web.rest.jsonClient(); 42 | var github = http.api("https://api.github.com/") 43 | 44 | return github.meta.get() : { git = { 45 | "192.30.252.0"; 46 | "185.199.108.0"; 47 | "140.82.112.0"; 48 | "13.114.40.48"; 49 | "13.229.188.59"; 50 | "13.234.176.102"; 51 | "13.234.210.38"; 52 | "13.236.229.21"; 53 | "13.237.44.5"; 54 | "13.250.177.223"; 55 | "15.164.81.167"; 56 | "18.194.104.89"; 57 | "18.195.85.27"; 58 | "35.159.8.160"; 59 | "52.192.72.89"; 60 | "52.64.108.95"; 61 | "52.69.186.44"; 62 | "52.74.223.119"; 63 | "52.78.231.108" 64 | } }; 65 | } 66 | ) 67 | 68 | if(metaInfo[["git"]]){ 69 | config.meta.data = table.map(metaInfo.git,lambda(v)..string.match(v,"^\d+\.\d+\.\d+\.\d+")) 70 | } 71 | elseif(!config.meta.data){ 72 | winform.msgboxErr("您的操作系统版本过低") 73 | } 74 | 75 | var pingThread = function( ip ) { 76 | import wsock.tcp.client; 77 | 78 | var result = 0; 79 | for(i=1;4;1){ 80 | 81 | var beginTick = ..time.tick() 82 | var tcp = wsock.tcp.client() 83 | 84 | tcp.setTimeouts(1000,1000); 85 | if( ! tcp.connectTimeout(ip,80,2) ){ 86 | return; 87 | }; 88 | 89 | sendData =/*********** 90 | GET / HTTP/1.1 91 | Host: www.github.com 92 | Connection: close 93 | User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) 94 | Accept: */*; 95 | Accept-Language: zh-CN,zh; 96 | Accept-Charset:utf-8; 97 | ***********/ 98 | tcp.write( sendData + '\r\n\r\n' ) 99 | 100 | var rep = tcp.readAll(); 101 | tcp.close(); 102 | 103 | if( rep && ..string.startWith(rep,"HTTP/1.1 301 Moved Permanently") ){ 104 | result = result + ((..time.tick() - beginTick)/1000); 105 | } 106 | else { 107 | return; 108 | } 109 | 110 | } 111 | 112 | return ip,math.round(result/4,2); 113 | 114 | } 115 | 116 | import thread.manage; 117 | manage = thread.manage(#config.meta.data) 118 | 119 | var ipData = {} 120 | for(k,ip in config.meta.data){ 121 | manage.createSuspended(pingThread,ip).onEnd = function(ip,sec){ 122 | if(ip && sec!==null ) { 123 | table.push(ipData,ip + " 响应速度:" + sec + "秒"); 124 | winform.cmbIpCidrs.items = ipData; 125 | winform.cmbIpCidrs.selIndex = 1; 126 | } 127 | } 128 | } 129 | 130 | manage.resume(); 131 | manage.waitClose(); 132 | 133 | winform.cmbIpCidrs.items = ipData; 134 | winform.cmbIpCidrs.selIndex = 1; 135 | winform.cmbIpCidrs.onListChange(); 136 | 137 | winform.editIpCurrent.text = wsock.getIp("www.github.com") 138 | 139 | winform.text = "github.com 网速优化工具" 140 | winform.plusUpdateIps.disabledText = null; 141 | } 142 | 143 | import fsys.hosts; 144 | winform.plusUpdateDns.oncommand = function(id,event){ 145 | var ip = winform.cmbIpCidrs.text; 146 | if(!#ip){ 147 | winform.msgboxErr("请选选择IP地址") 148 | return; 149 | } 150 | 151 | ip = string.match(ip,"\d+\.\d+\.\d+\.\d+"); 152 | if( ip == winform.editIpCurrent.text ) return winform.msgboxErr("域名之前已经解析到该IP地址") 153 | 154 | winform.plusUpdateDns.disabledText = "正在更新IP" 155 | 156 | var githubIps = { 157 | ["www.github.com"] = ip; 158 | ["github.com"] = ip; 159 | } 160 | 161 | import web.rest.jsonLiteClient; 162 | var http = web.rest.jsonLiteClient(); 163 | var ipApi = http.api("http://ip-api.com/json"); 164 | var data = ipApi["raw.githubusercontent.com"].get(); 165 | if(data[["query"]]){ 166 | githubIps["raw.github.com"] = data[["query"]]; 167 | githubIps["assets-cdn.github.com"] = data[["query"]]; 168 | githubIps["raw.githubusercontent.com"] = data[["query"]]; 169 | githubIps["gist.githubusercontent.com"] = data[["query"]]; 170 | githubIps["cloud.githubusercontent.com"] = data[["query"]]; 171 | githubIps["camo.githubusercontent.com"] = data[["query"]]; 172 | githubIps["avatars0.githubusercontent.com"] = data[["query"]]; 173 | githubIps["avatars1.githubusercontent.com"] = data[["query"]]; 174 | githubIps["avatars2.githubusercontent.com"] = data[["query"]]; 175 | githubIps["avatars3.githubusercontent.com"] = data[["query"]]; 176 | githubIps["avatars4.githubusercontent.com"] = data[["query"]]; 177 | githubIps["avatars5.githubusercontent.com"] = data[["query"]]; 178 | githubIps["avatars6.githubusercontent.com"] = data[["query"]]; 179 | githubIps["avatars7.githubusercontent.com"] = data[["query"]]; 180 | githubIps["avatars8.githubusercontent.com"] = data[["query"]]; 181 | githubIps["marketplace-screenshots.githubusercontent.com"] = data[["query"]]; 182 | githubIps["repository-images.githubusercontent.com"] = data[["query"]]; 183 | githubIps["user-images.githubusercontent.com"] = data[["query"]]; 184 | githubIps["desktop.githubusercontent.com"] = data[["query"]]; 185 | } 186 | 187 | fsys.hosts.ownCacls(); 188 | fsys.hosts.update(githubIps); 189 | 190 | win.delay(1000); 191 | winform.editIpCurrent.text = wsock.getIp("www.github.com"); 192 | winform.plusUpdateDns.disabledText = null; 193 | 194 | if( ip != winform.editIpCurrent.text ){ 195 | winform.msgboxErr("修改hosts文件失败,如果hosts文件被其他软件锁定时请先解除锁定,也可以搜索网上教程试试设置文件权限") 196 | } 197 | } 198 | 199 | winform.plusExploreHosts.skin( 200 | background = { 201 | hover = "/res/images/btn-hover.png"; 202 | focus = "/res/images/btn-focus.jpg"; 203 | active = "/res/images/btn-active.png"; 204 | disabled = "/res/images/btn-disabled.png"; 205 | } 206 | ) 207 | winform.plusExploreHosts.oncommand = function(id,event){ 208 | import process; 209 | process.exploreSelect(fsys.hosts.path) 210 | } 211 | 212 | winform.plusUpdateDns.skin({ 213 | background={ 214 | default=0xFF93C89E; 215 | hover=0xFF928BB3 216 | } 217 | }) 218 | 219 | winform.plusUpdateIps.skin({ 220 | background={ 221 | default=0xFF93C89E; 222 | hover=0xFF928BB3 223 | } 224 | }) 225 | 226 | winform.plusExploreHosts.skin({ 227 | background={ 228 | default=0xFF93C89E; 229 | hover=0xFF928BB3 230 | } 231 | }) 232 | 233 | import win.ui.simpleWindow2; 234 | win.ui.simpleWindow2(winform); 235 | 236 | winform.enableDpiScaling(); 237 | winform.show() 238 | winform.plusUpdateIps.oncommand(); 239 | 240 | win.loopMessage(); 241 | -------------------------------------------------------------------------------- /forms/main/tools/tools.aardio: -------------------------------------------------------------------------------- 1 | import fonts.fontAwesome; 2 | import win.ui; 3 | /*DSG{{*/ 4 | var winform = win.form(text="aardio form";right=759;bottom=469;bgcolor=16777215) 5 | winform.add( 6 | btnAardio={cls="plus";text="aardio 桌面软件快速开发";left=44;top=287;right=295;bottom=329;align="left";color=3947580;dl=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-27;name='FontAwesome');padding={left=18}};iconText='\uF17A';notify=1;textPadding={left=50};z=3}; 7 | btnDnsFlush={cls="plus";text="清空DNS缓存";left=44;top=106;right=430;bottom=148;align="left";color=3947580;dl=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-27;name='FontAwesome');padding={left=18}};iconText='\uF021';notify=1;textPadding={left=50};z=5}; 8 | btnGitHub={cls="plus";text="github 网速优化";left=44;top=227;right=280;bottom=269;align="left";color=3947580;dl=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-27;name='FontAwesome');padding={left=18}};iconText='\uF09B';notify=1;textPadding={left=50};z=2}; 9 | btnUpdateSystemTime={cls="plus";text="同步系统时间( 修正 v2ray 连接报错 )";left=44;top=46;right=430;bottom=88;align="left";color=3947580;dl=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-27;name='FontAwesome');padding={left=18}};iconText='\uF017';notify=1;textPadding={left=50};z=4}; 10 | btnYoutube={cls="plus";text="youtube 视频下载工具";left=44;top=167;right=280;bottom=209;align="left";color=3947580;dl=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-27;name='FontAwesome');padding={left=18}};iconText='\uF167';notify=1;textPadding={left=50};z=1} 11 | ) 12 | /*}}*/ 13 | 14 | import style; 15 | winform.btnYoutube.skin(style.plainButton) 16 | 17 | var frmYoutube; 18 | winform.btnYoutube.oncommand = function(id,event){ 19 | if(frmYoutube&&win.isWindow(frmYoutube.hwnd)){ 20 | if(win.isIconic(frmYoutube.hwnd)){ 21 | frmYoutube.show(9/*_SW_RESTORE*/); 22 | } 23 | 24 | frmYoutube.show(); 25 | return; 26 | } 27 | 28 | frmYoutube = winform.loadForm("\forms\main\tools\youtube.aardio"); 29 | frmYoutube.show(); 30 | } 31 | 32 | import process; 33 | winform.btnGitHub.skin(style.plainButton) 34 | winform.btnGitHub.oncommand = function(id,event){ 35 | if(_STUDIO_INVOKED){ 36 | winform.msgboxErr("请在发布后运行此功能"); 37 | return; 38 | } 39 | 40 | process.execute(io._exepath,"/github","runas"); 41 | } 42 | 43 | winform.btnAardio.skin(style.plainButton) 44 | winform.btnAardio.oncommand = function(id,event){ 45 | process.openUrl("http://www.aardio.com") 46 | } 47 | 48 | winform.btnUpdateSystemTime.skin(style.plainButton) 49 | winform.btnUpdateSystemTime.oncommand = function(id,event){ 50 | if(_STUDIO_INVOKED){ 51 | winform.msgboxErr("请在发布后运行此功能"); 52 | return; 53 | } 54 | 55 | winform.btnUpdateSystemTime.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 56 | if( process.executeWait(io._exepath,"/updateTime","runas") ){ 57 | winform.btnUpdateSystemTime.disabledText = null; 58 | winform.btnUpdateSystemTime.text = '时间已同步' 59 | winform.setTimeout( 60 | function(){ 61 | winform.btnUpdateSystemTime.text = '同步系统时间( 修正 v2ray 连接报错 )' 62 | },2000) 63 | } 64 | else { 65 | winform.btnUpdateSystemTime.disabledText = null; 66 | } 67 | } 68 | 69 | winform.btnDnsFlush.skin(style.plainButton) 70 | winform.btnDnsFlush.oncommand = function(id,event){ 71 | winform.btnDnsFlush.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 72 | 73 | import process.popen; 74 | var prcs = process.popen(,"ipconfig /flushdns"); 75 | var str = prcs.read(-1) 76 | 77 | var dnsapi = ..raw.loadDll("dnsapi.dll"); 78 | dnsapi.DnsFlushResolverCacheB(); 79 | 80 | winform.msgInfo(str); 81 | winform.btnDnsFlush.disabledText = null; 82 | } 83 | 84 | winform.enableDpiScaling(); 85 | winform.show(); 86 | win.loopMessage(); -------------------------------------------------------------------------------- /forms/main/tools/youtube.aardio: -------------------------------------------------------------------------------- 1 | import fonts.fontAwesome; 2 | import win.ui; 3 | import win.ui.atom; 4 | /*DSG{{*/ 5 | var winform = win.form(text="youtube 视频下载工具(点击视频链接即可下载)";right=759;bottom=469) 6 | winform.add( 7 | btnVideoInfo={cls="button";text="获取视频下载地址";left=511;top=27;right=732;bottom=60;dr=1;dt=1;font=LOGFONT(h=-16;name='FontAwesome');z=3}; 8 | editInfo={cls="richedit";left=29;top=83;right=735;bottom=445;autohscroll=false;db=1;dl=1;dr=1;dt=1;edge=1;link=1;multiline=1;vscroll=1;z=1}; 9 | editVideoUrl={cls="edit";text="https://www.youtube.com/watch?v=ZFA5Rax0ypU";left=27;top=29;right=499;bottom=58;dl=1;dr=1;dt=1;edge=1;multiline=1;z=2} 10 | ) 11 | /*}}*/ 12 | 13 | winform.btnVideoInfo.oncommand = function(id,event){ 14 | var url = winform.editVideoUrl.text; 15 | var vid = string.match(url,"v=([\w\-]+)"); 16 | if(!vid){ 17 | return winform.editVideoUrl.showErrorTip(,"请输入正确的视频地址"); 18 | } 19 | 20 | winform.btnVideoInfo.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 21 | thread.invoke( 22 | function(winform,vid){ 23 | import web.rest.jsonLiteClient; 24 | var http = web.rest.jsonLiteClient(); 25 | 26 | var videoApi = http.api("https://www.youtube.com/get_video_info?video_id={video_id}"); 27 | var info = videoApi[vid].get(); 28 | 29 | if(!info){ 30 | winform.editInfo.text = "网络错误"; 31 | winform.btnVideoInfo.disabledText = null; 32 | } 33 | 34 | winform.editInfo.text = ""; 35 | winform.editInfo.print( "视频作者:",info.player_response.videoDetails.author); 36 | winform.editInfo.print( "视频标题:",info.player_response.videoDetails.title); 37 | winform.editInfo.print( "视频说明:",info.player_response.videoDetails.shortDescription); 38 | winform.videoTitle = info.player_response.videoDetails.title; 39 | 40 | for(i,v in info.player_response.streamingData.adaptiveFormats){ 41 | if(!string.indexOf(v.mimeType,"audio/mp4")){ 42 | continue; 43 | } 44 | 45 | winform.editInfo.print(); 46 | winform.editInfo.print("音频质量:",v.quality); 47 | winform.editInfo.print("音频地址:",v.url); 48 | } 49 | 50 | for(i,v in info.player_response.streamingData.formats){ 51 | if(!string.indexOf(v.mimeType,"video/mp4")){ 52 | continue; 53 | } 54 | 55 | winform.editInfo.print(); 56 | winform.editInfo.print("视频质量:",v.qualityLabel); 57 | winform.editInfo.print("视频地址(含音频):",v.url); 58 | } 59 | 60 | 61 | var captionTracks = info.player_response.captions[["playerCaptionsTracklistRenderer"]][["captionTracks"]]; 62 | if(info.captions[["playerCaptionsTracklistRenderer"]][["captionTracks"]]){ 63 | if(#captionTracks){ 64 | for(k,v in captionTracks){ 65 | if(v.languageCode=="en"){ 66 | var str = http.get(v.baseUrl) 67 | winform.editInfo.print(str); 68 | break 69 | } 70 | } 71 | 72 | } 73 | } 74 | 75 | winform.editInfo.setFocus(); 76 | winform.btnVideoInfo.disabledText = null; 77 | },winform,vid 78 | ) 79 | } 80 | 81 | import fsys.dlg; 82 | import inet.downBox; 83 | winform.editInfo.onlink=function(message,href){ 84 | if( message = 0x202/*_WM_LBUTTONUP*/ ) { 85 | var path = fsys.dlg.open("*.mp4|*.mp4","请选择下载路径",,winform,,fsys.path.validName(winform.videoTitle)) 86 | if(!path) return; 87 | 88 | var downBox = inet.downBox(winform,"下载视频"); 89 | downBox.download(href,path); 90 | } 91 | } 92 | 93 | winform.enableDpiScaling(); 94 | winform.show(); 95 | win.loopMessage(); -------------------------------------------------------------------------------- /forms/main/v2ray.aardio: -------------------------------------------------------------------------------- 1 | import fonts.fontAwesome; 2 | import win.ui; 3 | import win.ui.atom; 4 | /*DSG{{*/ 5 | var frmV2ray = win.form(text="win2ray ";right=959;bottom=591;bgcolor=16777215) 6 | frmV2ray.add( 7 | btnEditPac={cls="plus";text="编辑PAC";left=346;top=364;right=452;bottom=400;align="left";db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=12}};iconText='\uF1C4';notify=1;textPadding={left=30};z=7}; 8 | btnImportServerFromClipBd={cls="plus";text="批量导入链接";left=459;top=364;right=592;bottom=400;align="left";db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=12}};iconText='\uF196 ';notify=1;textPadding={left=30};z=8}; 9 | btnTcping={cls="plus";text="自动检测并连接可用服务器";left=712;top=366;right=941;bottom=398;align="left";bgcolor=11580047;border={radius=4};db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=18}};iconText='\uF012';notify=1;textPadding={left=42};z=3}; 10 | chkAutoTest={cls="plus";text="断线重连";left=598;top=364;right=701;bottom=400;align="left";db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=12}};iconText='\uF0C8';notify=1;textPadding={left=30};z=9}; 11 | edit={cls="edit";left=6;top=405;right=953;bottom=585;bgcolor=0;color=16777215;db=1;dl=1;dr=1;edge=1;hscroll=1;multiline=1;vscroll=1;z=2}; 12 | listview={cls="listview";left=4;top=5;right=954;bottom=358;db=1;dl=1;dr=1;dt=1;edge=1;fullRow=1;gridLines=1;z=1}; 13 | radioProxy={cls="radiobutton";text="全局代理";left=129;top=370;right=226;bottom=395;bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-13);z=4}; 14 | radioProxyDirect={cls="radiobutton";text="不使用代理";left=14;top=370;right=120;bottom=395;bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-13);z=6}; 15 | radioProxyPac={cls="radiobutton";text="PAC代理";left=234;top=370;right=321;bottom=395;bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-13);z=5} 16 | ) 17 | /*}}*/ 18 | 19 | frmV2ray.show(); 20 | frmV2ray.listview.insertColumn("类型",100) 21 | frmV2ray.listview.insertColumn("服务器",160) 22 | frmV2ray.listview.insertColumn("端口",70) 23 | frmV2ray.listview.insertColumn("加密",90) 24 | frmV2ray.listview.insertColumn("协议",50) 25 | frmV2ray.listview.insertColumn("备注",190) 26 | frmV2ray.listview.insertColumn("响应速度",80) 27 | frmV2ray.listview.insertColumn("状态",-1) 28 | 29 | import config; 30 | import v2ray.core; 31 | var currentOutboundIndex,currentOutboundAddress; 32 | var activeOutbound = function(outboundIndex,outboundAddress,enableRetry){ 33 | if(!outboundIndex && !outboundAddress){ 34 | outboundIndex,outboundAddress = currentOutboundIndex,currentOutboundAddress ; 35 | if(!outboundIndex && !outboundAddress) return; 36 | } 37 | 38 | var currentTesting = currentOutboundIndex == outboundIndex && currentOutboundAddress == outboundAddress; 39 | if( currentTesting || v2ray.core.restart(frmV2ray.edit,config.proxy.outbounds[outboundIndex]) ){ 40 | currentOutboundIndex,currentOutboundAddress = outboundIndex,outboundAddress; 41 | frmV2ray.autoTesting = true; 42 | 43 | ..thread.invoke( 44 | function(frmV2ray,outboundIndex,outboundAddress,port,enableRetry,currentTesting){ 45 | 46 | import wsock.tcp.socks5Client; 47 | 48 | var client; 49 | for(i=1;5;1){ 50 | sleep(1000); 51 | client = wsock.tcp.socks5Client("127.0.0.1",port); 52 | if(client)break; 53 | } 54 | if(!client){ 55 | frmV2ray.onStartV2rayFailed(outboundIndex); 56 | return; 57 | }; 58 | 59 | client.setTimeouts(1000,1000); 60 | if(client.connect("www.google.com",80) ){ 61 | var sendData =/*********** 62 | GET / HTTP/1.1 63 | Host: www.google.com 64 | Connection: close 65 | User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) 66 | Accept: */*; 67 | Accept-Language: zh-CN,zh; 68 | Accept-Charset:utf-8; 69 | ***********/ 70 | client.write( sendData + '\r\n\r\n' ) 71 | var rep = client.read(9); 72 | client.close(); 73 | 74 | if(outboundAddress!=frmV2ray.listview.getItemText(outboundIndex,2)){ 75 | return; 76 | } 77 | 78 | if( rep && ..string.startWith(rep,"HTTP/1.1 ") ){ 79 | 80 | import inet.http; 81 | var http = inet.http(,"SOCKS=127.0.0.1:"+port); 82 | http.setTimeouts(1000,2000,2000); 83 | var html = http.get("https://www.google.com"); 84 | http.close(); 85 | 86 | if( html ){ 87 | frmV2ray.onStartV2rayComplete(outboundIndex,currentTesting); 88 | return; 89 | } 90 | } 91 | } 92 | 93 | frmV2ray.onStartV2rayFailed(outboundIndex,enableRetry); 94 | },frmV2ray,outboundIndex,outboundAddress,v2ray.core.getPort(),enableRetry,currentTesting 95 | ) 96 | } 97 | } 98 | 99 | frmV2ray.onStartV2rayComplete = function(outboundIndex,currentTesting){ 100 | frmV2ray.listview.ensureVisible(outboundIndex); 101 | 102 | for b,v in table.eachIndex(config.proxy.outbounds){ 103 | frmV2ray.listview.setItemText("",b,8); 104 | } 105 | 106 | frmV2ray.autoTesting = false; 107 | frmV2ray.listview.setItemText("活动服务器:正常",outboundIndex,8); 108 | 109 | if(!currentTesting) frmV2ray.edit.print("已切换到服务器:",frmV2ray.listview.getItemText(outboundIndex,2)); 110 | 111 | if(frmV2ray.listview.getItemText(outboundIndex,7)=="不可用"){ 112 | frmV2ray.listview.setItemText("...",outboundIndex,7); 113 | } 114 | 115 | frmV2ray.onTcpingReturn = function(){}; 116 | frmV2ray.btnTcping.disabledText = null; 117 | frmV2ray.validOutbounds = {}; 118 | } 119 | 120 | frmV2ray.onStartV2rayFailed = function(outboundIndex,enableRetry){ 121 | frmV2ray.listview.ensureVisible(outboundIndex); 122 | 123 | 124 | if(enableRetry){ 125 | if(#frmV2ray.validOutbounds){ 126 | var next = table.shift(frmV2ray.validOutbounds); 127 | activeOutbound(next[1],next[2],#frmV2ray.validOutbounds); 128 | frmV2ray.listview.setItemText("异常,测试下一个...",outboundIndex,8); 129 | frmV2ray.edit.print("服务器异常:",frmV2ray.listview.getItemText(outboundIndex,2)); 130 | frmV2ray.edit.print("正在尝试连接:",next[2]); 131 | } 132 | else { 133 | frmV2ray.btnTcping.oncommand(); 134 | } 135 | } 136 | else { 137 | frmV2ray.listview.setItemText("活动服务器:异常",outboundIndex,8); 138 | frmV2ray.edit.print("已切换到服务器:",frmV2ray.listview.getItemText(outboundIndex,2)); 139 | frmV2ray.autoTesting = false; 140 | } 141 | 142 | frmV2ray.btnTcping.disabledText = null; 143 | } 144 | 145 | var tcping = function(frmV2ray,outboundIndex,address){ 146 | import wsock.tcp.client; 147 | var item = frmV2ray.listview.items[outboundIndex]; 148 | 149 | var timeout = 0; 150 | for(i=1;8;1){ 151 | var client = wsock.tcp.client(); 152 | var tickBegin = time.tick(); 153 | if(client.connectTimeout(item[2],tonumber(item[3]),3)){ 154 | var ret = client.send("."); 155 | client.close(); 156 | 157 | if( ret != 1){ 158 | frmV2ray.listview.setItemText("不可用",outboundIndex,7); 159 | frmV2ray.onTcpingReturn(outboundIndex,address,false); 160 | return; 161 | } 162 | timeout = timeout + (time.tick() - tickBegin); 163 | } 164 | else { 165 | client.close(); 166 | frmV2ray.listview.setItemText("不可用",outboundIndex,7); 167 | frmV2ray.onTcpingReturn(outboundIndex,address,false); 168 | return; 169 | } 170 | } 171 | 172 | frmV2ray.onTcpingReturn(outboundIndex,address,true); 173 | frmV2ray.listview.setItemText(math.round(timeout / 8 / 1000,2)+"秒",outboundIndex,7); 174 | }; 175 | 176 | import style; 177 | frmV2ray.btnTcping.skin(style.primaryButton); 178 | frmV2ray.btnTcping.oncommand = function(id,event){ 179 | 180 | var items = frmV2ray.listview.items; 181 | if(#items){ 182 | frmV2ray.btnTcping.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 183 | if(!v2ray.core.getPath()){ 184 | frmV2ray.btnTcping.disabledText = null; 185 | frmV2ray.msgErr("下载 v2ray.exe 失败") 186 | return; 187 | } 188 | 189 | var faieldCount = 0; 190 | frmV2ray.onTcpingReturn = function(outboundIndex,address,succeeded){ 191 | if(address!=frmV2ray.listview.getItemText(outboundIndex,2)){ 192 | return; 193 | } 194 | 195 | if(config.proxy.outbounds[outboundIndex].autoConnect === false ){ 196 | faieldCount++; 197 | if(faieldCount==#items) frmV2ray.autoTesting = false; 198 | return; 199 | } 200 | 201 | if(succeeded){ 202 | frmV2ray.validOutbounds = {} 203 | frmV2ray.onTcpingReturn = function(outboundIndex,address,succeeded){ 204 | if(address!=frmV2ray.listview.getItemText(outboundIndex,2)){ 205 | return; 206 | } 207 | 208 | if(config.proxy.outbounds[outboundIndex].autoConnect === false ){ 209 | faieldCount++; 210 | if(faieldCount==#items) frmV2ray.autoTesting = false; 211 | return; 212 | } 213 | 214 | if(succeeded) table.push(frmV2ray.validOutbounds,{outboundIndex;address}); 215 | else{ 216 | faieldCount++; 217 | if(faieldCount==#items) frmV2ray.autoTesting = false; 218 | } 219 | } 220 | activeOutbound(outboundIndex,address,#items>1); 221 | } 222 | else{ 223 | faieldCount++; 224 | if(faieldCount==#items) frmV2ray.autoTesting = false; 225 | } 226 | } 227 | 228 | for b,v in table.eachIndex(config.proxy.outbounds){ 229 | frmV2ray.listview.setItemText("...",b,7); 230 | frmV2ray.listview.setItemText("",b,8); 231 | } 232 | 233 | var handles = {} 234 | thread.createSuspended(true); 235 | for(i=1;#items;1){ 236 | var h = thread.create(tcping,frmV2ray,i,frmV2ray.listview.getItemText(i,2)); 237 | table.push(handles,h); 238 | } 239 | thread.createSuspended(false); 240 | 241 | var dnsapi = ..raw.loadDll("dnsapi.dll"); 242 | dnsapi.DnsFlushResolverCacheB(); 243 | 244 | for(i=1;#handles;1){ 245 | var h = handles[i]; 246 | thread.resume(h); 247 | raw.closehandle(h); 248 | } 249 | 250 | ..thread.waitOne(handles); 251 | frmV2ray.btnTcping.disabledText = null; 252 | } 253 | else { 254 | if(id) frmV2ray.msgWarn("请先添加服务器") 255 | } 256 | } 257 | 258 | frmV2ray.onDestroy = function(){ 259 | v2ray.core.stop(); 260 | } 261 | 262 | frmV2ray.listview.onnotify = function(id,code,ptr){ 263 | select(code) { 264 | case 0xFFFFFFF4/*_NM_CUSTOMDRAW*/ { 265 | var lvcd = frmV2ray.listview.getNotifyCustomDraw(code,ptr); 266 | if( lvcd.nmcd.dwDrawStage == 0x10001/*_CDDS_ITEMPREPAINT*/) 267 | return 0x20/*_CDRF_NOTIFYSUBITEMDRAW*/ 268 | elseif( lvcd.nmcd.dwDrawStage == 1/*_CDDS_PREPAINT*/ ){ 269 | return 0x20/*_CDRF_NOTIFYITEMDRAW*/; 270 | } 271 | elseif( lvcd.nmcd.dwDrawStage == ( 0x10001/*_CDDS_ITEMPREPAINT*/ | 0x20000/*_CDDS_SUBITEM*/) ){ 272 | //注意这里 iSubItem 的索引自0开始( 其他函数通常自1开始 ) 273 | lvcd.clrText = lvcd.nmcd.dwItemSpec % 2 ? 0x373737 : 0; 274 | lvcd.clrTextBk = lvcd.nmcd.dwItemSpec % 2 ? 0xFFFFFF : 0xF5F5F5; 275 | lvcd.update() 276 | 277 | return 0/*_CDRF_DODEFAULT*/ 278 | } 279 | } 280 | case 0xFFFFFFFD/*_NM_DBLCLK*/ { 281 | var nm = frmV2ray.listview.getNotifyMessage(code,ptr); 282 | if( nm ){ 283 | frmV2ray.onTcpingReturn = function(i){} 284 | frmV2ray.btnTcping.disabledText = null; 285 | 286 | for b,v in table.eachIndex(config.proxy.outbounds){ 287 | frmV2ray.listview.setItemText("",b,8); 288 | } 289 | activeOutbound(nm.iItem,frmV2ray.listview.getItemText(nm.iItem,2),false); 290 | } 291 | } 292 | case 0xFFFFFFFB/*_NM_RCLICK*/ { 293 | var x,y = win.getCursorPos(); 294 | var nm = frmV2ray.listview.getNotifyMessage(code,ptr); 295 | if(!nm) return; 296 | 297 | var currentIdx = nm.iItem; 298 | var popmenu = win.ui.popmenu(frmV2ray); 299 | popmenu.add('设为活动服务器( 鼠标双击 )',function(id){ 300 | if(currentIdx){ 301 | frmV2ray.onTcpingReturn = function(i){} 302 | frmV2ray.btnTcping.disabledText = null; 303 | 304 | for b,v in table.eachIndex(config.proxy.outbounds){ 305 | frmV2ray.listview.setItemText("",b,8); 306 | } 307 | activeOutbound(currentIdx,frmV2ray.listview.getItemText(currentIdx,2),false) 308 | } 309 | }); 310 | 311 | popmenu.add('允许被自动选择为活动服务器',function(id){ 312 | if(currentIdx){ 313 | if(config.proxy.outbounds[currentIdx].autoConnect === false){ 314 | config.proxy.outbounds[currentIdx].autoConnect = null; 315 | } 316 | else { 317 | config.proxy.outbounds[currentIdx].autoConnect = false; 318 | } 319 | 320 | } 321 | }); 322 | popmenu.check(2,config.proxy.outbounds[currentIdx].autoConnect!==false); 323 | 324 | popmenu.add('复制服务器链接',function(id){ 325 | if(currentIdx){ 326 | import v2ray.config; 327 | import win.clip; 328 | 329 | var str = v2ray.config.exportToString({config.proxy.outbounds[currentIdx]}); 330 | if(str){ 331 | win.clip.write(str); 332 | frmV2ray.edit.print("已复制链接:",str); 333 | } 334 | } 335 | }); 336 | popmenu.add(); 337 | popmenu.add('删除服务器',function(id){ 338 | if(currentIdx){ 339 | frmV2ray.listview.delItem(currentIdx); 340 | ..table.remove(config.proxy.outbounds,currentIdx); 341 | publish("config.proxy.outbounds.config.changed"); 342 | } 343 | }); 344 | popmenu.popup(x,y,true); 345 | } 346 | } 347 | } 348 | 349 | subscribe("config.proxy.outbounds.changed",function(){ 350 | config.proxy.outbounds.fields = {"protocol";"address";"port";"security";"network";"ps";} 351 | frmV2ray.listview.setTable(config.proxy.outbounds); 352 | 353 | frmV2ray.btnTcping.oncommand(); 354 | } ) 355 | 356 | import process; 357 | process.kill("v2ray.exe"); 358 | if(#config.proxy.outbounds){ 359 | frmV2ray.setTimeout( 360 | function(){ 361 | publish("config.proxy.outbounds.changed"); 362 | },1000 363 | ); 364 | } 365 | 366 | subscribe("config.proxy.mode.changed",function(...){ 367 | frmV2ray.radioProxyPac.checked = false; 368 | frmV2ray.radioProxy.checked = false; 369 | frmV2ray.radioProxyDirect.checked = false; 370 | 371 | if(config.proxy.mode = "pac"){ 372 | frmV2ray.radioProxyPac.checked = true; 373 | } 374 | elseif(config.proxy.mode = "proxy"){ 375 | frmV2ray.radioProxy.checked = true; 376 | } 377 | else{ 378 | frmV2ray.radioProxyDirect.checked = true; 379 | } 380 | } ) 381 | publish("config.proxy.mode.changed"); 382 | 383 | var updatProxySetting = function(){ 384 | if(frmV2ray.radioProxyPac.checked){ 385 | config.proxy.mode = "pac"; 386 | inet.conn.setProxyAutoConfig(,v2ray.core.getPacUrl() ) 387 | } 388 | elseif(frmV2ray.radioProxy.checked){ 389 | config.proxy.mode = "proxy"; 390 | inet.conn.setProxy(,"SOCKS=127.0.0.1:"+v2ray.core.getPort()); 391 | } 392 | else { 393 | config.proxy.mode = "direct"; 394 | inet.conn.setProxy(); 395 | } 396 | 397 | config.proxy.save(); 398 | } 399 | frmV2ray.radioProxyPac.oncommand = updatProxySetting; 400 | frmV2ray.radioProxy.oncommand = updatProxySetting; 401 | frmV2ray.radioProxyDirect.oncommand = updatProxySetting; 402 | 403 | import win.dlg.message 404 | win.dlg.message.install() 405 | frmV2ray.btnEditPac.skin(style.plainButton) 406 | frmV2ray.btnEditPac.oncommand = function(id,event){ 407 | if(frmPac && win.isWindow(frmPac.hwnd)){ 408 | if( isIconic(frmPac.hwnd) ) win.show(frmPac.hwnd,9/*_SW_RESTORE*/ ); 409 | if( !isVisible(frmPac.hwnd) ) win.show(frmPac.hwnd,0x1/*_SW_NORMAL*/ ); 410 | win.setForeground(frmPac.hwnd) 411 | return; 412 | } 413 | 414 | frmPac = frmV2ray.loadForm("\forms\main\pac.aardio"); 415 | frmPac.show(); 416 | } 417 | 418 | frmV2ray.btnImportServerFromClipBd.skin(style.plainButton) 419 | frmV2ray.btnImportServerFromClipBd.oncommand = function(id,event){ 420 | var outbounds = v2ray.config.importFromClip(); 421 | if(#outbounds){ 422 | ..table.append(config.proxy.outbounds,outbounds); 423 | publish("config.proxy.outbounds.changed"); 424 | publish("config.proxy.outbounds.config.changed"); 425 | frmV2ray.msgOk("已成功导入" + #outbounds + "个服务器"); 426 | return; 427 | } 428 | 429 | frmV2ray.msgFrown("未导入服务器, 请先复制 vmess://、ss:// 链接、或订阅源!") 430 | } 431 | 432 | var testTimerId; 433 | frmV2ray.chkAutoTest.oncommand = function(id,event){ 434 | if(testTimerId){ 435 | frmV2ray.clearInterval(testTimerId); 436 | } 437 | 438 | config.proxy.test = frmV2ray.chkAutoTest.checked; 439 | if(!config.proxy.test) return; 440 | 441 | testTimerId = frmV2ray.setInterval( 442 | 15000,function(){ 443 | if( frmV2ray.autoTesting ) return; 444 | frmV2ray.autoTesting = true; 445 | 446 | activeOutbound(,,true); 447 | } 448 | ) 449 | } 450 | 451 | if(config.proxy.test){ 452 | frmV2ray.chkAutoTest.checked = true; 453 | frmV2ray.chkAutoTest.oncommand(); 454 | } 455 | 456 | frmV2ray.chkAutoTest.skin(style.checkBox); 457 | 458 | frmV2ray.enableDpiScaling(); 459 | return win.loopMessage(); -------------------------------------------------------------------------------- /lib/config.aardio: -------------------------------------------------------------------------------- 1 | //config 配置文件 2 | import v2ray.config; 3 | import fsys.config; 4 | config = fsys.config( io.appData("/win2ray-v2/") ); 5 | 6 | namespace config { 7 | __appName = "win2ray"; 8 | __loadDefaultOutbounds = function(){ 9 | var serverData = ..string.load("/v2ray-core/win2ray-default-servers.json") 10 | : $"/v2ray-core/win2ray-default-servers.json" 11 | 12 | proxy.outbounds = ..v2ray.config.importFromString(..string.removeBom(serverData)); 13 | 14 | ..publish("config.proxy.outbounds.config.changed",) 15 | } 16 | 17 | if(!proxy.outbounds){ 18 | __loadDefaultOutbounds(); 19 | } 20 | } 21 | 22 | if(!config.proxy.mode) config.proxy.mode = "pac"; 23 | 24 | if(!config.core.default){ 25 | config.core.default = { 26 | inbounds={ 27 | { 28 | listen="127.0.0.1"; 29 | port=0; 30 | protocol="socks"; 31 | settings={ 32 | auth="noauth"; 33 | udp=true; 34 | }; 35 | sniffing={ 36 | destOverride={ 37 | "http"; 38 | "tls" 39 | }; 40 | enabled=true 41 | }; 42 | tag="proxy"; 43 | }; 44 | }; 45 | log={ 46 | access=""; 47 | error=""; 48 | loglevel="warning" 49 | }; 50 | outbounds={ 51 | { 52 | tag="proxy" 53 | }; 54 | { 55 | protocol="freedom"; 56 | settings={}; 57 | tag="direct"; 58 | }; 59 | { 60 | protocol="blackhole"; 61 | settings={ 62 | response={ 63 | type="http" 64 | }; 65 | }; 66 | tag="block"; 67 | } 68 | }; 69 | routing={ 70 | domainStrategy="IPIfNonMatch"; 71 | rules={ 72 | { 73 | inboundTag={ 74 | "api" 75 | }; 76 | outboundTag="api"; 77 | type="field"; 78 | } 79 | } 80 | }; 81 | } 82 | } 83 | 84 | 85 | /**intellisense(config) 86 | __appName = 应用程序名 87 | ? = 配置文件名,\n读写配置并序列化为一个表对象,\n表的成员值可以是支持序列化的普通变量,支持table对象\n配置文件在首次使用时自动加载,退出程序时自动保存\n!fsys_table. 88 | end intellisense**/ -------------------------------------------------------------------------------- /lib/style.aardio: -------------------------------------------------------------------------------- 1 | //style 外观样式 2 | 3 | namespace style{ 4 | primaryButton = { 5 | background={ 6 | default=0xFF8FB2B0; 7 | hover=0xFF928BB3; 8 | disabled=0xFFCCCCCC; 9 | } 10 | }; 11 | button = { 12 | background={ 13 | default=0x668FB2B0; 14 | hover=0xFF928BB3; 15 | disabled=0xFFCCCCCC; 16 | } 17 | }; 18 | transButton = { 19 | background={ 20 | default=0; 21 | hover=0xFF928BB3; 22 | disabled=0xFFCCCCCC; 23 | } 24 | }; 25 | checkBox = { 26 | color = { 27 | hover = 0xFFFF0000; 28 | active = 0xFF00FF00; 29 | disabled = 0xEE666666; 30 | } 31 | checked = { 32 | color = { 33 | hover = 0xFFFF0000; 34 | active = 0xFF00FF00; 35 | disabled = 0xEE666666; 36 | } 37 | iconText = '\uF14a'/*_FA_CHECK_SQUARE*/ 38 | } 39 | }; 40 | radio ={ 41 | group = "PinyinMix"; 42 | color = { 43 | hover = 0xFFFF0000; 44 | active = 0xFF00FF00; 45 | } 46 | checked = { 47 | iconText = '\uF058'/*_FA_CHECK_CIRCLE*/ 48 | } 49 | }; 50 | link = { 51 | color = { 52 | default = 0xFF000080; 53 | hover = 0xFFFF0000; 54 | active = 0xFF00FF00; 55 | } 56 | }; 57 | plainButton = { 58 | color = { 59 | default = 0xFF3C3C3C; 60 | hover = 0xFFFF0000; 61 | active = 0xFF00FF00; 62 | disabled = 0xFFCCCCCC; 63 | } 64 | }; 65 | key = { 66 | foreground={ 67 | default = 0x00FFFFFF; 68 | hover= 0xFF8ADBAF; 69 | }; 70 | }; 71 | dropdown = { 72 | background={ 73 | default=0xFF68CC95; 74 | disabled=0xFFC4CCC8; 75 | hover=0xFF4A522F; 76 | }; 77 | color={ 78 | default=0xFF000000; 79 | disabled=0xFF8A8A8A; 80 | hover=0xFFFFFFFF 81 | }; 82 | checked = { 83 | foreground={ 84 | default = 0xFFDB8A8E; 85 | hover= 0xFF8ADBAF; 86 | }; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/v2ray/config.aardio: -------------------------------------------------------------------------------- 1 |  2 | namespace v2ray.config; 3 | 4 | setOutbound = function(outbound){ 5 | import config; 6 | import web.json; 7 | import v2ray.core; 8 | 9 | var current = ..table.clone(config.core.default); 10 | if( ! current.inbounds[1].port ){ 11 | current.inbounds[1].port = ..v2ray.core.getPort(); 12 | } 13 | else{ 14 | ..v2ray.core.setPort(current.inbounds[1].port); 15 | } 16 | 17 | if(outbound.protocol=="vmess"){ 18 | if(!#outbound.security) outbound.security = "auto"; 19 | 20 | var ob = { 21 | mux={ 22 | concurrency=8; 23 | enabled=true 24 | }; 25 | protocol="vmess"; 26 | settings={ 27 | vnext={ 28 | { 29 | address=outbound.address; 30 | level=outbound.level : null; 31 | port=tonumber(outbound.port); 32 | users={ 33 | { 34 | alterId=outbound.alterId : 0; 35 | email=outbound.email : "t@t.tt"; 36 | id=outbound.id; 37 | security=outbound.security; 38 | } 39 | } 40 | } 41 | }; 42 | }; 43 | streamSettings={ 44 | network=outbound.network : "tcp"; 45 | security=#outbound.tls ? outbound.tls : null; 46 | tlsSettings = #outbound.tls ? { 47 | allowInsecure = outbound.tlsAllowInsecure; 48 | serverName = outbound.tlsServerName; 49 | } : null 50 | }; 51 | tag="proxy" 52 | }; 53 | 54 | if( outbound.network == "ws" ){ 55 | ob.streamSettings.wsSettings = { 56 | path = outbound.path; 57 | headers = outbound.headers; 58 | connectionReuse = true; 59 | } 60 | 61 | if( outbound.host ) { 62 | if(!ob.streamSettings.wsSettings.headers){ 63 | ob.streamSettings.wsSettings.headers = {} 64 | } 65 | ob.streamSettings.wsSettings.headers.Host = outbound.host; 66 | } 67 | } 68 | elseif( outbound.network == "http" ){ 69 | ob.streamSettings.httpSettings = { 70 | connectionReuse = true; 71 | path = outbound.path; 72 | host = outbound.host; 73 | } 74 | } 75 | elseif( outbound.network == "tcp" ){ 76 | if( outbound.type == "http" ){ 77 | ob.streamSettings.tcpSettings = { 78 | connectionReuse = true; 79 | header = { 80 | request={ 81 | headers={ 82 | Connection={ 83 | "keep-alive" 84 | }; 85 | Host=outbound.host; 86 | Pragma="no-cache"; 87 | ["Accept-Encoding"]={ 88 | "gzip, deflate" 89 | }; 90 | ["User-Agent"]={ 91 | "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"; 92 | "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46" 93 | } 94 | }; 95 | method="GET"; 96 | path = outbound.path; 97 | version="1.1" 98 | }; 99 | type="http"; 100 | } 101 | } 102 | } 103 | } 104 | 105 | current.outbounds[1] = ob; 106 | } 107 | elseif(outbound.protocol=="shadowsocks"){ 108 | current.outbounds[1] = { 109 | mux={ 110 | concurrency=-1; 111 | enabled=false 112 | }; 113 | protocol="shadowsocks"; 114 | settings={ 115 | servers={ 116 | { 117 | address=outbound.address; 118 | level=outbound.level : 0; 119 | method=outbound.security; 120 | ota=false; 121 | password=outbound.id; 122 | port=outbound.port; 123 | } 124 | }; 125 | }; 126 | streamSettings={ 127 | network = outbound.network : "tcp"; 128 | }; 129 | tag="proxy" 130 | }; 131 | } 132 | 133 | ..thread.set("v2ray.core.config",web.json.stringify(current)) 134 | } 135 | 136 | getConfigJson = function(){ 137 | return ..thread.get("v2ray.core.config"); 138 | } 139 | 140 | exportToString = function(outbounds){ 141 | import web.json; 142 | import crypt; 143 | 144 | var outString = {}; 145 | for i,outbound in ..table.eachIndex(outbounds){ 146 | if(outbound.protocol == "vmess"){ 147 | var json = ..web.json.stringify({ 148 | v = 2; 149 | add = outbound.address; 150 | aid = outbound.alterId; 151 | id = outbound.id; 152 | port = outbound.port; 153 | net = outbound.network; 154 | type = outbound.type; 155 | path = outbound.path; 156 | host = outbound.host; 157 | tls = outbound.tls; 158 | ps = outbound.ps; 159 | }) 160 | 161 | var vmess = "vmess://" + ..crypt.encodeBin(json); 162 | ..table.push(outString,vmess); 163 | } 164 | elseif(outbound.protocol == "shadowsocks"){ 165 | var ss = "ss://" + ..crypt.encodeBin( outbound.security + ":" + outbound.id + "@" + outbound.address + ":" + outbound.port) + "#" + outbound.address + ":" + outbound.port; 166 | ..table.push(outString,ss); 167 | } 168 | } 169 | 170 | return ..string.join(outString,'\r\n'); 171 | } 172 | 173 | import crypt; 174 | import inet.http; 175 | importFromString = function(str){ 176 | import config; 177 | import web.json; 178 | import crypt; 179 | 180 | if(!str){ return; } 181 | 182 | str = ..string.trim(str); 183 | 184 | if( ..string.match(str,"%\[\]") ){ 185 | var cfg = web.json.tryParse(str); 186 | if(type(cfg)==type.table && #cfg){ 187 | return cfg; 188 | } 189 | } 190 | 191 | var subscribeUrl; 192 | if( ..string.startWith(str,"http://") || ..string.startWith(str,"https://") ){ 193 | 194 | import win.dlg.message; 195 | var msgDlg = win.dlg.message(..mainForm); 196 | var form = msgDlg.create('正在获取订阅源 ... ',,true) 197 | form.progress.startProgress(50); 198 | 199 | subscribeUrl = str; 200 | str = ..win.invoke( 201 | function(subscribeUrl){ 202 | import inet.http; 203 | return inet.http().get(subscribeUrl); 204 | },subscribeUrl 205 | ) 206 | 207 | form.close(); 208 | if(!str){ 209 | return; 210 | } 211 | } 212 | 213 | if(..string.match(str,"^[\w=+]+$")){ 214 | str = ..crypt.decodeBin(str); 215 | if(!str) return; 216 | } 217 | 218 | var outbounds = ..table.array(); 219 | for(line in ..string.lines(str) ){ 220 | if(line[1]=='/'# && line[2]=='/'#){ 221 | continue; 222 | } 223 | 224 | var vmess = ..string.match(line,`vmess\:\/\/([\w=+]+)`); 225 | if( vmess ) { 226 | var json = ..crypt.decodeBin(vmess); 227 | var outbound = ..web.json.tryParse(json); 228 | if(outbound){ 229 | ..table.clear(outbound@._defined); 230 | outbound.protocol = "vmess"; 231 | outbound.address = outbound.add; 232 | outbound.alterId = outbound.aid; 233 | outbound.security = "auto"; 234 | outbound.network = outbound.net : "tcp"; 235 | outbound.net = null; 236 | outbound.add = null; 237 | outbound.aid = null; 238 | outbound.v = null; 239 | outbound.subscribeUrl = subscribeUrl; 240 | ..table.push(outbounds,outbound); 241 | } 242 | 243 | continue; 244 | } 245 | 246 | var ss = ..string.match(line,`ss\:\/\/([\w=+]+)`); 247 | if( ss ) { 248 | var ss = ..crypt.decodeBin(ss); 249 | var method,password,address,port = ..string.match(ss,"^(.+)\:(.+)\@(.+)\:(\d+)") 250 | 251 | if(address && password && port){ 252 | var outbound = { 253 | address = address; 254 | port = tonumber(port); 255 | security = method; 256 | id = password; 257 | network = "tcp"; 258 | protocol = "shadowsocks" 259 | }; 260 | 261 | outbound.subscribeUrl = subscribeUrl; 262 | ..table.push(outbounds,outbound); 263 | } 264 | 265 | continue; 266 | } 267 | 268 | var address,port,id,network,path,tls = ..string.match(line,"(<\w+\.>+\w+)\s+(\d+)\s+([\w-]+)\s+(\a+)\s+(\S+)\s+(\a+)") 269 | if(!address) address,port,id,network,path = ..string.match(line,"(<\w+\.>+\w+)\s+(\d+)\s+(\S+)\s+(\a+)\s+(\S+)") 270 | if(!address) address,port,id,network = ..string.match(line,"(<\w+\.>+\w+)\s+(\d+)\s+(\S+)\s+(\a+)") 271 | 272 | if(address && port && id){ 273 | var outbound = { 274 | address = address; 275 | port = tonumber(port); 276 | id = id; 277 | network = network; 278 | path = path; 279 | tls = tls; 280 | security= "auto"; 281 | protocol = "vmess" 282 | } 283 | 284 | if(path){ 285 | var t,h,p = ..string.match(path,"(\w*)\:\/\/([^/]+)(.+)"); 286 | if(t&&h){ 287 | if(t!=network) outbound.type = t; 288 | outbound.host = h; 289 | outbound.path = p; 290 | } 291 | } 292 | 293 | if(outbound.network=="shadowsocks"){ 294 | outbound.network = "tcp"; 295 | outbound.protocol = "shadowsocks"; 296 | outbound.security = outbound.path; 297 | outbound.path = null; 298 | } 299 | 300 | outbound.subscribeUrl = subscribeUrl; 301 | ..table.push(outbounds,outbound) 302 | } 303 | } 304 | 305 | if(#outbounds){ 306 | 307 | } 308 | return outbounds; 309 | } 310 | 311 | importFromClip = function(){ 312 | import win.clip; 313 | var str = ..win.clip.read(); 314 | if(!str){ return; } 315 | 316 | return importFromString(str); 317 | } -------------------------------------------------------------------------------- /lib/v2ray/core.aardio: -------------------------------------------------------------------------------- 1 | import inet.http; 2 | import zlib.httpFile; 3 | import process.popen; 4 | import wsock.tcp.simpleHttpServer; 5 | import v2ray.config; 6 | import inet.conn; 7 | import config; 8 | import wsock; 9 | 10 | namespace v2ray.core; 11 | 12 | var getV2rayCoreUrl = function(){ 13 | var http = ..inet.http(); 14 | var url = http.location("https://github.com/v2ray/v2ray-core/releases/latest"); 15 | http.close(); 16 | 17 | if(!url)return; 18 | 19 | var tag = ..string.match(url,"[^/]+$") 20 | if(!tag)return; 21 | 22 | return "https://github.com/v2ray/v2ray-core/releases/download/" 23 | + tag + "/v2ray-windows-" + (..process().isWow64() ? "64" : "32") + ".zip" 24 | } 25 | 26 | getPath = function(hwnd){ 27 | var path = ..io.fullpath("/v2ray-core/v2ray.exe"); 28 | if(..io.exist(path)){ 29 | return path; 30 | } 31 | 32 | var path = ..io.appData("/win2ray-v2/core/v2ray.exe"); 33 | if(..io.exist(path)){ 34 | return path; 35 | } 36 | 37 | var url = getV2rayCoreUrl(); 38 | if(!url) return null; 39 | 40 | if( ..zlib.httpFile.download(url,"正在下载 V2ray Core" 41 | ,..io.appData("/win2ray-v2/download/") 42 | ,..io.appData("/win2ray-v2/core/"),,,hwnd) ) 43 | return ..io.exist(path); 44 | } 45 | 46 | var serverMain = ..wsock.tcp.simpleHttpServer.mainThread( 47 | function(response,request,session){ 48 | if(request.path=="/proxy.pac"){ 49 | var pacData = ..string.replace(..string.load(pacPath) 50 | ,"var\s+proxy\s+=\s+%''","var proxy = 'SOCKS 127.0.0.1:"+inboundPort.get()+";'"); 51 | response.contentType = "application/x-ns-proxy-autoconfig"; 52 | response.write(pacData); 53 | } 54 | elseif(request.path=="/config.json"){ 55 | import v2ray.config; 56 | response.contentType = "text/json"; 57 | response.write( v2ray.config.get() ); 58 | } 59 | } 60 | ); 61 | 62 | var pacPath = ..io.appData("/win2ray-v2/proxy.pac.txt"); 63 | if( ! ..io.exist(pacPath) ){ 64 | ..string.save(pacPath,$"/pac.txt" ) 65 | } 66 | 67 | var inboundPort = ..thread.var(); 68 | inboundPort.set( ..wsock.getFreePort() ); 69 | serverMain.threadGlobal = { 70 | inboundPort = inboundPort; 71 | pacPath = pacPath; 72 | } 73 | 74 | getPort = function(){ 75 | return inboundPort.get(); 76 | } 77 | 78 | setPort = function(port){ 79 | inboundPort.set(port); 80 | } 81 | 82 | serverMain.threadNum = 50; 83 | getPacUrl = function(){ 84 | return serverMain.getUrl("/proxy.pac?" + ..time.tick() ); 85 | } 86 | 87 | getConfigUrl = function(){ 88 | return serverMain.getUrl("/config.json"); 89 | } 90 | serverMain.start("127.0.0.1"); 91 | 92 | var prcsv2ray; 93 | restart = function(editor,outbound){ 94 | if(prcsv2ray){ 95 | prcsv2ray.terminate(); 96 | prcsv2ray = null; 97 | } 98 | 99 | var corePath = getPath(editor.hwnd); 100 | if(!corePath) return false,"启动失败,未找到v2ray.exe"; 101 | 102 | ..inet.conn.setProxy(); 103 | ..v2ray.config.setOutbound(outbound); 104 | 105 | var jsonPath = ..io.joinpath(..io.splitpath(corePath).dir,"config.json"); 106 | ..string.save(jsonPath,..v2ray.config.getConfigJson() ) 107 | 108 | prcsv2ray = ..process.popen(corePath,"-format=json","-config=config.json" ); 109 | prcsv2ray.logResponse(editor); 110 | 111 | if(..config.proxy.mode === "pac"){ 112 | ..inet.conn.setProxy(,"SOCKS=127.0.0.1:" + inboundPort.get()); 113 | ..inet.conn.setProxyAutoConfig(,getPacUrl()); 114 | } 115 | elseif(..config.proxy.mode === "proxy"){ 116 | ..inet.conn.setProxyAutoConfig(,getPacUrl()) 117 | 118 | 119 | ..inet.conn.setProxy(,"SOCKS=127.0.0.1:" + inboundPort.get()); 120 | } 121 | else { 122 | ..inet.conn.setProxy(); 123 | } 124 | return true; 125 | } 126 | 127 | stop = function(){ 128 | if(prcsv2ray){ 129 | prcsv2ray.terminate(); 130 | prcsv2ray = null; 131 | } 132 | 133 | if(serverMain){ 134 | serverMain.stop(); 135 | serverMain = null; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /main.aardio: -------------------------------------------------------------------------------- 1 | if(_ARGV.github){ 2 | loadcodex("\forms\main\tools\github.aardio"); 3 | return; 4 | } 5 | elseif(_ARGV.updateTime){ 6 | import time.ntp; 7 | time.ntp.updateSystemTime(); 8 | } 9 | 10 | import fonts.fontAwesome; 11 | import win.ui.atom; 12 | import win.ui; 13 | /*DSG{{*/ 14 | mainForm = win.form(text="win2ray";right=1019;bottom=679;bgcolor=15793151;border="none") 15 | mainForm.add( 16 | caption={cls="bkplus";text="win2ray - 本程序使用 aardio 编写,仅用于技术研究,请勿用于任何不当用途";left=67;top=9;right=727;bottom=27;align="left";color=6052956;dl=1;dt=1;font=LOGFONT(h=-14);z=7}; 17 | custom={cls="custom";left=83;top=40;right=1022;bottom=679;bgcolor=16777215;db=1;dl=1;dr=1;dt=1;z=4}; 18 | logo={cls="bkplus";text='\uF1D9';left=35;top=7;right=64;bottom=32;color=3054125;dl=1;dt=1;font=LOGFONT(h=-18;name='FontAwesome');z=6}; 19 | navBar={cls="bkplus";left=0;top=37;right=83;bottom=681;bgcolor=3442175;db=1;dl=1;dt=1;z=1}; 20 | navJsonConfig={cls="plus";text="配置";left=0;top=132;right=85;bottom=212;bkBottom=3;bkLeft=7;bkRight=8;bkTop=2;border={color=-65536};color=16777215;dl=1;dt=1;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-37;name='FontAwesome');padding={bottom=20}};iconText='\uF0F6';notify=1;textPadding={bottom=10};valign="bottom";x=0.5;y=0.2;z=5}; 21 | navStart={cls="plus";text="首页";left=0;top=48;right=85;bottom=128;bkBottom=3;bkLeft=7;bkRight=8;bkTop=2;border={color=-65536};color=16777215;dl=1;dt=1;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-37;name='FontAwesome');padding={bottom=20}};iconText='\uF015';notify=1;textPadding={bottom=10};valign="bottom";x=0.5;y=0.2;z=3}; 22 | navTools={cls="plus";text="工具";left=0;top=216;right=85;bottom=296;bkBottom=3;bkLeft=7;bkRight=8;bkTop=2;border={color=-65536};color=16777215;dl=1;dt=1;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-37;name='FontAwesome');padding={bottom=20}};iconText='\uF0AD';notify=1;textPadding={bottom=10};valign="bottom";x=0.5;y=0.2;z=8}; 23 | titleBar={cls="bkplus";left=0;top=0;right=1022;bottom=38;bgcolor=8036607;dl=1;dr=1;dt=1;forecolor=16777215;linearGradient=180;z=2} 24 | ) 25 | /*}}*/ 26 | 27 | var atom,hwnd = mainForm.atom("DFA5667E-1D8C-49C0-8918-C6FEC2DECCF8"); 28 | if(!atom){ 29 | win.showForeground(hwnd); 30 | win.quitMessage(); 31 | return; 32 | } 33 | 34 | import v2ray.core; 35 | if(!v2ray.core.getPath()) { 36 | mainForm.close(); 37 | return; 38 | } 39 | 40 | import win.ui.simpleWindow; 41 | win.ui.simpleWindow( mainForm ); 42 | 43 | import win.dlg.message; 44 | win.dlg.message.install(); 45 | 46 | import win.ui.tabs; 47 | var tbs = win.ui.tabs( 48 | mainForm.navStart, 49 | mainForm.navJsonConfig, 50 | mainForm.navTools 51 | ); 52 | 53 | tbs.skin({ 54 | background={ 55 | active=0xFFFFFFFF; 56 | default=0x00FFFFFF; 57 | hover=0x38FFFFFF 58 | }; 59 | color={ 60 | default=0xFFFFFFFF; 61 | }; 62 | checked={ 63 | background={default=0xFFFFFFFF;}; 64 | color={default=0xff86a543;}; 65 | } 66 | }) 67 | 68 | tbs.loadForm(1,"\forms\main\v2ray.aardio" ); 69 | tbs.loadForm(2,"\forms\main\json.aardio" ); 70 | tbs.loadForm(3,"\forms\main\tools\tools.aardio"); 71 | 72 | tbs.selIndex = 1; 73 | 74 | import win.util.tray; 75 | mainForm.onMinimize = function(lParam){ 76 | mainForm.tray = win.util.tray(mainForm) 77 | mainForm.show(false); 78 | return true; 79 | } 80 | 81 | import inet.conn; 82 | mainForm.onDestroy = function(){ 83 | mainForm.tray.delete(); 84 | inet.conn.setProxy(); 85 | } 86 | 87 | mainForm.onClose = function(hwnd,message,wParam,lParam){ 88 | mainForm.onMinimize(); 89 | return true; 90 | } 91 | 92 | import win.ui.menu; 93 | mainForm.wndproc = { 94 | [0xACCF/*_WM_TRAYMESSAGE*/ ] = function(hwnd,message,wParam,lParam){ 95 | if( lParam = 0x205/*_WM_RBUTTONUP*/ ){ 96 | win.setForeground(mainForm.hwnd) 97 | mainForm.popmenu = win.ui.popmenu(mainForm); 98 | mainForm.popmenu.add('自动切换到最快的服务器',function(id){ 99 | publish("config.proxy.outbounds.changed",) 100 | }); 101 | mainForm.popmenu.add(); 102 | 103 | import inet.conn; 104 | import v2ray.core; 105 | var id = mainForm.popmenu.add('使用PAC自动代理',function(id){ 106 | config.proxy.mode = "pac"; 107 | inet.conn.setProxyAutoConfig(,v2ray.core.getPacUrl() ); 108 | publish("config.proxy.mode.changed"); 109 | }); 110 | mainForm.popmenu.check(id,config.proxy.mode=="pac",0/*_MF_BYCOMMAND*/); 111 | 112 | var id = mainForm.popmenu.add('使用全局代理',function(id){ 113 | config.proxy.mode = "proxy"; 114 | inet.conn.setProxy(,"SOCKS=127.0.0.1:"+v2ray.core.getPort()); 115 | publish("config.proxy.mode.changed"); 116 | }); 117 | mainForm.popmenu.check(id,config.proxy.mode=="proxy",0/*_MF_BYCOMMAND*/); 118 | 119 | var id = mainForm.popmenu.add('不使用代理',function(id){ 120 | config.proxy.mode = "direct"; 121 | inet.conn.setProxy(); 122 | publish("config.proxy.mode.changed"); 123 | }); 124 | mainForm.popmenu.check(id,config.proxy.mode=="direct",0/*_MF_BYCOMMAND*/); 125 | mainForm.popmenu.add(); 126 | 127 | mainForm.popmenu.add('退出',function(id){ 128 | mainForm.onClose = null; 129 | mainForm.close() 130 | }); 131 | 132 | var pt = ::POINT(); 133 | ::User32.GetCursorPos(pt); 134 | mainForm.popmenu.popup(pt.x,pt.y,true) 135 | } 136 | elseif( lParam = 0x203/*_WM_LBUTTONDBLCLK*/) { 137 | mainForm.show(); 138 | win.setForeground(mainForm.hwnd); 139 | } 140 | } 141 | } 142 | 143 | mainForm.enableDpiScaling(); 144 | 145 | mainForm.show(); 146 | return win.loopMessage(); 147 | -------------------------------------------------------------------------------- /screenshots/config.json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuuniang/win2ray/fb32f0e8f71c7d353cbb8c721b6a022f4ef350f3/screenshots/config.json.png -------------------------------------------------------------------------------- /screenshots/pac.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuuniang/win2ray/fb32f0e8f71c7d353cbb8c721b6a022f4ef350f3/screenshots/pac.jpg -------------------------------------------------------------------------------- /screenshots/win2ray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuuniang/win2ray/fb32f0e8f71c7d353cbb8c721b6a022f4ef350f3/screenshots/win2ray.png -------------------------------------------------------------------------------- /v2ray-core/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2019 V2Ray 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /v2ray-core/v2ctl.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuuniang/win2ray/fb32f0e8f71c7d353cbb8c721b6a022f4ef350f3/v2ray-core/v2ctl.exe -------------------------------------------------------------------------------- /v2ray-core/v2ray.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuuniang/win2ray/fb32f0e8f71c7d353cbb8c721b6a022f4ef350f3/v2ray-core/v2ray.exe -------------------------------------------------------------------------------- /v2ray-core/win2ray-default-servers.json: -------------------------------------------------------------------------------- 1 | // 号开头为注释行。 2 | // 可以文本格式指定服务器数组,每行一个服务器配置, 3 | // vmess 服务器每行字段依次为 address port id network path tls 4 | // ss 服务器每行字段依次为 address port password shadowsocks method 5 | // 也可以每行一个vmess://或ss://链接 6 | // 本文件可以直接输入json格式的服务器数组( 服务器可以使用subscribeUrl字段指定订阅源地址,可以指定"autoConnect":false字段禁止自动连接 ) 7 | [ 8 | { 9 | "address": "c26s3.jamjams.net", 10 | "id": "0a021043-fb2e-4ecd-8d17-e62922b3aca7", 11 | "network": "tcp", 12 | "port": 50201, 13 | "protocol": "vmess", 14 | "ps": "CN2 GIA 直连", 15 | "security": "auto" 16 | }, 17 | { 18 | "address": "c26s4.jamjams.net", 19 | "id": "0a021043-fb2e-4ecd-8d17-e62922b3aca7", 20 | "network": "tcp", 21 | "port": 50201, 22 | "protocol": "vmess", 23 | "ps": "CN2 GT 直连", 24 | "security": "auto" 25 | }, 26 | { 27 | "address": "c26s5.jamjams.net", 28 | "id": "0a021043-fb2e-4ecd-8d17-e62922b3aca7", 29 | "network": "tcp", 30 | "port": 50201, 31 | "protocol": "vmess", 32 | "ps": "CN2 GT 直连", 33 | "security": "auto" 34 | }, 35 | { 36 | "address": "c26s801.jamjams.net", 37 | "id": "0a021043-fb2e-4ecd-8d17-e62922b3aca7", 38 | "network": "tcp", 39 | "port": 50201, 40 | "protocol": "vmess", 41 | "ps": "ChinaNet 直连丨0.1x", 42 | "security": "auto" 43 | }, 44 | { 45 | "address": "c26s1.jamjams.net", 46 | "id": "yjxz4ZBs8B", 47 | "network": "tcp", 48 | "port": 50201, 49 | "protocol": "shadowsocks", 50 | "ps": "CN2 GT 直连", 51 | "security": "aes-256-gcm" 52 | }, 53 | { 54 | "address": "c26s2.jamjams.net", 55 | "id": "yjxz4ZBs8B", 56 | "network": "tcp", 57 | "port": 50201, 58 | "protocol": "shadowsocks", 59 | "ps": "CN2 GT 直连", 60 | "security": "aes-256-gcm" 61 | } 62 | ] -------------------------------------------------------------------------------- /win2ray.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuuniang/win2ray/fb32f0e8f71c7d353cbb8c721b6a022f4ef350f3/win2ray.exe --------------------------------------------------------------------------------