mirror of
https://github.com/LamGC/ScalaBot.git
synced 2025-04-29 22:27:31 +00:00
265 lines
8.2 KiB
Kotlin
265 lines
8.2 KiB
Kotlin
package net.lamgc.scalabot
|
|
|
|
import ch.qos.logback.core.PropertyDefinerBase
|
|
import com.google.gson.Gson
|
|
import com.google.gson.GsonBuilder
|
|
import com.google.gson.reflect.TypeToken
|
|
import mu.KotlinLogging
|
|
import net.lamgc.scalabot.config.*
|
|
import net.lamgc.scalabot.config.serializer.*
|
|
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.eclipse.aether.repository.RepositoryPolicy
|
|
import org.telegram.telegrambots.bots.DefaultBotOptions
|
|
import java.io.File
|
|
import java.net.URL
|
|
import java.nio.charset.StandardCharsets
|
|
import java.util.concurrent.atomic.AtomicBoolean
|
|
import java.util.concurrent.atomic.AtomicInteger
|
|
import kotlin.system.exitProcess
|
|
|
|
private val log = KotlinLogging.logger { }
|
|
|
|
internal fun ProxyType.toTelegramBotsType(): DefaultBotOptions.ProxyType {
|
|
return when (this) {
|
|
ProxyType.NO_PROXY -> DefaultBotOptions.ProxyType.NO_PROXY
|
|
ProxyType.HTTP -> DefaultBotOptions.ProxyType.HTTP
|
|
ProxyType.HTTPS -> DefaultBotOptions.ProxyType.HTTP
|
|
ProxyType.SOCKS4 -> DefaultBotOptions.ProxyType.SOCKS4
|
|
ProxyType.SOCKS5 -> DefaultBotOptions.ProxyType.SOCKS5
|
|
}
|
|
}
|
|
|
|
internal fun ProxyConfig.toAetherProxy(): Proxy? {
|
|
val typeStr = when (type) {
|
|
ProxyType.HTTP -> Proxy.TYPE_HTTP
|
|
ProxyType.HTTPS -> Proxy.TYPE_HTTPS
|
|
else -> return null
|
|
}
|
|
return Proxy(typeStr, host, port)
|
|
}
|
|
|
|
internal fun MavenRepositoryConfig.toRemoteRepository(proxyConfig: ProxyConfig): RemoteRepository {
|
|
val builder =
|
|
RemoteRepository.Builder(id ?: createDefaultRepositoryId(), checkRepositoryLayout(layout), url.toString())
|
|
if (proxy != null) {
|
|
builder.setProxy(proxy)
|
|
} else if (proxyConfig.type == ProxyType.HTTP) {
|
|
builder.setProxy(proxyConfig.toAetherProxy())
|
|
}
|
|
|
|
builder.setReleasePolicy(
|
|
RepositoryPolicy(
|
|
enableReleases,
|
|
RepositoryPolicy.UPDATE_POLICY_NEVER,
|
|
RepositoryPolicy.CHECKSUM_POLICY_FAIL
|
|
)
|
|
)
|
|
builder.setSnapshotPolicy(
|
|
RepositoryPolicy(
|
|
enableSnapshots,
|
|
RepositoryPolicy.UPDATE_POLICY_ALWAYS,
|
|
RepositoryPolicy.CHECKSUM_POLICY_WARN
|
|
)
|
|
)
|
|
|
|
return builder.build()
|
|
}
|
|
|
|
private 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
|
|
}
|
|
|
|
private val repoNumberGenerator = AtomicInteger(1)
|
|
|
|
private fun createDefaultRepositoryId(): String {
|
|
return "Repository-${repoNumberGenerator.getAndIncrement()}"
|
|
}
|
|
|
|
/**
|
|
* 需要用到的路径.
|
|
*
|
|
* 必须提供 `pathSupplier` 或 `fileSupplier` 其中一个, 才能正常提供路径.
|
|
*/
|
|
internal enum class AppPaths(
|
|
private val pathSupplier: () -> String = { fileSupplier.invoke().canonicalPath },
|
|
private val initializer: AppPaths.() -> Unit = AppPaths::defaultInitializer,
|
|
private val fileSupplier: () -> File = { File(pathSupplier()) }
|
|
) {
|
|
/**
|
|
* 数据根目录.
|
|
*
|
|
* 所有运行数据的存放位置.
|
|
*
|
|
* 提示: 结尾不带 `/`.
|
|
*/
|
|
DATA_ROOT(fileSupplier = {
|
|
File(
|
|
System.getProperty(PathConst.PROP_DATA_PATH) ?: System.getenv(PathConst.ENV_DATA_PATH)
|
|
?: System.getProperty("user.dir") ?: "."
|
|
)
|
|
}, initializer = {
|
|
val f = file
|
|
if (!f.exists()) {
|
|
f.mkdirs()
|
|
}
|
|
}),
|
|
|
|
DEFAULT_CONFIG_APPLICATION({ "$DATA_ROOT/config.json" }, {
|
|
if (!file.exists()) {
|
|
file.bufferedWriter(StandardCharsets.UTF_8).use {
|
|
GsonConst.botConfigGson.toJson(
|
|
AppConfig(
|
|
mavenRepositories = listOf(
|
|
MavenRepositoryConfig(
|
|
id = "central",
|
|
url = URL(MavenRepositoryExtensionFinder.MAVEN_CENTRAL_URL)
|
|
)
|
|
)
|
|
), it
|
|
)
|
|
}
|
|
}
|
|
}),
|
|
DEFAULT_CONFIG_BOT({ "$DATA_ROOT/bot.json" }, {
|
|
if (!file.exists()) {
|
|
file.bufferedWriter(StandardCharsets.UTF_8).use {
|
|
GsonConst.botConfigGson.toJson(
|
|
setOf(
|
|
BotConfig(
|
|
enabled = true,
|
|
proxy = ProxyConfig(),
|
|
account = BotAccount(
|
|
"Bot Username",
|
|
"Bot API Token",
|
|
-1
|
|
), extensions = emptySet()
|
|
)
|
|
), it
|
|
)
|
|
}
|
|
}
|
|
}),
|
|
DATA_DB({ "$DATA_ROOT/data/db/" }),
|
|
DATA_LOGS({ "$DATA_ROOT/data/logs/" }),
|
|
EXTENSIONS({ "$DATA_ROOT/extensions/" }),
|
|
DATA_EXTENSIONS({ "$DATA_ROOT/data/extensions/" }),
|
|
TEMP({ "$DATA_ROOT/tmp/" })
|
|
;
|
|
|
|
val file: File
|
|
get() = fileSupplier.invoke()
|
|
val path: String
|
|
get() = pathSupplier.invoke()
|
|
|
|
private val initialized = AtomicBoolean(false)
|
|
|
|
@Synchronized
|
|
fun initial() {
|
|
if (!initialized.get()) {
|
|
initializer()
|
|
initialized.set(true)
|
|
}
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return path
|
|
}
|
|
|
|
private object PathConst {
|
|
const val PROP_DATA_PATH = "bot.path.data"
|
|
const val ENV_DATA_PATH = "BOT_DATA_PATH"
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 为 LogBack 提供日志目录路径.
|
|
*/
|
|
internal class LogDirectorySupplier : PropertyDefinerBase() {
|
|
override fun getPropertyValue(): String {
|
|
return AppPaths.DATA_LOGS.path
|
|
}
|
|
}
|
|
|
|
internal object Const {
|
|
val config = loadAppConfig()
|
|
}
|
|
|
|
private fun AppPaths.defaultInitializer() {
|
|
val f = file
|
|
val p = path
|
|
if (!f.exists()) {
|
|
val result = if (p.endsWith("/")) {
|
|
f.mkdirs()
|
|
} else {
|
|
f.createNewFile()
|
|
}
|
|
if (!result) {
|
|
log.warn { "初始化文件(夹)失败: $p" }
|
|
}
|
|
}
|
|
}
|
|
|
|
internal fun initialFiles() {
|
|
val configFilesNotInitialized = !AppPaths.DEFAULT_CONFIG_APPLICATION.file.exists()
|
|
&& !AppPaths.DEFAULT_CONFIG_BOT.file.exists()
|
|
|
|
for (path in AppPaths.values()) {
|
|
path.initial()
|
|
}
|
|
|
|
if (configFilesNotInitialized) {
|
|
log.warn { "配置文件已初始化, 请根据需要修改配置文件后重新启动本程序." }
|
|
exitProcess(1)
|
|
}
|
|
}
|
|
|
|
private object GsonConst {
|
|
val baseGson: Gson = GsonBuilder()
|
|
.setPrettyPrinting()
|
|
.serializeNulls()
|
|
.create()
|
|
|
|
val appConfigGson: Gson = baseGson.newBuilder()
|
|
.registerTypeAdapter(ProxyType::class.java, ProxyTypeSerializer)
|
|
.registerTypeAdapter(MavenRepositoryConfig::class.java, MavenRepositoryConfigSerializer)
|
|
.registerTypeAdapter(Authentication::class.java, AuthenticationSerializer)
|
|
.registerTypeAdapter(UsernameAuthenticator::class.java, UsernameAuthenticatorSerializer)
|
|
.create()
|
|
|
|
val botConfigGson: Gson = baseGson.newBuilder()
|
|
.registerTypeAdapter(ProxyType::class.java, ProxyTypeSerializer)
|
|
.registerTypeAdapter(BotConfig::class.java, BotConfigSerializer)
|
|
.registerTypeAdapter(Artifact::class.java, ArtifactSerializer)
|
|
.create()
|
|
}
|
|
|
|
internal fun loadAppConfig(configFile: File = AppPaths.DEFAULT_CONFIG_APPLICATION.file): AppConfig {
|
|
try {
|
|
configFile.bufferedReader(StandardCharsets.UTF_8).use {
|
|
return GsonConst.appConfigGson.fromJson(it, AppConfig::class.java)!!
|
|
}
|
|
} catch (e: Exception) {
|
|
log.error { "读取 config.json 时发生错误, 请检查配置格式是否正确." }
|
|
throw e
|
|
}
|
|
}
|
|
|
|
internal fun loadBotConfig(botConfigFile: File = AppPaths.DEFAULT_CONFIG_BOT.file): Set<BotConfig>? {
|
|
try {
|
|
botConfigFile.bufferedReader(StandardCharsets.UTF_8).use {
|
|
return GsonConst.botConfigGson.fromJson(it, object : TypeToken<Set<BotConfig>>() {}.type)!!
|
|
}
|
|
} catch (e: Exception) {
|
|
log.error(e) { "读取 Bot 配置文件 (bot.json) 时发生错误, 请检查配置格式是否正确." }
|
|
return null
|
|
}
|
|
}
|