Skip to content

Commit 1c79da0

Browse files
authored
[core] feat: Announcement confirmed member api (#2255)
* feat: Announcement confirmed * add: native * add: todo * fix: dump
1 parent a77c4e2 commit 1c79da0

File tree

7 files changed

+163
-0
lines changed

7 files changed

+163
-0
lines changed

mirai-core-api/compatibility-validation/android/api/android.api

+8
Original file line numberDiff line numberDiff line change
@@ -844,8 +844,10 @@ public abstract interface class net/mamoe/mirai/contact/announcement/Announcemen
844844
public abstract fun delete (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
845845
public fun get (Ljava/lang/String;)Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;
846846
public abstract fun get (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
847+
public abstract fun members (Ljava/lang/String;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
847848
public fun publish (Lnet/mamoe/mirai/contact/announcement/Announcement;)Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;
848849
public abstract fun publish (Lnet/mamoe/mirai/contact/announcement/Announcement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
850+
public abstract fun remind (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
849851
public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/contact/announcement/AnnouncementImage;
850852
public abstract fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
851853
}
@@ -886,6 +888,12 @@ public abstract interface class net/mamoe/mirai/contact/announcement/OnlineAnnou
886888
public abstract fun getPublicationTime ()J
887889
public abstract fun getSender ()Lnet/mamoe/mirai/contact/NormalMember;
888890
public abstract fun getSenderId ()J
891+
public fun members (Z)Ljava/util/List;
892+
public fun members (ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
893+
public static synthetic fun members$suspendImpl (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
894+
public fun remind ()V
895+
public fun remind (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
896+
public static synthetic fun remind$suspendImpl (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
889897
}
890898

891899
public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt {

mirai-core-api/compatibility-validation/jvm/api/jvm.api

+8
Original file line numberDiff line numberDiff line change
@@ -844,8 +844,10 @@ public abstract interface class net/mamoe/mirai/contact/announcement/Announcemen
844844
public abstract fun delete (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
845845
public fun get (Ljava/lang/String;)Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;
846846
public abstract fun get (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
847+
public abstract fun members (Ljava/lang/String;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
847848
public fun publish (Lnet/mamoe/mirai/contact/announcement/Announcement;)Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;
848849
public abstract fun publish (Lnet/mamoe/mirai/contact/announcement/Announcement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
850+
public abstract fun remind (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
849851
public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/contact/announcement/AnnouncementImage;
850852
public abstract fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
851853
}
@@ -886,6 +888,12 @@ public abstract interface class net/mamoe/mirai/contact/announcement/OnlineAnnou
886888
public abstract fun getPublicationTime ()J
887889
public abstract fun getSender ()Lnet/mamoe/mirai/contact/NormalMember;
888890
public abstract fun getSenderId ()J
891+
public fun members (Z)Ljava/util/List;
892+
public fun members (ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
893+
public static synthetic fun members$suspendImpl (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
894+
public fun remind ()V
895+
public fun remind (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
896+
public static synthetic fun remind$suspendImpl (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
889897
}
890898

891899
public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt {

mirai-core-api/src/commonMain/kotlin/contact/announcement/Announcements.kt

+26
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package net.mamoe.mirai.contact.announcement
1212
import kotlinx.coroutines.flow.Flow
1313
import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
1414
import net.mamoe.mirai.contact.Group
15+
import net.mamoe.mirai.contact.NormalMember
1516
import net.mamoe.mirai.contact.PermissionDeniedException
1617
import net.mamoe.mirai.utils.DeprecatedSinceMirai
1718
import net.mamoe.mirai.utils.ExternalResource
@@ -84,6 +85,31 @@ public interface Announcements : Streamable<OnlineAnnouncement> {
8485
@JvmBlockingBridge
8586
public suspend fun uploadImage(resource: ExternalResource): AnnouncementImage
8687

88+
/**
89+
* 获取 已确认/未确认 的群成员
90+
*
91+
* @param fid 公告的 [OnlineAnnouncement.fid]
92+
* @param confirmed 是否确认
93+
* @return 群成员列表
94+
*
95+
* @throws PermissionDeniedException 当没有权限时抛出
96+
* @throws IllegalStateException 当协议异常时抛出
97+
*
98+
* @see OnlineAnnouncement.members
99+
*/
100+
public suspend fun members(fid: String, confirmed: Boolean): List<NormalMember>
101+
102+
/**
103+
* 提醒 未确认 的群成员
104+
*
105+
* @param fid 公告的 [OnlineAnnouncement.fid]
106+
*
107+
* @throws PermissionDeniedException 当没有权限时抛出
108+
* @throws IllegalStateException 当协议异常时抛出
109+
*
110+
* @see OnlineAnnouncement.remind
111+
*/
112+
public suspend fun remind(fid: String)
87113

88114
// no blocking bridge for this method
89115
@Suppress("INAPPLICABLE_JVM_NAME")

mirai-core-api/src/commonMain/kotlin/contact/announcement/OnlineAnnouncement.kt

+23
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,29 @@ public interface OnlineAnnouncement : Announcement {
7777
* @see Announcements.delete
7878
*/
7979
public suspend fun delete(): Boolean = group.announcements.delete(fid)
80+
81+
/**
82+
* 获取 已确认/未确认 的群成员
83+
*
84+
* @param confirmed 是否确认
85+
* @return 群成员列表
86+
*
87+
* @throws PermissionDeniedException 当没有权限时抛出
88+
* @throws IllegalStateException 当协议异常时抛出
89+
*
90+
* @see Announcements.members
91+
*/
92+
public suspend fun members(confirmed: Boolean): List<NormalMember> = group.announcements.members(fid, confirmed)
93+
94+
/**
95+
* 提醒 未确认 的群成员
96+
*
97+
* @throws PermissionDeniedException 当没有权限时抛出
98+
* @throws IllegalStateException 当协议异常时抛出
99+
*
100+
* @see Announcements.remind
101+
*/
102+
public suspend fun remind(): Unit = group.announcements.remind(fid)
80103
}
81104

82105
/**

mirai-core-mock/src/internal/contact/MockAnnouncementsImpl.kt

+14
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,18 @@ internal class MockAnnouncementsImpl(
9292
override suspend fun uploadImage(resource: ExternalResource): AnnouncementImage = resource.inResource {
9393
AnnouncementImage.create(generateImageId(resource.md5), 500, 500)
9494
}
95+
96+
override suspend fun members(fid: String, confirmed: Boolean): List<NormalMember> {
97+
if (!group.botPermission.isOperator()) {
98+
throw PermissionDeniedException("Only administrator have permission see announcement confirmed detail")
99+
}
100+
// TODO: 设置用户可读状态,而不返回全部
101+
return group.members.toList()
102+
}
103+
104+
override suspend fun remind(fid: String) {
105+
if (!group.botPermission.isOperator()) {
106+
throw PermissionDeniedException("Only administrator have permission send announcement remind")
107+
}
108+
}
95109
}

mirai-core/src/commonMain/kotlin/contact/announcement/AnnouncementsImpl.kt

+60
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,25 @@ import io.ktor.client.request.forms.*
1414
import io.ktor.client.statement.*
1515
import io.ktor.http.*
1616
import kotlinx.coroutines.flow.*
17+
import kotlinx.serialization.KSerializer
1718
import kotlinx.serialization.SerialName
1819
import kotlinx.serialization.Serializable
1920
import net.mamoe.mirai.contact.Group
2021
import net.mamoe.mirai.contact.MemberPermission
22+
import net.mamoe.mirai.contact.NormalMember
2123
import net.mamoe.mirai.contact.announcement.*
2224
import net.mamoe.mirai.contact.checkBotPermission
2325
import net.mamoe.mirai.internal.AbstractBot
2426
import net.mamoe.mirai.internal.QQAndroidBot
2527
import net.mamoe.mirai.internal.contact.GroupImpl
2628
import net.mamoe.mirai.internal.contact.OnlineAnnouncementImpl
29+
import net.mamoe.mirai.internal.contact.active.defaultJson
2730
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.deleteGroupAnnouncement
2831
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.getGroupAnnouncement
2932
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.getRawGroupAnnouncements
33+
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.getReadDetail
3034
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.sendGroupAnnouncement
35+
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.sendRemind
3136
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.toAnnouncement
3237
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.toGroupAnnouncement
3338
import net.mamoe.mirai.internal.message.contextualBugReportException
@@ -140,6 +145,17 @@ internal abstract class CommonAnnouncementsImpl(
140145
AnnouncementProtocol.uploadGroupAnnouncementImage(bot, resource)
141146
}
142147
}
148+
149+
override suspend fun members(fid: String, confirmed: Boolean): List<NormalMember> {
150+
group.checkBotPermission(MemberPermission.ADMINISTRATOR) { "Only administrator have permission see announcement confirmed detail" }
151+
val detail = bot.getReadDetail(groupId = group.id, fid = fid, read = confirmed)
152+
return detail.users.mapNotNull { user -> group[user.uin] }
153+
}
154+
155+
override suspend fun remind(fid: String) {
156+
group.checkBotPermission(MemberPermission.ADMINISTRATOR) { "Only administrator have permission send announcement remind" }
157+
bot.sendRemind(groupId = group.id, fid = fid)
158+
}
143159
}
144160

145161
private val serversStub = listOf("web.qun.qq.com" to 80)
@@ -296,6 +312,50 @@ internal object AnnouncementProtocol {
296312
append("format", "json")
297313
})
298314

315+
private fun <T> CgiData.loadData(serializer: KSerializer<T>): T =
316+
defaultJson.decodeFromJsonElement(serializer, this.data)
317+
318+
suspend fun QQAndroidBot.getReadDetail(groupId: Long, fid: String, read: Boolean): GroupAnnouncementReadDetail {
319+
val cgi = bot.components[HttpClientProvider].getHttpClient().post {
320+
url("https://qun.qq.com/cgi-bin/qunapp/announce_unread")
321+
parameter("gc", groupId)
322+
parameter("start", 0)
323+
parameter("num", 3000)
324+
parameter("feed_id", fid)
325+
parameter("type", if (read) 1 else 0)
326+
parameter("bkn", client.wLoginSigInfo.bkn)
327+
headers {
328+
// ktor bug
329+
append(
330+
"cookie",
331+
"uin=o${id}; skey=${sKey}; p_uin=o${id}; p_skey=${psKey("qun.qq.com")};"
332+
)
333+
}
334+
}.bodyAsText().loadAs(CgiData.serializer())
335+
check(cgi.cgicode == 0) { cgi.errorMessage }
336+
return cgi.loadData(GroupAnnouncementReadDetail.serializer())
337+
}
338+
339+
suspend fun QQAndroidBot.sendRemind(groupId: Long, fid: String) {
340+
val cgi = bot.components[HttpClientProvider].getHttpClient().post {
341+
url("https://qun.qq.com/cgi-bin/qunapp/announce_remindread")
342+
parameter("gc", groupId)
343+
parameter("feed_id", fid)
344+
parameter("bkn", client.wLoginSigInfo.bkn)
345+
headers {
346+
// ktor bug
347+
append(
348+
"cookie",
349+
"uin=o${id}; skey=${sKey}; p_uin=o${id}; p_skey=${psKey("qun.qq.com")};"
350+
)
351+
}
352+
}.bodyAsText().loadAs(CgiData.serializer())
353+
// 14 "该公告不存在"
354+
// 54016 "该公告已提醒多次,不可再提醒。"
355+
// 54010 "提醒太频繁,请稍后再试"
356+
check(cgi.cgicode == 0) { cgi.errorMessage }
357+
}
358+
299359
fun Announcement.toGroupAnnouncement(senderId: Long): GroupAnnouncement {
300360
return GroupAnnouncement(
301361
sender = senderId,

mirai-core/src/commonMain/kotlin/contact/announcement/GroupAnnouncement.kt

+24
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ package net.mamoe.mirai.internal.contact.announcement
1313

1414
import kotlinx.serialization.SerialName
1515
import kotlinx.serialization.Serializable
16+
import kotlinx.serialization.json.JsonElement
1617
import net.mamoe.mirai.contact.announcement.AnnouncementImage
1718
import net.mamoe.mirai.utils.CheckableResponseA
1819
import net.mamoe.mirai.utils.JsonStruct
@@ -78,3 +79,26 @@ internal data class GroupAnnouncementSettings(
7879
val DEFAULT = GroupAnnouncementSettings()
7980
}
8081
}
82+
83+
@Serializable
84+
internal data class CgiData(
85+
@SerialName("cgicode") val cgicode: Int,
86+
@SerialName("data") val `data`: JsonElement,
87+
@SerialName("msg") override val errorMessage: String,
88+
@SerialName("retcode") override val errorCode: Int
89+
) : CheckableResponseA(), JsonStruct
90+
91+
@Serializable
92+
internal data class GroupAnnouncementReadDetail(
93+
@SerialName("read_total") val readTotal: Int = 0,
94+
@SerialName("unread_total") val unreadTotal: Int = 0,
95+
@SerialName("users") val users: List<User> = emptyList()
96+
) {
97+
@Serializable
98+
data class User(
99+
@SerialName("avatar") val avatar: String,
100+
@SerialName("display_name") val displayName: String,
101+
@SerialName("face_flag") val faceFlag: Int,
102+
@SerialName("uin") val uin: Long
103+
)
104+
}

0 commit comments

Comments
 (0)