mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 05:12:17 +00:00
Compare commits
161 Commits
1.0.6
...
64b7b00253
Author | SHA1 | Date | |
---|---|---|---|
64b7b00253 | |||
ec132d224e | |||
9c9fbfa753 | |||
4b5932b319 | |||
0c91028469 | |||
48b720bdd7 | |||
2038d81ce8 | |||
81be383b5f | |||
0fb88e3e44 | |||
e92b04ad0f | |||
160d1a11ac | |||
b9cfe73eae | |||
8d6d984849 | |||
25fe9fab37 | |||
ba7058a838 | |||
0858395e60 | |||
fdd769d9ff | |||
f47ae69653 | |||
f311ae3797 | |||
5a941f889f | |||
69e50c6f93 | |||
f7ac3a5d23 | |||
b6b54a805e | |||
131f56a468 | |||
e45e9e7fa0 | |||
cac0aad1f2 | |||
2c1bd9e726 | |||
2645e8f451 | |||
e92c227bba | |||
a31fe92c0b | |||
70cb876439 | |||
9aa4c37354 | |||
d44150ea1a | |||
0360c81bee | |||
cb904c1f1c | |||
bc754db959 | |||
8df799a6e4 | |||
71dd9469ca | |||
79788d2cdc | |||
c28c9981c4 | |||
8fadd0016a | |||
77504d68fd | |||
1490450178 | |||
88beaf8b6f | |||
df25b0bc76 | |||
d07eea7766 | |||
5000453002 | |||
35c82fcc51 | |||
89a4912ed7 | |||
aeabc66067 | |||
ccbfc9a1e1 | |||
31936feb98 | |||
538db69754 | |||
25ea5bd6a8 | |||
460cd84258 | |||
3f9613c43c | |||
34eccda233 | |||
741d2c7a84 | |||
6ee5ceb321 | |||
6107ec6ffb | |||
4ef014a8ac | |||
135a7c2f56 | |||
420f11784d | |||
951e7462c4 | |||
1b0550b5e1 | |||
919c4a7d80 | |||
dcb2b0a26f | |||
d388e5df0c | |||
5776524579 | |||
1d0a0731fb | |||
4dafa75944 | |||
85cdc86b07 | |||
cd19426d1b | |||
48b1b40e1c | |||
50d469cc45 | |||
37f74d5284 | |||
adb7b12c16 | |||
27791cc848 | |||
bd45523e25 | |||
c0f2242679 | |||
c309a2b3ed | |||
48c9048a00 | |||
4d2f7a794b | |||
ab6e431872 | |||
9423df2670 | |||
a2b3e42eee | |||
d9a045bbf0 | |||
42ca17339e | |||
003c4d4456 | |||
ec39aa7bc3 | |||
4e3870a512 | |||
97534b01a6 | |||
61bed61bfb | |||
a0ff4782db | |||
41dd1de8f8 | |||
fa6634d6af | |||
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 |
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 相关的,且可以实现.
|
||||
|
||||
|
65
.github/workflows/build-apk.yml
vendored
65
.github/workflows/build-apk.yml
vendored
@ -13,24 +13,38 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build APK
|
||||
name: Build Shamrock
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: "temurin"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup JDK 17
|
||||
uses: actions/setup-java@v4.0.0
|
||||
with:
|
||||
java-version: 17
|
||||
cache: 'gradle'
|
||||
distribution: "adopt"
|
||||
|
||||
- name: Setup cmake
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "cmake;3.22.1" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
echo "sdk.dir=${ANDROID_HOME}" > local.properties
|
||||
- name: Cache Gradle Dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
!~/.gradle/caches/build-cache-*
|
||||
key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }}
|
||||
restore-keys: gradle-deps
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.9.0
|
||||
- name: Cache Gradle Build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches/build-cache-*
|
||||
~/.gradle/buildOutputCleanup/cache.properties
|
||||
key: gradle-builds-core-${{ github.sha }}
|
||||
restore-keys: gradle-builds
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
@ -46,42 +60,41 @@ jobs:
|
||||
KEY_ALIAS: ${{ secrets.SIGN_ALIAS }}
|
||||
KEY_PASSWORD: ${{ secrets.SIGN_KEY_PASSWORD }}
|
||||
|
||||
- name: Install aapt
|
||||
run: sudo apt-get update && sudo apt-get install -y aapt
|
||||
|
||||
- name: Set Shamrock Version
|
||||
run: |
|
||||
apk_file=${{ env.APK_FILE_ALL }}
|
||||
apk_dump=$(aapt dump badging "$apk_file")
|
||||
version_name=$(sed -n "s/.*versionName='\([^']*\)'.*/\1/p" <<< "$apk_dump")
|
||||
echo "SHAMROCK_VERSION=$version_name" >> $GITHUB_ENV
|
||||
version_name_all=$(basename -s .apk "${{ env.APK_FILE_ALL }}")
|
||||
version_name_arm64=$(basename -s .apk "${{ env.APK_FILE_ARM64 }}")
|
||||
version_name_x86_64=$(basename -s .apk "${{ env.APK_FILE_X86_64 }}")
|
||||
echo "SHAMROCK_VERSION_ALL=$version_name_all" >> $GITHUB_ENV
|
||||
echo "SHAMROCK_VERSION_ARM64=$version_name_arm64" >> $GITHUB_ENV
|
||||
echo "SHAMROCK_VERSION_x86_64=$version_name_x86_64" >> $GITHUB_ENV
|
||||
|
||||
- name: Show Artifacts SHA256
|
||||
run: |
|
||||
echo "### Build Success :rocket:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|ABI|SHA256|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|:--------:|:----------|" >> $GITHUB_STEP_SUMMARY
|
||||
all=($(sha256sum ${{ env.APK_FILE_ALL }}))
|
||||
all=($(sha256sum "${{ env.APK_FILE_ALL }}"))
|
||||
echo "|all|$all" >> $GITHUB_STEP_SUMMARY
|
||||
arm64=($(sha256sum ${{ env.APK_FILE_ARM64 }}))
|
||||
arm64=($(sha256sum "${{ env.APK_FILE_ARM64 }}"))
|
||||
echo "|arm64|$arm64" >> $GITHUB_STEP_SUMMARY
|
||||
x86_64=($(sha256sum ${{ env.APK_FILE_X86_64 }}))
|
||||
x86_64=($(sha256sum "${{ env.APK_FILE_X86_64 }}"))
|
||||
echo "|x86_64|$x86_64" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Upload ALL APK RELEASE
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Shamrock-v${{ env.SHAMROCK_VERSION }}-all
|
||||
path: ${{ env.APK_FILE_ALL }}
|
||||
name: "${{ env.SHAMROCK_VERSION_ALL }}"
|
||||
path: "${{ env.APK_FILE_ALL }}"
|
||||
|
||||
- name: Upload ARM64 APK RELEASE
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Shamrock-v${{ env.SHAMROCK_VERSION }}-arm64
|
||||
path: ${{ env.APK_FILE_ARM64 }}
|
||||
name: "${{ env.SHAMROCK_VERSION_ARM64 }}"
|
||||
path: "${{ env.APK_FILE_ARM64 }}"
|
||||
|
||||
- name: Upload X86_64 APK RELEASE
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Shamrock-v${{ env.SHAMROCK_VERSION }}-x86_64
|
||||
path: ${{ env.APK_FILE_X86_64 }}
|
||||
name: "${{ env.SHAMROCK_VERSION_x86_64 }}"
|
||||
path: "${{ env.APK_FILE_X86_64 }}"
|
57
.github/workflows/codeql.yml
vendored
57
.github/workflows/codeql.yml
vendored
@ -1,57 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: '24 7 * * 4'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 360
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp', 'java' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.6.0
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Setup JDK 17
|
||||
uses: actions/setup-java@v3.12.0
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.8.0
|
||||
|
||||
- name: Setup cmake
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "cmake;3.22.1" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
echo "sdk.dir=${ANDROID_HOME}" > local.properties
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
chmod +x ./gradlew
|
||||
./gradlew :app:assembleAppRelease
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
73
.github/workflows/release.yml
vendored
73
.github/workflows/release.yml
vendored
@ -1,73 +0,0 @@
|
||||
name: Shamrock Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: 17
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Setup cmake
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "cmake;3.22.1" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
echo "sdk.dir=${ANDROID_HOME}" > local.properties
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.9.0
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
echo ${{ secrets.SIGN_KEYSTORE_BASE64 }} | base64 -d > keystore.jks
|
||||
chmod +x ./gradlew
|
||||
./gradlew :app:assembleRelease --build-cache --parallel --daemon --warning-mode all --stacktrace
|
||||
echo "APK_FILE_ALL=$(find app/build/outputs/apk/app/release -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "APK_FILE_ARM64=$(find app/build/outputs/apk/arm64/release -name '*.apk')" >> $GITHUB_ENV
|
||||
echo "APK_FILE_X86_64=$(find app/build/outputs/apk/x64/release -name '*.apk')" >> $GITHUB_ENV
|
||||
env:
|
||||
KEYSTORE_PATH: "../keystore.jks"
|
||||
KEYSTORE_PASSWORD: ${{ secrets.SIGN_KEYSTORE_PASSWORD }}
|
||||
KEY_ALIAS: ${{ secrets.SIGN_ALIAS }}
|
||||
KEY_PASSWORD: ${{ secrets.SIGN_KEY_PASSWORD }}
|
||||
|
||||
- name: Install aapt
|
||||
run: sudo apt-get update && sudo apt-get install -y aapt
|
||||
|
||||
- name: Set Shamrock Version
|
||||
run: |
|
||||
apk_file=${{ env.APK_FILE_ALL }}
|
||||
apk_dump=$(aapt dump badging "$apk_file")
|
||||
version_name=$(sed -n "s/.*versionName='\([^']*\)'.*/\1/p" <<< "$apk_dump")
|
||||
echo "SHAMROCK_VERSION=$version_name" >> $GITHUB_ENV
|
||||
|
||||
- name: Show Artifacts SHA256
|
||||
run: |
|
||||
echo "### Build Success :rocket:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|ABI|SHA256|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|:--------:|:----------|" >> $GITHUB_STEP_SUMMARY
|
||||
all=($(sha256sum ${{ env.APK_FILE_ALL }}))
|
||||
echo "|all|$all" >> $GITHUB_STEP_SUMMARY
|
||||
arm64=($(sha256sum ${{ env.APK_FILE_ARM64 }}))
|
||||
echo "|arm64|$arm64" >> $GITHUB_STEP_SUMMARY
|
||||
x86_64=($(sha256sum ${{ env.APK_FILE_X86_64 }}))
|
||||
echo "|x86_64|$x86_64" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Release
|
||||
uses: marvinpinto/action-automatic-releases@latest
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
automatic_release_tag: Shamrock v${{ env.SHAMROCK_VERSION }}
|
||||
files: |
|
||||
${{ env.APK_FILE_ALL }}
|
||||
${{ env.APK_FILE_ARM64 }}
|
||||
${{ env.APK_FILE_X86_64 }}
|
13
README.md
13
README.md
@ -10,16 +10,17 @@
|
||||
![][onebot-12]
|
||||
[![][license]](LICENSE)
|
||||
|
||||
[下载][download-link] | [部署][deploy-link] | [接口][api-link] | [文档][docs-link] | [加群][group-link]
|
||||
[下载][download-link] | [部署][deploy-link] | [接口][api-link] | [文档][docs-link]
|
||||
|
||||
</div>
|
||||
|
||||
## 简介
|
||||
|
||||
☘ 基于 Xposed 实现 OneBot 标准的 QQ 机器人框架,原作者[**fuqiuluo**](https://github.com/fuqiuluo)已脱离开发,接下来由白池接手哦!本项目为OpenShamrock,不会有任何收费行为,欢迎大家的加入!
|
||||
☘ 基于 Lsposed(**Non**-Riru) 实现 OneBot 标准的 QQ 机器人框架,原作者[**fuqiuluo**](https://github.com/fuqiuluo)已脱离开发,接下来由白池接手哦!本项目为OpenShamrock,不会有任何收费行为,欢迎大家的加入!
|
||||
|
||||
> 本项目仅提供学习与交流用途,请在24小时内删除。
|
||||
> 本项目目的是研究 Xposed 和 LSPosed 框架的使用。 Epic 框架开发相关知识。
|
||||
> Riru可能导致封禁,请减少使用。
|
||||
> 如有违反法律,请联系删除。
|
||||
> 请勿在任何平台宣传,宣扬,转发本项目,请勿恶意修改企业安装包造成相关企业产生损失,如有违背,必将追责到底。
|
||||
> 官方论坛,[点我直达](https://forum.libfekit.so/)!
|
||||
@ -29,8 +30,6 @@
|
||||
- 一键移植:本项目基于 go-cqhttp 的文档进行开发实现。
|
||||
- 平行部署:可多平台部署,未来将会支持 Docker 部署的教程。
|
||||
|
||||
> 若您追求小而轻便的Bot服务, [Chronocat](https://chronocat.vercel.app/)是您的不二之选。
|
||||
|
||||
## 权限声明
|
||||
|
||||
> 如出现未在此处声明的权限,请警惕 Shamrock 是否被修改/植入恶意代码
|
||||
@ -45,7 +44,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"> **我可爱吗?欢迎你的到来,这里是一个很大的地方,有着无限可能,主要是有你啦!**
|
||||
|
||||
## 开源协议
|
||||
|
||||
@ -96,9 +95,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
[docs-link]: https://whitechi73.github.io/OpenShamrock/
|
||||
|
||||
[group-link]: https://whitechi73.github.io/OpenShamrock/group.html
|
||||
|
||||
[hook-system]: https://github.com/whitechi73/OpenShamrock/wiki/perm_hook_android
|
||||
[hook-system]: https://github.com/whitechi73/OpenShamrock/blob/master/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/loader/KeepAlive.kt
|
||||
|
||||
[voice-support]: https://whitechi73.github.io/OpenShamrock/advanced/voice.html
|
||||
|
||||
|
@ -1,12 +1,5 @@
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
|
||||
fun gitCommitHash(): String {
|
||||
val builder = ProcessBuilder("git", "rev-parse", "--short", "HEAD")
|
||||
val process = builder.start()
|
||||
val reader = process.inputReader()
|
||||
val hash = reader.readText().trim()
|
||||
return if (hash.isNotEmpty()) ".$hash" else ""
|
||||
}
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
@ -17,19 +10,20 @@ plugins {
|
||||
android {
|
||||
namespace = "moe.fuqiuluo.shamrock"
|
||||
ndkVersion = "25.1.8937393"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "moe.fuqiuluo.shamrock"
|
||||
minSdk = 24
|
||||
targetSdk = 33
|
||||
versionCode = (System.currentTimeMillis() / 1000).toInt()
|
||||
versionName = "1.0.6-dev" + gitCommitHash()
|
||||
minSdk = 27
|
||||
targetSdk = 34
|
||||
versionCode = getVersionCode()
|
||||
versionName = "1.0.7" + ".r${getGitCommitCount()}." + getVersionName()
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
@Suppress("UnstableApiUsage")
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags += ""
|
||||
@ -50,8 +44,7 @@ android {
|
||||
android.applicationVariants.all {
|
||||
outputs.map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl }
|
||||
.forEach {
|
||||
val abi = it.outputFileName.split("-")[1].split(".apk")[0]
|
||||
val abiName = when (abi) {
|
||||
val abiName = when (val abi = it.outputFileName.split("-")[1].split(".apk")[0]) {
|
||||
"app" -> "all"
|
||||
"x64" -> "x86_64"
|
||||
else -> abi
|
||||
@ -69,6 +62,7 @@ android {
|
||||
println("Full architecture and full compilation.")
|
||||
abiFilters.add("arm64-v8a")
|
||||
abiFilters.add("x86_64")
|
||||
abiFilters.add("x86")
|
||||
}
|
||||
}
|
||||
create("arm64") {
|
||||
@ -133,6 +127,11 @@ android {
|
||||
}
|
||||
|
||||
configureAppSigningConfigsForRelease(project)
|
||||
packagingOptions {
|
||||
jniLibs {
|
||||
useLegacyPackaging = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun configureAppSigningConfigsForRelease(project: Project) {
|
||||
@ -154,10 +153,39 @@ fun configureAppSigningConfigsForRelease(project: Project) {
|
||||
release {
|
||||
signingConfig = signingConfigs.findByName("release")
|
||||
}
|
||||
debug {
|
||||
signingConfig = signingConfigs.findByName("release")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getGitCommitCount(): Int {
|
||||
val out = ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine("git", "rev-list", "--count", "HEAD")
|
||||
standardOutput = out
|
||||
}
|
||||
return out.toString().trim().toInt()
|
||||
}
|
||||
|
||||
fun getGitCommitHash(): String {
|
||||
val out = ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine("git", "rev-parse", "--short", "HEAD")
|
||||
standardOutput = out
|
||||
}
|
||||
return out.toString().trim()
|
||||
}
|
||||
|
||||
fun getVersionCode(): Int {
|
||||
return (System.currentTimeMillis() / 1000L).toInt()
|
||||
}
|
||||
|
||||
fun getVersionName(): String {
|
||||
return getGitCommitHash()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.core:core-ktx:1.9.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
|
||||
|
@ -3,10 +3,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:name=".app.MyApplication"
|
||||
@ -18,6 +14,7 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Shamrock"
|
||||
android:zygotePreloadName="@string/app_name"
|
||||
android:multiArch="true"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
@ -47,7 +44,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" />
|
||||
|
@ -9,52 +9,61 @@ inline void replace_string(std::string& str, const std::string& from, const std:
|
||||
}
|
||||
}
|
||||
|
||||
inline int utf8_next_len(const std::string& str, size_t offset)
|
||||
{
|
||||
uint8_t c = (uint8_t)str[offset];
|
||||
if (c >= 0xFC)
|
||||
return 6;
|
||||
else if (c >= 0xF8)
|
||||
return 5;
|
||||
else if (c >= 0xF0)
|
||||
return 4;
|
||||
else if (c >= 0xE0)
|
||||
return 3;
|
||||
else if (c >= 0xC0)
|
||||
return 2;
|
||||
else if (c > 0x00)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void decode_cqcode(const std::string& code, std::vector<std::unordered_map<std::string, std::string>>& dest) {
|
||||
std::string cache;
|
||||
bool is_start = false;
|
||||
std::string key_tmp;
|
||||
std::unordered_map<std::string, std::string> kv;
|
||||
for(int i = 0; i < code.size(); i++) {
|
||||
auto c = code[i];
|
||||
if (c == '[') {
|
||||
for(size_t i = 0; i < code.size(); i++) {
|
||||
int utf8_char_len = utf8_next_len(code, i);
|
||||
if(utf8_char_len == 0) {
|
||||
continue;
|
||||
}
|
||||
std::string_view c(&code[i],utf8_char_len);
|
||||
if (c == "[") {
|
||||
if (is_start) {
|
||||
throw illegal_code();
|
||||
} else {
|
||||
if (!cache.empty()) {
|
||||
std::unordered_map<std::string, std::string> kv;
|
||||
replace_string(cache, "[", "[");
|
||||
replace_string(cache, "]", "]");
|
||||
replace_string(cache, "&", "&");
|
||||
kv.emplace("_type", "text");
|
||||
kv.emplace("text", cache);
|
||||
dest.push_back(kv);
|
||||
cache.clear();
|
||||
}
|
||||
auto c1 = code[i + 1];
|
||||
if (c1 == 'C') {
|
||||
i++;
|
||||
auto c2 = code[i + 1];
|
||||
if(c2 == 'Q') {
|
||||
i++;
|
||||
auto c3 = code[i + 1];
|
||||
if (c3 == ':') {
|
||||
i++;
|
||||
is_start = true;
|
||||
} else {
|
||||
cache += c;
|
||||
cache += c1;
|
||||
cache += c2;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
cache += c;
|
||||
cache += c1;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
std::string_view cq_flag(&code[i],4);
|
||||
if(cq_flag == "[CQ:"){
|
||||
is_start = true;
|
||||
i += 3;
|
||||
}else{
|
||||
cache += c;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (c == '=') {
|
||||
else if (c == "=") {
|
||||
if (is_start) {
|
||||
if (cache.empty()) {
|
||||
throw illegal_code();
|
||||
@ -70,17 +79,17 @@ void decode_cqcode(const std::string& code, std::vector<std::unordered_map<std::
|
||||
cache += c;
|
||||
}
|
||||
}
|
||||
else if (c == ',') {
|
||||
else if (c == ",") {
|
||||
if (is_start) {
|
||||
if (kv.count("_type") == 0 && !cache.empty()) {
|
||||
kv.emplace("_type", cache);
|
||||
cache.clear();
|
||||
} else {
|
||||
if (!key_tmp.empty()) {
|
||||
replace_string(cache, "&", "&");
|
||||
replace_string(cache, "[", "[");
|
||||
replace_string(cache, "]", "]");
|
||||
replace_string(cache, ",", ",");
|
||||
replace_string(cache, "&", "&");
|
||||
kv.emplace(key_tmp, cache);
|
||||
cache.clear();
|
||||
key_tmp.clear();
|
||||
@ -90,15 +99,17 @@ void decode_cqcode(const std::string& code, std::vector<std::unordered_map<std::
|
||||
cache += c;
|
||||
}
|
||||
}
|
||||
else if (c == ']') {
|
||||
else if (c == "]") {
|
||||
if (is_start) {
|
||||
if (!cache.empty()) {
|
||||
if (!key_tmp.empty()) {
|
||||
replace_string(cache, "&", "&");
|
||||
replace_string(cache, "[", "[");
|
||||
replace_string(cache, "]", "]");
|
||||
replace_string(cache, ",", ",");
|
||||
replace_string(cache, "&", "&");
|
||||
kv.emplace(key_tmp, cache);
|
||||
} else {
|
||||
kv.emplace("_type", cache);
|
||||
}
|
||||
dest.push_back(kv);
|
||||
kv.clear();
|
||||
@ -112,10 +123,14 @@ void decode_cqcode(const std::string& code, std::vector<std::unordered_map<std::
|
||||
}
|
||||
else {
|
||||
cache += c;
|
||||
i += (utf8_char_len - 1);
|
||||
}
|
||||
}
|
||||
if (!cache.empty()) {
|
||||
std::unordered_map<std::string, std::string> kv;
|
||||
replace_string(cache, "[", "[");
|
||||
replace_string(cache, "]", "]");
|
||||
replace_string(cache, "&", "&");
|
||||
kv.emplace("_type", "text");
|
||||
kv.emplace("text", cache);
|
||||
dest.push_back(kv);
|
||||
|
@ -37,6 +37,26 @@ Java_moe_fuqiuluo_shamrock_utils_MD5_genFileMd5Hex(JNIEnv *env, jobject thiz, js
|
||||
return env->NewStringUTF(md5Hex.c_str());
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_moe_fuqiuluo_shamrock_utils_MD5_genFileMd5(JNIEnv *env, jobject thiz, jstring file_path) {
|
||||
auto cPathStr = env->GetStringUTFChars(file_path, nullptr);
|
||||
std::filesystem::path filePath(cPathStr);
|
||||
if (!std::filesystem::exists(filePath)) {
|
||||
jclass exClass = env->FindClass("java/io/FileNotFoundException");
|
||||
env->ThrowNew(exClass, "目标文件不存在");
|
||||
env->DeleteLocalRef(exClass);
|
||||
return nullptr;
|
||||
}
|
||||
auto file = std::ifstream(filePath.c_str(), std::ios::binary);
|
||||
MD5 md5;
|
||||
md5.update(file);
|
||||
auto md5Bytes = md5.digest();
|
||||
auto jByteArray = env->NewByteArray(16);
|
||||
env->SetByteArrayRegion(jByteArray, 0, 16, reinterpret_cast<const jbyte*>(md5Bytes));
|
||||
return jByteArray;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_moe_fuqiuluo_shamrock_utils_MD5_getMd5Hex(JNIEnv *env, jobject thiz, jbyteArray bytes) {
|
||||
|
@ -52,6 +52,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
@ -120,6 +121,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.init()
|
||||
|
||||
if (!AppRuntime.isInit) {
|
||||
AppRuntime.state = remember {
|
||||
@ -140,23 +142,22 @@ private fun AppMainView() {
|
||||
mutableStateOf("2854200454")
|
||||
}
|
||||
it.nick = remember {
|
||||
mutableStateOf("测试昵称")
|
||||
mutableStateOf(LocalString.testName)
|
||||
}
|
||||
}
|
||||
|
||||
AppRuntime.requestCount = remember { mutableIntStateOf(0) }
|
||||
|
||||
AppRuntime.isInit = false
|
||||
AppRuntime.isInit = true
|
||||
}
|
||||
|
||||
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,6 +249,11 @@ 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)
|
||||
@ -249,6 +264,11 @@ object ShamrockConfig {
|
||||
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()
|
||||
@ -259,19 +279,19 @@ object ShamrockConfig {
|
||||
return preferences.getBoolean("enable_self_msg", false)
|
||||
}
|
||||
|
||||
fun enableSyncMsgAsSentMsg(ctx: Context): Boolean {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
return preferences.getBoolean("enable_sync_msg_as_sent_msg", false)
|
||||
}
|
||||
|
||||
fun setEnableSelfMsg(ctx: Context, v: Boolean) {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
preferences.edit().putBoolean("enable_self_msg", v).apply()
|
||||
}
|
||||
|
||||
fun isEchoNumber(ctx: Context): Boolean {
|
||||
fun setEnableSyncMsgAsSentMsg(ctx: Context, v: Boolean) {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
return preferences.getBoolean("echo_number", false)
|
||||
}
|
||||
|
||||
fun setEchoNumber(ctx: Context, v: Boolean) {
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
preferences.edit().putBoolean("echo_number", v).apply()
|
||||
preferences.edit().putBoolean("enable_sync_msg_as_sent_msg", v).apply()
|
||||
}
|
||||
|
||||
fun getConfigMap(ctx: Context): Map<String, Any?> {
|
||||
@ -293,12 +313,15 @@ 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),
|
||||
"enable_sync_msg_as_sent_msg" to preferences.getBoolean("enable_sync_msg_as_sent_msg", 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(
|
||||
@ -277,9 +278,7 @@ private fun APIInfoCard(
|
||||
text = authToken,
|
||||
hint = "请填写鉴权token",
|
||||
error = "输入的参数不合法",
|
||||
checker = {
|
||||
it.length in 0 .. 36
|
||||
},
|
||||
checker = { true },
|
||||
confirm = {
|
||||
ShamrockConfig.setToken(ctx, authToken.value)
|
||||
AppRuntime.log("设置鉴权Token为[${authToken.value}]。")
|
||||
|
@ -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,26 +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
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "开启Shell接口",
|
||||
desc = "可能导致设备被入侵,请勿随意开启。",
|
||||
descColor = it,
|
||||
isSwitch = ShamrockConfig.allowShell(ctx)
|
||||
) {
|
||||
ShamrockConfig.setShellStatus(ctx, it)
|
||||
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(
|
||||
@ -161,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
|
||||
}
|
||||
}
|
||||
@ -190,6 +275,17 @@ fun LabFragment() {
|
||||
return@Function true
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "同步消息推送类型异换",
|
||||
desc = "推送来自同号异设备消息,将同步消息作为自发消息推送。",
|
||||
descColor = it,
|
||||
isSwitch = ShamrockConfig.enableSyncMsgAsSentMsg(ctx)
|
||||
) {
|
||||
ShamrockConfig.setEnableSyncMsgAsSentMsg(ctx, it)
|
||||
ShamrockConfig.pushUpdate(ctx)
|
||||
return@Function true
|
||||
}
|
||||
|
||||
/*
|
||||
Function(
|
||||
title = "使用纯数字ECHO",
|
||||
|
@ -8,6 +8,7 @@ import android.os.Bundle
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.request.parameter
|
||||
import io.ktor.client.request.url
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import io.ktor.http.HttpStatusCode
|
||||
@ -58,7 +59,8 @@ object DashboardInitializer {
|
||||
url("http://127.0.0.1:$servicePort/get_account_info")
|
||||
val token = ShamrockConfig.getToken(context)
|
||||
if (token.isNotBlank()) {
|
||||
header("Authorization", "Bearer $token")
|
||||
//header("Authorization", "Bearer $token")
|
||||
parameter("access_token", token)
|
||||
}
|
||||
}.let {
|
||||
if (it.status == HttpStatusCode.OK) {
|
||||
|
@ -6,7 +6,9 @@ package moe.fuqiuluo.shamrock.ui.theme
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import moe.fuqiuluo.shamrock.R
|
||||
|
||||
private val LocalStringDefault = Default()
|
||||
@ -69,6 +71,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 +104,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 +137,43 @@ 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
|
||||
) {
|
||||
private var inited = false
|
||||
|
||||
@Composable
|
||||
fun init(): VarString {
|
||||
if (inited) return this
|
||||
|
||||
inited = true
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
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>
|
@ -1,7 +1,7 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.1.2" apply false
|
||||
id("com.android.application") version "8.2.0" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
|
||||
id("com.android.library") version "8.1.2" apply false
|
||||
id("com.android.library") version "8.2.0" apply false
|
||||
//id("io.realm.kotlin") version "1.11.0" apply false
|
||||
}
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
|
3
gradlew
vendored
3
gradlew
vendored
@ -83,7 +83,8 @@ done
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
@ -5,10 +5,10 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "moe.fuqiuluo.qqinterface"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
minSdk = 27
|
||||
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.tencent.mobileqq.filemanager.api;
|
||||
|
||||
import com.tencent.mobileqq.qroute.QRouteApi;
|
||||
|
||||
public interface IFileManagerUtil extends QRouteApi {
|
||||
byte[] getSHA(String str);
|
||||
}
|
@ -21,6 +21,10 @@ public abstract class PBField<T> {
|
||||
return new PBBytesField(byteStringMicro, false);
|
||||
}
|
||||
|
||||
public static PBFloatField initFloat(float paramFloat) {
|
||||
return new PBFloatField(paramFloat, false);
|
||||
}
|
||||
|
||||
public static PBBoolField initBool(boolean z) {
|
||||
return new PBBoolField(z, false);
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package com.tencent.mobileqq.pb;
|
||||
|
||||
public class PBFloatField extends PBPrimitiveField<Float> {
|
||||
public PBFloatField(float i2, boolean z) {
|
||||
}
|
||||
|
||||
public int get() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void set(int i2) {
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.tencent.mobileqq.transfile;
|
||||
|
||||
public class HttpNetReq extends NetReq {
|
||||
public static final int HTTP_GET = 0;
|
||||
public static final int HTTP_POST = 1;
|
||||
public IFlowDecoder decoder;
|
||||
public DnsParseCallback mDnsParseCallback;
|
||||
public boolean mHaveIpConnect;
|
||||
public String mHostForHttpsVerify;
|
||||
public int mHttpMethod;
|
||||
public boolean mIsHostIP;
|
||||
public boolean mIsHttps;
|
||||
public boolean mIsPreStructPic;
|
||||
public boolean mIsSync;
|
||||
public boolean mNeedIpConnect;
|
||||
public boolean mNeedNotReferer;
|
||||
public boolean mNeedRedirectCallback;
|
||||
public String mReqUrl;
|
||||
public TimeoutParam mTimeoutParam;
|
||||
public boolean mUseCmwapConnectionTypeFromDpc;
|
||||
public String[] mWhiteListContentType;
|
||||
|
||||
public interface DecoderState {
|
||||
public static final int STATE_END = 2;
|
||||
public static final int STATE_INIT = 0;
|
||||
public static final int STATE_RUNNING = 1;
|
||||
}
|
||||
|
||||
public interface DnsParseCallback {
|
||||
void end();
|
||||
|
||||
void start();
|
||||
}
|
||||
|
||||
public interface IFlowDecoder {
|
||||
byte[] decode(byte[] bArr);
|
||||
|
||||
boolean isFinish();
|
||||
|
||||
void reset();
|
||||
}
|
||||
|
||||
public HttpNetReq() {
|
||||
this.mHttpMethod = 0;
|
||||
this.mNeedIpConnect = false;
|
||||
this.mHaveIpConnect = false;
|
||||
this.mNeedRedirectCallback = false;
|
||||
this.mUseCmwapConnectionTypeFromDpc = false;
|
||||
this.mNeedNotReferer = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.tencent.mobileqq.transfile;
|
||||
|
||||
public interface INetEngineListener {
|
||||
void onResp(NetResp netResp);
|
||||
|
||||
void onUpdateProgeress(NetReq netReq, long j2, long j3);
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.tencent.mobileqq.transfile;
|
||||
|
||||
public interface NetFailedListener {
|
||||
void onFailed(NetResp netResp);
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package com.tencent.mobileqq.transfile;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class NetReq {
|
||||
public static final int PRIOTY_HIGH = 0;
|
||||
public static final int PRIOTY_LOW = 2;
|
||||
public static final int PRIOTY_NORMAL = 1;
|
||||
public boolean bAcceptNegativeContentLength;
|
||||
public int mBusiProtoType;
|
||||
public INetEngineListener mCallback;
|
||||
public boolean mCanPrintUrl;
|
||||
public int mContinuErrorLimit;
|
||||
public int mContinueConnReusedErrorLimit;
|
||||
public long mDelayTime;
|
||||
public long mEndDownOffset;
|
||||
public long mExcuteTimeLimit;
|
||||
public NetFailedListener mFailedListener;
|
||||
public int mFileType;
|
||||
public HostParseToIp mHostParseToIp;
|
||||
public boolean mIsNetChgAsError;
|
||||
public boolean mIsRenameInEngine;
|
||||
public String mKey;
|
||||
public String mMsgId;
|
||||
public String mOutPath;
|
||||
public OutputStream mOutStream;
|
||||
public int mPrioty;
|
||||
public Object mPrivate;
|
||||
public HashMap<String, String> mReqProperties;
|
||||
public NetResp mResp;
|
||||
public byte[] mSendData;
|
||||
public List<ServerAddr> mServerList;
|
||||
public long mStartDownOffset;
|
||||
public boolean mSupportBreakResume;
|
||||
public String mTempPath;
|
||||
public boolean mUseByteArrayPool;
|
||||
public boolean mUseRaf;
|
||||
private Object mUserData;
|
||||
public long taskStartTime;
|
||||
|
||||
public interface HostParseToIp {
|
||||
List<ServerAddr> getIpByHost(String str);
|
||||
}
|
||||
|
||||
public NetReq() {
|
||||
this.mStartDownOffset = 0L;
|
||||
this.mEndDownOffset = 0L;
|
||||
this.mIsRenameInEngine = true;
|
||||
this.mDelayTime = 0L;
|
||||
this.mExcuteTimeLimit = 480000L;
|
||||
this.mContinuErrorLimit = 8;
|
||||
this.mContinueConnReusedErrorLimit = 5;
|
||||
this.mIsNetChgAsError = false;
|
||||
this.mPrioty = 1;
|
||||
this.mResp = null;
|
||||
this.mCanPrintUrl = true;
|
||||
this.bAcceptNegativeContentLength = true;
|
||||
this.mUseByteArrayPool = false;
|
||||
this.mKey = null;
|
||||
this.taskStartTime = -1L;
|
||||
this.mReqProperties = new HashMap<>();
|
||||
}
|
||||
|
||||
public long getTaskCostTime() {
|
||||
return System.currentTimeMillis() - taskStartTime;
|
||||
}
|
||||
|
||||
public synchronized Object getUserData() {
|
||||
return mUserData;
|
||||
}
|
||||
|
||||
public boolean isWriteToFile() {
|
||||
return this.mOutPath != null;
|
||||
}
|
||||
|
||||
public boolean isWriteToStream() {
|
||||
return this.mOutStream != null;
|
||||
}
|
||||
|
||||
public boolean saveRecvDataInTransLayer() {
|
||||
return this.mOutPath != null || this.mOutStream != null;
|
||||
}
|
||||
|
||||
public synchronized void setUserData(Object obj) {
|
||||
this.mUserData = obj;
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.tencent.mobileqq.transfile;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class NetResp {
|
||||
public static final String KEY_FIRST_USE_IP = "firstserverip";
|
||||
public static final String KEY_RAW_REQ_HTTP_HEADER = "param_reqHeader";
|
||||
public static final String KEY_RAW_RESP_HTTP_HEADER = "param_rspHeader";
|
||||
public static final String KEY_REASON = "netresp_param_reason";
|
||||
public static final String KEY_USE_SERVER_IP = "serverip";
|
||||
public static final String KEY_USE_URL = "param_url";
|
||||
public static final int RESULT_DOWNLOADING = 3;
|
||||
public static final int RESULT_FAIL = 1;
|
||||
public static final int RESULT_NOT_SET = 2;
|
||||
public static final int RESULT_OK = 0;
|
||||
public long inQueueCost;
|
||||
public long mConsumeTime;
|
||||
public int mErrCode;
|
||||
public String mErrDesc;
|
||||
public int mHttpCode;
|
||||
public long mLastReqStartTime;
|
||||
public int mRedirectCount;
|
||||
public long mRedirectTime;
|
||||
public NetReq mReq;
|
||||
public byte[] mRespData;
|
||||
public HashMap<String, String> mRespProperties;
|
||||
public int mResult;
|
||||
public long mTotalBlockLen;
|
||||
public long mTotalFileLen;
|
||||
public int mTryTime;
|
||||
public long mWrittenBlockLen;
|
||||
public long reqCost;
|
||||
|
||||
public NetResp(NetReq netReq) {
|
||||
this.mResult = 2;
|
||||
this.mTotalFileLen = 0L;
|
||||
this.mTotalBlockLen = 0L;
|
||||
this.mWrittenBlockLen = 0L;
|
||||
this.mConsumeTime = 0L;
|
||||
this.mTryTime = 0;
|
||||
this.mRespProperties = new HashMap<>();
|
||||
this.mRedirectCount = 0;
|
||||
this.mRedirectTime = 0L;
|
||||
this.reqCost = 0L;
|
||||
this.inQueueCost = 0L;
|
||||
this.mReq = netReq;
|
||||
this.mLastReqStartTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.mResult = 2;
|
||||
this.mErrCode = 0;
|
||||
this.mErrDesc = "";
|
||||
this.mHttpCode = 0;
|
||||
this.mTotalFileLen = 0L;
|
||||
this.mTotalBlockLen = 0L;
|
||||
this.mWrittenBlockLen = 0L;
|
||||
this.mConsumeTime = 0L;
|
||||
this.mTryTime = 0;
|
||||
this.mRespData = null;
|
||||
this.mRespProperties.clear();
|
||||
}
|
||||
|
||||
public void setResult(int i2, int i3, String str, HashMap<String, String> hashMap) {
|
||||
this.mResult = i2;
|
||||
this.mErrCode = i3;
|
||||
this.mErrDesc = str;
|
||||
if (hashMap != null) {
|
||||
this.mRespProperties.putAll(hashMap);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.tencent.mobileqq.transfile;
|
||||
|
||||
public class TimeoutParam {
|
||||
public static final int TIMEOUT_STEP = 2000;
|
||||
private int connectTimeoutBias;
|
||||
public int connectTimeoutFor2G;
|
||||
public int connectTimeoutFor3G;
|
||||
public int connectTimeoutForWifi;
|
||||
public int readTimeoutFor2G;
|
||||
public int readTimeoutFor3G;
|
||||
public int readTimeoutForWifi;
|
||||
|
||||
public TimeoutParam() {
|
||||
this.readTimeoutFor2G = 40000;
|
||||
this.readTimeoutFor3G = 30000;
|
||||
this.readTimeoutForWifi = 20000;
|
||||
this.connectTimeoutFor2G = 20000;
|
||||
this.connectTimeoutFor3G = 15000;
|
||||
this.connectTimeoutForWifi = 10000;
|
||||
this.connectTimeoutBias = 0;
|
||||
}
|
||||
|
||||
public void adjustConnectTimeout(int i2) {
|
||||
|
||||
this.connectTimeoutBias = i2 * 2000;
|
||||
}
|
||||
|
||||
public int getConnectTimeout(int i2) {
|
||||
if (i2 != 1) {
|
||||
if (i2 == 3) {
|
||||
return this.connectTimeoutFor3G + this.connectTimeoutBias;
|
||||
}
|
||||
if (i2 != 4 && i2 != 5) {
|
||||
return this.connectTimeoutFor2G + this.connectTimeoutBias;
|
||||
}
|
||||
}
|
||||
return this.connectTimeoutForWifi + this.connectTimeoutBias;
|
||||
}
|
||||
|
||||
public int getReadTimeout(int i2) {
|
||||
if (i2 != 1) {
|
||||
if (i2 == 3) {
|
||||
return this.readTimeoutFor3G;
|
||||
}
|
||||
if (i2 != 4 && i2 != 5) {
|
||||
return this.readTimeoutFor2G;
|
||||
}
|
||||
}
|
||||
return this.readTimeoutForWifi;
|
||||
}
|
||||
|
||||
//public TimeoutParam clone() {
|
||||
// IPatchRedirector iPatchRedirector = $redirector_;
|
||||
// return (iPatchRedirector == null || !iPatchRedirector.hasPatch((short) 4)) ? new TimeoutParam(this) : (TimeoutParam) iPatchRedirector.redirect((short) 4, (Object) this);
|
||||
//}
|
||||
|
||||
TimeoutParam(TimeoutParam timeoutParam) {
|
||||
this.readTimeoutFor2G = 40000;
|
||||
this.readTimeoutFor3G = 30000;
|
||||
this.readTimeoutForWifi = 20000;
|
||||
this.connectTimeoutFor2G = 20000;
|
||||
this.connectTimeoutFor3G = 15000;
|
||||
this.connectTimeoutForWifi = 10000;
|
||||
this.connectTimeoutBias = 0;
|
||||
this.readTimeoutFor2G = timeoutParam.readTimeoutFor2G;
|
||||
this.readTimeoutFor3G = timeoutParam.readTimeoutFor3G;
|
||||
this.readTimeoutForWifi = timeoutParam.readTimeoutForWifi;
|
||||
this.connectTimeoutFor2G = timeoutParam.connectTimeoutFor2G;
|
||||
this.connectTimeoutFor3G = timeoutParam.connectTimeoutFor3G;
|
||||
this.connectTimeoutForWifi = timeoutParam.connectTimeoutForWifi;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.tencent.mobileqq.transfile.api;
|
||||
|
||||
import com.tencent.mobileqq.transfile.NetReq;
|
||||
import com.tencent.mobileqq.transfile.NetResp;
|
||||
|
||||
import mqq.app.api.IRuntimeService;
|
||||
|
||||
@Deprecated
|
||||
public interface IHttpEngineService extends IRuntimeService {
|
||||
void cancelReq(NetReq netReq);
|
||||
|
||||
void sendReq(NetReq netReq);
|
||||
|
||||
NetResp sendReqSync(NetReq netReq);
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.tencent.mobileqq.transfile.api;
|
||||
|
||||
import com.tencent.mobileqq.qroute.QRouteApi;
|
||||
import com.tencent.mobileqq.transfile.NetReq;
|
||||
|
||||
public interface IOldHttpEngineProcessor extends QRouteApi {
|
||||
//void cancelMsg(HttpMsg httpMsg);
|
||||
|
||||
void cancelReq(NetReq netReq);
|
||||
|
||||
//int sendMsg(HttpMsg httpMsg);
|
||||
|
||||
void sendReq(NetReq netReq);
|
||||
}
|
@ -1,10 +1,32 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
/* compiled from: P */
|
||||
/* loaded from: classes4.dex */
|
||||
public enum DeviceType {
|
||||
KUNKNOWN,
|
||||
KPHONE,
|
||||
KPAD,
|
||||
KCOMPUTER
|
||||
public final class DeviceType {
|
||||
private static final DeviceType[] $VALUES;
|
||||
public static final DeviceType KCOMPUTER;
|
||||
public static final DeviceType KPAD;
|
||||
public static final DeviceType KPHONE;
|
||||
public static final DeviceType KUNKNOWN;
|
||||
|
||||
static {
|
||||
DeviceType deviceType = new DeviceType("KUNKNOWN", 0);
|
||||
KUNKNOWN = deviceType;
|
||||
DeviceType deviceType2 = new DeviceType("KPHONE", 1);
|
||||
KPHONE = deviceType2;
|
||||
DeviceType deviceType3 = new DeviceType("KPAD", 2);
|
||||
KPAD = deviceType3;
|
||||
DeviceType deviceType4 = new DeviceType("KCOMPUTER", 3);
|
||||
KCOMPUTER = deviceType4;
|
||||
$VALUES = new DeviceType[]{deviceType, deviceType2, deviceType3, deviceType4};
|
||||
}
|
||||
|
||||
DeviceType(String str, int i2) {
|
||||
}
|
||||
|
||||
public static DeviceType valueOf(String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static DeviceType[] values() {
|
||||
return (DeviceType[]) $VALUES.clone();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,4 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public class GuildInteractiveNotificationItem {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public enum GuildMsgAbFlag {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public class GuildNotificationAbstractInfo {
|
||||
}
|
@ -42,6 +42,12 @@ public interface IKernelMsgListener {
|
||||
|
||||
void onGroupTransferInfoUpdate(GroupFileListResult groupFileListResult);
|
||||
|
||||
void onGuildInteractiveUpdate(GuildInteractiveNotificationItem guildInteractiveNotificationItem);
|
||||
|
||||
void onGuildMsgAbFlagChanged(GuildMsgAbFlag guildMsgAbFlag);
|
||||
|
||||
void onGuildNotificationAbstractUpdate(GuildNotificationAbstractInfo guildNotificationAbstractInfo);
|
||||
|
||||
void onHitCsRelatedEmojiResult(DownloadRelateEmojiResultInfo downloadRelateEmojiResultInfo);
|
||||
|
||||
void onHitEmojiKeywordResult(HitRelatedEmojiWordsResult hitRelatedEmojiWordsResult);
|
||||
|
@ -0,0 +1,100 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public class InitSessionConfig {
|
||||
|
||||
String a2;
|
||||
String appid;
|
||||
String clientVer;
|
||||
String d2;
|
||||
String d2Key;
|
||||
String defaultFileDownloadPath;
|
||||
InitSessionDesktopPathConfig desktopPathConfig;
|
||||
DeviceType deviceType;
|
||||
String extDataPath;
|
||||
String gproDBName;
|
||||
String machineId;
|
||||
InitSessionMobilePathConfig mobilePathConfig;
|
||||
String platVer;
|
||||
PlatformType platform;
|
||||
RDeliveryConfig rdeliveryConfig;
|
||||
String selfUid;
|
||||
long selfUin;
|
||||
String sysPath;
|
||||
String userPath;
|
||||
|
||||
public String getA2() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getAppid() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getClientVer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getD2() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getD2Key() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getDefaultFileDownloadPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public InitSessionDesktopPathConfig getDesktopPathConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public DeviceType getDeviceType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getExtDataPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getGproDBName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getMachineId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public InitSessionMobilePathConfig getMobilePathConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getPlatVer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PlatformType getPlatform() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public RDeliveryConfig getRdeliveryConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSelfUid() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getSelfUin() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getSysPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getUserPath() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public class InitSessionDesktopPathConfig {
|
||||
String accountPath;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public final class InitSessionMobilePathConfig {
|
||||
String mobileQqFilePath;
|
||||
String mobileQqMarketPath;
|
||||
String mobileQqPicPath;
|
||||
String mobileQqPttPath;
|
||||
String mobileQqVideoPath;
|
||||
|
||||
public InitSessionMobilePathConfig() {
|
||||
this.mobileQqPicPath = "";
|
||||
this.mobileQqVideoPath = "";
|
||||
this.mobileQqPttPath = "";
|
||||
this.mobileQqFilePath = "";
|
||||
this.mobileQqMarketPath = "";
|
||||
}
|
||||
}
|
@ -18,7 +18,6 @@ public final class MarkdownElement {
|
||||
}
|
||||
|
||||
public MarkdownElement(String str) {
|
||||
this.content = "";
|
||||
this.content = str;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public final class PlatformType {
|
||||
private static final /* synthetic */ PlatformType[] $VALUES;
|
||||
public static final PlatformType KANDROID;
|
||||
public static final PlatformType KIOS;
|
||||
public static final PlatformType KMAC;
|
||||
public static final PlatformType KUNKNOWN;
|
||||
public static final PlatformType KWINDOWS;
|
||||
|
||||
static {
|
||||
PlatformType platformType = new PlatformType("KUNKNOWN", 0);
|
||||
KUNKNOWN = platformType;
|
||||
PlatformType platformType2 = new PlatformType("KANDROID", 1);
|
||||
KANDROID = platformType2;
|
||||
PlatformType platformType3 = new PlatformType("KIOS", 2);
|
||||
KIOS = platformType3;
|
||||
PlatformType platformType4 = new PlatformType("KWINDOWS", 3);
|
||||
KWINDOWS = platformType4;
|
||||
PlatformType platformType5 = new PlatformType("KMAC", 4);
|
||||
KMAC = platformType5;
|
||||
$VALUES = new PlatformType[]{platformType, platformType2, platformType3, platformType4, platformType5};
|
||||
}
|
||||
|
||||
PlatformType(String str, int i2) {
|
||||
}
|
||||
|
||||
public static PlatformType valueOf(String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PlatformType[] values() {
|
||||
return (PlatformType[]) $VALUES.clone();
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public final class RDeliveryConfig implements IKernelModel {
|
||||
String appId;
|
||||
String appKey;
|
||||
String appVersion;
|
||||
String bundleId;
|
||||
ArrayList<String> fixedAfterHitKeys;
|
||||
String language;
|
||||
String logicEnvironment;
|
||||
String osVersion;
|
||||
int platform;
|
||||
String sdkVersion;
|
||||
String serverUrl;
|
||||
int systemId;
|
||||
String userId;
|
||||
}
|
@ -3,6 +3,7 @@ package mqq.manager;
|
||||
import android.content.Context;
|
||||
|
||||
import oicq.wlogin_sdk.request.Ticket;
|
||||
import oicq.wlogin_sdk.request.WtTicketPromise;
|
||||
|
||||
public interface TicketManager extends Manager {
|
||||
String getA2(String uin);
|
||||
@ -15,6 +16,8 @@ public interface TicketManager extends Manager {
|
||||
|
||||
String getPskey(String uin, String domain);
|
||||
|
||||
Ticket getPskey(String str, long j2, String[] strArr, WtTicketPromise wtTicketPromise);
|
||||
|
||||
String getPt4Token(String uin, String domain);
|
||||
|
||||
String getSkey(String uin); // 假的Skey
|
||||
|
@ -0,0 +1,11 @@
|
||||
package oicq.wlogin_sdk.request;
|
||||
|
||||
import oicq.wlogin_sdk.tools.ErrMsg;
|
||||
|
||||
public interface WtTicketPromise {
|
||||
void Done(Ticket ticket);
|
||||
|
||||
void Failed(ErrMsg errMsg);
|
||||
|
||||
void Timeout(ErrMsg errMsg);
|
||||
}
|
118
qqinterface/src/main/java/oicq/wlogin_sdk/tools/ErrMsg.java
Normal file
118
qqinterface/src/main/java/oicq/wlogin_sdk/tools/ErrMsg.java
Normal file
@ -0,0 +1,118 @@
|
||||
package oicq.wlogin_sdk.tools;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class ErrMsg implements Cloneable, Parcelable {
|
||||
public static final Parcelable.Creator<ErrMsg> CREATOR = new Parcelable.Creator<ErrMsg>() { // from class: oicq.wlogin_sdk.tools.ErrMsg.1
|
||||
@Override // android.os.Parcelable.Creator
|
||||
public ErrMsg createFromParcel(Parcel parcel) {
|
||||
return new ErrMsg(parcel);
|
||||
}
|
||||
|
||||
@Override // android.os.Parcelable.Creator
|
||||
public ErrMsg[] newArray(int i2) {
|
||||
return new ErrMsg[i2];
|
||||
}
|
||||
};
|
||||
private String message;
|
||||
private String otherinfo;
|
||||
private String title;
|
||||
private int type;
|
||||
private int version;
|
||||
|
||||
public ErrMsg() {
|
||||
this.version = 0;
|
||||
this.type = 0;
|
||||
//this.title = InternationMsg.a(InternationMsg.MSG_TYPE.MSG_0);
|
||||
//this.message = InternationMsg.a(InternationMsg.MSG_TYPE.MSG_1);
|
||||
this.otherinfo = "";
|
||||
}
|
||||
|
||||
public ErrMsg(int i2, int i3, String str, String str2, String str3) {
|
||||
this.version = i2;
|
||||
this.type = i3;
|
||||
this.title = str;
|
||||
this.message = str2;
|
||||
this.otherinfo = str3;
|
||||
}
|
||||
|
||||
private ErrMsg(Parcel parcel) {
|
||||
readFromParcel(parcel);
|
||||
}
|
||||
|
||||
@Override // android.os.Parcelable
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
public String getOtherinfo() {
|
||||
return this.otherinfo;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel parcel) {
|
||||
this.version = parcel.readInt();
|
||||
this.type = parcel.readInt();
|
||||
this.title = parcel.readString();
|
||||
this.message = parcel.readString();
|
||||
this.otherinfo = parcel.readString();
|
||||
}
|
||||
|
||||
public void setMessage(String str) {
|
||||
this.message = str;
|
||||
}
|
||||
|
||||
public void setOtherinfo(String str) {
|
||||
this.otherinfo = str;
|
||||
}
|
||||
|
||||
public void setTitle(String str) {
|
||||
this.title = str;
|
||||
}
|
||||
|
||||
public void setType(int i2) {
|
||||
this.type = i2;
|
||||
}
|
||||
|
||||
public void setVersion(int i2) {
|
||||
this.version = i2;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
int i2 = this.version;
|
||||
sb.append(i2 < 0 ? Integer.valueOf(i2) : Integer.toString(i2));
|
||||
sb.append(")(");
|
||||
int i3 = this.type;
|
||||
sb.append(i3 < 0 ? Integer.valueOf(i3) : Integer.toString(i3));
|
||||
sb.append(")[");
|
||||
String sb2 = sb.toString();
|
||||
return sb2 + this.title + "]" + this.message + "[" + this.otherinfo + "]";
|
||||
}
|
||||
|
||||
@Override // android.os.Parcelable
|
||||
public void writeToParcel(Parcel parcel, int i2) {
|
||||
parcel.writeInt(this.version);
|
||||
parcel.writeInt(this.type);
|
||||
parcel.writeString(this.title);
|
||||
parcel.writeString(this.message);
|
||||
parcel.writeString(this.otherinfo);
|
||||
}
|
||||
}
|
@ -0,0 +1,326 @@
|
||||
package tencent.im.group;
|
||||
|
||||
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.PBField;
|
||||
import com.tencent.mobileqq.pb.PBRepeatField;
|
||||
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 class group_member_info {
|
||||
public static class CustomEntry extends MessageMicro<tencent.im.group.group_member_info.CustomEntry> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBBoolField bool_clicked;
|
||||
|
||||
public final PBBytesField str_name;
|
||||
|
||||
public final PBBytesField str_url;
|
||||
|
||||
public final PBBytesField str_value;
|
||||
|
||||
public final PBUInt64Field uint64_report_id;
|
||||
|
||||
static {
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
Boolean bool = Boolean.FALSE;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 24, 34, 40 }, new String[] { "str_name", "str_value", "bool_clicked", "str_url", "uint64_report_id" }, new Object[] { byteStringMicro, byteStringMicro, bool, byteStringMicro, Long.valueOf(0L) }, tencent.im.group.group_member_info.CustomEntry.class);
|
||||
}
|
||||
|
||||
public CustomEntry() {
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
this.str_name = PBField.initBytes(byteStringMicro);
|
||||
this.str_value = PBField.initBytes(byteStringMicro);
|
||||
this.bool_clicked = PBField.initBool(false);
|
||||
this.str_url = PBField.initBytes(byteStringMicro);
|
||||
this.uint64_report_id = PBField.initUInt64(0L);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ErrorInfo extends MessageMicro<tencent.im.group.group_member_info.ErrorInfo> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBUInt32Field error_code = PBField.initUInt32(0);
|
||||
|
||||
public final PBBytesField error_desc = PBField.initBytes(ByteStringMicro.EMPTY);
|
||||
|
||||
static {
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 18 }, new String[] { "error_code", "error_desc" }, new Object[] { Integer.valueOf(0), byteStringMicro }, tencent.im.group.group_member_info.ErrorInfo.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class FlowersEntry extends MessageMicro<tencent.im.group.group_member_info.FlowersEntry> {
|
||||
static final FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 8 }, new String[] { "uint64_flower_count" }, new Object[] { Long.valueOf(0L) }, tencent.im.group.group_member_info.FlowersEntry.class);
|
||||
|
||||
public final PBUInt64Field uint64_flower_count = PBField.initUInt64(0L);
|
||||
}
|
||||
|
||||
public static class GBarInfo extends MessageMicro<tencent.im.group.group_member_info.GBarInfo> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBBytesField bytes_gbar_name;
|
||||
|
||||
public final PBBytesField str_head_portrait;
|
||||
|
||||
public final PBUInt32Field uint32_gbar_id = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_uin_lev = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 26, 34 }, new String[] { "uint32_gbar_id", "uint32_uin_lev", "str_head_portrait", "bytes_gbar_name" }, new Object[] { integer, integer, byteStringMicro, byteStringMicro }, tencent.im.group.group_member_info.GBarInfo.class);
|
||||
}
|
||||
|
||||
public GBarInfo() {
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
this.str_head_portrait = PBField.initBytes(byteStringMicro);
|
||||
this.bytes_gbar_name = PBField.initBytes(byteStringMicro);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MemberGameInfo extends MessageMicro<tencent.im.group.group_member_info.MemberGameInfo> {
|
||||
static final FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 26, 34, 42, 50, 58 }, new String[] { "str_game_name", "str_level_name", "str_level_icon", "str_game_font_color", "str_game_background_color", "str_game_url", "str_desc_info" }, new Object[] { "", "", "", "", "", "", "" }, tencent.im.group.group_member_info.MemberGameInfo.class);
|
||||
|
||||
public final PBRepeatField<String> str_desc_info = PBField.initRepeat(PBField.initString(""));
|
||||
|
||||
public final PBStringField str_game_background_color = PBField.initString("");
|
||||
|
||||
public final PBStringField str_game_font_color = PBField.initString("");
|
||||
|
||||
public final PBStringField str_game_name = PBField.initString("");
|
||||
|
||||
public final PBStringField str_game_url = PBField.initString("");
|
||||
|
||||
public final PBStringField str_level_icon = PBField.initString("");
|
||||
|
||||
public final PBStringField str_level_name = PBField.initString("");
|
||||
}
|
||||
|
||||
|
||||
public static class MemberInfo extends MessageMicro<tencent.im.group.group_member_info.MemberInfo> {
|
||||
public static final int CONCERN_TYPE_CONCERN = 1;
|
||||
|
||||
public static final int CONCERN_TYPE_GENERAL = 0;
|
||||
|
||||
public static final int CONCERN_TYPE_HATE = 2;
|
||||
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBBoolField bool_is_allow_mod_card;
|
||||
|
||||
public final PBBoolField bool_is_concerned;
|
||||
|
||||
public final PBBoolField bool_is_friend;
|
||||
|
||||
public final PBBoolField bool_is_super_qq;
|
||||
|
||||
public final PBBoolField bool_is_super_vip;
|
||||
|
||||
public final PBBoolField bool_is_vip;
|
||||
|
||||
public final PBBoolField bool_is_year_vip;
|
||||
|
||||
public final PBBoolField bool_location_shared;
|
||||
|
||||
public final PBBytesField bytes_group_honor;
|
||||
|
||||
public final PBBytesField bytes_job;
|
||||
|
||||
public final PBBytesField bytes_phone_num;
|
||||
|
||||
public final PBBytesField bytes_special_title;
|
||||
|
||||
public final PBUInt32Field medal_id;
|
||||
|
||||
public tencent.im.group.group_member_info.FlowersEntry msg_flower_entry;
|
||||
|
||||
public tencent.im.group.group_member_info.MemberGameInfo msg_game_info;
|
||||
|
||||
public group_member_info.TeamEntry msg_team_entry;
|
||||
|
||||
// public group_member_info.RspGroupCardGetStory qqstory_infocard;
|
||||
|
||||
public final PBRepeatMessageField<tencent.im.group.group_member_info.CustomEntry> rpt_msg_custom_enties;
|
||||
|
||||
public final PBRepeatMessageField<tencent.im.group.group_member_info.GBarInfo> rpt_msg_gbar_concerned;
|
||||
|
||||
public final PBBytesField str_card;
|
||||
|
||||
public final PBBytesField str_errmsg;
|
||||
|
||||
public final PBBytesField str_gbar_title;
|
||||
|
||||
public final PBBytesField str_gbar_url;
|
||||
|
||||
public final PBBytesField str_lev;
|
||||
|
||||
public final PBBytesField str_location;
|
||||
|
||||
public final PBBytesField str_nick;
|
||||
|
||||
public final PBBytesField str_remark;
|
||||
|
||||
public final PBUInt32Field uint32_age;
|
||||
|
||||
public final PBUInt32Field uint32_concern_type;
|
||||
|
||||
public final PBUInt32Field uint32_credit;
|
||||
|
||||
public final PBUInt32Field uint32_gbar_cnt;
|
||||
|
||||
public final PBUInt32Field uint32_group_honor_bit;
|
||||
|
||||
public final PBUInt32Field uint32_level;
|
||||
|
||||
public final PBUInt32Field uint32_result = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_role;
|
||||
|
||||
public final PBUInt32Field uint32_sex;
|
||||
|
||||
public final PBUInt32Field uint32_special_title_expire_time;
|
||||
|
||||
public final PBUInt32Field uint32_vip_lev;
|
||||
|
||||
public final PBUInt64Field uint64_distance;
|
||||
|
||||
public final PBUInt64Field uint64_join;
|
||||
|
||||
public final PBUInt64Field uint64_last_speak;
|
||||
|
||||
public final PBUInt64Field uint64_uin = PBField.initUInt64(0L);
|
||||
|
||||
static {
|
||||
Long long_ = Long.valueOf(0L);
|
||||
Integer integer = Integer.valueOf(0);
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
Boolean bool = Boolean.FALSE;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] {
|
||||
8, 16, 26, 32, 42, 48, 56, 66, 72, 82,
|
||||
90, 96, 106, 112, 120, 130, 138, 146, 154, 160,
|
||||
168, 176, 184, 192, 200, 208, 216, 224, 232, 240,
|
||||
250, 256, 266, 274, 282, 290, 296, 306, 312, 322,
|
||||
330, 336 }, new String[] {
|
||||
"uint64_uin", "uint32_result", "str_errmsg", "bool_is_friend", "str_remark", "bool_is_concerned", "uint32_credit", "str_card", "uint32_sex", "str_location",
|
||||
"str_nick", "uint32_age", "str_lev", "uint64_join", "uint64_last_speak", "rpt_msg_custom_enties", "rpt_msg_gbar_concerned", "str_gbar_title", "str_gbar_url", "uint32_gbar_cnt",
|
||||
"bool_is_allow_mod_card", "bool_is_vip", "bool_is_year_vip", "bool_is_super_vip", "bool_is_super_qq", "uint32_vip_lev", "uint32_role", "bool_location_shared", "uint64_distance", "uint32_concern_type",
|
||||
"bytes_special_title", "uint32_special_title_expire_time", "msg_flower_entry", "msg_team_entry", "bytes_phone_num", "bytes_job", "medal_id", "qqstory_infocard", "uint32_level", "msg_game_info",
|
||||
"bytes_group_honor", "uint32_group_honor_bit" }, new Object[] {
|
||||
long_, integer, byteStringMicro, bool, byteStringMicro, bool, integer, byteStringMicro, integer, byteStringMicro,
|
||||
byteStringMicro, integer, byteStringMicro, long_, long_, null, null, byteStringMicro, byteStringMicro, integer,
|
||||
bool, bool, bool, bool, bool, integer, integer, bool, long_, integer,
|
||||
byteStringMicro, integer, null, null, byteStringMicro, byteStringMicro, integer, null, integer, null,
|
||||
byteStringMicro, integer }, tencent.im.group.group_member_info.MemberInfo.class);
|
||||
}
|
||||
|
||||
public MemberInfo() {
|
||||
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
|
||||
this.str_errmsg = PBField.initBytes(byteStringMicro);
|
||||
this.bool_is_friend = PBField.initBool(false);
|
||||
this.str_remark = PBField.initBytes(byteStringMicro);
|
||||
this.bool_is_concerned = PBField.initBool(false);
|
||||
this.uint32_credit = PBField.initUInt32(0);
|
||||
this.str_card = PBField.initBytes(byteStringMicro);
|
||||
this.uint32_sex = PBField.initUInt32(0);
|
||||
this.str_location = PBField.initBytes(byteStringMicro);
|
||||
this.str_nick = PBField.initBytes(byteStringMicro);
|
||||
this.uint32_age = PBField.initUInt32(0);
|
||||
this.str_lev = PBField.initBytes(byteStringMicro);
|
||||
this.uint64_join = PBField.initUInt64(0L);
|
||||
this.uint64_last_speak = PBField.initUInt64(0L);
|
||||
this.rpt_msg_custom_enties = PBField.initRepeatMessage(tencent.im.group.group_member_info.CustomEntry.class);
|
||||
this.rpt_msg_gbar_concerned = PBField.initRepeatMessage(tencent.im.group.group_member_info.GBarInfo.class);
|
||||
this.str_gbar_title = PBField.initBytes(byteStringMicro);
|
||||
this.str_gbar_url = PBField.initBytes(byteStringMicro);
|
||||
this.uint32_gbar_cnt = PBField.initUInt32(0);
|
||||
this.bool_is_allow_mod_card = PBField.initBool(false);
|
||||
this.bool_is_vip = PBField.initBool(false);
|
||||
this.bool_is_year_vip = PBField.initBool(false);
|
||||
this.bool_is_super_vip = PBField.initBool(false);
|
||||
this.bool_is_super_qq = PBField.initBool(false);
|
||||
this.uint32_vip_lev = PBField.initUInt32(0);
|
||||
this.uint32_role = PBField.initUInt32(0);
|
||||
this.bool_location_shared = PBField.initBool(false);
|
||||
this.uint64_distance = PBField.initUInt64(0L);
|
||||
this.uint32_concern_type = PBField.initUInt32(0);
|
||||
this.bytes_special_title = PBField.initBytes(byteStringMicro);
|
||||
this.uint32_special_title_expire_time = PBField.initUInt32(0);
|
||||
this.msg_flower_entry = new tencent.im.group.group_member_info.FlowersEntry();
|
||||
this.msg_team_entry = new group_member_info.TeamEntry();
|
||||
this.bytes_phone_num = PBField.initBytes(byteStringMicro);
|
||||
this.bytes_job = PBField.initBytes(byteStringMicro);
|
||||
this.medal_id = PBField.initUInt32(0);
|
||||
// this.qqstory_infocard = new group_member_info.RspGroupCardGetStory();
|
||||
this.uint32_level = PBField.initUInt32(0);
|
||||
this.msg_game_info = new tencent.im.group.group_member_info.MemberGameInfo();
|
||||
this.bytes_group_honor = PBField.initBytes(byteStringMicro);
|
||||
this.uint32_group_honor_bit = PBField.initUInt32(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ReqBody extends MessageMicro<tencent.im.group.group_member_info.ReqBody> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBBoolField bool_new_client = PBField.initBool(false);
|
||||
|
||||
public final PBUInt32Field uint32_client_type = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_rich_card_name_ver = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt64Field uint64_group_code = PBField.initUInt64(0L);
|
||||
|
||||
public final PBUInt64Field uint64_uin = PBField.initUInt64(0L);
|
||||
|
||||
static {
|
||||
Long long_ = Long.valueOf(0L);
|
||||
Integer integer = Integer.valueOf(0);
|
||||
Boolean bool = Boolean.FALSE;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24, 32, 40 }, new String[] { "uint64_group_code", "uint64_uin", "bool_new_client", "uint32_client_type", "uint32_rich_card_name_ver" }, new Object[] { long_, long_, bool, integer, integer }, tencent.im.group.group_member_info.ReqBody.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RspBody extends MessageMicro<RspBody> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBBoolField bool_self_location_shared = PBField.initBool(false);
|
||||
|
||||
public MemberInfo msg_meminfo = new MemberInfo();
|
||||
|
||||
public final PBUInt32Field uint32_group_type = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt32Field uint32_self_role = PBField.initUInt32(0);
|
||||
|
||||
public final PBUInt64Field uint64_group_code = PBField.initUInt64(0L);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
Boolean bool = Boolean.FALSE;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 26, 32, 40 }, new String[] { "uint64_group_code", "uint32_self_role", "msg_meminfo", "bool_self_location_shared", "uint32_group_type" }, new Object[] { Long.valueOf(0L), integer, null, bool, integer }, tencent.im.group.group_member_info.RspBody.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TeamEntry extends MessageMicro<TeamEntry> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBRepeatField<Long> rpt_uint64_depid;
|
||||
|
||||
public final PBRepeatField<Long> rpt_uint64_self_depid;
|
||||
|
||||
static {
|
||||
Long long_ = Long.valueOf(0L);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16 }, new String[] { "rpt_uint64_depid", "rpt_uint64_self_depid" }, new Object[] { long_, long_ }, tencent.im.group.group_member_info.TeamEntry.class);
|
||||
}
|
||||
|
||||
public TeamEntry() {
|
||||
PBUInt64Field pBUInt64Field = (PBUInt64Field) PBUInt64Field.__repeatHelper__;
|
||||
this.rpt_uint64_depid = PBField.initRepeat((PBField)pBUInt64Field);
|
||||
this.rpt_uint64_self_depid = PBField.initRepeat((PBField)pBUInt64Field);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package tencent.im.oidb.cmd0x8a7;
|
||||
|
||||
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.PBField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBUInt64Field;
|
||||
|
||||
public class cmd0x8a7 {
|
||||
public static class ReqBody
|
||||
extends MessageMicro<ReqBody>
|
||||
{
|
||||
static final MessageMicro.FieldMap __fieldMap__;
|
||||
public final PBUInt32Field uint32_limit_interval_type_for_group = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_limit_interval_type_for_uin = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_sub_cmd = PBField.initUInt32(0);
|
||||
public final PBUInt64Field uint64_group_code = PBField.initUInt64(0L);
|
||||
public final PBUInt64Field uint64_uin = PBField.initUInt64(0L);
|
||||
|
||||
static
|
||||
{
|
||||
Integer localInteger = Integer.valueOf(0);
|
||||
Long localLong = Long.valueOf(0L);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24, 32, 40 }, new String[] { "uint32_sub_cmd", "uint32_limit_interval_type_for_uin", "uint32_limit_interval_type_for_group", "uint64_uin", "uint64_group_code" }, new Object[] { localInteger, localInteger, localInteger, localLong, localLong }, ReqBody.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RspBody
|
||||
extends MessageMicro<RspBody>
|
||||
{
|
||||
static final MessageMicro.FieldMap __fieldMap__;
|
||||
public final PBBoolField bool_can_at_all = PBField.initBool(false);
|
||||
public final PBBoolField bool_show_at_all_lable = PBField.initBool(false);
|
||||
public final PBBytesField bytes_prompt_msg_1 = PBField.initBytes(ByteStringMicro.EMPTY);
|
||||
public final PBBytesField bytes_prompt_msg_2 = PBField.initBytes(ByteStringMicro.EMPTY);
|
||||
public final PBUInt32Field uint32_remain_at_all_count_for_group = PBField.initUInt32(0);
|
||||
public final PBUInt32Field uint32_remain_at_all_count_for_uin = PBField.initUInt32(0);
|
||||
|
||||
static
|
||||
{
|
||||
Integer localInteger = Integer.valueOf(0);
|
||||
Boolean localBoolean = Boolean.valueOf(false);
|
||||
ByteStringMicro localByteStringMicro1 = ByteStringMicro.EMPTY;
|
||||
ByteStringMicro localByteStringMicro2 = ByteStringMicro.EMPTY;
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24, 34, 42, 48 }, new String[] { "bool_can_at_all", "uint32_remain_at_all_count_for_uin", "uint32_remain_at_all_count_for_group", "bytes_prompt_msg_1", "bytes_prompt_msg_2", "bool_show_at_all_lable" }, new Object[] { localBoolean, localInteger, localInteger, localByteStringMicro1, localByteStringMicro2, localBoolean }, RspBody.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,245 @@
|
||||
package tencent.im.oidb.cmd0xeb7;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBEnumField;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBFloatField;
|
||||
import com.tencent.mobileqq.pb.PBInt32Field;
|
||||
import com.tencent.mobileqq.pb.PBInt64Field;
|
||||
import com.tencent.mobileqq.pb.PBRepeatField;
|
||||
import com.tencent.mobileqq.pb.PBRepeatMessageField;
|
||||
import com.tencent.mobileqq.pb.PBStringField;
|
||||
|
||||
public class oidb_0xeb7 {
|
||||
public static class ReqBody extends MessageMicro<ReqBody> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18 }, new String[] { "signInStatusReq", "signInWriteReq" }, new Object[] { null, null }, ReqBody.class);
|
||||
|
||||
public StSignInStatusReq signInStatusReq = new StSignInStatusReq();
|
||||
|
||||
public StSignInWriteReq signInWriteReq = new StSignInWriteReq();
|
||||
}
|
||||
|
||||
|
||||
public static class RspBody extends MessageMicro<RspBody>
|
||||
{
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18 }, new String[] { "signInStatusRsp", "signInWriteRsp" }, new Object[] { null, null }, RspBody.class);
|
||||
public StSignInStatusRsp signInStatusRsp = new StSignInStatusRsp();
|
||||
public StSignInWriteRsp signInWriteRsp = new StSignInWriteRsp();
|
||||
}
|
||||
|
||||
|
||||
public static class StSignInWriteRsp
|
||||
extends MessageMicro<StSignInWriteRsp>
|
||||
{
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 26 }, new String[] { "ret", "doneInfo", "groupScore" }, new Object[] { null, null, null }, StSignInWriteRsp.class);
|
||||
public SignInStatusDoneInfo doneInfo = new SignInStatusDoneInfo();
|
||||
public SignInStatusGroupScore groupScore = new SignInStatusGroupScore();
|
||||
public Ret ret = new Ret();
|
||||
}
|
||||
|
||||
public static class StSignInStatusRsp
|
||||
extends MessageMicro<StSignInStatusRsp>
|
||||
{
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 26, 34, 42, 50, 58, 66 }, new String[] { "ret", "base", "yesterday", "notInfo", "doneInfo", "groupScore", "mantleUrl", "backgroundUrl" }, new Object[] { null, null, null, null, null, null, "", "" }, StSignInStatusRsp.class);
|
||||
public final PBStringField backgroundUrl = PBField.initString("");
|
||||
public SignInStatusBase base = new SignInStatusBase();
|
||||
public SignInStatusDoneInfo doneInfo = new SignInStatusDoneInfo();
|
||||
public SignInStatusGroupScore groupScore = new SignInStatusGroupScore();
|
||||
public final PBStringField mantleUrl = PBField.initString("");
|
||||
public SignInStatusNotInfo notInfo = new SignInStatusNotInfo();
|
||||
public Ret ret = new Ret();
|
||||
public SignInStatusYesterdayFirst yesterday = new SignInStatusYesterdayFirst();
|
||||
}
|
||||
|
||||
public static class SignInStatusYesterdayFirst
|
||||
extends MessageMicro<SignInStatusYesterdayFirst>
|
||||
{
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 26 }, new String[] { "yesterdayFirstUid", "yesterdayWord", "yesterdayNick" }, new Object[] { "", "", "" }, SignInStatusYesterdayFirst.class);
|
||||
public final PBStringField yesterdayFirstUid = PBField.initString("");
|
||||
public final PBStringField yesterdayNick = PBField.initString("");
|
||||
public final PBStringField yesterdayWord = PBField.initString("");
|
||||
}
|
||||
|
||||
public static class SignInStatusNotInfo
|
||||
extends MessageMicro<SignInStatusNotInfo>
|
||||
{
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 26 }, new String[] { "buttonWord", "signDescWordLeft", "signDescWordRight" }, new Object[] { "", "", "" }, SignInStatusNotInfo.class);
|
||||
public final PBStringField buttonWord = PBField.initString("");
|
||||
public final PBStringField signDescWordLeft = PBField.initString("");
|
||||
public final PBStringField signDescWordRight = PBField.initString("");
|
||||
}
|
||||
|
||||
public static class SignInStatusGroupScore
|
||||
extends MessageMicro<SignInStatusGroupScore>
|
||||
{
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18 }, new String[] { "groupScoreWord", "scoreUrl" }, new Object[] { "", "" }, SignInStatusGroupScore.class);
|
||||
public final PBStringField groupScoreWord = PBField.initString("");
|
||||
public final PBStringField scoreUrl = PBField.initString("");
|
||||
}
|
||||
|
||||
public static class SignInStatusDoneInfo
|
||||
extends MessageMicro<SignInStatusDoneInfo>
|
||||
{
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 26, 34 }, new String[] { "leftTitleWrod", "rightDescWord", "belowPortraitWords", "recordUrl" }, new Object[] { "", "", "", "" }, SignInStatusDoneInfo.class);
|
||||
public final PBRepeatField<String> belowPortraitWords = PBField.initRepeat(PBField.initString(""));
|
||||
public final PBStringField leftTitleWrod = PBField.initString("");
|
||||
public final PBStringField recordUrl = PBField.initString("");
|
||||
public final PBStringField rightDescWord = PBField.initString("");
|
||||
}
|
||||
|
||||
public static class StSignInRecordDaySigned extends MessageMicro<StSignInRecordDaySigned> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 13, 16, 26, 34 }, new String[] { "daySignedRatio", "dayTotalSignedUid", "daySignedPage", "daySignedUrl" }, new Object[] { Float.valueOf(0.0F), Integer.valueOf(0), null, "" }, StSignInRecordDaySigned.class);
|
||||
|
||||
public StDaySignedPage daySignedPage = new StDaySignedPage();
|
||||
|
||||
public final PBFloatField daySignedRatio = PBField.initFloat(0.0F);
|
||||
|
||||
public final PBStringField daySignedUrl = PBField.initString("");
|
||||
|
||||
public final PBInt32Field dayTotalSignedUid = PBField.initInt32(0);
|
||||
}
|
||||
|
||||
|
||||
public static class SignInStatusBase extends MessageMicro<SignInStatusBase> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16 }, new String[] { "status", "currentTimeStamp" }, new Object[] { Integer.valueOf(0), Long.valueOf(0L) }, SignInStatusBase.class);
|
||||
|
||||
public final PBInt64Field currentTimeStamp = PBField.initInt64(0L);
|
||||
|
||||
public final PBEnumField status = PBField.initEnum(0);
|
||||
}
|
||||
|
||||
|
||||
public static class StSignInRecordRsp extends MessageMicro<StSignInRecordRsp> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 26, 34, 42, 50 }, new String[] { "ret", "base", "userRecord", "daySigned", "kingRecord", "level" }, new Object[] { null, null, null, null, null, null }, StSignInRecordRsp.class);
|
||||
|
||||
public SignInStatusBase base = new SignInStatusBase();
|
||||
|
||||
public StSignInRecordDaySigned daySigned = new StSignInRecordDaySigned();
|
||||
|
||||
public StSignInRecordKing kingRecord = new StSignInRecordKing();
|
||||
|
||||
public StViewGroupLevel level = new StViewGroupLevel();
|
||||
|
||||
public Ret ret = new Ret();
|
||||
|
||||
public StSignInRecordUser userRecord = new StSignInRecordUser();
|
||||
}
|
||||
|
||||
public static class Ret extends MessageMicro<Ret> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 18 }, new String[] { "code", "msg" }, new Object[] { Integer.valueOf(0), "" }, Ret.class);
|
||||
|
||||
public final PBEnumField code = PBField.initEnum(0);
|
||||
|
||||
public final PBStringField msg = PBField.initString("");
|
||||
}
|
||||
|
||||
|
||||
public static class StSignInRecordUser extends MessageMicro<StSignInRecordUser> {
|
||||
static final MessageMicro.FieldMap __fieldMap__;
|
||||
|
||||
public final PBInt64Field continueSignedDays = PBField.initInt64(0L);
|
||||
|
||||
public final PBInt64Field earliestSignedTimeStamp = PBField.initInt64(0L);
|
||||
|
||||
public final PBStringField groupName = PBField.initString("");
|
||||
|
||||
public final PBRepeatField<String> historySignedDays = PBField.initRepeat(PBField.initString(""));
|
||||
|
||||
public final PBInt32Field totalSignedDays = PBField.initInt32(0);
|
||||
|
||||
static {
|
||||
Long long_ = Long.valueOf(0L);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 16, 24, 32, 42, 50 }, new String[] { "totalSignedDays", "earliestSignedTimeStamp", "continueSignedDays", "historySignedDays", "groupName" }, new Object[] { Integer.valueOf(0), long_, long_, "", "" }, StSignInRecordUser.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StDaySignedInfo extends MessageMicro<StDaySignedInfo> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 24, 32 }, new String[] { "uid", "uidGroupNick", "signedTimeStamp", "signInRank" }, new Object[] { "", "", Long.valueOf(0L), Integer.valueOf(0) }, StDaySignedInfo.class);
|
||||
|
||||
public final PBInt32Field signInRank = PBField.initInt32(0);
|
||||
|
||||
public final PBInt64Field signedTimeStamp = PBField.initInt64(0L);
|
||||
|
||||
public final PBStringField uid = PBField.initString("");
|
||||
|
||||
public final PBStringField uidGroupNick = PBField.initString("");
|
||||
}
|
||||
|
||||
public static class StDaySignedPage extends MessageMicro<StDaySignedPage> {
|
||||
static final MessageMicro.FieldMap __fieldMap__;
|
||||
|
||||
public final PBRepeatMessageField<StDaySignedInfo> infos = PBField.initRepeatMessage(StDaySignedInfo.class);
|
||||
|
||||
public final PBInt32Field offset = PBField.initInt32(0);
|
||||
|
||||
public final PBInt32Field total = PBField.initInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 16, 24 }, new String[] { "infos", "offset", "total" }, new Object[] { null, integer, integer }, StDaySignedPage.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StKingSignedInfo extends MessageMicro<StKingSignedInfo> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 24, 32 }, new String[] { "uid", "groupNick", "signedTimeStamp", "signedCount" }, new Object[] { "", "", Long.valueOf(0L), Integer.valueOf(0) }, StKingSignedInfo.class);
|
||||
|
||||
public final PBStringField groupNick = PBField.initString("");
|
||||
|
||||
public final PBInt32Field signedCount = PBField.initInt32(0);
|
||||
|
||||
public final PBInt64Field signedTimeStamp = PBField.initInt64(0L);
|
||||
|
||||
public final PBStringField uid = PBField.initString("");
|
||||
}
|
||||
|
||||
public static class StSignInStatusReq extends MessageMicro<StSignInStatusReq> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 24, 34 }, new String[] { "uid", "groupId", "scene", "clientVersion" }, new Object[] { "", "", Integer.valueOf(0), "" }, StSignInStatusReq.class);
|
||||
|
||||
public final PBStringField clientVersion = PBField.initString("");
|
||||
|
||||
public final PBStringField groupId = PBField.initString("");
|
||||
|
||||
public final PBEnumField scene = PBField.initEnum(0);
|
||||
|
||||
public final PBStringField uid = PBField.initString("");
|
||||
}
|
||||
|
||||
|
||||
public static class StSignInWriteReq extends MessageMicro<StSignInWriteReq> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 26 }, new String[] { "uid", "groupId", "clientVersion" }, new Object[] { "", "", "" }, StSignInWriteReq.class);
|
||||
|
||||
public final PBStringField clientVersion = PBField.initString("");
|
||||
|
||||
public final PBStringField groupId = PBField.initString("");
|
||||
|
||||
public final PBStringField uid = PBField.initString("");
|
||||
}
|
||||
|
||||
public static class StViewGroupLevel extends MessageMicro<StViewGroupLevel> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18 }, new String[] { "title", "url" }, new Object[] { "", "" }, StViewGroupLevel.class);
|
||||
|
||||
public final PBStringField title = PBField.initString("");
|
||||
|
||||
public final PBStringField url = PBField.initString("");
|
||||
}
|
||||
|
||||
|
||||
public static class StSignInRecordKing extends MessageMicro<StSignInRecordKing> {
|
||||
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 26, 34 }, new String[] { "yesterdayFirst", "topSignedTotal", "topSignedContinue", "kingUrl" }, new Object[] { null, null, null, "" }, StSignInRecordKing.class);
|
||||
|
||||
public final PBStringField kingUrl = PBField.initString("");
|
||||
|
||||
public final PBRepeatMessageField<StKingSignedInfo> topSignedContinue = PBField.initRepeatMessage(StKingSignedInfo.class);
|
||||
|
||||
public final PBRepeatMessageField<StKingSignedInfo> topSignedTotal = PBField.initRepeatMessage(StKingSignedInfo.class);
|
||||
|
||||
public StKingSignedInfo yesterdayFirst = new StKingSignedInfo();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,21 @@
|
||||
package tencent.im.troop.honor;
|
||||
|
||||
import com.tencent.mobileqq.pb.MessageMicro;
|
||||
import com.tencent.mobileqq.pb.PBField;
|
||||
import com.tencent.mobileqq.pb.PBRepeatField;
|
||||
import com.tencent.mobileqq.pb.PBUInt32Field;
|
||||
|
||||
public class troop_honor {
|
||||
public static class GroupUserCardHonor extends MessageMicro<GroupUserCardHonor> {
|
||||
static final FieldMap __fieldMap__;
|
||||
|
||||
public final PBRepeatField<Integer> id = PBField.initRepeat((PBField) PBUInt32Field.__repeatHelper__);
|
||||
|
||||
public final PBUInt32Field level = PBField.initUInt32(0);
|
||||
|
||||
static {
|
||||
Integer integer = Integer.valueOf(0);
|
||||
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16 }, new String[] { "id", "level" }, new Object[] { integer, integer }, GroupUserCardHonor.class);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
|
@ -8,12 +8,13 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "moe.fuqiuluo.xposed"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
minSdk = 27
|
||||
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
@Suppress("UnstableApiUsage")
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags += ""
|
||||
@ -96,5 +97,11 @@ dependencies {
|
||||
//ksp("androidx.room:room-compiler:$roomVersion")
|
||||
// optional - Kotlin Extensions and Coroutines support for Room
|
||||
implementation("androidx.room:room-ktx:$roomVersion")
|
||||
|
||||
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"
|
||||
|
||||
|
||||
|
||||
|
||||
|
26
xposed/src/main/cpp/anti_detection/anti_detection.h
Normal file
26
xposed/src/main/cpp/anti_detection/anti_detection.h
Normal file
@ -0,0 +1,26 @@
|
||||
#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);
|
||||
static int (*backup_memcmp)(const void* __lhs, const void* __rhs, size_t __n);
|
||||
//static const char* (*backup_strstr)(const char* h, const char* n);
|
||||
|
||||
//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
|
189
xposed/src/main/cpp/clover.cpp
Normal file
189
xposed/src/main/cpp/clover.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
#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, "nox") //部分NoxAppPlayer
|
||||
|| 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;
|
||||
}
|
||||
|
||||
const char* emuSpecFile[] = {
|
||||
"libhoudini.so",
|
||||
"libndk.so",
|
||||
"libnoxd.so", //NoxAppPlayer
|
||||
"libnoxspeedup.so", //NoxAppPlayer
|
||||
"nox-prop", //NoxAppPlayer (MayUseless?)
|
||||
"nox-vbox-sf", //NoxAppPlayer (MayUseless?)
|
||||
"noxd", //NoxAppPlayer (MayUseless?)
|
||||
"noxspeedup", //NoxAppPlayer (MayUseless?)
|
||||
};
|
||||
for (const char* keyword : emuSpecFile) {
|
||||
if (strstr(filename, keyword)) {
|
||||
LOGI("[Shamrock] bypass emu detection");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (strstr(filename, "libdobby.so")) {
|
||||
LOGI("[Shamrock] bypass dobby detection");
|
||||
return nullptr;
|
||||
}
|
||||
return backup_fopen(filename, mode);
|
||||
}
|
||||
|
||||
char * __cdecl my_strstr(const char *lhs, const char *rhs) {
|
||||
char *cur = (char *)lhs;
|
||||
char *l;
|
||||
char *r;
|
||||
if (!*rhs) {
|
||||
return ((char *)lhs);
|
||||
}
|
||||
while (*cur) {
|
||||
l = cur;
|
||||
r = (char *)rhs;
|
||||
while (*r && !(*l - *r)) {
|
||||
l++;
|
||||
r++;
|
||||
}
|
||||
if (!*r) {
|
||||
return cur;
|
||||
}
|
||||
cur++;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int fake_memcmp(const void* __lhs, const void* __rhs, size_t __n) {
|
||||
//if (my_strstr((const char*) __rhs, "lsposed")) {
|
||||
//return -1;
|
||||
//}
|
||||
//if (my_strstr((const char*) __rhs, "xposed")) {
|
||||
// return -1;
|
||||
//}
|
||||
if (my_strstr((const char*) __rhs, "shamrock")) {
|
||||
if (backup_memcmp(__lhs, __rhs, __n) == 0) {
|
||||
// 底层广播判断
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (my_strstr((const char*) __rhs, "riru")) {
|
||||
return -1;
|
||||
}
|
||||
//if (my_strstr((const char*) __rhs, "zygisk")) {
|
||||
// return -1;
|
||||
//}
|
||||
//if (my_strstr((const char*) __rhs, "magisk")) {
|
||||
// return -1;
|
||||
//}
|
||||
return backup_memcmp(__lhs, __rhs, __n);
|
||||
}
|
||||
|
||||
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);
|
||||
//hook_function((void*) strstr, (void*) fake_strstr, (void**) &backup_strstr);
|
||||
hook_function((void*) memcmp, (void*) fake_memcmp, (void**) &backup_memcmp);
|
||||
|
||||
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)
|
||||
|
@ -61,7 +61,7 @@ class ProtoMap(
|
||||
var curMap = value
|
||||
tags.forEachIndexed { index, tag ->
|
||||
if (index == tags.size - 1) {
|
||||
return curMap[tag] ?: error("Tag $tag not found")
|
||||
return curMap[tag] ?: error("pb[${tags.joinToString(", ")}][$index] Tag $tag not found")
|
||||
}
|
||||
curMap[tag]?.let { v ->
|
||||
if (v is ProtoMap) {
|
||||
@ -69,7 +69,7 @@ class ProtoMap(
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
} ?: error("Tag $tag not found")
|
||||
} ?: error("pb[${tags.joinToString(", ")}][$index] Tag $tag not found")
|
||||
}
|
||||
error("Instance is not ProtoMap for get(${tags.first()})")
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ internal object CardSvc: BaseSvc() {
|
||||
val dataService = app
|
||||
.getRuntimeService(IProfileDataService::class.java, "all")
|
||||
val card = refreshCardLock.withLock {
|
||||
suspendCancellableCoroutine<Card?> {
|
||||
suspendCancellableCoroutine {
|
||||
app.addObserver(object: ProfileCardObserver() {
|
||||
override fun onGetProfileCard(success: Boolean, obj: Any) {
|
||||
app.removeObserver(this)
|
||||
|
@ -166,7 +166,11 @@ 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() == oidb_0x6d8.GetFileListRspBody.TYPE_FOLDER) {
|
||||
|
@ -13,11 +13,9 @@ 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`
|
||||
@ -58,7 +56,10 @@ internal object FriendSvc: BaseSvc() {
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun requestFriendSystemMsgNew(msgNum: Int, latestFriendSeq: Long, latestGroupSeq: Long): List<StructMsg>? {
|
||||
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)
|
||||
@ -90,10 +91,18 @@ internal object FriendSvc: BaseSvc() {
|
||||
req.uint32_req_msg_type.set(1)
|
||||
req.uint32_need_uid.set(1)
|
||||
val respBuffer = sendBufferAW("ProfileService.Pb.ReqSystemMsgNew.Friend", true, req.toByteArray())
|
||||
?: return ArrayList()
|
||||
val msg = `structmsg$RspSystemMsgNew`()
|
||||
msg.mergeFrom(respBuffer.slice(4))
|
||||
return msg.friendmsgs.get()
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
package moe.fuqiuluo.qqinterface.servlet
|
||||
|
||||
import androidx.core.text.HtmlCompat
|
||||
import com.tencent.common.app.AppInterface
|
||||
import com.tencent.mobileqq.app.BusinessHandlerFactory
|
||||
import com.tencent.mobileqq.app.QQAppInterface
|
||||
@ -13,7 +14,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,22 +38,51 @@ 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.TicketSvc.getLongUin
|
||||
import moe.fuqiuluo.qqinterface.servlet.TicketSvc.getUin
|
||||
import moe.fuqiuluo.qqinterface.servlet.entries.GroupAtAllRemainInfo
|
||||
import moe.fuqiuluo.qqinterface.servlet.entries.ProhibitedMemberInfo
|
||||
import moe.fuqiuluo.shamrock.helper.Level
|
||||
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.utils.PlatformUtils
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
|
||||
import mqq.app.MobileQQ
|
||||
import tencent.im.group.group_member_info
|
||||
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.cmd0x8a7.cmd0x8a7
|
||||
import tencent.im.oidb.cmd0x8fc.Oidb_0x8fc
|
||||
import tencent.im.oidb.cmd0xeb7.oidb_0xeb7
|
||||
import tencent.im.oidb.oidb_sso
|
||||
import tencent.im.troop.honor.troop_honor
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Modifier
|
||||
import java.nio.ByteBuffer
|
||||
@ -60,6 +105,27 @@ internal object GroupSvc: BaseSvc() {
|
||||
private lateinit var METHOD_REQ_TROOP_MEM_LIST: Method
|
||||
private lateinit var METHOD_REQ_MODIFY_GROUP_NAME: Method
|
||||
|
||||
suspend fun getGroupRemainAtAllRemain (groupId: Long): Result<GroupAtAllRemainInfo> {
|
||||
val buffer = sendOidbAW("OidbSvcTrpcTcp.0x8a7_0", 2215, 0, cmd0x8a7.ReqBody().apply {
|
||||
uint32_sub_cmd.set(1)
|
||||
uint32_limit_interval_type_for_uin.set(2)
|
||||
uint32_limit_interval_type_for_group.set(1)
|
||||
uint64_uin.set(getLongUin())
|
||||
uint64_group_code.set(groupId)
|
||||
}.toByteArray(), trpc = true) ?: 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 = cmd0x8a7.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray())
|
||||
return Result.success(GroupAtAllRemainInfo(
|
||||
canAtAll = resp.bool_can_at_all.get(),
|
||||
remainAtAllCountForGroup = resp.uint32_remain_at_all_count_for_group.get(),
|
||||
remainAtAllCountForUin = resp.uint32_remain_at_all_count_for_uin.get()
|
||||
))
|
||||
}
|
||||
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)
|
||||
@ -113,7 +179,7 @@ internal object GroupSvc: BaseSvc() {
|
||||
|
||||
var troopList = service.allTroopList
|
||||
if(refresh || !service.isTroopCacheInited || troopList == null) {
|
||||
if(!requestGroupList(service)) {
|
||||
if(!requestGroupInfo(service)) {
|
||||
return Result.failure(Exception("获取群列表失败"))
|
||||
} else {
|
||||
troopList = service.allTroopList
|
||||
@ -126,15 +192,14 @@ internal object GroupSvc: BaseSvc() {
|
||||
val service = app
|
||||
.getRuntimeService(ITroopInfoService::class.java, "all")
|
||||
|
||||
var groupInfo = getGroupInfo(groupId)
|
||||
val groupInfo = getGroupInfo(groupId)
|
||||
|
||||
if(refresh || !service.isTroopCacheInited || groupInfo.troopuin.isNullOrBlank()) {
|
||||
groupInfo = requestGroupList(service, groupId.toLong()).onFailure {
|
||||
return Result.failure(it)
|
||||
}.getOrThrow()
|
||||
return if(refresh || !service.isTroopCacheInited || groupInfo.troopuin.isNullOrBlank()) {
|
||||
requestGroupInfo(service, groupId.toLong())
|
||||
} else {
|
||||
Result.success(groupInfo)
|
||||
}
|
||||
|
||||
return Result.success(groupInfo)
|
||||
}
|
||||
|
||||
suspend fun setGroupUniqueTitle(groupId: String, userId: String, title: String) {
|
||||
@ -143,7 +208,7 @@ internal object GroupSvc: BaseSvc() {
|
||||
req.uint64_group_code.set(groupId.toLong())
|
||||
val memberInfo = Oidb_0x8fc.MemberInfo()
|
||||
memberInfo.uint64_uin.set(userId.toLong())
|
||||
memberInfo.bytes_uin_name.set(ByteStringMicro.copyFromUtf8(localMemberInfo.troopnick.ifBlank {
|
||||
memberInfo.bytes_uin_name.set(ByteStringMicro.copyFromUtf8(localMemberInfo.troopnick.ifEmpty {
|
||||
localMemberInfo.troopremark.ifNullOrEmpty("")
|
||||
}))
|
||||
memberInfo.bytes_special_title.set(ByteStringMicro.copyFromUtf8(title))
|
||||
@ -284,7 +349,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 {
|
||||
@ -386,6 +451,36 @@ internal object GroupSvc: BaseSvc() {
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (info != null && (info.alias == null || info.alias.isBlank())) {
|
||||
val req = group_member_info.ReqBody()
|
||||
req.uint64_group_code.set(groupId.toLong())
|
||||
req.uint64_uin.set(uin.toLong())
|
||||
req.bool_new_client.set(true)
|
||||
req.uint32_client_type.set(1)
|
||||
req.uint32_rich_card_name_ver.set(1)
|
||||
val respBuffer = sendBufferAW("group_member_card.get_group_member_card_info", true, req.toByteArray())
|
||||
if (respBuffer != null) {
|
||||
val rsp = group_member_info.RspBody()
|
||||
rsp.mergeFrom(respBuffer.slice(4))
|
||||
if (rsp.msg_meminfo.str_location.has()) {
|
||||
info.alias = rsp.msg_meminfo.str_location.get().toStringUtf8()
|
||||
}
|
||||
if (rsp.msg_meminfo.uint32_age.has()) {
|
||||
info.age = rsp.msg_meminfo.uint32_age.get().toByte()
|
||||
}
|
||||
if (rsp.msg_meminfo.bytes_group_honor.has()) {
|
||||
val honorBytes = rsp.msg_meminfo.bytes_group_honor.get().toByteArray()
|
||||
val honor = troop_honor.GroupUserCardHonor()
|
||||
honor.mergeFrom(honorBytes)
|
||||
info.level = honor.level.get()
|
||||
// 10315: medal_id not real group level
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err: Throwable) {
|
||||
LogCenter.log(err.stackTraceToString(), Level.WARN)
|
||||
}
|
||||
return if (info != null) {
|
||||
Result.success(info)
|
||||
} else {
|
||||
@ -465,7 +560,7 @@ internal object GroupSvc: BaseSvc() {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun requestGroupList(
|
||||
private suspend fun requestGroupInfo(
|
||||
service: ITroopInfoService
|
||||
): Boolean {
|
||||
refreshTroopList()
|
||||
@ -492,7 +587,7 @@ internal object GroupSvc: BaseSvc() {
|
||||
throw RuntimeException("AppRuntime cannot cast to AppInterface")
|
||||
val businessHandler = app.getBusinessHandler(BusinessHandlerFactory.TROOP_MEMBER_LIST_HANDLER)
|
||||
|
||||
// void C(boolean foreRefresh, String groupId, String troopcode, int reqType); // RequestedTroopList/refreshMemberListFromServer
|
||||
// void C(boolean forceRefresh, String groupId, String troopcode, int reqType); // RequestedTroopList/refreshMemberListFromServer
|
||||
if (!GroupSvc::METHOD_REQ_TROOP_MEM_LIST.isInitialized) {
|
||||
METHOD_REQ_TROOP_MEM_LIST = businessHandler.javaClass.declaredMethods.first {
|
||||
it.parameterCount == 4
|
||||
@ -558,9 +653,9 @@ internal object GroupSvc: BaseSvc() {
|
||||
METHOD_REQ_MEMBER_INFO_V2.invoke(businessHandler, groupId.toString(), groupUin2GroupCode(groupId).toString(), arrayListOf(memberUin.toString()))
|
||||
}
|
||||
|
||||
private suspend fun requestGroupList(dataService: ITroopInfoService, uin: Long): Result<TroopInfo> {
|
||||
private suspend fun requestGroupInfo(dataService: ITroopInfoService, uin: Long): Result<TroopInfo> {
|
||||
val strUin = uin.toString()
|
||||
val list = withTimeoutOrNull(5000) {
|
||||
val info = withTimeoutOrNull(5000) {
|
||||
var troopInfo: TroopInfo?
|
||||
do {
|
||||
troopInfo = dataService.getTroopInfo(strUin)
|
||||
@ -568,8 +663,8 @@ internal object GroupSvc: BaseSvc() {
|
||||
} while (troopInfo == null || troopInfo.troopuin.isNullOrBlank())
|
||||
return@withTimeoutOrNull troopInfo
|
||||
}
|
||||
return if (list != null) {
|
||||
Result.success(list)
|
||||
return if (info != null) {
|
||||
Result.success(info)
|
||||
} else {
|
||||
Result.failure(Exception("获取群列表失败"))
|
||||
}
|
||||
@ -609,21 +704,6 @@ internal object GroupSvc: BaseSvc() {
|
||||
notSee: Boolean? = false,
|
||||
subType: String
|
||||
): Result<String>{
|
||||
// val app = AppRuntimeFetcher.appRuntime
|
||||
// if (app !is AppInterface)
|
||||
// throw RuntimeException("AppRuntime cannot cast to AppInterface")
|
||||
// val service = QRoute.api(IAddFriendTempApi::class.java)
|
||||
// val action = `structmsg$SystemMsgActionInfo`()
|
||||
// action.type.set(if (approve != false) 11 else 12)
|
||||
// action.group_code.set(gid)
|
||||
// action.msg.set(msg)
|
||||
// val snInfo = `structmsg$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(2, msgSeq * 1000, uin, 1, 2, 30024, 1, action, 0, `structmsg$StructMsg`(), false,
|
||||
// app
|
||||
// )
|
||||
// 实在找不到接口了 发pb吧
|
||||
val buffer: ByteArray
|
||||
when (subType) {
|
||||
@ -654,7 +734,9 @@ internal object GroupSvc: BaseSvc() {
|
||||
7 to 1,
|
||||
8 to mapOf(
|
||||
1 to if (approve != false) 11 else 12,
|
||||
2 to gid
|
||||
2 to gid,
|
||||
50 to msg,
|
||||
53 to if (notSee != false) 1 else 0
|
||||
),
|
||||
9 to 1000
|
||||
).toByteArray()
|
||||
@ -670,14 +752,17 @@ internal object GroupSvc: BaseSvc() {
|
||||
if (result[1, 1].asInt == 0) {
|
||||
Result.success(result[2].asUtf8String)
|
||||
} else {
|
||||
Result.failure(Exception(result[2].asUtf8String))
|
||||
Result.failure(Exception(result[1, 2].asUtf8String))
|
||||
}
|
||||
} else {
|
||||
Result.failure(Exception("操作失败"))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun requestGroupSystemMsgNew(msgNum: Int, latestFriendSeq: Long, latestGroupSeq: Long): List<StructMsg>? {
|
||||
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)
|
||||
@ -706,12 +791,213 @@ internal object GroupSvc: BaseSvc() {
|
||||
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_req_msg_type.set(reqMsgType)
|
||||
req.uint32_need_uid.set(1)
|
||||
val respBuffer = sendBufferAW("ProfileService.Pb.ReqSystemMsgNew.Group", true, req.toByteArray())
|
||||
?: return ArrayList()
|
||||
val msg = RspSystemMsgNew()
|
||||
msg.mergeFrom(respBuffer.slice(4))
|
||||
return msg.groupmsgs.get()
|
||||
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,
|
||||
text = fromHtml(obj["msg"].asJsonObject["text"].asString),
|
||||
images = obj["msg"].asJsonObject["pics"].asJsonArrayOrNull?.map { pic ->
|
||||
GroupAnnouncementMessageImage(
|
||||
id = pic.jsonObject["id"].asString,
|
||||
width = pic.jsonObject["w"].asString,
|
||||
height = pic.jsonObject["h"].asString,
|
||||
)
|
||||
} ?: ArrayList()
|
||||
)
|
||||
)
|
||||
})
|
||||
} else {
|
||||
return Result.failure(Exception(body.jsonObject["em"].asStringOrNull))
|
||||
}
|
||||
}
|
||||
|
||||
private fun fromHtml(htmlString: String): String {
|
||||
return HtmlCompat
|
||||
// 特殊处理 ,目的是替换为换行符,否则会被fromHtml忽略并移除
|
||||
.fromHtml(htmlString.replace(" ", "[shamrockplaceholder]"), HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
.toString()
|
||||
.replace("[shamrockplaceholder]", "\n")
|
||||
}
|
||||
|
||||
@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))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun groupSign(groupId: Long): Result<String> {
|
||||
val req = oidb_0xeb7.ReqBody()
|
||||
val signInWriteReq = oidb_0xeb7.StSignInWriteReq()
|
||||
signInWriteReq.groupId.set(groupId.toString())
|
||||
signInWriteReq.uid.set(getUin())
|
||||
var version = PlatformUtils.getClientVersion(MobileQQ.getContext())
|
||||
version = version.replace("android", "").trimStart()
|
||||
signInWriteReq.clientVersion.set(version)
|
||||
req.signInWriteReq.set(signInWriteReq)
|
||||
val buffer = sendOidbAW("OidbSvc.0xeb7", 3767, 1, req.toByteArray())
|
||||
return if (buffer == null) {
|
||||
Result.failure(Exception("操作失败"))
|
||||
} else {
|
||||
val body = oidb_sso.OIDBSSOPkg()
|
||||
body.mergeFrom(buffer.slice(4))
|
||||
val rsp = oidb_0xeb7.RspBody()
|
||||
rsp.mergeFrom(body.bytes_bodybuffer.get().toByteArray())
|
||||
val doneInfo = rsp.signInWriteRsp.doneInfo
|
||||
LogCenter.log(rsp.toString(), Level.DEBUG)
|
||||
Result.success("${doneInfo.leftTitleWrod.get()} ${doneInfo.rightDescWord.get()} ${doneInfo.belowPortraitWords.get().joinToString(" ")}")
|
||||
}
|
||||
}
|
||||
}
|
@ -16,14 +16,13 @@ import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.time.withTimeoutOrNull
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import moe.fuqiuluo.proto.protobufOf
|
||||
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
||||
import moe.fuqiuluo.shamrock.helper.Level
|
||||
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
|
||||
@ -32,10 +31,6 @@ 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
|
||||
@ -174,22 +169,29 @@ internal object MsgSvc: BaseSvc() {
|
||||
chatType: Int,
|
||||
peedId: String,
|
||||
message: JsonArray,
|
||||
fromId: String = peedId
|
||||
): Pair<Long, Int> {
|
||||
//LogCenter.log(message.toString(), Level.ERROR)
|
||||
//callback.msgHash = result.second 什么垃圾代码,万一cb比你快,你不就寄了?
|
||||
|
||||
fromId: String = peedId,
|
||||
retryCnt: Int = 3
|
||||
): Result<Pair<Long, Int>> {
|
||||
// 主动临时消息
|
||||
when(chatType) {
|
||||
when (chatType) {
|
||||
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> {
|
||||
prepareTempChatFromGroup(fromId, peedId).onFailure {
|
||||
LogCenter.log("主动临时消息,创建临时会话失败。", Level.ERROR)
|
||||
return -1L to 0
|
||||
return Result.failure(Exception("主动临时消息,创建临时会话失败。"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, MessageCallback(peedId, 0), fromId)
|
||||
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>> {
|
||||
|
436
xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/QFavSvc.kt
Normal file
436
xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/QFavSvc.kt
Normal file
@ -0,0 +1,436 @@
|
||||
package moe.fuqiuluo.qqinterface.servlet
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import com.tencent.mobileqq.app.QQAppInterface
|
||||
import com.tencent.mobileqq.transfile.HttpNetReq
|
||||
import com.tencent.mobileqq.transfile.INetEngineListener
|
||||
import com.tencent.mobileqq.transfile.NetReq
|
||||
import com.tencent.mobileqq.transfile.NetResp
|
||||
import com.tencent.mobileqq.transfile.ServerAddr
|
||||
import com.tencent.mobileqq.transfile.api.IHttpEngineService
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.io.core.BytePacketBuilder
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.io.core.writeFully
|
||||
import moe.fuqiuluo.proto.protobufMapOf
|
||||
import moe.fuqiuluo.shamrock.helper.Level
|
||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||
import moe.fuqiuluo.shamrock.tools.hex2ByteArray
|
||||
import moe.fuqiuluo.shamrock.tools.toHexString
|
||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||
import moe.fuqiuluo.shamrock.utils.MD5
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
||||
import mqq.manager.TicketManager
|
||||
import oicq.wlogin_sdk.request.Ticket
|
||||
import oicq.wlogin_sdk.request.WtTicketPromise
|
||||
import oicq.wlogin_sdk.tools.ErrMsg
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
|
||||
/**
|
||||
* QQ收藏相关接口
|
||||
*/
|
||||
internal object QFavSvc: BaseSvc() {
|
||||
private val SERVER_LIST_COLLECTOR = listOf(ServerAddr().also {
|
||||
it.isIpv6 = false
|
||||
it.mIp = "collector.weiyun.com"
|
||||
it.port = 80
|
||||
})
|
||||
private val SERVER_LIST_PICUP = listOf(ServerAddr().also {
|
||||
it.isIpv6 = false
|
||||
it.mIp = "pic.pieceup.qq.com"
|
||||
it.port = 80
|
||||
})
|
||||
private const val VERSION = 12820
|
||||
private const val APPID = 30244
|
||||
private const val SUB_APPID = 538116905
|
||||
private const val MAJOR_VERSION = 8
|
||||
private const val MINOR_VERSION = 9
|
||||
private var seq = 1
|
||||
|
||||
suspend fun getItemList(
|
||||
category: Int,
|
||||
startPos: Int,
|
||||
pageSize: Int,
|
||||
): Result<NetResp> {
|
||||
val data = protobufMapOf {
|
||||
it[1] = mapOf(
|
||||
20000 to mapOf(
|
||||
/**
|
||||
* "type", "bid", "category", "start_time", "order_type", "start_pos", "page_size", "sync_policy", "req_source"
|
||||
*/
|
||||
1 to 0,
|
||||
2 to 0,
|
||||
3 to category,
|
||||
//4 to System.currentTimeMillis() - 1000 * 60,
|
||||
//4 to System.currentTimeMillis(),
|
||||
4 to 0,
|
||||
5 to 0,
|
||||
6 to startPos,
|
||||
7 to pageSize,
|
||||
8 to 0,
|
||||
9 to 0
|
||||
)
|
||||
)
|
||||
}.toByteArray()
|
||||
return sendWeiyunReq(20000, data)
|
||||
}
|
||||
|
||||
suspend fun getItemContent(
|
||||
id: String
|
||||
): Result<NetResp> {
|
||||
val data = protobufMapOf {
|
||||
it[1] = mapOf(
|
||||
20001 to mapOf(
|
||||
1 to id
|
||||
)
|
||||
)
|
||||
}.toByteArray()
|
||||
return sendWeiyunReq(20001, data)
|
||||
}
|
||||
|
||||
suspend fun addImageMsg(
|
||||
uin: Long,
|
||||
name: String,
|
||||
groupId: Long = 0,
|
||||
groupName: String = "",
|
||||
picUrl: String,
|
||||
pid: String,
|
||||
width: Int, height: Int,
|
||||
size: Long,
|
||||
md5: String,
|
||||
): Result<NetResp> {
|
||||
val md5Bytes = md5.hex2ByteArray()
|
||||
val data = protobufMapOf {
|
||||
it[1] = mapOf(
|
||||
20009 to mapOf(
|
||||
1 to mapOf(
|
||||
1 to 1, // bid
|
||||
2 to 1, // category
|
||||
3 to mapOf( // author
|
||||
1 to if (groupId == 0L) 1 else 2, // type
|
||||
2 to uin, // num_id
|
||||
3 to name, // str_id
|
||||
4 to groupId, // group_id
|
||||
5 to groupName // group_name
|
||||
),
|
||||
4 to System.currentTimeMillis() - 2000, // create_time
|
||||
5 to System.currentTimeMillis() - 1000, // sequence
|
||||
7 to """{"recordAudioOnly":false,"audioOnly":false,"fileOnly":false}""",
|
||||
9 to 0, // original_app_id
|
||||
10 to 0 // custom_group_id
|
||||
),
|
||||
2 to mapOf(
|
||||
1 to "",
|
||||
3 to "[图片]",
|
||||
4 to mapOf(
|
||||
1 to picUrl,
|
||||
2 to md5Bytes,
|
||||
3 to md5,
|
||||
6 to width,
|
||||
7 to height,
|
||||
8 to size,
|
||||
9 to 0,
|
||||
11 to pid
|
||||
),
|
||||
5 to 1
|
||||
),
|
||||
3 to mapOf(
|
||||
2 to """<img src="$picUrl" />""",
|
||||
4 to mapOf(
|
||||
1 to picUrl,
|
||||
2 to md5Bytes,
|
||||
3 to md5,
|
||||
6 to width,
|
||||
7 to height,
|
||||
8 to size,
|
||||
9 to 0,
|
||||
11 to pid
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}.toByteArray()
|
||||
return sendWeiyunReq(20009, data)
|
||||
}
|
||||
|
||||
suspend fun applyUpImageMsg(
|
||||
uin: Long,
|
||||
name: String,
|
||||
groupId: Long = 0,
|
||||
groupName: String = "",
|
||||
width: Int, height: Int,
|
||||
image: File
|
||||
): Result<NetResp> {
|
||||
if (!image.exists()) {
|
||||
return Result.failure(IllegalArgumentException("image file not exists"))
|
||||
}
|
||||
val md5 = MD5.genFileMd5(image.absolutePath)
|
||||
val data = protobufMapOf {
|
||||
it[1] = mapOf(
|
||||
20010 to mapOf(
|
||||
1 to mapOf(
|
||||
2 to md5,
|
||||
4 to md5.toHexString(),
|
||||
10 to mapOf( // author
|
||||
1 to if (groupId == 0L) 1 else 2, // type
|
||||
2 to uin, // num_id
|
||||
3 to name, // str_id
|
||||
4 to groupId, // group_id
|
||||
5 to groupName // group_name
|
||||
),
|
||||
6 to width, // width
|
||||
7 to height,
|
||||
8 to image.length(),
|
||||
9 to 1, // type
|
||||
11 to "/storage/emulated/0/DCIM/ShamrockUpload.jpeg" // pic_id
|
||||
)
|
||||
)
|
||||
)
|
||||
}.toByteArray()
|
||||
return sendWeiyunReq(20010, data)
|
||||
}
|
||||
|
||||
suspend fun addRichMediaMsg(
|
||||
uin: Long,
|
||||
name: String,
|
||||
groupId: Long = 0,
|
||||
groupName: String = "",
|
||||
time: Long = System.currentTimeMillis(),
|
||||
content: String
|
||||
): Result<NetResp> {
|
||||
val data = protobufMapOf {
|
||||
it[1] = mapOf(
|
||||
20009 to mapOf(
|
||||
1 to mapOf(
|
||||
/**
|
||||
* 1 => bid
|
||||
* 2 => category
|
||||
* 3 => author
|
||||
* 4 => create_time
|
||||
* 5 => sequence
|
||||
* 6 => biz_key
|
||||
* 7 => biz_data_list
|
||||
* 8 => share_url
|
||||
* 9 => original_app_id
|
||||
* 10 => custom_group_id
|
||||
* 506 => modify_time
|
||||
* 507 => qzone_ugc_key
|
||||
*/
|
||||
1 to 1, // bid
|
||||
2 to 1, // category
|
||||
3 to mapOf( // author
|
||||
1 to if (groupId == 0L) 1 else 2, // type
|
||||
2 to uin, // num_id
|
||||
3 to name, // str_id
|
||||
4 to groupId, // group_id
|
||||
5 to groupName // group_name
|
||||
),
|
||||
4 to time - 2000, // create_time
|
||||
5 to time - 1000, // sequence
|
||||
9 to 0, // original_app_id
|
||||
10 to 0 // custom_group_id
|
||||
),
|
||||
2 to mapOf(
|
||||
/**
|
||||
* 1 => title
|
||||
* 2 => sub_title
|
||||
* 3 => brief
|
||||
* 4 => pic_list
|
||||
* 5 => content_type
|
||||
* 6 => original_uri
|
||||
* 7 => publisher
|
||||
* 8 => rich_media_version
|
||||
*/
|
||||
3 to content,
|
||||
5 to 1
|
||||
),
|
||||
3 to mapOf(
|
||||
/**
|
||||
* 1 => rich_media
|
||||
* 2 => raw_data
|
||||
* 3 => biz_data_list
|
||||
* 4 => pic_list
|
||||
* 5 => file_list
|
||||
*/
|
||||
2 to content.textToHtml()
|
||||
)
|
||||
)
|
||||
)
|
||||
}.toByteArray()
|
||||
return sendWeiyunReq(20009, data)
|
||||
}
|
||||
|
||||
private fun String.textToHtml(): String {
|
||||
return replace("\n", "<div><br/></div>")
|
||||
}
|
||||
|
||||
suspend fun sendPicUpBlock(
|
||||
fileSize: Long,
|
||||
offset: Long,
|
||||
block: ByteArray,
|
||||
blockSize: Long,
|
||||
sha: ByteArray,
|
||||
pid: String,
|
||||
outputStream: ByteArrayOutputStream = ByteArrayOutputStream(),
|
||||
): Result<NetResp> {
|
||||
return suspendCancellableCoroutine {
|
||||
val httpNetReq = HttpNetReq()
|
||||
httpNetReq.userData = null
|
||||
httpNetReq.mCallback = object: INetEngineListener {
|
||||
override fun onResp(netResp: NetResp) {
|
||||
if (netResp.mHttpCode != 200 && netResp.mResult != 0 && netResp.mErrDesc.isNullOrEmpty()) {
|
||||
netResp.mErrDesc = netResp.mRespProperties["User-ErrMsg"]
|
||||
}
|
||||
netResp.mRespData = outputStream.toByteArray().copyOf()
|
||||
it.resume(Result.success(netResp))
|
||||
}
|
||||
|
||||
override fun onUpdateProgeress(netReq: NetReq, curr: Long, final: Long) {}
|
||||
}
|
||||
val vi = (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getA2(app.currentAccountUin)
|
||||
//LogCenter.log(pSKey)
|
||||
httpNetReq.mHttpMethod = HttpNetReq.HTTP_POST
|
||||
httpNetReq.mSendData = BytePacketBuilder().apply {
|
||||
writeInt(-1412589450)
|
||||
writeInt(10000)
|
||||
writeInt(0)
|
||||
writeInt(sha.size + 16 + blockSize.toInt())
|
||||
writeShort(0)
|
||||
writeShort(sha.size.toShort())
|
||||
writeFully(sha)
|
||||
writeInt(fileSize.toInt())
|
||||
writeInt(offset.toInt())
|
||||
writeInt(blockSize.toInt())
|
||||
writeFully(block)
|
||||
}.build().readBytes()
|
||||
httpNetReq.mOutStream = outputStream
|
||||
httpNetReq.mStartDownOffset = 0L
|
||||
httpNetReq.mReqProperties["Shamrock"] = "true"
|
||||
httpNetReq.mReqProperties["Cookie"] = String.format("uin=%s;vt=%d;vi=%s;pid=%s;appid=%d", app.currentAccountUin, 8, vi, pid, APPID)
|
||||
httpNetReq.mReqProperties["host"] = "pic.pieceup.qq.com"
|
||||
httpNetReq.mReqProperties["Range"] = "bytes=0-"
|
||||
httpNetReq.mReqProperties["Content-Length"] = httpNetReq.mSendData.size.toString()
|
||||
httpNetReq.mReqProperties["Accept-Encoding"] = "gzip"
|
||||
httpNetReq.mReqProperties["Content-Encoding"] = "gzip"
|
||||
httpNetReq.mPrioty = 1
|
||||
httpNetReq.mReqUrl = "https://pic.pieceup.qq.com/"
|
||||
httpNetReq.mServerList = SERVER_LIST_PICUP
|
||||
val service = AppRuntimeFetcher.appRuntime
|
||||
.getRuntimeService(IHttpEngineService::class.java, "qqfav")
|
||||
service.sendReq(httpNetReq)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendWeiyunReq(
|
||||
cmd: Int,
|
||||
body: ByteArray,
|
||||
outputStream: ByteArrayOutputStream = ByteArrayOutputStream(),
|
||||
): Result<NetResp> {
|
||||
return suspendCancellableCoroutine {
|
||||
val httpNetReq = HttpNetReq()
|
||||
httpNetReq.userData = null
|
||||
httpNetReq.mCallback = object: INetEngineListener {
|
||||
override fun onResp(netResp: NetResp) {
|
||||
if (netResp.mHttpCode != 200 && netResp.mResult != 0 && netResp.mErrDesc.isNullOrEmpty()) {
|
||||
netResp.mErrDesc = netResp.mRespProperties["User-ErrMsg"]
|
||||
}
|
||||
netResp.mRespData = outputStream.toByteArray().copyOf()
|
||||
it.resume(Result.success(netResp))
|
||||
}
|
||||
|
||||
override fun onUpdateProgeress(netReq: NetReq, curr: Long, final: Long) {}
|
||||
}
|
||||
val pSKey = getWeiYunPSKey()
|
||||
httpNetReq.mHttpMethod = HttpNetReq.HTTP_POST
|
||||
httpNetReq.mSendData = DeflateTools.gzip(packData(packHead(cmd, pSKey), body))
|
||||
httpNetReq.mOutStream = outputStream
|
||||
httpNetReq.mStartDownOffset = 0L
|
||||
httpNetReq.mReqProperties["Shamrock"] = "true"
|
||||
httpNetReq.mReqProperties["Cookie"] = String.format("uin=%s;vt=%d;vi=%s;appid=%d", app.currentAccountUin, 27, pSKey, APPID)
|
||||
httpNetReq.mReqProperties["host"] = "collector.weiyun.com"
|
||||
httpNetReq.mReqProperties["Range"] = "bytes=0-"
|
||||
httpNetReq.mReqProperties["Content-Length"] = httpNetReq.mSendData.size.toString()
|
||||
httpNetReq.mReqProperties["Accept-Encoding"] = "gzip"
|
||||
httpNetReq.mReqProperties["Content-Encoding"] = "gzip"
|
||||
httpNetReq.mPrioty = 1
|
||||
httpNetReq.mReqUrl = "https://collector.weiyun.com/collector.fcg"
|
||||
httpNetReq.mServerList = SERVER_LIST_COLLECTOR
|
||||
val service = AppRuntimeFetcher.appRuntime
|
||||
.getRuntimeService(IHttpEngineService::class.java, "qqfav")
|
||||
service.sendReq(httpNetReq)
|
||||
}
|
||||
}
|
||||
|
||||
private fun packHead(cmd: Int, pskey: String): ByteArray {
|
||||
/**
|
||||
* 1 => uin
|
||||
* 2 => seq
|
||||
* 3 => type
|
||||
* 4 => cmd
|
||||
* 5 => appid
|
||||
* 6 => version
|
||||
* 7 => nettype
|
||||
* 8 => clientip
|
||||
* 9 => encrypt
|
||||
* 10 => keytype
|
||||
* 11 => encryptkey
|
||||
* 14 => major_version
|
||||
* 15 => minor_version
|
||||
* 101 => retcode
|
||||
* 102 => retmsg
|
||||
* 103 => promptmsg
|
||||
* 111 => total_space
|
||||
* 112 => used_space
|
||||
*/
|
||||
return protobufMapOf {
|
||||
it[1] = app.longAccountUin
|
||||
it[2] = seq++ // seq
|
||||
it[3] = 1 // type
|
||||
it[4] = cmd
|
||||
it[5] = APPID
|
||||
it[6] = VERSION // VERSION
|
||||
it[7] = 3 // nettype
|
||||
it[10] = 27 // keytype
|
||||
it[11] = pskey
|
||||
it[14] = MAJOR_VERSION
|
||||
it[15] = MINOR_VERSION
|
||||
}.toByteArray()
|
||||
}
|
||||
|
||||
private fun packData(head: ByteArray, body: ByteArray): ByteArray {
|
||||
val len = 16 + head.size + body.size
|
||||
val buf = ByteBuffer.allocate(len)
|
||||
buf.putInt(SUB_APPID)
|
||||
buf.putShort(1)
|
||||
buf.putInt(len)
|
||||
buf.putInt(body.size)
|
||||
buf.putShort(0)
|
||||
buf.put(head)
|
||||
buf.put(body)
|
||||
return buf.array()
|
||||
}
|
||||
|
||||
private fun getWeiYunPSKey(): String {
|
||||
val pskey = (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager)
|
||||
.getPskey(app.currentAccountUin, 16L, arrayOf("weiyun.com"), WeiYunPSKeyPromise)
|
||||
return if (pskey != null) pskey.getPSkey("weiyun.com") else ""
|
||||
}
|
||||
|
||||
private object WeiYunPSKeyPromise: WtTicketPromise {
|
||||
override fun Done(ticket: Ticket) {
|
||||
LogCenter.log("Fav: getPskeyPromise: done", Level.DEBUG)
|
||||
}
|
||||
|
||||
override fun Failed(errMsg: ErrMsg) {
|
||||
LogCenter.log("Fav: getPskeyPromise: failed, $errMsg", Level.DEBUG)
|
||||
}
|
||||
|
||||
override fun Timeout(errMsg: ErrMsg) {
|
||||
LogCenter.log("Fav: getPskeyPromise: timeout, $errMsg", Level.DEBUG)
|
||||
}
|
||||
}
|
||||
}
|
@ -74,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()
|
||||
|
@ -27,6 +27,10 @@ data class FileInfo(
|
||||
@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
|
||||
|
@ -7,4 +7,11 @@ import kotlinx.serialization.Serializable
|
||||
internal data class ProhibitedMemberInfo(
|
||||
@SerialName("user_id") val memberUin: Long,
|
||||
@SerialName("time") val shutuptimestap: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
internal data class GroupAtAllRemainInfo(
|
||||
@SerialName("can_at_all") val canAtAll: Boolean,
|
||||
@SerialName("remain_at_all_count_for_group") val remainAtAllCountForGroup: Int,
|
||||
@SerialName("remain_at_all_count_for_uin") val remainAtAllCountForUin: Int
|
||||
)
|
@ -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>
|
||||
|
||||
@ -96,9 +101,91 @@ internal object MessageMaker {
|
||||
"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,
|
||||
@ -107,7 +194,20 @@ internal object MessageMaker {
|
||||
): Result<MsgElement> {
|
||||
data.checkAndThrow("data")
|
||||
val jsonStr = data["data"].let {
|
||||
if (it is JsonObject) it.asJsonObject.toString() else it.asString
|
||||
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
|
||||
@ -174,7 +274,7 @@ internal object MessageMaker {
|
||||
element.elementType = MsgConstant.KELEMTYPEREPLY
|
||||
val reply = ReplyElement()
|
||||
|
||||
val msgHash = data["id"].asString.toInt()
|
||||
val msgHash = data["id"].asInt
|
||||
val mapping = MessageHelper.getMsgMappingByHash(msgHash)
|
||||
?: return Result.failure(Exception("不存在该消息映射,无法回复消息"))
|
||||
|
||||
@ -527,10 +627,16 @@ internal object MessageMaker {
|
||||
else -> {
|
||||
val info = GroupSvc.getTroopMemberInfoByUin(peerId, qq, true).onFailure {
|
||||
LogCenter.log("无法获取群成员信息: $qq", Level.ERROR)
|
||||
}.getOrThrow()
|
||||
at.content = "@${info.troopnick
|
||||
.ifNullOrEmpty(info.friendnick)
|
||||
.ifNullOrEmpty(qq)}"
|
||||
}.getOrNull()
|
||||
if (info != null) {
|
||||
at.content = "@${
|
||||
info.troopnick
|
||||
.ifNullOrEmpty(info.friendnick)
|
||||
.ifNullOrEmpty(qq)
|
||||
}"
|
||||
} else {
|
||||
at.content = "@${data["name"].asStringOrNull.ifNullOrEmpty(qq)}"
|
||||
}
|
||||
at.atType = MsgConstant.ATTYPEONE
|
||||
at.atNtUid = ContactHelper.getUidByUinAsync(qq.toLong())
|
||||
}
|
||||
@ -634,24 +740,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)
|
||||
|
@ -53,6 +53,7 @@ internal object MessageConvert {
|
||||
MsgConstant.KELEMTYPEREPLY to MessageElemConverter.ReplyConverter,
|
||||
MsgConstant.KELEMTYPEGRAYTIP to MessageElemConverter.GrayTipsConverter,
|
||||
MsgConstant.KELEMTYPEFILE to MessageElemConverter.FileConverter,
|
||||
MsgConstant.KELEMTYPEMARKDOWN to MessageElemConverter.MarkdownConverter,
|
||||
//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
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,23 +319,24 @@ 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,
|
||||
/* 群头衔 */2407L -> {}
|
||||
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, /* 群打卡 */1068L -> {}
|
||||
else -> LogCenter.log("不支持的灰条类型(XML): ${notify.busiId}", Level.WARN)
|
||||
}
|
||||
}
|
||||
else -> LogCenter.log("不支持的提示类型: $tip", Level.WARN)
|
||||
else -> LogCenter.log("不支持的提示类型: ${tip.subElementType}", Level.WARN)
|
||||
}
|
||||
// 提示类消息,这里提供的是一个xml,不具备解析通用性
|
||||
// 在这里不推送
|
||||
@ -327,10 +358,10 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
||||
val fileSize = fileMsg.fileSize
|
||||
val expireTime = fileMsg.expireTime ?: 0
|
||||
val fileId = fileMsg.fileUuid
|
||||
val bizId = fileMsg.fileBizId
|
||||
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, fileMsg.fileBizId)
|
||||
else RichProtoSvc.getGroupFileDownUrl(peerId.toLong(), fileId, bizId)
|
||||
|
||||
return MessageSegment(
|
||||
type = "file",
|
||||
@ -382,6 +413,22 @@ internal sealed class MessageElemConverter: IMessageConvert {
|
||||
}
|
||||
}
|
||||
|
||||
object MarkdownConverter: MessageElemConverter() {
|
||||
override suspend fun convert(
|
||||
chatType: Int,
|
||||
peerId: String,
|
||||
element: MsgElement
|
||||
): MessageSegment {
|
||||
val markdown = element.markdownElement
|
||||
return MessageSegment(
|
||||
type = "markdown",
|
||||
data = mapOf(
|
||||
"content" to markdown.content
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun unknownChatType(chatType: Int) {
|
||||
throw UnsupportedOperationException("Not supported chat type: $chatType")
|
||||
}
|
||||
|
@ -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,11 @@ 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.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
@ -20,6 +25,8 @@ 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 {
|
||||
@ -32,35 +39,75 @@ internal object MessageHelper {
|
||||
): Pair<Long, Int> {
|
||||
val uniseq = generateMsgId(chatType)
|
||||
val msg = messageArrayToMessageElements(chatType, uniseq.second, peerId, decodeCQCode(message)).also {
|
||||
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
|
||||
if (it.second.isEmpty() && !it.first) {
|
||||
error("消息合成失败,请查看日志或者检查输入。")
|
||||
} else if (it.second.isEmpty()) {
|
||||
return System.currentTimeMillis() to 0
|
||||
}
|
||||
}.second.filter {
|
||||
it.elementType != -1
|
||||
} as ArrayList<MsgElement>
|
||||
return sendMessageWithoutMsgId(chatType, peerId, msg, callback, fromId)
|
||||
return sendMessageWithoutMsgId(chatType, peerId, msg, fromId, callback)
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
suspend fun sendMessageWithoutMsgId(
|
||||
chatType: Int,
|
||||
peerId: String,
|
||||
message: JsonArray,
|
||||
callback: IOperateCallback,
|
||||
fromId: String = peerId
|
||||
): Pair<Long, Int> {
|
||||
fromId: String = peerId,
|
||||
callback: IOperateCallback
|
||||
): Result<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>
|
||||
return sendMessageWithoutMsgId(chatType, peerId, msg, callback, fromId)
|
||||
|
||||
// ActionMsg No Care
|
||||
if (msg.isEmpty()) {
|
||||
return Result.success(System.currentTimeMillis() to 0)
|
||||
}
|
||||
|
||||
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 + 2000
|
||||
|
||||
lateinit var sendResultPair: Pair<Long, Int>
|
||||
val sendRet = withTimeoutOrNull<Pair<Int, String>>(estimateTime) {
|
||||
suspendCancellableCoroutine {
|
||||
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>,
|
||||
callback: IOperateCallback,
|
||||
fromId: String = peerId
|
||||
fromId: String = peerId,
|
||||
callback: IOperateCallback
|
||||
): Pair<Long, Int> {
|
||||
return sendMessageWithoutMsgId(generateContact(chatType, peerId, fromId), message, callback)
|
||||
}
|
||||
@ -74,7 +121,7 @@ internal object MessageHelper {
|
||||
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
|
||||
}
|
||||
|
||||
@ -107,7 +154,7 @@ internal object MessageHelper {
|
||||
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
|
||||
}
|
||||
|
||||
@ -132,7 +179,7 @@ internal object MessageHelper {
|
||||
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
|
||||
}
|
||||
|
||||
@ -148,6 +195,32 @@ internal object MessageHelper {
|
||||
}
|
||||
}
|
||||
|
||||
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 || MsgConstant.KCHATTYPETEMPC2CFROMGROUP == chatType) {
|
||||
ContactHelper.getUidByUinAsync(id.toLong())
|
||||
@ -156,7 +229,7 @@ internal object MessageHelper {
|
||||
}
|
||||
|
||||
fun obtainMessageTypeByDetailType(detailType: String): Int {
|
||||
return when(detailType) {
|
||||
return when (detailType) {
|
||||
"troop", "group" -> MsgConstant.KCHATTYPEGROUP
|
||||
"private" -> MsgConstant.KCHATTYPEC2C
|
||||
"less" -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
|
||||
@ -166,7 +239,7 @@ internal object MessageHelper {
|
||||
}
|
||||
|
||||
fun obtainDetailTypeByMsgType(msgType: Int): String {
|
||||
return when(msgType) {
|
||||
return when (msgType) {
|
||||
MsgConstant.KCHATTYPEGROUP -> "group"
|
||||
MsgConstant.KCHATTYPEC2C -> "private"
|
||||
MsgConstant.KCHATTYPEGUILD -> "guild"
|
||||
@ -180,9 +253,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)
|
||||
@ -193,16 +266,19 @@ 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"
|
||||
|
@ -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,
|
||||
|
@ -64,6 +64,7 @@ internal object HTTPServer {
|
||||
guildAction()
|
||||
testAction()
|
||||
requestRouter()
|
||||
fav()
|
||||
if (ShamrockConfig.isDev()) {
|
||||
qsign()
|
||||
obtainProtocolData()
|
||||
|
@ -23,7 +23,7 @@ internal object ActionManager {
|
||||
// UserActions
|
||||
GetProfileCard, GetFriendList, SendLike, GetUid, GetUinByUid, ScanQRCode, SetProfileCard,
|
||||
GetCookies, GetCSRF, GetCredentials, RestartMe, CleanCache, GetModelShow, SetModelShow,
|
||||
GetModelShowList, GetOnlineClients, GetStrangerInfo, IsBlackListUin, GetHttpCookies,
|
||||
GetModelShowList, GetOnlineClients, GetStrangerInfo, IsBlackListUin, GetHttpCookies, GetFriendSystemMsg,
|
||||
|
||||
// GroupInfo
|
||||
GetTroopList, GetTroopInfo, GetTroopList, GetTroopMemberInfo, GetTroopMemberList,
|
||||
@ -31,28 +31,32 @@ internal object ActionManager {
|
||||
// GroupActions
|
||||
ModifyTroopName, LeaveTroop, KickTroopMember, BanTroopMember, SetGroupWholeBan, SetGroupAdmin,
|
||||
ModifyTroopMemberName, SetGroupUnique, GetTroopHonor, GroupPoke, SetEssenceMessage, DeleteEssenceMessage,
|
||||
GetGroupSystemMsg, GetProhibitedMemberList,
|
||||
GetGroupSystemMsg, GetProhibitedMemberList, GetEssenceMessageList, GetGroupNotice, SendGroupNotice, SendGroupSign,
|
||||
GetGroupRemainAtAllRemain,
|
||||
|
||||
// MSG ACTIONS
|
||||
SendMessage, DeleteMessage, GetMsg, GetForwardMsg, SendGroupForwardMsg, SendGroupMessage, SendPrivateMessage,
|
||||
ClearMsgs, GetHistoryMsg, GetGroupMsgHistory, SendPrivateForwardMsg,
|
||||
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
|
||||
// REQUEST ACTION
|
||||
SetFriendAddRequest, SetGroupAddRequest,
|
||||
|
||||
// GUILD
|
||||
GetGuildServiceProfile,
|
||||
GetGuildServiceProfile, GetGuildList,
|
||||
|
||||
// WEATHER
|
||||
GetWeatherCityCode, GetWeather,
|
||||
|
||||
// FAV
|
||||
FavAddTextMsg, FavAddImageMsg, FavGetItemContent, FavGetItemList,
|
||||
|
||||
// OTHER
|
||||
GetDeviceBattery, DownloadFile
|
||||
GetDeviceBattery, DownloadFile, QuickOperation
|
||||
).forEach {
|
||||
it.alias.forEach { name ->
|
||||
actionMap[name] = it
|
||||
@ -193,8 +197,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,155 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.proto.ProtoUtils
|
||||
import moe.fuqiuluo.proto.asUtf8String
|
||||
import moe.fuqiuluo.qqinterface.servlet.QFavSvc
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
import moe.fuqiuluo.shamrock.utils.CryptTools
|
||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||
import moe.fuqiuluo.shamrock.utils.FileUtils
|
||||
|
||||
internal object FavAddImageMsg: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
val uin = session.getLong("user_id")
|
||||
val nickName = session.getString("nick")
|
||||
val groupName = session.getStringOrNull("group_name") ?: ""
|
||||
val groupId = session.getLongOrNull("group_id") ?: 0L
|
||||
val file = session.getString("file")
|
||||
return invoke(uin, nickName, file, groupName, groupId, session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(
|
||||
uin: Long,
|
||||
nickName: String,
|
||||
fileText: String,
|
||||
groupName: String = "",
|
||||
groupId: Long = 0L,
|
||||
echo: JsonElement = EmptyJsonString
|
||||
): String {
|
||||
val image = fileText.let {
|
||||
val md5 = it.replace(regex = "[{}\\-]".toRegex(), replacement = "").split(".")[0].lowercase()
|
||||
if (md5.length == 32) {
|
||||
FileUtils.getFile(it)
|
||||
} else {
|
||||
FileUtils.parseAndSave(it)
|
||||
}
|
||||
}
|
||||
|
||||
val options = BitmapFactory.Options()
|
||||
BitmapFactory.decodeFile(image.absolutePath, options)
|
||||
lateinit var picUrl: String
|
||||
lateinit var picId: String
|
||||
lateinit var itemId: String
|
||||
lateinit var md5: String
|
||||
|
||||
QFavSvc.applyUpImageMsg(uin, nickName,
|
||||
image = image,
|
||||
groupName = groupName,
|
||||
groupId = groupId,
|
||||
width = options.outWidth,
|
||||
height = options.outHeight
|
||||
).onSuccess {
|
||||
if (it.mHttpCode == 200 && it.mResult == 0) {
|
||||
val readPacket = ByteReadPacket(DeflateTools.ungzip(it.mRespData))
|
||||
readPacket.discardExact(6)
|
||||
val allLength = readPacket.readInt()
|
||||
val dataLength = readPacket.readInt()
|
||||
val headLength = allLength - dataLength - 16
|
||||
readPacket.discardExact(2)
|
||||
ByteArray(headLength).also {
|
||||
readPacket.readFully(it, 0, it.size)
|
||||
}
|
||||
val data = ByteArray(dataLength).also {
|
||||
readPacket.readFully(it, 0, it.size)
|
||||
}
|
||||
val pb = ProtoUtils.decodeFromByteArray(data)
|
||||
val resp = pb[2, 20010, 1, 2]
|
||||
picUrl = resp[1].asUtf8String
|
||||
picId = resp[11].asUtf8String
|
||||
md5 = resp[4].asUtf8String
|
||||
} else {
|
||||
return logic(it.mErrDesc, echo)
|
||||
}
|
||||
}.onFailure {
|
||||
return error(it.message ?: it.toString(), echo)
|
||||
}
|
||||
|
||||
val sha = CryptTools
|
||||
.getSHA1("/storage/emulated/0/Android/data/com.tencent.mobileqq/Tencent/QQ_Collection/pic/" + md5.uppercase() + "_0")
|
||||
|
||||
image.inputStream().use {
|
||||
var offset = 0L
|
||||
val block = ByteArray(131072)
|
||||
var rest = image.length()
|
||||
do {
|
||||
val length = if (rest <= 131072) rest else 131072L
|
||||
if(it.read(block, 0, length.toInt()) != -1) {
|
||||
QFavSvc.sendPicUpBlock(
|
||||
fileSize = image.length(),
|
||||
offset = offset,
|
||||
block = block,
|
||||
blockSize = length,
|
||||
pid = picId,
|
||||
sha = sha
|
||||
).onFailure {
|
||||
return error(it.message ?: it.toString(), echo)
|
||||
}
|
||||
offset += length
|
||||
rest -= length
|
||||
} else {
|
||||
rest = -1
|
||||
}
|
||||
} while (rest > 0)
|
||||
}
|
||||
|
||||
QFavSvc.addImageMsg(
|
||||
uin, nickName, groupId, groupName, picUrl, picId, options.outWidth, options.outHeight, image.length(), md5.uppercase()
|
||||
).onFailure {
|
||||
return error(it.message ?: it.toString(), echo)
|
||||
}.onSuccess {
|
||||
if (it.mHttpCode == 200 && it.mResult == 0) {
|
||||
val readPacket = ByteReadPacket(DeflateTools.ungzip(it.mRespData))
|
||||
readPacket.discardExact(6)
|
||||
val allLength = readPacket.readInt()
|
||||
val dataLength = readPacket.readInt()
|
||||
val headLength = allLength - dataLength - 16
|
||||
readPacket.discardExact(2)
|
||||
ByteArray(headLength).also {
|
||||
readPacket.readFully(it, 0, it.size)
|
||||
}
|
||||
val data = ByteArray(dataLength).also {
|
||||
readPacket.readFully(it, 0, it.size)
|
||||
}
|
||||
val pb = ProtoUtils.decodeFromByteArray(data)
|
||||
itemId = pb[2, 20009, 1].asUtf8String
|
||||
}
|
||||
|
||||
System.gc()
|
||||
}
|
||||
|
||||
return ok(PicInfo(
|
||||
picUrl = picUrl,
|
||||
picId = picId,
|
||||
id = itemId
|
||||
), echo)
|
||||
}
|
||||
|
||||
override fun path(): String = "fav.add_image_msg"
|
||||
|
||||
override val requiredParams: Array<String> = arrayOf("user_id", "nick", "file")
|
||||
|
||||
@Serializable
|
||||
private data class PicInfo(
|
||||
@SerialName("pic_url") val picUrl: String,
|
||||
@SerialName("pic_id") val picId: String,
|
||||
@SerialName("id") val id: String
|
||||
)
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.proto.ProtoUtils
|
||||
import moe.fuqiuluo.proto.asUtf8String
|
||||
import moe.fuqiuluo.qqinterface.servlet.QFavSvc
|
||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
import moe.fuqiuluo.shamrock.tools.toHexString
|
||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||
|
||||
internal object FavAddTextMsg: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
val uin = session.getLong("user_id")
|
||||
val nickName = session.getString("nick")
|
||||
val groupName = session.getStringOrNull("group_name") ?: ""
|
||||
val groupId = session.getLongOrNull("group_id") ?: 0L
|
||||
val time = session.getLongOrNull("time") ?: System.currentTimeMillis()
|
||||
val content = session.getString("content")
|
||||
return invoke(uin, nickName, time, content, groupName, groupId, session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(
|
||||
uin: Long,
|
||||
nickName: String,
|
||||
time: Long = System.currentTimeMillis(),
|
||||
content: String,
|
||||
groupName: String = "",
|
||||
groupId: Long = 0L,
|
||||
echo: JsonElement = EmptyJsonString
|
||||
): String {
|
||||
QFavSvc.addRichMediaMsg(uin, nickName,
|
||||
time = time,
|
||||
content = content,
|
||||
groupName = groupName,
|
||||
groupId = groupId
|
||||
).onSuccess {
|
||||
return if (it.mHttpCode == 200 && it.mResult == 0) {
|
||||
val readPacket = ByteReadPacket(DeflateTools.ungzip(it.mRespData))
|
||||
readPacket.discardExact(6)
|
||||
val allLength = readPacket.readInt()
|
||||
val dataLength = readPacket.readInt()
|
||||
val headLength = allLength - dataLength - 16
|
||||
readPacket.discardExact(2)
|
||||
ByteArray(headLength).also {
|
||||
readPacket.readFully(it, 0, it.size)
|
||||
}
|
||||
val data = ByteArray(dataLength).also {
|
||||
readPacket.readFully(it, 0, it.size)
|
||||
}
|
||||
val pb = ProtoUtils.decodeFromByteArray(data)
|
||||
|
||||
ok(data = QFavItem(
|
||||
pb[2, 20009, 1].asUtf8String
|
||||
), echo)
|
||||
} else {
|
||||
logic(it.mErrDesc, echo)
|
||||
}
|
||||
}
|
||||
return ok("请求已提交", echo)
|
||||
}
|
||||
|
||||
override fun path(): String = "fav.add_text_msg"
|
||||
|
||||
override val requiredParams: Array<String> = arrayOf("user_id", "nick", "content")
|
||||
|
||||
@Serializable
|
||||
private data class QFavItem(
|
||||
@SerialName("id") val id: String
|
||||
)
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.proto.ProtoUtils
|
||||
import moe.fuqiuluo.proto.asUtf8String
|
||||
import moe.fuqiuluo.qqinterface.servlet.QFavSvc
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
import moe.fuqiuluo.shamrock.tools.toHexString
|
||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||
|
||||
internal object FavGetItemContent: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
val id = session.getString("id")
|
||||
return invoke(id, session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(
|
||||
id: String,
|
||||
echo: JsonElement = EmptyJsonString
|
||||
): String {
|
||||
val respData = DeflateTools.ungzip(QFavSvc.getItemContent(id).onSuccess {
|
||||
if (it.mHttpCode != 200 || it.mResult != 0) {
|
||||
return logic(it.mErrDesc, echo)
|
||||
}
|
||||
}.getOrThrow().mRespData)
|
||||
val readPacket = ByteReadPacket(respData)
|
||||
readPacket.discardExact(6)
|
||||
val allLength = readPacket.readInt()
|
||||
val dataLength = readPacket.readInt()
|
||||
val headLength = allLength - dataLength - 16
|
||||
readPacket.discardExact(2)
|
||||
ByteArray(headLength).also {
|
||||
readPacket.readFully(it, 0, it.size)
|
||||
}
|
||||
val data = ByteArray(dataLength).also {
|
||||
readPacket.readFully(it, 0, it.size)
|
||||
}
|
||||
val pb = ProtoUtils.decodeFromByteArray(data)
|
||||
|
||||
return ok(ItemContent(pb[2, 20001, 1, 8, 2].asUtf8String))
|
||||
}
|
||||
|
||||
override fun path(): String = "fav.get_item_content"
|
||||
|
||||
override val requiredParams: Array<String> = arrayOf("id")
|
||||
|
||||
@Serializable
|
||||
private data class ItemContent(
|
||||
@SerialName("content") val content: String
|
||||
)
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.proto.ProtoUtils
|
||||
import moe.fuqiuluo.proto.asInt
|
||||
import moe.fuqiuluo.proto.asList
|
||||
import moe.fuqiuluo.proto.asLong
|
||||
import moe.fuqiuluo.proto.asMap
|
||||
import moe.fuqiuluo.proto.asULong
|
||||
import moe.fuqiuluo.proto.asUtf8String
|
||||
import moe.fuqiuluo.qqinterface.servlet.QFavSvc
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
import moe.fuqiuluo.shamrock.tools.toHexString
|
||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||
|
||||
internal object FavGetItemList: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
val category = session.getInt("category")
|
||||
val startPos = session.getInt("start_pos")
|
||||
val pageSize = session.getInt("page_size")
|
||||
return invoke(category, startPos, pageSize, session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(
|
||||
category: Int,
|
||||
startPos: Int,
|
||||
pageSize: Int,
|
||||
echo: JsonElement = EmptyJsonString
|
||||
): String {
|
||||
if (pageSize <= 1) {
|
||||
return logic("page_size must be greater than 1", echo)
|
||||
}
|
||||
|
||||
val result = DeflateTools.ungzip(QFavSvc.getItemList(
|
||||
category = category,
|
||||
startPos = startPos,
|
||||
pageSize = pageSize
|
||||
).onSuccess {
|
||||
if (it.mHttpCode != 200 || it.mResult != 0) {
|
||||
return logic("fav.get_item_list failed", echo)
|
||||
}
|
||||
}.getOrThrow().mRespData)
|
||||
val readPacket = ByteReadPacket(result)
|
||||
readPacket.discardExact(6)
|
||||
val allLength = readPacket.readInt()
|
||||
val dataLength = readPacket.readInt()
|
||||
val headLength = allLength - dataLength - 16
|
||||
readPacket.discardExact(2)
|
||||
ByteArray(headLength).also {
|
||||
readPacket.readFully(it, 0, it.size)
|
||||
}
|
||||
val data = ByteArray(dataLength).also {
|
||||
readPacket.readFully(it, 0, it.size)
|
||||
}
|
||||
val pb = ProtoUtils.decodeFromByteArray(data)
|
||||
|
||||
val itemList = arrayListOf<Item>()
|
||||
val rawItemList = pb[2, 20000, 1].asList
|
||||
rawItemList.value.forEach {
|
||||
val item = it.asMap
|
||||
val itemId = item[1].asUtf8String
|
||||
val authorType = item[4, 1].asInt
|
||||
val author = item[4, 2].asULong
|
||||
val authorName = item[4, 3].asUtf8String
|
||||
val groupName: String
|
||||
val groupId: Long
|
||||
if (authorType == 2) {
|
||||
groupName = item[4, 5].asUtf8String
|
||||
groupId = item[4, 4].asULong
|
||||
} else {
|
||||
groupName = ""
|
||||
groupId = 0L
|
||||
}
|
||||
val clientVersion = item[7].asUtf8String
|
||||
val time = item[9].asLong
|
||||
itemList.add(Item(
|
||||
id = itemId,
|
||||
authorType = authorType,
|
||||
author = author,
|
||||
authorName = authorName,
|
||||
groupName = groupName,
|
||||
groupId = groupId,
|
||||
clientVersion = clientVersion,
|
||||
time = time
|
||||
))
|
||||
}
|
||||
|
||||
return ok(ItemList(itemList), echo)
|
||||
}
|
||||
|
||||
override val requiredParams: Array<String> = arrayOf("category", "start_pos", "page_size")
|
||||
|
||||
override fun path(): String = "fav.get_item_list"
|
||||
|
||||
@Serializable
|
||||
private data class ItemList(
|
||||
val items: List<Item>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class Item(
|
||||
@SerialName("id") val id: String,
|
||||
@SerialName("author_type") val authorType: Int,
|
||||
@SerialName("author") val author: Long,
|
||||
@SerialName("author_name") val authorName: String,
|
||||
@SerialName("group_name") val groupName: String,
|
||||
@SerialName("group_id") val groupId: Long,
|
||||
@SerialName("client_version") val clientVersion: String,
|
||||
@SerialName("time") val time: Long
|
||||
)
|
||||
}
|
@ -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"
|
||||
}
|
@ -37,9 +37,9 @@ internal object GetForwardMsg: IActionHandler() {
|
||||
realId = msg.msgSeq.toInt(),
|
||||
sender = MessageSender(
|
||||
msg.senderUin, msg.sendNickName
|
||||
.ifBlank { msg.sendMemberName }
|
||||
.ifBlank { msg.sendRemarkName }
|
||||
.ifBlank { msg.peerName }, "unknown", 0, msg.senderUid
|
||||
.ifEmpty { msg.sendMemberName }
|
||||
.ifEmpty { msg.sendRemarkName }
|
||||
.ifEmpty { msg.peerName }, "unknown", 0, msg.senderUid
|
||||
),
|
||||
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
||||
it.toJson()
|
||||
|
@ -0,0 +1,45 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.qqinterface.servlet.FriendSvc
|
||||
import moe.fuqiuluo.shamrock.helper.Level
|
||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.remote.service.data.FriendRequest
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
|
||||
internal object GetFriendSystemMsg : IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
return invoke(echo = session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(echo: JsonElement = EmptyJsonString): String {
|
||||
val list = FriendSvc.requestFriendSystemMsgNew(20)
|
||||
val msgs = list
|
||||
// 13 是加别人好友
|
||||
?.filter { it.msg.sub_type.get() != 13 }
|
||||
?.map {
|
||||
LogCenter.log(it.toString(), Level.WARN)
|
||||
FriendRequest(
|
||||
seq = it.msg_seq.get(),
|
||||
userId = it.req_uin.get(),
|
||||
name = it.msg.req_uin_nick.get(),
|
||||
source = it.msg.msg_source.get(),
|
||||
subId = it.msg.src_id.get(),
|
||||
subSrcId = it.msg.sub_src_id.get(),
|
||||
msg = it.msg.msg_additional.get(),
|
||||
sourceGroupName = it.msg.group_name.get(),
|
||||
sourceGroupCode = it.msg.group_code.get(),
|
||||
flag = "${it.msg_seq.get()};${it.msg.src_id.get()};${it.msg.sub_src_id.get()};${it.req_uin.get()}",
|
||||
sex = if (it.msg.req_uin_gender.get() == 1) "female" else "male",
|
||||
age = it.msg.req_uin_age.get(),
|
||||
msgDetail = it.msg.msg_detail.get(),
|
||||
status = it.msg.msg_decided.get()
|
||||
)
|
||||
} ?: mutableListOf()
|
||||
return ok(msgs, echo = echo)
|
||||
}
|
||||
|
||||
override fun path(): String = "get_friend_system_msg"
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
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 GetGroupNotice: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
val groupId = session.getLong("group_id")
|
||||
return invoke(groupId, session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(groupId: Long, echo: JsonElement = EmptyJsonString): String {
|
||||
val announcements = GroupSvc.getGroupAnnouncements(groupId)
|
||||
if (announcements.isSuccess) {
|
||||
return ok(announcements.getOrNull(), echo)
|
||||
}
|
||||
return logic(announcements.exceptionOrNull()?.message ?: "", echo)
|
||||
|
||||
}
|
||||
|
||||
override val alias: Array<String> = arrayOf("get_group_notice")
|
||||
override fun path(): String = "_get_group_notice"
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
|
||||
internal object GetGroupRemainAtAllRemain: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
val groupId = session.getLong("group_id")
|
||||
return invoke(groupId, session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(
|
||||
groupId: Long,
|
||||
echo: JsonElement = EmptyJsonString
|
||||
): String {
|
||||
val result = GroupSvc.getGroupRemainAtAllRemain(groupId)
|
||||
if (result.isFailure) {
|
||||
return error(result.exceptionOrNull()?.message ?: "获取群 @全体成员 剩余次数失败", echo, arrayResult = true)
|
||||
}
|
||||
return ok(result.getOrThrow(), echo)
|
||||
}
|
||||
|
||||
override val requiredParams: Array<String> = arrayOf("group_id")
|
||||
|
||||
override fun path(): String = "get_group_at_all_remain"
|
||||
}
|
@ -10,16 +10,17 @@ import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
|
||||
internal object GetGroupSystemMsg: IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
return invoke(session.echo)
|
||||
return invoke(echo = session.echo)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(echo: JsonElement = EmptyJsonString): String {
|
||||
val list = GroupSvc.requestGroupSystemMsgNew(20, 0, 0)
|
||||
val list = GroupSvc.requestGroupSystemMsgNew(20)
|
||||
val riskList = GroupSvc.requestGroupSystemMsgNew(20, 2)
|
||||
val msgs = GroupSystemMessage(
|
||||
invited = mutableListOf(),
|
||||
join = mutableListOf()
|
||||
)
|
||||
list?.forEach {
|
||||
(list + riskList).forEach {
|
||||
when(it.msg.group_msg_type.get()) {
|
||||
22, 1 -> {
|
||||
// join 进群消息
|
||||
@ -41,8 +42,8 @@ internal object GetGroupSystemMsg: IActionHandler() {
|
||||
// invite 别人邀请我
|
||||
msgs.invited += GroupRequest (
|
||||
msgSeq = it.msg_seq.get(),
|
||||
invitorUin = null,
|
||||
invitorNick = null,
|
||||
invitorUin = it.msg.action_uin.get(),
|
||||
invitorNick = it.msg.action_uin_nick.get(),
|
||||
groupId = it.msg.group_code.get(),
|
||||
groupName = it.msg.group_name.get(),
|
||||
checked = it.msg.msg_decided.get().isNotBlank(),
|
||||
@ -60,7 +61,5 @@ internal object GetGroupSystemMsg: IActionHandler() {
|
||||
return ok(msgs, echo = echo)
|
||||
}
|
||||
|
||||
override val requiredParams: Array<String> = arrayOf("group_id", "folder_id")
|
||||
|
||||
override fun path(): String = "get_group_files_by_folder"
|
||||
override fun path(): String = "get_group_system_msg"
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonArray
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
|
||||
internal object GetGuildList : IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
return invoke(echo = session.echo)
|
||||
}
|
||||
|
||||
operator fun invoke(echo: JsonElement = EmptyJsonString): String {
|
||||
// TODO: get_guild_list
|
||||
return ok(EmptyJsonArray, echo, "此功能尚未实现")
|
||||
}
|
||||
|
||||
override fun path(): String = "get_guild_list"
|
||||
}
|
@ -4,16 +4,19 @@ import com.tencent.mobileqq.qqguildsdk.api.IGPSService
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import moe.fuqiuluo.shamrock.remote.action.ActionSession
|
||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonObject
|
||||
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
|
||||
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
||||
import mqq.app.MobileQQ
|
||||
|
||||
internal object GetGuildServiceProfile: IActionHandler() {
|
||||
internal object GetGuildServiceProfile : IActionHandler() {
|
||||
override suspend fun internalHandle(session: ActionSession): String {
|
||||
TODO("Not yet implemented")
|
||||
return invoke(echo = session.echo)
|
||||
}
|
||||
|
||||
operator fun invoke(echo: JsonElement = EmptyJsonString): String {
|
||||
// TODO: get_guild_service_profile
|
||||
return ok(EmptyJsonObject, echo, "此功能尚未实现")
|
||||
|
||||
val service = AppRuntimeFetcher.appRuntime
|
||||
.getRuntimeService(IGPSService::class.java, "all")
|
||||
if (!service.isGProSDKInitCompleted) {
|
||||
|
@ -85,9 +85,9 @@ internal object GetHistoryMsg: IActionHandler() {
|
||||
realId = seq,
|
||||
sender = MessageSender(
|
||||
msg.senderUin, msg.sendNickName
|
||||
.ifBlank { msg.sendMemberName }
|
||||
.ifBlank { msg.sendRemarkName }
|
||||
.ifBlank { msg.peerName }, "unknown", 0, msg.senderUid
|
||||
.ifEmpty { msg.sendMemberName }
|
||||
.ifEmpty { msg.sendRemarkName }
|
||||
.ifEmpty { msg.peerName }, "unknown", 0, msg.senderUid
|
||||
),
|
||||
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
||||
it.toJson()
|
||||
|
@ -30,9 +30,9 @@ internal object GetMsg: IActionHandler() {
|
||||
realId = seq,
|
||||
sender = MessageSender(
|
||||
msg.senderUin, msg.sendNickName
|
||||
.ifBlank { msg.sendMemberName }
|
||||
.ifBlank { msg.sendRemarkName }
|
||||
.ifBlank { msg.peerName }, "unknown", 0, msg.senderUid
|
||||
.ifEmpty { msg.sendMemberName }
|
||||
.ifEmpty { msg.sendRemarkName }
|
||||
.ifEmpty { msg.peerName }, "unknown", 0, msg.senderUid
|
||||
),
|
||||
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
|
||||
it.toJson()
|
||||
|
@ -29,7 +29,7 @@ internal object GetProfileCard: IActionHandler() {
|
||||
uin = card.uin.toLong(),
|
||||
name = card.strNick,
|
||||
mail = card.strShowName ?: card.strEmail ?: "",
|
||||
remark = card.strReMark.let { if (it.isNullOrBlank()) card.strAutoRemark else it },
|
||||
remark = card.strReMark.let { if (it.isNullOrEmpty()) card.strAutoRemark else it },
|
||||
findMethod = card.addSrcName,
|
||||
displayName = card.strContactName,
|
||||
maxVoteCnt = card.bAvailVoteCnt,
|
||||
|
@ -30,7 +30,7 @@ internal object GetTroopHonor: IActionHandler() {
|
||||
GroupSvc.parseHonor(member.honorList).forEach {
|
||||
val honor = nativeDecodeHonor(member.memberuin, it, member.mHonorRichFlag)
|
||||
if (honor != null) {
|
||||
honor.nick = member.troopnick.ifBlank { member.friendnick }
|
||||
honor.nick = member.troopnick.ifEmpty { member.friendnick }
|
||||
honorInfo.add(honor)
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,9 @@ internal object GetTroopMemberInfo : IActionHandler() {
|
||||
unfriendly = false,
|
||||
title = info.mUniqueTitle ?: "",
|
||||
titleExpireTime = info.mUniqueTitleExpire,
|
||||
cardChangeable = GroupSvc.isAdmin(groupId)
|
||||
cardChangeable = GroupSvc.isAdmin(groupId),
|
||||
age = info.age.toInt(),
|
||||
shutUpTimestamp = 0L
|
||||
), echo
|
||||
)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user