1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-04-14 23:20:49 +08:00

Merge remote-tracking branch 'origin/master'

This commit is contained in:
jiahua.liu 2020-02-13 00:11:31 +08:00
commit aca459f60d
12 changed files with 110 additions and 63 deletions
CHANGELOG.mdgradle.properties
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network
QQAndroidBotNetworkHandler.kt
protocol/packet/chat/receive
mirai-core/src/commonMain/kotlin/net.mamoe.mirai
settings.gradle

View File

@ -2,10 +2,12 @@
开发版本. 频繁更新, 不保证高稳定性
## `0.13.0` *On the way*
## `0.13.0` 2020/2/12
### mirai-core
1. 修改 BotFactory, 添加 `context` 参数.
2. currentTimeMillis 减少不必要对象创建
3. 优化无锁链表性能 (大幅提升 `addAll` 性能)
### mirai-core-qqanroid
安卓协议发布, 基于最新 QQ, 版本 `8.2.0`
@ -14,7 +16,8 @@
- 消息: 文字消息, 图片消息(含表情消息), 群员 At.
- 列表: 群列表, 群员列表, 好友列表均已稳定.
- 群操作: 查看和修改群名, 查看和修改群属性(含全体禁言, 坦白说, 自动批准加入, 匿名聊天, 允许成员拉人), 设置和解除成员禁言, 查看和修改成员名片, 踢出成员.
- 事件: 接受群消息和好友消息并解析
- 消息事件: 接受群消息和好友消息并解析
- 群事件: 群员加入, 群员离开, 禁言和解除禁言, 群属性(含全体禁言, 坦白说, 匿名聊天, 允许成员拉人)改动.
### mirai-api-http
HTTP API 已完成, by [@ryoii](https://github.com/ryoii).

View File

@ -1,7 +1,7 @@
# style guide
kotlin.code.style=official
# config
mirai_version=0.12.0
mirai_version=0.13.0
kotlin.incremental.multiplatform=true
kotlin.parallel.tasks.in.project=true
# kotlin

View File

@ -47,9 +47,10 @@ import kotlin.time.ExperimentalTime
internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler() {
override val bot: QQAndroidBot by bot.unsafeWeakRef()
override val supervisor: CompletableJob = SupervisorJob(bot.coroutineContext[Job])
override val logger: MiraiLogger get() = bot.configuration.networkLoggerSupplier(this)
override val coroutineContext: CoroutineContext = bot.coroutineContext + CoroutineExceptionHandler { _, throwable ->
bot.logger.error("Exception in NetworkHandler", throwable)
logger.error("Exception in NetworkHandler", throwable)
}
private lateinit var channel: PlatformSocket
@ -62,7 +63,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
channel.connect("113.96.13.208", 8080)
this.launch(CoroutineName("Incoming Packet Receiver")) { processReceive() }
// bot.logger.info("Trying login")
// logger.info("Trying login")
var response: WtLogin.Login.LoginPacketResponse = WtLogin.Login.SubCommand9(bot.client).sendAndExpect()
mainloop@ while (true) {
when (response) {
@ -104,7 +105,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
is WtLogin.Login.LoginPacketResponse.Success -> {
bot.logger.info("Login successful")
logger.info("Login successful")
break@mainloop
}
}
@ -118,7 +119,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
override suspend fun init(): Unit = coroutineScope {
this@QQAndroidBotNetworkHandler.subscribeAlways<BotOfflineEvent> {
if (this@QQAndroidBotNetworkHandler.bot == this.bot) {
this.bot.logger.error("被挤下线")
logger.error("被挤下线")
close()
}
}
@ -130,7 +131,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
val friendListJob = launch {
try {
bot.logger.info("开始加载好友信息")
logger.info("开始加载好友信息")
var currentFriendCount = 0
var totalFriendCount: Short
while (true) {
@ -149,21 +150,21 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
currentFriendCount++
}
}
bot.logger.verbose("正在加载好友列表 ${currentFriendCount}/${totalFriendCount}")
logger.verbose("正在加载好友列表 ${currentFriendCount}/${totalFriendCount}")
if (currentFriendCount >= totalFriendCount) {
break
}
// delay(200)
}
bot.logger.info("好友列表加载完成, 共 ${currentFriendCount}")
logger.info("好友列表加载完成, 共 ${currentFriendCount}")
} catch (e: Exception) {
bot.logger.error("加载好友列表失败|一般这是由于加载过于频繁导致/将以热加载方式加载好友列表")
logger.error("加载好友列表失败|一般这是由于加载过于频繁导致/将以热加载方式加载好友列表")
}
}
val groupJob = launch {
try {
bot.logger.info("开始加载群组列表与群成员列表")
logger.info("开始加载群组列表与群成员列表")
val troopListData = FriendList.GetTroopListSimplify(bot.client)
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
@ -196,15 +197,15 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
)
)
} catch (e: Exception) {
bot.logger.error("${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试")
bot.logger.error(e)
logger.error("${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试")
logger.error(e)
}
}
}
bot.logger.info("群组列表与群成员加载完成, 共 ${troopListData.groups.size}")
logger.info("群组列表与群成员加载完成, 共 ${troopListData.groups.size}")
} catch (e: Exception) {
bot.logger.error("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表")
bot.logger.error(e)
logger.error("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表")
logger.error(e)
}
}
@ -317,7 +318,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
if (packet is CancellableEvent && packet.isCancelled) return
}
bot.logger.info("Received: ${packet.toString().replace("\n", """\n""").replace("\r", "")}")
logger.info("Received: ${packet.toString().replace("\n", """\n""").replace("\r", "")}")
packetFactory?.run {
when (this) {
@ -416,13 +417,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
bot.tryReinitializeNetworkHandler(e)
return
} catch (e: ReadPacketInternalException) {
bot.logger.error("Socket channel read failed: ${e.message}")
logger.error("Socket channel read failed: ${e.message}")
bot.tryReinitializeNetworkHandler(e)
return
} catch (e: CancellationException) {
return
} catch (e: Throwable) {
bot.logger.error("Caught unexpected exceptions", e)
logger.error("Caught unexpected exceptions", e)
bot.tryReinitializeNetworkHandler(e)
return
}
@ -438,7 +439,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
* 发送一个包, 但不期待任何返回.
*/
suspend fun OutgoingPacket.sendWithoutExpect() {
bot.logger.info("Send: ${this.commandName}")
logger.info("Send: ${this.commandName}")
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
channel.send(delegate)
}
@ -461,7 +462,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
channel.send(delegate)
}
bot.logger.info("Send: ${this.commandName}")
logger.info("Send: ${this.commandName}")
return withTimeoutOrNull(timeoutMillis) {
@Suppress("UNCHECKED_CAST")
handler.await() as E
@ -480,7 +481,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
channel.send(data, 0, length)
}
bot.logger.info("Send: ${this.commandName}")
logger.info("Send: ${this.commandName}")
return withTimeoutOrNull(timeoutMillis) {
@Suppress("UNCHECKED_CAST")
handler.await() as E

View File

@ -132,9 +132,9 @@ internal class OnlinePush {
bot.getGroupByUin(groupUin).let { group ->
val member = group[target] as MemberImpl
this.discardExact(1)
return MemberLeaveEvent.Kick(member, group.members[readUInt().toLong()].also {
group.members.delegate.remove(it)
})
return MemberLeaveEvent.Kick(member.also {
group.members.delegate.remove(member)
}, group.members[readUInt().toLong()])
}
}
}
@ -187,14 +187,18 @@ internal class OnlinePush {
)
}
} else {
if (target == bot.uin) {
}
val member = group[target]
if (time == 0) {
MemberUnmuteEvent(operator = operator, member = member)
return if (target == bot.uin) {
if (time == 0) {
BotUnmuteEvent(operator)
} else
BotMuteEvent(durationSeconds = time, operator = operator)
} else {
MemberMuteEvent(operator = operator, member = member, durationSeconds = time)
val member = group[target]
if (time == 0) {
MemberUnmuteEvent(operator = operator, member = member)
} else {
MemberMuteEvent(operator = operator, member = member, durationSeconds = time)
}
}
}
}

View File

@ -40,7 +40,7 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
@UseExperimental(RawAccountIdUse::class)
override val uin: Long
get() = account.id
final override val logger: MiraiLogger by lazy { configuration.logger ?: DefaultLogger("Bot($uin)").also { configuration.logger = it } }
final override val logger: MiraiLogger by lazy { configuration.botLoggerSupplier(this) }
init {
instances.addLast(this.weakRef())

View File

@ -79,6 +79,12 @@ suspend fun <E : Event> E.broadcast(): E = apply {
this@broadcast.broadcastInternal() // inline, no extra cost
}
/**
* 设置为 `true` 以关闭事件.
* 所有的 `subscribe` 都能正常添加到监听器列表, 但所有的广播都会直接返回.
*/
var EventDisabled = false
/**
* 可控制是否需要广播这个事件包
*/

View File

@ -129,12 +129,27 @@ data class BotGroupPermissionChangeEvent(
*/
data class BotMuteEvent(
val durationSeconds: Int,
override val group: Group,
/**
* 操作人. null 则为机器人操作
* 操作人.
*/
val operator: Member?
) : GroupEvent, Packet, BotPassiveEvent
val operator: Member
) : GroupEvent, Packet, BotPassiveEvent {
override val group: Group
get() = operator.group
}
/**
* Bot 被取消禁言
*/
data class BotUnmuteEvent(
/**
* 操作人.
*/
val operator: Member
) : GroupEvent, Packet, BotPassiveEvent {
override val group: Group
get() = operator.group
}
/**
* Bot 加入了一个新群

View File

@ -11,22 +11,17 @@ package net.mamoe.mirai.event.internal
import kotlinx.coroutines.*
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.event.EventDisabled
import net.mamoe.mirai.event.Listener
import net.mamoe.mirai.event.ListeningStatus
import net.mamoe.mirai.utils.LockFreeLinkedList
import net.mamoe.mirai.utils.MiraiDebugAPI
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.logStacktrace
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext
import kotlin.jvm.JvmField
import kotlin.reflect.KClass
/**
* 设置为 `true` 以关闭事件.
* 所有的 `subscribe` 都能正常添加到监听器列表, 但所有的广播都会直接返回.
*/
var EventDisabled = false
val EventLogger: MiraiLoggerWithSwitch = DefaultLogger("Event").withSwitch(false)
@PublishedApi
internal fun <L : Listener<E>, E : Event> KClass<out E>.subscribeInternal(listener: L): L {
@ -108,6 +103,8 @@ internal object EventListenerManager {
internal suspend inline fun Event.broadcastInternal() {
if (EventDisabled) return
EventLogger.info { "Event broadcast: $this" }
callAndRemoveIfRequired(this::class.listeners())
var supertypes = this::class.supertypes

View File

@ -17,6 +17,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import net.mamoe.mirai.Bot
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.io.PlatformDatagramChannel
/**
@ -47,6 +48,11 @@ abstract class BotNetworkHandler : CoroutineScope {
*/
abstract val supervisor: CompletableJob
/**
* logger
*/
abstract val logger: MiraiLogger
/**
* 依次尝试登录到可用的服务器. 在任一服务器登录完成后返回.
* 本函数将挂起直到登录成功.

View File

@ -11,6 +11,7 @@ package net.mamoe.mirai.utils
import kotlinx.io.core.IoBuffer
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.BotNetworkHandler
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.JvmStatic
@ -38,7 +39,11 @@ class BotConfiguration {
/**
* 日志记录器
*/
var logger: MiraiLogger? = null
var botLoggerSupplier: ((Bot) -> MiraiLogger) = { DefaultLogger("Bot(${it.uin})") }
/**
* 网络层日志构造器
*/
var networkLoggerSupplier: ((BotNetworkHandler) -> MiraiLogger) = { DefaultLogger("Network(${it.bot.uin})") }
/**
* 设备信息覆盖
*/

View File

@ -12,14 +12,12 @@
package net.mamoe.mirai.utils
import io.ktor.client.HttpClient
import io.ktor.util.date.GMTDate
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.io.core.toByteArray
/**
* 时间戳
*/
inline val currentTimeMillis: Long get() = GMTDate().timestamp
expect val currentTimeMillis: Long
inline val currentTimeSeconds: Long get() = currentTimeMillis / 1000

View File

@ -21,15 +21,19 @@ pluginManagement {
rootProject.name = 'mirai'
def keyProps = new Properties()
def keyFile = file("local.properties")
if (keyFile.exists()) keyFile.withInputStream { keyProps.load(it) }
if (!keyProps.getProperty("sdk.dir", "").isEmpty()) {
include(':mirai-demos:mirai-demo-android')
project(':mirai-demos:mirai-demo-android').projectDir = file('mirai-demos/mirai-demo-android')
} else {
println("Android SDK 可能未安装. \n将不会加载模块 `mirai-demo-android`, 但这并不影响其他 demo 的加载 ")
println("Android SDK might not be installed. \nModule `mirai-demo-android` will not be included, but other demos will not be influenced")
try {
def keyProps = new Properties()
def keyFile = file("local.properties")
if (keyFile.exists()) keyFile.withInputStream { keyProps.load(it) }
if (!keyProps.getProperty("sdk.dir", "").isEmpty()) {
include(':mirai-demos:mirai-demo-android')
project(':mirai-demos:mirai-demo-android').projectDir = file('mirai-demos/mirai-demo-android')
} else {
println("Android SDK 可能未安装. \n将不会加载模块 `mirai-demo-android`, 但这并不影响其他 demo 的加载 ")
println("Android SDK might not be installed. \nModule `mirai-demo-android` will not be included, but other demos will not be influenced")
}
} catch (Exception e) {
e.printStackTrace()
}
include(':mirai-core')
@ -47,10 +51,18 @@ include(':mirai-plugins')
include(':mirai-plugins:image-sender')
def javaVersion = System.getProperty("java.version")
if (javaVersion.substring(0, javaVersion.indexOf(".")).toInteger() >= 11) {
include(':mirai-debug')
} else {
println("当前使用的 JDK 版本为 ${System.getProperty("java.version")}, 最低需要 JDK 11 才能引入模块 `:mirai-debug`")
def versionPos = javaVersion.indexOf(".")
if (versionPos==-1) versionPos = javaVersion.indexOf("-")
if (versionPos==-1){
println("jdk version unknown")
}else{
def javaVersionNum = javaVersion.substring(0, versionPos).toInteger()
if (javaVersionNum >= 11) {
println("jdk版本为 "+ javaVersionNum)
include(':mirai-debug')
} else {
println("当前使用的 JDK 版本为 ${System.getProperty("java.version")}, 最低需要 JDK 11 才能引入模块 `:mirai-debug`")
}
}
project(':mirai-demos:mirai-demo-1').projectDir = file('mirai-demos/mirai-demo-1')