mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 05:12:17 +00:00
Compare commits
8 Commits
262af4108b
...
db252b6b6c
Author | SHA1 | Date | |
---|---|---|---|
db252b6b6c | |||
137c354acc | |||
1c7f6bd034 | |||
649d8771ca | |||
af7b0f732e | |||
12738fd52c | |||
b165e1c0c2 | |||
29c1ad8bc9 |
@ -249,6 +249,11 @@ object ShamrockConfig {
|
|||||||
return preferences.getBoolean("enable_auto_start", false)
|
return preferences.getBoolean("enable_auto_start", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun disableAutoSyncSetting(ctx: Context): Boolean {
|
||||||
|
val preferences = ctx.getSharedPreferences("config", 0)
|
||||||
|
return preferences.getBoolean("disable_auto_sync_setting", false)
|
||||||
|
}
|
||||||
|
|
||||||
fun enableAliveReply(ctx: Context): Boolean {
|
fun enableAliveReply(ctx: Context): Boolean {
|
||||||
val preferences = ctx.getSharedPreferences("config", 0)
|
val preferences = ctx.getSharedPreferences("config", 0)
|
||||||
return preferences.getBoolean("alive_reply", false)
|
return preferences.getBoolean("alive_reply", false)
|
||||||
@ -264,6 +269,11 @@ object ShamrockConfig {
|
|||||||
preferences.edit().putBoolean("enable_auto_start", v).apply()
|
preferences.edit().putBoolean("enable_auto_start", v).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setDisableAutoSyncSetting(ctx: Context, v: Boolean) {
|
||||||
|
val preferences = ctx.getSharedPreferences("config", 0)
|
||||||
|
preferences.edit().putBoolean("disable_auto_sync_setting", v).apply()
|
||||||
|
}
|
||||||
|
|
||||||
fun setAliveReply(ctx: Context, v: Boolean) {
|
fun setAliveReply(ctx: Context, v: Boolean) {
|
||||||
val preferences = ctx.getSharedPreferences("config", 0)
|
val preferences = ctx.getSharedPreferences("config", 0)
|
||||||
preferences.edit().putBoolean("alive_reply", v).apply()
|
preferences.edit().putBoolean("alive_reply", v).apply()
|
||||||
@ -322,6 +332,7 @@ object ShamrockConfig {
|
|||||||
"shell" to preferences.getBoolean("shell", false),
|
"shell" to preferences.getBoolean("shell", false),
|
||||||
"alive_reply" to preferences.getBoolean("alive_reply", false),
|
"alive_reply" to preferences.getBoolean("alive_reply", false),
|
||||||
"enable_sync_msg_as_sent_msg" to preferences.getBoolean("enable_sync_msg_as_sent_msg", false),
|
"enable_sync_msg_as_sent_msg" to preferences.getBoolean("enable_sync_msg_as_sent_msg", false),
|
||||||
|
"disable_auto_sync_setting" to preferences.getBoolean("disable_auto_sync_setting", false),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ fun LabFragment() {
|
|||||||
ActionBox(
|
ActionBox(
|
||||||
modifier = Modifier.padding(top = 12.dp),
|
modifier = Modifier.padding(top = 12.dp),
|
||||||
painter = painterResource(id = R.drawable.round_logo_dev_24),
|
painter = painterResource(id = R.drawable.round_logo_dev_24),
|
||||||
title = "实验功能"
|
title = "基础设置"
|
||||||
) { color ->
|
) { color ->
|
||||||
Column {
|
Column {
|
||||||
Divider(
|
Divider(
|
||||||
@ -142,6 +142,16 @@ fun LabFragment() {
|
|||||||
return@Function true
|
return@Function true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Function(
|
||||||
|
title = "禁止Shamrock同步设置",
|
||||||
|
desc = "禁止Shamrock同步设置,防止恢复手动修改后的配置文件。",
|
||||||
|
descColor = color,
|
||||||
|
isSwitch = ShamrockConfig.disableAutoSyncSetting(ctx)
|
||||||
|
) {
|
||||||
|
ShamrockConfig.setDisableAutoSyncSetting(ctx, it)
|
||||||
|
return@Function true
|
||||||
|
}
|
||||||
|
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
ctx.getSharedPreferences("shared_config", Context.MODE_WORLD_READABLE)
|
ctx.getSharedPreferences("shared_config", Context.MODE_WORLD_READABLE)
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
@file:OptIn(ExperimentalSerializationApi::class)
|
||||||
|
package moe.whitechi73.protobuf.oidb.cmd0xfc2
|
||||||
|
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoNumber
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Oidb0xfc2ReqBody(
|
||||||
|
@ProtoNumber(1) var msgCmd: Int? = null,
|
||||||
|
@ProtoNumber(3) var msgBusType: Int? = null,
|
||||||
|
@ProtoNumber(4) var msgChannelInfo: Oidb0xfc2ChannelInfo? = null,
|
||||||
|
@ProtoNumber(5) var msgTerminalType: Int? = null,
|
||||||
|
//@ProtoNumber(100) var msg_apply_upload_req: Any? = null,
|
||||||
|
//@ProtoNumber(200) var msg_upload_completed_req: Any? = null,
|
||||||
|
@ProtoNumber(300) var msgApplyDownloadReq: Oidb0xfc2MsgApplyDownloadReq? = null,
|
||||||
|
//@ProtoNumber(400) var msg_apply_preview_req: Any? = null,
|
||||||
|
//@ProtoNumber(500) var msg_apply_security_strike_req: Any? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Oidb0xfc2RspBody(
|
||||||
|
@ProtoNumber(1) var msgCmd: Int? = null,
|
||||||
|
@ProtoNumber(5) var msgBusType: Int? = null,
|
||||||
|
//@ProtoNumber(110) var msg_apply_upload_rsp: Any? = null,
|
||||||
|
//@ProtoNumber(210) var msg_upload_completed_rsp: Any? = null,
|
||||||
|
@ProtoNumber(310) var msgApplyDownloadRsp: Oidb0xfc2MsgApplyDownloadRsp? = null,
|
||||||
|
//@ProtoNumber(410) var msg_apply_preview_rsp: Any? = null,
|
||||||
|
//@ProtoNumber(510) var msg_apply_security_strike_rsp: Any? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Oidb0xfc2MsgApplyDownloadRsp(
|
||||||
|
@ProtoNumber(1) var msgDownloadInfo: Oidb0xfc2MsgDownloadInfo? = null,
|
||||||
|
//@ProtoNumber(2) var msgFileInfo: Any? = null,
|
||||||
|
//@ProtoNumber(3) var msgChacha20Param: Any? = null,
|
||||||
|
//@ProtoNumber(4) var useEncrypt: UInt = UInt.MIN_VALUE,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Oidb0xfc2MsgDownloadInfo(
|
||||||
|
@ProtoNumber(1) var downloadKey: ByteArray? = null,
|
||||||
|
//@ProtoNumber(2) var msg_out_addr: Any? = null,
|
||||||
|
//@ProtoNumber(3) var msg_inner_addr: Any? = null,
|
||||||
|
//@ProtoNumber(4) var msg_out_addr_ipv6: Any? = null,
|
||||||
|
@ProtoNumber(5) var downloadDomain: String? = null,
|
||||||
|
@ProtoNumber(6) var downloadUrl: String? = null,
|
||||||
|
//@ProtoNumber(7) var str_cookie: Any? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Oidb0xfc2MsgApplyDownloadReq(
|
||||||
|
@ProtoNumber(1) val fieldId: String,
|
||||||
|
@ProtoNumber(2) val supportEncrypt: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Oidb0xfc2ChannelInfo(
|
||||||
|
@ProtoNumber(3) val guildId: ULong,
|
||||||
|
@ProtoNumber(4) val channelId: ULong,
|
||||||
|
)
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.tencent.qqnt.kernel.nativeinterface;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public interface IGProFetchMemberRolesCallback {
|
||||||
|
void onFetchMemberRolesCallback(int code, String reason, ArrayList<GProGuildRole> roles);
|
||||||
|
}
|
@ -3,5 +3,5 @@ package com.tencent.qqnt.kernel.nativeinterface;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public interface IGProGetUserInfoCallback {
|
public interface IGProGetUserInfoCallback {
|
||||||
void onGetUserInfo(int i2, String str, ArrayList<GProUser> arrayList, ArrayList<Long> arrayList2);
|
void onGetUserInfo(int code, String reason, ArrayList<GProUser> userList, ArrayList<Long> tinyIdList);
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,11 @@ public interface IKernelGuildService {
|
|||||||
|
|
||||||
void refreshGuildInfoOnly(long j2, boolean z, int i2);
|
void refreshGuildInfoOnly(long j2, boolean z, int i2);
|
||||||
|
|
||||||
void fetchUserInfo(long j2, long j3, ArrayList<Long> tinyIdList, int i2, IGProGetUserInfoCallback iGProGetUserInfoCallback);
|
void fetchMemberRoles(long guildId, long channelId, long tinyId, int seq, IGProFetchMemberRolesCallback cb);
|
||||||
|
|
||||||
|
void refreshGuildUserProfileInfo(long guildId, long tinyId, int seq);
|
||||||
|
|
||||||
|
void fetchUserInfo(long guildId, long channelId, ArrayList<Long> tinyIdList, int seq, IGProGetUserInfoCallback cb);
|
||||||
|
|
||||||
GProSimpleProfile getSimpleProfile(long guildId, long tinyId, int seq);
|
GProSimpleProfile getSimpleProfile(long guildId, long tinyId, int seq);
|
||||||
|
|
||||||
|
@ -138,6 +138,12 @@ char * __cdecl my_strstr(const char *lhs, const char *rhs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int fake_memcmp(const void* __lhs, const void* __rhs, size_t __n) {
|
int fake_memcmp(const void* __lhs, const void* __rhs, size_t __n) {
|
||||||
|
if (__lhs == nullptr || __rhs == nullptr) {
|
||||||
|
if (__n != 0) {
|
||||||
|
LOGI("[Shamrock] undefined behaviour in fake_memcmp");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (my_strstr((const char*) __rhs, "shamrock") && my_strstr((const char*) __lhs, "shamrock")) {
|
if (my_strstr((const char*) __rhs, "shamrock") && my_strstr((const char*) __lhs, "shamrock")) {
|
||||||
if (backup_memcmp(__lhs, __rhs, __n) == 0) {
|
if (backup_memcmp(__lhs, __rhs, __n) == 0) {
|
||||||
// 底层广播判断
|
// 底层广播判断
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet
|
package moe.fuqiuluo.qqinterface.servlet
|
||||||
|
|
||||||
import com.tencent.mobileqq.qqguildsdk.api.IGPSService
|
import com.tencent.mobileqq.qqguildsdk.api.IGPSService
|
||||||
|
import com.tencent.qqnt.kernel.nativeinterface.GProGuildRole
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.GProRoleMemberList
|
import com.tencent.qqnt.kernel.nativeinterface.GProRoleMemberList
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.IGProFetchMemberListWithRoleCallback
|
import com.tencent.qqnt.kernel.nativeinterface.IGProFetchMemberListWithRoleCallback
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
@ -108,6 +109,7 @@ internal object GProSvc: BaseSvc() {
|
|||||||
result: ArrayList<GProRoleMemberList> = arrayListOf()
|
result: ArrayList<GProRoleMemberList> = arrayListOf()
|
||||||
): Result<Pair<GetGuildMemberListNextToken, ArrayList<GProRoleMemberList>>> {
|
): Result<Pair<GetGuildMemberListNextToken, ArrayList<GProRoleMemberList>>> {
|
||||||
val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService
|
val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService
|
||||||
|
|
||||||
val fetchGuildMemberListResult: Pair<GetGuildMemberListNextToken, ArrayList<GProRoleMemberList>> = (withTimeoutOrNull(5000) {
|
val fetchGuildMemberListResult: Pair<GetGuildMemberListNextToken, ArrayList<GProRoleMemberList>> = (withTimeoutOrNull(5000) {
|
||||||
suspendCancellableCoroutine {
|
suspendCancellableCoroutine {
|
||||||
kernelGProService.fetchMemberListWithRole(guildId.toLong(), 0, startIndex, roleIndex, count, 0) { code, reason, finish, nextIndex, nextRoleIdIndex, _, seq, roleList ->
|
kernelGProService.fetchMemberListWithRole(guildId.toLong(), 0, startIndex, roleIndex, count, 0) { code, reason, finish, nextIndex, nextRoleIdIndex, _, seq, roleList ->
|
||||||
@ -203,6 +205,21 @@ internal object GProSvc: BaseSvc() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun fetchGuildMemberRoles(guildId: ULong, tinyId: ULong, refresh: Boolean = false): Result<ArrayList<GProGuildRole>> {
|
||||||
|
val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService
|
||||||
|
if (refresh) {
|
||||||
|
kernelGProService.refreshGuildUserProfileInfo(guildId.toLong(), tinyId.toLong(), 1)
|
||||||
|
}
|
||||||
|
val result: ArrayList<GProGuildRole> = withTimeoutOrNull(5000) {
|
||||||
|
suspendCancellableCoroutine {
|
||||||
|
kernelGProService.fetchMemberRoles(guildId.toLong(), 0, tinyId.toLong(), 2) { code, reason, roles ->
|
||||||
|
it.resume(roles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ?: return Result.failure(Exception("unable to fetch guild member roles"))
|
||||||
|
return Result.success(result)
|
||||||
|
}
|
||||||
|
|
||||||
fun getGuildList(refresh: Boolean = false, forceOldApi: Boolean): ArrayList<GuildInfo> {
|
fun getGuildList(refresh: Boolean = false, forceOldApi: Boolean): ArrayList<GuildInfo> {
|
||||||
val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService
|
val kernelGProService = NTServiceFetcher.kernelService.wrapperSession.guildService
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
|
@ -61,7 +61,7 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
?: return Result.failure(Exception("没有对应消息映射,消息获取失败"))
|
?: return Result.failure(Exception("没有对应消息映射,消息获取失败"))
|
||||||
|
|
||||||
val peerId = mapping.peerId
|
val peerId = mapping.peerId
|
||||||
val contact = MessageHelper.generateContact(mapping.chatType, peerId)
|
val contact = MessageHelper.generateContact(mapping.chatType, peerId, mapping.subPeerId ?: "")
|
||||||
|
|
||||||
val msg = withTimeoutOrNull(5000) {
|
val msg = withTimeoutOrNull(5000) {
|
||||||
val service = QRoute.api(IMsgService::class.java)
|
val service = QRoute.api(IMsgService::class.java)
|
||||||
@ -89,11 +89,11 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
suspend fun getMsgByQMsgId(
|
suspend fun getMsgByQMsgId(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
qqMsgId: Long
|
qqMsgId: Long,
|
||||||
|
subPeerId: String = ""
|
||||||
): Result<MsgRecord> {
|
): Result<MsgRecord> {
|
||||||
val contact = MessageHelper.generateContact(chatType, peerId)
|
val contact = MessageHelper.generateContact(chatType, peerId, subPeerId)
|
||||||
val service = QRoute.api(IMsgService::class.java) ?:
|
val service = QRoute.api(IMsgService::class.java)
|
||||||
return Result.failure(Exception("获取消息服务"))
|
|
||||||
|
|
||||||
val msg = withTimeoutOrNull(5000) {
|
val msg = withTimeoutOrNull(5000) {
|
||||||
suspendCoroutine { continuation ->
|
suspendCoroutine { continuation ->
|
||||||
@ -152,7 +152,7 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
val mapping = MessageHelper.getMsgMappingByHash(msgHash)
|
val mapping = MessageHelper.getMsgMappingByHash(msgHash)
|
||||||
?: return -1 to "无法找到消息映射"
|
?: return -1 to "无法找到消息映射"
|
||||||
|
|
||||||
val contact = MessageHelper.generateContact(mapping.chatType, mapping.peerId)
|
val contact = MessageHelper.generateContact(mapping.chatType, mapping.peerId, mapping.subPeerId ?: "")
|
||||||
|
|
||||||
return suspendCancellableCoroutine { continuation ->
|
return suspendCancellableCoroutine { continuation ->
|
||||||
msgService.recallMsg(contact, arrayListOf(mapping.qqMsgId)) { code, why ->
|
msgService.recallMsg(contact, arrayListOf(mapping.qqMsgId)) { code, why ->
|
||||||
@ -184,7 +184,7 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
}
|
}
|
||||||
val result = MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, fromId, MessageCallback(peedId, 0))
|
val result = MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, fromId, MessageCallback(peedId, 0))
|
||||||
result.onFailure {
|
result.onFailure {
|
||||||
LogCenter.log(it.stackTraceToString(), Level.ERROR)
|
LogCenter.log("sendToAio: " + it.stackTraceToString(), Level.ERROR)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
val sendResult = result.getOrThrow()
|
val sendResult = result.getOrThrow()
|
||||||
@ -192,7 +192,7 @@ internal object MsgSvc: BaseSvc() {
|
|||||||
// 发送失败,可能网络问题出现红色感叹号,重试
|
// 发送失败,可能网络问题出现红色感叹号,重试
|
||||||
// 例如 rich media transfer failed
|
// 例如 rich media transfer failed
|
||||||
delay(100)
|
delay(100)
|
||||||
MessageHelper.resendMsg(chatType, peedId, fromId, sendResult.qqMsgId, 3, sendResult.msgHashId)
|
MessageHelper.resendMsg(chatType, peedId, fromId, sendResult.qqMsgId, retryCnt, sendResult.msgHashId)
|
||||||
} else {
|
} else {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -32,12 +32,12 @@ internal suspend fun MsgRecord.toCQCode(): String {
|
|||||||
return MessageConvert.convertMessageRecordToCQCode(this)
|
return MessageConvert.convertMessageRecordToCQCode(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal suspend fun List<MsgElement>.toSegments(chatType: Int, peerId: String): MessageSegmentList {
|
internal suspend fun List<MsgElement>.toSegments(chatType: Int, peerId: String, subPeer: String): MessageSegmentList {
|
||||||
return MessageConvert.convertMessageElementsToMsgSegment(chatType, this, peerId)
|
return MessageConvert.convertMessageElementsToMsgSegment(chatType, this, peerId, subPeer)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal suspend fun List<MsgElement>.toCQCode(chatType: Int, peerId: String): String {
|
internal suspend fun List<MsgElement>.toCQCode(chatType: Int, peerId: String, subPeer: String): String {
|
||||||
return MessageConvert.convertMsgElementsToCQCode(this, chatType, peerId)
|
return MessageConvert.convertMsgElementsToCQCode(this, chatType, peerId, subPeer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -64,14 +64,15 @@ internal object MessageConvert {
|
|||||||
suspend fun convertMessageElementsToMsgSegment(
|
suspend fun convertMessageElementsToMsgSegment(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
elements: List<MsgElement>,
|
elements: List<MsgElement>,
|
||||||
peerId: String
|
peerId: String,
|
||||||
|
subPeer: String
|
||||||
): ArrayList<MessageSegment> {
|
): ArrayList<MessageSegment> {
|
||||||
val messageData = arrayListOf<MessageSegment>()
|
val messageData = arrayListOf<MessageSegment>()
|
||||||
elements.forEach { msg ->
|
elements.forEach { msg ->
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val elementId = msg.elementType
|
val elementId = msg.elementType
|
||||||
val converter = convertMap[elementId]
|
val converter = convertMap[elementId]
|
||||||
converter?.convert(chatType, peerId, msg)
|
converter?.convert(chatType, peerId, subPeer, msg)
|
||||||
?: throw UnsupportedOperationException("不支持的消息element类型:$elementId")
|
?: throw UnsupportedOperationException("不支持的消息element类型:$elementId")
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
messageData.add(it)
|
messageData.add(it)
|
||||||
@ -87,35 +88,45 @@ internal object MessageConvert {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun convertMessageRecordToMsgSegment(record: MsgRecord, chatType: Int = record.chatType): ArrayList<MessageSegment> {
|
suspend fun convertMessageRecordToMsgSegment(record: MsgRecord, chatType: Int = record.chatType): ArrayList<MessageSegment> {
|
||||||
return convertMessageElementsToMsgSegment(chatType, record.elements, record.peerUin.toString())
|
val peerId = when(chatType) {
|
||||||
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
|
else -> record.peerUin.toString()
|
||||||
|
}
|
||||||
|
return convertMessageElementsToMsgSegment(chatType, record.elements, peerId, record.channelId ?: peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun convertMsgElementsToCQCode(
|
suspend fun convertMsgElementsToCQCode(
|
||||||
elements: List<MsgElement>,
|
elements: List<MsgElement>,
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String
|
peerId: String,
|
||||||
|
subPeer: String
|
||||||
): String {
|
): String {
|
||||||
if(elements.isEmpty()) {
|
if(elements.isEmpty()) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
val msgList = convertMessageElementsToMsgSegment(chatType, elements, peerId).map {
|
val msgList = convertMessageElementsToMsgSegment(chatType, elements, peerId, subPeer).map {
|
||||||
it.toJson()
|
it.toJson()
|
||||||
}
|
}
|
||||||
return MessageHelper.encodeCQCode(msgList)
|
return MessageHelper.encodeCQCode(msgList)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun convertMessageRecordToCQCode(record: MsgRecord, chatType: Int = record.chatType): String {
|
suspend fun convertMessageRecordToCQCode(record: MsgRecord, chatType: Int = record.chatType): String {
|
||||||
|
val peerId = when(chatType) {
|
||||||
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
|
else -> record.peerUin.toString()
|
||||||
|
}
|
||||||
return MessageHelper.encodeCQCode(
|
return MessageHelper.encodeCQCode(
|
||||||
convertMessageElementsToMsgSegment(
|
convertMessageElementsToMsgSegment(
|
||||||
chatType,
|
chatType,
|
||||||
record.elements,
|
record.elements,
|
||||||
record.peerUin.toString()
|
peerId,
|
||||||
|
record.channelId ?: peerId
|
||||||
).map { it.toJson() }
|
).map { it.toJson() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun interface IMessageConvert {
|
internal fun interface IMessageConvert {
|
||||||
suspend fun convert(chatType: Int, peerId: String, element: MsgElement): MessageSegment
|
suspend fun convert(chatType: Int, peerId: String, subPeer: String, element: MsgElement): MessageSegment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,12 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
* 文本 / 艾特 消息转换消息段
|
* 文本 / 艾特 消息转换消息段
|
||||||
*/
|
*/
|
||||||
data object TextConverter: MessageElemConverter() {
|
data object TextConverter: MessageElemConverter() {
|
||||||
override suspend fun convert(chatType: Int, peerId: String, element: MsgElement): MessageSegment {
|
override suspend fun convert(
|
||||||
|
chatType: Int,
|
||||||
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
|
element: MsgElement
|
||||||
|
): MessageSegment {
|
||||||
val text = element.textElement
|
val text = element.textElement
|
||||||
return if (text.atType != MsgConstant.ATTYPEUNKNOWN) {
|
return if (text.atType != MsgConstant.ATTYPEUNKNOWN) {
|
||||||
MessageSegment(
|
MessageSegment(
|
||||||
@ -43,7 +48,12 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
* 小表情 / 戳一戳 消息转换消息段
|
* 小表情 / 戳一戳 消息转换消息段
|
||||||
*/
|
*/
|
||||||
data object FaceConverter: MessageElemConverter() {
|
data object FaceConverter: MessageElemConverter() {
|
||||||
override suspend fun convert(chatType: Int, peerId: String, element: MsgElement): MessageSegment {
|
override suspend fun convert(
|
||||||
|
chatType: Int,
|
||||||
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
|
element: MsgElement
|
||||||
|
): MessageSegment {
|
||||||
val face = element.faceElement
|
val face = element.faceElement
|
||||||
|
|
||||||
if (face.faceType == 5) {
|
if (face.faceType == 5) {
|
||||||
@ -112,6 +122,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val image = element.picElement
|
val image = element.picElement
|
||||||
@ -131,6 +142,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
"url" to when(chatType) {
|
"url" to when(chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(md5)
|
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(md5)
|
||||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(md5)
|
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(md5)
|
||||||
|
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(md5)
|
||||||
else -> unknownChatType(chatType)
|
else -> unknownChatType(chatType)
|
||||||
},
|
},
|
||||||
"subType" to image.picSubType,
|
"subType" to image.picSubType,
|
||||||
@ -147,6 +159,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val record = element.pttElement
|
val record = element.pttElement
|
||||||
@ -162,6 +175,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
"url" to when(chatType) {
|
"url" to when(chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPttDownUrl("0", record.md5HexStr, record.fileUuid)
|
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPttDownUrl("0", record.md5HexStr, record.fileUuid)
|
||||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", record.fileUuid)
|
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", record.fileUuid)
|
||||||
|
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl("0", record.md5HexStr, record.fileUuid)
|
||||||
else -> unknownChatType(chatType)
|
else -> unknownChatType(chatType)
|
||||||
}
|
}
|
||||||
).also {
|
).also {
|
||||||
@ -183,6 +197,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val video = element.videoElement
|
val video = element.videoElement
|
||||||
@ -195,6 +210,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
"url" to when(chatType) {
|
"url" to when(chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
|
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
|
||||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid)
|
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid)
|
||||||
|
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupVideoDownUrl(peerId, md5, video.fileUuid)
|
||||||
else -> unknownChatType(chatType)
|
else -> unknownChatType(chatType)
|
||||||
}
|
}
|
||||||
).also {
|
).also {
|
||||||
@ -212,6 +228,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val face = element.marketFaceElement
|
val face = element.marketFaceElement
|
||||||
@ -235,6 +252,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val data = element.arkElement.bytesData.asJsonObject
|
val data = element.arkElement.bytesData.asJsonObject
|
||||||
@ -297,6 +315,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val reply = element.replyElement
|
val reply = element.replyElement
|
||||||
@ -329,6 +348,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val tip = element.grayTipElement
|
val tip = element.grayTipElement
|
||||||
@ -364,6 +384,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val fileMsg = element.fileElement
|
val fileMsg = element.fileElement
|
||||||
@ -373,8 +394,11 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
val fileId = fileMsg.fileUuid
|
val fileId = fileMsg.fileUuid
|
||||||
val bizId = fileMsg.fileBizId ?: 0
|
val bizId = fileMsg.fileBizId ?: 0
|
||||||
val fileSubId = fileMsg.fileSubId ?: ""
|
val fileSubId = fileMsg.fileSubId ?: ""
|
||||||
val url = if (chatType == MsgConstant.KCHATTYPEC2C) RichProtoSvc.getC2CFileDownUrl(fileId, fileSubId)
|
val url = when (chatType) {
|
||||||
else RichProtoSvc.getGroupFileDownUrl(peerId.toLong(), fileId, bizId)
|
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CFileDownUrl(fileId, fileSubId)
|
||||||
|
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildFileDownUrl(peerId, subPeer, fileId, bizId)
|
||||||
|
else -> RichProtoSvc.getGroupFileDownUrl(peerId.toLong(), fileId, bizId)
|
||||||
|
}
|
||||||
|
|
||||||
return MessageSegment(
|
return MessageSegment(
|
||||||
type = "file",
|
type = "file",
|
||||||
@ -398,6 +422,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val multiMsg = element.multiForwardMsgElement
|
val multiMsg = element.multiForwardMsgElement
|
||||||
@ -414,6 +439,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val longMsg = element.structLongMsgElement
|
val longMsg = element.structLongMsgElement
|
||||||
@ -430,6 +456,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val markdown = element.markdownElement
|
val markdown = element.markdownElement
|
||||||
@ -446,6 +473,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
|||||||
override suspend fun convert(
|
override suspend fun convert(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeer: String,
|
||||||
element: MsgElement
|
element: MsgElement
|
||||||
): MessageSegment {
|
): MessageSegment {
|
||||||
val bubbleElement = element.faceBubbleElement
|
val bubbleElement = element.faceBubbleElement
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
@file:OptIn(ExperimentalSerializationApi::class)
|
||||||
package moe.fuqiuluo.qqinterface.servlet.transfile
|
package moe.fuqiuluo.qqinterface.servlet.transfile
|
||||||
|
|
||||||
import com.tencent.mobileqq.pb.ByteStringMicro
|
import com.tencent.mobileqq.pb.ByteStringMicro
|
||||||
@ -6,6 +7,10 @@ import com.tencent.mobileqq.transfile.api.IProtoReqManager
|
|||||||
import com.tencent.mobileqq.transfile.protohandler.RichProto
|
import com.tencent.mobileqq.transfile.protohandler.RichProto
|
||||||
import com.tencent.mobileqq.transfile.protohandler.RichProtoProc
|
import com.tencent.mobileqq.transfile.protohandler.RichProtoProc
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.decodeFromByteArray
|
||||||
|
import kotlinx.serialization.encodeToByteArray
|
||||||
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
||||||
import moe.fuqiuluo.shamrock.helper.Level
|
import moe.fuqiuluo.shamrock.helper.Level
|
||||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||||
@ -14,6 +19,10 @@ import moe.fuqiuluo.shamrock.tools.slice
|
|||||||
import moe.fuqiuluo.shamrock.tools.toHexString
|
import moe.fuqiuluo.shamrock.tools.toHexString
|
||||||
import moe.fuqiuluo.shamrock.utils.PlatformUtils
|
import moe.fuqiuluo.shamrock.utils.PlatformUtils
|
||||||
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
||||||
|
import moe.whitechi73.protobuf.oidb.cmd0xfc2.Oidb0xfc2ChannelInfo
|
||||||
|
import moe.whitechi73.protobuf.oidb.cmd0xfc2.Oidb0xfc2MsgApplyDownloadReq
|
||||||
|
import moe.whitechi73.protobuf.oidb.cmd0xfc2.Oidb0xfc2ReqBody
|
||||||
|
import moe.whitechi73.protobuf.oidb.cmd0xfc2.Oidb0xfc2RspBody
|
||||||
import mqq.app.MobileQQ
|
import mqq.app.MobileQQ
|
||||||
import tencent.im.cs.cmd0x346.cmd0x346
|
import tencent.im.cs.cmd0x346.cmd0x346
|
||||||
import tencent.im.oidb.cmd0x6d6.oidb_0x6d6
|
import tencent.im.oidb.cmd0x6d6.oidb_0x6d6
|
||||||
@ -22,6 +31,30 @@ import tencent.im.oidb.oidb_sso
|
|||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
internal object RichProtoSvc: BaseSvc() {
|
internal object RichProtoSvc: BaseSvc() {
|
||||||
|
suspend fun getGuildFileDownUrl(peerId: String, channelId: String, fileId: String, bizId: Int): String {
|
||||||
|
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xfc2_0", 4034, 0, ProtoBuf.encodeToByteArray(Oidb0xfc2ReqBody(
|
||||||
|
msgCmd = 1200,
|
||||||
|
msgBusType = 4202,
|
||||||
|
msgChannelInfo = Oidb0xfc2ChannelInfo(
|
||||||
|
guildId = peerId.toULong(),
|
||||||
|
channelId = channelId.toULong()
|
||||||
|
),
|
||||||
|
msgTerminalType = 2,
|
||||||
|
msgApplyDownloadReq = Oidb0xfc2MsgApplyDownloadReq(
|
||||||
|
fieldId = fileId,
|
||||||
|
supportEncrypt = 0
|
||||||
|
)
|
||||||
|
))) ?: return ""
|
||||||
|
val body = oidb_sso.OIDBSSOPkg()
|
||||||
|
body.mergeFrom(buffer.slice(4))
|
||||||
|
ProtoBuf.decodeFromByteArray<Oidb0xfc2RspBody>(body.bytes_bodybuffer.get().toByteArray()).msgApplyDownloadRsp?.let {
|
||||||
|
it.msgDownloadInfo?.let {
|
||||||
|
return "https://${it.downloadDomain}${it.downloadUrl}&fname=$fileId&isthumb=0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getGroupFileDownUrl(
|
suspend fun getGroupFileDownUrl(
|
||||||
peerId: Long,
|
peerId: Long,
|
||||||
fileId: String,
|
fileId: String,
|
||||||
@ -34,27 +67,23 @@ internal object RichProtoSvc: BaseSvc() {
|
|||||||
uint32_bus_id.set(bizId)
|
uint32_bus_id.set(bizId)
|
||||||
str_file_id.set(fileId)
|
str_file_id.set(fileId)
|
||||||
})
|
})
|
||||||
}.toByteArray())
|
}.toByteArray()) ?: return ""
|
||||||
if (buffer == null) {
|
val body = oidb_sso.OIDBSSOPkg()
|
||||||
|
body.mergeFrom(buffer.slice(4))
|
||||||
|
val result = oidb_0x6d6.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray())
|
||||||
|
if (body.uint32_result.get() != 0
|
||||||
|
|| result.download_file_rsp.int32_ret_code.get() != 0) {
|
||||||
return ""
|
return ""
|
||||||
} else {
|
|
||||||
val body = oidb_sso.OIDBSSOPkg()
|
|
||||||
body.mergeFrom(buffer.slice(4))
|
|
||||||
val result = oidb_0x6d6.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray())
|
|
||||||
if (body.uint32_result.get() != 0
|
|
||||||
|| result.download_file_rsp.int32_ret_code.get() != 0) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
val domain = if (!result.download_file_rsp.str_download_dns.has())
|
|
||||||
("https://" + result.download_file_rsp.str_download_ip.get())
|
|
||||||
else ("http://" + result.download_file_rsp.str_download_dns.get())
|
|
||||||
val downloadUrl = result.download_file_rsp.bytes_download_url.get().toByteArray().toHexString()
|
|
||||||
val appId = MobileQQ.getMobileQQ().appId
|
|
||||||
val version = PlatformUtils.getQQVersion(MobileQQ.getContext())
|
|
||||||
|
|
||||||
return "$domain/ftn_handler/$downloadUrl/?fname=$fileId&client_proto=qq&client_appid=$appId&client_type=android&client_ver=$version&client_down_type=auto&client_aio_type=unk"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val domain = if (!result.download_file_rsp.str_download_dns.has())
|
||||||
|
("https://" + result.download_file_rsp.str_download_ip.get())
|
||||||
|
else ("http://" + result.download_file_rsp.str_download_dns.get())
|
||||||
|
val downloadUrl = result.download_file_rsp.bytes_download_url.get().toByteArray().toHexString()
|
||||||
|
val appId = MobileQQ.getMobileQQ().appId
|
||||||
|
val version = PlatformUtils.getQQVersion(MobileQQ.getContext())
|
||||||
|
|
||||||
|
return "$domain/ftn_handler/$downloadUrl/?fname=$fileId&client_proto=qq&client_appid=$appId&client_type=android&client_ver=$version&client_down_type=auto&client_aio_type=unk"
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getC2CFileDownUrl(
|
suspend fun getC2CFileDownUrl(
|
||||||
@ -83,7 +112,7 @@ internal object RichProtoSvc: BaseSvc() {
|
|||||||
}.toByteArray())
|
}.toByteArray())
|
||||||
|
|
||||||
if (buffer == null) {
|
if (buffer == null) {
|
||||||
if (retryCnt < 3) {
|
if (retryCnt < 5) {
|
||||||
return getC2CFileDownUrl(fileId, subId, retryCnt + 1)
|
return getC2CFileDownUrl(fileId, subId, retryCnt + 1)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
@ -122,6 +151,10 @@ internal object RichProtoSvc: BaseSvc() {
|
|||||||
return "https://c2cpicdw.qpic.cn/offpic_new/0/123-0-${md5.uppercase()}/0?term=2"
|
return "https://c2cpicdw.qpic.cn/offpic_new/0/123-0-${md5.uppercase()}/0?term=2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getGuildPicDownUrl(md5: String): String {
|
||||||
|
return "https://gchat.qpic.cn/qmeetpic/0/0-0-${md5.uppercase()}/0?term=2"
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getC2CVideoDownUrl(
|
suspend fun getC2CVideoDownUrl(
|
||||||
peerId: String,
|
peerId: String,
|
||||||
md5Hex: String,
|
md5Hex: String,
|
||||||
@ -287,4 +320,11 @@ internal object RichProtoSvc: BaseSvc() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getGuildPttDownUrl(
|
||||||
|
peerId: String,
|
||||||
|
md5Hex: String,
|
||||||
|
fileUUId: String
|
||||||
|
): String {
|
||||||
|
return "unsupported"
|
||||||
|
}
|
||||||
}
|
}
|
@ -90,7 +90,7 @@ internal object MessageHelper {
|
|||||||
|
|
||||||
// ActionMsg No Care
|
// ActionMsg No Care
|
||||||
if (msg.isEmpty()) {
|
if (msg.isEmpty()) {
|
||||||
return Result.success(uniseq.copy(msgTime = System.currentTimeMillis(), msgHashId = 0))
|
return Result.success(uniseq.copy(msgTime = System.currentTimeMillis()))
|
||||||
}
|
}
|
||||||
|
|
||||||
val totalSize = msg.filter {
|
val totalSize = msg.filter {
|
||||||
@ -107,12 +107,7 @@ internal object MessageHelper {
|
|||||||
val sendRet = withTimeoutOrNull<Pair<Int, String>>(estimateTime) {
|
val sendRet = withTimeoutOrNull<Pair<Int, String>>(estimateTime) {
|
||||||
suspendCancellableCoroutine {
|
suspendCancellableCoroutine {
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
sendResult = sendMessageWithoutMsgId(
|
sendResult = sendMessageWithoutMsgId(chatType, peerId, msg, fromId) { code, message ->
|
||||||
chatType,
|
|
||||||
peerId,
|
|
||||||
msg,
|
|
||||||
fromId
|
|
||||||
) { code, message ->
|
|
||||||
callback.onResult(code, message)
|
callback.onResult(code, message)
|
||||||
it.resume(code to message)
|
it.resume(code to message)
|
||||||
}
|
}
|
||||||
@ -248,10 +243,17 @@ internal object MessageHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun generateContact(chatType: Int, id: String, subId: String = ""): Contact {
|
suspend fun generateContact(chatType: Int, id: String, subId: String = ""): Contact {
|
||||||
val peerId = if (MsgConstant.KCHATTYPEC2C == chatType || MsgConstant.KCHATTYPETEMPC2CFROMGROUP == chatType) {
|
val peerId = when(chatType) {
|
||||||
ContactHelper.getUidByUinAsync(id.toLong())
|
MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> {
|
||||||
} else id
|
ContactHelper.getUidByUinAsync(id.toLong())
|
||||||
return Contact(chatType, peerId, subId)
|
}
|
||||||
|
else -> id
|
||||||
|
}
|
||||||
|
return if (chatType == MsgConstant.KCHATTYPEGUILD) {
|
||||||
|
Contact(chatType, subId, peerId)
|
||||||
|
} else {
|
||||||
|
Contact(chatType, peerId, subId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun obtainMessageTypeByDetailType(detailType: String): Int {
|
fun obtainMessageTypeByDetailType(detailType: String): Int {
|
||||||
@ -308,6 +310,7 @@ internal object MessageHelper {
|
|||||||
MsgConstant.KCHATTYPEGROUP -> "grp$msgId"
|
MsgConstant.KCHATTYPEGROUP -> "grp$msgId"
|
||||||
MsgConstant.KCHATTYPEC2C -> "c2c$msgId"
|
MsgConstant.KCHATTYPEC2C -> "c2c$msgId"
|
||||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> "tmpgrp$msgId"
|
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> "tmpgrp$msgId"
|
||||||
|
MsgConstant.KCHATTYPEGUILD -> "guild$msgId"
|
||||||
else -> error("不支持的消息来源类型 | generateMsgIdHash: $chatType")
|
else -> error("不支持的消息来源类型 | generateMsgIdHash: $chatType")
|
||||||
}
|
}
|
||||||
return abs(key.hashCode())
|
return abs(key.hashCode())
|
||||||
@ -341,11 +344,12 @@ internal object MessageHelper {
|
|||||||
time: Long,
|
time: Long,
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
peerId: String,
|
peerId: String,
|
||||||
|
subPeerId: String,
|
||||||
msgSeq: Int,
|
msgSeq: Int,
|
||||||
subChatType: Int = chatType
|
subChatType: Int = chatType
|
||||||
) {
|
) {
|
||||||
val database = MessageDB.getInstance()
|
val database = MessageDB.getInstance()
|
||||||
val mapping = MessageMapping(hash, qqMsgId, chatType, subChatType, peerId, time, msgSeq)
|
val mapping = MessageMapping(hash, qqMsgId, chatType, subChatType, peerId, time, msgSeq, subPeerId)
|
||||||
database.messageMappingDao().insert(mapping)
|
database.messageMappingDao().insert(mapping)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import androidx.room.Room
|
|||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import mqq.app.MobileQQ
|
import mqq.app.MobileQQ
|
||||||
|
|
||||||
@Entity(tableName = "message_mapping")
|
@Entity(tableName = "message_mapping_v2")
|
||||||
data class MessageMapping (
|
data class MessageMapping (
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
val msgHashId: Int,
|
val msgHashId: Int,
|
||||||
@ -21,7 +21,8 @@ data class MessageMapping (
|
|||||||
val subChatType: Int, // 细化各种事件消息
|
val subChatType: Int, // 细化各种事件消息
|
||||||
val peerId: String,
|
val peerId: String,
|
||||||
val time: Long,
|
val time: Long,
|
||||||
val msgSeq: Int
|
val msgSeq: Int,
|
||||||
|
val subPeerId: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
@ -29,25 +30,25 @@ interface MessageMappingDao {
|
|||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insert(mapping: MessageMapping)
|
fun insert(mapping: MessageMapping)
|
||||||
|
|
||||||
@Query("UPDATE message_mapping 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)
|
||||||
|
|
||||||
@Query("DELETE FROM message_mapping WHERE msgHashId = :hash")
|
@Query("DELETE FROM message_mapping_v2 WHERE msgHashId = :hash")
|
||||||
fun deleteByMsgHash(hash: Int)
|
fun deleteByMsgHash(hash: Int)
|
||||||
|
|
||||||
@Query("SELECT * FROM message_mapping WHERE msgHashId = :msgHashId")
|
@Query("SELECT * FROM message_mapping_v2 WHERE msgHashId = :msgHashId")
|
||||||
fun queryByMsgHashId(msgHashId: Int): MessageMapping?
|
fun queryByMsgHashId(msgHashId: Int): MessageMapping?
|
||||||
|
|
||||||
@Query("SELECT * FROM message_mapping WHERE qqMsgId = :qqMsgId AND chatType = :chatType")
|
@Query("SELECT * FROM message_mapping_v2 WHERE qqMsgId = :qqMsgId AND chatType = :chatType")
|
||||||
fun queryByQqMsgId(chatType: Int, qqMsgId: Long): MessageMapping?
|
fun queryByQqMsgId(chatType: Int, qqMsgId: Long): MessageMapping?
|
||||||
|
|
||||||
@Query("SELECT * FROM message_mapping WHERE chatType = :chatType")
|
@Query("SELECT * FROM message_mapping_v2 WHERE chatType = :chatType")
|
||||||
fun queryByChatType(chatType: Int): List<MessageMapping>
|
fun queryByChatType(chatType: Int): List<MessageMapping>
|
||||||
|
|
||||||
@Query("SELECT * FROM message_mapping WHERE subChatType = :subChatType AND chatType = :chatType")
|
@Query("SELECT * FROM message_mapping_v2 WHERE subChatType = :subChatType AND chatType = :chatType")
|
||||||
fun queryBySubChatType(chatType: Int, subChatType: Int): List<MessageMapping>
|
fun queryBySubChatType(chatType: Int, subChatType: Int): List<MessageMapping>
|
||||||
|
|
||||||
@Query("SELECT * FROM message_mapping WHERE peerId = :peerId AND chatType = :chatType")
|
@Query("SELECT * FROM message_mapping_v2 WHERE peerId = :peerId AND chatType = :chatType")
|
||||||
fun queryByPeerId(chatType: Int, peerId: String): List<MessageMapping>
|
fun queryByPeerId(chatType: Int, peerId: String): List<MessageMapping>
|
||||||
|
|
||||||
//@Query("SELECT * FROM message_mapping WHERE msgSeq = :msgSeq AND chatType = :chatType")
|
//@Query("SELECT * FROM message_mapping WHERE msgSeq = :msgSeq AND chatType = :chatType")
|
||||||
@ -55,7 +56,7 @@ interface MessageMappingDao {
|
|||||||
// 不要调用这个,seq不唯一啊,老哥!!!!!!!!!!!!!
|
// 不要调用这个,seq不唯一啊,老哥!!!!!!!!!!!!!
|
||||||
// 我就说怎么这么多bug
|
// 我就说怎么这么多bug
|
||||||
|
|
||||||
@Query("SELECT * FROM message_mapping WHERE msgSeq = :msgSeq AND chatType = :chatType AND peerId = :peerId")
|
@Query("SELECT * FROM message_mapping_v2 WHERE msgSeq = :msgSeq AND chatType = :chatType AND peerId = :peerId")
|
||||||
fun queryByMsgSeq(chatType: Int, peerId: String, msgSeq: Int): MessageMapping?
|
fun queryByMsgSeq(chatType: Int, peerId: String, msgSeq: Int): MessageMapping?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ internal abstract class MessageDB: RoomDatabase() {
|
|||||||
abstract fun messageMappingDao(): MessageMappingDao
|
abstract fun messageMappingDao(): MessageMappingDao
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val DB_NAME = "message_mapping.db"
|
private const val DB_NAME = "message_mapping_v2.db"
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var instance: MessageDB? = null
|
private var instance: MessageDB? = null
|
||||||
|
@ -41,7 +41,7 @@ internal object GetForwardMsg: IActionHandler() {
|
|||||||
msg.senderUin, msg.sendNickName
|
msg.senderUin, msg.sendNickName
|
||||||
.ifEmpty { msg.sendMemberName }
|
.ifEmpty { msg.sendMemberName }
|
||||||
.ifEmpty { msg.sendRemarkName }
|
.ifEmpty { msg.sendRemarkName }
|
||||||
.ifEmpty { msg.peerName }, "unknown", 0, msg.senderUid
|
.ifEmpty { msg.peerName }, "unknown", 0, msg.senderUid, msg.senderUid
|
||||||
),
|
),
|
||||||
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
||||||
it.toJson()
|
it.toJson()
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.GProSvc
|
||||||
|
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||||
|
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||||
|
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||||
|
import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty
|
||||||
|
import moe.fuqiuluo.symbols.OneBotHandler
|
||||||
|
|
||||||
|
@OneBotHandler("get_guild_member_profile")
|
||||||
|
internal object GetGuildMemberProfile: IActionHandler() {
|
||||||
|
override suspend fun internalHandle(session: ActionSession): String {
|
||||||
|
val guildId = session.getString("guild_id").toULong()
|
||||||
|
val userId = session.getString("user_id").toULong()
|
||||||
|
return invoke(guildId, userId, session.echo)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend operator fun invoke(guildId: ULong, userId: ULong, echo: JsonElement = EmptyJsonString): String {
|
||||||
|
val userResult = GProSvc.getUserGuildInfo(guildId, userId).onFailure {
|
||||||
|
return error(it.message ?: "unable to fetch guild member info", echo)
|
||||||
|
}.getOrThrow()
|
||||||
|
val roles = GProSvc.fetchGuildMemberRoles(guildId, userId).onFailure {
|
||||||
|
return error(it.message ?: "unable to fetch guild member roles", echo)
|
||||||
|
}.getOrThrow()
|
||||||
|
|
||||||
|
return ok(GetGuildMemberInfo(
|
||||||
|
tinyId = userResult.memberTinyid,
|
||||||
|
nickname = userResult.nickName ?: "",
|
||||||
|
avatarUrl = userResult.url ?: "",
|
||||||
|
joinTime = userResult.joinTime,
|
||||||
|
roles = roles.map {
|
||||||
|
RoleInfo(
|
||||||
|
roleId = it.roleId.toString(),
|
||||||
|
roleName = it.name.ifNullOrEmpty(it.levelDsc.ifNullOrEmpty(it.displayTagName ?: ""))!!,
|
||||||
|
color = it.color,
|
||||||
|
permission = it.rolePermissions.permissionList.map {
|
||||||
|
Permission(
|
||||||
|
rootId = it.rootId,
|
||||||
|
childIds = it.childIds ?: emptyList()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
type = it.type,
|
||||||
|
displayName = it.displayTagName ?: ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
), echo = echo)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val requiredParams: Array<String> = arrayOf("guild_id", "user_id")
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GetGuildMemberInfo(
|
||||||
|
@SerialName("tiny_id") val tinyId: ULong,
|
||||||
|
@SerialName("nickname") val nickname: String,
|
||||||
|
@SerialName("avatar_url") val avatarUrl: String,
|
||||||
|
@SerialName("join_time") val joinTime: ULong,
|
||||||
|
@SerialName("roles") val roles: List<RoleInfo>
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class RoleInfo(
|
||||||
|
@SerialName("role_id") val roleId: String,
|
||||||
|
@SerialName("role_name") val roleName: String,
|
||||||
|
@SerialName("color") val color: Long,
|
||||||
|
@SerialName("permission") val permission: List<Permission>,
|
||||||
|
@SerialName("type") val type: Int,
|
||||||
|
@SerialName("display_name") val displayName: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Permission(
|
||||||
|
@SerialName("root_id") val rootId: Int,
|
||||||
|
@SerialName("child_ids") val childIds: List<Int>
|
||||||
|
)
|
||||||
|
}
|
@ -65,7 +65,7 @@ internal object GetHistoryMsg: IActionHandler() {
|
|||||||
msgId = msgHash,
|
msgId = msgHash,
|
||||||
realId = msg.msgSeq.toInt(),
|
realId = msg.msgSeq.toInt(),
|
||||||
sender = MessageSender(
|
sender = MessageSender(
|
||||||
msg.senderUin, msg.sendNickName, "unknown", 0, msg.senderUid
|
msg.senderUin, msg.sendNickName, "unknown", 0, msg.senderUid, msg.senderUid
|
||||||
),
|
),
|
||||||
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
||||||
it.toJson()
|
it.toJson()
|
||||||
@ -89,7 +89,7 @@ internal object GetHistoryMsg: IActionHandler() {
|
|||||||
msg.senderUin, msg.sendNickName
|
msg.senderUin, msg.sendNickName
|
||||||
.ifEmpty { msg.sendMemberName }
|
.ifEmpty { msg.sendMemberName }
|
||||||
.ifEmpty { msg.sendRemarkName }
|
.ifEmpty { msg.sendRemarkName }
|
||||||
.ifEmpty { msg.peerName }, "unknown", 0, msg.senderUid
|
.ifEmpty { msg.peerName }, "unknown", 0, msg.senderUid, msg.senderUid
|
||||||
),
|
),
|
||||||
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
||||||
it.toJson()
|
it.toJson()
|
||||||
|
@ -34,7 +34,10 @@ internal object GetMsg: IActionHandler() {
|
|||||||
msg.senderUin, msg.sendNickName
|
msg.senderUin, msg.sendNickName
|
||||||
.ifEmpty { msg.sendMemberName }
|
.ifEmpty { msg.sendMemberName }
|
||||||
.ifEmpty { msg.sendRemarkName }
|
.ifEmpty { msg.sendRemarkName }
|
||||||
.ifEmpty { msg.peerName }, "unknown", 0, msg.senderUid
|
.ifEmpty { msg.peerName }, "unknown",
|
||||||
|
0,
|
||||||
|
msg.senderUid,
|
||||||
|
msg.senderUid
|
||||||
),
|
),
|
||||||
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
||||||
it.toJson()
|
it.toJson()
|
||||||
|
@ -0,0 +1,112 @@
|
|||||||
|
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||||
|
|
||||||
|
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import kotlinx.serialization.json.jsonArray
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||||
|
import moe.fuqiuluo.shamrock.helper.Level
|
||||||
|
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||||
|
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||||
|
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||||
|
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||||
|
import moe.fuqiuluo.shamrock.remote.service.data.MessageResult
|
||||||
|
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||||
|
import moe.fuqiuluo.shamrock.tools.json
|
||||||
|
import moe.fuqiuluo.shamrock.tools.jsonArray
|
||||||
|
import moe.fuqiuluo.symbols.OneBotHandler
|
||||||
|
|
||||||
|
@OneBotHandler("send_guild_message", ["send_guild_msg", "send_guild_channel_msg"])
|
||||||
|
internal object SendGuildMessage: IActionHandler() {
|
||||||
|
override suspend fun internalHandle(session: ActionSession): String {
|
||||||
|
val guildId = session.getString("guild_id").toULong()
|
||||||
|
val channelId = session.getString("channel_id").toULong()
|
||||||
|
val retryCnt = session.getIntOrNull("retry_cnt") ?: 3
|
||||||
|
val recallDuration = session.getLongOrNull("recall_duration")
|
||||||
|
return if (session.isString("message")) {
|
||||||
|
val autoEscape = session.getBooleanOrDefault("auto_escape", false)
|
||||||
|
val message = session.getString("message")
|
||||||
|
return invoke(guildId, channelId, message, autoEscape, retryCnt, recallDuration, echo = session.echo)
|
||||||
|
} else if (session.isArray("message")) {
|
||||||
|
val message = session.getArray("message")
|
||||||
|
return invoke(guildId, channelId, message, echo = session.echo, retryCnt = retryCnt, recallDuration = recallDuration)
|
||||||
|
} else {
|
||||||
|
val message = session.getObject("message")
|
||||||
|
invoke(guildId, channelId, listOf(message).jsonArray, session.echo, retryCnt, recallDuration = recallDuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend operator fun invoke(
|
||||||
|
guildId: ULong,
|
||||||
|
channelId: ULong,
|
||||||
|
message: String,
|
||||||
|
autoEscape: Boolean,
|
||||||
|
retryCnt: Int,
|
||||||
|
recallDuration: Long?,
|
||||||
|
echo: JsonElement = EmptyJsonString
|
||||||
|
): String {
|
||||||
|
val result = if (autoEscape) {
|
||||||
|
MsgSvc.sendToAio(MsgConstant.KCHATTYPEGUILD, guildId.toString(), listOf(
|
||||||
|
mapOf(
|
||||||
|
"type" to "text",
|
||||||
|
"data" to mapOf(
|
||||||
|
"text" to message
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).json, fromId = channelId.toString(), retryCnt)
|
||||||
|
} else {
|
||||||
|
val msg = MessageHelper.decodeCQCode(message)
|
||||||
|
if (msg.isEmpty()) {
|
||||||
|
LogCenter.log("CQ码不合法", Level.WARN)
|
||||||
|
return logic("CQCode is illegal", echo)
|
||||||
|
} else {
|
||||||
|
MsgSvc.sendToAio(MsgConstant.KCHATTYPEGUILD, guildId.toString(), msg, fromId = channelId.toString(), retryCnt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.isFailure) {
|
||||||
|
return logic(result.exceptionOrNull()?.message ?: "", echo)
|
||||||
|
}
|
||||||
|
val sendMsgResult = result.getOrThrow()
|
||||||
|
if (sendMsgResult.msgHashId <= 0) {
|
||||||
|
return logic("send message failed", echo = echo)
|
||||||
|
}
|
||||||
|
recallDuration?.let { autoRecall(sendMsgResult.msgHashId, it) }
|
||||||
|
return ok(
|
||||||
|
MessageResult(
|
||||||
|
msgId = sendMsgResult.msgHashId,
|
||||||
|
time = (sendMsgResult.msgTime * 0.001).toLong()
|
||||||
|
), echo = echo)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend operator fun invoke(
|
||||||
|
guildId: ULong, channelId: ULong, message: JsonArray, echo: JsonElement = EmptyJsonString, retryCnt: Int, recallDuration: Long?,
|
||||||
|
): String {
|
||||||
|
val result = MsgSvc.sendToAio(MsgConstant.KCHATTYPEGUILD, guildId.toString(), message, fromId = channelId.toString(), retryCnt)
|
||||||
|
if (result.isFailure) {
|
||||||
|
return logic(result.exceptionOrNull()?.message ?: "", echo)
|
||||||
|
}
|
||||||
|
val sendMsgResult = result.getOrThrow()
|
||||||
|
if (sendMsgResult.msgHashId <= 0) {
|
||||||
|
return logic("send message failed", echo = echo)
|
||||||
|
}
|
||||||
|
recallDuration?.let { autoRecall(sendMsgResult.msgHashId, it) }
|
||||||
|
return ok(MessageResult(
|
||||||
|
msgId = sendMsgResult.msgHashId,
|
||||||
|
time = (sendMsgResult.msgTime * 0.001).toLong()
|
||||||
|
), echo)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val requiredParams: Array<String> = arrayOf("guild_id", "channel_id", "message")
|
||||||
|
|
||||||
|
private fun autoRecall(msgHash: Int, duration: Long) {
|
||||||
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
delay(duration)
|
||||||
|
MsgSvc.recallMsg(msgHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -81,9 +81,6 @@ internal object SendMessage: IActionHandler() {
|
|||||||
recallDuration: Long?,
|
recallDuration: Long?,
|
||||||
echo: JsonElement = EmptyJsonString
|
echo: JsonElement = EmptyJsonString
|
||||||
): String {
|
): String {
|
||||||
//if (!ContactHelper.checkContactAvailable(chatType, peerId)) {
|
|
||||||
// return logic("contact is not found", echo = echo)
|
|
||||||
//}
|
|
||||||
val result = if (autoEscape) {
|
val result = if (autoEscape) {
|
||||||
MsgSvc.sendToAio(chatType, peerId, listOf(
|
MsgSvc.sendToAio(chatType, peerId, listOf(
|
||||||
mapOf(
|
mapOf(
|
||||||
|
@ -4,15 +4,31 @@ import io.ktor.http.ContentType
|
|||||||
import io.ktor.server.application.call
|
import io.ktor.server.application.call
|
||||||
import io.ktor.server.response.respondText
|
import io.ktor.server.response.respondText
|
||||||
import io.ktor.server.routing.Routing
|
import io.ktor.server.routing.Routing
|
||||||
|
import io.ktor.server.routing.get
|
||||||
|
import io.ktor.server.routing.post
|
||||||
|
import io.ktor.server.routing.route
|
||||||
|
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||||
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGProChannelList
|
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGProChannelList
|
||||||
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildList
|
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildList
|
||||||
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildMemberList
|
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildMemberList
|
||||||
|
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildMemberProfile
|
||||||
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildMetaByGuest
|
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildMetaByGuest
|
||||||
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildServiceProfile
|
import moe.fuqiuluo.shamrock.remote.action.handlers.GetGuildServiceProfile
|
||||||
|
import moe.fuqiuluo.shamrock.remote.action.handlers.SendGuildMessage
|
||||||
|
import moe.fuqiuluo.shamrock.remote.action.handlers.SendMessage
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchGetOrNull
|
import moe.fuqiuluo.shamrock.tools.fetchGetOrNull
|
||||||
|
import moe.fuqiuluo.shamrock.tools.fetchGetOrThrow
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchOrNull
|
import moe.fuqiuluo.shamrock.tools.fetchOrNull
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchOrThrow
|
import moe.fuqiuluo.shamrock.tools.fetchOrThrow
|
||||||
|
import moe.fuqiuluo.shamrock.tools.fetchPostJsonArray
|
||||||
|
import moe.fuqiuluo.shamrock.tools.fetchPostJsonObject
|
||||||
|
import moe.fuqiuluo.shamrock.tools.fetchPostOrNull
|
||||||
|
import moe.fuqiuluo.shamrock.tools.fetchPostOrThrow
|
||||||
import moe.fuqiuluo.shamrock.tools.getOrPost
|
import moe.fuqiuluo.shamrock.tools.getOrPost
|
||||||
|
import moe.fuqiuluo.shamrock.tools.isJsonData
|
||||||
|
import moe.fuqiuluo.shamrock.tools.isJsonObject
|
||||||
|
import moe.fuqiuluo.shamrock.tools.isJsonString
|
||||||
|
import moe.fuqiuluo.shamrock.tools.jsonArray
|
||||||
|
|
||||||
fun Routing.guildAction() {
|
fun Routing.guildAction() {
|
||||||
getOrPost("/get_guild_service_profile") {
|
getOrPost("/get_guild_service_profile") {
|
||||||
@ -40,4 +56,57 @@ fun Routing.guildAction() {
|
|||||||
val refresh = fetchGetOrNull("refresh") ?: fetchOrNull("no_cache")
|
val refresh = fetchGetOrNull("refresh") ?: fetchOrNull("no_cache")
|
||||||
call.respondText(GetGProChannelList(guildId.toULong(), refresh?.toBoolean() ?: false), ContentType.Application.Json)
|
call.respondText(GetGProChannelList(guildId.toULong(), refresh?.toBoolean() ?: false), ContentType.Application.Json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOrPost("/get_guild_member_profile") {
|
||||||
|
val guildId = fetchOrThrow("guild_id")
|
||||||
|
val userId = fetchOrThrow("user_id")
|
||||||
|
call.respondText(GetGuildMemberProfile(guildId.toULong(), userId.toULong()), ContentType.Application.Json)
|
||||||
|
}
|
||||||
|
|
||||||
|
route("/(send_guild_channel_msg|send_guild_message|send_guild_msg)".toRegex()) {
|
||||||
|
get {
|
||||||
|
val guildId = fetchGetOrThrow("guild_id").toULong()
|
||||||
|
val channelId = fetchGetOrThrow("channel_id").toULong()
|
||||||
|
val message = fetchGetOrThrow("message")
|
||||||
|
val autoEscape = fetchGetOrNull("auto_escape")?.toBoolean() ?: false
|
||||||
|
val retryCnt = fetchGetOrNull("retry_cnt")?.toInt() ?: 3
|
||||||
|
val recallDuration = fetchGetOrNull("recall_duration")?.toLong()
|
||||||
|
call.respondText(SendGuildMessage(guildId, channelId, message, autoEscape, retryCnt, recallDuration), ContentType.Application.Json)
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
val retryCnt = fetchOrNull("retry_cnt")?.toInt() ?: 3
|
||||||
|
val recallDuration = fetchOrNull("recall_duration")?.toLong()
|
||||||
|
val guildId = fetchOrThrow("guild_id").toULong()
|
||||||
|
val channelId = fetchOrThrow("channel_id").toULong()
|
||||||
|
call.respondText(if (isJsonData() && !isJsonString("message")) {
|
||||||
|
if (isJsonObject("message")) {
|
||||||
|
SendGuildMessage(
|
||||||
|
guildId = guildId,
|
||||||
|
channelId = channelId,
|
||||||
|
message = listOf(fetchPostJsonObject("message")).jsonArray,
|
||||||
|
retryCnt = retryCnt,
|
||||||
|
recallDuration = recallDuration
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
SendGuildMessage(
|
||||||
|
guildId = guildId,
|
||||||
|
channelId = channelId,
|
||||||
|
message = fetchPostJsonArray("message"),
|
||||||
|
retryCnt = retryCnt,
|
||||||
|
recallDuration = recallDuration
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val autoEscape = fetchPostOrNull("auto_escape")?.toBooleanStrict() ?: false
|
||||||
|
SendGuildMessage(
|
||||||
|
guildId = guildId,
|
||||||
|
channelId = channelId,
|
||||||
|
message = fetchOrThrow("message"),
|
||||||
|
autoEscape = autoEscape,
|
||||||
|
retryCnt = retryCnt,
|
||||||
|
recallDuration = recallDuration
|
||||||
|
)
|
||||||
|
}, ContentType.Application.Json)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -74,7 +74,7 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
peerId = uin,
|
peerId = uin,
|
||||||
userId = record.senderUin,
|
userId = record.senderUin,
|
||||||
message = if(ShamrockConfig.useCQ()) rawMsg.json
|
message = if(ShamrockConfig.useCQ()) rawMsg.json
|
||||||
else elements.toSegments(record.chatType, record.peerUin.toString()).map {
|
else elements.toSegments(record.chatType, record.peerUin.toString(), "0").map {
|
||||||
it.toJson()
|
it.toJson()
|
||||||
}.json,
|
}.json,
|
||||||
rawMessage = rawMsg,
|
rawMessage = rawMsg,
|
||||||
@ -129,7 +129,7 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
peerId = botUin,
|
peerId = botUin,
|
||||||
userId = record.senderUin,
|
userId = record.senderUin,
|
||||||
message = if(ShamrockConfig.useCQ()) rawMsg.json
|
message = if(ShamrockConfig.useCQ()) rawMsg.json
|
||||||
else elements.toSegments(record.chatType, record.peerUin.toString()).map {
|
else elements.toSegments(record.chatType, record.peerUin.toString(), "0").map {
|
||||||
it.toJson()
|
it.toJson()
|
||||||
}.json,
|
}.json,
|
||||||
rawMessage = rawMsg,
|
rawMessage = rawMsg,
|
||||||
@ -147,6 +147,54 @@ internal object GlobalEventTransmitter: BaseSvc() {
|
|||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送私聊消息
|
||||||
|
*/
|
||||||
|
suspend fun transGuildMessage(
|
||||||
|
record: MsgRecord,
|
||||||
|
elements: ArrayList<MsgElement>,
|
||||||
|
rawMsg: String,
|
||||||
|
msgHash: Int,
|
||||||
|
postType: PostType,
|
||||||
|
): Boolean {
|
||||||
|
val botUin = app.longAccountUin
|
||||||
|
var nickName = record.sendNickName
|
||||||
|
if (nickName.isNullOrEmpty()) {
|
||||||
|
CardSvc.getProfileCard(record.senderUin.toString()).onSuccess {
|
||||||
|
nickName = it.strNick ?: record.peerName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transMessageEvent(record,
|
||||||
|
MessageEvent(
|
||||||
|
time = record.msgTime,
|
||||||
|
selfId = botUin,
|
||||||
|
postType = postType,
|
||||||
|
messageType = MsgType.Guild,
|
||||||
|
subType = MsgSubType.Channel,
|
||||||
|
messageId = msgHash,
|
||||||
|
targetId = record.peerUin,
|
||||||
|
peerId = botUin,
|
||||||
|
userId = record.senderUid.toLong(),
|
||||||
|
message = if(ShamrockConfig.useCQ()) rawMsg.json
|
||||||
|
else elements.toSegments(record.chatType, record.guildId, record.channelId).map {
|
||||||
|
it.toJson()
|
||||||
|
}.json,
|
||||||
|
rawMessage = rawMsg,
|
||||||
|
font = 0,
|
||||||
|
sender = Sender(
|
||||||
|
userId = record.senderUid.toLong(),
|
||||||
|
nickname = nickName,
|
||||||
|
card = record.sendMemberName,
|
||||||
|
role = MemberRole.Member,
|
||||||
|
title = record.sendNickName,
|
||||||
|
level = record.roleId.toString(),
|
||||||
|
tinyId = record.senderUid
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,19 +39,46 @@ internal object ShamrockConfig {
|
|||||||
fun updateConfig(intent: Intent) {
|
fun updateConfig(intent: Intent) {
|
||||||
val mmkv = MMKVFetcher.mmkvWithId("shamrock_config")
|
val mmkv = MMKVFetcher.mmkvWithId("shamrock_config")
|
||||||
mmkv.apply {
|
mmkv.apply {
|
||||||
putBoolean( "tablet", intent.getBooleanExtra("tablet", false)) // 强制平板模式
|
if (!intent.getBooleanExtra("disable_auto_sync_setting", false)) {
|
||||||
putInt( "port", intent.getIntExtra("port", 5700)) // 主动HTTP端口
|
putBoolean(
|
||||||
putBoolean( "ws", intent.getBooleanExtra("ws", false)) // 主动WS开关
|
"tablet",
|
||||||
putBoolean( "http", intent.getBooleanExtra("http", false)) // HTTP回调开关
|
intent.getBooleanExtra("tablet", false)
|
||||||
putString( "http_addr", intent.getStringExtra("http_addr")) // WebHook回调地址
|
) // 强制平板模式
|
||||||
putBoolean( "ws_client", intent.getBooleanExtra("ws_client", false)) // 被动WS开关
|
putInt("port", intent.getIntExtra("port", 5700)) // 主动HTTP端口
|
||||||
putBoolean( "use_cqcode", intent.getBooleanExtra("use_cqcode", false)) // 使用CQ码
|
putBoolean("ws", intent.getBooleanExtra("ws", false)) // 主动WS开关
|
||||||
putBoolean( "inject_packet", intent.getBooleanExtra("inject_packet", false)) // 拦截无用包
|
putBoolean(
|
||||||
putBoolean( "debug", intent.getBooleanExtra("debug", false)) // 调试模式
|
"http",
|
||||||
|
intent.getBooleanExtra("http", false)
|
||||||
|
) // HTTP回调开关
|
||||||
|
putString(
|
||||||
|
"http_addr",
|
||||||
|
intent.getStringExtra("http_addr")
|
||||||
|
) // WebHook回调地址
|
||||||
|
putBoolean(
|
||||||
|
"ws_client",
|
||||||
|
intent.getBooleanExtra("ws_client", false)
|
||||||
|
) // 被动WS开关
|
||||||
|
putBoolean(
|
||||||
|
"use_cqcode",
|
||||||
|
intent.getBooleanExtra("use_cqcode", false)
|
||||||
|
) // 使用CQ码
|
||||||
|
putBoolean(
|
||||||
|
"inject_packet",
|
||||||
|
intent.getBooleanExtra("inject_packet", false)
|
||||||
|
) // 拦截无用包
|
||||||
|
putBoolean("debug", intent.getBooleanExtra("debug", false)) // 调试模式
|
||||||
|
putString( "key_store", intent.getStringExtra("key_store")) // 证书路径
|
||||||
|
putString( "ssl_pwd", intent.getStringExtra("ssl_pwd")) // 证书密码
|
||||||
|
putString( "ssl_private_pwd", intent.getStringExtra("ssl_private_pwd")) // 证书私钥密码
|
||||||
|
putString( "ssl_alias", intent.getStringExtra("ssl_alias")) // 证书别名
|
||||||
|
putInt( "ssl_port", intent.getIntExtra("ssl_port", 5701)) // 主动HTTP端口
|
||||||
|
putBoolean("alive_reply", intent.getBooleanExtra("alive_reply", false)) // 自回复测试
|
||||||
|
putBoolean("enable_self_msg", intent.getBooleanExtra("enable_self_msg", false)) // 推送自己发的消息
|
||||||
|
putBoolean("shell", intent.getBooleanExtra("shell", false)) // 开启Shell接口
|
||||||
|
putBoolean("enable_sync_msg_as_sent_msg", intent.getBooleanExtra("enable_sync_msg_as_sent_msg", false)) // 推送同步消息
|
||||||
|
}
|
||||||
Config.defaultToken = intent.getStringExtra("token")
|
Config.defaultToken = intent.getStringExtra("token")
|
||||||
Config.antiTrace = intent.getBooleanExtra("anti_qq_trace", true)
|
Config.antiTrace = intent.getBooleanExtra("anti_qq_trace", true)
|
||||||
|
|
||||||
val wsPort = intent.getIntExtra("ws_port", 5800)
|
val wsPort = intent.getIntExtra("ws_port", 5800)
|
||||||
Config.activeWebSocket = if (Config.activeWebSocket == null) ConnectionConfig(
|
Config.activeWebSocket = if (Config.activeWebSocket == null) ConnectionConfig(
|
||||||
address = "0.0.0.0",
|
address = "0.0.0.0",
|
||||||
@ -59,28 +86,17 @@ internal object ShamrockConfig {
|
|||||||
) else Config.activeWebSocket?.also {
|
) else Config.activeWebSocket?.also {
|
||||||
it.port = wsPort
|
it.port = wsPort
|
||||||
}
|
}
|
||||||
|
|
||||||
Config.passiveWebSocket = intent.getStringExtra("ws_addr")?.split(",", "|", ",")?.filter { address ->
|
Config.passiveWebSocket = intent.getStringExtra("ws_addr")?.split(",", "|", ",")?.filter { address ->
|
||||||
address.isNotBlank() && (address.startsWith("ws://") || address.startsWith("wss://"))
|
address.isNotBlank() && (address.startsWith("ws://") || address.startsWith("wss://"))
|
||||||
}?.map {
|
}?.map {
|
||||||
ConnectionConfig(address = it)
|
ConnectionConfig(address = it)
|
||||||
}?.toMutableList()
|
}?.toMutableList()
|
||||||
|
|
||||||
putString( "key_store", intent.getStringExtra("key_store")) // 证书路径
|
|
||||||
putString( "ssl_pwd", intent.getStringExtra("ssl_pwd")) // 证书密码
|
|
||||||
putString( "ssl_private_pwd", intent.getStringExtra("ssl_private_pwd")) // 证书私钥密码
|
|
||||||
putString( "ssl_alias", intent.getStringExtra("ssl_alias")) // 证书别名
|
|
||||||
putInt( "ssl_port", intent.getIntExtra("ssl_port", 5701)) // 主动HTTP端口
|
|
||||||
|
|
||||||
putBoolean("alive_reply", intent.getBooleanExtra("alive_reply", false)) // 自回复测试
|
|
||||||
|
|
||||||
putBoolean("enable_self_msg", intent.getBooleanExtra("enable_self_msg", false)) // 推送自己发的消息
|
|
||||||
putBoolean("shell", intent.getBooleanExtra("shell", false)) // 开启Shell接口
|
|
||||||
putBoolean("enable_sync_msg_as_sent_msg", intent.getBooleanExtra("enable_sync_msg_as_sent_msg", false)) // 推送同步消息
|
|
||||||
|
|
||||||
putBoolean("isInit", true)
|
putBoolean("isInit", true)
|
||||||
}
|
}
|
||||||
updateConfig()
|
if (!intent.getBooleanExtra("disable_auto_sync_setting", false)) {
|
||||||
|
updateConfig()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val mmkv: MMKV
|
private val mmkv: MMKV
|
||||||
|
@ -35,6 +35,7 @@ internal data class MessageSender(
|
|||||||
@SerialName("sex") val sex: String,
|
@SerialName("sex") val sex: String,
|
||||||
@SerialName("age") val age: Int,
|
@SerialName("age") val age: Int,
|
||||||
@SerialName("uid") val uid: String,
|
@SerialName("uid") val uid: String,
|
||||||
|
@SerialName("tiny_id") val tinyId: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -21,12 +21,15 @@ internal enum class MsgSubType {
|
|||||||
@SerialName("group") GroupLess,
|
@SerialName("group") GroupLess,
|
||||||
@SerialName("friend") Friend,
|
@SerialName("friend") Friend,
|
||||||
@SerialName("other") Other,
|
@SerialName("other") Other,
|
||||||
|
|
||||||
|
@SerialName("channel") Channel
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal enum class MsgType {
|
internal enum class MsgType {
|
||||||
@SerialName("group") Group,
|
@SerialName("group") Group,
|
||||||
@SerialName("private") Private
|
@SerialName("private") Private,
|
||||||
|
@SerialName("guild") Guild
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -94,4 +97,5 @@ internal data class Sender(
|
|||||||
@SerialName("role") val role: MemberRole?,
|
@SerialName("role") val role: MemberRole?,
|
||||||
@SerialName("title") val title: String,
|
@SerialName("title") val title: String,
|
||||||
@SerialName("level") val level: String,
|
@SerialName("level") val level: String,
|
||||||
|
@SerialName("tiny_id") val tinyId: String = "0",
|
||||||
)
|
)
|
@ -38,8 +38,6 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
|
|
||||||
private suspend fun handleMsg(record: MsgRecord) {
|
private suspend fun handleMsg(record: MsgRecord) {
|
||||||
try {
|
try {
|
||||||
if (record.chatType == MsgConstant.KCHATTYPEGUILD) return // TODO: 频道消息暂不处理
|
|
||||||
|
|
||||||
messageLessListenerMap.firstNotNullOfOrNull {
|
messageLessListenerMap.firstNotNullOfOrNull {
|
||||||
if (it.key == record.msgSeq) it else null
|
if (it.key == record.msgSeq) it else null
|
||||||
}?.let {
|
}?.let {
|
||||||
@ -50,17 +48,22 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
|
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
||||||
|
|
||||||
|
val peerId = when(record.chatType) {
|
||||||
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
|
else -> record.peerUin.toString()
|
||||||
|
}
|
||||||
MessageHelper.saveMsgMapping(
|
MessageHelper.saveMsgMapping(
|
||||||
hash = msgHash,
|
hash = msgHash,
|
||||||
qqMsgId = record.msgId,
|
qqMsgId = record.msgId,
|
||||||
chatType = record.chatType,
|
chatType = record.chatType,
|
||||||
subChatType = record.chatType,
|
subChatType = record.chatType,
|
||||||
peerId = record.peerUin.toString(),
|
peerId = peerId,
|
||||||
msgSeq = record.msgSeq.toInt(),
|
msgSeq = record.msgSeq.toInt(),
|
||||||
time = record.msgTime
|
time = record.msgTime,
|
||||||
|
subPeerId = record.channelId ?: peerId
|
||||||
)
|
)
|
||||||
|
|
||||||
val rawMsg = record.elements.toCQCode(record.chatType, record.peerUin.toString())
|
val rawMsg = record.elements.toCQCode(record.chatType, peerId, record.channelId ?: peerId)
|
||||||
if (rawMsg.isEmpty()) return
|
if (rawMsg.isEmpty()) return
|
||||||
|
|
||||||
if (ShamrockConfig.aliveReply() && rawMsg == "ping") {
|
if (ShamrockConfig.aliveReply() && rawMsg == "ping") {
|
||||||
@ -119,6 +122,16 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
LogCenter.log("私聊临时消息推送失败 -> MessageTransmitter", Level.WARN)
|
LogCenter.log("私聊临时消息推送失败 -> MessageTransmitter", Level.WARN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MsgConstant.KCHATTYPEGUILD -> {
|
||||||
|
LogCenter.log("频道消息(guildId = ${record.guildId}, sender=${record.senderUid}, id = [$msgHash | ${record.msgId}], msg = $rawMsg)")
|
||||||
|
if(!GlobalEventTransmitter.MessageTransmitter
|
||||||
|
.transGuildMessage(record, record.elements, rawMsg, msgHash, postType = postType)
|
||||||
|
) {
|
||||||
|
LogCenter.log("频道消息推送失败 -> MessageTransmitter", Level.WARN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> LogCenter.log("不支持PUSH事件: ${record.chatType}")
|
else -> LogCenter.log("不支持PUSH事件: ${record.chatType}")
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
@ -131,21 +144,25 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onAddSendMsg(record: MsgRecord) {
|
override fun onAddSendMsg(record: MsgRecord) {
|
||||||
if (record.chatType == MsgConstant.KCHATTYPEGUILD) return // TODO: 频道消息暂不处理
|
|
||||||
if (record.peerUin == TicketSvc.getLongUin()) return // 发给自己的消息不处理
|
if (record.peerUin == TicketSvc.getLongUin()) return // 发给自己的消息不处理
|
||||||
|
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
try {
|
try {
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
||||||
|
|
||||||
|
val peerId = when(record.chatType) {
|
||||||
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
|
else -> record.peerUin.toString()
|
||||||
|
}
|
||||||
MessageHelper.saveMsgMapping(
|
MessageHelper.saveMsgMapping(
|
||||||
hash = msgHash,
|
hash = msgHash,
|
||||||
qqMsgId = record.msgId,
|
qqMsgId = record.msgId,
|
||||||
chatType = record.chatType,
|
chatType = record.chatType,
|
||||||
subChatType = record.chatType,
|
subChatType = record.chatType,
|
||||||
peerId = record.peerUin.toString(),
|
peerId = peerId,
|
||||||
msgSeq = record.msgSeq.toInt(),
|
msgSeq = record.msgSeq.toInt(),
|
||||||
time = record.msgTime
|
time = record.msgTime,
|
||||||
|
subPeerId = record.channelId ?: peerId
|
||||||
)
|
)
|
||||||
|
|
||||||
LogCenter.log("预发送消息($msgHash | ${record.msgSeq} | ${record.msgId})")
|
LogCenter.log("预发送消息($msgHash | ${record.msgSeq} | ${record.msgId})")
|
||||||
@ -167,6 +184,10 @@ 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) {
|
||||||
|
MsgConstant.KCHATTYPEGUILD -> record.guildId
|
||||||
|
else -> record.peerUin.toString()
|
||||||
|
}
|
||||||
|
|
||||||
val mapping = MessageHelper.getMsgMappingByHash(msgHash)
|
val mapping = MessageHelper.getMsgMappingByHash(msgHash)
|
||||||
if (mapping == null) {
|
if (mapping == null) {
|
||||||
@ -175,9 +196,10 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
qqMsgId = record.msgId,
|
qqMsgId = record.msgId,
|
||||||
chatType = record.chatType,
|
chatType = record.chatType,
|
||||||
subChatType = record.chatType,
|
subChatType = record.chatType,
|
||||||
peerId = record.peerUin.toString(),
|
peerId = peerId,
|
||||||
msgSeq = record.msgSeq.toInt(),
|
msgSeq = record.msgSeq.toInt(),
|
||||||
time = record.msgTime
|
time = record.msgTime,
|
||||||
|
subPeerId = record.channelId ?: peerId
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
LogCenter.log("Update message info from ${mapping.msgSeq} to ${record.msgSeq}", Level.INFO)
|
LogCenter.log("Update message info from ${mapping.msgSeq} to ${record.msgSeq}", Level.INFO)
|
||||||
@ -190,7 +212,7 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
|| record.peerUin == TicketSvc.getLongUin()
|
|| record.peerUin == TicketSvc.getLongUin()
|
||||||
) return@launch
|
) return@launch
|
||||||
|
|
||||||
val rawMsg = record.elements.toCQCode(record.chatType, record.peerUin.toString())
|
val rawMsg = record.elements.toCQCode(record.chatType, peerId, record.channelId ?: peerId)
|
||||||
if (rawMsg.isEmpty()) return@launch
|
if (rawMsg.isEmpty()) return@launch
|
||||||
LogCenter.log("自发消息(target = ${record.peerUin}, id = $msgHash, msg = $rawMsg)")
|
LogCenter.log("自发消息(target = ${record.peerUin}, id = $msgHash, msg = $rawMsg)")
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user