├── .build ├── default.Manifest.xml ├── default.init.aardio ├── default.main.aardio └── update-maker.table ├── .gitignore ├── LICENSE ├── README.md ├── app.ico ├── default.aproj ├── forms └── main │ ├── config │ ├── advancedConfig.aardio │ ├── jsonConfig.aardio │ └── subscription.aardio │ ├── json.aardio │ ├── pac.aardio │ ├── tools │ ├── github.aardio │ ├── qr.aardio │ ├── tools.aardio │ ├── uuid.aardio │ └── youtube.aardio │ └── xray.aardio ├── lib ├── config.aardio ├── dnsApi.aardio ├── style.aardio ├── sysProxy.aardio └── xray │ ├── core │ ├── _.aardio │ └── configJson.aardio │ ├── github.aardio │ ├── outbounds.aardio │ └── pacServer.aardio ├── main.aardio ├── pac.txt ├── release ├── update │ ├── checksum.lzma │ ├── files │ │ ├── winXray.exe.lzma │ │ └── xray-core │ │ │ ├── LICENSE.lzma │ │ │ ├── winXray-default-servers.json.lzma │ │ │ └── xray.exe.lzma │ └── version.txt ├── update32 │ ├── checksum.lzma │ ├── files │ │ ├── winXray.exe.lzma │ │ └── xray-core │ │ │ ├── LICENSE.lzma │ │ │ ├── winXray-default-servers.json.lzma │ │ │ └── xray.exe.lzma │ └── version.txt ├── winXray.7z └── winXray32.7z ├── screenshots ├── config.advanced.png ├── config.json.png ├── pac.png ├── telegram.gif └── winXray.png ├── sub ├── introduce.md └── sample.json └── xray-core ├── LICENSE ├── winXray-default-servers.json └── xray.exe /.build/default.Manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | winXray 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; 3 | io.remove("\..\xray-core\config.json") 4 | io.remove("\..\winXray\xray-core\config.json") 5 | -------------------------------------------------------------------------------- /.build/default.main.aardio: -------------------------------------------------------------------------------- 1 | //此触发器在生成EXE以后执行 2 | import ide; 3 | import fsys; 4 | 5 | //获取生成的EXE文件路径 6 | var publishFile = ide.getPublishPath(); -------------------------------------------------------------------------------- /.build/update-maker.table: -------------------------------------------------------------------------------- 1 | { 2 | updateUrl="https://raw.githubusercontent.com/winXray/winXray/master/release/update/"; 3 | description="发布 v2.3,修正刷新clash订阅连接重复添加服务器,改进订阅管理等。"; 4 | outputDir="/release/update/" 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /xray-core/config.json 2 | /winXray/xray-core/config.json 3 | /winXray/ 4 | /release/winXray/ 5 | /release/winXray32/ 6 | -------------------------------------------------------------------------------- /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 | [winXray 入门小技巧](./sub/introduce.md) 2 | [winXray 最新视频教程](https://www.youtube.com/results?search_query=winXray&sp=CAI%253D) 3 | [网络免费 vmess 服务器订阅链接](https://proxypool.ga/vmess/sub) 4 | [网络免费 Shadowsocks 服务器订阅链接](https://proxypool.ga/ss/sub) 5 | [网络免费 clash 服务器订阅链接](https://proxypoolss.tk/clash/proxies?speed=100&type=vmess,trojan) 6 | 可复制上面各种格式订阅链接,在 winXray 中点击「批量导入链接」一键体验 winXray 强大的兼容性。 7 | 8 | # winXray 9 | 本软件源码已贡献到公共领域并放弃版权,源码可使用 [aardio(开发环境仅6.5MB)](http://www.aardio.com) 编译生成单文件绿色EXE( 不需要.Net等任何外部运行库 ),**[点这里下载](./../../raw/master/release/winXray.7z)** ( [64位版本](./../../raw/master/release/winXray.7z) / [32位版本](./../../raw/master/release/winXray32.7z) ),解压即可直接使用( 仅 **[5.0MB](./../../raw/master/release/winXray.7z)** - 已自带 Xray-core)。 10 | 11 | winXray[:loud_sound:](http://dict.youdao.com/dictvoice?audio=winxray&type=2) 是一个简洁稳定的 [Xray/V2Ray(vmess/vless/xtls)、Shadowsocks、Trojan](https://github.com/XTLS/Xray-core) 通用客户端(Windows系统),可自动检测并连接访问速度最快的代理服务器。服务器连接异常时可以自动更换代理服务器 - 再也不用担心服务器抽风了。winXray 也提供一键安装 XRay(V2Ray、Shadowsocks、Trojan) 服务器工具。 12 | 13 | 之前我用过很多代理客户端,经常用一会就挂掉了,有些测试很久才找到下一个可用的服务器,有时怎么切换都不行,一定要把整个软件退出重启才能恢复。而且在WIN10上都有相同的BUG:PAC代理用一段时间就会卡死( winXray已经通过自行实现PAC服务器解决了这个问题 ),其实这些软件里提供的很多功能我并不需要,我只想愉快地用下 google 找点技术资料提升工作效率。但是在网上找了很久都没找到适合的软件,于是决定自己动手写一个,还好用 aardio 写软件的速度很快 - 大概用了几个小时就完成了 winXray 的主要代码,改进了几个版本以后就很稳定了,**我自己用了 winXray 几个月再也没有遇到 google 抽风访问不了的问题**。 14 | 15 | 一些网友对 winXray 的测速功能存在误解,简单的测速不但不必要的消耗流量而且没有什么实用价值,例如Ping通了TCP访问不了,TCP通了但浏览器被阻断,或者下载测速需要连接代理服务器以后长时间测试才能得到下载速度 - 如果有很多服务器需要频繁快速地测速这样显然是不行的。**根据我长时间的实测,我使用一堆的服务器,大多时候 winXray 都能最快的找到其中速度最快、且能正常使用的线路**。要考虑到网络速度与服务器响应速度本来就是波动的,**winXray 能在连接代理服务器以前在零点几秒以内快速地在上百个代理服务器中找出最快的服务器** - 可以节省我们大量手动测试切换的时间,如果你有一个比我们幸福的网络环境,有一堆稳定流畅看4K视频的代理服务器 - 那你确实可以关闭自动切换功能 - 手动切换即可。 16 | 17 | ![winXray](./screenshots/winXray.png) 18 | 19 | winXray支持批量导入 vless、vmess、ss、trojan、trojan-go …… 等格式的分享链接, 20 | 也可以导入v2ray,、Shadowsocks、trojan等通用订阅链接,兼容 base 64、json …… 等不同格式的服务器配置,也可以导入 Clash proxy-provider 配置,winXray将自动转化各种不兼容的配置为统一、规范的格式。winXray 有强大的兼容性,我们甚至可以直接自 github 项目地址导入代理服务器,例如直接复制链接 [https://github.com/winXray/winXray/blob/master/sub/sample.json](./sub/sample.json) 即可一键导入 winXray。 21 | 22 | ![服务器配置](./screenshots/config.json.png) 23 | **小技巧: JSON里点击任意字段都会显示该字段的用法说明。** 个人认为做很多对话框来配置服务器的参数是非常蠢的,winXray已经把各种代理协议的配置简化为几个统一命名的JSON字段( **也可以作为一种标准的、统一的、通用的订阅响应格式使用** ),只要稍加学习就可以非常熟练的添加、修改各种代理协议的配置。而且对于大多数用户根本不需要改配置 - 简单的复制导入分享链接就可以,我们不必要把简单的事搞复杂。 24 | 25 | 可选在 ["/xray-core/winXray-default-servers.json"](./xray-core/winXray-default-servers.json) 文件中添加默认服务器配置(生成EXE后默认配置自动嵌入到EXE文件,可选删除该文件,也可以继续使用该文件覆盖EXE自带的默认服务器列表)。 26 | 27 | ![PAC配置](./screenshots/pac.png) 28 | **小技巧: PAC编辑器里点击任意域名都会自动关联到单选框,可以直接切换代理或直连。** 29 | 30 | 软件首次运行时会在当前目录查找 "./xray-core/xray.exe" 31 | 发行文件仅需要 "./winXray.exe",可选带上 "./xray-core/" 目录( 如果没有找到会自动到v2ray官网下载,不过没有代理服务器下载有时候非常慢 )。 32 | 33 | ![端口配置](./screenshots/config.advanced.png) 34 | 35 | Telegram 端口配置 36 | -------------------------------------------------------------------------------- /app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/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 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /forms/main/config/advancedConfig.aardio: -------------------------------------------------------------------------------- 1 | import fonts.fontAwesome; 2 | import win.ui; 3 | /*DSG{{*/ 4 | var winform = win.form(text="aardio form";right=938;bottom=604;bgcolor=16777215) 5 | winform.add( 6 | btnOpenFirewall={cls="plus";text="设置防火墙";left=511;top=424;right=641;bottom=454;align="left";db=1;dl=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=12}};iconText='\uF09C';notify=1;textPadding={left=30};z=7}; 7 | btnUpdate={cls="plus";text="保 存";left=516;top=545;right=619;bottom=581;align="left";bgcolor=11580047;db=1;dl=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=5}; 8 | chkEnableGitConfigGithub={cls="plus";text="设置 git 客户端启用 github.com 代理提速(支持https、ssh协议)";left=302;top=463;right=818;bottom=494;align="left";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-16;name='FontAwesome')};iconText='\uF0C8';notify=1;textPadding={left=19};z=13}; 9 | chkLan={cls="plus";text="允许来自局域网的连接";left=302;top=423;right=509;bottom=454;align="left";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-16;name='FontAwesome')};iconText='\uF0C8';notify=1;textPadding={left=19};z=1}; 10 | chkSystemStart={cls="plus";text="允许开机启动 winXray";left=302;top=503;right=818;bottom=534;align="left";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-16;name='FontAwesome')};iconText='\uF0C8';notify=1;textPadding={left=19};z=17}; 11 | editHttpPort={cls="plus";left=304;top=163;right=384;bottom=187;align="left";bgcolor=16777215;border={bottom=1;color=-8355712};db=1;dl=1;editable="edit";font=LOGFONT(h=-16);textPadding={bottom=1};z=14}; 12 | editLanPacUrl={cls="plus";left=306;top=364;right=725;bottom=388;align="left";bgcolor=16777215;border={bottom=1;color=-8355712};db=1;dl=1;editable="edit";font=LOGFONT(h=-16);textPadding={bottom=1};z=11}; 13 | editPacPort={cls="plus";left=304;top=272;right=384;bottom=296;align="left";bgcolor=16777215;border={bottom=1;color=-8355712};db=1;dl=1;editable="edit";font=LOGFONT(h=-16);textPadding={bottom=1};z=2}; 14 | editPacUrl={cls="plus";left=304;top=319;right=723;bottom=343;align="left";bgcolor=16777215;border={bottom=1;color=-8355712};db=1;dl=1;editable="edit";font=LOGFONT(h=-16);textPadding={bottom=1};z=8}; 15 | editSocksPort={cls="plus";left=304;top=124;right=384;bottom=148;align="left";bgcolor=16777215;border={bottom=1;color=-8355712};db=1;dl=1;editable="edit";font=LOGFONT(h=-16);textPadding={bottom=1};z=3}; 16 | lbMaxTestServers={cls="static";text="100";left=590;top=93;right=926;bottom=119;bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-13);transparent=1;z=25}; 17 | lbTestInterval={cls="static";text="15秒";left=590;top=56;right=788;bottom=82;bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-13);transparent=1;z=20}; 18 | lnkVersion={cls="plus";text="正在检查新版本";left=19;top=575;right=380;bottom=606;align="left";color=8388608;db=1;dl=1;font=LOGFONT(h=-13);iconColor=2960685;iconStyle={align="left";font=LOGFONT(h=-16;name='FontAwesome')};iconText='\uF021';notify=1;paddingLeft=20;z=10}; 19 | radioHttpGlobal={cls="plus";text="全局/PAC 默认使用HTTP代理";left=246;top=206;right=491;bottom=237;align="left";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-16;name='FontAwesome')};iconText='\uF111 ';notify=1;textPadding={left=19};z=21}; 20 | radioSocksGlobal={cls="plus";text="全局/PAC 默认使用SOCKS代理";left=491;top=206;right=750;bottom=237;align="left";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-16;name='FontAwesome')};iconText='\uF111 ';notify=1;textPadding={left=19};z=22}; 21 | static={cls="static";text="SOCKS 代理服务端口:";left=94;top=128;right=292;bottom=154;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=4}; 22 | static2={cls="static";text="PAC 端口:";left=162;top=277;right=292;bottom=303;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=6}; 23 | static3={cls="static";text="本机 PAC 地址:";left=134;top=324;right=292;bottom=350;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=9}; 24 | static4={cls="static";text="局域网 PAC 地址:";left=134;top=370;right=292;bottom=396;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=12}; 25 | static5={cls="static";text="HTTP/HTTPS 代理服务端口:";left=34;top=167;right=292;bottom=193;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=15}; 26 | static6={cls="static";text="支持 SOCKS4、SOCKS4A、SOCKS5";left=392;top=128;right=679;bottom=154;bgcolor=16777215;color=5921370;db=1;dl=1;font=LOGFONT(h=-13);transparent=1;z=16}; 27 | static7={cls="static";text="检测服务器异常间隔时间:";left=47;top=50;right=292;bottom=76;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=19}; 28 | static8={cls="static";text="允许同时测速的服务器数目:";left=47;top=89;right=292;bottom=115;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=24}; 29 | static9={cls="static";text="PAC 模式默认启用 SOCKS5 或 HTTP 代理,都支持 DNS 保护。全局代理设为默认 HTTP 代理时支持 DNS保 护。";left=250;top=247;right=891;bottom=273;color=12632256;db=1;dl=1;font=LOGFONT(h=-11);transparent=1;z=26}; 30 | tbMaxTestServers={cls="plus";left=304;top=95;right=585;bottom=107;bgcolor=-2512093;border={radius=-1};color=23807;db=1;dl=1;foreRight=13;forecolor=-14911489;notify=1;paddingBottom=5;paddingTop=5;z=23}; 31 | tbTestInterval={cls="plus";left=304;top=57;right=585;bottom=69;bgcolor=-2512093;border={radius=-1};color=23807;db=1;dl=1;foreRight=13;forecolor=-14911489;notify=1;paddingBottom=5;paddingTop=5;z=18} 32 | ) 33 | /*}}*/ 34 | 35 | import style; 36 | winform.chkLan.skin(style.checkBox); 37 | winform.chkEnableGitConfigGithub.skin(style.checkBox); 38 | winform.chkSystemStart.skin(style.checkBox); 39 | winform.radioHttpGlobal.skin(style.radio) 40 | winform.radioSocksGlobal.skin(style.radio) 41 | winform.btnUpdate.skin(style.primaryButton); 42 | winform.editSocksPort.skin(style.edit); 43 | winform.editPacPort.skin(style.edit); 44 | winform.editPacUrl.editBox.readonly = true; 45 | winform.lnkVersion.skin(style.link) 46 | 47 | import win.ui.tooltip; 48 | var tooltipCtrl = win.ui.tooltip( winform ); 49 | tooltipCtrl.addTool(winform.lnkVersion,"点击检测更新" ); 50 | tooltipCtrl.addTool(winform.editSocksPort,"点击右键可以复制IP:端口" ); 51 | tooltipCtrl.addTool(winform.radioHttpGlobal,"点击右键可以复制IP:端口" ); 52 | tooltipCtrl.addTool(winform.radioHttpGlobal,"如果某些程序不支持SOCKS全局代理,可以切换到此选项。" ); 53 | tooltipCtrl.addTool(winform.radioSocksGlobal,"如果某些程序不支持SOCKS全局代理,可以切换到HTTP全局代理。" ); 54 | 55 | import xray.pacServer; 56 | onPacUpdated = function(){ 57 | winform.editPacPort.text = xray.pacServer.getPort() || config.proxy.pacPort || 1083; 58 | winform.editPacUrl.text = xray.pacServer.getUrl(); 59 | winform.editLanPacUrl.text = xray.pacServer.getUrl(true); 60 | winform.editLanPacUrl.disabled = !winform.chkLan.checked; 61 | } 62 | 63 | subscribe("pacServer.restarted",function(pacPort){ 64 | onPacUpdated() 65 | }) 66 | 67 | subscribe("sysProxy.modeChanged",function(mode){ 68 | if(mode==="mode"){ 69 | winform.editPacUrl.text = xray.pacServer.getUrl(); 70 | winform.editLanPacUrl.text = xray.pacServer.getUrl(true); 71 | } 72 | }) 73 | 74 | import xray.core; 75 | var onInboundsUpdated = function(){ 76 | var inbounds = ..config.core.default[["inbounds"]]; 77 | winform.editSocksPort.text = xray.core.socksProxyPort || inbounds[[1]][["port"]] || 1081; 78 | winform.editHttpPort.text = xray.core.httpProxyPort || inbounds[[2]][["port"]] || 1082; 79 | winform.chkLan.checked = inbounds[[1]][["listen"]] !== "127.0.0.1"; 80 | winform.btnOpenFirewall.disabled = !winform.chkLan.checked; 81 | } 82 | 83 | subscribe("config.core.changed",function(){ 84 | onInboundsUpdated(); 85 | }) 86 | 87 | subscribe("xrayCore.restarted",function(socksProxyPort,httpProxPort){ 88 | onInboundsUpdated(); 89 | }) 90 | 91 | import win.reg; 92 | var reg = win.reg("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run\"); 93 | winform.chkSystemStart.checked = reg.queryValue("winXray"); 94 | reg.close(); 95 | 96 | winform.chkEnableGitConfigGithub.checked = config.proxy.enableGitConfigGithub; 97 | winform.radioHttpGlobal.checked = config.proxy.useHttpGlobal; 98 | winform.radioSocksGlobal.checked = !config.proxy.useHttpGlobal; 99 | onInboundsUpdated(); 100 | onPacUpdated(); 101 | 102 | import wsock.tcp.client; 103 | winform.btnUpdate.oncommand = function(id,event){ 104 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}; 105 | config.proxy.useHttpGlobal = winform.radioHttpGlobal.checked; 106 | xray.pacServer.updateUseHttpProxy(config.proxy.useHttpGlobal); 107 | 108 | var client = wsock.tcp.client(); 109 | var oldPort = xray.core.socksProxyPort; 110 | if( tonumber(winform.editSocksPort.text) 111 | && tonumber(winform.editSocksPort.text) != oldPort 112 | && client.connectTimeout("127.0.0.1",tonumber(winform.editSocksPort.text) ,0.5) ){ 113 | winform.editSocksPort.editBox.showErrorTip(,"端口已被占用"); 114 | winform.btnUpdate.disabledText = null; 115 | return client.close(); 116 | } 117 | client.close(); 118 | 119 | var client = wsock.tcp.client(); 120 | var oldPacPort = xray.pacServer.getPort(); 121 | if( tonumber(winform.editPacPort.text) 122 | && tonumber(winform.editPacPort.text) != oldPacPort 123 | && client.connectTimeout("127.0.0.1",tonumber(winform.editPacPort.text) ,0.5) ){ 124 | winform.editPacPort.editBox.showErrorTip(,"PAC端口已被占用"); 125 | winform.btnUpdate.disabledText = null; 126 | return client.close(); 127 | } 128 | client.close(); 129 | 130 | 131 | if(winform.chkEnableGitConfigGithub.checked!=config.proxy.enableGitConfigGithub){ 132 | config.proxy.enableGitConfigGithub = winform.chkEnableGitConfigGithub.checked; 133 | 134 | import xray.github; 135 | if(!config.proxy.enableGitConfigGithub){ 136 | xray.github.setProxy(false); 137 | } 138 | else { 139 | xray.github.setProxy(null); 140 | } 141 | } 142 | 143 | var restartCore = false; 144 | var port = tonumber(winform.editSocksPort.text) 145 | config.core.default.inbounds[1]["port"] = port; 146 | if(port!=oldPort){ 147 | restartCore = true; 148 | } 149 | 150 | var port = tonumber(winform.editHttpPort.text) 151 | config.core.default.inbounds[2]["port"] = port; 152 | if(port!=oldPort){ 153 | restartCore = true; 154 | } 155 | 156 | var listen = winform.chkLan.checked ? "0.0.0.0" : "127.0.0.1"; 157 | if(listen!=config.core.default.inbounds[1]["listen"]){ 158 | config.core.default.inbounds[1]["listen"]= listen; 159 | restartCore = true; 160 | } 161 | 162 | if(listen!=config.core.default.inbounds[2]["listen"]){ 163 | config.core.default.inbounds[2]["listen"]= listen; 164 | restartCore = true; 165 | } 166 | 167 | config.core.save(); 168 | publish("config.inbounds.changed"); 169 | if(restartCore) { 170 | publish("uiCommand.restartXrayCore"); 171 | } 172 | 173 | config.proxy.pacPort = tonumber(winform.editPacPort.text) 174 | if(oldPacPort!=tonumber(winform.editPacPort.text)){ 175 | xray.pacServer.restart(); 176 | } 177 | 178 | config.proxy.save(); 179 | sysProxy.reset(true); 180 | 181 | import win.reg; 182 | var reg = win.reg("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run\"); 183 | if(!_STUDIO_INVOKED){ 184 | if(winform.chkSystemStart.checked){ 185 | reg.setSzValue("winXray",`"`+io._exepath+`" /tray`); 186 | } 187 | else { 188 | reg.delValue("winXray"); 189 | } 190 | } 191 | reg.close(); 192 | 193 | winform.btnUpdate.disabledText = null; 194 | winform.msgOk("配置已更新",1200); 195 | } 196 | 197 | winform.editPacUrl.editBox.readonly = true; 198 | winform.editLanPacUrl.editBox.readonly = true; 199 | 200 | import win.clip; 201 | import wsock.tcp.client; 202 | winform.editSocksPort.editBox.enablePopMenu(function(){ 203 | var lanIp = wsock.tcp.client.getLocalIp("www.baidu.com"); 204 | return { 205 | { "复制端口" + winform.editSocksPort.text; 206 | function(id){ 207 | win.clip.write("" + winform.editSocksPort.text) 208 | } 209 | }; 210 | { "复制本机IP与端口 127.0.0.1:" + winform.editSocksPort.text; 211 | function(id){ 212 | win.clip.write("127.0.0.1:" + winform.editSocksPort.text) 213 | } 214 | }; 215 | { "复制局域网IP与端口 "+lanIp+":" + winform.editSocksPort.text; 216 | function(id){ 217 | win.clip.write(lanIp+":" + winform.editSocksPort.text) 218 | } 219 | }; 220 | } 221 | } ) 222 | 223 | winform.editHttpPort.editBox.enablePopMenu(function(){ 224 | var lanIp = wsock.tcp.client.getLocalIp("www.baidu.com"); 225 | return { 226 | { "复制端口" + winform.editHttpPort.text; 227 | function(id){ 228 | win.clip.write("" + winform.editHttpPort.text) 229 | } 230 | }; 231 | { "复制本机IP与端口 127.0.0.1:" + winform.editHttpPort.text; 232 | function(id){ 233 | win.clip.write("127.0.0.1:" + winform.editHttpPort.text) 234 | } 235 | }; 236 | { "复制局域网IP与端口 "+lanIp+":" + winform.editHttpPort.text; 237 | function(id){ 238 | win.clip.write(lanIp+":" + winform.editHttpPort.text) 239 | } 240 | }; 241 | } 242 | } ) 243 | 244 | winform.editPacUrl.setCueBannerText("当前未启用本机PAC代理"); 245 | winform.editLanPacUrl.setCueBannerText("当前未启用局域网PAC代理"); 246 | 247 | winform.chkLan.oncommand = function(id,event){ 248 | winform.editLanPacUrl.disabled = !winform.chkLan.checked; 249 | winform.btnOpenFirewall.disabled = !winform.chkLan.checked; 250 | } 251 | 252 | winform.tbTestInterval.setTrackbarRange(1,30); 253 | winform.tbTestInterval.skin(style.trackbar); 254 | winform.tbTestInterval.progressPos = config.proxy.testInterval; 255 | winform.lbTestInterval.text = config.proxy.testInterval+"秒"; 256 | winform.tbTestInterval.onPosChanged = function( pos,thumbTrack ){ 257 | winform.lbTestInterval.text = pos+"秒"; 258 | config.proxy.testInterval = pos; 259 | config.proxy.save(); 260 | } 261 | 262 | winform.tbMaxTestServers.setTrackbarRange(50,1000); 263 | winform.tbMaxTestServers.skin(style.trackbar); 264 | winform.tbMaxTestServers.progressPos = config.proxy.maxTestServers; 265 | winform.lbMaxTestServers.text = config.proxy.maxTestServers+"个服务器"; 266 | winform.tbMaxTestServers.onPosChanged = function( pos,thumbTrack ){ 267 | if(pos>300){ 268 | winform.lbMaxTestServers.color = 0xFF; 269 | winform.lbMaxTestServers.text = pos+"个服务器( 数值太大可能导致卡顿 )"; 270 | } 271 | else { 272 | winform.lbMaxTestServers.color = 0; 273 | winform.lbMaxTestServers.text = pos+"个服务器"; 274 | } 275 | 276 | config.proxy.maxTestServers = pos; 277 | config.proxy.save(); 278 | } 279 | 280 | import fsys.update.simpleMain; 281 | fsys.update.simpleMain.onStatusChanged(function(version,description,status){ 282 | /*注意此回调可捕获到调用前或调用后的更新状态变更*/ 283 | if(status=="ready"){ 284 | winform.lnkVersion.disabledText = null; 285 | winform.lnkVersion.text = "点这里重新启动软件更新到新版本:" + version 286 | tooltipCtrl.addTool(winform.lnkVersion,description); 287 | } 288 | elseif(status=="complete"){ 289 | winform.lnkVersion.text = "已更新到最新版本:" + version; 290 | tooltipCtrl.addTool(winform.lnkVersion,description); 291 | ..publish("uiCommand.print",winform.lnkVersion.text ); 292 | } 293 | elseif(status=="latest"){ 294 | winform.lnkVersion.disabledText = null; 295 | winform.lnkVersion.text = "已经是最新版本"; 296 | 297 | } 298 | elseif(status=="failed"){ 299 | winform.lnkVersion.disabledText = null; 300 | ..publish("uiCommand.print",description); 301 | ..publish("uiCommand.print","建议在「工具」中运行「github优化工具」修复无法访问 raw.githubusercontent.com 的问题。"); 302 | } 303 | }) 304 | 305 | winform.lnkVersion.oncommand = function(id,event){ 306 | 307 | winform.lnkVersion.text = fsys.update.simpleMain.getReadyStatusInfo() ? "正在启动自动更新" : "正在检查新版本"; 308 | winform.lnkVersion.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 309 | if(fsys.update.simpleMain.checkUpdate(true)){ 310 | win.quitMessage(); 311 | } 312 | } 313 | 314 | winform.btnOpenFirewall.skin(style.plainButton) 315 | winform.btnOpenFirewall.oncommand = function(id,event){ 316 | import process.control; 317 | process.control("firewall.cpl") 318 | } 319 | 320 | winform.show(); 321 | win.loopMessage(); 322 | return winform; -------------------------------------------------------------------------------- /forms/main/config/jsonConfig.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 | advancedConfig={cls="\forms\main\config\advancedConfig.aardio";left=9;top=29;right=902;bottom=739;bgcolor=16777215;db=1;dl=1;dr=1;dt=1;edge=1;z=10}; 7 | btnUpdate={cls="plus";text="更新服务器配置";left=687;top=753;right=856;bottom=784;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=5}; 8 | editCoreConfig={cls="edit";left=9;top=29;right=902;bottom=739;db=1;dl=1;dr=1;dt=1;edge=1;hide=1;hscroll=1;multiline=1;vscroll=1;z=8}; 9 | editOutbounds={cls="edit";left=9;top=29;right=902;bottom=739;db=1;dl=1;dr=1;dt=1;edge=1;hscroll=1;multiline=1;vscroll=1;z=7}; 10 | 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=6}; 11 | navAdvancedConfig={cls="plus";text="代理端口 / 高级配置";left=359;top=750;right=493;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=9}; 12 | navCoreConfig={cls="plus";text="Xray配置 ( JSON )";left=162;top=750;right=293;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=2}; 13 | navOutbounds={cls="plus";text="服务器配置( JSON )";left=31;top=750;right=162;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=1}; 14 | navRight={cls="plus";left=493;top=750;right=762;bottom=751;bgcolor=16777215;border={color=-16744448};db=1;dl=1;dr=1;forecolor=32768;linearGradient=180;z=3}; 15 | navSubscription={cls="plus";text="订阅";left=293;top=750;right=359;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=12}; 16 | plus={cls="plus";left=9;top=750;right=31;bottom=751;border={color=-16744448};db=1;dl=1;forecolor=32768;z=4}; 17 | subscription={cls="\forms\main\config\subscription.aardio";left=9;top=29;right=902;bottom=739;bgcolor=16777215;db=1;dl=1;dr=1;dt=1;edge=1;z=11} 18 | ) 19 | /*}}*/ 20 | 21 | import config; 22 | import web.json; 23 | import win.ui.tabs; 24 | var tbs = win.ui.tabs(winform.navOutbounds,winform.navCoreConfig,winform.navSubscription,winform.navAdvancedConfig); 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.editOutbounds.hide = false; 46 | winform.advancedConfig.hide = true; 47 | winform.editCoreConfig.hide = true; 48 | winform.subscription.hide = true; 49 | 50 | winform.lbTip.text = `可在下面输入多行服务器分享链接,或JSON数组( 点击字段名会显示用法说明 ):`; 51 | winform.btnUpdate.text = "更新服务器配置"; 52 | winform.btnUpdate.hide = false; 53 | } 54 | elseif(idx==2) { 55 | winform.editCoreConfig.hide = false; 56 | winform.editOutbounds.hide = true; 57 | winform.advancedConfig.hide = true; 58 | winform.subscription.hide = true; 59 | 60 | winform.lbTip.text = `inbounds[0],inbounds[1]必须为入站socks、http 代理,outbounds[0] 会转换为出站服务器数组: ` 61 | winform.btnUpdate.text = "更新Xray配置 " 62 | winform.btnUpdate.hide = false; 63 | } 64 | elseif(idx==3) { 65 | 66 | winform.subscription.hide = false; 67 | winform.editOutbounds.hide = true; 68 | winform.advancedConfig.hide = true; 69 | winform.editCoreConfig.hide = true; 70 | 71 | winform.lbTip.text = `` 72 | winform.btnUpdate.hide = true; 73 | winform.subscription.loadSubscriptionsOnce(); 74 | } 75 | else { 76 | winform.advancedConfig.hide = false; 77 | winform.editOutbounds.hide = true; 78 | winform.subscription.hide = true; 79 | winform.editCoreConfig.hide = true; 80 | winform.lbTip.text = `` 81 | winform.btnUpdate.hide = true; 82 | } 83 | } 84 | tbs.selIndex = 1; 85 | 86 | import style; 87 | winform.btnUpdate.skin(style.primaryButton); 88 | winform.btnUpdate.oncommand = function(id,event){ 89 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}; 90 | 91 | if(tbs.selIndex==1){ 92 | if(..string.match(winform.editOutbounds.text,"%\[\]")){ 93 | var cfg,err = web.json.tryParse(winform.editOutbounds.text); 94 | if(!cfg){ 95 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 96 | winform.btnUpdate.disabledText = null; 97 | return; 98 | } 99 | 100 | if(cfg){ 101 | cfg = xray.outbounds.validAll(cfg) 102 | } 103 | config.proxy.outbounds = cfg : ..table.array(); 104 | } 105 | else { 106 | var outbounds = xray.outbounds.importFromString(winform.editOutbounds.text); 107 | config.proxy.outbounds = outbounds : ..table.array(); 108 | winform.editOutbounds.text = web.json.stringifyArray(outbounds,true,false,true); 109 | } 110 | 111 | config.proxy.save(); 112 | winform.editOutbounds.modified = false; 113 | publish("uiCommand.restartXrayCore",cfg) 114 | } 115 | elseif(tbs.selIndex==2) { 116 | var cfg,err = web.json.tryParse(winform.editCoreConfig.text); 117 | if(type(cfg)!==type.table){ 118 | winform.editCoreConfig.showErrorTip(,string.concat("JSON语法错误 ",err)); 119 | winform.btnUpdate.disabledText = null; 120 | return; 121 | } 122 | 123 | if(cfg.outbounds[[1]][["tag"]]!="proxy"){ 124 | winform.msgErr(`outbounds[0]必须指定为{"tag":"proxy"}`); 125 | winform.btnUpdate.disabledText = null; 126 | return; 127 | } 128 | 129 | if(type(cfg.inbounds[[1]][["listen"]])!="string"){ 130 | winform.msgErr(`inbounds[0].listen必须指定为SOCKS代理监听IP或0.0.0.0`); 131 | winform.btnUpdate.disabledText = null; 132 | return; 133 | } 134 | 135 | if(type(cfg.inbounds[[1]][["port"]])!="number"){ 136 | winform.msgErr(`inbounds[0].port必须指定为SOCKS代理监听端口`); 137 | winform.btnUpdate.disabledText = null; 138 | return; 139 | } 140 | 141 | if(type(cfg.inbounds[[2]][["listen"]])!="string"){ 142 | winform.msgErr(`inbounds[1].listen必须指定为HTTP代理监听IP或0.0.0.0`); 143 | winform.btnUpdate.disabledText = null; 144 | return; 145 | } 146 | 147 | if(type(cfg.inbounds[[2]][["port"]])!="number"){ 148 | winform.msgErr(`inbounds[1].port必须指定为HTTP代理监听端口`); 149 | winform.btnUpdate.disabledText = null; 150 | return; 151 | } 152 | 153 | config.core.default = cfg; 154 | config.core.save(); 155 | publish("uiCommand.restartXrayCore",cfg) 156 | publish("config.core.changed"); 157 | } 158 | 159 | win.delay(500); 160 | winform.btnUpdate.disabledText = null; 161 | winform.msgOk("配置已更新",1200); 162 | } 163 | 164 | 165 | import xray.outbounds; 166 | winform.editOutbounds.enablePopMenu(function(){ 167 | if(tbs.selIndex==1){ 168 | var strEditBounds = string.trim(winform.editOutbounds.text); 169 | var menu = { 170 | { /*---分隔线---*/ }; 171 | { "自剪贴板导入 vmess/vless/trojan/ss 链接或订阅源"; 172 | function(id){ 173 | 174 | var outbounds = xray.outbounds.importFromClipboard(); 175 | if(#outbounds){ 176 | var json = strEditBounds; 177 | if( (!#json) || ..string.match(json,"%\[\]")){ 178 | var cfgOutbounds,err = ..table.array(); 179 | if(#json){ 180 | cfgOutbounds,err= web.json.tryParse(strEditBounds); 181 | if(!cfgOutbounds){ 182 | winform.editOutbounds.showErrorTip(,string.concat("当前配置存在JSON语法错误 ",err)); 183 | return; 184 | } 185 | } 186 | 187 | var subscribeUrl = outbounds[1].subscribeUrl; 188 | if(subscribeUrl){ 189 | for(i=#cfgOutbounds;1;-1){ 190 | var outbound = cfgOutbounds[i] 191 | if(outbound.subscribeUrl==url){ 192 | ..table.remove( cfgOutbounds,i ); 193 | } 194 | } 195 | } 196 | 197 | ..table.append(cfgOutbounds,outbounds); 198 | winform.editOutbounds.text = ..web.json.stringifyArray(cfgOutbounds,true,false,true); 199 | } 200 | else { 201 | var str = xray.outbounds.exportSharedLinks(outbounds); 202 | if(#strEditBounds){ 203 | winform.editOutbounds.text = strEditBounds+ '\r\n' + str; 204 | } 205 | else { 206 | winform.editOutbounds.text = str; 207 | } 208 | } 209 | 210 | winform.msgOk("已成功导入" + #outbounds + "个服务器,请点击「更新设置」启用新配置。",1200); 211 | winform.editOutbounds.modified = true; 212 | winform.editOutbounds.setFocus(); 213 | return; 214 | } 215 | 216 | winform.msgFrown('未导入服务器!\r\n请先复制以下格式文本(自动清除其中的无效内容):\r\n\r\n1、一行或多行(忽略无效行)分享链接或服务器JSON配置。\r\n支持 vmess://,vless://,ss://,trojan://, trojan-go:// 等通用分享链接。\r\n\r\n2、包含多个服务器配置的JSON数组。\r\n\r\n3、单个 http:// 或 https:// 开头的通用订阅源地址。\r\n订阅源地址支持直接使用github项目文件地址(网址含raw或blob目录名)。\r\n订阅源可以BASE64编码或明文返回以上1、2条规定的配置或分享链接。') 217 | } 218 | }; 219 | } 220 | 221 | if( (!#strEditBounds) || ..string.match(strEditBounds,"%\[\]")){ 222 | menu[3] = { "更新当前配置内订阅源"; 223 | function(id){ 224 | var cfg,err = ..table.array(); 225 | 226 | var json = strEditBounds; 227 | if(#json){ 228 | cfg,err = web.json.tryParse(strEditBounds); 229 | if(!cfg){ 230 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 231 | return; 232 | } 233 | } 234 | 235 | var subscribeUrls = {} 236 | for(i=#cfg;1;-1){ 237 | var outbound = cfg[i] 238 | if(outbound.subscribeUrl){ 239 | subscribeUrls[outbound.subscribeUrl] = true; 240 | } 241 | } 242 | 243 | if(!table.count(subscribeUrls)){ 244 | import win.clip; 245 | var clibStr = win.clip.read(); 246 | if( ..string.startWith(clibStr,"http://") || ..string.startWith(clibStr,"https://") ){ 247 | subscribeUrls[clibStr] = true; 248 | } 249 | else { 250 | winform.msgWarn('当前配置不包含来自订阅源的服务器,\n请先复制订阅源网址到剪贴板!'); 251 | return; 252 | } 253 | } 254 | 255 | var count = 0; 256 | for(url,v in subscribeUrls){ 257 | var outbounds = xray.outbounds.importFromString(url); 258 | if(#outbounds){ 259 | for(i=#cfg;1;-1){ 260 | var outbound = cfg[i] 261 | if(outbound.subscribeUrl==url){ 262 | ..table.remove( cfg,i ); 263 | } 264 | } 265 | 266 | count = count + #outbounds; 267 | ..table.append(cfg,outbounds); 268 | } 269 | } 270 | 271 | winform.editOutbounds.text = ..web.json.stringifyArray(cfg,true,false,true); 272 | 273 | winform.msgOk("已成功刷新" + count + "个服务器,请点击「更新设置」启用新配置。",1200); 274 | winform.editOutbounds.modified = true; 275 | winform.editOutbounds.setFocus(); 276 | return; 277 | } 278 | }; 279 | } 280 | 281 | if(..string.match(strEditBounds,"%\[\]")){ 282 | menu[4] = {} 283 | menu[5] = { "转换为 vmess、vless、trojan、trojan-go、ss 分享链接"; 284 | function(id){ 285 | var cfgOutbouds,err = web.json.tryParse(strEditBounds); 286 | if(!cfgOutbouds){ 287 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 288 | return; 289 | } 290 | 291 | for(i,ob in cfgOutbouds){ 292 | if(ob.subscribeUrl){ 293 | if(!winform.msgAsk('当前配置包含订阅源,\n转换为分享链接所有服务器与订阅源将解除关联,\n按确定继续操作,按取消中止。')){ 294 | return; 295 | } 296 | 297 | break; 298 | } 299 | } 300 | 301 | var str = xray.outbounds.exportSharedLinks(cfgOutbouds); 302 | if(str){ 303 | winform.editOutbounds.text = str; 304 | winform.editOutbounds.modified = true; 305 | } 306 | } 307 | }; 308 | menu[6] = { "转换为 Base64 订阅源"; 309 | function(id){ 310 | var cfg,err = web.json.tryParse(strEditBounds); 311 | if(!cfg){ 312 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 313 | return; 314 | } 315 | 316 | var str = xray.outbounds.exportSharedLinks(cfg); 317 | if(str){ 318 | import crypt; 319 | winform.editOutbounds.text = ..crypt.encodeBin(str); 320 | winform.editOutbounds.modified = true; 321 | } 322 | } 323 | }; 324 | } 325 | elseif( #strEditBounds ) { 326 | menu[3] = {} 327 | 328 | menu[4] = { "转换为 JSON"; 329 | function(id){ 330 | import web.json; 331 | var outbounds = xray.outbounds.importFromString(strEditBounds); 332 | if(outbounds){ 333 | winform.editOutbounds.text = web.json.stringifyArray(outbounds,true,false,true); 334 | winform.editOutbounds.modified = true; 335 | } 336 | } 337 | }; 338 | } 339 | 340 | table.push(menu,{ /*---分隔线---*/ }); 341 | 342 | table.push(menu,{ "打开配置文件(JSON)"; 343 | function(id){ 344 | import fsys.dlg; 345 | var path = fsys.dlg.open("*.json|*.json||","打开 json 文件",,winform) 346 | if(path){ 347 | winform.editOutbounds.text = string.load(path); 348 | winform.editOutbounds.modified = true; 349 | } 350 | } 351 | }); 352 | 353 | table.push(menu,{ "另存为配置文件(JSON)"; 354 | function(id){ 355 | import fsys.dlg; 356 | var path = fsys.dlg.save("*.json|*.json||","另存为 json 文件",,winform) 357 | if(path){ 358 | var str = web.json.stringifyArray( 359 | config.proxy.outbounds,true,false,true 360 | ) 361 | 362 | string.save(path,str ) 363 | } 364 | } 365 | }); 366 | 367 | table.push(menu,{ "另存为默认服务器列表(JSON)"; 368 | function(id){ 369 | var str = web.json.stringifyArray( 370 | config.proxy.outbounds,true,false,true 371 | ) 372 | 373 | string.save("/xray-core/winXray-default-servers.json",str ); 374 | winform.msgOk("已存为默认服务器列表 /xray-core/winXray-default-servers.json",1000) 375 | } 376 | }); 377 | 378 | table.push(menu,{ /*---分隔线---*/ }); 379 | if(winform.editOutbounds.modified){ 380 | table.push(menu,{ "恢复为当前使用的服务器列表(JSON)"; 381 | function(id){ 382 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 383 | winform.editOutbounds.text = web.json.stringifyArray( 384 | config.proxy.outbounds,true,false,true 385 | ) 386 | winform.editOutbounds.modified = false; 387 | 388 | win.delay(500); 389 | winform.btnUpdate.disabledText = null; 390 | } 391 | }); 392 | } 393 | 394 | table.push(menu,{ "重置为默认服务器列表(JSON)"; 395 | function(id){ 396 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 397 | ..config.__loadDefaultOutbounds(); 398 | winform.editOutbounds.modified = false; 399 | publish("uiCommand.restartXrayCore",cfg); 400 | 401 | winform.btnUpdate.disabledText = null; 402 | } 403 | }); 404 | 405 | table.push(menu,{ "清空服务器列表"; 406 | function(id){ 407 | winform.editOutbounds.text = "[]"; 408 | winform.editOutbounds.modified = true; 409 | } 410 | }); 411 | 412 | if( ..string.match(winform.editOutbounds.text,"%\[\]")){ 413 | 414 | table.push(menu,{ /*---分隔线---*/ }); 415 | table.push(menu,{ "插入配置字段";{ 416 | { "代理服务协议:protocol"; 417 | function(id){ 418 | winform.editOutbounds.selText = '"protocol":"vless",' 419 | } 420 | }; 421 | { "SSH管理端口:sshPort"; 422 | function(id){ 423 | winform.editOutbounds.selText = '"sshPort":22,' 424 | } 425 | }; 426 | { "代理服务器地址:address"; 427 | function(id){ 428 | winform.editOutbounds.selText = '"address":"",' 429 | } 430 | }; 431 | { "端口:port"; 432 | function(id){ 433 | winform.editOutbounds.selText = '"port":443,' 434 | } 435 | }; 436 | { "网络协议:network"; 437 | function(id){ 438 | winform.editOutbounds.selText = '"network":"tcp",' 439 | } 440 | }; 441 | { "服务器密码:id"; 442 | function(id){ 443 | winform.editOutbounds.selText = '"id":"",' 444 | } 445 | }; 446 | { "加密方式:security"; 447 | function(id){ 448 | winform.editOutbounds.selText = '"security":"auto",' 449 | } 450 | }; 451 | { "启用TLS:tls"; 452 | function(id){ 453 | winform.editOutbounds.selText = '"tls":"xtls",' 454 | } 455 | }; 456 | { "允许忽略证书:allowInsecure"; 457 | function(id){ 458 | winform.editOutbounds.selText = '"allowInsecure":true,' 459 | } 460 | }; 461 | { "TLS服务器名:sni"; 462 | function(id){ 463 | winform.editOutbounds.selText = '"sni":"",' 464 | } 465 | }; 466 | { "启用TLS:tls"; 467 | function(id){ 468 | winform.editOutbounds.selText = '"tls":"xtls",' 469 | } 470 | }; 471 | { "流控:flow"; 472 | function(id){ 473 | winform.editOutbounds.selText = '"flow":"xtls-rprx-direct",' 474 | } 475 | }; 476 | { "多路复用最大连接:concurrency"; 477 | function(id){ 478 | winform.editOutbounds.selText = '"concurrency":4,' 479 | } 480 | }; 481 | 482 | { "伪装类型:type"; 483 | function(id){ 484 | winform.editOutbounds.selText = '"type":"http",' 485 | } 486 | }; 487 | { "备注:ps"; 488 | function(id){ 489 | winform.editOutbounds.selText = '"ps":"",' 490 | } 491 | }; 492 | { "请求主机名:host"; 493 | function(id){ 494 | winform.editOutbounds.selText = '"host":"",' 495 | } 496 | }; 497 | { "请求路径:path"; 498 | function(id){ 499 | winform.editOutbounds.selText = '"path":"/",' 500 | } 501 | }; 502 | { "HTTP请求方法:httpMethod"; 503 | function(id){ 504 | winform.editOutbounds.selText = '"httpMethod":"GET",' 505 | } 506 | }; 507 | { "HTTP请求头:headers"; 508 | function(id){ 509 | winform.editOutbounds.selText = '"headers":{"method":"GET"},' 510 | } 511 | }; 512 | { "绑定的订阅网址:subscribeUrl"; 513 | function(id){ 514 | winform.editOutbounds.selText = '"subscribeUrl":"https://",' 515 | } 516 | }; 517 | { "允许自动测试并连接:autoConnect"; 518 | function(id){ 519 | winform.editOutbounds.selText = '"autoConnect":false,' 520 | } 521 | }; 522 | { "备注:ps"; 523 | function(id){ 524 | winform.editOutbounds.selText = '"ps":"",' 525 | } 526 | }; 527 | }}); 528 | } 529 | 530 | return menu; 531 | } 532 | }) 533 | 534 | winform.editCoreConfig.enablePopMenu({ 535 | { /*---分隔线---*/ }; 536 | { "重置为默认 Xray 配置"; 537 | function(id){ 538 | config.__resetDefaultCore(); 539 | winform.editCoreConfig.text = web.json.stringify( 540 | config.core.default,true 541 | ) 542 | } 543 | }; 544 | } ) 545 | 546 | subscribe("outbounds.updateConfigJson",function(){ 547 | if(winform.editOutbounds.modified){ 548 | return; 549 | } 550 | 551 | winform.editOutbounds.text = web.json.stringifyArray( 552 | config.proxy.outbounds,true,false,true 553 | ) 554 | 555 | winform.editOutbounds.modified = false; 556 | } ) 557 | 558 | winform.editOutbounds.text = web.json.stringifyArray( 559 | config.proxy.outbounds,true,false,true 560 | ) : "[]" 561 | winform.editOutbounds.modified = false; 562 | 563 | subscribe("config.inbounds.changed",function(){ 564 | if(winform.editCoreConfig.modified){ 565 | return; 566 | } 567 | 568 | winform.editCoreConfig.text = web.json.stringify( 569 | config.core.default,true 570 | ) 571 | 572 | winform.editCoreConfig.modified = false; 573 | } ) 574 | winform.editCoreConfig.text = web.json.stringify( 575 | config.core.default,true 576 | ) 577 | 578 | var editOutboundsTip = { 579 | address = {"代理服务器地址";"值可以是域名或者IP地址"}; 580 | alterId = {"额外ID";"仅用于VMESS协议,与服务器一致即可"}; 581 | aid = {"额外ID(alterId)";"数值,仅用于VMESS协议,与服务器一致即可"}; 582 | network = {"传输协议(network)";"可选值为tcp,ws等"}; 583 | net = {"传输协议(network)";"可选值为tcp,ws等"}; 584 | type = {"伪装类型";"默认的可选值为none,http等,kcp或quic此字段表示header.type,可选值为 none,srtp,utp,wechat-video,dtls,wireguard"}; 585 | ps = {"备注";"备注"}; 586 | tls = {"是否启用TLS";"可选值为tls,xtls或空值,vless协议省略时默认值为tls"}; 587 | path = {"请求路径";"用于ws,http等协议指定请求路径"}; 588 | httpMethod = {"HTTP请求方法";"用于http协议指定请求方法"}; 589 | headers = {"HTTP请求头";"用于ws,http等协议指定请求头"}; 590 | id = {"密码";"服务器密码"}; 591 | host = {"主机名";"用于指定http,ws等协议请求头中的主机名"}; 592 | protocol = {"协议";"可选值为 vmess,vless,shadowsocks,trojan,trojan-go 之一"}; 593 | port = {"端口";"服务器端口,数值"}; 594 | security = {"加密方式";"可省略,vmess协议默认为auto,vless协议默认为none"}; 595 | sni = {"TLS服务器名";"TLS客户端请求中的服务器名字段,如果不指定则默认取host或address字段值。"}; 596 | flow = {"流控";"目前仅用于选择 XTLS 的算法,省略时默认值为xtls-rprx-direct"}; 597 | concurrency = {"Mux多路复用:最大并发连接数";"最小值1,最大值1024,特殊值-1,不加载mux模块。缺省由winXray根据连接协议自动设定此值。"}; 598 | } 599 | 600 | winform.editOutbounds.wndproc = function(hwnd,message,wParam,lParam){ 601 | if(message == 0x202/*_WM_LBUTTONUP*/){ 602 | var lineIndex = winform.editOutbounds.lineFromChar(); 603 | if(lineIndex == winform.editOutbounds.preLineIndex){ 604 | return; 605 | } 606 | winform.editOutbounds.preLineIndex = lineIndex; 607 | 608 | var line = winform.editOutbounds.lineText(); 609 | var n,v = string.match(line,`^\s*\"(\w+)\"\s*\:\s*(%"")[\,\s]*$`); 610 | if( !n ) n,v = string.match(line,`^\s*\"(\w+)\"\s*\:\s*(\d+)[\,\s]*$`); 611 | if( !n ) n,v = string.match(line,`^\s*\"(\w+)\"\s*\:\s*\{`); 612 | if(editOutboundsTip[n]){ 613 | winform.editOutbounds.showInfoTip(editOutboundsTip[n][1],editOutboundsTip[n][2]) 614 | } 615 | } 616 | } 617 | 618 | winform.editOutbounds.limit = 0; 619 | winform.editCoreConfig.limit = 0; 620 | 621 | winform.enableDpiScaling(); 622 | winform.show(); 623 | win.loopMessage(); 624 | return winform; 625 | -------------------------------------------------------------------------------- /forms/main/config/subscription.aardio: -------------------------------------------------------------------------------- 1 | import fonts.fontAwesome; 2 | import win.ui; 3 | /*DSG{{*/ 4 | var winform = win.form(text="aardio form";right=892;bottom=709;bgcolor=16777215) 5 | winform.add( 6 | btnAdd={cls="plus";text="添加订阅";left=10;top=1;right=143;bottom=28;align="left";dl=1;dt=1;font=LOGFONT(h=-15);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=12}};iconText='\uF196 ';notify=1;textPadding={left=30};z=5}; 7 | btnRemove={cls="plus";text="移除选中订阅";left=143;top=1;right=276;bottom=28;align="left";dl=1;dt=1;font=LOGFONT(h=-15);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=12}};iconText='\uF014';notify=1;textPadding={left=30};z=6}; 8 | btnUpdate={cls="plus";text="更新订阅";left=576;top=654;right=698;bottom=690;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=1}; 9 | chkAll={cls="plus";text="全部启用";left=11;top=628;right=118;bottom=659;align="left";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-16;name='FontAwesome')};iconText='\uF0C8';notify=1;textPadding={left=19};z=3}; 10 | chkAutoRefreshSubscription={cls="plus";text="允许出站服务器异常自动刷新订阅";left=11;top=663;right=527;bottom=694;align="left";bgcolor=16777215;db=1;dl=1;dr=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-16;name='FontAwesome')};iconText='\uF0C8';notify=1;textPadding={left=19};z=2}; 11 | listview={cls="listview";left=10;top=34;right=883;bottom=609;db=1;dl=1;dr=1;dt=1;edge=1;fullRow=1;gridLines=1;z=4}; 12 | static={cls="static";text="双击上面单元格可直接编辑";left=710;top=616;right=867;bottom=634;color=10789024;db=1;dr=1;transparent=1;z=7} 13 | ) 14 | /*}}*/ 15 | 16 | import style; 17 | winform.chkAutoRefreshSubscription.skin(style.checkBox); 18 | winform.chkAll.skin(style.checkBox); 19 | winform.btnAdd.skin(style.plainButton); 20 | 21 | winform.listview.insertColumn("",20) 22 | winform.listview.insertColumn("状态",50) 23 | winform.listview.insertColumn("备注",150) 24 | winform.listview.insertColumn("订阅地址",-1) 25 | winform.listview.checkbox = true; 26 | winform.listview.gridLines = 0; 27 | 28 | import win.ui.grid; 29 | var grid = win.ui.grid(winform.listview);//创建数据视图 30 | grid.readonlyColums = { [1] = true;[2] = true }; 31 | 32 | import config; 33 | import inet.url; 34 | var reloadSubscriptions = function(){ 35 | winform.chkAutoRefreshSubscription.checked = config.proxy.autoRefreshSubscription; 36 | config.proxy.subscribeUrls.fields = {"";"checked";"ps";"url";} 37 | var outbounds = ..config.proxy.outbounds : ..table.array(); 38 | 39 | var outbondSubscribeUrlMap = {}; 40 | for(i=#outbounds;1;-1){ 41 | var outbound = outbounds[i] 42 | if(outbound.subscribeUrl){ 43 | outbondSubscribeUrlMap[outbound.subscribeUrl] = true; 44 | } 45 | } 46 | 47 | var cfgSubscribeUrls = config.proxy.subscribeUrls; 48 | for(i=#cfgSubscribeUrls;1;-1){ 49 | var sub = cfgSubscribeUrls[i] 50 | if(outbondSubscribeUrlMap[sub.url]){ 51 | outbondSubscribeUrlMap[sub.url] = null; 52 | } 53 | else{ 54 | sub.checked = "禁用"; 55 | } 56 | } 57 | 58 | var count = 0; 59 | for(url,v in outbondSubscribeUrlMap){ 60 | var tUrl = ..inet.url.split(subscribeUrl); 61 | ..table.push(cfgSubscribeUrls,{url=url;ps=tUrl ? tUrl.host : "";checked="启用"}) 62 | } 63 | 64 | var chkAll = true; 65 | var checked = {} 66 | for(i=#cfgSubscribeUrls;1;-1){ 67 | var sub = cfgSubscribeUrls[i] 68 | if(sub.checked=="启用"){ 69 | table.push(checked,i) 70 | } 71 | else { 72 | chkAll = false; 73 | } 74 | } 75 | winform.chkAll.checked = chkAll; 76 | 77 | grid.setTable( cfgSubscribeUrls ); 78 | winform.listview.checked = checked; 79 | } 80 | 81 | winform.loadSubscriptionsOnce = function(){ 82 | reloadSubscriptions(); 83 | winform.loadSubscriptionsOnce = function(){}; 84 | } 85 | 86 | subscribe("uiCommand.subscriptionNew",function(subItem,existIndex){ 87 | if(existIndex){ 88 | winform.listview.setItemText(subItem.url,existIndex,4); 89 | winform.listview.setChecked(existIndex,true); 90 | return; 91 | } 92 | 93 | var item = winform.listview.addItem({"",subItem.checked,subItem.ps,subItem.url}); 94 | winform.listview.setChecked(item,true); 95 | } ) 96 | 97 | //编辑变更值会触发下面的事件 98 | grid.onEditChanged = function(text,iItem,iSubItem){ 99 | var name = config.proxy.subscribeUrls.fields[iSubItem] 100 | var sub = config.proxy.subscribeUrls[iItem]; 101 | 102 | if( name==="url" ){ 103 | if( text[1]=='/'#) { 104 | text = "https://github.com"+text; 105 | } 106 | 107 | if(sub && sub.ps=="订阅源"){ 108 | import inet.url; 109 | var tUrl = inet.url.split(text); 110 | if(tUrl&&#tUrl.host){ 111 | sub.ps = tUrl.host; 112 | winform.listview.setItemText(sub.ps,iItem,3); 113 | } 114 | } 115 | } 116 | 117 | if(sub){ 118 | sub[name] = text; 119 | config.proxy.save(); 120 | } 121 | else { 122 | winform.msgErr("错误的订阅索引") 123 | } 124 | } 125 | 126 | winform.popmenu = win.ui.popmenu(winform);//创建弹出菜单 127 | winform.popmenu.add('删除',function(id){ 128 | winform.btnRemove.oncommand(); 129 | }); 130 | winform.popmenu.add('全部删除',function(id){ 131 | for(i=1; winform.listview.count) winform.listview.setSelected(i,true); 132 | winform.btnRemove.oncommand(); 133 | }); 134 | winform.popmenu.add(); 135 | winform.popmenu.add('自剪贴板添加订阅',function(id){ 136 | winform.btnAdd.oncommand(); 137 | }); 138 | 139 | winform.btnUpdate.skin(style.primaryButton); 140 | winform.btnUpdate.oncommand = function(id,event){ 141 | 142 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 143 | config.proxy.autoRefreshSubscription = winform.chkAutoRefreshSubscription.checked; 144 | 145 | var outbondSubscribeUrlMap = {}; 146 | var cfgSubscribeUrls = config.proxy.subscribeUrls; 147 | for(i=#cfgSubscribeUrls;1;-1){ 148 | var sub = cfgSubscribeUrls[i] 149 | outbondSubscribeUrlMap[sub.url] = sub.checked=="启用"; 150 | } 151 | 152 | var removed = 0; 153 | var outbounds = ..config.proxy.outbounds : ..table.array(); 154 | for(i=#outbounds;1;-1){ 155 | var outbound = outbounds[i] 156 | if(outbound.subscribeUrl){ 157 | if(!outbondSubscribeUrlMap[outbound.subscribeUrl]){ 158 | table.remove(outbounds,i); 159 | removed++; 160 | } 161 | } 162 | } 163 | 164 | if(removed) config.proxy.save(); 165 | 166 | import xray.outbounds; 167 | if(!xray.outbounds.updateSubscription()){ 168 | if(removed) publish("uiCommand.restartXrayCore",outbounds) 169 | } 170 | 171 | winform.btnUpdate.disabledText = null; 172 | } 173 | 174 | winform.chkAll.oncommand = function(id,event){ 175 | var checked = winform.chkAll.checked 176 | for hItem in winform.listview.each(){ 177 | winform.listview.setChecked(hItem,checked) 178 | } 179 | } 180 | 181 | winform.btnRemove.skin(style.plainButton) 182 | winform.btnRemove.oncommand = function(id,event){ 183 | 184 | var outboundRemoved = 0; 185 | var subscribeUrls = config.proxy.subscribeUrls; 186 | var items = winform.listview.selected; 187 | if(!#items){ 188 | return winform.msgWarn("未选中任何订阅项( 请用鼠标单击订阅链接,不是指「勾选」 )") 189 | } 190 | 191 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 192 | 193 | for(i=#items;1;-1){ 194 | var url = subscribeUrls[items[i]].url; 195 | table.remove(subscribeUrls,items[i]); 196 | winform.listview.delItem(items[i]); 197 | 198 | var outbounds = ..config.proxy.outbounds : ..table.array(); 199 | for(i=#outbounds;1;-1){ 200 | var outbound = outbounds[i] 201 | if(outbound.subscribeUrl===url){ 202 | table.remove(outbounds,i); 203 | outboundRemoved++; 204 | } 205 | } 206 | } 207 | 208 | config.proxy.save(); 209 | 210 | if(outboundRemoved){ 211 | publish("uiCommand.restartXrayCore") 212 | } 213 | else { 214 | win.delay(200); 215 | } 216 | 217 | publish("outbounds.updateConfigJson"); 218 | winform.btnUpdate.disabledText = null; 219 | } 220 | 221 | winform.btnAdd.oncommand = function(id,event){ 222 | import inet.url; 223 | import win.clip; 224 | 225 | var ps = "订阅源"; 226 | var url = win.clip.read(); 227 | url = url ? string.trim(url,'"\'\s\t\r\n '); 228 | if(url && url[1]=='/'#){ 229 | url = "http://github.com" + url; 230 | } 231 | 232 | if(!inet.url.is(url)){ 233 | url = ""; 234 | } 235 | else { 236 | var tUrl = inet.url.split(url); 237 | if(tUrl && tUrl.host){ 238 | ps = tUrl.host 239 | } 240 | } 241 | 242 | config.proxy.subscribeUrls[winform.listview.count+1] = {checked="启用";ps="订阅源";url=url} 243 | var item = winform.listview.addItem({ 244 | "";"启用";ps;url 245 | }) 246 | 247 | winform.listview.setChecked(item,true); 248 | grid.beginEdit(item,4); 249 | } 250 | 251 | winform.listview.onnotify = function(id,code,ptr){ 252 | if( code == 0xFFFFFF9B/*_LVN_ITEMCHANGED*/ ){ 253 | var nmListView = winform.listview.getNotifyMessage(code,ptr); 254 | if( !nmListView ) 255 | return; 256 | 257 | if( nmListView.uNewState & 0xF000/*_LVIS_STATEIMAGEMASK*/){ 258 | var selIndex = nmListView.iItem; 259 | if( winform.listview.getChecked( selIndex ) ){ 260 | config.proxy.subscribeUrls[selIndex].checked = "启用"; 261 | config.proxy.save(); 262 | 263 | winform.listview.setItemText("启用",selIndex,2); 264 | } 265 | else { 266 | config.proxy.subscribeUrls[selIndex].checked = "禁用"; 267 | config.proxy.save(); 268 | 269 | winform.listview.setItemText("禁用",selIndex,2); 270 | } 271 | 272 | var chkAll = true; 273 | for(i=#config.proxy.subscribeUrls;1;-1){ 274 | var sub = config.proxy.subscribeUrls[i] 275 | if(sub.checked!="启用"){ 276 | chkAll = false; 277 | 278 | } 279 | } 280 | 281 | winform.chkAll.checked = chkAll; 282 | } 283 | } 284 | elseif( code == 0xFFFFFFF4/*_NM_CUSTOMDRAW*/ ) { 285 | var lvcd = winform.listview.getNotifyCustomDraw(code,ptr); 286 | if( lvcd.nmcd.dwDrawStage == 0x10001/*_CDDS_ITEMPREPAINT*/) 287 | return 0x20/*_CDRF_NOTIFYSUBITEMDRAW*/ 288 | elseif( lvcd.nmcd.dwDrawStage == 1/*_CDDS_PREPAINT*/ ){ 289 | return 0x20/*_CDRF_NOTIFYITEMDRAW*/; 290 | } 291 | elseif( lvcd.nmcd.dwDrawStage == ( 0x10001/*_CDDS_ITEMPREPAINT*/ | 0x20000/*_CDDS_SUBITEM*/) ){ 292 | //注意这里 iSubItem 的索引自0开始( 其他函数通常自1开始 ) 293 | lvcd.clrText = lvcd.nmcd.dwItemSpec % 2 ? 0x373737 : 0; 294 | lvcd.clrTextBk = lvcd.nmcd.dwItemSpec % 2 ? 0xFFFFFF : 0xF5F5F5; 295 | lvcd.update(); 296 | 297 | return 0/*_CDRF_DODEFAULT*/ 298 | } 299 | } 300 | elseif( code == 0xFFFFFFFB/*_NM_RCLICK*/ ) { 301 | var x,y = win.getCursorPos(); 302 | winform.popmenu.popup(x,y,true);//弹出菜单 303 | } 304 | } 305 | 306 | winform.show(); 307 | win.loopMessage(); 308 | return winform; -------------------------------------------------------------------------------- /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 = xray.outbounds.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("配置已更新",1200); 100 | } 101 | 102 | 103 | import xray.outbounds; 104 | winform.editOutbounds.enablePopMenu(function(){ 105 | if(tbs.selIndex==1){ 106 | var strEditBounds = string.trim(winform.editOutbounds.text); 107 | var menu = { 108 | { /*---分隔线---*/ }; 109 | { "自剪贴板批量导入vmess、trojan、ss链接、或订阅源"; 110 | function(id){ 111 | 112 | var outbounds = xray.outbounds.importOutboundFromClip(); 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 = xray.outbounds.exportSharedLinks(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 + "个服务器,请点击「更新设置」启用新配置。",1200); 139 | winform.editOutbounds.modified = true; 140 | winform.editOutbounds.setFocus(); 141 | return; 142 | } 143 | 144 | winform.msgFrown("未导入服务器, 请先复制订阅源、或vmess、ss、trojan链接!") 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 = xray.outbounds.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 + "个服务器,请点击「更新设置」启用新配置。",1200); 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、trojan、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 = xray.outbounds.exportSharedLinks(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 = xray.outbounds.exportSharedLinks(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 = xray.outbounds.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 | import win.ui; 2 | import win.ui.atom; 3 | import fonts.fontAwesome; 4 | /*DSG{{*/ 5 | var winform = win.form(text="winXray - 代理自动配置文件(PAC)";right=912;bottom=802;bgcolor=16777215) 6 | winform.add( 7 | btnAddDomain={cls="plus";text="添加";left=625;top=25;right=723;bottom=56;align="left";bgcolor=14935259;dr=1;dt=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=16}; 8 | btnDeleteDomain={cls="plus";text="移除";left=740;top=25;right=838;bottom=56;align="left";bgcolor=14935259;dr=1;dt=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=17}; 9 | 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='\uF0E2';notify=1;textPadding={left=40};z=9}; 10 | 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=10}; 11 | 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='\uF26B';notify=1;textPadding={left=40};z=7}; 12 | editPacDirect={cls="richedit";left=8;top=97;right=901;bottom=745;db=1;dl=1;dr=1;dt=1;edge=1;font=LOGFONT(h=-13);hidesel=false;hscroll=1;multiline=1;vscroll=1;z=2}; 13 | editPacDomain={cls="plus";left=91;top=29;right=436;bottom=53;align="right";bgcolor=16777215;border={bottom=1;color=-8355712};dl=1;dr=1;dt=1;editable="edit";font=LOGFONT(h=-16);textPadding={bottom=1};z=12}; 14 | editPacProxy={cls="richedit";left=8;top=97;right=901;bottom=751;db=1;dl=1;dr=1;dt=1;edge=1;font=LOGFONT(h=-15);hidesel=false;hscroll=1;multiline=1;vscroll=1;z=3}; 15 | groupbox={cls="groupbox";text="域名项";left=8;top=8;right=901;bottom=64;bgcolor=16777215;dl=1;dr=1;dt=1;edge=1;z=1}; 16 | lbTip={cls="plus";text="以下域名通过代理连接( 点域名绑定上面选项,点击右键菜单可批量导入域名 )。";left=10;top=66;right=898;bottom=92;align="left";color=3947580;dl=1;dr=1;dt=1;font=LOGFONT(h=-13);notify=1;transparent=1;z=13}; 17 | navDirect={cls="plus";text="直连域名( JSON )";left=184;top=750;right=315;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=8}; 18 | navProxy={cls="plus";text="代理域名( JSON )";left=53;top=750;right=184;bottom=782;border={top=1;color=-16744448};db=1;dl=1;z=5}; 19 | 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=11}; 20 | plus={cls="plus";left=9;top=750;right=53;bottom=751;border={color=-16744448};db=1;dl=1;forecolor=32768;z=14}; 21 | plus2={cls="plus";text="域名:";left=16;top=28;right=79;bottom=54;align="right";color=3947580;dl=1;dt=1;font=LOGFONT(h=-13);notify=1;transparent=1;z=15}; 22 | radioDirect={cls="plus";text="直连";left=540;top=27;right=611;bottom=58;align="left";dr=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-15;name='FontAwesome')};iconText='\uF111';notify=1;textPadding={left=15};z=6}; 23 | radioProxy={cls="plus";text="代理";left=460;top=27;right=529;bottom=58;align="left";dr=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-15;name='FontAwesome')};iconText='\uF111';notify=1;textPadding={left=15};z=4} 24 | ) 25 | /*}}*/ 26 | 27 | import config; 28 | import style; 29 | winform.btnSave.skin(style.primaryButton); 30 | winform.btnUpdate.skin(style.button); 31 | winform.btnReset.skin(style.button); 32 | winform.btnAddDomain.skin(style.button); 33 | winform.btnDeleteDomain.skin(style.button); 34 | winform.radioDirect.skin(style.radio); 35 | winform.radioProxy.skin(style.radio); 36 | winform.radioProxy.checked = true; 37 | 38 | import config; 39 | import web.json; 40 | import win.ui.tabs; 41 | var tbs = win.ui.tabs(winform.navProxy,winform.navDirect,winform.nav06,winform.nav091,winform.navZhengMa,winform.navTxt) 42 | tbs.margin = 0; 43 | 44 | tbs.skin({ 45 | foreground={ 46 | active=0xFFFFFFFF; 47 | default=0x00FFFFFF; 48 | hover=0xFFCCCCCC; 49 | }; 50 | checked={ 51 | foreground={ 52 | default=0x00FFFFFF; 53 | }; 54 | border = { 55 | default = {left=1;right=1;bottom=1;color=0xFF008000} 56 | }; 57 | } 58 | }); 59 | 60 | tbs.onSelchange = function(idx,strip,form){ 61 | if(idx==1){ 62 | winform.currentEditPac = winform.editPacProxy; 63 | winform.editPacProxy.hide = false; 64 | winform.editPacDirect.hide = true; 65 | winform.lbTip.text = `以下域名通过代理连接( 点域名绑定上面选项,点击右键菜单可批量导入代理域名 ):` 66 | } 67 | else { 68 | winform.currentEditPac = winform.editPacDirect; 69 | winform.editPacProxy.hide = true; 70 | winform.editPacDirect.hide = false; 71 | winform.lbTip.text = `以下域名不使用代理( 点域名绑定上面选项,点击右键菜单可批量导入直连域名 ): ` 72 | } 73 | } 74 | tbs.selIndex = 1; 75 | 76 | import win.dlg.message; 77 | win.dlg.message.install(); 78 | 79 | winform.loadPacToEdit = function(str){ 80 | var rules = string.match(str,"var\s+rules\s*=\s*(%\[\])") 81 | if(!rules) return winform.msgErr("PAC文件格式错误"); 82 | 83 | rules = web.json.parse(rules); 84 | if(!(rules && table.isArray(rules) && type(rules[1][1])==type.table)) return winform.msgErr("PAC文件格式错误"); 85 | 86 | var proxy,direct = table.array(),table.array() 87 | for(i=1;#rules){ 88 | table.append(direct,rules[i][1]); 89 | table.append(proxy,rules[i][2]); 90 | } 91 | 92 | winform.editPacProxy.text = web.json.stringify(proxy,true) 93 | winform.editPacDirect.text = web.json.stringify(direct,true) 94 | } 95 | 96 | 97 | winform.btnUpdate.oncommand = function(id,event){ 98 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}; 99 | 100 | thread.invoke( 101 | function(winform){ 102 | import web.rest.github; 103 | var result = web.rest.github.getContent("petronny","gfwlist2pac","gfwlist.pac"); 104 | if( result ){ 105 | winform.loadPacToEdit( result ); 106 | winform.editPacDirect.modified = true; 107 | winform.btnSave.disabled = false; 108 | winform.btnReset.disabled = false; 109 | winform.msgOk("PAC已同步到最新版本,请点击「保存PAC」按钮"); 110 | } 111 | else { 112 | winform.msgFrown("PAC更新失败,建议开启全局代理后重试。") 113 | } 114 | winform.btnUpdate.disabledText = null; 115 | },winform 116 | ) 117 | } 118 | 119 | import sysProxy; 120 | import string.conv; 121 | import xray.pacServer; 122 | winform.savePacToFile = function(){ 123 | var proxy = web.json.tryParse(string.conv.fromWide(winform.editPacProxy.text)) 124 | var direct = web.json.tryParse(string.conv.fromWide(winform.editPacDirect.text)) 125 | if(!(table.isArray(proxy))) { 126 | winform.btnSave.disabledText = null; 127 | return winform.msgErr("代理域名配置格式错误"); 128 | } 129 | if(!(table.isArray(direct))) { 130 | winform.btnSave.disabledText = null; 131 | return winform.msgErr("直连域名配置格式错误"); 132 | } 133 | 134 | xray.pacServer.savePacText(web.json.stringify({{direct;proxy}},true)); 135 | 136 | winform.editPacDirect.modified = false; 137 | winform.editPacProxy.modified = false; 138 | sysProxy.reset(true); 139 | 140 | winform.msgOk("已保存PAC文件",1200); 141 | winform.btnSave.disabledText = null; 142 | winform.btnSave.disabled = true; 143 | winform.btnReset.disabled = true; 144 | } 145 | 146 | winform.btnSave.oncommand = function(id,event){ 147 | winform.btnSave.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}; 148 | 149 | thread.invoke( 150 | function(winform){ 151 | sleep(300) 152 | winform.savePacToFile(); 153 | 154 | },winform 155 | ) 156 | } 157 | 158 | winform.btnReset.oncommand = function(id,event){ 159 | winform.btnReset.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}; 160 | 161 | thread.invoke( 162 | function(winform){ 163 | import xray.pacServer; 164 | winform.loadPacToEdit(xray.pacServer.loadPacText()); 165 | 166 | winform.btnReset.disabledText = null; 167 | winform.btnSave.disabled = true; 168 | winform.btnReset.disabled = true; 169 | winform.msgOk("已恢复到当前PAC配置") 170 | },winform 171 | ) 172 | } 173 | 174 | import xray.pacServer; 175 | winform.loadPacToEdit( xray.pacServer.loadPacText() ); 176 | 177 | winform.editPacProxy.modified = false; 178 | winform.editPacDirect.modified = false; 179 | winform.btnSave.disabled = true; 180 | winform.btnReset.disabled = true; 181 | 182 | winform.editPacDirect.limit = -1; 183 | winform.editPacDirect.onChange = function(){ 184 | winform.btnSave.disabled = false; 185 | winform.btnReset.disabled = false; 186 | } 187 | 188 | winform.editPacProxy.limit = -1; 189 | winform.editPacProxy.onChange = function(){ 190 | winform.btnSave.disabled = false; 191 | winform.btnReset.disabled = false; 192 | } 193 | 194 | import process; 195 | winform.lbTip.oncommand = function(id,event){ 196 | process.openUrl("https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_(PAC)_file") 197 | } 198 | 199 | winform.editPacDirect.wndproc = function(hwnd,message,wParam,lParam){ 200 | if(message == 0x202/*_WM_LBUTTONUP*/){ 201 | var lineIndex = winform.editPacDirect.lineFromChar(); 202 | if(lineIndex == winform.editPacDirect.preLineIndex){ 203 | return; 204 | } 205 | winform.editPacDirect.preLineIndex = lineIndex; 206 | 207 | var line = winform.editPacDirect.lineText(); 208 | var domain = string.match(line,`\"(.+)\"`); 209 | 210 | if(domain){ 211 | winform.editPacDomain.text = domain; 212 | winform.radioDirect.checked = true; 213 | } 214 | } 215 | } 216 | 217 | winform.editPacProxy.wndproc = function(hwnd,message,wParam,lParam){ 218 | if(message == 0x202/*_WM_LBUTTONUP*/){ 219 | var lineIndex = winform.editPacProxy.lineFromChar(); 220 | if(lineIndex == winform.editPacProxy.preLineIndex){ 221 | return; 222 | } 223 | winform.editPacProxy.preLineIndex = lineIndex; 224 | 225 | var line = winform.editPacProxy.lineText(); 226 | var domain = string.match(line,`\"(.+)\"`); 227 | 228 | if(domain){ 229 | winform.editPacDomain.text = domain; 230 | winform.radioProxy.checked = true; 231 | } 232 | } 233 | } 234 | 235 | import win.dlg.findReplace; 236 | var findDlgDirect = win.dlg.findReplace(winform); 237 | 238 | findDlgDirect.onSearch = function(findWhat,down,matchCase,wholeWord){ 239 | winform.currentEditPac.findText(findWhat,findDlgDirect.flags); 240 | } 241 | 242 | findDlgDirect.onReplace = function(findWhat,replaceWith,down,matchCase,wholeWord){ 243 | winform.currentEditPac.replaceText(findWhat,replaceWith,findDlgDirect.flags); 244 | } 245 | 246 | findDlgDirect.onReplaceAll = function(findWhat,replaceWith,down,matchCase,wholeWord){ 247 | winform.currentEditPac.replaceAll(findWhat,replaceWith,findDlgDirect.flags) 248 | } 249 | 250 | winform.editPacDirect.enablePopMenu(function(){ 251 | var items = { 252 | { "查找 Ctrl + F"; 253 | function(id){ 254 | findDlgDirect.find(winform.editPacDirect.selText) 255 | } 256 | }; 257 | { "替换 Ctrl + H"; 258 | function(id){ 259 | findDlgDirect.replace(winform.editPacDirect.selText); 260 | } 261 | }; 262 | {}; 263 | { "自剪贴板导入直连域名(支持任意空白分隔符)"; 264 | function(id){ 265 | var proxy = web.json.tryParse(string.conv.fromWide(winform.editPacProxy.text)) 266 | var direct = web.json.tryParse(string.conv.fromWide(winform.editPacDirect.text)) 267 | if(!(proxy)) return winform.msgErr("代理域名配置格式错误"); 268 | if(!(direct)) return winform.msgErr("直连域名配置格式错误"); 269 | 270 | import win.clip; 271 | var str = win.clip.read(); 272 | if(!str){ 273 | return winform.msgWarn("请先复制域名到剪贴板"); 274 | } 275 | 276 | var count,ignored = 0,0; 277 | for domain in string.gmatch(str,"\w<[\w-_]*\.>+\w{1,8}") { 278 | 279 | if(!table.indexOf(proxy,domain) && table.indexOf(direct,domain) ){ 280 | ignored++; 281 | continue; 282 | } 283 | 284 | proxy = table.filter(proxy,lambda(v) v!=domain); 285 | if(!table.indexOf(direct,domain)){ 286 | table.unshift(direct,domain) 287 | } 288 | count++; 289 | } 290 | 291 | winform.editPacProxy.text = web.json.stringify(proxy,true) 292 | winform.editPacDirect.text = web.json.stringify(direct,true) 293 | winform.editPacProxy.modified = true; 294 | winform.editPacDirect.modified = true; 295 | winform.msgInfo('已成功导入 '+count+' 个直连域名,\r\n' 296 | + ignored + '个域名已经是直连域名无需添加。\r\n下一步请点下面的「保存PAC」按钮。',1500); 297 | } 298 | }; 299 | { "重置为默认PAC"; 300 | function(id){ 301 | import xray.pacServer; 302 | xray.pacServer.resetPac(); 303 | winform.loadPacToEdit(xray.pacServer.loadPacText()); 304 | } 305 | }; 306 | { "恢复为当前使用的PAC"; 307 | function(id){ 308 | import xray.pacServer; 309 | winform.loadPacToEdit(xray.pacServer.loadPacText()); 310 | } 311 | }; 312 | } 313 | return items; 314 | }); 315 | 316 | winform.editPacProxy.enablePopMenu(function(){ 317 | var items = { 318 | { "查找 Ctrl + F"; 319 | function(id){ 320 | findDlgDirect.find(winform.editPacProxy.selText) 321 | } 322 | }; 323 | { "替换 Ctrl + H"; 324 | function(id){ 325 | findDlgDirect.replace(winform.editPacProxy.selText); 326 | } 327 | }; 328 | {}; 329 | { "自剪贴板导入代理域名(支持任意空白分隔符)"; 330 | function(id){ 331 | var proxy = web.json.tryParse(string.conv.fromWide(winform.editPacProxy.text)) 332 | var direct = web.json.tryParse(string.conv.fromWide(winform.editPacDirect.text)) 333 | if(!(proxy)) return winform.msgErr("代理域名配置格式错误"); 334 | if(!(direct)) return winform.msgErr("直连域名配置格式错误"); 335 | 336 | import win.clip; 337 | var str = win.clip.read(); 338 | if(!str){ 339 | return winform.msgWarn("请先复制域名到剪贴板"); 340 | } 341 | 342 | var count,ignored = 0,0; 343 | for domain in string.gmatch(str,"\w<[\w-_]*\.>+\w{1,8}") { 344 | 345 | if(!table.indexOf(direct,domain) && table.indexOf(proxy,domain) ){ 346 | ignored++; 347 | continue; 348 | } 349 | 350 | direct = table.filter(direct,lambda(v) v!=domain); 351 | if(!table.indexOf(proxy,domain)){ 352 | table.unshift(proxy,domain) 353 | } 354 | count++; 355 | } 356 | 357 | winform.editPacProxy.text = web.json.stringify(proxy,true) 358 | winform.editPacDirect.text = web.json.stringify(direct,true) 359 | winform.editPacProxy.modified = true; 360 | winform.editPacDirect.modified = true; 361 | winform.msgInfo('已成功导入 '+count+' 个代理域名,\r\n' 362 | + ignored + '个域名已经是代理域名无需添加。\r\n下一步请点下面的「保存PAC」按钮。',1500); 363 | } 364 | }; 365 | { "重置为默认PAC"; 366 | function(id){ 367 | import xray.pacServer; 368 | xray.pacServer.resetPac(); 369 | winform.loadPacToEdit(xray.pacServer.loadPacText()); 370 | } 371 | }; 372 | { "恢复为当前使用的PAC"; 373 | function(id){ 374 | import xray.pacServer; 375 | winform.loadPacToEdit(xray.pacServer.loadPacText()); 376 | } 377 | }; 378 | } 379 | return items; 380 | }); 381 | 382 | 383 | import win.ui.accelerator; 384 | var accelerator = win.ui.accelerator({ 385 | 386 | { 387 | ctrl = true; vkey = 'F'#; 388 | oncommand = function() findDlgDirect.find(winform.currentEditPac.selText); 389 | }; 390 | 391 | { 392 | ctrl = true; vkey = 'H'#; 393 | oncommand = function() findDlgDirect.replace(winform.currentEditPac.selText); 394 | }; 395 | 396 | },winform ); 397 | 398 | winform.btnDeleteDomain.oncommand = function(id,event){ 399 | var domain = winform.editPacDomain.text; 400 | if(!#domain){ 401 | return winform.editPacDomain.editBox.showErrorTip(,"请先指定域名") 402 | } 403 | 404 | var proxy = web.json.tryParse(string.conv.fromWide(winform.editPacProxy.text)) 405 | var direct = web.json.tryParse(string.conv.fromWide(winform.editPacDirect.text)) 406 | if(!(table.isArray(proxy))) return winform.msgErr("代理域名配置格式错误:不是有效的JSON数组。"); 407 | if(!(table.isArray(direct))) return winform.msgErr("直连域名配置格式错误:不是有效的JSON数组"); 408 | 409 | if(!table.indexOf(proxy,domain) && !table.indexOf(direct,domain) ){ 410 | winform.editPacDomain.editBox.showInfoTip(,"PAC配置不包含此域名") 411 | return; 412 | } 413 | 414 | direct = table.filter(direct,lambda(v) v!=domain); 415 | proxy = table.filter(proxy,lambda(v) v!=domain); 416 | 417 | winform.editPacProxy.text = web.json.stringify(proxy,true); 418 | winform.editPacDirect.text = web.json.stringify(direct,true); 419 | winform.editPacProxy.modified = true; 420 | winform.editPacDirect.modified = true; 421 | winform.btnUpdate.disabled = false; 422 | winform.editPacDomain.editBox.showInfoTip(,"已移除此域名"); 423 | } 424 | 425 | winform.btnAddDomain.oncommand = function(id,event){ 426 | var proxy = web.json.tryParse(string.conv.fromWide(winform.editPacProxy.text)) 427 | var direct = web.json.tryParse(string.conv.fromWide(winform.editPacDirect.text)) 428 | if(!(table.isArray(proxy))) return winform.msgErr("代理域名配置格式错误:不是有效的JSON数组。"); 429 | if(!(table.isArray(direct))) return winform.msgErr("直连域名配置格式错误:不是有效的JSON数组"); 430 | 431 | var domain = winform.editPacDomain.text; 432 | if(!#domain){ 433 | return winform.editPacDomain.editBox.showErrorTip(,"请先指定域名") 434 | } 435 | 436 | if(winform.radioProxy.checked){ 437 | if(!table.indexOf(direct,domain) && table.indexOf(proxy,domain) ){ 438 | winform.editPacDomain.editBox.showWarningTip(,"此域名已经是代理域名,无需添加") 439 | return; 440 | } 441 | 442 | direct = table.filter(direct,lambda(v) v!=domain); 443 | if(!table.indexOf(proxy,domain)){ 444 | table.unshift(proxy,domain) 445 | } 446 | winform.editPacDomain.editBox.showInfoTip(,"已添加到代理域名"); 447 | } 448 | else { 449 | if(table.indexOf(direct,domain) && !table.indexOf(proxy,domain) ){ 450 | winform.editPacDomain.editBox.showWarningTip(,"此域名已经是直连域名,无需添加") 451 | return; 452 | } 453 | 454 | proxy = table.filter(proxy,lambda(v) v!=domain); 455 | if(!table.indexOf(direct,domain)){ 456 | table.unshift(direct,domain) 457 | } 458 | winform.editPacDomain.editBox.showInfoTip(,"已添加到直连域名"); 459 | } 460 | 461 | winform.editPacProxy.text = web.json.stringify(proxy,true) 462 | winform.editPacDirect.text = web.json.stringify(direct,true) 463 | winform.editPacProxy.modified = true; 464 | winform.editPacDirect.modified = true; 465 | winform.btnUpdate.disabled = false; 466 | } 467 | 468 | winform.onClose = function(hwnd,message,wParam,lParam){ 469 | if(winform.editPacDirect.modified||winform.editPacProxy.modified){ 470 | winform.btnUpdate.disabled = true; 471 | 472 | if(!winform.msgAsk("PAC配置已修改但尚未保存,确定要退出并放弃更改吗?")){ 473 | return true; 474 | } 475 | } 476 | } 477 | 478 | winform.onDestroy = function(){ 479 | if(mainForm){ 480 | win.setActive(mainForm.hwnd); 481 | win.setForeground(mainForm.hwnd); 482 | } 483 | } 484 | 485 | import win.clip; 486 | winform.onActivate = function(state,hwndOther,minimized){ 487 | if(state){ 488 | if(#winform.editPacDomain.text){ 489 | return; 490 | } 491 | 492 | var str = win.clip.read(); 493 | if(str){ 494 | var domain = string.match(str,"\w<[\w-_]*\.>+\w{1,8}"); 495 | if(domain){ 496 | winform.editPacDomain.text = domain; 497 | 498 | var proxy = web.json.tryParse(string.conv.fromWide(winform.editPacProxy.text)) 499 | if( (proxy)) { 500 | winform.radioProxy.checked = table.indexOf(proxy,domain); 501 | winform.radioDirect.checked = !winform.radioProxy.checked; 502 | } 503 | } 504 | } 505 | } 506 | } 507 | 508 | import inet.url; 509 | winform.editPacDomain.editBox.onChange = function(){ 510 | var url = owner.text; 511 | if(string.startWith(url,"http:",true) || string.startWith(url,"https:",true) ){ 512 | var tUrl = inet.url.split(url); 513 | if(tUrl && #tUrl.host){ 514 | owner.text = tUrl.host; 515 | owner.setFocus(); 516 | } 517 | } 518 | } 519 | 520 | winform.enableDpiScaling(); 521 | winform.show(); 522 | win.loopMessage(); 523 | 524 | 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官网接口优化http协议访问官网速度( 适用于无代理连接 )。";left=49;top=50;right=635;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.editIpCurrent.text = wsock.getIp("www.github.com") 35 | 36 | winform.plusUpdateIps.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 37 | winform.text = "请稍候,正在测试github.com所有可用IP的响应速度" 38 | 39 | var metaInfo = win.invoke( 40 | function(){ 41 | import win; 42 | import web.rest.jsonClient; 43 | var http = web.rest.jsonClient(); 44 | var github = http.api("https://api.github.com/") 45 | 46 | return github.meta.get() : { git = { 47 | "192.30.252.0"; 48 | "185.199.108.0"; 49 | "140.82.112.0"; 50 | "13.114.40.48"; 51 | "13.229.188.59"; 52 | "13.234.176.102"; 53 | "13.234.210.38"; 54 | "13.236.229.21"; 55 | "13.237.44.5"; 56 | "13.250.177.223"; 57 | "15.164.81.167"; 58 | "18.194.104.89"; 59 | "18.195.85.27"; 60 | "35.159.8.160"; 61 | "52.192.72.89"; 62 | "52.64.108.95"; 63 | "52.69.186.44"; 64 | "52.74.223.119"; 65 | "52.78.231.108" 66 | } }; 67 | } 68 | ) 69 | 70 | if(metaInfo[["git"]]){ 71 | config.meta.data = table.map(metaInfo.git,lambda(v)..string.match(v,"^\d+\.\d+\.\d+\.\d+")) 72 | } 73 | elseif(!config.meta.data){ 74 | winform.msgboxErr("您的操作系统版本过低") 75 | } 76 | 77 | var pingThread = function( ip ) { 78 | import wsock.tcp.client; 79 | 80 | var result = 0; 81 | for(i=1;4;1){ 82 | 83 | var beginTick = ..time.tick() 84 | var tcp = wsock.tcp.client() 85 | 86 | tcp.setTimeouts(1000,1000); 87 | if( ! tcp.connectTimeout(ip,80,2) ){ 88 | return; 89 | }; 90 | 91 | sendData =/*********** 92 | GET / HTTP/1.1 93 | Host: www.github.com 94 | Connection: close 95 | User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) 96 | Accept: */*; 97 | Accept-Language: zh-CN,zh; 98 | Accept-Charset:utf-8; 99 | ***********/ 100 | tcp.write( sendData + '\r\n\r\n' ) 101 | 102 | var rep = tcp.readAll(); 103 | tcp.close(); 104 | 105 | if( rep && ..string.startWith(rep,"HTTP/1.1 301 Moved Permanently") ){ 106 | result = result + ((..time.tick() - beginTick)/1000); 107 | } 108 | else { 109 | return; 110 | } 111 | 112 | } 113 | 114 | return ip,math.round(result/4,2); 115 | 116 | } 117 | 118 | import thread.manage; 119 | manage = thread.manage(#config.meta.data) 120 | 121 | var ipData = {} 122 | for(k,ip in config.meta.data){ 123 | manage.createSuspended(pingThread,ip).onEnd = function(ip,sec){ 124 | if(ip && sec!==null ) { 125 | table.push(ipData,ip + " 响应速度:" + sec + "秒"); 126 | winform.cmbIpCidrs.items = ipData; 127 | winform.cmbIpCidrs.selIndex = 1; 128 | } 129 | } 130 | } 131 | 132 | manage.resume(); 133 | manage.waitClose(); 134 | 135 | winform.cmbIpCidrs.items = ipData; 136 | winform.cmbIpCidrs.selIndex = 1; 137 | winform.cmbIpCidrs.onListChange(); 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.execute("notepad.exe",fsys.hosts.path,"runas") 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/qr.aardio: -------------------------------------------------------------------------------- 1 | import win.ui; 2 | import win.ui.atom; 3 | import fonts.fontAwesome; 4 | /*DSG{{*/ 5 | var winform = win.form(text="winXray - 二维码生成工具";right=730;bottom=678;bgcolor=16777215;border="none";exmode="none";mode="popup") 6 | winform.add( 7 | bk={cls="bk";left=-6;top=-2;right=734;bottom=27;bgcolor=12632256;z=10}; 8 | btnQrEnode={cls="plus";text="生成二维码";left=562;top=565;right=694;bottom=595;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=9}; 9 | editUrl={cls="plus";left=44;top=569;right=537;bottom=594;align="right";bgcolor=16777215;border={bottom=1;color=-8355712};db=1;dl=1;dr=1;editable="edit";font=LOGFONT(h=-16);textPadding={bottom=1};z=8}; 10 | lbErrLevel={cls="static";text=" 0:L 可纠错7%数据码字";left=104;top=656;right=272;bottom=679;db=1;dl=1;transparent=1;z=6}; 11 | lbVersion={cls="static";text="自动选择版本";left=466;top=656;right=672;bottom=679;db=1;dr=1;transparent=1;z=7}; 12 | plus={cls="plus";left=28;top=49;right=708;bottom=522;bgcolor=16777215;db=1;dl=1;dr=1;dt=1;repeat="scale";z=1}; 13 | static={cls="static";text="纠错级别:";left=13;top=624;right=97;bottom=651;align="right";db=1;dl=1;transparent=1;z=3}; 14 | static2={cls="static";text="版本(大小):";left=353;top=624;right=437;bottom=651;align="right";db=1;dl=1;dr=1;transparent=1;z=5}; 15 | static3={cls="static";text="winXray - 二维码生成工具";left=17;top=4;right=230;bottom=24;color=3947580;transparent=1;z=11}; 16 | tbErrLevel={cls="trackbar";left=96;top=611;right=290;bottom=641;bgcolor=16777215;db=1;dl=1;max=3;min=0;z=2}; 17 | tbVersion={cls="trackbar";left=439;top=611;right=703;bottom=641;bgcolor=16777215;db=1;dr=1;max=40;min=0;z=4} 18 | ) 19 | /*}}*/ 20 | 21 | import style; 22 | winform.btnQrEnode.skin(style.primaryButton); 23 | 24 | winform.tbErrLevel.oncommand = function(id,event,pos){ 25 | 26 | if( event == 0x8/*_TB_ENDTRACK*/ ){ 27 | pos = winform.tbErrLevel.pos; 28 | var v = {[0]="L 可纠错7%数据码字";[1]="M 可纠错15%的数据码字";[2]="Q 可纠错25%的数据码字";[3]="H 可纠错30%的数据码字";} 29 | winform.lbErrLevel.text = pos +":"+ v[pos]; 30 | } 31 | } 32 | 33 | winform.tbVersion.oncommand = function(id,event,pos){ 34 | if( event == 0x8/*_TB_ENDTRACK*/ ){ 35 | pos = winform.tbVersion.pos; 36 | if(!pos)winform.lbVersion.text = "自动选择版本"; 37 | else { 38 | var width = ((pos-1)*4)+21; 39 | winform.lbVersion.text = string.format("版本:%d 二维码数据大小:%d x %d",pos,width,width ); 40 | } 41 | } 42 | } 43 | 44 | import win.dlg.message; 45 | import qrencode.bitmap; 46 | winform.btnQrEnode.oncommand = function(id,event){ 47 | if(!#winform.editUrl.text){ 48 | return winform.msgErr("请先输入内容") 49 | } 50 | 51 | winform.plus.hide = true; 52 | var qrBmp = qrencode.bitmap(winform.editUrl.text,winform.tbVersion.pos,winform.tbErrLevel.pos ); 53 | if(!qrBmp){ 54 | return winform.msgErr("请先输入内容") 55 | } 56 | winform.plus.setBackground(qrBmp.copyBitmap(winform.plus.width)); 57 | 58 | winform.plus.hide = false; 59 | winform.plus.redraw() 60 | } 61 | 62 | winform.createQrCode = function(url){ 63 | winform.editUrl.text = url; 64 | winform.btnQrEnode.oncommand(); 65 | } 66 | 67 | import win.ui.simpleWindow2; 68 | win.ui.simpleWindow2(winform); 69 | 70 | winform.show() 71 | win.loopMessage(); 72 | -------------------------------------------------------------------------------- /forms/main/tools/tools.aardio: -------------------------------------------------------------------------------- 1 | import fonts.fontAwesome; 2 | import win.ui; 3 | /*DSG{{*/ 4 | var winform = win.form(text="aardio form";right=938;bottom=638;bgcolor=16777215) 5 | winform.add( 6 | btnAardio={cls="plus";text="aardio 桌面软件快速开发( 开发环境仅 6.5MB )";left=26;top=579;right=460;bottom=621;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 | btnCreateLnk={cls="plus";text="创建桌面图标";left=26;top=438;right=205;bottom=480;align="left";color=3947580;dl=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-23;name='FontAwesome');padding={left=18}};iconText='\uF108';notify=1;textPadding={left=50};z=11}; 8 | btnDeleteIconCache={cls="plus";text="修复桌面图标空白";left=26;top=485;right=269;bottom=527;align="left";color=3947580;dl=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-23;name='FontAwesome');padding={left=18}};iconText='\uF0AD';notify=1;textPadding={left=50};z=17}; 9 | btnDnsFlush={cls="plus";text="清空DNS缓存";left=26;top=202;right=412;bottom=244;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}; 10 | btnGitHub={cls="plus";text="github 网速优化";left=26;top=296;right=262;bottom=338;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}; 11 | btnQrCode={cls="plus";text="二维码生成工具";left=26;top=344;right=262;bottom=386;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='\uF029';notify=1;textPadding={left=50};z=15}; 12 | btnSshInstall={cls="plus";text="一键安装 Xray 服务端";left=393;top=14;right=601;bottom=56;align="left";color=3947580;dl=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-27;name='FontAwesome');padding={left=11}};iconText='\uF17C';notify=1;textPadding={left=40};z=10}; 13 | btnSshInstallKey={cls="plus";text="安装SSH密钥";left=606;top=15;right=755;bottom=57;align="left";color=3947580;dl=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-24;name='FontAwesome');padding={left=11}};iconText='\uF084';notify=1;textPadding={left=40};z=12}; 14 | btnTestSpeed={cls="plus";text="当前代理服务器测速";left=26;top=61;right=244;bottom=103;align="left";color=3947580;dl=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-24;name='FontAwesome');padding={left=18}};iconText='\uF0ED';notify=1;textPadding={left=50};z=13}; 15 | btnUpdateSystemTime={cls="plus";text="同步系统时间( 修正 Xray 连接报错 )";left=26;top=155;right=412;bottom=197;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}; 16 | btnUpdateXrayCore={cls="plus";text="更新 Xray Core";left=26;top=108;right=269;bottom=150;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='\uF01A';notify=1;textPadding={left=50};z=6}; 17 | btnUuid={cls="plus";text="UUID 生成工具";left=26;top=391;right=262;bottom=433;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='\uF13E';notify=1;textPadding={left=50};z=16}; 18 | btnWubiLex={cls="plus";text="WIN10 五笔助手(仅830KB,自带五笔86、98、091、新世纪、郑码……)";left=26;top=532;right=661;bottom=574;align="left";color=3947580;dl=1;dt=1;font=LOGFONT(h=-16);iconStyle={align="left";font=LOGFONT(h=-24;name='FontAwesome');padding={left=18}};iconText='\uF11C';notify=1;textPadding={left=50};z=7}; 19 | btnYoutube={cls="plus";text="youtube 视频下载工具";left=26;top=249;right=262;bottom=291;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}; 20 | editSshHost={cls="plus";left=158;top=23;right=386;bottom=47;align="left";bgcolor=16777215;border={bottom=1;color=-8355712};dl=1;dt=1;editable="edit";font=LOGFONT(h=-16);textPadding={bottom=1};z=8}; 21 | lbCurrentProxyLocation={cls="static";left=245;top=74;right=729;bottom=101;color=32768;dl=1;dt=1;transparent=1;z=14}; 22 | static={cls="static";text="账号@服务器:";left=36;top=27;right=151;bottom=54;align="right";color=4934475;dl=1;dt=1;font=LOGFONT(h=-13);transparent=1;z=9} 23 | ) 24 | /*}}*/ 25 | 26 | import style; 27 | winform.btnYoutube.skin(style.plainButton); 28 | 29 | var frmYoutube; 30 | winform.btnYoutube.oncommand = function(id,event){ 31 | if(frmYoutube&&win.isWindow(frmYoutube.hwnd)){ 32 | if(win.isIconic(frmYoutube.hwnd)){ 33 | frmYoutube.show(9/*_SW_RESTORE*/); 34 | } 35 | 36 | frmYoutube.show(); 37 | return; 38 | } 39 | 40 | frmYoutube = winform.loadForm("\forms\main\tools\youtube.aardio"); 41 | frmYoutube.show(); 42 | } 43 | 44 | import process; 45 | winform.btnGitHub.skin(style.plainButton) 46 | winform.btnGitHub.oncommand = function(id,event){ 47 | if(_STUDIO_INVOKED){ 48 | winform.msgboxErr("请在发布后运行此功能"); 49 | return; 50 | } 51 | 52 | process.execute(io._exepath,"/github","runas"); 53 | } 54 | 55 | winform.btnAardio.skin(style.plainButton) 56 | winform.btnAardio.oncommand = function(id,event){ 57 | process.openUrl("http://www.aardio.com") 58 | } 59 | 60 | winform.btnUpdateSystemTime.skin(style.plainButton) 61 | winform.btnUpdateSystemTime.oncommand = function(id,event){ 62 | if(_STUDIO_INVOKED){ 63 | winform.msgboxErr("请在发布后运行此功能"); 64 | return; 65 | } 66 | 67 | winform.btnUpdateSystemTime.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 68 | if( process.executeWait(io._exepath,"/updateTime","runas") ){ 69 | winform.btnUpdateSystemTime.disabledText = null; 70 | winform.btnUpdateSystemTime.text = '时间已同步' 71 | winform.setTimeout( 72 | function(){ 73 | winform.btnUpdateSystemTime.text = '同步系统时间( 修正 xray 连接报错 )' 74 | },2000) 75 | } 76 | else { 77 | winform.btnUpdateSystemTime.disabledText = null; 78 | } 79 | } 80 | 81 | winform.btnDnsFlush.skin(style.plainButton) 82 | winform.btnDnsFlush.oncommand = function(id,event){ 83 | winform.btnDnsFlush.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 84 | 85 | import process.popen; 86 | var prcs = process.popen(,"ipconfig /flushdns"); 87 | var str = prcs.read(-1) 88 | 89 | var dnsapi = ..raw.loadDll("dnsapi.dll"); 90 | dnsapi.DnsFlushResolverCacheB(); 91 | 92 | winform.msgInfo(str); 93 | winform.btnDnsFlush.disabledText = null; 94 | } 95 | 96 | winform.btnUpdateXrayCore.skin(style.plainButton) 97 | winform.btnUpdateXrayCore.oncommand = function(id,event){ 98 | winform.btnUpdateXrayCore.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}; 99 | 100 | import xray.core; 101 | var versionTag = xray.core.updateCore(); 102 | if( versionTag ){ 103 | publish("uiCommand.restartXrayCore"); 104 | winform.btnUpdateXrayCore.disabledText = null; 105 | winform.msgOk("Xray Core 已更新到:" + versionTag,1500) 106 | } 107 | else { 108 | winform.btnUpdateXrayCore.disabledText = null; 109 | } 110 | } 111 | 112 | import process; 113 | winform.btnWubiLex.skin(style.plainButton) 114 | winform.btnWubiLex.oncommand = function(id,event){ 115 | process.openUrl("http://wubi.aardio.com/"); 116 | } 117 | 118 | winform.editSshHost.setCueBannerText("root@0.0.0.0:22") 119 | winform.btnSshInstall.skin(style.plainButton) 120 | winform.btnSshInstall.oncommand = function(id,event){ 121 | 122 | if(_STUDIO_INVOKED){ 123 | winform.msgErr("请在发布后运行此功能"); 124 | return; 125 | } 126 | 127 | config.ui.sshServer = winform.editSshHost.text; 128 | config.ui.save(); 129 | 130 | winform.btnSshInstall.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 131 | 132 | var server = winform.editSshHost.text; 133 | if( ! ..string.indexOf(server,"@") ){ 134 | server = "root@" + server; 135 | } 136 | process.execute(io._exepath,"/sshInstall " + server); 137 | win.delay(1000); 138 | winform.btnSshInstall.disabledText = null; 139 | } 140 | 141 | import config; 142 | if(config.ui.sshServer){ 143 | winform.editSshHost.text = config.ui.sshServer; 144 | } 145 | 146 | winform.btnSshInstallKey.skin(style.plainButton) 147 | winform.btnSshInstallKey.oncommand = function(id,event){ 148 | if(_STUDIO_INVOKED){ 149 | winform.msgErr("请在发布后运行此功能"); 150 | return; 151 | } 152 | 153 | config.ui.sshServer = winform.editSshHost.text; 154 | config.ui.save(); 155 | 156 | winform.btnSshInstall.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 157 | 158 | var server = winform.editSshHost.text; 159 | if( ! ..string.indexOf(server,"@") ){ 160 | server = "root@" + server; 161 | } 162 | process.execute(io._exepath,"/sshInstallKey " + server); 163 | win.delay(1000); 164 | winform.btnSshInstall.disabledText = null; 165 | } 166 | 167 | winform.btnCreateLnk.skin(style.plainButton) 168 | winform.btnCreateLnk.oncommand = function(id,event){ 169 | if(_STUDIO_INVOKED){ 170 | return winform.msgboxErr("请在发布后使用此功能") 171 | } 172 | 173 | import fsys.lnk; 174 | var lnk = fsys.lnk(); 175 | lnk.description = "winXray(Xray/V2Ray、Shadowsocks、Trojan通用客户端 )" 176 | 177 | lnk.path = io._exepath //设置目标路径 178 | lnk.setIcon(io._exepath,0); //设置图标 179 | 180 | lnk.save( 181 | io.getSpecial(0/*_CSIDL_DESKTOP*/,"winXray.lnk" ) 182 | ) 183 | 184 | ::Shell32.SHChangeNotify(0x8000000/*_SHCNE_ASSOCCHANGED*/,0,0,0); 185 | 186 | import com; 187 | com.CreateObject("Shell.Application").MinimizeAll(); 188 | } 189 | 190 | winform.btnTestSpeed.skin(style.plainButton) 191 | winform.btnTestSpeed.oncommand = function(id,event){ 192 | import chrome.path; 193 | var chromePath = chrome.path(); 194 | if(!chromePath){ 195 | winform.msgFrown('抱歉,\r\n测速工具需要调用Chrome或兼容浏览器才能运行,\r\n但是当前系统未找到任何Chrome兼容浏览器!'); 196 | return; 197 | } 198 | 199 | import xray.core; 200 | if(!xray.core.socksProxyPort){ 201 | winform.msgFrown('抱歉,当前未连接到任何代理服务器!'); 202 | return; 203 | } 204 | 205 | winform.btnTestSpeed.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 206 | 207 | var ipip = win.invoke( 208 | function(port){ 209 | import web.rest.client; 210 | var restClient = web.rest.client(,"SOCKS=127.0.0.1:"+port); 211 | restClient._http.setTimeouts(1000,2000,2000); 212 | restClient.get("https://www.google.com/generate_204"); 213 | var lastStatusCode = restClient.lastStatusCode; 214 | 215 | if( lastStatusCode == 204 ){ 216 | 217 | var ipip,err = restClient.get("http://myip.ipip.net"); 218 | if(ipip){ 219 | ipip = ..string.match(ipip,"\d+\.\d+\.\d+\.\d+\A+"); 220 | if(ipip) { 221 | restClient.close(); 222 | return "当前代理服务器:"+ipip; 223 | } 224 | } 225 | 226 | restClient.close(); 227 | return null; 228 | } 229 | restClient.close(); 230 | },xray.core.socksProxyPort 231 | ) 232 | 233 | if(!ipip){ 234 | winform.msgFrown('抱歉,当前未连接到任何代理服务器!'); 235 | winform.btnTestSpeed.disabledText = null; 236 | return; 237 | } 238 | 239 | winform.lbCurrentProxyLocation.text = ipip; 240 | 241 | import process 242 | process.execute(chromePath,` --proxy-server="SOCKS5://127.0.0.1:`+xray.core.socksProxyPort 243 | +`" --user-data-dir="` +io.appData("winXray/chrome-socks5")+ `" --app="https://speed.cloudflare.com/"`); 244 | winform.btnTestSpeed.disabledText = null; 245 | } 246 | 247 | onActiveOutbound = function(address){ 248 | winform.btnTestSpeed.disabled = !address; 249 | if(type.isString(address)){ 250 | winform.lbCurrentProxyLocation.text = "当前活动的代理服务器:" + address; 251 | } 252 | } 253 | 254 | subscribe("activeOutbound",function(address){ 255 | onActiveOutbound(address) 256 | } ) 257 | if(globalActiveOutbound){ 258 | onActiveOutbound(globalActiveOutbound) 259 | } 260 | 261 | winform.btnQrCode.skin(style.plainButton) 262 | winform.btnQrCode.oncommand = function(id,event){ 263 | 264 | var frmChild = winform.loadForm("\forms\main\tools\qr.aardio"); 265 | frmChild.show(); 266 | 267 | } 268 | 269 | winform.btnUuid.skin(style.plainButton) 270 | winform.btnUuid.oncommand = function(id,event){ 271 | var frmChild = winform.loadForm("\forms\main\tools\uuid.aardio"); 272 | frmChild.show(); 273 | } 274 | 275 | winform.btnDeleteIconCache.skin(style.plainButton) 276 | winform.btnDeleteIconCache.oncommand = function(id,event){ 277 | import fsys; 278 | import process; 279 | 280 | var explorerPath = process.kill("explorer.exe") 281 | if( explorerPath ) { 282 | fsys.delete(io.appData("iconcache.db")); 283 | process.execute(explorerPath); 284 | 285 | ::Shell32.SHChangeNotify(0x8000000/*_SHCNE_ASSOCCHANGED*/,0,0,0); 286 | } 287 | } 288 | 289 | winform.enableDpiScaling(); 290 | winform.show(); 291 | win.loopMessage(); -------------------------------------------------------------------------------- /forms/main/tools/uuid.aardio: -------------------------------------------------------------------------------- 1 | //GUID生成器 2 | import win.ui; 3 | import win.ui.menu; 4 | import win.guid; 5 | import win.clip; 6 | /*DSG{{*/ 7 | var winform = win.form(text="UUID 生成器 - 已生成新的UUID";right=550;bottom=91;border="dialog frame";exmode="none";max=false;parent=...) 8 | winform.add( 9 | btnCpy={cls="button";text="复制到剪贴板";left=436;top=21;right=529;bottom=47;z=3}; 10 | btnGenerant={cls="button";text="生成UUID";left=339;top=21;right=432;bottom=47;z=2}; 11 | static={cls="static";text="UUID理论上能产生全球唯一的值";left=37;top=63;right=543;bottom=80;transparent=1;z=4}; 12 | txtGuid={cls="edit";left=31;top=21;right=327;bottom=47;edge=1;multiline=1;z=1} 13 | ) 14 | /*}}*/ 15 | 16 | winform.btnCpy.oncommand = function(id,event){ 17 | win.clip.write(winform.txtGuid.text) 18 | } 19 | 20 | winform.btnGenerant.oncommand = function(id,event){ 21 | winform.txtGuid.text = string.lower( tostring(win.guid.create() )); 22 | } 23 | winform.btnGenerant.oncommand(); 24 | 25 | winform.enableDpiScaling(); 26 | winform.show(true) 27 | win.loopMessage(); 28 | return winform; 29 | -------------------------------------------------------------------------------- /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/xray.aardio: -------------------------------------------------------------------------------- 1 | import win.ui; 2 | import fonts.fontAwesome; 3 | /*DSG{{*/ 4 | var frmXray = win.form(text="winXray ";right=959;bottom=591;bgcolor=16777215) 5 | frmXray.add( 6 | btnEditPac={cls="plus";text="编辑PAC";left=332;top=366;right=438;bottom=396;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}; 7 | btnImportServerFromClipBd={cls="plus";text="批量导入链接";left=459;top=366;right=592;bottom=396;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}; 8 | btnTcping={cls="plus";text="检测并连接最快服务器";left=735;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}; 9 | chkAutoTest={cls="plus";text="异常自动重连 ";left=598;top=366;right=729;bottom=396;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}; 10 | 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}; 11 | 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}; 12 | radioProxy={cls="radiobutton";text="全局代理";left=129;top=369;right=226;bottom=395;bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-13);z=4}; 13 | radioProxyDirect={cls="radiobutton";text="不使用代理";left=14;top=369;right=120;bottom=395;bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-13);z=6}; 14 | radioProxyPac={cls="radiobutton";text="PAC代理";left=234;top=369;right=321;bottom=395;bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-13);z=5} 15 | ) 16 | /*}}*/ 17 | 18 | frmXray.listview.insertColumn("类型",100) 19 | frmXray.listview.insertColumn("服务器",160) 20 | frmXray.listview.insertColumn("端口",70) 21 | frmXray.listview.insertColumn("加密",90) 22 | frmXray.listview.insertColumn("协议",50) 23 | frmXray.listview.insertColumn("备注",190) 24 | frmXray.listview.insertColumn("响应速度",95) 25 | frmXray.listview.insertColumn("状态",-1) 26 | frmXray.show(); 27 | 28 | import config; 29 | import xray.core; 30 | var currentOutboundIndex,currentOutboundAddress; 31 | var activeOutbound = function(outboundIndex,outboundAddress,enableRetry){ 32 | if(!outboundIndex && !outboundAddress){ 33 | outboundIndex,outboundAddress = currentOutboundIndex,currentOutboundAddress; 34 | if(!outboundIndex && !outboundAddress) return; 35 | } 36 | 37 | var outbound = config.proxy.outbounds[outboundIndex]; 38 | if(!outbound){ 39 | return ; 40 | } 41 | 42 | var currentTesting = false; 43 | if( ! xray.core.isInboundPortChanged() ){ 44 | currentTesting = currentOutboundIndex == outboundIndex && currentOutboundAddress == outboundAddress 45 | } 46 | 47 | var startTesting = currentTesting; 48 | if(!startTesting){ 49 | var started,err = xray.core.restart(frmXray.edit,outbound); 50 | startTesting = started; 51 | if(err){ 52 | frmXray.edit.print("启动 Xray 错误:",err); 53 | return false; 54 | } 55 | } 56 | 57 | if( startTesting ){ 58 | currentOutboundIndex,currentOutboundAddress = outboundIndex,outboundAddress; 59 | frmXray.autoTesting = true; 60 | 61 | ..thread.invoke( 62 | function(frmXray,outboundIndex,outboundAddress,sockProxyPort,httpProxyAddress,enableRetry,currentTesting){ 63 | import web.rest.client; 64 | var restClient = web.rest.client(,httpProxyAddress); 65 | restClient._http.setTimeouts(1000,2000,2000); 66 | restClient.get("https://www.google.com/generate_204"); 67 | var lastStatusCode = restClient.lastStatusCode; 68 | if( lastStatusCode == 204 ){ 69 | frmXray.onStartXrayComplete(outboundIndex,currentTesting,outboundAddress); 70 | return; 71 | } 72 | restClient.close(); 73 | 74 | if(currentTesting){ 75 | sleep(500); 76 | } 77 | 78 | import inet.http; 79 | if(inet.http.isAlive(,httpProxyAddress)){ 80 | frmXray.onStartXrayComplete(outboundIndex,currentTesting,outboundAddress); 81 | return; 82 | } 83 | 84 | if(currentTesting){ 85 | sleep(500); 86 | } 87 | 88 | import wsock.tcp.socks5Client; 89 | 90 | var client; 91 | for(i=1;2;1){ 92 | sleep(500); 93 | client = wsock.tcp.socks5Client("127.0.0.1",sockProxyPort); 94 | if(client)break; 95 | } 96 | 97 | if(client){ 98 | client.setTimeouts(1000,1000); 99 | if(client.connect("www.google.com",80) ){ 100 | 101 | var sendData =/*********** 102 | GET / HTTP/1.1 103 | Host: www.google.com 104 | Connection: close 105 | User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) 106 | Accept: */*; 107 | Accept-Language: zh-CN,zh; 108 | Accept-Charset:utf-8; 109 | ***********/ 110 | client.write( sendData + '\r\n\r\n' ) 111 | var rep = client.read(9); 112 | client.close(); 113 | 114 | if( rep && ..string.startWith(rep,"HTTP/1.1 ") ){ 115 | frmXray.onStartXrayComplete(outboundIndex,currentTesting,outboundAddress); 116 | return; 117 | } 118 | } 119 | }; 120 | frmXray.onStartXrayFailed(outboundIndex,enableRetry,outboundAddress); 121 | },frmXray,outboundIndex,outboundAddress, 122 | xray.core.socksProxyPort,xray.core.getHttpProxyAddress(),enableRetry,currentTesting 123 | ) 124 | 125 | return true; 126 | } 127 | } 128 | 129 | var autoUpdateSubscriptionTimerId; 130 | frmXray.autoUpdateSubscription = function(timeout){ 131 | ..win.clearTimeout(autoUpdateSubscriptionTimerId); 132 | if(timeout===-1){ 133 | autoUpdateSubscriptionTimerId = null; 134 | return; 135 | } 136 | 137 | if(config.proxy.autoRefreshSubscription){ 138 | autoUpdateSubscriptionTimerId = ..win.setTimeout(function(){ 139 | autoUpdateSubscriptionTimerId = null; 140 | 141 | import xray.outbounds; 142 | xray.outbounds.autoUpdateSubscription(); 143 | },timeout:10000) 144 | } 145 | } 146 | 147 | frmXray.onStartXrayComplete = function(outboundIndex,currentTesting,outboundAddress){ 148 | frmXray.autoUpdateSubscription(-1); 149 | 150 | var outbound = config.proxy.outbounds[outboundIndex]; 151 | var outbound = ..config.proxy.outbounds[outboundIndex]; 152 | if(!(outbound && (outbound.address == outboundAddress))){ 153 | return; 154 | } 155 | 156 | var listview = frmXray.listview; 157 | listview.ensureVisible(outboundIndex); 158 | for b,v in table.eachIndex(config.proxy.outbounds){ 159 | listview.setItemText("",b,8); 160 | } 161 | 162 | frmXray.autoTesting = false; 163 | listview.setItemText("活动服务器:正常",outboundIndex,8); 164 | listview.activeOutboundIndex = outboundIndex; 165 | 166 | var address = listview.getItemText(outboundIndex,2); 167 | if(!currentTesting) frmXray.edit.print("已切换到服务器:",address); 168 | listview.activeOutboundAddress = address; 169 | 170 | if(listview.getItemText(outboundIndex,7)=="不可用"){ 171 | listview.setItemText("",outboundIndex,7); 172 | } 173 | 174 | frmXray.onTcpingReturn = function(outboundIndex,address,succeeded,speedText){ 175 | if(address==listview.getItemText(outboundIndex,2)){ 176 | listview.setItemText(speedText,outboundIndex,7); 177 | } 178 | }; 179 | frmXray.validOutbounds = {}; 180 | 181 | frmXray.btnTcping.disabledText = null; 182 | ..publish("activeOutbound",address); 183 | } 184 | 185 | subscribe("activeOutbound",function(address){ 186 | ..globalActiveOutbound = address; 187 | } ) 188 | 189 | frmXray.onStartXrayFailed = function(outboundIndex,enableRetry,outboundAddress){ 190 | 191 | ..publish("activeOutbound",false); 192 | 193 | var outbound = ..config.proxy.outbounds[outboundIndex]; 194 | if(!(outbound && (outbound.address == outboundAddress))){ 195 | return; 196 | } 197 | 198 | if(outbound.subscribeUrl){ 199 | frmXray.autoUpdateSubscription(); 200 | } 201 | 202 | var listview = frmXray.listview; 203 | listview.ensureVisible(outboundIndex); 204 | if(listview.activeOutboundIndex==outboundIndex){ 205 | listview.activeOutboundIndex=null; 206 | listview.activeOutboundAddress = null; 207 | } 208 | 209 | listview.setItemText("不可用",outboundIndex,7); 210 | if(enableRetry){ 211 | if(#frmXray.validOutbounds){ 212 | var next = table.shift(frmXray.validOutbounds); 213 | activeOutbound(next[1],next[2],#frmXray.validOutbounds); 214 | listview.setItemText("异常,测试下一个...",outboundIndex,8); 215 | frmXray.edit.print("服务器异常:",listview.getItemText(outboundIndex,2)); 216 | frmXray.edit.print("正在尝试连接:",next[2]); 217 | } 218 | else { 219 | frmXray.btnTcping.oncommand(); 220 | } 221 | } 222 | else { 223 | listview.setItemText("活动服务器:异常",outboundIndex,8); 224 | frmXray.edit.print("已切换到服务器:",listview.getItemText(outboundIndex,2)); 225 | frmXray.autoTesting = false; 226 | } 227 | 228 | frmXray.btnTcping.disabledText = null; 229 | } 230 | 231 | var tcping = function(frmXray,outboundIndex,outbound){ 232 | import wsock.tcp.client; 233 | var timeout = 0; 234 | var failed = 0; 235 | for(i=1;8;1){ 236 | sleep(1); 237 | 238 | var client = wsock.tcp.client(); 239 | var tickBegin = time.tick(); 240 | if(client.connectTimeout(outbound.address,tonumber(outbound.port),3)){ 241 | var ret = client.send("."); 242 | client.close(); 243 | 244 | if( ret != 1){ 245 | frmXray.onTcpingReturn(outboundIndex,outbound.address,false,"不可用"); 246 | return; 247 | } 248 | timeout = timeout + (time.tick() - tickBegin); 249 | } 250 | else { 251 | client.close(); 252 | 253 | failed++; 254 | if(failed>1){ 255 | frmXray.onTcpingReturn(outboundIndex,outbound.address,false,"不可用"); 256 | return; 257 | } 258 | } 259 | } 260 | 261 | frmXray.onTcpingReturn(outboundIndex,outbound.address,true,math.round(timeout / (8-failed) / 1000,2)+"秒"); 262 | }; 263 | 264 | var retryOnNetworkAliveTimerId; 265 | frmXray.onTcpingFailed = function(){ 266 | frmXray.autoTesting = false; 267 | frmXray.btnTcping.disabledText = null; 268 | 269 | if(retryOnNetworkAliveTimerId){ 270 | frmXray.clearTimeout(retryOnNetworkAliveTimerId); 271 | } 272 | 273 | if(inet.http.isAlive(,false)){ 274 | frmXray.autoUpdateSubscription(1000); 275 | } 276 | else { 277 | retryOnNetworkAliveTimerId = frmXray.setInterval( 278 | 1000,function(){ 279 | if(frmXray.listview.activeOutboundIndex){ 280 | retryOnNetworkAliveTimerId = null; 281 | return 0; 282 | } 283 | 284 | if(inet.http.isAlive(,false)){ 285 | frmXray.btnTcping.oncommand(); 286 | retryOnNetworkAliveTimerId = null; 287 | return false; 288 | } 289 | } 290 | ) 291 | } 292 | } 293 | 294 | import sysProxy; 295 | import style; 296 | frmXray.btnTcping.skin(style.primaryButton); 297 | frmXray.btnTcping.oncommand = function(id,event){ 298 | frmXray.listview.activeOutboundIndex = null; 299 | frmXray.listview.activeOutboundAddress = null; 300 | frmXray.listview.clearColumnImage(); 301 | 302 | var items = frmXray.listview.items; 303 | if(#items){ 304 | frmXray.btnTcping.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 305 | if(!xray.core.getPath()){ 306 | frmXray.btnTcping.disabledText = null; 307 | frmXray.msgErr("下载 xray.exe 失败") 308 | return; 309 | } 310 | 311 | var failedCount = 0; 312 | frmXray.onTcpingReturn = function(outboundIndex,address,succeeded,speedText){ 313 | if(address!=frmXray.listview.getItemText(outboundIndex,2)){ 314 | return; 315 | } 316 | frmXray.listview.setItemText(speedText,outboundIndex,7); 317 | 318 | if(succeeded){ 319 | frmXray.listview.setItemText(speedText,outboundIndex,7); 320 | frmXray.validOutbounds = {} 321 | frmXray.onTcpingReturn = function(outboundIndex,address,succeeded,speedText){ 322 | if(address!=frmXray.listview.getItemText(outboundIndex,2)){ 323 | return; 324 | } 325 | 326 | frmXray.listview.setItemText(speedText,outboundIndex,7); 327 | if(succeeded) { 328 | table.push(frmXray.validOutbounds,{outboundIndex;address}); 329 | } 330 | else{ 331 | failedCount++; 332 | if(failedCount==#items) { 333 | frmXray.onTcpingFailed(); 334 | } 335 | } 336 | } 337 | activeOutbound(outboundIndex,address,#items>1); 338 | } 339 | else{ 340 | failedCount++; 341 | if(failedCount==#items) { 342 | frmXray.onTcpingFailed(); 343 | } 344 | } 345 | } 346 | 347 | thread.createSuspended(true); 348 | 349 | var handles = {}; 350 | var outbounds = config.proxy.outbounds; 351 | var lenPing = 0; 352 | var listview = frmXray.listview; 353 | for(i=1;#items;1){ 354 | if(outbounds[i].autoConnect===false){ 355 | listview.setItemText("禁止测速",i,7); 356 | continue; 357 | } 358 | 359 | if(lenPing>config.proxy.maxTestServers){ 360 | frmXray.edit.print("测速服务器数超出设定上限:"+config.proxy.maxTestServers + ",您可在「配置 / 高级设置」中修改此值。") 361 | break; 362 | } 363 | lenPing++; 364 | 365 | listview.setItemText("...",i,7); 366 | listview.setItemText("",i,8); 367 | 368 | var h = thread.create(tcping,frmXray,i,config.proxy.outbounds[i]); 369 | table.push(handles,h); 370 | } 371 | thread.createSuspended(false); 372 | 373 | for(i=1;#handles;1){ 374 | var h = handles[i]; 375 | thread.resume(h); 376 | raw.closehandle(h); 377 | } 378 | 379 | if(#handles) ..thread.waitOne(handles); 380 | } 381 | else { 382 | if(id) frmXray.msgWarn("请先添加服务器") 383 | xray.core.stop(); 384 | 385 | frmXray.btnTcping.disabledText = null; 386 | frmXray.autoTesting = false; 387 | frmXray.onTcpingReturn = function(outboundIndex,address,succeeded,speedText){}; 388 | } 389 | } 390 | 391 | import xray.core; 392 | import xray.pacServer; 393 | frmXray.onDestroy = function(){ 394 | xray.core.stop(); 395 | xray.pacServer.stop(); 396 | } 397 | 398 | var pingThread = function( ip,frmXray ) { 399 | import icmp.ping; 400 | var ping = icmp.ping(); 401 | 402 | for(i=1;4;1){ 403 | if( ping.perform(ip) ){ 404 | frmXray.edit.print(ip,string.format( "Reply from %s: bytes=%d time=%dms TTL=%d" 405 | , ping.ip , #ping.requestData , ping.echoReply.roundTripTime , ping.echoReply.options.ttl ) ); 406 | } 407 | else { 408 | frmXray.edit.print(ip,"Request timed out"); 409 | } 410 | 411 | } 412 | } 413 | 414 | var removeOutbounds = function(selectedItems){ 415 | 416 | //避免后台线程改变activeIndex 417 | frmXray.onTcpingReturn = function(outboundIndex,address,succeeded,speedText){}; 418 | frmXray.btnTcping.disabledText = null; 419 | frmXray.autoTesting = false; 420 | 421 | var activeIndex = frmXray.listview.activeOutboundIndex; 422 | var outbounds = config.proxy.outbounds; 423 | for(i=#outbounds;1;-1){ 424 | var ob = outbounds[i]; 425 | if(activeIndex==i){ 426 | ob.active = true; 427 | } 428 | } 429 | 430 | if(#selectedItems){ 431 | for(i=#selectedItems;1;-1){ 432 | var item = selectedItems[i] 433 | frmXray.listview.delItem(item); 434 | ..table.remove(config.proxy.outbounds,item); 435 | } 436 | } 437 | else{ 438 | for(i=#outbounds;1;-1){ 439 | if(frmXray.listview.getItemText(i,7)=="不可用"){ 440 | ..table.remove(outbounds,i); 441 | frmXray.listview.delItem(i); 442 | } 443 | } 444 | } 445 | 446 | frmXray.listview.activeOutboundIndex = null; 447 | for(i=1;#outbounds;1){ 448 | if(outbounds[i].active){ 449 | frmXray.listview.activeOutboundIndex = i; 450 | break; 451 | } 452 | } 453 | publish("outbounds.updateConfigJson"); 454 | 455 | if(activeIndex && (!frmXray.listview.activeOutboundIndex) ){ 456 | frmXray.btnTcping.oncommand(); 457 | } 458 | } 459 | 460 | import xray.outbounds; 461 | frmXray.listview.onnotify = function(id,code,ptr){ 462 | select(code) { 463 | case 0xFFFFFFF4/*_NM_CUSTOMDRAW*/ { 464 | var lvcd = frmXray.listview.getNotifyCustomDraw(code,ptr); 465 | if( lvcd.nmcd.dwDrawStage == 0x10001/*_CDDS_ITEMPREPAINT*/) 466 | return 0x20/*_CDRF_NOTIFYSUBITEMDRAW*/ 467 | elseif( lvcd.nmcd.dwDrawStage == 1/*_CDDS_PREPAINT*/ ){ 468 | return 0x20/*_CDRF_NOTIFYITEMDRAW*/; 469 | } 470 | elseif( lvcd.nmcd.dwDrawStage == ( 0x10001/*_CDDS_ITEMPREPAINT*/ | 0x20000/*_CDDS_SUBITEM*/) ){ 471 | //注意这里 iSubItem 的索引自0开始( 其他函数通常自1开始 ) 472 | lvcd.clrText = lvcd.nmcd.dwItemSpec % 2 ? 0x373737 : 0; 473 | lvcd.clrTextBk = lvcd.nmcd.dwItemSpec % 2 ? 0xFFFFFF : 0xF5F5F5; 474 | 475 | var item = lvcd.nmcd.dwItemSpec+1; 476 | if(lvcd.iSubItem==7){ 477 | if(frmXray.listview.activeOutboundIndex=item){ 478 | lvcd.clrTextBk = 0x8AFFC3 479 | } 480 | } 481 | elseif(lvcd.iSubItem==6){ 482 | if(..config.proxy.outbounds[item].autoConnect===false){ 483 | lvcd.clrText = 0xCCCCCC; 484 | } 485 | } 486 | lvcd.update() 487 | 488 | return 0/*_CDRF_DODEFAULT*/ 489 | } 490 | } 491 | case 0xFFFFFFFD/*_NM_DBLCLK*/ { 492 | var nm = frmXray.listview.getNotifyMessage(code,ptr); 493 | if( nm ){ 494 | frmXray.onTcpingReturn = function(outboundIndex,address,succeeded,speedText){ 495 | if(address==frmXray.listview.getItemText(outboundIndex,2)){ 496 | frmXray.listview.setItemText(speedText,outboundIndex,7); 497 | } 498 | } 499 | frmXray.btnTcping.disabledText = null; 500 | 501 | for b,v in table.eachIndex(config.proxy.outbounds){ 502 | frmXray.listview.setItemText("",b,8); 503 | } 504 | activeOutbound(nm.iItem,frmXray.listview.getItemText(nm.iItem,2),false); 505 | } 506 | } 507 | case 0xFFFFFFFB/*_NM_RCLICK*/ { 508 | var x,y = win.getCursorPos(); 509 | var nm = frmXray.listview.getNotifyMessage(code,ptr); 510 | if(!nm) return; 511 | 512 | var currentIdx = nm.iItem; 513 | if(!currentIdx){ 514 | var popmenu = win.ui.popmenu(frmXray); 515 | 516 | popmenu.add("自剪贴板批量导入分享链接、订阅源",function(id){ 517 | frmXray.btnImportServerFromClipBd.oncommand(); 518 | }); 519 | 520 | popmenu.add('立即更新所有订阅源',function(id){ 521 | xray.outbounds.updateSubscription(); 522 | }); 523 | 524 | popmenu.add(); 525 | 526 | popmenu.add('清空服务器列表',function(id){ 527 | config.proxy.outbounds = ..table.array(); 528 | frmXray.listview.clear(); 529 | 530 | frmXray.btnTcping.disabledText = null; 531 | frmXray.autoTesting = false; 532 | frmXray.onTcpingReturn = function(outboundIndex,address,succeeded,speedText){} 533 | publish("outbounds.updateConfigJson"); 534 | }); 535 | 536 | popmenu.add('清除「不可用」服务器',function(id){ 537 | removeOutbounds(); 538 | }); 539 | 540 | popmenu.add("重置为默认服务器列表", 541 | function(id){ 542 | ..config.__loadDefaultOutbounds(); 543 | publish("outbounds.updateConfigJson"); 544 | publish("uiCommand.restartXrayCore"); 545 | } 546 | ); 547 | popmenu.add(); 548 | 549 | if(!frmXray.btnTcping.disabledText){ 550 | popmenu.add('按「响应速度」排序服务器',function(id){ 551 | frmXray.listview.sortBySpeed(); 552 | }); 553 | popmenu.add(); 554 | }; 555 | popmenu.popup(x,y,true); 556 | return; 557 | } 558 | 559 | var selectedItems = frmXray.listview.selected; 560 | var popmenu = win.ui.popmenu(frmXray); 561 | 562 | if(#selectedItems==1){ 563 | popmenu.add('设为活动服务器( 鼠标双击 )',function(id){ 564 | if(currentIdx){ 565 | frmXray.onTcpingReturn = function(outboundIndex,address,succeeded,speedText){ 566 | if(address==frmXray.listview.getItemText(outboundIndex,2)){ 567 | frmXray.listview.setItemText(speedText,outboundIndex,7); 568 | } 569 | } 570 | 571 | frmXray.btnTcping.disabledText = null; 572 | 573 | for b,v in table.eachIndex(config.proxy.outbounds){ 574 | frmXray.listview.setItemText("",b,8); 575 | } 576 | activeOutbound(currentIdx,frmXray.listview.getItemText(currentIdx,2),false) 577 | } 578 | }); 579 | } 580 | 581 | 582 | var autoConnect = config.proxy.outbounds[currentIdx].autoConnect!==false; 583 | var id = popmenu.add('允许自动测速并连接',function(id){ 584 | if(currentIdx){ 585 | for(i=#selectedItems;1;-1){ 586 | var item = selectedItems[i]; 587 | if(autoConnect === false){ 588 | config.proxy.outbounds[item].autoConnect = null; 589 | frmXray.listview.setItemText("",item,7); 590 | } 591 | else { 592 | config.proxy.outbounds[item].autoConnect = false; 593 | frmXray.listview.setItemText("禁止测速",item,7); 594 | } 595 | } 596 | } 597 | }); 598 | popmenu.check(id,autoConnect,0/*_MF_BYCOMMAND*/); 599 | 600 | popmenu.add(); 601 | 602 | 603 | popmenu.add("自剪贴板批量导入分享链接、订阅源",function(id){ 604 | frmXray.btnImportServerFromClipBd.oncommand(); 605 | }); 606 | 607 | var subscribeUrl = config.proxy.outbounds[currentIdx][["subscribeUrl"]]; 608 | if(#selectedItems=1 && subscribeUrl){ 609 | 610 | var ps; 611 | var cfgSubscribeUrls = ..config.proxy.subscribeUrls; 612 | for(i=#cfgSubscribeUrls;1;-1){ 613 | var sub = cfgSubscribeUrls[i] 614 | if(sub.url===subscribeUrl) { 615 | ps = sub.ps; 616 | break; 617 | } 618 | } 619 | 620 | if(#ps){ 621 | ps = ": "+ps; 622 | } 623 | 624 | popmenu.add('更新当前订阅源'+ps,function(id){ 625 | if(currentIdx){ 626 | xray.outbounds.updateSubscription({[subscribeUrl]=true}); 627 | } 628 | }); 629 | } 630 | 631 | popmenu.add('更新所有订阅源',function(id){ 632 | if(currentIdx){ 633 | xray.outbounds.updateSubscription(); 634 | } 635 | }); 636 | 637 | popmenu.add() 638 | 639 | popmenu.add('删除服务器',function(id){ 640 | removeOutbounds(selectedItems); 641 | }); 642 | 643 | popmenu.add('清空服务器列表',function(id){ 644 | if(currentIdx){ 645 | config.proxy.outbounds = ..table.array(); 646 | frmXray.listview.clear(); 647 | publish("outbounds.updateConfigJson"); 648 | } 649 | }); 650 | 651 | popmenu.add('清除「不可用」服务器',function(id){ 652 | removeOutbounds(); 653 | }); 654 | 655 | popmenu.add("重置为默认服务器列表", 656 | function(id){ 657 | ..config.__loadDefaultOutbounds(); 658 | publish("outbounds.updateConfigJson"); 659 | publish("uiCommand.restartXrayCore"); 660 | } 661 | ); 662 | 663 | if(#selectedItems==1){ 664 | 665 | popmenu.add(); 666 | popmenu.add('生成二维码',function(id){ 667 | if(currentIdx){ 668 | import xray.outbounds; 669 | import win.clip; 670 | 671 | var str = xray.outbounds.exportSharedLinks({config.proxy.outbounds[currentIdx]}); 672 | if(str){ 673 | var frmChild = frmXray.loadForm("\forms\main\tools\qr.aardio"); 674 | frmChild.createQrCode(str); 675 | frmChild.show(); 676 | } 677 | } 678 | }); 679 | 680 | popmenu.add('复制服务器分享链接',function(id){ 681 | if(currentIdx){ 682 | import xray.outbounds; 683 | import win.clip; 684 | 685 | var str = xray.outbounds.exportSharedLinks({config.proxy.outbounds[currentIdx]}); 686 | if(str){ 687 | win.clip.write(str); 688 | frmXray.edit.print("已复制链接:",str); 689 | } 690 | } 691 | }); 692 | 693 | popmenu.add('复制服务器地址',function(id){ 694 | if(currentIdx){ 695 | import win.clip; 696 | win.clip.write(config.proxy.outbounds[currentIdx].address + ":" + config.proxy.outbounds[currentIdx].port); 697 | frmXray.edit.print("已复制服务器地址:",config.proxy.outbounds[currentIdx].address + ":" + config.proxy.outbounds[currentIdx].port); 698 | } 699 | }); 700 | popmenu.add('复制服务器 JSON 配置',function(id){ 701 | if(currentIdx){ 702 | import win.clip; 703 | import web.json; 704 | var str = web.json.stringify(config.proxy.outbounds[currentIdx],true,false); 705 | win.clip.write(str); 706 | frmXray.edit.print(str); 707 | } 708 | }); 709 | popmenu.add(); 710 | 711 | var toolMenu = win.ui.popmenu(frmXray) 712 | toolMenu.add('本机 Ping 检测',function(id){ 713 | if(currentIdx){ 714 | thread.invoke(pingThread,config.proxy.outbounds[currentIdx].address,frmXray) 715 | } 716 | }); 717 | 718 | toolMenu.add('全球范围 Ping 检测 ...',function(id){ 719 | if(currentIdx){ 720 | process.openUrl("http://ping.pe/" + config.proxy.outbounds[currentIdx].address) 721 | } 722 | }); 723 | 724 | toolMenu.add('全球范围端口检测 ...',function(id){ 725 | if(currentIdx){ 726 | process.openUrl("http://port.ping.pe/" + config.proxy.outbounds[currentIdx].address + ":" + config.proxy.outbounds[currentIdx].port) 727 | } 728 | }); 729 | 730 | toolMenu.add('在线路由跟踪检测 ...',function(id){ 731 | if(currentIdx){ 732 | process.openUrl("https://www.boce.com/traceroute/" + config.proxy.outbounds[currentIdx].address) 733 | } 734 | }); 735 | 736 | toolMenu.add('本机路由检测',function(id){ 737 | if(currentIdx){ 738 | import process; 739 | process.execute("cmd.exe","/k tracert " + config.proxy.outbounds[currentIdx].address); 740 | } 741 | }); 742 | 743 | popmenu.add("服务器检测工具",toolMenu) 744 | 745 | popmenu.add('SSH 登录服务器',function(id){ 746 | if(currentIdx){ 747 | if(_STUDIO_INVOKED){ 748 | frmXray.msgErr("请在发布后运行此功能"); 749 | return; 750 | } 751 | import process; 752 | process.execute(io._exepath,"/sshLogin root@" 753 | + config.proxy.outbounds[currentIdx].address + ":" 754 | + (config.proxy.outbounds[currentIdx].sshPort||22)); 755 | } 756 | }); 757 | 758 | if(config.proxy.outbounds[currentIdx].port == 443 && config.proxy.outbounds[currentIdx].tls){ 759 | popmenu.add('浏览 HTTPS 主页 ...',function(id){ 760 | if(currentIdx){ 761 | process.openUrl("https" ++ "://" + config.proxy.outbounds[currentIdx].address); 762 | } 763 | }); 764 | popmenu.add(); 765 | } 766 | } 767 | popmenu.popup(x,y,true); 768 | } 769 | case 0xFFFFFF94/*_LVN_COLUMNCLICK*/{ 770 | var nm = frmXray.listview.getNotifyMessage(code,ptr) 771 | if(nm.iSubItem==7){ 772 | frmXray.listview.sortBySpeed(); 773 | } 774 | else { 775 | frmXray.msgWarn("排序请点击「响应速度」列") 776 | } 777 | } 778 | } 779 | } 780 | 781 | // 排序函数 782 | frmXray.listview.sortBySpeed = function () { 783 | if(frmXray.btnTcping.disabledText){ 784 | frmXray.msgWarn("请等待测速完成,然后再点击此排序列。"); 785 | return; 786 | } 787 | 788 | var outbounds = ..table.clone(config.proxy.outbounds); 789 | var activeIndex = frmXray.listview.activeOutboundIndex; 790 | frmXray.onTcpingReturn = function(outboundIndex,address,succeeded,speedText){}; //避免后台线程改变activeIndex 791 | 792 | for(i=#outbounds;1;-1){ 793 | var ob = outbounds[i]; 794 | if(type(ob)!="table"){ 795 | table.remove(outbounds,i); 796 | continue; 797 | } 798 | 799 | ob.speed = frmXray.listview.getItemText(i,7); 800 | if(ob.speed=="不可用"){ 801 | ob.speedNum = 0xFFFE; 802 | } 803 | elseif(ob.speed==""){ 804 | ob.speedNum = 0xFFFF; 805 | } 806 | elseif(ob.speed=="..."){ 807 | frmXray.msgWarn("请等待检测速度完成,然后再点击此排序列。") 808 | return; 809 | } 810 | else { 811 | ob.speedNum = tonumber(ob.speed):-3; 812 | } 813 | 814 | if(activeIndex==i){ 815 | ob.active = true; 816 | } 817 | } 818 | 819 | var hasStatus; 820 | for(i=1;#outbounds;1){ 821 | if(#outbounds[i].speed){ 822 | hasStatus = true; 823 | break; 824 | } 825 | } 826 | 827 | if(!hasStatus){ 828 | frmXray.msgWarn("请先点击「检测并连接服务器」获取服务器响应速度!"); 829 | return; 830 | } 831 | 832 | var desc = frmXray.listview.getColumnImage(7) == 0; 833 | if(desc){ 834 | table.sort(outbounds,function(b){ 835 | return owner.speedNum > b.speedNum; 836 | }) 837 | } 838 | else { 839 | table.sort(outbounds,function(b){ 840 | return owner.speedNum < b.speedNum; 841 | }) 842 | } 843 | 844 | frmXray.listview.activeOutboundIndex = null; 845 | for(i=1;#outbounds;1){ 846 | if(outbounds[i].active){ 847 | frmXray.listview.activeOutboundIndex = i; 848 | break; 849 | } 850 | } 851 | 852 | outbounds.fields = {"protocol";"address";"port";"security";"network";"ps";"speed"} 853 | config.proxy.outbounds = outbounds; 854 | frmXray.listview.setTable(outbounds); 855 | outbounds.fields = {"protocol";"address";"port";"security";"network";"ps"} 856 | 857 | //清理用于排序的临时字段 858 | for(i=1;#outbounds;1){ 859 | var ob = outbounds[i]; 860 | ob.speedNum = null; 861 | ob.speed = null; 862 | ob.active = null; 863 | } 864 | 865 | if(frmXray.listview.activeOutboundIndex){ 866 | frmXray.listview.setItemText("活动服务器:正常",frmXray.listview.activeOutboundIndex,8) 867 | } 868 | frmXray.listview.setColumnImage(7, desc ? 1 : 0); 869 | } 870 | 871 | import win.imageList; 872 | var iml = win.imageList(16, 15); 873 | iml.add('GIF\56\57a \0\15\0\x80\0\0\x80\x80\x80\xff\0\xff\33\xf9\4\0\0\0\0\0\44\0\0\0\0 \0\15\0\0\2\31\x8c\x8f\xa9\xcb\xed\15\xa3\x9c\xb4N\xf0\x80\xde\56k\xbfA\\\xd7\x84 \x97Y\xea\xca\xb6\xee\11\xc7F\1\0;', 0xff00ff); 874 | frmXray.listview.setColumnImageList(iml); 875 | 876 | subscribe("uiCommand.restartXrayCore",function(){ 877 | config.proxy.outbounds.fields = {"protocol";"address";"port";"security";"network";"ps";} 878 | 879 | var outbounds = config.proxy.outbounds; 880 | for(i=#outbounds;1;-1){ 881 | if(type(outbounds[i])!="table"){ 882 | table.remove(outbounds,i); 883 | } 884 | } 885 | frmXray.listview.setTable(outbounds); 886 | 887 | frmXray.btnTcping.oncommand(); 888 | } ) 889 | 890 | import process; 891 | if(#config.proxy.outbounds){ 892 | config.proxy.outbounds.fields = {"protocol";"address";"port";"security";"network";"ps";} 893 | 894 | var outbounds = config.proxy.outbounds; 895 | for(i=#outbounds;1;-1){ 896 | if(type(outbounds[i])!="table"){ 897 | table.remove(outbounds,i); 898 | } 899 | } 900 | frmXray.listview.setTable(outbounds); 901 | 902 | frmXray.setTimeout( 903 | function(){ 904 | publish("uiCommand.restartXrayCore"); 905 | },1000 906 | ); 907 | } 908 | 909 | subscribe("sysProxy.modeChanged",function(mode){ 910 | frmXray.radioProxyPac.checked = false; 911 | frmXray.radioProxy.checked = false; 912 | frmXray.radioProxyDirect.checked = false; 913 | 914 | if(mode = "pac"){ 915 | frmXray.radioProxyPac.checked = true; 916 | } 917 | elseif(mode = "proxy"){ 918 | frmXray.radioProxy.checked = true; 919 | } 920 | else{ 921 | frmXray.radioProxyDirect.checked = true; 922 | } 923 | } ) 924 | publish("sysProxy.modeChanged",config.proxy.mode); 925 | 926 | import sysProxy; 927 | var switchProxyMode = function(){ 928 | if(frmXray.radioProxyPac.checked){ 929 | sysProxy.switchToPacMode(); 930 | } 931 | elseif(frmXray.radioProxy.checked){ 932 | sysProxy.switchToGlobalMode(); 933 | } 934 | else { 935 | sysProxy.switchToDirectMode(); 936 | } 937 | } 938 | frmXray.radioProxyPac.oncommand = switchProxyMode; 939 | frmXray.radioProxy.oncommand = switchProxyMode; 940 | frmXray.radioProxyDirect.oncommand = switchProxyMode; 941 | 942 | import win.dlg.message; 943 | frmXray.btnEditPac.skin(style.plainButton) 944 | frmXray.btnEditPac.oncommand = function(id,event){ 945 | if(frmPac && win.isWindow(frmPac.hwnd)){ 946 | if( win.isIconic(frmPac.hwnd) ) win.show(frmPac.hwnd,9/*_SW_RESTORE*/ ); 947 | if( !win.isVisible(frmPac.hwnd) ) win.show(frmPac.hwnd,0x1/*_SW_NORMAL*/ ); 948 | win.setForeground(frmPac.hwnd) 949 | return; 950 | } 951 | 952 | frmPac = ..mainForm.loadForm("\forms\main\pac.aardio"); 953 | frmPac.show(); 954 | } 955 | subscribe("uiCommand.showPacForm",function(...){ 956 | frmXray.btnEditPac.oncommand(); 957 | } ) 958 | 959 | subscribe("uiCommand.print",function(...){ 960 | frmXray.edit.print(...); 961 | } ) 962 | 963 | import xray.outbounds; 964 | frmXray.btnImportServerFromClipBd.skin(style.plainButton) 965 | frmXray.btnImportServerFromClipBd.oncommand = function(id,event){ 966 | var str = ..win.clip.read(); 967 | if(str){ 968 | str = ..string.trim(str,'"\'\t\r\n '); 969 | if( ..string.startWith(str,"http://") 970 | || ..string.startWith(str,"https://") 971 | || ..string.match(str,"^\s*/\N+\s*$") ){ 972 | if(..string.match(str,"^\s*/\N+\s*$")){ 973 | str = "https://github.com" + str; 974 | } 975 | 976 | xray.outbounds.updateSubscription({[str]=true}); 977 | return; 978 | } 979 | 980 | var outbounds = xray.outbounds.importFromString(str); 981 | if(#outbounds){ 982 | ..table.append(config.proxy.outbounds,outbounds); 983 | publish("uiCommand.restartXrayCore"); 984 | publish("outbounds.updateConfigJson"); 985 | frmXray.msgOk("已成功导入" + #outbounds + "个服务器",1200); 986 | return; 987 | } 988 | } 989 | 990 | frmXray.msgFrown('未导入服务器!\r\n请先复制以下格式文本(自动清除其中的无效内容):\r\n\r\n1、一行或多行(忽略无效行)分享链接或服务器JSON配置。\r\n支持 vmess://,vless://,ss://,trojan://, trojan-go:// 等通用分享链接。\r\n\r\n2、包含多个服务器配置的JSON数组,支持winXray格式以及通用格式JSON。\r\n\r\n3、单个 http:// 或 https:// 开头的通用订阅源地址。\r\n可直接使用浏览器地址栏的github文件地址(含blob或raw目录名)。\r\n也可以仅复制单斜杆开始的github文件路径。\r\n\r\n订阅源可用BASE64编码或明文返回以上1、2条规定的配置或分享链接。') 991 | } 992 | 993 | var testTimerId; 994 | frmXray.chkAutoTest.oncommand = function(id,event){ 995 | if(testTimerId){ 996 | frmXray.clearInterval(testTimerId); 997 | } 998 | 999 | config.proxy.test = frmXray.chkAutoTest.checked; 1000 | if(!config.proxy.test) return; 1001 | 1002 | import inet.http; 1003 | testTimerId = frmXray.setInterval( 1004 | config.proxy.testInterval*1000,function(){ 1005 | if( frmXray.autoTesting ) return; 1006 | var idx = frmXray.listview.activeOutboundIndex; 1007 | var address = frmXray.listview.activeOutboundAddress; 1008 | if( !( idx&&address) ){ 1009 | if(!inet.http.isAlive(,false)){ 1010 | return config.proxy.testInterval*1000; 1011 | } 1012 | 1013 | frmXray.btnTcping.oncommand(); 1014 | return; 1015 | } 1016 | 1017 | frmXray.autoTesting = true; 1018 | activeOutbound(idx,address,true); 1019 | 1020 | return config.proxy.testInterval*1000; 1021 | } 1022 | ) 1023 | } 1024 | 1025 | if(config.proxy.test){ 1026 | frmXray.chkAutoTest.checked = true; 1027 | frmXray.chkAutoTest.oncommand(); 1028 | } 1029 | 1030 | frmXray.chkAutoTest.skin(style.checkBox); 1031 | 1032 | 1033 | frmXray.enableDpiScaling(); 1034 | win.loopMessage(); -------------------------------------------------------------------------------- /lib/config.aardio: -------------------------------------------------------------------------------- 1 | //config 配置文件 2 | import fsys.config; 3 | config = fsys.config( io.appData("/winXray/") ); 4 | 5 | namespace config { 6 | __appName = "winXray"; 7 | __loadDefaultOutbounds = function(){ 8 | import xray.outbounds; 9 | var serverData = ..string.load("/xray-core/winXray-default-servers.json") 10 | : $"/xray-core/winXray-default-servers.json" 11 | 12 | proxy.outbounds = ..xray.outbounds.importFromString(..string.removeBom(serverData)); 13 | ..publish("outbounds.updateConfigJson",) 14 | } 15 | 16 | if(!proxy.outbounds){ 17 | __loadDefaultOutbounds(); 18 | } 19 | } 20 | 21 | if(!config.proxy.mode) config.proxy.mode = "pac"; 22 | if(!config.proxy.pacPort) config.proxy.pacPort = 0; 23 | if(config.proxy.useHttpGlobal===null){ 24 | config.proxy.useHttpGlobal = !_WIN10_LATER; 25 | } 26 | 27 | if(config.proxy.enableGitConfigGithub===null){ 28 | config.proxy.enableGitConfigGithub = true; 29 | } 30 | 31 | if(config.proxy.test === null){ 32 | config.proxy.test = true; 33 | } 34 | 35 | if(config.proxy.autoRefreshSubscription === null){ 36 | config.proxy.autoRefreshSubscription = true; 37 | } 38 | 39 | if(!config.proxy.testInterval){ 40 | config.proxy.testInterval = 15; 41 | } 42 | 43 | if(!config.proxy.maxTestServers){ 44 | config.proxy.maxTestServers = 100; 45 | } 46 | 47 | if(!config.proxy.subscribeUrls){ 48 | config.proxy.subscribeUrls = { 49 | fields = {"";"checked";"ps";"url";} 50 | } 51 | } 52 | 53 | config.__resetDefaultCore = function(){ 54 | config.core.default = { 55 | inbounds={ 56 | { 57 | listen="127.0.0.1"; 58 | port=0; 59 | protocol="socks"; 60 | settings={ 61 | auth="noauth"; 62 | udp=true; 63 | }; 64 | sniffing={ 65 | destOverride={ 66 | "http"; 67 | "tls" 68 | }; 69 | enabled=true 70 | }; 71 | tag="proxy"; 72 | }; 73 | { 74 | listen="127.0.0.1"; 75 | port=0; 76 | protocol="http"; 77 | settings={}; 78 | sniffing={ 79 | destOverride={ 80 | "http"; 81 | "tls" 82 | }; 83 | enabled=true 84 | }; 85 | tag="http_proxy" 86 | } 87 | }; 88 | log={ 89 | access=""; 90 | error=""; 91 | loglevel="warning" 92 | }; 93 | outbounds={ 94 | { 95 | tag="proxy" 96 | }; 97 | { 98 | protocol="freedom"; 99 | settings={}; 100 | tag="direct"; 101 | }; 102 | { 103 | protocol="blackhole"; 104 | settings={ 105 | response={ 106 | type="http" 107 | }; 108 | }; 109 | tag="block"; 110 | } 111 | }; 112 | routing={ 113 | domainStrategy="IPIfNonMatch"; 114 | rules={ 115 | { 116 | inboundTag={ 117 | "api" 118 | }; 119 | outboundTag="api"; 120 | type="field"; 121 | } 122 | } 123 | }; 124 | } 125 | } 126 | 127 | if(!config.core.default){ 128 | config.__resetDefaultCore(); 129 | } 130 | 131 | if(!config.core.default.inbounds[2]){ 132 | config.core.default.inbounds[2] = { 133 | listen="127.0.0.1"; 134 | port=0; 135 | protocol="http"; 136 | settings={}; 137 | sniffing={ 138 | destOverride={ 139 | "http"; 140 | "tls" 141 | }; 142 | enabled=true 143 | }; 144 | tag="http_proxy" 145 | } 146 | } 147 | 148 | /**intellisense(config) 149 | __appName = 应用程序名 150 | ? = 配置文件名,\n读写配置并序列化为一个表对象,\n表的成员值可以是支持序列化的普通变量,支持table对象\n配置文件在首次使用时自动加载,退出程序时自动保存\n!fsys_table. 151 | end intellisense**/ -------------------------------------------------------------------------------- /lib/dnsApi.aardio: -------------------------------------------------------------------------------- 1 | //dnsApi 域名解析 2 | namespace dnsApi 3 | 4 | _dll = ..raw.loadDll("dnsapi.dll"); 5 | flush = function(domain){ 6 | return domain ? _dll.DnsFlushResolverCacheB() : _dll.DnsFlushResolverCacheEntry_A(domain); 7 | } 8 | 9 | /**intellisense(dnsApi) 10 | flush(.(domain) = 清空指定域名的DNS缓存,\n如果参数不指定域名则清空所有DNS缓存 11 | end intellisense**/ 12 | -------------------------------------------------------------------------------- /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 = "default"; 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 | edit = { 90 | border={ 91 | default={bottom=1;color=0xFF808080}; 92 | disabled={bottom=1;color=0xFFCCCCCC} 93 | } 94 | }; 95 | trackbar = { 96 | background={ 97 | default=0xFF23ABD9 98 | }; 99 | foreground={ 100 | default=0xFFFF771C; 101 | hover=0xFFFF6600 102 | }; 103 | color={ 104 | default=0xFFFF5C00; 105 | hover=0xFFFF6600 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/sysProxy.aardio: -------------------------------------------------------------------------------- 1 | //sysProxy 系统代理 2 | import config; 3 | import inet.conn; 4 | import xray.github; 5 | import xray.pacServer; 6 | 7 | namespace sysProxy; 8 | 9 | switch = function(mode){ 10 | ..config.proxy.mode = mode : "direct"; 11 | ..config.proxy.save(); 12 | 13 | if(mode=="pac"){ 14 | var pacUrl = ..xray.pacServer.getUrl(); 15 | if(pacUrl){ 16 | ..inet.conn.setProxyAutoConfig(,pacUrl ); 17 | } 18 | else { 19 | ..xray.pacServer.restart(); 20 | } 21 | ..xray.github.setProxy(true); 22 | } 23 | elseif(mode=="proxy"){ 24 | import xray.core; 25 | var address = xray.core.getHttpProxyAddress(); 26 | 27 | if(address){ 28 | ..inet.conn.setProxy(,address); 29 | ..xray.github.setProxy(true); 30 | } 31 | } 32 | else { 33 | ..inet.conn.setProxy(); 34 | ..xray.github.setProxy(false); 35 | } 36 | ..publish("sysProxy.modeChanged",mode) 37 | } 38 | 39 | switchToGlobalMode = function(){ 40 | switch("proxy") 41 | } 42 | 43 | switchToPacMode = function(){ 44 | switch("pac") 45 | } 46 | 47 | switchToDirectMode = function(){ 48 | switch("direct") 49 | } 50 | 51 | reset = function(proxy){ 52 | if(proxy===false){ 53 | if(..config.proxy.mode != "direct"){ 54 | ..inet.conn.setProxy(); 55 | ..xray.github.setProxy(false); 56 | } 57 | return; 58 | } 59 | 60 | ..inet.conn.setProxy(); 61 | switch(..config.proxy.mode); 62 | } 63 | 64 | ..subscribe("pacServer.restarted",function(pacPort){ 65 | if(..config.proxy.mode == "pac"){ 66 | switchToPacMode(); 67 | } 68 | } ) 69 | 70 | ..subscribe("xrayCore.restarted",function(socksProxyPort,httpProxPort){ 71 | if(..config.proxy.mode != "direct"){ 72 | reset(true); 73 | } 74 | } ) 75 | 76 | /**intellisense(sysProxy) 77 | switch(.(mode) = 切换代理模式,同步修改配置文件。\n可选参数:"proxy","pac","direct" 78 | switchToGlobalMode() = 设为全局代理,同步修改配置文件。 79 | switchToPacMode() = 设为PAC自动代理,同步修改配置文件。 80 | switchToDirectMode() = 设为禁用代理,同步修改配置文件。 81 | reset(.(是否启用代理)) = 重置系统代理设置,不修改配置文件。\n省略参数时默认值为true。 82 | end intellisense**/ 83 | -------------------------------------------------------------------------------- /lib/xray/core/_.aardio: -------------------------------------------------------------------------------- 1 | //core 代理服务 2 | import wsock.tcp.server; 3 | import process.popen; 4 | import config; 5 | 6 | namespace xray.core; 7 | 8 | socksProxyPort = null; 9 | httpProxPort = null; 10 | 11 | getHttpProxyAddress = function(){ 12 | if(!(socksProxyPort&&httpProxPort)){ return null; } 13 | return ..config.proxy.useHttpGlobal ? ("127.0.0.1:" + httpProxPort ): ("SOCKS=127.0.0.1:"+socksProxyPort) 14 | } 15 | 16 | isInboundPortChanged = function(){ 17 | var inbounds = ..config.core.default.inbounds; 18 | if(! (..table.isArray(inbounds) && inbounds[1] && inbounds[2]) ){ 19 | return true; 20 | } 21 | 22 | return (inbounds[1].port!=socksProxyPort) || (inbounds[2].port!=httpProxPort) 23 | } 24 | 25 | var prcsXray; 26 | import process.job.limitKill; 27 | restart = function(editor,outbound){ 28 | if(!outbound){ return null,"错误的出站代理服务器配置" } 29 | if(_WINXP){ 30 | return null,"抱歉!Xray Core 不支持 Windows XP, 仅支持 Windows 7, Windows 10 以及更新操作系统。"; 31 | } 32 | 33 | var inbounds = ..config.core.default[["inbounds"]]; 34 | if(! (..table.isArray(inbounds) && inbounds[1] ) ){ 35 | return null,"错误的入站SOCKS代理服务器配置" 36 | } 37 | if(! ( inbounds[2] ) ){ 38 | return null,"错误的入站HTTP代理服务器配置" 39 | } 40 | 41 | if(prcsXray){ 42 | prcsXray.terminate(); 43 | prcsXray = null; 44 | } 45 | 46 | socksProxyPort = inbounds[1].port; 47 | httpProxPort = inbounds[2].port; 48 | if( (! socksProxyPort) || socksProxyPort >= 49152 ){ socksProxyPort = ..wsock.tcp.server.getFreePort("127.0.0.1",1081,10801,10811,44821) } 49 | if( (! httpProxPort) || httpProxPort >= 49152 ){ httpProxPort = ..wsock.tcp.server.getFreePort("127.0.0.1",1082,10802,10812,44822) } 50 | 51 | var corePath = getPath(editor.hwnd); 52 | if(!corePath){ return false,"启动失败,未找到xray.exe"; } 53 | 54 | import xray.core.configJson; 55 | var jsonPath,err = ..xray.core.configJson.write( 56 | ..io.joinpath(..io.splitpath(corePath).dir,"config.json") 57 | ,outbound,socksProxyPort,httpProxPort); 58 | if(!jsonPath){ 59 | return false,err:"启动失败,写入配置文件遇到错误!"; 60 | } 61 | 62 | ..publish("activeOutbound",false); 63 | 64 | import sysProxy; 65 | ..sysProxy.reset(false); 66 | 67 | var err; 68 | prcsXray,err = ..process.popen(corePath,"-c=config.json" ); 69 | if(!prcsXray){ 70 | ..publish("uiCommand.print",err:"启动 Xray Core 时遇到未知错误!"); 71 | 72 | if(!..process().isWow64()){ 73 | if(..process.isExe(corePath)!="PE32"){ 74 | ..io.remove(corePath); 75 | if(!..io.exist(corePath)){ 76 | ..publish("uiCommand.print","当前操作系统是32位,已删除无效的64位 Xray Core,正在下载32位 Xray Core"); 77 | return restart(editor,outbound) 78 | } 79 | else { 80 | ..publish("uiCommand.print","当前操作系统是32位,但是找到的 Xray Core 是64位,请重新下载32位 Xray Core"); 81 | } 82 | } 83 | } 84 | 85 | return; 86 | } 87 | prcsXray.assignToJobObject(process.job.limitKill); 88 | prcsXray.codepage = 65001; 89 | prcsXray.logResponse(editor); 90 | 91 | inbounds[1].port = socksProxyPort; 92 | inbounds[2].port = httpProxPort; 93 | ..config.core.save(); 94 | 95 | ..publish("uiCommand.print","已启动代理服务器,SOCKS端口:" + socksProxyPort + " HTTP端口:" + httpProxPort) 96 | ..publish("xrayCore.restarted",socksProxyPort,httpProxPort); 97 | return true; 98 | } 99 | 100 | stop = function(){ 101 | if(prcsXray){ 102 | prcsXray.terminate(); 103 | prcsXray = null; 104 | } 105 | 106 | ..sysProxy.reset(false); 107 | 108 | import xray.github; 109 | xray.github.setProxy(false); 110 | } 111 | 112 | var getXrayCoreUrl = function(){ 113 | import inet.http; 114 | var http = ..inet.http(); 115 | var url = http.location("https://github.com/XTLS/Xray-core/releases/latest"); 116 | http.close(); 117 | 118 | if(!url)return; 119 | 120 | var tag = ..string.match(url,"[^/]+$") 121 | if(!tag) return; 122 | 123 | return "https://github.com/XTLS/Xray-core/releases/download/" 124 | + tag + "/xray-windows-" + (..process().isWow64() ? "64" : "32") + ".zip",tag 125 | } 126 | 127 | getPath = function(hwnd){ 128 | var path = ..io.fullpath("/xray-core/xray.exe"); 129 | if(..io.exist(path)){ 130 | return path; 131 | } 132 | 133 | var path = ..io.appData("/xray/core/xray.exe"); 134 | if(..io.exist(path)){ 135 | return path; 136 | } 137 | 138 | var url = getXrayCoreUrl(); 139 | if(!url) return null; 140 | 141 | import zlib.httpFile; 142 | if( ..zlib.httpFile.download(url,"正在下载 Xray Core" 143 | ,..io.appData("/xray/download/") 144 | ,..io.appData("/xray/core/"),,hwnd) ) 145 | return ..io.exist(path); 146 | } 147 | 148 | updateCore = function(){ 149 | var coreDir = ..io.exist("/xray-core/xray.exe") ? ..io.fullpath("/xray-core/"); 150 | if(!coreDir){ 151 | coreDir = ..io.appData("/xray/core/"); 152 | } 153 | 154 | var url,versionTag = getXrayCoreUrl(); 155 | if(!url) return null; 156 | 157 | import fsys; 158 | fsys.delete(..io.appData("/xray/temp/")) 159 | 160 | import zlib.httpFile; 161 | if( ..zlib.httpFile.download(url,"正在下载 Xray Core" 162 | ,..io.appData("/xray/download/") 163 | ,..io.appData("/xray/temp/"),,..mainForm.hwnd) ){ 164 | if(..io.exist(..io.appData("/xray/temp/xray.exe"))){ 165 | import sysProxy; 166 | sysProxy.reset(false); 167 | 168 | import process.file; 169 | process.file.terminate(..io.joinpath(coreDir,"xray.exe")); 170 | fsys.copy(..io.appData("/xray/temp/xray.exe"),..io.joinpath(coreDir,"xray.exe")); 171 | 172 | return versionTag; 173 | } 174 | } 175 | } 176 | 177 | /**intellisense(xray.core) 178 | socksProxyPort = SOCKS代理端口 179 | httpProxPort = HTTP代理端口 180 | isInboundPortChanged() = 是否已变更代理端口配置 181 | restart(.(editor,outbound) = 重启启动 Xray 服务进程 182 | end intellisense**/ 183 | -------------------------------------------------------------------------------- /lib/xray/core/configJson.aardio: -------------------------------------------------------------------------------- 1 | //configJson 启动配置 2 | import config; 3 | import web.json; 4 | import inet.url; 5 | import crypt; 6 | 7 | namespace xray.core.configJson; 8 | 9 | write = function(jsonPath,outbound,sockPort,httpPort){ 10 | if(!outbound){ return; } 11 | if(type(outbound.port)!=type.number){ 12 | outbound.port = tonumber(outbound.port); 13 | } 14 | 15 | var current = ..table.clone(..config.core.default); 16 | var inbounds = current.inbounds; 17 | if(! (..table.isArray(inbounds) && inbounds[1] ) ){ 18 | return null,"错误的入站SOCKS代理服务器配置" 19 | } 20 | 21 | inbounds[1].pacPort = null; 22 | inbounds[1].port = sockPort; 23 | if(inbounds[2]){ inbounds[2].port = httpPort; } 24 | 25 | if(#outbound.tls && !outbound.sni){ 26 | var host = outbound.host; 27 | if(..table.isArray(host)){ host = host[1] } 28 | outbound.sni = host ? host : outbound.address 29 | } 30 | 31 | if(outbound.protocol=="vless"){ 32 | if(!outbound.tls && outbound.port==443) outbound.tls = "tls"; 33 | if(!outbound.tls) outbound.tls = "tls"; 34 | 35 | var mux = (outbound.concurrency !== null) ? { 36 | concurrency=outbound.concurrency; 37 | enabled=outbound.concurrency ? ( outbound.concurrency > 0) 38 | } : { 39 | concurrency=8; 40 | enabled=true 41 | }; 42 | 43 | var ob = { 44 | mux = outbound.tls != "xtls" ? mux : null; 45 | protocol="vless"; 46 | 47 | settings={ 48 | vnext={ 49 | { 50 | address=outbound.address; 51 | port=tonumber(outbound.port); 52 | users={ 53 | { 54 | id=outbound.id; 55 | flow=outbound.flow : (outbound.tls == "xtls" ? "xtls-rprx-direct" : null); 56 | encryption=( (!#outbound.security) || outbound.security=="auto") ? "none" : outbound.security; 57 | level=outbound.level : 0; 58 | } 59 | } 60 | } 61 | }; 62 | }; 63 | streamSettings={ 64 | network=outbound.network : "tcp"; 65 | security=#outbound.tls ? outbound.tls : null; 66 | tlsSettings = (#outbound.tls && outbound.tls != "xtls") ? { 67 | allowInsecure = !!outbound.allowInsecure; 68 | serverName = outbound.sni : outbound.address; 69 | alpn = outbound.alpn; 70 | disableSessionResumption = outbound.disableSessionResumption; 71 | } : null; 72 | xtlsSettings = outbound.tls == "xtls" ? { 73 | allowInsecure = !!outbound.allowInsecure; 74 | serverName = outbound.sni : outbound.address; 75 | alpn = outbound.alpn; 76 | disableSessionResumption = outbound.disableSessionResumption; 77 | } : null; 78 | }; 79 | tag="proxy" 80 | }; 81 | 82 | if( outbound.network == "ws" ){ 83 | ob.streamSettings.wsSettings = { 84 | path = outbound.path; 85 | headers = outbound.headers; 86 | } 87 | 88 | if( outbound.host ) { 89 | if(!ob.streamSettings.wsSettings.headers){ 90 | ob.streamSettings.wsSettings.headers = {} 91 | } 92 | ob.streamSettings.wsSettings.headers.Host = outbound.host; 93 | } 94 | } 95 | elseif( (outbound.network == "http") || (outbound.network == "h2") ){ 96 | var host = outbound.host; 97 | if(type.isString(host)){host = {host}}; 98 | 99 | ob.streamSettings.httpSettings = { 100 | path = outbound.path; 101 | host = host; 102 | } 103 | } 104 | elseif( outbound.network == "tcp" ){ 105 | if( outbound.type == "http" ){ 106 | ob.streamSettings.tcpSettings = { 107 | header = { 108 | request={ 109 | headers=..table.assign({ 110 | Connection={ 111 | "keep-alive" 112 | }; 113 | Host=outbound.host; 114 | Pragma="no-cache"; 115 | ["Accept-Encoding"]={ 116 | "gzip, deflate" 117 | }; 118 | ["User-Agent"]={ 119 | "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"; 120 | "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" 121 | } 122 | },outbound.headers); 123 | method = outbound.httpMethod : "GET"; 124 | path = outbound.path; 125 | version="1.1" 126 | }; 127 | type="http"; 128 | } 129 | } 130 | } 131 | } 132 | elseif( outbound.network == "kcp" ){ 133 | ob.streamSettings.kcpSettings = { 134 | congestion = outbound.congestion; 135 | seed = outbound.seed; 136 | mtu = outbound.mtu; 137 | tti = outbound.tti; 138 | uplinkCapacity = outbound.uplinkCapacity; 139 | downlinkCapacity = outbound.downlinkCapacity; 140 | readBufferSize = outbound.readBufferSize; 141 | writeBufferSize = outbound.writeBufferSize; 142 | header = { 143 | type = outbound.type; 144 | } 145 | } 146 | } 147 | elseif( outbound.network == "quic" ){ 148 | ob.streamSettings.kcpSettings = { 149 | key = outbound.key; 150 | security = outbound.security ? outbound.security : "none"; 151 | header = { 152 | type = outbound.type; 153 | } 154 | } 155 | } 156 | 157 | ..table.assign(current.outbounds[1],ob); 158 | } 159 | elseif(outbound.protocol=="vmess"){ 160 | if(!#outbound.security) outbound.security = "auto"; 161 | 162 | var ob = { 163 | mux = (outbound.concurrency !== null) ? { 164 | concurrency=outbound.concurrency; 165 | enabled=outbound.concurrency ? ( outbound.concurrency > 0) 166 | } : { 167 | concurrency=8; 168 | enabled=true 169 | }; 170 | protocol="vmess"; 171 | settings={ 172 | vnext={ 173 | { 174 | address=outbound.address; 175 | level=outbound.level : null; 176 | port=tonumber(outbound.port); 177 | users={ 178 | { 179 | alterId=tonumber(outbound.alterId) : 0; 180 | email=outbound.email : "t@t.tt"; 181 | id=outbound.id; 182 | security=outbound.security; 183 | } 184 | } 185 | } 186 | }; 187 | }; 188 | streamSettings={ 189 | network=outbound.network : "tcp"; 190 | security=#outbound.tls ? outbound.tls : null; 191 | tlsSettings = #outbound.tls ? { 192 | allowInsecure = !!outbound.allowInsecure; 193 | serverName = outbound.sni; 194 | } : null 195 | }; 196 | tag="proxy" 197 | }; 198 | 199 | if( outbound.network == "ws" ){ 200 | ob.streamSettings.wsSettings = { 201 | path = outbound.path; 202 | headers = outbound.headers; 203 | } 204 | 205 | if( outbound.host ) { 206 | if(!ob.streamSettings.wsSettings.headers){ 207 | ob.streamSettings.wsSettings.headers = {} 208 | } 209 | ob.streamSettings.wsSettings.headers.Host = outbound.host; 210 | } 211 | } 212 | elseif( (outbound.network == "http") || (outbound.network == "h2") ){ 213 | var host = outbound.host; 214 | if(type.isString(host)){host = {host}}; 215 | 216 | ob.streamSettings.httpSettings = { 217 | path = outbound.path; 218 | host = host; 219 | } 220 | } 221 | elseif( outbound.network == "tcp" ){ 222 | if( outbound.type == "http" ){ 223 | ob.streamSettings.tcpSettings = { 224 | header = { 225 | request={ 226 | headers=..table.assign({ 227 | Connection={ 228 | "keep-alive" 229 | }; 230 | Host=outbound.host; 231 | Pragma="no-cache"; 232 | ["Accept-Encoding"]={ 233 | "gzip, deflate" 234 | }; 235 | ["User-Agent"]={ 236 | "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"; 237 | "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" 238 | } 239 | },outbound.headers); 240 | method=outbound.httpMethod : "GET"; 241 | path = outbound.path; 242 | version="1.1" 243 | }; 244 | type="http"; 245 | } 246 | } 247 | } 248 | } 249 | elseif( outbound.network == "kcp" ){ 250 | ob.streamSettings.kcpSettings = { 251 | congestion = outbound.congestion; 252 | seed = outbound.seed; 253 | mtu = outbound.mtu; 254 | tti = outbound.tti; 255 | uplinkCapacity = outbound.uplinkCapacity; 256 | downlinkCapacity = outbound.downlinkCapacity; 257 | readBufferSize = outbound.readBufferSize; 258 | writeBufferSize = outbound.writeBufferSize; 259 | header = { 260 | type = outbound.type; 261 | } 262 | } 263 | } 264 | elseif( outbound.network == "quic" ){ 265 | ob.streamSettings.kcpSettings = { 266 | key = outbound.key; 267 | security = outbound.security ? outbound.security : "none"; 268 | header = { 269 | type = outbound.type; 270 | } 271 | } 272 | } 273 | 274 | ..table.assign(current.outbounds[1],ob); 275 | } 276 | elseif(outbound.protocol=="trojan" || outbound.protocol=="trojan-go"){ 277 | outbound.tls = #outbound.tls ? outbound.tls : "tls"; 278 | var ob = { 279 | mux = (outbound.concurrency !== null) ? { 280 | concurrency=outbound.concurrency; 281 | enabled=outbound.concurrency ? ( outbound.concurrency > 0) 282 | } : { 283 | concurrency=-1; 284 | enabled=false 285 | }; 286 | protocol="trojan"; 287 | settings={ 288 | servers={ 289 | { 290 | address=outbound.address; 291 | email=outbound.email; 292 | password=outbound.id; 293 | port=outbound.port; 294 | level=outbound.level : 0; 295 | encryption=outbound.security; 296 | flow=outbound.flow : (outbound.tls == "xtls" ? "xtls-rprx-direct" : null); 297 | } 298 | }; 299 | }; 300 | streamSettings={ 301 | network = outbound.network : "tcp"; 302 | security = #outbound.tls ? outbound.tls : "tls"; 303 | 304 | tlsSettings = (outbound.tls === "tls") ? { 305 | allowInsecure = !!outbound.allowInsecure; 306 | serverName = outbound.sni; 307 | } : null 308 | }; 309 | tag="proxy" 310 | }; 311 | 312 | if( outbound.network == "ws" ){ 313 | ob.streamSettings.wsSettings = { 314 | path = outbound.path; 315 | headers = outbound.headers; 316 | } 317 | 318 | if( outbound.host ) { 319 | if(!ob.streamSettings.wsSettings.headers){ 320 | ob.streamSettings.wsSettings.headers = {} 321 | } 322 | ob.streamSettings.wsSettings.headers.Host = outbound.host; 323 | } 324 | } 325 | ..table.assign(current.outbounds[1],ob); 326 | } 327 | elseif(outbound.protocol=="shadowsocks"){ 328 | var ob = { 329 | mux = (outbound.concurrency !== null) ? { 330 | concurrency=outbound.concurrency; 331 | enabled=outbound.concurrency ? ( outbound.concurrency > 0) 332 | } : { 333 | concurrency=-1; 334 | enabled=false 335 | }; 336 | protocol="shadowsocks"; 337 | settings={ 338 | servers={ 339 | { 340 | address=outbound.address; 341 | level=outbound.level : 0; 342 | method=outbound.security; 343 | ota=false; 344 | password=outbound.id; 345 | port=outbound.port; 346 | } 347 | }; 348 | }; 349 | streamSettings={ 350 | network = outbound.network : "tcp"; 351 | }; 352 | tag="proxy" 353 | }; 354 | 355 | ..table.assign(current.outbounds[1],ob); 356 | } 357 | 358 | ..string.save(jsonPath,..web.json.stringify(current) ); 359 | return jsonPath; 360 | } 361 | 362 | -------------------------------------------------------------------------------- /lib/xray/github.aardio: -------------------------------------------------------------------------------- 1 | //github 代理设置 2 | import xray.core; 3 | import process.popen; 4 | namespace xray.github; 5 | 6 | setProxy = function(enableProxy){ 7 | if(enableProxy===null){ 8 | enableProxy = (..config.proxy.mode === "pac") || (..config.proxy.mode === "proxy") 9 | 10 | if(!..config.proxy.enableGitConfigGithub){ 11 | return; 12 | } 13 | } 14 | 15 | var socksPort = ..xray.core.socksProxyPort; 16 | if(!socksPort && enableProxy) return; 17 | 18 | var git; 19 | if(enableProxy){ 20 | git = ..process.popen("git config --global http.https://github.com.proxy socks5://127.0.0.1:" + socksPort) 21 | 22 | var path = ..io.getSpecial(0x28/*_CSIDL_PROFILE*/,".ssh/config") 23 | var str = ..string.load(path):""; 24 | if(..string.find(str,"Host\s+github.com" ) ){ 25 | if(!..string.find(str,"Host\s+github.com\s+ProxyCommand\s+connect\s+\-S\s+127\.0\.0\.1\:\d+ \-a none \%h \%p" )){ 26 | str = ..string.replace(str,"Host\s+github.com",'Host github.com\r\n ProxyCommand connect -S 127.0.0.1:' + socksPort + ' -a none %h %p') 27 | } 28 | } 29 | else { 30 | str = str + '\r\n\r\nHost github.com\r\n ProxyCommand connect -S 127.0.0.1:' + socksPort + ' -a none %h %p' 31 | } 32 | 33 | ..string.save(path,str) 34 | } 35 | else { 36 | git = ..process.popen("git config --global --unset http.https://github.com.proxy") 37 | var path = ..io.getSpecial(0x28/*_CSIDL_PROFILE*/,".ssh/config") 38 | var str = ..string.load(path):""; 39 | if(..string.find(str,"Host\s+github.com" ) ){ 40 | if(..string.find(str,"Host\s+github.com\s+ProxyCommand\s+connect\s+\-S\s+127\.0\.0\.1\:\d+ \-a none \%h \%p" )){ 41 | str = ..string.replace(str,"Host\s+github.com\s+ProxyCommand\s+connect\s+\-S\s+127\.0\.0\.1\:\d+ \-a none \%h \%p",'Host github.com') 42 | ..string.save(path,str) 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /lib/xray/outbounds.aardio: -------------------------------------------------------------------------------- 1 | //outbounds 出站代理 2 | import web.json; 3 | import inet.url; 4 | import win.clip; 5 | import crypt; 6 | 7 | namespace xray.outbounds; 8 | 9 | exportSharedLinks = function(outbounds){ 10 | var outString = {}; 11 | for i,outbound in ..table.eachIndex(outbounds){ 12 | if(outbound.protocol == "vmess"){ 13 | var json = ..web.json.stringify({ 14 | v = 2; 15 | add = outbound.address; 16 | aid = outbound.alterId; 17 | id = outbound.id; 18 | port = outbound.port; 19 | net = outbound.network; 20 | type = outbound.type; 21 | path = outbound.path; 22 | host = outbound.host; 23 | tls = outbound.tls; 24 | ps = outbound.ps; 25 | }) 26 | 27 | var vmess = "vmess://" + ..crypt.encodeBin(json); 28 | ..table.push(outString,vmess); 29 | } 30 | elseif(outbound.protocol == "shadowsocks"){ 31 | var ss = "ss://" + ..crypt.encodeBin( outbound.security + ":" + outbound.id + "@" + outbound.address + ":" + outbound.port) + "#" + outbound.address + ":" + outbound.port; 32 | ..table.push(outString,ss); 33 | } 34 | elseif(outbound.protocol == "trojan" || outbound.protocol == "trojan-go"){ 35 | var trojan = outbound.protocol + "://" + outbound.id + "@" + outbound.address + ":" + outbound.port; 36 | 37 | var info = {}; 38 | if(outbound.network!="tcp") info.type = outbound.network; 39 | if(outbound.sni){ info.sni = outbound.sni;} 40 | info.host = outbound.host; 41 | info.path = outbound.path; 42 | 43 | if(..table.count(info)){ 44 | trojan = ..inet.url.appendExtraInfo(trojan,info) 45 | } 46 | 47 | if(outbound.ps){ 48 | trojan = trojan + "#" + ..inet.url.encode(outbound.ps); 49 | } 50 | ..table.push(outString,trojan); 51 | } 52 | elseif(outbound.protocol == "vless"){ 53 | var vless = "vless://" + outbound.id + "@" + outbound.address + ":" + outbound.port; 54 | 55 | var info = {}; 56 | if(outbound.network!="tcp") info.type = outbound.network ; 57 | if(outbound.security!="none") info.encryption = outbound.security; 58 | if(outbound.tls) info.security = outbound.tls; 59 | if(outbound.flow) info.flow = outbound.flow; 60 | if(outbound.allowInsecure) info.allowInsecure = outbound.allowInsecure; 61 | if(outbound.alpn) info.alpn = outbound.alpn; 62 | if(outbound.disableSessionResumption) info.disableSessionResumption = outbound.disableSessionResumption; 63 | if(outbound.sni){ info.sni = outbound.sni;} 64 | info.host = outbound.host; 65 | info.path = outbound.path; 66 | info.seed = info.seed; 67 | info.key = info.key; 68 | info.headerType = info.type; 69 | 70 | if(..table.count(info)){ 71 | vless = ..inet.url.appendExtraInfo(vless,info) 72 | } 73 | 74 | if(outbound.ps){ 75 | vless = vless + "#" + ..inet.url.encode(outbound.ps); 76 | } 77 | ..table.push(outString,vless); 78 | } 79 | } 80 | 81 | return ..string.join(outString,'\r\n'); 82 | } 83 | 84 | var _subscriptionResponseCache = { 85 | @{_weak="kv"} 86 | } 87 | importFromString = function(str,subscribeUrl){ 88 | if(!str){ return; } 89 | str = ..string.trim(str); 90 | 91 | var jsonData; 92 | if(..string.match(str,"%\[\]") || ( 93 | ..string.match(str,"%\{\}") && !..string.match(str,"%\{\}.+%\{\}") 94 | ) ){ 95 | 96 | if(subscribeUrl){ 97 | var crc32 = ..string.crc32(str); 98 | _subscriptionResponseCache[subscribeUrl] = crc32; 99 | } 100 | jsonData = ..web.json.tryParse(str); 101 | } 102 | 103 | if( ..table.isArray(jsonData) ){ 104 | for(i=#jsonData;1;-1){ 105 | if( !validOutbound(jsonData[i]) ) { 106 | ..table.remove(jsonData,i); 107 | } 108 | 109 | if(subscribeUrl)jsonData[i].subscribeUrl = subscribeUrl; 110 | } 111 | 112 | return jsonData; 113 | } 114 | elseif( type(jsonData) == type.table ){ 115 | if( jsonData[["servers"]] ){ 116 | 117 | var servers = jsonData[["servers"]] 118 | var outbounds = ..table.array(); 119 | for i,v in ..table.eachIndex(servers){ 120 | var outbound = ..table.assign(,v); 121 | if(outbound.protocol === null){ 122 | outbound.protocol = (outbound.server_port&&outbound.password) ? "shadowsocks":"vless" 123 | } 124 | 125 | if(outbound.remarks){ 126 | outbound.ps = outbound.remarks; 127 | outbound.remarks = null; 128 | } 129 | 130 | if(outbound.server){ 131 | outbound.address = outbound.server; 132 | outbound.server = null; 133 | } 134 | 135 | if(outbound.server_port){ 136 | outbound.port = outbound.server_port; 137 | outbound.server_port = null; 138 | } 139 | 140 | if(outbound.password){ 141 | if(outbound.id){ 142 | outbound.serverId = outbound.id; 143 | } 144 | outbound.id = outbound.password; 145 | outbound.password = null; 146 | } 147 | 148 | if(outbound.method){ 149 | outbound.security = outbound.method; 150 | outbound.method = null; 151 | } 152 | 153 | if(subscribeUrl)outbound.subscribeUrl = subscribeUrl; 154 | ..table.push(outbounds,outbound); 155 | } 156 | 157 | return outbounds; 158 | } 159 | else { 160 | if( validOutbound(jsonData) ) { 161 | if(subscribeUrl)jsonData.subscribeUrl = subscribeUrl; 162 | return {jsonData}; 163 | } 164 | } 165 | } 166 | 167 | if( ..string.startWith(str,"http://") 168 | || ..string.startWith(str,"https://") 169 | || ..string.match(str,"^\s*/\N+\s*$") ){ 170 | 171 | import win.dlg.message; 172 | var msgDlg = win.dlg.message(..mainForm); 173 | 174 | var title = '正在获取订阅源 ... '; 175 | var cfgSubscribeUrls = ..config.proxy.subscribeUrls; 176 | for(i=#cfgSubscribeUrls;1;-1){ 177 | var sub = cfgSubscribeUrls[i] 178 | if( (sub.url === str) ){ 179 | if(#sub.ps) title = '正在获取订阅源: ' + sub.ps; 180 | ..config.proxy.save(); 181 | } 182 | } 183 | 184 | var form = msgDlg.create(title,,true) 185 | form.progress.startProgress(50); 186 | 187 | subscribeUrl = str; 188 | if(..string.match(subscribeUrl,"^\s*/\N+\s*$") ){ 189 | if(!..string.find(subscribeUrl,"/|")){ 190 | subscribeUrl = ..inet.url.append("https://raw.githubusercontent.com",subscribeUrl); 191 | } 192 | else { 193 | subscribeUrl = ..inet.url.append("https://github.com",subscribeUrl); 194 | } 195 | } 196 | 197 | str = ..win.invoke( 198 | function(subscribeUrl){ 199 | import inet.http; 200 | import web.rest.github; 201 | if( string.indexOf(subscribeUrl,"github.com") 202 | || string.indexOf(subscribeUrl,"raw.githubusercontent.com") 203 | ){ 204 | 205 | var ret = web.rest.github.getContent(subscribeUrl) 206 | if(ret) return ret; 207 | } 208 | 209 | return inet.http().get(subscribeUrl); 210 | },subscribeUrl 211 | ) 212 | 213 | form.close(); 214 | if(!str){ 215 | return; 216 | } 217 | 218 | var existIndex; 219 | var cfgSubscribeUrls = ..config.proxy.subscribeUrls : ..table.array(); 220 | for(i=#cfgSubscribeUrls;1;-1){ 221 | var sub = cfgSubscribeUrls[i] 222 | if(sub.url==subscribeUrl){ 223 | sub.checked="启用"; 224 | existIndex=i; 225 | break; 226 | } 227 | } 228 | if(!existIndex){ 229 | var tUrl = ..inet.url.split(subscribeUrl); 230 | ..table.push(cfgSubscribeUrls,{url=subscribeUrl;ps=tUrl ? tUrl.host : "";checked="启用"}) 231 | ..publish("uiCommand.subscriptionNew",cfgSubscribeUrls[#cfgSubscribeUrls]); 232 | } 233 | else{ 234 | ..publish("uiCommand.subscriptionNew",cfgSubscribeUrls[existIndex],existIndex); 235 | } 236 | ..config.proxy.subscribeUrls = cfgSubscribeUrls; 237 | ..config.proxy.save(); 238 | 239 | var jsonData; 240 | if(..string.match(str,"%\[\]") || ..string.match(str,"%\{\}")){ 241 | jsonData = ..web.json.tryParse(str); 242 | } 243 | 244 | if( type(jsonData) === type.table ){ 245 | return importFromString(str,subscribeUrl); 246 | } 247 | } 248 | 249 | if(subscribeUrl){ 250 | var crc32 = ..string.crc32(str); 251 | _subscriptionResponseCache[subscribeUrl] = crc32; 252 | } 253 | 254 | if(..string.match(str,"^[\w=+/-_]+$")){ 255 | str = ..crypt.decodeBin(str); 256 | if(!str) return; 257 | } 258 | 259 | var outbounds = ..table.array(); 260 | for(line in ..string.lines(str) ){ 261 | if(#outbounds>=1500){ 262 | import win.dlg.message; 263 | var msgDlg = win.dlg.message(..mainForm); 264 | var form = msgDlg.warn("导入的节点数目过多,已导入1500个节点并中止操作!") 265 | 266 | return outbounds; 267 | } 268 | 269 | if(line[1]=='/'# && line[2]=='/'#){ 270 | continue; 271 | } 272 | 273 | var vmess2 = ..string.match(line,`vmess\:\/\/[^\@\s]+\@[^\?\#\s\:]+\:\d+\S*`); 274 | if( vmess2 ) { 275 | var vmessUrl = ..inet.url.split(vmess2) 276 | if(vmessUrl.user && vmessUrl.host && vmessUrl.port){ 277 | var outbound = { 278 | address = vmessUrl.host; 279 | port = tonumber(vmessUrl.port) : 443; 280 | id = vmessUrl.user; 281 | network = "tcp"; 282 | protocol = "vmess"; 283 | tls = "tls"; 284 | }; 285 | 286 | if(vmessUrl.extraInfo){ 287 | var info = ..inet.url.splitParameters(vmessUrl.extraInfo); 288 | outbound.sni = info.sni; 289 | outbound.host = info.host; 290 | outbound.path = info.path; 291 | outbound.security = info.encryption; 292 | outbound.seed = info.seed; 293 | outbound.key = info.key; 294 | 295 | outbound.network = info.type; 296 | outbound.type = info.headerType; //kcp || quic 297 | 298 | if(info.security) outbound.tls = info.security; 299 | if(info.flow) outbound.flow = info.flow; 300 | if(info.allowInsecure!==null) outbound.allowInsecure = info.allowInsecure; 301 | if(info.disableSessionResumption) outbound.disableSessionResumption = info.disableSessionResumption; 302 | if(info.alpn) outbound.alpn = info.alpn; 303 | } 304 | 305 | if(vmessUrl.location){ 306 | outbound.ps = ..inet.url.decode(vmessUrl.location); 307 | } 308 | 309 | outbound.subscribeUrl = subscribeUrl; 310 | ..table.push(outbounds,outbound); 311 | } 312 | 313 | continue; 314 | } 315 | 316 | var vmess = ..string.match(line,`vmess\:\/\/([\w=+]+)`); 317 | if( vmess ) { 318 | var json = ..crypt.decodeBin(vmess); 319 | var outbound = ..web.json.tryParse(json); 320 | if(type(outbound)=="table"){ 321 | ..table.clear(outbound@._defined); 322 | outbound.protocol = "vmess"; 323 | outbound.address = outbound.add; 324 | outbound.alterId = tonumber(outbound.aid); 325 | outbound.security = "auto"; 326 | outbound.network = outbound.net : "tcp"; 327 | outbound.net = null; 328 | outbound.add = null; 329 | outbound.aid = null; 330 | outbound.v = null; 331 | outbound.subscribeUrl = subscribeUrl; 332 | 333 | if(outbound.ps){ 334 | var u,h,p = ..string.match(outbound.ps,"(.+)\@(.+)\:(\d+)") 335 | if(u&&h&&p){ 336 | if(h && ..string.endWith(h,"jamjams.net") && subscribeUrl && ..string.match(subscribeUrl,"justmysocks\d+\.net")){ 337 | if(..string.match(outbound.address,"^\d+\.\d+\.\d+\.\d+$") ){ 338 | outbound.address = h; 339 | } 340 | } 341 | outbound.ps = h; 342 | } 343 | } 344 | ..table.push(outbounds,outbound); 345 | } 346 | 347 | continue; 348 | } 349 | 350 | var vless = ..string.match(line,`vless\:\/\/[^\@\s]+\@[^\?\#\s\:]+\:\d+\S*`); 351 | if( vless ) { 352 | var vlessUrl = ..inet.url.split(vless) 353 | if(vlessUrl.user && vlessUrl.host && vlessUrl.port){ 354 | var outbound = { 355 | address = vlessUrl.host; 356 | port = tonumber(vlessUrl.port) : 443; 357 | id = vlessUrl.user; 358 | network = "tcp"; 359 | protocol = "vless"; 360 | tls = "tls"; 361 | }; 362 | 363 | if(vlessUrl.extraInfo){ 364 | var info = ..inet.url.splitParameters(vlessUrl.extraInfo); 365 | outbound.sni = info.sni; 366 | outbound.host = info.host; 367 | outbound.path = info.path; 368 | outbound.security = info.encryption; 369 | outbound.seed = info.seed; 370 | outbound.key = info.key; 371 | 372 | outbound.network = info.type; 373 | outbound.type = info.headerType; //kcp || quic 374 | 375 | if(info.security) outbound.tls = info.security; 376 | if(info.flow) outbound.flow = info.flow; 377 | if(info.allowInsecure!==null) outbound.allowInsecure = info.allowInsecure; 378 | if(info.disableSessionResumption) outbound.disableSessionResumption = info.disableSessionResumption; 379 | if(info.alpn) outbound.alpn = info.alpn; 380 | } 381 | 382 | if(vlessUrl.location){ 383 | outbound.ps = ..inet.url.decode(vlessUrl.location); 384 | } 385 | 386 | outbound.subscribeUrl = subscribeUrl; 387 | ..table.push(outbounds,outbound); 388 | } 389 | 390 | continue; 391 | } 392 | 393 | var ss,comment = ..string.match(line,`ss\:\/\/([^\s\#]+)(<#\S+>?)`); 394 | if( ss ) { 395 | var userinfo,address,port = ..string.match(ss,"^([^:]+)\@(.+)\:(\d+)") 396 | if(!(userinfo&&address&&port)){ 397 | var str = ..crypt.decodeBin(ss); 398 | if(str){ ss = str; } 399 | 400 | method,password,address,port = ..string.match(ss,"^(.+)\:(.+)\@(.+)\:(\d+)") 401 | } 402 | else { 403 | userinfo = ..crypt.decodeBin(userinfo); 404 | if(userinfo){ 405 | method,password = ..string.match(userinfo,"^(.+)\:(.+)$") 406 | } 407 | } 408 | 409 | if(address && password && port){ 410 | var outbound = { 411 | address = address; 412 | port = tonumber(port); 413 | security = method; 414 | id = password; 415 | network = "tcp"; 416 | protocol = "shadowsocks" 417 | }; 418 | 419 | if(comment){ 420 | comment = ..string.right(comment,-2); 421 | comment = ..inet.url.decode(comment); 422 | var u,h,p = ..string.match(comment,"(.+)\@(.+)\:(\d+)") 423 | if(u&&h&&p){ 424 | if(h && ..string.endWith(h,"jamjams.net") && subscribeUrl && ..string.match(subscribeUrl,"justmysocks\d+\.net")){ 425 | if(..string.match(outbound.address,"^\d+\.\d+\.\d+\.\d+$") ){ 426 | outbound.address = h; 427 | } 428 | } 429 | 430 | comment = h; 431 | } 432 | 433 | outbound.ps = comment; 434 | } 435 | 436 | outbound.subscribeUrl = subscribeUrl; 437 | ..table.push(outbounds,outbound); 438 | } 439 | 440 | continue; 441 | } 442 | 443 | var trojan = ..string.match(line,`trojan\:\/\/[^\@\s]+\@[^\?\#\s\:]+\:\d+\S*`); 444 | if( trojan ) { 445 | var trojanUrl = ..inet.url.split(trojan) 446 | if(trojanUrl.user && trojanUrl.host && trojanUrl.port){ 447 | var outbound = { 448 | address = trojanUrl.host; 449 | port = tonumber(trojanUrl.port) : 443; 450 | id = trojanUrl.user; 451 | network = "tcp"; 452 | protocol = "trojan" 453 | }; 454 | 455 | if(trojanUrl.extraInfo){ 456 | var info = ..inet.url.splitParameters(trojanUrl.extraInfo); 457 | outbound.sni = info.sni || info.peer; 458 | 459 | outbound.host = info.host; 460 | outbound.path = info.path; 461 | 462 | if(info.type && info.type!=="original"){ 463 | outbound.network = info.type; 464 | if(outbound.network=="h2"){ 465 | outbound.network="http" 466 | } 467 | } 468 | 469 | outbound.security = info.encryption; 470 | } 471 | 472 | if(trojanUrl.location){ 473 | outbound.ps = ..inet.url.decode(trojanUrl.location); 474 | } 475 | 476 | outbound.subscribeUrl = subscribeUrl; 477 | ..table.push(outbounds,outbound); 478 | } 479 | 480 | continue; 481 | } 482 | 483 | var trojan = ..string.match(line,`trojan-go\:\/\/[^\@\s]+\@[^\?\#\s\:]+\:\d+\S*`); 484 | if( trojan ) { 485 | var trojanUrl = ..inet.url.split(trojan) 486 | if(trojanUrl.user && trojanUrl.host && trojanUrl.port){ 487 | var outbound = { 488 | address = trojanUrl.host; 489 | port = tonumber(trojanUrl.port) : 443; 490 | id = trojanUrl.user; 491 | network = "tcp"; 492 | protocol = "trojan-go" 493 | }; 494 | 495 | if(trojanUrl.extraInfo){ 496 | var info = ..inet.url.splitParameters(trojanUrl.extraInfo); 497 | outbound.sni = info.sni || info.peer; 498 | outbound.host = info.host; 499 | outbound.path = info.path; 500 | 501 | if(info.type && info.type!=="original"){ 502 | outbound.network = info.type; 503 | if(outbound.network=="h2"){ 504 | outbound.network="http" 505 | } 506 | } 507 | 508 | outbound.security = info.encryption; 509 | } 510 | 511 | if(trojanUrl.location){ 512 | outbound.ps = ..inet.url.decode(trojanUrl.location); 513 | } 514 | 515 | outbound.subscribeUrl = subscribeUrl; 516 | ..table.push(outbounds,outbound); 517 | } 518 | 519 | continue; 520 | } 521 | 522 | var clashJson = ..string.match(line,`^\s*-\s*(%\{\})\s*$`); 523 | if( clashJson ) { 524 | var outbound = ..web.json.tryParse(clashJson) 525 | if(type(outbound)=="table"){ 526 | if(outbound.type){ 527 | if(outbound.type=="ssr"){ 528 | continue; 529 | } 530 | 531 | if(outbound.type=="ss"){ 532 | outbound.type = "shadowsocks"; 533 | outbound.network = "tcp"; 534 | } 535 | outbound.protocol = outbound.type; 536 | outbound.type = null; 537 | } 538 | 539 | if( validOutbound(outbound) ){ 540 | outbound.subscribeUrl = subscribeUrl; 541 | ..table.push(outbounds,outbound ); 542 | } 543 | continue; 544 | } 545 | } 546 | 547 | var json = ..string.match(line,`^\s*%\{\}\s*$`); 548 | if( json ) { 549 | var outbound = ..web.json.tryParse(json) 550 | if(type(outbound)=="table"){ 551 | if( validOutbound(outbound) ) ..table.push(outbounds,outbound ); 552 | continue; 553 | } 554 | } 555 | } 556 | 557 | return outbounds; 558 | } 559 | 560 | importFromClipboard = function(){ 561 | var str = ..win.clip.read(); 562 | if(!str){ return; } 563 | 564 | str = ..string.trim(str,'"\'\t\r\n '); 565 | return importFromString(str); 566 | } 567 | 568 | validAll = function(outbounds){ 569 | for(i=#outbounds;1;-1){ 570 | if(!validOutbound(outbounds[i])){ 571 | ..table.remove(outbounds,i); 572 | } 573 | } 574 | 575 | return outbounds; 576 | } 577 | 578 | validOutbound = function(outbound){ 579 | if(type(outbound)!="table"){ return; } 580 | 581 | if(outbound.aid){ 582 | outbound.alterId = outbound.aid; 583 | outbound.aid = null; 584 | } 585 | 586 | if(outbound.add){ 587 | outbound.address = outbound.add; 588 | outbound.add = null; 589 | ..table.define(outbound); 590 | } 591 | 592 | if(outbound.net){ 593 | outbound.network = outbound.net; 594 | outbound.net = null; 595 | ..table.define(outbound); 596 | } 597 | 598 | if(outbound.peer ){ 599 | if(#outbound.peer) outbound.sni = outbound.peer; 600 | outbound.peer = null; 601 | ..table.define(outbound); 602 | } 603 | 604 | if(!outbound.security){ 605 | if(outbound.method && outbound.method!="none"){ 606 | outbound.security = outbound.method; 607 | } 608 | if(outbound.cipher && outbound.cipher!="none" && outbound.cipher!="auto"){ 609 | outbound.security = outbound.cipher; 610 | } 611 | outbound.cipher = null; 612 | outbound.method = null; 613 | } 614 | 615 | if(!outbound.address){ 616 | if(outbound.server){ 617 | outbound.address = outbound.server; 618 | outbound.server = null; 619 | } 620 | } 621 | 622 | if(!outbound.ps){ 623 | if(outbound.remarks){ 624 | outbound.ps = outbound.remarks; 625 | outbound.remarks = null; 626 | } 627 | elseif(outbound.name){ 628 | outbound.ps = outbound.name; 629 | outbound.name = null; 630 | } 631 | } 632 | 633 | if(!outbound.port){ 634 | if(outbound.server_port){ 635 | outbound.port = outbound.server_port; 636 | outbound.server_port = null; 637 | 638 | if(!outbound.protocol && outbound.password){ 639 | outbound.protocol = "shadowsocks"; 640 | } 641 | } 642 | else { 643 | outbound.port=443; 644 | } 645 | } 646 | 647 | if(!outbound.protocol){ 648 | outbound.protocol = "vmess"; 649 | if(outbound.ps && (..string.find(outbound.ps,"@@vless") || ..string.find(outbound.ps,"@@xtls"))){ 650 | outbound.protocol = "vless"; 651 | } 652 | } 653 | 654 | if(!outbound.id){ 655 | if(outbound.password){ 656 | outbound.id = outbound.password; 657 | outbound.password = null; 658 | } 659 | if(outbound.uuid){ 660 | outbound.id = outbound.uuid; 661 | outbound.uuid = null; 662 | } 663 | } 664 | 665 | if(outbound["skip-cert-verify"]){ 666 | outbound.allowInsecure = outbound["skip-cert-verify"]; 667 | outbound["skip-cert-verify"] = null; 668 | } 669 | 670 | if(outbound["ws-path"]){ 671 | outbound.path = outbound["ws-path"]; 672 | outbound["ws-path"] = null; 673 | } 674 | 675 | if(outbound["ws-headers"]){ 676 | outbound.headers = outbound["ws-headers"]; 677 | outbound["ws-headers"] = null; 678 | } 679 | 680 | if(type(outbound.tls)!="string"){ 681 | if(outbound.tls) outbound.tls = "tls"; 682 | else outbound.tls = ""; 683 | } 684 | 685 | if(outbound.servername){ 686 | outbound.host = outbound.servername; 687 | outbound.servername = null; 688 | } 689 | 690 | var httpOpts = outbound["http-opts"]; 691 | if(httpOpts){ 692 | if(!..table.count(httpOpts)){ 693 | httpOpts = outbound["h2-opts"] : httpOpts; 694 | } 695 | 696 | if(httpOpts.method){ 697 | outbound.httpMethod = httpOpts.method; 698 | } 699 | 700 | if(httpOpts.path){ 701 | outbound.path = httpOpts.path; 702 | } 703 | 704 | if(httpOpts.headers){ 705 | outbound.headers = httpOpts.headers; 706 | } 707 | 708 | if(httpOpts.host){ 709 | outbound.host = httpOpts.host; 710 | } 711 | } 712 | 713 | if(outbound.headers){ 714 | var headers = {}; 715 | for k,v in ..table.eachName(outbound.headers){ 716 | var k = ..string.replace("HOST","(\a)(\a+)",lambda(a,b) ..string.upper(a) + ..string.lower(b) ); 717 | headers[k] = v; 718 | } 719 | outbound.headers = headers; 720 | 721 | if(outbound.headers.Host){ 722 | if(outbound.headers.Host==outbound.host){ 723 | outbound.headers.Host = null; 724 | } 725 | elseif(!outbound.host){ 726 | outbound.host = outbound.headers.Host; 727 | outbound.headers.Host = null; 728 | } 729 | } 730 | 731 | if(!..table.count(outbound.headers)){ 732 | outbound.headers = null; 733 | } 734 | } 735 | 736 | outbound["http-opts"] = null; 737 | outbound["h2-opts"] = null; 738 | outbound.country = null; 739 | outbound.v = null; 740 | 741 | ..table.define(outbound); 742 | 743 | if(outbound.address){ 744 | return outbound; 745 | } 746 | } 747 | 748 | updateSubscription = function(outbondSubscribeUrlMap){ 749 | 750 | var cfgOutbounds = ..config.proxy.outbounds : ..table.array(); 751 | if(!outbondSubscribeUrlMap) { 752 | outbondSubscribeUrlMap = {} 753 | 754 | for(i=#cfgOutbounds;1;-1){ 755 | var outbound = cfgOutbounds[i] 756 | if(outbound.subscribeUrl){ 757 | outbondSubscribeUrlMap[outbound.subscribeUrl] = true; 758 | } 759 | } 760 | 761 | var cfgSubscribeUrls = ..config.proxy.subscribeUrls; 762 | for(i=#cfgSubscribeUrls;1;-1){ 763 | var sub = cfgSubscribeUrls[i] 764 | if(sub.checked === "启用") { 765 | outbondSubscribeUrlMap[sub.url] = true; 766 | } 767 | else { 768 | outbondSubscribeUrlMap[sub.url] = null; 769 | } 770 | } 771 | 772 | if(!..table.count(outbondSubscribeUrlMap)){ 773 | ..mainForm.msgWarn('当前未启用任何订阅源,\n请先复制订阅源网址到剪贴板然后点击「批量导入链接」!'); 774 | return; 775 | } 776 | } 777 | 778 | var count = 0; 779 | for(url,data in outbondSubscribeUrlMap){ 780 | var impOutbounds = ..xray.outbounds.importFromString(url); 781 | if(#impOutbounds){ 782 | for(i=#cfgOutbounds;1;-1){ 783 | var outbound = cfgOutbounds[i] 784 | if(outbound.subscribeUrl==url){ 785 | ..table.remove( cfgOutbounds,i ); 786 | } 787 | } 788 | 789 | ..table.append(cfgOutbounds,impOutbounds); 790 | count = count + #impOutbounds; 791 | } 792 | } 793 | 794 | ..config.proxy.save(); 795 | ..publish("outbounds.updateConfigJson"); 796 | ..publish("uiCommand.restartXrayCore",cfgOutbounds) 797 | ..mainForm.msgOk("已刷新" + count + "个订阅服务器。",1500); 798 | 799 | return count; 800 | } 801 | 802 | autoUpdateSubscription = function(){ 803 | var outbondSubscribeUrlMap = {} 804 | var cfgSubscribeUrls = ..config.proxy.subscribeUrls; 805 | for(i=#cfgSubscribeUrls;1;-1){ 806 | var sub = cfgSubscribeUrls[i] 807 | if(!#sub.url){ 808 | continue; 809 | } 810 | 811 | if(sub.checked === "启用") { 812 | outbondSubscribeUrlMap[sub.url] = true; 813 | } 814 | } 815 | 816 | if(!..table.count(outbondSubscribeUrlMap)){ 817 | return; 818 | } 819 | 820 | ..thread.invoke( 821 | function(winform,outbondSubscribeUrlMap,responseCache){ 822 | import inet.http; 823 | import web.rest.github; 824 | 825 | var http = inet.http(); 826 | for(url,v in outbondSubscribeUrlMap){ 827 | 828 | if( string.indexOf(subscribeUrl,"github.com") 829 | || string.indexOf(subscribeUrl,"raw.githubusercontent.com") 830 | ){ 831 | outbondSubscribeUrlMap[url] = web.rest.github.getContent(url) 832 | } 833 | else{ 834 | outbondSubscribeUrlMap[url] = http.get(url); 835 | } 836 | 837 | if(!outbondSubscribeUrlMap[url]){ 838 | continue; 839 | } 840 | 841 | if(responseCache){ 842 | if(responseCache[url] && responseCache[url]===..string.crc32(outbondSubscribeUrlMap[url])){ 843 | outbondSubscribeUrlMap[url] = null; 844 | } 845 | } 846 | } 847 | 848 | winform.publish("thread.subscriptionUpdated",outbondSubscribeUrlMap) 849 | },..mainForm,outbondSubscribeUrlMap,_subscriptionResponseCache 850 | ) 851 | } 852 | 853 | ..subscribe("thread.subscriptionUpdated",function(outbondSubscribeUrlMap){ 854 | var cfgOutbounds = ..config.proxy.outbounds : ..table.array(); 855 | 856 | var count = 0; 857 | for(url,data in outbondSubscribeUrlMap){ 858 | 859 | var impOutbounds = ..xray.outbounds.importFromString(data,url); 860 | if(#impOutbounds){ 861 | for(i=#cfgOutbounds;1;-1){ 862 | var outbound = cfgOutbounds[i] 863 | if(outbound.subscribeUrl==url){ 864 | ..table.remove( cfgOutbounds,i ); 865 | } 866 | } 867 | 868 | ..table.append(cfgOutbounds,impOutbounds); 869 | count = count + #impOutbounds; 870 | } 871 | } 872 | 873 | if(!count){ 874 | return; 875 | } 876 | 877 | ..config.proxy.outbounds = cfgOutbounds; 878 | ..config.proxy.save(); 879 | 880 | ..publish("outbounds.updateConfigJson"); 881 | ..publish("uiCommand.restartXrayCore") 882 | ..publish("uiCommand.print","已自动刷新" + count + "个订阅服务器。") 883 | } ) 884 | 885 | /**intellisense(xray.outbounds) 886 | exportSharedLinks(.(outbounds) = 导出分享链接 887 | importFromString(.(str,subscribeUrl) = 导入分享链接 888 | importFromClipboard() = 自剪贴板导入分享链接 889 | autoUpdateSubscription() = 自动并静默更新订阅源 890 | updateSubscription() = 更新或自剪贴板导入订阅源\n可选在参数中用一个表自定义要更新的订阅地址,\n参数表中键为url,值为true 891 | end intellisense**/ 892 | -------------------------------------------------------------------------------- /lib/xray/pacServer.aardio: -------------------------------------------------------------------------------- 1 | //pacServer 自动代理服务 2 | import config; 3 | import wsock.tcp.simpleHttpServer; 4 | import inet.http; 5 | import inet.url; 6 | 7 | namespace xray.pacServer; 8 | $serverMain = ..wsock.tcp.simpleHttpServer.mainThread( 9 | function(response,request,session){ 10 | 11 | if(request.path=="/proxy.pac"){ 12 | var pacData = ..string.load(pacPath); 13 | var proxPorts = inboundPort.get(); 14 | if(!proxPorts[["socksProxy"]]){ 15 | response.errorStatus(404); 16 | return; 17 | } 18 | 19 | var reps = ..string.replace(pacData,"var\s+proxy\s+=\s+%''","var proxy = 'SOCKS5 "+ request.environ.SERVER_NAME +":"+proxPorts.socksProxy +";SOCKS "+ request.environ.SERVER_NAME +":"+proxPorts.socksProxy +"'"); 20 | if(useHttpProxy.get()){ 21 | reps = ..string.replace(pacData,"var\s+proxy\s+=\s+%''","var proxy = 'PROXY "+ request.environ.SERVER_NAME +":"+proxPorts.httpProxy +"'"); 22 | } 23 | 24 | response.write(reps); 25 | } 26 | } 27 | ); 28 | $serverMain.threadNum = 50; 29 | 30 | var pacPath = ..io.appData("/winXray/pac.txt"); 31 | resetPac = function(){ 32 | ..string.save(pacPath,$"/pac.txt" ) 33 | } 34 | 35 | if( ! ..io.exist(pacPath) ){ 36 | resetPac(); 37 | } 38 | 39 | loadPacText = function(){ 40 | return ..io.exist(pacPath) ? ..string.load(pacPath) || $"/pac.txt"; 41 | } 42 | 43 | savePacText = function(json){ 44 | var str = ..string.replace($"/pac.txt","var\s+rules\s*=\s*%\[\]","var rules = " + json); 45 | ..string.save(pacPath,str ); 46 | } 47 | 48 | var inboundPort = ..thread.var(); 49 | var useHttpProxy = ..thread.var(); 50 | restart = function(){ 51 | useHttpProxy.set(..config.proxy.useHttpGlobal); 52 | 53 | $serverMain.threadGlobal = { 54 | inboundPort = inboundPort; 55 | pacPath = pacPath; 56 | useHttpProxy = useHttpProxy; 57 | } 58 | 59 | var pacPort = ..config.proxy.pacPort; 60 | if( (! pacPort) || pacPort >= 49152 ){ pacPort = ..wsock.tcp.server.getFreePort("127.0.0.1",1083,10803,10813,44823) } 61 | 62 | var currentIp,currentPort = $serverMain.getLocalIp(); 63 | if(currentIp!==..config.core.default.inbounds[[1]][["listen"]] 64 | || currentPort != pacPort ){ 65 | $serverMain.stop(); 66 | 67 | var ok,err = $serverMain.start( 68 | ..config.core.default.inbounds[[1]][["listen"]] : "127.0.0.1",pacPort ); 69 | if(!ok){ 70 | ..publish("uiCommand.print","启动PAC服务器失败",err) 71 | return false,err; 72 | } 73 | 74 | ..publish("uiCommand.print","PAC服务器已启动",getUrl()) 75 | ..publish("pacServer.restarted",pacPort); 76 | } 77 | 78 | return true; 79 | } 80 | 81 | stop = function(){ 82 | if($serverMain){ 83 | $serverMain.stop(); 84 | } 85 | } 86 | 87 | updateUseHttpProxy = function(httpProxy){ 88 | useHttpProxy.set(httpProxy); 89 | } 90 | 91 | updateConfig = function(socksProxyPort,httpProxyPort){ 92 | if(socksProxyPort && httpProxyPort){ 93 | inboundPort.set( {socksProxy=socksProxyPort;httpProxy=httpProxyPort} ); 94 | } 95 | } 96 | 97 | ..subscribe("xrayCore.restarted",function(socksProxyPort,httpProxyPort){ 98 | updateConfig(socksProxyPort,httpProxyPort); 99 | } ) 100 | 101 | getUrl = function(localIp){ 102 | var url = $serverMain.getUrl("/proxy.pac?" + ..time.tick() ) 103 | if(!url){ return } 104 | 105 | var tUrl = ..inet.url.split(url); 106 | if(!localIp){ 107 | tUrl.host = "127.0.0.1" 108 | } 109 | else { 110 | tUrl.host = ..wsock.tcp.client.getLocalIp("www.baidu.com"); 111 | } 112 | 113 | return tostring(tUrl); 114 | } 115 | 116 | getPort = function(){ 117 | var ip,port = $serverMain.getLocalIp(); 118 | return port; 119 | } 120 | 121 | 122 | /**intellisense(xray.pacServer) 123 | stop() = 停止PAC服务器 124 | restart() = 重启PAC服务器 125 | getUrl(.(localIp) = 返回PAC服务地址 126 | getPort() = 返回PAC端口 127 | updateConfig(.(socksProxyPort,httpProxPort) = 更新代理端口或PAC数据 128 | resetPac() = 重置为默认PAC 129 | end intellisense**/ 130 | -------------------------------------------------------------------------------- /main.aardio: -------------------------------------------------------------------------------- 1 | /*启动参数检测{{*/ 2 | if(_ARGV.github){ 3 | loadcodex("\forms\main\tools\github.aardio"); 4 | return; 5 | } 6 | elseif(_ARGV.sshInstall){ 7 | import fsys.wow64; 8 | import process; 9 | import console; 10 | console.open(); 11 | 12 | var prcs; 13 | fsys.wow64.disableRedirection( 14 | function(){ 15 | var dest,port = string.match(_ARGV.sshInstall,"(.+)\:(\d+)"); 16 | if(dest&&port){ 17 | _ARGV.sshInstall = "-p" + port +" " + dest; 18 | } 19 | 20 | prcs = process("ssh","-t " +_ARGV.sshInstall + ` "echo -e '安装完成以后,您可以一次性复制所有服务器账号配置 - 然后在 winXray 主界面点击\e[1;31m「批量导入链接」\e[0m即可,winXray将逐行识别有效的服务器分享链接或通用JSON配置,并忽略无效的行。如果 CentOS 8安装失败 - 可尝试更换为 CentOS 7 并重新安装。\n';firewall-cmd --permanent --add-port=80/tcp;firewall-cmd --permanent --add-port=443/tcp;firewall-cmd --reload;iptables -A INPUT -p tcp --dport 80 -j ACCEPT;iptables -A INPUT -p tcp --dport 443 -j ACCEPT;wget -P /root -N --no-check-certificate ""https://raw.githubusercontent.com/mack-a/v2ray-agent/master/install.sh"" && chmod 700 /root/install.sh && /root/install.sh"`) 21 | if(!prcs){ 22 | if(_WIN10_LATER){ 23 | import process.control; 24 | process.control("ms-settings:optionalfeatures"); 25 | console.log("请检查是否指定了正确的SSH端口,或尝试重新安装 OpenSSH.Client"); 26 | } 27 | else { 28 | console.log("此功能仅支持 Windows 10 系统"); 29 | } 30 | } 31 | else { 32 | import process.job.limitKill; 33 | prcs.assignToJobObject(process.job.limitKill); 34 | prcs.wait(); 35 | } 36 | 37 | console.pause(); 38 | } 39 | ) 40 | 41 | return; 42 | } 43 | elseif(_ARGV.sshLogin){ 44 | import fsys.wow64; 45 | import process; 46 | import console; 47 | console.open(); 48 | 49 | var prcs; 50 | fsys.wow64.disableRedirection( 51 | function(){ 52 | var dest,port = string.match(_ARGV.sshLogin,"(.+)\:(\d+)"); 53 | if(dest&&port){ 54 | _ARGV.sshLogin = "-p" + port +" " + dest; 55 | } 56 | 57 | prcs = process("ssh","-t " +_ARGV.sshLogin + ``) 58 | if(!prcs){ 59 | if(_WIN10_LATER){ 60 | import process.control; 61 | process.control("ms-settings:optionalfeatures"); 62 | console.log("请检查是否指定了正确的SSH端口,或尝试重新安装 OpenSSH.Client"); 63 | } 64 | else { 65 | console.log("此功能仅支持 Windows 10 系统"); 66 | } 67 | } 68 | else { 69 | import process.job.limitKill; 70 | prcs.assignToJobObject(process.job.limitKill); 71 | prcs.wait(); 72 | } 73 | 74 | console.pause(); 75 | } 76 | ) 77 | 78 | return; 79 | } 80 | elseif(_ARGV.sshInstallKey){ 81 | import fsys.wow64; 82 | import process; 83 | import console; 84 | console.open(); 85 | 86 | var prcs; 87 | fsys.wow64.disableRedirection( 88 | function(){ 89 | var sshKeyPath = io.getSpecial(0x28/*_CSIDL_PROFILE*/,"/.ssh/id_rsa.pub") ; 90 | var keyData = string.load(sshKeyPath); 91 | if(!keyData){ 92 | var prcs = process.popen("ssh-keygen",` -t rsa -f "`+io.getSpecial(0x28/*_CSIDL_PROFILE*/,"/.ssh/id_rsa")+`" -y`); 93 | keyData = prcs? prcs.read(-1); 94 | } 95 | 96 | if(!keyData){ 97 | if(_WIN10_LATER){ 98 | import process.control; 99 | process.control("ms-settings:optionalfeatures"); 100 | console.log("请检查是否指定了正确的SSH端口,或尝试重新安装 OpenSSH.Client"); 101 | } 102 | else { 103 | console.log("此功能仅支持 Windows 10 系统"); 104 | } 105 | } 106 | else{ 107 | var dest,port = string.match(_ARGV.sshInstallKey,"(.+)\:(\d+)"); 108 | if(dest&&port){ 109 | _ARGV.sshInstallKey = "-p" + port +" " + dest; 110 | } 111 | 112 | prcs = process("ssh","-t " +_ARGV.sshInstallKey + ` "mkdir -p ~/.ssh;chmod 700 ~/.ssh;chmod 600 ~/.ssh/authorized_keys;echo '`+keyData+`' > ~/.ssh/authorized_keys"`) 113 | if(!prcs){ 114 | if(_WIN10_LATER){ 115 | import process.control; 116 | process.control("ms-settings:optionalfeatures"); 117 | console.log("请检查是否指定了正确的SSH端口,或尝试重新安装 OpenSSH.Client"); 118 | } 119 | else { 120 | console.log("此功能仅支持 Windows 10 系统"); 121 | } 122 | } 123 | else { 124 | import process.job.limitKill; 125 | prcs.assignToJobObject(process.job.limitKill); 126 | prcs.wait(); 127 | } 128 | } 129 | 130 | console.pause(); 131 | } 132 | ) 133 | 134 | return; 135 | } 136 | elseif(_ARGV.updateTime){ 137 | import time.ntp; 138 | time.ntp.updateSystemTime(); 139 | return; 140 | } 141 | else { 142 | import process; 143 | import fsys.update.simpleMain; 144 | 145 | if( fsys.update.simpleMain( 146 | "winXray(Xray/V2Ray、Shadowsocks、Trojan通用客户端 )", 147 | process().isWow64() ? "https://raw.githubusercontent.com/winXray/winXray/master/release/update/version.txt" 148 | : "https://raw.githubusercontent.com/winXray/winXray/master/release/update32/version.txt", 149 | ..io.appData("/winXray/update"), 150 | function(version,description,status){})){ 151 | return 0; 152 | } 153 | } 154 | 155 | /*}}*/ 156 | 157 | import fonts.fontAwesome; 158 | import win.ui; 159 | /*DSG{{*/ 160 | mainForm = win.form(text="winXray";right=1019;bottom=679;bgcolor=15793151;border="none") 161 | mainForm.add( 162 | caption={cls="bkplus";text="winXray 2.3";left=67;top=9;right=727;bottom=27;align="left";color=6052956;dl=1;dt=1;font=LOGFONT(h=-14);z=7}; 163 | custom={cls="custom";left=83;top=40;right=1022;bottom=679;bgcolor=16777215;db=1;dl=1;dr=1;dt=1;z=4}; 164 | 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}; 165 | navBar={cls="bkplus";left=0;top=37;right=83;bottom=681;bgcolor=3442175;db=1;dl=1;dt=1;z=1}; 166 | 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}; 167 | 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}; 168 | 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}; 169 | 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} 170 | ) 171 | /*}}*/ 172 | 173 | import win.ui.atom; 174 | var atom,hwnd = mainForm.atom("DFA5667E-1D8C-49C0-8918-C6FEC2DECCF8"); 175 | if(!atom){ 176 | win.showForeground(hwnd); 177 | win.quitMessage(); 178 | return; 179 | } 180 | 181 | import xray.core; 182 | if(!xray.core.getPath()) { 183 | mainForm.close(); 184 | return; 185 | } 186 | 187 | import win.ui.simpleWindow; 188 | win.ui.simpleWindow( mainForm ); 189 | 190 | import win.dlg.message; 191 | win.dlg.message.install(); 192 | 193 | import win.ui.tabs; 194 | var tbs = win.ui.tabs( 195 | mainForm.navStart, 196 | mainForm.navJsonConfig, 197 | mainForm.navTools 198 | ); 199 | 200 | tbs.skin({ 201 | background={ 202 | active=0xFFFFFFFF; 203 | default=0x00FFFFFF; 204 | hover=0x38FFFFFF 205 | }; 206 | color={ 207 | default=0xFFFFFFFF; 208 | }; 209 | checked={ 210 | background={default=0xFFFFFFFF;}; 211 | color={default=0xff86a543;}; 212 | } 213 | }) 214 | 215 | tbs.loadForm(1,"\forms\main\xray.aardio" ); 216 | tbs.loadForm(2,"\forms\main\config\jsonConfig.aardio" ); 217 | tbs.loadForm(3,"\forms\main\tools\tools.aardio"); 218 | 219 | tbs.selIndex = 1; 220 | 221 | import win.ui.menu; 222 | mainForm.wndproc = { 223 | [0xACCF/*_WM_TRAYMESSAGE*/ ] = function(hwnd,message,wParam,lParam){ 224 | if( lParam = 0x205/*_WM_RBUTTONUP*/ ){ 225 | win.setForeground(mainForm.hwnd) 226 | mainForm.popmenu = win.ui.popmenu(mainForm); 227 | mainForm.popmenu.add('自动切换到最快的服务器',function(id){ 228 | publish("uiCommand.restartXrayCore",) 229 | }); 230 | mainForm.popmenu.add('立即更新所有订阅源',function(id){ 231 | xray.outbounds.updateSubscription(); 232 | }); 233 | mainForm.popmenu.add(); 234 | 235 | import sysProxy; 236 | import xray.core; 237 | 238 | var id = mainForm.popmenu.add('不使用代理',function(id){ 239 | sysProxy.switch("direct"); 240 | }); 241 | mainForm.popmenu.check(id,config.proxy.mode=="direct",0/*_MF_BYCOMMAND*/); 242 | 243 | var id = mainForm.popmenu.add('使用全局代理',function(id){ 244 | sysProxy.switch("proxy"); 245 | }); 246 | mainForm.popmenu.check(id,config.proxy.mode=="proxy",0/*_MF_BYCOMMAND*/); 247 | 248 | var id = mainForm.popmenu.add('使用 PAC 自动代理',function(id){ 249 | sysProxy.switch("pac"); 250 | }); 251 | mainForm.popmenu.check(id,config.proxy.mode=="pac",0/*_MF_BYCOMMAND*/); 252 | 253 | mainForm.popmenu.add(); 254 | 255 | var id = mainForm.popmenu.add('编辑 PAC 配置',function(id){ 256 | publish("uiCommand.showPacForm"); 257 | }); 258 | 259 | mainForm.popmenu.add(); 260 | 261 | if(_WIN10_LATER){ 262 | mainForm.popmenu.add('打开系统代理设置',function(id){ 263 | import process.control; 264 | process.control("ms-settings:network-proxy") 265 | }); 266 | mainForm.popmenu.add('打开 Internet 代理选项',function(id){ 267 | import process.control; 268 | process.control("inetcpl.cpl",,4); 269 | }); 270 | } 271 | else { 272 | mainForm.popmenu.add('打开系统代理设置',function(id){ 273 | import process.control; 274 | process.control("inetcpl.cpl",,4); 275 | }); 276 | } 277 | 278 | mainForm.popmenu.add('打开网卡连接',function(id){ 279 | import process; 280 | process.explore("shell:::{7007ACC7-3202-11D1-AAD2-00805FC1270E}"); 281 | }); 282 | mainForm.popmenu.add(); 283 | 284 | mainForm.popmenu.add('获取 winXray 最新源代码',function(id){ 285 | import process; 286 | process.openUrl("https://github.com/search?q=winXray+license%3AUnlicense+fork%3Atrue+sort%3Aupdated") 287 | }); 288 | 289 | import xray.core; 290 | import chrome.path; 291 | var chromePath = chrome.path(); 292 | if(chromePath && xray.core.socksProxyPort){ 293 | mainForm.popmenu.add('使用 SOCKS5 代理打开浏览器',function(id){ 294 | import process 295 | process.execute(chromePath,` --proxy-server="SOCKS5://127.0.0.1:`+xray.core.socksProxyPort 296 | +`" --user-data-dir="` +io.appData("winXray/chrome-socks5")+ `" https://www.google.com`); 297 | }); 298 | } 299 | 300 | mainForm.popmenu.add(); 301 | mainForm.popmenu.add('退出',function(id){ 302 | mainForm.onClose = null; 303 | mainForm.close() 304 | }); 305 | 306 | var pt = ::POINT(); 307 | ::User32.GetCursorPos(pt); 308 | mainForm.popmenu.popup(pt.x,pt.y,true) 309 | } 310 | elseif( lParam = 0x203/*_WM_LBUTTONDBLCLK*/) { 311 | if(win.isIconic(mainForm.hwnd)){ 312 | mainForm.show(9/*_SW_RESTORE*/); 313 | } 314 | else { 315 | mainForm.show(); 316 | } 317 | 318 | win.setForeground(mainForm.hwnd); 319 | } 320 | } 321 | } 322 | 323 | mainForm.enableDpiScaling(); 324 | 325 | mainForm.onDestroy = function(){ 326 | mainForm.tray.delete(); 327 | } 328 | 329 | import win.util.tray; 330 | mainForm.onClose = function(hwnd,message,wParam,lParam){ 331 | if(!mainForm.tray){ 332 | mainForm.tray = win.util.tray(mainForm); 333 | } 334 | else { 335 | mainForm.tray.reset(); 336 | } 337 | 338 | mainForm.show(false); 339 | return true; 340 | } 341 | 342 | mainForm.tray = win.util.tray(mainForm) 343 | if(!_ARGV.tray){ 344 | mainForm.show(); 345 | } 346 | 347 | return win.loopMessage(); 348 | -------------------------------------------------------------------------------- /release/update/checksum.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/update/checksum.lzma -------------------------------------------------------------------------------- /release/update/files/winXray.exe.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/update/files/winXray.exe.lzma -------------------------------------------------------------------------------- /release/update/files/xray-core/LICENSE.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/update/files/xray-core/LICENSE.lzma -------------------------------------------------------------------------------- /release/update/files/xray-core/winXray-default-servers.json.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/update/files/xray-core/winXray-default-servers.json.lzma -------------------------------------------------------------------------------- /release/update/files/xray-core/xray.exe.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/update/files/xray-core/xray.exe.lzma -------------------------------------------------------------------------------- /release/update/version.txt: -------------------------------------------------------------------------------- 1 | { 2 | "description":"发布 v2.3,修正刷新clash订阅连接重复添加服务器,改进订阅管理等。", 3 | "format":".lzma", 4 | "main":"\\winXray.exe", 5 | "updater":"\\winXray.exe", 6 | "url":"https://raw.githubusercontent.com/winXray/winXray/master/release/update/", 7 | "version":"2.3" 8 | } -------------------------------------------------------------------------------- /release/update32/checksum.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/update32/checksum.lzma -------------------------------------------------------------------------------- /release/update32/files/winXray.exe.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/update32/files/winXray.exe.lzma -------------------------------------------------------------------------------- /release/update32/files/xray-core/LICENSE.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/update32/files/xray-core/LICENSE.lzma -------------------------------------------------------------------------------- /release/update32/files/xray-core/winXray-default-servers.json.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/update32/files/xray-core/winXray-default-servers.json.lzma -------------------------------------------------------------------------------- /release/update32/files/xray-core/xray.exe.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/update32/files/xray-core/xray.exe.lzma -------------------------------------------------------------------------------- /release/update32/version.txt: -------------------------------------------------------------------------------- 1 | { 2 | "description":"发布 v2.3,修正刷新clash订阅连接重复添加服务器,改进订阅管理等。", 3 | "format":".lzma", 4 | "main":"\\winXray.exe", 5 | "updater":"\\winXray.exe", 6 | "url":"https://raw.githubusercontent.com/winXray/winXray/master/release/update32/", 7 | "version":"2.3" 8 | } -------------------------------------------------------------------------------- /release/winXray.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/winXray.7z -------------------------------------------------------------------------------- /release/winXray32.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/release/winXray32.7z -------------------------------------------------------------------------------- /screenshots/config.advanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/screenshots/config.advanced.png -------------------------------------------------------------------------------- /screenshots/config.json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/screenshots/config.json.png -------------------------------------------------------------------------------- /screenshots/pac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/screenshots/pac.png -------------------------------------------------------------------------------- /screenshots/telegram.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/screenshots/telegram.gif -------------------------------------------------------------------------------- /screenshots/winXray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/screenshots/winXray.png -------------------------------------------------------------------------------- /sub/introduce.md: -------------------------------------------------------------------------------- 1 | # winXray 小技巧 2 | 3 | ### 一、批量导入链接技巧 4 | 5 | winXray 最新版支持直接使用 github 网址作为订阅链接。例如,我们在浏览器里访问 github项目文件的地址是这样的: [https://github.com/winXray/winXray/blob/master/sub/sample.json](./sample.json) 我们直接复制前面的链接,然后在 winXray 中点击 **「批量导入链接」** 就可以导入里面的服务器配置了。 6 | 7 | winXray可以兼容以下格式的github文件地址( 支持省略域或并以斜杆开头的地址 ): 8 | https://github.com/winXray/winXray/blob/master/sub/sample.json 9 | [/winXray/blob/master/sub/sample.json](./sample.json) 10 | https://github.com/winXray/winXray/raw/master/sub/sample.json 11 | [/winXray/winXray/raw/master/sub/sample.json](./sample.json) 12 | https://raw.githubusercontent.com/winXray/winXray/master/sub/sample.json 13 | 14 | 即使在国内打不开 githubusercontent.com,winXray 仍然可以机智地调用 github 的API 顺利地把目标文件下载下来。 15 | 16 | winXray 的订阅链接可以返回所有 winXray 能兼容的服务器配置,例如: 17 | 1、直接返回一行或多行分享链接,支持vmess,vless,ss,trojan,trojan-go 这一堆的各种通用分享格式,每行一个有效链接,无效的行 winXray 会自动忽略。 18 | 2、可以每行一个服务器配置JSON,也可以把多个服务器配置JSON放到一个数组里(就是放到方括号里),winXray都能兼容,JSON推荐使用 winXray 的语法 - winXray已经把各种不同的代理协议配置规范化为了几个简单且通用的字段。当然你可以返回一些 winXray 可以兼容的通用JSON,winXray 会最大可能的识别并转换各种JSON格式,例如服务器的字段名你可以写为add,address,server 等等不同的名字。 19 | 服务器可以返回base64编码的配置,也可以直接返回服务器配置,winXray都能识别。 winXray可以导入v2ray,、Shadowsocks、trojan等通用订阅链接,也可以导入 Clash proxy-provider 配置,winXray将自动转化各种不兼容的配置为统一、规范的格式。 20 | 21 | 我们平时复制配置和链接导入 winXray 一样,winXray都会最大可能的识别并兼容各种格式,并且尝试自动清除复制文本中的无效内容。例如使用 winXray 自带的 v2ray agent 安装服务端以后,我们不需要在给出的配置中挑选出JSON慢慢的修改和设置,你只需要把安装程序返回的一大堆账号配置文本直接复制出来(包含各种无效的、无关的说明文字), winXray 会自动分析识别并导入有效的服务器配置。 22 | 23 | 有些人崇尚复杂,但是winXray 的目标就是让一切变得更简单,软件能自动做的事就自动做好,**人生苦短,哪有那么多时间折腾?!** 24 | 25 | ### 二、使用JSON配置服务器技巧 26 | ![服务器配置](./../screenshots/config.json.png) 27 | JSON配置界面里点击任意字段都会显示该字段的用法说明。个人认为做很多对话框来配置服务器的参数是非常蠢的,winXray已经把各种代理协议的配置简化为几个统一命名的JSON字段( **也可以作为一种标准的、统一的、通用的订阅响应格式使用** ),只要稍加学习就可以非常熟练的添加、修改各种代理协议的配置。而且对于大多数用户根本不需要改配置 - 简单的复制导入分享链接就可以,我们不必要把简单的事搞复杂。 28 | 29 | ### 三、Telegram 代理设置 30 | 因为Telegram 使用的IP非常多,PAC代理确实不好支持Telegram ,但是使用IP路由去适配Telegram其实是不必要的,试想一下一个IP路由的数据就几十MB - 有这个必要吗?!我们为什么不直接告诉 Telegram 他要使用的代理服务器地址,而是 他在每个IP请求的时候都去判断一下呢?。 31 | 32 | 所以更简洁、更聪明的方法就是直接告诉 Telegram 他要使用的代理服务器地址,这个操作非常简单 - 绝对比下载 Telegram 更简单,请看下面的动画: 33 | 34 | ![Telegram 代理设置](./../screenshots/telegram.gif) 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /sub/sample.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "address":"us.winray.xyz", 4 | "flow":"xtls-rprx-direct", 5 | "host":"us.winray.xyz", 6 | "id":"0d841558-eade-cfa9-f3ea-b6ab735a7867", 7 | "port":443, 8 | "protocol":"vless", 9 | "ps":"美国西雅图xtls黑科技", 10 | "sni":"us.winray.xyz", 11 | "tls":"xtls" 12 | }, 13 | { 14 | "address":"jp.winray.xyz", 15 | "flow":"xtls-rprx-direct", 16 | "host":"jp.winray.xyz", 17 | "id":"1fd55a0e-67e1-a70e-acdc-86605b0b7f00", 18 | "port":443, 19 | "protocol":"vless", 20 | "ps":"日本东京xtls黑科技", 21 | "sni":"jp.winray.xyz", 22 | "tls":"xtls" 23 | }, 24 | { 25 | "address":"www.aech.cc", 26 | "alterId":64, 27 | "host":"", 28 | "id":"ef3c6aa0-e5f2-4332-bad8-17c0438dfaa3", 29 | "network":"ws", 30 | "path":"/ray", 31 | "port":443, 32 | "protocol":"vmess", 33 | "ps":"香港4", 34 | "security":"auto", 35 | "tls":"tls", 36 | "type":"none" 37 | }, 38 | { 39 | "address":"23.225.125.86", 40 | "alterId":64, 41 | "host":"www.56626173.xyz", 42 | "id":"8fb921c0-564d-4d5a-9b13-384c01820126", 43 | "network":"ws", 44 | "path":"/footers", 45 | "port":443, 46 | "protocol":"vmess", 47 | "ps":"美国4", 48 | "security":"auto", 49 | "tls":"tls", 50 | "type":"none" 51 | }, 52 | { 53 | "address":"104.16.160.20", 54 | "alterId":2, 55 | "host":"freeus.mcan.tech", 56 | "id":"9e6ceeff-2546-3690-ac00-6fcdf31dec94", 57 | "network":"ws", 58 | "path":"/y284", 59 | "port":443, 60 | "protocol":"vmess", 61 | "ps":"美国4", 62 | "security":"auto", 63 | "sni":"freeus.mcan.tech", 64 | "tls":"tls", 65 | "type":"none" 66 | }, 67 | { 68 | "address":"llajv.rebldp.tech", 69 | "alterId":64, 70 | "host":"llajv.rebldp.tech", 71 | "id":"3b5e258e-8c5e-45d3-b7d2-02c8f5fc0bb2", 72 | "network":"ws", 73 | "path":"/", 74 | "port":443, 75 | "protocol":"vmess", 76 | "ps":"德国4", 77 | "security":"auto", 78 | "tls":"tls", 79 | "type":"none" 80 | }, 81 | { 82 | "address":"154.84.1.140", 83 | "alterId":64, 84 | "host":"154.84.1.140", 85 | "id":"2a7349d6-994a-434b-9589-c0020685e528", 86 | "network":"tcp", 87 | "path":"/", 88 | "port":54671, 89 | "protocol":"vmess", 90 | "ps":"荷兰4", 91 | "security":"auto", 92 | "tls":"", 93 | "type":"none" 94 | }, 95 | { 96 | "address":"consum2.cukee.cc", 97 | "alterId":64, 98 | "host":"consum2.cukee.cc", 99 | "id":"02914F25-41C8-3114-50F3-19359B631C77", 100 | "network":"ws", 101 | "path":"/", 102 | "port":443, 103 | "protocol":"vmess", 104 | "ps":"法国4", 105 | "security":"auto", 106 | "tls":"tls", 107 | "type":"none" 108 | } 109 | ] -------------------------------------------------------------------------------- /xray-core/LICENSE: -------------------------------------------------------------------------------- 1 | https://github.com/XTLS/Xray-core/blob/main/LICENSE -------------------------------------------------------------------------------- /xray-core/winXray-default-servers.json: -------------------------------------------------------------------------------- 1 | // 号开头为注释行。 2 | // 本文件用于指定默认服务器列表。 3 | // 本文件可以直接输入json格式的服务器数组( 服务器可以使用subscribeUrl字段指定订阅源地址,可以指定"autoConnect":false字段禁止自动连接 ) 4 | // 也可以每行一个 vless://、vmess://、trojan、trojan-go、ss:// 等分享链接 5 | [ 6 | { 7 | "address":"服务器地址", 8 | "port":443, // 服务器端口 9 | "id":"UUID或密码", // v2ray就指定UUID,shadowsocks在这里指定密码 10 | "network":"tcp", // 网络协议可以改为 "tcp", "ws" 等 11 | "security":"none", // 加密方法,vmess 默认为 auto, vless 默认为 none, shadowsocks也可以在这里指定加密方法,例如:"aes-256-gcm", 12 | "protocol":"vless", // 协议可以更换为 "vless","vmess","shadowsocks","trojan","trojan-go" 等 13 | "host":"伪装主机域名", // 例如ws协议通过这个指定HTTP头里的host字段。 14 | "sni":"TLS域名", // 不指定使用host的值,如果host也没指定就使用 address的值 15 | "tls":"xtls", // 可以不写,值可以指定为 "","tls","xtls" 等 16 | "flow":"流控", // xtls需要用到的字段,可以不指定使用默认值 "xtls-rprx-direct" 17 | "ps":"描述文本" 18 | } 19 | ] -------------------------------------------------------------------------------- /xray-core/xray.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chinaboy5216/winXray-4/c73f69c88498378e6b6919eb137269bbea6da7eb/xray-core/xray.exe --------------------------------------------------------------------------------