Commit d047a363 authored by liujiahua123123's avatar liujiahua123123

Merge remote-tracking branch 'origin/master'

parents aade2e51 7cd1bc73
...@@ -4,6 +4,19 @@ ...@@ -4,6 +4,19 @@
开发版本. 频繁更新, 不保证高稳定性 开发版本. 频繁更新, 不保证高稳定性
### `0.8.0` *2019/12/14*
协议
- 现在查询群资料时可处理群号无效的情况
- 现在能正常分辨禁言事件包
功能
- 增加无锁链表: LockFreeLinkedList, 并将 ContactList 的实现改为该无锁链表
- **ContactSystem.getQQ 不再是 `suspend`**
- ContactSystem.getGroup 仍是 `suspend`, 原因为需要查询群资料. 在群 ID 无效时抛出 `GroupNotFoundException`
优化
- 日志中, 发送给服务器的包将会被以名字记录, 而不是 id
### `0.7.5` *2019/12/09* ### `0.7.5` *2019/12/09*
- 修复验证码包发出后无回复 (错误的验证码包) - 修复验证码包发出后无回复 (错误的验证码包)
......
# style guide # style guide
kotlin.code.style=official kotlin.code.style=official
# config # config
mirai_version=0.7.5 mirai_version=0.8.0
kotlin.incremental.multiplatform=true kotlin.incremental.multiplatform=true
kotlin.parallel.tasks.in.project=true kotlin.parallel.tasks.in.project=true
# kotlin # kotlin
......
...@@ -20,7 +20,6 @@ import net.mamoe.mirai.Bot ...@@ -20,7 +20,6 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.addFriend import net.mamoe.mirai.addFriend
import net.mamoe.mirai.contact.sendMessage import net.mamoe.mirai.contact.sendMessage
import net.mamoe.mirai.getGroup import net.mamoe.mirai.getGroup
import net.mamoe.mirai.getQQ
import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.hexToUBytes import net.mamoe.mirai.utils.io.hexToUBytes
...@@ -81,6 +80,7 @@ suspend inline fun ApplicationCall.ok() = this.respond(HttpStatusCode.OK, "OK") ...@@ -81,6 +80,7 @@ suspend inline fun ApplicationCall.ok() = this.respond(HttpStatusCode.OK, "OK")
/** /**
* 错误请求. 抛出这个异常后将会返回错误给一个请求 * 错误请求. 抛出这个异常后将会返回错误给一个请求
*/ */
@Suppress("unused")
open class IllegalAccessException : Exception { open class IllegalAccessException : Exception {
override val message: String get() = super.message!! override val message: String get() = super.message!!
...@@ -107,14 +107,14 @@ private inline fun <reified R> PipelineContext<Unit, ApplicationCall>.param(name ...@@ -107,14 +107,14 @@ private inline fun <reified R> PipelineContext<Unit, ApplicationCall>.param(name
@Suppress("IMPLICIT_CAST_TO_ANY") @Suppress("IMPLICIT_CAST_TO_ANY")
@UseExperimental(ExperimentalUnsignedTypes::class) @UseExperimental(ExperimentalUnsignedTypes::class)
private inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNull(name: String): R? = private inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNull(name: String): R? =
when { when (R::class) {
R::class == Byte::class -> call.parameters[name]?.toByte() Byte::class -> call.parameters[name]?.toByte()
R::class == Int::class -> call.parameters[name]?.toInt() Int::class -> call.parameters[name]?.toInt()
R::class == Short::class -> call.parameters[name]?.toShort() Short::class -> call.parameters[name]?.toShort()
R::class == Float::class -> call.parameters[name]?.toFloat() Float::class -> call.parameters[name]?.toFloat()
R::class == Long::class -> call.parameters[name]?.toLong() Long::class -> call.parameters[name]?.toLong()
R::class == Double::class -> call.parameters[name]?.toDouble() Double::class -> call.parameters[name]?.toDouble()
R::class == Boolean::class -> when (call.parameters[name]) { Boolean::class -> when (call.parameters[name]) {
"true" -> true "true" -> true
"false" -> false "false" -> false
"0" -> false "0" -> false
...@@ -123,13 +123,13 @@ private inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNul ...@@ -123,13 +123,13 @@ private inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNul
else -> illegalParam("boolean", name) else -> illegalParam("boolean", name)
} }
R::class == String::class -> call.parameters[name] String::class -> call.parameters[name]
R::class == UByte::class -> call.parameters[name]?.toUByte() UByte::class -> call.parameters[name]?.toUByte()
R::class == UInt::class -> call.parameters[name]?.toUInt() UInt::class -> call.parameters[name]?.toUInt()
R::class == UShort::class -> call.parameters[name]?.toUShort() UShort::class -> call.parameters[name]?.toUShort()
R::class == UByteArray::class -> call.parameters[name]?.hexToUBytes() UByteArray::class -> call.parameters[name]?.hexToUBytes()
R::class == ByteArray::class -> call.parameters[name]?.hexToBytes() ByteArray::class -> call.parameters[name]?.hexToBytes()
else -> error(name::class.simpleName + " is not supported") else -> error(name::class.simpleName + " is not supported")
} as R? } as R?
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai
data class BotAccount(
val id: UInt,
val password: String
) {
constructor(id: Long, password: String) : this(id.toUInt(), password)
}
\ No newline at end of file
...@@ -68,15 +68,15 @@ Mirai 22:04:48 : Packet received: UnknownEventPacket(id=00 D6, identity=(2092749 ...@@ -68,15 +68,15 @@ Mirai 22:04:48 : Packet received: UnknownEventPacket(id=00 D6, identity=(2092749
* @param remark 好友备注 * @param remark 好友备注
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
suspend fun Bot.ContactSystem.addFriend(id: UInt, message: String? = null, remark: String? = null): AddFriendResult = bot.withSession { suspend fun Bot.addFriend(id: UInt, message: String? = null, remark: String? = null): AddFriendResult = withSession {
return when (CanAddFriendPacket(bot.qqAccount, id, bot.sessionKey).sendAndExpect<CanAddFriendResponse>()) { return when (CanAddFriendPacket(qqAccount, id, sessionKey).sendAndExpect<CanAddFriendResponse>()) {
is CanAddFriendResponse.AlreadyAdded -> AddFriendResult.ALREADY_ADDED is CanAddFriendResponse.AlreadyAdded -> AddFriendResult.ALREADY_ADDED
is CanAddFriendResponse.Rejected -> AddFriendResult.REJECTED is CanAddFriendResponse.Rejected -> AddFriendResult.REJECTED
is CanAddFriendResponse.ReadyToAdd, is CanAddFriendResponse.ReadyToAdd,
is CanAddFriendResponse.RequireVerification -> { is CanAddFriendResponse.RequireVerification -> {
val key = RequestFriendAdditionKeyPacket(bot.qqAccount, id, sessionKey).sendAndExpect<RequestFriendAdditionKeyPacket.Response>().key val key = RequestFriendAdditionKeyPacket(qqAccount, id, sessionKey).sendAndExpect<RequestFriendAdditionKeyPacket.Response>().key
AddFriendPacket.RequestAdd(bot.qqAccount, id, sessionKey, message, remark, key).sendAndExpect<AddFriendPacket.Response>() AddFriendPacket.RequestAdd(qqAccount, id, sessionKey, message, remark, key).sendAndExpect<AddFriendPacket.Response>()
AddFriendResult.WAITING_FOR_APPROVE AddFriendResult.WAITING_FOR_APPROVE
} //这个做的是需要验证消息的情况, 不确定 ReadyToAdd 的是啥 } //这个做的是需要验证消息的情况, 不确定 ReadyToAdd 的是啥
...@@ -87,7 +87,7 @@ suspend fun Bot.ContactSystem.addFriend(id: UInt, message: String? = null, remar ...@@ -87,7 +87,7 @@ suspend fun Bot.ContactSystem.addFriend(id: UInt, message: String? = null, remar
/*is CanAddFriendResponse.ReadyToAdd -> { /*is CanAddFriendResponse.ReadyToAdd -> {
// TODO: 2019/11/11 这不需要验证信息的情况 // TODO: 2019/11/11 这不需要验证信息的情况
//AddFriendPacket(bot.qqAccount, id, bot.sessionKey, ).sendAndExpectAsync<AddFriendPacket.Response>().await() //AddFriendPacket(qqAccount, id, sessionKey, ).sendAndExpectAsync<AddFriendPacket.Response>().await()
TODO() TODO()
}*/ }*/
} }
......
@file:JvmMultifileClass @file:JvmMultifileClass
@file:JvmName("BotHelperKt") @file:JvmName("BotHelperKt")
@file:Suppress("unused", "EXPERIMENTAL_API_USAGE") @file:Suppress("unused", "EXPERIMENTAL_API_USAGE", "NOTHING_TO_INLINE")
package net.mamoe.mirai package net.mamoe.mirai
...@@ -26,26 +26,8 @@ import kotlin.jvm.JvmOverloads ...@@ -26,26 +26,8 @@ import kotlin.jvm.JvmOverloads
*/ */
//Contacts //Contacts
suspend inline fun Bot.getQQ(@PositiveNumbers number: Long): QQ = this.contacts.getQQ(number) suspend inline fun Bot.getGroup(id: UInt): Group = this.getGroup(GroupId(id))
suspend inline fun Bot.getQQ(number: UInt): QQ = this.contacts.getQQ(number)
suspend inline fun Bot.getGroup(id: UInt): Group = this.contacts.getGroup(GroupId(id))
suspend inline fun Bot.getGroup(@PositiveNumbers id: Long): Group =
this.contacts.getGroup(GroupId(id.coerceAtLeastOrFail(0).toUInt()))
suspend inline fun Bot.getGroup(id: GroupId): Group = this.contacts.getGroup(id)
suspend inline fun Bot.getGroup(internalId: GroupInternalId): Group = this.contacts.getGroup(internalId)
/**
* 取得群列表
*/
inline val Bot.groups: ContactList<Group> get() = this.contacts.groups
/**
* 取得好友列表
*/
inline val Bot.qqs: ContactList<QQ> get() = this.contacts.qqs
/** /**
* 以 [BotSession] 作为接收器 (receiver) 并调用 [block], 返回 [block] 的返回值. * 以 [BotSession] 作为接收器 (receiver) 并调用 [block], 返回 [block] 的返回值.
...@@ -70,7 +52,7 @@ internal suspend inline fun Bot.sendPacket(packet: OutgoingPacket) = ...@@ -70,7 +52,7 @@ internal suspend inline fun Bot.sendPacket(packet: OutgoingPacket) =
* 使用在默认配置基础上修改的配置进行登录 * 使用在默认配置基础上修改的配置进行登录
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
suspend inline fun Bot.login(noinline configuration: BotConfiguration.() -> Unit): LoginResult { suspend inline fun Bot.login(configuration: BotConfiguration.() -> Unit): LoginResult {
contract { contract {
callsInPlace(configuration, InvocationKind.EXACTLY_ONCE) callsInPlace(configuration, InvocationKind.EXACTLY_ONCE)
} }
...@@ -91,7 +73,7 @@ suspend inline fun Bot.alsoLogin(): Bot = apply { login().requireSuccess() } ...@@ -91,7 +73,7 @@ suspend inline fun Bot.alsoLogin(): Bot = apply { login().requireSuccess() }
* 使用在默认配置基础上修改的配置进行登录, 返回 [this] * 使用在默认配置基础上修改的配置进行登录, 返回 [this]
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
suspend inline fun Bot.alsoLogin(noinline configuration: BotConfiguration.() -> Unit): Bot { suspend inline fun Bot.alsoLogin(configuration: BotConfiguration.() -> Unit): Bot {
contract { contract {
callsInPlace(configuration, InvocationKind.EXACTLY_ONCE) callsInPlace(configuration, InvocationKind.EXACTLY_ONCE)
} }
...@@ -108,14 +90,6 @@ suspend inline fun Bot.alsoLogin(message: String): Bot { ...@@ -108,14 +90,6 @@ suspend inline fun Bot.alsoLogin(message: String): Bot {
} }
} }
/**
* 添加好友
*/
@UseExperimental(ExperimentalContracts::class)
@JvmOverloads
suspend inline fun Bot.addFriend(id: UInt, message: String? = null, remark: String? = null): AddFriendResult =
contacts.addFriend(id, message, remark)
/** /**
* 取得机器人的 QQ 号 * 取得机器人的 QQ 号
*/ */
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai
import kotlinx.coroutines.*
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.contact.internal.Group
import net.mamoe.mirai.contact.internal.QQ
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.KnownPacketId
import net.mamoe.mirai.network.protocol.tim.packet.action.GroupNotFound
import net.mamoe.mirai.network.protocol.tim.packet.action.GroupPacket
import net.mamoe.mirai.network.protocol.tim.packet.action.RawGroupInfo
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.network.protocol.tim.packet.login.isSuccess
import net.mamoe.mirai.network.qqAccount
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.internal.PositiveNumbers
import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
import net.mamoe.mirai.utils.io.inline
import net.mamoe.mirai.utils.io.logStacktrace
import kotlin.coroutines.CoroutineContext
import kotlin.jvm.JvmSynthetic
@PublishedApi
internal class BotImpl @PublishedApi internal constructor(
override val account: BotAccount,
override val logger: MiraiLogger = DefaultLogger("Bot(" + account.id + ")"),
context: CoroutineContext
) : Bot, CoroutineScope {
private val supervisorJob = SupervisorJob(context[Job])
override val coroutineContext: CoroutineContext =
context + supervisorJob + CoroutineExceptionHandler { _, e -> e.logStacktrace("An exception was thrown under a coroutine of Bot") }
init {
launch {
instances.addLast(this@BotImpl)
}
}
companion object {
init {
KnownPacketId.values() /* load id classes */
}
@PublishedApi
internal val instances: LockFreeLinkedList<Bot> = LockFreeLinkedList()
inline fun forEachInstance(block: (Bot) -> Unit) = instances.forEach(block)
fun instanceWhose(qq: UInt): Bot {
instances.forEach {
if (it.qqAccount == qq) {
return it
}
}
throw NoSuchElementException()
}
}
override fun toString(): String = "Bot(${account.id})"
// region network
override val network: BotNetworkHandler<*> get() = _network
private lateinit var _network: BotNetworkHandler<*>
override fun tryReinitializeNetworkHandler(// shouldn't be suspend!! This function MUST NOT inherit the context from the caller because the caller(NetworkHandler) is going to close
configuration: BotConfiguration,
cause: Throwable?
): Job = launch {
repeat(configuration.reconnectionRetryTimes) {
if (reinitializeNetworkHandlerAsync(configuration, cause).await().isSuccess()) {
logger.info("Reconnected successfully")
return@launch
} else {
delay(configuration.reconnectPeriodMillis)
}
}
}
override suspend fun reinitializeNetworkHandler(
configuration: BotConfiguration,
cause: Throwable?
): LoginResult {
logger.info("BotAccount: ${qqAccount.toLong()}")
logger.info("Initializing BotNetworkHandler")
try {
if (::_network.isInitialized) {
_network.close(cause)
}
} catch (e: Exception) {
logger.error("Cannot close network handler", e)
}
_network = TIMBotNetworkHandler(this.coroutineContext + configuration, this)
return _network.login()
}
override fun reinitializeNetworkHandlerAsync(
configuration: BotConfiguration,
cause: Throwable?
): Deferred<LoginResult> = async { reinitializeNetworkHandler(configuration, cause) }
// endregion
// region contacts
override val groups: ContactList<Group> = ContactList(LockFreeLinkedList())
override val qqs: ContactList<QQ> = ContactList(LockFreeLinkedList())
/**
* 线程安全地获取缓存的 QQ 对象. 若没有对应的缓存, 则会创建一个.
*/
@UseExperimental(MiraiInternalAPI::class)
@JvmSynthetic
override fun getQQ(id: UInt): QQ = qqs.delegate.getOrAdd(id) { QQ(this, id, coroutineContext) }
// NO INLINE!! to help Java
@UseExperimental(MiraiInternalAPI::class)
override fun getQQ(@PositiveNumbers id: Long): QQ = getQQ(id.coerceAtLeastOrFail(0).toUInt())
override suspend fun getGroup(internalId: GroupInternalId): Group = getGroup(internalId.toId())
@UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class)
override suspend fun getGroup(id: GroupId): Group = groups.delegate.getOrNull(id.value) ?: inline {
val info: RawGroupInfo = try {
when (val response =
withSession { GroupPacket.QueryGroupInfo(qqAccount, id.toInternalId(), sessionKey).sendAndExpect<GroupPacket.InfoResponse>() }) {
is RawGroupInfo -> response
is GroupNotFound -> throw GroupNotFoundException("id=${id.value.toLong()}")
else -> assertUnreachable()
}
} catch (e: Exception) {
throw IllegalStateException("Cannot obtain group info for id ${id.value.toLong()}", e)
}
return groups.delegate.getOrAdd(id.value) { Group(this, id, info, coroutineContext) }
}
// NO INLINE!! to help Java
@UseExperimental(MiraiInternalAPI::class)
override suspend fun getGroup(@PositiveNumbers id: Long): Group = id.coerceAtLeastOrFail(0).toUInt().let {
groups.delegate.getOrNull(it) ?: inline {
val info: RawGroupInfo = try {
withSession { GroupPacket.QueryGroupInfo(qqAccount, GroupId(it).toInternalId(), sessionKey).sendAndExpect() }
} catch (e: Exception) {
e.logStacktrace()
error("Cannot obtain group info for id ${it.toLong()}")
}
return groups.delegate.getOrAdd(it) { Group(this, GroupId(it), info, coroutineContext) }
}
}
// endregion
@UseExperimental(MiraiInternalAPI::class)
override fun close() {
_network.close()
this.supervisorJob.complete()
groups.delegate.clear()
qqs.delegate.clear()
}
}
...@@ -8,7 +8,9 @@ import net.mamoe.mirai.message.MessageChain ...@@ -8,7 +8,9 @@ import net.mamoe.mirai.message.MessageChain
import net.mamoe.mirai.message.chain import net.mamoe.mirai.message.chain
import net.mamoe.mirai.message.singleChain import net.mamoe.mirai.message.singleChain
import net.mamoe.mirai.network.BotSession import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.utils.LockFreeLinkedList
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.joinToString
import net.mamoe.mirai.withSession import net.mamoe.mirai.withSession
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
...@@ -54,55 +56,3 @@ inline fun <R> Contact.withSession(block: BotSession.() -> R): R { ...@@ -54,55 +56,3 @@ inline fun <R> Contact.withSession(block: BotSession.() -> R): R {
} }
return bot.withSession(block) return bot.withSession(block)
} }
/**
* 只读联系人列表
*/
@UseExperimental(MiraiInternalAPI::class)
inline class ContactList<C : Contact>(internal val mutable: MutableContactList<C>) : Map<UInt, C> {
/**
* ID 列表的字符串表示.
* 如:
* ```
* [123456, 321654, 123654]
* ```
*/
val idContentString: String get() = this.keys.joinToString(prefix = "[", postfix = "]") { it.toLong().toString() }
override fun toString(): String = mutable.toString()
// TODO: 2019/12/2 应该使用属性代理, 但属性代理会导致 UInt 内联错误. 等待 kotlin 修复后替换
override val size: Int get() = mutable.size
override fun containsKey(key: UInt): Boolean = mutable.containsKey(key)
override fun containsValue(value: C): Boolean = mutable.containsValue(value)
override fun get(key: UInt): C? = mutable[key]
override fun isEmpty(): Boolean = mutable.isEmpty()
override val entries: MutableSet<MutableMap.MutableEntry<UInt, C>> get() = mutable.entries
override val keys: MutableSet<UInt> get() = mutable.keys
override val values: MutableCollection<C> get() = mutable.values
}
/**
* 可修改联系人列表. 只会在内部使用.
*/
@MiraiInternalAPI
inline class MutableContactList<C : Contact>(private val delegate: MutableMap<UInt, C> = linkedMapOf()) : MutableMap<UInt, C> {
override fun toString(): String = asIterable().joinToString(separator = ", ", prefix = "ContactList(", postfix = ")") { it.value.toString() }
// TODO: 2019/12/2 应该使用属性代理, 但属性代理会导致 UInt 内联错误. 等待 kotlin 修复后替换
override val size: Int get() = delegate.size
override fun containsKey(key: UInt): Boolean = delegate.containsKey(key)
override fun containsValue(value: C): Boolean = delegate.containsValue(value)
override fun get(key: UInt): C? = delegate[key]
override fun isEmpty(): Boolean = delegate.isEmpty()
override val entries: MutableSet<MutableMap.MutableEntry<UInt, C>> get() = delegate.entries
override val keys: MutableSet<UInt> get() = delegate.keys
override val values: MutableCollection<C> get() = delegate.values
override fun clear() = delegate.clear()
override fun put(key: UInt, value: C): C? = delegate.put(key, value)
override fun putAll(from: Map<out UInt, C>) = delegate.putAll(from)
override fun remove(key: UInt): C? = delegate.remove(key)
}
\ No newline at end of file
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.contact
import net.mamoe.mirai.utils.LockFreeLinkedList
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.joinToString
/**
* 只读联系人列表, lock-free 实现
*/
@UseExperimental(MiraiInternalAPI::class)
@Suppress("unused")
class ContactList<C : Contact>(@PublishedApi internal val delegate: LockFreeLinkedList<C>) {
/**
* ID 列表的字符串表示.
* 如:
* ```
* [123456, 321654, 123654]
* ```
*/
val idContentString: String get() = "[" + buildString { delegate.forEach { append(it.id).append(", ") } }.dropLast(2) + "]"
operator fun get(id: UInt): C = delegate[id]
fun getOrNull(id: UInt): C? = delegate.getOrNull(id)
fun containsId(id: UInt): Boolean = delegate.getOrNull(id) != null
val size: Int get() = delegate.size
operator fun contains(element: C): Boolean = delegate.contains(element)
fun containsAll(elements: Collection<C>): Boolean = elements.all { contains(it) }
fun isEmpty(): Boolean = delegate.isEmpty()
inline fun forEach(block: (C) -> Unit) = delegate.forEach(block)
override fun toString(): String = delegate.joinToString(separator = ", ", prefix = "ContactList(", postfix = ")")
}
operator fun <C : Contact> LockFreeLinkedList<C>.get(id: UInt): C {
forEach { if (it.id == id) return it }
throw NoSuchElementException()
}
fun <C : Contact> LockFreeLinkedList<C>.getOrNull(id: UInt): C? {
forEach { if (it.id == id) return it }
return null
}
fun <C : Contact> LockFreeLinkedList<C>.getOrAdd(id: UInt, supplier: () -> C): C = filteringGetOrAdd({ it.id == id }, supplier)
...@@ -2,101 +2,50 @@ ...@@ -2,101 +2,50 @@
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
fun GroupId.toInternalId(): GroupInternalId {//求你别出错 import kotlin.math.pow
val left: Long = this.value.toString().let {
if (it.length < 6) {
return GroupInternalId(this.value) @Suppress("ObjectPropertyName")
} private val `10EXP6` = 10.0.pow(6).toUInt()
it.substring(0, it.length - 6).toLong()
}
val right: Long = this.value.toString().let { fun GroupId.toInternalId(): GroupInternalId {
it.substring(it.length - 6).toLong() if (this.value <= `10EXP6`) {
return GroupInternalId(this.value)
} }
val left: Long = this.value.toString().dropLast(6).toLong()
val right: Long = this.value.toString().takeLast(6).toLong()
return GroupInternalId( return GroupInternalId(
when (left) { when (left) {
in 1..10 -> { in 1..10 -> ((left + 202).toString() + right.toString()).toUInt()
((left + 202).toString() + right.toString()).toUInt() in 11..19 -> ((left + 469).toString() + right.toString()).toUInt()
} in 20..66 -> ((left + 208).toString() + right.toString()).toUInt()
in 11..19 -> { in 67..156 -> ((left + 1943).toString() + right.toString()).toUInt()
((left + 469).toString() + right.toString()).toUInt() in 157..209 -> ((left + 199).toString() + right.toString()).toUInt()
} in 210..309 -> ((left + 389).toString() + right.toString()).toUInt()
in 20..66 -> { in 310..499 -> ((left + 349).toString() + right.toString()).toUInt()
((left + 208).toString() + right.toString()).toUInt()
}
in 67..156 -> {
((left + 1943).toString() + right.toString()).toUInt()
}
in 157..209 -> {
((left + 199).toString() + right.toString()).toUInt()
}
in 210..309 -> {
((left + 389).toString() + right.toString()).toUInt()
}
in 310..499 -> {
((left + 349).toString() + right.toString()).toUInt()
}
else -> this.value else -> this.value
} }
) )
} }
fun GroupInternalId.toId(): GroupId = with(value) { fun GroupInternalId.toId(): GroupId = with(value.toString()) {
//求你别出错 if (value < `10EXP6`) {
var left: UInt = this.toString().let { return GroupId(value)
if (it.length < 6) {
return GroupId(value)
}
it.substring(0 until it.length - 6).toUInt()
} }
val left: UInt = this.dropLast(6).toUInt()
return GroupId(when (left.toInt()) { return GroupId(
in 203..212 -> { when (left.toInt()) {
val right: UInt = this.toString().let { in 203..212 -> ((left - 202u).toString() + this.takeLast(6).toInt().toString()).toUInt()
it.substring(it.length - 6).toUInt() in 480..488 -> ((left - 469u).toString() + this.takeLast(6).toInt().toString()).toUInt()
} in 2100..2146 -> ((left.toString().take(3).toUInt() - 208u).toString() + this.takeLast(7).toInt().toString()).toUInt()
((left - 202u).toString() + right.toString()).toUInt() in 2010..2099 -> ((left - 1943u).toString() + this.takeLast(6).toInt().toString()).toUInt()
in 2147..2199 -> ((left.toString().take(3).toUInt() - 199u).toString() + this.takeLast(7).toInt().toString()).toUInt()
in 4100..4199 -> ((left.toString().take(3).toUInt() - 389u).toString() + this.takeLast(7).toInt().toString()).toUInt()
in 3800..3989 -> ((left.toString().take(3).toUInt() - 349u).toString() + this.takeLast(7).toInt().toString()).toUInt()
else -> value
} }
in 480..488 -> { )
val right: UInt = this.toString().let {
it.substring(it.length - 6).toUInt()
}
((left - 469u).toString() + right.toString()).toUInt()
}
in 2100..2146 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 208u).toString() + right.toString()).toUInt()
}
in 2010..2099 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 6).toUInt()
}
((left - 1943u).toString() + right.toString()).toUInt()
}
in 2147..2199 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 199u).toString() + right.toString()).toUInt()
}
in 4100..4199 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 389u).toString() + right.toString()).toUInt()
}
in 3800..3989 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 349u).toString() + right.toString()).toUInt()
}
else -> value
})
} }
\ No newline at end of file
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
package net.mamoe.mirai.contact.internal package net.mamoe.mirai.contact.internal
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.contact.data.Profile import net.mamoe.mirai.contact.data.Profile
...@@ -15,9 +17,7 @@ import net.mamoe.mirai.network.qqAccount ...@@ -15,9 +17,7 @@ import net.mamoe.mirai.network.qqAccount
import net.mamoe.mirai.network.sessionKey import net.mamoe.mirai.network.sessionKey
import net.mamoe.mirai.qqAccount import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.sendPacket import net.mamoe.mirai.sendPacket
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.logStacktrace
import net.mamoe.mirai.withSession import net.mamoe.mirai.withSession
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
...@@ -35,15 +35,12 @@ internal sealed class ContactImpl : Contact { ...@@ -35,15 +35,12 @@ internal sealed class ContactImpl : Contact {
*/ */
@Suppress("FunctionName") @Suppress("FunctionName")
@PublishedApi @PublishedApi
internal suspend fun Group(bot: Bot, groupId: GroupId, context: CoroutineContext): Group { internal fun CoroutineScope.Group(bot: Bot, groupId: GroupId, info: RawGroupInfo, context: CoroutineContext): Group =
val info: RawGroupInfo = try { GroupImpl(bot, groupId, context).apply {
bot.withSession { GroupPacket.QueryGroupInfo(qqAccount, groupId.toInternalId(), sessionKey).sendAndExpect() } this@apply.info = info.parseBy(this@apply)
} catch (e: Exception) { launch { startUpdater() }
e.logStacktrace()
error("Cannot obtain group info for id ${groupId.value}")
} }
return GroupImpl(bot, groupId, context).apply { this.info = info.parseBy(this); startUpdater() }
}
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter") @Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
internal data class GroupImpl internal constructor(override val bot: Bot, val groupId: GroupId, override val coroutineContext: CoroutineContext) : internal data class GroupImpl internal constructor(override val bot: Bot, val groupId: GroupId, override val coroutineContext: CoroutineContext) :
...@@ -52,14 +49,15 @@ internal data class GroupImpl internal constructor(override val bot: Bot, val gr ...@@ -52,14 +49,15 @@ internal data class GroupImpl internal constructor(override val bot: Bot, val gr
override val internalId = GroupId(id).toInternalId() override val internalId = GroupId(id).toInternalId()
internal lateinit var info: GroupInfo internal lateinit var info: GroupInfo
internal lateinit var initialInfoJob: Job
override val owner: Member get() = info.owner override val owner: Member get() = info.owner
override val name: String get() = info.name override val name: String get() = info.name
override val announcement: String get() = info.announcement override val announcement: String get() = info.announcement
override val members: ContactList<Member> get() = info.members override val members: ContactList<Member> get() = info.members
override fun getMember(id: UInt): Member = override fun getMember(id: UInt): Member =
if (members.containsKey(id)) members[id]!! members.getOrNull(id) ?: throw NoSuchElementException("No such member whose id is ${id.toLong()} in group ${groupId.value.toLong()}")
else throw NoSuchElementException("No such member whose id is ${id.toLong()} in group ${groupId.value.toLong()}")
override suspend fun sendMessage(message: MessageChain) { override suspend fun sendMessage(message: MessageChain) {
bot.sendPacket(GroupPacket.Message(bot.qqAccount, internalId, bot.sessionKey, message)) bot.sendPacket(GroupPacket.Message(bot.qqAccount, internalId, bot.sessionKey, message))
...@@ -76,20 +74,18 @@ internal data class GroupImpl internal constructor(override val bot: Bot, val gr ...@@ -76,20 +74,18 @@ internal data class GroupImpl internal constructor(override val bot: Bot, val gr
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
override suspend fun startUpdater() { override suspend fun startUpdater() {
subscribeAlways<MemberJoinEventPacket> { subscribeAlways<MemberJoinEventPacket> {
// FIXME: 2019/11/29 非线程安全!! members.delegate.addLast(it.member)
members.mutable[it.member.id] = it.member
} }
subscribeAlways<MemberQuitEvent> { subscribeAlways<MemberQuitEvent> {
// FIXME: 2019/11/29 非线程安全!! members.delegate.remove(it.member)
members.mutable.remove(it.member.id)
} }
} }
override fun toString(): String = "Group(${this.id})" override fun toString(): String = "Group(${this.id})"
} }
@Suppress("FunctionName") @Suppress("FunctionName", "NOTHING_TO_INLINE")
suspend inline fun QQ(bot: Bot, id: UInt, coroutineContext: CoroutineContext): QQ = QQImpl(bot, id, coroutineContext).apply { startUpdater() } internal inline fun CoroutineScope.QQ(bot: Bot, id: UInt, coroutineContext: CoroutineContext): QQ = QQImpl(bot, id, coroutineContext).apply { launch { startUpdater() } }
@PublishedApi @PublishedApi
internal data class QQImpl @PublishedApi internal constructor(override val bot: Bot, override val id: UInt, override val coroutineContext: CoroutineContext) : internal data class QQImpl @PublishedApi internal constructor(override val bot: Bot, override val id: UInt, override val coroutineContext: CoroutineContext) :
...@@ -118,9 +114,9 @@ internal data class QQImpl @PublishedApi internal constructor(override val bot: ...@@ -118,9 +114,9 @@ internal data class QQImpl @PublishedApi internal constructor(override val bot:
override fun toString(): String = "QQ(${this.id})" override fun toString(): String = "QQ(${this.id})"
} }
@Suppress("FunctionName") @Suppress("FunctionName", "NOTHING_TO_INLINE")
suspend inline fun Member(delegate: QQ, group: Group, permission: MemberPermission, coroutineContext: CoroutineContext): Member = internal inline fun Group.Member(delegate: QQ, permission: MemberPermission, coroutineContext: CoroutineContext): Member =
MemberImpl(delegate, group, permission, coroutineContext).apply { startUpdater() } MemberImpl(delegate, this, permission, coroutineContext).apply { launch { startUpdater() } }
/** /**
* 群成员 * 群成员
......
@file:Suppress("MemberVisibilityCanBePrivate", "unused", "EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") @file:Suppress("MemberVisibilityCanBePrivate", "unused", "EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS", "NOTHING_TO_INLINE")
package net.mamoe.mirai.network package net.mamoe.mirai.network
...@@ -74,9 +74,9 @@ abstract class BotSessionBase internal constructor( ...@@ -74,9 +74,9 @@ abstract class BotSessionBase internal constructor(
*/ */
val gtk: Int get() = _gtk val gtk: Int get() = _gtk
suspend inline fun Int.qq(): QQ = bot.getQQ(this.coerceAtLeastOrFail(0).toUInt()) inline fun Int.qq(): QQ = bot.getQQ(this.coerceAtLeastOrFail(0).toUInt())
suspend inline fun Long.qq(): QQ = bot.getQQ(this.coerceAtLeastOrFail(0)) inline fun Long.qq(): QQ = bot.getQQ(this.coerceAtLeastOrFail(0))
suspend inline fun UInt.qq(): QQ = bot.getQQ(this) inline fun UInt.qq(): QQ = bot.getQQ(this)
suspend inline fun Int.group(): Group = bot.getGroup(this.coerceAtLeastOrFail(0).toUInt()) suspend inline fun Int.group(): Group = bot.getGroup(this.coerceAtLeastOrFail(0).toUInt())
suspend inline fun Long.group(): Group = bot.getGroup(this.coerceAtLeastOrFail(0)) suspend inline fun Long.group(): Group = bot.getGroup(this.coerceAtLeastOrFail(0))
......
...@@ -226,7 +226,9 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou ...@@ -226,7 +226,9 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou
return return
if (!packet::class.annotations.filterIsInstance<NoLog>().any()) { if (!packet::class.annotations.filterIsInstance<NoLog>().any()) {
bot.logger.verbose("Packet received: $packet") if ((packet as? BroadcastControllable)?.shouldBroadcast != false) {
bot.logger.verbose("Packet received: $packet")
}
} }
when (packet) { when (packet) {
...@@ -239,7 +241,7 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou ...@@ -239,7 +241,7 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou
temporaryPacketHandlers.filter { it.filter(session, packet, sequenceId) } temporaryPacketHandlers.filter { it.filter(session, packet, sequenceId) }
.also { temporaryPacketHandlers.removeAll(it) } .also { temporaryPacketHandlers.removeAll(it) }
}.forEach { }.forEach {
it.doReceiveWithoutExceptions(packet) it.doReceiveCatchingExceptions(packet)
} }
if (factory is SessionPacketFactory<*>) { if (factory is SessionPacketFactory<*>) {
...@@ -286,7 +288,7 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou ...@@ -286,7 +288,7 @@ internal class TIMBotNetworkHandler internal constructor(coroutineContext: Corou
it::class.annotations.filterIsInstance<NoLog>().any() it::class.annotations.filterIsInstance<NoLog>().any()
} }
}?.let { }?.let {
bot.logger.verbose("Packet sent: ${it.packetId}") bot.logger.verbose("Packet sent: ${it.name}")
} }
PacketSentEvent(bot, packet).broadcast() PacketSentEvent(bot, packet).broadcast()
......
...@@ -60,7 +60,7 @@ internal class TemporaryPacketHandler<P : Packet, R>( ...@@ -60,7 +60,7 @@ internal class TemporaryPacketHandler<P : Packet, R>(
internal inline fun filter(session: BotSession, packet: Packet, sequenceId: UShort): Boolean = internal inline fun filter(session: BotSession, packet: Packet, sequenceId: UShort): Boolean =
expectationClass.isInstance(packet) && session === this.fromSession && if (checkSequence) sequenceId == toSend.sequenceId else true expectationClass.isInstance(packet) && session === this.fromSession && if (checkSequence) sequenceId == toSend.sequenceId else true
internal suspend inline fun doReceiveWithoutExceptions(packet: Packet) { internal suspend inline fun doReceiveCatchingExceptions(packet: Packet) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val ret = try { val ret = try {
withContext(callerContext) { withContext(callerContext) {
......
...@@ -2,19 +2,6 @@ ...@@ -2,19 +2,6 @@
package net.mamoe.mirai.network.protocol.tim.packet package net.mamoe.mirai.network.protocol.tim.packet
/**
* 包 ID. 除特殊外, [PacketFactory] 都需要这个注解来指定包 ID.
*/
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
internal annotation class AnnotatedId( // 注解无法在 JS 平台使用, 但现在暂不需要考虑 JS
val id: KnownPacketId
)
internal inline val AnnotatedId.value: UShort get() = id.value
/** /**
* 包的最后一次修改时间, 和分析时使用的 TIM 版本 * 包的最后一次修改时间, 和分析时使用的 TIM 版本
*/ */
......
...@@ -24,7 +24,7 @@ internal class OutgoingPacket( ...@@ -24,7 +24,7 @@ internal class OutgoingPacket(
val sequenceId: UShort, val sequenceId: UShort,
internal val delegate: ByteReadPacket internal val delegate: ByteReadPacket
) : Packet { ) : Packet {
private val name: String by lazy { val name: String by lazy {
name ?: packetId.toString() name ?: packetId.toString()
} }
} }
......
...@@ -10,7 +10,6 @@ import kotlinx.io.pool.useInstance ...@@ -10,7 +10,6 @@ import kotlinx.io.pool.useInstance
import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.ByteArrayPool import net.mamoe.mirai.utils.io.ByteArrayPool
import net.mamoe.mirai.utils.io.debugPrint import net.mamoe.mirai.utils.io.debugPrint
import net.mamoe.mirai.utils.io.read import net.mamoe.mirai.utils.io.read
...@@ -26,20 +25,13 @@ import net.mamoe.mirai.utils.readProtoMap ...@@ -26,20 +25,13 @@ import net.mamoe.mirai.utils.readProtoMap
*/ */
internal abstract class PacketFactory<out TPacket : Packet, TDecrypter : Decrypter>(val decrypterType: DecrypterType<TDecrypter>) { internal abstract class PacketFactory<out TPacket : Packet, TDecrypter : Decrypter>(val decrypterType: DecrypterType<TDecrypter>) {
/** @Suppress("PropertyName")
* 2 Ubyte. internal var _id: PacketId = NullPacketId
* 读取注解 [AnnotatedId]
*/
private val annotatedId: AnnotatedId
get() = (this::class.annotations.firstOrNull { it is AnnotatedId } as? AnnotatedId)
?: error("Annotation AnnotatedId not found for class ${this::class.simpleName}")
// TODO: 2019/11/22 修改 包 ID 为参数
/** /**
* 包 ID. * 包 ID.
*/ */
open val id: PacketId by lazy { annotatedId.id } open val id: PacketId get() = _id
/** /**
* **解码**服务器的回复数据包 * **解码**服务器的回复数据包
......
...@@ -55,35 +55,35 @@ internal inline class IgnoredPacketId constructor(override val value: UShort) : ...@@ -55,35 +55,35 @@ internal inline class IgnoredPacketId constructor(override val value: UShort) :
* 已知的 [matchPacketId]. 所有在 Mirai 中实现过的包都会使用这些 Id * 已知的 [matchPacketId]. 所有在 Mirai 中实现过的包都会使用这些 Id
*/ */
@Suppress("unused") @Suppress("unused")
internal enum class KnownPacketId(override inline val value: UShort, override inline val factory: PacketFactory<*, *>) : internal enum class KnownPacketId(override val value: UShort, override val factory: PacketFactory<*, *>) :
PacketId { PacketId {
inline TOUCH(0x0825u, TouchPacket), TOUCH(0x0825u, TouchPacket),
inline SESSION_KEY(0x0828u, RequestSessionPacket), SESSION_KEY(0x0828u, RequestSessionPacket),
inline LOGIN(0x0836u, SubmitPasswordPacket), LOGIN(0x0836u, SubmitPasswordPacket),
inline CAPTCHA(0x00BAu, CaptchaPacket), CAPTCHA(0x00BAu, CaptchaPacket),
inline SERVER_EVENT_1(0x00CEu, EventPacketFactory), SERVER_EVENT_1(0x00CEu, EventPacketFactory),
inline SERVER_EVENT_2(0x0017u, EventPacketFactory), SERVER_EVENT_2(0x0017u, EventPacketFactory),
inline FRIEND_ONLINE_STATUS_CHANGE(0x0081u, FriendOnlineStatusChangedPacket), FRIEND_ONLINE_STATUS_CHANGE(0x0081u, FriendOnlineStatusChangedPacket),
inline CHANGE_ONLINE_STATUS(0x00ECu, ChangeOnlineStatusPacket), CHANGE_ONLINE_STATUS(0x00ECu, ChangeOnlineStatusPacket),
inline HEARTBEAT(0x0058u, HeartbeatPacket), HEARTBEAT(0x0058u, HeartbeatPacket),
inline S_KEY(0x001Du, RequestSKeyPacket), S_KEY(0x001Du, RequestSKeyPacket),
inline ACCOUNT_INFO(0x005Cu, RequestAccountInfoPacket), ACCOUNT_INFO(0x005Cu, RequestAccountInfoPacket),
inline GROUP_PACKET(0x0002u, GroupPacket), GROUP_PACKET(0x0002u, GroupPacket),
inline SEND_FRIEND_MESSAGE(0x00CDu, SendFriendMessagePacket), SEND_FRIEND_MESSAGE(0x00CDu, SendFriendMessagePacket),
inline CAN_ADD_FRIEND(0x00A7u, CanAddFriendPacket), CAN_ADD_FRIEND(0x00A7u, CanAddFriendPacket),
inline ADD_FRIEND(0x00A8u, AddFriendPacket), ADD_FRIEND(0x00A8u, AddFriendPacket),
inline REQUEST_FRIEND_ADDITION_KEY(0x00AEu, RequestFriendAdditionKeyPacket), REQUEST_FRIEND_ADDITION_KEY(0x00AEu, RequestFriendAdditionKeyPacket),
inline GROUP_IMAGE_ID(0x0388u, GroupImagePacket), GROUP_IMAGE_ID(0x0388u, GroupImagePacket),
inline FRIEND_IMAGE_ID(0x0352u, FriendImagePacket), FRIEND_IMAGE_ID(0x0352u, FriendImagePacket),
inline REQUEST_PROFILE_AVATAR(0x0031u, RequestProfileAvatarPacket), REQUEST_PROFILE_AVATAR(0x0031u, RequestProfileAvatarPacket),
inline REQUEST_PROFILE_DETAILS(0x003Cu, RequestProfileDetailsPacket), REQUEST_PROFILE_DETAILS(0x003Cu, RequestProfileDetailsPacket),
inline QUERY_NICKNAME(0x0126u, QueryNicknamePacket), QUERY_NICKNAME(0x0126u, QueryNicknamePacket),
inline QUERY_PREVIOUS_NAME(0x01BCu, QueryPreviousNamePacket), QUERY_PREVIOUS_NAME(0x01BCu, QueryPreviousNamePacket),
inline QUERY_FRIEND_REMARK(0x003Eu, QueryFriendRemarkPacket) QUERY_FRIEND_REMARK(0x003Eu, QueryFriendRemarkPacket)
// 031F 查询 "新朋友" 记录 // 031F 查询 "新朋友" 记录
...@@ -92,5 +92,9 @@ internal enum class KnownPacketId(override inline val value: UShort, override in ...@@ -92,5 +92,9 @@ internal enum class KnownPacketId(override inline val value: UShort, override in
; ;
init {
factory._id = this
}
override fun toString(): String = (factory::class.simpleName ?: this.name) + "(${value.toUHexString()})" override fun toString(): String = (factory::class.simpleName ?: this.name) + "(${value.toUHexString()})"
} }
...@@ -19,7 +19,6 @@ import net.mamoe.mirai.withSession ...@@ -19,7 +19,6 @@ import net.mamoe.mirai.withSession
* - 昵称 * - 昵称
* - 共同群内的群名片 * - 共同群内的群名片
*/ */
@AnnotatedId(KnownPacketId.QUERY_PREVIOUS_NAME)
@PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)")
internal object QueryPreviousNamePacket : SessionPacketFactory<PreviousNameList>() { internal object QueryPreviousNamePacket : SessionPacketFactory<PreviousNameList>() {
operator fun invoke( operator fun invoke(
...@@ -77,7 +76,6 @@ class PreviousNameList( ...@@ -77,7 +76,6 @@ class PreviousNameList(
* *
* @author Him188moe * @author Him188moe
*/ */
@AnnotatedId(KnownPacketId.CAN_ADD_FRIEND)
@PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)")
internal object CanAddFriendPacket : SessionPacketFactory<CanAddFriendResponse>() { internal object CanAddFriendPacket : SessionPacketFactory<CanAddFriendResponse>() {
operator fun invoke( operator fun invoke(
...@@ -155,7 +153,6 @@ inline class FriendAdditionKey(val value: IoBuffer) ...@@ -155,7 +153,6 @@ inline class FriendAdditionKey(val value: IoBuffer)
/** /**
* 请求一个 32 位 Key, 在添加好友时发出 * 请求一个 32 位 Key, 在添加好友时发出
*/ */
@AnnotatedId(KnownPacketId.REQUEST_FRIEND_ADDITION_KEY)
@PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)")
internal object RequestFriendAdditionKeyPacket : SessionPacketFactory<RequestFriendAdditionKeyPacket.Response>() { internal object RequestFriendAdditionKeyPacket : SessionPacketFactory<RequestFriendAdditionKeyPacket.Response>() {
operator fun invoke( operator fun invoke(
...@@ -182,7 +179,6 @@ internal object RequestFriendAdditionKeyPacket : SessionPacketFactory<RequestFri ...@@ -182,7 +179,6 @@ internal object RequestFriendAdditionKeyPacket : SessionPacketFactory<RequestFri
/** /**
* 请求添加好友 * 请求添加好友
*/ */
@AnnotatedId(KnownPacketId.ADD_FRIEND)
internal object AddFriendPacket : SessionPacketFactory<AddFriendPacket.Response>() { internal object AddFriendPacket : SessionPacketFactory<AddFriendPacket.Response>() {
@PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.11", timVersion = "2.3.2 (21173)")
@Suppress("FunctionName") @Suppress("FunctionName")
......
...@@ -92,7 +92,6 @@ internal object FriendImageOverFileSizeMax : FriendImageResponse { ...@@ -92,7 +92,6 @@ internal object FriendImageOverFileSizeMax : FriendImageResponse {
* - 服务器已经存有这个图片 * - 服务器已经存有这个图片
* - 服务器未存有, 返回一个 key 用于客户端上传 * - 服务器未存有, 返回一个 key 用于客户端上传
*/ */
@AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID)
@PacketVersion(date = "2019.11.16", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.16", timVersion = "2.3.2 (21173)")
internal object FriendImagePacket : SessionPacketFactory<FriendImageResponse>() { internal object FriendImagePacket : SessionPacketFactory<FriendImageResponse>() {
@Suppress("FunctionName") @Suppress("FunctionName")
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.network.protocol.tim.packet.action package net.mamoe.mirai.network.protocol.tim.packet.action
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.Packet
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.SessionPacketFactory
// 0001 // 0001
// 已确认 查好友列表的列表 // 已确认 查好友列表的列表
...@@ -15,6 +23,13 @@ package net.mamoe.mirai.network.protocol.tim.packet.action ...@@ -15,6 +23,13 @@ package net.mamoe.mirai.network.protocol.tim.packet.action
// 00 00 // 00 00
internal inline class FriendListList(val delegate: List<FriendList>): Packet
internal object QueryFriendListListPacket : SessionPacketFactory<FriendList>() {
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): FriendList {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
// 0134 // 0134
......
...@@ -16,7 +16,6 @@ import net.mamoe.mirai.utils.io.writeQQ ...@@ -16,7 +16,6 @@ import net.mamoe.mirai.utils.io.writeQQ
* *
* @author Him188moe * @author Him188moe
*/ */
@AnnotatedId(KnownPacketId.ACCOUNT_INFO)
internal object RequestAccountInfoPacket : SessionPacketFactory<RequestAccountInfoPacket.Response>() { internal object RequestAccountInfoPacket : SessionPacketFactory<RequestAccountInfoPacket.Response>() {
operator fun invoke( operator fun invoke(
qq: UInt, qq: UInt,
......
...@@ -99,7 +99,6 @@ internal class ImageUploadInfo( ...@@ -99,7 +99,6 @@ internal class ImageUploadInfo(
/** /**
* 获取 Image Id 和上传用的一个 uKey * 获取 Image Id 和上传用的一个 uKey
*/ */
@AnnotatedId(KnownPacketId.GROUP_IMAGE_ID)
@PacketVersion(date = "2019.11.22", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.11.22", timVersion = "2.3.2 (21173)")
internal object GroupImagePacket : SessionPacketFactory<GroupImageResponse>() { internal object GroupImagePacket : SessionPacketFactory<GroupImageResponse>() {
......
...@@ -17,7 +17,6 @@ inline class NicknameMap(val delegate: Map<UInt, String>) : Packet ...@@ -17,7 +17,6 @@ inline class NicknameMap(val delegate: Map<UInt, String>) : Packet
/** /**
* 批量查询昵称. * 批量查询昵称.
*/ */
@AnnotatedId(KnownPacketId.QUERY_NICKNAME)
internal object QueryNicknamePacket : SessionPacketFactory<NicknameMap>() { internal object QueryNicknamePacket : SessionPacketFactory<NicknameMap>() {
/** /**
* 单个查询. * 单个查询.
...@@ -135,7 +134,6 @@ body=03 00 00 00 00 00 00 00 00 00 00 00 03 8E 3C A6 A0 EE EF 02 07 6C 01 14 E8 ...@@ -135,7 +134,6 @@ body=03 00 00 00 00 00 00 00 00 00 00 00 03 8E 3C A6 A0 EE EF 02 07 6C 01 14 E8
/** /**
* 请求获取头像 * 请求获取头像
*/ // ? 这个包的数据跟下面那个包一样 */ // ? 这个包的数据跟下面那个包一样
@AnnotatedId(KnownPacketId.REQUEST_PROFILE_AVATAR)
internal object RequestProfileAvatarPacket : SessionPacketFactory<AvatarLink>() { internal object RequestProfileAvatarPacket : SessionPacketFactory<AvatarLink>() {
//00 01 00 17 D4 54 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5 //00 01 00 17 D4 54 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5
operator fun invoke( operator fun invoke(
...@@ -159,7 +157,6 @@ internal object RequestProfileAvatarPacket : SessionPacketFactory<AvatarLink>() ...@@ -159,7 +157,6 @@ internal object RequestProfileAvatarPacket : SessionPacketFactory<AvatarLink>()
* *
* @see Profile * @see Profile
*/ */
@AnnotatedId(KnownPacketId.REQUEST_PROFILE_DETAILS)
internal object RequestProfileDetailsPacket : SessionPacketFactory<RequestProfileDetailsResponse>() { internal object RequestProfileDetailsPacket : SessionPacketFactory<RequestProfileDetailsResponse>() {
//00 01 3E F8 FB E3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5 //00 01 3E F8 FB E3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5
//00 01 B1 89 BE 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5 //00 01 B1 89 BE 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5
...@@ -211,7 +208,6 @@ internal object RequestProfileDetailsPacket : SessionPacketFactory<RequestProfil ...@@ -211,7 +208,6 @@ internal object RequestProfileDetailsPacket : SessionPacketFactory<RequestProfil
} }
} }
@AnnotatedId(KnownPacketId.REQUEST_PROFILE_DETAILS)
internal data class RequestProfileDetailsResponse( internal data class RequestProfileDetailsResponse(
val qq: UInt, val qq: UInt,
val profile: Profile val profile: Profile
......
...@@ -15,7 +15,6 @@ import net.mamoe.mirai.utils.io.writeZero ...@@ -15,7 +15,6 @@ import net.mamoe.mirai.utils.io.writeZero
*/ */
inline class FriendNameRemark(val value: String) : Packet inline class FriendNameRemark(val value: String) : Packet
@AnnotatedId(KnownPacketId.QUERY_FRIEND_REMARK)
internal object QueryFriendRemarkPacket : SessionPacketFactory<FriendNameRemark>() { internal object QueryFriendRemarkPacket : SessionPacketFactory<FriendNameRemark>() {
/** /**
* 查询好友的备注 * 查询好友的备注
......
...@@ -11,7 +11,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.* ...@@ -11,7 +11,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.md5 import net.mamoe.mirai.utils.md5
@AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE)
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2 (21173)") @PacketVersion(date = "2019.10.19", timVersion = "2.3.2 (21173)")
internal object SendFriendMessagePacket : SessionPacketFactory<SendFriendMessagePacket.Response>() { internal object SendFriendMessagePacket : SessionPacketFactory<SendFriendMessagePacket.Response>() {
operator fun invoke( operator fun invoke(
......
...@@ -7,9 +7,7 @@ import kotlinx.io.core.discardExact ...@@ -7,9 +7,7 @@ import kotlinx.io.core.discardExact
import kotlinx.io.core.readUByte import kotlinx.io.core.readUByte
import kotlinx.io.core.readUInt import kotlinx.io.core.readUInt
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.getQQ
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.AnnotatedId
import net.mamoe.mirai.network.protocol.tim.packet.KnownPacketId import net.mamoe.mirai.network.protocol.tim.packet.KnownPacketId
import net.mamoe.mirai.network.protocol.tim.packet.PacketId import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.SessionPacketFactory import net.mamoe.mirai.network.protocol.tim.packet.SessionPacketFactory
...@@ -23,7 +21,6 @@ data class FriendStatusChanged( ...@@ -23,7 +21,6 @@ data class FriendStatusChanged(
/** /**
* 好友在线状态改变 * 好友在线状态改变
*/ */
@AnnotatedId(KnownPacketId.FRIEND_ONLINE_STATUS_CHANGE)
internal object FriendOnlineStatusChangedPacket : SessionPacketFactory<FriendStatusChanged>() { internal object FriendOnlineStatusChangedPacket : SessionPacketFactory<FriendStatusChanged>() {
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): FriendStatusChanged { override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): FriendStatusChanged {
......
...@@ -13,7 +13,6 @@ import net.mamoe.mirai.contact.internal.MemberImpl ...@@ -13,7 +13,6 @@ import net.mamoe.mirai.contact.internal.MemberImpl
import net.mamoe.mirai.event.Subscribable import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.getGroup import net.mamoe.mirai.getGroup
import net.mamoe.mirai.getQQ
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.discardExact import net.mamoe.mirai.utils.io.discardExact
...@@ -76,7 +75,7 @@ internal object MemberJoinPacketHandler : KnownEventParserAndHandler<MemberJoinE ...@@ -76,7 +75,7 @@ internal object MemberJoinPacketHandler : KnownEventParserAndHandler<MemberJoinE
discardExact(1) // 01 discardExact(1) // 01
val qq = bot.getQQ(readUInt()) val qq = bot.getQQ(readUInt())
val member = Member(qq, group, MemberPermission.MEMBER, qq.coroutineContext) val member = group.Member(qq, MemberPermission.MEMBER, qq.coroutineContext)
return if (readByte().toInt() == 0x03) { return if (readByte().toInt() == 0x03) {
MemberJoinEventPacket(member, null) MemberJoinEventPacket(member, null)
......
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE") @file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE", "MemberVisibilityCanBePrivate")
package net.mamoe.mirai.network.protocol.tim.packet.event package net.mamoe.mirai.network.protocol.tim.packet.event
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact import kotlinx.io.core.discardExact
import kotlinx.io.core.readBytes
import kotlinx.io.core.readUInt import kotlinx.io.core.readUInt
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.getGroup import net.mamoe.mirai.getGroup
import net.mamoe.mirai.qqAccount import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.io.toUHexString
// region mute // region mute
/** /**
...@@ -59,6 +61,7 @@ class MemberUnmuteEvent( ...@@ -59,6 +61,7 @@ class MemberUnmuteEvent(
/** /**
* 机器人被解除禁言事件 * 机器人被解除禁言事件
*/ */
@Suppress("SpellCheckingInspection")
class BeingUnmutedEvent( class BeingUnmutedEvent(
override val operator: Member override val operator: Member
) : UnmuteEvent() { ) : UnmuteEvent() {
...@@ -73,15 +76,24 @@ sealed class UnmuteEvent : EventOfMute() { ...@@ -73,15 +76,24 @@ sealed class UnmuteEvent : EventOfMute() {
// endregion // endregion
internal object Unknown0x02DCPacketFlag0x0EMaybeMutePacket : EventOfMute() {
override val operator: Member get() = error("Getting a field from Unknown0x02DCPacketFlag0x0EMaybeMutePacket")
override val group: Group get() = error("Getting a field from Unknown0x02DCPacketFlag0x0EMaybeMutePacket")
override fun toString(): String = "Unknown0x02DCPacketFlag0x0EMaybeMutePacket"
}
sealed class EventOfMute : EventPacket { sealed class EventOfMute : EventPacket {
abstract val operator: Member abstract val operator: Member
abstract val group: Group abstract val group: Group
} }
// TODO: 2019/12/14 这可能不只是禁言的包.
internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandler<EventOfMute>(0x02DCu) { internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandler<EventOfMute>(0x02DCu) {
override suspend fun ByteReadPacket.parse(bot: Bot, identity: EventPacketIdentity): EventOfMute { override suspend fun ByteReadPacket.parse(bot: Bot, identity: EventPacketIdentity): EventOfMute {
//取消 //取消
//00 00 00 11 00 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00 //00 00 00 11 00
// 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00
// 01 01 // 01 01
// 22 96 29 7B // 22 96 29 7B
// 0C 01 // 0C 01
...@@ -92,7 +104,8 @@ internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandl ...@@ -92,7 +104,8 @@ internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandl
// 00 00 00 00 // 00 00 00 00
// 禁言 // 禁言
//00 00 00 11 00 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00 //00 00 00 11 00
// 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00
// 01 // 01
// 01 // 01
// 22 96 29 7B // 22 96 29 7B
...@@ -104,28 +117,42 @@ internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandl ...@@ -104,28 +117,42 @@ internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandl
// 01 // 01
// 76 E4 B8 DD // 76 E4 B8 DD
// 00 27 8D 00 // 00 27 8D 00
discardExact(19)
discardExact(2) discardExact(3)
val group = bot.getGroup(readUInt()) return when (val flag = readByte().toUInt()) {
discardExact(2) 0x0Eu -> {
val operator = group.getMember(readUInt()) //00 00 00 0E 00 08 00 02 00 01 00
discardExact(4) //time // 0A 00 04 01 00 00 00 35 DB 60 A2 11 00 3E 08 07 20 A2 C1 ED AE 03 5A 34 08 A2 FF 8C F0 03 1A 19 08 F4 0E 10 FE 8C D3 EF 05 18 84 A1 F8 F9 06 20 00 28 00 30 A2 FF 8C F0 03 2A 0D 08 00 12 09 08 F4 0E 10 00 18 01 20 00 30 00 38 00
discardExact(2) Unknown0x02DCPacketFlag0x0EMaybeMutePacket
val memberQQ = readUInt()
val durationSeconds = readUInt().toInt()
return if (durationSeconds == 0) {
if (memberQQ == bot.qqAccount) {
BeingUnmutedEvent(operator)
} else {
MemberUnmuteEvent(group.getMember(memberQQ), operator)
} }
} else {
if (memberQQ == bot.qqAccount) { 0x11u -> {
BeingMutedEvent(durationSeconds, operator) discardExact(15)
} else { discardExact(2)
MemberMuteEvent(group.getMember(memberQQ), durationSeconds, operator) val group = bot.getGroup(readUInt())
discardExact(2)
val operator = group.getMember(readUInt())
discardExact(4) //time
discardExact(2)
val memberQQ = readUInt()
val durationSeconds = readUInt().toInt()
if (durationSeconds == 0) {
if (memberQQ == bot.qqAccount) {
BeingUnmutedEvent(operator)
} else {
MemberUnmuteEvent(group.getMember(memberQQ), operator)
}
} else {
if (memberQQ == bot.qqAccount) {
BeingMutedEvent(durationSeconds, operator)
} else {
MemberMuteEvent(group.getMember(memberQQ), durationSeconds, operator)
}
}
} }
else -> error("Unsupported flag in 0x02DC packet. flag=$flag, remainning=${readBytes().toUHexString()}")
} }
} }
} }
\ No newline at end of file
...@@ -11,7 +11,6 @@ import net.mamoe.mirai.contact.* ...@@ -11,7 +11,6 @@ import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.BroadcastControllable import net.mamoe.mirai.event.BroadcastControllable
import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.getGroup import net.mamoe.mirai.getGroup
import net.mamoe.mirai.getQQ
import net.mamoe.mirai.message.* import net.mamoe.mirai.message.*
import net.mamoe.mirai.message.internal.readMessageChain import net.mamoe.mirai.message.internal.readMessageChain
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
...@@ -117,6 +116,9 @@ data class GroupMessage( ...@@ -117,6 +116,9 @@ data class GroupMessage(
override val message: MessageChain override val message: MessageChain
) : MessagePacket<Member, Group>() { ) : MessagePacket<Member, Group>() {
/*
01 00 09 01 00 06 66 61 69 6C 65 64 19 00 45 01 00 42 AA 02 3F 08 06 50 02 60 00 68 00 88 01 00 9A 01 31 08 0A 78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 C8 02 00 98 03 00 A0 03 02 B0 03 00 C0 03 00 D0 03 00 E8 03 02 8A 04 04 08 02 08 01 90 04 80 C8 10 0E 00 0E 01 00 04 00 00 08 E4 07 00 04 00 00 00 01 12 00 1E 02 00 09 E9 85 B1 E9 87 8E E6 98 9F 03 00 01 02 05 00 04 00 00 00 03 08 00 04 00 00 00 04
*/
override val subject: Group get() = group override val subject: Group get() = group
inline fun At.member(): Member = group.getMember(this.target) inline fun At.member(): Member = group.getMember(this.target)
......
...@@ -12,7 +12,6 @@ internal object CaptchaKey : DecrypterByteArray, DecrypterType<CaptchaKey> { ...@@ -12,7 +12,6 @@ internal object CaptchaKey : DecrypterByteArray, DecrypterType<CaptchaKey> {
override val value: ByteArray = TIMProtocol.key00BA override val value: ByteArray = TIMProtocol.key00BA
} }
@AnnotatedId(KnownPacketId.CAPTCHA)
internal object CaptchaPacket : PacketFactory<CaptchaPacket.CaptchaResponse, CaptchaKey>(CaptchaKey) { internal object CaptchaPacket : PacketFactory<CaptchaPacket.CaptchaResponse, CaptchaKey>(CaptchaKey) {
/** /**
* 请求验证码传输 * 请求验证码传输
......
...@@ -16,7 +16,6 @@ import net.mamoe.mirai.utils.io.writeQQ ...@@ -16,7 +16,6 @@ import net.mamoe.mirai.utils.io.writeQQ
/** /**
* 改变在线状态: "我在线上", "隐身" 等 * 改变在线状态: "我在线上", "隐身" 等
*/ */
@AnnotatedId(KnownPacketId.CHANGE_ONLINE_STATUS)
internal object ChangeOnlineStatusPacket : PacketFactory<ChangeOnlineStatusPacket.ChangeOnlineStatusResponse, NoDecrypter>(NoDecrypter) { internal object ChangeOnlineStatusPacket : PacketFactory<ChangeOnlineStatusPacket.ChangeOnlineStatusResponse, NoDecrypter>(NoDecrypter) {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
......
...@@ -13,7 +13,6 @@ import net.mamoe.mirai.utils.io.writeHex ...@@ -13,7 +13,6 @@ import net.mamoe.mirai.utils.io.writeHex
import net.mamoe.mirai.utils.io.writeQQ import net.mamoe.mirai.utils.io.writeQQ
@NoLog @NoLog
@AnnotatedId(KnownPacketId.HEARTBEAT)
internal object HeartbeatPacket : SessionPacketFactory<HeartbeatPacketResponse>() { internal object HeartbeatPacket : SessionPacketFactory<HeartbeatPacketResponse>() {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
...@@ -31,5 +30,4 @@ internal object HeartbeatPacket : SessionPacketFactory<HeartbeatPacketResponse>( ...@@ -31,5 +30,4 @@ internal object HeartbeatPacket : SessionPacketFactory<HeartbeatPacketResponse>(
} }
@NoLog @NoLog
@AnnotatedId(KnownPacketId.HEARTBEAT)
internal object HeartbeatPacketResponse : Packet, Subscribable internal object HeartbeatPacketResponse : Packet, Subscribable
\ No newline at end of file
...@@ -44,7 +44,6 @@ internal inline class SubmitPasswordResponseDecrypter(private val privateKey: Pr ...@@ -44,7 +44,6 @@ internal inline class SubmitPasswordResponseDecrypter(private val privateKey: Pr
/** /**
* 提交密码 * 提交密码
*/ */
@AnnotatedId(KnownPacketId.LOGIN)
internal object SubmitPasswordPacket : PacketFactory<SubmitPasswordPacket.LoginResponse, SubmitPasswordResponseDecrypter>(SubmitPasswordResponseDecrypter) { internal object SubmitPasswordPacket : PacketFactory<SubmitPasswordPacket.LoginResponse, SubmitPasswordResponseDecrypter>(SubmitPasswordResponseDecrypter) {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
......
...@@ -23,7 +23,6 @@ internal inline class SKey( ...@@ -23,7 +23,6 @@ internal inline class SKey(
* 请求 `SKey` * 请求 `SKey`
* SKey 用于 http api * SKey 用于 http api
*/ */
@AnnotatedId(KnownPacketId.S_KEY)
internal object RequestSKeyPacket : SessionPacketFactory<SKey>() { internal object RequestSKeyPacket : SessionPacketFactory<SKey>() {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
......
...@@ -9,7 +9,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.* ...@@ -9,7 +9,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.localIpAddress import net.mamoe.mirai.utils.localIpAddress
@AnnotatedId(KnownPacketId.SESSION_KEY)
internal object RequestSessionPacket : PacketFactory<RequestSessionPacket.SessionKeyResponse, SessionResponseDecryptionKey>(SessionResponseDecryptionKey) { internal object RequestSessionPacket : PacketFactory<RequestSessionPacket.SessionKeyResponse, SessionResponseDecryptionKey>(SessionResponseDecryptionKey) {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
......
...@@ -20,7 +20,6 @@ internal object TouchKey : DecrypterByteArray, DecrypterType<TouchKey> { ...@@ -20,7 +20,6 @@ internal object TouchKey : DecrypterByteArray, DecrypterType<TouchKey> {
* *
* @author Him188moe * @author Him188moe
*/ */
@AnnotatedId(KnownPacketId.TOUCH)
internal object TouchPacket : PacketFactory<TouchPacket.TouchResponse, TouchKey>(TouchKey) { internal object TouchPacket : PacketFactory<TouchPacket.TouchResponse, TouchKey>(TouchKey) {
operator fun invoke( operator fun invoke(
bot: UInt, bot: UInt,
...@@ -45,7 +44,7 @@ internal object TouchPacket : PacketFactory<TouchPacket.TouchResponse, TouchKey> ...@@ -45,7 +44,7 @@ internal object TouchPacket : PacketFactory<TouchPacket.TouchResponse, TouchKey>
} }
} }
internal sealed class TouchResponse : Packet { internal sealed class TouchResponse : Packet {
class OK( class OK(
var loginTime: Int, var loginTime: Int,
val loginIP: String, val loginIP: String,
......
...@@ -62,6 +62,12 @@ class BotConfiguration : CoroutineContext.Element { ...@@ -62,6 +62,12 @@ class BotConfiguration : CoroutineContext.Element {
* 验证码处理器 * 验证码处理器
*/ */
var captchaSolver: CaptchaSolver = DefaultCaptchaSolver var captchaSolver: CaptchaSolver = DefaultCaptchaSolver
/**
* 登录完成后几秒会收到好友消息的历史记录,
* 这些历史记录不会触发事件.
* 这个选项为是否把这些记录添加到日志
*/
var logPreviousMessages: Boolean = false
companion object Key : CoroutineContext.Key<BotConfiguration> { companion object Key : CoroutineContext.Key<BotConfiguration> {
/** /**
......
@file:Suppress("unused", "UNUSED_PARAMETER")
package net.mamoe.mirai.utils
import net.mamoe.mirai.contact.Group
/**
* 在获取 [Group] 对象等操作时可能出现的异常
*/
class GroupNotFoundException : Exception {
constructor()
constructor(message: String?)
constructor(message: String?, cause: Throwable?)
constructor(cause: Throwable?)
}
open class MiraiInternalException : Exception {
constructor()
constructor(message: String?)
constructor(message: String?, cause: Throwable?)
constructor(cause: Throwable?)
}
\ No newline at end of file
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE") @file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE", "unused")
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
......
...@@ -54,6 +54,17 @@ internal fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket { ...@@ -54,6 +54,17 @@ internal fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket {
return bytes.toReadPacket() return bytes.toReadPacket()
} }
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this"))
internal inline fun <R> ByteReadPacket.debugPrintIfFail(name: String = "", block: ByteReadPacket.() -> R): R {
val bytes = this.readBytes()
try {
return block(bytes.toReadPacket())
} catch (e: Throwable) {
DebugLogger.debug("Error in ByteReadPacket $name=" + bytes.toUHexString())
throw e
}
}
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this")) @Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this"))
internal fun ByteReadPacket.debugColorizedPrint(name: String = "", ignoreUntilFirstConst: Boolean = false): ByteReadPacket { internal fun ByteReadPacket.debugColorizedPrint(name: String = "", ignoreUntilFirstConst: Boolean = false): ByteReadPacket {
val bytes = this.readBytes() val bytes = this.readBytes()
......
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") @file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS", "NOTHING_TO_INLINE")
package net.mamoe.mirai.utils.io package net.mamoe.mirai.utils.io
...@@ -95,7 +95,7 @@ fun Input.readTLVMap(expectingEOF: Boolean = false, tagSize: Int = 1): MutableMa ...@@ -95,7 +95,7 @@ fun Input.readTLVMap(expectingEOF: Boolean = false, tagSize: Int = 1): MutableMa
} }
/** /**
* 读扁平的 tag-UVarInt map. 重复的 tag 将不会只保留最后一个 * 读扁平的 tag-UVarInt map. 重复的 tag 将只保留最后一个
* *
* tag: UByte * tag: UByte
* value: UVarint * value: UVarint
...@@ -140,10 +140,11 @@ fun Map<UInt, ByteArray>.printTLVMap(name: String = "", keyLength: Int = 1) = ...@@ -140,10 +140,11 @@ fun Map<UInt, ByteArray>.printTLVMap(name: String = "", keyLength: Int = 1) =
} }
}) })
@Suppress("NOTHING_TO_INLINE")
internal inline fun unsupported(message: String? = null): Nothing = error(message ?: "Unsupported") internal inline fun unsupported(message: String? = null): Nothing = error(message ?: "Unsupported")
@Suppress("NOTHING_TO_INLINE") internal inline fun ByteReadPacket.unsupportedFlag(name: String, flag: String): Nothing = error("Unsupported flag of $name. flag=$flag, remaining=${readBytes().toUHexString()}")
internal inline fun ByteReadPacket.unsupportedType(name: String, type: String): Nothing = error("Unsupported type of $name. type=$type, remaining=${readBytes().toUHexString()}")
internal inline fun illegalArgument(message: String? = null): Nothing = error(message ?: "Illegal argument passed") internal inline fun illegalArgument(message: String? = null): Nothing = error(message ?: "Illegal argument passed")
@JvmName("printTLVStringMap") @JvmName("printTLVStringMap")
......
package net.mamoe.mirai.contact
import net.mamoe.mirai.test.shouldBeEqualTo
import org.junit.Test
import kotlin.random.Random
internal class GroupIdConversionsKtTest {
@UseExperimental(ExperimentalUnsignedTypes::class)
@Test
fun toInternalId() {
repeat(1000000) { _ ->
val it = Random.nextInt()
try {
GroupId(it.toUInt()).toInternalId() shouldBeEqualTo GroupId(it.toUInt()).toInternalIdOld()
} catch (e: Throwable) {
println(it)
throw e
}
}
}
@UseExperimental(ExperimentalUnsignedTypes::class)
@Test
fun toId() {
repeat(1000000) { _ ->
val it = Random.nextInt()
try {
GroupInternalId(it.toUInt()).toId() shouldBeEqualTo GroupInternalId(it.toUInt()).toIdOld()
} catch (e: Throwable) {
println(it)
throw e
}
}
}
}
@UseExperimental(ExperimentalUnsignedTypes::class)
private fun GroupId.toInternalIdOld(): GroupInternalId {//求你别出错
val left: Long = this.value.toString().let {
if (it.length <= 6) {
return GroupInternalId(this.value)
}
it.substring(0, it.length - 6).toLong()
}
val right: Long = this.value.toString().let {
it.substring(it.length - 6).toLong()
}
return GroupInternalId(
when (left) {
in 1..10 -> {
((left + 202).toString() + right.toString()).toUInt()
}
in 11..19 -> {
((left + 469).toString() + right.toString()).toUInt()
}
in 20..66 -> {
((left + 208).toString() + right.toString()).toUInt()
}
in 67..156 -> {
((left + 1943).toString() + right.toString()).toUInt()
}
in 157..209 -> {
((left + 199).toString() + right.toString()).toUInt()
}
in 210..309 -> {
((left + 389).toString() + right.toString()).toUInt()
}
in 310..499 -> {
((left + 349).toString() + right.toString()).toUInt()
}
else -> this.value
}
)
}
@UseExperimental(ExperimentalUnsignedTypes::class)
private fun GroupInternalId.toIdOld(): GroupId = with(value) {
//求你别出错
var left: UInt = this.toString().let {
if (it.length <= 6) {
return GroupId(value)
}
it.substring(0 until it.length - 6).toUInt()
}
return GroupId(when (left.toInt()) {
in 203..212 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 6).toUInt()
}
((left - 202u).toString() + right.toString()).toUInt()
}
in 480..488 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 6).toUInt()
}
((left - 469u).toString() + right.toString()).toUInt()
}
in 2100..2146 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 208u).toString() + right.toString()).toUInt()
}
in 2010..2099 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 6).toUInt()
}
((left - 1943u).toString() + right.toString()).toUInt()
}
in 2147..2199 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 199u).toString() + right.toString()).toUInt()
}
in 4100..4199 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 389u).toString() + right.toString()).toUInt()
}
in 3800..3989 -> {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 349u).toString() + right.toString()).toUInt()
}
else -> value
})
}
\ No newline at end of file
@file:Suppress("RemoveRedundantBackticks", "NonAsciiCharacters")
package net.mamoe.mirai.utils
import kotlinx.coroutines.*
import net.mamoe.mirai.test.shouldBeEqualTo
import net.mamoe.mirai.test.shouldBeTrue
import org.junit.Test
import kotlin.system.exitProcess
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@MiraiExperimentalAPI
internal class LockFreeLinkedListTest {
init {
GlobalScope.launch {
delay(30 * 1000)
exitProcess(-100)
}
}
@Test
fun addAndGetSingleThreaded() {
val list = LockFreeLinkedList<Int>()
list.addLast(1)
list.addLast(2)
list.addLast(3)
list.addLast(4)
list.size shouldBeEqualTo 4
}
@Test
fun addAndGetConcurrent() = runBlocking {
//withContext(Dispatchers.Default){
val list = LockFreeLinkedList<Int>()
list.concurrentDo(1000, 10) { addLast(1) }
list.size shouldBeEqualTo 1000 * 10
list.concurrentDo(100, 10) {
remove(1).shouldBeTrue()
}
list.size shouldBeEqualTo 1000 * 10 - 100 * 10
//}
}
@Test
fun addAndGetMassConcurrentAccess() = runBlocking {
val list = LockFreeLinkedList<Int>()
val addJob = async { list.concurrentDo(2, 30000) { addLast(1) } }
//delay(1) // let addJob fly
if (addJob.isCompleted) {
error("Number of elements are not enough")
}
val foreachJob = async {
list.concurrentDo(1, 10000) {
forEach { it + it }
}
}
val removeLastJob = async {
list.concurrentDo(1, 15000) {
removeLast() shouldBeEqualTo 1
}
}
val removeFirstJob = async {
list.concurrentDo(1, 10000) {
removeFirst() shouldBeEqualTo 1
}
}
val addJob2 = async {
list.concurrentDo(1, 5000) {
addLast(1)
}
}
val removeExactJob = launch {
list.concurrentDo(3, 1000) {
remove(1).shouldBeTrue()
}
}
val filteringGetOrAddJob = launch {
list.concurrentDo(1, 10000) {
filteringGetOrAdd({ it == 2 }, { 1 })
}
}
joinAll(addJob, addJob2, foreachJob, removeLastJob, removeFirstJob, removeExactJob, filteringGetOrAddJob)
list.size shouldBeEqualTo 2 * 30000 - 1 * 15000 - 1 * 10000 + 1 * 5000 - 3 * 1000 + 1 * 10000
}
@Test
fun removeWhileForeach() {
val list = LockFreeLinkedList<Int>()
repeat(10) { list.addLast(it) }
list.forEach {
list.remove(it + 1)
}
list.peekFirst() shouldBeEqualTo 0
}
@Test
fun remove() {
val list = LockFreeLinkedList<Int>()
assertFalse { list.remove(1) }
assertEquals(0, list.size)
list.addLast(1)
assertTrue { list.remove(1) }
assertEquals(0, list.size)
list.addLast(2)
assertFalse { list.remove(1) }
assertEquals(1, list.size)
}
@Test
fun addAll() {
val list = LockFreeLinkedList<Int>()
list.addAll(listOf(1, 2, 3, 4, 5))
list.size shouldBeEqualTo 5
}
@Test
fun clear() {
val list = LockFreeLinkedList<Int>()
list.addAll(listOf(1, 2, 3, 4, 5))
list.size shouldBeEqualTo 5
list.clear()
list.size shouldBeEqualTo 0
}
@UseExperimental(ExperimentalUnsignedTypes::class)
@Test
fun withInlineClassElements() {
val list = LockFreeLinkedList<UInt>()
list.addAll(listOf(1u, 2u, 3u, 4u, 5u))
list.size shouldBeEqualTo 5
list.toString() shouldBeEqualTo "[1, 2, 3, 4, 5]"
}
@Test
fun `filteringGetOrAdd when add`() {
val list = LockFreeLinkedList<Int>()
list.addAll(listOf(1, 2, 3, 4, 5))
val value = list.filteringGetOrAdd({ it == 6 }, { 6 })
println("Check value")
value shouldBeEqualTo 6
println("Check size")
println(list.getLinkStructure())
list.size shouldBeEqualTo 6
}
@Test
fun `filteringGetOrAdd when get`() {
val list = LockFreeLinkedList<Int>()
list.addAll(listOf(1, 2, 3, 4, 5))
val value = list.filteringGetOrAdd({ it == 2 }, { 2 })
println("Check value")
value shouldBeEqualTo 2
println("Check size")
println(list.getLinkStructure())
list.size shouldBeEqualTo 5
}
@Test
fun `filteringGetOrAdd when empty`() {
val list = LockFreeLinkedList<Int>()
val value = list.filteringGetOrAdd({ it == 2 }, { 2 })
println("Check value")
value shouldBeEqualTo 2
println("Check size")
println(list.getLinkStructure())
list.size shouldBeEqualTo 1
}
/*
@Test
fun indexOf() {
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 2, 3, 3)
assertEquals(0, list.indexOf(1))
assertEquals(2, list.indexOf(3))
assertEquals(-1, list.indexOf(4))
}
@Test
fun iterator() {
var list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(2)
list.forEach {
it shouldBeEqualTo 2
}
list = lockFreeLinkedListOf(1, 2)
list.joinToString { it.toString() } shouldBeEqualTo "1, 2"
list = lockFreeLinkedListOf(1, 2)
val iterator = list.iterator()
iterator.remove()
var reached = false
for (i in iterator) {
i shouldBeEqualTo 2
reached = true
}
reached shouldBeEqualTo true
list.joinToString { it.toString() } shouldBeEqualTo "2"
iterator.remove()
assertFailsWith<NoSuchElementException> { iterator.remove() }
}
@Test
fun `lastIndexOf of exact 1 match at first`() {
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(2, 1)
list.lastIndexOf(2) shouldBeEqualTo 0
}
@Test
fun `lastIndexOf of exact 1 match`() {
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 2)
list.lastIndexOf(2) shouldBeEqualTo 1
}
@Test
fun `lastIndexOf of multiply matches`() {
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 2, 2)
list.lastIndexOf(2) shouldBeEqualTo 2
}
@Test
fun `lastIndexOf of no match`() {
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(2)
list.lastIndexOf(3) shouldBeEqualTo -1
}
@Test
fun `lastIndexOf of many elements`() {
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 4, 2, 3, 4, 5)
list.lastIndexOf(4) shouldBeEqualTo 4
}
*/
}
@UseExperimental(ExperimentalCoroutinesApi::class)
@MiraiExperimentalAPI
internal suspend inline fun <E : LockFreeLinkedList<*>> E.concurrentDo(numberOfCoroutines: Int, times: Int, crossinline todo: E.() -> Unit) =
coroutineScope {
repeat(numberOfCoroutines) {
launch(start = CoroutineStart.UNDISPATCHED) {
repeat(times) {
todo()
}
}
}
}
\ No newline at end of file
...@@ -41,7 +41,7 @@ fun DependencyHandlerScope.ktor(id: String, version: String) = "io.ktor:ktor-$id ...@@ -41,7 +41,7 @@ fun DependencyHandlerScope.ktor(id: String, version: String) = "io.ktor:ktor-$id
dependencies { dependencies {
implementation(project(":mirai-core")) implementation(project(":mirai-core"))
runtimeOnly(files("../mirai-core/build/classes/kotlin/jvm/main")) // classpath is not added correctly by IDE // runtimeOnly(files("../mirai-core/build/classes/kotlin/jvm/main")) // classpath is not added correctly by IDE
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion") implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
......
...@@ -3,7 +3,7 @@ apply plugin: "java" ...@@ -3,7 +3,7 @@ apply plugin: "java"
dependencies { dependencies {
api project(":mirai-core") api project(":mirai-core")
runtime files("../../mirai-core/build/classes/kotlin/jvm/main") // classpath is not set correctly by IDE // runtime files("../../mirai-core/build/classes/kotlin/jvm/main") // classpath is not set correctly by IDE
api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: coroutinesVersion api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: coroutinesVersion
} }
...@@ -4,8 +4,8 @@ apply plugin: "application" ...@@ -4,8 +4,8 @@ apply plugin: "application"
dependencies { dependencies {
api project(":mirai-core") api project(":mirai-core")
runtime files("../../mirai-core/build/classes/kotlin/jvm/main") // classpath is not set correctly by IDE //runtime files("../../mirai-core/build/classes/kotlin/jvm/main") // classpath is not set correctly by IDE
//runtime files("../../mirai-core/build/classes/atomicfu/jvm/main") // classpath is not set correctly by IDE
implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion
implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: coroutinesVersion implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: coroutinesVersion
......
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