mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-05 23:50:08 +08:00
Remove announcements' low-level API and bundle them into AnnouncementProtocol
, improve code style and maintainability
This commit is contained in:
parent
251bf3d9df
commit
4a2b510a70
@ -16,7 +16,10 @@ import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.*
|
||||
import net.mamoe.mirai.message.data.Voice
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
import net.mamoe.mirai.utils.WeakRef
|
||||
import kotlin.annotation.AnnotationTarget.*
|
||||
|
||||
/**
|
||||
@ -129,64 +132,6 @@ public interface LowLevelApiAccessor {
|
||||
ownerId: Long
|
||||
): Sequence<MemberInfo>
|
||||
|
||||
/**
|
||||
* 获取群公告列表
|
||||
* @param page 页码
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
@Deprecated("Will be removed in the future. Use Announcement instead.", level = DeprecationLevel.WARNING)
|
||||
@LowLevelApi
|
||||
@MiraiExperimentalApi
|
||||
public suspend fun getRawGroupAnnouncements(
|
||||
bot: Bot,
|
||||
groupId: Long,
|
||||
page: Int = 1,
|
||||
amount: Int = 10
|
||||
): GroupAnnouncementList
|
||||
|
||||
/**
|
||||
* 发送群公告
|
||||
*
|
||||
* @return 公告的fid
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
@Deprecated("Will be removed in the future. Use Announcement instead.", level = DeprecationLevel.WARNING)
|
||||
@LowLevelApi
|
||||
@MiraiExperimentalApi
|
||||
public suspend fun sendGroupAnnouncement(
|
||||
bot: Bot,
|
||||
groupId: Long,
|
||||
announcement: GroupAnnouncement
|
||||
): String
|
||||
|
||||
/**
|
||||
* 删除群公告
|
||||
* @param fid [GroupAnnouncement.fid]
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
@Deprecated("Will be removed in the future. Use Announcement instead.", level = DeprecationLevel.WARNING)
|
||||
@LowLevelApi
|
||||
@MiraiExperimentalApi
|
||||
public suspend fun deleteGroupAnnouncement(
|
||||
bot: Bot,
|
||||
groupId: Long,
|
||||
fid: String
|
||||
): Boolean
|
||||
|
||||
/**
|
||||
* 获取一条群公告
|
||||
* @param fid [GroupAnnouncement.fid]
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
@Deprecated("Will be removed in the future. Use Announcement instead.", level = DeprecationLevel.WARNING)
|
||||
@LowLevelApi
|
||||
@MiraiExperimentalApi
|
||||
public suspend fun getGroupAnnouncement(
|
||||
bot: Bot,
|
||||
groupId: Long,
|
||||
fid: String
|
||||
): GroupAnnouncement?
|
||||
|
||||
|
||||
/**
|
||||
* 获取群活跃信息
|
||||
|
@ -21,10 +21,12 @@ import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
@SerialName(AnnouncementImage.SERIAL_NAME)
|
||||
@Serializable
|
||||
public class AnnouncementImage @MiraiInternalApi public constructor(
|
||||
@SerialName("h") public val height: String,
|
||||
@SerialName("w") public val width: String,
|
||||
@SerialName("id") public val id: String
|
||||
public val height: String,
|
||||
public val width: String,
|
||||
public val id: String
|
||||
) {
|
||||
// For stability, do not make it `data class`.
|
||||
|
||||
public companion object {
|
||||
public const val SERIAL_NAME: String = "AnnouncementImage"
|
||||
}
|
||||
|
@ -11,8 +11,6 @@ package net.mamoe.mirai.contact.announcement
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
|
||||
/**
|
||||
* 群公告的附加参数.
|
||||
@ -25,12 +23,10 @@ import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
@Serializable
|
||||
public class AnnouncementParameters internal constructor(
|
||||
/**
|
||||
* 群公告的图片,目前仅支持发送图片,不支持获得图片
|
||||
* @suppress 此 API 不稳定, 可能在任意时间改动
|
||||
* 群公告的图片,目前仅支持发送图片,不支持获得图片.
|
||||
* @see AnnouncementImage
|
||||
*/
|
||||
@Transient // do not serialize unstable properties
|
||||
@MiraiExperimentalApi
|
||||
public val image: ByteArray? = null,
|
||||
public val image: AnnouncementImage? = null,
|
||||
|
||||
/**
|
||||
* 是否发送给新成员
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
package net.mamoe.mirai.contact.announcement
|
||||
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@ -52,10 +51,8 @@ public class AnnouncementParametersBuilder @JvmOverloads constructor(
|
||||
) {
|
||||
/**
|
||||
* @see AnnouncementParameters.image
|
||||
* @suppress 此 API 不稳定, 可能在任意时间改动
|
||||
*/
|
||||
@MiraiExperimentalApi
|
||||
public var image: ByteArray? = prototype.image
|
||||
public var image: AnnouncementImage? = prototype.image
|
||||
|
||||
/**
|
||||
* @see AnnouncementParameters.sendToNewMember
|
||||
@ -83,10 +80,9 @@ public class AnnouncementParametersBuilder @JvmOverloads constructor(
|
||||
public var needConfirm: Boolean = prototype.needConfirm
|
||||
|
||||
/**
|
||||
* @suppress 此 API 不稳定, 可能在任意时间改动
|
||||
* @see AnnouncementParameters.image
|
||||
*/
|
||||
@MiraiExperimentalApi
|
||||
public fun image(image: ByteArray?): AnnouncementParametersBuilder {
|
||||
public fun image(image: AnnouncementImage): AnnouncementParametersBuilder {
|
||||
this.image = image
|
||||
return this
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.toList
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.contact.PermissionDeniedException
|
||||
import net.mamoe.mirai.utils.ExternalResource
|
||||
import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
import java.util.stream.Stream
|
||||
|
||||
|
||||
@ -25,6 +26,7 @@ import java.util.stream.Stream
|
||||
*
|
||||
* @since 2.7
|
||||
*/
|
||||
@NotStableForInheritance
|
||||
public interface Announcements {
|
||||
/**
|
||||
* 创建一个能获取该群内所有群公告列表的 [Flow]. 在 [Flow] 被使用时才会分页下载 [OnlineAnnouncement].
|
||||
|
@ -17,6 +17,7 @@ import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.NormalMember
|
||||
import net.mamoe.mirai.contact.PermissionDeniedException
|
||||
import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
import java.time.Instant
|
||||
|
||||
|
||||
@ -27,6 +28,7 @@ import java.time.Instant
|
||||
*
|
||||
* @since 2.7
|
||||
*/
|
||||
@NotStableForInheritance
|
||||
public interface OnlineAnnouncement : Announcement {
|
||||
/**
|
||||
* 公告所属群
|
||||
|
@ -18,7 +18,10 @@ import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.long
|
||||
import net.mamoe.mirai.*
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.*
|
||||
@ -557,139 +560,11 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
||||
check(response is PbMessageSvc.PbMsgWithDraw.Response.Success) { "Failed to recall message #${source.ids.contentToString()}: $response" }
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION", "OverridingDeprecatedMember")
|
||||
@LowLevelApi
|
||||
@MiraiExperimentalApi
|
||||
override suspend fun getRawGroupAnnouncements(
|
||||
bot: Bot,
|
||||
groupId: Long,
|
||||
page: Int,
|
||||
amount: Int
|
||||
): GroupAnnouncementList = bot.asQQAndroidBot().run {
|
||||
val rep = bot.asQQAndroidBot().network.run {
|
||||
Mirai.Http.post<String> {
|
||||
url("https://web.qun.qq.com/cgi-bin/announce/list_announce")
|
||||
body = MultiPartFormDataContent(formData {
|
||||
append("qid", groupId)
|
||||
append("bkn", bot.bkn)
|
||||
append("ft", 23) //好像是一个用来识别应用的参数
|
||||
append("s", if (page == 1) 0 else -(page * amount + 1)) // 第一页这里的参数应该是-1
|
||||
append("n", amount)
|
||||
append("ni", if (page == 1) 1 else 0)
|
||||
append("format", "json")
|
||||
})
|
||||
headers {
|
||||
append(
|
||||
"cookie",
|
||||
"uin=o${id}; skey=${client.wLoginSigInfo.sKey.data.encodeToString()};"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// bot.network.logger.error(rep)
|
||||
return json.decodeFromString(GroupAnnouncementList.serializer(), rep)
|
||||
}
|
||||
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
isLenient = true
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION", "OverridingDeprecatedMember")
|
||||
@LowLevelApi
|
||||
@MiraiExperimentalApi
|
||||
override suspend fun sendGroupAnnouncement(bot: Bot, groupId: Long, announcement: GroupAnnouncement): String =
|
||||
bot.asQQAndroidBot().run {
|
||||
val rep = Mirai.Http.post<String> {
|
||||
url("https://web.qun.qq.com/cgi-bin/announce/add_qun_notice")
|
||||
body = MultiPartFormDataContent(formData {
|
||||
append("qid", groupId)
|
||||
append("bkn", bkn)
|
||||
append("text", announcement.msg.text)
|
||||
append("pinned", announcement.pinned)
|
||||
append(
|
||||
"settings",
|
||||
json.encodeToString(
|
||||
GroupAnnouncementSettings.serializer(),
|
||||
announcement.settings ?: GroupAnnouncementSettings()
|
||||
)
|
||||
)
|
||||
append("format", "json")
|
||||
})
|
||||
headers {
|
||||
append(
|
||||
"cookie",
|
||||
"uin=o${id};" +
|
||||
" skey=${client.wLoginSigInfo.sKey.data.encodeToString()};" +
|
||||
" p_uin=o${id};" +
|
||||
" p_skey=${client.wLoginSigInfo.psKeyMap["qun.qq.com"]?.data?.encodeToString()}; "
|
||||
)
|
||||
}
|
||||
}
|
||||
val jsonObj = json.parseToJsonElement(rep)
|
||||
return jsonObj.jsonObject["new_fid"]?.jsonPrimitive?.content
|
||||
?: throw throw IllegalStateException("Send Announcement fail group:$groupId msg:${jsonObj.jsonObject["em"]} content:${announcement.msg.text}")
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION", "OverridingDeprecatedMember")
|
||||
@LowLevelApi
|
||||
@MiraiExperimentalApi
|
||||
override suspend fun deleteGroupAnnouncement(bot: Bot, groupId: Long, fid: String): Boolean =
|
||||
bot.asQQAndroidBot().run {
|
||||
val data = Mirai.Http.post<String> {
|
||||
url("https://web.qun.qq.com/cgi-bin/announce/del_feed")
|
||||
body = MultiPartFormDataContent(formData {
|
||||
append("qid", groupId)
|
||||
append("bkn", bkn)
|
||||
append("fid", fid)
|
||||
append("format", "json")
|
||||
})
|
||||
headers {
|
||||
append(
|
||||
"cookie",
|
||||
"uin=o${id};" +
|
||||
" skey=${client.wLoginSigInfo.sKey.data.encodeToString()};" +
|
||||
" p_uin=o${id};" +
|
||||
" p_skey=${client.wLoginSigInfo.psKeyMap["qun.qq.com"]?.data?.encodeToString()}; "
|
||||
)
|
||||
}
|
||||
}
|
||||
val jsonObj = json.parseToJsonElement(data)
|
||||
if (jsonObj.jsonObject["ec"]?.jsonPrimitive?.int ?: 1 != 0) {
|
||||
throw throw IllegalStateException("delete Announcement fail group:$groupId msg:${jsonObj.jsonObject["em"]} fid:$fid")
|
||||
}
|
||||
|
||||
return jsonObj.jsonObject["ec"]?.jsonPrimitive?.int == 0
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION", "OverridingDeprecatedMember")
|
||||
@LowLevelApi
|
||||
@MiraiExperimentalApi
|
||||
override suspend fun getGroupAnnouncement(bot: Bot, groupId: Long, fid: String): GroupAnnouncement? =
|
||||
bot.asQQAndroidBot().run {
|
||||
val rep = network.run {
|
||||
Mirai.Http.post<String> {
|
||||
url("https://web.qun.qq.com/cgi-bin/announce/get_feed")
|
||||
body = MultiPartFormDataContent(formData {
|
||||
append("qid", groupId)
|
||||
append("bkn", bkn)
|
||||
append("fid", fid)
|
||||
append("format", "json")
|
||||
})
|
||||
headers {
|
||||
append(
|
||||
"cookie",
|
||||
"uin=o${id}; skey=${client.wLoginSigInfo.sKey.data.encodeToString()}; p_uin=o${id};"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bot.network.logger.error(rep)
|
||||
return json.decodeFromString(GroupAnnouncement.serializer(), rep)
|
||||
|
||||
}
|
||||
|
||||
@LowLevelApi
|
||||
@MiraiExperimentalApi
|
||||
override suspend fun getRawGroupActiveData(bot: Bot, groupId: Long, page: Int): GroupActiveData =
|
||||
|
@ -1,257 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package net.mamoe.mirai.internal.contact
|
||||
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.request.forms.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.contact.announcement.*
|
||||
import net.mamoe.mirai.contact.checkBotPermission
|
||||
import net.mamoe.mirai.data.GroupAnnouncement
|
||||
import net.mamoe.mirai.data.GroupAnnouncementList
|
||||
import net.mamoe.mirai.data.GroupAnnouncementMsg
|
||||
import net.mamoe.mirai.data.GroupAnnouncementSettings
|
||||
import net.mamoe.mirai.internal.asQQAndroidBot
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
|
||||
import java.util.stream.Stream
|
||||
import kotlin.io.use
|
||||
|
||||
internal class AnnouncementsImpl(
|
||||
private val group: GroupImpl,
|
||||
) : Announcements {
|
||||
inline val bot get() = group.bot
|
||||
|
||||
override suspend fun asFlow(): Flow<OnlineAnnouncement> {
|
||||
return flow {
|
||||
var i = 1
|
||||
while (true) {
|
||||
val result = Mirai.getRawGroupAnnouncements(bot, group.id, i++)
|
||||
checkResult(result, i)
|
||||
|
||||
if (result.inst.isNullOrEmpty() && result.feeds.isNullOrEmpty()) break
|
||||
|
||||
result.inst?.let { emitAll(it.asFlow()) }
|
||||
result.feeds?.let { emitAll(it.asFlow()) }
|
||||
}
|
||||
}.map { it.toAnnouncement(group) }
|
||||
}
|
||||
|
||||
override suspend fun asStream(): Stream<OnlineAnnouncement> {
|
||||
return stream {
|
||||
var i = 1
|
||||
while (true) {
|
||||
val result = runBlocking { Mirai.getRawGroupAnnouncements(bot, group.id, i++) }
|
||||
checkResult(result, i)
|
||||
|
||||
if (result.inst.isNullOrEmpty() && result.feeds.isNullOrEmpty()) break
|
||||
|
||||
result.inst?.let { yieldAll(it) }
|
||||
result.feeds?.let { yieldAll(it) }
|
||||
}
|
||||
}.map { it.toAnnouncement(group) }
|
||||
}
|
||||
|
||||
private fun checkResult(result: GroupAnnouncementList, i: Int) {
|
||||
if (result.ec != 0) {
|
||||
bot.logger.warning { "Failed to get announcements for group ${group.id}, at page $i. result=$result" }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun delete(fid: String): Boolean {
|
||||
group.checkBotPermission(MemberPermission.ADMINISTRATOR) { "Only administrator have permission to delete group announcement" }
|
||||
return Mirai.deleteGroupAnnouncement(bot, group.id, fid)
|
||||
}
|
||||
|
||||
override suspend fun get(fid: String): OnlineAnnouncement? {
|
||||
return Mirai.getGroupAnnouncement(bot, group.id, fid)?.toAnnouncement(group)
|
||||
}
|
||||
|
||||
override suspend fun publish(announcement: Announcement): OnlineAnnouncement = announcement.run {
|
||||
val bot = group.bot
|
||||
group.checkBotPermission(MemberPermission.ADMINISTRATOR) { "Only administrator have permission to send group announcement" }
|
||||
val image = parameters.image
|
||||
val fid = image?.toExternalResource()?.use {
|
||||
val imageUp = AnnouncementProtocol.uploadGroupAnnouncementImage(bot, group.id, it)
|
||||
AnnouncementProtocol.sendGroupAnnouncementWithImage(bot, group.id, imageUp, toGroupAnnouncement(bot.id))
|
||||
} ?: Mirai.sendGroupAnnouncement(bot, group.id, toGroupAnnouncement(bot.id))
|
||||
|
||||
return OnlineAnnouncementImpl(
|
||||
group = group,
|
||||
senderId = bot.id,
|
||||
sender = group.botAsMember,
|
||||
title = title,
|
||||
body = body,
|
||||
parameters = parameters,
|
||||
fid = fid,
|
||||
isAllRead = false,
|
||||
readMemberNumber = 0,
|
||||
publishTime = currentTimeSeconds()
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun uploadImage(resource: ExternalResource): AnnouncementImage {
|
||||
return AnnouncementProtocol.uploadGroupAnnouncementImage(bot, group.id, resource)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
internal object AnnouncementProtocol {
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
isLenient = true
|
||||
}
|
||||
|
||||
suspend fun uploadGroupAnnouncementImage(
|
||||
bot: Bot,
|
||||
groupId: Long,
|
||||
resource: ExternalResource
|
||||
): AnnouncementImage = bot.asQQAndroidBot().run {
|
||||
//https://youtrack.jetbrains.com/issue/KTOR-455
|
||||
val rep = Mirai.Http.post<String> {
|
||||
url("https://web.qun.qq.com/cgi-bin/announce/upload_img")
|
||||
body = MultiPartFormDataContent(formData {
|
||||
append("\"bkn\"", bkn)
|
||||
append("\"source\"", "troopNotice")
|
||||
append("m", "0")
|
||||
append(
|
||||
"\"pic_up\"",
|
||||
headers = Headers.build {
|
||||
append(HttpHeaders.ContentType, ContentType.Image.PNG)
|
||||
append(HttpHeaders.ContentDisposition, "filename=\"temp_uploadFile.png\"")
|
||||
}
|
||||
) {
|
||||
writeFully(resource.inputStream().withUse { readBytes() })
|
||||
}
|
||||
})
|
||||
headers {
|
||||
append(
|
||||
"cookie",
|
||||
" p_uin=o${id};" +
|
||||
" p_skey=${client.wLoginSigInfo.psKeyMap["qun.qq.com"]?.data?.encodeToString() ?: error("cookie parse p_skey error")}; "
|
||||
)
|
||||
}
|
||||
}
|
||||
val jsonObj = json.parseToJsonElement(rep)
|
||||
if (jsonObj.jsonObject["ec"]?.jsonPrimitive?.int != 0) {
|
||||
throw IllegalStateException("Upload group announcement image fail group:$groupId msg:${jsonObj.jsonObject["em"]}")
|
||||
}
|
||||
val id = jsonObj.jsonObject["id"]?.jsonPrimitive?.content
|
||||
?: throw IllegalStateException("Upload group announcement image fail group:$groupId msg:${jsonObj.jsonObject["em"]}")
|
||||
return json.decodeFromString(AnnouncementImage.serializer(), id)
|
||||
}
|
||||
|
||||
suspend fun sendGroupAnnouncementWithImage(
|
||||
bot: Bot,
|
||||
groupId: Long,
|
||||
image: AnnouncementImage,
|
||||
announcement: GroupAnnouncement
|
||||
): String = bot.asQQAndroidBot().run {
|
||||
val rep = withContext(network.coroutineContext) {
|
||||
Mirai.Http.post<String> {
|
||||
url("https://web.qun.qq.com/cgi-bin/announce/add_qun_notice")
|
||||
body = MultiPartFormDataContent(formData {
|
||||
append("qid", groupId)
|
||||
append("bkn", bkn)
|
||||
append("text", announcement.msg.text)
|
||||
append("pinned", announcement.pinned)
|
||||
append("pic", image.id)
|
||||
append("imgWidth", image.width)
|
||||
append("imgHeight", image.height)
|
||||
append(
|
||||
"settings",
|
||||
json.encodeToString(
|
||||
GroupAnnouncementSettings.serializer(),
|
||||
announcement.settings ?: GroupAnnouncementSettings()
|
||||
)
|
||||
)
|
||||
append("format", "json")
|
||||
})
|
||||
headers {
|
||||
append(
|
||||
"cookie",
|
||||
" p_uin=o${id};" +
|
||||
" p_skey=${
|
||||
client.wLoginSigInfo.psKeyMap["qun.qq.com"]?.data?.encodeToString() ?: error(
|
||||
"parse error"
|
||||
)
|
||||
}; "
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
val jsonObj = json.parseToJsonElement(rep)
|
||||
return jsonObj.jsonObject["new_fid"]?.jsonPrimitive?.content
|
||||
?: throw throw IllegalStateException("Send Announcement with image fail group:$groupId msg:${jsonObj.jsonObject["em"]} content:${announcement.msg.text}")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
internal fun Announcement.toGroupAnnouncement(senderId: Long): GroupAnnouncement {
|
||||
return GroupAnnouncement(
|
||||
sender = senderId,
|
||||
msg = GroupAnnouncementMsg(
|
||||
title = title,
|
||||
text = body
|
||||
),
|
||||
type = if (parameters.sendToNewMember) 20 else 6,
|
||||
settings = GroupAnnouncementSettings(
|
||||
isShowEditCard = if (parameters.isShowEditCard) 1 else 0,
|
||||
tipWindowType = if (parameters.isTip) 0 else 1,
|
||||
confirmRequired = if (parameters.needConfirm) 1 else 0,
|
||||
),
|
||||
pinned = if (parameters.isPinned) 1 else 0,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun GroupAnnouncement.toAnnouncement(group: Group): OnlineAnnouncementImpl {
|
||||
val fid = this.fid
|
||||
val settings = this.settings
|
||||
|
||||
check(fid != null) { "GroupAnnouncement don't have id" }
|
||||
check(settings != null) { "GroupAnnouncement don't have setting" }
|
||||
|
||||
return OnlineAnnouncementImpl(
|
||||
group = group,
|
||||
senderId = sender,
|
||||
sender = group[sender],
|
||||
title = msg.title ?: "",
|
||||
body = msg.text,
|
||||
parameters = buildAnnouncementParameters {
|
||||
isPinned = pinned == 1
|
||||
sendToNewMember = type == 20
|
||||
isTip = settings.tipWindowType == 0
|
||||
needConfirm = settings.confirmRequired == 1
|
||||
isShowEditCard = settings.isShowEditCard == 1
|
||||
},
|
||||
fid = fid,
|
||||
isAllRead = isAllConfirm != 0,
|
||||
readMemberNumber = readNum,
|
||||
publishTime = time
|
||||
)
|
||||
}
|
@ -21,6 +21,7 @@ import net.mamoe.mirai.data.MemberInfo
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.contact.announcement.AnnouncementsImpl
|
||||
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
|
||||
import net.mamoe.mirai.internal.message.OfflineGroupImage
|
||||
import net.mamoe.mirai.internal.network.context.BdhSession
|
||||
|
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package net.mamoe.mirai.internal.contact.announcement
|
||||
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.request.forms.*
|
||||
import io.ktor.http.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.contact.announcement.*
|
||||
import net.mamoe.mirai.contact.checkBotPermission
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.asQQAndroidBot
|
||||
import net.mamoe.mirai.internal.contact.GroupImpl
|
||||
import net.mamoe.mirai.internal.contact.OnlineAnnouncementImpl
|
||||
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.deleteGroupAnnouncement
|
||||
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.getGroupAnnouncement
|
||||
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.toAnnouncement
|
||||
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.toGroupAnnouncement
|
||||
import net.mamoe.mirai.internal.network.psKey
|
||||
import net.mamoe.mirai.internal.network.sKey
|
||||
import net.mamoe.mirai.internal.utils.io.writeResource
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.Either.Companion.rightOrNull
|
||||
import java.util.stream.Stream
|
||||
|
||||
internal class AnnouncementsImpl(
|
||||
private val group: GroupImpl,
|
||||
) : Announcements {
|
||||
inline val bot get() = group.bot
|
||||
|
||||
override suspend fun asFlow(): Flow<OnlineAnnouncement> {
|
||||
return flow {
|
||||
var i = 1
|
||||
while (true) {
|
||||
val result = AnnouncementProtocol.getRawGroupAnnouncements(bot, group.id, i++).rightOrNull ?: break
|
||||
|
||||
if (result.inst.isNullOrEmpty() && result.feeds.isNullOrEmpty()) break
|
||||
|
||||
result.inst?.let { emitAll(it.asFlow()) }
|
||||
result.feeds?.let { emitAll(it.asFlow()) }
|
||||
}
|
||||
}.map { it.toAnnouncement(group) }
|
||||
}
|
||||
|
||||
override suspend fun asStream(): Stream<OnlineAnnouncement> {
|
||||
return stream {
|
||||
var i = 1
|
||||
while (true) {
|
||||
val result = runBlocking {
|
||||
AnnouncementProtocol.getRawGroupAnnouncements(bot, group.id, i++)
|
||||
}.rightOrNull ?: break
|
||||
|
||||
if (result.inst.isNullOrEmpty() && result.feeds.isNullOrEmpty()) break
|
||||
|
||||
result.inst?.let { yieldAll(it) }
|
||||
result.feeds?.let { yieldAll(it) }
|
||||
}
|
||||
}.map { it.toAnnouncement(group) }
|
||||
}
|
||||
|
||||
override suspend fun delete(fid: String): Boolean {
|
||||
group.checkBotPermission(MemberPermission.ADMINISTRATOR) { "Only administrator have permission to delete group announcement" }
|
||||
return bot.deleteGroupAnnouncement(group.id, fid)
|
||||
}
|
||||
|
||||
override suspend fun get(fid: String): OnlineAnnouncement {
|
||||
return bot.getGroupAnnouncement(group.id, fid).toAnnouncement(group)
|
||||
}
|
||||
|
||||
override suspend fun publish(announcement: Announcement): OnlineAnnouncement = announcement.run {
|
||||
val bot = group.bot
|
||||
group.checkBotPermission(MemberPermission.ADMINISTRATOR) { "Only administrator have permission to send group announcement" }
|
||||
val image = parameters.image
|
||||
val fid = AnnouncementProtocol.sendGroupAnnouncement(bot, group.id, toGroupAnnouncement(bot.id), image)
|
||||
|
||||
return OnlineAnnouncementImpl(
|
||||
group = group,
|
||||
senderId = bot.id,
|
||||
sender = group.botAsMember,
|
||||
title = title,
|
||||
body = body,
|
||||
parameters = parameters,
|
||||
fid = fid,
|
||||
isAllRead = false,
|
||||
readMemberNumber = 0,
|
||||
publishTime = currentTimeSeconds()
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun uploadImage(resource: ExternalResource): AnnouncementImage {
|
||||
return AnnouncementProtocol.uploadGroupAnnouncementImage(bot, resource)
|
||||
}
|
||||
}
|
||||
|
||||
internal object AnnouncementProtocol {
|
||||
@Serializable
|
||||
data class UploadImageResp(
|
||||
@SerialName("ec") override val errorCode: Int = 0,
|
||||
@SerialName("em") override val errorMessage: String? = null,
|
||||
@SerialName("id") val id: String,
|
||||
) : CheckableResponseA(), JsonStruct
|
||||
|
||||
suspend fun uploadGroupAnnouncementImage(
|
||||
bot: Bot,
|
||||
resource: ExternalResource
|
||||
): AnnouncementImage = bot.asQQAndroidBot().run {
|
||||
val resp = Mirai.Http.post<String> {
|
||||
url("https://web.qun.qq.com/cgi-bin/announce/upload_img")
|
||||
body = MultiPartFormDataContent(formData {
|
||||
append("\"bkn\"", bkn)
|
||||
append("\"source\"", "troopNotice")
|
||||
append("m", "0")
|
||||
append(
|
||||
"\"pic_up\"",
|
||||
headers = Headers.build {
|
||||
append(HttpHeaders.ContentType, ContentType.Image.PNG)
|
||||
append(HttpHeaders.ContentDisposition, "filename=\"temp_uploadFile.png\"")
|
||||
}
|
||||
) {
|
||||
writeResource(resource)
|
||||
}
|
||||
})
|
||||
cookie("p_uin", "o${bot.id}")
|
||||
cookie("p_skey", psKey("qun.qq.com"))
|
||||
}.loadSafelyAs(UploadImageResp.serializer()).checked()
|
||||
return resp.id.loadSafelyAs(GroupAnnouncementImage.serializer()).checked().toPublic()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class SendGroupAnnouncementResp(
|
||||
@SerialName("ec") override val errorCode: Int = 0,
|
||||
@SerialName("em") override val errorMessage: String? = null,
|
||||
@SerialName("new_fid") val fid: String,
|
||||
) : CheckableResponseA(), JsonStruct
|
||||
|
||||
suspend fun sendGroupAnnouncement(
|
||||
bot: Bot,
|
||||
groupId: Long,
|
||||
announcement: GroupAnnouncement,
|
||||
image: AnnouncementImage?,
|
||||
): String = bot.asQQAndroidBot().run {
|
||||
return Mirai.Http.post<String> {
|
||||
url("https://web.qun.qq.com/cgi-bin/announce/add_qun_notice")
|
||||
body = MultiPartFormDataContent(formData {
|
||||
append("qid", groupId)
|
||||
this.append("bkn", bkn)
|
||||
append("text", announcement.msg.text)
|
||||
append("pinned", announcement.pinned)
|
||||
image?.let {
|
||||
append("pic", image.id)
|
||||
append("imgWidth", image.width)
|
||||
append("imgHeight", image.height)
|
||||
}
|
||||
append(
|
||||
"settings",
|
||||
announcement.settings.toJsonString(GroupAnnouncementSettings.serializer()),
|
||||
)
|
||||
append("format", "json")
|
||||
})
|
||||
cookie("uin", "o${bot.id}")
|
||||
cookie("p_uin", "o${bot.id}")
|
||||
cookie("skey", sKey)
|
||||
cookie("p_skey", psKey("qun.qq.com"))
|
||||
}.loadSafelyAs(SendGroupAnnouncementResp.serializer()).checked().fid
|
||||
}
|
||||
|
||||
suspend fun getRawGroupAnnouncements(
|
||||
bot: Bot,
|
||||
groupId: Long,
|
||||
page: Int,
|
||||
amount: Int = 10
|
||||
): Either<DeserializationFailure, GroupAnnouncementList> = bot.asQQAndroidBot().run {
|
||||
return bot.asQQAndroidBot().network.run {
|
||||
Mirai.Http.post<String> {
|
||||
url("https://web.qun.qq.com/cgi-bin/announce/list_announce")
|
||||
body = MultiPartFormDataContent(formData {
|
||||
append("qid", groupId)
|
||||
append("bkn", bot.bkn)
|
||||
append("ft", 23) //好像是一个用来识别应用的参数
|
||||
append("s", if (page == 1) 0 else -(page * amount + 1)) // 第一页这里的参数应该是-1
|
||||
append("n", amount)
|
||||
append("ni", if (page == 1) 1 else 0)
|
||||
append("format", "json")
|
||||
})
|
||||
cookie("uin", "o${bot.id}")
|
||||
cookie("skey", sKey)
|
||||
}
|
||||
}.loadSafelyAs(GroupAnnouncementList.serializer())
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class DeleteResp(
|
||||
@SerialName("ec") override val errorCode: Int = 0,
|
||||
@SerialName("em") override val errorMessage: String? = null,
|
||||
) : CheckableResponseA(), JsonStruct
|
||||
|
||||
suspend fun QQAndroidBot.deleteGroupAnnouncement(groupId: Long, fid: String): Boolean {
|
||||
Mirai.Http.post<String> {
|
||||
url("https://web.qun.qq.com/cgi-bin/announce/del_feed")
|
||||
body = feedBody(groupId, fid)
|
||||
cookie("uin", "o$id")
|
||||
cookie("p_uin", "o$id")
|
||||
cookie("skey", sKey)
|
||||
cookie("p_skey", psKey("qun.qq.com"))
|
||||
}.loadSafelyAs(DeleteResp.serializer()).checked()
|
||||
return true
|
||||
}
|
||||
|
||||
suspend fun QQAndroidBot.getGroupAnnouncement(groupId: Long, fid: String): GroupAnnouncement {
|
||||
return Mirai.Http.post<String> {
|
||||
url("https://web.qun.qq.com/cgi-bin/announce/get_feed")
|
||||
body = feedBody(groupId, fid)
|
||||
cookie("uin", "o$id")
|
||||
cookie("p_uin", "o$id")
|
||||
cookie("skey", sKey)
|
||||
}.loadAs(GroupAnnouncement.serializer())
|
||||
}
|
||||
|
||||
private fun QQAndroidBot.feedBody(
|
||||
groupId: Long,
|
||||
fid: String
|
||||
) = MultiPartFormDataContent(formData {
|
||||
append("qid", groupId)
|
||||
append("bkn", bkn)
|
||||
append("fid", fid)
|
||||
append("format", "json")
|
||||
})
|
||||
|
||||
fun Announcement.toGroupAnnouncement(senderId: Long): GroupAnnouncement {
|
||||
return GroupAnnouncement(
|
||||
sender = senderId,
|
||||
msg = GroupAnnouncementMsg(
|
||||
title = title,
|
||||
text = body
|
||||
),
|
||||
type = if (parameters.sendToNewMember) 20 else 6,
|
||||
settings = GroupAnnouncementSettings(
|
||||
isShowEditCard = if (parameters.isShowEditCard) 1 else 0,
|
||||
tipWindowType = if (parameters.isTip) 0 else 1,
|
||||
confirmRequired = if (parameters.needConfirm) 1 else 0,
|
||||
),
|
||||
pinned = if (parameters.isPinned) 1 else 0,
|
||||
)
|
||||
}
|
||||
|
||||
fun GroupAnnouncement.toAnnouncement(group: Group): OnlineAnnouncementImpl {
|
||||
val fid = this.fid ?: ""
|
||||
|
||||
return OnlineAnnouncementImpl(
|
||||
group = group,
|
||||
senderId = sender,
|
||||
sender = group[sender],
|
||||
title = msg.title ?: "",
|
||||
body = msg.text,
|
||||
parameters = buildAnnouncementParameters {
|
||||
isPinned = pinned == 1
|
||||
sendToNewMember = type == 20
|
||||
isTip = settings.tipWindowType == 0
|
||||
needConfirm = settings.confirmRequired == 1
|
||||
isShowEditCard = settings.isShowEditCard == 1
|
||||
},
|
||||
fid = fid,
|
||||
isAllRead = isAllConfirm != 0,
|
||||
readMemberNumber = readNum,
|
||||
publishTime = time
|
||||
)
|
||||
}
|
||||
}
|
@ -9,27 +9,22 @@
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package net.mamoe.mirai.data
|
||||
package net.mamoe.mirai.internal.contact.announcement
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.contact.announcement.Announcement
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.contact.announcement.AnnouncementImage
|
||||
import net.mamoe.mirai.utils.CheckableResponseA
|
||||
import net.mamoe.mirai.utils.JsonStruct
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
|
||||
/**
|
||||
* 群公告的协议数据结构. 仅同于内部操作, 用户请使用 [Announcement].
|
||||
*
|
||||
* @suppress 此 API 非常不稳定, 将在未来版本删除
|
||||
*/
|
||||
@Deprecated("Will be removed in the future. Use Announcement instead.", level = DeprecationLevel.WARNING)
|
||||
@MiraiExperimentalApi
|
||||
@Serializable
|
||||
public data class GroupAnnouncementList(
|
||||
val ec: Int, //状态码 0 是正常的
|
||||
@SerialName("em") val msg: String, //信息
|
||||
internal data class GroupAnnouncementList(
|
||||
@SerialName("ec") override val errorCode: Int = 0,
|
||||
@SerialName("em") override val errorMessage: String? = null,
|
||||
val feeds: List<GroupAnnouncement>? = null, //群公告列表
|
||||
val inst: List<GroupAnnouncement>? = null //置顶列表? 应该是发送给新成员的
|
||||
) {
|
||||
) : CheckableResponseA(), JsonStruct {
|
||||
/*
|
||||
// notes from original implementor, luo123, on 2020/3/13
|
||||
|
||||
@ -41,52 +36,45 @@ public data class GroupAnnouncementList(
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* 群公告的协议数据结构. 仅同于内部操作, 用户请使用 [AnnouncementImpl].
|
||||
*
|
||||
* @suppress 此 API 非常不稳定, 将在未来版本删除
|
||||
*/
|
||||
@Deprecated("Will be removed in the future. Use Announcement instead.", level = DeprecationLevel.WARNING)
|
||||
@MiraiExperimentalApi
|
||||
@Serializable
|
||||
public data class GroupAnnouncement(
|
||||
internal data class GroupAnnouncement(
|
||||
@SerialName("u") val sender: Long = 0, //发送者id
|
||||
val msg: GroupAnnouncementMsg,
|
||||
val type: Int = 0, //20 为inst , 6 为feeds
|
||||
val settings: GroupAnnouncementSettings? = null,
|
||||
val settings: GroupAnnouncementSettings = GroupAnnouncementSettings.DEFAULT,
|
||||
@SerialName("pubt") val time: Long = 0, //发布时间
|
||||
@SerialName("read_num") val readNum: Int = 0, //如果需要确认,则为确认收到的人数,反之则为已经阅读的人数
|
||||
@SerialName("is_read") val isRead: Int = 0, //好像没用
|
||||
@SerialName("is_all_confirm") val isAllConfirm: Int = 0, //为0 则未全部收到
|
||||
val pinned: Int = 0, //1为置顶, 0为默认
|
||||
val fid: String? = null, //公告的id
|
||||
)
|
||||
) : JsonStruct
|
||||
|
||||
/**
|
||||
* 群公告的协议数据结构. 仅同于内部操作, 用户请使用 [AnnouncementImpl].
|
||||
*
|
||||
* @suppress 此 API 非常不稳定, 将在未来版本删除
|
||||
*/
|
||||
@Deprecated("Will be removed in the future. Use Announcement instead.", level = DeprecationLevel.WARNING)
|
||||
@MiraiExperimentalApi
|
||||
@Serializable
|
||||
public data class GroupAnnouncementMsg(
|
||||
internal class GroupAnnouncementImage @MiraiInternalApi constructor(
|
||||
@SerialName("h") val height: String,
|
||||
@SerialName("w") val width: String,
|
||||
@SerialName("id") val id: String
|
||||
) : JsonStruct {
|
||||
fun toPublic(): AnnouncementImage = AnnouncementImage(height, width, id)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
internal data class GroupAnnouncementMsg(
|
||||
val text: String,
|
||||
val text_face: String? = null,
|
||||
val title: String? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* 群公告的协议数据结构. 仅同于内部操作, 用户请使用 [AnnouncementImpl].
|
||||
*
|
||||
* @suppress 此 API 非常不稳定, 将在未来版本删除
|
||||
*/
|
||||
@Deprecated("Will be removed in the future. Use Announcement instead.", level = DeprecationLevel.WARNING)
|
||||
@MiraiExperimentalApi
|
||||
|
||||
@Serializable
|
||||
public data class GroupAnnouncementSettings(
|
||||
internal data class GroupAnnouncementSettings(
|
||||
@SerialName("is_show_edit_card") val isShowEditCard: Int = 0, //引导群成员修改该昵称 1 引导
|
||||
@SerialName("remind_ts") val remindTs: Int = 0,
|
||||
@SerialName("tip_window_type") val tipWindowType: Int = 0, //是否用弹窗展示 1 不使用
|
||||
@SerialName("confirm_required") val confirmRequired: Int = 0 // 是否需要确认收到 1 需要
|
||||
)
|
||||
) : JsonStruct {
|
||||
companion object {
|
||||
val DEFAULT = GroupAnnouncementSettings()
|
||||
}
|
||||
}
|
@ -14,6 +14,8 @@ import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.writeFully
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.internal.AbstractBot
|
||||
import net.mamoe.mirai.internal.network.components.BotClientHolder
|
||||
import net.mamoe.mirai.utils.*
|
||||
|
||||
|
||||
@ -137,6 +139,10 @@ internal data class WLoginSigInfo(
|
||||
override fun toString(): String {
|
||||
return "WLoginSigInfo(uin=$uin, encryptA1=${encryptA1?.toUHexString()}, noPicSig=${noPicSig?.toUHexString()}, simpleInfo=$simpleInfo, appPri=$appPri, a2ExpiryTime=$a2ExpiryTime, loginBitmap=$loginBitmap, tgt=${tgt.toUHexString()}, a2CreationTime=$a2CreationTime, tgtKey=${tgtKey.toUHexString()}, userStSig=$userStSig, userStKey=${userStKey.toUHexString()}, userStWebSig=$userStWebSig, userA5=$userA5, userA8=$userA8, lsKey=$lsKey, sKey=$sKey, userSig64=$userSig64, openId=${openId.toUHexString()}, openKey=$openKey, vKey=$vKey, accessToken=$accessToken, d2=$d2, d2Key=${d2Key.toUHexString()}, sid=$sid, aqSig=$aqSig, psKey=$psKeyMap, superKey=${superKey.toUHexString()}, payToken=${payToken.toUHexString()}, pf=${pf.toUHexString()}, pfKey=${pfKey.toUHexString()}, da2=${da2.toUHexString()}, wtSessionTicket=$wtSessionTicket, wtSessionTicketKey=${wtSessionTicketKey.toUHexString()}, deviceToken=${deviceToken.toUHexString()})"
|
||||
}
|
||||
|
||||
fun getPsKey(name: String): String {
|
||||
return psKeyMap[name]?.data?.encodeToString() ?: error("Cannot find PsKey $name")
|
||||
}
|
||||
}
|
||||
|
||||
internal typealias PSKeyMap = MutableMap<String, KeyWithExpiry>
|
||||
@ -173,6 +179,11 @@ internal open class KeyWithExpiry(
|
||||
}
|
||||
}
|
||||
|
||||
internal val KeyWithExpiry.str get() = data.encodeToString()
|
||||
internal val AbstractBot.sKey get() = client.wLoginSigInfo.sKey.str
|
||||
internal fun AbstractBot.psKey(name: String) = client.wLoginSigInfo.getPsKey(name)
|
||||
internal val AbstractBot.client get() = components[BotClientHolder].client
|
||||
|
||||
@Serializable
|
||||
internal open class KeyWithCreationTime(
|
||||
open val data: ByteArray,
|
||||
|
@ -13,9 +13,13 @@
|
||||
|
||||
package net.mamoe.mirai.internal.utils.io
|
||||
|
||||
import io.ktor.utils.io.streams.*
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.io.streams.outputStream
|
||||
import net.mamoe.mirai.internal.utils.coerceAtMostOrFail
|
||||
import net.mamoe.mirai.internal.utils.crypto.TEA
|
||||
import net.mamoe.mirai.utils.ExternalResource
|
||||
import net.mamoe.mirai.utils.withUse
|
||||
|
||||
internal fun BytePacketBuilder.writeShortLVByteArrayLimitedLength(array: ByteArray, maxLength: Int) {
|
||||
if (array.size <= maxLength) {
|
||||
@ -29,6 +33,17 @@ internal fun BytePacketBuilder.writeShortLVByteArrayLimitedLength(array: ByteArr
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BytePacketBuilder.writeResource(
|
||||
resource: ExternalResource,
|
||||
close: Boolean = false,
|
||||
): Long = resource.inputStream().withUse { copyTo(outputStream()) }.also {
|
||||
if (close) resource.close()
|
||||
}
|
||||
|
||||
internal fun io.ktor.utils.io.core.BytePacketBuilder.writeResource(
|
||||
resource: ExternalResource,
|
||||
): Long = resource.inputStream().withUse { copyTo(outputStream()) }
|
||||
|
||||
internal inline fun BytePacketBuilder.writeShortLVByteArray(byteArray: ByteArray): Int {
|
||||
this.writeShort(byteArray.size.toShort())
|
||||
this.writeFully(byteArray)
|
||||
|
Loading…
Reference in New Issue
Block a user