1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-03-26 07:20:09 +08:00

Merge remote-tracking branch 'origin/master'

This commit is contained in:
jiahua.liu 2020-01-30 20:39:42 +08:00
commit b9eacbcf1a
12 changed files with 99 additions and 74 deletions
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid
mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network
mirai-core/src
androidMain/kotlin/net/mamoe/mirai/utils
commonMain/kotlin/net.mamoe.mirai
jvmMain/kotlin/net/mamoe/mirai/utils
mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo

View File

@ -26,6 +26,9 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
TODO("not implemented")
}
override val isOnline: Boolean
get() = true
override suspend fun queryProfile(): Profile {
TODO("not implemented")
}
@ -40,11 +43,16 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
}
internal class MemberImpl(bot: QQAndroidBot, group: Group, override val coroutineContext: CoroutineContext, override val id: Long) : ContactImpl(), Member {
override val group: Group by group.unsafeWeakRef()
internal class MemberImpl(
qq: QQImpl,
group: GroupImpl,
override val coroutineContext: CoroutineContext
) : ContactImpl(), Member, QQ by qq {
override val group: GroupImpl by group.unsafeWeakRef()
val qq: QQImpl by qq.unsafeWeakRef()
override val permission: MemberPermission
get() = TODO("not implemented")
override val bot: QQAndroidBot by bot.unsafeWeakRef()
override suspend fun mute(durationSeconds: Int): Boolean {
TODO("not implemented")
@ -54,26 +62,6 @@ internal class MemberImpl(bot: QQAndroidBot, group: Group, override val coroutin
TODO("not implemented")
}
override suspend fun queryProfile(): Profile {
TODO("not implemented")
}
override suspend fun queryPreviousNameList(): PreviousNameList {
TODO("not implemented")
}
override suspend fun queryRemark(): FriendNameRemark {
TODO("not implemented")
}
override suspend fun sendMessage(message: MessageChain) {
TODO("not implemented")
}
override suspend fun uploadImage(image: ExternalImage): ImageId {
TODO("not implemented")
}
}
@ -89,7 +77,7 @@ internal class GroupImpl(bot: QQAndroidBot, override val coroutineContext: Corou
override val members: ContactList<Member> = ContactList(LockFreeLinkedList())
override fun getMember(id: Long): Member =
members.delegate.filteringGetOrAdd({ it.id == id }, { MemberImpl(bot as QQAndroidBot, this, coroutineContext, id) })
members.delegate.filteringGetOrAdd({ it.id == id }, { MemberImpl(bot.getQQ(id) as QQImpl, this, coroutineContext) })
override suspend fun updateGroupInfo(): GroupInfo {
TODO("not implemented")

View File

@ -14,17 +14,11 @@ import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.GetFriendListReq
import net.mamoe.mirai.qqandroid.network.protocol.packet.KnownPacketFactories
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.io.*
import kotlin.coroutines.CoroutineContext
@ -56,7 +50,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
is Captcha -> when (response) {
is Captcha.Picture -> {
var result = bot.configuration.loginSolver.onSolvePicCaptcha(bot, response.data)
var result = response.data.withUse {
bot.configuration.loginSolver.onSolvePicCaptcha(bot, this)
}
if (result == null || result.length != 4) {
//refresh captcha
result = "ABCD"
@ -325,19 +321,33 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
/**
* 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms)
*/
suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timoutMillis: Long = 3000): E {
suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 3000, retry: Int = 1): E {
require(timeoutMillis > 0) { "timeoutMillis must > 0" }
require(retry >= 0) { "retry must >= 0" }
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
packetListeners.addLast(handler)
bot.logger.info("Send: ${this.commandName}")
var lastException: Exception? = null
repeat(retry + 1) {
try {
return doSendAndReceive(timeoutMillis, handler)
} catch (e: Exception) {
lastException = e
}
}
throw lastException!!
}
private suspend inline fun <E : Packet> OutgoingPacket.doSendAndReceive(timeoutMillis: Long = 3000, handler: PacketListener): E {
channel.send(delegate)
return withTimeoutOrNull(timoutMillis) {
return withTimeoutOrNull(timeoutMillis) {
@Suppress("UNCHECKED_CAST")
handler.await() as E
// 不要 `withTimeout`. timeout 的异常会不知道去哪了.
} ?: net.mamoe.mirai.qqandroid.utils.inline {
packetListeners.remove(handler)
error("timeout when sending ${commandName}")
error("timeout when sending $commandName")
}
}

View File

@ -139,6 +139,9 @@ internal class QQImpl @PublishedApi internal constructor(bot: TIMPCBot, override
}
}
override val isOnline: Boolean
get() = true
override suspend fun queryProfile(): Profile = withTIMPCBot {
RequestProfileDetailsPacket(bot.uin, id, sessionKey).sendAndExpect<RequestProfileDetailsResponse>().profile
}

View File

@ -15,12 +15,7 @@ actual var defaultLoginSolver: LoginSolver = object : LoginSolver() {
error("should be implemented manually by you")
}
override suspend fun onGetPhoneNumber(): String {
override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
error("should be implemented manually by you")
}
override suspend fun onGetSMSVerifyCode(): String {
error("should be implemented manually by you")
}
}

View File

@ -34,6 +34,14 @@ actual class PlatformSocket : Closeable {
@PublishedApi
internal lateinit var readChannel: ByteReadChannel
actual suspend inline fun send(packet: ByteArray, offset: Int, length: Int) {
try {
writeChannel.writeFully(packet, offset, length)
} catch (e: Exception) {
throw SendPacketInternalException(e)
}
}
/**
* @throws SendPacketInternalException
*/

View File

@ -98,7 +98,15 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
}
_network = createNetworkHandler(this.coroutineContext)
_network.login()
while (true){
try {
return _network.login()
} catch (e: Exception){
e.logStacktrace("Exception when login")
}
delay(3000)
logger.warning("Login failed. Retrying in 3s...")
}
}
protected abstract fun createNetworkHandler(coroutineContext: CoroutineContext): N

View File

@ -13,6 +13,7 @@ interface Member : QQ, Contact {
/**
* 所在的群
*/
@WeakRefProperty
val group: Group
/**

View File

@ -21,10 +21,15 @@ import net.mamoe.mirai.data.Profile
* @author Him188moe
*/
interface QQ : Contact, CoroutineScope {
/**
* 是否在线. 这个属性的值将会与服务器同步更新.
*/
val isOnline: Boolean
/**
* 请求头像下载链接
*/
// @MiraiExperimentalAPI
// @MiraiExperimentalAPI
//suspend fun queryAvatar(): AvatarLink
/**

View File

@ -12,6 +12,11 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
expect class PlatformSocket() : Closeable {
suspend fun connect(serverHost: String, serverPort: Int)
/**
* @throws SendPacketInternalException
*/
suspend inline fun send(packet: ByteArray, offset: Int = 0, length: Int = packet.size - offset)
/**
* @throws SendPacketInternalException
*/

View File

@ -29,36 +29,34 @@ actual var defaultLoginSolver: LoginSolver = DefaultLoginSolver()
class DefaultLoginSolver : LoginSolver() {
override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? {
loginSolverLock.withLock {
val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
withContext(Dispatchers.IO) {
tempFile.createNewFile()
bot.logger.info("需要图片验证码登录, 验证码为 4 字母")
try {
tempFile.writeChannel().use { writeFully(data) }
bot.logger.info("将会显示字符图片. 若看不清字符图片, 请查看文件 ${tempFile.absolutePath}")
} catch (e: Exception) {
bot.logger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
}
override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? = loginSolverLock.withLock {
val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
withContext(Dispatchers.IO) {
tempFile.createNewFile()
bot.logger.info("需要图片验证码登录, 验证码为 4 字母")
try {
tempFile.writeChannel().use { writeFully(data) }
bot.logger.info("将会显示字符图片. 若看不清字符图片, 请查看文件 ${tempFile.absolutePath}")
} catch (e: Exception) {
bot.logger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
}
tempFile.inputStream().use {
val img = ImageIO.read(it)
if (img == null) {
bot.logger.info("无法创建字符图片. 请查看文件")
} else {
bot.logger.info(img.createCharImg())
}
tempFile.inputStream().use {
val img = ImageIO.read(it)
if (img == null) {
bot.logger.info("无法创建字符图片. 请查看文件")
} else {
bot.logger.info(img.createCharImg())
}
}
bot.logger.info("请输入 4 位字母验证码. 若要更换验证码, 请直接回车")
return readLine()?.takeUnless { it.isEmpty() || it.length != 4 }.also {
bot.logger.info("正在提交[" + it +"]中...")
}
}
bot.logger.info("请输入 4 位字母验证码. 若要更换验证码, 请直接回车")
return readLine()?.takeUnless { it.isEmpty() || it.length != 4 }.also {
bot.logger.info("正在提交[$it]中...")
}
}
override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? = loginSolverLock.withLock {
bot.logger.info("需要滑动验证码")
bot.logger.info("请在任意浏览器中打开以下链接并完成验证码. ")
bot.logger.info("完成后请输入任意字符 ")
@ -68,7 +66,7 @@ class DefaultLoginSolver : LoginSolver() {
}
}
override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? = loginSolverLock.withLock {
bot.logger.info("需要进行账户安全认证")
bot.logger.info("该账户有[设备锁]/[不常用登陆地点]/[不常用设备登陆]的问题")
bot.logger.info("完成以下账号认证即可成功登陆|理论本认证在mirai每个账户中最多出现1次")

View File

@ -34,6 +34,14 @@ actual class PlatformSocket : Closeable {
@PublishedApi
internal lateinit var readChannel: ByteReadChannel
actual suspend inline fun send(packet: ByteArray, offset: Int, length: Int) {
try {
writeChannel.writeFully(packet, offset, length)
} catch (e: Exception) {
throw SendPacketInternalException(e)
}
}
/**
* @throws SendPacketInternalException
*/

View File

@ -57,11 +57,7 @@ class MiraiService : Service() {
TODO("not implemented")
}
override suspend fun onGetPhoneNumber(): String {
TODO("not implemented")
}
override suspend fun onGetSMSVerifyCode(): String {
override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
TODO("not implemented")
}