mirror of
https://github.com/jie65535/mirai-console-jcf-plugin.git
synced 2025-06-02 17:39:15 +08:00
添加订阅处理
- 实现订阅处理 SubscribeHandler - 消息处理中的 HTMLPattern 设为 public - 补充用户回复订阅的处理 - PluginMain 启用订阅
This commit is contained in:
parent
f1e6e2fbf5
commit
09974aad9e
@ -1,7 +1,9 @@
|
|||||||
package top.jie65535.jcf
|
package top.jie65535.jcf
|
||||||
|
|
||||||
|
import io.ktor.http.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import net.mamoe.mirai.event.*
|
import net.mamoe.mirai.event.*
|
||||||
|
import net.mamoe.mirai.event.events.GroupMessageEvent
|
||||||
import net.mamoe.mirai.event.events.MessageEvent
|
import net.mamoe.mirai.event.events.MessageEvent
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
import net.mamoe.mirai.message.data.MessageSource.Key.quote
|
import net.mamoe.mirai.message.data.MessageSource.Key.quote
|
||||||
@ -125,7 +127,7 @@ class MessageHandler(
|
|||||||
subject.sendMessage(
|
subject.sendMessage(
|
||||||
buildForwardMessage {
|
buildForwardMessage {
|
||||||
// logo
|
// logo
|
||||||
mod.logo.thumbnailUrl?.let {
|
mod.logo?.thumbnailUrl?.let {
|
||||||
if (it.isNotBlank()) bot says loadImage(it)
|
if (it.isNotBlank()) bot says loadImage(it)
|
||||||
}
|
}
|
||||||
// basic info
|
// basic info
|
||||||
@ -141,7 +143,7 @@ class MessageHandler(
|
|||||||
主页:${mod.links.websiteUrl}
|
主页:${mod.links.websiteUrl}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
})
|
})
|
||||||
var msg = "$WAIT_REPLY_TIMEOUT_S 秒内回复 $SUBSCRIBE_KEYWORD 订阅模组更新(TODO)"
|
var msg = "$WAIT_REPLY_TIMEOUT_S 秒内回复 $SUBSCRIBE_KEYWORD 订阅模组更新"
|
||||||
if (mod.latestFiles.isNotEmpty()) {
|
if (mod.latestFiles.isNotEmpty()) {
|
||||||
msg += "\n回复编号查看文件详细信息\n" +
|
msg += "\n回复编号查看文件详细信息\n" +
|
||||||
"回复 $VIEW_FILES_KEYWORD 查看全部历史文件"
|
"回复 $VIEW_FILES_KEYWORD 查看全部历史文件"
|
||||||
@ -163,9 +165,14 @@ class MessageHandler(
|
|||||||
eventChannel.nextEvent<MessageEvent>(EventPriority.MONITOR) { it.sender == sender }
|
eventChannel.nextEvent<MessageEvent>(EventPriority.MONITOR) { it.sender == sender }
|
||||||
}
|
}
|
||||||
val nextMessage = next.message.content
|
val nextMessage = next.message.content
|
||||||
|
val subsHandler = PluginMain.subscribeHandler
|
||||||
if (nextMessage.equals(SUBSCRIBE_KEYWORD, true)) {
|
if (nextMessage.equals(SUBSCRIBE_KEYWORD, true)) {
|
||||||
// TODO 实现订阅模组更新功能
|
if (next is GroupMessageEvent) {
|
||||||
subject.sendMessage("订阅更新功能暂未完成,敬请期待")
|
subsHandler.sub(mod.id, next.sender.id, next.group.id)
|
||||||
|
} else {
|
||||||
|
subsHandler.sub(mod.id, next.sender.id)
|
||||||
|
}
|
||||||
|
subject.sendMessage(QuoteReply(next.source) + "已添加订阅")
|
||||||
} else if (mod.latestFiles.isNotEmpty()) {
|
} else if (mod.latestFiles.isNotEmpty()) {
|
||||||
if (nextMessage.equals(VIEW_FILES_KEYWORD, true)) {
|
if (nextMessage.equals(VIEW_FILES_KEYWORD, true)) {
|
||||||
// 查看所有文件
|
// 查看所有文件
|
||||||
@ -248,7 +255,7 @@ class MessageHandler(
|
|||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
|
|
||||||
private val HTMLPattern = Pattern.compile("<[^>]+>", Pattern.CASE_INSENSITIVE)
|
val HTMLPattern: Pattern = Pattern.compile("<[^>]+>", Pattern.CASE_INSENSITIVE)
|
||||||
fun MessageEvent.sendLargeMessage(message: String): Message {
|
fun MessageEvent.sendLargeMessage(message: String): Message {
|
||||||
return buildForwardMessage {
|
return buildForwardMessage {
|
||||||
for (g in message.indices step ONE_GRP_SIZE) {
|
for (g in message.indices step ONE_GRP_SIZE) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package top.jie65535.jcf
|
package top.jie65535.jcf
|
||||||
|
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
|
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
|
||||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||||
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||||
@ -17,8 +18,14 @@ object PluginMain: KotlinPlugin(
|
|||||||
"https://github.com/jie65535/mirai-console-jcf-plugin")
|
"https://github.com/jie65535/mirai-console-jcf-plugin")
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
/**
|
||||||
|
* 订阅处理类
|
||||||
|
*/
|
||||||
|
lateinit var subscribeHandler: SubscribeHandler private set
|
||||||
|
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
logger.info { "Plugin loaded" }
|
logger.info { "Plugin loaded" }
|
||||||
|
PluginData.reload()
|
||||||
PluginConfig.reload()
|
PluginConfig.reload()
|
||||||
PluginCommands.register()
|
PluginCommands.register()
|
||||||
|
|
||||||
@ -31,7 +38,12 @@ object PluginMain: KotlinPlugin(
|
|||||||
val service = MinecraftService(PluginConfig.apiKey)
|
val service = MinecraftService(PluginConfig.apiKey)
|
||||||
val eventChannel = GlobalEventChannel.parentScope(this)
|
val eventChannel = GlobalEventChannel.parentScope(this)
|
||||||
val messageHandler = MessageHandler(service, eventChannel, logger)
|
val messageHandler = MessageHandler(service, eventChannel, logger)
|
||||||
|
subscribeHandler = SubscribeHandler(service, logger)
|
||||||
messageHandler.startListen()
|
messageHandler.startListen()
|
||||||
|
launch {
|
||||||
|
subscribeHandler.load(this)
|
||||||
|
}
|
||||||
|
subscribeHandler.start()// TODO 添加可切换闲置状态的命令
|
||||||
logger.info { "Plugin Enabled" }
|
logger.info { "Plugin Enabled" }
|
||||||
}
|
}
|
||||||
}
|
}
|
309
src/main/kotlin/top/jie65535/jcf/SubscribeHandler.kt
Normal file
309
src/main/kotlin/top/jie65535/jcf/SubscribeHandler.kt
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
package top.jie65535.jcf
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.buffer
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.message.data.At
|
||||||
|
import net.mamoe.mirai.message.data.buildMessageChain
|
||||||
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
import top.jie65535.jcf.model.mod.Mod
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理订阅
|
||||||
|
*
|
||||||
|
* @param service api服务
|
||||||
|
* @param logger 日志
|
||||||
|
*/
|
||||||
|
class SubscribeHandler(
|
||||||
|
private val service: MinecraftService,
|
||||||
|
private val logger: MiraiLogger,
|
||||||
|
) {
|
||||||
|
|
||||||
|
// region -- 参数
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理订阅
|
||||||
|
*
|
||||||
|
* @param mod 模组id;为null将清空所有订阅
|
||||||
|
* @param group 群号;为null将移除指定mod下所有订阅
|
||||||
|
*/
|
||||||
|
fun clean(mod: Int? = null, group: Long? = null) {
|
||||||
|
var updInner = false
|
||||||
|
val modSet = HashMap(PluginData.modsLastFile)
|
||||||
|
val subSet = HashMap(PluginData.subscriptionSet)
|
||||||
|
if (mod == null) {
|
||||||
|
subSet.clear()
|
||||||
|
modSet.clear()
|
||||||
|
logger.info("清理所有订阅")
|
||||||
|
} else if (group == null) {
|
||||||
|
modSet -= mod
|
||||||
|
subSet -= mod
|
||||||
|
logger.info("清理mod[${MOD_INFO_CACHE[mod]?.name}]的订阅")
|
||||||
|
} else {
|
||||||
|
subSet[mod]?.let {
|
||||||
|
logger.info("清理群/分组[$group]的订阅")
|
||||||
|
updInner = true
|
||||||
|
it -= group
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modSet.size != PluginData.modsLastFile.size) {
|
||||||
|
PluginData.modsLastFile = modSet
|
||||||
|
}
|
||||||
|
if (subSet.size != PluginData.subscriptionSet.size || updInner) {
|
||||||
|
PluginData.subscriptionSet = subSet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消订阅
|
||||||
|
*
|
||||||
|
* @param mod 模组id
|
||||||
|
* @param qq 个人q号或群成员q号
|
||||||
|
* @param group 群号;为null时(默认)表示个人订阅
|
||||||
|
*/
|
||||||
|
fun unsub(mod: Int, qq: Long, group: Long? = null) {
|
||||||
|
if (mod < 0 || qq < 0) return
|
||||||
|
|
||||||
|
val gid = group ?: GROUP_ID_SINGLE
|
||||||
|
val subSet = HashMap(PluginData.subscriptionSet)
|
||||||
|
|
||||||
|
val groups = subSet[mod] ?: return
|
||||||
|
val members = groups[gid] ?: return
|
||||||
|
members -= qq
|
||||||
|
PluginData.subscriptionSet = subSet
|
||||||
|
logger.info("取消订阅--{$mod:{$gid:[$qq]}}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录订阅
|
||||||
|
*
|
||||||
|
* @param mod 模组id
|
||||||
|
* @param qq q号
|
||||||
|
* @param group 群号;为null时(默认)表示个人订阅
|
||||||
|
*/
|
||||||
|
fun sub(mod: Int, qq: Long, group: Long? = null) {
|
||||||
|
if (mod < 0 || qq < 0) return
|
||||||
|
val gid = group ?: GROUP_ID_SINGLE
|
||||||
|
val modSet = HashMap(PluginData.modsLastFile)
|
||||||
|
val subSet = HashMap(PluginData.subscriptionSet)
|
||||||
|
if (mod !in modSet) modSet[mod] = -1
|
||||||
|
|
||||||
|
val groupSet = subSet[mod] ?: mutableMapOf()
|
||||||
|
val qqSet = groupSet[gid] ?: mutableListOf()
|
||||||
|
var changed = gid !in groupSet
|
||||||
|
subSet[mod] = groupSet
|
||||||
|
groupSet[gid] = qqSet
|
||||||
|
if (qq !in qqSet) {
|
||||||
|
qqSet += qq
|
||||||
|
changed = true
|
||||||
|
logger.info("添加订阅--{$mod:{$gid:[$qq]}}")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mod !in PluginData.modsLastFile) {
|
||||||
|
PluginData.modsLastFile = modSet
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
PluginData.subscriptionSet = subSet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region -- 流程
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查更新
|
||||||
|
*
|
||||||
|
* @param init 是否初始化
|
||||||
|
* @return 检查到更新的{ mod : newFileId }
|
||||||
|
*/
|
||||||
|
private suspend fun checkUpdate(init: Boolean = false) = flow {
|
||||||
|
val oldSet = PluginData.modsLastFile
|
||||||
|
if (oldSet.isNotEmpty()) {
|
||||||
|
for ((mod, old) in oldSet) {
|
||||||
|
try {
|
||||||
|
val info = service.getMod(mod)
|
||||||
|
logger.info("模组更新【${info.name}】")
|
||||||
|
MOD_INFO_CACHE[mod] = info
|
||||||
|
val last = info.latestFilesIndexes[0].fileId
|
||||||
|
if (old != last || init) {
|
||||||
|
emit(mod to last)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.warning("err msg: ${e.message}")
|
||||||
|
emit(mod to -1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}// for
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取更新日志
|
||||||
|
*
|
||||||
|
* @param mod 模组id
|
||||||
|
* @param file 文件id
|
||||||
|
* @return 更新日志
|
||||||
|
*/
|
||||||
|
private suspend fun getChangeLogs(mod: Int, file: Int): String = try {
|
||||||
|
val changelog = service.getModFileChangelog(mod, file)
|
||||||
|
MessageHandler.HTMLPattern.matcher(changelog)
|
||||||
|
.replaceAll("")
|
||||||
|
.replace(Regex("\n+"), "\n")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.warning("err msg: ${e.message}")
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行发送
|
||||||
|
*
|
||||||
|
* @param sender 发送消息的bot
|
||||||
|
* @param modLogs { mod : changeLog }
|
||||||
|
*/
|
||||||
|
private suspend fun send(sender: Bot, modLogs: Pair<Int, String>) {
|
||||||
|
val (mod, logs) = modLogs
|
||||||
|
if (logs.isBlank()) return
|
||||||
|
|
||||||
|
val subGroups = PluginData.subscriptionSet[mod] ?: return
|
||||||
|
val modName = MOD_INFO_CACHE[mod]?.name ?: return
|
||||||
|
val title = "你订阅的mod【$modName】更新啦!"
|
||||||
|
val context = "更新日志:\n$logs"
|
||||||
|
subGroups.forEach { (group, qqs) ->
|
||||||
|
if (group == GROUP_ID_SINGLE) {
|
||||||
|
qqs.forEach {
|
||||||
|
sender.getFriend(it)?.apply {
|
||||||
|
sendMessage(title)
|
||||||
|
sendMessage(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sender.getGroup(group)?.apply {
|
||||||
|
val titleChain = buildMessageChain {
|
||||||
|
qqs.forEach { +At(it) }
|
||||||
|
+"\n$title"
|
||||||
|
}
|
||||||
|
sendMessage(titleChain)
|
||||||
|
sendMessage(context)
|
||||||
|
}
|
||||||
|
}// if else
|
||||||
|
}// foreach
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 准备发送更新日志
|
||||||
|
*
|
||||||
|
* @param senderQQ 指定发送消息的机器人id
|
||||||
|
* @param updMod { mod : file }
|
||||||
|
*/
|
||||||
|
private suspend fun feedback(senderQQ: Long, updMod: Pair<Int, Int>) {
|
||||||
|
val (mod, file) = updMod
|
||||||
|
if (mod < 0) return
|
||||||
|
|
||||||
|
Bot.instances.firstOrNull {
|
||||||
|
it.isOnline && it.id == senderQQ
|
||||||
|
}?.let { sender ->
|
||||||
|
val log = getChangeLogs(mod, file)
|
||||||
|
send(sender, mod to log)
|
||||||
|
}// let
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 循环执行
|
||||||
|
*/
|
||||||
|
private fun CoroutineScope.loop() = launch {
|
||||||
|
val senderQQ = PluginConfig.subscribeSender
|
||||||
|
val interval = PluginConfig.checkInterval
|
||||||
|
if (senderQQ < 0) {
|
||||||
|
logger.warning("必须配置订阅信息推送bot(qq id)才可以进行订阅推送!")
|
||||||
|
logger.warning("插件会持续收集订阅与检查mod更新,但无法进行消息推送。")
|
||||||
|
}
|
||||||
|
logger.info("subscription listening")
|
||||||
|
while (true) {
|
||||||
|
delay(1000 * interval)
|
||||||
|
if (isIdle) continue
|
||||||
|
|
||||||
|
val subSet = HashMap(PluginData.subscriptionSet)
|
||||||
|
val modFiles = HashMap(PluginData.modsLastFile)
|
||||||
|
checkUpdate()
|
||||||
|
.buffer()
|
||||||
|
.collect {
|
||||||
|
val (mod, file) = it
|
||||||
|
if (file < 0) {
|
||||||
|
modFiles -= mod
|
||||||
|
subSet -= mod
|
||||||
|
} else {
|
||||||
|
modFiles[mod] = file
|
||||||
|
feedback(senderQQ, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PluginData.subscriptionSet = subSet
|
||||||
|
PluginData.modsLastFile = modFiles
|
||||||
|
}// while
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region -- 状态
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否闲置
|
||||||
|
*/
|
||||||
|
var isIdle = true
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消闲置
|
||||||
|
*/
|
||||||
|
fun start() {
|
||||||
|
isIdle = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进入闲置
|
||||||
|
*/
|
||||||
|
fun idle() {
|
||||||
|
isIdle = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化,并开始订阅循环
|
||||||
|
*
|
||||||
|
* @param scope 指定协程上下文
|
||||||
|
*/
|
||||||
|
suspend fun load(scope: CoroutineScope) {
|
||||||
|
logger.info("loading plugin data...")
|
||||||
|
val subs = HashMap(PluginData.subscriptionSet)
|
||||||
|
val files = HashMap(PluginData.modsLastFile)
|
||||||
|
checkUpdate(true)
|
||||||
|
.buffer()
|
||||||
|
.collect { (mod, file) ->
|
||||||
|
if (file < 0) {
|
||||||
|
files -= mod
|
||||||
|
subs -= mod
|
||||||
|
} else {
|
||||||
|
files[mod] = file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PluginData.subscriptionSet = subs
|
||||||
|
PluginData.modsLastFile = files
|
||||||
|
scope.loop()
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识个人订阅
|
||||||
|
*/
|
||||||
|
const val GROUP_ID_SINGLE: Long = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存模组信息
|
||||||
|
*/
|
||||||
|
private val MOD_INFO_CACHE: MutableMap<Int, Mod> = mutableMapOf()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user