[core] feat: Announcement confirmed member api (#2255)

* feat: Announcement confirmed

* add: native

* add: todo

* fix: dump
This commit is contained in:
cssxsh 2022-11-03 18:42:21 +08:00 committed by GitHub
parent a77c4e2adf
commit 1c79da0f38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 163 additions and 0 deletions

View File

@ -844,8 +844,10 @@ public abstract interface class net/mamoe/mirai/contact/announcement/Announcemen
public abstract fun delete (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun get (Ljava/lang/String;)Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;
public abstract fun get (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun members (Ljava/lang/String;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun publish (Lnet/mamoe/mirai/contact/announcement/Announcement;)Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;
public abstract fun publish (Lnet/mamoe/mirai/contact/announcement/Announcement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun remind (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/contact/announcement/AnnouncementImage;
public abstract fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
@ -886,6 +888,12 @@ public abstract interface class net/mamoe/mirai/contact/announcement/OnlineAnnou
public abstract fun getPublicationTime ()J
public abstract fun getSender ()Lnet/mamoe/mirai/contact/NormalMember;
public abstract fun getSenderId ()J
public fun members (Z)Ljava/util/List;
public fun members (ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun members$suspendImpl (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun remind ()V
public fun remind (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun remind$suspendImpl (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt {

View File

@ -844,8 +844,10 @@ public abstract interface class net/mamoe/mirai/contact/announcement/Announcemen
public abstract fun delete (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun get (Ljava/lang/String;)Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;
public abstract fun get (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun members (Ljava/lang/String;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun publish (Lnet/mamoe/mirai/contact/announcement/Announcement;)Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;
public abstract fun publish (Lnet/mamoe/mirai/contact/announcement/Announcement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun remind (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/contact/announcement/AnnouncementImage;
public abstract fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
@ -886,6 +888,12 @@ public abstract interface class net/mamoe/mirai/contact/announcement/OnlineAnnou
public abstract fun getPublicationTime ()J
public abstract fun getSender ()Lnet/mamoe/mirai/contact/NormalMember;
public abstract fun getSenderId ()J
public fun members (Z)Ljava/util/List;
public fun members (ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun members$suspendImpl (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun remind ()V
public fun remind (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun remind$suspendImpl (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt {

View File

@ -12,6 +12,7 @@ package net.mamoe.mirai.contact.announcement
import kotlinx.coroutines.flow.Flow
import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.NormalMember
import net.mamoe.mirai.contact.PermissionDeniedException
import net.mamoe.mirai.utils.DeprecatedSinceMirai
import net.mamoe.mirai.utils.ExternalResource
@ -84,6 +85,31 @@ public interface Announcements : Streamable<OnlineAnnouncement> {
@JvmBlockingBridge
public suspend fun uploadImage(resource: ExternalResource): AnnouncementImage
/**
* 获取 已确认/未确认 的群成员
*
* @param fid 公告的 [OnlineAnnouncement.fid]
* @param confirmed 是否确认
* @return 群成员列表
*
* @throws PermissionDeniedException 当没有权限时抛出
* @throws IllegalStateException 当协议异常时抛出
*
* @see OnlineAnnouncement.members
*/
public suspend fun members(fid: String, confirmed: Boolean): List<NormalMember>
/**
* 提醒 未确认 的群成员
*
* @param fid 公告的 [OnlineAnnouncement.fid]
*
* @throws PermissionDeniedException 当没有权限时抛出
* @throws IllegalStateException 当协议异常时抛出
*
* @see OnlineAnnouncement.remind
*/
public suspend fun remind(fid: String)
// no blocking bridge for this method
@Suppress("INAPPLICABLE_JVM_NAME")

View File

@ -77,6 +77,29 @@ public interface OnlineAnnouncement : Announcement {
* @see Announcements.delete
*/
public suspend fun delete(): Boolean = group.announcements.delete(fid)
/**
* 获取 已确认/未确认 的群成员
*
* @param confirmed 是否确认
* @return 群成员列表
*
* @throws PermissionDeniedException 当没有权限时抛出
* @throws IllegalStateException 当协议异常时抛出
*
* @see Announcements.members
*/
public suspend fun members(confirmed: Boolean): List<NormalMember> = group.announcements.members(fid, confirmed)
/**
* 提醒 未确认 的群成员
*
* @throws PermissionDeniedException 当没有权限时抛出
* @throws IllegalStateException 当协议异常时抛出
*
* @see Announcements.remind
*/
public suspend fun remind(): Unit = group.announcements.remind(fid)
}
/**

View File

@ -92,4 +92,18 @@ internal class MockAnnouncementsImpl(
override suspend fun uploadImage(resource: ExternalResource): AnnouncementImage = resource.inResource {
AnnouncementImage.create(generateImageId(resource.md5), 500, 500)
}
override suspend fun members(fid: String, confirmed: Boolean): List<NormalMember> {
if (!group.botPermission.isOperator()) {
throw PermissionDeniedException("Only administrator have permission see announcement confirmed detail")
}
// TODO: 设置用户可读状态,而不返回全部
return group.members.toList()
}
override suspend fun remind(fid: String) {
if (!group.botPermission.isOperator()) {
throw PermissionDeniedException("Only administrator have permission send announcement remind")
}
}
}

View File

@ -14,20 +14,25 @@ import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.coroutines.flow.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.contact.NormalMember
import net.mamoe.mirai.contact.announcement.*
import net.mamoe.mirai.contact.checkBotPermission
import net.mamoe.mirai.internal.AbstractBot
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.GroupImpl
import net.mamoe.mirai.internal.contact.OnlineAnnouncementImpl
import net.mamoe.mirai.internal.contact.active.defaultJson
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.getRawGroupAnnouncements
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.getReadDetail
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.sendGroupAnnouncement
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.sendRemind
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.toAnnouncement
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.toGroupAnnouncement
import net.mamoe.mirai.internal.message.contextualBugReportException
@ -140,6 +145,17 @@ internal abstract class CommonAnnouncementsImpl(
AnnouncementProtocol.uploadGroupAnnouncementImage(bot, resource)
}
}
override suspend fun members(fid: String, confirmed: Boolean): List<NormalMember> {
group.checkBotPermission(MemberPermission.ADMINISTRATOR) { "Only administrator have permission see announcement confirmed detail" }
val detail = bot.getReadDetail(groupId = group.id, fid = fid, read = confirmed)
return detail.users.mapNotNull { user -> group[user.uin] }
}
override suspend fun remind(fid: String) {
group.checkBotPermission(MemberPermission.ADMINISTRATOR) { "Only administrator have permission send announcement remind" }
bot.sendRemind(groupId = group.id, fid = fid)
}
}
private val serversStub = listOf("web.qun.qq.com" to 80)
@ -296,6 +312,50 @@ internal object AnnouncementProtocol {
append("format", "json")
})
private fun <T> CgiData.loadData(serializer: KSerializer<T>): T =
defaultJson.decodeFromJsonElement(serializer, this.data)
suspend fun QQAndroidBot.getReadDetail(groupId: Long, fid: String, read: Boolean): GroupAnnouncementReadDetail {
val cgi = bot.components[HttpClientProvider].getHttpClient().post {
url("https://qun.qq.com/cgi-bin/qunapp/announce_unread")
parameter("gc", groupId)
parameter("start", 0)
parameter("num", 3000)
parameter("feed_id", fid)
parameter("type", if (read) 1 else 0)
parameter("bkn", client.wLoginSigInfo.bkn)
headers {
// ktor bug
append(
"cookie",
"uin=o${id}; skey=${sKey}; p_uin=o${id}; p_skey=${psKey("qun.qq.com")};"
)
}
}.bodyAsText().loadAs(CgiData.serializer())
check(cgi.cgicode == 0) { cgi.errorMessage }
return cgi.loadData(GroupAnnouncementReadDetail.serializer())
}
suspend fun QQAndroidBot.sendRemind(groupId: Long, fid: String) {
val cgi = bot.components[HttpClientProvider].getHttpClient().post {
url("https://qun.qq.com/cgi-bin/qunapp/announce_remindread")
parameter("gc", groupId)
parameter("feed_id", fid)
parameter("bkn", client.wLoginSigInfo.bkn)
headers {
// ktor bug
append(
"cookie",
"uin=o${id}; skey=${sKey}; p_uin=o${id}; p_skey=${psKey("qun.qq.com")};"
)
}
}.bodyAsText().loadAs(CgiData.serializer())
// 14 "该公告不存在"
// 54016 "该公告已提醒多次,不可再提醒。"
// 54010 "提醒太频繁,请稍后再试"
check(cgi.cgicode == 0) { cgi.errorMessage }
}
fun Announcement.toGroupAnnouncement(senderId: Long): GroupAnnouncement {
return GroupAnnouncement(
sender = senderId,

View File

@ -13,6 +13,7 @@ package net.mamoe.mirai.internal.contact.announcement
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import net.mamoe.mirai.contact.announcement.AnnouncementImage
import net.mamoe.mirai.utils.CheckableResponseA
import net.mamoe.mirai.utils.JsonStruct
@ -78,3 +79,26 @@ internal data class GroupAnnouncementSettings(
val DEFAULT = GroupAnnouncementSettings()
}
}
@Serializable
internal data class CgiData(
@SerialName("cgicode") val cgicode: Int,
@SerialName("data") val `data`: JsonElement,
@SerialName("msg") override val errorMessage: String,
@SerialName("retcode") override val errorCode: Int
) : CheckableResponseA(), JsonStruct
@Serializable
internal data class GroupAnnouncementReadDetail(
@SerialName("read_total") val readTotal: Int = 0,
@SerialName("unread_total") val unreadTotal: Int = 0,
@SerialName("users") val users: List<User> = emptyList()
) {
@Serializable
data class User(
@SerialName("avatar") val avatar: String,
@SerialName("display_name") val displayName: String,
@SerialName("face_flag") val faceFlag: Int,
@SerialName("uin") val uin: Long
)
}