Merge pull request #29 from mamoe/japt

Merge with Japt
This commit is contained in:
Him188 2020-01-11 10:04:23 -06:00 committed by GitHub
commit 08851e8794
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 361 additions and 15 deletions

View File

@ -39,7 +39,7 @@ interface Contact : CoroutineScope {
suspend fun uploadImage(image: ExternalImage): ImageId suspend fun uploadImage(image: ExternalImage): ImageId
} }
suspend inline fun Contact.sendMessage(message: Message) = sendMessage(message.chain()) suspend inline fun Contact.sendMessage(message: Message) = sendMessage(message.toChain())
suspend inline fun Contact.sendMessage(plain: String) = sendMessage(plain.singleChain()) suspend inline fun Contact.sendMessage(plain: String) = sendMessage(plain.singleChain())

View File

@ -61,8 +61,6 @@ interface Group : Contact, CoroutineScope/*, Map<UInt, Member>*/ { // TODO: 2019
/** /**
* 让机器人退出这个群. 机器人必须为非群主才能退出. 否则将会失败 * 让机器人退出这个群. 机器人必须为非群主才能退出. 否则将会失败
*
* @see QuitGroupResponse.isSuccess 判断是否成功
*/ */
suspend fun quit(): Boolean suspend fun quit(): Boolean

View File

@ -10,7 +10,6 @@ import net.mamoe.mirai.data.ImageLink
import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.coerceAtLeastOrFail
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
/** /**
@ -50,7 +49,7 @@ abstract class MessagePacketBase<TSender : QQ, TSubject : Contact>(_bot: Bot) :
*/ */
suspend inline fun reply(message: MessageChain) = subject.sendMessage(message) suspend inline fun reply(message: MessageChain) = subject.sendMessage(message)
suspend inline fun reply(message: Message) = subject.sendMessage(message.chain()) suspend inline fun reply(message: Message) = subject.sendMessage(message.toChain())
suspend inline fun reply(plain: String) = subject.sendMessage(plain.toMessage()) suspend inline fun reply(plain: String) = subject.sendMessage(plain.toMessage())
@JvmName("reply1") @JvmName("reply1")

View File

@ -63,7 +63,7 @@ fun MessageChain(initialCapacity: Int): MessageChain =
/** /**
* 构造 [MessageChain] * 构造 [MessageChain]
* 若仅提供一个参数, 请考虑使用 [Message.chain] 以优化性能 * 若仅提供一个参数, 请考虑使用 [Message.toChain] 以优化性能
*/ */
@Suppress("FunctionName") @Suppress("FunctionName")
fun MessageChain(vararg messages: Message): MessageChain = fun MessageChain(vararg messages: Message): MessageChain =
@ -81,12 +81,12 @@ fun MessageChain(messages: Iterable<Message>): MessageChain =
* 构造单元素的不可修改的 [MessageChain]. 内部类实现为 [SingleMessageChain] * 构造单元素的不可修改的 [MessageChain]. 内部类实现为 [SingleMessageChain]
* *
* 参数 [delegate] 不能为 [MessageChain] 的实例, 否则将会抛出异常. * 参数 [delegate] 不能为 [MessageChain] 的实例, 否则将会抛出异常.
* 使用 [Message.chain] 将帮助提前处理这个问题. * 使用 [Message.toChain] 将帮助提前处理这个问题.
* *
* @param delegate 所构造的单元素 [MessageChain] 代表的 [Message] * @param delegate 所构造的单元素 [MessageChain] 代表的 [Message]
* @throws IllegalArgumentException [delegate] [MessageChain] 的实例时 * @throws IllegalArgumentException [delegate] [MessageChain] 的实例时
* *
* @see Message.chain receiver 模式 * @see Message.toChain receiver 模式
*/ */
@MiraiExperimentalAPI @MiraiExperimentalAPI
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@ -106,17 +106,13 @@ fun SingleMessageChain(delegate: Message): MessageChain {
* 否则将调用 [MessageChain] 构造一个 [MessageChainImpl] * 否则将调用 [MessageChain] 构造一个 [MessageChainImpl]
*/ */
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun Message.chain(): MessageChain = if (this is MessageChain) this else MessageChain( inline fun Message.toChain(): MessageChain = if (this is MessageChain) this else MessageChain(this)
this
)
/** /**
* 构造 [MessageChain] * 构造 [MessageChain]
*/ */
@Suppress("unused", "NOTHING_TO_INLINE") @Suppress("unused", "NOTHING_TO_INLINE")
inline fun List<Message>.messageChain(): MessageChain = inline fun List<Message>.toMessageChain(): MessageChain = MessageChain(this)
MessageChain(this)
/** /**
* 获取第一个 [M] 类型的 [Message] 实例 * 获取第一个 [M] 类型的 [Message] 实例
@ -327,6 +323,7 @@ internal inline class MessageChainImpl constructor(
* *
* 在连接时将会把它当做一个普通 [Message] 看待, 但它不能被 [plusAssign] * 在连接时将会把它当做一个普通 [Message] 看待, 但它不能被 [plusAssign]
*/ */
@PublishedApi
internal inline class SingleMessageChainImpl( internal inline class SingleMessageChainImpl(
private val delegate: Message private val delegate: Message
) : Message, MutableList<Message>, ) : Message, MutableList<Message>,

View File

@ -23,4 +23,4 @@ inline fun String.toMessage(): PlainText = PlainText(this)
* @see SingleMessageChainImpl * @see SingleMessageChainImpl
*/ */
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun String.singleChain(): MessageChain = this.toMessage().chain() inline fun String.singleChain(): MessageChain = SingleMessageChainImpl(this.toMessage())

9
mirai-japt/README.md Normal file
View File

@ -0,0 +1,9 @@
# mirai-japt
Mirai Java Apt
提供一些阻塞/异步/RxJava API 来让 Java 调用 Mirai 的挂起函数 API 更容易
提供 Utils 类来让 Java 调用 Mirai 的内联方法更容易
该模块暂未完成.

View File

@ -0,0 +1,44 @@
plugins {
kotlin("jvm")
java
}
val kotlinVersion: String by rootProject.ext
val atomicFuVersion: String by rootProject.ext
val coroutinesVersion: String by rootProject.ext
val kotlinXIoVersion: String by rootProject.ext
val coroutinesIoVersion: String by rootProject.ext
val serializationVersion: String by rootProject.ext
val klockVersion: String by rootProject.ext
val ktorVersion: String by rootProject.ext
kotlin {
sourceSets {
all {
languageSettings.enableLanguageFeature("InlineClasses")
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
}
}
}
fun kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$version"
fun ktor(id: String, version: String) = "io.ktor:ktor-$id:$version"
dependencies {
api(project(":mirai-core"))
runtimeOnly(files("../mirai-core/build/classes/kotlin/jvm/main")) // classpath is not added correctly by IDE
api(group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-javafx", version = "1.3.2")
api(kotlin("stdlib", kotlinVersion))
api(kotlinx("io-jvm", kotlinXIoVersion))
api(kotlinx("io", kotlinXIoVersion))
api(kotlinx("coroutines-io", coroutinesIoVersion))
api(kotlinx("coroutines-core", coroutinesVersion))
}
tasks.withType<JavaCompile>() {
options.encoding = "UTF-8"
}

View File

@ -0,0 +1,5 @@
## Contact ##
.blocking : Blocking wrap
net.mamoe.mirai.contact.QQ → net.mamoe.mirai.japt.BlockingContacts.createBlocking($expr$)
net.mamoe.mirai.contact.QQ [net.mamoe.mirai.japt.BlockingContacts] → net.mamoe.mirai.japt.BlockingContacts.createBlocking($expr$)
net.mamoe.mirai.timpc.network.QQImpl [net.mamoe.mirai.japt.BlockingContacts] → net.mamoe.mirai.japt.BlockingContacts.createBlocking($expr$)

View File

@ -0,0 +1,4 @@
package net.mamoe.mirai.japt;
public interface BlockingBot {
}

View File

@ -0,0 +1,35 @@
package net.mamoe.mirai.japt;
import net.mamoe.mirai.Bot;
import net.mamoe.mirai.message.data.Message;
import net.mamoe.mirai.message.data.MessageChain;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("unused")
public interface BlockingContact {
/**
* 这个联系人所属 [Bot]
*/
@NotNull
Bot getBot();
/**
* 可以是 QQ 号码或者群号码 [GroupId].
*/
long getId();
/**
* 向这个对象发送消息.
*/
void sendMessage(@NotNull MessageChain messages);
/**
* 向这个对象发送消息.
*/
void sendMessage(@NotNull String message);
/**
* 向这个对象发送消息.
*/
void sendMessage(@NotNull Message message);
}

View File

@ -0,0 +1,22 @@
package net.mamoe.mirai.japt;
import net.mamoe.mirai.contact.Group;
import net.mamoe.mirai.contact.Member;
import net.mamoe.mirai.contact.QQ;
/**
* 构造阻塞式的联系人.
*/
public final class BlockingContacts {
public static BlockingQQ createBlocking(QQ qq) {
return new BlockingQQImpl(qq);
}
public static BlockingGroup createBlocking(Group group) {
return new BlockingGroupImpl(group);
}
public static BlockingMember createBlocking(Member member) {
return new BlockingMemberImpl(member);
}
}

View File

@ -0,0 +1,57 @@
package net.mamoe.mirai.japt;
import net.mamoe.mirai.data.GroupInfo;
import java.util.Map;
@SuppressWarnings("unused")
public interface BlockingGroup extends BlockingContact {
/**
* 内部 ID. 内部 ID [GroupId] 的映射
*/
long getInternalId();
/**
* 群主 (同步事件更新)
* 进行 [updateGroupInfo] 时将会更新这个值.
*/
BlockingMember getOwner();
/**
* 群名称 (同步事件更新)
* 进行 [updateGroupInfo] 时将会更新这个值.
*/
String getName();
/**
* 入群公告, 没有时为空字符串. (同步事件更新)
* 进行 [updateGroupInfo] 时将会更新这个值.
*/
String getAnnouncement();
/**
* [Group] 实例创建的时候查询一次. 并与事件同步事件更新
* <p>
* **注意**: 获得的列表仅为这一时刻的成员列表的镜像. 它将不会被更新
*/
Map<Long, BlockingMember> getMembers();
/**
* 获取群成员. 若此 ID 的成员不存在, 则会抛出 [kotlin.NoSuchElementException]
*/
BlockingMember getMember(long id);
/**
* 更新群资料. 群资料会与服务器事件同步事件更新, 一般情况下不需要手动更新.
*
* @return 这一时刻的群资料
*/
GroupInfo updateGroupInfo();
/**
* 让机器人退出这个群. 机器人必须为非群主才能退出. 否则将会失败
*/
boolean quit();
String toFullString();
}

View File

@ -0,0 +1,29 @@
package net.mamoe.mirai.japt;
import net.mamoe.mirai.contact.MemberPermission;
@SuppressWarnings("unused")
public interface BlockingMember {
/**
* 所在的群
*/
BlockingGroup getGroup();
/**
* 权限
*/
MemberPermission getPermission();
/**
* 禁言
*
* @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
* @return 若机器人无权限禁言这个群成员, 返回 `false`
*/
Boolean mute(int durationSeconds);
/**
* 解除禁言
*/
void unmute();
}

View File

@ -0,0 +1,31 @@
package net.mamoe.mirai.japt;
import net.mamoe.mirai.data.FriendNameRemark;
import net.mamoe.mirai.data.PreviousNameList;
import net.mamoe.mirai.data.Profile;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("unused")
public interface BlockingQQ extends BlockingContact {
/**
* 查询用户资料
*/
@NotNull
Profile queryProfile();
/**
* 查询曾用名.
* <p>
* 曾用名可能是:
* - 昵称
* - 共同群内的群名片
*/
@NotNull
PreviousNameList queryPreviousNameList();
/**
* 查询机器人账号给这个人设置的备注
*/
@NotNull
FriendNameRemark queryRemark();
}

View File

@ -0,0 +1,11 @@
@file:Suppress("NOTHING_TO_INLINE", "unused")
package net.mamoe.mirai.japt
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.QQ
inline fun Group.blocking(): BlockingGroup = BlockingContacts.createBlocking(this)
inline fun QQ.blocking(): BlockingQQ = BlockingContacts.createBlocking(this)
inline fun Member.blocking(): BlockingMember = BlockingContacts.createBlocking(this)

View File

@ -0,0 +1,59 @@
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.japt
import kotlinx.coroutines.runBlocking
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.contact.QQ
import net.mamoe.mirai.data.FriendNameRemark
import net.mamoe.mirai.data.GroupInfo
import net.mamoe.mirai.data.PreviousNameList
import net.mamoe.mirai.data.Profile
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.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.toList
internal class BlockingQQImpl(private val delegate: QQ) : BlockingQQ {
override fun getBot(): Bot = delegate.bot
override fun getId(): Long = delegate.id
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 queryProfile(): Profile = runBlocking { delegate.queryProfile() }
override fun queryPreviousNameList(): PreviousNameList = runBlocking { delegate.queryPreviousNameList() }
override fun queryRemark(): FriendNameRemark = runBlocking { delegate.queryRemark() }
}
internal class BlockingGroupImpl(private val delegate: Group) : BlockingGroup {
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 getOwner(): BlockingMember = delegate.owner.blocking()
override fun getName(): String = delegate.name
override fun getId(): Long = delegate.id
override fun updateGroupInfo(): GroupInfo = runBlocking { delegate.updateGroupInfo() }
override fun toFullString(): String = delegate.toFullString()
override fun getMember(id: Long): BlockingMember = delegate.getMember(id).blocking()
override fun getBot(): Bot = delegate.bot
override fun getAnnouncement(): String = delegate.announcement
@UseExperimental(MiraiInternalAPI::class)
override fun getMembers(): Map<Long, BlockingMember> =
delegate.members.delegate.toList().associateBy { it.id }.mapValues { it.value.blocking() }
override fun getInternalId(): Long = delegate.internalId.value
override fun quit(): Boolean = runBlocking { delegate.quit() }
}
internal class BlockingMemberImpl(private val delegate: Member) : BlockingMember {
override fun getGroup(): BlockingGroup = delegate.group.blocking()
override fun getPermission(): MemberPermission = delegate.permission
override fun mute(durationSeconds: Int): Boolean = runBlocking { delegate.mute(durationSeconds) }
override fun unmute() = runBlocking { delegate.unmute() }
}

View File

@ -0,0 +1,14 @@
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())
}
}

View File

@ -0,0 +1,31 @@
import net.mamoe.mirai.Bot
import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.japt.BlockingContacts
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import java.io.File
@ExperimentalUnsignedTypes
private fun readTestAccount(): BotAccount? {
val file = File("testAccount.txt")
if (!file.exists() || !file.canRead()) {
return null
}
val lines = file.readLines()
return try {
BotAccount(lines[0].toUInt(), lines[1])
} catch (e: Exception) {
null
}
}
@ExperimentalUnsignedTypes
suspend fun main() {
val bot = Bot(readTestAccount()!!)
if (bot.network.login() != LoginResult.SUCCESS) {
throw IllegalStateException("Login failed")
}
val createBlocking = BlockingContacts.createBlocking(bot.contacts.getQQ(123L))
println(createBlocking.queryRemark())
}

View File

@ -36,6 +36,7 @@ include(':mirai-core')
include(':mirai-core-timpc') include(':mirai-core-timpc')
include(':mirai-core-qqandroid') include(':mirai-core-qqandroid')
include(':mirai-japt')
include(':mirai-console') include(':mirai-console')
//include(':mirai-api') //include(':mirai-api')
include(':mirai-api-http') include(':mirai-api-http')