mirror of
https://github.com/MatsuriDayo/nekoray.git
synced 2025-12-18 21:14:37 +03:00
feat: full test for nekobox
This commit is contained in:
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/matsuridayo/libneko/neko_common"
|
"github.com/matsuridayo/libneko/neko_common"
|
||||||
@@ -18,7 +19,32 @@ func setupCore() {
|
|||||||
boxmain.DisableColor()
|
boxmain.DisableColor()
|
||||||
//
|
//
|
||||||
neko_log.SetupLog(50*1024, "./neko.log")
|
neko_log.SetupLog(50*1024, "./neko.log")
|
||||||
neko_common.GetProxyHttpClient = func() *http.Client {
|
//
|
||||||
|
neko_common.GetCurrentInstance = func() interface{} {
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
neko_common.DialContext = func(ctx context.Context, specifiedInstance interface{}, network, addr string) (net.Conn, error) {
|
||||||
|
if i, ok := specifiedInstance.(*boxbox.Box); ok {
|
||||||
|
return boxapi.DialContext(ctx, i, network, addr)
|
||||||
|
}
|
||||||
|
if instance != nil {
|
||||||
|
return boxapi.DialContext(ctx, instance, network, addr)
|
||||||
|
}
|
||||||
|
return neko_common.DialContextSystem(ctx, network, addr)
|
||||||
|
}
|
||||||
|
neko_common.DialUDP = func(ctx context.Context, specifiedInstance interface{}) (net.PacketConn, error) {
|
||||||
|
if i, ok := specifiedInstance.(*boxbox.Box); ok {
|
||||||
|
return boxapi.DialUDP(ctx, i)
|
||||||
|
}
|
||||||
|
if instance != nil {
|
||||||
|
return boxapi.DialUDP(ctx, instance)
|
||||||
|
}
|
||||||
|
return neko_common.DialUDPSystem(ctx)
|
||||||
|
}
|
||||||
|
neko_common.CreateProxyHttpClient = func(specifiedInstance interface{}) *http.Client {
|
||||||
|
if i, ok := specifiedInstance.(*boxbox.Box); ok {
|
||||||
|
return boxapi.CreateProxyHttpClient(i)
|
||||||
|
}
|
||||||
return boxapi.CreateProxyHttpClient(instance)
|
return boxapi.CreateProxyHttpClient(instance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,11 +95,13 @@ func (s *server) Test(ctx context.Context, in *gen.TestReq) (out *gen.TestResp,
|
|||||||
|
|
||||||
if in.Mode == gen.TestMode_UrlTest {
|
if in.Mode == gen.TestMode_UrlTest {
|
||||||
var i *boxbox.Box
|
var i *boxbox.Box
|
||||||
|
var cancel context.CancelFunc
|
||||||
if in.Config != nil {
|
if in.Config != nil {
|
||||||
// Test instance
|
// Test instance
|
||||||
i, instance_cancel, err = boxmain.Create([]byte(in.Config.CoreConfig))
|
i, cancel, err = boxmain.Create([]byte(in.Config.CoreConfig))
|
||||||
if instance_cancel != nil {
|
if i != nil {
|
||||||
defer instance_cancel()
|
defer i.Close()
|
||||||
|
defer cancel()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -115,8 +117,16 @@ func (s *server) Test(ctx context.Context, in *gen.TestReq) (out *gen.TestResp,
|
|||||||
out.Ms, err = speedtest.UrlTest(boxapi.CreateProxyHttpClient(i), in.Url, in.Timeout)
|
out.Ms, err = speedtest.UrlTest(boxapi.CreateProxyHttpClient(i), in.Url, in.Timeout)
|
||||||
} else if in.Mode == gen.TestMode_TcpPing {
|
} else if in.Mode == gen.TestMode_TcpPing {
|
||||||
out.Ms, err = speedtest.TcpPing(in.Address, in.Timeout)
|
out.Ms, err = speedtest.TcpPing(in.Address, in.Timeout)
|
||||||
} else {
|
} else if in.Mode == gen.TestMode_FullTest {
|
||||||
err = fmt.Errorf("not available")
|
i, cancel, err := boxmain.Create([]byte(in.Config.CoreConfig))
|
||||||
|
if i != nil {
|
||||||
|
defer i.Close()
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return grpc_server.DoFullTest(ctx, in, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -69,29 +69,51 @@ func setupCore() {
|
|||||||
return resolver_go.LookupIP(context.Background(), network, host)
|
return resolver_go.LookupIP(context.Background(), network, host)
|
||||||
})
|
})
|
||||||
//
|
//
|
||||||
neko_common.GetProxyHttpClient = func() *http.Client {
|
neko_common.GetCurrentInstance = func() interface{} {
|
||||||
return getProxyHttpClient(instance)
|
return instance
|
||||||
|
}
|
||||||
|
neko_common.DialContext = func(ctx context.Context, specifiedInstance interface{}, network, addr string) (net.Conn, error) {
|
||||||
|
dest, err := v2rayNet.ParseDestination(fmt.Sprintf("%s:%s", network, addr))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if i, ok := specifiedInstance.(*NekoV2RayInstance); ok {
|
||||||
|
return core.Dial(ctx, i.Instance, dest)
|
||||||
|
}
|
||||||
|
if instance != nil {
|
||||||
|
return core.Dial(ctx, instance.Instance, dest)
|
||||||
|
}
|
||||||
|
return neko_common.DialContextSystem(ctx, network, addr)
|
||||||
|
}
|
||||||
|
neko_common.DialUDP = func(ctx context.Context, specifiedInstance interface{}) (net.PacketConn, error) {
|
||||||
|
if i, ok := specifiedInstance.(*NekoV2RayInstance); ok {
|
||||||
|
return core.DialUDP(ctx, i.Instance)
|
||||||
|
}
|
||||||
|
if instance != nil {
|
||||||
|
return core.DialUDP(ctx, instance.Instance)
|
||||||
|
}
|
||||||
|
return neko_common.DialUDPSystem(ctx)
|
||||||
|
}
|
||||||
|
neko_common.CreateProxyHttpClient = func(specifiedInstance interface{}) *http.Client {
|
||||||
|
if i, ok := specifiedInstance.(*NekoV2RayInstance); ok {
|
||||||
|
return createProxyHttpClient(i)
|
||||||
|
}
|
||||||
|
return createProxyHttpClient(instance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PROXY
|
// PROXY
|
||||||
|
|
||||||
func getProxyHttpClient(_instance *NekoV2RayInstance) *http.Client {
|
func createProxyHttpClient(i *NekoV2RayInstance) *http.Client {
|
||||||
dailContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
dest, err := v2rayNet.ParseDestination(fmt.Sprintf("%s:%s", network, addr))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return core.Dial(ctx, _instance.Instance, dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
transport := &http.Transport{
|
transport := &http.Transport{
|
||||||
TLSHandshakeTimeout: time.Second * 3,
|
TLSHandshakeTimeout: time.Second * 3,
|
||||||
ResponseHeaderTimeout: time.Second * 3,
|
ResponseHeaderTimeout: time.Second * 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
if _instance != nil {
|
if i != nil {
|
||||||
transport.DialContext = dailContext
|
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
return neko_common.DialContext(ctx, i, network, addr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
|
|||||||
@@ -2,22 +2,15 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"grpc_server"
|
"grpc_server"
|
||||||
"grpc_server/gen"
|
"grpc_server/gen"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/matsuridayo/libneko/neko_common"
|
"github.com/matsuridayo/libneko/neko_common"
|
||||||
"github.com/matsuridayo/libneko/speedtest"
|
"github.com/matsuridayo/libneko/speedtest"
|
||||||
|
|
||||||
core "github.com/v2fly/v2ray-core/v5"
|
|
||||||
"github.com/v2fly/v2ray-core/v5/nekoutils"
|
"github.com/v2fly/v2ray-core/v5/nekoutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -120,7 +113,7 @@ func (s *server) Test(ctx context.Context, in *gen.TestReq) (out *gen.TestResp,
|
|||||||
|
|
||||||
// Latency
|
// Latency
|
||||||
var t int32
|
var t int32
|
||||||
t, err = speedtest.UrlTest(getProxyHttpClient(i), in.Url, in.Timeout)
|
t, err = speedtest.UrlTest(createProxyHttpClient(i), in.Url, in.Timeout)
|
||||||
out.Ms = t // sn: ms==0 是错误
|
out.Ms = t // sn: ms==0 是错误
|
||||||
} else if in.Mode == gen.TestMode_TcpPing {
|
} else if in.Mode == gen.TestMode_TcpPing {
|
||||||
out.Ms, err = speedtest.TcpPing(in.Address, in.Timeout)
|
out.Ms, err = speedtest.TcpPing(in.Address, in.Timeout)
|
||||||
@@ -140,123 +133,7 @@ func (s *server) Test(ctx context.Context, in *gen.TestReq) (out *gen.TestResp,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Latency
|
return grpc_server.DoFullTest(ctx, in, i)
|
||||||
var latency string
|
|
||||||
if in.FullLatency {
|
|
||||||
t, _ := speedtest.UrlTest(getProxyHttpClient(i), in.Url, in.Timeout)
|
|
||||||
out.Ms = t
|
|
||||||
if t > 0 {
|
|
||||||
latency = fmt.Sprint(t, "ms")
|
|
||||||
} else {
|
|
||||||
latency = "Error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UDP Latency
|
|
||||||
var udpLatency string
|
|
||||||
if in.FullUdpLatency {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
|
|
||||||
result := make(chan string)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
var startTime = time.Now()
|
|
||||||
pc, err := core.DialUDP(ctx, i.Instance)
|
|
||||||
if err == nil {
|
|
||||||
defer pc.Close()
|
|
||||||
dnsPacket, _ := hex.DecodeString("0000010000010000000000000377777706676f6f676c6503636f6d0000010001")
|
|
||||||
addr := &net.UDPAddr{
|
|
||||||
IP: net.ParseIP("8.8.8.8"),
|
|
||||||
Port: 53,
|
|
||||||
}
|
|
||||||
_, err = pc.WriteTo(dnsPacket, addr)
|
|
||||||
if err == nil {
|
|
||||||
var buf [1400]byte
|
|
||||||
_, _, err = pc.ReadFrom(buf[:])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
var endTime = time.Now()
|
|
||||||
result <- fmt.Sprint(endTime.Sub(startTime).Abs().Milliseconds(), "ms")
|
|
||||||
} else {
|
|
||||||
result <- "Error"
|
|
||||||
}
|
|
||||||
close(result)
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
udpLatency = "Timeout"
|
|
||||||
case r := <-result:
|
|
||||||
udpLatency = r
|
|
||||||
}
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 入口 IP
|
|
||||||
var in_ip string
|
|
||||||
if in.FullInOut {
|
|
||||||
_in_ip, err := net.ResolveIPAddr("ip", in.InAddress)
|
|
||||||
if err == nil {
|
|
||||||
in_ip = _in_ip.String()
|
|
||||||
} else {
|
|
||||||
in_ip = err.Error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client := getProxyHttpClient(i)
|
|
||||||
|
|
||||||
// 出口 IP
|
|
||||||
var out_ip string
|
|
||||||
if in.FullInOut {
|
|
||||||
resp, err := client.Get("https://httpbin.org/get")
|
|
||||||
if err == nil {
|
|
||||||
v := make(map[string]interface{})
|
|
||||||
json.NewDecoder(resp.Body).Decode(&v)
|
|
||||||
if a, ok := v["origin"]; ok {
|
|
||||||
if s, ok := a.(string); ok {
|
|
||||||
out_ip = s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resp.Body.Close()
|
|
||||||
} else {
|
|
||||||
out_ip = "Error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载
|
|
||||||
var speed string
|
|
||||||
if in.FullSpeed {
|
|
||||||
resp, err := client.Get("http://cachefly.cachefly.net/10mb.test")
|
|
||||||
if err == nil {
|
|
||||||
time_start := time.Now()
|
|
||||||
n, _ := io.Copy(io.Discard, resp.Body)
|
|
||||||
time_end := time.Now()
|
|
||||||
|
|
||||||
speed = fmt.Sprintf("%.2fMiB/s", (float64(n)/time_end.Sub(time_start).Seconds())/1048576)
|
|
||||||
resp.Body.Close()
|
|
||||||
} else {
|
|
||||||
speed = "Error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fr := make([]string, 0)
|
|
||||||
if latency != "" {
|
|
||||||
fr = append(fr, fmt.Sprintf("Latency: %s", latency))
|
|
||||||
}
|
|
||||||
if udpLatency != "" {
|
|
||||||
fr = append(fr, fmt.Sprintf("UDPLatency: %s", udpLatency))
|
|
||||||
}
|
|
||||||
if speed != "" {
|
|
||||||
fr = append(fr, fmt.Sprintf("Speed: %s", speed))
|
|
||||||
}
|
|
||||||
if in_ip != "" {
|
|
||||||
fr = append(fr, fmt.Sprintf("In: %s", in_ip))
|
|
||||||
}
|
|
||||||
if out_ip != "" {
|
|
||||||
fr = append(fr, fmt.Sprintf("Out: %s", out_ip))
|
|
||||||
}
|
|
||||||
|
|
||||||
out.FullReport = strings.Join(fr, " / ")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
137
go/grpc_server/fulltest.go
Normal file
137
go/grpc_server/fulltest.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package grpc_server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"grpc_server/gen"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/matsuridayo/libneko/neko_common"
|
||||||
|
"github.com/matsuridayo/libneko/speedtest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DoFullTest(ctx context.Context, in *gen.TestReq, instance interface{}) (out *gen.TestResp, _ error) {
|
||||||
|
out = &gen.TestResp{}
|
||||||
|
httpClient := neko_common.CreateProxyHttpClient(instance)
|
||||||
|
|
||||||
|
// Latency
|
||||||
|
var latency string
|
||||||
|
if in.FullLatency {
|
||||||
|
t, _ := speedtest.UrlTest(httpClient, in.Url, in.Timeout)
|
||||||
|
out.Ms = t
|
||||||
|
if t > 0 {
|
||||||
|
latency = fmt.Sprint(t, "ms")
|
||||||
|
} else {
|
||||||
|
latency = "Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UDP Latency
|
||||||
|
var udpLatency string
|
||||||
|
if in.FullUdpLatency {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
|
||||||
|
result := make(chan string)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
var startTime = time.Now()
|
||||||
|
pc, err := neko_common.DialContext(ctx, instance, "udp", "8.8.8.8:53")
|
||||||
|
if err == nil {
|
||||||
|
defer pc.Close()
|
||||||
|
dnsPacket, _ := hex.DecodeString("0000010000010000000000000377777706676f6f676c6503636f6d0000010001")
|
||||||
|
_, err = pc.Write(dnsPacket)
|
||||||
|
if err == nil {
|
||||||
|
var buf [1400]byte
|
||||||
|
_, err = pc.Read(buf[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
var endTime = time.Now()
|
||||||
|
result <- fmt.Sprint(endTime.Sub(startTime).Abs().Milliseconds(), "ms")
|
||||||
|
} else {
|
||||||
|
log.Println("UDP Latency test error:", err)
|
||||||
|
result <- "Error"
|
||||||
|
}
|
||||||
|
close(result)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
udpLatency = "Timeout"
|
||||||
|
case r := <-result:
|
||||||
|
udpLatency = r
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 入口 IP
|
||||||
|
var in_ip string
|
||||||
|
if in.FullInOut {
|
||||||
|
_in_ip, err := net.ResolveIPAddr("ip", in.InAddress)
|
||||||
|
if err == nil {
|
||||||
|
in_ip = _in_ip.String()
|
||||||
|
} else {
|
||||||
|
in_ip = err.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 出口 IP
|
||||||
|
var out_ip string
|
||||||
|
if in.FullInOut {
|
||||||
|
resp, err := httpClient.Get("https://httpbin.org/get")
|
||||||
|
if err == nil {
|
||||||
|
v := make(map[string]interface{})
|
||||||
|
json.NewDecoder(resp.Body).Decode(&v)
|
||||||
|
if a, ok := v["origin"]; ok {
|
||||||
|
if s, ok := a.(string); ok {
|
||||||
|
out_ip = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
} else {
|
||||||
|
out_ip = "Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载
|
||||||
|
var speed string
|
||||||
|
if in.FullSpeed {
|
||||||
|
resp, err := httpClient.Get("http://cachefly.cachefly.net/10mb.test")
|
||||||
|
if err == nil {
|
||||||
|
time_start := time.Now()
|
||||||
|
n, _ := io.Copy(io.Discard, resp.Body)
|
||||||
|
time_end := time.Now()
|
||||||
|
|
||||||
|
speed = fmt.Sprintf("%.2fMiB/s", (float64(n)/time_end.Sub(time_start).Seconds())/1048576)
|
||||||
|
resp.Body.Close()
|
||||||
|
} else {
|
||||||
|
speed = "Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fr := make([]string, 0)
|
||||||
|
if latency != "" {
|
||||||
|
fr = append(fr, fmt.Sprintf("Latency: %s", latency))
|
||||||
|
}
|
||||||
|
if udpLatency != "" {
|
||||||
|
fr = append(fr, fmt.Sprintf("UDPLatency: %s", udpLatency))
|
||||||
|
}
|
||||||
|
if speed != "" {
|
||||||
|
fr = append(fr, fmt.Sprintf("Speed: %s", speed))
|
||||||
|
}
|
||||||
|
if in_ip != "" {
|
||||||
|
fr = append(fr, fmt.Sprintf("In: %s", in_ip))
|
||||||
|
}
|
||||||
|
if out_ip != "" {
|
||||||
|
fr = append(fr, fmt.Sprintf("Out: %s", out_ip))
|
||||||
|
}
|
||||||
|
|
||||||
|
out.FullReport = strings.Join(fr, " / ")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ var update_download_url string
|
|||||||
func (s *BaseServer) Update(ctx context.Context, in *gen.UpdateReq) (*gen.UpdateResp, error) {
|
func (s *BaseServer) Update(ctx context.Context, in *gen.UpdateReq) (*gen.UpdateResp, error) {
|
||||||
ret := &gen.UpdateResp{}
|
ret := &gen.UpdateResp{}
|
||||||
|
|
||||||
client := neko_common.GetProxyHttpClient()
|
client := neko_common.CreateProxyHttpClient(neko_common.GetCurrentInstance())
|
||||||
|
|
||||||
if in.Action == gen.UpdateAction_Check { // Check update
|
if in.Action == gen.UpdateAction_Check { // Check update
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
if [ ! -z $ENV_NEKORAY ]; then
|
if [ ! -z $ENV_NEKORAY ]; then
|
||||||
export COMMIT_SING_BOX_EXTRA="e7c37b1587c38841f4eb687249a43dab421d8eff"
|
export COMMIT_SING_BOX_EXTRA="1ea593f166ddb44e12ba9c7b41bcc6e73b66b550"
|
||||||
export COMMIT_MATSURI_V2RAY="8134d3cc23aa6b8e2a056887addf22d7d22bd969"
|
export COMMIT_MATSURI_V2RAY="8134d3cc23aa6b8e2a056887addf22d7d22bd969"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -375,7 +375,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
ui->menuCurrent_Group->insertAction(ui->actionfake_5, ui->menu_clear_test_result);
|
ui->menuCurrent_Group->insertAction(ui->actionfake_5, ui->menu_clear_test_result);
|
||||||
ui->menuCurrent_Group->insertAction(ui->actionfake_5, ui->menu_resolve_domain);
|
ui->menuCurrent_Group->insertAction(ui->actionfake_5, ui->menu_resolve_domain);
|
||||||
}
|
}
|
||||||
ui->menu_full_test->setVisible(!IS_NEKO_BOX);
|
|
||||||
set_selected_or_group(menuCurrent_Select ? 1 : 0);
|
set_selected_or_group(menuCurrent_Select ? 1 : 0);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user