From 06df6a63dd3d98ee1fb0b1920ef00477be6b9abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 17 Feb 2021 14:28:33 +0800 Subject: [PATCH] Support add clash url as subscription --- TMessagesProj/build.gradle | 7 +- .../github/shadowsocks/plugin/PluginList.kt | 5 +- .../tw/nekomimi/nekogram/ShadowsocksLoader.kt | 39 +++++- .../nekomimi/nekogram/ShadowsocksRLoader.kt | 2 +- .../tw/nekomimi/nekogram/utils/ProxyUtil.kt | 114 +++++++++++++++++- build.gradle | 4 +- ss-rust/build.gradle.kts | 2 +- ssr-libev/build.gradle | 4 +- 8 files changed, 158 insertions(+), 19 deletions(-) diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index 0c9f80ff4..4917df13c 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -def verName = "7.4.2-rc06" -def verCode = 186 +def verName = "7.4.2-rc07" +def verCode = 188 def officialVer = "7.4.2" def officialCode = 2227 @@ -85,6 +85,7 @@ dependencies { implementation 'cn.hutool:hutool-crypto:5.5.8' implementation 'cn.hutool:hutool-http:5.5.8' + implementation 'org.yaml:snakeyaml:1.27' implementation 'com.jakewharton:process-phoenix:2.0.0' implementation project(":openpgp-api") @@ -138,7 +139,7 @@ pwd = pwd ?: System.getenv("ALIAS_PASS") android { compileSdkVersion 30 buildToolsVersion '30.0.3' - ndkVersion rootProject.ext.ndkVersion + ndkVersion '21.3.6528147' defaultConfig.applicationId = "nekox.messenger" diff --git a/TMessagesProj/src/main/java/com/github/shadowsocks/plugin/PluginList.kt b/TMessagesProj/src/main/java/com/github/shadowsocks/plugin/PluginList.kt index af5b186e0..196102e22 100644 --- a/TMessagesProj/src/main/java/com/github/shadowsocks/plugin/PluginList.kt +++ b/TMessagesProj/src/main/java/com/github/shadowsocks/plugin/PluginList.kt @@ -32,16 +32,17 @@ import org.telegram.messenger.R init { add(NoPlugin) addAll(ApplicationLoader.applicationContext.packageManager.queryIntentContentProviders( - Intent(PluginContract.ACTION_NATIVE_PLUGIN), PackageManager.GET_META_DATA).map { NativePlugin(it) }) + Intent(PluginContract.ACTION_NATIVE_PLUGIN), PackageManager.GET_META_DATA) + .map { NativePlugin(it) }) } val lookup = mutableMapOf().apply { for (plugin in this@PluginList) { fun check(old: Plugin?) = check(old == null || old === plugin) { LocaleController.formatString("SSPluginConflictingName",R.string.SSPluginConflictingName,plugin.id) } check(put(plugin.id, plugin)) - for (alias in plugin.idAliases) check(put(alias, plugin)) } } + val lookupNames get() = lookup.values.map { if (it.label.isNotBlank() && it.id.isNotBlank()) { "${it.label} (${it.id})" diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksLoader.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksLoader.kt index 8c29a02a7..b10fe8d57 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksLoader.kt +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksLoader.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import cn.hutool.core.codec.Base64 import com.github.shadowsocks.plugin.PluginConfiguration import com.github.shadowsocks.plugin.PluginManager +import com.github.shadowsocks.plugin.PluginOptions import com.v2ray.ang.V2RayConfig.SS_PROTOCOL import kotlinx.coroutines.runBlocking import okhttp3.HttpUrl @@ -106,16 +107,29 @@ class ShadowsocksLoader { val pl = PluginConfiguration(plugin) - if (pl.selected.contains("v2ray") && pl.selected != "v2ray") { + if (pl.selected.contains("v2ray") && pl.selected != "v2ray-plugin") { - pl.pluginsOptions["v2ray"] = pl.getOptions() + pl.pluginsOptions["v2ray-plugin"] = pl.getOptions().apply { id = "v2ray-plugin" } pl.pluginsOptions.remove(pl.selected) - pl.selected = "v2ray" + pl.selected = "v2ray-plugin" // reslove v2ray plugin } + if (pl.selected == "obfs") { + + pl.pluginsOptions["obfs-local"] = pl.getOptions().apply { id = "obfs-local" } + pl.pluginsOptions.remove(pl.selected) + pl.selected = "obfs-local" + + // reslove clash obfs + + } + + plugin = pl.toString() + + } override fun equals(other: Any?): Boolean { @@ -150,6 +164,23 @@ class ShadowsocksLoader { companion object { + fun parseJson(ssObj: JSONObject): Bean { + var pluginStr = "" + val pId = ssObj.optString("plugin") + if (!pId.isNullOrBlank()) { + val plugin = PluginOptions(pId, ssObj.optString("plugin_opts")) + pluginStr = plugin.toString(false) + } + return Bean( + ssObj.getString("server"), + ssObj.getInt("server_port"), + ssObj.getString("password"), + ssObj.getString("method"), + pluginStr, + ssObj.optString("remarks") + ) + } + fun parse(url: String): Bean { if (url.contains("@")) { @@ -157,7 +188,7 @@ class ShadowsocksLoader { // ss-android style val link = url.replace(SS_PROTOCOL, "https://").toHttpUrlOrNull() - ?: error("invalid ss-android link $url") + ?: error("invalid ss-android link $url") if (link.password.isNotBlank()) { diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksRLoader.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksRLoader.kt index a8fbd58e6..bd410a2b2 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksRLoader.kt +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksRLoader.kt @@ -99,7 +99,7 @@ class ShadowsocksRLoader { var remarks: String? = null ) { - val hash = (host + remotePort + password + protocol + obfs + method).hashCode() + val hash get() = (host + remotePort + password + protocol + obfs + method).hashCode() override fun equals(other: Any?): Boolean { return super.equals(other) || (other is Bean && hash == other.hash) diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ProxyUtil.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ProxyUtil.kt index 3a696368f..0e90bbcc7 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ProxyUtil.kt +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ProxyUtil.kt @@ -1,3 +1,5 @@ +@file:Suppress("UNCHECKED_CAST") + package tw.nekomimi.nekogram.utils import android.Manifest @@ -20,20 +22,28 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.Toast import androidx.core.view.setPadding +import com.github.shadowsocks.plugin.PluginOptions import com.google.zxing.* import com.google.zxing.common.GlobalHistogramBinarizer import com.google.zxing.qrcode.QRCodeReader import com.google.zxing.qrcode.QRCodeWriter import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel +import com.v2ray.ang.V2RayConfig import com.v2ray.ang.V2RayConfig.SSR_PROTOCOL import com.v2ray.ang.V2RayConfig.SS_PROTOCOL import com.v2ray.ang.V2RayConfig.TROJAN_PROTOCOL import com.v2ray.ang.V2RayConfig.VMESS1_PROTOCOL import com.v2ray.ang.V2RayConfig.VMESS_PROTOCOL +import com.v2ray.ang.dto.AngConfig import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import org.json.JSONArray +import org.json.JSONException import org.telegram.messenger.* import org.telegram.messenger.browser.Browser +import org.yaml.snakeyaml.Yaml import tw.nekomimi.nekogram.BottomBuilder +import tw.nekomimi.nekogram.ShadowsocksLoader +import tw.nekomimi.nekogram.ShadowsocksRLoader import tw.nekomimi.nekogram.utils.AlertUtil.showToast import java.io.File import java.net.NetworkInterface @@ -66,17 +76,113 @@ object ProxyUtil { fun parseProxies(_text: String): MutableList { val text = runCatching { - String(Base64.decode(_text, Base64.NO_PADDING)) - }.recover { - _text - }.getOrThrow() val proxies = mutableListOf() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + try { + // sip008 + val ssArray = JSONArray(text) + for (index in 0..ssArray.length()) { + proxies.add(ShadowsocksLoader.Bean.parseJson(ssArray.getJSONObject(index)).toString()) + } + return proxies + } catch (ignored: JSONException) { + } + + if (text.contains("proxies:\n")) { + // clash + + for (proxy in (Yaml().loadAs(text, Map::class.java)["proxies"] as List>)) { + val type = proxy["type"] as String + when (type) { + "ss" -> { + var pluginStr = "" + if (proxy.contains("plugin")) { + val opts = PluginOptions() + opts.id = proxy["plugin"] as String + opts.putAll(proxy["plugin-opts"] as Map) + pluginStr = opts.toString(false) + } + proxies.add(ShadowsocksLoader.Bean( + proxy["server"] as String, + proxy["port"] as Int, + proxy["password"] as String, + proxy["cipher"] as String, + pluginStr, + proxy["name"] as String + ).toString()) + } + "vmess" -> { + val opts = AngConfig.VmessBean() + for (opt in proxy) { + when (opt.key) { + "name" -> opts.remarks = opt.value as String + "server" -> opts.address = opt.value as String + "port" -> opts.port = opt.value as Int + "uuid" -> opts.id = opt.value as String + "alterId" -> opts.alterId = opt.value as Int + "cipher" -> opts.security = opt.value as String + "network" -> opts.network = opt.value as String + "tls" -> opts.streamSecurity = if (opt.value?.toString() == "true") "tls" else opts.streamSecurity + "ws-path" -> opts.path = opt.value as String + "servername" -> opts.requestHost = opt.value as String + "h2-opts" -> for (h2Opt in (opt.value as Map)) { + when (h2Opt.key) { + "host" -> opts.requestHost = (h2Opt.value as List).first() + "path" -> opts.path = h2Opt.value as String + } + } + "http-opts" -> for (httpOpt in (opt.value as Map)) { + when (httpOpt.key) { + "path" -> opts.path = (httpOpt.value as List).first() + } + } + } + } + proxies.add(opts.toString()) + } + "trojan" -> { + val opts = AngConfig.VmessBean() + opts.configType = V2RayConfig.EConfigType.Trojan + for (opt in proxy) { + when (opt.key) { + "name" -> opts.remarks = opt.value as String + "server" -> opts.address = opt.value as String + "port" -> opts.port = opt.value as Int + "password" -> opts.id = opt.value as String + "sni" -> opts.requestHost = opt.value as String + } + } + proxies.add(opts.toString()) + } + "ssr" -> { + val opts = ShadowsocksRLoader.Bean() + for (opt in proxy) { + when (opt.key) { + "name" -> opts.remarks = opt.value as String + "server" -> opts.host = opt.value as String + "port" -> opts.remotePort = opt.value as Int + "cipher" -> opts.method = opt.value as String + "password" -> opts.password = opt.value as String + "obfs" -> opts.obfs = opt.value as String + "protocol" -> opts.protocol = opt.value as String + "obfs-param" -> opts.obfs_param = opt.value as String + "protocol-param" -> opts.protocol_param = opt.value as String + } + } + proxies.add(opts.toString()) + } + } + } + return proxies + } + } + text.split('\n').map { it.split(" ") }.forEach { it.forEach { line -> diff --git a/build.gradle b/build.gradle index 9fb37319a..d894752a9 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { } } -static String detectNdkVersion() { +/*static String detectNdkVersion() { def version = "21.3.6528147" @@ -40,7 +40,7 @@ ext { ndkVersion = detectNdkVersion() -} +}*/ allprojects { repositories { diff --git a/ss-rust/build.gradle.kts b/ss-rust/build.gradle.kts index a6e0d3098..37c01d047 100644 --- a/ss-rust/build.gradle.kts +++ b/ss-rust/build.gradle.kts @@ -7,7 +7,7 @@ plugins { android { - ndkVersion = rootProject.extra.get("ndkVersion").toString() + ndkVersion = "21.3.6528147" compileSdkVersion(30) defaultConfig { diff --git a/ssr-libev/build.gradle b/ssr-libev/build.gradle index a8ec2ab7a..8054838be 100644 --- a/ssr-libev/build.gradle +++ b/ssr-libev/build.gradle @@ -4,8 +4,8 @@ plugins { android { compileSdkVersion 30 - buildToolsVersion '30.0.2' - ndkVersion rootProject.ext.ndkVersion + buildToolsVersion '30.0.3' + ndkVersion "21.3.6528147" defaultConfig { minSdkVersion 16