Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Announcement confirmed member api #2255

Merged
merged 5 commits into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 {
Expand Down
8 changes: 8 additions & 0 deletions mirai-core-api/compatibility-validation/jvm/api/jvm.api
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

/**
Expand Down
14 changes: 14 additions & 0 deletions mirai-core-mock/src/internal/contact/MockAnnouncementsImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
)
}