56 Commits

Author SHA1 Message Date
8b76a9aa9b release: 发布 0.6.0 版本. 2023-02-21 23:46:48 +08:00
bbc3288535 ci(github-action): 启用 API 二进制兼容性验证. (#37)
使用二进制兼容验证, 可以快速了解 API 是否出现修改, 这个功能有利于防止无意中修改 API.
引入该过程后, 需谨慎检查 api 列表,
以确保改动是必须的. 当 api 出现改动时, 需按照改动类型分配合适的版本号(遵循 SemVer 规范).
2023-02-21 23:22:40 +08:00
45ad734c7b build(deps): bump org.junit.jupiter:junit-jupiter-engine (#34)
Bumps [org.junit.jupiter:junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.2.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.2)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-engine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 18:52:21 +08:00
1eaed8ce6f build(deps): bump org.mockito:mockito-core from 4.8.0 to 5.1.1 (#35)
Bumps [org.mockito:mockito-core](https://github.com/mockito/mockito) from 4.8.0 to 5.1.1.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.8.0...v5.1.1)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 18:42:42 +08:00
a624fcd37d build(deps): bump com.google.code.gson:gson from 2.9.0 to 2.10.1 (#36)
Bumps [com.google.code.gson:gson](https://github.com/google/gson) from 2.9.0 to 2.10.1.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.9.0...gson-parent-2.10.1)

---
updated-dependencies:
- dependency-name: com.google.code.gson:gson
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 18:38:18 +08:00
59aa83c93e build(deps): bump org.junit.jupiter:junit-jupiter-api (#33)
Bumps [org.junit.jupiter:junit-jupiter-api](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.2.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.2)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 18:36:02 +08:00
3ea0f4eacb feat(metrics): 指标信息增加 bot_id label.
由于 bot_name 不能作为机器人的唯一标识, 因此增加 bot_id 为监控平台提供唯一标识.
2023-02-21 18:32:35 +08:00
d3e18d80ca ci(github-action): 移除针对子模块的依赖项更新.
根据目前的效果来看, 即使不指定子模块的路径, dependabot 也会检查子模块的依赖项更新情况, 因此尝试移除子模块配置.
2023-02-21 16:00:08 +08:00
1e0eebbd7e build(deps): 更新日志相关依赖(Slf4j 2.0.6, Logback-classic 1.4.5, Kotlin-logging 3.0.5).
Slf4j: 2.0.3 -> 2.0.6
Logback-classic: 1.4.4 -> 1.4.5
Kotlin-logging: 3.0.2 -> 3.0.5
2023-02-21 15:51:28 +08:00
6f3c1ec592 build(deps): bump org.junit.jupiter:junit-jupiter-api (#19)
Bumps [org.junit.jupiter:junit-jupiter-api](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.2.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.2)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 15:29:54 +08:00
c013dafdd7 build(deps): bump io.mockk:mockk from 1.13.2 to 1.13.4 in /scalabot-app (#26)
Bumps [io.mockk:mockk](https://github.com/mockk/mockk) from 1.13.2 to 1.13.4.
- [Release notes](https://github.com/mockk/mockk/releases)
- [Commits](https://github.com/mockk/mockk/compare/1.13.2...v1.13.4)

---
updated-dependencies:
- dependency-name: io.mockk:mockk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 15:08:06 +08:00
e0bfd8796c build(deps): bump org.jetbrains.kotlin:kotlin-reflect in /scalabot-app (#29)
Bumps [org.jetbrains.kotlin:kotlin-reflect](https://github.com/JetBrains/kotlin) from 1.6.20 to 1.8.10.
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.8.10/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.6.20...v1.8.10)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-reflect
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 15:07:45 +08:00
67d561c0ab build(deps): bump org.jetbrains.kotlinx.kover from 0.5.1 to 0.6.1 (#32)
Bumps [org.jetbrains.kotlinx.kover](https://github.com/Kotlin/kotlinx-kover) from 0.5.1 to 0.6.1.
- [Release notes](https://github.com/Kotlin/kotlinx-kover/releases)
- [Changelog](https://github.com/Kotlin/kotlinx-kover/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Kotlin/kotlinx-kover/compare/v0.5.1...v0.6.1)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlinx.kover
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 12:53:54 +08:00
63687da0bb build(dependencies): 更新 binary-compatibility-validator 插件(0.11.1 -> 0.13.0).
更新后插件将支持 Kotlin 1.8.10 版本.
2023-02-21 12:44:06 +08:00
cd76249c33 build(deps): bump gradle/gradle-build-action from 2.2.1 to 2.4.0 (#23)
Bumps [gradle/gradle-build-action](https://github.com/gradle/gradle-build-action) from 2.2.1 to 2.4.0.
- [Release notes](https://github.com/gradle/gradle-build-action/releases)
- [Commits](https://github.com/gradle/gradle-build-action/compare/v2.2.1...v2.4.0)

---
updated-dependencies:
- dependency-name: gradle/gradle-build-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 11:24:23 +08:00
5a8afd1549 build(deps): bump docker/build-push-action from 3 to 4 (#18)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 4.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 11:21:09 +08:00
651569dc8f ci(github-action): 增加 Dependabot 配置文件.
使用 Dependabot 来自动检查依赖新版本, 减轻工作量.
2023-02-21 10:51:30 +08:00
bf2ea9a367 refactor(launch): 在启动时输出 JVM 和 Kotlin 的版本号.
在日志中添加版本信息, 方便在反馈问题时可以了解出现问题所使用的环境.
2023-02-19 16:12:15 +08:00
540fe84f26 build(dependencies): 更新 Kotlin 版本(1.7.10 -> 1.8.10).
更新 Kotlin 版本, 支持新的平台优化.
2023-02-19 16:09:52 +08:00
712378b3ff build(dependencies): 更新 telegrambots 依赖项.
将 telegrambots 依赖项更新至 6.5.0, 支持新版 Api;
已确认 telegrambots 6.5.0 版本升级了jackson-databind 版本,
因此移除 jackson-databind 的显性依赖声明.
2023-02-08 10:12:33 +08:00
961382fb56 build(dependencies): 更新 TelegramBots 依赖项的版本号.
将 TelegramBots 依赖版本升级至 6.3.0, 支持新的 Telegram Bot API.
2022-12-16 01:15:09 +08:00
34d9ece6d7 feat(logging): 支持通过参数控制日志输出级别.
通过添加新的 -D 参数 `scalabot.log.level`, 使得用户可以根据需要, 调整日志输出级别, 通过日志来自行(或协助开发者)排查问题,
对于与网络有关的 Debug 级别日志, 则由 `scalabot.log.network.verbose` 参数进行控制;
另外, 由于开发过程中可通过该参数修改日志级别, 因此移除 `logback-test.xml` 文件.
2022-11-11 18:17:05 +08:00
30a2cb34d5 refactor(launch): 将 AppPaths 从 Launcher 解耦.
解耦后有助于后续改进, 以及单元测试的编写.
2022-11-07 11:16:41 +08:00
c94e0476b5 refactor(metrics): 为指标增加 namespace 名称.
遵循 OpenMetrics 规范指南, 为运行指标添加 namespace 名称, 防止指标混乱.

BREAKING CHANGE: 运行指标名称变更,
如果用户启用了运行指标功能, 请注意修改使用指标的名称.
2022-10-28 01:33:03 +08:00
6e5cd07c51 build(dependencies): 更新依赖项版本.
更新与日志和测试有关的依赖项版本.
2022-10-25 17:18:24 +08:00
d4b1438b0b release: 发布 0.5.2 版本. 2022-10-20 16:04:27 +08:00
90110335f5 fix: 更新 jackson-databind 依赖版本以缓解 CVE-2022-42004 漏洞.
CVE-2022-42004 漏洞报告指出, 由于 jackson-databind 的有关错误, 导致对象序列化过程可能存在数据错误的问题,
目前 jackson-databind 已发布 2.13.4.2 版本以解决该问题, 由于引入 jackson-databind 的 telegrambots 库尚未发布针对该问题的修复版本,
故在本项目中引入新版依赖项, 以确保用户不受该问题影响.
该版本已在 TelegramBots 项目(版本 6.1.0)中进行测试, 测试通过.
------------------------------------------
https://devhub.checkmarx.com/cve-details/CVE-2022-42004/
2022-10-20 01:41:48 +08:00
9c32d26c0d fix(dependencies): 更新 Commons-codec 依赖项的版本.
由于目前从 TelegramBots-Abilities 引入的 Commons-codec 存在 Base 32 和 64 的编解码漏洞,
考虑到需要防范潜在的安全问题, 因此决定更新 Commons-codec
的版本号.
----------------
参考链接:
https://devhub.checkmarx.com/cve-details/Cxeb68d52e-5509/
2022-09-19 14:32:40 +08:00
dfab6b14bd build(compatibility): 新增 API 兼容性检查插件(尚未启用).
新增 Jetbrains 的二进制兼容性验证插件, 该插件可确保在兼容性出现更改时及时报告出来.
计划在下一次发布版本时启用.
2022-09-19 14:28:06 +08:00
437cee499a build(dependencies): 更新依赖项版本.
更新版本有利于维持项目安全性.
2022-09-19 01:14:31 +08:00
4c30a1ac68 ci(github-action): 更改工作流名称.
后续可能会加上 Maven 的发布, 所以把名字和文件名都改一下, 防止出现歧义.
2022-08-25 16:11:10 +08:00
3932db11a1 release: 发布 0.5.1 版本. 2022-08-23 13:40:21 +08:00
d18c059498 ci(github-action): 显性声明允许工作流写入仓库.
安全起见, 仓库将设置工作流的 Github Token 默认不可写, 因此需要在该工作流添加权限声明, 以支持其创建 Release.
2022-08-23 13:40:20 +08:00
e6b581b8cd ci(github-action): 移除容器镜像对 windows-amd64 的构建.
windows-amd64 的镜像构建依然不太不稳定, 因此移除 windows-amd64 平台的镜像构建.
2022-08-23 13:40:18 +08:00
26d7443c87 ci(github-action): 把 Gradle Wrapper 检查步骤加入到所有构建工作流中.
单独检查 Wrapper 并不能阻止其他工作流被恶意 jar 影响, 所以取消单独的检查工作流, 并将检查步骤插入到其他构建工作流中.
2022-08-23 13:40:17 +08:00
2bf4eb684e ci(github-action): 延长镜像构建工作流的超时时间.
鉴于镜像构建可能会很慢, 因此将时间统一延长到 15 分钟.
2022-08-23 13:40:15 +08:00
5251b62733 ci(github-action): 修改开发版镜像构建工作流的名称和配置文件名.
为了与发布版镜像构建工作流区分开来, 防止弄混乱了.
2022-08-23 13:40:13 +08:00
3ec5a8e9c3 ci(github-action): 增加自动发布工作流.
使用 Action 自动执行发布过程, 可避免不少发布问题(比如构建成品包括了其他代码, 内容错误等).
同时在确认发布版本后,将自动构建并发布容器镜像, 支持多平台.
2022-08-23 13:40:11 +08:00
62f6c08cd2 ci(github-action): 移除对测试分支的容器镜像构建.
因为要并入 main 分支了.
2022-08-23 13:40:09 +08:00
5e6f64f056 ci(github-action): 尝试为 Windows 平台构建容器镜像.
看 OpenJdk 18 应该是有对 Windows/amd64 支持的, 所以添加上去, 看看能不能构建成功.
2022-08-23 13:40:07 +08:00
a036695330 ci(github-action): 修复推送 tag 错误的问题.
修复一下这个问题, 如果不加用户名, 就会推送到 library 去.
2022-08-23 13:40:05 +08:00
e9c975f5c5 ci(github-action): 更改容器发布配置名称
忘记改名字了, 改名后会更好的识别工作流.
2022-08-23 13:40:02 +08:00
9aac42d414 ci(github-action): 增加一段容器构建发布配置.
试一下看看能不能生成容器, 如果可以的话就整合到发布工作流中.
2022-08-23 13:39:58 +08:00
5add6d9909 build(container): 增加一个 Docker Context 过滤文件.
该文件用于优化向 Docker Daemon 发送 Context 文件的文件数量, 增加之后, 从四千个文件降至143个文件, 优化显著.
2022-08-23 13:39:56 +08:00
eee6b7d2c9 build(container): 新增一个比较基础的 Dockerfile.
初步测试了一下运行, 没啥问题.
2022-08-23 13:39:53 +08:00
ff396425a7 refactor: 补充缺失代码.
由于先前的修改导致代码缺失, 我现在同意暂存功能很好用了.
2022-08-18 01:06:54 +08:00
580d9122e5 refactor(utils): 把日志记录器的获取方式改的优雅一点(对于测试来说).
由于 Kover 一直不把无代码高阶函数忽略掉, 所以稍微改一下这个日志记录器的获取方法, 让测试覆盖数据好看点(doge).
2022-08-17 23:01:06 +08:00
2a607f1129 fix(extension): 修复 ExtensionClassLoader 无法过滤非扩展包内 BotExtensionFactory 对象的问题.
按照 ServiceLoader 的规范, 文件应该是在 META-INF/services/{class} 这里的, 但当时忘记这个设计了, 导致直接判断 name == class, 然后失效.
修复好了.
2022-08-15 02:00:34 +08:00
2d6da7c1ae build(test): 添加 Jacoco 插件.
给 Extension 模块添加 Jacoco 插件, 以后估计会开 Codecov 来统计测试覆盖率.
2022-08-15 01:45:22 +08:00
6235c5f51a build(dependencies): 更新 Kotlin 版本(1.6.10 -> 1.7.10). 2022-08-15 01:39:08 +08:00
255a02c93c refactor(config): 重构 AppPaths 的构造方法, 应对将来 Kotlin 更新中的特性.
先前的方法是利用了初始化与调用的顺序, 来实现的 Supplier 互补(虽然在代码中, 确实存在未初始化调用的情况, 但实际运行的时候, 会先初始化, 再调用 Supplier),
但是未来 Kotlin 的更新中,编译器会把这个操作视为未初始化错误, 所以在这次改动中修复掉这个 bug 操作.
2022-08-15 01:38:08 +08:00
dce28be9c7 fix(logging): 修复日志滚动文件路径错误.
由于在滚动格式中没有使用 DATA_LOGS 占位符, 导致在日志滚动时会把日志归档文件保存在运行目录下, 而不是指定的数据目录,
该改动已修复该问题.
2022-08-13 13:19:09 +08:00
673c6d8392 build: 为项目支持可重现构建.
为确保项目的使用者(无论是开发者, 还是最终用户)可以完全重现构建, 确保安全, 故调整相关配置, 以实现"可重现构建".
有关可重现构建, 可以看这个: https://reproducible-builds.org/
2022-08-06 03:23:35 +08:00
d586ca378e fix(launch): 修复缺省的 Maven 中央库不遵循代理规则的问题.
这个属于漏网之鱼, 已修复.
2022-08-05 00:23:13 +08:00
3ba4364a07 test(config): 补全对 ProxyConfig 的单元测试.
先前加了个 ProxyConfig.toString 方法, 所以补充了一下测试项.
2022-08-04 19:13:45 +08:00
eda0e522cd docs(config): 补充关于 Gson 类型适配器的使用指导.
为 AppConfig 和 BotConfig 补充关于 Gson 类型适配器使用的信息, 以便于开发者正确使用类型适配器解析和编码 Json.
2022-07-22 21:59:14 +08:00
27 changed files with 666 additions and 110 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
**/
!scalabot-app/build/install/

33
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,33 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "gradle"
directory: "/"
schedule:
interval: "weekly"
# - package-ecosystem: "gradle"
# directory: "/scalabot-app"
# schedule:
# interval: "weekly"
# - package-ecosystem: "gradle"
# directory: "/scalabot-meta"
# schedule:
# interval: "weekly"
# - package-ecosystem: "gradle"
# directory: "/scalabot-extension"
# schedule:
# interval: "weekly"
# - package-ecosystem: "gradle"
# directory: "/scalabot-ext-example"
# schedule:
# interval: "weekly"

View File

@ -0,0 +1,36 @@
name: Binary compatibility verification (for API)
on:
push:
paths:
- 'scalabot-meta/**'
- 'scalabot-extension/**'
pull_request:
paths:
- 'scalabot-meta/**'
- 'scalabot-extension/**'
permissions:
contents: read
jobs:
apiCompatibilityCheck:
timeout-minutes: 8
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'adopt-hotspot'
cache: 'gradle'
- uses: gradle/wrapper-validation-action@v1
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build and run binary compatibility verification
uses: gradle/gradle-build-action@v2.4.0
with:
gradle-version: 'wrapper'
arguments: apiCheck

View File

@ -18,17 +18,18 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up JDK 11 - name: Set up JDK 11
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
java-version: '11' java-version: '11'
distribution: 'adopt-hotspot' distribution: 'adopt-hotspot'
cache: 'gradle' cache: 'gradle'
- name: Grant execute permission for gradlew - uses: gradle/wrapper-validation-action@v1
run: chmod +x gradlew - name: Grant execute permission for gradlew
- name: Build and test run: chmod +x gradlew
uses: gradle/gradle-build-action@v2.2.1 - name: Build and test
with: uses: gradle/gradle-build-action@v2.4.0
gradle-version: 'wrapper' with:
arguments: test gradle-version: 'wrapper'
arguments: test

65
.github/workflows/create-release.yml vendored Normal file
View File

@ -0,0 +1,65 @@
name: Create release draft
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+*'
# 该 Action 有以下步骤:
# 1. 拉取并构建代码, 然后生成 Application 发行包;
# 2. 创建 Release, 并标记为 Draft(草稿);
# 3. 上传 Application 发行包;
permissions:
contents: write
jobs:
create-release:
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
# 创建更新日志.
- name: 'Get Previous tag'
id: previous-tag
uses: younited/get-previous-tag-action@v1.0.0
with:
match: "v*.*.*"
- name: Set up Python 3
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install Commitizen
run: pip install -U commitizen
- name: Create Change log
run: cz ch --start-rev ${{ steps.previous-tag.outputs.previous-tag }} --file-name ${{ github.workspace }}/CURRENT_CHANGELOG.md
# 开始构建项目.
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'adopt-hotspot'
cache: 'gradle'
- uses: gradle/wrapper-validation-action@v1
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build and test
uses: gradle/gradle-build-action@v2.4.0
with:
gradle-version: 'wrapper'
arguments: clean test assembleDist
# 创建新的发行版本
- name: Create Release
uses: softprops/action-gh-release@v1
with:
draft: true
body_path: ${{ github.workspace }}/CURRENT_CHANGELOG.md
files: |
*/build/distributions/*
*/build/libs/*

View File

@ -1,10 +0,0 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1

49
.github/workflows/publish-artifacts.yml vendored Normal file
View File

@ -0,0 +1,49 @@
name: Publish artifacts
on:
release:
types:
- published
env:
IMAGE_NAME: lamgc/scalabot
jobs:
publish-container-image:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'adopt-hotspot'
cache: 'gradle'
- uses: gradle/wrapper-validation-action@v1
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build and test
uses: gradle/gradle-build-action@v2.4.0
with:
gradle-version: 'wrapper'
arguments: clean test installDist
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push container image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ env.IMAGE_NAME }}:latest, ${{ env.IMAGE_NAME }}:${{ github.ref_name }}
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@ -0,0 +1,49 @@
name: Build development version container image
on:
push:
branches:
- "main"
permissions:
contents: read
jobs:
build:
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'adopt-hotspot'
cache: 'gradle'
- uses: gradle/wrapper-validation-action@v1
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build project and install Distribution package
uses: gradle/gradle-build-action@v2.4.0
with:
gradle-version: 'wrapper'
arguments: installDist
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push container image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: lamgc/scalabot:dev
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max

8
Dockerfile Normal file
View File

@ -0,0 +1,8 @@
FROM openjdk:18
ENV BOT_DATA_PATH /scalabot/data/
WORKDIR /scalabot/run/
CMD ["/scalabot/app/bin/scalabot-app"]
COPY scalabot-app/build/install/scalabot-app/ /scalabot/app/

View File

@ -1,6 +1,7 @@
plugins { plugins {
kotlin("jvm") version "1.6.10" apply false kotlin("jvm") version "1.8.10" apply false
id("org.jetbrains.kotlinx.kover") version "0.5.1" apply false id("org.jetbrains.kotlinx.kover") version "0.6.1" apply false
id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.13.0" apply false
} }
allprojects { allprojects {
@ -12,5 +13,5 @@ allprojects {
} }
group = "net.lamgc" group = "net.lamgc"
version = "0.5.0" version = "0.6.0"
} }

View File

@ -10,9 +10,9 @@ dependencies {
implementation(project(":scalabot-meta")) implementation(project(":scalabot-meta"))
implementation(project(":scalabot-extension")) implementation(project(":scalabot-extension"))
implementation("org.slf4j:slf4j-api:1.7.36") implementation("org.slf4j:slf4j-api:2.0.6")
implementation("io.github.microutils:kotlin-logging:2.1.23") implementation("io.github.microutils:kotlin-logging:3.0.5")
implementation("ch.qos.logback:logback-classic:1.2.11") implementation("ch.qos.logback:logback-classic:1.4.5")
val aetherVersion = "1.1.0" val aetherVersion = "1.1.0"
implementation("org.eclipse.aether:aether-api:$aetherVersion") implementation("org.eclipse.aether:aether-api:$aetherVersion")
@ -23,20 +23,20 @@ dependencies {
implementation("org.eclipse.aether:aether-connector-basic:$aetherVersion") implementation("org.eclipse.aether:aether-connector-basic:$aetherVersion")
implementation("org.apache.maven:maven-aether-provider:3.3.9") implementation("org.apache.maven:maven-aether-provider:3.3.9")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.6.20") implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.10")
implementation("com.google.code.gson:gson:2.9.0") implementation("com.google.code.gson:gson:2.10.1")
implementation("org.jdom:jdom2:2.0.6.1") implementation("org.jdom:jdom2:2.0.6.1")
implementation("org.telegram:telegrambots-abilities:6.1.0") implementation("org.telegram:telegrambots-abilities:6.5.0")
implementation("org.telegram:telegrambots:6.1.0") implementation("org.telegram:telegrambots:6.5.0")
implementation("io.prometheus:simpleclient:0.15.0") implementation("io.prometheus:simpleclient:0.16.0")
implementation("io.prometheus:simpleclient_httpserver:0.15.0") implementation("io.prometheus:simpleclient_httpserver:0.16.0")
testImplementation(kotlin("test")) testImplementation(kotlin("test"))
testImplementation("io.mockk:mockk:1.12.4") testImplementation("io.mockk:mockk:1.13.4")
testImplementation("com.github.stefanbirkner:system-lambda:1.2.1") testImplementation("com.github.stefanbirkner:system-lambda:1.2.1")
} }
@ -56,3 +56,8 @@ application {
tasks.jar.configure { tasks.jar.configure {
exclude("**/logback-test.xml") exclude("**/logback-test.xml")
} }
tasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}

View File

@ -12,12 +12,15 @@ import org.eclipse.aether.repository.Authentication
import org.eclipse.aether.repository.Proxy import org.eclipse.aether.repository.Proxy
import org.eclipse.aether.repository.RemoteRepository import org.eclipse.aether.repository.RemoteRepository
import org.eclipse.aether.repository.RepositoryPolicy import org.eclipse.aether.repository.RepositoryPolicy
import org.slf4j.event.Level
import org.telegram.telegrambots.bots.DefaultBotOptions import org.telegram.telegrambots.bots.DefaultBotOptions
import java.io.File import java.io.File
import java.net.URL import java.net.URL
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import java.util.function.Supplier
import kotlin.reflect.KProperty
private val log = KotlinLogging.logger { } private val log = KotlinLogging.logger { }
@ -102,9 +105,9 @@ private fun createDefaultRepositoryId(): String {
* 必须提供 `pathSupplier` 或 `fileSupplier` 其中一个, 才能正常提供路径. * 必须提供 `pathSupplier` 或 `fileSupplier` 其中一个, 才能正常提供路径.
*/ */
internal enum class AppPaths( internal enum class AppPaths(
private val pathSupplier: () -> String = { fileSupplier.invoke().canonicalPath }, private val pathSupplier: PathSupplier,
private val initializer: AppPaths.() -> Unit = AppPaths::defaultInitializer, private val initializer: AppPaths.() -> Unit = AppPaths::defaultInitializer,
private val fileSupplier: () -> File = { File(pathSupplier()) } private val fileSupplier: FileSupplier,
) { ) {
/** /**
* 数据根目录. * 数据根目录.
@ -113,7 +116,7 @@ internal enum class AppPaths(
* *
* 提示: 结尾不带 `/`. * 提示: 结尾不带 `/`.
*/ */
DATA_ROOT(fileSupplier = { DATA_ROOT(fileSupplier = FileSupplier {
File( File(
System.getProperty(PathConst.PROP_DATA_PATH) ?: System.getenv(PathConst.ENV_DATA_PATH) System.getProperty(PathConst.PROP_DATA_PATH) ?: System.getenv(PathConst.ENV_DATA_PATH)
?: System.getProperty("user.dir") ?: "." ?: System.getProperty("user.dir") ?: "."
@ -125,7 +128,7 @@ internal enum class AppPaths(
} }
}), }),
CONFIG_APPLICATION({ "$DATA_ROOT/config.json" }, { CONFIG_APPLICATION(PathSupplier { "$DATA_ROOT/config.json" }, {
if (!file.exists()) { if (!file.exists()) {
file.bufferedWriter(StandardCharsets.UTF_8).use { file.bufferedWriter(StandardCharsets.UTF_8).use {
GsonConst.appConfigGson.toJson( GsonConst.appConfigGson.toJson(
@ -141,7 +144,7 @@ internal enum class AppPaths(
} }
} }
}), }),
CONFIG_BOT({ "$DATA_ROOT/bot.json" }, { CONFIG_BOT(PathSupplier { "$DATA_ROOT/bot.json" }, {
if (!file.exists()) { if (!file.exists()) {
file.bufferedWriter(StandardCharsets.UTF_8).use { file.bufferedWriter(StandardCharsets.UTF_8).use {
GsonConst.botConfigGson.toJson( GsonConst.botConfigGson.toJson(
@ -167,10 +170,25 @@ internal enum class AppPaths(
TEMP({ "$DATA_ROOT/tmp/" }) TEMP({ "$DATA_ROOT/tmp/" })
; ;
val file: File constructor(pathSupplier: PathSupplier, initializer: AppPaths.() -> Unit = AppPaths::defaultInitializer) : this(
get() = fileSupplier.invoke() fileSupplier = FileSupplier { File(pathSupplier.path).canonicalFile },
val path: String pathSupplier = pathSupplier,
get() = pathSupplier.invoke() initializer = initializer
)
constructor(fileSupplier: FileSupplier, initializer: AppPaths.() -> Unit = AppPaths::defaultInitializer) : this(
fileSupplier = fileSupplier,
pathSupplier = PathSupplier { fileSupplier.file.canonicalPath },
initializer = initializer
)
constructor(pathSupplier: () -> String) : this(
fileSupplier = FileSupplier { File(pathSupplier.invoke()).canonicalFile },
pathSupplier = PathSupplier { pathSupplier.invoke() }
)
val file: File by fileSupplier
val path: String by pathSupplier
private val initialized = AtomicBoolean(false) private val initialized = AtomicBoolean(false)
@ -206,6 +224,20 @@ internal enum class AppPaths(
const val ENV_DATA_PATH = "BOT_DATA_PATH" const val ENV_DATA_PATH = "BOT_DATA_PATH"
} }
private class FileSupplier(private val supplier: Supplier<File>) {
operator fun getValue(appPaths: AppPaths, property: KProperty<*>): File = supplier.get()
val file: File
get() = supplier.get()
}
private class PathSupplier(private val supplier: Supplier<String>) {
operator fun getValue(appPaths: AppPaths, property: KProperty<*>): String = supplier.get()
val path: String
get() = supplier.get()
}
} }
/** /**
@ -217,8 +249,37 @@ internal class LogDirectorySupplier : PropertyDefinerBase() {
} }
} }
internal class LogLevelSupplier : PropertyDefinerBase() {
override fun getPropertyValue(): String {
val property = System.getProperty("scalabot.log.level", System.getenv("BOT_LOG_LEVEL"))
val level = if (property != null) {
try {
Level.valueOf(property.uppercase())
} catch (e: IllegalArgumentException) {
addWarn("Invalid log level: `$property`, the log will be output using the Info log level.")
Level.INFO
}
} else {
Level.INFO
}
return level.name
}
}
internal class NetworkVerboseLogSupplier : PropertyDefinerBase() {
override fun getPropertyValue(): String {
val propertyValue = System.getProperty("scalabot.log.network.verbose", "false")
return if (propertyValue.toBoolean()) {
"DEBUG"
} else {
"INFO"
}
}
}
internal object Const { internal object Const {
val config = loadAppConfig() val config = loadAppConfig()
const val METRICS_NAMESPACE = "scalabot"
} }
private fun AppPaths.defaultInitializer() { private fun AppPaths.defaultInitializer() {

View File

@ -26,6 +26,7 @@ private val log = KotlinLogging.logger { }
fun main(args: Array<String>): Unit = runBlocking { fun main(args: Array<String>): Unit = runBlocking {
log.info { "ScalaBot 正在启动中..." } log.info { "ScalaBot 正在启动中..." }
log.info { "数据目录: ${AppPaths.DATA_ROOT}" } log.info { "数据目录: ${AppPaths.DATA_ROOT}" }
log.debug { "Kotlin: ${KotlinVersion.CURRENT}, JVM: ${Runtime.version()}" }
log.debug { "启动参数: ${args.joinToString(prefix = "[", postfix = "]")}" } log.debug { "启动参数: ${args.joinToString(prefix = "[", postfix = "]")}" }
if (initialFiles()) { if (initialFiles()) {
exitProcess(1) exitProcess(1)
@ -61,7 +62,10 @@ internal fun startMetricsServer(config: MetricsConfig = Const.config.metrics): H
return httpServer return httpServer
} }
internal class Launcher(private val config: AppConfig = Const.config) : AutoCloseable { internal class Launcher(
private val config: AppConfig = Const.config,
private val configFile: File = AppPaths.CONFIG_APPLICATION.file,
) : AutoCloseable {
companion object { companion object {
@JvmStatic @JvmStatic
@ -75,27 +79,25 @@ internal class Launcher(private val config: AppConfig = Const.config) : AutoClos
private fun getMavenLocalRepository(): LocalRepository { private fun getMavenLocalRepository(): LocalRepository {
val localPath = val localPath =
if (config.mavenLocalRepository != null && config.mavenLocalRepository!!.isNotEmpty()) { if (config.mavenLocalRepository != null && config.mavenLocalRepository!!.isNotEmpty()) {
val repoPath = AppPaths.DATA_ROOT.file.toPath() val repoPath = configFile.toPath().resolve(config.mavenLocalRepository!!).apply {
.resolve(config.mavenLocalRepository!!) if (!exists()) {
.apply { if (!parent.isWritable() || !parent.isReadable()) {
if (!exists()) { throw IOException("Unable to read and write the directory where Maven repository is located.")
if (!parent.isWritable() || !parent.isReadable()) { }
throw IOException("Unable to read and write the directory where Maven repository is located.") if (System.getProperty("os.name").lowercase().startsWith("windows")) {
} createDirectories()
if (System.getProperty("os.name").lowercase().startsWith("windows")) { } else {
createDirectories() val fileAttributes = setOf(
} else { PosixFilePermission.OWNER_READ,
val fileAttributes = setOf( PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OWNER_READ, PosixFilePermission.GROUP_READ,
PosixFilePermission.OWNER_WRITE, PosixFilePermission.GROUP_WRITE,
PosixFilePermission.GROUP_READ, PosixFilePermission.OTHERS_READ,
PosixFilePermission.GROUP_WRITE, )
PosixFilePermission.OTHERS_READ, createDirectories(PosixFilePermissions.asFileAttribute(fileAttributes))
)
createDirectories(PosixFilePermissions.asFileAttribute(fileAttributes))
}
} }
} }
}
.toRealPath() .toRealPath()
.toFile() .toFile()
repoPath repoPath
@ -182,7 +184,7 @@ internal class Launcher(private val config: AppConfig = Const.config) : AutoClos
it.url == MavenRepositoryExtensionFinder.MAVEN_CENTRAL_URL it.url == MavenRepositoryExtensionFinder.MAVEN_CENTRAL_URL
|| it.url == MavenRepositoryExtensionFinder.MAVEN_CENTRAL_URL.trimEnd('/') || it.url == MavenRepositoryExtensionFinder.MAVEN_CENTRAL_URL.trimEnd('/')
}) { }) {
add(MavenRepositoryExtensionFinder.getMavenCentralRepository(proxy = config.proxy.toAetherProxy())) add(MavenRepositoryExtensionFinder.getMavenCentralRepository(proxy = proxyConfig.toAetherProxy()))
} }
}.toList() }.toList()
val extensionPackageFinders = setOf( val extensionPackageFinders = setOf(

View File

@ -413,14 +413,14 @@ internal class ExtensionClassLoader(urls: Array<URL>, dependencyLoader: ClassLoa
// 以免使用了不来自扩展包的机器人扩展. // 以免使用了不来自扩展包的机器人扩展.
override fun getResources(name: String?): Enumeration<URL> { override fun getResources(name: String?): Enumeration<URL> {
if (BotExtensionFactory::class.java.equals(name)) { if ("META-INF/services/${BotExtensionFactory::class.java.name}" == name) {
return findResources(name) return findResources(name)
} }
return super.getResources(name) return super.getResources(name)
} }
override fun getResource(name: String?): URL? { override fun getResource(name: String?): URL? {
if (BotExtensionFactory::class.java.equals(name)) { if ("META-INF/services/${BotExtensionFactory::class.java}" == name) {
return findResource(name) return findResource(name)
} }
return super.getResource(name) return super.getResource(name)

View File

@ -50,6 +50,8 @@ internal class ScalaBot(
extensionFinders = extensionFinders extensionFinders = extensionFinders
) )
private val accountIdString = accountId.toString()
init { init {
log.info { "[Bot $botUsername] 正在加载扩展..." } log.info { "[Bot $botUsername] 正在加载扩展..." }
val extensionEntries = extensionLoader.getExtensions() val extensionEntries = extensionLoader.getExtensions()
@ -66,18 +68,18 @@ internal class ScalaBot(
override fun creatorId(): Long = creatorId override fun creatorId(): Long = creatorId
override fun onUpdateReceived(update: Update?) { override fun onUpdateReceived(update: Update?) {
botUpdateCounter.labels(botUsername).inc() botUpdateCounter.labels(botUsername, accountIdString).inc()
botUpdateGauge.labels(botUsername).inc() botUpdateGauge.labels(botUsername, accountIdString).inc()
val timer = updateProcessTime.labels(botUsername).startTimer() val timer = updateProcessTime.labels(botUsername, accountIdString).startTimer()
try { try {
super.onUpdateReceived(update) super.onUpdateReceived(update)
} catch (e: Exception) { } catch (e: Exception) {
exceptionHandlingCounter.labels(botUsername).inc() exceptionHandlingCounter.labels(botUsername, accountIdString).inc()
throw e throw e
} finally { } finally {
timer.observeDuration() timer.observeDuration()
botUpdateGauge.labels(botUsername).dec() botUpdateGauge.labels(botUsername, accountIdString).dec()
} }
} }
@ -134,7 +136,8 @@ internal class ScalaBot(
private val botUpdateCounter = Counter.build() private val botUpdateCounter = Counter.build()
.name("updates_total") .name("updates_total")
.help("Total number of updates received by all bots.") .help("Total number of updates received by all bots.")
.labelNames("bot_name") .labelNames("bot_name", "bot_id")
.namespace(Const.METRICS_NAMESPACE)
.subsystem("telegrambots") .subsystem("telegrambots")
.register() .register()
@ -142,7 +145,8 @@ internal class ScalaBot(
private val botUpdateGauge = Gauge.build() private val botUpdateGauge = Gauge.build()
.name("updates_in_progress") .name("updates_in_progress")
.help("Number of updates in process by all bots.") .help("Number of updates in process by all bots.")
.labelNames("bot_name") .labelNames("bot_name", "bot_id")
.namespace(Const.METRICS_NAMESPACE)
.subsystem("telegrambots") .subsystem("telegrambots")
.register() .register()
@ -150,6 +154,7 @@ internal class ScalaBot(
private val onlineBotGauge = Gauge.build() private val onlineBotGauge = Gauge.build()
.name("bots_online") .name("bots_online")
.help("Number of bots Online.") .help("Number of bots Online.")
.namespace(Const.METRICS_NAMESPACE)
.subsystem("telegrambots") .subsystem("telegrambots")
.register() .register()
@ -161,7 +166,8 @@ internal class ScalaBot(
"so it may be different from the actual execution time of ability. " + "so it may be different from the actual execution time of ability. " +
"It is not recommended to use it as the accurate execution time of ability)" "It is not recommended to use it as the accurate execution time of ability)"
) )
.labelNames("bot_name") .labelNames("bot_name", "bot_id")
.namespace(Const.METRICS_NAMESPACE)
.subsystem("telegrambots") .subsystem("telegrambots")
.register() .register()
@ -169,7 +175,8 @@ internal class ScalaBot(
private val exceptionHandlingCounter = Counter.build() private val exceptionHandlingCounter = Counter.build()
.name("updates_exception_handling") .name("updates_exception_handling")
.help("Number of exceptions during processing.") .help("Number of exceptions during processing.")
.labelNames("bot_name") .labelNames("bot_name", "bot_id")
.namespace(Const.METRICS_NAMESPACE)
.subsystem("telegrambots") .subsystem("telegrambots")
.register() .register()
} }

View File

@ -74,7 +74,7 @@ fun <T : AutoCloseable> T.registerShutdownHook(): T {
private object UtilsInternal { private object UtilsInternal {
val autoCloseableSet = mutableSetOf<AutoCloseable>() val autoCloseableSet = mutableSetOf<AutoCloseable>()
private val log = KotlinLogging.logger { } private val log = KotlinLogging.logger(UtilsInternal::class.java.name)
init { init {
Runtime.getRuntime().addShutdownHook(Thread(this::doCloseResources, "Shutdown-AutoCloseable")) Runtime.getRuntime().addShutdownHook(Thread(this::doCloseResources, "Shutdown-AutoCloseable"))

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<included> <included>
<define name="DATA_LOGS" class="net.lamgc.scalabot.LogDirectorySupplier"/> <define name="DATA_LOGS" class="net.lamgc.scalabot.LogDirectorySupplier"/>
<define name="LOG_LEVEL" class="net.lamgc.scalabot.LogLevelSupplier"/>
<define name="NETWORK_LOG_LEVEL" class="net.lamgc.scalabot.NetworkVerboseLogSupplier"/>
<appender name="STD_OUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STD_OUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>
@ -24,7 +26,7 @@
<appender name="FILE_OUT" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="FILE_OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${DATA_LOGS}/latest.log</file> <file>${DATA_LOGS}/latest.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>data/logs/%d{yyyy-MM-dd}.log.gz</fileNamePattern> <fileNamePattern>${DATA_LOGS}/%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>30</maxHistory> <maxHistory>30</maxHistory>
</rollingPolicy> </rollingPolicy>
<encoder> <encoder>

View File

@ -1,16 +0,0 @@
<configuration scan="false" debug="false">
<include resource="base-logback.xml"/>
<logger name="org.apache.http" level="INFO"/>
<logger name="org.eclipse.aether.internal.impl.DefaultTransporterProvider" level="INFO"/>
<logger name="org.eclipse.aether.internal.impl.DefaultRepositoryConnectorProvider" level="INFO"/>
<logger name="org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManager" level="INFO"/>
<logger name="org.telegram.telegrambots.facilities.proxysocketfactorys" level="INFO"/>
<logger name="org.eclipse.aether.internal.impl.DefaultUpdateCheckManager" level="INFO"/>
<root level="DEBUG">
<appender-ref ref="FILE_OUT"/>
<appender-ref ref="STD_ERR"/>
<appender-ref ref="STD_OUT"/>
</root>
</configuration>

View File

@ -1,7 +1,14 @@
<configuration scan="false" debug="false"> <configuration scan="false" debug="false">
<include resource="base-logback.xml"/> <include resource="base-logback.xml"/>
<root level="INFO"> <logger name="org.apache.http" level="${NETWORK_LOG_LEVEL}"/>
<logger name="org.eclipse.aether.internal.impl.DefaultTransporterProvider" level="${NETWORK_LOG_LEVEL}"/>
<logger name="org.eclipse.aether.internal.impl.DefaultRepositoryConnectorProvider" level="${NETWORK_LOG_LEVEL}"/>
<logger name="org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManager" level="${NETWORK_LOG_LEVEL}"/>
<logger name="org.telegram.telegrambots.facilities.proxysocketfactorys" level="${NETWORK_LOG_LEVEL}"/>
<logger name="org.eclipse.aether.internal.impl.DefaultUpdateCheckManager" level="${NETWORK_LOG_LEVEL}"/>
<root level="${LOG_LEVEL}">
<appender-ref ref="FILE_OUT"/> <appender-ref ref="FILE_OUT"/>
<appender-ref ref="STD_ERR"/> <appender-ref ref="STD_ERR"/>
<appender-ref ref="STD_OUT"/> <appender-ref ref="STD_OUT"/>

View File

@ -21,6 +21,17 @@ import kotlin.test.*
internal class AppPathsTest { internal class AppPathsTest {
@Test
fun `Consistency check`() {
for (path in AppPaths.values()) {
assertEquals(
File(path.path).canonicalPath,
path.file.canonicalPath,
"路径 File 与 Path 不一致: ${path.name}"
)
}
}
@Test @Test
fun `Data root path priority`() { fun `Data root path priority`() {
System.setProperty(AppPaths.PathConst.PROP_DATA_PATH, "fromSystemProperties") System.setProperty(AppPaths.PathConst.PROP_DATA_PATH, "fromSystemProperties")

View File

@ -5,7 +5,7 @@ plugins {
dependencies { dependencies {
compileOnly(project(":scalabot-extension")) compileOnly(project(":scalabot-extension"))
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
} }

View File

@ -0,0 +1,9 @@
public abstract interface class net/lamgc/scalabot/extension/BotExtensionFactory {
public abstract fun createExtensionInstance (Lorg/telegram/abilitybots/api/bot/BaseAbilityBot;Ljava/io/File;)Lorg/telegram/abilitybots/api/util/AbilityExtension;
}
public class net/lamgc/scalabot/extension/util/AbilityBots {
public static fun cancelReplyState (Lorg/telegram/abilitybots/api/bot/BaseAbilityBot;J)Z
public static fun getBotAccountId (Lorg/telegram/abilitybots/api/bot/BaseAbilityBot;)J
}

View File

@ -1,16 +1,19 @@
plugins { plugins {
kotlin("jvm") kotlin("jvm")
java java
jacoco
`maven-publish` `maven-publish`
signing signing
id("org.jetbrains.kotlinx.binary-compatibility-validator")
} }
dependencies { dependencies {
api("org.telegram:telegrambots-abilities:6.1.0") implementation("commons-codec:commons-codec:1.15")
api("org.slf4j:slf4j-api:1.7.36") api("org.telegram:telegrambots-abilities:6.5.0")
api("org.slf4j:slf4j-api:2.0.3")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2")
testImplementation("org.mockito:mockito-core:4.6.1") testImplementation("org.mockito:mockito-core:5.1.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
} }
@ -29,6 +32,16 @@ java {
tasks.test { tasks.test {
useJUnitPlatform() useJUnitPlatform()
finalizedBy(tasks.jacocoTestReport)
}
tasks.jacocoTestReport {
dependsOn(tasks.test)
}
tasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
} }
publishing { publishing {

View File

@ -0,0 +1,195 @@
public final class net/lamgc/scalabot/config/AppConfig {
public fun <init> ()V
public fun <init> (Lnet/lamgc/scalabot/config/ProxyConfig;Lnet/lamgc/scalabot/config/MetricsConfig;Ljava/util/List;Ljava/lang/String;)V
public synthetic fun <init> (Lnet/lamgc/scalabot/config/ProxyConfig;Lnet/lamgc/scalabot/config/MetricsConfig;Ljava/util/List;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lnet/lamgc/scalabot/config/ProxyConfig;
public final fun component2 ()Lnet/lamgc/scalabot/config/MetricsConfig;
public final fun component3 ()Ljava/util/List;
public final fun component4 ()Ljava/lang/String;
public final fun copy (Lnet/lamgc/scalabot/config/ProxyConfig;Lnet/lamgc/scalabot/config/MetricsConfig;Ljava/util/List;Ljava/lang/String;)Lnet/lamgc/scalabot/config/AppConfig;
public static synthetic fun copy$default (Lnet/lamgc/scalabot/config/AppConfig;Lnet/lamgc/scalabot/config/ProxyConfig;Lnet/lamgc/scalabot/config/MetricsConfig;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Lnet/lamgc/scalabot/config/AppConfig;
public fun equals (Ljava/lang/Object;)Z
public final fun getMavenLocalRepository ()Ljava/lang/String;
public final fun getMavenRepositories ()Ljava/util/List;
public final fun getMetrics ()Lnet/lamgc/scalabot/config/MetricsConfig;
public final fun getProxy ()Lnet/lamgc/scalabot/config/ProxyConfig;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class net/lamgc/scalabot/config/BotAccount {
public fun <init> (Ljava/lang/String;Ljava/lang/String;J)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()J
public final fun copy (Ljava/lang/String;Ljava/lang/String;J)Lnet/lamgc/scalabot/config/BotAccount;
public static synthetic fun copy$default (Lnet/lamgc/scalabot/config/BotAccount;Ljava/lang/String;Ljava/lang/String;JILjava/lang/Object;)Lnet/lamgc/scalabot/config/BotAccount;
public fun equals (Ljava/lang/Object;)Z
public final fun getCreatorId ()J
public final fun getId ()J
public final fun getName ()Ljava/lang/String;
public final fun getToken ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class net/lamgc/scalabot/config/BotConfig {
public fun <init> (ZLnet/lamgc/scalabot/config/BotAccount;ZZLjava/util/Set;Lnet/lamgc/scalabot/config/ProxyConfig;Ljava/lang/String;)V
public synthetic fun <init> (ZLnet/lamgc/scalabot/config/BotAccount;ZZLjava/util/Set;Lnet/lamgc/scalabot/config/ProxyConfig;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Z
public final fun component2 ()Lnet/lamgc/scalabot/config/BotAccount;
public final fun component3 ()Z
public final fun component4 ()Z
public final fun component5 ()Ljava/util/Set;
public final fun component6 ()Lnet/lamgc/scalabot/config/ProxyConfig;
public final fun component7 ()Ljava/lang/String;
public final fun copy (ZLnet/lamgc/scalabot/config/BotAccount;ZZLjava/util/Set;Lnet/lamgc/scalabot/config/ProxyConfig;Ljava/lang/String;)Lnet/lamgc/scalabot/config/BotConfig;
public static synthetic fun copy$default (Lnet/lamgc/scalabot/config/BotConfig;ZLnet/lamgc/scalabot/config/BotAccount;ZZLjava/util/Set;Lnet/lamgc/scalabot/config/ProxyConfig;Ljava/lang/String;ILjava/lang/Object;)Lnet/lamgc/scalabot/config/BotConfig;
public fun equals (Ljava/lang/Object;)Z
public final fun getAccount ()Lnet/lamgc/scalabot/config/BotAccount;
public final fun getAutoUpdateCommandList ()Z
public final fun getBaseApiUrl ()Ljava/lang/String;
public final fun getDisableBuiltInAbility ()Z
public final fun getEnabled ()Z
public final fun getExtensions ()Ljava/util/Set;
public final fun getProxy ()Lnet/lamgc/scalabot/config/ProxyConfig;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class net/lamgc/scalabot/config/MavenRepositoryConfig {
public fun <init> (Ljava/lang/String;Ljava/net/URL;Lorg/eclipse/aether/repository/Proxy;Ljava/lang/String;ZZLorg/eclipse/aether/repository/Authentication;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/net/URL;Lorg/eclipse/aether/repository/Proxy;Ljava/lang/String;ZZLorg/eclipse/aether/repository/Authentication;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/net/URL;
public final fun component3 ()Lorg/eclipse/aether/repository/Proxy;
public final fun component4 ()Ljava/lang/String;
public final fun component5 ()Z
public final fun component6 ()Z
public final fun component7 ()Lorg/eclipse/aether/repository/Authentication;
public final fun copy (Ljava/lang/String;Ljava/net/URL;Lorg/eclipse/aether/repository/Proxy;Ljava/lang/String;ZZLorg/eclipse/aether/repository/Authentication;)Lnet/lamgc/scalabot/config/MavenRepositoryConfig;
public static synthetic fun copy$default (Lnet/lamgc/scalabot/config/MavenRepositoryConfig;Ljava/lang/String;Ljava/net/URL;Lorg/eclipse/aether/repository/Proxy;Ljava/lang/String;ZZLorg/eclipse/aether/repository/Authentication;ILjava/lang/Object;)Lnet/lamgc/scalabot/config/MavenRepositoryConfig;
public fun equals (Ljava/lang/Object;)Z
public final fun getAuthentication ()Lorg/eclipse/aether/repository/Authentication;
public final fun getEnableReleases ()Z
public final fun getEnableSnapshots ()Z
public final fun getId ()Ljava/lang/String;
public final fun getLayout ()Ljava/lang/String;
public final fun getProxy ()Lorg/eclipse/aether/repository/Proxy;
public final fun getUrl ()Ljava/net/URL;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class net/lamgc/scalabot/config/MetricsConfig {
public fun <init> ()V
public fun <init> (ZILjava/lang/String;Lnet/lamgc/scalabot/config/UsernameAuthenticator;)V
public synthetic fun <init> (ZILjava/lang/String;Lnet/lamgc/scalabot/config/UsernameAuthenticator;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Z
public final fun component2 ()I
public final fun component3 ()Ljava/lang/String;
public final fun component4 ()Lnet/lamgc/scalabot/config/UsernameAuthenticator;
public final fun copy (ZILjava/lang/String;Lnet/lamgc/scalabot/config/UsernameAuthenticator;)Lnet/lamgc/scalabot/config/MetricsConfig;
public static synthetic fun copy$default (Lnet/lamgc/scalabot/config/MetricsConfig;ZILjava/lang/String;Lnet/lamgc/scalabot/config/UsernameAuthenticator;ILjava/lang/Object;)Lnet/lamgc/scalabot/config/MetricsConfig;
public fun equals (Ljava/lang/Object;)Z
public final fun getAuthenticator ()Lnet/lamgc/scalabot/config/UsernameAuthenticator;
public final fun getBindAddress ()Ljava/lang/String;
public final fun getEnable ()Z
public final fun getPort ()I
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class net/lamgc/scalabot/config/ProxyConfig {
public fun <init> ()V
public fun <init> (Lnet/lamgc/scalabot/config/ProxyType;Ljava/lang/String;I)V
public synthetic fun <init> (Lnet/lamgc/scalabot/config/ProxyType;Ljava/lang/String;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lnet/lamgc/scalabot/config/ProxyType;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()I
public final fun copy (Lnet/lamgc/scalabot/config/ProxyType;Ljava/lang/String;I)Lnet/lamgc/scalabot/config/ProxyConfig;
public static synthetic fun copy$default (Lnet/lamgc/scalabot/config/ProxyConfig;Lnet/lamgc/scalabot/config/ProxyType;Ljava/lang/String;IILjava/lang/Object;)Lnet/lamgc/scalabot/config/ProxyConfig;
public fun equals (Ljava/lang/Object;)Z
public final fun getHost ()Ljava/lang/String;
public final fun getPort ()I
public final fun getType ()Lnet/lamgc/scalabot/config/ProxyType;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class net/lamgc/scalabot/config/ProxyType : java/lang/Enum {
public static final field HTTP Lnet/lamgc/scalabot/config/ProxyType;
public static final field HTTPS Lnet/lamgc/scalabot/config/ProxyType;
public static final field NO_PROXY Lnet/lamgc/scalabot/config/ProxyType;
public static final field SOCKS4 Lnet/lamgc/scalabot/config/ProxyType;
public static final field SOCKS5 Lnet/lamgc/scalabot/config/ProxyType;
public static fun valueOf (Ljava/lang/String;)Lnet/lamgc/scalabot/config/ProxyType;
public static fun values ()[Lnet/lamgc/scalabot/config/ProxyType;
}
public final class net/lamgc/scalabot/config/UsernameAuthenticator : com/sun/net/httpserver/BasicAuthenticator {
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public fun checkCredentials (Ljava/lang/String;Ljava/lang/String;)Z
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
public final fun toJsonObject ()Lcom/google/gson/JsonObject;
}
public final class net/lamgc/scalabot/config/serializer/ArtifactSerializer : com/google/gson/JsonDeserializer, com/google/gson/JsonSerializer {
public static final field INSTANCE Lnet/lamgc/scalabot/config/serializer/ArtifactSerializer;
public synthetic fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Ljava/lang/Object;
public fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lorg/eclipse/aether/artifact/Artifact;
public synthetic fun serialize (Ljava/lang/Object;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;
public fun serialize (Lorg/eclipse/aether/artifact/Artifact;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;
}
public final class net/lamgc/scalabot/config/serializer/AuthenticationSerializer : com/google/gson/JsonDeserializer {
public static final field INSTANCE Lnet/lamgc/scalabot/config/serializer/AuthenticationSerializer;
public synthetic fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Ljava/lang/Object;
public fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lorg/eclipse/aether/repository/Authentication;
}
public final class net/lamgc/scalabot/config/serializer/BotAccountSerializer : com/google/gson/JsonDeserializer {
public static final field INSTANCE Lnet/lamgc/scalabot/config/serializer/BotAccountSerializer;
public synthetic fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Ljava/lang/Object;
public fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/lamgc/scalabot/config/BotAccount;
}
public final class net/lamgc/scalabot/config/serializer/BotConfigSerializer : com/google/gson/JsonDeserializer, com/google/gson/JsonSerializer {
public static final field INSTANCE Lnet/lamgc/scalabot/config/serializer/BotConfigSerializer;
public synthetic fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Ljava/lang/Object;
public fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/lamgc/scalabot/config/BotConfig;
public synthetic fun serialize (Ljava/lang/Object;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;
public fun serialize (Lnet/lamgc/scalabot/config/BotConfig;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;
}
public final class net/lamgc/scalabot/config/serializer/MavenRepositoryConfigSerializer : com/google/gson/JsonDeserializer {
public static final field INSTANCE Lnet/lamgc/scalabot/config/serializer/MavenRepositoryConfigSerializer;
public synthetic fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Ljava/lang/Object;
public fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/lamgc/scalabot/config/MavenRepositoryConfig;
}
public final class net/lamgc/scalabot/config/serializer/ProxyConfigSerializer : com/google/gson/JsonDeserializer, com/google/gson/JsonSerializer {
public static final field INSTANCE Lnet/lamgc/scalabot/config/serializer/ProxyConfigSerializer;
public synthetic fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Ljava/lang/Object;
public fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/lamgc/scalabot/config/ProxyConfig;
public synthetic fun serialize (Ljava/lang/Object;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;
public fun serialize (Lnet/lamgc/scalabot/config/ProxyConfig;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;
}
public final class net/lamgc/scalabot/config/serializer/ProxyTypeSerializer : com/google/gson/JsonDeserializer, com/google/gson/JsonSerializer {
public static final field INSTANCE Lnet/lamgc/scalabot/config/serializer/ProxyTypeSerializer;
public synthetic fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Ljava/lang/Object;
public fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/lamgc/scalabot/config/ProxyType;
public synthetic fun serialize (Ljava/lang/Object;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;
public fun serialize (Lnet/lamgc/scalabot/config/ProxyType;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;
}
public final class net/lamgc/scalabot/config/serializer/UsernameAuthenticatorSerializer : com/google/gson/JsonDeserializer, com/google/gson/JsonSerializer {
public static final field INSTANCE Lnet/lamgc/scalabot/config/serializer/UsernameAuthenticatorSerializer;
public synthetic fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Ljava/lang/Object;
public fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/lamgc/scalabot/config/UsernameAuthenticator;
public synthetic fun serialize (Ljava/lang/Object;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;
public fun serialize (Lnet/lamgc/scalabot/config/UsernameAuthenticator;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;
}

View File

@ -1,9 +1,10 @@
plugins { plugins {
kotlin("jvm") kotlin("jvm")
id("org.jetbrains.kotlinx.kover") id("org.jetbrains.kotlinx.kover")
id("org.jetbrains.dokka") version "1.7.0" id("org.jetbrains.dokka") version "1.7.20"
`maven-publish` `maven-publish`
signing signing
id("org.jetbrains.kotlinx.binary-compatibility-validator")
} }
dependencies { dependencies {
@ -11,16 +12,16 @@ dependencies {
api("org.eclipse.aether:aether-api:$aetherVersion") api("org.eclipse.aether:aether-api:$aetherVersion")
implementation("org.eclipse.aether:aether-util:$aetherVersion") implementation("org.eclipse.aether:aether-util:$aetherVersion")
implementation("org.telegram:telegrambots-meta:6.1.0") implementation("org.telegram:telegrambots-meta:6.5.0")
api("com.google.code.gson:gson:2.9.0") api("com.google.code.gson:gson:2.10.1")
testImplementation(kotlin("test")) testImplementation(kotlin("test"))
testImplementation("io.mockk:mockk:1.12.4") testImplementation("io.mockk:mockk:1.13.2")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2")
dokkaHtmlPlugin("org.jetbrains.dokka:javadoc-plugin:1.7.0") dokkaHtmlPlugin("org.jetbrains.dokka:javadoc-plugin:1.7.20")
} }
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach { tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
@ -36,6 +37,11 @@ java {
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
} }
tasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}
tasks.getByName<Test>("test") { tasks.getByName<Test>("test") {
useJUnitPlatform() useJUnitPlatform()
} }

View File

@ -28,6 +28,13 @@ data class BotAccount(
/** /**
* 机器人配置. * 机器人配置.
*
* 使用 Gson 解析时, 请添加以下类型适配器:
* - [net.lamgc.scalabot.config.serializer.ProxyTypeSerializer]
* - [net.lamgc.scalabot.config.serializer.BotConfigSerializer]
* - [net.lamgc.scalabot.config.serializer.BotAccountSerializer]
* - [net.lamgc.scalabot.config.serializer.ArtifactSerializer]
*
* @property enabled 是否启用机器人. * @property enabled 是否启用机器人.
* @property account 机器人帐号信息, 用于访问 API. * @property account 机器人帐号信息, 用于访问 API.
* @property disableBuiltInAbility 是否禁用 AbilityBot 自带命令. * @property disableBuiltInAbility 是否禁用 AbilityBot 自带命令.
@ -126,6 +133,13 @@ data class MavenRepositoryConfig(
* ScalaBot App 配置. * ScalaBot App 配置.
* *
* App 配置信息与 BotConfig 分开, 分别存储在各自单独的文件中. * App 配置信息与 BotConfig 分开, 分别存储在各自单独的文件中.
*
* 使用 Gson 解析时, 请添加以下类型适配器:
* - [net.lamgc.scalabot.config.serializer.ProxyTypeSerializer]
* - [net.lamgc.scalabot.config.serializer.MavenRepositoryConfigSerializer]
* - [net.lamgc.scalabot.config.serializer.AuthenticationSerializer]
* - [net.lamgc.scalabot.config.serializer.UsernameAuthenticatorSerializer]
*
* @property proxy Telegram API 代理配置. * @property proxy Telegram API 代理配置.
* @property metrics 运行指标数据配置. 可通过时序数据库记录运行数据. * @property metrics 运行指标数据配置. 可通过时序数据库记录运行数据.
* @property mavenRepositories Maven 远端仓库配置. * @property mavenRepositories Maven 远端仓库配置.

View File

@ -219,6 +219,12 @@ internal class ProxyConfigTest {
assertEquals(8080, actualConfig.port) assertEquals(8080, actualConfig.port)
assertEquals(ProxyType.HTTP, actualConfig.type) assertEquals(ProxyType.HTTP, actualConfig.type)
} }
@Test
fun `toString test`() {
assertEquals("NO_PROXY", ProxyConfig(ProxyType.NO_PROXY).toString())
assertEquals("HTTP://example.org:1008", ProxyConfig(ProxyType.HTTP, "example.org", 1008).toString())
}
} }
internal class MetricsConfigTest { internal class MetricsConfigTest {