mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-24 15:00:38 +08:00
commit
b8f49b5037
2
.editorconfig
Normal file
2
.editorconfig
Normal file
@ -0,0 +1,2 @@
|
||||
[*.{kt, kts}]
|
||||
max_line_length = 120
|
10
.github/ISSUE_TEMPLATE/-------.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/-------.md
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
name: 疑问 / 帮助
|
||||
about: 询问一个问题
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--请详细描述你的问题. 我们会尊重每一个问题, 你不必担心问题是否过于简单-->
|
21
.github/ISSUE_TEMPLATE/----.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/----.md
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
name: 特性申请
|
||||
about: 申请 mirai 添加新的特性
|
||||
title: ''
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
以下相关功能将会被直接拒绝:
|
||||
- 资金相关: 红包, 转账
|
||||
- 主动加好友, 主动加群, 主动邀请加入群
|
||||
|
||||
|
||||
可以提交的内容:
|
||||
- 有较高使用频率的协议 (低频功能不接受提议)
|
||||
- 架构 / 功能上的建议 (非常欢迎,我们会尊重你的建议)
|
||||
|
||||
-->
|
||||
<!--请在下一行开始描述你的问题-->
|
22
.github/ISSUE_TEMPLATE/bug---.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/bug---.md
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Bug 报告
|
||||
about: 提交一个 bug
|
||||
title: ''
|
||||
labels: " bug "
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## 问题
|
||||
<!--在这里简略描述你遇到的问题-->
|
||||
<!--如果有控制台报错,请尽量附加全面的日志或截图-->
|
||||
|
||||
|
||||
## 如何复现
|
||||
<!--在这里简略说明如何让这个问题再次发生-->
|
||||
<!--可使用 1. 2. 3. 的列表格式,或其他任意恰当的格式>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--如有必要,你可以在下文继续添加其他信息-->
|
18
.github/workflows/main.yml
vendored
18
.github/workflows/main.yml
vendored
@ -1,6 +1,6 @@
|
||||
name: CI
|
||||
name: Gradle CI
|
||||
|
||||
on: [push]
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -8,10 +8,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: setup-android
|
||||
uses: msfjarvis/setup-android@0.2
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
# Gradle tasks to run - If you want to run ./gradlew assemble, specify assemble here.
|
||||
gradleTasks: build -x mirai-core:jvmTest
|
||||
|
||||
java-version: 1.8
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
40
.github/workflows/main2.yml
vendored
Normal file
40
.github/workflows/main2.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Shadow
|
||||
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# events but only for the master branch
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle and shadowJar
|
||||
run: ./gradlew :mirai-core:shadowJar :mirai-core-qqandroid:shadowJar
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
# Artifact name
|
||||
name: mirai-core-all
|
||||
# Directory containing files to upload
|
||||
path: "mirai-core/build/libs/mirai-core-*-all.jar"
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
# Artifact name
|
||||
name: mirai-core-qqandroid-all
|
||||
# Directory containing files to upload
|
||||
path: "mirai-core-qqandroid/build/libs/mirai-core-qqandroid-*-all.jar"
|
196
CHANGELOG.md
196
CHANGELOG.md
@ -2,6 +2,200 @@
|
||||
|
||||
开发版本. 频繁更新, 不保证高稳定性
|
||||
|
||||
## `0.27.0` 2020/3/8
|
||||
- 支持 `XML`, `Json`, `LightApp` 等 `RichMessage`
|
||||
|
||||
## `0.26.2` 2020/3/8
|
||||
- 新增 `MessageChain.repeat` 与 `MessageChain.times`
|
||||
- JVM 平台下 `PlatformLogger` 可重定向输出
|
||||
- 修复 `NullMessageChain.equals` 判断不正确的问题
|
||||
- 新增 `PlainText.of` 以应对一些特殊情况
|
||||
|
||||
## `0.26.1` 2020/3/8
|
||||
- 重写 Jce 序列化, 提升反序列性能
|
||||
- 更新 `Kotlin` 版本到 1.3.70
|
||||
- 更新 `kotlinx.coroutines`, `atomicfu`, `kotlinx.coroutines` 依赖版本
|
||||
|
||||
## `0.26.0` 2020/3/7
|
||||
- 使用 `kotlinx.io` 而不是 `ktor.io`
|
||||
- 修复 #111, #108, #116, #112
|
||||
|
||||
## `0.25.0` 2020/3/6
|
||||
- 适配 8.2.7 版本(2020 年 3 月)协议
|
||||
- 全面的 `Image` 类型: Online/Offline Image, Friend/Group Image
|
||||
- 修复查询图片链接时好友图片链接错误的问题
|
||||
- 修复 bugs: #105, #106, #107
|
||||
|
||||
## `0.24.1` 2020/3/3
|
||||
- 修复 `Member` 的委托 `QQ` 弱引用被释放的问题
|
||||
- 用 `Bot.friends` 替代 `Bot.qqs`
|
||||
- 用 `Bot.containsFriend`, `Bot.containsGroup` 替代 `Bot.contains`
|
||||
- 新增 `BotFactory.Bot(String, ByteArray)` 用 md5 密码登录
|
||||
- 为 `BotFactory` 等类型的一些扩展指定 `JvmName`
|
||||
- 移动 `Bot.QQ` 到低级 API
|
||||
|
||||
## `0.24.0` 2020/3/1
|
||||
- Java 完全友好: Java 使用者可以同 Kotlin 方式直接阻塞式或异步(Future)调用 API
|
||||
- 新增 `MessageSource.originalMessage: MessageChain` 以获取源消息内容
|
||||
- 群消息的撤回现在已稳定 (`Bot.recall`)
|
||||
- 现在可以引用回复机器人自己发送的消息: `MessageReceipt.quoteReply`
|
||||
- 新增 `MessageRecallEvent`
|
||||
|
||||
- 整理 `MessageChain` 的构造, 优化性能
|
||||
- 整理所有网络层代码, 弃用 `kotlinx.io` 而使用 `io.ktor.utils.io`
|
||||
- 其他杂项优化
|
||||
|
||||
## `0.23.0` 2020/2/28
|
||||
### mirai-core
|
||||
- 修复上传图片
|
||||
- 一些问题修复
|
||||
- 大量杂项优化
|
||||
|
||||
### mirai-core-qqandroid
|
||||
- `MessageReceipt.source` 现在为 public. 可获取源消息 id
|
||||
- 修复上传好友图片失败的问题
|
||||
- 上传群图片现在分包缓存, 优化性能
|
||||
|
||||
## `0.22.0` 2020/2/24
|
||||
### mirai-core
|
||||
- 重构 `MessageChain`, 引入 `CombinedMessage`. (兼容大部分原 API)
|
||||
- 新增 `MessageChainBuilder`, `buildMessageChain`
|
||||
- `ExternalImage` 现在接收多种输入参数
|
||||
|
||||
### mirai-core-qqandroid
|
||||
- 修复访问好友消息回执 `.sequenceId` 时抛出异常的问题
|
||||
|
||||
## `0.21.0` 2020/2/23
|
||||
- 支持好友消息的引用回复
|
||||
- 更加结构化的 `QuoteReply` 架构, 支持引用任意群/好友消息回复给任意群/好友.
|
||||
|
||||
## `0.20.0` 2020/2/23
|
||||
|
||||
### mirai-core
|
||||
- 支持图片下载: `image.channel(): ByteReadChannel`, `image.url()`
|
||||
|
||||
- 添加 `LockFreeLinkedList<E>.iterator`
|
||||
- 添加 `LockFreeLinkedList<E>.forEachNode`
|
||||
|
||||
- 并行处理事件监听
|
||||
- 添加 `nextMessageContaining` 和相关可空版本
|
||||
|
||||
- '撤回' 从 `Contact` 移动到 `Bot`
|
||||
- 删除 `MessageSource.sourceMessage`
|
||||
- 让 MessageSource 拥有唯一的 long 类型 id, 删除原 `uid` 和 `sequence` 结构.
|
||||
- 修复 `Message.eq` 歧义
|
||||
|
||||
## `0.19.1` 2020/2/21
|
||||
|
||||
### mirai-core
|
||||
- 支持机器人撤回群消息 (含自己发送的消息): `Group.recall`, `MessageReceipt.recall`
|
||||
- 支持一定时间后自动撤回: `Group.recallIn`, `MessageReceipt.recallIn`
|
||||
- `sendMessage` 返回 `MessageReceipt` 以实现撤回功能
|
||||
- 添加 `MessageChain.addOrRemove`
|
||||
- 添加 `ContactList.firstOrNull`, `ContactList.first`
|
||||
- 新的异步事件监听方式: `subscribingGetAsync` 启动一个协程并从一个事件从获取返回值到 `Deferred`.
|
||||
- 新的线性事件监听方式: `subscribingGet` 挂起当前协程并从一个事件从获取返回值.
|
||||
|
||||
##### 新的线性消息连续处理: `nextMessage` 挂起当前协程并等待下一条消息:
|
||||
使用该示例, 发送两条消息, 一条为 "禁言", 另一条包含一个 At
|
||||
```kotlin
|
||||
case("禁言") {
|
||||
val value: At = nextMessage { message.any(At) }[At]
|
||||
value.member().mute(10)
|
||||
}
|
||||
```
|
||||
示例 2:
|
||||
```kotlin
|
||||
case("复读下一条") {
|
||||
reply(nextMessage().message)
|
||||
}
|
||||
```
|
||||
|
||||
### mirai-core-qqandroid
|
||||
- 修复一些情况下 `At` 无法发送的问题
|
||||
- 统一 ImageId: 群消息收到的 ImageId 均为 `{xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx}.jpg` 形式(固定长度 37)
|
||||
- 支持成员主动离开事件的解析 (#51)
|
||||
|
||||
## `0.18.0` 2020/2/20
|
||||
|
||||
### mirai-core
|
||||
- 添加 `MessageSource.time`
|
||||
- 添加事件监听时额外的 `coroutineContext`
|
||||
- 为一些带有 `operator` 的事件添加 `.isByBot` 的属性扩展
|
||||
- 优化事件广播逻辑, 修复可能无法触发监听的问题
|
||||
- 为所有 `Contact` 添加 `toString()` (#80)
|
||||
|
||||
### mirai-core-qqandroid
|
||||
- 支持成员禁言状态和时间查询 `Member.muteTimeRemaining`
|
||||
- 修复 `At` 的 `display` (#73), 同时修复 `QuoteReply` 无法显示问题 (#54).
|
||||
- 广播 `BotReloginEvent` (#78)
|
||||
- 支持机器人自身禁言时间的更新和查询 (#82)
|
||||
|
||||
## `0.17.0` 2020/2/20
|
||||
|
||||
### mirai-core
|
||||
- 支持原生表情 `Face`
|
||||
- 修正 `groupCardOrNick` 为 `nameCardOrNick`
|
||||
- 增加 `MessageChain.foreachContent(lambda)` 和 `Message.hasContent(): Boolean`
|
||||
|
||||
### mirai-core-qqandroid
|
||||
- 提高重连速度
|
||||
- 修复重连后某些情况不会心跳
|
||||
- 修复收包时可能产生异常
|
||||
|
||||
## `0.16.0` 2020/2/19
|
||||
|
||||
### mirai-core
|
||||
- 添加 `Bot.subscribe` 等筛选 Bot 实例的监听方法
|
||||
- 其他一些小问题修复
|
||||
|
||||
### mirai-core-qqandroid
|
||||
- 优化重连处理逻辑
|
||||
- 确保好友消息和历史事件在初始化结束前同步完成
|
||||
- 同步好友消息记录时不广播
|
||||
|
||||
## `0.15.5` 2020/2/19
|
||||
|
||||
### mirai-core
|
||||
- 为 `MiraiLogger` 添加 common property `val isEnabled: Boolean`
|
||||
- 修复 #62: 掉线重连后无 heartbeat
|
||||
- 修复 #65: `Bot` close 后仍会重连
|
||||
- 修复 #70: ECDH is not available on Android platform
|
||||
|
||||
### mirai-core-qqandroid
|
||||
- 从服务器收到的事件将会额外使用 `bot.logger` 记录 (verbose).
|
||||
- 降低包记录的等级: `info` -> `verbose`
|
||||
- 改善 `Bot` 的 log 记录
|
||||
- 加载好友列表失败时会重试
|
||||
- 改善 `Bot` 或 `NetworkHandler` 关闭时取消 job 的逻辑
|
||||
- 修复初始化(init)时同步历史好友消息时出错的问题
|
||||
|
||||
## `0.15.4` 2020/2/18
|
||||
|
||||
- 放弃使用 `atomicfu` 以解决其编译错误的问题. (#60)
|
||||
|
||||
## `0.15.3` 2020/2/18
|
||||
|
||||
- 修复无法引入依赖的问题.
|
||||
|
||||
## `0.15.2` 2020/2/18
|
||||
|
||||
### mirai-core
|
||||
- 尝试修复 `atomicfu` 编译错误的问题
|
||||
|
||||
### mirai-core-qqandroid
|
||||
- 查询群信息失败后重试
|
||||
|
||||
## `0.15.1` 2020/2/15
|
||||
|
||||
### mirai-core
|
||||
- 统一异常处理: 所有群成员相关操作无权限时均抛出异常而不返回 `false`.
|
||||
|
||||
### mirai-core-qqandroid
|
||||
- 初始化未完成时缓存接收的所有事件包 (#46)
|
||||
- 解析群踢人事件时忽略找不到的群成员
|
||||
- 登录完成后广播事件 `BotOnlineEvent`
|
||||
|
||||
## `0.15.0` 2020/2/14
|
||||
|
||||
### mirai-core
|
||||
@ -132,7 +326,7 @@ TIMPC
|
||||
## `0.10.0` *2019/12/23*
|
||||
**事件优化**
|
||||
更快的监听过程
|
||||
现在监听不再是 `suspend`, 而必须显式指定 `CoroutineScope`. 详见 [`Subscribers.kt`](mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt#L69)
|
||||
现在监听不再是 `suspend`, 而必须显式指定 `CoroutineScope`. 详见 `Subscribers.kt`
|
||||
删除原本的 bot.subscribe 等监听模式.
|
||||
|
||||
**其他**
|
||||
|
29
CONTRIBUTING.md
Normal file
29
CONTRIBUTING.md
Normal file
@ -0,0 +1,29 @@
|
||||
# 贡献
|
||||
|
||||
感谢你来到这里和你对 mirai 做的所有贡献。
|
||||
|
||||
mirai 欢迎一切形式的代码贡献。你可以通过以下几种途径向 mirai 贡献。
|
||||
|
||||
## 主仓库 `mirai-core`
|
||||
|
||||
### 代码优化
|
||||
优化功能设计或实现, 或是引入一个新的设计(建议先通过 issue 与我们达成共识)
|
||||
|
||||
### 协议更新
|
||||
为 mirai 添加更广泛的协议支持。
|
||||
|
||||
### 注意事项
|
||||
- mirai 框架已经把实现协议需要做的工作最小化. 为避免工作重复, 请务必熟悉 `net.mamoe.mirai.utils` 和 `net.mamoe.mirai.qqandroid.utils` 中工具类
|
||||
- mirai 使用 [`kotlinx.io`](https://github.com/Kotlin/kotlinx-io) IO 库
|
||||
- mirai 为多平台项目, 请务必考虑多平台兼容性
|
||||
- mirai 为全协程实现, 请在有必要的时候考虑并发安全性
|
||||
- 尽量不要引用新的库
|
||||
- 遵守 Kotlin 代码规范(提交前使用 IDE 格式化代码)
|
||||
- 熟悉 [`PacketFactory`](https://github.com/mamoe/mirai/blob/master/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt) 架构
|
||||
- 不要手动拆解数据包. 请一定使用 `kotlinx.serialization` 拆解 ProtoBuf 和使用 mirai 的 `Jce` 序列化器拆解 Jce 数据包
|
||||
- 必须保证高代码效率(使用 `ByteArrayPool`,`WeakRef` 等)
|
||||
|
||||
## 社区
|
||||
|
||||
插件社区不要求太高的代码质量,任何人都可以帮助 mirai。
|
||||
可以为 [mirai-console](https://github.com/mamoe/mirai-console) 编写插件, 并发布到社区网站: (建设中)
|
@ -6,14 +6,16 @@ Some of the protocol came from the other open-source projects.
|
||||
|
||||
**The development is only for learning, DO NOT use it for illegal purposes.**
|
||||
|
||||
## UpdateLog
|
||||
## Changelog
|
||||
|
||||
You can inspect supported protocols at [Project](https://github.com/mamoe/mirai/projects/1)
|
||||
and logs of updates at [UpdateLog](https://github.com/mamoe/mirai/blob/master/UpdateLog.md)
|
||||
and logs of updates at [CHANGELOG](https://github.com/mamoe/mirai/blob/master/CHANGELOG.md)
|
||||
|
||||
## Use as a library
|
||||
You can install mirai as a library into your project.
|
||||
|
||||
Mirai is only published on `jcenter`, therefore please ensure you have the `jcenter()` repository in your `build.gradle`, like:
|
||||
Mirai is only published on `jcenter`, therefore please ensure you have the `jcenter()` repository in your `build.gradle`.
|
||||
|
||||
```kotlin
|
||||
repositories{
|
||||
jcenter()
|
||||
@ -24,6 +26,7 @@ If your project is a multiplatform project, you should add dependencies for each
|
||||
If your project is not a multiplatform project, you just need to add the platform-specific dependency.
|
||||
|
||||
`VERSION` should be replaced with the newest version, say [![Download](https://api.bintray.com/packages/him188moe/mirai/mirai-core/images/download.svg)](https://bintray.com/him188moe/mirai/mirai-core/)
|
||||
|
||||
Mirai is still under experimental stage, it is suggested to keep the version newest.
|
||||
|
||||
**common**
|
||||
@ -71,14 +74,20 @@ If you meet any problem or have any questions, be free to open a issue. Our goal
|
||||
Kotlin 1.3.61
|
||||
|
||||
On JVM: Java 6
|
||||
|
||||
On Android: SDK 15
|
||||
|
||||
### Using java
|
||||
Q: Can I use Mirai without Kotlin?
|
||||
|
||||
A: Calling from java is not yet supported. Coroutines, extensions and inlines, which are difficult to use from Java, are generally used in Mirai. Therefore you should have the skill of Kotlin before you use Mirai.
|
||||
|
||||
#### Libraries used
|
||||
Mirai uses these open-source libraries.
|
||||
## Acknowledgements
|
||||
|
||||
Thanks to [JetBrains](https://www.jetbrains.com/?from=mirai) for allocating free open-source licences for IDEs such as [IntelliJ IDEA](https://www.jetbrains.com/idea/?from=mirai).
|
||||
[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/?from=mirai)
|
||||
|
||||
### Third Party Libraries
|
||||
|
||||
- [kotlin-stdlib](https://github.com/JetBrains/kotlin)
|
||||
- [kotlinx-coroutines](https://github.com/Kotlin/kotlinx.coroutines)
|
||||
@ -92,7 +101,19 @@ Mirai uses these open-source libraries.
|
||||
- [javafx](https://github.com/openjdk/jfx)
|
||||
- [kotlinx-serialization](https://github.com/Kotlin/kotlinx.serialization)
|
||||
|
||||
## License
|
||||
|
||||
## Acknowledgement
|
||||
Thanks to [JetBrains](https://www.jetbrains.com/?from=mirai) for allocating free open-source licences for IDEs such as [IntelliJ IDEA](https://www.jetbrains.com/idea/?from=mirai).
|
||||
[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/?from=mirai)
|
||||
Copyright (C) 2019-2020 mamoe and Mirai contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
205
README.md
205
README.md
@ -1,151 +1,146 @@
|
||||
# Mirai
|
||||
<div align="center">
|
||||
<img width="160" src="http://img.mamoe.net/2020/02/16/a759783b42f72.png" alt="logo"></br>
|
||||
|
||||
|
||||
<img width="95" src="http://img.mamoe.net/2020/02/16/c4aece361224d.png" alt="title">
|
||||
|
||||
----
|
||||
|
||||
[![Gitter](https://badges.gitter.im/mamoe/mirai.svg)](https://gitter.im/mamoe/mirai?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
[![Actions Status](https://github.com/mamoe/mirai/workflows/CI/badge.svg)](https://github.com/mamoe/mirai/actions)
|
||||
![Gradle CI](https://github.com/mamoe/mirai/workflows/Gradle%20CI/badge.svg?branch=master)
|
||||
[![Download](https://api.bintray.com/packages/him188moe/mirai/mirai-core/images/download.svg)](https://bintray.com/him188moe/mirai/mirai-core/)
|
||||
|
||||
Mirai 是一个在全平台下运行,提供 QQ Android 和 TIM PC 协议支持的高效率机器人框架
|
||||
|
||||
这个项目的名字来源于
|
||||
<p><a href = "http://www.kyotoanimation.co.jp/">京都动画</a>作品<a href = "https://zh.moegirl.org/zh-hans/%E5%A2%83%E7%95%8C%E7%9A%84%E5%BD%BC%E6%96%B9">《境界的彼方》</a>的<a href = "https://zh.moegirl.org/zh-hans/%E6%A0%97%E5%B1%B1%E6%9C%AA%E6%9D%A5">栗山未来(Kuriyama <b>Mirai</b>)</a></p>
|
||||
<p><a href = "https://www.crypton.co.jp/">CRYPTON</a>以<a href = "https://www.crypton.co.jp/miku_eng">初音未来</a>为代表的创作与活动<a href = "https://magicalmirai.com/2019/index_en.html">(Magical <b>Mirai</b>)</a></p>
|
||||
图标以及形象由画师<a href = "">DazeCake</a>绘制
|
||||
</div>
|
||||
|
||||
## Mirai
|
||||
|
||||
**[English](README-eng.md)**
|
||||
|
||||
多平台 **QQ Android 和 TimPC** 协议支持库与高效率的机器人框架.
|
||||
|
||||
多平台 **QQ Android** 和 **TIM PC** 协议支持库与高效率的机器人框架.
|
||||
纯 Kotlin 实现协议和支持框架,模块<b>全部免费开源</b>。
|
||||
目前可运行在 JVM 或 Android。
|
||||
Mirai既可以作为你项目中的QQ协议支持Lib, 也可以作为单独的Application与插件承载QQ机器人
|
||||
目前可运行在 JVM 或 Android 平台。
|
||||
mirai 既可以作为你项目中的 QQ 协议支持库, 也可以作为单独的应用程序与插件承载 QQ 机器人服务。
|
||||
|
||||
**一切开发旨在学习,请勿用于非法用途**
|
||||
|
||||
加入 Gitter, 或加入 QQ 群: 655057127
|
||||
加入 [![Gitter](https://badges.gitter.im/mamoe/mirai.svg)](https://gitter.im/mamoe/mirai?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge), 或加入 QQ 群: 655057127
|
||||
|
||||
## CHANGELOG
|
||||
在 [Project](https://github.com/mamoe/mirai/projects/3) 查看已支持功能和计划
|
||||
在 [CHANGELOG](https://github.com/mamoe/mirai/blob/master/CHANGELOG.md) 查看版本更新记录 (仅发布的版本)
|
||||
|
||||
## Modules
|
||||
### mirai-core
|
||||
通用 API 模块,一套 API 适配两套协议。
|
||||
**请参考此模块的 API**
|
||||
## 开始
|
||||
|
||||
### mirai-core-qqandroid
|
||||
QQ for Android (8.2.0 版本,2019 年 12 月)协议的实现,目前完成大部分。
|
||||
- 高兼容性:协议仅含极少部分为硬编码,其余全部随官方方式动态生成
|
||||
- 高安全性:密匙随机,ECDH 动态计算
|
||||
- 已支持大部分使用场景, 详情请在[Project](https://github.com/mamoe/mirai/projects/3)查看
|
||||
Mirai 目前为快速流转(Moving fast)状态, 增量版本之间可能不具有兼容性,任何功能都可能在没有警告的情况下添加、删除或者更改。
|
||||
|
||||
### mirai-core-timpc
|
||||
TIM PC (2.3.2 版本,2019 年 8 月)协议的实现,相较于 core,仅新增少量 API. 详见 [README.md](mirai-core-timpc/)
|
||||
支持的功能:
|
||||
- 消息收发:图片文字复合消息,图片消息
|
||||
- 群管功能:群员列表,禁言
|
||||
(目前不再更新此协议,请关注上文的安卓协议)
|
||||
Mirai 源码完全开放, 您可以参考 Mirai 的协议实现来开发其他框架, 但需注明来源并遵守开源协议要求 (AGPLv3)。
|
||||
|
||||
## Use directly
|
||||
**直接使用 Mirai(终端环境/网页面板(将来)).**
|
||||
[Mirai-Console](https://github.com/mamoe/mirai/tree/master/mirai-console) 插件支持, 在终端中启动 Mirai 并获得机器人服务
|
||||
本模块还未完善。
|
||||
### 开发者
|
||||
|
||||
## Use as a library
|
||||
**mirai-core 为独立设计, 可以作为库内置于任意 Java(JVM)/Android 项目中使用.**
|
||||
**了解 mirai 架构**: [Wiki](https://github.com/mamoe/mirai/wiki/Home)
|
||||
|
||||
### Gradle
|
||||
Mirai 只发布在 `jcenter`, 因此请确保在 `build.gradle` 添加 `jcenter()` 仓库:
|
||||
```kotlin
|
||||
repositories{
|
||||
jcenter()
|
||||
}
|
||||
```
|
||||
若您需要使用在跨平台项目, 则要对各个目标平台添加不同的依赖,这与 kotlin 相关跨平台库的依赖是类似的。
|
||||
**若您只需要使用在单一平台, 则只需要添加一项该平台的依赖. 如只在 JVM 运行则只需要`-jvm`的依赖**
|
||||
#### 使用 mirai 作为服务器,为 mirai 开发插件
|
||||
|
||||
请将 `VERSION` 替换为最新的版本(如 `0.13.0`):
|
||||
[![Download](https://api.bintray.com/packages/him188moe/mirai/mirai-core/images/download.svg)](https://bintray.com/him188moe/mirai/mirai-core/)
|
||||
**Mirai 目前还处于实验性阶段, 我们无法保证任何稳定性, API 也可能会随时修改.**
|
||||
- (官方)`Java` 或 `Kotlin`: 为 [mirai-console](https://github.com/mamoe/mirai-console) 直接编写插件并与其他插件开发者合作共享
|
||||
- (社区)`C`, `C++` 等原生语言: [mirai-native](https://github.com/iTXTech/mirai-native) 支持酷Q插件在mirai上运行
|
||||
- (社区)`Python`: [python-mirai](https://github.com/Chenwe-i-lin/python-mirai) 基于`Mirai-http-api`的 Mirai Framework for Python
|
||||
- (社区)`JavaScript`(`NodeJS`) [node-mirai](https://github.com/RedBeanN/node-mirai) Mirai的NodeJs SDK
|
||||
- (官方)其他任意语言: [mirai HTTP 接口](https://github.com/mamoe/mirai-api-http) 进行接入
|
||||
|
||||
**注意:**
|
||||
Mirai 核心由 API 模块(`mirai-core`)和协议模块组成。
|
||||
只添加 API 模块将无法正常工作。
|
||||
现在只推荐使用 TIMPC 协议,请参照下文选择对应目标平台的依赖添加。
|
||||
#### 使用 mirai 为第三方依赖库引入项目
|
||||
|
||||
**common**
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid-common:VERSION")
|
||||
```
|
||||
**jvm**
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid-jvm:VERSION")
|
||||
```
|
||||
**android**
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid-android:VERSION")
|
||||
```
|
||||
### Performance
|
||||
Android 上, Mirai 运行需使用 80M 内存.
|
||||
JVM 上需 120M-150M 内存
|
||||
Demos: [mirai-demos](https://github.com/mamoe/mirai-demos)
|
||||
|
||||
## Contribution
|
||||
- `Kotlin` 简略版: [Mirai Guide - Quick Start](/docs/guide_quick_start.md)
|
||||
- `Kotlin` 新手版: [Mirai Guide - Getting Started](/docs/guide_getting_started.md)
|
||||
- `Java`: 查看上述 Demos
|
||||
|
||||
我们 (Mamoe Technologies) 将会一直维护这个项目,除非遇到不可抗力因素。
|
||||
### 使用者
|
||||
|
||||
- [mirai-console](https://github.com/mamoe/mirai-console) 支持插件 **本模块正在完善**
|
||||
|
||||
### 我是其他平台的使用者
|
||||
|
||||
#### 酷 Q 平台用户:
|
||||
|
||||
- 酷Q的插件可以在 mirai 中加载, 详见 [Mirai-Native](https://github.com/iTXTech/mirai-native)
|
||||
- 使用 `酷Q HTTP API` 的插件将可以在 mirai 中加载,`Mirai-CQ-Adapter` 正在进行中
|
||||
|
||||
## 更新日志
|
||||
|
||||
* 在 [Project](https://github.com/mamoe/mirai/projects/3) 查看已支持功能和计划
|
||||
* 在 [CHANGELOG](https://github.com/mamoe/mirai/blob/master/CHANGELOG.md) 查看版本更新记录 (仅发布的版本)
|
||||
|
||||
## [贡献](https://github.com/mamoe/mirai/blob/master/CONTRIBUTING.md)
|
||||
|
||||
我们欢迎一切形式的贡献。
|
||||
我们也期待有更多人能加入 Mirai 的开发。
|
||||
我们也期待有更多人能加入 mirai 的开发。
|
||||
|
||||
若在使用过程中有任何疑问, 可提交 issue 或是邮件联系(support@mamoe.net). 我们希望 Mirai 变得更易用.
|
||||
若在使用过程中有任何疑问, 可提交 `issue` 或是[邮件联系](mailto:support@mamoe.net). 我们希望 mirai 变得更易用.
|
||||
|
||||
您的 star 是对我们最大的鼓励(点击项目右上角);
|
||||
您的 `star` 是对我们最大的鼓励(点击项目右上角)
|
||||
|
||||
## Wiki
|
||||
在 [Wiki](https://github.com/mamoe/mirai/wiki/Development-Guide---Kotlin) 中查看各类帮助,**如 API 示例**(可能过时,待 QQ Android 协议完成后会重写)。
|
||||
### 贡献者
|
||||
感谢以下全体开发者对 mirai 的贡献(排名不分先后)
|
||||
|
||||
## Try
|
||||
|
||||
### On JVM or Android
|
||||
现在体验低付出高效率的 Mirai
|
||||
|
||||
```kotlin
|
||||
val bot = TIMPC.Bot(qqId, password).alsoLogin()
|
||||
bot.subscribeMessages {
|
||||
"你好" reply "你好!"
|
||||
"profile" reply { sender.queryProfile() }
|
||||
contains("图片"){ File(imagePath).send() }
|
||||
}
|
||||
bot.subscribeAlways<MemberPermissionChangedEvent> {
|
||||
if (it.kind == BECOME_OPERATOR)
|
||||
reply("${it.member.id} 成为了管理员")
|
||||
}
|
||||
```
|
||||
|
||||
我们也考虑到了 Java 兼容的问题,这正在计划中,但不是高优先的。
|
||||
|
||||
1. Clone
|
||||
2. Import as Gradle project
|
||||
3. 运行 Demo 程序: [mirai-demo](#mirai-demo) 示例和演示程序
|
||||
[<img width="60px" height="60px" src="https://avatars2.githubusercontent.com/u/12100985?s=60&v=4" />](https://github.com/Him188)
|
||||
[<img width="60px" height="60px" src="https://avatars0.githubusercontent.com/u/24618776?s=60&v=4" />](https://github.com/liujiahua123123)
|
||||
[<img width="60px" height="60px" src="https://avatars2.githubusercontent.com/u/28707253?s=60&v=4" />](https://github.com/ryoii)
|
||||
[<img width="60px" height="60px" src="https://avatars1.githubusercontent.com/u/11070535?s=60&v=4" />](https://github.com/jasonczc)
|
||||
[<img width="60px" height="60px" src="https://avatars2.githubusercontent.com/u/13656668?s=60&v=4" />](https://github.com/PeratX)
|
||||
[<img width="60px" height="60px" src="https://avatars2.githubusercontent.com/u/18532671?s=60&v=4" />](https://github.com/uebian)
|
||||
[<img width="60px" height="60px" src="https://avatars2.githubusercontent.com/u/10308687?s=60&v=4" />](https://github.com/Freedom0925)
|
||||
[<img width="60px" height="60px" src="https://avatars3.githubusercontent.com/u/16398479?s=60&v=4" />](https://github.com/ice1000)
|
||||
[<img width="60px" height="60px" src="https://avatars0.githubusercontent.com/u/20042607?s=60&v=4" />](https://github.com/PragmaTwice)
|
||||
[<img width="60px" height="60px" src="https://avatars0.githubusercontent.com/u/25280943?s=60&v=4" />](https://github.com/HoshinoTented)
|
||||
[<img width="60px" height="60px" src="https://avatars3.githubusercontent.com/u/40517459?s=60&v=4" />](https://github.com/Cyenoch)
|
||||
|
||||
|
||||
## Build Requirements
|
||||
## 鸣谢
|
||||
|
||||
- Kotlin 1.3.61
|
||||
- JDK 8 (required)
|
||||
- JDK 11(for protocol tools, optional)
|
||||
- Android SDK 29 (for Android target, optional)
|
||||
特别感谢 [JetBrains](https://www.jetbrains.com/?from=mirai) 为开源项目提供免费的 [IntelliJ IDEA](https://www.jetbrains.com/idea/?from=mirai) 等 IDE 的授权
|
||||
[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/?from=mirai)
|
||||
|
||||
### 第三方类库(无排名)
|
||||
|
||||
#### Libraries used
|
||||
感谢:
|
||||
- [kotlin-stdlib](https://github.com/JetBrains/kotlin)
|
||||
- [kotlinx-coroutines](https://github.com/Kotlin/kotlinx.coroutines)
|
||||
- [kotlinx-io](https://github.com/Kotlin/kotlinx-io)
|
||||
- [kotlin-reflect](https://github.com/JetBrains/kotlin)
|
||||
- [pcap4j](https://github.com/kaitoy/pcap4j)
|
||||
- [atomicfu](https://github.com/Kotlin/kotlinx.atomicfu)
|
||||
- [ktor](https://github.com/ktorio/ktor)
|
||||
- [tornadofx](https://github.com/edvin/tornadofx)
|
||||
- [javafx](https://github.com/openjdk/jfx)
|
||||
- [kotlinx-serialization](https://github.com/Kotlin/kotlinx.serialization)
|
||||
- [bouncycastle](https://www.bouncycastle.org/java.html)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
## 许可证
|
||||
|
||||
协议原版权归属腾讯科技股份有限公司所有,本项目其他代码遵守:
|
||||
**GNU AFFERO GENERAL PUBLIC LICENSE version 3**
|
||||
|
||||
其中部分要求:
|
||||
|
||||
- (见 LICENSE 第 13 节) 尽管本许可协议有其他规定,但如果您修改本程序,则修改后的版本必须显着地为所有通过计算机网络与它进行远程交互的用户(如果您的版本支持这种交互)提供从网络服务器通过一些标准或惯用的软件复制方法**免费**访问相应的**源代码**的机会
|
||||
- (见 LICENSE 第 4 节) 您可以免费或收费地传递这个项目的源代码或目标代码(即编译结果), **但前提是提供明显的版权声明** (您需要标注本 `GitHub` 项目地址)
|
||||
|
||||
## Acknowledgement
|
||||
特别感谢 [JetBrains](https://www.jetbrains.com/?from=mirai) 为开源项目提供免费的 [IntelliJ IDEA](https://www.jetbrains.com/idea/?from=mirai) 等 IDE 的授权
|
||||
[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/?from=mirai)
|
||||
------
|
||||
|
||||
Copyright (C) 2019-2020 mamoe and Mirai contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
48
build.gradle
48
build.gradle
@ -1,48 +0,0 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
google()
|
||||
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev/'}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Do try to waste your time.
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
classpath("com.github.jengelman.gradle.plugins:shadow:5.2.0")
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion"
|
||||
classpath "org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicFuVersion"
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
def keyProps = new Properties()
|
||||
def keyFile = file("local.properties")
|
||||
if (keyFile.exists()) keyFile.withInputStream { keyProps.load(it) }
|
||||
if (!keyProps.getProperty("sdk.dir", "").isEmpty()) {
|
||||
project.ext.set("isAndroidSDKAvailable", true)
|
||||
} else {
|
||||
project.ext.set("isAndroidSDKAvailable", false)
|
||||
}
|
||||
}catch(Exception e){}
|
||||
allprojects {
|
||||
group = "net.mamoe"
|
||||
version = getProperty("mirai_version")
|
||||
|
||||
// tasks.withType(KotlinCompile).all { task ->
|
||||
// task.kotlinOptions{
|
||||
// jvmTarget = '1.6'
|
||||
// }
|
||||
// }
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
google()
|
||||
maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
|
||||
maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
|
||||
}
|
||||
}
|
49
build.gradle.kts
Normal file
49
build.gradle.kts
Normal file
@ -0,0 +1,49 @@
|
||||
import java.lang.System.getProperty
|
||||
import java.util.*
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven(url = "https://mirrors.huaweicloud.com/repository/maven")
|
||||
jcenter()
|
||||
// mavenCentral()
|
||||
google()
|
||||
// maven (url="https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val kotlinVersion: String by project
|
||||
val atomicFuVersion: String by project
|
||||
|
||||
classpath("com.android.tools.build:gradle:3.5.3")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
|
||||
classpath("com.github.jengelman.gradle.plugins:shadow:5.2.0")
|
||||
classpath("org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion")
|
||||
classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicFuVersion")
|
||||
}
|
||||
}
|
||||
|
||||
runCatching {
|
||||
val keyProps = Properties().apply {
|
||||
file("local.properties").takeIf { it.exists() }?.inputStream()?.use { load(it) }
|
||||
}
|
||||
if (keyProps.getProperty("sdk.dir", "").isNotEmpty()) {
|
||||
project.ext.set("isAndroidSDKAvailable", true)
|
||||
} else {
|
||||
project.ext.set("isAndroidSDKAvailable", false)
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group = "net.mamoe"
|
||||
version = getProperty("miraiVersion")
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven(url = "https://mirrors.huaweicloud.com/repository/maven")
|
||||
jcenter()
|
||||
// mavenCentral()
|
||||
google()
|
||||
// maven (url="https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
}
|
||||
}
|
186
docs/guide_build_for_mirai.md
Normal file
186
docs/guide_build_for_mirai.md
Normal file
@ -0,0 +1,186 @@
|
||||
# Mirai Guide - Build For Mirai
|
||||
|
||||
由于Mirai项目在快速推进中,因此内容时有变动,本文档的最后更新日期为```2020-02-29```,对应版本```0.23.0```
|
||||
|
||||
本页面采用Kotlin作为开发语言,**若你希望使用 Java 开发**, 请参阅: [mirai-japt](https://github.com/mamoe/mirai-japt)
|
||||
|
||||
本页面是[Mirai Guide - Subscribe Events](/docs/guide_subscribe_events.md)的后续Guide
|
||||
|
||||
## build.gradle
|
||||
|
||||
我们首先来看一下完整的```build.gradle```文件
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
|
||||
}
|
||||
|
||||
group 'test'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'net.mamoe:mirai-core-qqandroid-jvm:0.19.1'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
```
|
||||
|
||||
使用gradle直接打包,不会将依赖也打包进去
|
||||
|
||||
因此,我们引入一些插件进行打包
|
||||
|
||||
### ShadowJar
|
||||
|
||||
shadowJar支持将依赖也打包到Jar内,下面介绍用法。
|
||||
|
||||
#### 1.buildscript
|
||||
|
||||
首先声明buildScript
|
||||
|
||||
```groovy
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在plugin前加入以上语句
|
||||
|
||||
|
||||
|
||||
#### 2.在plugins中进行插件的使用
|
||||
|
||||
将原本的plugins
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
|
||||
}
|
||||
```
|
||||
|
||||
覆盖为
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
|
||||
id 'com.github.johnrengelman.shadow' version '5.2.0'//使用shadow对依赖进行打包
|
||||
}
|
||||
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply plugin: 'java'
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.添加shadowJar
|
||||
|
||||
在文件底部添加
|
||||
|
||||
```groovy
|
||||
shadowJar {
|
||||
// 生成包的命名规则: baseName-version-classifier.jar
|
||||
manifest {
|
||||
attributes(
|
||||
'Main-Class': 'net.mamoe.mirai.simpleloader.MyLoaderKt'//入口点
|
||||
)
|
||||
}
|
||||
|
||||
// 将 build.gradle 打入到 jar 中, 方便查看依赖包版本
|
||||
from("./"){
|
||||
include 'build.gradle'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 4.运行build
|
||||
|
||||
在IDEA中点击```ShadowJar```左侧的run按钮(绿色小三角),build完成后在```build\libs```中找到jar
|
||||
|
||||
|
||||
|
||||
至此,build.gradle内的内容是
|
||||
|
||||
```groovy
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
|
||||
id 'com.github.johnrengelman.shadow' version '5.2.0'//使用shadow对依赖进行打包
|
||||
}
|
||||
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply plugin: 'java'
|
||||
|
||||
group 'test'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'net.mamoe:mirai-core-qqandroid-jvm:0.23.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
||||
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
// 生成包的命名规则: baseName-version-classifier.jar
|
||||
manifest {
|
||||
attributes(
|
||||
'Main-Class': 'net.mamoe.mirai.simpleloader.MyLoaderKt'
|
||||
)
|
||||
}
|
||||
|
||||
// 将 build.gradle 打入到 jar 中, 方便查看依赖包版本
|
||||
from("./"){
|
||||
include 'build.gradle'
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
126
docs/guide_getting_started.md
Normal file
126
docs/guide_getting_started.md
Normal file
@ -0,0 +1,126 @@
|
||||
# Mirai Guide - Getting Started
|
||||
|
||||
由于Mirai项目在快速推进中,因此内容时有变动,本文档的最后更新日期为```2020-02-29```,对应版本```0.23.0```
|
||||
|
||||
假如仅仅使用Mirai,不需要对整个项目进行Clone,只需在项目内添加Gradle Dependency或使用即可。
|
||||
|
||||
下面介绍详细的入门步骤。
|
||||
|
||||
本页采用Kotlin作为开发语言,**若你希望使用 Java 开发**, 请参阅: [mirai-japt](https://github.com/mamoe/mirai-japt)
|
||||
|
||||
|
||||
|
||||
## 起步步骤
|
||||
通过编写Kotlin程序,以第三方库的形式调用```mirai-core```,并定义你的Mirai Bot行为。
|
||||
|
||||
假如已经对Gradle有一定了解,可跳过1,2
|
||||
|
||||
### 1 安装IDEA与JDK
|
||||
|
||||
JDK要求6以上
|
||||
|
||||
### 2 新建Gradle项目
|
||||
|
||||
*使用gradle项目可能需要代理,在IDEA的```settings```->```proxy settings```中可以设置
|
||||
|
||||
- 在```File->new project```中选择```Gradle```
|
||||
- 在面板中的```Additional Libraries and Frameworks```中勾选```Java```以及```Kotlin/JVM```
|
||||
- 点击```next```,填入```GroupId```与```ArtifactId```(对于测试项目来说,可随意填写)
|
||||
- 点击```next```,点击```Use default gradle wrapper(recommended)```
|
||||
- 创建项目完成
|
||||
|
||||
### 3 添加依赖
|
||||
|
||||
- 打开项目的```Project```面板,点击编辑```build.gradle```
|
||||
|
||||
- 首先添加repositories
|
||||
|
||||
```groovy
|
||||
//添加jcenter仓库
|
||||
/*
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
原文内容,更新为下文
|
||||
*/
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
```
|
||||
|
||||
- 添加依赖,将dependencies部分覆盖。 `mirai-core` 的最新版本为: [![Download](https://api.bintray.com/packages/him188moe/mirai/mirai-core/images/download.svg)](https://bintray.com/him188moe/mirai/mirai-core/)
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation 'net.mamoe:mirai-core-qqandroid-jvm:0.23.0'//此处版本应替换为当前最新
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
||||
```
|
||||
|
||||
- 打开右侧Gradle面板,点击刷新按钮
|
||||
- 至此,依赖添加完成
|
||||
|
||||
### 4 Try Bot
|
||||
|
||||
- 在src/main文件夹下新建文件夹,命名为```kotlin```
|
||||
- 在```kotlin```下新建包(在```kotlin```文件夹上右键-```New```-```Package```) 包名为```net.mamoe.mirai.simpleloader```
|
||||
|
||||
- 在包下新建kotlin文件```MyLoader.kt```
|
||||
|
||||
```kotlin
|
||||
package net.mamoe.mirai.simpleloader
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.alsoLogin
|
||||
import net.mamoe.mirai.event.subscribeMessages
|
||||
|
||||
suspend fun main() {
|
||||
val qqId = 10000L//Bot的QQ号,需为Long类型,在结尾处添加大写L
|
||||
val password = "your_password"//Bot的密码
|
||||
val miraiBot = Bot(qqId, password).alsoLogin()//新建Bot并登录
|
||||
miraiBot.subscribeMessages {
|
||||
"你好" reply "你好!"
|
||||
case("at me") {
|
||||
reply(sender.at() + " 给爷爬 ")
|
||||
}
|
||||
|
||||
(contains("舔") or contains("刘老板")) {
|
||||
"刘老板太强了".reply()
|
||||
}
|
||||
}
|
||||
miraiBot.join() // 等待 Bot 离线, 避免主线程退出
|
||||
}
|
||||
```
|
||||
|
||||
- 单击编辑器内第8行(```suspend fun main```)左侧的run按钮(绿色三角),等待,MiraiBot成功登录。
|
||||
- 本例的功能中,在任意群内任意成员发送包含“舔”字或“刘老板”字样的消息,MiraiBot会回复“刘老板太强了”
|
||||
|
||||
|
||||
至此,简单的入门已经结束,下面可根据不同的需求参阅wiki进行功能的添加。
|
||||
|
||||
下面,可以尝试对不同事件进行监听[Mirai Guide - Subscribe Events](/docs/guide_subscribe_events.md)
|
||||
|
||||
### 此外,还可以使用Maven作为包管理工具
|
||||
本项目推荐使用gradle,因此不提供详细入门指导
|
||||
|
||||
```xml
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jcenter</id>
|
||||
<url>https://jcenter.bintray.com/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
```
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.mamoe</groupId>
|
||||
<artifactId>mirai-core-qqandroid-jvm</artifactId>
|
||||
<version>0.23.0</version> <!-- 替换版本为最新版本 -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
96
docs/guide_quick_start.md
Normal file
96
docs/guide_quick_start.md
Normal file
@ -0,0 +1,96 @@
|
||||
# Mirai Guide - Quick Start
|
||||
|
||||
由于 mirai 项目在快速推进中,因此内容时有变动,本文档的最后更新日期为```2020/3/1```,对应版本```0.23.0```
|
||||
|
||||
本文适用于对 Kotlin 较熟悉的开发者,
|
||||
使用 mirai 作为第三方依赖库引用任意一个 Kotlin, Java 或其他 JVM 平台的项目
|
||||
|
||||
**若你希望一份更基础的教程**, 请参阅: [mirai-guide-getting-started](guide_getting_started.md)
|
||||
|
||||
**若你希望使用 Java 开发**, 请参阅: [mirai-japt](https://github.com/mamoe/mirai-japt)
|
||||
|
||||
## 构建需求
|
||||
|
||||
- Kotlin 1.3.61 (必须)
|
||||
- JDK 6 或更高 (必须)
|
||||
- Android SDK 29 (可选, 用于编译安卓目标)
|
||||
|
||||
## 获取 Demo
|
||||
可在 [mirai-demos](https://github.com/mamoe/mirai-demos) 中获取已经配置好依赖的示例项目.
|
||||
|
||||
## Quick Start
|
||||
|
||||
请将 `VERSION` 替换为 `mirai-core` 的最新版本号(如 `0.23.0`):
|
||||
[![Download](https://api.bintray.com/packages/him188moe/mirai/mirai-core/images/download.svg)](https://bintray.com/him188moe/mirai/mirai-core/)
|
||||
|
||||
### 添加依赖
|
||||
|
||||
#### Maven
|
||||
|
||||
Kotlin 在 Maven 上只支持 JVM 平台.
|
||||
|
||||
```xml
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jcenter</id>
|
||||
<url>https://jcenter.bintray.com/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
```
|
||||
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.mamoe</groupId>
|
||||
<artifactId>mirai-core-qqandroid-jvm</artifactId>
|
||||
<version>0.23.0</version> <!-- 替换版本为最新版本 -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
#### Gradle
|
||||
|
||||
Mirai 只发布在 `jcenter`, 因此请确保添加 `jcenter()` 仓库:
|
||||
|
||||
```kotlin
|
||||
repositories{
|
||||
jcenter()
|
||||
}
|
||||
```
|
||||
|
||||
**注意:**
|
||||
Mirai 核心由 API 模块(`mirai-core`)和协议模块组成。
|
||||
只添加 API 模块将无法正常工作。
|
||||
现在只推荐使用 QQAndroid 协议,请参照下文选择对应目标平台的依赖添加。
|
||||
|
||||
**jvm** (JVM 平台源集)
|
||||
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid-jvm:VERSION")
|
||||
```
|
||||
|
||||
**common** (Kotlin 多平台项目的通用源集)
|
||||
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid-common:VERSION")
|
||||
```
|
||||
|
||||
**android** (Android 平台源集)
|
||||
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid-android:VERSION")
|
||||
```
|
||||
|
||||
### 开始使用
|
||||
|
||||
```kotlin
|
||||
val bot = Bot(qqId, password).alsoLogin()
|
||||
bot.subscribeMessages {
|
||||
"你好" reply "你好!"
|
||||
contains("图片"){ File("C:\\image.png").sendAsImage() }
|
||||
}
|
||||
bot.subscribeAlways<MemberPermissionChangedEvent> {
|
||||
if (it.kind == BECOME_OPERATOR)
|
||||
reply("${it.member.id} 成为了管理员")
|
||||
}
|
||||
```
|
116
docs/guide_subscribe_events.md
Normal file
116
docs/guide_subscribe_events.md
Normal file
@ -0,0 +1,116 @@
|
||||
# Mirai Guide - Subscribe Events
|
||||
|
||||
由于Mirai项目在快速推进中,因此内容时有变动,本文档的最后更新日期为```2020-02-29```,对应版本```0.23.0```
|
||||
|
||||
本页面采用Kotlin作为开发语言,**若你希望使用 Java 开发**, 请参阅: [mirai-japt](https://github.com/mamoe/mirai-japt)
|
||||
|
||||
本页面是[Mirai Guide - Getting Started](/docs/guide_getting_started.md)的后续Guide
|
||||
|
||||
## 消息事件-Message Event
|
||||
|
||||
首先我们来回顾上一个Guide的源码
|
||||
|
||||
```kotlin
|
||||
suspend fun main() {
|
||||
val qqId = 10000L//Bot的QQ号,需为Long类型,在结尾处添加大写L
|
||||
val password = "your_password"//Bot的密码
|
||||
val miraiBot = Bot(qqId, password).alsoLogin()//新建Bot并登录
|
||||
miraiBot.subscribeMessages {
|
||||
"你好" reply "你好!"
|
||||
case("at me") {
|
||||
reply(sender.at() + " 给爷爬 ")
|
||||
}
|
||||
|
||||
(contains("舔") or contains("刘老板")) {
|
||||
"刘老板太强了".reply()
|
||||
}
|
||||
}
|
||||
miraiBot.join() // 等待 Bot 离线, 避免主线程退出
|
||||
}
|
||||
```
|
||||
|
||||
在本例中,```miraiBot```是一个Bot对象,让其登录,然后对```Message Event```进行了监听。
|
||||
|
||||
对于``````Message Event``````,```Mirai```提供了较其他Event更强大的[MessageSubscribersBuilder](https://github.com/mamoe/mirai/wiki/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt#L140),本例也采用了[MessageSubscribersBuilder](https://github.com/mamoe/mirai/wiki/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt#L140)。其他具体使用方法可以参考[Wiki:Message Event](https://github.com/mamoe/mirai/wiki/Development-Guide---Kotlin#Message-Event)部分。
|
||||
|
||||
|
||||
|
||||
## 事件-Event
|
||||
|
||||
上一节中提到的```Message Event```仅仅是众多```Event```的这一种,其他```Event```有群员加入群,离开群,私聊等等...
|
||||
|
||||
具体doc暂不提供,现可翻阅源码[**BotEvents.kt**](https://github.com/mamoe/mirai/blob/master/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt),查看注释。当前事件仍在扩充中,可能有一定不足。
|
||||
|
||||
下面我们开始示例对一些事件进行监听。
|
||||
|
||||
|
||||
|
||||
## 尝试监听事件-Try Subscribing Events
|
||||
|
||||
### 监听加群事件
|
||||
|
||||
在代码中的```miraiBot.join()```前添加
|
||||
|
||||
```kotlin
|
||||
miraiBot.subscribeAlways<MemberJoinEvent> {
|
||||
it.group.sendMessage("欢迎 ${it.member.nameCardOrNick} 加入本群!")
|
||||
}
|
||||
```
|
||||
|
||||
本段语句监听了加入群的事件。
|
||||
|
||||
### 监听禁言事件
|
||||
|
||||
在代码中添加
|
||||
|
||||
```kotlin
|
||||
miraiBot.subscribeAlways<MemberMuteEvent> (){
|
||||
it.group.sendMessage("恭喜老哥 ${it.member.nameCardOrNick} 喜提禁言套餐一份")
|
||||
}
|
||||
```
|
||||
|
||||
在被禁言后,Bot将发送恭喜语句。
|
||||
|
||||
### 添加后的可执行代码
|
||||
|
||||
至此,当前的代码为
|
||||
|
||||
```kotlin
|
||||
package net.mamoe.mirai.simpleloader
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.alsoLogin
|
||||
import net.mamoe.mirai.event.subscribeMessages
|
||||
import net.mamoe.mirai.contact.nameCardOrNick
|
||||
import net.mamoe.mirai.contact.sendMessage
|
||||
import net.mamoe.mirai.event.events.MemberJoinEvent
|
||||
import net.mamoe.mirai.event.events.MemberMuteEvent
|
||||
import net.mamoe.mirai.event.subscribeAlways
|
||||
|
||||
suspend fun main() {
|
||||
val qqId = 10000L//Bot的QQ号,需为Long类型,在结尾处添加大写L
|
||||
val password = "your_password"//Bot的密码
|
||||
val miraiBot = Bot(qqId, password).alsoLogin()//新建Bot并登录
|
||||
miraiBot.subscribeMessages {
|
||||
"你好" reply "你好!"
|
||||
case("at me") {
|
||||
reply(sender.at() + " 给爷爬 ")
|
||||
}
|
||||
|
||||
(contains("舔") or contains("刘老板")) {
|
||||
"刘老板太强了".reply()
|
||||
}
|
||||
}
|
||||
miraiBot.subscribeAlways<MemberJoinEvent> {
|
||||
it.group.sendMessage("欢迎 ${it.member.nameCardOrNick} 加入本群!")
|
||||
}
|
||||
miraiBot.subscribeAlways<MemberMuteEvent> (){
|
||||
it.group.sendMessage("恭喜老哥 ${it.member.nameCardOrNick} 喜提禁言套餐一份")
|
||||
}
|
||||
miraiBot.join() // 等待 Bot 离线, 避免主线程退出
|
||||
}
|
||||
```
|
||||
|
||||
下面可以参阅[Mirai Guide - Build For Mirai](/docs/guide_build_for_mirai.md),对你的Mirai应用进行打包
|
||||
|
@ -1,20 +1,16 @@
|
||||
# style guide
|
||||
kotlin.code.style=official
|
||||
# config
|
||||
mirai_version=0.15.0
|
||||
mirai_japt_version=1.0.0
|
||||
miraiVersion=0.27.0
|
||||
kotlin.incremental.multiplatform=true
|
||||
kotlin.parallel.tasks.in.project=true
|
||||
# kotlin
|
||||
kotlinVersion=1.3.61
|
||||
kotlinVersion=1.3.70
|
||||
# kotlin libraries
|
||||
serializationVersion=0.14.0
|
||||
coroutinesVersion=1.3.3
|
||||
serializationVersion=0.20.0
|
||||
coroutinesVersion=1.3.4
|
||||
atomicFuVersion=0.14.1
|
||||
kotlinXIoVersion=0.1.16
|
||||
coroutinesIoVersion=0.1.16
|
||||
# utility
|
||||
ktorVersion=1.3.1
|
||||
klockVersion=1.7.0
|
||||
# gradle plugin
|
||||
protobufJavaVersion=3.10.0
|
104
gradle/publish-japt.gradle
Normal file
104
gradle/publish-japt.gradle
Normal file
@ -0,0 +1,104 @@
|
||||
// 部分源码来自 kotlinx.coroutines
|
||||
|
||||
def pomConfig = {
|
||||
licenses {
|
||||
license {
|
||||
name "AGPL-V3"
|
||||
url "https://www.gnu.org/licenses/agpl-3.0.txt"
|
||||
distribution "repo"
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id "mamoe"
|
||||
name "Mamoe Technologies"
|
||||
}
|
||||
}
|
||||
scm {
|
||||
url "https://github.com/mamoe/mirai"
|
||||
}
|
||||
}
|
||||
|
||||
bintray {
|
||||
def keyProps = new Properties()
|
||||
def keyFile = file("../keys.properties")
|
||||
if (keyFile.exists()) keyFile.withInputStream { keyProps.load(it) }
|
||||
|
||||
user = keyProps.getProperty("bintrayUser")
|
||||
key = keyProps.getProperty("bintrayKey")
|
||||
|
||||
pkg {
|
||||
repo = 'mirai'
|
||||
name = "mirai-japt"
|
||||
licenses = ['AGPL']
|
||||
vcsUrl = 'https://github.com/mamoe/mirai'
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
project.publishing.publications.forEach { publication ->
|
||||
publication.pom.withXml {
|
||||
def root = asNode()
|
||||
//root.appendNode('groupId', project.group)
|
||||
//root.appendNode('artifactId', project.name)
|
||||
//root.appendNode('version', project.version)
|
||||
root.appendNode('name', project.name)
|
||||
root.appendNode('description', project.description)
|
||||
root.appendNode('url', 'https://github.com/mamoe/mirai')
|
||||
root.children().last() + pomConfig
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bintrayUpload.doFirst {
|
||||
publications = project.publishing.publications
|
||||
}
|
||||
|
||||
bintrayUpload.dependsOn {
|
||||
def list = new LinkedList<Task>()
|
||||
list.add(tasks.getByName("build"))
|
||||
|
||||
list.addAll(tasks.findAll { task -> task.name.contains('Jar') })
|
||||
list.addAll(tasks.findAll { task -> task.name.startsWith('generateMetadataFileFor') })
|
||||
list.addAll(tasks.findAll { task -> task.name.startsWith('generatePomFileFor') })
|
||||
|
||||
list
|
||||
}
|
||||
|
||||
|
||||
// empty xxx-javadoc.jar
|
||||
task javadocJar(type: Jar) {
|
||||
archiveClassifier = 'javadoc'
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.all {
|
||||
// add empty javadocs (no need for MPP root publication which publishes only pom file)
|
||||
if (it.name != 'kotlinMultiplatform') {
|
||||
it.artifact(javadocJar)
|
||||
}
|
||||
|
||||
// Rename MPP artifacts for backward compatibility
|
||||
def type = it.name
|
||||
switch (type) {
|
||||
case 'kotlinMultiplatform':
|
||||
it.artifactId = "$project.name"
|
||||
break
|
||||
case 'metadata':
|
||||
it.artifactId = "$project.name-common"
|
||||
break
|
||||
case 'jvm':
|
||||
it.artifactId = "$project.name"
|
||||
break
|
||||
case 'js':
|
||||
case 'native':
|
||||
it.artifactId = "$project.name-$type"
|
||||
break
|
||||
}
|
||||
|
||||
// disable metadata everywhere, but in native modules
|
||||
if (type == 'maven' || type == 'metadata' || type == 'jvm' || type == 'js') {
|
||||
moduleDescriptorGenerator = null
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
// 部分源码来自 kotlinx.coroutines
|
||||
// Source code from kotlinx.coroutines
|
||||
|
||||
def pomConfig = {
|
||||
licenses {
|
||||
@ -12,6 +13,7 @@ def pomConfig = {
|
||||
developer {
|
||||
id "mamoe"
|
||||
name "Mamoe Technologies"
|
||||
email "support@mamoe.net"
|
||||
}
|
||||
}
|
||||
scm {
|
||||
@ -65,12 +67,16 @@ bintrayUpload.dependsOn {
|
||||
list
|
||||
}
|
||||
|
||||
try{
|
||||
|
||||
// empty xxx-javadoc.jar
|
||||
task javadocJar(type: Jar) {
|
||||
archiveClassifier = 'javadoc'
|
||||
}
|
||||
|
||||
} catch (Exception e){
|
||||
|
||||
}
|
||||
publishing {
|
||||
publications.all {
|
||||
// add empty javadocs (no need for MPP root publication which publishes only pom file)
|
||||
@ -88,7 +94,7 @@ publishing {
|
||||
it.artifactId = "$project.name-common"
|
||||
break
|
||||
case 'jvm':
|
||||
it.artifactId = "$project.name"
|
||||
it.artifactId = "$project.name-jvm"
|
||||
break
|
||||
case 'js':
|
||||
case 'native':
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
#Thu Feb 06 14:10:33 CST 2020
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
|
@ -1,58 +0,0 @@
|
||||
# mirai-api-http
|
||||
|
||||
<b>
|
||||
Mirai-API-http provides adapter for ALL langugae to access mirai via HTTP protocol.<br>
|
||||
</b>
|
||||
|
||||
**[中文](README_CH.md)**
|
||||
|
||||
|
||||
### Start Session-Authorize
|
||||
|
||||
```php
|
||||
Path: /auth
|
||||
Method: POST
|
||||
```
|
||||
this verify your session to one bot and you could have full access to that bot<br>
|
||||
NOTE that only 1 bot could be control under 1 session, you could have multiple session to control all bots.
|
||||
|
||||
#### Request:<br>
|
||||
|
||||
| name | type | optional|example|note|
|
||||
| --- | --- | --- | --- | --- |
|
||||
| key | String |false|U9HSaDXl39ksd918273hU|MIRAI API HTTP key, this could be found after initialize|
|
||||
| qq | String |false|1040400290|bot QQ number you want to access|
|
||||
|
||||
|
||||
#### Response if success:<br>
|
||||
|
||||
| name | type | example|note|
|
||||
| --- | --- | --- | --- |
|
||||
| success |Boolean |true|if this session is authorized|
|
||||
| session |String |UANSHDKSLAOISN|your session key|
|
||||
|
||||
|
||||
#### Response if failed:<br>
|
||||
|
||||
| name | type | example|note|
|
||||
| --- | --- | --- | --- |
|
||||
| success |Boolean |false|if this session is authorized|
|
||||
| session |String |null|your session key|
|
||||
| error |int |0|error code|
|
||||
|
||||
#### Error:<br>
|
||||
|
||||
| code | reason|
|
||||
| --- | --- |
|
||||
| 0 | wrong MIRAI API HTTP key |
|
||||
| 1 | unknown bot number |
|
||||
|
||||
|
||||
without session key, you are not able to access any method below.</br>
|
||||
session key should be attached to your <b>cookies</b> like this:
|
||||
|
||||
| name | value |
|
||||
| --- | --- |
|
||||
| session |your session key here |
|
||||
|
||||
if you were getting HTTP error code 403, you should ask for a new session key.
|
@ -1,882 +0,0 @@
|
||||
# mirai-api-http
|
||||
|
||||
<b>Mirai-API-http 提供HTTP API供所有语言使用mirai</b>
|
||||
|
||||
## 快速开始
|
||||
|
||||
```kotlin
|
||||
fun main() {
|
||||
val bot = Bot(123456789, "password")
|
||||
|
||||
bot.login()
|
||||
|
||||
MiraiHttpAPIServer.start()
|
||||
|
||||
bot.network.awaitDisconnection()
|
||||
}
|
||||
```
|
||||
|
||||
## 认证相关
|
||||
|
||||
### 开始会话-认证(Authorize)
|
||||
|
||||
```
|
||||
[POST] /auth
|
||||
```
|
||||
使用此方法验证你的身份,并返回一个会话
|
||||
|
||||
#### 请求:
|
||||
|
||||
```json5
|
||||
{
|
||||
"authKey": "U9HSaDXl39ksd918273hU"
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
||||
| ------- | ------ | ----- | ----------------------- | ---------------------------------------------------------- |
|
||||
| authKey | String | false | "U9HSaDXl39ksd918273hU" | 创建Mirai-Http-Server时生成的key,可在启动时指定或随机生成 |
|
||||
|
||||
#### 响应: 返回(成功):
|
||||
|
||||
```json5
|
||||
{
|
||||
"code": 0,
|
||||
"session": "UnVerifiedSession"
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 举例 | 说明 |
|
||||
| ------- | ------ | ------------------- | --------------- |
|
||||
| code | Int | 0 | 返回状态码 |
|
||||
| session | String | "UnVerifiedSession" | 你的session key |
|
||||
|
||||
#### 状态码:
|
||||
|
||||
| 代码 | 原因 |
|
||||
| ---- | ----------------------------- |
|
||||
| 0 | 正常 |
|
||||
| 1 | 错误的MIRAI API HTTP auth key |
|
||||
|
||||
session key 是使用以下方法必须携带的
|
||||
session key 使用前必须进行校验和绑定指定的Bot,**每个Session只能绑定一个Bot,但一个Bot可有多个Session**
|
||||
session Key 在未进行校验的情况下,一定时间后将会被自动释放
|
||||
|
||||
|
||||
### 校验Session
|
||||
|
||||
```
|
||||
[POST] /verify
|
||||
```
|
||||
|
||||
使用此方法校验并激活你的Session,同时将Session与一个**已登录**的Bot绑定
|
||||
|
||||
#### 请求:
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "UnVerifiedSession",
|
||||
"qq": 123456789
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
||||
| ---------- | ------ | ----- | ------------------- | -------------------------- |
|
||||
| sessionKey | String | false | "UnVerifiedSession" | 你的session key |
|
||||
| qq | Long | false | 123456789 | Session将要绑定的Bot的qq号 |
|
||||
|
||||
#### 响应: 返回统一状态码(后续不再赘述)
|
||||
|
||||
```json5
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success"
|
||||
}
|
||||
```
|
||||
|
||||
| 状态码 | 原因 |
|
||||
| ------ | ----------------------------------- |
|
||||
| 0 | 正常 |
|
||||
| 1 | 错误的auth key |
|
||||
| 2 | 指定的Bot不存在 |
|
||||
| 3 | Session失效或不存在 |
|
||||
| 4 | Session未认证(未激活) |
|
||||
| 5 | 发送消息目标不存在(指定对象不存在) |
|
||||
| 10 | 无操作权限,指Bot没有对应操作的限权 |
|
||||
| 400 | 错误的访问,如参数错误等 |
|
||||
|
||||
|
||||
|
||||
### 释放Session
|
||||
|
||||
```
|
||||
[POST] /release
|
||||
```
|
||||
|
||||
使用此方式释放session及其相关资源(Bot不会被释放)
|
||||
**不使用的Session应当被释放,否则Session持续保存Bot收到的消息,将会导致内存泄露**
|
||||
|
||||
#### 请求:
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "YourSessionKey",
|
||||
"qq": 123456789
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
||||
| ---------- | ------ | ----- | -----------------| -------------------------- |
|
||||
| sessionKey | String | false | "YourSessionKey" | 你的session key |
|
||||
| qq | Long | false | 123456789 | 与该Session绑定Bot的QQ号码 |
|
||||
|
||||
#### 响应: 返回统一状态码
|
||||
|
||||
```json5
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success"
|
||||
}
|
||||
```
|
||||
> SessionKey与Bot 对应错误时将会返回状态码5:指定对象不存在
|
||||
|
||||
|
||||
## 消息相关
|
||||
|
||||
|
||||
### 发送好友消息
|
||||
|
||||
```
|
||||
[POST] /sendFriendMessage
|
||||
```
|
||||
|
||||
使用此方法向指定好友发送消息
|
||||
|
||||
#### 请求
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "YourSession",
|
||||
"target": 987654321,
|
||||
"messageChain": [
|
||||
{ "type": "Plain", "text":"hello\n" },
|
||||
{ "type": "Plain", "text":"world" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
||||
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
||||
| target | Long | false | 987654321 | 发送消息目标好友的QQ号 |
|
||||
| messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 |
|
||||
|
||||
#### 响应: 返回统一状态码
|
||||
|
||||
```json5
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 发送群消息
|
||||
|
||||
```
|
||||
[POST] /sendGroupMessage
|
||||
```
|
||||
|
||||
使用此方法向指定群发送消息
|
||||
|
||||
#### 请求
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "YourSession",
|
||||
"target": 987654321,
|
||||
"messageChain": [
|
||||
{ "type": "Plain", "text":"hello\n" },
|
||||
{ "type": "Plain", "text":"world" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
||||
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
||||
| target | Long | false | 987654321 | 发送消息目标群的群号 |
|
||||
| messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 |
|
||||
|
||||
#### 响应: 返回统一状态码
|
||||
|
||||
```json5
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 发送引用回复消息(仅支持群消息)
|
||||
|
||||
```
|
||||
[POST] /sendQuoteMessage
|
||||
```
|
||||
|
||||
使用此方法向指定的消息进行引用回复
|
||||
|
||||
#### 请求
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "YourSession",
|
||||
"target": 987654321,
|
||||
"messageChain": [
|
||||
{ "type": "Plain", "text":"hello\n" },
|
||||
{ "type": "Plain", "text":"world" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
||||
| ------------ | ------ | ----- | ----------- | -------------------------------- |
|
||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
||||
| target | Long | false | 987654321 | 引用消息的Message Source的Uid |
|
||||
| messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 |
|
||||
|
||||
#### 响应: 返回统一状态码
|
||||
|
||||
```json5
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 发送图片消息(通过URL)
|
||||
|
||||
```
|
||||
[POST] /sendGroupMessage
|
||||
```
|
||||
|
||||
使用此方法向指定群发送消息
|
||||
|
||||
#### 请求
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "YourSession",
|
||||
"target": 987654321,
|
||||
"qq": 1234567890,
|
||||
"group": 987654321,
|
||||
"urls": [
|
||||
"https://xxx.yyy.zzz/",
|
||||
"https://aaa.bbb.ccc/"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
||||
| ------------ | ------ | ----- | ----------- | ---------------------------------- |
|
||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
||||
| target | Long | true | 987654321 | 发送对象的QQ号或群号,可能存在歧义 |
|
||||
| qq | Long | true | 123456789 | 发送对象的QQ号 |
|
||||
| group | Long | true | 987654321 | 发送对象的群号 |
|
||||
| urls | Array | false | [] | 是一个url字符串构成的数组 |
|
||||
|
||||
#### 响应: 图片的imageId数组
|
||||
|
||||
```json5
|
||||
[
|
||||
"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}.jpg",
|
||||
"{YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY}.jpg"
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 图片文件上传
|
||||
|
||||
```
|
||||
[POST] /sendGroupMessage
|
||||
```
|
||||
|
||||
使用此方法上传图片文件至服务器并返回ImageId
|
||||
|
||||
#### 请求
|
||||
|
||||
Content-Type:multipart/form-data
|
||||
|
||||
| 名字 | 类型 | 可选 | 举例 | 说明 |
|
||||
| ------------ | ------ | ----- | ----------- | ---------------------------------- |
|
||||
| sessionKey | String | false | YourSession | 已经激活的Session |
|
||||
| type | String | false | "friend " | "friend" 或 "group" |
|
||||
| img | File | false | - | 图片文件 |
|
||||
|
||||
|
||||
#### 响应: 图片的imageId(好友图片与群聊图片Id不同)
|
||||
|
||||
```
|
||||
{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}.jpg
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 获取Bot收到的消息
|
||||
|
||||
```
|
||||
[GET] /fetchMessage?sessionKey=YourSessionKey&count=10
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
| 名字 | 可选 | 举例 | 说明 |
|
||||
| ---------- | ----- | -------------- | --------------- |
|
||||
| sessionKey | false | YourSessionKey | 你的session key |
|
||||
| count | false | 10 | 获取消息的数量 |
|
||||
|
||||
#### 响应: 返回JSON对象
|
||||
|
||||
```json5
|
||||
[{
|
||||
"type": "GroupMessage", // 消息类型:GroupMessage或FriendMessage
|
||||
"messageChain": [{ // 消息链,是一个消息对象构成的数组
|
||||
"type": "Source",
|
||||
"uid": 123456
|
||||
},{
|
||||
"type": "Plain",
|
||||
"text": "Miral牛逼"
|
||||
}],
|
||||
"sender": { // 发送者信息
|
||||
"id": 123456789, // 发送者的QQ号码
|
||||
"memberName": "化腾", // 发送者的群名片
|
||||
"permission": "MEMBER", // 发送者的群限权:OWNER、ADMINISTRATOR或MEMBER
|
||||
"group": { // 消息发送群的信息
|
||||
"id": 1234567890, // 发送群的群号
|
||||
"name": "Miral Technology", // 发送群的群名称
|
||||
"permission": "MEMBER" // 发送群中,Bot的群限权
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "FriendMessage", // 消息类型:GroupMessage或FriendMessage
|
||||
"messageChain": [{ // 消息链,是一个消息对象构成的数组
|
||||
"type": "Plain",
|
||||
"text": "Miral牛逼"
|
||||
}],
|
||||
"sender": { // 发送者信息
|
||||
"id": 1234567890, // 发送者的QQ号码
|
||||
"nickName": "", // 发送者的昵称
|
||||
"remark": "" // 发送者的备注
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 消息类型一览
|
||||
|
||||
#### 消息是构成消息链的基本对象,目前支持的消息类型有
|
||||
|
||||
+ [x] At,@消息
|
||||
+ [x] AtAll,@全体成员
|
||||
+ [x] Face,表情消息
|
||||
+ [x] Plain,文字消息
|
||||
+ [x] Image,图片消息
|
||||
+ [ ] Xml,Xml卡片消息
|
||||
+ [ ] 敬请期待
|
||||
|
||||
#### Source
|
||||
|
||||
```json5
|
||||
{
|
||||
"type": "Source",
|
||||
"uid": 123456
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 说明 |
|
||||
| ---- | ---- | ------------------------------------------------------------ |
|
||||
| uid | Long | 消息的识别号,用于引用回复(Source类型只在群消息中返回,且永远为chain的第一个元素) |
|
||||
|
||||
#### At
|
||||
|
||||
```json5
|
||||
{
|
||||
"type": "At",
|
||||
"target": 123456,
|
||||
"display": "@Mirai"
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 说明 |
|
||||
| ------- | ------ | ------------------------- |
|
||||
| target | Long | 群员QQ号 |
|
||||
| display | String | @时显示的文本如:"@Mirai" |
|
||||
|
||||
#### AtAll
|
||||
|
||||
```json5
|
||||
{
|
||||
"type": "AtAll"
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 说明 |
|
||||
| ------- | ------ | ------------------------- |
|
||||
| - | - | - |
|
||||
|
||||
#### Face
|
||||
|
||||
```json5
|
||||
{
|
||||
"type": "Face",
|
||||
"faceId": 123
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 说明 |
|
||||
| ------ | ---- | ---------- |
|
||||
| faceId | Int | QQ表情编号 |
|
||||
|
||||
#### Plain
|
||||
|
||||
```json5
|
||||
{
|
||||
"type": "Plain",
|
||||
"text": "Mirai牛逼"
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 说明 |
|
||||
| ---- | ------ | -------- |
|
||||
| text | String | 文字消息 |
|
||||
|
||||
#### Image
|
||||
|
||||
```json5
|
||||
{
|
||||
"type": "Image"
|
||||
// 暂时不支持Image
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 说明 |
|
||||
| ---- | ---- | ---- |
|
||||
| | | |
|
||||
|
||||
#### Xml
|
||||
|
||||
```json5
|
||||
{
|
||||
"type": "Xml",
|
||||
"xml": "XML"
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 类型 | 说明 |
|
||||
| ---- | ------ | ------- |
|
||||
| xml | String | XML文本 |
|
||||
|
||||
|
||||
|
||||
## 管理相关
|
||||
|
||||
### 获取好友列表
|
||||
|
||||
使用此方法获取bot的好友列表
|
||||
|
||||
```
|
||||
[GET] /friendList?sessionKey=YourSessionKey
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
| 名字 | 可选 | 举例 | 说明 |
|
||||
| ---------- | ----- | -------------- | --------------- |
|
||||
| sessionKey | false | YourSessionKey | 你的session key |
|
||||
|
||||
#### 响应: 返回JSON对象
|
||||
|
||||
```json5
|
||||
[{
|
||||
"id":123456789,
|
||||
"nickName":"",
|
||||
"remark":""
|
||||
},{
|
||||
"id":987654321,
|
||||
"nickName":"",
|
||||
"remark":""
|
||||
}]
|
||||
```
|
||||
|
||||
|
||||
### 获取群列表
|
||||
|
||||
使用此方法获取bot的群列表
|
||||
|
||||
```
|
||||
[GET] /groupList?sessionKey=YourSessionKey
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
| 名字 | 可选 | 举例 | 说明 |
|
||||
| ---------- | ----- | -------------- | --------------- |
|
||||
| sessionKey | false | YourSessionKey | 你的session key |
|
||||
|
||||
#### 响应: 返回JSON对象
|
||||
|
||||
```json5
|
||||
[{
|
||||
"id":123456789,
|
||||
"name":"群名1",
|
||||
"permission": "MEMBER"
|
||||
},{
|
||||
"id":987654321,
|
||||
"name":"群名2",
|
||||
"permission": "MEMBER"
|
||||
}]
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 获取群成员列表
|
||||
|
||||
使用此方法获取bot指定群种的成员列表
|
||||
|
||||
```
|
||||
[GET] /memberList?sessionKey=YourSessionKey
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
| 名字 | 可选 | 举例 | 说明 |
|
||||
| ---------- | ----- | -------------- | --------------- |
|
||||
| sessionKey | false | YourSessionKey | 你的session key |
|
||||
| target | false | 123456789 | 指定群的群号 |
|
||||
|
||||
#### 响应: 返回JSON对象
|
||||
|
||||
```json5
|
||||
[{
|
||||
"id":1234567890,
|
||||
"memberName":"",
|
||||
"permission":"MEMBER",
|
||||
"group":{
|
||||
"id":12345,
|
||||
"name":"群名1",
|
||||
"permission": "MEMBER"
|
||||
}
|
||||
},{
|
||||
"id":9876543210,
|
||||
"memberName":"",
|
||||
"permission":"OWNER",
|
||||
"group":{
|
||||
"id":54321,
|
||||
"name":"群名2",
|
||||
"permission": "MEMBER"
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 群全体禁言
|
||||
|
||||
使用此方法令指定群进行全体禁言(需要有相关限权)
|
||||
|
||||
```
|
||||
[POST] /muteAll
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "YourSessionKey",
|
||||
"target": 123456789,
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
||||
| ---------- | ----- | ------ | ---------------- | --------------- |
|
||||
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
||||
| target | false | Long | 123456789 | 指定群的群号 |
|
||||
|
||||
|
||||
#### 响应: 返回统一状态码
|
||||
|
||||
```json5
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 群解除全体禁言
|
||||
|
||||
使用此方法令指定群解除全体禁言(需要有相关限权)
|
||||
|
||||
```
|
||||
[POST] /unmuteAll
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
同全体禁言
|
||||
|
||||
#### 响应
|
||||
|
||||
同全体禁言
|
||||
|
||||
|
||||
|
||||
### 群禁言群成员
|
||||
|
||||
使用此方法指定群禁言指定群员(需要有相关限权)
|
||||
|
||||
```
|
||||
[POST] /mute
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "YourSessionKey",
|
||||
"target": 123456789,
|
||||
"memberId": 987654321,
|
||||
"time": 1800
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
||||
| ---------- | ----- | ------ | ---------------- | ------------------------------------- |
|
||||
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
||||
| target | false | Long | 123456789 | 指定群的群号 |
|
||||
| memberId | false | Long | 987654321 | 指定群员QQ号 |
|
||||
| time | true | Int | 1800 | 禁言时长,单位为秒,最多30天,默认为0 |
|
||||
|
||||
#### 响应: 返回统一状态码
|
||||
|
||||
```json5
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 群解除群成员禁言
|
||||
|
||||
使用此方法令指定群解除全体禁言(需要有相关限权)
|
||||
|
||||
```
|
||||
[POST] /unmute
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "YourSessionKey",
|
||||
"target": 123456789,
|
||||
"memberId": 987654321
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应
|
||||
|
||||
同群禁言群成员
|
||||
|
||||
|
||||
|
||||
### 移除群成员
|
||||
|
||||
使用此方法移除指定群成员(需要有相关限权)
|
||||
|
||||
```
|
||||
[POST] /kick
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "YourSessionKey",
|
||||
"target": 123456789,
|
||||
"memberId": 987654321,
|
||||
"msg": "您已被移出群聊"
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
||||
| ---------- | ----- | ------ | ---------------- | --------------- |
|
||||
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
||||
| target | false | Long | 123456789 | 指定群的群号 |
|
||||
| memberId | false | Long | 987654321 | 指定群员QQ号 |
|
||||
| msg | true | String | "" | 信息 |
|
||||
|
||||
#### 响应
|
||||
|
||||
#### 响应: 返回统一状态码
|
||||
|
||||
```json5
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 群设置
|
||||
|
||||
使用此方法修改群设置(需要有相关限权)
|
||||
|
||||
```
|
||||
[POST] /groupConfig
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "YourSessionKey",
|
||||
"target": 123456789,
|
||||
"config": {
|
||||
"name": "群名称",
|
||||
"announcement": "群公告",
|
||||
"confessTalk": true,
|
||||
"allowMemberInvite": true,
|
||||
"autoApprove": true,
|
||||
"anonymousChat": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
||||
| ----------------- | ----- | ------- | ---------------- | -------------------- |
|
||||
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
||||
| target | false | Long | 123456789 | 指定群的群号 |
|
||||
| config | false | Object | {} | 群设置 |
|
||||
| name | true | String | "Name" | 群名 |
|
||||
| announcement | true | Boolean | true | 群公告 |
|
||||
| confessTalk | true | Boolean | true | 是否开启坦白说 |
|
||||
| allowMemberInvite | true | Boolean | true | 是否运行群员邀请 |
|
||||
| autoApprove | true | Boolean | true | 是否开启自动审批入群 |
|
||||
| anonymousChat | true | Boolean | true | 是否允许匿名聊天 |
|
||||
|
||||
#### 响应: 返回统一状态码
|
||||
|
||||
```json5
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success"
|
||||
}
|
||||
```
|
||||
|
||||
### 获取群设置
|
||||
|
||||
使用此方法获取群设置
|
||||
|
||||
```
|
||||
[Get] /groupConfig?sessionKey=YourSessionKey&target=123456789
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
||||
| ----------------- | ----- | ------- | ---------------- | -------------------- |
|
||||
| sessionKey | false | String | YourSessionKey | 你的session key |
|
||||
| target | false | Long | 123456789 | 指定群的群号 |
|
||||
|
||||
|
||||
#### 响应
|
||||
|
||||
```json5
|
||||
{
|
||||
"name": "群名称",
|
||||
"announcement": "群公告",
|
||||
"confessTalk": true,
|
||||
"allowMemberInvite": true,
|
||||
"autoApprove": true,
|
||||
"anonymousChat": true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 修改群员资料
|
||||
|
||||
使用此方法修改群员资料(需要有相关限权)
|
||||
|
||||
```
|
||||
[POST] /memberInfo
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
```json5
|
||||
{
|
||||
"sessionKey": "YourSessionKey",
|
||||
"target": 123456789,
|
||||
"memberId": 987654321,
|
||||
"info": {
|
||||
"name": "群名片",
|
||||
"specialTitle": "群头衔"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
||||
| ----------------- | ----- | ------- | ---------------- | -------------------- |
|
||||
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
||||
| target | false | Long | 123456789 | 指定群的群号 |
|
||||
| memberId | false | Long | 987654321 | 群员QQ号 |
|
||||
| info | false | Object | {} | 群员资料 |
|
||||
| name | true | String | "Name" | 群名片,即群昵称 |
|
||||
| specialTitle | true | String | "Title" | 群头衔 |
|
||||
|
||||
#### 响应: 返回统一状态码
|
||||
|
||||
```json5
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success"
|
||||
}
|
||||
```
|
||||
|
||||
### 获取群员资料
|
||||
|
||||
使用此方法获取群员资料
|
||||
|
||||
```
|
||||
[Get] /groupConfig?sessionKey=YourSessionKey&target=123456789
|
||||
```
|
||||
|
||||
#### 请求:
|
||||
|
||||
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
||||
| ----------------- | ----- | ------- | ---------------- | -------------------- |
|
||||
| sessionKey | false | String | YourSessionKey | 你的session key |
|
||||
| target | false | Long | 123456789 | 指定群的群号 |
|
||||
| memberId | false | Long | 987654321 | 群员QQ号 |
|
||||
|
||||
|
||||
#### 响应
|
||||
|
||||
```json5
|
||||
{
|
||||
"name": "群名片",
|
||||
"announcement": "群头衔"
|
||||
}
|
||||
```
|
@ -1,75 +0,0 @@
|
||||
@file:Suppress("UNUSED_VARIABLE")
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
||||
|
||||
plugins {
|
||||
id("kotlinx-atomicfu")
|
||||
kotlin("jvm")
|
||||
id("kotlinx-serialization")
|
||||
}
|
||||
|
||||
group = "net.mamoe.mirai"
|
||||
version = rootProject.ext["mirai_version"].toString()
|
||||
|
||||
description = "Mirai Http Api"
|
||||
|
||||
val kotlinVersion: String by rootProject.ext
|
||||
val atomicFuVersion: String by rootProject.ext
|
||||
val coroutinesVersion: String by rootProject.ext
|
||||
val kotlinXIoVersion: String by rootProject.ext
|
||||
val coroutinesIoVersion: String by rootProject.ext
|
||||
|
||||
val klockVersion: String by rootProject.ext
|
||||
val ktorVersion: String by rootProject.ext
|
||||
|
||||
val serializationVersion: String by rootProject.ext
|
||||
|
||||
fun KotlinDependencyHandler.kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$version"
|
||||
|
||||
fun KotlinDependencyHandler.ktor(id: String, version: String = ktorVersion) = "io.ktor:ktor-$id:$version"
|
||||
|
||||
kotlin {
|
||||
|
||||
|
||||
sourceSets["main"].apply {
|
||||
dependencies {
|
||||
implementation(project(":mirai-core-qqandroid"))
|
||||
|
||||
implementation(kotlin("stdlib-jdk8", kotlinVersion))
|
||||
implementation(kotlin("stdlib-jdk7", kotlinVersion))
|
||||
implementation(kotlin("reflect", kotlinVersion))
|
||||
|
||||
implementation(ktor("server-cio"))
|
||||
implementation(kotlinx("io-jvm", kotlinXIoVersion))
|
||||
implementation(ktor("http-jvm"))
|
||||
implementation("org.slf4j:slf4j-simple:1.7.26")
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets["test"].apply {
|
||||
dependencies {
|
||||
}
|
||||
kotlin.outputDir = file("build/classes/kotlin/jvm/test")
|
||||
kotlin.setSrcDirs(listOf("src/$name/kotlin"))
|
||||
|
||||
}
|
||||
|
||||
sourceSets.all {
|
||||
languageSettings.enableLanguageFeature("InlineClasses")
|
||||
|
||||
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib", kotlinVersion))
|
||||
implementation(kotlin("serialization", kotlinVersion))
|
||||
|
||||
implementation("org.jetbrains.kotlinx:atomicfu:$atomicFuVersion")
|
||||
implementation(kotlinx("io", kotlinXIoVersion))
|
||||
implementation(kotlinx("coroutines-io", coroutinesIoVersion))
|
||||
implementation(kotlinx("coroutines-core", coroutinesVersion))
|
||||
implementation(kotlinx("serialization-runtime", serializationVersion))
|
||||
implementation(ktor("server-core"))
|
||||
implementation(ktor("http"))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.api.http
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.applicationEngineEnvironment
|
||||
import io.ktor.server.engine.connector
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import net.mamoe.mirai.api.http.route.mirai
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.slf4j.helpers.NOPLogger
|
||||
import org.slf4j.helpers.NOPLoggerFactory
|
||||
|
||||
object MiraiHttpAPIServer {
|
||||
|
||||
private val logger = DefaultLogger("Mirai HTTP API")
|
||||
|
||||
init {
|
||||
SessionManager.authKey = generateSessionKey()//用于验证的key, 使用和SessionKey相同的方法生成, 但意义不同
|
||||
}
|
||||
|
||||
fun setAuthKey(key: String) {
|
||||
SessionManager.authKey = key
|
||||
}
|
||||
|
||||
@UseExperimental(KtorExperimentalAPI::class)
|
||||
fun start(
|
||||
port: Int = 8080,
|
||||
authKey: String,
|
||||
callback: (() -> Unit)? = null
|
||||
) {
|
||||
require(authKey.length in 8..128) { "Expected authKey length is between 8 to 128" }
|
||||
SessionManager.authKey = authKey
|
||||
|
||||
// TODO: start是无阻塞的,理应获取启动状态后再执行后续代码
|
||||
try {
|
||||
embeddedServer(CIO, environment = applicationEngineEnvironment {
|
||||
this.log = NOPLoggerFactory().getLogger("NMYSL")
|
||||
this.module(Application::mirai)
|
||||
|
||||
connector {
|
||||
this.port = port
|
||||
}
|
||||
}).start()
|
||||
|
||||
logger.info("Http api server is running with authKey: ${SessionManager.authKey}")
|
||||
callback?.invoke()
|
||||
} catch (e: Exception) {
|
||||
logger.error("Http api server launch error")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.api.http
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.api.http.queue.MessageQueue
|
||||
import net.mamoe.mirai.event.Listener
|
||||
import net.mamoe.mirai.event.subscribeMessages
|
||||
import net.mamoe.mirai.message.MessagePacket
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
tailrec fun generateSessionKey(): String {
|
||||
fun generateRandomSessionKey(): String {
|
||||
val all = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm"
|
||||
return buildString(capacity = 8) {
|
||||
repeat(8) {
|
||||
append(all.random())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val key = generateRandomSessionKey()
|
||||
if (!SessionManager.allSession.containsKey(key)) {
|
||||
return key
|
||||
}
|
||||
|
||||
return generateSessionKey()
|
||||
}
|
||||
|
||||
internal object SessionManager {
|
||||
|
||||
val allSession: MutableMap<String, Session> = mutableMapOf()
|
||||
|
||||
lateinit var authKey: String
|
||||
|
||||
|
||||
fun createTempSession(): TempSession = TempSession(EmptyCoroutineContext).also { newTempSession ->
|
||||
allSession[newTempSession.key] = newTempSession
|
||||
//设置180000ms后检测并回收
|
||||
newTempSession.launch {
|
||||
delay(180000)
|
||||
allSession[newTempSession.key]?.run {
|
||||
if (this is TempSession)
|
||||
closeSession(newTempSession.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(sessionKey: String) = allSession[sessionKey]
|
||||
|
||||
fun containSession(sessionKey: String): Boolean = allSession.containsKey(sessionKey)
|
||||
|
||||
fun closeSession(sessionKey: String) = allSession.remove(sessionKey)?.also { it.close() }
|
||||
|
||||
fun closeSession(session: Session) = closeSession(session.key)
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @author NaturalHG
|
||||
* 这个用于管理不同Client与Mirai HTTP的会话
|
||||
*
|
||||
* [Session]均为内部操作用类
|
||||
* 需使用[SessionManager]
|
||||
*/
|
||||
abstract class Session internal constructor(
|
||||
coroutineContext: CoroutineContext
|
||||
) : CoroutineScope {
|
||||
val supervisorJob = SupervisorJob(coroutineContext[Job])
|
||||
final override val coroutineContext: CoroutineContext = supervisorJob + coroutineContext
|
||||
|
||||
val key: String = generateSessionKey()
|
||||
|
||||
|
||||
internal open fun close() {
|
||||
supervisorJob.complete()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 任何新链接建立后分配一个[TempSession]
|
||||
*
|
||||
* TempSession在建立180s内没有转变为[AuthedSession]应被清除
|
||||
*/
|
||||
class TempSession internal constructor(coroutineContext: CoroutineContext) : Session(coroutineContext)
|
||||
|
||||
/**
|
||||
* 任何[TempSession]认证后转化为一个[AuthedSession]
|
||||
* 在这一步[AuthedSession]应该已经有assigned的bot
|
||||
*/
|
||||
class AuthedSession internal constructor(val bot: Bot, coroutineContext: CoroutineContext) : Session(coroutineContext) {
|
||||
|
||||
val messageQueue = MessageQueue()
|
||||
private val _listener: Listener<MessagePacket<*, *>>
|
||||
|
||||
init {
|
||||
bot.subscribeMessages {
|
||||
_listener = always { this.run(messageQueue::add) } // this aka messagePacket
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
_listener.complete()
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
package net.mamoe.mirai.api.http.data
|
||||
|
||||
/**
|
||||
* 错误请求. 抛出这个异常后将会返回错误给一个请求
|
||||
*/
|
||||
@Suppress("unused")
|
||||
open class IllegalAccessException : Exception {
|
||||
override val message: String get() = super.message!!
|
||||
|
||||
constructor(message: String) : super(message, null)
|
||||
constructor(cause: Throwable) : super(cause.toString(), cause)
|
||||
constructor(message: String, cause: Throwable?) : super(message, cause)
|
||||
}
|
||||
|
||||
/**
|
||||
* Session失效或不存在
|
||||
*/
|
||||
object IllegalSessionException : IllegalAccessException("Session失效或不存在")
|
||||
|
||||
/**
|
||||
* Session未激活
|
||||
*/
|
||||
object NotVerifiedSessionException : IllegalAccessException("Session未激活")
|
||||
|
||||
/**
|
||||
* 指定Bot不存在
|
||||
*/
|
||||
object NoSuchBotException: IllegalAccessException("指定Bot不存在")
|
||||
|
||||
/**
|
||||
* 指定Bot不存在
|
||||
*/
|
||||
object PermissionDeniedException: IllegalAccessException("无操作限权")
|
||||
|
||||
/**
|
||||
* 错误参数
|
||||
*/
|
||||
class IllegalParamException(message: String) : IllegalAccessException(message)
|
@ -1,21 +0,0 @@
|
||||
package net.mamoe.mirai.api.http.data
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
open class StateCode(val code: Int, var msg: String) {
|
||||
object Success : StateCode(0, "success") // 成功
|
||||
object NoBot : StateCode(2, "指定Bot不存在")
|
||||
object IllegalSession : StateCode(3, "Session失效或不存在")
|
||||
object NotVerifySession : StateCode(4, "Session未认证")
|
||||
object NoElement : StateCode(5, "指定对象不存在")
|
||||
object PermissionDenied : StateCode(10, "无操作权限")
|
||||
|
||||
// KS bug: 主构造器中不能有非字段参数 https://github.com/Kotlin/kotlinx.serialization/issues/575
|
||||
@Serializable
|
||||
class IllegalAccess() : StateCode(400, "") { // 非法访问
|
||||
constructor(msg: String) : this() {
|
||||
this.msg = msg
|
||||
}
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.api.http.data.common
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
|
||||
@Serializable
|
||||
abstract class ContactDTO : DTO {
|
||||
abstract val id: Long
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class QQDTO(
|
||||
override val id: Long,
|
||||
val nickName: String,
|
||||
val remark: String
|
||||
) : ContactDTO() {
|
||||
// TODO: queryProfile.nickname & queryRemark.value not support now
|
||||
constructor(qq: QQ) : this(qq.id, "", "")
|
||||
}
|
||||
|
||||
|
||||
@Serializable
|
||||
data class MemberDTO(
|
||||
override val id: Long,
|
||||
val memberName: String,
|
||||
val permission: MemberPermission,
|
||||
val group: GroupDTO
|
||||
) : ContactDTO() {
|
||||
constructor(member: Member) : this(
|
||||
member.id, member.groupCardOrNick, member.permission,
|
||||
GroupDTO(member.group)
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class GroupDTO(
|
||||
override val id: Long,
|
||||
val name: String,
|
||||
val permission: MemberPermission
|
||||
) : ContactDTO() {
|
||||
@UseExperimental(MiraiExperimentalAPI::class)
|
||||
constructor(group: Group) : this(group.id, group.name, group.botPermission)
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package net.mamoe.mirai.api.http.data.common
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import net.mamoe.mirai.api.http.AuthedSession
|
||||
|
||||
interface DTO
|
||||
|
||||
@Serializable
|
||||
data class AuthDTO(val authKey: String) : DTO
|
||||
|
||||
@Serializable
|
||||
abstract class VerifyDTO : DTO {
|
||||
abstract val sessionKey: String
|
||||
@Transient
|
||||
lateinit var session: AuthedSession // 反序列化验证成功后传入
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.api.http.data.common
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.message.FriendMessage
|
||||
import net.mamoe.mirai.message.GroupMessage
|
||||
import net.mamoe.mirai.message.MessagePacket
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
|
||||
/*
|
||||
* DTO data class
|
||||
* */
|
||||
|
||||
// MessagePacket
|
||||
@Serializable
|
||||
@SerialName("FriendMessage")
|
||||
data class FriendMessagePacketDTO(val sender: QQDTO) : MessagePacketDTO()
|
||||
|
||||
@Serializable
|
||||
@SerialName("GroupMessage")
|
||||
data class GroupMessagePacketDTO(val sender: MemberDTO) : MessagePacketDTO()
|
||||
|
||||
@Serializable
|
||||
@SerialName("UnKnownMessage")
|
||||
data class UnKnownMessagePacketDTO(val msg: String) : MessagePacketDTO()
|
||||
|
||||
// Message
|
||||
@Serializable
|
||||
@SerialName("Source")
|
||||
data class MessageSourceDTO(val uid: Long) : MessageDTO()
|
||||
@Serializable
|
||||
@SerialName("At")
|
||||
data class AtDTO(val target: Long, val display: String) : MessageDTO()
|
||||
@Serializable
|
||||
@SerialName("AtAll")
|
||||
data class AtAllDTO(val target: Long = 0) : MessageDTO() // target为保留字段
|
||||
@Serializable
|
||||
@SerialName("Face")
|
||||
data class FaceDTO(val faceId: Int) : MessageDTO()
|
||||
@Serializable
|
||||
@SerialName("Plain")
|
||||
data class PlainDTO(val text: String) : MessageDTO()
|
||||
@Serializable
|
||||
@SerialName("Image")
|
||||
data class ImageDTO(val imageId: String) : MessageDTO()
|
||||
@Serializable
|
||||
@SerialName("Xml")
|
||||
data class XmlDTO(val xml: String) : MessageDTO()
|
||||
@Serializable
|
||||
@SerialName("Unknown")
|
||||
data class UnknownMessageDTO(val text: String) : MessageDTO()
|
||||
|
||||
/*
|
||||
* Abstract Class
|
||||
* */
|
||||
@Serializable
|
||||
sealed class MessagePacketDTO : DTO {
|
||||
lateinit var messageChain : MessageChainDTO
|
||||
}
|
||||
|
||||
typealias MessageChainDTO = Array<MessageDTO>
|
||||
|
||||
@Serializable
|
||||
sealed class MessageDTO : DTO
|
||||
|
||||
|
||||
/*
|
||||
Extend function
|
||||
*/
|
||||
suspend fun MessagePacket<*, *>.toDTO(): MessagePacketDTO = when (this) {
|
||||
is FriendMessage -> FriendMessagePacketDTO(QQDTO(sender))
|
||||
is GroupMessage -> GroupMessagePacketDTO(MemberDTO(sender))
|
||||
else -> UnKnownMessagePacketDTO("UnKnown Message Packet")
|
||||
}.apply { messageChain = Array(message.size){ message[it].toDTO() }}
|
||||
|
||||
fun MessageChainDTO.toMessageChain() =
|
||||
MessageChain().apply { this@toMessageChain.forEach { add(it.toMessage()) } }
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
fun Message.toDTO() = when (this) {
|
||||
is MessageSource -> MessageSourceDTO(messageUid)
|
||||
is At -> AtDTO(target, display)
|
||||
is AtAll -> AtAllDTO(0L)
|
||||
is Face -> FaceDTO(id.value.toInt())
|
||||
is PlainText -> PlainDTO(stringValue)
|
||||
is Image -> ImageDTO(imageId)
|
||||
is XMLMessage -> XmlDTO(stringValue)
|
||||
else -> UnknownMessageDTO("未知消息类型")
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||
fun MessageDTO.toMessage() = when (this) {
|
||||
is AtDTO -> At(target, display)
|
||||
is AtAllDTO -> AtAll
|
||||
is FaceDTO -> Face(FaceId(faceId.toUByte()))
|
||||
is PlainDTO -> PlainText(text)
|
||||
is ImageDTO -> Image(imageId)
|
||||
is XmlDTO -> XMLMessage(xml)
|
||||
is MessageSourceDTO, is UnknownMessageDTO -> PlainText("assert cannot reach")
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.api.http.queue
|
||||
|
||||
import net.mamoe.mirai.message.GroupMessage
|
||||
import net.mamoe.mirai.message.MessagePacket
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentLinkedDeque
|
||||
|
||||
class MessageQueue : ConcurrentLinkedDeque<MessagePacket<*, *>>() {
|
||||
|
||||
val quoteCache = ConcurrentHashMap<Long, GroupMessage>()
|
||||
|
||||
fun fetch(size: Int): List<MessagePacket<*, *>> {
|
||||
var count = size
|
||||
quoteCache.clear()
|
||||
val ret = ArrayList<MessagePacket<*, *>>(count)
|
||||
while (!this.isEmpty() && count-- > 0) {
|
||||
val packet = pop()
|
||||
ret.add(packet)
|
||||
|
||||
if (packet is GroupMessage) {
|
||||
addCache(packet)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun addCache(msg: GroupMessage) {
|
||||
quoteCache[msg.message[MessageSource].messageUid] = msg
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.api.http.route
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.call
|
||||
import io.ktor.routing.routing
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.api.http.AuthedSession
|
||||
import net.mamoe.mirai.api.http.SessionManager
|
||||
import net.mamoe.mirai.api.http.data.NoSuchBotException
|
||||
import net.mamoe.mirai.api.http.data.StateCode
|
||||
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
|
||||
fun Application.authModule() {
|
||||
routing {
|
||||
miraiAuth("/auth") {
|
||||
if (it.authKey != SessionManager.authKey) {
|
||||
call.respondStateCode(StateCode(1, "Auth Key错误"))
|
||||
} else {
|
||||
call.respondStateCode(StateCode(0, SessionManager.createTempSession().key))
|
||||
}
|
||||
}
|
||||
|
||||
miraiVerify<BindDTO>("/verify", verifiedSessionKey = false) {
|
||||
val bot = getBotOrThrow(it.qq)
|
||||
with(SessionManager) {
|
||||
closeSession(it.sessionKey)
|
||||
allSession[it.sessionKey] = AuthedSession(bot, EmptyCoroutineContext)
|
||||
}
|
||||
call.respondStateCode(StateCode.Success)
|
||||
}
|
||||
|
||||
miraiVerify<BindDTO>("/release") {
|
||||
val bot = getBotOrThrow(it.qq)
|
||||
val session = SessionManager[it.sessionKey] as AuthedSession
|
||||
if (bot.uin == session.bot.uin) {
|
||||
SessionManager.closeSession(it.sessionKey)
|
||||
call.respondStateCode(StateCode.Success)
|
||||
} else {
|
||||
throw NoSuchElementException()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class BindDTO(override val sessionKey: String, val qq: Long) : VerifyDTO()
|
||||
|
||||
private fun getBotOrThrow(qq: Long) = try {
|
||||
Bot.instanceWhose(qq)
|
||||
} catch (e: NoSuchElementException) {
|
||||
throw NoSuchBotException
|
||||
}
|
@ -1,209 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.api.http.route
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.call
|
||||
import io.ktor.application.install
|
||||
import io.ktor.features.CallLogging
|
||||
import io.ktor.features.DefaultHeaders
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.http.content.PartData
|
||||
import io.ktor.request.receive
|
||||
import io.ktor.response.defaultTextContentType
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.route
|
||||
import io.ktor.util.pipeline.ContextDsl
|
||||
import io.ktor.util.pipeline.PipelineContext
|
||||
import net.mamoe.mirai.api.http.AuthedSession
|
||||
import net.mamoe.mirai.api.http.SessionManager
|
||||
import net.mamoe.mirai.api.http.TempSession
|
||||
import net.mamoe.mirai.api.http.data.*
|
||||
import net.mamoe.mirai.api.http.data.common.AuthDTO
|
||||
import net.mamoe.mirai.api.http.data.common.DTO
|
||||
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
||||
import net.mamoe.mirai.api.http.util.jsonParseOrNull
|
||||
import net.mamoe.mirai.api.http.util.toJson
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.helpers.NOPLogger
|
||||
import org.slf4j.helpers.NOPLoggerFactory
|
||||
import org.slf4j.impl.SimpleLogger
|
||||
import org.slf4j.impl.SimpleLoggerFactory
|
||||
|
||||
fun Application.mirai() {
|
||||
install(DefaultHeaders)
|
||||
install(CallLogging) {
|
||||
logger = NOPLoggerFactory().getLogger("NMSL")
|
||||
|
||||
}
|
||||
authModule()
|
||||
messageModule()
|
||||
infoModule()
|
||||
groupManageModule()
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth,处理http server的验证
|
||||
* 为闭包传入一个AuthDTO对象
|
||||
*/
|
||||
@ContextDsl
|
||||
internal fun Route.miraiAuth(
|
||||
path: String,
|
||||
body: suspend PipelineContext<Unit, ApplicationCall>.(AuthDTO) -> Unit
|
||||
): Route {
|
||||
return route(path, HttpMethod.Post) {
|
||||
intercept {
|
||||
val dto = context.receiveDTO<AuthDTO>() ?: throw IllegalParamException("参数格式错误")
|
||||
this.body(dto)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get,用于获取bot的属性
|
||||
* 验证请求参数中sessionKey参数的有效性
|
||||
*/
|
||||
@ContextDsl
|
||||
internal fun Route.miraiGet(
|
||||
path: String,
|
||||
body: suspend PipelineContext<Unit, ApplicationCall>.(AuthedSession) -> Unit
|
||||
): Route {
|
||||
return route(path, HttpMethod.Get) {
|
||||
intercept {
|
||||
val sessionKey = call.parameters["sessionKey"] ?: throw IllegalParamException("参数格式错误")
|
||||
if (!SessionManager.containSession(sessionKey)) throw IllegalSessionException
|
||||
|
||||
when(val session = SessionManager[sessionKey]) {
|
||||
is TempSession -> throw NotVerifiedSessionException
|
||||
is AuthedSession -> this.body(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify,用于处理bot的行为请求
|
||||
* 验证数据传输对象(DTO)中是否包含sessionKey字段
|
||||
* 且验证sessionKey的有效性
|
||||
*
|
||||
* @param verifiedSessionKey 是否验证sessionKey是否被激活
|
||||
*
|
||||
* it 为json解析出的DTO对象
|
||||
*/
|
||||
@ContextDsl
|
||||
internal inline fun <reified T : VerifyDTO> Route.miraiVerify(
|
||||
path: String,
|
||||
verifiedSessionKey: Boolean = true,
|
||||
crossinline body: suspend PipelineContext<Unit, ApplicationCall>.(T) -> Unit
|
||||
): Route {
|
||||
return route(path, HttpMethod.Post) {
|
||||
intercept {
|
||||
val dto = context.receiveDTO<T>() ?: throw IllegalParamException("参数格式错误")
|
||||
SessionManager[dto.sessionKey]?.let {
|
||||
when {
|
||||
it is TempSession && verifiedSessionKey -> throw NotVerifiedSessionException
|
||||
it is AuthedSession -> dto.session = it
|
||||
}
|
||||
} ?: throw IllegalSessionException
|
||||
|
||||
this.body(dto)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一捕获并处理异常
|
||||
*/
|
||||
internal inline fun Route.intercept(crossinline blk: suspend PipelineContext<Unit, ApplicationCall>.() -> Unit) = handle {
|
||||
try {
|
||||
blk(this)
|
||||
} catch (e: IllegalSessionException) {
|
||||
call.respondStateCode(StateCode.IllegalSession)
|
||||
} catch (e: NotVerifiedSessionException) {
|
||||
call.respondStateCode(StateCode.NotVerifySession)
|
||||
} catch (e: NoSuchBotException) {
|
||||
call.respondStateCode(StateCode.NoBot)
|
||||
} catch (e: NoSuchElementException) {
|
||||
call.respondStateCode(StateCode.NoElement)
|
||||
} catch (e: PermissionDeniedException) {
|
||||
call.respondStateCode(StateCode.PermissionDenied)
|
||||
} catch (e: IllegalAccessException) {
|
||||
call.respondStateCode(StateCode(400, e.message), HttpStatusCode.BadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
extend function
|
||||
*/
|
||||
internal suspend inline fun <reified T : StateCode> ApplicationCall.respondStateCode(code: T, status: HttpStatusCode = HttpStatusCode.OK) = respondJson(code.toJson(StateCode.serializer()), status)
|
||||
|
||||
internal suspend inline fun <reified T : DTO> ApplicationCall.respondDTO(dto: T, status: HttpStatusCode = HttpStatusCode.OK) = respondJson(dto.toJson(), status)
|
||||
|
||||
internal suspend fun ApplicationCall.respondJson(json: String, status: HttpStatusCode = HttpStatusCode.OK) =
|
||||
respondText(json, defaultTextContentType(ContentType("application", "json")), status)
|
||||
|
||||
internal suspend inline fun <reified T : DTO> ApplicationCall.receiveDTO(): T? = receive<String>().jsonParseOrNull()
|
||||
|
||||
|
||||
fun PipelineContext<Unit, ApplicationCall>.illegalParam(
|
||||
expectingType: String?,
|
||||
paramName: String,
|
||||
actualValue: String? = call.parameters[paramName]
|
||||
): Nothing = throw IllegalParamException("Illegal param. A $expectingType is required for `$paramName` while `$actualValue` is given")
|
||||
|
||||
|
||||
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
internal inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNull(name: String): R =
|
||||
when (R::class) {
|
||||
Byte::class -> call.parameters[name]?.toByte()
|
||||
Int::class -> call.parameters[name]?.toInt()
|
||||
Short::class -> call.parameters[name]?.toShort()
|
||||
Float::class -> call.parameters[name]?.toFloat()
|
||||
Long::class -> call.parameters[name]?.toLong()
|
||||
Double::class -> call.parameters[name]?.toDouble()
|
||||
Boolean::class -> when (call.parameters[name]) {
|
||||
"true" -> true
|
||||
"false" -> false
|
||||
"0" -> false
|
||||
"1" -> true
|
||||
null -> null
|
||||
else -> illegalParam("boolean", name)
|
||||
}
|
||||
|
||||
String::class -> call.parameters[name]
|
||||
|
||||
UByte::class -> call.parameters[name]?.toUByte()
|
||||
UInt::class -> call.parameters[name]?.toUInt()
|
||||
UShort::class -> call.parameters[name]?.toUShort()
|
||||
|
||||
else -> error(name::class.simpleName + " is not supported")
|
||||
} as R ?: illegalParam(R::class.simpleName, name)
|
||||
|
||||
/**
|
||||
* multi part
|
||||
*/
|
||||
internal fun List<PartData>.value(name: String) =
|
||||
try {
|
||||
(filter { it.name == name }[0] as PartData.FormItem).value
|
||||
} catch (e: Exception) {
|
||||
throw IllegalParamException("参数格式错误")
|
||||
}
|
||||
|
||||
internal fun List<PartData>.file(name: String) =
|
||||
try {
|
||||
filter { it.name == name }[0] as? PartData.FileItem
|
||||
} catch (e: Exception) {
|
||||
throw IllegalParamException("参数格式错误")
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.api.http.route
|
||||
|
@ -1,150 +0,0 @@
|
||||
package net.mamoe.mirai.api.http.route
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.call
|
||||
import io.ktor.routing.routing
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.api.http.data.PermissionDeniedException
|
||||
import net.mamoe.mirai.api.http.data.StateCode
|
||||
import net.mamoe.mirai.api.http.data.common.DTO
|
||||
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
|
||||
|
||||
fun Application.groupManageModule() {
|
||||
routing {
|
||||
|
||||
/**
|
||||
* 禁言(需要相关权限)
|
||||
*/
|
||||
miraiVerify<MuteDTO>("/muteAll") {
|
||||
it.session.bot.getGroup(it.target).isMuteAll = true
|
||||
call.respondStateCode(StateCode.Success)
|
||||
}
|
||||
|
||||
miraiVerify<MuteDTO>("/unmuteAll") {
|
||||
it.session.bot.getGroup(it.target).isMuteAll = false
|
||||
call.respondStateCode(StateCode.Success)
|
||||
}
|
||||
|
||||
miraiVerify<MuteDTO>("/mute") {
|
||||
when (it.session.bot.getGroup(it.target)[it.memberId].mute(it.time)) {
|
||||
true -> call.respondStateCode(StateCode.Success)
|
||||
else -> throw PermissionDeniedException
|
||||
}
|
||||
}
|
||||
|
||||
miraiVerify<MuteDTO>("/unmute") {
|
||||
when (it.session.bot.getGroup(it.target).members[it.memberId].unmute()) {
|
||||
true -> call.respondStateCode(StateCode.Success)
|
||||
else -> throw PermissionDeniedException
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移出群聊(需要相关权限)
|
||||
*/
|
||||
miraiVerify<KickDTO>("/kick") {
|
||||
when (it.session.bot.getGroup(it.target)[it.memberId].kick(it.msg)) {
|
||||
true -> call.respondStateCode(StateCode.Success)
|
||||
else -> throw PermissionDeniedException
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 群设置(需要相关权限)
|
||||
*/
|
||||
miraiGet("/groupConfig") {
|
||||
val group = it.bot.getGroup(paramOrNull("target"))
|
||||
call.respondDTO(GroupDetailDTO(group))
|
||||
}
|
||||
|
||||
miraiVerify<GroupConfigDTO>("/groupConfig") { dto ->
|
||||
val group = dto.session.bot.getGroup(dto.target)
|
||||
with(dto.config) {
|
||||
name?.let { group.name = it }
|
||||
announcement?.let { group.entranceAnnouncement = it }
|
||||
confessTalk?.let { group.isConfessTalkEnabled = it }
|
||||
allowMemberInvite?.let { group.isAllowMemberInvite = it }
|
||||
// TODO: 待core接口实现设置可改
|
||||
// autoApprove?.let { group.autoApprove = it }
|
||||
// anonymousChat?.let { group.anonymousChat = it }
|
||||
}
|
||||
call.respondStateCode(StateCode.Success)
|
||||
}
|
||||
|
||||
/**
|
||||
* 群员信息管理(需要相关权限)
|
||||
*/
|
||||
miraiGet("/memberInfo") {
|
||||
val member = it.bot.getGroup(paramOrNull("target"))[paramOrNull("memberId")]
|
||||
call.respondDTO(MemberDetailDTO(member))
|
||||
}
|
||||
|
||||
miraiVerify<MemberInfoDTO>("/memberInfo") { dto ->
|
||||
val member = dto.session.bot.getGroup(dto.target)[dto.memberId]
|
||||
with(dto.info) {
|
||||
name?.let { member.nameCard = it }
|
||||
specialTitle?.let { member.specialTitle = it }
|
||||
}
|
||||
call.respondStateCode(StateCode.Success)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Serializable
|
||||
private data class MuteDTO(
|
||||
override val sessionKey: String,
|
||||
val target: Long,
|
||||
val memberId: Long = 0,
|
||||
val time: Int = 0
|
||||
) : VerifyDTO()
|
||||
|
||||
@Serializable
|
||||
private data class KickDTO(
|
||||
override val sessionKey: String,
|
||||
val target: Long,
|
||||
val memberId: Long,
|
||||
val msg: String = ""
|
||||
) : VerifyDTO()
|
||||
|
||||
@Serializable
|
||||
private data class GroupConfigDTO(
|
||||
override val sessionKey: String,
|
||||
val target: Long,
|
||||
val config: GroupDetailDTO
|
||||
) : VerifyDTO()
|
||||
|
||||
@Serializable
|
||||
private data class GroupDetailDTO(
|
||||
val name: String? = null,
|
||||
val announcement: String? = null,
|
||||
val confessTalk: Boolean? = null,
|
||||
val allowMemberInvite: Boolean? = null,
|
||||
val autoApprove: Boolean? = null,
|
||||
val anonymousChat: Boolean? = null
|
||||
) : DTO {
|
||||
constructor(group: Group) : this(
|
||||
group.name, group.entranceAnnouncement, group.isConfessTalkEnabled, group.isAllowMemberInvite,
|
||||
group.isAutoApproveEnabled, group.isAnonymousChatEnabled
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class MemberInfoDTO(
|
||||
override val sessionKey: String,
|
||||
val target: Long,
|
||||
val memberId: Long,
|
||||
val info: MemberDetailDTO
|
||||
) : VerifyDTO()
|
||||
|
||||
@Serializable
|
||||
private data class MemberDetailDTO(
|
||||
val name: String? = null,
|
||||
val specialTitle: String? = null
|
||||
) : DTO {
|
||||
constructor(member: Member) : this(member.nameCard, member.specialTitle)
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.api.http.route
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.call
|
||||
import io.ktor.routing.routing
|
||||
import net.mamoe.mirai.api.http.data.common.GroupDTO
|
||||
import net.mamoe.mirai.api.http.data.common.MemberDTO
|
||||
import net.mamoe.mirai.api.http.data.common.QQDTO
|
||||
import net.mamoe.mirai.api.http.util.toJson
|
||||
import net.mamoe.mirai.contact.toMutableList
|
||||
|
||||
fun Application.infoModule() {
|
||||
routing {
|
||||
|
||||
miraiGet("/friendList") {
|
||||
val ls = it.bot.qqs.toMutableList().map { qq -> QQDTO(qq) }
|
||||
call.respondJson(ls.toJson())
|
||||
}
|
||||
|
||||
miraiGet("/groupList") {
|
||||
val ls = it.bot.groups.toMutableList().map { group -> GroupDTO(group) }
|
||||
call.respondJson(ls.toJson())
|
||||
}
|
||||
|
||||
miraiGet("/memberList") {
|
||||
val ls = it.bot.getGroup(paramOrNull("target")).members.toMutableList().map { member -> MemberDTO(member) }
|
||||
call.respondJson(ls.toJson())
|
||||
}
|
||||
}
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.api.http.route
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.content.readAllParts
|
||||
import io.ktor.http.content.streamProvider
|
||||
import io.ktor.request.receiveMultipart
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.post
|
||||
import io.ktor.routing.routing
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.api.http.AuthedSession
|
||||
import net.mamoe.mirai.api.http.SessionManager
|
||||
import net.mamoe.mirai.api.http.data.*
|
||||
import net.mamoe.mirai.api.http.data.common.MessageChainDTO
|
||||
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
||||
import net.mamoe.mirai.api.http.data.common.toDTO
|
||||
import net.mamoe.mirai.api.http.data.common.toMessageChain
|
||||
import net.mamoe.mirai.api.http.util.toJson
|
||||
import net.mamoe.mirai.contact.toList
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.uploadImage
|
||||
import java.net.URL
|
||||
|
||||
fun Application.messageModule() {
|
||||
routing {
|
||||
|
||||
miraiGet("/fetchMessage") {
|
||||
val count: Int = paramOrNull("count")
|
||||
val fetch = it.messageQueue.fetch(count)
|
||||
val ls = Array(fetch.size) { index -> fetch[index].toDTO() }
|
||||
|
||||
call.respondJson(ls.toList().toJson())
|
||||
}
|
||||
|
||||
miraiVerify<SendDTO>("/sendFriendMessage") {
|
||||
it.session.bot.getFriend(it.target).sendMessage(it.messageChain.toMessageChain())
|
||||
call.respondStateCode(StateCode.Success)
|
||||
}
|
||||
|
||||
miraiVerify<SendDTO>("/sendGroupMessage") {
|
||||
it.session.bot.getGroup(it.target).sendMessage(it.messageChain.toMessageChain())
|
||||
call.respondStateCode(StateCode.Success)
|
||||
}
|
||||
|
||||
miraiVerify<SendDTO>("/quoteMessage") {
|
||||
it.session.messageQueue.quoteCache[it.target]?.quoteReply(it.messageChain.toMessageChain())
|
||||
?: throw NoSuchElementException()
|
||||
call.respondStateCode(StateCode.Success)
|
||||
}
|
||||
|
||||
miraiVerify<SendImageDTO>("sendImageMessage") {
|
||||
val bot = it.session.bot
|
||||
val contact = when {
|
||||
it.target != null -> bot[it.target]
|
||||
it.qq != null -> bot.getFriend(it.qq)
|
||||
it.group != null -> bot.getGroup(it.group)
|
||||
else -> throw IllegalParamException("target、qq、group不可全为null")
|
||||
}
|
||||
val ls = it.urls.map { url -> contact.uploadImage(URL(url)) }
|
||||
contact.sendMessage(MessageChain(ls))
|
||||
call.respondJson(ls.map { image -> image.imageId }.toJson())
|
||||
}
|
||||
|
||||
// TODO: 重构
|
||||
post("uploadImage") {
|
||||
val parts = call.receiveMultipart().readAllParts()
|
||||
val sessionKey = parts.value("sessionKey")
|
||||
if (!SessionManager.containSession(sessionKey)) throw IllegalSessionException
|
||||
val session = try {
|
||||
SessionManager[sessionKey] as AuthedSession
|
||||
} catch (e: TypeCastException) {
|
||||
throw NotVerifiedSessionException
|
||||
}
|
||||
|
||||
val type = parts.value("type")
|
||||
parts.file("img")?.apply {
|
||||
val image = streamProvider().use {
|
||||
when (type) {
|
||||
"group" -> session.bot.groups.toList().random().uploadImage(it)
|
||||
"friend" -> session.bot.qqs.toList().random().uploadImage(it)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
image?.apply {
|
||||
call.respondText(imageId)
|
||||
} ?: throw IllegalAccessException("图片上传错误")
|
||||
} ?: throw IllegalAccessException("未知错误")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class SendDTO(
|
||||
override val sessionKey: String,
|
||||
val target: Long,
|
||||
val messageChain: MessageChainDTO
|
||||
) : VerifyDTO()
|
||||
|
||||
@Serializable
|
||||
private data class SendImageDTO(
|
||||
override val sessionKey: String,
|
||||
val target: Long? = null,
|
||||
val qq: Long? = null,
|
||||
val group: Long? = null,
|
||||
val urls: List<String>
|
||||
) : VerifyDTO()
|
||||
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.api.http.util
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import net.mamoe.mirai.api.http.data.common.*
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
|
||||
// 解析失败时直接返回null,由路由判断响应400状态
|
||||
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||
inline fun <reified T : Any> String.jsonParseOrNull(
|
||||
serializer: DeserializationStrategy<T>? = null
|
||||
): T? = try {
|
||||
if(serializer == null) MiraiJson.json.parse(this) else Json.parse(this)
|
||||
} catch (e: Exception) { null }
|
||||
|
||||
|
||||
@UseExperimental(ImplicitReflectionSerializer::class, UnstableDefault::class)
|
||||
inline fun <reified T : Any> T.toJson(
|
||||
serializer: SerializationStrategy<T>? = null
|
||||
): String = if (serializer == null) MiraiJson.json.stringify(this)
|
||||
else MiraiJson.json.stringify(serializer, this)
|
||||
|
||||
|
||||
// 序列化列表时,stringify需要使用的泛型是T,而非List<T>
|
||||
// 因为使用的stringify的stringify(objs: List<T>)重载
|
||||
@UseExperimental(ImplicitReflectionSerializer::class, UnstableDefault::class)
|
||||
inline fun <reified T : Any> List<T>.toJson(
|
||||
serializer: SerializationStrategy<List<T>>? = null
|
||||
): String = if (serializer == null) MiraiJson.json.stringify(this)
|
||||
else MiraiJson.json.stringify(serializer, this)
|
||||
|
||||
|
||||
/**
|
||||
* Json解析规则,需要注册支持的多态的类
|
||||
*/
|
||||
object MiraiJson {
|
||||
val json = Json(context = SerializersModule {
|
||||
polymorphic(MessagePacketDTO.serializer()) {
|
||||
GroupMessagePacketDTO::class with GroupMessagePacketDTO.serializer()
|
||||
FriendMessagePacketDTO::class with FriendMessagePacketDTO.serializer()
|
||||
UnKnownMessagePacketDTO::class with UnKnownMessagePacketDTO.serializer()
|
||||
}
|
||||
polymorphic(MessageDTO.serializer()) {
|
||||
MessageSourceDTO::class with MessageSourceDTO.serializer()
|
||||
AtDTO::class with AtDTO.serializer()
|
||||
AtAllDTO::class with AtAllDTO.serializer()
|
||||
FaceDTO::class with FaceDTO.serializer()
|
||||
PlainDTO::class with PlainDTO.serializer()
|
||||
ImageDTO::class with ImageDTO.serializer()
|
||||
XmlDTO::class with XmlDTO.serializer()
|
||||
UnknownMessageDTO::class with UnknownMessageDTO.serializer()
|
||||
}
|
||||
})
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
# Mirai Console
|
||||
#### Mirai Console allows you to run Mirai in command lines/terminal.
|
||||
#### 你可以终端中或命令行环境下运行在Mirai
|
||||
<br>
|
||||
|
||||
#### More Importantly, Mirai Console support <b>Plugins</b>, tells the bot what to do
|
||||
#### Mirai Console 支持插件系统, 你可以自己开发或使用公开的插件来逻辑化机器人, 如群管
|
||||
<br>
|
||||
|
||||
#### download 下载
|
||||
#### how to get/write plugins 如何获取/写插件
|
||||
<br>
|
||||
<br>
|
||||
|
||||
### how to use(如何使用)
|
||||
#### how to run Mirai Console
|
||||
<ul>
|
||||
<li>download mirai-console.jar</li>
|
||||
<li>open command line/terminal</li>
|
||||
<li>create a folder and put mirai-console.jar in</li>
|
||||
<li>cd that folder</li>
|
||||
<li>"java -jar mirai-console.jar"</li>
|
||||
</ul>
|
||||
|
||||
<ul>
|
||||
<li>下载mirai-console.jar</li>
|
||||
<li>打开终端</li>
|
||||
<li>在任何地方创建一个文件夹, 并放入mirai-console.jar</li>
|
||||
<li>在终端中打开该文件夹"cd"</li>
|
||||
<li>输入"java -jar mirai-console.jar"</li>
|
||||
</ul>
|
||||
|
||||
#### how to add plugins
|
||||
<ul>
|
||||
<li>After first time of running mirai console</li>
|
||||
<li>/plugins/folder will be created next to mirai-console.jar</li>
|
||||
<li>put plugin(.jar) into /plugins/</li>
|
||||
<li>restart mirai console</li>
|
||||
<li>checking logger and check if the plugin is loaded successfully</li>
|
||||
<li>if the plugin has it own Config file, it normally appears in /plugins/{pluginName}/</li>
|
||||
</ul>
|
||||
|
||||
<ul>
|
||||
<li>在首次运行mirai console后</li>
|
||||
<li>mirai-console.jar 的同级会出现/plugins/文件夹</li>
|
||||
<li>将插件(.jar)放入/plugins/文件夹</li>
|
||||
<li>重启mirai console</li>
|
||||
<li>在开启后检查日志, 是否成功加载</li>
|
||||
<li>如该插件有配置文件, 配置文件一般会创建在/plugins/插件名字/ 文件夹下</li>
|
||||
</ul>
|
||||
|
||||
|
@ -1,47 +0,0 @@
|
||||
plugins {
|
||||
id("kotlinx-serialization")
|
||||
id("kotlin")
|
||||
id("java")
|
||||
}
|
||||
|
||||
|
||||
apply(plugin = "com.github.johnrengelman.shadow")
|
||||
|
||||
apply(plugin = "java-library")
|
||||
|
||||
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>() {
|
||||
manifest {
|
||||
attributes["Main-Class"] = "net.mamoe.mirai.MiraiConsoleLoader"
|
||||
}
|
||||
}
|
||||
|
||||
val kotlinVersion: String by rootProject.ext
|
||||
val atomicFuVersion: String by rootProject.ext
|
||||
val coroutinesVersion: String by rootProject.ext
|
||||
val kotlinXIoVersion: String by rootProject.ext
|
||||
val coroutinesIoVersion: String by rootProject.ext
|
||||
|
||||
val klockVersion: String by rootProject.ext
|
||||
val ktorVersion: String by rootProject.ext
|
||||
|
||||
val serializationVersion: String by rootProject.ext
|
||||
|
||||
fun kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$version"
|
||||
|
||||
fun ktor(id: String, version: String) = "io.ktor:ktor-$id:$version"
|
||||
|
||||
|
||||
|
||||
dependencies {
|
||||
api(project(":mirai-core"))
|
||||
api(project(":mirai-core-qqandroid"))
|
||||
api(project(":mirai-api-http"))
|
||||
runtimeOnly(files("../mirai-core-qqandroid/build/classes/kotlin/jvm/main"))
|
||||
runtimeOnly(files("../mirai-core/build/classes/kotlin/jvm/main"))
|
||||
api(kotlin("serialization"))
|
||||
api(group = "com.alibaba", name = "fastjson", version = "1.2.62")
|
||||
api(group = "org.yaml", name = "snakeyaml", version = "1.25")
|
||||
api(group = "com.moandjiezana.toml", name = "toml4j", version = "0.7.2")
|
||||
api(group = "com.googlecode.lanterna", name = "lanterna", version = "3.0.2")
|
||||
// classpath is not set correctly by IDE
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
package net.mamoe.mirai
|
||||
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import net.mamoe.mirai.plugins.PluginManager
|
||||
|
||||
object CommandManager {
|
||||
private val registeredCommand: MutableMap<String, ICommand> = mutableMapOf()
|
||||
|
||||
fun getCommands(): Collection<ICommand> {
|
||||
return registeredCommand.values
|
||||
}
|
||||
|
||||
|
||||
fun register(command: ICommand) {
|
||||
val allNames = mutableListOf(command.name).also { it.addAll(command.alias) }
|
||||
allNames.forEach {
|
||||
if (registeredCommand.containsKey(it)) {
|
||||
error("net.mamoe.mirai.Command Name(or Alias) $it is already registered, consider if same function plugin was installed")
|
||||
}
|
||||
}
|
||||
allNames.forEach {
|
||||
registeredCommand[it] = command
|
||||
}
|
||||
}
|
||||
|
||||
fun unregister(command: ICommand) {
|
||||
val allNames = mutableListOf<String>(command.name).also { it.addAll(command.alias) }
|
||||
allNames.forEach {
|
||||
registeredCommand.remove(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun unregister(commandName: String) {
|
||||
registeredCommand.remove(commandName)
|
||||
}
|
||||
|
||||
fun runCommand(fullCommand: String): Boolean {
|
||||
val blocks = fullCommand.split(" ")
|
||||
val commandHead = blocks[0].replace("/", "")
|
||||
if (!registeredCommand.containsKey(commandHead)) {
|
||||
return false
|
||||
}
|
||||
val args = blocks.subList(1, blocks.size)
|
||||
registeredCommand[commandHead]?.run {
|
||||
if (onCommand(
|
||||
blocks.subList(1, blocks.size)
|
||||
)
|
||||
) {
|
||||
PluginManager.onCommand(this, args)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface ICommand {
|
||||
val name: String
|
||||
val alias: List<String>
|
||||
val description: String
|
||||
fun onCommand(args: List<String>): Boolean
|
||||
fun register()
|
||||
}
|
||||
|
||||
abstract class Command(
|
||||
override val name: String,
|
||||
override val alias: List<String> = listOf(),
|
||||
override val description: String = ""
|
||||
) : ICommand {
|
||||
/**
|
||||
* 最高优先级监听器
|
||||
* 如果return [false] 这次指令不会被[PluginBase]的全局onCommand监听器监听
|
||||
* */
|
||||
open override fun onCommand(args: List<String>): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun register() {
|
||||
CommandManager.register(this)
|
||||
}
|
||||
}
|
||||
|
||||
class AnonymousCommand internal constructor(
|
||||
override val name: String,
|
||||
override val alias: List<String>,
|
||||
override val description: String,
|
||||
val onCommand: ICommand.(args: List<String>) -> Boolean
|
||||
) : ICommand {
|
||||
override fun onCommand(args: List<String>): Boolean {
|
||||
return onCommand.invoke(this, args)
|
||||
}
|
||||
|
||||
override fun register() {
|
||||
CommandManager.register(this)
|
||||
}
|
||||
}
|
||||
|
||||
class CommandBuilder internal constructor() {
|
||||
var name: String? = null
|
||||
var alias: List<String>? = null
|
||||
var description: String = ""
|
||||
var onCommand: (ICommand.(args: List<String>) -> Boolean)? = null
|
||||
|
||||
fun onCommand(commandProcess: ICommand.(args: List<String>) -> Boolean) {
|
||||
onCommand = commandProcess
|
||||
}
|
||||
|
||||
fun register(): ICommand {
|
||||
if (name == null || onCommand == null) {
|
||||
error("net.mamoe.mirai.CommandBuilder not complete")
|
||||
}
|
||||
if (alias == null) {
|
||||
alias = listOf()
|
||||
}
|
||||
return AnonymousCommand(name!!, alias!!, description, onCommand!!).also { it.register() }
|
||||
}
|
||||
}
|
||||
|
||||
fun buildCommand(builder: CommandBuilder.() -> Unit): ICommand {
|
||||
return CommandBuilder().apply(builder).register()
|
||||
}
|
||||
|
@ -1,295 +0,0 @@
|
||||
package net.mamoe.mirai
|
||||
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.mamoe.mirai.plugins.PluginManager
|
||||
import net.mamoe.mirai.plugins.loadAsConfig
|
||||
import net.mamoe.mirai.plugins.withDefaultWrite
|
||||
import net.mamoe.mirai.plugins.withDefaultWriteSave
|
||||
import net.mamoe.mirai.api.http.MiraiHttpAPIServer
|
||||
import net.mamoe.mirai.api.http.generateSessionKey
|
||||
import net.mamoe.mirai.contact.sendMessage
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
object MiraiConsole {
|
||||
val bots
|
||||
get() = Bot.instances
|
||||
|
||||
fun getBotByUIN(uin: Long): Bot? {
|
||||
bots.forEach {
|
||||
if (it.get()?.uin == uin) {
|
||||
return it.get()
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
val pluginManager: PluginManager
|
||||
get() = PluginManager
|
||||
|
||||
var logger = UIPushLogger(0)
|
||||
|
||||
var path: String = System.getProperty("user.dir")
|
||||
|
||||
val version = "0.01"
|
||||
var coreVersion = "0.13"
|
||||
val build = "Beta"
|
||||
|
||||
fun start() {
|
||||
logger("Mirai-console v$version $build | core version v$coreVersion is still in testing stage, majority feature is available")
|
||||
logger("Mirai-console v$version $build | 核心版本 v${coreVersion}还处于测试阶段, 大部分功能可用")
|
||||
logger()
|
||||
logger("Mirai-console now running under " + System.getProperty("user.dir"))
|
||||
logger("Mirai-console 正在 " + System.getProperty("user.dir") + "下运行")
|
||||
logger()
|
||||
logger("Get news in github: https://github.com/mamoe/mirai")
|
||||
logger("在Github中获取项目最新进展: https://github.com/mamoe/mirai")
|
||||
logger("Mirai为开源项目,请自觉遵守开源项目协议")
|
||||
logger("Powered by Mamoe Technologies and contributors")
|
||||
logger()
|
||||
|
||||
runBlocking {
|
||||
DefaultCommands()
|
||||
HTTPAPIAdaptar()
|
||||
pluginManager.loadPlugins()
|
||||
CommandListener.start()
|
||||
}
|
||||
|
||||
logger("Mirai-console 启动完成")
|
||||
logger("\"/login qqnumber qqpassword \" to login a bot")
|
||||
logger("\"/login qq号 qq密码 \" 来登陆一个BOT")
|
||||
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
PluginManager.disableAllPlugins()
|
||||
}
|
||||
|
||||
object HTTPAPIAdaptar {
|
||||
operator fun invoke() {
|
||||
if (MiraiProperties.HTTP_API_ENABLE) {
|
||||
if (MiraiProperties.HTTP_API_AUTH_KEY.startsWith("InitKey")) {
|
||||
logger("请尽快更改初始生成的HTTP API AUTHKEY")
|
||||
}
|
||||
logger("正在启动HTTPAPI; 端口=" + MiraiProperties.HTTP_API_PORT)
|
||||
MiraiHttpAPIServer.start(
|
||||
MiraiProperties.HTTP_API_PORT,
|
||||
MiraiProperties.HTTP_API_AUTH_KEY
|
||||
)
|
||||
logger("HTTPAPI启动完成; 端口=" + MiraiProperties.HTTP_API_PORT)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults Commands are recommend to be replaced by plugin provided commands
|
||||
*/
|
||||
object DefaultCommands {
|
||||
operator fun invoke() {
|
||||
buildCommand {
|
||||
name = "login"
|
||||
description = "Mirai-Console default bot login command"
|
||||
onCommand {
|
||||
if (it.size < 2) {
|
||||
logger("\"/login qqnumber qqpassword \" to login a bot")
|
||||
logger("\"/login qq号 qq密码 \" 来登录一个BOT")
|
||||
return@onCommand false
|
||||
}
|
||||
val qqNumber = it[0].toLong()
|
||||
val qqPassword = it[1]
|
||||
logger("login...")
|
||||
try {
|
||||
runBlocking {
|
||||
Bot(qqNumber, qqPassword).alsoLogin()
|
||||
println("$qqNumber login successes")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println("$qqNumber login failed")
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
buildCommand {
|
||||
name = "status"
|
||||
description = "Mirai-Console default status command"
|
||||
onCommand {
|
||||
when (it.size) {
|
||||
0 -> {
|
||||
logger("当前有" + bots.size + "个BOT在线")
|
||||
}
|
||||
1 -> {
|
||||
val bot = it[0]
|
||||
var find = false
|
||||
bots.forEach {
|
||||
if (it.get()?.uin.toString().contains(bot)) {
|
||||
find = true
|
||||
logger("" + it.get()?.uin + ": 在线中; 好友数量:" + it.get()?.qqs?.size + "; 群组数量:" + it.get()?.groups?.size)
|
||||
}
|
||||
}
|
||||
if (!find) {
|
||||
logger("没有找到BOT$bot")
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
buildCommand {
|
||||
name = "say"
|
||||
description = "Mirai-Console default say command"
|
||||
onCommand {
|
||||
if (it.size < 2) {
|
||||
logger("say [好友qq号或者群号] [文本消息] //将默认使用第一个BOT")
|
||||
logger("say [bot号] [好友qq号或者群号] [文本消息]")
|
||||
return@onCommand false
|
||||
}
|
||||
val bot: Bot? = if (it.size == 2) {
|
||||
if (bots.size == 0) {
|
||||
logger("还没有BOT登陆")
|
||||
return@onCommand false
|
||||
}
|
||||
bots[0].get()
|
||||
} else {
|
||||
getBotByUIN(it[0].toLong())
|
||||
}
|
||||
if (bot == null) {
|
||||
logger("没有找到BOT")
|
||||
return@onCommand false
|
||||
}
|
||||
val target = it[it.size - 2].toLong()
|
||||
val message = it[it.size - 1]
|
||||
try {
|
||||
val contact = bot[target]
|
||||
runBlocking {
|
||||
contact.sendMessage(message)
|
||||
logger("消息已推送")
|
||||
}
|
||||
} catch (e: NoSuchElementException) {
|
||||
logger("没有找到群或好友 号码为${target}")
|
||||
return@onCommand false
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
buildCommand {
|
||||
name = "plugins"
|
||||
alias = listOf("plugin")
|
||||
description = "show all plugins"
|
||||
onCommand {
|
||||
PluginManager.getAllPluginDescriptions().let {
|
||||
println("loaded " + it.size + " plugins")
|
||||
it.forEach {
|
||||
logger("\t" + it.name + " v" + it.version + " by" + it.author + " " + it.info)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildCommand {
|
||||
name = "command"
|
||||
alias = listOf("commands", "help", "helps")
|
||||
description = "show all commands"
|
||||
onCommand {
|
||||
CommandManager.getCommands().let {
|
||||
println("currently have " + it.size + " commands")
|
||||
it.toSet().forEach {
|
||||
logger("\t" + it.name + " :" + it.description)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
buildCommand {
|
||||
name = "about"
|
||||
description = "About Mirai-Console"
|
||||
onCommand {
|
||||
logger("v$version $build is still in testing stage, majority feature is available")
|
||||
logger("now running under " + System.getProperty("user.dir"))
|
||||
logger("在Github中获取项目最新进展: https://github.com/mamoe/mirai")
|
||||
logger("Mirai为开源项目,请自觉遵守开源项目协议")
|
||||
logger("Powered by Mamoe Technologies and contributors")
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
object CommandListener {
|
||||
fun start() {
|
||||
thread {
|
||||
processNextCommandLine()
|
||||
}
|
||||
}
|
||||
|
||||
tailrec fun processNextCommandLine() {
|
||||
var fullCommand = readLine()
|
||||
if (fullCommand != null) {
|
||||
if (!fullCommand.startsWith("/")) {
|
||||
fullCommand = "/$fullCommand"
|
||||
}
|
||||
if (!CommandManager.runCommand(fullCommand)) {
|
||||
logger("未知指令 $fullCommand")
|
||||
}
|
||||
}
|
||||
processNextCommandLine();
|
||||
}
|
||||
}
|
||||
|
||||
class UIPushLogger(val identity: Long) {
|
||||
operator fun invoke(any: Any? = null) {
|
||||
MiraiConsoleUI.start()
|
||||
if (any != null) {
|
||||
MiraiConsoleUI.pushLog(identity, "[Mirai$version $build]: $any")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object MiraiProperties {
|
||||
var config = File("$path/mirai.properties").loadAsConfig()
|
||||
|
||||
var HTTP_API_ENABLE: Boolean by config.withDefaultWrite { true }
|
||||
var HTTP_API_PORT: Int by config.withDefaultWrite { 8080 }
|
||||
var HTTP_API_AUTH_KEY: String by config.withDefaultWriteSave {
|
||||
"InitKey" + generateSessionKey()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MiraiConsoleLoader {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
MiraiConsole.start()
|
||||
Runtime.getRuntime().addShutdownHook(thread(start = false) {
|
||||
MiraiConsole.stop()
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,319 +0,0 @@
|
||||
package net.mamoe.mirai
|
||||
|
||||
import com.googlecode.lanterna.SGR
|
||||
import com.googlecode.lanterna.TerminalSize
|
||||
import com.googlecode.lanterna.TextColor
|
||||
import com.googlecode.lanterna.graphics.TextGraphics
|
||||
import com.googlecode.lanterna.input.KeyStroke
|
||||
import com.googlecode.lanterna.input.KeyType
|
||||
import com.googlecode.lanterna.terminal.DefaultTerminalFactory
|
||||
import com.googlecode.lanterna.terminal.Terminal
|
||||
import com.googlecode.lanterna.terminal.TerminalResizeListener
|
||||
import com.googlecode.lanterna.terminal.swing.SwingTerminal
|
||||
import com.googlecode.lanterna.terminal.swing.SwingTerminalFrame
|
||||
import java.io.OutputStream
|
||||
import java.io.PrintStream
|
||||
import java.lang.StringBuilder
|
||||
import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
|
||||
object MiraiConsoleUI {
|
||||
|
||||
val log = mutableMapOf<Long, LimitLinkedQueue<String>>().also { it[0L] = LimitLinkedQueue(50) }
|
||||
|
||||
|
||||
private val screens = mutableListOf(0L)
|
||||
private var currentScreenId = 0
|
||||
|
||||
fun addBotScreen(uin: Long) {
|
||||
screens.add(uin)
|
||||
log[uin] = LimitLinkedQueue()
|
||||
}
|
||||
|
||||
|
||||
lateinit var terminal: Terminal
|
||||
lateinit var textGraphics: TextGraphics
|
||||
|
||||
var hasStart = false
|
||||
fun start() {
|
||||
if (hasStart) {
|
||||
return
|
||||
}
|
||||
hasStart = true
|
||||
val defaultTerminalFactory = DefaultTerminalFactory()
|
||||
try {
|
||||
terminal = defaultTerminalFactory.createTerminal()
|
||||
terminal.enterPrivateMode()
|
||||
terminal.clearScreen()
|
||||
terminal.setCursorVisible(false)
|
||||
} catch (e: Exception) {
|
||||
try {
|
||||
terminal = SwingTerminalFrame("Mirai Console")
|
||||
terminal.enterPrivateMode()
|
||||
terminal.clearScreen()
|
||||
terminal.setCursorVisible(false)
|
||||
} catch (e: Exception) {
|
||||
error("can not create terminal")
|
||||
}
|
||||
}
|
||||
textGraphics = terminal.newTextGraphics()
|
||||
|
||||
terminal.addResizeListener(TerminalResizeListener { terminal1: Terminal, newSize: TerminalSize ->
|
||||
terminal.clearScreen()
|
||||
inited = false
|
||||
update()
|
||||
redrawCommand()
|
||||
})
|
||||
|
||||
update()
|
||||
|
||||
val charList = listOf(',', '.', '/', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '=', '+', '!', ' ')
|
||||
thread {
|
||||
while (true) {
|
||||
var keyStroke: KeyStroke = terminal.readInput()
|
||||
|
||||
when (keyStroke.keyType) {
|
||||
KeyType.ArrowLeft -> {
|
||||
currentScreenId = getLeftScreenId()
|
||||
update()
|
||||
}
|
||||
KeyType.ArrowRight -> {
|
||||
currentScreenId = getRightScreenId()
|
||||
update()
|
||||
}
|
||||
KeyType.Enter -> {
|
||||
emptyCommand()
|
||||
}
|
||||
else -> {
|
||||
if (keyStroke.character != null) {
|
||||
if (keyStroke.character.toInt() == 8) {
|
||||
deleteCommandChar()
|
||||
}
|
||||
if (keyStroke.character.isLetterOrDigit() || charList.contains(keyStroke.character)) {
|
||||
addCommandChar(keyStroke.character)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getLeftScreenId(): Int {
|
||||
var newId = currentScreenId - 1
|
||||
if (newId < 0) {
|
||||
newId = screens.size - 1
|
||||
}
|
||||
return newId
|
||||
}
|
||||
|
||||
fun getRightScreenId(): Int {
|
||||
var newId = 1 + currentScreenId
|
||||
if (newId >= screens.size) {
|
||||
newId = 0
|
||||
}
|
||||
return newId
|
||||
}
|
||||
|
||||
fun getScreenName(id: Int): String {
|
||||
return when (screens[id]) {
|
||||
0L -> {
|
||||
"Console Screen"
|
||||
}
|
||||
else -> {
|
||||
"Bot: ${screens[id]}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var inited = false
|
||||
fun clearRows(row: Int) {
|
||||
textGraphics.putString(0, row, " ".repeat(terminal.terminalSize.columns))
|
||||
}
|
||||
|
||||
fun drawFrame(
|
||||
title: String
|
||||
) {
|
||||
val width = terminal.terminalSize.columns
|
||||
val height = terminal.terminalSize.rows
|
||||
terminal.setBackgroundColor(TextColor.ANSI.DEFAULT)
|
||||
if (!inited) {
|
||||
val mainTitle = "Mirai Console v0.01 Core v0.15"
|
||||
textGraphics.foregroundColor = TextColor.ANSI.WHITE
|
||||
textGraphics.backgroundColor = TextColor.ANSI.GREEN
|
||||
textGraphics.putString((width - mainTitle.length) / 2, 1, mainTitle, SGR.BOLD)
|
||||
textGraphics.foregroundColor = TextColor.ANSI.DEFAULT
|
||||
textGraphics.backgroundColor = TextColor.ANSI.DEFAULT
|
||||
textGraphics.putString(2, 3, "-".repeat(width - 4))
|
||||
textGraphics.putString(2, 5, "-".repeat(width - 4))
|
||||
textGraphics.putString(2, height - 4, "-".repeat(width - 4))
|
||||
textGraphics.putString(2, height - 3, "|>>>")
|
||||
textGraphics.putString(width - 3, height - 3, "|")
|
||||
textGraphics.putString(2, height - 2, "-".repeat(width - 4))
|
||||
inited = true
|
||||
}
|
||||
textGraphics.foregroundColor = TextColor.ANSI.DEFAULT
|
||||
textGraphics.backgroundColor = TextColor.ANSI.DEFAULT
|
||||
val leftName = getScreenName(getLeftScreenId())
|
||||
clearRows(2)
|
||||
textGraphics.putString((width - title.length) / 2 - "$leftName << ".length, 2, "$leftName << ")
|
||||
textGraphics.foregroundColor = TextColor.ANSI.WHITE
|
||||
textGraphics.backgroundColor = TextColor.ANSI.YELLOW
|
||||
textGraphics.putString((width - title.length) / 2, 2, title, SGR.BOLD)
|
||||
textGraphics.foregroundColor = TextColor.ANSI.DEFAULT
|
||||
textGraphics.backgroundColor = TextColor.ANSI.DEFAULT
|
||||
val rightName = getScreenName(getRightScreenId())
|
||||
textGraphics.putString((width + title.length) / 2 + 1, 2, ">> $rightName")
|
||||
}
|
||||
|
||||
fun drawMainFrame(
|
||||
onlineBotCount: Number
|
||||
) {
|
||||
drawFrame("Console Screen")
|
||||
val width = terminal.terminalSize.columns
|
||||
textGraphics.foregroundColor = TextColor.ANSI.DEFAULT
|
||||
textGraphics.backgroundColor = TextColor.ANSI.DEFAULT
|
||||
clearRows(4)
|
||||
textGraphics.putString(2, 4, "|Online Bots: $onlineBotCount")
|
||||
textGraphics.putString(
|
||||
width - 2 - "Powered By Mamoe Technologies|".length,
|
||||
4,
|
||||
"Powered By Mamoe Technologies|"
|
||||
)
|
||||
}
|
||||
|
||||
fun drawBotFrame(
|
||||
qq: Long,
|
||||
adminCount: Number
|
||||
) {
|
||||
drawFrame("Bot: $qq")
|
||||
val width = terminal.terminalSize.columns
|
||||
textGraphics.foregroundColor = TextColor.ANSI.DEFAULT
|
||||
textGraphics.backgroundColor = TextColor.ANSI.DEFAULT
|
||||
clearRows(4)
|
||||
textGraphics.putString(2, 4, "|Admins: $adminCount")
|
||||
textGraphics.putString(width - 2 - "Add admins via commands|".length, 4, "Add admins via commands|")
|
||||
}
|
||||
|
||||
fun drawLogs(values: List<String>) {
|
||||
val width = terminal.terminalSize.columns - 6
|
||||
val heightMin = 5
|
||||
|
||||
var currentHeight = terminal.terminalSize.rows - 5
|
||||
|
||||
for (index in heightMin until currentHeight) {
|
||||
clearRows(index)
|
||||
}
|
||||
|
||||
values.forEach {
|
||||
if (currentHeight > heightMin) {
|
||||
var x = it
|
||||
while (currentHeight > heightMin) {
|
||||
if (x.isEmpty() || x.isBlank()) break
|
||||
textGraphics.foregroundColor = TextColor.ANSI.GREEN
|
||||
textGraphics.backgroundColor = TextColor.ANSI.DEFAULT
|
||||
val towrite = if (x.length > width) {
|
||||
x.substring(0, width).also {
|
||||
x = x.substring(width)
|
||||
}
|
||||
} else {
|
||||
x.also {
|
||||
x = ""
|
||||
}
|
||||
}
|
||||
textGraphics.putString(3, currentHeight, towrite, SGR.ITALIC)
|
||||
--currentHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textGraphics.putString(3, 9, "AAAAAAAAAAAAAAAAAAAAAAa", SGR.ITALIC)
|
||||
terminal.flush()
|
||||
}
|
||||
|
||||
fun pushLog(uin: Long, str: String) {
|
||||
log[uin]!!.push(str)
|
||||
if (uin == screens[currentScreenId]) {
|
||||
drawLogs(log[screens[currentScreenId]]!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var commandBuilder = StringBuilder()
|
||||
|
||||
fun redrawCommand() {
|
||||
val height = terminal.terminalSize.rows
|
||||
val width = terminal.terminalSize.columns
|
||||
clearRows(height - 3)
|
||||
textGraphics.foregroundColor = TextColor.ANSI.DEFAULT
|
||||
textGraphics.putString(2, height - 3, "|>>>")
|
||||
textGraphics.putString(width - 3, height - 3, "|")
|
||||
textGraphics.foregroundColor = TextColor.ANSI.BLUE
|
||||
textGraphics.putString(7, height - 3, commandBuilder.toString())
|
||||
if (terminal is SwingTerminalFrame) {
|
||||
terminal.flush()
|
||||
}
|
||||
}
|
||||
|
||||
fun addCommandChar(
|
||||
c: Char
|
||||
) {
|
||||
if (commandBuilder.isEmpty() && c != '/') {
|
||||
addCommandChar('/')
|
||||
}
|
||||
textGraphics.foregroundColor = TextColor.ANSI.BLUE
|
||||
val height = terminal.terminalSize.rows
|
||||
commandBuilder.append(c)
|
||||
if (terminal is SwingTerminalFrame) {
|
||||
redrawCommand()
|
||||
} else {
|
||||
textGraphics.putString(6 + commandBuilder.length, height - 3, c.toString())
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteCommandChar() {
|
||||
if (!commandBuilder.isEmpty()) {
|
||||
commandBuilder = StringBuilder(commandBuilder.toString().substring(0, commandBuilder.length - 1))
|
||||
}
|
||||
val height = terminal.terminalSize.rows
|
||||
if (terminal is SwingTerminalFrame) {
|
||||
redrawCommand()
|
||||
} else {
|
||||
textGraphics.putString(7 + commandBuilder.length, height - 3, " ")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun emptyCommand() {
|
||||
commandBuilder = StringBuilder()
|
||||
redrawCommand()
|
||||
if (terminal is SwingTerminal) {
|
||||
terminal.flush()
|
||||
}
|
||||
}
|
||||
|
||||
fun update() {
|
||||
when (screens[currentScreenId]) {
|
||||
0L -> {
|
||||
drawMainFrame(screens.size - 1)
|
||||
}
|
||||
else -> {
|
||||
drawBotFrame(screens[currentScreenId], 0)
|
||||
}
|
||||
}
|
||||
terminal.flush()
|
||||
}
|
||||
}
|
||||
|
||||
class LimitLinkedQueue<T>(
|
||||
val limit: Int = 50
|
||||
) : LinkedList<T>(), List<T> {
|
||||
override fun push(e: T) {
|
||||
if (size >= limit) {
|
||||
pollLast()
|
||||
}
|
||||
super.push(e)
|
||||
}
|
||||
}
|
@ -1,406 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.plugins
|
||||
|
||||
import com.alibaba.fastjson.JSON
|
||||
import com.alibaba.fastjson.JSONObject
|
||||
import com.alibaba.fastjson.TypeReference
|
||||
import com.alibaba.fastjson.parser.Feature
|
||||
import com.moandjiezana.toml.Toml
|
||||
import com.moandjiezana.toml.TomlWriter
|
||||
import kotlinx.serialization.*
|
||||
import org.yaml.snakeyaml.Yaml
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
|
||||
/**
|
||||
* TODO: support all config types
|
||||
* only JSON is now supported
|
||||
*
|
||||
*/
|
||||
|
||||
interface Config {
|
||||
fun getConfigSection(key: String): ConfigSection
|
||||
fun getString(key: String): String
|
||||
fun getInt(key: String): Int
|
||||
fun getFloat(key: String): Float
|
||||
fun getDouble(key: String): Double
|
||||
fun getLong(key: String): Long
|
||||
fun getBoolean(key: String): Boolean
|
||||
fun getList(key: String): List<*>
|
||||
fun getStringList(key: String): List<String>
|
||||
fun getIntList(key: String): List<Int>
|
||||
fun getFloatList(key: String): List<Float>
|
||||
fun getDoubleList(key: String): List<Double>
|
||||
fun getLongList(key: String): List<Long>
|
||||
operator fun set(key: String, value: Any)
|
||||
operator fun get(key: String): Any?
|
||||
fun exist(key: String): Boolean
|
||||
fun setIfAbsent(key: String, value: Any)
|
||||
fun asMap(): Map<String, Any>
|
||||
fun save()
|
||||
|
||||
companion object {
|
||||
fun load(fileName: String): Config {
|
||||
return load(File(fileName.replace("//", "/")))
|
||||
}
|
||||
|
||||
fun load(file: File): Config {
|
||||
if (!file.exists()) {
|
||||
file.createNewFile()
|
||||
}
|
||||
return when (file.extension.toLowerCase()) {
|
||||
"json" -> JsonConfig(file)
|
||||
"yml" -> YamlConfig(file)
|
||||
"yaml" -> YamlConfig(file)
|
||||
"mirai" -> YamlConfig(file)
|
||||
"ini" -> TomlConfig(file)
|
||||
"toml" -> TomlConfig(file)
|
||||
"properties" -> TomlConfig(file)
|
||||
"property" -> TomlConfig(file)
|
||||
"data" -> TomlConfig(file)
|
||||
else -> error("Unsupported file config type ${file.extension.toLowerCase()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun File.loadAsConfig(): Config {
|
||||
return Config.load(this)
|
||||
}
|
||||
|
||||
/* 最简单的代理 */
|
||||
inline operator fun <reified T : Any> Config.getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||
return smartCast(property)
|
||||
}
|
||||
|
||||
inline operator fun <reified T : Any> Config.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
this[property.name] = value
|
||||
}
|
||||
|
||||
/* 带有默认值的代理 */
|
||||
inline fun <reified T : Any> Config.withDefault(
|
||||
noinline defaultValue: () -> T
|
||||
): ReadWriteProperty<Any, T> {
|
||||
val default by lazy { defaultValue.invoke() }
|
||||
return object : ReadWriteProperty<Any, T> {
|
||||
override fun getValue(thisRef: Any, property: KProperty<*>): T {
|
||||
if (this@withDefault.exist(property.name)) {//unsafe
|
||||
return this@withDefault.smartCast(property)
|
||||
}
|
||||
return default
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
|
||||
this@withDefault[property.name] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 带有默认值且如果为空会写入的代理 */
|
||||
inline fun <reified T : Any> Config.withDefaultWrite(
|
||||
noinline defaultValue: () -> T
|
||||
): WithDefaultWriteLoader<T> {
|
||||
return WithDefaultWriteLoader(T::class, this, defaultValue, false)
|
||||
}
|
||||
|
||||
/* 带有默认值且如果为空会写入保存的代理 */
|
||||
inline fun <reified T : Any> Config.withDefaultWriteSave(
|
||||
noinline defaultValue: () -> T
|
||||
): WithDefaultWriteLoader<T> {
|
||||
return WithDefaultWriteLoader(T::class, this, defaultValue, true)
|
||||
}
|
||||
|
||||
class WithDefaultWriteLoader<T : Any>(
|
||||
private val _class: KClass<T>,
|
||||
private val config: Config,
|
||||
private val defaultValue: () -> T,
|
||||
private val save: Boolean
|
||||
) {
|
||||
operator fun provideDelegate(
|
||||
thisRef: Any,
|
||||
prop: KProperty<*>
|
||||
): ReadWriteProperty<Any, T> {
|
||||
val defaultValue by lazy { defaultValue.invoke() }
|
||||
config.setIfAbsent(prop.name, defaultValue)
|
||||
if (save) {
|
||||
config.save()
|
||||
}
|
||||
return object : ReadWriteProperty<Any, T> {
|
||||
override fun getValue(thisRef: Any, property: KProperty<*>): T {
|
||||
if (config.exist(property.name)) {//unsafe
|
||||
return config._smartCast(property.name, _class)
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
|
||||
config[property.name] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> Config.smartCast(property: KProperty<*>): T {
|
||||
return _smartCast(property.name, T::class)
|
||||
}
|
||||
|
||||
@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
|
||||
fun <T : Any> Config._smartCast(propertyName: String, _class: KClass<T>): T {
|
||||
return when (_class) {
|
||||
String::class -> this.getString(propertyName)
|
||||
Int::class -> this.getInt(propertyName)
|
||||
Float::class -> this.getFloat(propertyName)
|
||||
Double::class -> this.getDouble(propertyName)
|
||||
Long::class -> this.getLong(propertyName)
|
||||
Boolean::class -> this.getBoolean(propertyName)
|
||||
else -> when {
|
||||
_class.isSubclassOf(ConfigSection::class) -> this.getConfigSection(propertyName)
|
||||
_class == List::class || _class == MutableList::class -> {
|
||||
val list = this.getList(propertyName)
|
||||
return if (list.isEmpty()) {
|
||||
list
|
||||
} else {
|
||||
when (list[0]!!::class) {
|
||||
String::class -> getStringList(propertyName)
|
||||
Int::class -> getIntList(propertyName)
|
||||
Float::class -> getFloatList(propertyName)
|
||||
Double::class -> getDoubleList(propertyName)
|
||||
Long::class -> getLongList(propertyName)
|
||||
else -> {
|
||||
error("unsupported type")
|
||||
}
|
||||
}
|
||||
} as T
|
||||
}
|
||||
else -> {
|
||||
error("unsupported type")
|
||||
}
|
||||
}
|
||||
} as T
|
||||
}
|
||||
|
||||
|
||||
interface ConfigSection : Config, MutableMap<String, Any> {
|
||||
override fun getConfigSection(key: String): ConfigSection {
|
||||
return (get(key) ?: error("ConfigSection does not contain $key ")) as ConfigSection
|
||||
}
|
||||
|
||||
override fun getString(key: String): String {
|
||||
return (get(key) ?: error("ConfigSection does not contain $key ")).toString()
|
||||
}
|
||||
|
||||
override fun getInt(key: String): Int {
|
||||
return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toInt()
|
||||
}
|
||||
|
||||
override fun getFloat(key: String): Float {
|
||||
return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toFloat()
|
||||
}
|
||||
|
||||
override fun getBoolean(key: String): Boolean {
|
||||
return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toBoolean()
|
||||
}
|
||||
|
||||
override fun getDouble(key: String): Double {
|
||||
return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toDouble()
|
||||
}
|
||||
|
||||
override fun getLong(key: String): Long {
|
||||
return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toLong()
|
||||
}
|
||||
|
||||
override fun getList(key: String): List<*> {
|
||||
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>)
|
||||
}
|
||||
|
||||
override fun getStringList(key: String): List<String> {
|
||||
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString() }
|
||||
}
|
||||
|
||||
override fun getIntList(key: String): List<Int> {
|
||||
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toInt() }
|
||||
}
|
||||
|
||||
override fun getFloatList(key: String): List<Float> {
|
||||
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toFloat() }
|
||||
}
|
||||
|
||||
override fun getDoubleList(key: String): List<Double> {
|
||||
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toDouble() }
|
||||
}
|
||||
|
||||
override fun getLongList(key: String): List<Long> {
|
||||
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toLong() }
|
||||
}
|
||||
|
||||
override fun exist(key: String): Boolean {
|
||||
return get(key) != null
|
||||
}
|
||||
|
||||
override fun setIfAbsent(key: String, value: Any) {
|
||||
if (!exist(key)) set(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
open class ConfigSectionImpl() : ConcurrentHashMap<String, Any>(), ConfigSection {
|
||||
override fun set(key: String, value: Any) {
|
||||
super.put(key, value)
|
||||
}
|
||||
|
||||
override operator fun get(key: String): Any? {
|
||||
return super.get(key)
|
||||
}
|
||||
|
||||
override fun exist(key: String): Boolean {
|
||||
return containsKey(key)
|
||||
}
|
||||
|
||||
override fun asMap(): Map<String, Any> {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
|
||||
}
|
||||
|
||||
override fun setIfAbsent(key: String, value: Any) {
|
||||
this.putIfAbsent(key, value)//atomic
|
||||
}
|
||||
}
|
||||
|
||||
open class ConfigSectionDelegation(
|
||||
private val delegation: MutableMap<String, Any>
|
||||
) : ConfigSection, MutableMap<String, Any> by delegation {
|
||||
override fun set(key: String, value: Any) {
|
||||
delegation.put(key, value)
|
||||
}
|
||||
|
||||
override fun asMap(): Map<String, Any> {
|
||||
return delegation
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface FileConfig : Config {
|
||||
fun deserialize(content: String): ConfigSection
|
||||
|
||||
fun serialize(config: ConfigSection): String
|
||||
}
|
||||
|
||||
|
||||
abstract class FileConfigImpl internal constructor(
|
||||
private val file: File
|
||||
) : FileConfig, ConfigSection {
|
||||
|
||||
private val content by lazy {
|
||||
deserialize(file.readText())
|
||||
}
|
||||
|
||||
override val size: Int get() = content.size
|
||||
override val entries: MutableSet<MutableMap.MutableEntry<String, Any>> get() = content.entries
|
||||
override val keys: MutableSet<String> get() = content.keys
|
||||
override val values: MutableCollection<Any> get() = content.values
|
||||
override fun containsKey(key: String): Boolean = content.containsKey(key)
|
||||
override fun containsValue(value: Any): Boolean = content.containsValue(value)
|
||||
override fun put(key: String, value: Any): Any? = content.put(key, value)
|
||||
override fun isEmpty(): Boolean = content.isEmpty()
|
||||
override fun putAll(from: Map<out String, Any>) = content.putAll(from)
|
||||
override fun clear() = content.clear()
|
||||
override fun remove(key: String): Any? = content.remove(key)
|
||||
|
||||
override fun save() {
|
||||
if (!file.exists()) {
|
||||
file.createNewFile()
|
||||
}
|
||||
file.writeText(serialize(content))
|
||||
}
|
||||
|
||||
override fun get(key: String): Any? {
|
||||
return content[key]
|
||||
}
|
||||
|
||||
override fun set(key: String, value: Any) {
|
||||
content[key] = value
|
||||
}
|
||||
|
||||
override fun asMap(): Map<String, Any> {
|
||||
return content.asMap()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class JsonConfig internal constructor(
|
||||
file: File
|
||||
) : FileConfigImpl(file) {
|
||||
@UnstableDefault
|
||||
override fun deserialize(content: String): ConfigSection {
|
||||
if (content.isEmpty() || content.isBlank() || content == "{}") {
|
||||
return ConfigSectionImpl()
|
||||
}
|
||||
return JSON.parseObject<ConfigSectionImpl>(
|
||||
content,
|
||||
object : TypeReference<ConfigSectionImpl>() {},
|
||||
Feature.OrderedField
|
||||
)
|
||||
}
|
||||
|
||||
@UnstableDefault
|
||||
override fun serialize(config: ConfigSection): String {
|
||||
return JSONObject.toJSONString(config)
|
||||
}
|
||||
}
|
||||
|
||||
class YamlConfig internal constructor(file: File) : FileConfigImpl(file) {
|
||||
override fun deserialize(content: String): ConfigSection {
|
||||
if (content.isEmpty() || content.isBlank()) {
|
||||
return ConfigSectionImpl()
|
||||
}
|
||||
return ConfigSectionDelegation(
|
||||
Collections.synchronizedMap(
|
||||
Yaml().load<LinkedHashMap<String, Any>>(content) as LinkedHashMap<String, Any>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun serialize(config: ConfigSection): String {
|
||||
return Yaml().dumpAsMap(config)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TomlConfig internal constructor(file: File) : FileConfigImpl(file) {
|
||||
override fun deserialize(content: String): ConfigSection {
|
||||
if (content.isEmpty() || content.isBlank()) {
|
||||
return ConfigSectionImpl()
|
||||
}
|
||||
return ConfigSectionDelegation(
|
||||
Collections.synchronizedMap(
|
||||
Toml().read(content).toMap()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun serialize(config: ConfigSection): String {
|
||||
return TomlWriter().write(config)
|
||||
}
|
||||
}
|
@ -1,319 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.plugins
|
||||
|
||||
import net.mamoe.mirai.ICommand
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.io.encodeToString
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.util.jar.JarFile
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
|
||||
abstract class PluginBase(coroutineContext: CoroutineContext) : CoroutineScope {
|
||||
constructor() : this(EmptyCoroutineContext)
|
||||
|
||||
private val supervisorJob = SupervisorJob()
|
||||
final override val coroutineContext: CoroutineContext = coroutineContext + supervisorJob
|
||||
|
||||
val dataFolder: File by lazy {
|
||||
File(PluginManager.pluginsPath + pluginDescription.name).also { it.mkdir() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 当一个插件被加载时调用
|
||||
*/
|
||||
open fun onLoad() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 当所有插件全部被加载后被调用
|
||||
*/
|
||||
open fun onEnable() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 当插件关闭前被调用
|
||||
*/
|
||||
open fun onDisable() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 当任意指令被使用
|
||||
*/
|
||||
open fun onCommand(command: ICommand, args: List<String>) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal fun enable() {
|
||||
this.onEnable()
|
||||
}
|
||||
|
||||
|
||||
fun loadConfig(fileName: String): Config {
|
||||
return Config.load(File(fileName))
|
||||
}
|
||||
|
||||
|
||||
@JvmOverloads
|
||||
internal fun disable(throwable: CancellationException? = null) {
|
||||
this.coroutineContext[Job]!!.cancelChildren(throwable)
|
||||
this.onDisable()
|
||||
}
|
||||
|
||||
private lateinit var pluginDescription: PluginDescription
|
||||
|
||||
internal fun init(pluginDescription: PluginDescription) {
|
||||
this.pluginDescription = pluginDescription
|
||||
this.onLoad()
|
||||
}
|
||||
|
||||
fun getPluginManager() = PluginManager
|
||||
|
||||
val logger: MiraiLogger by lazy {
|
||||
DefaultLogger(pluginDescription.name)
|
||||
}
|
||||
}
|
||||
|
||||
class PluginDescription(
|
||||
val name: String,
|
||||
val author: String,
|
||||
val basePath: String,
|
||||
val version: String,
|
||||
val info: String,
|
||||
val depends: List<String>,//插件的依赖
|
||||
internal var loaded: Boolean = false,
|
||||
internal var noCircularDepend: Boolean = true
|
||||
) {
|
||||
|
||||
override fun toString(): String {
|
||||
return "name: $name\nauthor: $author\npath: $basePath\nver: $version\ninfo: $info\ndepends: $depends"
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun readFromContent(content_: String): PluginDescription {
|
||||
val content = content_.split("\n")
|
||||
|
||||
var name = "Plugin"
|
||||
var author = "Unknown"
|
||||
var basePath = "net.mamoe.mirai.PluginMain"
|
||||
var info = "Unknown"
|
||||
var version = "1.0.0"
|
||||
val depends = mutableListOf<String>();
|
||||
|
||||
content.forEach {
|
||||
val line = it.trim()
|
||||
val lowercaseLine = line.toLowerCase()
|
||||
if (it.contains(":")) {
|
||||
when {
|
||||
lowercaseLine.startsWith("name") -> {
|
||||
name = line.substringAfter(":").trim()
|
||||
}
|
||||
lowercaseLine.startsWith("author") -> {
|
||||
author = line.substringAfter(":").trim()
|
||||
}
|
||||
lowercaseLine.startsWith("info") || lowercaseLine.startsWith("information") -> {
|
||||
info = line.substringAfter(":").trim()
|
||||
}
|
||||
lowercaseLine.startsWith("main") || lowercaseLine.startsWith("path") || lowercaseLine.startsWith(
|
||||
"basepath"
|
||||
) -> {
|
||||
basePath = line.substringAfter(":").trim()
|
||||
}
|
||||
lowercaseLine.startsWith("version") || lowercaseLine.startsWith("ver") -> {
|
||||
version = line.substringAfter(":").trim()
|
||||
}
|
||||
}
|
||||
} else if (line.startsWith("-")) {
|
||||
depends.add(line.substringAfter("-").trim())
|
||||
}
|
||||
}
|
||||
return PluginDescription(name, author, basePath, version, info, depends)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class PluginClassLoader(file: File, parent: ClassLoader) : URLClassLoader(arrayOf(file.toURI().toURL()), parent)
|
||||
|
||||
object PluginManager {
|
||||
internal val pluginsPath = System.getProperty("user.dir") + "/plugins/".replace("//", "/").also {
|
||||
File(it).mkdirs()
|
||||
}
|
||||
|
||||
val logger = DefaultLogger("Mirai Plugin Manager")
|
||||
|
||||
//已完成加载的
|
||||
private val nameToPluginBaseMap: MutableMap<String, PluginBase> = mutableMapOf()
|
||||
private val pluginDescriptions: MutableMap<String, PluginDescription> = mutableMapOf()
|
||||
|
||||
fun onCommand(command: ICommand, args: List<String>) {
|
||||
nameToPluginBaseMap.values.forEach {
|
||||
it.onCommand(command, args)
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllPluginDescriptions(): Collection<PluginDescription> {
|
||||
return pluginDescriptions.values
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试加载全部插件
|
||||
*/
|
||||
fun loadPlugins() {
|
||||
val pluginsFound: MutableMap<String, PluginDescription> = mutableMapOf()
|
||||
val pluginsLocation: MutableMap<String, File> = mutableMapOf()
|
||||
|
||||
logger.info("""开始加载${pluginsPath}下的插件""")
|
||||
|
||||
File(pluginsPath).listFiles()?.forEach { file ->
|
||||
if (file != null && file.extension == "jar") {
|
||||
val jar = JarFile(file)
|
||||
val pluginYml =
|
||||
jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull()
|
||||
if (pluginYml == null) {
|
||||
logger.info("plugin.yml not found in jar " + jar.name + ", it will not be consider as a Plugin")
|
||||
} else {
|
||||
val description =
|
||||
PluginDescription.readFromContent(URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name).openConnection().inputStream.use {
|
||||
it.readBytes().encodeToString()
|
||||
})
|
||||
pluginsFound[description.name] = description
|
||||
pluginsLocation[description.name] = file
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun checkNoCircularDepends(
|
||||
target: PluginDescription,
|
||||
needDepends: List<String>,
|
||||
existDepends: MutableList<String>
|
||||
) {
|
||||
|
||||
if (!target.noCircularDepend) {
|
||||
return
|
||||
}
|
||||
|
||||
existDepends.add(target.name)
|
||||
|
||||
if (needDepends.any { existDepends.contains(it) }) {
|
||||
target.noCircularDepend = false
|
||||
}
|
||||
|
||||
existDepends.addAll(needDepends)
|
||||
|
||||
needDepends.forEach {
|
||||
if (pluginsFound.containsKey(it)) {
|
||||
checkNoCircularDepends(pluginsFound[it]!!, pluginsFound[it]!!.depends, existDepends)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pluginsFound.values.forEach {
|
||||
checkNoCircularDepends(it, it.depends, mutableListOf())
|
||||
}
|
||||
|
||||
//load
|
||||
|
||||
|
||||
fun loadPlugin(description: PluginDescription): Boolean {
|
||||
if (!description.noCircularDepend) {
|
||||
logger.error("Failed to load plugin " + description.name + " because it has circular dependency")
|
||||
return false
|
||||
}
|
||||
|
||||
//load depends first
|
||||
description.depends.forEach { dependent ->
|
||||
if (!pluginsFound.containsKey(dependent)) {
|
||||
logger.error("Failed to load plugin " + description.name + " because it need " + dependent + " as dependency")
|
||||
return false
|
||||
}
|
||||
val depend = pluginsFound[dependent]!!
|
||||
//还没有加载
|
||||
if (!depend.loaded && !loadPlugin(pluginsFound[dependent]!!)) {
|
||||
logger.error("Failed to load plugin " + description.name + " because " + dependent + " as dependency failed to load")
|
||||
return false
|
||||
}
|
||||
}
|
||||
//在这里所有的depends都已经加载了
|
||||
|
||||
|
||||
//real load
|
||||
logger.info("loading plugin " + description.name)
|
||||
|
||||
try {
|
||||
val pluginClass = try {
|
||||
PluginClassLoader(
|
||||
(pluginsLocation[description.name]!!),
|
||||
this.javaClass.classLoader
|
||||
)
|
||||
.loadClass(description.basePath)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
logger.info("failed to find Main: " + description.basePath + " checking if it's kotlin's path")
|
||||
PluginClassLoader(
|
||||
(pluginsLocation[description.name]!!),
|
||||
this.javaClass.classLoader
|
||||
)
|
||||
.loadClass("${description.basePath}Kt")
|
||||
}
|
||||
return try {
|
||||
val subClass = pluginClass.asSubclass(PluginBase::class.java)
|
||||
val plugin: PluginBase = subClass.getDeclaredConstructor().newInstance()
|
||||
description.loaded = true
|
||||
logger.info("successfully loaded plugin " + description.name + " version " + description.version + " by " + description.author)
|
||||
logger.info(description.info)
|
||||
|
||||
nameToPluginBaseMap[description.name] = plugin
|
||||
pluginDescriptions[description.name] = description
|
||||
plugin.init(description)
|
||||
true
|
||||
} catch (e: ClassCastException) {
|
||||
logger.error("failed to load plugin " + description.name + " , Main class does not extends PluginBase ")
|
||||
false
|
||||
}
|
||||
} catch (e: ClassNotFoundException) {
|
||||
e.printStackTrace()
|
||||
logger.error("failed to load plugin " + description.name + " , Main class not found under " + description.basePath)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
pluginsFound.values.forEach {
|
||||
loadPlugin(it)
|
||||
}
|
||||
|
||||
nameToPluginBaseMap.values.forEach {
|
||||
it.enable()
|
||||
}
|
||||
|
||||
logger.info("""加载了${nameToPluginBaseMap.size}个插件""")
|
||||
|
||||
}
|
||||
|
||||
|
||||
@JvmOverloads
|
||||
fun disableAllPlugins(throwable: CancellationException? = null) {
|
||||
nameToPluginBaseMap.values.forEach {
|
||||
it.disable(throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,8 +1,3 @@
|
||||
# mirai-core-qqandroid
|
||||
|
||||
Protocol support for QQ for Android for Mirai.
|
||||
|
||||
Not yet available to work.
|
||||
|
||||
## Protocol Structure
|
||||
See [README.md](src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/README.md)
|
@ -5,10 +5,10 @@ plugins {
|
||||
id("kotlinx-atomicfu")
|
||||
id("kotlinx-serialization")
|
||||
`maven-publish`
|
||||
id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME
|
||||
id("com.jfrog.bintray") version "1.8.4-jetbrains-3"
|
||||
}
|
||||
|
||||
apply(from = rootProject.file("gradle/publish.gradle"))
|
||||
apply(plugin = "com.github.johnrengelman.shadow")
|
||||
|
||||
val kotlinVersion: String by rootProject.ext
|
||||
val atomicFuVersion: String by rootProject.ext
|
||||
@ -16,7 +16,7 @@ val coroutinesVersion: String by rootProject.ext
|
||||
val kotlinXIoVersion: String by rootProject.ext
|
||||
val coroutinesIoVersion: String by rootProject.ext
|
||||
|
||||
val klockVersion: String by rootProject.ext
|
||||
|
||||
val ktorVersion: String by rootProject.ext
|
||||
|
||||
val serializationVersion: String by rootProject.ext
|
||||
@ -27,10 +27,12 @@ fun ktor(id: String, version: String) = "io.ktor:ktor-$id:$version"
|
||||
|
||||
|
||||
description = "QQ protocol library"
|
||||
version = rootProject.ext.get("mirai_version")!!.toString()
|
||||
|
||||
val isAndroidSDKAvailable: Boolean by project
|
||||
|
||||
val miraiVersion: String by project
|
||||
version = miraiVersion
|
||||
|
||||
kotlin {
|
||||
if (isAndroidSDKAvailable) {
|
||||
apply(from = rootProject.file("gradle/android.gradle"))
|
||||
@ -75,6 +77,7 @@ kotlin {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(kotlinx("serialization-runtime-common", serializationVersion))
|
||||
api(kotlinx("serialization-protobuf-common", serializationVersion))
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
@ -88,6 +91,7 @@ kotlin {
|
||||
if (isAndroidSDKAvailable) {
|
||||
val androidMain by getting {
|
||||
dependencies {
|
||||
api(kotlinx("serialization-protobuf", serializationVersion))
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,6 +109,7 @@ kotlin {
|
||||
dependencies {
|
||||
runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
|
||||
api(kotlinx("serialization-runtime", serializationVersion))
|
||||
//api(kotlinx("serialization-protobuf", serializationVersion))
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,3 +125,9 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
// kotlinOptions.jvmTarget = "1.8"
|
||||
//}
|
||||
|
||||
apply(from = rootProject.file("gradle/publish.gradle"))
|
||||
|
@ -19,9 +19,23 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
/**
|
||||
* QQ for Android
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
actual object QQAndroid : BotFactory {
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
@JvmName("newBot")
|
||||
actual override fun Bot(context: Context, qq: Long, password: String, configuration: BotConfiguration): Bot {
|
||||
return QQAndroidBot(context, BotAccount(qq, password), configuration)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定的 [配置][configuration] 构造 [Bot] 实例
|
||||
*/
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
@JvmName("newBot")
|
||||
actual override fun Bot(
|
||||
context: Context,
|
||||
qq: Long,
|
||||
passwordMd5: ByteArray,
|
||||
configuration: BotConfiguration
|
||||
): Bot = QQAndroidBot(context, BotAccount(qq, passwordMd5), configuration)
|
||||
}
|
@ -9,15 +9,188 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid
|
||||
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.consumeEachBufferRange
|
||||
import io.ktor.utils.io.core.Input
|
||||
import io.ktor.utils.io.core.readBytes
|
||||
import kotlinx.coroutines.io.*
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.utils.BotConfiguration
|
||||
import net.mamoe.mirai.utils.Context
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||
import net.mamoe.mirai.utils.io.toReadPacket
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal actual class QQAndroidBot
|
||||
actual constructor(
|
||||
context: Context,
|
||||
account: BotAccount,
|
||||
configuration: BotConfiguration
|
||||
) : QQAndroidBotBase(context, account, configuration)
|
||||
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
@Suppress("DEPRECATION")
|
||||
internal actual fun ByteReadChannel.toKotlinByteReadChannel(): kotlinx.coroutines.io.ByteReadChannel {
|
||||
return object : kotlinx.coroutines.io.ByteReadChannel {
|
||||
override val availableForRead: Int
|
||||
get() = this@toKotlinByteReadChannel.availableForRead
|
||||
override val isClosedForRead: Boolean
|
||||
get() = this@toKotlinByteReadChannel.isClosedForRead
|
||||
override val isClosedForWrite: Boolean
|
||||
get() = this@toKotlinByteReadChannel.isClosedForWrite
|
||||
|
||||
@Suppress("DEPRECATION_ERROR", "OverridingDeprecatedMember")
|
||||
override var readByteOrder: ByteOrder
|
||||
get() = when (this@toKotlinByteReadChannel.readByteOrder) {
|
||||
io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN -> ByteOrder.BIG_ENDIAN
|
||||
io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN -> ByteOrder.LITTLE_ENDIAN
|
||||
}
|
||||
set(value) {
|
||||
this@toKotlinByteReadChannel.readByteOrder = when (value) {
|
||||
ByteOrder.BIG_ENDIAN -> io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN
|
||||
ByteOrder.LITTLE_ENDIAN -> io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION_ERROR", "DEPRECATION", "OverridingDeprecatedMember")
|
||||
override val totalBytesRead: Long
|
||||
get() = this@toKotlinByteReadChannel.totalBytesRead
|
||||
|
||||
override fun cancel(cause: Throwable?): Boolean = this@toKotlinByteReadChannel.cancel(cause)
|
||||
override suspend fun consumeEachBufferRange(visitor: ConsumeEachBufferVisitor) =
|
||||
this@toKotlinByteReadChannel.consumeEachBufferRange(visitor)
|
||||
|
||||
override suspend fun discard(max: Long): Long = this@toKotlinByteReadChannel.discard(max)
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
|
||||
@ExperimentalIoApi
|
||||
override fun <R> lookAhead(visitor: LookAheadSession.() -> R): R {
|
||||
return this@toKotlinByteReadChannel.lookAhead l@{
|
||||
visitor(object : LookAheadSession {
|
||||
override fun consumed(n: Int) {
|
||||
return this@l.consumed(n)
|
||||
}
|
||||
|
||||
override fun request(skip: Int, atLeast: Int): ByteBuffer? {
|
||||
return this@l.request(skip, atLeast)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
|
||||
@ExperimentalIoApi
|
||||
override suspend fun <R> lookAheadSuspend(visitor: suspend LookAheadSuspendSession.() -> R): R =
|
||||
this@toKotlinByteReadChannel.lookAheadSuspend l@{
|
||||
visitor(object : LookAheadSuspendSession {
|
||||
override suspend fun awaitAtLeast(n: Int): Boolean {
|
||||
return this@l.awaitAtLeast(n)
|
||||
}
|
||||
|
||||
override fun consumed(n: Int) {
|
||||
return this@l.consumed(n)
|
||||
}
|
||||
|
||||
override fun request(skip: Int, atLeast: Int): ByteBuffer? {
|
||||
return this@l.request(skip, atLeast)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override suspend fun read(min: Int, consumer: (ByteBuffer) -> Unit) =
|
||||
this@toKotlinByteReadChannel.read(min, consumer)
|
||||
|
||||
override suspend fun readAvailable(dst: ByteBuffer): Int = this@toKotlinByteReadChannel.readAvailable(dst)
|
||||
override suspend fun readAvailable(dst: ByteArray, offset: Int, length: Int): Int =
|
||||
this@toKotlinByteReadChannel.readAvailable(dst, offset, length)
|
||||
|
||||
override suspend fun readAvailable(dst: IoBuffer): Int {
|
||||
ByteArrayPool.useInstance {
|
||||
val read = this@toKotlinByteReadChannel.readAvailable(it, 0, it.size)
|
||||
dst.writeFully(it, 0, read)
|
||||
return read
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun readBoolean(): Boolean = this@toKotlinByteReadChannel.readBoolean()
|
||||
override suspend fun readByte(): Byte = this@toKotlinByteReadChannel.readByte()
|
||||
override suspend fun readDouble(): Double = this@toKotlinByteReadChannel.readDouble()
|
||||
override suspend fun readFloat(): Float = this@toKotlinByteReadChannel.readFloat()
|
||||
override suspend fun readFully(dst: ByteBuffer): Int {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override suspend fun readFully(dst: ByteArray, offset: Int, length: Int) =
|
||||
this@toKotlinByteReadChannel.readFully(dst, offset, length)
|
||||
|
||||
override suspend fun readFully(dst: IoBuffer, n: Int) {
|
||||
ByteArrayPool.useInstance {
|
||||
dst.writeFully(it, 0, this.readAvailable(it, 0, it.size))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun readInt(): Int = this@toKotlinByteReadChannel.readInt()
|
||||
override suspend fun readLong(): Long = this@toKotlinByteReadChannel.readLong()
|
||||
override suspend fun readPacket(size: Int, headerSizeHint: Int): ByteReadPacket {
|
||||
return this@toKotlinByteReadChannel.readPacket(size, headerSizeHint).readBytes().toReadPacket()
|
||||
}
|
||||
|
||||
override suspend fun readRemaining(limit: Long, headerSizeHint: Int): ByteReadPacket {
|
||||
return this@toKotlinByteReadChannel.readRemaining(limit, headerSizeHint).readBytes().toReadPacket()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalIoApi::class)
|
||||
@ExperimentalIoApi
|
||||
override fun readSession(consumer: ReadSession.() -> Unit) {
|
||||
@Suppress("DEPRECATION")
|
||||
this@toKotlinByteReadChannel.readSession lambda@{
|
||||
consumer(object : ReadSession {
|
||||
override val availableForRead: Int
|
||||
get() = this@lambda.availableForRead
|
||||
|
||||
override fun discard(n: Int): Int = this@lambda.discard(n)
|
||||
|
||||
override fun request(atLeast: Int): IoBuffer? {
|
||||
val ioBuffer: io.ktor.utils.io.core.IoBuffer = this@lambda.request(atLeast) ?: return null
|
||||
val buffer = IoBuffer.Pool.borrow()
|
||||
val bytes = (ioBuffer as Input).readBytes()
|
||||
buffer.writeFully(bytes)
|
||||
return buffer
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun readShort(): Short = this@toKotlinByteReadChannel.readShort()
|
||||
|
||||
@Suppress("EXPERIMENTAL_OVERRIDE", "EXPERIMENTAL_API_USAGE")
|
||||
@ExperimentalIoApi
|
||||
override suspend fun readSuspendableSession(consumer: suspend SuspendableReadSession.() -> Unit) =
|
||||
this@toKotlinByteReadChannel.readSuspendableSession l@{
|
||||
consumer(object : SuspendableReadSession {
|
||||
override val availableForRead: Int
|
||||
get() = this@l.availableForRead
|
||||
|
||||
override suspend fun await(atLeast: Int): Boolean = this@l.await(atLeast)
|
||||
override fun discard(n: Int): Int = this@l.discard(n)
|
||||
override fun request(atLeast: Int): IoBuffer? {
|
||||
@Suppress("DuplicatedCode") val ioBuffer: io.ktor.utils.io.core.IoBuffer =
|
||||
this@l.request(atLeast) ?: return null
|
||||
val buffer = IoBuffer.Pool.borrow()
|
||||
val bytes = (ioBuffer as Input).readBytes()
|
||||
buffer.writeFully(bytes)
|
||||
return buffer
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override suspend fun readUTF8Line(limit: Int): String? = this@toKotlinByteReadChannel.readUTF8Line(limit)
|
||||
override suspend fun <A : Appendable> readUTF8LineTo(out: A, limit: Int): Boolean =
|
||||
this@toKotlinByteReadChannel.readUTF8LineTo(out, limit)
|
||||
}
|
||||
}
|
@ -11,16 +11,31 @@ package net.mamoe.mirai.qqandroid
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.BotFactory
|
||||
import net.mamoe.mirai.qqandroid.QQAndroid.Bot
|
||||
import net.mamoe.mirai.utils.BotConfiguration
|
||||
import net.mamoe.mirai.utils.Context
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
* QQ for Android
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
expect object QQAndroid : BotFactory {
|
||||
|
||||
/**
|
||||
* 使用指定的 [配置][configuration] 构造 [Bot] 实例
|
||||
*/
|
||||
@JvmName("newBot")
|
||||
override fun Bot(context: Context, qq: Long, password: String, configuration: BotConfiguration): Bot
|
||||
|
||||
/**
|
||||
* 使用指定的 [配置][configuration] 构造 [Bot] 实例
|
||||
*/
|
||||
@JvmName("newBot")
|
||||
override fun Bot(
|
||||
context: Context,
|
||||
qq: Long,
|
||||
passwordMd5: ByteArray,
|
||||
configuration: BotConfiguration
|
||||
): Bot
|
||||
}
|
@ -10,16 +10,21 @@
|
||||
package net.mamoe.mirai.qqandroid
|
||||
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlinx.io.core.Closeable
|
||||
import net.mamoe.mirai.LowLevelAPI
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.*
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
|
||||
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
|
||||
import net.mamoe.mirai.message.data.CustomFaceFromFile
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.NotOnlineImageFromFile
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import net.mamoe.mirai.message.data.OfflineFriendImage
|
||||
import net.mamoe.mirai.message.data.OfflineGroupImage
|
||||
import net.mamoe.mirai.qqandroid.message.MessageSourceFromSendGroup
|
||||
import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper
|
||||
import net.mamoe.mirai.qqandroid.network.highway.postImage
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo
|
||||
@ -31,24 +36,11 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
|
||||
import net.mamoe.mirai.qqandroid.utils.toIpV4AddressString
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.io.toUHexString
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.FriendInfo as JceFriendInfo
|
||||
|
||||
internal abstract class ContactImpl : Contact {
|
||||
override fun hashCode(): Int {
|
||||
var result = bot.hashCode()
|
||||
result = 31 * result + id.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Contact) return false
|
||||
if (this::class != other::class) return false
|
||||
return this.id == other.id && this.bot == other.bot
|
||||
}
|
||||
}
|
||||
|
||||
internal inline class FriendInfoImpl(
|
||||
private val jceFriendInfo: JceFriendInfo
|
||||
) : FriendInfo {
|
||||
@ -61,28 +53,33 @@ internal class QQImpl(
|
||||
override val coroutineContext: CoroutineContext,
|
||||
override val id: Long,
|
||||
private val friendInfo: FriendInfo
|
||||
) : ContactImpl(), QQ {
|
||||
) : QQ() {
|
||||
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||
override val nick: String
|
||||
get() = friendInfo.nick
|
||||
|
||||
override suspend fun sendMessage(message: MessageChain) {
|
||||
override suspend fun sendMessage(message: MessageChain): MessageReceipt<QQ> {
|
||||
val event = FriendMessageSendEvent(this, message).broadcast()
|
||||
if (event.isCancelled) {
|
||||
throw EventCancelledException("cancelled by FriendMessageSendEvent")
|
||||
}
|
||||
lateinit var source: MessageSource
|
||||
bot.network.run {
|
||||
check(
|
||||
MessageSvc.PbSendMsg.ToFriend(
|
||||
bot.client,
|
||||
id,
|
||||
event.message
|
||||
).sendAndExpect<MessageSvc.PbSendMsg.Response>() is MessageSvc.PbSendMsg.Response.SUCCESS
|
||||
) {
|
||||
source = it
|
||||
}.sendAndExpect<MessageSvc.PbSendMsg.Response>() is MessageSvc.PbSendMsg.Response.SUCCESS
|
||||
) { "send message failed" }
|
||||
}
|
||||
return MessageReceipt(source, this, null)
|
||||
}
|
||||
|
||||
override suspend fun uploadImage(image: ExternalImage): Image = try {
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = try {
|
||||
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
|
||||
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
|
||||
}
|
||||
@ -102,8 +99,9 @@ internal class QQImpl(
|
||||
)
|
||||
).sendAndExpect<LongConn.OffPicUp.Response>()
|
||||
|
||||
@Suppress("UNCHECKED_CAST") // bug
|
||||
return when (response) {
|
||||
is LongConn.OffPicUp.Response.FileExists -> NotOnlineImageFromFile(
|
||||
is LongConn.OffPicUp.Response.FileExists -> OfflineFriendImage(
|
||||
filepath = response.resourceId,
|
||||
md5 = response.imageInfo.fileMd5,
|
||||
fileLength = response.imageInfo.fileSize.toInt(),
|
||||
@ -114,19 +112,27 @@ internal class QQImpl(
|
||||
ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
|
||||
}
|
||||
is LongConn.OffPicUp.Response.RequireUpload -> {
|
||||
Http.postImage("0x6ff0070", bot.uin, null, imageInput = image.input, inputSize = image.inputSize, uKeyHex = response.uKey.toUHexString(""))
|
||||
MiraiPlatformUtils.Http.postImage(
|
||||
"0x6ff0070",
|
||||
bot.uin,
|
||||
null,
|
||||
imageInput = image.input,
|
||||
inputSize = image.inputSize,
|
||||
uKeyHex = response.uKey.toUHexString("")
|
||||
)
|
||||
//HighwayHelper.uploadImage(
|
||||
// client = bot.client,
|
||||
// serverIp = response.serverIp[0].toIpV4AddressString(),
|
||||
// serverPort = response.serverPort[0],
|
||||
// imageInput = image.input,
|
||||
// inputSize = image.inputSize.toInt(),
|
||||
// md5 = image.md5,
|
||||
// fileMd5 = image.md5,
|
||||
// uKey = response.uKey,
|
||||
// commandId = 1
|
||||
//)
|
||||
// 为什么不能 ??
|
||||
|
||||
return NotOnlineImageFromFile(
|
||||
return OfflineFriendImage(
|
||||
filepath = response.resourceId,
|
||||
md5 = image.md5,
|
||||
fileLength = image.inputSize.toInt(),
|
||||
@ -144,7 +150,22 @@ internal class QQImpl(
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
image.input.close()
|
||||
(image.input as? Closeable)?.close()
|
||||
(image.input as? Closeable)?.close()
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = bot.hashCode()
|
||||
result = 31 * result + id.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@Suppress("DuplicatedCode")
|
||||
if (this === other) return true
|
||||
if (other !is Contact) return false
|
||||
if (this::class != other::class) return false
|
||||
return this.id == other.id && this.bot == other.bot
|
||||
}
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
@ -162,29 +183,54 @@ internal class QQImpl(
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
return other is QQ && other.id == this.id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = super.hashCode()
|
||||
override fun toString(): String = "QQ($id)"
|
||||
}
|
||||
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
internal class MemberImpl(
|
||||
qq: QQImpl,
|
||||
val qq: QQImpl, // 不要 WeakRef
|
||||
group: GroupImpl,
|
||||
override val coroutineContext: CoroutineContext,
|
||||
memberInfo: MemberInfo
|
||||
) : ContactImpl(), Member, QQ by qq {
|
||||
) : Member() {
|
||||
override val group: GroupImpl by group.unsafeWeakRef()
|
||||
val qq: QQImpl by qq.unsafeWeakRef()
|
||||
|
||||
// region QQ delegate
|
||||
override val id: Long = qq.id
|
||||
override val nick: String = qq.nick
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
override suspend fun queryProfile(): Profile = qq.queryProfile()
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
override suspend fun queryPreviousNameList(): PreviousNameList = qq.queryPreviousNameList()
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
override suspend fun queryRemark(): FriendNameRemark = qq.queryRemark()
|
||||
|
||||
override suspend fun sendMessage(message: MessageChain): MessageReceipt<QQ> = qq.sendMessage(message)
|
||||
override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = qq.uploadImage(image)
|
||||
// endregion
|
||||
|
||||
override var permission: MemberPermission = memberInfo.permission
|
||||
|
||||
@Suppress("PropertyName")
|
||||
internal var _nameCard: String = memberInfo.nameCard
|
||||
|
||||
@Suppress("PropertyName")
|
||||
internal var _specialTitle: String = memberInfo.specialTitle
|
||||
|
||||
@Suppress("PropertyName")
|
||||
var _muteTimestamp: Int = memberInfo.muteTimestamp
|
||||
|
||||
override val muteTimeRemaining: Int =
|
||||
if (_muteTimestamp == 0 || _muteTimestamp == 0xFFFFFFFF.toInt()) {
|
||||
0
|
||||
} else {
|
||||
_muteTimestamp - currentTimeSeconds.toInt() - bot.client.timeDifference.toInt()
|
||||
}
|
||||
|
||||
override var nameCard: String
|
||||
get() = _nameCard
|
||||
set(newValue) {
|
||||
@ -220,7 +266,7 @@ internal class MemberImpl(
|
||||
newValue
|
||||
).sendWithoutExpect()
|
||||
}
|
||||
MemberSpecialTitleChangeEvent(oldValue, newValue, this@MemberImpl).broadcast()
|
||||
MemberSpecialTitleChangeEvent(oldValue, newValue, this@MemberImpl, null).broadcast()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -279,62 +325,102 @@ internal class MemberImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
return other is Member && other.id == this.id
|
||||
override fun hashCode(): Int {
|
||||
var result = bot.hashCode()
|
||||
result = 31 * result + id.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = super.hashCode()
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Contact) return false
|
||||
if (this::class != other::class) return false
|
||||
return this.id == other.id && this.bot == other.bot
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Member($id)"
|
||||
}
|
||||
}
|
||||
|
||||
internal class MemberInfoImpl(
|
||||
private val jceInfo: StTroopMemberInfo,
|
||||
private val groupOwnerId: Long
|
||||
jceInfo: StTroopMemberInfo,
|
||||
groupOwnerId: Long
|
||||
) : MemberInfo {
|
||||
override val uin: Long get() = jceInfo.memberUin
|
||||
override val nameCard: String get() = jceInfo.sName ?: ""
|
||||
override val nick: String get() = jceInfo.nick
|
||||
override val permission: MemberPermission
|
||||
get() = when {
|
||||
override val uin: Long = jceInfo.memberUin
|
||||
override val nameCard: String = jceInfo.sName ?: ""
|
||||
override val nick: String = jceInfo.nick
|
||||
override val permission: MemberPermission = when {
|
||||
jceInfo.memberUin == groupOwnerId -> MemberPermission.OWNER
|
||||
jceInfo.dwFlag == 1L -> MemberPermission.ADMINISTRATOR
|
||||
else -> MemberPermission.MEMBER
|
||||
}
|
||||
override val specialTitle: String get() = jceInfo.sSpecialTitle ?: ""
|
||||
override val specialTitle: String = jceInfo.sSpecialTitle ?: ""
|
||||
override val muteTimestamp: Int = jceInfo.dwShutupTimestap?.toInt() ?: 0
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
internal fun GroupImpl.Companion.checkIsInstance(expression: Boolean) {
|
||||
contract {
|
||||
returns() implies expression
|
||||
}
|
||||
check(expression) { "group is not an instanceof GroupImpl!! DO NOT interlace two or more protocol implementations!!" }
|
||||
}
|
||||
|
||||
/**
|
||||
* 对GroupImpl
|
||||
* 中name/announcement的更改会直接向服务器异步汇报
|
||||
*/
|
||||
@Suppress("PropertyName")
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal class GroupImpl(
|
||||
bot: QQAndroidBot, override val coroutineContext: CoroutineContext,
|
||||
override val id: Long,
|
||||
groupInfo: GroupInfo,
|
||||
members: Sequence<MemberInfo>
|
||||
) : ContactImpl(), Group {
|
||||
) : Group() {
|
||||
@Suppress("\"RemoveEmptyClassBody\"") // things will go wrong after removal, don't try
|
||||
companion object {
|
||||
|
||||
}
|
||||
|
||||
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||
val uin: Long = groupInfo.uin
|
||||
|
||||
override lateinit var owner: Member
|
||||
|
||||
@UseExperimental(MiraiExperimentalAPI::class)
|
||||
@OptIn(MiraiExperimentalAPI::class)
|
||||
override val botAsMember: Member by lazy {
|
||||
Member(object : MemberInfo {
|
||||
override val nameCard: String
|
||||
get() = bot.nick // TODO: 2020/2/21 机器人在群内的昵称获取
|
||||
override val permission: MemberPermission
|
||||
get() = botPermission
|
||||
override val specialTitle: String
|
||||
get() = "" // TODO: 2020/2/21 获取机器人在群里的头衔
|
||||
override val muteTimestamp: Int
|
||||
get() = botMuteRemaining
|
||||
override val uin: Long
|
||||
get() = bot.uin
|
||||
override val nick: String
|
||||
get() = bot.nick
|
||||
})
|
||||
}
|
||||
|
||||
@OptIn(MiraiExperimentalAPI::class)
|
||||
override lateinit var botPermission: MemberPermission
|
||||
|
||||
var _botMuteRemaining: Int = groupInfo.botMuteRemaining
|
||||
var _botMuteTimestamp: Int = groupInfo.botMuteRemaining
|
||||
|
||||
override val botMuteRemaining: Int =
|
||||
if (_botMuteRemaining == 0 || _botMuteRemaining == 0xFFFFFFFF.toInt()) {
|
||||
if (_botMuteTimestamp == 0 || _botMuteTimestamp == 0xFFFFFFFF.toInt()) {
|
||||
0
|
||||
} else {
|
||||
_botMuteRemaining - currentTimeSeconds.toInt() - bot.client.timeDifference.toInt()
|
||||
_botMuteTimestamp - currentTimeSeconds.toInt() - bot.client.timeDifference.toInt()
|
||||
}
|
||||
|
||||
override val members: ContactList<Member> = ContactList(members.mapNotNull {
|
||||
if (it.uin == bot.uin) {
|
||||
botPermission = it.permission
|
||||
if (it.permission == MemberPermission.OWNER) {
|
||||
owner = botAsMember
|
||||
}
|
||||
null
|
||||
} else Member(it).also { member ->
|
||||
if (member.permission == MemberPermission.OWNER) {
|
||||
@ -344,11 +430,11 @@ internal class GroupImpl(
|
||||
}.toLockFreeLinkedList())
|
||||
|
||||
internal var _name: String = groupInfo.name
|
||||
internal var _announcement: String = groupInfo.memo
|
||||
internal var _allowMemberInvite: Boolean = groupInfo.allowMemberInvite
|
||||
private var _announcement: String = groupInfo.memo
|
||||
private var _allowMemberInvite: Boolean = groupInfo.allowMemberInvite
|
||||
internal var _confessTalk: Boolean = groupInfo.confessTalk
|
||||
internal var _muteAll: Boolean = groupInfo.muteAll
|
||||
internal var _autoApprove: Boolean = groupInfo.autoApprove
|
||||
private var _autoApprove: Boolean = groupInfo.autoApprove
|
||||
internal var _anonymousChat: Boolean = groupInfo.allowAnonymousChat
|
||||
|
||||
override var name: String
|
||||
@ -414,12 +500,14 @@ internal class GroupImpl(
|
||||
|
||||
override var isAutoApproveEnabled: Boolean
|
||||
get() = _autoApprove
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
set(newValue) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override var isAnonymousChatEnabled: Boolean
|
||||
get() = _anonymousChat
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
set(newValue) {
|
||||
TODO()
|
||||
}
|
||||
@ -465,15 +553,17 @@ internal class GroupImpl(
|
||||
}
|
||||
}
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
override suspend fun quit(): Boolean {
|
||||
check(botPermission != MemberPermission.OWNER) { "An owner cannot quit from a owning group" }
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
@UseExperimental(MiraiExperimentalAPI::class)
|
||||
@OptIn(MiraiExperimentalAPI::class)
|
||||
override fun Member(memberInfo: MemberInfo): Member {
|
||||
return MemberImpl(
|
||||
bot.QQ(memberInfo) as QQImpl,
|
||||
@OptIn(LowLevelAPI::class)
|
||||
bot._lowLevelNewQQ(memberInfo) as QQImpl,
|
||||
this,
|
||||
this.coroutineContext,
|
||||
memberInfo
|
||||
@ -482,7 +572,8 @@ internal class GroupImpl(
|
||||
|
||||
|
||||
override operator fun get(id: Long): Member {
|
||||
return members.delegate.filteringGetOrNull { it.id == id } ?: throw NoSuchElementException("member $id not found in group $uin")
|
||||
return members.delegate.filteringGetOrNull { it.id == id }
|
||||
?: throw NoSuchElementException("member $id not found in group $uin")
|
||||
}
|
||||
|
||||
override fun contains(id: Long): Boolean {
|
||||
@ -493,25 +584,31 @@ internal class GroupImpl(
|
||||
return members.delegate.filteringGetOrNull { it.id == id }
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(message: MessageChain) {
|
||||
override suspend fun sendMessage(message: MessageChain): MessageReceipt<Group> {
|
||||
check(!isBotMuted) { "bot is muted. Remaining seconds=$botMuteRemaining" }
|
||||
val event = GroupMessageSendEvent(this, message).broadcast()
|
||||
if (event.isCancelled) {
|
||||
throw EventCancelledException("cancelled by FriendMessageSendEvent")
|
||||
}
|
||||
lateinit var source: MessageSourceFromSendGroup
|
||||
bot.network.run {
|
||||
val response = MessageSvc.PbSendMsg.ToGroup(
|
||||
val response: MessageSvc.PbSendMsg.Response = MessageSvc.PbSendMsg.ToGroup(
|
||||
bot.client,
|
||||
id,
|
||||
event.message
|
||||
).sendAndExpect<MessageSvc.PbSendMsg.Response>()
|
||||
) {
|
||||
source = it
|
||||
source.startWaitingSequenceId(this)
|
||||
}.sendAndExpect()
|
||||
check(
|
||||
response is MessageSvc.PbSendMsg.Response.SUCCESS
|
||||
) { "send message failed: $response" }
|
||||
}
|
||||
|
||||
return MessageReceipt(source, this, botAsMember)
|
||||
}
|
||||
|
||||
override suspend fun uploadImage(image: ExternalImage): Image = try {
|
||||
override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage = try {
|
||||
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
|
||||
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
|
||||
}
|
||||
@ -528,9 +625,11 @@ internal class GroupImpl(
|
||||
filename = image.filename
|
||||
).sendAndExpect()
|
||||
|
||||
@Suppress("UNCHECKED_CAST") // bug
|
||||
when (response) {
|
||||
is ImgStore.GroupPicUp.Response.Failed -> {
|
||||
ImageUploadEvent.Failed(this@GroupImpl, image, response.resultCode, response.message).broadcast()
|
||||
if (response.message == "over file size max") throw OverFileSizeMaxException()
|
||||
error("upload group image failed with reason ${response.message}")
|
||||
}
|
||||
is ImgStore.GroupPicUp.Response.FileExists -> {
|
||||
@ -546,22 +645,25 @@ internal class GroupImpl(
|
||||
// fileId = response.fileId.toInt()
|
||||
// )
|
||||
// println("NMSL")
|
||||
return CustomFaceFromFile(
|
||||
return OfflineGroupImage(
|
||||
md5 = image.md5,
|
||||
filepath = resourceId
|
||||
).also { ImageUploadEvent.Succeed(this@GroupImpl, image, it).broadcast() }
|
||||
}
|
||||
is ImgStore.GroupPicUp.Response.RequireUpload -> {
|
||||
// 每 10KB 等 1 秒
|
||||
withTimeoutOrNull(image.inputSize * 1000 / 1024 / 10) {
|
||||
HighwayHelper.uploadImage(
|
||||
client = bot.client,
|
||||
serverIp = response.uploadIpList.first().toIpV4AddressString(),
|
||||
serverPort = response.uploadPortList.first(),
|
||||
imageInput = image.input,
|
||||
inputSize = image.inputSize.toInt(),
|
||||
md5 = image.md5,
|
||||
fileMd5 = image.md5,
|
||||
uKey = response.uKey,
|
||||
commandId = 2
|
||||
)
|
||||
} ?: error("timeout uploading image: ${image.filename}")
|
||||
val resourceId = image.calculateImageResourceId()
|
||||
// return NotOnlineImageFromFile(
|
||||
// resourceId = resourceId,
|
||||
@ -573,7 +675,7 @@ internal class GroupImpl(
|
||||
// imageType = image.imageType,
|
||||
// fileId = response.fileId.toInt()
|
||||
// )
|
||||
return CustomFaceFromFile(
|
||||
return OfflineGroupImage(
|
||||
md5 = image.md5,
|
||||
filepath = resourceId
|
||||
).also { ImageUploadEvent.Succeed(this@GroupImpl, image, it).broadcast() }
|
||||
@ -597,13 +699,24 @@ internal class GroupImpl(
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
image.input.close()
|
||||
(image.input as? Closeable)?.close()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Group($id)"
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = bot.hashCode()
|
||||
result = 31 * result + id.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@Suppress("DuplicatedCode", "DuplicatedCode")
|
||||
if (this === other) return true
|
||||
return other is Group && other.id == this.id
|
||||
if (other !is Contact) return false
|
||||
if (this::class != other::class) return false
|
||||
return this.id == other.id && this.bot == other.bot
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = super.hashCode()
|
||||
}
|
@ -9,41 +9,46 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid
|
||||
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.BotImpl
|
||||
import net.mamoe.mirai.contact.ContactList
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.contact.filteringGetOrNull
|
||||
import net.mamoe.mirai.LowLevelAPI
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.AddFriendResult
|
||||
import net.mamoe.mirai.data.FriendInfo
|
||||
import net.mamoe.mirai.data.GroupInfo
|
||||
import net.mamoe.mirai.data.MemberInfo
|
||||
import net.mamoe.mirai.event.events.BotEvent
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
import net.mamoe.mirai.event.events.MessageRecallEvent
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.qqandroid.message.OnlineFriendImageImpl
|
||||
import net.mamoe.mirai.qqandroid.message.OnlineGroupImageImpl
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.PbMessageSvc
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
||||
import net.mamoe.mirai.utils.*
|
||||
import kotlin.collections.asSequence
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal expect class QQAndroidBot constructor(
|
||||
context: Context,
|
||||
account: BotAccount,
|
||||
configuration: BotConfiguration
|
||||
) : QQAndroidBotBase
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
|
||||
internal abstract class QQAndroidBotBase constructor(
|
||||
context: Context,
|
||||
account: BotAccount,
|
||||
configuration: BotConfiguration
|
||||
) : BotImpl<QQAndroidBotNetworkHandler>(account, configuration) {
|
||||
) : BotImpl<QQAndroidBotNetworkHandler>(context, account, configuration) {
|
||||
val client: QQAndroidClient =
|
||||
QQAndroidClient(
|
||||
context,
|
||||
@ -53,17 +58,32 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
)
|
||||
internal var firstLoginSucceed: Boolean = false
|
||||
override val uin: Long get() = client.uin
|
||||
override val qqs: ContactList<QQ> = ContactList(LockFreeLinkedList())
|
||||
|
||||
@Deprecated(
|
||||
"use friends instead",
|
||||
level = DeprecationLevel.ERROR,
|
||||
replaceWith = ReplaceWith("this.friends")
|
||||
)
|
||||
override val qqs: ContactList<QQ>
|
||||
get() = friends
|
||||
override val friends: ContactList<QQ> = ContactList(LockFreeLinkedList())
|
||||
|
||||
override val selfQQ: QQ by lazy {
|
||||
QQ(object : FriendInfo {
|
||||
@OptIn(LowLevelAPI::class)
|
||||
_lowLevelNewQQ(object : FriendInfo {
|
||||
override val uin: Long get() = this@QQAndroidBotBase.uin
|
||||
override val nick: String get() = this@QQAndroidBotBase.nick
|
||||
})
|
||||
}
|
||||
|
||||
override fun QQ(friendInfo: FriendInfo): QQ {
|
||||
return QQImpl(this as QQAndroidBot, coroutineContext, friendInfo.uin, friendInfo)
|
||||
@LowLevelAPI
|
||||
override fun _lowLevelNewQQ(friendInfo: FriendInfo): QQ {
|
||||
return QQImpl(
|
||||
this as QQAndroidBot,
|
||||
coroutineContext + CoroutineName("QQ(${friendInfo.uin}"),
|
||||
friendInfo.uin,
|
||||
friendInfo
|
||||
)
|
||||
}
|
||||
|
||||
override fun createNetworkHandler(coroutineContext: CoroutineContext): QQAndroidBotNetworkHandler {
|
||||
@ -74,28 +94,36 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
|
||||
// internally visible only
|
||||
fun getGroupByUin(uin: Long): Group {
|
||||
return groups.delegate.filteringGetOrNull { (it as GroupImpl).uin == uin } ?: throw NoSuchElementException("Can not found group with ID=${uin}")
|
||||
return groups.delegate.getOrNull(uin) ?: throw NoSuchElementException("Can not found group with ID=${uin}")
|
||||
}
|
||||
|
||||
fun getGroupByUinOrNull(uin: Long): Group? {
|
||||
return groups.delegate.filteringGetOrNull { (it as GroupImpl).uin == uin }
|
||||
return groups.delegate.getOrNull(uin)
|
||||
}
|
||||
|
||||
override suspend fun queryGroupList(): Sequence<Long> {
|
||||
@OptIn(LowLevelAPI::class)
|
||||
override suspend fun _lowLevelQueryGroupList(): Sequence<Long> {
|
||||
return network.run {
|
||||
FriendList.GetTroopListSimplify(bot.client)
|
||||
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
|
||||
}.groups.asSequence().map { it.groupUin.shl(32) and it.groupCode }
|
||||
}
|
||||
|
||||
override suspend fun queryGroupInfo(id: Long): GroupInfo = network.run {
|
||||
@OptIn(LowLevelAPI::class)
|
||||
override suspend fun _lowLevelQueryGroupInfo(groupCode: Long): GroupInfo = network.run {
|
||||
TroopManagement.GetGroupInfo(
|
||||
client = bot.client,
|
||||
groupCode = id
|
||||
).sendAndExpect<GroupInfoImpl>()
|
||||
groupCode = groupCode
|
||||
).sendAndExpect<GroupInfoImpl>(retry = 2)
|
||||
}
|
||||
|
||||
override suspend fun queryGroupMemberList(groupUin: Long, groupCode: Long, ownerId: Long): Sequence<MemberInfo> = network.run {
|
||||
@OptIn(LowLevelAPI::class)
|
||||
override suspend fun _lowLevelQueryGroupMemberList(
|
||||
groupUin: Long,
|
||||
groupCode: Long,
|
||||
ownerId: Long
|
||||
): Sequence<MemberInfo> =
|
||||
network.run {
|
||||
var nextUin = 0L
|
||||
var sequence = sequenceOf<MemberInfoImpl>()
|
||||
while (true) {
|
||||
@ -116,24 +144,88 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
return sequence
|
||||
}
|
||||
|
||||
override fun onEvent(event: BotEvent): Boolean {
|
||||
return firstLoginSucceed
|
||||
}
|
||||
|
||||
override suspend fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override suspend fun Image.download(): ByteReadPacket {
|
||||
TODO("not implemented")
|
||||
override suspend fun recall(source: MessageSource) {
|
||||
if (source.senderId != uin && source.groupId != 0L) {
|
||||
getGroup(source.groupId).checkBotPermissionOperator()
|
||||
}
|
||||
|
||||
@Suppress("OverridingDeprecatedMember")
|
||||
override suspend fun Image.downloadAsByteArray(): ByteArray {
|
||||
TODO("not implemented")
|
||||
// println(source._miraiContentToString())
|
||||
source.ensureSequenceIdAvailable()
|
||||
|
||||
network.run {
|
||||
val response: PbMessageSvc.PbMsgWithDraw.Response =
|
||||
if (source.groupId == 0L) {
|
||||
PbMessageSvc.PbMsgWithDraw.Friend(
|
||||
bot.client,
|
||||
source.senderId,
|
||||
source.sequenceId,
|
||||
source.messageRandom,
|
||||
source.time
|
||||
).sendAndExpect()
|
||||
} else {
|
||||
MessageRecallEvent.GroupRecall(
|
||||
bot,
|
||||
source.senderId,
|
||||
source.id,
|
||||
source.time.toInt(),
|
||||
null,
|
||||
getGroup(source.groupId)
|
||||
).broadcast()
|
||||
PbMessageSvc.PbMsgWithDraw.Group(
|
||||
bot.client,
|
||||
source.groupId,
|
||||
source.sequenceId,
|
||||
source.messageRandom
|
||||
).sendAndExpect()
|
||||
}
|
||||
|
||||
override suspend fun approveFriendAddRequest(id: Long, remark: String?) {
|
||||
TODO("not implemented")
|
||||
check(response is PbMessageSvc.PbMsgWithDraw.Response.Success) { "Failed to recall message #${source.id}: $response" }
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(LowLevelAPI::class)
|
||||
override suspend fun _lowLevelRecallFriendMessage(friendId: Long, messageId: Long, time: Long) {
|
||||
network.run {
|
||||
val response: PbMessageSvc.PbMsgWithDraw.Response =
|
||||
PbMessageSvc.PbMsgWithDraw.Friend(client, friendId, (messageId shr 32).toInt(), messageId.toInt(), time)
|
||||
.sendAndExpect()
|
||||
|
||||
check(response is PbMessageSvc.PbMsgWithDraw.Response.Success) { "Failed to recall message #${messageId}: $response" }
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(LowLevelAPI::class)
|
||||
override suspend fun _lowLevelRecallGroupMessage(groupId: Long, messageId: Long) {
|
||||
network.run {
|
||||
val response: PbMessageSvc.PbMsgWithDraw.Response =
|
||||
PbMessageSvc.PbMsgWithDraw.Group(client, groupId, (messageId shr 32).toInt(), messageId.toInt())
|
||||
.sendAndExpect()
|
||||
|
||||
check(response is PbMessageSvc.PbMsgWithDraw.Response.Success) { "Failed to recall message #${messageId}: $response" }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun queryImageUrl(image: Image): String = when (image) {
|
||||
is OnlineFriendImageImpl -> image.originUrl
|
||||
is OnlineGroupImageImpl -> image.originUrl
|
||||
is OfflineGroupImage -> {
|
||||
TODO("暂不支持获取离线图片链接")
|
||||
}
|
||||
is OfflineFriendImage -> {
|
||||
TODO("暂不支持获取离线图片链接")
|
||||
}
|
||||
else -> error("unsupported image class: ${image::class.simpleName}")
|
||||
}
|
||||
|
||||
override suspend fun openChannel(image: Image): ByteReadChannel {
|
||||
return MiraiPlatformUtils.Http.get<HttpResponse>(queryImageUrl(image)).content.toKotlinByteReadChannel()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal expect fun io.ktor.utils.io.ByteReadChannel.toKotlinByteReadChannel(): ByteReadChannel
|
@ -0,0 +1,14 @@
|
||||
package net.mamoe.mirai.qqandroid.io.serialization
|
||||
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.Output
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
import kotlinx.serialization.SerialFormat
|
||||
import kotlinx.serialization.SerializationStrategy
|
||||
|
||||
interface IOFormat : SerialFormat {
|
||||
|
||||
fun <T> dumpTo(serializer: SerializationStrategy<T>, ojb: T, output: Output)
|
||||
|
||||
fun <T> load(deserializer: DeserializationStrategy<T>, input: Input): T
|
||||
}
|
@ -12,16 +12,39 @@ package net.mamoe.mirai.qqandroid.io.serialization
|
||||
import kotlinx.io.charsets.Charset
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.builtins.ByteArraySerializer
|
||||
import kotlinx.serialization.builtins.MapEntrySerializer
|
||||
import kotlinx.serialization.builtins.SetSerializer
|
||||
import kotlinx.serialization.internal.*
|
||||
import kotlinx.serialization.modules.EmptyModule
|
||||
import kotlinx.serialization.modules.SerialModule
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.BYTE
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.DOUBLE
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.FLOAT
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.INT
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.JCE_MAX_STRING_LENGTH
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.LIST
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.LONG
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.MAP
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.SHORT
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.SIMPLE_LIST
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.STRING1
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.STRING4
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.STRUCT_BEGIN
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.STRUCT_END
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce.Companion.ZERO_TYPE
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceHead
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||
import net.mamoe.mirai.utils.io.readString
|
||||
import net.mamoe.mirai.utils.io.toReadPacket
|
||||
|
||||
@PublishedApi
|
||||
internal val CharsetGBK = Charset.forName("GBK")
|
||||
|
||||
@PublishedApi
|
||||
internal val CharsetUTF8 = Charset.forName("UTF8")
|
||||
|
||||
@ -30,12 +53,15 @@ enum class JceCharset(val kotlinCharset: Charset) {
|
||||
UTF8(Charset.forName("UTF8"))
|
||||
}
|
||||
|
||||
internal fun getSerialId(desc: SerialDescriptor, index: Int): Int? = desc.findAnnotation<SerialId>(index)?.id
|
||||
internal fun getSerialId(desc: SerialDescriptor, index: Int): Int? = desc.findAnnotation<JceId>(index)?.id
|
||||
|
||||
/**
|
||||
* Jce 数据结构序列化和反序列化工具, 能将 kotlinx.serialization 通用的注解标记格式的 `class` 序列化为 [ByteArray]
|
||||
*/
|
||||
class Jce private constructor(private val charset: JceCharset, context: SerialModule = EmptyModule) : AbstractSerialFormat(context), BinaryFormat {
|
||||
@Suppress("DEPRECATION_ERROR")
|
||||
@OptIn(InternalSerializationApi::class)
|
||||
class JceOld private constructor(private val charset: JceCharset, override val context: SerialModule = EmptyModule) :
|
||||
SerialFormat, BinaryFormat {
|
||||
|
||||
private inner class ListWriter(
|
||||
private val count: Int,
|
||||
@ -46,7 +72,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun endEncode(desc: SerialDescriptor) {
|
||||
override fun endEncode(descriptor: SerialDescriptor) {
|
||||
parentEncoder.writeHead(LIST, this.tag)
|
||||
parentEncoder.encodeTaggedInt(0, count)
|
||||
parentEncoder.output.writePacket(this.output.build())
|
||||
@ -68,11 +94,18 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
parentEncoder.output.write(this.output.toByteArray())
|
||||
}*/
|
||||
|
||||
override fun beginCollection(desc: SerialDescriptor, collectionSize: Int, vararg typeParams: KSerializer<*>): CompositeEncoder {
|
||||
override fun beginCollection(
|
||||
descriptor: SerialDescriptor,
|
||||
collectionSize: Int,
|
||||
vararg typeSerializers: KSerializer<*>
|
||||
): CompositeEncoder {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeEncoder {
|
||||
override fun beginStructure(
|
||||
descriptor: SerialDescriptor,
|
||||
vararg typeSerializers: KSerializer<*>
|
||||
): CompositeEncoder {
|
||||
return this
|
||||
}
|
||||
}
|
||||
@ -81,11 +114,11 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
* From: com.qq.taf.jce.JceOutputStream
|
||||
*/
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
@UseExperimental(ExperimentalIoApi::class)
|
||||
@OptIn(ExperimentalIoApi::class)
|
||||
private open inner class JceEncoder(
|
||||
internal val output: BytePacketBuilder
|
||||
) : TaggedEncoder<Int>() {
|
||||
override val context get() = this@Jce.context
|
||||
override val context get() = this@JceOld.context
|
||||
|
||||
override fun SerialDescriptor.getTag(index: Int): Int {
|
||||
return getSerialId(this, index) ?: error("cannot find @SerialId")
|
||||
@ -94,28 +127,38 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
/**
|
||||
* 序列化最开始的时候的
|
||||
*/
|
||||
override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeEncoder = when (desc.kind) {
|
||||
override fun beginStructure(
|
||||
descriptor: SerialDescriptor,
|
||||
vararg typeSerializers: KSerializer<*>
|
||||
): CompositeEncoder =
|
||||
when (descriptor.kind) {
|
||||
StructureKind.LIST -> this
|
||||
StructureKind.MAP -> this
|
||||
StructureKind.CLASS, UnionKind.OBJECT -> this
|
||||
StructureKind.CLASS, StructureKind.OBJECT -> this
|
||||
is PolymorphicKind -> this
|
||||
else -> throw SerializationException("Primitives are not supported at top-level")
|
||||
}
|
||||
|
||||
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||
@OptIn(ImplicitReflectionSerializer::class)
|
||||
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
|
||||
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when (serializer.descriptor) {
|
||||
is MapLikeDescriptor -> {
|
||||
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when {
|
||||
serializer.descriptor.kind == StructureKind.MAP -> {
|
||||
try {
|
||||
val entries = (value as Map<*, *>).entries
|
||||
val serializer = (serializer as MapLikeSerializer<Any?, Any?, T, *>)
|
||||
val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
|
||||
|
||||
this.writeHead(MAP, currentTag)
|
||||
this.encodeTaggedInt(0, entries.count())
|
||||
HashSetSerializer(mapEntrySerial).serialize(JceMapWriter(this.output), entries)
|
||||
SetSerializer(mapEntrySerial).serialize(JceMapWriter(this.output), entries)
|
||||
} catch (e: Exception) {
|
||||
super.encodeSerializableValue(serializer, value)
|
||||
}
|
||||
ByteArraySerializer.descriptor -> encodeTaggedByteArray(popTag(), value as ByteArray)
|
||||
is PrimitiveArrayDescriptor -> {
|
||||
}
|
||||
serializer.descriptor.kind == StructureKind.LIST
|
||||
&& value is ByteArray -> encodeTaggedByteArray(popTag(), value as ByteArray)
|
||||
serializer.descriptor.kind == StructureKind.LIST
|
||||
&& serializer.descriptor.getElementDescriptor(0) is PrimitiveKind -> {
|
||||
serializer.serialize(
|
||||
ListWriter(
|
||||
when (value) {
|
||||
@ -133,9 +176,8 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
value
|
||||
)
|
||||
}
|
||||
is ArrayClassDesc -> {
|
||||
val descriptor = serializer.descriptor as ReferenceArraySerializer<Any, Any?>
|
||||
if (descriptor.typeParams.isNotEmpty() && descriptor.typeParams[0] is ByteSerializer) {
|
||||
serializer.descriptor.kind == StructureKind.LIST && value is Array<*> -> {
|
||||
if (serializer.descriptor.getElementDescriptor(0).kind is PrimitiveKind.BYTE) {
|
||||
encodeTaggedByteArray(popTag(), (value as Array<Byte>).toByteArray())
|
||||
} else
|
||||
serializer.serialize(
|
||||
@ -143,7 +185,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
value
|
||||
)
|
||||
}
|
||||
is ListLikeDescriptor -> {
|
||||
serializer.descriptor.kind == StructureKind.LIST -> {
|
||||
serializer.serialize(
|
||||
ListWriter((value as Collection<*>).size, popTag(), this),
|
||||
value
|
||||
@ -262,7 +304,8 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
is Double -> encodeTaggedDouble(tag, value)
|
||||
is Boolean -> encodeTaggedBoolean(tag, value)
|
||||
is String -> encodeTaggedString(tag, value)
|
||||
is Unit -> encodeTaggedUnit(tag)
|
||||
is Unit -> {
|
||||
}
|
||||
else -> error("unsupported type: ${value.getClassName()}")
|
||||
}
|
||||
}
|
||||
@ -286,7 +329,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
val size: Int,
|
||||
input: JceInput
|
||||
) : JceDecoder(input) {
|
||||
override fun decodeCollectionSize(desc: SerialDescriptor): Int {
|
||||
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
||||
return size
|
||||
}
|
||||
|
||||
@ -300,7 +343,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
val size: Int,
|
||||
input: JceInput
|
||||
) : JceDecoder(input) {
|
||||
override fun decodeCollectionSize(desc: SerialDescriptor): Int {
|
||||
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
||||
return size
|
||||
}
|
||||
|
||||
@ -312,7 +355,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
private open inner class JceStructReader(
|
||||
input: JceInput
|
||||
) : JceDecoder(input) {
|
||||
override fun endStructure(desc: SerialDescriptor) {
|
||||
override fun endStructure(descriptor: SerialDescriptor) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -342,22 +385,27 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
return input.readInt(tag)
|
||||
}
|
||||
|
||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [KSerializer.serialize] 前
|
||||
*/
|
||||
override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
|
||||
override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
|
||||
//// println("beginStructure: desc=${desc.getClassName()}, typeParams: ${typeParams.contentToString()}")
|
||||
when (desc) {
|
||||
when {
|
||||
// 由于 Byte 的数组有两种方式写入, 需特定读取器
|
||||
ByteArraySerializer.descriptor -> {
|
||||
descriptor.kind == StructureKind.LIST
|
||||
&& descriptor.getElementDescriptor(0).kind == PrimitiveKind.BYTE -> {
|
||||
// ByteArray, 交给 decodeSerializableValue 进行处理
|
||||
return this
|
||||
}
|
||||
is ListLikeDescriptor -> {
|
||||
if (typeParams.isNotEmpty() && typeParams[0] is ByteSerializer) {
|
||||
// Array<Byte>
|
||||
return this // 交给 decodeSerializableValue
|
||||
}
|
||||
descriptor.kind == StructureKind.LIST -> {
|
||||
// if (typeParams.isNotEmpty() && typeParams[0] is ByteSerializer) {
|
||||
// // Array<Byte>
|
||||
// return this // 交给 decodeSerializableValue
|
||||
// }
|
||||
|
||||
val tag = currentTagOrNull
|
||||
@Suppress("SENSELESS_COMPARISON") // 推断 bug
|
||||
@ -371,12 +419,12 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
MAP -> JceMapReader(input.readInt(0), this.input)
|
||||
else -> error("type mismatch")
|
||||
}
|
||||
} == null && desc.isNullable) {
|
||||
} == null && descriptor.isNullable) {
|
||||
return NullReader(this.input)
|
||||
}
|
||||
}
|
||||
|
||||
is MapLikeDescriptor -> {
|
||||
descriptor.kind == StructureKind.MAP -> {
|
||||
val tag = currentTagOrNull
|
||||
if (tag != null) {
|
||||
popTag()
|
||||
@ -391,7 +439,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
return NullReader(this.input)
|
||||
}
|
||||
|
||||
return super.beginStructure(desc, *typeParams)
|
||||
return super.beginStructure(descriptor, *typeParams)
|
||||
}
|
||||
|
||||
override fun decodeTaggedNull(tag: Int): Nothing? {
|
||||
@ -410,7 +458,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T?>): T? {
|
||||
//
|
||||
//println("decodeNullableSerializableValue: ${deserializer::class.qualifiedName}")
|
||||
println("decodeNullableSerializableValue: ${deserializer::class.qualifiedName}")
|
||||
if (deserializer is NullReader) {
|
||||
return null
|
||||
}
|
||||
@ -419,13 +467,13 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
return null
|
||||
}
|
||||
}
|
||||
when (deserializer.descriptor) {
|
||||
ByteArraySerializer.descriptor -> {
|
||||
when {
|
||||
deserializer.descriptor == ByteArraySerializer().descriptor -> {
|
||||
val tag = popTag()
|
||||
return if (isTagMissing(tag)) input.readByteArrayOrNull(tag) as? T
|
||||
else input.readByteArray(tag) as T
|
||||
}
|
||||
is ListLikeDescriptor -> {
|
||||
deserializer.descriptor.kind == StructureKind.LIST -> {
|
||||
if (deserializer is ReferenceArraySerializer<*, *>
|
||||
&& (deserializer as ListLikeSerializer<Any?, T, Any?>).typeParams.isNotEmpty()
|
||||
&& (deserializer as ListLikeSerializer<Any?, T, Any?>).typeParams[0] is ByteSerializer
|
||||
@ -453,15 +501,17 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
}
|
||||
error("UNREACHABLE CODE")
|
||||
}
|
||||
is MapLikeDescriptor -> {
|
||||
deserializer.descriptor.kind == StructureKind.MAP -> {
|
||||
val tag = popTag()
|
||||
@Suppress("SENSELESS_COMPARISON")
|
||||
if (input.skipToTagOrNull(tag) { head ->
|
||||
check(head.type == MAP) { "type mismatch: ${head.type}" }
|
||||
// 将 mapOf(k1 to v1, k2 to v2, ...) 转换为 listOf(k1, v1, k2, v2, ...) 以便于写入.
|
||||
val serializer = (deserializer as MapLikeSerializer<Any?, Any?, T, *>)
|
||||
val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
|
||||
val setOfEntries = HashSetSerializer(mapEntrySerial).deserialize(JceMapReader(input.readInt(0), input))
|
||||
val mapEntrySerial =
|
||||
MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
|
||||
val setOfEntries =
|
||||
SetSerializer(mapEntrySerial).deserialize(JceMapReader(input.readInt(0), input))
|
||||
return setOfEntries.associateBy({ it.key }, { it.value }) as T
|
||||
} == null) {
|
||||
if (isTagMissing(tag)) {
|
||||
@ -472,7 +522,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
}
|
||||
}
|
||||
|
||||
if (deserializer.descriptor.kind == StructureKind.CLASS || deserializer.descriptor.kind == UnionKind.OBJECT) {
|
||||
if (deserializer.descriptor.kind == StructureKind.CLASS || deserializer.descriptor.kind == StructureKind.OBJECT) {
|
||||
val tag = currentTagOrNull
|
||||
if (tag != null) {
|
||||
@Suppress("SENSELESS_COMPARISON") // 推断 bug
|
||||
@ -515,7 +565,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
}
|
||||
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
internal inner class JceInput(
|
||||
@PublishedApi
|
||||
internal val input: ByteReadPacket,
|
||||
@ -556,22 +606,38 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
}
|
||||
tag = readUByte().toUInt()
|
||||
}
|
||||
currentJceHead = JceHead(tag = tag.toInt(), type = type.toByte())
|
||||
currentJceHead = JceHead(
|
||||
tag = tag.toInt(),
|
||||
type = type.toByte()
|
||||
)
|
||||
// println("doReadHead: $currentJceHead")
|
||||
return currentJceHead
|
||||
}
|
||||
|
||||
fun readBoolean(tag: Int): Boolean = readBooleanOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
fun readByte(tag: Int): Byte = readByteOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
fun readShort(tag: Int): Short = readShortOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
fun readBoolean(tag: Int): Boolean =
|
||||
readBooleanOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
|
||||
fun readByte(tag: Int): Byte =
|
||||
readByteOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
|
||||
fun readShort(tag: Int): Short =
|
||||
readShortOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
|
||||
fun readInt(tag: Int): Int = readIntOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
fun readLong(tag: Int): Long = readLongOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
fun readFloat(tag: Int): Float = readFloatOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
fun readDouble(tag: Int): Double = readDoubleOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
fun readLong(tag: Int): Long =
|
||||
readLongOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
|
||||
fun readString(tag: Int): String = readStringOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
fun readFloat(tag: Int): Float =
|
||||
readFloatOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
|
||||
fun readByteArray(tag: Int): ByteArray = readByteArrayOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
fun readDouble(tag: Int): Double =
|
||||
readDoubleOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
|
||||
fun readString(tag: Int): String =
|
||||
readStringOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
|
||||
fun readByteArray(tag: Int): ByteArray =
|
||||
readByteArrayOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||
|
||||
fun readByteArrayOrNull(tag: Int): ByteArray? = skipToTagOrNull(tag) {
|
||||
when (it.type) {
|
||||
@ -667,7 +733,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
} while (head.type.toInt() != 11)
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
@PublishedApi
|
||||
internal fun skipField(type: Byte) = when (type.toInt()) {
|
||||
0 -> this.input.discardExact(1)
|
||||
@ -704,10 +770,10 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
companion object {
|
||||
val UTF8 = Jce(JceCharset.UTF8)
|
||||
val GBK = Jce(JceCharset.GBK)
|
||||
val UTF8 = JceOld(JceCharset.UTF8)
|
||||
val GBK = JceOld(JceCharset.GBK)
|
||||
|
||||
fun byCharSet(c: JceCharset): Jce {
|
||||
fun byCharSet(c: JceCharset): JceOld {
|
||||
return if (c == JceCharset.UTF8) {
|
||||
UTF8
|
||||
} else {
|
||||
@ -715,24 +781,9 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
}
|
||||
}
|
||||
|
||||
internal const val BYTE: Byte = 0
|
||||
internal const val DOUBLE: Byte = 5
|
||||
internal const val FLOAT: Byte = 4
|
||||
internal const val INT: Byte = 2
|
||||
internal const val JCE_MAX_STRING_LENGTH = 104857600
|
||||
internal const val LIST: Byte = 9
|
||||
internal const val LONG: Byte = 3
|
||||
internal const val MAP: Byte = 8
|
||||
internal const val SHORT: Byte = 1
|
||||
internal const val SIMPLE_LIST: Byte = 13
|
||||
internal const val STRING1: Byte = 6
|
||||
internal const val STRING4: Byte = 7
|
||||
internal const val STRUCT_BEGIN: Byte = 10
|
||||
internal const val STRUCT_END: Byte = 11
|
||||
internal const val ZERO_TYPE: Byte = 12
|
||||
|
||||
private fun Any?.getClassName(): String =
|
||||
(if (this == null) Unit::class else this::class).qualifiedName?.split(".")?.takeLast(2)?.joinToString(".") ?: "<unnamed class>"
|
||||
(if (this == null) Unit::class else this::class).qualifiedName?.split(".")?.takeLast(2)?.joinToString(".")
|
||||
?: "<unnamed class>"
|
||||
}
|
||||
|
||||
fun <T> dumpAsPacket(serializer: SerializationStrategy<T>, obj: T): ByteReadPacket {
|
||||
@ -742,14 +793,18 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
return encoder.build()
|
||||
}
|
||||
|
||||
override fun <T> dump(serializer: SerializationStrategy<T>, obj: T): ByteArray {
|
||||
return dumpAsPacket(serializer, obj).readBytes()
|
||||
override fun <T> dump(serializer: SerializationStrategy<T>, value: T): ByteArray {
|
||||
return dumpAsPacket(serializer, value).readBytes()
|
||||
}
|
||||
|
||||
/**
|
||||
* 注意 close [packet]!!
|
||||
*/
|
||||
fun <T> load(deserializer: DeserializationStrategy<T>, packet: ByteReadPacket, length: Int = packet.remaining.toInt()): T {
|
||||
fun <T> load(
|
||||
deserializer: DeserializationStrategy<T>,
|
||||
packet: ByteReadPacket,
|
||||
length: Int = packet.remaining.toInt()
|
||||
): T {
|
||||
return JceDecoder(JceInput(packet, length.toLong())).decode(deserializer)
|
||||
}
|
||||
|
||||
@ -761,7 +816,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <R> Jce.JceInput.skipToTagOrNull(tag: Int, block: (JceHead) -> R): R? {
|
||||
internal inline fun <R> JceOld.JceInput.skipToTagOrNull(tag: Int, block: (JceHead) -> R): R? {
|
||||
// println("skipping to $tag start")
|
||||
while (true) {
|
||||
if (isEndOfInput) { // 读不了了
|
||||
@ -793,32 +848,3 @@ internal inline fun <R> Jce.JceInput.skipToTagOrNull(tag: Int, block: (JceHead)
|
||||
currentJceHead = readHeadOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
inline class JceHead(private val value: Long) {
|
||||
constructor(tag: Int, type: Byte) : this(tag.toLong().shl(32) or type.toLong())
|
||||
|
||||
val tag: Int get() = (value ushr 32).toInt()
|
||||
val type: Byte get() = value.toUInt().toByte()
|
||||
|
||||
override fun toString(): String {
|
||||
val typeString = when (type) {
|
||||
Jce.BYTE -> "Byte"
|
||||
Jce.DOUBLE -> "Double"
|
||||
Jce.FLOAT -> "Float"
|
||||
Jce.INT -> "Int"
|
||||
Jce.LIST -> "List"
|
||||
Jce.LONG -> "Long"
|
||||
Jce.MAP -> "Map"
|
||||
Jce.SHORT -> "Short"
|
||||
Jce.SIMPLE_LIST -> "SimpleList"
|
||||
Jce.STRING1 -> "String1"
|
||||
Jce.STRING4 -> "String4"
|
||||
Jce.STRUCT_BEGIN -> "StructBegin"
|
||||
Jce.STRUCT_END -> "StructEnd"
|
||||
Jce.ZERO_TYPE -> "Zero"
|
||||
else -> error("unreachable")
|
||||
}
|
||||
return "JceHead(tag=$tag, type=$type($typeString))"
|
||||
}
|
||||
}
|
@ -5,11 +5,19 @@
|
||||
* Some code changed by Mamoe is annotated around "MIRAI MODIFY START" and "MIRAI MODIFY END"
|
||||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION_ERROR")
|
||||
|
||||
package net.mamoe.mirai.qqandroid.io.serialization
|
||||
|
||||
import kotlinx.io.*
|
||||
import kotlinx.io.ByteArrayOutputStream
|
||||
import kotlinx.io.ByteBuffer
|
||||
import kotlinx.io.ByteOrder
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.internal.*
|
||||
import kotlinx.serialization.builtins.ByteArraySerializer
|
||||
import kotlinx.serialization.builtins.MapEntrySerializer
|
||||
import kotlinx.serialization.builtins.SetSerializer
|
||||
import kotlinx.serialization.internal.MapLikeSerializer
|
||||
import kotlinx.serialization.internal.TaggedEncoder
|
||||
import kotlinx.serialization.modules.EmptyModule
|
||||
import kotlinx.serialization.modules.SerialModule
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
@ -33,15 +41,19 @@ internal fun extractParameters(desc: SerialDescriptor, index: Int, zeroBasedDefa
|
||||
*
|
||||
* 代码复制自 kotlinx.serialization. 修改部分已进行标注 (详见 "MIRAI MODIFY START")
|
||||
*/
|
||||
class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : AbstractSerialFormat(context), BinaryFormat {
|
||||
@OptIn(InternalSerializationApi::class)
|
||||
class ProtoBufWithNullableSupport(override val context: SerialModule = EmptyModule) : SerialFormat, BinaryFormat {
|
||||
|
||||
internal open inner class ProtobufWriter(val encoder: ProtobufEncoder) : TaggedEncoder<ProtoDesc>() {
|
||||
public override val context
|
||||
internal open inner class ProtobufWriter(private val encoder: ProtobufEncoder) : TaggedEncoder<ProtoDesc>() {
|
||||
override val context
|
||||
get() = this@ProtoBufWithNullableSupport.context
|
||||
|
||||
override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeEncoder = when (desc.kind) {
|
||||
override fun beginStructure(
|
||||
descriptor: SerialDescriptor,
|
||||
vararg typeSerializers: KSerializer<*>
|
||||
): CompositeEncoder = when (descriptor.kind) {
|
||||
StructureKind.LIST -> RepeatedWriter(encoder, currentTag)
|
||||
StructureKind.CLASS, UnionKind.OBJECT, is PolymorphicKind -> ObjectWriter(currentTagOrNull, encoder)
|
||||
StructureKind.CLASS, StructureKind.OBJECT, is PolymorphicKind -> ObjectWriter(currentTagOrNull, encoder)
|
||||
StructureKind.MAP -> MapRepeatedWriter(currentTagOrNull, encoder)
|
||||
else -> throw SerializationException("Primitives are not supported at top-level")
|
||||
}
|
||||
@ -82,12 +94,15 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
|
||||
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
|
||||
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when {
|
||||
// encode maps as collection of map entries, not merged collection of key-values
|
||||
serializer.descriptor is MapLikeDescriptor -> {
|
||||
serializer.descriptor.kind == StructureKind.MAP -> {
|
||||
val serializer = (serializer as MapLikeSerializer<Any?, Any?, T, *>)
|
||||
val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
|
||||
HashSetSerializer(mapEntrySerial).serialize(this, (value as Map<*, *>).entries)
|
||||
SetSerializer(mapEntrySerial).serialize(this, (value as Map<*, *>).entries)
|
||||
}
|
||||
serializer.descriptor == ByteArraySerializer.descriptor -> encoder.writeBytes(value as ByteArray, popTag().first)
|
||||
serializer.descriptor == ByteArraySerializer().descriptor -> encoder.writeBytes(
|
||||
value as ByteArray,
|
||||
popTag().first
|
||||
)
|
||||
else -> serializer.serialize(this, value)
|
||||
}
|
||||
}
|
||||
@ -96,7 +111,7 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
|
||||
val parentTag: ProtoDesc?, private val parentEncoder: ProtobufEncoder,
|
||||
private val stream: ByteArrayOutputStream = ByteArrayOutputStream()
|
||||
) : ProtobufWriter(ProtobufEncoder(stream)) {
|
||||
override fun endEncode(desc: SerialDescriptor) {
|
||||
override fun endEncode(descriptor: SerialDescriptor) {
|
||||
if (parentTag != null) {
|
||||
parentEncoder.writeBytes(stream.toByteArray(), parentTag.first)
|
||||
} else {
|
||||
@ -111,7 +126,8 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
|
||||
else 2 to (parentTag?.second ?: ProtoNumberType.DEFAULT)
|
||||
}
|
||||
|
||||
internal inner class RepeatedWriter(encoder: ProtobufEncoder, val curTag: ProtoDesc) : ProtobufWriter(encoder) {
|
||||
internal inner class RepeatedWriter(encoder: ProtobufEncoder, private val curTag: ProtoDesc) :
|
||||
ProtobufWriter(encoder) {
|
||||
override fun SerialDescriptor.getTag(index: Int) = curTag
|
||||
}
|
||||
|
||||
@ -141,8 +157,9 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
|
||||
out.write(content)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
fun writeString(value: String, tag: Int) {
|
||||
val bytes = value.toUtf8Bytes()
|
||||
val bytes = value.encodeToByteArray()
|
||||
writeBytes(bytes, tag)
|
||||
}
|
||||
|
||||
@ -228,17 +245,17 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
|
||||
internal const val SIZE_DELIMITED = 2
|
||||
internal const val i32 = 5
|
||||
|
||||
val plain = ProtoBufWithNullableSupport()
|
||||
private val plain = ProtoBufWithNullableSupport()
|
||||
|
||||
override fun <T> dump(serializer: SerializationStrategy<T>, obj: T): ByteArray = plain.dump(serializer, obj)
|
||||
override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T = plain.load(deserializer, bytes)
|
||||
override fun install(module: SerialModule) = throw IllegalStateException("You should not install anything to global instance")
|
||||
override fun <T> dump(serializer: SerializationStrategy<T>, value: T): ByteArray = plain.dump(serializer, value)
|
||||
override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T =
|
||||
plain.load(deserializer, bytes)
|
||||
}
|
||||
|
||||
override fun <T> dump(serializer: SerializationStrategy<T>, obj: T): ByteArray {
|
||||
override fun <T> dump(serializer: SerializationStrategy<T>, value: T): ByteArray {
|
||||
val encoder = ByteArrayOutputStream()
|
||||
val dumper = ProtobufWriter(ProtobufEncoder(encoder))
|
||||
dumper.encode(serializer, obj)
|
||||
dumper.encode(serializer, value)
|
||||
return encoder.toByteArray()
|
||||
}
|
||||
|
||||
@ -248,20 +265,3 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
|
||||
|
||||
}
|
||||
|
||||
internal fun InputStream.readExactNBytes(bytes: Int): ByteArray {
|
||||
val array = ByteArray(bytes)
|
||||
var read = 0
|
||||
while (read < bytes) {
|
||||
val i = this.read(array, read, bytes - read)
|
||||
if (i == -1) throw IOException("Unexpected EOF")
|
||||
read += i
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
internal fun InputStream.readToByteBuffer(bytes: Int): ByteBuffer {
|
||||
val arr = readExactNBytes(bytes)
|
||||
val buf = ByteBuffer.allocate(bytes)
|
||||
buf.put(arr).flip()
|
||||
return buf
|
||||
}
|
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("PrivatePropertyName")
|
||||
|
||||
package net.mamoe.mirai.qqandroid.io.serialization.jce
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.builtins.AbstractDecoder
|
||||
import kotlinx.serialization.internal.TaggedDecoder
|
||||
import kotlinx.serialization.modules.SerialModule
|
||||
|
||||
|
||||
@OptIn(InternalSerializationApi::class) // 将来 kotlinx 修改后再复制过来 mirai.
|
||||
internal class JceDecoder(
|
||||
val jce: JceInput, override val context: SerialModule
|
||||
) : TaggedDecoder<JceTag>() {
|
||||
override val updateMode: UpdateMode
|
||||
get() = UpdateMode.BANNED
|
||||
|
||||
override fun SerialDescriptor.getTag(index: Int): JceTag {
|
||||
val annotations = this.getElementAnnotations(index)
|
||||
|
||||
val id = annotations.filterIsInstance<JceId>().single().id
|
||||
// ?: error("cannot find @JceId or @ProtoId for ${this.getElementName(index)} in ${this.serialName}")
|
||||
//println("getTag: ${this.getElementName(index)}=$id")
|
||||
|
||||
return JceTagCommon(id)
|
||||
}
|
||||
|
||||
private fun SerialDescriptor.getJceTagId(index: Int): Int {
|
||||
//println("getTag: ${getElementName(index)}")
|
||||
return getElementAnnotations(index).filterIsInstance<JceId>().singleOrNull()?.id
|
||||
?: error("missing @JceId for ${getElementName(index)} in ${this.serialName}")
|
||||
}
|
||||
|
||||
private val SimpleByteArrayReader: SimpleByteArrayReaderImpl = SimpleByteArrayReaderImpl()
|
||||
|
||||
private inner class SimpleByteArrayReaderImpl : AbstractDecoder() {
|
||||
override fun decodeSequentially(): Boolean = true
|
||||
|
||||
override fun endStructure(descriptor: SerialDescriptor) {
|
||||
this@JceDecoder.endStructure(descriptor)
|
||||
}
|
||||
|
||||
override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
|
||||
this@JceDecoder.pushTag(JceTagListElement)
|
||||
return this@JceDecoder.beginStructure(descriptor, *typeParams)
|
||||
}
|
||||
|
||||
override fun decodeByte(): Byte = jce.input.readByte()
|
||||
override fun decodeShort(): Short = error("illegal access")
|
||||
override fun decodeInt(): Int = error("illegal access")
|
||||
override fun decodeLong(): Long = error("illegal access")
|
||||
override fun decodeFloat(): Float = error("illegal access")
|
||||
override fun decodeDouble(): Double = error("illegal access")
|
||||
override fun decodeBoolean(): Boolean = error("illegal access")
|
||||
override fun decodeChar(): Char = error("illegal access")
|
||||
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = error("illegal access")
|
||||
override fun decodeString(): String = error("illegal access")
|
||||
|
||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
||||
error("should not be reached")
|
||||
}
|
||||
|
||||
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
||||
// 不要读下一个 head
|
||||
return jce.currentHead.let { jce.readJceIntValue(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private val ListReader: ListReaderImpl = ListReaderImpl()
|
||||
|
||||
private inner class ListReaderImpl : AbstractDecoder() {
|
||||
override fun decodeSequentially(): Boolean = true
|
||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int = error("should not be reached")
|
||||
override fun endStructure(descriptor: SerialDescriptor) {
|
||||
this@JceDecoder.endStructure(descriptor)
|
||||
}
|
||||
|
||||
override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
|
||||
this@JceDecoder.pushTag(JceTagListElement)
|
||||
|
||||
return this@JceDecoder.beginStructure(descriptor, *typeParams)
|
||||
}
|
||||
|
||||
override fun decodeByte(): Byte = jce.useHead { jce.readJceByteValue(it) }
|
||||
override fun decodeShort(): Short = jce.useHead { jce.readJceShortValue(it) }
|
||||
override fun decodeInt(): Int = jce.useHead { jce.readJceIntValue(it) }
|
||||
override fun decodeLong(): Long = jce.useHead { jce.readJceLongValue(it) }
|
||||
override fun decodeFloat(): Float = jce.useHead { jce.readJceFloatValue(it) }
|
||||
override fun decodeDouble(): Double = jce.useHead { jce.readJceDoubleValue(it) }
|
||||
override fun decodeBoolean(): Boolean = jce.useHead { jce.readJceBooleanValue(it) }
|
||||
override fun decodeChar(): Char = decodeByte().toChar()
|
||||
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeInt()
|
||||
override fun decodeString(): String = jce.useHead { jce.readJceStringValue(it) }
|
||||
|
||||
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
||||
//println("decodeCollectionSize: ${descriptor.serialName}")
|
||||
// 不读下一个 head
|
||||
return jce.useHead { jce.readJceIntValue(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val MapReader: MapReaderImpl = MapReaderImpl()
|
||||
|
||||
private inner class MapReaderImpl : AbstractDecoder() {
|
||||
override fun decodeSequentially(): Boolean = true
|
||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int = error("stub")
|
||||
|
||||
override fun endStructure(descriptor: SerialDescriptor) {
|
||||
this@JceDecoder.endStructure(descriptor)
|
||||
}
|
||||
|
||||
private var state: Boolean = true
|
||||
|
||||
override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
|
||||
this@JceDecoder.pushTag(if (jce.currentHead.tag == 0) JceTagMapEntryKey else JceTagMapEntryValue)
|
||||
state = !state
|
||||
return this@JceDecoder.beginStructure(descriptor, *typeParams)
|
||||
}
|
||||
|
||||
override fun decodeByte(): Byte = jce.useHead { jce.readJceByteValue(it) }
|
||||
override fun decodeShort(): Short = jce.useHead { jce.readJceShortValue(it) }
|
||||
override fun decodeInt(): Int = jce.useHead { jce.readJceIntValue(it) }
|
||||
override fun decodeLong(): Long = jce.useHead { jce.readJceLongValue(it) }
|
||||
override fun decodeFloat(): Float = jce.useHead { jce.readJceFloatValue(it) }
|
||||
override fun decodeDouble(): Double = jce.useHead { jce.readJceDoubleValue(it) }
|
||||
|
||||
override fun decodeBoolean(): Boolean = jce.useHead { jce.readJceBooleanValue(it) }
|
||||
override fun decodeChar(): Char = decodeByte().toChar()
|
||||
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeInt()
|
||||
override fun decodeString(): String = jce.useHead { jce.readJceStringValue(it) }
|
||||
|
||||
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
||||
//println("decodeCollectionSize in MapReader: ${descriptor.serialName}")
|
||||
// 不读下一个 head
|
||||
return jce.useHead { jce.readJceIntValue(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun endStructure(descriptor: SerialDescriptor) {
|
||||
//println("endStructure: $descriptor")
|
||||
if (currentTagOrNull?.isSimpleByteArray == true) {
|
||||
jce.prepareNextHead() // read to next head
|
||||
}
|
||||
if (descriptor.kind == StructureKind.CLASS) {
|
||||
if (currentTagOrNull == null) {
|
||||
return
|
||||
}
|
||||
while (true) {
|
||||
val currentHead = jce.currentHeadOrNull ?: return
|
||||
if (currentHead.type == Jce.STRUCT_END) {
|
||||
jce.prepareNextHead()
|
||||
//println("current end")
|
||||
break
|
||||
}
|
||||
//println("current $currentHead")
|
||||
jce.skipField(currentHead.type)
|
||||
jce.prepareNextHead()
|
||||
}
|
||||
// pushTag(JceTag(0, true))
|
||||
// skip STRUCT_END
|
||||
// popTag()
|
||||
}
|
||||
}
|
||||
|
||||
override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
|
||||
//println()
|
||||
//println("beginStructure: ${descriptor.serialName}")
|
||||
return when (descriptor.kind) {
|
||||
is PrimitiveKind -> this@JceDecoder
|
||||
|
||||
StructureKind.MAP -> {
|
||||
//println("!! MAP")
|
||||
return jce.skipToHeadAndUseIfPossibleOrFail(popTag().id) {
|
||||
it.checkType(Jce.MAP)
|
||||
MapReader
|
||||
}
|
||||
}
|
||||
StructureKind.LIST -> {
|
||||
//println("!! ByteArray")
|
||||
//println("decoderTag: $currentTagOrNull")
|
||||
//println("jceHead: " + jce.currentHeadOrNull)
|
||||
return jce.skipToHeadAndUseIfPossibleOrFail(currentTag.id) {
|
||||
//println("listHead: $it")
|
||||
when (it.type) {
|
||||
Jce.SIMPLE_LIST -> {
|
||||
currentTag.isSimpleByteArray = true
|
||||
jce.prepareNextHead() // 无用的元素类型
|
||||
SimpleByteArrayReader
|
||||
}
|
||||
Jce.LIST -> ListReader
|
||||
else -> error("type mismatch. Expected SIMPLE_LIST or LIST, got ${it.type} instead")
|
||||
}
|
||||
}
|
||||
}
|
||||
StructureKind.CLASS -> {
|
||||
currentTagOrNull ?: return this@JceDecoder // outermost
|
||||
|
||||
//println("!! CLASS")
|
||||
//println("decoderTag: $currentTag")
|
||||
//println("jceHead: " + jce.currentHeadOrNull)
|
||||
return jce.skipToHeadAndUseIfPossibleOrFail(popTag().id) { jceHead ->
|
||||
jceHead.checkType(Jce.STRUCT_BEGIN)
|
||||
|
||||
repeat(descriptor.elementsCount) {
|
||||
pushTag(descriptor.getTag(descriptor.elementsCount - it - 1)) // better performance
|
||||
}
|
||||
this // independent tag stack
|
||||
}
|
||||
}
|
||||
|
||||
StructureKind.OBJECT -> error("unsupported StructureKind.OBJECT: ${descriptor.serialName}")
|
||||
is UnionKind -> error("unsupported UnionKind: ${descriptor.serialName}")
|
||||
is PolymorphicKind -> error("unsupported PolymorphicKind: ${descriptor.serialName}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun decodeSequentially(): Boolean = false
|
||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
||||
val jceHead = jce.currentHeadOrNull ?: return CompositeDecoder.READ_DONE
|
||||
if (jceHead.type == Jce.STRUCT_END) {
|
||||
return CompositeDecoder.READ_DONE
|
||||
}
|
||||
|
||||
repeat(descriptor.elementsCount) {
|
||||
val tag = descriptor.getJceTagId(it)
|
||||
if (tag == jceHead.tag) {
|
||||
return it
|
||||
}
|
||||
}
|
||||
|
||||
return CompositeDecoder.READ_DONE // optional support
|
||||
}
|
||||
|
||||
override fun decodeTaggedInt(tag: JceTag): Int =
|
||||
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceIntValue(it) }
|
||||
|
||||
override fun decodeTaggedByte(tag: JceTag): Byte =
|
||||
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceByteValue(it) }
|
||||
|
||||
override fun decodeTaggedBoolean(tag: JceTag): Boolean =
|
||||
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceBooleanValue(it) }
|
||||
|
||||
override fun decodeTaggedFloat(tag: JceTag): Float =
|
||||
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceFloatValue(it) }
|
||||
|
||||
override fun decodeTaggedDouble(tag: JceTag): Double =
|
||||
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceDoubleValue(it) }
|
||||
|
||||
override fun decodeTaggedShort(tag: JceTag): Short =
|
||||
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceShortValue(it) }
|
||||
|
||||
override fun decodeTaggedLong(tag: JceTag): Long =
|
||||
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceLongValue(it) }
|
||||
|
||||
override fun decodeTaggedString(tag: JceTag): String =
|
||||
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceStringValue(it) }
|
||||
|
||||
override fun decodeTaggedNotNullMark(tag: JceTag): Boolean {
|
||||
return jce.skipToHeadOrNull(tag.id) != null
|
||||
}
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.io.serialization.jce
|
||||
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.JceCharset
|
||||
import net.mamoe.mirai.utils.io.readString
|
||||
|
||||
|
||||
/**
|
||||
* Jce Input. 需要手动管理 head.
|
||||
*/
|
||||
internal class JceInput(
|
||||
val input: Input, val charset: JceCharset
|
||||
) {
|
||||
private var _head: JceHead? = null
|
||||
|
||||
val currentHead: JceHead get() = _head ?: throw EOFException("No current JceHead available")
|
||||
val currentHeadOrNull: JceHead? get() = _head
|
||||
|
||||
init {
|
||||
prepareNextHead()
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取下一个 [JceHead] 并保存. 可通过 [currentHead] 获取这个 [JceHead].
|
||||
*
|
||||
* @return 是否成功读取. 返回 `false` 时代表 [Input.endOfInput]
|
||||
*/
|
||||
fun prepareNextHead(): Boolean {
|
||||
return readNextHeadButDoNotAssignTo_Head().also { _head = it; } != null
|
||||
}
|
||||
|
||||
fun nextHead(): JceHead {
|
||||
if (!prepareNextHead()) {
|
||||
throw EOFException("No more JceHead available")
|
||||
}
|
||||
return currentHead
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接读取下一个 [JceHead] 并返回.
|
||||
* 返回 `null` 则代表 [Input.endOfInput]
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
private fun readNextHeadButDoNotAssignTo_Head(): JceHead? {
|
||||
if (input.endOfInput) {
|
||||
return null
|
||||
}
|
||||
val var2 = input.readUByte()
|
||||
val type = var2 and 15u
|
||||
var tag = var2.toUInt() shr 4
|
||||
if (tag == 15u) {
|
||||
tag = input.readUByte().toUInt()
|
||||
}
|
||||
return JceHead(
|
||||
tag = tag.toInt(),
|
||||
type = type.toByte()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用这个 [JceHead].
|
||||
* [block] 结束后将会 [准备下一个 [JceHead]][prepareNextHead]
|
||||
*/
|
||||
inline fun <R> useHead(crossinline block: (JceHead) -> R): R {
|
||||
return currentHead.let(block).also { prepareNextHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳过 [JceHead] 和对应的数据值, 直到找到 [tag], 否则返回 `null`
|
||||
*/
|
||||
inline fun <R> skipToHeadAndUseIfPossibleOrNull(tag: Int, crossinline block: (JceHead) -> R): R? {
|
||||
return skipToHeadOrNull(tag)?.let(block).also { prepareNextHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳过 [JceHead] 和对应的数据值, 直到找到 [tag], 否则抛出异常
|
||||
*/
|
||||
inline fun <R : Any> skipToHeadAndUseIfPossibleOrFail(
|
||||
tag: Int,
|
||||
crossinline message: () -> String = { "tag not found: $tag" },
|
||||
crossinline block: (JceHead) -> R
|
||||
): R {
|
||||
return checkNotNull<R>(skipToHeadAndUseIfPossibleOrNull(tag, block), message)
|
||||
}
|
||||
|
||||
tailrec fun skipToHeadOrNull(tag: Int): JceHead? {
|
||||
val current: JceHead = currentHeadOrNull ?: return null // no backing field
|
||||
|
||||
return when {
|
||||
current.tag > tag -> null // tag 大了,即找不到
|
||||
current.tag == tag -> current // 满足需要.
|
||||
else -> { // tag 小了
|
||||
skipField(current.type)
|
||||
check(prepareNextHead()) { "cannot skip to tag $tag, early EOF" }
|
||||
skipToHeadOrNull(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun skipToHeadOrFail(
|
||||
tag: Int,
|
||||
message: () -> String = { "head not found: $tag" }
|
||||
): JceHead {
|
||||
return checkNotNull(skipToHeadOrNull(tag), message)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
@PublishedApi
|
||||
internal fun skipField(type: Byte): Unit = when (type) {
|
||||
Jce.BYTE -> this.input.discardExact(1)
|
||||
Jce.SHORT -> this.input.discardExact(2)
|
||||
Jce.INT -> this.input.discardExact(4)
|
||||
Jce.LONG -> this.input.discardExact(8)
|
||||
Jce.FLOAT -> this.input.discardExact(4)
|
||||
Jce.DOUBLE -> this.input.discardExact(8)
|
||||
Jce.STRING1 -> this.input.discardExact(this.input.readUByte().toInt())
|
||||
Jce.STRING4 -> this.input.discardExact(this.input.readInt())
|
||||
Jce.MAP -> { // map
|
||||
repeat(skipToHeadAndUseIfPossibleOrFail(0) {
|
||||
readJceIntValue(it)
|
||||
} * 2) {
|
||||
useHead { skipField(it.type) }
|
||||
}
|
||||
}
|
||||
Jce.LIST -> { // list
|
||||
repeat(skipToHeadAndUseIfPossibleOrFail(0) {
|
||||
readJceIntValue(it)
|
||||
}) {
|
||||
useHead { skipField(it.type) }
|
||||
}
|
||||
}
|
||||
Jce.STRUCT_BEGIN -> {
|
||||
fun skipToStructEnd() {
|
||||
var head: JceHead
|
||||
do {
|
||||
head = nextHead()
|
||||
skipField(head.type)
|
||||
} while (head.type.toInt() != 11)
|
||||
}
|
||||
skipToStructEnd()
|
||||
}
|
||||
Jce.STRUCT_END, Jce.ZERO_TYPE -> {
|
||||
|
||||
}
|
||||
Jce.SIMPLE_LIST -> {
|
||||
val head = nextHead()
|
||||
check(head.type.toInt() == 0) { "skipField with invalid type, type value: " + type + ", " + head.type }
|
||||
this.input.discardExact(
|
||||
skipToHeadAndUseIfPossibleOrFail(0) {
|
||||
readJceIntValue(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
else -> error("invalid type: $type")
|
||||
}
|
||||
|
||||
// region readers
|
||||
fun readJceIntValue(head: JceHead): Int {
|
||||
//println("readJceIntValue: $head")
|
||||
return when (head.type) {
|
||||
Jce.ZERO_TYPE -> 0
|
||||
Jce.BYTE -> input.readByte().toInt()
|
||||
Jce.SHORT -> input.readShort().toInt()
|
||||
Jce.INT -> input.readInt()
|
||||
else -> error("type mismatch: ${head.type}")
|
||||
}
|
||||
}
|
||||
|
||||
fun readJceShortValue(head: JceHead): Short {
|
||||
return when (head.type) {
|
||||
Jce.ZERO_TYPE -> 0
|
||||
Jce.BYTE -> input.readByte().toShort()
|
||||
Jce.SHORT -> input.readShort()
|
||||
else -> error("type mismatch: ${head.type}")
|
||||
}
|
||||
}
|
||||
|
||||
fun readJceLongValue(head: JceHead): Long {
|
||||
return when (head.type) {
|
||||
Jce.ZERO_TYPE -> 0
|
||||
Jce.BYTE -> input.readByte().toLong()
|
||||
Jce.SHORT -> input.readShort().toLong()
|
||||
Jce.INT -> input.readInt().toLong()
|
||||
Jce.LONG -> input.readLong()
|
||||
else -> error("type mismatch ${head.type}")
|
||||
}
|
||||
}
|
||||
|
||||
fun readJceByteValue(head: JceHead): Byte {
|
||||
//println("readJceByteValue: $head")
|
||||
return when (head.type) {
|
||||
Jce.ZERO_TYPE -> 0
|
||||
Jce.BYTE -> input.readByte()
|
||||
else -> error("type mismatch: ${head.type}")
|
||||
}
|
||||
}
|
||||
|
||||
fun readJceFloatValue(head: JceHead): Float {
|
||||
return when (head.type) {
|
||||
Jce.ZERO_TYPE -> 0f
|
||||
Jce.FLOAT -> input.readFloat()
|
||||
else -> error("type mismatch: ${head.type}")
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
fun readJceStringValue(head: JceHead): String {
|
||||
//println("readJceStringValue: $head")
|
||||
return when (head.type) {
|
||||
Jce.STRING1 -> input.readString(input.readUByte().toInt(), charset = charset.kotlinCharset)
|
||||
Jce.STRING4 -> input.readString(
|
||||
input.readUInt().toInt().also { require(it in 1 until 104857600) { "bad string length: $it" } },
|
||||
charset = charset.kotlinCharset
|
||||
)
|
||||
else -> error("type mismatch: ${head.type}, expecting 6 or 7 (for string)")
|
||||
}
|
||||
}
|
||||
|
||||
fun readJceDoubleValue(head: JceHead): Double {
|
||||
return when (head.type.toInt()) {
|
||||
12 -> 0.0
|
||||
4 -> input.readFloat().toDouble()
|
||||
5 -> input.readDouble()
|
||||
else -> error("type mismatch: ${head.type}")
|
||||
}
|
||||
}
|
||||
|
||||
fun readJceBooleanValue(head: JceHead): Boolean {
|
||||
return readJceByteValue(head) == 1.toByte()
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.io.serialization.jce
|
||||
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.serialization.BinaryFormat
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
import kotlinx.serialization.SerialFormat
|
||||
import kotlinx.serialization.SerializationStrategy
|
||||
import kotlinx.serialization.modules.EmptyModule
|
||||
import kotlinx.serialization.modules.SerialModule
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.IOFormat
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.JceCharset
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.JceOld
|
||||
import net.mamoe.mirai.utils.io.toReadPacket
|
||||
|
||||
/**
|
||||
* Jce 数据结构序列化和反序列化器.
|
||||
*
|
||||
* @author Him188
|
||||
*/
|
||||
class Jce(
|
||||
override val context: SerialModule,
|
||||
val charset: JceCharset
|
||||
) : SerialFormat, IOFormat, BinaryFormat {
|
||||
override fun <T> dumpTo(serializer: SerializationStrategy<T>, ojb: T, output: Output) {
|
||||
output.writePacket(JceOld.byCharSet(this.charset).dumpAsPacket(serializer, ojb))
|
||||
}
|
||||
|
||||
override fun <T> load(deserializer: DeserializationStrategy<T>, input: Input): T {
|
||||
return JceDecoder(JceInput(input, charset), context).decodeSerializableValue(deserializer)
|
||||
}
|
||||
|
||||
override fun <T> dump(serializer: SerializationStrategy<T>, value: T): ByteArray {
|
||||
return buildPacket { dumpTo(serializer, value, this) }.readBytes()
|
||||
}
|
||||
|
||||
override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T {
|
||||
return load(deserializer, bytes.toReadPacket())
|
||||
}
|
||||
|
||||
companion object {
|
||||
val UTF_8 = Jce(EmptyModule, JceCharset.UTF8)
|
||||
val GBK = Jce(EmptyModule, JceCharset.GBK)
|
||||
|
||||
fun byCharSet(c: JceCharset): Jce {
|
||||
return if (c == JceCharset.UTF8) UTF_8 else GBK
|
||||
}
|
||||
|
||||
internal const val BYTE: Byte = 0
|
||||
internal const val DOUBLE: Byte = 5
|
||||
internal const val FLOAT: Byte = 4
|
||||
internal const val INT: Byte = 2
|
||||
internal const val JCE_MAX_STRING_LENGTH = 104857600
|
||||
internal const val LIST: Byte = 9
|
||||
internal const val LONG: Byte = 3
|
||||
internal const val MAP: Byte = 8
|
||||
internal const val SHORT: Byte = 1
|
||||
internal const val SIMPLE_LIST: Byte = 13
|
||||
internal const val STRING1: Byte = 6
|
||||
internal const val STRING4: Byte = 7
|
||||
internal const val STRUCT_BEGIN: Byte = 10
|
||||
internal const val STRUCT_END: Byte = 11
|
||||
internal const val ZERO_TYPE: Byte = 12
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.io.serialization.jce
|
||||
|
||||
import kotlinx.io.core.Output
|
||||
import kotlinx.serialization.SerialInfo
|
||||
|
||||
|
||||
/**
|
||||
* 标注 JCE 序列化时使用的 ID
|
||||
*/
|
||||
@SerialInfo
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
annotation class JceId(val id: Int)
|
||||
|
||||
/**
|
||||
* 类中元素的 tag
|
||||
*
|
||||
* 保留这个结构, 为将来增加功能的兼容性.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal abstract class JceTag {
|
||||
abstract val id: Int
|
||||
|
||||
internal var isSimpleByteArray: Boolean = false
|
||||
}
|
||||
|
||||
internal object JceTagListElement : JceTag() {
|
||||
override val id: Int get() = 0
|
||||
override fun toString(): String {
|
||||
return "JceTagListElement"
|
||||
}
|
||||
}
|
||||
|
||||
internal object JceTagMapEntryKey : JceTag() {
|
||||
override val id: Int get() = 0
|
||||
override fun toString(): String {
|
||||
return "JceTagMapEntryKey"
|
||||
}
|
||||
}
|
||||
|
||||
internal object JceTagMapEntryValue : JceTag() {
|
||||
override val id: Int get() = 1
|
||||
override fun toString(): String {
|
||||
return "JceTagMapEntryValue"
|
||||
}
|
||||
}
|
||||
|
||||
internal data class JceTagCommon(
|
||||
override val id: Int
|
||||
) : JceTag()
|
||||
|
||||
fun JceHead.checkType(type: Byte) {
|
||||
check(this.type == type) { "type mismatch. Expected $type, actual ${this.type}" }
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun Output.writeJceHead(type: Byte, tag: Int) {
|
||||
if (tag < 15) {
|
||||
writeByte(((tag shl 4) or type.toInt()).toByte())
|
||||
return
|
||||
}
|
||||
if (tag < 256) {
|
||||
writeByte((type.toInt() or 0xF0).toByte())
|
||||
writeByte(tag.toByte())
|
||||
return
|
||||
}
|
||||
error("tag is too large: $tag")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
inline class JceHead(private val value: Long) {
|
||||
constructor(tag: Int, type: Byte) : this(tag.toLong().shl(32) or type.toLong())
|
||||
|
||||
val tag: Int get() = (value ushr 32).toInt()
|
||||
val type: Byte get() = value.toUInt().toByte()
|
||||
|
||||
override fun toString(): String {
|
||||
val typeString = when (type) {
|
||||
Jce.BYTE -> "Byte"
|
||||
Jce.DOUBLE -> "Double"
|
||||
Jce.FLOAT -> "Float"
|
||||
Jce.INT -> "Int"
|
||||
Jce.LIST -> "List"
|
||||
Jce.LONG -> "Long"
|
||||
Jce.MAP -> "Map"
|
||||
Jce.SHORT -> "Short"
|
||||
Jce.SIMPLE_LIST -> "SimpleList"
|
||||
Jce.STRING1 -> "String1"
|
||||
Jce.STRING4 -> "String4"
|
||||
Jce.STRUCT_BEGIN -> "StructBegin"
|
||||
Jce.STRUCT_END -> "StructEnd"
|
||||
Jce.ZERO_TYPE -> "Zero"
|
||||
else -> error("illegal jce type: $type")
|
||||
}
|
||||
return "JceHead(tag=$tag, type=$type($typeString))"
|
||||
}
|
||||
}
|
@ -7,6 +7,9 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmName("SerializationUtils")
|
||||
@file:JvmMultifileClass
|
||||
|
||||
package net.mamoe.mirai.qqandroid.io.serialization
|
||||
|
||||
import kotlinx.io.core.*
|
||||
@ -15,20 +18,29 @@ import kotlinx.serialization.SerialDescriptor
|
||||
import kotlinx.serialization.SerializationStrategy
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion2
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion3
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.firstValue
|
||||
import net.mamoe.mirai.utils.io.read
|
||||
import net.mamoe.mirai.utils.io.toUHexString
|
||||
import net.mamoe.mirai.utils.io.readPacketExact
|
||||
import net.mamoe.mirai.utils.io.toReadPacket
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
|
||||
fun <T : JceStruct> ByteArray.loadAs(deserializer: DeserializationStrategy<T>, c: JceCharset = JceCharset.UTF8): T {
|
||||
return Jce.byCharSet(c).load(deserializer, this)
|
||||
return Jce.byCharSet(c).load(deserializer, this.toReadPacket())
|
||||
}
|
||||
|
||||
fun <T : JceStruct> BytePacketBuilder.writeJceStruct(serializer: SerializationStrategy<T>, struct: T, charset: JceCharset = JceCharset.GBK) {
|
||||
this.writePacket(Jce.byCharSet(charset).dumpAsPacket(serializer, struct))
|
||||
fun <T : JceStruct> BytePacketBuilder.writeJceStruct(
|
||||
serializer: SerializationStrategy<T>,
|
||||
struct: T,
|
||||
charset: JceCharset = JceCharset.GBK
|
||||
) {
|
||||
Jce.byCharSet(charset).dumpTo(serializer, struct, this)
|
||||
}
|
||||
|
||||
fun <T : JceStruct> ByteReadPacket.readJceStruct(
|
||||
@ -36,7 +48,8 @@ fun <T : JceStruct> ByteReadPacket.readJceStruct(
|
||||
charset: JceCharset = JceCharset.UTF8,
|
||||
length: Int = this.remaining.toInt()
|
||||
): T {
|
||||
return Jce.byCharSet(charset).load(serializer, this, length)
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
return Jce.byCharSet(charset).load(serializer, this.readPacketExact(length))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,18 +79,20 @@ fun <T : ProtoBuf> ByteReadPacket.decodeUniPacket(deserializer: DeserializationS
|
||||
fun <R> ByteReadPacket.decodeUniRequestPacketAndDeserialize(name: String? = null, block: (ByteArray) -> R): R {
|
||||
val request = this.readJceStruct(RequestPacket.serializer())
|
||||
|
||||
return block(if (name == null) when (request.iVersion.toInt()) {
|
||||
return block(if (name == null) when (request.iVersion?.toInt() ?: 3) {
|
||||
2 -> request.sBuffer.loadAs(RequestDataVersion2.serializer()).map.firstValue().firstValue()
|
||||
3 -> request.sBuffer.loadAs(RequestDataVersion3.serializer()).map.firstValue()
|
||||
else -> error("unsupported version ${request.iVersion}")
|
||||
} else when (request.iVersion.toInt()) {
|
||||
2 -> request.sBuffer.loadAs(RequestDataVersion2.serializer()).map.getOrElse(name) { error("cannot find $name") }.firstValue()
|
||||
} else when (request.iVersion?.toInt() ?: 3) {
|
||||
2 -> request.sBuffer.loadAs(RequestDataVersion2.serializer()).map.getOrElse(name) { error("cannot find $name") }
|
||||
.firstValue()
|
||||
3 -> request.sBuffer.loadAs(RequestDataVersion3.serializer()).map.getOrElse(name) { error("cannot find $name") }
|
||||
else -> error("unsupported version ${request.iVersion}")
|
||||
})
|
||||
}
|
||||
|
||||
fun <T : JceStruct> T.toByteArray(serializer: SerializationStrategy<T>, c: JceCharset = JceCharset.GBK): ByteArray = Jce.byCharSet(c).dump(serializer, this)
|
||||
fun <T : JceStruct> T.toByteArray(serializer: SerializationStrategy<T>, c: JceCharset = JceCharset.GBK): ByteArray =
|
||||
Jce.byCharSet(c).dump(serializer, this)
|
||||
|
||||
fun <T : ProtoBuf> BytePacketBuilder.writeProtoBuf(serializer: SerializationStrategy<T>, v: T) {
|
||||
this.writeFully(v.toByteArray(serializer))
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.message
|
||||
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.loadAs
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SourceMsg
|
||||
|
||||
internal inline class MessageSourceFromServer(
|
||||
val delegate: ImMsgBody.SourceMsg
|
||||
) : MessageSource {
|
||||
override val messageUid: Long get() = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!!
|
||||
override val sourceMessage: MessageChain get() = delegate.toMessageChain()
|
||||
override val senderId: Long get() = delegate.senderUin
|
||||
override val groupId: Long get() = Group.calculateGroupCodeByGroupUin(delegate.toUin)
|
||||
|
||||
override fun toString(): String = ""
|
||||
}
|
||||
|
||||
internal inline class MessageSourceFromMsg(
|
||||
val delegate: MsgComm.Msg
|
||||
) : MessageSource {
|
||||
override val messageUid: Long get() = delegate.msgBody.richText.attr!!.random.toLong()
|
||||
override val sourceMessage: MessageChain get() = delegate.toMessageChain()
|
||||
override val senderId: Long get() = delegate.msgHead.fromUin
|
||||
override val groupId: Long get() = delegate.msgHead.groupInfo!!.groupCode
|
||||
|
||||
fun toJceData(): ImMsgBody.SourceMsg {
|
||||
|
||||
val groupUin = Group.calculateGroupUinByGroupCode(delegate.msgHead.groupInfo!!.groupCode)
|
||||
|
||||
return ImMsgBody.SourceMsg(
|
||||
origSeqs = listOf(delegate.msgHead.msgSeq),
|
||||
senderUin = delegate.msgHead.fromUin,
|
||||
toUin = groupUin,
|
||||
flag = 1,
|
||||
elems = delegate.msgBody.richText.elems,
|
||||
type = 0,
|
||||
time = delegate.msgHead.msgTime,
|
||||
pbReserve = SourceMsg.ResvAttr(
|
||||
origUids = messageUid
|
||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||
srcMsg = MsgComm.Msg(
|
||||
msgHead = MsgComm.MsgHead(
|
||||
fromUin = delegate.msgHead.fromUin, // qq
|
||||
toUin = groupUin, // group
|
||||
msgType = delegate.msgHead.msgType, // 82?
|
||||
c2cCmd = delegate.msgHead.c2cCmd,
|
||||
msgSeq = delegate.msgHead.msgSeq,
|
||||
msgTime = delegate.msgHead.msgTime,
|
||||
msgUid = messageUid, // ok
|
||||
groupInfo = MsgComm.GroupInfo(groupCode = delegate.msgHead.groupInfo.groupCode),
|
||||
isSrcMsg = true
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = delegate.msgBody.richText.elems.also {
|
||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
||||
}
|
||||
)
|
||||
)
|
||||
).toByteArray(MsgComm.Msg.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString(): String = ""
|
||||
}
|
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.message
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.event.subscribingGetAsync
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import net.mamoe.mirai.message.data.messageRandom
|
||||
import net.mamoe.mirai.message.data.sequenceId
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.loadAs
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SourceMsg
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
|
||||
internal class MessageSourceFromServer(
|
||||
val delegate: ImMsgBody.SourceMsg
|
||||
) : MessageSource {
|
||||
override val time: Long get() = delegate.time.toLong() and 0xFFFFFFFF
|
||||
|
||||
override val originalMessage: MessageChain by lazy {
|
||||
delegate.toMessageChain()
|
||||
}
|
||||
|
||||
override val id: Long
|
||||
get() = (delegate.origSeqs?.firstOrNull()
|
||||
?: error("cannot find sequenceId from ImMsgBody.SourceMsg")).toLong().shl(32) or
|
||||
delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!!.and(0xFFFFFFFF)
|
||||
|
||||
override val toUin: Long get() = delegate.toUin // always 0
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
// override val sourceMessage: MessageChain get() = delegate.toMessageChain()
|
||||
override val senderId: Long get() = delegate.senderUin
|
||||
override val groupId: Long get() = Group.calculateGroupCodeByGroupUin(delegate.toUin)
|
||||
|
||||
override fun toString(): String = ""
|
||||
}
|
||||
|
||||
internal class MessageSourceFromMsg(
|
||||
val delegate: MsgComm.Msg
|
||||
) : MessageSource {
|
||||
override val time: Long get() = delegate.msgHead.msgTime.toLong() and 0xFFFFFFFF
|
||||
override val id: Long =
|
||||
delegate.msgHead.msgSeq.toLong().shl(32) or
|
||||
delegate.msgBody.richText.attr!!.random.toLong().and(0xFFFFFFFF)
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
override val toUin: Long get() = delegate.msgHead.toUin
|
||||
override val senderId: Long get() = delegate.msgHead.fromUin
|
||||
override val groupId: Long get() = delegate.msgHead.groupInfo?.groupCode ?: 0
|
||||
override val originalMessage: MessageChain by lazy {
|
||||
delegate.toMessageChain()
|
||||
}
|
||||
|
||||
fun toJceData(): ImMsgBody.SourceMsg {
|
||||
return if (groupId == 0L) {
|
||||
toJceDataImplForFriend()
|
||||
} else toJceDataImplForGroup()
|
||||
}
|
||||
|
||||
val elems by lazy {
|
||||
delegate.msgBody.richText.elems.toMutableList().also {
|
||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun toJceDataImplForFriend(): ImMsgBody.SourceMsg {
|
||||
return ImMsgBody.SourceMsg(
|
||||
origSeqs = listOf(delegate.msgHead.msgSeq),
|
||||
senderUin = delegate.msgHead.fromUin,
|
||||
toUin = delegate.msgHead.toUin,
|
||||
flag = 1,
|
||||
elems = delegate.msgBody.richText.elems,
|
||||
type = 0,
|
||||
time = delegate.msgHead.msgTime,
|
||||
pbReserve = SourceMsg.ResvAttr(
|
||||
origUids = messageRandom.toLong() and 0xffFFffFF
|
||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||
srcMsg = MsgComm.Msg(
|
||||
msgHead = MsgComm.MsgHead(
|
||||
fromUin = delegate.msgHead.fromUin, // qq
|
||||
toUin = delegate.msgHead.toUin, // group
|
||||
msgType = delegate.msgHead.msgType, // 82?
|
||||
c2cCmd = delegate.msgHead.c2cCmd,
|
||||
msgSeq = delegate.msgHead.msgSeq,
|
||||
msgTime = delegate.msgHead.msgTime,
|
||||
msgUid = messageRandom.toLong() and 0xffFFffFF, // ok
|
||||
// groupInfo = MsgComm.GroupInfo(groupCode = delegate.msgHead.groupInfo.groupCode),
|
||||
isSrcMsg = true
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = elems
|
||||
)
|
||||
)
|
||||
).toByteArray(MsgComm.Msg.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
private fun toJceDataImplForGroup(): ImMsgBody.SourceMsg {
|
||||
return ImMsgBody.SourceMsg(
|
||||
origSeqs = listOf(delegate.msgHead.msgSeq),
|
||||
senderUin = delegate.msgHead.fromUin,
|
||||
toUin = 0,
|
||||
flag = 1,
|
||||
elems = delegate.msgBody.richText.elems,
|
||||
type = 0,
|
||||
time = delegate.msgHead.msgTime,
|
||||
pbReserve = EMPTY_BYTE_ARRAY,
|
||||
srcMsg = EMPTY_BYTE_ARRAY
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString(): String = ""
|
||||
}
|
||||
|
||||
internal abstract class MessageSourceFromSend : MessageSource {
|
||||
|
||||
abstract override val originalMessage: MessageChain
|
||||
|
||||
fun toJceData(): ImMsgBody.SourceMsg {
|
||||
return if (groupId == 0L) {
|
||||
toJceDataImplForFriend()
|
||||
} else toJceDataImplForGroup()
|
||||
}
|
||||
|
||||
private val elems by lazy {
|
||||
originalMessage.toRichTextElems(groupId != 0L)
|
||||
}
|
||||
|
||||
private fun toJceDataImplForFriend(): ImMsgBody.SourceMsg {
|
||||
val messageUid: Long = 262144L.shl(32) or messageRandom.toLong().and(0xffFFffFF)
|
||||
return ImMsgBody.SourceMsg(
|
||||
origSeqs = listOf(sequenceId),
|
||||
senderUin = senderId,
|
||||
toUin = toUin,
|
||||
flag = 1,
|
||||
elems = elems,
|
||||
type = 0,
|
||||
time = time.toInt(),
|
||||
pbReserve = SourceMsg.ResvAttr(
|
||||
origUids = messageUid
|
||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||
srcMsg = MsgComm.Msg(
|
||||
msgHead = MsgComm.MsgHead(
|
||||
fromUin = senderId, // qq
|
||||
toUin = toUin, // group
|
||||
msgType = 9, // 82?
|
||||
c2cCmd = 11,
|
||||
msgSeq = sequenceId,
|
||||
msgTime = time.toInt(),
|
||||
msgUid = messageUid, // ok
|
||||
// groupInfo = MsgComm.GroupInfo(groupCode = delegate.msgHead.groupInfo.groupCode),
|
||||
isSrcMsg = true
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = elems.toMutableList().also {
|
||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
||||
}
|
||||
)
|
||||
)
|
||||
).toByteArray(MsgComm.Msg.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
private fun toJceDataImplForGroup(): ImMsgBody.SourceMsg {
|
||||
return ImMsgBody.SourceMsg(
|
||||
origSeqs = listOf(sequenceId),
|
||||
senderUin = senderId,
|
||||
toUin = toUin,
|
||||
flag = 1,
|
||||
elems = elems,
|
||||
type = 0,
|
||||
time = time.toInt(),
|
||||
pbReserve = SourceMsg.ResvAttr(
|
||||
origUids = messageRandom.toLong() and 0xffFFffFF
|
||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||
srcMsg = MsgComm.Msg(
|
||||
msgHead = MsgComm.MsgHead(
|
||||
fromUin = senderId, // qq
|
||||
toUin = toUin, // group
|
||||
msgType = 82, // 82?
|
||||
c2cCmd = 1,
|
||||
msgSeq = sequenceId,
|
||||
msgTime = time.toInt(),
|
||||
msgUid = messageRandom.toLong() and 0xffFFffFF, // ok
|
||||
groupInfo = MsgComm.GroupInfo(groupCode = groupId),
|
||||
isSrcMsg = true
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = elems.toMutableList().also {
|
||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
||||
}
|
||||
)
|
||||
)
|
||||
).toByteArray(MsgComm.Msg.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal class MessageSourceFromSendFriend(
|
||||
val messageRandom: Int,
|
||||
override val time: Long,
|
||||
override val senderId: Long,
|
||||
override val toUin: Long,
|
||||
override val groupId: Long,
|
||||
val sequenceId: Int,
|
||||
override val originalMessage: MessageChain
|
||||
) : MessageSourceFromSend() {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override val id: Long
|
||||
get() = sequenceId.toLong().shl(32) or
|
||||
messageRandom.toLong().and(0xFFFFFFFF)
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
internal class MessageSourceFromSendGroup(
|
||||
val messageRandom: Int,
|
||||
override val time: Long,
|
||||
override val senderId: Long,
|
||||
override val toUin: Long,
|
||||
override val groupId: Long,
|
||||
override val originalMessage: MessageChain
|
||||
) : MessageSourceFromSend() {
|
||||
private lateinit var sequenceIdDeferred: Deferred<Int>
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override val id: Long
|
||||
get() = sequenceIdDeferred.getCompleted().toLong().shl(32) or
|
||||
messageRandom.toLong().and(0xFFFFFFFF)
|
||||
|
||||
@OptIn(MiraiExperimentalAPI::class)
|
||||
internal fun startWaitingSequenceId(coroutineScope: CoroutineScope) {
|
||||
sequenceIdDeferred =
|
||||
coroutineScope.subscribingGetAsync<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>(
|
||||
timeoutMillis = 3000
|
||||
) {
|
||||
if (it.messageRandom == this@MessageSourceFromSendGroup.messageRandom) {
|
||||
it.sequenceId
|
||||
} else null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
sequenceIdDeferred.join()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return ""
|
||||
}
|
||||
}
|
@ -9,28 +9,35 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.message
|
||||
|
||||
import kotlinx.io.core.readUInt
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.LowLevelAPI
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.utils.MiraiDebugAPI
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.io.discardExact
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.io.encodeToString
|
||||
import net.mamoe.mirai.utils.io.hexToBytes
|
||||
import net.mamoe.mirai.utils.io.read
|
||||
import net.mamoe.mirai.utils.io.toByteArray
|
||||
|
||||
private val AT_BUF_1 = byteArrayOf(0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00)
|
||||
private val AT_BUF_2 = ByteArray(2)
|
||||
|
||||
internal fun At.toJceData(): ImMsgBody.Text {
|
||||
val text = this.toString()
|
||||
return ImMsgBody.Text(
|
||||
str = this.toString(),
|
||||
attr6Buf = AT_BUF_1 + this.target.toInt().toByteArray() + AT_BUF_2
|
||||
str = text,
|
||||
attr6Buf = buildPacket {
|
||||
// MessageForText$AtTroopMemberInfo
|
||||
writeShort(1) // const
|
||||
writeShort(0) // startPos
|
||||
writeShort(text.length.toShort()) // textLen
|
||||
writeByte(0) // flag, may=1
|
||||
writeInt(target.toInt()) // uin
|
||||
writeShort(0) // const
|
||||
}.readBytes()
|
||||
)
|
||||
}
|
||||
|
||||
internal fun NotOnlineImageFromFile.toJceData(): ImMsgBody.NotOnlineImage {
|
||||
internal fun OfflineFriendImage.toJceData(): ImMsgBody.NotOnlineImage {
|
||||
return ImMsgBody.NotOnlineImage(
|
||||
filePath = this.filepath,
|
||||
resId = this.resourceId,
|
||||
@ -86,7 +93,17 @@ _400Height=0x000000EB(235)
|
||||
pbReserve=<Empty ByteArray>
|
||||
}
|
||||
*/
|
||||
internal fun CustomFaceFromFile.toJceData(): ImMsgBody.CustomFace {
|
||||
val FACE_BUF = "00 01 00 04 52 CC F5 D0".hexToBytes()
|
||||
|
||||
internal fun Face.toJceData(): ImMsgBody.Face {
|
||||
return ImMsgBody.Face(
|
||||
index = this.id,
|
||||
old = (0x1445 - 4 + this.id).toShort().toByteArray(),
|
||||
buf = FACE_BUF
|
||||
)
|
||||
}
|
||||
|
||||
internal fun OfflineGroupImage.toJceData(): ImMsgBody.CustomFace {
|
||||
return ImMsgBody.CustomFace(
|
||||
filePath = this.filepath,
|
||||
fileId = this.fileId,
|
||||
@ -189,30 +206,75 @@ notOnlineImage=NotOnlineImage#2050019814 {
|
||||
private val atAllData = ImMsgBody.Elem(
|
||||
text = ImMsgBody.Text(
|
||||
str = "@全体成员",
|
||||
attr6Buf = "00 01 00 00 00 05 01 00 00 00 00 00 00".hexToBytes()
|
||||
attr6Buf = buildPacket {
|
||||
// MessageForText$AtTroopMemberInfo
|
||||
writeShort(1) // const
|
||||
writeShort(0) // startPos
|
||||
writeShort("@全体成员".length.toShort()) // textLen
|
||||
writeByte(1) // flag, may=1
|
||||
writeInt(0) // uin
|
||||
writeShort(0) // const
|
||||
}.readBytes()
|
||||
)
|
||||
)
|
||||
|
||||
internal fun MessageChain.toRichTextElems(): MutableList<ImMsgBody.Elem> {
|
||||
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
|
||||
internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgBody.Elem> {
|
||||
val elements = mutableListOf<ImMsgBody.Elem>()
|
||||
|
||||
if (this.any<QuoteReply>()) {
|
||||
when (val source = this[QuoteReply].source) {
|
||||
is MessageSourceFromServer -> elements.add(ImMsgBody.Elem(srcMsg = source.delegate))
|
||||
is MessageSourceFromMsg -> elements.add(ImMsgBody.Elem(srcMsg = source.toJceData()))
|
||||
is MessageSourceFromSend -> elements.add(ImMsgBody.Elem(srcMsg = source.toJceData()))
|
||||
else -> error("unsupported MessageSource implementation: ${source::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
this.forEach {
|
||||
|
||||
fun transformOneMessage(it: Message) {
|
||||
when (it) {
|
||||
is PlainText -> elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue)))
|
||||
is At -> elements.add(ImMsgBody.Elem(text = it.toJceData()))
|
||||
is CustomFaceFromFile -> elements.add(ImMsgBody.Elem(customFace = it.toJceData()))
|
||||
is CustomFaceFromServer -> elements.add(ImMsgBody.Elem(customFace = it.delegate))
|
||||
is NotOnlineImageFromServer -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
|
||||
is NotOnlineImageFromFile -> elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData()))
|
||||
is At -> {
|
||||
elements.add(ImMsgBody.Elem(text = it.toJceData()))
|
||||
elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = " ")))
|
||||
}
|
||||
is LightApp -> elements.add(
|
||||
ImMsgBody.Elem(
|
||||
lightApp = ImMsgBody.LightAppElem(
|
||||
data = byteArrayOf(1) + MiraiPlatformUtils.zip(it.content.toByteArray())
|
||||
)
|
||||
)
|
||||
)
|
||||
is RichMessage -> elements.add(
|
||||
ImMsgBody.Elem(
|
||||
richMsg = ImMsgBody.RichMsg(
|
||||
serviceId = when (it) {
|
||||
is XmlMessage -> 60
|
||||
is JsonMessage -> 1
|
||||
else -> error("unsupported RichMessage")
|
||||
},
|
||||
template1 = byteArrayOf(1) + MiraiPlatformUtils.zip(it.content.toByteArray())
|
||||
)
|
||||
)
|
||||
)
|
||||
is OfflineGroupImage -> elements.add(ImMsgBody.Elem(customFace = it.toJceData()))
|
||||
is OnlineGroupImageImpl -> elements.add(ImMsgBody.Elem(customFace = it.delegate))
|
||||
is OnlineFriendImageImpl -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
|
||||
is OfflineFriendImage -> elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData()))
|
||||
is AtAll -> elements.add(atAllData)
|
||||
is Face -> elements.add(ImMsgBody.Elem(face = it.toJceData()))
|
||||
is QuoteReplyToSend -> {
|
||||
if (forGroup) {
|
||||
check(it is QuoteReplyToSend.ToGroup) {
|
||||
"sending a quote to group using QuoteReplyToSend.ToFriend"
|
||||
}
|
||||
if (it.sender is Member) {
|
||||
transformOneMessage(it.createAt())
|
||||
}
|
||||
transformOneMessage(" ".toMessage())
|
||||
}
|
||||
}
|
||||
is QuoteReply,
|
||||
is MessageSource -> {
|
||||
|
||||
@ -220,18 +282,20 @@ internal fun MessageChain.toRichTextElems(): MutableList<ImMsgBody.Elem> {
|
||||
else -> error("unsupported message type: ${it::class.simpleName}")
|
||||
}
|
||||
}
|
||||
this.forEach(::transformOneMessage)
|
||||
|
||||
// if(this.any<QuoteReply>()){
|
||||
elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 F8 01 00 C8 02 00".hexToBytes())))
|
||||
// }
|
||||
if (this.any<RichMessage>()) {
|
||||
// 08 09 78 00 A0 01 81 DC 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 02 08 03 90 04 80 80 80 10 B8 04 00 C0 04 00
|
||||
elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "08 09 78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 C8 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 02 08 03 90 04 80 80 80 10 B8 04 00 C0 04 00".hexToBytes())))
|
||||
} else elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 F8 01 00 C8 02 00".hexToBytes())))
|
||||
|
||||
return elements
|
||||
}
|
||||
|
||||
internal class CustomFaceFromServer(
|
||||
internal class OnlineGroupImageImpl(
|
||||
internal val delegate: ImMsgBody.CustomFace
|
||||
) : CustomFace() {
|
||||
override val filepath: String get() = delegate.filePath
|
||||
) : OnlineGroupImage() {
|
||||
override val filepath: String = delegate.filePath
|
||||
override val fileId: Int get() = delegate.fileId
|
||||
override val serverIp: Int get() = delegate.serverIp
|
||||
override val serverPort: Int get() = delegate.serverPort
|
||||
@ -247,20 +311,22 @@ internal class CustomFaceFromServer(
|
||||
override val size: Int get() = delegate.size
|
||||
override val original: Int get() = delegate.origin
|
||||
override val pbReserve: ByteArray get() = delegate.pbReserve
|
||||
override val imageId: String get() = delegate.filePath
|
||||
override val imageId: String = ExternalImage.generateImageId(delegate.md5, imageType)
|
||||
override val originUrl: String
|
||||
get() = "http://gchat.qpic.cn" + delegate.origUrl
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is CustomFaceFromServer && other.filepath == this.filepath && other.md5.contentEquals(this.md5)
|
||||
return other is OnlineGroupImageImpl && other.filepath == this.filepath && other.md5.contentEquals(this.md5)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return filepath.hashCode() + 31 * md5.hashCode()
|
||||
return imageId.hashCode() + 31 * md5.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
internal class NotOnlineImageFromServer(
|
||||
internal class OnlineFriendImageImpl(
|
||||
internal val delegate: ImMsgBody.NotOnlineImage
|
||||
) : NotOnlineImage() {
|
||||
) : OnlineFriendImage() {
|
||||
override val resourceId: String get() = delegate.resId
|
||||
override val md5: ByteArray get() = delegate.picMd5
|
||||
override val filepath: String get() = delegate.filePath
|
||||
@ -272,52 +338,72 @@ internal class NotOnlineImageFromServer(
|
||||
override val downloadPath: String get() = delegate.downloadPath
|
||||
override val fileId: Int get() = delegate.fileId
|
||||
override val original: Int get() = delegate.original
|
||||
override val originUrl: String
|
||||
get() = "http://c2cpicdw.qpic.cn" + this.delegate.origUrl
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is NotOnlineImageFromServer && other.resourceId == this.resourceId && other.md5.contentEquals(this.md5)
|
||||
return other is OnlineFriendImageImpl && other.resourceId == this.resourceId && other.md5
|
||||
.contentEquals(this.md5)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return resourceId.hashCode() + 31 * md5.hashCode()
|
||||
return imageId.hashCode() + 31 * md5.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||
internal fun MsgComm.Msg.toMessageChain(): MessageChain {
|
||||
val elements = this.msgBody.richText.elems
|
||||
|
||||
val message = MessageChain(initialCapacity = elements.size + 1)
|
||||
message.add(MessageSourceFromMsg(delegate = this))
|
||||
elements.joinToMessageChain(message)
|
||||
return message
|
||||
return buildMessageChain(elements.size + 1) {
|
||||
+MessageSourceFromMsg(delegate = this@toMessageChain)
|
||||
elements.joinToMessageChain(this)
|
||||
}.removeAtIfHasQuoteReply()
|
||||
}
|
||||
|
||||
// These two functions are not the same.
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||
// These two functions are not identical, dont combine.
|
||||
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||
internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain {
|
||||
val elements = this.elems!!
|
||||
|
||||
val message = MessageChain(initialCapacity = elements.size + 1)
|
||||
message.add(MessageSourceFromServer(delegate = this))
|
||||
elements.joinToMessageChain(message)
|
||||
return message
|
||||
return buildMessageChain(elements.size + 1) {
|
||||
+MessageSourceFromServer(delegate = this@toMessageChain)
|
||||
elements.joinToMessageChain(this)
|
||||
}.removeAtIfHasQuoteReply()
|
||||
}
|
||||
|
||||
private fun MessageChain.removeAtIfHasQuoteReply(): MessageChain =
|
||||
this
|
||||
/*
|
||||
if (this.any<QuoteReply>()) {
|
||||
var removed = false
|
||||
this.filter {
|
||||
if (it is At && !removed) {
|
||||
false
|
||||
} else {
|
||||
removed = true
|
||||
true
|
||||
}
|
||||
}.asMessageChain()
|
||||
} else this*/
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class)
|
||||
internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChain) {
|
||||
@OptIn(
|
||||
MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class, LowLevelAPI::class
|
||||
)
|
||||
internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilder) {
|
||||
this.forEach {
|
||||
when {
|
||||
it.srcMsg != null -> message.add(QuoteReply(MessageSourceFromServer(it.srcMsg)))
|
||||
it.notOnlineImage != null -> message.add(NotOnlineImageFromServer(it.notOnlineImage))
|
||||
it.customFace != null -> message.add(CustomFaceFromServer(it.customFace))
|
||||
it.notOnlineImage != null -> message.add(OnlineFriendImageImpl(it.notOnlineImage))
|
||||
it.customFace != null -> message.add(OnlineGroupImageImpl(it.customFace))
|
||||
it.face != null -> message.add(Face(it.face.index))
|
||||
it.text != null -> {
|
||||
if (it.text.attr6Buf.isEmpty()) {
|
||||
message.add(it.text.str.toMessage())
|
||||
} else {
|
||||
// 00 01 00 00 00 05 01 00 00 00 00 00 00 all
|
||||
//00 01 00 00 00 0A 00 3E 03 3F A2 00 00 one
|
||||
// 00 01 00 00 00 0A 00 3E 03 3F A2 00 00 one/nick
|
||||
// 00 01 00 00 00 07 00 44 71 47 90 00 00 one/groupCard
|
||||
val id: Long
|
||||
it.text.attr6Buf.read {
|
||||
discardExact(7)
|
||||
@ -326,7 +412,24 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChain) {
|
||||
if (id == 0L) {
|
||||
message.add(AtAll)
|
||||
} else {
|
||||
message.add(At(id, it.text.str))
|
||||
message.add(At._lowLevelConstructAtInstance(id, it.text.str))
|
||||
}
|
||||
}
|
||||
}
|
||||
it.lightApp != null -> {
|
||||
val content = MiraiPlatformUtils.unzip(it.lightApp.data, 1).encodeToString()
|
||||
message.add(LightApp(content))
|
||||
}
|
||||
it.richMsg != null -> {
|
||||
val content = MiraiPlatformUtils.unzip(it.richMsg.template1, 1).encodeToString()
|
||||
when (it.richMsg.serviceId) {
|
||||
1 -> message.add(JsonMessage(content))
|
||||
60 -> message.add(XmlMessage(content))
|
||||
else -> {
|
||||
@Suppress("DEPRECATION")
|
||||
MiraiLogger.debug {
|
||||
"unknown richMsg.serviceId: ${it.richMsg.serviceId}, content=${it.richMsg.template1.contentToString()}, \ntryUnzip=${content}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,11 +20,9 @@ import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.use
|
||||
import net.mamoe.mirai.data.MultiPacket
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.event.BroadcastControllable
|
||||
import net.mamoe.mirai.event.CancellableEvent
|
||||
import net.mamoe.mirai.event.Event
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
import net.mamoe.mirai.event.*
|
||||
import net.mamoe.mirai.event.events.BotOfflineEvent
|
||||
import net.mamoe.mirai.event.events.BotOnlineEvent
|
||||
import net.mamoe.mirai.network.BotNetworkHandler
|
||||
import net.mamoe.mirai.network.WrongPasswordException
|
||||
import net.mamoe.mirai.qqandroid.FriendInfoImpl
|
||||
@ -43,14 +41,14 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.WtLogin
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||
import net.mamoe.mirai.utils.io.PlatformSocket
|
||||
import net.mamoe.mirai.utils.io.readPacket
|
||||
import net.mamoe.mirai.utils.io.readPacketExact
|
||||
import net.mamoe.mirai.utils.io.useBytes
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.jvm.Volatile
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler() {
|
||||
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||
override val supervisor: CompletableJob = SupervisorJob(bot.coroutineContext[Job])
|
||||
@ -67,17 +65,20 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
|
||||
private val packetReceiveLock: Mutex = Mutex()
|
||||
|
||||
private fun startPacketReceiverJobOrKill(cancelCause: CancellationException? = null): Job {
|
||||
private suspend fun startPacketReceiverJobOrKill(cancelCause: CancellationException? = null): Job {
|
||||
_packetReceiverJob?.cancel(cancelCause)
|
||||
_packetReceiverJob?.join()
|
||||
|
||||
return this.launch(CoroutineName("Incoming Packet Receiver")) {
|
||||
while (channel.isOpen) {
|
||||
while (channel.isOpen && isActive) {
|
||||
val rawInput = try {
|
||||
channel.read()
|
||||
} catch (e: CancellationException) {
|
||||
return@launch
|
||||
} catch (e: Throwable) {
|
||||
BotOfflineEvent.Dropped(bot).broadcast()
|
||||
if (this@QQAndroidBotNetworkHandler.isActive) {
|
||||
bot.launch { BotOfflineEvent.Dropped(bot, e).broadcast() }
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
packetReceiveLock.withLock {
|
||||
@ -87,25 +88,42 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
}.also { _packetReceiverJob = it }
|
||||
}
|
||||
|
||||
override suspend fun relogin() {
|
||||
heartbeatJob?.cancel()
|
||||
private fun startHeartbeatJobOrKill(cancelCause: CancellationException? = null): Job {
|
||||
heartbeatJob?.cancel(cancelCause)
|
||||
|
||||
return this@QQAndroidBotNetworkHandler.launch(CoroutineName("Heartbeat")) {
|
||||
while (this.isActive) {
|
||||
delay(bot.configuration.heartbeatPeriodMillis)
|
||||
val failException = doHeartBeat()
|
||||
if (failException != null) {
|
||||
delay(bot.configuration.firstReconnectDelayMillis)
|
||||
bot.launch { BotOfflineEvent.Dropped(bot, failException).broadcast() }
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
}.also { heartbeatJob = it }
|
||||
}
|
||||
|
||||
override suspend fun relogin(cause: Throwable?) {
|
||||
heartbeatJob?.cancel(CancellationException("relogin", cause))
|
||||
heartbeatJob?.join()
|
||||
if (::channel.isInitialized) {
|
||||
if (channel.isOpen) {
|
||||
kotlin.runCatching {
|
||||
registerClientOnline()
|
||||
registerClientOnline(500)
|
||||
}.exceptionOrNull() ?: return
|
||||
logger.info("Cannot do fast relogin. Trying slow relogin")
|
||||
}
|
||||
channel.close()
|
||||
}
|
||||
channel = PlatformSocket()
|
||||
// TODO: 2020/2/14 连接多个服务器
|
||||
// TODO: 2020/2/14 连接多个服务器, #52
|
||||
withTimeoutOrNull(3000) {
|
||||
channel.connect("113.96.13.208", 8080)
|
||||
} ?: error("timeout connecting server")
|
||||
startPacketReceiverJobOrKill(CancellationException("reconnect"))
|
||||
logger.info("Connected to server 113.96.13.208:8080")
|
||||
startPacketReceiverJobOrKill(CancellationException("relogin", cause))
|
||||
|
||||
// logger.info("Trying login")
|
||||
var response: WtLogin.Login.LoginPacketResponse = WtLogin.Login.SubCommand9(bot.client).sendAndExpect()
|
||||
mainloop@ while (true) {
|
||||
when (response) {
|
||||
@ -116,21 +134,17 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
|
||||
is WtLogin.Login.LoginPacketResponse.Captcha -> when (response) {
|
||||
is WtLogin.Login.LoginPacketResponse.Captcha.Picture -> {
|
||||
var result = response.data.withUse {
|
||||
bot.configuration.loginSolver.onSolvePicCaptcha(bot, this)
|
||||
}
|
||||
var result = bot.configuration.loginSolver.onSolvePicCaptcha(bot, response.data)
|
||||
if (result == null || result.length != 4) {
|
||||
//refresh captcha
|
||||
result = "ABCD"
|
||||
}
|
||||
response = WtLogin.Login.SubCommand2.SubmitPictureCaptcha(bot.client, response.sign, result).sendAndExpect()
|
||||
response = WtLogin.Login.SubCommand2.SubmitPictureCaptcha(bot.client, response.sign, result)
|
||||
.sendAndExpect()
|
||||
continue@mainloop
|
||||
}
|
||||
is WtLogin.Login.LoginPacketResponse.Captcha.Slider -> {
|
||||
var ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url)
|
||||
if (ticket == null) {
|
||||
ticket = ""
|
||||
}
|
||||
val ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url).orEmpty()
|
||||
response = WtLogin.Login.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect()
|
||||
continue@mainloop
|
||||
}
|
||||
@ -156,55 +170,73 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
|
||||
// println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}")
|
||||
registerClientOnline()
|
||||
startHeartbeatJobOrKill()
|
||||
}
|
||||
|
||||
private suspend fun registerClientOnline() {
|
||||
StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>()
|
||||
private suspend fun registerClientOnline(timeoutMillis: Long = 3000) {
|
||||
StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>(timeoutMillis)
|
||||
}
|
||||
|
||||
// caches
|
||||
private val _pendingEnabled = atomic(true)
|
||||
internal val pendingEnabled get() = _pendingEnabled.value
|
||||
internal var pendingIncomingPackets: LockFreeLinkedList<KnownPacketFactories.IncomingPacket<*>>? = LockFreeLinkedList()
|
||||
internal var pendingIncomingPackets: LockFreeLinkedList<KnownPacketFactories.IncomingPacket<*>>? =
|
||||
LockFreeLinkedList()
|
||||
|
||||
@UseExperimental(MiraiExperimentalAPI::class, ExperimentalTime::class)
|
||||
@OptIn(MiraiExperimentalAPI::class, ExperimentalTime::class)
|
||||
override suspend fun init(): Unit = coroutineScope {
|
||||
MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect()
|
||||
check(bot.isActive) { "bot is dead therefore network can't init" }
|
||||
check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't init" }
|
||||
|
||||
bot.qqs.delegate.clear()
|
||||
bot.friends.delegate.clear()
|
||||
bot.groups.delegate.clear()
|
||||
|
||||
val friendListJob = launch {
|
||||
try {
|
||||
lateinit var loadFriends: suspend () -> Unit
|
||||
// 不要用 fun, 不要 join declaration, 不要用 val, 编译失败警告
|
||||
loadFriends = suspend loadFriends@{
|
||||
logger.info("开始加载好友信息")
|
||||
var currentFriendCount = 0
|
||||
var totalFriendCount: Short
|
||||
while (true) {
|
||||
val data = FriendList.GetFriendGroupList(
|
||||
val data = runCatching {
|
||||
FriendList.GetFriendGroupList(
|
||||
bot.client,
|
||||
currentFriendCount,
|
||||
150,
|
||||
0,
|
||||
0
|
||||
).sendAndExpect<FriendList.GetFriendGroupList.Response>(timeoutMillis = 5000, retry = 2)
|
||||
|
||||
}.getOrElse {
|
||||
logger.error("无法加载好友列表", it)
|
||||
this@QQAndroidBotNetworkHandler.launch { delay(10.secondsToMillis); loadFriends() }
|
||||
logger.error("稍后重试加载好友列表")
|
||||
return@loadFriends
|
||||
}
|
||||
totalFriendCount = data.totalFriendCount
|
||||
data.friendList.forEach {
|
||||
// atomic add
|
||||
bot.qqs.delegate.addLast(QQImpl(bot, bot.coroutineContext, it.friendUin, FriendInfoImpl(it))).also {
|
||||
bot.friends.delegate.addLast(
|
||||
QQImpl(
|
||||
bot,
|
||||
bot.coroutineContext,
|
||||
it.friendUin,
|
||||
FriendInfoImpl(it)
|
||||
)
|
||||
).also {
|
||||
currentFriendCount++
|
||||
}
|
||||
}
|
||||
logger.verbose("正在加载好友列表 ${currentFriendCount}/${totalFriendCount}")
|
||||
logger.verbose { "正在加载好友列表 ${currentFriendCount}/${totalFriendCount}" }
|
||||
if (currentFriendCount >= totalFriendCount) {
|
||||
break
|
||||
}
|
||||
// delay(200)
|
||||
}
|
||||
logger.info("好友列表加载完成, 共 ${currentFriendCount}个")
|
||||
} catch (e: Exception) {
|
||||
logger.error("加载好友列表失败|一般这是由于加载过于频繁导致/将以热加载方式加载好友列表")
|
||||
logger.info { "好友列表加载完成, 共 ${currentFriendCount}个" }
|
||||
}
|
||||
|
||||
loadFriends()
|
||||
}
|
||||
|
||||
val groupJob = launch {
|
||||
@ -214,15 +246,18 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
|
||||
|
||||
troopListData.groups.forEach { troopNum ->
|
||||
launch {
|
||||
try {
|
||||
// 别用 fun, 别 val, 编译失败警告
|
||||
lateinit var loadGroup: suspend () -> Unit
|
||||
|
||||
loadGroup = suspend {
|
||||
tryNTimesOrException(3) {
|
||||
bot.groups.delegate.addLast(
|
||||
@Suppress("DuplicatedCode")
|
||||
GroupImpl(
|
||||
(GroupImpl(
|
||||
bot = bot,
|
||||
coroutineContext = bot.coroutineContext,
|
||||
id = troopNum.groupCode,
|
||||
groupInfo = bot.queryGroupInfo(troopNum.groupCode).apply {
|
||||
groupInfo = bot._lowLevelQueryGroupInfo(troopNum.groupCode).apply {
|
||||
this as GroupInfoImpl
|
||||
|
||||
if (this.delegate.groupName == null) {
|
||||
@ -239,51 +274,78 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
|
||||
this.delegate.groupCode = troopNum.groupCode
|
||||
},
|
||||
members = bot.queryGroupMemberList(troopNum.groupUin, troopNum.groupCode, troopNum.dwGroupOwnerUin)
|
||||
members = bot._lowLevelQueryGroupMemberList(
|
||||
troopNum.groupUin,
|
||||
troopNum.groupCode,
|
||||
troopNum.dwGroupOwnerUin
|
||||
)
|
||||
))
|
||||
)
|
||||
}?.let {
|
||||
logger.error { "群${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试" }
|
||||
logger.error(it)
|
||||
this@QQAndroidBotNetworkHandler.launch {
|
||||
delay(10_000)
|
||||
loadGroup()
|
||||
}
|
||||
}
|
||||
Unit // 别删, 编译失败警告
|
||||
}
|
||||
launch {
|
||||
loadGroup()
|
||||
}
|
||||
}
|
||||
logger.info { "群组列表与群成员加载完成, 共 ${troopListData.groups.size}个" }
|
||||
} catch (e: Exception) {
|
||||
logger.error("群${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试")
|
||||
logger.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info("群组列表与群成员加载完成, 共 ${troopListData.groups.size}个")
|
||||
} catch (e: Exception) {
|
||||
logger.error("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表")
|
||||
logger.error { "加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表" }
|
||||
logger.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
joinAll(friendListJob, groupJob)
|
||||
|
||||
heartbeatJob = this@QQAndroidBotNetworkHandler.launch(CoroutineName("Heartbeat")) {
|
||||
while (this.isActive) {
|
||||
delay(bot.configuration.heartbeatPeriodMillis)
|
||||
val failException = doHeartBeat()
|
||||
if (failException != null) {
|
||||
delay(bot.configuration.firstReconnectDelayMillis)
|
||||
close()
|
||||
BotOfflineEvent.Dropped(bot).broadcast()
|
||||
}
|
||||
withTimeoutOrNull(5000) {
|
||||
lateinit var listener: Listener<PacketReceivedEvent>
|
||||
listener = this.subscribeAlways {
|
||||
if (it.packet is MessageSvc.PbGetMsg.GetMsgSuccess) {
|
||||
listener.complete()
|
||||
}
|
||||
}
|
||||
|
||||
MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect()
|
||||
} ?: error("timeout syncing friend message history")
|
||||
|
||||
bot.firstLoginSucceed = true
|
||||
|
||||
_pendingEnabled.value = false
|
||||
pendingIncomingPackets?.forEach {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
KnownPacketFactories.handleIncomingPacket(it as KnownPacketFactories.IncomingPacket<Packet>, bot, it.flag2, it.consumer)
|
||||
KnownPacketFactories.handleIncomingPacket(
|
||||
it as KnownPacketFactories.IncomingPacket<Packet>,
|
||||
bot,
|
||||
it.flag2,
|
||||
it.consumer
|
||||
)
|
||||
}
|
||||
pendingIncomingPackets = null // release
|
||||
val list = pendingIncomingPackets
|
||||
pendingIncomingPackets = null // release, help gc
|
||||
list?.clear() // help gc
|
||||
|
||||
Unit
|
||||
BotOnlineEvent(bot).broadcast()
|
||||
Unit // dont remove. can help type inference
|
||||
}
|
||||
|
||||
suspend fun doHeartBeat(): Exception? {
|
||||
val lastException: Exception?
|
||||
try {
|
||||
kotlin.runCatching {
|
||||
Heartbeat.Alive(bot.client)
|
||||
.sendAndExpect<Heartbeat.Alive.Response>(
|
||||
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
|
||||
retry = 2
|
||||
)
|
||||
return null
|
||||
}
|
||||
Heartbeat.Alive(bot.client)
|
||||
.sendAndExpect<Heartbeat.Alive.Response>(
|
||||
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
|
||||
@ -301,10 +363,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
*/
|
||||
@Volatile
|
||||
private var cachedPacketTimeoutJob: Job? = null
|
||||
|
||||
/**
|
||||
* 缓存的包
|
||||
*/
|
||||
private val cachedPacket: AtomicRef<ByteReadPacket?> = atomic(null)
|
||||
|
||||
/**
|
||||
* 缓存的包还差多少长度
|
||||
*/
|
||||
@ -316,10 +380,17 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
*
|
||||
* @param input 一个完整的包的内容, 去掉开头的 int 包长度
|
||||
*/
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun parsePacketAsync(input: Input): Job {
|
||||
return this.launch(start = CoroutineStart.ATOMIC) {
|
||||
return this.launch(
|
||||
start = CoroutineStart.ATOMIC
|
||||
) {
|
||||
try {
|
||||
input.use { parsePacket(it) }
|
||||
} catch (e: Exception) {
|
||||
// 傻逼协程吞异常
|
||||
logger.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,35 +405,55 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
}
|
||||
|
||||
// with generic type, less mistakes
|
||||
private suspend inline fun <P : Packet> generifiedParsePacket(input: Input) {
|
||||
KnownPacketFactories.parseIncomingPacket(bot, input) { packetFactory: PacketFactory<P>, packet: P, commandName: String, sequenceId: Int ->
|
||||
handlePacket(packetFactory, packet, commandName, sequenceId)
|
||||
private suspend fun <P : Packet?> generifiedParsePacket(input: Input) {
|
||||
KnownPacketFactories.parseIncomingPacket(
|
||||
bot,
|
||||
input
|
||||
) { packetFactory: PacketFactory<P>, packet: P, commandName: String, sequenceId: Int ->
|
||||
if (packet is MultiPacket<*>) {
|
||||
packet.forEach {
|
||||
handlePacket(null, it, commandName, sequenceId)
|
||||
}
|
||||
}
|
||||
handlePacket(packetFactory, packet, commandName, sequenceId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理解析完成的包.
|
||||
*/
|
||||
suspend fun <P : Packet> handlePacket(packetFactory: PacketFactory<P>?, packet: P, commandName: String, sequenceId: Int) {
|
||||
suspend fun <P : Packet?> handlePacket(
|
||||
packetFactory: PacketFactory<P>?,
|
||||
packet: P,
|
||||
commandName: String,
|
||||
sequenceId: Int
|
||||
) {
|
||||
// highest priority: pass to listeners (attached by sendAndExpect).
|
||||
if (packet != null && (bot.logger.isEnabled || logger.isEnabled)) {
|
||||
val logMessage = "Received: ${packet.toString().replace("\n", """\n""").replace("\r", "")}"
|
||||
|
||||
if (packet is Event) {
|
||||
bot.logger.verbose(logMessage)
|
||||
} else logger.verbose(logMessage)
|
||||
}
|
||||
|
||||
packetListeners.forEach { listener ->
|
||||
if (listener.filter(commandName, sequenceId) && packetListeners.remove(listener)) {
|
||||
listener.complete(packet)
|
||||
}
|
||||
}
|
||||
|
||||
// check top-level cancelling
|
||||
if (PacketReceivedEvent(packet).broadcast().isCancelled) {
|
||||
packetFactory?.run {
|
||||
when (this) {
|
||||
is OutgoingPacketFactory<P> -> bot.handle(packet)
|
||||
is IncomingPacketFactory<P> -> bot.handle(packet, sequenceId)?.sendWithoutExpect()
|
||||
}
|
||||
}
|
||||
|
||||
if (packet != null && PacketReceivedEvent(packet).broadcast().isCancelled) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// broadcast
|
||||
if (packet is Event) {
|
||||
if (packet is BroadcastControllable) {
|
||||
if (packet.shouldBroadcast) packet.broadcast()
|
||||
@ -372,15 +463,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
|
||||
if (packet is CancellableEvent && packet.isCancelled) return
|
||||
}
|
||||
|
||||
logger.info("Received: ${packet.toString().replace("\n", """\n""").replace("\r", "")}")
|
||||
|
||||
packetFactory?.run {
|
||||
when (this) {
|
||||
is OutgoingPacketFactory<P> -> bot.handle(packet)
|
||||
is IncomingPacketFactory<P> -> bot.handle(packet, sequenceId)?.sendWithoutExpect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -404,7 +486,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
}
|
||||
// 循环所有完整的包
|
||||
while (rawInput.remaining >= length) {
|
||||
parsePacketAsync(rawInput.readPacket(length))
|
||||
parsePacketAsync(rawInput.readPacketExact(length))
|
||||
|
||||
if (rawInput.remaining == 0L) {
|
||||
cachedPacket.value = null // 表示包长度正好
|
||||
@ -467,63 +549,77 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
* 发送一个包, 但不期待任何返回.
|
||||
*/
|
||||
suspend fun OutgoingPacket.sendWithoutExpect() {
|
||||
logger.info("Send: ${this.commandName}")
|
||||
check(bot.isActive) { "bot is dead therefore can't send any packet" }
|
||||
check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't send any packet" }
|
||||
logger.verbose("Send: ${this.commandName}")
|
||||
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
|
||||
PacketLogger.debug { "Channel sending: $commandName" }
|
||||
channel.send(delegate)
|
||||
PacketLogger.debug { "Channel send done: $commandName" }
|
||||
}
|
||||
}
|
||||
|
||||
class TimeoutException(override val message: String?) : Exception()
|
||||
|
||||
/**
|
||||
* 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms)
|
||||
*
|
||||
* @param retry 当不为 0 时将使用 [ByteArrayPool] 缓存. 因此若非必要, 请不要允许 retry
|
||||
*/
|
||||
suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 3000, retry: Int = 0): E {
|
||||
require(timeoutMillis > 0) { "timeoutMillis must > 0" }
|
||||
require(timeoutMillis > 100) { "timeoutMillis must > 100" }
|
||||
require(retry >= 0) { "retry must >= 0" }
|
||||
|
||||
var lastException: Exception? = null
|
||||
check(bot.isActive) { "bot is dead therefore can't send any packet" }
|
||||
check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't send any packet" }
|
||||
|
||||
suspend fun doSendAndReceive(handler: PacketListener, data: Any, length: Int): E {
|
||||
val result = async {
|
||||
withTimeoutOrNull(3000) {
|
||||
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
|
||||
PacketLogger.debug { "Channel sending: $commandName" }
|
||||
when (data) {
|
||||
is ByteArray -> channel.send(data, 0, length)
|
||||
is ByteReadPacket -> channel.send(data)
|
||||
else -> error("Internal error: unexpected data type: ${data::class.simpleName}")
|
||||
}
|
||||
PacketLogger.debug { "Channel send done: $commandName" }
|
||||
}
|
||||
} ?: return@async "timeout sending packet $commandName"
|
||||
|
||||
logger.verbose("Send done: $commandName")
|
||||
|
||||
withTimeoutOrNull(timeoutMillis) {
|
||||
handler.await()
|
||||
// 不要 `withTimeout`. timeout 的报错会不正常.
|
||||
} ?: return@async "timeout receiving response of $commandName"
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
when (val value = result.await()) {
|
||||
is String -> throw TimeoutException(value)
|
||||
else -> return value as E
|
||||
}
|
||||
}
|
||||
|
||||
if (retry == 0) {
|
||||
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
|
||||
packetListeners.addLast(handler)
|
||||
try {
|
||||
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
|
||||
channel.send(delegate)
|
||||
}
|
||||
logger.info("Send: ${this.commandName}")
|
||||
return withTimeoutOrNull(timeoutMillis) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
handler.await() as E
|
||||
// 不要 `withTimeout`. timeout 的异常会不知道去哪了.
|
||||
} ?: net.mamoe.mirai.qqandroid.utils.inline {
|
||||
error("timeout when receiving response of $commandName")
|
||||
}
|
||||
return doSendAndReceive(handler, delegate, 0) // no need
|
||||
} finally {
|
||||
packetListeners.remove(handler)
|
||||
}
|
||||
} else this.delegate.useBytes { data, length ->
|
||||
repeat(retry + 1) {
|
||||
return tryNTimes(retry + 1) {
|
||||
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
|
||||
packetListeners.addLast(handler)
|
||||
try {
|
||||
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
|
||||
channel.send(data, 0, length)
|
||||
}
|
||||
logger.info("Send: ${this.commandName}")
|
||||
return withTimeoutOrNull(timeoutMillis) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
handler.await() as E
|
||||
// 不要 `withTimeout`. timeout 的异常会不知道去哪了.
|
||||
} ?: net.mamoe.mirai.qqandroid.utils.inline {
|
||||
error("timeout when receiving response of $commandName")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
lastException = e
|
||||
doSendAndReceive(handler, data, length)
|
||||
} finally {
|
||||
packetListeners.remove(handler)
|
||||
}
|
||||
}
|
||||
throw lastException!!
|
||||
}
|
||||
}
|
||||
|
||||
@ -534,8 +630,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
internal inner class PacketListener( // callback
|
||||
val commandName: String,
|
||||
val sequenceId: Int
|
||||
) : CompletableDeferred<Packet> by CompletableDeferred(supervisor) {
|
||||
fun filter(commandName: String, sequenceId: Int) = this.commandName == commandName && this.sequenceId == sequenceId
|
||||
) : CompletableDeferred<Packet?> by CompletableDeferred(supervisor) {
|
||||
fun filter(commandName: String, sequenceId: Int) =
|
||||
this.commandName == commandName && this.sequenceId == sequenceId
|
||||
}
|
||||
|
||||
override fun close(cause: Throwable?) {
|
||||
@ -545,5 +642,5 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
super.close(cause)
|
||||
}
|
||||
|
||||
override suspend fun awaitDisconnection() = supervisor.join()
|
||||
override suspend fun join() = supervisor.join()
|
||||
}
|
@ -7,12 +7,13 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network
|
||||
|
||||
import kotlinx.atomicfu.AtomicInt
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.toByteArray
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.RawAccountIdUse
|
||||
import net.mamoe.mirai.data.OnlineStatus
|
||||
@ -20,12 +21,10 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.Tlv
|
||||
import net.mamoe.mirai.utils.DeviceInfo
|
||||
import net.mamoe.mirai.qqandroid.utils.NetworkType
|
||||
import net.mamoe.mirai.utils.SystemDeviceInfo
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.cryptor.ECDH
|
||||
import net.mamoe.mirai.utils.cryptor.decryptBy
|
||||
import net.mamoe.mirai.utils.cryptor.TEA
|
||||
import net.mamoe.mirai.utils.io.*
|
||||
|
||||
/*
|
||||
@ -41,7 +40,7 @@ import net.mamoe.mirai.utils.io.*
|
||||
DOMAINS
|
||||
Pskey: "openmobile.qq.com"
|
||||
*/
|
||||
@UseExperimental(MiraiExperimentalAPI::class, MiraiInternalAPI::class)
|
||||
@OptIn(MiraiExperimentalAPI::class, MiraiInternalAPI::class)
|
||||
@PublishedApi
|
||||
internal open class QQAndroidClient(
|
||||
context: Context,
|
||||
@ -72,7 +71,7 @@ internal open class QQAndroidClient(
|
||||
internal inline fun <R> tryDecryptOrNull(data: ByteArray, size: Int = data.size, mapper: (ByteArray) -> R): R? {
|
||||
keys.forEach { (key, value) ->
|
||||
kotlin.runCatching {
|
||||
return mapper(data.decryptBy(value, size).also { PacketLogger.verbose { "成功使用 $key 解密" } })
|
||||
return mapper(TEA.decrypt(data, value, size).also { PacketLogger.verbose { "成功使用 $key 解密" } })
|
||||
}
|
||||
}
|
||||
return null
|
||||
@ -101,8 +100,8 @@ internal open class QQAndroidClient(
|
||||
|
||||
var openAppId: Long = 715019303L
|
||||
|
||||
val apkVersionName: ByteArray get() = "8.2.0".toByteArray()
|
||||
val buildVer: String get() = "8.2.0.1296"
|
||||
val apkVersionName: ByteArray get() = "8.2.7".toByteArray()
|
||||
val buildVer: String get() = "8.2.7.4410"
|
||||
|
||||
private val messageSequenceId: AtomicInt = atomic(22911)
|
||||
internal fun atomicNextMessageSequenceId(): Int = messageSequenceId.getAndAdd(2)
|
||||
@ -113,7 +112,7 @@ internal open class QQAndroidClient(
|
||||
private val highwayDataTransSequenceIdForGroup: AtomicInt = atomic(87017)
|
||||
internal fun nextHighwayDataTransSequenceIdForGroup(): Int = highwayDataTransSequenceIdForGroup.getAndAdd(2)
|
||||
|
||||
private val highwayDataTransSequenceIdForFriend: AtomicInt = atomic(40717)
|
||||
private val highwayDataTransSequenceIdForFriend: AtomicInt = atomic(43973)
|
||||
internal fun nextHighwayDataTransSequenceIdForFriend(): Int = highwayDataTransSequenceIdForFriend.getAndAdd(2)
|
||||
|
||||
val appClientVersion: Int = 0
|
||||
@ -123,7 +122,7 @@ internal open class QQAndroidClient(
|
||||
val apkSignatureMd5: ByteArray = "A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D".hexToBytes()
|
||||
|
||||
/**
|
||||
* 协议版本?, 8.2.0 的为 8001
|
||||
* 协议版本?, 8.2.7 的为 8001
|
||||
*/
|
||||
val protocolVersion: Short = 8001
|
||||
|
||||
@ -159,16 +158,18 @@ internal open class QQAndroidClient(
|
||||
*/
|
||||
val uin: Long get() = _uin
|
||||
|
||||
@UseExperimental(RawAccountIdUse::class)
|
||||
@Suppress("PropertyName")
|
||||
@OptIn(RawAccountIdUse::class)
|
||||
@Suppress("PropertyName", "DEPRECATION_ERROR")
|
||||
internal var _uin: Long = bot.account.id
|
||||
|
||||
var t530: ByteArray? = null
|
||||
var t528: ByteArray? = null
|
||||
|
||||
/**
|
||||
* t108 时更新
|
||||
*/
|
||||
var ksid: ByteArray = "|454001228437590|A8.2.0.27f6ea96".toByteArray()
|
||||
var ksid: ByteArray = "|454001228437590|A8.2.7.27f6ea96".toByteArray()
|
||||
|
||||
/**
|
||||
* t186
|
||||
*/
|
||||
@ -190,8 +191,9 @@ internal open class QQAndroidClient(
|
||||
lateinit var t104: ByteArray
|
||||
}
|
||||
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal fun generateTgtgtKey(guid: ByteArray): ByteArray =
|
||||
md5(getRandomByteArray(16) + guid)
|
||||
MiraiPlatformUtils.md5(getRandomByteArray(16) + guid)
|
||||
|
||||
|
||||
internal class ReserveUinInfo(
|
||||
@ -314,6 +316,10 @@ internal class Pt4Token(data: ByteArray, creationTime: Long, expireTime: Long) :
|
||||
internal typealias PSKeyMap = MutableMap<String, PSKey>
|
||||
internal typealias Pt4TokenMap = MutableMap<String, Pt4Token>
|
||||
|
||||
internal inline fun Input.readUShortLVString(): String = kotlinx.io.core.String(this.readUShortLVByteArray())
|
||||
|
||||
internal inline fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt())
|
||||
|
||||
internal fun parsePSKeyMapAndPt4TokenMap(data: ByteArray, creationTime: Long, expireTime: Long, outPSKeyMap: PSKeyMap, outPt4TokenMap: Pt4TokenMap) =
|
||||
data.read {
|
||||
repeat(readShort().toInt()) {
|
||||
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.highway
|
||||
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
object Highway {
|
||||
fun RequestDataTrans(
|
||||
uin: Long,
|
||||
command: String,
|
||||
sequenceId: Int,
|
||||
appId: Int = 537062845,
|
||||
dataFlag: Int = 4096,
|
||||
commandId: Int,
|
||||
localId: Int = 2052,
|
||||
uKey: ByteArray,
|
||||
|
||||
data: Input,
|
||||
dataSize: Int,
|
||||
md5: ByteArray
|
||||
): ByteReadPacket {
|
||||
require(uKey.size == 128) { "bad uKey. Required size=128, got ${uKey.size}" }
|
||||
require(data !is ByteReadPacket || data.remaining.toInt() == dataSize) { "bad input. given dataSize=$dataSize, but actual readRemaining=${(data as ByteReadPacket).remaining}" }
|
||||
require(data !is IoBuffer || data.readRemaining == dataSize) { "bad input. given dataSize=$dataSize, but actual readRemaining=${(data as IoBuffer).readRemaining}" }
|
||||
|
||||
val dataHighwayHead = CSDataHighwayHead.DataHighwayHead(
|
||||
version = 1,
|
||||
uin = uin.toString(),
|
||||
command = command,
|
||||
seq = sequenceId,
|
||||
retryTimes = 0,
|
||||
appid = appId,
|
||||
dataflag = dataFlag,
|
||||
commandId = commandId,
|
||||
localeId = localId
|
||||
)
|
||||
val segHead = CSDataHighwayHead.SegHead(
|
||||
datalength = dataSize,
|
||||
filesize = dataSize.toLong(),
|
||||
serviceticket = uKey,
|
||||
md5 = md5,
|
||||
fileMd5 = md5,
|
||||
flag = 0,
|
||||
rtcode = 0
|
||||
)
|
||||
//println(data.readBytes().toUHexString())
|
||||
return Codec.buildC2SData(dataHighwayHead, segHead, EMPTY_BYTE_ARRAY, null, data, dataSize)
|
||||
}
|
||||
|
||||
private object Codec {
|
||||
fun buildC2SData(
|
||||
dataHighwayHead: CSDataHighwayHead.DataHighwayHead,
|
||||
segHead: CSDataHighwayHead.SegHead,
|
||||
extendInfo: ByteArray,
|
||||
loginSigHead: CSDataHighwayHead.LoginSigHead?,
|
||||
body: Input,
|
||||
bodySize: Int
|
||||
): ByteReadPacket {
|
||||
val head = CSDataHighwayHead.ReqDataHighwayHead(
|
||||
msgBasehead = dataHighwayHead,
|
||||
msgSeghead = segHead,
|
||||
reqExtendinfo = extendInfo,
|
||||
msgLoginSigHead = loginSigHead
|
||||
).toByteArray(CSDataHighwayHead.ReqDataHighwayHead.serializer())
|
||||
|
||||
return buildPacket {
|
||||
writeByte(40)
|
||||
writeInt(head.size)
|
||||
writeInt(bodySize)
|
||||
writeFully(head)
|
||||
check(body.copyTo(this).toInt() == bodySize) { "bad body size" }
|
||||
writeByte(41)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -16,30 +16,36 @@ import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.http.URLProtocol
|
||||
import io.ktor.http.content.OutgoingContent
|
||||
import io.ktor.http.userAgent
|
||||
import io.ktor.utils.io.ByteWriteChannel
|
||||
import kotlinx.coroutines.InternalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import kotlinx.io.InputStream
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.io.core.readAvailable
|
||||
import kotlinx.io.core.use
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.withUse
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.copyAndClose
|
||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||
import net.mamoe.mirai.utils.io.PlatformSocket
|
||||
import net.mamoe.mirai.utils.io.discardExact
|
||||
|
||||
import net.mamoe.mirai.utils.io.withUse
|
||||
import kotlinx.serialization.InternalSerializationApi
|
||||
|
||||
@OptIn(MiraiInternalAPI::class, InternalSerializationApi::class)
|
||||
@Suppress("SpellCheckingInspection")
|
||||
internal suspend inline fun HttpClient.postImage(
|
||||
internal suspend fun HttpClient.postImage(
|
||||
htcmd: String,
|
||||
uin: Long,
|
||||
groupcode: Long?,
|
||||
imageInput: Input,
|
||||
imageInput: Any, // Input from kotlinx.io, InputStream from kotlinx.io MPP, ByteReadChannel from ktor
|
||||
inputSize: Long,
|
||||
uKeyHex: String
|
||||
): Boolean = try {
|
||||
post<HttpStatusCode> {
|
||||
): Boolean = post<HttpStatusCode> {
|
||||
url {
|
||||
protocol = URLProtocol.HTTP
|
||||
host = "htdata2.qq.com"
|
||||
@ -63,54 +69,62 @@ internal suspend inline fun HttpClient.postImage(
|
||||
override val contentType: ContentType = ContentType.Image.Any
|
||||
override val contentLength: Long = inputSize
|
||||
|
||||
override suspend fun writeTo(channel: io.ktor.utils.io.ByteWriteChannel) {
|
||||
override suspend fun writeTo(channel: ByteWriteChannel) {
|
||||
ByteArrayPool.useInstance { buffer: ByteArray ->
|
||||
when (imageInput) {
|
||||
is Input -> {
|
||||
var size: Int
|
||||
while (imageInput.readAvailable(buffer).also { size = it } != 0) {
|
||||
while (imageInput.readAvailable(buffer).also { size = it } > 0) {
|
||||
channel.writeFully(buffer, 0, size)
|
||||
channel.flush()
|
||||
}
|
||||
}
|
||||
is ByteReadChannel -> imageInput.copyAndClose(channel)
|
||||
is InputStream -> {
|
||||
var size: Int
|
||||
while (imageInput.read(buffer).also { size = it } > 0) {
|
||||
channel.writeFully(buffer, 0, size)
|
||||
channel.flush()
|
||||
}
|
||||
}
|
||||
else -> error("unsupported imageInput: ${imageInput::class.simpleName}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} == HttpStatusCode.OK
|
||||
} finally {
|
||||
imageInput.close()
|
||||
}
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class, InternalSerializationApi::class)
|
||||
internal object HighwayHelper {
|
||||
@OptIn(InternalCoroutinesApi::class)
|
||||
suspend fun uploadImage(
|
||||
client: QQAndroidClient,
|
||||
serverIp: String,
|
||||
serverPort: Int,
|
||||
uKey: ByteArray,
|
||||
imageInput: Input,
|
||||
imageInput: Any,
|
||||
inputSize: Int,
|
||||
md5: ByteArray,
|
||||
fileMd5: ByteArray,
|
||||
commandId: Int // group=2, friend=1
|
||||
) {
|
||||
require(md5.size == 16) { "bad md5. Required size=16, got ${md5.size}" }
|
||||
require(imageInput is Input || imageInput is InputStream || imageInput is ByteReadChannel) { "unsupported imageInput: ${imageInput::class.simpleName}" }
|
||||
require(fileMd5.size == 16) { "bad md5. Required size=16, got ${fileMd5.size}" }
|
||||
require(uKey.size == 128) { "bad uKey. Required size=128, got ${uKey.size}" }
|
||||
require(commandId == 2 || commandId == 1) { "bad commandId. Must be 1 or 2" }
|
||||
|
||||
val socket = PlatformSocket()
|
||||
socket.connect(serverIp, serverPort)
|
||||
socket.use {
|
||||
socket.send(
|
||||
Highway.RequestDataTrans(
|
||||
uin = client.uin,
|
||||
createImageDataPacketSequence(
|
||||
client = client,
|
||||
command = "PicUp.DataUp",
|
||||
sequenceId =
|
||||
if (commandId == 2) client.nextHighwayDataTransSequenceIdForGroup()
|
||||
else client.nextHighwayDataTransSequenceIdForFriend(),
|
||||
commandId = commandId,
|
||||
uKey = uKey,
|
||||
data = imageInput,
|
||||
dataSize = inputSize,
|
||||
md5 = md5,
|
||||
commandId = commandId
|
||||
)
|
||||
)
|
||||
|
||||
fileMd5 = fileMd5
|
||||
).collect {
|
||||
socket.send(it)
|
||||
//0A 3C 08 01 12 0A 31 39 39 34 37 30 31 30 32 31 1A 0C 50 69 63 55 70 2E 44 61 74 61 55 70 20 E9 A7 05 28 00 30 BD DB 8B 80 02 38 80 20 40 02 4A 0A 38 2E 32 2E 30 2E 31 32 39 36 50 84 10 12 3D 08 00 10 FD 08 18 00 20 FD 08 28 C6 01 38 00 42 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 4A 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 50 89 92 A2 FB 06 58 00 60 00 18 53 20 01 28 00 30 04 3A 00 40 E6 B7 F7 D9 80 2E 48 00 50 00
|
||||
socket.read().withUse {
|
||||
discardExact(1)
|
||||
@ -122,3 +136,4 @@ internal object HighwayHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.highway
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.io.ByteReadChannel
|
||||
import kotlinx.io.InputStream
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.writeFully
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.io.*
|
||||
import kotlinx.serialization.InternalSerializationApi
|
||||
import net.mamoe.mirai.utils.MiraiPlatformUtils
|
||||
|
||||
@OptIn(MiraiInternalAPI::class, InternalSerializationApi::class)
|
||||
internal fun createImageDataPacketSequence( // RequestDataTrans
|
||||
client: QQAndroidClient,
|
||||
command: String,
|
||||
appId: Int = 537062845,
|
||||
dataFlag: Int = 4096,
|
||||
commandId: Int,
|
||||
localId: Int = 2052,
|
||||
uKey: ByteArray,
|
||||
|
||||
data: Any,
|
||||
dataSize: Int,
|
||||
fileMd5: ByteArray,
|
||||
sizePerPacket: Int = 8192
|
||||
): Flow<ByteReadPacket> {
|
||||
ByteArrayPool.checkBufferSize(sizePerPacket)
|
||||
require(data is Input || data is InputStream || data is ByteReadChannel) { "unsupported data: ${data::class.simpleName}" }
|
||||
require(uKey.size == 128) { "bad uKey. Required size=128, got ${uKey.size}" }
|
||||
require(data !is ByteReadPacket || data.remaining.toInt() == dataSize) { "bad input. given dataSize=$dataSize, but actual readRemaining=${(data as ByteReadPacket).remaining}" }
|
||||
|
||||
val flow = when (data) {
|
||||
is ByteReadPacket -> data.chunkedFlow(sizePerPacket)
|
||||
is Input -> data.chunkedFlow(sizePerPacket)
|
||||
is ByteReadChannel -> data.chunkedFlow(sizePerPacket)
|
||||
is InputStream -> data.chunkedFlow(sizePerPacket)
|
||||
else -> error("unreachable code")
|
||||
}
|
||||
|
||||
var offset = 0L
|
||||
return flow.map { chunkedInput ->
|
||||
buildPacket {
|
||||
val head = CSDataHighwayHead.ReqDataHighwayHead(
|
||||
msgBasehead = CSDataHighwayHead.DataHighwayHead(
|
||||
version = 1,
|
||||
uin = client.uin.toString(),
|
||||
command = command,
|
||||
seq = if (commandId == 2) client.nextHighwayDataTransSequenceIdForGroup()
|
||||
else client.nextHighwayDataTransSequenceIdForFriend(),
|
||||
retryTimes = 0,
|
||||
appid = appId,
|
||||
dataflag = dataFlag,
|
||||
commandId = commandId,
|
||||
localeId = localId
|
||||
),
|
||||
msgSeghead = CSDataHighwayHead.SegHead(
|
||||
// cacheAddr = 812157193,
|
||||
datalength = chunkedInput.bufferSize,
|
||||
dataoffset = offset,
|
||||
filesize = dataSize.toLong(),
|
||||
serviceticket = uKey,
|
||||
md5 = MiraiPlatformUtils.md5(chunkedInput.buffer, 0, chunkedInput.bufferSize),
|
||||
fileMd5 = fileMd5,
|
||||
flag = 0,
|
||||
rtcode = 0
|
||||
),
|
||||
reqExtendinfo = EMPTY_BYTE_ARRAY,
|
||||
msgLoginSigHead = null
|
||||
).toByteArray(CSDataHighwayHead.ReqDataHighwayHead.serializer())
|
||||
|
||||
offset += chunkedInput.bufferSize
|
||||
|
||||
writeByte(40)
|
||||
writeInt(head.size)
|
||||
writeInt(chunkedInput.bufferSize)
|
||||
writeFully(head)
|
||||
writeFully(chunkedInput.buffer, 0, chunkedInput.bufferSize)
|
||||
writeByte(41)
|
||||
}
|
||||
}
|
||||
}
|
@ -9,139 +9,139 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||
|
||||
@Serializable
|
||||
internal class BigDataChannel(
|
||||
@SerialId(0) val vBigdataIplists: List<BigDataIpList>,
|
||||
@SerialId(1) val sBigdataSigSession: ByteArray? = null,
|
||||
@SerialId(2) val sBigdataKeySession: ByteArray? = null,
|
||||
@SerialId(3) val uSigUin: Long? = null,
|
||||
@SerialId(4) val iConnectFlag: Int? = 1,
|
||||
@SerialId(5) val vBigdataPbBuf: ByteArray? = null
|
||||
@JceId(0) val vBigdataIplists: List<BigDataIpList>,
|
||||
@JceId(1) val sBigdataSigSession: ByteArray? = null,
|
||||
@JceId(2) val sBigdataKeySession: ByteArray? = null,
|
||||
@JceId(3) val uSigUin: Long? = null,
|
||||
@JceId(4) val iConnectFlag: Int? = 1,
|
||||
@JceId(5) val vBigdataPbBuf: ByteArray? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class BigDataIpInfo(
|
||||
@SerialId(0) val uType: Long,
|
||||
@SerialId(1) val sIp: String = "",
|
||||
@SerialId(2) val uPort: Long
|
||||
@JceId(0) val uType: Long,
|
||||
@JceId(1) val sIp: String = "",
|
||||
@JceId(2) val uPort: Long
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class BigDataIpList(
|
||||
@SerialId(0) val uServiceType: Long,
|
||||
@SerialId(1) val vIplist: List<BigDataIpInfo>,
|
||||
@SerialId(2) val netSegConfs: List<NetSegConf>? = null,
|
||||
@SerialId(3) val ufragmentSize: Long? = null
|
||||
@JceId(0) val uServiceType: Long,
|
||||
@JceId(1) val vIplist: List<BigDataIpInfo>,
|
||||
@JceId(2) val netSegConfs: List<NetSegConf>? = null,
|
||||
@JceId(3) val ufragmentSize: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class ClientLogConfig(
|
||||
@SerialId(1) val type: Int,
|
||||
@SerialId(2) val timeStart: TimeStamp? = null,
|
||||
@SerialId(3) val timeFinish: TimeStamp? = null,
|
||||
@SerialId(4) val loglevel: Byte? = null,
|
||||
@SerialId(5) val cookie: Int? = null,
|
||||
@SerialId(6) val lseq: Long? = null
|
||||
@JceId(1) val type: Int,
|
||||
@JceId(2) val timeStart: TimeStamp? = null,
|
||||
@JceId(3) val timeFinish: TimeStamp? = null,
|
||||
@JceId(4) val loglevel: Byte? = null,
|
||||
@JceId(5) val cookie: Int? = null,
|
||||
@JceId(6) val lseq: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class DomainIpChannel(
|
||||
@SerialId(0) val vDomainIplists: List<DomainIpList>
|
||||
@JceId(0) val vDomainIplists: List<DomainIpList>
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class DomainIpInfo(
|
||||
@SerialId(1) val uIp: Int,
|
||||
@SerialId(2) val uPort: Int
|
||||
@JceId(1) val uIp: Int,
|
||||
@JceId(2) val uPort: Int
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class DomainIpList(
|
||||
@SerialId(0) val uDomainType: Int,
|
||||
@SerialId(1) val vIplist: List<DomainIpInfo>
|
||||
@JceId(0) val uDomainType: Int,
|
||||
@JceId(1) val vIplist: List<DomainIpInfo>
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class FileStoragePushFSSvcList(
|
||||
@SerialId(0) val vUpLoadList: List<FileStorageServerListInfo>,
|
||||
@SerialId(1) val vPicDownLoadList: List<FileStorageServerListInfo>,
|
||||
@SerialId(2) val vGPicDownLoadList: List<FileStorageServerListInfo>? = null,
|
||||
@SerialId(3) val vQzoneProxyServiceList: List<FileStorageServerListInfo>? = null,
|
||||
@SerialId(4) val vUrlEncodeServiceList: List<FileStorageServerListInfo>? = null,
|
||||
@SerialId(5) val bigDataChannel: BigDataChannel? = null,
|
||||
@SerialId(6) val vVipEmotionList: List<FileStorageServerListInfo>? = null,
|
||||
@SerialId(7) val vC2CPicDownList: List<FileStorageServerListInfo>? = null,
|
||||
@SerialId(8) val fmtIPInfo: FmtIPInfo? = null,
|
||||
@SerialId(9) val domainIpChannel: DomainIpChannel? = null,
|
||||
@SerialId(10) val pttlist: ByteArray? = null
|
||||
@JceId(0) val vUpLoadList: List<FileStorageServerListInfo>,
|
||||
@JceId(1) val vPicDownLoadList: List<FileStorageServerListInfo>,
|
||||
@JceId(2) val vGPicDownLoadList: List<FileStorageServerListInfo>? = null,
|
||||
@JceId(3) val vQzoneProxyServiceList: List<FileStorageServerListInfo>? = null,
|
||||
@JceId(4) val vUrlEncodeServiceList: List<FileStorageServerListInfo>? = null,
|
||||
@JceId(5) val bigDataChannel: BigDataChannel? = null,
|
||||
@JceId(6) val vVipEmotionList: List<FileStorageServerListInfo>? = null,
|
||||
@JceId(7) val vC2CPicDownList: List<FileStorageServerListInfo>? = null,
|
||||
@JceId(8) val fmtIPInfo: FmtIPInfo? = null,
|
||||
@JceId(9) val domainIpChannel: DomainIpChannel? = null,
|
||||
@JceId(10) val pttlist: ByteArray? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class FileStorageServerListInfo(
|
||||
@SerialId(1) val sIP: String = "",
|
||||
@SerialId(2) val iPort: Int
|
||||
@JceId(1) val sIP: String = "",
|
||||
@JceId(2) val iPort: Int
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class FmtIPInfo(
|
||||
@SerialId(0) val sGateIp: String = "",
|
||||
@SerialId(1) val iGateIpOper: Long
|
||||
@JceId(0) val sGateIp: String = "",
|
||||
@JceId(1) val iGateIpOper: Long
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class NetSegConf(
|
||||
@SerialId(0) val uint32NetType: Long? = null,
|
||||
@SerialId(1) val uint32Segsize: Long? = null,
|
||||
@SerialId(2) val uint32Segnum: Long? = null,
|
||||
@SerialId(3) val uint32Curconnnum: Long? = null
|
||||
@JceId(0) val uint32NetType: Long? = null,
|
||||
@JceId(1) val uint32Segsize: Long? = null,
|
||||
@JceId(2) val uint32Segnum: Long? = null,
|
||||
@JceId(3) val uint32Curconnnum: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Suppress("ArrayInDataClass")
|
||||
@Serializable
|
||||
internal data class PushReq(
|
||||
@SerialId(1) val type: Int,
|
||||
@SerialId(2) val jcebuf: ByteArray,
|
||||
@SerialId(3) val seq: Long
|
||||
@JceId(1) val type: Int,
|
||||
@JceId(2) val jcebuf: ByteArray,
|
||||
@JceId(3) val seq: Long
|
||||
) : JceStruct, Packet
|
||||
|
||||
@Serializable
|
||||
internal class PushResp(
|
||||
@SerialId(1) val type: Int,
|
||||
@SerialId(2) val seq: Long,
|
||||
@SerialId(3) val jcebuf: ByteArray? = null
|
||||
@JceId(1) val type: Int,
|
||||
@JceId(2) val seq: Long,
|
||||
@JceId(3) val jcebuf: ByteArray? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class SsoServerList(
|
||||
@SerialId(1) val v2G3GList: List<SsoServerListInfo>,
|
||||
@SerialId(3) val vWifiList: List<SsoServerListInfo>,
|
||||
@SerialId(4) val iReconnect: Int,
|
||||
@SerialId(5) val testSpeed: Byte? = null,
|
||||
@SerialId(6) val useNewList: Byte? = null,
|
||||
@SerialId(7) val iMultiConn: Int? = 1,
|
||||
@SerialId(8) val vHttp2g3glist: List<SsoServerListInfo>? = null,
|
||||
@SerialId(9) val vHttpWifilist: List<SsoServerListInfo>? = null
|
||||
@JceId(1) val v2G3GList: List<SsoServerListInfo>,
|
||||
@JceId(3) val vWifiList: List<SsoServerListInfo>,
|
||||
@JceId(4) val iReconnect: Int,
|
||||
@JceId(5) val testSpeed: Byte? = null,
|
||||
@JceId(6) val useNewList: Byte? = null,
|
||||
@JceId(7) val iMultiConn: Int? = 1,
|
||||
@JceId(8) val vHttp2g3glist: List<SsoServerListInfo>? = null,
|
||||
@JceId(9) val vHttpWifilist: List<SsoServerListInfo>? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class SsoServerListInfo(
|
||||
@SerialId(1) val sIP: String = "",
|
||||
@SerialId(2) val iPort: Int,
|
||||
@SerialId(3) val linkType: Byte,
|
||||
@SerialId(4) val proxy: Byte,
|
||||
@SerialId(5) val protocolType: Byte? = null,
|
||||
@SerialId(6) val iTimeOut: Int? = 10
|
||||
@JceId(1) val sIP: String = "",
|
||||
@JceId(2) val iPort: Int,
|
||||
@JceId(3) val linkType: Byte,
|
||||
@JceId(4) val proxy: Byte,
|
||||
@JceId(5) val protocolType: Byte? = null,
|
||||
@JceId(6) val iTimeOut: Int? = 10
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class TimeStamp(
|
||||
@SerialId(1) val year: Int,
|
||||
@SerialId(2) val month: Byte,
|
||||
@SerialId(3) val day: Byte,
|
||||
@SerialId(4) val hour: Byte
|
||||
@JceId(1) val year: Int,
|
||||
@JceId(2) val month: Byte,
|
||||
@JceId(3) val day: Byte,
|
||||
@JceId(4) val hour: Byte
|
||||
) : JceStruct
|
||||
|
@ -9,172 +9,172 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||
|
||||
@Serializable
|
||||
internal class ModifyGroupCardReq(
|
||||
@SerialId(0) val dwZero: Long,
|
||||
@SerialId(1) val dwGroupCode: Long,
|
||||
@SerialId(2) val dwNewSeq: Long,
|
||||
@SerialId(3) val vecUinInfo: List<stUinInfo>
|
||||
@JceId(0) val dwZero: Long,
|
||||
@JceId(1) val dwGroupCode: Long,
|
||||
@JceId(2) val dwNewSeq: Long,
|
||||
@JceId(3) val vecUinInfo: List<stUinInfo>
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class stUinInfo(
|
||||
@SerialId(0) val dwuin: Long,
|
||||
@SerialId(1) val dwFlag: Long,
|
||||
@SerialId(2) val sName: String = "",
|
||||
@SerialId(3) val gender: Byte,
|
||||
@SerialId(4) val sPhone: String = "",
|
||||
@SerialId(5) val sEmail: String = "",
|
||||
@SerialId(6) val sRemark: String = ""
|
||||
@JceId(0) val dwuin: Long,
|
||||
@JceId(1) val dwFlag: Long,
|
||||
@JceId(2) val sName: String = "",
|
||||
@JceId(3) val gender: Byte,
|
||||
@JceId(4) val sPhone: String = "",
|
||||
@JceId(5) val sEmail: String = "",
|
||||
@JceId(6) val sRemark: String = ""
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class GetFriendListReq(
|
||||
@SerialId(0) val reqtype: Int? = null,
|
||||
@SerialId(1) val ifReflush: Byte? = null,
|
||||
@SerialId(2) val uin: Long? = null,
|
||||
@SerialId(3) val startIndex: Short? = null,
|
||||
@SerialId(4) val getfriendCount: Short? = null,
|
||||
@SerialId(5) val groupid: Byte? = null,
|
||||
@SerialId(6) val ifGetGroupInfo: Byte? = null,
|
||||
@SerialId(7) val groupstartIndex: Byte? = null,
|
||||
@SerialId(8) val getgroupCount: Byte? = null,
|
||||
@SerialId(9) val ifGetMSFGroup: Byte? = null,
|
||||
@SerialId(10) val ifShowTermType: Byte? = null,
|
||||
@SerialId(11) val version: Long? = null,
|
||||
@SerialId(12) val uinList: List<Long>? = null,
|
||||
@SerialId(13) val eAppType: Int = 0,
|
||||
@SerialId(14) val ifGetDOVId: Byte? = null,
|
||||
@SerialId(15) val ifGetBothFlag: Byte? = null,
|
||||
@SerialId(16) val vec0xd50Req: ByteArray? = null,
|
||||
@SerialId(17) val vec0xd6bReq: ByteArray? = null,
|
||||
@SerialId(18) val vecSnsTypelist: List<Long>? = null
|
||||
@JceId(0) val reqtype: Int? = null,
|
||||
@JceId(1) val ifReflush: Byte? = null,
|
||||
@JceId(2) val uin: Long? = null,
|
||||
@JceId(3) val startIndex: Short? = null,
|
||||
@JceId(4) val getfriendCount: Short? = null,
|
||||
@JceId(5) val groupid: Byte? = null,
|
||||
@JceId(6) val ifGetGroupInfo: Byte? = null,
|
||||
@JceId(7) val groupstartIndex: Byte? = null,
|
||||
@JceId(8) val getgroupCount: Byte? = null,
|
||||
@JceId(9) val ifGetMSFGroup: Byte? = null,
|
||||
@JceId(10) val ifShowTermType: Byte? = null,
|
||||
@JceId(11) val version: Long? = null,
|
||||
@JceId(12) val uinList: List<Long>? = null,
|
||||
@JceId(13) val eAppType: Int = 0,
|
||||
@JceId(14) val ifGetDOVId: Byte? = null,
|
||||
@JceId(15) val ifGetBothFlag: Byte? = null,
|
||||
@JceId(16) val vec0xd50Req: ByteArray? = null,
|
||||
@JceId(17) val vec0xd6bReq: ByteArray? = null,
|
||||
@JceId(18) val vecSnsTypelist: List<Long>? = null
|
||||
) : JceStruct
|
||||
|
||||
|
||||
@Serializable
|
||||
internal class GetFriendListResp(
|
||||
@SerialId(0) val reqtype: Int,
|
||||
@SerialId(1) val ifReflush: Byte,
|
||||
@SerialId(2) val uin: Long,
|
||||
@SerialId(3) val startIndex: Short,
|
||||
@SerialId(4) val getfriendCount: Short,
|
||||
@SerialId(5) val totoalFriendCount: Short,
|
||||
@SerialId(6) val friendCount: Short,
|
||||
@SerialId(7) val vecFriendInfo: List<FriendInfo>? = null,
|
||||
@SerialId(8) val groupid: Byte? = null,
|
||||
@SerialId(9) val ifGetGroupInfo: Byte,
|
||||
@SerialId(10) val groupstartIndex: Byte? = null,
|
||||
@SerialId(11) val getgroupCount: Byte? = null,
|
||||
@SerialId(12) val totoalGroupCount: Short? = null,
|
||||
@SerialId(13) val groupCount: Byte? = null,
|
||||
@SerialId(14) val vecGroupInfo: List<GroupInfo>? = null,
|
||||
@SerialId(15) val result: Int,
|
||||
@SerialId(16) val errorCode: Short? = null,
|
||||
@SerialId(17) val onlineFriendCount: Short? = null,
|
||||
@SerialId(18) val serverTime: Long? = null,
|
||||
@SerialId(19) val sqqOnLineCount: Short? = null,
|
||||
@SerialId(20) val vecMSFGroupInfo: List<GroupInfo>? = null,
|
||||
@SerialId(21) val respType: Byte? = null,
|
||||
@SerialId(22) val hasOtherRespFlag: Byte? = null,
|
||||
@SerialId(23) val stSelfInfo: FriendInfo? = null,
|
||||
@SerialId(24) val showPcIcon: Byte? = null,
|
||||
@SerialId(25) val wGetExtSnsRspCode: Short? = null,
|
||||
@SerialId(26) val stSubSrvRspCode: FriendListSubSrvRspCode? = null
|
||||
@JceId(0) val reqtype: Int,
|
||||
@JceId(1) val ifReflush: Byte,
|
||||
@JceId(2) val uin: Long,
|
||||
@JceId(3) val startIndex: Short,
|
||||
@JceId(4) val getfriendCount: Short,
|
||||
@JceId(5) val totoalFriendCount: Short,
|
||||
@JceId(6) val friendCount: Short,
|
||||
@JceId(7) val vecFriendInfo: List<FriendInfo>? = null,
|
||||
@JceId(8) val groupid: Byte? = null,
|
||||
@JceId(9) val ifGetGroupInfo: Byte,
|
||||
@JceId(10) val groupstartIndex: Byte? = null,
|
||||
@JceId(11) val getgroupCount: Byte? = null,
|
||||
@JceId(12) val totoalGroupCount: Short? = null,
|
||||
@JceId(13) val groupCount: Byte? = null,
|
||||
@JceId(14) val vecGroupInfo: List<GroupInfo>? = null,
|
||||
@JceId(15) val result: Int,
|
||||
@JceId(16) val errorCode: Short? = null,
|
||||
@JceId(17) val onlineFriendCount: Short? = null,
|
||||
@JceId(18) val serverTime: Long? = null,
|
||||
@JceId(19) val sqqOnLineCount: Short? = null,
|
||||
@JceId(20) val vecMSFGroupInfo: List<GroupInfo>? = null,
|
||||
@JceId(21) val respType: Byte? = null,
|
||||
@JceId(22) val hasOtherRespFlag: Byte? = null,
|
||||
@JceId(23) val stSelfInfo: FriendInfo? = null,
|
||||
@JceId(24) val showPcIcon: Byte? = null,
|
||||
@JceId(25) val wGetExtSnsRspCode: Short? = null,
|
||||
@JceId(26) val stSubSrvRspCode: FriendListSubSrvRspCode? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class FriendListSubSrvRspCode(
|
||||
@SerialId(0) val wGetMutualMarkRspCode: Short? = null,
|
||||
@SerialId(1) val wGetIntimateInfoRspCode: Short? = null
|
||||
@JceId(0) val wGetMutualMarkRspCode: Short? = null,
|
||||
@JceId(1) val wGetIntimateInfoRspCode: Short? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class FriendInfo(
|
||||
@SerialId(0) val friendUin: Long,
|
||||
@SerialId(1) val groupId: Byte,
|
||||
@SerialId(2) val faceId: Short,
|
||||
@SerialId(3) val remark: String = "",
|
||||
@SerialId(4) val sqqtype: Byte,
|
||||
@SerialId(5) val status: Byte = 20,
|
||||
@SerialId(6) val memberLevel: Byte? = null,
|
||||
@SerialId(7) val isMqqOnLine: Byte? = null,
|
||||
@SerialId(8) val sqqOnLineState: Byte? = null,
|
||||
@SerialId(9) val isIphoneOnline: Byte? = null,
|
||||
@SerialId(10) val detalStatusFlag: Byte? = null,
|
||||
@SerialId(11) val sqqOnLineStateV2: Byte? = null,
|
||||
@SerialId(12) val sShowName: String? = "",
|
||||
@SerialId(13) val isRemark: Byte? = null,
|
||||
@SerialId(14) val nick: String? = "",
|
||||
@SerialId(15) val specialFlag: Byte? = null,
|
||||
@SerialId(16) val vecIMGroupID: ByteArray? = null,
|
||||
@SerialId(17) val vecMSFGroupID: ByteArray? = null,
|
||||
@SerialId(18) val iTermType: Int? = null,
|
||||
@SerialId(19) val oVipInfo: VipBaseInfo? = null,
|
||||
@SerialId(20) val network: Byte? = null,
|
||||
@SerialId(21) val vecRing: ByteArray? = null,
|
||||
@SerialId(22) val uAbiFlag: Long? = null,
|
||||
@SerialId(23) val ulFaceAddonId: Long? = null,
|
||||
@SerialId(24) val eNetworkType: Int? = 0,
|
||||
@SerialId(25) val uVipFont: Long? = null,
|
||||
@SerialId(26) val eIconType: Int? = 0,
|
||||
@SerialId(27) val termDesc: String? = "",
|
||||
@SerialId(28) val uColorRing: Long? = null,
|
||||
@SerialId(29) val apolloFlag: Byte? = null,
|
||||
@SerialId(30) val uApolloTimestamp: Long? = null,
|
||||
@SerialId(31) val sex: Byte? = null,
|
||||
@SerialId(32) val uFounderFont: Long? = null,
|
||||
@SerialId(33) val eimId: String? = "",
|
||||
@SerialId(34) val eimMobile: String? = "",
|
||||
@SerialId(35) val olympicTorch: Byte? = null,
|
||||
@SerialId(36) val uApolloSignTime: Long? = null,
|
||||
@SerialId(37) val uLaviUin: Long? = null,
|
||||
@SerialId(38) val uTagUpdateTime: Long? = null,
|
||||
@SerialId(39) val uGameLastLoginTime: Long? = null,
|
||||
@SerialId(40) val uGameAppid: Long? = null,
|
||||
@SerialId(41) val vecCardID: ByteArray? = null,
|
||||
@SerialId(42) val ulBitSet: Long? = null,
|
||||
@SerialId(43) val kingOfGloryFlag: Byte? = null,
|
||||
@SerialId(44) val ulKingOfGloryRank: Long? = null,
|
||||
@SerialId(45) val masterUin: String? = "",
|
||||
@SerialId(46) val uLastMedalUpdateTime: Long? = null,
|
||||
@SerialId(47) val uFaceStoreId: Long? = null,
|
||||
@SerialId(48) val uFontEffect: Long? = null,
|
||||
@SerialId(49) val sDOVId: String? = "",
|
||||
@SerialId(50) val uBothFlag: Long? = null,
|
||||
@SerialId(51) val centiShow3DFlag: Byte? = null,
|
||||
@SerialId(52) val vecIntimateInfo: ByteArray? = null,
|
||||
@SerialId(53) val showNameplate: Byte? = null,
|
||||
@SerialId(54) val newLoverDiamondFlag: Byte? = null,
|
||||
@SerialId(55) val vecExtSnsFrdData: ByteArray? = null,
|
||||
@SerialId(56) val vecMutualMarkData: ByteArray? = null
|
||||
@JceId(0) val friendUin: Long,
|
||||
@JceId(1) val groupId: Byte,
|
||||
@JceId(2) val faceId: Short,
|
||||
@JceId(3) val remark: String = "",
|
||||
@JceId(4) val sqqtype: Byte,
|
||||
@JceId(5) val status: Byte = 20,
|
||||
@JceId(6) val memberLevel: Byte? = null,
|
||||
@JceId(7) val isMqqOnLine: Byte? = null,
|
||||
@JceId(8) val sqqOnLineState: Byte? = null,
|
||||
@JceId(9) val isIphoneOnline: Byte? = null,
|
||||
@JceId(10) val detalStatusFlag: Byte? = null,
|
||||
@JceId(11) val sqqOnLineStateV2: Byte? = null,
|
||||
@JceId(12) val sShowName: String? = "",
|
||||
@JceId(13) val isRemark: Byte? = null,
|
||||
@JceId(14) val nick: String? = "",
|
||||
@JceId(15) val specialFlag: Byte? = null,
|
||||
@JceId(16) val vecIMGroupID: ByteArray? = null,
|
||||
@JceId(17) val vecMSFGroupID: ByteArray? = null,
|
||||
@JceId(18) val iTermType: Int? = null,
|
||||
@JceId(19) val oVipInfo: VipBaseInfo? = null,
|
||||
@JceId(20) val network: Byte? = null,
|
||||
@JceId(21) val vecRing: ByteArray? = null,
|
||||
@JceId(22) val uAbiFlag: Long? = null,
|
||||
@JceId(23) val ulFaceAddonId: Long? = null,
|
||||
@JceId(24) val eNetworkType: Int? = 0,
|
||||
@JceId(25) val uVipFont: Long? = null,
|
||||
@JceId(26) val eIconType: Int? = 0,
|
||||
@JceId(27) val termDesc: String? = "",
|
||||
@JceId(28) val uColorRing: Long? = null,
|
||||
@JceId(29) val apolloFlag: Byte? = null,
|
||||
@JceId(30) val uApolloTimestamp: Long? = null,
|
||||
@JceId(31) val sex: Byte? = null,
|
||||
@JceId(32) val uFounderFont: Long? = null,
|
||||
@JceId(33) val eimId: String? = "",
|
||||
@JceId(34) val eimMobile: String? = "",
|
||||
@JceId(35) val olympicTorch: Byte? = null,
|
||||
@JceId(36) val uApolloSignTime: Long? = null,
|
||||
@JceId(37) val uLaviUin: Long? = null,
|
||||
@JceId(38) val uTagUpdateTime: Long? = null,
|
||||
@JceId(39) val uGameLastLoginTime: Long? = null,
|
||||
@JceId(40) val uGameAppid: Long? = null,
|
||||
@JceId(41) val vecCardID: ByteArray? = null,
|
||||
@JceId(42) val ulBitSet: Long? = null,
|
||||
@JceId(43) val kingOfGloryFlag: Byte? = null,
|
||||
@JceId(44) val ulKingOfGloryRank: Long? = null,
|
||||
@JceId(45) val masterUin: String? = "",
|
||||
@JceId(46) val uLastMedalUpdateTime: Long? = null,
|
||||
@JceId(47) val uFaceStoreId: Long? = null,
|
||||
@JceId(48) val uFontEffect: Long? = null,
|
||||
@JceId(49) val sDOVId: String? = "",
|
||||
@JceId(50) val uBothFlag: Long? = null,
|
||||
@JceId(51) val centiShow3DFlag: Byte? = null,
|
||||
@JceId(52) val vecIntimateInfo: ByteArray? = null,
|
||||
@JceId(53) val showNameplate: Byte? = null,
|
||||
@JceId(54) val newLoverDiamondFlag: Byte? = null,
|
||||
@JceId(55) val vecExtSnsFrdData: ByteArray? = null,
|
||||
@JceId(56) val vecMutualMarkData: ByteArray? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class VipBaseInfo(
|
||||
@SerialId(0) val mOpenInfo: Map<Int, VipOpenInfo>
|
||||
@JceId(0) val mOpenInfo: Map<Int, VipOpenInfo>
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class VipOpenInfo(
|
||||
@SerialId(0) val open: Boolean,
|
||||
@SerialId(1) val iVipType: Int = -1,
|
||||
@SerialId(2) val iVipLevel: Int = -1,
|
||||
@SerialId(3) val iVipFlag: Int? = null,
|
||||
@SerialId(4) val nameplateId: Long? = null
|
||||
@JceId(0) val open: Boolean,
|
||||
@JceId(1) val iVipType: Int = -1,
|
||||
@JceId(2) val iVipLevel: Int = -1,
|
||||
@JceId(3) val iVipFlag: Int? = null,
|
||||
@JceId(4) val nameplateId: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class GroupInfo(
|
||||
@SerialId(0) val groupId: Byte,
|
||||
@SerialId(1) val groupname: String = "",
|
||||
@SerialId(2) val friendCount: Int,
|
||||
@SerialId(3) val onlineFriendCount: Int,
|
||||
@SerialId(4) val seqid: Byte? = null,
|
||||
@SerialId(5) val sqqOnLineCount: Int? = null
|
||||
@JceId(0) val groupId: Byte,
|
||||
@JceId(1) val groupname: String = "",
|
||||
@JceId(2) val friendCount: Int,
|
||||
@JceId(3) val onlineFriendCount: Int,
|
||||
@JceId(4) val seqid: Byte? = null,
|
||||
@JceId(5) val sqqOnLineCount: Int? = null
|
||||
) : JceStruct
|
||||
|
||||
|
@ -9,250 +9,250 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||
|
||||
internal class OnlinePushPack {
|
||||
@Serializable
|
||||
internal class DelMsgInfo(
|
||||
@SerialId(0) val fromUin: Long,
|
||||
@SerialId(1) val uMsgTime: Long,
|
||||
@SerialId(2) val shMsgSeq: Short,
|
||||
@SerialId(3) val vMsgCookies: ByteArray? = null,
|
||||
@SerialId(4) val wCmd: Short? = null,
|
||||
@SerialId(5) val uMsgType: Long? = null,
|
||||
@SerialId(6) val uAppId: Long? = null,
|
||||
@SerialId(7) val sendTime: Long? = null,
|
||||
@SerialId(8) val ssoSeq: Int? = null,
|
||||
@SerialId(9) val ssoIp: Int? = null,
|
||||
@SerialId(10) val clientIp: Int? = null
|
||||
@JceId(0) val fromUin: Long,
|
||||
@JceId(1) val uMsgTime: Long,
|
||||
@JceId(2) val shMsgSeq: Short,
|
||||
@JceId(3) val vMsgCookies: ByteArray? = null,
|
||||
@JceId(4) val wCmd: Short? = null,
|
||||
@JceId(5) val uMsgType: Long? = null,
|
||||
@JceId(6) val uAppId: Long? = null,
|
||||
@JceId(7) val sendTime: Long? = null,
|
||||
@JceId(8) val ssoSeq: Int? = null,
|
||||
@JceId(9) val ssoIp: Int? = null,
|
||||
@JceId(10) val clientIp: Int? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class DeviceInfo(
|
||||
@SerialId(0) val netType: Byte? = null,
|
||||
@SerialId(1) val devType: String? = "",
|
||||
@SerialId(2) val oSVer: String? = "",
|
||||
@SerialId(3) val vendorName: String? = "",
|
||||
@SerialId(4) val vendorOSName: String? = "",
|
||||
@SerialId(5) val iOSIdfa: String? = ""
|
||||
@JceId(0) val netType: Byte? = null,
|
||||
@JceId(1) val devType: String? = "",
|
||||
@JceId(2) val oSVer: String? = "",
|
||||
@JceId(3) val vendorName: String? = "",
|
||||
@JceId(4) val vendorOSName: String? = "",
|
||||
@JceId(5) val iOSIdfa: String? = ""
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class Name(
|
||||
@SerialId(0) val fromUin: Long,
|
||||
@SerialId(1) val uMsgTime: Long,
|
||||
@SerialId(2) val shMsgType: Short,
|
||||
@SerialId(3) val shMsgSeq: Short,
|
||||
@SerialId(4) val msg: String = "",
|
||||
@SerialId(5) val uRealMsgTime: Int? = null,
|
||||
@SerialId(6) val vMsg: ByteArray? = null,
|
||||
@SerialId(7) val uAppShareID: Long? = null,
|
||||
@SerialId(8) val vMsgCookies: ByteArray? = null,
|
||||
@SerialId(9) val vAppShareCookie: ByteArray? = null,
|
||||
@SerialId(10) val msgUid: Long? = null,
|
||||
@SerialId(11) val lastChangeTime: Long? = 1L,
|
||||
@SerialId(12) val vCPicInfo: List<CPicInfo>? = null,
|
||||
@SerialId(13) val stShareData: ShareData? = null,
|
||||
@SerialId(14) val fromInstId: Long? = null,
|
||||
@SerialId(15) val vRemarkOfSender: ByteArray? = null,
|
||||
@SerialId(16) val fromMobile: String? = "",
|
||||
@SerialId(17) val fromName: String? = "",
|
||||
@SerialId(18) val vNickName: List<String>? = null,
|
||||
@SerialId(19) val stC2CTmpMsgHead: TempMsgHead? = null
|
||||
@JceId(0) val fromUin: Long,
|
||||
@JceId(1) val uMsgTime: Long,
|
||||
@JceId(2) val shMsgType: Short,
|
||||
@JceId(3) val shMsgSeq: Short,
|
||||
@JceId(4) val msg: String = "",
|
||||
@JceId(5) val uRealMsgTime: Int? = null,
|
||||
@JceId(6) val vMsg: ByteArray? = null,
|
||||
@JceId(7) val uAppShareID: Long? = null,
|
||||
@JceId(8) val vMsgCookies: ByteArray? = null,
|
||||
@JceId(9) val vAppShareCookie: ByteArray? = null,
|
||||
@JceId(10) val msgUid: Long? = null,
|
||||
@JceId(11) val lastChangeTime: Long? = 1L,
|
||||
@JceId(12) val vCPicInfo: List<CPicInfo>? = null,
|
||||
@JceId(13) val stShareData: ShareData? = null,
|
||||
@JceId(14) val fromInstId: Long? = null,
|
||||
@JceId(15) val vRemarkOfSender: ByteArray? = null,
|
||||
@JceId(16) val fromMobile: String? = "",
|
||||
@JceId(17) val fromName: String? = "",
|
||||
@JceId(18) val vNickName: List<String>? = null,
|
||||
@JceId(19) val stC2CTmpMsgHead: TempMsgHead? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class SvcReqPushMsg(
|
||||
@SerialId(0) val uin: Long,
|
||||
@SerialId(1) val uMsgTime: Long,
|
||||
@SerialId(2) val vMsgInfos: List<MsgInfo>,
|
||||
@SerialId(3) val svrip: Int? = 0,
|
||||
@SerialId(4) val vSyncCookie: ByteArray? = null,
|
||||
@SerialId(5) val vUinPairMsg: List<UinPairMsg>? = null,
|
||||
@SerialId(6) val mPreviews: Map<String, ByteArray>? = null
|
||||
@JceId(0) val uin: Long,
|
||||
@JceId(1) val uMsgTime: Long,
|
||||
@JceId(2) val vMsgInfos: List<MsgInfo>,
|
||||
@JceId(3) val svrip: Int? = 0,
|
||||
@JceId(4) val vSyncCookie: ByteArray? = null,
|
||||
@JceId(5) val vUinPairMsg: List<UinPairMsg>? = null,
|
||||
@JceId(6) val mPreviews: Map<String, ByteArray>? = null
|
||||
// @SerialId(7) val wUserActive: Int? = null,
|
||||
//@SerialId(12) val wGeneralFlag: Int? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class SvcRespPushMsg(
|
||||
@SerialId(0) val uin: Long,
|
||||
@SerialId(1) val vDelInfos: List<DelMsgInfo>,
|
||||
@SerialId(2) val svrip: Int,
|
||||
@SerialId(3) val pushToken: ByteArray? = null,
|
||||
@SerialId(4) val serviceType: Int? = null,
|
||||
@SerialId(5) val deviceInfo: DeviceInfo? = null
|
||||
@JceId(0) val uin: Long,
|
||||
@JceId(1) val vDelInfos: List<DelMsgInfo>,
|
||||
@JceId(2) val svrip: Int,
|
||||
@JceId(3) val pushToken: ByteArray? = null,
|
||||
@JceId(4) val serviceType: Int? = null,
|
||||
@JceId(5) val deviceInfo: DeviceInfo? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class UinPairMsg(
|
||||
@SerialId(1) val uLastReadTime: Long? = null,
|
||||
@SerialId(2) val peerUin: Long? = null,
|
||||
@SerialId(3) val uMsgCompleted: Long? = null,
|
||||
@SerialId(4) val vMsgInfos: List<MsgInfo>? = null
|
||||
@JceId(1) val uLastReadTime: Long? = null,
|
||||
@JceId(2) val peerUin: Long? = null,
|
||||
@JceId(3) val uMsgCompleted: Long? = null,
|
||||
@JceId(4) val vMsgInfos: List<MsgInfo>? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class MsgType0x210(
|
||||
@SerialId(0) val uSubMsgType: Long,
|
||||
@SerialId(1) val stMsgInfo0x2: MsgType0x210SubMsgType0x2? = null,
|
||||
@SerialId(3) val stMsgInfo0xa: MsgType0x210SubMsgType0xa? = null,
|
||||
@SerialId(4) val stMsgInfo0xe: MsgType0x210SubMsgType0xe? = null,
|
||||
@SerialId(5) val stMsgInfo0x13: MsgType0x210SubMsgType0x13? = null,
|
||||
@SerialId(6) val stMsgInfo0x17: MsgType0x210SubMsgType0x17? = null,
|
||||
@SerialId(7) val stMsgInfo0x20: MsgType0x210SubMsgType0x20? = null,
|
||||
@SerialId(8) val stMsgInfo0x1d: MsgType0x210SubMsgType0x1d? = null,
|
||||
@SerialId(9) val stMsgInfo0x24: MsgType0x210SubMsgType0x24? = null,
|
||||
@SerialId(10) val vProtobuf: ByteArray? = null
|
||||
@JceId(0) val uSubMsgType: Long,
|
||||
@JceId(1) val stMsgInfo0x2: MsgType0x210SubMsgType0x2? = null,
|
||||
@JceId(3) val stMsgInfo0xa: MsgType0x210SubMsgType0xa? = null,
|
||||
@JceId(4) val stMsgInfo0xe: MsgType0x210SubMsgType0xe? = null,
|
||||
@JceId(5) val stMsgInfo0x13: MsgType0x210SubMsgType0x13? = null,
|
||||
@JceId(6) val stMsgInfo0x17: MsgType0x210SubMsgType0x17? = null,
|
||||
@JceId(7) val stMsgInfo0x20: MsgType0x210SubMsgType0x20? = null,
|
||||
@JceId(8) val stMsgInfo0x1d: MsgType0x210SubMsgType0x1d? = null,
|
||||
@JceId(9) val stMsgInfo0x24: MsgType0x210SubMsgType0x24? = null,
|
||||
@JceId(10) val vProtobuf: ByteArray? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class MsgType0x210SubMsgType0x13(
|
||||
@SerialId(0) val uint32SrcAppId: Long? = null,
|
||||
@SerialId(1) val uint32SrcInstId: Long? = null,
|
||||
@SerialId(2) val uint32DstAppId: Long? = null,
|
||||
@SerialId(3) val uint32DstInstId: Long? = null,
|
||||
@SerialId(4) val uint64DstUin: Long? = null,
|
||||
@SerialId(5) val uint64Sessionid: Long? = null,
|
||||
@SerialId(6) val uint32Size: Long? = null,
|
||||
@SerialId(7) val uint32Index: Long? = null,
|
||||
@SerialId(8) val uint32Type: Long? = null,
|
||||
@SerialId(9) val buf: ByteArray? = null
|
||||
@JceId(0) val uint32SrcAppId: Long? = null,
|
||||
@JceId(1) val uint32SrcInstId: Long? = null,
|
||||
@JceId(2) val uint32DstAppId: Long? = null,
|
||||
@JceId(3) val uint32DstInstId: Long? = null,
|
||||
@JceId(4) val uint64DstUin: Long? = null,
|
||||
@JceId(5) val uint64Sessionid: Long? = null,
|
||||
@JceId(6) val uint32Size: Long? = null,
|
||||
@JceId(7) val uint32Index: Long? = null,
|
||||
@JceId(8) val uint32Type: Long? = null,
|
||||
@JceId(9) val buf: ByteArray? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class MsgType0x210SubMsgType0x17(
|
||||
@SerialId(0) val dwOpType: Long? = null,
|
||||
@SerialId(1) val stAddGroup: AddGroup? = null,
|
||||
@SerialId(2) val stDelGroup: DelGroup? = null,
|
||||
@SerialId(3) val stModGroupName: ModGroupName? = null,
|
||||
@SerialId(4) val stModGroupSort: ModGroupSort? = null,
|
||||
@SerialId(5) val stModFriendGroup: ModFriendGroup? = null
|
||||
@JceId(0) val dwOpType: Long? = null,
|
||||
@JceId(1) val stAddGroup: AddGroup? = null,
|
||||
@JceId(2) val stDelGroup: DelGroup? = null,
|
||||
@JceId(3) val stModGroupName: ModGroupName? = null,
|
||||
@JceId(4) val stModGroupSort: ModGroupSort? = null,
|
||||
@JceId(5) val stModFriendGroup: ModFriendGroup? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class AddGroup(
|
||||
@SerialId(0) val dwGroupID: Long? = null,
|
||||
@SerialId(1) val dwSortID: Long? = null,
|
||||
@SerialId(2) val groupName: String? = ""
|
||||
@JceId(0) val dwGroupID: Long? = null,
|
||||
@JceId(1) val dwSortID: Long? = null,
|
||||
@JceId(2) val groupName: String? = ""
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class DelGroup(
|
||||
@SerialId(0) val dwGroupID: Long? = null
|
||||
@JceId(0) val dwGroupID: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class ModFriendGroup(
|
||||
@SerialId(0) val vMsgFrdGroup: List<FriendGroup>? = null
|
||||
@JceId(0) val vMsgFrdGroup: List<FriendGroup>? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class FriendGroup(
|
||||
@SerialId(0) val dwFuin: Long? = null,
|
||||
@SerialId(1) val vOldGroupID: List<Long>? = null,
|
||||
@SerialId(2) val vNewGroupID: List<Long>? = null
|
||||
@JceId(0) val dwFuin: Long? = null,
|
||||
@JceId(1) val vOldGroupID: List<Long>? = null,
|
||||
@JceId(2) val vNewGroupID: List<Long>? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class ModGroupName(
|
||||
@SerialId(0) val dwGroupID: Long? = null,
|
||||
@SerialId(1) val groupName: String? = ""
|
||||
@JceId(0) val dwGroupID: Long? = null,
|
||||
@JceId(1) val groupName: String? = ""
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class ModGroupSort(
|
||||
@SerialId(0) val vMsgGroupSort: List<GroupSort>? = null
|
||||
@JceId(0) val vMsgGroupSort: List<GroupSort>? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class GroupSort(
|
||||
@SerialId(0) val dwGroupID: Long? = null,
|
||||
@SerialId(1) val dwSortID: Long? = null
|
||||
@JceId(0) val dwGroupID: Long? = null,
|
||||
@JceId(1) val dwSortID: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class MsgType0x210SubMsgType0x1d(
|
||||
@SerialId(0) val dwOpType: Long? = null,
|
||||
@SerialId(1) val dwUin: Long? = null,
|
||||
@SerialId(2) val dwID: Long? = null,
|
||||
@SerialId(3) val value: String? = ""
|
||||
@JceId(0) val dwOpType: Long? = null,
|
||||
@JceId(1) val dwUin: Long? = null,
|
||||
@JceId(2) val dwID: Long? = null,
|
||||
@JceId(3) val value: String? = ""
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class MsgType0x210SubMsgType0x2(
|
||||
@SerialId(0) val uSrcAppId: Long? = null,
|
||||
@SerialId(1) val uSrcInstId: Long? = null,
|
||||
@SerialId(2) val uDstAppId: Long? = null,
|
||||
@SerialId(3) val uDstInstId: Long? = null,
|
||||
@SerialId(4) val uDstUin: Long? = null,
|
||||
@SerialId(5) val fileName: ByteArray? = null,
|
||||
@SerialId(6) val fileIndex: ByteArray? = null,
|
||||
@SerialId(7) val fileMd5: ByteArray? = null,
|
||||
@SerialId(8) val fileKey: ByteArray? = null,
|
||||
@SerialId(9) val uServerIp: Long? = null,
|
||||
@SerialId(10) val uServerPort: Long? = null,
|
||||
@SerialId(11) val fileLen: Long? = null,
|
||||
@SerialId(12) val sessionId: Long? = null,
|
||||
@SerialId(13) val originfileMd5: ByteArray? = null,
|
||||
@SerialId(14) val uOriginfiletype: Long? = null,
|
||||
@SerialId(15) val uSeq: Long? = null
|
||||
@JceId(0) val uSrcAppId: Long? = null,
|
||||
@JceId(1) val uSrcInstId: Long? = null,
|
||||
@JceId(2) val uDstAppId: Long? = null,
|
||||
@JceId(3) val uDstInstId: Long? = null,
|
||||
@JceId(4) val uDstUin: Long? = null,
|
||||
@JceId(5) val fileName: ByteArray? = null,
|
||||
@JceId(6) val fileIndex: ByteArray? = null,
|
||||
@JceId(7) val fileMd5: ByteArray? = null,
|
||||
@JceId(8) val fileKey: ByteArray? = null,
|
||||
@JceId(9) val uServerIp: Long? = null,
|
||||
@JceId(10) val uServerPort: Long? = null,
|
||||
@JceId(11) val fileLen: Long? = null,
|
||||
@JceId(12) val sessionId: Long? = null,
|
||||
@JceId(13) val originfileMd5: ByteArray? = null,
|
||||
@JceId(14) val uOriginfiletype: Long? = null,
|
||||
@JceId(15) val uSeq: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class MsgType0x210SubMsgType0x20(
|
||||
@SerialId(0) val dwOpType: Long? = null,
|
||||
@SerialId(1) val dwType: Long? = null,
|
||||
@SerialId(2) val dwUin: Long? = null,
|
||||
@SerialId(3) val remaek: String? = ""
|
||||
@JceId(0) val dwOpType: Long? = null,
|
||||
@JceId(1) val dwType: Long? = null,
|
||||
@JceId(2) val dwUin: Long? = null,
|
||||
@JceId(3) val remaek: String? = ""
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class MsgType0x210SubMsgType0x24(
|
||||
@SerialId(0) val vPluginNumList: List<PluginNum>? = null
|
||||
@JceId(0) val vPluginNumList: List<PluginNum>? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class PluginNum(
|
||||
@SerialId(0) val dwID: Long? = null,
|
||||
@SerialId(1) val dwNUm: Long? = null,
|
||||
@SerialId(2) val flag: Byte? = null
|
||||
@JceId(0) val dwID: Long? = null,
|
||||
@JceId(1) val dwNUm: Long? = null,
|
||||
@JceId(2) val flag: Byte? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class MsgType0x210SubMsgType0xa(
|
||||
@SerialId(0) val uSrcAppId: Long? = null,
|
||||
@SerialId(1) val uSrcInstId: Long? = null,
|
||||
@SerialId(2) val uDstAppId: Long? = null,
|
||||
@SerialId(3) val uDstInstId: Long? = null,
|
||||
@SerialId(4) val uDstUin: Long? = null,
|
||||
@SerialId(5) val uType: Long? = null,
|
||||
@SerialId(6) val uServerIp: Long? = null,
|
||||
@SerialId(7) val uServerPort: Long? = null,
|
||||
@SerialId(8) val vUrlNotify: ByteArray? = null,
|
||||
@SerialId(9) val vTokenKey: ByteArray? = null,
|
||||
@SerialId(10) val uFileLen: Long? = null,
|
||||
@SerialId(11) val fileName: ByteArray? = null,
|
||||
@SerialId(12) val vMd5: ByteArray? = null,
|
||||
@SerialId(13) val sessionId: Long? = null,
|
||||
@SerialId(14) val originfileMd5: ByteArray? = null,
|
||||
@SerialId(15) val uOriginfiletype: Long? = null,
|
||||
@SerialId(16) val uSeq: Long? = null
|
||||
@JceId(0) val uSrcAppId: Long? = null,
|
||||
@JceId(1) val uSrcInstId: Long? = null,
|
||||
@JceId(2) val uDstAppId: Long? = null,
|
||||
@JceId(3) val uDstInstId: Long? = null,
|
||||
@JceId(4) val uDstUin: Long? = null,
|
||||
@JceId(5) val uType: Long? = null,
|
||||
@JceId(6) val uServerIp: Long? = null,
|
||||
@JceId(7) val uServerPort: Long? = null,
|
||||
@JceId(8) val vUrlNotify: ByteArray? = null,
|
||||
@JceId(9) val vTokenKey: ByteArray? = null,
|
||||
@JceId(10) val uFileLen: Long? = null,
|
||||
@JceId(11) val fileName: ByteArray? = null,
|
||||
@JceId(12) val vMd5: ByteArray? = null,
|
||||
@JceId(13) val sessionId: Long? = null,
|
||||
@JceId(14) val originfileMd5: ByteArray? = null,
|
||||
@JceId(15) val uOriginfiletype: Long? = null,
|
||||
@JceId(16) val uSeq: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class MsgType0x210SubMsgType0xe(
|
||||
@SerialId(0) val uint32SrcAppId: Long? = null,
|
||||
@SerialId(1) val uint32SrcInstId: Long? = null,
|
||||
@SerialId(2) val uint32DstAppId: Long? = null,
|
||||
@SerialId(3) val uint32DstInstId: Long? = null,
|
||||
@SerialId(4) val uint64DstUin: Long? = null,
|
||||
@SerialId(5) val uint64Sessionid: Long? = null,
|
||||
@SerialId(6) val uint32Operate: Long? = null,
|
||||
@SerialId(7) val uint32Seq: Long? = null,
|
||||
@SerialId(8) val uint32Code: Long? = null,
|
||||
@SerialId(9) val msg: String? = ""
|
||||
@JceId(0) val uint32SrcAppId: Long? = null,
|
||||
@JceId(1) val uint32SrcInstId: Long? = null,
|
||||
@JceId(2) val uint32DstAppId: Long? = null,
|
||||
@JceId(3) val uint32DstInstId: Long? = null,
|
||||
@JceId(4) val uint64DstUin: Long? = null,
|
||||
@JceId(5) val uint64Sessionid: Long? = null,
|
||||
@JceId(6) val uint32Operate: Long? = null,
|
||||
@JceId(7) val uint32Seq: Long? = null,
|
||||
@JceId(8) val uint32Code: Long? = null,
|
||||
@JceId(9) val msg: String? = ""
|
||||
) : JceStruct
|
||||
}
|
@ -9,72 +9,72 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
@Suppress("ArrayInDataClass")
|
||||
@Serializable
|
||||
internal data class RequestPushNotify(
|
||||
@SerialId(0) val uin: Long? = 0L,
|
||||
@SerialId(1) val ctype: Byte = 0,
|
||||
@SerialId(2) val strService: String?,
|
||||
@SerialId(3) val strCmd: String?,
|
||||
@SerialId(4) val vNotifyCookie: ByteArray? = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val usMsgType: Int?,
|
||||
@SerialId(6) val wUserActive: Int?,
|
||||
@SerialId(7) val wGeneralFlag: Int?,
|
||||
@SerialId(8) val bindedUin: Long?,
|
||||
@SerialId(9) val stMsgInfo: MsgInfo?,
|
||||
@SerialId(10) val msgCtrlBuf: String?,
|
||||
@SerialId(11) val serverBuf: ByteArray?,
|
||||
@SerialId(12) val pingFlag: Long?,
|
||||
@SerialId(13) val svrip: Int?
|
||||
@JceId(0) val uin: Long? = 0L,
|
||||
@JceId(1) val ctype: Byte = 0,
|
||||
@JceId(2) val strService: String?,
|
||||
@JceId(3) val strCmd: String?,
|
||||
@JceId(4) val vNotifyCookie: ByteArray? = EMPTY_BYTE_ARRAY,
|
||||
@JceId(5) val usMsgType: Int?,
|
||||
@JceId(6) val wUserActive: Int?,
|
||||
@JceId(7) val wGeneralFlag: Int?,
|
||||
@JceId(8) val bindedUin: Long?,
|
||||
@JceId(9) val stMsgInfo: MsgInfo?,
|
||||
@JceId(10) val msgCtrlBuf: String?,
|
||||
@JceId(11) val serverBuf: ByteArray?,
|
||||
@JceId(12) val pingFlag: Long?,
|
||||
@JceId(13) val svrip: Int?
|
||||
) : JceStruct, Packet
|
||||
|
||||
@Serializable
|
||||
internal class MsgInfo(
|
||||
@SerialId(0) val lFromUin: Long? = 0L,
|
||||
@SerialId(1) val uMsgTime: Long? = 0L,
|
||||
@SerialId(2) val shMsgType: Short,
|
||||
@SerialId(3) val shMsgSeq: Short,
|
||||
@SerialId(4) val strMsg: String?,
|
||||
@SerialId(5) val uRealMsgTime: Int?,
|
||||
@SerialId(6) val vMsg: ByteArray?,
|
||||
@SerialId(7) val uAppShareID: Long?,
|
||||
@SerialId(8) val vMsgCookies: ByteArray? = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(9) val vAppShareCookie: ByteArray? = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(10) val lMsgUid: Long?,
|
||||
@SerialId(11) val lLastChangeTime: Long?,
|
||||
@SerialId(12) val vCPicInfo: List<CPicInfo>?,
|
||||
@SerialId(13) val stShareData: ShareData?,
|
||||
@SerialId(14) val lFromInstId: Long?,
|
||||
@SerialId(15) val vRemarkOfSender: ByteArray?,
|
||||
@SerialId(16) val strFromMobile: String?,
|
||||
@SerialId(17) val strFromName: String?,
|
||||
@SerialId(18) val vNickName: List<String>?//,
|
||||
@JceId(0) val lFromUin: Long? = 0L,
|
||||
@JceId(1) val uMsgTime: Long? = 0L,
|
||||
@JceId(2) val shMsgType: Short,
|
||||
@JceId(3) val shMsgSeq: Short,
|
||||
@JceId(4) val strMsg: String?,
|
||||
@JceId(5) val uRealMsgTime: Int?,
|
||||
@JceId(6) val vMsg: ByteArray?,
|
||||
@JceId(7) val uAppShareID: Long?,
|
||||
@JceId(8) val vMsgCookies: ByteArray? = EMPTY_BYTE_ARRAY,
|
||||
@JceId(9) val vAppShareCookie: ByteArray? = EMPTY_BYTE_ARRAY,
|
||||
@JceId(10) val lMsgUid: Long?,
|
||||
@JceId(11) val lLastChangeTime: Long?,
|
||||
@JceId(12) val vCPicInfo: List<CPicInfo>?,
|
||||
@JceId(13) val stShareData: ShareData?,
|
||||
@JceId(14) val lFromInstId: Long?,
|
||||
@JceId(15) val vRemarkOfSender: ByteArray?,
|
||||
@JceId(16) val strFromMobile: String?,
|
||||
@JceId(17) val strFromName: String?,
|
||||
@JceId(18) val vNickName: List<String>?//,
|
||||
//@SerialId(19) val stC2CTmpMsgHead: TempMsgHead?
|
||||
) : JceStruct
|
||||
|
||||
|
||||
@Serializable
|
||||
internal class ShareData(
|
||||
@SerialId(0) val pkgname: String = "",
|
||||
@SerialId(1) val msgtail: String = "",
|
||||
@SerialId(2) val picurl: String = "",
|
||||
@SerialId(3) val url: String = ""
|
||||
@JceId(0) val pkgname: String = "",
|
||||
@JceId(1) val msgtail: String = "",
|
||||
@JceId(2) val picurl: String = "",
|
||||
@JceId(3) val url: String = ""
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class TempMsgHead(
|
||||
@SerialId(0) val c2c_type: Int? = 0,
|
||||
@SerialId(1) val serviceType: Int? = 0
|
||||
@JceId(0) val c2c_type: Int? = 0,
|
||||
@JceId(1) val serviceType: Int? = 0
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class CPicInfo(
|
||||
@SerialId(0) val vPath: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(1) val vHost: ByteArray? = EMPTY_BYTE_ARRAY
|
||||
@JceId(0) val vPath: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JceId(1) val vHost: ByteArray? = EMPTY_BYTE_ARRAY
|
||||
) : JceStruct
|
@ -9,38 +9,38 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
private val EMPTY_MAP = mapOf<String, String>()
|
||||
|
||||
@Serializable
|
||||
internal class RequestPacket(
|
||||
@SerialId(1) val iVersion: Short = 3,
|
||||
@SerialId(2) val cPacketType: Byte = 0,
|
||||
@SerialId(3) val iMessageType: Int = 0,
|
||||
@SerialId(4) val iRequestId: Int,
|
||||
@SerialId(5) val sServantName: String = "",
|
||||
@SerialId(6) val sFuncName: String = "",
|
||||
@SerialId(7) val sBuffer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(8) val iTimeout: Int? = 0,
|
||||
@SerialId(9) val context: Map<String, String>? = EMPTY_MAP,
|
||||
@SerialId(10) val status: Map<String, String>? = EMPTY_MAP
|
||||
@JceId(1) val iVersion: Short? = 3,
|
||||
@JceId(2) val cPacketType: Byte = 0,
|
||||
@JceId(3) val iMessageType: Int = 0,
|
||||
@JceId(4) val iRequestId: Int,
|
||||
@JceId(5) val sServantName: String = "",
|
||||
@JceId(6) val sFuncName: String = "",
|
||||
@JceId(7) val sBuffer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JceId(8) val iTimeout: Int? = 0,
|
||||
@JceId(9) val context: Map<String, String>? = EMPTY_MAP,
|
||||
@JceId(10) val status: Map<String, String>? = EMPTY_MAP
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class RequestDataVersion3(
|
||||
@SerialId(0) val map: Map<String, ByteArray> // 注意: ByteArray 不能直接放序列化的 JceStruct!! 要放类似 RequestDataStructSvcReqRegister 的
|
||||
@JceId(0) val map: Map<String, ByteArray> // 注意: ByteArray 不能直接放序列化的 JceStruct!! 要放类似 RequestDataStructSvcReqRegister 的
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class RequestDataVersion2(
|
||||
@SerialId(0) val map: Map<String, Map<String, ByteArray>>
|
||||
@JceId(0) val map: Map<String, Map<String, ByteArray>>
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class RequestDataStructSvcReqRegister(
|
||||
@SerialId(0) val struct: SvcReqRegister
|
||||
@JceId(0) val struct: SvcReqRegister
|
||||
) : JceStruct
|
@ -9,14 +9,15 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||
|
||||
@Serializable
|
||||
internal class RequestPushForceOffline(
|
||||
@SerialId(0) val uin: Long,
|
||||
@SerialId(1) val title: String? = "",
|
||||
@SerialId(2) val tips: String? = "",
|
||||
@SerialId(3) val sameDevice: Byte? = null
|
||||
@JceId(0) val uin: Long,
|
||||
@JceId(1) val title: String? = "",
|
||||
@JceId(2) val tips: String? = "",
|
||||
@JceId(3) val sameDevice: Byte? = null
|
||||
) : JceStruct
|
@ -9,47 +9,47 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||
|
||||
@Serializable
|
||||
internal class SvcReqRegister(
|
||||
@SerialId(0) val lUin: Long = 0L,
|
||||
@SerialId(1) val lBid: Long = 0L,
|
||||
@SerialId(2) val cConnType: Byte = 0,
|
||||
@SerialId(3) val sOther: String = "",
|
||||
@SerialId(4) val iStatus: Int = 11,
|
||||
@SerialId(5) val bOnlinePush: Byte = 0,
|
||||
@SerialId(6) val bIsOnline: Byte = 0,
|
||||
@SerialId(7) val bIsShowOnline: Byte = 0,
|
||||
@SerialId(8) val bKikPC: Byte = 0,
|
||||
@SerialId(9) val bKikWeak: Byte = 0,
|
||||
@SerialId(10) val timeStamp: Long = 0L,
|
||||
@SerialId(11) val iOSVersion: Long = 0L,
|
||||
@SerialId(12) val cNetType: Byte = 0,
|
||||
@SerialId(13) val sBuildVer: String? = "",
|
||||
@SerialId(14) val bRegType: Byte = 0,
|
||||
@SerialId(15) val vecDevParam: ByteArray? = null,
|
||||
@SerialId(16) val vecGuid: ByteArray? = null,
|
||||
@SerialId(17) val iLocaleID: Int = 2052,
|
||||
@SerialId(18) val bSlientPush: Byte = 0,
|
||||
@SerialId(19) val strDevName: String? = null,
|
||||
@SerialId(20) val strDevType: String? = null,
|
||||
@SerialId(21) val strOSVer: String? = null,
|
||||
@SerialId(22) val bOpenPush: Byte = 1,
|
||||
@SerialId(23) val iLargeSeq: Long = 0L,
|
||||
@SerialId(24) val iLastWatchStartTime: Long = 0L,
|
||||
@SerialId(26) val uOldSSOIp: Long = 0L,
|
||||
@SerialId(27) val uNewSSOIp: Long = 0L,
|
||||
@SerialId(28) val sChannelNo: String? = null,
|
||||
@SerialId(29) val lCpId: Long = 0L,
|
||||
@SerialId(30) val strVendorName: String? = null,
|
||||
@SerialId(31) val strVendorOSName: String? = null,
|
||||
@SerialId(32) val strIOSIdfa: String? = null,
|
||||
@SerialId(33) val bytes_0x769_reqbody: ByteArray? = null,
|
||||
@SerialId(34) val bIsSetStatus: Byte = 0,
|
||||
@SerialId(35) val vecServerBuf: ByteArray? = null,
|
||||
@SerialId(36) val bSetMute: Byte = 0
|
||||
@JceId(0) val lUin: Long = 0L,
|
||||
@JceId(1) val lBid: Long = 0L,
|
||||
@JceId(2) val cConnType: Byte = 0,
|
||||
@JceId(3) val sOther: String = "",
|
||||
@JceId(4) val iStatus: Int = 11,
|
||||
@JceId(5) val bOnlinePush: Byte = 0,
|
||||
@JceId(6) val bIsOnline: Byte = 0,
|
||||
@JceId(7) val bIsShowOnline: Byte = 0,
|
||||
@JceId(8) val bKikPC: Byte = 0,
|
||||
@JceId(9) val bKikWeak: Byte = 0,
|
||||
@JceId(10) val timeStamp: Long = 0L,
|
||||
@JceId(11) val iOSVersion: Long = 0L,
|
||||
@JceId(12) val cNetType: Byte = 0,
|
||||
@JceId(13) val sBuildVer: String? = "",
|
||||
@JceId(14) val bRegType: Byte = 0,
|
||||
@JceId(15) val vecDevParam: ByteArray? = null,
|
||||
@JceId(16) val vecGuid: ByteArray? = null,
|
||||
@JceId(17) val iLocaleID: Int = 2052,
|
||||
@JceId(18) val bSlientPush: Byte = 0,
|
||||
@JceId(19) val strDevName: String? = null,
|
||||
@JceId(20) val strDevType: String? = null,
|
||||
@JceId(21) val strOSVer: String? = null,
|
||||
@JceId(22) val bOpenPush: Byte = 1,
|
||||
@JceId(23) val iLargeSeq: Long = 0L,
|
||||
@JceId(24) val iLastWatchStartTime: Long = 0L,
|
||||
@JceId(26) val uOldSSOIp: Long = 0L,
|
||||
@JceId(27) val uNewSSOIp: Long = 0L,
|
||||
@JceId(28) val sChannelNo: String? = null,
|
||||
@JceId(29) val lCpId: Long = 0L,
|
||||
@JceId(30) val strVendorName: String? = null,
|
||||
@JceId(31) val strVendorOSName: String? = null,
|
||||
@JceId(32) val strIOSIdfa: String? = null,
|
||||
@JceId(33) val bytes_0x769_reqbody: ByteArray? = null,
|
||||
@JceId(34) val bIsSetStatus: Byte = 0,
|
||||
@JceId(35) val vecServerBuf: ByteArray? = null,
|
||||
@JceId(36) val bSetMute: Byte = 0
|
||||
// @SerialId(25) var vecBindUin: ArrayList<*>? = null // ?? 未知泛型
|
||||
) : JceStruct
|
@ -9,183 +9,183 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||
|
||||
@Serializable
|
||||
internal class GetTroopListReqV2Simplify(
|
||||
@SerialId(0) val uin: Long,
|
||||
@SerialId(1) val getMSFMsgFlag: Byte? = null,
|
||||
@SerialId(2) val vecCookies: ByteArray? = null,
|
||||
@SerialId(3) val vecGroupInfo: List<StTroopNumSimplify>? = null,
|
||||
@SerialId(4) val groupFlagExt: Byte? = null,
|
||||
@SerialId(5) val shVersion: Int? = null,
|
||||
@SerialId(6) val dwCompanyId: Long? = null,
|
||||
@SerialId(7) val versionNum: Long? = null,
|
||||
@SerialId(8) val getLongGroupName: Byte? = null
|
||||
@JceId(0) val uin: Long,
|
||||
@JceId(1) val getMSFMsgFlag: Byte? = null,
|
||||
@JceId(2) val vecCookies: ByteArray? = null,
|
||||
@JceId(3) val vecGroupInfo: List<StTroopNumSimplify>? = null,
|
||||
@JceId(4) val groupFlagExt: Byte? = null,
|
||||
@JceId(5) val shVersion: Int? = null,
|
||||
@JceId(6) val dwCompanyId: Long? = null,
|
||||
@JceId(7) val versionNum: Long? = null,
|
||||
@JceId(8) val getLongGroupName: Byte? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class StTroopNumSimplify(
|
||||
@SerialId(0) val groupCode: Long,
|
||||
@SerialId(1) val dwGroupInfoSeq: Long? = null,
|
||||
@SerialId(2) val dwGroupFlagExt: Long? = null,
|
||||
@SerialId(3) val dwGroupRankSeq: Long? = null
|
||||
@JceId(0) val groupCode: Long,
|
||||
@JceId(1) val dwGroupInfoSeq: Long? = null,
|
||||
@JceId(2) val dwGroupFlagExt: Long? = null,
|
||||
@JceId(3) val dwGroupRankSeq: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
|
||||
@Serializable
|
||||
internal class GetTroopListRespV2(
|
||||
@SerialId(0) val uin: Long,
|
||||
@SerialId(1) val troopCount: Short,
|
||||
@SerialId(2) val result: Int,
|
||||
@SerialId(3) val errorCode: Short? = null,
|
||||
@SerialId(4) val vecCookies: ByteArray? = null,
|
||||
@SerialId(5) val vecTroopList: List<StTroopNum>? = null,
|
||||
@SerialId(6) val vecTroopListDel: List<StTroopNum>? = null,
|
||||
@SerialId(7) val vecTroopRank: List<StGroupRankInfo>? = null,
|
||||
@SerialId(8) val vecFavGroup: List<StFavoriteGroup>? = null,
|
||||
@SerialId(9) val vecTroopListExt: List<StTroopNum>? = null
|
||||
@JceId(0) val uin: Long,
|
||||
@JceId(1) val troopCount: Short,
|
||||
@JceId(2) val result: Int,
|
||||
@JceId(3) val errorCode: Short? = null,
|
||||
@JceId(4) val vecCookies: ByteArray? = null,
|
||||
@JceId(5) val vecTroopList: List<StTroopNum>? = null,
|
||||
@JceId(6) val vecTroopListDel: List<StTroopNum>? = null,
|
||||
@JceId(7) val vecTroopRank: List<StGroupRankInfo>? = null,
|
||||
@JceId(8) val vecFavGroup: List<StFavoriteGroup>? = null,
|
||||
@JceId(9) val vecTroopListExt: List<StTroopNum>? = null
|
||||
) : JceStruct
|
||||
|
||||
|
||||
@Serializable
|
||||
internal class StTroopNum(
|
||||
@SerialId(0) val groupUin: Long,
|
||||
@SerialId(1) val groupCode: Long,
|
||||
@SerialId(2) val flag: Byte? = null,
|
||||
@SerialId(3) val dwGroupInfoSeq: Long? = null,
|
||||
@SerialId(4) val groupName: String = "",
|
||||
@SerialId(5) val groupMemo: String = "",
|
||||
@SerialId(6) val dwGroupFlagExt: Long? = null,
|
||||
@SerialId(7) val dwGroupRankSeq: Long? = null,
|
||||
@SerialId(8) val dwCertificationType: Long? = null,
|
||||
@SerialId(9) val dwShutUpTimestamp: Long? = null,
|
||||
@SerialId(10) val dwMyShutUpTimestamp: Long? = null,
|
||||
@SerialId(11) val dwCmdUinUinFlag: Long? = null,
|
||||
@SerialId(12) val dwAdditionalFlag: Long? = null,
|
||||
@SerialId(13) val dwGroupTypeFlag: Long? = null,
|
||||
@SerialId(14) val dwGroupSecType: Long? = null,
|
||||
@SerialId(15) val dwGroupSecTypeInfo: Long? = null,
|
||||
@SerialId(16) val dwGroupClassExt: Long? = null,
|
||||
@SerialId(17) val dwAppPrivilegeFlag: Long? = null,
|
||||
@SerialId(18) val dwSubscriptionUin: Long? = null,
|
||||
@SerialId(19) val dwMemberNum: Long? = null,
|
||||
@SerialId(20) val dwMemberNumSeq: Long? = null,
|
||||
@SerialId(21) val dwMemberCardSeq: Long? = null,
|
||||
@SerialId(22) val dwGroupFlagExt3: Long? = null,
|
||||
@SerialId(23) val dwGroupOwnerUin: Long,
|
||||
@SerialId(24) val isConfGroup: Byte? = null,
|
||||
@SerialId(25) val isModifyConfGroupFace: Byte? = null,
|
||||
@SerialId(26) val isModifyConfGroupName: Byte? = null,
|
||||
@SerialId(27) val dwCmduinJoinTime: Long? = null,
|
||||
@SerialId(28) val ulCompanyId: Long? = null,
|
||||
@SerialId(29) val dwMaxGroupMemberNum: Long? = null,
|
||||
@SerialId(30) val dwCmdUinGroupMask: Long? = null,
|
||||
@SerialId(31) val udwHLGuildAppid: Long? = null,
|
||||
@SerialId(32) val udwHLGuildSubType: Long? = null,
|
||||
@SerialId(33) val udwCmdUinRingtoneID: Long? = null,
|
||||
@SerialId(34) val udwCmdUinFlagEx2: Long? = null
|
||||
@JceId(0) val groupUin: Long,
|
||||
@JceId(1) val groupCode: Long,
|
||||
@JceId(2) val flag: Byte? = null,
|
||||
@JceId(3) val dwGroupInfoSeq: Long? = null,
|
||||
@JceId(4) val groupName: String = "",
|
||||
@JceId(5) val groupMemo: String = "",
|
||||
@JceId(6) val dwGroupFlagExt: Long? = null,
|
||||
@JceId(7) val dwGroupRankSeq: Long? = null,
|
||||
@JceId(8) val dwCertificationType: Long? = null,
|
||||
@JceId(9) val dwShutUpTimestamp: Long? = null,
|
||||
@JceId(10) val dwMyShutUpTimestamp: Long? = null,
|
||||
@JceId(11) val dwCmdUinUinFlag: Long? = null,
|
||||
@JceId(12) val dwAdditionalFlag: Long? = null,
|
||||
@JceId(13) val dwGroupTypeFlag: Long? = null,
|
||||
@JceId(14) val dwGroupSecType: Long? = null,
|
||||
@JceId(15) val dwGroupSecTypeInfo: Long? = null,
|
||||
@JceId(16) val dwGroupClassExt: Long? = null,
|
||||
@JceId(17) val dwAppPrivilegeFlag: Long? = null,
|
||||
@JceId(18) val dwSubscriptionUin: Long? = null,
|
||||
@JceId(19) val dwMemberNum: Long? = null,
|
||||
@JceId(20) val dwMemberNumSeq: Long? = null,
|
||||
@JceId(21) val dwMemberCardSeq: Long? = null,
|
||||
@JceId(22) val dwGroupFlagExt3: Long? = null,
|
||||
@JceId(23) val dwGroupOwnerUin: Long,
|
||||
@JceId(24) val isConfGroup: Byte? = null,
|
||||
@JceId(25) val isModifyConfGroupFace: Byte? = null,
|
||||
@JceId(26) val isModifyConfGroupName: Byte? = null,
|
||||
@JceId(27) val dwCmduinJoinTime: Long? = null,
|
||||
@JceId(28) val ulCompanyId: Long? = null,
|
||||
@JceId(29) val dwMaxGroupMemberNum: Long? = null,
|
||||
@JceId(30) val dwCmdUinGroupMask: Long? = null,
|
||||
@JceId(31) val udwHLGuildAppid: Long? = null,
|
||||
@JceId(32) val udwHLGuildSubType: Long? = null,
|
||||
@JceId(33) val udwCmdUinRingtoneID: Long? = null,
|
||||
@JceId(34) val udwCmdUinFlagEx2: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class StGroupRankInfo(
|
||||
@SerialId(0) val dwGroupCode: Long,
|
||||
@SerialId(1) val groupRankSysFlag: Byte? = null,
|
||||
@SerialId(2) val groupRankUserFlag: Byte? = null,
|
||||
@SerialId(3) val vecRankMap: List<StLevelRankPair>? = null,
|
||||
@SerialId(4) val dwGroupRankSeq: Long? = null,
|
||||
@SerialId(5) val ownerName: String? = "",
|
||||
@SerialId(6) val adminName: String? = "",
|
||||
@SerialId(7) val dwOfficeMode: Long? = null
|
||||
@JceId(0) val dwGroupCode: Long,
|
||||
@JceId(1) val groupRankSysFlag: Byte? = null,
|
||||
@JceId(2) val groupRankUserFlag: Byte? = null,
|
||||
@JceId(3) val vecRankMap: List<StLevelRankPair>? = null,
|
||||
@JceId(4) val dwGroupRankSeq: Long? = null,
|
||||
@JceId(5) val ownerName: String? = "",
|
||||
@JceId(6) val adminName: String? = "",
|
||||
@JceId(7) val dwOfficeMode: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class StFavoriteGroup(
|
||||
@SerialId(0) val dwGroupCode: Long,
|
||||
@SerialId(1) val dwTimestamp: Long? = null,
|
||||
@SerialId(2) val dwSnsFlag: Long? = 1L,
|
||||
@SerialId(3) val dwOpenTimestamp: Long? = null
|
||||
@JceId(0) val dwGroupCode: Long,
|
||||
@JceId(1) val dwTimestamp: Long? = null,
|
||||
@JceId(2) val dwSnsFlag: Long? = 1L,
|
||||
@JceId(3) val dwOpenTimestamp: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class StLevelRankPair(
|
||||
@SerialId(0) val dwLevel: Long? = null,
|
||||
@SerialId(1) val rank: String? = ""
|
||||
@JceId(0) val dwLevel: Long? = null,
|
||||
@JceId(1) val rank: String? = ""
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class GetTroopMemberListReq(
|
||||
@SerialId(0) val uin: Long,
|
||||
@SerialId(1) val groupCode: Long,
|
||||
@SerialId(2) val nextUin: Long,
|
||||
@SerialId(3) val groupUin: Long,
|
||||
@SerialId(4) val version: Long? = null,
|
||||
@SerialId(5) val reqType: Long? = null,
|
||||
@SerialId(6) val getListAppointTime: Long? = null,
|
||||
@SerialId(7) val richCardNameVer: Byte? = null
|
||||
@JceId(0) val uin: Long,
|
||||
@JceId(1) val groupCode: Long,
|
||||
@JceId(2) val nextUin: Long,
|
||||
@JceId(3) val groupUin: Long,
|
||||
@JceId(4) val version: Long? = null,
|
||||
@JceId(5) val reqType: Long? = null,
|
||||
@JceId(6) val getListAppointTime: Long? = null,
|
||||
@JceId(7) val richCardNameVer: Byte? = null
|
||||
) : JceStruct
|
||||
|
||||
|
||||
@Serializable
|
||||
internal class GetTroopMemberListResp(
|
||||
@SerialId(0) val uin: Long,
|
||||
@SerialId(1) val groupCode: Long,
|
||||
@SerialId(2) val groupUin: Long,
|
||||
@SerialId(3) val vecTroopMember: List<StTroopMemberInfo>,
|
||||
@SerialId(4) val nextUin: Long,
|
||||
@SerialId(5) val result: Int,
|
||||
@SerialId(6) val errorCode: Short? = null,
|
||||
@SerialId(7) val officeMode: Long? = null,
|
||||
@SerialId(8) val nextGetTime: Long? = null
|
||||
@JceId(0) val uin: Long,
|
||||
@JceId(1) val groupCode: Long,
|
||||
@JceId(2) val groupUin: Long,
|
||||
@JceId(3) val vecTroopMember: List<StTroopMemberInfo>,
|
||||
@JceId(4) val nextUin: Long,
|
||||
@JceId(5) val result: Int,
|
||||
@JceId(6) val errorCode: Short? = null,
|
||||
@JceId(7) val officeMode: Long? = null,
|
||||
@JceId(8) val nextGetTime: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class StTroopMemberInfo(
|
||||
@SerialId(0) val memberUin: Long,
|
||||
@SerialId(1) val faceId: Short,
|
||||
@SerialId(2) val age: Byte,
|
||||
@SerialId(3) val gender: Byte,
|
||||
@SerialId(4) val nick: String = "",
|
||||
@SerialId(5) val status: Byte = 20,
|
||||
@SerialId(6) val sShowName: String? = null,
|
||||
@SerialId(8) val sName: String? = null,
|
||||
@SerialId(9) val cGender: Byte? = null,
|
||||
@SerialId(10) val sPhone: String? = "",
|
||||
@SerialId(11) val sEmail: String? = "",
|
||||
@SerialId(12) val sMemo: String? = "",
|
||||
@SerialId(13) val autoRemark: String? = "",
|
||||
@SerialId(14) val dwMemberLevel: Long? = null,
|
||||
@SerialId(15) val dwJoinTime: Long? = null,
|
||||
@SerialId(16) val dwLastSpeakTime: Long? = null,
|
||||
@SerialId(17) val dwCreditLevel: Long? = null,
|
||||
@SerialId(18) val dwFlag: Long? = null,
|
||||
@SerialId(19) val dwFlagExt: Long? = null,
|
||||
@SerialId(20) val dwPoint: Long? = null,
|
||||
@SerialId(21) val concerned: Byte? = null,
|
||||
@SerialId(22) val shielded: Byte? = null,
|
||||
@SerialId(23) val sSpecialTitle: String? = "",
|
||||
@SerialId(24) val dwSpecialTitleExpireTime: Long? = null,
|
||||
@SerialId(25) val job: String? = "",
|
||||
@SerialId(26) val apolloFlag: Byte? = null,
|
||||
@SerialId(27) val dwApolloTimestamp: Long? = null,
|
||||
@SerialId(28) val dwGlobalGroupLevel: Long? = null,
|
||||
@SerialId(29) val dwTitleId: Long? = null,
|
||||
@SerialId(30) val dwShutupTimestap: Long? = null,
|
||||
@SerialId(31) val dwGlobalGroupPoint: Long? = null,
|
||||
@SerialId(32) val qzusrinfo: QzoneUserInfo? = null,
|
||||
@SerialId(33) val richCardNameVer: Byte? = null,
|
||||
@SerialId(34) val dwVipType: Long? = null,
|
||||
@SerialId(35) val dwVipLevel: Long? = null,
|
||||
@SerialId(36) val dwBigClubLevel: Long? = null,
|
||||
@SerialId(37) val dwBigClubFlag: Long? = null,
|
||||
@SerialId(38) val dwNameplate: Long? = null,
|
||||
@SerialId(39) val vecGroupHonor: ByteArray? = null
|
||||
@JceId(0) val memberUin: Long,
|
||||
@JceId(1) val faceId: Short,
|
||||
@JceId(2) val age: Byte,
|
||||
@JceId(3) val gender: Byte,
|
||||
@JceId(4) val nick: String = "",
|
||||
@JceId(5) val status: Byte = 20,
|
||||
@JceId(6) val sShowName: String? = null,
|
||||
@JceId(8) val sName: String? = null,
|
||||
@JceId(9) val cGender: Byte? = null,
|
||||
@JceId(10) val sPhone: String? = "",
|
||||
@JceId(11) val sEmail: String? = "",
|
||||
@JceId(12) val sMemo: String? = "",
|
||||
@JceId(13) val autoRemark: String? = "",
|
||||
@JceId(14) val dwMemberLevel: Long? = null,
|
||||
@JceId(15) val dwJoinTime: Long? = null,
|
||||
@JceId(16) val dwLastSpeakTime: Long? = null,
|
||||
@JceId(17) val dwCreditLevel: Long? = null,
|
||||
@JceId(18) val dwFlag: Long? = null,
|
||||
@JceId(19) val dwFlagExt: Long? = null,
|
||||
@JceId(20) val dwPoint: Long? = null,
|
||||
@JceId(21) val concerned: Byte? = null,
|
||||
@JceId(22) val shielded: Byte? = null,
|
||||
@JceId(23) val sSpecialTitle: String? = "",
|
||||
@JceId(24) val dwSpecialTitleExpireTime: Long? = null,
|
||||
@JceId(25) val job: String? = "",
|
||||
@JceId(26) val apolloFlag: Byte? = null,
|
||||
@JceId(27) val dwApolloTimestamp: Long? = null,
|
||||
@JceId(28) val dwGlobalGroupLevel: Long? = null,
|
||||
@JceId(29) val dwTitleId: Long? = null,
|
||||
@JceId(30) val dwShutupTimestap: Long? = null,
|
||||
@JceId(31) val dwGlobalGroupPoint: Long? = null,
|
||||
@JceId(32) val qzusrinfo: QzoneUserInfo? = null,
|
||||
@JceId(33) val richCardNameVer: Byte? = null,
|
||||
@JceId(34) val dwVipType: Long? = null,
|
||||
@JceId(35) val dwVipLevel: Long? = null,
|
||||
@JceId(36) val dwBigClubLevel: Long? = null,
|
||||
@JceId(37) val dwBigClubFlag: Long? = null,
|
||||
@JceId(38) val dwNameplate: Long? = null,
|
||||
@JceId(39) val vecGroupHonor: ByteArray? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class QzoneUserInfo(
|
||||
@SerialId(0) val eStarState: Int? = null,
|
||||
@SerialId(1) val extendInfo: Map<String, String>? = null
|
||||
@JceId(0) val eStarState: Int? = null,
|
||||
@JceId(1) val extendInfo: Map<String, String>? = null
|
||||
) : JceStruct
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
@ -18,118 +18,118 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
internal class Cmd0x352 : ProtoBuf {
|
||||
@Serializable
|
||||
class DelImgReq(
|
||||
@SerialId(1) val srcUin: Long = 0L,
|
||||
@SerialId(2) val dstUin: Long = 0L,
|
||||
@SerialId(3) val reqTerm: Int = 0,
|
||||
@SerialId(4) val reqPlatformType: Int = 0,
|
||||
@SerialId(5) val buType: Int = 0,
|
||||
@SerialId(6) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(7) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(8) val picWidth: Int = 0,
|
||||
@SerialId(9) val picHeight: Int = 0
|
||||
@ProtoId(1) val srcUin: Long = 0L,
|
||||
@ProtoId(2) val dstUin: Long = 0L,
|
||||
@ProtoId(3) val reqTerm: Int = 0,
|
||||
@ProtoId(4) val reqPlatformType: Int = 0,
|
||||
@ProtoId(5) val buType: Int = 0,
|
||||
@ProtoId(6) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(8) val picWidth: Int = 0,
|
||||
@ProtoId(9) val picHeight: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class DelImgRsp(
|
||||
@SerialId(1) val result: Int = 0,
|
||||
@SerialId(2) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val result: Int = 0,
|
||||
@ProtoId(2) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GetImgUrlReq(
|
||||
@SerialId(1) val srcUin: Long = 0L,
|
||||
@SerialId(2) val dstUin: Long = 0L,
|
||||
@SerialId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val urlFlag: Int = 0,
|
||||
@SerialId(6) val urlType: Int = 0,
|
||||
@SerialId(7) val reqTerm: Int = 0,
|
||||
@SerialId(8) val reqPlatformType: Int = 0,
|
||||
@SerialId(9) val srcFileType: Int = 0,
|
||||
@SerialId(10) val innerIp: Int = 0,
|
||||
@SerialId(11) val boolAddressBook: Boolean = false,
|
||||
@SerialId(12) val buType: Int = 0,
|
||||
@SerialId(13) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(14) val picUpTimestamp: Int = 0,
|
||||
@SerialId(15) val reqTransferType: Int = 0
|
||||
@ProtoId(1) val srcUin: Long = 0L,
|
||||
@ProtoId(2) val dstUin: Long = 0L,
|
||||
@ProtoId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val urlFlag: Int = 0,
|
||||
@ProtoId(6) val urlType: Int = 0,
|
||||
@ProtoId(7) val reqTerm: Int = 0,
|
||||
@ProtoId(8) val reqPlatformType: Int = 0,
|
||||
@ProtoId(9) val srcFileType: Int = 0,
|
||||
@ProtoId(10) val innerIp: Int = 0,
|
||||
@ProtoId(11) val boolAddressBook: Boolean = false,
|
||||
@ProtoId(12) val buType: Int = 0,
|
||||
@ProtoId(13) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(14) val picUpTimestamp: Int = 0,
|
||||
@ProtoId(15) val reqTransferType: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GetImgUrlRsp(
|
||||
@SerialId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val clientIp: Int = 0,
|
||||
@SerialId(3) val result: Int = 0,
|
||||
@SerialId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val bytesThumbDownUrl: List<ByteArray>? = null,
|
||||
@SerialId(6) val bytesOriginalDownUrl: List<ByteArray>? = null,
|
||||
@SerialId(7) val msgImgInfo: ImgInfo? = null,
|
||||
@SerialId(8) val uint32DownIp: List<Int>? = null,
|
||||
@SerialId(9) val uint32DownPort: List<Int>? = null,
|
||||
@SerialId(10) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(11) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(12) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(13) val bytesBigDownUrl: List<ByteArray>? = null,
|
||||
@SerialId(14) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(15) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(16) val httpsUrlFlag: Int = 0,
|
||||
@SerialId(26) val msgDownIp6: List<IPv6Info>? = null,
|
||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val clientIp: Int = 0,
|
||||
@ProtoId(3) val result: Int = 0,
|
||||
@ProtoId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val bytesThumbDownUrl: List<ByteArray>? = null,
|
||||
@ProtoId(6) val bytesOriginalDownUrl: List<ByteArray>? = null,
|
||||
@ProtoId(7) val msgImgInfo: ImgInfo? = null,
|
||||
@ProtoId(8) val uint32DownIp: List<Int>? = null,
|
||||
@ProtoId(9) val uint32DownPort: List<Int>? = null,
|
||||
@ProtoId(10) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(11) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(12) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(13) val bytesBigDownUrl: List<ByteArray>? = null,
|
||||
@ProtoId(14) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(15) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(16) val httpsUrlFlag: Int = 0,
|
||||
@ProtoId(26) val msgDownIp6: List<IPv6Info>? = null,
|
||||
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Suppress("ArrayInDataClass")
|
||||
@Serializable
|
||||
data class ImgInfo(
|
||||
@SerialId(1) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val fileType: Int = 0,
|
||||
@SerialId(3) val fileSize: Long = 0L,
|
||||
@SerialId(4) val fileWidth: Int = 0,
|
||||
@SerialId(5) val fileHeight: Int = 0,
|
||||
@SerialId(6) val fileFlag: Long = 0L,
|
||||
@SerialId(7) val fileCutPos: Int = 0
|
||||
@ProtoId(1) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val fileType: Int = 0,
|
||||
@ProtoId(3) val fileSize: Long = 0L,
|
||||
@ProtoId(4) val fileWidth: Int = 0,
|
||||
@ProtoId(5) val fileHeight: Int = 0,
|
||||
@ProtoId(6) val fileFlag: Long = 0L,
|
||||
@ProtoId(7) val fileCutPos: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class IPv6Info(
|
||||
@SerialId(1) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val port: Int = 0
|
||||
@ProtoId(1) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val port: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ReqBody(
|
||||
@SerialId(1) val subcmd: Int = 0, //2是GetImgUrlReq 1是UploadImgReq
|
||||
@SerialId(2) val msgTryupImgReq: List<TryUpImgReq>? = null,// optional
|
||||
@SerialId(3) val msgGetimgUrlReq: List<GetImgUrlReq>? = null,// optional
|
||||
@SerialId(4) val msgDelImgReq: List<DelImgReq>? = null,
|
||||
@SerialId(10) val netType: Int = 3// 数据网络=5
|
||||
@ProtoId(1) val subcmd: Int = 0, //2是GetImgUrlReq 1是UploadImgReq
|
||||
@ProtoId(2) val msgTryupImgReq: List<TryUpImgReq>? = null,// optional
|
||||
@ProtoId(3) val msgGetimgUrlReq: List<GetImgUrlReq>? = null,// optional
|
||||
@ProtoId(4) val msgDelImgReq: List<DelImgReq>? = null,
|
||||
@ProtoId(10) val netType: Int = 3// 数据网络=5
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RspBody(
|
||||
@SerialId(1) val subcmd: Int = 0,
|
||||
@SerialId(2) val msgTryupImgRsp: List<TryUpImgRsp>? = null,
|
||||
@SerialId(3) val msgGetimgUrlRsp: List<GetImgUrlRsp>? = null,
|
||||
@SerialId(4) val boolNewBigchan: Boolean = false,
|
||||
@SerialId(5) val msgDelImgRsp: List<DelImgRsp>? = null,
|
||||
@SerialId(10) val failMsg: String? = ""
|
||||
@ProtoId(1) val subcmd: Int = 0,
|
||||
@ProtoId(2) val msgTryupImgRsp: List<TryUpImgRsp>? = null,
|
||||
@ProtoId(3) val msgGetimgUrlRsp: List<GetImgUrlRsp>? = null,
|
||||
@ProtoId(4) val boolNewBigchan: Boolean = false,
|
||||
@ProtoId(5) val msgDelImgRsp: List<DelImgRsp>? = null,
|
||||
@ProtoId(10) val failMsg: String? = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class TryUpImgReq(
|
||||
@SerialId(1) val srcUin: Int,
|
||||
@SerialId(2) val dstUin: Int,
|
||||
@SerialId(3) val fileId: Int = 0,//从0开始的自增数?貌似有一个连接就要自增1, 但是又会重置回0
|
||||
@SerialId(4) val fileMd5: ByteArray,
|
||||
@SerialId(5) val fileSize: Int,
|
||||
@SerialId(6) val fileName: String,//默认为md5+".jpg"
|
||||
@SerialId(7) val srcTerm: Int = 5,
|
||||
@SerialId(8) val platformType: Int = 9,
|
||||
@SerialId(9) val innerIP: Int = 0,
|
||||
@SerialId(10) val addressBook: Int = 0,//chatType == 1006为1 我觉得发0没问题
|
||||
@SerialId(11) val retry: Int = 0,//default
|
||||
@SerialId(12) val buType: Int = 1,//1或96 不确定
|
||||
@SerialId(13) val imgOriginal: Int,//是否为原图
|
||||
@SerialId(14) val imgWidth: Int,
|
||||
@SerialId(15) val imgHeight: Int,
|
||||
@ProtoId(1) val srcUin: Int,
|
||||
@ProtoId(2) val dstUin: Int,
|
||||
@ProtoId(3) val fileId: Int = 0,//从0开始的自增数?貌似有一个连接就要自增1, 但是又会重置回0
|
||||
@ProtoId(4) val fileMd5: ByteArray,
|
||||
@ProtoId(5) val fileSize: Int,
|
||||
@ProtoId(6) val fileName: String,//默认为md5+".jpg"
|
||||
@ProtoId(7) val srcTerm: Int = 5,
|
||||
@ProtoId(8) val platformType: Int = 9,
|
||||
@ProtoId(9) val innerIP: Int = 0,
|
||||
@ProtoId(10) val addressBook: Int = 0,//chatType == 1006为1 我觉得发0没问题
|
||||
@ProtoId(11) val retry: Int = 0,//default
|
||||
@ProtoId(12) val buType: Int = 1,//1或96 不确定
|
||||
@ProtoId(13) val imgOriginal: Int,//是否为原图
|
||||
@ProtoId(14) val imgWidth: Int,
|
||||
@ProtoId(15) val imgHeight: Int,
|
||||
/**
|
||||
* ImgType:
|
||||
* JPG: 1000
|
||||
@ -140,50 +140,50 @@ internal class Cmd0x352 : ProtoBuf {
|
||||
* APNG: 2001
|
||||
* SHARPP: 1004
|
||||
*/
|
||||
@SerialId(16) val imgType: Int = 1000,
|
||||
@SerialId(17) val buildVer: String = "8.2.0.1296",//版本号
|
||||
@SerialId(18) val fileIndex: ByteArray = EMPTY_BYTE_ARRAY,//default
|
||||
@SerialId(19) val fileStoreDays: Int = 0,//default
|
||||
@SerialId(20) val stepFlag: Int = 0,//default
|
||||
@SerialId(21) val rejectTryFast: Int = 0,//bool
|
||||
@SerialId(22) val srvUpload: Int = 1,//typeHotPic[1/2/3]
|
||||
@SerialId(23) val transferUrl: ByteArray = EMPTY_BYTE_ARRAY//rawDownloadUrl, 如果没有就是EMPTY_BYTE_ARRAY
|
||||
@ProtoId(16) val imgType: Int = 1000,
|
||||
@ProtoId(17) val buildVer: String = "8.2.7.4410",//版本号
|
||||
@ProtoId(18) val fileIndex: ByteArray = EMPTY_BYTE_ARRAY,//default
|
||||
@ProtoId(19) val fileStoreDays: Int = 0,//default
|
||||
@ProtoId(20) val stepFlag: Int = 0,//default
|
||||
@ProtoId(21) val rejectTryFast: Int = 0,//bool
|
||||
@ProtoId(22) val srvUpload: Int = 1,//typeHotPic[1/2/3]
|
||||
@ProtoId(23) val transferUrl: ByteArray = EMPTY_BYTE_ARRAY//rawDownloadUrl, 如果没有就是EMPTY_BYTE_ARRAY
|
||||
) : ImgReq
|
||||
|
||||
@Serializable
|
||||
class TryUpImgRsp(
|
||||
@SerialId(1) val fileId: Long = 0L,
|
||||
@SerialId(2) val clientIp: Int = 0,
|
||||
@SerialId(3) val result: Int = 0,
|
||||
@SerialId(4) val failMsg: String? = "",
|
||||
@SerialId(5) val boolFileExit: Boolean = false,
|
||||
@SerialId(6) val msgImgInfo: ImgInfo? = null,
|
||||
@SerialId(7) val uint32UpIp: List<Int>? = null,
|
||||
@SerialId(8) val uint32UpPort: List<Int>? = null,
|
||||
@SerialId(9) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(10) val upResid: String = "",
|
||||
@SerialId(11) val upUuid: String = "",
|
||||
@SerialId(12) val upOffset: Long = 0L,
|
||||
@SerialId(13) val blockSize: Long = 0L,
|
||||
@SerialId(14) val encryptDstip: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(15) val roamdays: Int = 0,
|
||||
@SerialId(26) val msgUpIp6: List<IPv6Info>? = null,
|
||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(60) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(61) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(62) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(64) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(65) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(66) val httpsUrlFlag: Int = 0,
|
||||
@SerialId(1001) val msgInfo4busi: TryUpInfo4Busi? = null
|
||||
@ProtoId(1) val fileId: Long = 0L,
|
||||
@ProtoId(2) val clientIp: Int = 0,
|
||||
@ProtoId(3) val result: Int = 0,
|
||||
@ProtoId(4) val failMsg: String? = "",
|
||||
@ProtoId(5) val boolFileExit: Boolean = false,
|
||||
@ProtoId(6) val msgImgInfo: ImgInfo? = null,
|
||||
@ProtoId(7) val uint32UpIp: List<Int>? = null,
|
||||
@ProtoId(8) val uint32UpPort: List<Int>? = null,
|
||||
@ProtoId(9) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(10) val upResid: String = "",
|
||||
@ProtoId(11) val upUuid: String = "",
|
||||
@ProtoId(12) val upOffset: Long = 0L,
|
||||
@ProtoId(13) val blockSize: Long = 0L,
|
||||
@ProtoId(14) val encryptDstip: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(15) val roamdays: Int = 0,
|
||||
@ProtoId(26) val msgUpIp6: List<IPv6Info>? = null,
|
||||
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(60) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(61) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(62) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(64) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(65) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(66) val httpsUrlFlag: Int = 0,
|
||||
@ProtoId(1001) val msgInfo4busi: TryUpInfo4Busi? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class TryUpInfo4Busi(
|
||||
@SerialId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val thumbDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val originalDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val bigDownUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val thumbDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val originalDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val bigDownUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
}
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
@ -18,141 +18,141 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
internal class Cmd0x388 : ProtoBuf {
|
||||
@Serializable
|
||||
class DelImgReq(
|
||||
@SerialId(1) val srcUin: Long = 0L,
|
||||
@SerialId(2) val dstUin: Long = 0L,
|
||||
@SerialId(3) val reqTerm: Int = 0,
|
||||
@SerialId(4) val reqPlatformType: Int = 0,
|
||||
@SerialId(5) val buType: Int = 0,
|
||||
@SerialId(6) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(7) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(8) val picWidth: Int = 0,
|
||||
@SerialId(9) val picHeight: Int = 0
|
||||
@ProtoId(1) val srcUin: Long = 0L,
|
||||
@ProtoId(2) val dstUin: Long = 0L,
|
||||
@ProtoId(3) val reqTerm: Int = 0,
|
||||
@ProtoId(4) val reqPlatformType: Int = 0,
|
||||
@ProtoId(5) val buType: Int = 0,
|
||||
@ProtoId(6) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(8) val picWidth: Int = 0,
|
||||
@ProtoId(9) val picHeight: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class DelImgRsp(
|
||||
@SerialId(1) val result: Int = 0,
|
||||
@SerialId(2) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val result: Int = 0,
|
||||
@ProtoId(2) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ExpRoamExtendInfo(
|
||||
@SerialId(1) val resid: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val resid: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ExpRoamPicInfo(
|
||||
@SerialId(1) val shopFlag: Int = 0,
|
||||
@SerialId(2) val pkgId: Int = 0,
|
||||
@SerialId(3) val picId: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val shopFlag: Int = 0,
|
||||
@ProtoId(2) val pkgId: Int = 0,
|
||||
@ProtoId(3) val picId: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ExtensionCommPicTryUp(
|
||||
@SerialId(1) val bytesExtinfo: List<ByteArray>? = null
|
||||
@ProtoId(1) val bytesExtinfo: List<ByteArray>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ExtensionExpRoamTryUp(
|
||||
@SerialId(1) val msgExproamPicInfo: List<ExpRoamPicInfo>? = null
|
||||
@ProtoId(1) val msgExproamPicInfo: List<ExpRoamPicInfo>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GetImgUrlReq(
|
||||
@SerialId(1) val groupCode: Long = 0L,
|
||||
@SerialId(2) val dstUin: Long = 0L,
|
||||
@SerialId(3) val fileid: Long = 0L,
|
||||
@SerialId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val urlFlag: Int = 0,
|
||||
@SerialId(6) val urlType: Int = 0,
|
||||
@SerialId(7) val reqTerm: Int = 0,
|
||||
@SerialId(8) val reqPlatformType: Int = 0,
|
||||
@SerialId(9) val innerIp: Int = 0,
|
||||
@SerialId(10) val buType: Int = 0,
|
||||
@SerialId(11) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(12) val fileId: Long = 0L,
|
||||
@SerialId(13) val fileSize: Long = 0L,
|
||||
@SerialId(14) val originalPic: Int = 0,
|
||||
@SerialId(15) val retryReq: Int = 0,
|
||||
@SerialId(16) val fileHeight: Int = 0,
|
||||
@SerialId(17) val fileWidth: Int = 0,
|
||||
@SerialId(18) val picType: Int = 0,
|
||||
@SerialId(19) val picUpTimestamp: Int = 0,
|
||||
@SerialId(20) val reqTransferType: Int = 0
|
||||
@ProtoId(1) val groupCode: Long = 0L,
|
||||
@ProtoId(2) val dstUin: Long = 0L,
|
||||
@ProtoId(3) val fileid: Long = 0L,
|
||||
@ProtoId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val urlFlag: Int = 0,
|
||||
@ProtoId(6) val urlType: Int = 0,
|
||||
@ProtoId(7) val reqTerm: Int = 0,
|
||||
@ProtoId(8) val reqPlatformType: Int = 0,
|
||||
@ProtoId(9) val innerIp: Int = 0,
|
||||
@ProtoId(10) val buType: Int = 0,
|
||||
@ProtoId(11) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(12) val fileId: Long = 0L,
|
||||
@ProtoId(13) val fileSize: Long = 0L,
|
||||
@ProtoId(14) val originalPic: Int = 0,
|
||||
@ProtoId(15) val retryReq: Int = 0,
|
||||
@ProtoId(16) val fileHeight: Int = 0,
|
||||
@ProtoId(17) val fileWidth: Int = 0,
|
||||
@ProtoId(18) val picType: Int = 0,
|
||||
@ProtoId(19) val picUpTimestamp: Int = 0,
|
||||
@ProtoId(20) val reqTransferType: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GetImgUrlRsp(
|
||||
@SerialId(1) val fileid: Long = 0L,
|
||||
@SerialId(2) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val result: Int = 0,
|
||||
@SerialId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val msgImgInfo: ImgInfo? = null,
|
||||
@SerialId(6) val bytesThumbDownUrl: List<ByteArray>? = null,
|
||||
@SerialId(7) val bytesOriginalDownUrl: List<ByteArray>? = null,
|
||||
@SerialId(8) val bytesBigDownUrl: List<ByteArray>? = null,
|
||||
@SerialId(9) val uint32DownIp: List<Int>? = null,
|
||||
@SerialId(10) val uint32DownPort: List<Int>? = null,
|
||||
@SerialId(11) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(12) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(13) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(14) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(15) val fileId: Long = 0L,
|
||||
@SerialId(16) val autoDownType: Int = 0,
|
||||
@SerialId(17) val uint32OrderDownType: List<Int>? = null,
|
||||
@SerialId(19) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(20) val httpsUrlFlag: Int = 0,
|
||||
@SerialId(26) val msgDownIp6: List<IPv6Info>? = null,
|
||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val fileid: Long = 0L,
|
||||
@ProtoId(2) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val result: Int = 0,
|
||||
@ProtoId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val msgImgInfo: ImgInfo? = null,
|
||||
@ProtoId(6) val bytesThumbDownUrl: List<ByteArray>? = null,
|
||||
@ProtoId(7) val bytesOriginalDownUrl: List<ByteArray>? = null,
|
||||
@ProtoId(8) val bytesBigDownUrl: List<ByteArray>? = null,
|
||||
@ProtoId(9) val uint32DownIp: List<Int>? = null,
|
||||
@ProtoId(10) val uint32DownPort: List<Int>? = null,
|
||||
@ProtoId(11) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(12) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(13) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(14) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(15) val fileId: Long = 0L,
|
||||
@ProtoId(16) val autoDownType: Int = 0,
|
||||
@ProtoId(17) val uint32OrderDownType: List<Int>? = null,
|
||||
@ProtoId(19) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(20) val httpsUrlFlag: Int = 0,
|
||||
@ProtoId(26) val msgDownIp6: List<IPv6Info>? = null,
|
||||
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GetPttUrlReq(
|
||||
@SerialId(1) val groupCode: Long = 0L,
|
||||
@SerialId(2) val dstUin: Long = 0L,
|
||||
@SerialId(3) val fileid: Long = 0L,
|
||||
@SerialId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val reqTerm: Int = 0,
|
||||
@SerialId(6) val reqPlatformType: Int = 0,
|
||||
@SerialId(7) val innerIp: Int = 0,
|
||||
@SerialId(8) val buType: Int = 0,
|
||||
@SerialId(9) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(10) val fileId: Long = 0L,
|
||||
@SerialId(11) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(12) val codec: Int = 0,
|
||||
@SerialId(13) val buId: Int = 0,
|
||||
@SerialId(14) val reqTransferType: Int = 0,
|
||||
@SerialId(15) val isAuto: Int = 0
|
||||
@ProtoId(1) val groupCode: Long = 0L,
|
||||
@ProtoId(2) val dstUin: Long = 0L,
|
||||
@ProtoId(3) val fileid: Long = 0L,
|
||||
@ProtoId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val reqTerm: Int = 0,
|
||||
@ProtoId(6) val reqPlatformType: Int = 0,
|
||||
@ProtoId(7) val innerIp: Int = 0,
|
||||
@ProtoId(8) val buType: Int = 0,
|
||||
@ProtoId(9) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(10) val fileId: Long = 0L,
|
||||
@ProtoId(11) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(12) val codec: Int = 0,
|
||||
@ProtoId(13) val buId: Int = 0,
|
||||
@ProtoId(14) val reqTransferType: Int = 0,
|
||||
@ProtoId(15) val isAuto: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GetPttUrlRsp(
|
||||
@SerialId(1) val fileid: Long = 0L,
|
||||
@SerialId(2) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val result: Int = 0,
|
||||
@SerialId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val bytesDownUrl: List<ByteArray>? = null,
|
||||
@SerialId(6) val uint32DownIp: List<Int>? = null,
|
||||
@SerialId(7) val uint32DownPort: List<Int>? = null,
|
||||
@SerialId(8) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(9) val downPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(10) val fileId: Long = 0L,
|
||||
@SerialId(11) val transferType: Int = 0,
|
||||
@SerialId(12) val allowRetry: Int = 0,
|
||||
@SerialId(26) val msgDownIp6: List<IPv6Info>? = null,
|
||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(28) val strDomain: String = ""
|
||||
@ProtoId(1) val fileid: Long = 0L,
|
||||
@ProtoId(2) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val result: Int = 0,
|
||||
@ProtoId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val bytesDownUrl: List<ByteArray>? = null,
|
||||
@ProtoId(6) val uint32DownIp: List<Int>? = null,
|
||||
@ProtoId(7) val uint32DownPort: List<Int>? = null,
|
||||
@ProtoId(8) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(9) val downPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(10) val fileId: Long = 0L,
|
||||
@ProtoId(11) val transferType: Int = 0,
|
||||
@ProtoId(12) val allowRetry: Int = 0,
|
||||
@ProtoId(26) val msgDownIp6: List<IPv6Info>? = null,
|
||||
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(28) val strDomain: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Suppress("ArrayInDataClass")
|
||||
@Serializable
|
||||
data class ImgInfo(
|
||||
@SerialId(1) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val fileType: Int = 0,
|
||||
@SerialId(3) val fileSize: Long = 0L,
|
||||
@SerialId(4) val fileWidth: Int = 0,
|
||||
@SerialId(5) val fileHeight: Int = 0
|
||||
@ProtoId(1) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val fileType: Int = 0,
|
||||
@ProtoId(3) val fileSize: Long = 0L,
|
||||
@ProtoId(4) val fileWidth: Int = 0,
|
||||
@ProtoId(5) val fileHeight: Int = 0
|
||||
) : ProtoBuf {
|
||||
override fun toString(): String {
|
||||
return "ImgInfo(fileMd5=${fileMd5.contentToString()}, fileType=$fileType, fileSize=$fileSize, fileWidth=$fileWidth, fileHeight=$fileHeight)"
|
||||
@ -161,128 +161,128 @@ internal class Cmd0x388 : ProtoBuf {
|
||||
|
||||
@Serializable
|
||||
class IPv6Info(
|
||||
@SerialId(1) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val port: Int = 0
|
||||
@ProtoId(1) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val port: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class PicSize(
|
||||
@SerialId(1) val original: Int = 0,
|
||||
@SerialId(2) val thumb: Int = 0,
|
||||
@SerialId(3) val high: Int = 0
|
||||
@ProtoId(1) val original: Int = 0,
|
||||
@ProtoId(2) val thumb: Int = 0,
|
||||
@ProtoId(3) val high: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ReqBody(
|
||||
@SerialId(1) val netType: Int = 0,
|
||||
@SerialId(2) val subcmd: Int = 0,
|
||||
@SerialId(3) val msgTryupImgReq: List<TryUpImgReq>? = null,
|
||||
@SerialId(4) val msgGetimgUrlReq: List<GetImgUrlReq>? = null,
|
||||
@SerialId(5) val msgTryupPttReq: List<TryUpPttReq>? = null,
|
||||
@SerialId(6) val msgGetpttUrlReq: List<GetPttUrlReq>? = null,
|
||||
@SerialId(7) val commandId: Int = 0,
|
||||
@SerialId(8) val msgDelImgReq: List<DelImgReq>? = null,
|
||||
@SerialId(1001) val extension: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val netType: Int = 0,
|
||||
@ProtoId(2) val subcmd: Int = 0,
|
||||
@ProtoId(3) val msgTryupImgReq: List<TryUpImgReq>? = null,
|
||||
@ProtoId(4) val msgGetimgUrlReq: List<GetImgUrlReq>? = null,
|
||||
@ProtoId(5) val msgTryupPttReq: List<TryUpPttReq>? = null,
|
||||
@ProtoId(6) val msgGetpttUrlReq: List<GetPttUrlReq>? = null,
|
||||
@ProtoId(7) val commandId: Int = 0,
|
||||
@ProtoId(8) val msgDelImgReq: List<DelImgReq>? = null,
|
||||
@ProtoId(1001) val extension: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RspBody(
|
||||
@SerialId(1) val clientIp: Int = 0,
|
||||
@SerialId(2) val subcmd: Int = 0,
|
||||
@SerialId(3) val msgTryupImgRsp: List<TryUpImgRsp>? = null,
|
||||
@SerialId(4) val msgGetimgUrlRsp: List<GetImgUrlRsp>? = null,
|
||||
@SerialId(5) val msgTryupPttRsp: List<TryUpPttRsp>? = null,
|
||||
@SerialId(6) val msgGetpttUrlRsp: List<GetPttUrlRsp>? = null,
|
||||
@SerialId(7) val msgDelImgRsp: List<DelImgRsp>? = null
|
||||
@ProtoId(1) val clientIp: Int = 0,
|
||||
@ProtoId(2) val subcmd: Int = 0,
|
||||
@ProtoId(3) val msgTryupImgRsp: List<TryUpImgRsp>? = null,
|
||||
@ProtoId(4) val msgGetimgUrlRsp: List<GetImgUrlRsp>? = null,
|
||||
@ProtoId(5) val msgTryupPttRsp: List<TryUpPttRsp>? = null,
|
||||
@ProtoId(6) val msgGetpttUrlRsp: List<GetPttUrlRsp>? = null,
|
||||
@ProtoId(7) val msgDelImgRsp: List<DelImgRsp>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class TryUpImgReq(
|
||||
@SerialId(1) val groupCode: Long = 0L,
|
||||
@SerialId(2) val srcUin: Long = 0L,
|
||||
@SerialId(3) val fileId: Long = 0L,
|
||||
@SerialId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val fileSize: Long = 0L,
|
||||
@SerialId(6) val fileName: String ="",
|
||||
@SerialId(7) val srcTerm: Int = 0,
|
||||
@SerialId(8) val platformType: Int = 0,
|
||||
@SerialId(9) val buType: Int = 0,
|
||||
@SerialId(10) val picWidth: Int = 0,
|
||||
@SerialId(11) val picHeight: Int = 0,
|
||||
@SerialId(12) val picType: Int = 0,
|
||||
@SerialId(13) val buildVer: String = "",
|
||||
@SerialId(14) val innerIp: Int = 0,
|
||||
@SerialId(15) val appPicType: Int = 0,
|
||||
@SerialId(16) val originalPic: Int = 0,
|
||||
@SerialId(17) val fileIndex: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(18) val dstUin: Long = 0L,
|
||||
@SerialId(19) val srvUpload: Int = 0,
|
||||
@SerialId(20) val transferUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val groupCode: Long = 0L,
|
||||
@ProtoId(2) val srcUin: Long = 0L,
|
||||
@ProtoId(3) val fileId: Long = 0L,
|
||||
@ProtoId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val fileSize: Long = 0L,
|
||||
@ProtoId(6) val fileName: String = "",
|
||||
@ProtoId(7) val srcTerm: Int = 0,
|
||||
@ProtoId(8) val platformType: Int = 0,
|
||||
@ProtoId(9) val buType: Int = 0,
|
||||
@ProtoId(10) val picWidth: Int = 0,
|
||||
@ProtoId(11) val picHeight: Int = 0,
|
||||
@ProtoId(12) val picType: Int = 0,
|
||||
@ProtoId(13) val buildVer: String = "",
|
||||
@ProtoId(14) val innerIp: Int = 0,
|
||||
@ProtoId(15) val appPicType: Int = 0,
|
||||
@ProtoId(16) val originalPic: Int = 0,
|
||||
@ProtoId(17) val fileIndex: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(18) val dstUin: Long = 0L,
|
||||
@ProtoId(19) val srvUpload: Int = 0,
|
||||
@ProtoId(20) val transferUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ImgReq
|
||||
|
||||
@Serializable
|
||||
class TryUpImgRsp(
|
||||
@SerialId(1) val fileId: Long = 0L,
|
||||
@SerialId(2) val result: Int = 0,
|
||||
@SerialId(3) val failMsg: String = "",
|
||||
@SerialId(4) val boolFileExit: Boolean = false,
|
||||
@SerialId(5) val msgImgInfo: ImgInfo? = null,
|
||||
@SerialId(6) val uint32UpIp: List<Int>? = null,
|
||||
@SerialId(7) val uint32UpPort: List<Int>? = null,
|
||||
@SerialId(8) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(9) val fileid: Long = 0L,
|
||||
@SerialId(10) val upOffset: Long = 0L,
|
||||
@SerialId(11) val blockSize: Long = 0L,
|
||||
@SerialId(12) val boolNewBigChan: Boolean = false,
|
||||
@SerialId(26) val msgUpIp6: List<IPv6Info>? = null,
|
||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(1001) val msgInfo4busi: TryUpInfo4Busi? = null
|
||||
@ProtoId(1) val fileId: Long = 0L,
|
||||
@ProtoId(2) val result: Int = 0,
|
||||
@ProtoId(3) val failMsg: String = "",
|
||||
@ProtoId(4) val boolFileExit: Boolean = false,
|
||||
@ProtoId(5) val msgImgInfo: ImgInfo? = null,
|
||||
@ProtoId(6) val uint32UpIp: List<Int>? = null,
|
||||
@ProtoId(7) val uint32UpPort: List<Int>? = null,
|
||||
@ProtoId(8) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(9) val fileid: Long = 0L,
|
||||
@ProtoId(10) val upOffset: Long = 0L,
|
||||
@ProtoId(11) val blockSize: Long = 0L,
|
||||
@ProtoId(12) val boolNewBigChan: Boolean = false,
|
||||
@ProtoId(26) val msgUpIp6: List<IPv6Info>? = null,
|
||||
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(1001) val msgInfo4busi: TryUpInfo4Busi? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class TryUpInfo4Busi(
|
||||
@SerialId(1) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val thumbDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val originalDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val bigDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val thumbDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val originalDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val bigDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class TryUpPttReq(
|
||||
@SerialId(1) val groupCode: Long = 0L,
|
||||
@SerialId(2) val srcUin: Long = 0L,
|
||||
@SerialId(3) val fileId: Long = 0L,
|
||||
@SerialId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val fileSize: Long = 0L,
|
||||
@SerialId(6) val fileName: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(7) val srcTerm: Int = 0,
|
||||
@SerialId(8) val platformType: Int = 0,
|
||||
@SerialId(9) val buType: Int = 0,
|
||||
@SerialId(10) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(11) val innerIp: Int = 0,
|
||||
@SerialId(12) val voiceLength: Int = 0,
|
||||
@SerialId(13) val boolNewUpChan: Boolean = false,
|
||||
@SerialId(14) val codec: Int = 0,
|
||||
@SerialId(15) val voiceType: Int = 0,
|
||||
@SerialId(16) val buId: Int = 0
|
||||
@ProtoId(1) val groupCode: Long = 0L,
|
||||
@ProtoId(2) val srcUin: Long = 0L,
|
||||
@ProtoId(3) val fileId: Long = 0L,
|
||||
@ProtoId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val fileSize: Long = 0L,
|
||||
@ProtoId(6) val fileName: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val srcTerm: Int = 0,
|
||||
@ProtoId(8) val platformType: Int = 0,
|
||||
@ProtoId(9) val buType: Int = 0,
|
||||
@ProtoId(10) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(11) val innerIp: Int = 0,
|
||||
@ProtoId(12) val voiceLength: Int = 0,
|
||||
@ProtoId(13) val boolNewUpChan: Boolean = false,
|
||||
@ProtoId(14) val codec: Int = 0,
|
||||
@ProtoId(15) val voiceType: Int = 0,
|
||||
@ProtoId(16) val buId: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class TryUpPttRsp(
|
||||
@SerialId(1) val fileId: Long = 0L,
|
||||
@SerialId(2) val result: Int = 0,
|
||||
@SerialId(3) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val boolFileExit: Boolean = false,
|
||||
@SerialId(5) val uint32UpIp: List<Int>? = null,
|
||||
@SerialId(6) val uint32UpPort: List<Int>? = null,
|
||||
@SerialId(7) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(8) val fileid: Long = 0L,
|
||||
@SerialId(9) val upOffset: Long = 0L,
|
||||
@SerialId(10) val blockSize: Long = 0L,
|
||||
@SerialId(11) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(12) val channelType: Int = 0,
|
||||
@SerialId(26) val msgUpIp6: List<IPv6Info>? = null,
|
||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val fileId: Long = 0L,
|
||||
@ProtoId(2) val result: Int = 0,
|
||||
@ProtoId(3) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val boolFileExit: Boolean = false,
|
||||
@ProtoId(5) val uint32UpIp: List<Int>? = null,
|
||||
@ProtoId(6) val uint32UpPort: List<Int>? = null,
|
||||
@ProtoId(7) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(8) val fileid: Long = 0L,
|
||||
@ProtoId(9) val upOffset: Long = 0L,
|
||||
@ProtoId(10) val blockSize: Long = 0L,
|
||||
@ProtoId(11) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(12) val channelType: Int = 0,
|
||||
@ProtoId(26) val msgUpIp6: List<IPv6Info>? = null,
|
||||
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
}
|
@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("SpellCheckingInspection")
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import kotlinx.serialization.protobuf.ProtoNumberType
|
||||
import kotlinx.serialization.protobuf.ProtoType
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
class GroupOpenSysMsg : ProtoBuf {
|
||||
@Serializable
|
||||
class LightApp(
|
||||
@ProtoId(1) val app: String = "",
|
||||
@ProtoId(2) val view: String = "",
|
||||
@ProtoId(3) val desc: String = "",
|
||||
@ProtoId(4) val prompt: String = "",
|
||||
@ProtoId(5) val ver: String = "",
|
||||
@ProtoId(6) val meta: String = "",
|
||||
@ProtoId(7) val config: String = "",
|
||||
@ProtoId(8) val source: Source? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RichMsg(
|
||||
@ProtoId(1) val title: String = "",
|
||||
@ProtoId(2) val desc: String = "",
|
||||
@ProtoId(3) val brief: String = "",
|
||||
@ProtoId(4) val cover: String = "",
|
||||
@ProtoId(5) val url: String = "",
|
||||
@ProtoId(6) val source: Source? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Sender(
|
||||
@ProtoId(1) val uin: Long = 0L,
|
||||
@ProtoId(2) val nick: String = "",
|
||||
@ProtoId(3) val avatar: String = "",
|
||||
@ProtoId(4) val url: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Source(
|
||||
@ProtoId(1) val name: String = "",
|
||||
@ProtoId(2) val icon: String = "",
|
||||
@ProtoId(3) val url: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class SysMsgBody(
|
||||
@ProtoId(1) val groupId: Long = 0L,
|
||||
@ProtoId(2) val appid: Long = 0L,
|
||||
@ProtoId(3) val sender: Sender? = null,
|
||||
@ProtoId(4) val msgType: Int = 0,
|
||||
@ProtoId(5) val content: String = "",
|
||||
@ProtoId(6) val richMsg: RichMsg? = null,
|
||||
@ProtoId(7) val lightApp: LightApp? = null
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class TroopTips0x857 : ProtoBuf {
|
||||
@Serializable
|
||||
class AIOGrayTipsInfo(
|
||||
@ProtoId(1) val optUint32ShowLastest: Int = 0,
|
||||
@ProtoId(2) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val optUint32Remind: Int = 0,
|
||||
@ProtoId(4) val optBytesBrief: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val receiverUin: Long = 0L,
|
||||
@ProtoId(6) val reliaoAdminOpt: Int = 0,
|
||||
@ProtoId(7) val robotGroupOpt: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class AIOTopTipsInfo(
|
||||
@ProtoId(1) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val optUint32Icon: Int = 0,
|
||||
@ProtoId(3) val optEnumAction: Int /* enum */ = 1,
|
||||
@ProtoId(4) val optBytesUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val optBytesData: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val optBytesDataI: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val optBytesDataA: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(8) val optBytesDataP: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class FloatedTipsInfo(
|
||||
@ProtoId(1) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GeneralGrayTipInfo(
|
||||
@ProtoId(1) val busiType: Long = 0L,
|
||||
@ProtoId(2) val busiId: Long = 0L,
|
||||
@ProtoId(3) val ctrlFlag: Int = 0,
|
||||
@ProtoId(4) val c2cType: Int = 0,
|
||||
@ProtoId(5) val serviceType: Int = 0,
|
||||
@ProtoId(6) val templId: Long = 0L,
|
||||
@ProtoId(7) val msgTemplParam: List<TemplParam>? = null,
|
||||
@ProtoId(8) val content: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(10) val tipsSeqId: Long = 0L,
|
||||
@ProtoId(100) val pbReserv: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GoldMsgTipsElem(
|
||||
@ProtoId(1) val type: Int = 0,
|
||||
@ProtoId(2) val billno: String = "",
|
||||
@ProtoId(3) val result: Int = 0,
|
||||
@ProtoId(4) val amount: Int = 0,
|
||||
@ProtoId(5) val total: Int = 0,
|
||||
@ProtoId(6) val interval: Int = 0,
|
||||
@ProtoId(7) val finish: Int = 0,
|
||||
@ProtoId(8) val uin: List<Long>? = null,
|
||||
@ProtoId(9) val action: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GroupInfoChange(
|
||||
@ProtoId(1) val groupHonorSwitch: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GroupNotifyInfo(
|
||||
@ProtoId(1) val optUint32AutoPullFlag: Int = 0,
|
||||
@ProtoId(2) val optBytesFeedsId: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class InstCtrl(
|
||||
@ProtoId(1) val msgSendToInst: List<InstInfo>? = null,
|
||||
@ProtoId(2) val msgExcludeInst: List<InstInfo>? = null,
|
||||
@ProtoId(3) val msgFromInst: InstInfo? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class InstInfo(
|
||||
@ProtoId(1) val apppid: Int = 0,
|
||||
@ProtoId(2) val instid: Int = 0,
|
||||
@ProtoId(3) val platform: Int = 0,
|
||||
@ProtoId(4) val openAppid: Int = 0,
|
||||
@ProtoId(5) val productid: Int = 0,
|
||||
@ProtoId(6) val ssoBid: Int = 0,
|
||||
@ProtoId(7) val guid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(8) val verMin: Int = 0,
|
||||
@ProtoId(9) val verMax: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class LbsShareChangePushInfo(
|
||||
@ProtoId(1) val msgType: Int = 0,
|
||||
@ProtoId(2) val msgInfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val versionCtrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val groupId: Long = 0L,
|
||||
@ProtoId(5) val operUin: Long = 0L,
|
||||
@ProtoId(6) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val msgSeq: Long = 0L,
|
||||
@ProtoId(8) val joinNums: Int = 0,
|
||||
@ProtoId(99) val pushType: Int = 0,
|
||||
@ProtoId(100) val extInfo: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class LuckyBagNotify(
|
||||
@ProtoId(1) val msgTips: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MediaChangePushInfo(
|
||||
@ProtoId(1) val msgType: Int = 0,
|
||||
@ProtoId(2) val msgInfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val versionCtrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val groupId: Long = 0L,
|
||||
@ProtoId(5) val operUin: Long = 0L,
|
||||
@ProtoId(6) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val msgSeq: Long = 0L,
|
||||
@ProtoId(8) val joinNums: Int = 0,
|
||||
@ProtoId(9) val msgPerSetting: PersonalSetting? = null,
|
||||
@ProtoId(10) val playMode: Int = 0,
|
||||
@ProtoId(99) val mediaType: Int = 0,
|
||||
@ProtoId(100) val extInfo: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf {
|
||||
@Serializable
|
||||
class PersonalSetting(
|
||||
@ProtoId(1) val themeId: Int = 0,
|
||||
@ProtoId(2) val playerId: Int = 0,
|
||||
@ProtoId(3) val fontId: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class MessageBoxInfo(
|
||||
@ProtoId(1) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val optBytesTitle: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val optBytesButton: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MessageRecallReminder(
|
||||
@ProtoId(1) val uin: Long = 0L,
|
||||
@ProtoId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val recalledMsgList: List<MessageMeta> = listOf(),
|
||||
@ProtoId(4) val reminderContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val userdef: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val groupType: Int = 0,
|
||||
@ProtoId(7) val opType: Int = 0
|
||||
) : ProtoBuf {
|
||||
@Serializable
|
||||
class MessageMeta(
|
||||
@ProtoId(1) val seq: Int = 0,
|
||||
@ProtoId(2) val time: Int = 0,
|
||||
@ProtoId(3) val msgRandom: Int = 0,
|
||||
@ProtoId(4) val msgType: Int = 0,
|
||||
@ProtoId(5) val msgFlag: Int = 0,
|
||||
@ProtoId(6) val authorUin: Long = 0L
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class MiniAppNotify(
|
||||
@ProtoId(1) val msg: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class NotifyMsgBody(
|
||||
@ProtoId(1) val optEnumType: Int /* enum */ = 1,
|
||||
@ProtoId(2) val optUint64MsgTime: Long = 0L,
|
||||
@ProtoId(3) val optUint64MsgExpires: Long = 0L,
|
||||
@ProtoId(4) val optUint64GroupCode: Long = 0L,
|
||||
@ProtoId(5) val optMsgGraytips: AIOGrayTipsInfo? = null,
|
||||
@ProtoId(6) val optMsgMessagebox: MessageBoxInfo? = null,
|
||||
@ProtoId(7) val optMsgFloatedtips: FloatedTipsInfo? = null,
|
||||
@ProtoId(8) val optMsgToptips: AIOTopTipsInfo? = null,
|
||||
@ProtoId(9) val optMsgRedtips: RedGrayTipsInfo? = null,
|
||||
@ProtoId(10) val optMsgGroupNotify: GroupNotifyInfo? = null,
|
||||
@ProtoId(11) val optMsgRecall: MessageRecallReminder? = null,
|
||||
@ProtoId(12) val optMsgThemeNotify: ThemeStateNotify? = null,
|
||||
@ProtoId(13) val serviceType: Int = 0,
|
||||
@ProtoId(14) val optMsgObjmsgUpdate: NotifyObjmsgUpdate? = null,
|
||||
@ProtoId(15) val optMsgWerewolfPush: WereWolfPush? = null,
|
||||
// @SerialId(16) val optStcmGameState: ApolloGameStatus.STCMGameMessage? = null,
|
||||
// @SerialId(17) val aplloMsgPush: ApolloPushMsgInfo.STPushMsgElem? = null,
|
||||
@ProtoId(18) val optMsgGoldtips: GoldMsgTipsElem? = null,
|
||||
@ProtoId(20) val optMsgMiniappNotify: MiniAppNotify? = null,
|
||||
@ProtoId(21) val optUint64SenderUin: Long = 0L,
|
||||
@ProtoId(22) val optMsgLuckybagNotify: LuckyBagNotify? = null,
|
||||
@ProtoId(23) val optMsgTroopformtipsPush: TroopFormGrayTipsInfo? = null,
|
||||
@ProtoId(24) val optMsgMediaPush: MediaChangePushInfo? = null,
|
||||
@ProtoId(26) val optGeneralGrayTip: GeneralGrayTipInfo? = null,
|
||||
@ProtoId(27) val optMsgVideoPush: VideoChangePushInfo? = null,
|
||||
@ProtoId(28) val optLbsShareChangePlusInfo: LbsShareChangePushInfo? = null,
|
||||
@ProtoId(29) val optMsgSingPush: SingChangePushInfo? = null,
|
||||
@ProtoId(30) val optMsgGroupInfoChange: GroupInfoChange? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class NotifyObjmsgUpdate(
|
||||
@ProtoId(1) val objmsgId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val updateType: Int = 0,
|
||||
@ProtoId(3) val extMsg: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RedGrayTipsInfo(
|
||||
@ProtoId(1) val optUint32ShowLastest: Int = 0,
|
||||
@ProtoId(2) val senderUin: Long = 0L,
|
||||
@ProtoId(3) val receiverUin: Long = 0L,
|
||||
@ProtoId(4) val senderRichContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val receiverRichContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val authkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoType(ProtoNumberType.SIGNED) @ProtoId(7) val sint32Msgtype: Int = 0,
|
||||
@ProtoId(8) val luckyFlag: Int = 0,
|
||||
@ProtoId(9) val hideFlag: Int = 0,
|
||||
@ProtoId(10) val pcBody: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(11) val icon: Int = 0,
|
||||
@ProtoId(12) val luckyUin: Long = 0L,
|
||||
@ProtoId(13) val time: Int = 0,
|
||||
@ProtoId(14) val random: Int = 0,
|
||||
@ProtoId(15) val broadcastRichContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(16) val idiom: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(17) val idiomSeq: Int = 0,
|
||||
@ProtoId(18) val idiomAlpha: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(19) val jumpurl: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ReqBody(
|
||||
@ProtoId(1) val optUint64GroupCode: Long = 0L,
|
||||
@ProtoId(2) val uint64Memberuins: List<Long>? = null,
|
||||
@ProtoId(3) val optUint32Offline: Int = 0,
|
||||
@ProtoId(4) val msgInstCtrl: InstCtrl? = null,
|
||||
@ProtoId(5) val optBytesMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val optUint32BusiType: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RspBody(
|
||||
@ProtoId(1) val optUint64GroupCode: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class SingChangePushInfo(
|
||||
@ProtoId(1) val seq: Long = 0L,
|
||||
@ProtoId(2) val actionType: Int = 0,
|
||||
@ProtoId(3) val groupId: Long = 0L,
|
||||
@ProtoId(4) val operUin: Long = 0L,
|
||||
@ProtoId(5) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val joinNums: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class TemplParam(
|
||||
@ProtoId(1) val name: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val value: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ThemeStateNotify(
|
||||
@ProtoId(1) val state: Int = 0,
|
||||
@ProtoId(2) val feedsId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val themeName: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val actionUin: Long = 0L,
|
||||
@ProtoId(5) val createUin: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class TroopFormGrayTipsInfo(
|
||||
@ProtoId(1) val writerUin: Long = 0L,
|
||||
@ProtoId(2) val creatorUin: Long = 0L,
|
||||
@ProtoId(3) val richContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val optBytesUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val creatorNick: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class VideoChangePushInfo(
|
||||
@ProtoId(1) val seq: Long = 0L,
|
||||
@ProtoId(2) val actionType: Int = 0,
|
||||
@ProtoId(3) val groupId: Long = 0L,
|
||||
@ProtoId(4) val operUin: Long = 0L,
|
||||
@ProtoId(5) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val joinNums: Int = 0,
|
||||
@ProtoId(100) val extInfo: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class WereWolfPush(
|
||||
@ProtoId(1) val pushType: Int = 0,
|
||||
@ProtoId(2) val gameRoom: Long = 0L,
|
||||
@ProtoId(3) val enumGameState: Int = 0,
|
||||
@ProtoId(4) val gameRound: Int = 0,
|
||||
@ProtoId(5) val roles: List<Role>? = null,
|
||||
@ProtoId(6) val speaker: Long = 0L,
|
||||
@ProtoId(7) val judgeUin: Long = 0L,
|
||||
@ProtoId(8) val judgeWords: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(9) val enumOperation: Int = 0,
|
||||
@ProtoId(10) val srcUser: Long = 0L,
|
||||
@ProtoId(11) val dstUser: Long = 0L,
|
||||
@ProtoId(12) val deadUsers: List<Long>? = null,
|
||||
@ProtoId(13) val gameResult: Int = 0,
|
||||
@ProtoId(14) val timeoutSec: Int = 0,
|
||||
@ProtoId(15) val killConfirmed: Int = 0,
|
||||
@ProtoId(16) val judgeNickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(17) val votedTieUsers: List<Long>? = null
|
||||
) : ProtoBuf {
|
||||
@Serializable
|
||||
class GameRecord(
|
||||
@ProtoId(1) val total: Int = 0,
|
||||
@ProtoId(2) val win: Int = 0,
|
||||
@ProtoId(3) val lose: Int = 0,
|
||||
@ProtoId(4) val draw: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Role(
|
||||
@ProtoId(1) val uin: Long = 0L,
|
||||
@ProtoId(2) val enumType: Int = 0,
|
||||
@ProtoId(3) val enumState: Int = 0,
|
||||
@ProtoId(4) val canSpeak: Int = 0,
|
||||
@ProtoId(5) val canListen: Int = 0,
|
||||
@ProtoId(6) val position: Int = 0,
|
||||
@ProtoId(7) val canVote: Int = 0,
|
||||
@ProtoId(8) val canVoted: Int = 0,
|
||||
@ProtoId(9) val alreadyChecked: Int = 0,
|
||||
@ProtoId(10) val alreadySaved: Int = 0,
|
||||
@ProtoId(11) val alreadyPoisoned: Int = 0,
|
||||
@ProtoId(12) val playerState: Int = 0,
|
||||
@ProtoId(13) val enumDeadOp: Int = 0,
|
||||
@ProtoId(14) val enumOperation: Int = 0,
|
||||
@ProtoId(15) val dstUser: Long = 0L,
|
||||
@ProtoId(16) val operationRound: Int = 0,
|
||||
@ProtoId(17) val msgGameRecord: GameRecord? = null,
|
||||
@ProtoId(18) val isWerewolf: Int = 0,
|
||||
@ProtoId(19) val defendedUser: Long = 0L,
|
||||
@ProtoId(20) val isSheriff: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("SpellCheckingInspection")
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import kotlinx.serialization.protobuf.ProtoNumberType
|
||||
import kotlinx.serialization.protobuf.ProtoType
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
@Serializable
|
||||
class Oidb0x858 : ProtoBuf {
|
||||
@Serializable
|
||||
class GoldMsgTipsElem(
|
||||
@ProtoId(1) val type: Int = 0,
|
||||
@ProtoId(2) val billno: String = "",
|
||||
@ProtoId(3) val result: Int = 0,
|
||||
@ProtoId(4) val amount: Int = 0,
|
||||
@ProtoId(5) val total: Int = 0,
|
||||
@ProtoId(6) val interval: Int = 0,
|
||||
@ProtoId(7) val finish: Int = 0,
|
||||
@ProtoId(8) val uin: List<Long>? = null,
|
||||
@ProtoId(9) val action: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MessageRecallReminder(
|
||||
@ProtoId(1) val uin: Long = 0L,
|
||||
@ProtoId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val recalledMsgList: List<MessageMeta> = listOf(),
|
||||
@ProtoId(4) val reminderContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val userdef: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf {
|
||||
@Serializable
|
||||
class MessageMeta(
|
||||
@ProtoId(1) val seq: Int = 0,
|
||||
@ProtoId(2) val time: Int = 0,
|
||||
@ProtoId(3) val msgRandom: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class NotifyMsgBody(
|
||||
@ProtoId(1) val optEnumType: Int /* enum */ = 5,
|
||||
@ProtoId(2) val optUint64MsgTime: Long = 0L,
|
||||
@ProtoId(3) val optUint64MsgExpires: Long = 0L,
|
||||
@ProtoId(4) val optUint64ConfUin: Long = 0L,
|
||||
@ProtoId(5) val optMsgRedtips: RedGrayTipsInfo? = null,
|
||||
@ProtoId(6) val optMsgRecallReminder: MessageRecallReminder? = null,
|
||||
@ProtoId(7) val optMsgObjUpdate: NotifyObjmsgUpdate? = null,
|
||||
// @SerialId(8) val optStcmGameState: ApolloGameStatus.STCMGameMessage? = null,
|
||||
// @SerialId(9) val aplloMsgPush: ApolloPushMsgInfo.STPushMsgElem? = null,
|
||||
@ProtoId(10) val optMsgGoldtips: GoldMsgTipsElem? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class NotifyObjmsgUpdate(
|
||||
@ProtoId(1) val objmsgId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val updateType: Int = 0,
|
||||
@ProtoId(3) val extMsg: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RedGrayTipsInfo(
|
||||
@ProtoId(1) val optUint32ShowLastest: Int = 0,
|
||||
@ProtoId(2) val senderUin: Long = 0L,
|
||||
@ProtoId(3) val receiverUin: Long = 0L,
|
||||
@ProtoId(4) val senderRichContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val receiverRichContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val authkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoType(ProtoNumberType.SIGNED) @ProtoId(7) val sint32Msgtype: Int = 0,
|
||||
@ProtoId(8) val luckyFlag: Int = 0,
|
||||
@ProtoId(9) val hideFlag: Int = 0,
|
||||
@ProtoId(10) val pcBody: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(11) val icon: Int = 0,
|
||||
@ProtoId(12) val luckyUin: Long = 0L,
|
||||
@ProtoId(13) val time: Int = 0,
|
||||
@ProtoId(14) val random: Int = 0,
|
||||
@ProtoId(15) val broadcastRichContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(16) val idiom: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(17) val idiomSeq: Int = 0,
|
||||
@ProtoId(18) val idiomAlpha: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(19) val jumpurl: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Common : ProtoBuf {
|
||||
@Serializable
|
||||
class BindInfo(
|
||||
@SerialId(1) val friUin: Long = 0L,
|
||||
@SerialId(2) val friNick: String = "",
|
||||
@SerialId(3) val time: Long = 0L,
|
||||
@SerialId(4) val bindStatus: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MedalInfo(
|
||||
@SerialId(1) val id: Int = 0,
|
||||
@SerialId(2) val type: Int = 0,
|
||||
@SerialId(4) val seq: Long = 0,
|
||||
@SerialId(5) val name: String = "",
|
||||
@SerialId(6) val newflag: Int = 0,
|
||||
@SerialId(7) val time: Long = 0L,
|
||||
@SerialId(8) val msgBindFri: Common.BindInfo? = null,
|
||||
@SerialId(11) val desc: String = "",
|
||||
@SerialId(31) val level: Int = 0,
|
||||
@SerialId(36) val taskinfos: List<Common.MedalTaskInfo>? = null,
|
||||
@SerialId(40) val point: Int = 0,
|
||||
@SerialId(41) val pointLevel2: Int = 0,
|
||||
@SerialId(42) val pointLevel3: Int = 0,
|
||||
@SerialId(43) val seqLevel2: Long = 0,
|
||||
@SerialId(44) val seqLevel3: Long = 0,
|
||||
@SerialId(45) val timeLevel2: Long = 0L,
|
||||
@SerialId(46) val timeLevel3: Long = 0L,
|
||||
@SerialId(47) val descLevel2: String = "",
|
||||
@SerialId(48) val descLevel3: String = "",
|
||||
@SerialId(49) val endtime: Int = 0,
|
||||
@SerialId(50) val detailUrl: String = "",
|
||||
@SerialId(51) val detailUrl2: String = "",
|
||||
@SerialId(52) val detailUrl3: String = "",
|
||||
@SerialId(53) val taskDesc: String = "",
|
||||
@SerialId(54) val taskDesc2: String = "",
|
||||
@SerialId(55) val taskDesc3: String = "",
|
||||
@SerialId(56) val levelCount: Int = 0,
|
||||
@SerialId(57) val noProgress: Int = 0,
|
||||
@SerialId(58) val resource: String = "",
|
||||
@SerialId(59) val fromuinLevel: Int = 0,
|
||||
@SerialId(60) val unread: Int = 0,
|
||||
@SerialId(61) val unread2: Int = 0,
|
||||
@SerialId(62) val unread3: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MedalTaskInfo(
|
||||
@SerialId(1) val taskid: Int = 0,
|
||||
@SerialId(32) val int32TaskValue: Int = 0,
|
||||
@SerialId(33) val tarValue: Int = 0,
|
||||
@SerialId(34) val tarValueLevel2: Int = 0,
|
||||
@SerialId(35) val tarValueLevel3: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
@ -18,56 +18,56 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
class Common : ProtoBuf {
|
||||
@Serializable
|
||||
class BindInfo(
|
||||
@SerialId(1) val friUin: Long = 0L,
|
||||
@SerialId(2) val friNick: String = "",
|
||||
@SerialId(3) val time: Long = 0L,
|
||||
@SerialId(4) val bindStatus: Int = 0
|
||||
@ProtoId(1) val friUin: Long = 0L,
|
||||
@ProtoId(2) val friNick: String = "",
|
||||
@ProtoId(3) val time: Long = 0L,
|
||||
@ProtoId(4) val bindStatus: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MedalInfo(
|
||||
@SerialId(1) val id: Int = 0,
|
||||
@SerialId(2) val type: Int = 0,
|
||||
@SerialId(4) val seq: Long = 0,
|
||||
@SerialId(5) val name: String = "",
|
||||
@SerialId(6) val newflag: Int = 0,
|
||||
@SerialId(7) val time: Long = 0L,
|
||||
@SerialId(8) val msgBindFri: Common.BindInfo? = null,
|
||||
@SerialId(11) val desc: String = "",
|
||||
@SerialId(31) val level: Int = 0,
|
||||
@SerialId(36) val taskinfos: List<Common.MedalTaskInfo>? = null,
|
||||
@SerialId(40) val point: Int = 0,
|
||||
@SerialId(41) val pointLevel2: Int = 0,
|
||||
@SerialId(42) val pointLevel3: Int = 0,
|
||||
@SerialId(43) val seqLevel2: Long = 0,
|
||||
@SerialId(44) val seqLevel3: Long = 0,
|
||||
@SerialId(45) val timeLevel2: Long = 0L,
|
||||
@SerialId(46) val timeLevel3: Long = 0L,
|
||||
@SerialId(47) val descLevel2: String = "",
|
||||
@SerialId(48) val descLevel3: String = "",
|
||||
@SerialId(49) val endtime: Int = 0,
|
||||
@SerialId(50) val detailUrl: String = "",
|
||||
@SerialId(51) val detailUrl2: String = "",
|
||||
@SerialId(52) val detailUrl3: String = "",
|
||||
@SerialId(53) val taskDesc: String = "",
|
||||
@SerialId(54) val taskDesc2: String = "",
|
||||
@SerialId(55) val taskDesc3: String = "",
|
||||
@SerialId(56) val levelCount: Int = 0,
|
||||
@SerialId(57) val noProgress: Int = 0,
|
||||
@SerialId(58) val resource: String = "",
|
||||
@SerialId(59) val fromuinLevel: Int = 0,
|
||||
@SerialId(60) val unread: Int = 0,
|
||||
@SerialId(61) val unread2: Int = 0,
|
||||
@SerialId(62) val unread3: Int = 0
|
||||
@ProtoId(1) val id: Int = 0,
|
||||
@ProtoId(2) val type: Int = 0,
|
||||
@ProtoId(4) val seq: Long = 0,
|
||||
@ProtoId(5) val name: String = "",
|
||||
@ProtoId(6) val newflag: Int = 0,
|
||||
@ProtoId(7) val time: Long = 0L,
|
||||
@ProtoId(8) val msgBindFri: Common.BindInfo? = null,
|
||||
@ProtoId(11) val desc: String = "",
|
||||
@ProtoId(31) val level: Int = 0,
|
||||
@ProtoId(36) val taskinfos: List<Common.MedalTaskInfo>? = null,
|
||||
@ProtoId(40) val point: Int = 0,
|
||||
@ProtoId(41) val pointLevel2: Int = 0,
|
||||
@ProtoId(42) val pointLevel3: Int = 0,
|
||||
@ProtoId(43) val seqLevel2: Long = 0,
|
||||
@ProtoId(44) val seqLevel3: Long = 0,
|
||||
@ProtoId(45) val timeLevel2: Long = 0L,
|
||||
@ProtoId(46) val timeLevel3: Long = 0L,
|
||||
@ProtoId(47) val descLevel2: String = "",
|
||||
@ProtoId(48) val descLevel3: String = "",
|
||||
@ProtoId(49) val endtime: Int = 0,
|
||||
@ProtoId(50) val detailUrl: String = "",
|
||||
@ProtoId(51) val detailUrl2: String = "",
|
||||
@ProtoId(52) val detailUrl3: String = "",
|
||||
@ProtoId(53) val taskDesc: String = "",
|
||||
@ProtoId(54) val taskDesc2: String = "",
|
||||
@ProtoId(55) val taskDesc3: String = "",
|
||||
@ProtoId(56) val levelCount: Int = 0,
|
||||
@ProtoId(57) val noProgress: Int = 0,
|
||||
@ProtoId(58) val resource: String = "",
|
||||
@ProtoId(59) val fromuinLevel: Int = 0,
|
||||
@ProtoId(60) val unread: Int = 0,
|
||||
@ProtoId(61) val unread2: Int = 0,
|
||||
@ProtoId(62) val unread3: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MedalTaskInfo(
|
||||
@SerialId(1) val taskid: Int = 0,
|
||||
@SerialId(32) val int32TaskValue: Int = 0,
|
||||
@SerialId(33) val tarValue: Int = 0,
|
||||
@SerialId(34) val tarValueLevel2: Int = 0,
|
||||
@SerialId(35) val tarValueLevel3: Int = 0
|
||||
@ProtoId(1) val taskid: Int = 0,
|
||||
@ProtoId(32) val int32TaskValue: Int = 0,
|
||||
@ProtoId(33) val tarValue: Int = 0,
|
||||
@ProtoId(34) val tarValueLevel2: Int = 0,
|
||||
@ProtoId(35) val tarValueLevel3: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@ -75,475 +75,475 @@ class Common : ProtoBuf {
|
||||
class AppointDefine : ProtoBuf {
|
||||
@Serializable
|
||||
class ADFeedContent(
|
||||
@SerialId(1) val msgUserInfo: AppointDefine.UserInfo? = null,
|
||||
@SerialId(2) val strPicUrl: List<String> = listOf(),
|
||||
@SerialId(3) val msgText: AppointDefine.RichText? = null,
|
||||
@SerialId(4) val attendInfo: String = "",
|
||||
@SerialId(5) val actionUrl: String = "",
|
||||
@SerialId(6) val publishTime: Int = 0,
|
||||
@SerialId(7) val msgHotTopicList: AppointDefine.HotTopicList? = null,
|
||||
@SerialId(8) val moreUrl: String = "",
|
||||
@SerialId(9) val recordDuration: String = ""
|
||||
@ProtoId(1) val msgUserInfo: AppointDefine.UserInfo? = null,
|
||||
@ProtoId(2) val strPicUrl: List<String> = listOf(),
|
||||
@ProtoId(3) val msgText: AppointDefine.RichText? = null,
|
||||
@ProtoId(4) val attendInfo: String = "",
|
||||
@ProtoId(5) val actionUrl: String = "",
|
||||
@ProtoId(6) val publishTime: Int = 0,
|
||||
@ProtoId(7) val msgHotTopicList: AppointDefine.HotTopicList? = null,
|
||||
@ProtoId(8) val moreUrl: String = "",
|
||||
@ProtoId(9) val recordDuration: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RichText(
|
||||
@SerialId(1) val msgElems: List<AppointDefine.Elem>? = null
|
||||
@ProtoId(1) val msgElems: List<AppointDefine.Elem>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RankEvent(
|
||||
@SerialId(1) val listtype: Int = 0,
|
||||
@SerialId(2) val notifytype: Int = 0,
|
||||
@SerialId(3) val eventtime: Int = 0,
|
||||
@SerialId(4) val seq: Int = 0,
|
||||
@SerialId(5) val notifyTips: String = ""
|
||||
@ProtoId(1) val listtype: Int = 0,
|
||||
@ProtoId(2) val notifytype: Int = 0,
|
||||
@ProtoId(3) val eventtime: Int = 0,
|
||||
@ProtoId(4) val seq: Int = 0,
|
||||
@ProtoId(5) val notifyTips: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Wifi(
|
||||
@SerialId(1) val mac: Long = 0L,
|
||||
@SerialId(2) val int32Rssi: Int = 0
|
||||
@ProtoId(1) val mac: Long = 0L,
|
||||
@ProtoId(2) val int32Rssi: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class InterestItem(
|
||||
@SerialId(1) val tagId: Long = 0L,
|
||||
@SerialId(2) val tagName: String = "",
|
||||
@SerialId(3) val tagIconUrl: String = "",
|
||||
@SerialId(4) val tagHref: String = "",
|
||||
@SerialId(5) val tagBackColor: String = "",
|
||||
@SerialId(6) val tagFontColor: String = "",
|
||||
@SerialId(7) val tagVid: String = "",
|
||||
@SerialId(8) val tagType: Int = 0,
|
||||
@SerialId(9) val addTime: Int = 0,
|
||||
@SerialId(10) val tagCategory: String = "",
|
||||
@SerialId(11) val tagOtherUrl: String = "",
|
||||
@SerialId(12) val bid: Int = 0
|
||||
@ProtoId(1) val tagId: Long = 0L,
|
||||
@ProtoId(2) val tagName: String = "",
|
||||
@ProtoId(3) val tagIconUrl: String = "",
|
||||
@ProtoId(4) val tagHref: String = "",
|
||||
@ProtoId(5) val tagBackColor: String = "",
|
||||
@ProtoId(6) val tagFontColor: String = "",
|
||||
@ProtoId(7) val tagVid: String = "",
|
||||
@ProtoId(8) val tagType: Int = 0,
|
||||
@ProtoId(9) val addTime: Int = 0,
|
||||
@ProtoId(10) val tagCategory: String = "",
|
||||
@ProtoId(11) val tagOtherUrl: String = "",
|
||||
@ProtoId(12) val bid: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ShopID(
|
||||
@SerialId(1) val shopid: String = "",
|
||||
@SerialId(2) val sp: Int = 0
|
||||
@ProtoId(1) val shopid: String = "",
|
||||
@ProtoId(2) val sp: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class FeedComment(
|
||||
@SerialId(1) val commentId: String = "",
|
||||
@SerialId(2) val feedId: String = "",
|
||||
@SerialId(3) val msgPublisherInfo: AppointDefine.StrangerInfo? = null,
|
||||
@SerialId(4) val time: Int = 0,
|
||||
@SerialId(6) val msgReplyInfo: AppointDefine.ReplyInfo? = null,
|
||||
@SerialId(7) val flag: Int = 0,
|
||||
@SerialId(8) val msgContent: AppointDefine.RichText? = null,
|
||||
@SerialId(9) val hot: Int = 0
|
||||
@ProtoId(1) val commentId: String = "",
|
||||
@ProtoId(2) val feedId: String = "",
|
||||
@ProtoId(3) val msgPublisherInfo: AppointDefine.StrangerInfo? = null,
|
||||
@ProtoId(4) val time: Int = 0,
|
||||
@ProtoId(6) val msgReplyInfo: AppointDefine.ReplyInfo? = null,
|
||||
@ProtoId(7) val flag: Int = 0,
|
||||
@ProtoId(8) val msgContent: AppointDefine.RichText? = null,
|
||||
@ProtoId(9) val hot: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ADFeed(
|
||||
@SerialId(1) val taskId: Int = 0,
|
||||
@SerialId(2) val style: Int = 0,
|
||||
@SerialId(3) val content: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val taskId: Int = 0,
|
||||
@ProtoId(2) val style: Int = 0,
|
||||
@ProtoId(3) val content: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Cell(
|
||||
@SerialId(1) val int32Mcc: Int = -1,
|
||||
@SerialId(2) val int32Mnc: Int = -1,
|
||||
@SerialId(3) val int32Lac: Int = -1,
|
||||
@SerialId(4) val int32Cellid: Int = -1,
|
||||
@SerialId(5) val int32Rssi: Int = 0
|
||||
@ProtoId(1) val int32Mcc: Int = -1,
|
||||
@ProtoId(2) val int32Mnc: Int = -1,
|
||||
@ProtoId(3) val int32Lac: Int = -1,
|
||||
@ProtoId(4) val int32Cellid: Int = -1,
|
||||
@ProtoId(5) val int32Rssi: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RecentVistorEvent(
|
||||
@SerialId(1) val eventtype: Int = 0,
|
||||
@SerialId(2) val eventTinyid: Long = 0L,
|
||||
@SerialId(3) val unreadCount: Int = 0
|
||||
@ProtoId(1) val eventtype: Int = 0,
|
||||
@ProtoId(2) val eventTinyid: Long = 0L,
|
||||
@ProtoId(3) val unreadCount: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class OrganizerInfo(
|
||||
@SerialId(1) val hostName: String = "",
|
||||
@SerialId(2) val hostUrl: String = "",
|
||||
@SerialId(3) val hostCover: String = ""
|
||||
@ProtoId(1) val hostName: String = "",
|
||||
@ProtoId(2) val hostUrl: String = "",
|
||||
@ProtoId(3) val hostCover: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class InterestTag(
|
||||
@SerialId(1) val tagType: Int = 0,
|
||||
@SerialId(2) val msgTagList: List<AppointDefine.InterestItem>? = null
|
||||
@ProtoId(1) val tagType: Int = 0,
|
||||
@ProtoId(2) val msgTagList: List<AppointDefine.InterestItem>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class AppointInfoEx(
|
||||
@SerialId(1) val feedsPicUrl: String = "",
|
||||
@SerialId(2) val feedsUrl: String = "",
|
||||
@SerialId(3) val detailTitle: String = "",
|
||||
@SerialId(4) val detailDescribe: String = "",
|
||||
@SerialId(5) val showPublisher: Int = 0,
|
||||
@SerialId(6) val detailPicUrl: String = "",
|
||||
@SerialId(7) val detailUrl: String = "",
|
||||
@SerialId(8) val showAttend: Int = 0
|
||||
@ProtoId(1) val feedsPicUrl: String = "",
|
||||
@ProtoId(2) val feedsUrl: String = "",
|
||||
@ProtoId(3) val detailTitle: String = "",
|
||||
@ProtoId(4) val detailDescribe: String = "",
|
||||
@ProtoId(5) val showPublisher: Int = 0,
|
||||
@ProtoId(6) val detailPicUrl: String = "",
|
||||
@ProtoId(7) val detailUrl: String = "",
|
||||
@ProtoId(8) val showAttend: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class DateComment(
|
||||
@SerialId(1) val commentId: String = "",
|
||||
@SerialId(2) val msgAppointId: AppointDefine.AppointID? = null,
|
||||
@SerialId(3) val msgPublisherInfo: AppointDefine.StrangerInfo? = null,
|
||||
@SerialId(4) val time: Int = 0,
|
||||
@SerialId(6) val msgReplyInfo: AppointDefine.ReplyInfo? = null,
|
||||
@SerialId(7) val flag: Int = 0,
|
||||
@SerialId(8) val msgContent: AppointDefine.RichText? = null
|
||||
@ProtoId(1) val commentId: String = "",
|
||||
@ProtoId(2) val msgAppointId: AppointDefine.AppointID? = null,
|
||||
@ProtoId(3) val msgPublisherInfo: AppointDefine.StrangerInfo? = null,
|
||||
@ProtoId(4) val time: Int = 0,
|
||||
@ProtoId(6) val msgReplyInfo: AppointDefine.ReplyInfo? = null,
|
||||
@ProtoId(7) val flag: Int = 0,
|
||||
@ProtoId(8) val msgContent: AppointDefine.RichText? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class AppointContent(
|
||||
@SerialId(1) val appointSubject: Int = 0,
|
||||
@SerialId(2) val payType: Int = 0,
|
||||
@SerialId(3) val appointDate: Int = 0,
|
||||
@SerialId(4) val appointGender: Int = 0,
|
||||
@SerialId(5) val appointIntroduce: String = "",
|
||||
@SerialId(6) val msgAppointAddress: AppointDefine.AddressInfo? = null,
|
||||
@SerialId(7) val msgTravelInfo: AppointDefine.TravelInfo? = null
|
||||
@ProtoId(1) val appointSubject: Int = 0,
|
||||
@ProtoId(2) val payType: Int = 0,
|
||||
@ProtoId(3) val appointDate: Int = 0,
|
||||
@ProtoId(4) val appointGender: Int = 0,
|
||||
@ProtoId(5) val appointIntroduce: String = "",
|
||||
@ProtoId(6) val msgAppointAddress: AppointDefine.AddressInfo? = null,
|
||||
@ProtoId(7) val msgTravelInfo: AppointDefine.TravelInfo? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class FeedInfo(
|
||||
@SerialId(1) val feedType: Long = 0L,
|
||||
@SerialId(2) val feedId: String = "",
|
||||
@SerialId(3) val msgFeedContent: AppointDefine.FeedContent? = null,
|
||||
@SerialId(4) val msgTopicInfo: AppointDefine.NearbyTopic? = null,
|
||||
@SerialId(5) val publishTime: Long = 0,
|
||||
@SerialId(6) val praiseCount: Int = 0,
|
||||
@SerialId(7) val praiseFlag: Int = 0,
|
||||
@SerialId(8) val msgPraiseUser: List<AppointDefine.StrangerInfo>? = null,
|
||||
@SerialId(9) val commentCount: Int = 0,
|
||||
@SerialId(10) val msgCommentList: List<AppointDefine.FeedComment>? = null,
|
||||
@SerialId(11) val commentRetAll: Int = 0,
|
||||
@SerialId(12) val hotFlag: Int = 0,
|
||||
@SerialId(13) val svrReserved: Long = 0L,
|
||||
@SerialId(14) val msgHotEntry: AppointDefine.HotEntry? = null
|
||||
@ProtoId(1) val feedType: Long = 0L,
|
||||
@ProtoId(2) val feedId: String = "",
|
||||
@ProtoId(3) val msgFeedContent: AppointDefine.FeedContent? = null,
|
||||
@ProtoId(4) val msgTopicInfo: AppointDefine.NearbyTopic? = null,
|
||||
@ProtoId(5) val publishTime: Long = 0,
|
||||
@ProtoId(6) val praiseCount: Int = 0,
|
||||
@ProtoId(7) val praiseFlag: Int = 0,
|
||||
@ProtoId(8) val msgPraiseUser: List<AppointDefine.StrangerInfo>? = null,
|
||||
@ProtoId(9) val commentCount: Int = 0,
|
||||
@ProtoId(10) val msgCommentList: List<AppointDefine.FeedComment>? = null,
|
||||
@ProtoId(11) val commentRetAll: Int = 0,
|
||||
@ProtoId(12) val hotFlag: Int = 0,
|
||||
@ProtoId(13) val svrReserved: Long = 0L,
|
||||
@ProtoId(14) val msgHotEntry: AppointDefine.HotEntry? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class HotTopicList(
|
||||
@SerialId(1) val topicList: List<AppointDefine.HotTopic>? = null
|
||||
@ProtoId(1) val topicList: List<AppointDefine.HotTopic>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class FeedContent(
|
||||
@SerialId(1) val strPicUrl: List<String> = listOf(),
|
||||
@SerialId(2) val msgText: AppointDefine.RichText? = null,
|
||||
@SerialId(3) val hrefUrl: String = "",
|
||||
@SerialId(5) val groupName: String = "",
|
||||
@SerialId(6) val groupBulletin: String = "",
|
||||
@SerialId(7) val feedType: Int = 0,
|
||||
@SerialId(8) val poiId: String = "",
|
||||
@SerialId(9) val poiTitle: String = "",
|
||||
@SerialId(20) val effectiveTime: Int = 0,
|
||||
@SerialId(21) val expiationTime: Int = 0,
|
||||
@SerialId(22) val msgLocale: AppointDefine.LocaleInfo? = null,
|
||||
@SerialId(23) val feedsIndex: Int = 0,
|
||||
@SerialId(24) val msgAd: AppointDefine.ADFeed? = null,
|
||||
@SerialId(25) val privateData: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val strPicUrl: List<String> = listOf(),
|
||||
@ProtoId(2) val msgText: AppointDefine.RichText? = null,
|
||||
@ProtoId(3) val hrefUrl: String = "",
|
||||
@ProtoId(5) val groupName: String = "",
|
||||
@ProtoId(6) val groupBulletin: String = "",
|
||||
@ProtoId(7) val feedType: Int = 0,
|
||||
@ProtoId(8) val poiId: String = "",
|
||||
@ProtoId(9) val poiTitle: String = "",
|
||||
@ProtoId(20) val effectiveTime: Int = 0,
|
||||
@ProtoId(21) val expiationTime: Int = 0,
|
||||
@ProtoId(22) val msgLocale: AppointDefine.LocaleInfo? = null,
|
||||
@ProtoId(23) val feedsIndex: Int = 0,
|
||||
@ProtoId(24) val msgAd: AppointDefine.ADFeed? = null,
|
||||
@ProtoId(25) val privateData: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class TravelInfo(
|
||||
@SerialId(1) val msgDepartLocale: AppointDefine.LocaleInfo? = null,
|
||||
@SerialId(2) val msgDestination: AppointDefine.LocaleInfo? = null,
|
||||
@SerialId(3) val vehicle: Int = 0,
|
||||
@SerialId(4) val partnerCount: Int = 0,
|
||||
@SerialId(5) val placePicUrl: String = "",
|
||||
@SerialId(6) val placeUrl: String = ""
|
||||
@ProtoId(1) val msgDepartLocale: AppointDefine.LocaleInfo? = null,
|
||||
@ProtoId(2) val msgDestination: AppointDefine.LocaleInfo? = null,
|
||||
@ProtoId(3) val vehicle: Int = 0,
|
||||
@ProtoId(4) val partnerCount: Int = 0,
|
||||
@ProtoId(5) val placePicUrl: String = "",
|
||||
@ProtoId(6) val placeUrl: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RecentFreshFeed(
|
||||
@SerialId(1) val freshFeedInfo: List<AppointDefine.FreshFeedInfo>? = null,
|
||||
@SerialId(2) val uid: Long = 0L
|
||||
@ProtoId(1) val freshFeedInfo: List<AppointDefine.FreshFeedInfo>? = null,
|
||||
@ProtoId(2) val uid: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GPS(
|
||||
@SerialId(1) val int32Lat: Int = 900000000,
|
||||
@SerialId(2) val int32Lon: Int = 900000000,
|
||||
@SerialId(3) val int32Alt: Int = -10000000,
|
||||
@SerialId(4) val int32Type: Int = 0
|
||||
@ProtoId(1) val int32Lat: Int = 900000000,
|
||||
@ProtoId(2) val int32Lon: Int = 900000000,
|
||||
@ProtoId(3) val int32Alt: Int = -10000000,
|
||||
@ProtoId(4) val int32Type: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class AppointID(
|
||||
@SerialId(1) val requestId: String = ""
|
||||
@ProtoId(1) val requestId: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class LocaleInfo(
|
||||
@SerialId(1) val name: String = "",
|
||||
@SerialId(2) val country: String = "",
|
||||
@SerialId(3) val province: String = "",
|
||||
@SerialId(4) val city: String = "",
|
||||
@SerialId(5) val region: String = "",
|
||||
@SerialId(6) val poi: String = "",
|
||||
@SerialId(7) val msgGps: AppointDefine.GPS? = null,
|
||||
@SerialId(8) val address: String = ""
|
||||
@ProtoId(1) val name: String = "",
|
||||
@ProtoId(2) val country: String = "",
|
||||
@ProtoId(3) val province: String = "",
|
||||
@ProtoId(4) val city: String = "",
|
||||
@ProtoId(5) val region: String = "",
|
||||
@ProtoId(6) val poi: String = "",
|
||||
@ProtoId(7) val msgGps: AppointDefine.GPS? = null,
|
||||
@ProtoId(8) val address: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class LBSInfo(
|
||||
@SerialId(1) val msgGps: AppointDefine.GPS? = null,
|
||||
@SerialId(2) val msgWifis: List<AppointDefine.Wifi>? = null,
|
||||
@SerialId(3) val msgCells: List<AppointDefine.Cell>? = null
|
||||
@ProtoId(1) val msgGps: AppointDefine.GPS? = null,
|
||||
@ProtoId(2) val msgWifis: List<AppointDefine.Wifi>? = null,
|
||||
@ProtoId(3) val msgCells: List<AppointDefine.Cell>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class FeedEvent(
|
||||
@SerialId(1) val eventId: Long = 0L,
|
||||
@SerialId(2) val time: Int = 0,
|
||||
@SerialId(3) val eventtype: Int = 0,
|
||||
@SerialId(4) val msgUserInfo: AppointDefine.StrangerInfo? = null,
|
||||
@SerialId(5) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
||||
@SerialId(6) val eventTips: String = "",
|
||||
@SerialId(7) val msgComment: AppointDefine.FeedComment? = null,
|
||||
@SerialId(8) val cancelEventId: Long = 0L
|
||||
@ProtoId(1) val eventId: Long = 0L,
|
||||
@ProtoId(2) val time: Int = 0,
|
||||
@ProtoId(3) val eventtype: Int = 0,
|
||||
@ProtoId(4) val msgUserInfo: AppointDefine.StrangerInfo? = null,
|
||||
@ProtoId(5) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
||||
@ProtoId(6) val eventTips: String = "",
|
||||
@ProtoId(7) val msgComment: AppointDefine.FeedComment? = null,
|
||||
@ProtoId(8) val cancelEventId: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class FeedsCookie(
|
||||
@SerialId(1) val strList: List<String> = listOf(),
|
||||
@SerialId(2) val pose: Int = 0,
|
||||
@SerialId(3) val cookie: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val uint64Topics: List<Long>? = null
|
||||
@ProtoId(1) val strList: List<String> = listOf(),
|
||||
@ProtoId(2) val pose: Int = 0,
|
||||
@ProtoId(3) val cookie: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val uint64Topics: List<Long>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class NearbyTopic(
|
||||
@SerialId(1) val topicId: Long = 0L,
|
||||
@SerialId(2) val topic: String = "",
|
||||
@SerialId(3) val foreword: String = "",
|
||||
@SerialId(4) val createTime: Int = 0,
|
||||
@SerialId(5) val updateTime: Int = 0,
|
||||
@SerialId(6) val hotFlag: Int = 0,
|
||||
@SerialId(7) val buttonStyle: Int = 0,
|
||||
@SerialId(8) val buttonSrc: String = "",
|
||||
@SerialId(9) val backgroundSrc: String = "",
|
||||
@SerialId(10) val attendeeInfo: String = "",
|
||||
@SerialId(11) val index: Int = 0,
|
||||
@SerialId(12) val publishScope: Int = 0,
|
||||
@SerialId(13) val effectiveTime: Int = 0,
|
||||
@SerialId(14) val expiationTime: Int = 0,
|
||||
@SerialId(15) val pushedUsrCount: Int = 0,
|
||||
@SerialId(16) val timerangeLeft: Int = 0,
|
||||
@SerialId(17) val timerangeRight: Int = 0,
|
||||
@SerialId(18) val area: String = ""
|
||||
@ProtoId(1) val topicId: Long = 0L,
|
||||
@ProtoId(2) val topic: String = "",
|
||||
@ProtoId(3) val foreword: String = "",
|
||||
@ProtoId(4) val createTime: Int = 0,
|
||||
@ProtoId(5) val updateTime: Int = 0,
|
||||
@ProtoId(6) val hotFlag: Int = 0,
|
||||
@ProtoId(7) val buttonStyle: Int = 0,
|
||||
@ProtoId(8) val buttonSrc: String = "",
|
||||
@ProtoId(9) val backgroundSrc: String = "",
|
||||
@ProtoId(10) val attendeeInfo: String = "",
|
||||
@ProtoId(11) val index: Int = 0,
|
||||
@ProtoId(12) val publishScope: Int = 0,
|
||||
@ProtoId(13) val effectiveTime: Int = 0,
|
||||
@ProtoId(14) val expiationTime: Int = 0,
|
||||
@ProtoId(15) val pushedUsrCount: Int = 0,
|
||||
@ProtoId(16) val timerangeLeft: Int = 0,
|
||||
@ProtoId(17) val timerangeRight: Int = 0,
|
||||
@ProtoId(18) val area: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class NearbyEvent(
|
||||
@SerialId(1) val eventtype: Int = 0,
|
||||
@SerialId(2) val msgRankevent: AppointDefine.RankEvent? = null,
|
||||
@SerialId(3) val eventUin: Long = 0L,
|
||||
@SerialId(4) val eventTinyid: Long = 0L
|
||||
@ProtoId(1) val eventtype: Int = 0,
|
||||
@ProtoId(2) val msgRankevent: AppointDefine.RankEvent? = null,
|
||||
@ProtoId(3) val eventUin: Long = 0L,
|
||||
@ProtoId(4) val eventTinyid: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Feed(
|
||||
@SerialId(1) val msgUserInfo: AppointDefine.PublisherInfo? = null,
|
||||
@SerialId(2) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
||||
@SerialId(3) val ownerFlag: Int = 0
|
||||
@ProtoId(1) val msgUserInfo: AppointDefine.PublisherInfo? = null,
|
||||
@ProtoId(2) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
||||
@ProtoId(3) val ownerFlag: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ActivityInfo(
|
||||
@SerialId(2) val name: String = "",
|
||||
@SerialId(3) val cover: String = "",
|
||||
@SerialId(4) val url: String = "",
|
||||
@SerialId(5) val startTime: Int = 0,
|
||||
@SerialId(6) val endTime: Int = 0,
|
||||
@SerialId(7) val locName: String = "",
|
||||
@SerialId(8) val enroll: Long = 0L,
|
||||
@SerialId(9) val createUin: Long = 0L,
|
||||
@SerialId(10) val createTime: Int = 0,
|
||||
@SerialId(11) val organizerInfo: AppointDefine.OrganizerInfo = OrganizerInfo(),
|
||||
@SerialId(12) val flag: Long? = null
|
||||
@ProtoId(2) val name: String = "",
|
||||
@ProtoId(3) val cover: String = "",
|
||||
@ProtoId(4) val url: String = "",
|
||||
@ProtoId(5) val startTime: Int = 0,
|
||||
@ProtoId(6) val endTime: Int = 0,
|
||||
@ProtoId(7) val locName: String = "",
|
||||
@ProtoId(8) val enroll: Long = 0L,
|
||||
@ProtoId(9) val createUin: Long = 0L,
|
||||
@ProtoId(10) val createTime: Int = 0,
|
||||
@ProtoId(11) val organizerInfo: AppointDefine.OrganizerInfo = OrganizerInfo(),
|
||||
@ProtoId(12) val flag: Long? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class HotEntry(
|
||||
@SerialId(1) val openFlag: Int = 0,
|
||||
@SerialId(2) val restTime: Int = 0,
|
||||
@SerialId(3) val foreword: String = "",
|
||||
@SerialId(4) val backgroundSrc: String = ""
|
||||
@ProtoId(1) val openFlag: Int = 0,
|
||||
@ProtoId(2) val restTime: Int = 0,
|
||||
@ProtoId(3) val foreword: String = "",
|
||||
@ProtoId(4) val backgroundSrc: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class UserFeed(
|
||||
@SerialId(1) val msgUserInfo: AppointDefine.PublisherInfo? = null,
|
||||
@SerialId(2) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
||||
@SerialId(3) val ownerFlag: Int = 0,
|
||||
@SerialId(4) val msgActivityInfo: AppointDefine.ActivityInfo? = null
|
||||
@ProtoId(1) val msgUserInfo: AppointDefine.PublisherInfo? = null,
|
||||
@ProtoId(2) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
||||
@ProtoId(3) val ownerFlag: Int = 0,
|
||||
@ProtoId(4) val msgActivityInfo: AppointDefine.ActivityInfo? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Elem(
|
||||
@SerialId(1) val content: String = "",
|
||||
@SerialId(2) val msgFaceInfo: AppointDefine.Face? = null
|
||||
@ProtoId(1) val content: String = "",
|
||||
@ProtoId(2) val msgFaceInfo: AppointDefine.Face? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class HotFreshFeedList(
|
||||
@SerialId(1) val msgFeeds: List<AppointDefine.HotUserFeed>? = null,
|
||||
@SerialId(2) val updateTime: Int = 0
|
||||
@ProtoId(1) val msgFeeds: List<AppointDefine.HotUserFeed>? = null,
|
||||
@ProtoId(2) val updateTime: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RptInterestTag(
|
||||
@SerialId(1) val interestTags: List<AppointDefine.InterestTag>? = null
|
||||
@ProtoId(1) val interestTags: List<AppointDefine.InterestTag>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class AddressInfo(
|
||||
@SerialId(1) val companyZone: String = "",
|
||||
@SerialId(2) val companyName: String = "",
|
||||
@SerialId(3) val companyAddr: String = "",
|
||||
@SerialId(4) val companyPicUrl: String = "",
|
||||
@SerialId(5) val companyUrl: String = "",
|
||||
@SerialId(6) val msgCompanyId: AppointDefine.ShopID? = null
|
||||
@ProtoId(1) val companyZone: String = "",
|
||||
@ProtoId(2) val companyName: String = "",
|
||||
@ProtoId(3) val companyAddr: String = "",
|
||||
@ProtoId(4) val companyPicUrl: String = "",
|
||||
@ProtoId(5) val companyUrl: String = "",
|
||||
@ProtoId(6) val msgCompanyId: AppointDefine.ShopID? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class PublisherInfo(
|
||||
@SerialId(1) val tinyid: Long = 0L,
|
||||
@SerialId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val age: Int = 0,
|
||||
@SerialId(4) val gender: Int = 0,
|
||||
@SerialId(5) val constellation: String = "",
|
||||
@SerialId(6) val profession: Int = 0,
|
||||
@SerialId(7) val distance: String = "",
|
||||
@SerialId(8) val marriage: Int = 0,
|
||||
@SerialId(9) val vipinfo: String = "",
|
||||
@SerialId(10) val recommend: Int = 0,
|
||||
@SerialId(11) val godflag: Int = 0,
|
||||
@SerialId(12) val chatflag: Int = 0,
|
||||
@SerialId(13) val chatupCount: Int = 0,
|
||||
@SerialId(14) val charm: Int = 0,
|
||||
@SerialId(15) val charmLevel: Int = 0,
|
||||
@SerialId(16) val pubNumber: Int = 0,
|
||||
@SerialId(17) val msgCommonLabel: AppointDefine.CommonLabel? = null,
|
||||
@SerialId(18) val recentVistorTime: Int = 0,
|
||||
@SerialId(19) val strangerDeclare: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(20) val friendUin: Long = 0L,
|
||||
@SerialId(21) val historyFlag: Int = 0,
|
||||
@SerialId(22) val followflag: Long = 0L
|
||||
@ProtoId(1) val tinyid: Long = 0L,
|
||||
@ProtoId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val age: Int = 0,
|
||||
@ProtoId(4) val gender: Int = 0,
|
||||
@ProtoId(5) val constellation: String = "",
|
||||
@ProtoId(6) val profession: Int = 0,
|
||||
@ProtoId(7) val distance: String = "",
|
||||
@ProtoId(8) val marriage: Int = 0,
|
||||
@ProtoId(9) val vipinfo: String = "",
|
||||
@ProtoId(10) val recommend: Int = 0,
|
||||
@ProtoId(11) val godflag: Int = 0,
|
||||
@ProtoId(12) val chatflag: Int = 0,
|
||||
@ProtoId(13) val chatupCount: Int = 0,
|
||||
@ProtoId(14) val charm: Int = 0,
|
||||
@ProtoId(15) val charmLevel: Int = 0,
|
||||
@ProtoId(16) val pubNumber: Int = 0,
|
||||
@ProtoId(17) val msgCommonLabel: AppointDefine.CommonLabel? = null,
|
||||
@ProtoId(18) val recentVistorTime: Int = 0,
|
||||
@ProtoId(19) val strangerDeclare: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(20) val friendUin: Long = 0L,
|
||||
@ProtoId(21) val historyFlag: Int = 0,
|
||||
@ProtoId(22) val followflag: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class HotUserFeed(
|
||||
@SerialId(1) val feedId: String = "",
|
||||
@SerialId(2) val praiseCount: Int = 0,
|
||||
@SerialId(3) val publishUid: Long = 0L,
|
||||
@SerialId(4) val publishTime: Int = 0
|
||||
@ProtoId(1) val feedId: String = "",
|
||||
@ProtoId(2) val praiseCount: Int = 0,
|
||||
@ProtoId(3) val publishUid: Long = 0L,
|
||||
@ProtoId(4) val publishTime: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class FreshFeedInfo(
|
||||
@SerialId(1) val uin: Long = 0L,
|
||||
@SerialId(2) val time: Int = 0,
|
||||
@SerialId(3) val feedId: String = "",
|
||||
@SerialId(4) val feedType: Long = 0L
|
||||
@ProtoId(1) val uin: Long = 0L,
|
||||
@ProtoId(2) val time: Int = 0,
|
||||
@ProtoId(3) val feedId: String = "",
|
||||
@ProtoId(4) val feedType: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class CommonLabel(
|
||||
@SerialId(1) val lableId: Int = 0,
|
||||
@SerialId(2) val lableMsgPre: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val lableMsgLast: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val interstName: List<ByteArray>? = null,
|
||||
@SerialId(5) val interstType: List<Int>? = null
|
||||
@ProtoId(1) val lableId: Int = 0,
|
||||
@ProtoId(2) val lableMsgPre: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val lableMsgLast: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val interstName: List<ByteArray>? = null,
|
||||
@ProtoId(5) val interstType: List<Int>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Face(
|
||||
@SerialId(1) val index: Int = 0
|
||||
@ProtoId(1) val index: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class StrangerInfo(
|
||||
@SerialId(1) val tinyid: Long = 0L,
|
||||
@SerialId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val age: Int = 0,
|
||||
@SerialId(4) val gender: Int = 0,
|
||||
@SerialId(5) val dating: Int = 0,
|
||||
@SerialId(6) val listIdx: Int = 0,
|
||||
@SerialId(7) val constellation: String = "",
|
||||
@SerialId(8) val profession: Int = 0,
|
||||
@SerialId(9) val marriage: Int = 0,
|
||||
@SerialId(10) val vipinfo: String = "",
|
||||
@SerialId(11) val recommend: Int = 0,
|
||||
@SerialId(12) val godflag: Int = 0,
|
||||
@SerialId(13) val charm: Int = 0,
|
||||
@SerialId(14) val charmLevel: Int = 0,
|
||||
@SerialId(15) val uin: Long = 0L
|
||||
@ProtoId(1) val tinyid: Long = 0L,
|
||||
@ProtoId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val age: Int = 0,
|
||||
@ProtoId(4) val gender: Int = 0,
|
||||
@ProtoId(5) val dating: Int = 0,
|
||||
@ProtoId(6) val listIdx: Int = 0,
|
||||
@ProtoId(7) val constellation: String = "",
|
||||
@ProtoId(8) val profession: Int = 0,
|
||||
@ProtoId(9) val marriage: Int = 0,
|
||||
@ProtoId(10) val vipinfo: String = "",
|
||||
@ProtoId(11) val recommend: Int = 0,
|
||||
@ProtoId(12) val godflag: Int = 0,
|
||||
@ProtoId(13) val charm: Int = 0,
|
||||
@ProtoId(14) val charmLevel: Int = 0,
|
||||
@ProtoId(15) val uin: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class HotTopic(
|
||||
@SerialId(1) val id: Long = 0L,
|
||||
@SerialId(2) val title: String = "",
|
||||
@SerialId(3) val topicType: Long = 0L,
|
||||
@SerialId(4) val total: Long = 0L,
|
||||
@SerialId(5) val times: Long = 0L,
|
||||
@SerialId(6) val historyTimes: Long = 0L,
|
||||
@SerialId(7) val bgUrl: String = "",
|
||||
@SerialId(8) val url: String = "",
|
||||
@SerialId(9) val extraInfo: String = ""
|
||||
@ProtoId(1) val id: Long = 0L,
|
||||
@ProtoId(2) val title: String = "",
|
||||
@ProtoId(3) val topicType: Long = 0L,
|
||||
@ProtoId(4) val total: Long = 0L,
|
||||
@ProtoId(5) val times: Long = 0L,
|
||||
@ProtoId(6) val historyTimes: Long = 0L,
|
||||
@ProtoId(7) val bgUrl: String = "",
|
||||
@ProtoId(8) val url: String = "",
|
||||
@ProtoId(9) val extraInfo: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class DateEvent(
|
||||
@SerialId(1) val eventId: Long = 0L,
|
||||
@SerialId(2) val time: Int = 0,
|
||||
@SerialId(3) val type: Int = 0,
|
||||
@SerialId(4) val msgUserInfo: AppointDefine.StrangerInfo? = null,
|
||||
@SerialId(5) val msgDateInfo: AppointDefine.AppointInfo? = null,
|
||||
@SerialId(6) val attendIdx: Int = 0,
|
||||
@SerialId(7) val eventTips: String = "",
|
||||
@SerialId(8) val msgComment: AppointDefine.DateComment? = null,
|
||||
@SerialId(9) val cancelEventId: Long = 0L
|
||||
@ProtoId(1) val eventId: Long = 0L,
|
||||
@ProtoId(2) val time: Int = 0,
|
||||
@ProtoId(3) val type: Int = 0,
|
||||
@ProtoId(4) val msgUserInfo: AppointDefine.StrangerInfo? = null,
|
||||
@ProtoId(5) val msgDateInfo: AppointDefine.AppointInfo? = null,
|
||||
@ProtoId(6) val attendIdx: Int = 0,
|
||||
@ProtoId(7) val eventTips: String = "",
|
||||
@ProtoId(8) val msgComment: AppointDefine.DateComment? = null,
|
||||
@ProtoId(9) val cancelEventId: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class AppointInfo(
|
||||
@SerialId(1) val msgAppointId: AppointDefine.AppointID? = null,
|
||||
@SerialId(2) val msgAppointment: AppointDefine.AppointContent? = null,
|
||||
@SerialId(3) val appointStatus: Int = 0,
|
||||
@SerialId(4) val joinWording: String = "",
|
||||
@SerialId(5) val viewWording: String = "",
|
||||
@SerialId(6) val unreadCount: Int = 0,
|
||||
@SerialId(7) val owner: Int = 0,
|
||||
@SerialId(8) val join: Int = 0,
|
||||
@SerialId(9) val view: Int = 0,
|
||||
@SerialId(10) val commentWording: String = "",
|
||||
@SerialId(11) val commentNum: Int = 0,
|
||||
@SerialId(12) val attendStatus: Int = 0,
|
||||
@SerialId(13) val msgAppointmentEx: AppointDefine.AppointInfoEx? = null
|
||||
@ProtoId(1) val msgAppointId: AppointDefine.AppointID? = null,
|
||||
@ProtoId(2) val msgAppointment: AppointDefine.AppointContent? = null,
|
||||
@ProtoId(3) val appointStatus: Int = 0,
|
||||
@ProtoId(4) val joinWording: String = "",
|
||||
@ProtoId(5) val viewWording: String = "",
|
||||
@ProtoId(6) val unreadCount: Int = 0,
|
||||
@ProtoId(7) val owner: Int = 0,
|
||||
@ProtoId(8) val join: Int = 0,
|
||||
@ProtoId(9) val view: Int = 0,
|
||||
@ProtoId(10) val commentWording: String = "",
|
||||
@ProtoId(11) val commentNum: Int = 0,
|
||||
@ProtoId(12) val attendStatus: Int = 0,
|
||||
@ProtoId(13) val msgAppointmentEx: AppointDefine.AppointInfoEx? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class UserInfo(
|
||||
@SerialId(1) val uin: Long = 0L,
|
||||
@SerialId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val age: Int = 0,
|
||||
@SerialId(4) val gender: Int = 0,
|
||||
@SerialId(5) val avatar: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val uin: Long = 0L,
|
||||
@ProtoId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val age: Int = 0,
|
||||
@ProtoId(4) val gender: Int = 0,
|
||||
@ProtoId(5) val avatar: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ReplyInfo(
|
||||
@SerialId(1) val commentId: String = "",
|
||||
@SerialId(2) val msgStrangerInfo: AppointDefine.StrangerInfo? = null
|
||||
@ProtoId(1) val commentId: String = "",
|
||||
@ProtoId(2) val msgStrangerInfo: AppointDefine.StrangerInfo? = null
|
||||
) : ProtoBuf
|
||||
}
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
@ -18,40 +18,40 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
internal class Vec0xd50 : ProtoBuf {
|
||||
@Serializable
|
||||
internal class ExtSnsFrdData(
|
||||
@SerialId(1) val frdUin: Long = 0L,
|
||||
@SerialId(91001) val musicSwitch: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(101001) val mutualmarkAlienation: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(141001) val mutualmarkScore: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(151001) val ksingSwitch: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(181001) val lbsShare: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val frdUin: Long = 0L,
|
||||
@ProtoId(91001) val musicSwitch: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(101001) val mutualmarkAlienation: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(141001) val mutualmarkScore: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(151001) val ksingSwitch: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(181001) val lbsShare: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class RspBody(
|
||||
@SerialId(1) val msgUpdateData: List<Vec0xd50.ExtSnsFrdData>? = null,
|
||||
@SerialId(11) val over: Int = 0,
|
||||
@SerialId(12) val nextStart: Int = 0,
|
||||
@SerialId(13) val uint64UnfinishedUins: List<Long>? = null
|
||||
@ProtoId(1) val msgUpdateData: List<Vec0xd50.ExtSnsFrdData>? = null,
|
||||
@ProtoId(11) val over: Int = 0,
|
||||
@ProtoId(12) val nextStart: Int = 0,
|
||||
@ProtoId(13) val uint64UnfinishedUins: List<Long>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class ReqBody(
|
||||
@SerialId(1) val appid: Long = 0L,
|
||||
@SerialId(2) val maxPkgSize: Int = 0,
|
||||
@SerialId(3) val startTime: Int = 0,
|
||||
@SerialId(4) val startIndex: Int = 0,
|
||||
@SerialId(5) val reqNum: Int = 0,
|
||||
@SerialId(6) val uinList: List<Long>? = null,
|
||||
@SerialId(91001) val reqMusicSwitch: Int = 0,
|
||||
@SerialId(101001) val reqMutualmarkAlienation: Int = 0,
|
||||
@SerialId(141001) val reqMutualmarkScore: Int = 0,
|
||||
@SerialId(151001) val reqKsingSwitch: Int = 0,
|
||||
@SerialId(181001) val reqMutualmarkLbsshare: Int = 0
|
||||
@ProtoId(1) val appid: Long = 0L,
|
||||
@ProtoId(2) val maxPkgSize: Int = 0,
|
||||
@ProtoId(3) val startTime: Int = 0,
|
||||
@ProtoId(4) val startIndex: Int = 0,
|
||||
@ProtoId(5) val reqNum: Int = 0,
|
||||
@ProtoId(6) val uinList: List<Long>? = null,
|
||||
@ProtoId(91001) val reqMusicSwitch: Int = 0,
|
||||
@ProtoId(101001) val reqMutualmarkAlienation: Int = 0,
|
||||
@ProtoId(141001) val reqMutualmarkScore: Int = 0,
|
||||
@ProtoId(151001) val reqKsingSwitch: Int = 0,
|
||||
@ProtoId(181001) val reqMutualmarkLbsshare: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class KSingRelationInfo(
|
||||
@SerialId(1) val flag: Int = 0
|
||||
@ProtoId(1) val flag: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@ -59,21 +59,21 @@ internal class Vec0xd50 : ProtoBuf {
|
||||
internal class Vec0xd6b : ProtoBuf {
|
||||
@Serializable
|
||||
internal class ReqBody(
|
||||
@SerialId(1) val maxPkgSize: Int = 0,
|
||||
@SerialId(2) val startTime: Int = 0,
|
||||
@SerialId(11) val uinList: List<Long>? = null
|
||||
@ProtoId(1) val maxPkgSize: Int = 0,
|
||||
@ProtoId(2) val startTime: Int = 0,
|
||||
@ProtoId(11) val uinList: List<Long>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class RspBody(
|
||||
@SerialId(11) val msgMutualmarkData: List<Vec0xd6b.MutualMarkData>? = null,
|
||||
@SerialId(12) val uint64UnfinishedUins: List<Long>? = null
|
||||
@ProtoId(11) val msgMutualmarkData: List<Vec0xd6b.MutualMarkData>? = null,
|
||||
@ProtoId(12) val uint64UnfinishedUins: List<Long>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class MutualMarkData(
|
||||
@SerialId(1) val frdUin: Long = 0L,
|
||||
@SerialId(2) val result: Int = 0
|
||||
@ProtoId(1) val frdUin: Long = 0L,
|
||||
@ProtoId(2) val result: Int = 0
|
||||
// @SerialId(11) val mutualmarkInfo: List<Mutualmark.MutualMark>? = null
|
||||
) : ProtoBuf
|
||||
}
|
||||
@ -82,26 +82,26 @@ internal class Vec0xd6b : ProtoBuf {
|
||||
internal class Mutualmark : ProtoBuf {
|
||||
@Serializable
|
||||
internal class MutualmarkInfo(
|
||||
@SerialId(1) val lastActionTime: Long = 0L,
|
||||
@SerialId(2) val level: Int = 0,
|
||||
@SerialId(3) val lastChangeTime: Long = 0L,
|
||||
@SerialId(4) val continueDays: Int = 0,
|
||||
@SerialId(5) val wildcardWording: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(6) val notifyTime: Long = 0L,
|
||||
@SerialId(7) val iconStatus: Long = 0L,
|
||||
@SerialId(8) val iconStatusEndTime: Long = 0L,
|
||||
@SerialId(9) val closeFlag: Int = 0,
|
||||
@SerialId(10) val resourceInfo: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val lastActionTime: Long = 0L,
|
||||
@ProtoId(2) val level: Int = 0,
|
||||
@ProtoId(3) val lastChangeTime: Long = 0L,
|
||||
@ProtoId(4) val continueDays: Int = 0,
|
||||
@ProtoId(5) val wildcardWording: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val notifyTime: Long = 0L,
|
||||
@ProtoId(7) val iconStatus: Long = 0L,
|
||||
@ProtoId(8) val iconStatusEndTime: Long = 0L,
|
||||
@ProtoId(9) val closeFlag: Int = 0,
|
||||
@ProtoId(10) val resourceInfo: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class ResourceInfo17(
|
||||
@SerialId(1) val dynamicUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val staticUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val cartoonUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val cartoonMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val playCartoon: Int = 0,
|
||||
@SerialId(6) val word: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val dynamicUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val staticUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val cartoonUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val cartoonMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val playCartoon: Int = 0,
|
||||
@ProtoId(6) val word: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
@ -18,51 +18,51 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
class GroupLabel : ProtoBuf {
|
||||
@Serializable
|
||||
class Label(
|
||||
@SerialId(1) val name: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val enumType: Int /* enum */ = 1,
|
||||
@SerialId(3) val textColor: GroupLabel.Color? = null,
|
||||
@SerialId(4) val edgingColor: GroupLabel.Color? = null,
|
||||
@SerialId(5) val labelAttr: Int = 0,
|
||||
@SerialId(6) val labelType: Int = 0
|
||||
@ProtoId(1) val name: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val enumType: Int /* enum */ = 1,
|
||||
@ProtoId(3) val textColor: GroupLabel.Color? = null,
|
||||
@ProtoId(4) val edgingColor: GroupLabel.Color? = null,
|
||||
@ProtoId(5) val labelAttr: Int = 0,
|
||||
@ProtoId(6) val labelType: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RspBody(
|
||||
@SerialId(1) val error: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val groupInfo: List<GroupLabel.GroupInfo>? = null
|
||||
@ProtoId(1) val error: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val groupInfo: List<GroupLabel.GroupInfo>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class SourceId(
|
||||
@SerialId(1) val sourceId: Int = 0
|
||||
@ProtoId(1) val sourceId: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GroupInfo(
|
||||
@SerialId(1) val int32Result: Int = 0,
|
||||
@SerialId(2) val groupCode: Long = 0L,
|
||||
@SerialId(3) val groupLabel: List<GroupLabel.Label>? = null
|
||||
@ProtoId(1) val int32Result: Int = 0,
|
||||
@ProtoId(2) val groupCode: Long = 0L,
|
||||
@ProtoId(3) val groupLabel: List<GroupLabel.Label>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Color(
|
||||
@SerialId(1) val r: Int = 0,
|
||||
@SerialId(2) val g: Int = 0,
|
||||
@SerialId(3) val b: Int = 0
|
||||
@ProtoId(1) val r: Int = 0,
|
||||
@ProtoId(2) val g: Int = 0,
|
||||
@ProtoId(3) val b: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ReqBody(
|
||||
@SerialId(1) val sourceId: GroupLabel.SourceId? = null,
|
||||
@SerialId(2) val uinInfo: GroupLabel.UinInfo? = null,
|
||||
@SerialId(3) val numberLabel: Int = 5,
|
||||
@SerialId(4) val groupCode: List<Long>? = null,
|
||||
@SerialId(5) val labelStyle: Int = 0
|
||||
@ProtoId(1) val sourceId: GroupLabel.SourceId? = null,
|
||||
@ProtoId(2) val uinInfo: GroupLabel.UinInfo? = null,
|
||||
@ProtoId(3) val numberLabel: Int = 5,
|
||||
@ProtoId(4) val groupCode: List<Long>? = null,
|
||||
@ProtoId(5) val labelStyle: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class UinInfo(
|
||||
@SerialId(1) val int64Longitude: Long = 0L,
|
||||
@SerialId(2) val int64Latitude: Long = 0L
|
||||
@ProtoId(1) val int64Longitude: Long = 0L,
|
||||
@ProtoId(2) val int64Latitude: Long = 0L
|
||||
) : ProtoBuf
|
||||
}
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import kotlinx.serialization.protobuf.ProtoNumberType
|
||||
import kotlinx.serialization.protobuf.ProtoType
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
@ -20,90 +20,90 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
class BdhExtinfo : ProtoBuf {
|
||||
@Serializable
|
||||
class CommFileExtReq(
|
||||
@SerialId(1) val actionType: Int = 0,
|
||||
@SerialId(2) val uuid: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val actionType: Int = 0,
|
||||
@ProtoId(2) val uuid: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class CommFileExtRsp(
|
||||
@SerialId(1) val int32Retcode: Int = 0,
|
||||
@SerialId(2) val downloadUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val int32Retcode: Int = 0,
|
||||
@ProtoId(2) val downloadUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class PicInfo(
|
||||
@SerialId(1) val idx: Int = 0,
|
||||
@SerialId(2) val size: Int = 0,
|
||||
@SerialId(3) val binMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val type: Int = 0
|
||||
@ProtoId(1) val idx: Int = 0,
|
||||
@ProtoId(2) val size: Int = 0,
|
||||
@ProtoId(3) val binMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val type: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class QQVoiceExtReq(
|
||||
@SerialId(1) val qid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val fmt: Int = 0,
|
||||
@SerialId(3) val rate: Int = 0,
|
||||
@SerialId(4) val bits: Int = 0,
|
||||
@SerialId(5) val channel: Int = 0,
|
||||
@SerialId(6) val pinyin: Int = 0
|
||||
@ProtoId(1) val qid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val fmt: Int = 0,
|
||||
@ProtoId(3) val rate: Int = 0,
|
||||
@ProtoId(4) val bits: Int = 0,
|
||||
@ProtoId(5) val channel: Int = 0,
|
||||
@ProtoId(6) val pinyin: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class QQVoiceExtRsp(
|
||||
@SerialId(1) val qid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val int32Retcode: Int = 0,
|
||||
@SerialId(3) val msgResult: List<QQVoiceResult>? = null
|
||||
@ProtoId(1) val qid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val int32Retcode: Int = 0,
|
||||
@ProtoId(3) val msgResult: List<QQVoiceResult>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class QQVoiceResult(
|
||||
@SerialId(1) val text: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val pinyin: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val source: Int = 0
|
||||
@ProtoId(1) val text: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val pinyin: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val source: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ShortVideoReqExtInfo(
|
||||
@SerialId(1) val cmd: Int = 0,
|
||||
@SerialId(2) val sessionId: Long = 0L,
|
||||
@SerialId(3) val msgThumbinfo: PicInfo? = null,
|
||||
@SerialId(4) val msgVideoinfo: VideoInfo? = null,
|
||||
@SerialId(5) val msgShortvideoSureReq: ShortVideoSureReqInfo? = null,
|
||||
@SerialId(6) val boolIsMergeCmdBeforeData: Boolean = false
|
||||
@ProtoId(1) val cmd: Int = 0,
|
||||
@ProtoId(2) val sessionId: Long = 0L,
|
||||
@ProtoId(3) val msgThumbinfo: PicInfo? = null,
|
||||
@ProtoId(4) val msgVideoinfo: VideoInfo? = null,
|
||||
@ProtoId(5) val msgShortvideoSureReq: ShortVideoSureReqInfo? = null,
|
||||
@ProtoId(6) val boolIsMergeCmdBeforeData: Boolean = false
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ShortVideoRspExtInfo(
|
||||
@SerialId(1) val cmd: Int = 0,
|
||||
@SerialId(2) val sessionId: Long = 0L,
|
||||
@SerialId(3) val int32Retcode: Int = 0,
|
||||
@SerialId(4) val errinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val msgThumbinfo: PicInfo? = null,
|
||||
@SerialId(6) val msgVideoinfo: VideoInfo? = null,
|
||||
@SerialId(7) val msgShortvideoSureRsp: ShortVideoSureRspInfo? = null,
|
||||
@SerialId(8) val retryFlag: Int = 0
|
||||
@ProtoId(1) val cmd: Int = 0,
|
||||
@ProtoId(2) val sessionId: Long = 0L,
|
||||
@ProtoId(3) val int32Retcode: Int = 0,
|
||||
@ProtoId(4) val errinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val msgThumbinfo: PicInfo? = null,
|
||||
@ProtoId(6) val msgVideoinfo: VideoInfo? = null,
|
||||
@ProtoId(7) val msgShortvideoSureRsp: ShortVideoSureRspInfo? = null,
|
||||
@ProtoId(8) val retryFlag: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ShortVideoSureReqInfo(
|
||||
@SerialId(1) val fromuin: Long = 0L,
|
||||
@SerialId(2) val chatType: Int = 0,
|
||||
@SerialId(3) val touin: Long = 0L,
|
||||
@SerialId(4) val groupCode: Long = 0L,
|
||||
@SerialId(5) val clientType: Int = 0,
|
||||
@SerialId(6) val msgThumbinfo: PicInfo? = null,
|
||||
@SerialId(7) val msgMergeVideoinfo: List<VideoInfo>? = null,
|
||||
@SerialId(8) val msgDropVideoinfo: List<VideoInfo>? = null,
|
||||
@SerialId(9) val businessType: Int = 0,
|
||||
@SerialId(10) val subBusinessType: Int = 0
|
||||
@ProtoId(1) val fromuin: Long = 0L,
|
||||
@ProtoId(2) val chatType: Int = 0,
|
||||
@ProtoId(3) val touin: Long = 0L,
|
||||
@ProtoId(4) val groupCode: Long = 0L,
|
||||
@ProtoId(5) val clientType: Int = 0,
|
||||
@ProtoId(6) val msgThumbinfo: PicInfo? = null,
|
||||
@ProtoId(7) val msgMergeVideoinfo: List<VideoInfo>? = null,
|
||||
@ProtoId(8) val msgDropVideoinfo: List<VideoInfo>? = null,
|
||||
@ProtoId(9) val businessType: Int = 0,
|
||||
@ProtoId(10) val subBusinessType: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ShortVideoSureRspInfo(
|
||||
@SerialId(1) val fileid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val ukey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val msgVideoinfo: VideoInfo? = null,
|
||||
@SerialId(4) val mergeCost: Int = 0
|
||||
@ProtoId(1) val fileid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val ukey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val msgVideoinfo: VideoInfo? = null,
|
||||
@ProtoId(4) val mergeCost: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
@ -111,31 +111,31 @@ class BdhExtinfo : ProtoBuf {
|
||||
|
||||
@Serializable
|
||||
class StoryVideoExtRsp(
|
||||
@SerialId(1) val int32Retcode: Int = 0,
|
||||
@SerialId(2) val msg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val cdnUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val fileId: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val int32Retcode: Int = 0,
|
||||
@ProtoId(2) val msg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val cdnUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val fileId: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class UploadPicExtInfo(
|
||||
@SerialId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val downloadUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val thumbDownloadUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val downloadUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val thumbDownloadUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class VideoInfo(
|
||||
@SerialId(1) val idx: Int = 0,
|
||||
@SerialId(2) val size: Int = 0,
|
||||
@SerialId(3) val binMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val format: Int = 0,
|
||||
@SerialId(5) val resLen: Int = 0,
|
||||
@SerialId(6) val resWidth: Int = 0,
|
||||
@SerialId(7) val time: Int = 0,
|
||||
@SerialId(8) val starttime: Long = 0L,
|
||||
@SerialId(9) val isAudio: Int = 0
|
||||
@ProtoId(1) val idx: Int = 0,
|
||||
@ProtoId(2) val size: Int = 0,
|
||||
@ProtoId(3) val binMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val format: Int = 0,
|
||||
@ProtoId(5) val resLen: Int = 0,
|
||||
@ProtoId(6) val resWidth: Int = 0,
|
||||
@ProtoId(7) val time: Int = 0,
|
||||
@ProtoId(8) val starttime: Long = 0L,
|
||||
@ProtoId(9) val isAudio: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@ -143,142 +143,142 @@ class BdhExtinfo : ProtoBuf {
|
||||
class CSDataHighwayHead : ProtoBuf {
|
||||
@Serializable
|
||||
class C2CCommonExtendinfo(
|
||||
@SerialId(1) val infoId: Int = 0,
|
||||
@SerialId(2) val msgFilterExtendinfo: FilterExtendinfo? = null
|
||||
@ProtoId(1) val infoId: Int = 0,
|
||||
@ProtoId(2) val msgFilterExtendinfo: FilterExtendinfo? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class DataHighwayHead(
|
||||
@SerialId(1) val version: Int = 0,
|
||||
@SerialId(2) val uin: String = "", // yes
|
||||
@SerialId(3) val command: String = "",
|
||||
@SerialId(4) val seq: Int = 0,
|
||||
@SerialId(5) val retryTimes: Int = 0,
|
||||
@SerialId(6) val appid: Int = 0,
|
||||
@SerialId(7) val dataflag: Int = 0,
|
||||
@SerialId(8) val commandId: Int = 0,
|
||||
@SerialId(9) val buildVer: String = "",
|
||||
@SerialId(10) val localeId: Int = 0
|
||||
@ProtoId(1) val version: Int = 0,
|
||||
@ProtoId(2) val uin: String = "", // yes
|
||||
@ProtoId(3) val command: String = "",
|
||||
@ProtoId(4) val seq: Int = 0,
|
||||
@ProtoId(5) val retryTimes: Int = 0,
|
||||
@ProtoId(6) val appid: Int = 0,
|
||||
@ProtoId(7) val dataflag: Int = 0,
|
||||
@ProtoId(8) val commandId: Int = 0,
|
||||
@ProtoId(9) val buildVer: String = "",
|
||||
@ProtoId(10) val localeId: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class DataHole(
|
||||
@SerialId(1) val begin: Long = 0L,
|
||||
@SerialId(2) val end: Long = 0L
|
||||
@ProtoId(1) val begin: Long = 0L,
|
||||
@ProtoId(2) val end: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class FilterExtendinfo(
|
||||
@SerialId(1) val filterFlag: Int = 0,
|
||||
@SerialId(2) val msgImageFilterRequest: ImageFilterRequest? = null
|
||||
@ProtoId(1) val filterFlag: Int = 0,
|
||||
@ProtoId(2) val msgImageFilterRequest: ImageFilterRequest? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class FilterStyle(
|
||||
@SerialId(1) val styleId: Int = 0,
|
||||
@SerialId(2) val styleName: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val styleId: Int = 0,
|
||||
@ProtoId(2) val styleName: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ImageFilterRequest(
|
||||
@SerialId(1) val sessionId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val clientIp: Int = 0,
|
||||
@SerialId(3) val uin: Long = 0L,
|
||||
@SerialId(4) val style: FilterStyle? = null,
|
||||
@SerialId(5) val width: Int = 0,
|
||||
@SerialId(6) val height: Int = 0,
|
||||
@SerialId(7) val imageData: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val sessionId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val clientIp: Int = 0,
|
||||
@ProtoId(3) val uin: Long = 0L,
|
||||
@ProtoId(4) val style: FilterStyle? = null,
|
||||
@ProtoId(5) val width: Int = 0,
|
||||
@ProtoId(6) val height: Int = 0,
|
||||
@ProtoId(7) val imageData: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ImageFilterResponse(
|
||||
@SerialId(1) val retCode: Int = 0,
|
||||
@SerialId(2) val imageData: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val costTime: Int = 0
|
||||
@ProtoId(1) val retCode: Int = 0,
|
||||
@ProtoId(2) val imageData: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val costTime: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class LoginSigHead(
|
||||
@SerialId(1) val loginsigType: Int = 0,
|
||||
@SerialId(2) val loginsig: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val loginsigType: Int = 0,
|
||||
@ProtoId(2) val loginsig: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class NewServiceTicket(
|
||||
@SerialId(1) val signature: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val ukey: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val signature: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val ukey: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class PicInfoExt(
|
||||
@SerialId(1) val picWidth: Int = 0,
|
||||
@SerialId(2) val picHeight: Int = 0,
|
||||
@SerialId(3) val picFlag: Int = 0,
|
||||
@SerialId(4) val busiType: Int = 0,
|
||||
@SerialId(5) val srcTerm: Int = 0,
|
||||
@SerialId(6) val platType: Int = 0,
|
||||
@SerialId(7) val netType: Int = 0,
|
||||
@SerialId(8) val imgType: Int = 0,
|
||||
@SerialId(9) val appPicType: Int = 0
|
||||
@ProtoId(1) val picWidth: Int = 0,
|
||||
@ProtoId(2) val picHeight: Int = 0,
|
||||
@ProtoId(3) val picFlag: Int = 0,
|
||||
@ProtoId(4) val busiType: Int = 0,
|
||||
@ProtoId(5) val srcTerm: Int = 0,
|
||||
@ProtoId(6) val platType: Int = 0,
|
||||
@ProtoId(7) val netType: Int = 0,
|
||||
@ProtoId(8) val imgType: Int = 0,
|
||||
@ProtoId(9) val appPicType: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class PicRspExtInfo(
|
||||
@SerialId(1) val skey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val clientIp: Int = 0,
|
||||
@SerialId(3) val upOffset: Long = 0L,
|
||||
@SerialId(4) val blockSize: Long = 0L
|
||||
@ProtoId(1) val skey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val clientIp: Int = 0,
|
||||
@ProtoId(3) val upOffset: Long = 0L,
|
||||
@ProtoId(4) val blockSize: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class QueryHoleRsp(
|
||||
@SerialId(1) val result: Int = 0,
|
||||
@SerialId(2) val dataHole: List<DataHole>? = null,
|
||||
@SerialId(3) val boolCompFlag: Boolean = false
|
||||
@ProtoId(1) val result: Int = 0,
|
||||
@ProtoId(2) val dataHole: List<DataHole>? = null,
|
||||
@ProtoId(3) val boolCompFlag: Boolean = false
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ReqDataHighwayHead(
|
||||
@SerialId(1) val msgBasehead: DataHighwayHead? = null,
|
||||
@SerialId(2) val msgSeghead: SegHead? = null,
|
||||
@SerialId(3) val reqExtendinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val timestamp: Long = 0L,
|
||||
@SerialId(5) val msgLoginSigHead: LoginSigHead? = null
|
||||
@ProtoId(1) val msgBasehead: DataHighwayHead? = null,
|
||||
@ProtoId(2) val msgSeghead: SegHead? = null,
|
||||
@ProtoId(3) val reqExtendinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val timestamp: Long = 0L,
|
||||
@ProtoId(5) val msgLoginSigHead: LoginSigHead? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RspBody(
|
||||
@SerialId(1) val msgQueryHoleRsp: QueryHoleRsp? = null
|
||||
@ProtoId(1) val msgQueryHoleRsp: QueryHoleRsp? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RspDataHighwayHead(
|
||||
@SerialId(1) val msgBasehead: DataHighwayHead? = null,
|
||||
@SerialId(2) val msgSeghead: SegHead? = null,
|
||||
@SerialId(3) val errorCode: Int = 0,
|
||||
@SerialId(4) val allowRetry: Int = 0,
|
||||
@SerialId(5) val cachecost: Int = 0,
|
||||
@SerialId(6) val htcost: Int = 0,
|
||||
@SerialId(7) val rspExtendinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(8) val timestamp: Long = 0L,
|
||||
@SerialId(9) val range: Long = 0L,
|
||||
@SerialId(10) val isReset: Int = 0
|
||||
@ProtoId(1) val msgBasehead: DataHighwayHead? = null,
|
||||
@ProtoId(2) val msgSeghead: SegHead? = null,
|
||||
@ProtoId(3) val errorCode: Int = 0,
|
||||
@ProtoId(4) val allowRetry: Int = 0,
|
||||
@ProtoId(5) val cachecost: Int = 0,
|
||||
@ProtoId(6) val htcost: Int = 0,
|
||||
@ProtoId(7) val rspExtendinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(8) val timestamp: Long = 0L,
|
||||
@ProtoId(9) val range: Long = 0L,
|
||||
@ProtoId(10) val isReset: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class SegHead(
|
||||
@SerialId(1) val serviceid: Int = 0,
|
||||
@SerialId(2) val filesize: Long = 0L,
|
||||
@SerialId(3) val dataoffset: Long = 0L,
|
||||
@SerialId(4) val datalength: Int = 0,
|
||||
@SerialId(5) val rtcode: Int = 0,
|
||||
@SerialId(6) val serviceticket: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(7) val flag: Int = 0,
|
||||
@SerialId(8) val md5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(9) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(10) val cacheAddr: Int = 0,
|
||||
@SerialId(11) val queryTimes: Int = 0,
|
||||
@SerialId(12) val updateCacheip: Int = 0
|
||||
@ProtoId(1) val serviceid: Int = 0,
|
||||
@ProtoId(2) val filesize: Long = 0L,
|
||||
@ProtoId(3) val dataoffset: Long = 0L,
|
||||
@ProtoId(4) val datalength: Int = 0,
|
||||
@ProtoId(5) val rtcode: Int = 0,
|
||||
@ProtoId(6) val serviceticket: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val flag: Int = 0,
|
||||
@ProtoId(8) val md5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(9) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(10) val cacheAddr: Int = 0,
|
||||
@ProtoId(11) val queryTimes: Int = 0,
|
||||
@ProtoId(12) val updateCacheip: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@ -286,31 +286,31 @@ class CSDataHighwayHead : ProtoBuf {
|
||||
class HwConfigPersistentPB : ProtoBuf {
|
||||
@Serializable
|
||||
class HwConfigItemPB(
|
||||
@SerialId(1) val ingKey: String = "",
|
||||
@SerialId(2) val endPointList: List<HwEndPointPB>? = null
|
||||
@ProtoId(1) val ingKey: String = "",
|
||||
@ProtoId(2) val endPointList: List<HwEndPointPB>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class HwConfigPB(
|
||||
@SerialId(1) val configItemList: List<HwConfigItemPB>? = null,
|
||||
@SerialId(2) val netSegConfList: List<HwNetSegConfPB>? = null,
|
||||
@SerialId(3) val shortVideoNetConf: List<HwNetSegConfPB>? = null,
|
||||
@SerialId(4) val configItemListIp6: List<HwConfigItemPB>? = null
|
||||
@ProtoId(1) val configItemList: List<HwConfigItemPB>? = null,
|
||||
@ProtoId(2) val netSegConfList: List<HwNetSegConfPB>? = null,
|
||||
@ProtoId(3) val shortVideoNetConf: List<HwNetSegConfPB>? = null,
|
||||
@ProtoId(4) val configItemListIp6: List<HwConfigItemPB>? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class HwEndPointPB(
|
||||
@SerialId(1) val ingHost: String = "",
|
||||
@SerialId(2) val int32Port: Int = 0,
|
||||
@SerialId(3) val int64Timestampe: Long = 0L
|
||||
@ProtoId(1) val ingHost: String = "",
|
||||
@ProtoId(2) val int32Port: Int = 0,
|
||||
@ProtoId(3) val int64Timestampe: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class HwNetSegConfPB(
|
||||
@SerialId(1) val int64NetType: Long = 0L,
|
||||
@SerialId(2) val int64SegSize: Long = 0L,
|
||||
@SerialId(3) val int64SegNum: Long = 0L,
|
||||
@SerialId(4) val int64CurConnNum: Long = 0L
|
||||
@ProtoId(1) val int64NetType: Long = 0L,
|
||||
@ProtoId(2) val int64SegSize: Long = 0L,
|
||||
@ProtoId(3) val int64SegNum: Long = 0L,
|
||||
@ProtoId(4) val int64CurConnNum: Long = 0L
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@ -318,8 +318,8 @@ class HwConfigPersistentPB : ProtoBuf {
|
||||
class HwSessionInfoPersistentPB : ProtoBuf {
|
||||
@Serializable
|
||||
class HwSessionInfoPB(
|
||||
@SerialId(1) val httpconnSigSession: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val sessionKey: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val httpconnSigSession: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val sessionKey: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@ -327,137 +327,137 @@ class HwSessionInfoPersistentPB : ProtoBuf {
|
||||
class Subcmd0x501 : ProtoBuf {
|
||||
@Serializable
|
||||
class ReqBody(
|
||||
@SerialId(1281) val msgSubcmd0x501ReqBody: SubCmd0x501ReqBody? = null
|
||||
@ProtoId(1281) val msgSubcmd0x501ReqBody: SubCmd0x501ReqBody? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RspBody(
|
||||
@SerialId(1281) val msgSubcmd0x501RspBody: SubCmd0x501Rspbody? = null
|
||||
@ProtoId(1281) val msgSubcmd0x501RspBody: SubCmd0x501Rspbody? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class SubCmd0x501ReqBody(
|
||||
@SerialId(1) val uin: Long = 0L,
|
||||
@SerialId(2) val idcId: Int = 0,
|
||||
@SerialId(3) val appid: Int = 0,
|
||||
@SerialId(4) val loginSigType: Int = 0,
|
||||
@SerialId(5) val loginSigTicket: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(6) val requestFlag: Int = 0,
|
||||
@SerialId(7) val uint32ServiceTypes: List<Int>? = null,
|
||||
@SerialId(8) val bid: Int = 0,
|
||||
@SerialId(9) val term: Int = 0,
|
||||
@SerialId(10) val plat: Int = 0,
|
||||
@SerialId(11) val net: Int = 0,
|
||||
@SerialId(12) val caller: Int = 0
|
||||
@ProtoId(1) val uin: Long = 0L,
|
||||
@ProtoId(2) val idcId: Int = 0,
|
||||
@ProtoId(3) val appid: Int = 0,
|
||||
@ProtoId(4) val loginSigType: Int = 0,
|
||||
@ProtoId(5) val loginSigTicket: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val requestFlag: Int = 0,
|
||||
@ProtoId(7) val uint32ServiceTypes: List<Int>? = null,
|
||||
@ProtoId(8) val bid: Int = 0,
|
||||
@ProtoId(9) val term: Int = 0,
|
||||
@ProtoId(10) val plat: Int = 0,
|
||||
@ProtoId(11) val net: Int = 0,
|
||||
@ProtoId(12) val caller: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class SubCmd0x501Rspbody(
|
||||
@SerialId(1) val httpconnSigSession: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val sessionKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val msgHttpconnAddrs: List<SrvAddrs>? = null,
|
||||
@SerialId(4) val preConnection: Int = 0,
|
||||
@SerialId(5) val csConn: Int = 0,
|
||||
@SerialId(6) val msgIpLearnConf: IpLearnConf? = null,
|
||||
@SerialId(7) val msgDynTimeoutConf: DynTimeOutConf? = null,
|
||||
@SerialId(8) val msgOpenUpConf: OpenUpConf? = null,
|
||||
@SerialId(9) val msgDownloadEncryptConf: DownloadEncryptConf? = null,
|
||||
@SerialId(10) val msgShortVideoConf: ShortVideoConf? = null,
|
||||
@SerialId(11) val msgPtvConf: PTVConf? = null
|
||||
@ProtoId(1) val httpconnSigSession: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val sessionKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val msgHttpconnAddrs: List<SrvAddrs>? = null,
|
||||
@ProtoId(4) val preConnection: Int = 0,
|
||||
@ProtoId(5) val csConn: Int = 0,
|
||||
@ProtoId(6) val msgIpLearnConf: IpLearnConf? = null,
|
||||
@ProtoId(7) val msgDynTimeoutConf: DynTimeOutConf? = null,
|
||||
@ProtoId(8) val msgOpenUpConf: OpenUpConf? = null,
|
||||
@ProtoId(9) val msgDownloadEncryptConf: DownloadEncryptConf? = null,
|
||||
@ProtoId(10) val msgShortVideoConf: ShortVideoConf? = null,
|
||||
@ProtoId(11) val msgPtvConf: PTVConf? = null
|
||||
) : ProtoBuf {
|
||||
@Serializable
|
||||
class DownloadEncryptConf(
|
||||
@SerialId(1) val boolEnableEncryptRequest: Boolean = false,
|
||||
@SerialId(2) val boolEnableEncryptedPic: Boolean = false,
|
||||
@SerialId(3) val ctrlFlag: Int = 0
|
||||
@ProtoId(1) val boolEnableEncryptRequest: Boolean = false,
|
||||
@ProtoId(2) val boolEnableEncryptedPic: Boolean = false,
|
||||
@ProtoId(3) val ctrlFlag: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class DynTimeOutConf(
|
||||
@SerialId(1) val tbase2g: Int = 0,
|
||||
@SerialId(2) val tbase3g: Int = 0,
|
||||
@SerialId(3) val tbase4g: Int = 0,
|
||||
@SerialId(4) val tbaseWifi: Int = 0,
|
||||
@SerialId(5) val torg2g: Int = 0,
|
||||
@SerialId(6) val torg3g: Int = 0,
|
||||
@SerialId(7) val torg4g: Int = 0,
|
||||
@SerialId(8) val torgWifi: Int = 0,
|
||||
@SerialId(9) val maxTimeout: Int = 0,
|
||||
@SerialId(10) val enableDynTimeout: Int = 0,
|
||||
@SerialId(11) val maxTimeout2g: Int = 0,
|
||||
@SerialId(12) val maxTimeout3g: Int = 0,
|
||||
@SerialId(13) val maxTimeout4g: Int = 0,
|
||||
@SerialId(14) val maxTimeoutWifi: Int = 0,
|
||||
@SerialId(15) val hbTimeout2g: Int = 0,
|
||||
@SerialId(16) val hbTimeout3g: Int = 0,
|
||||
@SerialId(17) val hbTimeout4g: Int = 0,
|
||||
@SerialId(18) val hbTimeoutWifi: Int = 0,
|
||||
@SerialId(19) val hbTimeoutDefault: Int = 0
|
||||
@ProtoId(1) val tbase2g: Int = 0,
|
||||
@ProtoId(2) val tbase3g: Int = 0,
|
||||
@ProtoId(3) val tbase4g: Int = 0,
|
||||
@ProtoId(4) val tbaseWifi: Int = 0,
|
||||
@ProtoId(5) val torg2g: Int = 0,
|
||||
@ProtoId(6) val torg3g: Int = 0,
|
||||
@ProtoId(7) val torg4g: Int = 0,
|
||||
@ProtoId(8) val torgWifi: Int = 0,
|
||||
@ProtoId(9) val maxTimeout: Int = 0,
|
||||
@ProtoId(10) val enableDynTimeout: Int = 0,
|
||||
@ProtoId(11) val maxTimeout2g: Int = 0,
|
||||
@ProtoId(12) val maxTimeout3g: Int = 0,
|
||||
@ProtoId(13) val maxTimeout4g: Int = 0,
|
||||
@ProtoId(14) val maxTimeoutWifi: Int = 0,
|
||||
@ProtoId(15) val hbTimeout2g: Int = 0,
|
||||
@ProtoId(16) val hbTimeout3g: Int = 0,
|
||||
@ProtoId(17) val hbTimeout4g: Int = 0,
|
||||
@ProtoId(18) val hbTimeoutWifi: Int = 0,
|
||||
@ProtoId(19) val hbTimeoutDefault: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Ip6Addr(
|
||||
@SerialId(1) val type: Int = 0,
|
||||
@SerialId(2) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val port: Int = 0,
|
||||
@SerialId(4) val area: Int = 0,
|
||||
@SerialId(5) val sameIsp: Int = 0
|
||||
@ProtoId(1) val type: Int = 0,
|
||||
@ProtoId(2) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val port: Int = 0,
|
||||
@ProtoId(4) val area: Int = 0,
|
||||
@ProtoId(5) val sameIsp: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class IpAddr(
|
||||
@SerialId(1) val type: Int = 0,
|
||||
@ProtoType(ProtoNumberType.FIXED) @SerialId(2) val ip: Int = 0,
|
||||
@SerialId(3) val port: Int = 0,
|
||||
@SerialId(4) val area: Int = 0,
|
||||
@SerialId(5) val sameIsp: Int = 0
|
||||
@ProtoId(1) val type: Int = 0,
|
||||
@ProtoType(ProtoNumberType.FIXED) @ProtoId(2) val ip: Int = 0,
|
||||
@ProtoId(3) val port: Int = 0,
|
||||
@ProtoId(4) val area: Int = 0,
|
||||
@ProtoId(5) val sameIsp: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class IpLearnConf(
|
||||
@SerialId(1) val refreshCachedIp: Int = 0,
|
||||
@SerialId(2) val enableIpLearn: Int = 0
|
||||
@ProtoId(1) val refreshCachedIp: Int = 0,
|
||||
@ProtoId(2) val enableIpLearn: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class NetSegConf(
|
||||
@SerialId(1) val netType: Int = 0,
|
||||
@SerialId(2) val segsize: Int = 0,
|
||||
@SerialId(3) val segnum: Int = 0,
|
||||
@SerialId(4) val curconnnum: Int = 0
|
||||
@ProtoId(1) val netType: Int = 0,
|
||||
@ProtoId(2) val segsize: Int = 0,
|
||||
@ProtoId(3) val segnum: Int = 0,
|
||||
@ProtoId(4) val curconnnum: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class OpenUpConf(
|
||||
@SerialId(1) val boolEnableOpenup: Boolean = false,
|
||||
@SerialId(2) val preSendSegnum: Int = 0,
|
||||
@SerialId(3) val preSendSegnum3g: Int = 0,
|
||||
@SerialId(4) val preSendSegnum4g: Int = 0,
|
||||
@SerialId(5) val preSendSegnumWifi: Int = 0
|
||||
@ProtoId(1) val boolEnableOpenup: Boolean = false,
|
||||
@ProtoId(2) val preSendSegnum: Int = 0,
|
||||
@ProtoId(3) val preSendSegnum3g: Int = 0,
|
||||
@ProtoId(4) val preSendSegnum4g: Int = 0,
|
||||
@ProtoId(5) val preSendSegnumWifi: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class PTVConf(
|
||||
@SerialId(1) val channelType: Int = 0,
|
||||
@SerialId(2) val msgNetsegconf: List<NetSegConf>? = null,
|
||||
@SerialId(3) val boolOpenHardwareCodec: Boolean = false
|
||||
@ProtoId(1) val channelType: Int = 0,
|
||||
@ProtoId(2) val msgNetsegconf: List<NetSegConf>? = null,
|
||||
@ProtoId(3) val boolOpenHardwareCodec: Boolean = false
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ShortVideoConf(
|
||||
@SerialId(1) val channelType: Int = 0,
|
||||
@SerialId(2) val msgNetsegconf: List<NetSegConf>? = null,
|
||||
@SerialId(3) val boolOpenHardwareCodec: Boolean = false,
|
||||
@SerialId(4) val boolSendAheadSignal: Boolean = false
|
||||
@ProtoId(1) val channelType: Int = 0,
|
||||
@ProtoId(2) val msgNetsegconf: List<NetSegConf>? = null,
|
||||
@ProtoId(3) val boolOpenHardwareCodec: Boolean = false,
|
||||
@ProtoId(4) val boolSendAheadSignal: Boolean = false
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class SrvAddrs(
|
||||
@SerialId(1) val serviceType: Int = 0,
|
||||
@SerialId(2) val msgAddrs: List<IpAddr>? = null,
|
||||
@SerialId(3) val fragmentSize: Int = 0,
|
||||
@SerialId(4) val msgNetsegconf: List<NetSegConf>? = null,
|
||||
@SerialId(5) val msgAddrsV6: List<Ip6Addr>? = null
|
||||
@ProtoId(1) val serviceType: Int = 0,
|
||||
@ProtoId(2) val msgAddrs: List<IpAddr>? = null,
|
||||
@ProtoId(3) val fragmentSize: Int = 0,
|
||||
@ProtoId(4) val msgNetsegconf: List<NetSegConf>? = null,
|
||||
@ProtoId(5) val msgAddrsV6: List<Ip6Addr>? = null
|
||||
) : ProtoBuf
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||
|
||||
@ -20,22 +20,22 @@ interface ImgReq : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class GetImgUrlReq(
|
||||
@SerialId(1) val srcUni: Int,
|
||||
@SerialId(2) val dstUni: Int,
|
||||
@SerialId(3) val fileResID: String,//UUID
|
||||
@ProtoId(1) val srcUni: Int,
|
||||
@ProtoId(2) val dstUni: Int,
|
||||
@ProtoId(3) val fileResID: String,//UUID
|
||||
/**
|
||||
* UUID例子: 没有找到
|
||||
*/
|
||||
@SerialId(4) val urlFlag: Int = 1,
|
||||
@ProtoId(4) val urlFlag: Int = 1,
|
||||
//5 unknown, 好像没用
|
||||
@SerialId(6) val urlType: Int = 4,
|
||||
@SerialId(7) val requestTerm: Int = 5,//确定
|
||||
@SerialId(8) val requestPlatformType: Int = 9,//确定
|
||||
@SerialId(9) val srcFileType: Int = 1,//2=ftn,1=picplatform,255
|
||||
@SerialId(10) val innerIP: Int = 0,//确定
|
||||
@SerialId(11) val addressBook: Int = 0,//[ChatType.internalID]== 1006为1[为CONTACT时] 我觉得发0没问题
|
||||
@SerialId(12) val buType: Int = 1,//确定
|
||||
@SerialId(13) val buildVer: String = "8.2.0.1296",//版本号
|
||||
@SerialId(14) val timestamp: Int = currentTimeSeconds.toInt(),//(pic_up_timestamp)
|
||||
@SerialId(15) val requestTransferType: Int = 1
|
||||
@ProtoId(6) val urlType: Int = 4,
|
||||
@ProtoId(7) val requestTerm: Int = 5,//确定
|
||||
@ProtoId(8) val requestPlatformType: Int = 9,//确定
|
||||
@ProtoId(9) val srcFileType: Int = 1,//2=ftn,1=picplatform,255
|
||||
@ProtoId(10) val innerIP: Int = 0,//确定
|
||||
@ProtoId(11) val addressBook: Int = 0,//[ChatType.internalID]== 1006为1[为CONTACT时] 我觉得发0没问题
|
||||
@ProtoId(12) val buType: Int = 1,//确定
|
||||
@ProtoId(13) val buildVer: String = "8.2.7.4410",//版本号
|
||||
@ProtoId(14) val timestamp: Int = currentTimeSeconds.toInt(),//(pic_up_timestamp)
|
||||
@ProtoId(15) val requestTransferType: Int = 1
|
||||
) : ImgReq
|
File diff suppressed because it is too large
Load Diff
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
@ -21,143 +21,143 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
internal class MsgComm : ProtoBuf {
|
||||
@Serializable
|
||||
internal class AppShareInfo(
|
||||
@SerialId(1) val appshareId: Int = 0,
|
||||
@SerialId(2) val appshareCookie: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val appshareResource: PluginInfo? = null
|
||||
@ProtoId(1) val appshareId: Int = 0,
|
||||
@ProtoId(2) val appshareCookie: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val appshareResource: PluginInfo? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class C2CTmpMsgHead(
|
||||
@SerialId(1) val c2cType: Int = 0,
|
||||
@SerialId(2) val serviceType: Int = 0,
|
||||
@SerialId(3) val groupUin: Long = 0L,
|
||||
@SerialId(4) val groupCode: Long = 0L,
|
||||
@SerialId(5) val sig: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(6) val sigType: Int = 0,
|
||||
@SerialId(7) val fromPhone: String = "",
|
||||
@SerialId(8) val toPhone: String = "",
|
||||
@SerialId(9) val lockDisplay: Int = 0,
|
||||
@SerialId(10) val directionFlag: Int = 0,
|
||||
@SerialId(11) val reserved: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val c2cType: Int = 0,
|
||||
@ProtoId(2) val serviceType: Int = 0,
|
||||
@ProtoId(3) val groupUin: Long = 0L,
|
||||
@ProtoId(4) val groupCode: Long = 0L,
|
||||
@ProtoId(5) val sig: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val sigType: Int = 0,
|
||||
@ProtoId(7) val fromPhone: String = "",
|
||||
@ProtoId(8) val toPhone: String = "",
|
||||
@ProtoId(9) val lockDisplay: Int = 0,
|
||||
@ProtoId(10) val directionFlag: Int = 0,
|
||||
@ProtoId(11) val reserved: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class ContentHead(
|
||||
@SerialId(1) val pkgNum: Int = 0,
|
||||
@SerialId(2) val pkgIndex: Int = 0,
|
||||
@SerialId(3) val divSeq: Int = 0,
|
||||
@SerialId(4) val autoReply: Int = 0
|
||||
@ProtoId(1) val pkgNum: Int = 0,
|
||||
@ProtoId(2) val pkgIndex: Int = 0,
|
||||
@ProtoId(3) val divSeq: Int = 0,
|
||||
@ProtoId(4) val autoReply: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class DiscussInfo(
|
||||
@SerialId(1) val discussUin: Long = 0L,
|
||||
@SerialId(2) val discussType: Int = 0,
|
||||
@SerialId(3) val discussInfoSeq: Long = 0L,
|
||||
@SerialId(4) val discussRemark: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val discussName: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val discussUin: Long = 0L,
|
||||
@ProtoId(2) val discussType: Int = 0,
|
||||
@ProtoId(3) val discussInfoSeq: Long = 0L,
|
||||
@ProtoId(4) val discussRemark: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val discussName: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class ExtGroupKeyInfo(
|
||||
@SerialId(1) val curMaxSeq: Int = 0,
|
||||
@SerialId(2) val curTime: Long = 0L
|
||||
@ProtoId(1) val curMaxSeq: Int = 0,
|
||||
@ProtoId(2) val curTime: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class GroupInfo(
|
||||
@SerialId(1) val groupCode: Long = 0L,
|
||||
@SerialId(2) val groupType: Int = 0,
|
||||
@SerialId(3) val groupInfoSeq: Long = 0L,
|
||||
@SerialId(4) val groupCard: String = "",
|
||||
@SerialId(5) val groupRank: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(6) val groupLevel: Int = 0,
|
||||
@SerialId(7) val groupCardType: Int = 0,
|
||||
@SerialId(8) val groupName: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val groupCode: Long = 0L,
|
||||
@ProtoId(2) val groupType: Int = 0,
|
||||
@ProtoId(3) val groupInfoSeq: Long = 0L,
|
||||
@ProtoId(4) val groupCard: String = "",
|
||||
@ProtoId(5) val groupRank: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val groupLevel: Int = 0,
|
||||
@ProtoId(7) val groupCardType: Int = 0,
|
||||
@ProtoId(8) val groupName: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class Msg(
|
||||
@SerialId(1) val msgHead: MsgHead,
|
||||
@SerialId(2) val contentHead: ContentHead? = null,
|
||||
@SerialId(3) val msgBody: ImMsgBody.MsgBody,
|
||||
@SerialId(4) val appshareInfo: AppShareInfo? = null
|
||||
@ProtoId(1) val msgHead: MsgHead,
|
||||
@ProtoId(2) val contentHead: ContentHead? = null,
|
||||
@ProtoId(3) val msgBody: ImMsgBody.MsgBody,
|
||||
@ProtoId(4) val appshareInfo: AppShareInfo? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class MsgHead(
|
||||
@SerialId(1) val fromUin: Long = 0L,
|
||||
@SerialId(2) val toUin: Long = 0L,
|
||||
@SerialId(3) val msgType: Int = 0,
|
||||
@SerialId(4) val c2cCmd: Int = 0,
|
||||
@SerialId(5) val msgSeq: Int = 0,
|
||||
@SerialId(6) val msgTime: Int = 0,
|
||||
@SerialId(7) var msgUid: Long = 0L,
|
||||
@SerialId(8) val c2cTmpMsgHead: C2CTmpMsgHead? = null,
|
||||
@SerialId(9) val groupInfo: GroupInfo? = null,
|
||||
@SerialId(10) val fromAppid: Int = 0,
|
||||
@SerialId(11) val fromInstid: Int = 0,
|
||||
@SerialId(12) val userActive: Int = 0,
|
||||
@SerialId(13) val discussInfo: DiscussInfo? = null,
|
||||
@SerialId(14) val fromNick: String = "",
|
||||
@SerialId(15) val authUin: Long = 0L,
|
||||
@SerialId(16) val authNick: String = "",
|
||||
@SerialId(17) val msgFlag: Int = 0,
|
||||
@SerialId(18) val authRemark: String = "",
|
||||
@SerialId(19) val groupName: String = "",
|
||||
@SerialId(20) val mutiltransHead: MutilTransHead? = null,
|
||||
@SerialId(21) val msgInstCtrl: ImMsgHead.InstCtrl? = null,
|
||||
@SerialId(22) val publicAccountGroupSendFlag: Int = 0,
|
||||
@SerialId(23) val wseqInC2cMsghead: Int = 0,
|
||||
@SerialId(24) val cpid: Long = 0L,
|
||||
@SerialId(25) val extGroupKeyInfo: ExtGroupKeyInfo? = null,
|
||||
@SerialId(26) val multiCompatibleText: String = "",
|
||||
@SerialId(27) val authSex: Int = 0,
|
||||
@SerialId(28) val isSrcMsg: Boolean = false
|
||||
@ProtoId(1) val fromUin: Long = 0L,
|
||||
@ProtoId(2) val toUin: Long = 0L,
|
||||
@ProtoId(3) val msgType: Int = 0,
|
||||
@ProtoId(4) val c2cCmd: Int = 0,
|
||||
@ProtoId(5) val msgSeq: Int = 0,
|
||||
@ProtoId(6) val msgTime: Int = 0,
|
||||
@ProtoId(7) var msgUid: Long = 0L,
|
||||
@ProtoId(8) val c2cTmpMsgHead: C2CTmpMsgHead? = null,
|
||||
@ProtoId(9) val groupInfo: GroupInfo? = null,
|
||||
@ProtoId(10) val fromAppid: Int = 0,
|
||||
@ProtoId(11) val fromInstid: Int = 0,
|
||||
@ProtoId(12) val userActive: Int = 0,
|
||||
@ProtoId(13) val discussInfo: DiscussInfo? = null,
|
||||
@ProtoId(14) val fromNick: String = "",
|
||||
@ProtoId(15) val authUin: Long = 0L,
|
||||
@ProtoId(16) val authNick: String = "",
|
||||
@ProtoId(17) val msgFlag: Int = 0,
|
||||
@ProtoId(18) val authRemark: String = "",
|
||||
@ProtoId(19) val groupName: String = "",
|
||||
@ProtoId(20) val mutiltransHead: MutilTransHead? = null,
|
||||
@ProtoId(21) val msgInstCtrl: ImMsgHead.InstCtrl? = null,
|
||||
@ProtoId(22) val publicAccountGroupSendFlag: Int = 0,
|
||||
@ProtoId(23) val wseqInC2cMsghead: Int = 0,
|
||||
@ProtoId(24) val cpid: Long = 0L,
|
||||
@ProtoId(25) val extGroupKeyInfo: ExtGroupKeyInfo? = null,
|
||||
@ProtoId(26) val multiCompatibleText: String = "",
|
||||
@ProtoId(27) val authSex: Int = 0,
|
||||
@ProtoId(28) val isSrcMsg: Boolean = false
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class MsgType0x210(
|
||||
@SerialId(1) val subMsgType: Int = 0,
|
||||
@SerialId(2) val msgContent: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val subMsgType: Int = 0,
|
||||
@ProtoId(2) val msgContent: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class MutilTransHead(
|
||||
@SerialId(1) val status: Int = 0,
|
||||
@SerialId(2) val msgId: Int = 0
|
||||
@ProtoId(1) val status: Int = 0,
|
||||
@ProtoId(2) val msgId: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PluginInfo(
|
||||
@SerialId(1) val resId: Int = 0,
|
||||
@SerialId(2) val pkgName: String = "",
|
||||
@SerialId(3) val newVer: Int = 0,
|
||||
@SerialId(4) val resType: Int = 0,
|
||||
@SerialId(5) val lanType: Int = 0,
|
||||
@SerialId(6) val priority: Int = 0,
|
||||
@SerialId(7) val resName: String = "",
|
||||
@SerialId(8) val resDesc: String = "",
|
||||
@SerialId(9) val resUrlBig: String = "",
|
||||
@SerialId(10) val resUrlSmall: String = "",
|
||||
@SerialId(11) val resConf: String = ""
|
||||
@ProtoId(1) val resId: Int = 0,
|
||||
@ProtoId(2) val pkgName: String = "",
|
||||
@ProtoId(3) val newVer: Int = 0,
|
||||
@ProtoId(4) val resType: Int = 0,
|
||||
@ProtoId(5) val lanType: Int = 0,
|
||||
@ProtoId(6) val priority: Int = 0,
|
||||
@ProtoId(7) val resName: String = "",
|
||||
@ProtoId(8) val resDesc: String = "",
|
||||
@ProtoId(9) val resUrlBig: String = "",
|
||||
@ProtoId(10) val resUrlSmall: String = "",
|
||||
@ProtoId(11) val resConf: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class Uin2Nick(
|
||||
@SerialId(1) val uin: Long = 0L,
|
||||
@SerialId(2) val nick: String = ""
|
||||
@ProtoId(1) val uin: Long = 0L,
|
||||
@ProtoId(2) val nick: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class UinPairMsg(
|
||||
@SerialId(1) val lastReadTime: Int = 0,
|
||||
@SerialId(2) val peerUin: Long = 0L,
|
||||
@SerialId(3) val msgCompleted: Int = 0,
|
||||
@SerialId(4) val msg: List<Msg>? = null,
|
||||
@SerialId(5) val unreadMsgNum: Int = 0,
|
||||
@SerialId(8) val c2cType: Int = 0,
|
||||
@SerialId(9) val serviceType: Int = 0,
|
||||
@SerialId(10) val pbReserve: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val lastReadTime: Int = 0,
|
||||
@ProtoId(2) val peerUin: Long = 0L,
|
||||
@ProtoId(3) val msgCompleted: Int = 0,
|
||||
@ProtoId(4) val msg: List<Msg>? = null,
|
||||
@ProtoId(5) val unreadMsgNum: Int = 0,
|
||||
@ProtoId(8) val c2cType: Int = 0,
|
||||
@ProtoId(9) val serviceType: Int = 0,
|
||||
@ProtoId(10) val pbReserve: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
|
||||
class MsgRevokeUserDef : ProtoBuf {
|
||||
@Serializable
|
||||
class MsgInfoUserDef(
|
||||
@ProtoId(1) val longMessageFlag: Int = 0,
|
||||
@ProtoId(2) val longMsgInfo: List<MsgInfoDef>? = null,
|
||||
@ProtoId(3) val fileUuid: List<String> = listOf()
|
||||
) : ProtoBuf {
|
||||
@Serializable
|
||||
class MsgInfoDef(
|
||||
@ProtoId(1) val msgSeq: Int = 0,
|
||||
@ProtoId(2) val longMsgId: Int = 0,
|
||||
@ProtoId(3) val longMsgNum: Int = 0,
|
||||
@ProtoId(4) val longMsgIndex: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class UinTypeUserDef(
|
||||
@ProtoId(1) val fromUinType: Int = 0,
|
||||
@ProtoId(2) val fromGroupCode: Long = 0L,
|
||||
@ProtoId(3) val fileUuid: List<String> = listOf()
|
||||
) : ProtoBuf
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -7,16 +7,16 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.oidb.oidb0x769
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
|
||||
class Oidb0x769 {
|
||||
@Serializable
|
||||
class RequestBody(
|
||||
@SerialId(1) val rpt_config_list: List<ConfigSeq>
|
||||
@ProtoId(1) val rpt_config_list: List<ConfigSeq>
|
||||
// @SerialId(2) val msg_device_info: DeviceInfo,
|
||||
// @SerialId(3) val str_info: String = "",
|
||||
// @SerialId(4) val province: String,
|
||||
@ -27,20 +27,20 @@ class Oidb0x769 {
|
||||
|
||||
@Serializable
|
||||
class QueryUinPackageUsageReq(
|
||||
@SerialId(1) val type: Int,
|
||||
@SerialId(2) val uinFileSize: Long = 0
|
||||
@ProtoId(1) val type: Int,
|
||||
@ProtoId(2) val uinFileSize: Long = 0
|
||||
): ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ConfigSeq(
|
||||
@SerialId(1) val type: Int, // uint
|
||||
@SerialId(2) val version: Int // uint
|
||||
@ProtoId(1) val type: Int, // uint
|
||||
@ProtoId(2) val version: Int // uint
|
||||
): ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class DeviceInfo(
|
||||
@SerialId(1) val brand: String,
|
||||
@SerialId(2) val model: String
|
||||
@ProtoId(1) val brand: String,
|
||||
@ProtoId(2) val model: String
|
||||
//@SerialId(3) val os: OS,
|
||||
//@SerialId(4) val cpu: CPU,
|
||||
//@SerialId(5) val memory: Memory,
|
||||
@ -51,45 +51,45 @@ class Oidb0x769 {
|
||||
|
||||
@Serializable
|
||||
class OS(
|
||||
@SerialId(1) val type: Int = 1,
|
||||
@SerialId(2) val version: String,
|
||||
@SerialId(3) val sdk: String,
|
||||
@SerialId(4) val kernel: String,
|
||||
@SerialId(5) val rom: String
|
||||
@ProtoId(1) val type: Int = 1,
|
||||
@ProtoId(2) val version: String,
|
||||
@ProtoId(3) val sdk: String,
|
||||
@ProtoId(4) val kernel: String,
|
||||
@ProtoId(5) val rom: String
|
||||
): ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Camera(
|
||||
@SerialId(1) val primary: Long,
|
||||
@SerialId(2) val secondary: Long,
|
||||
@SerialId(3) val flag: Boolean
|
||||
@ProtoId(1) val primary: Long,
|
||||
@ProtoId(2) val secondary: Long,
|
||||
@ProtoId(3) val flag: Boolean
|
||||
): ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class CPU(
|
||||
@SerialId(1) val model: String,
|
||||
@SerialId(2) val frequency: Int,
|
||||
@SerialId(3) val cores: Int
|
||||
@ProtoId(1) val model: String,
|
||||
@ProtoId(2) val frequency: Int,
|
||||
@ProtoId(3) val cores: Int
|
||||
): ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Memory(
|
||||
@SerialId(1) val total: Int,
|
||||
@SerialId(2) val process: Int
|
||||
@ProtoId(1) val total: Int,
|
||||
@ProtoId(2) val process: Int
|
||||
): ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Screen(
|
||||
@SerialId(1) val model: String,
|
||||
@SerialId(2) val width: Int,
|
||||
@SerialId(3) val height: Int,
|
||||
@SerialId(4) val dpi: Int,
|
||||
@SerialId(5) val multiTouch: Boolean
|
||||
@ProtoId(1) val model: String,
|
||||
@ProtoId(2) val width: Int,
|
||||
@ProtoId(3) val height: Int,
|
||||
@ProtoId(4) val dpi: Int,
|
||||
@ProtoId(5) val multiTouch: Boolean
|
||||
): ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class Storage(
|
||||
@SerialId(1) val builtin: Int,
|
||||
@SerialId(2) val external: Int
|
||||
@ProtoId(1) val builtin: Int,
|
||||
@ProtoId(2) val external: Int
|
||||
): ProtoBuf
|
||||
}
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
@ -18,11 +18,11 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
internal class MsgOnlinePush {
|
||||
@Serializable
|
||||
internal class PbPushMsg(
|
||||
@SerialId(1) val msg: MsgComm.Msg,
|
||||
@SerialId(2) val svrip: Int = 0,
|
||||
@SerialId(3) val pushToken: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val pingFlag: Int = 0,
|
||||
@SerialId(9) val generalFlag: Int = 0
|
||||
@ProtoId(1) val msg: MsgComm.Msg,
|
||||
@ProtoId(2) val svrip: Int = 0,
|
||||
@ProtoId(3) val pushToken: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val pingFlag: Int = 0,
|
||||
@ProtoId(9) val generalFlag: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@ -30,24 +30,24 @@ internal class MsgOnlinePush {
|
||||
class OnlinePushTrans : ProtoBuf {
|
||||
@Serializable
|
||||
class ExtGroupKeyInfo(
|
||||
@SerialId(1) val curMaxSeq: Int = 0,
|
||||
@SerialId(2) val curTime: Long = 0L
|
||||
@ProtoId(1) val curMaxSeq: Int = 0,
|
||||
@ProtoId(2) val curTime: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class PbMsgInfo(
|
||||
@SerialId(1) val fromUin: Long = 0L,
|
||||
@SerialId(2) val toUin: Long = 0L,
|
||||
@SerialId(3) val msgType: Int = 0,
|
||||
@SerialId(4) val msgSubtype: Int = 0,
|
||||
@SerialId(5) val msgSeq: Int = 0,
|
||||
@SerialId(6) val msgUid: Long = 0L,
|
||||
@SerialId(7) val msgTime: Int = 0,
|
||||
@SerialId(8) val realMsgTime: Int = 0,
|
||||
@SerialId(9) val nickName: String = "",
|
||||
@SerialId(10) val msgData: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(11) val svrIp: Int = 0,
|
||||
@SerialId(12) val extGroupKeyInfo: OnlinePushTrans.ExtGroupKeyInfo? = null,
|
||||
@SerialId(17) val generalFlag: Int = 0
|
||||
@ProtoId(1) val fromUin: Long = 0L,
|
||||
@ProtoId(2) val toUin: Long = 0L,
|
||||
@ProtoId(3) val msgType: Int = 0,
|
||||
@ProtoId(4) val msgSubtype: Int = 0,
|
||||
@ProtoId(5) val msgSeq: Int = 0,
|
||||
@ProtoId(6) val msgUid: Long = 0L,
|
||||
@ProtoId(7) val msgTime: Int = 0,
|
||||
@ProtoId(8) val realMsgTime: Int = 0,
|
||||
@ProtoId(9) val nickName: String = "",
|
||||
@ProtoId(10) val msgData: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(11) val svrIp: Int = 0,
|
||||
@ProtoId(12) val extGroupKeyInfo: OnlinePushTrans.ExtGroupKeyInfo? = null,
|
||||
@ProtoId(17) val generalFlag: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
@ -18,68 +18,68 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
class Generalflags : ProtoBuf {
|
||||
@Serializable
|
||||
class ResvAttr(
|
||||
@SerialId(1) val globalGroupLevel: Int = 0,
|
||||
@SerialId(2) val nearbyCharmLevel: Int = 0,
|
||||
@SerialId(3) val redbagMsgSenderUin: Long = 0L,
|
||||
@SerialId(4) val titleId: Int = 0,
|
||||
@SerialId(5) val robotMsgFlag: Int = 0,
|
||||
@SerialId(6) val wantGiftSenderUin: Long = 0L,
|
||||
@SerialId(7) val stickerX: Float = 0.0f,
|
||||
@SerialId(8) val stickerY: Float = 0.0f,
|
||||
@SerialId(9) val stickerWidth: Float = 0.0f,
|
||||
@SerialId(10) val stickerHeight: Float = 0.0f,
|
||||
@SerialId(11) val stickerRotate: Int = 0,
|
||||
@SerialId(12) val stickerHostMsgseq: Long = 0L,
|
||||
@SerialId(13) val stickerHostMsguid: Long = 0L,
|
||||
@SerialId(14) val stickerHostTime: Long = 0L,
|
||||
@SerialId(15) val mobileCustomFont: Int = 0,
|
||||
@SerialId(16) val tailKey: Int = 0,
|
||||
@SerialId(17) val showTailFlag: Int = 0,
|
||||
@SerialId(18) val doutuMsgType: Int = 0,
|
||||
@SerialId(19) val doutuCombo: Int = 0,
|
||||
@SerialId(20) val customFeatureid: Int = 0,
|
||||
@SerialId(21) val goldenMsgType: Int = 0,
|
||||
@SerialId(22) val goldenMsgInfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(23) val botMessageClassId: Int = 0,
|
||||
@SerialId(24) val subscriptionUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(25) val pendantDiyId: Int = 0,
|
||||
@SerialId(26) val timedMessage: Int = 0,
|
||||
@SerialId(27) val holidayFlag: Int = 0,
|
||||
@SerialId(29) val kplInfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(30) val faceId: Int = 0,
|
||||
@SerialId(31) val diyFontTimestamp: Int = 0,
|
||||
@SerialId(32) val redEnvelopeType: Int = 0,
|
||||
@SerialId(33) val shortVideoId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(34) val reqFontEffectId: Int = 0,
|
||||
@SerialId(35) val loveLanguageFlag: Int = 0,
|
||||
@SerialId(36) val aioSyncToStoryFlag: Int = 0,
|
||||
@SerialId(37) val uploadImageToQzoneFlag: Int = 0,
|
||||
@SerialId(39) val uploadImageToQzoneParam: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(40) val groupConfessSig: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(41) val subfontId: Long = 0L,
|
||||
@SerialId(42) val msgFlagType: Int = 0,
|
||||
@SerialId(43) val uint32CustomFeatureid: List<Int>? = null,
|
||||
@SerialId(44) val richCardNameVer: Int = 0,
|
||||
@SerialId(47) val msgInfoFlag: Int = 0,
|
||||
@SerialId(48) val serviceMsgType: Int = 0,
|
||||
@SerialId(49) val serviceMsgRemindType: Int = 0,
|
||||
@SerialId(50) val serviceMsgName: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(51) val vipType: Int = 0,
|
||||
@SerialId(52) val vipLevel: Int = 0,
|
||||
@SerialId(53) val pbPttWaveform: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(54) val userBigclubLevel: Int = 0,
|
||||
@SerialId(55) val userBigclubFlag: Int = 0,
|
||||
@SerialId(56) val nameplate: Int = 0,
|
||||
@SerialId(57) val autoReply: Int = 0,
|
||||
@SerialId(58) val reqIsBigclubHidden: Int = 0,
|
||||
@SerialId(59) val showInMsgList: Int = 0,
|
||||
@SerialId(60) val oacMsgExtend: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(61) val groupMemberFlagEx2: Int = 0,
|
||||
@SerialId(62) val groupRingtoneId: Int = 0,
|
||||
@SerialId(63) val robotGeneralTrans: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(64) val troopPobingTemplate: Int = 0,
|
||||
@SerialId(65) val hudongMark: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(66) val groupInfoFlagEx3: Int = 0
|
||||
@ProtoId(1) val globalGroupLevel: Int = 0,
|
||||
@ProtoId(2) val nearbyCharmLevel: Int = 0,
|
||||
@ProtoId(3) val redbagMsgSenderUin: Long = 0L,
|
||||
@ProtoId(4) val titleId: Int = 0,
|
||||
@ProtoId(5) val robotMsgFlag: Int = 0,
|
||||
@ProtoId(6) val wantGiftSenderUin: Long = 0L,
|
||||
@ProtoId(7) val stickerX: Float = 0.0f,
|
||||
@ProtoId(8) val stickerY: Float = 0.0f,
|
||||
@ProtoId(9) val stickerWidth: Float = 0.0f,
|
||||
@ProtoId(10) val stickerHeight: Float = 0.0f,
|
||||
@ProtoId(11) val stickerRotate: Int = 0,
|
||||
@ProtoId(12) val stickerHostMsgseq: Long = 0L,
|
||||
@ProtoId(13) val stickerHostMsguid: Long = 0L,
|
||||
@ProtoId(14) val stickerHostTime: Long = 0L,
|
||||
@ProtoId(15) val mobileCustomFont: Int = 0,
|
||||
@ProtoId(16) val tailKey: Int = 0,
|
||||
@ProtoId(17) val showTailFlag: Int = 0,
|
||||
@ProtoId(18) val doutuMsgType: Int = 0,
|
||||
@ProtoId(19) val doutuCombo: Int = 0,
|
||||
@ProtoId(20) val customFeatureid: Int = 0,
|
||||
@ProtoId(21) val goldenMsgType: Int = 0,
|
||||
@ProtoId(22) val goldenMsgInfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(23) val botMessageClassId: Int = 0,
|
||||
@ProtoId(24) val subscriptionUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(25) val pendantDiyId: Int = 0,
|
||||
@ProtoId(26) val timedMessage: Int = 0,
|
||||
@ProtoId(27) val holidayFlag: Int = 0,
|
||||
@ProtoId(29) val kplInfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(30) val faceId: Int = 0,
|
||||
@ProtoId(31) val diyFontTimestamp: Int = 0,
|
||||
@ProtoId(32) val redEnvelopeType: Int = 0,
|
||||
@ProtoId(33) val shortVideoId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(34) val reqFontEffectId: Int = 0,
|
||||
@ProtoId(35) val loveLanguageFlag: Int = 0,
|
||||
@ProtoId(36) val aioSyncToStoryFlag: Int = 0,
|
||||
@ProtoId(37) val uploadImageToQzoneFlag: Int = 0,
|
||||
@ProtoId(39) val uploadImageToQzoneParam: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(40) val groupConfessSig: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(41) val subfontId: Long = 0L,
|
||||
@ProtoId(42) val msgFlagType: Int = 0,
|
||||
@ProtoId(43) val uint32CustomFeatureid: List<Int>? = null,
|
||||
@ProtoId(44) val richCardNameVer: Int = 0,
|
||||
@ProtoId(47) val msgInfoFlag: Int = 0,
|
||||
@ProtoId(48) val serviceMsgType: Int = 0,
|
||||
@ProtoId(49) val serviceMsgRemindType: Int = 0,
|
||||
@ProtoId(50) val serviceMsgName: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(51) val vipType: Int = 0,
|
||||
@ProtoId(52) val vipLevel: Int = 0,
|
||||
@ProtoId(53) val pbPttWaveform: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(54) val userBigclubLevel: Int = 0,
|
||||
@ProtoId(55) val userBigclubFlag: Int = 0,
|
||||
@ProtoId(56) val nameplate: Int = 0,
|
||||
@ProtoId(57) val autoReply: Int = 0,
|
||||
@ProtoId(58) val reqIsBigclubHidden: Int = 0,
|
||||
@ProtoId(59) val showInMsgList: Int = 0,
|
||||
@ProtoId(60) val oacMsgExtend: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(61) val groupMemberFlagEx2: Int = 0,
|
||||
@ProtoId(62) val groupRingtoneId: Int = 0,
|
||||
@ProtoId(63) val robotGeneralTrans: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(64) val troopPobingTemplate: Int = 0,
|
||||
@ProtoId(65) val hudongMark: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(66) val groupInfoFlagEx3: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@ -87,27 +87,27 @@ class Generalflags : ProtoBuf {
|
||||
class ResvAttrForGiftMsg : ProtoBuf {
|
||||
@Serializable
|
||||
class ActivityGiftInfo(
|
||||
@SerialId(1) val isActivityGift: Int = 0,
|
||||
@SerialId(2) val textColor: String = "",
|
||||
@SerialId(3) val text: String = "",
|
||||
@SerialId(4) val url: String = ""
|
||||
@ProtoId(1) val isActivityGift: Int = 0,
|
||||
@ProtoId(2) val textColor: String = "",
|
||||
@ProtoId(3) val text: String = "",
|
||||
@ProtoId(4) val url: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class InteractGift(
|
||||
@SerialId(1) val interactId: ByteArray = EMPTY_BYTE_ARRAY
|
||||
@ProtoId(1) val interactId: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ResvAttr(
|
||||
@SerialId(1) val int32SendScore: Int = 0,
|
||||
@SerialId(2) val int32RecvScore: Int = 0,
|
||||
@SerialId(3) val charmHeroism: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val buttonFlag: Int = 0,
|
||||
@SerialId(5) val objColor: Int = 0,
|
||||
@SerialId(6) val animationType: Int = 0,
|
||||
@SerialId(7) val msgInteractGift: ResvAttrForGiftMsg.InteractGift? = null,
|
||||
@SerialId(8) val activityGiftInfo: ResvAttrForGiftMsg.ActivityGiftInfo? = null
|
||||
@ProtoId(1) val int32SendScore: Int = 0,
|
||||
@ProtoId(2) val int32RecvScore: Int = 0,
|
||||
@ProtoId(3) val charmHeroism: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val buttonFlag: Int = 0,
|
||||
@ProtoId(5) val objColor: Int = 0,
|
||||
@ProtoId(6) val animationType: Int = 0,
|
||||
@ProtoId(7) val msgInteractGift: ResvAttrForGiftMsg.InteractGift? = null,
|
||||
@ProtoId(8) val activityGiftInfo: ResvAttrForGiftMsg.ActivityGiftInfo? = null
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@ -115,9 +115,9 @@ class ResvAttrForGiftMsg : ProtoBuf {
|
||||
class SourceMsg : ProtoBuf {
|
||||
@Serializable
|
||||
class ResvAttr(
|
||||
@SerialId(1) val richMsg2: ByteArray? = null,
|
||||
@SerialId(2) val oriMsgtype: Int? = null,
|
||||
@SerialId(3) val origUids: Long? = null // 原来是 list
|
||||
@ProtoId(1) val richMsg2: ByteArray? = null,
|
||||
@ProtoId(2) val oriMsgtype: Int? = null,
|
||||
@ProtoId(3) val origUids: Long? = null // 原来是 list
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@ -125,17 +125,17 @@ class SourceMsg : ProtoBuf {
|
||||
class VideoFile : ProtoBuf {
|
||||
@Serializable
|
||||
class ResvAttr(
|
||||
@SerialId(1) val hotvideoIcon: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(2) val hotvideoTitle: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(3) val hotvideoUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(4) val hotvideoIconSub: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(5) val specialVideoType: Int = 0,
|
||||
@SerialId(6) val dynamicText: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(7) val msgTailType: Int = 0,
|
||||
@SerialId(8) val redEnvelopeType: Int = 0,
|
||||
@SerialId(9) val shortVideoId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(10) val animojiModelId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@SerialId(11) val longVideoKandianType: Int = 0,
|
||||
@SerialId(12) val source: Int = 0
|
||||
@ProtoId(1) val hotvideoIcon: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val hotvideoTitle: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val hotvideoUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val hotvideoIconSub: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val specialVideoType: Int = 0,
|
||||
@ProtoId(6) val dynamicText: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val msgTailType: Int = 0,
|
||||
@ProtoId(8) val redEnvelopeType: Int = 0,
|
||||
@ProtoId(9) val shortVideoId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(10) val animojiModelId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(11) val longVideoKandianType: Int = 0,
|
||||
@ProtoId(12) val source: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
@ -9,30 +9,30 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
|
||||
class StatSvcGetOnline {
|
||||
@Serializable
|
||||
class Instance(
|
||||
@SerialId(1) val instanceId: Int = 0,
|
||||
@SerialId(2) val clientType: Int = 0
|
||||
@ProtoId(1) val instanceId: Int = 0,
|
||||
@ProtoId(2) val clientType: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class ReqBody(
|
||||
@SerialId(1) val uin: Long = 0L,
|
||||
@SerialId(2) val appid: Int = 0
|
||||
@ProtoId(1) val uin: Long = 0L,
|
||||
@ProtoId(2) val appid: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class RspBody(
|
||||
@SerialId(1) val errorCode: Int = 0,
|
||||
@SerialId(2) val errorMsg: String = "",
|
||||
@SerialId(3) val uin: Long = 0L,
|
||||
@SerialId(4) val appid: Int = 0,
|
||||
@SerialId(5) val timeInterval: Int = 0,
|
||||
@SerialId(6) val msgInstances: List<StatSvcGetOnline.Instance>? = null
|
||||
@ProtoId(1) val errorCode: Int = 0,
|
||||
@ProtoId(2) val errorMsg: String = "",
|
||||
@ProtoId(3) val uin: Long = 0L,
|
||||
@ProtoId(4) val appid: Int = 0,
|
||||
@ProtoId(5) val timeInterval: Int = 0,
|
||||
@ProtoId(6) val msgInstances: List<StatSvcGetOnline.Instance>? = null
|
||||
) : ProtoBuf
|
||||
}
|
@ -9,23 +9,23 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.random.Random
|
||||
|
||||
@Serializable
|
||||
class SyncCookie(
|
||||
@SerialId(1) val time1: Long? = null, // 1580277992
|
||||
@SerialId(2) val time: Long, // 1580277992
|
||||
@SerialId(3) val unknown1: Long = Random.nextLong().absoluteValue,// 678328038
|
||||
@SerialId(4) val unknown2: Long = Random.nextLong().absoluteValue, // 1687142153
|
||||
@SerialId(5) val const1: Long = const1_, // 1458467940
|
||||
@SerialId(11) val const2: Long = const2_, // 2683038258
|
||||
@SerialId(12) val unknown3: Long = 0x1d,
|
||||
@SerialId(13) val lastSyncTime: Long? = null,
|
||||
@SerialId(14) val unknown4: Long = 0
|
||||
@ProtoId(1) val time1: Long? = null, // 1580277992
|
||||
@ProtoId(2) val time: Long, // 1580277992
|
||||
@ProtoId(3) val unknown1: Long = Random.nextLong().absoluteValue,// 678328038
|
||||
@ProtoId(4) val unknown2: Long = Random.nextLong().absoluteValue, // 1687142153
|
||||
@ProtoId(5) val const1: Long = const1_, // 1458467940
|
||||
@ProtoId(11) val const2: Long = const2_, // 2683038258
|
||||
@ProtoId(12) val unknown3: Long = 0x1d,
|
||||
@ProtoId(13) val lastSyncTime: Long? = null,
|
||||
@ProtoId(14) val unknown4: Long = 0
|
||||
) : ProtoBuf
|
||||
|
||||
private val const1_: Long = Random.nextLong().absoluteValue
|
||||
|
@ -15,13 +15,11 @@ import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.writeFully
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.utils.cryptor.ECDH
|
||||
import net.mamoe.mirai.utils.cryptor.ECDHKeyPair
|
||||
import net.mamoe.mirai.utils.io.encryptAndWrite
|
||||
import net.mamoe.mirai.utils.io.writeShortLVByteArray
|
||||
|
||||
/**
|
||||
* Encryption method to be used for packet body.
|
||||
*/
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
internal interface EncryptMethod {
|
||||
val id: Int
|
||||
|
||||
@ -33,16 +31,6 @@ internal interface EncryptMethodSessionKey : EncryptMethod {
|
||||
val currentLoginState: Int
|
||||
val sessionKey: ByteArray
|
||||
|
||||
/**
|
||||
* buildPacket{
|
||||
* byte 1
|
||||
* byte if (currentLoginState == 2) 3 else 2
|
||||
* fully key
|
||||
* short 258
|
||||
* short 0
|
||||
* fully encrypted
|
||||
* }
|
||||
*/
|
||||
override fun makeBody(client: QQAndroidClient, body: BytePacketBuilder.() -> Unit): ByteReadPacket =
|
||||
buildPacket {
|
||||
require(currentLoginState == 2 || currentLoginState == 3) { "currentLoginState must be either 2 or 3" }
|
||||
@ -65,29 +53,27 @@ inline class EncryptMethodSessionKeyLoginState3(override val sessionKey: ByteArr
|
||||
override val currentLoginState: Int get() = 3
|
||||
}
|
||||
|
||||
inline class EncryptMethodECDH135(override val ecdh: ECDH) :
|
||||
internal inline class EncryptMethodECDH135(override val ecdh: ECDH) :
|
||||
EncryptMethodECDH {
|
||||
override val id: Int get() = 135
|
||||
}
|
||||
|
||||
inline class EncryptMethodECDH7(override val ecdh: ECDH) :
|
||||
internal inline class EncryptMethodECDH7(override val ecdh: ECDH) :
|
||||
EncryptMethodECDH {
|
||||
override val id: Int get() = 7
|
||||
}
|
||||
|
||||
internal interface EncryptMethodECDH : EncryptMethod {
|
||||
companion object {
|
||||
operator fun invoke(ecdh: ECDH): EncryptMethodECDH {
|
||||
return if (ecdh.keyPair === ECDHKeyPair.DefaultStub) {
|
||||
EncryptMethodECDH135(ecdh)
|
||||
} else EncryptMethodECDH7(ecdh)
|
||||
}
|
||||
}
|
||||
|
||||
val ecdh: ECDH
|
||||
|
||||
/**
|
||||
* **Packet Structure**
|
||||
* byte 1
|
||||
* byte 1
|
||||
* byte[] [ECDH.privateKey]
|
||||
* short 258
|
||||
* short [ECDH.publicKey].size
|
||||
* byte[] [ECDH.publicKey]
|
||||
* byte[] encrypted `body()` by [ECDH.shareKey]
|
||||
*/
|
||||
override fun makeBody(client: QQAndroidClient, body: BytePacketBuilder.() -> Unit): ByteReadPacket =
|
||||
buildPacket {
|
||||
writeByte(1) // const
|
||||
@ -95,15 +81,15 @@ internal interface EncryptMethodECDH : EncryptMethod {
|
||||
writeFully(client.randomKey)
|
||||
writeShort(258) // const
|
||||
|
||||
// writeShortLVByteArray("04 CB 36 66 98 56 1E 93 6E 80 C1 57 E0 74 CA B1 3B 0B B6 8D DE B2 82 45 48 A1 B1 8D D4 FB 61 22 AF E1 2F E4 8C 52 66 D8 D7 26 9D 76 51 A8 EB 6F E7".hexToBytes())
|
||||
|
||||
if (ecdh.keyPair === ECDHKeyPair.DefaultStub) {
|
||||
writeShortLVByteArray(ECDHKeyPair.DefaultStub.defaultPublicKey)
|
||||
encryptAndWrite(ECDHKeyPair.DefaultStub.defaultShareKey, body)
|
||||
} else {
|
||||
writeShortLVByteArray(ecdh.keyPair.publicKey.getEncoded().drop(23).take(49).toByteArray().also {
|
||||
// it.toUHexString().debugPrint("PUBLIC KEY")
|
||||
check(it[0].toInt() == 0x04) { "Bad publicKey generated. Expected first element=0x04, got${it[0]}" }
|
||||
//check(ecdh.calculateShareKeyByPeerPublicKey(it.adjustToPublicKey()).contentEquals(ecdh.keyPair.shareKey)) { "PublicKey Validation failed" }
|
||||
})
|
||||
|
||||
// encryptAndWrite("26 33 BA EC 86 EB 79 E6 BC E0 20 06 5E A9 56 6C".hexToBytes(), body)
|
||||
encryptAndWrite(ecdh.keyPair.initialShareKey, body)
|
||||
}
|
||||
}
|
||||
}
|
@ -34,39 +34,7 @@ internal class OutgoingPacket constructor(
|
||||
internal val KEY_16_ZEROS = ByteArray(16)
|
||||
internal val EMPTY_BYTE_ARRAY = ByteArray(0)
|
||||
|
||||
/**
|
||||
* com.tencent.qphone.base.util.CodecWarpper#encodeRequest(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, byte[], int, int, java.lang.String, byte, byte, byte, byte[], byte[], boolean)
|
||||
*/
|
||||
@Deprecated("危险", level = DeprecationLevel.ERROR)
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
internal inline fun OutgoingPacketFactory<*>.buildOutgoingPacket(
|
||||
client: QQAndroidClient,
|
||||
bodyType: Byte = 1, // 1: PB?
|
||||
name: String? = this.commandName,
|
||||
commandName: String = this.commandName,
|
||||
key: ByteArray = client.wLoginSigInfo.d2Key,
|
||||
body: BytePacketBuilder.(sequenceId: Int) -> Unit
|
||||
): OutgoingPacket {
|
||||
val sequenceId: Int = client.nextSsoSequenceId()
|
||||
|
||||
return OutgoingPacket(name, commandName, sequenceId, buildPacket {
|
||||
writeIntLVPacket(lengthOffset = { it + 4 }) {
|
||||
writeInt(0x0B)
|
||||
writeByte(bodyType)
|
||||
writeInt(sequenceId)
|
||||
writeByte(0)
|
||||
client.uin.toString().let {
|
||||
writeInt(it.length + 4)
|
||||
writeStringUtf8(it)
|
||||
}
|
||||
encryptAndWrite(key) {
|
||||
body(sequenceId)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal inline fun OutgoingPacketFactory<*>.buildOutgoingUniPacket(
|
||||
client: QQAndroidClient,
|
||||
bodyType: Byte = 1, // 1: PB?
|
||||
@ -98,7 +66,7 @@ internal inline fun OutgoingPacketFactory<*>.buildOutgoingUniPacket(
|
||||
}
|
||||
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal inline fun IncomingPacketFactory<*>.buildResponseUniPacket(
|
||||
client: QQAndroidClient,
|
||||
bodyType: Byte = 1, // 1: PB?
|
||||
@ -128,7 +96,7 @@ internal inline fun IncomingPacketFactory<*>.buildResponseUniPacket(
|
||||
})
|
||||
}
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal inline fun BytePacketBuilder.writeUniPacket(
|
||||
commandName: String,
|
||||
unknownData: ByteArray,
|
||||
@ -161,7 +129,7 @@ internal val NO_ENCRYPT: ByteArray = ByteArray(0)
|
||||
/**
|
||||
* com.tencent.qphone.base.util.CodecWarpper#encodeRequest(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, byte[], int, int, java.lang.String, byte, byte, byte, byte[], byte[], boolean)
|
||||
*/
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal inline fun OutgoingPacketFactory<*>.buildLoginOutgoingPacket(
|
||||
client: QQAndroidClient,
|
||||
bodyType: Byte,
|
||||
@ -199,7 +167,7 @@ internal inline fun OutgoingPacketFactory<*>.buildLoginOutgoingPacket(
|
||||
|
||||
private inline val BRP_STUB get() = ByteReadPacket.Empty
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal inline fun BytePacketBuilder.writeSsoPacket(
|
||||
client: QQAndroidClient,
|
||||
subAppId: Long,
|
||||
@ -265,7 +233,7 @@ internal inline fun BytePacketBuilder.writeSsoPacket(
|
||||
writeIntLVPacket(lengthOffset = { it + 4 }, builder = body)
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||
internal fun BytePacketBuilder.writeOicqRequestPacket(
|
||||
client: QQAndroidClient,
|
||||
encryptMethod: EncryptMethod,
|
||||
|
File diff suppressed because one or more lines are too long
@ -15,9 +15,10 @@ import kotlinx.io.core.toByteArray
|
||||
import kotlinx.io.core.writeFully
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.LoginType
|
||||
import net.mamoe.mirai.qqandroid.utils.NetworkType
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.MiraiPlatformUtils
|
||||
import net.mamoe.mirai.utils.currentTimeMillis
|
||||
import net.mamoe.mirai.utils.io.*
|
||||
import net.mamoe.mirai.utils.md5
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
@ -76,6 +77,7 @@ fun BytePacketBuilder.t18(
|
||||
} shouldEqualsTo 22
|
||||
}
|
||||
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
fun BytePacketBuilder.t106(
|
||||
appId: Long = 16L,
|
||||
subAppId: Long = 537062845L,
|
||||
@ -96,7 +98,7 @@ fun BytePacketBuilder.t106(
|
||||
guid?.requireSize(16)
|
||||
|
||||
writeShortLVPacket {
|
||||
encryptAndWrite(md5(passwordMd5 + ByteArray(4) + (salt.takeIf { it != 0L } ?: uin).toInt().toByteArray())) {
|
||||
encryptAndWrite(MiraiPlatformUtils.md5(passwordMd5 + ByteArray(4) + (salt.takeIf { it != 0L } ?: uin).toInt().toByteArray())) {
|
||||
writeShort(4)//TGTGTVer
|
||||
writeInt(Random.nextInt())
|
||||
writeInt(5)//ssoVer
|
||||
@ -321,12 +323,13 @@ fun BytePacketBuilder.t144(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
fun BytePacketBuilder.t109(
|
||||
androidId: ByteArray
|
||||
) {
|
||||
writeShort(0x109)
|
||||
writeShortLVPacket {
|
||||
writeFully(md5(androidId))
|
||||
writeFully(MiraiPlatformUtils.md5(androidId))
|
||||
} shouldEqualsTo 16
|
||||
}
|
||||
|
||||
@ -556,21 +559,23 @@ fun BytePacketBuilder.t400(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
fun BytePacketBuilder.t187(
|
||||
macAddress: ByteArray
|
||||
) {
|
||||
writeShort(0x187)
|
||||
writeShortLVPacket {
|
||||
writeFully(md5(macAddress)) // may be md5
|
||||
writeFully(MiraiPlatformUtils.md5(macAddress)) // may be md5
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
fun BytePacketBuilder.t188(
|
||||
androidId: ByteArray
|
||||
) {
|
||||
writeShort(0x188)
|
||||
writeShortLVPacket {
|
||||
writeFully(md5(androidId))
|
||||
writeFully(MiraiPlatformUtils.md5(androidId))
|
||||
} shouldEqualsTo 16
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user