90 Commits
1.0.5 ... 1.0.6

Author SHA1 Message Date
5f0cf952e8 Shamrock: support /switch_account 2023-11-23 09:48:30 +08:00
03d62c55c2 Shamrock: fix wrong group request event 2023-11-23 01:24:47 +08:00
89154f0c49 Shamrock: fix wrong types of get_group_system_msg 2023-11-23 00:38:55 +08:00
5218c153f0 fix: merge conflict 2023-11-22 23:54:36 +08:00
2b64aa8b2c Shamrock feat: GroupSystemMessage 2023-11-22 23:53:26 +08:00
67ef465f14 Shamrock: fix #70 2023-11-22 23:08:23 +08:00
ea62640057 Shamrock: support /get_prohibited_member_list 2023-11-22 23:03:20 +08:00
f67dfac4d9 Shamrock: support /get_prohibited_member_list for #72 2023-11-22 22:59:05 +08:00
1593d973a0 Merge branch 'master' of github.com:whitechi73/OpenShamrock 2023-11-21 23:39:26 +08:00
3e27c64e84 Shamrock: 友人およびグループのメンバーシップ要求の処理 2023-11-21 23:39:20 +08:00
c87268df09 Create FUNDING.yml 2023-11-21 22:47:38 +08:00
98c6316e23 Shamrock: fix #61 2023-11-21 22:30:53 +08:00
2af8d6d817 Shamrock: Gwellekaat dilemel ar restr strollad 2023-11-21 22:05:34 +08:00
b9c2de744e Merge pull request #69 from XSana/master
视频类型消息,md5文件名携带后缀导致hex2bytes失败
2023-11-21 21:09:41 +08:00
fcf18bd3fd Shamrock: fix #68 2023-11-21 21:09:07 +08:00
8265f74ca3 视频类型消息,md5文件名携带后缀导致hex2bytes失败 2023-11-21 18:15:54 +08:00
2c49b10772 Shamrock: fix #66 2023-11-21 11:58:32 +08:00
4266afd1a5 Merge remote-tracking branch 'origin/master' 2023-11-21 11:55:33 +08:00
b9ef0ca8f5 Shamrock: fix #67 2023-11-21 11:55:14 +08:00
040b132836 Shamrock: fix action name 2023-11-20 19:19:45 +08:00
16f77de5a6 Shamrock: feat: 精华消息 2023-11-20 18:36:46 +08:00
46c83f5cb9 Shamrock: fix #62 2023-11-20 17:07:24 +08:00
ca71ecae09 fix: #60 2023-11-19 19:51:48 +08:00
58d93b8f56 Shamrock: fix #33 2023-11-17 20:20:33 +08:00
472b17f744 Merge pull request #55 from ikechan8370/master
fix: グループイベントが正しく通知されない問題
2023-11-17 19:32:28 +08:00
8a4212ffd7 feat: グループに参加して友達と一緒に通知を申請しましょう 2023-11-17 19:18:50 +08:00
bd28b0f7f7 fix: グループイベントが正しく通知されない問題 2023-11-17 16:02:27 +08:00
f2dc3bc9fd Shamrock: fix #45 2023-11-16 11:23:31 +08:00
ba8322dc55 Merge pull request #50 from super1207/master
允许崩溃后快速重启
2023-11-16 00:25:27 +08:00
cf445d17d9 Merge pull request #49 from ikechan8370/master
fix: グループ限定タイトルインターフェースの問題
2023-11-16 00:24:50 +08:00
e6e03ee328 support vote 2023-11-15 19:50:06 +08:00
497df6b649 允许崩溃后快速重启 2023-11-15 17:20:36 +08:00
f87fd3887a fix: エコーフィールド欠落の問題 2023-11-15 16:18:37 +08:00
71a462e48f fix: グループ限定タイトルインターフェースの問題 2023-11-15 15:30:00 +08:00
93cb6fc46b Merge pull request #43 from ikechan8370/master
fix: add response headers and fix a null pointer exception
2023-11-14 22:52:00 +08:00
8fdea083b0 fix: add more response headers 2023-11-14 16:15:51 +08:00
1620e18bf4 fix: 私聊获取文件类型消息时的空指针异常 2023-11-13 22:45:33 +08:00
2f264ee9e4 Merge branch 'master' of https://github.com/ikechan8370/OpenShamrock 2023-11-13 22:45:15 +08:00
19e3846e40 Shamrock: fix #41 2023-11-13 01:32:53 +08:00
6b4f763a90 Merge pull request #35 from callng/dev
`Shamrock`: 使拦截的收包cmd更加清晰
2023-11-12 12:22:37 +08:00
d5378d8acb Shamrock: 使拦截的收包cmd更加清晰 2023-11-12 05:49:22 +08:00
ca62b33275 Shamrock: 修复获取历史消息丢失当前ID的消息 2023-11-11 23:02:36 +08:00
e09b68576a fix: Add response headers for some actions 2023-11-11 20:36:10 +08:00
80bb591a1b Shamrock: support /shell 2023-11-11 11:48:53 +08:00
08bce05c66 Shamrock: Sofitu pa unviar ficheros con nomes personalizaos 2023-11-10 21:42:29 +08:00
0fb2eeb2d8 Shamrock: Error fixa en cargar l'interface d'archivo sin parametro headers 2023-11-10 21:28:18 +08:00
3cf2474938 Shamrock: mkaefaɛu khWe’Wmɔdɔ, owsɛeɛe ɛgaooa 2023-11-10 21:03:37 +08:00
ce1e850c78 Shamrock: 連結転送のサポート(去勢) 2023-11-10 20:25:07 +08:00
5200a4d8d4 Shamrock: fix #26 2023-11-10 18:33:04 +08:00
0a41429d71 Shamrock: 連結転送逆順の修正 2023-11-10 00:39:58 +08:00
bc967cf926 Shamrock: Support du chat de groupe et transfert de messages 2023-11-10 00:34:51 +08:00
d28c6dc820 Shamrock: 返信ファイルメッセージの修正 2023-11-07 09:43:15 +08:00
10d25167e8 Shamrock: チャンネルフラッシュバックの修正を試みる 2023-11-07 09:28:13 +08:00
346798dc9a Shamrock: 履歴メッセージの取得をサポート x2 2023-11-07 09:15:00 +08:00
7584390408 Shamrock: 履歴メッセージの取得をサポート 2023-11-07 09:09:29 +08:00
3988ad3811 Shamrock: ローカルキャッシュメッセージの削除をサポート 2023-11-06 00:18:24 +08:00
72600364ff Shamrock: 修復ファイルリストの取得に失敗しました 2023-11-05 15:58:20 +08:00
68977e4a86 Shamrock: Nothing 2023-11-05 14:19:41 +08:00
92b3d4f94e Shamrock: Reparieren der Gruppenstimme kann nicht gesendet werden 2023-11-05 12:58:45 +08:00
852eb0d87b Shamrock: HTTPコールバック繰り返し登録Listenerの修正 2023-11-04 22:56:44 +08:00
c8143bcf67 Shamrock: 雑談音声フラッシュバックの修正 2023-11-04 22:53:01 +08:00
75dc5c0294 ShamrockPublic: オーバメモリローミング検出
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-04 03:41:43 +08:00
21341caf62 ShamrockSafe: 这是一次提交测试
Signed-off-by: WhiteChi <tencent@qq.com>
2023-11-03 22:53:31 +08:00
ee9cf694a0 ShamrockPrivate: 正确GUI配置配置文件
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-03 22:14:05 +08:00
d347bd0a41 ShamrockPublic: キャッシュディレクトリへのファイルのアップロードをサポートする
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-03 17:54:40 +08:00
e5cca58198 ShamrockPublic: 異なるスキーマの音声ライブラリの修復
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-03 15:12:27 +08:00
259de3d3aa ShamrockPublic: アクティブな一時チャットを許可する
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-03 15:12:06 +08:00
c30e3db1a1 伏秋洛: 那天早上雾散了,不止早上,不止雾。
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-03 02:19:58 +08:00
fb5718dc61 ShamrockPublic: 自動メッセージイベントの撤回エラーの修正
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-03 01:24:38 +08:00
6043c21de5 ShamrockPublic: 自発メッセージ同期ロジックの変更 x2
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-03 01:17:11 +08:00
c3c14d6ead ShamrockPublic: 自発メッセージ同期ロジックの変更
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-03 01:11:42 +08:00
41675ed874 ShamrockPrivate: 尝试修复自发消息异常闪退
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-02 18:44:02 +08:00
dc2503b045 ShamrockPrivate: 修改WebSocket发信逻辑
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-02 08:22:59 +08:00
b76ef7efb3 Shamrock: Allow modification of active WebSocket listening address
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-02 08:05:16 +08:00
c67b49790f Shamrock: 修复被动WS缺失链接
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-01 23:59:47 +08:00
86b29b982c Shamrock: 添加被动WS链接日志
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-01 22:47:44 +08:00
f17b4924c9 Shamrock: 雑談ニックネームの紛失を修正する
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-01 21:32:16 +08:00
e162da7e7b Merge remote-tracking branch 'origin/master' 2023-11-01 21:24:24 +08:00
6f1ba71664 Shamrock: 自発メッセージメッセージIDエラーの修正
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-11-01 21:24:16 +08:00
16e0c9542e Shamrock: 增加拦截内部浏览器的URL检测 2023-11-01 06:50:39 +08:00
e41b7515d3 Shamrock: 臨時メッセージ
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-10-31 14:30:40 +08:00
52b8db70be Shamrock: 暗黙的コンフィギュレータの追加
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-10-31 13:47:49 +08:00
184064d199 Shamrock: 更新逆検出
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-10-31 10:26:16 +08:00
ae1684a885 Shamrock: グループメンバーリスト取得ロジックの変更
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-10-31 10:21:06 +08:00
688041b6be Shamrock: わけのわからないフラッシュバックを修復する
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-10-31 00:40:42 +08:00
4adf2eb84a Shamrock: プロファイルロジックの変更
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-10-31 00:19:51 +08:00
3f8af384b0 Shamrock: 外部リンクライブラリ初期化ロジックの変更
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-10-30 09:37:47 +08:00
79f2594a2f Shamrock: 更新反检测逻辑
Signed-off-by: WhiteChi <whitechi73@outlook.com>
2023-10-29 18:04:03 +08:00
547f224140 fix: README.md 2023-10-28 11:40:38 +08:00
94937a75a2 fix: README.md 2023-10-28 11:40:06 +08:00
134 changed files with 4308 additions and 729 deletions

13
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: shamrock320 # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

BIN
AudioLibrary-x64.zip Normal file

Binary file not shown.

View File

@ -22,6 +22,7 @@
> 本项目目的是研究 Xposed 和 LSPosed 框架的使用。 Epic 框架开发相关知识。
> 如有违反法律,请联系删除。
> 请勿在任何平台宣传,宣扬,转发本项目,请勿恶意修改企业安装包造成相关企业产生损失,如有违背,必将追责到底。
> 官方论坛,[点我直达](https://forum.libfekit.so/)
## 兼容|迁移|替代 说明
@ -69,38 +70,38 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
[![][contrib-image]][contrib-link]
[banner]: https://socialify.git.ci/whitechi73/Shamrock/image?description=1&forks=1&issues=1&logo=https%3A%2F%2Fwhitechi73.github.io%2FShamrock%2Fshamrock.jpg&pattern=Plus&pulls=1&stargazers=1&theme=Auto
[banner]: https://socialify.git.ci/whitechi73/OpenShamrock/image?description=1&forks=1&issues=1&logo=https%3A%2F%2Fwhitechi73.github.io%2FOpenShamrock%2Fshamrock.jpg&pattern=Plus&pulls=1&stargazers=1&theme=Auto
[actions]: https://img.shields.io/github/actions/workflow/status/whitechi73/Shamrock/build-apk.yml?style=for-the-badge
[actions]: https://img.shields.io/github/actions/workflow/status/whitechi73/OpenShamrock/build-apk.yml?style=for-the-badge
[actions-link]: https://github.com/whitechi73/Shamrock/actions/workflows/build-apk.yml
[actions-link]: https://github.com/whitechi73/OpenShamrock/actions/workflows/build-apk.yml
[releases]: https://img.shields.io/github/v/release/whitechi73/Shamrock?style=for-the-badge
[releases]: https://img.shields.io/github/v/release/whitechi73/OpenShamrock?style=for-the-badge
[releases-link]: https://github.com/whitechi73/Shamrock/releases
[releases-link]: https://github.com/whitechi73/OpenShamrock/releases
[downloads]: https://img.shields.io/github/downloads/whitechi73/Shamrock/total?style=for-the-badge
[downloads]: https://img.shields.io/github/downloads/whitechi73/OpenShamrock/total?style=for-the-badge
[license]: https://img.shields.io/github/license/whitechi73/Shamrock?style=for-the-badge
[license]: https://img.shields.io/github/license/whitechi73/OpenShamrock?style=for-the-badge
[onebot-11]: https://img.shields.io/badge/OneBot-11-black?style=for-the-badge
[onebot-12]: https://img.shields.io/badge/OneBot-12-black?style=for-the-badge
[download-link]: https://whitechi73.github.io/Shamrock/guide/getting-started.html#%E4%B8%8B%E8%BD%BD
[download-link]: https://whitechi73.github.io/OpenShamrock/guide/getting-started.html#%E4%B8%8B%E8%BD%BD
[deploy-link]: https://whitechi73.github.io/Shamrock/guide/getting-started.html#%E9%83%A8%E7%BD%B2
[deploy-link]: https://whitechi73.github.io/OpenShamrock/guide/getting-started.html#%E9%83%A8%E7%BD%B2
[api-link]: https://whitechi73.github.io/Shamrock/api
[api-link]: https://whitechi73.github.io/OpenShamrock/api
[docs-link]: https://whitechi73.github.io/Shamrock/
[docs-link]: https://whitechi73.github.io/OpenShamrock/
[group-link]: https://whitechi73.github.io/Shamrock/group.html
[group-link]: https://whitechi73.github.io/OpenShamrock/group.html
[hook-system]: https://github.com/whitechi73/Shamrock/wiki/perm_hook_android
[hook-system]: https://github.com/whitechi73/OpenShamrock/wiki/perm_hook_android
[voice-support]: https://whitechi73.github.io/Shamrock/advanced/voice.html
[voice-support]: https://whitechi73.github.io/OpenShamrock/advanced/voice.html
[contrib-image]: https://contrib.rocks/image?repo=whitechi73/Shamrock
[contrib-image]: https://contrib.rocks/image?repo=whitechi73/OpenShamrock
[contrib-link]: https://github.com/whitechi73/Shamrock/graphs/contributors
[contrib-link]: https://github.com/whitechi73/OpenShamrock/graphs/contributors

View File

@ -24,7 +24,7 @@ android {
minSdk = 24
targetSdk = 33
versionCode = (System.currentTimeMillis() / 1000).toInt()
versionName = "1.0.5-dev" + gitCommitHash()
versionName = "1.0.6-dev" + gitCommitHash()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
@ -66,6 +66,7 @@ android {
create("app") {
dimension = "mode"
ndk {
println("Full architecture and full compilation.")
abiFilters.add("arm64-v8a")
abiFilters.add("x86_64")
}
@ -73,12 +74,14 @@ android {
create("arm64") {
dimension = "mode"
ndk {
println("Full compilation of arm64 architecture")
abiFilters.add("arm64-v8a")
}
}
create("x64") {
dimension = "mode"
ndk {
println("Full compilation of x64 architecture")
abiFilters.add("x86_64")
}
}
@ -173,24 +176,19 @@ dependencies {
implementation("io.coil-kt:coil:2.4.0")
implementation("io.coil-kt:coil-compose:2.4.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.1.16")
implementation("io.ktor:ktor-server-core:2.3.3")
implementation("io.ktor:ktor-server-host-common:2.3.3")
implementation("io.ktor:ktor-server-status-pages:2.3.3")
implementation("io.ktor:ktor-server-netty:2.3.3")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.3")
implementation("io.ktor:ktor-server-content-negotiation:2.3.3")
implementation("io.ktor:ktor-client-core:2.3.3")
implementation("io.ktor:ktor-client-cio:2.3.3")
implementation("io.ktor:ktor-client-content-negotiation:2.3.3")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.3")
// useless
//implementation ("com.maxkeppeler.sheets-compose-dialogs:core:1.2.0")
//implementation ("com.maxkeppeler.sheets-compose-dialogs:info:1.2.0")
//implementation ("com.maxkeppeler.sheets-compose-dialogs:input:1.2.0")
//implementation ("com.maxkeppeler.sheets-compose-dialogs:list:1.2.0")
//implementation ("com.maxkeppeler.sheets-compose-dialogs:state:1.2.0")
val ktorVersion = "2.3.3"
implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("io.ktor:ktor-server-host-common:$ktorVersion")
implementation("io.ktor:ktor-server-status-pages:$ktorVersion")
implementation("io.ktor:ktor-server-netty:$ktorVersion")
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-cio:$ktorVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
//implementation("io.ktor:ktor-serialization-kotlinx-protobuf:$ktorVersion")
implementation(project(":xposed"))

View File

@ -61,8 +61,8 @@ const std::byte MD5::PADDING[64] = { (byte) 0x80 };
const char MD5::HEX[16] = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
'8', '9', 'A', 'B',
'C', 'D', 'E', 'F'
};

View File

@ -239,11 +239,21 @@ object ShamrockConfig {
return preferences.getBoolean("enable_auto_start", false)
}
fun allowShell(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("shell", false)
}
fun setAutoStart(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("enable_auto_start", v).apply()
}
fun setShellStatus(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("shell", v).apply()
}
fun enableSelfMsg(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("enable_self_msg", false)
@ -288,6 +298,7 @@ object ShamrockConfig {
"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),
)
}

View File

@ -257,10 +257,10 @@ private fun APIInfoCard(
hint = "请输入被动地址",
error = "输入的地址不合法",
checker = {
it.isNotBlank()
true
},
confirm = {
if (it.startsWith("ws://") || it.startsWith("wss://")) {
if (it.startsWith("ws://") || it.startsWith("wss://") || it.isBlank()) {
ShamrockConfig.setWsAddr(ctx, wsAddress.value)
AppRuntime.log("设置被动WebSocket地址为[${wsAddress.value}]。")
} else {

View File

@ -100,7 +100,7 @@ fun LabFragment() {
Function(
title = "自动清理QQ垃圾",
desc = "也许会导致奇怪的问题。",
desc = "也许会导致奇怪的问题(无效)",
descColor = it,
isSwitch = ShamrockConfig.isAutoClean(ctx)
) {
@ -110,8 +110,8 @@ fun LabFragment() {
}
Function(
title = "拦截QQ无用",
desc = "测试阶段,可能导致网络异常。",
title = "拦截QQ无用",
desc = "测试阶段,可能导致网络异常或掉线",
descColor = it,
isSwitch = ShamrockConfig.isInjectPacket(ctx)
) {
@ -129,6 +129,16 @@ fun LabFragment() {
ShamrockConfig.setAutoStart(ctx, it)
return@Function true
}
Function(
title = "开启Shell接口",
desc = "可能导致设备被入侵,请勿随意开启。",
descColor = it,
isSwitch = ShamrockConfig.allowShell(ctx)
) {
ShamrockConfig.setShellStatus(ctx, it)
return@Function true
}
}
}
@ -180,6 +190,7 @@ fun LabFragment() {
return@Function true
}
/*
Function(
title = "使用纯数字ECHO",
desc = "在部分强类型语言框架,需要打开此开关。",
@ -189,7 +200,7 @@ fun LabFragment() {
ShamrockConfig.setEchoNumber(ctx, it)
ShamrockConfig.pushUpdate(ctx)
return@Function true
}
}*/
}
}
}

View File

@ -10,7 +10,6 @@ android {
defaultConfig {
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
@ -36,7 +35,4 @@ dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.9.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

View File

@ -1,6 +1,50 @@
package com.tencent.mobileqq.pb;
import java.lang.reflect.Field;
import java.util.Arrays;
public class MessageMicro<T extends MessageMicro<T>> {
public static final class FieldMap {
private Object[] defaultValues;
private Field[] fields;
private int[] tags;
FieldMap(int[] iArr, String[] strArr, Object[] objArr, Class<?> cls) {
this.tags = iArr;
this.defaultValues = objArr;
this.fields = new Field[iArr.length];
for (int i2 = 0; i2 < iArr.length; i2++) {
try {
this.fields[i2] = cls.getField(strArr[i2]);
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
void clear(MessageMicro<?> messageMicro) {
}
<U extends MessageMicro<U>> void copyFields(U u, U u2) {
}
Field get(int i2) {
int binarySearch = Arrays.binarySearch(this.tags, i2);
if (binarySearch < 0) {
return null;
}
return this.fields[binarySearch];
}
int getSerializedSize(MessageMicro<?> messageMicro) {
return 0;
}
}
public static FieldMap initFieldMap(int[] iArr, String[] strArr, Object[] objArr, Class<?> cls) {
return new FieldMap(iArr, strArr, objArr, cls);
}
public final T mergeFrom(byte[] bArr) {
return null;
}

View File

@ -0,0 +1,186 @@
package com.tencent.mobileqq.relation.api;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import com.tencent.common.app.AppInterface;
import com.tencent.mobileqq.data.MessageRecord;
import com.tencent.mobileqq.data.PhoneContact;
import com.tencent.mobileqq.qroute.QRouteApi;
import java.util.ArrayList;
import java.util.HashMap;
import tencent.mobileim.structmsg.*;
public interface IAddFriendTempApi extends QRouteApi {
public static final int ENTER_FROM_CONTACT_TAB = 1;
public static final int ENTER_FROM_DEFAULT = 0;
public static final int ENTER_FROM_MESSAGE_TAB = 2;
public static final int ENTER_FROM_NEW_FRIEND = 3;
public static final int ENTER_FROM_NEW_FRIEND_MORE = 4;
// void addBatchQIMFriends(List<QIMNotifyAddFriend> paramList, AppInterface paramAppInterface);
void addFriendToFriendList(AppInterface paramAppInterface, String paramString1, int paramInt1, int paramInt2, String paramString2, boolean paramBoolean1, boolean paramBoolean2, long paramLong);
void addListener(Object paramObject, AppInterface paramAppInterface);
void cancelMayKnowRecommend(String paramString, AppInterface paramAppInterface);
// void changeStructMsgActions(structmsg.StructMsg paramStructMsg, int paramInt1, String paramString, int paramInt2);
// boolean changeStructMsgActionsWhenFail(structmsg.StructMsg paramStructMsg, int paramInt, String paramString1, String paramString2);
void checkReadContactPermission(Runnable paramRunnable, AppInterface paramAppInterface);
void checkUpdate(AppInterface paramAppInterface, String paramString);
void clearAllSystemMsg(AppInterface paramAppInterface);
void clickQIMSource(Context paramContext, MessageRecord paramMessageRecord, AppInterface paramAppInterface);
Intent composeReturnIntent(Class<?> paramClass, String paramString, Activity paramActivity);
void deleteAllSuspiciousMsg(AppInterface paramAppInterface);
// void deleteQIMNotifyAddFriendData(QIMNotifyAddFriend paramQIMNotifyAddFriend, AppInterface paramAppInterface);
boolean enableCheckPermission();
void followUser(String paramString, boolean paramBoolean);
Intent getAddRequestIntent(Context paramContext);
Intent getBindNumberIntent(Context paramContext);
Intent getChatActivityIntent(Context paramContext);
// ArrayList<MayKnowRecommend> getConnectionsPersonLocal(int paramInt, AppInterface paramAppInterface);
void getConnectionsPersonRemoteNextPage(int paramInt, AppInterface paramAppInterface);
// ArrayList<a> getConnectionsTabInfoListLocal(AppInterface paramAppInterface);
void getDiscussInfo(long paramLong, AppInterface paramAppInterface);
String getDiscussionNameCanNull(AppInterface paramAppInterface, String paramString);
int getForwardSelectionRequest();
Intent getFriendProfileMoreInfoIntent(Context paramContext);
int getMayKnowLoadConnectionBizTypeFirstLoad();
int getMayKnowPersonNum(AppInterface paramAppInterface);
boolean getMayKnowRecommendRemoteFromNewFrd(AppInterface paramAppInterface);
String getQIMNewFriendSource(AppInterface paramAppInterface);
// ArrayList<s> getQIMNotifyAddFriendsMsg(boolean paramBoolean, AppInterface paramAppInterface);
String getQQInfoFromQQUin(long paramLong1, long paramLong2, AppInterface paramAppInterface);
HashMap<String, String> getQidianExternal(HashMap<String, Object> paramHashMap);
// String getRecommendLabelString(List<MayKnowRecommend.MayKnowRecommendLabel> paramList);
int getRequestForSetting();
int getSizeSmall();
void getSuspiciousFriendsUnreadNum(AppInterface paramAppInterface);
int getTypeSetConnectionsSwitch();
Object getValue(String paramString1, String paramString2, int paramInt1, int paramInt2);
void gotoFriendSettingBrowser(Context paramContext);
boolean hasQidianExternal(HashMap<String, Object> paramHashMap);
boolean hasQimSource(AppInterface paramAppInterface);
void insertCommonHobbyIfNeeded(AppInterface paramAppInterface, String paramString);
boolean isMayKnowConnectionsUserClosed(AppInterface paramAppInterface);
boolean isNewFrdMiniCardSwitchOn(AppInterface paramAppInterface);
boolean isPhoneContactEnabled(AppInterface paramAppInterface);
boolean isQidianMaster(AppInterface paramAppInterface, String paramString);
boolean isStudyMode(AppInterface paramAppInterface);
boolean isSuspiciousSwitchOpen();
// void jumpToMoveGroup(Activity paramActivity, QBaseFragment paramQBaseFragment, String paramString, int paramInt1, int paramInt2);
void jumpToNewFriendMoreSysMsgSuspiciousFragment(Context paramContext);
void jumpToNewFriendMoreSysMsgSuspiciousFragment(Context paramContext, Intent paramIntent, int paramInt);
void jumpToQidianProfile(String paramString, Activity paramActivity);
void jumpToSplash(Activity paramActivity);
void launchPluginBroadcastWhenToggleSwitch(String paramString, AppInterface paramAppInterface, boolean paramBoolean);
void loadConnectionsTabData(AppInterface paramAppInterface, int paramInt1, int paramInt2);
void markQIMNotifyAddFriendsRead(AppInterface paramAppInterface);
// void openSecCheckWebForFragment(AppInterface paramAppInterface, Context paramContext, QBaseFragment paramQBaseFragment, int paramInt, String paramString1, String paramString2);
// void recordStartExpose(MayKnowRecommend paramMayKnowRecommend, int paramInt1, int paramInt2, int paramInt3, AppInterface paramAppInterface);
// void recordStopExpose(MayKnowRecommend paramMayKnowRecommend, int paramInt1, int paramInt2, int paramInt3, AppInterface paramAppInterface);
void removeListener(Object paramObject, AppInterface paramAppInterface);
void reportExtendFriend(int paramInt, String paramString, Intent paramIntent);
void reportRecommend(AppInterface paramAppInterface, String paramString1, String paramString2, int paramInt1, int paramInt2, String paramString3, int paramInt3, byte[] paramArrayOfbyte, String paramString4, int paramInt4);
void reportRecommendExpose(AppInterface paramAppInterface, int paramInt1, ArrayList<String> paramArrayList1, ArrayList<String> paramArrayList2, ArrayList<Integer> paramArrayList, ArrayList<byte[]> paramArrayList3, int paramInt2);
void sendAddFriendNoticeForBaby(AppInterface paramAppInterface, Intent paramIntent);
// void sendDelSingleSystemMsg(structmsg.StructMsg paramStructMsg, String paramString, int paramInt, long paramLong, AppInterface paramAppInterface);
void sendFriendSystemMsgAction(int msg_type, long msg_seq, long req_uin, int sub_type, int src_id, int sub_src_id, int group_msg_type, structmsg$SystemMsgActionInfo action_info, int system_msg_action_type, structmsg$StructMsg paramStructMsg, boolean isUncommonlyUsedFrd, AppInterface paramAppInterface);
void sendFriendSystemMsgReadedReport(AppInterface paramAppInterface);
void sendGetNextFriendSystemMsg(AppInterface paramAppInterface);
void sendPokeMsg(AppInterface paramAppInterface, Context paramContext, String paramString);
void setConnectionsSwitch(boolean paramBoolean, AppInterface paramAppInterface);
boolean shouldShowMayKnowInNewFriend(AppInterface paramAppInterface);
// void startAddContactsPage(Context paramContext, int paramInt1, int paramInt2, LaunchMode paramLaunchMode, @Nullable Bundle paramBundle);
void startAddContactsPageForResult(Activity paramActivity, int paramInt1, int paramInt2, int paramInt3);
void startAddRequestPage(Context paramContext, Intent paramIntent);
void startAddRequestPageForResult(Activity paramActivity, Intent paramIntent, int paramInt);
void startAddRequestSuspiciousPage(Context paramContext, Intent paramIntent);
void startContactBindFromOther(AppInterface paramAppInterface, int paramInt1, int paramInt2, Intent paramIntent);
void startContactBindFromOther(AppInterface paramAppInterface, int paramInt, ArrayList<PhoneContact> paramArrayList);
void startRemarkAfterAgree(Activity paramActivity, int paramInt, String paramString, long paramLong, Bundle paramBundle);
void updateCustomNoteTxt(TextView paramTextView, int paramInt1, int paramInt2);
}

View File

@ -8,6 +8,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
import mqq.app.AppRuntime;
public class BaseTransFileController implements ITransFileController {
@Override
public boolean containsProcessor(String name, long uin) {
return false;
}
@Override
public IHttpCommunicatorListener findProcessor(String str) {
return null;

View File

@ -14,7 +14,7 @@ public interface ITransFileController extends IRuntimeService {
//@Deprecated
//void addProcessor(String str, IHttpCommunicatorListener iHttpCommunicatorListener);
//boolean containsProcessor(String str, long j2);
boolean containsProcessor(String name, long uin);
IHttpCommunicatorListener findProcessor(String str);

View File

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

View File

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

View File

@ -44,8 +44,6 @@ public final class Contact implements IKernelModel, Serializable {
public Contact(int i2, String str, String str2) {
this.serialVersionUID = 1L;
this.peerUid = "";
this.guildId = "";
this.chatType = i2;
this.peerUid = str;
this.guildId = str2;

View File

@ -1,7 +1,5 @@
package com.tencent.qqnt.kernel.nativeinterface;
/* compiled from: P */
/* loaded from: classes2.dex */
public final class GroupFileCommonResult {
String clientWording;
int retCode;
@ -29,8 +27,6 @@ public final class GroupFileCommonResult {
}
public GroupFileCommonResult(int i2, String str, String str2) {
this.retMsg = "";
this.clientWording = "";
this.retCode = i2;
this.retMsg = str;
this.clientWording = str2;

View File

@ -0,0 +1,5 @@
package com.tencent.qqnt.kernel.nativeinterface;
public interface IClearMsgRecordsCallback {
void onResult(int code, String reason, long lastMsgId);
}

View File

@ -1,5 +1,5 @@
package com.tencent.qqnt.kernel.nativeinterface;
public interface IDeleteGroupFileCallback {
void onResult(int i2, String str, DeleteGroupFileResult deleteGroupFileResult);
void onResult(int code, String why, DeleteGroupFileResult result);
}

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
@ -16,7 +18,25 @@ public interface IKernelMsgService {
void addLocalRecordMsg(Contact contact, long msgId, MsgElement elem, HashMap<Integer, MsgAttributeInfo> hashMap, boolean z, IOperateCallback callback);
void getMultiMsg(Contact contact, long msgId, long uniseq, IGetMultiMsgCallback cb);
long getMsgUniqueId(long time);
void addSendMsg(long msgId, Contact contact, ArrayList<MsgElement> msgList, HashMap<Integer, MsgAttributeInfo> hashMap);
void getMsgs(@NotNull Contact contact, long startMsgId, int cnt, boolean queryOrder, @NotNull IMsgOperateCallback iMsgOperateCallback);
void getMsgsIncludeSelf(Contact contact, long startMsgId, int count, boolean queryOrder, IMsgOperateCallback iMsgOperateCallback);
void translatePtt2Text(long j2, Contact contact, MsgElement msgElement, IOperateCallback iOperateCallback);
void getMultiMsg(Contact contact, long rootMsgId, long parentMsgId, IGetMultiMsgCallback cb);
void multiForwardMsg(ArrayList<MultiMsgInfo> arrayList, Contact from, Contact to, IOperateCallback cb);
void setAllC2CAndGroupMsgRead(IOperateCallback cb);
void clearMsgRecords(Contact contact, IClearMsgRecordsCallback cb);
String createUidFromTinyId(long j2, long j3);
void switchBackGround(BackGroundInfo backGroundInfo, IOperateCallback cb);

View File

@ -9,7 +9,7 @@ public interface IKernelRichMediaService {
void cancelTransferTask(Contact contact, ArrayList<Long> arrayList, ArrayList<Integer> arrayList2, IOperateTransferInfoCallback iOperateTransferInfoCallback);
void deleteGroupFile(long j2, String str, int i2, IDeleteGroupFileCallback iDeleteGroupFileCallback);
void deleteGroupFile(long groupCode, String fileUid, int bizId, IDeleteGroupFileCallback cb);
void deleteTransferInfo(Contact contact, ArrayList<Long> arrayList, IOperateTransferInfoCallback iOperateTransferInfoCallback);

View File

@ -1,7 +1,5 @@
package com.tencent.qqnt.kernel.nativeinterface;
/* compiled from: P */
/* loaded from: classes2.dex */
public final class MultiMsgInfo {
long msgId;
String senderShowName;
@ -21,8 +19,6 @@ public final class MultiMsgInfo {
return "MultiMsgInfo{msgId=" + this.msgId + ",senderShowName=" + this.senderShowName + ",}";
}
public MultiMsgInfo(long j2, String str) {
this.msgId = j2;
this.senderShowName = str;
public MultiMsgInfo(long msgId, String showName) {
}
}

View File

@ -1,7 +1,5 @@
package com.tencent.qqnt.kernel.nativeinterface;
/* compiled from: P */
/* loaded from: classes.dex */
public final class TempChatInfo {
int chatType;
String fromNick;

View File

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

View File

@ -5,6 +5,7 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.tencent.qphone.base.remote.SimpleAccount;
import com.tencent.qphone.base.remote.ToServiceMsg;
import mqq.app.api.IRuntimeService;
@ -72,6 +73,10 @@ public abstract class AppRuntime {
public <T extends IRuntimeService> T getRuntimeServiceIPCSync(@NonNull Class<T> cls, String str) {
throw new UnsupportedOperationException();
}
public void switchAccount(SimpleAccount simpleAccount, String process) {
}
public String getAccount() {
return "";
}
@ -81,6 +86,10 @@ public abstract class AppRuntime {
return !"0".equals(getCurrentAccountUin()) ? getCurrentAccountUin() : "";
}
public String getCurrentUid() {
return "";
}
public long getLongAccountUin() {
return 0;
}

View File

@ -0,0 +1,84 @@
package tencent.im.oidb.cmd0x899;
import com.tencent.mobileqq.pb.ByteStringMicro;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBBytesField;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBRepeatField;
import com.tencent.mobileqq.pb.PBRepeatMessageField;
import com.tencent.mobileqq.pb.PBUInt32Field;
import com.tencent.mobileqq.pb.PBUInt64Field;
public class oidb_0x899 {
public static class ReqBody extends MessageMicro<ReqBody> {
//static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[]{8, 16, 24, 32, 42, 48, 56, 64}, new String[]{
// "uint64_group_code",
// "uint64_start_uin",
// "uint32_identify_flag",
// "rpt_uint64_uin_list",
// "memberlist_opt",
// "uint32_member_num", "uint32_filter_method", "uint32_online_flag"}, new Object[]{0L, 0L, 0, 0L, null, 0, 0, 0}, ReqBody.class);
public final PBUInt64Field uint64_group_code = PBField.initUInt64(0);
public final PBUInt64Field uint64_start_uin = PBField.initUInt64(0);
public final PBUInt32Field uint32_identify_flag = PBField.initUInt32(0);
public final PBRepeatField<Long> rpt_uint64_uin_list = PBField.initRepeat(PBUInt64Field.__repeatHelper__);
public memberlist memberlist_opt = new memberlist();
public final PBUInt32Field uint32_member_num = PBField.initUInt32(0);
public final PBUInt32Field uint32_filter_method = PBField.initUInt32(0);
public final PBUInt32Field uint32_online_flag = PBField.initUInt32(0);
}
public static class RspBody extends MessageMicro<RspBody> {
static final MessageMicro.FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[]{8, 16, 24, 34, 42}, new String[]{"uint64_group_code", "uint64_start_uin", "uint32_identify_flag", "rpt_memberlist", "str_errorinfo"}, new Object[]{0L, 0L, 0, null, ByteStringMicro.EMPTY}, RspBody.class);
public final PBUInt64Field uint64_group_code = PBField.initUInt64(0);
public final PBUInt64Field uint64_start_uin = PBField.initUInt64(0);
public final PBUInt32Field uint32_identify_flag = PBField.initUInt32(0);
public final PBRepeatMessageField<memberlist> rpt_memberlist = PBField.initRepeatMessage(memberlist.class);
public final PBBytesField str_errorinfo = PBField.initBytes(ByteStringMicro.EMPTY);
}
public static class memberlist extends MessageMicro<memberlist> {
static final MessageMicro.FieldMap __fieldMap__;
public final PBBytesField bytes_rich_info;
public final PBBytesField bytes_special_title;
public final PBBytesField bytes_uin_key;
public final PBUInt32Field uint32_active_day;
public final PBUInt32Field uint32_privilege;
public final PBUInt32Field uint32_special_title_expire_time;
public final PBUInt64Field uint64_member_uin = PBField.initUInt64(0);
public final PBUInt32Field uint32_uin_flag = PBField.initUInt32(0);
public final PBUInt32Field uint32_uin_flagex = PBField.initUInt32(0);
public final PBUInt32Field uint32_uin_mobile_flag = PBField.initUInt32(0);
public final PBUInt32Field uint32_uin_arch_flag = PBField.initUInt32(0);
public final PBUInt32Field uint32_join_time = PBField.initUInt32(0);
public final PBUInt32Field uint32_old_msg_seq = PBField.initUInt32(0);
public final PBUInt32Field uint32_new_msg_seq = PBField.initUInt32(0);
public final PBUInt32Field uint32_last_speak_time = PBField.initUInt32(0);
public final PBUInt32Field uint32_level = PBField.initUInt32(0);
public final PBUInt32Field uint32_point = PBField.initUInt32(0);
public final PBUInt32Field uint32_shutup_timestap = PBField.initUInt32(0);
public final PBUInt32Field uint32_flagex2 = PBField.initUInt32(0);
static {
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
__fieldMap__ = MessageMicro.initFieldMap(new int[]{8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 114, 120, 128, 138, 144, 154}, new String[]{
"uint64_member_uin", // 1
"uint32_uin_flag", "uint32_uin_flagex", "uint32_uin_mobile_flag", "uint32_uin_arch_flag", "uint32_join_time", "uint32_old_msg_seq", "uint32_new_msg_seq", "uint32_last_speak_time", "uint32_level", "uint32_point",
"uint32_shutup_timestap", //12
"uint32_flagex2", "bytes_special_title", "uint32_special_title_expire_time", "uint32_active_day", "bytes_uin_key", "uint32_privilege", "bytes_rich_info"}, new Object[]{0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byteStringMicro, 0, 0, byteStringMicro, 0, byteStringMicro}, memberlist.class);
}
public memberlist() {
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
this.bytes_special_title = PBField.initBytes(byteStringMicro);
this.uint32_special_title_expire_time = PBField.initUInt32(0);
this.uint32_active_day = PBField.initUInt32(0);
this.bytes_uin_key = PBField.initBytes(byteStringMicro);
this.uint32_privilege = PBField.initUInt32(0);
this.bytes_rich_info = PBField.initBytes(byteStringMicro);
}
}
}

View File

@ -0,0 +1,19 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBUInt32Field;
public final class structmsg$AddFrdSNInfo extends MessageMicro<structmsg$AddFrdSNInfo> {
static final FieldMap __fieldMap__;
public final PBUInt32Field uint32_not_see_dynamic = PBField.initUInt32(0);
public final PBUInt32Field uint32_set_sn = PBField.initUInt32(0);
static {
Integer integer = Integer.valueOf(0);
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16 }, new String[] { "uint32_not_see_dynamic", "uint32_set_sn" }, new Object[] { integer, integer }, structmsg$AddFrdSNInfo.class);
}
}

View File

@ -0,0 +1,54 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBUInt32Field;
public final class structmsg$FlagInfo extends MessageMicro<structmsg$FlagInfo> {
static final FieldMap __fieldMap__;
public final PBUInt32Field FrdMsg_Discuss2ManyChat = PBField.initUInt32(0);
public final PBUInt32Field FrdMsg_GetBusiCard = PBField.initUInt32(0);
public final PBUInt32Field FrdMsg_NeedWaitingMsg = PBField.initUInt32(0);
public final PBUInt32Field FrdMsg_uint32_need_all_unread_msg = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_GetC2cInviteJoinGroup = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_GetDisbandedByAdmin = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_GetOfficialAccount = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_GetPayInGroup = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_HiddenGrp = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_Kick_Admin = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_NeedAutoAdminWording = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_NotAllowJoinGrp_InviteNotFrd = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_WordingDown = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_get_quit_pay_group_msg_flag = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_get_transfer_group_msg_flag = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_mask_invite_auto_join = PBField.initUInt32(0);
public final PBUInt32Field GrpMsg_support_invite_auto_join = PBField.initUInt32(0);
static {
Integer integer = Integer.valueOf(0);
__fieldMap__ = MessageMicro.initFieldMap(new int[] {
8, 16, 24, 32, 40, 48, 56, 64, 72, 80,
88, 96, 104, 112, 120, 128, 136 }, new String[] {
"GrpMsg_Kick_Admin", "GrpMsg_HiddenGrp", "GrpMsg_WordingDown", "FrdMsg_GetBusiCard", "GrpMsg_GetOfficialAccount", "GrpMsg_GetPayInGroup", "FrdMsg_Discuss2ManyChat", "GrpMsg_NotAllowJoinGrp_InviteNotFrd", "FrdMsg_NeedWaitingMsg", "FrdMsg_uint32_need_all_unread_msg",
"GrpMsg_NeedAutoAdminWording", "GrpMsg_get_transfer_group_msg_flag", "GrpMsg_get_quit_pay_group_msg_flag", "GrpMsg_support_invite_auto_join", "GrpMsg_mask_invite_auto_join", "GrpMsg_GetDisbandedByAdmin", "GrpMsg_GetC2cInviteJoinGroup" }, new Object[] {
integer, integer, integer, integer, integer, integer, integer, integer, integer, integer,
integer, integer, integer, integer, integer, integer, integer }, structmsg$FlagInfo.class);
}
}

View File

@ -0,0 +1,13 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBStringField;
public final class structmsg$FriendInfo extends MessageMicro<structmsg$FriendInfo> {
static final FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18 }, new String[] { "msg_joint_friend", "msg_blacklist" }, new Object[] { "", "" }, structmsg$FriendInfo.class);
public final PBStringField msg_blacklist = PBField.initString("");
public final PBStringField msg_joint_friend = PBField.initString("");
}

View File

@ -0,0 +1,27 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBStringField;
import com.tencent.mobileqq.pb.PBUInt32Field;
public final class structmsg$GroupInfo extends MessageMicro<structmsg$GroupInfo> {
static final FieldMap __fieldMap__;
public final PBUInt32Field display_action = PBField.initUInt32(0);
public final PBUInt32Field group_auth_type = PBField.initUInt32(0);
public final PBStringField msg_alert = PBField.initString("");
public final PBStringField msg_detail_alert = PBField.initString("");
public final PBStringField msg_other_admin_done = PBField.initString("");
public final PBUInt32Field uint32_app_privilege_flag = PBField.initUInt32(0);
static {
Integer integer = Integer.valueOf(0);
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 26, 34, 42, 48 }, new String[] { "group_auth_type", "display_action", "msg_alert", "msg_detail_alert", "msg_other_admin_done", "uint32_app_privilege_flag" }, new Object[] { integer, integer, "", "", "", integer }, structmsg$GroupInfo.class);
}
}

View File

@ -0,0 +1,21 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBUInt32Field;
import com.tencent.mobileqq.pb.PBUInt64Field;
public final class structmsg$MsgInviteExt extends MessageMicro<structmsg$MsgInviteExt> {
static final FieldMap __fieldMap__;
public final PBUInt32Field uint32_src_type = PBField.initUInt32(0);
public final PBUInt32Field uint32_wait_state = PBField.initUInt32(0);
public final PBUInt64Field uint64_src_code = PBField.initUInt64(0L);
static {
Integer integer = Integer.valueOf(0);
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24 }, new String[] { "uint32_src_type", "uint64_src_code", "uint32_wait_state" }, new Object[] { integer, Long.valueOf(0L), integer }, structmsg$MsgInviteExt.class);
}
}

View File

@ -0,0 +1,18 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBUInt64Field;
public final class structmsg$MsgPayGroupExt extends MessageMicro<structmsg$MsgPayGroupExt> {
static final FieldMap __fieldMap__;
public final PBUInt64Field uint64_join_grp_time = PBField.initUInt64(0L);
public final PBUInt64Field uint64_quit_grp_time = PBField.initUInt64(0L);
static {
Long long_ = Long.valueOf(0L);
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16 }, new String[] { "uint64_join_grp_time", "uint64_quit_grp_time" }, new Object[] { long_, long_ }, structmsg$MsgPayGroupExt.class);
}
}

View File

@ -0,0 +1,37 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBEnumField;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBUInt32Field;
import com.tencent.mobileqq.pb.PBUInt64Field;
public final class structmsg$ReqNextSystemMsg extends MessageMicro<structmsg$ReqNextSystemMsg> {
static final FieldMap __fieldMap__;
public final PBEnumField checktype = PBField.initEnum(1);
public structmsg$FlagInfo flag = new structmsg$FlagInfo();
public final PBUInt64Field following_friend_seq = PBField.initUInt64(0L);
public final PBUInt64Field following_group_seq = PBField.initUInt64(0L);
public final PBUInt64Field friend_msg_type_flag = PBField.initUInt64(0L);
public final PBUInt32Field language = PBField.initUInt32(0);
public final PBUInt32Field msg_num = PBField.initUInt32(0);
public final PBUInt32Field uint32_need_uid = PBField.initUInt32(0);
public final PBUInt32Field uint32_req_msg_type = PBField.initUInt32(0);
public final PBUInt32Field version = PBField.initUInt32(0);
static {
Integer integer = Integer.valueOf(0);
Long long_ = Long.valueOf(0L);
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24, 32, 42, 48, 56, 64, 72, 128 }, new String[] { "msg_num", "following_friend_seq", "following_group_seq", "checktype", "flag", "language", "version", "friend_msg_type_flag", "uint32_req_msg_type", "uint32_need_uid" }, new Object[] { integer, long_, long_, Integer.valueOf(1), null, integer, integer, long_, integer, integer }, structmsg$ReqNextSystemMsg.class);
}
}

View File

@ -0,0 +1,35 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBEnumField;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBUInt32Field;
import com.tencent.mobileqq.pb.PBUInt64Field;
public final class structmsg$ReqSystemMsgAction extends MessageMicro<structmsg$ReqSystemMsgAction> {
static final FieldMap __fieldMap__;
public structmsg$SystemMsgActionInfo action_info = new structmsg$SystemMsgActionInfo();
public final PBUInt32Field group_msg_type = PBField.initUInt32(0);
public final PBUInt32Field language = PBField.initUInt32(0);
public final PBUInt64Field msg_seq = PBField.initUInt64(0L);
public final PBEnumField msg_type = PBField.initEnum(1);
public final PBUInt64Field req_uin = PBField.initUInt64(0L);
public final PBUInt32Field src_id = PBField.initUInt32(0);
public final PBUInt32Field sub_src_id = PBField.initUInt32(0);
public final PBUInt32Field sub_type = PBField.initUInt32(0);
static {
Integer integer = Integer.valueOf(0);
Long long_ = Long.valueOf(0L);
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24, 32, 40, 48, 56, 66, 72 }, new String[] { "msg_type", "msg_seq", "req_uin", "sub_type", "src_id", "sub_src_id", "group_msg_type", "action_info", "language" }, new Object[] { Integer.valueOf(1), long_, long_, integer, integer, integer, integer, null, integer }, structmsg$ReqSystemMsgAction.class);
}
}

View File

@ -0,0 +1,49 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBBoolField;
import com.tencent.mobileqq.pb.PBEnumField;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBUInt32Field;
import com.tencent.mobileqq.pb.PBUInt64Field;
public final class structmsg$ReqSystemMsgNew extends MessageMicro<structmsg$ReqSystemMsgNew> {
static final FieldMap __fieldMap__;
public final PBEnumField checktype = PBField.initEnum(1);
public structmsg$FlagInfo flag = new structmsg$FlagInfo();
public final PBUInt64Field friend_msg_type_flag = PBField.initUInt64(0L);
public final PBBoolField is_get_frd_ribbon = PBField.initBool(true);
public final PBBoolField is_get_grp_ribbon = PBField.initBool(true);
public final PBUInt32Field language = PBField.initUInt32(0);
public final PBUInt64Field latest_friend_seq = PBField.initUInt64(0L);
public final PBUInt64Field latest_group_seq = PBField.initUInt64(0L);
public final PBUInt32Field msg_num = PBField.initUInt32(0);
public final PBUInt32Field uint32_need_uid = PBField.initUInt32(0);
public final PBUInt32Field uint32_req_msg_type = PBField.initUInt32(0);
public final PBUInt32Field version = PBField.initUInt32(0);
static {
Integer integer = Integer.valueOf(0);
Long long_ = Long.valueOf(0L);
Boolean bool = Boolean.TRUE;
__fieldMap__ = MessageMicro.initFieldMap(new int[] {
8, 16, 24, 32, 40, 50, 56, 64, 72, 80,
88, 128 }, new String[] {
"msg_num", "latest_friend_seq", "latest_group_seq", "version", "checktype", "flag", "language", "is_get_frd_ribbon", "is_get_grp_ribbon", "friend_msg_type_flag",
"uint32_req_msg_type", "uint32_need_uid" }, new Object[] {
integer, long_, long_, integer, Integer.valueOf(1), null, integer, bool, bool, long_,
integer, integer }, structmsg$ReqSystemMsgNew.class);
}
}

View File

@ -0,0 +1,27 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBEnumField;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBUInt32Field;
import com.tencent.mobileqq.pb.PBUInt64Field;
public final class structmsg$ReqSystemMsgRead extends MessageMicro<structmsg$ReqSystemMsgRead> {
static final FieldMap __fieldMap__;
public final PBEnumField checktype = PBField.initEnum(1);
public final PBUInt64Field latest_friend_seq = PBField.initUInt64(0L);
public final PBUInt64Field latest_group_seq = PBField.initUInt64(0L);
public final PBUInt32Field type = PBField.initUInt32(0);
public final PBUInt32Field uint32_req_msg_type = PBField.initUInt32(0);
static {
Long long_ = Long.valueOf(0L);
Integer integer = Integer.valueOf(0);
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24, 32, 40 }, new String[] { "latest_friend_seq", "latest_group_seq", "type", "checktype", "uint32_req_msg_type" }, new Object[] { long_, long_, integer, Integer.valueOf(1), integer }, structmsg$ReqSystemMsgRead.class);
}
}

View File

@ -0,0 +1,15 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBInt32Field;
import com.tencent.mobileqq.pb.PBStringField;
public final class structmsg$RspHead extends MessageMicro<structmsg$RspHead> {
static final FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 18 }, new String[] { "result", "msg_fail" }, new Object[] { Integer.valueOf(0), "" }, structmsg$RspHead.class);
public final PBStringField msg_fail = PBField.initString("");
public final PBInt32Field result = PBField.initInt32(0);
}

View File

@ -0,0 +1,37 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.ByteStringMicro;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBBytesField;
import com.tencent.mobileqq.pb.PBEnumField;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBRepeatMessageField;
import com.tencent.mobileqq.pb.PBStringField;
import com.tencent.mobileqq.pb.PBUInt32Field;
import com.tencent.mobileqq.pb.PBUInt64Field;
public final class structmsg$RspNextSystemMsg extends MessageMicro<structmsg$RspNextSystemMsg> {
static final FieldMap __fieldMap__;
public final PBStringField bytes_game_nick = PBField.initString("");
public final PBBytesField bytes_undecid_for_qim = PBField.initBytes(ByteStringMicro.EMPTY);
public final PBEnumField checktype = PBField.initEnum(1);
public final PBUInt64Field following_friend_seq = PBField.initUInt64(0L);
public final PBUInt64Field following_group_seq = PBField.initUInt64(0L);
public structmsg$RspHead head = new structmsg$RspHead();
public final PBRepeatMessageField<structmsg$StructMsg> msgs = PBField.initRepeatMessage(structmsg$StructMsg.class);
public final PBUInt32Field uint32_un_read_count3 = PBField.initUInt32(0);
static {
Long long_ = Long.valueOf(0L);
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 24, 32, 40, 802, 810, 816 }, new String[] { "head", "msgs", "following_friend_seq", "following_group_seq", "checktype", "bytes_game_nick", "bytes_undecid_for_qim", "uint32_un_read_count3" }, new Object[] { null, null, long_, long_, Integer.valueOf(1), "", byteStringMicro, Integer.valueOf(0) }, structmsg$RspNextSystemMsg.class);
}
}

View File

@ -0,0 +1,25 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBStringField;
import com.tencent.mobileqq.pb.PBUInt32Field;
public final class structmsg$RspSystemMsgAction extends MessageMicro<structmsg$RspSystemMsgAction> {
static final FieldMap __fieldMap__;
public structmsg$RspHead head = new structmsg$RspHead();
public final PBStringField msg_detail = PBField.initString("");
public final PBStringField msg_invalid_decided = PBField.initString("");
public final PBUInt32Field remark_result = PBField.initUInt32(0);
public final PBUInt32Field type = PBField.initUInt32(0);
static {
Integer integer = Integer.valueOf(0);
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 24, 42, 48 }, new String[] { "head", "msg_detail", "type", "msg_invalid_decided", "remark_result" }, new Object[] { null, "", integer, "", integer }, structmsg$RspSystemMsgAction.class);
}
}

View File

@ -0,0 +1,66 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.ByteStringMicro;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBBytesField;
import com.tencent.mobileqq.pb.PBEnumField;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBRepeatMessageField;
import com.tencent.mobileqq.pb.PBStringField;
import com.tencent.mobileqq.pb.PBUInt32Field;
import com.tencent.mobileqq.pb.PBUInt64Field;
public final class structmsg$RspSystemMsgNew extends MessageMicro<structmsg$RspSystemMsgNew> {
static final FieldMap __fieldMap__;
public final PBStringField bytes_game_nick = PBField.initString("");
public final PBBytesField bytes_undecid_for_qim = PBField.initBytes(ByteStringMicro.EMPTY);
public final PBEnumField checktype = PBField.initEnum(1);
public final PBUInt64Field following_friend_seq = PBField.initUInt64(0L);
public final PBUInt64Field following_group_seq = PBField.initUInt64(0L);
public final PBRepeatMessageField<structmsg$StructMsg> friendmsgs = PBField.initRepeatMessage(structmsg$StructMsg.class);
public final PBRepeatMessageField<structmsg$StructMsg> groupmsgs = PBField.initRepeatMessage(structmsg$StructMsg.class);
public final PBStringField grp_msg_display = PBField.initString("");
public structmsg$RspHead head = new structmsg$RspHead();
public final PBUInt64Field latest_friend_seq = PBField.initUInt64(0L);
public final PBUInt64Field latest_group_seq = PBField.initUInt64(0L);
public final PBStringField msg_display = PBField.initString("");
public structmsg$StructMsg msg_ribbon_friend = new structmsg$StructMsg();
public structmsg$StructMsg msg_ribbon_group = new structmsg$StructMsg();
public final PBUInt32Field uint32_has_suspicious_flag = PBField.initUInt32(0);
public final PBUInt32Field uint32_over = PBField.initUInt32(0);
public final PBUInt32Field uint32_un_read_count3 = PBField.initUInt32(0);
public final PBUInt32Field unread_friend_count = PBField.initUInt32(0);
public final PBUInt32Field unread_group_count = PBField.initUInt32(0);
static {
Integer integer = Integer.valueOf(0);
Long long_ = Long.valueOf(0L);
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
__fieldMap__ = MessageMicro.initFieldMap(new int[] {
10, 16, 24, 32, 40, 48, 56, 74, 82, 90,
98, 106, 114, 120, 160, 802, 810, 816, 824 }, new String[] {
"head", "unread_friend_count", "unread_group_count", "latest_friend_seq", "latest_group_seq", "following_friend_seq", "following_group_seq", "friendmsgs", "groupmsgs", "msg_ribbon_friend",
"msg_ribbon_group", "msg_display", "grp_msg_display", "uint32_over", "checktype", "bytes_game_nick", "bytes_undecid_for_qim", "uint32_un_read_count3", "uint32_has_suspicious_flag" }, new Object[] {
null, integer, integer, long_, long_, long_, long_, null, null, null,
null, "", "", integer, Integer.valueOf(1), "", byteStringMicro, integer, integer }, structmsg$RspSystemMsgNew.class);
}
}

View File

@ -0,0 +1,17 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBEnumField;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBUInt32Field;
public final class structmsg$RspSystemMsgRead extends MessageMicro<structmsg$RspSystemMsgRead> {
static final FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 16, 24 }, new String[] { "head", "type", "checktype" }, new Object[] { null, Integer.valueOf(0), Integer.valueOf(1) }, structmsg$RspSystemMsgRead.class);
public final PBEnumField checktype = PBField.initEnum(1);
public structmsg$RspHead head = new structmsg$RspHead();
public final PBUInt32Field type = PBField.initUInt32(0);
}

View File

@ -0,0 +1,31 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBEnumField;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBUInt32Field;
import com.tencent.mobileqq.pb.PBUInt64Field;
public final class structmsg$StructMsg extends MessageMicro<structmsg$StructMsg> {
static final FieldMap __fieldMap__;
public structmsg$SystemMsg msg = new structmsg$SystemMsg();
public final PBUInt64Field msg_seq = PBField.initUInt64(0L);
public final PBUInt64Field msg_time = PBField.initUInt64(0L);
public final PBEnumField msg_type = PBField.initEnum(1);
public final PBUInt64Field req_uin = PBField.initUInt64(0L);
public final PBUInt32Field uint32_unread_flag = PBField.initUInt32(0);
public final PBUInt32Field version = PBField.initUInt32(0);
static {
Integer integer = Integer.valueOf(0);
Long long_ = Long.valueOf(0L);
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 24, 32, 40, 48, 402 }, new String[] { "version", "msg_type", "msg_seq", "msg_time", "req_uin", "uint32_unread_flag", "msg" }, new Object[] { integer, Integer.valueOf(1), long_, long_, long_, integer, null }, structmsg$StructMsg.class);
}
}

View File

@ -0,0 +1,186 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.ByteStringMicro;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBBytesField;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBInt32Field;
import com.tencent.mobileqq.pb.PBRepeatMessageField;
import com.tencent.mobileqq.pb.PBStringField;
import com.tencent.mobileqq.pb.PBUInt32Field;
import com.tencent.mobileqq.pb.PBUInt64Field;
public final class structmsg$SystemMsg extends MessageMicro<structmsg$SystemMsg> {
static final FieldMap __fieldMap__;
public final PBUInt64Field action_uin = PBField.initUInt64(0L);
public final PBStringField action_uin_nick;
public final PBBytesField action_uin_qq_nick;
public final PBBytesField action_uin_remark;
public final PBRepeatMessageField<structmsg$SystemMsgAction> actions = PBField.initRepeatMessage(structmsg$SystemMsgAction.class);
public final PBUInt64Field actor_uin = PBField.initUInt64(0L);
public final PBStringField actor_uin_nick;
public final PBBytesField bytes_addtion;
public final PBBytesField bytes_game_msg;
public final PBBytesField bytes_game_nick;
public final PBBytesField bytes_name_more;
public final PBBytesField bytes_source_desc;
public final PBBytesField bytes_transparent_group_notify;
public final PBBytesField bytes_warning_tips;
public final PBUInt32Field card_switch;
public final PBUInt64Field clone_uin = PBField.initUInt64(0L);
public final PBStringField clone_uin_nick;
public final PBBytesField eim_group_id_name;
public structmsg$FriendInfo friend_info = new structmsg$FriendInfo();
public final PBUInt64Field group_code = PBField.initUInt64(0L);
public final PBUInt32Field group_ext_flag;
public structmsg$GroupInfo group_info = new structmsg$GroupInfo();
public final PBUInt32Field group_inviter_role = PBField.initUInt32(0);
public final PBUInt32Field group_msg_type = PBField.initUInt32(0);
public final PBStringField group_name;
public final PBStringField msg_actor_describe = PBField.initString("");
public final PBStringField msg_additional = PBField.initString("");
public final PBStringField msg_additional_list = PBField.initString("");
public final PBStringField msg_decided = PBField.initString("");
public final PBStringField msg_describe = PBField.initString("");
public final PBStringField msg_detail;
public structmsg$MsgInviteExt msg_invite_extinfo = new structmsg$MsgInviteExt();
public structmsg$MsgPayGroupExt msg_pay_group_extinfo = new structmsg$MsgPayGroupExt();
public final PBStringField msg_qna;
public final PBStringField msg_source = PBField.initString("");
public final PBStringField msg_title = PBField.initString("");
public final PBBytesField pic_url;
public final PBUInt32Field relation = PBField.initUInt32(0);
public final PBUInt32Field req_uin_age;
public final PBBytesField req_uin_business_card;
public final PBInt32Field req_uin_faceid;
public final PBUInt32Field req_uin_gender;
public final PBStringField req_uin_nick;
public final PBBytesField req_uin_pre_remark;
public final PBUInt32Field reqsubtype = PBField.initUInt32(0);
public final PBUInt32Field src_id = PBField.initUInt32(0);
public final PBUInt32Field sub_src_id = PBField.initUInt32(0);
public final PBUInt32Field sub_type = PBField.initUInt32(0);
public final PBStringField uid;
public final PBUInt32Field uint32_c2c_invite_join_group_flag;
public final PBUInt32Field uint32_doubt_flag;
public final PBUInt32Field uint32_group_flagext3;
public final PBUInt32Field uint32_source_flag = PBField.initUInt32(0);
public final PBUInt64Field uint64_discuss_uin = PBField.initUInt64(0L);
public final PBUInt64Field uint64_eim_group_id = PBField.initUInt64(0L);
public final PBUInt64Field uint64_group_owner_uin;
static {
Integer integer = Integer.valueOf(0);
Long long_ = Long.valueOf(0L);
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
__fieldMap__ = MessageMicro.initFieldMap(new int[] {
8, 18, 26, 34, 42, 50, 56, 64, 74, 80,
88, 96, 104, 114, 122, 128, 138, 146, 152, 160,
168, 176, 184, 194, 202, 208, 218, 226, 232, 240,
248, 258, 266, 274, 282, 400, 410, 418, 426, 434,
442, 456, 466, 474, 482, 490, 506, 514, 522, 530,
536, 544, 552, 808, 866, 882 }, new String[] {
"sub_type", "msg_title", "msg_describe", "msg_additional", "msg_source", "msg_decided", "src_id", "sub_src_id", "actions", "group_code",
"action_uin", "group_msg_type", "group_inviter_role", "friend_info", "group_info", "actor_uin", "msg_actor_describe", "msg_additional_list", "relation", "reqsubtype",
"clone_uin", "uint64_discuss_uin", "uint64_eim_group_id", "msg_invite_extinfo", "msg_pay_group_extinfo", "uint32_source_flag", "bytes_game_nick", "bytes_game_msg", "uint32_group_flagext3", "uint64_group_owner_uin",
"uint32_doubt_flag", "bytes_warning_tips", "bytes_name_more", "bytes_addtion", "bytes_transparent_group_notify", "req_uin_faceid", "req_uin_nick", "group_name", "action_uin_nick", "msg_qna",
"msg_detail", "group_ext_flag", "actor_uin_nick", "pic_url", "clone_uin_nick", "req_uin_business_card", "eim_group_id_name", "req_uin_pre_remark", "action_uin_qq_nick", "action_uin_remark",
"req_uin_gender", "req_uin_age", "uint32_c2c_invite_join_group_flag", "card_switch", "bytes_source_desc", "uid" }, new Object[] {
integer, "", "", "", "", "", integer, integer, null, long_,
long_, integer, integer, null, null, long_, "", "", integer, integer,
long_, long_, long_, null, null, integer, byteStringMicro, byteStringMicro, integer, long_,
integer, byteStringMicro, byteStringMicro, byteStringMicro, byteStringMicro, integer, "", "", "", "",
"", integer, "", byteStringMicro, "", byteStringMicro, byteStringMicro, byteStringMicro, byteStringMicro, byteStringMicro,
integer, integer, integer, integer, byteStringMicro, "" }, structmsg$SystemMsg.class);
}
public structmsg$SystemMsg() {
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
this.bytes_game_nick = PBField.initBytes(byteStringMicro);
this.bytes_game_msg = PBField.initBytes(byteStringMicro);
this.uint32_group_flagext3 = PBField.initUInt32(0);
this.uint64_group_owner_uin = PBField.initUInt64(0L);
this.uint32_doubt_flag = PBField.initUInt32(0);
this.bytes_warning_tips = PBField.initBytes(byteStringMicro);
this.bytes_name_more = PBField.initBytes(byteStringMicro);
this.bytes_addtion = PBField.initBytes(byteStringMicro);
this.bytes_transparent_group_notify = PBField.initBytes(byteStringMicro);
this.req_uin_faceid = PBField.initInt32(0);
this.req_uin_nick = PBField.initString("");
this.group_name = PBField.initString("");
this.action_uin_nick = PBField.initString("");
this.msg_qna = PBField.initString("");
this.msg_detail = PBField.initString("");
this.group_ext_flag = PBField.initUInt32(0);
this.actor_uin_nick = PBField.initString("");
this.pic_url = PBField.initBytes(byteStringMicro);
this.clone_uin_nick = PBField.initString("");
this.req_uin_business_card = PBField.initBytes(byteStringMicro);
this.eim_group_id_name = PBField.initBytes(byteStringMicro);
this.req_uin_pre_remark = PBField.initBytes(byteStringMicro);
this.action_uin_qq_nick = PBField.initBytes(byteStringMicro);
this.action_uin_remark = PBField.initBytes(byteStringMicro);
this.req_uin_gender = PBField.initUInt32(0);
this.req_uin_age = PBField.initUInt32(0);
this.uint32_c2c_invite_join_group_flag = PBField.initUInt32(0);
this.card_switch = PBField.initUInt32(0);
this.bytes_source_desc = PBField.initBytes(byteStringMicro);
this.uid = PBField.initString("");
}
}

View File

@ -0,0 +1,20 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBStringField;
import com.tencent.mobileqq.pb.PBUInt32Field;
public final class structmsg$SystemMsgAction extends MessageMicro<structmsg$SystemMsgAction> {
static final FieldMap __fieldMap__ = MessageMicro.initFieldMap(new int[] { 10, 18, 24, 34, 42 }, new String[] { "name", "result", "action", "action_info", "detail_name" }, new Object[] { "", "", Integer.valueOf(0), null, "" }, structmsg$SystemMsgAction.class);
public final PBUInt32Field action = PBField.initUInt32(0);
public structmsg$SystemMsgActionInfo action_info = new structmsg$SystemMsgActionInfo();
public final PBStringField detail_name = PBField.initString("");
public final PBStringField name = PBField.initString("");
public final PBStringField result = PBField.initString("");
}

View File

@ -0,0 +1,40 @@
package tencent.mobileim.structmsg;
import com.tencent.mobileqq.pb.ByteStringMicro;
import com.tencent.mobileqq.pb.MessageMicro;
import com.tencent.mobileqq.pb.PBBoolField;
import com.tencent.mobileqq.pb.PBBytesField;
import com.tencent.mobileqq.pb.PBEnumField;
import com.tencent.mobileqq.pb.PBField;
import com.tencent.mobileqq.pb.PBStringField;
import com.tencent.mobileqq.pb.PBUInt32Field;
import com.tencent.mobileqq.pb.PBUInt64Field;
public final class structmsg$SystemMsgActionInfo extends MessageMicro<structmsg$SystemMsgActionInfo> {
static final FieldMap __fieldMap__;
public structmsg$AddFrdSNInfo addFrdSNInfo = new structmsg$AddFrdSNInfo();
public final PBBoolField blacklist = PBField.initBool(false);
public final PBUInt64Field group_code = PBField.initUInt64(0L);
public final PBUInt32Field group_id = PBField.initUInt32(0);
public final PBStringField msg = PBField.initString("");
public final PBStringField remark = PBField.initString("");
public final PBBytesField sig = PBField.initBytes(ByteStringMicro.EMPTY);
public final PBEnumField type = PBField.initEnum(1);
public final PBUInt32Field uint32_req_msg_type = PBField.initUInt32(0);
static {
Integer integer = Integer.valueOf(0);
ByteStringMicro byteStringMicro = ByteStringMicro.EMPTY;
Boolean bool = Boolean.FALSE;
__fieldMap__ = MessageMicro.initFieldMap(new int[] { 8, 16, 26, 402, 408, 418, 424, 434, 440 }, new String[] { "type", "group_code", "sig", "msg", "group_id", "remark", "blacklist", "addFrdSNInfo", "uint32_req_msg_type" }, new Object[] { Integer.valueOf(1), Long.valueOf(0L), byteStringMicro, "", integer, "", bool, null, integer }, structmsg$SystemMsgActionInfo.class);
}
}

View File

@ -3,7 +3,7 @@ plugins {
id("org.jetbrains.kotlin.android")
//id("io.realm.kotlin")
id("kotlin-kapt")
kotlin("plugin.serialization") version "1.8.0"
kotlin("plugin.serialization") version "1.8.10"
}
android {
@ -13,7 +13,6 @@ android {
defaultConfig {
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
externalNativeBuild {
cmake {
@ -50,6 +49,9 @@ android {
}
dependencies {
compileOnly ("de.robv.android.xposed:api:82")
compileOnly (project(":qqinterface"))
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.9.0")
@ -59,7 +61,8 @@ dependencies {
//implementation("io.realm.kotlin:library-sync:1.11.0")
val ktorVersion = "2.3.3"
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
//implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("io.github.xn32:json5k:0.3.0")
implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.1.16")
implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("io.ktor:ktor-server-host-common:$ktorVersion")
@ -71,6 +74,7 @@ dependencies {
implementation("io.ktor:ktor-client-cio:$ktorVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
//implementation("io.ktor:ktor-serialization-kotlinx-protobuf:$ktorVersion")
implementation("io.ktor:ktor-network-tls-certificates:$ktorVersion")
/**
@ -92,12 +96,5 @@ dependencies {
//ksp("androidx.room:room-compiler:$roomVersion")
// optional - Kotlin Extensions and Coroutines support for Room
implementation("androidx.room:room-ktx:$roomVersion")
compileOnly ("de.robv.android.xposed:api:82")
compileOnly (project(":qqinterface"))
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

View File

@ -0,0 +1,5 @@
@file:OptIn(ExperimentalSerializationApi::class)
package moe.fuqiuluo.qqinterface.entries
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable

View File

@ -11,6 +11,8 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.proto.protobufOf
import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
@ -35,45 +37,41 @@ internal abstract class BaseSvc {
return ToServiceMsg("mobileqq.service", app.currentAccountUin, cmd)
}
suspend fun sendOidbAW(cmd: String, cmdId: Int, serviceId: Int, data: ByteArray, trpc: Boolean = false): ByteArray? {
return suspendCoroutine { continuation ->
val seq = MsfCore.getNextSeq()
val timer = timer(initialDelay = 5000L, period = 5000L) {
suspend fun sendOidbAW(cmd: String, cmdId: Int, serviceId: Int, data: ByteArray, trpc: Boolean = false, timeout: Long = 5000L): ByteArray? {
val seq = MsfCore.getNextSeq()
return withTimeoutOrNull(timeout) {
suspendCancellableCoroutine { continuation ->
GlobalScope.launch(Dispatchers.Default) {
PacketHandler.unregisterLessHandler(seq)
continuation.resume(null)
DynamicReceiver.register(IPCRequest(cmd, seq) {
val buffer = it.getByteArrayExtra("buffer")!!
continuation.resume(buffer)
})
}
if (trpc) sendTrpcOidb(cmd, cmdId, serviceId, data, seq)
else sendOidb(cmd, cmdId, serviceId, data, seq)
}
GlobalScope.launch(Dispatchers.Default) {
DynamicReceiver.register(IPCRequest(cmd, seq) {
val buffer = it.getByteArrayExtra("buffer")!!
timer.cancel()
continuation.resume(buffer)
})
}
if (trpc) sendTrpcOidb(cmd, cmdId, serviceId, data, seq)
else sendOidb(cmd, cmdId, serviceId, data, seq)
}
}.also {
if (it == null)
DynamicReceiver.unregister(seq)
}?.copyOf()
}
suspend fun sendBufferAW(cmd: String, isPb: Boolean, data: ByteArray): ByteArray? {
return suspendCoroutine { continuation ->
val seq = MsfCore.getNextSeq()
val timer = timer(initialDelay = 5000L, period = 5000L) {
suspend fun sendBufferAW(cmd: String, isPb: Boolean, data: ByteArray, timeout: Long = 5000L): ByteArray? {
val seq = MsfCore.getNextSeq()
return withTimeoutOrNull<ByteArray?>(timeout) {
suspendCancellableCoroutine { continuation ->
GlobalScope.launch(Dispatchers.Default) {
PacketHandler.unregisterLessHandler(seq)
continuation.resume(null)
DynamicReceiver.register(IPCRequest(cmd, seq) {
val buffer = it.getByteArrayExtra("buffer")!!
continuation.resume(buffer)
})
sendBuffer(cmd, isPb, data, seq)
}
}
GlobalScope.launch(Dispatchers.Default) {
DynamicReceiver.register(IPCRequest(cmd, seq) {
val buffer = it.getByteArrayExtra("buffer")!!
timer.cancel()
continuation.resume(buffer)
})
sendBuffer(cmd, isPb, data, seq)
}
}
}.also {
if (it == null)
DynamicReceiver.unregister(seq)
}?.copyOf()
}
fun sendOidb(cmd: String, cmdId: Int, serviceId: Int, buffer: ByteArray, seq: Int = -1, trpc: Boolean = false) {
@ -110,7 +108,7 @@ internal abstract class BaseSvc {
app.sendToService(to)
}
fun sendBuffer(cmd: String, isPb: Boolean, buffer: ByteArray, seq: Int) {
fun sendBuffer(cmd: String, isPb: Boolean, buffer: ByteArray, seq: Int = MsfCore.getNextSeq()) {
val toServiceMsg = ToServiceMsg("mobileqq.service", app.currentUin, cmd)
toServiceMsg.putWupBuffer(buffer)
toServiceMsg.addAttribute("req_pb_protocol_flag", isPb)

View File

@ -1,12 +1,15 @@
package moe.fuqiuluo.qqinterface.servlet
import com.tencent.mobileqq.pb.ByteStringMicro
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import moe.fuqiuluo.proto.protobufOf
import moe.fuqiuluo.qqinterface.servlet.entries.*
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.shamrock.utils.DeflateTools
import tencent.im.oidb.cmd0x6d8.oidb_0x6d8
import tencent.im.oidb.oidb_sso
@ -33,8 +36,23 @@ internal object FileSvc: BaseSvc() {
}
fun deleteGroupFile(groupId: String, bizId: Int, fileUid: String) {
/*
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val richMediaService = sessionService.richMediaService
val result = withTimeoutOrNull(3000L) {
suspendCancellableCoroutine {
richMediaService.deleteGroupFile(groupId.toLong(), fileUid, bizId) { code, _, result ->
it.resume(code to result.result)
}
}
}
return if (result == null) Result.failure(RuntimeException("delete group file timeout")) else Result.success(result)*/
// 调用QQ内部实现会导致闪退
sendOidb("OidbSvc.0x6d6_3", 1750, 3, protobufOf(
4 to mapOf(
4 to mapOf(
1 to groupId.toLong(),
2 to 3,
3 to bizId,
@ -92,7 +110,7 @@ internal object FileSvc: BaseSvc() {
)
}
suspend fun getGroupRootFiles(groupId: Long): GroupFileList {
suspend fun getGroupRootFiles(groupId: Long): Result<GroupFileList> {
return getGroupFiles(groupId, "/")
}
@ -100,7 +118,7 @@ internal object FileSvc: BaseSvc() {
return FileUrl(RichProtoSvc.getGroupFileDownUrl(groupId, fileId, busid))
}
suspend fun getGroupFiles(groupId: Long, folderId: String): GroupFileList {
suspend fun getGroupFiles(groupId: Long, folderId: String): Result<GroupFileList> {
val fileSystemInfo = getGroupFileSystemInfo(groupId)
val rspGetFileListBuffer = sendOidbAW("OidbSvc.0x6d8_1", 1752, 1, oidb_0x6d8.ReqBody().also {
it.file_list_info_req.set(oidb_0x6d8.GetFileListReqBody().apply {
@ -122,17 +140,20 @@ internal object FileSvc: BaseSvc() {
uint32_show_onlinedoc_folder.set(0)
})
}.toByteArray())
}.toByteArray(), timeout = 15_000L)
val files = arrayListOf<FileInfo>()
val dirs = arrayListOf<FolderInfo>()
if (rspGetFileListBuffer != null) {
oidb_0x6d8.RspBody().mergeFrom(oidb_sso.OIDBSSOPkg()
.mergeFrom(rspGetFileListBuffer.slice(4))
.bytes_bodybuffer.get()
.toByteArray()).file_list_info_rsp.apply {
return kotlin.runCatching {
val files = arrayListOf<FileInfo>()
val dirs = arrayListOf<FolderInfo>()
if (rspGetFileListBuffer != null) {
val oidb = oidb_sso.OIDBSSOPkg().mergeFrom(rspGetFileListBuffer.slice(4).let {
if (it[0] == 0x78.toByte()) DeflateTools.uncompress(it) else it
})
oidb_0x6d8.RspBody().mergeFrom(oidb.bytes_bodybuffer.get().toByteArray())
.file_list_info_rsp.apply {
rpt_item_list.get().forEach { file ->
if (file.uint32_type.get() == 1) {
if (file.uint32_type.get() == oidb_0x6d8.GetFileListRspBody.TYPE_FILE) {
val fileInfo = file.file_info
files.add(FileInfo(
groupId = groupId,
@ -147,7 +168,8 @@ internal object FileSvc: BaseSvc() {
uploadUin = fileInfo.uint64_uploader_uin.get(),
uploadNick = fileInfo.str_uploader_name.get()
))
} else if (file.uint32_type.get() == 2) {
}
else if (file.uint32_type.get() == oidb_0x6d8.GetFileListRspBody.TYPE_FOLDER) {
val folderInfo = file.folder_info
dirs.add(FolderInfo(
groupId = groupId,
@ -158,58 +180,18 @@ internal object FileSvc: BaseSvc() {
creator = folderInfo.uint64_create_uin.get(),
creatorNick = folderInfo.str_creator_name.get()
))
} else {
LogCenter.log("未知文件类型: ${file.uint32_type.get()}", Level.WARN)
}
}
}
} else {
throw RuntimeException("获取群文件列表失败")
}
} else {
throw RuntimeException("获取群文件列表失败")
GroupFileList(files, dirs)
}.onFailure {
LogCenter.log(it.message + ", buffer: ${rspGetFileListBuffer.toHexString()}", Level.ERROR)
}
return GroupFileList(files, dirs)
}
@Serializable
data class FileUrl(
@SerialName("url") val url: String,
)
@Serializable
data class GroupFileList(
@SerialName("files") val files: List<FileInfo>,
@SerialName("folders") val folders: List<FolderInfo>,
)
@Serializable
data class FileInfo(
@SerialName("group_id") val groupId: Long,
@SerialName("file_id") val fileId: String,
@SerialName("file_name") val fileName: String,
@SerialName("file_size") val fileSize: Long,
@SerialName("busid") val busid: Int,
@SerialName("upload_time") val uploadTime: Int,
@SerialName("dead_time") val deadTime: Int,
@SerialName("modify_time") val modifyTime: Int,
@SerialName("download_times") val downloadTimes: Int,
@SerialName("uploader") val uploadUin: Long,
@SerialName("upload_name") val uploadNick: String,
)
@Serializable
data class FolderInfo(
@SerialName("group_id") val groupId: Long,
@SerialName("folder_id") val folderId: String,
@SerialName("folder_name") val folderName: String,
@SerialName("total_file_count") val totalFileCount: Int,
@SerialName("create_time") val createTime: Int,
@SerialName("creator") val creator: Long,
@SerialName("creator_name") val creatorNick: String,
)
@Serializable
data class FileSystemInfo(
@SerialName("file_count") val fileCount: Int,
@SerialName("limit_count") val fileLimitCount: Int,
@SerialName("used_space") val usedSpace: Long,
@SerialName("total_space") val totalSpace: Long,
)
}

View File

@ -1,19 +1,30 @@
@file:OptIn(DelicateCoroutinesApi::class)
@file:Suppress("IllegalIdentifier")
package moe.fuqiuluo.qqinterface.servlet
import com.tencent.common.app.AppInterface
import com.tencent.mobileqq.data.Friends
import com.tencent.mobileqq.friend.api.IFriendDataService
import com.tencent.mobileqq.friend.api.IFriendHandlerService
import com.tencent.mobileqq.qroute.QRoute
import com.tencent.mobileqq.relation.api.IAddFriendTempApi
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import mqq.app.AppRuntime
import mqq.app.MobileQQ
import tencent.mobileim.structmsg.`structmsg$FlagInfo`
import tencent.mobileim.structmsg.`structmsg$ReqSystemMsgNew`
import tencent.mobileim.structmsg.`structmsg$RspSystemMsgNew`
import kotlin.coroutines.resume
import tencent.mobileim.structmsg.`structmsg$SystemMsgActionInfo` as ActionInfo
import tencent.mobileim.structmsg.`structmsg$AddFrdSNInfo` as AddFrdSNInfo
import tencent.mobileim.structmsg.`structmsg$StructMsg` as StructMsg
internal object FriendSvc: BaseSvc() {
@ -28,6 +39,64 @@ internal object FriendSvc: BaseSvc() {
return Result.success(service.allFriends)
}
// ProfileService.Pb.ReqSystemMsgAction.Friend
fun requestFriendRequest(msgSeq: Long, uin: Long, remark: String = "", approve: Boolean? = true, notSee: Boolean? = false) {
val app = AppRuntimeFetcher.appRuntime
if (app !is AppInterface)
throw RuntimeException("AppRuntime cannot cast to AppInterface")
val service = QRoute.api(IAddFriendTempApi::class.java)
val action = ActionInfo()
action.type.set(if (approve != false) 2 else 3)
action.group_id.set(0)
action.remark.set(remark)
val snInfo = AddFrdSNInfo()
snInfo.uint32_not_see_dynamic.set(if (notSee != false) 1 else 0)
snInfo.uint32_set_sn.set(0)
action.addFrdSNInfo.set(snInfo)
service.sendFriendSystemMsgAction(1, msgSeq, uin, 1, 2004, 11, 0, action, 0, StructMsg(), false,
app
)
}
suspend fun requestFriendSystemMsgNew(msgNum: Int, latestFriendSeq: Long, latestGroupSeq: Long): List<StructMsg>? {
val req = `structmsg$ReqSystemMsgNew`()
req.msg_num.set(msgNum)
req.latest_friend_seq.set(latestFriendSeq)
req.latest_group_seq.set(latestGroupSeq)
req.version.set(1000)
req.checktype.set(2)
val flag = `structmsg$FlagInfo`()
// flag.GrpMsg_Kick_Admin.set(1)
// flag.GrpMsg_HiddenGrp.set(1)
// flag.GrpMsg_WordingDown.set(1)
flag.FrdMsg_GetBusiCard.set(1)
// flag.GrpMsg_GetOfficialAccount.set(1)
// flag.GrpMsg_GetPayInGroup.set(1)
flag.FrdMsg_Discuss2ManyChat.set(1)
// flag.GrpMsg_NotAllowJoinGrp_InviteNotFrd.set(1)
flag.FrdMsg_NeedWaitingMsg.set(1)
flag.FrdMsg_uint32_need_all_unread_msg.set(1)
// flag.GrpMsg_NeedAutoAdminWording.set(1)
// flag.GrpMsg_get_transfer_group_msg_flag.set(1)
// flag.GrpMsg_get_quit_pay_group_msg_flag.set(1)
// flag.GrpMsg_support_invite_auto_join.set(1)
// flag.GrpMsg_mask_invite_auto_join.set(1)
// flag.GrpMsg_GetDisbandedByAdmin.set(1)
flag.GrpMsg_GetC2cInviteJoinGroup.set(1)
req.flag.set(flag)
req.is_get_frd_ribbon.set(false)
req.is_get_grp_ribbon.set(false)
req.friend_msg_type_flag.set(1)
req.uint32_req_msg_type.set(1)
req.uint32_need_uid.set(1)
val respBuffer = sendBufferAW("ProfileService.Pb.ReqSystemMsgNew.Friend", true, req.toByteArray())
?: return ArrayList()
val msg = `structmsg$RspSystemMsgNew`()
msg.mergeFrom(respBuffer.slice(4))
return msg.friendmsgs.get()
}
private suspend fun requestFriendList(runtime: AppRuntime, dataService: IFriendDataService): Boolean {
val service = runtime.getRuntimeService(IFriendHandlerService::class.java, "all")
service.requestFriendList(true, 0)

View File

@ -22,21 +22,30 @@ import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.proto.ProtoUtils
import moe.fuqiuluo.proto.asInt
import moe.fuqiuluo.proto.asUtf8String
import moe.fuqiuluo.proto.protobufOf
import moe.fuqiuluo.qqinterface.servlet.entries.ProhibitedMemberInfo
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty
import moe.fuqiuluo.shamrock.tools.putBuf32Long
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import mqq.app.MobileQQ
import tencent.im.oidb.cmd0x899.oidb_0x899
import tencent.im.oidb.cmd0x89a.oidb_0x89a
import tencent.im.oidb.cmd0x8a0.oidb_0x8a0
import tencent.im.oidb.cmd0x8fc.Oidb_0x8fc
import tencent.im.oidb.oidb_sso
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.nio.ByteBuffer
import kotlin.coroutines.resume
import tencent.mobileim.structmsg.`structmsg$ReqSystemMsgNew` as ReqSystemMsgNew
import tencent.mobileim.structmsg.`structmsg$FlagInfo` as FlagInfo
import tencent.mobileim.structmsg.`structmsg$RspSystemMsgNew` as RspSystemMsgNew
import tencent.mobileim.structmsg.`structmsg$StructMsg` as StructMsg
internal object GroupSvc: BaseSvc() {
private val RefreshTroopMemberInfoLock by lazy {
Mutex()
@ -51,6 +60,28 @@ internal object GroupSvc: BaseSvc() {
private lateinit var METHOD_REQ_TROOP_MEM_LIST: Method
private lateinit var METHOD_REQ_MODIFY_GROUP_NAME: Method
suspend fun getProhibitedMemberList(groupId: Long): Result<List<ProhibitedMemberInfo>> {
val buffer = sendOidbAW("OidbSvc.0x899_0", 2201, 0, oidb_0x899.ReqBody().apply {
uint64_group_code.set(groupId)
uint64_start_uin.set(0)
uint32_identify_flag.set(6)
memberlist_opt.set(oidb_0x899.memberlist().apply {
uint64_member_uin.set(0)
uint32_shutup_timestap.set(0)
})
}.toByteArray()) ?: return Result.failure(RuntimeException("[oidb] timeout"))
val body = oidb_sso.OIDBSSOPkg()
body.mergeFrom(buffer.slice(4))
if(body.uint32_result.get() != 0) {
return Result.failure(RuntimeException(body.str_error_msg.get()))
}
val resp = oidb_0x899.RspBody().mergeFrom(body.bytes_bodybuffer.get().toByteArray())
return Result.success(resp.rpt_memberlist.get().map {
ProhibitedMemberInfo(it.uint64_member_uin.get(), it.uint32_shutup_timestap.get())
})
}
fun poke(groupId: String, userId: String) {
sendOidb("OidbSvc.0xed3", 3795, 1, protobufOf(
1 to userId.toLong(),
@ -68,6 +99,12 @@ internal object GroupSvc: BaseSvc() {
}.getOrThrow()
}
getGroupInfo(groupId, true).onSuccess {
if(it.wMemberNum > memberList.size) {
return getGroupMemberList(groupId, true)
}
}
return Result.success(memberList)
}
@ -107,7 +144,7 @@ internal object GroupSvc: BaseSvc() {
val memberInfo = Oidb_0x8fc.MemberInfo()
memberInfo.uint64_uin.set(userId.toLong())
memberInfo.bytes_uin_name.set(ByteStringMicro.copyFromUtf8(localMemberInfo.troopnick.ifBlank {
localMemberInfo.troopremark
localMemberInfo.troopremark.ifNullOrEmpty("")
}))
memberInfo.bytes_special_title.set(ByteStringMicro.copyFromUtf8(title))
memberInfo.uint32_special_title_expire_time.set(-1)
@ -133,6 +170,49 @@ internal object GroupSvc: BaseSvc() {
return true
}
suspend fun setEssenceMessage(groupId: Long, seq: Long, rand: Long): Pair<Boolean, String> {
val array = protobufOf(
1 to groupId,
2 to seq,
3 to rand
).toByteArray()
val buffer = sendOidbAW("OidbSvc.0xeac_1", 3756, 1, array)
?: return Pair(false, "unknown error")
val body = oidb_sso.OIDBSSOPkg()
body.mergeFrom(buffer.slice(4))
val result = ProtoUtils.decodeFromByteArray(body.bytes_bodybuffer.get().toByteArray())
return if (result.has(1)) {
LogCenter.log("设置群精华失败: ${result[1].asUtf8String}")
Pair(false, "设置群精华失败: ${result[1].asUtf8String}")
} else {
LogCenter.log("设置群精华 -> $groupId:$seq")
Pair(true, "ok")
}
}
suspend fun deleteEssenceMessage(groupId: Long, seq: Long, rand: Long): Pair<Boolean, String> {
val array = protobufOf(
1 to groupId,
2 to seq,
3 to rand
).toByteArray()
val buffer = sendOidbAW("OidbSvc.0xeac_2", 3756, 2, array)
val body = oidb_sso.OIDBSSOPkg()
if (buffer == null) {
return Pair(false, "unknown error")
}
body.mergeFrom(buffer.slice(4))
val result = ProtoUtils.decodeFromByteArray(body.bytes_bodybuffer.get().toByteArray())
return if (result.has(1)) {
LogCenter.log("移除群精华失败: ${result[1].asUtf8String}")
Pair(false, "移除群精华失败: ${result[1].asUtf8String}")
} else {
LogCenter.log("移除群精华 -> $groupId:$seq")
Pair(true, "ok")
}
}
fun setGroupAdmin(groupId: Long, userId: Long, enable: Boolean) {
val buffer = ByteBuffer.allocate(9)
buffer.putBuf32Long(groupId)
@ -518,4 +598,120 @@ internal object GroupSvc: BaseSvc() {
Result.failure(Exception("获取群成员信息失败"))
}
}
// ProfileService.Pb.ReqSystemMsgAction.Group
suspend fun requestGroupRequest(
msgSeq: Long,
uin: Long,
gid: Long,
msg: String? = "",
approve: Boolean? = true,
notSee: Boolean? = false,
subType: String
): Result<String>{
// 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) {
"invite" -> {
buffer = protobufOf(
1 to 1,
2 to msgSeq,
3 to uin, // self
4 to 1,
5 to 3,
6 to 10016,
7 to 2,
8 to mapOf(
1 to if (approve != false) 11 else 12,
2 to gid
),
9 to 1000
).toByteArray()
}
"add" -> {
buffer = protobufOf(
1 to 2,
2 to msgSeq,
3 to uin,
4 to 1,
5 to 2,
6 to 30024,
7 to 1,
8 to mapOf(
1 to if (approve != false) 11 else 12,
2 to gid
),
9 to 1000
).toByteArray()
}
else -> {
return Result.failure(Exception("不支持的sub_type"))
}
}
val respBuffer = sendBufferAW("ProfileService.Pb.ReqSystemMsgAction.Group", true, buffer)
?: return Result.failure(Exception("操作失败"))
val result = ProtoUtils.decodeFromByteArray(respBuffer.slice(4))
return if (result.has(1, 1)) {
if (result[1, 1].asInt == 0) {
Result.success(result[2].asUtf8String)
} else {
Result.failure(Exception(result[2].asUtf8String))
}
} else {
Result.failure(Exception("操作失败"))
}
}
suspend fun requestGroupSystemMsgNew(msgNum: Int, latestFriendSeq: Long, latestGroupSeq: Long): List<StructMsg>? {
val req = ReqSystemMsgNew()
req.msg_num.set(msgNum)
req.latest_friend_seq.set(latestFriendSeq)
req.latest_group_seq.set(latestGroupSeq)
req.version.set(1000)
req.checktype.set(3)
val flag = FlagInfo()
flag.GrpMsg_Kick_Admin.set(1)
flag.GrpMsg_HiddenGrp.set(1)
flag.GrpMsg_WordingDown.set(1)
// flag.FrdMsg_GetBusiCard.set(1)
flag.GrpMsg_GetOfficialAccount.set(1)
flag.GrpMsg_GetPayInGroup.set(1)
flag.FrdMsg_Discuss2ManyChat.set(1)
flag.GrpMsg_NotAllowJoinGrp_InviteNotFrd.set(1)
flag.FrdMsg_NeedWaitingMsg.set(1)
// flag.FrdMsg_uint32_need_all_unread_msg.set(1)
flag.GrpMsg_NeedAutoAdminWording.set(1)
flag.GrpMsg_get_transfer_group_msg_flag.set(1)
flag.GrpMsg_get_quit_pay_group_msg_flag.set(1)
flag.GrpMsg_support_invite_auto_join.set(1)
flag.GrpMsg_mask_invite_auto_join.set(1)
flag.GrpMsg_GetDisbandedByAdmin.set(1)
flag.GrpMsg_GetC2cInviteJoinGroup.set(1)
req.flag.set(flag)
req.is_get_frd_ribbon.set(false)
req.is_get_grp_ribbon.set(false)
req.friend_msg_type_flag.set(1)
req.uint32_req_msg_type.set(1)
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()
}
}

View File

@ -1,23 +1,62 @@
@file:OptIn(DelicateCoroutinesApi::class)
package moe.fuqiuluo.qqinterface.servlet
import com.tencent.mobileqq.qroute.QRoute
import com.tencent.mobileqq.troop.api.ITroopMemberNameService
import com.tencent.qqnt.kernel.api.IKernelService
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.kernel.nativeinterface.TempChatGameSession
import com.tencent.qqnt.kernel.nativeinterface.TempChatPrepareInfo
import com.tencent.qqnt.msg.api.IMsgService
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.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.MessageHelper
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import moe.fuqiuluo.shamrock.xposed.helper.msgService
import java.util.UUID
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
internal object MsgSvc: BaseSvc() {
fun uploadForwardMsg(): Result<String> {
return Result.failure(Exception("Not implemented"))
}
suspend fun prepareTempChatFromGroup(
groupId: String,
peerId: String
): Result<Unit> {
LogCenter.log("主动临时消息,创建临时会话。", Level.INFO)
val msgService = app.getRuntimeService(IKernelService::class.java, "all").msgService
?: return Result.failure(Exception("获取消息服务失败"))
msgService.prepareTempChat(TempChatPrepareInfo(
MsgConstant.KCHATTYPETEMPC2CFROMGROUP,
ContactHelper.getUidByUinAsync(peerId = peerId.toLong()),
app.getRuntimeService(ITroopMemberNameService::class.java, "all")
.getTroopMemberNameRemarkFirst(groupId, peerId),
groupId, EMPTY_BYTE_ARRAY, app.currentUid, "", TempChatGameSession()
)) { code, reason ->
if (code != 0) {
LogCenter.log("临时会话创建失败: $code, $reason", Level.ERROR)
}
}
return Result.success(Unit)
}
/**
* 正常获取
*/
@ -51,6 +90,34 @@ internal object MsgSvc: BaseSvc() {
}
}
suspend fun getMsgByQMsgId(
chatType: Int,
peerId: String,
qqMsgId: Long
): Result<MsgRecord> {
val contact = MessageHelper.generateContact(chatType, peerId)
val service = QRoute.api(IMsgService::class.java) ?:
return Result.failure(Exception("获取消息服务"))
val msg = withTimeoutOrNull(5000) {
suspendCoroutine { continuation ->
service.getMsgsByMsgId(contact, arrayListOf(qqMsgId)) { code, _, msgRecords ->
if (code == 0 && msgRecords.isNotEmpty()) {
continuation.resume(msgRecords.first())
} else {
continuation.resume(null)
}
}
}
}
return if (msg != null) {
Result.success(msg)
} else {
Result.failure(Exception("获取消息失败"))
}
}
/**
* 什么鸟屎都获取不到
*/
@ -60,7 +127,7 @@ internal object MsgSvc: BaseSvc() {
seq: Long
): Result<MsgRecord> {
val contact = MessageHelper.generateContact(chatType, peerId)
val msg = withTimeoutOrNull(60 * 1000) {
val msg = withTimeoutOrNull(1000) {
val service = QRoute.api(IMsgService::class.java)
suspendCancellableCoroutine { continuation ->
service.getMsgsBySeqs(contact, arrayListOf(seq)) { code, _, msgRecords ->
@ -103,10 +170,64 @@ internal object MsgSvc: BaseSvc() {
*
* Aio 腾讯内部命名 All In One
*/
suspend fun sendToAio(chatType: Int, peedId: String, message: JsonArray): Pair<Long, Int> {
suspend fun sendToAio(
chatType: Int,
peedId: String,
message: JsonArray,
fromId: String = peedId
): Pair<Long, Int> {
//LogCenter.log(message.toString(), Level.ERROR)
//callback.msgHash = result.second 什么垃圾代码万一cb比你快你不就寄了
return MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, MessageCallback(peedId, 0))
// 主动临时消息
when(chatType) {
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> {
prepareTempChatFromGroup(fromId, peedId).onFailure {
LogCenter.log("主动临时消息,创建临时会话失败。", Level.ERROR)
return -1L to 0
}
}
}
return MessageHelper.sendMessageWithoutMsgId(chatType, peedId, message, MessageCallback(peedId, 0), fromId)
}
suspend fun getMultiMsg(resId: String): Result<List<MsgRecord>> {
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val msgService = sessionService.msgService
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, TicketSvc.getUin())
val content = "{\"app\":\"com.tencent.multimsg\",\"config\":{\"autosize\":1,\"forward\":1,\"round\":1,\"type\":\"normal\",\"width\":300},\"desc\":\"[聊天记录]\",\"extra\":\"\",\"meta\":{\"detail\":{\"news\":[{\"text\":\"Shamrock: 这是条假消息!\"}],\"resid\":\"$resId\",\"source\":\"聊天记录\",\"summary\":\"转发消息\",\"uniseq\":\"${UUID.randomUUID()}\"}},\"prompt\":\"[聊天记录]\",\"ver\":\"0.0.0.5\",\"view\":\"contact\"}"
val msgId = PacketSvc.fakeSelfRecvJsonMsg(msgService, content)
if (msgId < 0) {
return Result.failure(Exception("获取合并转发消息ID失败"))
}
val msgList = withTimeoutOrNull(5000L) {
suspendCancellableCoroutine<ArrayList<MsgRecord>> {
val job = GlobalScope.launch {
var hasResult = false
while (!hasResult) {
msgService.getMultiMsg(contact, msgId, msgId) { code, why, msgList ->
if (code == 0) {
it.resume(msgList)
hasResult = true
} else {
LogCenter.log("获取合并转发消息失败: $code($why): $msgId", Level.ERROR)
}
}
delay(200)
}
}
it.invokeOnCancellation {
job.cancel()
}
}
} ?: return Result.failure(Exception("获取合并转发消息失败"))
//msgService.deleteMsg(contact, arrayListOf(msgId), null)
return Result.success(msgList)
}
class MessageCallback(

View File

@ -0,0 +1,98 @@
package moe.fuqiuluo.qqinterface.servlet
import com.tencent.mobileqq.msf.core.MsfCore
import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.IKernelMsgService
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import io.ktor.utils.io.core.BytePacketBuilder
import io.ktor.utils.io.core.readBytes
import io.ktor.utils.io.core.writeFully
import io.ktor.utils.io.core.writeInt
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.proto.protobufOf
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.remote.action.handlers.GetHistoryMsg
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
import moe.fuqiuluo.shamrock.tools.broadcast
import moe.fuqiuluo.shamrock.utils.DeflateTools
import mqq.app.MobileQQ
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlin.math.abs
import kotlin.random.Random
import kotlin.random.nextInt
import kotlin.random.nextLong
internal object PacketSvc: BaseSvc() {
/**
* 伪造收到Json卡片消息
*/
suspend fun fakeSelfRecvJsonMsg(msgService: IKernelMsgService, content: String): Long {
return fakeReceiveSelfMsg(msgService) { arrayOf(
mapOf(
51 to 1 to (byteArrayOf(1) + DeflateTools.compress(content.toByteArray()))
)
) }
}
private suspend fun fakeReceiveSelfMsg(msgService: IKernelMsgService, builder: () -> Array<Map<*, *>>): Long {
val latestMsg = withTimeoutOrNull(3000) {
suspendCancellableCoroutine {
msgService.getMsgs(Contact(MsgConstant.KCHATTYPEC2C, app.currentUid, ""), 0L, 1, true) { code, why, msgs ->
it.resume(GetHistoryMsg.GetMsgResult(code, why, msgs))
}
}
}?.data?.firstOrNull()
val msgSeq = (latestMsg?.msgSeq ?: 0) + 1
fakeReceive("trpc.msg.olpush.OlPushService.MsgPush", 10000, protobufOf(
1 to mapOf(
1 to mapOf(
1 to app.currentUin.toLong(),
2 to app.currentUid,
3 to 1001,
5 to app.currentUin.toLong(),
6 to app.currentUid
),
2 to mapOf(
1 to 166,
3 to 11,
4 to msgSeq,
5 to msgSeq,
6 to (System.currentTimeMillis() / 1000).toInt(),
7 to 1,
11 to msgSeq,
12 to msgService.getMsgUniqueId(System.currentTimeMillis()),
14 to msgSeq - 2,
28 to msgSeq
),
3 to 1 to 2 to builder()
)
).toByteArray())
return withTimeoutOrNull(5000L) {
suspendCancellableCoroutine {
AioListener.messageLessListenerMap[msgSeq] = {
it.resume(this.msgId)
}
}
} ?: -1L
}
/**
* 伪造QQ收到某个包
*/
private fun fakeReceive(cmd: String, seq: Int, buffer: ByteArray) {
MobileQQ.getContext().broadcast("msf") {
putExtra("__cmd", "fake_packet")
putExtra("package_cmd", cmd)
putExtra("package_uin", app.currentUin)
putExtra("package_seq", seq)
val wupBuffer = BytePacketBuilder().apply {
writeInt(buffer.size + 4)
writeFully(buffer)
}.build()
putExtra("package_buffer", wupBuffer.readBytes())
wupBuffer.release()
}
}
}

View File

@ -43,6 +43,10 @@ internal object TicketSvc: BaseSvc() {
return app.currentUin.ifBlank { "0" }
}
fun getLongUin(): Long {
return app.longAccountUin
}
fun getCookie(): String {
val uin = getUin()
val skey = getRealSkey(uin)

View File

@ -86,10 +86,10 @@ internal object VisitorSvc: BaseSvc() {
it.putLong(moe.fuqiuluo.shamrock.remote.service.data.profile.ProfileProtocolConst.PARAM_TARGET_UIN, target)
it.putByteArray("vCookies", card.vCookies)
it.putBoolean("nearby_people", true)
it.putInt("favoriteSource", SUB_FROM_SHARE_CARD_TROOP)
it.putInt("favoriteSource", FROM_CONTACTS_TAB)
it.putInt("iCount", count)
it.putInt("from", FROM_SHARE_CARD)
it.putInt("from", FROM_CONTACTS_TAB)
}
return Result.success(Unit)
}
}
}

View File

@ -0,0 +1,49 @@
package moe.fuqiuluo.qqinterface.servlet.entries
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class FileUrl(
@SerialName("url") val url: String,
)
@Serializable
data class GroupFileList(
@SerialName("files") val files: List<FileInfo>,
@SerialName("folders") val folders: List<FolderInfo>,
)
@Serializable
data class FileInfo(
@SerialName("group_id") val groupId: Long,
@SerialName("file_id") val fileId: String,
@SerialName("file_name") val fileName: String,
@SerialName("file_size") val fileSize: Long,
@SerialName("busid") val busid: Int,
@SerialName("upload_time") val uploadTime: Int,
@SerialName("dead_time") val deadTime: Int,
@SerialName("modify_time") val modifyTime: Int,
@SerialName("download_times") val downloadTimes: Int,
@SerialName("uploader") val uploadUin: Long,
@SerialName("upload_name") val uploadNick: String,
)
@Serializable
data class FolderInfo(
@SerialName("group_id") val groupId: Long,
@SerialName("folder_id") val folderId: String,
@SerialName("folder_name") val folderName: String,
@SerialName("total_file_count") val totalFileCount: Int,
@SerialName("create_time") val createTime: Int,
@SerialName("creator") val creator: Long,
@SerialName("creator_name") val creatorNick: String,
)
@Serializable
data class FileSystemInfo(
@SerialName("file_count") val fileCount: Int,
@SerialName("limit_count") val fileLimitCount: Int,
@SerialName("used_space") val usedSpace: Long,
@SerialName("total_space") val totalSpace: Long,
)

View File

@ -0,0 +1,10 @@
package moe.fuqiuluo.qqinterface.servlet.entries
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
internal data class ProhibitedMemberInfo(
@SerialName("user_id") val memberUin: Long,
@SerialName("time") val shutuptimestap: Int
)

View File

@ -1,103 +0,0 @@
package moe.fuqiuluo.qqinterface.servlet.msg
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import moe.fuqiuluo.proto.ProtoUtils
import moe.fuqiuluo.proto.asUtf8String
import moe.fuqiuluo.proto.protobufOf
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.utils.DeflateTools
internal object LongMsgHelper: BaseSvc() {
private const val GROUP_LONG_MSG_CMD = "trpc.group.long_msg_interface.MsgService.SsoSendLongMsg"
suspend fun uploadGroupMsg(groupId: String, msgs: List<MsgRecord>): String {
val reqBody = protobufOf(
2 to mapOf(
1 to 3,
2 to 2 to groupId,
3 to groupId.toLong(),
4 to DeflateTools.gzip(toGroupByteArray(msgs))
),
15 to mapOf(
1 to 4,
2 to 2,
3 to 9,
4 to 0
)
).toByteArray()
val buffer = sendBufferAW(GROUP_LONG_MSG_CMD, true, reqBody)
?: error("获取消息资源ID失败")
val pb = ProtoUtils.decodeFromByteArray(buffer.slice(4))
return pb[2, 3].asUtf8String
}
private fun toGroupByteArray(msgs: List<MsgRecord>): ByteArray {
return protobufOf(
2 to mapOf(
1 to "MultiMsg",
2 to 1 to msgs.map { record ->
mapOf(
1 to mapOf(
2 to record.senderUid,
8 to mapOf(
1 to record.peerUin,
4 to record.sendNickName,
5 to 2
)
),
2 to mapOf(
1 to 82,
4 to record.msgRandom,
5 to record.msgSeq,
6 to record.msgTime,
7 to 1,
8 to 0,
9 to 0,
15 to mapOf(
1 to 0,
2 to 0,
3 to 0,
4 to "",
5 to ""
)
),
3 to mapOf(
1 to 2 to (record.elements.map {
when (val type = it.elementType) {
MsgConstant.KELEMTYPETEXT -> mapOf(1 to 1 to it.textElement.content)
else -> error("不支持的合并转发消息类型: $type")
}
} as ArrayList<Any>).also {
it.add(0, mapOf(
37 to mapOf(
1 to 8,
16 to 0,
17 to 0,
19 to mapOf(
15 to 65536,
25 to 0,
30 to 0,
31 to 0,
34 to 0,
41 to 0,
52 to 64,
54 to 1,
55 to 0,
72 to 0,
73 to 1 to 0,
96 to 0
)
)
))
}
)
)
}
)
).toByteArray()
}
}

View File

@ -95,9 +95,27 @@ internal object MessageMaker {
"reply" to MessageMaker::createReplyElem,
"touch" to MessageMaker::createTouchElem,
"weather" to MessageMaker::createWeatherElem,
"json" to MessageMaker::createJsonElem,
//"multi_msg" to MessageMaker::createLongMsgStruct,
)
private suspend fun createJsonElem(
chatType: Int,
msgId: Long,
peerId: String,
data: JsonObject
): Result<MsgElement> {
data.checkAndThrow("data")
val jsonStr = data["data"].let {
if (it is JsonObject) it.asJsonObject.toString() else it.asString
}
val element = MsgElement()
element.elementType = MsgConstant.KELEMTYPEARKSTRUCT
val ark = ArkElement(jsonStr, null, null)
element.arkElement = ark
return Result.success(element)
}
private suspend fun createTouchElem(
chatType: Int,
msgId: Long,
@ -539,7 +557,7 @@ internal object MessageMaker {
file = FileUtils.parseAndSave(data["url"].asString)
}
if (!file.exists()) {
throw LogicException("Voice(${file.name}) file is not exists, please check your filename.")
return Result.failure(LogicException("Voice(${file.name}) file is not exists, please check your filename."))
}
val isMagic = data["magic"].asStringOrNull == "1"
@ -580,11 +598,14 @@ internal object MessageMaker {
// QQNTWrapperUtil.CppProxy.copyFile(file.absolutePath, originalPath)
//}
Transfer with when (chatType) {
if(!(Transfer with when (chatType) {
MsgConstant.KCHATTYPEGROUP -> Troop(peerId)
MsgConstant.KCHATTYPEC2C -> Private(peerId)
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> Private(peerId)
else -> error("Not supported chatType($chatType) for RecordMsg")
} trans VoiceResource(file)
} trans VoiceResource(file))) {
return Result.failure(RuntimeException("上传语音失败: $file"))
}
val elem = MsgElement()
elem.elementType = MsgConstant.KELEMTYPEPTT

View File

@ -52,7 +52,9 @@ internal object MessageConvert {
MsgConstant.KELEMTYPEARKSTRUCT to MessageElemConverter.StructJsonConverter,
MsgConstant.KELEMTYPEREPLY to MessageElemConverter.ReplyConverter,
MsgConstant.KELEMTYPEGRAYTIP to MessageElemConverter.GrayTipsConverter,
MsgConstant.KELEMTYPEFILE to MessageElemConverter.FileConverter
MsgConstant.KELEMTYPEFILE to MessageElemConverter.FileConverter,
//MsgConstant.KELEMTYPEMULTIFORWARD to MessageElemConverter.XmlMultiMsgConverter,
//MsgConstant.KELEMTYPESTRUCTLONGMSG to MessageElemConverter.XmlLongMsgConverter,
)
}

View File

@ -143,13 +143,15 @@ internal sealed class MessageElemConverter: IMessageConvert {
element: MsgElement
): MessageSegment {
val video = element.videoElement
val md5 = video.fileName.split(".")[0]
return MessageSegment(
type = "video",
data = hashMapOf(
"file" to video.fileName,
"url" to when(chatType) {
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", video.fileName, video.fileUuid)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", video.fileName, video.fileUuid)
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid)
else -> unknownChatType(chatType)
}
).also {
@ -194,6 +196,15 @@ internal sealed class MessageElemConverter: IMessageConvert {
): MessageSegment {
val data = element.arkElement.bytesData.asJsonObject
return when (data["app"].asString) {
"com.tencent.multimsg" -> {
val info = data["meta"].asJsonObject["detail"].asJsonObject
MessageSegment(
type = "forward",
data = mapOf(
"id" to info["resid"].asString
)
)
}
"com.tencent.troopsharecard" -> {
val info = data["meta"].asJsonObject["contact"].asJsonObject
MessageSegment(
@ -229,7 +240,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
else -> MessageSegment(
type = "json",
data = mapOf(
"data" to element.arkElement.bytesData.asJsonObject
"data" to element.arkElement.bytesData.asJsonObject.toString()
)
)
}
@ -252,7 +263,11 @@ internal sealed class MessageElemConverter: IMessageConvert {
} else {
MessageDB.getInstance().messageMappingDao()
.queryByMsgSeq(chatType, peerId, reply.replayMsgSeq?.toInt() ?: 0)?.msgHashId
?: MessageHelper.generateMsgIdHash(chatType, reply.sourceMsgIdInRecords)
?:
kotlin.run {
LogCenter.log("消息映射关系未找到: Message($reply)", Level.WARN)
MessageHelper.generateMsgIdHash(chatType, reply.sourceMsgIdInRecords)
}
}
return MessageSegment(
@ -307,8 +322,63 @@ internal sealed class MessageElemConverter: IMessageConvert {
peerId: String,
element: MsgElement
): MessageSegment {
// 使用其他地方的推送,而不是使用消息
throw UnknownError()
val fileMsg = element.fileElement
val fileName = fileMsg.fileName
val fileSize = fileMsg.fileSize
val expireTime = fileMsg.expireTime ?: 0
val fileId = fileMsg.fileUuid
val bizId = fileMsg.fileBizId
val fileSubId = fileMsg.fileSubId ?: ""
val url = if (chatType == MsgConstant.KCHATTYPEC2C) RichProtoSvc.getC2CFileDownUrl(fileId, fileSubId)
else RichProtoSvc.getGroupFileDownUrl(peerId.toLong(), fileId, fileMsg.fileBizId)
return MessageSegment(
type = "file",
data = mapOf(
"name" to fileName,
"size" to fileSize,
"expire" to expireTime,
"id" to fileId,
"url" to url,
"biz" to bizId,
"sub" to fileSubId
)
)
}
}
/**
* 老板QQ的合并转发信息
*/
object XmlMultiMsgConverter: MessageElemConverter() {
override suspend fun convert(
chatType: Int,
peerId: String,
element: MsgElement
): MessageSegment {
val multiMsg = element.multiForwardMsgElement
return MessageSegment(
type = "forward",
data = mapOf(
"id" to multiMsg.resId
)
)
}
}
object XmlLongMsgConverter: MessageElemConverter() {
override suspend fun convert(
chatType: Int,
peerId: String,
element: MsgElement
): MessageSegment {
val longMsg = element.structLongMsgElement
return MessageSegment(
type = "forward",
data = mapOf(
"id" to longMsg.resId
)
)
}
}

View File

@ -10,6 +10,7 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.utils.MD5
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import mqq.app.AppRuntime
@ -73,30 +74,34 @@ internal abstract class FileTransfer {
transferRequest: TransferRequest,
wait: Boolean
): Boolean {
val service = runtime.getRuntimeService(ITransFileController::class.java, "all")
if(service.transferAsync(transferRequest)) {
if (!wait) { // 如果无需等待直接返回
return true
}
return suspendCancellableCoroutine { continuation ->
val waiter = GlobalScope.launch {
while (
service.findProcessor(transferRequest.keyForTransfer) != null
// 如果上传处理器依旧存在,说明没有上传成功
) {
delay(100)
return withTimeoutOrNull(60_000) {
val service = runtime.getRuntimeService(ITransFileController::class.java, "all")
if(service.transferAsync(transferRequest)) {
if (!wait) { // 如果无需等待直接返回
return@withTimeoutOrNull true
}
suspendCancellableCoroutine { continuation ->
GlobalScope.launch {
while (
//service.findProcessor(
// transferRequest.keyForTransfer // uin + uniseq
//) != null
service.containsProcessor(runtime.currentAccountUin, transferRequest.mUniseq)
// 如果上传处理器依旧存在,说明没有上传成功
&& service.isWorking.get()
) {
delay(100)
}
continuation.resume(true)
}
// 实现取消上传器
// 目前没什么用
continuation.invokeOnCancellation {
continuation.resume(false)
}
continuation.resume(true)
}
// 实现取消上传器
// 目前没什么用
continuation.invokeOnCancellation {
waiter.cancel()
continuation.resume(false)
}
}
}
return false
} else true
} ?: false
}
companion object {

View File

@ -63,24 +63,30 @@ internal object RichProtoSvc: BaseSvc() {
suspend fun getC2CFileDownUrl(
fileId: String,
subId: String,
retryCnt: Int = 0
): String {
val uid = ContactHelper.getUidByUinAsync(app.currentUin.toLong())
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xe37_1200", 3639, 1200, protobufOf(
val buffer = sendOidbAW("OidbSvc.0xe37_1200", 3639, 1200, protobufOf(
1 to 1200,
2 to 1 /* QRoute.api(IAudioHelperApi::class.java).genDebugSeq().toInt() */, /* seq */
14 to mapOf(
10 to uid,
10 to app.longAccountUin,
20 to fileId,
30 to 2, /* ver */
60 to subId,
601 to 0
),
101 to 3,
101 to 3, // uint32_business_id
102 to 104, /* client_type */
200 to 1, /* url_type */
99999 to 90200 to 1
).toByteArray(), trpc = true)
200 to 1, /* uint32_flag_support_mediaplatform */
99999 to mapOf(
90200 to 1 // uint32_download_url_type
)
).toByteArray())
if (buffer == null) {
if (retryCnt < 3) {
return getC2CFileDownUrl(fileId, subId, retryCnt + 1)
}
return ""
} else {
val body = oidb_sso.OIDBSSOPkg()

View File

@ -24,12 +24,12 @@ internal object Transfer: FileTransfer() {
)
suspend fun uploadC2CVideo(
groupId: String,
userId: String,
file: File,
thumb: File,
wait: Boolean = true
): Boolean {
return transC2CResource(groupId, file, FileMsg.TRANSFILE_TYPE_SHORT_VIDEO_C2C, BUSI_TYPE_SHORT_VIDEO, wait) {
return transC2CResource(userId, file, FileMsg.TRANSFILE_TYPE_SHORT_VIDEO_C2C, BUSI_TYPE_SHORT_VIDEO, wait) {
it.mSourceVideoCodecFormat = VIDEO_FORMAT_MP4
it.mRec = MessageForShortVideo().also {
it.busiType = BUSI_TYPE_SHORT_VIDEO
@ -56,11 +56,11 @@ internal object Transfer: FileTransfer() {
}
suspend fun uploadC2CVoice(
groupId: String,
userId: String,
file: File,
wait: Boolean = true
): Boolean {
return transC2CResource(groupId, file, FileMsg.TRANSFILE_TYPE_PTT, 1002, wait) {
return transC2CResource(userId, file, FileMsg.TRANSFILE_TYPE_PTT, 1002, wait) {
it.mPttUploadPanel = 3
it.mPttCompressFinish = true
it.mIsPttPreSend = true

View File

@ -23,25 +23,65 @@ import moe.fuqiuluo.shamrock.tools.jsonArray
import kotlin.math.abs
internal object MessageHelper {
suspend fun sendMessageWithoutMsgId(chatType: Int, peerId: String, message: JsonArray, callback: IOperateCallback): Pair<Long, Int> {
suspend fun sendMessageWithoutMsgId(
chatType: Int,
peerId: String,
message: String,
callback: IOperateCallback,
fromId: String = peerId
): Pair<Long, Int> {
val uniseq = generateMsgId(chatType)
val msg = messageArrayToMessageElements(chatType, uniseq.second, peerId, decodeCQCode(message)).also {
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
}.second.filter {
it.elementType != -1
} as ArrayList<MsgElement>
return sendMessageWithoutMsgId(chatType, peerId, msg, callback, fromId)
}
suspend fun sendMessageWithoutMsgId(
chatType: Int,
peerId: String,
message: JsonArray,
callback: IOperateCallback,
fromId: String = peerId
): Pair<Long, Int> {
val uniseq = generateMsgId(chatType)
var nonMsg: Boolean
val msg = messageArrayToMessageElements(chatType, uniseq.second, peerId, message).also {
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
}.second.filter {
it.elementType != -1
}.also {
nonMsg = it.isEmpty()
}
} as ArrayList<MsgElement>
return sendMessageWithoutMsgId(chatType, peerId, msg, callback, fromId)
}
suspend fun sendMessageWithoutMsgId(
chatType: Int,
peerId: String,
message: ArrayList<MsgElement>,
callback: IOperateCallback,
fromId: String = peerId
): Pair<Long, Int> {
return sendMessageWithoutMsgId(generateContact(chatType, peerId, fromId), message, callback)
}
fun sendMessageWithoutMsgId(
contact: Contact,
message: ArrayList<MsgElement>,
callback: IOperateCallback
): Pair<Long, Int> {
val uniseq = generateMsgId(contact.chatType)
val nonMsg: Boolean = message.isEmpty()
return if (!nonMsg) {
val service = QRoute.api(IMsgService::class.java)
if(callback is MsgSvc.MessageCallback) {
callback.msgHash = uniseq.first
}
service.sendMsg(
generateContact(chatType, peerId),
contact,
uniseq.second,
msg as ArrayList<MsgElement>,
message,
callback
)
System.currentTimeMillis() to uniseq.first
@ -50,8 +90,66 @@ internal object MessageHelper {
}
}
suspend fun sendMessageWithMsgId(
chatType: Int,
peerId: String,
message: JsonArray,
callback: IOperateCallback,
fromId: String = peerId
): Pair<Long, Int> {
val uniseq = generateMsgId(chatType)
val msg = messageArrayToMessageElements(chatType, uniseq.second, peerId, message).also {
if (it.second.isEmpty() && !it.first) error("消息合成失败,请查看日志或者检查输入。")
}.second.filter {
it.elementType != -1
} as ArrayList<MsgElement>
val contact = generateContact(chatType, peerId, fromId)
val nonMsg: Boolean = message.isEmpty()
return if (!nonMsg) {
val service = QRoute.api(IMsgService::class.java)
if(callback is MsgSvc.MessageCallback) {
callback.msgHash = uniseq.first
}
service.sendMsg(
contact,
uniseq.second,
msg,
callback
)
uniseq.second to uniseq.first
} else {
uniseq.second to 0
}
}
fun sendMessageWithMsgId(
contact: Contact,
message: ArrayList<MsgElement>,
callback: IOperateCallback
): Pair<Long, Int> {
val uniseq = generateMsgId(contact.chatType)
val nonMsg: Boolean = message.isEmpty()
return if (!nonMsg) {
val service = QRoute.api(IMsgService::class.java)
if(callback is MsgSvc.MessageCallback) {
callback.msgHash = uniseq.first
}
service.sendMsg(
contact,
uniseq.second,
message,
callback
)
uniseq.second to uniseq.first
} else {
0L to 0
}
}
suspend fun generateContact(chatType: Int, id: String, subId: String = ""): Contact {
val peerId = if (MsgConstant.KCHATTYPEC2C == chatType) {
val peerId = if (MsgConstant.KCHATTYPEC2C == chatType || MsgConstant.KCHATTYPETEMPC2CFROMGROUP == chatType) {
ContactHelper.getUidByUinAsync(id.toLong())
} else id
return Contact(chatType, peerId, subId)
@ -107,6 +205,7 @@ internal object MessageHelper {
val key = when (chatType) {
MsgConstant.KCHATTYPEGROUP -> "grp$msgId"
MsgConstant.KCHATTYPEC2C -> "c2c$msgId"
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> "tmpgrp$msgId"
else -> error("不支持的消息来源类型 | generateMsgIdHash: $chatType")
}
return abs(key.hashCode())

View File

@ -29,6 +29,9 @@ interface MessageMappingDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(mapping: MessageMapping)
@Query("UPDATE message_mapping SET msgSeq = :msgSeq WHERE msgHashId = :hash")
fun updateMsgSeqByMsgHash(hash: Int, msgSeq: Int)
@Query("DELETE FROM message_mapping WHERE msgHashId = :hash")
fun deleteByMsgHash(hash: Int)

View File

@ -62,11 +62,17 @@ internal object HTTPServer {
weatherAction()
otherAction()
guildAction()
testAction()
requestRouter()
if (ShamrockConfig.isDev()) {
qsign()
obtainProtocolData()
}
}
// intercept(ApplicationCallPipeline.Plugins) {
// call.response.headers.appendIfAbsent("Content-Type", ContentType.Application.Json.toString())
// }
}
private fun ApplicationEngineEnvironmentBuilder.configSSL() {

View File

@ -18,6 +18,7 @@ internal object ActionManager {
arrayOf(
// Framework Info
TestHandler, GetLatestEvents, GetSupportedActions, GetStatus, GetVersionInfo, GetSelfInfo, GetLoginInfo,
SwitchAccount,
// UserActions
GetProfileCard, GetFriendList, SendLike, GetUid, GetUinByUid, ScanQRCode, SetProfileCard,
@ -29,16 +30,21 @@ internal object ActionManager {
// GroupActions
ModifyTroopName, LeaveTroop, KickTroopMember, BanTroopMember, SetGroupWholeBan, SetGroupAdmin,
ModifyTroopMemberName, SetGroupUnique, GetTroopHonor, GroupPoke,
ModifyTroopMemberName, SetGroupUnique, GetTroopHonor, GroupPoke, SetEssenceMessage, DeleteEssenceMessage,
GetGroupSystemMsg, GetProhibitedMemberList,
// MSG ACTIONS
SendMessage, DeleteMessage, GetMsg, GetForwardMsg, SendGroupForwardMsg, SendGroupMessage, SendPrivateMessage,
ClearMsgs, GetHistoryMsg, GetGroupMsgHistory, SendPrivateForwardMsg,
// RESOURCE ACTION
GetRecord, GetImage, UploadGroupFile, CreateGroupFileFolder, DeleteGroupFolder,
DeleteGroupFile, GetGroupFileSystemInfo, GetGroupRootFiles, GetGroupSubFiles,
GetGroupFileUrl, UploadPrivateFile,
//REQUEST ACTION
SetFriendAddRequest, SetGroupAddRequest,
// GUILD
GetGuildServiceProfile,
@ -97,16 +103,16 @@ internal abstract class IActionHandler {
return failed(Status.BadParam, why, echo)
}
protected fun error(why: String, echo: JsonElement): String {
return failed(Status.InternalHandlerError, why, echo)
protected fun error(why: String, echo: JsonElement, arrayResult: Boolean = false): String {
return failed(Status.InternalHandlerError, why, echo, arrayResult)
}
protected fun logic(why: String, echo: JsonElement): String {
return failed(Status.LogicError, why, echo)
protected fun logic(why: String, echo: JsonElement, arraayResult: Boolean = false): String {
return failed(Status.LogicError, why, echo, arraayResult)
}
protected fun failed(status: Status, msg: String, echo: JsonElement): String {
return resultToString(false, status, EmptyObject, msg, echo = echo)
protected fun failed(status: Status, msg: String, echo: JsonElement, arrResult: Boolean = false): String {
return resultToString(false, status, if (arrResult) EmptyJsonArray else EmptyJsonObject, msg, echo = echo)
}
}

View File

@ -0,0 +1,32 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
internal object ClearMsgs: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val msgType = session.getString("message_type")
val peerId = session.getString(if (msgType == "group") "group_id" else "user_id")
return invoke(msgType, peerId, session.echo)
}
suspend operator fun invoke(
msgType: String,
peerId: String,
echo: JsonElement = EmptyJsonString
): String {
val chatType = MessageHelper.obtainMessageTypeByDetailType(msgType)
val contact = MessageHelper.generateContact(chatType, peerId, "")
NTServiceFetcher.kernelService.wrapperSession.msgService.clearMsgRecords(contact, null)
return ok(echo = echo)
}
override val requiredParams: Array<String>
get() = arrayOf("message_type")
override fun path(): String = "clear_msgs"
}

View File

@ -0,0 +1,35 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
internal object DeleteEssenceMessage: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val messageId = session.getInt("message_id")
return invoke(messageId, session.echo)
}
suspend operator fun invoke(messageId: Int, echo: JsonElement = EmptyJsonString): String {
val msg = MsgSvc.getMsg(messageId).onFailure {
return logic("Obtain msg failed, please check your msg_id.", echo)
}.getOrThrow()
val (success, tip) = GroupSvc.deleteEssenceMessage(
if (msg.chatType == MsgConstant.KCHATTYPEGROUP) msg.peerUin else 0,
msg.msgSeq,
msg.msgRandom
)
return if (success) {
ok("成功", echo)
} else {
logic(tip, echo)
}
}
override val alias: Array<String> = arrayOf("delete_essence_message")
override fun path(): String = "delete_essence_msg"
}

View File

@ -14,10 +14,30 @@ internal object DeleteGroupFile: IActionHandler() {
return invoke(groupId, fileId, busid, session.echo)
}
/*
suspend operator fun invoke(
groupId: String,
fileId: String,
bizId: Int,
echo: JsonElement = EmptyJsonString
): String {
val result = FileSvc.deleteGroupFile(groupId, bizId, fileId)
if(result.isFailure) {
return error(result.exceptionOrNull()?.message ?: "删除群文件失败", echo)
}
val commonResult = result.getOrThrow()
if (commonResult.first != 0 || commonResult.second.retCode != 0) {
return error(commonResult.second.clientWording, echo)
}
return ok("成功", echo)
}
*/
operator fun invoke(groupId: String, fileId: String, bizId: Int, echo: JsonElement = EmptyJsonString): String {
FileSvc.deleteGroupFile(groupId, bizId, fileId)
return ok("成功", echo)
}
override val requiredParams: Array<String> = arrayOf("group_id", "file_id", "busid")
override fun path(): String = "delete_group_file"
}

View File

@ -1,5 +1,6 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import android.util.Base64
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.shamrock.remote.action.ActionSession
@ -13,35 +14,72 @@ import moe.fuqiuluo.shamrock.utils.MD5
internal object DownloadFile: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val url = session.getString("url")
val url = session.getStringOrNull("url")
val name = session.getStringOrNull("name")
val b64 = session.getStringOrNull("base64")
val threadCnt = session.getIntOrNull("thread_cnt") ?: 3
val headers = if (session.isArray("headers")) {
val headers = if (session.has("headers")) (if (session.isArray("headers")) {
session.getArray("headers").map {
it.asString
}
} else {
session.getString("headers").split("\r\n")
}
return invoke(url, threadCnt, headers, session.echo)
}) else emptyList()
return invoke(url, b64, threadCnt, headers, name, session.echo)
}
suspend operator fun invoke(
url: String,
url: String?,
base64: String?,
threadCnt: Int,
headers: List<String>,
name: String?,
echo: JsonElement = EmptyJsonString
): String {
val headerMap = mutableMapOf(
"User-Agent" to "Shamrock"
)
headers.forEach {
val pair = it.split("=")
if (pair.size >= 2) {
val (k, v) = pair
headerMap[k] = v
if (url != null) {
val headerMap = mutableMapOf(
"User-Agent" to "Shamrock"
)
headers.forEach {
val pair = it.split("=")
if (pair.size >= 2) {
val (k, v) = pair
headerMap[k] = v
}
}
return invoke(url, threadCnt, headerMap, echo)
} else if (base64 != null) {
return invoke(base64, name, echo)
} else {
return noParam("url/base64", echo)
}
return invoke(url, threadCnt, headerMap, echo)
}
operator fun invoke(
base64: String,
name: String?,
echo: JsonElement
): String {
kotlin.runCatching {
val bytes = Base64.decode(base64, Base64.DEFAULT)
FileUtils.getTmpFile("cache").also {
it.writeBytes(bytes)
}
}.onSuccess {
val tmp = if (name == null)
FileUtils.renameByMd5(it)
else it.parentFile!!.resolve(name).also { target ->
it.renameTo(target)
it.delete()
}
return ok(data = DownloadResult(
file = tmp.absolutePath,
md5 = MD5.genFileMd5Hex(tmp.absolutePath)
), msg = "成功", echo = echo)
}.onFailure {
return logic("Base64格式错误", echo)
}
return logic("未知错误", echo)
}
suspend operator fun invoke(
@ -70,8 +108,6 @@ internal object DownloadFile: IActionHandler() {
}
}
override val requiredParams: Array<String> = arrayOf("url")
override fun path(): String = "download_file"
@Serializable

View File

@ -17,7 +17,7 @@ internal object GetCSRF: IActionHandler() {
suspend operator fun invoke(domain: String, echo: JsonElement = EmptyJsonString): String {
val uin = TicketSvc.getUin()
val pskey = TicketSvc.getPSKey(uin, domain)
?: return invoke()
?: return invoke(echo)
return ok(Credentials(bkn = TicketSvc.getCSRF(pskey)), echo)
}

View File

@ -1,20 +1,61 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
import moe.fuqiuluo.qqinterface.servlet.msg.convert.MessageConvert
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import moe.fuqiuluo.shamrock.remote.service.data.MessageDetail
import moe.fuqiuluo.shamrock.remote.service.data.MessageSender
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
internal object GetForwardMsg: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val id = session.getString("id")
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val msgService = sessionService.msgService
return error("不支持实现请提交ISSUE", session.echo)
return invoke(id, session.echo)
}
suspend operator fun invoke(
resId: String,
echo: JsonElement = EmptyJsonString
): String {
val result = MsgSvc.getMultiMsg(resId)
if (result.isFailure) {
return logic(result.exceptionOrNull().toString(), echo)
}
return ok(data = GetForwardMsgResult(result.getOrThrow().map { msg ->
val msgHash = MessageHelper.generateMsgIdHash(msg.chatType, msg.msgId)
MessageDetail(
time = msg.msgTime.toInt(),
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
msgId = msgHash,
realId = msg.msgSeq.toInt(),
sender = MessageSender(
msg.senderUin, msg.sendNickName
.ifBlank { msg.sendMemberName }
.ifBlank { msg.sendRemarkName }
.ifBlank { msg.peerName }, "unknown", 0, msg.senderUid
),
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
it.toJson()
},
peerId = msg.peerUin,
groupId = if (msg.chatType == MsgConstant.KCHATTYPEGROUP) msg.peerUin else 0,
targetId = if (msg.chatType != MsgConstant.KCHATTYPEGROUP) msg.peerUin else 0
)
}), echo = echo)
}
@Serializable
data class GetForwardMsgResult(
@SerialName("messages") val msgs: List<MessageDetail>
)
override val requiredParams: Array<String> = arrayOf("id")
override fun path(): String = "get_forward_msg"

View File

@ -16,7 +16,7 @@ internal object GetFriendList: IActionHandler() {
suspend operator fun invoke(refresh: Boolean, echo: JsonElement = EmptyJsonString): String {
val friendList = FriendSvc.getFriendList(refresh).onFailure {
return error(it.message ?: "unknown error", echo)
return error(it.message ?: "unknown error", echo, arrayResult = true)
}.getOrThrow()
return ok(friendList.map { friend ->
FriendEntry(

View File

@ -0,0 +1,24 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import moe.fuqiuluo.shamrock.helper.db.MessageDB
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
internal object GetGroupMsgHistory: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val groupId = session.getString("group_id")
val cnt = session.getIntOrNull("count") ?: 20
val startId = session.getIntOrNull("message_seq")?.let {
if (it == 0) return@let 0L
MessageDB.getInstance()
.messageMappingDao()
.queryByMsgHashId(it)?.qqMsgId
} ?: 0L
return GetHistoryMsg("group", groupId, cnt, startId, session.echo)
}
override val requiredParams: Array<String>
get() = arrayOf("group_id")
override fun path(): String = "get_group_msg_history"
}

View File

@ -13,7 +13,10 @@ internal object GetGroupRootFiles: IActionHandler() {
}
suspend operator fun invoke(groupId: String, echo: JsonElement = EmptyJsonString): String {
return ok(FileSvc.getGroupRootFiles(groupId.toLong()), echo = echo)
FileSvc.getGroupRootFiles(groupId.toLong()).onSuccess {
return ok(it, echo = echo)
}.getOrNull()
return error(why = "获取失败,请查看日志", echo = echo)
}
override val requiredParams: Array<String> = arrayOf("group_id")

View File

@ -14,7 +14,10 @@ internal object GetGroupSubFiles: IActionHandler() {
}
suspend operator fun invoke(groupId: String, folderId: String, echo: JsonElement = EmptyJsonString): String {
return ok(FileSvc.getGroupFiles(groupId.toLong(), folderId), echo)
FileSvc.getGroupFiles(groupId.toLong(), folderId).onSuccess {
return ok(it, echo = echo)
}.getOrNull()
return error(why = "获取失败,请查看日志", echo = echo)
}
override val requiredParams: Array<String> = arrayOf("group_id", "folder_id")

View File

@ -0,0 +1,66 @@
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.remote.service.data.GroupRequest
import moe.fuqiuluo.shamrock.remote.service.data.GroupSystemMessage
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
internal object GetGroupSystemMsg: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
return invoke(session.echo)
}
suspend operator fun invoke(echo: JsonElement = EmptyJsonString): String {
val list = GroupSvc.requestGroupSystemMsgNew(20, 0, 0)
val msgs = GroupSystemMessage(
invited = mutableListOf(),
join = mutableListOf()
)
list?.forEach {
when(it.msg.group_msg_type.get()) {
22, 1 -> {
// join 进群消息
msgs.join += GroupRequest (
msgSeq = it.msg_seq.get(),
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(),
actor = it.msg.actor_uin.get(),
requesterUin = it.req_uin.get(),
requesterNick = it.msg.req_uin_nick.get(),
message = it.msg.msg_additional.get(),
flag = "${it.msg_seq.get()};${it.msg.group_code.get()};${it.req_uin.get()}"
)
}
2 -> {
// invite 别人邀请我
msgs.invited += GroupRequest (
msgSeq = it.msg_seq.get(),
invitorUin = null,
invitorNick = null,
groupId = it.msg.group_code.get(),
groupName = it.msg.group_name.get(),
checked = it.msg.msg_decided.get().isNotBlank(),
actor = it.msg.actor_uin.get(),
requesterUin = 0,
requesterNick = "",
message = it.msg.msg_additional.get(),
flag = "${it.msg_seq.get()};${it.msg.group_code.get()};${it.req_uin.get()}"
)
}
else -> {}
}
}
return ok(msgs, echo = echo)
}
override val requiredParams: Array<String> = arrayOf("group_id", "folder_id")
override fun path(): String = "get_group_files_by_folder"
}

View File

@ -0,0 +1,120 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
import moe.fuqiuluo.qqinterface.servlet.msg.convert.MessageConvert
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.helper.db.MessageDB
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.remote.service.data.MessageDetail
import moe.fuqiuluo.shamrock.remote.service.data.MessageSender
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import java.util.ArrayList
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
internal object GetHistoryMsg: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val msgType = session.getString("message_type")
val peerId = session.getString(if (msgType == "group") "group_id" else "user_id")
val cnt = session.getIntOrNull("count") ?: 20
val startId = session.getIntOrNull("message_seq")?.let {
if (it == 0) return@let 0L
MessageDB.getInstance()
.messageMappingDao()
.queryByMsgHashId(it)?.qqMsgId
} ?: 0L
return invoke(msgType, peerId, cnt, startId, echo = session.echo)
}
suspend operator fun invoke(
msgType: String,
peerId: String,
cnt: Int,
startMsgId: Long = 0,
echo: JsonElement = EmptyJsonString
): String {
val msgService = NTServiceFetcher.kernelService.wrapperSession.msgService
val chatType = MessageHelper.obtainMessageTypeByDetailType(msgType)
val contact = MessageHelper.generateContact(chatType, peerId)
val result = suspendCoroutine {
msgService.getMsgs(contact, startMsgId, cnt, true) { code, why, msgs ->
it.resume(GetMsgResult(code, why, msgs))
}
}
if (result.code != 0) {
return logic(result.msg ?: "获取历史消息失败", echo = echo)
}
val msgList = ArrayList<MessageDetail>().apply {
addAll(result.data!!.map { msg ->
val msgHash = MessageHelper.generateMsgIdHash(msg.chatType, msg.msgId)
MessageDetail(
time = msg.msgTime.toInt(),
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
msgId = msgHash,
realId = msg.msgSeq.toInt(),
sender = MessageSender(
msg.senderUin, msg.sendNickName, "unknown", 0, msg.senderUid
),
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
it.toJson()
},
peerId = msg.peerUin,
groupId = if (msg.chatType == MsgConstant.KCHATTYPEGROUP) msg.peerUin else 0,
targetId = if (msg.chatType != MsgConstant.KCHATTYPEGROUP) msg.peerUin else 0
)
})
if (startMsgId != 0L) {
val msg = MsgSvc.getMsgByQMsgId(chatType, peerId, startMsgId).onFailure {
return logic("Obtain msg failed, please check your msg_id.", echo)
}.getOrThrow()
val seq = msg.clientSeq.toInt()
add(MessageDetail(
time = msg.msgTime.toInt(),
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
msgId = MessageHelper.generateMsgIdHash(msg.chatType, msg.msgId),
realId = seq,
sender = MessageSender(
msg.senderUin, msg.sendNickName
.ifBlank { msg.sendMemberName }
.ifBlank { msg.sendRemarkName }
.ifBlank { msg.peerName }, "unknown", 0, msg.senderUid
),
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
it.toJson()
},
peerId = msg.peerUin,
groupId = if (msg.chatType == MsgConstant.KCHATTYPEGROUP) msg.peerUin else 0,
targetId = if (msg.chatType != MsgConstant.KCHATTYPEGROUP) msg.peerUin else 0
))
}
}
return ok(data = GetHistoryMsgResult(msgList), echo = echo)
}
override val requiredParams: Array<String>
get() = arrayOf("message_type")
override fun path(): String = "get_history_msg"
@Serializable
data class GetHistoryMsgResult(
@SerialName("messages") val msgs: List<MessageDetail>
)
data class GetMsgResult(
val code: Int,
val msg: String?,
val data: ArrayList<MsgRecord>?
)
}

View File

@ -22,14 +22,17 @@ internal object GetMsg: IActionHandler() {
val msg = MsgSvc.getMsg(msgHash).onFailure {
return logic("Obtain msg failed, please check your msg_id.", echo)
}.getOrThrow()
val seq = msg.clientSeq.toInt()
val seq = msg.msgSeq.toInt()
return ok(MessageDetail(
time = msg.msgTime.toInt(),
msgType = MessageHelper.obtainDetailTypeByMsgType(msg.chatType),
msgId = msgHash,
realId = seq,
sender = MessageSender(
msg.senderUin, msg.sendNickName, "unknown", 0, msg.senderUid
msg.senderUin, msg.sendNickName
.ifBlank { msg.sendMemberName }
.ifBlank { msg.sendRemarkName }
.ifBlank { msg.peerName }, "unknown", 0, msg.senderUid
),
message = MessageConvert.convertMessageRecordToMsgSegment(msg).map {
it.toJson()

View File

@ -15,7 +15,7 @@ internal object GetOnlineClients: IActionHandler() {
suspend operator fun invoke(echo: JsonElement = EmptyJsonString): String {
val clients = QSafeSvc.getOnlineClients()
?: return logic("获取在线设备信息失败", echo)
?: return logic("获取在线设备信息失败", echo, arraayResult = true)
return ok(clients.map {
DevInfo(it.iAppId, it.strDeviceName, it.strDeviceTypeInfo, it.iLoginTime,
it.iLoginPlatform, it.strLoginLocation

View File

@ -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 GetProhibitedMemberList: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val groupCode = session.getLong("group_id")
return invoke(groupCode, session.echo)
}
suspend operator fun invoke(
groupCode: Long,
echo: JsonElement = EmptyJsonString
): String {
val result = GroupSvc.getProhibitedMemberList(groupCode)
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_prohibited_member_list"
}

View File

@ -16,7 +16,7 @@ internal object GetTroopList : IActionHandler() {
suspend operator fun invoke(refresh: Boolean, echo: JsonElement = EmptyJsonString): String {
val troopList = arrayListOf<SimpleTroopInfo>()
GroupSvc.getGroupList(refresh).onFailure {
return error(it.message ?: "unknown error", echo)
return error(it.message ?: "unknown error", echo, arrayResult = true)
}.onSuccess { troops ->
troops.forEach { groupInfo ->
if (groupInfo.troopcode.isNullOrEmpty()) return@forEach

View File

@ -23,7 +23,7 @@ internal object GetTroopMemberList : IActionHandler() {
echo: JsonElement = EmptyJsonString
): String {
val memberList = GroupSvc.getGroupMemberList(groupId, refresh).onFailure {
return error(it.message ?: "unknown error", echo)
return error(it.message ?: "unknown error", echo, arrayResult = true)
}.getOrThrow()
return ok(arrayListOf<SimpleTroopMemberInfo>().apply {
@ -53,7 +53,6 @@ internal object GetTroopMemberList : IActionHandler() {
role = when {
GroupSvc.getOwner(groupId)
.toString() == info.memberuin -> MemberRole.Owner
info.memberuin.toLong() in GroupSvc.getAdminList(groupId) -> MemberRole.Admin
else -> MemberRole.Member
},

View File

@ -20,7 +20,7 @@ internal object GetVersionInfo : IActionHandler() {
appVersion = ShamrockVersion,
impl = "shamrock",
version = ShamrockVersion,
onebotVersion = "12",
onebotVersion = "11",
),
echo = echo
)

View File

@ -0,0 +1,233 @@
@file:OptIn(DelicateCoroutinesApi::class)
package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MultiMsgInfo
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.qqinterface.servlet.msg.convert.toSegments
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.tools.EmptyJsonObject
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.tools.asInt
import moe.fuqiuluo.shamrock.tools.asJsonObject
import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.asStringOrNull
import moe.fuqiuluo.shamrock.tools.json
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
/**
* 合并转发消息节点数据类
*/
sealed interface ForwardMsgNode {
class MessageIdNode(
val id: Int
): ForwardMsgNode
open class MessageNode(
val name: String,
val content: JsonElement?
): ForwardMsgNode
object EmptyNode: MessageNode("", null)
}
/**
* 私聊合并转发
*/
internal object SendPrivateForwardMsg: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val groupId = session.getString("user_id")
if (session.isArray("messages")) {
val messages = session.getArray("messages")
return invoke(messages, groupId, session.echo)
}
return logic("未知格式合并转发消息", session.echo)
}
suspend operator fun invoke(
message: JsonArray,
userId: String,
echo: JsonElement = EmptyJsonString
): String {
kotlin.runCatching {
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val msgService = sessionService.msgService
val selfUin = TicketSvc.getUin()
val msgs = message.map {
if (it.asJsonObject["type"].asStringOrNull != "node") return@map ForwardMsgNode.EmptyNode // 过滤非node类型消息段
it.asJsonObject["data"].asJsonObject.let { data ->
if (data.containsKey("content"))
ForwardMsgNode.MessageNode(
name = data["name"].asStringOrNull ?: "",
content = data["content"]
)
else ForwardMsgNode.MessageIdNode(data["id"].asInt)
}
}.map {
if (it is ForwardMsgNode.MessageIdNode) {
val recordResult = MsgSvc.getMsg(it.id)
if (recordResult.isFailure) {
ForwardMsgNode.EmptyNode
} else {
val record = recordResult.getOrThrow()
ForwardMsgNode.MessageNode(
name = record.sendMemberName
.ifBlank { record.sendNickName }
.ifBlank { record.sendRemarkName }
.ifBlank { record.peerName },
content = record.toSegments().map { segment ->
segment.toJson()
}.json
)
}
} else {
it as ForwardMsgNode.MessageNode
}
}.filter {
it.content != null
}
val multiNodes = msgs.map { node ->
suspendCoroutine {
GlobalScope.launch {
var msgId: Long = 0
msgId = MessageHelper.sendMessageWithMsgId(MsgConstant.KCHATTYPEC2C, selfUin, node.content!!.let { msg ->
if (msg is JsonArray) msg else MessageHelper.decodeCQCode(msg.asString)
}, { code, why ->
if (code != 0) {
LogCenter.log("合并转发消息节点消息发送失败:$code($why)", Level.WARN)
}
it.resume(node.name to msgId)
}).first
}
}
}
val from = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, selfUin)
val to = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, userId)
msgService.multiForwardMsg(ArrayList<MultiMsgInfo>().apply {
multiNodes.forEach { add(MultiMsgInfo(it.second, it.first)) }
}.also { it.reverse() }, from, to) { code, why ->
if (code != 0)
LogCenter.log("合并转发消息:$code($why)", Level.WARN)
}
return ok(data = EmptyJsonObject, echo = echo)
}.onFailure {
return error("error: $it", echo)
}
return logic("合并转发消息失败(unknown error)", echo)
}
override val requiredParams: Array<String> = arrayOf("user_id")
override fun path(): String = "send_private_forward_msg"
}
/**
* 群聊合并转发
*/
internal object SendGroupForwardMsg: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val groupId = session.getString("group_id")
if (session.isArray("messages")) {
val messages = session.getArray("messages")
return invoke(messages, groupId, session.echo)
}
return logic("未知格式合并转发消息", session.echo)
}
suspend operator fun invoke(
message: JsonArray,
groupId: String,
echo: JsonElement = EmptyJsonString
): String {
kotlin.runCatching {
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val msgService = sessionService.msgService
val selfUin = TicketSvc.getUin()
val msgs = message.map {
if (it.asJsonObject["type"].asStringOrNull != "node") return@map ForwardMsgNode.EmptyNode // 过滤非node类型消息段
it.asJsonObject["data"].asJsonObject.let { data ->
if (data.containsKey("content"))
ForwardMsgNode.MessageNode(
name = data["name"].asStringOrNull ?: "",
content = data["content"]
)
else ForwardMsgNode.MessageIdNode(data["id"].asInt)
}
}.map {
if (it is ForwardMsgNode.MessageIdNode) {
val recordResult = MsgSvc.getMsg(it.id)
if (recordResult.isFailure) {
ForwardMsgNode.EmptyNode
} else {
val record = recordResult.getOrThrow()
ForwardMsgNode.MessageNode(
name = record.sendMemberName
.ifBlank { record.sendNickName }
.ifBlank { record.sendRemarkName }
.ifBlank { record.peerName },
content = record.toSegments().map { segment ->
segment.toJson()
}.json
)
}
} else {
it as ForwardMsgNode.MessageNode
}
}.filter {
it.content != null
}
val multiNodes = msgs.map { node ->
suspendCoroutine {
GlobalScope.launch {
var msgId: Long = 0
msgId = MessageHelper.sendMessageWithMsgId(MsgConstant.KCHATTYPEC2C, selfUin, node.content!!.let { msg ->
if (msg is JsonArray) msg else MessageHelper.decodeCQCode(msg.asString)
}, { code, why ->
if (code != 0) {
LogCenter.log("合并转发消息节点消息发送失败:$code($why)", Level.WARN)
}
it.resume(node.name to msgId)
}).first
}
}
}
val from = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, selfUin)
val to = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupId)
msgService.multiForwardMsg(ArrayList<MultiMsgInfo>().apply {
multiNodes.forEach { add(MultiMsgInfo(it.second, it.first)) }
}.also { it.reverse() }, from, to) { code, why ->
if (code != 0)
LogCenter.log("合并转发消息:$code($why)", Level.WARN)
}
return ok(data = EmptyJsonObject, echo = echo)
}.onFailure {
return error("error: $it", echo)
}
return logic("合并转发消息失败(unknown error)", echo)
}
override val requiredParams: Array<String> = arrayOf("group_id")
override fun path(): String = "send_group_forward_msg"
}

View File

@ -1,41 +0,0 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.tools.asInt
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
import moe.fuqiuluo.qqinterface.servlet.msg.LongMsgHelper
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
internal object SendGroupForwardMsg: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val groupId = session.getLong("group_id")
val hashList = session.getArrayOrNull("seqs")?.map { it.asInt }
if (hashList != null) {
val msgs = hashList.map {
MsgSvc.getMsg(it).getOrNull()
}
val resId = LongMsgHelper.uploadGroupMsg(groupId.toString(), msgs.filterNotNull())
return ok(mapOf("res_id" to resId), session.echo)
}
return "xxx"
}
operator fun invoke(msgs: List<MsgRecord>, echo: JsonElement = EmptyJsonString): String {
if (msgs.isEmpty()) {
return logic("消息为空", echo)
} else if (msgs.size > 100) {
return logic("消息数量过多", echo)
}
TODO()
}
override fun path(): String = "send_group_forward_msg"
}

View File

@ -10,7 +10,7 @@ internal object SendGroupMessage: IActionHandler() {
return if (session.isString("message")) {
val autoEscape = session.getBooleanOrDefault("auto_escape", false)
val message = session.getString("message")
SendMessage(MsgConstant.KCHATTYPEGROUP, groupId, message, autoEscape, session.echo)
SendMessage(MsgConstant.KCHATTYPEGROUP, groupId, message, autoEscape, echo = session.echo)
} else {
val message = session.getArray("message")
SendMessage(MsgConstant.KCHATTYPEGROUP, groupId, message, session.echo)

View File

@ -19,13 +19,13 @@ internal object SendMessage: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val detailType = session.getStringOrNull("detail_type") ?: session.getStringOrNull("message_type")
try {
val chatType = detailType?.let {
var chatType = detailType?.let {
MessageHelper.obtainMessageTypeByDetailType(it)
} ?: run {
if (session.has("group_id")) {
MsgConstant.KCHATTYPEGROUP
} else if (session.has("user_id")) {
if (session.has("user_id")) {
MsgConstant.KCHATTYPEC2C
} else if (session.has("group_id")) {
MsgConstant.KCHATTYPEGROUP
} else {
return noParam("detail_type/message_type", session.echo)
}
@ -35,13 +35,21 @@ internal object SendMessage: IActionHandler() {
MsgConstant.KCHATTYPEC2C -> session.getStringOrNull("user_id") ?: return noParam("user_id", session.echo)
else -> error("unknown chat type: $chatType")
}
var fromId = peerId
if (chatType == MsgConstant.KCHATTYPEC2C) {
val groupId = session.getStringOrNull("group_id")
if (groupId != null) {
chatType = MsgConstant.KCHATTYPETEMPC2CFROMGROUP
fromId = groupId
}
}
return if (session.isString("message")) {
val autoEscape = session.getBooleanOrDefault("auto_escape", false)
val message = session.getString("message")
invoke(chatType, peerId, message, autoEscape, session.echo)
invoke(chatType, peerId, message, autoEscape, echo = session.echo, fromId = fromId)
} else {
val message = session.getArray("message")
invoke(chatType, peerId, message, session.echo)
invoke(chatType, peerId, message, session.echo, fromId = fromId)
}
} catch (e: ParamsException) {
return noParam(e.message!!, session.echo)
@ -56,39 +64,46 @@ internal object SendMessage: IActionHandler() {
peerId: String,
message: String,
autoEscape: Boolean,
fromId: String = peerId,
echo: JsonElement = EmptyJsonString
): String {
//if (!ContactHelper.checkContactAvailable(chatType, peerId)) {
// return logic("contact is not found", echo = echo)
//}
val result = if (autoEscape) {
MsgSvc.sendToAio(chatType, peerId, arrayListOf(message).json)
MsgSvc.sendToAio(chatType, peerId, arrayListOf(message).json, fromId = fromId)
} else {
val msg = MessageHelper.decodeCQCode(message)
if (msg.isEmpty()) {
LogCenter.log("CQ码不合法", Level.WARN)
return logic("CQCode is illegal", echo)
} else {
MsgSvc.sendToAio(chatType, peerId, msg)
MsgSvc.sendToAio(chatType, peerId, msg, fromId = fromId)
}
}
if (result.first <= 0) {
return logic("send message failed", echo = echo)
}
return ok(MessageResult(
msgId = result.second,
time = result.first * 0.001
), echo)
time = (result.first * 0.001).toLong()
), echo = echo)
}
// 消息段格式消息
suspend operator fun invoke(
chatType: Int, peerId: String, message: JsonArray, echo: JsonElement = EmptyJsonString
chatType: Int, peerId: String, message: JsonArray, echo: JsonElement = EmptyJsonString, fromId: String = peerId
): String {
//if (!ContactHelper.checkContactAvailable(chatType, peerId)) {
// return logic("contact is not found", echo = echo)
//}
val result = MsgSvc.sendToAio(chatType, peerId, message)
val result = MsgSvc.sendToAio(chatType, peerId, message, fromId = fromId)
if (result.first <= 0) {
return logic("send message failed", echo = echo)
}
return ok(MessageResult(
msgId = result.second,
time = result.first * 0.001
time = (result.first * 0.001).toLong()
), echo)
}

View File

@ -7,13 +7,28 @@ import moe.fuqiuluo.shamrock.remote.action.IActionHandler
internal object SendPrivateMessage: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val userId = session.getString("user_id")
val groupId = session.getStringOrNull("group_id")
val chatTYpe = if (groupId == null) MsgConstant.KCHATTYPEC2C else MsgConstant.KCHATTYPETEMPC2CFROMGROUP
return if (session.isString("message")) {
val autoEscape = session.getBooleanOrDefault("auto_escape", false)
val message = session.getString("message")
SendMessage(MsgConstant.KCHATTYPEC2C, userId, message, autoEscape, session.echo)
SendMessage.invoke(
chatType = chatTYpe,
peerId = userId,
message = message,
autoEscape = autoEscape,
echo = session.echo,
fromId = groupId ?: userId
)
} else {
val message = session.getArray("message")
SendMessage(MsgConstant.KCHATTYPEC2C, userId, message, session.echo)
SendMessage(
chatType = chatTYpe,
peerId = userId,
message = message,
echo = session.echo,
fromId = groupId ?: userId
)
}
}

View File

@ -0,0 +1,36 @@
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 SetEssenceMessage: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val messageId = session.getInt("message_id")
return invoke(messageId, session.echo)
}
suspend operator fun invoke(messageId: Int, echo: JsonElement = EmptyJsonString): String {
val msg = MsgSvc.getMsg(messageId).onFailure {
return logic("Obtain msg failed, please check your msg_id.", echo)
}.getOrThrow()
val (success, tip) = GroupSvc.setEssenceMessage(
if (msg.chatType == MsgConstant.KCHATTYPEGROUP) msg.peerUin else 0,
msg.msgSeq,
msg.msgRandom
)
return if (success) {
ok("成功", echo)
} else {
logic(tip, echo)
}
}
override val alias: Array<String> = arrayOf("set_essence_message")
override fun path(): String = "set_essence_msg"
}

View File

@ -0,0 +1,36 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.FriendSvc
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 SetFriendAddRequest: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val flag = session.getString("flag")
val approve = session.getBoolean("approve")
val remark = session.getStringOrNull("remark")
val notSeen = session.getBoolean("notSeen")
return invoke(flag, approve, remark, notSeen, session.echo)
}
operator fun invoke(flag: String, approve: Boolean? = true, remark: String? = "", notSeen: Boolean? = false, echo: JsonElement = EmptyJsonString): String {
val flags = flag.split(";")
val ts = flags[0].toLong()
// val src = flags[1].toInt()
// val subSrc = flags[2].toInt()
val applier = flags[3].toLong()
return try {
FriendSvc.requestFriendRequest(ts, applier, remark ?: "", approve, notSeen)
ok("成功", echo)
} catch (err: Throwable) {
err.printStackTrace()
error("失败:${err.message}", echo)
}
}
override fun path(): String = "set_friend_add_request"
}

View File

@ -0,0 +1,39 @@
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 SetGroupAddRequest: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val flag = session.getString("flag")
val approve = session.getBoolean("approve")
val remark = session.getStringOrNull("reason")
val notSeen = session.getBoolean("notSeen")
val subType = session.getString("sub_type")
return invoke(flag, approve, subType, remark, notSeen, session.echo)
}
suspend operator fun invoke(flag: String, approve: Boolean? = true, subType: String, remark: String? = "", notSeen: Boolean? = false, echo: JsonElement = EmptyJsonString): String {
val flags = flag.split(";")
val ts = flags[0].toLong()
val groupCode = flags[1].toLong()
val uin = flags[2].toLong()
return try {
val result = GroupSvc.requestGroupRequest(ts, uin, groupCode, remark ?: "", approve, notSeen, subType)
if (result.isSuccess) {
ok(result.getOrNull(), echo)
} else {
logic(result.getOrNull() ?: "", echo)
}
} catch (err: Throwable) {
err.printStackTrace()
error("失败:${err.message}", echo)
}
}
override fun path(): String = "set_group_add_request"
}

View File

@ -15,8 +15,8 @@ internal object SetGroupUnique: IActionHandler() {
}
suspend operator fun invoke(groupId: String, userId: String, unique: String, echo: JsonElement = EmptyJsonString): String {
if (!GroupSvc.isAdmin(groupId)) {
return error("you are not admin", echo)
if (!GroupSvc.isOwner(groupId)) {
return error("you are not owner", echo)
}
GroupSvc.setGroupUniqueTitle(groupId, userId, unique)
return ok("成功", echo)

View File

@ -0,0 +1,35 @@
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.EmptyJsonString
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import mqq.app.MobileQQ
internal object SwitchAccount: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val userId = session.getString("user_id")
return invoke(userId, session.echo)
}
operator fun invoke(
userId: String,
echo: JsonElement = EmptyJsonString
): String {
val account = MobileQQ.getMobileQQ().allAccounts.firstOrNull { it.uin == userId }
?: return error("账号不存在", echo)
val runtime = AppRuntimeFetcher.appRuntime
val result = kotlin.runCatching {
runtime.switchAccount(account, null)
}
if (result.isFailure) {
return error(result.exceptionOrNull()?.message ?: "切换账号失败", echo)
}
return ok("切换成功", echo)
}
override val requiredParams: Array<String> = arrayOf("user_id")
override fun path(): String = "switch_account"
}

View File

@ -2,6 +2,7 @@ package moe.fuqiuluo.shamrock.remote.api
import com.tencent.mobileqq.profilecard.api.IProfileCardBlacklistApi
import com.tencent.mobileqq.qroute.QRoute
import io.ktor.http.ContentType
import io.ktor.server.application.call
import io.ktor.server.response.respondText
import io.ktor.server.routing.Routing
@ -17,16 +18,16 @@ import moe.fuqiuluo.shamrock.tools.getOrPost
fun Routing.friendAction() {
getOrPost("/get_stranger_info") {
val uin = fetchOrThrow("user_id")
call.respondText(GetStrangerInfo(uin))
call.respondText(GetStrangerInfo(uin), ContentType.Application.Json)
}
getOrPost("/get_friend_list") {
val refresh = fetchOrNull("refresh")?.toBooleanStrictOrNull() ?: false
call.respondText(GetFriendList(refresh))
call.respondText(GetFriendList(refresh), ContentType.Application.Json)
}
getOrPost("/is_blacklist_uin") {
val uin = fetchOrThrow("user_id")
call.respondText(IsBlackListUin(uin))
call.respondText(IsBlackListUin(uin), ContentType.Application.Json)
}
}

View File

@ -1,5 +1,6 @@
package moe.fuqiuluo.shamrock.remote.api
import io.ktor.http.ContentType
import moe.fuqiuluo.shamrock.helper.LogicException
import io.ktor.server.application.call
import io.ktor.server.response.respondText
@ -12,73 +13,83 @@ import moe.fuqiuluo.shamrock.tools.fetchOrThrow
import moe.fuqiuluo.shamrock.tools.getOrPost
fun Routing.troopAction() {
getOrPost("/get_prohibited_member_list") {
val groupId = fetchOrThrow("group_id").toLong()
call.respondText(GetProhibitedMemberList(groupId), ContentType.Application.Json)
}
getOrPost("/group_touch") {
val groupId = fetchOrThrow("group_id")
val userId = fetchOrThrow("user_id")
call.respondText(GroupPoke(groupId, userId))
call.respondText(GroupPoke(groupId, userId), ContentType.Application.Json)
}
getOrPost("/get_group_honor_info") {
val groupId = fetchOrThrow("group_id")
val refresh = fetchOrNull("refresh")?.toBooleanStrict() ?: false
call.respondText(GetTroopHonor(groupId, refresh))
val refresh = fetchOrNull("no_cache")?.toBooleanStrict()
?: fetchOrNull("refresh")?.toBooleanStrict() ?: false
call.respondText(GetTroopHonor(groupId, refresh), ContentType.Application.Json)
}
getOrPost("/get_group_member_list") {
val groupId = fetchOrThrow("group_id")
val refresh = fetchOrNull("refresh")?.toBooleanStrict() ?: false
call.respondText(GetTroopMemberList(groupId, refresh))
val refresh = fetchOrNull("no_cache")?.toBooleanStrict()
?: fetchOrNull("refresh")?.toBooleanStrict() ?: false
call.respondText(GetTroopMemberList(groupId, refresh), ContentType.Application.Json)
}
getOrPost("/get_group_member_info") {
val groupId = fetchOrThrow("group_id")
val userId = fetchOrThrow("user_id")
val refresh = fetchOrNull("no_cache")?.toBooleanStrict() ?: false
call.respondText(GetTroopMemberInfo(groupId, userId, refresh))
val refresh = fetchOrNull("no_cache")?.toBooleanStrict()
?: fetchOrNull("refresh")?.toBooleanStrict() ?: false
call.respondText(GetTroopMemberInfo(groupId, userId, refresh), ContentType.Application.Json)
}
getOrPost("/get_group_list") {
val refresh = fetchOrNull("refresh")?.toBooleanStrict() ?: true
call.respondText(GetTroopList(refresh))
val refresh = fetchOrNull("refresh")?.toBooleanStrict()
?: fetchOrNull("refresh")?.toBooleanStrict() ?: true
call.respondText(GetTroopList(refresh), ContentType.Application.Json)
}
getOrPost("/get_group_info") {
val groupId = fetchOrThrow("group_id")
val refresh = fetchOrNull("no_cache")?.toBooleanStrict() ?: false
call.respondText(GetTroopInfo(groupId, refresh))
val refresh = fetchOrNull("no_cache")?.toBooleanStrict()
?: fetchOrNull("refresh")?.toBooleanStrict() ?: false
call.respondText(GetTroopInfo(groupId, refresh), ContentType.Application.Json)
}
getOrPost("/set_group_special_title") {
val groupId = fetchOrThrow("group_id")
val userId = fetchOrThrow("user_id")
val title = fetchOrThrow("special_title")
call.respondText(SetGroupUnique(groupId, userId, title))
call.respondText(SetGroupUnique(groupId, userId, title), ContentType.Application.Json)
}
getOrPost("/set_group_name") {
val groupId = fetchOrThrow("group_id")
val card = fetchOrThrow("group_name")
call.respondText(ModifyTroopName(groupId, card))
call.respondText(ModifyTroopName(groupId, card), ContentType.Application.Json)
}
getOrPost("/set_group_card") {
val groupId = fetchOrThrow("group_id")
val userId = fetchOrThrow("user_id")
val card = fetchOrNull("card") ?: ""
call.respondText(ModifyTroopMemberName(groupId, userId, card))
call.respondText(ModifyTroopMemberName(groupId, userId, card), ContentType.Application.Json)
}
getOrPost("/set_group_admin") {
val groupId = fetchOrThrow("group_id") .toLong()
val userId = fetchOrThrow("user_id") .toLong()
val enable = fetchOrThrow("enable").toBooleanStrict()
call.respondText(SetGroupAdmin(groupId, userId, enable))
call.respondText(SetGroupAdmin(groupId, userId, enable), ContentType.Application.Json)
}
getOrPost("/set_group_whole_ban") {
val groupId = fetchOrThrow("group_id") .toLong()
val enable = fetchOrThrow("enable").toBooleanStrict()
call.respondText(SetGroupWholeBan(groupId, enable))
call.respondText(SetGroupWholeBan(groupId, enable), ContentType.Application.Json)
}
getOrPost("/set_group_ban") {
@ -86,12 +97,27 @@ fun Routing.troopAction() {
val userId = fetchOrThrow("user_id") .toLong()
val duration = fetchOrNull("duration")?.toInt() ?: (30 * 60)
call.respondText(BanTroopMember(groupId, userId, duration))
call.respondText(BanTroopMember(groupId, userId, duration), ContentType.Application.Json)
}
getOrPost("/set_group_kick") {
val userId = fetchOrThrow("user_id").toLong()
val groupId = fetchOrThrow("group_id").toLong()
call.respondText(KickTroopMember(groupId, userId))
call.respondText(KickTroopMember(groupId, userId), ContentType.Application.Json)
}
getOrPost("/set_essence_msg") {
val messageId = fetchOrThrow("message_id").toInt()
call.respondText(SetEssenceMessage(messageId), ContentType.Application.Json)
}
getOrPost("/delete_essence_msg") {
val messageId = fetchOrThrow("message_id").toInt()
call.respondText(DeleteEssenceMessage(messageId), ContentType.Application.Json)
}
getOrPost("/get_group_system_msg") {
call.respondText(GetGroupSystemMsg(), ContentType.Application.Json)
}
}

View File

@ -17,10 +17,12 @@ import moe.fuqiuluo.shamrock.remote.config.ECHO_KEY
import moe.fuqiuluo.shamrock.remote.entries.EmptyObject
import moe.fuqiuluo.shamrock.remote.entries.IndexData
import moe.fuqiuluo.shamrock.remote.entries.Status
import moe.fuqiuluo.shamrock.tools.EmptyJsonObject
import moe.fuqiuluo.shamrock.tools.fetchOrNull
import moe.fuqiuluo.shamrock.tools.fetchOrThrow
import moe.fuqiuluo.shamrock.tools.fetchPostJsonElement
import moe.fuqiuluo.shamrock.tools.fetchPostJsonObject
import moe.fuqiuluo.shamrock.tools.fetchPostJsonObjectOrNull
import moe.fuqiuluo.shamrock.tools.isJsonArray
import moe.fuqiuluo.shamrock.tools.isJsonObject
import moe.fuqiuluo.shamrock.tools.isJsonString
@ -55,7 +57,7 @@ fun Routing.echoVersion() {
}
call.attributes.put(ECHO_KEY, echo)
val params = fetchPostJsonObject("params")
val params = fetchPostJsonObjectOrNull("params") ?: EmptyJsonObject
val handler = ActionManager[action]
if (handler == null) {

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