aardio 文档
aardio 范例: WebView2 模拟手机设备 + 动态设置 SOCKS5 代理服务器
//WebView2 模拟手机设备 + 动态设置 SOCKS5 代理服务器
import fonts.fontAwesome;
import web.view;
import win.ui;
/*DSG{{*/
var winform = win.form(text="SOCKS5 浏览器";right=959;bottom=640;bgcolor=0xFFFFFF)
winform.add(
btnGo={cls="plus";left=895;top=7;right=950;bottom=33;color=0x3C3C3C;disabled=1;dr=1;dt=1;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-15;name='FontAwesome')};iconText='\uF1D8';notify=1;z=4};
btnRefresh={cls="plus";left=241;top=7;right=271;bottom=33;color=0x3C3C3C;dl=1;dt=1;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-13;name='FontAwesome')};iconText='\uF021';notify=1;z=6};
comboProxy={cls="combobox";left=50;top=9;right=240;bottom=33;dl=1;dt=1;edge=1;items={};mode="dropdown";z=2};
custom={cls="custom";left=0;top=40;right=942;bottom=640;db=1;dl=1;dr=1;dt=1;z=1};
editUrl={cls="plus";left=489;top=7;right=893;bottom=33;align="right";border={bottom=1;color=0x969696};disabled=1;dr=1;dt=1;editable="edit";font=LOGFONT(h=-13);notify=1;textPadding={left=5;top=3;bottom=3};z=3};
labelProxy={cls="static";text="代理:";left=8;top=12;right=48;bottom=28;dl=1;dt=1;transparent=1;z=5};
labelStatus={cls="static";text="正在搜索代理...";left=277;top=12;right=441;bottom=28;dl=1;dt=1;transparent=1;z=7}
)
/*}}*/
// 创建 SOCKS5 中转代理服务器
import wsock.tcp.socks5Server;
var socks5Server = wsock.tcp.socks5Server.async("127.0.0.1",0);
socks5Server.start();
// 创建 WebView2 控件,模拟手机浏览器
var wb = web.view(winform.custom,{
startArguments = {
acceptLang = "zh-CN";
userAgent = "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166";
proxyServer = socks5Server.getUrl();//使用 SOCKS5 代理
};
userDataDir = _STUDIO_INVOKED ? io.appData("/aardio/webview2/user-data/ide/socks5-browser")
})
// 启用 CDP 的 Emulation 域,模拟手机设备
wb.cdp("Emulation.enable");
wb.cdp("Emulation.setDeviceMetricsOverride", {
mobile = true;
});
// 设置移动设备 HTTP 请求头
wb.cdp("Network.enable");
wb.cdp("Network.setExtraHTTPHeaders", {
headers = {
["Sec-CH-UA-Mobile"] = "?1";
["Sec-CH-UA-Platform"] = '"Android"';
}
});
wb.onDocumentComplete = function(url){
if(!string.find(url,"^<chrome\-error>|<about>\:")){
winform.editUrl.text = url;
}
}
wb.html = $"/socks5.html";
// 应用代理设置
var applyProxy = function(proxy){
if(#proxy){
socks5Server.setUpstreamProxy(proxy);
winform.text = "SOCKS5 浏览器 - 已切换到代理: " + proxy;
}
}
// 代理下拉框选择事件
winform.comboProxy.onSelChange = function(){
applyProxy(winform.comboProxy.selText);
}
import fsys.table;
var cfgTable = fsys.table(io.appData("/aardio/std/tools/socks5.webview.table"));
if(cfgTable.url){
winform.editUrl.text = cfgTable.url;
}
else{
winform.editUrl.text = "github.com"
}
// 打开网页
winform.btnGo.oncommand = function(){
var proxy = winform.comboProxy.text;
if(#proxy){
applyProxy(winform.comboProxy.text);
winform.text = "SOCKS5 浏览器 - 已切换到代理: " + proxy;
}
var url = winform.editUrl.text;
if(#url){
cfgTable.url = url;
cfgTable.save();
// 如果没有协议前缀,自动添加 https://
if(!string.find(url,"^<<@@https@>|<@@http@>>?\://")){
url = "https://" + url;
}
wb.html = "<body style='text-align: right;'>正在打开:"+url+" <progress></progress> </body>"
wb.go(url);
}
else{
winform.editUrl.editBox.showErrorTip("请输入网址");
}
}
// 地址栏回车事件
winform.editUrl.editBox.onOk = function(ctrl,alt,shift){
winform.btnGo.oncommand();
return false;
}
// 线程通信
import thread.command;
var threadCommand = thread.command();
// 记录已找到的代理数量
var proxyFoundCount = 0;
// 启用网址输入框和打开按钮
var enableUrlInput = function(){
winform.editUrl.disabled = false;
winform.btnGo.disabled = false;
winform.editUrl.setFocus();
}
// 接收代理服务器
threadCommand.$onProxyFound = function(proxyStr){
winform.comboProxy.add(proxyStr);
proxyFoundCount++;
// 第一个代理自动选中并应用,同时启用网址输入
if(proxyFoundCount == 1){
winform.comboProxy.selIndex = 1;
applyProxy(winform.comboProxy.selText);
enableUrlInput();
}
winform.labelStatus.text = "已找到 " + proxyFoundCount + " 个代理";
}
// 搜索完成
threadCommand.$onSearchComplete = function(total){
if(total == 0){
winform.labelStatus.text = "未找到可用代理";
}
else {
winform.labelStatus.text = "共 " + total + " 个可用代理";
}
winform.btnRefresh.disabledText = null;
winform.comboProxy.disabled = false;
}
// 启动代理搜索线程
var startProxySearch = function(){
proxyFoundCount = 0;
winform.comboProxy.clear();
winform.labelStatus.text = "正在搜索代理...";
winform.btnRefresh.disabledText = ['\uF254','\uF251','\uF252','\uF253','\uF250']
winform.comboProxy.disabled = true;
// 搜索时禁用网址输入
winform.editUrl.disabled = true;
winform.btnGo.disabled = true;
thread.invoke(
function(threadCommand){
// 获取前 10 个可用代理
var foundCount = 0;
var maxCount = 10;
var knownSocks5Ports = function (p) {
if( ({51837=1,40000=1,9050=1,9150=1,10085=1,2334=1,6153=1,20170=1,2801=1})[p]) return 1;
if( p < 1080 || p >= 49152 ) return false;
return (
(p >= 1080 && p < 1090)
|| (p >= 7890 && p < 7900)
|| (p >= 10800 && p < 10810)
|| (p >= 2080 && p <= 2081)
|| (p >= 8888 && p <= 8890)
);
}
import inet.stat;
var stat = inet.stat();
var listeningPorts = table.filter(
stat.tcp,
lambda(conn)
conn.state == "LISTENING"
&& knownSocks5Ports(conn.localPort)
&& (conn.local == "0.0.0.0" || conn.local = "127.0.0.1" )
);
listeningPorts = table.unique(table.map(listeningPorts,lambda(v) v.localPort));
table.sort(listeningPorts);
var localSock5Proxy;
import wsock.tcp.socks5Client;
for i, port in listeningPorts {
if( wsock.tcp.socks5Client.test("127.0.0.1",port)){
localSock5Proxy = "SOCKS5://127.0.0.1:"+port;
break
}
}
if(localSock5Proxy){
foundCount++;
threadCommand.$onProxyFound(localSock5Proxy);
}
import web.rest.github;
import thread.works;
// 创建 250 个检测线程
var works = thread.works(250,
function(proxy){
import wsock.tcp.socks5Client;
// 检测 SOCKS5 可用性,参数 3 必须指定域名
if(wsock.tcp.socks5Client.testHttp(proxy.ip, proxy.port, "duck.ai")){
return proxy;
}
}
);
// 从网络获取 SOCKS5 服务器列表
var proxies = web.rest.github.getJson("proxifly","free-proxy-list","proxies/protocols/socks5/data.json");
if(proxies){
works.pushAll(proxies);
var timeout = 30000; // 30 秒超时
var startTime = time.tick();
while(foundCount < maxCount){
var result = works.pop(1, true);
if(result){
var proxyStr = result.proxy;
threadCommand.$onProxyFound(proxyStr);
foundCount++;
}
// 超时检查
if(time.tick() - startTime > timeout){
break;
}
}
threadCommand.$onSearchComplete(foundCount);
works.quit(10);
}
else {
threadCommand.$onSearchComplete(foundCount);
}
}, threadCommand
)
}
// 刷新按钮点击事件
winform.btnRefresh.oncommand = function(){
startProxySearch();
}
// 启动时自动搜索代理
startProxySearch();
winform.editUrl.editBox.onFocusGot = function(){
owner.selectAll()
}
winform.btnGo.skin({
color={
active=0xFF00FF00;
default=0xFF3C3C3C;
disabled=0xFF6D6D6D;
hover=0xFF0078D4
}
})
winform.btnRefresh.skin({
color={
active=0xFF00FF00;
default=0xFF3C3C3C;
disabled=0xFF6D6D6D;
hover=0xFF0078D4
}
})
winform.show(3/*_SW_MAXIMIZE*/);
win.loopMessage();
Markdown 格式