Commit e27c9bd4 authored by jiahua.liu's avatar jiahua.liu

Merge remote-tracking branch 'origin/master'

parents 2d6dd098 751d6535
......@@ -2,6 +2,14 @@
开发版本. 频繁更新, 不保证高稳定性
## `0.15.1` Unreleased
### mirai-core
- 统一异常处理: 所有群成员相关操作无权限时均抛出异常而不返回 `false`.
### mirai-core-qqandroid
- 初始化未完成时缓存接收的所有事件包 (#46)
## `0.15.0` 2020/2/14
### mirai-core
......
......@@ -2,6 +2,7 @@
kotlin.code.style=official
# config
mirai_version=0.15.0
mirai_japt_version=1.0.0
kotlin.incremental.multiplatform=true
kotlin.parallel.tasks.in.project=true
# kotlin
......
......@@ -27,11 +27,6 @@ object NotVerifiedSessionException : IllegalAccessException("Session未激活")
*/
object NoSuchBotException: IllegalAccessException("指定Bot不存在")
/**
* 指定Bot不存在
*/
object PermissionDeniedException: IllegalAccessException("无操作限权")
/**
* 错误参数
*/
......
......@@ -35,6 +35,7 @@ import net.mamoe.mirai.api.http.data.common.DTO
import net.mamoe.mirai.api.http.data.common.VerifyDTO
import net.mamoe.mirai.api.http.util.jsonParseOrNull
import net.mamoe.mirai.api.http.util.toJson
import net.mamoe.mirai.contact.PermissionDeniedException
import org.slf4j.Logger
import org.slf4j.helpers.NOPLogger
import org.slf4j.helpers.NOPLoggerFactory
......
......@@ -4,7 +4,6 @@ import io.ktor.application.Application
import io.ktor.application.call
import io.ktor.routing.routing
import kotlinx.serialization.Serializable
import net.mamoe.mirai.api.http.data.PermissionDeniedException
import net.mamoe.mirai.api.http.data.StateCode
import net.mamoe.mirai.api.http.data.common.DTO
import net.mamoe.mirai.api.http.data.common.VerifyDTO
......@@ -19,37 +18,31 @@ fun Application.groupManageModule() {
* 禁言(需要相关权限)
*/
miraiVerify<MuteDTO>("/muteAll") {
it.session.bot.getGroup(it.target).muteAll = true
it.session.bot.getGroup(it.target).isMuteAll = true
call.respondStateCode(StateCode.Success)
}
miraiVerify<MuteDTO>("/unmuteAll") {
it.session.bot.getGroup(it.target).muteAll = false
it.session.bot.getGroup(it.target).isMuteAll = false
call.respondStateCode(StateCode.Success)
}
miraiVerify<MuteDTO>("/mute") {
when (it.session.bot.getGroup(it.target)[it.memberId].mute(it.time)) {
true -> call.respondStateCode(StateCode.Success)
else -> throw PermissionDeniedException
}
it.session.bot.getGroup(it.target)[it.memberId].mute(it.time)
call.respondStateCode(StateCode.Success)
}
miraiVerify<MuteDTO>("/unmute") {
when (it.session.bot.getGroup(it.target).members[it.memberId].unmute()) {
true -> call.respondStateCode(StateCode.Success)
else -> throw PermissionDeniedException
}
it.session.bot.getGroup(it.target).members[it.memberId].unmute()
call.respondStateCode(StateCode.Success)
}
/**
* 移出群聊(需要相关权限)
*/
miraiVerify<KickDTO>("/kick") {
when (it.session.bot.getGroup(it.target)[it.memberId].kick(it.msg)) {
true -> call.respondStateCode(StateCode.Success)
else -> throw PermissionDeniedException
}
it.session.bot.getGroup(it.target)[it.memberId].kick(it.msg)
call.respondStateCode(StateCode.Success)
}
/**
......@@ -65,8 +58,8 @@ fun Application.groupManageModule() {
with(dto.config) {
name?.let { group.name = it }
announcement?.let { group.entranceAnnouncement = it }
confessTalk?.let { group.confessTalk = it }
allowMemberInvite?.let { group.allowMemberInvite = it }
confessTalk?.let { group.isConfessTalkEnabled = it }
allowMemberInvite?.let { group.isAllowMemberInvite = it }
// TODO: 待core接口实现设置可改
// autoApprove?.let { group.autoApprove = it }
// anonymousChat?.let { group.anonymousChat = it }
......@@ -128,8 +121,8 @@ private data class GroupDetailDTO(
val anonymousChat: Boolean? = null
) : DTO {
constructor(group: Group) : this(
group.name, group.entranceAnnouncement, group.confessTalk, group.allowMemberInvite,
group.autoApprove, group.anonymousChat
group.name, group.entranceAnnouncement, group.isConfessTalkEnabled, group.isAllowMemberInvite,
group.isAutoApproveEnabled, group.isAnonymousChatEnabled
)
}
......
......@@ -227,9 +227,9 @@ internal class MemberImpl(
override val bot: QQAndroidBot get() = qq.bot
override suspend fun mute(durationSeconds: Int): Boolean {
override suspend fun mute(durationSeconds: Int) {
if (group.botPermission != MemberPermission.OWNER && (!group.botPermission.isOperator() || this.isOperator())) {
return false
throw PermissionDeniedException()
}
bot.network.run {
......@@ -243,12 +243,11 @@ internal class MemberImpl(
@Suppress("RemoveRedundantQualifierName") // or unresolved reference
net.mamoe.mirai.event.events.MemberMuteEvent(this@MemberImpl, durationSeconds, null).broadcast()
return true
}
override suspend fun unmute(): Boolean {
override suspend fun unmute() {
if (group.botPermission != MemberPermission.OWNER && (!group.botPermission.isOperator() || this.isOperator())) {
return false
throw PermissionDeniedException()
}
bot.network.run {
......@@ -262,16 +261,15 @@ internal class MemberImpl(
@Suppress("RemoveRedundantQualifierName") // or unresolved reference
net.mamoe.mirai.event.events.MemberUnmuteEvent(this@MemberImpl, null).broadcast()
return true
}
override suspend fun kick(message: String): Boolean {
override suspend fun kick(message: String) {
if (group.botPermission != MemberPermission.OWNER && (!group.botPermission.isOperator() || this.isOperator())) {
return false
throw PermissionDeniedException()
}
bot.network.run {
return TroopManagement.Kick(
TroopManagement.Kick(
client = bot.client,
member = this@MemberImpl,
message = message
......@@ -394,7 +392,7 @@ internal class GroupImpl(
}
override var allowMemberInvite: Boolean
override var isAllowMemberInvite: Boolean
get() = _allowMemberInvite
set(newValue) {
this.checkBotPermissionOperator()
......@@ -414,19 +412,19 @@ internal class GroupImpl(
}
}
override var autoApprove: Boolean
override var isAutoApproveEnabled: Boolean
get() = _autoApprove
set(newValue) {
TODO()
}
override var anonymousChat: Boolean
override var isAnonymousChatEnabled: Boolean
get() = _anonymousChat
set(newValue) {
TODO()
}
override var confessTalk: Boolean
override var isConfessTalkEnabled: Boolean
get() = _confessTalk
set(newValue) {
this.checkBotPermissionOperator()
......@@ -447,7 +445,7 @@ internal class GroupImpl(
}
override var muteAll: Boolean
override var isMuteAll: Boolean
get() = _muteAll
set(newValue) {
this.checkBotPermissionOperator()
......
......@@ -180,14 +180,14 @@ internal class OnlinePush {
return if (target == 0L) {
if (time == 0) {
GroupMuteAllEvent(
origin = group.muteAll.also { group._muteAll = false },
origin = group.isMuteAll.also { group._muteAll = false },
new = false,
operator = operator,
group = group
)
} else {
GroupMuteAllEvent(
origin = group.muteAll.also { group._muteAll = true },
origin = group.isMuteAll.also { group._muteAll = true },
new = true,
operator = operator,
group = group
......@@ -213,7 +213,7 @@ internal class OnlinePush {
val operator = group[this.readUInt().toLong()]
val switch = this.readInt() == 0
return GroupAllowAnonymousChatEvent(
origin = group.anonymousChat.also { group._anonymousChat = switch },
origin = group.isAnonymousChatEnabled.also { group._anonymousChat = switch },
new = switch,
operator = operator,
group = group
......@@ -236,7 +236,7 @@ internal class OnlinePush {
when (message) {
"管理员已关闭群聊坦白说" -> {
return GroupAllowConfessTalkEvent(
origin = group.confessTalk.also { group._confessTalk = false },
origin = group.isConfessTalkEnabled.also { group._confessTalk = false },
new = false,
group = group,
isByBot = false
......@@ -244,7 +244,7 @@ internal class OnlinePush {
}
"管理员已开启群聊坦白说" -> {
return GroupAllowConfessTalkEvent(
origin = group.confessTalk.also { group._confessTalk = true },
origin = group.isConfessTalkEnabled.also { group._confessTalk = true },
new = true,
group = group,
isByBot = false
......
......@@ -215,7 +215,7 @@ fun ByteReadPacket.readIoBuffer(
* 解析 SSO 层包装
*/
@UseExperimental(ExperimentalUnsignedTypes::class)
private fun parseSsoFrame(flag3: Int, input: ByteReadPacket): KnownPacketFactories.IncomingPacket {
private fun parseSsoFrame(flag3: Int, input: ByteReadPacket): KnownPacketFactories.IncomingPacket<*> {
val commandName: String
val ssoSequenceId: Int
......@@ -257,7 +257,7 @@ private fun parseSsoFrame(flag3: Int, input: ByteReadPacket): KnownPacketFactori
* 解析 Uni 层包装
*/
@UseExperimental(ExperimentalUnsignedTypes::class)
private fun parseUniFrame(input: ByteReadPacket): KnownPacketFactories.IncomingPacket {
private fun parseUniFrame(input: ByteReadPacket): KnownPacketFactories.IncomingPacket<*> {
// 00 00 00 30 00 01 2F 7C 00 00 00 00 00 00 00 04 00 00 00 14 67 78 68 72 65 70 6F 72 74 2E 72 65 70 6F 72 74 00 00 00 08 66 82 D3 0B 00 00 00 00
// 00 00 00 06 08 00
......
package net.mamoe.mirai.contact
/**
* 权限不足
*/
actual class PermissionDeniedException : IllegalStateException {
actual constructor() : super("Permission denied")
actual constructor(message: String?) : super(message)
}
\ No newline at end of file
package net.mamoe.mirai.event.events
@Suppress("unused")
actual class EventCancelledException : RuntimeException {
actual constructor() : super()
actual constructor(message: String?) : super(message)
actual constructor(message: String?, cause: Throwable?) : super(message, cause)
actual constructor(cause: Throwable?) : super(cause)
}
\ No newline at end of file
......@@ -27,6 +27,7 @@ import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.LoginFailedException
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.transferTo
import kotlin.jvm.JvmStatic
/**
* 机器人对象. 一个机器人实例登录一个 QQ 账号.
......@@ -42,6 +43,7 @@ abstract class Bot : CoroutineScope {
/**
* 复制一份此时的 [Bot] 实例列表.
*/
@JvmStatic
val instances: List<WeakRef<Bot>> get() = BotImpl.instances.toList()
/**
......@@ -52,6 +54,7 @@ abstract class Bot : CoroutineScope {
/**
* 获取一个 [Bot] 实例, 找不到则 [NoSuchElementException]
*/
@JvmStatic
fun instanceWhose(qq: Long): Bot = BotImpl.instanceWhose(qq = qq)
}
......
......@@ -26,6 +26,24 @@ data class BotAccount(
val passwordMd5: ByteArray // md5
) {
constructor(id: Long, passwordPlainText: String) : this(id, md5(passwordPlainText.toByteArray()))
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as BotAccount
if (id != other.id) return false
if (!passwordMd5.contentEquals(other.passwordMd5)) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + passwordMd5.contentHashCode()
return result
}
}
/**
......
......@@ -38,8 +38,8 @@ interface Contact : CoroutineScope {
/**
* 可以是 QQ 号码或者群号码.
*
* 对于 QQ, `uin` 与 `id` 是相同的意思.
* 对于 Group, `groupCode` 与 `id` 是相同的意思.
* 对于 [QQ], `uin` 与 `id` 是相同的意思.
* 对于 [Group], `groupCode` 与 `id` 是相同的意思.
*/
val id: Long
......
......@@ -46,8 +46,9 @@ interface Group : Contact, CoroutineScope {
* 当前仅能修改状态.
*
* @see GroupMuteAllEvent
*/// TODO: 2020/2/5 实现 muteAll 的查询
var muteAll: Boolean
* @throws PermissionDeniedException 无权限修改时将会抛出异常
*/
var isMuteAll: Boolean
/**
* 坦白说状态. `true` 为允许.
*
......@@ -56,7 +57,7 @@ interface Group : Contact, CoroutineScope {
* @see GroupAllowConfessTalkEvent
* @throws PermissionDeniedException 无权限修改时将会抛出异常
*/
var confessTalk: Boolean
var isConfessTalkEnabled: Boolean
/**
* 允许群员邀请好友入群的状态. `true` 为允许
*
......@@ -65,15 +66,15 @@ interface Group : Contact, CoroutineScope {
* @see GroupAllowMemberInviteEvent
* @throws PermissionDeniedException 无权限修改时将会抛出异常
*/
var allowMemberInvite: Boolean
var isAllowMemberInvite: Boolean
/**
* 自动加群审批
*/
val autoApprove: Boolean
val isAutoApproveEnabled: Boolean
/**
* 匿名聊天
*/
val anonymousChat: Boolean
val isAnonymousChatEnabled: Boolean
/**
* 同为 groupCode, 用户看到的群号码.
......@@ -143,7 +144,7 @@ interface Group : Contact, CoroutineScope {
/**
* by @kar98k
*/
*/ // don't @JvmStatic: JDK 1.8 required
fun calculateGroupUinByGroupCode(groupCode: Long): Long {
var left: Long = groupCode / 1000000L
......
......@@ -35,20 +35,22 @@ interface Member : QQ, Contact {
/**
* 群名片. 可能为空. 修改时将会触发事件
*
* 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
* 在修改时将会异步上传至服务器.
*
* @see [groupCardOrNick] 获取非空群名片或昵称
*
* @see MemberCardChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件
* @throws PermissionDeniedException 无权限修改时
*/
var nameCard: String
/**
* 群头衔
*
* 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
* 在修改时将会异步上传至服务器.
*
* @see MemberSpecialTitleChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件
* @throws PermissionDeniedException 无权限修改时
*/
var specialTitle: String
......@@ -63,22 +65,25 @@ interface Member : QQ, Contact {
* @see Int.daysToSeconds
*
* @see MemberMuteEvent 成员被禁言事件
* @throws PermissionDeniedException 无权限修改时
*/
suspend fun mute(durationSeconds: Int): Boolean
suspend fun mute(durationSeconds: Int)
/**
* 解除禁言. 机器人无权限时返回 `false`.
* 解除禁言.
*
* @see MemberUnmuteEvent 成员被取消禁言事件.
* @throws PermissionDeniedException 无权限修改时
*/
suspend fun unmute(): Boolean
suspend fun unmute()
/**
* 踢出该成员. 机器人无权限时返回 `false`.
* 踢出该成员.
*
* @see MemberLeaveEvent.Kick 成员被踢出事件.
* @throws PermissionDeniedException 无权限修改时
*/
suspend fun kick(message: String = ""): Boolean
suspend fun kick(message: String = "")
/**
* 当且仅当 `[other] is [Member] && [other].id == this.id && [other].group == this.group` 时为 true
......@@ -94,10 +99,10 @@ interface Member : QQ, Contact {
val Member.groupCardOrNick: String get() = this.nameCard.takeIf { it.isNotEmpty() } ?: this.nick
@ExperimentalTime
suspend inline fun Member.mute(duration: Duration): Boolean {
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" }
return this.mute(duration.inSeconds.toInt())
this.mute(duration.inSeconds.toInt())
}
suspend inline fun Member.mute(durationSeconds: Long) = this.mute(durationSeconds.toInt())
\ No newline at end of file
......@@ -72,9 +72,9 @@ inline fun Member.isOperator(): Boolean = this.permission.isOperator()
/**
* 权限不足
*/
class PermissionDeniedException : IllegalStateException {
constructor() : super("Permission denied")
constructor(message: String?) : super(message)
expect class PermissionDeniedException : IllegalStateException {
constructor()
constructor(message: String?)
}
@UseExperimental(MiraiExperimentalAPI::class)
......
......@@ -438,14 +438,14 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
*/
@MessageDsl
inline fun <reified M : Message> has(): ListeningFilter =
content { message.any { it::class == M::class } }
content { message.any { it is M } }
/**
* 如果消息内容包含 [M] 类型的 [Message], 就执行 [onEvent]
*/
@MessageDsl
inline fun <reified M : Message> has(crossinline onEvent: MessageListener<T>): Listener<T> =
content({ message.any { it::class == M::class } }, onEvent)
content({ message.any { it is M } }, onEvent)
/**
* 如果 [filter] 返回 `true`
......
......@@ -22,11 +22,11 @@ import net.mamoe.mirai.utils.MiraiExperimentalAPI
@Suppress("unused")
class EventCancelledException : RuntimeException {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
expect class EventCancelledException : RuntimeException {
constructor()
constructor(message: String?)
constructor(message: String?, cause: Throwable?)
constructor(cause: Throwable?)
}
// note: 若你使用 IntelliJ IDEA, 按 alt + 7 可打开结构
......
......@@ -13,6 +13,7 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.message.data.At
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain
......@@ -31,7 +32,7 @@ class GroupMessage(
val permission: MemberPermission,
sender: Member,
override val message: MessageChain
) : MessagePacket<Member, Group>(bot) {
) : MessagePacket<Member, Group>(bot), Event {
val group: Group by group.unsafeWeakRef()
override val sender: Member by sender.unsafeWeakRef()
......
......@@ -12,6 +12,7 @@
package net.mamoe.mirai.message.data
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.groupCardOrNick
import net.mamoe.mirai.utils.MiraiInternalAPI
......@@ -22,7 +23,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
*/
class At @MiraiInternalAPI constructor(val target: Long, val display: String) : Message {
@UseExperimental(MiraiInternalAPI::class)
constructor(member: Member) : this(member.id, "@${member.nick}")
constructor(member: Member) : this(member.id, "@${member.groupCardOrNick}")
override fun toString(): String = display
......
......@@ -44,6 +44,7 @@ internal val factory: BotFactory = run {
/**
* 加载现有协议的 [BotFactory], 并使用指定的 [配置][configuration] 构造 [Bot] 实例
*/
@JvmOverloads
fun Bot(context: Context, qq: Long, password: String, configuration: BotConfiguration = BotConfiguration.Default): Bot =
factory.Bot(context, qq, password, configuration)
......@@ -57,6 +58,7 @@ inline fun Bot(context: Context, qq: Long, password: String, configuration: (Bot
/**
* 加载现有协议的 [BotFactory], 并使用指定的 [配置][configuration] 构造 [Bot] 实例
*/
@JvmOverloads
fun Bot(qq: Long, password: String, configuration: BotConfiguration = BotConfiguration.Default): Bot =
factory.Bot(ContextImpl(), qq, password, configuration)
......
package net.mamoe.mirai.contact
/**
* 权限不足
*/ // 不要删除多平台结构
actual class PermissionDeniedException : IllegalStateException {
actual constructor() : super("Permission denied")
actual constructor(message: String?) : super(message)
}
\ No newline at end of file
package net.mamoe.mirai.event.events
// 不要删除跨平台结构.
// 否则在 Java 中这个 class 不会被认为是 java.lang.RuntimeException (Kotlin bug)
@Suppress("unused")
actual class EventCancelledException : RuntimeException {
actual constructor() : super()
actual constructor(message: String?) : super(message)
actual constructor(message: String?, cause: Throwable?) : super(message, cause)
actual constructor(cause: Throwable?) : super(cause)
}
\ No newline at end of file
......@@ -62,7 +62,7 @@ internal class DefaultLoginSolver : LoginSolver() {
}
}
bot.logger.info("请输入 4 位字母验证码. 若要更换验证码, 请直接回车")
return readLine()?.takeUnless { it.isEmpty() || it.length != 4 }.also {
return readLine()!!.takeUnless { it.isEmpty() || it.length != 4 }.also {
bot.logger.info("正在提交[$it]中...")
}
}
......
apply plugin: "java"
apply plugin: "kotlin"
dependencies {
implementation files("../../mirai-core/build/classes/kotlin/jvm/main") // IDE bug
implementation files("../../mirai-core-qqandroid/build/classes/kotlin/jvm/main") // IDE bug
implementation project(":mirai-core-qqandroid")
implementation project(":mirai-japt")
}
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'
\ No newline at end of file
package demo;
import net.mamoe.mirai.japt.BlockingBot;
import net.mamoe.mirai.japt.BlockingContacts;
import net.mamoe.mirai.japt.BlockingQQ;
import net.mamoe.mirai.japt.Events;
import net.mamoe.mirai.message.GroupMessage;
class BlockingTest {
public static void main(String[] args) throws InterruptedException {
BlockingBot bot = BlockingBot.newInstance(123456, "");
bot.login();
bot.getFriendList().forEach(friend -> {
System.out.println(friend.getNick());
});
Events.subscribeAlways(GroupMessage.class, (GroupMessage message) -> {
final BlockingQQ sender = BlockingContacts.createBlocking(message.getSender());
sender.sendMessage("Hello");
});
Thread.sleep(999999999);
}
}
......@@ -3,7 +3,51 @@
Mirai Java Apt
提供一些阻塞/异步/RxJava API 来让 Java 调用 Mirai 的挂起函数 API 更容易
提供 Utils 类来让 Java 调用 Mirai 的内联方法更容易
提供阻塞API 来让 Java 调用 Mirai 的 API 更容易
该模块暂未完成.
\ No newline at end of file
## Requirements
- JDK 1.8+
## 开始
```java
class Test{
public static void main(String[] args){
BlockingBot bot = BlockingBot.newInstance(123456, "");
bot.login();
bot.getFriendList().forEach(friend -> {
System.out.println(friend.getNick());
});
Events.subscribeAlways(GroupMessage.class, (GroupMessage message) -> {
final BlockingQQ sender = BlockingContacts.createBlocking(message.getSender());
sender.sendMessage("Hello");
});
Thread.sleep(999999999);
}
}
```
## 便捷开发
在 IntelliJ IDEA 或 Android Studio 中找到设置 `Editor -> General -> Postfix Completion`, 添加一个设置到 `Java` 分类中:
![](.README_images/ce3034e3.png)
Applicable expression types:
```
net.mamoe.mirai.contact.Contact
```
转换后表达式:
```
net.mamoe.mirai.japt.BlockingContacts.createBlocking($EXPR$)
```
效果:
![4SY8BC@J4ZKQM7OZ_~BC1I_1](.README_images/4SY8BC%40J4ZKQM%5D7OZ_~BC1I_1.png)
![722WEHTTXD6XFFH43](.README_images/722W%28E%24HTTX%7BD6XFFH%5D%5D%2443.png)
plugins {
kotlin("jvm")
java
`maven-publish`
id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME
}
apply(from = rootProject.file("gradle/publish.gradle"))
val kotlinVersion: String by rootProject.ext
val atomicFuVersion: String by rootProject.ext
val coroutinesVersion: String by rootProject.ext
......@@ -13,6 +17,12 @@ val serializationVersion: String by rootProject.ext
val klockVersion: String by rootProject.ext
val ktorVersion: String by rootProject.ext
description = "Java helper for Mirai"
@Suppress("PropertyName")
val mirai_japt_version: String by rootProject.ext
version = mirai_japt_version
kotlin {
sourceSets {
all {
......@@ -38,6 +48,8 @@ dependencies {
api(kotlinx("io", kotlinXIoVersion))
api(kotlinx("coroutines-io", coroutinesIoVersion))
api(kotlinx("coroutines-core", coroutinesVersion))
api(kotlin("stdlib-jdk7", kotlinVersion))
api(kotlin("stdlib-jdk8", kotlinVersion))
}
tasks.withType<JavaCompile>() {
......
package net.mamoe.mirai.japt;
import kotlinx.io.core.ByteReadPacket;
import net.mamoe.mirai.Bot;
import net.mamoe.mirai.BotAccount;
import net.mamoe.mirai.BotFactoryJvmKt;
import net.mamoe.mirai.contact.QQ;
import net.mamoe.mirai.data.AddFriendResult;
import net.mamoe.mirai.data.GroupInfo;
import net.mamoe.mirai.data.MemberInfo;
import net.mamoe.mirai.message.data.Image;
import net.mamoe.mirai.network.BotNetworkHandler;
import net.mamoe.mirai.utils.BotConfiguration;
import net.mamoe.mirai.utils.MiraiExperimentalAPI;
import net.mamoe.mirai.utils.MiraiInternalAPI;
import net.mamoe.mirai.utils.MiraiLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.OutputStream;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Stream;
/**
* 对 {@link Bot} 的阻塞式包装
*
* @see Bot
*/
@SuppressWarnings("unused")
public interface BlockingBot {
/**
* 使用默认配置创建一个机器人实例
*
* @param id qq 号
* @param password 密码
* @return 机器人实例
*/
static BlockingBot newInstance(long id, String password) {
return BlockingContacts.createBlocking(BotFactoryJvmKt.Bot(id, password));
}
// TODO: 2020/2/3 需要更新
/**
* 使用特定配置创建一个机器人实例
*
* @param id qq 号
* @param password 密码
* @return 机器人实例
*/
static BlockingBot newInstance(long id, String password, BotConfiguration configuration) {
return BlockingContacts.createBlocking(BotFactoryJvmKt.Bot(id, password, configuration));
}
// 要获取 Bot 实例列表, 请前往 BotKt
/**
* 账号信息
......@@ -29,6 +65,13 @@ public interface BlockingBot {
*/
long getUin();
/**
* 获取昵称
*/
@NotNull
@MiraiExperimentalAPI(message = "还未支持")
String getNick();
/**
* 日志记录器
*/
......@@ -37,27 +80,33 @@ public interface BlockingBot {
// region contacts
/**
* 获取自身 QQ 实例
*/
@NotNull
QQ getSelfQQ();
/**
* 与这个机器人相关的 QQ 列表. 机器人与 QQ 不一定是好友
*/
@NotNull
List<BlockingQQ> getQQs();
List<BlockingQQ> getFriendList();
/**
* 获取缓存的 QQ 对象. 若没有对应的缓存, 则会线程安全地创建一个.
*/
@NotNull
BlockingQQ getQQ(long id);
BlockingQQ getFriend(long id);
/**
* 与这个机器人相关的群列表. 机器人不一定是群成员.
*/
@NotNull
List<BlockingGroup> getGroups();
List<BlockingGroup> getGroupList();
/**
* 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个.
* 若 {@code id} 无效, 将会抛出 {@link java.util.NoSuchElementException}
* 若 {@code id} 无效, 将会抛出 {@link NoSuchElementException}
*/
@NotNull
BlockingGroup getGroup(long id);
......@@ -75,20 +124,48 @@ public interface BlockingBot {
/**
* 登录.
* <p>
* 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login]
*/
void login();
/**
* 查询群列表. 返回值前 32 bits 为 uin, 后 32 bits 为 groupCode
*/
@NotNull
Stream<Long> queryGroupList();
/**
* 查询群资料. 获得的仅为当前时刻的资料.
* 请优先使用 {@link #getGroup(long)} 然后查看群资料.
*/
@NotNull
GroupInfo queryGroupInfo(long groupCode);
/**
* 查询群成员列表.
* 请优先使用 {@link #getGroup(long)} , {@link BlockingGroup#getMembers()} 查看群成员.
* <p>
* 这个函数很慢. 请不要频繁使用.
*/
@NotNull
Stream<MemberInfo> queryGroupMemberList(long groupUin, long groupCode, long ownerId);
// endregion
// region actions
@NotNull
byte[] downloadAsByteArray(@NotNull Image image);
@NotNull
ByteReadPacket download(@NotNull Image image);
/**
* 下载图片到 {@code outputStream}.
* <p>
* 不会自动关闭 {@code outputStream}
*/
void download(@NotNull Image image, @NotNull OutputStream outputStream);
/**
* 添加一个好友
*
......
package net.mamoe.mirai.japt;
import net.mamoe.mirai.Bot;
import net.mamoe.mirai.contact.Contact;
import net.mamoe.mirai.contact.Member;
import net.mamoe.mirai.contact.QQ;
import net.mamoe.mirai.event.events.BeforeImageUploadEvent;
import net.mamoe.mirai.event.events.EventCancelledException;
import net.mamoe.mirai.event.events.ImageUploadEvent;
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent;
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent;
import net.mamoe.mirai.message.data.Image;
import net.mamoe.mirai.message.data.Message;
import net.mamoe.mirai.message.data.MessageChain;
import net.mamoe.mirai.utils.ExternalImage;
import org.jetbrains.annotations.NotNull;
/**
* 对 {@link Contact} 的阻塞式包装.
*/
@SuppressWarnings("unused")
public interface BlockingContact {
/**
* 这个联系人所属 [Bot]
* 这个联系人所属 {@link Bot}
*/
@NotNull
BlockingBot getBot();
/**
* 可以是 QQ 号码或者群号码 [GroupId].
* 可以是 QQ 号码或者群号码.
* <p>
* 对于 QQ, {@code uin} 与 {@code id} 是相同的意思.
* 对于 Group, {@code groupCode} 与 {@code id} 是相同的意思.
*/
long getId();
/**
* 向这个对象发送消息.
*
* @throws EventCancelledException 当发送消息事件被取消
* @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出
* @see FriendMessageSendEvent 发送好友信息事件, cancellable
* @see GroupMessageSendEvent 发送群消息事件. cancellable
*/
void sendMessage(@NotNull MessageChain messages);
// kotlin bug
void sendMessage(@NotNull MessageChain messages) throws EventCancelledException, IllegalStateException;
/**
* 向这个对象发送消息.
*
* @throws EventCancelledException 当发送消息事件被取消
* @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出
* @see FriendMessageSendEvent 发送好友信息事件, cancellable
* @see GroupMessageSendEvent 发送群消息事件. cancellable
*/
void sendMessage(@NotNull String message);
void sendMessage(@NotNull String message) throws EventCancelledException, IllegalStateException;
/**
* 向这个对象发送消息.
*
* @throws EventCancelledException 当发送消息事件被取消
* @throws IllegalStateException 发送群消息时若 [Bot] 被禁言抛出
* @see FriendMessageSendEvent 发送好友信息事件, cancellable
* @see GroupMessageSendEvent 发送群消息事件. cancellable
*/
void sendMessage(@NotNull Message message);
void sendMessage(@NotNull Message message) throws EventCancelledException, IllegalStateException;
/**
* 上传一个图片以备发送.
* 群图片与好友图片在服务器上是通用的, 在 mirai 目前不通用.
*
* @throws EventCancelledException 当发送消息事件被取消
* @see BeforeImageUploadEvent 图片发送前事件, cancellable
* @see ImageUploadEvent 图片发送完成事件
*/
Image uploadImage(@NotNull ExternalImage image) throws EventCancelledException;
/**
* 判断 {@code this} 和 {@code other} 是否是相同的类型, 并且 {@link Contact#getId()} 相同.
* <p>
* 注:
* {@link Contact#getId()} 相同的 {@link Member} 和 {@link QQ}, 他们并不 equals.
* 因为, {@link Member} 含义为群员, 必属于一个群.
* 而 {@link QQ} 含义为一个独立的人, 可以是好友, 也可以是陌生人.
*/
boolean equals(Object other);
}
......@@ -8,24 +8,31 @@ import net.mamoe.mirai.japt.internal.BlockingBotImpl;
import net.mamoe.mirai.japt.internal.BlockingGroupImpl;
import net.mamoe.mirai.japt.internal.BlockingMemberImpl;
import net.mamoe.mirai.japt.internal.BlockingQQImpl;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* 构造阻塞式的联系人.
*/
public final class BlockingContacts {
public static BlockingQQ createBlocking(QQ qq) {
return new BlockingQQImpl(qq);
@NotNull
public static BlockingQQ createBlocking(@NotNull QQ qq) {
return new BlockingQQImpl(Objects.requireNonNull(qq));
}
public static BlockingGroup createBlocking(Group group) {
return new BlockingGroupImpl(group);
@NotNull
public static BlockingGroup createBlocking(@NotNull Group group) {
return new BlockingGroupImpl(Objects.requireNonNull(group));
}
public static BlockingMember createBlocking(Member member) {
return new BlockingMemberImpl(member);
@NotNull
public static BlockingMember createBlocking(@NotNull Member member) {
return new BlockingMemberImpl(Objects.requireNonNull(member));
}
public static BlockingBot createBlocking(Bot bot) {
return new BlockingBotImpl(bot);
@NotNull
public static BlockingBot createBlocking(@NotNull Bot bot) {
return new BlockingBotImpl(Objects.requireNonNull(bot));
}
}
package net.mamoe.mirai.japt;
import net.mamoe.mirai.contact.Group;
import net.mamoe.mirai.contact.*;
import net.mamoe.mirai.data.MemberInfo;
import net.mamoe.mirai.event.events.*;
import net.mamoe.mirai.utils.MiraiExperimentalAPI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.List;
import java.util.NoSuchElementException;
@SuppressWarnings("unused")
public interface BlockingGroup extends BlockingContact {
/**
* 群名称.
*/
@NotNull
String getName();
/**
* 修改群名称
* 频繁修改可能会被服务器拒绝.
*
* @throws PermissionDeniedException 无权限修改时将会抛出异常
* @see MemberPermissionChangeEvent
*/
void setName(@NotNull String name) throws PermissionDeniedException;
/**
* 入群公告, 没有时为空字符串. (同步事件更新)
*/
@NotNull
String getEntranceAnnouncement();
/**
* 修改入群公告.
*
* @throws PermissionDeniedException 无权限修改时将会抛出异常
* @see GroupEntranceAnnouncementChangeEvent
*/
void setEntranceAnnouncement(@NotNull String announcement) throws PermissionDeniedException;
/**
* 获取全员禁言状态
*
* @return 全员禁言状态. true 为开启
*/
boolean isMuteAll();
/**
* 设置全体禁言
*
* @see GroupMuteAllEvent
*/
void setMuteAll(boolean enabled) throws PermissionDeniedException;
/**
* 获取坦白说状态
*
* @return 坦白说状态, true 为允许
*/
boolean isConfessTalkEnabled();
/**
* 设置坦白说状态
*
* @throws PermissionDeniedException 无权限修改时将会抛出异常
* @see GroupAllowConfessTalkEvent
*/
void setConfessTalk(boolean enabled) throws PermissionDeniedException;
/**
* 获取允许群员邀请好友入群的状态.
*
* @return 允许群员邀请好友入群的状态. `true` 为允许
*/
boolean isAllowMemberInvite();
/**
* 设置允许群员邀请好友入群的状态.
*
* @throws PermissionDeniedException 无权限修改时将会抛出异常
* @see GroupAllowMemberInviteEvent
*/
void setAllowMemberInvite(boolean allow) throws PermissionDeniedException;
/**
* 获取自动加群审批的状态
*/
boolean isAutoApproveEnabled();
/**
* 匿名聊天是否开启
*/
boolean isAnonymousChatEnabled();
/**
* 同为 groupCode, 用户看到的群号码.
*/
@Override
long getId();
/**
* 群主 (同步事件更新)
*/
......@@ -15,16 +108,29 @@ public interface BlockingGroup extends BlockingContact {
BlockingMember getOwner();
/**
* 群名称 (同步事件更新)
* 机器人被禁言还剩余多少秒
*
* @see BotMuteEvent
* @see GroupKt#isBotMuted
*/
@NotNull
String getName();
int getBotMuteRemaining();
/**
* 入群公告, 没有时为空字符串. (同步事件更新)
* 检查机器人是否正处于禁言状态
*/
default boolean isBotMuted() {
int time = getBotMuteRemaining();
return time != 0 && time != 0xFFFFFFFF;
}
/**
* 机器人在这个群里的权限
*
* @see BotGroupPermissionChangeEvent
*/
@NotNull
String getAnnouncement();
@MiraiExperimentalAPI
MemberPermission getBotPermission();
/**
* 在 {@link Group} 实例创建的时候查询一次. 并与事件同步事件更新
......@@ -32,7 +138,7 @@ public interface BlockingGroup extends BlockingContact {
* **注意**: 获得的列表仅为这一时刻的成员列表的镜像. 它将不会被更新
*/
@NotNull
Map<Long, BlockingMember> getMembers();
List<BlockingMember> getMembers();
/**
* 获取群成员. 若此 ID 的成员不存在, 则会抛出 {@link NoSuchElementException}
......@@ -40,11 +146,38 @@ public interface BlockingGroup extends BlockingContact {
@NotNull
BlockingMember getMember(long id);
/**
* 获取群成员. 若此 ID 的成员不存在则返回 null
*/
@Nullable
BlockingMember getMemberOrNull(long id);
/**
* 检查此 id 的群成员是否存在
*/
boolean containsMember(long id);
/**
* 让机器人退出这个群. 机器人必须为非群主才能退出. 否则将会失败
*/
boolean quit();
/**
* 构造一个 [Member].
* 非特殊情况请不要使用这个函数. 优先使用 [get].
*/
@MiraiExperimentalAPI(message = "dangerous")
@NotNull
Member newMember(@NotNull MemberInfo memberInfo);
@NotNull
String toFullString();
static long calculateGroupUinByGroupCode(long groupCode) {
return Group.Companion.calculateGroupUinByGroupCode(groupCode);
}
static long calculateGroupCodeByGroupUin(long groupUin) {
return Group.Companion.calculateGroupCodeByGroupUin(groupUin);
}
}
\ No newline at end of file
package net.mamoe.mirai.japt;
import kotlin.text.StringsKt;
import net.mamoe.mirai.contact.MemberPermission;
import net.mamoe.mirai.contact.PermissionDeniedException;
import net.mamoe.mirai.event.events.MemberCardChangeEvent;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("unused")
public interface BlockingMember {
public interface BlockingMember extends BlockingQQ {
/**
* 所在的群
*/
......@@ -17,16 +20,72 @@ public interface BlockingMember {
@NotNull
MemberPermission getPermission();
/**
* 群名片. 可能为空.
*/
@NotNull
String getNameCard();
/**
* 修改群名片. 将会触发事件
*
* @throws PermissionDeniedException 无权限修改时
* @see #getGroupCardOrNick() 获取非空群名片或昵称
* @see MemberCardChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件
*/
void setNameCard(@NotNull String nameCard) throws PermissionDeniedException;
/**
* 获取群名片或昵称
*/
@NotNull
default String getGroupCardOrNick() {
String nameCard = this.getNameCard();
if (!StringsKt.isBlank(nameCard)) {
return nameCard;
}
return this.getNick();
}
/**
* 禁言
*
* @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
* @return 若机器人无权限禁言这个群成员, 返回 `false`
* @throws PermissionDeniedException 无权限修改时
*/
boolean mute(int durationSeconds);
void mute(int durationSeconds);
/**
* 禁言
*
* @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
* @throws PermissionDeniedException 无权限修改时
*/
default void mute(long durationSeconds) {
mute((int) durationSeconds);
}
/**
* 解除禁言
*
* @throws PermissionDeniedException 无权限修改时
*/
void unmute();
/**
* 踢出该成员.
*
* @param message 消息
* @throws PermissionDeniedException 无权限修改时
*/
void kick(@NotNull String message);
/**
* 踢出该成员.
*
* @throws PermissionDeniedException 无权限修改时
*/
boolean unmute();
default void kick() {
kick("");
}
}
......@@ -3,13 +3,30 @@ package net.mamoe.mirai.japt;
import net.mamoe.mirai.data.FriendNameRemark;
import net.mamoe.mirai.data.PreviousNameList;
import net.mamoe.mirai.data.Profile;
import net.mamoe.mirai.utils.MiraiExperimentalAPI;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("unused")
public interface BlockingQQ extends BlockingContact {
/**
* 获取 QQ 号码
*
* @return QQ 号码
*/
@Override
long getId();
/**
* 获取昵称
*
* @return 昵称
*/
String getNick();
/**
* 查询用户资料
*/
@MiraiExperimentalAPI(message = "还未支持")
@NotNull
Profile queryProfile();
......@@ -20,12 +37,14 @@ public interface BlockingQQ extends BlockingContact {
* - 昵称
* - 共同群内的群名片
*/
@MiraiExperimentalAPI(message = "还未支持")
@NotNull
PreviousNameList queryPreviousNameList();
/**
* 查询机器人账号给这个人设置的备注
*/
@MiraiExperimentalAPI(message = "还未支持")
@NotNull
FriendNameRemark queryRemark();
}
......@@ -20,18 +20,46 @@ import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* 事件处理
*/
public final class Events {
/**
* 监听一个事件, 当 {@code onEvent} 返回 {@link ListeningStatus#STOPPED} 时停止监听.
* 机器人离线后不会停止监听.
*
* @param eventClass 事件类
* @param onEvent 事件处理. 返回 {@link ListeningStatus#LISTENING} 时继续监听.
* @param <E> 事件类型
* @return 事件监听器. 可调用 {@link Listener#complete()} 或 {@link Listener#completeExceptionally(Throwable)} 让监听正常停止或异常停止.
*/
@NotNull
public static <E extends Event> Listener<E> subscribe(@NotNull Class<E> eventClass, @NotNull Function<E, ListeningStatus> onEvent) {
return EventInternalJvmKt._subscribeEventForJaptOnly(eventClass, GlobalScope.INSTANCE, onEvent);
}
/**
* 监听一个事件, 直到手动停止.
* 机器人离线后不会停止监听.
*
* @param eventClass 事件类
* @param onEvent 事件处理. 返回 {@link ListeningStatus#LISTENING} 时继续监听.
* @param <E> 事件类型
* @return 事件监听器. 可调用 {@link Listener#complete()} 或 {@link Listener#completeExceptionally(Throwable)} 让监听正常停止或异常停止.
*/
@NotNull
public static <E extends Event> Listener<E> subscribeAlways(@NotNull Class<E> eventClass, @NotNull Consumer<E> onEvent) {
return EventInternalJvmKt._subscribeEventForJaptOnly(eventClass, GlobalScope.INSTANCE, onEvent);
}
/**
* 阻塞地广播一个事件.
*
* @param event 事件
* @param <E> 事件类型
* @return {@code event} 本身
*/
@NotNull
public static <E extends Event> E broadcast(@NotNull E event) {
return EventsImplKt.broadcast(event);
......
......@@ -14,34 +14,55 @@ import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.readBytes
import net.mamoe.mirai.Bot
import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.data.GroupInfo
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.japt.BlockingBot
import net.mamoe.mirai.japt.BlockingGroup
import net.mamoe.mirai.japt.BlockingQQ
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.toList
import java.io.OutputStream
import java.util.stream.Stream
import kotlin.streams.asStream
internal class BlockingBotImpl(private val bot: Bot) : BlockingBot {
@MiraiInternalAPI
override fun getAccount(): BotAccount = bot.account
override fun getUin(): Long = bot.uin
@MiraiExperimentalAPI
override fun getNick(): String = bot.nick
override fun getLogger(): MiraiLogger = bot.logger
override fun getSelfQQ(): QQ = bot.selfQQ
override fun queryGroupMemberList(groupUin: Long, groupCode: Long, ownerId: Long): Stream<MemberInfo> =
runBlocking { bot.queryGroupMemberList(groupUin, groupCode, ownerId) }.asStream()
@UseExperimental(MiraiInternalAPI::class)
override fun getQQs(): List<BlockingQQ> = bot.qqs.delegate.toList().map { it.blocking() }
override fun getFriendList(): List<BlockingQQ> = bot.qqs.delegate.toList().map { it.blocking() }
override fun getFriend(id: Long): BlockingQQ = bot.getFriend(id).blocking()
override fun queryGroupList(): Stream<Long> = runBlocking { bot.queryGroupList() }.asStream()
override fun getQQ(id: Long): BlockingQQ = bot.getFriend(id).blocking()
@UseExperimental(MiraiInternalAPI::class)
override fun getGroups(): List<BlockingGroup> = bot.groups.delegate.toList().map { it.blocking() }
override fun getGroupList(): List<BlockingGroup> = bot.groups.delegate.toList().map { it.blocking() }
override fun queryGroupInfo(code: Long): GroupInfo = runBlocking { bot.queryGroupInfo(code) }
override fun getGroup(id: Long): BlockingGroup = runBlocking { bot.getGroup(id).blocking() }
override fun getNetwork(): BotNetworkHandler = bot.network
override fun login() = runBlocking { bot.login() }
override fun downloadAsByteArray(image: Image): ByteArray = bot.run { runBlocking { image.download().readBytes() } }
override fun download(image: Image): ByteReadPacket = bot.run { runBlocking { image.download() } }
override fun download(image: Image, outputStream: OutputStream) = bot.run { runBlocking { image.downloadTo(outputStream) } }
override fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult = runBlocking { bot.addFriend(id, message, remark) }
override fun approveFriendAddRequest(id: Long, remark: String?) = runBlocking { bot.approveFriendAddRequest(id, remark) }
override fun dispose(throwable: Throwable?) = bot.close(throwable)
......
......@@ -17,28 +17,36 @@ import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.data.FriendNameRemark
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.data.PreviousNameList
import net.mamoe.mirai.data.Profile
import net.mamoe.mirai.japt.BlockingBot
import net.mamoe.mirai.japt.BlockingGroup
import net.mamoe.mirai.japt.BlockingMember
import net.mamoe.mirai.japt.BlockingQQ
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.toChain
import net.mamoe.mirai.message.data.toMessage
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.toList
internal class BlockingQQImpl(private val delegate: QQ) : BlockingQQ {
override fun getBot(): BlockingBot = delegate.bot.blocking()
override fun getId(): Long = delegate.id
override fun getNick(): String = delegate.nick
override fun sendMessage(messages: MessageChain) = runBlocking { delegate.sendMessage(messages) }
override fun sendMessage(message: String) = runBlocking { delegate.sendMessage(message.toMessage().toChain()) }
override fun sendMessage(message: Message) = runBlocking { delegate.sendMessage(message.toChain()) }
override fun uploadImage(image: ExternalImage): Image = runBlocking { delegate.uploadImage(image) }
@MiraiExperimentalAPI
override fun queryProfile(): Profile = runBlocking { delegate.queryProfile() }
@MiraiExperimentalAPI
override fun queryPreviousNameList(): PreviousNameList = runBlocking { delegate.queryPreviousNameList() }
@MiraiExperimentalAPI
override fun queryRemark(): FriendNameRemark = runBlocking { delegate.queryRemark() }
}
......@@ -47,22 +55,76 @@ internal class BlockingGroupImpl(private val delegate: Group) : BlockingGroup {
override fun sendMessage(message: String) = runBlocking { delegate.sendMessage(message.toMessage().toChain()) }
override fun sendMessage(message: Message) = runBlocking { delegate.sendMessage(message.toChain()) }
override fun getOwner(): BlockingMember = delegate.owner.blocking()
@MiraiExperimentalAPI
override fun newMember(memberInfo: MemberInfo): Member = delegate.Member(memberInfo)
override fun uploadImage(image: ExternalImage): Image = runBlocking { delegate.uploadImage(image) }
override fun setEntranceAnnouncement(announcement: String) {
delegate.entranceAnnouncement = announcement
}
override fun getName(): String = delegate.name
override fun getId(): Long = delegate.id
@MiraiExperimentalAPI
override fun getBotPermission(): MemberPermission = delegate.botPermission
override fun setConfessTalk(enabled: Boolean) {
delegate.isConfessTalkEnabled = enabled
}
override fun isAnonymousChatEnabled(): Boolean = delegate.isAnonymousChatEnabled
override fun isAutoApproveEnabled(): Boolean = delegate.isAutoApproveEnabled
override fun isConfessTalkEnabled(): Boolean = delegate.isConfessTalkEnabled
override fun toFullString(): String = delegate.toFullString()
override fun containsMember(id: Long): Boolean = delegate.contains(id)
override fun isAllowMemberInvite(): Boolean = delegate.isAllowMemberInvite
override fun getMember(id: Long): BlockingMember = delegate[id].blocking()
override fun getBot(): BlockingBot = delegate.bot.blocking()
override fun getAnnouncement(): String = delegate.entranceAnnouncement
override fun getBotMuteRemaining(): Int = delegate.botMuteRemaining
override fun isMuteAll(): Boolean = delegate.isMuteAll
override fun setName(name: String) {
delegate.name = name
}
override fun setMuteAll(enabled: Boolean) {
delegate.isMuteAll = enabled
}
override fun getEntranceAnnouncement(): String = delegate.entranceAnnouncement
@UseExperimental(MiraiInternalAPI::class)
override fun getMembers(): Map<Long, BlockingMember> =
delegate.members.delegate.toList().associateBy { it.id }.mapValues { it.value.blocking() }
override fun getMembers(): List<BlockingMember> =
delegate.members.delegate.toList().map { it.blocking() }
override fun setAllowMemberInvite(allow: Boolean) {
delegate.isAllowMemberInvite = allow
}
override fun getMemberOrNull(id: Long): BlockingMember? {
return delegate.getOrNull(id)?.blocking()
}
override fun quit(): Boolean = runBlocking { delegate.quit() }
}
internal class BlockingMemberImpl(private val delegate: Member) : BlockingMember {
internal class BlockingMemberImpl(private val delegate: Member) : BlockingMember, BlockingQQ by (delegate as QQ).blocking() {
override fun getGroup(): BlockingGroup = delegate.group.blocking()
override fun getNameCard(): String = delegate.nameCard
override fun getPermission(): MemberPermission = delegate.permission
override fun mute(durationSeconds: Int): Boolean = runBlocking { delegate.mute(durationSeconds) }
override fun setNameCard(nameCard: String) {
delegate.nameCard = nameCard
}
override fun mute(durationSeconds: Int) = runBlocking { delegate.mute(durationSeconds) }
override fun unmute() = runBlocking { delegate.unmute() }
override fun kick(message: String) {
runBlocking { delegate.kick(message) }
}
}
\ No newline at end of file
public class BlockingTest {
public static void main(String[] args) {
//Bot bot = new Bot(new BotAccount(123456, ""), EmptyCoroutineContext.INSTANCE);
//if (bot.getNetwork().login() != LoginResult.) {
// throw IllegalStateException("Login failed")
//}
//bot.getContacts().getGroups();
//var qq = BlockingContacts.createBlocking(bot.getContacts().getQQ(123L))
//println(createBlocking.queryRemark())
}
}
......@@ -21,6 +21,8 @@ pluginManagement {
rootProject.name = 'mirai'
include(':mirai-demos')
try {
def keyProps = new Properties()
def keyFile = file("local.properties")
......@@ -46,7 +48,7 @@ include(':mirai-console')
include(':mirai-api-http')
include(':mirai-demos:mirai-demo-1')
include(':mirai-demos:mirai-demo-gentleman')
include(':mirai-demos')
include(':mirai-demos:mirai-demo-java')
include(':mirai-plugins')
include(':mirai-plugins:image-sender')
......@@ -67,6 +69,7 @@ if (versionPos==-1){
project(':mirai-demos:mirai-demo-1').projectDir = file('mirai-demos/mirai-demo-1')
project(':mirai-demos:mirai-demo-gentleman').projectDir = file('mirai-demos/mirai-demo-gentleman')
project(':mirai-demos:mirai-demo-java').projectDir = file('mirai-demos/mirai-demo-java')
project(':mirai-plugins:image-sender').projectDir = file('mirai-plugins/image-sender')
enableFeaturePreview('GRADLE_METADATA')
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment