├── .github
└── workflows
│ └── release.yml
├── .gitignore
├── README.md
├── app_brook
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── moe
│ └── matsuri
│ └── plugin
│ └── brook
│ └── BinaryProvider.kt
├── app_hysteria
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── moe
│ │ └── matsuri
│ │ └── exe
│ │ └── hysteria
│ │ └── BinaryProvider.kt
│ └── res
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ └── mipmap-xxxhdpi
│ └── ic_launcher.png
├── app_juicity
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── moe
│ └── matsuri
│ └── plugin
│ └── singbox
│ └── BinaryProvider.kt
├── app_naive
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── moe
│ │ └── matsuri
│ │ └── exe
│ │ └── naive
│ │ └── BinaryProvider.kt
│ └── res
│ ├── drawable
│ └── ic_launcher_foreground.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ └── ic_launcher_background.xml
├── app_singbox
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── moe
│ │ └── matsuri
│ │ └── plugin
│ │ └── singbox
│ │ └── BinaryProvider.kt
│ └── res
│ └── mipmap-xxxhdpi
│ └── ic_launcher.png
├── app_trojan-go
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── moe
│ │ └── matsuri
│ │ └── exe
│ │ └── trojan_go
│ │ └── BinaryProvider.kt
│ └── res
│ ├── drawable
│ └── ic_launcher_foreground.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ └── ic_launcher_background.xml
├── app_tuic
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── moe
│ └── matsuri
│ └── exe
│ └── tuic
│ └── BinaryProvider.kt
├── app_tuic5
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── moe
│ └── matsuri
│ └── exe
│ └── tuic
│ └── BinaryProvider.kt
├── app_xray
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── moe
│ │ └── matsuri
│ │ └── plugin
│ │ └── xray
│ │ └── BinaryProvider.kt
│ └── res
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ └── mipmap-xxxhdpi
│ └── ic_launcher.png
├── build.gradle
├── buildSrc
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── BuildSrc.kt
├── common
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── io
│ └── nekohasekai
│ └── sagernet
│ └── plugin
│ ├── NativePluginProvider.kt
│ ├── PathProvider.kt
│ └── PluginContract.kt
├── download.sh
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── js
├── .gitignore
├── common
│ ├── common.js
│ ├── translate.js
│ └── util.js
├── make.sh
├── package-lock.json
├── package.json
├── plugin.html
├── plugin_brook
│ ├── brook.js
│ └── main.js
├── plugin_juicity
│ ├── juicity.js
│ └── main.js
├── plugin_singbox
│ ├── main.js
│ ├── shadowtls.js
│ └── wireguard.js
├── plugin_xray
│ ├── main.js
│ ├── ss2022.js
│ └── vless.js
└── webpack.config.js
├── make.sh
├── release.keystore
├── requirement.sh
└── settings.gradle
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release Build
2 | on:
3 | workflow_dispatch:
4 | inputs:
5 | tag:
6 | description: 'Release Tag'
7 | required: true
8 | plugin:
9 | description: 'Plugin to build'
10 | required: true
11 | publish:
12 | description: 'Publish: If want ignore'
13 | required: false
14 | jobs:
15 | build:
16 | name: Build
17 | runs-on: ubuntu-latest
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v2
21 | - name: Gradle cache
22 | uses: actions/cache@v2
23 | with:
24 | path: ~/.gradle
25 | key: gradle-${{ hashFiles('**/*.gradle.kts') }}
26 | - name: Release Build
27 | run: |
28 | export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
29 | bash requirement.sh
30 | dl=1 bash make.sh ${{ github.event.inputs.plugin }}
31 | APK=$(find . -name '*arm64-v8a*.apk')
32 | APK=$(dirname $APK)
33 | echo "APK=$APK" >> $GITHUB_ENV
34 | - uses: actions/upload-artifact@v2
35 | with:
36 | name: APKs
37 | path: ${{ env.APK }}
38 | publish:
39 | name: Publish Release
40 | if: github.event.inputs.publish != 'y'
41 | runs-on: ubuntu-latest
42 | needs: build
43 | steps:
44 | - name: Checkout
45 | uses: actions/checkout@v2
46 | - name: Donwload Artifacts
47 | uses: actions/download-artifact@v2
48 | with:
49 | name: APKs
50 | path: artifacts
51 | - name: Release
52 | run: |
53 | wget -O ghr.tar.gz https://github.com/tcnksm/ghr/releases/download/v0.13.0/ghr_v0.13.0_linux_amd64.tar.gz
54 | tar -xvf ghr.tar.gz
55 | mv ghr*linux_amd64/ghr .
56 | mkdir apks
57 | find artifacts -name "*.apk" -exec cp {} apks \;
58 | ./ghr -delete -t "${{ github.token }}" -n "${{ github.event.inputs.tag }}" "${{ github.event.inputs.tag }}" apks
59 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | .idea
5 | .vscode
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | .cxx
11 | local.properties
12 |
13 | /app_*/build
14 | /app_*/libs
15 | /app_*/html
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Matsuri Plugins
2 |
3 | ## To find plugin download
4 |
5 | https://matsuridayo.github.io/m-plugin/
6 |
7 | https://github.com/MatsuriDayo/plugins/releases
8 |
9 | ## To build a plugin
10 |
11 | TODO
12 |
--------------------------------------------------------------------------------
/app_brook/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | setupAll()
7 |
8 | android {
9 | defaultConfig {
10 | applicationId = "moe.matsuri.plugin.brook"
11 | versionCode = 2
12 | versionName = "v20220707-1"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app_brook/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
31 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app_brook/src/main/java/moe/matsuri/plugin/brook/BinaryProvider.kt:
--------------------------------------------------------------------------------
1 | package moe.matsuri.plugin.brook
2 |
3 | import android.net.Uri
4 | import android.os.ParcelFileDescriptor
5 | import io.nekohasekai.sagernet.plugin.NativePluginProvider
6 | import io.nekohasekai.sagernet.plugin.PathProvider
7 | import java.io.File
8 | import java.io.FileNotFoundException
9 |
10 | class BinaryProvider : NativePluginProvider() {
11 | override fun populateFiles(provider: PathProvider) {
12 | provider.addPath("moe.matsuri.plugin.brook", 0b111101101)
13 | }
14 |
15 | override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libbrook.so"
16 |
17 | override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) {
18 | "/moe.matsuri.plugin.brook" -> ParcelFileDescriptor.open(
19 | File(getExecutable()),
20 | ParcelFileDescriptor.MODE_READ_ONLY
21 | )
22 | else -> throw FileNotFoundException()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app_hysteria/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | setupAll()
7 |
8 | android {
9 | defaultConfig {
10 | applicationId = "moe.matsuri.exe.hysteria"
11 | versionCode = 8
12 | versionName = "1.3.5-1"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app_hysteria/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
14 |
15 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
35 |
36 |
37 |
40 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app_hysteria/src/main/java/moe/matsuri/exe/hysteria/BinaryProvider.kt:
--------------------------------------------------------------------------------
1 | package moe.matsuri.exe.hysteria
2 |
3 | import android.net.Uri
4 | import android.os.ParcelFileDescriptor
5 | import io.nekohasekai.sagernet.plugin.NativePluginProvider
6 | import io.nekohasekai.sagernet.plugin.PathProvider
7 | import java.io.File
8 | import java.io.FileNotFoundException
9 |
10 | class BinaryProvider : NativePluginProvider() {
11 | override fun populateFiles(provider: PathProvider) {
12 | provider.addPath("hysteria-plugin", 0b111101101)
13 | }
14 |
15 | override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libhysteria.so"
16 | override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) {
17 | "/hysteria-plugin" -> ParcelFileDescriptor.open(
18 | File(getExecutable()),
19 | ParcelFileDescriptor.MODE_READ_ONLY
20 | )
21 | else -> throw FileNotFoundException()
22 | }
23 | }
--------------------------------------------------------------------------------
/app_hysteria/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_hysteria/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_hysteria/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_hysteria/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_hysteria/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_hysteria/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_hysteria/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_hysteria/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_hysteria/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_hysteria/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_juicity/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | setupAll()
7 |
8 | android {
9 | defaultConfig {
10 | applicationId = "moe.matsuri.plugin.juicity"
11 | versionCode = 6
12 | versionName = "v0.4.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app_juicity/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
31 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app_juicity/src/main/java/moe/matsuri/plugin/singbox/BinaryProvider.kt:
--------------------------------------------------------------------------------
1 | package moe.matsuri.plugin.juicity
2 |
3 | import android.net.Uri
4 | import android.os.ParcelFileDescriptor
5 | import io.nekohasekai.sagernet.plugin.NativePluginProvider
6 | import io.nekohasekai.sagernet.plugin.PathProvider
7 | import java.io.File
8 | import java.io.FileNotFoundException
9 |
10 | class BinaryProvider : NativePluginProvider() {
11 | override fun populateFiles(provider: PathProvider) {
12 | provider.addPath("moe.matsuri.plugin.juicity", 0b111101101)
13 | }
14 |
15 | override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libjuicity.so"
16 |
17 | override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) {
18 | "/moe.matsuri.plugin.juicity" -> ParcelFileDescriptor.open(
19 | File(getExecutable()),
20 | ParcelFileDescriptor.MODE_READ_ONLY
21 | )
22 | else -> throw FileNotFoundException()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app_naive/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /libs
3 | /html
4 |
--------------------------------------------------------------------------------
/app_naive/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | setupAll()
7 |
8 | android {
9 | defaultConfig {
10 | applicationId = "moe.matsuri.exe.naive"
11 | versionCode = 8
12 | versionName = "116.0.5845.92-2"
13 | splits.abi {
14 | reset()
15 | include("arm64-v8a", "armeabi-v7a")
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app_naive/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
32 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app_naive/src/main/java/moe/matsuri/exe/naive/BinaryProvider.kt:
--------------------------------------------------------------------------------
1 | package moe.matsuri.exe.naive
2 |
3 | import android.net.Uri
4 | import android.os.ParcelFileDescriptor
5 | import io.nekohasekai.sagernet.plugin.NativePluginProvider
6 | import io.nekohasekai.sagernet.plugin.PathProvider
7 | import java.io.File
8 | import java.io.FileNotFoundException
9 |
10 | class BinaryProvider : NativePluginProvider() {
11 | override fun populateFiles(provider: PathProvider) {
12 | provider.addPath("naive-plugin", 0b111101101)
13 | }
14 |
15 | override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libnaive.so"
16 |
17 | override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) {
18 | "/naive-plugin" -> ParcelFileDescriptor.open(
19 | File(getExecutable()),
20 | ParcelFileDescriptor.MODE_READ_ONLY
21 | )
22 | else -> throw FileNotFoundException()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app_naive/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
12 |
13 |
15 |
17 |
19 |
21 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_naive/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_naive/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_naive/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_naive/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_naive/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_naive/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_naive/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_naive/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_naive/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_naive/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_naive/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app_naive/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #E91E63
4 |
--------------------------------------------------------------------------------
/app_singbox/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | setupAll()
7 |
8 | android {
9 | defaultConfig {
10 | applicationId = "moe.matsuri.plugin.singbox"
11 | versionCode = 18
12 | versionName = "v1.3.4"
13 | splits.abi {
14 | reset()
15 | include("arm64-v8a", "armeabi-v7a")
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app_singbox/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
32 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app_singbox/src/main/java/moe/matsuri/plugin/singbox/BinaryProvider.kt:
--------------------------------------------------------------------------------
1 | package moe.matsuri.plugin.singbox
2 |
3 | import android.net.Uri
4 | import android.os.ParcelFileDescriptor
5 | import io.nekohasekai.sagernet.plugin.NativePluginProvider
6 | import io.nekohasekai.sagernet.plugin.PathProvider
7 | import java.io.File
8 | import java.io.FileNotFoundException
9 |
10 | class BinaryProvider : NativePluginProvider() {
11 | override fun populateFiles(provider: PathProvider) {
12 | provider.addPath("moe.matsuri.plugin.singbox", 0b111101101)
13 | }
14 |
15 | override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libsingbox.so"
16 |
17 | override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) {
18 | "/moe.matsuri.plugin.singbox" -> ParcelFileDescriptor.open(
19 | File(getExecutable()),
20 | ParcelFileDescriptor.MODE_READ_ONLY
21 | )
22 | else -> throw FileNotFoundException()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app_singbox/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_singbox/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_trojan-go/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /libs
3 | /html
4 |
--------------------------------------------------------------------------------
/app_trojan-go/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | setupAll()
7 |
8 | android {
9 | defaultConfig {
10 | applicationId = "moe.matsuri.exe.trojan_go"
11 | versionCode = 1
12 | versionName = "0.10.15"
13 | splits.abi {
14 | reset()
15 | include("arm64-v8a", "armeabi-v7a")
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app_trojan-go/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
32 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app_trojan-go/src/main/java/moe/matsuri/exe/trojan_go/BinaryProvider.kt:
--------------------------------------------------------------------------------
1 | package moe.matsuri.exe.trojan_go
2 |
3 | import android.net.Uri
4 | import android.os.ParcelFileDescriptor
5 | import io.nekohasekai.sagernet.plugin.NativePluginProvider
6 | import io.nekohasekai.sagernet.plugin.PathProvider
7 | import java.io.File
8 | import java.io.FileNotFoundException
9 |
10 | class BinaryProvider : NativePluginProvider() {
11 | override fun populateFiles(provider: PathProvider) {
12 | provider.addPath("trojan-go-plugin", 0b111101101)
13 | }
14 |
15 | override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libtrojan-go.so"
16 |
17 | override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) {
18 | "/trojan-go-plugin" -> ParcelFileDescriptor.open(
19 | File(getExecutable()),
20 | ParcelFileDescriptor.MODE_READ_ONLY
21 | )
22 | else -> throw FileNotFoundException()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
12 |
13 |
15 |
17 |
19 |
21 |
23 |
25 |
26 |
27 |
29 |
31 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_trojan-go/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_trojan-go/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_trojan-go/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_trojan-go/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_trojan-go/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_trojan-go/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_trojan-go/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_trojan-go/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_trojan-go/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_trojan-go/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app_trojan-go/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #E91E63
4 |
--------------------------------------------------------------------------------
/app_tuic/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | setupAll()
7 |
8 | android {
9 | defaultConfig {
10 | applicationId = "moe.matsuri.exe.tuic"
11 | versionCode = 1
12 | versionName = "0.8.5-2"
13 | splits.abi {
14 | reset()
15 | include("arm64-v8a", "x86_64")
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app_tuic/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
14 |
15 |
19 |
25 |
26 |
27 |
28 |
29 |
30 |
34 |
35 |
36 |
39 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app_tuic/src/main/java/moe/matsuri/exe/tuic/BinaryProvider.kt:
--------------------------------------------------------------------------------
1 | package moe.matsuri.exe.tuic
2 |
3 | import android.net.Uri
4 | import android.os.ParcelFileDescriptor
5 | import io.nekohasekai.sagernet.plugin.NativePluginProvider
6 | import io.nekohasekai.sagernet.plugin.PathProvider
7 | import java.io.File
8 | import java.io.FileNotFoundException
9 |
10 | class BinaryProvider : NativePluginProvider() {
11 | override fun populateFiles(provider: PathProvider) {
12 | provider.addPath("tuic-plugin", 0b111101101)
13 | }
14 |
15 | override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libtuic.so"
16 | override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) {
17 | "/tuic-plugin" -> ParcelFileDescriptor.open(
18 | File(getExecutable()),
19 | ParcelFileDescriptor.MODE_READ_ONLY
20 | )
21 | else -> throw FileNotFoundException()
22 | }
23 | }
--------------------------------------------------------------------------------
/app_tuic5/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | setupAll()
7 |
8 | android {
9 | defaultConfig {
10 | applicationId = "moe.matsuri.exe.tuic5"
11 | versionCode = 2
12 | versionName = "1.0.0-3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app_tuic5/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
14 |
15 |
19 |
25 |
26 |
27 |
28 |
29 |
30 |
34 |
35 |
36 |
39 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app_tuic5/src/main/java/moe/matsuri/exe/tuic/BinaryProvider.kt:
--------------------------------------------------------------------------------
1 | package moe.matsuri.exe.tuic5
2 |
3 | import android.net.Uri
4 | import android.os.ParcelFileDescriptor
5 | import io.nekohasekai.sagernet.plugin.NativePluginProvider
6 | import io.nekohasekai.sagernet.plugin.PathProvider
7 | import java.io.File
8 | import java.io.FileNotFoundException
9 |
10 | class BinaryProvider : NativePluginProvider() {
11 | override fun populateFiles(provider: PathProvider) {
12 | provider.addPath("tuic-v5-plugin", 0b111101101)
13 | }
14 |
15 | override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libtuic.so"
16 | override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) {
17 | "/tuic-v5-plugin" -> ParcelFileDescriptor.open(
18 | File(getExecutable()),
19 | ParcelFileDescriptor.MODE_READ_ONLY
20 | )
21 | else -> throw FileNotFoundException()
22 | }
23 | }
--------------------------------------------------------------------------------
/app_xray/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | setupAll()
7 |
8 | android {
9 | defaultConfig {
10 | applicationId = "moe.matsuri.plugin.xray"
11 | versionCode = 20
12 | versionName = "v1.8.6"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app_xray/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
32 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app_xray/src/main/java/moe/matsuri/plugin/xray/BinaryProvider.kt:
--------------------------------------------------------------------------------
1 | package moe.matsuri.plugin.xray
2 |
3 | import android.net.Uri
4 | import android.os.ParcelFileDescriptor
5 | import io.nekohasekai.sagernet.plugin.NativePluginProvider
6 | import io.nekohasekai.sagernet.plugin.PathProvider
7 | import java.io.File
8 | import java.io.FileNotFoundException
9 |
10 | class BinaryProvider : NativePluginProvider() {
11 | override fun populateFiles(provider: PathProvider) {
12 | provider.addPath("moe.matsuri.plugin.xray", 0b111101101)
13 | }
14 |
15 | override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libxray.so"
16 |
17 | override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) {
18 | "/moe.matsuri.plugin.xray" -> ParcelFileDescriptor.open(
19 | File(getExecutable()),
20 | ParcelFileDescriptor.MODE_READ_ONLY
21 | )
22 | else -> throw FileNotFoundException()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app_xray/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_xray/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_xray/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_xray/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_xray/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_xray/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_xray/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_xray/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_xray/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/app_xray/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:7.3.1'
9 | classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | task clean(type: Delete) {
17 | delete rootProject.buildDir
18 | }
--------------------------------------------------------------------------------
/buildSrc/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `java-gradle-plugin`
3 | `kotlin-dsl`
4 | }
5 |
6 | repositories {
7 | google()
8 | mavenCentral()
9 | gradlePluginPortal()
10 | maven(url = "https://jitpack.io")
11 | }
12 |
13 | dependencies {
14 | implementation(kotlin("gradle-plugin", "1.6.10"))
15 |
16 | implementation("com.android.tools.build:gradle:7.3.1")
17 | implementation("com.android.tools.build:gradle-api:7.3.1")
18 | }
19 |
--------------------------------------------------------------------------------
/buildSrc/src/main/java/BuildSrc.kt:
--------------------------------------------------------------------------------
1 | import com.android.build.gradle.BaseExtension
2 | import com.android.build.gradle.internal.api.BaseVariantOutputImpl
3 | import org.gradle.api.JavaVersion
4 | import org.gradle.api.Project
5 | import org.gradle.api.plugins.ExtensionAware
6 | import org.gradle.kotlin.dsl.getByName
7 | import org.gradle.util.GUtil.loadProperties
8 | import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
9 | import com.android.build.gradle.AbstractAppExtension
10 | import java.io.File
11 | import java.security.MessageDigest
12 |
13 | private val Project.android get() = extensions.getByName("android")
14 | private lateinit var flavor: String
15 |
16 | private val javaVersion = JavaVersion.VERSION_1_8
17 |
18 | fun Project.requireFlavor(): String {
19 | if (::flavor.isInitialized) return flavor
20 | if (gradle.startParameter.taskNames.isNotEmpty()) {
21 | val taskName = gradle.startParameter.taskNames[0]
22 | when {
23 | taskName.contains("assemble") -> {
24 | flavor = taskName.substringAfter("assemble")
25 | return flavor
26 | }
27 | taskName.contains("install") -> {
28 | flavor = taskName.substringAfter("install")
29 | return flavor
30 | }
31 | taskName.contains("publish") -> {
32 | flavor = taskName.substringAfter("publish").substringBefore("Bundle")
33 | return flavor
34 | }
35 | }
36 | }
37 |
38 | flavor = ""
39 | return flavor
40 | }
41 |
42 | fun Project.requireLocalProperties(): java.util.Properties? {
43 | if (project.rootProject.file("local.properties").exists()) {
44 | return loadProperties(rootProject.file("local.properties"))
45 | }
46 | return null
47 | }
48 |
49 | fun Project.setupCommon() {
50 | dependencies.apply {
51 | add("implementation", project(":common"))
52 | }
53 |
54 | android.apply {
55 | compileSdkVersion(33)
56 |
57 | defaultConfig.apply {
58 | minSdk = 21
59 | targetSdk = 33
60 | }
61 |
62 | compileOptions {
63 | sourceCompatibility = javaVersion
64 | targetCompatibility = javaVersion
65 | }
66 |
67 | (android as ExtensionAware).extensions.getByName("kotlinOptions").apply {
68 | jvmTarget = javaVersion.toString()
69 | }
70 |
71 | lintOptions {
72 | isShowAll = true
73 | isCheckAllWarnings = true
74 | isCheckReleaseBuilds = false
75 | isWarningsAsErrors = true
76 | textOutput = project.file("build/lint.txt")
77 | htmlOutput = project.file("build/lint.html")
78 | }
79 |
80 | //
81 |
82 | sourceSets.getByName("main") {
83 | jniLibs.srcDir("libs")
84 | assets.srcDir("html")
85 | }
86 |
87 | splits.abi {
88 | if (requireFlavor().startsWith("Fdroid")) {
89 | isEnable = false
90 | } else {
91 | isEnable = true
92 | isUniversalApk = false
93 | }
94 | }
95 |
96 | (this as? AbstractAppExtension)?.apply {
97 | applicationVariants.all {
98 | outputs.all {
99 | this as BaseVariantOutputImpl
100 | outputFileName =
101 | outputFileName.replace(project.name, "${project.name}-plugin-$versionName")
102 | .replace("-release", "")
103 | .replace("-oss", "")
104 | .replace("app_", "")
105 | }
106 | }
107 | }
108 | }
109 | }
110 |
111 | fun Project.setupRelease() {
112 | val lp = requireLocalProperties()!!
113 | val keystorePwd = lp.getProperty("KEYSTORE_PASS") ?: System.getenv("KEYSTORE_PASS")
114 | val alias = lp.getProperty("ALIAS_NAME") ?: System.getenv("ALIAS_NAME")
115 | val pwd = lp.getProperty("ALIAS_PASS") ?: System.getenv("ALIAS_PASS")
116 |
117 | android.apply {
118 | if (keystorePwd != null) {
119 | signingConfigs {
120 | create("release") {
121 | storeFile = rootProject.file("release.keystore")
122 | storePassword = keystorePwd
123 | keyAlias = alias
124 | keyPassword = pwd
125 | }
126 | }
127 | }
128 |
129 | buildTypes {
130 | getByName("release") {
131 | proguardFiles(
132 | getDefaultProguardFile("proguard-android-optimize.txt"),
133 | file("proguard-rules.pro")
134 | )
135 | isMinifyEnabled = true
136 | }
137 |
138 | val key = signingConfigs.findByName("release")
139 | if (key != null) {
140 | getByName("release").signingConfig = key
141 | }
142 | }
143 | }
144 | }
145 |
146 | fun Project.setupAll() {
147 | setupCommon()
148 | setupRelease()
149 | }
--------------------------------------------------------------------------------
/common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /libs
3 | /html
4 |
--------------------------------------------------------------------------------
/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.konan.properties.loadProperties
2 |
3 | plugins {
4 | id("com.android.library")
5 | id("kotlin-android")
6 | }
7 |
8 | android {
9 | compileSdk = 33
10 |
11 | defaultConfig {
12 | minSdk = 21
13 | targetSdk = 33
14 | }
15 |
16 | compileOptions {
17 | sourceCompatibility = JavaVersion.VERSION_1_8
18 | targetCompatibility = JavaVersion.VERSION_1_8
19 | }
20 | kotlinOptions {
21 | jvmTarget = "1.8"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/common/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/common/src/main/java/io/nekohasekai/sagernet/plugin/NativePluginProvider.kt:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * *
3 | * Copyright (C) 2021 by nekohasekai *
4 | * *
5 | * This program is free software: you can redistribute it and/or modify *
6 | * it under the terms of the GNU General Public License as published by *
7 | * the Free Software Foundation, either version 3 of the License, or *
8 | * (at your option) any later version. *
9 | * *
10 | * This program is distributed in the hope that it will be useful, *
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 | * GNU General Public License for more details. *
14 | * *
15 | * You should have received a copy of the GNU General Public License *
16 | * along with this program. If not, see . *
17 | * *
18 | ******************************************************************************/
19 |
20 | package io.nekohasekai.sagernet.plugin
21 |
22 | import android.content.ContentProvider
23 | import android.content.ContentValues
24 | import android.database.Cursor
25 | import android.database.MatrixCursor
26 | import android.net.Uri
27 | import android.os.Bundle
28 | import android.os.ParcelFileDescriptor
29 |
30 | abstract class NativePluginProvider : ContentProvider() {
31 | override fun getType(uri: Uri): String? = "application/x-elf"
32 |
33 | override fun onCreate(): Boolean = true
34 |
35 | /**
36 | * Provide all files needed for native plugin.
37 | *
38 | * @param provider A helper object to use to add files.
39 | */
40 | protected abstract fun populateFiles(provider: PathProvider)
41 |
42 | override fun query(
43 | uri: Uri,
44 | projection: Array?,
45 | selection: String?,
46 | selectionArgs: Array?,
47 | sortOrder: String?,
48 | ): Cursor? {
49 | check(selection == null && selectionArgs == null && sortOrder == null)
50 | val result = MatrixCursor(projection)
51 | populateFiles(PathProvider(uri, result))
52 | return result
53 | }
54 |
55 | /**
56 | * Returns executable entry absolute path.
57 | * This is used for fast mode initialization where ss-local launches your native binary at the path given directly.
58 | * In order for this to work, plugin app is encouraged to have the following in its AndroidManifest.xml:
59 | * - android:installLocation="internalOnly" for
60 | * - android:extractNativeLibs="true" for
61 | *
62 | * Default behavior is throwing UnsupportedOperationException. If you don't wish to use this feature, use the
63 | * default behavior.
64 | *
65 | * @return Absolute path for executable entry.
66 | */
67 | open fun getExecutable(): String = throw UnsupportedOperationException()
68 |
69 | abstract fun openFile(uri: Uri): ParcelFileDescriptor
70 | override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor {
71 | check(mode == "r")
72 | return openFile(uri)
73 | }
74 |
75 | override fun call(method: String, arg: String?, extras: Bundle?): Bundle? = when (method) {
76 | PluginContract.METHOD_GET_EXECUTABLE -> {
77 | Bundle().apply {
78 | putString(PluginContract.EXTRA_ENTRY, getExecutable())
79 | }
80 | }
81 | else -> super.call(method, arg, extras)
82 | }
83 |
84 | // Methods that should not be used
85 | override fun insert(uri: Uri, values: ContentValues?): Uri? =
86 | throw UnsupportedOperationException()
87 |
88 | override fun update(
89 | uri: Uri,
90 | values: ContentValues?,
91 | selection: String?,
92 | selectionArgs: Array?,
93 | ): Int =
94 | throw UnsupportedOperationException()
95 |
96 | override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int =
97 | throw UnsupportedOperationException()
98 | }
99 |
--------------------------------------------------------------------------------
/common/src/main/java/io/nekohasekai/sagernet/plugin/PathProvider.kt:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * *
3 | * Copyright (C) 2021 by nekohasekai *
4 | * *
5 | * This program is free software: you can redistribute it and/or modify *
6 | * it under the terms of the GNU General Public License as published by *
7 | * the Free Software Foundation, either version 3 of the License, or *
8 | * (at your option) any later version. *
9 | * *
10 | * This program is distributed in the hope that it will be useful, *
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 | * GNU General Public License for more details. *
14 | * *
15 | * You should have received a copy of the GNU General Public License *
16 | * along with this program. If not, see . *
17 | * *
18 | ******************************************************************************/
19 |
20 | package io.nekohasekai.sagernet.plugin
21 |
22 | import android.database.MatrixCursor
23 | import android.net.Uri
24 | import java.io.File
25 |
26 | /**
27 | * Helper class to provide relative paths of files to copy.
28 | */
29 | class PathProvider internal constructor(baseUri: Uri, private val cursor: MatrixCursor) {
30 | private val basePath = baseUri.path?.trim('/') ?: ""
31 |
32 | fun addPath(path: String, mode: Int = 0b110100100): PathProvider {
33 | val trimmed = path.trim('/')
34 | if (trimmed.startsWith(basePath)) cursor.newRow()
35 | .add(PluginContract.COLUMN_PATH, trimmed)
36 | .add(PluginContract.COLUMN_MODE, mode)
37 | return this
38 | }
39 | fun addTo(file: File, to: String = "", mode: Int = 0b110100100): PathProvider {
40 | var sub = to + file.name
41 | if (basePath.startsWith(sub)) if (file.isDirectory) {
42 | sub += '/'
43 | file.listFiles()!!.forEach { addTo(it, sub, mode) }
44 | } else addPath(sub, mode)
45 | return this
46 | }
47 | fun addAt(file: File, at: String = "", mode: Int = 0b110100100): PathProvider {
48 | if (basePath.startsWith(at)) {
49 | if (file.isDirectory) file.listFiles()!!.forEach { addTo(it, at, mode) } else addPath(at, mode)
50 | }
51 | return this
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/common/src/main/java/io/nekohasekai/sagernet/plugin/PluginContract.kt:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * *
3 | * Copyright (C) 2021 by nekohasekai *
4 | * *
5 | * This program is free software: you can redistribute it and/or modify *
6 | * it under the terms of the GNU General Public License as published by *
7 | * the Free Software Foundation, either version 3 of the License, or *
8 | * (at your option) any later version. *
9 | * *
10 | * This program is distributed in the hope that it will be useful, *
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 | * GNU General Public License for more details. *
14 | * *
15 | * You should have received a copy of the GNU General Public License *
16 | * along with this program. If not, see . *
17 | * *
18 | ******************************************************************************/
19 |
20 | package io.nekohasekai.sagernet.plugin
21 |
22 | object PluginContract {
23 |
24 | const val ACTION_NATIVE_PLUGIN = "io.nekohasekai.sagernet.plugin.ACTION_NATIVE_PLUGIN"
25 | const val EXTRA_ENTRY = "io.nekohasekai.sagernet.plugin.EXTRA_ENTRY"
26 | const val METADATA_KEY_ID = "io.nekohasekai.sagernet.plugin.id"
27 | const val METADATA_KEY_EXECUTABLE_PATH = "io.nekohasekai.sagernet.plguin.executable_path"
28 | const val METHOD_GET_EXECUTABLE = "sagernet:getExecutable"
29 |
30 | const val COLUMN_PATH = "path"
31 | const val COLUMN_MODE = "mode"
32 | const val SCHEME = "plugin"
33 | const val AUTHORITY = "io.nekohasekai.sagernet"
34 | }
35 |
--------------------------------------------------------------------------------
/download.sh:
--------------------------------------------------------------------------------
1 | set -e
2 |
3 | mkdir_libs() {
4 | rm -rf "$1"
5 | mkdir "$1"
6 | cd "$1"
7 | mkdir arm64-v8a armeabi-v7a x86 x86_64
8 | }
9 |
10 | unzip_xray() {
11 | rm -rf tmp
12 | unzip -d tmp tmp.zip
13 | mv tmp/xray "$1"/libxray.so
14 | rm -rf tmp tmp.zip
15 | }
16 |
17 | unzip_singbox() {
18 | rm -rf tmp
19 | mkdir tmp
20 | tar -zxvf tmp.tar.gz -C tmp
21 | mv tmp/*/sing-box "$1"/libsingbox.so
22 | rm -rf tmp tmp.tar.gz
23 | }
24 |
25 | unzip_juicity() {
26 | rm -rf tmp
27 | unzip -d tmp tmp.zip
28 | mv tmp/juicity-client "$1"/libjuicity.so
29 | rm -rf tmp tmp.zip
30 | }
31 |
32 | unzip_naive() {
33 | rm -rf tmp
34 | mkdir tmp
35 | tar -xvf naiveproxy.tar.xz -C tmp
36 | mv tmp/*/naive "$1"/libnaive.so
37 | rm -rf tmp naiveproxy.tar.xz
38 | }
39 |
40 | download_xray() {
41 | VERSION="v1.8.6"
42 | mkdir_libs "app_xray/libs"
43 |
44 | dl_and_chmod arm64-v8a/libxray.so "https://github.com/maskedeken/Xray-core/releases/download/$VERSION/xray-android-arm64"
45 | dl_and_chmod armeabi-v7a/libxray.so "https://github.com/maskedeken/Xray-core/releases/download/$VERSION/xray-android-arm"
46 | dl_and_chmod x86_64/libxray.so "https://github.com/maskedeken/Xray-core/releases/download/$VERSION/xray-android-x64"
47 | dl_and_chmod x86/libxray.so "https://github.com/maskedeken/Xray-core/releases/download/$VERSION/xray-android-x86"
48 | }
49 |
50 | download_singbox() {
51 | VERSION="1.3.4"
52 | mkdir_libs "app_singbox/libs"
53 | dl_and_chmod arm64-v8a/libsingbox.so "https://github.com/maskedeken/sing-box/releases/download/v$VERSION/sing-box-android-arm64"
54 | dl_and_chmod armeabi-v7a/libsingbox.so "https://github.com/maskedeken/sing-box/releases/download/v$VERSION/sing-box-android-arm"
55 | }
56 |
57 | download_naive() {
58 | VERSION="116.0.5845.92-2"
59 | mkdir_libs "app_naive/libs"
60 |
61 | curl -Lso naiveproxy.tar.xz "https://github.com/klzgrad/naiveproxy/releases/download/v$VERSION/naiveproxy-v$VERSION-android-arm64.tar.xz"
62 | unzip_naive arm64-v8a
63 | curl -Lso naiveproxy.tar.xz "https://github.com/klzgrad/naiveproxy/releases/download/v$VERSION/naiveproxy-v$VERSION-android-arm.tar.xz"
64 | unzip_naive armeabi-v7a
65 | }
66 |
67 | download_trojan-go() {
68 | VERSION="0.10.15"
69 | mkdir_libs "app_trojan-go/libs"
70 | dl_and_chmod arm64-v8a/libtrojan-go.so "https://github.com/maskedeken/trojan-go/releases/download/v$VERSION/trojan-go-android-arm64"
71 | dl_and_chmod armeabi-v7a/libtrojan-go.so "https://github.com/maskedeken/trojan-go/releases/download/v$VERSION/trojan-go-android-arm"
72 | }
73 |
74 | dl_and_chmod() {
75 | curl -fLso "$1" "$2" && chmod +x "$1"
76 | }
77 |
78 | download_brook() {
79 | VERSION="v20220707"
80 | mkdir_libs "app_brook/libs"
81 |
82 | dl_and_chmod arm64-v8a/libbrook.so "https://github.com/txthinking/brook/releases/download/$VERSION/brook_linux_arm64"
83 | dl_and_chmod armeabi-v7a/libbrook.so "https://github.com/txthinking/brook/releases/download/$VERSION/brook_linux_arm7"
84 | dl_and_chmod x86/libbrook.so "https://github.com/txthinking/brook/releases/download/$VERSION/brook_linux_386"
85 | dl_and_chmod x86_64/libbrook.so "https://github.com/txthinking/brook/releases/download/$VERSION/brook_linux_amd64"
86 | }
87 |
88 | download_hysteria() {
89 | VERSION="v1.3.5-1"
90 | mkdir_libs "app_hysteria/libs"
91 |
92 | dl_and_chmod arm64-v8a/libhysteria.so "https://github.com/MatsuriDayo/hysteria/releases/download/$VERSION/hysteria-linux-arm64"
93 | dl_and_chmod armeabi-v7a/libhysteria.so "https://github.com/MatsuriDayo/hysteria/releases/download/$VERSION/hysteria-linux-arm"
94 | dl_and_chmod x86/libhysteria.so "https://github.com/MatsuriDayo/hysteria/releases/download/$VERSION/hysteria-linux-386"
95 | dl_and_chmod x86_64/libhysteria.so "https://github.com/MatsuriDayo/hysteria/releases/download/$VERSION/hysteria-linux-amd64"
96 | }
97 |
98 | download_tuic() {
99 | mkdir_libs "app_tuic/libs"
100 |
101 | dl_and_chmod arm64-v8a/libtuic.so "https://github.com/MatsuriDayo/tuic/releases/download/rel/tuic-client-0.8.5-2-aarch64-android"
102 | dl_and_chmod x86_64/libtuic.so "https://github.com/MatsuriDayo/tuic/releases/download/rel/tuic-client-0.8.5-2-x86_64-android"
103 | }
104 |
105 | download_tuic5() {
106 | VERSION="1.0.0-3"
107 | mkdir_libs "app_tuic5/libs"
108 |
109 | dl_and_chmod arm64-v8a/libtuic.so "https://github.com/MatsuriDayo/tuic/releases/download/rel/tuic-client-"$VERSION"-aarch64-linux-android"
110 | dl_and_chmod armeabi-v7a/libtuic.so "https://github.com/MatsuriDayo/tuic/releases/download/rel/tuic-client-"$VERSION"-armv7-linux-androideabi"
111 | dl_and_chmod x86/libtuic.so "https://github.com/MatsuriDayo/tuic/releases/download/rel/tuic-client-"$VERSION"-i686-linux-android"
112 | dl_and_chmod x86_64/libtuic.so "https://github.com/MatsuriDayo/tuic/releases/download/rel/tuic-client-"$VERSION"-x86_64-linux-android"
113 | }
114 |
115 | download_juicity() {
116 | VERSION="v0.4.3"
117 | mkdir_libs "app_juicity/libs"
118 |
119 | dl_and_chmod arm64-v8a/libjuicity.so "https://github.com/maskedeken/juicity/releases/download/"$VERSION"/juicity-client-android-arm64"
120 | dl_and_chmod armeabi-v7a/libjuicity.so "https://github.com/maskedeken/juicity/releases/download/"$VERSION"/juicity-client-android-arm"
121 | dl_and_chmod x86/libjuicity.so "https://github.com/maskedeken/juicity/releases/download/"$VERSION"/juicity-client-android-x86"
122 | dl_and_chmod x86_64/libjuicity.so "https://github.com/maskedeken/juicity/releases/download/"$VERSION"/juicity-client-android-x64"
123 | }
124 |
125 | download_"$1"
126 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jan 27 22:42:44 HKT 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/js/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | test*js
3 | test*html
4 | dist
5 |
--------------------------------------------------------------------------------
/js/common/common.js:
--------------------------------------------------------------------------------
1 | export class commomClass {
2 | constructor() {
3 | this.typeMap = {}
4 | }
5 |
6 | getKV(key) {
7 | let jsonStr = neko.getKV(this.typeMap[key], key)
8 | let v = JSON.parse(jsonStr)
9 | return v.v
10 | }
11 | setKV(key, obj) {
12 | let v = { "v": obj }
13 | let jsonStr = JSON.stringify(v)
14 | neko.setKV(this.typeMap[key], key, jsonStr)
15 | }
16 |
17 | _setType(k, type) {
18 | if (type == 'string') {
19 | this.typeMap[k] = 4
20 | } else if (type == 'boolean') {
21 | this.typeMap[k] = 0
22 | } else if (type == 'number') { //TODO int
23 | this.typeMap[k] = 2
24 | // 但是preference大多是不用int的
25 | }
26 | }
27 |
28 | _applyTranslateToPreferenceScreenConfig(sb, TR) {
29 | sb.forEach((category) => {
30 | if (category["title"] == null) {
31 | category["title"] = TR(category["key"])
32 | }
33 | category.preferences.forEach((preference) => {
34 | if (preference["title"] == null) {
35 | preference["title"] = TR(preference["key"])
36 | }
37 | })
38 | })
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/js/common/translate.js:
--------------------------------------------------------------------------------
1 | export var LANG = ""
2 |
3 | export function LANG_TR() {
4 | if (LANG.startsWith("zh-")) return "zh_CN"
5 | return LANG
6 | }
7 |
8 | export function TR(key) {
9 | let transalte_item = translates[key]
10 | if (transalte_item == null) return key // key not exist
11 |
12 | let transalte_current_lang = transalte_item[LANG_TR()]
13 | if (transalte_current_lang == null) {
14 | let transalte_default_lang = transalte_item[""]
15 | if (transalte_default_lang == null) return key
16 | return transalte_default_lang
17 | } else {
18 | return transalte_current_lang
19 | }
20 | }
21 |
22 | Object.prototype.global_debug_setlang = function (lang) { LANG = lang }
23 |
24 | // Translates
25 |
26 | export var translates = {
27 | // v2ray common
28 | serverSettings: {
29 | "zh_CN": "服务器设置",
30 | "": "Server Settings",
31 | },
32 | serverAddress: {
33 | "zh_CN": "服务器",
34 | "": "Server Address",
35 | },
36 | serverPort: {
37 | "zh_CN": "服务器端口",
38 | "": "Server Port",
39 | },
40 | serverUserId: {
41 | "zh_CN": "用户 ID",
42 | "": "UUID",
43 | },
44 | serverEncryption: {
45 | "zh_CN": "加密",
46 | "": "Encryption",
47 | },
48 | serverMethod: {
49 | "zh_CN": "加密方式",
50 | "": "Encryption method",
51 | },
52 | serverPassword: {
53 | "zh_CN": "密码",
54 | "": "Password",
55 | },
56 | serverNetwork: {
57 | "zh_CN": "传输协议",
58 | "": "Network",
59 | },
60 | serverHeader: {
61 | "zh_CN": "伪装类型",
62 | "": "Header type",
63 | },
64 | serverSecurity: {
65 | "zh_CN": "传输层加密",
66 | "": "Transport layer encryption",
67 | },
68 | serverHost_ws: {
69 | "zh_CN": "WebSocket 主机",
70 | "": "WebSocket Host",
71 | },
72 | serverHost_http: {
73 | "zh_CN": "HTTP 主机",
74 | "": "HTTP Host",
75 | },
76 | serverPath: {
77 | "zh_CN": "路径",
78 | "": "Path",
79 | },
80 | serverPath_ws: {
81 | "zh_CN": "WebSocket 路径",
82 | "": "WebSocket Path",
83 | },
84 | serverPath_http: {
85 | "zh_CN": "HTTP 路径",
86 | "": "HTTP Path",
87 | },
88 | serverPath_mkcp: {
89 | "zh_CN": "mKCP 混淆密码",
90 | "": "mKCP Seed",
91 | },
92 | serverPath_quic: {
93 | "zh_CN": "QUIC 密钥",
94 | "": "QUIC Key",
95 | },
96 | serverPath_grpc: {
97 | "zh_CN": "gRPC 服务名称",
98 | "": "gRPC ServiceName",
99 | },
100 | serverQuicSecurity: {
101 | "zh_CN": "QUIC 加密方式",
102 | "": "QUIC Security",
103 | },
104 | grpcMultiMode: {
105 | "": "gRPC Multi Mode"
106 | },
107 | grpcMultiMode_summary: {
108 | "zh_CN": "这是一个 实验性 选项,可能不会被长期保留,也不保证跨版本兼容。此模式在 测试环境中 能够带来约 20% 的性能提升,实际效果因传输速率不同而不同。",
109 | "": "This is an experimental option",
110 | },
111 |
112 | // tls
113 | serverSecurityCategory: {
114 | "zh_CN": "安全设置",
115 | "": "Security Settings",
116 | },
117 | serverSNI: {
118 | "zh_CN": "服务器名称指示",
119 | "": "SNI",
120 | },
121 | utlsEnabled: {
122 | "zh_CN": "uTLS 开关",
123 | "": "uTLS Enabled",
124 | },
125 | utlsFingerprint: {
126 | "zh_CN": "uTLS 指纹",
127 | "": "uTLS Fingerprint",
128 | },
129 | serverALPN: {
130 | "zh_CN": "应用层协议协商",
131 | "": "ALPN",
132 | },
133 | serverCertificates: {
134 | "zh_CN": "证书(链)",
135 | "": "Certificate (chain)",
136 | },
137 | serverFlow: {
138 | "zh_CN": "流控",
139 | "": "Flow Control",
140 | },
141 | serverFlowVision: {
142 | "zh_CN": "流控",
143 | "": "Flow Control",
144 | },
145 | serverAllowInsecure: {
146 | "zh_CN": "允许不安全的连接",
147 | "": "Allow insecure",
148 | },
149 | serverAllowInsecure_summary: {
150 | "zh_CN": "禁用证书检查. 启用后该配置安全性相当于明文",
151 | "": "Disable certificate checking. When enabled, this configuration is as secure as plaintext.",
152 | },
153 | insecure_cleartext: {
154 | "zh_CN": "该配置 (不安全) 能够被检测识别,传输的内容对审查者完全可见,并且无法抵抗中间人篡改通讯内容.",
155 | "": "The configuration (insecure) can be detected and identified, the transmission is fully visible to the censor and is not resistant to man-in-the-middle tampering with the content of the communication.",
156 | },
157 | serverUoT: {
158 | "": "UDP over TCP",
159 | },
160 | publicKey: {
161 | "": "PublicKey",
162 | },
163 | shortId: {
164 | "": "ShortId",
165 | },
166 | spiderX: {
167 | "": "SpiderX",
168 | },
169 | reducedIvHeadEntropy: {
170 | "": "ReducedIvHeadEntropy",
171 | },
172 | reducedIvHeadEntropy_summary: {
173 | "zh_CN": "这是 GFWReport 针对 GFW 上观察到的类似随机流的协议阻塞行为提出的对策。 这个选项将 IV 的前 6 个字节重新映射为可打印的字符,启用后,特权网络路径上的任何人都有可能识别该协议。",
174 | "": "This is GFWReport's proposal for a countermeasure for the random stream like protocol blocking behaviour observed on GFW. This option remap the first 6 bytes of IV to printable characters, it's possible for anyone on the privileged network path to identify the protocol when enabled.",
175 | },
176 |
177 | // Brook
178 | serverProtocol: {
179 | "": "Protocol",
180 | "zh_CN": "协议",
181 | },
182 | withoutBrookProtocol: {
183 | "": "Without Brook Protocol"
184 | },
185 | withoutBrookProtocol_summary: {
186 | "zh_CN": "不使用 Brook 协议",
187 | "": "Don't use Brook protocol",
188 | },
189 | udpovertcp: {
190 | "": "UDP over TCP",
191 | },
192 |
193 | // shadowTLS
194 | shadowTlsServerName: {
195 | "zh_CN": "ShadowTLS 伪装域名",
196 | "": "ShadowTLS Server Name",
197 | },
198 | shadowTlsVersion: {
199 | "zh_CN": "ShadowTLS 版本",
200 | "": "ShadowTLS Version",
201 | },
202 | shadowTlsServerPassword: {
203 | "zh_CN": "ShadowTLS 密码",
204 | "": "ShadowTLS Password",
205 | },
206 |
207 | // WireGurad
208 | wireguardLocalAddress: {
209 | "zh_CN": "本地地址",
210 | "": "Local Address",
211 | },
212 | wireguardPrivateKey: {
213 | "zh_CN": "私钥",
214 | "": "Private Key",
215 | },
216 | wireguardCertificates: {
217 | "zh_CN": "节点公钥",
218 | "": "Peer Public Key",
219 | },
220 | wireguardPeerPreSharedKey: {
221 | "zh_CN": "节点预共享密钥",
222 | "": "Peer Pre-Shared Key",
223 | },
224 | wireguardMTU: {
225 | "": "MTU",
226 | },
227 | wireguardDNS: {
228 | "": "DNS",
229 | },
230 | }
231 |
--------------------------------------------------------------------------------
/js/common/util.js:
--------------------------------------------------------------------------------
1 | import { Base64 } from 'js-base64'
2 |
3 | //fuck Chrome, force use core-js URL
4 | const configurator = require('core-js/configurator');
5 | configurator({
6 | usePolyfill: ['URL'], // polyfills will be used anyway
7 | });
8 | require('core-js/actual/url')
9 |
10 | class utilClass {
11 | newURL(protocol) {
12 | return new URL(protocol + "://donotexist/")
13 | }
14 |
15 | tryParseURL(str) {
16 | try {
17 | return new URL(str)
18 | } catch (error) {
19 | return error.toString()
20 | }
21 | }
22 |
23 | isBlank(str) {
24 | return (!str || /^\s*$/.test(str));
25 | }
26 |
27 | isNotBlank(str) {
28 | return !this.isBlank(str);
29 | }
30 |
31 | isPureIp(str) {
32 | return this.isIpv4(str) || this.isIpv6(str);
33 | }
34 |
35 | isIpv4(str) {
36 | const regexExp = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/;
37 | return regexExp.test(str);
38 | }
39 |
40 | isIpv6(str) {
41 | const regexExp = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/gi;
42 | return regexExp.test(str);
43 | }
44 |
45 | wrapUri(addr, port) {
46 | if (this.isIpv6(addr)) {
47 | return "[" + addr + "]" + ":" + port
48 | } else {
49 | return addr + ":" + port
50 | }
51 | }
52 |
53 | unwrapIpv6(addr) {
54 | if (addr.startsWith("[") && addr.endsWith("]")) {
55 | return addr.replace("[", "").replace("]", "")
56 | }
57 | return addr
58 | }
59 |
60 | addSplash(str) {
61 | if (str.startsWith("/")) {
62 | return str
63 | } else {
64 | return "/" + str
65 | }
66 | }
67 |
68 | ifNotNull(any, callback) {
69 | if (any != null) callback(any)
70 | }
71 |
72 | decodeB64Str(b64Str) {
73 | try {
74 | let jsonStr = Base64.decode(b64Str)
75 | return JSON.parse(jsonStr)
76 | } catch (error) {
77 | return {}
78 | }
79 | }
80 |
81 | encodeB64Str(obj) {
82 | return Base64.encode(JSON.stringify(obj), true)
83 | }
84 |
85 | }
86 |
87 | export const util = new utilClass();
88 | try {
89 | window.util = util
90 | } catch (error) {
91 |
92 | }
93 |
94 | // add utils to prototype
95 |
96 | Object.prototype.global_export = function (name, f) {
97 | Object.prototype[name] = f
98 | }
99 |
100 | String.prototype.contains = function (s) { return this.indexOf(s) >= 0 }
101 | String.prototype.isBlank = function () { return util.isBlank(this) }
102 | String.prototype.isNotBlank = function () { return util.isNotBlank(this) }
103 | String.prototype.isIpv4 = function () { return util.isIpv4(this) }
104 | String.prototype.isIpv6 = function () { return util.isIpv6(this) }
105 | String.prototype.isPureIp = function () { return util.isPureIp(this) }
106 | String.prototype.substringBefore = function (s) {
107 | if (!this.contains(s)) return this
108 | return this.substring(0, this.indexOf(s))
109 | }
110 | String.prototype.substringAfter = function (s) {
111 | if (!this.contains(s)) return this
112 | return this.substring(this.indexOf(s) + s.length)
113 | }
114 | String.prototype.toInt = function () { return parseInt(this) }
115 | String.prototype.firstLine = function () { return this.split("\n")[0] }
116 | String.prototype.lines = function () { return this.split("\n") }
117 |
--------------------------------------------------------------------------------
/js/make.sh:
--------------------------------------------------------------------------------
1 | set -e
2 |
3 | # moe.matsur.exe.* -> no js build
4 | [ $1 == "hysteria" ] && exit
5 | [ $1 == "tuic" ] && exit
6 | [ $1 == "naive" ] && exit
7 | [ $1 == "trojan-go" ] && exit
8 | [ $1 == "tuic5" ] && exit
9 |
10 | HTML=../app_$1/html
11 | SRC=./plugin_$1
12 |
13 | webpack --entry "$SRC"/main.js
14 |
15 | rm -rf "$HTML"
16 | mkdir -p "$HTML"
17 | cp plugin.html "$HTML"
18 | cp dist/p.js "$HTML"
19 |
--------------------------------------------------------------------------------
/js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "test.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "@babel/core": "^7.16.12",
13 | "@babel/preset-env": "^7.16.11",
14 | "babel-loader": "^8.2.3",
15 | "webpack-cli": "^4.9.2"
16 | },
17 | "dependencies": {
18 | "core-js": "^3.20.3",
19 | "js-base64": "^3.7.2"
20 | },
21 | "browserslist": [
22 | "chrome > 30"
23 | ]
24 | }
--------------------------------------------------------------------------------
/js/plugin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Webpack App
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/js/plugin_brook/brook.js:
--------------------------------------------------------------------------------
1 | import { util } from "../common/util.js";
2 | import { commomClass } from "../common/common.js"
3 | import { TR } from "../common/translate.js"
4 |
5 | class brookClass {
6 | constructor() {
7 | this.sharedStorage = {}
8 | this.defaultSharedStorage = {}
9 | this.common = new commomClass()
10 | }
11 |
12 | _initDefaultSharedStorage() {
13 | // start of default keys
14 | this.defaultSharedStorage.jsVersion = 1
15 | this.defaultSharedStorage.name = ""
16 | this.defaultSharedStorage.serverAddress = "127.0.0.1"
17 | this.defaultSharedStorage.serverPort = "1080"
18 | // end of default keys
19 | this.defaultSharedStorage.serverProtocol = "default"
20 | this.defaultSharedStorage.serverPassword = ""
21 | this.defaultSharedStorage.serverPath = ""
22 | this.defaultSharedStorage.serverAllowInsecure = false
23 | this.defaultSharedStorage.withoutBrookProtocol = false
24 | this.defaultSharedStorage.udpovertcp = false
25 |
26 |
27 | for (var k in this.defaultSharedStorage) {
28 | let v = this.defaultSharedStorage[k]
29 | this.common._setType(k, typeof v)
30 |
31 | if (!this.sharedStorage.hasOwnProperty(k)) {
32 | this.sharedStorage[k] = v
33 | }
34 | }
35 |
36 | }
37 |
38 | _onSharedStorageUpdated() {
39 | // not null
40 | for (var k in this.sharedStorage) {
41 | if (this.sharedStorage[k] == null) {
42 | this.sharedStorage[k] = ""
43 | }
44 | }
45 | this._setShareLink()
46 | }
47 |
48 | _setShareLink() {
49 | var builder = util.newURL("brook")
50 |
51 | var serverString = util.wrapUri(this.sharedStorage.serverAddress, this.sharedStorage.serverPort)
52 | var wsPath = this.sharedStorage.serverPath
53 |
54 | if (this.sharedStorage.serverProtocol.startsWith("ws")) {
55 | if (wsPath.isNotBlank() && wsPath != "/") {
56 | if (!wsPath.startsWith("/")) wsPath = "/" + wsPath
57 | serverString += wsPath
58 | }
59 | }
60 |
61 | switch (this.sharedStorage.serverProtocol) {
62 | case "ws": {
63 | builder.host = "wsserver"
64 | builder.searchParams.set("wsserver", serverString)
65 | break
66 | }
67 | case "wss": {
68 | builder.host = "wssserver"
69 | builder.searchParams.set("wssserver", serverString)
70 | break
71 | }
72 | default: {
73 | builder.host = "server"
74 | builder.searchParams.set("server", serverString)
75 | }
76 | }
77 |
78 | if (this.sharedStorage.serverPassword.isNotBlank()) {
79 | builder.searchParams.set("password", this.sharedStorage.serverPassword)
80 | }
81 | if (this.sharedStorage.name.isNotBlank()) {
82 | builder.searchParams.set("remarks", this.sharedStorage.name)
83 | }
84 |
85 | this.sharedStorage.shareLink = builder.toString()
86 | }
87 |
88 | // UI Interface
89 |
90 | requirePreferenceScreenConfig() {
91 | let sb = [
92 | {
93 | "title": TR("serverSettings"),
94 | "preferences": [
95 | {
96 | "type": "EditTextPreference",
97 | "key": "serverAddress",
98 | "icon": "ic_hardware_router",
99 | },
100 | {
101 | "type": "EditTextPreference",
102 | "key": "serverPort",
103 | "icon": "ic_maps_directions_boat",
104 | "EditTextPreferenceModifiers": "Port",
105 | },
106 | {
107 | "type": "SimpleMenuPreference",
108 | "key": "serverProtocol",
109 | "icon": "ic_baseline_compare_arrows_24",
110 | "entries": {
111 | "default": "DEFAULT",
112 | "ws": "WS",
113 | "wss": "WSS",
114 | }
115 | },
116 | {
117 | "type": "EditTextPreference",
118 | "key": "serverPassword",
119 | "icon": "ic_settings_password",
120 | "summaryProvider": "PasswordSummaryProvider",
121 | },
122 | {
123 | "type": "EditTextPreference",
124 | "key": "serverPath",
125 | "title": TR("serverPath_ws"),
126 | "icon": "ic_baseline_format_align_left_24",
127 | },
128 | {
129 | "type": "SwitchPreference",
130 | "key": "serverAllowInsecure",
131 | "icon": "ic_baseline_warning_24",
132 | "summary": TR("serverAllowInsecure_summary")
133 | },
134 | {
135 | "type": "SwitchPreference",
136 | "key": "withoutBrookProtocol",
137 | "icon": "baseline_public_24",
138 | "summary": TR("withoutBrookProtocol_summary")
139 | },
140 | {
141 | "type": "SwitchPreference",
142 | "key": "udpovertcp",
143 | "icon": "baseline_wrap_text_24",
144 | },
145 | ]
146 | },
147 | ]
148 | this.common._applyTranslateToPreferenceScreenConfig(sb, TR)
149 | return JSON.stringify(sb)
150 | }
151 |
152 | // 开启设置界面时调用
153 | setSharedStorage(b64Str) {
154 | this.sharedStorage = util.decodeB64Str(b64Str)
155 | this._initDefaultSharedStorage()
156 | }
157 |
158 | // 开启设置界面时调用
159 | requireSetProfileCache() {
160 | for (var k in this.defaultSharedStorage) {
161 | this.common.setKV(k, this.sharedStorage[k])
162 | }
163 | }
164 |
165 | // 设置界面创建后调用
166 | onPreferenceCreated() {
167 | let this2 = this
168 |
169 | function listenOnPreferenceChangedNow(key) {
170 | neko.listenOnPreferenceChanged(key)
171 | this2._onPreferenceChanged(key, this2.sharedStorage[key])
172 | }
173 |
174 | listenOnPreferenceChangedNow("serverProtocol")
175 | }
176 |
177 | // 保存时调用(混合编辑后的值)
178 | sharedStorageFromProfileCache() {
179 | for (var k in this.defaultSharedStorage) {
180 | this.sharedStorage[k] = this.common.getKV(k)
181 | }
182 | this._onSharedStorageUpdated()
183 | return JSON.stringify(this.sharedStorage)
184 | }
185 |
186 | onPreferenceChanged(b64Str) {
187 | let args = util.decodeB64Str(b64Str)
188 | this._onPreferenceChanged(args.key, args.newValue)
189 | }
190 |
191 | _onPreferenceChanged(key, newValue) {
192 | if (key == "serverProtocol") {
193 | if (newValue == "wss") {
194 | neko.setPreferenceVisibility("serverAllowInsecure", true)
195 | } else {
196 | neko.setPreferenceVisibility("serverAllowInsecure", false)
197 | }
198 | if (newValue == "default") {
199 | neko.setPreferenceVisibility("serverPath", false)
200 | neko.setPreferenceVisibility("withoutBrookProtocol", false)
201 | neko.setPreferenceVisibility("udpovertcp", true)
202 | } else {
203 | neko.setPreferenceVisibility("serverPath", true)
204 | neko.setPreferenceVisibility("withoutBrookProtocol", true)
205 | neko.setPreferenceVisibility("udpovertcp", false)
206 | }
207 | }
208 | }
209 |
210 | // Interface
211 |
212 | parseShareLink(b64Str) {
213 | let args = util.decodeB64Str(b64Str)
214 |
215 | this.sharedStorage = {}
216 | this._initDefaultSharedStorage()
217 |
218 | var link = util.tryParseURL(args.shareLink)
219 | if (typeof link == "string") return link
220 |
221 | this.sharedStorage.name = link.searchParams.get("remarks")
222 |
223 | switch (link.host) {
224 | case "server": {
225 | this.sharedStorage.serverProtocol = "default"
226 |
227 | var server = link.searchParams.get("server")
228 | if (server == null) return "Invalid brook server url (Missing server parameter)"
229 |
230 | this.sharedStorage.serverAddress = server.substringBefore(":")
231 | this.sharedStorage.serverPort = server.substringAfter(":").toInt()
232 | var password = link.searchParams.get("password")
233 | if (password == null) ("Invalid brook server url (Missing password parameter)")
234 |
235 | this.sharedStorage.serverPassword = password
236 | break
237 | }
238 | case "wsserver": {
239 | this.sharedStorage.serverProtocol = "ws"
240 |
241 | var wsserver = link.searchParams.get("wsserver")
242 | if (wsserver == null) return "Invalid brook wsserver url (Missing wsserver parameter)"
243 |
244 | wsserver = wsserver.substringAfter("://")
245 |
246 | if (wsserver.contains("/")) {
247 | this.sharedStorage.serverPath = "/" + wsserver.substringAfter("/")
248 | wsserver = wsserver.substringBefore("/")
249 | }
250 |
251 | this.sharedStorage.serverAddress = wsserver.substringBefore(":")
252 | this.sharedStorage.serverPort = wsserver.substringAfter(":").toInt()
253 |
254 | var password = link.searchParams.get("password")
255 | if (password == null) ("Invalid brook wsserver url (Missing password parameter)")
256 |
257 | this.sharedStorage.serverPassword = password
258 | break
259 | }
260 | case "wssserver": {
261 | this.sharedStorage.serverProtocol = "wss"
262 |
263 | var wsserver = link.searchParams.get("wssserver")
264 | if (wsserver == null) return "Invalid brook wssserver url (Missing wssserver parameter)"
265 |
266 | wsserver = wsserver.substringAfter("://")
267 |
268 | if (wsserver.contains("/")) {
269 | this.sharedStorage.serverPath = "/" + wsserver.substringAfter("/")
270 | wsserver = wsserver.substringBefore("/")
271 | }
272 |
273 | this.sharedStorage.serverAddress = wsserver.substringBefore(":")
274 | this.sharedStorage.serverPort = wsserver.substringAfter(":").toInt()
275 |
276 | var password = link.searchParams.get("password")
277 | if (password == null) ("Invalid brook wssserver url (Missing password parameter)")
278 |
279 | this.sharedStorage.serverPassword = password
280 | break
281 | }
282 | }
283 |
284 | this._onSharedStorageUpdated()
285 | return JSON.stringify(this.sharedStorage)
286 | }
287 |
288 | buildAllConfig(b64Str) {
289 | try {
290 | let args = util.decodeB64Str(b64Str)
291 | let cs = util.decodeB64Str(args.sharedStorage)
292 |
293 | console.log(args, cs)
294 |
295 | let v = {}
296 | v.nekoCommands = ["%exe%"]
297 |
298 | switch (cs.serverProtocol) {
299 | case "ws": {
300 | v.nekoCommands.push("wsclient")
301 | v.nekoCommands.push("--wsserver")
302 | break
303 | }
304 | case "wss": {
305 | v.nekoCommands.push("wssclient")
306 | v.nekoCommands.push("--wssserver")
307 | break
308 | }
309 | default: {
310 | v.nekoCommands.push("client")
311 | v.nekoCommands.push("--server")
312 | }
313 | }
314 |
315 | let internalUri = util.wrapUri(args.finalAddress, args.finalPort)
316 |
317 | switch (cs.serverProtocol) {
318 | case "ws": {
319 | internalUri = "ws://" + util.wrapUri(cs.serverAddress, args.finalPort)
320 | break
321 | }
322 | case "wss": {
323 | internalUri = "wss://" + util.wrapUri(cs.serverAddress, args.finalPort)
324 | break
325 | }
326 | }
327 |
328 | if (cs.serverPath.isNotBlank()) {
329 | if (!cs.serverPath.startsWith("/")) {
330 | internalUri += "/"
331 | }
332 | internalUri += cs.serverPath
333 | }
334 |
335 | v.nekoCommands.push(internalUri)
336 |
337 | if (cs.serverProtocol.startsWith("ws")) {
338 | if (cs.withoutBrookProtocol) {
339 | v.nekoCommands.push("--withoutBrookProtocol")
340 | }
341 | if (cs.serverProtocol == "wss" && cs.serverAllowInsecure) {
342 | v.nekoCommands.push("--insecure")
343 | }
344 | // Mapping
345 | v.nekoCommands.push("--address")
346 | v.nekoCommands.push(util.wrapUri(args.finalAddress, args.finalPort))
347 | } else {
348 | if (cs.udpovertcp) {
349 | v.nekoCommands.push("--udpovertcp")
350 | }
351 | }
352 |
353 | if (cs.serverPassword.isNotBlank()) {
354 | v.nekoCommands.push("--password")
355 | v.nekoCommands.push(cs.serverPassword)
356 | }
357 |
358 | v.nekoCommands.push("--socks5")
359 | v.nekoCommands.push("127.0.0.1:" + args.port)
360 |
361 | return JSON.stringify(v)
362 | } catch (error) {
363 | neko.logError(error.toString())
364 | }
365 | }
366 | }
367 |
368 | export const brook = new brookClass()
369 |
--------------------------------------------------------------------------------
/js/plugin_brook/main.js:
--------------------------------------------------------------------------------
1 | import { util } from "../common/util.js"
2 | import { brook } from "./brook.js"
3 | import { LANG } from "../common/translate.js"
4 |
5 | // Init
6 |
7 | export function nekoInit(b64Str) {
8 | let args = util.decodeB64Str(b64Str)
9 |
10 | //TODO
11 | console.log(args)
12 |
13 | LANG = args.lang
14 |
15 | let plgConfig = {
16 | "ok": true,
17 | "reason": "",
18 | "minVersion": 1,
19 | "protocols": [
20 | {
21 | "protocolId": "brook",
22 | "links": ["brook://"],
23 | "haveStandardLink": true,
24 | "canShare": true,
25 | "canMux": false,
26 | "canMapping": true,
27 | "canTCPing": true,
28 | "canICMPing": true,
29 | "needBypassRootUid": false,
30 | }
31 | ]
32 | }
33 | return JSON.stringify(plgConfig)
34 | }
35 |
36 | export function nekoProtocol(protocolId) {
37 | if (protocolId == "brook") {
38 | return brook
39 | }
40 | }
41 |
42 | // export interface to browser
43 | global_export("nekoInit", nekoInit)
44 | global_export("nekoProtocol", nekoProtocol)
45 |
--------------------------------------------------------------------------------
/js/plugin_juicity/juicity.js:
--------------------------------------------------------------------------------
1 | import { util } from "../common/util.js";
2 | import { commomClass } from "../common/common.js";
3 | import { TR } from "../common/translate.js";
4 |
5 | class juicityClass {
6 | constructor() {
7 | this.sharedStorage = {};
8 | this.defaultSharedStorage = {};
9 | this.common = new commomClass();
10 | }
11 |
12 | _initDefaultSharedStorage() {
13 | // start of default keys
14 | this.defaultSharedStorage.jsVersion = 1;
15 | this.defaultSharedStorage.name = "";
16 | this.defaultSharedStorage.serverAddress = "127.0.0.1";
17 | this.defaultSharedStorage.serverPort = "1080";
18 | // end of default keys
19 | this.defaultSharedStorage.uuid = "00000000-0000-0000-0000-000000000000";
20 | this.defaultSharedStorage.password = "";
21 | this.defaultSharedStorage.sni = "";
22 | this.defaultSharedStorage.allowInsecure = false;
23 | this.defaultSharedStorage.congestionControl = "bbr";
24 |
25 | for (var k in this.defaultSharedStorage) {
26 | let v = this.defaultSharedStorage[k];
27 | this.common._setType(k, typeof v);
28 |
29 | if (!this.sharedStorage.hasOwnProperty(k)) {
30 | this.sharedStorage[k] = v;
31 | }
32 | }
33 | }
34 |
35 | _onSharedStorageUpdated() {
36 | // not null
37 | for (var k in this.sharedStorage) {
38 | if (this.sharedStorage[k] == null) {
39 | this.sharedStorage[k] = "";
40 | }
41 | }
42 | this._setShareLink();
43 | }
44 |
45 | // 生成并存储 share link
46 | _setShareLink() {
47 | var builder = util.newURL("juicity")
48 | if (this.sharedStorage.name.isNotBlank()) builder.hash = "#" + encodeURIComponent(this.sharedStorage.name)
49 | builder.host = util.wrapUri(this.sharedStorage.serverAddress, this.sharedStorage.serverPort)
50 | builder.username = this.sharedStorage.uuid
51 | builder.password = this.sharedStorage.password
52 | if (this.sharedStorage.congestionControl.isNotBlank()) {
53 | builder.searchParams.set("congestion_control", this.sharedStorage.congestionControl)
54 | }
55 | if (this.sharedStorage.sni.isNotBlank()) {
56 | builder.searchParams.set("sni", this.sharedStorage.sni)
57 | }
58 | if (this.sharedStorage.allowInsecure) {
59 | builder.searchParams.set("allow_insecure", "1")
60 | }
61 | this.sharedStorage.shareLink = builder.toString()
62 | }
63 |
64 | // UI Interface
65 |
66 | requirePreferenceScreenConfig() {
67 | let sb = [
68 | {
69 | title: TR("serverSettings"),
70 | preferences: [
71 | {
72 | type: "EditTextPreference",
73 | key: "serverAddress",
74 | icon: "ic_hardware_router",
75 | },
76 | {
77 | type: "EditTextPreference",
78 | key: "serverPort",
79 | icon: "ic_maps_directions_boat",
80 | EditTextPreferenceModifiers: "Port",
81 | },
82 | //
83 | {
84 | "type": "EditTextPreference",
85 | "key": "uuid",
86 | "icon": "ic_baseline_person_24",
87 | },
88 | {
89 | "type": "EditTextPreference",
90 | "key": "password",
91 | "icon": "ic_settings_password",
92 | "summaryProvider": "PasswordSummaryProvider",
93 | },
94 | //
95 | {
96 | "type": "EditTextPreference",
97 | "key": "sni",
98 | "icon": "ic_action_copyright"
99 | },
100 | {
101 | "type": "SwitchPreference",
102 | "key": "allowInsecure",
103 | "icon": "ic_notification_enhanced_encryption",
104 | },
105 | {
106 | "type": "EditTextPreference",
107 | "key": "congestionControl",
108 | "icon": "ic_baseline_stream_24",
109 | },
110 | ],
111 | },
112 | ];
113 | this.common._applyTranslateToPreferenceScreenConfig(sb, TR);
114 | return JSON.stringify(sb);
115 | }
116 |
117 | // 开启设置界面时调用
118 | setSharedStorage(b64Str) {
119 | this.sharedStorage = util.decodeB64Str(b64Str);
120 | this._initDefaultSharedStorage();
121 | }
122 |
123 | // 开启设置界面时调用
124 | requireSetProfileCache() {
125 | for (var k in this.defaultSharedStorage) {
126 | this.common.setKV(k, this.sharedStorage[k]);
127 | }
128 | }
129 | // 开启设置界面时调用
130 |
131 | // 设置界面创建后调用
132 | onPreferenceCreated() {
133 | let this2 = this
134 |
135 | function listenOnPreferenceChangedNow(key) {
136 | neko.listenOnPreferenceChanged(key)
137 | this2._onPreferenceChanged(key, this2.sharedStorage[key])
138 | }
139 | }
140 |
141 | // 保存时调用(混合编辑后的值)
142 | sharedStorageFromProfileCache() {
143 | for (var k in this.defaultSharedStorage) {
144 | this.sharedStorage[k] = this.common.getKV(k);
145 | }
146 | this._onSharedStorageUpdated();
147 | return JSON.stringify(this.sharedStorage);
148 | }
149 |
150 | // 用户修改 preference 时调用
151 | onPreferenceChanged(b64Str) {
152 | let args = util.decodeB64Str(b64Str)
153 | this._onPreferenceChanged(args.key, args.newValue)
154 | }
155 |
156 | _onPreferenceChanged(key, newValue) {
157 | }
158 |
159 | // Interface
160 |
161 | parseShareLink(b64Str) {
162 | let args = util.decodeB64Str(b64Str)
163 |
164 | this.sharedStorage = {}
165 | this._initDefaultSharedStorage()
166 |
167 | var url = util.tryParseURL(args.shareLink)
168 | if (typeof url == "string") return url // error string
169 |
170 | var serverAddress = util.unwrapIpv6(url.hostname)
171 | this.sharedStorage.serverAddress = serverAddress
172 | this.sharedStorage.serverPort = url.host.replace(serverAddress, "").substringAfter(":")
173 | this.sharedStorage.uuid = url.username
174 | this.sharedStorage.password = url.password
175 | this.sharedStorage.name = decodeURIComponent(url.hash.substringAfter("#"))
176 |
177 | util.ifNotNull(url.searchParams.get("congestion_control"), (it) => {
178 | this.sharedStorage.congestionControl = it
179 | })
180 | util.ifNotNull(url.searchParams.get("sni"), (it) => {
181 | this.sharedStorage.sni = it
182 | })
183 | util.ifNotNull(url.searchParams.get("allow_insecure"), (it) => {
184 | if (it == "1" || it == "true") this.sharedStorage.allowInsecure = it
185 | })
186 |
187 | this._onSharedStorageUpdated()
188 | return JSON.stringify(this.sharedStorage)
189 | }
190 |
191 | buildAllConfig(b64Str) {
192 | try {
193 | let args = util.decodeB64Str(b64Str);
194 | let ss = util.decodeB64Str(args.sharedStorage);
195 |
196 | let configObject = {
197 | "listen": "127.0.0.1:" + args.port,
198 | "server": util.wrapUri(args.finalAddress, args.finalPort),
199 | "uuid": ss.uuid,
200 | "password": ss.password,
201 | "allow_insecure": ss.allowInsecure,
202 | "congestion_control": ss.congestionControl,
203 | "log_level": "info"
204 | };
205 | if (ss.sni.isNotBlank()) {
206 | configObject["sni"] = ss.sni
207 | } else if (!ss.serverAddress.isPureIp()) {
208 | configObject["sni"] = ss.serverAddress
209 | }
210 |
211 | let v = {};
212 | v.nekoCommands = ["%exe%", "run", "-c", "config.json"];
213 | v.nekoRunConfigs = [
214 | {
215 | name: "config.json",
216 | content: JSON.stringify(configObject),
217 | },
218 | ];
219 | return JSON.stringify(v);
220 | } catch (error) {
221 | neko.logError(error.toString());
222 | }
223 | }
224 | }
225 |
226 | export const juicity = new juicityClass();
227 |
--------------------------------------------------------------------------------
/js/plugin_juicity/main.js:
--------------------------------------------------------------------------------
1 | import { util } from "../common/util.js";
2 | import { LANG, LANG_TR } from "../common/translate.js";
3 |
4 | import { juicity } from "./juicity.js";
5 |
6 | // Init
7 |
8 | export function nekoInit(b64Str) {
9 | let args = util.decodeB64Str(b64Str);
10 |
11 | LANG = args.lang;
12 |
13 | let plgConfig = {
14 | ok: true,
15 | reason: "",
16 | minVersion: 2,
17 | protocols: [
18 | {
19 | protocolId: "Juicity",
20 | links: ["juicity://"],
21 | haveStandardLink: true,
22 | canShare: true,
23 | canMux: false,
24 | canMapping: true,
25 | canTCPing: false,
26 | canICMPing: true,
27 | needBypassRootUid: false,
28 | }
29 | ],
30 | };
31 | return JSON.stringify(plgConfig);
32 | }
33 |
34 | export function nekoProtocol(protocolId) {
35 | if (protocolId == "Juicity") {
36 | return juicity;
37 | }
38 | }
39 |
40 | export function nekoAbout() {
41 | return "早期测试版本,上游版本 v0.3.0\n" +
42 | "1 目前不兼容链式代理\n" +
43 | "这个插件是实验性的。如果在使用过程中遇到任何问题,请自行解决。"
44 | }
45 |
46 | // export interface to browser
47 | global_export("nekoInit", nekoInit)
48 | global_export("nekoProtocol", nekoProtocol)
49 | global_export("nekoAbout", nekoAbout)
50 |
--------------------------------------------------------------------------------
/js/plugin_singbox/main.js:
--------------------------------------------------------------------------------
1 | import { util } from "../common/util.js";
2 | import { LANG, LANG_TR } from "../common/translate.js";
3 |
4 | import { shadowTls } from "./shadowtls.js";
5 | import { wireguard } from "./wireguard.js";
6 |
7 | // Init
8 |
9 | export function nekoInit(b64Str) {
10 | let args = util.decodeB64Str(b64Str);
11 |
12 | LANG = args.lang;
13 |
14 | let plgConfig = {
15 | ok: true,
16 | reason: "",
17 | minVersion: 2,
18 | protocols: [
19 | {
20 | protocolId: "ShadowTLS",
21 | haveStandardLink: false,
22 | canShare: false,
23 | canMux: false,
24 | canMapping: true,
25 | canTCPing: true,
26 | canICMPing: true,
27 | needBypassRootUid: false,
28 | },
29 | {
30 | protocolId: "WireGuard",
31 | haveStandardLink: false,
32 | canShare: false,
33 | canMux: false,
34 | canMapping: true,
35 | canTCPing: false,
36 | canICMPing: true,
37 | needBypassRootUid: false,
38 | },
39 | ],
40 | };
41 | return JSON.stringify(plgConfig);
42 | }
43 |
44 | export function nekoProtocol(protocolId) {
45 | if (protocolId == "ShadowTLS") {
46 | return shadowTls;
47 | } else if (protocolId == "WireGuard") {
48 | return wireguard;
49 | }
50 | }
51 |
52 | export function nekoAbout() {
53 | switch (LANG_TR()) {
54 | case "zh_CN":
55 | return "作者:杰洛特\n" +
56 | "GitHub: https://github.com/mliyuanbiao\n" +
57 | "这个插件是实验性的。如果在使用过程中遇到任何问题,请自行解决。"
58 | default:
59 | return "Author: 杰洛特\n" +
60 | "GitHub: https://github.com/mliyuanbiao\n" +
61 | "This plugin is experimental. If you have any problems during use, please solve them yourself."
62 | }
63 | }
64 |
65 | // export interface to browser
66 | global_export("nekoInit", nekoInit)
67 | global_export("nekoProtocol", nekoProtocol)
68 | global_export("nekoAbout", nekoAbout)
69 |
--------------------------------------------------------------------------------
/js/plugin_singbox/shadowtls.js:
--------------------------------------------------------------------------------
1 | import { util } from "../common/util.js";
2 | import { commomClass } from "../common/common.js";
3 | import { TR } from "../common/translate.js";
4 |
5 | class shadowTlsClass {
6 | constructor() {
7 | this.sharedStorage = {};
8 | this.defaultSharedStorage = {};
9 | this.common = new commomClass();
10 | }
11 |
12 | _initDefaultSharedStorage() {
13 | // start of default keys
14 | this.defaultSharedStorage.jsVersion = 1;
15 | this.defaultSharedStorage.name = "";
16 | this.defaultSharedStorage.serverAddress = "127.0.0.1";
17 | this.defaultSharedStorage.serverPort = "1080";
18 | // end of default keys
19 | this.defaultSharedStorage.serverMethod = "2022-blake3-aes-128-gcm";
20 | this.defaultSharedStorage.serverPassword = "";
21 | this.defaultSharedStorage.shadowTlsServerName = "";
22 | this.defaultSharedStorage.shadowTlsServerPassword = "";
23 | this.defaultSharedStorage.shadowTlsVersion = "2";
24 | this.defaultSharedStorage.utlsEnabled = "false";
25 | this.defaultSharedStorage.utlsFingerprint = "chrome";
26 | // UDP over TCP
27 | this.defaultSharedStorage.serverUoT = false
28 |
29 | for (var k in this.defaultSharedStorage) {
30 | let v = this.defaultSharedStorage[k];
31 | this.common._setType(k, typeof v);
32 |
33 | if (!this.sharedStorage.hasOwnProperty(k)) {
34 | this.sharedStorage[k] = v;
35 | }
36 | }
37 | }
38 |
39 | _onSharedStorageUpdated() {
40 | // not null
41 | for (var k in this.sharedStorage) {
42 | if (this.sharedStorage[k] == null) {
43 | this.sharedStorage[k] = "";
44 | }
45 | }
46 | this._setShareLink();
47 | }
48 |
49 | _setShareLink() { }
50 |
51 | // UI Interface
52 |
53 | requirePreferenceScreenConfig() {
54 | let sb = [
55 | {
56 | title: TR("serverSettings"),
57 | preferences: [
58 | {
59 | type: "EditTextPreference",
60 | key: "serverAddress",
61 | icon: "ic_hardware_router",
62 | },
63 | {
64 | type: "EditTextPreference",
65 | key: "serverPort",
66 | icon: "ic_maps_directions_boat",
67 | EditTextPreferenceModifiers: "Port",
68 | },
69 | {
70 | type: "EditTextPreference",
71 | key: "shadowTlsServerName",
72 | icon: "ic_action_copyright",
73 | },
74 | {
75 | type: "EditTextPreference",
76 | key: "shadowTlsServerPassword",
77 | icon: "ic_settings_password",
78 | summaryProvider: "PasswordSummaryProvider",
79 | },
80 | {
81 | type: "SimpleMenuPreference",
82 | key: "shadowTlsVersion",
83 | icon: "ic_baseline_layers_24",
84 | entries: {
85 | 1: "1",
86 | 2: "2",
87 | 3: "3"
88 | },
89 | },
90 | {
91 | type: "SimpleMenuPreference",
92 | key: "serverMethod",
93 | icon: "ic_notification_enhanced_encryption",
94 | entries: {
95 | "2022-blake3-aes-128-gcm": "2022-blake3-aes-128-gcm",
96 | "2022-blake3-aes-256-gcm": "2022-blake3-aes-256-gcm",
97 | "2022-blake3-chacha20-poly1305": "2022-blake3-chacha20-poly1305",
98 | "aes-128-gcm": "aes-128-gcm",
99 | "aes-192-gcm": "aes-192-gcm",
100 | "aes-256-gcm": "aes-256-gcm",
101 | "chacha20-ietf-poly1305": "chacha20-ietf-poly1305",
102 | "xchacha20-ietf-poly1305": "xchacha20-ietf-poly1305",
103 | },
104 | },
105 | {
106 | type: "EditTextPreference",
107 | key: "serverPassword",
108 | icon: "ic_settings_password",
109 | summaryProvider: "PasswordSummaryProvider",
110 | },
111 | {
112 | type: "SimpleMenuPreference",
113 | key: "utlsEnabled",
114 | icon: "ic_baseline_vpn_key_24",
115 | entries: {
116 | "true": "true",
117 | "false": "false",
118 | },
119 | },
120 | {
121 | type: "SimpleMenuPreference",
122 | key: "utlsFingerprint",
123 | icon: "ic_baseline_fiber_manual_record_24",
124 | entries: {
125 | "chrome": "chrome",
126 | "firefox": "firefox",
127 | "edge": "edge",
128 | "safari": "safari",
129 | "360": "360",
130 | "qq": "qq",
131 | "ios": "ios",
132 | "android": "android",
133 | "random": "random",
134 | }
135 | },
136 | {
137 | type: "SwitchPreference",
138 | key: "serverUoT",
139 | icon: "baseline_wrap_text_24",
140 | },
141 | ],
142 | },
143 | ];
144 | this.common._applyTranslateToPreferenceScreenConfig(sb, TR);
145 | return JSON.stringify(sb);
146 | }
147 |
148 | // 开启设置界面时调用
149 | setSharedStorage(b64Str) {
150 | this.sharedStorage = util.decodeB64Str(b64Str);
151 | this._initDefaultSharedStorage();
152 | }
153 |
154 | // 开启设置界面时调用
155 | requireSetProfileCache() {
156 | for (var k in this.defaultSharedStorage) {
157 | this.common.setKV(k, this.sharedStorage[k]);
158 | }
159 | }
160 | // 开启设置界面时调用
161 |
162 | // 设置界面创建后调用
163 | onPreferenceCreated() {
164 | let this2 = this
165 |
166 | function listenOnPreferenceChangedNow(key) {
167 | neko.listenOnPreferenceChanged(key)
168 | this2._onPreferenceChanged(key, this2.sharedStorage[key])
169 | }
170 |
171 | listenOnPreferenceChangedNow("utlsEnabled")
172 | listenOnPreferenceChangedNow("shadowTlsVersion")
173 | }
174 |
175 | // 保存时调用(混合编辑后的值)
176 | sharedStorageFromProfileCache() {
177 | for (var k in this.defaultSharedStorage) {
178 | this.sharedStorage[k] = this.common.getKV(k);
179 | }
180 | this._onSharedStorageUpdated();
181 | return JSON.stringify(this.sharedStorage);
182 | }
183 |
184 | // 用户修改 preference 时调用
185 | onPreferenceChanged(b64Str) {
186 | let args = util.decodeB64Str(b64Str)
187 | this._onPreferenceChanged(args.key, args.newValue)
188 | }
189 |
190 | _onPreferenceChanged(key, newValue) {
191 | if (key == "utlsEnabled") {
192 | neko.setPreferenceVisibility("utlsFingerprint", false)
193 | if (newValue == "true") {
194 | neko.setPreferenceVisibility("utlsFingerprint", true)
195 | }
196 | } else if (key == "shadowTlsVersion") {
197 | neko.setPreferenceVisibility("shadowTlsServerPassword", true)
198 | if (newValue == "1") {
199 | neko.setPreferenceVisibility("shadowTlsServerPassword", false)
200 | }
201 | }
202 | }
203 |
204 | // Interface
205 |
206 | parseShareLink(b64Str) { }
207 |
208 | buildAllConfig(b64Str) {
209 | try {
210 | let args = util.decodeB64Str(b64Str);
211 | let ss = util.decodeB64Str(args.sharedStorage);
212 |
213 | // convert string to boolean
214 | if (ss.utlsEnabled === "true"){
215 | ss.utlsEnabled = true
216 | } else {
217 | ss.utlsEnabled = false
218 | }
219 |
220 | let t0 = {
221 | log: {
222 | disabled: false,
223 | level: "warn",
224 | timestamp: true,
225 | },
226 | inbounds: [
227 | {
228 | type: "socks",
229 | tag: "socks-in",
230 | listen: "127.0.0.1",
231 | listen_port: args.port,
232 | },
233 | ],
234 | outbounds: [
235 | {
236 | type: "shadowsocks",
237 | method: ss.serverMethod,
238 | password: ss.serverPassword,
239 | detour: "shadowtls-out",
240 | udp_over_tcp: ss.serverUoT,
241 | multiplex: {
242 | enabled: !ss.serverUoT,
243 | max_connections: 4,
244 | min_streams: 4,
245 | },
246 | },
247 | {
248 | type: "shadowtls",
249 | tag: "shadowtls-out",
250 | server: args.finalAddress,
251 | server_port: args.finalPort,
252 | version: parseInt(ss.shadowTlsVersion, 10),
253 | password: ss.shadowTlsServerPassword,
254 | tls: {
255 | enabled: true,
256 | server_name: ss.shadowTlsServerName,
257 | utls: {
258 | enabled: ss.utlsEnabled,
259 | fingerprint: ss.utlsFingerprint
260 | }
261 | },
262 | },
263 | ],
264 | };
265 |
266 | // check shadowTlsVersion if = 1, remove password entity from config
267 | if (ss.shadowTlsVersion == 1) {
268 | delete t0.outbounds[1].password;
269 | }
270 |
271 | let v = {};
272 | v.nekoCommands = ["%exe%", "run", "--config", "config.json"];
273 |
274 | v.nekoRunConfigs = [
275 | {
276 | name: "config.json",
277 | content: JSON.stringify(t0),
278 | },
279 | ];
280 |
281 | return JSON.stringify(v);
282 | } catch (error) {
283 | neko.logError(error.toString());
284 | }
285 | }
286 | }
287 |
288 | export const shadowTls = new shadowTlsClass();
289 |
--------------------------------------------------------------------------------
/js/plugin_singbox/wireguard.js:
--------------------------------------------------------------------------------
1 | import { util } from "../common/util.js";
2 | import { commomClass } from "../common/common.js";
3 | import { TR } from "../common/translate.js";
4 |
5 | class wireguardClass {
6 | constructor() {
7 | this.sharedStorage = {};
8 | this.defaultSharedStorage = {};
9 | this.common = new commomClass();
10 | }
11 |
12 | _initDefaultSharedStorage() {
13 | // start of default keys
14 | this.defaultSharedStorage.jsVersion = 1;
15 | this.defaultSharedStorage.name = "";
16 | this.defaultSharedStorage.serverAddress = "127.0.0.1";
17 | this.defaultSharedStorage.serverPort = "1080";
18 | // end of default keys
19 | this.defaultSharedStorage.wireguardLocalAddress = "";
20 | this.defaultSharedStorage.wireguardPrivateKey = "";
21 | this.defaultSharedStorage.wireguardCertificates = "";
22 | this.defaultSharedStorage.wireguardPeerPreSharedKey = "";
23 | this.defaultSharedStorage.wireguardMTU = "1420";
24 | this.defaultSharedStorage.wireguardDNS = "";
25 |
26 | for (var k in this.defaultSharedStorage) {
27 | let v = this.defaultSharedStorage[k];
28 | this.common._setType(k, typeof v);
29 |
30 | if (!this.sharedStorage.hasOwnProperty(k)) {
31 | this.sharedStorage[k] = v;
32 | }
33 | }
34 | }
35 |
36 | _onSharedStorageUpdated() {
37 | // not null
38 | for (var k in this.sharedStorage) {
39 | if (this.sharedStorage[k] == null) {
40 | this.sharedStorage[k] = "";
41 | }
42 | }
43 | this._setShareLink();
44 | }
45 |
46 | _setShareLink() { }
47 |
48 | // UI Interface
49 |
50 | requirePreferenceScreenConfig() {
51 | let sb = [
52 | {
53 | title: TR("serverSettings"),
54 | preferences: [
55 | {
56 | type: "EditTextPreference",
57 | key: "wireguardLocalAddress",
58 | icon: "ic_baseline_domain_24",
59 | },
60 | {
61 | type: "EditTextPreference",
62 | key: "wireguardPrivateKey",
63 | icon: "ic_baseline_vpn_key_24",
64 | },
65 | {
66 | type: "EditTextPreference",
67 | key: "serverAddress",
68 | icon: "ic_hardware_router",
69 | },
70 | {
71 | type: "EditTextPreference",
72 | key: "serverPort",
73 | icon: "ic_maps_directions_boat",
74 | EditTextPreferenceModifiers: "Port",
75 | },
76 | {
77 | type: "EditTextPreference",
78 | key: "wireguardCertificates",
79 | icon: "ic_action_copyright",
80 | },
81 | {
82 | type: "EditTextPreference",
83 | key: "wireguardPeerPreSharedKey",
84 | icon: "ic_settings_password",
85 | summaryProvider: "PasswordSummaryProvider",
86 | },
87 | {
88 | type: "EditTextPreference",
89 | key: "wireguardMTU",
90 | icon: "baseline_public_24",
91 | },
92 | {
93 | type: "EditTextPreference",
94 | key: "wireguardDNS",
95 | icon: "ic_action_dns",
96 | },
97 | ],
98 | },
99 | ];
100 | this.common._applyTranslateToPreferenceScreenConfig(sb, TR);
101 | return JSON.stringify(sb);
102 | }
103 |
104 | // 开启设置界面时调用
105 | setSharedStorage(b64Str) {
106 | this.sharedStorage = util.decodeB64Str(b64Str);
107 | this._initDefaultSharedStorage();
108 | }
109 |
110 | // 开启设置界面时调用
111 | requireSetProfileCache() {
112 | for (var k in this.defaultSharedStorage) {
113 | this.common.setKV(k, this.sharedStorage[k]);
114 | }
115 | }
116 |
117 | // 设置界面创建后调用
118 | onPreferenceCreated() { }
119 |
120 | // 保存时调用(混合编辑后的值)
121 | sharedStorageFromProfileCache() {
122 | for (var k in this.defaultSharedStorage) {
123 | this.sharedStorage[k] = this.common.getKV(k);
124 | }
125 | this._onSharedStorageUpdated();
126 | return JSON.stringify(this.sharedStorage);
127 | }
128 |
129 | // Interface
130 |
131 | parseShareLink(b64Str) { }
132 |
133 | buildAllConfig(b64Str) {
134 | try {
135 | let args = util.decodeB64Str(b64Str);
136 | let wg = util.decodeB64Str(args.sharedStorage);
137 |
138 | let t0 = {
139 | log: {
140 | disabled: false,
141 | level: "warn",
142 | timestamp: true,
143 | },
144 | inbounds: [
145 | {
146 | type: "socks",
147 | tag: "socks-in",
148 | listen: "127.0.0.1",
149 | listen_port: args.port,
150 | },
151 | ],
152 | outbounds: [
153 | {
154 | type: "wireguard",
155 | tag: "wireguard-out",
156 | server: args.finalAddress,
157 | server_port: args.finalPort,
158 | system_interface: false,
159 | interface_name: "wg0",
160 | local_address: wg.wireguardLocalAddress
161 | .split("\n")
162 | .map((item) => item.trim())
163 | .filter((item) => item.length > 0),
164 | private_key: wg.wireguardPrivateKey,
165 | peer_public_key: wg.wireguardCertificates,
166 | pre_shared_key: wg.wireguardPeerPreSharedKey,
167 | mtu: parseInt(wg.wireguardMTU),
168 | },
169 | ],
170 | };
171 | if (!wg.wireguardDNS.isBlank()) {
172 | t0.dns = {
173 | servers: [
174 | {
175 | address: wg.wireguardDNS,
176 | },
177 | ]
178 | }
179 | }
180 |
181 | let v = {};
182 | v.nekoCommands = ["%exe%", "run", "--config", "config.json"];
183 |
184 | v.nekoRunConfigs = [
185 | {
186 | name: "config.json",
187 | content: JSON.stringify(t0),
188 | },
189 | ];
190 |
191 | return JSON.stringify(v);
192 | } catch (error) {
193 | neko.logError(error.toString());
194 | }
195 | }
196 | }
197 |
198 | export const wireguard = new wireguardClass();
199 |
--------------------------------------------------------------------------------
/js/plugin_xray/main.js:
--------------------------------------------------------------------------------
1 | import { util } from "../common/util.js"
2 | import { LANG, LANG_TR } from "../common/translate.js"
3 |
4 | import { vless } from "./vless.js"
5 | import { ss2022 } from "./ss2022.js"
6 |
7 | // Init
8 |
9 | export function nekoInit(b64Str) {
10 | let args = util.decodeB64Str(b64Str)
11 |
12 | LANG = args.lang
13 |
14 | let plgConfig = {
15 | "ok": true,
16 | "reason": "",
17 | "minVersion": 2,
18 | "protocols": [
19 | {
20 | "protocolId": "VLESS",
21 | "links": ["vless://"],
22 | "haveStandardLink": true,
23 | "canShare": true,
24 | "canMux": true,
25 | "canMapping": true,
26 | "canTCPing": true,
27 | "canICMPing": true,
28 | "needBypassRootUid": false,
29 | },
30 | {
31 | "protocolId": "Shadowsocks-2022",
32 | "haveStandardLink": false,
33 | "canShare": false,
34 | "canMux": false,
35 | "canMapping": true,
36 | "canTCPing": true,
37 | "canICMPing": true,
38 | "needBypassRootUid": false,
39 | }
40 | ]
41 | }
42 | return JSON.stringify(plgConfig)
43 | }
44 |
45 | export function nekoProtocol(protocolId) {
46 | if (protocolId == "VLESS") {
47 | return vless
48 | }
49 | if (protocolId == "Shadowsocks-2022") {
50 | return ss2022
51 | }
52 | }
53 |
54 | export function nekoAbout() {
55 | switch (LANG_TR()) {
56 | case "zh_CN":
57 | return "使用 Xray 内核提供 VLESS 等协议的实验性支持,效果未知。\n如果在使用过程中遇到任何问题,请自行解决。"
58 | default:
59 | return "Experimental support for protocols such as VLESS using the Xray core, effects unknown. \nIf you have any problems during use, please solve them yourself."
60 | }
61 | }
62 |
63 | // export interface to browser
64 | global_export("nekoInit", nekoInit)
65 | global_export("nekoProtocol", nekoProtocol)
66 | global_export("nekoAbout", nekoAbout)
67 |
--------------------------------------------------------------------------------
/js/plugin_xray/ss2022.js:
--------------------------------------------------------------------------------
1 | import { util } from "../common/util.js"
2 | import { commomClass } from "../common/common.js"
3 | import { TR } from "../common/translate.js"
4 |
5 | class ss2022Class {
6 | constructor() {
7 | this.sharedStorage = {}
8 | this.defaultSharedStorage = {}
9 | this.common = new commomClass()
10 | }
11 |
12 | _initDefaultSharedStorage() {
13 | // start of default keys
14 | this.defaultSharedStorage.jsVersion = 1
15 | this.defaultSharedStorage.name = ""
16 | this.defaultSharedStorage.serverAddress = "127.0.0.1"
17 | this.defaultSharedStorage.serverPort = "1080"
18 | // end of default keys
19 | this.defaultSharedStorage.serverMethod = "2022-blake3-aes-128-gcm"
20 | this.defaultSharedStorage.serverPassword = ""
21 | // UDP over TCP
22 | this.defaultSharedStorage.serverUoT = false
23 | // reduce IV header entropy
24 | this.defaultSharedStorage.reducedIvHeadEntropy = false
25 |
26 | for (var k in this.defaultSharedStorage) {
27 | let v = this.defaultSharedStorage[k]
28 | this.common._setType(k, typeof v)
29 |
30 | if (!this.sharedStorage.hasOwnProperty(k)) {
31 | this.sharedStorage[k] = v
32 | }
33 | }
34 |
35 | }
36 |
37 | _onSharedStorageUpdated() {
38 | // not null
39 | for (var k in this.sharedStorage) {
40 | if (this.sharedStorage[k] == null) {
41 | this.sharedStorage[k] = ""
42 | }
43 | }
44 | this._setShareLink()
45 | }
46 |
47 | _setShareLink() { }
48 |
49 | // UI Interface
50 |
51 | requirePreferenceScreenConfig() {
52 | let sb = [
53 | {
54 | "title": TR("serverSettings"),
55 | "preferences": [
56 | {
57 | "type": "EditTextPreference",
58 | "key": "serverAddress",
59 | "icon": "ic_hardware_router",
60 | },
61 | {
62 | "type": "EditTextPreference",
63 | "key": "serverPort",
64 | "icon": "ic_maps_directions_boat",
65 | "EditTextPreferenceModifiers": "Port",
66 | },
67 | {
68 | "type": "SimpleMenuPreference",
69 | "key": "serverMethod",
70 | "icon": "ic_notification_enhanced_encryption",
71 | "entries": {
72 | "2022-blake3-aes-128-gcm": "2022-blake3-aes-128-gcm",
73 | "2022-blake3-aes-256-gcm": "2022-blake3-aes-256-gcm",
74 | "2022-blake3-chacha20-poly1305": "2022-blake3-chacha20-poly1305",
75 | }
76 | },
77 | {
78 | "type": "EditTextPreference",
79 | "key": "serverPassword",
80 | "icon": "ic_baseline_person_24",
81 | "summaryProvider": "PasswordSummaryProvider",
82 | },
83 | {
84 | "type": "SwitchPreference",
85 | "key": "serverUoT",
86 | "icon": "baseline_wrap_text_24",
87 | },
88 | {
89 | "type": "SwitchPreference",
90 | "icon": "ic_baseline_grid_3x3_24",
91 | "key": "reducedIvHeadEntropy",
92 | "summary": TR("reducedIvHeadEntropy_summary")
93 | },
94 | ]
95 | }
96 | ]
97 | this.common._applyTranslateToPreferenceScreenConfig(sb, TR)
98 | return JSON.stringify(sb)
99 | }
100 |
101 | // 开启设置界面时调用
102 | setSharedStorage(b64Str) {
103 | this.sharedStorage = util.decodeB64Str(b64Str)
104 | this._initDefaultSharedStorage()
105 | }
106 |
107 | // 开启设置界面时调用
108 | requireSetProfileCache() {
109 | for (var k in this.defaultSharedStorage) {
110 | this.common.setKV(k, this.sharedStorage[k])
111 | }
112 | }
113 |
114 | // 设置界面创建后调用
115 | onPreferenceCreated() { }
116 |
117 | // 保存时调用(混合编辑后的值)
118 | sharedStorageFromProfileCache() {
119 | for (var k in this.defaultSharedStorage) {
120 | this.sharedStorage[k] = this.common.getKV(k)
121 | }
122 | this._onSharedStorageUpdated()
123 | return JSON.stringify(this.sharedStorage)
124 | }
125 |
126 | // Interface
127 |
128 | parseShareLink(b64Str) { }
129 |
130 | buildAllConfig(b64Str) {
131 | try {
132 | let args = util.decodeB64Str(b64Str)
133 | let ss = util.decodeB64Str(args.sharedStorage)
134 |
135 | let t0 = {
136 | "log": {
137 | "loglevel": "debug"
138 | },
139 | "inbounds": [
140 | {
141 | "port": args.port,
142 | "listen": "127.0.0.1",
143 | "protocol": "socks",
144 | "settings": {
145 | "udp": true
146 | }
147 | }
148 | ],
149 | "outbounds": [
150 | {
151 | "protocol": "shadowsocks",
152 | "settings": {
153 |
154 | "servers": [
155 | {
156 | "address": args.finalAddress,
157 | "port": args.finalPort,
158 | "method": ss.serverMethod,
159 | "password": ss.serverPassword,
160 | "uot": ss.serverUoT,
161 | "reducedIvHeadEntropy": ss.reducedIvHeadEntropy,
162 | }
163 | ]
164 | }
165 | }
166 | ]
167 | }
168 |
169 | let v = {}
170 | v.nekoCommands = ["%exe%", "-config", "config.json"]
171 |
172 | v.nekoRunConfigs = [
173 | {
174 | "name": "config.json",
175 | "content": JSON.stringify(t0)
176 | }
177 | ]
178 |
179 | return JSON.stringify(v)
180 | } catch (error) {
181 | neko.logError(error.toString())
182 | }
183 | }
184 | }
185 |
186 | export const ss2022 = new ss2022Class()
187 |
--------------------------------------------------------------------------------
/js/plugin_xray/vless.js:
--------------------------------------------------------------------------------
1 | import { util } from "../common/util.js"
2 | import { commomClass } from "../common/common.js"
3 | import { TR } from "../common/translate.js"
4 | import { Base64 } from 'js-base64'
5 |
6 | class vlessClass {
7 | constructor() {
8 | this.sharedStorage = {}
9 | this.defaultSharedStorage = {}
10 | this.common = new commomClass()
11 | }
12 |
13 | _initDefaultSharedStorage() {
14 | // start of default keys
15 | this.defaultSharedStorage.jsVersion = 2
16 | this.defaultSharedStorage.insecureHint = ""
17 | this.defaultSharedStorage.name = ""
18 | this.defaultSharedStorage.serverAddress = "127.0.0.1"
19 | this.defaultSharedStorage.serverPort = "1080"
20 | // end of default keys
21 | this.defaultSharedStorage.serverUserId = ""
22 | this.defaultSharedStorage.serverEncryption = "none"
23 | this.defaultSharedStorage.serverNetwork = "tcp"
24 | this.defaultSharedStorage.serverHeader = "none"
25 | this.defaultSharedStorage.serverQuicSecurity = "chacha20-poly1305"
26 | this.defaultSharedStorage.serverHost = ""
27 | this.defaultSharedStorage.serverPath = ""
28 | this.defaultSharedStorage.serverSecurity = "none"
29 | this.defaultSharedStorage.grpcMultiMode = false
30 | // tls
31 | this.defaultSharedStorage.utlsFingerprint = ""
32 | this.defaultSharedStorage.serverSNI = ""
33 | this.defaultSharedStorage.serverALPN = ""
34 | this.defaultSharedStorage.serverCertificates = ""
35 | this.defaultSharedStorage.serverFlowVision = ""
36 | this.defaultSharedStorage.serverAllowInsecure = false
37 | // reality
38 | this.defaultSharedStorage.publicKey = ""
39 | this.defaultSharedStorage.shortId = ""
40 | this.defaultSharedStorage.spiderX = ""
41 |
42 | for (var k in this.defaultSharedStorage) {
43 | let v = this.defaultSharedStorage[k]
44 | this.common._setType(k, typeof v)
45 |
46 | if (!this.sharedStorage.hasOwnProperty(k)) {
47 | this.sharedStorage[k] = v
48 | }
49 | }
50 |
51 | }
52 |
53 | _onSharedStorageUpdated() {
54 | // not null
55 | for (var k in this.sharedStorage) {
56 | if (this.sharedStorage[k] == null) {
57 | this.sharedStorage[k] = ""
58 | }
59 | }
60 | this._setShareLink()
61 | this.sharedStorage.insecureHint = this._insecureHint()
62 | }
63 |
64 | _insecureHint() {
65 | if (this.sharedStorage.serverSecurity == "none") {
66 | return TR("insecure_cleartext")
67 | }
68 | return ""
69 | }
70 |
71 | _setShareLink() {
72 | var builder = util.newURL("vless")
73 |
74 | if (this.sharedStorage.name.isNotBlank()) builder.hash = "#" + encodeURIComponent(this.sharedStorage.name)
75 | builder.username = this.sharedStorage.serverUserId
76 | builder.host = util.wrapUri(this.sharedStorage.serverAddress, this.sharedStorage.serverPort)
77 | builder.searchParams.set("encryption", this.sharedStorage.serverEncryption)
78 |
79 | var type = this.sharedStorage.serverNetwork
80 | if (type == "h2") type = "http"
81 | builder.searchParams.set("type", type)
82 |
83 | if (type == "tcp") {
84 | if (this.sharedStorage.serverHeader == "http") {
85 | builder.searchParams.set("headerType", this.sharedStorage.serverHeader)
86 | if (this.sharedStorage.serverHost.isNotBlank()) {
87 | builder.searchParams.set("host", this.sharedStorage.serverHost)
88 | }
89 | if (this.sharedStorage.serverPath.isNotBlank()) {
90 | builder.searchParams.set("path", this.sharedStorage.serverPath)
91 | }
92 | }
93 | } else if (type == "kcp") {
94 | if (this.sharedStorage.serverHeader.isNotBlank() && this.sharedStorage.serverHeader != "none") {
95 | builder.searchParams.set("headerType", this.sharedStorage.serverHeader)
96 | }
97 | if (this.sharedStorage.serverPath.isNotBlank()) {
98 | builder.searchParams.set("seed", this.sharedStorage.serverPath)
99 | }
100 | } else if (type == "ws" || type == "http") {
101 | if (this.sharedStorage.serverHost.isNotBlank()) {
102 | builder.searchParams.set("host", this.sharedStorage.serverHost)
103 | }
104 | if (this.sharedStorage.serverPath.isNotBlank()) {
105 | builder.searchParams.set("path", this.sharedStorage.serverPath)
106 | }
107 | } else if (type == "quic") {
108 | if (this.sharedStorage.serverHeader.isNotBlank() && this.sharedStorage.serverHeader != "none") {
109 | builder.searchParams.set("headerType", this.sharedStorage.serverHeader)
110 | }
111 | if (this.sharedStorage.serverQuicSecurity.isNotBlank() && this.sharedStorage.serverQuicSecurity != "none") {
112 | builder.searchParams.set("key", this.sharedStorage.serverPath)
113 | builder.searchParams.set("serverQuicSecurity", this.sharedStorage.serverQuicSecurity)
114 | }
115 | } else if (type == "grpc") {
116 | if (this.sharedStorage.serverPath.isNotBlank()) {
117 | builder.searchParams.set("serviceName", this.sharedStorage.serverPath)
118 | }
119 | if (this.sharedStorage.grpcMultiMode == true) {
120 | builder.searchParams.set("mode", "multi")
121 | }
122 | }
123 |
124 | if (this.sharedStorage.serverSecurity.isNotBlank() && this.sharedStorage.serverSecurity != "none") {
125 | builder.searchParams.set("security", this.sharedStorage.serverSecurity)
126 | switch (this.sharedStorage.serverSecurity) {
127 | case "tls": {
128 | if (this.sharedStorage.serverSNI.isNotBlank()) {
129 | builder.searchParams.set("sni", this.sharedStorage.serverSNI)
130 | }
131 | if (this.sharedStorage.serverALPN.isNotBlank()) {
132 | builder.searchParams.set("alpn", this.sharedStorage.serverALPN)
133 | }
134 | if (this.sharedStorage.serverCertificates.isNotBlank()) {
135 | builder.searchParams.set("cert", this.sharedStorage.serverCertificates)
136 | }
137 | if (this.sharedStorage.serverFlowVision.isNotBlank()) {
138 | builder.searchParams.set("flow", this.sharedStorage.serverFlowVision)
139 | }
140 | if (this.sharedStorage.utlsFingerprint.isNotBlank()) {
141 | builder.searchParams.set("fp", this.sharedStorage.utlsFingerprint)
142 | }
143 | break
144 | }
145 | case "reality": {
146 | if (this.sharedStorage.serverSNI.isNotBlank()) {
147 | builder.searchParams.set("sni", this.sharedStorage.serverSNI)
148 | }
149 | if (this.sharedStorage.serverFlowVision.isNotBlank()) {
150 | builder.searchParams.set("flow", this.sharedStorage.serverFlowVision)
151 | }
152 | if (this.sharedStorage.publicKey.isNotBlank()) {
153 | builder.searchParams.set("pbk", this.sharedStorage.publicKey)
154 | }
155 | if (this.sharedStorage.shortId.isNotBlank()) {
156 | builder.searchParams.set("sid", this.sharedStorage.shortId)
157 | }
158 | if (this.sharedStorage.spiderX.isNotBlank()) {
159 | builder.searchParams.set("spx", this.sharedStorage.spiderX)
160 | }
161 | if (this.sharedStorage.utlsFingerprint.isNotBlank()) {
162 | builder.searchParams.set("fp", this.sharedStorage.utlsFingerprint)
163 | }
164 | break
165 | }
166 | }
167 | }
168 |
169 | this.sharedStorage.shareLink = builder.toString()
170 | }
171 |
172 | // UI Interface
173 |
174 | requirePreferenceScreenConfig() {
175 | let sb = [
176 | {
177 | "title": TR("serverSettings"),
178 | "preferences": [
179 | {
180 | "type": "EditTextPreference",
181 | "key": "serverAddress",
182 | "icon": "ic_hardware_router",
183 | },
184 | {
185 | "type": "EditTextPreference",
186 | "key": "serverPort",
187 | "icon": "ic_maps_directions_boat",
188 | "EditTextPreferenceModifiers": "Port",
189 | },
190 | {
191 | "type": "EditTextPreference",
192 | "key": "serverUserId",
193 | "icon": "ic_baseline_person_24",
194 | "summaryProvider": "PasswordSummaryProvider",
195 | },
196 | {
197 | "type": "SimpleMenuPreference",
198 | "key": "serverEncryption",
199 | "icon": "ic_notification_enhanced_encryption",
200 | "entries": {
201 | "none": "none",
202 | }
203 | },
204 | {
205 | "type": "SimpleMenuPreference",
206 | "key": "serverNetwork",
207 | "icon": "ic_baseline_compare_arrows_24",
208 | "entries": {
209 | "tcp": "tcp",
210 | "kcp": "kcp",
211 | "ws": "ws",
212 | "h2": "h2",
213 | "quic": "quic",
214 | "grpc": "grpc",
215 | }
216 | },
217 | {
218 | "type": "SimpleMenuPreference",
219 | "key": "serverHeader",
220 | "icon": "ic_baseline_texture_24",
221 | },
222 | {
223 | "type": "SimpleMenuPreference",
224 | "key": "serverQuicSecurity",
225 | "icon": "ic_baseline_security_24",
226 | "entries": {
227 | "chacha20-poly1305": "chacha20-poly1305",
228 | "aes-128-gcm": "aes-128-gcm",
229 | "none": "none",
230 | }
231 | },
232 | {
233 | "type": "EditTextPreference",
234 | "key": "serverHost",
235 | "icon": "ic_baseline_airplanemode_active_24"
236 | },
237 | {
238 | "type": "EditTextPreference",
239 | "key": "serverPath",
240 | "icon": "ic_baseline_format_align_left_24"
241 | },
242 | {
243 | "type": "SimpleMenuPreference",
244 | "key": "serverSecurity",
245 | "icon": "ic_baseline_layers_24",
246 | "entries": {
247 | "none": "none",
248 | "tls": "tls",
249 | "reality": "reality",
250 | }
251 | },
252 | {
253 | "type": "SwitchPreference",
254 | "key": "grpcMultiMode",
255 | "summary": TR("grpcMultiMode_summary")
256 | },
257 | ]
258 | },
259 | {
260 | "key": "serverSecurityCategory",
261 | "preferences": [
262 | {
263 | "type": "SimpleMenuPreference",
264 | "key": "utlsFingerprint",
265 | "entries": {
266 | "": "",
267 | "chrome": "chrome",
268 | "firefox": "firefox",
269 | "safari": "safari",
270 | "ios": "ios",
271 | "android": "android",
272 | "edge": "edge",
273 | "360": "360",
274 | "qq": "qq",
275 | "random": "random",
276 | "randomized": "randomized",
277 | }
278 | },
279 | {
280 | "type": "EditTextPreference",
281 | "key": "serverSNI",
282 | "icon": "ic_action_copyright"
283 | },
284 | {
285 | "type": "EditTextPreference",
286 | "key": "serverALPN",
287 | "icon": "ic_baseline_legend_toggle_24"
288 | },
289 | {
290 | "type": "EditTextPreference",
291 | "key": "serverCertificates",
292 | "icon": "ic_baseline_vpn_key_24"
293 | },
294 | {
295 | "type": "SimpleMenuPreference",
296 | "key": "serverFlowVision",
297 | "icon": "ic_baseline_stream_24",
298 | "entries": {
299 | "": "",
300 | "xtls-rprx-vision": "xtls-rprx-vision",
301 | "xtls-rprx-vision-udp443": "xtls-rprx-vision-udp443"
302 | }
303 | },
304 | {
305 | "type": "SwitchPreference",
306 | "key": "serverAllowInsecure",
307 | "icon": "ic_notification_enhanced_encryption",
308 | "summary": TR("serverAllowInsecure_summary")
309 | },
310 | {
311 | "type": "EditTextPreference",
312 | "key": "publicKey",
313 | "icon": "ic_baseline_vpn_key_24"
314 | },
315 | {
316 | "type": "EditTextPreference",
317 | "key": "shortId",
318 | "icon": "ic_baseline_texture_24"
319 | },
320 | {
321 | "type": "EditTextPreference",
322 | "key": "spiderX",
323 | "icon": "ic_baseline_format_align_left_24"
324 | },
325 | ]
326 | }
327 | ]
328 | this.common._applyTranslateToPreferenceScreenConfig(sb, TR)
329 | return JSON.stringify(sb)
330 | }
331 |
332 | // 开启设置界面时调用
333 | setSharedStorage(b64Str) {
334 | this.sharedStorage = util.decodeB64Str(b64Str)
335 | this._initDefaultSharedStorage()
336 | }
337 |
338 | // 开启设置界面时调用
339 | requireSetProfileCache() {
340 | for (var k in this.defaultSharedStorage) {
341 | this.common.setKV(k, this.sharedStorage[k])
342 | }
343 | }
344 |
345 | // 设置界面创建后调用
346 | onPreferenceCreated() {
347 | let this2 = this
348 |
349 | function listenOnPreferenceChangedNow(key) {
350 | neko.listenOnPreferenceChanged(key)
351 | this2._onPreferenceChanged(key, this2.sharedStorage[key])
352 | }
353 |
354 | listenOnPreferenceChangedNow("serverNetwork")
355 | listenOnPreferenceChangedNow("serverSecurity")
356 | }
357 |
358 | // 保存时调用(混合编辑后的值)
359 | sharedStorageFromProfileCache() {
360 | for (var k in this.defaultSharedStorage) {
361 | this.sharedStorage[k] = this.common.getKV(k)
362 | }
363 | this._onSharedStorageUpdated()
364 | return JSON.stringify(this.sharedStorage)
365 | }
366 |
367 | // 用户修改 preference 时调用
368 | onPreferenceChanged(b64Str) {
369 | let args = util.decodeB64Str(b64Str)
370 | this._onPreferenceChanged(args.key, args.newValue)
371 | }
372 |
373 | _onPreferenceChanged(key, newValue) {
374 | if (key == "serverNetwork") {
375 | neko.setPreferenceVisibility("serverQuicSecurity", false)
376 | // neko.setPreferenceVisibility("serverWsCategory", false)
377 | neko.setPreferenceVisibility("grpcMultiMode", false)
378 | neko.setMenu("serverHeader", JSON.stringify({
379 | "none": "none",
380 | "srtp": "srtp",
381 | "utp": "utp",
382 | "wechat-video": "wechat-video",
383 | "dtls": "dtls",
384 | "wireguard": "wireguard",
385 | }))
386 |
387 | if (newValue == "tcp") {
388 | neko.setPreferenceVisibility("serverHeader", true)
389 | neko.setPreferenceVisibility("serverHost", false)
390 | neko.setPreferenceVisibility("serverPath", false)
391 | neko.setMenu("serverHeader", JSON.stringify({
392 | "none": "none",
393 | "http": "http",
394 | }))
395 | } else if (newValue == "mkcp") {
396 | neko.setPreferenceVisibility("serverHeader", true)
397 | neko.setPreferenceVisibility("serverHost", false)
398 | neko.setPreferenceVisibility("serverPath", true)
399 | neko.setPreferenceTitle("serverPath", TR("serverPath_mkcp"))
400 | } else if (newValue == "ws") {
401 | neko.setPreferenceVisibility("serverHeader", false)
402 | neko.setPreferenceVisibility("serverHost", true)
403 | neko.setPreferenceVisibility("serverPath", true)
404 | neko.setPreferenceTitle("serverHost", TR("serverHost_ws"))
405 | neko.setPreferenceTitle("serverPath", TR("serverPath_ws"))
406 | } else if (newValue == "h2") {
407 | neko.setPreferenceVisibility("serverHeader", false)
408 | neko.setPreferenceVisibility("serverHost", true)
409 | neko.setPreferenceVisibility("serverPath", true)
410 | neko.setPreferenceVisibility("serverWsCategory", true) // TODO 暂时不支持浏览器转发
411 | neko.setPreferenceTitle("serverHost", TR("serverHost_http"))
412 | neko.setPreferenceTitle("serverPath", TR("serverPath_http"))
413 | } else if (newValue == "quic") {
414 | neko.setPreferenceVisibility("serverHeader", true)
415 | neko.setPreferenceVisibility("serverQuicSecurity", true)
416 | neko.setPreferenceVisibility("serverHost", false)
417 | neko.setPreferenceVisibility("serverPath", true)
418 | neko.setPreferenceTitle("serverPath", TR("serverPath_quic"))
419 | } else if (newValue == "grpc") {
420 | neko.setPreferenceVisibility("serverHeader", false)
421 | neko.setPreferenceVisibility("serverHost", false)
422 | neko.setPreferenceVisibility("serverPath", true)
423 | neko.setPreferenceVisibility("grpcMultiMode", true)
424 | neko.setPreferenceTitle("serverPath", TR("serverPath_grpc"))
425 | }
426 | } else if (key == "serverSecurity") {
427 | if (newValue == "none") {
428 | neko.setPreferenceVisibility("serverSecurityCategory", false)
429 | } else {
430 | neko.setPreferenceVisibility("serverSecurityCategory", true)
431 | let isREALITY = (newValue == "reality")
432 | neko.setPreferenceVisibility("publicKey", isREALITY)
433 | neko.setPreferenceVisibility("shortId", isREALITY)
434 | neko.setPreferenceVisibility("spiderX", isREALITY)
435 | neko.setPreferenceVisibility("serverALPN", !isREALITY)
436 | neko.setPreferenceVisibility("serverCertificates", !isREALITY)
437 | neko.setPreferenceVisibility("serverAllowInsecure", !isREALITY)
438 | }
439 | }
440 | }
441 |
442 | // Interface
443 |
444 | parseShareLink(b64Str) {
445 | let args = util.decodeB64Str(b64Str)
446 |
447 | this.sharedStorage = {}
448 | this._initDefaultSharedStorage()
449 |
450 | var url = util.tryParseURL(args.shareLink)
451 | if (typeof url == "string") return url
452 |
453 | if (Base64.isValid(url.hostname)) return "不支持的格式"
454 |
455 | var serverAddress = util.unwrapIpv6(url.hostname)
456 | this.sharedStorage.serverAddress = serverAddress
457 | this.sharedStorage.serverPort = url.host.replace(serverAddress, "").substringAfter(":")
458 | this.sharedStorage.serverUserId = url.username
459 | this.sharedStorage.name = decodeURIComponent(url.hash.substringAfter("#"))
460 |
461 | util.ifNotNull(url.searchParams.get("path"), (it) => {
462 | this.sharedStorage.serverPath = it
463 | })
464 |
465 | var protocol = url.searchParams.get("type")
466 | if (protocol == null) protocol = "tcp"
467 | if (protocol == "http") protocol = "h2"
468 | this.sharedStorage.serverNetwork = protocol
469 |
470 | switch (url.searchParams.get("security")) {
471 | case "tls": {
472 | this.sharedStorage.serverSecurity = "tls"
473 | util.ifNotNull(url.searchParams.get("sni"), (it) => {
474 | this.sharedStorage.serverSNI = it
475 | })
476 | util.ifNotNull(url.searchParams.get("alpn"), (it) => {
477 | this.sharedStorage.serverALPN = it
478 | })
479 | util.ifNotNull(url.searchParams.get("cert"), (it) => {
480 | this.sharedStorage.serverCertificates = it
481 | })
482 | util.ifNotNull(url.searchParams.get("flow"), (it) => {
483 | this.sharedStorage.serverFlowVision = it
484 | })
485 | util.ifNotNull(url.searchParams.get("fp"), (it) => {
486 | this.sharedStorage.utlsFingerprint = it
487 | })
488 | break
489 | }
490 | case "reality": {
491 | this.sharedStorage.serverSecurity = "reality"
492 | util.ifNotNull(url.searchParams.get("sni"), (it) => {
493 | this.sharedStorage.serverSNI = it
494 | })
495 | util.ifNotNull(url.searchParams.get("flow"), (it) => {
496 | this.sharedStorage.serverFlowVision = it
497 | })
498 | util.ifNotNull(url.searchParams.get("pbk"), (it) => {
499 | this.sharedStorage.publicKey = it
500 | })
501 | util.ifNotNull(url.searchParams.get("sid"), (it) => {
502 | this.sharedStorage.shortId = it
503 | })
504 | util.ifNotNull(url.searchParams.get("spx"), (it) => {
505 | this.sharedStorage.spiderX = it
506 | })
507 | util.ifNotNull(url.searchParams.get("fp"), (it) => {
508 | this.sharedStorage.utlsFingerprint = it
509 | })
510 | break
511 | }
512 | }
513 |
514 | switch (protocol) {
515 | case "tcp": {
516 | util.ifNotNull(url.searchParams.get("headerType"), (headerType) => {
517 | if (headerType == "http") {
518 | this.sharedStorage.serverHeader = headerType
519 | util.ifNotNull(url.searchParams.get("host"), (it) => {
520 | this.sharedStorage.serverHost = it
521 | })
522 | }
523 | })
524 | break
525 | }
526 | case "kcp": {
527 | util.ifNotNull(url.searchParams.get("headerType"), (it) => {
528 | this.sharedStorage.serverHeader = it
529 | })
530 | util.ifNotNull(url.searchParams.get("seed"), (it) => {
531 | this.sharedStorage.serverPath = it
532 | })
533 | break
534 | }
535 | case "h2": {
536 | util.ifNotNull(url.searchParams.get("host"), (it) => {
537 | this.sharedStorage.serverHost = it
538 | })
539 | break
540 | }
541 | case "ws": {
542 | util.ifNotNull(url.searchParams.get("host"), (it) => {
543 | this.sharedStorage.serverHost = it
544 | })
545 | break
546 | }
547 | case "quic": {
548 | util.ifNotNull(url.searchParams.get("headerType"), (it) => {
549 | this.sharedStorage.serverHeader = it
550 | })
551 | util.ifNotNull(url.searchParams.get("quicSecurity"), (quicSecurity) => {
552 | this.sharedStorage.serverQuicSecurity = quicSecurity
553 | util.ifNotNull(url.searchParams.get("key"), (it) => {
554 | this.sharedStorage.serverPath = it
555 | })
556 | })
557 | break
558 | }
559 | case "grpc": {
560 | util.ifNotNull(url.searchParams.get("serviceName"), (it) => {
561 | this.sharedStorage.serverPath = it
562 | })
563 | if (url.searchParams.get("serviceName") == "multi") this.sharedStorage.grpcMultiMode = true
564 | break
565 | }
566 | }
567 |
568 | this._onSharedStorageUpdated()
569 | return JSON.stringify(this.sharedStorage)
570 | }
571 |
572 | buildAllConfig(b64Str) {
573 | try {
574 | let args = util.decodeB64Str(b64Str)
575 | let ss = util.decodeB64Str(args.sharedStorage)
576 |
577 | let canMux = false
578 | if (ss.serverNetwork == "tcp" || ss.serverNetwork == "ws" || ss.serverNetwork == "h2") {
579 | canMux = ss.serverFlowVision.isBlank()
580 | }
581 |
582 | let t0 = {
583 | "log": {
584 | "loglevel": "debug"
585 | },
586 | "inbounds": [
587 | {
588 | "port": args.port,
589 | "listen": "127.0.0.1",
590 | "protocol": "socks",
591 | "settings": {
592 | "udp": true
593 | }
594 | }
595 | ],
596 | "outbounds": [
597 | {
598 | "mux": {
599 | "enabled": args.muxEnabled && canMux,
600 | "concurrency": args.muxConcurrency
601 | },
602 | "protocol": "vless",
603 | "settings": {
604 | "vnext": [
605 | {
606 | "address": args.finalAddress, // 换成你的域名或服务器 IP(发起请求时无需解析域名了)
607 | "port": args.finalPort,
608 | "users": [
609 | {
610 | "id": ss.serverUserId, // 填写你的 UUID
611 | "encryption": ss.serverEncryption,
612 | "level": 0,
613 | }
614 | ]
615 | }
616 | ]
617 | },
618 | "streamSettings": {
619 | "network": ss.serverNetwork,
620 | "security": ss.serverSecurity,
621 | }
622 | }
623 | ]
624 | }
625 |
626 | // fill http host & SNI
627 | if (ss.serverHost.isBlank()) {
628 | ss.serverHost = ss.serverAddress
629 | }
630 | if (ss.serverSNI.isBlank()) {
631 | ss.serverSNI = ss.serverAddress
632 | }
633 |
634 | switch (ss.serverNetwork) {
635 | case "tcp": {
636 | let t1 = {
637 | "header": {
638 | "type": ss.serverHeader
639 | },
640 | }
641 | t0.outbounds[0].streamSettings["tcpSettings"] = t1
642 | break
643 | }
644 | case "ws": {
645 | let t1 = {
646 | "path": util.addSplash(ss.serverPath),
647 | }
648 | t1["headers"] = { "Host": ss.serverHost.firstLine() }
649 | t0.outbounds[0].streamSettings["wsSettings"] = t1
650 | break
651 | }
652 | case "kcp": {
653 | let t1 = {
654 | "header": {
655 | "type": ss.serverHeader
656 | },
657 | "seed": ss.serverPath,
658 | }
659 | t0.outbounds[0].streamSettings["kcpSettings"] = t1
660 | break
661 | }
662 | case "h2": {
663 | let t1 = {
664 | "host": ss.serverHost.lines(),
665 | "path": util.addSplash(ss.serverPath),
666 | }
667 | t0.outbounds[0].streamSettings["httpSettings"] = t1
668 | break
669 | }
670 | case "quic": {
671 | let t1 = {
672 | "security": ss.serverQuicSecurity,
673 | "key": ss.serverPath,
674 | "header": {
675 | "type": ss.serverHeader
676 | },
677 | }
678 | t0.outbounds[0].streamSettings["quicSettings"] = t1
679 | break
680 | }
681 | case "grpc": {
682 | let t1 = {
683 | "serviceName": ss.serverPath,
684 | "multiMode": ss.grpcMultiMode,
685 | }
686 | t0.outbounds[0].streamSettings["grpcSettings"] = t1
687 | break
688 | }
689 | }
690 |
691 | switch (ss.serverSecurity) {
692 | case "tls": {
693 | let t2 = {
694 | "serverName": ss.serverSNI,
695 | "allowInsecure": ss.serverAllowInsecure,
696 | }
697 | if (ss.utlsFingerprint.isNotBlank()) t2["fingerprint"] = ss.utlsFingerprint
698 | if (ss.serverALPN.isNotBlank()) t2["alpn"] = ss.serverALPN.lines()
699 | if (ss.serverCertificates.isNotBlank()) t2["certificates"] = { "certificate": ss.serverCertificates.lines() }
700 | t0.outbounds[0].streamSettings["tlsSettings"] = t2
701 | t0.outbounds[0].settings.vnext[0].users[0]["flow"] = ss.serverFlowVision
702 | break
703 | }
704 | case "reality": {
705 | let t2 = {
706 | "serverName": ss.serverSNI,
707 | }
708 | if (ss.utlsFingerprint.isNotBlank()) t2["fingerprint"] = ss.utlsFingerprint
709 | if (ss.publicKey.isNotBlank()) t2["publicKey"] = ss.publicKey
710 | if (ss.shortId.isNotBlank()) t2["shortId"] = ss.shortId
711 | if (ss.spiderX.isNotBlank()) t2["spiderX"] = ss.spiderX
712 | t0.outbounds[0].streamSettings["realitySettings"] = t2
713 | t0.outbounds[0].settings.vnext[0].users[0]["flow"] = ss.serverFlowVision
714 | break
715 | }
716 | }
717 |
718 | let v = {}
719 | v.nekoCommands = ["%exe%", "-config", "config.json"]
720 |
721 | v.nekoRunConfigs = [
722 | {
723 | "name": "config.json",
724 | "content": JSON.stringify(t0)
725 | }
726 | ]
727 |
728 | return JSON.stringify(v)
729 | } catch (error) {
730 | neko.logError(error.toString())
731 | }
732 | }
733 | }
734 |
735 | export const vless = new vlessClass()
736 |
--------------------------------------------------------------------------------
/js/webpack.config.js:
--------------------------------------------------------------------------------
1 |
2 | const path = require('path');
3 |
4 | module.exports = {
5 | mode: 'production',
6 | target: ['browserslist'],
7 | output: {
8 | path: path.resolve(__dirname, 'dist'),
9 | filename: 'p.js',
10 | publicPath: './'
11 | },
12 | optimization: {
13 | minimize: true
14 | },
15 | module: {
16 | rules: [
17 | {
18 | test: /\.js$/, exclude: /node_modules/,
19 | use: [
20 | {
21 | loader: 'babel-loader',
22 | options: {
23 | presets: [
24 | '@babel/preset-env'
25 | ],
26 | }
27 | }
28 | ]
29 | }
30 | ]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/make.sh:
--------------------------------------------------------------------------------
1 | set -e
2 |
3 | build() {
4 | [ $dl ] && bash download.sh "$1"
5 | cd js
6 | bash make.sh "$1"
7 | cd ..
8 | rm -f app_"$1"/build/outputs/apk/release/*
9 | ./gradlew :app_"$1":assembleRelease
10 | }
11 |
12 | build $1
13 |
--------------------------------------------------------------------------------
/release.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maskedeken/plugins/592119092fecb6f7e970bded792fd8cdce397f24/release.keystore
--------------------------------------------------------------------------------
/requirement.sh:
--------------------------------------------------------------------------------
1 | set -e
2 |
3 | echo "$LOCAL_PROPERTIES" | base64 -d > local.properties
4 |
5 | sudo npm install -g webpack-cli@4.9.2
6 | cd js
7 | npm install
8 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | rootProject.name = "Matsuri Plugins"
9 |
10 | include ':common'
11 |
12 | include ':app_xray'
13 | include ':app_brook'
14 | include ':app_singbox'
15 |
16 | include ':app_hysteria'
17 | include ':app_tuic'
18 |
19 | include ':app_naive'
20 | include ':app_trojan-go'
21 | include ':app_tuic5'
22 | include ':app_juicity'
23 |
--------------------------------------------------------------------------------