mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 05:12:17 +00:00
Compare commits
17 Commits
1.0.8
...
cf943fd13a
Author | SHA1 | Date | |
---|---|---|---|
cf943fd13a | |||
9608b46799 | |||
502956e3ec | |||
27b4c26da7 | |||
65f54360f8 | |||
9a9fad975f | |||
7153b21cd4 | |||
fdb2486090 | |||
d60b2a25d1 | |||
2d8dde6951 | |||
78fd60dade | |||
80dbf6af28 | |||
1e53753b5a | |||
e727877268 | |||
63b69df3ea | |||
b03e02675b | |||
e68a1ffd37 |
12
SECURITY.md
12
SECURITY.md
@ -1,11 +1,19 @@
|
|||||||
# Security Policy
|
# Security Policy
|
||||||
|
|
||||||
## Support Version
|
## 支持的版本
|
||||||
|
|
||||||
| Version | Supported |
|
| 版本 | 支持状态 |
|
||||||
| ------- | ------------------ |
|
| ------- | ------------------ |
|
||||||
|
| 9.0.15 | :white_check_mark: |
|
||||||
| 8.9.75 | :white_check_mark: |
|
| 8.9.75 | :white_check_mark: |
|
||||||
| 8.9.73 | :white_check_mark: |
|
| 8.9.73 | :white_check_mark: |
|
||||||
| 8.9.98 | :white_check_mark: |
|
| 8.9.98 | :white_check_mark: |
|
||||||
| < 8.9.68| :x: |
|
| < 8.9.68| :x: |
|
||||||
|
|
||||||
|
## 频道支持性说明
|
||||||
|
|
||||||
|
如果需要使用`频道`相关功能,请升级QQ到9.0.8版本!
|
||||||
|
|
||||||
|
## Riru检测问题
|
||||||
|
|
||||||
|
QQ自`9.0.8`开始将会检测riru,可能作为封号因素。
|
||||||
|
@ -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,23 +581,84 @@ 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) {
|
||||||
groupService.getTransferableMemberInfo(groupId.toLong()) { code, _, data ->
|
suspendCancellableCoroutine {
|
||||||
if (code != 0) {
|
groupService.getTransferableMemberInfo(groupId.toLong()) { code, _, data ->
|
||||||
it.resume(null)
|
if (code != 0) {
|
||||||
return@getTransferableMemberInfo
|
it.resume(null)
|
||||||
}
|
return@getTransferableMemberInfo
|
||||||
data.forEach { (_, info) ->
|
|
||||||
if (info.uin == qq) {
|
|
||||||
it.resume(info)
|
|
||||||
return@forEach
|
|
||||||
}
|
}
|
||||||
|
data.forEach { (_, info) ->
|
||||||
|
if (info.uin == qq) {
|
||||||
|
it.resume(info)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.resume(null)
|
||||||
}
|
}
|
||||||
it.resume(null)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return if (info != null) {
|
return if (info != null) {
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -670,17 +670,22 @@ internal object MessageMaker {
|
|||||||
at.atNtUid = "0"
|
at.atNtUid = "0"
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val info = GroupSvc.getTroopMemberInfoByUin(peerId, qq, true).onFailure {
|
val name = data["name"].asStringOrNull
|
||||||
LogCenter.log("无法获取群成员信息: $qq", Level.ERROR)
|
if (name == null) {
|
||||||
}.getOrNull()
|
val info = GroupSvc.getTroopMemberInfoByUinV2(peerId, qq, true).onFailure {
|
||||||
if (info != null) {
|
LogCenter.log("无法获取群成员信息: $qq", Level.ERROR)
|
||||||
at.content = "@${
|
}.getOrNull()
|
||||||
info.troopnick
|
if (info != null) {
|
||||||
.ifNullOrEmpty(info.friendnick)
|
at.content = "@${
|
||||||
.ifNullOrEmpty(qq)
|
info.troopnick
|
||||||
}"
|
.ifNullOrEmpty(info.friendnick)
|
||||||
|
.ifNullOrEmpty(qq)
|
||||||
|
}"
|
||||||
|
} else {
|
||||||
|
at.content = "@$qq"
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
at.content = "@${data["name"].asStringOrNull.ifNullOrEmpty(qq)}"
|
at.content = "@$name"
|
||||||
}
|
}
|
||||||
at.atType = MsgConstant.ATTYPEONE
|
at.atType = MsgConstant.ATTYPEONE
|
||||||
at.atNtUid = ContactHelper.getUidByUinAsync(qq.toLong())
|
at.atNtUid = ContactHelper.getUidByUinAsync(qq.toLong())
|
||||||
|
@ -353,6 +353,21 @@ internal object MessageHelper {
|
|||||||
database.messageMappingDao().insert(mapping)
|
database.messageMappingDao().insert(mapping)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveMsgMappingNotExist(
|
||||||
|
hash: Int,
|
||||||
|
qqMsgId: Long,
|
||||||
|
time: Long,
|
||||||
|
chatType: Int,
|
||||||
|
peerId: String,
|
||||||
|
subPeerId: String,
|
||||||
|
msgSeq: Int,
|
||||||
|
subChatType: Int = chatType
|
||||||
|
) {
|
||||||
|
val database = MessageDB.getInstance()
|
||||||
|
val mapping = MessageMapping(hash, qqMsgId, chatType, subChatType, peerId, time, msgSeq, subPeerId)
|
||||||
|
database.messageMappingDao().insertNotExist(mapping)
|
||||||
|
}
|
||||||
|
|
||||||
external fun createMessageUniseq(chatType: Int, time: Long): Long
|
external fun createMessageUniseq(chatType: Int, time: Long): Long
|
||||||
|
|
||||||
fun decodeCQCode(code: String): JsonArray {
|
fun decodeCQCode(code: String): JsonArray {
|
||||||
|
@ -30,6 +30,9 @@ interface MessageMappingDao {
|
|||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insert(mapping: MessageMapping)
|
fun insert(mapping: MessageMapping)
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||||
|
fun insertNotExist(mapping: MessageMapping)
|
||||||
|
|
||||||
@Query("UPDATE message_mapping_v2 SET msgSeq = :msgSeq WHERE msgHashId = :hash")
|
@Query("UPDATE message_mapping_v2 SET msgSeq = :msgSeq WHERE msgHashId = :hash")
|
||||||
fun updateMsgSeqByMsgHash(hash: Int, msgSeq: Int)
|
fun updateMsgSeqByMsgHash(hash: Int, msgSeq: Int)
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||||
|
|
||||||
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
@ -18,8 +19,10 @@ internal object GetGProChannelList: IActionHandler() {
|
|||||||
return invoke(guildId.toULong(), refresh, echo = session.echo)
|
return invoke(guildId.toULong(), refresh, echo = session.echo)
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun invoke(guildId: ULong, refresh: Boolean, echo: JsonElement = EmptyJsonString): String {
|
suspend operator fun invoke(guildId: ULong, refresh: Boolean, echo: JsonElement = EmptyJsonString): String {
|
||||||
val result = GProSvc.getChannelList(guildId, refresh)
|
val result = withTimeoutOrNull(5000) {
|
||||||
|
GProSvc.getChannelList(guildId, refresh)
|
||||||
|
} ?: return error("timeout", echo)
|
||||||
result.onFailure {
|
result.onFailure {
|
||||||
return error(it.message ?: "unable to fetch channel list", echo)
|
return error(it.message ?: "unable to fetch channel list", echo)
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,16 @@ internal object GetHistoryMsg: IActionHandler() {
|
|||||||
val msgList = ArrayList<MessageDetail>().apply {
|
val msgList = ArrayList<MessageDetail>().apply {
|
||||||
addAll(result.data!!.map { msg ->
|
addAll(result.data!!.map { msg ->
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(msg.chatType, msg.msgId)
|
val msgHash = MessageHelper.generateMsgIdHash(msg.chatType, msg.msgId)
|
||||||
|
MessageHelper.saveMsgMappingNotExist(
|
||||||
|
hash = msgHash,
|
||||||
|
qqMsgId = msg.msgId,
|
||||||
|
chatType = msg.chatType,
|
||||||
|
subChatType = msg.chatType,
|
||||||
|
peerId = peerId,
|
||||||
|
msgSeq = msg.msgSeq.toInt(),
|
||||||
|
time = msg.msgTime,
|
||||||
|
subPeerId = msg.channelId ?: peerId
|
||||||
|
)
|
||||||
MessageDetail(
|
MessageDetail(
|
||||||
time = msg.msgTime.toInt(),
|
time = msg.msgTime.toInt(),
|
||||||
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -2,6 +2,7 @@ package moe.fuqiuluo.shamrock.remote.action.handlers
|
|||||||
|
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
||||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||||
@ -17,7 +18,7 @@ internal object ModifyTroopMemberName: IActionHandler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
operator fun invoke(groupId: String, userId: String, card: String, echo: JsonElement = EmptyJsonString): String {
|
operator fun invoke(groupId: String, userId: String, card: String, echo: JsonElement = EmptyJsonString): String {
|
||||||
if (!GroupSvc.isAdmin(groupId)) {
|
if (!GroupSvc.isAdmin(groupId) && userId != TicketSvc.getUin()) {
|
||||||
return logic("you are not admin", echo)
|
return logic("you are not admin", echo)
|
||||||
}
|
}
|
||||||
return if(GroupSvc.modifyGroupMemberCard(groupId.toLong(), userId.toLong(), card))
|
return if(GroupSvc.modifyGroupMemberCard(groupId.toLong(), userId.toLong(), card))
|
||||||
|
@ -5,6 +5,7 @@ import com.tencent.mobileqq.transfile.TransferRequest
|
|||||||
import com.tencent.mobileqq.transfile.api.ITransFileController
|
import com.tencent.mobileqq.transfile.api.ITransFileController
|
||||||
import io.ktor.server.routing.Routing
|
import io.ktor.server.routing.Routing
|
||||||
import io.ktor.server.routing.post
|
import io.ktor.server.routing.post
|
||||||
|
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
|
||||||
import moe.fuqiuluo.shamrock.remote.structures.Status
|
import moe.fuqiuluo.shamrock.remote.structures.Status
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchPost
|
import moe.fuqiuluo.shamrock.tools.fetchPost
|
||||||
import moe.fuqiuluo.shamrock.tools.respond
|
import moe.fuqiuluo.shamrock.tools.respond
|
||||||
@ -15,7 +16,7 @@ import kotlin.random.Random
|
|||||||
import kotlin.random.nextLong
|
import kotlin.random.nextLong
|
||||||
|
|
||||||
fun Routing.registerBDH() {
|
fun Routing.registerBDH() {
|
||||||
post("/upload_group_image") {
|
if(ShamrockConfig.isDev()) post("/upload_group_image") {
|
||||||
val troop = fetchPost("troop")
|
val troop = fetchPost("troop")
|
||||||
val picBytes = Base64.decode(fetchPost("pic"), Base64.DEFAULT)
|
val picBytes = Base64.decode(fetchPost("pic"), Base64.DEFAULT)
|
||||||
val md5Str = MD5.getMd5Hex(picBytes)
|
val md5Str = MD5.getMd5Hex(picBytes)
|
||||||
@ -46,5 +47,4 @@ fun Routing.registerBDH() {
|
|||||||
.transferAsync(transferRequest)
|
.transferAsync(transferRequest)
|
||||||
respond(isOk = true, Status.Ok, "$md5Str.jpg")
|
respond(isOk = true, Status.Ok, "$md5Str.jpg")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -52,7 +52,6 @@ internal object HttpService: HttpTransmitServlet() {
|
|||||||
GlobalEventTransmitter.onNoticeEvent { event ->
|
GlobalEventTransmitter.onNoticeEvent { event ->
|
||||||
pushTo(event)
|
pushTo(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
submitFlowJob(GlobalScope.launch {
|
submitFlowJob(GlobalScope.launch {
|
||||||
GlobalEventTransmitter.onRequestEvent {
|
GlobalEventTransmitter.onRequestEvent {
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
|
@file:OptIn(DelicateCoroutinesApi::class)
|
||||||
|
|
||||||
package moe.fuqiuluo.shamrock.remote.service.api
|
package moe.fuqiuluo.shamrock.remote.service.api
|
||||||
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
import kotlinx.coroutines.flow.FlowCollector
|
import kotlinx.coroutines.flow.FlowCollector
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.CardSvc
|
import moe.fuqiuluo.qqinterface.servlet.CardSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
||||||
@ -48,7 +54,7 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) = messageEventFlow.emit(record to message)
|
private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) = messageEventFlow.emit(record to message)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息 手淫器
|
* 消息
|
||||||
*/
|
*/
|
||||||
object MessageTransmitter {
|
object MessageTransmitter {
|
||||||
/**
|
/**
|
||||||
@ -86,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 = "",
|
||||||
)
|
)
|
||||||
@ -172,6 +178,8 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
postType = postType,
|
postType = postType,
|
||||||
messageType = MsgType.Guild,
|
messageType = MsgType.Guild,
|
||||||
subType = MsgSubType.Channel,
|
subType = MsgSubType.Channel,
|
||||||
|
guildId = record.guildId,
|
||||||
|
channelId = record.channelId,
|
||||||
messageId = msgHash,
|
messageId = msgHash,
|
||||||
targetId = record.peerUin,
|
targetId = record.peerUin,
|
||||||
peerId = botUin,
|
peerId = botUin,
|
||||||
@ -186,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
|
||||||
@ -554,19 +562,29 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
|
|
||||||
@ShamrockDsl
|
@ShamrockDsl
|
||||||
suspend inline fun onMessageEvent(collector: FlowCollector<Pair<MsgRecord, MessageEvent>>) {
|
suspend inline fun onMessageEvent(collector: FlowCollector<Pair<MsgRecord, MessageEvent>>) {
|
||||||
messageEventFlow.collect(collector)
|
messageEventFlow.collect {
|
||||||
|
GlobalScope.launch {
|
||||||
|
collector.emit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ShamrockDsl
|
@ShamrockDsl
|
||||||
suspend inline fun onNoticeEvent(collector: FlowCollector<NoticeEvent>) {
|
suspend inline fun onNoticeEvent(collector: FlowCollector<NoticeEvent>) {
|
||||||
noticeEventFlow
|
noticeEventFlow.collect {
|
||||||
.collect(collector)
|
GlobalScope.launch {
|
||||||
|
collector.emit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ShamrockDsl
|
@ShamrockDsl
|
||||||
suspend inline fun onRequestEvent(collector: FlowCollector<RequestEvent>) {
|
suspend inline fun onRequestEvent(collector: FlowCollector<RequestEvent>) {
|
||||||
requestEventFlow
|
requestEventFlow.collect {
|
||||||
.collect(collector)
|
GlobalScope.launch {
|
||||||
|
collector.emit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,10 @@ internal abstract class WebSocketTransmitServlet(
|
|||||||
private val sendLock = Mutex()
|
private val sendLock = Mutex()
|
||||||
protected val eventReceivers: MutableList<WebSocket> = Collections.synchronizedList(mutableListOf<WebSocket>())
|
protected val eventReceivers: MutableList<WebSocket> = Collections.synchronizedList(mutableListOf<WebSocket>())
|
||||||
|
|
||||||
|
init {
|
||||||
|
connectionLostTimeout = 0
|
||||||
|
}
|
||||||
|
|
||||||
override val address: String
|
override val address: String
|
||||||
get() = "-"
|
get() = "-"
|
||||||
|
|
||||||
|
@ -53,6 +53,8 @@ internal data class MessageEvent (
|
|||||||
@SerialName("sub_type") val subType: MsgSubType,
|
@SerialName("sub_type") val subType: MsgSubType,
|
||||||
@SerialName("message_id") val messageId: Int,
|
@SerialName("message_id") val messageId: Int,
|
||||||
@SerialName("group_id") val groupId: Long = 0,
|
@SerialName("group_id") val groupId: Long = 0,
|
||||||
|
@SerialName("guild_id") val guildId: String? = null,
|
||||||
|
@SerialName("channel_id") val channelId: String? = null,
|
||||||
@SerialName("target_id") val targetId: Long = 0,
|
@SerialName("target_id") val targetId: Long = 0,
|
||||||
@SerialName("peer_id") val peerId: Long,
|
@SerialName("peer_id") val peerId: Long,
|
||||||
@SerialName("user_id") val userId: Long,
|
@SerialName("user_id") val userId: Long,
|
||||||
@ -86,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
|
||||||
|
@ -44,11 +44,11 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
it.value(record)
|
it.value(record)
|
||||||
messageLessListenerMap.remove(it.key)
|
messageLessListenerMap.remove(it.key)
|
||||||
}
|
}
|
||||||
if (record.msgSeq < 0) return
|
if (record.msgSeq < 0) return
|
||||||
|
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
||||||
|
|
||||||
val peerId = when(record.chatType) {
|
val peerId = when (record.chatType) {
|
||||||
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
else -> record.peerUin.toString()
|
else -> record.peerUin.toString()
|
||||||
}
|
}
|
||||||
@ -87,12 +87,14 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!GlobalEventTransmitter.MessageTransmitter.transGroupMessage(
|
if (!GlobalEventTransmitter.MessageTransmitter.transGroupMessage(
|
||||||
record, record.elements, rawMsg, msgHash, postType
|
record, record.elements, rawMsg, msgHash, postType
|
||||||
)) {
|
)
|
||||||
|
) {
|
||||||
LogCenter.log("群消息推送失败 -> 推送目标可能不存在", Level.WARN)
|
LogCenter.log("群消息推送失败 -> 推送目标可能不存在", Level.WARN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MsgConstant.KCHATTYPEC2C -> {
|
MsgConstant.KCHATTYPEC2C -> {
|
||||||
LogCenter.log("私聊消息(private = ${record.senderUin}, id = [$msgHash | ${record.msgId} | ${record.msgSeq}], msg = $rawMsg)")
|
LogCenter.log("私聊消息(private = ${record.senderUin}, id = [$msgHash | ${record.msgId} | ${record.msgSeq}], msg = $rawMsg)")
|
||||||
ShamrockConfig.getPrivateRule()?.let { rule ->
|
ShamrockConfig.getPrivateRule()?.let { rule ->
|
||||||
@ -100,9 +102,10 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!GlobalEventTransmitter.MessageTransmitter.transPrivateMessage(
|
if (!GlobalEventTransmitter.MessageTransmitter.transPrivateMessage(
|
||||||
record, record.elements, rawMsg, msgHash, postType
|
record, record.elements, rawMsg, msgHash, postType
|
||||||
)) {
|
)
|
||||||
|
) {
|
||||||
LogCenter.log("私聊消息推送失败 -> MessageTransmitter", Level.WARN)
|
LogCenter.log("私聊消息推送失败 -> MessageTransmitter", Level.WARN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,18 +119,24 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
if (!rule.white.isNullOrEmpty() && !rule.white.contains(record.senderUin)) return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!GlobalEventTransmitter.MessageTransmitter.transPrivateMessage(
|
if (!GlobalEventTransmitter.MessageTransmitter.transPrivateMessage(
|
||||||
record, record.elements, rawMsg, msgHash, tempSource = MessageTempSource.Group, postType = postType
|
record,
|
||||||
)) {
|
record.elements,
|
||||||
|
rawMsg,
|
||||||
|
msgHash,
|
||||||
|
tempSource = MessageTempSource.Group,
|
||||||
|
postType = postType
|
||||||
|
)
|
||||||
|
) {
|
||||||
LogCenter.log("私聊临时消息推送失败 -> MessageTransmitter", Level.WARN)
|
LogCenter.log("私聊临时消息推送失败 -> MessageTransmitter", Level.WARN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MsgConstant.KCHATTYPEGUILD -> {
|
MsgConstant.KCHATTYPEGUILD -> {
|
||||||
LogCenter.log("频道消息(guildId = ${record.guildId}, sender=${record.senderUid}, id = [$msgHash | ${record.msgId}], msg = $rawMsg)")
|
LogCenter.log("频道消息(guildId = ${record.guildId}, sender=${record.senderUid}, id = [$msgHash | ${record.msgId}], msg = $rawMsg)")
|
||||||
if(!GlobalEventTransmitter.MessageTransmitter
|
if (!GlobalEventTransmitter.MessageTransmitter
|
||||||
.transGuildMessage(record, record.elements, rawMsg, msgHash, postType = postType)
|
.transGuildMessage(record, record.elements, rawMsg, msgHash, postType = postType)
|
||||||
) {
|
) {
|
||||||
LogCenter.log("频道消息推送失败 -> MessageTransmitter", Level.WARN)
|
LogCenter.log("频道消息推送失败 -> MessageTransmitter", Level.WARN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,7 +159,7 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
try {
|
try {
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
||||||
|
|
||||||
val peerId = when(record.chatType) {
|
val peerId = when (record.chatType) {
|
||||||
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
else -> record.peerUin.toString()
|
else -> record.peerUin.toString()
|
||||||
}
|
}
|
||||||
@ -174,8 +183,6 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
|
|
||||||
override fun onMsgInfoListUpdate(msgList: ArrayList<MsgRecord>?) {
|
override fun onMsgInfoListUpdate(msgList: ArrayList<MsgRecord>?) {
|
||||||
msgList?.forEach { record ->
|
msgList?.forEach { record ->
|
||||||
if (record.chatType == MsgConstant.KCHATTYPEGUILD) return@forEach// TODO: 频道消息暂不处理
|
|
||||||
|
|
||||||
if (record.sendStatus == MsgConstant.KSENDSTATUSFAILED
|
if (record.sendStatus == MsgConstant.KSENDSTATUSFAILED
|
||||||
|| record.sendStatus == MsgConstant.KSENDSTATUSSENDING
|
|| record.sendStatus == MsgConstant.KSENDSTATUSSENDING
|
||||||
) {
|
) {
|
||||||
@ -184,7 +191,7 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
|
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
||||||
val peerId = when(record.chatType) {
|
val peerId = when (record.chatType) {
|
||||||
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
else -> record.peerUin.toString()
|
else -> record.peerUin.toString()
|
||||||
}
|
}
|
||||||
@ -449,6 +456,7 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
override fun onGuildMsgAbFlagChanged(guildMsgAbFlag: GuildMsgAbFlag?) {
|
override fun onGuildMsgAbFlagChanged(guildMsgAbFlag: GuildMsgAbFlag?) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: GuildNotificationAbstractInfo?) {
|
override fun onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: GuildNotificationAbstractInfo?) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -534,12 +534,11 @@ internal object PrimitiveListener {
|
|||||||
val groupCode = event.groupCode
|
val groupCode = event.groupCode
|
||||||
val applierUid = event.applierUid
|
val applierUid = event.applierUid
|
||||||
val reason = event.applyMsg ?: ""
|
val reason = event.applyMsg ?: ""
|
||||||
val applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
var applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
||||||
if (applier == getLongUin()) {
|
if (applier == getLongUin()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val msgSeq = contentHead.msgSeq
|
val msgSeq = contentHead.msgSeq
|
||||||
LogCenter.log("入群申请($groupCode) $applier: \"$reason\", seq: $msgSeq")
|
|
||||||
val flag = try {
|
val flag = try {
|
||||||
var reqs = requestGroupSystemMsgNew(10, 1)
|
var reqs = requestGroupSystemMsgNew(10, 1)
|
||||||
val riskReqs = requestGroupSystemMsgNew(5, 2)
|
val riskReqs = requestGroupSystemMsgNew(5, 2)
|
||||||
@ -548,10 +547,14 @@ internal object PrimitiveListener {
|
|||||||
it.msg_time.get() == time && it.msg?.group_code?.get() == groupCode
|
it.msg_time.get() == time && it.msg?.group_code?.get() == groupCode
|
||||||
}
|
}
|
||||||
val seq = req?.msg_seq?.get() ?: time
|
val seq = req?.msg_seq?.get() ?: time
|
||||||
|
if (applier == 0L) {
|
||||||
|
applier = req?.req_uin?.get() ?: 0L
|
||||||
|
}
|
||||||
"$seq;$groupCode;$applier"
|
"$seq;$groupCode;$applier"
|
||||||
} catch (err: Throwable) {
|
} catch (err: Throwable) {
|
||||||
"$time;$groupCode;$applier"
|
"$time;$groupCode;$applier"
|
||||||
}
|
}
|
||||||
|
LogCenter.log("入群申请($groupCode) $applier: \"$reason\", seq: $msgSeq")
|
||||||
if (!GlobalEventTransmitter.RequestTransmitter
|
if (!GlobalEventTransmitter.RequestTransmitter
|
||||||
.transGroupApply(time, applier, applierUid, reason, groupCode, flag, RequestSubType.Add)
|
.transGroupApply(time, applier, applierUid, reason, groupCode, flag, RequestSubType.Add)
|
||||||
) {
|
) {
|
||||||
@ -562,7 +565,7 @@ internal object PrimitiveListener {
|
|||||||
val event = ProtoBuf.decodeFromByteArray<GroupInvitedApplyEvent>(richMsg.rawBuffer!!)
|
val event = ProtoBuf.decodeFromByteArray<GroupInvitedApplyEvent>(richMsg.rawBuffer!!)
|
||||||
val groupCode = event.applyInfo?.groupCode ?: return
|
val groupCode = event.applyInfo?.groupCode ?: return
|
||||||
val applierUid = event.applyInfo?.applierUid ?: return
|
val applierUid = event.applyInfo?.applierUid ?: return
|
||||||
val applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
var applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
|
||||||
if (applier == getLongUin()) {
|
if (applier == getLongUin()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -570,7 +573,6 @@ internal object PrimitiveListener {
|
|||||||
// todo
|
// todo
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
LogCenter.log("邀请入群申请($groupCode): $applier")
|
|
||||||
val flag = try {
|
val flag = try {
|
||||||
var reqs = requestGroupSystemMsgNew(10, 1)
|
var reqs = requestGroupSystemMsgNew(10, 1)
|
||||||
val riskReqs = requestGroupSystemMsgNew(5, 2)
|
val riskReqs = requestGroupSystemMsgNew(5, 2)
|
||||||
@ -579,10 +581,14 @@ internal object PrimitiveListener {
|
|||||||
it.msg_time.get() == time
|
it.msg_time.get() == time
|
||||||
}
|
}
|
||||||
val seq = req?.msg_seq?.get() ?: time
|
val seq = req?.msg_seq?.get() ?: time
|
||||||
|
if (applier == 0L) {
|
||||||
|
applier = req?.req_uin?.get() ?: 0L
|
||||||
|
}
|
||||||
"$seq;$groupCode;$applier"
|
"$seq;$groupCode;$applier"
|
||||||
} catch (err: Throwable) {
|
} catch (err: Throwable) {
|
||||||
"$time;$groupCode;$applier"
|
"$time;$groupCode;$applier"
|
||||||
}
|
}
|
||||||
|
LogCenter.log("邀请入群申请($groupCode): $applier")
|
||||||
if (!GlobalEventTransmitter.RequestTransmitter
|
if (!GlobalEventTransmitter.RequestTransmitter
|
||||||
.transGroupApply(time, applier, applierUid, "", groupCode, flag, RequestSubType.Add)
|
.transGroupApply(time, applier, applierUid, "", groupCode, flag, RequestSubType.Add)
|
||||||
) {
|
) {
|
||||||
|
Reference in New Issue
Block a user