3 Commits

6 changed files with 115 additions and 35 deletions

View File

@ -59,6 +59,7 @@ import moe.fuqiuluo.shamrock.remote.service.data.EssenceMessage
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncement import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncement
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessage import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessage
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessageImage import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessageImage
import moe.fuqiuluo.shamrock.remote.service.data.push.MemberRole
import moe.fuqiuluo.shamrock.tools.EmptyJsonArray import moe.fuqiuluo.shamrock.tools.EmptyJsonArray
import moe.fuqiuluo.shamrock.tools.GlobalClient import moe.fuqiuluo.shamrock.tools.GlobalClient
import moe.fuqiuluo.shamrock.tools.asInt import moe.fuqiuluo.shamrock.tools.asInt
@ -394,6 +395,20 @@ internal object GroupSvc: BaseSvc() {
.filter { it != 0L } .filter { it != 0L }
} }
suspend fun getMemberRole(groupId: Long, memberUin: Long): MemberRole {
return when(getTroopMemberInfoByUinViaNt(groupId.toString(), memberUin, 3000).getOrNull()?.role) {
com.tencent.qqnt.kernel.nativeinterface.MemberRole.STRANGER -> MemberRole.Stranger
com.tencent.qqnt.kernel.nativeinterface.MemberRole.MEMBER -> MemberRole.Member
com.tencent.qqnt.kernel.nativeinterface.MemberRole.ADMIN -> MemberRole.Admin
com.tencent.qqnt.kernel.nativeinterface.MemberRole.OWNER -> MemberRole.Owner
com.tencent.qqnt.kernel.nativeinterface.MemberRole.UNSPECIFIED, null -> when (memberUin) {
getOwner(groupId.toString()) -> MemberRole.Owner
in getAdminList(groupId.toString()) -> MemberRole.Admin
else -> MemberRole.Member
}
}
}
fun getOwner(groupId: String): Long { fun getOwner(groupId: String): Long {
val groupInfo = getGroupInfo(groupId) val groupInfo = getGroupInfo(groupId)
return groupInfo.troopowneruin?.toLong() ?: 0 return groupInfo.troopowneruin?.toLong() ?: 0
@ -566,11 +581,71 @@ internal object GroupSvc: BaseSvc() {
} }
} }
private suspend fun getTroopMemberInfoByUinViaNt(groupId: String, qq: Long): Result<MemberInfo> { suspend fun getTroopMemberInfoByUinV2(
groupId: String,
uin: String,
refresh: Boolean = false
): Result<TroopMemberInfo> {
val service = app.getRuntimeService(ITroopMemberInfoService::class.java, "all")
var info = service.getTroopMember(groupId, uin)
if (refresh || !service.isMemberInCache(groupId, uin) || info == null || info.troopnick == null) {
info = requestTroopMemberInfo(service, groupId.toLong(), uin.toLong(), timeout = 2000).getOrNull()
}
if (info == null) {
info = getTroopMemberInfoByUinViaNt(groupId, uin.toLong(), timeout = 2000L).getOrNull()?.let {
TroopMemberInfo().apply {
troopnick = it.cardName
friendnick = it.nick
}
}
}
try {
if (info != null && (info.alias == null || info.alias.isBlank())) {
val req = group_member_info.ReqBody()
req.uint64_group_code.set(groupId.toLong())
req.uint64_uin.set(uin.toLong())
req.bool_new_client.set(true)
req.uint32_client_type.set(1)
req.uint32_rich_card_name_ver.set(1)
val respBuffer = sendBufferAW("group_member_card.get_group_member_card_info", true, req.toByteArray(), timeout = 2000)
if (respBuffer != null) {
val rsp = group_member_info.RspBody()
rsp.mergeFrom(respBuffer.slice(4))
if (rsp.msg_meminfo.str_location.has()) {
info.alias = rsp.msg_meminfo.str_location.get().toStringUtf8()
}
if (rsp.msg_meminfo.uint32_age.has()) {
info.age = rsp.msg_meminfo.uint32_age.get().toByte()
}
if (rsp.msg_meminfo.bytes_group_honor.has()) {
val honorBytes = rsp.msg_meminfo.bytes_group_honor.get().toByteArray()
val honor = troop_honor.GroupUserCardHonor()
honor.mergeFrom(honorBytes)
info.level = honor.level.get()
// 10315: medal_id not real group level
}
}
}
} catch (err: Throwable) {
LogCenter.log(err.stackTraceToString(), Level.WARN)
}
return if (info != null) {
Result.success(info)
} else {
Result.failure(Exception("获取群成员信息失败"))
}
}
suspend fun getTroopMemberInfoByUinViaNt(
groupId: String,
qq: Long,
timeout: Long = 5000L
): Result<MemberInfo> {
val kernelService = NTServiceFetcher.kernelService val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession val sessionService = kernelService.wrapperSession
val groupService = sessionService.groupService val groupService = sessionService.groupService
val info = suspendCancellableCoroutine { val info = withTimeoutOrNull(timeout) {
suspendCancellableCoroutine {
groupService.getTransferableMemberInfo(groupId.toLong()) { code, _, data -> groupService.getTransferableMemberInfo(groupId.toLong()) { code, _, data ->
if (code != 0) { if (code != 0) {
it.resume(null) it.resume(null)
@ -585,6 +660,7 @@ internal object GroupSvc: BaseSvc() {
it.resume(null) it.resume(null)
} }
} }
}
return if (info != null) { return if (info != null) {
Result.success(info) Result.success(info)
} else { } else {
@ -748,7 +824,7 @@ internal object GroupSvc: BaseSvc() {
} }
} }
private suspend fun requestTroopMemberInfo(service: ITroopMemberInfoService, groupId: Long, memberUin: Long): Result<TroopMemberInfo> { private suspend fun requestTroopMemberInfo(service: ITroopMemberInfoService, groupId: Long, memberUin: Long, timeout: Long = 10_000): Result<TroopMemberInfo> {
val info = RefreshTroopMemberInfoLock.withLock { val info = RefreshTroopMemberInfoLock.withLock {
val groupIdStr = groupId.toString() val groupIdStr = groupId.toString()
val memberUinStr = memberUin.toString() val memberUinStr = memberUin.toString()
@ -758,7 +834,7 @@ internal object GroupSvc: BaseSvc() {
requestMemberInfoV2(groupId, memberUin) requestMemberInfoV2(groupId, memberUin)
requestMemberInfo(groupId, memberUin) requestMemberInfo(groupId, memberUin)
withTimeoutOrNull(10000) { withTimeoutOrNull(timeout) {
while (!service.isMemberInCache(groupIdStr, memberUinStr)) { while (!service.isMemberInCache(groupIdStr, memberUinStr)) {
delay(200) delay(200)
} }

View File

@ -670,7 +670,9 @@ internal object MessageMaker {
at.atNtUid = "0" at.atNtUid = "0"
} }
else -> { else -> {
val info = GroupSvc.getTroopMemberInfoByUin(peerId, qq, true).onFailure { val name = data["name"].asStringOrNull
if (name == null) {
val info = GroupSvc.getTroopMemberInfoByUinV2(peerId, qq, true).onFailure {
LogCenter.log("无法获取群成员信息: $qq", Level.ERROR) LogCenter.log("无法获取群成员信息: $qq", Level.ERROR)
}.getOrNull() }.getOrNull()
if (info != null) { if (info != null) {
@ -680,7 +682,10 @@ internal object MessageMaker {
.ifNullOrEmpty(qq) .ifNullOrEmpty(qq)
}" }"
} else { } else {
at.content = "@${data["name"].asStringOrNull.ifNullOrEmpty(qq)}" at.content = "@$qq"
}
} else {
at.content = "@$name"
} }
at.atType = MsgConstant.ATTYPEONE at.atType = MsgConstant.ATTYPEONE
at.atNtUid = ContactHelper.getUidByUinAsync(qq.toLong()) at.atNtUid = ContactHelper.getUidByUinAsync(qq.toLong())

View File

@ -52,11 +52,7 @@ internal object GetTroopMemberInfo : IActionHandler() {
area = info.alias ?: "", area = info.alias ?: "",
lastSentTime = info.last_active_time, lastSentTime = info.last_active_time,
level = info.level, level = info.level,
role = when { role = GroupSvc.getMemberRole(groupId.toLong(), uin.toLong()),
GroupSvc.getOwner(groupId).toString() == uin -> MemberRole.Owner
uin.toLong() in GroupSvc.getAdminList(groupId) -> MemberRole.Admin
else -> MemberRole.Member
},
unfriendly = false, unfriendly = false,
title = info.mUniqueTitle ?: "", title = info.mUniqueTitle ?: "",
titleExpireTime = info.mUniqueTitleExpire, titleExpireTime = info.mUniqueTitleExpire,

View File

@ -54,12 +54,13 @@ internal object GetTroopMemberList : IActionHandler() {
area = info.alias ?: "", area = info.alias ?: "",
lastSentTime = info.last_active_time, lastSentTime = info.last_active_time,
level = info.level, level = info.level,
role = when { role = GroupSvc.getMemberRole(groupId.toLong(), info.memberuin.toLong())
/*when {
GroupSvc.getOwner(groupId) GroupSvc.getOwner(groupId)
.toString() == info.memberuin -> MemberRole.Owner .toString() == info.memberuin -> MemberRole.Owner
info.memberuin.toLong() in GroupSvc.getAdminList(groupId) -> MemberRole.Admin info.memberuin.toLong() in GroupSvc.getAdminList(groupId) -> MemberRole.Admin
else -> MemberRole.Member else -> MemberRole.Member
}, }*/,
unfriendly = false, unfriendly = false,
title = info.mUniqueTitle ?: "", title = info.mUniqueTitle ?: "",
titleExpireTime = info.mUniqueTitleExpire, titleExpireTime = info.mUniqueTitleExpire,

View File

@ -92,11 +92,11 @@ internal object GlobalEventTransmitter: BaseSvc() {
.ifEmpty { record.sendMemberName } .ifEmpty { record.sendMemberName }
.ifEmpty { record.peerName }, .ifEmpty { record.peerName },
card = record.sendMemberName, card = record.sendMemberName,
role = when (record.senderUin) { role = GroupSvc.getMemberRole(record.peerUin, record.senderUin)/*when (record.senderUin) {
GroupSvc.getOwner(record.peerUin.toString()) -> MemberRole.Owner GroupSvc.getOwner(record.peerUin.toString()) -> MemberRole.Owner
in GroupSvc.getAdminList(record.peerUin.toString()) -> MemberRole.Admin in GroupSvc.getAdminList(record.peerUin.toString()) -> MemberRole.Admin
else -> MemberRole.Member else -> MemberRole.Member
}, }*/,
title = "", title = "",
level = "", level = "",
) )
@ -194,7 +194,7 @@ internal object GlobalEventTransmitter: BaseSvc() {
userId = record.senderUid.toLong(), userId = record.senderUid.toLong(),
nickname = nickName, nickname = nickName,
card = record.sendMemberName, card = record.sendMemberName,
role = MemberRole.Member, role = MemberRole.Member, // TODO(GUILD ROLE)
title = record.sendNickName, title = record.sendNickName,
level = record.roleId.toString(), level = record.roleId.toString(),
tinyId = record.senderUid tinyId = record.senderUid

View File

@ -88,7 +88,9 @@ internal data class Anonymous(
internal enum class MemberRole { internal enum class MemberRole {
@SerialName("owner") Owner, @SerialName("owner") Owner,
@SerialName("admin") Admin, @SerialName("admin") Admin,
@SerialName("member") Member @SerialName("member") Member,
@SerialName("stranger") Stranger,
@SerialName("unknown") Unknown
} }
@Serializable @Serializable