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