diff --git a/README.md b/README.md index 90d36e7..47e36a4 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,14 @@ A Mirai Console Plugin ### 本插件提供的功能 * 防止撤回,自动将撤回内容重新发布到群中并@发布人(请注意,被管理员撤回的内容将会被本插件忽略) -* ~~将撤回内容保存到本地 (硬编码中默认关闭,需要开启此功能请修改代码并编译自己的版本)~~ -* 保存到本地的功能先摆了 -* ~~这个插件的测试也先摆了,emo中谁爱怎么弄怎么弄吧~~ +* 将撤回内容保存到本地 (硬编码中默认关闭,需要开启此功能请修改代码并编译自己的版本) ### 为什么要有这个插件? ``` null好像在群里发了双丁然后撤回了。 我从中午12点emo到下午一点半, -连null发的图都没看到, -我真的要emo到第二天天亮去。 - -SO F**K THIS S**T -下次别想撤回了! +连null发的图都没看到。 ``` -累了,赶紧的,毁灭吧。 \ No newline at end of file +累了,赶紧的,毁灭吧。 +### Download +自己编译,请。 \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 2619cbf..f7f729f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,9 +7,14 @@ plugins { } group = "love.marblegate" -version = "0.1.0" +version = "1.0.0" repositories { maven("https://maven.aliyun.com/repository/public") mavenCentral() } + +dependencies{ + implementation("cn.hutool:hutool-http:5.8.0") + implementation("com.google.guava:guava:31.1-jre") +} diff --git a/src/main/kotlin/love/marblegate/evilgemira/EvilService.kt b/src/main/kotlin/love/marblegate/evilgemira/EvilService.kt index 2f2f23b..5cd025b 100644 --- a/src/main/kotlin/love/marblegate/evilgemira/EvilService.kt +++ b/src/main/kotlin/love/marblegate/evilgemira/EvilService.kt @@ -1,9 +1,85 @@ package love.marblegate.evilgemira -import net.mamoe.mirai.message.data.MessageChain +import cn.hutool.http.HttpUtil +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import net.mamoe.mirai.event.events.MessageRecallEvent +import net.mamoe.mirai.message.data.* +import net.mamoe.mirai.message.data.Image.Key.queryUrl +import net.mamoe.mirai.utils.MiraiInternalApi +import java.io.ByteArrayInputStream +import java.io.File +import java.io.IOException +import java.io.OutputStreamWriter +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.StandardOpenOption +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import javax.imageio.ImageIO object EvilService{ - fun MessageChain.saveTo(path: String){ - // TODO + @OptIn(MiraiInternalApi::class) + fun DataUnit.saveTo(filePath: String){ + val m = if(this.message.contains(QuoteReply)) "引用消息" else "消息" + val content = this.message.filter { singleMessage -> singleMessage !is MessageMetadata } + val images = content.filterIsInstance() + if(images.isNotEmpty()){ + CoroutineScope(Dispatchers.IO).launch{ + for (image in images) { + image.saveTo( "$filePath${this@saveTo.event.group.id}" + File.separator + "image" + File.separator + "${image.imageId}.${image.imageType.formatName}") + } + } + } + val detailedPath = "$filePath${this.event.group.id}" + File.separator + "${LocalDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE)}.txt" + val ctx = """ + 群成员${this.event.author.nick}(${this.event.author.id}) 于 ${LocalDateTime.now()} 撤回了一条$m: + ${ + content.joinToString("\n") { singleMessage -> + when (singleMessage) { + is At -> "At@${singleMessage.target}" + is Image -> "图片内容(已保存至/image/${singleMessage.imageId}.${singleMessage.imageType.formatName})" + else -> singleMessage.content + } + }} + """.trimIndent() + "\n" + CoroutineScope(Dispatchers.IO).launch{ + ctx.appendToRecord(ensureDir(detailedPath)) + } +} + + private fun String.appendToRecord(path: String) { + OutputStreamWriter( + Files.newOutputStream(Paths.get(path), StandardOpenOption.APPEND), + StandardCharsets.UTF_8 + ).use { writer -> writer.write(this) } + } + + @OptIn(MiraiInternalApi::class) + private suspend fun Image.saveTo(path:String){ + val stream = ByteArrayInputStream(HttpUtil.downloadBytes(this.queryUrl())) + val image = withContext(Dispatchers.IO) { + ImageIO.read(stream) + } + withContext(Dispatchers.IO) { + ImageIO.write(image, this@saveTo.imageType.formatName, File(ensureDir(path))) + } } + + private fun ensureDir(path: String): String { + val file = File(path) + val fileParent = file.parentFile + if (!fileParent.exists()) { + fileParent.mkdirs() + } + if (!file.exists()) { + file.createNewFile() + } + return path + } + + data class DataUnit(val event: MessageRecallEvent.GroupRecall, val message: MessageChain); } diff --git a/src/main/kotlin/love/marblegate/evilgemira/GemiraService.kt b/src/main/kotlin/love/marblegate/evilgemira/GemiraService.kt index f833707..a19ec91 100644 --- a/src/main/kotlin/love/marblegate/evilgemira/GemiraService.kt +++ b/src/main/kotlin/love/marblegate/evilgemira/GemiraService.kt @@ -1,5 +1,6 @@ package love.marblegate.evilgemira +import com.google.common.collect.HashBasedTable import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -7,11 +8,13 @@ import love.marblegate.evilgemira.EvilService.saveTo import net.mamoe.mirai.event.events.GroupMessageEvent import net.mamoe.mirai.event.events.MessageRecallEvent import net.mamoe.mirai.message.data.* +import java.io.File import java.util.* object GemiraService { - private val datas = listOf(mutableMapOf(),mutableMapOf()) + private val datas = listOf(HashBasedTable.create(),HashBasedTable.create()) private var activeMap = 0 + private val path = System.getProperty("user.dir") + File.separator + "data" + File.separator + "evil_gemira" + File.separator init { Timer().schedule(object : TimerTask(){ @@ -29,9 +32,17 @@ object GemiraService { val messageChain = event.retrieve() if(messageChain!=null){ val builder = MessageChainBuilder() - builder.append(At(event.authorId)).append(" 刚刚撤回了:\n").append(messageChain) - CoroutineScope(Dispatchers.Default).launch{ + builder.append(At(event.authorId)) + if(messageChain.contains(QuoteReply)){ + builder.append(" 刚刚撤回一条引用消息,内容如下:\n") + } else { + builder.append(" 刚刚撤回一条消息,内容如下:\n") + } + builder.append(messageChain.filter { singleMessage -> singleMessage !is MessageMetadata }.toMessageChain()) + CoroutineScope(Dispatchers.IO).launch{ event.group.sendMessage(builder.asMessageChain()) + // Pandora's box + // EvilService.DataUnit(event,messageChain).saveTo(path) } } } @@ -39,7 +50,7 @@ object GemiraService { fun saveMessageTOData(event: GroupMessageEvent){ if(event.message.valid()) - datas[activeMap][event.message.ids] = event.message + datas[activeMap].put(event.group.id,event.time,event.message) } private fun MessageRecallEvent.GroupRecall.valid(): Boolean{ @@ -51,27 +62,19 @@ object GemiraService { } private fun MessageRecallEvent.GroupRecall.retrieve(): MessageChain?{ - if(datas[0].contains(this.messageIds)){ - return datas[0][this.messageIds] + if(datas[0].contains(this.group.id,this.messageTime)){ + return datas[0].get(this.group.id,this.messageTime) } else { - if(datas[1].contains(this.messageIds)) - return datas[1][this.messageIds] + if(datas[1].contains(this.group.id,this.messageTime)) + return datas[1][this.group.id,this.messageTime] } return null } private fun MessageChain.valid(): Boolean { - if (this.contains(QuoteReply)) { - return false - } for (message in this) { - if (!(message is MessageSource || message is PlainText || message is At || message is Image)) return false + if (!(message is QuoteReply || message is MessageSource || message is PlainText || message is At || message is Image)) return false } return true } - - private fun save(message: MessageChain) { - message.saveTo("TODO") - // TODO use EvilService shit to save content to local - } } \ No newline at end of file