13 Commits

Author SHA1 Message Date
7eaa470dd2 fix #334 2024-07-17 17:27:16 +08:00
c49861d982 fix #340 2024-07-17 17:25:16 +08:00
36ed55d220 fix #337 2024-07-16 21:05:39 +08:00
e2f27cb36a fix #339 2024-07-16 20:56:09 +08:00
65ddca2ea4 fix: optimize import 2024-07-07 18:03:19 +08:00
a6f570e4f0 fix: remove debug 2024-07-07 18:02:41 +08:00
29dfc1590b feat: ocr 2024-07-07 17:51:51 +08:00
ffeda0a472 fix: msf core 2024-07-06 22:36:45 +08:00
0e5add2146 fix: some compatibility issues on 9.0.70+
It is not recommended to use this build for lower versions
2024-07-06 22:29:10 +08:00
be9ff46134 Merge pull request #325 from StarryKira/v1.0.9
fix get_image
2024-06-06 12:44:10 +08:00
1d6ac3e022 fix get_image 2024-06-06 12:32:59 +08:00
2db187e3d5 (#319) 2024-04-11 12:41:23 +08:00
18ec586b12 试着修复group_increase事件在大群中依然可能出现target_id=0的故障 (#318) 2024-04-11 01:04:52 +08:00
127 changed files with 3206 additions and 1765 deletions

View File

@ -3,7 +3,7 @@ name: Build Shamrock APK
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: [ master ] branches: [ v1.0.9 ]
paths-ignore: paths-ignore:
- '**.md' - '**.md'
- '**.txt' - '**.txt'

View File

@ -17,7 +17,7 @@ android {
minSdk = 27 minSdk = 27
targetSdk = 34 targetSdk = 34
versionCode = getVersionCode() versionCode = getVersionCode()
versionName = "1.0.9" + ".r${getGitCommitCount()}." + getVersionName() versionName = "1.1.1.onebot" + ".r${getGitCommitCount()}." + getVersionName()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {

View File

@ -12,7 +12,8 @@
extern "C" extern "C"
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_moe_fuqiuluo_shamrock_xposed_hooks_PullConfig_testNativeLibrary(JNIEnv *env, jobject thiz) { Java_moe_fuqiuluo_shamrock_xposed_hooks_PullConfig_00024Companion_testNativeLibrary(JNIEnv *env,
jobject thiz) {
return env->NewStringUTF("加载Shamrock库成功~"); return env->NewStringUTF("加载Shamrock库成功~");
} }

View File

@ -52,7 +52,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@ -64,6 +63,7 @@ import androidx.compose.ui.unit.sp
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import moe.fuqiuluo.shamrock.ui.app.AppRuntime import moe.fuqiuluo.shamrock.ui.app.AppRuntime
import moe.fuqiuluo.shamrock.ui.app.Logger import moe.fuqiuluo.shamrock.ui.app.Logger
@ -79,7 +79,7 @@ import moe.fuqiuluo.shamrock.ui.theme.RANDOM_SUB_TITLE
import moe.fuqiuluo.shamrock.ui.theme.RANDOM_TITLE import moe.fuqiuluo.shamrock.ui.theme.RANDOM_TITLE
import moe.fuqiuluo.shamrock.ui.theme.ShamrockTheme import moe.fuqiuluo.shamrock.ui.theme.ShamrockTheme
import moe.fuqiuluo.shamrock.ui.tools.NoIndication import moe.fuqiuluo.shamrock.ui.tools.NoIndication
import moe.fuqiuluo.shamrock.ui.tools.ShamrockTab import moe.fuqiuluo.shamrock.ui.tools.ShamrockTabV2
import moe.fuqiuluo.shamrock.ui.tools.getShamrockVersion import moe.fuqiuluo.shamrock.ui.tools.getShamrockVersion
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@ -87,6 +87,15 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
LaunchedEffect(Unit) {
while (true) {
delay(5_000) // Delay in milliseconds
broadcastToModule {
putExtra("__cmd", "switch_status")
}
}
}
CompositionLocalProvider( CompositionLocalProvider(
LocalIndication provides NoIndication LocalIndication provides NoIndication
) { ) {
@ -336,7 +345,7 @@ private fun AnimatedTab(
} }
} }
ShamrockTab( ShamrockTabV2(
selected = curSelected, selected = curSelected,
onClick = { onClick = {
scope.launch { scope.launch {

View File

@ -30,6 +30,7 @@ abstract class ModuleHandler {
} }
} }
putExtra("__hash", callbackId) putExtra("__hash", callbackId)
putExtra("__cmd", cmd)
} }
} }
} }

View File

@ -58,7 +58,7 @@ class MultifunctionalProvider: ContentProvider() {
inline fun Context.broadcastToModule(intentBuilder: Intent.() -> Unit) { inline fun Context.broadcastToModule(intentBuilder: Intent.() -> Unit) {
val intent = Intent() val intent = Intent()
intent.action = "moe.fuqiuluo.xqbot.dynamic" intent.action = "moe.fuqiuluo.onebot.dynamic"
intent.intentBuilder() intent.intentBuilder()
sendBroadcast(intent) sendBroadcast(intent)
} }

View File

@ -242,13 +242,13 @@ private fun Placeable.PlacementScope.placeTextAndIcon(
} }
@Composable @Composable
fun ShamrockTab( fun ShamrockTabV2(
selected: Boolean, selected: Boolean,
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
text: @Composable (() -> Unit)? = null, text: (@Composable (() -> Unit))? = null,
icon: @Composable (() -> Unit)? = null, icon: (@Composable (() -> Unit))? = null,
selectedContentColor: Color = GlobalColor.TabSelected, selectedContentColor: Color = GlobalColor.TabSelected,
unselectedContentColor: Color = selectedContentColor, unselectedContentColor: Color = selectedContentColor,
indication: Indication? = rememberRipple(bounded = true, color = selectedContentColor), indication: Indication? = rememberRipple(bounded = true, color = selectedContentColor),
@ -262,7 +262,7 @@ fun ShamrockTab(
ProvideTextStyle(style, content = text) ProvideTextStyle(style, content = text)
} }
} }
ShamrockTab( ShamrockTabV2(
selected, selected,
onClick, onClick,
modifier, modifier,
@ -277,7 +277,7 @@ fun ShamrockTab(
} }
@Composable @Composable
fun ShamrockTab( fun ShamrockTabV2(
selected: Boolean, selected: Boolean,
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,

View File

@ -94,7 +94,8 @@ data class DeleteReq(
@Serializable @Serializable
data class DownloadRkeyReq( data class DownloadRkeyReq(
@ProtoNumber(1) val types: List<Int> @ProtoNumber(1) val types: List<Int>,
@ProtoNumber(2) val downloadType: Int
) )
@Serializable @Serializable

View File

@ -52,11 +52,11 @@ data class DownloadRkeyRsp(
@Serializable @Serializable
data class RKeyInfo( data class RKeyInfo(
@ProtoNumber(1) val rkey: String?, @ProtoNumber(1) val rkey: String,
@ProtoNumber(2) val rkeyTtlSec: ULong?, @ProtoNumber(2) val rkeyTtlSec: ULong?,
@ProtoNumber(3) val storeId: UInt = 0u, @ProtoNumber(3) val storeId: UInt = 0u,
@ProtoNumber(4) val rkeyCreateTime: UInt?, @ProtoNumber(4) val rkeyCreateTime: UInt?,
@ProtoNumber(4) val type: UInt?, @ProtoNumber(4) val type: UInt,
) )
@Serializable @Serializable

View File

@ -1,63 +1,92 @@
package com.tencent.mobileqq.data.troop; package com.tencent.mobileqq.data.troop;
import org.jetbrains.annotations.Nullable;
import com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole;
public class TroopMemberInfo { public class TroopMemberInfo {
public long active_point; public static final long VALUE_DISTANCE_TO_SELF_UNKOWN = -100;
protected static final int VALUE_INVALID = -100;
public static final long VALUE_MEMBER_CLOSE_SHARE_LBS = -1001;
public int addState; public int addState;
public byte age;
public String alias; @Deprecated(since = "推荐使用TroopMemberNickInfo")
public String autoremark; public String autoremark;
public long cmduinFlagEx3Grocery;
public long credit_level; public long credit_level;
public long datetime;
public String displayedNamePinyinFirst; public String displayedNamePinyinFirst;
public short faceid;
@Nullable
public TroopMemberInfoExt extInfo;
@Deprecated(since = "推荐使用TroopMemberNickInfo")
public String friendnick; public String friendnick;
public long gagTimeStamp; public long gagTimeStamp;
public String honorList; public String honorList;
public boolean isTroopFollowed;
public long join_time;
public long last_active_time;
public int level;
public int mBigClubVipType;
public byte mHonorRichFlag;
public boolean mIsShielded;
public int mVipType;
public String memberUid;
public String memberuin;
public int newRealLevel;
public TroopMemberNickInfo nickInfo;
public int realLevel;
public MemberRole role;
@Nullable
public TroopMemberSpecialTitleInfo specialTitleInfo;
@Deprecated(since = "推荐使用TroopMemberNickInfo")
public String troopColorNick;
@Deprecated(since = "推荐使用TroopMemberNickInfo")
public int troopColorNickId;
@Deprecated(since = "推荐使用TroopMemberNickInfo")
public String troopnick;
public String troopuin;
public int globalTroopLevel = VALUE_INVALID;
public int flagEx = 0;
// QQ OLD API DATA
public long active_point;
public byte age;
public String alias;
public long cmduinFlagEx3Grocery;
public long datetime;
public short faceid;
public String hwCourse; public String hwCourse;
public int hwIdentity; public int hwIdentity;
public String hwName; public String hwName;
public int isShowQZone; public int isShowQZone;
public boolean isTroopFollowed;
public long join_time;
public long lastMsgUpdateHonorRichTime; public long lastMsgUpdateHonorRichTime;
public long last_active_time;
public int level;
public int mBigClubTemplateId; public int mBigClubTemplateId;
public int mBigClubVipLevel; public int mBigClubVipLevel;
public int mBigClubVipType;
public int mGlamourLevel; public int mGlamourLevel;
public byte mHonorRichFlag;
public int mIsHideBigClub; public int mIsHideBigClub;
public boolean mIsShielded;
public String mUniqueTitle; public String mUniqueTitle;
public int mVipLevel; public int mVipLevel;
public int mVipTemplateId; public int mVipTemplateId;
public int mVipType;
public String memberuin;
public int newRealLevel;
public String pyAll_autoremark; public String pyAll_autoremark;
public String pyAll_friendnick; public String pyAll_friendnick;
public String pyAll_troopnick; public String pyAll_troopnick;
public String pyFirst_autoremark; public String pyFirst_autoremark;
public String pyFirst_friendnick; public String pyFirst_friendnick;
public String pyFirst_troopnick; public String pyFirst_troopnick;
public int realLevel;
public String recommendRemark; public String recommendRemark;
public byte sex; public byte sex;
public byte status; public byte status;
public int tribeLevel; public int tribeLevel;
public int tribePoint; public int tribePoint;
public String troopColorNick;
public int troopColorNickId;
public String troopnick;
public String troopremark; public String troopremark;
public String troopuin;
public int qqVipInfo = 0; public int qqVipInfo = 0;
public int superQqInfo = 0; public int superQqInfo = 0;
public int superVipInfo = 0; public int superVipInfo = 0;
public int hotChatGlamourLevel = -100; public int hotChatGlamourLevel = -100;
public int globalTroopLevel = -100;
public int distance = 0; public int distance = 0;
public long msgseq = -100; public long msgseq = -100;
public double distanceToSelf = -100.0d; public double distanceToSelf = -100.0d;

View File

@ -0,0 +1,48 @@
package com.tencent.mobileqq.data.troop;
import org.jetbrains.annotations.Nullable;
import kotlin.jvm.JvmField;
public class TroopMemberInfoExt {
@JvmField
public int commonFrdCnt;
@JvmField
public long flagEx3;
@JvmField
public int hwIdentity;
@JvmField
public long lastMsgUpdateHonorRichTime;
@JvmField
@Nullable
public String memberUin;
@JvmField
@Nullable
public byte[] nickIconRepeatMsgBuffer;
@JvmField
@Nullable
public String recommendRemark;
@JvmField
@Nullable
public String showNameForPinyin;
@JvmField
@Nullable
public String showNamePinyinAll;
@JvmField
@Nullable
public String showNamePinyinFirst;
@JvmField
@Nullable
public String troopUin;
}

View File

@ -0,0 +1,54 @@
package com.tencent.mobileqq.data.troop;
import org.jetbrains.annotations.NotNull;
public class TroopMemberNickInfo {
@NotNull
public final String getAutoRemark() {
return null;
}
@NotNull
public final String getColorNick() {
return null;
}
public final int getColorNickId() {
return 0;
}
@NotNull
public final String getFriendNick() {
return null;
}
@NotNull
public final String getHBShowName() {
return null;
}
@NotNull
public final String getRemarkFromFriend() {
return null;
}
@NotNull
public final String getRemarkFromFriendV2() {
return null;
}
@NotNull
public final String getShowName() {
return null;
}
@NotNull
public final String getTroopNick() {
return null;
}
@NotNull
public final String getTroopUin() {
return null;
}
}

View File

@ -0,0 +1,29 @@
package com.tencent.mobileqq.data.troop;
import org.jetbrains.annotations.NotNull;
public class TroopMemberSpecialTitleInfo {
public final int getExpireTimeSec() {
return 0;
}
@NotNull
public final String getFriendNick() {
return null;
}
@NotNull
public final String getSpecialTitle() {
return null;
}
@NotNull
public final String getTroopUin() {
return null;
}
@NotNull
public final String getUin() {
return null;
}
}

View File

@ -0,0 +1,15 @@
package com.tencent.mobileqq.l0.b;
public class a {
public void b () {
}
public void c (c cVar) {
}
public void onUpdate(int progress, boolean z, Object obj) {
}
}

View File

@ -0,0 +1,23 @@
package com.tencent.mobileqq.l0.b;
import android.graphics.Point;
import java.util.ArrayList;
public class b {
// text
public String a;
// confidence
public int b;
// coordinates
public ArrayList<Point> c;
/* renamed from: d */
public int d;
/* renamed from: e */
public boolean e;
}

View File

@ -0,0 +1,39 @@
package com.tencent.mobileqq.l0.b;
import java.util.ArrayList;
import java.util.HashMap;
public class c {
// image
public String a;
// width
public int b;
// height
public int c;
// lang
public String d;
// url
public String e;
// results
public ArrayList<b> f;
public ArrayList<String> g;
public HashMap<String, String> h;
public int i;
public int j;
public int k;
public String l;
public int m;
}

View File

@ -1,7 +1,7 @@
package com.tencent.mobileqq.msf.core; package com.tencent.mobileqq.msf.core;
public class MsfCore { public class MsfCore {
public static synchronized int getNextSeq() { public synchronized int getNextSeq() {
return 0; return 0;
} }
} }

View File

@ -0,0 +1,18 @@
package com.tencent.mobileqq.msf.sdk;
import com.tencent.qphone.base.remote.FromServiceMsg;
import com.tencent.qphone.base.remote.ToServiceMsg;
public class MsfMessagePair {
public FromServiceMsg fromServiceMsg;
public String sendProcess;
public ToServiceMsg toServiceMsg;
public MsfMessagePair(String str, ToServiceMsg toServiceMsg, FromServiceMsg fromServiceMsg) {
}
public MsfMessagePair(ToServiceMsg toServiceMsg, FromServiceMsg fromServiceMsg) {
}
}

View File

@ -0,0 +1,22 @@
package com.tencent.mobileqq.msf.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import com.tencent.mobileqq.msf.core.MsfCore;
public class MsfService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
public static MsfCore getCore() {
return null;
}
}

View File

@ -0,0 +1,38 @@
package com.tencent.mobileqq.ocr.a;
public class a {
public String a;
// 1
public int b;
// file location
public String c;
// null
public String d;
// 0
public long e;
// md5
public String f;
// null
public String g;
// false
public boolean h;
// 0
public int i;
// 0
public int j;
// 0
public long k;
// null
public String l;
}

View File

@ -0,0 +1,10 @@
package com.tencent.mobileqq.ocr.api;
import mqq.app.api.IRuntimeService;
public interface IPicOcrService extends IRuntimeService {
void putOcrResult(String str, com.tencent.mobileqq.l0.b.c cVar);
void requestOcr(com.tencent.mobileqq.ocr.a.a aVar, com.tencent.mobileqq.l0.b.a callback);
}

View File

@ -0,0 +1,4 @@
package com.tencent.mobileqq.ocr.api.impl;
public class OcrServiceImpl {
}

View File

@ -0,0 +1,4 @@
package com.tencent.mobileqq.ocr.api.impl;
public class PicOcrServiceImpl {
}

View File

@ -1,7 +1,9 @@
package com.tencent.qphone.base.util; package com.tencent.qphone.base.util;
public abstract class CodecWarpper { public abstract class CodecWarpper {
public abstract void onResponse(int i2, Object obj, int i3); // public abstract void onResponse(int i2, Object obj, int i3);
public abstract void onInvalidData(int i2, int i3, String str);
public abstract void onResponse(int i2, Object obj, int i3, byte[] bArr); public abstract void onResponse(int i2, Object obj, int i3, byte[] bArr);

View File

@ -1,8 +1,8 @@
package com.tencent.qqnt.aio.api; package com.tencent.qqnt.aio.api;
import com.tencent.mobileqq.qroute.QRouteApi; import com.tencent.mobileqq.qroute.QRouteApi;
import com.tencent.qqnt.kernel.nativeinterface.Contact;
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback; import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
public interface IAIOFileTransfer extends QRouteApi { public interface IAIOFileTransfer extends QRouteApi {
void sendLocalFile(Contact contact, String path, IOperateCallback cb); void sendLocalFile(Contact contact, String path, IOperateCallback cb);

View File

@ -0,0 +1,4 @@
package com.tencent.qqnt.kernel.api.impl;
public class GroupService {
}

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
import java.util.ArrayList; import java.util.ArrayList;

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole;
import java.io.Serializable; import java.io.Serializable;
public final class GrayTipGroupMember implements Serializable { public final class GrayTipGroupMember implements Serializable {

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole;
import java.util.ArrayList; import java.util.ArrayList;

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
import java.util.ArrayList; import java.util.ArrayList;

View File

@ -0,0 +1,36 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
public class GroupMemberCommonListResult {
public long groupCode;
public int identifyFlag;
public long startUin;
public ArrayList<MemberCommonInfo> memberList = new ArrayList<>();
public String strErrorInfo = "";
public long getGroupCode() {
return this.groupCode;
}
public int getIdentifyFlag() {
return this.identifyFlag;
}
public ArrayList<MemberCommonInfo> getMemberList() {
return this.memberList;
}
public long getStartUin() {
return this.startUin;
}
public String getStrErrorInfo() {
return this.strErrorInfo;
}
public String toString() {
return "GroupMemberCommonListResult{groupCode=" + this.groupCode + ",startUin=" + this.startUin + ",identifyFlag=" + this.identifyFlag + ",memberList=" + this.memberList + ",strErrorInfo=" + this.strErrorInfo + ",}";
}
}

View File

@ -0,0 +1,101 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
public class GroupMemberCommonReq {
public long groupCode;
public int sourceType;
public String startUin = "";
public String identifyFlag = "";
public ArrayList<Long> uinList = new ArrayList<>();
public MemberCommonInfoFilter memberCommonFilter = new MemberCommonInfoFilter();
public String memberNum = "";
public String filterMethod = "";
public String onlineFlag = "";
public String realSpecialTitleFlag = "";
public String getFilterMethod() {
return this.filterMethod;
}
public long getGroupCode() {
return this.groupCode;
}
public String getIdentifyFlag() {
return this.identifyFlag;
}
public MemberCommonInfoFilter getMemberCommonFilter() {
return this.memberCommonFilter;
}
public String getMemberNum() {
return this.memberNum;
}
public String getOnlineFlag() {
return this.onlineFlag;
}
public String getRealSpecialTitleFlag() {
return this.realSpecialTitleFlag;
}
public int getSourceType() {
return this.sourceType;
}
public String getStartUin() {
return this.startUin;
}
public ArrayList<Long> getUinList() {
return this.uinList;
}
public void setFilterMethod(String str) {
this.filterMethod = str;
}
public void setGroupCode(long j2) {
this.groupCode = j2;
}
public void setIdentifyFlag(String str) {
this.identifyFlag = str;
}
public void setMemberCommonFilter(MemberCommonInfoFilter memberCommonInfoFilter) {
this.memberCommonFilter = memberCommonInfoFilter;
}
public void setMemberNum(String str) {
this.memberNum = str;
}
public void setOnlineFlag(String str) {
this.onlineFlag = str;
}
public void setRealSpecialTitleFlag(String str) {
this.realSpecialTitleFlag = str;
}
public void setSourceType(int i2) {
this.sourceType = i2;
}
public void setStartUin(String str) {
this.startUin = str;
}
public void setUinList(ArrayList<Long> arrayList) {
this.uinList = arrayList;
}
public String toString() {
return "GroupMemberCommonReq{groupCode=" + this.groupCode + ",startUin=" + this.startUin + ",identifyFlag=" + this.identifyFlag + ",uinList=" + this.uinList + ",memberCommonFilter=" + this.memberCommonFilter + ",memberNum=" + this.memberNum + ",filterMethod=" + this.filterMethod + ",onlineFlag=" + this.onlineFlag + ",realSpecialTitleFlag=" + this.realSpecialTitleFlag + ",sourceType=" + this.sourceType + ",}";
}
}

View File

@ -0,0 +1,81 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
public class GroupMemberExtListResult {
public long dataTime;
public long endUin;
public long groupCode;
public int levelNameSeq;
public int memberInfoSeq;
public int sysShowFlag;
public int timeToUpdate;
public int userShowFlag;
public int userShowFlagNew;
public ArrayList<MemberExtInfo> memberLevelInfo = new ArrayList<>();
public ArrayList<MemberLevelName> msgLevelName = new ArrayList<>();
public String strOwnerName = "";
public String strAdminName = "";
public ArrayList<MemberLevelName> msgLevelNameNew = new ArrayList<>();
public long getDataTime() {
return this.dataTime;
}
public long getEndUin() {
return this.endUin;
}
public long getGroupCode() {
return this.groupCode;
}
public int getLevelNameSeq() {
return this.levelNameSeq;
}
public int getMemberInfoSeq() {
return this.memberInfoSeq;
}
public ArrayList<MemberExtInfo> getMemberLevelInfo() {
return this.memberLevelInfo;
}
public ArrayList<MemberLevelName> getMsgLevelName() {
return this.msgLevelName;
}
public ArrayList<MemberLevelName> getMsgLevelNameNew() {
return this.msgLevelNameNew;
}
public String getStrAdminName() {
return this.strAdminName;
}
public String getStrOwnerName() {
return this.strOwnerName;
}
public int getSysShowFlag() {
return this.sysShowFlag;
}
public int getTimeToUpdate() {
return this.timeToUpdate;
}
public int getUserShowFlag() {
return this.userShowFlag;
}
public int getUserShowFlagNew() {
return this.userShowFlagNew;
}
public String toString() {
return "GroupMemberExtListResult{groupCode=" + this.groupCode + ",memberLevelInfo=" + this.memberLevelInfo + ",msgLevelName=" + this.msgLevelName + ",endUin=" + this.endUin + ",dataTime=" + this.dataTime + ",userShowFlag=" + this.userShowFlag + ",sysShowFlag=" + this.sysShowFlag + ",timeToUpdate=" + this.timeToUpdate + ",strOwnerName=" + this.strOwnerName + ",strAdminName=" + this.strAdminName + ",levelNameSeq=" + this.levelNameSeq + ",userShowFlagNew=" + this.userShowFlagNew + ",msgLevelNameNew=" + this.msgLevelNameNew + ",memberInfoSeq=" + this.memberInfoSeq + ",}";
}
}

View File

@ -0,0 +1,101 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
public class GroupMemberExtReq {
public long groupCode;
public int sourceType;
public String beginUin = "";
public String dataTime = "";
public ArrayList<Long> uinList = new ArrayList<>();
public MemberExtInfoFilter memberExtFilter = new MemberExtInfoFilter();
public String seq = "";
public String uinNum = "";
public String groupType = "";
public String richCardNameVer = "";
public String getBeginUin() {
return this.beginUin;
}
public String getDataTime() {
return this.dataTime;
}
public long getGroupCode() {
return this.groupCode;
}
public String getGroupType() {
return this.groupType;
}
public MemberExtInfoFilter getMemberExtFilter() {
return this.memberExtFilter;
}
public String getRichCardNameVer() {
return this.richCardNameVer;
}
public String getSeq() {
return this.seq;
}
public int getSourceType() {
return this.sourceType;
}
public ArrayList<Long> getUinList() {
return this.uinList;
}
public String getUinNum() {
return this.uinNum;
}
public void setBeginUin(String str) {
this.beginUin = str;
}
public void setDataTime(String str) {
this.dataTime = str;
}
public void setGroupCode(long j2) {
this.groupCode = j2;
}
public void setGroupType(String str) {
this.groupType = str;
}
public void setMemberExtFilter(MemberExtInfoFilter memberExtInfoFilter) {
this.memberExtFilter = memberExtInfoFilter;
}
public void setRichCardNameVer(String str) {
this.richCardNameVer = str;
}
public void setSeq(String str) {
this.seq = str;
}
public void setSourceType(int i2) {
this.sourceType = i2;
}
public void setUinList(ArrayList<Long> arrayList) {
this.uinList = arrayList;
}
public void setUinNum(String str) {
this.uinNum = str;
}
public String toString() {
return "GroupMemberExtReq{groupCode=" + this.groupCode + ",beginUin=" + this.beginUin + ",dataTime=" + this.dataTime + ",uinList=" + this.uinList + ",memberExtFilter=" + this.memberExtFilter + ",seq=" + this.seq + ",uinNum=" + this.uinNum + ",groupType=" + this.groupType + ",richCardNameVer=" + this.richCardNameVer + ",sourceType=" + this.sourceType + ",}";
}
}

View File

@ -0,0 +1,32 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
import java.util.HashMap;
public class GroupMemberListResult {
public boolean finish;
public boolean hasRobot;
public ArrayList<GroupMemberInfoListId> ids = new ArrayList<>();
public HashMap<String, MemberInfo> infos = new HashMap<>();
public boolean getFinish() {
return this.finish;
}
public boolean getHasRobot() {
return this.hasRobot;
}
public ArrayList<GroupMemberInfoListId> getIds() {
return this.ids;
}
public HashMap<String, MemberInfo> getInfos() {
return this.infos;
}
public String toString() {
return "GroupMemberListResult{ids=" + this.ids + ",infos=" + this.infos + ",finish=" + this.finish + ",hasRobot=" + this.hasRobot + ",}";
}
}

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole;
public final class GroupSimpleInfo implements IKernelModel { public final class GroupSimpleInfo implements IKernelModel {
String avatarUrl; String avatarUrl;
int discussToGroupMaxMsgSeq; int discussToGroupMaxMsgSeq;

View File

@ -0,0 +1,6 @@
package com.tencent.qqnt.kernel.nativeinterface;
public interface IGroupMemberCommonCallback {
void onResult(int i2, String str, GroupMemberCommonListResult groupMemberCommonListResult);
}

View File

@ -0,0 +1,5 @@
package com.tencent.qqnt.kernel.nativeinterface;
public interface IGroupMemberExtCallback {
void onResult(int i2, String str, GroupMemberExtListResult groupMemberExtListResult);
}

View File

@ -0,0 +1,6 @@
package com.tencent.qqnt.kernel.nativeinterface;
public interface IGroupMemberListCallback {
void onResult(int result, String str, GroupMemberListResult groupMemberListResult);
}

View File

@ -1,7 +1,25 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
public interface IKernelGroupService { public interface IKernelGroupService {
void getTransferableMemberInfo(long uin, IGetTransferableMemberCallback cb); void getTransferableMemberInfo(long uin, IGetTransferableMemberCallback cb);
long addKernelGroupListener(IKernelGroupListener ln); long addKernelGroupListener(IKernelGroupListener ln);
void getAllMemberList(long groupCode, boolean refresh, IGroupMemberListCallback iGroupMemberListCallback);
void getMemberCommonInfo(GroupMemberCommonReq groupMemberCommonReq, IGroupMemberCommonCallback iGroupMemberCommonCallback);
void getMemberExtInfo(GroupMemberExtReq groupMemberExtReq, IGroupMemberExtCallback iGroupMemberExtCallback);
void getMemberInfo(long j2, ArrayList<String> arrayList, boolean z, IOperateCallback iOperateCallback);
void getMemberInfoForMqq(long groupCode, ArrayList<String> uids, boolean z, IGroupMemberListCallback iGroupMemberListCallback);
void getNextMemberList(String sceneId, GroupMemberInfoListId groupMemberInfoListId, int i2, IGroupMemberListCallback iGroupMemberListCallback);
void getPrevMemberList(String sceneId, GroupMemberInfoListId groupMemberInfoListId, int i2, IGroupMemberListCallback iGroupMemberListCallback);
} }

View File

@ -2,6 +2,7 @@ package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
public interface IKernelMsgListener { public interface IKernelMsgListener {
void onAddSendMsg(MsgRecord msgRecord); void onAddSendMsg(MsgRecord msgRecord);
@ -116,7 +117,7 @@ public interface IKernelMsgListener {
void onSendMsgError(long j2, Contact contact, int i2, String str); void onSendMsgError(long j2, Contact contact, int i2, String str);
void onSysMsgNotification(int i2, long j2, long j3, ArrayList<Byte> arrayList); void onSysMsgNotification(int i2, long j2, long j3, boolean z, ArrayList<Byte> arrayList);
void onTempChatInfoUpdate(TempChatInfo tempChatInfo); void onTempChatInfoUpdate(TempChatInfo tempChatInfo);

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
import java.util.ArrayList; import java.util.ArrayList;
public interface IKernelRichMediaService { public interface IKernelRichMediaService {

View File

@ -0,0 +1,63 @@
package com.tencent.qqnt.kernel.nativeinterface;
public class MemberCommonInfo {
public long memberUin;
public int privilege;
public int shutUpTime;
public int uinFlag;
public int uinFlagExt;
public int uinMobileFlag;
public long getMemberUin() {
return this.memberUin;
}
public int getPrivilege() {
return this.privilege;
}
public int getShutUpTime() {
return this.shutUpTime;
}
public int getUinFlag() {
return this.uinFlag;
}
public int getUinFlagExt() {
return this.uinFlagExt;
}
public int getUinMobileFlag() {
return this.uinMobileFlag;
}
public void setMemberUin(long j2) {
this.memberUin = j2;
}
public void setPrivilege(int i2) {
this.privilege = i2;
}
public void setShutUpTime(int i2) {
this.shutUpTime = i2;
}
public void setUinFlag(int i2) {
this.uinFlag = i2;
}
public void setUinFlagExt(int i2) {
this.uinFlagExt = i2;
}
public void setUinMobileFlag(int i2) {
this.uinMobileFlag = i2;
}
public String toString() {
return "MemberCommonInfo{memberUin=" + this.memberUin + ",uinFlag=" + this.uinFlag + ",uinFlagExt=" + this.uinFlagExt + ",uinMobileFlag=" + this.uinMobileFlag + ",shutUpTime=" + this.shutUpTime + ",privilege=" + this.privilege + ",}";
}
}

View File

@ -0,0 +1,63 @@
package com.tencent.qqnt.kernel.nativeinterface;
public class MemberCommonInfoFilter {
public int memberUin;
public int privilege;
public int shutUpTime;
public int uinFlag;
public int uinFlagExt;
public int uinMobileFlag;
public int getMemberUin() {
return this.memberUin;
}
public int getPrivilege() {
return this.privilege;
}
public int getShutUpTime() {
return this.shutUpTime;
}
public int getUinFlag() {
return this.uinFlag;
}
public int getUinFlagExt() {
return this.uinFlagExt;
}
public int getUinMobileFlag() {
return this.uinMobileFlag;
}
public void setMemberUin(int i2) {
this.memberUin = i2;
}
public void setPrivilege(int i2) {
this.privilege = i2;
}
public void setShutUpTime(int i2) {
this.shutUpTime = i2;
}
public void setUinFlag(int i2) {
this.uinFlag = i2;
}
public void setUinFlagExt(int i2) {
this.uinFlagExt = i2;
}
public void setUinMobileFlag(int i2) {
this.uinMobileFlag = i2;
}
public String toString() {
return "MemberCommonInfoFilter{memberUin=" + this.memberUin + ",uinFlag=" + this.uinFlag + ",uinFlagExt=" + this.uinFlagExt + ",uinMobileFlag=" + this.uinMobileFlag + ",shutUpTime=" + this.shutUpTime + ",privilege=" + this.privilege + ",}";
}
}

View File

@ -0,0 +1,110 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
public class MemberExtInfo {
public int activeDay;
public int cmdUinFlagExt3Grocery;
public int level;
public int point;
public long specialTitleExpireTime;
public long uin;
public String strName = "";
public String nickName = "";
public String specialTitle = "";
public byte[] msgNeedField = new byte[0];
public ArrayList<MemberIcon> memberIconList = new ArrayList<>();
public int getActiveDay() {
return this.activeDay;
}
public int getCmdUinFlagExt3Grocery() {
return this.cmdUinFlagExt3Grocery;
}
public int getLevel() {
return this.level;
}
public ArrayList<MemberIcon> getMemberIconList() {
return this.memberIconList;
}
public byte[] getMsgNeedField() {
return this.msgNeedField;
}
public String getNickName() {
return this.nickName;
}
public int getPoint() {
return this.point;
}
public String getSpecialTitle() {
return this.specialTitle;
}
public long getSpecialTitleExpireTime() {
return this.specialTitleExpireTime;
}
public String getStrName() {
return this.strName;
}
public long getUin() {
return this.uin;
}
public void setActiveDay(int i2) {
this.activeDay = i2;
}
public void setCmdUinFlagExt3Grocery(int i2) {
this.cmdUinFlagExt3Grocery = i2;
}
public void setLevel(int i2) {
this.level = i2;
}
public void setMemberIconList(ArrayList<MemberIcon> arrayList) {
this.memberIconList = arrayList;
}
public void setMsgNeedField(byte[] bArr) {
this.msgNeedField = bArr;
}
public void setNickName(String str) {
this.nickName = str;
}
public void setPoint(int i2) {
this.point = i2;
}
public void setSpecialTitle(String str) {
this.specialTitle = str;
}
public void setSpecialTitleExpireTime(long j2) {
this.specialTitleExpireTime = j2;
}
public void setStrName(String str) {
this.strName = str;
}
public void setUin(long j2) {
this.uin = j2;
}
public String toString() {
return "MemberExtInfo{uin=" + this.uin + ",point=" + this.point + ",activeDay=" + this.activeDay + ",level=" + this.level + ",strName=" + this.strName + ",nickName=" + this.nickName + ",specialTitle=" + this.specialTitle + ",specialTitleExpireTime=" + this.specialTitleExpireTime + ",msgNeedField=" + this.msgNeedField + ",cmdUinFlagExt3Grocery=" + this.cmdUinFlagExt3Grocery + ",memberIconList=" + this.memberIconList + ",}";
}
}

View File

@ -0,0 +1,171 @@
package com.tencent.qqnt.kernel.nativeinterface;
public class MemberExtInfoFilter {
public int cmdUinFlagExt3Grocery;
public int dataTime;
public int levelName;
public int levelNameNew;
public int memberIcon;
public int memberInfoSeq;
public int memberLevelInfoActiveDay;
public int memberLevelInfoLevel;
public int memberLevelInfoName;
public int memberLevelInfoPoint;
public int memberLevelInfoUin;
public int msgNeedField;
public int nickName;
public int specialTitle;
public int sysShowFlag;
public int timeToUpdate;
public int userShowFlag;
public int userShowFlagNew;
public int getCmdUinFlagExt3Grocery() {
return this.cmdUinFlagExt3Grocery;
}
public int getDataTime() {
return this.dataTime;
}
public int getLevelName() {
return this.levelName;
}
public int getLevelNameNew() {
return this.levelNameNew;
}
public int getMemberIcon() {
return this.memberIcon;
}
public int getMemberInfoSeq() {
return this.memberInfoSeq;
}
public int getMemberLevelInfoActiveDay() {
return this.memberLevelInfoActiveDay;
}
public int getMemberLevelInfoLevel() {
return this.memberLevelInfoLevel;
}
public int getMemberLevelInfoName() {
return this.memberLevelInfoName;
}
public int getMemberLevelInfoPoint() {
return this.memberLevelInfoPoint;
}
public int getMemberLevelInfoUin() {
return this.memberLevelInfoUin;
}
public int getMsgNeedField() {
return this.msgNeedField;
}
public int getNickName() {
return this.nickName;
}
public int getSpecialTitle() {
return this.specialTitle;
}
public int getSysShowFlag() {
return this.sysShowFlag;
}
public int getTimeToUpdate() {
return this.timeToUpdate;
}
public int getUserShowFlag() {
return this.userShowFlag;
}
public int getUserShowFlagNew() {
return this.userShowFlagNew;
}
public void setCmdUinFlagExt3Grocery(int i2) {
this.cmdUinFlagExt3Grocery = i2;
}
public void setDataTime(int i2) {
this.dataTime = i2;
}
public void setLevelName(int i2) {
this.levelName = i2;
}
public void setLevelNameNew(int i2) {
this.levelNameNew = i2;
}
public void setMemberIcon(int i2) {
this.memberIcon = i2;
}
public void setMemberInfoSeq(int i2) {
this.memberInfoSeq = i2;
}
public void setMemberLevelInfoActiveDay(int i2) {
this.memberLevelInfoActiveDay = i2;
}
public void setMemberLevelInfoLevel(int i2) {
this.memberLevelInfoLevel = i2;
}
public void setMemberLevelInfoName(int i2) {
this.memberLevelInfoName = i2;
}
public void setMemberLevelInfoPoint(int i2) {
this.memberLevelInfoPoint = i2;
}
public void setMemberLevelInfoUin(int i2) {
this.memberLevelInfoUin = i2;
}
public void setMsgNeedField(int i2) {
this.msgNeedField = i2;
}
public void setNickName(int i2) {
this.nickName = i2;
}
public void setSpecialTitle(int i2) {
this.specialTitle = i2;
}
public void setSysShowFlag(int i2) {
this.sysShowFlag = i2;
}
public void setTimeToUpdate(int i2) {
this.timeToUpdate = i2;
}
public void setUserShowFlag(int i2) {
this.userShowFlag = i2;
}
public void setUserShowFlagNew(int i2) {
this.userShowFlagNew = i2;
}
public String toString() {
return "MemberExtInfoFilter{memberLevelInfoUin=" + this.memberLevelInfoUin + ",memberLevelInfoPoint=" + this.memberLevelInfoPoint + ",memberLevelInfoActiveDay=" + this.memberLevelInfoActiveDay + ",memberLevelInfoLevel=" + this.memberLevelInfoLevel + ",memberLevelInfoName=" + this.memberLevelInfoName + ",levelName=" + this.levelName + ",dataTime=" + this.dataTime + ",userShowFlag=" + this.userShowFlag + ",sysShowFlag=" + this.sysShowFlag + ",timeToUpdate=" + this.timeToUpdate + ",nickName=" + this.nickName + ",specialTitle=" + this.specialTitle + ",levelNameNew=" + this.levelNameNew + ",userShowFlagNew=" + this.userShowFlagNew + ",msgNeedField=" + this.msgNeedField + ",cmdUinFlagExt3Grocery=" + this.cmdUinFlagExt3Grocery + ",memberIcon=" + this.memberIcon + ",memberInfoSeq=" + this.memberInfoSeq + ",}";
}
}

View File

@ -0,0 +1,29 @@
package com.tencent.qqnt.kernel.nativeinterface;
public class MemberIcon {
public int bizId;
public byte[] exInfo = new byte[0];
public long expireTime;
public int resId;
public int getBizId() {
return this.bizId;
}
public byte[] getExInfo() {
return this.exInfo;
}
public long getExpireTime() {
return this.expireTime;
}
public int getResId() {
return this.resId;
}
public String toString() {
return "MemberIcon{resId=" + this.resId + ",expireTime=" + this.expireTime + ",bizId=" + this.bizId + ",exInfo=" + this.exInfo + ",}";
}
}

View File

@ -1,51 +1,146 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
public final class MemberInfo implements IKernelModel { import com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole;
String avatarPath;
String cardName;
int cardType;
boolean isDelete;
boolean isSpecialConcerned;
String nick;
String qid;
String remark;
MemberRole role;
long serialVersionUID;
int shutUpTime;
String uid;
long uin;
public MemberInfo() { public final class MemberInfo implements IKernelModel {
this.serialVersionUID = 1L; public int bigClubFlag;
this.uid = ""; public int bigClubLevel;
this.qid = ""; public int cardNameId;
this.nick = ""; public int cardType;
this.remark = ""; public int creditLevel;
this.cardName = ""; public int globalGroupLevel;
this.role = MemberRole.values()[0]; public int globalGroupPoint;
this.avatarPath = ""; public boolean isDelete;
public boolean isRobot;
public boolean isSpecialConcerned;
public boolean isSpecialShielded;
public int joinTime;
public int lastSpeakTime;
public int memberFlag;
public int memberFlagExt;
public int memberFlagExt2;
public int memberLevel;
public int memberMobileFlag;
public int memberTitleId;
public int mssVipType;
public int richFlag;
public int shutUpTime;
public long specialTitleExpireTime;
public long uin;
public int userShowFlag;
public int userShowFlagNew;
long serialVersionUID = 1;
public String uid = "";
public String qid = "";
public String nick = "";
public String remark = "";
public String cardName = "";
public MemberRole role = MemberRole.values()[0];
public String avatarPath = "";
public byte[] groupHonor = new byte[0];
public String memberSpecialTitle = "";
public String autoRemark = "";
public String getAutoRemark() {
return this.autoRemark;
} }
public String getAvatarPath() { public String getAvatarPath() {
return this.avatarPath; return this.avatarPath;
} }
public int getBigClubFlag() {
return this.bigClubFlag;
}
public int getBigClubLevel() {
return this.bigClubLevel;
}
public String getCardName() { public String getCardName() {
return this.cardName; return this.cardName;
} }
public int getCardNameId() {
return this.cardNameId;
}
public int getCardType() { public int getCardType() {
return this.cardType; return this.cardType;
} }
public int getCreditLevel() {
return this.creditLevel;
}
public int getGlobalGroupLevel() {
return this.globalGroupLevel;
}
public int getGlobalGroupPoint() {
return this.globalGroupPoint;
}
public byte[] getGroupHonor() {
return this.groupHonor;
}
public boolean getIsDelete() { public boolean getIsDelete() {
return this.isDelete; return this.isDelete;
} }
public boolean getIsRobot() {
return this.isRobot;
}
public boolean getIsSpecialConcerned() { public boolean getIsSpecialConcerned() {
return this.isSpecialConcerned; return this.isSpecialConcerned;
} }
public boolean getIsSpecialShielded() {
return this.isSpecialShielded;
}
public int getJoinTime() {
return this.joinTime;
}
public int getLastSpeakTime() {
return this.lastSpeakTime;
}
public int getMemberFlag() {
return this.memberFlag;
}
public int getMemberFlagExt() {
return this.memberFlagExt;
}
public int getMemberFlagExt2() {
return this.memberFlagExt2;
}
public int getMemberLevel() {
return this.memberLevel;
}
public int getMemberMobileFlag() {
return this.memberMobileFlag;
}
public String getMemberSpecialTitle() {
return this.memberSpecialTitle;
}
public int getMemberTitleId() {
return this.memberTitleId;
}
public int getMssVipType() {
return this.mssVipType;
}
public String getNick() { public String getNick() {
return this.nick; return this.nick;
} }
@ -58,6 +153,10 @@ public final class MemberInfo implements IKernelModel {
return this.remark; return this.remark;
} }
public int getRichFlag() {
return this.richFlag;
}
public MemberRole getRole() { public MemberRole getRole() {
return this.role; return this.role;
} }
@ -66,6 +165,10 @@ public final class MemberInfo implements IKernelModel {
return this.shutUpTime; return this.shutUpTime;
} }
public long getSpecialTitleExpireTime() {
return this.specialTitleExpireTime;
}
public String getUid() { public String getUid() {
return this.uid; return this.uid;
} }
@ -74,26 +177,114 @@ public final class MemberInfo implements IKernelModel {
return this.uin; return this.uin;
} }
public int getUserShowFlag() {
return this.userShowFlag;
}
public int getUserShowFlagNew() {
return this.userShowFlagNew;
}
public void setAutoRemark(String str) {
this.autoRemark = str;
}
public void setAvatarPath(String str) { public void setAvatarPath(String str) {
this.avatarPath = str; this.avatarPath = str;
} }
public void setBigClubFlag(int i2) {
this.bigClubFlag = i2;
}
public void setBigClubLevel(int i2) {
this.bigClubLevel = i2;
}
public void setCardName(String str) { public void setCardName(String str) {
this.cardName = str; this.cardName = str;
} }
public void setCardNameId(int i2) {
this.cardNameId = i2;
}
public void setCardType(int i2) { public void setCardType(int i2) {
this.cardType = i2; this.cardType = i2;
} }
public void setCreditLevel(int i2) {
this.creditLevel = i2;
}
public void setGlobalGroupLevel(int i2) {
this.globalGroupLevel = i2;
}
public void setGlobalGroupPoint(int i2) {
this.globalGroupPoint = i2;
}
public void setGroupHonor(byte[] bArr) {
this.groupHonor = bArr;
}
public void setIsDelete(boolean z) { public void setIsDelete(boolean z) {
this.isDelete = z; this.isDelete = z;
} }
public void setIsRobot(boolean z) {
this.isRobot = z;
}
public void setIsSpecialConcerned(boolean z) { public void setIsSpecialConcerned(boolean z) {
this.isSpecialConcerned = z; this.isSpecialConcerned = z;
} }
public void setIsSpecialShielded(boolean z) {
this.isSpecialShielded = z;
}
public void setJoinTime(int i2) {
this.joinTime = i2;
}
public void setLastSpeakTime(int i2) {
this.lastSpeakTime = i2;
}
public void setMemberFlag(int i2) {
this.memberFlag = i2;
}
public void setMemberFlagExt(int i2) {
this.memberFlagExt = i2;
}
public void setMemberFlagExt2(int i2) {
this.memberFlagExt2 = i2;
}
public void setMemberLevel(int i2) {
this.memberLevel = i2;
}
public void setMemberMobileFlag(int i2) {
this.memberMobileFlag = i2;
}
public void setMemberSpecialTitle(String str) {
this.memberSpecialTitle = str;
}
public void setMemberTitleId(int i2) {
this.memberTitleId = i2;
}
public void setMssVipType(int i2) {
this.mssVipType = i2;
}
public void setNick(String str) { public void setNick(String str) {
this.nick = str; this.nick = str;
} }
@ -106,6 +297,10 @@ public final class MemberInfo implements IKernelModel {
this.remark = str; this.remark = str;
} }
public void setRichFlag(int i2) {
this.richFlag = i2;
}
public void setRole(MemberRole memberRole) { public void setRole(MemberRole memberRole) {
this.role = memberRole; this.role = memberRole;
} }
@ -114,6 +309,10 @@ public final class MemberInfo implements IKernelModel {
this.shutUpTime = i2; this.shutUpTime = i2;
} }
public void setSpecialTitleExpireTime(long j2) {
this.specialTitleExpireTime = j2;
}
public void setUid(String str) { public void setUid(String str) {
this.uid = str; this.uid = str;
} }
@ -122,30 +321,16 @@ public final class MemberInfo implements IKernelModel {
this.uin = j2; this.uin = j2;
} }
public String toString() { public void setUserShowFlag(int i2) {
return "MemberInfo{uid=" + this.uid + ",qid=" + this.qid + ",uin=" + this.uin + ",nick=" + this.nick + ",remark=" + this.remark + ",cardType=" + this.cardType + ",cardName=" + this.cardName + ",role=" + this.role + ",avatarPath=" + this.avatarPath + ",shutUpTime=" + this.shutUpTime + ",isDelete=" + this.isDelete + ",isSpecialConcerned=" + this.isSpecialConcerned + ",}"; this.userShowFlag = i2;
} }
public MemberInfo(String str, String str2, long j2, String str3, String str4, int i2, String str5, MemberRole memberRole, String str6, int i3, boolean z, boolean z2) { public void setUserShowFlagNew(int i2) {
this.serialVersionUID = 1L; this.userShowFlagNew = i2;
this.uid = "";
this.qid = "";
this.nick = "";
this.remark = "";
this.cardName = "";
this.role = MemberRole.values()[0];
this.avatarPath = "";
this.uid = str;
this.qid = str2;
this.uin = j2;
this.nick = str3;
this.remark = str4;
this.cardType = i2;
this.cardName = str5;
this.role = memberRole;
this.avatarPath = str6;
this.shutUpTime = i3;
this.isDelete = z;
this.isSpecialConcerned = z2;
} }
public String toString() {
return "MemberInfo{uid=" + this.uid + ",qid=" + this.qid + ",uin=" + this.uin + ",nick=" + this.nick + ",remark=" + this.remark + ",cardType=" + this.cardType + ",cardName=" + this.cardName + ",role=" + this.role + ",avatarPath=" + this.avatarPath + ",shutUpTime=" + this.shutUpTime + ",isDelete=" + this.isDelete + ",isSpecialConcerned=" + this.isSpecialConcerned + ",isRobot=" + this.isRobot + ",groupHonor=" + this.groupHonor + ",memberLevel=" + this.memberLevel + ",globalGroupLevel=" + this.globalGroupLevel + ",globalGroupPoint=" + this.globalGroupPoint + ",memberTitleId=" + this.memberTitleId + ",memberSpecialTitle=" + this.memberSpecialTitle + ",specialTitleExpireTime=" + this.specialTitleExpireTime + ",userShowFlag=" + this.userShowFlag + ",userShowFlagNew=" + this.userShowFlagNew + ",richFlag=" + this.richFlag + ",mssVipType=" + this.mssVipType + ",bigClubLevel=" + this.bigClubLevel + ",bigClubFlag=" + this.bigClubFlag + ",autoRemark=" + this.autoRemark + ",creditLevel=" + this.creditLevel + ",joinTime=" + this.joinTime + ",lastSpeakTime=" + this.lastSpeakTime + ",memberFlag=" + this.memberFlag + ",memberFlagExt=" + this.memberFlagExt + ",memberMobileFlag=" + this.memberMobileFlag + ",memberFlagExt2=" + this.memberFlagExt2 + ",isSpecialShielded=" + this.isSpecialShielded + ",cardNameId=" + this.cardNameId + ",}";
}
} }

View File

@ -0,0 +1,19 @@
package com.tencent.qqnt.kernel.nativeinterface;
public class MemberLevelName {
public int level;
public String strName = "";
public int getLevel() {
return this.level;
}
public String getStrName() {
return this.strName;
}
public String toString() {
return "MemberLevelName{level=" + this.level + ",strName=" + this.strName + ",}";
}
}

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;

View File

@ -1,6 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
public final class UnreadCntInfo { public final class UnreadCntInfo {
UnreadCnt allUnreadCnt; UnreadCnt allUnreadCnt;

View File

@ -1,4 +1,6 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernelpublic.nativeinterface;
import com.tencent.qqnt.kernel.nativeinterface.IKernelModel;
import java.io.Serializable; import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package com.tencent.qqnt.kernel.nativeinterface; package com.tencent.qqnt.kernelpublic.nativeinterface;
public enum MemberRole { public enum MemberRole {
UNSPECIFIED, UNSPECIFIED,

View File

@ -2,6 +2,7 @@ package com.tencent.qqnt.msg.api;
import com.tencent.mobileqq.qroute.QRouteApi; import com.tencent.mobileqq.qroute.QRouteApi;
import com.tencent.qqnt.kernel.nativeinterface.*; import com.tencent.qqnt.kernel.nativeinterface.*;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;

View File

@ -0,0 +1,75 @@
package com.tencent.qqnt.troopmemberlist;
import androidx.lifecycle.LifecycleOwner;
import com.tencent.mobileqq.data.troop.TroopMemberInfo;
import com.tencent.mobileqq.data.troop.TroopMemberNickInfo;
import com.tencent.mobileqq.qroute.QRouteApi;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import kotlin.Deprecated;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;
public interface ITroopMemberListRepoApi extends QRouteApi {
//void fetchGagTroopMemberInfo(@Nullable String str, @Nullable LifecycleOwner lifecycleOwner, boolean z, @NotNull String str2, @Nullable f fVar);
//void fetchTroopMemberInfo(@Nullable String str, @Nullable String str2, boolean z, @Nullable LifecycleOwner lifecycleOwner, @NotNull String str3, @Nullable g gVar);
//void fetchTroopMemberInfoWithExtInfo(@Nullable String str, @Nullable String str2, boolean z, @Nullable LifecycleOwner lifecycleOwner, @NotNull String str3, @Nullable g gVar);
//void fetchTroopMemberList(@Nullable String str, @Nullable LifecycleOwner lifecycleOwner, boolean z, @NotNull String str2, @Nullable f fVar);
//void fetchTroopMemberListWithExtInfo(@Nullable String str, @Nullable LifecycleOwner lifecycleOwner, boolean z, @NotNull String str2, @Nullable f fVar);
void fetchTroopMemberName(@Nullable String str, @Nullable String str2, @Nullable LifecycleOwner lifecycleOwner, @NotNull String str3, @Nullable Function1<? super TroopMemberNickInfo, Unit> cb);
void fetchTroopMemberName(@Nullable String str, @Nullable String str2, @NotNull String str3, @Nullable Function1<? super TroopMemberNickInfo, Unit> cb);
void fetchTroopMemberUid(@Nullable String str, @NotNull Function2<? super Boolean, ? super String, Unit> function2);
void fetchTroopMemberUid(@NotNull List<String> list, @NotNull Function2<? super Boolean, ? super Map<String, String>, Unit> function2);
void fetchTroopMemberUin(@Nullable String str, @NotNull Function2<? super Boolean, ? super String, Unit> function2);
void fetchTroopMemberUin(@NotNull List<String> list, @NotNull Function2<? super Boolean, ? super Map<String, String>, Unit> function2);
//void fetchTroopMemberUinListInfo(@Nullable String str, @Nullable List<String> list, boolean z, @Nullable LifecycleOwner lifecycleOwner, @NotNull String str2, @Nullable f fVar);
//void fetchTroopMemberUinListInfoWithExtInfo(@Nullable String str, @Nullable List<String> list, boolean z, @Nullable LifecycleOwner lifecycleOwner, @NotNull String str2, @Nullable f fVar);
//@Nullable
//TroopMemberInfo getTroopMemberFromCacheOrFetchAsync(@Nullable String str, @Nullable String str2, @Nullable LifecycleOwner lifecycleOwner, @NotNull String str3, @Nullable g gVar);
int getTroopMemberInfoDBVersion();
@Deprecated(message = "兼容旧逻辑,过渡用,新逻辑不要使用")
@Nullable
TroopMemberInfo getTroopMemberInfoSync(@Nullable String groupId, @Nullable String userId, @Nullable LifecycleOwner lifecycleOwner, @NotNull String from);
//@Deprecated(message = "兼容旧逻辑,过渡用,新逻辑不要使用")
//@Nullable
//TroopMemberInfo getTroopMemberInfoSync(@Nullable String str, @Nullable String str2, @Nullable LifecycleOwner lifecycleOwner, @NotNull String str3, long j);
//@Deprecated(message = "兼容旧逻辑,过渡用,新逻辑不要使用")
//@Nullable
//TroopMemberInfo getTroopMemberWithExtFromCacheOrFetchAsync(@Nullable String str, @Nullable String str2, @Nullable LifecycleOwner lifecycleOwner, @NotNull String str3, @Nullable g gVar);
//@Deprecated(message = "兼容旧逻辑,过渡用,新逻辑不要使用")
//@Nullable
//TroopMemberInfo getTroopMemberWithExtInfoSync(@Nullable String str, @Nullable String str2, @Nullable LifecycleOwner lifecycleOwner, @NotNull String str3);
boolean isTroopMemberInfoDBInited(@NotNull String str);
//void preLoadTroopMemberUinListInfo(@Nullable String str, @Nullable List<String> list, boolean z, @Nullable LifecycleOwner lifecycleOwner, @NotNull String str2, @Nullable f fVar);
}

View File

@ -1,201 +0,0 @@
@file:OptIn(DelicateCoroutinesApi::class)
package moe.fuqiuluo.qqinterface.servlet
import android.os.Bundle
import com.tencent.mobileqq.app.QQAppInterface
import com.tencent.mobileqq.msf.core.MsfCore
import com.tencent.mobileqq.pb.ByteStringMicro
import com.tencent.qphone.base.remote.ToServiceMsg
import io.ktor.utils.io.core.BytePacketBuilder
import io.ktor.utils.io.core.readBytes
import io.ktor.utils.io.core.writeFully
import io.ktor.utils.io.core.writeInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.encodeToByteArray
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import moe.fuqiuluo.shamrock.xposed.helper.internal.DynamicReceiver
import moe.fuqiuluo.shamrock.xposed.helper.internal.IPCRequest
import protobuf.oidb.TrpcOidb
import mqq.app.MobileQQ
import protobuf.auto.toByteArray
import tencent.im.oidb.oidb_sso
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.resume
internal abstract class BaseSvc {
companion object Default: CoroutineScope {
val currentUin: String
get() = app.currentAccountUin
val app: QQAppInterface
get() = AppRuntimeFetcher.appRuntime as QQAppInterface
fun createToServiceMsg(cmd: String): ToServiceMsg {
return ToServiceMsg("mobileqq.service", app.currentAccountUin, cmd)
}
suspend fun sendOidbAW(cmd: String, cmdId: Int, serviceId: Int, data: ByteArray, trpc: Boolean = false, timeout: Long = 5000L): ByteArray? {
val seq = MsfCore.getNextSeq()
val buffer = withTimeoutOrNull(timeout) {
suspendCancellableCoroutine { continuation ->
launch(Dispatchers.Default) {
DynamicReceiver.register(IPCRequest(cmd, seq) {
val buffer = it.getByteArrayExtra("buffer")!!
continuation.resume(buffer)
})
}
if (trpc) sendTrpcOidb(cmd, cmdId, serviceId, data, seq)
else sendOidb(cmd, cmdId, serviceId, data, seq)
}
}.also {
if (it == null)
DynamicReceiver.unregister(seq)
}?.copyOf()
try {
if (buffer != null && buffer.size >= 5 && buffer[4] == 120.toByte()) {
val builder = BytePacketBuilder()
val deBuffer = DeflateTools.uncompress(buffer.slice(4))
builder.writeInt(deBuffer.size)
builder.writeFully(deBuffer)
return builder.build().readBytes()
}
} catch (_: Exception) { }
return buffer
}
suspend fun sendBufferAW(cmd: String, isPb: Boolean, data: ByteArray, timeout: Long = 5000L): ByteArray? {
val seq = MsfCore.getNextSeq()
val buffer = withTimeoutOrNull<ByteArray?>(timeout) {
suspendCancellableCoroutine { continuation ->
launch(Dispatchers.Default) {
DynamicReceiver.register(IPCRequest(cmd, seq) {
val buffer = it.getByteArrayExtra("buffer")!!
continuation.resume(buffer)
})
sendBuffer(cmd, isPb, data, seq)
}
}
}.also {
if (it == null)
DynamicReceiver.unregister(seq)
}?.copyOf()
try {
if (buffer != null && buffer.size >= 5 && buffer[4] == 120.toByte()) {
val builder = BytePacketBuilder()
val deBuffer = DeflateTools.uncompress(buffer.slice(4))
builder.writeInt(deBuffer.size)
builder.writeFully(deBuffer)
return builder.build().readBytes()
}
} catch (_: Exception) { }
return buffer
}
fun sendOidb(cmd: String, cmdId: Int, serviceId: Int, buffer: ByteArray, seq: Int = -1, trpc: Boolean = false) {
if (trpc) {
sendTrpcOidb(cmd, cmdId, serviceId, buffer, seq)
return
}
val to = createToServiceMsg(cmd)
val oidb = oidb_sso.OIDBSSOPkg()
oidb.uint32_command.set(cmdId)
oidb.uint32_service_type.set(serviceId)
oidb.bytes_bodybuffer.set(ByteStringMicro.copyFrom(buffer))
oidb.str_client_version.set(PlatformUtils.getClientVersion(MobileQQ.getContext()))
to.putWupBuffer(oidb.toByteArray())
to.addAttribute("req_pb_protocol_flag", true)
if (seq != -1) {
to.addAttribute("shamrock_seq", seq)
}
app.sendToService(to)
}
fun sendTrpcOidb(cmd: String, cmdId: Int, serviceId: Int, buffer: ByteArray, seq: Int = -1) {
val to = createToServiceMsg(cmd)
val oidb = TrpcOidb(
cmd = cmdId,
service = serviceId,
buffer = buffer,
flag = 1
)
to.putWupBuffer(oidb.toByteArray())
to.addAttribute("req_pb_protocol_flag", true)
if (seq != -1) {
to.addAttribute("shamrock_seq", seq)
}
app.sendToService(to)
}
fun sendBuffer(cmd: String, isPb: Boolean, buffer: ByteArray, seq: Int = MsfCore.getNextSeq()) {
val toServiceMsg = ToServiceMsg("mobileqq.service", app.currentUin, cmd)
toServiceMsg.putWupBuffer(buffer)
toServiceMsg.addAttribute("req_pb_protocol_flag", isPb)
toServiceMsg.addAttribute("shamrock_seq", seq)
app.sendToService(toServiceMsg)
}
@OptIn(ExperimentalCoroutinesApi::class)
override val coroutineContext: CoroutineContext by lazy {
Dispatchers.IO.limitedParallelism(12)
}
}
protected fun send(toServiceMsg: ToServiceMsg) {
app.sendToService(toServiceMsg)
}
protected suspend fun sendAW(toServiceMsg: ToServiceMsg, timeout: Long = 5000L): ByteArray? {
val seq = MsfCore.getNextSeq()
val buffer = withTimeoutOrNull<ByteArray?>(timeout) {
suspendCancellableCoroutine { continuation ->
launch(Dispatchers.Default) {
DynamicReceiver.register(IPCRequest(toServiceMsg.serviceCmd, seq) {
val buffer = it.getByteArrayExtra("buffer")!!
continuation.resume(buffer)
})
toServiceMsg.addAttribute("shamrock_seq", seq)
send(toServiceMsg)
}
}
}.also {
if (it == null) DynamicReceiver.unregister(seq)
}?.copyOf()
try {
if (buffer != null && buffer.size >= 5 && buffer[4] == 120.toByte()) {
val builder = BytePacketBuilder()
val deBuffer = DeflateTools.uncompress(buffer.slice(4))
builder.writeInt(deBuffer.size)
builder.writeFully(deBuffer)
return builder.build().readBytes()
}
} catch (_: Exception) { }
return buffer
}
protected fun sendExtra(cmd: String, builder: (Bundle) -> Unit) {
val toServiceMsg = createToServiceMsg(cmd)
builder(toServiceMsg.extraData)
app.sendToService(toServiceMsg)
}
protected fun sendPb(cmd: String, buffer: ByteArray, seq: Int) {
val toServiceMsg = createToServiceMsg(cmd)
toServiceMsg.putWupBuffer(buffer)
toServiceMsg.addAttribute("req_pb_protocol_flag", true)
toServiceMsg.addAttribute("shamrock_seq", seq)
app.sendToService(toServiceMsg)
}
}

View File

@ -21,12 +21,14 @@ import moe.fuqiuluo.shamrock.tools.json
import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.decodeToOidb
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import mqq.app.Packet import mqq.app.Packet
import tencent.im.oidb.cmd0x11b2.oidb_0x11b2 import tencent.im.oidb.cmd0x11b2.oidb_0x11b2
import tencent.im.oidb.oidb_sso import tencent.im.oidb.oidb_sso
import kotlin.coroutines.resume import kotlin.coroutines.resume
internal object CardSvc: BaseSvc() { internal object CardSvc: QQInterfaces() {
private val GetModelShowLock by lazy { private val GetModelShowLock by lazy {
Mutex() Mutex()
} }
@ -46,7 +48,8 @@ internal object CardSvc: BaseSvc() {
val resp = sendBufferAW("VipCustom.GetCustomOnlineStatus", false, uniPacket.encode()) val resp = sendBufferAW("VipCustom.GetCustomOnlineStatus", false, uniPacket.encode())
?: error("unable to fetch contact model_show") ?: error("unable to fetch contact model_show")
Packet.decodePacket(resp, "rsp", GetCustomOnlineStatusRsp()).sBuffer val buffer = resp.wupBuffer
Packet.decodePacket(buffer, "rsp", GetCustomOnlineStatusRsp()).sBuffer
} }
} }
@ -79,10 +82,9 @@ internal object CardSvc: BaseSvc() {
reqBody.uin.set(peerId) reqBody.uin.set(peerId)
reqBody.jump_url.set("mqqapi://card/show_pslcard?src_type=internal&source=sharecard&version=1&uin=$peerId") reqBody.jump_url.set("mqqapi://card/show_pslcard?src_type=internal&source=sharecard&version=1&uin=$peerId")
val buffer = sendOidbAW("OidbSvcTrpcTcp.0x11ca_0", 4790, 0, reqBody.toByteArray()) val fromServiceMsg = sendOidbAW("OidbSvcTrpcTcp.0x11ca_0", 4790, 0, reqBody.toByteArray())
?: error("unable to fetch contact ark_json_text") ?: error("unable to fetch contact ark_json_text")
val body = oidb_sso.OIDBSSOPkg() val body = fromServiceMsg.decodeToOidb()
body.mergeFrom(buffer.slice(4))
val rsp = oidb_0x11b2.BusinessCardV3Rsp() val rsp = oidb_0x11b2.BusinessCardV3Rsp()
rsp.mergeFrom(body.bytes_bodybuffer.get().toByteArray()) rsp.mergeFrom(body.bytes_bodybuffer.get().toByteArray())
return rsp.signed_ark_msg.get() return rsp.signed_ark_msg.get()

View File

@ -1,11 +1,12 @@
package moe.fuqiuluo.qqinterface.servlet package moe.fuqiuluo.qqinterface.servlet
import kotlinx.serialization.encodeToByteArray import kotlinx.serialization.encodeToByteArray
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import protobuf.auto.toByteArray import protobuf.auto.toByteArray
import protobuf.oidb.cmd0x9082.Oidb0x9082 import protobuf.oidb.cmd0x9082.Oidb0x9082
internal object ChatSvc: BaseSvc() { internal object ChatSvc: QQInterfaces() {
fun setGroupMessageCommentFace(peer: Long, msgSeq: ULong, faceIndex: String, isSet: Boolean) { fun setGroupMessageCommentFace(peer: Long, msgSeq: ULong, faceIndex: String, isSet: Boolean) {
val serviceId = if (isSet) 1 else 2 val serviceId = if (isSet) 1 else 2
sendOidb("OidbSvcTrpcTcp.0x9082_$serviceId", 36994, serviceId, Oidb0x9082( sendOidb("OidbSvcTrpcTcp.0x9082_$serviceId", 36994, serviceId, Oidb0x9082(

View File

@ -6,9 +6,11 @@ import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
import moe.fuqiuluo.shamrock.tools.decodeToOidb
import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.tools.toHexString import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.shamrock.utils.DeflateTools import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import moe.fuqiuluo.symbols.decodeProtobuf import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.oidb.cmd0x6d7.CreateFolderReq import protobuf.oidb.cmd0x6d7.CreateFolderReq
import protobuf.oidb.cmd0x6d7.DeleteFolderReq import protobuf.oidb.cmd0x6d7.DeleteFolderReq
@ -21,8 +23,10 @@ import tencent.im.oidb.cmd0x6d8.oidb_0x6d8
import tencent.im.oidb.oidb_sso import tencent.im.oidb.oidb_sso
import protobuf.group_file_common.FolderInfo as GroupFileCommonFolderInfo import protobuf.group_file_common.FolderInfo as GroupFileCommonFolderInfo
import protobuf.auto.toByteArray import protobuf.auto.toByteArray
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
internal object FileSvc: BaseSvc() { internal object FileSvc: QQInterfaces() {
suspend fun createFileFolder(groupId: Long, folderName: String, parentFolderId: String = "/"): Result<GroupFileCommonFolderInfo> { suspend fun createFileFolder(groupId: Long, folderName: String, parentFolderId: String = "/"): Result<GroupFileCommonFolderInfo> {
val data = Oidb0x6d7ReqBody( val data = Oidb0x6d7ReqBody(
createFolder = CreateFolderReq( createFolder = CreateFolderReq(
@ -32,10 +36,9 @@ internal object FileSvc: BaseSvc() {
folderName = folderName folderName = folderName
) )
).toByteArray() ).toByteArray()
val resultBuffer = sendOidbAW("OidbSvc.0x6d7_0", 1751, 0, data) val fromServiceMsg = sendOidbAW("OidbSvc.0x6d7_0", 1751, 0, data)
?: return Result.failure(Exception("unable to fetch result")) ?: return Result.failure(Exception("unable to fetch result"))
val oidbPkg = oidb_sso.OIDBSSOPkg() val oidbPkg = fromServiceMsg.decodeToOidb()
oidbPkg.mergeFrom(resultBuffer.slice(4))
val rsp = oidbPkg.bytes_bodybuffer.get() val rsp = oidbPkg.bytes_bodybuffer.get()
.toByteArray() .toByteArray()
.decodeProtobuf<Oidb0x6d7RespBody>() .decodeProtobuf<Oidb0x6d7RespBody>()
@ -46,21 +49,20 @@ internal object FileSvc: BaseSvc() {
} }
suspend fun deleteGroupFolder(groupId: Long, folderUid: String): Boolean { suspend fun deleteGroupFolder(groupId: Long, folderUid: String): Boolean {
val buffer = sendOidbAW("OidbSvc.0x6d7_1", 1751, 1, Oidb0x6d7ReqBody( val fromServiceMsg = sendOidbAW("OidbSvc.0x6d7_1", 1751, 1, Oidb0x6d7ReqBody(
deleteFolder = DeleteFolderReq( deleteFolder = DeleteFolderReq(
groupCode = groupId.toULong(), groupCode = groupId.toULong(),
appId = 3u, appId = 3u,
folderId = folderUid folderId = folderUid
) )
).toByteArray()) ?: return false ).toByteArray()) ?: return false
val oidbPkg = oidb_sso.OIDBSSOPkg() val oidbPkg = fromServiceMsg.decodeToOidb()
oidbPkg.mergeFrom(buffer.slice(4))
val rsp = oidbPkg.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0x6d7RespBody>() val rsp = oidbPkg.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0x6d7RespBody>()
return rsp.deleteFolder?.retCode == 0 return rsp.deleteFolder?.retCode == 0
} }
suspend fun moveGroupFolder(groupId: Long, folderUid: String, newParentFolderUid: String): Boolean { suspend fun moveGroupFolder(groupId: Long, folderUid: String, newParentFolderUid: String): Boolean {
val buffer = sendOidbAW("OidbSvc.0x6d7_2", 1751, 2, Oidb0x6d7ReqBody( val fromServiceMsg = sendOidbAW("OidbSvc.0x6d7_2", 1751, 2, Oidb0x6d7ReqBody(
moveFolder = MoveFolderReq( moveFolder = MoveFolderReq(
groupCode = groupId.toULong(), groupCode = groupId.toULong(),
appId = 3u, appId = 3u,
@ -68,14 +70,13 @@ internal object FileSvc: BaseSvc() {
parentFolderId = "/" parentFolderId = "/"
) )
).toByteArray()) ?: return false ).toByteArray()) ?: return false
val oidbPkg = oidb_sso.OIDBSSOPkg() val oidbPkg = fromServiceMsg.decodeToOidb()
oidbPkg.mergeFrom(buffer.slice(4))
val rsp = oidbPkg.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0x6d7RespBody>() val rsp = oidbPkg.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0x6d7RespBody>()
return rsp.moveFolder?.retCode == 0 return rsp.moveFolder?.retCode == 0
} }
suspend fun renameFolder(groupId: Long, folderUid: String, name: String): Boolean { suspend fun renameFolder(groupId: Long, folderUid: String, name: String): Boolean {
val buffer = sendOidbAW("OidbSvc.0x6d7_3", 1751, 3, Oidb0x6d7ReqBody( val fromServiceMsg = sendOidbAW("OidbSvc.0x6d7_3", 1751, 3, Oidb0x6d7ReqBody(
renameFolder = RenameFolderReq( renameFolder = RenameFolderReq(
groupCode = groupId.toULong(), groupCode = groupId.toULong(),
appId = 3u, appId = 3u,
@ -83,8 +84,7 @@ internal object FileSvc: BaseSvc() {
folderName = name folderName = name
) )
).toByteArray()) ?: return false ).toByteArray()) ?: return false
val oidbPkg = oidb_sso.OIDBSSOPkg() val oidbPkg = fromServiceMsg.decodeToOidb()
oidbPkg.mergeFrom(buffer.slice(4))
val rsp = oidbPkg.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0x6d7RespBody>() val rsp = oidbPkg.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0x6d7RespBody>()
return rsp.renameFolder?.retCode == 0 return rsp.renameFolder?.retCode == 0
} }
@ -101,8 +101,7 @@ internal object FileSvc: BaseSvc() {
} }
val result = sendOidbAW("OidbSvc.0x6d6_3", 1750, 3, oidb0x6d6ReqBody.toByteArray()) val result = sendOidbAW("OidbSvc.0x6d6_3", 1750, 3, oidb0x6d6ReqBody.toByteArray())
?: return false ?: return false
val oidbPkg = oidb_sso.OIDBSSOPkg() val oidbPkg = result.decodeToOidb()
oidbPkg.mergeFrom(result.slice(4))
val rsp = oidb_0x6d6.RspBody().apply { val rsp = oidb_0x6d6.RspBody().apply {
mergeFrom(oidbPkg.bytes_bodybuffer.get().toByteArray()) mergeFrom(oidbPkg.bytes_bodybuffer.get().toByteArray())
} }
@ -120,8 +119,8 @@ internal object FileSvc: BaseSvc() {
val fileCnt: Int val fileCnt: Int
val limitCnt: Int val limitCnt: Int
if (rspGetFileCntBuffer != null) { if (rspGetFileCntBuffer != null) {
oidb_0x6d8.RspBody().mergeFrom(oidb_sso.OIDBSSOPkg() oidb_0x6d8.RspBody().mergeFrom(
.mergeFrom(rspGetFileCntBuffer.slice(4)) rspGetFileCntBuffer.decodeToOidb()
.bytes_bodybuffer.get() .bytes_bodybuffer.get()
.toByteArray() .toByteArray()
).group_file_cnt_rsp.apply { ).group_file_cnt_rsp.apply {
@ -141,8 +140,8 @@ internal object FileSvc: BaseSvc() {
val totalSpace: Long val totalSpace: Long
val usedSpace: Long val usedSpace: Long
if (rspGetFileSpaceBuffer != null) { if (rspGetFileSpaceBuffer != null) {
oidb_0x6d8.RspBody().mergeFrom(oidb_sso.OIDBSSOPkg() oidb_0x6d8.RspBody().mergeFrom(
.mergeFrom(rspGetFileSpaceBuffer.slice(4)) rspGetFileSpaceBuffer.decodeToOidb()
.bytes_bodybuffer.get() .bytes_bodybuffer.get()
.toByteArray()).group_space_rsp.apply { .toByteArray()).group_space_rsp.apply {
totalSpace = uint64_total_space.get() totalSpace = uint64_total_space.get()
@ -187,15 +186,13 @@ internal object FileSvc: BaseSvc() {
uint32_show_onlinedoc_folder.set(0) uint32_show_onlinedoc_folder.set(0)
}) })
}.toByteArray(), timeout = 15_000L) }.toByteArray(), timeout = 15.seconds)
return kotlin.runCatching { return kotlin.runCatching {
val files = arrayListOf<FileInfo>() val files = arrayListOf<FileInfo>()
val dirs = arrayListOf<FolderInfo>() val dirs = arrayListOf<FolderInfo>()
if (rspGetFileListBuffer != null) { if (rspGetFileListBuffer != null) {
val oidb = oidb_sso.OIDBSSOPkg().mergeFrom(rspGetFileListBuffer.slice(4).let { val oidb = rspGetFileListBuffer.decodeToOidb()
if (it[0] == 0x78.toByte()) DeflateTools.uncompress(it) else it
})
oidb_0x6d8.RspBody().mergeFrom(oidb.bytes_bodybuffer.get().toByteArray()) oidb_0x6d8.RspBody().mergeFrom(oidb.bytes_bodybuffer.get().toByteArray())
.file_list_info_rsp.apply { .file_list_info_rsp.apply {
@ -242,7 +239,7 @@ internal object FileSvc: BaseSvc() {
GroupFileList(files, dirs) GroupFileList(files, dirs)
}.onFailure { }.onFailure {
LogCenter.log(it.message + ", buffer: ${rspGetFileListBuffer.toHexString()}", Level.ERROR) LogCenter.log(it.message + ", buffer: ${rspGetFileListBuffer?.wupBuffer?.toHexString()}", Level.ERROR)
} }
} }
} }

View File

@ -13,13 +13,15 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import moe.fuqiuluo.shamrock.tools.decodeToObject
import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import mqq.app.AppRuntime import mqq.app.AppRuntime
import tencent.mobileim.structmsg.structmsg import tencent.mobileim.structmsg.structmsg
import kotlin.coroutines.resume import kotlin.coroutines.resume
internal object FriendSvc: BaseSvc() { internal object FriendSvc: QQInterfaces() {
suspend fun getFriendList(refresh: Boolean): Result<List<Friends>> { suspend fun getFriendList(refresh: Boolean): Result<List<Friends>> {
val runtime = AppRuntimeFetcher.appRuntime val runtime = AppRuntimeFetcher.appRuntime
@ -91,8 +93,7 @@ internal object FriendSvc: BaseSvc() {
ArrayList() ArrayList()
} else { } else {
try { try {
val msg = structmsg.RspSystemMsgNew() val msg = respBuffer.decodeToObject(structmsg.RspSystemMsgNew())
msg.mergeFrom(respBuffer.slice(4))
return msg.friendmsgs.get() return msg.friendmsgs.get()
} catch (err: Throwable) { } catch (err: Throwable) {
requestFriendSystemMsgNew(msgNum, latestFriendSeq, latestGroupSeq, retryCnt - 1) requestFriendSystemMsgNew(msgNum, latestFriendSeq, latestGroupSeq, retryCnt - 1)

View File

@ -19,9 +19,12 @@ import moe.fuqiuluo.qqinterface.servlet.structures.SlowModeInfo
import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
import moe.fuqiuluo.shamrock.tools.decodeToObject
import moe.fuqiuluo.shamrock.tools.decodeToOidb
import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.utils.PlatformUtils import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import moe.fuqiuluo.symbols.decodeProtobuf import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.auto.toByteArray import protobuf.auto.toByteArray
import protobuf.guild.GetGuildFeedsReq import protobuf.guild.GetGuildFeedsReq
@ -44,7 +47,7 @@ import protobuf.qweb.QWebRsp
import tencent.im.oidb.oidb_sso import tencent.im.oidb.oidb_sso
import kotlin.coroutines.resume import kotlin.coroutines.resume
internal object GProSvc: BaseSvc() { internal object GProSvc: QQInterfaces() {
fun getSelfTinyId(): ULong { fun getSelfTinyId(): ULong {
val service = app.getRuntimeService(IGPSService::class.java, "all") val service = app.getRuntimeService(IGPSService::class.java, "all")
return service.selfTinyId.toULong() return service.selfTinyId.toULong()
@ -57,12 +60,8 @@ internal object GProSvc: BaseSvc() {
u2 = Oidb0xf57U2(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u) u2 = Oidb0xf57U2(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u)
), ),
guildInfo = Oidb0xf57GuildInfo(guildId = guildId) guildInfo = Oidb0xf57GuildInfo(guildId = guildId)
).toByteArray()) ).toByteArray()) ?: return Result.failure(Exception("unable to send packet"))
val body = oidb_sso.OIDBSSOPkg() val body = respBuffer.decodeToOidb()
if (respBuffer == null) {
return Result.failure(Exception("unable to send packet"))
}
body.mergeFrom(respBuffer.slice(4))
return runCatching { return runCatching {
body.bytes_bodybuffer.get() body.bytes_bodybuffer.get()
.toByteArray() .toByteArray()
@ -71,7 +70,7 @@ internal object GProSvc: BaseSvc() {
} }
suspend fun getGuildFeeds(guildId: ULong, channelId: ULong, startIndex: Int): Result<GetGuildFeedsRsp> { suspend fun getGuildFeeds(guildId: ULong, channelId: ULong, startIndex: Int): Result<GetGuildFeedsRsp> {
val buffer = sendBufferAW("QChannelSvr.trpc.qchannel.commreader.ComReader.GetGuildFeeds", true, QWebReq( val fromServiceMsg = sendBufferAW("QChannelSvr.trpc.qchannel.commreader.ComReader.GetGuildFeeds", true, QWebReq(
seq = 10, seq = 10,
qua = PlatformUtils.getQUA(), qua = PlatformUtils.getQUA(),
deviceInfo = DEFAULT_DEVICE_INFO, deviceInfo = DEFAULT_DEVICE_INFO,
@ -92,7 +91,7 @@ internal object GProSvc: BaseSvc() {
QWebExtInfo("tiny_id", getSelfTinyId().toString()), QWebExtInfo("tiny_id", getSelfTinyId().toString()),
) )
).toByteArray()) ?: return Result.failure(Exception("unable to send packet")) ).toByteArray()) ?: return Result.failure(Exception("unable to send packet"))
val webRsp = buffer.slice(4).decodeProtobuf<QWebRsp>() val webRsp = fromServiceMsg.decodeToObject<QWebRsp>()
if(webRsp.buffer == null) return Result.failure(Exception("server error")) if(webRsp.buffer == null) return Result.failure(Exception("server error"))
val wupBuffer = webRsp.buffer!! val wupBuffer = webRsp.buffer!!
val feeds = wupBuffer.decodeProtobuf<GetGuildFeedsRsp>() val feeds = wupBuffer.decodeProtobuf<GetGuildFeedsRsp>()
@ -188,12 +187,8 @@ internal object GProSvc: BaseSvc() {
memberId = 0uL, memberId = 0uL,
tinyId = memberTinyId, tinyId = memberTinyId,
guildId = guildId guildId = guildId
).toByteArray()) ).toByteArray()) ?: return Result.failure(Exception("unable to send packet"))
val body = oidb_sso.OIDBSSOPkg() val body = respBuffer.decodeToOidb()
if (respBuffer == null) {
return Result.failure(Exception("unable to send packet"))
}
body.mergeFrom(respBuffer.slice(4))
return runCatching { return runCatching {
body.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0xf88Rsp>().userInfo!! body.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0xf88Rsp>().userInfo!!
} }

View File

@ -10,13 +10,16 @@ import com.tencent.mobileqq.app.BusinessHandlerFactory
import com.tencent.mobileqq.app.QQAppInterface import com.tencent.mobileqq.app.QQAppInterface
import com.tencent.mobileqq.data.troop.TroopInfo import com.tencent.mobileqq.data.troop.TroopInfo
import com.tencent.mobileqq.data.troop.TroopMemberInfo import com.tencent.mobileqq.data.troop.TroopMemberInfo
import com.tencent.mobileqq.data.troop.TroopMemberNickInfo
import com.tencent.mobileqq.pb.ByteStringMicro import com.tencent.mobileqq.pb.ByteStringMicro
import com.tencent.mobileqq.qroute.QRoute
import com.tencent.mobileqq.troop.api.ITroopInfoService import com.tencent.mobileqq.troop.api.ITroopInfoService
import com.tencent.mobileqq.troop.api.ITroopMemberInfoService import com.tencent.mobileqq.troop.api.ITroopMemberInfoService
import com.tencent.protofile.join_group_link.join_group_link import com.tencent.protofile.join_group_link.join_group_link
import com.tencent.qphone.base.remote.ToServiceMsg import com.tencent.qphone.base.remote.ToServiceMsg
import com.tencent.qqnt.kernel.nativeinterface.MemberInfo import com.tencent.qqnt.kernel.nativeinterface.MemberInfo
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.troopmemberlist.ITroopMemberListRepoApi
import friendlist.stUinInfo import friendlist.stUinInfo
import io.ktor.client.call.body import io.ktor.client.call.body
import io.ktor.client.request.forms.MultiPartFormDataContent import io.ktor.client.request.forms.MultiPartFormDataContent
@ -41,12 +44,10 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import moe.fuqiuluo.qqinterface.servlet.TicketSvc.getLongUin import moe.fuqiuluo.qqinterface.servlet.TicketSvc.getLongUin
import moe.fuqiuluo.qqinterface.servlet.TicketSvc.getUin import moe.fuqiuluo.qqinterface.servlet.TicketSvc.getUin
import moe.fuqiuluo.qqinterface.servlet.structures.GroupAtAllRemainInfo import moe.fuqiuluo.qqinterface.servlet.structures.GroupAtAllRemainInfo
@ -55,6 +56,7 @@ import moe.fuqiuluo.qqinterface.servlet.structures.ProhibitedMemberInfo
import moe.fuqiuluo.shamrock.helper.Level 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.helper.MessageHelper
import moe.fuqiuluo.shamrock.remote.action.handlers.GetTroopMemberInfo
import moe.fuqiuluo.shamrock.remote.service.data.EssenceMessage import moe.fuqiuluo.shamrock.remote.service.data.EssenceMessage
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncement import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncement
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessage import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessage
@ -68,17 +70,21 @@ import moe.fuqiuluo.shamrock.tools.asJsonObject
import moe.fuqiuluo.shamrock.tools.asLong import moe.fuqiuluo.shamrock.tools.asLong
import moe.fuqiuluo.shamrock.tools.asString import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.asStringOrNull import moe.fuqiuluo.shamrock.tools.asStringOrNull
import moe.fuqiuluo.shamrock.tools.decodeToObject
import moe.fuqiuluo.shamrock.tools.decodeToOidb
import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty
import moe.fuqiuluo.shamrock.tools.putBuf32Long import moe.fuqiuluo.shamrock.tools.putBuf32Long
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.PlatformUtils import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.utils.PlatformUtils.QQ_9_0_65_VER
import moe.fuqiuluo.shamrock.utils.PlatformUtils.QQ_9_0_8_VER
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import protobuf.oidb.cmd0xf16.Oidb0xf16 import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import protobuf.oidb.cmd0xf16.SetGroupRemarkReq
import mqq.app.MobileQQ import mqq.app.MobileQQ
import protobuf.auto.toByteArray import protobuf.auto.toByteArray
import protobuf.oidb.cmd0xf16.Oidb0xf16
import protobuf.oidb.cmd0xf16.SetGroupRemarkReq
import tencent.im.group.group_member_info import tencent.im.group.group_member_info
import tencent.im.oidb.cmd0x88d.oidb_0x88d import tencent.im.oidb.cmd0x88d.oidb_0x88d
import tencent.im.oidb.cmd0x899.oidb_0x899 import tencent.im.oidb.cmd0x899.oidb_0x899
@ -96,8 +102,9 @@ import java.lang.reflect.Method
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import java.nio.ByteBuffer import java.nio.ByteBuffer
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.time.Duration.Companion.seconds
internal object GroupSvc: BaseSvc() { internal object GroupSvc: QQInterfaces() {
private const val GET_MEMBER_ROLE_BY_NT = false private const val GET_MEMBER_ROLE_BY_NT = false
private val RefreshTroopMemberInfoLock by lazy { private val RefreshTroopMemberInfoLock by lazy {
@ -114,15 +121,14 @@ internal object GroupSvc: BaseSvc() {
private lateinit var METHOD_REQ_MODIFY_GROUP_NAME: Method private lateinit var METHOD_REQ_MODIFY_GROUP_NAME: Method
suspend fun getGroupRemainAtAllRemain (groupId: Long): Result<GroupAtAllRemainInfo> { suspend fun getGroupRemainAtAllRemain (groupId: Long): Result<GroupAtAllRemainInfo> {
val buffer = sendOidbAW("OidbSvcTrpcTcp.0x8a7_0", 2215, 0, cmd0x8a7.ReqBody().apply { val fromServiceMsg = sendOidbAW("OidbSvcTrpcTcp.0x8a7_0", 2215, 0, cmd0x8a7.ReqBody().apply {
uint32_sub_cmd.set(1) uint32_sub_cmd.set(1)
uint32_limit_interval_type_for_uin.set(2) uint32_limit_interval_type_for_uin.set(2)
uint32_limit_interval_type_for_group.set(1) uint32_limit_interval_type_for_group.set(1)
uint64_uin.set(getLongUin()) uint64_uin.set(getLongUin())
uint64_group_code.set(groupId) uint64_group_code.set(groupId)
}.toByteArray(), trpc = true) ?: return Result.failure(RuntimeException("[oidb] timeout")) }.toByteArray(), trpc = true) ?: return Result.failure(RuntimeException("[oidb] timeout"))
val body = oidb_sso.OIDBSSOPkg() val body = fromServiceMsg.decodeToOidb()
body.mergeFrom(buffer.slice(4))
if(body.uint32_result.get() != 0) { if(body.uint32_result.get() != 0) {
return Result.failure(RuntimeException(body.str_error_msg.get())) return Result.failure(RuntimeException(body.str_error_msg.get()))
} }
@ -135,7 +141,7 @@ internal object GroupSvc: BaseSvc() {
)) ))
} }
suspend fun getProhibitedMemberList(groupId: Long): Result<List<ProhibitedMemberInfo>> { suspend fun getProhibitedMemberList(groupId: Long): Result<List<ProhibitedMemberInfo>> {
val buffer = sendOidbAW("OidbSvc.0x899_0", 2201, 0, oidb_0x899.ReqBody().apply { val fromServiceMsg = sendOidbAW("OidbSvc.0x899_0", 2201, 0, oidb_0x899.ReqBody().apply {
uint64_group_code.set(groupId) uint64_group_code.set(groupId)
uint64_start_uin.set(0) uint64_start_uin.set(0)
uint32_identify_flag.set(6) uint32_identify_flag.set(6)
@ -144,8 +150,7 @@ internal object GroupSvc: BaseSvc() {
uint32_shutup_timestap.set(0) uint32_shutup_timestap.set(0)
}) })
}.toByteArray()) ?: return Result.failure(RuntimeException("[oidb] timeout")) }.toByteArray()) ?: return Result.failure(RuntimeException("[oidb] timeout"))
val body = oidb_sso.OIDBSSOPkg() val body = fromServiceMsg.decodeToOidb()
body.mergeFrom(buffer.slice(4))
if(body.uint32_result.get() != 0) { if(body.uint32_result.get() != 0) {
return Result.failure(RuntimeException(body.str_error_msg.get())) return Result.failure(RuntimeException(body.str_error_msg.get()))
} }
@ -165,22 +170,62 @@ internal object GroupSvc: BaseSvc() {
sendOidb("OidbSvc.0xed3", 3795, 1, req.toByteArray()) sendOidb("OidbSvc.0xed3", 3795, 1, req.toByteArray())
} }
suspend fun getGroupMemberList(groupId: Long, refresh: Boolean): Result<List<TroopMemberInfo>> {
val service = app.getRuntimeService(ITroopMemberInfoService::class.java, "all")
var memberList = service.getAllTroopMembers(groupId.toString())
if (refresh || memberList == null) {
memberList = requestTroopMemberInfo(service, groupId).onFailure {
return Result.failure(Exception("获取群成员列表失败"))
}.getOrThrow()
}
getGroupInfo(groupId, true).onSuccess { suspend fun getGroupMemberList(groupId: Long, refresh: Boolean): Result<HashMap<String, MemberInfo>> {
if(it.wMemberNum > memberList.size) { val kernelService = NTServiceFetcher.kernelService
return getGroupMemberList(groupId, true) val sessionService = kernelService.wrapperSession
val service = sessionService.groupService
val uids = suspendCancellableCoroutine { continuation ->
service.getAllMemberList(groupId, refresh) { _, _, groupMemberListResult ->
continuation.resume(groupMemberListResult?.ids?.map {
it.uid
})
} }
} }
val memberMap = suspendCancellableCoroutine { continuation ->
service.getMemberInfoForMqq(groupId, ArrayList(uids ?: emptyList()), refresh) { _, _, groupMemberListResult ->
continuation.resume(groupMemberListResult.infos)
}
}
// val extInfo = suspendCancellableCoroutine { continuation ->
// service.getMemberExtInfo(GroupMemberExtReq().apply {
// this.groupCode = groupId
// this.beginUin = 0.toString()
// this.groupType = ""
// this.memberExtFilter = MemberExtInfoFilter().apply {
// this.memberLevelInfoName = 1
// this.memberLevelInfoUin = 1
// this.nickName = 1
// this.specialTitle = 1
// this.memberLevelInfoActiveDay = 1
// }
// this.richCardNameVer = "1"
// this.sourceType = 1
// this.uinList = ArrayList(memberMap.values.toList().map {
// it.uin
// })
// }) { _, _, groupMemberExtListResult ->
// continuation.resume(groupMemberExtListResult)
// }
// }
return Result.success(memberMap)
// var memberList = service.getAllTroopMembers(groupId.toString())
// if (refresh || memberList == null) {
// memberList = requestTroopMemberInfo(service, groupId).onFailure {
// return Result.failure(Exception("获取群成员列表失败"))
// }.getOrThrow()
// }
//
// getGroupInfo(groupId, true).onSuccess {
// if(it.wMemberNum > memberList.size) {
// return getGroupMemberList(groupId, true)
// }
// }
// return Result.success(memberList)
return Result.success(memberList)
} }
suspend fun getGroupList(refresh: Boolean): Result<List<TroopInfo>> { suspend fun getGroupList(refresh: Boolean): Result<List<TroopInfo>> {
@ -203,10 +248,10 @@ internal object GroupSvc: BaseSvc() {
toServiceMsg.extraData.putLong("troop_code", groupId) toServiceMsg.extraData.putLong("troop_code", groupId)
toServiceMsg.extraData.putBoolean("is_admin", false) toServiceMsg.extraData.putBoolean("is_admin", false)
toServiceMsg.extraData.putInt("from", 0) toServiceMsg.extraData.putInt("from", 0)
val buffer = sendAW(toServiceMsg) val fromServiceMsg = sendToServiceMsgAW(toServiceMsg) ?: return@timeout Result.failure(Exception("获取群信息超时"))
val uniPacket = UniPacket(true) val uniPacket = UniPacket(true)
uniPacket.encodeName = "utf-8" uniPacket.encodeName = "utf-8"
uniPacket.decode(buffer) uniPacket.decode(fromServiceMsg.wupBuffer)
val respBatchProcess = uniPacket.getByClass("RespBatchProcess", RespBatchProcess()) val respBatchProcess = uniPacket.getByClass("RespBatchProcess", RespBatchProcess())
val batchRespInfo = oidb_0x88d.RspBody().mergeFrom(oidb_sso.OIDBSSOPkg() val batchRespInfo = oidb_0x88d.RspBody().mergeFrom(oidb_sso.OIDBSSOPkg()
.mergeFrom(respBatchProcess.batch_response_list.first().buffer) .mergeFrom(respBatchProcess.batch_response_list.first().buffer)
@ -242,14 +287,24 @@ internal object GroupSvc: BaseSvc() {
} }
suspend fun setGroupUniqueTitle(groupId: Long, userId: Long, title: String) { suspend fun setGroupUniqueTitle(groupId: Long, userId: Long, title: String) {
val localMemberInfo = getTroopMemberInfoByUin(groupId, userId, true).getOrThrow() var nick = getTroopMemberInfoByUinV2(groupId, userId, true).getOrThrow().let {
it.troopnick.ifEmpty { it.troopremark.ifNullOrEmpty("") }
}
if (PlatformUtils.getQQVersionCode() > QQ_9_0_65_VER && nick == null) {
nick = getTroopMemberNickByUin(groupId, userId)?.let {
it.troopNick
.ifNullOrEmpty(it.friendNick)
.ifNullOrEmpty(it.showName)
.ifNullOrEmpty(it.autoRemark)
.ifNullOrEmpty(it.colorNick)
}
}
val req = Oidb_0x8fc.ReqBody() val req = Oidb_0x8fc.ReqBody()
req.uint64_group_code.set(groupId) req.uint64_group_code.set(groupId)
val memberInfo = Oidb_0x8fc.MemberInfo() val memberInfo = Oidb_0x8fc.MemberInfo()
memberInfo.uint64_uin.set(userId) memberInfo.uint64_uin.set(userId)
memberInfo.bytes_uin_name.set(ByteStringMicro.copyFromUtf8(localMemberInfo.troopnick.ifEmpty { memberInfo.bytes_uin_name.set(ByteStringMicro.copyFromUtf8(nick))
localMemberInfo.troopremark.ifNullOrEmpty("")
}))
memberInfo.bytes_special_title.set(ByteStringMicro.copyFromUtf8(title)) memberInfo.bytes_special_title.set(ByteStringMicro.copyFromUtf8(title))
memberInfo.uint32_special_title_expire_time.set(-1) memberInfo.uint32_special_title_expire_time.set(-1)
req.rpt_mem_level_info.add(memberInfo) req.rpt_mem_level_info.add(memberInfo)
@ -270,7 +325,7 @@ internal object GroupSvc: BaseSvc() {
info.dwFlag = 1 info.dwFlag = 1
createToServiceMsg.extraData.putSerializable("vecUinInfo", arrayListOf(info)) createToServiceMsg.extraData.putSerializable("vecUinInfo", arrayListOf(info))
createToServiceMsg.extraData.putLong("dwNewSeq", 0L) createToServiceMsg.extraData.putLong("dwNewSeq", 0L)
send(createToServiceMsg) sendToServiceMsg(createToServiceMsg)
return true return true
} }
@ -286,13 +341,12 @@ internal object GroupSvc: BaseSvc() {
} }
suspend fun setEssenceMessage(groupId: Long, seq: Long, rand: Long): Pair<Boolean, String> { suspend fun setEssenceMessage(groupId: Long, seq: Long, rand: Long): Pair<Boolean, String> {
val buffer = sendOidbAW("OidbSvc.0xeac_1", 3756, 1, oidb_0xeac.ReqBody().apply { val fromServiceMsg = sendOidbAW("OidbSvc.0xeac_1", 3756, 1, oidb_0xeac.ReqBody().apply {
group_code.set(groupId) group_code.set(groupId)
msg_seq.set(seq.toInt()) msg_seq.set(seq.toInt())
msg_random.set(rand.toInt()) msg_random.set(rand.toInt())
}.toByteArray()) ?: return Pair(false, "unknown error") }.toByteArray()) ?: return Pair(false, "unknown error")
val body = oidb_sso.OIDBSSOPkg() val body = fromServiceMsg.decodeToOidb()
body.mergeFrom(buffer.slice(4))
val result = oidb_0xeac.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray()) val result = oidb_0xeac.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray())
return if (result.wording.has()) { return if (result.wording.has()) {
LogCenter.log("设置群精华失败: ${result.wording.get()}") LogCenter.log("设置群精华失败: ${result.wording.get()}")
@ -304,16 +358,12 @@ internal object GroupSvc: BaseSvc() {
} }
suspend fun deleteEssenceMessage(groupId: Long, seq: Long, rand: Long): Pair<Boolean, String> { suspend fun deleteEssenceMessage(groupId: Long, seq: Long, rand: Long): Pair<Boolean, String> {
val buffer = sendOidbAW("OidbSvc.0xeac_2", 3756, 2, oidb_0xeac.ReqBody().apply { val fromServiceMsg = sendOidbAW("OidbSvc.0xeac_2", 3756, 2, oidb_0xeac.ReqBody().apply {
group_code.set(groupId) group_code.set(groupId)
msg_seq.set(seq.toInt()) msg_seq.set(seq.toInt())
msg_random.set(rand.toInt()) msg_random.set(rand.toInt())
}.toByteArray()) }.toByteArray()) ?: return Pair(false, "unknown error")
val body = oidb_sso.OIDBSSOPkg() val body = fromServiceMsg.decodeToOidb()
if (buffer == null) {
return Pair(false, "unknown error")
}
body.mergeFrom(buffer.slice(4))
val result = oidb_0xeac.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray()) val result = oidb_0xeac.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray())
return if (result.wording.has()) { return if (result.wording.has()) {
LogCenter.log("移除群精华失败: ${result.wording.get()}") LogCenter.log("移除群精华失败: ${result.wording.get()}")
@ -405,11 +455,11 @@ internal object GroupSvc: BaseSvc() {
} }
} }
return when(getTroopMemberInfoByUinViaNt(groupId, memberUin, 3000).getOrNull()?.role) { return when(getTroopMemberInfoByUinViaNt(groupId, memberUin, 3000).getOrNull()?.role) {
com.tencent.qqnt.kernel.nativeinterface.MemberRole.STRANGER -> MemberRole.Stranger com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole.STRANGER -> MemberRole.Stranger
com.tencent.qqnt.kernel.nativeinterface.MemberRole.MEMBER -> MemberRole.Member com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole.MEMBER -> MemberRole.Member
com.tencent.qqnt.kernel.nativeinterface.MemberRole.ADMIN -> MemberRole.Admin com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole.ADMIN -> MemberRole.Admin
com.tencent.qqnt.kernel.nativeinterface.MemberRole.OWNER -> MemberRole.Owner com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole.OWNER -> MemberRole.Owner
com.tencent.qqnt.kernel.nativeinterface.MemberRole.UNSPECIFIED, null -> when (memberUin) { com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole.UNSPECIFIED, null -> when (memberUin) {
getOwner(groupId) -> MemberRole.Owner getOwner(groupId) -> MemberRole.Owner
in getAdminList(groupId) -> MemberRole.Admin in getAdminList(groupId) -> MemberRole.Admin
else -> MemberRole.Member else -> MemberRole.Member
@ -427,12 +477,21 @@ internal object GroupSvc: BaseSvc() {
return groupInfo.troopowneruin == app.account return groupInfo.troopowneruin == app.account
} }
fun isAdmin(groupId: Long): Boolean { suspend fun isAdmin(groupId: Long): Boolean {
if (PlatformUtils.getQQVersionCode() > QQ_9_0_65_VER) {
// 针对新版本api做的适配
val account = app.longAccountUin
getTroopMemberInfoByUinV2(groupId, account, false).onSuccess {
if (it.role == com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole.ADMIN
|| it.role == com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole.OWNER) {
return true
}
}
}
val service = app val service = app
.getRuntimeService(ITroopInfoService::class.java, "all") .getRuntimeService(ITroopInfoService::class.java, "all")
val groupInfo = service.getTroopInfo(groupId.toString()) val groupInfo = service.getTroopInfo(groupId.toString())
return groupInfo.isAdmin || groupInfo.troopowneruin == app.account return groupInfo.isAdmin || groupInfo.troopowneruin == app.account
} }
@ -527,65 +586,38 @@ internal object GroupSvc: BaseSvc() {
reqBody.get_ark.set(true) reqBody.get_ark.set(true)
reqBody.type.set(1) reqBody.type.set(1)
reqBody.group_code.set(groupId) reqBody.group_code.set(groupId)
val buffer = sendBufferAW("GroupSvc.JoinGroupLink", true, reqBody.toByteArray()) val fromServiceMsg = sendBufferAW("GroupSvc.JoinGroupLink", true, reqBody.toByteArray())
?: error("unable to fetch contact ark_json_text") ?: error("unable to fetch contact ark_json_text")
val body = join_group_link.RspBody() val body = fromServiceMsg.decodeToObject(join_group_link.RspBody())
body.mergeFrom(buffer.slice(4))
return body.signed_ark.get().toStringUtf8() return body.signed_ark.get().toStringUtf8()
} }
suspend fun getTroopMemberInfoByUin( fun getTroopMemberInfoByUinFromNt(
groupId: Long, groupId: Long,
uin: Long, uin: Long
refresh: Boolean = false
): Result<TroopMemberInfo> { ): Result<TroopMemberInfo> {
val service = app.getRuntimeService(ITroopMemberInfoService::class.java, "all") return kotlin.runCatching {
var info = service.getTroopMember(groupId.toString(), uin.toString()) val api = QRoute.api(ITroopMemberListRepoApi::class.java)
if (refresh || !service.isMemberInCache(groupId.toString(), uin.toString()) || info == null || info.troopnick == null) { api.getTroopMemberInfoSync(groupId.toString(), uin.toString(), null, groupId.toString())
info = requestTroopMemberInfo(service, groupId, uin).getOrNull() ?: throw Exception("获取群成员信息失败: NT兼容接口已废弃")
} }
if (info == null) { }
info = getTroopMemberInfoByUinViaNt(groupId, uin).getOrNull()?.let {
TroopMemberInfo().apply { suspend fun getTroopMemberNickByUin(
troopnick = it.cardName groupId: Long,
friendnick = it.nick uin: Long
} ): TroopMemberNickInfo? {
} if (PlatformUtils.getQQVersionCode() > QQ_9_0_65_VER) {
} val api = QRoute.api(ITroopMemberListRepoApi::class.java)
try { return withTimeoutOrNull(5.seconds) {
if (info != null && (info.alias == null || info.alias.isBlank())) { suspendCancellableCoroutine<TroopMemberNickInfo> { continuation ->
val req = group_member_info.ReqBody() api.fetchTroopMemberName(groupId.toString(), uin.toString(), null, groupId.toString()) {
req.uint64_group_code.set(groupId) continuation.resume(it)
req.uint64_uin.set(uin.toLong())
req.bool_new_client.set(true)
req.uint32_client_type.set(1)
req.uint32_rich_card_name_ver.set(1)
val respBuffer = sendBufferAW("group_member_card.get_group_member_card_info", true, req.toByteArray())
if (respBuffer != null) {
val rsp = group_member_info.RspBody()
rsp.mergeFrom(respBuffer.slice(4))
if (rsp.msg_meminfo.str_location.has()) {
info.alias = rsp.msg_meminfo.str_location.get().toStringUtf8()
}
if (rsp.msg_meminfo.uint32_age.has()) {
info.age = rsp.msg_meminfo.uint32_age.get().toByte()
}
if (rsp.msg_meminfo.bytes_group_honor.has()) {
val honorBytes = rsp.msg_meminfo.bytes_group_honor.get().toByteArray()
val honor = troop_honor.GroupUserCardHonor()
honor.mergeFrom(honorBytes)
info.level = honor.level.get()
// 10315: medal_id not real group level
} }
} }
} }
} catch (err: Throwable) {
LogCenter.log(err.stackTraceToString(), Level.WARN)
}
return if (info != null) {
Result.success(info)
} else { } else {
Result.failure(Exception("获取群成员信息失败")) return null
} }
} }
@ -594,10 +626,15 @@ internal object GroupSvc: BaseSvc() {
uin: Long, uin: Long,
refresh: Boolean = false refresh: Boolean = false
): Result<TroopMemberInfo> { ): Result<TroopMemberInfo> {
val service = app.getRuntimeService(ITroopMemberInfoService::class.java, "all") var info: TroopMemberInfo? = null
var info = service.getTroopMember(groupId.toString(), uin.toString()) if (PlatformUtils.getQQVersionCode() <= QQ_9_0_65_VER) {
if (refresh || !service.isMemberInCache(groupId.toString(), uin.toString()) || info == null || info.troopnick == null) { val service = app.getRuntimeService(ITroopMemberInfoService::class.java, "all")
info = requestTroopMemberInfo(service, groupId, uin, timeout = 2000).getOrNull() info = service.getTroopMember(groupId.toString(), uin.toString())
if (refresh || !service.isMemberInCache(groupId.toString(), uin.toString()) || info == null || info.troopnick == null) {
info = requestTroopMemberInfo(service, groupId, uin, timeout = 2000).getOrNull()
}
} else {
info = getTroopMemberInfoByUinFromNt(groupId, uin).getOrNull()
} }
if (info == null) { if (info == null) {
info = getTroopMemberInfoByUinViaNt(groupId, uin, timeout = 2000L).getOrNull()?.let { info = getTroopMemberInfoByUinViaNt(groupId, uin, timeout = 2000L).getOrNull()?.let {
@ -607,35 +644,36 @@ internal object GroupSvc: BaseSvc() {
} }
} }
} }
try { if (PlatformUtils.getQQVersionCode() <= QQ_9_0_8_VER) {
if (info != null && (info.alias == null || info.alias.isBlank())) { try {
val req = group_member_info.ReqBody() if (info != null && (info.alias == null || info.alias.isBlank())) {
req.uint64_group_code.set(groupId) val req = group_member_info.ReqBody()
req.uint64_uin.set(uin) req.uint64_group_code.set(groupId)
req.bool_new_client.set(true) req.uint64_uin.set(uin)
req.uint32_client_type.set(1) req.bool_new_client.set(true)
req.uint32_rich_card_name_ver.set(1) req.uint32_client_type.set(1)
val respBuffer = sendBufferAW("group_member_card.get_group_member_card_info", true, req.toByteArray(), timeout = 2000) req.uint32_rich_card_name_ver.set(1)
if (respBuffer != null) { val respBuffer = sendBufferAW("group_member_card.get_group_member_card_info", true, req.toByteArray(), timeout = 2.seconds)
val rsp = group_member_info.RspBody() if (respBuffer != null) {
rsp.mergeFrom(respBuffer.slice(4)) val rsp = respBuffer.decodeToObject(group_member_info.RspBody())
if (rsp.msg_meminfo.str_location.has()) { if (rsp.msg_meminfo.str_location.has()) {
info.alias = rsp.msg_meminfo.str_location.get().toStringUtf8() info.alias = rsp.msg_meminfo.str_location.get().toStringUtf8()
} }
if (rsp.msg_meminfo.uint32_age.has()) { if (rsp.msg_meminfo.uint32_age.has()) {
info.age = rsp.msg_meminfo.uint32_age.get().toByte() info.age = rsp.msg_meminfo.uint32_age.get().toByte()
} }
if (rsp.msg_meminfo.bytes_group_honor.has()) { if (rsp.msg_meminfo.bytes_group_honor.has()) {
val honorBytes = rsp.msg_meminfo.bytes_group_honor.get().toByteArray() val honorBytes = rsp.msg_meminfo.bytes_group_honor.get().toByteArray()
val honor = troop_honor.GroupUserCardHonor() val honor = troop_honor.GroupUserCardHonor()
honor.mergeFrom(honorBytes) honor.mergeFrom(honorBytes)
info.level = honor.level.get() info.level = honor.level.get()
// 10315: medal_id not real group level // 10315: medal_id not real group level
}
} }
} }
} catch (err: Throwable) {
LogCenter.log(err.stackTraceToString(), Level.WARN)
} }
} catch (err: Throwable) {
LogCenter.log(err.stackTraceToString(), Level.WARN)
} }
return if (info != null) { return if (info != null) {
Result.success(info) Result.success(info)
@ -644,7 +682,7 @@ internal object GroupSvc: BaseSvc() {
} }
} }
suspend fun getTroopMemberInfoByUinViaNt( private suspend fun getTroopMemberInfoByUinViaNt(
groupId: Long, groupId: Long,
qq: Long, qq: Long,
timeout: Long = 5000L timeout: Long = 5000L
@ -673,7 +711,7 @@ internal object GroupSvc: BaseSvc() {
return if (info != null) { return if (info != null) {
Result.success(info) Result.success(info)
} else { } else {
Result.failure(Exception("获取群成员信息失败")) Result.failure(Exception("[NT]获取群成员信息失败"))
} }
} }
} }
@ -895,7 +933,7 @@ internal object GroupSvc: BaseSvc() {
} }
val respBuffer = sendBufferAW("ProfileService.Pb.ReqSystemMsgAction.Group", true, req.toByteArray()) val respBuffer = sendBufferAW("ProfileService.Pb.ReqSystemMsgAction.Group", true, req.toByteArray())
?: return Result.failure(Exception("操作失败")) ?: return Result.failure(Exception("操作失败"))
val rsp = structmsg.RspSystemMsgAction().mergeFrom(respBuffer.slice(4)) val rsp = respBuffer.decodeToObject(structmsg.RspSystemMsgAction())
return if (rsp.head.result.has()) { return if (rsp.head.result.has()) {
if (rsp.head.result.get() == 0) { if (rsp.head.result.get() == 0) {
Result.success(rsp.msg_detail.get()) Result.success(rsp.msg_detail.get())
@ -946,8 +984,7 @@ internal object GroupSvc: BaseSvc() {
ArrayList() ArrayList()
} else { } else {
try { try {
val msg = structmsg.RspSystemMsgNew() val msg = respBuffer.decodeToObject(structmsg.RspSystemMsgNew())
msg.mergeFrom(respBuffer.slice(4))
return msg.groupmsgs.get().orEmpty() return msg.groupmsgs.get().orEmpty()
} catch (err: Throwable) { } catch (err: Throwable) {
requestGroupSystemMsgNew(msgNum, reqMsgType, latestFriendSeq, latestGroupSeq, retryCnt - 1) requestGroupSystemMsgNew(msgNum, reqMsgType, latestFriendSeq, latestGroupSeq, retryCnt - 1)
@ -1140,8 +1177,7 @@ internal object GroupSvc: BaseSvc() {
return if (buffer == null) { return if (buffer == null) {
Result.failure(Exception("操作失败")) Result.failure(Exception("操作失败"))
} else { } else {
val body = oidb_sso.OIDBSSOPkg() val body = buffer.decodeToOidb()
body.mergeFrom(buffer.slice(4))
val rsp = oidb_0xeb7.RspBody() val rsp = oidb_0xeb7.RspBody()
rsp.mergeFrom(body.bytes_bodybuffer.get().toByteArray()) rsp.mergeFrom(body.bytes_bodybuffer.get().toByteArray())
val doneInfo = rsp.signInWriteRsp.doneInfo val doneInfo = rsp.signInWriteRsp.doneInfo

View File

@ -2,13 +2,16 @@ package moe.fuqiuluo.qqinterface.servlet
import com.tencent.biz.map.trpcprotocol.LbsSendInfo import com.tencent.biz.map.trpcprotocol.LbsSendInfo
import com.tencent.mobileqq.msf.core.MsfCore import com.tencent.mobileqq.msf.core.MsfCore
import com.tencent.mobileqq.msf.service.MsfService
import com.tencent.proto.lbsshare.LBSShare import com.tencent.proto.lbsshare.LBSShare
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import moe.fuqiuluo.shamrock.helper.IllegalParamsException import moe.fuqiuluo.shamrock.helper.IllegalParamsException
import moe.fuqiuluo.shamrock.tools.decodeToObject
import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import kotlin.math.roundToInt import kotlin.math.roundToInt
internal object LbsSvc: BaseSvc() { internal object LbsSvc: QQInterfaces() {
suspend fun tryShareLocation(chatType: Int, peerId: Long, lat: Double, lon: Double): Result<Unit> { suspend fun tryShareLocation(chatType: Int, peerId: Long, lat: Double, lon: Double): Result<Unit> {
val req = LbsSendInfo.SendMessageReq() val req = LbsSendInfo.SendMessageReq()
req.uint64_peer_account.set(peerId) req.uint64_peer_account.set(peerId)
@ -23,8 +26,8 @@ internal object LbsSvc: BaseSvc() {
}.getOrNull()) }.getOrNull())
req.str_lat.set(lat.toString()) req.str_lat.set(lat.toString())
req.str_lng.set(lon.toString()) req.str_lng.set(lon.toString())
sendPb("trpc.qq_lbs.qq_lbs_ark.LocationArk.SsoSendMessage", req.toByteArray(), MsfCore.getNextSeq()) sendBuffer("trpc.qq_lbs.qq_lbs_ark.LocationArk.SsoSendMessage", true, req.toByteArray())
//sendPb("trpc.qq_lbs.qq_lbs_ark.LocationArk.SsoSendMessage", req.toByteArray(), MsfService.getCore().nextSeq)
return Result.success(Unit) return Result.success(Unit)
} }
@ -49,8 +52,7 @@ internal object LbsSvc: BaseSvc() {
req.imei.set("") req.imei.set("")
val buffer = sendBufferAW("LbsShareSvr.location", true, req.toByteArray()) val buffer = sendBufferAW("LbsShareSvr.location", true, req.toByteArray())
?: return Result.failure(Exception("获取位置失败")) ?: return Result.failure(Exception("获取位置失败"))
val resp = LBSShare.LocationResp() val resp = buffer.decodeToObject(LBSShare.LocationResp())
resp.mergeFrom(buffer.slice(4))
val location = resp.mylbs val location = resp.mylbs
return Result.success(location.addr.get()) return Result.success(location.addr.get())
} }

View File

@ -24,6 +24,7 @@ import moe.fuqiuluo.shamrock.remote.structures.SendMsgResult
import moe.fuqiuluo.shamrock.tools.* import moe.fuqiuluo.shamrock.tools.*
import moe.fuqiuluo.shamrock.utils.DeflateTools import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import moe.fuqiuluo.shamrock.xposed.helper.msgService import moe.fuqiuluo.shamrock.xposed.helper.msgService
import moe.fuqiuluo.symbols.decodeProtobuf import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.auto.toByteArray import protobuf.auto.toByteArray
@ -33,9 +34,10 @@ import java.util.*
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
import kotlin.random.Random import kotlin.random.Random
import kotlin.time.Duration.Companion.seconds
internal object MsgSvc : BaseSvc() { internal object MsgSvc : QQInterfaces() {
private suspend fun prepareTempChatFromGroup( suspend fun prepareTempChatFromGroup(
groupId: String, groupId: String,
peerId: String peerId: String
): Result<Unit> { ): Result<Unit> {
@ -418,13 +420,9 @@ internal object MsgSvc : BaseSvc() {
) )
).toByteArray() ).toByteArray()
val buffer = sendBufferAW("trpc.group.long_msg_interface.MsgService.SsoSendLongMsg", true, req, timeout = 30_000) val buffer = sendBufferAW("trpc.group.long_msg_interface.MsgService.SsoSendLongMsg", true, req, timeout = 30.seconds)
?: return Result.failure(Exception("unable to upload multi message, response timeout")) ?: return Result.failure(Exception("unable to upload multi message, response timeout"))
val rsp = runCatching { val rsp = buffer.decodeToObject<LongMsgRsp>()
buffer.slice(4).decodeProtobuf<LongMsgRsp>()
}.getOrElse {
buffer.decodeProtobuf<LongMsgRsp>()
}
val resId = rsp.sendResult?.resId ?: return Result.failure(Exception("unable to upload multi message")) val resId = rsp.sendResult?.resId ?: return Result.failure(Exception("unable to upload multi message"))
return Result.success(MessageSegment( return Result.success(MessageSegment(
type = "forward", type = "forward",
@ -456,7 +454,7 @@ internal object MsgSvc : BaseSvc() {
true, true,
req.toByteArray() req.toByteArray()
) ?: return Result.failure(Exception("unable to get multi message")) ) ?: return Result.failure(Exception("unable to get multi message"))
val rsp = buffer.slice(4).decodeProtobuf<LongMsgRsp>() val rsp = buffer.decodeToObject<LongMsgRsp>()
val zippedPayload = DeflateTools.ungzip( val zippedPayload = DeflateTools.ungzip(
rsp.recvResult?.payload ?: return Result.failure(Exception("payload is empty")) rsp.recvResult?.payload ?: return Result.failure(Exception("payload is empty"))
) )

View File

@ -1,8 +1,8 @@
package moe.fuqiuluo.qqinterface.servlet package moe.fuqiuluo.qqinterface.servlet
import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.IKernelMsgService import com.tencent.qqnt.kernel.nativeinterface.IKernelMsgService
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact
import io.ktor.utils.io.core.BytePacketBuilder import io.ktor.utils.io.core.BytePacketBuilder
import io.ktor.utils.io.core.readBytes import io.ktor.utils.io.core.readBytes
import io.ktor.utils.io.core.writeFully import io.ktor.utils.io.core.writeFully
@ -13,6 +13,7 @@ import moe.fuqiuluo.qqinterface.servlet.msg.MessageTempHandler
import moe.fuqiuluo.shamrock.remote.action.handlers.GetHistoryMsg import moe.fuqiuluo.shamrock.remote.action.handlers.GetHistoryMsg
import moe.fuqiuluo.shamrock.tools.broadcast import moe.fuqiuluo.shamrock.tools.broadcast
import moe.fuqiuluo.shamrock.utils.DeflateTools import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import mqq.app.MobileQQ import mqq.app.MobileQQ
import protobuf.auto.toByteArray import protobuf.auto.toByteArray
import protobuf.message.* import protobuf.message.*
@ -21,7 +22,7 @@ import protobuf.push.MessagePush
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.text.toByteArray import kotlin.text.toByteArray
internal object PacketSvc : BaseSvc() { internal object PacketSvc : QQInterfaces() {
/** /**
* 伪造收到Json卡片消息 * 伪造收到Json卡片消息
*/ */

View File

@ -23,6 +23,7 @@ import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.shamrock.utils.DeflateTools import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.shamrock.utils.MD5 import moe.fuqiuluo.shamrock.utils.MD5
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import protobuf.fav.WeiyunAddRichMediaReq import protobuf.fav.WeiyunAddRichMediaReq
import protobuf.fav.WeiyunAuthor import protobuf.fav.WeiyunAuthor
import protobuf.fav.WeiyunCollectCommInfo import protobuf.fav.WeiyunCollectCommInfo
@ -49,7 +50,7 @@ import kotlin.coroutines.resume
/** /**
* QQ收藏相关接口 * QQ收藏相关接口
*/ */
internal object QFavSvc: BaseSvc() { internal object QFavSvc: QQInterfaces() {
private val SERVER_LIST_COLLECTOR = listOf(ServerAddr().also { private val SERVER_LIST_COLLECTOR = listOf(ServerAddr().also {
it.isIpv6 = false it.isIpv6 = false
it.mIp = "collector.weiyun.com" it.mIp = "collector.weiyun.com"
@ -275,7 +276,7 @@ internal object QFavSvc: BaseSvc() {
override fun onUpdateProgeress(netReq: NetReq, curr: Long, final: Long) {} override fun onUpdateProgeress(netReq: NetReq, curr: Long, final: Long) {}
} }
val vi = (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getA2(app.currentAccountUin) val vi = ((app as QQAppInterface).getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getA2(app.currentAccountUin)
//LogCenter.log(pSKey) //LogCenter.log(pSKey)
httpNetReq.mHttpMethod = HttpNetReq.HTTP_POST httpNetReq.mHttpMethod = HttpNetReq.HTTP_POST
httpNetReq.mSendData = BytePacketBuilder().apply { httpNetReq.mSendData = BytePacketBuilder().apply {
@ -381,7 +382,7 @@ internal object QFavSvc: BaseSvc() {
} }
private fun getWeiYunPSKey(): String { private fun getWeiYunPSKey(): String {
val pskey = (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager) val pskey = ((app as QQAppInterface).getManager(QQAppInterface.TICKET_MANAGER) as TicketManager)
.getPskey(app.currentAccountUin, 16L, arrayOf("weiyun.com"), WeiYunPSKeyPromise) .getPskey(app.currentAccountUin, 16L, arrayOf("weiyun.com"), WeiYunPSKeyPromise)
return if (pskey != null) pskey.getPSkey("weiyun.com") else "" return if (pskey != null) pskey.getPSkey("weiyun.com") else ""
} }

View File

@ -4,12 +4,12 @@ import QQService.SvcDevLoginInfo
import QQService.SvcReqGetDevLoginInfo import QQService.SvcReqGetDevLoginInfo
import QQService.SvcRspGetDevLoginInfo import QQService.SvcRspGetDevLoginInfo
import com.qq.jce.wup.UniPacket import com.qq.jce.wup.UniPacket
import moe.fuqiuluo.qqinterface.servlet.BaseSvc import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import mqq.app.MobileQQ import mqq.app.MobileQQ
import mqq.app.Packet import mqq.app.Packet
import oicq.wlogin_sdk.tools.util import oicq.wlogin_sdk.tools.util
internal object QSafeSvc: BaseSvc() { internal object QSafeSvc: QQInterfaces() {
suspend fun getOnlineClients(): ArrayList<SvcDevLoginInfo>? { suspend fun getOnlineClients(): ArrayList<SvcDevLoginInfo>? {
val req = SvcReqGetDevLoginInfo() val req = SvcReqGetDevLoginInfo()
@ -26,7 +26,7 @@ internal object QSafeSvc: BaseSvc() {
val resp = sendBufferAW("StatSvc.GetDevLoginInfo", false, uniPacket.encode()) val resp = sendBufferAW("StatSvc.GetDevLoginInfo", false, uniPacket.encode())
?: return null ?: return null
return Packet.decodePacket(resp, "SvcRspGetDevLoginInfo", SvcRspGetDevLoginInfo()).vecCurrentLoginDevInfo return Packet.decodePacket(resp.wupBuffer, "SvcRspGetDevLoginInfo", SvcRspGetDevLoginInfo()).vecCurrentLoginDevInfo
} }

View File

@ -8,13 +8,15 @@ import io.ktor.client.request.get
import io.ktor.client.request.header import io.ktor.client.request.header
import moe.fuqiuluo.shamrock.remote.service.data.BigDataTicket import moe.fuqiuluo.shamrock.remote.service.data.BigDataTicket
import moe.fuqiuluo.shamrock.tools.GlobalClientNoRedirect import moe.fuqiuluo.shamrock.tools.GlobalClientNoRedirect
import moe.fuqiuluo.shamrock.tools.decodeToOidb
import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import mqq.app.MobileQQ import mqq.app.MobileQQ
import mqq.manager.TicketManager import mqq.manager.TicketManager
import oicq.wlogin_sdk.request.Ticket import oicq.wlogin_sdk.request.Ticket
import tencent.im.oidb.oidb_sso import tencent.im.oidb.oidb_sso
internal object TicketSvc: BaseSvc() { internal object TicketSvc: QQInterfaces() {
object SigType { object SigType {
const val WLOGIN_A5 = 2 const val WLOGIN_A5 = 2
const val WLOGIN_RESERVED = 16 const val WLOGIN_RESERVED = 16
@ -109,23 +111,23 @@ internal object TicketSvc: BaseSvc() {
} }
fun getTicket(uin: String, id: Int): Ticket? { fun getTicket(uin: String, id: Int): Ticket? {
return (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getLocalTicket(uin, id) return ((app as QQAppInterface).getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getLocalTicket(uin, id)
} }
fun getStWeb(uin: String): String { fun getStWeb(uin: String): String {
return (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getStweb(uin) return ((app as QQAppInterface).getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getStweb(uin)
} }
fun getSKey(uin: String): String { fun getSKey(uin: String): String {
return (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getSkey(uin) return ((app as QQAppInterface).getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getSkey(uin)
} }
fun getRealSkey(uin: String): String { fun getRealSkey(uin: String): String {
return (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getRealSkey(uin) return ((app as QQAppInterface).getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getRealSkey(uin)
} }
fun getPSKey(uin: String): String { fun getPSKey(uin: String): String {
val manager = (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager) val manager = ((app as QQAppInterface).getManager(QQAppInterface.TICKET_MANAGER) as TicketManager)
manager.reloadCache(MobileQQ.getContext()) manager.reloadCache(MobileQQ.getContext())
return manager.getSuperkey(uin) ?: "" return manager.getSuperkey(uin) ?: ""
} }
@ -135,14 +137,13 @@ internal object TicketSvc: BaseSvc() {
req.domains.set(domain.toList()) req.domains.set(domain.toList())
val buffer = sendOidbAW("OidbSvcTcp.0x102a", 4138, 0, req.toByteArray()) val buffer = sendOidbAW("OidbSvcTcp.0x102a", 4138, 0, req.toByteArray())
?: return Result.failure(Exception("getLessPSKey failed")) ?: return Result.failure(Exception("getLessPSKey failed"))
val body = oidb_sso.OIDBSSOPkg() val body = buffer.decodeToOidb()
body.mergeFrom(buffer.slice(4))
val rsp = oidb_cmd0x102a.GetPSkeyResponse().mergeFrom(body.bytes_bodybuffer.get().toByteArray()) val rsp = oidb_cmd0x102a.GetPSkeyResponse().mergeFrom(body.bytes_bodybuffer.get().toByteArray())
return Result.success(rsp.private_keys.get()) return Result.success(rsp.private_keys.get())
} }
suspend fun getPSKey(uin: String, domain: String): String? { suspend fun getPSKey(uin: String, domain: String): String? {
return (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getPskey(uin, domain).let { return ((app as QQAppInterface).getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getPskey(uin, domain).let {
if (it.isNullOrBlank()) if (it.isNullOrBlank())
getLessPSKey(domain).getOrNull()?.firstOrNull()?.key?.get() getLessPSKey(domain).getOrNull()?.firstOrNull()?.key?.get()
else it else it
@ -150,7 +151,7 @@ internal object TicketSvc: BaseSvc() {
} }
fun getPt4Token(uin: String, domain: String): String? { fun getPt4Token(uin: String, domain: String): String? {
return (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getPt4Token(uin, domain) return ((app as QQAppInterface).getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getPt4Token(uin, domain)
} }
suspend fun GetHttpCookies(appid: String, daid: String, jumpurl: String): String? { suspend fun GetHttpCookies(appid: String, daid: String, jumpurl: String): String? {

View File

@ -1,6 +1,8 @@
package moe.fuqiuluo.qqinterface.servlet package moe.fuqiuluo.qqinterface.servlet
internal object VisitorSvc: BaseSvc() { import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
internal object VisitorSvc: QQInterfaces() {
const val FROM_C2C_AIO = 2 const val FROM_C2C_AIO = 2
const val FROM_CONDITION_SEARCH = 9 const val FROM_CONDITION_SEARCH = 9
const val FROM_CONTACTS_TAB = 5 const val FROM_CONTACTS_TAB = 5

View File

@ -1,11 +1,11 @@
package moe.fuqiuluo.qqinterface.servlet.ark package moe.fuqiuluo.qqinterface.servlet.ark
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.qqinterface.servlet.ark.data.ArkAppInfo import moe.fuqiuluo.qqinterface.servlet.ark.data.ArkAppInfo
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import tencent.im.oidb.cmd0xb77.oidb_cmd0xb77 import tencent.im.oidb.cmd0xb77.oidb_cmd0xb77
internal object ArkMsgSvc: BaseSvc() { internal object ArkMsgSvc: QQInterfaces() {
fun tryShareMusic( fun tryShareMusic(
chatType: Int, chatType: Int,
peerId: Long, peerId: Long,

View File

@ -1,8 +1,8 @@
package moe.fuqiuluo.qqinterface.servlet.ark package moe.fuqiuluo.qqinterface.servlet.ark
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.qqinterface.servlet.ark.data.ArkAppInfo import moe.fuqiuluo.qqinterface.servlet.ark.data.ArkAppInfo
import moe.fuqiuluo.shamrock.utils.PlatformUtils import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import moe.fuqiuluo.symbols.decodeProtobuf import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.auto.toByteArray import protobuf.auto.toByteArray
import protobuf.lightapp.AdaptShareInfoReq import protobuf.lightapp.AdaptShareInfoReq
@ -11,7 +11,7 @@ import protobuf.qweb.DEFAULT_DEVICE_INFO
import protobuf.qweb.QWebReq import protobuf.qweb.QWebReq
import protobuf.qweb.QWebRsp import protobuf.qweb.QWebRsp
internal object LightAppSvc: BaseSvc() { internal object LightAppSvc: QQInterfaces() {
suspend fun adaptShareJumpUrl( suspend fun adaptShareJumpUrl(
arkAppInfo: ArkAppInfo, arkAppInfo: ArkAppInfo,
coverUrl: String, coverUrl: String,
@ -37,7 +37,7 @@ internal object LightAppSvc: BaseSvc() {
webURL = url, webURL = url,
).toByteArray(), ).toByteArray(),
traceId = app.account + "_0_0", traceId = app.account + "_0_0",
).toByteArray())?.decodeProtobuf<QWebRsp>()?.buffer?.decodeProtobuf<AdaptShareInfoResp>() ).toByteArray())?.wupBuffer?.decodeProtobuf<QWebRsp>()?.buffer?.decodeProtobuf<AdaptShareInfoResp>()
if (rsp == null || rsp.json.isNullOrEmpty()) if (rsp == null || rsp.json.isNullOrEmpty())
return Result.failure(Exception("unable to adapt ShareInfo")) return Result.failure(Exception("unable to adapt ShareInfo"))
return Result.success(rsp.json!!) return Result.success(rsp.json!!)

View File

@ -30,7 +30,7 @@ internal object NtMsgElementConverter {
MsgConstant.KELEMTYPEMARKETFACE to NtMsgElementConverter::convertMarketFaceElem, MsgConstant.KELEMTYPEMARKETFACE to NtMsgElementConverter::convertMarketFaceElem,
MsgConstant.KELEMTYPEARKSTRUCT to NtMsgElementConverter::convertStructJsonElem, MsgConstant.KELEMTYPEARKSTRUCT to NtMsgElementConverter::convertStructJsonElem,
MsgConstant.KELEMTYPEREPLY to NtMsgElementConverter::convertReplyElem, MsgConstant.KELEMTYPEREPLY to NtMsgElementConverter::convertReplyElem,
MsgConstant.KELEMTYPEGRAYTIP to NtMsgElementConverter::convertGrayTipsElem, //MsgConstant.KELEMTYPEGRAYTIP to NtMsgElementConverter::convertGrayTipsElem,
MsgConstant.KELEMTYPEFILE to NtMsgElementConverter::convertFileElem, MsgConstant.KELEMTYPEFILE to NtMsgElementConverter::convertFileElem,
MsgConstant.KELEMTYPEMARKDOWN to NtMsgElementConverter::convertMarkdownElem, MsgConstant.KELEMTYPEMARKDOWN to NtMsgElementConverter::convertMarkdownElem,
//MsgConstant.KELEMTYPEMULTIFORWARD to MsgElementConverter::convertXmlMultiMsgElem, //MsgConstant.KELEMTYPEMULTIFORWARD to MsgElementConverter::convertXmlMultiMsgElem,
@ -182,48 +182,14 @@ internal object NtMsgElementConverter {
/* /*
PicElement{picSubType=0,fileName=A655FCDADABC40D0CEAF6F9AF92937CD.jpg,fileSize=142865,picWidth=886,picHeight=1920,original=false,md5HexStr=a655fcdadabc40d0ceaf6f9af92937cd,sourcePath=null,thumbPath=null,transferStatus=2,progress=0,picType=1000,invalidState=0,fileUuid=CgoxMDI5Mzc0MTE1EhTnucgrUbp3MJjjagUM2-VxSQ5V7hiR3Agg_goo9ZCZt-HNhANQgJqeAQ,fileSubId=,thumbFileSize=0,fileBizId=null,downloadIndex=null,summary=,emojiFrom=null,emojiWebUrl=null,emojiAd=EmojiAD{url=,desc=,},emojiMall=EmojiMall{packageId=0,emojiId=0,},emojiZplan=EmojiZPlan{actionId=0,actionName=,actionType=0,playerNumber=0,peerUid=0,bytesReserveInfo=,},originImageMd5=,originImageUrl=null,importRichMediaContext=null,isFlashPic=false,} PicElement{picSubType=0,fileName=A655FCDADABC40D0CEAF6F9AF92937CD.jpg,fileSize=142865,picWidth=886,picHeight=1920,original=false,md5HexStr=a655fcdadabc40d0ceaf6f9af92937cd,sourcePath=null,thumbPath=null,transferStatus=2,progress=0,picType=1000,invalidState=0,fileUuid=CgoxMDI5Mzc0MTE1EhTnucgrUbp3MJjjagUM2-VxSQ5V7hiR3Agg_goo9ZCZt-HNhANQgJqeAQ,fileSubId=,thumbFileSize=0,fileBizId=null,downloadIndex=null,summary=,emojiFrom=null,emojiWebUrl=null,emojiAd=EmojiAD{url=,desc=,},emojiMall=EmojiMall{packageId=0,emojiId=0,},emojiZplan=EmojiZPlan{actionId=0,actionName=,actionType=0,playerNumber=0,peerUid=0,bytesReserveInfo=,},originImageMd5=,originImageUrl=null,importRichMediaContext=null,isFlashPic=false,}
*/ */
val url = RichProtoSvc.getTempPicDownloadUrl(chatType, originalUrl, md5, image, storeId)
return MessageSegment( return MessageSegment(
type = "image", type = "image",
data = hashMapOf( data = hashMapOf(
"file" to md5, "file" to md5,
"url" to when (chatType) { "url" to url,
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
originalUrl = originalUrl,
md5 = md5,
fileId = image.fileUuid,
width = image.picWidth.toUInt(),
height = image.picHeight.toUInt(),
sha = "",
fileSize = image.fileSize.toULong(),
peer = peerId
)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(
originalUrl = originalUrl,
md5 = md5,
fileId = image.fileUuid,
width = image.picWidth.toUInt(),
height = image.picHeight.toUInt(),
sha = "",
fileSize = image.fileSize.toULong(),
peer = peerId,
storeId = storeId
)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(
originalUrl = originalUrl,
md5 = md5,
fileId = image.fileUuid,
width = image.picWidth.toUInt(),
height = image.picHeight.toUInt(),
sha = "",
fileSize = image.fileSize.toULong(),
peer = peerId,
subPeer = subPeer
)
else -> throw UnsupportedOperationException("Not supported chat type: $chatType")
},
"subType" to image.picSubType, "subType" to image.picSubType,
"type" to if (image.isFlashPic == true) "flash" else if (image.original) "original" else "show" "type" to if (image.isFlashPic == true) "flash" else if (image.original) "original" else "show"
) )

View File

@ -124,18 +124,23 @@ internal class ElemMaker {
else -> { else -> {
qq = qqStr.toLong() qq = qqStr.toLong()
type = 0 type = 0
"@" + (data["name"].asStringOrNull ?: GroupSvc.getTroopMemberInfoByUinV2( val name = (data["name"].asStringOrNull
peerId.toLong(), ?: GroupSvc.getTroopMemberNickByUin(peerId.toLong(), qq)?.let {
qq, it.troopNick
true .ifNullOrEmpty(it.friendNick)
).let { .ifNullOrEmpty(it.showName)
val info = it.getOrNull() .ifNullOrEmpty(it.autoRemark)
if (info == null) .ifNullOrEmpty(it.colorNick)
LogCenter.log("无法获取群成员信息: $qqStr", Level.ERROR) }
else info.troopnick ?: GroupSvc.getTroopMemberInfoByUinV2(peerId.toLong(), qq, true).let {
.ifNullOrEmpty(info.friendnick) val info = it.getOrNull()
.ifNullOrEmpty(qqStr) if (info == null)
}) LogCenter.log("无法获取群成员信息: $qqStr", Level.ERROR)
else info.troopnick
.ifNullOrEmpty(info.friendnick)
.ifNullOrEmpty(qqStr)
})
"@$name"
} }
} }

View File

@ -856,7 +856,15 @@ internal object NtMsgElementMaker {
.ifNullOrEmpty(qqStr) .ifNullOrEmpty(qqStr)
}" }"
} else { } else {
at.content = "@$qqStr" at.content = "@${
GroupSvc.getTroopMemberNickByUin(peerId.toLong(), qq)?.let {
it.troopNick
.ifNullOrEmpty(it.friendNick)
.ifNullOrEmpty(it.showName)
.ifNullOrEmpty(it.autoRemark)
.ifNullOrEmpty(it.colorNick)
} ?: qqStr
}"
} }
} else { } else {
at.content = "@$name" at.content = "@$name"

View File

@ -5,7 +5,6 @@ import androidx.exifinterface.media.ExifInterface
import com.tencent.mobileqq.qroute.QRoute import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qqnt.aio.adapter.api.IAIOPttApi import com.tencent.qqnt.aio.adapter.api.IAIOPttApi
import com.tencent.qqnt.kernel.nativeinterface.CommonFileInfo import com.tencent.qqnt.kernel.nativeinterface.CommonFileInfo
import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.kernel.nativeinterface.PicElement import com.tencent.qqnt.kernel.nativeinterface.PicElement
@ -13,29 +12,33 @@ import com.tencent.qqnt.kernel.nativeinterface.PttElement
import com.tencent.qqnt.kernel.nativeinterface.QQNTWrapperUtil import com.tencent.qqnt.kernel.nativeinterface.QQNTWrapperUtil
import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo
import com.tencent.qqnt.kernel.nativeinterface.VideoElement import com.tencent.qqnt.kernel.nativeinterface.VideoElement
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.qqinterface.servlet.TicketSvc import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.qqinterface.servlet.transfile.data.TryUpPicData import moe.fuqiuluo.qqinterface.servlet.transfile.data.TryUpPicData
import moe.fuqiuluo.shamrock.helper.MessageHelper import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
import moe.fuqiuluo.shamrock.tools.decodeToObject
import moe.fuqiuluo.shamrock.tools.decodeToTrpcOidb
import moe.fuqiuluo.shamrock.tools.hex2ByteArray import moe.fuqiuluo.shamrock.tools.hex2ByteArray
import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.shamrock.utils.AudioUtils import moe.fuqiuluo.shamrock.utils.AudioUtils
import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MediaType import moe.fuqiuluo.shamrock.utils.MediaType
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import moe.fuqiuluo.shamrock.xposed.helper.msgService import moe.fuqiuluo.shamrock.xposed.helper.msgService
import moe.fuqiuluo.symbols.decodeProtobuf import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.auto.toByteArray import protobuf.auto.toByteArray
import protobuf.oidb.TrpcOidb
import protobuf.oidb.cmd0x11c5.ClientMeta import protobuf.oidb.cmd0x11c5.ClientMeta
import protobuf.oidb.cmd0x11c5.CodecConfigReq import protobuf.oidb.cmd0x11c5.CodecConfigReq
import protobuf.oidb.cmd0x11c5.CommonHead import protobuf.oidb.cmd0x11c5.CommonHead
import protobuf.oidb.cmd0x11c5.DownloadExt import protobuf.oidb.cmd0x11c5.DownloadExt
import protobuf.oidb.cmd0x11c5.DownloadReq import protobuf.oidb.cmd0x11c5.DownloadReq
import protobuf.oidb.cmd0x11c5.DownloadRkeyReq
import protobuf.oidb.cmd0x11c5.DownloadRkeyRsp
import protobuf.oidb.cmd0x11c5.FileInfo import protobuf.oidb.cmd0x11c5.FileInfo
import protobuf.oidb.cmd0x11c5.FileType import protobuf.oidb.cmd0x11c5.FileType
import protobuf.oidb.cmd0x11c5.IndexNode import protobuf.oidb.cmd0x11c5.IndexNode
@ -57,8 +60,9 @@ import kotlin.random.Random
import kotlin.random.nextUInt import kotlin.random.nextUInt
import kotlin.random.nextULong import kotlin.random.nextULong
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
internal object NtV2RichMediaSvc: BaseSvc() { internal object NtV2RichMediaSvc: QQInterfaces() {
private val requestIdSeq = atomic(2L) private val requestIdSeq = atomic(2L)
fun fetchGroupResUploadTo(): String { fun fetchGroupResUploadTo(): String {
@ -320,6 +324,40 @@ internal object NtV2RichMediaSvc: BaseSvc() {
return Result.success(result) return Result.success(result)
} }
suspend fun getTempNtRKey(): Result<DownloadRkeyRsp> {
runCatching {
val req = NtV2RichMediaReq(
head = MultiMediaReqHead(
commonHead = CommonHead(
requestId = requestIdSeq.incrementAndGet().toULong(),
cmd = 202u
),
sceneInfo = SceneInfo(
requestType = 2u,
businessType = 1u,
sceneType = 0u,
),
clientMeta = ClientMeta(2u)
),
downloadRkey = DownloadRkeyReq(
types = listOf(10, 20),
downloadType = 2
)
).toByteArray()
val fromServiceMsg = sendOidbAW("OidbSvcTrpcTcp.0x9067_202", 0x9067, 202, req, true)
if (fromServiceMsg == null || fromServiceMsg.wupBuffer == null) {
return Result.failure(Exception("failed to fetch NtTempRKey: ${fromServiceMsg?.wupBuffer?.toHexString()}"))
}
val trpc = fromServiceMsg.decodeToTrpcOidb()
trpc.buffer.decodeProtobuf<NtV2RichMediaRsp>().downloadRkeyRsp?.let {
return Result.success(it)
}
}.onFailure {
return Result.failure(it)
}
return Result.failure(Exception("failed to fetch NtTempRKey"))
}
/** /**
* 获取NT图片的RKEY * 获取NT图片的RKEY
*/ */
@ -386,8 +424,9 @@ internal object NtV2RichMediaSvc: BaseSvc() {
) )
) )
).toByteArray() ).toByteArray()
val buffer = sendOidbAW("OidbSvcTrpcTcp.0x11c5_200", 4549, 200, req, true)?.slice(4) val buffer = sendOidbAW("OidbSvcTrpcTcp.0x11c5_200", 4549, 200, req, true)
buffer?.decodeProtobuf<TrpcOidb>()?.buffer?.decodeProtobuf<NtV2RichMediaRsp>()?.download?.rkeyParam?.let { ?: return Result.failure(Exception("no response"))
buffer.decodeToTrpcOidb().buffer.decodeProtobuf<NtV2RichMediaRsp>().download?.rkeyParam?.let {
return Result.success(it) return Result.success(it)
} }
}.onFailure { }.onFailure {
@ -470,17 +509,16 @@ internal object NtV2RichMediaSvc: BaseSvc() {
).toByteArray() ).toByteArray()
val buffer = when (chatType) { val buffer = when (chatType) {
MsgConstant.KCHATTYPEGROUP -> { MsgConstant.KCHATTYPEGROUP -> {
sendOidbAW("OidbSvcTrpcTcp.0x11c4_100", 4548, 100, req, true, timeout = 3_000)?.slice(4) sendOidbAW("OidbSvcTrpcTcp.0x11c4_100", 4548, 100, req, true, timeout = 3.seconds)
?: return Result.failure(Exception("no response: timeout")) ?: return Result.failure(Exception("no response: timeout"))
} }
MsgConstant.KCHATTYPEC2C -> { MsgConstant.KCHATTYPEC2C -> {
sendOidbAW("OidbSvcTrpcTcp.0x11c5_100", 4549, 100, req, true, timeout = 3_000)?.slice(4) sendOidbAW("OidbSvcTrpcTcp.0x11c5_100", 4549, 100, req, true, timeout = 3.seconds)
?: return Result.failure(Exception("no response: timeout")) ?: return Result.failure(Exception("no response: timeout"))
} }
else -> return Result.failure(Exception("unknown chat type: $chatType")) else -> return Result.failure(Exception("unknown chat type: $chatType"))
} }
val rspBuffer = buffer.decodeProtobuf<TrpcOidb>().buffer val rspBuffer = buffer.decodeToTrpcOidb().buffer
val rsp = rspBuffer.decodeProtobuf<NtV2RichMediaRsp>() val rsp = rspBuffer.decodeProtobuf<NtV2RichMediaRsp>()
if (rsp.upload == null) { if (rsp.upload == null) {
return Result.failure(Exception("unable to request upload nt pic: ${rsp.head}")) return Result.failure(Exception("unable to request upload nt pic: ${rsp.head}"))
@ -522,7 +560,7 @@ internal object NtV2RichMediaSvc: BaseSvc() {
) )
), ),
).toByteArray())!! ).toByteArray())!!
val rsp = rspBuffer.decodeProtobuf<Cmd0x388RspBody>() val rsp = rspBuffer.decodeToObject<Cmd0x388RspBody>()
.msgTryUpImgRsp!!.first() .msgTryUpImgRsp!!.first()
TryUpPicData( TryUpPicData(
uKey = rsp.ukey, uKey = rsp.ukey,

View File

@ -6,18 +6,20 @@ import com.tencent.mobileqq.transfile.FileMsg
import com.tencent.mobileqq.transfile.api.IProtoReqManager 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 com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.PicElement
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.qqinterface.servlet.transfile.NtV2RichMediaSvc.getNtPicRKey import moe.fuqiuluo.qqinterface.servlet.transfile.NtV2RichMediaSvc.getNtPicRKey
import moe.fuqiuluo.shamrock.helper.ContactHelper import moe.fuqiuluo.shamrock.helper.ContactHelper
import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.hex2ByteArray import moe.fuqiuluo.shamrock.tools.decodeToOidb
import moe.fuqiuluo.shamrock.tools.slice 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.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import moe.fuqiuluo.symbols.decodeProtobuf import moe.fuqiuluo.symbols.decodeProtobuf
import mqq.app.MobileQQ import mqq.app.MobileQQ
import protobuf.auto.toByteArray import protobuf.auto.toByteArray
@ -38,7 +40,7 @@ private const val GPRO_PIC = "gchat.qpic.cn"
private const val MULTIMEDIA_DOMAIN = "multimedia.nt.qq.com.cn" private const val MULTIMEDIA_DOMAIN = "multimedia.nt.qq.com.cn"
private const val C2C_PIC = "c2cpicdw.qpic.cn" private const val C2C_PIC = "c2cpicdw.qpic.cn"
internal object RichProtoSvc: BaseSvc() { internal object RichProtoSvc: QQInterfaces() {
suspend fun getGuildFileDownUrl(peerId: String, channelId: String, fileId: String, bizId: Int): String { suspend fun getGuildFileDownUrl(peerId: String, channelId: String, fileId: String, bizId: Int): String {
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xfc2_0", 4034, 0, Oidb0xfc2ReqBody( val buffer = sendOidbAW("OidbSvcTrpcTcp.0xfc2_0", 4034, 0, Oidb0xfc2ReqBody(
msgCmd = 1200, msgCmd = 1200,
@ -53,8 +55,7 @@ internal object RichProtoSvc: BaseSvc() {
supportEncrypt = 0 supportEncrypt = 0
) )
).toByteArray()) ?: return "" ).toByteArray()) ?: return ""
val body = oidb_sso.OIDBSSOPkg() val body = buffer.decodeToOidb()
body.mergeFrom(buffer.slice(4))
body.bytes_bodybuffer body.bytes_bodybuffer
.get().toByteArray() .get().toByteArray()
.decodeProtobuf<Oidb0xfc2RspBody>() .decodeProtobuf<Oidb0xfc2RspBody>()
@ -79,8 +80,7 @@ internal object RichProtoSvc: BaseSvc() {
str_file_id.set(fileId) str_file_id.set(fileId)
}) })
}.toByteArray()) ?: return "" }.toByteArray()) ?: return ""
val body = oidb_sso.OIDBSSOPkg() val body = buffer.decodeToOidb()
body.mergeFrom(buffer.slice(4))
val result = oidb_0x6d6.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray()) val result = oidb_0x6d6.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray())
if (body.uint32_result.get() != 0 if (body.uint32_result.get() != 0
|| result.download_file_rsp.int32_ret_code.get() != 0) { || result.download_file_rsp.int32_ret_code.get() != 0) {
@ -128,8 +128,7 @@ internal object RichProtoSvc: BaseSvc() {
} }
return "" return ""
} else { } else {
val body = oidb_sso.OIDBSSOPkg() val body = buffer.decodeToOidb()
body.mergeFrom(buffer.slice(4))
val result = cmd0x346.RspBody().mergeFrom(cmd0xe37.Resp0xe37().mergeFrom( val result = cmd0x346.RspBody().mergeFrom(cmd0xe37.Resp0xe37().mergeFrom(
body.bytes_bodybuffer.get().toByteArray() body.bytes_bodybuffer.get().toByteArray()
).bytes_cmd_0x346_rsp_body.get().toByteArray()) ).bytes_cmd_0x346_rsp_body.get().toByteArray())
@ -150,6 +149,75 @@ internal object RichProtoSvc: BaseSvc() {
} }
} }
suspend fun getTempPicDownloadUrl(
chatType: Int,
originalUrl: String,
md5: String,
image: PicElement,
storeId: Int = 0,
peer: String? = null,
subPeer: String? = null,
): String {
val isNtServer = originalUrl.startsWith("/download")
if (isNtServer) {
val tmpRKey = NtV2RichMediaSvc.getTempNtRKey()
if (tmpRKey.isSuccess) {
val tmpRKeyRsp = tmpRKey.getOrThrow()
val tmpRKeyMap = hashMapOf<UInt, String>()
tmpRKeyRsp.rkeys?.forEach { rKeyInfo ->
tmpRKeyMap[rKeyInfo.type] = rKeyInfo.rkey
}
val rkey = tmpRKeyMap[when(chatType) {
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> 10u
MsgConstant.KCHATTYPEC2C -> 20u
MsgConstant.KCHATTYPEGUILD -> 10u
else -> 0u
}]
if (rkey != null) {
return "https://$MULTIMEDIA_DOMAIN$originalUrl$rkey"
}
}
}
return when (chatType) {
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> getGroupPicDownUrl(
originalUrl = originalUrl,
md5 = md5,
fileId = image.fileUuid,
width = image.picWidth.toUInt(),
height = image.picHeight.toUInt(),
sha = "",
fileSize = image.fileSize.toULong(),
peer = peer ?: "0"
)
MsgConstant.KCHATTYPEC2C -> getC2CPicDownUrl(
originalUrl = originalUrl,
md5 = md5,
fileId = image.fileUuid,
width = image.picWidth.toUInt(),
height = image.picHeight.toUInt(),
sha = "",
fileSize = image.fileSize.toULong(),
peer = peer ?: "0",
storeId = storeId
)
MsgConstant.KCHATTYPEGUILD -> getGuildPicDownUrl(
originalUrl = originalUrl,
md5 = md5,
fileId = image.fileUuid,
width = image.picWidth.toUInt(),
height = image.picHeight.toUInt(),
sha = "",
fileSize = image.fileSize.toULong(),
peer = peer ?: "0",
subPeer = subPeer ?: "0"
)
else -> throw UnsupportedOperationException("Not supported chat type: $chatType")
}
}
suspend fun getGroupPicDownUrl( suspend fun getGroupPicDownUrl(
originalUrl: String, originalUrl: String,
md5: String, md5: String,

View File

@ -1,11 +1,11 @@
package moe.fuqiuluo.shamrock.helper package moe.fuqiuluo.shamrock.helper
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import mqq.app.MobileQQ import mqq.app.MobileQQ
import java.io.File import java.io.File
internal object LocalCacheHelper: BaseSvc() { internal object LocalCacheHelper: QQInterfaces() {
// 获取外部储存data目录 // 获取外部储存data目录
private val dataDir = MobileQQ.getContext().getExternalFilesDir(null)!! private val dataDir = MobileQQ.getContext().getExternalFilesDir(null)!!
.parentFile!!.resolve("Tencent") .parentFile!!.resolve("Tencent")

View File

@ -9,8 +9,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
import moe.fuqiuluo.shamrock.xposed.hooks.toast import moe.fuqiuluo.shamrock.tools.toast
import moe.fuqiuluo.shamrock.xposed.helper.internal.DataRequester import moe.fuqiuluo.shamrock.xposed.helper.AppTalker
import mqq.app.MobileQQ import mqq.app.MobileQQ
import java.io.File import java.io.File
import java.util.Date import java.util.Date
@ -60,10 +60,10 @@ internal object LogCenter {
} }
// 把日志广播到主进程 // 把日志广播到主进程
GlobalScope.launch(Dispatchers.Default) { GlobalScope.launch(Dispatchers.Default) {
DataRequester.request("send_message", bodyBuilder = { AppTalker.talk("send_message") {
put("string", string) put("string", string)
put("level", level.id) put("level", level.id)
}) }
} }
if (!LogFile.exists()) { if (!LogFile.exists()) {
@ -89,10 +89,10 @@ internal object LogCenter {
} }
// 把日志广播到主进程 // 把日志广播到主进程
GlobalScope.launch(Dispatchers.Default) { GlobalScope.launch(Dispatchers.Default) {
DataRequester.request("send_message", bodyBuilder = { AppTalker.talk("send_message") {
put("string", log) put("string", log)
put("level", level.id) put("level", level.id)
}) }
} }
if (!LogFile.exists()) { if (!LogFile.exists()) {

View File

@ -1,10 +1,13 @@
package moe.fuqiuluo.shamrock.helper package moe.fuqiuluo.shamrock.helper
import com.tencent.mobileqq.qroute.QRoute import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qqnt.kernel.nativeinterface.Contact 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.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.kernel.nativeinterface.TempChatInfo
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact
import com.tencent.qqnt.msg.api.IMsgService import com.tencent.qqnt.msg.api.IMsgService
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -16,13 +19,15 @@ import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import moe.fuqiuluo.qqinterface.servlet.MsgSvc import moe.fuqiuluo.qqinterface.servlet.MsgSvc
import moe.fuqiuluo.qqinterface.servlet.MsgSvc.prepareTempChatFromGroup
import moe.fuqiuluo.qqinterface.servlet.msg.maker.ElemMaker import moe.fuqiuluo.qqinterface.servlet.msg.maker.ElemMaker
import moe.fuqiuluo.qqinterface.servlet.msg.maker.NtMsgElementMaker import moe.fuqiuluo.qqinterface.servlet.msg.maker.NtMsgElementMaker
import moe.fuqiuluo.shamrock.helper.db.MessageDB import moe.fuqiuluo.shamrock.helper.db.MessageDB
import moe.fuqiuluo.shamrock.helper.db.MessageMapping import moe.fuqiuluo.shamrock.helper.db.MessageMapping
import moe.fuqiuluo.shamrock.remote.structures.SendMsgResult import moe.fuqiuluo.shamrock.remote.structures.SendMsgResult
import moe.fuqiuluo.shamrock.tools.* import moe.fuqiuluo.shamrock.tools.*
import protobuf.message.Elem import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces.Companion.app
import moe.fuqiuluo.shamrock.xposed.helper.msgService
import protobuf.message.RichText import protobuf.message.RichText
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.math.abs import kotlin.math.abs
@ -49,6 +54,28 @@ internal object MessageHelper {
return sendMessageWithoutMsgId(chatType, peerId, msg, fromId, callback) return sendMessageWithoutMsgId(chatType, peerId, msg, fromId, callback)
} }
suspend fun sendMessage(contact: Contact, msgs: ArrayList<MsgElement>, retry: Int, msgId: SendMsgResult): Result<SendMsgResult> {
if (contact.chatType == MsgConstant.KCHATTYPETEMPC2CFROMGROUP) {
prepareTempChatFromGroup(contact.guildId, contact.peerUid).getOrThrow()
}
return withTimeoutOrNull(5000) {
suspendCancellableCoroutine {
QRoute.api(IMsgService::class.java).sendMsg(contact, msgId.qqMsgId, msgs) { code: Int, msg: String ->
if (code == 0) {
it.resume(msgId.qqMsgId)
} else {
LogCenter.log("消息发送失败: $code:$msg", Level.WARN)
it.resume(null)
}
}
}
}?.let { Result.success(SendMsgResult(
msgHashId = msgId.msgHashId,
qqMsgId = it,
msgTime = System.currentTimeMillis()
)) } ?: resendMsg(contact, msgId.qqMsgId, retry, msgHashId = msgId.msgHashId)
}
suspend fun resendMsg( suspend fun resendMsg(
chatType: Int, chatType: Int,
peerId: String, peerId: String,
@ -247,6 +274,40 @@ internal object MessageHelper {
} }
} }
suspend fun getTempChatInfo(chatType: Int, uid: String): Result<TempChatInfo> {
val msgService = app.getRuntimeService(IKernelService::class.java, "all").msgService
?: return Result.failure(Exception("获取消息服务失败"))
val info: TempChatInfo = withTimeoutOrNull(5000) {
suspendCancellableCoroutine {
msgService.getTempChatInfo(chatType, uid) { code, msg, tempChatInfo ->
if (code == 0) {
it.resume(tempChatInfo)
} else {
LogCenter.log("获取临时会话信息失败: $code:$msg", Level.ERROR)
it.resume(null)
}
}
}
} ?: return Result.failure(Exception("获取临时会话信息失败"))
return Result.success(info)
}
suspend fun generateContact(record: MsgRecord): Contact {
val peerId = when (record.chatType) {
MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> record.senderUid
MsgConstant.KCHATTYPEGUILD -> record.channelId
else -> record.peerUin.toString()
}
return Contact(record.chatType, peerId, if (record.chatType == MsgConstant.KCHATTYPEGUILD) {
record.guildId
} else if(record.chatType == MsgConstant.KCHATTYPETEMPC2CFROMGROUP) {
val tempInfo = getTempChatInfo(record.chatType, peerId).getOrThrow()
tempInfo.groupCode
} else {
null
})
}
suspend fun generateContact(chatType: Int, id: String, subId: String = ""): Contact { suspend fun generateContact(chatType: Int, id: String, subId: String = ""): Contact {
val peerId = when (chatType) { val peerId = when (chatType) {
MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> { MsgConstant.KCHATTYPEC2C, MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> {

View File

@ -1,5 +1,6 @@
package moe.fuqiuluo.shamrock.remote package moe.fuqiuluo.shamrock.remote
import com.tencent.mobileqq.app.QQAppInterface
import io.ktor.server.application.Application import io.ktor.server.application.Application
import io.ktor.server.application.install import io.ktor.server.application.install
import io.ktor.server.engine.ApplicationEngine import io.ktor.server.engine.ApplicationEngine
@ -21,7 +22,9 @@ import moe.fuqiuluo.shamrock.remote.plugin.Auth
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.xposed.helper.internal.DataRequester import moe.fuqiuluo.shamrock.tools.ShamrockVersion
import moe.fuqiuluo.shamrock.xposed.helper.AppTalker
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces.Companion.app
import moe.fuqiuluo.shamrock.xposed.loader.NativeLoader import moe.fuqiuluo.shamrock.xposed.loader.NativeLoader
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.security.KeyStore import java.security.KeyStore
@ -138,10 +141,13 @@ internal object HTTPServer {
isServiceStarted = true isServiceStarted = true
currServerPort = port currServerPort = port
LogCenter.log("Start HTTP Server: http://0.0.0.0:$currServerPort/") LogCenter.log("Start HTTP Server: http://0.0.0.0:$currServerPort/")
DataRequester.request("success", values = mapOf( AppTalker.talk("success") {
"port" to currServerPort, put("account", app.currentAccountUin)
"voice" to NativeLoader.isVoiceLoaded put("nickname", if (app is QQAppInterface) (app.currentNickname ?: "unknown") else "unknown")
)) put("voice", NativeLoader.isVoiceLoaded)
put("core_version", ShamrockVersion)
put("port", currServerPort)
}
} }
fun isActive(): Boolean { fun isActive(): Boolean {

View File

@ -17,7 +17,7 @@ internal object BanTroopMember: IActionHandler() {
return invoke(groupId, userId, duration, session.echo) return invoke(groupId, userId, duration, session.echo)
} }
operator fun invoke( suspend operator fun invoke(
groupId: Long, groupId: Long,
userId: Long, userId: Long,
duration: Int = 30 * 60, duration: Int = 30 * 60,

View File

@ -14,6 +14,7 @@ import moe.fuqiuluo.shamrock.remote.service.data.HONOR_NEWBIE
import moe.fuqiuluo.shamrock.remote.service.data.HONOR_TALKATIVE import moe.fuqiuluo.shamrock.remote.service.data.HONOR_TALKATIVE
import moe.fuqiuluo.shamrock.tools.EmptyJsonString import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.symbols.OneBotHandler import moe.fuqiuluo.symbols.OneBotHandler
import tencent.im.troop.honor.troop_honor
@OneBotHandler("get_group_honor_info", ["get_troop_honor_info"]) @OneBotHandler("get_group_honor_info", ["get_troop_honor_info"])
internal object GetTroopHonor: IActionHandler() { internal object GetTroopHonor: IActionHandler() {
@ -29,11 +30,15 @@ internal object GetTroopHonor: IActionHandler() {
GroupSvc.getGroupMemberList(groupId, refresh).onFailure { GroupSvc.getGroupMemberList(groupId, refresh).onFailure {
return error(it.message ?: "unknown error", echo) return error(it.message ?: "unknown error", echo)
}.onSuccess { memberList -> }.onSuccess { memberList ->
memberList.forEach { member -> memberList.values.forEach { info ->
GroupSvc.parseHonor(member.honorList).forEach { info.groupHonor.let { bytes ->
val honor = decodeHonor(member.memberuin.toLong(), it, member.mHonorRichFlag) val honor = troop_honor.GroupUserCardHonor()
honor.mergeFrom(bytes)
honor.id.get()
}.forEach {
val honor = decodeHonor(info.uin, it, 0)
if (honor != null) { if (honor != null) {
honor.nick = member.troopnick.ifEmpty { member.friendnick } honor.nick = info.nick.ifEmpty { info.cardName }
honorInfo.add(honor) honorInfo.add(honor)
} }
} }

View File

@ -1,13 +1,18 @@
package moe.fuqiuluo.shamrock.remote.action.handlers package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.mobileqq.data.Card import com.tencent.mobileqq.data.Card
import com.tencent.mobileqq.data.troop.TroopMemberInfo
import com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole as NtMemberRole
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.GroupSvc import moe.fuqiuluo.qqinterface.servlet.GroupSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.remote.service.data.SimpleTroopMemberInfo import moe.fuqiuluo.shamrock.remote.service.data.SimpleTroopMemberInfo
import moe.fuqiuluo.shamrock.remote.service.data.push.MemberRole
import moe.fuqiuluo.shamrock.tools.EmptyJsonString import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty
import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.utils.PlatformUtils.QQ_9_0_65_VER
import moe.fuqiuluo.symbols.OneBotHandler import moe.fuqiuluo.symbols.OneBotHandler
@OneBotHandler("get_group_member_info") @OneBotHandler("get_group_member_info")
@ -26,40 +31,91 @@ internal object GetTroopMemberInfo : IActionHandler() {
refresh: Boolean, refresh: Boolean,
echo: JsonElement = EmptyJsonString echo: JsonElement = EmptyJsonString
): String { ): String {
val info = GroupSvc.getTroopMemberInfoByUin(groupId, userId, refresh).onFailure { val info = GroupSvc.getTroopMemberInfoByUinV2(groupId, userId, refresh).onFailure {
return error(it.message ?: "unknown error", echo) return error(it.message ?: "unknown error", echo)
}.getOrThrow() }.getOrThrow()
return ok( val code = PlatformUtils.getQQVersionCode()
SimpleTroopMemberInfo( return ok(when {
uin = info.memberuin.toLong(), (code >= QQ_9_0_65_VER) -> ntQQApiData(groupId, userId, info)
name = info.friendnick.ifNullOrEmpty(info.autoremark) ?: "", else -> oldQQApiData(groupId, userId, info)
showName = info.troopnick.ifNullOrEmpty(info.troopColorNick), }, echo)
cardName = info.troopnick.ifNullOrEmpty(info.troopColorNick), }
distance = info.distance,
honor = GroupSvc.parseHonor(info.honorList), private suspend fun ntQQApiData(groupId: Long, userId: Long, info: TroopMemberInfo): SimpleTroopMemberInfo {
joinTime = info.join_time, return SimpleTroopMemberInfo(
lastActiveTime = info.last_active_time, uin = info.memberuin.toLong(),
uniqueName = info.mUniqueTitle, name = info.friendnick.ifNullOrEmpty(info.autoremark) ?: "",
groupId = groupId, showName = info.troopnick.ifNullOrEmpty(info.troopColorNick),
nick = info.friendnick.ifNullOrEmpty(info.autoremark) ?: "", cardName = info.troopnick.ifNullOrEmpty(info.troopColorNick),
sex = when (info.sex.toShort()) { distance = 0,
Card.FEMALE -> "female" honor = GroupSvc.parseHonor(info.honorList),
Card.MALE -> "male" joinTime = info.join_time,
else -> "unknown" lastActiveTime = info.last_active_time,
}, uniqueName = null,
area = info.alias ?: "", groupId = groupId,
lastSentTime = info.last_active_time, nick = info.friendnick.ifNullOrEmpty(info.autoremark) ?: "",
level = info.level, sex = "unknown",
role = GroupSvc.getMemberRole(groupId, userId), area = "",
unfriendly = false, lastSentTime = info.last_active_time,
title = info.mUniqueTitle ?: "", level = info.level,
titleExpireTime = info.mUniqueTitleExpire, role = when(info.role) {
cardChangeable = GroupSvc.isAdmin(groupId), NtMemberRole.UNSPECIFIED -> MemberRole.Unknown
age = info.age.toInt(), NtMemberRole.STRANGER -> MemberRole.Stranger
shutUpTimestamp = 0L NtMemberRole.MEMBER -> MemberRole.Member
), echo NtMemberRole.ADMIN -> MemberRole.Admin
) NtMemberRole.OWNER -> MemberRole.Owner
null -> MemberRole.Unknown
},
unfriendly = false,
title = "",
titleExpireTime = 0,
cardChangeable = info.role == NtMemberRole.OWNER || info.role == NtMemberRole.ADMIN,
age = 0,
shutUpTimestamp = 0L
).also {
if (info.specialTitleInfo != null) {
it.uniqueName = info.specialTitleInfo?.specialTitle ?: ""
it.title = info.specialTitleInfo?.specialTitle ?: ""
it.titleExpireTime = info.specialTitleInfo?.expireTimeSec ?: 0
}
}
}
private suspend fun oldQQApiData(groupId: Long, userId: Long, info: TroopMemberInfo): SimpleTroopMemberInfo {
return SimpleTroopMemberInfo(
uin = info.memberuin.toLong(),
name = info.friendnick.ifNullOrEmpty(info.autoremark) ?: "",
showName = info.troopnick.ifNullOrEmpty(info.troopColorNick),
cardName = info.troopnick.ifNullOrEmpty(info.troopColorNick),
distance = 0,
honor = GroupSvc.parseHonor(info.honorList),
joinTime = info.join_time,
lastActiveTime = info.last_active_time,
uniqueName = info.mUniqueTitle,
groupId = groupId,
nick = info.friendnick.ifNullOrEmpty(info.autoremark) ?: "",
sex = when (info.sex.toShort()) {
Card.FEMALE -> "female"
Card.MALE -> "male"
else -> "unknown"
},
area = "",
lastSentTime = info.last_active_time,
level = info.level,
role = GroupSvc.getMemberRole(groupId, userId),
unfriendly = false,
title = info.mUniqueTitle ?: "",
titleExpireTime = info.mUniqueTitleExpire,
cardChangeable = GroupSvc.isAdmin(groupId),
age = info.age.toInt(),
shutUpTimestamp = 0L
).also {
if (PlatformUtils.getQQVersionCode() <= QQ_9_0_65_VER) {
it.distance = info.distance
it.area = info.alias
}
}
} }
override val requiredParams: Array<String> = arrayOf("user_id", "group_id") override val requiredParams: Array<String> = arrayOf("user_id", "group_id")

View File

@ -1,6 +1,5 @@
package moe.fuqiuluo.shamrock.remote.action.handlers package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.mobileqq.data.Card
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.GroupSvc import moe.fuqiuluo.qqinterface.servlet.GroupSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.ActionSession
@ -8,8 +7,8 @@ import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.remote.service.data.SimpleTroopMemberInfo import moe.fuqiuluo.shamrock.remote.service.data.SimpleTroopMemberInfo
import moe.fuqiuluo.shamrock.remote.service.data.push.MemberRole import moe.fuqiuluo.shamrock.remote.service.data.push.MemberRole
import moe.fuqiuluo.shamrock.tools.EmptyJsonString import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty
import moe.fuqiuluo.symbols.OneBotHandler import moe.fuqiuluo.symbols.OneBotHandler
import tencent.im.troop.honor.troop_honor
@OneBotHandler("get_group_member_list") @OneBotHandler("get_group_member_list")
internal object GetTroopMemberList : IActionHandler() { internal object GetTroopMemberList : IActionHandler() {
@ -27,49 +26,48 @@ internal object GetTroopMemberList : IActionHandler() {
val memberList = GroupSvc.getGroupMemberList(groupId, refresh).onFailure { val memberList = GroupSvc.getGroupMemberList(groupId, refresh).onFailure {
return error(it.message ?: "unknown error", echo, arrayResult = true) return error(it.message ?: "unknown error", echo, arrayResult = true)
}.getOrThrow() }.getOrThrow()
val prohibitedMemberList = GroupSvc.getProhibitedMemberList(groupId) // val prohibitedMemberList = GroupSvc.getProhibitedMemberList(groupId)
.getOrDefault(arrayListOf()) // .getOrDefault(arrayListOf())
.associate { it.memberUin to it.shutuptimestap.toLong() } // .associate { it.memberUin to it.shutuptimestap.toLong() }
return ok(arrayListOf<SimpleTroopMemberInfo>().apply { return ok(arrayListOf<SimpleTroopMemberInfo>().apply {
memberList.forEach { info -> memberList.values.forEach { info ->
if (info.memberuin != "0") { add(
add( SimpleTroopMemberInfo(
SimpleTroopMemberInfo( uin = info.uin,
uin = info.memberuin.toLong(), name = info.nick ?: "",
name = info.friendnick.ifNullOrEmpty(info.autoremark) ?: "", showName = info.cardName,
showName = info.troopnick.ifNullOrEmpty(info.troopColorNick), cardName = info.cardName,
cardName = info.troopnick.ifNullOrEmpty(info.troopColorNick), distance = 0,
distance = info.distance, honor = info.groupHonor.let { bytes ->
honor = GroupSvc.parseHonor(info.honorList), val honor = troop_honor.GroupUserCardHonor()
joinTime = info.join_time, honor.mergeFrom(bytes)
lastActiveTime = info.last_active_time, honor.id.get()
uniqueName = info.mUniqueTitle, },
groupId = groupId, joinTime = info.joinTime.toLong(),
nick = info.friendnick.ifNullOrEmpty(info.autoremark) ?: "", lastActiveTime = info.lastSpeakTime.toLong(),
sex = when (info.sex.toShort()) { uniqueName = info.memberSpecialTitle,
Card.FEMALE -> "female" groupId = groupId,
Card.MALE -> "male" nick = info.nick ?: "",
else -> "unknown" sex = "unknown",
}, area = "",
area = info.alias ?: "", lastSentTime = info.lastSpeakTime.toLong(),
lastSentTime = info.last_active_time, level = info.memberLevel,
level = info.level, role = info.role.let { role ->
role = GroupSvc.getMemberRole(groupId, info.memberuin.toLong()) when (role) {
/*when { com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole.OWNER -> MemberRole.Owner
GroupSvc.getOwner(groupId) com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole.ADMIN -> MemberRole.Admin
.toString() == info.memberuin -> MemberRole.Owner
info.memberuin.toLong() in GroupSvc.getAdminList(groupId) -> MemberRole.Admin
else -> MemberRole.Member else -> MemberRole.Member
}*/, }
unfriendly = false, },
title = info.mUniqueTitle ?: "", unfriendly = false,
titleExpireTime = info.mUniqueTitleExpire, title = info.memberSpecialTitle ?: "",
cardChangeable = GroupSvc.isAdmin(groupId), titleExpireTime = info.specialTitleExpireTime.toInt(),
age = 0, cardChangeable = info.role == com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole.ADMIN,
shutUpTimestamp = prohibitedMemberList[info.memberuin.toLong()] ?: 0L age = 0,
) shutUpTimestamp = info.shutUpTime.toLong()
) )
} )
} }
}, echo) }, echo)
} }

View File

@ -17,7 +17,7 @@ internal object ModifyTroopMemberName: IActionHandler() {
return invoke(groupId, userId, name, session.echo) return invoke(groupId, userId, name, session.echo)
} }
operator fun invoke(groupId: Long, userId: Long, card: String, echo: JsonElement = EmptyJsonString): String { suspend operator fun invoke(groupId: Long, userId: Long, card: String, echo: JsonElement = EmptyJsonString): String {
if (!GroupSvc.isAdmin(groupId) && userId != TicketSvc.getUin().toLong()) { if (!GroupSvc.isAdmin(groupId) && userId != TicketSvc.getUin().toLong()) {
return logic("you are not admin", echo) return logic("you are not admin", echo)
} }

View File

@ -16,7 +16,7 @@ internal object ModifyTroopName: IActionHandler() {
return invoke(groupId, groupName, session.echo) return invoke(groupId, groupName, session.echo)
} }
operator fun invoke(groupId: Long, name: String, echo: JsonElement = EmptyJsonString): String { suspend operator fun invoke(groupId: Long, name: String, echo: JsonElement = EmptyJsonString): String {
return if (GroupSvc.isAdmin(groupId)) { return if (GroupSvc.isAdmin(groupId)) {
GroupSvc.modifyTroopName(groupId, name) GroupSvc.modifyTroopName(groupId, name)
ok("成功", echo) ok("成功", echo)

View File

@ -0,0 +1,134 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.mobileqq.l0.b.c
import com.tencent.mobileqq.ocr.a.a
import com.tencent.mobileqq.ocr.api.IPicOcrService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MD5
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import moe.fuqiuluo.symbols.OneBotHandler
import java.io.File
import kotlin.coroutines.resume
@OneBotHandler("ocr_image")
internal object OcrImage: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val image = session.getString("image")
return invoke(image, session.echo)
}
suspend operator fun invoke(image: String, echo: JsonElement = EmptyJsonString): String {
val file = FileUtils.getFile(image).let {
if (!it.exists()) {
return@let FileUtils.parseAndSave(image)
}
it
}
LogCenter.log("${file.absoluteFile.absolutePath}进行OCR", Level.DEBUG)
val retryCount = 10
val delayMillis = 1000L
repeat(retryCount) {
val result = getOcrResult(file)
if (result.isSuccess) {
return ok(result.getOrNull(), echo)
} else if (result.isFailure && result.exceptionOrNull()?.message == "no cache") {
withContext(Dispatchers.IO) {
Thread.sleep(delayMillis)
}
} else {
return error(result.exceptionOrNull()?.message ?: "", echo)
}
}
return error("ocr failed", echo)
}
private suspend fun getOcrResult(file: File): Result<OcrResult> {
val ocrService = QQInterfaces.app.getRuntimeService(IPicOcrService::class.java, "all")
?: return Result.failure(Error("获取OCR服务失败"))
return withTimeoutOrNull(5000) {
suspendCancellableCoroutine { continuation ->
ocrService.requestOcr(
a().apply {
this.b = 1
this.c = file.absolutePath
this.f = MD5.genFileMd5Hex(file.absolutePath)
}, object : com.tencent.mobileqq.l0.b.a() {
override fun b() {
// call uploadOcrPic and then call b
continuation.resume(Result.failure(Error("no cache")))
}
override fun c(result: c?) {
continuation.resume(Result.success(OcrResult(
texts = result?.f?.map {
TextDetection(
text = it.a,
confidence = it.b,
coordinates = it.c.map {
ArrayList<Int>().apply {
add(it.x)
add(it.y)
}
}
)
}!!,
language = result.h[result.d] ?: result.d,
url = result.e
)))
}
override fun onUpdate(i2: Int, z: Boolean, obj: Any?) {
LogCenter.log("$i2, $z, ${obj.toString()}")
if (i2 == 100) {
val result = obj as c
continuation.resume(Result.success(OcrResult(
texts = result.f.map {
TextDetection(
text = it.a,
confidence = it.b,
coordinates = it.c.map {
ArrayList<Int>().apply {
add(it.x)
add(it.y)
}
}
)
},
language = result.h[result.d] ?: result.d,
url = result.e
)))
}
}
})
}
} ?: Result.failure(Exception("OCR timed out"))
}
override val requiredParams: Array<String> = arrayOf("group_id", "user_id")
@Serializable
data class OcrResult (
val texts: List<TextDetection>,
val language: String,
val url: String
)
@Serializable
data class TextDetection (
val text: String,
val confidence: Int,
val coordinates: List<List<Int>>
)
}

View File

@ -2,11 +2,10 @@ package moe.fuqiuluo.shamrock.remote.action.handlers
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.tools.EmptyJsonString import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import moe.fuqiuluo.symbols.OneBotHandler import moe.fuqiuluo.symbols.OneBotHandler
import protobuf.auto.toByteArray import protobuf.auto.toByteArray
import protobuf.message.* import protobuf.message.*
@ -51,7 +50,7 @@ internal object SendMsgByResid : IActionHandler() {
msgRand = Random.nextUInt(), msgRand = Random.nextUInt(),
msgVia = 0u msgVia = 0u
) )
BaseSvc.sendBufferAW("MessageSvc.PbSendMsg", true, req.toByteArray()) QQInterfaces.sendBufferAW("MessageSvc.PbSendMsg", true, req.toByteArray())
return ok("ok", echo) return ok("ok", echo)
} }

View File

@ -39,7 +39,6 @@ import moe.fuqiuluo.shamrock.tools.hex2ByteArray
import moe.fuqiuluo.shamrock.tools.json import moe.fuqiuluo.shamrock.tools.json
import moe.fuqiuluo.shamrock.tools.respond import moe.fuqiuluo.shamrock.tools.respond
import moe.fuqiuluo.shamrock.tools.toHexString import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.shamrock.xposed.ipc.ShamrockIpc
import moe.fuqiuluo.shamrock.xposed.ipc.bytedata.IByteData import moe.fuqiuluo.shamrock.xposed.ipc.bytedata.IByteData
import moe.fuqiuluo.shamrock.xposed.ipc.qsign.IQSigner import moe.fuqiuluo.shamrock.xposed.ipc.qsign.IQSigner
import mqq.app.MobileQQ import mqq.app.MobileQQ
@ -113,10 +112,7 @@ fun Routing.qsign() {
return@get return@get
} }
if (signer == null || signer?.asBinder()?.isBinderAlive == false) { if (signer == null || signer?.asBinder()?.isBinderAlive == false) {
if (!initSigner()) { call.respond(OldApiResult(-2, "不支持的操作", null))
respond(false, Status.InternalHandlerError)
return@get
}
} }
val list = signer!!.cmdWhiteList val list = signer!!.cmdWhiteList
call.respond(OldApiResult(0, "success", list)) call.respond(OldApiResult(0, "success", list))
@ -128,10 +124,7 @@ fun Routing.qsign() {
return@getOrPost return@getOrPost
} }
if (signer == null || signer?.asBinder()?.isBinderAlive == false) { if (signer == null || signer?.asBinder()?.isBinderAlive == false) {
if (!initSigner()) { call.respond(OldApiResult(-2, "不支持的操作", null))
respond(false, Status.InternalHandlerError)
return@getOrPost
}
} }
val uin = fetchOrThrow("uin") val uin = fetchOrThrow("uin")
@ -213,43 +206,7 @@ fun Routing.qsign() {
} }
get("/get_byte") { get("/get_byte") {
if (!isMsfServiceAlive()) { call.respond(OldApiResult(-2, "不支持的操作", null))
call.respond(OldApiResult(-2, "MSF服务未启动", null))
return@get
}
if (byteData == null || byteData?.asBinder()?.isBinderAlive == false) {
val binder = ShamrockIpc.get(ShamrockIpc.IPC_BYTEDATA)
if (binder == null) {
call.respond(OldApiResult(-2, "获取失败", null))
return@get
} else {
byteData = IByteData.Stub.asInterface(binder)
binder.linkToDeath({
byteData = null
}, 0)
}
}
val data = fetchGetOrThrow("data")
if(!(data.startsWith("810_") || data.startsWith("812_"))) {
call.respond(OldApiResult(-2, "data参数不合法", null))
return@get
}
val uin = fetchOrThrow("uin")
val salt = fetchSalt(data, uin)
if (salt.isEmpty()) {
call.respond(OldApiResult(-2, "无法自动决断mode请主动提供", null))
return@get
}
val sign = byteData!!.sign(uin, data, salt).sign
if (sign == null) {
call.respond(OldApiResult(-2, "获取失败", null))
} else {
call.respond(OldApiResult(0, "success", sign.toHexString()))
}
} }
get("/friend_sign") { get("/friend_sign") {
@ -258,10 +215,7 @@ fun Routing.qsign() {
return@get return@get
} }
if (signer == null || signer?.asBinder()?.isBinderAlive == false) { if (signer == null || signer?.asBinder()?.isBinderAlive == false) {
if (!initSigner()) { call.respond(OldApiResult(-2, "不支持的操作", null))
respond(false, Status.InternalHandlerError)
return@get
}
} }
val addUin = fetchOrThrow("add_uin") val addUin = fetchOrThrow("add_uin")
@ -286,10 +240,7 @@ fun Routing.qsign() {
return@get return@get
} }
if (signer == null || signer?.asBinder()?.isBinderAlive == false) { if (signer == null || signer?.asBinder()?.isBinderAlive == false) {
if (!initSigner()) { call.respond(OldApiResult(-2, "不支持的操作", null))
respond(false, Status.InternalHandlerError)
return@get
}
} }
val addUin = fetchOrThrow("group_uin") val addUin = fetchOrThrow("group_uin")
@ -387,23 +338,6 @@ private data class Sign(
val requestCallback: List<Int> val requestCallback: List<Int>
) )
private suspend fun initSigner(): Boolean {
if (!isMsfServiceAlive()) {
return false
}
val binder = ShamrockIpc.get(ShamrockIpc.IPC_QSIGN)
if (binder == null) {
//respond(false, Status.InternalHandlerError)
return false
} else {
signer = IQSigner.Stub.asInterface(binder)
binder.linkToDeath({
signer = null
}, 0)
return true
}
}
private suspend fun PipelineContext<Unit, ApplicationCall>.requestSign( private suspend fun PipelineContext<Unit, ApplicationCall>.requestSign(
cmd: String, cmd: String,
uin: String, uin: String,
@ -415,10 +349,7 @@ private suspend fun PipelineContext<Unit, ApplicationCall>.requestSign(
return return
} }
if (signer == null || signer?.asBinder()?.isBinderAlive == false) { if (signer == null || signer?.asBinder()?.isBinderAlive == false) {
if (!initSigner()) { call.respond(OldApiResult(-2, "不支持的操作", null))
respond(false, Status.InternalHandlerError)
return
}
} }
val sign = withTimeoutOrNull(5000) { val sign = withTimeoutOrNull(5000) {

View File

@ -12,6 +12,7 @@ import io.ktor.server.routing.post
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import moe.fuqiuluo.shamrock.remote.action.handlers.CleanCache import moe.fuqiuluo.shamrock.remote.action.handlers.CleanCache
import moe.fuqiuluo.shamrock.remote.action.handlers.DownloadFile import moe.fuqiuluo.shamrock.remote.action.handlers.DownloadFile
@ -19,8 +20,8 @@ import moe.fuqiuluo.shamrock.remote.action.handlers.GetDeviceBattery
import moe.fuqiuluo.shamrock.remote.action.handlers.GetVersionInfo import moe.fuqiuluo.shamrock.remote.action.handlers.GetVersionInfo
import moe.fuqiuluo.shamrock.remote.action.handlers.RestartMe import moe.fuqiuluo.shamrock.remote.action.handlers.RestartMe
import moe.fuqiuluo.shamrock.remote.action.handlers.UploadFileToShamrock import moe.fuqiuluo.shamrock.remote.action.handlers.UploadFileToShamrock
import moe.fuqiuluo.shamrock.remote.structures.Status
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
import moe.fuqiuluo.shamrock.remote.structures.Status
import moe.fuqiuluo.shamrock.tools.asString import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.fetchOrNull import moe.fuqiuluo.shamrock.tools.fetchOrNull
import moe.fuqiuluo.shamrock.tools.fetchOrThrow import moe.fuqiuluo.shamrock.tools.fetchOrThrow
@ -34,6 +35,7 @@ import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MD5 import moe.fuqiuluo.shamrock.utils.MD5
import java.io.File import java.io.File
@OptIn(ExperimentalSerializationApi::class)
fun Routing.otherAction() { fun Routing.otherAction() {
if (ShamrockConfig.allowShell()) { if (ShamrockConfig.allowShell()) {
@ -144,4 +146,5 @@ fun Routing.otherAction() {
ShamrockConfig[key] = value ShamrockConfig[key] = value
respond(true, Status.Ok, "success") respond(true, Status.Ok, "success")
} }
} }

View File

@ -2,13 +2,13 @@ package moe.fuqiuluo.shamrock.remote.api
import com.tencent.mobileqq.dt.model.FEBound import com.tencent.mobileqq.dt.model.FEBound
import io.ktor.server.routing.Routing import io.ktor.server.routing.Routing
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.shamrock.remote.structures.Protocol import moe.fuqiuluo.shamrock.remote.structures.Protocol
import moe.fuqiuluo.shamrock.remote.structures.QSignDtConfig import moe.fuqiuluo.shamrock.remote.structures.QSignDtConfig
import moe.fuqiuluo.shamrock.remote.structures.Status import moe.fuqiuluo.shamrock.remote.structures.Status
import moe.fuqiuluo.shamrock.tools.* import moe.fuqiuluo.shamrock.tools.*
import moe.fuqiuluo.shamrock.utils.MMKVFetcher import moe.fuqiuluo.shamrock.utils.MMKVFetcher
import moe.fuqiuluo.shamrock.utils.PlatformUtils import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import mqq.app.MobileQQ import mqq.app.MobileQQ
import oicq.wlogin_sdk.tlv_type.tlv_t100 import oicq.wlogin_sdk.tlv_type.tlv_t100
import oicq.wlogin_sdk.tlv_type.tlv_t106 import oicq.wlogin_sdk.tlv_type.tlv_t106
@ -20,8 +20,8 @@ fun Routing.obtainProtocolData() {
val cmd = fetchOrThrow("cmd") val cmd = fetchOrThrow("cmd")
val isPb = fetchOrThrow("proto").toBooleanStrict() val isPb = fetchOrThrow("proto").toBooleanStrict()
val buffer = fetchOrThrow("buffer").hex2ByteArray() val buffer = fetchOrThrow("buffer").hex2ByteArray()
val resp = BaseSvc.sendBufferAW(cmd, isPb, buffer) val resp = QQInterfaces.sendBufferAW(cmd, isPb, buffer)
respond(true, Status.Ok, data = resp?.toHexString() ?: "null", msg = "成功") respond(true, Status.Ok, data = resp?.wupBuffer?.toHexString() ?: "null", msg = "成功")
} }
getOrPost("/set_guid") { getOrPost("/set_guid") {

View File

@ -21,22 +21,27 @@ private fun formatFileName(file: String): String = file
fun Routing.fetchRes() { fun Routing.fetchRes() {
getOrPost("/get_record") { getOrPost("/get_record") {
val file = formatFileName( fetchGetOrThrow("file") ) val file = formatFileName( fetchOrThrow("file") )
val format = fetchOrThrow("out_format") val format = fetchOrThrow("out_format")
call.respondText(GetRecord(file, format), ContentType.Application.Json) call.respondText(GetRecord(file, format), ContentType.Application.Json)
} }
getOrPost("/get_file") { getOrPost("/get_file") {
val file = formatFileName( fetchGetOrThrow("file") ) val file = formatFileName( fetchOrThrow("file") )
val fileType = fetchOrThrow("file_type") val fileType = fetchOrThrow("file_type")
call.respondText(GetFile(file, fileType), ContentType.Application.Json) call.respondText(GetFile(file, fileType), ContentType.Application.Json)
} }
getOrPost("/get_image") { getOrPost("/get_image") {
val file = formatFileName( fetchGetOrThrow("file") ) val file = formatFileName( fetchOrThrow("file") )
call.respondText(GetImage(file), ContentType.Application.Json) call.respondText(GetImage(file), ContentType.Application.Json)
} }
getOrPost("/ocr_image") {
val file = fetchOrThrow("image")
call.respondText(OcrImage(file), ContentType.Application.Json)
}
getOrPost("/upload_group_file") { getOrPost("/upload_group_file") {
val groupId = fetchOrThrow("group_id").toLong() val groupId = fetchOrThrow("group_id").toLong()
val file = fetchOrThrow("file") val file = fetchOrThrow("file")

View File

@ -10,9 +10,7 @@ import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.shamrock.helper.Level 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.helper.MessageHelper
import moe.fuqiuluo.shamrock.remote.action.handlers.SendMsgByResid
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
import moe.fuqiuluo.shamrock.tools.fetchOrNull
import moe.fuqiuluo.shamrock.tools.fetchOrThrow import moe.fuqiuluo.shamrock.tools.fetchOrThrow
import moe.fuqiuluo.shamrock.tools.getOrPost import moe.fuqiuluo.shamrock.tools.getOrPost
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher

View File

@ -6,7 +6,6 @@ import io.ktor.server.application.createApplicationPlugin
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
import moe.fuqiuluo.shamrock.tools.fetchOrNull import moe.fuqiuluo.shamrock.tools.fetchOrNull
import java.net.URLDecoder import java.net.URLDecoder
import java.nio.charset.Charset
private suspend fun ApplicationCall.checkToken() { private suspend fun ApplicationCall.checkToken() {
val token = ShamrockConfig.getToken() val token = ShamrockConfig.getToken()

View File

@ -1,69 +0,0 @@
@file:OptIn(DelicateCoroutinesApi::class)
package moe.fuqiuluo.shamrock.remote.service
import com.tencent.qphone.base.remote.FromServiceMsg
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import moe.fuqiuluo.shamrock.tools.broadcast
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.xposed.helper.internal.DynamicReceiver
import moe.fuqiuluo.shamrock.xposed.helper.internal.IPCRequest
import mqq.app.MobileQQ
internal object PacketReceiver {
private val allowCommandList: MutableSet<String> by lazy { mutableSetOf(
"trpc.msg.olpush.OlPushService.MsgPush",
) } // 非动态注册,永久常驻的包
private val HandlerByIpcSet = hashSetOf<String>()
fun init() {
DynamicReceiver.register("register_handler_cmd", IPCRequest {
val cmd = it.getStringExtra("handler_cmd")!!
LogCenter.log({ "RegisterHandler(cmd = $cmd)" }, Level.DEBUG)
HandlerByIpcSet.add(cmd)
})
DynamicReceiver.register("unregister_handler_cmd", IPCRequest {
val cmd = it.getStringExtra("handler_cmd")!!
LogCenter.log({ "UnRegisterHandler(cmd = $cmd)" }, Level.DEBUG)
HandlerByIpcSet.remove(cmd)
})
MobileQQ.getContext().broadcast("xqbot") {
putExtra("__cmd", "msf_waiter")
LogCenter.log("MSF Packet Receiver running!")
}
}
private fun onReceive(from: FromServiceMsg) {
if (HandlerByIpcSet.contains(from.serviceCmd)
|| allowCommandList.contains(from.serviceCmd)
) {
LogCenter.log({ "ReceivePacket(cmd = ${from.serviceCmd})" }, Level.DEBUG)
MobileQQ.getContext().broadcast("xqbot") {
putExtra("__cmd", from.serviceCmd)
putExtra("buffer", from.wupBuffer)
putExtra("seq", from.requestSsoSeq)
}
} else {
val seq = if (from.appSeq == -1) from.requestSsoSeq else from.appSeq
val hash = (from.serviceCmd + seq).hashCode()
LogCenter.log({ "ReceivePacket[$hash](cmd = ${from.serviceCmd}, seq = $seq)" }, Level.DEBUG)
MobileQQ.getContext().broadcast("xqbot") {
putExtra("__hash", hash)
putExtra("buffer", from.wupBuffer)
putExtra("seq", seq)
}
}
}
fun internalOnReceive(from: FromServiceMsg?) {
if (from == null) return
GlobalScope.launch(Dispatchers.Default) {
onReceive(from)
}
}
}

View File

@ -9,7 +9,6 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.qqinterface.servlet.CardSvc import moe.fuqiuluo.qqinterface.servlet.CardSvc
import moe.fuqiuluo.qqinterface.servlet.GroupSvc import moe.fuqiuluo.qqinterface.servlet.GroupSvc
import moe.fuqiuluo.qqinterface.servlet.msg.toSegments import moe.fuqiuluo.qqinterface.servlet.msg.toSegments
@ -35,9 +34,10 @@ import moe.fuqiuluo.shamrock.remote.service.data.push.Sender
import moe.fuqiuluo.shamrock.remote.service.data.push.SignDetail import moe.fuqiuluo.shamrock.remote.service.data.push.SignDetail
import moe.fuqiuluo.shamrock.tools.ShamrockDsl import moe.fuqiuluo.shamrock.tools.ShamrockDsl
import moe.fuqiuluo.shamrock.tools.json import moe.fuqiuluo.shamrock.tools.json
import moe.fuqiuluo.shamrock.xposed.helper.QQInterfaces
import java.util.ArrayList import java.util.ArrayList
internal object GlobalEventTransmitter: BaseSvc() { internal object GlobalEventTransmitter: QQInterfaces() {
private val messageEventFlow by lazy { private val messageEventFlow by lazy {
MutableSharedFlow<Pair<MsgRecord, MessageEvent>>() MutableSharedFlow<Pair<MsgRecord, MessageEvent>>()
} }

View File

@ -1,284 +1,13 @@
package moe.fuqiuluo.shamrock.remote.service.config package moe.fuqiuluo.shamrock.remote.service.config
import android.content.Intent import android.content.Intent
import com.tencent.mmkv.MMKV
import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedBridge
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import moe.fuqiuluo.qqinterface.servlet.transfile.NtV2RichMediaSvc
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.GlobalJson5 import moe.fuqiuluo.shamrock.tools.GlobalJson5
import moe.fuqiuluo.shamrock.utils.MMKVFetcher import moe.fuqiuluo.shamrock.xposed.loader.LuoClassloader.moduleLoader
import mqq.app.MobileQQ import mqq.app.MobileQQ
import java.io.File import java.util.Properties
internal object ShamrockConfig { internal object ShamrockConfig: ShamrockConfigV0() {
private val ConfigDir = MobileQQ.getContext().getExternalFilesDir(null)!!
.parentFile!!.resolve("Tencent/Shamrock").also {
if (it.exists()) it.delete()
it.mkdirs()
}
private val config = kotlin.runCatching {
GlobalJson5.decodeFromString<ServiceConfig>(ConfigDir.resolve("config.json").also {
if (!it.exists()) it.writeText("{}")
}.readText())
}.onFailure {
LogCenter.log("您的配置文件出现错误: ${it.stackTraceToString()}", Level.ERROR)
}.getOrElse {
ServiceConfig()
}
fun isInit(): Boolean {
val mmkv = MMKVFetcher.mmkvWithId("shamrock_config")
return mmkv.getBoolean("isInit", false)
}
private fun updateConfig(config: ServiceConfig = this.config) {
ConfigDir.resolve("config.json").writeText(GlobalJson5.encodeToString(config))
}
fun updateConfig(intent: Intent) {
mmkv.apply {
if (!intent.getBooleanExtra("disable_auto_sync_setting", false)) {
putBoolean("tablet", intent.getBooleanExtra("tablet", false)) // 强制平板模式
putInt("port", intent.getIntExtra("port", 5700)) // 主动HTTP端口
putBoolean("ws", intent.getBooleanExtra("ws", false)) // 主动WS开关
putBoolean("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)) // 推送同步消息
putBoolean("forbid_useless_process", intent.getBooleanExtra("forbid_useless_process", false)) // 禁用QQ生成无用进程
putBoolean("enable_old_bdh", intent.getBooleanExtra("enable_old_bdh", false)) // 启用旧版BDH
intent.getStringExtra("up_res_group")?.let { putString("up_res_group", it) }
} else {
XposedBridge.log("[Shamrock] 已禁用自动同步配置")
}
config.defaultToken = intent.getStringExtra("token")
config.antiTrace = intent.getBooleanExtra("anti_qq_trace", true)
val wsPort = intent.getIntExtra("ws_port", 5800)
config.activeWebSocket = if (config.activeWebSocket == null) ConnectionConfig(
address = "0.0.0.0",
port = wsPort,
) else config.activeWebSocket?.also {
it.port = wsPort
}
config.passiveWebSocket = intent.getStringExtra("ws_addr")?.split(",", "|", "")?.filter { address ->
address.isNotBlank() && (address.startsWith("ws://") || address.startsWith("wss://"))
}?.map {
ConnectionConfig(address = it)
}?.toMutableList()
putBoolean("isInit", true)
}
if (!intent.getBooleanExtra("disable_auto_sync_setting", false)) {
updateConfig()
}
}
fun putDefaultSettings() {
val mmkv = MMKVFetcher.mmkvWithId("shamrock_config")
if ((!isInit()) && (!mmkv.getBoolean("isEmergencyInitBefore", false))){
mmkv.apply {
putBoolean("tablet", false) // 强制平板模式
putInt("port", 5700) // 主动HTTP端口
putBoolean("ws", false) // 主动WS开关
putBoolean("http", false) // HTTP回调开关
putString("http_addr", null) // WebHook回调地址
putBoolean("ws_client", false) // 被动WS开关
putBoolean("use_cqcode", false) // 使用CQ码
putBoolean("inject_packet", false) // 拦截无用包
putBoolean("debug", false) // 调试模式
putString("key_store", null) // 证书路径
putString("ssl_pwd", null) // 证书密码
putString("ssl_private_pwd", null) // 证书私钥密码
putString("ssl_alias", null) // 证书别名
putInt("ssl_port", 5701) // 主动HTTP端口
putBoolean("alive_reply", true) // 自回复测试
putBoolean("enable_self_msg", false) // 推送自己发的消息
putBoolean("shell", false) // 开启Shell接口
putBoolean("enable_sync_msg_as_sent_msg", true) // 推送同步消息
putBoolean("forbid_useless_process", false) // 禁用QQ生成无用进程
putBoolean("enable_old_bdh", false) // 启用旧版BDH
putBoolean("antiTrace", false)
putBoolean("super_anti", true)
putString("up_res_group", "")
config.defaultToken = null
// config.antiTrace = true
val wsPort = 5800
config.activeWebSocket =
if (config.activeWebSocket == null) ConnectionConfig(
address = "0.0.0.0",
port = wsPort,
) else config.activeWebSocket?.also {
it.port = wsPort
}
config.passiveWebSocket = null
putBoolean("isInit", true)
putBoolean("isEmergencyInitBefore", true)
}
XposedBridge.log("[Shamrock] 强制初始化配置完成,请重新打开QQ")
} else {
XposedBridge.log("[Shamrock] 程序逻辑错误或错误地多次强制初始化")
mmkv.putBoolean("isEmergencyInitBefore", false)
XposedBridge.log("[Shamrock] 如果执意要再次强制初始化,请重新执行程序")
}
}
private val mmkv: MMKV
get() = MMKVFetcher.mmkvWithId("shamrock_config")
fun getUpResGroup(): String {
return mmkv.getString("up_res_group", "") ?: ""
}
fun aliveReply(): Boolean {
return mmkv.getBoolean("alive_reply", false)
}
fun allowTempSession(): Boolean {
return config.allowTempSession
}
fun getGroupMsgRule(): GroupRule? {
return config.rules?.groupRule
}
fun getPrivateRule(): PrivateRule? {
return config.rules?.privateRule
}
fun enableSyncMsgAsSentMsg(): Boolean {
return mmkv.getBoolean("enable_sync_msg_as_sent_msg", false)
}
fun enableSelfMsg(): Boolean {
return mmkv.getBoolean("enable_self_msg", false)
}
fun forbidUselessProcess(): Boolean {
return mmkv.getBoolean("forbid_useless_process", false)
}
fun openWebSocketClient(): Boolean {
return mmkv.getBoolean("ws_client", false)
}
fun getWebSocketClientAddress(): List<ConnectionConfig> {
return config.passiveWebSocket ?: emptyList()
}
fun openWebSocket(): Boolean {
return mmkv.getBoolean("ws", false)
}
fun getActiveWebSocketConfig(): ConnectionConfig? {
return config.activeWebSocket
}
fun getToken(): String {
return config.defaultToken ?: ""
}
fun useCQ(): Boolean {
return mmkv.getBoolean("use_cqcode", false)
}
fun allowWebHook(): Boolean {
return mmkv.getBoolean("http", false)
}
fun getWebHookAddress(): String {
return mmkv.getString("http_addr", "") ?: ""
}
fun forceTablet(): Boolean {
return mmkv.getBoolean("tablet", true)
}
fun getPort(): Int {
return mmkv.getInt("port", 5700)
}
fun isInjectPacket(): Boolean {
return mmkv.getBoolean("inject_packet", false)
}
fun enableOldBDH(): Boolean {
return mmkv.getBoolean("enable_old_bdh", false)
}
fun isDebug(): Boolean {
return mmkv.getBoolean("debug", false)
}
fun ssl(): Boolean {
return getKeyStorePath()?.exists() == true
}
fun getKeyStorePath(): File? {
mmkv.getString("key_store", null)?.let {
return File(it)
}
return null
}
fun sslPwd(): CharArray? {
return mmkv.getString("ssl_pwd", null)?.toCharArray()
}
fun sslPrivatePwd(): String? {
return mmkv.getString("ssl_private_pwd", null)
}
fun sslAlias(): String? {
return mmkv.getString("ssl_alias", null)
}
fun getSslPort(): Int {
return mmkv.getInt("ssl_port", getPort())
}
fun isDev(): Boolean {
return mmkv.getBoolean("dev", false)
}
operator fun set(key: String, value: String) {
mmkv.putString(key, value)
}
operator fun set(key: String, value: Boolean) {
mmkv.putBoolean(key, value)
}
operator fun set(key: String, value: Int) {
mmkv.putInt(key, value)
}
operator fun set(key: String, value: Long) {
mmkv.putLong(key, value)
}
operator fun set(key: String, value: Float) {
mmkv.putFloat(key, value)
}
fun isAntiTrace(): Boolean {
return config.antiTrace
}
fun allowShell(): Boolean {
return mmkv.getBoolean("shell", false)
}
} }

View File

@ -0,0 +1,285 @@
package moe.fuqiuluo.shamrock.remote.service.config
import android.content.Intent
import com.tencent.mmkv.MMKV
import de.robv.android.xposed.XposedBridge
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.GlobalJson5
import moe.fuqiuluo.shamrock.tools.toast
import moe.fuqiuluo.shamrock.utils.MMKVFetcher
import mqq.app.MobileQQ
import java.io.File
internal abstract class ShamrockConfigV0 {
private val configDir = MobileQQ.getContext().getExternalFilesDir(null)!!
.parentFile!!.resolve("Tencent/Shamrock").also {
if (it.exists()) it.delete()
it.mkdirs()
}
private val config = kotlin.runCatching {
GlobalJson5.decodeFromString<ServiceConfig>(configDir.resolve("config.json").also {
if (!it.exists()) it.writeText("{}")
}.readText())
}.onFailure {
LogCenter.log("您的配置文件出现错误: ${it.stackTraceToString()}", Level.ERROR)
}.getOrElse {
ServiceConfig()
}
fun isInit(): Boolean {
val mmkv = MMKVFetcher.mmkvWithId("shamrock_config")
return mmkv.getBoolean("isInit", false)
}
private fun updateConfig(config: ServiceConfig = this.config) {
configDir.resolve("config.json").writeText(GlobalJson5.encodeToString(config))
}
fun updateConfig(intent: Intent) {
MobileQQ.getContext().toast("同步配置数据解析中...")
mmkv.apply {
if (!intent.getBooleanExtra("disable_auto_sync_setting", false)) {
putBoolean("tablet", intent.getBooleanExtra("tablet", false)) // 强制平板模式
putInt("port", intent.getIntExtra("port", 5700)) // 主动HTTP端口
putBoolean("ws", intent.getBooleanExtra("ws", false)) // 主动WS开关
putBoolean("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)) // 推送同步消息
putBoolean("forbid_useless_process", intent.getBooleanExtra("forbid_useless_process", false)) // 禁用QQ生成无用进程
putBoolean("enable_old_bdh", intent.getBooleanExtra("enable_old_bdh", false)) // 启用旧版BDH
intent.getStringExtra("up_res_group")?.let { putString("up_res_group", it) }
} else {
XposedBridge.log("[Shamrock] 已禁用自动同步配置")
}
config.defaultToken = intent.getStringExtra("token")
config.antiTrace = intent.getBooleanExtra("anti_qq_trace", true)
val wsPort = intent.getIntExtra("ws_port", 5800)
config.activeWebSocket = if (config.activeWebSocket == null) ConnectionConfig(
address = "0.0.0.0",
port = wsPort,
) else config.activeWebSocket?.also {
it.port = wsPort
}
config.passiveWebSocket = intent.getStringExtra("ws_addr")?.split(",", "|", "")?.filter { address ->
address.isNotBlank() && (address.startsWith("ws://") || address.startsWith("wss://"))
}?.map {
ConnectionConfig(address = it)
}?.toMutableList()
putBoolean("isInit", true)
}
if (!intent.getBooleanExtra("disable_auto_sync_setting", false)) {
updateConfig()
}
}
fun putDefaultSettings() {
val mmkv = MMKVFetcher.mmkvWithId("shamrock_config")
if ((!isInit()) && (!mmkv.getBoolean("isEmergencyInitBefore", false))){
mmkv.apply {
putBoolean("tablet", false) // 强制平板模式
putInt("port", 5700) // 主动HTTP端口
putBoolean("ws", false) // 主动WS开关
putBoolean("http", false) // HTTP回调开关
putString("http_addr", null) // WebHook回调地址
putBoolean("ws_client", false) // 被动WS开关
putBoolean("use_cqcode", false) // 使用CQ码
putBoolean("inject_packet", false) // 拦截无用包
putBoolean("debug", false) // 调试模式
putString("key_store", null) // 证书路径
putString("ssl_pwd", null) // 证书密码
putString("ssl_private_pwd", null) // 证书私钥密码
putString("ssl_alias", null) // 证书别名
putInt("ssl_port", 5701) // 主动HTTP端口
putBoolean("alive_reply", true) // 自回复测试
putBoolean("enable_self_msg", false) // 推送自己发的消息
putBoolean("shell", false) // 开启Shell接口
putBoolean("enable_sync_msg_as_sent_msg", true) // 推送同步消息
putBoolean("forbid_useless_process", false) // 禁用QQ生成无用进程
putBoolean("enable_old_bdh", false) // 启用旧版BDH
putBoolean("antiTrace", false)
putBoolean("super_anti", true)
putString("up_res_group", "")
config.defaultToken = null
// config.antiTrace = true
val wsPort = 5800
config.activeWebSocket =
if (config.activeWebSocket == null) ConnectionConfig(
address = "0.0.0.0",
port = wsPort,
) else config.activeWebSocket?.also {
it.port = wsPort
}
config.passiveWebSocket = null
putBoolean("isInit", true)
putBoolean("isEmergencyInitBefore", true)
}
XposedBridge.log("[Shamrock] 强制初始化配置完成,请重新打开QQ")
} else {
XposedBridge.log("[Shamrock] 程序逻辑错误或错误地多次强制初始化")
mmkv.putBoolean("isEmergencyInitBefore", false)
XposedBridge.log("[Shamrock] 如果执意要再次强制初始化,请重新执行程序")
}
}
protected val mmkv: MMKV
get() = MMKVFetcher.mmkvWithId("shamrock_config")
fun getUpResGroup(): String {
return mmkv.getString("up_res_group", "") ?: ""
}
fun aliveReply(): Boolean {
return mmkv.getBoolean("alive_reply", false)
}
fun allowTempSession(): Boolean {
return config.allowTempSession
}
fun getGroupMsgRule(): GroupRule? {
return config.rules?.groupRule
}
fun getPrivateRule(): PrivateRule? {
return config.rules?.privateRule
}
fun enableSyncMsgAsSentMsg(): Boolean {
return mmkv.getBoolean("enable_sync_msg_as_sent_msg", false)
}
fun enableSelfMsg(): Boolean {
return mmkv.getBoolean("enable_self_msg", false)
}
fun forbidUselessProcess(): Boolean {
return mmkv.getBoolean("forbid_useless_process", false)
}
fun openWebSocketClient(): Boolean {
return mmkv.getBoolean("ws_client", false)
}
fun getWebSocketClientAddress(): List<ConnectionConfig> {
return config.passiveWebSocket ?: emptyList()
}
fun openWebSocket(): Boolean {
return mmkv.getBoolean("ws", false)
}
fun getActiveWebSocketConfig(): ConnectionConfig? {
return config.activeWebSocket
}
fun getToken(): String {
return config.defaultToken ?: ""
}
fun useCQ(): Boolean {
return mmkv.getBoolean("use_cqcode", false)
}
fun allowWebHook(): Boolean {
return mmkv.getBoolean("http", false)
}
fun getWebHookAddress(): String {
return mmkv.getString("http_addr", "") ?: ""
}
fun forceTablet(): Boolean {
return mmkv.getBoolean("tablet", true)
}
fun getPort(): Int {
return mmkv.getInt("port", 5700)
}
fun isInjectPacket(): Boolean {
return mmkv.getBoolean("inject_packet", false)
}
fun enableOldBDH(): Boolean {
return mmkv.getBoolean("enable_old_bdh", false)
}
fun isDebug(): Boolean {
return mmkv.getBoolean("debug", false)
}
fun ssl(): Boolean {
return getKeyStorePath()?.exists() == true
}
fun getKeyStorePath(): File? {
mmkv.getString("key_store", null)?.let {
return File(it)
}
return null
}
fun sslPwd(): CharArray? {
return mmkv.getString("ssl_pwd", null)?.toCharArray()
}
fun sslPrivatePwd(): String? {
return mmkv.getString("ssl_private_pwd", null)
}
fun sslAlias(): String? {
return mmkv.getString("ssl_alias", null)
}
fun getSslPort(): Int {
return mmkv.getInt("ssl_port", getPort())
}
fun isDev(): Boolean {
return mmkv.getBoolean("dev", false)
}
operator fun set(key: String, value: String) {
mmkv.putString(key, value)
}
operator fun set(key: String, value: Boolean) {
mmkv.putBoolean(key, value)
}
operator fun set(key: String, value: Int) {
mmkv.putInt(key, value)
}
operator fun set(key: String, value: Long) {
mmkv.putLong(key, value)
}
operator fun set(key: String, value: Float) {
mmkv.putFloat(key, value)
}
fun isAntiTrace(): Boolean {
return config.antiTrace
}
fun allowShell(): Boolean {
return mmkv.getBoolean("shell", false)
}
}

View File

@ -27,18 +27,18 @@ internal data class SimpleTroopMemberInfo(
@SerialName("user_name") val name: String, @SerialName("user_name") val name: String,
@SerialName("sex") val sex: String, @SerialName("sex") val sex: String,
@SerialName("age") val age: Int, @SerialName("age") val age: Int,
@SerialName("title") val title: String, @SerialName("title") var title: String?,
@SerialName("title_expire_time") val titleExpireTime: Int, @SerialName("title_expire_time") var titleExpireTime: Int,
@SerialName("nickname") val nick: String, @SerialName("nickname") val nick: String,
@SerialName("user_displayname") val showName: String?, @SerialName("user_displayname") val showName: String?,
@SerialName("card") val cardName: String?, @SerialName("card") val cardName: String?,
@SerialName("distance") val distance: Int, @SerialName("distance") var distance: Int,
@SerialName("honor") val honor: List<Int>, @SerialName("honor") val honor: List<Int>,
@SerialName("join_time") val joinTime: Long, @SerialName("join_time") val joinTime: Long,
@SerialName("last_active_time") val lastActiveTime: Long, @SerialName("last_active_time") val lastActiveTime: Long,
@SerialName("last_sent_time") val lastSentTime: Long, @SerialName("last_sent_time") val lastSentTime: Long,
@SerialName("unique_name") val uniqueName: String?, @SerialName("unique_name") var uniqueName: String?,
@SerialName("area") val area: String, @SerialName("area") var area: String,
@SerialName("level") val level: Int, @SerialName("level") val level: Int,
@SerialName("role") val role: MemberRole, @SerialName("role") val role: MemberRole,
@SerialName("unfriendly") val unfriendly: Boolean, @SerialName("unfriendly") val unfriendly: Boolean,

Some files were not shown because too many files have changed in this diff Show More