ShamrockPublic: アクティブな一時チャットを許可する

Signed-off-by: WhiteChi <whitechi73@outlook.com>
This commit is contained in:
WhiteChi 2023-11-03 15:12:06 +08:00
parent c30e3db1a1
commit 259de3d3aa
10 changed files with 122 additions and 24 deletions

View File

@ -0,0 +1,28 @@
package com.tencent.mobileqq.troop.api;
import mqq.app.api.IRuntimeService;
import com.tencent.mobileqq.data.troop.TroopMemberInfo;
public interface ITroopMemberNameService extends IRuntimeService {
String getTroopMemberColorNick(String str, String str2);
String getTroopMemberName(TroopMemberInfo troopMemberInfo);
String getTroopMemberName(String str, String str2);
String getTroopMemberName(String str, String str2, String str3, String str4);
String getTroopMemberName(String str, String str2, boolean z, boolean z2);
//void getTroopMemberNameAsync(String str, String str2, a aVar);
String getTroopMemberNameInUI(String str, String str2);
String getTroopMemberNameRemarkFirst(String str, String str2);
String getTroopMemberNameWithoutRemark(String str, String str2);
String getTroopMemberNick(String str, String str2);
String getTroopMemberNickByTroopCode(String str, String str2);
}

View File

@ -1,7 +1,9 @@
package com.tencent.qqnt.kernel.api.impl; package com.tencent.qqnt.kernel.api.impl;
import com.tencent.qqnt.kernel.nativeinterface.IKernelMsgListener; import com.tencent.qqnt.kernel.nativeinterface.IKernelMsgListener;
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback;
import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo; import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo;
import com.tencent.qqnt.kernel.nativeinterface.TempChatPrepareInfo;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -18,4 +20,8 @@ public class MsgService {
public String getRichMediaFilePathForMobileQQSend(@NotNull RichMediaFilePathInfo richMediaFilePathInfo) { public String getRichMediaFilePathForMobileQQSend(@NotNull RichMediaFilePathInfo richMediaFilePathInfo) {
return null; return null;
} }
public void prepareTempChat(TempChatPrepareInfo tempChatPrepareInfo, IOperateCallback cb) {
}
} }

View File

@ -56,20 +56,14 @@ public final class TempChatPrepareInfo {
return "TempChatPrepareInfo{chatType=" + this.chatType + ",peerUid=" + this.peerUid + ",peerNickname=" + this.peerNickname + ",fromGroupCode=" + this.fromGroupCode + ",sig=" + this.sig + ",selfUid=" + this.selfUid + ",selfPhone=" + this.selfPhone + ",gameSession=" + this.gameSession + ",}"; return "TempChatPrepareInfo{chatType=" + this.chatType + ",peerUid=" + this.peerUid + ",peerNickname=" + this.peerNickname + ",fromGroupCode=" + this.fromGroupCode + ",sig=" + this.sig + ",selfUid=" + this.selfUid + ",selfPhone=" + this.selfPhone + ",gameSession=" + this.gameSession + ",}";
} }
public TempChatPrepareInfo(int i2, String str, String str2, String str3, byte[] bArr, String str4, String str5, TempChatGameSession tempChatGameSession) { public TempChatPrepareInfo(int chatType, String peerId, String nickName, String fromGroup, byte[] sig, String selfUid, String selfPhone, TempChatGameSession session) {
this.peerUid = ""; this.chatType = chatType;
this.peerNickname = ""; this.peerUid = peerId;
this.fromGroupCode = ""; this.peerNickname = nickName;
this.sig = new byte[0]; this.fromGroupCode = fromGroup;
this.selfUid = ""; this.sig = sig;
this.selfPhone = ""; this.selfUid = selfUid;
this.chatType = i2; this.selfPhone = selfPhone;
this.peerUid = str; this.gameSession = session;
this.peerNickname = str2;
this.fromGroupCode = str3;
this.sig = bArr;
this.selfUid = str4;
this.selfPhone = str5;
this.gameSession = tempChatGameSession;
} }
} }

View File

@ -81,6 +81,10 @@ public abstract class AppRuntime {
return !"0".equals(getCurrentAccountUin()) ? getCurrentAccountUin() : ""; return !"0".equals(getCurrentAccountUin()) ? getCurrentAccountUin() : "";
} }
public String getCurrentUid() {
return "";
}
public long getLongAccountUin() { public long getLongAccountUin() {
return 0; return 0;
} }

View File

@ -1,15 +1,24 @@
package moe.fuqiuluo.qqinterface.servlet package moe.fuqiuluo.qqinterface.servlet
import com.tencent.mobileqq.qroute.QRoute import com.tencent.mobileqq.qroute.QRoute
import com.tencent.mobileqq.troop.api.ITroopMemberNameService
import com.tencent.qqnt.kernel.api.IKernelService
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.kernel.nativeinterface.TempChatGameSession
import com.tencent.qqnt.kernel.nativeinterface.TempChatPrepareInfo
import com.tencent.qqnt.msg.api.IMsgService import com.tencent.qqnt.msg.api.IMsgService
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonArray
import moe.fuqiuluo.shamrock.helper.MessageHelper import moe.fuqiuluo.shamrock.helper.ContactHelper
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import moe.fuqiuluo.shamrock.xposed.helper.msgService
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
@ -18,6 +27,27 @@ internal object MsgSvc: BaseSvc() {
return Result.failure(Exception("Not implemented")) return Result.failure(Exception("Not implemented"))
} }
suspend fun prepareTempChatFromGroup(
groupId: String,
peerId: String
): Result<Unit> {
LogCenter.log("主动临时消息,创建临时会话。", Level.INFO)
val msgService = app.getRuntimeService(IKernelService::class.java, "all").msgService
?: return Result.failure(Exception("获取消息服务失败"))
msgService.prepareTempChat(TempChatPrepareInfo(
MsgConstant.KCHATTYPETEMPC2CFROMGROUP,
ContactHelper.getUidByUinAsync(peerId = peerId.toLong()),
app.getRuntimeService(ITroopMemberNameService::class.java, "all")
.getTroopMemberNameRemarkFirst(groupId, peerId),
groupId, EMPTY_BYTE_ARRAY, app.currentUid, "", TempChatGameSession()
)) { code, reason ->
if (code != 0) {
LogCenter.log("临时会话创建失败: $code, $reason", Level.ERROR)
}
}
return Result.success(Unit)
}
/** /**
* 正常获取 * 正常获取
*/ */
@ -139,6 +169,17 @@ internal object MsgSvc: BaseSvc() {
): Pair<Long, Int> { ): Pair<Long, Int> {
//LogCenter.log(message.toString(), Level.ERROR) //LogCenter.log(message.toString(), Level.ERROR)
//callback.msgHash = result.second 什么垃圾代码万一cb比你快你不就寄了 //callback.msgHash = result.second 什么垃圾代码万一cb比你快你不就寄了
// 主动临时消息
when(chatType) {
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> {
prepareTempChatFromGroup(fromId, peedId).onFailure {
LogCenter.log("主动临时消息,创建临时会话失败。", Level.ERROR)
return -1L to 0
}
}
}
return MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, MessageCallback(peedId, 0), fromId) return MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, MessageCallback(peedId, 0), fromId)
} }

View File

@ -44,6 +44,7 @@ internal object MessageHelper {
if(callback is MsgSvc.MessageCallback) { if(callback is MsgSvc.MessageCallback) {
callback.msgHash = uniseq.first callback.msgHash = uniseq.first
} }
service.sendMsg( service.sendMsg(
generateContact(chatType, peerId, fromId), generateContact(chatType, peerId, fromId),
uniseq.second, uniseq.second,

View File

@ -81,10 +81,13 @@ internal object SendMessage: IActionHandler() {
MsgSvc.sendToAio(chatType, peerId, msg, fromId = fromId) MsgSvc.sendToAio(chatType, peerId, msg, fromId = fromId)
} }
} }
if (result.first <= 0) {
return logic("send message failed", echo = echo)
}
return ok(MessageResult( return ok(MessageResult(
msgId = result.second, msgId = result.second,
time = result.first * 0.001 time = result.first * 0.001
), echo) ), echo = echo)
} }
// 消息段格式消息 // 消息段格式消息
@ -95,6 +98,9 @@ internal object SendMessage: IActionHandler() {
// return logic("contact is not found", echo = echo) // return logic("contact is not found", echo = echo)
//} //}
val result = MsgSvc.sendToAio(chatType, peerId, message, fromId = fromId) val result = MsgSvc.sendToAio(chatType, peerId, message, fromId = fromId)
if (result.first <= 0) {
return logic("send message failed", echo = echo)
}
return ok(MessageResult( return ok(MessageResult(
msgId = result.second, msgId = result.second,
time = result.first * 0.001 time = result.first * 0.001

View File

@ -12,10 +12,23 @@ internal object SendPrivateMessage: IActionHandler() {
return if (session.isString("message")) { return if (session.isString("message")) {
val autoEscape = session.getBooleanOrDefault("auto_escape", false) val autoEscape = session.getBooleanOrDefault("auto_escape", false)
val message = session.getString("message") val message = session.getString("message")
SendMessage(chatTYpe, userId, message, autoEscape, echo = session.echo, fromId = groupId ?: userId) SendMessage.invoke(
chatType = chatTYpe,
peerId = userId,
message = message,
autoEscape = autoEscape,
echo = session.echo,
fromId = groupId ?: userId
)
} else { } else {
val message = session.getArray("message") val message = session.getArray("message")
SendMessage(chatTYpe, userId, message, session.echo, fromId = groupId ?: userId) SendMessage(
chatType = chatTYpe,
peerId = userId,
message = message,
echo = session.echo,
fromId = groupId ?: userId
)
} }
} }

View File

@ -42,13 +42,13 @@ fun Routing.messageAction() {
} }
post { post {
val msgType = fetchPostOrThrow("message_type") val msgType = fetchPostOrThrow("message_type")
val peerIdKey = if(msgType == "group") "group_id" else "user_id"
val chatType = MessageHelper.obtainMessageTypeByDetailType(msgType) val chatType = MessageHelper.obtainMessageTypeByDetailType(msgType)
val peerId = fetchPostOrThrow(if(msgType == "group") "group_id" else "user_id")
call.respondText(if (isJsonData() && !isJsonString("message")) { call.respondText(if (isJsonData() && !isJsonString("message")) {
SendMessage(chatType, fetchPostOrThrow(peerIdKey), fetchPostJsonArray("message")) SendMessage(chatType, peerId, fetchPostJsonArray("message"))
} else { } else {
val autoEscape = fetchPostOrNull("auto_escape")?.toBooleanStrict() ?: false val autoEscape = fetchPostOrNull("auto_escape")?.toBooleanStrict() ?: false
SendMessage(chatType, fetchPostOrThrow(peerIdKey), fetchPostOrThrow("message"), autoEscape) SendMessage(chatType, peerId, fetchPostOrThrow("message"), autoEscape)
}) })
} }
} }

View File

@ -25,6 +25,8 @@ import io.ktor.http.parseUrlEncodedParameters
import io.ktor.server.request.httpMethod import io.ktor.server.request.httpMethod
import io.ktor.server.routing.route import io.ktor.server.routing.route
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.remote.entries.CommonResult import moe.fuqiuluo.shamrock.remote.entries.CommonResult
import moe.fuqiuluo.shamrock.remote.entries.EmptyObject import moe.fuqiuluo.shamrock.remote.entries.EmptyObject
import moe.fuqiuluo.shamrock.remote.entries.Status import moe.fuqiuluo.shamrock.remote.entries.Status
@ -88,7 +90,7 @@ suspend fun ApplicationCall.fetchPostOrThrow(key: String): String {
} }
fun ApplicationCall.isJsonData(): Boolean { fun ApplicationCall.isJsonData(): Boolean {
return ContentType.Application.Json == request.contentType() return ContentType.Application.Json == request.contentType() || ContentType.Application.ProblemJson == request.contentType()
} }
suspend fun ApplicationCall.fetchPostOrNull(key: String): String? { suspend fun ApplicationCall.fetchPostOrNull(key: String): String? {
@ -102,6 +104,7 @@ suspend fun ApplicationCall.fetchPostOrNull(key: String): String? {
if (isJsonData()) { if (isJsonData()) {
Json.parseToJsonElement(receiveText()).jsonObject.also { Json.parseToJsonElement(receiveText()).jsonObject.also {
attributes.put(jsonKey, it) attributes.put(jsonKey, it)
attributes.put(isJsonKey, true)
}[key].asStringOrNull }[key].asStringOrNull
} else if ( } else if (
ContentType.Application.FormUrlEncoded == request.contentType() ContentType.Application.FormUrlEncoded == request.contentType()
@ -113,7 +116,7 @@ suspend fun ApplicationCall.fetchPostOrNull(key: String): String? {
receiveTextAsUnknown(key) receiveTextAsUnknown(key)
} }
}.getOrElse { }.getOrElse {
receiveTextAsUnknown(key) throw IllegalArgumentException("JSON数据格式不合法")
} }
} }
@ -177,6 +180,7 @@ suspend fun PipelineContext<Unit, ApplicationCall>.isJsonString(key: String): Bo
} else { } else {
Json.parseToJsonElement(call.receiveText()).jsonObject.also { Json.parseToJsonElement(call.receiveText()).jsonObject.also {
call.attributes.put(jsonKey, it) call.attributes.put(jsonKey, it)
call.attributes.put(isJsonKey, true)
} }
} }
return data[key] is JsonPrimitive return data[key] is JsonPrimitive
@ -245,6 +249,7 @@ suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonArray(key: Strin
} else { } else {
Json.parseToJsonElement(call.receiveText()).jsonObject.also { Json.parseToJsonElement(call.receiveText()).jsonObject.also {
call.attributes.put(jsonKey, it) call.attributes.put(jsonKey, it)
call.attributes.put(isJsonKey, true)
} }
} }
return data[key].asJsonArray return data[key].asJsonArray