mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-03 02:29:21 +08:00
Merge remote-tracking branch 'origin/dev' into anonymous
This commit is contained in:
commit
0bb34ee05b
42
.github/workflows/bintray.yml
vendored
42
.github/workflows/bintray.yml
vendored
@ -6,8 +6,7 @@ name: Bintray Publish
|
||||
# events but only for the master branch
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
types: [created, prereleased]
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
@ -28,31 +27,36 @@ jobs:
|
||||
run: ./gradlew build # if test's failed, don't publish
|
||||
|
||||
- name: Check keys
|
||||
run: ./gradlew :mirai-core-utils:ensureBintrayAvailable
|
||||
:mirai-core-api:ensureBintrayAvailable
|
||||
:mirai-core:ensureBintrayAvailable
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
run: >
|
||||
./gradlew :mirai-core-utils:ensureBintrayAvailable
|
||||
:mirai-core-api:ensureBintrayAvailable
|
||||
:mirai-core:ensureBintrayAvailable
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
- name: Gradle :mirai-core-utils:publish
|
||||
run: ./gradlew :mirai-core-utils:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
run: >
|
||||
./gradlew :mirai-core-utils:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
- name: Gradle :mirai-core-api:publish
|
||||
run: ./gradlew :mirai-core-api:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
run: >
|
||||
./gradlew :mirai-core-api:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
- name: Gradle :mirai-core:publish
|
||||
run: ./gradlew :mirai-core:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
run: >
|
||||
./gradlew :mirai-core:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
- name: Gradle :mirai-core-all:bintrayUpload
|
||||
run: ./gradlew :mirai-core-all:bintrayUpload --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
run: >
|
||||
./gradlew :mirai-core-all:bintrayUpload --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
# - name: Upload artifact
|
||||
# uses: actions/upload-artifact@v1.0.0
|
||||
|
39
.github/workflows/devTagPublish.yml
vendored
39
.github/workflows/devTagPublish.yml
vendored
@ -29,31 +29,36 @@ jobs:
|
||||
run: ./gradlew build # if test's failed, don't publish
|
||||
|
||||
- name: Check keys
|
||||
run: ./gradlew :mirai-core-utils:ensureBintrayAvailable
|
||||
:mirai-core-api:ensureBintrayAvailable
|
||||
:mirai-core:ensureBintrayAvailable
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
run: >-
|
||||
./gradlew :mirai-core-utils:ensureBintrayAvailable
|
||||
:mirai-core-api:ensureBintrayAvailable
|
||||
:mirai-core:ensureBintrayAvailable
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
- name: Gradle :mirai-core-utils:publish
|
||||
run: ./gradlew :mirai-core-utils:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
run: >-
|
||||
./gradlew :mirai-core-utils:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
- name: Gradle :mirai-core-api:publish
|
||||
run: ./gradlew :mirai-core-api:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
run: >-
|
||||
./gradlew :mirai-core-api:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
- name: Gradle :mirai-core:publish
|
||||
run: ./gradlew :mirai-core:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
run: >-
|
||||
./gradlew :mirai-core:publish --info
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
- name: Gradle :mirai-core-all:bintrayUpload
|
||||
run: ./gradlew :mirai-core-all:bintrayUpload
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
run: >-
|
||||
./gradlew :mirai-core-all:bintrayUpload
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
# - name: Upload artifact
|
||||
# uses: actions/upload-artifact@v1.0.0
|
||||
|
1180
CHANGELOG.md
1180
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -10,8 +10,6 @@ Mirai is designed to handle all sorts of messaging works that can be automatical
|
||||
## Start
|
||||
**Development document**: [docs/mirai.md](docs/mirai.md)
|
||||
|
||||
[CHANGELOG](https://github.com/mamoe/mirai/blob/master/CHANGELOG.md)
|
||||
|
||||
### Use as a framework
|
||||
|
||||
Mirai is able to run as a plugin-supported framework.
|
||||
|
73
README.md
73
README.md
@ -109,84 +109,27 @@ mirai 是一个在全平台下运行,提供 QQ Android 协议支持的高效
|
||||
|
||||
## 开始
|
||||
|
||||
### 在开始之前,建议你了解一下 Mirai 生态
|
||||
[Mirai 生态概览](docs/mirai-ecology.md)
|
||||
|
||||
### 文档
|
||||
|
||||
**对于一般使用者, 更建议使用 [Mirai Console](https://github.com/mamoe/mirai-console)。拥有更完善的文档。**
|
||||
|
||||
- 快速上手:[quickstart](docs/guide_quick_start.md)
|
||||
- 开发文档:[docs/mirai.md](docs/mirai.md)
|
||||
- 常见问题: [docs/FAQ.md](docs/FAQ.md)
|
||||
- 更新日志: [CHANGELOG](https://github.com/mamoe/mirai/blob/master/CHANGELOG.md) 或 [release](https://github.com/mamoe/mirai/releases)
|
||||
- 开发文档(新,编写中):[docs](docs/README.md)
|
||||
- 开发文档(旧):[docs/mirai.md](docs/mirai.md)
|
||||
- 更新日志: [release](https://github.com/mamoe/mirai/releases)
|
||||
- 开发计划: [milestones](https://github.com/mamoe/mirai/milestones)
|
||||
|
||||
- 贡献: [CONTRIBUTING](CONTRIBUTING.md)
|
||||
|
||||
### 使用者
|
||||
|
||||
- [mirai-console](https://github.com/mamoe/mirai-console) 支持插件的控制台服务端,支持PC和Android平台 **本模块正在开发中**
|
||||
- [awesome-mirai](https://github.com/project-mirai/awsome-mirai/blob/master/README.md) **mirai相关项目合集**
|
||||
|
||||
#### 从其他平台迁移
|
||||
|
||||
- 酷Q的插件可以在 `mirai` 中加载,详见 [Mirai Native](https://github.com/iTXTech/mirai-native)
|
||||
- 使用 `酷Q HTTP API` 的插件将可以在 `mirai` 中通过`CQHTTP Mirai`加载,详见 [cqhttp-mirai](https://github.com/yyuueexxiinngg/cqhttp-mirai)
|
||||
|
||||
### 开发者
|
||||
|
||||
开发交流:[![Gitter](https://badges.gitter.im/mamoe/mirai.svg)](https://gitter.im/mamoe/mirai?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
#### 使用 mirai-console 服务端,为 mirai-console 开发插件
|
||||
|
||||
官方支持 SDK 列表:
|
||||
|
||||
- `Java`,`Kotlin` 等 JVM 语言: 为 [mirai-console](https://github.com/mamoe/mirai-console) 直接编写插件并与其他插件开发者合作共享
|
||||
- `Kotlin Script`: [mirai-kts](https://github.com/iTXTech/mirai-kts) 支持使用 `kts` 编写插件,享受 `Kotlin` 带来的一切便利(**仅 OpenJDK 8 以上环境,不支持 Android**)
|
||||
- `C`,`C++` 等原生语言: [mirai-native](https://github.com/iTXTech/mirai-native) 支持酷 Q 插件在 mirai 上运行 **(仅限 `Windows 32 位 JRE`/支持 `Wine`)**
|
||||
- `JavaScript`: [mirai-js](https://github.com/iTXTech/mirai-js) 支持使用 `JavaScript` 编写插件并**直接**与 mirai 交互
|
||||
- *Http*:使用由 [mirai-api-http](https://github.com/mamoe/mirai-api-http) 提供的 http 接口进行接入
|
||||
|
||||
<details>
|
||||
<summary>社区支持的 SDK 列表</summary>
|
||||
|
||||
基于 `mirai-core` (独立使用):
|
||||
- `Lua`: [lua-mirai](https://github.com/only52607/lua-mirai) 基于 mirai-core 的 Lua SDK,并提供了 Java 扩展支持,可在 Lua 中调用 Java 代码开发机器人
|
||||
|
||||
|
||||
基于 `mirai-http-api` (配合 [mirai-console](https://github.com/mamoe/mirai-console)):
|
||||
|
||||
- `Python`: [Graia Framework](https://github.com/GraiaProject/Application) 基于 `mirai-api-http` 的机器人开发框架
|
||||
- `JavaScript`(`Node.js`): [node-mirai](https://github.com/RedBeanN/node-mirai) mirai 的 Node.js SDK
|
||||
- `Go`: [gomirai](https://github.com/Logiase/gomirai) 基于 mirai-api-http 的 GoLang SDK
|
||||
- `Mozilla Rhino`: [mirai-rhinojs-sdk](https://github.com/StageGuard/mirai-rhinojs-sdk) 为基于 Rhino(如 Auto.js 等安卓 app 或运行环境)的 JavaScript 提供简单易用的 SDK
|
||||
- `C++`: [mirai-cpp](https://github.com/cyanray/mirai-cpp) mirai-http-api 的 C++ 封装,方便使用 C++ 开发 mirai-http-api 插件
|
||||
- `C++`: [miraipp](https://github.com/Chlorie/miraipp-template) mirai-http-api 的另一个 C++ 封装,使用现代 C++ 特性,并提供了较完善的说明文档
|
||||
- `C#`: [mirai-CSharp](https://github.com/Executor-Cheng/mirai-CSharp) 基于 mirai-api-http 的 C# SDK
|
||||
- `Rust`: [mirai-rs](https://github.com/HoshinoTented/mirai-rs) mirai-http-api 的 Rust 封装
|
||||
- `TypeScript`: [mirai-ts](https://github.com/YunYouJun/mirai-ts) mirai-api-http 的 TypeScript SDK,附带声明文件,拥有良好的注释和类型提示,也可作为 JavaScript SDK 使用。
|
||||
- `易语言`: [e-mirai](https://github.com/only52607/e-mirai) mirai-api-http 的 易语言 SDK,使用全中文环境开发插件,适合编程新手使用。
|
||||
- `.Net/C#`: [Hyperai](https://github.com/theGravityLab/ProjHyperai) 从 mirai-api-http 对接到机器人开发框架再到开箱即用的插件式机器人程序一应俱全。
|
||||
|
||||
</details>
|
||||
|
||||
#### 使用 mirai-core 为第三方依赖库引入项目
|
||||
|
||||
Demos: [mirai-demos](https://github.com/mamoe/mirai-demos)
|
||||
|
||||
- `Kotlin` 简略版: [mirai Guide - Quick Start](/docs/guide_quick_start.md)
|
||||
- `Kotlin` 新手版: [mirai Guide - Getting Started](/docs/guide_getting_started.md)
|
||||
- `Java`: 查看上述 Demos
|
||||
- 常见问题: [docs/FAQ.md](docs/FAQ.md)
|
||||
|
||||
## [贡献](CONTRIBUTING.md)
|
||||
|
||||
我们欢迎一切形式的贡献。
|
||||
我们也期待有更多人能加入 mirai 的开发。
|
||||
|
||||
若在使用过程中有任何疑问,可提交 `issue` 或是[邮件联系](mailto:support@mamoe.net). 我们希望 mirai 变得更易用.
|
||||
若在使用过程中有任何疑问,可提交 [`issue`](https://github.com/mamoe/mirai/issues) 或在 [`Discussions`](https://github.com/mamoe/mirai/discussions) 讨论。 我们希望 mirai 变得更易用.
|
||||
|
||||
您的 `star` 是对我们最大的鼓励(点击项目右上角)
|
||||
|
||||
开发交流:[![Gitter](https://badges.gitter.im/mamoe/mirai.svg)](https://gitter.im/mamoe/mirai?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
### 加入 mirai 开发组
|
||||
若您有意加入 mirai, mirai-console 和相关社区开发, 请 [邮件联系](mailto:support@mamoe.net) (`support@mamoe.net`), 并附加相关开发经验证明.
|
||||
|
||||
|
@ -21,7 +21,7 @@ import org.gradle.api.attributes.Attribute
|
||||
*/
|
||||
|
||||
object Versions {
|
||||
const val project = "2.0-M1-dev-3"
|
||||
const val project = "2.0-M1-1"
|
||||
|
||||
const val kotlinCompiler = "1.4.21"
|
||||
const val kotlinStdlib = "1.4.21"
|
||||
|
41
docs/Bots.md
Normal file
41
docs/Bots.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Mirai - Creating Bots
|
||||
|
||||
## 获取 `Bot` 实例
|
||||
|
||||
一个机器人被以 `Bot` 对象描述。mirai 的交互入口点是 `Bot`。`Bot` 只可通过 [`BotFactory`](../mirai-core-api/src/commonMain/kotlin/BotFactory.kt#L22-L87) 内的 `newBot` 方法获得:
|
||||
|
||||
```kotlin
|
||||
interface BotFactory {
|
||||
fun newBot(qq: Long, password: String, configuration: BotConfiguration): Bot
|
||||
fun newBot(qq: Long, password: String): Bot
|
||||
fun newBot(qq: Long, passwordMd5: ByteArray, configuration: BotConfiguration): Bot
|
||||
fun newBot(qq: Long, passwordMd5: ByteArray): Bot
|
||||
|
||||
companion object : BotFactory by BotFactoryImpl
|
||||
}
|
||||
```
|
||||
|
||||
通常的调用方法为:
|
||||
```
|
||||
// Kotlin
|
||||
val bot = BotFactory.newBot( )
|
||||
|
||||
// Java
|
||||
Bot bot = BotFactory.INSTANCE.newBot( );
|
||||
```
|
||||
|
||||
### 获取当前所有 `Bot` 实例
|
||||
在登录后,`Bot` 实例会被自动记录。可在 `Bot.instances` 获取到当前在线的所有 `Bot` 列表。当 `Bot` 离线,其实例就会被删除。
|
||||
|
||||
## 登录
|
||||
|
||||
创建 `Bot` 后不会自动登录,需要手动调用其 `login()` 方法。在 Kotlin 还可以使用 `Bot.alsoLogin()` 扩展,相当于 `bot.apply { login() }`
|
||||
|
||||
### 重新登录
|
||||
|
||||
可以再次调用 `Bot.login()` 以重新登录 `Bot`。这一般没有必要——`Bot` 运行时会自动与服务器同步事件(如群成员变化,好友数量变化)。
|
||||
|
||||
|
||||
> 下一步,[Contacts](Contacts.md)
|
||||
>
|
||||
> [回到 Mirai 文档索引](README.md)
|
132
docs/ConfiguringProjects.md
Normal file
132
docs/ConfiguringProjects.md
Normal file
@ -0,0 +1,132 @@
|
||||
# Mirai - Configuring Projects
|
||||
|
||||
本文介绍如何在一个项目中使用 mirai。
|
||||
|
||||
mirai 使用纯 Kotlin 开发,最低要求 `JDK 1.8`,`Kotlin 1.4`。但注意不要使用 Oracle JDK,推荐使用 OpenJDK。
|
||||
|
||||
如果你熟悉 Gradle,只需要添加 `jcenter` 仓库和依赖 `net.mamoe:mirai-core:VERSION` 即可而不需要继续阅读。下文将详细解释其他方法。
|
||||
|
||||
本文提供如下三种配置方法,但更推荐使用 Gradle 构建。
|
||||
|
||||
- [A. 使用 Gradle](#a-使用-gradle)
|
||||
- [B. 使用 Maven](#b-使用-maven)
|
||||
- [C. 使用 IntelliJ](#c-使用-intellij)
|
||||
|
||||
### 选择版本
|
||||
有关各类版本的区别,参考 [版本规范](Evolution.md#版本规范)
|
||||
|
||||
[Version]: https://api.bintray.com/packages/him188moe/mirai/mirai-core/images/download.svg?
|
||||
[Bintray Download]: https://bintray.com/him188moe/mirai/mirai-core/
|
||||
|
||||
| 版本类型 | 版本号 |
|
||||
|:------:|:------------------------------:|
|
||||
| 稳定 | 1.3.3 |
|
||||
| 预览 | 2.0-M1-1 |
|
||||
| 开发 | [![Version]][Bintray Download] |
|
||||
|
||||
即使 2.0 还没有稳定,也建议使用 2.0 预览版本,因 1.x 版本将不会收到任何更新。
|
||||
|
||||
## A. 使用 Gradle
|
||||
|
||||
### Gradle Kotlin DSL
|
||||
|
||||
在 `build.gradle.kts` 添加:
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
kotlin("jvm") version "1.4.21"
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api("net.mamoe", "mirai-core", "1.3.3") // 替换为你需要的版本号
|
||||
}
|
||||
```
|
||||
|
||||
注意,必须添加 Kotlin 插件才能正确获取 mirai 软件包。
|
||||
|
||||
### Gradle Groovy DSL
|
||||
|
||||
在 `build.gradle` 添加
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.4.21'
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api('net.mamoe', 'mirai-core', '1.3.3') // 替换为你需要的版本号
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## B. 使用 Maven
|
||||
|
||||
在 `pom.xml` 中:
|
||||
|
||||
### 1. 添加 jcenter 仓库
|
||||
```xml
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jcenter</id>
|
||||
<url>https://jcenter.bintray.com/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
```
|
||||
|
||||
### 2. 添加 mirai 依赖
|
||||
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.mamoe</groupId>
|
||||
<artifactId>mirai-core-jvm</artifactId>
|
||||
<version>1.3.3</version> <!-- 替换版本为你需要的版本 -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
### 3. 添加 Kotlin 依赖
|
||||
|
||||
通常 mirai 可以直接使用。但 mirai 使用的 Kotlin 1.4 可能与你的项目使用的其他库依赖的 Kotlin 版本冲突,Maven 无法正确处理这种冲突。此时请手动添加 Kotlin 标准库依赖。
|
||||
|
||||
```xml
|
||||
<properties>
|
||||
<kotlin.version>1.4.20</kotlin.version>
|
||||
</properties>
|
||||
```
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
> 可以在 [Kotlin 官方文档](https://www.kotlincn=.net/docs/reference/using-maven.html) 获取更多有关配置 Kotlin 的信息。
|
||||
|
||||
## C. 使用 IntelliJ
|
||||
|
||||
### 1. 创建项目
|
||||
|
||||
使用现有项目,或创建一个新项目(`File->New->Project`)或新模块(`File->New->Module`)。
|
||||
|
||||
### 2. 添加依赖
|
||||
|
||||
1. 进入 `Project Structure`(`File->Project Structure`,`Ctrl+Alt+Shift+S`)
|
||||
2. 进入 `Libraries`
|
||||
3. 找到 `+` 按钮,点击 `From Maven`
|
||||
4. 输入 `net.mamoe:mirai-core:1.3.3`,勾选 `Sources`
|
||||
5. 确认并等待下载
|
||||
|
||||
|
||||
> [回到 Mirai 文档索引](README.md)
|
11
docs/Contacts.md
Normal file
11
docs/Contacts.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Mirai - Contacts
|
||||
|
||||
[![](https://mermaid.ink/img/eyJjb2RlIjoiY2xhc3NEaWFncmFtXG5cbmNsYXNzIEJvdCB7XG4gICAgK2ZyaWVuZHM6IENvbnRhY3RMaXN0XG4gICAgK2dyb3VwczogQ29udGFjdExpc3RcbiAgICArZ2V0RnJpZW5kKExvbmcpIEZyaWVuZD9cbiAgICArZ2V0RnJpZW5kT3JOdWxsKExvbmcpIEZyaWVuZFxuICAgICtnZXRHcm91cChMb25nKSBHcm91cD9cbiAgICArZ2V0R3JvdXBPckZhaWwoTG9uZykgR3JvdXBcbiAgICArbG9naW4oKVxuICAgICtjbG9zZSgpXG59XG5cbmNsYXNzIENvbnRhY3RPckJvdCB7XG4gICAgK2lkOiBJbnRcbn1cblxuY2xhc3MgVXNlck9yQm90IHtcbiAgICArbnVkZ2UoKSBOdWRnZVxufVxuXG5jbGFzcyBDb250YWN0IHtcbiAgICArYm90OiBCb3RcbiAgICArc2VuZE1lc3NhZ2UoTWVzc2FnZSkgTWVzc2FnZVJlY2VpcHRcbiAgICArc2VuZE1lc3NhZ2UoU3RyaW5nKSBNZXNzYWdlUmVjZWlwdFxuICAgICt1cGxvYWRJbWFnZShFeHRlcm5hbEltYWdlKSBJbWFnZVxufVxuXG5jbGFzcyBVc2VyIHtcbiAgICArbmljazogU3RyaW5nXG4gICAgK3JlbWFyazogU3RyaW5nXG4gICAgK2F2YXRhclVybDogU3RyaW5nXG59XG5cbmNsYXNzIEdyb3VwIHtcbiAgICArbWVtYmVyczogQ29udGFjdExpc3RcbiAgICArbmFtZTogU3RyaW5nXG4gICAgK3NldHRpbmdzOiBHcm91cFNldHRpbmdzXG4gICAgK293bmVyOiBOb3JtYWxNZW1iZXJcbiAgICArYm90TXV0ZVJlbWFpbmluZzogTG9uZ1xuICAgICtib3RQZXJtaXNzaW9uOiBNZW1iZXJQZXJtaXNzaW9uXG4gICAgK3F1aXQoKSBCb29sZWFuXG4gICAgK3VwbG9hZFZvaWNlKCkgVm9pY2Vcbn1cblxuY2xhc3MgTm9ybWFsTWVtYmVyIHtcbiAgICArbXV0ZSgpXG4gICAgK2tpY2soKVxufVxuXG5jbGFzcyBBbm9ueW1vdXNNZW1iZXIge1xuICAgICthbm9ueW1vdXNJZDogU3RyaW5nXG59XG5cbmNsYXNzIE1lbWJlciB7XG4gICAgK2dyb3VwOiBHcm91cFxufVxuXG5Db250YWN0T3JCb3Q8LS1Db250YWN0XG5Db250YWN0T3JCb3Q8LS1Vc2VyT3JCb3RcblxuVXNlck9yQm90PC0tQm90XG5Vc2VyT3JCb3Q8LS1Vc2VyXG5cbkNvbnRhY3Q8LS1Vc2VyXG5Db250YWN0PC0tR3JvdXBcblxuVXNlcjwtLU1lbWJlclxuVXNlcjwtLUZyaWVuZFxuXG5NZW1iZXI8LS1Ob3JtYWxNZW1iZXJcbk1lbWJlcjwtLUFub255bW91c01lbWJlciIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0In0sInVwZGF0ZUVkaXRvciI6ZmFsc2V9)](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiY2xhc3NEaWFncmFtXG5cbmNsYXNzIEJvdCB7XG4gICAgK2ZyaWVuZHM6IENvbnRhY3RMaXN0XG4gICAgK2dyb3VwczogQ29udGFjdExpc3RcbiAgICArZ2V0RnJpZW5kKExvbmcpIEZyaWVuZD9cbiAgICArZ2V0RnJpZW5kT3JOdWxsKExvbmcpIEZyaWVuZFxuICAgICtnZXRHcm91cChMb25nKSBHcm91cD9cbiAgICArZ2V0R3JvdXBPckZhaWwoTG9uZykgR3JvdXBcbiAgICArbG9naW4oKVxuICAgICtjbG9zZSgpXG59XG5cbmNsYXNzIENvbnRhY3RPckJvdCB7XG4gICAgK2lkOiBJbnRcbn1cblxuY2xhc3MgVXNlck9yQm90IHtcbiAgICArbnVkZ2UoKSBOdWRnZVxufVxuXG5jbGFzcyBDb250YWN0IHtcbiAgICArYm90OiBCb3RcbiAgICArc2VuZE1lc3NhZ2UoTWVzc2FnZSkgTWVzc2FnZVJlY2VpcHRcbiAgICArc2VuZE1lc3NhZ2UoU3RyaW5nKSBNZXNzYWdlUmVjZWlwdFxuICAgICt1cGxvYWRJbWFnZShFeHRlcm5hbEltYWdlKSBJbWFnZVxufVxuXG5jbGFzcyBVc2VyIHtcbiAgICArbmljazogU3RyaW5nXG4gICAgK3JlbWFyazogU3RyaW5nXG4gICAgK2F2YXRhclVybDogU3RyaW5nXG59XG5cbmNsYXNzIEdyb3VwIHtcbiAgICArbWVtYmVyczogQ29udGFjdExpc3RcbiAgICArbmFtZTogU3RyaW5nXG4gICAgK3NldHRpbmdzOiBHcm91cFNldHRpbmdzXG4gICAgK293bmVyOiBOb3JtYWxNZW1iZXJcbiAgICArYm90TXV0ZVJlbWFpbmluZzogTG9uZ1xuICAgICtib3RQZXJtaXNzaW9uOiBNZW1iZXJQZXJtaXNzaW9uXG4gICAgK3F1aXQoKSBCb29sZWFuXG4gICAgK3VwbG9hZFZvaWNlKCkgVm9pY2Vcbn1cblxuY2xhc3MgTm9ybWFsTWVtYmVyIHtcbiAgICArbXV0ZSgpXG4gICAgK2tpY2soKVxufVxuXG5jbGFzcyBBbm9ueW1vdXNNZW1iZXIge1xuICAgICthbm9ueW1vdXNJZDogU3RyaW5nXG59XG5cbmNsYXNzIE1lbWJlciB7XG4gICAgK2dyb3VwOiBHcm91cFxufVxuXG5Db250YWN0T3JCb3Q8LS1Db250YWN0XG5Db250YWN0T3JCb3Q8LS1Vc2VyT3JCb3RcblxuVXNlck9yQm90PC0tQm90XG5Vc2VyT3JCb3Q8LS1Vc2VyXG5cbkNvbnRhY3Q8LS1Vc2VyXG5Db250YWN0PC0tR3JvdXBcblxuVXNlcjwtLU1lbWJlclxuVXNlcjwtLUZyaWVuZFxuXG5NZW1iZXI8LS1Ob3JtYWxNZW1iZXJcbk1lbWJlcjwtLUFub255bW91c01lbWJlciIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0In0sInVwZGF0ZUVkaXRvciI6ZmFsc2V9)
|
||||
|
||||
要主动发送一条消息,调用 `Contact.sendMessage()` 即可。
|
||||
|
||||
可通过 `Bot.getFriend` 或 `Bot.getGroup` 获取相关对象。
|
||||
|
||||
> 下一步,[Events](Events.md)
|
||||
>
|
||||
> [回到 Mirai 文档索引](README.md)
|
333
docs/Events.md
Normal file
333
docs/Events.md
Normal file
@ -0,0 +1,333 @@
|
||||
# Mirai - Events
|
||||
|
||||
## 目录
|
||||
|
||||
- [事件系统](#事件系统)
|
||||
- [监听事件](#监听事件)
|
||||
- [使用 `ListenerHost` 监听事件](#使用-listenerhost-监听事件)
|
||||
- [在 Kotlin 函数式监听](#在-kotlin-函数式监听)
|
||||
- [实现事件](#实现事件)
|
||||
- [工具函数(Kotlin)](#工具函数kotlin)
|
||||
|
||||
|
||||
## 事件系统
|
||||
|
||||
Mirai 以事件驱动,使用者需要监听如 `收到消息`,`收到入群申请` 等事件。
|
||||
|
||||
[`Event`]: ../mirai-core-api/src/commonMain/kotlin/event/Event.kt#L21-L62
|
||||
|
||||
每个事件都实现接口 [`Event`],且继承 `AbstractEvent`。
|
||||
实现 `CancellableEvent` 的事件可以被取消(`CancellableEvent.cancel`)。
|
||||
|
||||
[事件列表](../mirai-core-api/src/commonMain/kotlin/event/events/README.md#事件)
|
||||
|
||||
## 监听事件
|
||||
|
||||
有两种方法监听事件:
|
||||
- [使用 `ListenerHost` 监听事件](#使用-listenerhost-监听事件)
|
||||
- [在 Kotlin 函数式监听](#在-kotlin-函数式监听)
|
||||
|
||||
### 使用 `ListenerHost` 监听事件
|
||||
|
||||
标注一个函数(方法)为事件监听器。mirai 通过反射获取他们并为之注册事件。
|
||||
|
||||
> 详见 [EventHandler](../mirai-core-api/src/commonMain/kotlin/event/JvmMethodListeners.kt#L27-L168)
|
||||
|
||||
#### Kotlin 函数
|
||||
|
||||
Kotlin 函数要求:
|
||||
- 接收者 (英 receiver) 和函数参数: 所标注的 Kotlin 函数必须至少拥有一个接收者或一个函数参数, 或二者都具有. 接收者和函数参数的类型必须相同 (如果二者都存在)
|
||||
接收者或函数参数的类型都必须为 `Event` 或其子类.
|
||||
- 返回值: 为 `Unit` 或不指定返回值时将注册为 `CoroutineScope.subscribeAlways`, 为 `ListeningStatus` 时将注册为 `CoroutineScope.subscribe`.
|
||||
任何其他类型的返回值将会在注册时抛出异常.
|
||||
|
||||
所有 Kotlin 非 `suspend` 的函数都将会在 `Dispatchers.IO` 中调用
|
||||
|
||||
支持的函数类型:
|
||||
```kotlin
|
||||
// 所有函数参数, 函数返回值都不允许标记为可空 (带有 '?' 符号)
|
||||
// T 表示任何 Event 类型.
|
||||
suspend fun T.onEvent(T)
|
||||
suspend fun T.onEvent(T): ListeningStatus
|
||||
suspend fun T.onEvent(T): Nothing
|
||||
suspend fun onEvent(T)
|
||||
suspend fun onEvent(T): ListeningStatus
|
||||
suspend fun onEvent(T): Nothing
|
||||
suspend fun T.onEvent()
|
||||
suspend fun T.onEvent(): ListeningStatus
|
||||
suspend fun T.onEvent(): Nothing
|
||||
fun T.onEvent(T)
|
||||
fun T.onEvent(T): ListeningStatus
|
||||
fun T.onEvent(T): Nothing
|
||||
fun onEvent(T)
|
||||
fun onEvent(T): ListeningStatus
|
||||
fun onEvent(T): Nothing
|
||||
fun T.onEvent()
|
||||
fun T.onEvent(): ListeningStatus
|
||||
fun T.onEvent(): Nothing
|
||||
```
|
||||
|
||||
Kotlin 使用示例:
|
||||
|
||||
- 独立 `CoroutineScope` 和 `ListenerHost`
|
||||
```kotlin
|
||||
object MyEvents : ListenerHost {
|
||||
override val coroutineContext = SupervisorJob()
|
||||
// 可以抛出任何异常, 将在 this.coroutineContext 或 registerEvents 时提供的 CoroutineScope.coroutineContext 中的 CoroutineExceptionHandler 处理.
|
||||
@EventHandler
|
||||
suspend fun MessageEvent.onMessage() {
|
||||
reply("received")
|
||||
}
|
||||
}
|
||||
myCoroutineScope.registerEvents(MyEvents)
|
||||
```
|
||||
`onMessage` 抛出的异常将会交给 `myCoroutineScope` 处理
|
||||
|
||||
- 合并 `CoroutineScope` 和 `ListenerHost`: 使用 `SimpleListenerHost`
|
||||
```kotlin
|
||||
object MyEvents : SimpleListenerHost( /* override coroutineContext here */ ) {
|
||||
override fun handleException(context: CoroutineContext, exception: Throwable) {
|
||||
// 处理 onMessage 中未捕获的异常
|
||||
}
|
||||
@EventHandler
|
||||
suspend fun MessageEvent.onMessage() { // 可以抛出任何异常, 将在 handleException 处理
|
||||
reply("received")
|
||||
// 无返回值 (或者返回 Unit), 表示一直监听事件.
|
||||
}
|
||||
@EventHandler
|
||||
suspend fun MessageEvent.onMessage(): ListeningStatus { // 可以抛出任何异常, 将在 handleException 处理
|
||||
reply("received")
|
||||
return ListeningStatus.LISTENING // 表示继续监听事件
|
||||
// return ListeningStatus.STOPPED // 表示停止监听事件
|
||||
}
|
||||
}
|
||||
MyEvents.registerEvents()
|
||||
```
|
||||
#### Java 方法
|
||||
|
||||
所有 Java 方法都会在 `Dispatchers.IO` 中调用,因此在 Java 可以调用阻塞方法。
|
||||
|
||||
支持的方法类型:
|
||||
```
|
||||
// T 表示任何 Event 类型.
|
||||
void onEvent(T)
|
||||
Void onEvent(T)
|
||||
ListeningStatus onEvent(T) // 返回 null 时将抛出异常
|
||||
```
|
||||
|
||||
Java 使用示例:
|
||||
|
||||
```java
|
||||
public class MyEventHandlers extends SimpleListenerHost {
|
||||
@Override
|
||||
public void handleException(@NotNull CoroutineContext context, @NotNull Throwable exception){
|
||||
// 处理事件处理时抛出的异常
|
||||
}
|
||||
@EventHandler
|
||||
public void onMessage(@NotNull MessageEvent event) throws Exception { // 可以抛出任何异常, 将在 handleException 处理
|
||||
event.subject.sendMessage("received");
|
||||
// 无返回值, 表示一直监听事件.
|
||||
}
|
||||
@NotNull
|
||||
@EventHandler
|
||||
public ListeningStatus onMessage(@NotNull MessageEvent event) throws Exception { // 可以抛出任何异常, 将在 handleException 处理
|
||||
event.subject.sendMessage("received");
|
||||
return ListeningStatus.LISTENING; // 表示继续监听事件
|
||||
// return ListeningStatus.STOPPED; // 表示停止监听事件
|
||||
}
|
||||
}
|
||||
|
||||
// 注册:
|
||||
// Events.registerEvents(new MyEventHandlers())
|
||||
```
|
||||
|
||||
### 在 Kotlin 函数式监听
|
||||
[CoroutineScope.subscribe](../mirai-core-api/src/commonMain/kotlin/event/subscriber.kt#L137-L220)
|
||||
|
||||
用法示例:
|
||||
```kotlin
|
||||
object MyApplication : CoroutineScope by CoroutineScope(SupervisorJob())
|
||||
|
||||
// 启动事件监听器
|
||||
MyApplication.subscribeAlways<GroupMessageEvent> {
|
||||
// this: GroupMessageEvent
|
||||
// it: GroupMessageEvent
|
||||
// lambda 的 this 和参数都是 GroupMessageEvent
|
||||
|
||||
group.sendMessage(sender.at() + "Hello! ${sender.nick}")
|
||||
}
|
||||
|
||||
// YouApplication[Job]!!.cancel() //
|
||||
```
|
||||
|
||||
Mirai 也支持传递函数引用:
|
||||
```kotlin
|
||||
suspend fun GroupMessageEvent.onEvent() {
|
||||
group.sendMessage(sender.at() + "Hello! ${sender.nick}")
|
||||
}
|
||||
|
||||
MyApplication.subscribeAlways<GroupMessageEvent>(GroupMessageEvent::onEvent)
|
||||
```
|
||||
既可以使用接收者参数,又可以使用普通参数,还可以同时拥有。如下三个定义都是被接受的:
|
||||
```kotlin
|
||||
suspend fun GroupMessageEvent.onEvent()
|
||||
suspend fun GroupMessageEvent.onEvent(event: GroupMessageEvent)
|
||||
suspend fun onEvent(event: GroupMessageEvent)
|
||||
```
|
||||
|
||||
### 在 Kotlin 使用 DSL 监听事件
|
||||
> **警告:此节内容需要坚实的 Kotlin 技能,盲目使用会导致问题**
|
||||
|
||||
[subscribeMessages](../mirai-core-api/src/commonMain/kotlin/event/subscribeMessages.kt#L37-L64)
|
||||
|
||||
示例:
|
||||
```kotlin
|
||||
MyApplication.subscribeMessages {
|
||||
"test" {
|
||||
// 当消息内容为 "test" 时执行
|
||||
// this: MessageEvent
|
||||
reply("test!")
|
||||
}
|
||||
|
||||
"Hello" reply "Hi" // 当消息内容为 "Hello" 时回复 "Hi"
|
||||
"quote me" quoteReply "ok" // 当消息内容为 "quote me" 时引用该消息并回复 "ok"
|
||||
"quote me2" quoteReply {
|
||||
// lambda 也是允许的:
|
||||
// 返回值接受 Any?
|
||||
// 为 Unit 时不发送任何内容;
|
||||
// 为 Message 时直接发送;
|
||||
// 为 String 时发送为 PlainText;
|
||||
// 否则 toString 并发送为 PlainText
|
||||
|
||||
"ok"
|
||||
}
|
||||
|
||||
case("iGNorECase", ignoreCase=true) reply "OK" // 忽略大小写
|
||||
startsWith("-") reply { cmd ->
|
||||
// 当消息内容以 "-" 开头时执行
|
||||
// cmd 为消息去除开头 "-" 的内容
|
||||
}
|
||||
|
||||
|
||||
val listener: Listener<MessageEvent> = "1" reply "2"
|
||||
// 每个语句都会被注册为事件监听器,可以这样获取监听器
|
||||
|
||||
listener.complete() // 停止 "1" reply "2" 这个事件监听
|
||||
}
|
||||
```
|
||||
|
||||
## 实现事件
|
||||
|
||||
只要实现接口 `Event` 并继承 `AbstractEvent` 的对象就可以被广播。
|
||||
|
||||
要广播一个事件,使用 `Event.broadcast()`(Kotlin)或 `EventKt.broadcast(Event)`(Java)。
|
||||
|
||||
## 工具函数(Kotlin)
|
||||
|
||||
*可能需要较好的 Kotlin 技能才能理解以下内容。*
|
||||
|
||||
基于 Kotlin 协程特性,mirai 提供 `
|
||||
|
||||
### 线性同步(`syncFromEvent`)
|
||||
[linear.kt](../mirai-core-api/src/commonMain/kotlin/event/linear.kt)
|
||||
|
||||
挂起协程并获取下一个戳 Bot 的对象:
|
||||
```kotlin
|
||||
val target: UserOrBot = syncFromEvent<BotNudgedEvent> { sender }
|
||||
```
|
||||
|
||||
带超时版本:
|
||||
```kotlin
|
||||
val target: UserOrBot = syncFromEvent<BotNudgedEvent>(5000) { sender } // 5000ms
|
||||
```
|
||||
|
||||
异步 `async` 版本:
|
||||
|
||||
```kotlin
|
||||
val target: Deferred<UserOrBot> = coroutineScope.asyncFromEvent<BotNudgedEvent> { sender }
|
||||
```
|
||||
|
||||
### 线性同步(`nextEvent`)
|
||||
[nextEvent.kt](../mirai-core-api/src/commonMain/kotlin/event/nextEvent.kt)
|
||||
|
||||
挂起协程并获取下一个指定事件:
|
||||
|
||||
```kotlin
|
||||
val event: BotNudgedEvent = nextEvent<BotNudgedEvent>()
|
||||
```
|
||||
|
||||
带超时和过滤器版本:
|
||||
|
||||
```kotlin
|
||||
val event: BotNudgedEvent = nextEvent<BotNudgedEvent>(5000) { it.bot.id == 123456L }
|
||||
```
|
||||
|
||||
### 条件选择(`selectMessages`)
|
||||
> **警告:此节内容需要坚实的 Kotlin 技能,盲目使用会导致问题**
|
||||
|
||||
[select.kt](../mirai-core-api/src/commonMain/kotlin/event/select.kt)
|
||||
|
||||
类似于 Kotlin 协程 `select`,mirai 也提供类似的功能。
|
||||
|
||||
`selectMessages`:挂起当前协程,等待任意一个事件监听器触发后返回其返回值。
|
||||
|
||||
```kotlin
|
||||
MyCoroutineScope.subscribeAlways<GroupMessageEvent> {
|
||||
if (message.contentEquals("ocr")) {
|
||||
reply("请发送你要进行 OCR 的图片或图片链接")
|
||||
val image: InputStream = selectMessages {
|
||||
has<Image> { URL(it.queryUrl()).openStream() }
|
||||
has<PlainText> { URL(it.content).openStream() }
|
||||
defaultReply { "请发送图片或图片链接" }
|
||||
timeout(30_000) { event.quoteReply("请在 30 秒内发送图片或图片链接"); null }
|
||||
} ?: return@subscribeAlways
|
||||
|
||||
val result = ocr(image)
|
||||
quoteReply(result)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这种语法就相当于(伪代码):
|
||||
```
|
||||
val image = when (下一条消息) {
|
||||
包含图片 { 查询图片链接() }
|
||||
包含纯文本 { 下载图片() }
|
||||
其他情况 { 引用回复() }
|
||||
超时 { 引用回复() }
|
||||
}
|
||||
```
|
||||
|
||||
### 循环条件选择(`whileSelectMessages`)
|
||||
> **警告:此节内容需要坚实的 Kotlin 技能,盲目使用会导致问题**
|
||||
|
||||
[select.kt](../mirai-core-api/src/commonMain/kotlin/event/select.kt)
|
||||
|
||||
类似于 Kotlin 协程 `whileSelect`,mirai 也提供类似的功能。
|
||||
|
||||
`whileSelectMessages`:挂起当前协程,等待任意一个事件监听器返回 `false` 后返回。
|
||||
|
||||
```kotlin
|
||||
reply("开启复读模式")
|
||||
whileSelectMessages {
|
||||
"stop" {
|
||||
reply("已关闭复读")
|
||||
false // 停止循环
|
||||
}
|
||||
// 也可以使用 startsWith("") { true } 等 DSL
|
||||
default {
|
||||
reply(message)
|
||||
true // 继续循环
|
||||
}
|
||||
timeout(3000) {
|
||||
// on
|
||||
true
|
||||
}
|
||||
} // 等待直到 `false`
|
||||
reply("复读模式结束")
|
||||
```
|
||||
|
||||
|
||||
> 下一步,[Messages](Messages.md)
|
||||
>
|
||||
> [回到 Mirai 文档索引](README.md)
|
50
docs/Evolution.md
Normal file
50
docs/Evolution.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Mirai - Evolution
|
||||
|
||||
### Mirai 演进
|
||||
|
||||
Mirai 是不断前进的库,将来必定会发生 API 弃用和重构。
|
||||
维护者会严谨地推进每一项修改,并提供迁移周期(至少 2 个次版本)。
|
||||
|
||||
### 版本规范
|
||||
|
||||
Mirai 的版本号遵循 [语义化版本 2.0.0](https://semver.org/lang/zh-CN/#spec-item-9) 规范。
|
||||
|
||||
在日常开发中, Mirai 会以 `-dev-1`,`-dev-2` 等版本后缀发布开发预览版本。这些版本仅用于兼容性测试等目的,无稳定性保证。
|
||||
|
||||
在大版本开发过程中,Mirai 会以 `-M1`, `-M2` 等版本后缀发布里程碑预览版本。代表一系列功能的完成,但还不稳定。
|
||||
这些版本里新增的 API 仍可能还会在下一个 Milestone 版本变化,因此请按需使用。
|
||||
|
||||
在大版本即将发布前,Mirai 会以 `-RC` 版本后缀发布最终的预览版本。
|
||||
`RC` 表示新版本 API 已经确定,离稳定版发布只差最后的一些内部优化或 bug 修复。
|
||||
|
||||
### 版本选择
|
||||
|
||||
**稳定性**:稳定 (`x.y.z`) > 发布预览 (`-RC`) > 里程碑预览 (`-M`) > 开发 (`-dev`)。
|
||||
|
||||
| 目的 | 推荐至少更新到版本 |
|
||||
|:------------------:|:--------------:|
|
||||
| 生产环境 | `x.y.z` |
|
||||
| 希望尽早体验稳定新特性 | `-RC` |
|
||||
| 无论如何都想体验新特性 | `-M` |
|
||||
| 为 Mirai 提交 PR | `-dev` |
|
||||
|
||||
## 更新兼容性
|
||||
|
||||
对于 `x.y.z` 版本号:
|
||||
- 当 `z` 增加时,只会有 bug 修复,和必要的新函数添加(为了解决某一个问题),不会有破坏性变化。
|
||||
- 当 `y` 增加时,可能有新 API 的引入,和旧 API 的弃用。但这些弃用会经过一个弃用周期后才被删除(隐藏)。向下兼容得到保证。
|
||||
- 当 `x` 增加时,任何 API 都可能会有变化。无兼容性保证。
|
||||
|
||||
## 弃用周期
|
||||
|
||||
一个计划被删除的 API,将会在下一个次版本开始经历弃用周期。
|
||||
|
||||
如一个 API 在 `1.1.0` 起被弃用,它首先会是 `WARNING` (使用时会得到一个编译警告)弃用级别。
|
||||
在 `1.2.0` 上升为 `ERROR`(使用时会得到一个编译错误);
|
||||
在 `1.3.0` 上升为 `HIDDEN`(使用者无法看到这些 API)。
|
||||
|
||||
`HIDDEN` 的 API 仍然会保留在代码中并正常编译,以提供二进制兼容性,直到下一个主版本更新。
|
||||
|
||||
|
||||
|
||||
> [回到 Mirai 文档索引](README.md)
|
11
docs/Messages.md
Normal file
11
docs/Messages.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Mirai - Messages
|
||||
|
||||
## 消息系统
|
||||
|
||||
在 Contacts 章节提到,要发送消息,使用 `Contact.sendMessage(Message)`。
|
||||
|
||||
[![](https://mermaid.ink/img/eyJjb2RlIjoiY2xhc3NEaWFncmFtXG5cbmNsYXNzIE1lc3NhZ2VDaGFpblxuTWVzc2FnZUNoYWluIDogTGlzdH5TaW5nbGVNZXNzYWdlflxuXG5NZXNzYWdlPHwtLU1lc3NhZ2VDaGFpblxuTWVzc2FnZTx8LS1TaW5nbGVNZXNzYWdlXG5cbk1lc3NhZ2VDaGFpbiBvLS0gU2luZ2xlTWVzc2FnZVxuXG5TaW5nbGVNZXNzYWdlPHwtLU1lc3NhZ2VDb250ZW50XG5TaW5nbGVNZXNzYWdlPHwtLU1lc3NhZ2VNZXRhZGF0YVxuXG4iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ)](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiY2xhc3NEaWFncmFtXG5cbmNsYXNzIE1lc3NhZ2VDaGFpblxuTWVzc2FnZUNoYWluIDogTGlzdH5TaW5nbGVNZXNzYWdlflxuXG5NZXNzYWdlPHwtLU1lc3NhZ2VDaGFpblxuTWVzc2FnZTx8LS1TaW5nbGVNZXNzYWdlXG5cbk1lc3NhZ2VDaGFpbiBvLS0gU2luZ2xlTWVzc2FnZVxuXG5TaW5nbGVNZXNzYWdlPHwtLU1lc3NhZ2VDb250ZW50XG5TaW5nbGVNZXNzYWdlPHwtLU1lc3NhZ2VNZXRhZGF0YVxuXG4iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ)
|
||||
|
||||
`SingleMessage` 表示单个消息元素,`MessageChain`(消息链) 是 `List<SingleMessage>`。主动发送的消息和从服务器接收消息都是 `MessageChain`。
|
||||
|
||||
mirai 提供大量消息链的扩展:[MessageChain.kt](../mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt#L59)。
|
@ -4,12 +4,10 @@
|
||||
|
||||
## mirai 码
|
||||
mirai 的部分 [消息](../mirai-core-api/src/commonMain/kotlin/message/data/Message.kt) 可以表示为形如 `[mirai:atall]` 的字符串.
|
||||
模块 `mirai-core` 包含消息到 mirai 码的单向转换; 模块 `mirai-serialization` 提供 mirai 码的解析.
|
||||
|
||||
运行时: [mirai-serialization](../mirai-serialization/)
|
||||
|
||||
## 变更记录
|
||||
- `1.1.0`: 引入 mirai 码于 `mirai-serialization` 模块
|
||||
- `1.2.0`: mirai 码集成到 mirai-core。不再需要 `mirai-serialization` 模块。
|
||||
|
||||
## 格式
|
||||
|
71
docs/README.md
Normal file
71
docs/README.md
Normal file
@ -0,0 +1,71 @@
|
||||
# Mirai
|
||||
|
||||
欢迎来到 mirai 文档。
|
||||
|
||||
## 生态
|
||||
|
||||
[Mirai 生态概览](mirai-ecology.md)
|
||||
|
||||
## 确定 SDK
|
||||
|
||||
mirai 官方提供 Kotlin/Java 等 JVM 平台语言开发支持。如果你不熟悉该语言,请使用以下社区提供的 SDK:
|
||||
|
||||
### 基于 [`mirai-console`]
|
||||
|
||||
[`mirai-console`]: https://github.com/mamoe/mirai-console
|
||||
|
||||
这些 SDK 基于 [`mirai-console`],意味着需要使用 [`mirai-console`] 框架。[`mirai-console`] 也是 mirai 的官方项目之一。
|
||||
|
||||
[mamoe/mirai-api-http]: https://github.com/mamoe/mirai-api-http
|
||||
[iTXTech/mirai-native]: https://github.com/iTXTech/mirai-native
|
||||
[iTXTech/mirai-js]: https://github.com/iTXTech/mirai-js
|
||||
[GraiaProject/Application]: https://github.com/GraiaProject/Application
|
||||
[RedBeanN/node-mirai]: https://github.com/RedBeanN/node-mirai
|
||||
[Logiase/gomirai]: https://github.com/Logiase/gomirai
|
||||
[StageGuard/mirai-rhinojs-sdk]: https://github.com/StageGuard/mirai-rhinojs-sdk
|
||||
[cyanray/mirai-cpp]: https://github.com/cyanray/mirai-cpp
|
||||
[Chlorie/miraipp]: https://github.com/Chlorie/miraipp-template
|
||||
[Executor-Cheng/mirai-CSharp]: https://github.com/Executor-Cheng/mirai-CSharp
|
||||
[HoshinoTented/mirai-rs]: https://github.com/HoshinoTented/mirai-rs
|
||||
[YunYouJun/mirai-ts]: https://github.com/YunYouJun/mirai-ts
|
||||
[only52607/e-mirai]: https://github.com/only52607/e-mirai
|
||||
[theGravityLab/ProjHyperai]: https://github.com/theGravityLab/ProjHyperai
|
||||
[yyuueexxiinngg/cqhttp-mirai]: https://github.com/yyuueexxiinngg/cqhttp-mirai
|
||||
|
||||
| 技术 | 维护者及项目地址 | 描述 |
|
||||
|:----------------|:--------------------------------------------|:-----------------------------------------------------------------------|
|
||||
| *Http* | [mamoe/mirai-api-http] | Mirai 官方维护的 HTTP API 插件 |
|
||||
| `JavaScript` | [iTXTech/mirai-js] | 支持使用 `JavaScript` 编写插件并**直接**与 mirai 交互 |
|
||||
| `Python` | [Graia Framework][GraiaProject/Application] | 基于 `mirai-api-http` 的机器人开发框架 |
|
||||
| `Node.js` | [RedBeanN/node-mirai] | mirai 的 Node.js SDK |
|
||||
| `Go` | [Logiase/gomirai] | 基于 mirai-api-http 的 GoLang SDK |
|
||||
| `Mozilla Rhino` | [StageGuard/mirai-rhinojs-sdk] | 为基于 Rhino(如 Auto.js 等安卓 app 或运行环境)的 JavaScript 提供简单易用的 SDK |
|
||||
| `C++` | [cyanray/mirai-cpp] | mirai-http-api 的 C++ 封装,方便使用 C++ 开发 mirai-http-api 插件 |
|
||||
| `C++` | [Chlorie/miraipp] | mirai-http-api 的另一个 C++ 封装,使用现代 C++ 特性,并提供了较完善的说明文档 |
|
||||
| `C#` | [Executor-Cheng/mirai-CSharp] | 基于 mirai-api-http 的 C# SDK |
|
||||
| `Rust` | [HoshinoTented/mirai-rs] | mirai-http-api 的 Rust 封装 |
|
||||
| `TypeScript` | [YunYouJun/mirai-ts] | mirai-api-http 的 TypeScript SDK,附带声明文件,拥有良好的注释和类型提示 |
|
||||
| `易语言` | [only52607/e-mirai] | mirai-api-http 的 易语言 SDK,使用全中文环境开发插件,适合编程新手使用 |
|
||||
| `.Net/C#` | [Hyperai][theGravityLab/ProjHyperai] | 从 mirai-api-http 对接到机器人开发框架再到开箱即用的插件式机器人程序一应俱全 |
|
||||
| *酷 Q 插件* | [iTXTech/mirai-native] | 支持酷 Q 插件在 mirai 上运行 |
|
||||
| *酷 Q HTTP* | [yyuueexxiinngg/cqhttp-mirai] | 在 mirai-console 开启酷 Q HTTP 服务。 |
|
||||
|
||||
### 基于 `mirai-core` 的 SDK
|
||||
|
||||
- `Lua`: [lua-mirai](https://github.com/only52607/lua-mirai) 基于 mirai-core 的 Lua SDK,并提供了 Java 扩展支持,可在 Lua 中调用 Java 代码开发机器人
|
||||
|
||||
## 配置 JVM 项目使用 mirai
|
||||
|
||||
> 可以首先体验让机器人发送消息:在 IDE 克隆 [mirai-hello-world](https://github.com/project-mirai/mirai-hello-world) 并运行其中入口点。
|
||||
|
||||
要把 mirai 作为一个依赖库使用,请参考 [Configuring Projects](ConfiguringProjects.md)。
|
||||
|
||||
要使用 mirai-console 框架,请前往 [mirai-console](https://github.com/mamoe/mirai-console)。
|
||||
|
||||
## 开发
|
||||
|
||||
***本文档正在更新中***
|
||||
|
||||
- [Bots](Bots.md)
|
||||
- [Contacts](Contacts.md)
|
||||
- [Events](Events.md)
|
@ -1,186 +0,0 @@
|
||||
# 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: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: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'
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
@ -1,131 +0,0 @@
|
||||
# Mirai Guide - Getting Started
|
||||
|
||||
由于Mirai项目在快速推进中,因此内容时有变动,本文档的最后更新日期为```2020-04-01```,对应版本```0.31.4```
|
||||
|
||||
假如仅仅使用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 {
|
||||
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
|
||||
mavenCentral()
|
||||
}
|
||||
原文内容,更新为下文
|
||||
*/
|
||||
|
||||
repositories {
|
||||
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
|
||||
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:1.1-EA'//此处版本应替换为当前最新
|
||||
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.join
|
||||
import net.mamoe.mirai.message.data.At
|
||||
import net.mamoe.mirai.contact.Member
|
||||
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(At(sender as Member) + " 给爷爬 ")
|
||||
}
|
||||
|
||||
(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</artifactId>
|
||||
<version>0.23.0</version> <!-- 替换版本为最新版本 -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
@ -1,103 +0,0 @@
|
||||
# Mirai Guide - Quick Start
|
||||
|
||||
由于 mirai 项目在快速推进中,因此内容时有变动,本文档的最后更新日期为```2020/6/22```,对应版本```1.0.2```
|
||||
|
||||
本文适用于对 Kotlin 较熟悉的开发者,
|
||||
使用 mirai 作为第三方依赖库引用到任意一个 Kotlin, Java 或其他 JVM 平台的项目
|
||||
|
||||
**若你希望一份更基础的教程**, 请参阅: [mirai-guide-getting-started](guide_getting_started.md)
|
||||
|
||||
## 构建需求
|
||||
|
||||
- JDK 6 或更高
|
||||
|
||||
## 获取 Demo
|
||||
可在 [mirai-demos](https://github.com/mamoe/mirai-demos) 中获取已经配置好依赖的示例项目.
|
||||
|
||||
## Quick Start
|
||||
|
||||
请将 `VERSION` 替换为 `mirai-core` 的最新版本号(如 `1.0.4`):
|
||||
[![Download](https://api.bintray.com/packages/him188moe/mirai/mirai-core/images/download.svg)](https://bintray.com/him188moe/mirai/mirai-core/)
|
||||
|
||||
### 添加依赖
|
||||
可通过以下三种方法之一添加 mirai 依赖.
|
||||
|
||||
#### Maven
|
||||
|
||||
```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</artifactId>
|
||||
<version>0.23.0</version> <!-- 替换版本为最新版本 -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
#### Gradle (推荐)
|
||||
|
||||
Mirai 只发布在 `jcenter`, 因此请确保添加 `jcenter()` 仓库:
|
||||
|
||||
```kotlin
|
||||
repositories{
|
||||
jcenter()
|
||||
}
|
||||
```
|
||||
|
||||
**注意:**
|
||||
Mirai 核心由 API 模块(`mirai-core`)和协议模块组成。依赖协议模块时会自动依赖相应版本的 API 模块。
|
||||
请参照下文选择目标平台的依赖添加。
|
||||
|
||||
如果你只用 Java / Kotlin 或其他语言开发 JVM 平台应用,只需要添加下文第一条。
|
||||
如果你只开发 Android 应用,只需添加下文第三条。
|
||||
|
||||
**jvm** (JVM 平台源集)
|
||||
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid:VERSION")
|
||||
```
|
||||
|
||||
**common** (Kotlin 多平台项目的通用源集)
|
||||
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid-common:VERSION")
|
||||
```
|
||||
|
||||
**android** (Android 平台源集)
|
||||
**注意**: 在 [KT-37152](https://youtrack.jetbrains.com/issue/KT-37152) 修复前, mirai 无法支持 Android 平台目标.
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid-android:VERSION")
|
||||
```
|
||||
|
||||
#### 直接导入jar包 (不推荐)
|
||||
下载已经编译好的 Jar 包, 并添加 Jar 依赖:
|
||||
- [mirai-core](https://github.com/mamoe/mirai-repo/tree/master/shadow/mirai-core)
|
||||
- [mirai-qqandroid](https://github.com/mamoe/mirai-repo/tree/master/shadow/mirai-core-qqandroid)
|
||||
|
||||
|
||||
### 开始使用
|
||||
|
||||
```kotlin
|
||||
val bot = Bot(qqId, password).alsoLogin()
|
||||
bot.subscribeAlways<GroupMessageEvent> { event ->
|
||||
if (event.message.content.contains("你好")) {
|
||||
reply("你好!")
|
||||
} else if (event.message.content.contains("你好")) {
|
||||
File("C:\\image.png").sendAsImage()
|
||||
}
|
||||
}
|
||||
|
||||
bot.subscribeAlways<MemberPermissionChangedEvent> { event ->
|
||||
if (event.kind == BECOME_OPERATOR)
|
||||
reply("${event.member.id} 成为了管理员")
|
||||
}
|
||||
```
|
@ -1,111 +0,0 @@
|
||||
# Mirai Guide - Subscribe Events
|
||||
|
||||
由于Mirai项目在快速推进中,因此内容时有变动,本文档的最后更新日期为`2020-04-01`,对应版本`0.31.4`
|
||||
|
||||
本页面采用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-api/src/commonMain/kotlin/event/MessageSubscribers.kt#L140),本例也采用了[MessageSubscribersBuilder](https://github.com/mamoe/mirai/wiki/mirai-core-api/src/commonMain/kotlin/event/MessageSubscribers.kt#L140)。其他具体使用方法可以参考[Wiki:Message Event](https://github.com/mamoe/mirai/wiki/Development-Guide---Kotlin#Message-Event)部分。
|
||||
|
||||
## 事件-Event
|
||||
|
||||
上一节中提到的`Message Event`仅仅是众多`Event`的这一种,其他`Event`有:群员加入群,离开群,私聊等等...
|
||||
|
||||
具体事件文档暂不提供,可翻阅源码[**BotEvents.kt**](https://github.com/mamoe/mirai/blob/master/mirai-core-api/src/commonMain/kotlin/event/events/BotEvents.kt),查看注释。当前事件仍在扩充中,可能有一定不足。
|
||||
|
||||
下面我们开始示例对一些事件进行监听。
|
||||
|
||||
## 尝试监听事件-Try Subscribing Events
|
||||
|
||||
### 监听加群事件
|
||||
|
||||
在代码中的`miraiBot.join()`前添加
|
||||
|
||||
```kotlin
|
||||
miraiBot.subscribeAlways<MemberJoinEvent> {
|
||||
it.group.sendMessage(PlainText("欢迎 ${it.member.nameCardOrNick} 加入本群!"))
|
||||
}
|
||||
```
|
||||
|
||||
本段语句监听了加入群的事件。
|
||||
|
||||
### 监听禁言事件
|
||||
|
||||
在代码中添加
|
||||
|
||||
```kotlin
|
||||
miraiBot.subscribeAlways<MemberMuteEvent> {
|
||||
it.group.sendMessage(PlainText("恭喜老哥 ${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.contact.nameCardOrNick
|
||||
import net.mamoe.mirai.event.events.MemberJoinEvent
|
||||
import net.mamoe.mirai.event.events.MemberMuteEvent
|
||||
import net.mamoe.mirai.event.subscribeAlways
|
||||
import net.mamoe.mirai.event.subscribeMessages
|
||||
import net.mamoe.mirai.message.data.PlainText
|
||||
|
||||
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(PlainText("欢迎 ${it.member.nameCardOrNick} 加入本群!"))
|
||||
}
|
||||
miraiBot.subscribeAlways<MemberMuteEvent> {
|
||||
it.group.sendMessage(PlainText("恭喜老哥 ${it.member.nameCardOrNick} 喜提禁言套餐一份"))
|
||||
}
|
||||
miraiBot.join() // 等待 Bot 离线, 避免主线程退出
|
||||
}
|
||||
```
|
||||
|
||||
下面可以参阅[Mirai Guide - Build For Mirai](/docs/guide_build_for_mirai.md),对你的Mirai应用进行打包
|
@ -132,3 +132,5 @@ mirai-console-loader 应运而生,它的工作就是简化 console 启动流
|
||||
以上就是整个 Mirai 生态的概览,如有疏漏或错误,欢迎提出 Issue 修正。
|
||||
|
||||
实体关系图采用 [Mermaid](https://github.com/mermaid-js/mermaid) 绘制。
|
||||
|
||||
> [回到 Mirai 文档索引](README.md)
|
@ -26,7 +26,7 @@ mirai 项目整体由 核心 (`mirai-core`) 与 控制台(`mirai-console`) 组
|
||||
|
||||
`mirai-core` 设计为一个 **`支持库`**, 意味着它可以被独立依赖, 在任意项目中使用. 详见下文.
|
||||
|
||||
- `mirai-serialization` 依赖 `mirai-core`, 是 mirai-core 的序列化支持模块. 提供 `Message` 类型的序列化支持与相关 [mirai 码](mirai-code-specification.md) 支持.
|
||||
- `mirai-serialization` 依赖 `mirai-core`, 是 mirai-core 的序列化支持模块. 提供 `Message` 类型的序列化支持与相关 [mirai 码](MiraiCodeSepecification.md) 支持.
|
||||
此模块自 mirai `1.1.0` 起可用, 引用方法同 `mirai-core`.
|
||||
|
||||
- [`mirai-console`](https://github.com/mamoe/mirai-console) 是基于 `mirai-core` 的, 支持插件加载, 指令系统, 和配置等的**控制台框架**.
|
||||
|
74
docs/src/Contacts.mermaid.md
Normal file
74
docs/src/Contacts.mermaid.md
Normal file
@ -0,0 +1,74 @@
|
||||
```mermaid
|
||||
classDiagram
|
||||
|
||||
class Bot {
|
||||
+friends: ContactList
|
||||
+groups: ContactList
|
||||
+getFriend(Long) Friend?
|
||||
+getFriendOrNull(Long) Friend
|
||||
+getGroup(Long) Group?
|
||||
+getGroupOrFail(Long) Group
|
||||
+login()
|
||||
+close()
|
||||
}
|
||||
|
||||
class ContactOrBot {
|
||||
+id: Int
|
||||
}
|
||||
|
||||
class UserOrBot {
|
||||
+nudge() Nudge
|
||||
}
|
||||
|
||||
class Contact {
|
||||
+bot: Bot
|
||||
+sendMessage(Message) MessageReceipt
|
||||
+sendMessage(String) MessageReceipt
|
||||
+uploadImage(ExternalImage) Image
|
||||
}
|
||||
|
||||
class User {
|
||||
+nick: String
|
||||
+remark: String
|
||||
+avatarUrl: String
|
||||
}
|
||||
|
||||
class Group {
|
||||
+members: ContactList
|
||||
+name: String
|
||||
+settings: GroupSettings
|
||||
+owner: NormalMember
|
||||
+botMuteRemaining: Long
|
||||
+botPermission: MemberPermission
|
||||
+quit() Boolean
|
||||
+uploadVoice() Voice
|
||||
}
|
||||
|
||||
class NormalMember {
|
||||
+mute()
|
||||
+kick()
|
||||
}
|
||||
|
||||
class AnonymousMember {
|
||||
+anonymousId: String
|
||||
}
|
||||
|
||||
class Member {
|
||||
+group: Group
|
||||
}
|
||||
|
||||
ContactOrBot<|--Contact
|
||||
ContactOrBot<|--UserOrBot
|
||||
|
||||
UserOrBot<|--Bot
|
||||
UserOrBot<|--User
|
||||
|
||||
Contact<|--User
|
||||
Contact<|--Group
|
||||
|
||||
User<|--Member
|
||||
User<|--Friend
|
||||
|
||||
Member<|--NormalMember
|
||||
Member<|--AnonymousMember
|
||||
```
|
47
docs/src/Messages.mermaid.md
Normal file
47
docs/src/Messages.mermaid.md
Normal file
@ -0,0 +1,47 @@
|
||||
```mermaid
|
||||
classDiagram
|
||||
|
||||
class MessageChain
|
||||
MessageChain : List~SingleMessage~
|
||||
|
||||
Message<|--MessageChain
|
||||
Message<|--SingleMessage
|
||||
|
||||
MessageChain o-- SingleMessage
|
||||
|
||||
SingleMessage<|--MessageContent
|
||||
SingleMessage<|--MessageMetadata
|
||||
|
||||
|
||||
%%%
|
||||
|
||||
|
||||
MessageMetadata<|--QuoteReply
|
||||
MessageMetadata<|--MessageSource
|
||||
|
||||
|
||||
%%
|
||||
|
||||
MessageSource<|--OnlineMessageSource
|
||||
MessageSource<|--OfflineMessageSource
|
||||
|
||||
MessageContent<|--PlainText
|
||||
MessageContent<|--Image
|
||||
MessageContent<|--At
|
||||
MessageContent<|--AtAll
|
||||
MessageContent<|--Face
|
||||
MessageContent<|--ForwardMessage
|
||||
|
||||
MessageContent<|--HummerMessage
|
||||
HummerMessage<|--PokeMessage
|
||||
HummerMessage<|--VipFace
|
||||
HummerMessage<|--FlashImage
|
||||
|
||||
MessageContent<|--RichMessage
|
||||
RichMessage<|--ServiceMessage
|
||||
RichMessage<|--LightApp
|
||||
|
||||
|
||||
MessageContent<|--PttMessage
|
||||
PttMessage<|--Voice
|
||||
```
|
@ -24,6 +24,7 @@ description = "Mirai core shadowed"
|
||||
dependencies {
|
||||
api(project(":mirai-core"))
|
||||
api(project(":mirai-core-api"))
|
||||
api(project(":mirai-core-utils"))
|
||||
}
|
||||
|
||||
configurePublishing("mirai-core-all")
|
@ -24,8 +24,7 @@ import net.mamoe.mirai.utils.BotConfiguration
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
import java.util.*
|
||||
import kotlin.NoSuchElementException
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* 登录, 返回 [this]
|
||||
@ -153,14 +152,14 @@ public interface Bot : CoroutineScope, ContactOrBot, UserOrBot {
|
||||
|
||||
public companion object {
|
||||
@Suppress("ObjectPropertyName")
|
||||
internal val _instances: WeakHashMap<Long, Bot> = WeakHashMap()
|
||||
internal val _instances: ConcurrentHashMap<Long, Bot> = ConcurrentHashMap()
|
||||
|
||||
/**
|
||||
* 复制一份此时的 [Bot] 实例列表.
|
||||
*/
|
||||
@JvmStatic
|
||||
public val instances: List<Bot>
|
||||
get() = _instances.values.filterNotNull()
|
||||
get() = _instances.values.toList()
|
||||
|
||||
/**
|
||||
* 复制一份此时的 [Bot] 实例列表.
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2019-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.contact
|
||||
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.utils.MemberDeprecatedApi
|
||||
|
||||
/**
|
||||
* 匿名
|
||||
*
|
||||
* 代表匿名群成员
|
||||
*/
|
||||
public interface AnonymousMember : Member {
|
||||
/** 该匿名群成员 ID */
|
||||
public val anonymousId: String
|
||||
|
||||
@MemberDeprecatedApi(message = "无法发送信息至 AnonymousMember")
|
||||
@Deprecated(level = DeprecationLevel.ERROR, message = "无法发送信息至 AnonymousMember")
|
||||
public override suspend fun sendMessage(message: Message): Nothing
|
||||
|
||||
@Deprecated(level = DeprecationLevel.ERROR, message = "无法发送信息至 AnonymousMember")
|
||||
@MemberDeprecatedApi(message = "无法发送信息至 AnonymousMember")
|
||||
public override suspend fun sendMessage(message: String): Nothing
|
||||
|
||||
}
|
@ -53,13 +53,13 @@ public interface Group : Contact, CoroutineScope {
|
||||
*
|
||||
* @return 若机器人是群主, 返回 [botAsMember]. 否则返回相应的成员
|
||||
*/
|
||||
public val owner: Member
|
||||
public val owner: NormalMember
|
||||
|
||||
/**
|
||||
* [Bot] 在群内的 [Member] 实例
|
||||
*/
|
||||
@MiraiExperimentalApi
|
||||
public val botAsMember: Member
|
||||
public val botAsMember: NormalMember
|
||||
|
||||
/**
|
||||
* 机器人被禁言还剩余多少秒
|
||||
@ -89,14 +89,14 @@ public interface Group : Contact, CoroutineScope {
|
||||
*
|
||||
* 在 [Group] 实例创建的时候查询一次. 并与事件同步事件更新.
|
||||
*/
|
||||
public val members: ContactList<Member>
|
||||
public val members: ContactList<NormalMember>
|
||||
|
||||
/**
|
||||
* 获取群成员实例. 不存在时返回 `null`.
|
||||
*
|
||||
* 当 [id] 为 [Bot.id] 时返回 [botAsMember].
|
||||
*/
|
||||
public operator fun get(id: Long): Member?
|
||||
public operator fun get(id: Long): NormalMember?
|
||||
|
||||
@Deprecated("Use get", ReplaceWith("get(id)"))
|
||||
@PlannedRemoval("2.0-M2")
|
||||
@ -104,14 +104,14 @@ public interface Group : Contact, CoroutineScope {
|
||||
* 获取群成员实例, 不存在则 null
|
||||
* 当 [id] 为 [Bot.id] 时返回 [botAsMember]
|
||||
*/
|
||||
public fun getOrNull(id: Long): Member? = get(id)
|
||||
public fun getOrNull(id: Long): NormalMember? = get(id)
|
||||
|
||||
/**
|
||||
* 获取群成员实例. 不存在时抛出 [kotlin.NoSuchElementException].
|
||||
*
|
||||
* 当 [id] 为 [Bot.id] 时返回 [botAsMember].
|
||||
*/
|
||||
public fun getOrFail(id: Long): Member =
|
||||
public fun getOrFail(id: Long): NormalMember =
|
||||
get(id) ?: throw NoSuchElementException("member $id not found in group ${this.id}")
|
||||
|
||||
|
||||
@ -125,7 +125,7 @@ public interface Group : Contact, CoroutineScope {
|
||||
/**
|
||||
* 当 [member] 是本群成员时返回 `true`. 将同时成员 [所属群][Member.group]. 同一个用户在不同群内的 [Member] 对象不相等.
|
||||
*/
|
||||
public operator fun contains(member: Member): Boolean = member in members
|
||||
public operator fun contains(member: NormalMember): Boolean = member in members
|
||||
|
||||
|
||||
/**
|
||||
|
@ -12,33 +12,29 @@
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.JavaFriendlyAPI
|
||||
import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.event.events.BotMuteEvent
|
||||
import net.mamoe.mirai.event.events.MemberMuteEvent
|
||||
import net.mamoe.mirai.event.events.MemberPermissionChangeEvent
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.MessageReceipt.Companion.recall
|
||||
import net.mamoe.mirai.message.action.MemberNudge
|
||||
import net.mamoe.mirai.message.action.Nudge
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.isContentEmpty
|
||||
import net.mamoe.mirai.message.data.toPlainText
|
||||
import net.mamoe.mirai.utils.MemberDeprecatedApi
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
import net.mamoe.mirai.utils.WeakRefProperty
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
/**
|
||||
* 代表一位群成员.
|
||||
*
|
||||
* 群成员可能也是好友, 但他们在对象类型上不同.
|
||||
* 群成员可以通过 [asFriend] 得到相关好友对象.
|
||||
* 群成员分为 [普通成员][NormalMember] 和 [匿名成员][AnonymousMember]
|
||||
*
|
||||
* ## 与好友相关的操作
|
||||
* 一个群成员可能也是机器人的好友, 但他们在对象类型上不同 ([Member] != [Friend]). 可以通过 [Member.asFriend] 得到相关好友对象.
|
||||
*
|
||||
* ## 相关的操作
|
||||
* [Member.isFriend] 判断此成员是否为好友
|
||||
* [Member.isAnonymous] 判断此成员是否为匿名群成员
|
||||
* [Member.isNormal] 判断此成员是否为正常群成员
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_SUPER_CLASS")
|
||||
@OptIn(JavaFriendlyAPI::class)
|
||||
public interface Member : User {
|
||||
/**
|
||||
* 所在的群.
|
||||
@ -56,125 +52,62 @@ public interface Member : User {
|
||||
/**
|
||||
* 群名片. 可能为空.
|
||||
*
|
||||
* 管理员和群主都可修改任何人(包括群主)的群名片.
|
||||
*
|
||||
* 在修改时将会异步上传至服务器.
|
||||
*
|
||||
* @see [nameCardOrNick] 获取非空群名片或昵称
|
||||
*
|
||||
* @see MemberCardChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
|
||||
* @throws PermissionDeniedException 无权限修改时
|
||||
* @see [NormalMember.nameCard]
|
||||
* @see [AnonymousMember.nameCard]
|
||||
*/
|
||||
public var nameCard: String
|
||||
public val nameCard: String
|
||||
|
||||
/**
|
||||
* 群头衔.
|
||||
*
|
||||
* 仅群主可以修改群头衔.
|
||||
* 为 [AnonymousMember] 时一定是 `"匿名"`
|
||||
*
|
||||
* 在修改时将会异步上传至服务器.
|
||||
*
|
||||
* @see MemberSpecialTitleChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
|
||||
* @throws PermissionDeniedException 无权限修改时
|
||||
* @see [NormalMember.specialTitle]
|
||||
*/
|
||||
public var specialTitle: String
|
||||
public val specialTitle: String
|
||||
|
||||
/**
|
||||
* 被禁言剩余时长. 单位为秒.
|
||||
*
|
||||
* @see isMuted 判断改成员是否处于禁言状态
|
||||
* @see mute 设置禁言
|
||||
* @see unmute 取消禁言
|
||||
*/
|
||||
@MemberDeprecatedApi("仅 NormalMember 支持 muteTimeRemaining. 请先检查类型为 NormalMember.")
|
||||
@PlannedRemoval("2.0-M2")
|
||||
public val muteTimeRemaining: Int
|
||||
|
||||
/**
|
||||
* 禁言.
|
||||
* 禁言这个群成员 [durationSeconds] 秒, 在机器人无权限操作时抛出 [PermissionDeniedException].
|
||||
*
|
||||
* QQ 中最小操作和显示的时间都是一分钟.
|
||||
* 机器人可以实现精确到秒, 会被客户端显示为 1 分钟但不影响实际禁言时间.
|
||||
* QQ 中最小操作和显示的时间都是一分钟. 机器人可以实现精确到秒, 会被客户端显示为 1 分钟但不影响实际禁言时间.
|
||||
*
|
||||
* 管理员可禁言成员, 群主可禁言管理员和群员.
|
||||
*
|
||||
* @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
|
||||
* @return 机器人无权限时返回 `false`
|
||||
* @param durationSeconds 持续时间. 精确到秒. 最短 0 秒, 最长 30 天. 超过范围则会抛出异常 [IllegalStateException].
|
||||
*
|
||||
* @see Member.isMuted 判断此成员是否正处于禁言状态中
|
||||
* @see unmute 取消禁言此成员
|
||||
*
|
||||
* @see Int.minutesToSeconds
|
||||
* @see Int.hoursToSeconds
|
||||
* @see Int.daysToSeconds
|
||||
* @see NormalMember.isMuted 判断此成员是否正处于禁言状态中
|
||||
* @see NormalMember.unmute 取消禁言此成员
|
||||
*
|
||||
* @see MemberMuteEvent 成员被禁言事件
|
||||
* @see BotMuteEvent Bot 被禁言事件
|
||||
*
|
||||
* @throws PermissionDeniedException 无权限修改时抛出
|
||||
* @see Member.mute 支持 Kotlin [kotlin.time.Duration] 的扩展
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public suspend fun mute(durationSeconds: Int)
|
||||
|
||||
/**
|
||||
* 解除禁言.
|
||||
*
|
||||
* 管理员可解除成员的禁言, 群主可解除管理员和群员的禁言.
|
||||
*
|
||||
* @see Member.isMuted 判断此成员是否正处于禁言状态中
|
||||
*
|
||||
* @see MemberUnmuteEvent 成员被取消禁言事件
|
||||
*
|
||||
* @throws PermissionDeniedException 无权限修改时抛出
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
@MemberDeprecatedApi("仅 NormalMember 支持 unmute. 请先检查类型为 NormalMember.")
|
||||
@PlannedRemoval("2.0-M2")
|
||||
public suspend fun unmute()
|
||||
|
||||
/**
|
||||
* 踢出该成员.
|
||||
*
|
||||
* 管理员可踢出成员, 群主可踢出管理员和群员.
|
||||
*
|
||||
* @see MemberLeaveEvent.Kick 成员被踢出事件.
|
||||
* @throws PermissionDeniedException 无权限修改时
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
@MemberDeprecatedApi("仅 NormalMember 支持 kick. 请先检查类型为 NormalMember.")
|
||||
@PlannedRemoval("2.0-M2")
|
||||
public suspend fun kick(message: String = "")
|
||||
|
||||
/**
|
||||
* 向群成员发送消息.
|
||||
* 若群成员同时是好友, 则会发送好友消息. 否则发送临时会话消息.
|
||||
*
|
||||
* 单条消息最大可发送 4500 字符或 50 张图片.
|
||||
*
|
||||
* @see FriendMessagePreSendEvent 当此成员是好友时发送消息前事件
|
||||
* @see FriendMessagePostSendEvent 当此成员是好友时发送消息后事件
|
||||
*
|
||||
* @see TempMessagePreSendEvent 当此成员不是好友时发送消息前事件
|
||||
* @see TempMessagePostSendEvent 当此成员不是好友时发送消息后事件
|
||||
*
|
||||
* @throws EventCancelledException 当发送消息事件被取消时抛出
|
||||
* @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
|
||||
* @throws MessageTooLargeException 当消息过长时抛出
|
||||
* @throws IllegalArgumentException 当消息内容为空时抛出 (详见 [Message.isContentEmpty])
|
||||
*
|
||||
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
@MemberDeprecatedApi("仅 NormalMember 支持 sendMessage. 请先检查类型为 NormalMember.")
|
||||
public override suspend fun sendMessage(message: Message): MessageReceipt<Member>
|
||||
|
||||
/**
|
||||
* 发送纯文本消息
|
||||
* @see sendMessage
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public override suspend fun sendMessage(message: String): MessageReceipt<Member> =
|
||||
this.sendMessage(message.toPlainText())
|
||||
@MemberDeprecatedApi("仅 NormalMember 支持 sendMessage. 请先检查类型为 NormalMember.")
|
||||
public override suspend fun sendMessage(message: String): MessageReceipt<Member>
|
||||
|
||||
/**
|
||||
* 创建一个 "戳一戳" 消息
|
||||
*
|
||||
* @see Nudge.sendTo 发送这个戳一戳消息
|
||||
*/
|
||||
@MemberDeprecatedApi("仅 NormalMember 支持 nudge. 请先检查类型为 NormalMember.")
|
||||
@PlannedRemoval("2.0-M2")
|
||||
@MiraiExperimentalApi
|
||||
public override fun nudge(): MemberNudge = MemberNudge(this)
|
||||
public override fun nudge(): MemberNudge
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,7 +133,8 @@ public inline val Member.isFriend: Boolean
|
||||
*/
|
||||
@Deprecated(
|
||||
"Ambiguous function name and its behaviour. Use asFriendOrNull and let manually.",
|
||||
ReplaceWith("this.asFriendOrNull()?.let(block)")
|
||||
ReplaceWith("this.asFriendOrNull()?.let(block)"),
|
||||
level = DeprecationLevel.ERROR
|
||||
)
|
||||
@PlannedRemoval("2.0-M2")
|
||||
public inline fun <R> Member.takeIfIsFriend(block: (Friend) -> R): R? {
|
||||
@ -213,39 +147,5 @@ public inline fun <R> Member.takeIfIsFriend(block: (Friend) -> R): R? {
|
||||
* 若 [群名片][Member.nameCard] 不为空则返回群名片, 为空则返回 [User.nick]
|
||||
*/
|
||||
public val Member.nameCardOrNick: String get() = this.nameCard.takeIf { it.isNotEmpty() } ?: this.nick
|
||||
|
||||
/**
|
||||
* 获取非空群名片或昵称.
|
||||
*
|
||||
* @return 当 [User] 为 [Member] 时返回 [Member.nameCardOrNick]
|
||||
*
|
||||
* 否则返回 [Member.nick]
|
||||
*/
|
||||
public val User.nameCardOrNick: String
|
||||
get() = when (this) {
|
||||
is Member -> this.nameCardOrNick
|
||||
else -> this.nick
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断群成员是否处于禁言状态.
|
||||
*/
|
||||
public val Member.isMuted: Boolean
|
||||
get() = muteTimeRemaining != 0 && muteTimeRemaining != 0xFFFFFFFF.toInt()
|
||||
|
||||
/**
|
||||
* @see Member.mute
|
||||
*/
|
||||
@ExperimentalTime
|
||||
public suspend inline fun Member.mute(duration: Duration) {
|
||||
require(duration.inDays <= 30) { "duration must be at most 1 month" }
|
||||
require(duration.inSeconds > 0) { "duration must be greater than 0 second" }
|
||||
this.mute(duration.inSeconds.toInt())
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Member.mute
|
||||
*/
|
||||
@Deprecated("Convert duration to int manually.", ReplaceWith("this.mute(durationSeconds.toInt())"))
|
||||
@PlannedRemoval("2.0-M2")
|
||||
public suspend inline fun Member.mute(durationSeconds: Long): Unit = this.mute(durationSeconds.toInt())
|
||||
public val Member.isNormal: Boolean get() = this is NormalMember
|
||||
public val Member.isAnonymous: Boolean get() = this is AnonymousMember
|
||||
|
173
mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt
Normal file
173
mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2019-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.contact
|
||||
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.MessageReceipt.Companion.recall
|
||||
import net.mamoe.mirai.message.action.MemberNudge
|
||||
import net.mamoe.mirai.message.action.Nudge
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.isContentEmpty
|
||||
import net.mamoe.mirai.message.data.toPlainText
|
||||
import net.mamoe.mirai.utils.MemberDeprecatedApi
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
/**
|
||||
* 代表一位正常的群成员.
|
||||
*
|
||||
* 群成员可能也是好友, 但他们在对象类型上不同.
|
||||
* 群成员可以通过 [asFriend] 得到相关好友对象.
|
||||
*
|
||||
* ## 相关的操作
|
||||
* [Member.isFriend] 判断此成员是否为好友
|
||||
*/
|
||||
@OptIn(MemberDeprecatedApi::class)
|
||||
public interface NormalMember : Member {
|
||||
/**
|
||||
* 群名片. 可能为空.
|
||||
*
|
||||
* 管理员和群主都可修改任何人(包括群主)的群名片.
|
||||
*
|
||||
* 在修改时将会异步上传至服务器.
|
||||
*
|
||||
* @see [nameCardOrNick] 获取非空群名片或昵称
|
||||
*
|
||||
* @see MemberCardChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
|
||||
* @throws PermissionDeniedException 无权限修改时
|
||||
*/
|
||||
public override var nameCard: String
|
||||
|
||||
/**
|
||||
* 群头衔.
|
||||
*
|
||||
* 仅群主可以修改群头衔.
|
||||
*
|
||||
* 在修改时将会异步上传至服务器.
|
||||
*
|
||||
* @see MemberSpecialTitleChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
|
||||
* @throws PermissionDeniedException 无权限修改时
|
||||
*/
|
||||
public override var specialTitle: String
|
||||
|
||||
/**
|
||||
* 被禁言剩余时长. 单位为秒.
|
||||
*
|
||||
* @see isMuted 判断改成员是否处于禁言状态
|
||||
* @see mute 设置禁言
|
||||
* @see unmute 取消禁言
|
||||
*/
|
||||
public override val muteTimeRemaining: Int
|
||||
|
||||
/**
|
||||
* 解除禁言.
|
||||
*
|
||||
* 管理员可解除成员的禁言, 群主可解除管理员和群员的禁言.
|
||||
*
|
||||
* @see Member.isMuted 判断此成员是否正处于禁言状态中
|
||||
*
|
||||
* @see MemberUnmuteEvent 成员被取消禁言事件
|
||||
*
|
||||
* @throws PermissionDeniedException 无权限修改时抛出
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public override suspend fun unmute()
|
||||
|
||||
/**
|
||||
* 踢出该成员.
|
||||
*
|
||||
* 管理员可踢出成员, 群主可踢出管理员和群员.
|
||||
*
|
||||
* @see MemberLeaveEvent.Kick 成员被踢出事件.
|
||||
* @throws PermissionDeniedException 无权限修改时
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public override suspend fun kick(message: String)
|
||||
|
||||
/**
|
||||
* 向群成员发送消息.
|
||||
* 若群成员同时是好友, 则会发送好友消息. 否则发送临时会话消息.
|
||||
*
|
||||
* 单条消息最大可发送 4500 字符或 50 张图片.
|
||||
*
|
||||
* @see FriendMessagePreSendEvent 当此成员是好友时发送消息前事件
|
||||
* @see FriendMessagePostSendEvent 当此成员是好友时发送消息后事件
|
||||
*
|
||||
* @see TempMessagePreSendEvent 当此成员不是好友时发送消息前事件
|
||||
* @see TempMessagePostSendEvent 当此成员不是好友时发送消息后事件
|
||||
*
|
||||
* @throws EventCancelledException 当发送消息事件被取消时抛出
|
||||
* @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
|
||||
* @throws MessageTooLargeException 当消息过长时抛出
|
||||
* @throws IllegalArgumentException 当消息内容为空时抛出 (详见 [Message.isContentEmpty])
|
||||
*
|
||||
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public override suspend fun sendMessage(message: Message): MessageReceipt<Member>
|
||||
|
||||
/**
|
||||
* 发送纯文本消息
|
||||
* @see sendMessage
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public override suspend fun sendMessage(message: String): MessageReceipt<Member> =
|
||||
this.sendMessage(message.toPlainText())
|
||||
|
||||
/**
|
||||
* 创建一个 "戳一戳" 消息
|
||||
*
|
||||
* @see Nudge.sendTo 发送这个戳一戳消息
|
||||
*/
|
||||
@MiraiExperimentalApi
|
||||
public override fun nudge(): MemberNudge = MemberNudge(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取非空群名片或昵称.
|
||||
*
|
||||
* @return 当 [User] 为 [Member] 时返回 [Member.nameCardOrNick]
|
||||
*
|
||||
* 否则返回 [Member.nick]
|
||||
*/
|
||||
public val User.nameCardOrNick: String
|
||||
get() = when (this) {
|
||||
is NormalMember -> this.nameCardOrNick
|
||||
else -> this.nick
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断群成员是否处于禁言状态.
|
||||
*/
|
||||
public val NormalMember.isMuted: Boolean
|
||||
get() = muteTimeRemaining != 0 && muteTimeRemaining != 0xFFFFFFFF.toInt()
|
||||
|
||||
/**
|
||||
* @see Member.mute
|
||||
*/
|
||||
@ExperimentalTime
|
||||
public suspend inline fun NormalMember.mute(duration: Duration) {
|
||||
require(duration.inDays <= 30) { "duration must be at most 1 month" }
|
||||
require(duration.inSeconds > 0) { "duration must be greater than 0 second" }
|
||||
this.mute(duration.inSeconds.toInt())
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Member.mute
|
||||
*/
|
||||
@Deprecated("Convert duration to int manually.", ReplaceWith("this.mute(durationSeconds.toInt())"))
|
||||
@PlannedRemoval("2.0-M2")
|
||||
public suspend inline fun NormalMember.mute(durationSeconds: Long): Unit = this.mute(durationSeconds.toInt())
|
@ -14,6 +14,7 @@
|
||||
package net.mamoe.mirai.event
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.utils.EventListenerLikeJava
|
||||
import java.lang.reflect.Method
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
@ -238,6 +239,23 @@ public fun CoroutineScope.registerEvents(
|
||||
}
|
||||
}
|
||||
|
||||
private fun Method.isKotlinFunction(): Boolean {
|
||||
|
||||
if (getDeclaredAnnotation(EventListenerLikeJava::class.java) != null) return false
|
||||
if (declaringClass.getDeclaredAnnotation(EventListenerLikeJava::class.java) != null) return false
|
||||
|
||||
@Suppress("RemoveRedundantQualifierName") // for strict
|
||||
return declaringClass.getDeclaredAnnotation(kotlin.Metadata::class.java) != null
|
||||
}
|
||||
|
||||
private fun Method.invokeWithErrorReport(self: Any?, vararg args: Any?): Any? = try {
|
||||
invoke(self, *args)
|
||||
} catch (exception: IllegalArgumentException) {
|
||||
throw IllegalArgumentException(
|
||||
"Internal Error: $exception, method=${this}, this=$self, arguments=$args, please report to https://github.com/mamoe/mirai",
|
||||
exception
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun Method.registerEvent(
|
||||
@ -248,7 +266,7 @@ private fun Method.registerEvent(
|
||||
): Listener<Event> {
|
||||
this.isAccessible = true
|
||||
val kotlinFunction = kotlin.runCatching { this.kotlinFunction }.getOrNull()
|
||||
return if (kotlinFunction != null) {
|
||||
return if (kotlinFunction != null && isKotlinFunction()) {
|
||||
// kotlin functions
|
||||
|
||||
val param = kotlinFunction.parameters
|
||||
@ -337,7 +355,7 @@ private fun Method.registerEvent(
|
||||
|
||||
val paramType = this.parameters[0].type
|
||||
check(this.parameterCount == 1 && Event::class.java.isAssignableFrom(paramType)) {
|
||||
"Illegal method parameter. Required one exact Event subclass. found $paramType"
|
||||
"Illegal method parameter. Required one exact Event subclass. found ${this.parameters.contentToString()}"
|
||||
}
|
||||
when (this.returnType) {
|
||||
Void::class.java, Void.TYPE, Nothing::class.java -> {
|
||||
@ -350,11 +368,11 @@ private fun Method.registerEvent(
|
||||
if (annotation.ignoreCancelled) {
|
||||
if ((this as? CancellableEvent)?.isCancelled != true) {
|
||||
withContext(Dispatchers.IO) {
|
||||
this@registerEvent.invoke(owner, this)
|
||||
this@registerEvent.invokeWithErrorReport(owner, this@subscribeAlways)
|
||||
}
|
||||
}
|
||||
} else withContext(Dispatchers.IO) {
|
||||
this@registerEvent.invoke(owner, this)
|
||||
this@registerEvent.invokeWithErrorReport(owner, this@subscribeAlways)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -368,11 +386,11 @@ private fun Method.registerEvent(
|
||||
if (annotation.ignoreCancelled) {
|
||||
if ((this as? CancellableEvent)?.isCancelled != true) {
|
||||
withContext(Dispatchers.IO) {
|
||||
this@registerEvent.invoke(owner, this) as ListeningStatus
|
||||
this@registerEvent.invokeWithErrorReport(owner, this@subscribe) as ListeningStatus
|
||||
}
|
||||
} else ListeningStatus.LISTENING
|
||||
} else withContext(Dispatchers.IO) {
|
||||
this@registerEvent.invoke(owner, this) as ListeningStatus
|
||||
this@registerEvent.invokeWithErrorReport(owner, this@subscribe) as ListeningStatus
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,9 +25,10 @@
|
||||
- (`1.3.0+`) Bot 被戳: BotNudgedEvent
|
||||
|
||||
### [消息](message.kt)
|
||||
- (`1.1.0-`) 主动发送消息: MessageSendEvent
|
||||
- 群消息: GroupMessageSendEvent
|
||||
- 好友消息: FriendMessageSendEvent
|
||||
- 被动收到消息:MessageEvent
|
||||
- 群消息:GroupMessageEvent
|
||||
- 好友消息:FriendMessageEvent
|
||||
- 群临时会话消息:TempMessageEvent
|
||||
- (`1.1.0+`) 主动发送消息前: MessagePreSendEvent
|
||||
- 群消息: GroupMessagePreSendEvent
|
||||
- 好友消息: FriendMessagePreSendEvent
|
||||
@ -44,6 +45,9 @@
|
||||
- 图片上传完成: ImageUploadEvent
|
||||
- 图片上传成功: Succeed
|
||||
- 图片上传失败: Failed
|
||||
- (`1.1.0-`) ~~主动发送消息: MessageSendEvent~~
|
||||
- ~~群消息: GroupMessageSendEvent~~
|
||||
- ~~好友消息: FriendMessageSendEvent~~
|
||||
|
||||
### [群](group.kt)
|
||||
- 机器人被踢出群或在其他客户端主动退出一个群: BotLeaveEvent
|
||||
@ -60,7 +64,7 @@
|
||||
- 入群公告改变: GroupEntranceAnnouncementChangeEvent
|
||||
- 全员禁言状态改变: GroupMuteAllEvent
|
||||
- 匿名聊天状态改变: GroupAllowAnonymousChatEvent
|
||||
- 坦白说状态改变: GroupAllowConfessTalkEvent
|
||||
- (`1.3.0-`) ~~坦白说状态改变: GroupAllowConfessTalkEvent~~
|
||||
- 允许群员邀请好友加群状态改变: GroupAllowMemberInviteEvent
|
||||
|
||||
#### 群成员
|
||||
|
@ -16,10 +16,7 @@ package net.mamoe.mirai.event.events
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.contact.Friend
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.event.AbstractEvent
|
||||
import net.mamoe.mirai.event.BroadcastControllable
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
@ -245,14 +242,14 @@ public data class GroupAllowMemberInviteEvent internal constructor(
|
||||
* 成员已经加入群的事件
|
||||
*/
|
||||
public sealed class MemberJoinEvent(
|
||||
public override val member: Member
|
||||
public override val member: NormalMember
|
||||
) : GroupMemberEvent, BotPassiveEvent, Packet,
|
||||
AbstractEvent() {
|
||||
/**
|
||||
* 被邀请加入群
|
||||
*/
|
||||
public data class Invite internal constructor(
|
||||
public override val member: Member
|
||||
public override val member: NormalMember
|
||||
) : MemberJoinEvent(member) {
|
||||
public override fun toString(): String = "MemberJoinEvent.Invite(member=${member.id})"
|
||||
}
|
||||
@ -261,7 +258,7 @@ public sealed class MemberJoinEvent(
|
||||
* 成员主动加入群
|
||||
*/
|
||||
public data class Active internal constructor(
|
||||
public override val member: Member
|
||||
public override val member: NormalMember
|
||||
) : MemberJoinEvent(member) {
|
||||
public override fun toString(): String = "MemberJoinEvent.Active(member=${member.id})"
|
||||
}
|
||||
@ -271,7 +268,7 @@ public sealed class MemberJoinEvent(
|
||||
* 此时 [member] 的 [Member.permission] 肯定是 [MemberPermission.OWNER]
|
||||
*/
|
||||
public data class Retrieve internal constructor(
|
||||
public override val member: Member
|
||||
public override val member: NormalMember
|
||||
) : MemberJoinEvent(member) {
|
||||
override fun toString(): String = "MemberJoinEvent.Retrieve(member=${member.id})"
|
||||
}
|
||||
@ -285,11 +282,11 @@ public sealed class MemberLeaveEvent : GroupMemberEvent, AbstractEvent() {
|
||||
* 成员被踢出群. 成员不可能是机器人自己.
|
||||
*/
|
||||
public data class Kick(
|
||||
public override val member: Member,
|
||||
public override val member: NormalMember,
|
||||
/**
|
||||
* 操作人. 为 null 则是机器人操作.
|
||||
*/
|
||||
public override val operator: Member?
|
||||
public override val operator: NormalMember?
|
||||
) : MemberLeaveEvent(), Packet, GroupOperableEvent {
|
||||
public override fun toString(): String = "MemberLeaveEvent.Kick(member=${member.id}, operator=${operator?.id})"
|
||||
}
|
||||
@ -298,7 +295,7 @@ public sealed class MemberLeaveEvent : GroupMemberEvent, AbstractEvent() {
|
||||
* 成员主动离开
|
||||
*/
|
||||
public data class Quit(
|
||||
public override val member: Member
|
||||
public override val member: NormalMember
|
||||
) : MemberLeaveEvent(), Packet {
|
||||
public override fun toString(): String = "MemberLeaveEvent.Quit(member=${member.id})"
|
||||
}
|
||||
@ -439,7 +436,7 @@ public data class MemberSpecialTitleChangeEvent internal constructor(
|
||||
* 不为 null 时一定为群主. 可能与 [member] 引用相同, 此时为群员自己修改.
|
||||
* 为 null 时则是机器人操作.
|
||||
*/
|
||||
public override val operator: Member?
|
||||
public override val operator: NormalMember?
|
||||
) : GroupMemberEvent, GroupOperableEvent, AbstractEvent()
|
||||
|
||||
// endregion
|
||||
|
@ -440,7 +440,7 @@ public class FriendMessageEvent constructor(
|
||||
public override val sender: Friend,
|
||||
public override val message: MessageChain,
|
||||
public override val time: Int
|
||||
) : AbstractEvent(), MessageEvent, MessageEventExtensions<User, Contact>, BroadcastControllable, FriendEvent {
|
||||
) : AbstractMessageEvent(), MessageEvent, MessageEventExtensions<User, Contact>, BroadcastControllable, FriendEvent {
|
||||
init {
|
||||
val source =
|
||||
message[MessageSource] ?: throw IllegalArgumentException("Cannot find MessageSource from message")
|
||||
@ -480,7 +480,7 @@ public class GroupMessageEvent(
|
||||
public override val sender: Member,
|
||||
public override val message: MessageChain,
|
||||
public override val time: Int
|
||||
) : AbstractEvent(), GroupAwareMessageEvent, MessageEvent, Event, GroupEvent {
|
||||
) : AbstractMessageEvent(), GroupAwareMessageEvent, MessageEvent, Event, GroupEvent {
|
||||
init {
|
||||
val source = message[MessageSource] ?: error("Cannot find MessageSource from message")
|
||||
check(source is OnlineMessageSource.Incoming.FromGroup) { "source provided to a GroupMessage must be an instance of OnlineMessageSource.Incoming.FromGroup" }
|
||||
@ -512,7 +512,7 @@ public class TempMessageEvent(
|
||||
public override val sender: Member,
|
||||
public override val message: MessageChain,
|
||||
public override val time: Int
|
||||
) : AbstractEvent(), GroupAwareMessageEvent, MessageEvent, BroadcastControllable {
|
||||
) : AbstractMessageEvent(), GroupAwareMessageEvent, MessageEvent, BroadcastControllable {
|
||||
init {
|
||||
val source = message[MessageSource] ?: error("Cannot find MessageSource from message")
|
||||
check(source is OnlineMessageSource.Incoming.FromTemp) { "source provided to a TempMessage must be an instance of OnlineMessageSource.Incoming.FromTemp" }
|
||||
@ -538,6 +538,75 @@ public interface UserMessageEvent : MessageEvent {
|
||||
public override val subject: User
|
||||
}
|
||||
|
||||
@MiraiInternalApi
|
||||
public abstract class AbstractMessageEvent : MessageEvent, AbstractEvent() {
|
||||
public override suspend fun reply(message: Message): MessageReceipt<Contact> =
|
||||
subject.sendMessage(message.asMessageChain())
|
||||
|
||||
public override suspend fun reply(plain: String): MessageReceipt<Contact> =
|
||||
subject.sendMessage(PlainText(plain).asMessageChain())
|
||||
|
||||
public override suspend fun ExternalImage.upload(): Image = this.upload(subject)
|
||||
|
||||
public override suspend fun ExternalImage.send(): MessageReceipt<Contact> = this.sendTo(subject)
|
||||
|
||||
public override suspend fun Image.send(): MessageReceipt<Contact> = this.sendTo(subject)
|
||||
|
||||
public override suspend fun Message.send(): MessageReceipt<Contact> = this.sendTo(subject)
|
||||
|
||||
public override suspend fun String.send(): MessageReceipt<Contact> = PlainText(this).sendTo(subject)
|
||||
|
||||
// region 引用回复
|
||||
/**
|
||||
* 给这个消息事件的主体发送引用回复消息
|
||||
* 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
|
||||
* 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
|
||||
*/
|
||||
public override suspend fun quoteReply(message: MessageChain): MessageReceipt<Contact> =
|
||||
reply(this.message.quote() + message)
|
||||
|
||||
public override suspend fun quoteReply(message: Message): MessageReceipt<Contact> =
|
||||
reply(this.message.quote() + message)
|
||||
|
||||
public override suspend fun quoteReply(plain: String): MessageReceipt<Contact> = reply(this.message.quote() + plain)
|
||||
|
||||
public override fun At.isBot(): Boolean = target == bot.id
|
||||
|
||||
|
||||
/**
|
||||
* 获取图片下载链接
|
||||
* @return "http://gchat.qpic.cn/gchatpic_new/..."
|
||||
*/
|
||||
public override suspend fun Image.url(): String = this@url.queryUrl()
|
||||
|
||||
|
||||
// region 上传图片
|
||||
|
||||
public override suspend fun uploadImage(image: BufferedImage): Image = subject.uploadImage(image)
|
||||
public override suspend fun uploadImage(image: InputStream): Image = subject.uploadImage(image)
|
||||
public override suspend fun uploadImage(image: File): Image = subject.uploadImage(image)
|
||||
// endregion
|
||||
|
||||
// region 发送图片
|
||||
public override suspend fun sendImage(image: BufferedImage): MessageReceipt<Contact> = subject.sendImage(image)
|
||||
public override suspend fun sendImage(image: InputStream): MessageReceipt<Contact> = subject.sendImage(image)
|
||||
public override suspend fun sendImage(image: File): MessageReceipt<Contact> = subject.sendImage(image)
|
||||
// endregion
|
||||
|
||||
// region 上传图片 (扩展)
|
||||
public override suspend fun BufferedImage.upload(): Image = upload(subject)
|
||||
public override suspend fun InputStream.uploadAsImage(): Image = uploadAsImage(subject)
|
||||
public override suspend fun File.uploadAsImage(): Image = uploadAsImage(subject)
|
||||
// endregion 上传图片 (扩展)
|
||||
|
||||
// region 发送图片 (扩展)
|
||||
public override suspend fun BufferedImage.send(): MessageReceipt<Contact> = sendTo(subject)
|
||||
public override suspend fun InputStream.sendAsImage(): MessageReceipt<Contact> = sendAsImageTo(subject)
|
||||
public override suspend fun File.sendAsImage(): MessageReceipt<Contact> = sendAsImageTo(subject)
|
||||
// endregion 发送图片 (扩展)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 一个 (收到的) 消息事件.
|
||||
*
|
||||
@ -611,29 +680,27 @@ public interface MessageEventExtensions<out TSender : User, out TSubject : Conta
|
||||
* 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public suspend fun reply(message: Message): MessageReceipt<TSubject> =
|
||||
subject.sendMessage(message.asMessageChain()) as MessageReceipt<TSubject>
|
||||
public suspend fun reply(message: Message): MessageReceipt<TSubject>
|
||||
|
||||
@JvmBlockingBridge
|
||||
public suspend fun reply(plain: String): MessageReceipt<TSubject> =
|
||||
subject.sendMessage(PlainText(plain).asMessageChain()) as MessageReceipt<TSubject>
|
||||
public suspend fun reply(plain: String): MessageReceipt<TSubject>
|
||||
|
||||
// endregion
|
||||
|
||||
@JvmSynthetic
|
||||
public suspend fun ExternalImage.upload(): Image = this.upload(subject)
|
||||
public suspend fun ExternalImage.upload(): Image
|
||||
|
||||
@JvmSynthetic
|
||||
public suspend fun ExternalImage.send(): MessageReceipt<TSubject> = this.sendTo(subject)
|
||||
public suspend fun ExternalImage.send(): MessageReceipt<TSubject>
|
||||
|
||||
@JvmSynthetic
|
||||
public suspend fun Image.send(): MessageReceipt<TSubject> = this.sendTo(subject)
|
||||
public suspend fun Image.send(): MessageReceipt<TSubject>
|
||||
|
||||
@JvmSynthetic
|
||||
public suspend fun Message.send(): MessageReceipt<TSubject> = this.sendTo(subject)
|
||||
public suspend fun Message.send(): MessageReceipt<TSubject>
|
||||
|
||||
@JvmSynthetic
|
||||
public suspend fun String.send(): MessageReceipt<TSubject> = PlainText(this).sendTo(subject)
|
||||
public suspend fun String.send(): MessageReceipt<TSubject>
|
||||
|
||||
// region 引用回复
|
||||
/**
|
||||
@ -642,18 +709,16 @@ public interface MessageEventExtensions<out TSender : User, out TSubject : Conta
|
||||
* 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public suspend fun quoteReply(message: MessageChain): MessageReceipt<TSubject> =
|
||||
reply(this.message.quote() + message)
|
||||
public suspend fun quoteReply(message: MessageChain): MessageReceipt<TSubject>
|
||||
|
||||
@JvmBlockingBridge
|
||||
public suspend fun quoteReply(message: Message): MessageReceipt<TSubject> =
|
||||
reply(this.message.quote() + message)
|
||||
public suspend fun quoteReply(message: Message): MessageReceipt<TSubject>
|
||||
|
||||
@JvmBlockingBridge
|
||||
public suspend fun quoteReply(plain: String): MessageReceipt<TSubject> = reply(this.message.quote() + plain)
|
||||
public suspend fun quoteReply(plain: String): MessageReceipt<TSubject>
|
||||
|
||||
@JvmSynthetic
|
||||
public fun At.isBot(): Boolean = target == bot.id
|
||||
public fun At.isBot(): Boolean
|
||||
|
||||
|
||||
/**
|
||||
@ -661,7 +726,7 @@ public interface MessageEventExtensions<out TSender : User, out TSubject : Conta
|
||||
* @return "http://gchat.qpic.cn/gchatpic_new/..."
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public suspend fun Image.url(): String = this@url.queryUrl()
|
||||
public suspend fun Image.url(): String
|
||||
}
|
||||
|
||||
/** 一个消息事件在各平台的相关扩展. 请使用 [MessageEventExtensions] */
|
||||
@ -679,46 +744,46 @@ public interface MessageEventPlatformExtensions<out TSender : User, out TSubject
|
||||
// region 上传图片
|
||||
|
||||
@JvmBlockingBridge
|
||||
public suspend fun uploadImage(image: BufferedImage): Image = subject.uploadImage(image)
|
||||
public suspend fun uploadImage(image: BufferedImage): Image
|
||||
|
||||
@JvmBlockingBridge
|
||||
public suspend fun uploadImage(image: InputStream): Image = subject.uploadImage(image)
|
||||
public suspend fun uploadImage(image: InputStream): Image
|
||||
|
||||
@JvmBlockingBridge
|
||||
public suspend fun uploadImage(image: File): Image = subject.uploadImage(image)
|
||||
public suspend fun uploadImage(image: File): Image
|
||||
// endregion
|
||||
|
||||
// region 发送图片
|
||||
@JvmBlockingBridge
|
||||
public suspend fun sendImage(image: BufferedImage): MessageReceipt<TSubject> = subject.sendImage(image)
|
||||
public suspend fun sendImage(image: BufferedImage): MessageReceipt<TSubject>
|
||||
|
||||
@JvmBlockingBridge
|
||||
public suspend fun sendImage(image: InputStream): MessageReceipt<TSubject> = subject.sendImage(image)
|
||||
public suspend fun sendImage(image: InputStream): MessageReceipt<TSubject>
|
||||
|
||||
@JvmBlockingBridge
|
||||
public suspend fun sendImage(image: File): MessageReceipt<TSubject> = subject.sendImage(image)
|
||||
public suspend fun sendImage(image: File): MessageReceipt<TSubject>
|
||||
// endregion
|
||||
|
||||
// region 上传图片 (扩展)
|
||||
@JvmSynthetic
|
||||
public suspend fun BufferedImage.upload(): Image = upload(subject)
|
||||
public suspend fun BufferedImage.upload(): Image
|
||||
|
||||
@JvmSynthetic
|
||||
public suspend fun InputStream.uploadAsImage(): Image = uploadAsImage(subject)
|
||||
public suspend fun InputStream.uploadAsImage(): Image
|
||||
|
||||
@JvmSynthetic
|
||||
public suspend fun File.uploadAsImage(): Image = uploadAsImage(subject)
|
||||
public suspend fun File.uploadAsImage(): Image
|
||||
// endregion 上传图片 (扩展)
|
||||
|
||||
// region 发送图片 (扩展)
|
||||
@JvmSynthetic
|
||||
public suspend fun BufferedImage.send(): MessageReceipt<TSubject> = sendTo(subject)
|
||||
public suspend fun BufferedImage.send(): MessageReceipt<TSubject>
|
||||
|
||||
@JvmSynthetic
|
||||
public suspend fun InputStream.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
||||
public suspend fun InputStream.sendAsImage(): MessageReceipt<TSubject>
|
||||
|
||||
@JvmSynthetic
|
||||
public suspend fun File.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
||||
public suspend fun File.sendAsImage(): MessageReceipt<TSubject>
|
||||
// endregion 发送图片 (扩展)
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import net.mamoe.mirai.message.data.*
|
||||
* 使用 `mirai-serialization` 中 `String.parseMiraiCode()` 转回 [Message].
|
||||
*
|
||||
* ## 规范
|
||||
* 可在 [mirai-code-specification.md](https://github.com/mamoe/mirai/blob/dev/docs/mirai-code-specification.md) 查看 mirai 码规范.
|
||||
* 可在 [MiraiCodeSepecification.md](https://github.com/mamoe/mirai/blob/dev/docs/MiraiCodeSepecification.md) 查看 mirai 码规范.
|
||||
*
|
||||
* @suppress 警告: 此 API 可能在任何时刻被改变
|
||||
*
|
||||
|
@ -16,8 +16,10 @@ package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.LowLevelApi
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.contact.UserOrBot
|
||||
import net.mamoe.mirai.contact.nameCardOrNick
|
||||
import net.mamoe.mirai.message.code.CodableMessage
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
|
||||
@ -37,6 +39,22 @@ public data class At(
|
||||
public override fun toString(): String = "[mirai:at:$target]"
|
||||
public override fun contentToString(): String = "@$target"
|
||||
|
||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||
@Deprecated("Use getDisplay", ReplaceWith("this.getDisplay()"), DeprecationLevel.ERROR)
|
||||
@PlannedRemoval("2.0-M2")
|
||||
val display: Nothing
|
||||
get() = error("At.display is no longer supported")
|
||||
|
||||
/**
|
||||
* 获取 [At] 发送于指定 [Group] 时会显示的内容.
|
||||
*
|
||||
* 若 [group] 非 `null` 且包含成员 [target], 返回 `"@成员名片或昵称"`. 否则返回 `"@123456"` 其中 123456 表示 [target]
|
||||
*/
|
||||
public fun getDisplay(group: Group?): String {
|
||||
val member = group?.get(this.target) ?: return "@$target"
|
||||
return "@${member.nameCardOrNick}"
|
||||
}
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 构造一个 [At], 仅供内部使用, 否则可能造成消息无法发出的问题.
|
||||
|
@ -17,6 +17,7 @@ import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.message.code.CodableMessage
|
||||
import net.mamoe.mirai.message.data.VipFace.Kind
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
import net.mamoe.mirai.utils.castOrNull
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
@ -56,7 +57,14 @@ public data class PokeMessage internal constructor(
|
||||
public val id: Int
|
||||
) : HummerMessage(), CodableMessage {
|
||||
@ExperimentalMessageKey
|
||||
override val key: MessageKey<HummerMessage> get() = Key
|
||||
override val key: MessageKey<HummerMessage>
|
||||
get() = Key
|
||||
|
||||
|
||||
@PlannedRemoval("2.0-M2")
|
||||
@Deprecated("Use pokeType", ReplaceWith("pokeType"), DeprecationLevel.ERROR)
|
||||
val type: Int
|
||||
get() = pokeType
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<HummerMessage, PokeMessage>(HummerMessage, { it.castOrNull() }) {
|
||||
@ -206,7 +214,8 @@ public data class VipFace internal constructor(
|
||||
}
|
||||
|
||||
@ExperimentalMessageKey
|
||||
override val key: MessageKey<VipFace> get() = Key
|
||||
override val key: MessageKey<VipFace>
|
||||
get() = Key
|
||||
|
||||
@Suppress("DEPRECATION_ERROR", "DEPRECATION", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
public companion object Key :
|
||||
|
@ -28,6 +28,7 @@ import net.mamoe.mirai.message.data.MessageSource.Key.isAboutGroup
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.isAboutTemp
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.quote
|
||||
import net.mamoe.mirai.utils.LazyProperty
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
/**
|
||||
@ -145,7 +146,8 @@ public sealed class MessageSource : Message, MessageMetadata, ConstrainSingle {
|
||||
/**
|
||||
* 返回 `"[mirai:source:${ids.contentToString()},${internalIds.contentToString()}]"`
|
||||
*/
|
||||
public final override fun toString(): String = "[mirai:source:${ids.contentToString()},${internalIds.contentToString()}]"
|
||||
public final override fun toString(): String =
|
||||
"[mirai:source:${ids.contentToString()},${internalIds.contentToString()}]"
|
||||
|
||||
public companion object Key : AbstractMessageKey<MessageSource>({ it.safeCast() }) {
|
||||
/**
|
||||
@ -397,6 +399,13 @@ public abstract class OfflineMessageSource : MessageSource() {
|
||||
* 消息种类
|
||||
*/
|
||||
public abstract val kind: MessageSourceKind
|
||||
|
||||
@PlannedRemoval("2.0-M2")
|
||||
@Deprecated(
|
||||
"Use MessageSourceKind",
|
||||
ReplaceWith("MessageSourceKind", "net.mamoe.mirai.message.data.MessageSourceKind")
|
||||
)
|
||||
private enum class Kind
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
@ -15,6 +15,7 @@ package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.PlannedRemoval
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
import kotlin.annotation.AnnotationTarget.*
|
||||
|
||||
@ -150,6 +151,15 @@ public interface ServiceMessage : RichMessage {
|
||||
public val serviceId: Int
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
@Deprecated(
|
||||
"Use SimpleServiceMessage.",
|
||||
ReplaceWith("SimpleServiceMessage(serviceId, content)", "net.mamoe.mirai.message.data.SimpleServiceMessage")
|
||||
)
|
||||
@PlannedRemoval("2.0-M2")
|
||||
public fun ServiceMessage(serviceId: Int, content: String): SimpleServiceMessage =
|
||||
SimpleServiceMessage(serviceId, content)
|
||||
|
||||
@MiraiExperimentalApi
|
||||
@Serializable
|
||||
public abstract class AbstractServiceMessage : ServiceMessage {
|
||||
|
@ -17,7 +17,7 @@ import kotlin.annotation.AnnotationTarget.*
|
||||
* 这些 API 可能会在任意时刻更改, 且不会发布任何预警.
|
||||
* 非常不建议在发行版本中使用这些 API.
|
||||
*/
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.ERROR)
|
||||
@Target(
|
||||
CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR,
|
||||
@ -34,7 +34,7 @@ public annotation class MiraiInternalApi(
|
||||
* 这些 API 不具有稳定性, 且可能会在任意时刻更改.
|
||||
* 不建议在发行版本中使用这些 API.
|
||||
*/
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||
@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
|
||||
@MustBeDocumented
|
||||
@ -48,4 +48,24 @@ public annotation class MiraiExperimentalApi(
|
||||
@Target(CLASS, PROPERTY, FIELD, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, TYPEALIAS)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
@MustBeDocumented
|
||||
internal annotation class PlannedRemoval(val version: String)
|
||||
internal annotation class PlannedRemoval(val version: String)
|
||||
|
||||
/**
|
||||
* 标记已过时的 Member API
|
||||
*/
|
||||
@Target(CLASS, PROPERTY, FIELD, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, TYPEALIAS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.ERROR)
|
||||
@MustBeDocumented
|
||||
@PlannedRemoval("2.0-M2")
|
||||
internal annotation class MemberDeprecatedApi(val message: String)
|
||||
|
||||
/**
|
||||
* 该注解仅用于测试 EventHandler
|
||||
*
|
||||
* 标注了此注解的意为像处理 java 方法那样处理 kotlin 方法
|
||||
*/
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class EventListenerLikeJava
|
||||
|
||||
|
||||
|
@ -208,24 +208,22 @@ public open class BotConfiguration { // open for Java
|
||||
}
|
||||
|
||||
@Suppress("ACTUAL_WITHOUT_EXPECT")
|
||||
public enum class MiraiProtocol constructor(
|
||||
/** 协议模块使用的 ID */
|
||||
@JvmField internal val id: Long
|
||||
) {
|
||||
public enum class MiraiProtocol {
|
||||
/**
|
||||
* Android 手机.
|
||||
*/
|
||||
ANDROID_PHONE(537066439),
|
||||
ANDROID_PHONE,
|
||||
|
||||
/**
|
||||
* Android 平板.
|
||||
*/
|
||||
ANDROID_PAD(537062409),
|
||||
ANDROID_PAD,
|
||||
|
||||
/**
|
||||
* Android 手表.
|
||||
* */
|
||||
ANDROID_WATCH(537061176)
|
||||
ANDROID_WATCH,
|
||||
|
||||
}
|
||||
|
||||
public companion object {
|
||||
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-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.event;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static kotlin.test.AssertionsKt.assertEquals;
|
||||
|
||||
public class JvmMethodEventsTestJava extends SimpleListenerHost {
|
||||
private final AtomicInteger called = new AtomicInteger(0);
|
||||
|
||||
@EventHandler
|
||||
public void ev(TestEvent event) {
|
||||
called.incrementAndGet();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public Void ev2(TestEvent event) {
|
||||
called.incrementAndGet();
|
||||
return null;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public ListeningStatus ev3(TestEvent event) {
|
||||
called.incrementAndGet();
|
||||
return ListeningStatus.LISTENING;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void ev(TestEvent event, TestEvent event2) {
|
||||
called.incrementAndGet();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public Void ev2(TestEvent event, TestEvent event2) {
|
||||
called.incrementAndGet();
|
||||
return null;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public ListeningStatus ev3(TestEvent event, TestEvent event2) {
|
||||
called.incrementAndGet();
|
||||
return ListeningStatus.LISTENING;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
Events.registerEvents(this);
|
||||
EventKt.broadcast(new TestEvent());
|
||||
assertEquals(6, called.get(), null);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2019-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.event
|
||||
|
||||
import net.mamoe.mirai.JavaFriendlyAPI
|
||||
import net.mamoe.mirai.utils.EventListenerLikeJava
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@EventListenerLikeJava
|
||||
@JavaFriendlyAPI
|
||||
internal class JvmMethodEventsTestJava : SimpleListenerHost() {
|
||||
private val called = AtomicInteger(0)
|
||||
|
||||
@EventHandler
|
||||
fun ev(event: TestEvent?) {
|
||||
called.incrementAndGet()
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun ev2(event: TestEvent?): Void? {
|
||||
called.incrementAndGet()
|
||||
return null
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun ev3(event: TestEvent?): ListeningStatus? {
|
||||
called.incrementAndGet()
|
||||
return ListeningStatus.LISTENING
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test() {
|
||||
this.registerEvents()
|
||||
TestEvent().__broadcastJava()
|
||||
assertEquals(3, called.get(), null)
|
||||
}
|
||||
}
|
@ -46,6 +46,7 @@ import net.mamoe.mirai.message.data.Image.Key.FRIEND_IMAGE_ID_REGEX_2
|
||||
import net.mamoe.mirai.message.data.Image.Key.GROUP_IMAGE_ID_REGEX
|
||||
import net.mamoe.mirai.utils.BotConfiguration
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.cast
|
||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.math.absoluteValue
|
||||
@ -733,7 +734,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
||||
override val nick: String get() = fromNick
|
||||
override val remark: String
|
||||
get() = ""
|
||||
}))
|
||||
}).cast())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,23 +77,23 @@ internal class GroupImpl(
|
||||
|
||||
val uin: Long = groupInfo.uin
|
||||
|
||||
override lateinit var owner: Member
|
||||
override lateinit var owner: NormalMember
|
||||
|
||||
override lateinit var botAsMember: Member
|
||||
override lateinit var botAsMember: NormalMember
|
||||
|
||||
override val botPermission: MemberPermission get() = botAsMember.permission
|
||||
|
||||
// e.g. 600
|
||||
override val botMuteRemaining: Int get() = botAsMember.muteTimeRemaining
|
||||
|
||||
override val members: ContactList<Member> = ContactList(members.mapNotNull {
|
||||
override val members: ContactList<NormalMember> = ContactList(members.mapNotNull {
|
||||
if (it.uin == bot.id) {
|
||||
botAsMember = newMember(it)
|
||||
botAsMember = newMember(it).cast()
|
||||
if (it.permission == MemberPermission.OWNER) {
|
||||
owner = botAsMember
|
||||
}
|
||||
null
|
||||
} else newMember(it).also { member ->
|
||||
} else newMember(it).cast<NormalMember>().also { member ->
|
||||
if (member.permission == MemberPermission.OWNER) {
|
||||
owner = member
|
||||
}
|
||||
@ -250,7 +250,7 @@ internal class GroupImpl(
|
||||
}
|
||||
)
|
||||
|
||||
override operator fun get(id: Long): Member? {
|
||||
override operator fun get(id: Long): NormalMember? {
|
||||
if (id == bot.id) {
|
||||
return botAsMember
|
||||
}
|
||||
@ -298,7 +298,7 @@ internal class GroupImpl(
|
||||
throw EventCancelledException("exception thrown when broadcasting GroupMessagePreSendEvent", it)
|
||||
}.message.asMessageChain()
|
||||
|
||||
val length = chain.estimateLength(703) // 阈值为700左右,限制到3的倍数
|
||||
val length = chain.estimateLength(this, 703) // 阈值为700左右,限制到3的倍数
|
||||
var imageCnt = 0 // 通过下方逻辑短路延迟计算
|
||||
|
||||
if (length > 5000 || chain.count { it is Image }.apply { imageCnt = this } > 50) {
|
||||
|
@ -44,7 +44,7 @@ internal class MemberImpl constructor(
|
||||
group: GroupImpl,
|
||||
coroutineContext: CoroutineContext,
|
||||
memberInfo: MemberInfo
|
||||
) : Member {
|
||||
) : NormalMember {
|
||||
override val group: GroupImpl by group.unsafeWeakRef()
|
||||
override val coroutineContext: CoroutineContext = coroutineContext + SupervisorJob(coroutineContext[Job])
|
||||
|
||||
|
@ -45,6 +45,7 @@ import net.mamoe.mirai.network.RetryLaterException
|
||||
import net.mamoe.mirai.network.UnsupportedSMSLoginException
|
||||
import net.mamoe.mirai.network.WrongPasswordException
|
||||
import net.mamoe.mirai.utils.*
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@ -220,8 +221,8 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
||||
|
||||
@JvmField
|
||||
@Volatile
|
||||
internal var pendingIncomingPackets: LockFreeLinkedList<KnownPacketFactories.IncomingPacket<*>>? =
|
||||
LockFreeLinkedList()
|
||||
internal var pendingIncomingPackets: ConcurrentLinkedQueue<KnownPacketFactories.IncomingPacket<*>>? =
|
||||
ConcurrentLinkedQueue()
|
||||
|
||||
private var initFriendOk = false
|
||||
private var initGroupOk = false
|
||||
@ -322,7 +323,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
||||
}
|
||||
|
||||
if (!pendingEnabled) {
|
||||
pendingIncomingPackets = LockFreeLinkedList()
|
||||
pendingIncomingPackets = ConcurrentLinkedQueue()
|
||||
_pendingEnabled.value = true
|
||||
}
|
||||
|
||||
|
@ -76,9 +76,10 @@ internal open class QQAndroidClient(
|
||||
val device: DeviceInfo,
|
||||
bot: QQAndroidBot
|
||||
) {
|
||||
@Suppress("INVISIBLE_MEMBER")
|
||||
val protocol = MiraiProtocolInternal[bot.configuration.protocol]
|
||||
|
||||
val subAppId: Long
|
||||
get() = bot.configuration.protocol.id
|
||||
get() = protocol.id
|
||||
|
||||
internal val serverList: MutableList<Pair<String, Int>> = DefaultServerList.toMutableList()
|
||||
|
||||
@ -118,9 +119,10 @@ internal open class QQAndroidClient(
|
||||
var tgtgtKey: ByteArray = generateTgtgtKey(device.guid)
|
||||
val randomKey: ByteArray = getRandomByteArray(16)
|
||||
|
||||
var miscBitMap: Int = 184024956 // 也可能是 150470524 ?
|
||||
private var mainSigMap: Int = 16724722
|
||||
var subSigMap: Int = 0x10400 //=66,560
|
||||
|
||||
val miscBitMap: Int get() = protocol.miscBitMap // 184024956 // 也可能是 150470524 ?
|
||||
private val mainSigMap: Int = protocol.mainSigMap
|
||||
var subSigMap: Int = protocol.subSigMap // 0x10400 //=66,560
|
||||
|
||||
private val _ssoSequenceId: AtomicInt = atomic(85600)
|
||||
|
||||
@ -157,9 +159,12 @@ internal open class QQAndroidClient(
|
||||
|
||||
var openAppId: Long = 715019303L
|
||||
|
||||
val apkVersionName: ByteArray get() = "8.4.18".toByteArray()
|
||||
val apkVersionName: ByteArray get() = protocol.ver.toByteArray() //"8.4.18".toByteArray()
|
||||
val buildVer: String get() = "8.4.18.4810" // 8.2.0.1296 // 8.4.8.4810 // 8.2.7.4410
|
||||
|
||||
val buildTime: Long get() = protocol.buildTime
|
||||
val sdkVersion: String get() = protocol.sdkVer
|
||||
|
||||
private val messageSequenceId: AtomicInt = atomic(22911)
|
||||
internal fun atomicNextMessageSequenceId(): Int = messageSequenceId.getAndAdd(2)
|
||||
|
||||
@ -194,7 +199,7 @@ internal open class QQAndroidClient(
|
||||
|
||||
var networkType: NetworkType = NetworkType.WIFI
|
||||
|
||||
val apkSignatureMd5: ByteArray = "A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D".hexToBytes()
|
||||
val apkSignatureMd5: ByteArray get() = protocol.sign.hexToBytes() // "A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D".hexToBytes()
|
||||
|
||||
/**
|
||||
* 协议版本?, 8.2.7 的为 8001
|
||||
|
@ -184,60 +184,59 @@ internal object KnownPacketFactories {
|
||||
bot: QQAndroidBot,
|
||||
rawInput: ByteReadPacket,
|
||||
consumer: PacketConsumer<T>
|
||||
) =
|
||||
with(rawInput) {
|
||||
// login
|
||||
val flag1 = readInt()
|
||||
): Unit = with(rawInput) {
|
||||
// login
|
||||
val flag1 = readInt()
|
||||
|
||||
PacketLogger.verbose { "开始处理一个包" }
|
||||
PacketLogger.verbose { "开始处理一个包" }
|
||||
|
||||
val flag2 = readByte().toInt()
|
||||
val flag3 = readByte().toInt()
|
||||
check(flag3 == 0) {
|
||||
"Illegal flag3. Expected 0, whereas got $flag3. flag1=$flag1, flag2=$flag2. " +
|
||||
"Remaining=${this.readBytes().toUHexString()}"
|
||||
}
|
||||
val flag2 = readByte().toInt()
|
||||
val flag3 = readByte().toInt()
|
||||
check(flag3 == 0) {
|
||||
"Illegal flag3. Expected 0, whereas got $flag3. flag1=$flag1, flag2=$flag2. " +
|
||||
"Remaining=${this.readBytes().toUHexString()}"
|
||||
}
|
||||
|
||||
readString(readInt() - 4)// uinAccount
|
||||
readString(readInt() - 4)// uinAccount
|
||||
|
||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
ByteArrayPool.useInstance(this.remaining.toInt()) { data ->
|
||||
val size = this.readAvailable(data)
|
||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
ByteArrayPool.useInstance(this.remaining.toInt()) { data ->
|
||||
val size = this.readAvailable(data)
|
||||
|
||||
kotlin.runCatching {
|
||||
when (flag2) {
|
||||
2 -> TEA.decrypt(data, DECRYPTER_16_ZERO, size)
|
||||
1 -> TEA.decrypt(data, bot.client.wLoginSigInfo.d2Key, size)
|
||||
0 -> data
|
||||
else -> error("")
|
||||
}
|
||||
}.getOrElse {
|
||||
bot.client.tryDecryptOrNull(data, size) { it }
|
||||
}?.toReadPacket()?.let { decryptedData ->
|
||||
when (flag1) {
|
||||
0x0A -> parseSsoFrame(bot, decryptedData)
|
||||
0x0B -> parseSsoFrame(bot, decryptedData) // 这里可能是 uni?? 但测试时候发现结构跟 sso 一样.
|
||||
else -> error("unknown flag1: ${flag1.toByte().toUHexString()}")
|
||||
}
|
||||
}?.let {
|
||||
it as IncomingPacket<T>
|
||||
|
||||
if (it.packetFactory is IncomingPacketFactory<T> && it.packetFactory.canBeCached && bot.network.pendingEnabled) {
|
||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
bot.network.pendingIncomingPackets?.addLast(it.also {
|
||||
it.consumer = consumer
|
||||
it.flag2 = flag2
|
||||
PacketLogger.info { "Cached ${it.commandName} #${it.sequenceId}" }
|
||||
}) ?: handleIncomingPacket(it, bot, flag2, consumer)
|
||||
} else {
|
||||
handleIncomingPacket(it, bot, flag2, consumer)
|
||||
}
|
||||
} ?: kotlin.run {
|
||||
PacketLogger.error { "任何key都无法解密: ${data.take(size).toUHexString()}" }
|
||||
return
|
||||
kotlin.runCatching {
|
||||
when (flag2) {
|
||||
2 -> TEA.decrypt(data, DECRYPTER_16_ZERO, size)
|
||||
1 -> TEA.decrypt(data, bot.client.wLoginSigInfo.d2Key, size)
|
||||
0 -> data
|
||||
else -> error("")
|
||||
}
|
||||
}.getOrElse {
|
||||
bot.client.tryDecryptOrNull(data, size) { it }
|
||||
}?.toReadPacket()?.let { decryptedData ->
|
||||
when (flag1) {
|
||||
0x0A -> parseSsoFrame(bot, decryptedData)
|
||||
0x0B -> parseSsoFrame(bot, decryptedData) // 这里可能是 uni?? 但测试时候发现结构跟 sso 一样.
|
||||
else -> error("unknown flag1: ${flag1.toByte().toUHexString()}")
|
||||
}
|
||||
}?.let {
|
||||
it as IncomingPacket<T>
|
||||
|
||||
if (it.packetFactory is IncomingPacketFactory<T> && it.packetFactory.canBeCached && bot.network.pendingEnabled) {
|
||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
bot.network.pendingIncomingPackets?.add(it.also {
|
||||
it.consumer = consumer
|
||||
it.flag2 = flag2
|
||||
PacketLogger.info { "Cached ${it.commandName} #${it.sequenceId}" }
|
||||
}) ?: handleIncomingPacket(it, bot, flag2, consumer)
|
||||
} else {
|
||||
handleIncomingPacket(it, bot, flag2, consumer)
|
||||
}
|
||||
} ?: kotlin.run {
|
||||
PacketLogger.error { "任何key都无法解密: ${data.take(size).toUHexString()}" }
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal suspend fun <T : Packet?> handleIncomingPacket(
|
||||
it: IncomingPacket<T>,
|
||||
|
@ -120,7 +120,7 @@ internal fun BytePacketBuilder.t106(
|
||||
writeByte(isSavePassword.toByte())
|
||||
writeFully(passwordMd5)
|
||||
writeFully(tgtgtKey)
|
||||
writeInt(0)
|
||||
writeInt(0) // wtf
|
||||
writeByte(isGuidAvailable.toByte())
|
||||
if (isGuidAvailable) {
|
||||
require(guid != null) { "Guid must not be null when isGuidAvailable==true" }
|
||||
@ -193,7 +193,7 @@ internal fun BytePacketBuilder.t107(
|
||||
internal fun BytePacketBuilder.t108(
|
||||
ksid: ByteArray
|
||||
) {
|
||||
require(ksid.size == 16) { "ksid should length 16" }
|
||||
// require(ksid.size == 16) { "ksid should length 16" }
|
||||
writeShort(0x108)
|
||||
writeShortLVPacket {
|
||||
writeFully(ksid)
|
||||
|
@ -22,6 +22,7 @@ import kotlinx.io.core.discardExact
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.contact.NormalMember
|
||||
import net.mamoe.mirai.data.MemberInfo
|
||||
import net.mamoe.mirai.event.AbstractEvent
|
||||
import net.mamoe.mirai.event.Event
|
||||
@ -51,6 +52,7 @@ import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf
|
||||
import net.mamoe.mirai.internal.utils.read
|
||||
import net.mamoe.mirai.internal.utils.toInt
|
||||
import net.mamoe.mirai.internal.utils.toUHexString
|
||||
import net.mamoe.mirai.utils.cast
|
||||
import net.mamoe.mirai.utils.debug
|
||||
import net.mamoe.mirai.utils.warning
|
||||
import kotlin.random.Random
|
||||
@ -235,10 +237,12 @@ internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Re
|
||||
readByte().toInt().and(0xff)
|
||||
} == 0x83) {
|
||||
return@mapNotNull MemberJoinEvent.Invite(group.newMember(msg.getNewMemberInfo())
|
||||
.cast<NormalMember>()
|
||||
.also { group.members.delegate.add(it) })
|
||||
}
|
||||
|
||||
return@mapNotNull MemberJoinEvent.Active(group.newMember(msg.getNewMemberInfo())
|
||||
.cast<NormalMember>()
|
||||
.also { group.members.delegate.add(it) })
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ internal class WtLogin {
|
||||
writeSsoPacket(client, client.subAppId, commandName, sequenceId = sequenceId) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
|
||||
writeShort(9) // subCommand
|
||||
writeShort(17) // count of TLVs, probably ignored by server?
|
||||
writeShort(0x18) // count of TLVs, probably ignored by server?
|
||||
//writeShort(LoginType.PASSWORD.value.toShort())
|
||||
|
||||
t18(appId, client.appClientVersion, client.uin)
|
||||
@ -161,7 +161,8 @@ internal class WtLogin {
|
||||
*/
|
||||
t116(client.miscBitMap, client.subSigMap)
|
||||
t100(appId, client.subAppId, client.appClientVersion, client.ssoVersion)
|
||||
t107(6)
|
||||
t107(0)
|
||||
t108(client.device.imei.toByteArray())
|
||||
|
||||
// t108(byteArrayOf())
|
||||
// ignored: t104()
|
||||
@ -192,9 +193,11 @@ internal class WtLogin {
|
||||
t145(client.device.guid)
|
||||
t147(appId, client.apkVersionName, client.apkSignatureMd5)
|
||||
|
||||
/*
|
||||
if (client.miscBitMap and 0x80 != 0) {
|
||||
t166(1)
|
||||
}
|
||||
*/
|
||||
|
||||
// ignored t16a because array5 is null
|
||||
|
||||
@ -210,14 +213,14 @@ internal class WtLogin {
|
||||
"connect.qq.com",
|
||||
"qzone.qq.com",
|
||||
"vip.qq.com",
|
||||
"gamecenter.qq.com",
|
||||
"qun.qq.com",
|
||||
"game.qq.com",
|
||||
"qqweb.qq.com",
|
||||
"office.qq.com",
|
||||
"ti.qq.com",
|
||||
"mail.qq.com",
|
||||
"qzone.com",
|
||||
"mma.qq.com"
|
||||
"mma.qq.com",
|
||||
)
|
||||
)
|
||||
|
||||
@ -243,7 +246,10 @@ internal class WtLogin {
|
||||
t202(bssid, ssid)
|
||||
}
|
||||
|
||||
t177()
|
||||
t177(
|
||||
buildTime = client.buildTime,
|
||||
buildVersion = client.sdkVersion,
|
||||
)
|
||||
t516()
|
||||
t521()
|
||||
|
||||
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2019-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.internal.utils
|
||||
|
||||
import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol
|
||||
import java.util.*
|
||||
|
||||
internal class MiraiProtocolInternal(
|
||||
@JvmField internal val apkId: String,
|
||||
@JvmField internal val id: Long,
|
||||
@JvmField internal val ver: String,
|
||||
@JvmField internal val sdkVer: String,
|
||||
@JvmField internal val miscBitMap: Int,
|
||||
@JvmField internal val subSigMap: Int,
|
||||
@JvmField internal val mainSigMap: Int,
|
||||
@JvmField internal val sign: String,
|
||||
@JvmField internal val buildTime: Long,
|
||||
) {
|
||||
internal companion object {
|
||||
internal val protocols = EnumMap<MiraiProtocol, MiraiProtocolInternal>(
|
||||
MiraiProtocol::class.java
|
||||
)
|
||||
|
||||
operator fun get(protocol: MiraiProtocol): MiraiProtocolInternal =
|
||||
protocols[protocol] ?: error("Internal Error: Missing protocol $protocol")
|
||||
|
||||
init {
|
||||
protocols[MiraiProtocol.ANDROID_PHONE] = MiraiProtocolInternal(
|
||||
"com.tencent.mobileqq",
|
||||
537066419,
|
||||
"8.4.18",
|
||||
"6.0.0.2454",
|
||||
184024956,
|
||||
0x10400,
|
||||
34869472,
|
||||
"A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D",
|
||||
1604580615L,
|
||||
)
|
||||
protocols[MiraiProtocol.ANDROID_PAD] = MiraiProtocolInternal(
|
||||
"com.tencent.mobileqq",
|
||||
537062409, "8.4.18",
|
||||
"6.0.0.2454",
|
||||
184024956,
|
||||
0x10400,
|
||||
34869472,
|
||||
"A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D",
|
||||
1604580615L,
|
||||
)
|
||||
protocols[MiraiProtocol.ANDROID_WATCH] = MiraiProtocolInternal(
|
||||
"com.tencent.mobileqq",
|
||||
537061176,
|
||||
"8.2.7",
|
||||
"6.0.0.2413",
|
||||
184024956,
|
||||
0x10400,
|
||||
34869472,
|
||||
"A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D",
|
||||
1571193922L
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -12,10 +12,10 @@
|
||||
|
||||
package net.mamoe.mirai.internal.utils
|
||||
|
||||
import net.mamoe.mirai.contact.ContactOrBot
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.message.data.AtAll.display
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
|
||||
internal fun Int.toIpV4AddressString(): String {
|
||||
@ -43,17 +43,17 @@ internal fun String.chineseLength(upTo: Int): Int {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun MessageChain.estimateLength(upTo: Int): Int =
|
||||
internal fun MessageChain.estimateLength(target: ContactOrBot, upTo: Int): Int =
|
||||
sumUpTo(upTo) { it, up ->
|
||||
it.estimateLength(up)
|
||||
it.estimateLength(target, up)
|
||||
}
|
||||
|
||||
internal fun SingleMessage.estimateLength(upTo: Int): Int {
|
||||
internal fun SingleMessage.estimateLength(target: ContactOrBot, upTo: Int): Int {
|
||||
return when (this) {
|
||||
is QuoteReply -> 444 + this.source.originalMessage.estimateLength(upTo) // Magic number
|
||||
is QuoteReply -> 444 + this.source.originalMessage.estimateLength(target, upTo) // Magic number
|
||||
is Image -> 260 // Magic number
|
||||
is PlainText -> content.chineseLength(upTo)
|
||||
is At -> display.chineseLength(upTo)
|
||||
is At -> this.getDisplay(target.safeCast()).chineseLength(upTo)
|
||||
is AtAll -> display.chineseLength(upTo)
|
||||
else -> this.toString().chineseLength(upTo)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user