diff --git a/README.md b/README.md index 5ba5d8e..6378661 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,20 @@ - /jcf setSubsSender \ # 设置订阅信息推送bot(qq id) - /jcf setCheckInterval \ # 设置更新检查间隔(单位:秒) +### 网络代理 + +国内部署访问 CurseForge、Modrinth 或图片资源超时时,可以编辑插件配置文件 +`config/top.jie65535.jcf/JCurseforgeConfig.yml`,设置本机 HTTP 或 SOCKS 代理: + +```yaml +proxyType: HTTP +proxyHost: 127.0.0.1 +proxyPort: 7890 +``` + +`proxyType` 可选值为 `NONE`、`HTTP`、`SOCKS`,默认为 `NONE`。修改配置后需要重启插件。 +如果 Mirai Console 运行在 Docker 容器中,`127.0.0.1` 指向容器自身,应改为宿主机可访问的地址。 + ### CurseForge 分类搜索命令(可配置) - 搜索模组: cfmod \ - 搜索整合包: cfpack \ @@ -81,7 +95,7 @@ - [x] 搜索模组、整合包、资源包、光影、插件、数据包 - [x] 查看项目详情与版本列表 - [x] 订阅项目更新通知 -- [ ] 设置代理 +- [x] 设置代理 ## 鸣谢 diff --git a/build.gradle.kts b/build.gradle.kts index 4dbf1a1..b2970f7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { } group = "top.jie65535.jcf" -version = "1.2.1" +version = "1.2.2" repositories { maven("https://maven.aliyun.com/repository/public") diff --git a/src/main/kotlin/top/jie65535/jcf/CurseforgeApi.kt b/src/main/kotlin/top/jie65535/jcf/CurseforgeApi.kt index 6fa5b3d..b964393 100644 --- a/src/main/kotlin/top/jie65535/jcf/CurseforgeApi.kt +++ b/src/main/kotlin/top/jie65535/jcf/CurseforgeApi.kt @@ -17,6 +17,7 @@ import top.jie65535.jcf.model.mod.* import top.jie65535.jcf.model.request.* import top.jie65535.jcf.model.request.SortOrder.* import top.jie65535.jcf.model.response.* +import top.jie65535.jcf.util.ProxySupport /** * [Api docs](https://docs.curseforge.com/) @@ -32,11 +33,11 @@ class CurseforgeApi(apiKey: String) { } private val http = HttpClient(OkHttp) { -// engine { -// config { -// protocols(listOf(okhttp3.Protocol.HTTP_1_1)) -// } -// } + engine { + config { + ProxySupport.configure(this) + } + } install(HttpTimeout) { this.requestTimeoutMillis = 60_000 this.connectTimeoutMillis = 60_000 @@ -286,4 +287,4 @@ class CurseforgeApi(apiKey: String) { } //endregion -} \ No newline at end of file +} diff --git a/src/main/kotlin/top/jie65535/jcf/ModrinthApi.kt b/src/main/kotlin/top/jie65535/jcf/ModrinthApi.kt index 76b8651..cc8425f 100644 --- a/src/main/kotlin/top/jie65535/jcf/ModrinthApi.kt +++ b/src/main/kotlin/top/jie65535/jcf/ModrinthApi.kt @@ -13,6 +13,7 @@ import kotlinx.serialization.json.Json import top.jie65535.jcf.model.modrinth.Project import top.jie65535.jcf.model.modrinth.SearchResponse import top.jie65535.jcf.model.modrinth.Version +import top.jie65535.jcf.util.ProxySupport /** * HTTP client for the Modrinth API. No API key is required for read operations. @@ -28,6 +29,11 @@ class ModrinthApi { } private val http = HttpClient(OkHttp) { + engine { + config { + ProxySupport.configure(this) + } + } install(HttpTimeout) { this.requestTimeoutMillis = 60_000 this.connectTimeoutMillis = 60_000 diff --git a/src/main/kotlin/top/jie65535/jcf/PluginConfig.kt b/src/main/kotlin/top/jie65535/jcf/PluginConfig.kt index 9a2aa00..3534620 100644 --- a/src/main/kotlin/top/jie65535/jcf/PluginConfig.kt +++ b/src/main/kotlin/top/jie65535/jcf/PluginConfig.kt @@ -8,6 +8,15 @@ object PluginConfig : AutoSavePluginConfig("JCurseforgeConfig") { @ValueDescription("Curseforge API KEY") var apiKey: String by value() + @ValueDescription("网络代理类型(NONE、HTTP、SOCKS)") + var proxyType: ProxyType by value(ProxyType.NONE) + + @ValueDescription("网络代理主机") + var proxyHost: String by value("127.0.0.1") + + @ValueDescription("网络代理端口") + var proxyPort: Int by value(7890) + @ValueDescription("搜索命令 (MODS,MODPACKS,RESOURCE_PACKS,WORLDS,BUKKIT_PLUGINS,ADDONS,CUSTOMIZATION)") val searchCommands: MutableMap by value( mutableMapOf( @@ -45,3 +54,9 @@ object PluginConfig : AutoSavePluginConfig("JCurseforgeConfig") { @ValueDescription("检查间隔(单位:秒)") var checkInterval: Long by value(60 * 60 * 4L) } + +enum class ProxyType { + NONE, + HTTP, + SOCKS, +} diff --git a/src/main/kotlin/top/jie65535/jcf/PluginMain.kt b/src/main/kotlin/top/jie65535/jcf/PluginMain.kt index 83d7968..b03021b 100644 --- a/src/main/kotlin/top/jie65535/jcf/PluginMain.kt +++ b/src/main/kotlin/top/jie65535/jcf/PluginMain.kt @@ -6,12 +6,13 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin import net.mamoe.mirai.event.GlobalEventChannel import net.mamoe.mirai.utils.info +import top.jie65535.jcf.util.ProxySupport object PluginMain: KotlinPlugin( JvmPluginDescription( id = "top.jie65535.jcf", name = "J Curseforge Util", - version = "1.2.1", + version = "1.2.2", ) { author("jie65535") info("MC Curseforge & Modrinth Util\n" + @@ -40,6 +41,13 @@ object PluginMain: KotlinPlugin( PluginConfig.reload() PluginCommands.register() + val proxyValidationError = ProxySupport.validationError() + if (proxyValidationError != null) { + logger.warning(proxyValidationError) + } else if (PluginConfig.proxyType != ProxyType.NONE) { + logger.info("网络请求将使用代理:${ProxySupport.description()}") + } + val eventChannel = GlobalEventChannel.parentScope(this) // Initialize Modrinth (no API key required) diff --git a/src/main/kotlin/top/jie65535/jcf/util/HttpUtil.kt b/src/main/kotlin/top/jie65535/jcf/util/HttpUtil.kt index 16adee1..a21bb49 100644 --- a/src/main/kotlin/top/jie65535/jcf/util/HttpUtil.kt +++ b/src/main/kotlin/top/jie65535/jcf/util/HttpUtil.kt @@ -11,6 +11,7 @@ object HttpUtil { // private val JSON: MediaType? = "application/json; charset=utf-8".toMediaTypeOrNull() private val okHttpClient: OkHttpClient by lazy { OkHttpClient.Builder() + .also(ProxySupport::configure) .readTimeout(10, TimeUnit.SECONDS) .build() } diff --git a/src/main/kotlin/top/jie65535/jcf/util/ProxySupport.kt b/src/main/kotlin/top/jie65535/jcf/util/ProxySupport.kt new file mode 100644 index 0000000..ca73955 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/util/ProxySupport.kt @@ -0,0 +1,39 @@ +package top.jie65535.jcf.util + +import okhttp3.OkHttpClient +import top.jie65535.jcf.PluginConfig +import top.jie65535.jcf.ProxyType +import java.net.InetSocketAddress +import java.net.Proxy + +object ProxySupport { + fun configure(builder: OkHttpClient.Builder) { + createProxy()?.let(builder::proxy) + } + + fun validationError(): String? = when { + PluginConfig.proxyType == ProxyType.NONE -> null + PluginConfig.proxyHost.isBlank() -> "代理已启用,但 proxyHost 为空,将忽略该代理配置" + PluginConfig.proxyPort !in 1..65535 -> + "代理已启用,但 proxyPort 不在 1..65535 范围内,将忽略该代理配置" + else -> null + } + + fun description(): String = + "${PluginConfig.proxyType} ${PluginConfig.proxyHost.trim()}:${PluginConfig.proxyPort}" + + private fun createProxy(): Proxy? { + if (validationError() != null) return null + + val type = when (PluginConfig.proxyType) { + ProxyType.NONE -> return null + ProxyType.HTTP -> Proxy.Type.HTTP + ProxyType.SOCKS -> Proxy.Type.SOCKS + } + val address = InetSocketAddress.createUnresolved( + PluginConfig.proxyHost.trim(), + PluginConfig.proxyPort, + ) + return Proxy(type, address) + } +}