├── .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 ├── 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 └── 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="发布 v1.7.1,细节优化,支持直接使用 github.com 项目文件地址作为订阅源,兼容raw,blob格式,示例 https://github.com/winXray/winXray/blob/master/sub/sample.json "; 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 |
展开折叠并复制下面 vless/xtls 黑科技公益服务器分享链接,在winXray里点「批量导入链接」即可
vless://0d841558-eade-cfa9-f3ea-b6ab735a7867@us.winray.xyz:443/?flow=xtls-rprx-direct&host=us.winray.xyz&sni=us.winray.xyz&tls=xtls#%E7%BE%8E%E5%9B%BD%E8%A5%BF%E9%9B%85%E5%9B%BExtls%E9%BB%91%E7%A7%91%E6%8A%80
 2 | vless://1fd55a0e-67e1-a70e-acdc-86605b0b7f00@jp.winray.xyz:443/?flow=xtls-rprx-direct&host=jp.winray.xyz&sni=jp.winray.xyz&tls=xtls#%E6%97%A5%E6%9C%AC%E4%B8%9C%E4%BA%ACxtls%E9%BB%91%E7%A7%91%E6%8A%80
 3 | vmess://eyJhZGQiOiJ3d3cuYWVjaC5jYyIsImFpZCI6NjQsImhvc3QiOiIiLCJpZCI6ImVmM2M2YWEwLWU1ZjItNDMzMi1iYWQ4LTE3YzA0MzhkZmFhMyIsIm5ldCI6IndzIiwicGF0aCI6Ii9yYXkiLCJwb3J0Ijo0NDMsInBzIjoiXHU5OTk5XHU2RTJGNCIsInRscyI6InRscyIsInR5cGUiOiJub25lIiwidiI6Mn0=
 4 | vmess://eyJhZGQiOiIyMy4yMjUuMTI1Ljg2IiwiYWlkIjo2NCwiaG9zdCI6Ind3dy41NjYyNjE3My54eXoiLCJpZCI6IjhmYjkyMWMwLTU2NGQtNGQ1YS05YjEzLTM4NGMwMTgyMDEyNiIsIm5ldCI6IndzIiwicGF0aCI6Ii9mb290ZXJzIiwicG9ydCI6NDQzLCJwcyI6Ilx1N0Y4RVx1NTZGRDQiLCJ0bHMiOiJ0bHMiLCJ0eXBlIjoibm9uZSIsInYiOjJ9
 5 | vmess://eyJhZGQiOiIxMDQuMTYuMTYwLjIwIiwiYWlkIjoyLCJob3N0IjoiZnJlZXVzLm1jYW4udGVjaCIsImlkIjoiOWU2Y2VlZmYtMjU0Ni0zNjkwLWFjMDAtNmZjZGYzMWRlYzk0IiwibmV0Ijoid3MiLCJwYXRoIjoiL3kyODQiLCJwb3J0Ijo0NDMsInBzIjoiXHU3RjhFXHU1NkZENCIsInRscyI6InRscyIsInR5cGUiOiJub25lIiwidiI6Mn0=
 6 | vmess://eyJhZGQiOiJsbGFqdi5yZWJsZHAudGVjaCIsImFpZCI6NjQsImhvc3QiOiJsbGFqdi5yZWJsZHAudGVjaCIsImlkIjoiM2I1ZTI1OGUtOGM1ZS00NWQzLWI3ZDItMDJjOGY1ZmMwYmIyIiwibmV0Ijoid3MiLCJwYXRoIjoiLyIsInBvcnQiOjQ0MywicHMiOiJcdTVGQjdcdTU2RkQ0IiwidGxzIjoidGxzIiwidHlwZSI6Im5vbmUiLCJ2IjoyfQ==
 7 | vmess://eyJhZGQiOiIxNTQuODQuMS4xNDAiLCJhaWQiOjY0LCJob3N0IjoiMTU0Ljg0LjEuMTQwIiwiaWQiOiIyYTczNDlkNi05OTRhLTQzNGItOTU4OS1jMDAyMDY4NWU1MjgiLCJuZXQiOiJ0Y3AiLCJwYXRoIjoiLyIsInBvcnQiOjU0NjcxLCJwcyI6Ilx1ODM3N1x1NTE3MDQiLCJ0bHMiOiIiLCJ0eXBlIjoibm9uZSIsInYiOjJ9
 8 | vmess://eyJhZGQiOiJjb25zdW0yLmN1a2VlLmNjIiwiYWlkIjo2NCwiaG9zdCI6ImNvbnN1bTIuY3VrZWUuY2MiLCJpZCI6IjAyOTE0RjI1LTQxQzgtMzExNC01MEYzLTE5MzU5QjYzMUM3NyIsIm5ldCI6IndzIiwicGF0aCI6Ii8iLCJwb3J0Ijo0NDMsInBzIjoiXHU2Q0Q1XHU1NkZENCIsInRscyI6InRscyIsInR5cGUiOiJub25lIiwidiI6Mn0=
9 | 10 | 也可以直接导入 [公益服务器订阅链接](./sub/sample.json) 11 | 12 | # winXray 13 | 本软件源码已贡献到公共领域并放弃版权,源码可使用 [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)。 14 | 15 | 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) 服务器工具。 16 | 17 | 之前我用过很多代理客户端,经常用一会就挂掉了,有些测试很久才找到下一个可用的服务器,有时怎么切换都不行,一定要把整个软件退出重启才能恢复。而且在WIN10上都有相同的BUG:PAC代理用一段时间就会卡死( winXray已经通过自行实现PAC服务器解决了这个问题 ),其实这些软件里提供的很多功能我并不需要,我只想愉快地用下 google 找点技术资料提升工作效率。但是在网上找了很久都没找到适合的软件,于是决定自己动手写一个,还好用 aardio 写软件的速度很快 - 大概用了几个小时就完成了 winXray 的主要代码,改进了几个版本以后就很稳定了,**我自己用了 winXray 几个月再也没有遇到 google 抽风访问不了的问题**。 18 | 19 | 一些网友对 winXray 的测速功能存在误解,简单的测速不但不必要的消耗流量而且没有什么实用价值,例如Ping通了TCP访问不了,TCP通了但浏览器被阻断,或者下载测速需要连接代理服务器以后长时间测试才能得到下载速度 - 如果有很多服务器需要频繁快速地测速这样显然是不行的。**根据我长时间的实测,我使用一堆的服务器,大多时候 winXray 都能最快的找到其中速度最快、且能正常使用的线路**。要考虑到网络速度与服务器响应速度本来就是波动的,**winXray 能在连接代理服务器以前在零点几秒以内快速地在上百个代理服务器中找出最快的服务器** - 可以节省我们大量手动测试切换的时间,如果你有一个比我们幸福的网络环境,有一堆稳定流畅看4K视频的代理服务器 - 那你确实可以关闭自动切换功能 - 手动切换即可。 20 | 21 | ![winXray](./screenshots/winXray.png) 22 | 23 | winXray支持批量导入 vless、vmess、ss、trojan、trojan-go …… 等格式的分享链接, 24 | 也可以导入通用订阅链接,以及 base 64、json …… 等不同格式的服务器配置。winXray 有强大的兼容性,我们甚至可以直接自 github 项目地址导入代理服务器,例如直接复制链接 https://github.com/winXray/winXray/blob/master/sub/sample.json 即可一键导入 winXray。 25 | 26 | ![服务器配置](./screenshots/config.json.png) 27 | **小技巧: JSON里点击任意字段都会显示该字段的用法说明。** 个人认为做很多对话框来配置服务器的参数是非常蠢的,winXray已经把各种代理协议的配置简化为几个统一命名的JSON字段( **也可以作为一种标准的、统一的、通用的订阅响应格式使用** ),只要稍加学习就可以非常熟练的添加、修改各种代理协议的配置。而且对于大多数用户根本不需要改配置 - 简单的复制导入分享链接就可以,我们不必要把把简单的事搞复杂。 28 | 29 | 可选在 ["/xray-core/winXray-default-servers.json"](./xray-core/winXray-default-servers.json) 文件中添加默认服务器配置(生成EXE后默认配置自动嵌入到EXE文件,可选删除该文件,也可以继续使用该文件覆盖EXE自带的默认服务器列表)。 30 | 31 | ![PAC配置](./screenshots/pac.png) 32 | **小技巧: PAC编辑器里点击任意域名都会自动关联到单选框,可以直接切换代理或直连。** 33 | 34 | 软件首次运行时会在当前目录查找 "./xray-core/xray.exe" 35 | 发行文件仅需要 "./winXray.exe",可选带上 "./xray-core/" 目录( 如果没有找到会自动到v2ray官网下载,不过没有代理服务器下载有时候非常慢 )。 36 | 37 | ![端口配置](./screenshots/config.advanced.png) 38 | 39 | Telegram 端口配置 40 | -------------------------------------------------------------------------------- /app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/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=638;bgcolor=16777215) 5 | winform.add( 6 | btnOpenFirewall={cls="plus";text="设置防火墙";left=511;top=448;right=641;bottom=478;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=569;right=619;bottom=605;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=487;right=818;bottom=518;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=447;right=509;bottom=478;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=527;right=818;bottom=558;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=198;right=384;bottom=222;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=393;right=725;bottom=417;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=292;right=384;bottom=316;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=344;right=723;bottom=368;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=152;right=384;bottom=176;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 | lbTestInterval={cls="static";text="15秒";left=590;top=101;right=788;bottom=127;bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=20}; 17 | lnkVersion={cls="plus";text="正在检查新版本";left=19;top=607;right=380;bottom=638;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}; 18 | radioHttpGlobal={cls="plus";text="全局/PAC 默认使用HTTP代理";left=246;top=243;right=491;bottom=274;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}; 19 | radioSocksGlobal={cls="plus";text="全局/PAC 默认使用SOCKS代理";left=491;top=243;right=750;bottom=274;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}; 20 | static={cls="static";text="SOCKS 代理服务端口:";left=94;top=158;right=292;bottom=184;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=4}; 21 | static2={cls="static";text="PAC 端口:";left=162;top=297;right=292;bottom=323;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=6}; 22 | static3={cls="static";text="本机 PAC 地址:";left=135;top=349;right=293;bottom=375;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=9}; 23 | static4={cls="static";text="局域网 PAC 地址:";left=137;top=398;right=295;bottom=424;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=12}; 24 | static5={cls="static";text="HTTP/HTTPS 代理服务端口:";left=34;top=204;right=292;bottom=230;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=15}; 25 | static6={cls="static";text="支持 SOCKS4、SOCKS4A、SOCKS5";left=392;top=158;right=679;bottom=184;bgcolor=16777215;color=5921370;db=1;dl=1;font=LOGFONT(h=-13);transparent=1;z=16}; 26 | static7={cls="static";text="检测服务器异常间隔时间:";left=47;top=101;right=292;bottom=127;align="right";bgcolor=16777215;db=1;dl=1;font=LOGFONT(h=-16);transparent=1;z=19}; 27 | tbTestInterval={cls="plus";left=304;top=105;right=585;bottom=117;bgcolor=-2512093;border={radius=-1};color=23807;db=1;dl=1;foreRight=13;forecolor=-14911489;notify=1;paddingBottom=5;paddingTop=5;z=18} 28 | ) 29 | /*}}*/ 30 | 31 | import style; 32 | winform.chkLan.skin(style.checkBox); 33 | winform.chkEnableGitConfigGithub.skin(style.checkBox); 34 | winform.chkSystemStart.skin(style.checkBox); 35 | winform.radioHttpGlobal.skin(style.radio) 36 | winform.radioSocksGlobal.skin(style.radio) 37 | winform.btnUpdate.skin(style.primaryButton); 38 | winform.editSocksPort.skin(style.edit); 39 | winform.editPacPort.skin(style.edit); 40 | winform.editPacUrl.editBox.readonly = true; 41 | winform.lnkVersion.skin(style.link) 42 | 43 | import win.ui.tooltip; 44 | var tooltipCtrl = win.ui.tooltip( winform ); 45 | tooltipCtrl.addTool(winform.lnkVersion,"点击检测更新" ); 46 | tooltipCtrl.addTool(winform.editSocksPort,"点击右键可以复制IP:端口" ); 47 | tooltipCtrl.addTool(winform.radioHttpGlobal,"点击右键可以复制IP:端口" ); 48 | tooltipCtrl.addTool(winform.radioHttpGlobal,"如果某些程序不支持SOCKS全局代理,可以切换到此选项。" ); 49 | tooltipCtrl.addTool(winform.radioSocksGlobal,"如果某些程序不支持SOCKS全局代理,可以切换到HTTP全局代理。" ); 50 | 51 | import xray.pacServer; 52 | onPacUpdated = function(){ 53 | winform.editPacPort.text = xray.pacServer.getPort() || config.proxy.pacPort || 1083; 54 | winform.editPacUrl.text = xray.pacServer.getUrl(); 55 | winform.editLanPacUrl.text = xray.pacServer.getUrl(true); 56 | winform.editLanPacUrl.disabled = !winform.chkLan.checked; 57 | } 58 | 59 | subscribe("pacServer.restarted",function(pacPort){ 60 | onPacUpdated() 61 | }) 62 | 63 | subscribe("sysProxy.modeChanged",function(mode){ 64 | if(mode==="mode"){ 65 | winform.editPacUrl.text = xray.pacServer.getUrl(); 66 | winform.editLanPacUrl.text = xray.pacServer.getUrl(true); 67 | } 68 | }) 69 | 70 | import xray.core; 71 | var onInboundsUpdated = function(){ 72 | var inbounds = ..config.core.default[["inbounds"]]; 73 | winform.editSocksPort.text = xray.core.socksProxyPort || inbounds[[1]][["port"]] || 1081; 74 | winform.editHttpPort.text = xray.core.httpProxyPort || inbounds[[2]][["port"]] || 1082; 75 | winform.chkLan.checked = inbounds[[1]][["listen"]] !== "127.0.0.1"; 76 | winform.btnOpenFirewall.disabled = !winform.chkLan.checked; 77 | } 78 | 79 | subscribe("config.core.changed",function(){ 80 | onInboundsUpdated(); 81 | }) 82 | 83 | subscribe("xrayCore.restarted",function(socksProxyPort,httpProxPort){ 84 | onInboundsUpdated(); 85 | }) 86 | 87 | import win.reg; 88 | var reg = win.reg("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run\"); 89 | winform.chkSystemStart.checked = reg.queryValue("winXray"); 90 | reg.close(); 91 | 92 | winform.chkEnableGitConfigGithub.checked = config.proxy.enableGitConfigGithub; 93 | winform.radioHttpGlobal.checked = config.proxy.useHttpGlobal; 94 | winform.radioSocksGlobal.checked = !config.proxy.useHttpGlobal; 95 | onInboundsUpdated(); 96 | onPacUpdated(); 97 | 98 | import wsock.tcp.client; 99 | winform.btnUpdate.oncommand = function(id,event){ 100 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}; 101 | config.proxy.useHttpGlobal = winform.radioHttpGlobal.checked; 102 | xray.pacServer.updateUseHttpProxy(config.proxy.useHttpGlobal); 103 | 104 | var client = wsock.tcp.client(); 105 | var oldPort = xray.core.socksProxyPort; 106 | if( tonumber(winform.editSocksPort.text) 107 | && tonumber(winform.editSocksPort.text) != oldPort 108 | && client.connectTimeout("127.0.0.1",tonumber(winform.editSocksPort.text) ,0.5) ){ 109 | winform.editSocksPort.editBox.showErrorTip(,"端口已被占用"); 110 | winform.btnUpdate.disabledText = null; 111 | return client.close(); 112 | } 113 | client.close(); 114 | 115 | var client = wsock.tcp.client(); 116 | var oldPacPort = xray.pacServer.getPort(); 117 | if( tonumber(winform.editPacPort.text) 118 | && tonumber(winform.editPacPort.text) != oldPacPort 119 | && client.connectTimeout("127.0.0.1",tonumber(winform.editPacPort.text) ,0.5) ){ 120 | winform.editPacPort.editBox.showErrorTip(,"PAC端口已被占用"); 121 | winform.btnUpdate.disabledText = null; 122 | return client.close(); 123 | } 124 | client.close(); 125 | 126 | 127 | if(winform.chkEnableGitConfigGithub.checked!=config.proxy.enableGitConfigGithub){ 128 | config.proxy.enableGitConfigGithub = winform.chkEnableGitConfigGithub.checked; 129 | 130 | import xray.github; 131 | if(!config.proxy.enableGitConfigGithub){ 132 | xray.github.setProxy(false); 133 | } 134 | else { 135 | xray.github.setProxy(null); 136 | } 137 | } 138 | 139 | var restartCore = false; 140 | var port = tonumber(winform.editSocksPort.text) 141 | config.core.default.inbounds[1]["port"] = port; 142 | if(port!=oldPort){ 143 | restartCore = true; 144 | } 145 | 146 | var port = tonumber(winform.editHttpPort.text) 147 | config.core.default.inbounds[2]["port"] = port; 148 | if(port!=oldPort){ 149 | restartCore = true; 150 | } 151 | 152 | var listen = winform.chkLan.checked ? "0.0.0.0" : "127.0.0.1"; 153 | if(listen!=config.core.default.inbounds[1]["listen"]){ 154 | config.core.default.inbounds[1]["listen"]= listen; 155 | restartCore = true; 156 | } 157 | 158 | if(listen!=config.core.default.inbounds[2]["listen"]){ 159 | config.core.default.inbounds[2]["listen"]= listen; 160 | restartCore = true; 161 | } 162 | 163 | config.core.save(); 164 | publish("config.inbounds.changed"); 165 | if(restartCore) { 166 | publish("uiCommand.restartXrayCore"); 167 | } 168 | 169 | config.proxy.pacPort = tonumber(winform.editPacPort.text) 170 | if(oldPacPort!=tonumber(winform.editPacPort.text)){ 171 | xray.pacServer.restart(); 172 | } 173 | 174 | config.proxy.save(); 175 | sysProxy.reset(true); 176 | 177 | import win.reg; 178 | var reg = win.reg("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run\"); 179 | if(!_STUDIO_INVOKED){ 180 | if(winform.chkSystemStart.checked){ 181 | reg.setSzValue("winXray",`"`+io._exepath+`" /tray`); 182 | } 183 | else { 184 | reg.delValue("winXray"); 185 | } 186 | } 187 | reg.close(); 188 | 189 | winform.btnUpdate.disabledText = null; 190 | winform.msgOk("配置已更新",1200); 191 | } 192 | 193 | winform.editPacUrl.editBox.readonly = true; 194 | winform.editLanPacUrl.editBox.readonly = true; 195 | 196 | import win.clip; 197 | import wsock.tcp.client; 198 | winform.editSocksPort.editBox.enablePopMenu(function(){ 199 | var lanIp = wsock.tcp.client.getLocalIp("www.baidu.com"); 200 | return { 201 | { "复制端口" + winform.editSocksPort.text; 202 | function(id){ 203 | win.clip.write("" + winform.editSocksPort.text) 204 | } 205 | }; 206 | { "复制本机IP与端口 127.0.0.1:" + winform.editSocksPort.text; 207 | function(id){ 208 | win.clip.write("127.0.0.1:" + winform.editSocksPort.text) 209 | } 210 | }; 211 | { "复制局域网IP与端口 "+lanIp+":" + winform.editSocksPort.text; 212 | function(id){ 213 | win.clip.write(lanIp+":" + winform.editSocksPort.text) 214 | } 215 | }; 216 | } 217 | } ) 218 | 219 | winform.editHttpPort.editBox.enablePopMenu(function(){ 220 | var lanIp = wsock.tcp.client.getLocalIp("www.baidu.com"); 221 | return { 222 | { "复制端口" + winform.editHttpPort.text; 223 | function(id){ 224 | win.clip.write("" + winform.editHttpPort.text) 225 | } 226 | }; 227 | { "复制本机IP与端口 127.0.0.1:" + winform.editHttpPort.text; 228 | function(id){ 229 | win.clip.write("127.0.0.1:" + winform.editHttpPort.text) 230 | } 231 | }; 232 | { "复制局域网IP与端口 "+lanIp+":" + winform.editHttpPort.text; 233 | function(id){ 234 | win.clip.write(lanIp+":" + winform.editHttpPort.text) 235 | } 236 | }; 237 | } 238 | } ) 239 | 240 | winform.editPacUrl.setCueBannerText("当前未启用本机PAC代理"); 241 | winform.editLanPacUrl.setCueBannerText("当前未启用局域网PAC代理"); 242 | 243 | winform.chkLan.oncommand = function(id,event){ 244 | winform.editLanPacUrl.disabled = !winform.chkLan.checked; 245 | winform.btnOpenFirewall.disabled = !winform.chkLan.checked; 246 | } 247 | 248 | winform.tbTestInterval.setTrackbarRange(1,30); 249 | winform.tbTestInterval.skin({ 250 | background={ 251 | default=0xFF23ABD9 252 | }; 253 | foreground={ 254 | default=0xFFFF771C; 255 | hover=0xFFFF6600 256 | }; 257 | color={ 258 | default=0xFFFF5C00; 259 | hover=0xFFFF6600 260 | } 261 | }) 262 | winform.tbTestInterval.progressPos = config.proxy.testInterval; 263 | winform.lbTestInterval.text = config.proxy.testInterval+"秒"; 264 | winform.tbTestInterval.onPosChanged = function( pos,thumbTrack ){ 265 | winform.lbTestInterval.text = pos+"秒"; 266 | config.proxy.testInterval = pos; 267 | config.proxy.save(); 268 | } 269 | 270 | import fsys.update.simpleMain; 271 | fsys.update.simpleMain.onStatusChanged(function(version,description,status){ 272 | /*注意此回调可捕获到调用前或调用后的更新状态变更*/ 273 | if(status=="ready"){ 274 | winform.lnkVersion.disabledText = null; 275 | winform.lnkVersion.text = "点这里重新启动软件更新到新版本:" + version 276 | tooltipCtrl.addTool(winform.lnkVersion,description); 277 | } 278 | elseif(status=="complete"){ 279 | winform.lnkVersion.text = "已更新到最新版本:" + version; 280 | tooltipCtrl.addTool(winform.lnkVersion,description); 281 | ..publish("uiCommand.print",winform.lnkVersion.text ); 282 | } 283 | elseif(status=="latest"){ 284 | winform.lnkVersion.disabledText = null; 285 | winform.lnkVersion.text = "已经是最新版本"; 286 | 287 | } 288 | elseif(status=="failed"){ 289 | winform.lnkVersion.disabledText = null; 290 | ..publish("uiCommand.print",description); 291 | ..publish("uiCommand.print","建议在「工具」中运行「github优化工具」修复无法访问 raw.githubusercontent.com 的问题。"); 292 | } 293 | }) 294 | 295 | winform.lnkVersion.oncommand = function(id,event){ 296 | 297 | winform.lnkVersion.text = fsys.update.simpleMain.getReadyStatusInfo() ? "正在启动自动更新" : "正在检查新版本"; 298 | winform.lnkVersion.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 299 | if(fsys.update.simpleMain.checkUpdate(true)){ 300 | win.quitMessage(); 301 | } 302 | } 303 | 304 | winform.btnOpenFirewall.skin(style.plainButton) 305 | winform.btnOpenFirewall.oncommand = function(id,event){ 306 | import process.control; 307 | process.control("firewall.cpl") 308 | } 309 | 310 | winform.show(); 311 | win.loopMessage(); 312 | 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 cfg,err = ..table.array(); 179 | if(#json){ 180 | cfg,err= web.json.tryParse(strEditBounds); 181 | if(!cfg){ 182 | winform.editOutbounds.showErrorTip(,string.concat("当前配置存在JSON语法错误 ",err)); 183 | return; 184 | } 185 | } 186 | 187 | ..table.append(cfg,outbounds); 188 | winform.editOutbounds.text = ..web.json.stringifyArray(cfg,true,false,true); 189 | } 190 | else { 191 | var str = xray.outbounds.exportSharedLinks(outbounds); 192 | if(#strEditBounds){ 193 | winform.editOutbounds.text = strEditBounds+ '\r\n' + str; 194 | } 195 | else { 196 | winform.editOutbounds.text = str; 197 | } 198 | } 199 | 200 | winform.msgOk("已成功导入" + #outbounds + "个服务器,请点击「更新设置」启用新配置。",1200); 201 | winform.editOutbounds.modified = true; 202 | winform.editOutbounds.setFocus(); 203 | return; 204 | } 205 | 206 | winform.msgFrown('未导入服务器! \r\n请先复制一个或多个以下格式服务器分享链接:\r\n\r\nvmess:// 链接\r\nvless:// 链接\r\nss:// 链接\r\ntrojan:// 链接\r\ntrojan-go:// 链接\r\nhttp:// 或 https:// 通用订阅源!\r\n\r\n订阅源可直接返回winXray格式的配置JSON。\n订阅源地址可以直接指定github项目网址(兼容raw,blob格式)。') 207 | } 208 | }; 209 | } 210 | 211 | if( (!#strEditBounds) || ..string.match(strEditBounds,"%\[\]")){ 212 | menu[3] = { "更新订阅源"; 213 | function(id){ 214 | var cfg,err = ..table.array(); 215 | 216 | var json = strEditBounds; 217 | if(#json){ 218 | cfg,err = web.json.tryParse(strEditBounds); 219 | if(!cfg){ 220 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 221 | return; 222 | } 223 | } 224 | 225 | var subscribeUrls = {} 226 | for(i=#cfg;1;-1){ 227 | var outbound = cfg[i] 228 | if(outbound.subscribeUrl){ 229 | subscribeUrls[outbound.subscribeUrl] = true; 230 | } 231 | } 232 | 233 | if(!table.count(subscribeUrls)){ 234 | import win.clip; 235 | var clibStr = win.clip.read(); 236 | if( ..string.startWith(clibStr,"http://") || ..string.startWith(clibStr,"https://") ){ 237 | subscribeUrls[clibStr] = true; 238 | } 239 | else { 240 | winform.msgWarn('当前配置不包含来自订阅源的服务器,\n请先复制订阅源网址到剪贴板!'); 241 | return; 242 | } 243 | } 244 | 245 | var count = 0; 246 | for(url,v in subscribeUrls){ 247 | var outbounds = xray.outbounds.importFromString(url); 248 | if(#outbounds){ 249 | for(i=#cfg;1;-1){ 250 | var outbound = cfg[i] 251 | if(outbound.subscribeUrl==url){ 252 | ..table.remove( cfg,i ); 253 | } 254 | } 255 | 256 | count = count + #outbounds; 257 | ..table.append(cfg,outbounds); 258 | } 259 | } 260 | 261 | winform.editOutbounds.text = ..web.json.stringifyArray(cfg,true,false,true); 262 | 263 | winform.msgOk("已成功刷新" + count + "个服务器,请点击「更新设置」启用新配置。",1200); 264 | winform.editOutbounds.modified = true; 265 | winform.editOutbounds.setFocus(); 266 | return; 267 | } 268 | }; 269 | } 270 | 271 | if(..string.match(strEditBounds,"%\[\]")){ 272 | menu[4] = {} 273 | menu[5] = { "转换为 vmess、vless、trojan、trojan-go、ss 分享链接"; 274 | function(id){ 275 | var cfg,err = web.json.tryParse(strEditBounds); 276 | if(!cfg){ 277 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 278 | return; 279 | } 280 | 281 | var str = xray.outbounds.exportSharedLinks(cfg); 282 | if(str){ 283 | winform.editOutbounds.text = str; 284 | winform.editOutbounds.modified = true; 285 | } 286 | } 287 | }; 288 | menu[6] = { "转换为 Base64 订阅源"; 289 | function(id){ 290 | var cfg,err = web.json.tryParse(strEditBounds); 291 | if(!cfg){ 292 | winform.editOutbounds.showErrorTip(,string.concat("JSON语法错误 ",err)); 293 | return; 294 | } 295 | 296 | var str = xray.outbounds.exportSharedLinks(cfg); 297 | if(str){ 298 | import crypt; 299 | winform.editOutbounds.text = ..crypt.encodeBin(str); 300 | winform.editOutbounds.modified = true; 301 | } 302 | } 303 | }; 304 | } 305 | elseif( #strEditBounds ) { 306 | menu[3] = {} 307 | 308 | menu[4] = { "转换为 JSON"; 309 | function(id){ 310 | import web.json; 311 | var outbounds = xray.outbounds.importFromString(strEditBounds); 312 | if(outbounds){ 313 | winform.editOutbounds.text = web.json.stringifyArray(outbounds,true,false,true); 314 | winform.editOutbounds.modified = true; 315 | } 316 | } 317 | }; 318 | } 319 | 320 | table.push(menu,{ /*---分隔线---*/ }); 321 | 322 | table.push(menu,{ "打开配置文件(JSON)"; 323 | function(id){ 324 | import fsys.dlg; 325 | var path = fsys.dlg.open("*.json|*.json||","打开 json 文件",,winform) 326 | if(path){ 327 | winform.editOutbounds.text = string.load(path); 328 | winform.editOutbounds.modified = true; 329 | } 330 | } 331 | }); 332 | 333 | table.push(menu,{ "另存为配置文件(JSON)"; 334 | function(id){ 335 | import fsys.dlg; 336 | var path = fsys.dlg.save("*.json|*.json||","另存为 json 文件",,winform) 337 | if(path){ 338 | var str = web.json.stringifyArray( 339 | config.proxy.outbounds,true,false,true 340 | ) 341 | 342 | string.save(path,str ) 343 | } 344 | } 345 | }); 346 | 347 | table.push(menu,{ "另存为默认服务器列表(JSON)"; 348 | function(id){ 349 | var str = web.json.stringifyArray( 350 | config.proxy.outbounds,true,false,true 351 | ) 352 | 353 | string.save("/xray-core/winXray-default-servers.json",str ); 354 | winform.msgOk("已存为默认服务器列表 /xray-core/winXray-default-servers.json",1000) 355 | } 356 | }); 357 | 358 | table.push(menu,{ /*---分隔线---*/ }); 359 | if(winform.editOutbounds.modified){ 360 | table.push(menu,{ "恢复为当前使用的服务器列表(JSON)"; 361 | function(id){ 362 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 363 | winform.editOutbounds.text = web.json.stringifyArray( 364 | config.proxy.outbounds,true,false,true 365 | ) 366 | winform.editOutbounds.modified = false; 367 | 368 | win.delay(500); 369 | winform.btnUpdate.disabledText = null; 370 | } 371 | }); 372 | } 373 | 374 | table.push(menu,{ "重置为默认服务器列表(JSON)"; 375 | function(id){ 376 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 377 | ..config.__loadDefaultOutbounds(); 378 | winform.editOutbounds.modified = false; 379 | publish("uiCommand.restartXrayCore",cfg); 380 | 381 | winform.btnUpdate.disabledText = null; 382 | } 383 | }); 384 | 385 | table.push(menu,{ "清空服务器列表"; 386 | function(id){ 387 | winform.editOutbounds.text = "[]"; 388 | winform.editOutbounds.modified = true; 389 | } 390 | }); 391 | 392 | if( ..string.match(winform.editOutbounds.text,"%\[\]")){ 393 | 394 | table.push(menu,{ /*---分隔线---*/ }); 395 | table.push(menu,{ "插入配置字段";{ 396 | { "代理服务协议:protocol"; 397 | function(id){ 398 | winform.editOutbounds.selText = '"protocol":"vless",' 399 | } 400 | }; 401 | { "SSH管理端口:sshPort"; 402 | function(id){ 403 | winform.editOutbounds.selText = '"sshPort":22,' 404 | } 405 | }; 406 | { "代理服务器地址:address"; 407 | function(id){ 408 | winform.editOutbounds.selText = '"address":"",' 409 | } 410 | }; 411 | { "端口:port"; 412 | function(id){ 413 | winform.editOutbounds.selText = '"port":443,' 414 | } 415 | }; 416 | { "网络协议:network"; 417 | function(id){ 418 | winform.editOutbounds.selText = '"network":"tcp",' 419 | } 420 | }; 421 | { "服务器密码:id"; 422 | function(id){ 423 | winform.editOutbounds.selText = '"id":"",' 424 | } 425 | }; 426 | { "加密方式:security"; 427 | function(id){ 428 | winform.editOutbounds.selText = '"security":"auto",' 429 | } 430 | }; 431 | { "启用TLS:tls"; 432 | function(id){ 433 | winform.editOutbounds.selText = '"tls":"xtls",' 434 | } 435 | }; 436 | { "允许忽略证书:allowInsecure"; 437 | function(id){ 438 | winform.editOutbounds.selText = '"allowInsecure":true,' 439 | } 440 | }; 441 | { "TLS服务器名:sni"; 442 | function(id){ 443 | winform.editOutbounds.selText = '"sni":"",' 444 | } 445 | }; 446 | { "启用TLS:tls"; 447 | function(id){ 448 | winform.editOutbounds.selText = '"tls":"xtls",' 449 | } 450 | }; 451 | { "流控:flow"; 452 | function(id){ 453 | winform.editOutbounds.selText = '"flow":"xtls-rprx-direct",' 454 | } 455 | }; 456 | { "多路复用最大连接:concurrency"; 457 | function(id){ 458 | winform.editOutbounds.selText = '"concurrency":4,' 459 | } 460 | }; 461 | 462 | { "伪装类型:type"; 463 | function(id){ 464 | winform.editOutbounds.selText = '"type":"http",' 465 | } 466 | }; 467 | { "备注:ps"; 468 | function(id){ 469 | winform.editOutbounds.selText = '"ps":"",' 470 | } 471 | }; 472 | { "请求主机名:host"; 473 | function(id){ 474 | winform.editOutbounds.selText = '"host":"",' 475 | } 476 | }; 477 | { "请求路径:path"; 478 | function(id){ 479 | winform.editOutbounds.selText = '"path":"/",' 480 | } 481 | }; 482 | { "绑定的订阅网址:subscribeUrl"; 483 | function(id){ 484 | winform.editOutbounds.selText = '"subscribeUrl":"https://",' 485 | } 486 | }; 487 | { "允许自动测试并连接:autoConnect"; 488 | function(id){ 489 | winform.editOutbounds.selText = '"autoConnect":false,' 490 | } 491 | }; 492 | { "备注:ps"; 493 | function(id){ 494 | winform.editOutbounds.selText = '"ps":"",' 495 | } 496 | }; 497 | }}); 498 | } 499 | 500 | return menu; 501 | } 502 | }) 503 | 504 | winform.editCoreConfig.enablePopMenu({ 505 | { /*---分隔线---*/ }; 506 | { "重置为默认 Xray 配置"; 507 | function(id){ 508 | config.__resetDefaultCore(); 509 | winform.editCoreConfig.text = web.json.stringify( 510 | config.core.default,true 511 | ) 512 | } 513 | }; 514 | } ) 515 | 516 | subscribe("outbounds.updateConfigJson",function(){ 517 | if(winform.editOutbounds.modified){ 518 | return; 519 | } 520 | 521 | winform.editOutbounds.text = web.json.stringifyArray( 522 | config.proxy.outbounds,true,false,true 523 | ) 524 | 525 | winform.editOutbounds.modified = false; 526 | } ) 527 | 528 | winform.editOutbounds.text = web.json.stringifyArray( 529 | config.proxy.outbounds,true,false,true 530 | ) : "[]" 531 | winform.editOutbounds.modified = false; 532 | 533 | subscribe("config.inbounds.changed",function(){ 534 | if(winform.editCoreConfig.modified){ 535 | return; 536 | } 537 | 538 | winform.editCoreConfig.text = web.json.stringify( 539 | config.core.default,true 540 | ) 541 | 542 | winform.editCoreConfig.modified = false; 543 | } ) 544 | winform.editCoreConfig.text = web.json.stringify( 545 | config.core.default,true 546 | ) 547 | 548 | var editOutboundsTip = { 549 | address = {"代理服务器地址";"值可以是域名或者IP地址"}; 550 | alterId = {"额外ID";"仅用于VMESS协议,与服务器一致即可"}; 551 | aid = {"额外ID(alterId)";"数值,仅用于VMESS协议,与服务器一致即可"}; 552 | network = {"传输协议(network)";"可选值为tcp,ws等"}; 553 | net = {"传输协议(network)";"可选值为tcp,ws等"}; 554 | type = {"伪装类型";"可选值为none,http等"}; 555 | ps = {"备注";"备注"}; 556 | tls = {"是否启用TLS";"可选值为tls,xtls或空值,vless协议省略时默认值为tls"}; 557 | path = {"请求路径";"用于ws,http等协议指定请求路径"}; 558 | id = {"密码";"服务器密码"}; 559 | host = {"主机名";"用于指定http,ws等协议请求头中的主机名"}; 560 | protocol = {"协议";"可选值为 vmess,vless,shadowsocks,trojan,trojan-go 之一"}; 561 | port = {"端口";"服务器端口,数值"}; 562 | security = {"加密方式";"可省略,vmess协议默认为auto,vless协议默认为none"}; 563 | sni = {"TLS服务器名";"TLS客户端请求中的服务器名字段,如果不指定则默认取host或address字段值。"}; 564 | flow = {"流控";"目前仅用于选择 XTLS 的算法,省略时默认值为xtls-rprx-direct"}; 565 | concurrency = {"Mux多路复用:最大并发连接数";"最小值1,最大值1024,特殊值-1,不加载mux模块。缺省由winXray根据连接协议自动设定此值。"}; 566 | } 567 | 568 | winform.editOutbounds.wndproc = function(hwnd,message,wParam,lParam){ 569 | if(message == 0x202/*_WM_LBUTTONUP*/){ 570 | var lineIndex = winform.editOutbounds.lineFromChar(); 571 | if(lineIndex == winform.editOutbounds.preLineIndex){ 572 | return; 573 | } 574 | winform.editOutbounds.preLineIndex = lineIndex; 575 | 576 | var line = winform.editOutbounds.lineText(); 577 | var n,v = string.match(line,`^\s*\"(\w+)\"\s*\:\s*(%"")[\,\s]*$`); 578 | if( !n ) n,v = string.match(line,`^\s*\"(\w+)\"\s*\:\s*(\d+)[\,\s]*$`); 579 | 580 | if(editOutboundsTip[n]){ 581 | winform.editOutbounds.showInfoTip(editOutboundsTip[n][1],editOutboundsTip[n][2]) 582 | } 583 | } 584 | } 585 | 586 | winform.editOutbounds.limit = 0; 587 | winform.editCoreConfig.limit = 0; 588 | 589 | winform.enableDpiScaling(); 590 | winform.show(); 591 | win.loopMessage(); 592 | return winform; 593 | -------------------------------------------------------------------------------- /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=578;top=654;right=681;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 | var reloadSubscriptions = function(){ 34 | winform.chkAutoRefreshSubscription.checked = config.proxy.autoRefreshSubscription; 35 | config.proxy.subscribeUrls.fields = {"";"checked";"ps";"url";} 36 | var outbounds = ..config.proxy.outbounds : ..table.array(); 37 | 38 | var outbondSubscribeUrlMap = {}; 39 | for(i=#outbounds;1;-1){ 40 | var outbound = outbounds[i] 41 | if(outbound.subscribeUrl){ 42 | outbondSubscribeUrlMap[outbound.subscribeUrl] = true; 43 | } 44 | } 45 | 46 | var cfgSubscribeUrls = config.proxy.subscribeUrls; 47 | for(i=#cfgSubscribeUrls;1;-1){ 48 | var sub = cfgSubscribeUrls[i] 49 | if(outbondSubscribeUrlMap[sub.url]){ 50 | 51 | outbondSubscribeUrlMap[sub.url] = null; 52 | } 53 | } 54 | 55 | var count = 0; 56 | for(url,v in outbondSubscribeUrlMap){ 57 | ..table.push(cfgSubscribeUrls,{url=url;ps="";checked="启用"}) 58 | } 59 | 60 | var chkAll = true; 61 | var checked = {} 62 | for(i=#cfgSubscribeUrls;1;-1){ 63 | var sub = cfgSubscribeUrls[i] 64 | if(sub.checked=="启用"){ 65 | table.push(checked,i) 66 | } 67 | else { 68 | chkAll = false; 69 | } 70 | } 71 | winform.chkAll.checked = chkAll; 72 | 73 | grid.setTable( cfgSubscribeUrls ); 74 | winform.listview.checked = checked; 75 | } 76 | 77 | winform.loadSubscriptionsOnce = function(){ 78 | reloadSubscriptions(); 79 | winform.loadSubscriptionsOnce = function(){}; 80 | } 81 | 82 | subscribe("uiCommand.subscriptionNew",function(url){ 83 | var cfgSubscribeUrls = config.proxy.subscribeUrls; 84 | for(i=#cfgSubscribeUrls;1;-1){ 85 | var sub = cfgSubscribeUrls[i] 86 | if(sub.url==url){ 87 | winform.listview.setChecked(i,true); 88 | return; 89 | } 90 | } 91 | 92 | ..table.push(cfgSubscribeUrls,{url=url;ps="";checked="启用"}) 93 | var item = winform.listview.addItem({"","启用","",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(sub){ 103 | sub[name] = text; 104 | config.proxy.save(); 105 | } 106 | else { 107 | winform.msgErr("错误的订阅索引") 108 | } 109 | } 110 | 111 | winform.popmenu = win.ui.popmenu(winform);//创建弹出菜单 112 | winform.popmenu.add('删除',function(id){ 113 | winform.btnRemove.oncommand(); 114 | }); 115 | 116 | winform.btnUpdate.skin(style.primaryButton); 117 | winform.btnUpdate.oncommand = function(id,event){ 118 | 119 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 120 | config.proxy.autoRefreshSubscription = winform.chkAutoRefreshSubscription.checked; 121 | 122 | var outbondSubscribeUrlMap = {}; 123 | var cfgSubscribeUrls = config.proxy.subscribeUrls; 124 | for(i=#cfgSubscribeUrls;1;-1){ 125 | var sub = cfgSubscribeUrls[i] 126 | outbondSubscribeUrlMap[sub.url] = sub.checked=="启用"; 127 | } 128 | 129 | var removed = 0; 130 | var outbounds = ..config.proxy.outbounds : ..table.array(); 131 | for(i=#outbounds;1;-1){ 132 | var outbound = outbounds[i] 133 | if(outbound.subscribeUrl){ 134 | if(!outbondSubscribeUrlMap[outbound.subscribeUrl]){ 135 | table.remove(outbounds,i); 136 | removed++; 137 | } 138 | } 139 | } 140 | if(removed) config.proxy.save(); 141 | 142 | import xray.outbounds; 143 | if(!xray.outbounds.updateSubscription(true)){ 144 | if(removed) publish("uiCommand.restartXrayCore",outbounds) 145 | } 146 | 147 | winform.btnUpdate.disabledText = null; 148 | } 149 | 150 | winform.chkAll.oncommand = function(id,event){ 151 | var checked = winform.chkAll.checked 152 | for hItem in winform.listview.each(){ 153 | winform.listview.setChecked(hItem,checked) 154 | } 155 | } 156 | 157 | winform.btnRemove.skin(style.plainButton) 158 | winform.btnRemove.oncommand = function(id,event){ 159 | 160 | var outboundRemoved = 0; 161 | var subscribeUrls = config.proxy.subscribeUrls; 162 | var items = winform.listview.selected; 163 | if(!#items){ 164 | return winform.msgWarn("未选中任何订阅项( 请用鼠标单击订阅链接,不是指「勾选」 )") 165 | } 166 | 167 | winform.btnUpdate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 168 | 169 | for(i=#items;1;-1){ 170 | var url = subscribeUrls[items[i]].url; 171 | table.remove(subscribeUrls,items[i]); 172 | winform.listview.delItem(items[i]); 173 | 174 | var outbounds = ..config.proxy.outbounds : ..table.array(); 175 | for(i=#outbounds;1;-1){ 176 | var outbound = outbounds[i] 177 | if(outbound.subscribeUrl===url){ 178 | table.remove(outbounds,i); 179 | outboundRemoved++; 180 | } 181 | } 182 | } 183 | 184 | config.proxy.save(); 185 | 186 | if(outboundRemoved){ 187 | publish("uiCommand.restartXrayCore") 188 | } 189 | else { 190 | win.delay(200); 191 | } 192 | 193 | winform.btnUpdate.disabledText = null; 194 | } 195 | 196 | winform.btnAdd.oncommand = function(id,event){ 197 | 198 | 199 | import inet.url; 200 | import win.clip; 201 | var url = win.clip.read(); 202 | url = url ? string.trim(url,'"\'\s\t\r\n '); 203 | if(!inet.url.is(url)){ 204 | url = ""; 205 | } 206 | 207 | config.proxy.subscribeUrls[winform.listview.count+1] = {checked="启用";ps="订阅源";url=url} 208 | var item = winform.listview.addItem({ 209 | "";"启用";"订阅源";url 210 | }) 211 | 212 | winform.listview.setChecked(item,true); 213 | grid.beginEdit(item,4); 214 | } 215 | 216 | winform.listview.onnotify = function(id,code,ptr){ 217 | if( code == 0xFFFFFF9B/*_LVN_ITEMCHANGED*/ ){ 218 | var nmListView = winform.listview.getNotifyMessage(code,ptr); 219 | if( !nmListView ) 220 | return; 221 | 222 | if( nmListView.uNewState & 0xF000/*_LVIS_STATEIMAGEMASK*/){ 223 | var selIndex = nmListView.iItem; 224 | if( winform.listview.getChecked( selIndex ) ){ 225 | config.proxy.subscribeUrls[selIndex].checked = "启用"; 226 | config.proxy.save(); 227 | 228 | winform.listview.setItemText("启用",selIndex,2); 229 | } 230 | else { 231 | config.proxy.subscribeUrls[selIndex].checked = "禁用"; 232 | config.proxy.save(); 233 | 234 | winform.listview.setItemText("禁用",selIndex,2); 235 | } 236 | 237 | var chkAll = true; 238 | for(i=#config.proxy.subscribeUrls;1;-1){ 239 | var sub = config.proxy.subscribeUrls[i] 240 | if(sub.checked!="启用"){ 241 | chkAll = false; 242 | 243 | } 244 | } 245 | 246 | winform.chkAll.checked = chkAll; 247 | } 248 | } 249 | elseif( code == 0xFFFFFFF4/*_NM_CUSTOMDRAW*/ ) { 250 | var lvcd = winform.listview.getNotifyCustomDraw(code,ptr); 251 | if( lvcd.nmcd.dwDrawStage == 0x10001/*_CDDS_ITEMPREPAINT*/) 252 | return 0x20/*_CDRF_NOTIFYSUBITEMDRAW*/ 253 | elseif( lvcd.nmcd.dwDrawStage == 1/*_CDDS_PREPAINT*/ ){ 254 | return 0x20/*_CDRF_NOTIFYITEMDRAW*/; 255 | } 256 | elseif( lvcd.nmcd.dwDrawStage == ( 0x10001/*_CDDS_ITEMPREPAINT*/ | 0x20000/*_CDDS_SUBITEM*/) ){ 257 | //注意这里 iSubItem 的索引自0开始( 其他函数通常自1开始 ) 258 | lvcd.clrText = lvcd.nmcd.dwItemSpec % 2 ? 0x373737 : 0; 259 | lvcd.clrTextBk = lvcd.nmcd.dwItemSpec % 2 ? 0xFFFFFF : 0xF5F5F5; 260 | lvcd.update(); 261 | 262 | return 0/*_CDRF_DODEFAULT*/ 263 | } 264 | } 265 | elseif( code == 0xFFFFFFFB/*_NM_RCLICK*/ ) { 266 | var x,y = win.getCursorPos(); 267 | winform.popmenu.popup(x,y,true);//弹出菜单 268 | } 269 | } 270 | 271 | winform.show(); 272 | win.loopMessage(); 273 | 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/speed.cloudflare")+ `" --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("响应速度",80) 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,port,enableRetry,currentTesting){ 63 | import web.rest.client; 64 | var restClient = web.rest.client(,"SOCKS=127.0.0.1:"+port); 65 | restClient._http.setTimeouts(1000,2000,2000); 66 | restClient.get("https://www.google.com/generate_204"); 67 | var lastStatusCode = restClient.lastStatusCode; 68 | 69 | 70 | if( lastStatusCode == 204 ){ 71 | frmXray.onStartXrayComplete(outboundIndex,currentTesting); 72 | return; 73 | } 74 | restClient.close(); 75 | 76 | import wsock.tcp.socks5Client; 77 | for(i=1;2;1){ 78 | sleep(100); 79 | 80 | var client; 81 | for(i=1;5;1){ 82 | sleep(1000); 83 | client = wsock.tcp.socks5Client("127.0.0.1",port); 84 | if(client)break; 85 | } 86 | 87 | if(!client){ 88 | continue; 89 | }; 90 | 91 | client.setTimeouts(1000,1000); 92 | if(client.connect("www.google.com",80) ){ 93 | 94 | var sendData =/*********** 95 | GET / HTTP/1.1 96 | Host: www.google.com 97 | Connection: close 98 | User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) 99 | Accept: */*; 100 | Accept-Language: zh-CN,zh; 101 | Accept-Charset:utf-8; 102 | ***********/ 103 | client.write( sendData + '\r\n\r\n' ) 104 | var rep = client.read(9); 105 | client.close(); 106 | 107 | if(outboundAddress!=frmXray.listview.getItemText(outboundIndex,2)){ 108 | return; 109 | } 110 | 111 | if( rep && ..string.startWith(rep,"HTTP/1.1 ") ){ 112 | frmXray.onStartXrayComplete(outboundIndex,currentTesting); 113 | return; 114 | } 115 | } 116 | } 117 | frmXray.onStartXrayFailed(outboundIndex,enableRetry); 118 | },frmXray,outboundIndex,outboundAddress,xray.core.socksProxyPort,enableRetry,currentTesting 119 | ) 120 | 121 | return true; 122 | } 123 | } 124 | 125 | var autoUpdateSubscriptionTimerId; 126 | var autoUpdateSubscription = function(...){ 127 | ..win.clearTimeout(autoUpdateSubscriptionTimerId); 128 | autoUpdateSubscriptionTimerId = ..win.setTimeout(function(...){ 129 | autoUpdateSubscriptionTimerId = null; 130 | 131 | import xray.outbounds; 132 | xray.outbounds.autoUpdateSubscription(); 133 | },3000,...) 134 | } 135 | 136 | frmXray.onStartXrayComplete = function(outboundIndex,currentTesting){ 137 | if(autoUpdateSubscriptionTimerId){ 138 | ..win.clearTimeout(autoUpdateSubscriptionTimerId); 139 | autoUpdateSubscriptionTimerId = null; 140 | } 141 | 142 | frmXray.listview.ensureVisible(outboundIndex); 143 | 144 | for b,v in table.eachIndex(config.proxy.outbounds){ 145 | frmXray.listview.setItemText("",b,8); 146 | } 147 | 148 | frmXray.autoTesting = false; 149 | frmXray.listview.setItemText("活动服务器:正常",outboundIndex,8); 150 | frmXray.listview.activeOutboundIndex = outboundIndex; 151 | 152 | var address = frmXray.listview.getItemText(outboundIndex,2); 153 | if(!currentTesting) frmXray.edit.print("已切换到服务器:",address); 154 | 155 | if(frmXray.listview.getItemText(outboundIndex,7)=="不可用"){ 156 | frmXray.listview.setItemText("...",outboundIndex,7); 157 | } 158 | 159 | frmXray.onTcpingReturn = function(){}; 160 | frmXray.btnTcping.disabledText = null; 161 | frmXray.validOutbounds = {}; 162 | 163 | ..publish("activeOutbound",address); 164 | } 165 | 166 | subscribe("activeOutbound",function(address){ 167 | ..globalActiveOutbound = address; 168 | } ) 169 | 170 | frmXray.onStartXrayFailed = function(outboundIndex,enableRetry){ 171 | ..publish("activeOutbound",false); 172 | autoUpdateSubscription(); 173 | 174 | frmXray.listview.ensureVisible(outboundIndex); 175 | if(frmXray.listview.activeOutboundIndex==outboundIndex){ 176 | frmXray.listview.activeOutboundIndex=null; 177 | } 178 | 179 | if(enableRetry){ 180 | if(#frmXray.validOutbounds){ 181 | var next = table.shift(frmXray.validOutbounds); 182 | activeOutbound(next[1],next[2],#frmXray.validOutbounds); 183 | frmXray.listview.setItemText("异常,测试下一个...",outboundIndex,8); 184 | frmXray.edit.print("服务器异常:",frmXray.listview.getItemText(outboundIndex,2)); 185 | frmXray.edit.print("正在尝试连接:",next[2]); 186 | } 187 | else { 188 | frmXray.btnTcping.oncommand(); 189 | } 190 | } 191 | else { 192 | frmXray.listview.setItemText("活动服务器:异常",outboundIndex,8); 193 | frmXray.edit.print("已切换到服务器:",frmXray.listview.getItemText(outboundIndex,2)); 194 | frmXray.autoTesting = false; 195 | } 196 | 197 | frmXray.btnTcping.disabledText = null; 198 | } 199 | 200 | var tcping = function(frmXray,outboundIndex,address){ 201 | import wsock.tcp.client; 202 | var item = frmXray.listview.items[outboundIndex]; 203 | 204 | var timeout = 0; 205 | for(i=1;8;1){ 206 | var client = wsock.tcp.client(); 207 | var tickBegin = time.tick(); 208 | if(client.connectTimeout(item[2],tonumber(item[3]),3)){ 209 | var ret = client.send("."); 210 | client.close(); 211 | 212 | if( ret != 1){ 213 | frmXray.listview.setItemText("不可用",outboundIndex,7); 214 | frmXray.onTcpingReturn(outboundIndex,address,false); 215 | return; 216 | } 217 | timeout = timeout + (time.tick() - tickBegin); 218 | } 219 | else { 220 | client.close(); 221 | frmXray.listview.setItemText("不可用",outboundIndex,7); 222 | frmXray.onTcpingReturn(outboundIndex,address,false); 223 | return; 224 | } 225 | } 226 | 227 | frmXray.onTcpingReturn(outboundIndex,address,true); 228 | frmXray.listview.setItemText(math.round(timeout / 8 / 1000,2)+"秒",outboundIndex,7); 229 | }; 230 | 231 | import sysProxy; 232 | import style; 233 | frmXray.btnTcping.skin(style.primaryButton); 234 | frmXray.btnTcping.oncommand = function(id,event){ 235 | frmXray.listview.activeOutboundIndex = null; 236 | var items = frmXray.listview.items; 237 | if(#items){ 238 | frmXray.btnTcping.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'} 239 | if(!xray.core.getPath()){ 240 | frmXray.btnTcping.disabledText = null; 241 | frmXray.msgErr("下载 xray.exe 失败") 242 | return; 243 | } 244 | 245 | var faieldCount = 0; 246 | frmXray.onTcpingReturn = function(outboundIndex,address,succeeded){ 247 | if(address!=frmXray.listview.getItemText(outboundIndex,2)){ 248 | return; 249 | } 250 | 251 | if(config.proxy.outbounds[outboundIndex].autoConnect === false ){ 252 | faieldCount++; 253 | if(faieldCount==#items) frmXray.autoTesting = false; 254 | return; 255 | } 256 | 257 | if(succeeded){ 258 | frmXray.validOutbounds = {} 259 | frmXray.onTcpingReturn = function(outboundIndex,address,succeeded){ 260 | if(address!=frmXray.listview.getItemText(outboundIndex,2)){ 261 | return; 262 | } 263 | 264 | if(config.proxy.outbounds[outboundIndex].autoConnect === false ){ 265 | faieldCount++; 266 | if(faieldCount==#items) frmXray.autoTesting = false; 267 | return; 268 | } 269 | 270 | if(succeeded) table.push(frmXray.validOutbounds,{outboundIndex;address}); 271 | else{ 272 | faieldCount++; 273 | if(faieldCount==#items) frmXray.autoTesting = false; 274 | } 275 | } 276 | activeOutbound(outboundIndex,address,#items>1); 277 | } 278 | else{ 279 | faieldCount++; 280 | if(faieldCount==#items) frmXray.autoTesting = false; 281 | } 282 | } 283 | 284 | thread.createSuspended(true); 285 | 286 | var handles = {}; 287 | var outbounds = config.proxy.outbounds; 288 | var lenPing = 0; 289 | for(i=1;#items;1){ 290 | if(outbounds[i].autoConnect===false){ 291 | frmXray.listview.setItemText("禁止测速",i,7); 292 | continue; 293 | } 294 | 295 | if(lenPing>300){ 296 | break; 297 | } 298 | lenPing++; 299 | 300 | frmXray.listview.setItemText("...",i,7); 301 | frmXray.listview.setItemText("",i,8); 302 | 303 | var h = thread.create(tcping,frmXray,i,frmXray.listview.getItemText(i,2)); 304 | table.push(handles,h); 305 | } 306 | thread.createSuspended(false); 307 | 308 | var dnsapi = ..raw.loadDll("dnsapi.dll"); 309 | dnsapi.DnsFlushResolverCacheB(); 310 | 311 | for(i=1;#handles;1){ 312 | var h = handles[i]; 313 | thread.resume(h); 314 | raw.closehandle(h); 315 | } 316 | 317 | if(#handles) ..thread.waitOne(handles); 318 | frmXray.btnTcping.disabledText = null; 319 | } 320 | else { 321 | if(id) frmXray.msgWarn("请先添加服务器") 322 | xray.core.stop(); 323 | } 324 | } 325 | 326 | import xray.core; 327 | import xray.pacServer; 328 | frmXray.onDestroy = function(){ 329 | xray.core.stop(); 330 | xray.pacServer.stop(); 331 | } 332 | 333 | var pingThread = function( ip,frmXray ) { 334 | import icmp.ping; 335 | var ping = icmp.ping(); 336 | 337 | for(i=1;4;1){ 338 | if( ping.perform(ip) ){ 339 | frmXray.edit.print(ip,string.format( "Reply from %s: bytes=%d time=%dms TTL=%d" 340 | , ping.ip , #ping.requestData , ping.echoReply.roundTripTime , ping.echoReply.options.ttl ) ); 341 | } 342 | else { 343 | frmXray.edit.print(ip,"Request timed out"); 344 | } 345 | 346 | } 347 | } 348 | 349 | import xray.outbounds; 350 | frmXray.listview.onnotify = function(id,code,ptr){ 351 | select(code) { 352 | case 0xFFFFFFF4/*_NM_CUSTOMDRAW*/ { 353 | var lvcd = frmXray.listview.getNotifyCustomDraw(code,ptr); 354 | if( lvcd.nmcd.dwDrawStage == 0x10001/*_CDDS_ITEMPREPAINT*/) 355 | return 0x20/*_CDRF_NOTIFYSUBITEMDRAW*/ 356 | elseif( lvcd.nmcd.dwDrawStage == 1/*_CDDS_PREPAINT*/ ){ 357 | return 0x20/*_CDRF_NOTIFYITEMDRAW*/; 358 | } 359 | elseif( lvcd.nmcd.dwDrawStage == ( 0x10001/*_CDDS_ITEMPREPAINT*/ | 0x20000/*_CDDS_SUBITEM*/) ){ 360 | //注意这里 iSubItem 的索引自0开始( 其他函数通常自1开始 ) 361 | lvcd.clrText = lvcd.nmcd.dwItemSpec % 2 ? 0x373737 : 0; 362 | lvcd.clrTextBk = lvcd.nmcd.dwItemSpec % 2 ? 0xFFFFFF : 0xF5F5F5; 363 | 364 | var item = lvcd.nmcd.dwItemSpec+1; 365 | if(lvcd.iSubItem==7){ 366 | if(frmXray.listview.activeOutboundIndex=item){ 367 | lvcd.clrTextBk = 0x8AFFC3 368 | } 369 | } 370 | elseif(lvcd.iSubItem==6){ 371 | if(..config.proxy.outbounds[item].autoConnect===false){ 372 | lvcd.clrText = 0xCCCCCC; 373 | } 374 | } 375 | lvcd.update() 376 | 377 | return 0/*_CDRF_DODEFAULT*/ 378 | } 379 | } 380 | case 0xFFFFFFFD/*_NM_DBLCLK*/ { 381 | var nm = frmXray.listview.getNotifyMessage(code,ptr); 382 | if( nm ){ 383 | frmXray.onTcpingReturn = function(i){} 384 | frmXray.btnTcping.disabledText = null; 385 | 386 | for b,v in table.eachIndex(config.proxy.outbounds){ 387 | frmXray.listview.setItemText("",b,8); 388 | } 389 | activeOutbound(nm.iItem,frmXray.listview.getItemText(nm.iItem,2),false); 390 | } 391 | } 392 | case 0xFFFFFFFB/*_NM_RCLICK*/ { 393 | var x,y = win.getCursorPos(); 394 | var nm = frmXray.listview.getNotifyMessage(code,ptr); 395 | if(!nm) return; 396 | ; 397 | var currentIdx = nm.iItem; 398 | if(!currentIdx){ 399 | var popmenu = win.ui.popmenu(frmXray); 400 | 401 | popmenu.add("自剪贴板批量导入分享链接、订阅源",function(id){ 402 | frmXray.btnImportServerFromClipBd.oncommand(); 403 | }); 404 | 405 | popmenu.add("重置为默认服务器列表", 406 | function(id){ 407 | ..config.__loadDefaultOutbounds(); 408 | publish("outbounds.updateConfigJson"); 409 | publish("uiCommand.restartXrayCore"); 410 | } 411 | ); 412 | 413 | popmenu.add('清空服务器列表',function(id){ 414 | config.proxy.outbounds = ..table.array(); 415 | frmXray.listview.clear(); 416 | publish("outbounds.updateConfigJson"); 417 | }); 418 | 419 | popmenu.add() 420 | popmenu.add('立即更新所有订阅源',function(id){ 421 | xray.outbounds.updateSubscription(); 422 | }); 423 | 424 | popmenu.popup(x,y,true); 425 | return; 426 | } 427 | 428 | var selectedItems = frmXray.listview.selected; 429 | var popmenu = win.ui.popmenu(frmXray); 430 | 431 | if(#selectedItems==1){ 432 | popmenu.add('设为活动服务器( 鼠标双击 )',function(id){ 433 | if(currentIdx){ 434 | frmXray.onTcpingReturn = function(i){} 435 | frmXray.btnTcping.disabledText = null; 436 | 437 | for b,v in table.eachIndex(config.proxy.outbounds){ 438 | frmXray.listview.setItemText("",b,8); 439 | } 440 | activeOutbound(currentIdx,frmXray.listview.getItemText(currentIdx,2),false) 441 | } 442 | }); 443 | } 444 | 445 | if(#selectedItems>0){ 446 | var autoConnect = config.proxy.outbounds[currentIdx].autoConnect!==false; 447 | var id = popmenu.add('允许自动测速并连接',function(id){ 448 | if(currentIdx){ 449 | for(i=#selectedItems;1;-1){ 450 | var item = selectedItems[i]; 451 | if(autoConnect === false){ 452 | config.proxy.outbounds[item].autoConnect = null; 453 | frmXray.listview.setItemText("",item,7); 454 | } 455 | else { 456 | config.proxy.outbounds[item].autoConnect = false; 457 | frmXray.listview.setItemText("禁止测速",item,7); 458 | } 459 | } 460 | } 461 | }); 462 | popmenu.check(id,autoConnect,0/*_MF_BYCOMMAND*/); 463 | } 464 | 465 | if(#selectedItems==1){ 466 | popmenu.add(); 467 | popmenu.add('SSH 登录服务器',function(id){ 468 | if(currentIdx){ 469 | if(_STUDIO_INVOKED){ 470 | frmXray.msgErr("请在发布后运行此功能"); 471 | return; 472 | } 473 | import process; 474 | process.execute(io._exepath,"/sshLogin root@" 475 | + config.proxy.outbounds[currentIdx].address + ":" 476 | + (config.proxy.outbounds[currentIdx].sshPort||22)); 477 | } 478 | }); 479 | 480 | popmenu.add(); 481 | popmenu.add('生成二维码',function(id){ 482 | if(currentIdx){ 483 | import xray.outbounds; 484 | import win.clip; 485 | 486 | var str = xray.outbounds.exportSharedLinks({config.proxy.outbounds[currentIdx]}); 487 | if(str){ 488 | var frmChild = frmXray.loadForm("\forms\main\tools\qr.aardio"); 489 | frmChild.createQrCode(str); 490 | frmChild.show(); 491 | } 492 | } 493 | }); 494 | 495 | popmenu.add('复制服务器分享链接',function(id){ 496 | if(currentIdx){ 497 | import xray.outbounds; 498 | import win.clip; 499 | 500 | var str = xray.outbounds.exportSharedLinks({config.proxy.outbounds[currentIdx]}); 501 | if(str){ 502 | win.clip.write(str); 503 | frmXray.edit.print("已复制链接:",str); 504 | } 505 | } 506 | }); 507 | 508 | popmenu.add('复制服务器地址',function(id){ 509 | if(currentIdx){ 510 | import win.clip; 511 | win.clip.write(config.proxy.outbounds[currentIdx].address + ":" + config.proxy.outbounds[currentIdx].port); 512 | frmXray.edit.print("已复制服务器地址:",config.proxy.outbounds[currentIdx].address + ":" + config.proxy.outbounds[currentIdx].port); 513 | } 514 | }); 515 | popmenu.add('复制服务器 JSON 配置',function(id){ 516 | if(currentIdx){ 517 | import win.clip; 518 | import web.json; 519 | var str = web.json.stringify(config.proxy.outbounds[currentIdx],true,false); 520 | win.clip.write(str); 521 | frmXray.edit.print(str); 522 | } 523 | }); 524 | popmenu.add(); 525 | 526 | popmenu.add('本机 Ping 检测',function(id){ 527 | if(currentIdx){ 528 | thread.invoke(pingThread,config.proxy.outbounds[currentIdx].address,frmXray) 529 | } 530 | }); 531 | 532 | popmenu.add('全球范围 Ping 检测 ...',function(id){ 533 | if(currentIdx){ 534 | process.openUrl("http://ping.pe/" + config.proxy.outbounds[currentIdx].address) 535 | } 536 | }); 537 | 538 | popmenu.add('全球范围端口检测 ...',function(id){ 539 | if(currentIdx){ 540 | process.openUrl("http://port.ping.pe/" + config.proxy.outbounds[currentIdx].address + ":" + config.proxy.outbounds[currentIdx].port) 541 | } 542 | }); 543 | 544 | popmenu.add(); 545 | 546 | if(config.proxy.outbounds[currentIdx].port == 443 && config.proxy.outbounds[currentIdx].tls){ 547 | popmenu.add('浏览 HTTPS 主页 ...',function(id){ 548 | if(currentIdx){ 549 | process.openUrl("https" ++ "://" + config.proxy.outbounds[currentIdx].address); 550 | } 551 | }); 552 | popmenu.add(); 553 | } 554 | } 555 | 556 | popmenu.add() 557 | popmenu.add("自剪贴板批量导入分享链接、订阅源",function(id){ 558 | frmXray.btnImportServerFromClipBd.oncommand(); 559 | }); 560 | 561 | popmenu.add("重置为默认服务器列表", 562 | function(id){ 563 | ..config.__loadDefaultOutbounds(); 564 | publish("outbounds.updateConfigJson"); 565 | publish("uiCommand.restartXrayCore"); 566 | } 567 | ); 568 | 569 | popmenu.add('清空服务器列表',function(id){ 570 | if(currentIdx){ 571 | config.proxy.outbounds = ..table.array(); 572 | frmXray.listview.clear(); 573 | publish("outbounds.updateConfigJson"); 574 | } 575 | }); 576 | 577 | popmenu.add() 578 | popmenu.add('立即更新所有订阅源',function(id){ 579 | if(currentIdx){ 580 | xray.outbounds.updateSubscription(); 581 | } 582 | }); 583 | 584 | popmenu.add() 585 | popmenu.add('删除服务器',function(id){ 586 | if(currentIdx){ 587 | for(i=#selectedItems;1;-1){ 588 | var item = selectedItems[i] 589 | frmXray.listview.delItem(item); 590 | ..table.remove(config.proxy.outbounds,item); 591 | } 592 | 593 | publish("outbounds.updateConfigJson"); 594 | } 595 | }); 596 | 597 | popmenu.popup(x,y,true); 598 | } 599 | } 600 | } 601 | 602 | subscribe("uiCommand.restartXrayCore",function(){ 603 | config.proxy.outbounds.fields = {"protocol";"address";"port";"security";"network";"ps";} 604 | 605 | var outbounds = config.proxy.outbounds; 606 | for(i=#outbounds;1;-1){ 607 | if(type(outbounds[i])!="table"){ 608 | table.remove(outbounds,i); 609 | } 610 | } 611 | frmXray.listview.setTable(outbounds); 612 | 613 | frmXray.btnTcping.oncommand(); 614 | } ) 615 | 616 | import process; 617 | if(#config.proxy.outbounds){ 618 | config.proxy.outbounds.fields = {"protocol";"address";"port";"security";"network";"ps";} 619 | 620 | var outbounds = config.proxy.outbounds; 621 | for(i=#outbounds;1;-1){ 622 | if(type(outbounds[i])!="table"){ 623 | table.remove(outbounds,i); 624 | } 625 | } 626 | frmXray.listview.setTable(outbounds); 627 | 628 | frmXray.setTimeout( 629 | function(){ 630 | publish("uiCommand.restartXrayCore"); 631 | },1000 632 | ); 633 | } 634 | 635 | subscribe("sysProxy.modeChanged",function(mode){ 636 | frmXray.radioProxyPac.checked = false; 637 | frmXray.radioProxy.checked = false; 638 | frmXray.radioProxyDirect.checked = false; 639 | 640 | if(mode = "pac"){ 641 | frmXray.radioProxyPac.checked = true; 642 | } 643 | elseif(mode = "proxy"){ 644 | frmXray.radioProxy.checked = true; 645 | } 646 | else{ 647 | frmXray.radioProxyDirect.checked = true; 648 | } 649 | } ) 650 | publish("sysProxy.modeChanged",config.proxy.mode); 651 | 652 | import sysProxy; 653 | var switchProxyMode = function(){ 654 | if(frmXray.radioProxyPac.checked){ 655 | sysProxy.switchToPacMode(); 656 | } 657 | elseif(frmXray.radioProxy.checked){ 658 | sysProxy.switchToGlobalMode(); 659 | } 660 | else { 661 | sysProxy.switchToDirectMode(); 662 | } 663 | } 664 | frmXray.radioProxyPac.oncommand = switchProxyMode; 665 | frmXray.radioProxy.oncommand = switchProxyMode; 666 | frmXray.radioProxyDirect.oncommand = switchProxyMode; 667 | 668 | import win.dlg.message; 669 | frmXray.btnEditPac.skin(style.plainButton) 670 | frmXray.btnEditPac.oncommand = function(id,event){ 671 | if(frmPac && win.isWindow(frmPac.hwnd)){ 672 | if( win.isIconic(frmPac.hwnd) ) win.show(frmPac.hwnd,9/*_SW_RESTORE*/ ); 673 | if( !win.isVisible(frmPac.hwnd) ) win.show(frmPac.hwnd,0x1/*_SW_NORMAL*/ ); 674 | win.setForeground(frmPac.hwnd) 675 | return; 676 | } 677 | 678 | frmPac = ..mainForm.loadForm("\forms\main\pac.aardio"); 679 | frmPac.show(); 680 | } 681 | subscribe("uiCommand.showPacForm",function(...){ 682 | frmXray.btnEditPac.oncommand(); 683 | } ) 684 | 685 | subscribe("uiCommand.print",function(...){ 686 | frmXray.edit.print(...); 687 | } ) 688 | 689 | import xray.outbounds; 690 | frmXray.btnImportServerFromClipBd.skin(style.plainButton) 691 | frmXray.btnImportServerFromClipBd.oncommand = function(id,event){ 692 | var outbounds = xray.outbounds.importFromClipboard(); 693 | if(#outbounds){ 694 | ..table.append(config.proxy.outbounds,outbounds); 695 | publish("uiCommand.restartXrayCore"); 696 | publish("outbounds.updateConfigJson"); 697 | frmXray.msgOk("已成功导入" + #outbounds + "个服务器",1200); 698 | return; 699 | } 700 | 701 | frmXray.msgFrown('未导入服务器! \r\n请先复制一个或多个以下格式服务器分享链接:\r\n\r\nvmess:// 链接\r\nvless:// 链接\r\nss:// 链接\r\ntrojan:// 链接\r\ntrojan-go:// 链接\r\nhttp:// 或 https:// 通用订阅源!\r\n\r\n订阅源可直接返回winXray格式的配置JSON。\n订阅源地址可以直接指定github项目网址(兼容raw,blob格式)。') 702 | } 703 | 704 | var testTimerId; 705 | frmXray.chkAutoTest.oncommand = function(id,event){ 706 | if(testTimerId){ 707 | frmXray.clearInterval(testTimerId); 708 | } 709 | 710 | config.proxy.test = frmXray.chkAutoTest.checked; 711 | if(!config.proxy.test) return; 712 | 713 | testTimerId = frmXray.setInterval( 714 | config.proxy.testInterval*1000,function(){ 715 | if( frmXray.autoTesting ) return; 716 | frmXray.autoTesting = true; 717 | 718 | activeOutbound(,,true); 719 | return config.proxy.testInterval*1000; 720 | } 721 | ) 722 | } 723 | 724 | if(config.proxy.test){ 725 | frmXray.chkAutoTest.checked = true; 726 | frmXray.chkAutoTest.oncommand(); 727 | } 728 | 729 | frmXray.chkAutoTest.skin(style.checkBox); 730 | 731 | frmXray.enableDpiScaling(); 732 | 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.testInterval){ 36 | config.proxy.testInterval = 15; 37 | } 38 | 39 | if(!config.proxy.subscribeUrls){ 40 | config.proxy.subscribeUrls = { 41 | fields = {"";"checked";"ps";"url";} 42 | } 43 | } 44 | 45 | config.__resetDefaultCore = function(){ 46 | config.core.default = { 47 | inbounds={ 48 | { 49 | listen="127.0.0.1"; 50 | port=0; 51 | protocol="socks"; 52 | settings={ 53 | auth="noauth"; 54 | udp=true; 55 | }; 56 | sniffing={ 57 | destOverride={ 58 | "http"; 59 | "tls" 60 | }; 61 | enabled=true 62 | }; 63 | tag="proxy"; 64 | }; 65 | { 66 | listen="127.0.0.1"; 67 | port=0; 68 | protocol="http"; 69 | settings={}; 70 | sniffing={ 71 | destOverride={ 72 | "http"; 73 | "tls" 74 | }; 75 | enabled=true 76 | }; 77 | tag="http_proxy" 78 | } 79 | }; 80 | log={ 81 | access=""; 82 | error=""; 83 | loglevel="warning" 84 | }; 85 | outbounds={ 86 | { 87 | tag="proxy" 88 | }; 89 | { 90 | protocol="freedom"; 91 | settings={}; 92 | tag="direct"; 93 | }; 94 | { 95 | protocol="blackhole"; 96 | settings={ 97 | response={ 98 | type="http" 99 | }; 100 | }; 101 | tag="block"; 102 | } 103 | }; 104 | routing={ 105 | domainStrategy="IPIfNonMatch"; 106 | rules={ 107 | { 108 | inboundTag={ 109 | "api" 110 | }; 111 | outboundTag="api"; 112 | type="field"; 113 | } 114 | } 115 | }; 116 | } 117 | } 118 | 119 | if(!config.core.default){ 120 | config.__resetDefaultCore(); 121 | } 122 | 123 | if(!config.core.default.inbounds[2]){ 124 | config.core.default.inbounds[2] = { 125 | listen="127.0.0.1"; 126 | port=0; 127 | protocol="http"; 128 | settings={}; 129 | sniffing={ 130 | destOverride={ 131 | "http"; 132 | "tls" 133 | }; 134 | enabled=true 135 | }; 136 | tag="http_proxy" 137 | } 138 | } 139 | 140 | /**intellisense(config) 141 | __appName = 应用程序名 142 | ? = 配置文件名,\n读写配置并序列化为一个表对象,\n表的成员值可以是支持序列化的普通变量,支持table对象\n配置文件在首次使用时自动加载,退出程序时自动保存\n!fsys_table. 143 | end intellisense**/ -------------------------------------------------------------------------------- /lib/style.aardio: -------------------------------------------------------------------------------- 1 | //style 外观样式 2 | 3 | namespace style{ 4 | primaryButton = { 5 | background={ 6 | default=0xFF8FB2B0; 7 | hover=0xFF928BB3; 8 | disabled=0xFFCCCCCC; 9 | } 10 | }; 11 | button = { 12 | background={ 13 | default=0x668FB2B0; 14 | hover=0xFF928BB3; 15 | disabled=0xFFCCCCCC; 16 | } 17 | }; 18 | transButton = { 19 | background={ 20 | default=0; 21 | hover=0xFF928BB3; 22 | disabled=0xFFCCCCCC; 23 | } 24 | }; 25 | checkBox = { 26 | color = { 27 | hover = 0xFFFF0000; 28 | active = 0xFF00FF00; 29 | disabled = 0xEE666666; 30 | } 31 | checked = { 32 | color = { 33 | hover = 0xFFFF0000; 34 | active = 0xFF00FF00; 35 | disabled = 0xEE666666; 36 | } 37 | iconText = '\uF14a'/*_FA_CHECK_SQUARE*/ 38 | } 39 | }; 40 | radio ={ 41 | group = "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 | } 96 | -------------------------------------------------------------------------------- /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 httpProxPort = ..xray.core.httpProxPort; 26 | var socksProxyPort = ..xray.core.socksProxyPort; 27 | 28 | if(socksProxyPort){ 29 | if(..config.proxy.useHttpGlobal){ 30 | ..inet.conn.setProxy(,"127.0.0.1:" + httpProxPort) 31 | } 32 | else { 33 | ..inet.conn.setProxy(,"SOCKS=127.0.0.1:"+socksProxyPort); 34 | } 35 | 36 | ..xray.github.setProxy(true); 37 | } 38 | } 39 | else { 40 | ..inet.conn.setProxy(); 41 | ..xray.github.setProxy(false); 42 | } 43 | 44 | ..publish("sysProxy.modeChanged",mode) 45 | } 46 | 47 | switchToGlobalMode = function(){ 48 | switch("proxy") 49 | } 50 | 51 | switchToPacMode = function(){ 52 | switch("pac") 53 | } 54 | 55 | switchToDirectMode = function(){ 56 | switch("direct") 57 | } 58 | 59 | reset = function(proxy){ 60 | if(proxy===false){ 61 | if(..config.proxy.mode != "direct"){ 62 | ..inet.conn.setProxy(); 63 | ..xray.github.setProxy(false); 64 | } 65 | return; 66 | } 67 | 68 | ..inet.conn.setProxy(); 69 | switch(..config.proxy.mode); 70 | } 71 | 72 | ..subscribe("pacServer.restarted",function(pacPort){ 73 | if(..config.proxy.mode == "pac"){ 74 | switchToPacMode(); 75 | } 76 | } ) 77 | 78 | ..subscribe("xrayCore.restarted",function(socksProxyPort,httpProxPort){ 79 | if(..config.proxy.mode != "direct"){ 80 | reset(true); 81 | } 82 | } ) 83 | 84 | /**intellisense(sysProxy) 85 | switch(.(mode) = 切换代理模式,同步修改配置文件。\n可选参数:"proxy","pac","direct" 86 | switchToGlobalMode() = 设为全局代理,同步修改配置文件。 87 | switchToPacMode() = 设为PAC自动代理,同步修改配置文件。 88 | switchToDirectMode() = 设为禁用代理,同步修改配置文件。 89 | reset(.(是否启用代理)) = 重置系统代理设置,不修改配置文件。\n省略参数时默认值为true。 90 | end intellisense**/ 91 | -------------------------------------------------------------------------------- /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 | isInboundPortChanged = function(){ 12 | var inbounds = ..config.core.default.inbounds; 13 | if(! (..table.isArray(inbounds) && inbounds[1] && inbounds[2]) ){ 14 | return true; 15 | } 16 | 17 | return (inbounds[1].port!=socksProxyPort) || (inbounds[2].port!=httpProxPort) 18 | } 19 | 20 | var prcsXray; 21 | import process.job.limitKill; 22 | restart = function(editor,outbound){ 23 | if(!outbound){ return null,"错误的出站代理服务器配置" } 24 | if(_WINXP){ 25 | return null,"抱歉!Xray Core 不支持 Windows XP, 仅支持 Windows 7, Windows 10 以及更新操作系统。"; 26 | } 27 | 28 | var inbounds = ..config.core.default[["inbounds"]]; 29 | if(! (..table.isArray(inbounds) && inbounds[1] ) ){ 30 | return null,"错误的入站SOCKS代理服务器配置" 31 | } 32 | if(! ( inbounds[2] ) ){ 33 | return null,"错误的入站HTTP代理服务器配置" 34 | } 35 | 36 | if(prcsXray){ 37 | prcsXray.terminate(); 38 | prcsXray = null; 39 | } 40 | 41 | socksProxyPort = inbounds[1].port; 42 | httpProxPort = inbounds[2].port; 43 | if( (! socksProxyPort) || socksProxyPort >= 49152 ){ socksProxyPort = ..wsock.tcp.server.getFreePort("127.0.0.1",1081,10801,10811,44821) } 44 | if( (! httpProxPort) || httpProxPort >= 49152 ){ httpProxPort = ..wsock.tcp.server.getFreePort("127.0.0.1",1082,10802,10812,44822) } 45 | 46 | var corePath = getPath(editor.hwnd); 47 | if(!corePath){ return false,"启动失败,未找到xray.exe"; } 48 | 49 | import xray.core.configJson; 50 | var jsonPath,err = ..xray.core.configJson.write( 51 | ..io.joinpath(..io.splitpath(corePath).dir,"config.json") 52 | ,outbound,socksProxyPort,httpProxPort); 53 | if(!jsonPath){ 54 | return false,err:"启动失败,写入配置文件遇到错误!"; 55 | } 56 | 57 | ..publish("activeOutbound",false); 58 | 59 | import sysProxy; 60 | ..sysProxy.reset(false); 61 | 62 | var err; 63 | prcsXray,err = ..process.popen(corePath,"run","-c=config.json" ); 64 | if(!prcsXray){ 65 | ..publish("uiCommand.print",err:"启动 Xray Core 时遇到未知错误!"); 66 | 67 | if(!..process().isWow64()){ 68 | if(..process.isExe(corePath)!="PE32"){ 69 | ..io.remove(corePath); 70 | if(!..io.exist(corePath)){ 71 | ..publish("uiCommand.print","当前操作系统是32位,已删除无效的64位 Xray Core,正在下载32位 Xray Core"); 72 | return restart(editor,outbound) 73 | } 74 | else { 75 | ..publish("uiCommand.print","当前操作系统是32位,但是找到的 Xray Core 是64位,请重新下载32位 Xray Core"); 76 | } 77 | } 78 | } 79 | 80 | return; 81 | } 82 | prcsXray.assignToJobObject(process.job.limitKill); 83 | prcsXray.logResponse(editor); 84 | 85 | inbounds[1].port = socksProxyPort; 86 | inbounds[2].port = httpProxPort; 87 | ..config.core.save(); 88 | 89 | ..publish("uiCommand.print","已启动代理服务器,SOCKS端口:" + socksProxyPort + " HTTP端口:" + httpProxPort) 90 | ..publish("xrayCore.restarted",socksProxyPort,httpProxPort); 91 | return true; 92 | } 93 | 94 | stop = function(){ 95 | if(prcsXray){ 96 | prcsXray.terminate(); 97 | prcsXray = null; 98 | } 99 | 100 | ..sysProxy.reset(false); 101 | 102 | import xray.github; 103 | xray.github.setProxy(false); 104 | } 105 | 106 | var getXrayCoreUrl = function(){ 107 | import inet.http; 108 | var http = ..inet.http(); 109 | var url = http.location("https://github.com/XTLS/Xray-core/releases/latest"); 110 | http.close(); 111 | 112 | if(!url)return; 113 | 114 | var tag = ..string.match(url,"[^/]+$") 115 | if(!tag) return; 116 | 117 | return "https://github.com/XTLS/Xray-core/releases/download/" 118 | + tag + "/xray-windows-" + (..process().isWow64() ? "64" : "32") + ".zip",tag 119 | } 120 | 121 | getPath = function(hwnd){ 122 | var path = ..io.fullpath("/xray-core/xray.exe"); 123 | if(..io.exist(path)){ 124 | return path; 125 | } 126 | 127 | var path = ..io.appData("/xray/core/xray.exe"); 128 | if(..io.exist(path)){ 129 | return path; 130 | } 131 | 132 | var url = getXrayCoreUrl(); 133 | if(!url) return null; 134 | 135 | import zlib.httpFile; 136 | if( ..zlib.httpFile.download(url,"正在下载 Xray Core" 137 | ,..io.appData("/xray/download/") 138 | ,..io.appData("/xray/core/"),,hwnd) ) 139 | return ..io.exist(path); 140 | } 141 | 142 | updateCore = function(){ 143 | var coreDir = ..io.exist("/xray-core/xray.exe") ? ..io.fullpath("/xray-core/"); 144 | if(!coreDir){ 145 | coreDir = ..io.appData("/xray/core/"); 146 | } 147 | 148 | var url,versionTag = getXrayCoreUrl(); 149 | if(!url) return null; 150 | 151 | import fsys; 152 | fsys.delete(..io.appData("/xray/temp/")) 153 | 154 | import zlib.httpFile; 155 | if( ..zlib.httpFile.download(url,"正在下载 Xray Core" 156 | ,..io.appData("/xray/download/") 157 | ,..io.appData("/xray/temp/"),,..mainForm.hwnd) ){ 158 | if(..io.exist(..io.appData("/xray/temp/xray.exe"))){ 159 | import sysProxy; 160 | sysProxy.reset(false); 161 | 162 | import process.file; 163 | process.file.terminate(..io.joinpath(coreDir,"xray.exe")); 164 | fsys.copy(..io.appData("/xray/temp/xray.exe"),..io.joinpath(coreDir,"xray.exe")); 165 | 166 | return versionTag; 167 | } 168 | } 169 | } 170 | 171 | /**intellisense(xray.core) 172 | socksProxyPort = SOCKS代理端口 173 | httpProxPort = HTTP代理端口 174 | isInboundPortChanged() = 是否已变更代理端口配置 175 | restart(.(editor,outbound) = 重启启动 Xray 服务进程 176 | end intellisense**/ 177 | -------------------------------------------------------------------------------- /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={ 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 | }; 123 | method="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 | 148 | ..table.assign(current.outbounds[1],ob); 149 | } 150 | elseif(outbound.protocol=="vmess"){ 151 | if(!#outbound.security) outbound.security = "auto"; 152 | 153 | var ob = { 154 | mux = (outbound.concurrency !== null) ? { 155 | concurrency=outbound.concurrency; 156 | enabled=outbound.concurrency ? ( outbound.concurrency > 0) 157 | } : { 158 | concurrency=8; 159 | enabled=true 160 | }; 161 | protocol="vmess"; 162 | settings={ 163 | vnext={ 164 | { 165 | address=outbound.address; 166 | level=outbound.level : null; 167 | port=tonumber(outbound.port); 168 | users={ 169 | { 170 | alterId=tonumber(outbound.alterId) : 0; 171 | email=outbound.email : "t@t.tt"; 172 | id=outbound.id; 173 | security=outbound.security; 174 | } 175 | } 176 | } 177 | }; 178 | }; 179 | streamSettings={ 180 | network=outbound.network : "tcp"; 181 | security=#outbound.tls ? outbound.tls : null; 182 | tlsSettings = #outbound.tls ? { 183 | allowInsecure = !!outbound.allowInsecure; 184 | serverName = outbound.sni; 185 | } : null 186 | }; 187 | tag="proxy" 188 | }; 189 | 190 | if( outbound.network == "ws" ){ 191 | ob.streamSettings.wsSettings = { 192 | path = outbound.path; 193 | headers = outbound.headers; 194 | } 195 | 196 | if( outbound.host ) { 197 | if(!ob.streamSettings.wsSettings.headers){ 198 | ob.streamSettings.wsSettings.headers = {} 199 | } 200 | ob.streamSettings.wsSettings.headers.Host = outbound.host; 201 | } 202 | } 203 | elseif( (outbound.network == "http") || (outbound.network == "h2") ){ 204 | var host = outbound.host; 205 | if(type.isString(host)){host = {host}}; 206 | 207 | ob.streamSettings.httpSettings = { 208 | path = outbound.path; 209 | host = host; 210 | } 211 | } 212 | elseif( outbound.network == "tcp" ){ 213 | if( outbound.type == "http" ){ 214 | ob.streamSettings.tcpSettings = { 215 | header = { 216 | request={ 217 | headers={ 218 | Connection={ 219 | "keep-alive" 220 | }; 221 | Host=outbound.host; 222 | Pragma="no-cache"; 223 | ["Accept-Encoding"]={ 224 | "gzip, deflate" 225 | }; 226 | ["User-Agent"]={ 227 | "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"; 228 | "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" 229 | } 230 | }; 231 | method="GET"; 232 | path = outbound.path; 233 | version="1.1" 234 | }; 235 | type="http"; 236 | } 237 | } 238 | } 239 | } 240 | elseif( outbound.network == "kcp" ){ 241 | ob.streamSettings.kcpSettings = { 242 | congestion = outbound.congestion; 243 | seed = outbound.seed; 244 | mtu = outbound.mtu; 245 | tti = outbound.tti; 246 | uplinkCapacity = outbound.uplinkCapacity; 247 | downlinkCapacity = outbound.downlinkCapacity; 248 | readBufferSize = outbound.readBufferSize; 249 | writeBufferSize = outbound.writeBufferSize; 250 | header = { 251 | type = outbound.type; 252 | } 253 | } 254 | } 255 | 256 | ..table.assign(current.outbounds[1],ob); 257 | } 258 | elseif(outbound.protocol=="trojan" || outbound.protocol=="trojan-go"){ 259 | outbound.tls = #outbound.tls ? outbound.tls : "tls"; 260 | var ob = { 261 | mux = (outbound.concurrency !== null) ? { 262 | concurrency=outbound.concurrency; 263 | enabled=outbound.concurrency ? ( outbound.concurrency > 0) 264 | } : { 265 | concurrency=-1; 266 | enabled=false 267 | }; 268 | protocol="trojan"; 269 | settings={ 270 | servers={ 271 | { 272 | address=outbound.address; 273 | email=outbound.email; 274 | password=outbound.id; 275 | port=outbound.port; 276 | level=outbound.level : 0; 277 | encryption=outbound.security; 278 | flow=outbound.flow : (outbound.tls == "xtls" ? "xtls-rprx-direct" : null); 279 | } 280 | }; 281 | }; 282 | streamSettings={ 283 | network = outbound.network : "tcp"; 284 | security = #outbound.tls ? outbound.tls : "tls"; 285 | 286 | tlsSettings = (outbound.tls === "tls") ? { 287 | allowInsecure = !!outbound.allowInsecure; 288 | serverName = outbound.sni; 289 | } : null 290 | }; 291 | tag="proxy" 292 | }; 293 | 294 | if( outbound.network == "ws" ){ 295 | ob.streamSettings.wsSettings = { 296 | path = outbound.path; 297 | headers = outbound.headers; 298 | } 299 | 300 | if( outbound.host ) { 301 | if(!ob.streamSettings.wsSettings.headers){ 302 | ob.streamSettings.wsSettings.headers = {} 303 | } 304 | ob.streamSettings.wsSettings.headers.Host = outbound.host; 305 | } 306 | } 307 | ..table.assign(current.outbounds[1],ob); 308 | } 309 | elseif(outbound.protocol=="shadowsocks"){ 310 | var ob = { 311 | mux = (outbound.concurrency !== null) ? { 312 | concurrency=outbound.concurrency; 313 | enabled=outbound.concurrency ? ( outbound.concurrency > 0) 314 | } : { 315 | concurrency=-1; 316 | enabled=false 317 | }; 318 | protocol="shadowsocks"; 319 | settings={ 320 | servers={ 321 | { 322 | address=outbound.address; 323 | level=outbound.level : 0; 324 | method=outbound.security; 325 | ota=false; 326 | password=outbound.id; 327 | port=outbound.port; 328 | } 329 | }; 330 | }; 331 | streamSettings={ 332 | network = outbound.network : "tcp"; 333 | }; 334 | tag="proxy" 335 | }; 336 | 337 | ..table.assign(current.outbounds[1],ob); 338 | } 339 | 340 | ..string.save(jsonPath,..web.json.stringify(current) ); 341 | return jsonPath; 342 | } 343 | 344 | -------------------------------------------------------------------------------- /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.network = outbound.network; 57 | if(outbound.security!="none") info.security = outbound.security; 58 | if(outbound.tls) info.tls = outbound.tls; 59 | if(outbound.flow) info.flow = outbound.flow; 60 | if(outbound.allowInsecure!==null) 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 | 67 | if(..table.count(info)){ 68 | vless = ..inet.url.appendExtraInfo(vless,info) 69 | } 70 | 71 | if(outbound.ps){ 72 | vless = vless + "#" + ..inet.url.encode(outbound.ps); 73 | } 74 | ..table.push(outString,vless); 75 | } 76 | } 77 | 78 | return ..string.join(outString,'\r\n'); 79 | } 80 | 81 | var _subscriptionResponseCache = { 82 | @{_weak="kv"} 83 | } 84 | importFromString = function(str,subscribeUrl){ 85 | if(!str){ return; } 86 | str = ..string.trim(str); 87 | 88 | var jsonData; 89 | if(..string.match(str,"%\[\]") || ..string.match(str,"%\{\}")){ 90 | if(subscribeUrl)_subscriptionResponseCache[subscribeUrl] = ..string.crc32(str); 91 | jsonData = ..web.json.tryParse(str); 92 | } 93 | 94 | if( ..table.isArray(jsonData) ){ 95 | for(i=#jsonData;1;-1){ 96 | if( !validOutbound(jsonData[i]) ) { 97 | ..table.remove(jsonData,i); 98 | } 99 | 100 | if(subscribeUrl)jsonData[i].subscribeUrl = subscribeUrl; 101 | } 102 | 103 | return jsonData; 104 | } 105 | elseif( type(jsonData) == type.table ){ 106 | if( jsonData[["servers"]] ){ 107 | 108 | var servers = jsonData[["servers"]] 109 | var outbounds = ..table.array(); 110 | for i,v in ..table.eachIndex(servers){ 111 | var outbound = ..table.assign(,v); 112 | if(outbound.protocol === null){ 113 | outbound.protocol = (outbound.server_port&&outbound.password) ? "shadowsocks":"vless" 114 | } 115 | 116 | if(outbound.remarks){ 117 | outbound.ps = outbound.remarks; 118 | outbound.remarks = null; 119 | } 120 | 121 | if(outbound.server){ 122 | outbound.address = outbound.server; 123 | outbound.server = null; 124 | } 125 | 126 | if(outbound.server_port){ 127 | outbound.port = outbound.server_port; 128 | outbound.server_port = null; 129 | } 130 | 131 | if(outbound.password){ 132 | if(outbound.id){ 133 | outbound.serverId = outbound.id; 134 | } 135 | outbound.id = outbound.password; 136 | outbound.password = null; 137 | } 138 | 139 | if(outbound.method){ 140 | outbound.security = outbound.method; 141 | outbound.method = null; 142 | } 143 | 144 | if(subscribeUrl)outbound.subscribeUrl = subscribeUrl; 145 | ..table.push(outbounds,outbound); 146 | } 147 | 148 | return outbounds; 149 | } 150 | else { 151 | if( validOutbound(jsonData) ) { 152 | if(subscribeUrl)jsonData.subscribeUrl = subscribeUrl; 153 | return {jsonData}; 154 | } 155 | } 156 | } 157 | 158 | if( ..string.startWith(str,"http://") || ..string.startWith(str,"https://") ){ 159 | 160 | import win.dlg.message; 161 | var msgDlg = win.dlg.message(..mainForm); 162 | 163 | var title = '正在获取订阅源 ... '; 164 | var cfgSubscribeUrls = ..config.proxy.subscribeUrls; 165 | for(i=#cfgSubscribeUrls;1;-1){ 166 | var sub = cfgSubscribeUrls[i] 167 | if( (sub.url === str) ){ 168 | if(#sub.ps) title = '正在获取订阅源: ' + sub.ps; 169 | sub.checked = "启用"; 170 | ..config.proxy.save(); 171 | } 172 | } 173 | 174 | var form = msgDlg.create(title,,true) 175 | form.progress.startProgress(50); 176 | 177 | subscribeUrl = str; 178 | str = ..win.invoke( 179 | function(subscribeUrl){ 180 | import inet.http; 181 | import web.rest.github; 182 | if( string.indexOf(subscribeUrl,"github.com") ){ 183 | var ret = web.rest.github.getContent(subscribeUrl) 184 | if(ret) return ret; 185 | } 186 | 187 | return inet.http().get(subscribeUrl); 188 | },subscribeUrl 189 | ) 190 | 191 | form.close(); 192 | if(!str){ 193 | return; 194 | } 195 | ..publish("uiCommand.subscriptionNew",subscribeUrl); 196 | 197 | var jsonData; 198 | if(..string.match(str,"%\[\]") || ..string.match(str,"%\{\}")){ 199 | jsonData = ..web.json.tryParse(str); 200 | } 201 | 202 | if( type(jsonData) === type.table ){ 203 | return importFromString(str,subscribeUrl); 204 | } 205 | } 206 | 207 | if(subscribeUrl)_subscriptionResponseCache[subscribeUrl] = ..string.crc32(str); 208 | 209 | if(..string.match(str,"^[\w=+/-_]+$")){ 210 | str = ..crypt.decodeBin(str); 211 | if(!str) return; 212 | } 213 | 214 | var outbounds = ..table.array(); 215 | for(line in ..string.lines(str) ){ 216 | if(#outbounds>=300){ 217 | import win.dlg.message; 218 | var msgDlg = win.dlg.message(..mainForm); 219 | var form = msgDlg.warn("导入的节点数目过多,已导入300个节点并中止操作!") 220 | 221 | return outbounds; 222 | } 223 | 224 | if(line[1]=='/'# && line[2]=='/'#){ 225 | continue; 226 | } 227 | 228 | var vmess = ..string.match(line,`vmess\:\/\/([\w=+]+)`); 229 | if( vmess ) { 230 | var json = ..crypt.decodeBin(vmess); 231 | var outbound = ..web.json.tryParse(json); 232 | if(outbound){ 233 | ..table.clear(outbound@._defined); 234 | outbound.protocol = "vmess"; 235 | outbound.address = outbound.add; 236 | outbound.alterId = tonumber(outbound.aid); 237 | outbound.security = "auto"; 238 | outbound.network = outbound.net : "tcp"; 239 | outbound.net = null; 240 | outbound.add = null; 241 | outbound.aid = null; 242 | outbound.v = null; 243 | outbound.subscribeUrl = subscribeUrl; 244 | 245 | if(outbound.ps){ 246 | var u,h,p = ..string.match(outbound.ps,"(.+)\@(.+)\:(\d+)") 247 | if(u&&h&&p){ 248 | if(h && ..string.endWith(h,"jamjams.net") && subscribeUrl && ..string.match(subscribeUrl,"justmysocks\d+\.net")){ 249 | if(..string.match(outbound.address,"^\d+\.\d+\.\d+\.\d+$") ){ 250 | outbound.address = h; 251 | } 252 | } 253 | outbound.ps = h; 254 | } 255 | } 256 | ..table.push(outbounds,outbound); 257 | } 258 | 259 | continue; 260 | } 261 | 262 | var vless = ..string.match(line,`vless\:\/\/[^\@\s]+\@[^\?\#\s\:]+\:\d+\S*`); 263 | if( vless ) { 264 | var vlessUrl = ..inet.url.split(vless) 265 | if(vlessUrl.user && vlessUrl.host && vlessUrl.port){ 266 | var outbound = { 267 | address = vlessUrl.host; 268 | port = tonumber(vlessUrl.port) : 443; 269 | id = vlessUrl.user; 270 | network = "tcp"; 271 | protocol = "vless"; 272 | tls = "tls"; 273 | }; 274 | 275 | if(vlessUrl.extraInfo){ 276 | var info = ..inet.url.splitParameters(vlessUrl.extraInfo); 277 | outbound.sni = info.sni; 278 | outbound.host = info.host; 279 | outbound.path = info.path; 280 | outbound.network = info.network 281 | outbound.security = info.security; 282 | if(info.tls) outbound.tls = info.tls; 283 | if(info.flow) outbound.flow = info.flow; 284 | if(info.allowInsecure!==null) outbound.allowInsecure = info.allowInsecure; 285 | if(info.disableSessionResumption) outbound.disableSessionResumption = info.disableSessionResumption; 286 | if(info.alpn) outbound.alpn = info.alpn; 287 | } 288 | 289 | if(vlessUrl.location){ 290 | outbound.ps = ..inet.url.decode(vlessUrl.location); 291 | } 292 | 293 | outbound.subscribeUrl = subscribeUrl; 294 | ..table.push(outbounds,outbound); 295 | } 296 | 297 | continue; 298 | } 299 | 300 | var ss,comment = ..string.match(line,`ss\:\/\/([^\s\#]+)(<#\S+>?)`); 301 | if( ss ) { 302 | var userinfo,address,port = ..string.match(ss,"^([^:]+)\@(.+)\:(\d+)") 303 | if(!(userinfo&&address&&port)){ 304 | var str = ..crypt.decodeBin(ss); 305 | if(str){ ss = str; } 306 | 307 | method,password,address,port = ..string.match(ss,"^(.+)\:(.+)\@(.+)\:(\d+)") 308 | } 309 | else { 310 | userinfo = ..crypt.decodeBin(userinfo); 311 | if(userinfo){ 312 | method,password = ..string.match(userinfo,"^(.+)\:(.+)$") 313 | } 314 | } 315 | 316 | if(address && password && port){ 317 | var outbound = { 318 | address = address; 319 | port = tonumber(port); 320 | security = method; 321 | id = password; 322 | network = "tcp"; 323 | protocol = "shadowsocks" 324 | }; 325 | 326 | if(comment){ 327 | comment = ..string.right(comment,-2); 328 | comment = ..inet.url.decode(comment); 329 | var u,h,p = ..string.match(comment,"(.+)\@(.+)\:(\d+)") 330 | if(u&&h&&p){ 331 | if(h && ..string.endWith(h,"jamjams.net") && subscribeUrl && ..string.match(subscribeUrl,"justmysocks\d+\.net")){ 332 | if(..string.match(outbound.address,"^\d+\.\d+\.\d+\.\d+$") ){ 333 | outbound.address = h; 334 | } 335 | } 336 | 337 | comment = h; 338 | } 339 | 340 | outbound.ps = comment; 341 | } 342 | 343 | outbound.subscribeUrl = subscribeUrl; 344 | ..table.push(outbounds,outbound); 345 | } 346 | 347 | continue; 348 | } 349 | 350 | var trojan = ..string.match(line,`trojan\:\/\/[^\@\s]+\@[^\?\#\s\:]+\:\d+\S*`); 351 | if( trojan ) { 352 | var trojanUrl = ..inet.url.split(trojan) 353 | if(trojanUrl.user && trojanUrl.host && trojanUrl.port){ 354 | var outbound = { 355 | address = trojanUrl.host; 356 | port = tonumber(trojanUrl.port) : 443; 357 | id = trojanUrl.user; 358 | network = "tcp"; 359 | protocol = "trojan" 360 | }; 361 | 362 | if(trojanUrl.extraInfo){ 363 | var info = ..inet.url.splitParameters(trojanUrl.extraInfo); 364 | outbound.sni = info.sni || info.peer; 365 | 366 | outbound.host = info.host; 367 | outbound.path = info.path; 368 | 369 | if(info.type && info.type!=="original"){ 370 | outbound.network = info.type; 371 | if(outbound.network=="h2"){ 372 | outbound.network="http" 373 | } 374 | } 375 | 376 | outbound.security = info.encryption; 377 | } 378 | 379 | if(trojanUrl.location){ 380 | outbound.ps = ..inet.url.decode(trojanUrl.location); 381 | } 382 | 383 | outbound.subscribeUrl = subscribeUrl; 384 | ..table.push(outbounds,outbound); 385 | } 386 | 387 | continue; 388 | } 389 | 390 | var trojan = ..string.match(line,`trojan-go\:\/\/[^\@\s]+\@[^\?\#\s\:]+\:\d+\S*`); 391 | if( trojan ) { 392 | var trojanUrl = ..inet.url.split(trojan) 393 | if(trojanUrl.user && trojanUrl.host && trojanUrl.port){ 394 | var outbound = { 395 | address = trojanUrl.host; 396 | port = tonumber(trojanUrl.port) : 443; 397 | id = trojanUrl.user; 398 | network = "tcp"; 399 | protocol = "trojan-go" 400 | }; 401 | 402 | if(trojanUrl.extraInfo){ 403 | var info = ..inet.url.splitParameters(trojanUrl.extraInfo); 404 | outbound.sni = info.sni || info.peer; 405 | outbound.host = info.host; 406 | outbound.path = info.path; 407 | 408 | if(info.type && info.type!=="original"){ 409 | outbound.network = info.type; 410 | if(outbound.network=="h2"){ 411 | outbound.network="http" 412 | } 413 | } 414 | 415 | outbound.security = info.encryption; 416 | } 417 | 418 | if(trojanUrl.location){ 419 | outbound.ps = ..inet.url.decode(trojanUrl.location); 420 | } 421 | 422 | outbound.subscribeUrl = subscribeUrl; 423 | ..table.push(outbounds,outbound); 424 | } 425 | 426 | continue; 427 | } 428 | 429 | var json = ..string.match(line,`^\s*%\{\}\s*$`); 430 | if( json ) { 431 | var outbound = ..web.json.tryParse(json) 432 | if(type(outbound)=="table"){ 433 | if( validOutbound(outbound) ) ..table.push(outbounds,outbound ); 434 | continue; 435 | } 436 | } 437 | } 438 | 439 | return outbounds; 440 | } 441 | 442 | importFromClipboard = function(){ 443 | var str = ..win.clip.read(); 444 | if(!str){ return; } 445 | 446 | str = ..string.trim(str,'"\'\s\t\r\n '); 447 | return importFromString(str); 448 | } 449 | 450 | validAll = function(outbounds){ 451 | for(i=#outbounds;1;-1){ 452 | if(!validOutbound(outbounds[i])){ 453 | ..table.remove(outbounds,i); 454 | } 455 | } 456 | 457 | return outbounds; 458 | } 459 | 460 | validOutbound = function(outbound){ 461 | if(type(outbound)!="table"){ return; } 462 | 463 | if(outbound.aid){ 464 | outbound.alterId = outbound.aid; 465 | outbound.aid = null; 466 | } 467 | 468 | if(outbound.add){ 469 | outbound.address = outbound.add; 470 | outbound.add = null; 471 | ..table.define(outbound); 472 | } 473 | 474 | if(outbound.net){ 475 | outbound.network = outbound.net; 476 | outbound.net = null; 477 | ..table.define(outbound); 478 | } 479 | 480 | if(outbound.peer && #outbound.peer){ 481 | outbound.sni = outbound.peer; 482 | outbound.peer = null; 483 | ..table.define(outbound); 484 | } 485 | 486 | if(!outbound.security){ 487 | if(outbound.method && outbound.method!="none"){ 488 | outbound.security = outbound.method; 489 | outbound.method = null; 490 | ..table.define(outbound); 491 | } 492 | } 493 | 494 | if(!outbound.address){ 495 | if(outbound.server){ 496 | outbound.address = outbound.server; 497 | outbound.server = null; 498 | ..table.define(outbound); 499 | } 500 | } 501 | 502 | if(!outbound.ps){ 503 | if(outbound.remarks){ 504 | outbound.ps = outbound.remarks; 505 | outbound.remarks = null; 506 | ..table.define(outbound); 507 | } 508 | } 509 | 510 | if(!outbound.protocol){ 511 | outbound.protocol = "vmess"; 512 | if(outbound.ps && ..string.find(outbound.ps,"@@vless")){ 513 | outbound.protocol = "vless"; 514 | } 515 | } 516 | 517 | if(!outbound.port){ 518 | outbound.port=443; 519 | } 520 | 521 | outbound.v = null; 522 | 523 | if(outbound.address){ 524 | return outbound; 525 | } 526 | } 527 | 528 | 529 | updateSubscription = function(bySetting){ 530 | 531 | var outbondSubscribeUrlMap = {} 532 | var cfg = ..config.proxy.outbounds : ..table.array(); 533 | for(i=#cfg;1;-1){ 534 | var outbound = cfg[i] 535 | if(outbound.subscribeUrl){ 536 | outbondSubscribeUrlMap[outbound.subscribeUrl] = true; 537 | } 538 | } 539 | 540 | var cfgSubscribeUrls = ..config.proxy.subscribeUrls; 541 | for(i=#cfgSubscribeUrls;1;-1){ 542 | var sub = cfgSubscribeUrls[i] 543 | if(!#sub.url){ 544 | continue; 545 | } 546 | 547 | if(sub.checked === "启用") { 548 | outbondSubscribeUrlMap[sub.url] = true; 549 | } 550 | elseif(sub.checked === "禁用") { 551 | outbondSubscribeUrlMap[sub.url] = null; 552 | } 553 | } 554 | 555 | if(!..table.count(outbondSubscribeUrlMap)){ 556 | if(bySetting){ return; } 557 | 558 | import win.clip; 559 | var clibStr = win.clip.read(); 560 | if( ..string.startWith(clibStr,"http://") || ..string.startWith(clibStr,"https://") ){ 561 | outbondSubscribeUrlMap[clibStr] = true; 562 | } 563 | else { 564 | ..mainForm.msgWarn('当前配置不包含来自订阅源的服务器,\n请先复制订阅源网址到剪贴板!'); 565 | return; 566 | } 567 | } 568 | 569 | var count = 0; 570 | for(url,v in outbondSubscribeUrlMap){ 571 | var outbounds = ..xray.outbounds.importFromString(url); 572 | if(#outbounds){ 573 | for(i=#cfg;1;-1){ 574 | var outbound = cfg[i] 575 | if(outbound.subscribeUrl==url){ 576 | ..table.remove( cfg,i ); 577 | } 578 | } 579 | 580 | count = count + #outbounds; 581 | ..table.append(cfg,outbounds); 582 | } 583 | } 584 | 585 | ..config.proxy.save(); 586 | if(count){ 587 | ..publish("uiCommand.restartXrayCore",cfg) 588 | } 589 | ..mainForm.msgOk("已成功刷新" + count + "个服务器。",1200); 590 | return count; 591 | } 592 | 593 | updateSubscription = function(bySetting){ 594 | 595 | var outbondSubscribeUrlMap = {} 596 | 597 | var cfgSubscribeUrls = ..config.proxy.subscribeUrls; 598 | for(i=#cfgSubscribeUrls;1;-1){ 599 | var sub = cfgSubscribeUrls[i] 600 | if(sub.checked === "启用") { 601 | outbondSubscribeUrlMap[sub.url] = true; 602 | } 603 | elseif(sub.checked === "禁用") { 604 | outbondSubscribeUrlMap[sub.url] = null; 605 | } 606 | } 607 | 608 | var cfg = ..config.proxy.outbounds : ..table.array(); 609 | for(i=#cfg;1;-1){ 610 | var outbound = cfg[i] 611 | if(outbound.subscribeUrl){ 612 | outbondSubscribeUrlMap[outbound.subscribeUrl] = true; 613 | } 614 | } 615 | 616 | if(!..table.count(outbondSubscribeUrlMap)){ 617 | if(bySetting){ return; } 618 | 619 | import win.clip; 620 | var clibStr = win.clip.read(); 621 | if( ..string.startWith(clibStr,"http://") || ..string.startWith(clibStr,"https://") ){ 622 | outbondSubscribeUrlMap[clibStr] = true; 623 | } 624 | else { 625 | ..mainForm.msgWarn('当前配置不包含来自订阅源的服务器,\n请先复制订阅源网址到剪贴板!'); 626 | return; 627 | } 628 | } 629 | 630 | var count = 0; 631 | for(url,v in outbondSubscribeUrlMap){ 632 | var outbounds = ..xray.outbounds.importFromString(url); 633 | if(#outbounds){ 634 | for(i=#cfg;1;-1){ 635 | var outbound = cfg[i] 636 | if(outbound.subscribeUrl==url){ 637 | ..table.remove( cfg,i ); 638 | } 639 | } 640 | 641 | count = count + #outbounds; 642 | ..table.append(cfg,outbounds); 643 | } 644 | } 645 | 646 | ..config.proxy.save(); 647 | ..publish("uiCommand.restartXrayCore",cfg) 648 | ..mainForm.msgOk("已成功刷新" + count + "个服务器。",1200); 649 | return count; 650 | } 651 | 652 | autoUpdateSubscription = function(){ 653 | 654 | var outbondSubscribeUrlMap = {} 655 | var cfgSubscribeUrls = ..config.proxy.subscribeUrls; 656 | for(i=#cfgSubscribeUrls;1;-1){ 657 | var sub = cfgSubscribeUrls[i] 658 | if(!#sub.url){ 659 | continue; 660 | } 661 | 662 | if(sub.checked === "启用") { 663 | outbondSubscribeUrlMap[sub.url] = true; 664 | } 665 | } 666 | 667 | if(!..table.count(outbondSubscribeUrlMap)){ 668 | return; 669 | } 670 | 671 | ..thread.invoke( 672 | function(winform,outbondSubscribeUrlMap,responseCache){ 673 | import inet.http; 674 | var http = inet.http(); 675 | 676 | var updated = {} 677 | for(url,v in outbondSubscribeUrlMap){ 678 | outbondSubscribeUrlMap[url] = http.get(url); 679 | if(!outbondSubscribeUrlMap[url]){ 680 | continue; 681 | } 682 | 683 | if(responseCache){ 684 | if(responseCache[url] && responseCache[url]===..string.crc32(outbondSubscribeUrlMap[url])){ 685 | outbondSubscribeUrlMap[url] = null; 686 | } 687 | } 688 | } 689 | 690 | winform.publish("thread.subscriptionUpdated",outbondSubscribeUrlMap) 691 | },..mainForm,outbondSubscribeUrlMap,_subscriptionResponseCache 692 | ) 693 | } 694 | 695 | ..subscribe("thread.subscriptionUpdated",function(outbondSubscribeUrlMap){ 696 | var cfg = ..config.proxy.outbounds : ..table.array(); 697 | 698 | var count = 0; 699 | for(url,data in outbondSubscribeUrlMap){ 700 | 701 | var outbounds = ..xray.outbounds.importFromString(data,url); 702 | if(#outbounds){ 703 | for(i=#cfg;1;-1){ 704 | var outbound = cfg[i] 705 | if(outbound.subscribeUrl==url){ 706 | ..table.remove( cfg,i ); 707 | } 708 | } 709 | 710 | ..table.append(cfg,outbounds); 711 | count = count + #outbounds; 712 | } 713 | } 714 | if(!count){ 715 | return; 716 | } 717 | 718 | ..config.proxy.outbounds = cfg; 719 | ..config.proxy.save(); 720 | 721 | ..publish("uiCommand.restartXrayCore") 722 | ..publish("uiCommand.print","已自动刷新" + count + "个订阅服务器。") 723 | } ) 724 | 725 | /**intellisense(xray.outbounds) 726 | exportSharedLinks(.(outbounds) = 导出分享链接 727 | importFromString(.(str,subscribeUrl) = 导入分享链接 728 | importFromClipboard() = 自剪贴板导入分享链接 729 | autoUpdateSubscription() = 自动更新订阅源 730 | end intellisense**/ 731 | -------------------------------------------------------------------------------- /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 | 9 | $serverMain = ..wsock.tcp.simpleHttpServer.mainThread( 10 | function(response,request,session){ 11 | 12 | if(request.path=="/proxy.pac"){ 13 | response.contentType = "application/x-ns-proxy-autoconfig"; 14 | 15 | var pacData = ..string.load(pacPath); 16 | var proxPorts = inboundPort.get(); 17 | if(!proxPorts[["socksProxy"]]){ 18 | response.errorStatus(404); 19 | return; 20 | } 21 | 22 | var reps = ..string.replace(pacData,"var\s+proxy\s+=\s+%''","var proxy = 'SOCKS "+ request.environ.SERVER_NAME +":"+proxPorts.socksProxy +"'"); 23 | if(useHttpProxy.get()){ 24 | reps = ..string.replace(pacData,"var\s+proxy\s+=\s+%''","var proxy = 'PROXY "+ request.environ.SERVER_NAME +":"+proxPorts.httpProxy +"'"); 25 | } 26 | 27 | response.write(reps); 28 | } 29 | } 30 | ); 31 | $serverMain.threadNum = 50; 32 | 33 | var pacPath = ..io.appData("/winXray/pac.txt"); 34 | resetPac = function(){ 35 | ..string.save(pacPath,$"/pac.txt" ) 36 | } 37 | 38 | if( ! ..io.exist(pacPath) ){ 39 | resetPac(); 40 | } 41 | 42 | loadPacText = function(){ 43 | return ..io.exist(pacPath) ? ..string.load(pacPath) || $"/pac.txt"; 44 | } 45 | 46 | savePacText = function(json){ 47 | var str = ..string.replace($"/pac.txt","var\s+rules\s*=\s*%\[\]","var rules = " + json); 48 | ..string.save(pacPath,str ); 49 | } 50 | 51 | var inboundPort = ..thread.var(); 52 | var useHttpProxy = ..thread.var(); 53 | restart = function(){ 54 | useHttpProxy.set(..config.proxy.useHttpGlobal); 55 | 56 | $serverMain.threadGlobal = { 57 | inboundPort = inboundPort; 58 | pacPath = pacPath; 59 | useHttpProxy = useHttpProxy; 60 | } 61 | 62 | var pacPort = ..config.proxy.pacPort; 63 | if( (! pacPort) || pacPort >= 49152 ){ pacPort = ..wsock.tcp.server.getFreePort("127.0.0.1",1083,10803,10813,44823) } 64 | 65 | var currentIp,currentPort = $serverMain.getLocalIp(); 66 | if(currentIp!==..config.core.default.inbounds[[1]][["listen"]] 67 | || currentPort != pacPort ){ 68 | $serverMain.stop(); 69 | 70 | var ok,err = $serverMain.start( 71 | ..config.core.default.inbounds[[1]][["listen"]] : "127.0.0.1",pacPort ); 72 | if(!ok){ 73 | ..publish("uiCommand.print","启动PAC服务器失败",err) 74 | return false,err; 75 | } 76 | 77 | ..publish("uiCommand.print","PAC服务器已启动",getUrl()) 78 | ..publish("pacServer.restarted",pacPort); 79 | } 80 | 81 | return true; 82 | } 83 | 84 | stop = function(){ 85 | if($serverMain){ 86 | $serverMain.stop(); 87 | } 88 | } 89 | 90 | updateUseHttpProxy = function(httpProxy){ 91 | useHttpProxy.set(httpProxy); 92 | } 93 | 94 | updateConfig = function(socksProxyPort,httpProxyPort){ 95 | if(socksProxyPort && httpProxyPort){ 96 | inboundPort.set( {socksProxy=socksProxyPort;httpProxy=httpProxyPort} ); 97 | } 98 | } 99 | 100 | ..subscribe("xrayCore.restarted",function(socksProxyPort,httpProxyPort){ 101 | updateConfig(socksProxyPort,httpProxyPort); 102 | } ) 103 | 104 | getUrl = function(localIp){ 105 | var url = $serverMain.getUrl("/proxy.pac?" + ..time.tick() ) 106 | if(!url){ return } 107 | 108 | var tUrl = ..inet.url.split(url); 109 | if(!localIp){ 110 | tUrl.host = "127.0.0.1" 111 | } 112 | else { 113 | tUrl.host = ..wsock.tcp.client.getLocalIp("www.baidu.com"); 114 | } 115 | 116 | return tostring(tUrl); 117 | } 118 | 119 | getPort = function(){ 120 | var ip,port = $serverMain.getLocalIp(); 121 | return port; 122 | } 123 | 124 | 125 | /**intellisense(xray.pacServer) 126 | stop() = 停止PAC服务器 127 | restart() = 重启PAC服务器 128 | getUrl(.(localIp) = 返回PAC服务地址 129 | getPort() = 返回PAC端口 130 | updateConfig(.(socksProxyPort,httpProxPort) = 更新代理端口或PAC数据 131 | resetPac() = 重置为默认PAC 132 | end intellisense**/ 133 | -------------------------------------------------------------------------------- /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 + ` "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 1.7.1";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 | mainForm.popmenu.add(); 290 | mainForm.popmenu.add('退出',function(id){ 291 | mainForm.onClose = null; 292 | mainForm.close() 293 | }); 294 | 295 | var pt = ::POINT(); 296 | ::User32.GetCursorPos(pt); 297 | mainForm.popmenu.popup(pt.x,pt.y,true) 298 | } 299 | elseif( lParam = 0x203/*_WM_LBUTTONDBLCLK*/) { 300 | if(win.isIconic(mainForm.hwnd)){ 301 | mainForm.show(9/*_SW_RESTORE*/); 302 | } 303 | else { 304 | mainForm.show(); 305 | } 306 | 307 | win.setForeground(mainForm.hwnd); 308 | } 309 | } 310 | } 311 | 312 | mainForm.enableDpiScaling(); 313 | 314 | mainForm.onDestroy = function(){ 315 | mainForm.tray.delete(); 316 | } 317 | 318 | import win.util.tray; 319 | mainForm.onClose = function(hwnd,message,wParam,lParam){ 320 | if(!mainForm.tray){ 321 | mainForm.tray = win.util.tray(mainForm); 322 | } 323 | else { 324 | mainForm.tray.reset(); 325 | } 326 | 327 | mainForm.show(false); 328 | return true; 329 | } 330 | 331 | mainForm.tray = win.util.tray(mainForm) 332 | if(!_ARGV.tray){ 333 | mainForm.show(); 334 | } 335 | 336 | return win.loopMessage(); 337 | -------------------------------------------------------------------------------- /release/update/checksum.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/update/checksum.lzma -------------------------------------------------------------------------------- /release/update/files/winXray.exe.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/update/files/winXray.exe.lzma -------------------------------------------------------------------------------- /release/update/files/xray-core/LICENSE.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/update/files/xray-core/LICENSE.lzma -------------------------------------------------------------------------------- /release/update/files/xray-core/winXray-default-servers.json.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/update/files/xray-core/winXray-default-servers.json.lzma -------------------------------------------------------------------------------- /release/update/files/xray-core/xray.exe.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/update/files/xray-core/xray.exe.lzma -------------------------------------------------------------------------------- /release/update/version.txt: -------------------------------------------------------------------------------- 1 | { 2 | "description":"发布 v1.7.1,细节优化,支持直接使用 github.com 项目文件地址作为订阅源,兼容raw,blob格式,示例 https://github.com/winXray/winXray/blob/master/sub/sample.json ", 3 | "format":".lzma", 4 | "main":"\\winXray.exe", 5 | "updater":"\\winXray.exe", 6 | "url":"https://raw.githubusercontent.com/winXray/winXray/master/release/update/", 7 | "version":"1.7.01" 8 | } -------------------------------------------------------------------------------- /release/update32/checksum.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/update32/checksum.lzma -------------------------------------------------------------------------------- /release/update32/files/winXray.exe.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/update32/files/winXray.exe.lzma -------------------------------------------------------------------------------- /release/update32/files/xray-core/LICENSE.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/update32/files/xray-core/LICENSE.lzma -------------------------------------------------------------------------------- /release/update32/files/xray-core/winXray-default-servers.json.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/update32/files/xray-core/winXray-default-servers.json.lzma -------------------------------------------------------------------------------- /release/update32/files/xray-core/xray.exe.lzma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/update32/files/xray-core/xray.exe.lzma -------------------------------------------------------------------------------- /release/update32/version.txt: -------------------------------------------------------------------------------- 1 | { 2 | "description":"发布 v1.7.1,细节优化,支持直接使用 github.com 项目文件地址作为订阅源,兼容raw,blob格式,示例 https://github.com/winXray/winXray/blob/master/sub/sample.json ", 3 | "format":".lzma", 4 | "main":"\\winXray.exe", 5 | "updater":"\\winXray.exe", 6 | "url":"https://raw.githubusercontent.com/winXray/winXray/master/release/update32/", 7 | "version":"1.7.01" 8 | } -------------------------------------------------------------------------------- /release/winXray.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/winXray.7z -------------------------------------------------------------------------------- /release/winXray32.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/release/winXray32.7z -------------------------------------------------------------------------------- /screenshots/config.advanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/screenshots/config.advanced.png -------------------------------------------------------------------------------- /screenshots/config.json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/screenshots/config.json.png -------------------------------------------------------------------------------- /screenshots/pac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/screenshots/pac.png -------------------------------------------------------------------------------- /screenshots/telegram.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/screenshots/telegram.gif -------------------------------------------------------------------------------- /screenshots/winXray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/screenshots/winXray.png -------------------------------------------------------------------------------- /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/pojiezhiyuanjun/winXray/5dba4a215600e0beccb0ae3b337d8df625d74218/xray-core/xray.exe --------------------------------------------------------------------------------