mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-24 23:20:09 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
577348ff32
@ -173,14 +173,16 @@ fun main() {
|
|||||||
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
||||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
| sessionKey | String | false | YourSession | 已经激活的Session |
|
||||||
| target | Long | false | 987654321 | 发送消息目标好友的QQ号 |
|
| target | Long | false | 987654321 | 发送消息目标好友的QQ号 |
|
||||||
|
| quote | Long | true | 135798642 | 引用一条消息的messageId进行回复 |
|
||||||
| messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 |
|
| messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 |
|
||||||
|
|
||||||
#### 响应: 返回统一状态码
|
#### 响应: 返回统一状态码(并携带messageId)
|
||||||
|
|
||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
"code": 0,
|
"code": 0,
|
||||||
"msg": "success"
|
"msg": "success",
|
||||||
|
"messageId": 1234567890 // 一个Long类型属性,标识本条消息,用于撤回和引用回复
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -211,52 +213,16 @@ fun main() {
|
|||||||
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
||||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
| sessionKey | String | false | YourSession | 已经激活的Session |
|
||||||
| target | Long | false | 987654321 | 发送消息目标群的群号 |
|
| target | Long | false | 987654321 | 发送消息目标群的群号 |
|
||||||
|
| quote | Long | true | 135798642 | 引用一条消息的messageId进行回复 |
|
||||||
| messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 |
|
| messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 |
|
||||||
|
|
||||||
#### 响应: 返回统一状态码
|
#### 响应: 返回统一状态码(并携带messageId)
|
||||||
|
|
||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
"code": 0,
|
"code": 0,
|
||||||
"msg": "success"
|
"msg": "success",
|
||||||
}
|
"messageId": 1234567890 // 一个Long类型属性,标识本条消息,用于撤回和引用回复
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 发送引用回复消息(仅支持群消息)
|
|
||||||
|
|
||||||
```
|
|
||||||
[POST] /sendQuoteMessage
|
|
||||||
```
|
|
||||||
|
|
||||||
使用此方法向指定的消息进行引用回复
|
|
||||||
|
|
||||||
#### 请求
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"sessionKey": "YourSession",
|
|
||||||
"target": 987654321,
|
|
||||||
"messageChain": [
|
|
||||||
{ "type": "Plain", "text":"hello\n" },
|
|
||||||
{ "type": "Plain", "text":"world" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
|
||||||
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
|
||||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
|
||||||
| target | Long | false | 987654321 | 引用消息的Message Source的Uid |
|
|
||||||
| messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 |
|
|
||||||
|
|
||||||
#### 响应: 返回统一状态码
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"msg": "success"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -331,6 +297,39 @@ Content-Type:multipart/form-data
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 撤回消息
|
||||||
|
|
||||||
|
```
|
||||||
|
[POST] /recall
|
||||||
|
```
|
||||||
|
|
||||||
|
使用此方法撤回指定消息。对于bot发送的消息,又2分钟时间限制。对于撤回群聊中群员的消息,需要有相应权限
|
||||||
|
|
||||||
|
#### 请求
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
"sessionKey": "YourSession",
|
||||||
|
"target": 987654321
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
||||||
|
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
||||||
|
| sessionKey | String | false | YourSession | 已经激活的Session |
|
||||||
|
| target | Long | false | 987654321 | 需要撤回的消息的messageId |
|
||||||
|
|
||||||
|
#### 响应: 返回统一状态码
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"msg": "success"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 获取Bot收到的消息和事件
|
### 获取Bot收到的消息和事件
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -371,6 +370,9 @@ Content-Type:multipart/form-data
|
|||||||
},{
|
},{
|
||||||
"type": "FriendMessage", // 消息类型:GroupMessage或FriendMessage或各类Event
|
"type": "FriendMessage", // 消息类型:GroupMessage或FriendMessage或各类Event
|
||||||
"messageChain": [{ // 消息链,是一个消息对象构成的数组
|
"messageChain": [{ // 消息链,是一个消息对象构成的数组
|
||||||
|
"type": "Source",
|
||||||
|
"uid": 123456
|
||||||
|
},{
|
||||||
"type": "Plain",
|
"type": "Plain",
|
||||||
"text": "Miral牛逼"
|
"text": "Miral牛逼"
|
||||||
}],
|
}],
|
||||||
|
@ -12,7 +12,7 @@ import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
|||||||
sealed class BotEventDTO : EventDTO()
|
sealed class BotEventDTO : EventDTO()
|
||||||
|
|
||||||
@UseExperimental(MiraiExperimentalAPI::class)
|
@UseExperimental(MiraiExperimentalAPI::class)
|
||||||
fun BotEvent.toDTO() = when(this) {
|
suspend fun BotEvent.toDTO() = when(this) {
|
||||||
is MessagePacket<*, *> -> toDTO()
|
is MessagePacket<*, *> -> toDTO()
|
||||||
else -> when(this) {
|
else -> when(this) {
|
||||||
is BotOnlineEvent -> BotOnlineEventDTO(bot.uin)
|
is BotOnlineEvent -> BotOnlineEventDTO(bot.uin)
|
||||||
|
@ -17,8 +17,9 @@ import net.mamoe.mirai.message.FriendMessage
|
|||||||
import net.mamoe.mirai.message.GroupMessage
|
import net.mamoe.mirai.message.GroupMessage
|
||||||
import net.mamoe.mirai.message.MessagePacket
|
import net.mamoe.mirai.message.MessagePacket
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
import net.mamoe.mirai.message.uploadImage
|
||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DTO data class
|
* DTO data class
|
||||||
@ -57,7 +58,7 @@ data class PlainDTO(val text: String) : MessageDTO()
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("Image")
|
@SerialName("Image")
|
||||||
data class ImageDTO(val imageId: String) : MessageDTO()
|
data class ImageDTO(val imageId: String? = null, val url: String? = null) : MessageDTO()
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("Xml")
|
@SerialName("Xml")
|
||||||
@ -84,42 +85,49 @@ sealed class MessageDTO : DTO
|
|||||||
/*
|
/*
|
||||||
Extend function
|
Extend function
|
||||||
*/
|
*/
|
||||||
fun MessagePacket<*, *>.toDTO() = when (this) {
|
suspend fun MessagePacket<*, *>.toDTO() = when (this) {
|
||||||
is FriendMessage -> FriendMessagePacketDTO(QQDTO(sender))
|
is FriendMessage -> FriendMessagePacketDTO(QQDTO(sender))
|
||||||
is GroupMessage -> GroupMessagePacketDTO(MemberDTO(sender))
|
is GroupMessage -> GroupMessagePacketDTO(MemberDTO(sender))
|
||||||
else -> IgnoreEventDTO
|
else -> IgnoreEventDTO
|
||||||
}.apply {
|
}.apply {
|
||||||
if (this is MessagePacketDTO) { messageChain = message.toDTOChain() }
|
if (this is MessagePacketDTO) {
|
||||||
|
// 将MessagePacket中的所有Message转为DTO对象,并添加到messageChain
|
||||||
|
// foreachContent会忽略MessageSource,一次主动获取
|
||||||
|
messageChain = mutableListOf(messageDTO(message[MessageSource])).apply {
|
||||||
|
message.foreachContent { content -> messageDTO(content).takeUnless { it == UnknownMessageDTO }?.let(::add) }
|
||||||
|
}
|
||||||
// else: `this` is bot event
|
// else: `this` is bot event
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MessageChain.toDTOChain() = mutableListOf(this[MessageSource].toDTO()).apply {
|
|
||||||
foreachContent { content -> content.toDTO().takeUnless { it == UnknownMessageDTO }?.let(::add) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MessageChainDTO.toMessageChain(contact: Contact) =
|
suspend fun MessageChainDTO.toMessageChain(contact: Contact) =
|
||||||
buildMessageChain { this@toMessageChain.forEach { add(it.toMessage(contact)) } }
|
buildMessageChain { this@toMessageChain.forEach { it.toMessage(contact)?.let(::add) } }
|
||||||
|
|
||||||
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiExperimentalAPI::class)
|
|
||||||
fun Message.toDTO() = when (this) {
|
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||||
is MessageSource -> MessageSourceDTO(id)
|
suspend fun MessagePacket<*, *>.messageDTO(message: Message) = when (message) {
|
||||||
is At -> AtDTO(target, display)
|
is MessageSource -> MessageSourceDTO(message.id)
|
||||||
|
is At -> AtDTO(message.target, message.display)
|
||||||
is AtAll -> AtAllDTO(0L)
|
is AtAll -> AtAllDTO(0L)
|
||||||
is Face -> FaceDTO(id)
|
is Face -> FaceDTO(message.id)
|
||||||
is PlainText -> PlainDTO(stringValue)
|
is PlainText -> PlainDTO(message.stringValue)
|
||||||
is Image -> ImageDTO(imageId)
|
is Image -> ImageDTO(message.imageId, message.url())
|
||||||
is XMLMessage -> XmlDTO(stringValue)
|
is XMLMessage -> XmlDTO(message.stringValue)
|
||||||
else -> UnknownMessageDTO
|
else -> UnknownMessageDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class, MiraiExperimentalAPI::class)
|
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||||
fun MessageDTO.toMessage(contact: Contact) = when (this) {
|
suspend fun MessageDTO.toMessage(contact: Contact) = when (this) {
|
||||||
is AtDTO -> At((contact as Group)[target])
|
is AtDTO -> At((contact as Group)[target])
|
||||||
is AtAllDTO -> AtAll
|
is AtAllDTO -> AtAll
|
||||||
is FaceDTO -> Face(faceId)
|
is FaceDTO -> Face(faceId)
|
||||||
is PlainDTO -> PlainText(text)
|
is PlainDTO -> PlainText(text)
|
||||||
is ImageDTO -> Image(imageId)
|
is ImageDTO -> when {
|
||||||
|
!imageId.isNullOrBlank() -> Image(imageId)
|
||||||
|
!url.isNullOrBlank() -> contact.uploadImage(URL(url))
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
is XmlDTO -> XMLMessage(xml)
|
is XmlDTO -> XMLMessage(xml)
|
||||||
is MessageSourceDTO, is UnknownMessageDTO -> PlainText("assert cannot reach")
|
is MessageSourceDTO, is UnknownMessageDTO -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,17 +13,17 @@ import net.mamoe.mirai.api.http.data.common.EventDTO
|
|||||||
import net.mamoe.mirai.api.http.data.common.IgnoreEventDTO
|
import net.mamoe.mirai.api.http.data.common.IgnoreEventDTO
|
||||||
import net.mamoe.mirai.api.http.data.common.toDTO
|
import net.mamoe.mirai.api.http.data.common.toDTO
|
||||||
import net.mamoe.mirai.event.events.BotEvent
|
import net.mamoe.mirai.event.events.BotEvent
|
||||||
import net.mamoe.mirai.message.GroupMessage
|
import net.mamoe.mirai.message.MessagePacket
|
||||||
import net.mamoe.mirai.message.data.MessageSource
|
import net.mamoe.mirai.message.data.MessageSource
|
||||||
import net.mamoe.mirai.utils.firstKey
|
import net.mamoe.mirai.utils.firstKey
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque
|
import java.util.concurrent.ConcurrentLinkedDeque
|
||||||
|
|
||||||
class MessageQueue : ConcurrentLinkedDeque<BotEvent>() {
|
class MessageQueue : ConcurrentLinkedDeque<BotEvent>() {
|
||||||
|
|
||||||
val quoteCacheSize = 4096
|
val cacheSize = 4096
|
||||||
val quoteCache = LinkedHashMap<Long, GroupMessage>()
|
val cache = LinkedHashMap<Long, MessagePacket<*, *>>()
|
||||||
|
|
||||||
fun fetch(size: Int): List<EventDTO> {
|
suspend fun fetch(size: Int): List<EventDTO> {
|
||||||
var count = size
|
var count = size
|
||||||
|
|
||||||
val ret = ArrayList<EventDTO>(count)
|
val ret = ArrayList<EventDTO>(count)
|
||||||
@ -37,18 +37,20 @@ class MessageQueue : ConcurrentLinkedDeque<BotEvent>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 等FriendMessage支持quote
|
if (event is MessagePacket<*, *>) {
|
||||||
if (event is GroupMessage) {
|
|
||||||
addQuoteCache(event)
|
addQuoteCache(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addQuoteCache(msg: GroupMessage) {
|
fun cache(messageId: Long) =
|
||||||
quoteCache[msg.message[MessageSource].id] = msg
|
cache[messageId] ?: throw NoSuchElementException()
|
||||||
if (quoteCache.size > quoteCacheSize) {
|
|
||||||
quoteCache.remove(quoteCache.firstKey())
|
fun addQuoteCache(msg: MessagePacket<*, *>) {
|
||||||
|
cache[msg.message[MessageSource].id] = msg
|
||||||
|
if (cache.size > cacheSize) {
|
||||||
|
cache.remove(cache.firstKey())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,15 +20,19 @@ import io.ktor.response.respondText
|
|||||||
import io.ktor.routing.post
|
import io.ktor.routing.post
|
||||||
import io.ktor.routing.routing
|
import io.ktor.routing.routing
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.Transient
|
|
||||||
import net.mamoe.mirai.api.http.AuthedSession
|
import net.mamoe.mirai.api.http.AuthedSession
|
||||||
import net.mamoe.mirai.api.http.SessionManager
|
import net.mamoe.mirai.api.http.SessionManager
|
||||||
import net.mamoe.mirai.api.http.data.*
|
import net.mamoe.mirai.api.http.data.*
|
||||||
|
import net.mamoe.mirai.api.http.data.common.DTO
|
||||||
import net.mamoe.mirai.api.http.data.common.MessageChainDTO
|
import net.mamoe.mirai.api.http.data.common.MessageChainDTO
|
||||||
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
||||||
import net.mamoe.mirai.api.http.data.common.toMessageChain
|
import net.mamoe.mirai.api.http.data.common.toMessageChain
|
||||||
import net.mamoe.mirai.api.http.util.toJson
|
import net.mamoe.mirai.api.http.util.toJson
|
||||||
import net.mamoe.mirai.message.data.MessageChain
|
import net.mamoe.mirai.contact.Contact
|
||||||
|
import net.mamoe.mirai.message.FriendMessage
|
||||||
|
import net.mamoe.mirai.message.GroupMessage
|
||||||
|
import net.mamoe.mirai.message.MessageReceipt
|
||||||
|
import net.mamoe.mirai.message.data.*
|
||||||
import net.mamoe.mirai.message.uploadImage
|
import net.mamoe.mirai.message.uploadImage
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
@ -42,23 +46,47 @@ fun Application.messageModule() {
|
|||||||
call.respondJson(fetch.toJson())
|
call.respondJson(fetch.toJson())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun <C : Contact> sendMessage(
|
||||||
|
quote: QuoteReplyToSend?,
|
||||||
|
messageChain: MessageChain,
|
||||||
|
target: C
|
||||||
|
): MessageReceipt<out Contact> {
|
||||||
|
val send = if (quote == null) {
|
||||||
|
messageChain
|
||||||
|
} else {
|
||||||
|
(quote + messageChain).toChain()
|
||||||
|
}
|
||||||
|
return target.sendMessage(send)
|
||||||
|
}
|
||||||
|
|
||||||
miraiVerify<SendDTO>("/sendFriendMessage") {
|
miraiVerify<SendDTO>("/sendFriendMessage") {
|
||||||
|
val quote = it.quote?.let { q ->
|
||||||
|
it.session.messageQueue.cache(q).run {
|
||||||
|
this[MessageSource].quote(sender)
|
||||||
|
}}
|
||||||
|
|
||||||
it.session.bot.getFriend(it.target).apply {
|
it.session.bot.getFriend(it.target).apply {
|
||||||
sendMessage(it.messageChain.toMessageChain(this)) // this aka QQ
|
val receipt = sendMessage(quote, it.messageChain.toMessageChain(this), this)
|
||||||
|
receipt.source.ensureSequenceIdAvailable()
|
||||||
|
|
||||||
|
it.session.messageQueue.addQuoteCache(FriendMessage(bot.selfQQ, receipt.source.toChain()))
|
||||||
|
call.respondDTO(SendRetDTO(messageId = receipt.source.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
miraiVerify<SendDTO>("/sendGroupMessage") {
|
miraiVerify<SendDTO>("/sendGroupMessage") {
|
||||||
it.session.bot.getGroup(it.target).apply {
|
val quote = it.quote?.let { q ->
|
||||||
sendMessage(it.messageChain.toMessageChain(this)) // this aka Group
|
it.session.messageQueue.cache(q).run {
|
||||||
}
|
this[MessageSource].quote(sender)
|
||||||
}
|
}}
|
||||||
|
|
||||||
miraiVerify<SendDTO>("/sendQuoteMessage") {
|
it.session.bot.getGroup(it.target).apply {
|
||||||
it.session.messageQueue.quoteCache[it.target]?.apply {
|
val receipt = sendMessage(quote, it.messageChain.toMessageChain(this), this)
|
||||||
quoteReply(it.messageChain.toMessageChain(group))
|
receipt.source.ensureSequenceIdAvailable()
|
||||||
} ?: throw NoSuchElementException()
|
|
||||||
call.respondStateCode(StateCode.Success)
|
it.session.messageQueue.addQuoteCache(GroupMessage("", botPermission, botAsMember, receipt.source.toChain()))
|
||||||
|
call.respondDTO(SendRetDTO(messageId = receipt.source.id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
miraiVerify<SendImageDTO>("sendImageMessage") {
|
miraiVerify<SendImageDTO>("sendImageMessage") {
|
||||||
@ -101,8 +129,10 @@ fun Application.messageModule() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
miraiVerify<RecallDTO>("recall") {
|
miraiVerify<RecallDTO>("recall") {
|
||||||
// TODO
|
it.session.messageQueue.cache(it.target).apply {
|
||||||
call.respond(HttpStatusCode.NotFound, "未完成")
|
it.session.bot.recall(get(MessageSource))
|
||||||
|
}
|
||||||
|
call.respondStateCode(StateCode.Success)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,6 +140,7 @@ fun Application.messageModule() {
|
|||||||
@Serializable
|
@Serializable
|
||||||
private data class SendDTO(
|
private data class SendDTO(
|
||||||
override val sessionKey: String,
|
override val sessionKey: String,
|
||||||
|
val quote: Long? = null,
|
||||||
val target: Long,
|
val target: Long,
|
||||||
val messageChain: MessageChainDTO
|
val messageChain: MessageChainDTO
|
||||||
) : VerifyDTO()
|
) : VerifyDTO()
|
||||||
@ -125,13 +156,13 @@ private data class SendImageDTO(
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
private class SendRetDTO(
|
private class SendRetDTO(
|
||||||
val messageId: Long,
|
val code: Int = 0,
|
||||||
@Transient val stateCode: StateCode = Success
|
val msg: String = "success",
|
||||||
) : StateCode(stateCode.code, stateCode.msg)
|
val messageId: Long
|
||||||
|
) : DTO
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
private data class RecallDTO(
|
private data class RecallDTO(
|
||||||
override val sessionKey: String,
|
override val sessionKey: String,
|
||||||
val target: Long,
|
val target: Long
|
||||||
val sender: Long
|
|
||||||
) : VerifyDTO()
|
) : VerifyDTO()
|
||||||
|
@ -17,8 +17,11 @@ import com.moandjiezana.toml.Toml
|
|||||||
import com.moandjiezana.toml.TomlWriter
|
import com.moandjiezana.toml.TomlWriter
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.UnstableDefault
|
import kotlinx.serialization.UnstableDefault
|
||||||
|
import net.mamoe.mirai.utils.io.encodeToString
|
||||||
import org.yaml.snakeyaml.Yaml
|
import org.yaml.snakeyaml.Yaml
|
||||||
|
import tornadofx.c
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import kotlin.collections.LinkedHashMap
|
import kotlin.collections.LinkedHashMap
|
||||||
@ -69,6 +72,9 @@ interface Config {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a read-write config
|
||||||
|
* */
|
||||||
fun load(file: File): Config {
|
fun load(file: File): Config {
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
file.createNewFile()
|
file.createNewFile()
|
||||||
@ -86,6 +92,32 @@ interface Config {
|
|||||||
else -> error("Unsupported file config type ${file.extension.toLowerCase()}")
|
else -> error("Unsupported file config type ${file.extension.toLowerCase()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a read-only config
|
||||||
|
*/
|
||||||
|
fun load(content: String, type: String): Config {
|
||||||
|
return when (type.toLowerCase()) {
|
||||||
|
"json" -> JsonConfig(content)
|
||||||
|
"yml" -> YamlConfig(content)
|
||||||
|
"yaml" -> YamlConfig(content)
|
||||||
|
"mirai" -> YamlConfig(content)
|
||||||
|
"ini" -> TomlConfig(content)
|
||||||
|
"toml" -> TomlConfig(content)
|
||||||
|
"properties" -> TomlConfig(content)
|
||||||
|
"property" -> TomlConfig(content)
|
||||||
|
"data" -> TomlConfig(content)
|
||||||
|
else -> error("Unsupported file config type $content")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a read-only config
|
||||||
|
*/
|
||||||
|
fun load(inputStream: InputStream, type: String): Config {
|
||||||
|
return load(inputStream.readBytes().encodeToString(), type)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,14 +395,23 @@ interface FileConfig : Config {
|
|||||||
|
|
||||||
|
|
||||||
abstract class FileConfigImpl internal constructor(
|
abstract class FileConfigImpl internal constructor(
|
||||||
private val file: File
|
private val rawContent: String
|
||||||
) : FileConfig,
|
) : FileConfig,
|
||||||
ConfigSection {
|
ConfigSection {
|
||||||
|
|
||||||
private val content by lazy {
|
internal var file: File? = null
|
||||||
deserialize(file.readText())
|
|
||||||
|
|
||||||
|
constructor(file: File) : this(file.readText()) {
|
||||||
|
this.file = file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val content by lazy {
|
||||||
|
deserialize(rawContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override val size: Int get() = content.size
|
override val size: Int get() = content.size
|
||||||
override val entries: MutableSet<MutableMap.MutableEntry<String, Any>> get() = content.entries
|
override val entries: MutableSet<MutableMap.MutableEntry<String, Any>> get() = content.entries
|
||||||
override val keys: MutableSet<String> get() = content.keys
|
override val keys: MutableSet<String> get() = content.keys
|
||||||
@ -384,11 +425,16 @@ abstract class FileConfigImpl internal constructor(
|
|||||||
override fun remove(key: String): Any? = content.remove(key)
|
override fun remove(key: String): Any? = content.remove(key)
|
||||||
|
|
||||||
override fun save() {
|
override fun save() {
|
||||||
if (!file.exists()) {
|
if (isReadOnly()) {
|
||||||
file.createNewFile()
|
error("Config is readonly")
|
||||||
}
|
}
|
||||||
file.writeText(serialize(content))
|
if (!((file?.exists())!!)) {
|
||||||
|
file?.createNewFile()
|
||||||
}
|
}
|
||||||
|
file?.writeText(serialize(content))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isReadOnly() = file == null
|
||||||
|
|
||||||
override fun contains(key: String): Boolean {
|
override fun contains(key: String): Boolean {
|
||||||
return content.contains(key)
|
return content.contains(key)
|
||||||
@ -409,8 +455,12 @@ abstract class FileConfigImpl internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class JsonConfig internal constructor(
|
class JsonConfig internal constructor(
|
||||||
file: File
|
content: String
|
||||||
) : FileConfigImpl(file) {
|
) : FileConfigImpl(content) {
|
||||||
|
constructor(file: File) : this(file.readText()) {
|
||||||
|
this.file = file
|
||||||
|
}
|
||||||
|
|
||||||
@UnstableDefault
|
@UnstableDefault
|
||||||
override fun deserialize(content: String): ConfigSection {
|
override fun deserialize(content: String): ConfigSection {
|
||||||
if (content.isEmpty() || content.isBlank() || content == "{}") {
|
if (content.isEmpty() || content.isBlank() || content == "{}") {
|
||||||
@ -429,7 +479,11 @@ class JsonConfig internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class YamlConfig internal constructor(file: File) : FileConfigImpl(file) {
|
class YamlConfig internal constructor(content: String) : FileConfigImpl(content) {
|
||||||
|
constructor(file: File) : this(file.readText()) {
|
||||||
|
this.file = file
|
||||||
|
}
|
||||||
|
|
||||||
override fun deserialize(content: String): ConfigSection {
|
override fun deserialize(content: String): ConfigSection {
|
||||||
if (content.isEmpty() || content.isBlank()) {
|
if (content.isEmpty() || content.isBlank()) {
|
||||||
return ConfigSectionImpl()
|
return ConfigSectionImpl()
|
||||||
@ -447,7 +501,11 @@ class YamlConfig internal constructor(file: File) : FileConfigImpl(file) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class TomlConfig internal constructor(file: File) : FileConfigImpl(file) {
|
class TomlConfig internal constructor(content: String) : FileConfigImpl(content) {
|
||||||
|
constructor(file: File) : this(file.readText()) {
|
||||||
|
this.file = file
|
||||||
|
}
|
||||||
|
|
||||||
override fun deserialize(content: String): ConfigSection {
|
override fun deserialize(content: String): ConfigSection {
|
||||||
if (content.isEmpty() || content.isBlank()) {
|
if (content.isEmpty() || content.isBlank()) {
|
||||||
return ConfigSectionImpl()
|
return ConfigSectionImpl()
|
||||||
|
@ -17,6 +17,7 @@ import net.mamoe.mirai.utils.MiraiLogger
|
|||||||
import net.mamoe.mirai.utils.SimpleLogger
|
import net.mamoe.mirai.utils.SimpleLogger
|
||||||
import net.mamoe.mirai.utils.io.encodeToString
|
import net.mamoe.mirai.utils.io.encodeToString
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.util.jar.JarFile
|
import java.util.jar.JarFile
|
||||||
@ -91,6 +92,13 @@ abstract class PluginBase(coroutineContext: CoroutineContext) : CoroutineScope {
|
|||||||
val logger: MiraiLogger by lazy {
|
val logger: MiraiLogger by lazy {
|
||||||
DefaultLogger(pluginDescription.name)
|
DefaultLogger(pluginDescription.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getResources(fileName: String): InputStream? {
|
||||||
|
return PluginManager.getFileInJarByName(
|
||||||
|
this.pluginDescription.name,
|
||||||
|
fileName
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PluginDescription(
|
class PluginDescription(
|
||||||
@ -325,6 +333,47 @@ object PluginManager {
|
|||||||
it.disable(throwable)
|
it.disable(throwable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据插件名字找Jar的文件
|
||||||
|
* null => 没找到
|
||||||
|
*/
|
||||||
|
fun getJarPath(pluginName: String): File? {
|
||||||
|
File(pluginsPath).listFiles()?.forEach { file ->
|
||||||
|
if (file != null && file.extension == "jar") {
|
||||||
|
val jar = JarFile(file)
|
||||||
|
val pluginYml =
|
||||||
|
jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull()
|
||||||
|
if (pluginYml != null) {
|
||||||
|
val description =
|
||||||
|
PluginDescription.readFromContent(
|
||||||
|
URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name).openConnection().inputStream.use {
|
||||||
|
it.readBytes().encodeToString()
|
||||||
|
})
|
||||||
|
if (description.name.toLowerCase() == pluginName.toLowerCase()) {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据插件名字找Jar resources中的文件
|
||||||
|
* null => 没找到
|
||||||
|
*/
|
||||||
|
fun getFileInJarByName(pluginName: String, toFind: String): InputStream? {
|
||||||
|
val jarFile = getJarPath(pluginName)
|
||||||
|
if (jarFile == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val jar = JarFile(jarFile)
|
||||||
|
val toFindFile =
|
||||||
|
jar.entries().asSequence().filter { it.name == toFind }.firstOrNull() ?: return null
|
||||||
|
return URL("jar:file:" + jarFile.absoluteFile + "!/" + toFindFile.name).openConnection().inputStream
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ import net.mamoe.mirai.utils.unsafeWeakRef
|
|||||||
* @see QQ.sendMessage 发送群消息, 返回回执(此对象)
|
* @see QQ.sendMessage 发送群消息, 返回回执(此对象)
|
||||||
*/
|
*/
|
||||||
open class MessageReceipt<C : Contact>(
|
open class MessageReceipt<C : Contact>(
|
||||||
private val source: MessageSource,
|
val source: MessageSource,
|
||||||
target: C
|
target: C
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
|
@ -161,7 +161,7 @@ internal class LockFreeLinkedListTest {
|
|||||||
println("Check value")
|
println("Check value")
|
||||||
value shouldBeEqualTo 6
|
value shouldBeEqualTo 6
|
||||||
println("Check size")
|
println("Check size")
|
||||||
println(list.getLinkStructure())
|
// println(list.getLinkStructure())
|
||||||
list.size shouldBeEqualTo 6
|
list.size shouldBeEqualTo 6
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ internal class LockFreeLinkedListTest {
|
|||||||
println("Check value")
|
println("Check value")
|
||||||
value shouldBeEqualTo 2
|
value shouldBeEqualTo 2
|
||||||
println("Check size")
|
println("Check size")
|
||||||
println(list.getLinkStructure())
|
// println(list.getLinkStructure())
|
||||||
list.size shouldBeEqualTo 5
|
list.size shouldBeEqualTo 5
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +198,7 @@ internal class LockFreeLinkedListTest {
|
|||||||
println("Check value")
|
println("Check value")
|
||||||
value shouldBeEqualTo 2
|
value shouldBeEqualTo 2
|
||||||
println("Check size")
|
println("Check size")
|
||||||
println(list.getLinkStructure())
|
// println(list.getLinkStructure())
|
||||||
list.size shouldBeEqualTo 1
|
list.size shouldBeEqualTo 1
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -12,17 +12,17 @@ package net.mamoe.mirai.imageplugin
|
|||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import net.mamoe.mirai.console.plugins.Config
|
import net.mamoe.mirai.console.plugins.Config
|
||||||
import net.mamoe.mirai.console.plugins.ConfigSection
|
import net.mamoe.mirai.console.plugins.ConfigSection
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginBase
|
||||||
|
import net.mamoe.mirai.contact.Contact
|
||||||
import net.mamoe.mirai.event.events.BotOnlineEvent
|
import net.mamoe.mirai.event.events.BotOnlineEvent
|
||||||
import net.mamoe.mirai.event.subscribeAlways
|
import net.mamoe.mirai.event.subscribeAlways
|
||||||
import net.mamoe.mirai.event.subscribeMessages
|
import net.mamoe.mirai.event.subscribeMessages
|
||||||
import net.mamoe.mirai.console.plugins.PluginBase
|
|
||||||
import net.mamoe.mirai.contact.Contact
|
|
||||||
import net.mamoe.mirai.message.data.Image
|
import net.mamoe.mirai.message.data.Image
|
||||||
import net.mamoe.mirai.message.uploadAsImage
|
import net.mamoe.mirai.message.uploadAsImage
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.random.Random
|
import java.net.URL
|
||||||
|
|
||||||
class ImageSenderMain : PluginBase() {
|
class ImageSenderMain : PluginBase() {
|
||||||
|
|
||||||
@ -60,7 +60,6 @@ class ImageSenderMain : PluginBase() {
|
|||||||
reply(e.message ?: "unknown error")
|
reply(e.message ?: "unknown error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,16 +83,21 @@ class ImageSenderMain : PluginBase() {
|
|||||||
|
|
||||||
override fun onLoad() {
|
override fun onLoad() {
|
||||||
logger.info("loading local image data")
|
logger.info("loading local image data")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
images = Config.load(this.javaClass.classLoader.getResource("data.yml")!!.path!!)
|
images = Config.load(getResources(fileName = "data.yml")!!, "yml")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
logger.info("无法加载本地图片")
|
logger.info("无法加载本地图片")
|
||||||
}
|
}
|
||||||
logger.info("本地图片版本" + images.getString("version"))
|
logger.info("本地图片版本" + images.getString("version"))
|
||||||
logger.info("Normal * " + images.getList("normal").size)
|
r18 = images.getConfigSectionList("R18")
|
||||||
logger.info("R18 * " + images.getList("R18").size)
|
normal = images.getConfigSectionList("normal")
|
||||||
|
logger.info("Normal * " + normal.size)
|
||||||
|
logger.info("R18 * " + r18.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onDisable() {
|
override fun onDisable() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user