mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-24 23:20:09 +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:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -8,10 +8,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
- name: setup-android
|
- name: Set up JDK 1.8
|
||||||
uses: msfjarvis/setup-android@0.2
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
# Gradle tasks to run - If you want to run ./gradlew assemble, specify assemble here.
|
java-version: 1.8
|
||||||
gradleTasks: build -x mirai-core:jvmTest
|
- 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
|
## `0.15.0` 2020/2/14
|
||||||
|
|
||||||
### mirai-core
|
### mirai-core
|
||||||
@ -132,7 +326,7 @@ TIMPC
|
|||||||
## `0.10.0` *2019/12/23*
|
## `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 等监听模式.
|
删除原本的 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.**
|
**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)
|
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
|
## Use as a library
|
||||||
You can install mirai as a library into your project.
|
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
|
```kotlin
|
||||||
repositories{
|
repositories{
|
||||||
jcenter()
|
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.
|
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/)
|
`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.
|
Mirai is still under experimental stage, it is suggested to keep the version newest.
|
||||||
|
|
||||||
**common**
|
**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
|
Kotlin 1.3.61
|
||||||
|
|
||||||
On JVM: Java 6
|
On JVM: Java 6
|
||||||
|
|
||||||
On Android: SDK 15
|
On Android: SDK 15
|
||||||
|
|
||||||
### Using java
|
### Using java
|
||||||
Q: Can I use Mirai without Kotlin?
|
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.
|
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
|
## Acknowledgements
|
||||||
Mirai uses these open-source libraries.
|
|
||||||
|
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)
|
- [kotlin-stdlib](https://github.com/JetBrains/kotlin)
|
||||||
- [kotlinx-coroutines](https://github.com/Kotlin/kotlinx.coroutines)
|
- [kotlinx-coroutines](https://github.com/Kotlin/kotlinx.coroutines)
|
||||||
@ -92,7 +101,19 @@ Mirai uses these open-source libraries.
|
|||||||
- [javafx](https://github.com/openjdk/jfx)
|
- [javafx](https://github.com/openjdk/jfx)
|
||||||
- [kotlinx-serialization](https://github.com/Kotlin/kotlinx.serialization)
|
- [kotlinx-serialization](https://github.com/Kotlin/kotlinx.serialization)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
## Acknowledgement
|
Copyright (C) 2019-2020 mamoe and Mirai contributors
|
||||||
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)
|
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)
|
[![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/)
|
[![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)**
|
**[English](README-eng.md)**
|
||||||
|
|
||||||
多平台 **QQ Android 和 TimPC** 协议支持库与高效率的机器人框架.
|
|
||||||
|
多平台 **QQ Android** 和 **TIM PC** 协议支持库与高效率的机器人框架.
|
||||||
纯 Kotlin 实现协议和支持框架,模块<b>全部免费开源</b>。
|
纯 Kotlin 实现协议和支持框架,模块<b>全部免费开源</b>。
|
||||||
目前可运行在 JVM 或 Android。
|
目前可运行在 JVM 或 Android 平台。
|
||||||
Mirai既可以作为你项目中的QQ协议支持Lib, 也可以作为单独的Application与插件承载QQ机器人
|
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
|
Mirai 目前为快速流转(Moving fast)状态, 增量版本之间可能不具有兼容性,任何功能都可能在没有警告的情况下添加、删除或者更改。
|
||||||
QQ for Android (8.2.0 版本,2019 年 12 月)协议的实现,目前完成大部分。
|
|
||||||
- 高兼容性:协议仅含极少部分为硬编码,其余全部随官方方式动态生成
|
|
||||||
- 高安全性:密匙随机,ECDH 动态计算
|
|
||||||
- 已支持大部分使用场景, 详情请在[Project](https://github.com/mamoe/mirai/projects/3)查看
|
|
||||||
|
|
||||||
### mirai-core-timpc
|
Mirai 源码完全开放, 您可以参考 Mirai 的协议实现来开发其他框架, 但需注明来源并遵守开源协议要求 (AGPLv3)。
|
||||||
TIM PC (2.3.2 版本,2019 年 8 月)协议的实现,相较于 core,仅新增少量 API. 详见 [README.md](mirai-core-timpc/)
|
|
||||||
支持的功能:
|
|
||||||
- 消息收发:图片文字复合消息,图片消息
|
|
||||||
- 群管功能:群员列表,禁言
|
|
||||||
(目前不再更新此协议,请关注上文的安卓协议)
|
|
||||||
|
|
||||||
## Use directly
|
### 开发者
|
||||||
**直接使用 Mirai(终端环境/网页面板(将来)).**
|
|
||||||
[Mirai-Console](https://github.com/mamoe/mirai/tree/master/mirai-console) 插件支持, 在终端中启动 Mirai 并获得机器人服务
|
|
||||||
本模块还未完善。
|
|
||||||
|
|
||||||
## Use as a library
|
**了解 mirai 架构**: [Wiki](https://github.com/mamoe/mirai/wiki/Home)
|
||||||
**mirai-core 为独立设计, 可以作为库内置于任意 Java(JVM)/Android 项目中使用.**
|
|
||||||
|
|
||||||
### Gradle
|
#### 使用 mirai 作为服务器,为 mirai 开发插件
|
||||||
Mirai 只发布在 `jcenter`, 因此请确保在 `build.gradle` 添加 `jcenter()` 仓库:
|
|
||||||
```kotlin
|
|
||||||
repositories{
|
|
||||||
jcenter()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
若您需要使用在跨平台项目, 则要对各个目标平台添加不同的依赖,这与 kotlin 相关跨平台库的依赖是类似的。
|
|
||||||
**若您只需要使用在单一平台, 则只需要添加一项该平台的依赖. 如只在 JVM 运行则只需要`-jvm`的依赖**
|
|
||||||
|
|
||||||
请将 `VERSION` 替换为最新的版本(如 `0.13.0`):
|
- (官方)`Java` 或 `Kotlin`: 为 [mirai-console](https://github.com/mamoe/mirai-console) 直接编写插件并与其他插件开发者合作共享
|
||||||
[![Download](https://api.bintray.com/packages/him188moe/mirai/mirai-core/images/download.svg)](https://bintray.com/him188moe/mirai/mirai-core/)
|
- (社区)`C`, `C++` 等原生语言: [mirai-native](https://github.com/iTXTech/mirai-native) 支持酷Q插件在mirai上运行
|
||||||
**Mirai 目前还处于实验性阶段, 我们无法保证任何稳定性, API 也可能会随时修改.**
|
- (社区)`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 为第三方依赖库引入项目
|
||||||
Mirai 核心由 API 模块(`mirai-core`)和协议模块组成。
|
|
||||||
只添加 API 模块将无法正常工作。
|
|
||||||
现在只推荐使用 TIMPC 协议,请参照下文选择对应目标平台的依赖添加。
|
|
||||||
|
|
||||||
**common**
|
Demos: [mirai-demos](https://github.com/mamoe/mirai-demos)
|
||||||
```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 内存
|
|
||||||
|
|
||||||
## 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
|
[<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)
|
||||||
### On JVM or Android
|
[<img width="60px" height="60px" src="https://avatars2.githubusercontent.com/u/28707253?s=60&v=4" />](https://github.com/ryoii)
|
||||||
现在体验低付出高效率的 Mirai
|
[<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)
|
||||||
```kotlin
|
[<img width="60px" height="60px" src="https://avatars2.githubusercontent.com/u/18532671?s=60&v=4" />](https://github.com/uebian)
|
||||||
val bot = TIMPC.Bot(qqId, password).alsoLogin()
|
[<img width="60px" height="60px" src="https://avatars2.githubusercontent.com/u/10308687?s=60&v=4" />](https://github.com/Freedom0925)
|
||||||
bot.subscribeMessages {
|
[<img width="60px" height="60px" src="https://avatars3.githubusercontent.com/u/16398479?s=60&v=4" />](https://github.com/ice1000)
|
||||||
"你好" reply "你好!"
|
[<img width="60px" height="60px" src="https://avatars0.githubusercontent.com/u/20042607?s=60&v=4" />](https://github.com/PragmaTwice)
|
||||||
"profile" reply { sender.queryProfile() }
|
[<img width="60px" height="60px" src="https://avatars0.githubusercontent.com/u/25280943?s=60&v=4" />](https://github.com/HoshinoTented)
|
||||||
contains("图片"){ File(imagePath).send() }
|
[<img width="60px" height="60px" src="https://avatars3.githubusercontent.com/u/40517459?s=60&v=4" />](https://github.com/Cyenoch)
|
||||||
}
|
|
||||||
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) 示例和演示程序
|
|
||||||
|
|
||||||
|
|
||||||
## Build Requirements
|
## 鸣谢
|
||||||
|
|
||||||
- Kotlin 1.3.61
|
特别感谢 [JetBrains](https://www.jetbrains.com/?from=mirai) 为开源项目提供免费的 [IntelliJ IDEA](https://www.jetbrains.com/idea/?from=mirai) 等 IDE 的授权
|
||||||
- JDK 8 (required)
|
[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/?from=mirai)
|
||||||
- JDK 11(for protocol tools, optional)
|
|
||||||
- Android SDK 29 (for Android target, optional)
|
### 第三方类库(无排名)
|
||||||
|
|
||||||
#### Libraries used
|
|
||||||
感谢:
|
|
||||||
- [kotlin-stdlib](https://github.com/JetBrains/kotlin)
|
- [kotlin-stdlib](https://github.com/JetBrains/kotlin)
|
||||||
- [kotlinx-coroutines](https://github.com/Kotlin/kotlinx.coroutines)
|
- [kotlinx-coroutines](https://github.com/Kotlin/kotlinx.coroutines)
|
||||||
- [kotlinx-io](https://github.com/Kotlin/kotlinx-io)
|
- [kotlinx-io](https://github.com/Kotlin/kotlinx-io)
|
||||||
- [kotlin-reflect](https://github.com/JetBrains/kotlin)
|
- [kotlin-reflect](https://github.com/JetBrains/kotlin)
|
||||||
- [pcap4j](https://github.com/kaitoy/pcap4j)
|
|
||||||
- [atomicfu](https://github.com/Kotlin/kotlinx.atomicfu)
|
- [atomicfu](https://github.com/Kotlin/kotlinx.atomicfu)
|
||||||
- [ktor](https://github.com/ktorio/ktor)
|
- [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)
|
- [kotlinx-serialization](https://github.com/Kotlin/kotlinx.serialization)
|
||||||
- [bouncycastle](https://www.bouncycastle.org/java.html)
|
- [bouncycastle](https://www.bouncycastle.org/java.html)
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
协议原版权归属腾讯科技股份有限公司所有,本项目其他代码遵守:
|
协议原版权归属腾讯科技股份有限公司所有,本项目其他代码遵守:
|
||||||
**GNU AFFERO GENERAL PUBLIC LICENSE version 3**
|
**GNU AFFERO GENERAL PUBLIC LICENSE version 3**
|
||||||
|
|
||||||
其中部分要求:
|
其中部分要求:
|
||||||
|
|
||||||
- (见 LICENSE 第 13 节) 尽管本许可协议有其他规定,但如果您修改本程序,则修改后的版本必须显着地为所有通过计算机网络与它进行远程交互的用户(如果您的版本支持这种交互)提供从网络服务器通过一些标准或惯用的软件复制方法**免费**访问相应的**源代码**的机会
|
- (见 LICENSE 第 13 节) 尽管本许可协议有其他规定,但如果您修改本程序,则修改后的版本必须显着地为所有通过计算机网络与它进行远程交互的用户(如果您的版本支持这种交互)提供从网络服务器通过一些标准或惯用的软件复制方法**免费**访问相应的**源代码**的机会
|
||||||
- (见 LICENSE 第 4 节) 您可以免费或收费地传递这个项目的源代码或目标代码(即编译结果), **但前提是提供明显的版权声明** (您需要标注本 `GitHub` 项目地址)
|
- (见 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
|
# style guide
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
# config
|
# config
|
||||||
mirai_version=0.15.0
|
miraiVersion=0.27.0
|
||||||
mirai_japt_version=1.0.0
|
|
||||||
kotlin.incremental.multiplatform=true
|
kotlin.incremental.multiplatform=true
|
||||||
kotlin.parallel.tasks.in.project=true
|
kotlin.parallel.tasks.in.project=true
|
||||||
# kotlin
|
# kotlin
|
||||||
kotlinVersion=1.3.61
|
kotlinVersion=1.3.70
|
||||||
# kotlin libraries
|
# kotlin libraries
|
||||||
serializationVersion=0.14.0
|
serializationVersion=0.20.0
|
||||||
coroutinesVersion=1.3.3
|
coroutinesVersion=1.3.4
|
||||||
atomicFuVersion=0.14.1
|
atomicFuVersion=0.14.1
|
||||||
kotlinXIoVersion=0.1.16
|
kotlinXIoVersion=0.1.16
|
||||||
coroutinesIoVersion=0.1.16
|
coroutinesIoVersion=0.1.16
|
||||||
# utility
|
# utility
|
||||||
ktorVersion=1.3.1
|
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
|
// 部分源码来自 kotlinx.coroutines
|
||||||
|
// Source code from kotlinx.coroutines
|
||||||
|
|
||||||
def pomConfig = {
|
def pomConfig = {
|
||||||
licenses {
|
licenses {
|
||||||
@ -12,6 +13,7 @@ def pomConfig = {
|
|||||||
developer {
|
developer {
|
||||||
id "mamoe"
|
id "mamoe"
|
||||||
name "Mamoe Technologies"
|
name "Mamoe Technologies"
|
||||||
|
email "support@mamoe.net"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scm {
|
scm {
|
||||||
@ -65,12 +67,16 @@ bintrayUpload.dependsOn {
|
|||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
// empty xxx-javadoc.jar
|
// empty xxx-javadoc.jar
|
||||||
task javadocJar(type: Jar) {
|
task javadocJar(type: Jar) {
|
||||||
archiveClassifier = 'javadoc'
|
archiveClassifier = 'javadoc'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (Exception e){
|
||||||
|
|
||||||
|
}
|
||||||
publishing {
|
publishing {
|
||||||
publications.all {
|
publications.all {
|
||||||
// add empty javadocs (no need for MPP root publication which publishes only pom file)
|
// add empty javadocs (no need for MPP root publication which publishes only pom file)
|
||||||
@ -88,7 +94,7 @@ publishing {
|
|||||||
it.artifactId = "$project.name-common"
|
it.artifactId = "$project.name-common"
|
||||||
break
|
break
|
||||||
case 'jvm':
|
case 'jvm':
|
||||||
it.artifactId = "$project.name"
|
it.artifactId = "$project.name-jvm"
|
||||||
break
|
break
|
||||||
case 'js':
|
case 'js':
|
||||||
case 'native':
|
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
|
#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
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=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
|
# mirai-core-qqandroid
|
||||||
|
|
||||||
Protocol support for QQ for Android for Mirai.
|
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-atomicfu")
|
||||||
id("kotlinx-serialization")
|
id("kotlinx-serialization")
|
||||||
`maven-publish`
|
`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 kotlinVersion: String by rootProject.ext
|
||||||
val atomicFuVersion: 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 kotlinXIoVersion: String by rootProject.ext
|
||||||
val coroutinesIoVersion: String by rootProject.ext
|
val coroutinesIoVersion: String by rootProject.ext
|
||||||
|
|
||||||
val klockVersion: String by rootProject.ext
|
|
||||||
val ktorVersion: String by rootProject.ext
|
val ktorVersion: String by rootProject.ext
|
||||||
|
|
||||||
val serializationVersion: 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"
|
description = "QQ protocol library"
|
||||||
version = rootProject.ext.get("mirai_version")!!.toString()
|
|
||||||
|
|
||||||
val isAndroidSDKAvailable: Boolean by project
|
val isAndroidSDKAvailable: Boolean by project
|
||||||
|
|
||||||
|
val miraiVersion: String by project
|
||||||
|
version = miraiVersion
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
if (isAndroidSDKAvailable) {
|
if (isAndroidSDKAvailable) {
|
||||||
apply(from = rootProject.file("gradle/android.gradle"))
|
apply(from = rootProject.file("gradle/android.gradle"))
|
||||||
@ -75,6 +77,7 @@ kotlin {
|
|||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(kotlinx("serialization-runtime-common", serializationVersion))
|
api(kotlinx("serialization-runtime-common", serializationVersion))
|
||||||
|
api(kotlinx("serialization-protobuf-common", serializationVersion))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commonTest {
|
commonTest {
|
||||||
@ -88,6 +91,7 @@ kotlin {
|
|||||||
if (isAndroidSDKAvailable) {
|
if (isAndroidSDKAvailable) {
|
||||||
val androidMain by getting {
|
val androidMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
api(kotlinx("serialization-protobuf", serializationVersion))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +109,7 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
|
runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
|
||||||
api(kotlinx("serialization-runtime", serializationVersion))
|
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
|
* QQ for Android
|
||||||
*/
|
*/
|
||||||
|
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||||
actual object QQAndroid : BotFactory {
|
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 {
|
actual override fun Bot(context: Context, qq: Long, password: String, configuration: BotConfiguration): Bot {
|
||||||
return QQAndroidBot(context, BotAccount(qq, password), configuration)
|
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
|
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.BotAccount
|
||||||
import net.mamoe.mirai.utils.BotConfiguration
|
import net.mamoe.mirai.utils.BotConfiguration
|
||||||
import net.mamoe.mirai.utils.Context
|
import net.mamoe.mirai.utils.Context
|
||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
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
|
internal actual class QQAndroidBot
|
||||||
actual constructor(
|
actual constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
account: BotAccount,
|
account: BotAccount,
|
||||||
configuration: BotConfiguration
|
configuration: BotConfiguration
|
||||||
) : QQAndroidBotBase(context, account, configuration)
|
) : 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.Bot
|
||||||
import net.mamoe.mirai.BotFactory
|
import net.mamoe.mirai.BotFactory
|
||||||
|
import net.mamoe.mirai.qqandroid.QQAndroid.Bot
|
||||||
import net.mamoe.mirai.utils.BotConfiguration
|
import net.mamoe.mirai.utils.BotConfiguration
|
||||||
import net.mamoe.mirai.utils.Context
|
import net.mamoe.mirai.utils.Context
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QQ for Android
|
* QQ for Android
|
||||||
*/
|
*/
|
||||||
|
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||||
expect object QQAndroid : BotFactory {
|
expect object QQAndroid : BotFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用指定的 [配置][configuration] 构造 [Bot] 实例
|
* 使用指定的 [配置][configuration] 构造 [Bot] 实例
|
||||||
*/
|
*/
|
||||||
|
@JvmName("newBot")
|
||||||
override fun Bot(context: Context, qq: Long, password: String, configuration: BotConfiguration): Bot
|
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
|
package net.mamoe.mirai.qqandroid
|
||||||
|
|
||||||
import kotlinx.coroutines.launch
|
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.contact.*
|
||||||
import net.mamoe.mirai.data.*
|
import net.mamoe.mirai.data.*
|
||||||
import net.mamoe.mirai.event.broadcast
|
import net.mamoe.mirai.event.broadcast
|
||||||
import net.mamoe.mirai.event.events.*
|
import net.mamoe.mirai.event.events.*
|
||||||
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
|
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
|
||||||
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
|
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
|
||||||
import net.mamoe.mirai.message.data.CustomFaceFromFile
|
import net.mamoe.mirai.message.MessageReceipt
|
||||||
import net.mamoe.mirai.message.data.Image
|
|
||||||
import net.mamoe.mirai.message.data.MessageChain
|
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.HighwayHelper
|
||||||
import net.mamoe.mirai.qqandroid.network.highway.postImage
|
import net.mamoe.mirai.qqandroid.network.highway.postImage
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo
|
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.qqandroid.utils.toIpV4AddressString
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
import net.mamoe.mirai.utils.io.toUHexString
|
import net.mamoe.mirai.utils.io.toUHexString
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlin.contracts.contract
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.FriendInfo as JceFriendInfo
|
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(
|
internal inline class FriendInfoImpl(
|
||||||
private val jceFriendInfo: JceFriendInfo
|
private val jceFriendInfo: JceFriendInfo
|
||||||
) : FriendInfo {
|
) : FriendInfo {
|
||||||
@ -61,28 +53,33 @@ internal class QQImpl(
|
|||||||
override val coroutineContext: CoroutineContext,
|
override val coroutineContext: CoroutineContext,
|
||||||
override val id: Long,
|
override val id: Long,
|
||||||
private val friendInfo: FriendInfo
|
private val friendInfo: FriendInfo
|
||||||
) : ContactImpl(), QQ {
|
) : QQ() {
|
||||||
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||||
override val nick: String
|
override val nick: String
|
||||||
get() = friendInfo.nick
|
get() = friendInfo.nick
|
||||||
|
|
||||||
override suspend fun sendMessage(message: MessageChain) {
|
override suspend fun sendMessage(message: MessageChain): MessageReceipt<QQ> {
|
||||||
val event = FriendMessageSendEvent(this, message).broadcast()
|
val event = FriendMessageSendEvent(this, message).broadcast()
|
||||||
if (event.isCancelled) {
|
if (event.isCancelled) {
|
||||||
throw EventCancelledException("cancelled by FriendMessageSendEvent")
|
throw EventCancelledException("cancelled by FriendMessageSendEvent")
|
||||||
}
|
}
|
||||||
|
lateinit var source: MessageSource
|
||||||
bot.network.run {
|
bot.network.run {
|
||||||
check(
|
check(
|
||||||
MessageSvc.PbSendMsg.ToFriend(
|
MessageSvc.PbSendMsg.ToFriend(
|
||||||
bot.client,
|
bot.client,
|
||||||
id,
|
id,
|
||||||
event.message
|
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" }
|
) { "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) {
|
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
|
||||||
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
|
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
|
||||||
}
|
}
|
||||||
@ -102,8 +99,9 @@ internal class QQImpl(
|
|||||||
)
|
)
|
||||||
).sendAndExpect<LongConn.OffPicUp.Response>()
|
).sendAndExpect<LongConn.OffPicUp.Response>()
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST") // bug
|
||||||
return when (response) {
|
return when (response) {
|
||||||
is LongConn.OffPicUp.Response.FileExists -> NotOnlineImageFromFile(
|
is LongConn.OffPicUp.Response.FileExists -> OfflineFriendImage(
|
||||||
filepath = response.resourceId,
|
filepath = response.resourceId,
|
||||||
md5 = response.imageInfo.fileMd5,
|
md5 = response.imageInfo.fileMd5,
|
||||||
fileLength = response.imageInfo.fileSize.toInt(),
|
fileLength = response.imageInfo.fileSize.toInt(),
|
||||||
@ -114,19 +112,27 @@ internal class QQImpl(
|
|||||||
ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
|
ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
|
||||||
}
|
}
|
||||||
is LongConn.OffPicUp.Response.RequireUpload -> {
|
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(
|
//HighwayHelper.uploadImage(
|
||||||
// client = bot.client,
|
// client = bot.client,
|
||||||
// serverIp = response.serverIp[0].toIpV4AddressString(),
|
// serverIp = response.serverIp[0].toIpV4AddressString(),
|
||||||
// serverPort = response.serverPort[0],
|
// serverPort = response.serverPort[0],
|
||||||
// imageInput = image.input,
|
// imageInput = image.input,
|
||||||
// inputSize = image.inputSize.toInt(),
|
// inputSize = image.inputSize.toInt(),
|
||||||
// md5 = image.md5,
|
// fileMd5 = image.md5,
|
||||||
// uKey = response.uKey,
|
// uKey = response.uKey,
|
||||||
// commandId = 1
|
// commandId = 1
|
||||||
//)
|
//)
|
||||||
|
// 为什么不能 ??
|
||||||
|
|
||||||
return NotOnlineImageFromFile(
|
return OfflineFriendImage(
|
||||||
filepath = response.resourceId,
|
filepath = response.resourceId,
|
||||||
md5 = image.md5,
|
md5 = image.md5,
|
||||||
fileLength = image.inputSize.toInt(),
|
fileLength = image.inputSize.toInt(),
|
||||||
@ -144,7 +150,22 @@ internal class QQImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} 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
|
@MiraiExperimentalAPI
|
||||||
@ -162,29 +183,54 @@ internal class QQImpl(
|
|||||||
TODO("not implemented")
|
TODO("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun toString(): String = "QQ($id)"
|
||||||
if (this === other) return true
|
|
||||||
return other is QQ && other.id == this.id
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = super.hashCode()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
internal class MemberImpl(
|
internal class MemberImpl(
|
||||||
qq: QQImpl,
|
val qq: QQImpl, // 不要 WeakRef
|
||||||
group: GroupImpl,
|
group: GroupImpl,
|
||||||
override val coroutineContext: CoroutineContext,
|
override val coroutineContext: CoroutineContext,
|
||||||
memberInfo: MemberInfo
|
memberInfo: MemberInfo
|
||||||
) : ContactImpl(), Member, QQ by qq {
|
) : Member() {
|
||||||
override val group: GroupImpl by group.unsafeWeakRef()
|
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
|
override var permission: MemberPermission = memberInfo.permission
|
||||||
|
|
||||||
|
@Suppress("PropertyName")
|
||||||
internal var _nameCard: String = memberInfo.nameCard
|
internal var _nameCard: String = memberInfo.nameCard
|
||||||
|
|
||||||
|
@Suppress("PropertyName")
|
||||||
internal var _specialTitle: String = memberInfo.specialTitle
|
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
|
override var nameCard: String
|
||||||
get() = _nameCard
|
get() = _nameCard
|
||||||
set(newValue) {
|
set(newValue) {
|
||||||
@ -220,7 +266,7 @@ internal class MemberImpl(
|
|||||||
newValue
|
newValue
|
||||||
).sendWithoutExpect()
|
).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 {
|
override fun hashCode(): Int {
|
||||||
if (this === other) return true
|
var result = bot.hashCode()
|
||||||
return other is Member && other.id == this.id
|
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(
|
internal class MemberInfoImpl(
|
||||||
private val jceInfo: StTroopMemberInfo,
|
jceInfo: StTroopMemberInfo,
|
||||||
private val groupOwnerId: Long
|
groupOwnerId: Long
|
||||||
) : MemberInfo {
|
) : MemberInfo {
|
||||||
override val uin: Long get() = jceInfo.memberUin
|
override val uin: Long = jceInfo.memberUin
|
||||||
override val nameCard: String get() = jceInfo.sName ?: ""
|
override val nameCard: String = jceInfo.sName ?: ""
|
||||||
override val nick: String get() = jceInfo.nick
|
override val nick: String = jceInfo.nick
|
||||||
override val permission: MemberPermission
|
override val permission: MemberPermission = when {
|
||||||
get() = when {
|
|
||||||
jceInfo.memberUin == groupOwnerId -> MemberPermission.OWNER
|
jceInfo.memberUin == groupOwnerId -> MemberPermission.OWNER
|
||||||
jceInfo.dwFlag == 1L -> MemberPermission.ADMINISTRATOR
|
jceInfo.dwFlag == 1L -> MemberPermission.ADMINISTRATOR
|
||||||
else -> MemberPermission.MEMBER
|
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")
|
@Suppress("PropertyName")
|
||||||
@UseExperimental(MiraiInternalAPI::class)
|
@OptIn(MiraiInternalAPI::class)
|
||||||
internal class GroupImpl(
|
internal class GroupImpl(
|
||||||
bot: QQAndroidBot, override val coroutineContext: CoroutineContext,
|
bot: QQAndroidBot, override val coroutineContext: CoroutineContext,
|
||||||
override val id: Long,
|
override val id: Long,
|
||||||
groupInfo: GroupInfo,
|
groupInfo: GroupInfo,
|
||||||
members: Sequence<MemberInfo>
|
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()
|
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||||
val uin: Long = groupInfo.uin
|
val uin: Long = groupInfo.uin
|
||||||
|
|
||||||
override lateinit var owner: Member
|
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
|
override lateinit var botPermission: MemberPermission
|
||||||
|
|
||||||
var _botMuteRemaining: Int = groupInfo.botMuteRemaining
|
var _botMuteTimestamp: Int = groupInfo.botMuteRemaining
|
||||||
|
|
||||||
override val botMuteRemaining: Int =
|
override val botMuteRemaining: Int =
|
||||||
if (_botMuteRemaining == 0 || _botMuteRemaining == 0xFFFFFFFF.toInt()) {
|
if (_botMuteTimestamp == 0 || _botMuteTimestamp == 0xFFFFFFFF.toInt()) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
_botMuteRemaining - currentTimeSeconds.toInt() - bot.client.timeDifference.toInt()
|
_botMuteTimestamp - currentTimeSeconds.toInt() - bot.client.timeDifference.toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val members: ContactList<Member> = ContactList(members.mapNotNull {
|
override val members: ContactList<Member> = ContactList(members.mapNotNull {
|
||||||
if (it.uin == bot.uin) {
|
if (it.uin == bot.uin) {
|
||||||
botPermission = it.permission
|
botPermission = it.permission
|
||||||
|
if (it.permission == MemberPermission.OWNER) {
|
||||||
|
owner = botAsMember
|
||||||
|
}
|
||||||
null
|
null
|
||||||
} else Member(it).also { member ->
|
} else Member(it).also { member ->
|
||||||
if (member.permission == MemberPermission.OWNER) {
|
if (member.permission == MemberPermission.OWNER) {
|
||||||
@ -344,11 +430,11 @@ internal class GroupImpl(
|
|||||||
}.toLockFreeLinkedList())
|
}.toLockFreeLinkedList())
|
||||||
|
|
||||||
internal var _name: String = groupInfo.name
|
internal var _name: String = groupInfo.name
|
||||||
internal var _announcement: String = groupInfo.memo
|
private var _announcement: String = groupInfo.memo
|
||||||
internal var _allowMemberInvite: Boolean = groupInfo.allowMemberInvite
|
private var _allowMemberInvite: Boolean = groupInfo.allowMemberInvite
|
||||||
internal var _confessTalk: Boolean = groupInfo.confessTalk
|
internal var _confessTalk: Boolean = groupInfo.confessTalk
|
||||||
internal var _muteAll: Boolean = groupInfo.muteAll
|
internal var _muteAll: Boolean = groupInfo.muteAll
|
||||||
internal var _autoApprove: Boolean = groupInfo.autoApprove
|
private var _autoApprove: Boolean = groupInfo.autoApprove
|
||||||
internal var _anonymousChat: Boolean = groupInfo.allowAnonymousChat
|
internal var _anonymousChat: Boolean = groupInfo.allowAnonymousChat
|
||||||
|
|
||||||
override var name: String
|
override var name: String
|
||||||
@ -414,12 +500,14 @@ internal class GroupImpl(
|
|||||||
|
|
||||||
override var isAutoApproveEnabled: Boolean
|
override var isAutoApproveEnabled: Boolean
|
||||||
get() = _autoApprove
|
get() = _autoApprove
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
set(newValue) {
|
set(newValue) {
|
||||||
TODO()
|
TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
override var isAnonymousChatEnabled: Boolean
|
override var isAnonymousChatEnabled: Boolean
|
||||||
get() = _anonymousChat
|
get() = _anonymousChat
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
set(newValue) {
|
set(newValue) {
|
||||||
TODO()
|
TODO()
|
||||||
}
|
}
|
||||||
@ -465,15 +553,17 @@ internal class GroupImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MiraiExperimentalAPI
|
||||||
override suspend fun quit(): Boolean {
|
override suspend fun quit(): Boolean {
|
||||||
check(botPermission != MemberPermission.OWNER) { "An owner cannot quit from a owning group" }
|
check(botPermission != MemberPermission.OWNER) { "An owner cannot quit from a owning group" }
|
||||||
TODO("not implemented")
|
TODO("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseExperimental(MiraiExperimentalAPI::class)
|
@OptIn(MiraiExperimentalAPI::class)
|
||||||
override fun Member(memberInfo: MemberInfo): Member {
|
override fun Member(memberInfo: MemberInfo): Member {
|
||||||
return MemberImpl(
|
return MemberImpl(
|
||||||
bot.QQ(memberInfo) as QQImpl,
|
@OptIn(LowLevelAPI::class)
|
||||||
|
bot._lowLevelNewQQ(memberInfo) as QQImpl,
|
||||||
this,
|
this,
|
||||||
this.coroutineContext,
|
this.coroutineContext,
|
||||||
memberInfo
|
memberInfo
|
||||||
@ -482,7 +572,8 @@ internal class GroupImpl(
|
|||||||
|
|
||||||
|
|
||||||
override operator fun get(id: Long): Member {
|
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 {
|
override fun contains(id: Long): Boolean {
|
||||||
@ -493,25 +584,31 @@ internal class GroupImpl(
|
|||||||
return members.delegate.filteringGetOrNull { it.id == id }
|
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" }
|
check(!isBotMuted) { "bot is muted. Remaining seconds=$botMuteRemaining" }
|
||||||
val event = GroupMessageSendEvent(this, message).broadcast()
|
val event = GroupMessageSendEvent(this, message).broadcast()
|
||||||
if (event.isCancelled) {
|
if (event.isCancelled) {
|
||||||
throw EventCancelledException("cancelled by FriendMessageSendEvent")
|
throw EventCancelledException("cancelled by FriendMessageSendEvent")
|
||||||
}
|
}
|
||||||
|
lateinit var source: MessageSourceFromSendGroup
|
||||||
bot.network.run {
|
bot.network.run {
|
||||||
val response = MessageSvc.PbSendMsg.ToGroup(
|
val response: MessageSvc.PbSendMsg.Response = MessageSvc.PbSendMsg.ToGroup(
|
||||||
bot.client,
|
bot.client,
|
||||||
id,
|
id,
|
||||||
event.message
|
event.message
|
||||||
).sendAndExpect<MessageSvc.PbSendMsg.Response>()
|
) {
|
||||||
|
source = it
|
||||||
|
source.startWaitingSequenceId(this)
|
||||||
|
}.sendAndExpect()
|
||||||
check(
|
check(
|
||||||
response is MessageSvc.PbSendMsg.Response.SUCCESS
|
response is MessageSvc.PbSendMsg.Response.SUCCESS
|
||||||
) { "send message failed: $response" }
|
) { "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) {
|
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
|
||||||
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
|
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
|
||||||
}
|
}
|
||||||
@ -528,9 +625,11 @@ internal class GroupImpl(
|
|||||||
filename = image.filename
|
filename = image.filename
|
||||||
).sendAndExpect()
|
).sendAndExpect()
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST") // bug
|
||||||
when (response) {
|
when (response) {
|
||||||
is ImgStore.GroupPicUp.Response.Failed -> {
|
is ImgStore.GroupPicUp.Response.Failed -> {
|
||||||
ImageUploadEvent.Failed(this@GroupImpl, image, response.resultCode, response.message).broadcast()
|
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}")
|
error("upload group image failed with reason ${response.message}")
|
||||||
}
|
}
|
||||||
is ImgStore.GroupPicUp.Response.FileExists -> {
|
is ImgStore.GroupPicUp.Response.FileExists -> {
|
||||||
@ -546,22 +645,25 @@ internal class GroupImpl(
|
|||||||
// fileId = response.fileId.toInt()
|
// fileId = response.fileId.toInt()
|
||||||
// )
|
// )
|
||||||
// println("NMSL")
|
// println("NMSL")
|
||||||
return CustomFaceFromFile(
|
return OfflineGroupImage(
|
||||||
md5 = image.md5,
|
md5 = image.md5,
|
||||||
filepath = resourceId
|
filepath = resourceId
|
||||||
).also { ImageUploadEvent.Succeed(this@GroupImpl, image, it).broadcast() }
|
).also { ImageUploadEvent.Succeed(this@GroupImpl, image, it).broadcast() }
|
||||||
}
|
}
|
||||||
is ImgStore.GroupPicUp.Response.RequireUpload -> {
|
is ImgStore.GroupPicUp.Response.RequireUpload -> {
|
||||||
|
// 每 10KB 等 1 秒
|
||||||
|
withTimeoutOrNull(image.inputSize * 1000 / 1024 / 10) {
|
||||||
HighwayHelper.uploadImage(
|
HighwayHelper.uploadImage(
|
||||||
client = bot.client,
|
client = bot.client,
|
||||||
serverIp = response.uploadIpList.first().toIpV4AddressString(),
|
serverIp = response.uploadIpList.first().toIpV4AddressString(),
|
||||||
serverPort = response.uploadPortList.first(),
|
serverPort = response.uploadPortList.first(),
|
||||||
imageInput = image.input,
|
imageInput = image.input,
|
||||||
inputSize = image.inputSize.toInt(),
|
inputSize = image.inputSize.toInt(),
|
||||||
md5 = image.md5,
|
fileMd5 = image.md5,
|
||||||
uKey = response.uKey,
|
uKey = response.uKey,
|
||||||
commandId = 2
|
commandId = 2
|
||||||
)
|
)
|
||||||
|
} ?: error("timeout uploading image: ${image.filename}")
|
||||||
val resourceId = image.calculateImageResourceId()
|
val resourceId = image.calculateImageResourceId()
|
||||||
// return NotOnlineImageFromFile(
|
// return NotOnlineImageFromFile(
|
||||||
// resourceId = resourceId,
|
// resourceId = resourceId,
|
||||||
@ -573,7 +675,7 @@ internal class GroupImpl(
|
|||||||
// imageType = image.imageType,
|
// imageType = image.imageType,
|
||||||
// fileId = response.fileId.toInt()
|
// fileId = response.fileId.toInt()
|
||||||
// )
|
// )
|
||||||
return CustomFaceFromFile(
|
return OfflineGroupImage(
|
||||||
md5 = image.md5,
|
md5 = image.md5,
|
||||||
filepath = resourceId
|
filepath = resourceId
|
||||||
).also { ImageUploadEvent.Succeed(this@GroupImpl, image, it).broadcast() }
|
).also { ImageUploadEvent.Succeed(this@GroupImpl, image, it).broadcast() }
|
||||||
@ -597,13 +699,24 @@ internal class GroupImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} 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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
@Suppress("DuplicatedCode", "DuplicatedCode")
|
||||||
if (this === other) return true
|
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
|
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.BotAccount
|
||||||
import net.mamoe.mirai.BotImpl
|
import net.mamoe.mirai.BotImpl
|
||||||
import net.mamoe.mirai.contact.ContactList
|
import net.mamoe.mirai.LowLevelAPI
|
||||||
import net.mamoe.mirai.contact.Group
|
import net.mamoe.mirai.contact.*
|
||||||
import net.mamoe.mirai.contact.QQ
|
|
||||||
import net.mamoe.mirai.contact.filteringGetOrNull
|
|
||||||
import net.mamoe.mirai.data.AddFriendResult
|
import net.mamoe.mirai.data.AddFriendResult
|
||||||
import net.mamoe.mirai.data.FriendInfo
|
import net.mamoe.mirai.data.FriendInfo
|
||||||
import net.mamoe.mirai.data.GroupInfo
|
import net.mamoe.mirai.data.GroupInfo
|
||||||
import net.mamoe.mirai.data.MemberInfo
|
import net.mamoe.mirai.data.MemberInfo
|
||||||
import net.mamoe.mirai.event.events.BotEvent
|
import net.mamoe.mirai.event.broadcast
|
||||||
import net.mamoe.mirai.message.data.Image
|
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.QQAndroidBotNetworkHandler
|
||||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
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.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.chat.TroopManagement
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
import kotlin.collections.asSequence
|
import kotlin.collections.asSequence
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
@UseExperimental(MiraiInternalAPI::class)
|
@OptIn(MiraiInternalAPI::class)
|
||||||
internal expect class QQAndroidBot constructor(
|
internal expect class QQAndroidBot constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
account: BotAccount,
|
account: BotAccount,
|
||||||
configuration: BotConfiguration
|
configuration: BotConfiguration
|
||||||
) : QQAndroidBotBase
|
) : QQAndroidBotBase
|
||||||
|
|
||||||
@UseExperimental(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
|
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
|
||||||
internal abstract class QQAndroidBotBase constructor(
|
internal abstract class QQAndroidBotBase constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
account: BotAccount,
|
account: BotAccount,
|
||||||
configuration: BotConfiguration
|
configuration: BotConfiguration
|
||||||
) : BotImpl<QQAndroidBotNetworkHandler>(account, configuration) {
|
) : BotImpl<QQAndroidBotNetworkHandler>(context, account, configuration) {
|
||||||
val client: QQAndroidClient =
|
val client: QQAndroidClient =
|
||||||
QQAndroidClient(
|
QQAndroidClient(
|
||||||
context,
|
context,
|
||||||
@ -53,17 +58,32 @@ internal abstract class QQAndroidBotBase constructor(
|
|||||||
)
|
)
|
||||||
internal var firstLoginSucceed: Boolean = false
|
internal var firstLoginSucceed: Boolean = false
|
||||||
override val uin: Long get() = client.uin
|
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 {
|
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 uin: Long get() = this@QQAndroidBotBase.uin
|
||||||
override val nick: String get() = this@QQAndroidBotBase.nick
|
override val nick: String get() = this@QQAndroidBotBase.nick
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun QQ(friendInfo: FriendInfo): QQ {
|
@LowLevelAPI
|
||||||
return QQImpl(this as QQAndroidBot, coroutineContext, friendInfo.uin, friendInfo)
|
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 {
|
override fun createNetworkHandler(coroutineContext: CoroutineContext): QQAndroidBotNetworkHandler {
|
||||||
@ -74,28 +94,36 @@ internal abstract class QQAndroidBotBase constructor(
|
|||||||
|
|
||||||
// internally visible only
|
// internally visible only
|
||||||
fun getGroupByUin(uin: Long): Group {
|
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? {
|
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 {
|
return network.run {
|
||||||
FriendList.GetTroopListSimplify(bot.client)
|
FriendList.GetTroopListSimplify(bot.client)
|
||||||
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
|
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
|
||||||
}.groups.asSequence().map { it.groupUin.shl(32) and it.groupCode }
|
}.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(
|
TroopManagement.GetGroupInfo(
|
||||||
client = bot.client,
|
client = bot.client,
|
||||||
groupCode = id
|
groupCode = groupCode
|
||||||
).sendAndExpect<GroupInfoImpl>()
|
).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 nextUin = 0L
|
||||||
var sequence = sequenceOf<MemberInfoImpl>()
|
var sequence = sequenceOf<MemberInfoImpl>()
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -116,24 +144,88 @@ internal abstract class QQAndroidBotBase constructor(
|
|||||||
return sequence
|
return sequence
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEvent(event: BotEvent): Boolean {
|
|
||||||
return firstLoginSucceed
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult {
|
override suspend fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult {
|
||||||
TODO("not implemented")
|
TODO("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun Image.download(): ByteReadPacket {
|
override suspend fun recall(source: MessageSource) {
|
||||||
TODO("not implemented")
|
if (source.senderId != uin && source.groupId != 0L) {
|
||||||
|
getGroup(source.groupId).checkBotPermissionOperator()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("OverridingDeprecatedMember")
|
// println(source._miraiContentToString())
|
||||||
override suspend fun Image.downloadAsByteArray(): ByteArray {
|
source.ensureSequenceIdAvailable()
|
||||||
TODO("not implemented")
|
|
||||||
|
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?) {
|
check(response is PbMessageSvc.PbMsgWithDraw.Response.Success) { "Failed to recall message #${source.id}: $response" }
|
||||||
TODO("not implemented")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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.charsets.Charset
|
||||||
import kotlinx.io.core.*
|
import kotlinx.io.core.*
|
||||||
import kotlinx.serialization.*
|
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.internal.*
|
||||||
import kotlinx.serialization.modules.EmptyModule
|
import kotlinx.serialization.modules.EmptyModule
|
||||||
import kotlinx.serialization.modules.SerialModule
|
import kotlinx.serialization.modules.SerialModule
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
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.readString
|
||||||
import net.mamoe.mirai.utils.io.toReadPacket
|
import net.mamoe.mirai.utils.io.toReadPacket
|
||||||
|
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal val CharsetGBK = Charset.forName("GBK")
|
internal val CharsetGBK = Charset.forName("GBK")
|
||||||
|
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal val CharsetUTF8 = Charset.forName("UTF8")
|
internal val CharsetUTF8 = Charset.forName("UTF8")
|
||||||
|
|
||||||
@ -30,12 +53,15 @@ enum class JceCharset(val kotlinCharset: Charset) {
|
|||||||
UTF8(Charset.forName("UTF8"))
|
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]
|
* 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 inner class ListWriter(
|
||||||
private val count: Int,
|
private val count: Int,
|
||||||
@ -46,7 +72,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun endEncode(desc: SerialDescriptor) {
|
override fun endEncode(descriptor: SerialDescriptor) {
|
||||||
parentEncoder.writeHead(LIST, this.tag)
|
parentEncoder.writeHead(LIST, this.tag)
|
||||||
parentEncoder.encodeTaggedInt(0, count)
|
parentEncoder.encodeTaggedInt(0, count)
|
||||||
parentEncoder.output.writePacket(this.output.build())
|
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())
|
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
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeEncoder {
|
override fun beginStructure(
|
||||||
|
descriptor: SerialDescriptor,
|
||||||
|
vararg typeSerializers: KSerializer<*>
|
||||||
|
): CompositeEncoder {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,11 +114,11 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
* From: com.qq.taf.jce.JceOutputStream
|
* From: com.qq.taf.jce.JceOutputStream
|
||||||
*/
|
*/
|
||||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||||
@UseExperimental(ExperimentalIoApi::class)
|
@OptIn(ExperimentalIoApi::class)
|
||||||
private open inner class JceEncoder(
|
private open inner class JceEncoder(
|
||||||
internal val output: BytePacketBuilder
|
internal val output: BytePacketBuilder
|
||||||
) : TaggedEncoder<Int>() {
|
) : TaggedEncoder<Int>() {
|
||||||
override val context get() = this@Jce.context
|
override val context get() = this@JceOld.context
|
||||||
|
|
||||||
override fun SerialDescriptor.getTag(index: Int): Int {
|
override fun SerialDescriptor.getTag(index: Int): Int {
|
||||||
return getSerialId(this, index) ?: error("cannot find @SerialId")
|
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.LIST -> this
|
||||||
StructureKind.MAP -> this
|
StructureKind.MAP -> this
|
||||||
StructureKind.CLASS, UnionKind.OBJECT -> this
|
StructureKind.CLASS, StructureKind.OBJECT -> this
|
||||||
is PolymorphicKind -> this
|
is PolymorphicKind -> this
|
||||||
else -> throw SerializationException("Primitives are not supported at top-level")
|
else -> throw SerializationException("Primitives are not supported at top-level")
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseExperimental(ImplicitReflectionSerializer::class)
|
@OptIn(ImplicitReflectionSerializer::class)
|
||||||
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
|
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
|
||||||
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when (serializer.descriptor) {
|
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when {
|
||||||
is MapLikeDescriptor -> {
|
serializer.descriptor.kind == StructureKind.MAP -> {
|
||||||
|
try {
|
||||||
val entries = (value as Map<*, *>).entries
|
val entries = (value as Map<*, *>).entries
|
||||||
val serializer = (serializer as MapLikeSerializer<Any?, Any?, T, *>)
|
val serializer = (serializer as MapLikeSerializer<Any?, Any?, T, *>)
|
||||||
val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
|
val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
|
||||||
|
|
||||||
this.writeHead(MAP, currentTag)
|
this.writeHead(MAP, currentTag)
|
||||||
this.encodeTaggedInt(0, entries.count())
|
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(
|
serializer.serialize(
|
||||||
ListWriter(
|
ListWriter(
|
||||||
when (value) {
|
when (value) {
|
||||||
@ -133,9 +176,8 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
value
|
value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is ArrayClassDesc -> {
|
serializer.descriptor.kind == StructureKind.LIST && value is Array<*> -> {
|
||||||
val descriptor = serializer.descriptor as ReferenceArraySerializer<Any, Any?>
|
if (serializer.descriptor.getElementDescriptor(0).kind is PrimitiveKind.BYTE) {
|
||||||
if (descriptor.typeParams.isNotEmpty() && descriptor.typeParams[0] is ByteSerializer) {
|
|
||||||
encodeTaggedByteArray(popTag(), (value as Array<Byte>).toByteArray())
|
encodeTaggedByteArray(popTag(), (value as Array<Byte>).toByteArray())
|
||||||
} else
|
} else
|
||||||
serializer.serialize(
|
serializer.serialize(
|
||||||
@ -143,7 +185,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
value
|
value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is ListLikeDescriptor -> {
|
serializer.descriptor.kind == StructureKind.LIST -> {
|
||||||
serializer.serialize(
|
serializer.serialize(
|
||||||
ListWriter((value as Collection<*>).size, popTag(), this),
|
ListWriter((value as Collection<*>).size, popTag(), this),
|
||||||
value
|
value
|
||||||
@ -262,7 +304,8 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
is Double -> encodeTaggedDouble(tag, value)
|
is Double -> encodeTaggedDouble(tag, value)
|
||||||
is Boolean -> encodeTaggedBoolean(tag, value)
|
is Boolean -> encodeTaggedBoolean(tag, value)
|
||||||
is String -> encodeTaggedString(tag, value)
|
is String -> encodeTaggedString(tag, value)
|
||||||
is Unit -> encodeTaggedUnit(tag)
|
is Unit -> {
|
||||||
|
}
|
||||||
else -> error("unsupported type: ${value.getClassName()}")
|
else -> error("unsupported type: ${value.getClassName()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,7 +329,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
val size: Int,
|
val size: Int,
|
||||||
input: JceInput
|
input: JceInput
|
||||||
) : JceDecoder(input) {
|
) : JceDecoder(input) {
|
||||||
override fun decodeCollectionSize(desc: SerialDescriptor): Int {
|
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +343,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
val size: Int,
|
val size: Int,
|
||||||
input: JceInput
|
input: JceInput
|
||||||
) : JceDecoder(input) {
|
) : JceDecoder(input) {
|
||||||
override fun decodeCollectionSize(desc: SerialDescriptor): Int {
|
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,7 +355,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
private open inner class JceStructReader(
|
private open inner class JceStructReader(
|
||||||
input: JceInput
|
input: JceInput
|
||||||
) : JceDecoder(input) {
|
) : 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)
|
return input.readInt(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在 [KSerializer.serialize] 前
|
* 在 [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()}")
|
//// println("beginStructure: desc=${desc.getClassName()}, typeParams: ${typeParams.contentToString()}")
|
||||||
when (desc) {
|
when {
|
||||||
// 由于 Byte 的数组有两种方式写入, 需特定读取器
|
// 由于 Byte 的数组有两种方式写入, 需特定读取器
|
||||||
ByteArraySerializer.descriptor -> {
|
descriptor.kind == StructureKind.LIST
|
||||||
|
&& descriptor.getElementDescriptor(0).kind == PrimitiveKind.BYTE -> {
|
||||||
// ByteArray, 交给 decodeSerializableValue 进行处理
|
// ByteArray, 交给 decodeSerializableValue 进行处理
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
is ListLikeDescriptor -> {
|
descriptor.kind == StructureKind.LIST -> {
|
||||||
if (typeParams.isNotEmpty() && typeParams[0] is ByteSerializer) {
|
// if (typeParams.isNotEmpty() && typeParams[0] is ByteSerializer) {
|
||||||
// Array<Byte>
|
// // Array<Byte>
|
||||||
return this // 交给 decodeSerializableValue
|
// return this // 交给 decodeSerializableValue
|
||||||
}
|
// }
|
||||||
|
|
||||||
val tag = currentTagOrNull
|
val tag = currentTagOrNull
|
||||||
@Suppress("SENSELESS_COMPARISON") // 推断 bug
|
@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)
|
MAP -> JceMapReader(input.readInt(0), this.input)
|
||||||
else -> error("type mismatch")
|
else -> error("type mismatch")
|
||||||
}
|
}
|
||||||
} == null && desc.isNullable) {
|
} == null && descriptor.isNullable) {
|
||||||
return NullReader(this.input)
|
return NullReader(this.input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is MapLikeDescriptor -> {
|
descriptor.kind == StructureKind.MAP -> {
|
||||||
val tag = currentTagOrNull
|
val tag = currentTagOrNull
|
||||||
if (tag != null) {
|
if (tag != null) {
|
||||||
popTag()
|
popTag()
|
||||||
@ -391,7 +439,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
return NullReader(this.input)
|
return NullReader(this.input)
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.beginStructure(desc, *typeParams)
|
return super.beginStructure(descriptor, *typeParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun decodeTaggedNull(tag: Int): Nothing? {
|
override fun decodeTaggedNull(tag: Int): Nothing? {
|
||||||
@ -410,7 +458,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T?>): T? {
|
override fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T?>): T? {
|
||||||
//
|
//
|
||||||
//println("decodeNullableSerializableValue: ${deserializer::class.qualifiedName}")
|
println("decodeNullableSerializableValue: ${deserializer::class.qualifiedName}")
|
||||||
if (deserializer is NullReader) {
|
if (deserializer is NullReader) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -419,13 +467,13 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when (deserializer.descriptor) {
|
when {
|
||||||
ByteArraySerializer.descriptor -> {
|
deserializer.descriptor == ByteArraySerializer().descriptor -> {
|
||||||
val tag = popTag()
|
val tag = popTag()
|
||||||
return if (isTagMissing(tag)) input.readByteArrayOrNull(tag) as? T
|
return if (isTagMissing(tag)) input.readByteArrayOrNull(tag) as? T
|
||||||
else input.readByteArray(tag) as T
|
else input.readByteArray(tag) as T
|
||||||
}
|
}
|
||||||
is ListLikeDescriptor -> {
|
deserializer.descriptor.kind == StructureKind.LIST -> {
|
||||||
if (deserializer is ReferenceArraySerializer<*, *>
|
if (deserializer is ReferenceArraySerializer<*, *>
|
||||||
&& (deserializer as ListLikeSerializer<Any?, T, Any?>).typeParams.isNotEmpty()
|
&& (deserializer as ListLikeSerializer<Any?, T, Any?>).typeParams.isNotEmpty()
|
||||||
&& (deserializer as ListLikeSerializer<Any?, T, Any?>).typeParams[0] is ByteSerializer
|
&& (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")
|
error("UNREACHABLE CODE")
|
||||||
}
|
}
|
||||||
is MapLikeDescriptor -> {
|
deserializer.descriptor.kind == StructureKind.MAP -> {
|
||||||
val tag = popTag()
|
val tag = popTag()
|
||||||
@Suppress("SENSELESS_COMPARISON")
|
@Suppress("SENSELESS_COMPARISON")
|
||||||
if (input.skipToTagOrNull(tag) { head ->
|
if (input.skipToTagOrNull(tag) { head ->
|
||||||
check(head.type == MAP) { "type mismatch: ${head.type}" }
|
check(head.type == MAP) { "type mismatch: ${head.type}" }
|
||||||
// 将 mapOf(k1 to v1, k2 to v2, ...) 转换为 listOf(k1, v1, k2, v2, ...) 以便于写入.
|
// 将 mapOf(k1 to v1, k2 to v2, ...) 转换为 listOf(k1, v1, k2, v2, ...) 以便于写入.
|
||||||
val serializer = (deserializer as MapLikeSerializer<Any?, Any?, T, *>)
|
val serializer = (deserializer as MapLikeSerializer<Any?, Any?, T, *>)
|
||||||
val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
|
val mapEntrySerial =
|
||||||
val setOfEntries = HashSetSerializer(mapEntrySerial).deserialize(JceMapReader(input.readInt(0), input))
|
MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
|
||||||
|
val setOfEntries =
|
||||||
|
SetSerializer(mapEntrySerial).deserialize(JceMapReader(input.readInt(0), input))
|
||||||
return setOfEntries.associateBy({ it.key }, { it.value }) as T
|
return setOfEntries.associateBy({ it.key }, { it.value }) as T
|
||||||
} == null) {
|
} == null) {
|
||||||
if (isTagMissing(tag)) {
|
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
|
val tag = currentTagOrNull
|
||||||
if (tag != null) {
|
if (tag != null) {
|
||||||
@Suppress("SENSELESS_COMPARISON") // 推断 bug
|
@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(
|
internal inner class JceInput(
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal val input: ByteReadPacket,
|
internal val input: ByteReadPacket,
|
||||||
@ -556,22 +606,38 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
}
|
}
|
||||||
tag = readUByte().toUInt()
|
tag = readUByte().toUInt()
|
||||||
}
|
}
|
||||||
currentJceHead = JceHead(tag = tag.toInt(), type = type.toByte())
|
currentJceHead = JceHead(
|
||||||
|
tag = tag.toInt(),
|
||||||
|
type = type.toByte()
|
||||||
|
)
|
||||||
// println("doReadHead: $currentJceHead")
|
// println("doReadHead: $currentJceHead")
|
||||||
return currentJceHead
|
return currentJceHead
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readBoolean(tag: Int): Boolean = readBooleanOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
fun readBoolean(tag: Int): Boolean =
|
||||||
fun readByte(tag: Int): Byte = readByteOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
readBooleanOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
||||||
fun readShort(tag: Int): Short = readShortOrNull(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 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 readLong(tag: Int): Long =
|
||||||
fun readFloat(tag: Int): Float = readFloatOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
|
readLongOrNull(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 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) {
|
fun readByteArrayOrNull(tag: Int): ByteArray? = skipToTagOrNull(tag) {
|
||||||
when (it.type) {
|
when (it.type) {
|
||||||
@ -667,7 +733,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
} while (head.type.toInt() != 11)
|
} while (head.type.toInt() != 11)
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal fun skipField(type: Byte) = when (type.toInt()) {
|
internal fun skipField(type: Byte) = when (type.toInt()) {
|
||||||
0 -> this.input.discardExact(1)
|
0 -> this.input.discardExact(1)
|
||||||
@ -704,10 +770,10 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
|
|||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
companion object {
|
companion object {
|
||||||
val UTF8 = Jce(JceCharset.UTF8)
|
val UTF8 = JceOld(JceCharset.UTF8)
|
||||||
val GBK = Jce(JceCharset.GBK)
|
val GBK = JceOld(JceCharset.GBK)
|
||||||
|
|
||||||
fun byCharSet(c: JceCharset): Jce {
|
fun byCharSet(c: JceCharset): JceOld {
|
||||||
return if (c == JceCharset.UTF8) {
|
return if (c == JceCharset.UTF8) {
|
||||||
UTF8
|
UTF8
|
||||||
} else {
|
} 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 =
|
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 {
|
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()
|
return encoder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T> dump(serializer: SerializationStrategy<T>, obj: T): ByteArray {
|
override fun <T> dump(serializer: SerializationStrategy<T>, value: T): ByteArray {
|
||||||
return dumpAsPacket(serializer, obj).readBytes()
|
return dumpAsPacket(serializer, value).readBytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注意 close [packet]!!
|
* 注意 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)
|
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")
|
// println("skipping to $tag start")
|
||||||
while (true) {
|
while (true) {
|
||||||
if (isEndOfInput) { // 读不了了
|
if (isEndOfInput) { // 读不了了
|
||||||
@ -793,32 +848,3 @@ internal inline fun <R> Jce.JceInput.skipToTagOrNull(tag: Int, block: (JceHead)
|
|||||||
currentJceHead = readHeadOrNull()
|
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"
|
* 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
|
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.*
|
||||||
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.EmptyModule
|
||||||
import kotlinx.serialization.modules.SerialModule
|
import kotlinx.serialization.modules.SerialModule
|
||||||
import kotlinx.serialization.protobuf.ProtoBuf
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
@ -33,15 +41,19 @@ internal fun extractParameters(desc: SerialDescriptor, index: Int, zeroBasedDefa
|
|||||||
*
|
*
|
||||||
* 代码复制自 kotlinx.serialization. 修改部分已进行标注 (详见 "MIRAI MODIFY START")
|
* 代码复制自 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>() {
|
internal open inner class ProtobufWriter(private val encoder: ProtobufEncoder) : TaggedEncoder<ProtoDesc>() {
|
||||||
public override val context
|
override val context
|
||||||
get() = this@ProtoBufWithNullableSupport.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.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)
|
StructureKind.MAP -> MapRepeatedWriter(currentTagOrNull, encoder)
|
||||||
else -> throw SerializationException("Primitives are not supported at top-level")
|
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")
|
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
|
||||||
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when {
|
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when {
|
||||||
// encode maps as collection of map entries, not merged collection of key-values
|
// 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 serializer = (serializer as MapLikeSerializer<Any?, Any?, T, *>)
|
||||||
val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
|
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)
|
else -> serializer.serialize(this, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,7 +111,7 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
|
|||||||
val parentTag: ProtoDesc?, private val parentEncoder: ProtobufEncoder,
|
val parentTag: ProtoDesc?, private val parentEncoder: ProtobufEncoder,
|
||||||
private val stream: ByteArrayOutputStream = ByteArrayOutputStream()
|
private val stream: ByteArrayOutputStream = ByteArrayOutputStream()
|
||||||
) : ProtobufWriter(ProtobufEncoder(stream)) {
|
) : ProtobufWriter(ProtobufEncoder(stream)) {
|
||||||
override fun endEncode(desc: SerialDescriptor) {
|
override fun endEncode(descriptor: SerialDescriptor) {
|
||||||
if (parentTag != null) {
|
if (parentTag != null) {
|
||||||
parentEncoder.writeBytes(stream.toByteArray(), parentTag.first)
|
parentEncoder.writeBytes(stream.toByteArray(), parentTag.first)
|
||||||
} else {
|
} else {
|
||||||
@ -111,7 +126,8 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
|
|||||||
else 2 to (parentTag?.second ?: ProtoNumberType.DEFAULT)
|
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
|
override fun SerialDescriptor.getTag(index: Int) = curTag
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,8 +157,9 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
|
|||||||
out.write(content)
|
out.write(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
fun writeString(value: String, tag: Int) {
|
fun writeString(value: String, tag: Int) {
|
||||||
val bytes = value.toUtf8Bytes()
|
val bytes = value.encodeToByteArray()
|
||||||
writeBytes(bytes, tag)
|
writeBytes(bytes, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,17 +245,17 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
|
|||||||
internal const val SIZE_DELIMITED = 2
|
internal const val SIZE_DELIMITED = 2
|
||||||
internal const val i32 = 5
|
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> 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> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T =
|
||||||
override fun install(module: SerialModule) = throw IllegalStateException("You should not install anything to global instance")
|
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 encoder = ByteArrayOutputStream()
|
||||||
val dumper = ProtobufWriter(ProtobufEncoder(encoder))
|
val dumper = ProtobufWriter(ProtobufEncoder(encoder))
|
||||||
dumper.encode(serializer, obj)
|
dumper.encode(serializer, value)
|
||||||
return encoder.toByteArray()
|
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
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:JvmName("SerializationUtils")
|
||||||
|
@file:JvmMultifileClass
|
||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.io.serialization
|
package net.mamoe.mirai.qqandroid.io.serialization
|
||||||
|
|
||||||
import kotlinx.io.core.*
|
import kotlinx.io.core.*
|
||||||
@ -15,20 +18,29 @@ import kotlinx.serialization.SerialDescriptor
|
|||||||
import kotlinx.serialization.SerializationStrategy
|
import kotlinx.serialization.SerializationStrategy
|
||||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
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.RequestDataVersion2
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion3
|
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion3
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
|
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.firstValue
|
||||||
import net.mamoe.mirai.utils.io.read
|
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 {
|
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) {
|
fun <T : JceStruct> BytePacketBuilder.writeJceStruct(
|
||||||
this.writePacket(Jce.byCharSet(charset).dumpAsPacket(serializer, struct))
|
serializer: SerializationStrategy<T>,
|
||||||
|
struct: T,
|
||||||
|
charset: JceCharset = JceCharset.GBK
|
||||||
|
) {
|
||||||
|
Jce.byCharSet(charset).dumpTo(serializer, struct, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : JceStruct> ByteReadPacket.readJceStruct(
|
fun <T : JceStruct> ByteReadPacket.readJceStruct(
|
||||||
@ -36,7 +48,8 @@ fun <T : JceStruct> ByteReadPacket.readJceStruct(
|
|||||||
charset: JceCharset = JceCharset.UTF8,
|
charset: JceCharset = JceCharset.UTF8,
|
||||||
length: Int = this.remaining.toInt()
|
length: Int = this.remaining.toInt()
|
||||||
): T {
|
): 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 {
|
fun <R> ByteReadPacket.decodeUniRequestPacketAndDeserialize(name: String? = null, block: (ByteArray) -> R): R {
|
||||||
val request = this.readJceStruct(RequestPacket.serializer())
|
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()
|
2 -> request.sBuffer.loadAs(RequestDataVersion2.serializer()).map.firstValue().firstValue()
|
||||||
3 -> request.sBuffer.loadAs(RequestDataVersion3.serializer()).map.firstValue()
|
3 -> request.sBuffer.loadAs(RequestDataVersion3.serializer()).map.firstValue()
|
||||||
else -> error("unsupported version ${request.iVersion}")
|
else -> error("unsupported version ${request.iVersion}")
|
||||||
} else when (request.iVersion.toInt()) {
|
} else when (request.iVersion?.toInt() ?: 3) {
|
||||||
2 -> request.sBuffer.loadAs(RequestDataVersion2.serializer()).map.getOrElse(name) { error("cannot find $name") }.firstValue()
|
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") }
|
3 -> request.sBuffer.loadAs(RequestDataVersion3.serializer()).map.getOrElse(name) { error("cannot find $name") }
|
||||||
else -> error("unsupported version ${request.iVersion}")
|
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) {
|
fun <T : ProtoBuf> BytePacketBuilder.writeProtoBuf(serializer: SerializationStrategy<T>, v: T) {
|
||||||
this.writeFully(v.toByteArray(serializer))
|
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
|
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.message.data.*
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
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.MsgComm
|
||||||
import net.mamoe.mirai.utils.MiraiDebugAPI
|
import net.mamoe.mirai.utils.*
|
||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
import net.mamoe.mirai.utils.io.encodeToString
|
||||||
import net.mamoe.mirai.utils.io.discardExact
|
|
||||||
import net.mamoe.mirai.utils.io.hexToBytes
|
import net.mamoe.mirai.utils.io.hexToBytes
|
||||||
import net.mamoe.mirai.utils.io.read
|
import net.mamoe.mirai.utils.io.read
|
||||||
import net.mamoe.mirai.utils.io.toByteArray
|
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 {
|
internal fun At.toJceData(): ImMsgBody.Text {
|
||||||
|
val text = this.toString()
|
||||||
return ImMsgBody.Text(
|
return ImMsgBody.Text(
|
||||||
str = this.toString(),
|
str = text,
|
||||||
attr6Buf = AT_BUF_1 + this.target.toInt().toByteArray() + AT_BUF_2
|
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(
|
return ImMsgBody.NotOnlineImage(
|
||||||
filePath = this.filepath,
|
filePath = this.filepath,
|
||||||
resId = this.resourceId,
|
resId = this.resourceId,
|
||||||
@ -86,7 +93,17 @@ _400Height=0x000000EB(235)
|
|||||||
pbReserve=<Empty ByteArray>
|
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(
|
return ImMsgBody.CustomFace(
|
||||||
filePath = this.filepath,
|
filePath = this.filepath,
|
||||||
fileId = this.fileId,
|
fileId = this.fileId,
|
||||||
@ -189,30 +206,75 @@ notOnlineImage=NotOnlineImage#2050019814 {
|
|||||||
private val atAllData = ImMsgBody.Elem(
|
private val atAllData = ImMsgBody.Elem(
|
||||||
text = ImMsgBody.Text(
|
text = ImMsgBody.Text(
|
||||||
str = "@全体成员",
|
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>()
|
val elements = mutableListOf<ImMsgBody.Elem>()
|
||||||
|
|
||||||
if (this.any<QuoteReply>()) {
|
if (this.any<QuoteReply>()) {
|
||||||
when (val source = this[QuoteReply].source) {
|
when (val source = this[QuoteReply].source) {
|
||||||
is MessageSourceFromServer -> elements.add(ImMsgBody.Elem(srcMsg = source.delegate))
|
is MessageSourceFromServer -> elements.add(ImMsgBody.Elem(srcMsg = source.delegate))
|
||||||
is MessageSourceFromMsg -> elements.add(ImMsgBody.Elem(srcMsg = source.toJceData()))
|
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}")
|
else -> error("unsupported MessageSource implementation: ${source::class.simpleName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.forEach {
|
|
||||||
|
fun transformOneMessage(it: Message) {
|
||||||
when (it) {
|
when (it) {
|
||||||
is PlainText -> elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue)))
|
is PlainText -> elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue)))
|
||||||
is At -> elements.add(ImMsgBody.Elem(text = it.toJceData()))
|
is At -> {
|
||||||
is CustomFaceFromFile -> elements.add(ImMsgBody.Elem(customFace = it.toJceData()))
|
elements.add(ImMsgBody.Elem(text = it.toJceData()))
|
||||||
is CustomFaceFromServer -> elements.add(ImMsgBody.Elem(customFace = it.delegate))
|
elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = " ")))
|
||||||
is NotOnlineImageFromServer -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
|
}
|
||||||
is NotOnlineImageFromFile -> elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData()))
|
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 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 QuoteReply,
|
||||||
is MessageSource -> {
|
is MessageSource -> {
|
||||||
|
|
||||||
@ -220,18 +282,20 @@ internal fun MessageChain.toRichTextElems(): MutableList<ImMsgBody.Elem> {
|
|||||||
else -> error("unsupported message type: ${it::class.simpleName}")
|
else -> error("unsupported message type: ${it::class.simpleName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.forEach(::transformOneMessage)
|
||||||
|
|
||||||
// if(this.any<QuoteReply>()){
|
if (this.any<RichMessage>()) {
|
||||||
elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 F8 01 00 C8 02 00".hexToBytes())))
|
// 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
|
return elements
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class CustomFaceFromServer(
|
internal class OnlineGroupImageImpl(
|
||||||
internal val delegate: ImMsgBody.CustomFace
|
internal val delegate: ImMsgBody.CustomFace
|
||||||
) : CustomFace() {
|
) : OnlineGroupImage() {
|
||||||
override val filepath: String get() = delegate.filePath
|
override val filepath: String = delegate.filePath
|
||||||
override val fileId: Int get() = delegate.fileId
|
override val fileId: Int get() = delegate.fileId
|
||||||
override val serverIp: Int get() = delegate.serverIp
|
override val serverIp: Int get() = delegate.serverIp
|
||||||
override val serverPort: Int get() = delegate.serverPort
|
override val serverPort: Int get() = delegate.serverPort
|
||||||
@ -247,20 +311,22 @@ internal class CustomFaceFromServer(
|
|||||||
override val size: Int get() = delegate.size
|
override val size: Int get() = delegate.size
|
||||||
override val original: Int get() = delegate.origin
|
override val original: Int get() = delegate.origin
|
||||||
override val pbReserve: ByteArray get() = delegate.pbReserve
|
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 {
|
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 {
|
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
|
internal val delegate: ImMsgBody.NotOnlineImage
|
||||||
) : NotOnlineImage() {
|
) : OnlineFriendImage() {
|
||||||
override val resourceId: String get() = delegate.resId
|
override val resourceId: String get() = delegate.resId
|
||||||
override val md5: ByteArray get() = delegate.picMd5
|
override val md5: ByteArray get() = delegate.picMd5
|
||||||
override val filepath: String get() = delegate.filePath
|
override val filepath: String get() = delegate.filePath
|
||||||
@ -272,52 +338,72 @@ internal class NotOnlineImageFromServer(
|
|||||||
override val downloadPath: String get() = delegate.downloadPath
|
override val downloadPath: String get() = delegate.downloadPath
|
||||||
override val fileId: Int get() = delegate.fileId
|
override val fileId: Int get() = delegate.fileId
|
||||||
override val original: Int get() = delegate.original
|
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 {
|
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 {
|
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 {
|
internal fun MsgComm.Msg.toMessageChain(): MessageChain {
|
||||||
val elements = this.msgBody.richText.elems
|
val elements = this.msgBody.richText.elems
|
||||||
|
|
||||||
val message = MessageChain(initialCapacity = elements.size + 1)
|
return buildMessageChain(elements.size + 1) {
|
||||||
message.add(MessageSourceFromMsg(delegate = this))
|
+MessageSourceFromMsg(delegate = this@toMessageChain)
|
||||||
elements.joinToMessageChain(message)
|
elements.joinToMessageChain(this)
|
||||||
return message
|
}.removeAtIfHasQuoteReply()
|
||||||
}
|
}
|
||||||
|
|
||||||
// These two functions are not the same.
|
// These two functions are not identical, dont combine.
|
||||||
|
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||||
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
|
||||||
internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain {
|
internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain {
|
||||||
val elements = this.elems!!
|
val elements = this.elems!!
|
||||||
|
|
||||||
val message = MessageChain(initialCapacity = elements.size + 1)
|
return buildMessageChain(elements.size + 1) {
|
||||||
message.add(MessageSourceFromServer(delegate = this))
|
+MessageSourceFromServer(delegate = this@toMessageChain)
|
||||||
elements.joinToMessageChain(message)
|
elements.joinToMessageChain(this)
|
||||||
return message
|
}.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)
|
@OptIn(
|
||||||
internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChain) {
|
MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class, LowLevelAPI::class
|
||||||
|
)
|
||||||
|
internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilder) {
|
||||||
this.forEach {
|
this.forEach {
|
||||||
when {
|
when {
|
||||||
it.srcMsg != null -> message.add(QuoteReply(MessageSourceFromServer(it.srcMsg)))
|
it.srcMsg != null -> message.add(QuoteReply(MessageSourceFromServer(it.srcMsg)))
|
||||||
it.notOnlineImage != null -> message.add(NotOnlineImageFromServer(it.notOnlineImage))
|
it.notOnlineImage != null -> message.add(OnlineFriendImageImpl(it.notOnlineImage))
|
||||||
it.customFace != null -> message.add(CustomFaceFromServer(it.customFace))
|
it.customFace != null -> message.add(OnlineGroupImageImpl(it.customFace))
|
||||||
|
it.face != null -> message.add(Face(it.face.index))
|
||||||
it.text != null -> {
|
it.text != null -> {
|
||||||
if (it.text.attr6Buf.isEmpty()) {
|
if (it.text.attr6Buf.isEmpty()) {
|
||||||
message.add(it.text.str.toMessage())
|
message.add(it.text.str.toMessage())
|
||||||
} else {
|
} else {
|
||||||
// 00 01 00 00 00 05 01 00 00 00 00 00 00 all
|
// 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
|
val id: Long
|
||||||
it.text.attr6Buf.read {
|
it.text.attr6Buf.read {
|
||||||
discardExact(7)
|
discardExact(7)
|
||||||
@ -326,7 +412,24 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChain) {
|
|||||||
if (id == 0L) {
|
if (id == 0L) {
|
||||||
message.add(AtAll)
|
message.add(AtAll)
|
||||||
} else {
|
} 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 kotlinx.io.core.use
|
||||||
import net.mamoe.mirai.data.MultiPacket
|
import net.mamoe.mirai.data.MultiPacket
|
||||||
import net.mamoe.mirai.data.Packet
|
import net.mamoe.mirai.data.Packet
|
||||||
import net.mamoe.mirai.event.BroadcastControllable
|
import net.mamoe.mirai.event.*
|
||||||
import net.mamoe.mirai.event.CancellableEvent
|
|
||||||
import net.mamoe.mirai.event.Event
|
|
||||||
import net.mamoe.mirai.event.broadcast
|
|
||||||
import net.mamoe.mirai.event.events.BotOfflineEvent
|
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.BotNetworkHandler
|
||||||
import net.mamoe.mirai.network.WrongPasswordException
|
import net.mamoe.mirai.network.WrongPasswordException
|
||||||
import net.mamoe.mirai.qqandroid.FriendInfoImpl
|
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.*
|
||||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||||
import net.mamoe.mirai.utils.io.PlatformSocket
|
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 net.mamoe.mirai.utils.io.useBytes
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.jvm.Volatile
|
import kotlin.jvm.Volatile
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
@UseExperimental(MiraiInternalAPI::class)
|
@OptIn(MiraiInternalAPI::class)
|
||||||
internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler() {
|
internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler() {
|
||||||
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||||
override val supervisor: CompletableJob = SupervisorJob(bot.coroutineContext[Job])
|
override val supervisor: CompletableJob = SupervisorJob(bot.coroutineContext[Job])
|
||||||
@ -67,17 +65,20 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
|
|
||||||
private val packetReceiveLock: Mutex = Mutex()
|
private val packetReceiveLock: Mutex = Mutex()
|
||||||
|
|
||||||
private fun startPacketReceiverJobOrKill(cancelCause: CancellationException? = null): Job {
|
private suspend fun startPacketReceiverJobOrKill(cancelCause: CancellationException? = null): Job {
|
||||||
_packetReceiverJob?.cancel(cancelCause)
|
_packetReceiverJob?.cancel(cancelCause)
|
||||||
|
_packetReceiverJob?.join()
|
||||||
|
|
||||||
return this.launch(CoroutineName("Incoming Packet Receiver")) {
|
return this.launch(CoroutineName("Incoming Packet Receiver")) {
|
||||||
while (channel.isOpen) {
|
while (channel.isOpen && isActive) {
|
||||||
val rawInput = try {
|
val rawInput = try {
|
||||||
channel.read()
|
channel.read()
|
||||||
} catch (e: CancellationException) {
|
} catch (e: CancellationException) {
|
||||||
return@launch
|
return@launch
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
BotOfflineEvent.Dropped(bot).broadcast()
|
if (this@QQAndroidBotNetworkHandler.isActive) {
|
||||||
|
bot.launch { BotOfflineEvent.Dropped(bot, e).broadcast() }
|
||||||
|
}
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
packetReceiveLock.withLock {
|
packetReceiveLock.withLock {
|
||||||
@ -87,25 +88,42 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
}.also { _packetReceiverJob = it }
|
}.also { _packetReceiverJob = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun relogin() {
|
private fun startHeartbeatJobOrKill(cancelCause: CancellationException? = null): Job {
|
||||||
heartbeatJob?.cancel()
|
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.isInitialized) {
|
||||||
if (channel.isOpen) {
|
if (channel.isOpen) {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
registerClientOnline()
|
registerClientOnline(500)
|
||||||
}.exceptionOrNull() ?: return
|
}.exceptionOrNull() ?: return
|
||||||
logger.info("Cannot do fast relogin. Trying slow relogin")
|
logger.info("Cannot do fast relogin. Trying slow relogin")
|
||||||
}
|
}
|
||||||
channel.close()
|
channel.close()
|
||||||
}
|
}
|
||||||
channel = PlatformSocket()
|
channel = PlatformSocket()
|
||||||
// TODO: 2020/2/14 连接多个服务器
|
// TODO: 2020/2/14 连接多个服务器, #52
|
||||||
withTimeoutOrNull(3000) {
|
withTimeoutOrNull(3000) {
|
||||||
channel.connect("113.96.13.208", 8080)
|
channel.connect("113.96.13.208", 8080)
|
||||||
} ?: error("timeout connecting server")
|
} ?: 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()
|
var response: WtLogin.Login.LoginPacketResponse = WtLogin.Login.SubCommand9(bot.client).sendAndExpect()
|
||||||
mainloop@ while (true) {
|
mainloop@ while (true) {
|
||||||
when (response) {
|
when (response) {
|
||||||
@ -116,21 +134,17 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
|
|
||||||
is WtLogin.Login.LoginPacketResponse.Captcha -> when (response) {
|
is WtLogin.Login.LoginPacketResponse.Captcha -> when (response) {
|
||||||
is WtLogin.Login.LoginPacketResponse.Captcha.Picture -> {
|
is WtLogin.Login.LoginPacketResponse.Captcha.Picture -> {
|
||||||
var result = response.data.withUse {
|
var result = bot.configuration.loginSolver.onSolvePicCaptcha(bot, response.data)
|
||||||
bot.configuration.loginSolver.onSolvePicCaptcha(bot, this)
|
|
||||||
}
|
|
||||||
if (result == null || result.length != 4) {
|
if (result == null || result.length != 4) {
|
||||||
//refresh captcha
|
//refresh captcha
|
||||||
result = "ABCD"
|
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
|
continue@mainloop
|
||||||
}
|
}
|
||||||
is WtLogin.Login.LoginPacketResponse.Captcha.Slider -> {
|
is WtLogin.Login.LoginPacketResponse.Captcha.Slider -> {
|
||||||
var ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url)
|
val ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url).orEmpty()
|
||||||
if (ticket == null) {
|
|
||||||
ticket = ""
|
|
||||||
}
|
|
||||||
response = WtLogin.Login.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect()
|
response = WtLogin.Login.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect()
|
||||||
continue@mainloop
|
continue@mainloop
|
||||||
}
|
}
|
||||||
@ -156,55 +170,73 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
|
|
||||||
// println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}")
|
// println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}")
|
||||||
registerClientOnline()
|
registerClientOnline()
|
||||||
|
startHeartbeatJobOrKill()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun registerClientOnline() {
|
private suspend fun registerClientOnline(timeoutMillis: Long = 3000) {
|
||||||
StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>()
|
StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>(timeoutMillis)
|
||||||
}
|
}
|
||||||
|
|
||||||
// caches
|
// caches
|
||||||
private val _pendingEnabled = atomic(true)
|
private val _pendingEnabled = atomic(true)
|
||||||
internal val pendingEnabled get() = _pendingEnabled.value
|
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 {
|
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()
|
bot.groups.delegate.clear()
|
||||||
|
|
||||||
val friendListJob = launch {
|
val friendListJob = launch {
|
||||||
try {
|
lateinit var loadFriends: suspend () -> Unit
|
||||||
|
// 不要用 fun, 不要 join declaration, 不要用 val, 编译失败警告
|
||||||
|
loadFriends = suspend loadFriends@{
|
||||||
logger.info("开始加载好友信息")
|
logger.info("开始加载好友信息")
|
||||||
var currentFriendCount = 0
|
var currentFriendCount = 0
|
||||||
var totalFriendCount: Short
|
var totalFriendCount: Short
|
||||||
while (true) {
|
while (true) {
|
||||||
val data = FriendList.GetFriendGroupList(
|
val data = runCatching {
|
||||||
|
FriendList.GetFriendGroupList(
|
||||||
bot.client,
|
bot.client,
|
||||||
currentFriendCount,
|
currentFriendCount,
|
||||||
150,
|
150,
|
||||||
0,
|
0,
|
||||||
0
|
0
|
||||||
).sendAndExpect<FriendList.GetFriendGroupList.Response>(timeoutMillis = 5000, retry = 2)
|
).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
|
totalFriendCount = data.totalFriendCount
|
||||||
data.friendList.forEach {
|
data.friendList.forEach {
|
||||||
// atomic add
|
// 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++
|
currentFriendCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.verbose("正在加载好友列表 ${currentFriendCount}/${totalFriendCount}")
|
logger.verbose { "正在加载好友列表 ${currentFriendCount}/${totalFriendCount}" }
|
||||||
if (currentFriendCount >= totalFriendCount) {
|
if (currentFriendCount >= totalFriendCount) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// delay(200)
|
// delay(200)
|
||||||
}
|
}
|
||||||
logger.info("好友列表加载完成, 共 ${currentFriendCount}个")
|
logger.info { "好友列表加载完成, 共 ${currentFriendCount}个" }
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.error("加载好友列表失败|一般这是由于加载过于频繁导致/将以热加载方式加载好友列表")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadFriends()
|
||||||
}
|
}
|
||||||
|
|
||||||
val groupJob = launch {
|
val groupJob = launch {
|
||||||
@ -214,15 +246,18 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
|
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
|
||||||
|
|
||||||
troopListData.groups.forEach { troopNum ->
|
troopListData.groups.forEach { troopNum ->
|
||||||
launch {
|
// 别用 fun, 别 val, 编译失败警告
|
||||||
try {
|
lateinit var loadGroup: suspend () -> Unit
|
||||||
|
|
||||||
|
loadGroup = suspend {
|
||||||
|
tryNTimesOrException(3) {
|
||||||
bot.groups.delegate.addLast(
|
bot.groups.delegate.addLast(
|
||||||
@Suppress("DuplicatedCode")
|
@Suppress("DuplicatedCode")
|
||||||
GroupImpl(
|
(GroupImpl(
|
||||||
bot = bot,
|
bot = bot,
|
||||||
coroutineContext = bot.coroutineContext,
|
coroutineContext = bot.coroutineContext,
|
||||||
id = troopNum.groupCode,
|
id = troopNum.groupCode,
|
||||||
groupInfo = bot.queryGroupInfo(troopNum.groupCode).apply {
|
groupInfo = bot._lowLevelQueryGroupInfo(troopNum.groupCode).apply {
|
||||||
this as GroupInfoImpl
|
this as GroupInfoImpl
|
||||||
|
|
||||||
if (this.delegate.groupName == null) {
|
if (this.delegate.groupName == null) {
|
||||||
@ -239,51 +274,78 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
|
|
||||||
this.delegate.groupCode = troopNum.groupCode
|
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) {
|
} catch (e: Exception) {
|
||||||
logger.error("群${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试")
|
logger.error { "加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表" }
|
||||||
logger.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info("群组列表与群成员加载完成, 共 ${troopListData.groups.size}个")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.error("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表")
|
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
joinAll(friendListJob, groupJob)
|
joinAll(friendListJob, groupJob)
|
||||||
|
|
||||||
heartbeatJob = this@QQAndroidBotNetworkHandler.launch(CoroutineName("Heartbeat")) {
|
withTimeoutOrNull(5000) {
|
||||||
while (this.isActive) {
|
lateinit var listener: Listener<PacketReceivedEvent>
|
||||||
delay(bot.configuration.heartbeatPeriodMillis)
|
listener = this.subscribeAlways {
|
||||||
val failException = doHeartBeat()
|
if (it.packet is MessageSvc.PbGetMsg.GetMsgSuccess) {
|
||||||
if (failException != null) {
|
listener.complete()
|
||||||
delay(bot.configuration.firstReconnectDelayMillis)
|
|
||||||
close()
|
|
||||||
BotOfflineEvent.Dropped(bot).broadcast()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect()
|
||||||
|
} ?: error("timeout syncing friend message history")
|
||||||
|
|
||||||
bot.firstLoginSucceed = true
|
bot.firstLoginSucceed = true
|
||||||
|
|
||||||
_pendingEnabled.value = false
|
_pendingEnabled.value = false
|
||||||
pendingIncomingPackets?.forEach {
|
pendingIncomingPackets?.forEach {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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? {
|
suspend fun doHeartBeat(): Exception? {
|
||||||
val lastException: Exception?
|
val lastException: Exception?
|
||||||
try {
|
try {
|
||||||
|
kotlin.runCatching {
|
||||||
|
Heartbeat.Alive(bot.client)
|
||||||
|
.sendAndExpect<Heartbeat.Alive.Response>(
|
||||||
|
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
|
||||||
|
retry = 2
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
Heartbeat.Alive(bot.client)
|
Heartbeat.Alive(bot.client)
|
||||||
.sendAndExpect<Heartbeat.Alive.Response>(
|
.sendAndExpect<Heartbeat.Alive.Response>(
|
||||||
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
|
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
|
||||||
@ -301,10 +363,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
*/
|
*/
|
||||||
@Volatile
|
@Volatile
|
||||||
private var cachedPacketTimeoutJob: Job? = null
|
private var cachedPacketTimeoutJob: Job? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存的包
|
* 缓存的包
|
||||||
*/
|
*/
|
||||||
private val cachedPacket: AtomicRef<ByteReadPacket?> = atomic(null)
|
private val cachedPacket: AtomicRef<ByteReadPacket?> = atomic(null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存的包还差多少长度
|
* 缓存的包还差多少长度
|
||||||
*/
|
*/
|
||||||
@ -316,10 +380,17 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
*
|
*
|
||||||
* @param input 一个完整的包的内容, 去掉开头的 int 包长度
|
* @param input 一个完整的包的内容, 去掉开头的 int 包长度
|
||||||
*/
|
*/
|
||||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
fun parsePacketAsync(input: Input): Job {
|
fun parsePacketAsync(input: Input): Job {
|
||||||
return this.launch(start = CoroutineStart.ATOMIC) {
|
return this.launch(
|
||||||
|
start = CoroutineStart.ATOMIC
|
||||||
|
) {
|
||||||
|
try {
|
||||||
input.use { parsePacket(it) }
|
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
|
// with generic type, less mistakes
|
||||||
private suspend inline fun <P : Packet> generifiedParsePacket(input: Input) {
|
private suspend fun <P : Packet?> generifiedParsePacket(input: Input) {
|
||||||
KnownPacketFactories.parseIncomingPacket(bot, input) { packetFactory: PacketFactory<P>, packet: P, commandName: String, sequenceId: Int ->
|
KnownPacketFactories.parseIncomingPacket(
|
||||||
handlePacket(packetFactory, packet, commandName, sequenceId)
|
bot,
|
||||||
|
input
|
||||||
|
) { packetFactory: PacketFactory<P>, packet: P, commandName: String, sequenceId: Int ->
|
||||||
if (packet is MultiPacket<*>) {
|
if (packet is MultiPacket<*>) {
|
||||||
packet.forEach {
|
packet.forEach {
|
||||||
handlePacket(null, it, commandName, sequenceId)
|
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).
|
// 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 ->
|
packetListeners.forEach { listener ->
|
||||||
if (listener.filter(commandName, sequenceId) && packetListeners.remove(listener)) {
|
if (listener.filter(commandName, sequenceId) && packetListeners.remove(listener)) {
|
||||||
listener.complete(packet)
|
listener.complete(packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check top-level cancelling
|
packetFactory?.run {
|
||||||
if (PacketReceivedEvent(packet).broadcast().isCancelled) {
|
when (this) {
|
||||||
|
is OutgoingPacketFactory<P> -> bot.handle(packet)
|
||||||
|
is IncomingPacketFactory<P> -> bot.handle(packet, sequenceId)?.sendWithoutExpect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet != null && PacketReceivedEvent(packet).broadcast().isCancelled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// broadcast
|
|
||||||
if (packet is Event) {
|
if (packet is Event) {
|
||||||
if (packet is BroadcastControllable) {
|
if (packet is BroadcastControllable) {
|
||||||
if (packet.shouldBroadcast) packet.broadcast()
|
if (packet.shouldBroadcast) packet.broadcast()
|
||||||
@ -372,15 +463,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
|
|
||||||
if (packet is CancellableEvent && packet.isCancelled) return
|
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) {
|
while (rawInput.remaining >= length) {
|
||||||
parsePacketAsync(rawInput.readPacket(length))
|
parsePacketAsync(rawInput.readPacketExact(length))
|
||||||
|
|
||||||
if (rawInput.remaining == 0L) {
|
if (rawInput.remaining == 0L) {
|
||||||
cachedPacket.value = null // 表示包长度正好
|
cachedPacket.value = null // 表示包长度正好
|
||||||
@ -467,63 +549,77 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
* 发送一个包, 但不期待任何返回.
|
* 发送一个包, 但不期待任何返回.
|
||||||
*/
|
*/
|
||||||
suspend fun OutgoingPacket.sendWithoutExpect() {
|
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")) {
|
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
|
||||||
|
PacketLogger.debug { "Channel sending: $commandName" }
|
||||||
channel.send(delegate)
|
channel.send(delegate)
|
||||||
|
PacketLogger.debug { "Channel send done: $commandName" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TimeoutException(override val message: String?) : Exception()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms)
|
* 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms)
|
||||||
*
|
*
|
||||||
* @param retry 当不为 0 时将使用 [ByteArrayPool] 缓存. 因此若非必要, 请不要允许 retry
|
* @param retry 当不为 0 时将使用 [ByteArrayPool] 缓存. 因此若非必要, 请不要允许 retry
|
||||||
*/
|
*/
|
||||||
suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 3000, retry: Int = 0): E {
|
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" }
|
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) {
|
if (retry == 0) {
|
||||||
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
|
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
|
||||||
packetListeners.addLast(handler)
|
packetListeners.addLast(handler)
|
||||||
try {
|
try {
|
||||||
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
|
return doSendAndReceive(handler, delegate, 0) // no need
|
||||||
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")
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
packetListeners.remove(handler)
|
packetListeners.remove(handler)
|
||||||
}
|
}
|
||||||
} else this.delegate.useBytes { data, length ->
|
} else this.delegate.useBytes { data, length ->
|
||||||
repeat(retry + 1) {
|
return tryNTimes(retry + 1) {
|
||||||
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
|
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
|
||||||
packetListeners.addLast(handler)
|
packetListeners.addLast(handler)
|
||||||
try {
|
try {
|
||||||
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
|
doSendAndReceive(handler, data, length)
|
||||||
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
|
|
||||||
} finally {
|
} finally {
|
||||||
packetListeners.remove(handler)
|
packetListeners.remove(handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw lastException!!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,8 +630,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
internal inner class PacketListener( // callback
|
internal inner class PacketListener( // callback
|
||||||
val commandName: String,
|
val commandName: String,
|
||||||
val sequenceId: Int
|
val sequenceId: Int
|
||||||
) : CompletableDeferred<Packet> by CompletableDeferred(supervisor) {
|
) : CompletableDeferred<Packet?> by CompletableDeferred(supervisor) {
|
||||||
fun filter(commandName: String, sequenceId: Int) = this.commandName == commandName && this.sequenceId == sequenceId
|
fun filter(commandName: String, sequenceId: Int) =
|
||||||
|
this.commandName == commandName && this.sequenceId == sequenceId
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close(cause: Throwable?) {
|
override fun close(cause: Throwable?) {
|
||||||
@ -545,5 +642,5 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
super.close(cause)
|
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
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("NOTHING_TO_INLINE", "EXPERIMENTAL_API_USAGE")
|
||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network
|
package net.mamoe.mirai.qqandroid.network
|
||||||
|
|
||||||
import kotlinx.atomicfu.AtomicInt
|
import kotlinx.atomicfu.AtomicInt
|
||||||
import kotlinx.atomicfu.atomic
|
import kotlinx.atomicfu.atomic
|
||||||
import kotlinx.io.core.ByteReadPacket
|
import kotlinx.io.core.*
|
||||||
import kotlinx.io.core.toByteArray
|
|
||||||
import net.mamoe.mirai.BotAccount
|
import net.mamoe.mirai.BotAccount
|
||||||
import net.mamoe.mirai.RawAccountIdUse
|
import net.mamoe.mirai.RawAccountIdUse
|
||||||
import net.mamoe.mirai.data.OnlineStatus
|
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.EMPTY_BYTE_ARRAY
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.Tlv
|
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.qqandroid.utils.NetworkType
|
||||||
import net.mamoe.mirai.utils.SystemDeviceInfo
|
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
import net.mamoe.mirai.utils.cryptor.ECDH
|
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.*
|
import net.mamoe.mirai.utils.io.*
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -41,7 +40,7 @@ import net.mamoe.mirai.utils.io.*
|
|||||||
DOMAINS
|
DOMAINS
|
||||||
Pskey: "openmobile.qq.com"
|
Pskey: "openmobile.qq.com"
|
||||||
*/
|
*/
|
||||||
@UseExperimental(MiraiExperimentalAPI::class, MiraiInternalAPI::class)
|
@OptIn(MiraiExperimentalAPI::class, MiraiInternalAPI::class)
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal open class QQAndroidClient(
|
internal open class QQAndroidClient(
|
||||||
context: Context,
|
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? {
|
internal inline fun <R> tryDecryptOrNull(data: ByteArray, size: Int = data.size, mapper: (ByteArray) -> R): R? {
|
||||||
keys.forEach { (key, value) ->
|
keys.forEach { (key, value) ->
|
||||||
kotlin.runCatching {
|
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
|
return null
|
||||||
@ -101,8 +100,8 @@ internal open class QQAndroidClient(
|
|||||||
|
|
||||||
var openAppId: Long = 715019303L
|
var openAppId: Long = 715019303L
|
||||||
|
|
||||||
val apkVersionName: ByteArray get() = "8.2.0".toByteArray()
|
val apkVersionName: ByteArray get() = "8.2.7".toByteArray()
|
||||||
val buildVer: String get() = "8.2.0.1296"
|
val buildVer: String get() = "8.2.7.4410"
|
||||||
|
|
||||||
private val messageSequenceId: AtomicInt = atomic(22911)
|
private val messageSequenceId: AtomicInt = atomic(22911)
|
||||||
internal fun atomicNextMessageSequenceId(): Int = messageSequenceId.getAndAdd(2)
|
internal fun atomicNextMessageSequenceId(): Int = messageSequenceId.getAndAdd(2)
|
||||||
@ -113,7 +112,7 @@ internal open class QQAndroidClient(
|
|||||||
private val highwayDataTransSequenceIdForGroup: AtomicInt = atomic(87017)
|
private val highwayDataTransSequenceIdForGroup: AtomicInt = atomic(87017)
|
||||||
internal fun nextHighwayDataTransSequenceIdForGroup(): Int = highwayDataTransSequenceIdForGroup.getAndAdd(2)
|
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)
|
internal fun nextHighwayDataTransSequenceIdForFriend(): Int = highwayDataTransSequenceIdForFriend.getAndAdd(2)
|
||||||
|
|
||||||
val appClientVersion: Int = 0
|
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()
|
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
|
val protocolVersion: Short = 8001
|
||||||
|
|
||||||
@ -159,16 +158,18 @@ internal open class QQAndroidClient(
|
|||||||
*/
|
*/
|
||||||
val uin: Long get() = _uin
|
val uin: Long get() = _uin
|
||||||
|
|
||||||
@UseExperimental(RawAccountIdUse::class)
|
@OptIn(RawAccountIdUse::class)
|
||||||
@Suppress("PropertyName")
|
@Suppress("PropertyName", "DEPRECATION_ERROR")
|
||||||
internal var _uin: Long = bot.account.id
|
internal var _uin: Long = bot.account.id
|
||||||
|
|
||||||
var t530: ByteArray? = null
|
var t530: ByteArray? = null
|
||||||
var t528: ByteArray? = null
|
var t528: ByteArray? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* t108 时更新
|
* t108 时更新
|
||||||
*/
|
*/
|
||||||
var ksid: ByteArray = "|454001228437590|A8.2.0.27f6ea96".toByteArray()
|
var ksid: ByteArray = "|454001228437590|A8.2.7.27f6ea96".toByteArray()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* t186
|
* t186
|
||||||
*/
|
*/
|
||||||
@ -190,8 +191,9 @@ internal open class QQAndroidClient(
|
|||||||
lateinit var t104: ByteArray
|
lateinit var t104: ByteArray
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(MiraiInternalAPI::class)
|
||||||
internal fun generateTgtgtKey(guid: ByteArray): ByteArray =
|
internal fun generateTgtgtKey(guid: ByteArray): ByteArray =
|
||||||
md5(getRandomByteArray(16) + guid)
|
MiraiPlatformUtils.md5(getRandomByteArray(16) + guid)
|
||||||
|
|
||||||
|
|
||||||
internal class ReserveUinInfo(
|
internal class ReserveUinInfo(
|
||||||
@ -314,6 +316,10 @@ internal class Pt4Token(data: ByteArray, creationTime: Long, expireTime: Long) :
|
|||||||
internal typealias PSKeyMap = MutableMap<String, PSKey>
|
internal typealias PSKeyMap = MutableMap<String, PSKey>
|
||||||
internal typealias Pt4TokenMap = MutableMap<String, Pt4Token>
|
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) =
|
internal fun parsePSKeyMapAndPt4TokenMap(data: ByteArray, creationTime: Long, expireTime: Long, outPSKeyMap: PSKeyMap, outPt4TokenMap: Pt4TokenMap) =
|
||||||
data.read {
|
data.read {
|
||||||
repeat(readShort().toInt()) {
|
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.URLProtocol
|
||||||
import io.ktor.http.content.OutgoingContent
|
import io.ktor.http.content.OutgoingContent
|
||||||
import io.ktor.http.userAgent
|
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.Input
|
||||||
|
import kotlinx.io.core.discardExact
|
||||||
import kotlinx.io.core.readAvailable
|
import kotlinx.io.core.readAvailable
|
||||||
import kotlinx.io.core.use
|
import kotlinx.io.core.use
|
||||||
import kotlinx.io.pool.useInstance
|
import kotlinx.io.pool.useInstance
|
||||||
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
|
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
|
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.MiraiInternalAPI
|
||||||
|
import net.mamoe.mirai.utils.copyAndClose
|
||||||
import net.mamoe.mirai.utils.io.ByteArrayPool
|
import net.mamoe.mirai.utils.io.ByteArrayPool
|
||||||
import net.mamoe.mirai.utils.io.PlatformSocket
|
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")
|
@Suppress("SpellCheckingInspection")
|
||||||
internal suspend inline fun HttpClient.postImage(
|
internal suspend fun HttpClient.postImage(
|
||||||
htcmd: String,
|
htcmd: String,
|
||||||
uin: Long,
|
uin: Long,
|
||||||
groupcode: Long?,
|
groupcode: Long?,
|
||||||
imageInput: Input,
|
imageInput: Any, // Input from kotlinx.io, InputStream from kotlinx.io MPP, ByteReadChannel from ktor
|
||||||
inputSize: Long,
|
inputSize: Long,
|
||||||
uKeyHex: String
|
uKeyHex: String
|
||||||
): Boolean = try {
|
): Boolean = post<HttpStatusCode> {
|
||||||
post<HttpStatusCode> {
|
|
||||||
url {
|
url {
|
||||||
protocol = URLProtocol.HTTP
|
protocol = URLProtocol.HTTP
|
||||||
host = "htdata2.qq.com"
|
host = "htdata2.qq.com"
|
||||||
@ -63,54 +69,62 @@ internal suspend inline fun HttpClient.postImage(
|
|||||||
override val contentType: ContentType = ContentType.Image.Any
|
override val contentType: ContentType = ContentType.Image.Any
|
||||||
override val contentLength: Long = inputSize
|
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 ->
|
ByteArrayPool.useInstance { buffer: ByteArray ->
|
||||||
|
when (imageInput) {
|
||||||
|
is Input -> {
|
||||||
var size: Int
|
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.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
|
} == HttpStatusCode.OK
|
||||||
} finally {
|
|
||||||
imageInput.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
@UseExperimental(MiraiInternalAPI::class)
|
@OptIn(MiraiInternalAPI::class, InternalSerializationApi::class)
|
||||||
internal object HighwayHelper {
|
internal object HighwayHelper {
|
||||||
|
@OptIn(InternalCoroutinesApi::class)
|
||||||
suspend fun uploadImage(
|
suspend fun uploadImage(
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
serverIp: String,
|
serverIp: String,
|
||||||
serverPort: Int,
|
serverPort: Int,
|
||||||
uKey: ByteArray,
|
uKey: ByteArray,
|
||||||
imageInput: Input,
|
imageInput: Any,
|
||||||
inputSize: Int,
|
inputSize: Int,
|
||||||
md5: ByteArray,
|
fileMd5: ByteArray,
|
||||||
commandId: Int // group=2, friend=1
|
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(uKey.size == 128) { "bad uKey. Required size=128, got ${uKey.size}" }
|
||||||
require(commandId == 2 || commandId == 1) { "bad commandId. Must be 1 or 2" }
|
require(commandId == 2 || commandId == 1) { "bad commandId. Must be 1 or 2" }
|
||||||
|
|
||||||
val socket = PlatformSocket()
|
val socket = PlatformSocket()
|
||||||
socket.connect(serverIp, serverPort)
|
socket.connect(serverIp, serverPort)
|
||||||
socket.use {
|
socket.use {
|
||||||
socket.send(
|
createImageDataPacketSequence(
|
||||||
Highway.RequestDataTrans(
|
client = client,
|
||||||
uin = client.uin,
|
|
||||||
command = "PicUp.DataUp",
|
command = "PicUp.DataUp",
|
||||||
sequenceId =
|
commandId = commandId,
|
||||||
if (commandId == 2) client.nextHighwayDataTransSequenceIdForGroup()
|
|
||||||
else client.nextHighwayDataTransSequenceIdForFriend(),
|
|
||||||
uKey = uKey,
|
uKey = uKey,
|
||||||
data = imageInput,
|
data = imageInput,
|
||||||
dataSize = inputSize,
|
dataSize = inputSize,
|
||||||
md5 = md5,
|
fileMd5 = fileMd5
|
||||||
commandId = commandId
|
).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
|
//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 {
|
socket.read().withUse {
|
||||||
discardExact(1)
|
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
|
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import net.mamoe.mirai.data.Packet
|
import net.mamoe.mirai.data.Packet
|
||||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||||
|
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class BigDataChannel(
|
internal class BigDataChannel(
|
||||||
@SerialId(0) val vBigdataIplists: List<BigDataIpList>,
|
@JceId(0) val vBigdataIplists: List<BigDataIpList>,
|
||||||
@SerialId(1) val sBigdataSigSession: ByteArray? = null,
|
@JceId(1) val sBigdataSigSession: ByteArray? = null,
|
||||||
@SerialId(2) val sBigdataKeySession: ByteArray? = null,
|
@JceId(2) val sBigdataKeySession: ByteArray? = null,
|
||||||
@SerialId(3) val uSigUin: Long? = null,
|
@JceId(3) val uSigUin: Long? = null,
|
||||||
@SerialId(4) val iConnectFlag: Int? = 1,
|
@JceId(4) val iConnectFlag: Int? = 1,
|
||||||
@SerialId(5) val vBigdataPbBuf: ByteArray? = null
|
@JceId(5) val vBigdataPbBuf: ByteArray? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class BigDataIpInfo(
|
internal class BigDataIpInfo(
|
||||||
@SerialId(0) val uType: Long,
|
@JceId(0) val uType: Long,
|
||||||
@SerialId(1) val sIp: String = "",
|
@JceId(1) val sIp: String = "",
|
||||||
@SerialId(2) val uPort: Long
|
@JceId(2) val uPort: Long
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class BigDataIpList(
|
internal class BigDataIpList(
|
||||||
@SerialId(0) val uServiceType: Long,
|
@JceId(0) val uServiceType: Long,
|
||||||
@SerialId(1) val vIplist: List<BigDataIpInfo>,
|
@JceId(1) val vIplist: List<BigDataIpInfo>,
|
||||||
@SerialId(2) val netSegConfs: List<NetSegConf>? = null,
|
@JceId(2) val netSegConfs: List<NetSegConf>? = null,
|
||||||
@SerialId(3) val ufragmentSize: Long? = null
|
@JceId(3) val ufragmentSize: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ClientLogConfig(
|
internal class ClientLogConfig(
|
||||||
@SerialId(1) val type: Int,
|
@JceId(1) val type: Int,
|
||||||
@SerialId(2) val timeStart: TimeStamp? = null,
|
@JceId(2) val timeStart: TimeStamp? = null,
|
||||||
@SerialId(3) val timeFinish: TimeStamp? = null,
|
@JceId(3) val timeFinish: TimeStamp? = null,
|
||||||
@SerialId(4) val loglevel: Byte? = null,
|
@JceId(4) val loglevel: Byte? = null,
|
||||||
@SerialId(5) val cookie: Int? = null,
|
@JceId(5) val cookie: Int? = null,
|
||||||
@SerialId(6) val lseq: Long? = null
|
@JceId(6) val lseq: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class DomainIpChannel(
|
internal class DomainIpChannel(
|
||||||
@SerialId(0) val vDomainIplists: List<DomainIpList>
|
@JceId(0) val vDomainIplists: List<DomainIpList>
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class DomainIpInfo(
|
internal class DomainIpInfo(
|
||||||
@SerialId(1) val uIp: Int,
|
@JceId(1) val uIp: Int,
|
||||||
@SerialId(2) val uPort: Int
|
@JceId(2) val uPort: Int
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class DomainIpList(
|
internal class DomainIpList(
|
||||||
@SerialId(0) val uDomainType: Int,
|
@JceId(0) val uDomainType: Int,
|
||||||
@SerialId(1) val vIplist: List<DomainIpInfo>
|
@JceId(1) val vIplist: List<DomainIpInfo>
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class FileStoragePushFSSvcList(
|
internal class FileStoragePushFSSvcList(
|
||||||
@SerialId(0) val vUpLoadList: List<FileStorageServerListInfo>,
|
@JceId(0) val vUpLoadList: List<FileStorageServerListInfo>,
|
||||||
@SerialId(1) val vPicDownLoadList: List<FileStorageServerListInfo>,
|
@JceId(1) val vPicDownLoadList: List<FileStorageServerListInfo>,
|
||||||
@SerialId(2) val vGPicDownLoadList: List<FileStorageServerListInfo>? = null,
|
@JceId(2) val vGPicDownLoadList: List<FileStorageServerListInfo>? = null,
|
||||||
@SerialId(3) val vQzoneProxyServiceList: List<FileStorageServerListInfo>? = null,
|
@JceId(3) val vQzoneProxyServiceList: List<FileStorageServerListInfo>? = null,
|
||||||
@SerialId(4) val vUrlEncodeServiceList: List<FileStorageServerListInfo>? = null,
|
@JceId(4) val vUrlEncodeServiceList: List<FileStorageServerListInfo>? = null,
|
||||||
@SerialId(5) val bigDataChannel: BigDataChannel? = null,
|
@JceId(5) val bigDataChannel: BigDataChannel? = null,
|
||||||
@SerialId(6) val vVipEmotionList: List<FileStorageServerListInfo>? = null,
|
@JceId(6) val vVipEmotionList: List<FileStorageServerListInfo>? = null,
|
||||||
@SerialId(7) val vC2CPicDownList: List<FileStorageServerListInfo>? = null,
|
@JceId(7) val vC2CPicDownList: List<FileStorageServerListInfo>? = null,
|
||||||
@SerialId(8) val fmtIPInfo: FmtIPInfo? = null,
|
@JceId(8) val fmtIPInfo: FmtIPInfo? = null,
|
||||||
@SerialId(9) val domainIpChannel: DomainIpChannel? = null,
|
@JceId(9) val domainIpChannel: DomainIpChannel? = null,
|
||||||
@SerialId(10) val pttlist: ByteArray? = null
|
@JceId(10) val pttlist: ByteArray? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class FileStorageServerListInfo(
|
internal class FileStorageServerListInfo(
|
||||||
@SerialId(1) val sIP: String = "",
|
@JceId(1) val sIP: String = "",
|
||||||
@SerialId(2) val iPort: Int
|
@JceId(2) val iPort: Int
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class FmtIPInfo(
|
internal class FmtIPInfo(
|
||||||
@SerialId(0) val sGateIp: String = "",
|
@JceId(0) val sGateIp: String = "",
|
||||||
@SerialId(1) val iGateIpOper: Long
|
@JceId(1) val iGateIpOper: Long
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class NetSegConf(
|
internal class NetSegConf(
|
||||||
@SerialId(0) val uint32NetType: Long? = null,
|
@JceId(0) val uint32NetType: Long? = null,
|
||||||
@SerialId(1) val uint32Segsize: Long? = null,
|
@JceId(1) val uint32Segsize: Long? = null,
|
||||||
@SerialId(2) val uint32Segnum: Long? = null,
|
@JceId(2) val uint32Segnum: Long? = null,
|
||||||
@SerialId(3) val uint32Curconnnum: Long? = null
|
@JceId(3) val uint32Curconnnum: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Suppress("ArrayInDataClass")
|
@Suppress("ArrayInDataClass")
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class PushReq(
|
internal data class PushReq(
|
||||||
@SerialId(1) val type: Int,
|
@JceId(1) val type: Int,
|
||||||
@SerialId(2) val jcebuf: ByteArray,
|
@JceId(2) val jcebuf: ByteArray,
|
||||||
@SerialId(3) val seq: Long
|
@JceId(3) val seq: Long
|
||||||
) : JceStruct, Packet
|
) : JceStruct, Packet
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class PushResp(
|
internal class PushResp(
|
||||||
@SerialId(1) val type: Int,
|
@JceId(1) val type: Int,
|
||||||
@SerialId(2) val seq: Long,
|
@JceId(2) val seq: Long,
|
||||||
@SerialId(3) val jcebuf: ByteArray? = null
|
@JceId(3) val jcebuf: ByteArray? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class SsoServerList(
|
internal class SsoServerList(
|
||||||
@SerialId(1) val v2G3GList: List<SsoServerListInfo>,
|
@JceId(1) val v2G3GList: List<SsoServerListInfo>,
|
||||||
@SerialId(3) val vWifiList: List<SsoServerListInfo>,
|
@JceId(3) val vWifiList: List<SsoServerListInfo>,
|
||||||
@SerialId(4) val iReconnect: Int,
|
@JceId(4) val iReconnect: Int,
|
||||||
@SerialId(5) val testSpeed: Byte? = null,
|
@JceId(5) val testSpeed: Byte? = null,
|
||||||
@SerialId(6) val useNewList: Byte? = null,
|
@JceId(6) val useNewList: Byte? = null,
|
||||||
@SerialId(7) val iMultiConn: Int? = 1,
|
@JceId(7) val iMultiConn: Int? = 1,
|
||||||
@SerialId(8) val vHttp2g3glist: List<SsoServerListInfo>? = null,
|
@JceId(8) val vHttp2g3glist: List<SsoServerListInfo>? = null,
|
||||||
@SerialId(9) val vHttpWifilist: List<SsoServerListInfo>? = null
|
@JceId(9) val vHttpWifilist: List<SsoServerListInfo>? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class SsoServerListInfo(
|
internal class SsoServerListInfo(
|
||||||
@SerialId(1) val sIP: String = "",
|
@JceId(1) val sIP: String = "",
|
||||||
@SerialId(2) val iPort: Int,
|
@JceId(2) val iPort: Int,
|
||||||
@SerialId(3) val linkType: Byte,
|
@JceId(3) val linkType: Byte,
|
||||||
@SerialId(4) val proxy: Byte,
|
@JceId(4) val proxy: Byte,
|
||||||
@SerialId(5) val protocolType: Byte? = null,
|
@JceId(5) val protocolType: Byte? = null,
|
||||||
@SerialId(6) val iTimeOut: Int? = 10
|
@JceId(6) val iTimeOut: Int? = 10
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class TimeStamp(
|
internal class TimeStamp(
|
||||||
@SerialId(1) val year: Int,
|
@JceId(1) val year: Int,
|
||||||
@SerialId(2) val month: Byte,
|
@JceId(2) val month: Byte,
|
||||||
@SerialId(3) val day: Byte,
|
@JceId(3) val day: Byte,
|
||||||
@SerialId(4) val hour: Byte
|
@JceId(4) val hour: Byte
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
@ -9,172 +9,172 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||||
|
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ModifyGroupCardReq(
|
internal class ModifyGroupCardReq(
|
||||||
@SerialId(0) val dwZero: Long,
|
@JceId(0) val dwZero: Long,
|
||||||
@SerialId(1) val dwGroupCode: Long,
|
@JceId(1) val dwGroupCode: Long,
|
||||||
@SerialId(2) val dwNewSeq: Long,
|
@JceId(2) val dwNewSeq: Long,
|
||||||
@SerialId(3) val vecUinInfo: List<stUinInfo>
|
@JceId(3) val vecUinInfo: List<stUinInfo>
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class stUinInfo(
|
internal class stUinInfo(
|
||||||
@SerialId(0) val dwuin: Long,
|
@JceId(0) val dwuin: Long,
|
||||||
@SerialId(1) val dwFlag: Long,
|
@JceId(1) val dwFlag: Long,
|
||||||
@SerialId(2) val sName: String = "",
|
@JceId(2) val sName: String = "",
|
||||||
@SerialId(3) val gender: Byte,
|
@JceId(3) val gender: Byte,
|
||||||
@SerialId(4) val sPhone: String = "",
|
@JceId(4) val sPhone: String = "",
|
||||||
@SerialId(5) val sEmail: String = "",
|
@JceId(5) val sEmail: String = "",
|
||||||
@SerialId(6) val sRemark: String = ""
|
@JceId(6) val sRemark: String = ""
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class GetFriendListReq(
|
internal class GetFriendListReq(
|
||||||
@SerialId(0) val reqtype: Int? = null,
|
@JceId(0) val reqtype: Int? = null,
|
||||||
@SerialId(1) val ifReflush: Byte? = null,
|
@JceId(1) val ifReflush: Byte? = null,
|
||||||
@SerialId(2) val uin: Long? = null,
|
@JceId(2) val uin: Long? = null,
|
||||||
@SerialId(3) val startIndex: Short? = null,
|
@JceId(3) val startIndex: Short? = null,
|
||||||
@SerialId(4) val getfriendCount: Short? = null,
|
@JceId(4) val getfriendCount: Short? = null,
|
||||||
@SerialId(5) val groupid: Byte? = null,
|
@JceId(5) val groupid: Byte? = null,
|
||||||
@SerialId(6) val ifGetGroupInfo: Byte? = null,
|
@JceId(6) val ifGetGroupInfo: Byte? = null,
|
||||||
@SerialId(7) val groupstartIndex: Byte? = null,
|
@JceId(7) val groupstartIndex: Byte? = null,
|
||||||
@SerialId(8) val getgroupCount: Byte? = null,
|
@JceId(8) val getgroupCount: Byte? = null,
|
||||||
@SerialId(9) val ifGetMSFGroup: Byte? = null,
|
@JceId(9) val ifGetMSFGroup: Byte? = null,
|
||||||
@SerialId(10) val ifShowTermType: Byte? = null,
|
@JceId(10) val ifShowTermType: Byte? = null,
|
||||||
@SerialId(11) val version: Long? = null,
|
@JceId(11) val version: Long? = null,
|
||||||
@SerialId(12) val uinList: List<Long>? = null,
|
@JceId(12) val uinList: List<Long>? = null,
|
||||||
@SerialId(13) val eAppType: Int = 0,
|
@JceId(13) val eAppType: Int = 0,
|
||||||
@SerialId(14) val ifGetDOVId: Byte? = null,
|
@JceId(14) val ifGetDOVId: Byte? = null,
|
||||||
@SerialId(15) val ifGetBothFlag: Byte? = null,
|
@JceId(15) val ifGetBothFlag: Byte? = null,
|
||||||
@SerialId(16) val vec0xd50Req: ByteArray? = null,
|
@JceId(16) val vec0xd50Req: ByteArray? = null,
|
||||||
@SerialId(17) val vec0xd6bReq: ByteArray? = null,
|
@JceId(17) val vec0xd6bReq: ByteArray? = null,
|
||||||
@SerialId(18) val vecSnsTypelist: List<Long>? = null
|
@JceId(18) val vecSnsTypelist: List<Long>? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class GetFriendListResp(
|
internal class GetFriendListResp(
|
||||||
@SerialId(0) val reqtype: Int,
|
@JceId(0) val reqtype: Int,
|
||||||
@SerialId(1) val ifReflush: Byte,
|
@JceId(1) val ifReflush: Byte,
|
||||||
@SerialId(2) val uin: Long,
|
@JceId(2) val uin: Long,
|
||||||
@SerialId(3) val startIndex: Short,
|
@JceId(3) val startIndex: Short,
|
||||||
@SerialId(4) val getfriendCount: Short,
|
@JceId(4) val getfriendCount: Short,
|
||||||
@SerialId(5) val totoalFriendCount: Short,
|
@JceId(5) val totoalFriendCount: Short,
|
||||||
@SerialId(6) val friendCount: Short,
|
@JceId(6) val friendCount: Short,
|
||||||
@SerialId(7) val vecFriendInfo: List<FriendInfo>? = null,
|
@JceId(7) val vecFriendInfo: List<FriendInfo>? = null,
|
||||||
@SerialId(8) val groupid: Byte? = null,
|
@JceId(8) val groupid: Byte? = null,
|
||||||
@SerialId(9) val ifGetGroupInfo: Byte,
|
@JceId(9) val ifGetGroupInfo: Byte,
|
||||||
@SerialId(10) val groupstartIndex: Byte? = null,
|
@JceId(10) val groupstartIndex: Byte? = null,
|
||||||
@SerialId(11) val getgroupCount: Byte? = null,
|
@JceId(11) val getgroupCount: Byte? = null,
|
||||||
@SerialId(12) val totoalGroupCount: Short? = null,
|
@JceId(12) val totoalGroupCount: Short? = null,
|
||||||
@SerialId(13) val groupCount: Byte? = null,
|
@JceId(13) val groupCount: Byte? = null,
|
||||||
@SerialId(14) val vecGroupInfo: List<GroupInfo>? = null,
|
@JceId(14) val vecGroupInfo: List<GroupInfo>? = null,
|
||||||
@SerialId(15) val result: Int,
|
@JceId(15) val result: Int,
|
||||||
@SerialId(16) val errorCode: Short? = null,
|
@JceId(16) val errorCode: Short? = null,
|
||||||
@SerialId(17) val onlineFriendCount: Short? = null,
|
@JceId(17) val onlineFriendCount: Short? = null,
|
||||||
@SerialId(18) val serverTime: Long? = null,
|
@JceId(18) val serverTime: Long? = null,
|
||||||
@SerialId(19) val sqqOnLineCount: Short? = null,
|
@JceId(19) val sqqOnLineCount: Short? = null,
|
||||||
@SerialId(20) val vecMSFGroupInfo: List<GroupInfo>? = null,
|
@JceId(20) val vecMSFGroupInfo: List<GroupInfo>? = null,
|
||||||
@SerialId(21) val respType: Byte? = null,
|
@JceId(21) val respType: Byte? = null,
|
||||||
@SerialId(22) val hasOtherRespFlag: Byte? = null,
|
@JceId(22) val hasOtherRespFlag: Byte? = null,
|
||||||
@SerialId(23) val stSelfInfo: FriendInfo? = null,
|
@JceId(23) val stSelfInfo: FriendInfo? = null,
|
||||||
@SerialId(24) val showPcIcon: Byte? = null,
|
@JceId(24) val showPcIcon: Byte? = null,
|
||||||
@SerialId(25) val wGetExtSnsRspCode: Short? = null,
|
@JceId(25) val wGetExtSnsRspCode: Short? = null,
|
||||||
@SerialId(26) val stSubSrvRspCode: FriendListSubSrvRspCode? = null
|
@JceId(26) val stSubSrvRspCode: FriendListSubSrvRspCode? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class FriendListSubSrvRspCode(
|
internal class FriendListSubSrvRspCode(
|
||||||
@SerialId(0) val wGetMutualMarkRspCode: Short? = null,
|
@JceId(0) val wGetMutualMarkRspCode: Short? = null,
|
||||||
@SerialId(1) val wGetIntimateInfoRspCode: Short? = null
|
@JceId(1) val wGetIntimateInfoRspCode: Short? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class FriendInfo(
|
internal class FriendInfo(
|
||||||
@SerialId(0) val friendUin: Long,
|
@JceId(0) val friendUin: Long,
|
||||||
@SerialId(1) val groupId: Byte,
|
@JceId(1) val groupId: Byte,
|
||||||
@SerialId(2) val faceId: Short,
|
@JceId(2) val faceId: Short,
|
||||||
@SerialId(3) val remark: String = "",
|
@JceId(3) val remark: String = "",
|
||||||
@SerialId(4) val sqqtype: Byte,
|
@JceId(4) val sqqtype: Byte,
|
||||||
@SerialId(5) val status: Byte = 20,
|
@JceId(5) val status: Byte = 20,
|
||||||
@SerialId(6) val memberLevel: Byte? = null,
|
@JceId(6) val memberLevel: Byte? = null,
|
||||||
@SerialId(7) val isMqqOnLine: Byte? = null,
|
@JceId(7) val isMqqOnLine: Byte? = null,
|
||||||
@SerialId(8) val sqqOnLineState: Byte? = null,
|
@JceId(8) val sqqOnLineState: Byte? = null,
|
||||||
@SerialId(9) val isIphoneOnline: Byte? = null,
|
@JceId(9) val isIphoneOnline: Byte? = null,
|
||||||
@SerialId(10) val detalStatusFlag: Byte? = null,
|
@JceId(10) val detalStatusFlag: Byte? = null,
|
||||||
@SerialId(11) val sqqOnLineStateV2: Byte? = null,
|
@JceId(11) val sqqOnLineStateV2: Byte? = null,
|
||||||
@SerialId(12) val sShowName: String? = "",
|
@JceId(12) val sShowName: String? = "",
|
||||||
@SerialId(13) val isRemark: Byte? = null,
|
@JceId(13) val isRemark: Byte? = null,
|
||||||
@SerialId(14) val nick: String? = "",
|
@JceId(14) val nick: String? = "",
|
||||||
@SerialId(15) val specialFlag: Byte? = null,
|
@JceId(15) val specialFlag: Byte? = null,
|
||||||
@SerialId(16) val vecIMGroupID: ByteArray? = null,
|
@JceId(16) val vecIMGroupID: ByteArray? = null,
|
||||||
@SerialId(17) val vecMSFGroupID: ByteArray? = null,
|
@JceId(17) val vecMSFGroupID: ByteArray? = null,
|
||||||
@SerialId(18) val iTermType: Int? = null,
|
@JceId(18) val iTermType: Int? = null,
|
||||||
@SerialId(19) val oVipInfo: VipBaseInfo? = null,
|
@JceId(19) val oVipInfo: VipBaseInfo? = null,
|
||||||
@SerialId(20) val network: Byte? = null,
|
@JceId(20) val network: Byte? = null,
|
||||||
@SerialId(21) val vecRing: ByteArray? = null,
|
@JceId(21) val vecRing: ByteArray? = null,
|
||||||
@SerialId(22) val uAbiFlag: Long? = null,
|
@JceId(22) val uAbiFlag: Long? = null,
|
||||||
@SerialId(23) val ulFaceAddonId: Long? = null,
|
@JceId(23) val ulFaceAddonId: Long? = null,
|
||||||
@SerialId(24) val eNetworkType: Int? = 0,
|
@JceId(24) val eNetworkType: Int? = 0,
|
||||||
@SerialId(25) val uVipFont: Long? = null,
|
@JceId(25) val uVipFont: Long? = null,
|
||||||
@SerialId(26) val eIconType: Int? = 0,
|
@JceId(26) val eIconType: Int? = 0,
|
||||||
@SerialId(27) val termDesc: String? = "",
|
@JceId(27) val termDesc: String? = "",
|
||||||
@SerialId(28) val uColorRing: Long? = null,
|
@JceId(28) val uColorRing: Long? = null,
|
||||||
@SerialId(29) val apolloFlag: Byte? = null,
|
@JceId(29) val apolloFlag: Byte? = null,
|
||||||
@SerialId(30) val uApolloTimestamp: Long? = null,
|
@JceId(30) val uApolloTimestamp: Long? = null,
|
||||||
@SerialId(31) val sex: Byte? = null,
|
@JceId(31) val sex: Byte? = null,
|
||||||
@SerialId(32) val uFounderFont: Long? = null,
|
@JceId(32) val uFounderFont: Long? = null,
|
||||||
@SerialId(33) val eimId: String? = "",
|
@JceId(33) val eimId: String? = "",
|
||||||
@SerialId(34) val eimMobile: String? = "",
|
@JceId(34) val eimMobile: String? = "",
|
||||||
@SerialId(35) val olympicTorch: Byte? = null,
|
@JceId(35) val olympicTorch: Byte? = null,
|
||||||
@SerialId(36) val uApolloSignTime: Long? = null,
|
@JceId(36) val uApolloSignTime: Long? = null,
|
||||||
@SerialId(37) val uLaviUin: Long? = null,
|
@JceId(37) val uLaviUin: Long? = null,
|
||||||
@SerialId(38) val uTagUpdateTime: Long? = null,
|
@JceId(38) val uTagUpdateTime: Long? = null,
|
||||||
@SerialId(39) val uGameLastLoginTime: Long? = null,
|
@JceId(39) val uGameLastLoginTime: Long? = null,
|
||||||
@SerialId(40) val uGameAppid: Long? = null,
|
@JceId(40) val uGameAppid: Long? = null,
|
||||||
@SerialId(41) val vecCardID: ByteArray? = null,
|
@JceId(41) val vecCardID: ByteArray? = null,
|
||||||
@SerialId(42) val ulBitSet: Long? = null,
|
@JceId(42) val ulBitSet: Long? = null,
|
||||||
@SerialId(43) val kingOfGloryFlag: Byte? = null,
|
@JceId(43) val kingOfGloryFlag: Byte? = null,
|
||||||
@SerialId(44) val ulKingOfGloryRank: Long? = null,
|
@JceId(44) val ulKingOfGloryRank: Long? = null,
|
||||||
@SerialId(45) val masterUin: String? = "",
|
@JceId(45) val masterUin: String? = "",
|
||||||
@SerialId(46) val uLastMedalUpdateTime: Long? = null,
|
@JceId(46) val uLastMedalUpdateTime: Long? = null,
|
||||||
@SerialId(47) val uFaceStoreId: Long? = null,
|
@JceId(47) val uFaceStoreId: Long? = null,
|
||||||
@SerialId(48) val uFontEffect: Long? = null,
|
@JceId(48) val uFontEffect: Long? = null,
|
||||||
@SerialId(49) val sDOVId: String? = "",
|
@JceId(49) val sDOVId: String? = "",
|
||||||
@SerialId(50) val uBothFlag: Long? = null,
|
@JceId(50) val uBothFlag: Long? = null,
|
||||||
@SerialId(51) val centiShow3DFlag: Byte? = null,
|
@JceId(51) val centiShow3DFlag: Byte? = null,
|
||||||
@SerialId(52) val vecIntimateInfo: ByteArray? = null,
|
@JceId(52) val vecIntimateInfo: ByteArray? = null,
|
||||||
@SerialId(53) val showNameplate: Byte? = null,
|
@JceId(53) val showNameplate: Byte? = null,
|
||||||
@SerialId(54) val newLoverDiamondFlag: Byte? = null,
|
@JceId(54) val newLoverDiamondFlag: Byte? = null,
|
||||||
@SerialId(55) val vecExtSnsFrdData: ByteArray? = null,
|
@JceId(55) val vecExtSnsFrdData: ByteArray? = null,
|
||||||
@SerialId(56) val vecMutualMarkData: ByteArray? = null
|
@JceId(56) val vecMutualMarkData: ByteArray? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class VipBaseInfo(
|
internal class VipBaseInfo(
|
||||||
@SerialId(0) val mOpenInfo: Map<Int, VipOpenInfo>
|
@JceId(0) val mOpenInfo: Map<Int, VipOpenInfo>
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class VipOpenInfo(
|
internal class VipOpenInfo(
|
||||||
@SerialId(0) val open: Boolean,
|
@JceId(0) val open: Boolean,
|
||||||
@SerialId(1) val iVipType: Int = -1,
|
@JceId(1) val iVipType: Int = -1,
|
||||||
@SerialId(2) val iVipLevel: Int = -1,
|
@JceId(2) val iVipLevel: Int = -1,
|
||||||
@SerialId(3) val iVipFlag: Int? = null,
|
@JceId(3) val iVipFlag: Int? = null,
|
||||||
@SerialId(4) val nameplateId: Long? = null
|
@JceId(4) val nameplateId: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class GroupInfo(
|
internal class GroupInfo(
|
||||||
@SerialId(0) val groupId: Byte,
|
@JceId(0) val groupId: Byte,
|
||||||
@SerialId(1) val groupname: String = "",
|
@JceId(1) val groupname: String = "",
|
||||||
@SerialId(2) val friendCount: Int,
|
@JceId(2) val friendCount: Int,
|
||||||
@SerialId(3) val onlineFriendCount: Int,
|
@JceId(3) val onlineFriendCount: Int,
|
||||||
@SerialId(4) val seqid: Byte? = null,
|
@JceId(4) val seqid: Byte? = null,
|
||||||
@SerialId(5) val sqqOnLineCount: Int? = null
|
@JceId(5) val sqqOnLineCount: Int? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
|
@ -9,250 +9,250 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||||
|
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||||
|
|
||||||
internal class OnlinePushPack {
|
internal class OnlinePushPack {
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class DelMsgInfo(
|
internal class DelMsgInfo(
|
||||||
@SerialId(0) val fromUin: Long,
|
@JceId(0) val fromUin: Long,
|
||||||
@SerialId(1) val uMsgTime: Long,
|
@JceId(1) val uMsgTime: Long,
|
||||||
@SerialId(2) val shMsgSeq: Short,
|
@JceId(2) val shMsgSeq: Short,
|
||||||
@SerialId(3) val vMsgCookies: ByteArray? = null,
|
@JceId(3) val vMsgCookies: ByteArray? = null,
|
||||||
@SerialId(4) val wCmd: Short? = null,
|
@JceId(4) val wCmd: Short? = null,
|
||||||
@SerialId(5) val uMsgType: Long? = null,
|
@JceId(5) val uMsgType: Long? = null,
|
||||||
@SerialId(6) val uAppId: Long? = null,
|
@JceId(6) val uAppId: Long? = null,
|
||||||
@SerialId(7) val sendTime: Long? = null,
|
@JceId(7) val sendTime: Long? = null,
|
||||||
@SerialId(8) val ssoSeq: Int? = null,
|
@JceId(8) val ssoSeq: Int? = null,
|
||||||
@SerialId(9) val ssoIp: Int? = null,
|
@JceId(9) val ssoIp: Int? = null,
|
||||||
@SerialId(10) val clientIp: Int? = null
|
@JceId(10) val clientIp: Int? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class DeviceInfo(
|
internal class DeviceInfo(
|
||||||
@SerialId(0) val netType: Byte? = null,
|
@JceId(0) val netType: Byte? = null,
|
||||||
@SerialId(1) val devType: String? = "",
|
@JceId(1) val devType: String? = "",
|
||||||
@SerialId(2) val oSVer: String? = "",
|
@JceId(2) val oSVer: String? = "",
|
||||||
@SerialId(3) val vendorName: String? = "",
|
@JceId(3) val vendorName: String? = "",
|
||||||
@SerialId(4) val vendorOSName: String? = "",
|
@JceId(4) val vendorOSName: String? = "",
|
||||||
@SerialId(5) val iOSIdfa: String? = ""
|
@JceId(5) val iOSIdfa: String? = ""
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class Name(
|
internal class Name(
|
||||||
@SerialId(0) val fromUin: Long,
|
@JceId(0) val fromUin: Long,
|
||||||
@SerialId(1) val uMsgTime: Long,
|
@JceId(1) val uMsgTime: Long,
|
||||||
@SerialId(2) val shMsgType: Short,
|
@JceId(2) val shMsgType: Short,
|
||||||
@SerialId(3) val shMsgSeq: Short,
|
@JceId(3) val shMsgSeq: Short,
|
||||||
@SerialId(4) val msg: String = "",
|
@JceId(4) val msg: String = "",
|
||||||
@SerialId(5) val uRealMsgTime: Int? = null,
|
@JceId(5) val uRealMsgTime: Int? = null,
|
||||||
@SerialId(6) val vMsg: ByteArray? = null,
|
@JceId(6) val vMsg: ByteArray? = null,
|
||||||
@SerialId(7) val uAppShareID: Long? = null,
|
@JceId(7) val uAppShareID: Long? = null,
|
||||||
@SerialId(8) val vMsgCookies: ByteArray? = null,
|
@JceId(8) val vMsgCookies: ByteArray? = null,
|
||||||
@SerialId(9) val vAppShareCookie: ByteArray? = null,
|
@JceId(9) val vAppShareCookie: ByteArray? = null,
|
||||||
@SerialId(10) val msgUid: Long? = null,
|
@JceId(10) val msgUid: Long? = null,
|
||||||
@SerialId(11) val lastChangeTime: Long? = 1L,
|
@JceId(11) val lastChangeTime: Long? = 1L,
|
||||||
@SerialId(12) val vCPicInfo: List<CPicInfo>? = null,
|
@JceId(12) val vCPicInfo: List<CPicInfo>? = null,
|
||||||
@SerialId(13) val stShareData: ShareData? = null,
|
@JceId(13) val stShareData: ShareData? = null,
|
||||||
@SerialId(14) val fromInstId: Long? = null,
|
@JceId(14) val fromInstId: Long? = null,
|
||||||
@SerialId(15) val vRemarkOfSender: ByteArray? = null,
|
@JceId(15) val vRemarkOfSender: ByteArray? = null,
|
||||||
@SerialId(16) val fromMobile: String? = "",
|
@JceId(16) val fromMobile: String? = "",
|
||||||
@SerialId(17) val fromName: String? = "",
|
@JceId(17) val fromName: String? = "",
|
||||||
@SerialId(18) val vNickName: List<String>? = null,
|
@JceId(18) val vNickName: List<String>? = null,
|
||||||
@SerialId(19) val stC2CTmpMsgHead: TempMsgHead? = null
|
@JceId(19) val stC2CTmpMsgHead: TempMsgHead? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class SvcReqPushMsg(
|
internal class SvcReqPushMsg(
|
||||||
@SerialId(0) val uin: Long,
|
@JceId(0) val uin: Long,
|
||||||
@SerialId(1) val uMsgTime: Long,
|
@JceId(1) val uMsgTime: Long,
|
||||||
@SerialId(2) val vMsgInfos: List<MsgInfo>,
|
@JceId(2) val vMsgInfos: List<MsgInfo>,
|
||||||
@SerialId(3) val svrip: Int? = 0,
|
@JceId(3) val svrip: Int? = 0,
|
||||||
@SerialId(4) val vSyncCookie: ByteArray? = null,
|
@JceId(4) val vSyncCookie: ByteArray? = null,
|
||||||
@SerialId(5) val vUinPairMsg: List<UinPairMsg>? = null,
|
@JceId(5) val vUinPairMsg: List<UinPairMsg>? = null,
|
||||||
@SerialId(6) val mPreviews: Map<String, ByteArray>? = null
|
@JceId(6) val mPreviews: Map<String, ByteArray>? = null
|
||||||
// @SerialId(7) val wUserActive: Int? = null,
|
// @SerialId(7) val wUserActive: Int? = null,
|
||||||
//@SerialId(12) val wGeneralFlag: Int? = null
|
//@SerialId(12) val wGeneralFlag: Int? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class SvcRespPushMsg(
|
internal class SvcRespPushMsg(
|
||||||
@SerialId(0) val uin: Long,
|
@JceId(0) val uin: Long,
|
||||||
@SerialId(1) val vDelInfos: List<DelMsgInfo>,
|
@JceId(1) val vDelInfos: List<DelMsgInfo>,
|
||||||
@SerialId(2) val svrip: Int,
|
@JceId(2) val svrip: Int,
|
||||||
@SerialId(3) val pushToken: ByteArray? = null,
|
@JceId(3) val pushToken: ByteArray? = null,
|
||||||
@SerialId(4) val serviceType: Int? = null,
|
@JceId(4) val serviceType: Int? = null,
|
||||||
@SerialId(5) val deviceInfo: DeviceInfo? = null
|
@JceId(5) val deviceInfo: DeviceInfo? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class UinPairMsg(
|
internal class UinPairMsg(
|
||||||
@SerialId(1) val uLastReadTime: Long? = null,
|
@JceId(1) val uLastReadTime: Long? = null,
|
||||||
@SerialId(2) val peerUin: Long? = null,
|
@JceId(2) val peerUin: Long? = null,
|
||||||
@SerialId(3) val uMsgCompleted: Long? = null,
|
@JceId(3) val uMsgCompleted: Long? = null,
|
||||||
@SerialId(4) val vMsgInfos: List<MsgInfo>? = null
|
@JceId(4) val vMsgInfos: List<MsgInfo>? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgType0x210(
|
internal class MsgType0x210(
|
||||||
@SerialId(0) val uSubMsgType: Long,
|
@JceId(0) val uSubMsgType: Long,
|
||||||
@SerialId(1) val stMsgInfo0x2: MsgType0x210SubMsgType0x2? = null,
|
@JceId(1) val stMsgInfo0x2: MsgType0x210SubMsgType0x2? = null,
|
||||||
@SerialId(3) val stMsgInfo0xa: MsgType0x210SubMsgType0xa? = null,
|
@JceId(3) val stMsgInfo0xa: MsgType0x210SubMsgType0xa? = null,
|
||||||
@SerialId(4) val stMsgInfo0xe: MsgType0x210SubMsgType0xe? = null,
|
@JceId(4) val stMsgInfo0xe: MsgType0x210SubMsgType0xe? = null,
|
||||||
@SerialId(5) val stMsgInfo0x13: MsgType0x210SubMsgType0x13? = null,
|
@JceId(5) val stMsgInfo0x13: MsgType0x210SubMsgType0x13? = null,
|
||||||
@SerialId(6) val stMsgInfo0x17: MsgType0x210SubMsgType0x17? = null,
|
@JceId(6) val stMsgInfo0x17: MsgType0x210SubMsgType0x17? = null,
|
||||||
@SerialId(7) val stMsgInfo0x20: MsgType0x210SubMsgType0x20? = null,
|
@JceId(7) val stMsgInfo0x20: MsgType0x210SubMsgType0x20? = null,
|
||||||
@SerialId(8) val stMsgInfo0x1d: MsgType0x210SubMsgType0x1d? = null,
|
@JceId(8) val stMsgInfo0x1d: MsgType0x210SubMsgType0x1d? = null,
|
||||||
@SerialId(9) val stMsgInfo0x24: MsgType0x210SubMsgType0x24? = null,
|
@JceId(9) val stMsgInfo0x24: MsgType0x210SubMsgType0x24? = null,
|
||||||
@SerialId(10) val vProtobuf: ByteArray? = null
|
@JceId(10) val vProtobuf: ByteArray? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgType0x210SubMsgType0x13(
|
internal class MsgType0x210SubMsgType0x13(
|
||||||
@SerialId(0) val uint32SrcAppId: Long? = null,
|
@JceId(0) val uint32SrcAppId: Long? = null,
|
||||||
@SerialId(1) val uint32SrcInstId: Long? = null,
|
@JceId(1) val uint32SrcInstId: Long? = null,
|
||||||
@SerialId(2) val uint32DstAppId: Long? = null,
|
@JceId(2) val uint32DstAppId: Long? = null,
|
||||||
@SerialId(3) val uint32DstInstId: Long? = null,
|
@JceId(3) val uint32DstInstId: Long? = null,
|
||||||
@SerialId(4) val uint64DstUin: Long? = null,
|
@JceId(4) val uint64DstUin: Long? = null,
|
||||||
@SerialId(5) val uint64Sessionid: Long? = null,
|
@JceId(5) val uint64Sessionid: Long? = null,
|
||||||
@SerialId(6) val uint32Size: Long? = null,
|
@JceId(6) val uint32Size: Long? = null,
|
||||||
@SerialId(7) val uint32Index: Long? = null,
|
@JceId(7) val uint32Index: Long? = null,
|
||||||
@SerialId(8) val uint32Type: Long? = null,
|
@JceId(8) val uint32Type: Long? = null,
|
||||||
@SerialId(9) val buf: ByteArray? = null
|
@JceId(9) val buf: ByteArray? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgType0x210SubMsgType0x17(
|
internal class MsgType0x210SubMsgType0x17(
|
||||||
@SerialId(0) val dwOpType: Long? = null,
|
@JceId(0) val dwOpType: Long? = null,
|
||||||
@SerialId(1) val stAddGroup: AddGroup? = null,
|
@JceId(1) val stAddGroup: AddGroup? = null,
|
||||||
@SerialId(2) val stDelGroup: DelGroup? = null,
|
@JceId(2) val stDelGroup: DelGroup? = null,
|
||||||
@SerialId(3) val stModGroupName: ModGroupName? = null,
|
@JceId(3) val stModGroupName: ModGroupName? = null,
|
||||||
@SerialId(4) val stModGroupSort: ModGroupSort? = null,
|
@JceId(4) val stModGroupSort: ModGroupSort? = null,
|
||||||
@SerialId(5) val stModFriendGroup: ModFriendGroup? = null
|
@JceId(5) val stModFriendGroup: ModFriendGroup? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class AddGroup(
|
internal class AddGroup(
|
||||||
@SerialId(0) val dwGroupID: Long? = null,
|
@JceId(0) val dwGroupID: Long? = null,
|
||||||
@SerialId(1) val dwSortID: Long? = null,
|
@JceId(1) val dwSortID: Long? = null,
|
||||||
@SerialId(2) val groupName: String? = ""
|
@JceId(2) val groupName: String? = ""
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class DelGroup(
|
internal class DelGroup(
|
||||||
@SerialId(0) val dwGroupID: Long? = null
|
@JceId(0) val dwGroupID: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ModFriendGroup(
|
internal class ModFriendGroup(
|
||||||
@SerialId(0) val vMsgFrdGroup: List<FriendGroup>? = null
|
@JceId(0) val vMsgFrdGroup: List<FriendGroup>? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class FriendGroup(
|
internal class FriendGroup(
|
||||||
@SerialId(0) val dwFuin: Long? = null,
|
@JceId(0) val dwFuin: Long? = null,
|
||||||
@SerialId(1) val vOldGroupID: List<Long>? = null,
|
@JceId(1) val vOldGroupID: List<Long>? = null,
|
||||||
@SerialId(2) val vNewGroupID: List<Long>? = null
|
@JceId(2) val vNewGroupID: List<Long>? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ModGroupName(
|
internal class ModGroupName(
|
||||||
@SerialId(0) val dwGroupID: Long? = null,
|
@JceId(0) val dwGroupID: Long? = null,
|
||||||
@SerialId(1) val groupName: String? = ""
|
@JceId(1) val groupName: String? = ""
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ModGroupSort(
|
internal class ModGroupSort(
|
||||||
@SerialId(0) val vMsgGroupSort: List<GroupSort>? = null
|
@JceId(0) val vMsgGroupSort: List<GroupSort>? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class GroupSort(
|
internal class GroupSort(
|
||||||
@SerialId(0) val dwGroupID: Long? = null,
|
@JceId(0) val dwGroupID: Long? = null,
|
||||||
@SerialId(1) val dwSortID: Long? = null
|
@JceId(1) val dwSortID: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgType0x210SubMsgType0x1d(
|
internal class MsgType0x210SubMsgType0x1d(
|
||||||
@SerialId(0) val dwOpType: Long? = null,
|
@JceId(0) val dwOpType: Long? = null,
|
||||||
@SerialId(1) val dwUin: Long? = null,
|
@JceId(1) val dwUin: Long? = null,
|
||||||
@SerialId(2) val dwID: Long? = null,
|
@JceId(2) val dwID: Long? = null,
|
||||||
@SerialId(3) val value: String? = ""
|
@JceId(3) val value: String? = ""
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgType0x210SubMsgType0x2(
|
internal class MsgType0x210SubMsgType0x2(
|
||||||
@SerialId(0) val uSrcAppId: Long? = null,
|
@JceId(0) val uSrcAppId: Long? = null,
|
||||||
@SerialId(1) val uSrcInstId: Long? = null,
|
@JceId(1) val uSrcInstId: Long? = null,
|
||||||
@SerialId(2) val uDstAppId: Long? = null,
|
@JceId(2) val uDstAppId: Long? = null,
|
||||||
@SerialId(3) val uDstInstId: Long? = null,
|
@JceId(3) val uDstInstId: Long? = null,
|
||||||
@SerialId(4) val uDstUin: Long? = null,
|
@JceId(4) val uDstUin: Long? = null,
|
||||||
@SerialId(5) val fileName: ByteArray? = null,
|
@JceId(5) val fileName: ByteArray? = null,
|
||||||
@SerialId(6) val fileIndex: ByteArray? = null,
|
@JceId(6) val fileIndex: ByteArray? = null,
|
||||||
@SerialId(7) val fileMd5: ByteArray? = null,
|
@JceId(7) val fileMd5: ByteArray? = null,
|
||||||
@SerialId(8) val fileKey: ByteArray? = null,
|
@JceId(8) val fileKey: ByteArray? = null,
|
||||||
@SerialId(9) val uServerIp: Long? = null,
|
@JceId(9) val uServerIp: Long? = null,
|
||||||
@SerialId(10) val uServerPort: Long? = null,
|
@JceId(10) val uServerPort: Long? = null,
|
||||||
@SerialId(11) val fileLen: Long? = null,
|
@JceId(11) val fileLen: Long? = null,
|
||||||
@SerialId(12) val sessionId: Long? = null,
|
@JceId(12) val sessionId: Long? = null,
|
||||||
@SerialId(13) val originfileMd5: ByteArray? = null,
|
@JceId(13) val originfileMd5: ByteArray? = null,
|
||||||
@SerialId(14) val uOriginfiletype: Long? = null,
|
@JceId(14) val uOriginfiletype: Long? = null,
|
||||||
@SerialId(15) val uSeq: Long? = null
|
@JceId(15) val uSeq: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgType0x210SubMsgType0x20(
|
internal class MsgType0x210SubMsgType0x20(
|
||||||
@SerialId(0) val dwOpType: Long? = null,
|
@JceId(0) val dwOpType: Long? = null,
|
||||||
@SerialId(1) val dwType: Long? = null,
|
@JceId(1) val dwType: Long? = null,
|
||||||
@SerialId(2) val dwUin: Long? = null,
|
@JceId(2) val dwUin: Long? = null,
|
||||||
@SerialId(3) val remaek: String? = ""
|
@JceId(3) val remaek: String? = ""
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgType0x210SubMsgType0x24(
|
internal class MsgType0x210SubMsgType0x24(
|
||||||
@SerialId(0) val vPluginNumList: List<PluginNum>? = null
|
@JceId(0) val vPluginNumList: List<PluginNum>? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class PluginNum(
|
internal class PluginNum(
|
||||||
@SerialId(0) val dwID: Long? = null,
|
@JceId(0) val dwID: Long? = null,
|
||||||
@SerialId(1) val dwNUm: Long? = null,
|
@JceId(1) val dwNUm: Long? = null,
|
||||||
@SerialId(2) val flag: Byte? = null
|
@JceId(2) val flag: Byte? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgType0x210SubMsgType0xa(
|
internal class MsgType0x210SubMsgType0xa(
|
||||||
@SerialId(0) val uSrcAppId: Long? = null,
|
@JceId(0) val uSrcAppId: Long? = null,
|
||||||
@SerialId(1) val uSrcInstId: Long? = null,
|
@JceId(1) val uSrcInstId: Long? = null,
|
||||||
@SerialId(2) val uDstAppId: Long? = null,
|
@JceId(2) val uDstAppId: Long? = null,
|
||||||
@SerialId(3) val uDstInstId: Long? = null,
|
@JceId(3) val uDstInstId: Long? = null,
|
||||||
@SerialId(4) val uDstUin: Long? = null,
|
@JceId(4) val uDstUin: Long? = null,
|
||||||
@SerialId(5) val uType: Long? = null,
|
@JceId(5) val uType: Long? = null,
|
||||||
@SerialId(6) val uServerIp: Long? = null,
|
@JceId(6) val uServerIp: Long? = null,
|
||||||
@SerialId(7) val uServerPort: Long? = null,
|
@JceId(7) val uServerPort: Long? = null,
|
||||||
@SerialId(8) val vUrlNotify: ByteArray? = null,
|
@JceId(8) val vUrlNotify: ByteArray? = null,
|
||||||
@SerialId(9) val vTokenKey: ByteArray? = null,
|
@JceId(9) val vTokenKey: ByteArray? = null,
|
||||||
@SerialId(10) val uFileLen: Long? = null,
|
@JceId(10) val uFileLen: Long? = null,
|
||||||
@SerialId(11) val fileName: ByteArray? = null,
|
@JceId(11) val fileName: ByteArray? = null,
|
||||||
@SerialId(12) val vMd5: ByteArray? = null,
|
@JceId(12) val vMd5: ByteArray? = null,
|
||||||
@SerialId(13) val sessionId: Long? = null,
|
@JceId(13) val sessionId: Long? = null,
|
||||||
@SerialId(14) val originfileMd5: ByteArray? = null,
|
@JceId(14) val originfileMd5: ByteArray? = null,
|
||||||
@SerialId(15) val uOriginfiletype: Long? = null,
|
@JceId(15) val uOriginfiletype: Long? = null,
|
||||||
@SerialId(16) val uSeq: Long? = null
|
@JceId(16) val uSeq: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgType0x210SubMsgType0xe(
|
internal class MsgType0x210SubMsgType0xe(
|
||||||
@SerialId(0) val uint32SrcAppId: Long? = null,
|
@JceId(0) val uint32SrcAppId: Long? = null,
|
||||||
@SerialId(1) val uint32SrcInstId: Long? = null,
|
@JceId(1) val uint32SrcInstId: Long? = null,
|
||||||
@SerialId(2) val uint32DstAppId: Long? = null,
|
@JceId(2) val uint32DstAppId: Long? = null,
|
||||||
@SerialId(3) val uint32DstInstId: Long? = null,
|
@JceId(3) val uint32DstInstId: Long? = null,
|
||||||
@SerialId(4) val uint64DstUin: Long? = null,
|
@JceId(4) val uint64DstUin: Long? = null,
|
||||||
@SerialId(5) val uint64Sessionid: Long? = null,
|
@JceId(5) val uint64Sessionid: Long? = null,
|
||||||
@SerialId(6) val uint32Operate: Long? = null,
|
@JceId(6) val uint32Operate: Long? = null,
|
||||||
@SerialId(7) val uint32Seq: Long? = null,
|
@JceId(7) val uint32Seq: Long? = null,
|
||||||
@SerialId(8) val uint32Code: Long? = null,
|
@JceId(8) val uint32Code: Long? = null,
|
||||||
@SerialId(9) val msg: String? = ""
|
@JceId(9) val msg: String? = ""
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
}
|
}
|
@ -9,72 +9,72 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import net.mamoe.mirai.data.Packet
|
import net.mamoe.mirai.data.Packet
|
||||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
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
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||||
|
|
||||||
@Suppress("ArrayInDataClass")
|
@Suppress("ArrayInDataClass")
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class RequestPushNotify(
|
internal data class RequestPushNotify(
|
||||||
@SerialId(0) val uin: Long? = 0L,
|
@JceId(0) val uin: Long? = 0L,
|
||||||
@SerialId(1) val ctype: Byte = 0,
|
@JceId(1) val ctype: Byte = 0,
|
||||||
@SerialId(2) val strService: String?,
|
@JceId(2) val strService: String?,
|
||||||
@SerialId(3) val strCmd: String?,
|
@JceId(3) val strCmd: String?,
|
||||||
@SerialId(4) val vNotifyCookie: ByteArray? = EMPTY_BYTE_ARRAY,
|
@JceId(4) val vNotifyCookie: ByteArray? = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val usMsgType: Int?,
|
@JceId(5) val usMsgType: Int?,
|
||||||
@SerialId(6) val wUserActive: Int?,
|
@JceId(6) val wUserActive: Int?,
|
||||||
@SerialId(7) val wGeneralFlag: Int?,
|
@JceId(7) val wGeneralFlag: Int?,
|
||||||
@SerialId(8) val bindedUin: Long?,
|
@JceId(8) val bindedUin: Long?,
|
||||||
@SerialId(9) val stMsgInfo: MsgInfo?,
|
@JceId(9) val stMsgInfo: MsgInfo?,
|
||||||
@SerialId(10) val msgCtrlBuf: String?,
|
@JceId(10) val msgCtrlBuf: String?,
|
||||||
@SerialId(11) val serverBuf: ByteArray?,
|
@JceId(11) val serverBuf: ByteArray?,
|
||||||
@SerialId(12) val pingFlag: Long?,
|
@JceId(12) val pingFlag: Long?,
|
||||||
@SerialId(13) val svrip: Int?
|
@JceId(13) val svrip: Int?
|
||||||
) : JceStruct, Packet
|
) : JceStruct, Packet
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgInfo(
|
internal class MsgInfo(
|
||||||
@SerialId(0) val lFromUin: Long? = 0L,
|
@JceId(0) val lFromUin: Long? = 0L,
|
||||||
@SerialId(1) val uMsgTime: Long? = 0L,
|
@JceId(1) val uMsgTime: Long? = 0L,
|
||||||
@SerialId(2) val shMsgType: Short,
|
@JceId(2) val shMsgType: Short,
|
||||||
@SerialId(3) val shMsgSeq: Short,
|
@JceId(3) val shMsgSeq: Short,
|
||||||
@SerialId(4) val strMsg: String?,
|
@JceId(4) val strMsg: String?,
|
||||||
@SerialId(5) val uRealMsgTime: Int?,
|
@JceId(5) val uRealMsgTime: Int?,
|
||||||
@SerialId(6) val vMsg: ByteArray?,
|
@JceId(6) val vMsg: ByteArray?,
|
||||||
@SerialId(7) val uAppShareID: Long?,
|
@JceId(7) val uAppShareID: Long?,
|
||||||
@SerialId(8) val vMsgCookies: ByteArray? = EMPTY_BYTE_ARRAY,
|
@JceId(8) val vMsgCookies: ByteArray? = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(9) val vAppShareCookie: ByteArray? = EMPTY_BYTE_ARRAY,
|
@JceId(9) val vAppShareCookie: ByteArray? = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(10) val lMsgUid: Long?,
|
@JceId(10) val lMsgUid: Long?,
|
||||||
@SerialId(11) val lLastChangeTime: Long?,
|
@JceId(11) val lLastChangeTime: Long?,
|
||||||
@SerialId(12) val vCPicInfo: List<CPicInfo>?,
|
@JceId(12) val vCPicInfo: List<CPicInfo>?,
|
||||||
@SerialId(13) val stShareData: ShareData?,
|
@JceId(13) val stShareData: ShareData?,
|
||||||
@SerialId(14) val lFromInstId: Long?,
|
@JceId(14) val lFromInstId: Long?,
|
||||||
@SerialId(15) val vRemarkOfSender: ByteArray?,
|
@JceId(15) val vRemarkOfSender: ByteArray?,
|
||||||
@SerialId(16) val strFromMobile: String?,
|
@JceId(16) val strFromMobile: String?,
|
||||||
@SerialId(17) val strFromName: String?,
|
@JceId(17) val strFromName: String?,
|
||||||
@SerialId(18) val vNickName: List<String>?//,
|
@JceId(18) val vNickName: List<String>?//,
|
||||||
//@SerialId(19) val stC2CTmpMsgHead: TempMsgHead?
|
//@SerialId(19) val stC2CTmpMsgHead: TempMsgHead?
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ShareData(
|
internal class ShareData(
|
||||||
@SerialId(0) val pkgname: String = "",
|
@JceId(0) val pkgname: String = "",
|
||||||
@SerialId(1) val msgtail: String = "",
|
@JceId(1) val msgtail: String = "",
|
||||||
@SerialId(2) val picurl: String = "",
|
@JceId(2) val picurl: String = "",
|
||||||
@SerialId(3) val url: String = ""
|
@JceId(3) val url: String = ""
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class TempMsgHead(
|
internal class TempMsgHead(
|
||||||
@SerialId(0) val c2c_type: Int? = 0,
|
@JceId(0) val c2c_type: Int? = 0,
|
||||||
@SerialId(1) val serviceType: Int? = 0
|
@JceId(1) val serviceType: Int? = 0
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class CPicInfo(
|
internal class CPicInfo(
|
||||||
@SerialId(0) val vPath: ByteArray = EMPTY_BYTE_ARRAY,
|
@JceId(0) val vPath: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(1) val vHost: ByteArray? = EMPTY_BYTE_ARRAY
|
@JceId(1) val vHost: ByteArray? = EMPTY_BYTE_ARRAY
|
||||||
) : JceStruct
|
) : JceStruct
|
@ -9,38 +9,38 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
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
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||||
|
|
||||||
private val EMPTY_MAP = mapOf<String, String>()
|
private val EMPTY_MAP = mapOf<String, String>()
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class RequestPacket(
|
internal class RequestPacket(
|
||||||
@SerialId(1) val iVersion: Short = 3,
|
@JceId(1) val iVersion: Short? = 3,
|
||||||
@SerialId(2) val cPacketType: Byte = 0,
|
@JceId(2) val cPacketType: Byte = 0,
|
||||||
@SerialId(3) val iMessageType: Int = 0,
|
@JceId(3) val iMessageType: Int = 0,
|
||||||
@SerialId(4) val iRequestId: Int,
|
@JceId(4) val iRequestId: Int,
|
||||||
@SerialId(5) val sServantName: String = "",
|
@JceId(5) val sServantName: String = "",
|
||||||
@SerialId(6) val sFuncName: String = "",
|
@JceId(6) val sFuncName: String = "",
|
||||||
@SerialId(7) val sBuffer: ByteArray = EMPTY_BYTE_ARRAY,
|
@JceId(7) val sBuffer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(8) val iTimeout: Int? = 0,
|
@JceId(8) val iTimeout: Int? = 0,
|
||||||
@SerialId(9) val context: Map<String, String>? = EMPTY_MAP,
|
@JceId(9) val context: Map<String, String>? = EMPTY_MAP,
|
||||||
@SerialId(10) val status: Map<String, String>? = EMPTY_MAP
|
@JceId(10) val status: Map<String, String>? = EMPTY_MAP
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class RequestDataVersion3(
|
internal class RequestDataVersion3(
|
||||||
@SerialId(0) val map: Map<String, ByteArray> // 注意: ByteArray 不能直接放序列化的 JceStruct!! 要放类似 RequestDataStructSvcReqRegister 的
|
@JceId(0) val map: Map<String, ByteArray> // 注意: ByteArray 不能直接放序列化的 JceStruct!! 要放类似 RequestDataStructSvcReqRegister 的
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class RequestDataVersion2(
|
internal class RequestDataVersion2(
|
||||||
@SerialId(0) val map: Map<String, Map<String, ByteArray>>
|
@JceId(0) val map: Map<String, Map<String, ByteArray>>
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class RequestDataStructSvcReqRegister(
|
internal class RequestDataStructSvcReqRegister(
|
||||||
@SerialId(0) val struct: SvcReqRegister
|
@JceId(0) val struct: SvcReqRegister
|
||||||
) : JceStruct
|
) : JceStruct
|
@ -9,14 +9,15 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||||
|
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class RequestPushForceOffline(
|
internal class RequestPushForceOffline(
|
||||||
@SerialId(0) val uin: Long,
|
@JceId(0) val uin: Long,
|
||||||
@SerialId(1) val title: String? = "",
|
@JceId(1) val title: String? = "",
|
||||||
@SerialId(2) val tips: String? = "",
|
@JceId(2) val tips: String? = "",
|
||||||
@SerialId(3) val sameDevice: Byte? = null
|
@JceId(3) val sameDevice: Byte? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
@ -9,47 +9,47 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||||
|
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class SvcReqRegister(
|
internal class SvcReqRegister(
|
||||||
@SerialId(0) val lUin: Long = 0L,
|
@JceId(0) val lUin: Long = 0L,
|
||||||
@SerialId(1) val lBid: Long = 0L,
|
@JceId(1) val lBid: Long = 0L,
|
||||||
@SerialId(2) val cConnType: Byte = 0,
|
@JceId(2) val cConnType: Byte = 0,
|
||||||
@SerialId(3) val sOther: String = "",
|
@JceId(3) val sOther: String = "",
|
||||||
@SerialId(4) val iStatus: Int = 11,
|
@JceId(4) val iStatus: Int = 11,
|
||||||
@SerialId(5) val bOnlinePush: Byte = 0,
|
@JceId(5) val bOnlinePush: Byte = 0,
|
||||||
@SerialId(6) val bIsOnline: Byte = 0,
|
@JceId(6) val bIsOnline: Byte = 0,
|
||||||
@SerialId(7) val bIsShowOnline: Byte = 0,
|
@JceId(7) val bIsShowOnline: Byte = 0,
|
||||||
@SerialId(8) val bKikPC: Byte = 0,
|
@JceId(8) val bKikPC: Byte = 0,
|
||||||
@SerialId(9) val bKikWeak: Byte = 0,
|
@JceId(9) val bKikWeak: Byte = 0,
|
||||||
@SerialId(10) val timeStamp: Long = 0L,
|
@JceId(10) val timeStamp: Long = 0L,
|
||||||
@SerialId(11) val iOSVersion: Long = 0L,
|
@JceId(11) val iOSVersion: Long = 0L,
|
||||||
@SerialId(12) val cNetType: Byte = 0,
|
@JceId(12) val cNetType: Byte = 0,
|
||||||
@SerialId(13) val sBuildVer: String? = "",
|
@JceId(13) val sBuildVer: String? = "",
|
||||||
@SerialId(14) val bRegType: Byte = 0,
|
@JceId(14) val bRegType: Byte = 0,
|
||||||
@SerialId(15) val vecDevParam: ByteArray? = null,
|
@JceId(15) val vecDevParam: ByteArray? = null,
|
||||||
@SerialId(16) val vecGuid: ByteArray? = null,
|
@JceId(16) val vecGuid: ByteArray? = null,
|
||||||
@SerialId(17) val iLocaleID: Int = 2052,
|
@JceId(17) val iLocaleID: Int = 2052,
|
||||||
@SerialId(18) val bSlientPush: Byte = 0,
|
@JceId(18) val bSlientPush: Byte = 0,
|
||||||
@SerialId(19) val strDevName: String? = null,
|
@JceId(19) val strDevName: String? = null,
|
||||||
@SerialId(20) val strDevType: String? = null,
|
@JceId(20) val strDevType: String? = null,
|
||||||
@SerialId(21) val strOSVer: String? = null,
|
@JceId(21) val strOSVer: String? = null,
|
||||||
@SerialId(22) val bOpenPush: Byte = 1,
|
@JceId(22) val bOpenPush: Byte = 1,
|
||||||
@SerialId(23) val iLargeSeq: Long = 0L,
|
@JceId(23) val iLargeSeq: Long = 0L,
|
||||||
@SerialId(24) val iLastWatchStartTime: Long = 0L,
|
@JceId(24) val iLastWatchStartTime: Long = 0L,
|
||||||
@SerialId(26) val uOldSSOIp: Long = 0L,
|
@JceId(26) val uOldSSOIp: Long = 0L,
|
||||||
@SerialId(27) val uNewSSOIp: Long = 0L,
|
@JceId(27) val uNewSSOIp: Long = 0L,
|
||||||
@SerialId(28) val sChannelNo: String? = null,
|
@JceId(28) val sChannelNo: String? = null,
|
||||||
@SerialId(29) val lCpId: Long = 0L,
|
@JceId(29) val lCpId: Long = 0L,
|
||||||
@SerialId(30) val strVendorName: String? = null,
|
@JceId(30) val strVendorName: String? = null,
|
||||||
@SerialId(31) val strVendorOSName: String? = null,
|
@JceId(31) val strVendorOSName: String? = null,
|
||||||
@SerialId(32) val strIOSIdfa: String? = null,
|
@JceId(32) val strIOSIdfa: String? = null,
|
||||||
@SerialId(33) val bytes_0x769_reqbody: ByteArray? = null,
|
@JceId(33) val bytes_0x769_reqbody: ByteArray? = null,
|
||||||
@SerialId(34) val bIsSetStatus: Byte = 0,
|
@JceId(34) val bIsSetStatus: Byte = 0,
|
||||||
@SerialId(35) val vecServerBuf: ByteArray? = null,
|
@JceId(35) val vecServerBuf: ByteArray? = null,
|
||||||
@SerialId(36) val bSetMute: Byte = 0
|
@JceId(36) val bSetMute: Byte = 0
|
||||||
// @SerialId(25) var vecBindUin: ArrayList<*>? = null // ?? 未知泛型
|
// @SerialId(25) var vecBindUin: ArrayList<*>? = null // ?? 未知泛型
|
||||||
) : JceStruct
|
) : JceStruct
|
@ -9,183 +9,183 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||||
|
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class GetTroopListReqV2Simplify(
|
internal class GetTroopListReqV2Simplify(
|
||||||
@SerialId(0) val uin: Long,
|
@JceId(0) val uin: Long,
|
||||||
@SerialId(1) val getMSFMsgFlag: Byte? = null,
|
@JceId(1) val getMSFMsgFlag: Byte? = null,
|
||||||
@SerialId(2) val vecCookies: ByteArray? = null,
|
@JceId(2) val vecCookies: ByteArray? = null,
|
||||||
@SerialId(3) val vecGroupInfo: List<StTroopNumSimplify>? = null,
|
@JceId(3) val vecGroupInfo: List<StTroopNumSimplify>? = null,
|
||||||
@SerialId(4) val groupFlagExt: Byte? = null,
|
@JceId(4) val groupFlagExt: Byte? = null,
|
||||||
@SerialId(5) val shVersion: Int? = null,
|
@JceId(5) val shVersion: Int? = null,
|
||||||
@SerialId(6) val dwCompanyId: Long? = null,
|
@JceId(6) val dwCompanyId: Long? = null,
|
||||||
@SerialId(7) val versionNum: Long? = null,
|
@JceId(7) val versionNum: Long? = null,
|
||||||
@SerialId(8) val getLongGroupName: Byte? = null
|
@JceId(8) val getLongGroupName: Byte? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class StTroopNumSimplify(
|
internal class StTroopNumSimplify(
|
||||||
@SerialId(0) val groupCode: Long,
|
@JceId(0) val groupCode: Long,
|
||||||
@SerialId(1) val dwGroupInfoSeq: Long? = null,
|
@JceId(1) val dwGroupInfoSeq: Long? = null,
|
||||||
@SerialId(2) val dwGroupFlagExt: Long? = null,
|
@JceId(2) val dwGroupFlagExt: Long? = null,
|
||||||
@SerialId(3) val dwGroupRankSeq: Long? = null
|
@JceId(3) val dwGroupRankSeq: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class GetTroopListRespV2(
|
internal class GetTroopListRespV2(
|
||||||
@SerialId(0) val uin: Long,
|
@JceId(0) val uin: Long,
|
||||||
@SerialId(1) val troopCount: Short,
|
@JceId(1) val troopCount: Short,
|
||||||
@SerialId(2) val result: Int,
|
@JceId(2) val result: Int,
|
||||||
@SerialId(3) val errorCode: Short? = null,
|
@JceId(3) val errorCode: Short? = null,
|
||||||
@SerialId(4) val vecCookies: ByteArray? = null,
|
@JceId(4) val vecCookies: ByteArray? = null,
|
||||||
@SerialId(5) val vecTroopList: List<StTroopNum>? = null,
|
@JceId(5) val vecTroopList: List<StTroopNum>? = null,
|
||||||
@SerialId(6) val vecTroopListDel: List<StTroopNum>? = null,
|
@JceId(6) val vecTroopListDel: List<StTroopNum>? = null,
|
||||||
@SerialId(7) val vecTroopRank: List<StGroupRankInfo>? = null,
|
@JceId(7) val vecTroopRank: List<StGroupRankInfo>? = null,
|
||||||
@SerialId(8) val vecFavGroup: List<StFavoriteGroup>? = null,
|
@JceId(8) val vecFavGroup: List<StFavoriteGroup>? = null,
|
||||||
@SerialId(9) val vecTroopListExt: List<StTroopNum>? = null
|
@JceId(9) val vecTroopListExt: List<StTroopNum>? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class StTroopNum(
|
internal class StTroopNum(
|
||||||
@SerialId(0) val groupUin: Long,
|
@JceId(0) val groupUin: Long,
|
||||||
@SerialId(1) val groupCode: Long,
|
@JceId(1) val groupCode: Long,
|
||||||
@SerialId(2) val flag: Byte? = null,
|
@JceId(2) val flag: Byte? = null,
|
||||||
@SerialId(3) val dwGroupInfoSeq: Long? = null,
|
@JceId(3) val dwGroupInfoSeq: Long? = null,
|
||||||
@SerialId(4) val groupName: String = "",
|
@JceId(4) val groupName: String = "",
|
||||||
@SerialId(5) val groupMemo: String = "",
|
@JceId(5) val groupMemo: String = "",
|
||||||
@SerialId(6) val dwGroupFlagExt: Long? = null,
|
@JceId(6) val dwGroupFlagExt: Long? = null,
|
||||||
@SerialId(7) val dwGroupRankSeq: Long? = null,
|
@JceId(7) val dwGroupRankSeq: Long? = null,
|
||||||
@SerialId(8) val dwCertificationType: Long? = null,
|
@JceId(8) val dwCertificationType: Long? = null,
|
||||||
@SerialId(9) val dwShutUpTimestamp: Long? = null,
|
@JceId(9) val dwShutUpTimestamp: Long? = null,
|
||||||
@SerialId(10) val dwMyShutUpTimestamp: Long? = null,
|
@JceId(10) val dwMyShutUpTimestamp: Long? = null,
|
||||||
@SerialId(11) val dwCmdUinUinFlag: Long? = null,
|
@JceId(11) val dwCmdUinUinFlag: Long? = null,
|
||||||
@SerialId(12) val dwAdditionalFlag: Long? = null,
|
@JceId(12) val dwAdditionalFlag: Long? = null,
|
||||||
@SerialId(13) val dwGroupTypeFlag: Long? = null,
|
@JceId(13) val dwGroupTypeFlag: Long? = null,
|
||||||
@SerialId(14) val dwGroupSecType: Long? = null,
|
@JceId(14) val dwGroupSecType: Long? = null,
|
||||||
@SerialId(15) val dwGroupSecTypeInfo: Long? = null,
|
@JceId(15) val dwGroupSecTypeInfo: Long? = null,
|
||||||
@SerialId(16) val dwGroupClassExt: Long? = null,
|
@JceId(16) val dwGroupClassExt: Long? = null,
|
||||||
@SerialId(17) val dwAppPrivilegeFlag: Long? = null,
|
@JceId(17) val dwAppPrivilegeFlag: Long? = null,
|
||||||
@SerialId(18) val dwSubscriptionUin: Long? = null,
|
@JceId(18) val dwSubscriptionUin: Long? = null,
|
||||||
@SerialId(19) val dwMemberNum: Long? = null,
|
@JceId(19) val dwMemberNum: Long? = null,
|
||||||
@SerialId(20) val dwMemberNumSeq: Long? = null,
|
@JceId(20) val dwMemberNumSeq: Long? = null,
|
||||||
@SerialId(21) val dwMemberCardSeq: Long? = null,
|
@JceId(21) val dwMemberCardSeq: Long? = null,
|
||||||
@SerialId(22) val dwGroupFlagExt3: Long? = null,
|
@JceId(22) val dwGroupFlagExt3: Long? = null,
|
||||||
@SerialId(23) val dwGroupOwnerUin: Long,
|
@JceId(23) val dwGroupOwnerUin: Long,
|
||||||
@SerialId(24) val isConfGroup: Byte? = null,
|
@JceId(24) val isConfGroup: Byte? = null,
|
||||||
@SerialId(25) val isModifyConfGroupFace: Byte? = null,
|
@JceId(25) val isModifyConfGroupFace: Byte? = null,
|
||||||
@SerialId(26) val isModifyConfGroupName: Byte? = null,
|
@JceId(26) val isModifyConfGroupName: Byte? = null,
|
||||||
@SerialId(27) val dwCmduinJoinTime: Long? = null,
|
@JceId(27) val dwCmduinJoinTime: Long? = null,
|
||||||
@SerialId(28) val ulCompanyId: Long? = null,
|
@JceId(28) val ulCompanyId: Long? = null,
|
||||||
@SerialId(29) val dwMaxGroupMemberNum: Long? = null,
|
@JceId(29) val dwMaxGroupMemberNum: Long? = null,
|
||||||
@SerialId(30) val dwCmdUinGroupMask: Long? = null,
|
@JceId(30) val dwCmdUinGroupMask: Long? = null,
|
||||||
@SerialId(31) val udwHLGuildAppid: Long? = null,
|
@JceId(31) val udwHLGuildAppid: Long? = null,
|
||||||
@SerialId(32) val udwHLGuildSubType: Long? = null,
|
@JceId(32) val udwHLGuildSubType: Long? = null,
|
||||||
@SerialId(33) val udwCmdUinRingtoneID: Long? = null,
|
@JceId(33) val udwCmdUinRingtoneID: Long? = null,
|
||||||
@SerialId(34) val udwCmdUinFlagEx2: Long? = null
|
@JceId(34) val udwCmdUinFlagEx2: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class StGroupRankInfo(
|
internal class StGroupRankInfo(
|
||||||
@SerialId(0) val dwGroupCode: Long,
|
@JceId(0) val dwGroupCode: Long,
|
||||||
@SerialId(1) val groupRankSysFlag: Byte? = null,
|
@JceId(1) val groupRankSysFlag: Byte? = null,
|
||||||
@SerialId(2) val groupRankUserFlag: Byte? = null,
|
@JceId(2) val groupRankUserFlag: Byte? = null,
|
||||||
@SerialId(3) val vecRankMap: List<StLevelRankPair>? = null,
|
@JceId(3) val vecRankMap: List<StLevelRankPair>? = null,
|
||||||
@SerialId(4) val dwGroupRankSeq: Long? = null,
|
@JceId(4) val dwGroupRankSeq: Long? = null,
|
||||||
@SerialId(5) val ownerName: String? = "",
|
@JceId(5) val ownerName: String? = "",
|
||||||
@SerialId(6) val adminName: String? = "",
|
@JceId(6) val adminName: String? = "",
|
||||||
@SerialId(7) val dwOfficeMode: Long? = null
|
@JceId(7) val dwOfficeMode: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class StFavoriteGroup(
|
internal class StFavoriteGroup(
|
||||||
@SerialId(0) val dwGroupCode: Long,
|
@JceId(0) val dwGroupCode: Long,
|
||||||
@SerialId(1) val dwTimestamp: Long? = null,
|
@JceId(1) val dwTimestamp: Long? = null,
|
||||||
@SerialId(2) val dwSnsFlag: Long? = 1L,
|
@JceId(2) val dwSnsFlag: Long? = 1L,
|
||||||
@SerialId(3) val dwOpenTimestamp: Long? = null
|
@JceId(3) val dwOpenTimestamp: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class StLevelRankPair(
|
internal class StLevelRankPair(
|
||||||
@SerialId(0) val dwLevel: Long? = null,
|
@JceId(0) val dwLevel: Long? = null,
|
||||||
@SerialId(1) val rank: String? = ""
|
@JceId(1) val rank: String? = ""
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class GetTroopMemberListReq(
|
internal class GetTroopMemberListReq(
|
||||||
@SerialId(0) val uin: Long,
|
@JceId(0) val uin: Long,
|
||||||
@SerialId(1) val groupCode: Long,
|
@JceId(1) val groupCode: Long,
|
||||||
@SerialId(2) val nextUin: Long,
|
@JceId(2) val nextUin: Long,
|
||||||
@SerialId(3) val groupUin: Long,
|
@JceId(3) val groupUin: Long,
|
||||||
@SerialId(4) val version: Long? = null,
|
@JceId(4) val version: Long? = null,
|
||||||
@SerialId(5) val reqType: Long? = null,
|
@JceId(5) val reqType: Long? = null,
|
||||||
@SerialId(6) val getListAppointTime: Long? = null,
|
@JceId(6) val getListAppointTime: Long? = null,
|
||||||
@SerialId(7) val richCardNameVer: Byte? = null
|
@JceId(7) val richCardNameVer: Byte? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class GetTroopMemberListResp(
|
internal class GetTroopMemberListResp(
|
||||||
@SerialId(0) val uin: Long,
|
@JceId(0) val uin: Long,
|
||||||
@SerialId(1) val groupCode: Long,
|
@JceId(1) val groupCode: Long,
|
||||||
@SerialId(2) val groupUin: Long,
|
@JceId(2) val groupUin: Long,
|
||||||
@SerialId(3) val vecTroopMember: List<StTroopMemberInfo>,
|
@JceId(3) val vecTroopMember: List<StTroopMemberInfo>,
|
||||||
@SerialId(4) val nextUin: Long,
|
@JceId(4) val nextUin: Long,
|
||||||
@SerialId(5) val result: Int,
|
@JceId(5) val result: Int,
|
||||||
@SerialId(6) val errorCode: Short? = null,
|
@JceId(6) val errorCode: Short? = null,
|
||||||
@SerialId(7) val officeMode: Long? = null,
|
@JceId(7) val officeMode: Long? = null,
|
||||||
@SerialId(8) val nextGetTime: Long? = null
|
@JceId(8) val nextGetTime: Long? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class StTroopMemberInfo(
|
internal class StTroopMemberInfo(
|
||||||
@SerialId(0) val memberUin: Long,
|
@JceId(0) val memberUin: Long,
|
||||||
@SerialId(1) val faceId: Short,
|
@JceId(1) val faceId: Short,
|
||||||
@SerialId(2) val age: Byte,
|
@JceId(2) val age: Byte,
|
||||||
@SerialId(3) val gender: Byte,
|
@JceId(3) val gender: Byte,
|
||||||
@SerialId(4) val nick: String = "",
|
@JceId(4) val nick: String = "",
|
||||||
@SerialId(5) val status: Byte = 20,
|
@JceId(5) val status: Byte = 20,
|
||||||
@SerialId(6) val sShowName: String? = null,
|
@JceId(6) val sShowName: String? = null,
|
||||||
@SerialId(8) val sName: String? = null,
|
@JceId(8) val sName: String? = null,
|
||||||
@SerialId(9) val cGender: Byte? = null,
|
@JceId(9) val cGender: Byte? = null,
|
||||||
@SerialId(10) val sPhone: String? = "",
|
@JceId(10) val sPhone: String? = "",
|
||||||
@SerialId(11) val sEmail: String? = "",
|
@JceId(11) val sEmail: String? = "",
|
||||||
@SerialId(12) val sMemo: String? = "",
|
@JceId(12) val sMemo: String? = "",
|
||||||
@SerialId(13) val autoRemark: String? = "",
|
@JceId(13) val autoRemark: String? = "",
|
||||||
@SerialId(14) val dwMemberLevel: Long? = null,
|
@JceId(14) val dwMemberLevel: Long? = null,
|
||||||
@SerialId(15) val dwJoinTime: Long? = null,
|
@JceId(15) val dwJoinTime: Long? = null,
|
||||||
@SerialId(16) val dwLastSpeakTime: Long? = null,
|
@JceId(16) val dwLastSpeakTime: Long? = null,
|
||||||
@SerialId(17) val dwCreditLevel: Long? = null,
|
@JceId(17) val dwCreditLevel: Long? = null,
|
||||||
@SerialId(18) val dwFlag: Long? = null,
|
@JceId(18) val dwFlag: Long? = null,
|
||||||
@SerialId(19) val dwFlagExt: Long? = null,
|
@JceId(19) val dwFlagExt: Long? = null,
|
||||||
@SerialId(20) val dwPoint: Long? = null,
|
@JceId(20) val dwPoint: Long? = null,
|
||||||
@SerialId(21) val concerned: Byte? = null,
|
@JceId(21) val concerned: Byte? = null,
|
||||||
@SerialId(22) val shielded: Byte? = null,
|
@JceId(22) val shielded: Byte? = null,
|
||||||
@SerialId(23) val sSpecialTitle: String? = "",
|
@JceId(23) val sSpecialTitle: String? = "",
|
||||||
@SerialId(24) val dwSpecialTitleExpireTime: Long? = null,
|
@JceId(24) val dwSpecialTitleExpireTime: Long? = null,
|
||||||
@SerialId(25) val job: String? = "",
|
@JceId(25) val job: String? = "",
|
||||||
@SerialId(26) val apolloFlag: Byte? = null,
|
@JceId(26) val apolloFlag: Byte? = null,
|
||||||
@SerialId(27) val dwApolloTimestamp: Long? = null,
|
@JceId(27) val dwApolloTimestamp: Long? = null,
|
||||||
@SerialId(28) val dwGlobalGroupLevel: Long? = null,
|
@JceId(28) val dwGlobalGroupLevel: Long? = null,
|
||||||
@SerialId(29) val dwTitleId: Long? = null,
|
@JceId(29) val dwTitleId: Long? = null,
|
||||||
@SerialId(30) val dwShutupTimestap: Long? = null,
|
@JceId(30) val dwShutupTimestap: Long? = null,
|
||||||
@SerialId(31) val dwGlobalGroupPoint: Long? = null,
|
@JceId(31) val dwGlobalGroupPoint: Long? = null,
|
||||||
@SerialId(32) val qzusrinfo: QzoneUserInfo? = null,
|
@JceId(32) val qzusrinfo: QzoneUserInfo? = null,
|
||||||
@SerialId(33) val richCardNameVer: Byte? = null,
|
@JceId(33) val richCardNameVer: Byte? = null,
|
||||||
@SerialId(34) val dwVipType: Long? = null,
|
@JceId(34) val dwVipType: Long? = null,
|
||||||
@SerialId(35) val dwVipLevel: Long? = null,
|
@JceId(35) val dwVipLevel: Long? = null,
|
||||||
@SerialId(36) val dwBigClubLevel: Long? = null,
|
@JceId(36) val dwBigClubLevel: Long? = null,
|
||||||
@SerialId(37) val dwBigClubFlag: Long? = null,
|
@JceId(37) val dwBigClubFlag: Long? = null,
|
||||||
@SerialId(38) val dwNameplate: Long? = null,
|
@JceId(38) val dwNameplate: Long? = null,
|
||||||
@SerialId(39) val vecGroupHonor: ByteArray? = null
|
@JceId(39) val vecGroupHonor: ByteArray? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class QzoneUserInfo(
|
internal class QzoneUserInfo(
|
||||||
@SerialId(0) val eStarState: Int? = null,
|
@JceId(0) val eStarState: Int? = null,
|
||||||
@SerialId(1) val extendInfo: Map<String, String>? = null
|
@JceId(1) val extendInfo: Map<String, String>? = null
|
||||||
) : JceStruct
|
) : JceStruct
|
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
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 {
|
internal class Cmd0x352 : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class DelImgReq(
|
class DelImgReq(
|
||||||
@SerialId(1) val srcUin: Long = 0L,
|
@ProtoId(1) val srcUin: Long = 0L,
|
||||||
@SerialId(2) val dstUin: Long = 0L,
|
@ProtoId(2) val dstUin: Long = 0L,
|
||||||
@SerialId(3) val reqTerm: Int = 0,
|
@ProtoId(3) val reqTerm: Int = 0,
|
||||||
@SerialId(4) val reqPlatformType: Int = 0,
|
@ProtoId(4) val reqPlatformType: Int = 0,
|
||||||
@SerialId(5) val buType: Int = 0,
|
@ProtoId(5) val buType: Int = 0,
|
||||||
@SerialId(6) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(6) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(7) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(7) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(8) val picWidth: Int = 0,
|
@ProtoId(8) val picWidth: Int = 0,
|
||||||
@SerialId(9) val picHeight: Int = 0
|
@ProtoId(9) val picHeight: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class DelImgRsp(
|
class DelImgRsp(
|
||||||
@SerialId(1) val result: Int = 0,
|
@ProtoId(1) val result: Int = 0,
|
||||||
@SerialId(2) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class GetImgUrlReq(
|
class GetImgUrlReq(
|
||||||
@SerialId(1) val srcUin: Long = 0L,
|
@ProtoId(1) val srcUin: Long = 0L,
|
||||||
@SerialId(2) val dstUin: Long = 0L,
|
@ProtoId(2) val dstUin: Long = 0L,
|
||||||
@SerialId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val urlFlag: Int = 0,
|
@ProtoId(4) val urlFlag: Int = 0,
|
||||||
@SerialId(6) val urlType: Int = 0,
|
@ProtoId(6) val urlType: Int = 0,
|
||||||
@SerialId(7) val reqTerm: Int = 0,
|
@ProtoId(7) val reqTerm: Int = 0,
|
||||||
@SerialId(8) val reqPlatformType: Int = 0,
|
@ProtoId(8) val reqPlatformType: Int = 0,
|
||||||
@SerialId(9) val srcFileType: Int = 0,
|
@ProtoId(9) val srcFileType: Int = 0,
|
||||||
@SerialId(10) val innerIp: Int = 0,
|
@ProtoId(10) val innerIp: Int = 0,
|
||||||
@SerialId(11) val boolAddressBook: Boolean = false,
|
@ProtoId(11) val boolAddressBook: Boolean = false,
|
||||||
@SerialId(12) val buType: Int = 0,
|
@ProtoId(12) val buType: Int = 0,
|
||||||
@SerialId(13) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(13) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(14) val picUpTimestamp: Int = 0,
|
@ProtoId(14) val picUpTimestamp: Int = 0,
|
||||||
@SerialId(15) val reqTransferType: Int = 0
|
@ProtoId(15) val reqTransferType: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class GetImgUrlRsp(
|
class GetImgUrlRsp(
|
||||||
@SerialId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val clientIp: Int = 0,
|
@ProtoId(2) val clientIp: Int = 0,
|
||||||
@SerialId(3) val result: Int = 0,
|
@ProtoId(3) val result: Int = 0,
|
||||||
@SerialId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val bytesThumbDownUrl: List<ByteArray>? = null,
|
@ProtoId(5) val bytesThumbDownUrl: List<ByteArray>? = null,
|
||||||
@SerialId(6) val bytesOriginalDownUrl: List<ByteArray>? = null,
|
@ProtoId(6) val bytesOriginalDownUrl: List<ByteArray>? = null,
|
||||||
@SerialId(7) val msgImgInfo: ImgInfo? = null,
|
@ProtoId(7) val msgImgInfo: ImgInfo? = null,
|
||||||
@SerialId(8) val uint32DownIp: List<Int>? = null,
|
@ProtoId(8) val uint32DownIp: List<Int>? = null,
|
||||||
@SerialId(9) val uint32DownPort: List<Int>? = null,
|
@ProtoId(9) val uint32DownPort: List<Int>? = null,
|
||||||
@SerialId(10) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(10) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(11) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(11) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(12) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(12) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(13) val bytesBigDownUrl: List<ByteArray>? = null,
|
@ProtoId(13) val bytesBigDownUrl: List<ByteArray>? = null,
|
||||||
@SerialId(14) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(14) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(15) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(15) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(16) val httpsUrlFlag: Int = 0,
|
@ProtoId(16) val httpsUrlFlag: Int = 0,
|
||||||
@SerialId(26) val msgDownIp6: List<IPv6Info>? = null,
|
@ProtoId(26) val msgDownIp6: List<IPv6Info>? = null,
|
||||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Suppress("ArrayInDataClass")
|
@Suppress("ArrayInDataClass")
|
||||||
@Serializable
|
@Serializable
|
||||||
data class ImgInfo(
|
data class ImgInfo(
|
||||||
@SerialId(1) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val fileType: Int = 0,
|
@ProtoId(2) val fileType: Int = 0,
|
||||||
@SerialId(3) val fileSize: Long = 0L,
|
@ProtoId(3) val fileSize: Long = 0L,
|
||||||
@SerialId(4) val fileWidth: Int = 0,
|
@ProtoId(4) val fileWidth: Int = 0,
|
||||||
@SerialId(5) val fileHeight: Int = 0,
|
@ProtoId(5) val fileHeight: Int = 0,
|
||||||
@SerialId(6) val fileFlag: Long = 0L,
|
@ProtoId(6) val fileFlag: Long = 0L,
|
||||||
@SerialId(7) val fileCutPos: Int = 0
|
@ProtoId(7) val fileCutPos: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class IPv6Info(
|
class IPv6Info(
|
||||||
@SerialId(1) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val port: Int = 0
|
@ProtoId(2) val port: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ReqBody(
|
class ReqBody(
|
||||||
@SerialId(1) val subcmd: Int = 0, //2是GetImgUrlReq 1是UploadImgReq
|
@ProtoId(1) val subcmd: Int = 0, //2是GetImgUrlReq 1是UploadImgReq
|
||||||
@SerialId(2) val msgTryupImgReq: List<TryUpImgReq>? = null,// optional
|
@ProtoId(2) val msgTryupImgReq: List<TryUpImgReq>? = null,// optional
|
||||||
@SerialId(3) val msgGetimgUrlReq: List<GetImgUrlReq>? = null,// optional
|
@ProtoId(3) val msgGetimgUrlReq: List<GetImgUrlReq>? = null,// optional
|
||||||
@SerialId(4) val msgDelImgReq: List<DelImgReq>? = null,
|
@ProtoId(4) val msgDelImgReq: List<DelImgReq>? = null,
|
||||||
@SerialId(10) val netType: Int = 3// 数据网络=5
|
@ProtoId(10) val netType: Int = 3// 数据网络=5
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RspBody(
|
class RspBody(
|
||||||
@SerialId(1) val subcmd: Int = 0,
|
@ProtoId(1) val subcmd: Int = 0,
|
||||||
@SerialId(2) val msgTryupImgRsp: List<TryUpImgRsp>? = null,
|
@ProtoId(2) val msgTryupImgRsp: List<TryUpImgRsp>? = null,
|
||||||
@SerialId(3) val msgGetimgUrlRsp: List<GetImgUrlRsp>? = null,
|
@ProtoId(3) val msgGetimgUrlRsp: List<GetImgUrlRsp>? = null,
|
||||||
@SerialId(4) val boolNewBigchan: Boolean = false,
|
@ProtoId(4) val boolNewBigchan: Boolean = false,
|
||||||
@SerialId(5) val msgDelImgRsp: List<DelImgRsp>? = null,
|
@ProtoId(5) val msgDelImgRsp: List<DelImgRsp>? = null,
|
||||||
@SerialId(10) val failMsg: String? = ""
|
@ProtoId(10) val failMsg: String? = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class TryUpImgReq(
|
internal class TryUpImgReq(
|
||||||
@SerialId(1) val srcUin: Int,
|
@ProtoId(1) val srcUin: Int,
|
||||||
@SerialId(2) val dstUin: Int,
|
@ProtoId(2) val dstUin: Int,
|
||||||
@SerialId(3) val fileId: Int = 0,//从0开始的自增数?貌似有一个连接就要自增1, 但是又会重置回0
|
@ProtoId(3) val fileId: Int = 0,//从0开始的自增数?貌似有一个连接就要自增1, 但是又会重置回0
|
||||||
@SerialId(4) val fileMd5: ByteArray,
|
@ProtoId(4) val fileMd5: ByteArray,
|
||||||
@SerialId(5) val fileSize: Int,
|
@ProtoId(5) val fileSize: Int,
|
||||||
@SerialId(6) val fileName: String,//默认为md5+".jpg"
|
@ProtoId(6) val fileName: String,//默认为md5+".jpg"
|
||||||
@SerialId(7) val srcTerm: Int = 5,
|
@ProtoId(7) val srcTerm: Int = 5,
|
||||||
@SerialId(8) val platformType: Int = 9,
|
@ProtoId(8) val platformType: Int = 9,
|
||||||
@SerialId(9) val innerIP: Int = 0,
|
@ProtoId(9) val innerIP: Int = 0,
|
||||||
@SerialId(10) val addressBook: Int = 0,//chatType == 1006为1 我觉得发0没问题
|
@ProtoId(10) val addressBook: Int = 0,//chatType == 1006为1 我觉得发0没问题
|
||||||
@SerialId(11) val retry: Int = 0,//default
|
@ProtoId(11) val retry: Int = 0,//default
|
||||||
@SerialId(12) val buType: Int = 1,//1或96 不确定
|
@ProtoId(12) val buType: Int = 1,//1或96 不确定
|
||||||
@SerialId(13) val imgOriginal: Int,//是否为原图
|
@ProtoId(13) val imgOriginal: Int,//是否为原图
|
||||||
@SerialId(14) val imgWidth: Int,
|
@ProtoId(14) val imgWidth: Int,
|
||||||
@SerialId(15) val imgHeight: Int,
|
@ProtoId(15) val imgHeight: Int,
|
||||||
/**
|
/**
|
||||||
* ImgType:
|
* ImgType:
|
||||||
* JPG: 1000
|
* JPG: 1000
|
||||||
@ -140,50 +140,50 @@ internal class Cmd0x352 : ProtoBuf {
|
|||||||
* APNG: 2001
|
* APNG: 2001
|
||||||
* SHARPP: 1004
|
* SHARPP: 1004
|
||||||
*/
|
*/
|
||||||
@SerialId(16) val imgType: Int = 1000,
|
@ProtoId(16) val imgType: Int = 1000,
|
||||||
@SerialId(17) val buildVer: String = "8.2.0.1296",//版本号
|
@ProtoId(17) val buildVer: String = "8.2.7.4410",//版本号
|
||||||
@SerialId(18) val fileIndex: ByteArray = EMPTY_BYTE_ARRAY,//default
|
@ProtoId(18) val fileIndex: ByteArray = EMPTY_BYTE_ARRAY,//default
|
||||||
@SerialId(19) val fileStoreDays: Int = 0,//default
|
@ProtoId(19) val fileStoreDays: Int = 0,//default
|
||||||
@SerialId(20) val stepFlag: Int = 0,//default
|
@ProtoId(20) val stepFlag: Int = 0,//default
|
||||||
@SerialId(21) val rejectTryFast: Int = 0,//bool
|
@ProtoId(21) val rejectTryFast: Int = 0,//bool
|
||||||
@SerialId(22) val srvUpload: Int = 1,//typeHotPic[1/2/3]
|
@ProtoId(22) val srvUpload: Int = 1,//typeHotPic[1/2/3]
|
||||||
@SerialId(23) val transferUrl: ByteArray = EMPTY_BYTE_ARRAY//rawDownloadUrl, 如果没有就是EMPTY_BYTE_ARRAY
|
@ProtoId(23) val transferUrl: ByteArray = EMPTY_BYTE_ARRAY//rawDownloadUrl, 如果没有就是EMPTY_BYTE_ARRAY
|
||||||
) : ImgReq
|
) : ImgReq
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class TryUpImgRsp(
|
class TryUpImgRsp(
|
||||||
@SerialId(1) val fileId: Long = 0L,
|
@ProtoId(1) val fileId: Long = 0L,
|
||||||
@SerialId(2) val clientIp: Int = 0,
|
@ProtoId(2) val clientIp: Int = 0,
|
||||||
@SerialId(3) val result: Int = 0,
|
@ProtoId(3) val result: Int = 0,
|
||||||
@SerialId(4) val failMsg: String? = "",
|
@ProtoId(4) val failMsg: String? = "",
|
||||||
@SerialId(5) val boolFileExit: Boolean = false,
|
@ProtoId(5) val boolFileExit: Boolean = false,
|
||||||
@SerialId(6) val msgImgInfo: ImgInfo? = null,
|
@ProtoId(6) val msgImgInfo: ImgInfo? = null,
|
||||||
@SerialId(7) val uint32UpIp: List<Int>? = null,
|
@ProtoId(7) val uint32UpIp: List<Int>? = null,
|
||||||
@SerialId(8) val uint32UpPort: List<Int>? = null,
|
@ProtoId(8) val uint32UpPort: List<Int>? = null,
|
||||||
@SerialId(9) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(9) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(10) val upResid: String = "",
|
@ProtoId(10) val upResid: String = "",
|
||||||
@SerialId(11) val upUuid: String = "",
|
@ProtoId(11) val upUuid: String = "",
|
||||||
@SerialId(12) val upOffset: Long = 0L,
|
@ProtoId(12) val upOffset: Long = 0L,
|
||||||
@SerialId(13) val blockSize: Long = 0L,
|
@ProtoId(13) val blockSize: Long = 0L,
|
||||||
@SerialId(14) val encryptDstip: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(14) val encryptDstip: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(15) val roamdays: Int = 0,
|
@ProtoId(15) val roamdays: Int = 0,
|
||||||
@SerialId(26) val msgUpIp6: List<IPv6Info>? = null,
|
@ProtoId(26) val msgUpIp6: List<IPv6Info>? = null,
|
||||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(60) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(60) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(61) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(61) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(62) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(62) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(64) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(64) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(65) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(65) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(66) val httpsUrlFlag: Int = 0,
|
@ProtoId(66) val httpsUrlFlag: Int = 0,
|
||||||
@SerialId(1001) val msgInfo4busi: TryUpInfo4Busi? = null
|
@ProtoId(1001) val msgInfo4busi: TryUpInfo4Busi? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class TryUpInfo4Busi(
|
class TryUpInfo4Busi(
|
||||||
@SerialId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val thumbDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val thumbDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val originalDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val originalDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val bigDownUrl: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(5) val bigDownUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
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 {
|
internal class Cmd0x388 : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class DelImgReq(
|
class DelImgReq(
|
||||||
@SerialId(1) val srcUin: Long = 0L,
|
@ProtoId(1) val srcUin: Long = 0L,
|
||||||
@SerialId(2) val dstUin: Long = 0L,
|
@ProtoId(2) val dstUin: Long = 0L,
|
||||||
@SerialId(3) val reqTerm: Int = 0,
|
@ProtoId(3) val reqTerm: Int = 0,
|
||||||
@SerialId(4) val reqPlatformType: Int = 0,
|
@ProtoId(4) val reqPlatformType: Int = 0,
|
||||||
@SerialId(5) val buType: Int = 0,
|
@ProtoId(5) val buType: Int = 0,
|
||||||
@SerialId(6) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(6) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(7) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(7) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(8) val picWidth: Int = 0,
|
@ProtoId(8) val picWidth: Int = 0,
|
||||||
@SerialId(9) val picHeight: Int = 0
|
@ProtoId(9) val picHeight: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class DelImgRsp(
|
class DelImgRsp(
|
||||||
@SerialId(1) val result: Int = 0,
|
@ProtoId(1) val result: Int = 0,
|
||||||
@SerialId(2) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(3) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ExpRoamExtendInfo(
|
class ExpRoamExtendInfo(
|
||||||
@SerialId(1) val resid: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(1) val resid: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ExpRoamPicInfo(
|
class ExpRoamPicInfo(
|
||||||
@SerialId(1) val shopFlag: Int = 0,
|
@ProtoId(1) val shopFlag: Int = 0,
|
||||||
@SerialId(2) val pkgId: Int = 0,
|
@ProtoId(2) val pkgId: Int = 0,
|
||||||
@SerialId(3) val picId: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(3) val picId: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ExtensionCommPicTryUp(
|
class ExtensionCommPicTryUp(
|
||||||
@SerialId(1) val bytesExtinfo: List<ByteArray>? = null
|
@ProtoId(1) val bytesExtinfo: List<ByteArray>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ExtensionExpRoamTryUp(
|
class ExtensionExpRoamTryUp(
|
||||||
@SerialId(1) val msgExproamPicInfo: List<ExpRoamPicInfo>? = null
|
@ProtoId(1) val msgExproamPicInfo: List<ExpRoamPicInfo>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class GetImgUrlReq(
|
class GetImgUrlReq(
|
||||||
@SerialId(1) val groupCode: Long = 0L,
|
@ProtoId(1) val groupCode: Long = 0L,
|
||||||
@SerialId(2) val dstUin: Long = 0L,
|
@ProtoId(2) val dstUin: Long = 0L,
|
||||||
@SerialId(3) val fileid: Long = 0L,
|
@ProtoId(3) val fileid: Long = 0L,
|
||||||
@SerialId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val urlFlag: Int = 0,
|
@ProtoId(5) val urlFlag: Int = 0,
|
||||||
@SerialId(6) val urlType: Int = 0,
|
@ProtoId(6) val urlType: Int = 0,
|
||||||
@SerialId(7) val reqTerm: Int = 0,
|
@ProtoId(7) val reqTerm: Int = 0,
|
||||||
@SerialId(8) val reqPlatformType: Int = 0,
|
@ProtoId(8) val reqPlatformType: Int = 0,
|
||||||
@SerialId(9) val innerIp: Int = 0,
|
@ProtoId(9) val innerIp: Int = 0,
|
||||||
@SerialId(10) val buType: Int = 0,
|
@ProtoId(10) val buType: Int = 0,
|
||||||
@SerialId(11) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(11) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(12) val fileId: Long = 0L,
|
@ProtoId(12) val fileId: Long = 0L,
|
||||||
@SerialId(13) val fileSize: Long = 0L,
|
@ProtoId(13) val fileSize: Long = 0L,
|
||||||
@SerialId(14) val originalPic: Int = 0,
|
@ProtoId(14) val originalPic: Int = 0,
|
||||||
@SerialId(15) val retryReq: Int = 0,
|
@ProtoId(15) val retryReq: Int = 0,
|
||||||
@SerialId(16) val fileHeight: Int = 0,
|
@ProtoId(16) val fileHeight: Int = 0,
|
||||||
@SerialId(17) val fileWidth: Int = 0,
|
@ProtoId(17) val fileWidth: Int = 0,
|
||||||
@SerialId(18) val picType: Int = 0,
|
@ProtoId(18) val picType: Int = 0,
|
||||||
@SerialId(19) val picUpTimestamp: Int = 0,
|
@ProtoId(19) val picUpTimestamp: Int = 0,
|
||||||
@SerialId(20) val reqTransferType: Int = 0
|
@ProtoId(20) val reqTransferType: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class GetImgUrlRsp(
|
class GetImgUrlRsp(
|
||||||
@SerialId(1) val fileid: Long = 0L,
|
@ProtoId(1) val fileid: Long = 0L,
|
||||||
@SerialId(2) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val result: Int = 0,
|
@ProtoId(3) val result: Int = 0,
|
||||||
@SerialId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val msgImgInfo: ImgInfo? = null,
|
@ProtoId(5) val msgImgInfo: ImgInfo? = null,
|
||||||
@SerialId(6) val bytesThumbDownUrl: List<ByteArray>? = null,
|
@ProtoId(6) val bytesThumbDownUrl: List<ByteArray>? = null,
|
||||||
@SerialId(7) val bytesOriginalDownUrl: List<ByteArray>? = null,
|
@ProtoId(7) val bytesOriginalDownUrl: List<ByteArray>? = null,
|
||||||
@SerialId(8) val bytesBigDownUrl: List<ByteArray>? = null,
|
@ProtoId(8) val bytesBigDownUrl: List<ByteArray>? = null,
|
||||||
@SerialId(9) val uint32DownIp: List<Int>? = null,
|
@ProtoId(9) val uint32DownIp: List<Int>? = null,
|
||||||
@SerialId(10) val uint32DownPort: List<Int>? = null,
|
@ProtoId(10) val uint32DownPort: List<Int>? = null,
|
||||||
@SerialId(11) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(11) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(12) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(12) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(13) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(13) val originalDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(14) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(14) val bigDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(15) val fileId: Long = 0L,
|
@ProtoId(15) val fileId: Long = 0L,
|
||||||
@SerialId(16) val autoDownType: Int = 0,
|
@ProtoId(16) val autoDownType: Int = 0,
|
||||||
@SerialId(17) val uint32OrderDownType: List<Int>? = null,
|
@ProtoId(17) val uint32OrderDownType: List<Int>? = null,
|
||||||
@SerialId(19) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(19) val bigThumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(20) val httpsUrlFlag: Int = 0,
|
@ProtoId(20) val httpsUrlFlag: Int = 0,
|
||||||
@SerialId(26) val msgDownIp6: List<IPv6Info>? = null,
|
@ProtoId(26) val msgDownIp6: List<IPv6Info>? = null,
|
||||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class GetPttUrlReq(
|
class GetPttUrlReq(
|
||||||
@SerialId(1) val groupCode: Long = 0L,
|
@ProtoId(1) val groupCode: Long = 0L,
|
||||||
@SerialId(2) val dstUin: Long = 0L,
|
@ProtoId(2) val dstUin: Long = 0L,
|
||||||
@SerialId(3) val fileid: Long = 0L,
|
@ProtoId(3) val fileid: Long = 0L,
|
||||||
@SerialId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val reqTerm: Int = 0,
|
@ProtoId(5) val reqTerm: Int = 0,
|
||||||
@SerialId(6) val reqPlatformType: Int = 0,
|
@ProtoId(6) val reqPlatformType: Int = 0,
|
||||||
@SerialId(7) val innerIp: Int = 0,
|
@ProtoId(7) val innerIp: Int = 0,
|
||||||
@SerialId(8) val buType: Int = 0,
|
@ProtoId(8) val buType: Int = 0,
|
||||||
@SerialId(9) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(9) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(10) val fileId: Long = 0L,
|
@ProtoId(10) val fileId: Long = 0L,
|
||||||
@SerialId(11) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(11) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(12) val codec: Int = 0,
|
@ProtoId(12) val codec: Int = 0,
|
||||||
@SerialId(13) val buId: Int = 0,
|
@ProtoId(13) val buId: Int = 0,
|
||||||
@SerialId(14) val reqTransferType: Int = 0,
|
@ProtoId(14) val reqTransferType: Int = 0,
|
||||||
@SerialId(15) val isAuto: Int = 0
|
@ProtoId(15) val isAuto: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class GetPttUrlRsp(
|
class GetPttUrlRsp(
|
||||||
@SerialId(1) val fileid: Long = 0L,
|
@ProtoId(1) val fileid: Long = 0L,
|
||||||
@SerialId(2) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val result: Int = 0,
|
@ProtoId(3) val result: Int = 0,
|
||||||
@SerialId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val bytesDownUrl: List<ByteArray>? = null,
|
@ProtoId(5) val bytesDownUrl: List<ByteArray>? = null,
|
||||||
@SerialId(6) val uint32DownIp: List<Int>? = null,
|
@ProtoId(6) val uint32DownIp: List<Int>? = null,
|
||||||
@SerialId(7) val uint32DownPort: List<Int>? = null,
|
@ProtoId(7) val uint32DownPort: List<Int>? = null,
|
||||||
@SerialId(8) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(8) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(9) val downPara: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(9) val downPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(10) val fileId: Long = 0L,
|
@ProtoId(10) val fileId: Long = 0L,
|
||||||
@SerialId(11) val transferType: Int = 0,
|
@ProtoId(11) val transferType: Int = 0,
|
||||||
@SerialId(12) val allowRetry: Int = 0,
|
@ProtoId(12) val allowRetry: Int = 0,
|
||||||
@SerialId(26) val msgDownIp6: List<IPv6Info>? = null,
|
@ProtoId(26) val msgDownIp6: List<IPv6Info>? = null,
|
||||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(28) val strDomain: String = ""
|
@ProtoId(28) val strDomain: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Suppress("ArrayInDataClass")
|
@Suppress("ArrayInDataClass")
|
||||||
@Serializable
|
@Serializable
|
||||||
data class ImgInfo(
|
data class ImgInfo(
|
||||||
@SerialId(1) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val fileType: Int = 0,
|
@ProtoId(2) val fileType: Int = 0,
|
||||||
@SerialId(3) val fileSize: Long = 0L,
|
@ProtoId(3) val fileSize: Long = 0L,
|
||||||
@SerialId(4) val fileWidth: Int = 0,
|
@ProtoId(4) val fileWidth: Int = 0,
|
||||||
@SerialId(5) val fileHeight: Int = 0
|
@ProtoId(5) val fileHeight: Int = 0
|
||||||
) : ProtoBuf {
|
) : ProtoBuf {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ImgInfo(fileMd5=${fileMd5.contentToString()}, fileType=$fileType, fileSize=$fileSize, fileWidth=$fileWidth, fileHeight=$fileHeight)"
|
return "ImgInfo(fileMd5=${fileMd5.contentToString()}, fileType=$fileType, fileSize=$fileSize, fileWidth=$fileWidth, fileHeight=$fileHeight)"
|
||||||
@ -161,128 +161,128 @@ internal class Cmd0x388 : ProtoBuf {
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class IPv6Info(
|
class IPv6Info(
|
||||||
@SerialId(1) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val port: Int = 0
|
@ProtoId(2) val port: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class PicSize(
|
class PicSize(
|
||||||
@SerialId(1) val original: Int = 0,
|
@ProtoId(1) val original: Int = 0,
|
||||||
@SerialId(2) val thumb: Int = 0,
|
@ProtoId(2) val thumb: Int = 0,
|
||||||
@SerialId(3) val high: Int = 0
|
@ProtoId(3) val high: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ReqBody(
|
class ReqBody(
|
||||||
@SerialId(1) val netType: Int = 0,
|
@ProtoId(1) val netType: Int = 0,
|
||||||
@SerialId(2) val subcmd: Int = 0,
|
@ProtoId(2) val subcmd: Int = 0,
|
||||||
@SerialId(3) val msgTryupImgReq: List<TryUpImgReq>? = null,
|
@ProtoId(3) val msgTryupImgReq: List<TryUpImgReq>? = null,
|
||||||
@SerialId(4) val msgGetimgUrlReq: List<GetImgUrlReq>? = null,
|
@ProtoId(4) val msgGetimgUrlReq: List<GetImgUrlReq>? = null,
|
||||||
@SerialId(5) val msgTryupPttReq: List<TryUpPttReq>? = null,
|
@ProtoId(5) val msgTryupPttReq: List<TryUpPttReq>? = null,
|
||||||
@SerialId(6) val msgGetpttUrlReq: List<GetPttUrlReq>? = null,
|
@ProtoId(6) val msgGetpttUrlReq: List<GetPttUrlReq>? = null,
|
||||||
@SerialId(7) val commandId: Int = 0,
|
@ProtoId(7) val commandId: Int = 0,
|
||||||
@SerialId(8) val msgDelImgReq: List<DelImgReq>? = null,
|
@ProtoId(8) val msgDelImgReq: List<DelImgReq>? = null,
|
||||||
@SerialId(1001) val extension: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(1001) val extension: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RspBody(
|
class RspBody(
|
||||||
@SerialId(1) val clientIp: Int = 0,
|
@ProtoId(1) val clientIp: Int = 0,
|
||||||
@SerialId(2) val subcmd: Int = 0,
|
@ProtoId(2) val subcmd: Int = 0,
|
||||||
@SerialId(3) val msgTryupImgRsp: List<TryUpImgRsp>? = null,
|
@ProtoId(3) val msgTryupImgRsp: List<TryUpImgRsp>? = null,
|
||||||
@SerialId(4) val msgGetimgUrlRsp: List<GetImgUrlRsp>? = null,
|
@ProtoId(4) val msgGetimgUrlRsp: List<GetImgUrlRsp>? = null,
|
||||||
@SerialId(5) val msgTryupPttRsp: List<TryUpPttRsp>? = null,
|
@ProtoId(5) val msgTryupPttRsp: List<TryUpPttRsp>? = null,
|
||||||
@SerialId(6) val msgGetpttUrlRsp: List<GetPttUrlRsp>? = null,
|
@ProtoId(6) val msgGetpttUrlRsp: List<GetPttUrlRsp>? = null,
|
||||||
@SerialId(7) val msgDelImgRsp: List<DelImgRsp>? = null
|
@ProtoId(7) val msgDelImgRsp: List<DelImgRsp>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class TryUpImgReq(
|
class TryUpImgReq(
|
||||||
@SerialId(1) val groupCode: Long = 0L,
|
@ProtoId(1) val groupCode: Long = 0L,
|
||||||
@SerialId(2) val srcUin: Long = 0L,
|
@ProtoId(2) val srcUin: Long = 0L,
|
||||||
@SerialId(3) val fileId: Long = 0L,
|
@ProtoId(3) val fileId: Long = 0L,
|
||||||
@SerialId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val fileSize: Long = 0L,
|
@ProtoId(5) val fileSize: Long = 0L,
|
||||||
@SerialId(6) val fileName: String ="",
|
@ProtoId(6) val fileName: String = "",
|
||||||
@SerialId(7) val srcTerm: Int = 0,
|
@ProtoId(7) val srcTerm: Int = 0,
|
||||||
@SerialId(8) val platformType: Int = 0,
|
@ProtoId(8) val platformType: Int = 0,
|
||||||
@SerialId(9) val buType: Int = 0,
|
@ProtoId(9) val buType: Int = 0,
|
||||||
@SerialId(10) val picWidth: Int = 0,
|
@ProtoId(10) val picWidth: Int = 0,
|
||||||
@SerialId(11) val picHeight: Int = 0,
|
@ProtoId(11) val picHeight: Int = 0,
|
||||||
@SerialId(12) val picType: Int = 0,
|
@ProtoId(12) val picType: Int = 0,
|
||||||
@SerialId(13) val buildVer: String = "",
|
@ProtoId(13) val buildVer: String = "",
|
||||||
@SerialId(14) val innerIp: Int = 0,
|
@ProtoId(14) val innerIp: Int = 0,
|
||||||
@SerialId(15) val appPicType: Int = 0,
|
@ProtoId(15) val appPicType: Int = 0,
|
||||||
@SerialId(16) val originalPic: Int = 0,
|
@ProtoId(16) val originalPic: Int = 0,
|
||||||
@SerialId(17) val fileIndex: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(17) val fileIndex: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(18) val dstUin: Long = 0L,
|
@ProtoId(18) val dstUin: Long = 0L,
|
||||||
@SerialId(19) val srvUpload: Int = 0,
|
@ProtoId(19) val srvUpload: Int = 0,
|
||||||
@SerialId(20) val transferUrl: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(20) val transferUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ImgReq
|
) : ImgReq
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class TryUpImgRsp(
|
class TryUpImgRsp(
|
||||||
@SerialId(1) val fileId: Long = 0L,
|
@ProtoId(1) val fileId: Long = 0L,
|
||||||
@SerialId(2) val result: Int = 0,
|
@ProtoId(2) val result: Int = 0,
|
||||||
@SerialId(3) val failMsg: String = "",
|
@ProtoId(3) val failMsg: String = "",
|
||||||
@SerialId(4) val boolFileExit: Boolean = false,
|
@ProtoId(4) val boolFileExit: Boolean = false,
|
||||||
@SerialId(5) val msgImgInfo: ImgInfo? = null,
|
@ProtoId(5) val msgImgInfo: ImgInfo? = null,
|
||||||
@SerialId(6) val uint32UpIp: List<Int>? = null,
|
@ProtoId(6) val uint32UpIp: List<Int>? = null,
|
||||||
@SerialId(7) val uint32UpPort: List<Int>? = null,
|
@ProtoId(7) val uint32UpPort: List<Int>? = null,
|
||||||
@SerialId(8) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(8) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(9) val fileid: Long = 0L,
|
@ProtoId(9) val fileid: Long = 0L,
|
||||||
@SerialId(10) val upOffset: Long = 0L,
|
@ProtoId(10) val upOffset: Long = 0L,
|
||||||
@SerialId(11) val blockSize: Long = 0L,
|
@ProtoId(11) val blockSize: Long = 0L,
|
||||||
@SerialId(12) val boolNewBigChan: Boolean = false,
|
@ProtoId(12) val boolNewBigChan: Boolean = false,
|
||||||
@SerialId(26) val msgUpIp6: List<IPv6Info>? = null,
|
@ProtoId(26) val msgUpIp6: List<IPv6Info>? = null,
|
||||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(1001) val msgInfo4busi: TryUpInfo4Busi? = null
|
@ProtoId(1001) val msgInfo4busi: TryUpInfo4Busi? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class TryUpInfo4Busi(
|
class TryUpInfo4Busi(
|
||||||
@SerialId(1) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val downDomain: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val thumbDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val thumbDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val originalDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val originalDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val bigDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val bigDownUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(5) val fileResid: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class TryUpPttReq(
|
class TryUpPttReq(
|
||||||
@SerialId(1) val groupCode: Long = 0L,
|
@ProtoId(1) val groupCode: Long = 0L,
|
||||||
@SerialId(2) val srcUin: Long = 0L,
|
@ProtoId(2) val srcUin: Long = 0L,
|
||||||
@SerialId(3) val fileId: Long = 0L,
|
@ProtoId(3) val fileId: Long = 0L,
|
||||||
@SerialId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val fileSize: Long = 0L,
|
@ProtoId(5) val fileSize: Long = 0L,
|
||||||
@SerialId(6) val fileName: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(6) val fileName: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(7) val srcTerm: Int = 0,
|
@ProtoId(7) val srcTerm: Int = 0,
|
||||||
@SerialId(8) val platformType: Int = 0,
|
@ProtoId(8) val platformType: Int = 0,
|
||||||
@SerialId(9) val buType: Int = 0,
|
@ProtoId(9) val buType: Int = 0,
|
||||||
@SerialId(10) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(10) val buildVer: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(11) val innerIp: Int = 0,
|
@ProtoId(11) val innerIp: Int = 0,
|
||||||
@SerialId(12) val voiceLength: Int = 0,
|
@ProtoId(12) val voiceLength: Int = 0,
|
||||||
@SerialId(13) val boolNewUpChan: Boolean = false,
|
@ProtoId(13) val boolNewUpChan: Boolean = false,
|
||||||
@SerialId(14) val codec: Int = 0,
|
@ProtoId(14) val codec: Int = 0,
|
||||||
@SerialId(15) val voiceType: Int = 0,
|
@ProtoId(15) val voiceType: Int = 0,
|
||||||
@SerialId(16) val buId: Int = 0
|
@ProtoId(16) val buId: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class TryUpPttRsp(
|
class TryUpPttRsp(
|
||||||
@SerialId(1) val fileId: Long = 0L,
|
@ProtoId(1) val fileId: Long = 0L,
|
||||||
@SerialId(2) val result: Int = 0,
|
@ProtoId(2) val result: Int = 0,
|
||||||
@SerialId(3) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val failMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val boolFileExit: Boolean = false,
|
@ProtoId(4) val boolFileExit: Boolean = false,
|
||||||
@SerialId(5) val uint32UpIp: List<Int>? = null,
|
@ProtoId(5) val uint32UpIp: List<Int>? = null,
|
||||||
@SerialId(6) val uint32UpPort: List<Int>? = null,
|
@ProtoId(6) val uint32UpPort: List<Int>? = null,
|
||||||
@SerialId(7) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(7) val upUkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(8) val fileid: Long = 0L,
|
@ProtoId(8) val fileid: Long = 0L,
|
||||||
@SerialId(9) val upOffset: Long = 0L,
|
@ProtoId(9) val upOffset: Long = 0L,
|
||||||
@SerialId(10) val blockSize: Long = 0L,
|
@ProtoId(10) val blockSize: Long = 0L,
|
||||||
@SerialId(11) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(11) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(12) val channelType: Int = 0,
|
@ProtoId(12) val channelType: Int = 0,
|
||||||
@SerialId(26) val msgUpIp6: List<IPv6Info>? = null,
|
@ProtoId(26) val msgUpIp6: List<IPv6Info>? = null,
|
||||||
@SerialId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(27) val clientIp6: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : 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
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
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 {
|
class Common : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class BindInfo(
|
class BindInfo(
|
||||||
@SerialId(1) val friUin: Long = 0L,
|
@ProtoId(1) val friUin: Long = 0L,
|
||||||
@SerialId(2) val friNick: String = "",
|
@ProtoId(2) val friNick: String = "",
|
||||||
@SerialId(3) val time: Long = 0L,
|
@ProtoId(3) val time: Long = 0L,
|
||||||
@SerialId(4) val bindStatus: Int = 0
|
@ProtoId(4) val bindStatus: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class MedalInfo(
|
class MedalInfo(
|
||||||
@SerialId(1) val id: Int = 0,
|
@ProtoId(1) val id: Int = 0,
|
||||||
@SerialId(2) val type: Int = 0,
|
@ProtoId(2) val type: Int = 0,
|
||||||
@SerialId(4) val seq: Long = 0,
|
@ProtoId(4) val seq: Long = 0,
|
||||||
@SerialId(5) val name: String = "",
|
@ProtoId(5) val name: String = "",
|
||||||
@SerialId(6) val newflag: Int = 0,
|
@ProtoId(6) val newflag: Int = 0,
|
||||||
@SerialId(7) val time: Long = 0L,
|
@ProtoId(7) val time: Long = 0L,
|
||||||
@SerialId(8) val msgBindFri: Common.BindInfo? = null,
|
@ProtoId(8) val msgBindFri: Common.BindInfo? = null,
|
||||||
@SerialId(11) val desc: String = "",
|
@ProtoId(11) val desc: String = "",
|
||||||
@SerialId(31) val level: Int = 0,
|
@ProtoId(31) val level: Int = 0,
|
||||||
@SerialId(36) val taskinfos: List<Common.MedalTaskInfo>? = null,
|
@ProtoId(36) val taskinfos: List<Common.MedalTaskInfo>? = null,
|
||||||
@SerialId(40) val point: Int = 0,
|
@ProtoId(40) val point: Int = 0,
|
||||||
@SerialId(41) val pointLevel2: Int = 0,
|
@ProtoId(41) val pointLevel2: Int = 0,
|
||||||
@SerialId(42) val pointLevel3: Int = 0,
|
@ProtoId(42) val pointLevel3: Int = 0,
|
||||||
@SerialId(43) val seqLevel2: Long = 0,
|
@ProtoId(43) val seqLevel2: Long = 0,
|
||||||
@SerialId(44) val seqLevel3: Long = 0,
|
@ProtoId(44) val seqLevel3: Long = 0,
|
||||||
@SerialId(45) val timeLevel2: Long = 0L,
|
@ProtoId(45) val timeLevel2: Long = 0L,
|
||||||
@SerialId(46) val timeLevel3: Long = 0L,
|
@ProtoId(46) val timeLevel3: Long = 0L,
|
||||||
@SerialId(47) val descLevel2: String = "",
|
@ProtoId(47) val descLevel2: String = "",
|
||||||
@SerialId(48) val descLevel3: String = "",
|
@ProtoId(48) val descLevel3: String = "",
|
||||||
@SerialId(49) val endtime: Int = 0,
|
@ProtoId(49) val endtime: Int = 0,
|
||||||
@SerialId(50) val detailUrl: String = "",
|
@ProtoId(50) val detailUrl: String = "",
|
||||||
@SerialId(51) val detailUrl2: String = "",
|
@ProtoId(51) val detailUrl2: String = "",
|
||||||
@SerialId(52) val detailUrl3: String = "",
|
@ProtoId(52) val detailUrl3: String = "",
|
||||||
@SerialId(53) val taskDesc: String = "",
|
@ProtoId(53) val taskDesc: String = "",
|
||||||
@SerialId(54) val taskDesc2: String = "",
|
@ProtoId(54) val taskDesc2: String = "",
|
||||||
@SerialId(55) val taskDesc3: String = "",
|
@ProtoId(55) val taskDesc3: String = "",
|
||||||
@SerialId(56) val levelCount: Int = 0,
|
@ProtoId(56) val levelCount: Int = 0,
|
||||||
@SerialId(57) val noProgress: Int = 0,
|
@ProtoId(57) val noProgress: Int = 0,
|
||||||
@SerialId(58) val resource: String = "",
|
@ProtoId(58) val resource: String = "",
|
||||||
@SerialId(59) val fromuinLevel: Int = 0,
|
@ProtoId(59) val fromuinLevel: Int = 0,
|
||||||
@SerialId(60) val unread: Int = 0,
|
@ProtoId(60) val unread: Int = 0,
|
||||||
@SerialId(61) val unread2: Int = 0,
|
@ProtoId(61) val unread2: Int = 0,
|
||||||
@SerialId(62) val unread3: Int = 0
|
@ProtoId(62) val unread3: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class MedalTaskInfo(
|
class MedalTaskInfo(
|
||||||
@SerialId(1) val taskid: Int = 0,
|
@ProtoId(1) val taskid: Int = 0,
|
||||||
@SerialId(32) val int32TaskValue: Int = 0,
|
@ProtoId(32) val int32TaskValue: Int = 0,
|
||||||
@SerialId(33) val tarValue: Int = 0,
|
@ProtoId(33) val tarValue: Int = 0,
|
||||||
@SerialId(34) val tarValueLevel2: Int = 0,
|
@ProtoId(34) val tarValueLevel2: Int = 0,
|
||||||
@SerialId(35) val tarValueLevel3: Int = 0
|
@ProtoId(35) val tarValueLevel3: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,475 +75,475 @@ class Common : ProtoBuf {
|
|||||||
class AppointDefine : ProtoBuf {
|
class AppointDefine : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class ADFeedContent(
|
class ADFeedContent(
|
||||||
@SerialId(1) val msgUserInfo: AppointDefine.UserInfo? = null,
|
@ProtoId(1) val msgUserInfo: AppointDefine.UserInfo? = null,
|
||||||
@SerialId(2) val strPicUrl: List<String> = listOf(),
|
@ProtoId(2) val strPicUrl: List<String> = listOf(),
|
||||||
@SerialId(3) val msgText: AppointDefine.RichText? = null,
|
@ProtoId(3) val msgText: AppointDefine.RichText? = null,
|
||||||
@SerialId(4) val attendInfo: String = "",
|
@ProtoId(4) val attendInfo: String = "",
|
||||||
@SerialId(5) val actionUrl: String = "",
|
@ProtoId(5) val actionUrl: String = "",
|
||||||
@SerialId(6) val publishTime: Int = 0,
|
@ProtoId(6) val publishTime: Int = 0,
|
||||||
@SerialId(7) val msgHotTopicList: AppointDefine.HotTopicList? = null,
|
@ProtoId(7) val msgHotTopicList: AppointDefine.HotTopicList? = null,
|
||||||
@SerialId(8) val moreUrl: String = "",
|
@ProtoId(8) val moreUrl: String = "",
|
||||||
@SerialId(9) val recordDuration: String = ""
|
@ProtoId(9) val recordDuration: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RichText(
|
class RichText(
|
||||||
@SerialId(1) val msgElems: List<AppointDefine.Elem>? = null
|
@ProtoId(1) val msgElems: List<AppointDefine.Elem>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RankEvent(
|
class RankEvent(
|
||||||
@SerialId(1) val listtype: Int = 0,
|
@ProtoId(1) val listtype: Int = 0,
|
||||||
@SerialId(2) val notifytype: Int = 0,
|
@ProtoId(2) val notifytype: Int = 0,
|
||||||
@SerialId(3) val eventtime: Int = 0,
|
@ProtoId(3) val eventtime: Int = 0,
|
||||||
@SerialId(4) val seq: Int = 0,
|
@ProtoId(4) val seq: Int = 0,
|
||||||
@SerialId(5) val notifyTips: String = ""
|
@ProtoId(5) val notifyTips: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Wifi(
|
class Wifi(
|
||||||
@SerialId(1) val mac: Long = 0L,
|
@ProtoId(1) val mac: Long = 0L,
|
||||||
@SerialId(2) val int32Rssi: Int = 0
|
@ProtoId(2) val int32Rssi: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class InterestItem(
|
class InterestItem(
|
||||||
@SerialId(1) val tagId: Long = 0L,
|
@ProtoId(1) val tagId: Long = 0L,
|
||||||
@SerialId(2) val tagName: String = "",
|
@ProtoId(2) val tagName: String = "",
|
||||||
@SerialId(3) val tagIconUrl: String = "",
|
@ProtoId(3) val tagIconUrl: String = "",
|
||||||
@SerialId(4) val tagHref: String = "",
|
@ProtoId(4) val tagHref: String = "",
|
||||||
@SerialId(5) val tagBackColor: String = "",
|
@ProtoId(5) val tagBackColor: String = "",
|
||||||
@SerialId(6) val tagFontColor: String = "",
|
@ProtoId(6) val tagFontColor: String = "",
|
||||||
@SerialId(7) val tagVid: String = "",
|
@ProtoId(7) val tagVid: String = "",
|
||||||
@SerialId(8) val tagType: Int = 0,
|
@ProtoId(8) val tagType: Int = 0,
|
||||||
@SerialId(9) val addTime: Int = 0,
|
@ProtoId(9) val addTime: Int = 0,
|
||||||
@SerialId(10) val tagCategory: String = "",
|
@ProtoId(10) val tagCategory: String = "",
|
||||||
@SerialId(11) val tagOtherUrl: String = "",
|
@ProtoId(11) val tagOtherUrl: String = "",
|
||||||
@SerialId(12) val bid: Int = 0
|
@ProtoId(12) val bid: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ShopID(
|
class ShopID(
|
||||||
@SerialId(1) val shopid: String = "",
|
@ProtoId(1) val shopid: String = "",
|
||||||
@SerialId(2) val sp: Int = 0
|
@ProtoId(2) val sp: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class FeedComment(
|
class FeedComment(
|
||||||
@SerialId(1) val commentId: String = "",
|
@ProtoId(1) val commentId: String = "",
|
||||||
@SerialId(2) val feedId: String = "",
|
@ProtoId(2) val feedId: String = "",
|
||||||
@SerialId(3) val msgPublisherInfo: AppointDefine.StrangerInfo? = null,
|
@ProtoId(3) val msgPublisherInfo: AppointDefine.StrangerInfo? = null,
|
||||||
@SerialId(4) val time: Int = 0,
|
@ProtoId(4) val time: Int = 0,
|
||||||
@SerialId(6) val msgReplyInfo: AppointDefine.ReplyInfo? = null,
|
@ProtoId(6) val msgReplyInfo: AppointDefine.ReplyInfo? = null,
|
||||||
@SerialId(7) val flag: Int = 0,
|
@ProtoId(7) val flag: Int = 0,
|
||||||
@SerialId(8) val msgContent: AppointDefine.RichText? = null,
|
@ProtoId(8) val msgContent: AppointDefine.RichText? = null,
|
||||||
@SerialId(9) val hot: Int = 0
|
@ProtoId(9) val hot: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ADFeed(
|
class ADFeed(
|
||||||
@SerialId(1) val taskId: Int = 0,
|
@ProtoId(1) val taskId: Int = 0,
|
||||||
@SerialId(2) val style: Int = 0,
|
@ProtoId(2) val style: Int = 0,
|
||||||
@SerialId(3) val content: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(3) val content: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Cell(
|
class Cell(
|
||||||
@SerialId(1) val int32Mcc: Int = -1,
|
@ProtoId(1) val int32Mcc: Int = -1,
|
||||||
@SerialId(2) val int32Mnc: Int = -1,
|
@ProtoId(2) val int32Mnc: Int = -1,
|
||||||
@SerialId(3) val int32Lac: Int = -1,
|
@ProtoId(3) val int32Lac: Int = -1,
|
||||||
@SerialId(4) val int32Cellid: Int = -1,
|
@ProtoId(4) val int32Cellid: Int = -1,
|
||||||
@SerialId(5) val int32Rssi: Int = 0
|
@ProtoId(5) val int32Rssi: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RecentVistorEvent(
|
class RecentVistorEvent(
|
||||||
@SerialId(1) val eventtype: Int = 0,
|
@ProtoId(1) val eventtype: Int = 0,
|
||||||
@SerialId(2) val eventTinyid: Long = 0L,
|
@ProtoId(2) val eventTinyid: Long = 0L,
|
||||||
@SerialId(3) val unreadCount: Int = 0
|
@ProtoId(3) val unreadCount: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class OrganizerInfo(
|
class OrganizerInfo(
|
||||||
@SerialId(1) val hostName: String = "",
|
@ProtoId(1) val hostName: String = "",
|
||||||
@SerialId(2) val hostUrl: String = "",
|
@ProtoId(2) val hostUrl: String = "",
|
||||||
@SerialId(3) val hostCover: String = ""
|
@ProtoId(3) val hostCover: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class InterestTag(
|
class InterestTag(
|
||||||
@SerialId(1) val tagType: Int = 0,
|
@ProtoId(1) val tagType: Int = 0,
|
||||||
@SerialId(2) val msgTagList: List<AppointDefine.InterestItem>? = null
|
@ProtoId(2) val msgTagList: List<AppointDefine.InterestItem>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class AppointInfoEx(
|
class AppointInfoEx(
|
||||||
@SerialId(1) val feedsPicUrl: String = "",
|
@ProtoId(1) val feedsPicUrl: String = "",
|
||||||
@SerialId(2) val feedsUrl: String = "",
|
@ProtoId(2) val feedsUrl: String = "",
|
||||||
@SerialId(3) val detailTitle: String = "",
|
@ProtoId(3) val detailTitle: String = "",
|
||||||
@SerialId(4) val detailDescribe: String = "",
|
@ProtoId(4) val detailDescribe: String = "",
|
||||||
@SerialId(5) val showPublisher: Int = 0,
|
@ProtoId(5) val showPublisher: Int = 0,
|
||||||
@SerialId(6) val detailPicUrl: String = "",
|
@ProtoId(6) val detailPicUrl: String = "",
|
||||||
@SerialId(7) val detailUrl: String = "",
|
@ProtoId(7) val detailUrl: String = "",
|
||||||
@SerialId(8) val showAttend: Int = 0
|
@ProtoId(8) val showAttend: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class DateComment(
|
class DateComment(
|
||||||
@SerialId(1) val commentId: String = "",
|
@ProtoId(1) val commentId: String = "",
|
||||||
@SerialId(2) val msgAppointId: AppointDefine.AppointID? = null,
|
@ProtoId(2) val msgAppointId: AppointDefine.AppointID? = null,
|
||||||
@SerialId(3) val msgPublisherInfo: AppointDefine.StrangerInfo? = null,
|
@ProtoId(3) val msgPublisherInfo: AppointDefine.StrangerInfo? = null,
|
||||||
@SerialId(4) val time: Int = 0,
|
@ProtoId(4) val time: Int = 0,
|
||||||
@SerialId(6) val msgReplyInfo: AppointDefine.ReplyInfo? = null,
|
@ProtoId(6) val msgReplyInfo: AppointDefine.ReplyInfo? = null,
|
||||||
@SerialId(7) val flag: Int = 0,
|
@ProtoId(7) val flag: Int = 0,
|
||||||
@SerialId(8) val msgContent: AppointDefine.RichText? = null
|
@ProtoId(8) val msgContent: AppointDefine.RichText? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class AppointContent(
|
class AppointContent(
|
||||||
@SerialId(1) val appointSubject: Int = 0,
|
@ProtoId(1) val appointSubject: Int = 0,
|
||||||
@SerialId(2) val payType: Int = 0,
|
@ProtoId(2) val payType: Int = 0,
|
||||||
@SerialId(3) val appointDate: Int = 0,
|
@ProtoId(3) val appointDate: Int = 0,
|
||||||
@SerialId(4) val appointGender: Int = 0,
|
@ProtoId(4) val appointGender: Int = 0,
|
||||||
@SerialId(5) val appointIntroduce: String = "",
|
@ProtoId(5) val appointIntroduce: String = "",
|
||||||
@SerialId(6) val msgAppointAddress: AppointDefine.AddressInfo? = null,
|
@ProtoId(6) val msgAppointAddress: AppointDefine.AddressInfo? = null,
|
||||||
@SerialId(7) val msgTravelInfo: AppointDefine.TravelInfo? = null
|
@ProtoId(7) val msgTravelInfo: AppointDefine.TravelInfo? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class FeedInfo(
|
class FeedInfo(
|
||||||
@SerialId(1) val feedType: Long = 0L,
|
@ProtoId(1) val feedType: Long = 0L,
|
||||||
@SerialId(2) val feedId: String = "",
|
@ProtoId(2) val feedId: String = "",
|
||||||
@SerialId(3) val msgFeedContent: AppointDefine.FeedContent? = null,
|
@ProtoId(3) val msgFeedContent: AppointDefine.FeedContent? = null,
|
||||||
@SerialId(4) val msgTopicInfo: AppointDefine.NearbyTopic? = null,
|
@ProtoId(4) val msgTopicInfo: AppointDefine.NearbyTopic? = null,
|
||||||
@SerialId(5) val publishTime: Long = 0,
|
@ProtoId(5) val publishTime: Long = 0,
|
||||||
@SerialId(6) val praiseCount: Int = 0,
|
@ProtoId(6) val praiseCount: Int = 0,
|
||||||
@SerialId(7) val praiseFlag: Int = 0,
|
@ProtoId(7) val praiseFlag: Int = 0,
|
||||||
@SerialId(8) val msgPraiseUser: List<AppointDefine.StrangerInfo>? = null,
|
@ProtoId(8) val msgPraiseUser: List<AppointDefine.StrangerInfo>? = null,
|
||||||
@SerialId(9) val commentCount: Int = 0,
|
@ProtoId(9) val commentCount: Int = 0,
|
||||||
@SerialId(10) val msgCommentList: List<AppointDefine.FeedComment>? = null,
|
@ProtoId(10) val msgCommentList: List<AppointDefine.FeedComment>? = null,
|
||||||
@SerialId(11) val commentRetAll: Int = 0,
|
@ProtoId(11) val commentRetAll: Int = 0,
|
||||||
@SerialId(12) val hotFlag: Int = 0,
|
@ProtoId(12) val hotFlag: Int = 0,
|
||||||
@SerialId(13) val svrReserved: Long = 0L,
|
@ProtoId(13) val svrReserved: Long = 0L,
|
||||||
@SerialId(14) val msgHotEntry: AppointDefine.HotEntry? = null
|
@ProtoId(14) val msgHotEntry: AppointDefine.HotEntry? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class HotTopicList(
|
class HotTopicList(
|
||||||
@SerialId(1) val topicList: List<AppointDefine.HotTopic>? = null
|
@ProtoId(1) val topicList: List<AppointDefine.HotTopic>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class FeedContent(
|
class FeedContent(
|
||||||
@SerialId(1) val strPicUrl: List<String> = listOf(),
|
@ProtoId(1) val strPicUrl: List<String> = listOf(),
|
||||||
@SerialId(2) val msgText: AppointDefine.RichText? = null,
|
@ProtoId(2) val msgText: AppointDefine.RichText? = null,
|
||||||
@SerialId(3) val hrefUrl: String = "",
|
@ProtoId(3) val hrefUrl: String = "",
|
||||||
@SerialId(5) val groupName: String = "",
|
@ProtoId(5) val groupName: String = "",
|
||||||
@SerialId(6) val groupBulletin: String = "",
|
@ProtoId(6) val groupBulletin: String = "",
|
||||||
@SerialId(7) val feedType: Int = 0,
|
@ProtoId(7) val feedType: Int = 0,
|
||||||
@SerialId(8) val poiId: String = "",
|
@ProtoId(8) val poiId: String = "",
|
||||||
@SerialId(9) val poiTitle: String = "",
|
@ProtoId(9) val poiTitle: String = "",
|
||||||
@SerialId(20) val effectiveTime: Int = 0,
|
@ProtoId(20) val effectiveTime: Int = 0,
|
||||||
@SerialId(21) val expiationTime: Int = 0,
|
@ProtoId(21) val expiationTime: Int = 0,
|
||||||
@SerialId(22) val msgLocale: AppointDefine.LocaleInfo? = null,
|
@ProtoId(22) val msgLocale: AppointDefine.LocaleInfo? = null,
|
||||||
@SerialId(23) val feedsIndex: Int = 0,
|
@ProtoId(23) val feedsIndex: Int = 0,
|
||||||
@SerialId(24) val msgAd: AppointDefine.ADFeed? = null,
|
@ProtoId(24) val msgAd: AppointDefine.ADFeed? = null,
|
||||||
@SerialId(25) val privateData: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(25) val privateData: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class TravelInfo(
|
class TravelInfo(
|
||||||
@SerialId(1) val msgDepartLocale: AppointDefine.LocaleInfo? = null,
|
@ProtoId(1) val msgDepartLocale: AppointDefine.LocaleInfo? = null,
|
||||||
@SerialId(2) val msgDestination: AppointDefine.LocaleInfo? = null,
|
@ProtoId(2) val msgDestination: AppointDefine.LocaleInfo? = null,
|
||||||
@SerialId(3) val vehicle: Int = 0,
|
@ProtoId(3) val vehicle: Int = 0,
|
||||||
@SerialId(4) val partnerCount: Int = 0,
|
@ProtoId(4) val partnerCount: Int = 0,
|
||||||
@SerialId(5) val placePicUrl: String = "",
|
@ProtoId(5) val placePicUrl: String = "",
|
||||||
@SerialId(6) val placeUrl: String = ""
|
@ProtoId(6) val placeUrl: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RecentFreshFeed(
|
class RecentFreshFeed(
|
||||||
@SerialId(1) val freshFeedInfo: List<AppointDefine.FreshFeedInfo>? = null,
|
@ProtoId(1) val freshFeedInfo: List<AppointDefine.FreshFeedInfo>? = null,
|
||||||
@SerialId(2) val uid: Long = 0L
|
@ProtoId(2) val uid: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class GPS(
|
class GPS(
|
||||||
@SerialId(1) val int32Lat: Int = 900000000,
|
@ProtoId(1) val int32Lat: Int = 900000000,
|
||||||
@SerialId(2) val int32Lon: Int = 900000000,
|
@ProtoId(2) val int32Lon: Int = 900000000,
|
||||||
@SerialId(3) val int32Alt: Int = -10000000,
|
@ProtoId(3) val int32Alt: Int = -10000000,
|
||||||
@SerialId(4) val int32Type: Int = 0
|
@ProtoId(4) val int32Type: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class AppointID(
|
class AppointID(
|
||||||
@SerialId(1) val requestId: String = ""
|
@ProtoId(1) val requestId: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class LocaleInfo(
|
class LocaleInfo(
|
||||||
@SerialId(1) val name: String = "",
|
@ProtoId(1) val name: String = "",
|
||||||
@SerialId(2) val country: String = "",
|
@ProtoId(2) val country: String = "",
|
||||||
@SerialId(3) val province: String = "",
|
@ProtoId(3) val province: String = "",
|
||||||
@SerialId(4) val city: String = "",
|
@ProtoId(4) val city: String = "",
|
||||||
@SerialId(5) val region: String = "",
|
@ProtoId(5) val region: String = "",
|
||||||
@SerialId(6) val poi: String = "",
|
@ProtoId(6) val poi: String = "",
|
||||||
@SerialId(7) val msgGps: AppointDefine.GPS? = null,
|
@ProtoId(7) val msgGps: AppointDefine.GPS? = null,
|
||||||
@SerialId(8) val address: String = ""
|
@ProtoId(8) val address: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class LBSInfo(
|
class LBSInfo(
|
||||||
@SerialId(1) val msgGps: AppointDefine.GPS? = null,
|
@ProtoId(1) val msgGps: AppointDefine.GPS? = null,
|
||||||
@SerialId(2) val msgWifis: List<AppointDefine.Wifi>? = null,
|
@ProtoId(2) val msgWifis: List<AppointDefine.Wifi>? = null,
|
||||||
@SerialId(3) val msgCells: List<AppointDefine.Cell>? = null
|
@ProtoId(3) val msgCells: List<AppointDefine.Cell>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class FeedEvent(
|
class FeedEvent(
|
||||||
@SerialId(1) val eventId: Long = 0L,
|
@ProtoId(1) val eventId: Long = 0L,
|
||||||
@SerialId(2) val time: Int = 0,
|
@ProtoId(2) val time: Int = 0,
|
||||||
@SerialId(3) val eventtype: Int = 0,
|
@ProtoId(3) val eventtype: Int = 0,
|
||||||
@SerialId(4) val msgUserInfo: AppointDefine.StrangerInfo? = null,
|
@ProtoId(4) val msgUserInfo: AppointDefine.StrangerInfo? = null,
|
||||||
@SerialId(5) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
@ProtoId(5) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
||||||
@SerialId(6) val eventTips: String = "",
|
@ProtoId(6) val eventTips: String = "",
|
||||||
@SerialId(7) val msgComment: AppointDefine.FeedComment? = null,
|
@ProtoId(7) val msgComment: AppointDefine.FeedComment? = null,
|
||||||
@SerialId(8) val cancelEventId: Long = 0L
|
@ProtoId(8) val cancelEventId: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class FeedsCookie(
|
class FeedsCookie(
|
||||||
@SerialId(1) val strList: List<String> = listOf(),
|
@ProtoId(1) val strList: List<String> = listOf(),
|
||||||
@SerialId(2) val pose: Int = 0,
|
@ProtoId(2) val pose: Int = 0,
|
||||||
@SerialId(3) val cookie: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val cookie: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val uint64Topics: List<Long>? = null
|
@ProtoId(4) val uint64Topics: List<Long>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class NearbyTopic(
|
class NearbyTopic(
|
||||||
@SerialId(1) val topicId: Long = 0L,
|
@ProtoId(1) val topicId: Long = 0L,
|
||||||
@SerialId(2) val topic: String = "",
|
@ProtoId(2) val topic: String = "",
|
||||||
@SerialId(3) val foreword: String = "",
|
@ProtoId(3) val foreword: String = "",
|
||||||
@SerialId(4) val createTime: Int = 0,
|
@ProtoId(4) val createTime: Int = 0,
|
||||||
@SerialId(5) val updateTime: Int = 0,
|
@ProtoId(5) val updateTime: Int = 0,
|
||||||
@SerialId(6) val hotFlag: Int = 0,
|
@ProtoId(6) val hotFlag: Int = 0,
|
||||||
@SerialId(7) val buttonStyle: Int = 0,
|
@ProtoId(7) val buttonStyle: Int = 0,
|
||||||
@SerialId(8) val buttonSrc: String = "",
|
@ProtoId(8) val buttonSrc: String = "",
|
||||||
@SerialId(9) val backgroundSrc: String = "",
|
@ProtoId(9) val backgroundSrc: String = "",
|
||||||
@SerialId(10) val attendeeInfo: String = "",
|
@ProtoId(10) val attendeeInfo: String = "",
|
||||||
@SerialId(11) val index: Int = 0,
|
@ProtoId(11) val index: Int = 0,
|
||||||
@SerialId(12) val publishScope: Int = 0,
|
@ProtoId(12) val publishScope: Int = 0,
|
||||||
@SerialId(13) val effectiveTime: Int = 0,
|
@ProtoId(13) val effectiveTime: Int = 0,
|
||||||
@SerialId(14) val expiationTime: Int = 0,
|
@ProtoId(14) val expiationTime: Int = 0,
|
||||||
@SerialId(15) val pushedUsrCount: Int = 0,
|
@ProtoId(15) val pushedUsrCount: Int = 0,
|
||||||
@SerialId(16) val timerangeLeft: Int = 0,
|
@ProtoId(16) val timerangeLeft: Int = 0,
|
||||||
@SerialId(17) val timerangeRight: Int = 0,
|
@ProtoId(17) val timerangeRight: Int = 0,
|
||||||
@SerialId(18) val area: String = ""
|
@ProtoId(18) val area: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class NearbyEvent(
|
class NearbyEvent(
|
||||||
@SerialId(1) val eventtype: Int = 0,
|
@ProtoId(1) val eventtype: Int = 0,
|
||||||
@SerialId(2) val msgRankevent: AppointDefine.RankEvent? = null,
|
@ProtoId(2) val msgRankevent: AppointDefine.RankEvent? = null,
|
||||||
@SerialId(3) val eventUin: Long = 0L,
|
@ProtoId(3) val eventUin: Long = 0L,
|
||||||
@SerialId(4) val eventTinyid: Long = 0L
|
@ProtoId(4) val eventTinyid: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Feed(
|
class Feed(
|
||||||
@SerialId(1) val msgUserInfo: AppointDefine.PublisherInfo? = null,
|
@ProtoId(1) val msgUserInfo: AppointDefine.PublisherInfo? = null,
|
||||||
@SerialId(2) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
@ProtoId(2) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
||||||
@SerialId(3) val ownerFlag: Int = 0
|
@ProtoId(3) val ownerFlag: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ActivityInfo(
|
class ActivityInfo(
|
||||||
@SerialId(2) val name: String = "",
|
@ProtoId(2) val name: String = "",
|
||||||
@SerialId(3) val cover: String = "",
|
@ProtoId(3) val cover: String = "",
|
||||||
@SerialId(4) val url: String = "",
|
@ProtoId(4) val url: String = "",
|
||||||
@SerialId(5) val startTime: Int = 0,
|
@ProtoId(5) val startTime: Int = 0,
|
||||||
@SerialId(6) val endTime: Int = 0,
|
@ProtoId(6) val endTime: Int = 0,
|
||||||
@SerialId(7) val locName: String = "",
|
@ProtoId(7) val locName: String = "",
|
||||||
@SerialId(8) val enroll: Long = 0L,
|
@ProtoId(8) val enroll: Long = 0L,
|
||||||
@SerialId(9) val createUin: Long = 0L,
|
@ProtoId(9) val createUin: Long = 0L,
|
||||||
@SerialId(10) val createTime: Int = 0,
|
@ProtoId(10) val createTime: Int = 0,
|
||||||
@SerialId(11) val organizerInfo: AppointDefine.OrganizerInfo = OrganizerInfo(),
|
@ProtoId(11) val organizerInfo: AppointDefine.OrganizerInfo = OrganizerInfo(),
|
||||||
@SerialId(12) val flag: Long? = null
|
@ProtoId(12) val flag: Long? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class HotEntry(
|
class HotEntry(
|
||||||
@SerialId(1) val openFlag: Int = 0,
|
@ProtoId(1) val openFlag: Int = 0,
|
||||||
@SerialId(2) val restTime: Int = 0,
|
@ProtoId(2) val restTime: Int = 0,
|
||||||
@SerialId(3) val foreword: String = "",
|
@ProtoId(3) val foreword: String = "",
|
||||||
@SerialId(4) val backgroundSrc: String = ""
|
@ProtoId(4) val backgroundSrc: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class UserFeed(
|
class UserFeed(
|
||||||
@SerialId(1) val msgUserInfo: AppointDefine.PublisherInfo? = null,
|
@ProtoId(1) val msgUserInfo: AppointDefine.PublisherInfo? = null,
|
||||||
@SerialId(2) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
@ProtoId(2) val msgFeedInfo: AppointDefine.FeedInfo? = null,
|
||||||
@SerialId(3) val ownerFlag: Int = 0,
|
@ProtoId(3) val ownerFlag: Int = 0,
|
||||||
@SerialId(4) val msgActivityInfo: AppointDefine.ActivityInfo? = null
|
@ProtoId(4) val msgActivityInfo: AppointDefine.ActivityInfo? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Elem(
|
class Elem(
|
||||||
@SerialId(1) val content: String = "",
|
@ProtoId(1) val content: String = "",
|
||||||
@SerialId(2) val msgFaceInfo: AppointDefine.Face? = null
|
@ProtoId(2) val msgFaceInfo: AppointDefine.Face? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class HotFreshFeedList(
|
class HotFreshFeedList(
|
||||||
@SerialId(1) val msgFeeds: List<AppointDefine.HotUserFeed>? = null,
|
@ProtoId(1) val msgFeeds: List<AppointDefine.HotUserFeed>? = null,
|
||||||
@SerialId(2) val updateTime: Int = 0
|
@ProtoId(2) val updateTime: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RptInterestTag(
|
class RptInterestTag(
|
||||||
@SerialId(1) val interestTags: List<AppointDefine.InterestTag>? = null
|
@ProtoId(1) val interestTags: List<AppointDefine.InterestTag>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class AddressInfo(
|
class AddressInfo(
|
||||||
@SerialId(1) val companyZone: String = "",
|
@ProtoId(1) val companyZone: String = "",
|
||||||
@SerialId(2) val companyName: String = "",
|
@ProtoId(2) val companyName: String = "",
|
||||||
@SerialId(3) val companyAddr: String = "",
|
@ProtoId(3) val companyAddr: String = "",
|
||||||
@SerialId(4) val companyPicUrl: String = "",
|
@ProtoId(4) val companyPicUrl: String = "",
|
||||||
@SerialId(5) val companyUrl: String = "",
|
@ProtoId(5) val companyUrl: String = "",
|
||||||
@SerialId(6) val msgCompanyId: AppointDefine.ShopID? = null
|
@ProtoId(6) val msgCompanyId: AppointDefine.ShopID? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class PublisherInfo(
|
class PublisherInfo(
|
||||||
@SerialId(1) val tinyid: Long = 0L,
|
@ProtoId(1) val tinyid: Long = 0L,
|
||||||
@SerialId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val age: Int = 0,
|
@ProtoId(3) val age: Int = 0,
|
||||||
@SerialId(4) val gender: Int = 0,
|
@ProtoId(4) val gender: Int = 0,
|
||||||
@SerialId(5) val constellation: String = "",
|
@ProtoId(5) val constellation: String = "",
|
||||||
@SerialId(6) val profession: Int = 0,
|
@ProtoId(6) val profession: Int = 0,
|
||||||
@SerialId(7) val distance: String = "",
|
@ProtoId(7) val distance: String = "",
|
||||||
@SerialId(8) val marriage: Int = 0,
|
@ProtoId(8) val marriage: Int = 0,
|
||||||
@SerialId(9) val vipinfo: String = "",
|
@ProtoId(9) val vipinfo: String = "",
|
||||||
@SerialId(10) val recommend: Int = 0,
|
@ProtoId(10) val recommend: Int = 0,
|
||||||
@SerialId(11) val godflag: Int = 0,
|
@ProtoId(11) val godflag: Int = 0,
|
||||||
@SerialId(12) val chatflag: Int = 0,
|
@ProtoId(12) val chatflag: Int = 0,
|
||||||
@SerialId(13) val chatupCount: Int = 0,
|
@ProtoId(13) val chatupCount: Int = 0,
|
||||||
@SerialId(14) val charm: Int = 0,
|
@ProtoId(14) val charm: Int = 0,
|
||||||
@SerialId(15) val charmLevel: Int = 0,
|
@ProtoId(15) val charmLevel: Int = 0,
|
||||||
@SerialId(16) val pubNumber: Int = 0,
|
@ProtoId(16) val pubNumber: Int = 0,
|
||||||
@SerialId(17) val msgCommonLabel: AppointDefine.CommonLabel? = null,
|
@ProtoId(17) val msgCommonLabel: AppointDefine.CommonLabel? = null,
|
||||||
@SerialId(18) val recentVistorTime: Int = 0,
|
@ProtoId(18) val recentVistorTime: Int = 0,
|
||||||
@SerialId(19) val strangerDeclare: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(19) val strangerDeclare: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(20) val friendUin: Long = 0L,
|
@ProtoId(20) val friendUin: Long = 0L,
|
||||||
@SerialId(21) val historyFlag: Int = 0,
|
@ProtoId(21) val historyFlag: Int = 0,
|
||||||
@SerialId(22) val followflag: Long = 0L
|
@ProtoId(22) val followflag: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class HotUserFeed(
|
class HotUserFeed(
|
||||||
@SerialId(1) val feedId: String = "",
|
@ProtoId(1) val feedId: String = "",
|
||||||
@SerialId(2) val praiseCount: Int = 0,
|
@ProtoId(2) val praiseCount: Int = 0,
|
||||||
@SerialId(3) val publishUid: Long = 0L,
|
@ProtoId(3) val publishUid: Long = 0L,
|
||||||
@SerialId(4) val publishTime: Int = 0
|
@ProtoId(4) val publishTime: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class FreshFeedInfo(
|
class FreshFeedInfo(
|
||||||
@SerialId(1) val uin: Long = 0L,
|
@ProtoId(1) val uin: Long = 0L,
|
||||||
@SerialId(2) val time: Int = 0,
|
@ProtoId(2) val time: Int = 0,
|
||||||
@SerialId(3) val feedId: String = "",
|
@ProtoId(3) val feedId: String = "",
|
||||||
@SerialId(4) val feedType: Long = 0L
|
@ProtoId(4) val feedType: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class CommonLabel(
|
class CommonLabel(
|
||||||
@SerialId(1) val lableId: Int = 0,
|
@ProtoId(1) val lableId: Int = 0,
|
||||||
@SerialId(2) val lableMsgPre: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val lableMsgPre: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val lableMsgLast: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val lableMsgLast: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val interstName: List<ByteArray>? = null,
|
@ProtoId(4) val interstName: List<ByteArray>? = null,
|
||||||
@SerialId(5) val interstType: List<Int>? = null
|
@ProtoId(5) val interstType: List<Int>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Face(
|
class Face(
|
||||||
@SerialId(1) val index: Int = 0
|
@ProtoId(1) val index: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class StrangerInfo(
|
class StrangerInfo(
|
||||||
@SerialId(1) val tinyid: Long = 0L,
|
@ProtoId(1) val tinyid: Long = 0L,
|
||||||
@SerialId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val age: Int = 0,
|
@ProtoId(3) val age: Int = 0,
|
||||||
@SerialId(4) val gender: Int = 0,
|
@ProtoId(4) val gender: Int = 0,
|
||||||
@SerialId(5) val dating: Int = 0,
|
@ProtoId(5) val dating: Int = 0,
|
||||||
@SerialId(6) val listIdx: Int = 0,
|
@ProtoId(6) val listIdx: Int = 0,
|
||||||
@SerialId(7) val constellation: String = "",
|
@ProtoId(7) val constellation: String = "",
|
||||||
@SerialId(8) val profession: Int = 0,
|
@ProtoId(8) val profession: Int = 0,
|
||||||
@SerialId(9) val marriage: Int = 0,
|
@ProtoId(9) val marriage: Int = 0,
|
||||||
@SerialId(10) val vipinfo: String = "",
|
@ProtoId(10) val vipinfo: String = "",
|
||||||
@SerialId(11) val recommend: Int = 0,
|
@ProtoId(11) val recommend: Int = 0,
|
||||||
@SerialId(12) val godflag: Int = 0,
|
@ProtoId(12) val godflag: Int = 0,
|
||||||
@SerialId(13) val charm: Int = 0,
|
@ProtoId(13) val charm: Int = 0,
|
||||||
@SerialId(14) val charmLevel: Int = 0,
|
@ProtoId(14) val charmLevel: Int = 0,
|
||||||
@SerialId(15) val uin: Long = 0L
|
@ProtoId(15) val uin: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class HotTopic(
|
class HotTopic(
|
||||||
@SerialId(1) val id: Long = 0L,
|
@ProtoId(1) val id: Long = 0L,
|
||||||
@SerialId(2) val title: String = "",
|
@ProtoId(2) val title: String = "",
|
||||||
@SerialId(3) val topicType: Long = 0L,
|
@ProtoId(3) val topicType: Long = 0L,
|
||||||
@SerialId(4) val total: Long = 0L,
|
@ProtoId(4) val total: Long = 0L,
|
||||||
@SerialId(5) val times: Long = 0L,
|
@ProtoId(5) val times: Long = 0L,
|
||||||
@SerialId(6) val historyTimes: Long = 0L,
|
@ProtoId(6) val historyTimes: Long = 0L,
|
||||||
@SerialId(7) val bgUrl: String = "",
|
@ProtoId(7) val bgUrl: String = "",
|
||||||
@SerialId(8) val url: String = "",
|
@ProtoId(8) val url: String = "",
|
||||||
@SerialId(9) val extraInfo: String = ""
|
@ProtoId(9) val extraInfo: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class DateEvent(
|
class DateEvent(
|
||||||
@SerialId(1) val eventId: Long = 0L,
|
@ProtoId(1) val eventId: Long = 0L,
|
||||||
@SerialId(2) val time: Int = 0,
|
@ProtoId(2) val time: Int = 0,
|
||||||
@SerialId(3) val type: Int = 0,
|
@ProtoId(3) val type: Int = 0,
|
||||||
@SerialId(4) val msgUserInfo: AppointDefine.StrangerInfo? = null,
|
@ProtoId(4) val msgUserInfo: AppointDefine.StrangerInfo? = null,
|
||||||
@SerialId(5) val msgDateInfo: AppointDefine.AppointInfo? = null,
|
@ProtoId(5) val msgDateInfo: AppointDefine.AppointInfo? = null,
|
||||||
@SerialId(6) val attendIdx: Int = 0,
|
@ProtoId(6) val attendIdx: Int = 0,
|
||||||
@SerialId(7) val eventTips: String = "",
|
@ProtoId(7) val eventTips: String = "",
|
||||||
@SerialId(8) val msgComment: AppointDefine.DateComment? = null,
|
@ProtoId(8) val msgComment: AppointDefine.DateComment? = null,
|
||||||
@SerialId(9) val cancelEventId: Long = 0L
|
@ProtoId(9) val cancelEventId: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class AppointInfo(
|
class AppointInfo(
|
||||||
@SerialId(1) val msgAppointId: AppointDefine.AppointID? = null,
|
@ProtoId(1) val msgAppointId: AppointDefine.AppointID? = null,
|
||||||
@SerialId(2) val msgAppointment: AppointDefine.AppointContent? = null,
|
@ProtoId(2) val msgAppointment: AppointDefine.AppointContent? = null,
|
||||||
@SerialId(3) val appointStatus: Int = 0,
|
@ProtoId(3) val appointStatus: Int = 0,
|
||||||
@SerialId(4) val joinWording: String = "",
|
@ProtoId(4) val joinWording: String = "",
|
||||||
@SerialId(5) val viewWording: String = "",
|
@ProtoId(5) val viewWording: String = "",
|
||||||
@SerialId(6) val unreadCount: Int = 0,
|
@ProtoId(6) val unreadCount: Int = 0,
|
||||||
@SerialId(7) val owner: Int = 0,
|
@ProtoId(7) val owner: Int = 0,
|
||||||
@SerialId(8) val join: Int = 0,
|
@ProtoId(8) val join: Int = 0,
|
||||||
@SerialId(9) val view: Int = 0,
|
@ProtoId(9) val view: Int = 0,
|
||||||
@SerialId(10) val commentWording: String = "",
|
@ProtoId(10) val commentWording: String = "",
|
||||||
@SerialId(11) val commentNum: Int = 0,
|
@ProtoId(11) val commentNum: Int = 0,
|
||||||
@SerialId(12) val attendStatus: Int = 0,
|
@ProtoId(12) val attendStatus: Int = 0,
|
||||||
@SerialId(13) val msgAppointmentEx: AppointDefine.AppointInfoEx? = null
|
@ProtoId(13) val msgAppointmentEx: AppointDefine.AppointInfoEx? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class UserInfo(
|
class UserInfo(
|
||||||
@SerialId(1) val uin: Long = 0L,
|
@ProtoId(1) val uin: Long = 0L,
|
||||||
@SerialId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val age: Int = 0,
|
@ProtoId(3) val age: Int = 0,
|
||||||
@SerialId(4) val gender: Int = 0,
|
@ProtoId(4) val gender: Int = 0,
|
||||||
@SerialId(5) val avatar: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(5) val avatar: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ReplyInfo(
|
class ReplyInfo(
|
||||||
@SerialId(1) val commentId: String = "",
|
@ProtoId(1) val commentId: String = "",
|
||||||
@SerialId(2) val msgStrangerInfo: AppointDefine.StrangerInfo? = null
|
@ProtoId(2) val msgStrangerInfo: AppointDefine.StrangerInfo? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
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 {
|
internal class Vec0xd50 : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ExtSnsFrdData(
|
internal class ExtSnsFrdData(
|
||||||
@SerialId(1) val frdUin: Long = 0L,
|
@ProtoId(1) val frdUin: Long = 0L,
|
||||||
@SerialId(91001) val musicSwitch: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(91001) val musicSwitch: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(101001) val mutualmarkAlienation: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(101001) val mutualmarkAlienation: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(141001) val mutualmarkScore: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(141001) val mutualmarkScore: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(151001) val ksingSwitch: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(151001) val ksingSwitch: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(181001) val lbsShare: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(181001) val lbsShare: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class RspBody(
|
internal class RspBody(
|
||||||
@SerialId(1) val msgUpdateData: List<Vec0xd50.ExtSnsFrdData>? = null,
|
@ProtoId(1) val msgUpdateData: List<Vec0xd50.ExtSnsFrdData>? = null,
|
||||||
@SerialId(11) val over: Int = 0,
|
@ProtoId(11) val over: Int = 0,
|
||||||
@SerialId(12) val nextStart: Int = 0,
|
@ProtoId(12) val nextStart: Int = 0,
|
||||||
@SerialId(13) val uint64UnfinishedUins: List<Long>? = null
|
@ProtoId(13) val uint64UnfinishedUins: List<Long>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ReqBody(
|
internal class ReqBody(
|
||||||
@SerialId(1) val appid: Long = 0L,
|
@ProtoId(1) val appid: Long = 0L,
|
||||||
@SerialId(2) val maxPkgSize: Int = 0,
|
@ProtoId(2) val maxPkgSize: Int = 0,
|
||||||
@SerialId(3) val startTime: Int = 0,
|
@ProtoId(3) val startTime: Int = 0,
|
||||||
@SerialId(4) val startIndex: Int = 0,
|
@ProtoId(4) val startIndex: Int = 0,
|
||||||
@SerialId(5) val reqNum: Int = 0,
|
@ProtoId(5) val reqNum: Int = 0,
|
||||||
@SerialId(6) val uinList: List<Long>? = null,
|
@ProtoId(6) val uinList: List<Long>? = null,
|
||||||
@SerialId(91001) val reqMusicSwitch: Int = 0,
|
@ProtoId(91001) val reqMusicSwitch: Int = 0,
|
||||||
@SerialId(101001) val reqMutualmarkAlienation: Int = 0,
|
@ProtoId(101001) val reqMutualmarkAlienation: Int = 0,
|
||||||
@SerialId(141001) val reqMutualmarkScore: Int = 0,
|
@ProtoId(141001) val reqMutualmarkScore: Int = 0,
|
||||||
@SerialId(151001) val reqKsingSwitch: Int = 0,
|
@ProtoId(151001) val reqKsingSwitch: Int = 0,
|
||||||
@SerialId(181001) val reqMutualmarkLbsshare: Int = 0
|
@ProtoId(181001) val reqMutualmarkLbsshare: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class KSingRelationInfo(
|
internal class KSingRelationInfo(
|
||||||
@SerialId(1) val flag: Int = 0
|
@ProtoId(1) val flag: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,21 +59,21 @@ internal class Vec0xd50 : ProtoBuf {
|
|||||||
internal class Vec0xd6b : ProtoBuf {
|
internal class Vec0xd6b : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ReqBody(
|
internal class ReqBody(
|
||||||
@SerialId(1) val maxPkgSize: Int = 0,
|
@ProtoId(1) val maxPkgSize: Int = 0,
|
||||||
@SerialId(2) val startTime: Int = 0,
|
@ProtoId(2) val startTime: Int = 0,
|
||||||
@SerialId(11) val uinList: List<Long>? = null
|
@ProtoId(11) val uinList: List<Long>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class RspBody(
|
internal class RspBody(
|
||||||
@SerialId(11) val msgMutualmarkData: List<Vec0xd6b.MutualMarkData>? = null,
|
@ProtoId(11) val msgMutualmarkData: List<Vec0xd6b.MutualMarkData>? = null,
|
||||||
@SerialId(12) val uint64UnfinishedUins: List<Long>? = null
|
@ProtoId(12) val uint64UnfinishedUins: List<Long>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MutualMarkData(
|
internal class MutualMarkData(
|
||||||
@SerialId(1) val frdUin: Long = 0L,
|
@ProtoId(1) val frdUin: Long = 0L,
|
||||||
@SerialId(2) val result: Int = 0
|
@ProtoId(2) val result: Int = 0
|
||||||
// @SerialId(11) val mutualmarkInfo: List<Mutualmark.MutualMark>? = null
|
// @SerialId(11) val mutualmarkInfo: List<Mutualmark.MutualMark>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
@ -82,26 +82,26 @@ internal class Vec0xd6b : ProtoBuf {
|
|||||||
internal class Mutualmark : ProtoBuf {
|
internal class Mutualmark : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MutualmarkInfo(
|
internal class MutualmarkInfo(
|
||||||
@SerialId(1) val lastActionTime: Long = 0L,
|
@ProtoId(1) val lastActionTime: Long = 0L,
|
||||||
@SerialId(2) val level: Int = 0,
|
@ProtoId(2) val level: Int = 0,
|
||||||
@SerialId(3) val lastChangeTime: Long = 0L,
|
@ProtoId(3) val lastChangeTime: Long = 0L,
|
||||||
@SerialId(4) val continueDays: Int = 0,
|
@ProtoId(4) val continueDays: Int = 0,
|
||||||
@SerialId(5) val wildcardWording: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(5) val wildcardWording: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(6) val notifyTime: Long = 0L,
|
@ProtoId(6) val notifyTime: Long = 0L,
|
||||||
@SerialId(7) val iconStatus: Long = 0L,
|
@ProtoId(7) val iconStatus: Long = 0L,
|
||||||
@SerialId(8) val iconStatusEndTime: Long = 0L,
|
@ProtoId(8) val iconStatusEndTime: Long = 0L,
|
||||||
@SerialId(9) val closeFlag: Int = 0,
|
@ProtoId(9) val closeFlag: Int = 0,
|
||||||
@SerialId(10) val resourceInfo: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(10) val resourceInfo: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ResourceInfo17(
|
internal class ResourceInfo17(
|
||||||
@SerialId(1) val dynamicUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val dynamicUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val staticUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val staticUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val cartoonUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val cartoonUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val cartoonMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val cartoonMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val playCartoon: Int = 0,
|
@ProtoId(5) val playCartoon: Int = 0,
|
||||||
@SerialId(6) val word: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(6) val word: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
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 {
|
class GroupLabel : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class Label(
|
class Label(
|
||||||
@SerialId(1) val name: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val name: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val enumType: Int /* enum */ = 1,
|
@ProtoId(2) val enumType: Int /* enum */ = 1,
|
||||||
@SerialId(3) val textColor: GroupLabel.Color? = null,
|
@ProtoId(3) val textColor: GroupLabel.Color? = null,
|
||||||
@SerialId(4) val edgingColor: GroupLabel.Color? = null,
|
@ProtoId(4) val edgingColor: GroupLabel.Color? = null,
|
||||||
@SerialId(5) val labelAttr: Int = 0,
|
@ProtoId(5) val labelAttr: Int = 0,
|
||||||
@SerialId(6) val labelType: Int = 0
|
@ProtoId(6) val labelType: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RspBody(
|
class RspBody(
|
||||||
@SerialId(1) val error: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val error: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val groupInfo: List<GroupLabel.GroupInfo>? = null
|
@ProtoId(2) val groupInfo: List<GroupLabel.GroupInfo>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SourceId(
|
class SourceId(
|
||||||
@SerialId(1) val sourceId: Int = 0
|
@ProtoId(1) val sourceId: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class GroupInfo(
|
class GroupInfo(
|
||||||
@SerialId(1) val int32Result: Int = 0,
|
@ProtoId(1) val int32Result: Int = 0,
|
||||||
@SerialId(2) val groupCode: Long = 0L,
|
@ProtoId(2) val groupCode: Long = 0L,
|
||||||
@SerialId(3) val groupLabel: List<GroupLabel.Label>? = null
|
@ProtoId(3) val groupLabel: List<GroupLabel.Label>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Color(
|
class Color(
|
||||||
@SerialId(1) val r: Int = 0,
|
@ProtoId(1) val r: Int = 0,
|
||||||
@SerialId(2) val g: Int = 0,
|
@ProtoId(2) val g: Int = 0,
|
||||||
@SerialId(3) val b: Int = 0
|
@ProtoId(3) val b: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ReqBody(
|
class ReqBody(
|
||||||
@SerialId(1) val sourceId: GroupLabel.SourceId? = null,
|
@ProtoId(1) val sourceId: GroupLabel.SourceId? = null,
|
||||||
@SerialId(2) val uinInfo: GroupLabel.UinInfo? = null,
|
@ProtoId(2) val uinInfo: GroupLabel.UinInfo? = null,
|
||||||
@SerialId(3) val numberLabel: Int = 5,
|
@ProtoId(3) val numberLabel: Int = 5,
|
||||||
@SerialId(4) val groupCode: List<Long>? = null,
|
@ProtoId(4) val groupCode: List<Long>? = null,
|
||||||
@SerialId(5) val labelStyle: Int = 0
|
@ProtoId(5) val labelStyle: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class UinInfo(
|
class UinInfo(
|
||||||
@SerialId(1) val int64Longitude: Long = 0L,
|
@ProtoId(1) val int64Longitude: Long = 0L,
|
||||||
@SerialId(2) val int64Latitude: Long = 0L
|
@ProtoId(2) val int64Latitude: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import kotlinx.serialization.protobuf.ProtoNumberType
|
import kotlinx.serialization.protobuf.ProtoNumberType
|
||||||
import kotlinx.serialization.protobuf.ProtoType
|
import kotlinx.serialization.protobuf.ProtoType
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
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 {
|
class BdhExtinfo : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class CommFileExtReq(
|
class CommFileExtReq(
|
||||||
@SerialId(1) val actionType: Int = 0,
|
@ProtoId(1) val actionType: Int = 0,
|
||||||
@SerialId(2) val uuid: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(2) val uuid: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class CommFileExtRsp(
|
class CommFileExtRsp(
|
||||||
@SerialId(1) val int32Retcode: Int = 0,
|
@ProtoId(1) val int32Retcode: Int = 0,
|
||||||
@SerialId(2) val downloadUrl: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(2) val downloadUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class PicInfo(
|
class PicInfo(
|
||||||
@SerialId(1) val idx: Int = 0,
|
@ProtoId(1) val idx: Int = 0,
|
||||||
@SerialId(2) val size: Int = 0,
|
@ProtoId(2) val size: Int = 0,
|
||||||
@SerialId(3) val binMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val binMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val type: Int = 0
|
@ProtoId(4) val type: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class QQVoiceExtReq(
|
class QQVoiceExtReq(
|
||||||
@SerialId(1) val qid: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val qid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val fmt: Int = 0,
|
@ProtoId(2) val fmt: Int = 0,
|
||||||
@SerialId(3) val rate: Int = 0,
|
@ProtoId(3) val rate: Int = 0,
|
||||||
@SerialId(4) val bits: Int = 0,
|
@ProtoId(4) val bits: Int = 0,
|
||||||
@SerialId(5) val channel: Int = 0,
|
@ProtoId(5) val channel: Int = 0,
|
||||||
@SerialId(6) val pinyin: Int = 0
|
@ProtoId(6) val pinyin: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class QQVoiceExtRsp(
|
class QQVoiceExtRsp(
|
||||||
@SerialId(1) val qid: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val qid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val int32Retcode: Int = 0,
|
@ProtoId(2) val int32Retcode: Int = 0,
|
||||||
@SerialId(3) val msgResult: List<QQVoiceResult>? = null
|
@ProtoId(3) val msgResult: List<QQVoiceResult>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class QQVoiceResult(
|
class QQVoiceResult(
|
||||||
@SerialId(1) val text: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val text: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val pinyin: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val pinyin: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val source: Int = 0
|
@ProtoId(3) val source: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ShortVideoReqExtInfo(
|
class ShortVideoReqExtInfo(
|
||||||
@SerialId(1) val cmd: Int = 0,
|
@ProtoId(1) val cmd: Int = 0,
|
||||||
@SerialId(2) val sessionId: Long = 0L,
|
@ProtoId(2) val sessionId: Long = 0L,
|
||||||
@SerialId(3) val msgThumbinfo: PicInfo? = null,
|
@ProtoId(3) val msgThumbinfo: PicInfo? = null,
|
||||||
@SerialId(4) val msgVideoinfo: VideoInfo? = null,
|
@ProtoId(4) val msgVideoinfo: VideoInfo? = null,
|
||||||
@SerialId(5) val msgShortvideoSureReq: ShortVideoSureReqInfo? = null,
|
@ProtoId(5) val msgShortvideoSureReq: ShortVideoSureReqInfo? = null,
|
||||||
@SerialId(6) val boolIsMergeCmdBeforeData: Boolean = false
|
@ProtoId(6) val boolIsMergeCmdBeforeData: Boolean = false
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ShortVideoRspExtInfo(
|
class ShortVideoRspExtInfo(
|
||||||
@SerialId(1) val cmd: Int = 0,
|
@ProtoId(1) val cmd: Int = 0,
|
||||||
@SerialId(2) val sessionId: Long = 0L,
|
@ProtoId(2) val sessionId: Long = 0L,
|
||||||
@SerialId(3) val int32Retcode: Int = 0,
|
@ProtoId(3) val int32Retcode: Int = 0,
|
||||||
@SerialId(4) val errinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val errinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val msgThumbinfo: PicInfo? = null,
|
@ProtoId(5) val msgThumbinfo: PicInfo? = null,
|
||||||
@SerialId(6) val msgVideoinfo: VideoInfo? = null,
|
@ProtoId(6) val msgVideoinfo: VideoInfo? = null,
|
||||||
@SerialId(7) val msgShortvideoSureRsp: ShortVideoSureRspInfo? = null,
|
@ProtoId(7) val msgShortvideoSureRsp: ShortVideoSureRspInfo? = null,
|
||||||
@SerialId(8) val retryFlag: Int = 0
|
@ProtoId(8) val retryFlag: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ShortVideoSureReqInfo(
|
class ShortVideoSureReqInfo(
|
||||||
@SerialId(1) val fromuin: Long = 0L,
|
@ProtoId(1) val fromuin: Long = 0L,
|
||||||
@SerialId(2) val chatType: Int = 0,
|
@ProtoId(2) val chatType: Int = 0,
|
||||||
@SerialId(3) val touin: Long = 0L,
|
@ProtoId(3) val touin: Long = 0L,
|
||||||
@SerialId(4) val groupCode: Long = 0L,
|
@ProtoId(4) val groupCode: Long = 0L,
|
||||||
@SerialId(5) val clientType: Int = 0,
|
@ProtoId(5) val clientType: Int = 0,
|
||||||
@SerialId(6) val msgThumbinfo: PicInfo? = null,
|
@ProtoId(6) val msgThumbinfo: PicInfo? = null,
|
||||||
@SerialId(7) val msgMergeVideoinfo: List<VideoInfo>? = null,
|
@ProtoId(7) val msgMergeVideoinfo: List<VideoInfo>? = null,
|
||||||
@SerialId(8) val msgDropVideoinfo: List<VideoInfo>? = null,
|
@ProtoId(8) val msgDropVideoinfo: List<VideoInfo>? = null,
|
||||||
@SerialId(9) val businessType: Int = 0,
|
@ProtoId(9) val businessType: Int = 0,
|
||||||
@SerialId(10) val subBusinessType: Int = 0
|
@ProtoId(10) val subBusinessType: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ShortVideoSureRspInfo(
|
class ShortVideoSureRspInfo(
|
||||||
@SerialId(1) val fileid: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val fileid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val ukey: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val ukey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val msgVideoinfo: VideoInfo? = null,
|
@ProtoId(3) val msgVideoinfo: VideoInfo? = null,
|
||||||
@SerialId(4) val mergeCost: Int = 0
|
@ProtoId(4) val mergeCost: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -111,31 +111,31 @@ class BdhExtinfo : ProtoBuf {
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class StoryVideoExtRsp(
|
class StoryVideoExtRsp(
|
||||||
@SerialId(1) val int32Retcode: Int = 0,
|
@ProtoId(1) val int32Retcode: Int = 0,
|
||||||
@SerialId(2) val msg: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val msg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val cdnUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val cdnUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val fileKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val fileId: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(5) val fileId: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class UploadPicExtInfo(
|
class UploadPicExtInfo(
|
||||||
@SerialId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val fileResid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val downloadUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val downloadUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val thumbDownloadUrl: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(3) val thumbDownloadUrl: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class VideoInfo(
|
class VideoInfo(
|
||||||
@SerialId(1) val idx: Int = 0,
|
@ProtoId(1) val idx: Int = 0,
|
||||||
@SerialId(2) val size: Int = 0,
|
@ProtoId(2) val size: Int = 0,
|
||||||
@SerialId(3) val binMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val binMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val format: Int = 0,
|
@ProtoId(4) val format: Int = 0,
|
||||||
@SerialId(5) val resLen: Int = 0,
|
@ProtoId(5) val resLen: Int = 0,
|
||||||
@SerialId(6) val resWidth: Int = 0,
|
@ProtoId(6) val resWidth: Int = 0,
|
||||||
@SerialId(7) val time: Int = 0,
|
@ProtoId(7) val time: Int = 0,
|
||||||
@SerialId(8) val starttime: Long = 0L,
|
@ProtoId(8) val starttime: Long = 0L,
|
||||||
@SerialId(9) val isAudio: Int = 0
|
@ProtoId(9) val isAudio: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,142 +143,142 @@ class BdhExtinfo : ProtoBuf {
|
|||||||
class CSDataHighwayHead : ProtoBuf {
|
class CSDataHighwayHead : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class C2CCommonExtendinfo(
|
class C2CCommonExtendinfo(
|
||||||
@SerialId(1) val infoId: Int = 0,
|
@ProtoId(1) val infoId: Int = 0,
|
||||||
@SerialId(2) val msgFilterExtendinfo: FilterExtendinfo? = null
|
@ProtoId(2) val msgFilterExtendinfo: FilterExtendinfo? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class DataHighwayHead(
|
class DataHighwayHead(
|
||||||
@SerialId(1) val version: Int = 0,
|
@ProtoId(1) val version: Int = 0,
|
||||||
@SerialId(2) val uin: String = "", // yes
|
@ProtoId(2) val uin: String = "", // yes
|
||||||
@SerialId(3) val command: String = "",
|
@ProtoId(3) val command: String = "",
|
||||||
@SerialId(4) val seq: Int = 0,
|
@ProtoId(4) val seq: Int = 0,
|
||||||
@SerialId(5) val retryTimes: Int = 0,
|
@ProtoId(5) val retryTimes: Int = 0,
|
||||||
@SerialId(6) val appid: Int = 0,
|
@ProtoId(6) val appid: Int = 0,
|
||||||
@SerialId(7) val dataflag: Int = 0,
|
@ProtoId(7) val dataflag: Int = 0,
|
||||||
@SerialId(8) val commandId: Int = 0,
|
@ProtoId(8) val commandId: Int = 0,
|
||||||
@SerialId(9) val buildVer: String = "",
|
@ProtoId(9) val buildVer: String = "",
|
||||||
@SerialId(10) val localeId: Int = 0
|
@ProtoId(10) val localeId: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class DataHole(
|
class DataHole(
|
||||||
@SerialId(1) val begin: Long = 0L,
|
@ProtoId(1) val begin: Long = 0L,
|
||||||
@SerialId(2) val end: Long = 0L
|
@ProtoId(2) val end: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class FilterExtendinfo(
|
class FilterExtendinfo(
|
||||||
@SerialId(1) val filterFlag: Int = 0,
|
@ProtoId(1) val filterFlag: Int = 0,
|
||||||
@SerialId(2) val msgImageFilterRequest: ImageFilterRequest? = null
|
@ProtoId(2) val msgImageFilterRequest: ImageFilterRequest? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class FilterStyle(
|
class FilterStyle(
|
||||||
@SerialId(1) val styleId: Int = 0,
|
@ProtoId(1) val styleId: Int = 0,
|
||||||
@SerialId(2) val styleName: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(2) val styleName: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ImageFilterRequest(
|
class ImageFilterRequest(
|
||||||
@SerialId(1) val sessionId: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val sessionId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val clientIp: Int = 0,
|
@ProtoId(2) val clientIp: Int = 0,
|
||||||
@SerialId(3) val uin: Long = 0L,
|
@ProtoId(3) val uin: Long = 0L,
|
||||||
@SerialId(4) val style: FilterStyle? = null,
|
@ProtoId(4) val style: FilterStyle? = null,
|
||||||
@SerialId(5) val width: Int = 0,
|
@ProtoId(5) val width: Int = 0,
|
||||||
@SerialId(6) val height: Int = 0,
|
@ProtoId(6) val height: Int = 0,
|
||||||
@SerialId(7) val imageData: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(7) val imageData: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ImageFilterResponse(
|
class ImageFilterResponse(
|
||||||
@SerialId(1) val retCode: Int = 0,
|
@ProtoId(1) val retCode: Int = 0,
|
||||||
@SerialId(2) val imageData: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val imageData: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val costTime: Int = 0
|
@ProtoId(3) val costTime: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class LoginSigHead(
|
class LoginSigHead(
|
||||||
@SerialId(1) val loginsigType: Int = 0,
|
@ProtoId(1) val loginsigType: Int = 0,
|
||||||
@SerialId(2) val loginsig: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(2) val loginsig: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class NewServiceTicket(
|
class NewServiceTicket(
|
||||||
@SerialId(1) val signature: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val signature: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val ukey: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(2) val ukey: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class PicInfoExt(
|
class PicInfoExt(
|
||||||
@SerialId(1) val picWidth: Int = 0,
|
@ProtoId(1) val picWidth: Int = 0,
|
||||||
@SerialId(2) val picHeight: Int = 0,
|
@ProtoId(2) val picHeight: Int = 0,
|
||||||
@SerialId(3) val picFlag: Int = 0,
|
@ProtoId(3) val picFlag: Int = 0,
|
||||||
@SerialId(4) val busiType: Int = 0,
|
@ProtoId(4) val busiType: Int = 0,
|
||||||
@SerialId(5) val srcTerm: Int = 0,
|
@ProtoId(5) val srcTerm: Int = 0,
|
||||||
@SerialId(6) val platType: Int = 0,
|
@ProtoId(6) val platType: Int = 0,
|
||||||
@SerialId(7) val netType: Int = 0,
|
@ProtoId(7) val netType: Int = 0,
|
||||||
@SerialId(8) val imgType: Int = 0,
|
@ProtoId(8) val imgType: Int = 0,
|
||||||
@SerialId(9) val appPicType: Int = 0
|
@ProtoId(9) val appPicType: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class PicRspExtInfo(
|
class PicRspExtInfo(
|
||||||
@SerialId(1) val skey: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val skey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val clientIp: Int = 0,
|
@ProtoId(2) val clientIp: Int = 0,
|
||||||
@SerialId(3) val upOffset: Long = 0L,
|
@ProtoId(3) val upOffset: Long = 0L,
|
||||||
@SerialId(4) val blockSize: Long = 0L
|
@ProtoId(4) val blockSize: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class QueryHoleRsp(
|
class QueryHoleRsp(
|
||||||
@SerialId(1) val result: Int = 0,
|
@ProtoId(1) val result: Int = 0,
|
||||||
@SerialId(2) val dataHole: List<DataHole>? = null,
|
@ProtoId(2) val dataHole: List<DataHole>? = null,
|
||||||
@SerialId(3) val boolCompFlag: Boolean = false
|
@ProtoId(3) val boolCompFlag: Boolean = false
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ReqDataHighwayHead(
|
class ReqDataHighwayHead(
|
||||||
@SerialId(1) val msgBasehead: DataHighwayHead? = null,
|
@ProtoId(1) val msgBasehead: DataHighwayHead? = null,
|
||||||
@SerialId(2) val msgSeghead: SegHead? = null,
|
@ProtoId(2) val msgSeghead: SegHead? = null,
|
||||||
@SerialId(3) val reqExtendinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val reqExtendinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val timestamp: Long = 0L,
|
@ProtoId(4) val timestamp: Long = 0L,
|
||||||
@SerialId(5) val msgLoginSigHead: LoginSigHead? = null
|
@ProtoId(5) val msgLoginSigHead: LoginSigHead? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RspBody(
|
class RspBody(
|
||||||
@SerialId(1) val msgQueryHoleRsp: QueryHoleRsp? = null
|
@ProtoId(1) val msgQueryHoleRsp: QueryHoleRsp? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RspDataHighwayHead(
|
class RspDataHighwayHead(
|
||||||
@SerialId(1) val msgBasehead: DataHighwayHead? = null,
|
@ProtoId(1) val msgBasehead: DataHighwayHead? = null,
|
||||||
@SerialId(2) val msgSeghead: SegHead? = null,
|
@ProtoId(2) val msgSeghead: SegHead? = null,
|
||||||
@SerialId(3) val errorCode: Int = 0,
|
@ProtoId(3) val errorCode: Int = 0,
|
||||||
@SerialId(4) val allowRetry: Int = 0,
|
@ProtoId(4) val allowRetry: Int = 0,
|
||||||
@SerialId(5) val cachecost: Int = 0,
|
@ProtoId(5) val cachecost: Int = 0,
|
||||||
@SerialId(6) val htcost: Int = 0,
|
@ProtoId(6) val htcost: Int = 0,
|
||||||
@SerialId(7) val rspExtendinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(7) val rspExtendinfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(8) val timestamp: Long = 0L,
|
@ProtoId(8) val timestamp: Long = 0L,
|
||||||
@SerialId(9) val range: Long = 0L,
|
@ProtoId(9) val range: Long = 0L,
|
||||||
@SerialId(10) val isReset: Int = 0
|
@ProtoId(10) val isReset: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SegHead(
|
class SegHead(
|
||||||
@SerialId(1) val serviceid: Int = 0,
|
@ProtoId(1) val serviceid: Int = 0,
|
||||||
@SerialId(2) val filesize: Long = 0L,
|
@ProtoId(2) val filesize: Long = 0L,
|
||||||
@SerialId(3) val dataoffset: Long = 0L,
|
@ProtoId(3) val dataoffset: Long = 0L,
|
||||||
@SerialId(4) val datalength: Int = 0,
|
@ProtoId(4) val datalength: Int = 0,
|
||||||
@SerialId(5) val rtcode: Int = 0,
|
@ProtoId(5) val rtcode: Int = 0,
|
||||||
@SerialId(6) val serviceticket: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(6) val serviceticket: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(7) val flag: Int = 0,
|
@ProtoId(7) val flag: Int = 0,
|
||||||
@SerialId(8) val md5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(8) val md5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(9) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(9) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(10) val cacheAddr: Int = 0,
|
@ProtoId(10) val cacheAddr: Int = 0,
|
||||||
@SerialId(11) val queryTimes: Int = 0,
|
@ProtoId(11) val queryTimes: Int = 0,
|
||||||
@SerialId(12) val updateCacheip: Int = 0
|
@ProtoId(12) val updateCacheip: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,31 +286,31 @@ class CSDataHighwayHead : ProtoBuf {
|
|||||||
class HwConfigPersistentPB : ProtoBuf {
|
class HwConfigPersistentPB : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class HwConfigItemPB(
|
class HwConfigItemPB(
|
||||||
@SerialId(1) val ingKey: String = "",
|
@ProtoId(1) val ingKey: String = "",
|
||||||
@SerialId(2) val endPointList: List<HwEndPointPB>? = null
|
@ProtoId(2) val endPointList: List<HwEndPointPB>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class HwConfigPB(
|
class HwConfigPB(
|
||||||
@SerialId(1) val configItemList: List<HwConfigItemPB>? = null,
|
@ProtoId(1) val configItemList: List<HwConfigItemPB>? = null,
|
||||||
@SerialId(2) val netSegConfList: List<HwNetSegConfPB>? = null,
|
@ProtoId(2) val netSegConfList: List<HwNetSegConfPB>? = null,
|
||||||
@SerialId(3) val shortVideoNetConf: List<HwNetSegConfPB>? = null,
|
@ProtoId(3) val shortVideoNetConf: List<HwNetSegConfPB>? = null,
|
||||||
@SerialId(4) val configItemListIp6: List<HwConfigItemPB>? = null
|
@ProtoId(4) val configItemListIp6: List<HwConfigItemPB>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class HwEndPointPB(
|
class HwEndPointPB(
|
||||||
@SerialId(1) val ingHost: String = "",
|
@ProtoId(1) val ingHost: String = "",
|
||||||
@SerialId(2) val int32Port: Int = 0,
|
@ProtoId(2) val int32Port: Int = 0,
|
||||||
@SerialId(3) val int64Timestampe: Long = 0L
|
@ProtoId(3) val int64Timestampe: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class HwNetSegConfPB(
|
class HwNetSegConfPB(
|
||||||
@SerialId(1) val int64NetType: Long = 0L,
|
@ProtoId(1) val int64NetType: Long = 0L,
|
||||||
@SerialId(2) val int64SegSize: Long = 0L,
|
@ProtoId(2) val int64SegSize: Long = 0L,
|
||||||
@SerialId(3) val int64SegNum: Long = 0L,
|
@ProtoId(3) val int64SegNum: Long = 0L,
|
||||||
@SerialId(4) val int64CurConnNum: Long = 0L
|
@ProtoId(4) val int64CurConnNum: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,8 +318,8 @@ class HwConfigPersistentPB : ProtoBuf {
|
|||||||
class HwSessionInfoPersistentPB : ProtoBuf {
|
class HwSessionInfoPersistentPB : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class HwSessionInfoPB(
|
class HwSessionInfoPB(
|
||||||
@SerialId(1) val httpconnSigSession: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val httpconnSigSession: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val sessionKey: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(2) val sessionKey: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,137 +327,137 @@ class HwSessionInfoPersistentPB : ProtoBuf {
|
|||||||
class Subcmd0x501 : ProtoBuf {
|
class Subcmd0x501 : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class ReqBody(
|
class ReqBody(
|
||||||
@SerialId(1281) val msgSubcmd0x501ReqBody: SubCmd0x501ReqBody? = null
|
@ProtoId(1281) val msgSubcmd0x501ReqBody: SubCmd0x501ReqBody? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RspBody(
|
class RspBody(
|
||||||
@SerialId(1281) val msgSubcmd0x501RspBody: SubCmd0x501Rspbody? = null
|
@ProtoId(1281) val msgSubcmd0x501RspBody: SubCmd0x501Rspbody? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SubCmd0x501ReqBody(
|
class SubCmd0x501ReqBody(
|
||||||
@SerialId(1) val uin: Long = 0L,
|
@ProtoId(1) val uin: Long = 0L,
|
||||||
@SerialId(2) val idcId: Int = 0,
|
@ProtoId(2) val idcId: Int = 0,
|
||||||
@SerialId(3) val appid: Int = 0,
|
@ProtoId(3) val appid: Int = 0,
|
||||||
@SerialId(4) val loginSigType: Int = 0,
|
@ProtoId(4) val loginSigType: Int = 0,
|
||||||
@SerialId(5) val loginSigTicket: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(5) val loginSigTicket: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(6) val requestFlag: Int = 0,
|
@ProtoId(6) val requestFlag: Int = 0,
|
||||||
@SerialId(7) val uint32ServiceTypes: List<Int>? = null,
|
@ProtoId(7) val uint32ServiceTypes: List<Int>? = null,
|
||||||
@SerialId(8) val bid: Int = 0,
|
@ProtoId(8) val bid: Int = 0,
|
||||||
@SerialId(9) val term: Int = 0,
|
@ProtoId(9) val term: Int = 0,
|
||||||
@SerialId(10) val plat: Int = 0,
|
@ProtoId(10) val plat: Int = 0,
|
||||||
@SerialId(11) val net: Int = 0,
|
@ProtoId(11) val net: Int = 0,
|
||||||
@SerialId(12) val caller: Int = 0
|
@ProtoId(12) val caller: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SubCmd0x501Rspbody(
|
class SubCmd0x501Rspbody(
|
||||||
@SerialId(1) val httpconnSigSession: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val httpconnSigSession: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val sessionKey: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val sessionKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val msgHttpconnAddrs: List<SrvAddrs>? = null,
|
@ProtoId(3) val msgHttpconnAddrs: List<SrvAddrs>? = null,
|
||||||
@SerialId(4) val preConnection: Int = 0,
|
@ProtoId(4) val preConnection: Int = 0,
|
||||||
@SerialId(5) val csConn: Int = 0,
|
@ProtoId(5) val csConn: Int = 0,
|
||||||
@SerialId(6) val msgIpLearnConf: IpLearnConf? = null,
|
@ProtoId(6) val msgIpLearnConf: IpLearnConf? = null,
|
||||||
@SerialId(7) val msgDynTimeoutConf: DynTimeOutConf? = null,
|
@ProtoId(7) val msgDynTimeoutConf: DynTimeOutConf? = null,
|
||||||
@SerialId(8) val msgOpenUpConf: OpenUpConf? = null,
|
@ProtoId(8) val msgOpenUpConf: OpenUpConf? = null,
|
||||||
@SerialId(9) val msgDownloadEncryptConf: DownloadEncryptConf? = null,
|
@ProtoId(9) val msgDownloadEncryptConf: DownloadEncryptConf? = null,
|
||||||
@SerialId(10) val msgShortVideoConf: ShortVideoConf? = null,
|
@ProtoId(10) val msgShortVideoConf: ShortVideoConf? = null,
|
||||||
@SerialId(11) val msgPtvConf: PTVConf? = null
|
@ProtoId(11) val msgPtvConf: PTVConf? = null
|
||||||
) : ProtoBuf {
|
) : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class DownloadEncryptConf(
|
class DownloadEncryptConf(
|
||||||
@SerialId(1) val boolEnableEncryptRequest: Boolean = false,
|
@ProtoId(1) val boolEnableEncryptRequest: Boolean = false,
|
||||||
@SerialId(2) val boolEnableEncryptedPic: Boolean = false,
|
@ProtoId(2) val boolEnableEncryptedPic: Boolean = false,
|
||||||
@SerialId(3) val ctrlFlag: Int = 0
|
@ProtoId(3) val ctrlFlag: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class DynTimeOutConf(
|
class DynTimeOutConf(
|
||||||
@SerialId(1) val tbase2g: Int = 0,
|
@ProtoId(1) val tbase2g: Int = 0,
|
||||||
@SerialId(2) val tbase3g: Int = 0,
|
@ProtoId(2) val tbase3g: Int = 0,
|
||||||
@SerialId(3) val tbase4g: Int = 0,
|
@ProtoId(3) val tbase4g: Int = 0,
|
||||||
@SerialId(4) val tbaseWifi: Int = 0,
|
@ProtoId(4) val tbaseWifi: Int = 0,
|
||||||
@SerialId(5) val torg2g: Int = 0,
|
@ProtoId(5) val torg2g: Int = 0,
|
||||||
@SerialId(6) val torg3g: Int = 0,
|
@ProtoId(6) val torg3g: Int = 0,
|
||||||
@SerialId(7) val torg4g: Int = 0,
|
@ProtoId(7) val torg4g: Int = 0,
|
||||||
@SerialId(8) val torgWifi: Int = 0,
|
@ProtoId(8) val torgWifi: Int = 0,
|
||||||
@SerialId(9) val maxTimeout: Int = 0,
|
@ProtoId(9) val maxTimeout: Int = 0,
|
||||||
@SerialId(10) val enableDynTimeout: Int = 0,
|
@ProtoId(10) val enableDynTimeout: Int = 0,
|
||||||
@SerialId(11) val maxTimeout2g: Int = 0,
|
@ProtoId(11) val maxTimeout2g: Int = 0,
|
||||||
@SerialId(12) val maxTimeout3g: Int = 0,
|
@ProtoId(12) val maxTimeout3g: Int = 0,
|
||||||
@SerialId(13) val maxTimeout4g: Int = 0,
|
@ProtoId(13) val maxTimeout4g: Int = 0,
|
||||||
@SerialId(14) val maxTimeoutWifi: Int = 0,
|
@ProtoId(14) val maxTimeoutWifi: Int = 0,
|
||||||
@SerialId(15) val hbTimeout2g: Int = 0,
|
@ProtoId(15) val hbTimeout2g: Int = 0,
|
||||||
@SerialId(16) val hbTimeout3g: Int = 0,
|
@ProtoId(16) val hbTimeout3g: Int = 0,
|
||||||
@SerialId(17) val hbTimeout4g: Int = 0,
|
@ProtoId(17) val hbTimeout4g: Int = 0,
|
||||||
@SerialId(18) val hbTimeoutWifi: Int = 0,
|
@ProtoId(18) val hbTimeoutWifi: Int = 0,
|
||||||
@SerialId(19) val hbTimeoutDefault: Int = 0
|
@ProtoId(19) val hbTimeoutDefault: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Ip6Addr(
|
class Ip6Addr(
|
||||||
@SerialId(1) val type: Int = 0,
|
@ProtoId(1) val type: Int = 0,
|
||||||
@SerialId(2) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val ip6: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val port: Int = 0,
|
@ProtoId(3) val port: Int = 0,
|
||||||
@SerialId(4) val area: Int = 0,
|
@ProtoId(4) val area: Int = 0,
|
||||||
@SerialId(5) val sameIsp: Int = 0
|
@ProtoId(5) val sameIsp: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class IpAddr(
|
class IpAddr(
|
||||||
@SerialId(1) val type: Int = 0,
|
@ProtoId(1) val type: Int = 0,
|
||||||
@ProtoType(ProtoNumberType.FIXED) @SerialId(2) val ip: Int = 0,
|
@ProtoType(ProtoNumberType.FIXED) @ProtoId(2) val ip: Int = 0,
|
||||||
@SerialId(3) val port: Int = 0,
|
@ProtoId(3) val port: Int = 0,
|
||||||
@SerialId(4) val area: Int = 0,
|
@ProtoId(4) val area: Int = 0,
|
||||||
@SerialId(5) val sameIsp: Int = 0
|
@ProtoId(5) val sameIsp: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class IpLearnConf(
|
class IpLearnConf(
|
||||||
@SerialId(1) val refreshCachedIp: Int = 0,
|
@ProtoId(1) val refreshCachedIp: Int = 0,
|
||||||
@SerialId(2) val enableIpLearn: Int = 0
|
@ProtoId(2) val enableIpLearn: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class NetSegConf(
|
class NetSegConf(
|
||||||
@SerialId(1) val netType: Int = 0,
|
@ProtoId(1) val netType: Int = 0,
|
||||||
@SerialId(2) val segsize: Int = 0,
|
@ProtoId(2) val segsize: Int = 0,
|
||||||
@SerialId(3) val segnum: Int = 0,
|
@ProtoId(3) val segnum: Int = 0,
|
||||||
@SerialId(4) val curconnnum: Int = 0
|
@ProtoId(4) val curconnnum: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class OpenUpConf(
|
class OpenUpConf(
|
||||||
@SerialId(1) val boolEnableOpenup: Boolean = false,
|
@ProtoId(1) val boolEnableOpenup: Boolean = false,
|
||||||
@SerialId(2) val preSendSegnum: Int = 0,
|
@ProtoId(2) val preSendSegnum: Int = 0,
|
||||||
@SerialId(3) val preSendSegnum3g: Int = 0,
|
@ProtoId(3) val preSendSegnum3g: Int = 0,
|
||||||
@SerialId(4) val preSendSegnum4g: Int = 0,
|
@ProtoId(4) val preSendSegnum4g: Int = 0,
|
||||||
@SerialId(5) val preSendSegnumWifi: Int = 0
|
@ProtoId(5) val preSendSegnumWifi: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class PTVConf(
|
class PTVConf(
|
||||||
@SerialId(1) val channelType: Int = 0,
|
@ProtoId(1) val channelType: Int = 0,
|
||||||
@SerialId(2) val msgNetsegconf: List<NetSegConf>? = null,
|
@ProtoId(2) val msgNetsegconf: List<NetSegConf>? = null,
|
||||||
@SerialId(3) val boolOpenHardwareCodec: Boolean = false
|
@ProtoId(3) val boolOpenHardwareCodec: Boolean = false
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ShortVideoConf(
|
class ShortVideoConf(
|
||||||
@SerialId(1) val channelType: Int = 0,
|
@ProtoId(1) val channelType: Int = 0,
|
||||||
@SerialId(2) val msgNetsegconf: List<NetSegConf>? = null,
|
@ProtoId(2) val msgNetsegconf: List<NetSegConf>? = null,
|
||||||
@SerialId(3) val boolOpenHardwareCodec: Boolean = false,
|
@ProtoId(3) val boolOpenHardwareCodec: Boolean = false,
|
||||||
@SerialId(4) val boolSendAheadSignal: Boolean = false
|
@ProtoId(4) val boolSendAheadSignal: Boolean = false
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SrvAddrs(
|
class SrvAddrs(
|
||||||
@SerialId(1) val serviceType: Int = 0,
|
@ProtoId(1) val serviceType: Int = 0,
|
||||||
@SerialId(2) val msgAddrs: List<IpAddr>? = null,
|
@ProtoId(2) val msgAddrs: List<IpAddr>? = null,
|
||||||
@SerialId(3) val fragmentSize: Int = 0,
|
@ProtoId(3) val fragmentSize: Int = 0,
|
||||||
@SerialId(4) val msgNetsegconf: List<NetSegConf>? = null,
|
@ProtoId(4) val msgNetsegconf: List<NetSegConf>? = null,
|
||||||
@SerialId(5) val msgAddrsV6: List<Ip6Addr>? = null
|
@ProtoId(5) val msgAddrsV6: List<Ip6Addr>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||||
|
|
||||||
@ -20,22 +20,22 @@ interface ImgReq : ProtoBuf
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class GetImgUrlReq(
|
internal class GetImgUrlReq(
|
||||||
@SerialId(1) val srcUni: Int,
|
@ProtoId(1) val srcUni: Int,
|
||||||
@SerialId(2) val dstUni: Int,
|
@ProtoId(2) val dstUni: Int,
|
||||||
@SerialId(3) val fileResID: String,//UUID
|
@ProtoId(3) val fileResID: String,//UUID
|
||||||
/**
|
/**
|
||||||
* UUID例子: 没有找到
|
* UUID例子: 没有找到
|
||||||
*/
|
*/
|
||||||
@SerialId(4) val urlFlag: Int = 1,
|
@ProtoId(4) val urlFlag: Int = 1,
|
||||||
//5 unknown, 好像没用
|
//5 unknown, 好像没用
|
||||||
@SerialId(6) val urlType: Int = 4,
|
@ProtoId(6) val urlType: Int = 4,
|
||||||
@SerialId(7) val requestTerm: Int = 5,//确定
|
@ProtoId(7) val requestTerm: Int = 5,//确定
|
||||||
@SerialId(8) val requestPlatformType: Int = 9,//确定
|
@ProtoId(8) val requestPlatformType: Int = 9,//确定
|
||||||
@SerialId(9) val srcFileType: Int = 1,//2=ftn,1=picplatform,255
|
@ProtoId(9) val srcFileType: Int = 1,//2=ftn,1=picplatform,255
|
||||||
@SerialId(10) val innerIP: Int = 0,//确定
|
@ProtoId(10) val innerIP: Int = 0,//确定
|
||||||
@SerialId(11) val addressBook: Int = 0,//[ChatType.internalID]== 1006为1[为CONTACT时] 我觉得发0没问题
|
@ProtoId(11) val addressBook: Int = 0,//[ChatType.internalID]== 1006为1[为CONTACT时] 我觉得发0没问题
|
||||||
@SerialId(12) val buType: Int = 1,//确定
|
@ProtoId(12) val buType: Int = 1,//确定
|
||||||
@SerialId(13) val buildVer: String = "8.2.0.1296",//版本号
|
@ProtoId(13) val buildVer: String = "8.2.7.4410",//版本号
|
||||||
@SerialId(14) val timestamp: Int = currentTimeSeconds.toInt(),//(pic_up_timestamp)
|
@ProtoId(14) val timestamp: Int = currentTimeSeconds.toInt(),//(pic_up_timestamp)
|
||||||
@SerialId(15) val requestTransferType: Int = 1
|
@ProtoId(15) val requestTransferType: Int = 1
|
||||||
) : ImgReq
|
) : ImgReq
|
File diff suppressed because it is too large
Load Diff
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
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 {
|
internal class MsgComm : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class AppShareInfo(
|
internal class AppShareInfo(
|
||||||
@SerialId(1) val appshareId: Int = 0,
|
@ProtoId(1) val appshareId: Int = 0,
|
||||||
@SerialId(2) val appshareCookie: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val appshareCookie: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val appshareResource: PluginInfo? = null
|
@ProtoId(3) val appshareResource: PluginInfo? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class C2CTmpMsgHead(
|
internal class C2CTmpMsgHead(
|
||||||
@SerialId(1) val c2cType: Int = 0,
|
@ProtoId(1) val c2cType: Int = 0,
|
||||||
@SerialId(2) val serviceType: Int = 0,
|
@ProtoId(2) val serviceType: Int = 0,
|
||||||
@SerialId(3) val groupUin: Long = 0L,
|
@ProtoId(3) val groupUin: Long = 0L,
|
||||||
@SerialId(4) val groupCode: Long = 0L,
|
@ProtoId(4) val groupCode: Long = 0L,
|
||||||
@SerialId(5) val sig: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(5) val sig: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(6) val sigType: Int = 0,
|
@ProtoId(6) val sigType: Int = 0,
|
||||||
@SerialId(7) val fromPhone: String = "",
|
@ProtoId(7) val fromPhone: String = "",
|
||||||
@SerialId(8) val toPhone: String = "",
|
@ProtoId(8) val toPhone: String = "",
|
||||||
@SerialId(9) val lockDisplay: Int = 0,
|
@ProtoId(9) val lockDisplay: Int = 0,
|
||||||
@SerialId(10) val directionFlag: Int = 0,
|
@ProtoId(10) val directionFlag: Int = 0,
|
||||||
@SerialId(11) val reserved: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(11) val reserved: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ContentHead(
|
internal class ContentHead(
|
||||||
@SerialId(1) val pkgNum: Int = 0,
|
@ProtoId(1) val pkgNum: Int = 0,
|
||||||
@SerialId(2) val pkgIndex: Int = 0,
|
@ProtoId(2) val pkgIndex: Int = 0,
|
||||||
@SerialId(3) val divSeq: Int = 0,
|
@ProtoId(3) val divSeq: Int = 0,
|
||||||
@SerialId(4) val autoReply: Int = 0
|
@ProtoId(4) val autoReply: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class DiscussInfo(
|
internal class DiscussInfo(
|
||||||
@SerialId(1) val discussUin: Long = 0L,
|
@ProtoId(1) val discussUin: Long = 0L,
|
||||||
@SerialId(2) val discussType: Int = 0,
|
@ProtoId(2) val discussType: Int = 0,
|
||||||
@SerialId(3) val discussInfoSeq: Long = 0L,
|
@ProtoId(3) val discussInfoSeq: Long = 0L,
|
||||||
@SerialId(4) val discussRemark: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val discussRemark: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val discussName: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(5) val discussName: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ExtGroupKeyInfo(
|
internal class ExtGroupKeyInfo(
|
||||||
@SerialId(1) val curMaxSeq: Int = 0,
|
@ProtoId(1) val curMaxSeq: Int = 0,
|
||||||
@SerialId(2) val curTime: Long = 0L
|
@ProtoId(2) val curTime: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class GroupInfo(
|
internal class GroupInfo(
|
||||||
@SerialId(1) val groupCode: Long = 0L,
|
@ProtoId(1) val groupCode: Long = 0L,
|
||||||
@SerialId(2) val groupType: Int = 0,
|
@ProtoId(2) val groupType: Int = 0,
|
||||||
@SerialId(3) val groupInfoSeq: Long = 0L,
|
@ProtoId(3) val groupInfoSeq: Long = 0L,
|
||||||
@SerialId(4) val groupCard: String = "",
|
@ProtoId(4) val groupCard: String = "",
|
||||||
@SerialId(5) val groupRank: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(5) val groupRank: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(6) val groupLevel: Int = 0,
|
@ProtoId(6) val groupLevel: Int = 0,
|
||||||
@SerialId(7) val groupCardType: Int = 0,
|
@ProtoId(7) val groupCardType: Int = 0,
|
||||||
@SerialId(8) val groupName: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(8) val groupName: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class Msg(
|
internal class Msg(
|
||||||
@SerialId(1) val msgHead: MsgHead,
|
@ProtoId(1) val msgHead: MsgHead,
|
||||||
@SerialId(2) val contentHead: ContentHead? = null,
|
@ProtoId(2) val contentHead: ContentHead? = null,
|
||||||
@SerialId(3) val msgBody: ImMsgBody.MsgBody,
|
@ProtoId(3) val msgBody: ImMsgBody.MsgBody,
|
||||||
@SerialId(4) val appshareInfo: AppShareInfo? = null
|
@ProtoId(4) val appshareInfo: AppShareInfo? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgHead(
|
internal class MsgHead(
|
||||||
@SerialId(1) val fromUin: Long = 0L,
|
@ProtoId(1) val fromUin: Long = 0L,
|
||||||
@SerialId(2) val toUin: Long = 0L,
|
@ProtoId(2) val toUin: Long = 0L,
|
||||||
@SerialId(3) val msgType: Int = 0,
|
@ProtoId(3) val msgType: Int = 0,
|
||||||
@SerialId(4) val c2cCmd: Int = 0,
|
@ProtoId(4) val c2cCmd: Int = 0,
|
||||||
@SerialId(5) val msgSeq: Int = 0,
|
@ProtoId(5) val msgSeq: Int = 0,
|
||||||
@SerialId(6) val msgTime: Int = 0,
|
@ProtoId(6) val msgTime: Int = 0,
|
||||||
@SerialId(7) var msgUid: Long = 0L,
|
@ProtoId(7) var msgUid: Long = 0L,
|
||||||
@SerialId(8) val c2cTmpMsgHead: C2CTmpMsgHead? = null,
|
@ProtoId(8) val c2cTmpMsgHead: C2CTmpMsgHead? = null,
|
||||||
@SerialId(9) val groupInfo: GroupInfo? = null,
|
@ProtoId(9) val groupInfo: GroupInfo? = null,
|
||||||
@SerialId(10) val fromAppid: Int = 0,
|
@ProtoId(10) val fromAppid: Int = 0,
|
||||||
@SerialId(11) val fromInstid: Int = 0,
|
@ProtoId(11) val fromInstid: Int = 0,
|
||||||
@SerialId(12) val userActive: Int = 0,
|
@ProtoId(12) val userActive: Int = 0,
|
||||||
@SerialId(13) val discussInfo: DiscussInfo? = null,
|
@ProtoId(13) val discussInfo: DiscussInfo? = null,
|
||||||
@SerialId(14) val fromNick: String = "",
|
@ProtoId(14) val fromNick: String = "",
|
||||||
@SerialId(15) val authUin: Long = 0L,
|
@ProtoId(15) val authUin: Long = 0L,
|
||||||
@SerialId(16) val authNick: String = "",
|
@ProtoId(16) val authNick: String = "",
|
||||||
@SerialId(17) val msgFlag: Int = 0,
|
@ProtoId(17) val msgFlag: Int = 0,
|
||||||
@SerialId(18) val authRemark: String = "",
|
@ProtoId(18) val authRemark: String = "",
|
||||||
@SerialId(19) val groupName: String = "",
|
@ProtoId(19) val groupName: String = "",
|
||||||
@SerialId(20) val mutiltransHead: MutilTransHead? = null,
|
@ProtoId(20) val mutiltransHead: MutilTransHead? = null,
|
||||||
@SerialId(21) val msgInstCtrl: ImMsgHead.InstCtrl? = null,
|
@ProtoId(21) val msgInstCtrl: ImMsgHead.InstCtrl? = null,
|
||||||
@SerialId(22) val publicAccountGroupSendFlag: Int = 0,
|
@ProtoId(22) val publicAccountGroupSendFlag: Int = 0,
|
||||||
@SerialId(23) val wseqInC2cMsghead: Int = 0,
|
@ProtoId(23) val wseqInC2cMsghead: Int = 0,
|
||||||
@SerialId(24) val cpid: Long = 0L,
|
@ProtoId(24) val cpid: Long = 0L,
|
||||||
@SerialId(25) val extGroupKeyInfo: ExtGroupKeyInfo? = null,
|
@ProtoId(25) val extGroupKeyInfo: ExtGroupKeyInfo? = null,
|
||||||
@SerialId(26) val multiCompatibleText: String = "",
|
@ProtoId(26) val multiCompatibleText: String = "",
|
||||||
@SerialId(27) val authSex: Int = 0,
|
@ProtoId(27) val authSex: Int = 0,
|
||||||
@SerialId(28) val isSrcMsg: Boolean = false
|
@ProtoId(28) val isSrcMsg: Boolean = false
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgType0x210(
|
internal class MsgType0x210(
|
||||||
@SerialId(1) val subMsgType: Int = 0,
|
@ProtoId(1) val subMsgType: Int = 0,
|
||||||
@SerialId(2) val msgContent: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(2) val msgContent: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MutilTransHead(
|
internal class MutilTransHead(
|
||||||
@SerialId(1) val status: Int = 0,
|
@ProtoId(1) val status: Int = 0,
|
||||||
@SerialId(2) val msgId: Int = 0
|
@ProtoId(2) val msgId: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class PluginInfo(
|
internal class PluginInfo(
|
||||||
@SerialId(1) val resId: Int = 0,
|
@ProtoId(1) val resId: Int = 0,
|
||||||
@SerialId(2) val pkgName: String = "",
|
@ProtoId(2) val pkgName: String = "",
|
||||||
@SerialId(3) val newVer: Int = 0,
|
@ProtoId(3) val newVer: Int = 0,
|
||||||
@SerialId(4) val resType: Int = 0,
|
@ProtoId(4) val resType: Int = 0,
|
||||||
@SerialId(5) val lanType: Int = 0,
|
@ProtoId(5) val lanType: Int = 0,
|
||||||
@SerialId(6) val priority: Int = 0,
|
@ProtoId(6) val priority: Int = 0,
|
||||||
@SerialId(7) val resName: String = "",
|
@ProtoId(7) val resName: String = "",
|
||||||
@SerialId(8) val resDesc: String = "",
|
@ProtoId(8) val resDesc: String = "",
|
||||||
@SerialId(9) val resUrlBig: String = "",
|
@ProtoId(9) val resUrlBig: String = "",
|
||||||
@SerialId(10) val resUrlSmall: String = "",
|
@ProtoId(10) val resUrlSmall: String = "",
|
||||||
@SerialId(11) val resConf: String = ""
|
@ProtoId(11) val resConf: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class Uin2Nick(
|
internal class Uin2Nick(
|
||||||
@SerialId(1) val uin: Long = 0L,
|
@ProtoId(1) val uin: Long = 0L,
|
||||||
@SerialId(2) val nick: String = ""
|
@ProtoId(2) val nick: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class UinPairMsg(
|
internal class UinPairMsg(
|
||||||
@SerialId(1) val lastReadTime: Int = 0,
|
@ProtoId(1) val lastReadTime: Int = 0,
|
||||||
@SerialId(2) val peerUin: Long = 0L,
|
@ProtoId(2) val peerUin: Long = 0L,
|
||||||
@SerialId(3) val msgCompleted: Int = 0,
|
@ProtoId(3) val msgCompleted: Int = 0,
|
||||||
@SerialId(4) val msg: List<Msg>? = null,
|
@ProtoId(4) val msg: List<Msg>? = null,
|
||||||
@SerialId(5) val unreadMsgNum: Int = 0,
|
@ProtoId(5) val unreadMsgNum: Int = 0,
|
||||||
@SerialId(8) val c2cType: Int = 0,
|
@ProtoId(8) val c2cType: Int = 0,
|
||||||
@SerialId(9) val serviceType: Int = 0,
|
@ProtoId(9) val serviceType: Int = 0,
|
||||||
@SerialId(10) val pbReserve: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(10) val pbReserve: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : 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
|
* 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.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
|
|
||||||
class Oidb0x769 {
|
class Oidb0x769 {
|
||||||
@Serializable
|
@Serializable
|
||||||
class RequestBody(
|
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(2) val msg_device_info: DeviceInfo,
|
||||||
// @SerialId(3) val str_info: String = "",
|
// @SerialId(3) val str_info: String = "",
|
||||||
// @SerialId(4) val province: String,
|
// @SerialId(4) val province: String,
|
||||||
@ -27,20 +27,20 @@ class Oidb0x769 {
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class QueryUinPackageUsageReq(
|
class QueryUinPackageUsageReq(
|
||||||
@SerialId(1) val type: Int,
|
@ProtoId(1) val type: Int,
|
||||||
@SerialId(2) val uinFileSize: Long = 0
|
@ProtoId(2) val uinFileSize: Long = 0
|
||||||
): ProtoBuf
|
): ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ConfigSeq(
|
class ConfigSeq(
|
||||||
@SerialId(1) val type: Int, // uint
|
@ProtoId(1) val type: Int, // uint
|
||||||
@SerialId(2) val version: Int // uint
|
@ProtoId(2) val version: Int // uint
|
||||||
): ProtoBuf
|
): ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class DeviceInfo(
|
class DeviceInfo(
|
||||||
@SerialId(1) val brand: String,
|
@ProtoId(1) val brand: String,
|
||||||
@SerialId(2) val model: String
|
@ProtoId(2) val model: String
|
||||||
//@SerialId(3) val os: OS,
|
//@SerialId(3) val os: OS,
|
||||||
//@SerialId(4) val cpu: CPU,
|
//@SerialId(4) val cpu: CPU,
|
||||||
//@SerialId(5) val memory: Memory,
|
//@SerialId(5) val memory: Memory,
|
||||||
@ -51,45 +51,45 @@ class Oidb0x769 {
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class OS(
|
class OS(
|
||||||
@SerialId(1) val type: Int = 1,
|
@ProtoId(1) val type: Int = 1,
|
||||||
@SerialId(2) val version: String,
|
@ProtoId(2) val version: String,
|
||||||
@SerialId(3) val sdk: String,
|
@ProtoId(3) val sdk: String,
|
||||||
@SerialId(4) val kernel: String,
|
@ProtoId(4) val kernel: String,
|
||||||
@SerialId(5) val rom: String
|
@ProtoId(5) val rom: String
|
||||||
): ProtoBuf
|
): ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Camera(
|
class Camera(
|
||||||
@SerialId(1) val primary: Long,
|
@ProtoId(1) val primary: Long,
|
||||||
@SerialId(2) val secondary: Long,
|
@ProtoId(2) val secondary: Long,
|
||||||
@SerialId(3) val flag: Boolean
|
@ProtoId(3) val flag: Boolean
|
||||||
): ProtoBuf
|
): ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class CPU(
|
class CPU(
|
||||||
@SerialId(1) val model: String,
|
@ProtoId(1) val model: String,
|
||||||
@SerialId(2) val frequency: Int,
|
@ProtoId(2) val frequency: Int,
|
||||||
@SerialId(3) val cores: Int
|
@ProtoId(3) val cores: Int
|
||||||
): ProtoBuf
|
): ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Memory(
|
class Memory(
|
||||||
@SerialId(1) val total: Int,
|
@ProtoId(1) val total: Int,
|
||||||
@SerialId(2) val process: Int
|
@ProtoId(2) val process: Int
|
||||||
): ProtoBuf
|
): ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Screen(
|
class Screen(
|
||||||
@SerialId(1) val model: String,
|
@ProtoId(1) val model: String,
|
||||||
@SerialId(2) val width: Int,
|
@ProtoId(2) val width: Int,
|
||||||
@SerialId(3) val height: Int,
|
@ProtoId(3) val height: Int,
|
||||||
@SerialId(4) val dpi: Int,
|
@ProtoId(4) val dpi: Int,
|
||||||
@SerialId(5) val multiTouch: Boolean
|
@ProtoId(5) val multiTouch: Boolean
|
||||||
): ProtoBuf
|
): ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Storage(
|
class Storage(
|
||||||
@SerialId(1) val builtin: Int,
|
@ProtoId(1) val builtin: Int,
|
||||||
@SerialId(2) val external: Int
|
@ProtoId(2) val external: Int
|
||||||
): ProtoBuf
|
): ProtoBuf
|
||||||
}
|
}
|
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
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 {
|
internal class MsgOnlinePush {
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class PbPushMsg(
|
internal class PbPushMsg(
|
||||||
@SerialId(1) val msg: MsgComm.Msg,
|
@ProtoId(1) val msg: MsgComm.Msg,
|
||||||
@SerialId(2) val svrip: Int = 0,
|
@ProtoId(2) val svrip: Int = 0,
|
||||||
@SerialId(3) val pushToken: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val pushToken: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val pingFlag: Int = 0,
|
@ProtoId(4) val pingFlag: Int = 0,
|
||||||
@SerialId(9) val generalFlag: Int = 0
|
@ProtoId(9) val generalFlag: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,24 +30,24 @@ internal class MsgOnlinePush {
|
|||||||
class OnlinePushTrans : ProtoBuf {
|
class OnlinePushTrans : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class ExtGroupKeyInfo(
|
class ExtGroupKeyInfo(
|
||||||
@SerialId(1) val curMaxSeq: Int = 0,
|
@ProtoId(1) val curMaxSeq: Int = 0,
|
||||||
@SerialId(2) val curTime: Long = 0L
|
@ProtoId(2) val curTime: Long = 0L
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class PbMsgInfo(
|
class PbMsgInfo(
|
||||||
@SerialId(1) val fromUin: Long = 0L,
|
@ProtoId(1) val fromUin: Long = 0L,
|
||||||
@SerialId(2) val toUin: Long = 0L,
|
@ProtoId(2) val toUin: Long = 0L,
|
||||||
@SerialId(3) val msgType: Int = 0,
|
@ProtoId(3) val msgType: Int = 0,
|
||||||
@SerialId(4) val msgSubtype: Int = 0,
|
@ProtoId(4) val msgSubtype: Int = 0,
|
||||||
@SerialId(5) val msgSeq: Int = 0,
|
@ProtoId(5) val msgSeq: Int = 0,
|
||||||
@SerialId(6) val msgUid: Long = 0L,
|
@ProtoId(6) val msgUid: Long = 0L,
|
||||||
@SerialId(7) val msgTime: Int = 0,
|
@ProtoId(7) val msgTime: Int = 0,
|
||||||
@SerialId(8) val realMsgTime: Int = 0,
|
@ProtoId(8) val realMsgTime: Int = 0,
|
||||||
@SerialId(9) val nickName: String = "",
|
@ProtoId(9) val nickName: String = "",
|
||||||
@SerialId(10) val msgData: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(10) val msgData: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(11) val svrIp: Int = 0,
|
@ProtoId(11) val svrIp: Int = 0,
|
||||||
@SerialId(12) val extGroupKeyInfo: OnlinePushTrans.ExtGroupKeyInfo? = null,
|
@ProtoId(12) val extGroupKeyInfo: OnlinePushTrans.ExtGroupKeyInfo? = null,
|
||||||
@SerialId(17) val generalFlag: Int = 0
|
@ProtoId(17) val generalFlag: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
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 {
|
class Generalflags : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class ResvAttr(
|
class ResvAttr(
|
||||||
@SerialId(1) val globalGroupLevel: Int = 0,
|
@ProtoId(1) val globalGroupLevel: Int = 0,
|
||||||
@SerialId(2) val nearbyCharmLevel: Int = 0,
|
@ProtoId(2) val nearbyCharmLevel: Int = 0,
|
||||||
@SerialId(3) val redbagMsgSenderUin: Long = 0L,
|
@ProtoId(3) val redbagMsgSenderUin: Long = 0L,
|
||||||
@SerialId(4) val titleId: Int = 0,
|
@ProtoId(4) val titleId: Int = 0,
|
||||||
@SerialId(5) val robotMsgFlag: Int = 0,
|
@ProtoId(5) val robotMsgFlag: Int = 0,
|
||||||
@SerialId(6) val wantGiftSenderUin: Long = 0L,
|
@ProtoId(6) val wantGiftSenderUin: Long = 0L,
|
||||||
@SerialId(7) val stickerX: Float = 0.0f,
|
@ProtoId(7) val stickerX: Float = 0.0f,
|
||||||
@SerialId(8) val stickerY: Float = 0.0f,
|
@ProtoId(8) val stickerY: Float = 0.0f,
|
||||||
@SerialId(9) val stickerWidth: Float = 0.0f,
|
@ProtoId(9) val stickerWidth: Float = 0.0f,
|
||||||
@SerialId(10) val stickerHeight: Float = 0.0f,
|
@ProtoId(10) val stickerHeight: Float = 0.0f,
|
||||||
@SerialId(11) val stickerRotate: Int = 0,
|
@ProtoId(11) val stickerRotate: Int = 0,
|
||||||
@SerialId(12) val stickerHostMsgseq: Long = 0L,
|
@ProtoId(12) val stickerHostMsgseq: Long = 0L,
|
||||||
@SerialId(13) val stickerHostMsguid: Long = 0L,
|
@ProtoId(13) val stickerHostMsguid: Long = 0L,
|
||||||
@SerialId(14) val stickerHostTime: Long = 0L,
|
@ProtoId(14) val stickerHostTime: Long = 0L,
|
||||||
@SerialId(15) val mobileCustomFont: Int = 0,
|
@ProtoId(15) val mobileCustomFont: Int = 0,
|
||||||
@SerialId(16) val tailKey: Int = 0,
|
@ProtoId(16) val tailKey: Int = 0,
|
||||||
@SerialId(17) val showTailFlag: Int = 0,
|
@ProtoId(17) val showTailFlag: Int = 0,
|
||||||
@SerialId(18) val doutuMsgType: Int = 0,
|
@ProtoId(18) val doutuMsgType: Int = 0,
|
||||||
@SerialId(19) val doutuCombo: Int = 0,
|
@ProtoId(19) val doutuCombo: Int = 0,
|
||||||
@SerialId(20) val customFeatureid: Int = 0,
|
@ProtoId(20) val customFeatureid: Int = 0,
|
||||||
@SerialId(21) val goldenMsgType: Int = 0,
|
@ProtoId(21) val goldenMsgType: Int = 0,
|
||||||
@SerialId(22) val goldenMsgInfo: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(22) val goldenMsgInfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(23) val botMessageClassId: Int = 0,
|
@ProtoId(23) val botMessageClassId: Int = 0,
|
||||||
@SerialId(24) val subscriptionUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(24) val subscriptionUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(25) val pendantDiyId: Int = 0,
|
@ProtoId(25) val pendantDiyId: Int = 0,
|
||||||
@SerialId(26) val timedMessage: Int = 0,
|
@ProtoId(26) val timedMessage: Int = 0,
|
||||||
@SerialId(27) val holidayFlag: Int = 0,
|
@ProtoId(27) val holidayFlag: Int = 0,
|
||||||
@SerialId(29) val kplInfo: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(29) val kplInfo: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(30) val faceId: Int = 0,
|
@ProtoId(30) val faceId: Int = 0,
|
||||||
@SerialId(31) val diyFontTimestamp: Int = 0,
|
@ProtoId(31) val diyFontTimestamp: Int = 0,
|
||||||
@SerialId(32) val redEnvelopeType: Int = 0,
|
@ProtoId(32) val redEnvelopeType: Int = 0,
|
||||||
@SerialId(33) val shortVideoId: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(33) val shortVideoId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(34) val reqFontEffectId: Int = 0,
|
@ProtoId(34) val reqFontEffectId: Int = 0,
|
||||||
@SerialId(35) val loveLanguageFlag: Int = 0,
|
@ProtoId(35) val loveLanguageFlag: Int = 0,
|
||||||
@SerialId(36) val aioSyncToStoryFlag: Int = 0,
|
@ProtoId(36) val aioSyncToStoryFlag: Int = 0,
|
||||||
@SerialId(37) val uploadImageToQzoneFlag: Int = 0,
|
@ProtoId(37) val uploadImageToQzoneFlag: Int = 0,
|
||||||
@SerialId(39) val uploadImageToQzoneParam: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(39) val uploadImageToQzoneParam: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(40) val groupConfessSig: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(40) val groupConfessSig: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(41) val subfontId: Long = 0L,
|
@ProtoId(41) val subfontId: Long = 0L,
|
||||||
@SerialId(42) val msgFlagType: Int = 0,
|
@ProtoId(42) val msgFlagType: Int = 0,
|
||||||
@SerialId(43) val uint32CustomFeatureid: List<Int>? = null,
|
@ProtoId(43) val uint32CustomFeatureid: List<Int>? = null,
|
||||||
@SerialId(44) val richCardNameVer: Int = 0,
|
@ProtoId(44) val richCardNameVer: Int = 0,
|
||||||
@SerialId(47) val msgInfoFlag: Int = 0,
|
@ProtoId(47) val msgInfoFlag: Int = 0,
|
||||||
@SerialId(48) val serviceMsgType: Int = 0,
|
@ProtoId(48) val serviceMsgType: Int = 0,
|
||||||
@SerialId(49) val serviceMsgRemindType: Int = 0,
|
@ProtoId(49) val serviceMsgRemindType: Int = 0,
|
||||||
@SerialId(50) val serviceMsgName: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(50) val serviceMsgName: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(51) val vipType: Int = 0,
|
@ProtoId(51) val vipType: Int = 0,
|
||||||
@SerialId(52) val vipLevel: Int = 0,
|
@ProtoId(52) val vipLevel: Int = 0,
|
||||||
@SerialId(53) val pbPttWaveform: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(53) val pbPttWaveform: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(54) val userBigclubLevel: Int = 0,
|
@ProtoId(54) val userBigclubLevel: Int = 0,
|
||||||
@SerialId(55) val userBigclubFlag: Int = 0,
|
@ProtoId(55) val userBigclubFlag: Int = 0,
|
||||||
@SerialId(56) val nameplate: Int = 0,
|
@ProtoId(56) val nameplate: Int = 0,
|
||||||
@SerialId(57) val autoReply: Int = 0,
|
@ProtoId(57) val autoReply: Int = 0,
|
||||||
@SerialId(58) val reqIsBigclubHidden: Int = 0,
|
@ProtoId(58) val reqIsBigclubHidden: Int = 0,
|
||||||
@SerialId(59) val showInMsgList: Int = 0,
|
@ProtoId(59) val showInMsgList: Int = 0,
|
||||||
@SerialId(60) val oacMsgExtend: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(60) val oacMsgExtend: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(61) val groupMemberFlagEx2: Int = 0,
|
@ProtoId(61) val groupMemberFlagEx2: Int = 0,
|
||||||
@SerialId(62) val groupRingtoneId: Int = 0,
|
@ProtoId(62) val groupRingtoneId: Int = 0,
|
||||||
@SerialId(63) val robotGeneralTrans: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(63) val robotGeneralTrans: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(64) val troopPobingTemplate: Int = 0,
|
@ProtoId(64) val troopPobingTemplate: Int = 0,
|
||||||
@SerialId(65) val hudongMark: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(65) val hudongMark: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(66) val groupInfoFlagEx3: Int = 0
|
@ProtoId(66) val groupInfoFlagEx3: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,27 +87,27 @@ class Generalflags : ProtoBuf {
|
|||||||
class ResvAttrForGiftMsg : ProtoBuf {
|
class ResvAttrForGiftMsg : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class ActivityGiftInfo(
|
class ActivityGiftInfo(
|
||||||
@SerialId(1) val isActivityGift: Int = 0,
|
@ProtoId(1) val isActivityGift: Int = 0,
|
||||||
@SerialId(2) val textColor: String = "",
|
@ProtoId(2) val textColor: String = "",
|
||||||
@SerialId(3) val text: String = "",
|
@ProtoId(3) val text: String = "",
|
||||||
@SerialId(4) val url: String = ""
|
@ProtoId(4) val url: String = ""
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class InteractGift(
|
class InteractGift(
|
||||||
@SerialId(1) val interactId: ByteArray = EMPTY_BYTE_ARRAY
|
@ProtoId(1) val interactId: ByteArray = EMPTY_BYTE_ARRAY
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ResvAttr(
|
class ResvAttr(
|
||||||
@SerialId(1) val int32SendScore: Int = 0,
|
@ProtoId(1) val int32SendScore: Int = 0,
|
||||||
@SerialId(2) val int32RecvScore: Int = 0,
|
@ProtoId(2) val int32RecvScore: Int = 0,
|
||||||
@SerialId(3) val charmHeroism: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val charmHeroism: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val buttonFlag: Int = 0,
|
@ProtoId(4) val buttonFlag: Int = 0,
|
||||||
@SerialId(5) val objColor: Int = 0,
|
@ProtoId(5) val objColor: Int = 0,
|
||||||
@SerialId(6) val animationType: Int = 0,
|
@ProtoId(6) val animationType: Int = 0,
|
||||||
@SerialId(7) val msgInteractGift: ResvAttrForGiftMsg.InteractGift? = null,
|
@ProtoId(7) val msgInteractGift: ResvAttrForGiftMsg.InteractGift? = null,
|
||||||
@SerialId(8) val activityGiftInfo: ResvAttrForGiftMsg.ActivityGiftInfo? = null
|
@ProtoId(8) val activityGiftInfo: ResvAttrForGiftMsg.ActivityGiftInfo? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,9 +115,9 @@ class ResvAttrForGiftMsg : ProtoBuf {
|
|||||||
class SourceMsg : ProtoBuf {
|
class SourceMsg : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class ResvAttr(
|
class ResvAttr(
|
||||||
@SerialId(1) val richMsg2: ByteArray? = null,
|
@ProtoId(1) val richMsg2: ByteArray? = null,
|
||||||
@SerialId(2) val oriMsgtype: Int? = null,
|
@ProtoId(2) val oriMsgtype: Int? = null,
|
||||||
@SerialId(3) val origUids: Long? = null // 原来是 list
|
@ProtoId(3) val origUids: Long? = null // 原来是 list
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,17 +125,17 @@ class SourceMsg : ProtoBuf {
|
|||||||
class VideoFile : ProtoBuf {
|
class VideoFile : ProtoBuf {
|
||||||
@Serializable
|
@Serializable
|
||||||
class ResvAttr(
|
class ResvAttr(
|
||||||
@SerialId(1) val hotvideoIcon: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(1) val hotvideoIcon: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(2) val hotvideoTitle: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(2) val hotvideoTitle: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(3) val hotvideoUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(3) val hotvideoUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(4) val hotvideoIconSub: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(4) val hotvideoIconSub: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(5) val specialVideoType: Int = 0,
|
@ProtoId(5) val specialVideoType: Int = 0,
|
||||||
@SerialId(6) val dynamicText: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(6) val dynamicText: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(7) val msgTailType: Int = 0,
|
@ProtoId(7) val msgTailType: Int = 0,
|
||||||
@SerialId(8) val redEnvelopeType: Int = 0,
|
@ProtoId(8) val redEnvelopeType: Int = 0,
|
||||||
@SerialId(9) val shortVideoId: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(9) val shortVideoId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(10) val animojiModelId: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoId(10) val animojiModelId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@SerialId(11) val longVideoKandianType: Int = 0,
|
@ProtoId(11) val longVideoKandianType: Int = 0,
|
||||||
@SerialId(12) val source: Int = 0
|
@ProtoId(12) val source: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
||||||
|
@ -9,30 +9,30 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
|
|
||||||
class StatSvcGetOnline {
|
class StatSvcGetOnline {
|
||||||
@Serializable
|
@Serializable
|
||||||
class Instance(
|
class Instance(
|
||||||
@SerialId(1) val instanceId: Int = 0,
|
@ProtoId(1) val instanceId: Int = 0,
|
||||||
@SerialId(2) val clientType: Int = 0
|
@ProtoId(2) val clientType: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ReqBody(
|
class ReqBody(
|
||||||
@SerialId(1) val uin: Long = 0L,
|
@ProtoId(1) val uin: Long = 0L,
|
||||||
@SerialId(2) val appid: Int = 0
|
@ProtoId(2) val appid: Int = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class RspBody(
|
class RspBody(
|
||||||
@SerialId(1) val errorCode: Int = 0,
|
@ProtoId(1) val errorCode: Int = 0,
|
||||||
@SerialId(2) val errorMsg: String = "",
|
@ProtoId(2) val errorMsg: String = "",
|
||||||
@SerialId(3) val uin: Long = 0L,
|
@ProtoId(3) val uin: Long = 0L,
|
||||||
@SerialId(4) val appid: Int = 0,
|
@ProtoId(4) val appid: Int = 0,
|
||||||
@SerialId(5) val timeInterval: Int = 0,
|
@ProtoId(5) val timeInterval: Int = 0,
|
||||||
@SerialId(6) val msgInstances: List<StatSvcGetOnline.Instance>? = null
|
@ProtoId(6) val msgInstances: List<StatSvcGetOnline.Instance>? = null
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
}
|
}
|
@ -9,23 +9,23 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||||
|
|
||||||
import kotlinx.serialization.SerialId
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.protobuf.ProtoId
|
||||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SyncCookie(
|
class SyncCookie(
|
||||||
@SerialId(1) val time1: Long? = null, // 1580277992
|
@ProtoId(1) val time1: Long? = null, // 1580277992
|
||||||
@SerialId(2) val time: Long, // 1580277992
|
@ProtoId(2) val time: Long, // 1580277992
|
||||||
@SerialId(3) val unknown1: Long = Random.nextLong().absoluteValue,// 678328038
|
@ProtoId(3) val unknown1: Long = Random.nextLong().absoluteValue,// 678328038
|
||||||
@SerialId(4) val unknown2: Long = Random.nextLong().absoluteValue, // 1687142153
|
@ProtoId(4) val unknown2: Long = Random.nextLong().absoluteValue, // 1687142153
|
||||||
@SerialId(5) val const1: Long = const1_, // 1458467940
|
@ProtoId(5) val const1: Long = const1_, // 1458467940
|
||||||
@SerialId(11) val const2: Long = const2_, // 2683038258
|
@ProtoId(11) val const2: Long = const2_, // 2683038258
|
||||||
@SerialId(12) val unknown3: Long = 0x1d,
|
@ProtoId(12) val unknown3: Long = 0x1d,
|
||||||
@SerialId(13) val lastSyncTime: Long? = null,
|
@ProtoId(13) val lastSyncTime: Long? = null,
|
||||||
@SerialId(14) val unknown4: Long = 0
|
@ProtoId(14) val unknown4: Long = 0
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
private val const1_: Long = Random.nextLong().absoluteValue
|
private val const1_: Long = Random.nextLong().absoluteValue
|
||||||
|
@ -15,13 +15,11 @@ import kotlinx.io.core.buildPacket
|
|||||||
import kotlinx.io.core.writeFully
|
import kotlinx.io.core.writeFully
|
||||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||||
import net.mamoe.mirai.utils.cryptor.ECDH
|
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.encryptAndWrite
|
||||||
import net.mamoe.mirai.utils.io.writeShortLVByteArray
|
import net.mamoe.mirai.utils.io.writeShortLVByteArray
|
||||||
|
|
||||||
/**
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
* Encryption method to be used for packet body.
|
|
||||||
*/
|
|
||||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
|
||||||
internal interface EncryptMethod {
|
internal interface EncryptMethod {
|
||||||
val id: Int
|
val id: Int
|
||||||
|
|
||||||
@ -33,16 +31,6 @@ internal interface EncryptMethodSessionKey : EncryptMethod {
|
|||||||
val currentLoginState: Int
|
val currentLoginState: Int
|
||||||
val sessionKey: ByteArray
|
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 =
|
override fun makeBody(client: QQAndroidClient, body: BytePacketBuilder.() -> Unit): ByteReadPacket =
|
||||||
buildPacket {
|
buildPacket {
|
||||||
require(currentLoginState == 2 || currentLoginState == 3) { "currentLoginState must be either 2 or 3" }
|
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
|
override val currentLoginState: Int get() = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
inline class EncryptMethodECDH135(override val ecdh: ECDH) :
|
internal inline class EncryptMethodECDH135(override val ecdh: ECDH) :
|
||||||
EncryptMethodECDH {
|
EncryptMethodECDH {
|
||||||
override val id: Int get() = 135
|
override val id: Int get() = 135
|
||||||
}
|
}
|
||||||
|
|
||||||
inline class EncryptMethodECDH7(override val ecdh: ECDH) :
|
internal inline class EncryptMethodECDH7(override val ecdh: ECDH) :
|
||||||
EncryptMethodECDH {
|
EncryptMethodECDH {
|
||||||
override val id: Int get() = 7
|
override val id: Int get() = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface EncryptMethodECDH : EncryptMethod {
|
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
|
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 =
|
override fun makeBody(client: QQAndroidClient, body: BytePacketBuilder.() -> Unit): ByteReadPacket =
|
||||||
buildPacket {
|
buildPacket {
|
||||||
writeByte(1) // const
|
writeByte(1) // const
|
||||||
@ -95,15 +81,15 @@ internal interface EncryptMethodECDH : EncryptMethod {
|
|||||||
writeFully(client.randomKey)
|
writeFully(client.randomKey)
|
||||||
writeShort(258) // const
|
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 {
|
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(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)
|
encryptAndWrite(ecdh.keyPair.initialShareKey, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
@ -34,39 +34,7 @@ internal class OutgoingPacket constructor(
|
|||||||
internal val KEY_16_ZEROS = ByteArray(16)
|
internal val KEY_16_ZEROS = ByteArray(16)
|
||||||
internal val EMPTY_BYTE_ARRAY = ByteArray(0)
|
internal val EMPTY_BYTE_ARRAY = ByteArray(0)
|
||||||
|
|
||||||
/**
|
@OptIn(MiraiInternalAPI::class)
|
||||||
* 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)
|
|
||||||
internal inline fun OutgoingPacketFactory<*>.buildOutgoingUniPacket(
|
internal inline fun OutgoingPacketFactory<*>.buildOutgoingUniPacket(
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
bodyType: Byte = 1, // 1: PB?
|
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(
|
internal inline fun IncomingPacketFactory<*>.buildResponseUniPacket(
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
bodyType: Byte = 1, // 1: PB?
|
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(
|
internal inline fun BytePacketBuilder.writeUniPacket(
|
||||||
commandName: String,
|
commandName: String,
|
||||||
unknownData: ByteArray,
|
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)
|
* 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(
|
internal inline fun OutgoingPacketFactory<*>.buildLoginOutgoingPacket(
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
bodyType: Byte,
|
bodyType: Byte,
|
||||||
@ -199,7 +167,7 @@ internal inline fun OutgoingPacketFactory<*>.buildLoginOutgoingPacket(
|
|||||||
|
|
||||||
private inline val BRP_STUB get() = ByteReadPacket.Empty
|
private inline val BRP_STUB get() = ByteReadPacket.Empty
|
||||||
|
|
||||||
@UseExperimental(MiraiInternalAPI::class)
|
@OptIn(MiraiInternalAPI::class)
|
||||||
internal inline fun BytePacketBuilder.writeSsoPacket(
|
internal inline fun BytePacketBuilder.writeSsoPacket(
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
subAppId: Long,
|
subAppId: Long,
|
||||||
@ -265,7 +233,7 @@ internal inline fun BytePacketBuilder.writeSsoPacket(
|
|||||||
writeIntLVPacket(lengthOffset = { it + 4 }, builder = body)
|
writeIntLVPacket(lengthOffset = { it + 4 }, builder = body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||||
internal fun BytePacketBuilder.writeOicqRequestPacket(
|
internal fun BytePacketBuilder.writeOicqRequestPacket(
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
encryptMethod: EncryptMethod,
|
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 kotlinx.io.core.writeFully
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.LoginType
|
import net.mamoe.mirai.qqandroid.network.protocol.LoginType
|
||||||
import net.mamoe.mirai.qqandroid.utils.NetworkType
|
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.currentTimeMillis
|
||||||
import net.mamoe.mirai.utils.io.*
|
import net.mamoe.mirai.utils.io.*
|
||||||
import net.mamoe.mirai.utils.md5
|
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,6 +77,7 @@ fun BytePacketBuilder.t18(
|
|||||||
} shouldEqualsTo 22
|
} shouldEqualsTo 22
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(MiraiInternalAPI::class)
|
||||||
fun BytePacketBuilder.t106(
|
fun BytePacketBuilder.t106(
|
||||||
appId: Long = 16L,
|
appId: Long = 16L,
|
||||||
subAppId: Long = 537062845L,
|
subAppId: Long = 537062845L,
|
||||||
@ -96,7 +98,7 @@ fun BytePacketBuilder.t106(
|
|||||||
guid?.requireSize(16)
|
guid?.requireSize(16)
|
||||||
|
|
||||||
writeShortLVPacket {
|
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
|
writeShort(4)//TGTGTVer
|
||||||
writeInt(Random.nextInt())
|
writeInt(Random.nextInt())
|
||||||
writeInt(5)//ssoVer
|
writeInt(5)//ssoVer
|
||||||
@ -321,12 +323,13 @@ fun BytePacketBuilder.t144(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(MiraiInternalAPI::class)
|
||||||
fun BytePacketBuilder.t109(
|
fun BytePacketBuilder.t109(
|
||||||
androidId: ByteArray
|
androidId: ByteArray
|
||||||
) {
|
) {
|
||||||
writeShort(0x109)
|
writeShort(0x109)
|
||||||
writeShortLVPacket {
|
writeShortLVPacket {
|
||||||
writeFully(md5(androidId))
|
writeFully(MiraiPlatformUtils.md5(androidId))
|
||||||
} shouldEqualsTo 16
|
} shouldEqualsTo 16
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -556,21 +559,23 @@ fun BytePacketBuilder.t400(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(MiraiInternalAPI::class)
|
||||||
fun BytePacketBuilder.t187(
|
fun BytePacketBuilder.t187(
|
||||||
macAddress: ByteArray
|
macAddress: ByteArray
|
||||||
) {
|
) {
|
||||||
writeShort(0x187)
|
writeShort(0x187)
|
||||||
writeShortLVPacket {
|
writeShortLVPacket {
|
||||||
writeFully(md5(macAddress)) // may be md5
|
writeFully(MiraiPlatformUtils.md5(macAddress)) // may be md5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(MiraiInternalAPI::class)
|
||||||
fun BytePacketBuilder.t188(
|
fun BytePacketBuilder.t188(
|
||||||
androidId: ByteArray
|
androidId: ByteArray
|
||||||
) {
|
) {
|
||||||
writeShort(0x188)
|
writeShort(0x188)
|
||||||
writeShortLVPacket {
|
writeShortLVPacket {
|
||||||
writeFully(md5(androidId))
|
writeFully(MiraiPlatformUtils.md5(androidId))
|
||||||
} shouldEqualsTo 16
|
} 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