mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 05:12:17 +00:00
Compare commits
155 Commits
Author | SHA1 | Date | |
---|---|---|---|
6201d12f5f | |||
b2adc5cedf | |||
0bb871bf01 | |||
dc969440ee | |||
b9b6e133d0 | |||
b5a9884448 | |||
bffb7caf04 | |||
2c3466b4c3 | |||
007e5fef2f | |||
48773cc47c | |||
b2ad4438ab | |||
2fdcfe332b | |||
7d8772ebf6 | |||
2a75160ef8 | |||
76bd58d984 | |||
39120bdeae | |||
7b07698f7b | |||
5c10a5a04e | |||
3a0dc41329 | |||
64c800c945 | |||
ecb3cea5a5 | |||
8e0ae6f85b | |||
9d893b481d | |||
85aaa54e4e | |||
c6dad5677c | |||
80a4a208b9 | |||
ae663e6b2e | |||
780f3577a5 | |||
3518f974cc | |||
911b003f7f | |||
69bc80e9b3 | |||
da0b74db1a | |||
7212938df3 | |||
ae1e78b267 | |||
b7266e490f | |||
6b4a429821 | |||
4a4507dfcd | |||
f63bcabf1b | |||
4932b36ee1 | |||
8c307c4f6e | |||
8d8846fafb | |||
544e216ddb | |||
4fedab719b | |||
75a567d5cd | |||
1a814e565a | |||
5ea260c24b | |||
2d57dc021d | |||
dabe2ea886 | |||
673902e514 | |||
5062ff7c3a | |||
0de6f851a6 | |||
c758b1576d | |||
5ba8bd11e2 | |||
679b7619ce | |||
282233131a | |||
edf857bcb6 | |||
cd1d1e928a | |||
45d6421153 | |||
8c6f529b4b | |||
b23620b5ef | |||
e09e00fcd3 | |||
0d35d5834b | |||
ee8dc75be3 | |||
5f91be547e | |||
7439622cd6 | |||
5f0cf952e8 | |||
03d62c55c2 | |||
89154f0c49 | |||
5218c153f0 | |||
2b64aa8b2c | |||
67ef465f14 | |||
ea62640057 | |||
f67dfac4d9 | |||
1593d973a0 | |||
3e27c64e84 | |||
c87268df09 | |||
98c6316e23 | |||
2af8d6d817 | |||
b9c2de744e | |||
fcf18bd3fd | |||
8265f74ca3 | |||
2c49b10772 | |||
4266afd1a5 | |||
b9ef0ca8f5 | |||
040b132836 | |||
16f77de5a6 | |||
46c83f5cb9 | |||
ca71ecae09 | |||
58d93b8f56 | |||
472b17f744 | |||
8a4212ffd7 | |||
bd28b0f7f7 | |||
f2dc3bc9fd | |||
ba8322dc55 | |||
cf445d17d9 | |||
e6e03ee328 | |||
497df6b649 | |||
f87fd3887a | |||
71a462e48f | |||
93cb6fc46b | |||
8fdea083b0 | |||
1620e18bf4 | |||
2f264ee9e4 | |||
19e3846e40 | |||
6b4f763a90 | |||
d5378d8acb | |||
ca62b33275 | |||
e09b68576a | |||
80bb591a1b | |||
08bce05c66 | |||
0fb2eeb2d8 | |||
3cf2474938 | |||
ce1e850c78 | |||
5200a4d8d4 | |||
0a41429d71 | |||
bc967cf926 | |||
d28c6dc820 | |||
10d25167e8 | |||
346798dc9a | |||
7584390408 | |||
3988ad3811 | |||
72600364ff | |||
68977e4a86 | |||
92b3d4f94e | |||
852eb0d87b | |||
c8143bcf67 | |||
75dc5c0294 | |||
21341caf62 | |||
ee9cf694a0 | |||
d347bd0a41 | |||
e5cca58198 | |||
259de3d3aa | |||
c30e3db1a1 | |||
fb5718dc61 | |||
6043c21de5 | |||
c3c14d6ead | |||
41675ed874 | |||
dc2503b045 | |||
b76ef7efb3 | |||
c67b49790f | |||
86b29b982c | |||
f17b4924c9 | |||
e162da7e7b | |||
6f1ba71664 | |||
16e0c9542e | |||
e41b7515d3 | |||
52b8db70be | |||
184064d199 | |||
ae1684a885 | |||
688041b6be | |||
4adf2eb84a | |||
3f8af384b0 | |||
79f2594a2f | |||
547f224140 | |||
94937a75a2 |
13
.github/FUNDING.yml
vendored
Normal file
13
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: shamrock320 # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
3
.github/ISSUE_TEMPLATE/bug.md
vendored
3
.github/ISSUE_TEMPLATE/bug.md
vendored
@ -6,7 +6,7 @@ labels: bug
|
||||
---
|
||||
|
||||
警告: 在进一步操作之前,请检查下列选项。如果您忽视此模板或者没有提供关键信息,您的 Issue 将直接被关闭
|
||||
- 确保您使用的是 [最新开发版本](https://github.com/whitechi73/Shamrock/actions/workflows/build-apk.yml) 的 Shamrock.
|
||||
- 确保您使用的是 [最新开发版本](https://github.com/whitechi73/OpenShamrock/actions/workflows/build-apk.yml) 的 Shamrock.
|
||||
- 确保您的问题尚未在 Issues 列表中提出.
|
||||
- 确保您的问题不是由于您的代码错误导致的.
|
||||
|
||||
@ -22,5 +22,6 @@ labels: bug
|
||||
|
||||
- Shamrock 版本:
|
||||
- Android 版本:
|
||||
- LSPosed 框架版本:
|
||||
- 设备的制造商和型号:
|
||||
- 设备的 CPU 架构:
|
||||
|
2
.github/ISSUE_TEMPLATE/feature.md
vendored
2
.github/ISSUE_TEMPLATE/feature.md
vendored
@ -7,7 +7,7 @@ labels: enhancement
|
||||
|
||||
警告: 在进一步操作之前,请检查下列选项。如果您忽视此模板或者没有提供关键信息,您的 Issue 将直接被关闭
|
||||
|
||||
- 确保您使用的是 [最新开发版本](https://github.com/whitechi73/Shamrock/actions/workflows/build-apk.yml) 的 Shamrock.
|
||||
- 确保您使用的是 [最新开发版本](https://github.com/whitechi73/OpenShamrock/actions/workflows/build-apk.yml) 的 Shamrock.
|
||||
- 确保您的功能请求尚未在 Issues 列表中提出.
|
||||
- 确保您的功能请求是与 Shamrock 相关的,且可以实现.
|
||||
|
||||
|
BIN
AudioLibrary-x64.zip
Normal file
BIN
AudioLibrary-x64.zip
Normal file
Binary file not shown.
39
README.md
39
README.md
@ -10,7 +10,7 @@
|
||||
![][onebot-12]
|
||||
[![][license]](LICENSE)
|
||||
|
||||
[下载][download-link] | [部署][deploy-link] | [接口][api-link] | [文档][docs-link] | [加群][group-link]
|
||||
[下载][download-link] | [部署][deploy-link] | [接口][api-link] | [文档][docs-link]
|
||||
|
||||
</div>
|
||||
|
||||
@ -22,14 +22,13 @@
|
||||
> 本项目目的是研究 Xposed 和 LSPosed 框架的使用。 Epic 框架开发相关知识。
|
||||
> 如有违反法律,请联系删除。
|
||||
> 请勿在任何平台宣传,宣扬,转发本项目,请勿恶意修改企业安装包造成相关企业产生损失,如有违背,必将追责到底。
|
||||
> 官方论坛,[点我直达](https://forum.libfekit.so/)!
|
||||
|
||||
## 兼容|迁移|替代 说明
|
||||
|
||||
- 一键移植:本项目基于 go-cqhttp 的文档进行开发实现。
|
||||
- 平行部署:可多平台部署,未来将会支持 Docker 部署的教程。
|
||||
|
||||
> 若您追求小而轻便的Bot服务, [Chronocat](https://chronocat.vercel.app/)是您的不二之选。
|
||||
|
||||
## 权限声明
|
||||
|
||||
> 如出现未在此处声明的权限,请警惕 Shamrock 是否被修改/植入恶意代码
|
||||
@ -44,7 +43,7 @@
|
||||
|
||||
## 贡献说明
|
||||
|
||||
<img src="https://github.com/whitechi73/OpenShamrock/assets/98259561/f04d60bc-ec40-41fc-bc15-62c146f1a1f1" width="160px"> **我可爱吗?欢迎你的到来,这里是一个很大的地方,有着无限可能,主要是有你啦!**
|
||||
<img src="https://github.com/whitechi73/OpenShamrock/assets/98259561/f04d60bc-ec40-41fc-bc15-62c146f1a1f1" width="160px" alt="Shamrock"> **我可爱吗?欢迎你的到来,这里是一个很大的地方,有着无限可能,主要是有你啦!**
|
||||
|
||||
## 开源协议
|
||||
|
||||
@ -69,38 +68,36 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
[![][contrib-image]][contrib-link]
|
||||
|
||||
[banner]: https://socialify.git.ci/whitechi73/Shamrock/image?description=1&forks=1&issues=1&logo=https%3A%2F%2Fwhitechi73.github.io%2FShamrock%2Fshamrock.jpg&pattern=Plus&pulls=1&stargazers=1&theme=Auto
|
||||
[banner]: https://socialify.git.ci/whitechi73/OpenShamrock/image?description=1&forks=1&issues=1&logo=https%3A%2F%2Fwhitechi73.github.io%2FOpenShamrock%2Fshamrock.jpg&pattern=Plus&pulls=1&stargazers=1&theme=Auto
|
||||
|
||||
[actions]: https://img.shields.io/github/actions/workflow/status/whitechi73/Shamrock/build-apk.yml?style=for-the-badge
|
||||
[actions]: https://img.shields.io/github/actions/workflow/status/whitechi73/OpenShamrock/build-apk.yml?style=for-the-badge
|
||||
|
||||
[actions-link]: https://github.com/whitechi73/Shamrock/actions/workflows/build-apk.yml
|
||||
[actions-link]: https://github.com/whitechi73/OpenShamrock/actions/workflows/build-apk.yml
|
||||
|
||||
[releases]: https://img.shields.io/github/v/release/whitechi73/Shamrock?style=for-the-badge
|
||||
[releases]: https://img.shields.io/github/v/release/whitechi73/OpenShamrock?style=for-the-badge
|
||||
|
||||
[releases-link]: https://github.com/whitechi73/Shamrock/releases
|
||||
[releases-link]: https://github.com/whitechi73/OpenShamrock/releases
|
||||
|
||||
[downloads]: https://img.shields.io/github/downloads/whitechi73/Shamrock/total?style=for-the-badge
|
||||
[downloads]: https://img.shields.io/github/downloads/whitechi73/OpenShamrock/total?style=for-the-badge
|
||||
|
||||
[license]: https://img.shields.io/github/license/whitechi73/Shamrock?style=for-the-badge
|
||||
[license]: https://img.shields.io/github/license/whitechi73/OpenShamrock?style=for-the-badge
|
||||
|
||||
[onebot-11]: https://img.shields.io/badge/OneBot-11-black?style=for-the-badge
|
||||
|
||||
[onebot-12]: https://img.shields.io/badge/OneBot-12-black?style=for-the-badge
|
||||
|
||||
[download-link]: https://whitechi73.github.io/Shamrock/guide/getting-started.html#%E4%B8%8B%E8%BD%BD
|
||||
[download-link]: https://whitechi73.github.io/OpenShamrock/guide/getting-started.html#%E4%B8%8B%E8%BD%BD
|
||||
|
||||
[deploy-link]: https://whitechi73.github.io/Shamrock/guide/getting-started.html#%E9%83%A8%E7%BD%B2
|
||||
[deploy-link]: https://whitechi73.github.io/OpenShamrock/guide/getting-started.html#%E9%83%A8%E7%BD%B2
|
||||
|
||||
[api-link]: https://whitechi73.github.io/Shamrock/api
|
||||
[api-link]: https://whitechi73.github.io/OpenShamrock/api
|
||||
|
||||
[docs-link]: https://whitechi73.github.io/Shamrock/
|
||||
[docs-link]: https://whitechi73.github.io/OpenShamrock/
|
||||
|
||||
[group-link]: https://whitechi73.github.io/Shamrock/group.html
|
||||
[hook-system]: https://github.com/whitechi73/OpenShamrock/blob/master/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/loader/FuckAMS.kt
|
||||
|
||||
[hook-system]: https://github.com/whitechi73/Shamrock/wiki/perm_hook_android
|
||||
[voice-support]: https://whitechi73.github.io/OpenShamrock/advanced/voice.html
|
||||
|
||||
[voice-support]: https://whitechi73.github.io/Shamrock/advanced/voice.html
|
||||
[contrib-image]: https://contrib.rocks/image?repo=whitechi73/OpenShamrock
|
||||
|
||||
[contrib-image]: https://contrib.rocks/image?repo=whitechi73/Shamrock
|
||||
|
||||
[contrib-link]: https://github.com/whitechi73/Shamrock/graphs/contributors
|
||||
[contrib-link]: https://github.com/whitechi73/OpenShamrock/graphs/contributors
|
||||
|
@ -24,7 +24,7 @@ android {
|
||||
minSdk = 24
|
||||
targetSdk = 33
|
||||
versionCode = (System.currentTimeMillis() / 1000).toInt()
|
||||
versionName = "1.0.5-dev" + gitCommitHash()
|
||||
versionName = "1.0.7-dev" + gitCommitHash()
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
@ -66,6 +66,7 @@ android {
|
||||
create("app") {
|
||||
dimension = "mode"
|
||||
ndk {
|
||||
println("Full architecture and full compilation.")
|
||||
abiFilters.add("arm64-v8a")
|
||||
abiFilters.add("x86_64")
|
||||
}
|
||||
@ -73,12 +74,14 @@ android {
|
||||
create("arm64") {
|
||||
dimension = "mode"
|
||||
ndk {
|
||||
println("Full compilation of arm64 architecture")
|
||||
abiFilters.add("arm64-v8a")
|
||||
}
|
||||
}
|
||||
create("x64") {
|
||||
dimension = "mode"
|
||||
ndk {
|
||||
println("Full compilation of x64 architecture")
|
||||
abiFilters.add("x86_64")
|
||||
}
|
||||
}
|
||||
@ -173,24 +176,19 @@ dependencies {
|
||||
implementation("io.coil-kt:coil:2.4.0")
|
||||
implementation("io.coil-kt:coil-compose:2.4.0")
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.1.16")
|
||||
implementation("io.ktor:ktor-server-core:2.3.3")
|
||||
implementation("io.ktor:ktor-server-host-common:2.3.3")
|
||||
implementation("io.ktor:ktor-server-status-pages:2.3.3")
|
||||
implementation("io.ktor:ktor-server-netty:2.3.3")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.3")
|
||||
implementation("io.ktor:ktor-server-content-negotiation:2.3.3")
|
||||
implementation("io.ktor:ktor-client-core:2.3.3")
|
||||
implementation("io.ktor:ktor-client-cio:2.3.3")
|
||||
implementation("io.ktor:ktor-client-content-negotiation:2.3.3")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.3")
|
||||
// useless
|
||||
//implementation ("com.maxkeppeler.sheets-compose-dialogs:core:1.2.0")
|
||||
//implementation ("com.maxkeppeler.sheets-compose-dialogs:info:1.2.0")
|
||||
//implementation ("com.maxkeppeler.sheets-compose-dialogs:input:1.2.0")
|
||||
//implementation ("com.maxkeppeler.sheets-compose-dialogs:list:1.2.0")
|
||||
//implementation ("com.maxkeppeler.sheets-compose-dialogs:state:1.2.0")
|
||||
|
||||
val ktorVersion = "2.3.3"
|
||||
implementation("io.ktor:ktor-server-core:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-host-common:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-status-pages:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-netty:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-cio:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
|
||||
//implementation("io.ktor:ktor-serialization-kotlinx-protobuf:$ktorVersion")
|
||||
|
||||
implementation(project(":xposed"))
|
||||
|
||||
|
@ -18,6 +18,8 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Shamrock"
|
||||
android:zygotePreloadName="@string/app_name"
|
||||
android:multiArch="true"
|
||||
android:extractNativeLibs="true"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
@ -47,7 +49,7 @@
|
||||
android:value="基于 Xposed 实现 OneBot 标准的 QQ 机器人框架" />
|
||||
<meta-data
|
||||
android:name="xposedminversion"
|
||||
android:value="23" />
|
||||
android:value="93" />
|
||||
<meta-data
|
||||
android:name="xposedscope"
|
||||
android:resource="@array/xposed_scope" />
|
||||
|
@ -99,6 +99,8 @@ void decode_cqcode(const std::string& code, std::vector<std::unordered_map<std::
|
||||
replace_string(cache, "]", "]");
|
||||
replace_string(cache, ",", ",");
|
||||
kv.emplace(key_tmp, cache);
|
||||
} else {
|
||||
kv.emplace("_type", cache);
|
||||
}
|
||||
dest.push_back(kv);
|
||||
kv.clear();
|
||||
|
@ -61,8 +61,8 @@ const std::byte MD5::PADDING[64] = { (byte) 0x80 };
|
||||
const char MD5::HEX[16] = {
|
||||
'0', '1', '2', '3',
|
||||
'4', '5', '6', '7',
|
||||
'8', '9', 'a', 'b',
|
||||
'c', 'd', 'e', 'f'
|
||||
'8', '9', 'A', 'B',
|
||||
'C', 'D', 'E', 'F'
|
||||
};
|
||||
|
||||
|
||||
|
@ -120,6 +120,7 @@ private fun AppMainView() {
|
||||
val coreVersion = remember { mutableStateOf(getShamrockVersion(context)) }
|
||||
val coreName = remember { mutableStateOf("Xposed") }
|
||||
val voiceSwitch = remember { mutableStateOf(false) }
|
||||
@Suppress("LocalVariableName") val LocalString = LocalString
|
||||
|
||||
if (!AppRuntime.isInit) {
|
||||
AppRuntime.state = remember {
|
||||
@ -140,7 +141,7 @@ private fun AppMainView() {
|
||||
mutableStateOf("2854200454")
|
||||
}
|
||||
it.nick = remember {
|
||||
mutableStateOf("测试昵称")
|
||||
mutableStateOf(LocalString.testName)
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,13 +151,12 @@ private fun AppMainView() {
|
||||
}
|
||||
|
||||
val ctx = LocalContext.current
|
||||
@Suppress("LocalVariableName") val LocalString = LocalString
|
||||
LaunchedEffect(isFined.value) {
|
||||
if (isFined.value) {
|
||||
AppRuntime.log("日志框架激活成功,开放操作许可。")
|
||||
AppRuntime.log(LocalString.logCentralLoadSuccessfully)
|
||||
Toast.makeText(ctx, LocalString.frameworkYes, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
AppRuntime.log("日志框架处于未激活状态,请检查。")
|
||||
AppRuntime.log(LocalString.logCentralLoadFailed)
|
||||
Toast.makeText(ctx, LocalString.frameworkNo, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
@ -224,6 +224,16 @@ object ShamrockConfig {
|
||||
preferences.edit().putBoolean("debug", v).apply()
|
||||
}
|
||||
|
||||
fun isAntiTrace(ctx: Context): Boolean {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
return preferences.getBoolean("anti_qq_trace", true)
|
||||
}
|
||||
|
||||
fun setAntiTrace(ctx: Context, v: Boolean) {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
preferences.edit().putBoolean("anti_qq_trace", v).apply()
|
||||
}
|
||||
|
||||
fun isInjectPacket(ctx: Context): Boolean {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
return preferences.getBoolean("inject_packet", false)
|
||||
@ -239,11 +249,31 @@ object ShamrockConfig {
|
||||
return preferences.getBoolean("enable_auto_start", false)
|
||||
}
|
||||
|
||||
fun enableAliveReply(ctx: Context): Boolean {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
return preferences.getBoolean("alive_reply", false)
|
||||
}
|
||||
|
||||
fun allowShell(ctx: Context): Boolean {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
return preferences.getBoolean("shell", false)
|
||||
}
|
||||
|
||||
fun setAutoStart(ctx: Context, v: Boolean) {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
preferences.edit().putBoolean("enable_auto_start", v).apply()
|
||||
}
|
||||
|
||||
fun setAliveReply(ctx: Context, v: Boolean) {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
preferences.edit().putBoolean("alive_reply", v).apply()
|
||||
}
|
||||
|
||||
fun setShellStatus(ctx: Context, v: Boolean) {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
preferences.edit().putBoolean("shell", v).apply()
|
||||
}
|
||||
|
||||
fun enableSelfMsg(ctx: Context): Boolean {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
return preferences.getBoolean("enable_self_msg", false)
|
||||
@ -283,11 +313,14 @@ object ShamrockConfig {
|
||||
"ssl_pwd" to preferences.getString("ssl_pwd", ""),
|
||||
"inject_packet" to preferences.getBoolean("inject_packet", false),
|
||||
"debug" to preferences.getBoolean("debug", false),
|
||||
"auto_clear" to preferences.getBoolean("auto_clear", false),
|
||||
"anti_qq_trace" to preferences.getBoolean("anti_qq_trace", true),
|
||||
//"auto_clear" to preferences.getBoolean("auto_clear", false),
|
||||
"ssl_private_pwd" to preferences.getString("ssl_private_pwd", ""),
|
||||
"key_store" to preferences.getString("key_store", ""),
|
||||
"enable_self_msg" to preferences.getBoolean("enable_self_msg", false),
|
||||
"echo_number" to preferences.getBoolean("echo_number", false),
|
||||
"shell" to preferences.getBoolean("shell", false),
|
||||
"alive_reply" to preferences.getBoolean("alive_reply", false),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,7 @@ import moe.fuqiuluo.shamrock.ui.app.AppRuntime
|
||||
import moe.fuqiuluo.shamrock.ui.app.Level
|
||||
import moe.fuqiuluo.shamrock.ui.app.ShamrockConfig
|
||||
import moe.fuqiuluo.shamrock.ui.theme.GlobalColor
|
||||
import moe.fuqiuluo.shamrock.ui.theme.LocalString
|
||||
import moe.fuqiuluo.shamrock.ui.theme.ThemeColor
|
||||
import moe.fuqiuluo.shamrock.ui.tools.InputDialog
|
||||
|
||||
@ -70,7 +71,7 @@ fun DashboardFragment(
|
||||
AccountCard(nick, uin)
|
||||
InformationCard(ctx)
|
||||
APIInfoCard(ctx)
|
||||
FunctionCard(scope, ctx, "功能设置")
|
||||
FunctionCard(scope, ctx, LocalString.functionSetting)
|
||||
SSLCard(ctx)
|
||||
}
|
||||
}
|
||||
@ -80,7 +81,7 @@ private fun SSLCard(ctx: Context) {
|
||||
ActionBox(
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
painter = painterResource(id = R.drawable.baseline_security_24),
|
||||
title = "SSL配置"
|
||||
title = LocalString.sslSetting
|
||||
) {
|
||||
Column {
|
||||
Divider(
|
||||
@ -257,10 +258,10 @@ private fun APIInfoCard(
|
||||
hint = "请输入被动地址",
|
||||
error = "输入的地址不合法",
|
||||
checker = {
|
||||
it.isNotBlank()
|
||||
true
|
||||
},
|
||||
confirm = {
|
||||
if (it.startsWith("ws://") || it.startsWith("wss://")) {
|
||||
if (it.startsWith("ws://") || it.startsWith("wss://") || it.isBlank()) {
|
||||
ShamrockConfig.setWsAddr(ctx, wsAddress.value)
|
||||
AppRuntime.log("设置被动WebSocket地址为[${wsAddress.value}]。")
|
||||
} else {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package moe.fuqiuluo.shamrock.ui.fragment
|
||||
|
||||
import android.widget.Toast
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.absolutePadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@ -22,6 +22,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import moe.fuqiuluo.shamrock.R
|
||||
import moe.fuqiuluo.shamrock.ui.app.AppRuntime
|
||||
import moe.fuqiuluo.shamrock.ui.app.Level
|
||||
import moe.fuqiuluo.shamrock.ui.app.ShamrockConfig
|
||||
import moe.fuqiuluo.shamrock.ui.theme.GlobalColor
|
||||
import moe.fuqiuluo.shamrock.ui.theme.LocalString
|
||||
@ -46,10 +47,11 @@ fun LabFragment() {
|
||||
}
|
||||
NoticeTextDialog(
|
||||
openDialog = showNoticeDialog,
|
||||
title = "温馨提示",
|
||||
text = "实验室功能会导致一些奇怪的问题,请谨慎使用!"
|
||||
title = LocalString.warnTitle,
|
||||
text = LocalString.labWarning
|
||||
)
|
||||
|
||||
val LocalString = LocalString
|
||||
ActionBox(
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
painter = painterResource(id = R.drawable.baseline_preview_24),
|
||||
@ -63,19 +65,19 @@ fun LabFragment() {
|
||||
)
|
||||
|
||||
Function(
|
||||
title = "中二病模式",
|
||||
desc = "也许会导致奇怪的问题,大抵就是你看不懂罢了。",
|
||||
title = LocalString.b2Mode,
|
||||
desc = LocalString.b2ModeDesc,
|
||||
descColor = it,
|
||||
isSwitch = ShamrockConfig.is2B(ctx)
|
||||
) {
|
||||
ShamrockConfig.set2B(ctx, it)
|
||||
scope.toast(ctx, "重启生效哦!")
|
||||
scope.toast(ctx, LocalString.restartToast)
|
||||
return@Function true
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "显示调试日志",
|
||||
desc = "会导致日志刷屏。",
|
||||
title = LocalString.showDebugLog,
|
||||
desc = LocalString.showDebugLogDesc,
|
||||
descColor = it,
|
||||
isSwitch = ShamrockConfig.isDebug(ctx)
|
||||
) {
|
||||
@ -90,7 +92,92 @@ fun LabFragment() {
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
painter = painterResource(id = R.drawable.round_logo_dev_24),
|
||||
title = "实验功能"
|
||||
) {
|
||||
) { color ->
|
||||
Column {
|
||||
Divider(
|
||||
modifier = Modifier,
|
||||
color = GlobalColor.Divider,
|
||||
thickness = 0.2.dp
|
||||
)
|
||||
|
||||
/*
|
||||
Function(
|
||||
title = "自动清理QQ垃圾",
|
||||
desc = "也许会导致奇怪的问题(无效)。",
|
||||
descColor = color,
|
||||
isSwitch = ShamrockConfig.isAutoClean(ctx)
|
||||
) {
|
||||
ShamrockConfig.setAutoClean(ctx, it)
|
||||
ShamrockConfig.pushUpdate(ctx)
|
||||
return@Function false
|
||||
}*/
|
||||
|
||||
Function(
|
||||
title = "自回复测试",
|
||||
desc = "发送[ping],机器人发送一个具有调试信息的返回。",
|
||||
descColor = color,
|
||||
isSwitch = ShamrockConfig.enableAliveReply(ctx)
|
||||
) {
|
||||
ShamrockConfig.setAliveReply(ctx, it)
|
||||
return@Function true
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "开启Shell接口",
|
||||
desc = "可能导致设备被入侵,请勿随意开启。",
|
||||
descColor = color,
|
||||
isSwitch = ShamrockConfig.allowShell(ctx)
|
||||
) {
|
||||
ShamrockConfig.setShellStatus(ctx, it)
|
||||
return@Function true
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "自动唤醒QQ",
|
||||
desc = "QQ进程死亡时重新打开QQ进程,前提本进程存活。",
|
||||
descColor = color,
|
||||
isSwitch = ShamrockConfig.enableAutoStart(ctx)
|
||||
) {
|
||||
ShamrockConfig.setAutoStart(ctx, it)
|
||||
return@Function true
|
||||
}
|
||||
|
||||
kotlin.runCatching {
|
||||
ctx.getSharedPreferences("shared_config", Context.MODE_WORLD_READABLE)
|
||||
}.onSuccess {
|
||||
Function(
|
||||
title = LocalString.persistentText,
|
||||
desc = LocalString.persistentTextDesc,
|
||||
descColor = color,
|
||||
isSwitch = it.getBoolean("persistent", false)
|
||||
) { v ->
|
||||
it.edit().putBoolean("persistent", v).apply()
|
||||
scope.toast(ctx, LocalString.restartSysToast)
|
||||
return@Function true
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "禁用Doze模式",
|
||||
desc = "禁止系统进入节能模式。",
|
||||
descColor = color,
|
||||
isSwitch = it.getBoolean("hook_doze", false)
|
||||
) { value ->
|
||||
it.edit().putBoolean("hook_doze", value).apply()
|
||||
scope.toast(ctx, LocalString.restartSysToast)
|
||||
return@Function true
|
||||
}
|
||||
}.onFailure {
|
||||
AppRuntime.log("无法启用附加选项,LSPosed模块未激活或者不支持XSharedPreferences", Level.WARN)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ActionBox(
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
painter = painterResource(id = R.drawable.sharp_lock_24),
|
||||
title = "安全性设置"
|
||||
) { color ->
|
||||
Column {
|
||||
Divider(
|
||||
modifier = Modifier,
|
||||
@ -99,20 +186,9 @@ fun LabFragment() {
|
||||
)
|
||||
|
||||
Function(
|
||||
title = "自动清理QQ垃圾",
|
||||
desc = "也许会导致奇怪的问题。",
|
||||
descColor = it,
|
||||
isSwitch = ShamrockConfig.isAutoClean(ctx)
|
||||
) {
|
||||
ShamrockConfig.setAutoClean(ctx, it)
|
||||
ShamrockConfig.pushUpdate(ctx)
|
||||
return@Function false
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "拦截QQ无用发包",
|
||||
desc = "测试阶段,可能导致网络异常。",
|
||||
descColor = it,
|
||||
title = LocalString.injectPacket,
|
||||
desc = LocalString.injectPacketDesc,
|
||||
descColor = color,
|
||||
isSwitch = ShamrockConfig.isInjectPacket(ctx)
|
||||
) {
|
||||
ShamrockConfig.setInjectPacket(ctx, it)
|
||||
@ -121,16 +197,31 @@ fun LabFragment() {
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "自动唤醒QQ",
|
||||
desc = "QQ进程死亡时重新打开QQ进程,前提本进程存活。",
|
||||
descColor = it,
|
||||
isSwitch = ShamrockConfig.enableAutoStart(ctx)
|
||||
title = LocalString.antiTrace,
|
||||
desc = LocalString.antiTraceDesc,
|
||||
descColor = color,
|
||||
isSwitch = ShamrockConfig.isAntiTrace(ctx)
|
||||
) {
|
||||
ShamrockConfig.setAutoStart(ctx, it)
|
||||
ShamrockConfig.setAntiTrace(ctx, it)
|
||||
ShamrockConfig.pushUpdate(ctx)
|
||||
return@Function true
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.runCatching {
|
||||
ctx.getSharedPreferences("shared_config", Context.MODE_WORLD_READABLE)
|
||||
}.onSuccess {
|
||||
Function(
|
||||
title = "反检测加强",
|
||||
desc = "可能导致某些设备频繁闪退",
|
||||
descColor = color,
|
||||
isSwitch = it.getBoolean("super_anti", false)
|
||||
) { v ->
|
||||
it.edit().putBoolean("super_anti", v).apply()
|
||||
scope.toast(ctx, LocalString.restartToast)
|
||||
return@Function true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionBox(
|
||||
@ -151,7 +242,11 @@ fun LabFragment() {
|
||||
descColor = it,
|
||||
isSwitch = AppRuntime.state.supportVoice.value
|
||||
) {
|
||||
if(AppRuntime.state.supportVoice.value) {
|
||||
scope.toast(ctx, "关闭请手动删除文件。")
|
||||
} else {
|
||||
scope.toast(ctx, "请按照Github提示手动操作。")
|
||||
}
|
||||
return@Function false
|
||||
}
|
||||
}
|
||||
@ -180,6 +275,7 @@ fun LabFragment() {
|
||||
return@Function true
|
||||
}
|
||||
|
||||
/*
|
||||
Function(
|
||||
title = "使用纯数字ECHO",
|
||||
desc = "在部分强类型语言框架,需要打开此开关。",
|
||||
@ -189,7 +285,7 @@ fun LabFragment() {
|
||||
ShamrockConfig.setEchoNumber(ctx, it)
|
||||
ShamrockConfig.pushUpdate(ctx)
|
||||
return@Function true
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,24 @@ private open class Chūnibyō: Default() {
|
||||
"执明起,至除免于灾祸。\n" +
|
||||
"元冥浩浩,非凡不可动之。"
|
||||
labWarning = "寒酥降矣,梅熟日久,莫不可测。"
|
||||
logTitle = "无极"
|
||||
testName = "未名之人"
|
||||
logCentralLoadSuccessfully = "无极开,天地始纷争。"
|
||||
logCentralLoadFailed = "无极闭,天地始归宁。"
|
||||
functionSetting = "天地法则"
|
||||
sslSetting = "天行御令"
|
||||
warnTitle = "仙人指路"
|
||||
b2Mode = "通仙之路"
|
||||
b2ModeDesc = "凡人勿近"
|
||||
restartToast = "复关喏哉!"
|
||||
showDebugLog = "窥探天机"
|
||||
showDebugLogDesc = "迷失自我,走火入魔"
|
||||
antiTrace = "鬼影迷踪"
|
||||
antiTraceDesc = "唐门绝学,已有取死之道"
|
||||
injectPacket = "遮匿无用之禀"
|
||||
injectPacketDesc = "试于试之,逆则魂飞魄散"
|
||||
persistentText = "丹书铁券"
|
||||
persistentTextDesc = "由天地之起也,须复动之。"
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,7 +102,25 @@ private open class Default: VarString(
|
||||
"同时声明本项目仅用于学习与交流,请于24小时内删除。\n" +
|
||||
"同时开源贡献者均享受免责条例。",
|
||||
labWarning = "实验室功能,可能会导致出乎意料的BUG!",
|
||||
"日志"
|
||||
logTitle = "日志",
|
||||
testName = "测试昵称",
|
||||
logCentralLoadSuccessfully = "日志框架激活成功,开放操作许可。",
|
||||
logCentralLoadFailed = "日志框架处于未激活状态,请检查。",
|
||||
functionSetting = "功能设置",
|
||||
sslSetting = "SSL配置",
|
||||
warnTitle = "温馨提示",
|
||||
b2Mode = "中二病模式",
|
||||
b2ModeDesc = "也许会导致奇怪的问题,大抵就是你看不懂罢了。",
|
||||
restartToast = "重启生效哦!",
|
||||
restartSysToast = "重启系统生效哦!",
|
||||
showDebugLog = "显示调试日志",
|
||||
showDebugLogDesc = "会导致日志刷屏。",
|
||||
antiTrace = "防止调用栈检测",
|
||||
antiTraceDesc = "防止QQ进行堆栈跟踪检测,需要重新启动QQ。",
|
||||
injectPacket = "拦截QQ无用收包",
|
||||
injectPacketDesc = "测试阶段,可能导致网络异常或掉线。",
|
||||
persistentText = "免死金牌",
|
||||
persistentTextDesc = "由系统复活QQ和Shamrock,需要重新启动系统。"
|
||||
)
|
||||
|
||||
open class VarString(
|
||||
@ -99,5 +135,33 @@ open class VarString(
|
||||
|
||||
var labWarning: String,
|
||||
|
||||
var logTitle: String
|
||||
var logTitle: String,
|
||||
|
||||
var testName: String,
|
||||
|
||||
var logCentralLoadSuccessfully: String,
|
||||
var logCentralLoadFailed: String,
|
||||
|
||||
var functionSetting: String,
|
||||
var sslSetting: String,
|
||||
|
||||
var warnTitle: String,
|
||||
|
||||
var b2Mode: String,
|
||||
var b2ModeDesc: String,
|
||||
|
||||
var restartToast: String,
|
||||
var restartSysToast: String,
|
||||
|
||||
var showDebugLog: String,
|
||||
var showDebugLogDesc: String,
|
||||
|
||||
var antiTrace: String,
|
||||
var antiTraceDesc: String,
|
||||
|
||||
var injectPacket: String,
|
||||
var injectPacketDesc: String,
|
||||
|
||||
var persistentText: String,
|
||||
var persistentTextDesc: String
|
||||
)
|
||||
|
5
app/src/main/res/drawable/sharp_lock_24.xml
Normal file
5
app/src/main/res/drawable/sharp_lock_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#9D9D9D"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,8h-3L17,6.21c0,-2.61 -1.91,-4.94 -4.51,-5.19C9.51,0.74 7,3.08 7,6v2L4,8v14h16L20,8zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM9,8L9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L9,8z"/>
|
||||
</vector>
|
@ -10,7 +10,6 @@ android {
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
@ -36,7 +35,4 @@ dependencies {
|
||||
implementation("androidx.core:core-ktx:1.9.0")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("com.google.android.material:material:1.9.0")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
}
|
@ -33,7 +33,7 @@ public class MMKV implements SharedPreferences, SharedPreferences.Editor {
|
||||
return null;
|
||||
}
|
||||
|
||||
public SharedPreferences.Editor putBoolean(String str, boolean z) {
|
||||
public SharedPreferences.Editor putBoolean(String s, boolean z) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,50 @@
|
||||
package com.tencent.mobileqq.pb;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class MessageMicro<T extends MessageMicro<T>> {
|
||||
public static final class FieldMap {
|
||||
private Object[] defaultValues;
|
||||
private Field[] fields;
|
||||
private int[] tags;
|
||||
|
||||
FieldMap(int[] iArr, String[] strArr, Object[] objArr, Class<?> cls) {
|
||||
this.tags = iArr;
|
||||
this.defaultValues = objArr;
|
||||
this.fields = new Field[iArr.length];
|
||||
for (int i2 = 0; i2 < iArr.length; i2++) {
|
||||
try {
|
||||
this.fields[i2] = cls.getField(strArr[i2]);
|
||||
} catch (Exception e2) {
|
||||
e2.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear(MessageMicro<?> messageMicro) {
|
||||
}
|
||||
|
||||
<U extends MessageMicro<U>> void copyFields(U u, U u2) {
|
||||
}
|
||||
|
||||
Field get(int i2) {
|
||||
int binarySearch = Arrays.binarySearch(this.tags, i2);
|
||||
if (binarySearch < 0) {
|
||||
return null;
|
||||
}
|
||||
return this.fields[binarySearch];
|
||||
}
|
||||
|
||||
int getSerializedSize(MessageMicro<?> messageMicro) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static FieldMap initFieldMap(int[] iArr, String[] strArr, Object[] objArr, Class<?> cls) {
|
||||
return new FieldMap(iArr, strArr, objArr, cls);
|
||||
}
|
||||
|
||||
public final T mergeFrom(byte[] bArr) {
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,186 @@
|
||||
package com.tencent.mobileqq.relation.api;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.TextView;
|
||||
import com.tencent.common.app.AppInterface;
|
||||
import com.tencent.mobileqq.data.MessageRecord;
|
||||
import com.tencent.mobileqq.data.PhoneContact;
|
||||
import com.tencent.mobileqq.qroute.QRouteApi;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import tencent.mobileim.structmsg.*;
|
||||
|
||||
public interface IAddFriendTempApi extends QRouteApi {
|
||||
public static final int ENTER_FROM_CONTACT_TAB = 1;
|
||||
|
||||
public static final int ENTER_FROM_DEFAULT = 0;
|
||||
|
||||
public static final int ENTER_FROM_MESSAGE_TAB = 2;
|
||||
|
||||
public static final int ENTER_FROM_NEW_FRIEND = 3;
|
||||
|
||||
public static final int ENTER_FROM_NEW_FRIEND_MORE = 4;
|
||||
|
||||
// void addBatchQIMFriends(List<QIMNotifyAddFriend> paramList, AppInterface paramAppInterface);
|
||||
|
||||
void addFriendToFriendList(AppInterface paramAppInterface, String paramString1, int paramInt1, int paramInt2, String paramString2, boolean paramBoolean1, boolean paramBoolean2, long paramLong);
|
||||
|
||||
void addListener(Object paramObject, AppInterface paramAppInterface);
|
||||
|
||||
void cancelMayKnowRecommend(String paramString, AppInterface paramAppInterface);
|
||||
|
||||
// void changeStructMsgActions(structmsg.StructMsg paramStructMsg, int paramInt1, String paramString, int paramInt2);
|
||||
|
||||
// boolean changeStructMsgActionsWhenFail(structmsg.StructMsg paramStructMsg, int paramInt, String paramString1, String paramString2);
|
||||
|
||||
void checkReadContactPermission(Runnable paramRunnable, AppInterface paramAppInterface);
|
||||
|
||||
void checkUpdate(AppInterface paramAppInterface, String paramString);
|
||||
|
||||
void clearAllSystemMsg(AppInterface paramAppInterface);
|
||||
|
||||
void clickQIMSource(Context paramContext, MessageRecord paramMessageRecord, AppInterface paramAppInterface);
|
||||
|
||||
Intent composeReturnIntent(Class<?> paramClass, String paramString, Activity paramActivity);
|
||||
|
||||
void deleteAllSuspiciousMsg(AppInterface paramAppInterface);
|
||||
|
||||
// void deleteQIMNotifyAddFriendData(QIMNotifyAddFriend paramQIMNotifyAddFriend, AppInterface paramAppInterface);
|
||||
|
||||
boolean enableCheckPermission();
|
||||
|
||||
void followUser(String paramString, boolean paramBoolean);
|
||||
|
||||
Intent getAddRequestIntent(Context paramContext);
|
||||
|
||||
Intent getBindNumberIntent(Context paramContext);
|
||||
|
||||
Intent getChatActivityIntent(Context paramContext);
|
||||
|
||||
// ArrayList<MayKnowRecommend> getConnectionsPersonLocal(int paramInt, AppInterface paramAppInterface);
|
||||
|
||||
void getConnectionsPersonRemoteNextPage(int paramInt, AppInterface paramAppInterface);
|
||||
|
||||
// ArrayList<a> getConnectionsTabInfoListLocal(AppInterface paramAppInterface);
|
||||
|
||||
void getDiscussInfo(long paramLong, AppInterface paramAppInterface);
|
||||
|
||||
String getDiscussionNameCanNull(AppInterface paramAppInterface, String paramString);
|
||||
|
||||
int getForwardSelectionRequest();
|
||||
|
||||
Intent getFriendProfileMoreInfoIntent(Context paramContext);
|
||||
|
||||
int getMayKnowLoadConnectionBizTypeFirstLoad();
|
||||
|
||||
int getMayKnowPersonNum(AppInterface paramAppInterface);
|
||||
|
||||
boolean getMayKnowRecommendRemoteFromNewFrd(AppInterface paramAppInterface);
|
||||
|
||||
String getQIMNewFriendSource(AppInterface paramAppInterface);
|
||||
|
||||
// ArrayList<s> getQIMNotifyAddFriendsMsg(boolean paramBoolean, AppInterface paramAppInterface);
|
||||
|
||||
String getQQInfoFromQQUin(long paramLong1, long paramLong2, AppInterface paramAppInterface);
|
||||
|
||||
HashMap<String, String> getQidianExternal(HashMap<String, Object> paramHashMap);
|
||||
|
||||
// String getRecommendLabelString(List<MayKnowRecommend.MayKnowRecommendLabel> paramList);
|
||||
|
||||
int getRequestForSetting();
|
||||
|
||||
int getSizeSmall();
|
||||
|
||||
void getSuspiciousFriendsUnreadNum(AppInterface paramAppInterface);
|
||||
|
||||
int getTypeSetConnectionsSwitch();
|
||||
|
||||
Object getValue(String paramString1, String paramString2, int paramInt1, int paramInt2);
|
||||
|
||||
void gotoFriendSettingBrowser(Context paramContext);
|
||||
|
||||
boolean hasQidianExternal(HashMap<String, Object> paramHashMap);
|
||||
|
||||
boolean hasQimSource(AppInterface paramAppInterface);
|
||||
|
||||
void insertCommonHobbyIfNeeded(AppInterface paramAppInterface, String paramString);
|
||||
|
||||
boolean isMayKnowConnectionsUserClosed(AppInterface paramAppInterface);
|
||||
|
||||
boolean isNewFrdMiniCardSwitchOn(AppInterface paramAppInterface);
|
||||
|
||||
boolean isPhoneContactEnabled(AppInterface paramAppInterface);
|
||||
|
||||
boolean isQidianMaster(AppInterface paramAppInterface, String paramString);
|
||||
|
||||
boolean isStudyMode(AppInterface paramAppInterface);
|
||||
|
||||
boolean isSuspiciousSwitchOpen();
|
||||
|
||||
// void jumpToMoveGroup(Activity paramActivity, QBaseFragment paramQBaseFragment, String paramString, int paramInt1, int paramInt2);
|
||||
|
||||
void jumpToNewFriendMoreSysMsgSuspiciousFragment(Context paramContext);
|
||||
|
||||
void jumpToNewFriendMoreSysMsgSuspiciousFragment(Context paramContext, Intent paramIntent, int paramInt);
|
||||
|
||||
void jumpToQidianProfile(String paramString, Activity paramActivity);
|
||||
|
||||
void jumpToSplash(Activity paramActivity);
|
||||
|
||||
void launchPluginBroadcastWhenToggleSwitch(String paramString, AppInterface paramAppInterface, boolean paramBoolean);
|
||||
|
||||
void loadConnectionsTabData(AppInterface paramAppInterface, int paramInt1, int paramInt2);
|
||||
|
||||
void markQIMNotifyAddFriendsRead(AppInterface paramAppInterface);
|
||||
|
||||
// void openSecCheckWebForFragment(AppInterface paramAppInterface, Context paramContext, QBaseFragment paramQBaseFragment, int paramInt, String paramString1, String paramString2);
|
||||
|
||||
// void recordStartExpose(MayKnowRecommend paramMayKnowRecommend, int paramInt1, int paramInt2, int paramInt3, AppInterface paramAppInterface);
|
||||
|
||||
// void recordStopExpose(MayKnowRecommend paramMayKnowRecommend, int paramInt1, int paramInt2, int paramInt3, AppInterface paramAppInterface);
|
||||
|
||||
void removeListener(Object paramObject, AppInterface paramAppInterface);
|
||||
|
||||
void reportExtendFriend(int paramInt, String paramString, Intent paramIntent);
|
||||
|
||||
void reportRecommend(AppInterface paramAppInterface, String paramString1, String paramString2, int paramInt1, int paramInt2, String paramString3, int paramInt3, byte[] paramArrayOfbyte, String paramString4, int paramInt4);
|
||||
|
||||
void reportRecommendExpose(AppInterface paramAppInterface, int paramInt1, ArrayList<String> paramArrayList1, ArrayList<String> paramArrayList2, ArrayList<Integer> paramArrayList, ArrayList<byte[]> paramArrayList3, int paramInt2);
|
||||
|
||||
void sendAddFriendNoticeForBaby(AppInterface paramAppInterface, Intent paramIntent);
|
||||
|
||||
// void sendDelSingleSystemMsg(structmsg.StructMsg paramStructMsg, String paramString, int paramInt, long paramLong, AppInterface paramAppInterface);
|
||||
|
||||
void sendFriendSystemMsgAction(int msg_type, long msg_seq, long req_uin, int sub_type, int src_id, int sub_src_id, int group_msg_type, structmsg$SystemMsgActionInfo action_info, int system_msg_action_type, structmsg$StructMsg paramStructMsg, boolean isUncommonlyUsedFrd, AppInterface paramAppInterface);
|
||||
|
||||
void sendFriendSystemMsgReadedReport(AppInterface paramAppInterface);
|
||||
|
||||
void sendGetNextFriendSystemMsg(AppInterface paramAppInterface);
|
||||
|
||||
void sendPokeMsg(AppInterface paramAppInterface, Context paramContext, String paramString);
|
||||
|
||||
void setConnectionsSwitch(boolean paramBoolean, AppInterface paramAppInterface);
|
||||
|
||||
boolean shouldShowMayKnowInNewFriend(AppInterface paramAppInterface);
|
||||
|
||||
// void startAddContactsPage(Context paramContext, int paramInt1, int paramInt2, LaunchMode paramLaunchMode, @Nullable Bundle paramBundle);
|
||||
|
||||
void startAddContactsPageForResult(Activity paramActivity, int paramInt1, int paramInt2, int paramInt3);
|
||||
|
||||
void startAddRequestPage(Context paramContext, Intent paramIntent);
|
||||
|
||||
void startAddRequestPageForResult(Activity paramActivity, Intent paramIntent, int paramInt);
|
||||
|
||||
void startAddRequestSuspiciousPage(Context paramContext, Intent paramIntent);
|
||||
|
||||
void startContactBindFromOther(AppInterface paramAppInterface, int paramInt1, int paramInt2, Intent paramIntent);
|
||||
|
||||
void startContactBindFromOther(AppInterface paramAppInterface, int paramInt, ArrayList<PhoneContact> paramArrayList);
|
||||
|
||||
void startRemarkAfterAgree(Activity paramActivity, int paramInt, String paramString, long paramLong, Bundle paramBundle);
|
||||
|
||||
void updateCustomNoteTxt(TextView paramTextView, int paramInt1, int paramInt2);
|
||||
}
|
@ -8,6 +8,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import mqq.app.AppRuntime;
|
||||
|
||||
public class BaseTransFileController implements ITransFileController {
|
||||
@Override
|
||||
public boolean containsProcessor(String name, long uin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IHttpCommunicatorListener findProcessor(String str) {
|
||||
return null;
|
||||
|
@ -14,7 +14,7 @@ public interface ITransFileController extends IRuntimeService {
|
||||
//@Deprecated
|
||||
//void addProcessor(String str, IHttpCommunicatorListener iHttpCommunicatorListener);
|
||||
|
||||
//boolean containsProcessor(String str, long j2);
|
||||
boolean containsProcessor(String name, long uin);
|
||||
|
||||
IHttpCommunicatorListener findProcessor(String str);
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
package com.tencent.mobileqq.troop.api;
|
||||
|
||||
import mqq.app.api.IRuntimeService;
|
||||
import com.tencent.mobileqq.data.troop.TroopMemberInfo;
|
||||
|
||||
public interface ITroopMemberNameService extends IRuntimeService {
|
||||
String getTroopMemberColorNick(String str, String str2);
|
||||
|
||||
String getTroopMemberName(TroopMemberInfo troopMemberInfo);
|
||||
|
||||
String getTroopMemberName(String str, String str2);
|
||||
|
||||
String getTroopMemberName(String str, String str2, String str3, String str4);
|
||||
|
||||
String getTroopMemberName(String str, String str2, boolean z, boolean z2);
|
||||
|
||||
//void getTroopMemberNameAsync(String str, String str2, a aVar);
|
||||
|
||||
String getTroopMemberNameInUI(String str, String str2);
|
||||
|
||||
String getTroopMemberNameRemarkFirst(String str, String str2);
|
||||
|
||||
String getTroopMemberNameWithoutRemark(String str, String str2);
|
||||
|
||||
String getTroopMemberNick(String str, String str2);
|
||||
|
||||
String getTroopMemberNickByTroopCode(String str, String str2);
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
package com.tencent.qqnt.kernel.api.impl;
|
||||
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IKernelMsgListener;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.TempChatPrepareInfo;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -18,4 +20,8 @@ public class MsgService {
|
||||
public String getRichMediaFilePathForMobileQQSend(@NotNull RichMediaFilePathInfo richMediaFilePathInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void prepareTempChat(TempChatPrepareInfo tempChatPrepareInfo, IOperateCallback cb) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -44,8 +44,6 @@ public final class Contact implements IKernelModel, Serializable {
|
||||
|
||||
public Contact(int i2, String str, String str2) {
|
||||
this.serialVersionUID = 1L;
|
||||
this.peerUid = "";
|
||||
this.guildId = "";
|
||||
this.chatType = i2;
|
||||
this.peerUid = str;
|
||||
this.guildId = str2;
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
/* compiled from: P */
|
||||
/* loaded from: classes2.dex */
|
||||
public final class GroupFileCommonResult {
|
||||
String clientWording;
|
||||
int retCode;
|
||||
@ -29,8 +27,6 @@ public final class GroupFileCommonResult {
|
||||
}
|
||||
|
||||
public GroupFileCommonResult(int i2, String str, String str2) {
|
||||
this.retMsg = "";
|
||||
this.clientWording = "";
|
||||
this.retCode = i2;
|
||||
this.retMsg = str;
|
||||
this.clientWording = str2;
|
||||
|
@ -0,0 +1,4 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public class GuildInteractiveNotificationItem {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public class GuildNotificationAbstractInfo {
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public interface IClearMsgRecordsCallback {
|
||||
void onResult(int code, String reason, long lastMsgId);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public interface IDeleteGroupFileCallback {
|
||||
void onResult(int i2, String str, DeleteGroupFileResult deleteGroupFileResult);
|
||||
void onResult(int code, String why, DeleteGroupFileResult result);
|
||||
}
|
@ -42,6 +42,10 @@ public interface IKernelMsgListener {
|
||||
|
||||
void onGroupTransferInfoUpdate(GroupFileListResult groupFileListResult);
|
||||
|
||||
void onGuildInteractiveUpdate(GuildInteractiveNotificationItem guildInteractiveNotificationItem);
|
||||
|
||||
void onGuildNotificationAbstractUpdate(GuildNotificationAbstractInfo guildNotificationAbstractInfo);
|
||||
|
||||
void onHitCsRelatedEmojiResult(DownloadRelateEmojiResultInfo downloadRelateEmojiResultInfo);
|
||||
|
||||
void onHitEmojiKeywordResult(HitRelatedEmojiWordsResult hitRelatedEmojiWordsResult);
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
@ -16,7 +18,25 @@ public interface IKernelMsgService {
|
||||
|
||||
void addLocalRecordMsg(Contact contact, long msgId, MsgElement elem, HashMap<Integer, MsgAttributeInfo> hashMap, boolean z, IOperateCallback callback);
|
||||
|
||||
void getMultiMsg(Contact contact, long msgId, long uniseq, IGetMultiMsgCallback cb);
|
||||
long getMsgUniqueId(long time);
|
||||
|
||||
void addSendMsg(long msgId, Contact contact, ArrayList<MsgElement> msgList, HashMap<Integer, MsgAttributeInfo> hashMap);
|
||||
|
||||
void getMsgs(@NotNull Contact contact, long startMsgId, int cnt, boolean queryOrder, @NotNull IMsgOperateCallback iMsgOperateCallback);
|
||||
|
||||
void getMsgsIncludeSelf(Contact contact, long startMsgId, int count, boolean queryOrder, IMsgOperateCallback iMsgOperateCallback);
|
||||
|
||||
void translatePtt2Text(long j2, Contact contact, MsgElement msgElement, IOperateCallback iOperateCallback);
|
||||
|
||||
void getMultiMsg(Contact contact, long rootMsgId, long parentMsgId, IGetMultiMsgCallback cb);
|
||||
|
||||
void multiForwardMsg(ArrayList<MultiMsgInfo> arrayList, Contact from, Contact to, IOperateCallback cb);
|
||||
|
||||
void setAllC2CAndGroupMsgRead(IOperateCallback cb);
|
||||
|
||||
void clearMsgRecords(Contact contact, IClearMsgRecordsCallback cb);
|
||||
|
||||
String createUidFromTinyId(long j2, long j3);
|
||||
|
||||
void switchBackGround(BackGroundInfo backGroundInfo, IOperateCallback cb);
|
||||
|
||||
|
@ -9,7 +9,7 @@ public interface IKernelRichMediaService {
|
||||
|
||||
void cancelTransferTask(Contact contact, ArrayList<Long> arrayList, ArrayList<Integer> arrayList2, IOperateTransferInfoCallback iOperateTransferInfoCallback);
|
||||
|
||||
void deleteGroupFile(long j2, String str, int i2, IDeleteGroupFileCallback iDeleteGroupFileCallback);
|
||||
void deleteGroupFile(long groupCode, String fileUid, int bizId, IDeleteGroupFileCallback cb);
|
||||
|
||||
void deleteTransferInfo(Contact contact, ArrayList<Long> arrayList, IOperateTransferInfoCallback iOperateTransferInfoCallback);
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
/* compiled from: P */
|
||||
/* loaded from: classes2.dex */
|
||||
public final class MultiMsgInfo {
|
||||
long msgId;
|
||||
String senderShowName;
|
||||
@ -21,8 +19,6 @@ public final class MultiMsgInfo {
|
||||
return "MultiMsgInfo{msgId=" + this.msgId + ",senderShowName=" + this.senderShowName + ",}";
|
||||
}
|
||||
|
||||
public MultiMsgInfo(long j2, String str) {
|
||||
this.msgId = j2;
|
||||
this.senderShowName = str;
|
||||
public MultiMsgInfo(long msgId, String showName) {
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
/* compiled from: P */
|
||||
/* loaded from: classes.dex */
|
||||
public final class TempChatInfo {
|
||||
int chatType;
|
||||
String fromNick;
|
||||
|
@ -56,20 +56,14 @@ public final class TempChatPrepareInfo {
|
||||
return "TempChatPrepareInfo{chatType=" + this.chatType + ",peerUid=" + this.peerUid + ",peerNickname=" + this.peerNickname + ",fromGroupCode=" + this.fromGroupCode + ",sig=" + this.sig + ",selfUid=" + this.selfUid + ",selfPhone=" + this.selfPhone + ",gameSession=" + this.gameSession + ",}";
|
||||
}
|
||||
|
||||
public TempChatPrepareInfo(int i2, String str, String str2, String str3, byte[] bArr, String str4, String str5, TempChatGameSession tempChatGameSession) {
|
||||
this.peerUid = "";
|
||||
this.peerNickname = "";
|
||||
this.fromGroupCode = "";
|
||||
this.sig = new byte[0];
|
||||
this.selfUid = "";
|
||||
this.selfPhone = "";
|
||||
this.chatType = i2;
|
||||
this.peerUid = str;
|
||||
this.peerNickname = str2;
|
||||
this.fromGroupCode = str3;
|
||||
this.sig = bArr;
|
||||
this.selfUid = str4;
|
||||
this.selfPhone = str5;
|
||||
this.gameSession = tempChatGameSession;
|
||||
public TempChatPrepareInfo(int chatType, String peerId, String nickName, String fromGroup, byte[] sig, String selfUid, String selfPhone, TempChatGameSession session) {
|
||||
this.chatType = chatType;
|
||||
this.peerUid = peerId;
|
||||
this.peerNickname = nickName;
|
||||
this.fromGroupCode = fromGroup;
|
||||
this.sig = sig;
|
||||
this.selfUid = selfUid;
|
||||
this.selfPhone = selfPhone;
|
||||
this.gameSession = session;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.tencent.qphone.base.remote.SimpleAccount;
|
||||
import com.tencent.qphone.base.remote.ToServiceMsg;
|
||||
|
||||
import mqq.app.api.IRuntimeService;
|
||||
@ -72,6 +73,10 @@ public abstract class AppRuntime {
|
||||
public <T extends IRuntimeService> T getRuntimeServiceIPCSync(@NonNull Class<T> cls, String str) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void switchAccount(SimpleAccount simpleAccount, String process) {
|
||||
|
||||
}
|
||||
public String getAccount() {
|
||||
return "";
|
||||
}
|
||||
@ -81,6 +86,10 @@ public abstract class AppRuntime {
|
||||
return !"0".equals(getCurrentAccountUin()) ? getCurrentAccountUin() : "";
|
||||
}
|
||||
|
||||
public String getCurrentUid() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public long getLongAccountUin() {
|
||||
return 0;
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
package tencent.im.oidb.cmd0x899;
|
||||
|
||||
import com.tencent.mobileqq.pb.ByteStringMicro;
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBBytesField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBRepeatField;
|
||||
import com.tencent.mobileqq.pb.PBRepeatMessageField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public class oidb_0x899 {
|
||||
public static class ReqBody extends MessageMicro<ReqBody> {
|
||||
//static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[]{8, 16, 24, 32, 42, 48, 56, 64}, new String[]{
|
||||
// "uint64_group_code",
|
||||
// "uint64_start_uin",
|
||||
// "uint32_identify_flag",
|
||||
// "rpt_uint64_uin_list",
|
||||
// "memberlist_opt",
|
||||
// "uint32_member_num", "uint32_filter_method", "uint32_online_flag"}, new Object[]{0L, 0L, 0, 0L, null, 0, 0, 0}, ReqBody.class);
|
||||
public final PBUInt64Field uint64_group_code = PBField.initUInt64(0);
|
||||
public final PBUInt64Field uint64_start_uin = PBField.initUInt64(0);
|
||||
public final PBUInt32Field uint32_identify_flag = PBField.initUInt32(0);
|
||||
public final PBRepeatField<Long> rpt_uint64_uin_list = PBField.initRepeat(PBUInt64Field.__repeatHelper__);
|
||||
public memberlist memberlist_opt = new memberlist();
|
||||
public final PBUInt32Field uint32_member_num = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_filter_method = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_online_flag = PBField.initUInt32(0);
|
||||
}
|
||||
|
||||
public static class RspBody extends MessageMicro<RspBody> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[]{8, 16, 24, 34, 42}, new String[]{"uint64_group_code", "uint64_start_uin", "uint32_identify_flag", "rpt_memberlist", "str_errorinfo"}, new Object[]{0L, 0L, 0, null, ByteStringMicro.EMPTY}, RspBody.class);
|
||||
public final PBUInt64Field uint64_group_code = PBField.initUInt64(0);
|
||||
public final PBUInt64Field uint64_start_uin = PBField.initUInt64(0);
|
||||
public final PBUInt32Field uint32_identify_flag = PBField.initUInt32(0);
|
||||
public final PBRepeatMessageField<memberlist> rpt_memberlist = PBField.initRepeatMessage(memberlist.class);
|
||||
public final PBBytesField str_errorinfo = PBField.initBytes(ByteStringMicro.EMPTY);
|
||||
}
|
||||
|
||||
public static class memberlist extends MessageMicro<memberlist> {
|
||||
static final MessageMicro.FieldMap __fieldMap__;
|
||||
public final PBBytesField bytes_rich_info;
|
||||
public final PBBytesField bytes_special_title;
|
||||
public final PBBytesField bytes_uin_key;
|
||||
public final PBUInt32Field uint32_active_day;
|
||||
public final PBUInt32Field uint32_privilege;
|
||||
public final PBUInt32Field uint32_special_title_expire_time;
|
||||
public final PBUInt64Field uint64_member_uin = PBField.initUInt64(0);
|
||||
public final PBUInt32Field uint32_uin_flag = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_uin_flagex = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_uin_mobile_flag = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_uin_arch_flag = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_join_time = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_old_msg_seq = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_new_msg_seq = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_last_speak_time = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_level = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_point = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_shutup_timestap = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_flagex2 = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[]{8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 114, 120, 128, 138, 144, 154}, new String[]{
|
||||
"uint64_member_uin", // 1
|
||||
|
||||
"uint32_uin_flag", "uint32_uin_flagex", "uint32_uin_mobile_flag", "uint32_uin_arch_flag", "uint32_join_time", "uint32_old_msg_seq", "uint32_new_msg_seq", "uint32_last_speak_time", "uint32_level", "uint32_point",
|
||||
|
||||
"uint32_shutup_timestap", //12
|
||||
|
||||
"uint32_flagex2", "bytes_special_title", "uint32_special_title_expire_time", "uint32_active_day", "bytes_uin_key", "uint32_privilege", "bytes_rich_info"}, new Object[]{0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byteStringMicro, 0, 0, byteStringMicro, 0, byteStringMicro}, memberlist.class);
|
||||
}
|
||||
|
||||
public memberlist() {
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
this.bytes_special_title = PBField.initBytes(byteStringMicro);
|
||||
this.uint32_special_title_expire_time = PBField.initUInt32(0);
|
||||
this.uint32_active_day = PBField.initUInt32(0);
|
||||
this.bytes_uin_key = PBField.initBytes(byteStringMicro);
|
||||
this.uint32_privilege = PBField.initUInt32(0);
|
||||
this.bytes_rich_info = PBField.initBytes(byteStringMicro);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
|
||||
public final class structmsg$AddFrdSNInfo extends MessageMicro<structmsg$AddFrdSNInfo> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBUInt32Field uint32_not_see_dynamic = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_set_sn = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16 }, new String[] { "uint32_not_see_dynamic", "uint32_set_sn" }, new Object[] { integer, integer }, structmsg$AddFrdSNInfo.class);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
|
||||
public final class structmsg$FlagInfo extends MessageMicro<structmsg$FlagInfo> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBUInt32Field FrdMsg_Discuss2ManyChat = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field FrdMsg_GetBusiCard = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field FrdMsg_NeedWaitingMsg = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field FrdMsg_uint32_need_all_unread_msg = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_GetC2cInviteJoinGroup = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_GetDisbandedByAdmin = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_GetOfficialAccount = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_GetPayInGroup = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_HiddenGrp = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_Kick_Admin = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_NeedAutoAdminWording = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_NotAllowJoinGrp_InviteNotFrd = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_WordingDown = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_get_quit_pay_group_msg_flag = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_get_transfer_group_msg_flag = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_mask_invite_auto_join = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field GrpMsg_support_invite_auto_join = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] {
|
||||
8, 16, 24, 32, 40, 48, 56, 64, 72, 80,
|
||||
88, 96, 104, 112, 120, 128, 136 }, new String[] {
|
||||
"GrpMsg_Kick_Admin", "GrpMsg_HiddenGrp", "GrpMsg_WordingDown", "FrdMsg_GetBusiCard", "GrpMsg_GetOfficialAccount", "GrpMsg_GetPayInGroup", "FrdMsg_Discuss2ManyChat", "GrpMsg_NotAllowJoinGrp_InviteNotFrd", "FrdMsg_NeedWaitingMsg", "FrdMsg_uint32_need_all_unread_msg",
|
||||
"GrpMsg_NeedAutoAdminWording", "GrpMsg_get_transfer_group_msg_flag", "GrpMsg_get_quit_pay_group_msg_flag", "GrpMsg_support_invite_auto_join", "GrpMsg_mask_invite_auto_join", "GrpMsg_GetDisbandedByAdmin", "GrpMsg_GetC2cInviteJoinGroup" }, new Object[] {
|
||||
integer, integer, integer, integer, integer, integer, integer, integer, integer, integer,
|
||||
integer, integer, integer, integer, integer, integer, integer }, structmsg$FlagInfo.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBStringField;
|
||||
|
||||
public final class structmsg$FriendInfo extends MessageMicro<structmsg$FriendInfo> {
|
||||
static final FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18 }, new String[] { "msg_joint_friend", "msg_blacklist" }, new Object[] { "", "" }, structmsg$FriendInfo.class);
|
||||
|
||||
public final PBStringField msg_blacklist = PBField.initString("");
|
||||
|
||||
public final PBStringField msg_joint_friend = PBField.initString("");
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBStringField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
|
||||
public final class structmsg$GroupInfo extends MessageMicro<structmsg$GroupInfo> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBUInt32Field display_action = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field group_auth_type = PBField.initUInt32(0);
|
||||
|
||||
public final PBStringField msg_alert = PBField.initString("");
|
||||
|
||||
public final PBStringField msg_detail_alert = PBField.initString("");
|
||||
|
||||
public final PBStringField msg_other_admin_done = PBField.initString("");
|
||||
|
||||
public final PBUInt32Field uint32_app_privilege_flag = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 26, 34, 42, 48 }, new String[] { "group_auth_type", "display_action", "msg_alert", "msg_detail_alert", "msg_other_admin_done", "uint32_app_privilege_flag" }, new Object[] { integer, integer, "", "", "", integer }, structmsg$GroupInfo.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public final class structmsg$MsgInviteExt extends MessageMicro<structmsg$MsgInviteExt> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBUInt32Field uint32_src_type = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_wait_state = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt64Field uint64_src_code = PBField.initUInt64(0L);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24 }, new String[] { "uint32_src_type", "uint64_src_code", "uint32_wait_state" }, new Object[] { integer, Long.valueOf(0L), integer }, structmsg$MsgInviteExt.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public final class structmsg$MsgPayGroupExt extends MessageMicro<structmsg$MsgPayGroupExt> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBUInt64Field uint64_join_grp_time = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field uint64_quit_grp_time = PBField.initUInt64(0L);
|
||||
|
||||
static {
|
||||
Long long_ = Long.valueOf(0L);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16 }, new String[] { "uint64_join_grp_time", "uint64_quit_grp_time" }, new Object[] { long_, long_ }, structmsg$MsgPayGroupExt.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBEnumField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public final class structmsg$ReqNextSystemMsg extends MessageMicro<structmsg$ReqNextSystemMsg> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBEnumField checktype = PBField.initEnum(1);
|
||||
|
||||
public structmsg$FlagInfo flag = new structmsg$FlagInfo();
|
||||
|
||||
public final PBUInt64Field following_friend_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field following_group_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field friend_msg_type_flag = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt32Field language = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field msg_num = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_need_uid = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_req_msg_type = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field version = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
Long long_ = Long.valueOf(0L);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24, 32, 42, 48, 56, 64, 72, 128 }, new String[] { "msg_num", "following_friend_seq", "following_group_seq", "checktype", "flag", "language", "version", "friend_msg_type_flag", "uint32_req_msg_type", "uint32_need_uid" }, new Object[] { integer, long_, long_, Integer.valueOf(1), null, integer, integer, long_, integer, integer }, structmsg$ReqNextSystemMsg.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBEnumField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public final class structmsg$ReqSystemMsgAction extends MessageMicro<structmsg$ReqSystemMsgAction> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public structmsg$SystemMsgActionInfo action_info = new structmsg$SystemMsgActionInfo();
|
||||
|
||||
public final PBUInt32Field group_msg_type = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field language = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt64Field msg_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBEnumField msg_type = PBField.initEnum(1);
|
||||
|
||||
public final PBUInt64Field req_uin = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt32Field src_id = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field sub_src_id = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field sub_type = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
Long long_ = Long.valueOf(0L);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24, 32, 40, 48, 56, 66, 72 }, new String[] { "msg_type", "msg_seq", "req_uin", "sub_type", "src_id", "sub_src_id", "group_msg_type", "action_info", "language" }, new Object[] { Integer.valueOf(1), long_, long_, integer, integer, integer, integer, null, integer }, structmsg$ReqSystemMsgAction.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBBoolField;
|
||||
import com.tencent.mobileqq.pb.PBEnumField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public final class structmsg$ReqSystemMsgNew extends MessageMicro<structmsg$ReqSystemMsgNew> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBEnumField checktype = PBField.initEnum(1);
|
||||
|
||||
public structmsg$FlagInfo flag = new structmsg$FlagInfo();
|
||||
|
||||
public final PBUInt64Field friend_msg_type_flag = PBField.initUInt64(0L);
|
||||
|
||||
public final PBBoolField is_get_frd_ribbon = PBField.initBool(true);
|
||||
|
||||
public final PBBoolField is_get_grp_ribbon = PBField.initBool(true);
|
||||
|
||||
public final PBUInt32Field language = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt64Field latest_friend_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field latest_group_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt32Field msg_num = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_need_uid = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_req_msg_type = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field version = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
Long long_ = Long.valueOf(0L);
|
||||
Boolean bool = Boolean.TRUE;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] {
|
||||
8, 16, 24, 32, 40, 50, 56, 64, 72, 80,
|
||||
88, 128 }, new String[] {
|
||||
"msg_num", "latest_friend_seq", "latest_group_seq", "version", "checktype", "flag", "language", "is_get_frd_ribbon", "is_get_grp_ribbon", "friend_msg_type_flag",
|
||||
"uint32_req_msg_type", "uint32_need_uid" }, new Object[] {
|
||||
integer, long_, long_, integer, Integer.valueOf(1), null, integer, bool, bool, long_,
|
||||
integer, integer }, structmsg$ReqSystemMsgNew.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBEnumField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public final class structmsg$ReqSystemMsgRead extends MessageMicro<structmsg$ReqSystemMsgRead> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBEnumField checktype = PBField.initEnum(1);
|
||||
|
||||
public final PBUInt64Field latest_friend_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field latest_group_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt32Field type = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_req_msg_type = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Long long_ = Long.valueOf(0L);
|
||||
Integer integer = Integer.valueOf(0);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24, 32, 40 }, new String[] { "latest_friend_seq", "latest_group_seq", "type", "checktype", "uint32_req_msg_type" }, new Object[] { long_, long_, integer, Integer.valueOf(1), integer }, structmsg$ReqSystemMsgRead.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBStringField;
|
||||
|
||||
public final class structmsg$RspHead extends MessageMicro<structmsg$RspHead> {
|
||||
static final FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 18 }, new String[] { "result", "msg_fail" }, new Object[] { Integer.valueOf(0), "" }, structmsg$RspHead.class);
|
||||
|
||||
public final PBStringField msg_fail = PBField.initString("");
|
||||
|
||||
public final PBInt32Field result = PBField.initInt32(0);
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.ByteStringMicro;
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBBytesField;
|
||||
import com.tencent.mobileqq.pb.PBEnumField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBRepeatMessageField;
|
||||
import com.tencent.mobileqq.pb.PBStringField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public final class structmsg$RspNextSystemMsg extends MessageMicro<structmsg$RspNextSystemMsg> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBStringField bytes_game_nick = PBField.initString("");
|
||||
|
||||
public final PBBytesField bytes_undecid_for_qim = PBField.initBytes(ByteStringMicro.EMPTY);
|
||||
|
||||
public final PBEnumField checktype = PBField.initEnum(1);
|
||||
|
||||
public final PBUInt64Field following_friend_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field following_group_seq = PBField.initUInt64(0L);
|
||||
|
||||
public structmsg$RspHead head = new structmsg$RspHead();
|
||||
|
||||
public final PBRepeatMessageField<structmsg$StructMsg> msgs = PBField.initRepeatMessage(structmsg$StructMsg.class);
|
||||
|
||||
public final PBUInt32Field uint32_un_read_count3 = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Long long_ = Long.valueOf(0L);
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 24, 32, 40, 802, 810, 816 }, new String[] { "head", "msgs", "following_friend_seq", "following_group_seq", "checktype", "bytes_game_nick", "bytes_undecid_for_qim", "uint32_un_read_count3" }, new Object[] { null, null, long_, long_, Integer.valueOf(1), "", byteStringMicro, Integer.valueOf(0) }, structmsg$RspNextSystemMsg.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBStringField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
|
||||
public final class structmsg$RspSystemMsgAction extends MessageMicro<structmsg$RspSystemMsgAction> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public structmsg$RspHead head = new structmsg$RspHead();
|
||||
|
||||
public final PBStringField msg_detail = PBField.initString("");
|
||||
|
||||
public final PBStringField msg_invalid_decided = PBField.initString("");
|
||||
|
||||
public final PBUInt32Field remark_result = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field type = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 24, 42, 48 }, new String[] { "head", "msg_detail", "type", "msg_invalid_decided", "remark_result" }, new Object[] { null, "", integer, "", integer }, structmsg$RspSystemMsgAction.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.ByteStringMicro;
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBBytesField;
|
||||
import com.tencent.mobileqq.pb.PBEnumField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBRepeatMessageField;
|
||||
import com.tencent.mobileqq.pb.PBStringField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public final class structmsg$RspSystemMsgNew extends MessageMicro<structmsg$RspSystemMsgNew> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBStringField bytes_game_nick = PBField.initString("");
|
||||
|
||||
public final PBBytesField bytes_undecid_for_qim = PBField.initBytes(ByteStringMicro.EMPTY);
|
||||
|
||||
public final PBEnumField checktype = PBField.initEnum(1);
|
||||
|
||||
public final PBUInt64Field following_friend_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field following_group_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBRepeatMessageField<structmsg$StructMsg> friendmsgs = PBField.initRepeatMessage(structmsg$StructMsg.class);
|
||||
|
||||
public final PBRepeatMessageField<structmsg$StructMsg> groupmsgs = PBField.initRepeatMessage(structmsg$StructMsg.class);
|
||||
|
||||
public final PBStringField grp_msg_display = PBField.initString("");
|
||||
|
||||
public structmsg$RspHead head = new structmsg$RspHead();
|
||||
|
||||
public final PBUInt64Field latest_friend_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field latest_group_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBStringField msg_display = PBField.initString("");
|
||||
|
||||
public structmsg$StructMsg msg_ribbon_friend = new structmsg$StructMsg();
|
||||
|
||||
public structmsg$StructMsg msg_ribbon_group = new structmsg$StructMsg();
|
||||
|
||||
public final PBUInt32Field uint32_has_suspicious_flag = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_over = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_un_read_count3 = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field unread_friend_count = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field unread_group_count = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
Long long_ = Long.valueOf(0L);
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] {
|
||||
10, 16, 24, 32, 40, 48, 56, 74, 82, 90,
|
||||
98, 106, 114, 120, 160, 802, 810, 816, 824 }, new String[] {
|
||||
"head", "unread_friend_count", "unread_group_count", "latest_friend_seq", "latest_group_seq", "following_friend_seq", "following_group_seq", "friendmsgs", "groupmsgs", "msg_ribbon_friend",
|
||||
"msg_ribbon_group", "msg_display", "grp_msg_display", "uint32_over", "checktype", "bytes_game_nick", "bytes_undecid_for_qim", "uint32_un_read_count3", "uint32_has_suspicious_flag" }, new Object[] {
|
||||
null, integer, integer, long_, long_, long_, long_, null, null, null,
|
||||
null, "", "", integer, Integer.valueOf(1), "", byteStringMicro, integer, integer }, structmsg$RspSystemMsgNew.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBEnumField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
|
||||
public final class structmsg$RspSystemMsgRead extends MessageMicro<structmsg$RspSystemMsgRead> {
|
||||
static final FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 16, 24 }, new String[] { "head", "type", "checktype" }, new Object[] { null, Integer.valueOf(0), Integer.valueOf(1) }, structmsg$RspSystemMsgRead.class);
|
||||
|
||||
public final PBEnumField checktype = PBField.initEnum(1);
|
||||
|
||||
public structmsg$RspHead head = new structmsg$RspHead();
|
||||
|
||||
public final PBUInt32Field type = PBField.initUInt32(0);
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBEnumField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public final class structmsg$StructMsg extends MessageMicro<structmsg$StructMsg> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public structmsg$SystemMsg msg = new structmsg$SystemMsg();
|
||||
|
||||
public final PBUInt64Field msg_seq = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field msg_time = PBField.initUInt64(0L);
|
||||
|
||||
public final PBEnumField msg_type = PBField.initEnum(1);
|
||||
|
||||
public final PBUInt64Field req_uin = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt32Field uint32_unread_flag = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field version = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
Long long_ = Long.valueOf(0L);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24, 32, 40, 48, 402 }, new String[] { "version", "msg_type", "msg_seq", "msg_time", "req_uin", "uint32_unread_flag", "msg" }, new Object[] { integer, Integer.valueOf(1), long_, long_, long_, integer, null }, structmsg$StructMsg.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,186 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.ByteStringMicro;
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBBytesField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBRepeatMessageField;
|
||||
import com.tencent.mobileqq.pb.PBStringField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public final class structmsg$SystemMsg extends MessageMicro<structmsg$SystemMsg> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBUInt64Field action_uin = PBField.initUInt64(0L);
|
||||
|
||||
public final PBStringField action_uin_nick;
|
||||
|
||||
public final PBBytesField action_uin_qq_nick;
|
||||
|
||||
public final PBBytesField action_uin_remark;
|
||||
|
||||
public final PBRepeatMessageField<structmsg$SystemMsgAction> actions = PBField.initRepeatMessage(structmsg$SystemMsgAction.class);
|
||||
|
||||
public final PBUInt64Field actor_uin = PBField.initUInt64(0L);
|
||||
|
||||
public final PBStringField actor_uin_nick;
|
||||
|
||||
public final PBBytesField bytes_addtion;
|
||||
|
||||
public final PBBytesField bytes_game_msg;
|
||||
|
||||
public final PBBytesField bytes_game_nick;
|
||||
|
||||
public final PBBytesField bytes_name_more;
|
||||
|
||||
public final PBBytesField bytes_source_desc;
|
||||
|
||||
public final PBBytesField bytes_transparent_group_notify;
|
||||
|
||||
public final PBBytesField bytes_warning_tips;
|
||||
|
||||
public final PBUInt32Field card_switch;
|
||||
|
||||
public final PBUInt64Field clone_uin = PBField.initUInt64(0L);
|
||||
|
||||
public final PBStringField clone_uin_nick;
|
||||
|
||||
public final PBBytesField eim_group_id_name;
|
||||
|
||||
public structmsg$FriendInfo friend_info = new structmsg$FriendInfo();
|
||||
|
||||
public final PBUInt64Field group_code = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt32Field group_ext_flag;
|
||||
|
||||
public structmsg$GroupInfo group_info = new structmsg$GroupInfo();
|
||||
|
||||
public final PBUInt32Field group_inviter_role = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field group_msg_type = PBField.initUInt32(0);
|
||||
|
||||
public final PBStringField group_name;
|
||||
|
||||
public final PBStringField msg_actor_describe = PBField.initString("");
|
||||
|
||||
public final PBStringField msg_additional = PBField.initString("");
|
||||
|
||||
public final PBStringField msg_additional_list = PBField.initString("");
|
||||
|
||||
public final PBStringField msg_decided = PBField.initString("");
|
||||
|
||||
public final PBStringField msg_describe = PBField.initString("");
|
||||
|
||||
public final PBStringField msg_detail;
|
||||
|
||||
public structmsg$MsgInviteExt msg_invite_extinfo = new structmsg$MsgInviteExt();
|
||||
|
||||
public structmsg$MsgPayGroupExt msg_pay_group_extinfo = new structmsg$MsgPayGroupExt();
|
||||
|
||||
public final PBStringField msg_qna;
|
||||
|
||||
public final PBStringField msg_source = PBField.initString("");
|
||||
|
||||
public final PBStringField msg_title = PBField.initString("");
|
||||
|
||||
public final PBBytesField pic_url;
|
||||
|
||||
public final PBUInt32Field relation = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field req_uin_age;
|
||||
|
||||
public final PBBytesField req_uin_business_card;
|
||||
|
||||
public final PBInt32Field req_uin_faceid;
|
||||
|
||||
public final PBUInt32Field req_uin_gender;
|
||||
|
||||
public final PBStringField req_uin_nick;
|
||||
|
||||
public final PBBytesField req_uin_pre_remark;
|
||||
|
||||
public final PBUInt32Field reqsubtype = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field src_id = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field sub_src_id = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field sub_type = PBField.initUInt32(0);
|
||||
|
||||
public final PBStringField uid;
|
||||
|
||||
public final PBUInt32Field uint32_c2c_invite_join_group_flag;
|
||||
|
||||
public final PBUInt32Field uint32_doubt_flag;
|
||||
|
||||
public final PBUInt32Field uint32_group_flagext3;
|
||||
|
||||
public final PBUInt32Field uint32_source_flag = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt64Field uint64_discuss_uin = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field uint64_eim_group_id = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field uint64_group_owner_uin;
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
Long long_ = Long.valueOf(0L);
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] {
|
||||
8, 18, 26, 34, 42, 50, 56, 64, 74, 80,
|
||||
88, 96, 104, 114, 122, 128, 138, 146, 152, 160,
|
||||
168, 176, 184, 194, 202, 208, 218, 226, 232, 240,
|
||||
248, 258, 266, 274, 282, 400, 410, 418, 426, 434,
|
||||
442, 456, 466, 474, 482, 490, 506, 514, 522, 530,
|
||||
536, 544, 552, 808, 866, 882 }, new String[] {
|
||||
"sub_type", "msg_title", "msg_describe", "msg_additional", "msg_source", "msg_decided", "src_id", "sub_src_id", "actions", "group_code",
|
||||
"action_uin", "group_msg_type", "group_inviter_role", "friend_info", "group_info", "actor_uin", "msg_actor_describe", "msg_additional_list", "relation", "reqsubtype",
|
||||
"clone_uin", "uint64_discuss_uin", "uint64_eim_group_id", "msg_invite_extinfo", "msg_pay_group_extinfo", "uint32_source_flag", "bytes_game_nick", "bytes_game_msg", "uint32_group_flagext3", "uint64_group_owner_uin",
|
||||
"uint32_doubt_flag", "bytes_warning_tips", "bytes_name_more", "bytes_addtion", "bytes_transparent_group_notify", "req_uin_faceid", "req_uin_nick", "group_name", "action_uin_nick", "msg_qna",
|
||||
"msg_detail", "group_ext_flag", "actor_uin_nick", "pic_url", "clone_uin_nick", "req_uin_business_card", "eim_group_id_name", "req_uin_pre_remark", "action_uin_qq_nick", "action_uin_remark",
|
||||
"req_uin_gender", "req_uin_age", "uint32_c2c_invite_join_group_flag", "card_switch", "bytes_source_desc", "uid" }, new Object[] {
|
||||
integer, "", "", "", "", "", integer, integer, null, long_,
|
||||
long_, integer, integer, null, null, long_, "", "", integer, integer,
|
||||
long_, long_, long_, null, null, integer, byteStringMicro, byteStringMicro, integer, long_,
|
||||
integer, byteStringMicro, byteStringMicro, byteStringMicro, byteStringMicro, integer, "", "", "", "",
|
||||
"", integer, "", byteStringMicro, "", byteStringMicro, byteStringMicro, byteStringMicro, byteStringMicro, byteStringMicro,
|
||||
integer, integer, integer, integer, byteStringMicro, "" }, structmsg$SystemMsg.class);
|
||||
}
|
||||
|
||||
public structmsg$SystemMsg() {
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
this.bytes_game_nick = PBField.initBytes(byteStringMicro);
|
||||
this.bytes_game_msg = PBField.initBytes(byteStringMicro);
|
||||
this.uint32_group_flagext3 = PBField.initUInt32(0);
|
||||
this.uint64_group_owner_uin = PBField.initUInt64(0L);
|
||||
this.uint32_doubt_flag = PBField.initUInt32(0);
|
||||
this.bytes_warning_tips = PBField.initBytes(byteStringMicro);
|
||||
this.bytes_name_more = PBField.initBytes(byteStringMicro);
|
||||
this.bytes_addtion = PBField.initBytes(byteStringMicro);
|
||||
this.bytes_transparent_group_notify = PBField.initBytes(byteStringMicro);
|
||||
this.req_uin_faceid = PBField.initInt32(0);
|
||||
this.req_uin_nick = PBField.initString("");
|
||||
this.group_name = PBField.initString("");
|
||||
this.action_uin_nick = PBField.initString("");
|
||||
this.msg_qna = PBField.initString("");
|
||||
this.msg_detail = PBField.initString("");
|
||||
this.group_ext_flag = PBField.initUInt32(0);
|
||||
this.actor_uin_nick = PBField.initString("");
|
||||
this.pic_url = PBField.initBytes(byteStringMicro);
|
||||
this.clone_uin_nick = PBField.initString("");
|
||||
this.req_uin_business_card = PBField.initBytes(byteStringMicro);
|
||||
this.eim_group_id_name = PBField.initBytes(byteStringMicro);
|
||||
this.req_uin_pre_remark = PBField.initBytes(byteStringMicro);
|
||||
this.action_uin_qq_nick = PBField.initBytes(byteStringMicro);
|
||||
this.action_uin_remark = PBField.initBytes(byteStringMicro);
|
||||
this.req_uin_gender = PBField.initUInt32(0);
|
||||
this.req_uin_age = PBField.initUInt32(0);
|
||||
this.uint32_c2c_invite_join_group_flag = PBField.initUInt32(0);
|
||||
this.card_switch = PBField.initUInt32(0);
|
||||
this.bytes_source_desc = PBField.initBytes(byteStringMicro);
|
||||
this.uid = PBField.initString("");
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBStringField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
|
||||
public final class structmsg$SystemMsgAction extends MessageMicro<structmsg$SystemMsgAction> {
|
||||
static final FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 24, 34, 42 }, new String[] { "name", "result", "action", "action_info", "detail_name" }, new Object[] { "", "", Integer.valueOf(0), null, "" }, structmsg$SystemMsgAction.class);
|
||||
|
||||
public final PBUInt32Field action = PBField.initUInt32(0);
|
||||
|
||||
public structmsg$SystemMsgActionInfo action_info = new structmsg$SystemMsgActionInfo();
|
||||
|
||||
public final PBStringField detail_name = PBField.initString("");
|
||||
|
||||
public final PBStringField name = PBField.initString("");
|
||||
|
||||
public final PBStringField result = PBField.initString("");
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package tencent.mobileim.structmsg;
|
||||
|
||||
import com.tencent.mobileqq.pb.ByteStringMicro;
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBBoolField;
|
||||
import com.tencent.mobileqq.pb.PBBytesField;
|
||||
import com.tencent.mobileqq.pb.PBEnumField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBStringField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public final class structmsg$SystemMsgActionInfo extends MessageMicro<structmsg$SystemMsgActionInfo> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public structmsg$AddFrdSNInfo addFrdSNInfo = new structmsg$AddFrdSNInfo();
|
||||
|
||||
public final PBBoolField blacklist = PBField.initBool(false);
|
||||
|
||||
public final PBUInt64Field group_code = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt32Field group_id = PBField.initUInt32(0);
|
||||
|
||||
public final PBStringField msg = PBField.initString("");
|
||||
|
||||
public final PBStringField remark = PBField.initString("");
|
||||
|
||||
public final PBBytesField sig = PBField.initBytes(ByteStringMicro.EMPTY);
|
||||
|
||||
public final PBEnumField type = PBField.initEnum(1);
|
||||
|
||||
public final PBUInt32Field uint32_req_msg_type = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
Boolean bool = Boolean.FALSE;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 26, 402, 408, 418, 424, 434, 440 }, new String[] { "type", "group_code", "sig", "msg", "group_id", "remark", "blacklist", "addFrdSNInfo", "uint32_req_msg_type" }, new Object[] { Integer.valueOf(1), Long.valueOf(0L), byteStringMicro, "", integer, "", bool, null, integer }, structmsg$SystemMsgActionInfo.class);
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ plugins {
|
||||
id("org.jetbrains.kotlin.android")
|
||||
//id("io.realm.kotlin")
|
||||
id("kotlin-kapt")
|
||||
kotlin("plugin.serialization") version "1.8.0"
|
||||
kotlin("plugin.serialization") version "1.8.10"
|
||||
}
|
||||
|
||||
android {
|
||||
@ -13,7 +13,6 @@ android {
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
@ -50,6 +49,9 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly ("de.robv.android.xposed:api:82")
|
||||
compileOnly (project(":qqinterface"))
|
||||
|
||||
implementation("androidx.core:core-ktx:1.9.0")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("com.google.android.material:material:1.9.0")
|
||||
@ -59,7 +61,8 @@ dependencies {
|
||||
//implementation("io.realm.kotlin:library-sync:1.11.0")
|
||||
|
||||
val ktorVersion = "2.3.3"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||
//implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||
implementation("io.github.xn32:json5k:0.3.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.1.16")
|
||||
implementation("io.ktor:ktor-server-core:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-host-common:$ktorVersion")
|
||||
@ -71,6 +74,7 @@ dependencies {
|
||||
implementation("io.ktor:ktor-client-cio:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
|
||||
//implementation("io.ktor:ktor-serialization-kotlinx-protobuf:$ktorVersion")
|
||||
implementation("io.ktor:ktor-network-tls-certificates:$ktorVersion")
|
||||
|
||||
/**
|
||||
@ -93,11 +97,10 @@ dependencies {
|
||||
// optional - Kotlin Extensions and Coroutines support for Room
|
||||
implementation("androidx.room:room-ktx:$roomVersion")
|
||||
|
||||
compileOnly ("de.robv.android.xposed:api:82")
|
||||
compileOnly (project(":qqinterface"))
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2023.06.01"))
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||
}
|
||||
|
||||
|
1
xposed/src/main/assets/native_init
Normal file
1
xposed/src/main/assets/native_init
Normal file
@ -0,0 +1 @@
|
||||
libclover.so
|
@ -1,38 +1,17 @@
|
||||
|
||||
# For more information about using CMake with Android Studio, read the
|
||||
# documentation: https://d.android.com/studio/projects/add-native-code.html.
|
||||
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
|
||||
|
||||
# Sets the minimum CMake version required for this project.
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
|
||||
# Since this is the top level CMakeLists.txt, the project name is also accessible
|
||||
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
|
||||
# build script scope).
|
||||
project("xposed")
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
project("clover")
|
||||
|
||||
include_directories(helper)
|
||||
|
||||
# Creates and names a library, sets it as either STATIC
|
||||
# or SHARED, and provides the relative paths to its source code.
|
||||
# You can define multiple libraries, and CMake builds them for you.
|
||||
# Gradle automatically packages shared libraries with your APK.
|
||||
#
|
||||
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
|
||||
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
|
||||
# is preferred for the same purpose.
|
||||
#
|
||||
# In order to load a library into your app from Java/Kotlin, you must call
|
||||
# System.loadLibrary() and pass the name of the library defined here;
|
||||
# for GameActivity/NativeActivity derived applications, the same library name must be
|
||||
# used in the AndroidManifest.xml file.
|
||||
add_library(${CMAKE_PROJECT_NAME} SHARED
|
||||
# List C/C++ source files with relative paths to this CMakeLists.txt.
|
||||
xposed.cpp)
|
||||
anti_detection/anti_detection.cpp
|
||||
helper/jnihelper.cpp
|
||||
clover.cpp)
|
||||
|
||||
# Specifies libraries CMake should link to your target library. You
|
||||
# can link libraries from various origins, such as libraries defined in this
|
||||
# build script, prebuilt third-party libraries, or Android system libraries.
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||
# List libraries link to the target library
|
||||
android
|
||||
log)
|
||||
|
6
xposed/src/main/cpp/anti_detection/anti_detection.cpp
Normal file
6
xposed/src/main/cpp/anti_detection/anti_detection.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "anti_detection.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
24
xposed/src/main/cpp/anti_detection/anti_detection.h
Normal file
24
xposed/src/main/cpp/anti_detection/anti_detection.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef SHAMROCK_ANTI_DETECTION_H
|
||||
#define SHAMROCK_ANTI_DETECTION_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <initializer_list>
|
||||
#include "lsposed.h"
|
||||
#include "jnihelper.h"
|
||||
|
||||
static std::vector<std::string> qemu_detect_props = {
|
||||
"init.svc.qemu-props", "qemu.hw.mainkeys", "qemu.sf.fake_camera", "ro.kernel.android.qemud",
|
||||
"qemu.sf.lcd_density", "init.svc.qemud", "ro.kernel.qemu",
|
||||
"libc.debug.malloc"
|
||||
};
|
||||
|
||||
static int (*backup_system_property_get)(const char *name, char *value);
|
||||
static FILE* (*backup_fopen)(const char *filename, const char *mode);
|
||||
|
||||
//int fake_system_property_get(const char *name, char *value);
|
||||
//FILE* fake_fopen(const char *filename, const char *mode);
|
||||
|
||||
//void on_library_loaded(const char *name, void *handle);
|
||||
|
||||
#endif //SHAMROCK_ANTI_DETECTION_H
|
123
xposed/src/main/cpp/clover.cpp
Normal file
123
xposed/src/main/cpp/clover.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include <jni.h>
|
||||
#include "anti_detection/anti_detection.h"
|
||||
#include "helper/lsposed.h"
|
||||
#include "jnihelper.h"
|
||||
|
||||
static JavaVM *global_jvm = nullptr;
|
||||
static HookFunType hook_function = nullptr;
|
||||
|
||||
extern "C" [[gnu::visibility("default")]] [[gnu::used]]
|
||||
jint JNI_OnLoad(JavaVM *jvm, void*) {
|
||||
global_jvm = jvm;
|
||||
int attach = 0;
|
||||
JNIEnv *env = JNIHelper::getJNIEnv(jvm, &attach);
|
||||
|
||||
// do something
|
||||
LOGI("[Shamrock] JNI_OnLoad NativeModule Init: %p", env);
|
||||
|
||||
if (attach == 1) {
|
||||
JNIHelper::delJNIEnv(jvm);
|
||||
}
|
||||
|
||||
//hook_function((void *)env->functions->FindClass, (void *)fake_FindClass, (void **)&backup_FindClass);
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_moe_fuqiuluo_shamrock_xposed_XposedEntry_00024Companion_injected(JNIEnv *env, jobject thiz) {
|
||||
LOGI("[Shamrock] injected: %p", hook_function);
|
||||
return hook_function != nullptr;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_moe_fuqiuluo_shamrock_xposed_XposedEntry_00024Companion_hasEnv(JNIEnv *env, jobject thiz) {
|
||||
LOGI("[Shamrock] hasEnv: %p", global_jvm);
|
||||
return global_jvm != nullptr;
|
||||
}
|
||||
|
||||
int fake_system_property_get(const char *name, char *value) {
|
||||
for (auto &prop: qemu_detect_props) {
|
||||
if (strstr(name, prop.c_str())) {
|
||||
LOGI("[Shamrock] bypass qemu detection");
|
||||
value[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (strstr(name, "ro.debuggable")
|
||||
|| strstr(name, "ro.kernel.qemu.gles")
|
||||
|| strstr(name, "debug.atrace.tags.enableflags")) {
|
||||
strcpy(value, "0");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strstr(name, "ro.product.cpu.abilist")) {
|
||||
int len = backup_system_property_get(name, value);
|
||||
if (len > 0) {
|
||||
if (strstr(value, "x86")) {
|
||||
strcpy(value, "arm64-v8a,armeabi-v7a,armeabi");
|
||||
return 29;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
if (strstr(name, "ro.hardware")) {
|
||||
int len = backup_system_property_get(name, value);
|
||||
if (len > 0) {
|
||||
if (strstr(value, "generic")
|
||||
|| strstr(value, "unknown")
|
||||
|| strstr(value, "emulator")
|
||||
|| strstr(value, "vbox")
|
||||
|| strstr(value, "genymotion")
|
||||
|| strstr(value, "goldfish")) {
|
||||
strcpy(value, "qcom");
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
//LOGI("[Shamrock] fake_system_property_get(%s)", name);
|
||||
return backup_system_property_get(name, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
FILE* fake_fopen(const char *filename, const char *mode) {
|
||||
if (strstr(filename, "qemu_pipe")) {
|
||||
LOGI("[Shamrock] bypass qemu detection");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (strstr(filename, "libhoudini.so")) {
|
||||
LOGI("[Shamrock] bypass emu detection");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return backup_fopen(filename, mode);
|
||||
}
|
||||
|
||||
void on_library_loaded(const char *name, void *handle) {
|
||||
|
||||
}
|
||||
|
||||
extern "C" [[gnu::visibility("default")]] [[gnu::used]]
|
||||
NativeOnModuleLoaded native_init(const NativeAPIEntries *entries) {
|
||||
hook_function = entries->hook_func;
|
||||
LOGI("[Shamrock] LSPosed NativeModule Init: %p", hook_function);
|
||||
|
||||
return on_library_loaded;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_moe_fuqiuluo_shamrock_xposed_actions_AntiDetection_antiNativeDetections(JNIEnv *env,
|
||||
jobject thiz) {
|
||||
if (hook_function == nullptr) return false;
|
||||
hook_function((void*) __system_property_get, (void *)fake_system_property_get, (void **) &backup_system_property_get);
|
||||
hook_function((void*) fopen, (void*) fake_fopen, (void**) &backup_fopen);
|
||||
return true;
|
||||
}
|
26
xposed/src/main/cpp/helper/jnihelper.cpp
Normal file
26
xposed/src/main/cpp/helper/jnihelper.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "jnihelper.h"
|
||||
|
||||
JNIEnv *JNIHelper::getJNIEnv(JavaVM * jvm, int *attach) {
|
||||
if (jvm == NULL) return NULL;
|
||||
|
||||
*attach = 0;
|
||||
JNIEnv *jni_env = NULL;
|
||||
|
||||
int status = jvm->GetEnv((void **)&jni_env, JNI_VERSION_1_6);
|
||||
|
||||
if (status == JNI_EDETACHED || jni_env == NULL) {
|
||||
status = jvm->AttachCurrentThread(&jni_env, NULL);
|
||||
if (status < 0) {
|
||||
jni_env = NULL;
|
||||
} else {
|
||||
*attach = 1;
|
||||
}
|
||||
}
|
||||
return jni_env;
|
||||
}
|
||||
|
||||
jint JNIHelper::delJNIEnv(JavaVM * jvm) {
|
||||
if (jvm == nullptr) return 0;
|
||||
return jvm->DetachCurrentThread();
|
||||
}
|
||||
|
14
xposed/src/main/cpp/helper/jnihelper.h
Normal file
14
xposed/src/main/cpp/helper/jnihelper.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef SHAMROCK_JNIHELPER_H
|
||||
#define SHAMROCK_JNIHELPER_H
|
||||
|
||||
#include "jni.h"
|
||||
#include "android/log.h"
|
||||
|
||||
namespace JNIHelper {
|
||||
JNIEnv *getJNIEnv(JavaVM * jvm, int *attach);
|
||||
|
||||
jint delJNIEnv(JavaVM * jvm);
|
||||
}
|
||||
|
||||
|
||||
#endif //SHAMROCK_JNIHELPER_H
|
27
xposed/src/main/cpp/helper/lsposed.h
Normal file
27
xposed/src/main/cpp/helper/lsposed.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef SHAMROCK_LSPOSED_H
|
||||
#define SHAMROCK_LSPOSED_H
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#define TAG "LSPosed-Bridge"
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
|
||||
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)
|
||||
|
||||
typedef int (*HookFunType)(void *func, void *replace, void **backup);
|
||||
|
||||
typedef int (*UnhookFunType)(void *func);
|
||||
|
||||
typedef void (*NativeOnModuleLoaded)(const char *name, void *handle);
|
||||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
HookFunType hook_func;
|
||||
UnhookFunType unhook_func;
|
||||
} NativeAPIEntries;
|
||||
|
||||
typedef NativeOnModuleLoaded (*NativeInit)(const NativeAPIEntries *entries);
|
||||
|
||||
#endif //SHAMROCK_LSPOSED_H
|
@ -1,5 +0,0 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <sys/auxv.h>
|
||||
|
@ -50,7 +50,7 @@ val ProtoValue.asLong: Long
|
||||
get() = (this as ProtoNumber).value.toLong()
|
||||
|
||||
val ProtoValue.asULong: Long
|
||||
get() = (this as ProtoNumber).value.toLong() and 0xFFFFFFFFL
|
||||
get() = (this as ProtoNumber).value.toLong() and Long.MAX_VALUE
|
||||
|
||||
val ProtoValue.asMap: ProtoMap
|
||||
get() = (this as ProtoMap)
|
||||
|
@ -0,0 +1,5 @@
|
||||
@file:OptIn(ExperimentalSerializationApi::class)
|
||||
package moe.fuqiuluo.qqinterface.entries
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
@ -11,6 +11,8 @@ import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import moe.fuqiuluo.proto.protobufOf
|
||||
import moe.fuqiuluo.shamrock.utils.PlatformUtils
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
||||
@ -35,45 +37,41 @@ internal abstract class BaseSvc {
|
||||
return ToServiceMsg("mobileqq.service", app.currentAccountUin, cmd)
|
||||
}
|
||||
|
||||
suspend fun sendOidbAW(cmd: String, cmdId: Int, serviceId: Int, data: ByteArray, trpc: Boolean = false): ByteArray? {
|
||||
return suspendCoroutine { continuation ->
|
||||
val seq = MsfCore.getNextSeq()
|
||||
val timer = timer(initialDelay = 5000L, period = 5000L) {
|
||||
suspend fun sendOidbAW(cmd: String, cmdId: Int, serviceId: Int, data: ByteArray, trpc: Boolean = false, timeout: Long = 5000L): ByteArray? {
|
||||
val seq = MsfCore.getNextSeq()
|
||||
return withTimeoutOrNull(timeout) {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
PacketHandler.unregisterLessHandler(seq)
|
||||
continuation.resume(null)
|
||||
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)
|
||||
}
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
DynamicReceiver.register(IPCRequest(cmd, seq) {
|
||||
val buffer = it.getByteArrayExtra("buffer")!!
|
||||
timer.cancel()
|
||||
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()
|
||||
}
|
||||
|
||||
suspend fun sendBufferAW(cmd: String, isPb: Boolean, data: ByteArray): ByteArray? {
|
||||
return suspendCoroutine { continuation ->
|
||||
val seq = MsfCore.getNextSeq()
|
||||
val timer = timer(initialDelay = 5000L, period = 5000L) {
|
||||
suspend fun sendBufferAW(cmd: String, isPb: Boolean, data: ByteArray, timeout: Long = 5000L): ByteArray? {
|
||||
val seq = MsfCore.getNextSeq()
|
||||
return withTimeoutOrNull<ByteArray?>(timeout) {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
PacketHandler.unregisterLessHandler(seq)
|
||||
continuation.resume(null)
|
||||
DynamicReceiver.register(IPCRequest(cmd, seq) {
|
||||
val buffer = it.getByteArrayExtra("buffer")!!
|
||||
continuation.resume(buffer)
|
||||
})
|
||||
sendBuffer(cmd, isPb, data, seq)
|
||||
}
|
||||
}
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
DynamicReceiver.register(IPCRequest(cmd, seq) {
|
||||
val buffer = it.getByteArrayExtra("buffer")!!
|
||||
timer.cancel()
|
||||
continuation.resume(buffer)
|
||||
})
|
||||
sendBuffer(cmd, isPb, data, seq)
|
||||
}
|
||||
}
|
||||
}.also {
|
||||
if (it == null)
|
||||
DynamicReceiver.unregister(seq)
|
||||
}?.copyOf()
|
||||
}
|
||||
|
||||
fun sendOidb(cmd: String, cmdId: Int, serviceId: Int, buffer: ByteArray, seq: Int = -1, trpc: Boolean = false) {
|
||||
@ -110,7 +108,7 @@ internal abstract class BaseSvc {
|
||||
app.sendToService(to)
|
||||
}
|
||||
|
||||
fun sendBuffer(cmd: String, isPb: Boolean, buffer: ByteArray, seq: Int) {
|
||||
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)
|
||||
|
@ -1,12 +1,15 @@
|
||||
package moe.fuqiuluo.qqinterface.servlet
|
||||
|
||||
import com.tencent.mobileqq.pb.ByteStringMicro
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import moe.fuqiuluo.proto.protobufOf
|
||||
import moe.fuqiuluo.qqinterface.servlet.entries.*
|
||||
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
|
||||
import moe.fuqiuluo.shamrock.helper.Level
|
||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
|
||||
import moe.fuqiuluo.shamrock.tools.slice
|
||||
import moe.fuqiuluo.shamrock.tools.toHexString
|
||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||
import tencent.im.oidb.cmd0x6d8.oidb_0x6d8
|
||||
import tencent.im.oidb.oidb_sso
|
||||
|
||||
@ -33,8 +36,23 @@ internal object FileSvc: BaseSvc() {
|
||||
}
|
||||
|
||||
fun deleteGroupFile(groupId: String, bizId: Int, fileUid: String) {
|
||||
/*
|
||||
val kernelService = NTServiceFetcher.kernelService
|
||||
val sessionService = kernelService.wrapperSession
|
||||
val richMediaService = sessionService.richMediaService
|
||||
|
||||
val result = withTimeoutOrNull(3000L) {
|
||||
suspendCancellableCoroutine {
|
||||
richMediaService.deleteGroupFile(groupId.toLong(), fileUid, bizId) { code, _, result ->
|
||||
it.resume(code to result.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (result == null) Result.failure(RuntimeException("delete group file timeout")) else Result.success(result)*/
|
||||
// 调用QQ内部实现会导致闪退!
|
||||
sendOidb("OidbSvc.0x6d6_3", 1750, 3, protobufOf(
|
||||
4 to mapOf(
|
||||
4 to mapOf(
|
||||
1 to groupId.toLong(),
|
||||
2 to 3,
|
||||
3 to bizId,
|
||||
@ -92,7 +110,7 @@ internal object FileSvc: BaseSvc() {
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getGroupRootFiles(groupId: Long): GroupFileList {
|
||||
suspend fun getGroupRootFiles(groupId: Long): Result<GroupFileList> {
|
||||
return getGroupFiles(groupId, "/")
|
||||
}
|
||||
|
||||
@ -100,7 +118,7 @@ internal object FileSvc: BaseSvc() {
|
||||
return FileUrl(RichProtoSvc.getGroupFileDownUrl(groupId, fileId, busid))
|
||||
}
|
||||
|
||||
suspend fun getGroupFiles(groupId: Long, folderId: String): GroupFileList {
|
||||
suspend fun getGroupFiles(groupId: Long, folderId: String): Result<GroupFileList> {
|
||||
val fileSystemInfo = getGroupFileSystemInfo(groupId)
|
||||
val rspGetFileListBuffer = sendOidbAW("OidbSvc.0x6d8_1", 1752, 1, oidb_0x6d8.ReqBody().also {
|
||||
it.file_list_info_req.set(oidb_0x6d8.GetFileListReqBody().apply {
|
||||
@ -122,17 +140,20 @@ internal object FileSvc: BaseSvc() {
|
||||
|
||||
uint32_show_onlinedoc_folder.set(0)
|
||||
})
|
||||
}.toByteArray())
|
||||
}.toByteArray(), timeout = 15_000L)
|
||||
|
||||
val files = arrayListOf<FileInfo>()
|
||||
val dirs = arrayListOf<FolderInfo>()
|
||||
if (rspGetFileListBuffer != null) {
|
||||
oidb_0x6d8.RspBody().mergeFrom(oidb_sso.OIDBSSOPkg()
|
||||
.mergeFrom(rspGetFileListBuffer.slice(4))
|
||||
.bytes_bodybuffer.get()
|
||||
.toByteArray()).file_list_info_rsp.apply {
|
||||
return kotlin.runCatching {
|
||||
val files = arrayListOf<FileInfo>()
|
||||
val dirs = arrayListOf<FolderInfo>()
|
||||
if (rspGetFileListBuffer != null) {
|
||||
val oidb = oidb_sso.OIDBSSOPkg().mergeFrom(rspGetFileListBuffer.slice(4).let {
|
||||
if (it[0] == 0x78.toByte()) DeflateTools.uncompress(it) else it
|
||||
})
|
||||
|
||||
oidb_0x6d8.RspBody().mergeFrom(oidb.bytes_bodybuffer.get().toByteArray())
|
||||
.file_list_info_rsp.apply {
|
||||
rpt_item_list.get().forEach { file ->
|
||||
if (file.uint32_type.get() == 1) {
|
||||
if (file.uint32_type.get() == oidb_0x6d8.GetFileListRspBody.TYPE_FILE) {
|
||||
val fileInfo = file.file_info
|
||||
files.add(FileInfo(
|
||||
groupId = groupId,
|
||||
@ -145,9 +166,14 @@ internal object FileSvc: BaseSvc() {
|
||||
modifyTime = fileInfo.uint32_modify_time.get(),
|
||||
downloadTimes = fileInfo.uint32_download_times.get(),
|
||||
uploadUin = fileInfo.uint64_uploader_uin.get(),
|
||||
uploadNick = fileInfo.str_uploader_name.get()
|
||||
uploadNick = fileInfo.str_uploader_name.get(),
|
||||
md5 = fileInfo.bytes_md5.get().toByteArray().toHexString(),
|
||||
sha = fileInfo.bytes_sha.get().toByteArray().toHexString(),
|
||||
// 根本没有
|
||||
sha3 = fileInfo.bytes_sha3.get().toByteArray().toHexString(),
|
||||
))
|
||||
} else if (file.uint32_type.get() == 2) {
|
||||
}
|
||||
else if (file.uint32_type.get() == oidb_0x6d8.GetFileListRspBody.TYPE_FOLDER) {
|
||||
val folderInfo = file.folder_info
|
||||
dirs.add(FolderInfo(
|
||||
groupId = groupId,
|
||||
@ -158,58 +184,18 @@ internal object FileSvc: BaseSvc() {
|
||||
creator = folderInfo.uint64_create_uin.get(),
|
||||
creatorNick = folderInfo.str_creator_name.get()
|
||||
))
|
||||
} else {
|
||||
LogCenter.log("未知文件类型: ${file.uint32_type.get()}", Level.WARN)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw RuntimeException("获取群文件列表失败")
|
||||
}
|
||||
} else {
|
||||
throw RuntimeException("获取群文件列表失败")
|
||||
|
||||
GroupFileList(files, dirs)
|
||||
}.onFailure {
|
||||
LogCenter.log(it.message + ", buffer: ${rspGetFileListBuffer.toHexString()}", Level.ERROR)
|
||||
}
|
||||
|
||||
return GroupFileList(files, dirs)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class FileUrl(
|
||||
@SerialName("url") val url: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GroupFileList(
|
||||
@SerialName("files") val files: List<FileInfo>,
|
||||
@SerialName("folders") val folders: List<FolderInfo>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FileInfo(
|
||||
@SerialName("group_id") val groupId: Long,
|
||||
@SerialName("file_id") val fileId: String,
|
||||
@SerialName("file_name") val fileName: String,
|
||||
@SerialName("file_size") val fileSize: Long,
|
||||
@SerialName("busid") val busid: Int,
|
||||
@SerialName("upload_time") val uploadTime: Int,
|
||||
@SerialName("dead_time") val deadTime: Int,
|
||||
@SerialName("modify_time") val modifyTime: Int,
|
||||
@SerialName("download_times") val downloadTimes: Int,
|
||||
@SerialName("uploader") val uploadUin: Long,
|
||||
@SerialName("upload_name") val uploadNick: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FolderInfo(
|
||||
@SerialName("group_id") val groupId: Long,
|
||||
@SerialName("folder_id") val folderId: String,
|
||||
@SerialName("folder_name") val folderName: String,
|
||||
@SerialName("total_file_count") val totalFileCount: Int,
|
||||
@SerialName("create_time") val createTime: Int,
|
||||
@SerialName("creator") val creator: Long,
|
||||
@SerialName("creator_name") val creatorNick: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FileSystemInfo(
|
||||
@SerialName("file_count") val fileCount: Int,
|
||||
@SerialName("limit_count") val fileLimitCount: Int,
|
||||
@SerialName("used_space") val usedSpace: Long,
|
||||
@SerialName("total_space") val totalSpace: Long,
|
||||
)
|
||||
}
|
@ -1,19 +1,28 @@
|
||||
@file:OptIn(DelicateCoroutinesApi::class)
|
||||
@file:Suppress("IllegalIdentifier")
|
||||
package moe.fuqiuluo.qqinterface.servlet
|
||||
|
||||
import com.tencent.common.app.AppInterface
|
||||
import com.tencent.mobileqq.data.Friends
|
||||
import com.tencent.mobileqq.friend.api.IFriendDataService
|
||||
import com.tencent.mobileqq.friend.api.IFriendHandlerService
|
||||
import com.tencent.mobileqq.qroute.QRoute
|
||||
import com.tencent.mobileqq.relation.api.IAddFriendTempApi
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
||||
import moe.fuqiuluo.shamrock.tools.slice
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
||||
import mqq.app.AppRuntime
|
||||
import mqq.app.MobileQQ
|
||||
import tencent.mobileim.structmsg.`structmsg$FlagInfo`
|
||||
import tencent.mobileim.structmsg.`structmsg$ReqSystemMsgNew`
|
||||
import tencent.mobileim.structmsg.`structmsg$RspSystemMsgNew`
|
||||
import kotlin.coroutines.resume
|
||||
import tencent.mobileim.structmsg.`structmsg$SystemMsgActionInfo` as ActionInfo
|
||||
import tencent.mobileim.structmsg.`structmsg$AddFrdSNInfo` as AddFrdSNInfo
|
||||
import tencent.mobileim.structmsg.`structmsg$StructMsg` as StructMsg
|
||||
|
||||
internal object FriendSvc: BaseSvc() {
|
||||
|
||||
@ -28,6 +37,75 @@ internal object FriendSvc: BaseSvc() {
|
||||
return Result.success(service.allFriends)
|
||||
}
|
||||
|
||||
// ProfileService.Pb.ReqSystemMsgAction.Friend
|
||||
fun requestFriendRequest(msgSeq: Long, uin: Long, remark: String = "", approve: Boolean? = true, notSee: Boolean? = false) {
|
||||
val app = AppRuntimeFetcher.appRuntime
|
||||
if (app !is AppInterface)
|
||||
throw RuntimeException("AppRuntime cannot cast to AppInterface")
|
||||
val service = QRoute.api(IAddFriendTempApi::class.java)
|
||||
val action = ActionInfo()
|
||||
action.type.set(if (approve != false) 2 else 3)
|
||||
action.group_id.set(0)
|
||||
action.remark.set(remark)
|
||||
val snInfo = AddFrdSNInfo()
|
||||
snInfo.uint32_not_see_dynamic.set(if (notSee != false) 1 else 0)
|
||||
snInfo.uint32_set_sn.set(0)
|
||||
action.addFrdSNInfo.set(snInfo)
|
||||
service.sendFriendSystemMsgAction(1, msgSeq, uin, 1, 2004, 11, 0, action, 0, StructMsg(), false,
|
||||
app
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun requestFriendSystemMsgNew(msgNum: Int, latestFriendSeq: Long = 0, latestGroupSeq: Long = 0, retryCnt: Int = 3): List<StructMsg>? {
|
||||
if (retryCnt < 0) {
|
||||
return ArrayList()
|
||||
}
|
||||
val req = `structmsg$ReqSystemMsgNew`()
|
||||
req.msg_num.set(msgNum)
|
||||
req.latest_friend_seq.set(latestFriendSeq)
|
||||
req.latest_group_seq.set(latestGroupSeq)
|
||||
req.version.set(1000)
|
||||
req.checktype.set(2)
|
||||
val flag = `structmsg$FlagInfo`()
|
||||
// flag.GrpMsg_Kick_Admin.set(1)
|
||||
// flag.GrpMsg_HiddenGrp.set(1)
|
||||
// flag.GrpMsg_WordingDown.set(1)
|
||||
flag.FrdMsg_GetBusiCard.set(1)
|
||||
// flag.GrpMsg_GetOfficialAccount.set(1)
|
||||
// flag.GrpMsg_GetPayInGroup.set(1)
|
||||
flag.FrdMsg_Discuss2ManyChat.set(1)
|
||||
// flag.GrpMsg_NotAllowJoinGrp_InviteNotFrd.set(1)
|
||||
flag.FrdMsg_NeedWaitingMsg.set(1)
|
||||
flag.FrdMsg_uint32_need_all_unread_msg.set(1)
|
||||
// flag.GrpMsg_NeedAutoAdminWording.set(1)
|
||||
// flag.GrpMsg_get_transfer_group_msg_flag.set(1)
|
||||
// flag.GrpMsg_get_quit_pay_group_msg_flag.set(1)
|
||||
// flag.GrpMsg_support_invite_auto_join.set(1)
|
||||
// flag.GrpMsg_mask_invite_auto_join.set(1)
|
||||
// flag.GrpMsg_GetDisbandedByAdmin.set(1)
|
||||
flag.GrpMsg_GetC2cInviteJoinGroup.set(1)
|
||||
req.flag.set(flag)
|
||||
req.is_get_frd_ribbon.set(false)
|
||||
req.is_get_grp_ribbon.set(false)
|
||||
req.friend_msg_type_flag.set(1)
|
||||
req.uint32_req_msg_type.set(1)
|
||||
req.uint32_need_uid.set(1)
|
||||
val respBuffer = sendBufferAW("ProfileService.Pb.ReqSystemMsgNew.Friend", true, req.toByteArray())
|
||||
return if (respBuffer == null) {
|
||||
ArrayList()
|
||||
} else {
|
||||
try {
|
||||
val msg = `structmsg$RspSystemMsgNew`()
|
||||
msg.mergeFrom(respBuffer.slice(4))
|
||||
return msg.friendmsgs.get()
|
||||
} catch (err: Throwable) {
|
||||
requestFriendSystemMsgNew(msgNum, latestFriendSeq, latestGroupSeq, retryCnt - 1)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private suspend fun requestFriendList(runtime: AppRuntime, dataService: IFriendDataService): Boolean {
|
||||
val service = runtime.getRuntimeService(IFriendHandlerService::class.java, "all")
|
||||
service.requestFriendList(true, 0)
|
||||
|
@ -13,7 +13,22 @@ import com.tencent.mobileqq.troop.api.ITroopMemberInfoService
|
||||
import com.tencent.protofile.join_group_link.join_group_link
|
||||
import com.tencent.qphone.base.remote.ToServiceMsg
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MemberInfo
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||
import friendlist.stUinInfo
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.forms.MultiPartFormDataContent
|
||||
import io.ktor.client.request.forms.formData
|
||||
import io.ktor.client.request.forms.submitForm
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.setBody
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.Headers
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.http.contentType
|
||||
import io.ktor.http.headers
|
||||
import io.ktor.http.parameters
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
@ -22,21 +37,49 @@ import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import moe.fuqiuluo.proto.ProtoUtils
|
||||
import moe.fuqiuluo.proto.asInt
|
||||
import moe.fuqiuluo.proto.asUtf8String
|
||||
import moe.fuqiuluo.proto.protobufOf
|
||||
import moe.fuqiuluo.qqinterface.servlet.entries.ProhibitedMemberInfo
|
||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||
import moe.fuqiuluo.shamrock.remote.service.data.EssenceMessage
|
||||
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncement
|
||||
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessage
|
||||
import moe.fuqiuluo.shamrock.remote.service.data.GroupAnnouncementMessageImage
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonArray
|
||||
import moe.fuqiuluo.shamrock.tools.GlobalClient
|
||||
import moe.fuqiuluo.shamrock.tools.asInt
|
||||
import moe.fuqiuluo.shamrock.tools.asJsonArrayOrNull
|
||||
import moe.fuqiuluo.shamrock.tools.asJsonObject
|
||||
import moe.fuqiuluo.shamrock.tools.asLong
|
||||
import moe.fuqiuluo.shamrock.tools.asString
|
||||
import moe.fuqiuluo.shamrock.tools.asStringOrNull
|
||||
import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty
|
||||
import moe.fuqiuluo.shamrock.tools.putBuf32Long
|
||||
import moe.fuqiuluo.shamrock.tools.slice
|
||||
import moe.fuqiuluo.shamrock.utils.FileUtils
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
|
||||
import mqq.app.MobileQQ
|
||||
import tencent.im.oidb.cmd0x899.oidb_0x899
|
||||
import tencent.im.oidb.cmd0x89a.oidb_0x89a
|
||||
import tencent.im.oidb.cmd0x8a0.oidb_0x8a0
|
||||
import tencent.im.oidb.cmd0x8fc.Oidb_0x8fc
|
||||
import tencent.im.oidb.oidb_sso
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Modifier
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
import tencent.mobileim.structmsg.`structmsg$ReqSystemMsgNew` as ReqSystemMsgNew
|
||||
import tencent.mobileim.structmsg.`structmsg$FlagInfo` as FlagInfo
|
||||
import tencent.mobileim.structmsg.`structmsg$RspSystemMsgNew` as RspSystemMsgNew
|
||||
import tencent.mobileim.structmsg.`structmsg$StructMsg` as StructMsg
|
||||
internal object GroupSvc: BaseSvc() {
|
||||
private val RefreshTroopMemberInfoLock by lazy {
|
||||
Mutex()
|
||||
@ -51,6 +94,28 @@ internal object GroupSvc: BaseSvc() {
|
||||
private lateinit var METHOD_REQ_TROOP_MEM_LIST: Method
|
||||
private lateinit var METHOD_REQ_MODIFY_GROUP_NAME: Method
|
||||
|
||||
suspend fun getProhibitedMemberList(groupId: Long): Result<List<ProhibitedMemberInfo>> {
|
||||
val buffer = sendOidbAW("OidbSvc.0x899_0", 2201, 0, oidb_0x899.ReqBody().apply {
|
||||
uint64_group_code.set(groupId)
|
||||
uint64_start_uin.set(0)
|
||||
uint32_identify_flag.set(6)
|
||||
memberlist_opt.set(oidb_0x899.memberlist().apply {
|
||||
uint64_member_uin.set(0)
|
||||
uint32_shutup_timestap.set(0)
|
||||
})
|
||||
}.toByteArray()) ?: return Result.failure(RuntimeException("[oidb] timeout"))
|
||||
val body = oidb_sso.OIDBSSOPkg()
|
||||
body.mergeFrom(buffer.slice(4))
|
||||
if(body.uint32_result.get() != 0) {
|
||||
return Result.failure(RuntimeException(body.str_error_msg.get()))
|
||||
}
|
||||
|
||||
val resp = oidb_0x899.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray())
|
||||
return Result.success(resp.rpt_memberlist.get().map {
|
||||
ProhibitedMemberInfo(it.uint64_member_uin.get(), it.uint32_shutup_timestap.get())
|
||||
})
|
||||
}
|
||||
|
||||
fun poke(groupId: String, userId: String) {
|
||||
sendOidb("OidbSvc.0xed3", 3795, 1, protobufOf(
|
||||
1 to userId.toLong(),
|
||||
@ -68,6 +133,12 @@ internal object GroupSvc: BaseSvc() {
|
||||
}.getOrThrow()
|
||||
}
|
||||
|
||||
getGroupInfo(groupId, true).onSuccess {
|
||||
if(it.wMemberNum > memberList.size) {
|
||||
return getGroupMemberList(groupId, true)
|
||||
}
|
||||
}
|
||||
|
||||
return Result.success(memberList)
|
||||
}
|
||||
|
||||
@ -107,7 +178,7 @@ internal object GroupSvc: BaseSvc() {
|
||||
val memberInfo = Oidb_0x8fc.MemberInfo()
|
||||
memberInfo.uint64_uin.set(userId.toLong())
|
||||
memberInfo.bytes_uin_name.set(ByteStringMicro.copyFromUtf8(localMemberInfo.troopnick.ifBlank {
|
||||
localMemberInfo.troopremark
|
||||
localMemberInfo.troopremark.ifNullOrEmpty("")
|
||||
}))
|
||||
memberInfo.bytes_special_title.set(ByteStringMicro.copyFromUtf8(title))
|
||||
memberInfo.uint32_special_title_expire_time.set(-1)
|
||||
@ -133,6 +204,49 @@ internal object GroupSvc: BaseSvc() {
|
||||
return true
|
||||
}
|
||||
|
||||
suspend fun setEssenceMessage(groupId: Long, seq: Long, rand: Long): Pair<Boolean, String> {
|
||||
val array = protobufOf(
|
||||
1 to groupId,
|
||||
2 to seq,
|
||||
3 to rand
|
||||
).toByteArray()
|
||||
val buffer = sendOidbAW("OidbSvc.0xeac_1", 3756, 1, array)
|
||||
?: return Pair(false, "unknown error")
|
||||
val body = oidb_sso.OIDBSSOPkg()
|
||||
body.mergeFrom(buffer.slice(4))
|
||||
val result = ProtoUtils.decodeFromByteArray(body.bytes_bodybuffer.get().toByteArray())
|
||||
return if (result.has(1)) {
|
||||
LogCenter.log("设置群精华失败: ${result[1].asUtf8String}")
|
||||
Pair(false, "设置群精华失败: ${result[1].asUtf8String}")
|
||||
} else {
|
||||
LogCenter.log("设置群精华 -> $groupId:$seq")
|
||||
Pair(true, "ok")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteEssenceMessage(groupId: Long, seq: Long, rand: Long): Pair<Boolean, String> {
|
||||
val array = protobufOf(
|
||||
1 to groupId,
|
||||
2 to seq,
|
||||
3 to rand
|
||||
).toByteArray()
|
||||
val buffer = sendOidbAW("OidbSvc.0xeac_2", 3756, 2, array)
|
||||
val body = oidb_sso.OIDBSSOPkg()
|
||||
if (buffer == null) {
|
||||
return Pair(false, "unknown error")
|
||||
}
|
||||
body.mergeFrom(buffer.slice(4))
|
||||
val result = ProtoUtils.decodeFromByteArray(body.bytes_bodybuffer.get().toByteArray())
|
||||
return if (result.has(1)) {
|
||||
LogCenter.log("移除群精华失败: ${result[1].asUtf8String}")
|
||||
Pair(false, "移除群精华失败: ${result[1].asUtf8String}")
|
||||
} else {
|
||||
LogCenter.log("移除群精华 -> $groupId:$seq")
|
||||
Pair(true, "ok")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun setGroupAdmin(groupId: Long, userId: Long, enable: Boolean) {
|
||||
val buffer = ByteBuffer.allocate(9)
|
||||
buffer.putBuf32Long(groupId)
|
||||
@ -204,7 +318,7 @@ internal object GroupSvc: BaseSvc() {
|
||||
|
||||
fun getOwner(groupId: String): Long {
|
||||
val groupInfo = getGroupInfo(groupId)
|
||||
return groupInfo.troopowneruin.toLong()
|
||||
return groupInfo.troopowneruin?.toLong() ?: 0
|
||||
}
|
||||
|
||||
fun isOwner(groupId: String): Boolean {
|
||||
@ -518,4 +632,279 @@ internal object GroupSvc: BaseSvc() {
|
||||
Result.failure(Exception("获取群成员信息失败"))
|
||||
}
|
||||
}
|
||||
|
||||
// ProfileService.Pb.ReqSystemMsgAction.Group
|
||||
suspend fun requestGroupRequest(
|
||||
msgSeq: Long,
|
||||
uin: Long,
|
||||
gid: Long,
|
||||
msg: String? = "",
|
||||
approve: Boolean? = true,
|
||||
notSee: Boolean? = false,
|
||||
subType: String
|
||||
): Result<String>{
|
||||
// 实在找不到接口了 发pb吧
|
||||
val buffer: ByteArray
|
||||
when (subType) {
|
||||
"invite" -> {
|
||||
buffer = protobufOf(
|
||||
1 to 1,
|
||||
2 to msgSeq,
|
||||
3 to uin, // self
|
||||
4 to 1,
|
||||
5 to 3,
|
||||
6 to 10016,
|
||||
7 to 2,
|
||||
8 to mapOf(
|
||||
1 to if (approve != false) 11 else 12,
|
||||
2 to gid
|
||||
),
|
||||
9 to 1000
|
||||
).toByteArray()
|
||||
}
|
||||
"add" -> {
|
||||
buffer = protobufOf(
|
||||
1 to 2,
|
||||
2 to msgSeq,
|
||||
3 to uin,
|
||||
4 to 1,
|
||||
5 to 2,
|
||||
6 to 30024,
|
||||
7 to 1,
|
||||
8 to mapOf(
|
||||
1 to if (approve != false) 11 else 12,
|
||||
2 to gid,
|
||||
50 to msg,
|
||||
53 to if (notSee != false) 1 else 0
|
||||
),
|
||||
9 to 1000
|
||||
).toByteArray()
|
||||
}
|
||||
else -> {
|
||||
return Result.failure(Exception("不支持的sub_type"))
|
||||
}
|
||||
}
|
||||
val respBuffer = sendBufferAW("ProfileService.Pb.ReqSystemMsgAction.Group", true, buffer)
|
||||
?: return Result.failure(Exception("操作失败"))
|
||||
val result = ProtoUtils.decodeFromByteArray(respBuffer.slice(4))
|
||||
return if (result.has(1, 1)) {
|
||||
if (result[1, 1].asInt == 0) {
|
||||
Result.success(result[2].asUtf8String)
|
||||
} else {
|
||||
Result.failure(Exception(result[1, 2].asUtf8String))
|
||||
}
|
||||
} else {
|
||||
Result.failure(Exception("操作失败"))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun requestGroupSystemMsgNew(msgNum: Int, reqMsgType: Int = 1, latestFriendSeq: Long = 0, latestGroupSeq: Long = 0, retryCnt: Int = 5): List<StructMsg> {
|
||||
if (retryCnt < 0) {
|
||||
return ArrayList()
|
||||
}
|
||||
val req = ReqSystemMsgNew()
|
||||
req.msg_num.set(msgNum)
|
||||
req.latest_friend_seq.set(latestFriendSeq)
|
||||
req.latest_group_seq.set(latestGroupSeq)
|
||||
req.version.set(1000)
|
||||
req.checktype.set(3)
|
||||
val flag = FlagInfo()
|
||||
flag.GrpMsg_Kick_Admin.set(1)
|
||||
flag.GrpMsg_HiddenGrp.set(1)
|
||||
flag.GrpMsg_WordingDown.set(1)
|
||||
// flag.FrdMsg_GetBusiCard.set(1)
|
||||
flag.GrpMsg_GetOfficialAccount.set(1)
|
||||
flag.GrpMsg_GetPayInGroup.set(1)
|
||||
flag.FrdMsg_Discuss2ManyChat.set(1)
|
||||
flag.GrpMsg_NotAllowJoinGrp_InviteNotFrd.set(1)
|
||||
flag.FrdMsg_NeedWaitingMsg.set(1)
|
||||
// flag.FrdMsg_uint32_need_all_unread_msg.set(1)
|
||||
flag.GrpMsg_NeedAutoAdminWording.set(1)
|
||||
flag.GrpMsg_get_transfer_group_msg_flag.set(1)
|
||||
flag.GrpMsg_get_quit_pay_group_msg_flag.set(1)
|
||||
flag.GrpMsg_support_invite_auto_join.set(1)
|
||||
flag.GrpMsg_mask_invite_auto_join.set(1)
|
||||
flag.GrpMsg_GetDisbandedByAdmin.set(1)
|
||||
flag.GrpMsg_GetC2cInviteJoinGroup.set(1)
|
||||
req.flag.set(flag)
|
||||
req.is_get_frd_ribbon.set(false)
|
||||
req.is_get_grp_ribbon.set(false)
|
||||
req.friend_msg_type_flag.set(1)
|
||||
req.uint32_req_msg_type.set(reqMsgType)
|
||||
req.uint32_need_uid.set(1)
|
||||
val respBuffer = sendBufferAW("ProfileService.Pb.ReqSystemMsgNew.Group", true, req.toByteArray())
|
||||
return if (respBuffer == null) {
|
||||
ArrayList()
|
||||
} else {
|
||||
try {
|
||||
val msg = RspSystemMsgNew()
|
||||
msg.mergeFrom(respBuffer.slice(4))
|
||||
return msg.groupmsgs.get().orEmpty()
|
||||
} catch (err: Throwable) {
|
||||
requestGroupSystemMsgNew(msgNum, reqMsgType, latestFriendSeq, latestGroupSeq, retryCnt - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
suspend fun getEssenceMessageList(groupId: Long, page: Int = 0, pageSize: Int = 20): Result<List<EssenceMessage>>{
|
||||
// GlobalClient.get()
|
||||
val cookie = TicketSvc.getCookie("qun.qq.com")
|
||||
val bkn = TicketSvc.getBkn(TicketSvc.getRealSkey(TicketSvc.getUin()))
|
||||
val url = "https://qun.qq.com/cgi-bin/group_digest/digest_list?bkn=${bkn}&group_code=${groupId}&page_start=${page}&page_limit=${pageSize}"
|
||||
val response = GlobalClient.get(url) {
|
||||
header("Cookie", cookie)
|
||||
}
|
||||
val body = Json.decodeFromStream<JsonElement>(response.body())
|
||||
if (body.jsonObject["retcode"].asInt == 0) {
|
||||
val data = body.jsonObject["data"].asJsonObject
|
||||
val list = data["msg_list"].asJsonArrayOrNull
|
||||
?: // is_end
|
||||
return Result.success(ArrayList())
|
||||
return Result.success(list.map {
|
||||
val obj = it.jsonObject
|
||||
val msgSeq = obj["msg_seq"].asInt
|
||||
val msg = EssenceMessage(
|
||||
senderId = obj["sender_uin"].asString.toLong(),
|
||||
senderNick = obj["sender_nick"].asString,
|
||||
senderTime = obj["sender_time"].asLong,
|
||||
operatorId = obj["add_digest_uin"].asString.toLong(),
|
||||
operatorNick = obj["add_digest_nick"].asString,
|
||||
operatorTime = obj["add_digest_time"].asLong,
|
||||
messageId = 0,
|
||||
messageSeq = msgSeq,
|
||||
messageContent = obj["msg_content"] ?: EmptyJsonArray
|
||||
)
|
||||
val mapping = MessageHelper.getMsgMappingBySeq(MsgConstant.KCHATTYPEGROUP, msgSeq)
|
||||
if (mapping != null) {
|
||||
msg.messageId = mapping.msgHashId
|
||||
}
|
||||
msg
|
||||
})
|
||||
} else {
|
||||
return Result.failure(Exception(body.jsonObject["retmsg"].asStringOrNull))
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
suspend fun getGroupAnnouncements(groupId: Long): Result<List<GroupAnnouncement>>{
|
||||
val cookie = TicketSvc.getCookie("qun.qq.com")
|
||||
val bkn = TicketSvc.getBkn(TicketSvc.getRealSkey(TicketSvc.getUin()))
|
||||
val url = "https://web.qun.qq.com/cgi-bin/announce/get_t_list?bkn=${bkn}&qid=${groupId}&ft=23&s=-1&n=20"
|
||||
val response = GlobalClient.get(url) {
|
||||
header("Cookie", cookie)
|
||||
}
|
||||
val body = Json.decodeFromStream<JsonElement>(response.body())
|
||||
if (body.jsonObject["ec"].asInt == 0) {
|
||||
val list = body.jsonObject["feeds"].asJsonArrayOrNull
|
||||
?: return Result.success(ArrayList())
|
||||
return Result.success(list.map {
|
||||
val obj = it.jsonObject
|
||||
GroupAnnouncement(
|
||||
senderId = obj["u"].asLong,
|
||||
publishTime = obj["pubt"].asLong,
|
||||
message = GroupAnnouncementMessage(
|
||||
text = obj["msg"].asJsonObject["text"].asString,
|
||||
images = obj["msg"].asJsonObject["pics"].asJsonArrayOrNull?.map {
|
||||
GroupAnnouncementMessageImage(
|
||||
id = it.jsonObject["id"].asString,
|
||||
width = it.jsonObject["w"].asString,
|
||||
height = it.jsonObject["h"].asString,
|
||||
)
|
||||
} ?: ArrayList()
|
||||
)
|
||||
)
|
||||
})
|
||||
} else {
|
||||
return Result.failure(Exception(body.jsonObject["em"].asStringOrNull))
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
suspend fun uploadImageTroopNotice(image: String): Result<GroupAnnouncementMessageImage> {
|
||||
val file = FileUtils.parseAndSave(image)
|
||||
val cookie = TicketSvc.getCookie("qun.qq.com")
|
||||
val bkn = TicketSvc.getBkn(TicketSvc.getRealSkey(TicketSvc.getUin()))
|
||||
val response = GlobalClient.post("https://web.qun.qq.com/cgi-bin/announce/upload_img") {
|
||||
headers {
|
||||
header("Cookie", cookie)
|
||||
}
|
||||
contentType(ContentType.MultiPart.FormData)
|
||||
setBody(MultiPartFormDataContent(
|
||||
// 黑人问号 ktor默认formdata传的tx不认。默认是name=bkn,非要写成name="bkn"才认?
|
||||
formData {
|
||||
append("filename", "001.png", Headers.build {
|
||||
append(HttpHeaders.ContentDisposition, "name=\"filename\"")
|
||||
})
|
||||
append("source", "troopNotice", Headers.build {
|
||||
append(HttpHeaders.ContentDisposition, "name=\"source\"")
|
||||
})
|
||||
append("bkn", bkn, Headers.build {
|
||||
append(HttpHeaders.ContentDisposition, "name=\"bkn\"")
|
||||
})
|
||||
append("m", "0", Headers.build {
|
||||
append(HttpHeaders.ContentDisposition, "name=\"m\"")
|
||||
})
|
||||
append("pic_up", file.readBytes(), Headers.build {
|
||||
append(HttpHeaders.ContentType, "image/png")
|
||||
append(HttpHeaders.ContentDisposition, "name=\"pic_up\" filename=\"001.png\"")
|
||||
|
||||
})
|
||||
}
|
||||
))
|
||||
}
|
||||
val body = Json.decodeFromStream<JsonElement>(response.body())
|
||||
if (body.jsonObject["ec"].asInt == 0) {
|
||||
var idJsonStr = body.jsonObject["id"].asStringOrNull
|
||||
return if (idJsonStr != null) {
|
||||
idJsonStr = idJsonStr.replace(""", "\"")
|
||||
val idJson = Json.decodeFromString<JsonElement>(idJsonStr)
|
||||
LogCenter.log(idJson.toString())
|
||||
Result.success(GroupAnnouncementMessageImage(
|
||||
height = idJson.asJsonObject["h"].asString,
|
||||
width = idJson.asJsonObject["w"].asString,
|
||||
id = idJson.asJsonObject["id"].asString,
|
||||
))
|
||||
} else {
|
||||
Result.failure(Exception("图片上传失败"))
|
||||
}
|
||||
} else {
|
||||
return Result.failure(Exception(body.jsonObject["em"].asStringOrNull))
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
suspend fun addQunNotice(groupId: Long, text: String, image: GroupAnnouncementMessageImage?) : Result<Boolean> {
|
||||
val cookie = TicketSvc.getCookie("qun.qq.com")
|
||||
val bkn = TicketSvc.getBkn(TicketSvc.getRealSkey(TicketSvc.getUin()))
|
||||
val response = GlobalClient.submitForm(
|
||||
url = "https://web.qun.qq.com/cgi-bin/announce/add_qun_notice",
|
||||
formParameters = parameters {
|
||||
append("qid", groupId.toString())
|
||||
append("bkn", bkn)
|
||||
append("text", text)
|
||||
append("pinned", "0")
|
||||
append("type", "1")
|
||||
// todo allow custom settings
|
||||
append("settings", "{\"is_show_edit_card:\"1,\"tip_window_type\":1,\"confirm_required\":1}")
|
||||
if (null != image) {
|
||||
append("pic", image.id)
|
||||
append("imgWidth", image.width)
|
||||
append("imgHeight", image.height)
|
||||
}
|
||||
},
|
||||
block = {
|
||||
headers {
|
||||
header("Cookie", cookie)
|
||||
}
|
||||
}
|
||||
)
|
||||
val body = Json.decodeFromStream<JsonElement>(response.body())
|
||||
return if (body.jsonObject["ec"].asInt == 0) {
|
||||
Result.success(true)
|
||||
} else {
|
||||
Result.failure(Exception(body.jsonObject["em"].asStringOrNull))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +1,61 @@
|
||||
@file:OptIn(DelicateCoroutinesApi::class)
|
||||
|
||||
package moe.fuqiuluo.qqinterface.servlet
|
||||
|
||||
import com.tencent.mobileqq.qroute.QRoute
|
||||
import com.tencent.mobileqq.troop.api.ITroopMemberNameService
|
||||
import com.tencent.qqnt.kernel.api.IKernelService
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
||||
import com.tencent.qqnt.kernel.nativeinterface.TempChatGameSession
|
||||
import com.tencent.qqnt.kernel.nativeinterface.TempChatPrepareInfo
|
||||
import com.tencent.qqnt.msg.api.IMsgService
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
||||
import moe.fuqiuluo.shamrock.helper.Level
|
||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||
import moe.fuqiuluo.shamrock.helper.SendMsgException
|
||||
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.msgService
|
||||
import java.util.UUID
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
internal object MsgSvc: BaseSvc() {
|
||||
fun uploadForwardMsg(): Result<String> {
|
||||
return Result.failure(Exception("Not implemented"))
|
||||
}
|
||||
|
||||
suspend fun prepareTempChatFromGroup(
|
||||
groupId: String,
|
||||
peerId: String
|
||||
): Result<Unit> {
|
||||
LogCenter.log("主动临时消息,创建临时会话。", Level.INFO)
|
||||
val msgService = app.getRuntimeService(IKernelService::class.java, "all").msgService
|
||||
?: return Result.failure(Exception("获取消息服务失败"))
|
||||
msgService.prepareTempChat(TempChatPrepareInfo(
|
||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP,
|
||||
ContactHelper.getUidByUinAsync(peerId = peerId.toLong()),
|
||||
app.getRuntimeService(ITroopMemberNameService::class.java, "all")
|
||||
.getTroopMemberNameRemarkFirst(groupId, peerId),
|
||||
groupId, EMPTY_BYTE_ARRAY, app.currentUid, "", TempChatGameSession()
|
||||
)) { code, reason ->
|
||||
if (code != 0) {
|
||||
LogCenter.log("临时会话创建失败: $code, $reason", Level.ERROR)
|
||||
}
|
||||
}
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
/**
|
||||
* 正常获取
|
||||
*/
|
||||
@ -51,6 +89,34 @@ internal object MsgSvc: BaseSvc() {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getMsgByQMsgId(
|
||||
chatType: Int,
|
||||
peerId: String,
|
||||
qqMsgId: Long
|
||||
): Result<MsgRecord> {
|
||||
val contact = MessageHelper.generateContact(chatType, peerId)
|
||||
val service = QRoute.api(IMsgService::class.java) ?:
|
||||
return Result.failure(Exception("获取消息服务"))
|
||||
|
||||
val msg = withTimeoutOrNull(5000) {
|
||||
suspendCoroutine { continuation ->
|
||||
service.getMsgsByMsgId(contact, arrayListOf(qqMsgId)) { code, _, msgRecords ->
|
||||
if (code == 0 && msgRecords.isNotEmpty()) {
|
||||
continuation.resume(msgRecords.first())
|
||||
} else {
|
||||
continuation.resume(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (msg != null) {
|
||||
Result.success(msg)
|
||||
} else {
|
||||
Result.failure(Exception("获取消息失败"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 什么鸟屎都获取不到
|
||||
*/
|
||||
@ -60,7 +126,7 @@ internal object MsgSvc: BaseSvc() {
|
||||
seq: Long
|
||||
): Result<MsgRecord> {
|
||||
val contact = MessageHelper.generateContact(chatType, peerId)
|
||||
val msg = withTimeoutOrNull(60 * 1000) {
|
||||
val msg = withTimeoutOrNull(1000) {
|
||||
val service = QRoute.api(IMsgService::class.java)
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
service.getMsgsBySeqs(contact, arrayListOf(seq)) { code, _, msgRecords ->
|
||||
@ -103,10 +169,80 @@ internal object MsgSvc: BaseSvc() {
|
||||
*
|
||||
* Aio 腾讯内部命名 All In One
|
||||
*/
|
||||
suspend fun sendToAio(chatType: Int, peedId: String, message: JsonArray): Pair<Long, Int> {
|
||||
suspend fun sendToAio(
|
||||
chatType: Int,
|
||||
peedId: String,
|
||||
message: JsonArray,
|
||||
fromId: String = peedId,
|
||||
retryCnt: Int = 3
|
||||
): Result<Pair<Long, Int>> {
|
||||
//LogCenter.log(message.toString(), Level.ERROR)
|
||||
//callback.msgHash = result.second 什么垃圾代码,万一cb比你快,你不就寄了?
|
||||
return MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, MessageCallback(peedId, 0))
|
||||
|
||||
// 主动临时消息
|
||||
when (chatType) {
|
||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> {
|
||||
prepareTempChatFromGroup(fromId, peedId).onFailure {
|
||||
LogCenter.log("主动临时消息,创建临时会话失败。", Level.ERROR)
|
||||
return Result.failure(Exception("主动临时消息,创建临时会话失败。"))
|
||||
}
|
||||
}
|
||||
}
|
||||
val result = MessageHelper.sendMessageWithoutMsgId(
|
||||
chatType,
|
||||
peedId,
|
||||
message,
|
||||
fromId,
|
||||
MessageCallback(peedId, 0)
|
||||
)
|
||||
return if (result.isFailure
|
||||
&& result.exceptionOrNull()?.javaClass == SendMsgException::class.java
|
||||
&& retryCnt > 0) {
|
||||
// 发送失败,可能网络问题出现红色感叹号,重试
|
||||
// 例如 rich media transfer failed
|
||||
delay(100)
|
||||
sendToAio(chatType, peedId, message, fromId, retryCnt - 1)
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getMultiMsg(resId: String): Result<List<MsgRecord>> {
|
||||
val kernelService = NTServiceFetcher.kernelService
|
||||
val sessionService = kernelService.wrapperSession
|
||||
val msgService = sessionService.msgService
|
||||
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, TicketSvc.getUin())
|
||||
|
||||
val content = "{\"app\":\"com.tencent.multimsg\",\"config\":{\"autosize\":1,\"forward\":1,\"round\":1,\"type\":\"normal\",\"width\":300},\"desc\":\"[聊天记录]\",\"extra\":\"\",\"meta\":{\"detail\":{\"news\":[{\"text\":\"Shamrock: 这是条假消息!\"}],\"resid\":\"$resId\",\"source\":\"聊天记录\",\"summary\":\"转发消息\",\"uniseq\":\"${UUID.randomUUID()}\"}},\"prompt\":\"[聊天记录]\",\"ver\":\"0.0.0.5\",\"view\":\"contact\"}"
|
||||
val msgId = PacketSvc.fakeSelfRecvJsonMsg(msgService, content)
|
||||
if (msgId < 0) {
|
||||
return Result.failure(Exception("获取合并转发消息ID失败"))
|
||||
}
|
||||
val msgList = withTimeoutOrNull(5000L) {
|
||||
suspendCancellableCoroutine<ArrayList<MsgRecord>> {
|
||||
val job = GlobalScope.launch {
|
||||
var hasResult = false
|
||||
while (!hasResult) {
|
||||
msgService.getMultiMsg(contact, msgId, msgId) { code, why, msgList ->
|
||||
if (code == 0) {
|
||||
it.resume(msgList)
|
||||
hasResult = true
|
||||
} else {
|
||||
LogCenter.log("获取合并转发消息失败: $code($why): $msgId", Level.ERROR)
|
||||
}
|
||||
}
|
||||
delay(200)
|
||||
}
|
||||
}
|
||||
it.invokeOnCancellation {
|
||||
job.cancel()
|
||||
}
|
||||
}
|
||||
} ?: return Result.failure(Exception("获取合并转发消息失败"))
|
||||
|
||||
//msgService.deleteMsg(contact, arrayListOf(msgId), null)
|
||||
|
||||
return Result.success(msgList)
|
||||
}
|
||||
|
||||
class MessageCallback(
|
||||
|
@ -0,0 +1,98 @@
|
||||
package moe.fuqiuluo.qqinterface.servlet
|
||||
|
||||
import com.tencent.mobileqq.msf.core.MsfCore
|
||||
import com.tencent.qqnt.kernel.nativeinterface.Contact
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IKernelMsgService
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||
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.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import moe.fuqiuluo.proto.protobufOf
|
||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||
import moe.fuqiuluo.shamrock.remote.action.handlers.GetHistoryMsg
|
||||
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
|
||||
import moe.fuqiuluo.shamrock.tools.broadcast
|
||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||
import mqq.app.MobileQQ
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.math.abs
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextInt
|
||||
import kotlin.random.nextLong
|
||||
|
||||
internal object PacketSvc: BaseSvc() {
|
||||
/**
|
||||
* 伪造收到Json卡片消息
|
||||
*/
|
||||
suspend fun fakeSelfRecvJsonMsg(msgService: IKernelMsgService, content: String): Long {
|
||||
return fakeReceiveSelfMsg(msgService) { arrayOf(
|
||||
mapOf(
|
||||
51 to 1 to (byteArrayOf(1) + DeflateTools.compress(content.toByteArray()))
|
||||
)
|
||||
) }
|
||||
}
|
||||
|
||||
private suspend fun fakeReceiveSelfMsg(msgService: IKernelMsgService, builder: () -> Array<Map<*, *>>): Long {
|
||||
val latestMsg = withTimeoutOrNull(3000) {
|
||||
suspendCancellableCoroutine {
|
||||
msgService.getMsgs(Contact(MsgConstant.KCHATTYPEC2C, app.currentUid, ""), 0L, 1, true) { code, why, msgs ->
|
||||
it.resume(GetHistoryMsg.GetMsgResult(code, why, msgs))
|
||||
}
|
||||
}
|
||||
}?.data?.firstOrNull()
|
||||
val msgSeq = (latestMsg?.msgSeq ?: 0) + 1
|
||||
fakeReceive("trpc.msg.olpush.OlPushService.MsgPush", 10000, protobufOf(
|
||||
1 to mapOf(
|
||||
1 to mapOf(
|
||||
1 to app.currentUin.toLong(),
|
||||
2 to app.currentUid,
|
||||
3 to 1001,
|
||||
5 to app.currentUin.toLong(),
|
||||
6 to app.currentUid
|
||||
),
|
||||
2 to mapOf(
|
||||
1 to 166,
|
||||
3 to 11,
|
||||
4 to msgSeq,
|
||||
5 to msgSeq,
|
||||
6 to (System.currentTimeMillis() / 1000).toInt(),
|
||||
7 to 1,
|
||||
11 to msgSeq,
|
||||
12 to msgService.getMsgUniqueId(System.currentTimeMillis()),
|
||||
14 to msgSeq - 2,
|
||||
28 to msgSeq
|
||||
),
|
||||
3 to 1 to 2 to builder()
|
||||
)
|
||||
).toByteArray())
|
||||
return withTimeoutOrNull(5000L) {
|
||||
suspendCancellableCoroutine {
|
||||
AioListener.messageLessListenerMap[msgSeq] = {
|
||||
it.resume(this.msgId)
|
||||
}
|
||||
}
|
||||
} ?: -1L
|
||||
}
|
||||
|
||||
/**
|
||||
* 伪造QQ收到某个包
|
||||
*/
|
||||
private fun fakeReceive(cmd: String, seq: Int, buffer: ByteArray) {
|
||||
MobileQQ.getContext().broadcast("msf") {
|
||||
putExtra("__cmd", "fake_packet")
|
||||
putExtra("package_cmd", cmd)
|
||||
putExtra("package_uin", app.currentUin)
|
||||
putExtra("package_seq", seq)
|
||||
val wupBuffer = BytePacketBuilder().apply {
|
||||
writeInt(buffer.size + 4)
|
||||
writeFully(buffer)
|
||||
}.build()
|
||||
putExtra("package_buffer", wupBuffer.readBytes())
|
||||
wupBuffer.release()
|
||||
}
|
||||
}
|
||||
}
|
@ -43,6 +43,10 @@ internal object TicketSvc: BaseSvc() {
|
||||
return app.currentUin.ifBlank { "0" }
|
||||
}
|
||||
|
||||
fun getLongUin(): Long {
|
||||
return app.longAccountUin
|
||||
}
|
||||
|
||||
fun getCookie(): String {
|
||||
val uin = getUin()
|
||||
val skey = getRealSkey(uin)
|
||||
@ -70,8 +74,13 @@ internal object TicketSvc: BaseSvc() {
|
||||
}
|
||||
|
||||
suspend fun getCSRF(uin: String, domain: String): String {
|
||||
// 是不是要用Skey?
|
||||
return getBkn(getPSKey(uin, domain) ?: "")
|
||||
}
|
||||
|
||||
fun getBkn(arg: String): String {
|
||||
var v: Long = 5381
|
||||
for (element in getPSKey(uin, domain) ?: "") {
|
||||
for (element in arg) {
|
||||
v += (v shl 5 and 2147483647L) + element.code.toLong()
|
||||
}
|
||||
return (v and 2147483647L).toString()
|
||||
|
@ -86,10 +86,10 @@ internal object VisitorSvc: BaseSvc() {
|
||||
it.putLong(moe.fuqiuluo.shamrock.remote.service.data.profile.ProfileProtocolConst.PARAM_TARGET_UIN, target)
|
||||
it.putByteArray("vCookies", card.vCookies)
|
||||
it.putBoolean("nearby_people", true)
|
||||
it.putInt("favoriteSource", SUB_FROM_SHARE_CARD_TROOP)
|
||||
it.putInt("favoriteSource", FROM_CONTACTS_TAB)
|
||||
it.putInt("iCount", count)
|
||||
it.putInt("from", FROM_SHARE_CARD)
|
||||
it.putInt("from", FROM_CONTACTS_TAB)
|
||||
}
|
||||
return Result.success(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
package moe.fuqiuluo.qqinterface.servlet.entries
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class FileUrl(
|
||||
@SerialName("url") val url: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GroupFileList(
|
||||
@SerialName("files") val files: List<FileInfo>,
|
||||
@SerialName("folders") val folders: List<FolderInfo>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FileInfo(
|
||||
@SerialName("group_id") val groupId: Long,
|
||||
@SerialName("file_id") val fileId: String,
|
||||
@SerialName("file_name") val fileName: String,
|
||||
@SerialName("file_size") val fileSize: Long,
|
||||
@SerialName("busid") val busid: Int,
|
||||
@SerialName("upload_time") val uploadTime: Int,
|
||||
@SerialName("dead_time") val deadTime: Int,
|
||||
@SerialName("modify_time") val modifyTime: Int,
|
||||
@SerialName("download_times") val downloadTimes: Int,
|
||||
@SerialName("uploader") val uploadUin: Long,
|
||||
@SerialName("upload_name") val uploadNick: String,
|
||||
@SerialName("sha") val sha: String,
|
||||
@SerialName("sha3") val sha3: String,
|
||||
@SerialName("md5") val md5: String,
|
||||
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FolderInfo(
|
||||
@SerialName("group_id") val groupId: Long,
|
||||
@SerialName("folder_id") val folderId: String,
|
||||
@SerialName("folder_name") val folderName: String,
|
||||
@SerialName("total_file_count") val totalFileCount: Int,
|
||||
@SerialName("create_time") val createTime: Int,
|
||||
@SerialName("creator") val creator: Long,
|
||||
@SerialName("creator_name") val creatorNick: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FileSystemInfo(
|
||||
@SerialName("file_count") val fileCount: Int,
|
||||
@SerialName("limit_count") val fileLimitCount: Int,
|
||||
@SerialName("used_space") val usedSpace: Long,
|
||||
@SerialName("total_space") val totalSpace: Long,
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package moe.fuqiuluo.qqinterface.servlet.entries
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
internal data class ProhibitedMemberInfo(
|
||||
@SerialName("user_id") val memberUin: Long,
|
||||
@SerialName("time") val shutuptimestap: Int
|
||||
)
|
@ -1,103 +0,0 @@
|
||||
package moe.fuqiuluo.qqinterface.servlet.msg
|
||||
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
||||
import moe.fuqiuluo.proto.ProtoUtils
|
||||
import moe.fuqiuluo.proto.asUtf8String
|
||||
import moe.fuqiuluo.proto.protobufOf
|
||||
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
||||
import moe.fuqiuluo.shamrock.tools.slice
|
||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||
|
||||
internal object LongMsgHelper: BaseSvc() {
|
||||
private const val GROUP_LONG_MSG_CMD = "trpc.group.long_msg_interface.MsgService.SsoSendLongMsg"
|
||||
|
||||
suspend fun uploadGroupMsg(groupId: String, msgs: List<MsgRecord>): String {
|
||||
val reqBody = protobufOf(
|
||||
2 to mapOf(
|
||||
1 to 3,
|
||||
2 to 2 to groupId,
|
||||
3 to groupId.toLong(),
|
||||
4 to DeflateTools.gzip(toGroupByteArray(msgs))
|
||||
),
|
||||
15 to mapOf(
|
||||
1 to 4,
|
||||
2 to 2,
|
||||
3 to 9,
|
||||
4 to 0
|
||||
)
|
||||
).toByteArray()
|
||||
val buffer = sendBufferAW(GROUP_LONG_MSG_CMD, true, reqBody)
|
||||
?: error("获取消息资源ID失败")
|
||||
val pb = ProtoUtils.decodeFromByteArray(buffer.slice(4))
|
||||
return pb[2, 3].asUtf8String
|
||||
}
|
||||
|
||||
private fun toGroupByteArray(msgs: List<MsgRecord>): ByteArray {
|
||||
return protobufOf(
|
||||
2 to mapOf(
|
||||
1 to "MultiMsg",
|
||||
2 to 1 to msgs.map { record ->
|
||||
mapOf(
|
||||
1 to mapOf(
|
||||
2 to record.senderUid,
|
||||
8 to mapOf(
|
||||
1 to record.peerUin,
|
||||
4 to record.sendNickName,
|
||||
5 to 2
|
||||
)
|
||||
),
|
||||
2 to mapOf(
|
||||
1 to 82,
|
||||
4 to record.msgRandom,
|
||||
5 to record.msgSeq,
|
||||
6 to record.msgTime,
|
||||
7 to 1,
|
||||
8 to 0,
|
||||
9 to 0,
|
||||
15 to mapOf(
|
||||
1 to 0,
|
||||
2 to 0,
|
||||
3 to 0,
|
||||
4 to "",
|
||||
5 to ""
|
||||
)
|
||||
),
|
||||
3 to mapOf(
|
||||
1 to 2 to (record.elements.map {
|
||||
when (val type = it.elementType) {
|
||||
MsgConstant.KELEMTYPETEXT -> mapOf(1 to 1 to it.textElement.content)
|
||||
|
||||
else -> error("不支持的合并转发消息类型: $type")
|
||||
}
|
||||
} as ArrayList<Any>).also {
|
||||
it.add(0, mapOf(
|
||||
37 to mapOf(
|
||||
1 to 8,
|
||||
16 to 0,
|
||||
17 to 0,
|
||||
19 to mapOf(
|
||||
15 to 65536,
|
||||
25 to 0,
|
||||
30 to 0,
|
||||
31 to 0,
|
||||
34 to 0,
|
||||
41 to 0,
|
||||
52 to 64,
|
||||
54 to 1,
|
||||
55 to 0,
|
||||
72 to 0,
|
||||
73 to 1 to 0,
|
||||
96 to 0
|
||||
)
|
||||
)
|
||||
))
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
).toByteArray()
|
||||
}
|
||||
|
||||
}
|
@ -22,6 +22,9 @@ import com.tencent.qqnt.kernel.nativeinterface.ReplyElement
|
||||
import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo
|
||||
import com.tencent.qqnt.kernel.nativeinterface.TextElement
|
||||
import com.tencent.qqnt.kernel.nativeinterface.VideoElement
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import moe.fuqiuluo.qqinterface.servlet.CardSvc
|
||||
@ -70,6 +73,8 @@ import tencent.im.oidb.cmd0xdc2.oidb_cmd0xdc2
|
||||
import tencent.im.oidb.oidb_sso
|
||||
import java.io.File
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextInt
|
||||
|
||||
internal typealias IMaker = suspend (Int, Long, String, JsonObject) -> Result<MsgElement>
|
||||
|
||||
@ -95,9 +100,122 @@ internal object MessageMaker {
|
||||
"reply" to MessageMaker::createReplyElem,
|
||||
"touch" to MessageMaker::createTouchElem,
|
||||
"weather" to MessageMaker::createWeatherElem,
|
||||
"json" to MessageMaker::createJsonElem,
|
||||
"new_dice" to MessageMaker::createNewDiceElem,
|
||||
"new_rps" to MessageMaker::createNewRpsElem,
|
||||
"basketball" to MessageMaker::createBasketballElem,
|
||||
//"node" to MessageMaker::createNodeElem,
|
||||
//"multi_msg" to MessageMaker::createLongMsgStruct,
|
||||
)
|
||||
|
||||
// private suspend fun createNodeElem(
|
||||
// chatType: Int,
|
||||
// msgId: Long,
|
||||
// peerId: String,
|
||||
// data: JsonObject
|
||||
// ): Result<MsgElement> {
|
||||
// data.checkAndThrow("data")
|
||||
// SendForwardMessage(MsgConstant.KCHATTYPEC2C, TicketSvc.getUin(), data["content"].asJsonArray)
|
||||
//
|
||||
// }
|
||||
/**\
|
||||
* msgElement.setFaceElement(new FaceElement());
|
||||
* msgElement.getFaceElement().setFaceIndex(114);
|
||||
* msgElement.getFaceElement().setFaceText("/篮球");
|
||||
* msgElement.getFaceElement().setFaceType(3);
|
||||
* msgElement.getFaceElement().setPackId("1");
|
||||
* msgElement.getFaceElement().setStickerId("13");
|
||||
* msgElement.getFaceElement().setRandomType(1);
|
||||
* msgElement.getFaceElement().setImageType(1);
|
||||
* msgElement.getFaceElement().setStickerType(2);
|
||||
* msgElement.getFaceElement().setSourceType(1);
|
||||
* msgElement.getFaceElement().setSurpriseId("");
|
||||
* msgElement.getFaceElement().setResultId(String.valueOf(new Random().nextInt(5) + 1));
|
||||
*/
|
||||
private suspend fun createBasketballElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
||||
val elem = MsgElement()
|
||||
elem.elementType = MsgConstant.KELEMTYPEFACE
|
||||
val face = FaceElement()
|
||||
face.faceIndex = 114
|
||||
face.faceText = "/篮球"
|
||||
face.faceType = 3
|
||||
face.packId = "1"
|
||||
face.stickerId = "13"
|
||||
face.sourceType = 1
|
||||
face.stickerType = 2
|
||||
face.resultId = Random.nextInt(1 .. 5).toString()
|
||||
face.surpriseId = ""
|
||||
face.randomType = 1
|
||||
elem.faceElement = face
|
||||
return Result.success(elem)
|
||||
}
|
||||
|
||||
private suspend fun createNewRpsElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
||||
val elem = MsgElement()
|
||||
elem.elementType = MsgConstant.KELEMTYPEFACE
|
||||
val face = FaceElement()
|
||||
face.faceIndex = 359
|
||||
face.faceText = "/包剪锤"
|
||||
face.faceType = 3
|
||||
face.packId = "1"
|
||||
face.stickerId = "34"
|
||||
face.sourceType = 1
|
||||
face.stickerType = 2
|
||||
face.resultId = ""
|
||||
face.surpriseId = ""
|
||||
face.randomType = 1
|
||||
elem.faceElement = face
|
||||
return Result.success(elem)
|
||||
}
|
||||
|
||||
private suspend fun createNewDiceElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
||||
val elem = MsgElement()
|
||||
elem.elementType = MsgConstant.KELEMTYPEFACE
|
||||
val face = FaceElement()
|
||||
face.faceIndex = 358
|
||||
face.faceText = "/骰子"
|
||||
face.faceType = 3
|
||||
face.packId = "1"
|
||||
face.stickerId = "33"
|
||||
face.sourceType = 1
|
||||
face.stickerType = 2
|
||||
face.resultId = ""
|
||||
face.surpriseId = ""
|
||||
face.randomType = 1
|
||||
elem.faceElement = face
|
||||
return Result.success(elem)
|
||||
}
|
||||
|
||||
private suspend fun createJsonElem(
|
||||
chatType: Int,
|
||||
msgId: Long,
|
||||
peerId: String,
|
||||
data: JsonObject
|
||||
): Result<MsgElement> {
|
||||
data.checkAndThrow("data")
|
||||
val jsonStr = data["data"].let {
|
||||
if (it is JsonObject) it.asJsonObject.toString() else {
|
||||
val str = it.asStringOrNull ?: ""
|
||||
// 检查字符串是否是合法json,不然qq会闪退
|
||||
try {
|
||||
val element = Json.decodeFromString<JsonElement>(str)
|
||||
if (element !is JsonObject) {
|
||||
return Result.failure(Exception("不合法的JSON字符串"))
|
||||
}
|
||||
} catch (err: Throwable) {
|
||||
LogCenter.log(err.stackTraceToString(), Level.ERROR)
|
||||
return Result.failure(Exception("不合法的JSON字符串"))
|
||||
}
|
||||
str
|
||||
}
|
||||
}
|
||||
val element = MsgElement()
|
||||
element.elementType = MsgConstant.KELEMTYPEARKSTRUCT
|
||||
val ark = ArkElement(jsonStr, null, null)
|
||||
element.arkElement = ark
|
||||
return Result.success(element)
|
||||
}
|
||||
|
||||
private suspend fun createTouchElem(
|
||||
chatType: Int,
|
||||
msgId: Long,
|
||||
@ -539,7 +657,7 @@ internal object MessageMaker {
|
||||
file = FileUtils.parseAndSave(data["url"].asString)
|
||||
}
|
||||
if (!file.exists()) {
|
||||
throw LogicException("Voice(${file.name}) file is not exists, please check your filename.")
|
||||
return Result.failure(LogicException("Voice(${file.name}) file is not exists, please check your filename."))
|
||||
}
|
||||
val isMagic = data["magic"].asStringOrNull == "1"
|
||||
|
||||
@ -580,11 +698,14 @@ internal object MessageMaker {
|
||||
// QQNTWrapperUtil.CppProxy.copyFile(file.absolutePath, originalPath)
|
||||
//}
|
||||
|
||||
Transfer with when (chatType) {
|
||||
if(!(Transfer with when (chatType) {
|
||||
MsgConstant.KCHATTYPEGROUP -> Troop(peerId)
|
||||
MsgConstant.KCHATTYPEC2C -> Private(peerId)
|
||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> Private(peerId)
|
||||
else -> error("Not supported chatType($chatType) for RecordMsg")
|
||||
} trans VoiceResource(file)
|
||||
} trans VoiceResource(file))) {
|
||||
return Result.failure(RuntimeException("上传语音失败: $file"))
|
||||
}
|
||||
|
||||
val elem = MsgElement()
|
||||
elem.elementType = MsgConstant.KELEMTYPEPTT
|
||||
@ -613,24 +734,26 @@ internal object MessageMaker {
|
||||
}
|
||||
|
||||
private suspend fun createImageElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
|
||||
data.checkAndThrow("file")
|
||||
val isOriginal = data["original"].asBooleanOrNull ?: true
|
||||
val isFlash = data["flash"].asBooleanOrNull ?: false
|
||||
val file = data["file"].asString.let {
|
||||
val md5 = it.replace(regex = "[{}\\-]".toRegex(), replacement = "").split(".")[0].lowercase()
|
||||
var tmpPicFile = if (md5.length == 32) {
|
||||
val filePath = data["file"].asStringOrNull
|
||||
val url = data["url"].asStringOrNull
|
||||
var file: File? = null
|
||||
if (filePath != null) {
|
||||
val md5 = filePath.replace(regex = "[{}\\-]".toRegex(), replacement = "").split(".")[0].lowercase()
|
||||
file = if (md5.length == 32) {
|
||||
FileUtils.getFile(md5)
|
||||
} else {
|
||||
FileUtils.parseAndSave(it)
|
||||
FileUtils.parseAndSave(filePath)
|
||||
}
|
||||
if (!tmpPicFile.exists() && data.containsKey("url")) {
|
||||
tmpPicFile = FileUtils.parseAndSave(data["url"].asString)
|
||||
}
|
||||
return@let tmpPicFile
|
||||
}
|
||||
if (!file.exists()) {
|
||||
if ((file == null || !file.exists()) && url != null) {
|
||||
file = FileUtils.parseAndSave(url)
|
||||
}
|
||||
if (file?.exists() == false) {
|
||||
throw LogicException("Image(${file.name}) file is not exists, please check your filename.")
|
||||
}
|
||||
requireNotNull(file)
|
||||
|
||||
Transfer with when (chatType) {
|
||||
MsgConstant.KCHATTYPEGROUP -> Troop(peerId)
|
||||
|
@ -52,7 +52,9 @@ internal object MessageConvert {
|
||||
MsgConstant.KELEMTYPEARKSTRUCT to MessageElemConverter.StructJsonConverter,
|
||||
MsgConstant.KELEMTYPEREPLY to MessageElemConverter.ReplyConverter,
|
||||
MsgConstant.KELEMTYPEGRAYTIP to MessageElemConverter.GrayTipsConverter,
|
||||
MsgConstant.KELEMTYPEFILE to MessageElemConverter.FileConverter
|
||||
MsgConstant.KELEMTYPEFILE to MessageElemConverter.FileConverter,
|
||||
//MsgConstant.KELEMTYPEMULTIFORWARD to MessageElemConverter.XmlMultiMsgConverter,
|
||||
//MsgConstant.KELEMTYPESTRUCTLONGMSG to MessageElemConverter.XmlLongMsgConverter,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -55,12 +55,42 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
||||
)
|
||||
)
|
||||
}
|
||||
return MessageSegment(
|
||||
type = "face",
|
||||
data = hashMapOf(
|
||||
"id" to face.faceIndex
|
||||
|
||||
|
||||
when (face.faceIndex) {
|
||||
114 -> {
|
||||
return MessageSegment(
|
||||
type = "basketball",
|
||||
data = hashMapOf(
|
||||
"id" to face.resultId.ifEmpty { "0" }.toInt(),
|
||||
)
|
||||
)
|
||||
}
|
||||
358 -> {
|
||||
if (face.sourceType == 1) return MessageSegment("new_dice")
|
||||
return MessageSegment(
|
||||
type = "new_dice",
|
||||
data = hashMapOf(
|
||||
"id" to face.resultId.ifEmpty { "0" }.toInt()
|
||||
)
|
||||
)
|
||||
}
|
||||
359 -> {
|
||||
if (face.resultId.isEmpty()) return MessageSegment("new_rps")
|
||||
return MessageSegment(
|
||||
type = "new_rps",
|
||||
data = hashMapOf(
|
||||
"id" to face.resultId.ifEmpty { "0" }.toInt()
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> return MessageSegment(
|
||||
type = "face",
|
||||
data = hashMapOf(
|
||||
"id" to face.faceIndex
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,13 +173,15 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
||||
element: MsgElement
|
||||
): MessageSegment {
|
||||
val video = element.videoElement
|
||||
val md5 = video.fileName.split(".")[0]
|
||||
|
||||
return MessageSegment(
|
||||
type = "video",
|
||||
data = hashMapOf(
|
||||
"file" to video.fileName,
|
||||
"url" to when(chatType) {
|
||||
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", video.fileName, video.fileUuid)
|
||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", video.fileName, video.fileUuid)
|
||||
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
|
||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid)
|
||||
else -> unknownChatType(chatType)
|
||||
}
|
||||
).also {
|
||||
@ -194,6 +226,15 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
||||
): MessageSegment {
|
||||
val data = element.arkElement.bytesData.asJsonObject
|
||||
return when (data["app"].asString) {
|
||||
"com.tencent.multimsg" -> {
|
||||
val info = data["meta"].asJsonObject["detail"].asJsonObject
|
||||
MessageSegment(
|
||||
type = "forward",
|
||||
data = mapOf(
|
||||
"id" to info["resid"].asString
|
||||
)
|
||||
)
|
||||
}
|
||||
"com.tencent.troopsharecard" -> {
|
||||
val info = data["meta"].asJsonObject["contact"].asJsonObject
|
||||
MessageSegment(
|
||||
@ -229,7 +270,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
||||
else -> MessageSegment(
|
||||
type = "json",
|
||||
data = mapOf(
|
||||
"data" to element.arkElement.bytesData.asJsonObject
|
||||
"data" to element.arkElement.bytesData.asJsonObject.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -252,7 +293,11 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
||||
} else {
|
||||
MessageDB.getInstance().messageMappingDao()
|
||||
.queryByMsgSeq(chatType, peerId, reply.replayMsgSeq?.toInt() ?: 0)?.msgHashId
|
||||
?: MessageHelper.generateMsgIdHash(chatType, reply.sourceMsgIdInRecords)
|
||||
?:
|
||||
kotlin.run {
|
||||
LogCenter.log("消息映射关系未找到: Message($reply)", Level.WARN)
|
||||
MessageHelper.generateMsgIdHash(chatType, reply.sourceMsgIdInRecords)
|
||||
}
|
||||
}
|
||||
|
||||
return MessageSegment(
|
||||
@ -274,23 +319,23 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
||||
element: MsgElement
|
||||
): MessageSegment {
|
||||
val tip = element.grayTipElement
|
||||
when(val tipType = tip.subElementType) {
|
||||
when(tip.subElementType) {
|
||||
MsgConstant.GRAYTIPELEMENTSUBTYPEJSON -> {
|
||||
val notify = tip.jsonGrayTipElement
|
||||
when(notify.busiId) {
|
||||
/* 新人入群 */ 17L,
|
||||
/* 群戳一戳 */1061L, /* 群撤回 */1014L -> {}
|
||||
else -> LogCenter.log("不支持的灰条类型(JSON): $tipType", Level.WARN)
|
||||
/* 新人入群 */ 17L, /* 群戳一戳 */1061L,
|
||||
/* 群撤回 */1014L, /* 群设精消息 */2401L -> {}
|
||||
else -> LogCenter.log("不支持的灰条类型(JSON): ${notify.busiId}", Level.WARN)
|
||||
}
|
||||
}
|
||||
MsgConstant.GRAYTIPELEMENTSUBTYPEXMLMSG -> {
|
||||
val notify = tip.xmlElement
|
||||
when(notify.busiId) {
|
||||
/* 群戳一戳 */12L -> {}
|
||||
else -> LogCenter.log("不支持的灰条类型(XML): $tipType", Level.WARN)
|
||||
/* 群戳一戳 */1061L -> {}
|
||||
else -> LogCenter.log("不支持的灰条类型(XML): ${notify.busiId}", Level.WARN)
|
||||
}
|
||||
}
|
||||
else -> LogCenter.log("不支持的提示类型: $tip", Level.WARN)
|
||||
else -> LogCenter.log("不支持的提示类型: ${tip.subElementType}", Level.WARN)
|
||||
}
|
||||
// 提示类消息,这里提供的是一个xml,不具备解析通用性
|
||||
// 在这里不推送
|
||||
@ -307,8 +352,63 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
||||
peerId: String,
|
||||
element: MsgElement
|
||||
): MessageSegment {
|
||||
// 使用其他地方的推送,而不是使用消息
|
||||
throw UnknownError()
|
||||
val fileMsg = element.fileElement
|
||||
val fileName = fileMsg.fileName
|
||||
val fileSize = fileMsg.fileSize
|
||||
val expireTime = fileMsg.expireTime ?: 0
|
||||
val fileId = fileMsg.fileUuid
|
||||
val bizId = fileMsg.fileBizId ?: 0
|
||||
val fileSubId = fileMsg.fileSubId ?: ""
|
||||
val url = if (chatType == MsgConstant.KCHATTYPEC2C) RichProtoSvc.getC2CFileDownUrl(fileId, fileSubId)
|
||||
else RichProtoSvc.getGroupFileDownUrl(peerId.toLong(), fileId, bizId)
|
||||
|
||||
return MessageSegment(
|
||||
type = "file",
|
||||
data = mapOf(
|
||||
"name" to fileName,
|
||||
"size" to fileSize,
|
||||
"expire" to expireTime,
|
||||
"id" to fileId,
|
||||
"url" to url,
|
||||
"biz" to bizId,
|
||||
"sub" to fileSubId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 老板QQ的合并转发信息
|
||||
*/
|
||||
object XmlMultiMsgConverter: MessageElemConverter() {
|
||||
override suspend fun convert(
|
||||
chatType: Int,
|
||||
peerId: String,
|
||||
element: MsgElement
|
||||
): MessageSegment {
|
||||
val multiMsg = element.multiForwardMsgElement
|
||||
return MessageSegment(
|
||||
type = "forward",
|
||||
data = mapOf(
|
||||
"id" to multiMsg.resId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object XmlLongMsgConverter: MessageElemConverter() {
|
||||
override suspend fun convert(
|
||||
chatType: Int,
|
||||
peerId: String,
|
||||
element: MsgElement
|
||||
): MessageSegment {
|
||||
val longMsg = element.structLongMsgElement
|
||||
return MessageSegment(
|
||||
type = "forward",
|
||||
data = mapOf(
|
||||
"id" to longMsg.resId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import moe.fuqiuluo.shamrock.utils.MD5
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
||||
import mqq.app.AppRuntime
|
||||
@ -73,30 +74,34 @@ internal abstract class FileTransfer {
|
||||
transferRequest: TransferRequest,
|
||||
wait: Boolean
|
||||
): Boolean {
|
||||
val service = runtime.getRuntimeService(ITransFileController::class.java, "all")
|
||||
if(service.transferAsync(transferRequest)) {
|
||||
if (!wait) { // 如果无需等待直接返回
|
||||
return true
|
||||
}
|
||||
return suspendCancellableCoroutine { continuation ->
|
||||
val waiter = GlobalScope.launch {
|
||||
while (
|
||||
service.findProcessor(transferRequest.keyForTransfer) != null
|
||||
// 如果上传处理器依旧存在,说明没有上传成功
|
||||
) {
|
||||
delay(100)
|
||||
return withTimeoutOrNull(60_000) {
|
||||
val service = runtime.getRuntimeService(ITransFileController::class.java, "all")
|
||||
if(service.transferAsync(transferRequest)) {
|
||||
if (!wait) { // 如果无需等待直接返回
|
||||
return@withTimeoutOrNull true
|
||||
}
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
GlobalScope.launch {
|
||||
while (
|
||||
//service.findProcessor(
|
||||
// transferRequest.keyForTransfer // uin + uniseq
|
||||
//) != null
|
||||
service.containsProcessor(runtime.currentAccountUin, transferRequest.mUniseq)
|
||||
// 如果上传处理器依旧存在,说明没有上传成功
|
||||
&& service.isWorking.get()
|
||||
) {
|
||||
delay(100)
|
||||
}
|
||||
continuation.resume(true)
|
||||
}
|
||||
// 实现取消上传器
|
||||
// 目前没什么用
|
||||
continuation.invokeOnCancellation {
|
||||
continuation.resume(false)
|
||||
}
|
||||
continuation.resume(true)
|
||||
}
|
||||
// 实现取消上传器
|
||||
// 目前没什么用
|
||||
continuation.invokeOnCancellation {
|
||||
waiter.cancel()
|
||||
continuation.resume(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
} else true
|
||||
} ?: false
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -63,24 +63,30 @@ internal object RichProtoSvc: BaseSvc() {
|
||||
suspend fun getC2CFileDownUrl(
|
||||
fileId: String,
|
||||
subId: String,
|
||||
retryCnt: Int = 0
|
||||
): String {
|
||||
val uid = ContactHelper.getUidByUinAsync(app.currentUin.toLong())
|
||||
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xe37_1200", 3639, 1200, protobufOf(
|
||||
val buffer = sendOidbAW("OidbSvc.0xe37_1200", 3639, 1200, protobufOf(
|
||||
1 to 1200,
|
||||
2 to 1 /* QRoute.api(IAudioHelperApi::class.java).genDebugSeq().toInt() */, /* seq */
|
||||
14 to mapOf(
|
||||
10 to uid,
|
||||
10 to app.longAccountUin,
|
||||
20 to fileId,
|
||||
30 to 2, /* ver */
|
||||
60 to subId,
|
||||
601 to 0
|
||||
),
|
||||
101 to 3,
|
||||
101 to 3, // uint32_business_id
|
||||
102 to 104, /* client_type */
|
||||
200 to 1, /* url_type */
|
||||
99999 to 90200 to 1
|
||||
).toByteArray(), trpc = true)
|
||||
200 to 1, /* uint32_flag_support_mediaplatform */
|
||||
99999 to mapOf(
|
||||
90200 to 1 // uint32_download_url_type
|
||||
)
|
||||
).toByteArray())
|
||||
|
||||
if (buffer == null) {
|
||||
if (retryCnt < 3) {
|
||||
return getC2CFileDownUrl(fileId, subId, retryCnt + 1)
|
||||
}
|
||||
return ""
|
||||
} else {
|
||||
val body = oidb_sso.OIDBSSOPkg()
|
||||
|
@ -24,12 +24,12 @@ internal object Transfer: FileTransfer() {
|
||||
)
|
||||
|
||||
suspend fun uploadC2CVideo(
|
||||
groupId: String,
|
||||
userId: String,
|
||||
file: File,
|
||||
thumb: File,
|
||||
wait: Boolean = true
|
||||
): Boolean {
|
||||
return transC2CResource(groupId, file, FileMsg.TRANSFILE_TYPE_SHORT_VIDEO_C2C, BUSI_TYPE_SHORT_VIDEO, wait) {
|
||||
return transC2CResource(userId, file, FileMsg.TRANSFILE_TYPE_SHORT_VIDEO_C2C, BUSI_TYPE_SHORT_VIDEO, wait) {
|
||||
it.mSourceVideoCodecFormat = VIDEO_FORMAT_MP4
|
||||
it.mRec = MessageForShortVideo().also {
|
||||
it.busiType = BUSI_TYPE_SHORT_VIDEO
|
||||
@ -56,11 +56,11 @@ internal object Transfer: FileTransfer() {
|
||||
}
|
||||
|
||||
suspend fun uploadC2CVoice(
|
||||
groupId: String,
|
||||
userId: String,
|
||||
file: File,
|
||||
wait: Boolean = true
|
||||
): Boolean {
|
||||
return transC2CResource(groupId, file, FileMsg.TRANSFILE_TYPE_PTT, 1002, wait) {
|
||||
return transC2CResource(userId, file, FileMsg.TRANSFILE_TYPE_PTT, 1002, wait) {
|
||||
it.mPttUploadPanel = 3
|
||||
it.mPttCompressFinish = true
|
||||
it.mIsPttPreSend = true
|
||||
|
@ -12,3 +12,4 @@ internal class LogicException(why: String) : InternalMessageMakerError(why)
|
||||
|
||||
internal object ErrorTokenException : InternalMessageMakerError("access_token error")
|
||||
|
||||
internal class SendMsgException(why: String) : InternalMessageMakerError(why)
|
||||
|
@ -13,6 +13,7 @@ import moe.fuqiuluo.shamrock.utils.FileUtils
|
||||
import moe.fuqiuluo.shamrock.xposed.actions.toast
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.internal.DataRequester
|
||||
import mqq.app.MobileQQ
|
||||
import java.io.File
|
||||
import java.util.Date
|
||||
|
||||
internal enum class Level(
|
||||
@ -26,19 +27,53 @@ internal enum class Level(
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
internal object LogCenter {
|
||||
private val logFileBaseName = MobileQQ.getMobileQQ().qqProcessName.replace(":", ".") + "_${
|
||||
// 格式化时间
|
||||
SimpleDateFormat("yyyy-MM-dd").format(Date())
|
||||
}_"
|
||||
private val LogFile = MobileQQ.getContext().getExternalFilesDir(null)!!
|
||||
.parentFile!!.resolve("Tencent/Shamrock/log").also {
|
||||
if (it.exists()) it.delete()
|
||||
it.mkdirs()
|
||||
}.let {
|
||||
var i = 1
|
||||
lateinit var result: File
|
||||
while (true) {
|
||||
result = it.resolve("$logFileBaseName$i.log")
|
||||
if (result.exists()) {
|
||||
i++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return@let result
|
||||
}
|
||||
.resolve(MobileQQ.getMobileQQ().qqProcessName.replace(":", ".") + "_${
|
||||
// 格式化时间
|
||||
SimpleDateFormat("yyyy-MM-dd").format(Date())
|
||||
}_" + ".log")
|
||||
|
||||
private val format = SimpleDateFormat("[HH:mm:ss] ")
|
||||
|
||||
fun log(string: String, level: Level = Level.INFO, toast: Boolean = false) =
|
||||
log({ string }, level, toast)
|
||||
fun log(string: String, level: Level = Level.INFO, toast: Boolean = false) {
|
||||
if (!ShamrockConfig.isDebug() && level == Level.DEBUG) {
|
||||
return
|
||||
}
|
||||
|
||||
if (toast) {
|
||||
MobileQQ.getContext().toast(string)
|
||||
}
|
||||
// 把日志广播到主进程
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
DataRequester.request("send_message", bodyBuilder = {
|
||||
put("string", string)
|
||||
put("level", level.id)
|
||||
})
|
||||
}
|
||||
|
||||
if (!LogFile.exists()) {
|
||||
LogFile.createNewFile()
|
||||
}
|
||||
val format = "%s%s %s\n".format(format.format(Date()), level.name, string)
|
||||
|
||||
LogFile.appendText(format)
|
||||
}
|
||||
|
||||
fun log(
|
||||
string: () -> String,
|
||||
|
@ -6,6 +6,10 @@ import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
||||
import com.tencent.qqnt.msg.api.IMsgService
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
@ -20,28 +24,100 @@ import moe.fuqiuluo.shamrock.tools.asJsonObjectOrNull
|
||||
import moe.fuqiuluo.shamrock.tools.asString
|
||||
import moe.fuqiuluo.shamrock.tools.json
|
||||
import moe.fuqiuluo.shamrock.tools.jsonArray
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.math.abs
|
||||
|
||||
internal object MessageHelper {
|
||||
suspend fun sendMessageWithoutMsgId(chatType: Int, peerId: String, message: JsonArray, callback: IOperateCallback): Pair<Long, Int> {
|
||||
suspend fun sendMessageWithoutMsgId(
|
||||
chatType: Int,
|
||||
peerId: String,
|
||||
message: String,
|
||||
callback: IOperateCallback,
|
||||
fromId: String = peerId
|
||||
): Pair<Long, Int> {
|
||||
val uniseq = generateMsgId(chatType)
|
||||
val msg = messageArrayToMessageElements(chatType, uniseq.second, peerId, decodeCQCode(message)).also {
|
||||
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
||||
}.second.filter {
|
||||
it.elementType != -1
|
||||
} as ArrayList<MsgElement>
|
||||
return sendMessageWithoutMsgId(chatType, peerId, msg, fromId, callback)
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
suspend fun sendMessageWithoutMsgId(
|
||||
chatType: Int,
|
||||
peerId: String,
|
||||
message: JsonArray,
|
||||
fromId: String = peerId,
|
||||
callback: IOperateCallback
|
||||
): Result<Pair<Long, Int>> {
|
||||
val uniseq = generateMsgId(chatType)
|
||||
var nonMsg: Boolean
|
||||
val msg = messageArrayToMessageElements(chatType, uniseq.second, peerId, message).also {
|
||||
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
||||
}.second.filter {
|
||||
it.elementType != -1
|
||||
}.also {
|
||||
nonMsg = it.isEmpty()
|
||||
} as ArrayList<MsgElement>
|
||||
val totalSize = msg.filter {
|
||||
it.elementType == MsgConstant.KELEMTYPEPIC ||
|
||||
it.elementType == MsgConstant.KELEMTYPEPTT ||
|
||||
it.elementType == MsgConstant.KELEMTYPEVIDEO
|
||||
}.map {
|
||||
(it.picElement?.fileSize ?: 0) + (it.pttElement?.fileSize
|
||||
?: 0) + (it.videoElement?.fileSize ?: 0)
|
||||
}.reduceOrNull { a, b -> a + b } ?: 0
|
||||
|
||||
val estimateTime = (totalSize / (300 * 1024)) * 1000 + 5000
|
||||
lateinit var sendResultPair: Pair<Long, Int>
|
||||
val sendRet = withTimeoutOrNull<Pair<Int, String>>(estimateTime) {
|
||||
suspendCoroutine {
|
||||
GlobalScope.launch {
|
||||
sendResultPair = sendMessageWithoutMsgId(
|
||||
chatType,
|
||||
peerId,
|
||||
msg,
|
||||
fromId
|
||||
) { code, message ->
|
||||
callback.onResult(code, message)
|
||||
it.resume(code to message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sendRet?.first != 0) {
|
||||
return Result.failure(SendMsgException(sendRet?.second ?: "发送消息超时"))
|
||||
}
|
||||
return Result.success(sendResultPair)
|
||||
}
|
||||
|
||||
suspend fun sendMessageWithoutMsgId(
|
||||
chatType: Int,
|
||||
peerId: String,
|
||||
message: ArrayList<MsgElement>,
|
||||
fromId: String = peerId,
|
||||
callback: IOperateCallback
|
||||
): Pair<Long, Int> {
|
||||
return sendMessageWithoutMsgId(generateContact(chatType, peerId, fromId), message, callback)
|
||||
}
|
||||
|
||||
fun sendMessageWithoutMsgId(
|
||||
contact: Contact,
|
||||
message: ArrayList<MsgElement>,
|
||||
callback: IOperateCallback
|
||||
): Pair<Long, Int> {
|
||||
val uniseq = generateMsgId(contact.chatType)
|
||||
val nonMsg: Boolean = message.isEmpty()
|
||||
return if (!nonMsg) {
|
||||
val service = QRoute.api(IMsgService::class.java)
|
||||
if(callback is MsgSvc.MessageCallback) {
|
||||
if (callback is MsgSvc.MessageCallback) {
|
||||
callback.msgHash = uniseq.first
|
||||
}
|
||||
|
||||
service.sendMsg(
|
||||
generateContact(chatType, peerId),
|
||||
contact,
|
||||
uniseq.second,
|
||||
msg as ArrayList<MsgElement>,
|
||||
message,
|
||||
callback
|
||||
)
|
||||
System.currentTimeMillis() to uniseq.first
|
||||
@ -50,15 +126,99 @@ internal object MessageHelper {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendMessageWithMsgId(
|
||||
chatType: Int,
|
||||
peerId: String,
|
||||
message: JsonArray,
|
||||
callback: IOperateCallback,
|
||||
fromId: String = peerId
|
||||
): Pair<Long, Int> {
|
||||
val uniseq = generateMsgId(chatType)
|
||||
val msg = messageArrayToMessageElements(chatType, uniseq.second, peerId, message).also {
|
||||
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
||||
}.second.filter {
|
||||
it.elementType != -1
|
||||
} as ArrayList<MsgElement>
|
||||
val contact = generateContact(chatType, peerId, fromId)
|
||||
val nonMsg: Boolean = message.isEmpty()
|
||||
return if (!nonMsg) {
|
||||
val service = QRoute.api(IMsgService::class.java)
|
||||
if (callback is MsgSvc.MessageCallback) {
|
||||
callback.msgHash = uniseq.first
|
||||
}
|
||||
|
||||
service.sendMsg(
|
||||
contact,
|
||||
uniseq.second,
|
||||
msg,
|
||||
callback
|
||||
)
|
||||
uniseq.second to uniseq.first
|
||||
} else {
|
||||
uniseq.second to 0
|
||||
}
|
||||
}
|
||||
|
||||
fun sendMessageWithMsgId(
|
||||
contact: Contact,
|
||||
message: ArrayList<MsgElement>,
|
||||
callback: IOperateCallback
|
||||
): Pair<Long, Int> {
|
||||
val uniseq = generateMsgId(contact.chatType)
|
||||
val nonMsg: Boolean = message.isEmpty()
|
||||
return if (!nonMsg) {
|
||||
val service = QRoute.api(IMsgService::class.java)
|
||||
if (callback is MsgSvc.MessageCallback) {
|
||||
callback.msgHash = uniseq.first
|
||||
}
|
||||
|
||||
service.sendMsg(
|
||||
contact,
|
||||
uniseq.second,
|
||||
message,
|
||||
callback
|
||||
)
|
||||
uniseq.second to uniseq.first
|
||||
} else {
|
||||
0L to 0
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendMessageNoCb(
|
||||
chatType: Int,
|
||||
peerId: String,
|
||||
message: JsonArray,
|
||||
fromId: String = peerId
|
||||
): Pair<Int, Long> {
|
||||
val uniseq = generateMsgId(chatType)
|
||||
val msg = messageArrayToMessageElements(chatType, uniseq.second, peerId, message).also {
|
||||
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
||||
}.second.filter {
|
||||
it.elementType != -1
|
||||
} as ArrayList<MsgElement>
|
||||
val contact = generateContact(chatType, peerId, fromId)
|
||||
val nonMsg: Boolean = message.isEmpty()
|
||||
return if (!nonMsg) {
|
||||
val service = QRoute.api(IMsgService::class.java)
|
||||
return suspendCoroutine {
|
||||
service.sendMsg(contact, uniseq.second, msg) { code, why ->
|
||||
it.resume(code to uniseq.second)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
-1 to uniseq.second
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun generateContact(chatType: Int, id: String, subId: String = ""): Contact {
|
||||
val peerId = if (MsgConstant.KCHATTYPEC2C == chatType) {
|
||||
val peerId = if (MsgConstant.KCHATTYPEC2C == chatType || MsgConstant.KCHATTYPETEMPC2CFROMGROUP == chatType) {
|
||||
ContactHelper.getUidByUinAsync(id.toLong())
|
||||
} else id
|
||||
return Contact(chatType, peerId, subId)
|
||||
}
|
||||
|
||||
fun obtainMessageTypeByDetailType(detailType: String): Int {
|
||||
return when(detailType) {
|
||||
return when (detailType) {
|
||||
"troop", "group" -> MsgConstant.KCHATTYPEGROUP
|
||||
"private" -> MsgConstant.KCHATTYPEC2C
|
||||
"less" -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
|
||||
@ -68,7 +228,7 @@ internal object MessageHelper {
|
||||
}
|
||||
|
||||
fun obtainDetailTypeByMsgType(msgType: Int): String {
|
||||
return when(msgType) {
|
||||
return when (msgType) {
|
||||
MsgConstant.KCHATTYPEGROUP -> "group"
|
||||
MsgConstant.KCHATTYPEC2C -> "private"
|
||||
MsgConstant.KCHATTYPEGUILD -> "guild"
|
||||
@ -82,9 +242,9 @@ internal object MessageHelper {
|
||||
var hasActionMsg = false
|
||||
messageList.forEach {
|
||||
val msg = it.jsonObject
|
||||
try {
|
||||
val maker = MessageMaker[msg["type"].asString]
|
||||
if (maker != null) {
|
||||
val maker = MessageMaker[msg["type"].asString]
|
||||
if (maker != null) {
|
||||
try {
|
||||
val data = msg["data"].asJsonObjectOrNull ?: EmptyJsonObject
|
||||
maker(chatType, msgId, targetUin, data).onSuccess { msgElem ->
|
||||
msgList.add(msgElem)
|
||||
@ -95,18 +255,22 @@ internal object MessageHelper {
|
||||
hasActionMsg = true
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
LogCenter.log(e.stackTraceToString(), Level.ERROR)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
LogCenter.log(e.stackTraceToString(), Level.ERROR)
|
||||
} else {
|
||||
LogCenter.log("不支持的消息类型: ${msg["type"].asString}", Level.ERROR)
|
||||
return false to arrayListOf()
|
||||
}
|
||||
}
|
||||
return hasActionMsg to msgList
|
||||
}
|
||||
|
||||
fun generateMsgIdHash(chatType: Int, msgId: Long): Int {
|
||||
val key = when (chatType) {
|
||||
val key = when (chatType) {
|
||||
MsgConstant.KCHATTYPEGROUP -> "grp$msgId"
|
||||
MsgConstant.KCHATTYPEC2C -> "c2c$msgId"
|
||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> "tmpgrp$msgId"
|
||||
else -> error("不支持的消息来源类型 | generateMsgIdHash: $chatType")
|
||||
}
|
||||
return abs(key.hashCode())
|
||||
|
@ -8,8 +8,10 @@ import moe.fuqiuluo.qqinterface.servlet.ark.ArkMsgSvc
|
||||
import moe.fuqiuluo.shamrock.tools.GlobalClient
|
||||
import moe.fuqiuluo.shamrock.tools.asInt
|
||||
import moe.fuqiuluo.shamrock.tools.asJsonArray
|
||||
import moe.fuqiuluo.shamrock.tools.asJsonArrayOrNull
|
||||
import moe.fuqiuluo.shamrock.tools.asJsonObject
|
||||
import moe.fuqiuluo.shamrock.tools.asString
|
||||
import moe.fuqiuluo.shamrock.tools.asStringOrNull
|
||||
import moe.fuqiuluo.shamrock.utils.MD5
|
||||
|
||||
internal object MusicHelper {
|
||||
@ -53,12 +55,26 @@ internal object MusicHelper {
|
||||
val trackInfo = data["track_info"].asJsonObject
|
||||
val mid = trackInfo["mid"].asString
|
||||
val previewMid = trackInfo["album"].asJsonObject["mid"].asString
|
||||
val singerMid = trackInfo["singer"].asJsonArrayOrNull?.let {
|
||||
it[0].asJsonObject["mid"].asStringOrNull
|
||||
} ?: ""
|
||||
val name = trackInfo["name"].asString
|
||||
val title = trackInfo["title"].asString
|
||||
val singerName = trackInfo["singer"].asJsonArray.first().asJsonObject["name"].asString
|
||||
val vs = trackInfo["vs"].asJsonArrayOrNull?.let {
|
||||
it[0].asStringOrNull
|
||||
} ?: ""
|
||||
val code = MD5.getMd5Hex("${mid}q;z(&l~sdf2!nK".toByteArray()).substring(0 .. 4).uppercase()
|
||||
val playUrl = "http://c6.y.qq.com/rsc/fcgi-bin/fcg_pyq_play.fcg?songid=&songmid=$mid&songtype=1&fromtag=50&uin=&code=$code"
|
||||
val previewUrl = "http://y.gtimg.cn/music/photo_new/T002R180x180M000$previewMid.jpg"
|
||||
val previewUrl = if (vs.isNotEmpty()) {
|
||||
"http://y.gtimg.cn/music/photo_new/T062R150x150M000$vs}.jpg"
|
||||
} else if (previewMid.isNotEmpty()) {
|
||||
"http://y.gtimg.cn/music/photo_new/T002R150x150M000$previewMid.jpg"
|
||||
} else if (singerMid.isNotEmpty()){
|
||||
"http://y.gtimg.cn/music/photo_new/T001R150x150M000$singerMid.jpg"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
val jumpUrl = "https://i.y.qq.com/v8/playsong.html?platform=11&appshare=android_qq&appversion=10030010&hosteuin=oKnlNenz7i-s7c**&songmid=${mid}&type=0&appsongtype=1&_wv=1&source=qq&ADTAG=qfshare"
|
||||
ArkMsgSvc.tryShareMusic(
|
||||
chatType,
|
||||
|
@ -29,6 +29,9 @@ interface MessageMappingDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insert(mapping: MessageMapping)
|
||||
|
||||
@Query("UPDATE message_mapping SET msgSeq = :msgSeq WHERE msgHashId = :hash")
|
||||
fun updateMsgSeqByMsgHash(hash: Int, msgSeq: Int)
|
||||
|
||||
@Query("DELETE FROM message_mapping WHERE msgHashId = :hash")
|
||||
fun deleteByMsgHash(hash: Int)
|
||||
|
||||
|
@ -62,11 +62,17 @@ internal object HTTPServer {
|
||||
weatherAction()
|
||||
otherAction()
|
||||
guildAction()
|
||||
testAction()
|
||||
requestRouter()
|
||||
if (ShamrockConfig.isDev()) {
|
||||
qsign()
|
||||
obtainProtocolData()
|
||||
}
|
||||
}
|
||||
|
||||
// intercept(ApplicationCallPipeline.Plugins) {
|
||||
// call.response.headers.appendIfAbsent("Content-Type", ContentType.Application.Json.toString())
|
||||
// }
|
||||
}
|
||||
|
||||
private fun ApplicationEngineEnvironmentBuilder.configSSL() {
|
||||
|
@ -18,6 +18,7 @@ internal object ActionManager {
|
||||
arrayOf(
|
||||
// Framework Info
|
||||
TestHandler, GetLatestEvents, GetSupportedActions, GetStatus, GetVersionInfo, GetSelfInfo, GetLoginInfo,
|
||||
SwitchAccount,
|
||||
|
||||
// UserActions
|
||||
GetProfileCard, GetFriendList, SendLike, GetUid, GetUinByUid, ScanQRCode, SetProfileCard,
|
||||
@ -29,16 +30,21 @@ internal object ActionManager {
|
||||
|
||||
// GroupActions
|
||||
ModifyTroopName, LeaveTroop, KickTroopMember, BanTroopMember, SetGroupWholeBan, SetGroupAdmin,
|
||||
ModifyTroopMemberName, SetGroupUnique, GetTroopHonor, GroupPoke,
|
||||
ModifyTroopMemberName, SetGroupUnique, GetTroopHonor, GroupPoke, SetEssenceMessage, DeleteEssenceMessage,
|
||||
GetGroupSystemMsg, GetProhibitedMemberList, GetEssenceMessageList, GetGroupNotice, SendGroupNotice,
|
||||
|
||||
// MSG ACTIONS
|
||||
SendMessage, DeleteMessage, GetMsg, GetForwardMsg, SendGroupForwardMsg, SendGroupMessage, SendPrivateMessage,
|
||||
SendMessage, DeleteMessage, GetMsg, GetForwardMsg, SendPrivateForwardMessage, SendGroupMessage, SendPrivateMessage,
|
||||
ClearMsgs, GetHistoryMsg, GetGroupMsgHistory, SendGroupForwardMessage,
|
||||
|
||||
// RESOURCE ACTION
|
||||
GetRecord, GetImage, UploadGroupFile, CreateGroupFileFolder, DeleteGroupFolder,
|
||||
DeleteGroupFile, GetGroupFileSystemInfo, GetGroupRootFiles, GetGroupSubFiles,
|
||||
GetGroupFileUrl, UploadPrivateFile,
|
||||
|
||||
//REQUEST ACTION
|
||||
SetFriendAddRequest, SetGroupAddRequest,
|
||||
|
||||
// GUILD
|
||||
GetGuildServiceProfile,
|
||||
|
||||
@ -97,16 +103,16 @@ internal abstract class IActionHandler {
|
||||
return failed(Status.BadParam, why, echo)
|
||||
}
|
||||
|
||||
protected fun error(why: String, echo: JsonElement): String {
|
||||
return failed(Status.InternalHandlerError, why, echo)
|
||||
protected fun error(why: String, echo: JsonElement, arrayResult: Boolean = false): String {
|
||||
return failed(Status.InternalHandlerError, why, echo, arrayResult)
|
||||
}
|
||||
|
||||
protected fun logic(why: String, echo: JsonElement): String {
|
||||
return failed(Status.LogicError, why, echo)
|
||||
protected fun logic(why: String, echo: JsonElement, arraayResult: Boolean = false): String {
|
||||
return failed(Status.LogicError, why, echo, arraayResult)
|
||||
}
|
||||
|
||||
protected fun failed(status: Status, msg: String, echo: JsonElement): String {
|
||||
return resultToString(false, status, EmptyObject, msg, echo = echo)
|
||||
protected fun failed(status: Status, msg: String, echo: JsonElement, arrResult: Boolean = false): String {
|
||||
return resultToString(false, status, if (arrResult) EmptyJsonArray else EmptyJsonObject, msg, echo = echo)
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,8 +193,8 @@ internal class ActionSession {
|
||||
return params[key].asBoolean
|
||||
}
|
||||
|
||||
fun <T: Boolean?> getBooleanOrDefault(key: String, default: T? = null): T {
|
||||
return (params[key].asBooleanOrNull as? T) ?: default as T
|
||||
fun getBooleanOrDefault(key: String, default: Boolean? = null): Boolean {
|
||||
return params[key].asBooleanOrNull ?: default as Boolean
|
||||
}
|
||||
|
||||
fun getObject(key: String): JsonObject {
|
||||
|
@ -0,0 +1,32 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
|
||||
|
||||
internal object ClearMsgs: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
val msgType = session.getString("message_type")
|
||||
val peerId = session.getString(if (msgType == "group") "group_id" else "user_id")
|
||||
return invoke(msgType, peerId, session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(
|
||||
msgType: String,
|
||||
peerId: String,
|
||||
echo: JsonElement = EmptyJsonString
|
||||
): String {
|
||||
val chatType = MessageHelper.obtainMessageTypeByDetailType(msgType)
|
||||
val contact = MessageHelper.generateContact(chatType, peerId, "")
|
||||
NTServiceFetcher.kernelService.wrapperSession.msgService.clearMsgRecords(contact, null)
|
||||
return ok(echo = echo)
|
||||
}
|
||||
|
||||
override val requiredParams: Array<String>
|
||||
get() = arrayOf("message_type")
|
||||
|
||||
override fun path(): String = "clear_msgs"
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
||||
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
|
||||
internal object DeleteEssenceMessage: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
val messageId = session.getInt("message_id")
|
||||
return invoke(messageId, session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(messageId: Int, echo: JsonElement = EmptyJsonString): String {
|
||||
val msg = MsgSvc.getMsg(messageId).onFailure {
|
||||
return logic("Obtain msg failed, please check your msg_id.", echo)
|
||||
}.getOrThrow()
|
||||
val (success, tip) = GroupSvc.deleteEssenceMessage(
|
||||
if (msg.chatType == MsgConstant.KCHATTYPEGROUP) msg.peerUin else 0,
|
||||
msg.msgSeq,
|
||||
msg.msgRandom
|
||||
)
|
||||
return if (success) {
|
||||
ok("成功", echo)
|
||||
} else {
|
||||
logic(tip, echo)
|
||||
}
|
||||
}
|
||||
|
||||
override val alias: Array<String> = arrayOf("delete_essence_message")
|
||||
override fun path(): String = "delete_essence_msg"
|
||||
}
|
@ -14,10 +14,30 @@ internal object DeleteGroupFile: IActionHandler() {
|
||||
return invoke(groupId, fileId, busid, session.echo)
|
||||
}
|
||||
|
||||
/*
|
||||
suspend operator fun invoke(
|
||||
groupId: String,
|
||||
fileId: String,
|
||||
bizId: Int,
|
||||
echo: JsonElement = EmptyJsonString
|
||||
): String {
|
||||
val result = FileSvc.deleteGroupFile(groupId, bizId, fileId)
|
||||
if(result.isFailure) {
|
||||
return error(result.exceptionOrNull()?.message ?: "删除群文件失败", echo)
|
||||
}
|
||||
val commonResult = result.getOrThrow()
|
||||
if (commonResult.first != 0 || commonResult.second.retCode != 0) {
|
||||
return error(commonResult.second.clientWording, echo)
|
||||
}
|
||||
return ok("成功", echo)
|
||||
}
|
||||
*/
|
||||
operator fun invoke(groupId: String, fileId: String, bizId: Int, echo: JsonElement = EmptyJsonString): String {
|
||||
FileSvc.deleteGroupFile(groupId, bizId, fileId)
|
||||
return ok("成功", echo)
|
||||
}
|
||||
|
||||
override val requiredParams: Array<String> = arrayOf("group_id", "file_id", "busid")
|
||||
|
||||
override fun path(): String = "delete_group_file"
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import android.util.Base64
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
@ -13,35 +14,72 @@ import moe.fuqiuluo.shamrock.utils.MD5
|
||||
|
||||
internal object DownloadFile: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
val url = session.getString("url")
|
||||
val url = session.getStringOrNull("url")
|
||||
val name = session.getStringOrNull("name")
|
||||
val b64 = session.getStringOrNull("base64")
|
||||
val threadCnt = session.getIntOrNull("thread_cnt") ?: 3
|
||||
val headers = if (session.isArray("headers")) {
|
||||
val headers = if (session.has("headers")) (if (session.isArray("headers")) {
|
||||
session.getArray("headers").map {
|
||||
it.asString
|
||||
}
|
||||
} else {
|
||||
session.getString("headers").split("\r\n")
|
||||
}
|
||||
return invoke(url, threadCnt, headers, session.echo)
|
||||
}) else emptyList()
|
||||
return invoke(url, b64, threadCnt, headers, name, session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(
|
||||
url: String,
|
||||
url: String?,
|
||||
base64: String?,
|
||||
threadCnt: Int,
|
||||
headers: List<String>,
|
||||
name: String?,
|
||||
echo: JsonElement = EmptyJsonString
|
||||
): String {
|
||||
val headerMap = mutableMapOf(
|
||||
"User-Agent" to "Shamrock"
|
||||
)
|
||||
headers.forEach {
|
||||
val pair = it.split("=")
|
||||
if (pair.size >= 2) {
|
||||
val (k, v) = pair
|
||||
headerMap[k] = v
|
||||
if (url != null) {
|
||||
val headerMap = mutableMapOf(
|
||||
"User-Agent" to "Shamrock"
|
||||
)
|
||||
headers.forEach {
|
||||
val pair = it.split("=")
|
||||
if (pair.size >= 2) {
|
||||
val (k, v) = pair
|
||||
headerMap[k] = v
|
||||
}
|
||||
}
|
||||
return invoke(url, threadCnt, headerMap, echo)
|
||||
} else if (base64 != null) {
|
||||
return invoke(base64, name, echo)
|
||||
} else {
|
||||
return noParam("url/base64", echo)
|
||||
}
|
||||
return invoke(url, threadCnt, headerMap, echo)
|
||||
}
|
||||
|
||||
operator fun invoke(
|
||||
base64: String,
|
||||
name: String?,
|
||||
echo: JsonElement
|
||||
): String {
|
||||
kotlin.runCatching {
|
||||
val bytes = Base64.decode(base64, Base64.DEFAULT)
|
||||
FileUtils.getTmpFile("cache").also {
|
||||
it.writeBytes(bytes)
|
||||
}
|
||||
}.onSuccess {
|
||||
val tmp = if (name == null)
|
||||
FileUtils.renameByMd5(it)
|
||||
else it.parentFile!!.resolve(name).also { target ->
|
||||
it.renameTo(target)
|
||||
it.delete()
|
||||
}
|
||||
return ok(data = DownloadResult(
|
||||
file = tmp.absolutePath,
|
||||
md5 = MD5.genFileMd5Hex(tmp.absolutePath)
|
||||
), msg = "成功", echo = echo)
|
||||
}.onFailure {
|
||||
return logic("Base64格式错误", echo)
|
||||
}
|
||||
return logic("未知错误", echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(
|
||||
@ -70,8 +108,6 @@ internal object DownloadFile: IActionHandler() {
|
||||
}
|
||||
}
|
||||
|
||||
override val requiredParams: Array<String> = arrayOf("url")
|
||||
|
||||
override fun path(): String = "download_file"
|
||||
|
||||
@Serializable
|
||||
|
@ -17,7 +17,7 @@ internal object GetCSRF: IActionHandler() {
|
||||
suspend operator fun invoke(domain: String, echo: JsonElement = EmptyJsonString): String {
|
||||
val uin = TicketSvc.getUin()
|
||||
val pskey = TicketSvc.getPSKey(uin, domain)
|
||||
?: return invoke()
|
||||
?: return invoke(echo)
|
||||
return ok(Credentials(bkn = TicketSvc.getCSRF(pskey)), echo)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,33 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
||||
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
|
||||
internal object GetEssenceMessageList: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
val groupId = session.getLong("group_id")
|
||||
val page = session.getIntOrNull("page") ?: 0
|
||||
val pageSize = session.getIntOrNull("page_size") ?: 20
|
||||
return invoke(groupId, page, pageSize, session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(groupId: Long, page: Int = 0, pageSize: Int = 20, echo: JsonElement = EmptyJsonString): String {
|
||||
if (page < 0 || pageSize > 50) {
|
||||
return badParam("参数不正确:page_size不得大于50", echo)
|
||||
}
|
||||
val essenceMessageList = GroupSvc.getEssenceMessageList(groupId, page, pageSize)
|
||||
if (essenceMessageList.isSuccess) {
|
||||
return ok(essenceMessageList.getOrNull(), echo)
|
||||
}
|
||||
return logic(essenceMessageList.exceptionOrNull()?.message ?: "", echo)
|
||||
|
||||
}
|
||||
|
||||
override val alias: Array<String> = arrayOf("get_essence_message_list")
|
||||
override fun path(): String = "get_essence_msg_list"
|
||||
}
|
@ -1,20 +1,61 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||
import moe.fuqiuluo.qqinterface.servlet.msg.convert.MessageConvert
|
||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
|
||||
import moe.fuqiuluo.shamrock.remote.service.data.MessageDetail
|
||||
import moe.fuqiuluo.shamrock.remote.service.data.MessageSender
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
|
||||
internal object GetForwardMsg: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
val id = session.getString("id")
|
||||
|
||||
val kernelService = NTServiceFetcher.kernelService
|
||||
val sessionService = kernelService.wrapperSession
|
||||
val msgService = sessionService.msgService
|
||||
|
||||
return error("不支持实现,请提交ISSUE!", session.echo)
|
||||
return invoke(id, session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(
|
||||
resId: String,
|
||||
echo: JsonElement = EmptyJsonString
|
||||
): String {
|
||||
val result = MsgSvc.getMultiMsg(resId)
|
||||
if (result.isFailure) {
|
||||
return logic(result.exceptionOrNull().toString(), echo)
|
||||
}
|
||||
|
||||
return ok(data = GetForwardMsgResult(result.getOrThrow().map { msg ->
|
||||
val msgHash = MessageHelper.generateMsgIdHash(msg.chatType, msg.msgId)
|
||||
MessageDetail(
|
||||
time = msg.msgTime.toInt(),
|
||||
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
|
||||
msgId = msgHash,
|
||||
realId = msg.msgSeq.toInt(),
|
||||
sender = MessageSender(
|
||||
msg.senderUin, msg.sendNickName
|
||||
.ifBlank { msg.sendMemberName }
|
||||
.ifBlank { msg.sendRemarkName }
|
||||
.ifBlank { msg.peerName }, "unknown", 0, msg.senderUid
|
||||
),
|
||||
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
||||
it.toJson()
|
||||
},
|
||||
peerId = msg.peerUin,
|
||||
groupId = if (msg.chatType == MsgConstant.KCHATTYPEGROUP) msg.peerUin else 0,
|
||||
targetId = if (msg.chatType != MsgConstant.KCHATTYPEGROUP) msg.peerUin else 0
|
||||
)
|
||||
}), echo = echo)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class GetForwardMsgResult(
|
||||
@SerialName("messages") val msgs: List<MessageDetail>
|
||||
)
|
||||
|
||||
override val requiredParams: Array<String> = arrayOf("id")
|
||||
|
||||
override fun path(): String = "get_forward_msg"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user