feat(service): 添加 JM 下载功能
All checks were successful
Sakura-Miki-build / Automatic-Packaging (push) Successful in 2m27s

- 新增 DownloadUtil 工具类,用于处理 JMComic 下载任务
- 添加 JmDownloadService 服务类,实现 /JmDownload 命令的处理
- 更新 .gitea/workflows/build-test.yaml,重命名为 package.yaml 并调整工作流名称- 修改 MFAGenerateService、RandomPhotoService 和 ExampleService,优化命令前缀和参数处理
- 新增 JsonUtil 工具类,用于解析 JSON 数据
- 更新 AlistUtil 依赖版本至 1.0.1-spring
This commit is contained in:
Grand-cocoa 2025-03-24 17:23:13 +08:00
parent 4bc526d48c
commit 4f37aace85
9 changed files with 155 additions and 8 deletions

View File

@ -1,11 +1,11 @@
name: Sakura-Miki-build
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
run-name: Automatic-Packaging 📦
on: [ push ]
env:
BARE_REPO_DIR: https://git.alina-dace.info/Dace/Sakura-Miki.git
CLONED_REPO_DIR: ./
jobs:
Explore-Gitea-Actions:
Automatic-Packaging:
runs-on: ubuntu-latest
steps:
- name: Checkout Git Repo
@ -18,7 +18,7 @@ jobs:
distribution: 'temurin'
cache: maven
- run: chmod +x ./mvnw
- run: ./mvnw install:install-file -Dfile=./lib/AlistUtil-1.0.0-spring.jar -Dpackaging=jar -DgroupId=io.github.1530624156 -DartifactId=AlistUtil -Dversion=1.0.0-spring
- run: ./mvnw install:install-file -Dfile=./lib/AlistUtil-1.0.1-spring.jar -Dpackaging=jar -DgroupId=io.github.1530624156 -DartifactId=AlistUtil -Dversion=1.0.1-spring
- name: build
run: ./mvnw clean package -DskipTests=true -P prod
- run: docker build -t sakura-miki:latest ./

Binary file not shown.

View File

@ -136,7 +136,7 @@
<dependency>
<groupId>io.github.1530624156</groupId>
<artifactId>AlistUtil</artifactId>
<version>1.0.0-spring</version>
<version>1.0.1-spring</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>

View File

@ -27,7 +27,7 @@ class ExampleService : Service<MessageV2Event> {
override fun entrance(event: MessageV2Event): Boolean {
val chain = event.message
if (chain.size == 1 && chain[0] is PlainText && event.sender.id == botConfig.admin) {
if (chain[0].toString() == "测试") {
if (chain[0].toString().trim() == "测试") {
return true
}
}

View File

@ -0,0 +1,49 @@
package info.alinadace.sakuramiki.service.jm
import info.alinadace.sakuramiki.annotation.BotFunction
import info.alinadace.sakuramiki.configuration.BotConfig
import info.alinadace.sakuramiki.service.Service
import info.alinadace.sakuramiki.service.jm.util.DownloadUtil
import io.github.kloping.qqbot.api.v2.FriendMessageEvent
import io.github.kloping.qqbot.api.v2.GroupMessageEvent
import io.github.kloping.qqbot.api.v2.MessageV2Event
import io.github.kloping.qqbot.entities.ex.PlainText
import jakarta.annotation.Resource
/**
* @author Kane
* @since 2025/3/24 15:36
*/
@BotFunction(FriendMessageEvent::class, GroupMessageEvent::class)
class JmDownloadService: Service<MessageV2Event>{
@Resource
lateinit var botConfig: BotConfig
@Resource
lateinit var downloadUtil: DownloadUtil
/**
* 服务入口
*/
override fun entrance(event: MessageV2Event): Boolean {
val chain = event.message
if (chain.size == 1 && chain[0] is PlainText && event.sender.id == botConfig.admin) {
if (chain[0].toString().trim().startsWith("/JmDownload")) {
return true
}
}
return false
}
/**
* 服务行为
*/
override fun active(event: MessageV2Event) {
val chain = event.message
val code = chain[0].toString().trim().split(" ")[1]
event.send("任务已添加")
downloadUtil.start(code)
event.send("任务已完成请前往Alist查看")
}
}

View File

@ -0,0 +1,85 @@
package info.alinadace.sakuramiki.service.jm.util
import cn.hutool.core.lang.UUID
import com.mavis.service.AlistService
import info.alinadace.sakuramiki.util.okHttpClient
import info.alinadace.sakuramiki.util.parse
import jakarta.annotation.Resource
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.springframework.stereotype.Service
/**
* @author Kane
* @since 2025/3/24 15:38
*/
@Service
class DownloadUtil {
companion object {
private const val DIR = "/AliDrive/JMComic"
private const val OWNER = "Grand-cocoa"
private const val REPO = "JMComic-Crawler-Python"
private const val WORKFLOW_ID = "151600689"
private const val BASE = "https://api.github.com"
private const val CREATE = "${BASE}/repos/${OWNER}/${REPO}/actions/workflows/${WORKFLOW_ID}/dispatches"
private const val LIST = "${BASE}/repos/${OWNER}/${REPO}/actions/workflows/${WORKFLOW_ID}/runs"
private const val GET = "${BASE}/repos/${OWNER}/${REPO}/actions/runs/%d"
private const val ARTIFACTS = "${BASE}/repos/${OWNER}/${REPO}/actions/runs/%d/artifacts"
private const val DOWNLOAD = "${BASE}/repos/${OWNER}/${REPO}/actions/artifacts/%d/zip"
private const val APIKEY = "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"
private const val CREATE_BODY = """{"inputs": {"JM_ALBUM_IDS": "%s","JM_PHOTO_IDS": ""},"ref": "master"}"""
private fun OkHttpClient.newCall(url: String, method: String, body: RequestBody? = null): Response {
return this.newCall(Request.Builder()
.url(url)
.method(method, body)
// .addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer $APIKEY")
.addHeader("User-Agent", "Apifox/1.0.0 (https://apifox.com)")
.addHeader("Accept", "*/*")
.addHeader("Host", "api.github.com")
.addHeader("Connection", "keep-alive")
.build()).execute()
}
}
@Resource
lateinit var alistService: AlistService
fun start(id: String){
val createBody = CREATE_BODY.format(id).toRequestBody()
// Request.Builder().post(createBody).addHeader()
val create = okHttpClient.newCall(CREATE, "POST", createBody)
if (create.code != 200) return
Thread.sleep(3000)
val list = okHttpClient.newCall(LIST, "GET")
if (list.code != 200) return
val listBody = parse(list.body?.string()!!)
val jobId = listBody.getByPath<Int>("workflow_runs[0].id")
var status = false
for (i in 0..10){
Thread.sleep(10000)
val get = okHttpClient.newCall(GET.format(jobId), "GET")
if (get.code != 200) return
val getBody = parse(get.body?.string()!!)
if (getBody.getByPath<String>("conclusion") == "success"){
status = true
break
}
}
if (!status) return
val artifacts = okHttpClient.newCall(ARTIFACTS.format(jobId), "GET")
if (artifacts.code != 200) return
val artifactsBody = parse(artifacts.body?.string()!!)
val artifactId = artifactsBody.getByPath<Int>("artifacts[0].id")
val download = okHttpClient.newCall(DOWNLOAD.format(artifactId), "GET")
if (download.code != 200) return
val blob = download.body?.bytes()
val fastUUID = UUID.fastUUID().toString(true)
alistService.uploadFile(blob, DIR, fastUUID)
}
}

View File

@ -31,7 +31,7 @@ class MFAGenerateService : Service<FriendMessageEvent> {
if (message.size != 1 && message.size != 2) {
return false
}
if (message[0] is PlainText && message[0].toString().startsWith("mfa")){
if (message[0] is PlainText && message[0].toString().startsWith("/mfa")){
if (message.size == 1){
val split = message[0].toString().split(" ")
return split.size <= 2
@ -45,7 +45,7 @@ class MFAGenerateService : Service<FriendMessageEvent> {
*/
override fun active(event: FriendMessageEvent) {
val message = event.message
if (message[0] is PlainText && message[0].toString().startsWith("mfa")){
if (message[0] is PlainText && message[0].toString().startsWith("/mfa")){
val command = message[0].toString().split(" ")
if (command.size == 1){
val mfa = mapper.selectOne(

View File

@ -31,7 +31,7 @@ class RandomPhotoService : Service<MessageV2Event> {
*/
override fun entrance(event: MessageV2Event): Boolean {
val chain = event.message
return chain.size == 1 && chain[0] is PlainText && chain[0].toString() == "#photo"
return chain.size == 1 && chain[0] is PlainText && chain[0].toString().trim() == "/photo"
}
/**

View File

@ -0,0 +1,13 @@
package info.alinadace.sakuramiki.util
import cn.hutool.core.lang.Dict
import com.fasterxml.jackson.databind.ObjectMapper
/**
* @author Kane
* @since 2025/3/24 16:10
*/
private val OBJECT_MAPPER = ObjectMapper()
fun parse(json: String): Dict {
return OBJECT_MAPPER.readValue(json, OBJECT_MAPPER.typeFactory.constructType(Dict::class.java))
}