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:
commit
b9eacbcf1a
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
@ -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")
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -13,6 +13,7 @@ interface Member : QQ, Contact {
|
||||
/**
|
||||
* 所在的群
|
||||
*/
|
||||
@WeakRefProperty
|
||||
val group: Group
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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次")
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user