mirror of
https://github.com/LamGC/ScalaBot.git
synced 2025-04-29 14:17:30 +00:00
feat: 可通过配置文件设置用于查找扩展包的 Maven 仓库.
使用 Maven 扩展包搜素器将不再限制仓库, 可通过配置文件添加其他仓库.
This commit is contained in:
parent
6df9f1b3c7
commit
a642948f45
@ -6,12 +6,17 @@ import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import mu.KotlinLogging
|
||||
import net.lamgc.scalabot.util.ArtifactSerializer
|
||||
import net.lamgc.scalabot.util.AuthenticationSerializer
|
||||
import net.lamgc.scalabot.util.MavenRepositoryConfigSerializer
|
||||
import net.lamgc.scalabot.util.ProxyTypeSerializer
|
||||
import org.eclipse.aether.artifact.Artifact
|
||||
import org.eclipse.aether.repository.Authentication
|
||||
import org.eclipse.aether.repository.Proxy
|
||||
import org.eclipse.aether.repository.RemoteRepository
|
||||
import org.telegram.telegrambots.bots.DefaultBotOptions
|
||||
import org.telegram.telegrambots.meta.ApiConstants
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
@ -76,18 +81,56 @@ internal data class ProxyConfig(
|
||||
internal data class MetricsConfig(
|
||||
val enable: Boolean = false,
|
||||
val port: Int = 9386,
|
||||
val bindAddress: String? = null
|
||||
val bindAddress: String? = "0.0.0.0"
|
||||
)
|
||||
|
||||
/**
|
||||
* Maven 远端仓库配置.
|
||||
* @property url 仓库地址.
|
||||
* @property proxy 访问仓库所使用的代理, 仅支持 http/https 代理.
|
||||
* @property layout 仓库布局版本, Maven 2 及以上使用 `default`, Maven 1 使用 `legacy`.
|
||||
*/
|
||||
internal data class MavenRepositoryConfig(
|
||||
val url: URL,
|
||||
val proxy: Proxy? = Proxy("http", "127.0.0.1", 1080),
|
||||
val layout: String = "default",
|
||||
// 可能要设计个 type 来判断解析成什么类型的 Authentication.
|
||||
val authentication: Authentication? = null
|
||||
) {
|
||||
|
||||
fun toRemoteRepository(): RemoteRepository {
|
||||
val builder = RemoteRepository.Builder(null, checkRepositoryLayout(layout), url.toString())
|
||||
if (proxy != null) {
|
||||
builder.setProxy(proxy)
|
||||
} else if (Const.config.proxy.type == DefaultBotOptions.ProxyType.HTTP) {
|
||||
builder.setProxy(Const.config.proxy.toAetherProxy())
|
||||
}
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
fun checkRepositoryLayout(layoutType: String): String {
|
||||
val type = layoutType.trim().lowercase()
|
||||
if (type != "default" && type != "legacy") {
|
||||
throw IllegalArgumentException("Invalid layout type (expecting 'default' or 'legacy')")
|
||||
}
|
||||
return type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ScalaBot App 配置.
|
||||
*
|
||||
* App 配置信息与 BotConfig 分开, 分别存储在各自单独的文件中.
|
||||
* @property proxy Telegram API 代理配置.
|
||||
* @property metrics 运行指标数据配置. 可通过时序数据库记录运行数据.
|
||||
* @property mavenRepositories Maven 远端仓库配置.
|
||||
*/
|
||||
internal data class AppConfig(
|
||||
val proxy: ProxyConfig = ProxyConfig(),
|
||||
val metrics: MetricsConfig = MetricsConfig()
|
||||
val metrics: MetricsConfig = MetricsConfig(),
|
||||
val mavenRepositories: List<MavenRepositoryConfig> = emptyList()
|
||||
)
|
||||
|
||||
/**
|
||||
@ -119,7 +162,15 @@ internal enum class AppPaths(
|
||||
DEFAULT_CONFIG_APPLICATION({ "$DATA_ROOT/config.json" }, {
|
||||
if (!file.exists()) {
|
||||
file.bufferedWriter(StandardCharsets.UTF_8).use {
|
||||
GsonConst.botConfigGson.toJson(AppConfig(), it)
|
||||
GsonConst.botConfigGson.toJson(
|
||||
AppConfig(
|
||||
mavenRepositories = listOf(
|
||||
MavenRepositoryConfig(
|
||||
URL(MavenRepositoryExtensionFinder.MAVEN_CENTRAL_URL)
|
||||
)
|
||||
)
|
||||
), it
|
||||
)
|
||||
}
|
||||
}
|
||||
}),
|
||||
@ -217,6 +268,8 @@ private object GsonConst {
|
||||
|
||||
val appConfigGson: Gson = baseGson.newBuilder()
|
||||
.registerTypeAdapter(DefaultBotOptions.ProxyType::class.java, ProxyTypeSerializer)
|
||||
.registerTypeAdapter(MavenRepositoryConfig::class.java, MavenRepositoryConfigSerializer)
|
||||
.registerTypeAdapter(Authentication::class.java, AuthenticationSerializer)
|
||||
.create()
|
||||
|
||||
val botConfigGson: Gson = baseGson.newBuilder()
|
||||
|
@ -53,6 +53,11 @@ internal class Launcher : AutoCloseable {
|
||||
|
||||
private val botApi = TelegramBotsApi(DefaultBotSession::class.java)
|
||||
private val botSessionMap = mutableMapOf<ScalaBot, BotSession>()
|
||||
private val remoteRepositories = Const.config.mavenRepositories
|
||||
.map(MavenRepositoryConfig::toRemoteRepository)
|
||||
.toMutableList().apply {
|
||||
add(MavenRepositoryExtensionFinder.getMavenCentralRepository(proxy = Const.config.proxy.toAetherProxy()))
|
||||
}.toList()
|
||||
|
||||
@Synchronized
|
||||
fun launch(): Boolean {
|
||||
@ -94,6 +99,13 @@ internal class Launcher : AutoCloseable {
|
||||
}
|
||||
}
|
||||
val account = botConfig.account
|
||||
val extensionPackageFinders = setOf(
|
||||
MavenRepositoryExtensionFinder(
|
||||
remoteRepositories = remoteRepositories,
|
||||
proxy = Const.config.proxy.toAetherProxy()
|
||||
)
|
||||
)
|
||||
|
||||
val bot = ScalaBot(
|
||||
account.name,
|
||||
account.token,
|
||||
@ -101,6 +113,7 @@ internal class Launcher : AutoCloseable {
|
||||
BotDBMaker.getBotMaker(account),
|
||||
botOption,
|
||||
botConfig.extensions,
|
||||
extensionPackageFinders,
|
||||
botConfig.disableBuiltInAbility
|
||||
)
|
||||
botSessionMap[bot] = botApi.registerBot(bot)
|
||||
|
@ -4,7 +4,6 @@ import mu.KotlinLogging
|
||||
import net.lamgc.scalabot.extension.BotExtensionFactory
|
||||
import net.lamgc.scalabot.util.getPriority
|
||||
import org.eclipse.aether.artifact.Artifact
|
||||
import org.eclipse.aether.repository.LocalRepository
|
||||
import org.telegram.abilitybots.api.util.AbilityExtension
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
@ -18,18 +17,18 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||
internal class ExtensionLoader(
|
||||
private val bot: ScalaBot,
|
||||
private val extensionsDataFolder: File = AppPaths.DATA_EXTENSIONS.file,
|
||||
private val extensionsPath: File = AppPaths.EXTENSIONS.file
|
||||
private val extensionsPath: File = AppPaths.EXTENSIONS.file,
|
||||
private val extensionFinders: Set<ExtensionPackageFinder> = setOf()
|
||||
) {
|
||||
private val log = KotlinLogging.logger { }
|
||||
|
||||
private val finders: Set<ExtensionPackageFinder> = setOf(
|
||||
private val finders: Set<ExtensionPackageFinder> = mutableSetOf(
|
||||
FileNameFinder,
|
||||
MavenMetaInformationFinder,
|
||||
MavenRepositoryExtensionFinder(
|
||||
LocalRepository("${System.getProperty("user.home")}/.m2/repository"),
|
||||
proxy = Const.config.proxy.toAetherProxy()
|
||||
)
|
||||
)
|
||||
).apply { addAll(extensionFinders) }.toSet()
|
||||
|
||||
fun getExtensions(): Set<LoadedExtensionEntry> {
|
||||
val extensionEntries = mutableSetOf<LoadedExtensionEntry>()
|
||||
|
@ -234,7 +234,7 @@ internal object MavenMetaInformationFinder : ExtensionPackageFinder {
|
||||
*/
|
||||
@FinderRules(priority = FinderPriority.REMOTE)
|
||||
internal class MavenRepositoryExtensionFinder(
|
||||
private val localRepository: LocalRepository,
|
||||
private val localRepository: LocalRepository = LocalRepository("${System.getProperty("user.home")}/.m2/repository"),
|
||||
private val proxy: Proxy? = null,
|
||||
private val remoteRepositories: List<RemoteRepository> = listOf(getMavenCentralRepository(proxy)),
|
||||
) : ExtensionPackageFinder {
|
||||
|
@ -30,11 +30,15 @@ internal class ScalaBot(
|
||||
db: DBContext,
|
||||
options: DefaultBotOptions,
|
||||
val extensions: Set<Artifact>,
|
||||
extensionFinders: Set<ExtensionPackageFinder>,
|
||||
disableBuiltInAbility: Boolean
|
||||
) :
|
||||
AbilityBot(token, name, db, if (disableBuiltInAbility) BareboneToggle() else DefaultToggle(), options) {
|
||||
|
||||
private val extensionLoader = ExtensionLoader(this)
|
||||
private val extensionLoader = ExtensionLoader(
|
||||
bot = this,
|
||||
extensionFinders = extensionFinders
|
||||
)
|
||||
|
||||
init {
|
||||
val extensionEntries = extensionLoader.getExtensions()
|
||||
|
@ -1,10 +1,16 @@
|
||||
package net.lamgc.scalabot.util
|
||||
|
||||
import com.google.gson.*
|
||||
import mu.KotlinLogging
|
||||
import net.lamgc.scalabot.MavenRepositoryConfig
|
||||
import org.eclipse.aether.artifact.Artifact
|
||||
import org.eclipse.aether.artifact.DefaultArtifact
|
||||
import org.eclipse.aether.repository.Authentication
|
||||
import org.eclipse.aether.repository.Proxy
|
||||
import org.eclipse.aether.util.repository.AuthenticationBuilder
|
||||
import org.telegram.telegrambots.bots.DefaultBotOptions
|
||||
import java.lang.reflect.Type
|
||||
import java.net.URL
|
||||
|
||||
internal object ProxyTypeSerializer : JsonDeserializer<DefaultBotOptions.ProxyType>,
|
||||
JsonSerializer<DefaultBotOptions.ProxyType> {
|
||||
@ -55,3 +61,105 @@ internal object ArtifactSerializer : JsonSerializer<Artifact>, JsonDeserializer<
|
||||
|
||||
}
|
||||
|
||||
internal object AuthenticationSerializer : JsonDeserializer<Authentication> {
|
||||
|
||||
private val log = KotlinLogging.logger { }
|
||||
|
||||
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Authentication? {
|
||||
val builder = AuthenticationBuilder()
|
||||
when (json) {
|
||||
is JsonArray -> {
|
||||
for (element in json) {
|
||||
if (element is JsonArray) {
|
||||
builder.addCustom(jsonArrayToAuthentication(element))
|
||||
} else if (element is JsonObject) {
|
||||
jsonToAuthentication(element, builder)
|
||||
}
|
||||
}
|
||||
}
|
||||
is JsonObject -> {
|
||||
jsonToAuthentication(json, builder)
|
||||
}
|
||||
else -> {
|
||||
throw JsonParseException("Unsupported JSON data type: ${json::class.java}")
|
||||
}
|
||||
}
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
private fun jsonArrayToAuthentication(jsonArray: JsonArray): Authentication {
|
||||
val builder = AuthenticationBuilder()
|
||||
for (element in jsonArray) {
|
||||
when (element) {
|
||||
is JsonObject -> jsonToAuthentication(element, builder)
|
||||
is JsonArray -> builder.addCustom(jsonArrayToAuthentication(element))
|
||||
else -> log.warn { "不支持的 Json 类型: ${element::class.java}" }
|
||||
}
|
||||
}
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
private const val KEY_TYPE = "type"
|
||||
|
||||
private fun jsonToAuthentication(json: JsonObject, builder: AuthenticationBuilder) {
|
||||
if (!json.has(KEY_TYPE)) {
|
||||
log.warn { "缺少 type 字段, 无法判断 Maven 认证信息类型." }
|
||||
return
|
||||
} else if (!json.get(KEY_TYPE).isJsonPrimitive) {
|
||||
log.warn { "type 字段类型错误(应为 Primitive 类型), 无法判断 Maven 认证信息类型.(实际类型: `${json::class.java}`)" }
|
||||
return
|
||||
}
|
||||
|
||||
when (json.get(KEY_TYPE).asString.trim().lowercase()) {
|
||||
"string" -> {
|
||||
builder.addString(checkJsonKey(json, "key"), checkJsonKey(json, "value"))
|
||||
}
|
||||
"secret" -> {
|
||||
builder.addSecret(checkJsonKey(json, "key"), checkJsonKey(json, "value"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkJsonKey(json: JsonObject, key: String): String {
|
||||
if (!json.has(key)) {
|
||||
throw JsonParseException("Required field does not exist: $key")
|
||||
} else if (!json.get(key).isJsonPrimitive) {
|
||||
throw JsonParseException("Wrong field `$key` type: ${json.get(key)::class.java}")
|
||||
}
|
||||
return json.get(key).asString
|
||||
}
|
||||
|
||||
internal object MavenRepositoryConfigSerializer
|
||||
: JsonDeserializer<MavenRepositoryConfig> {
|
||||
|
||||
override fun deserialize(
|
||||
json: JsonElement,
|
||||
typeOfT: Type,
|
||||
context: JsonDeserializationContext
|
||||
): MavenRepositoryConfig {
|
||||
return when (json) {
|
||||
is JsonObject -> {
|
||||
MavenRepositoryConfig(
|
||||
url = URL(checkJsonKey(json, "url")),
|
||||
proxy = if (json.has("proxy") && json.get("proxy").isJsonObject)
|
||||
context.deserialize<Proxy>(
|
||||
json.getAsJsonObject("proxy"), Proxy::class.java
|
||||
) else null,
|
||||
layout = json.get("layout").asString ?: "default",
|
||||
authentication = if (json.has("authentication") && json.get("authentication").isJsonObject)
|
||||
context.deserialize<Authentication>(
|
||||
json.getAsJsonObject("authentication"), Authentication::class.java
|
||||
) else null
|
||||
)
|
||||
}
|
||||
is JsonPrimitive -> {
|
||||
MavenRepositoryConfig(URL(json.asString))
|
||||
}
|
||||
else -> {
|
||||
throw JsonParseException("Unsupported Maven warehouse configuration type.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user