Commit fdf50e4d authored by Him188's avatar Him188

Image uploading

parent 4d346a2c
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai
import kotlinx.coroutines.runBlocking
......@@ -172,7 +174,7 @@ object MiraiServer {
get() {
for (it in qqList.split("\n").dropLastWhile { it.isEmpty() }.toTypedArray()) {
val strings = it.split("----").dropLastWhile { it.isEmpty() }.toTypedArray()
val bot = Bot(BotAccount(strings[0].toLong(), strings[1]), MiraiLogger)
val bot = Bot(BotAccount(strings[0].toUInt(), strings[1]), MiraiLogger)
if (runBlocking { bot.login() } === LoginResult.SUCCESS) {
bot.logGreen("Login succeed")
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai
import kotlinx.atomicfu.atomic
......@@ -86,16 +88,16 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
* 通过群号码获取群对象.
* 注意: 在并发调用时, 这个方法并不是原子的.
*/
fun getQQ(account: Long): QQ = qqs.getOrPut(account) { QQ(this@Bot, account) }
fun getQQ(account: UInt): QQ = qqs.getOrPut(account) { QQ(this@Bot, account) }
/**
* 通过群号码获取群对象.
* 注意: 在并发调用时, 这个方法并不是原子的.
*/
fun getGroupByNumber(groupNumber: Long): Group = groups.getOrPut(groupNumber) { Group(this@Bot, groupNumber) }
fun getGroupByNumber(groupNumber: UInt): Group = groups.getOrPut(groupNumber) { Group(this@Bot, groupNumber) }
fun getGroupById(groupId: Long): Group {
fun getGroupById(groupId: UInt): Group {
return getGroupByNumber(Group.groupIdToNumber(groupId))
}
}
......
......@@ -17,14 +17,14 @@ import net.mamoe.mirai.utils.toUHexString
*/
//Contacts
fun Bot.getQQ(number: Long): QQ = this.contacts.getQQ(number)
fun Bot.getQQ(number: Long): QQ = this.contacts.getQQ(number.toUInt())
fun Bot.getQQ(number: UInt): QQ = getQQ(number.toLong())
fun Bot.getQQ(number: UInt): QQ = this.contacts.getQQ(number)
fun Bot.getGroupByNumber(number: Long): Group = this.contacts.getGroupByNumber(number)
fun Bot.getGroupByNumber(number: UInt): Group = getGroupByNumber(number.toLong())
fun Bot.getGroupByNumber(number: Long): Group = this.contacts.getGroupByNumber(number.toUInt())
fun Bot.getGroupByNumber(number: UInt): Group = this.contacts.getGroupByNumber(number)
fun Bot.getGroupById(number: Long): Group = this.contacts.getGroupById(number)
fun Bot.getGroupById(number: UInt): Group = this.contacts.getGroupById(number)
val Bot.groups: ContactList<Group> get() = this.contacts.groups
......@@ -39,7 +39,7 @@ suspend fun Bot.login(configuration: BotNetworkConfiguration.() -> Unit): LoginR
suspend fun Bot.login(): LoginResult = this.network.login(BotNetworkConfiguration.Default)
//BotAccount
val Bot.qqAccount: Long get() = this.account.account
val Bot.qqAccount: UInt get() = this.account.account
//logging
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.contact
import net.mamoe.mirai.Bot
......@@ -11,7 +13,7 @@ import net.mamoe.mirai.message.toChain
*
* @author Him188moe
*/
abstract class PlatformContactBase internal constructor(val bot: Bot, val number: Long) {
abstract class PlatformContactBase internal constructor(val bot: Bot, val number: UInt) {
abstract suspend fun sendMessage(message: MessageChain)
......@@ -33,4 +35,4 @@ abstract class PlatformContactBase internal constructor(val bot: Bot, val number
* 在不同平台可能有不同的实现.
* 如在 JVM, suspend 调用不便, [Contact] 中有简化调用的 `blocking`() 和 `async`
*/
expect sealed class Contact(bot: Bot, number: Long) : PlatformContactBase
\ No newline at end of file
expect sealed class Contact(bot: Bot, number: UInt) : PlatformContactBase
\ No newline at end of file
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
package net.mamoe.mirai.contact
import net.mamoe.mirai.Bot
......@@ -13,8 +15,8 @@ import net.mamoe.mirai.utils.ContactList
*
* @author Him188moe
*/
expect class Group(bot: Bot, number: Long) : Contact {
val groupId: Long
expect class Group(bot: Bot, number: UInt) : Contact {
val groupId: UInt
val members: ContactList<QQ>
override suspend fun sendMessage(message: MessageChain)
......@@ -23,7 +25,7 @@ expect class Group(bot: Bot, number: Long) : Contact {
companion object
}
fun Group.Companion.groupNumberToId(number: Long): Long {//求你别出错
fun Group.Companion.groupNumberToId(number: UInt): UInt {//求你别出错
val left: Long = number.toString().let {
if (it.length < 6) {
return@groupNumberToId number
......@@ -36,84 +38,84 @@ fun Group.Companion.groupNumberToId(number: Long): Long {//求你别出错
return when (left) {
in 1..10 -> {
((left + 202).toString() + right.toString()).toLong()
((left + 202).toString() + right.toString()).toUInt()
}
in 11..19 -> {
((left + 469).toString() + right.toString()).toLong()
((left + 469).toString() + right.toString()).toUInt()
}
in 20..66 -> {
((left + 208).toString() + right.toString()).toLong()
((left + 208).toString() + right.toString()).toUInt()
}
in 67..156 -> {
((left + 1943).toString() + right.toString()).toLong()
((left + 1943).toString() + right.toString()).toUInt()
}
in 157..209 -> {
((left + 199).toString() + right.toString()).toLong()
((left + 199).toString() + right.toString()).toUInt()
}
in 210..309 -> {
((left + 389).toString() + right.toString()).toLong()
((left + 389).toString() + right.toString()).toUInt()
}
in 310..499 -> {
((left + 349).toString() + right.toString()).toLong()
((left + 349).toString() + right.toString()).toUInt()
}
else -> number
}
}
fun Group.Companion.groupIdToNumber(id: Long): Long {//求你别出错
var left: Long = id.toString().let {
fun Group.Companion.groupIdToNumber(id: UInt): UInt {//求你别出错
var left: UInt = id.toString().let {
if (it.length < 6) {
return@groupIdToNumber id
}
it.substring(0 until it.length - 6).toLong()
it.substring(0 until it.length - 6).toUInt()
}
return when (left) {
return when (left.toInt()) {
in 203..212 -> {
val right: Long = id.toString().let {
it.substring(it.length - 6).toLong()
val right: UInt = id.toString().let {
it.substring(it.length - 6).toUInt()
}
((left - 202).toString() + right.toString()).toLong()
((left - 202u).toString() + right.toString()).toUInt()
}
in 480..488 -> {
val right: Long = id.toString().let {
it.substring(it.length - 6).toLong()
val right: UInt = id.toString().let {
it.substring(it.length - 6).toUInt()
}
((left - 469).toString() + right.toString()).toLong()
((left - 469u).toString() + right.toString()).toUInt()
}
in 2100..2146 -> {
val right: Long = id.toString().let {
it.substring(it.length - 7).toLong()
val right: UInt = id.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toLong()
((left - 208).toString() + right.toString()).toLong()
left = left.toString().substring(0 until 3).toUInt()
((left - 208u).toString() + right.toString()).toUInt()
}
in 2010..2099 -> {
val right: Long = id.toString().let {
it.substring(it.length - 6).toLong()
val right: UInt = id.toString().let {
it.substring(it.length - 6).toUInt()
}
((left - 1943).toString() + right.toString()).toLong()
((left - 1943u).toString() + right.toString()).toUInt()
}
in 2147..2199 -> {
val right: Long = id.toString().let {
it.substring(it.length - 7).toLong()
val right: UInt = id.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toLong()
((left - 199).toString() + right.toString()).toLong()
left = left.toString().substring(0 until 3).toUInt()
((left - 199u).toString() + right.toString()).toUInt()
}
in 4100..4199 -> {
val right: Long = id.toString().let {
it.substring(it.length - 7).toLong()
val right: UInt = id.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toLong()
((left - 389).toString() + right.toString()).toLong()
left = left.toString().substring(0 until 3).toUInt()
((left - 389u).toString() + right.toString()).toUInt()
}
in 3800..3989 -> {
val right: Long = id.toString().let {
it.substring(it.length - 7).toLong()
val right: UInt = id.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toLong()
((left - 349).toString() + right.toString()).toLong()
left = left.toString().substring(0 until 3).toUInt()
((left - 349u).toString() + right.toString()).toUInt()
}
else -> id
}
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.contact
import net.mamoe.mirai.Bot
......@@ -14,7 +16,7 @@ import net.mamoe.mirai.message.MessageChain
*
* @author Him188moe
*/
expect class QQ(bot: Bot, number: Long) : Contact {
expect class QQ(bot: Bot, number: UInt) : Contact {
override suspend fun sendMessage(message: MessageChain)
override suspend fun sendXMLMessage(message: String)
......
@file:Suppress("MemberVisibilityCanBePrivate", "unused")
@file:Suppress("MemberVisibilityCanBePrivate", "unused", "EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.message
......@@ -87,21 +87,24 @@ data class PlainText(override val stringValue: String) : Message() {
// ==================================== Image ====================================
/**
* 图片消息.
* 图片消息. 在发送时将会区分群图片和好友图片发送.
* 由接收消息时构建, 可直接发送
*
* @param id 类似 `{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg`. 群的是大写id, 好友的是小写id
* @param id 类似 `/01ee6426-5ff1-4cf0-8278-e8634d2909ef`. 群的是大写id, 好友的是小写id
* @param filename 文件名. 这将决定图片的显示
*/
data class Image(val id: String) : Message() {
override val stringValue: String = "[$id]"
data class Image(val id: ImageId, val filename: String = "") : Message() {
override val stringValue: String = "[${id.value}]"
}
inline class ImageId(val value: String)
// ==================================== At ====================================
/**
* At 一个人
*/
data class At(val targetQQ: Long) : Message() {
data class At(val targetQQ: UInt) : Message() {
constructor(target: QQ) : this(target.number)
override val stringValue: String = "[@$targetQQ]"
......
......@@ -8,7 +8,8 @@ enum class MessageType(val value: UByte) {
PLAIN_TEXT(0x01u),
AT(0x06u),
FACE(0x02u),
IMAGE(0x03u),//may be 0x06?
GROUP_IMAGE(0x03u),
FRIEND_IMAGE(0x06u),
;
......
......@@ -70,7 +70,7 @@ interface BotNetworkHandler<Socket : DataPacketSocketAdapter> {
* @see [BotSession.sendAndExpect] 发送并期待一个包
* @see [TemporaryPacketHandler] 临时包处理器
*/
suspend fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*>)
suspend fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*, *>)
/**
* 发送数据包
......
@file:Suppress("MemberVisibilityCanBePrivate", "unused")
@file:Suppress("MemberVisibilityCanBePrivate", "unused", "EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.network
import kotlinx.coroutines.CompletableJob
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import net.mamoe.mirai.Bot
......@@ -65,10 +65,10 @@ class BotSession(
* @param handlerTemporary 处理器.
*/
//@JvmSynthetic
suspend inline fun <reified P : ServerPacket> sendAndExpect(handlerTemporary: TemporaryPacketHandler<P>.() -> Unit): CompletableJob {
val job = coroutineContext[Job].takeIf { it != null }?.let { Job(it) } ?: Job()
this.bot.network.addHandler(TemporaryPacketHandler(P::class, job, this).also(handlerTemporary))
return job
suspend inline fun <reified P : ServerPacket, R> sendAndExpect(handlerTemporary: TemporaryPacketHandler<P, R>.() -> Unit): CompletableDeferred<R> {
val deferred: CompletableDeferred<R> = coroutineContext[Job].takeIf { it != null }?.let { CompletableDeferred<R>(it) } ?: CompletableDeferred()
this.bot.network.addHandler(TemporaryPacketHandler(P::class, deferred, this).also(handlerTemporary))
return deferred
}
/**
......@@ -86,13 +86,13 @@ class BotSession(
* @param P 期待的包
* @param handler 处理期待的包
*/
suspend inline fun <reified P : ServerPacket> ClientPacket.sendAndExpect(noinline handler: suspend (P) -> Unit): CompletableJob {
val job = coroutineContext[Job].takeIf { it != null }?.let { Job(it) } ?: Job()
bot.network.addHandler(TemporaryPacketHandler(P::class, job, this@BotSession).also {
suspend inline fun <reified P : ServerPacket, R> ClientPacket.sendAndExpect(noinline handler: suspend (P) -> R): CompletableDeferred<R> {
val deferred: CompletableDeferred<R> = coroutineContext[Job].takeIf { it != null }?.let { CompletableDeferred<R>(it) } ?: CompletableDeferred()
bot.network.addHandler(TemporaryPacketHandler(P::class, deferred, this@BotSession).also {
it.toSend(this)
it.onExpect(handler)
})
return job
return deferred
}
suspend inline fun ClientPacket.send() = socket.sendPacket(this)
......@@ -101,6 +101,6 @@ class BotSession(
suspend fun BotSession.distributePacket(packet: ServerPacket) = this.socket.distributePacket(packet)
val BotSession.isOpen: Boolean get() = socket.isOpen
val BotSession.account: Long get() = bot.account.account
val BotSession.account: UInt get() = bot.account.account
val <T : BotNetworkHandler<*>> T.session get() = this[ActionPacketHandler].session
\ No newline at end of file
......@@ -33,13 +33,13 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
override lateinit var socket: BotSocketAdapter
private set
internal val temporaryPacketHandlers = mutableListOf<TemporaryPacketHandler<*>>()
internal val temporaryPacketHandlers = mutableListOf<TemporaryPacketHandler<*, *>>()
private val handlersLock = Mutex()
private var heartbeatJob: Job? = null
override suspend fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*>) {
override suspend fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*, *>) {
handlersLock.withLock {
temporaryPacketHandlers.add(temporaryPacketHandler)
}
......@@ -408,7 +408,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
class HeartbeatTimeoutException : CancellationException("heartbeat timeout")
if (withTimeoutOrNull(configuration.heartbeatTimeout.millisecondsLong) {
ClientHeartbeatPacket(bot.qqAccount, sessionKey).sendAndExpect<ServerHeartbeatResponsePacket> {}
ClientHeartbeatPacket(bot.qqAccount, sessionKey).sendAndExpect<ServerHeartbeatResponsePacket, Unit> {}
} == null) {
bot.logPurple("Heartbeat timed out")
bot.reinitializeNetworkHandler(configuration, HeartbeatTimeoutException())
......
......@@ -50,7 +50,12 @@ class ActionPacketHandler(session: BotSession) : PacketHandler(session) {
}
is ServerSubmitImageFilenameResponsePacket -> {
}
is ServerTryGetImageIDResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
is ServerSubmitImageFilenameResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
is ServerAccountInfoResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
is ServerAccountInfoResponsePacket -> {
......@@ -84,7 +89,7 @@ class ActionPacketHandler(session: BotSession) : PacketHandler(session) {
}
//@JvmSynthetic
suspend fun addFriend(account: Long, message: Lazy<String> = lazyOf("")): CompletableDeferred<AddFriendResult> {
suspend fun addFriend(account: UInt, message: Lazy<String> = lazyOf("")): CompletableDeferred<AddFriendResult> {
val future = CompletableDeferred<AddFriendResult>()
val session = AddFriendSession(account, future, message)
// uploadImageSessions.add(session)
......@@ -160,7 +165,7 @@ class ActionPacketHandler(session: BotSession) : PacketHandler(session) {
}
private inner class AddFriendSession(
private val qq: Long,
private val qq: UInt,
private val future: CompletableDeferred<AddFriendResult>,
private val message: Lazy<String>
) {
......
......@@ -41,7 +41,7 @@ class EventPacketHandler(session: BotSession) : PacketHandler(session) {
}
is ServerGroupMessageEventPacket -> {
if (packet.qq.toLong() == bot.account.account) return
if (packet.qq == bot.account.account) return
GroupMessageEvent(
bot,
......
package net.mamoe.mirai.network.protocol.tim.handler
import kotlinx.coroutines.CompletableJob
import kotlinx.coroutines.CompletableDeferred
import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
......@@ -19,14 +19,14 @@ import kotlin.reflect.KClass
*
* @see BotSession.sendAndExpect
*/
class TemporaryPacketHandler<P : ServerPacket>(
class TemporaryPacketHandler<P : ServerPacket, R>(
private val expectationClass: KClass<P>,
private val job: CompletableJob,
private val deferred: CompletableDeferred<R>,
private val fromSession: BotSession
) {
private lateinit var toSend: ClientPacket
private lateinit var expect: suspend (P) -> Unit
private lateinit var expect: suspend (P) -> R
lateinit var session: BotSession//无需覆盖
......@@ -40,7 +40,7 @@ class TemporaryPacketHandler<P : ServerPacket>(
}
fun onExpect(handler: suspend (P) -> Unit) {
fun onExpect(handler: suspend (P) -> R) {
this.expect = handler
}
......@@ -51,10 +51,15 @@ class TemporaryPacketHandler<P : ServerPacket>(
suspend fun shouldRemove(session: BotSession, packet: ServerPacket): Boolean {
if (expectationClass.isInstance(packet) && session === this.fromSession) {
kotlin.runCatching {
@Suppress("UNCHECKED_CAST")
@Suppress("UNCHECKED_CAST")
val ret = try {
expect(packet as P)
}.onFailure { job.completeExceptionally(it) }.onSuccess { job.complete() }
} catch (e: Exception) {
deferred.completeExceptionally(e)
return true
}
deferred.complete(ret)
return true
}
return false
......
......@@ -58,4 +58,9 @@ abstract class ClientPacket : Packet(), Closeable {
}
}
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class PacketVersion(val date: String, val timVersion: String)
private val UninitializedByteReadPacket = ByteReadPacket(IoBuffer.Empty, IoBuffer.EmptyPool)
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.writeFully
import net.mamoe.mirai.utils.encryptAndWrite
import net.mamoe.mirai.utils.writeQQ
class ClientRawPacket(
override val id: UShort,
private val bot: UInt,
private val version: ByteArray,
private val sessionKey: ByteArray,
private val data: ByteArray
) : ClientPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
writeQQ(bot)
writeFully(version)
encryptAndWrite(sessionKey) {
writeFully(data)
}
}
}
\ No newline at end of file
......@@ -9,7 +9,6 @@ import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.utils.encryptAndWrite
import net.mamoe.mirai.utils.writeHex
import net.mamoe.mirai.utils.writeQQ
import net.mamoe.mirai.utils.writeRandom
/**
* 获取升级天数等.
......@@ -18,7 +17,7 @@ import net.mamoe.mirai.utils.writeRandom
*/
@PacketId(0x00_5Cu)
class ClientAccountInfoRequestPacket(
private val qq: Long,
private val qq: UInt,
private val sessionKey: ByteArray
) : ClientPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
......
......@@ -11,7 +11,7 @@ import net.mamoe.mirai.utils.writeQQ
@PacketId(0x00_58u)
class ClientHeartbeatPacket(
private val bot: Long,
private val bot: UInt,
private val sessionKey: ByteArray
) : ClientPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
......
......@@ -4,7 +4,7 @@ package net.mamoe.mirai.network.protocol.tim.packet
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE)
@Target(AnnotationTarget.CLASS)
annotation class PacketId(
val value: UShort
)
......@@ -27,6 +27,8 @@ abstract class ServerPacket(val input: ByteReadPacket) : Packet(), Closeable {
override fun close() = this.input.close()
override fun toString(): String = this.packetToString()
fun <S : ServerPacket> S.applySequence() = this.applySequence(this@ServerPacket.sequenceId)
}
fun <S : ServerPacket> S.applySequence(sequenceId: UShort): S {
......
......@@ -2,10 +2,15 @@
package net.mamoe.mirai.network.protocol.tim.packet.action
import kotlinx.io.core.*
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUShort
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.encryptAndWrite
import net.mamoe.mirai.utils.writeHex
import net.mamoe.mirai.utils.writeQQ
/**
* 向服务器检查是否可添加某人为好友
......@@ -14,8 +19,8 @@ import net.mamoe.mirai.utils.*
*/
@PacketId(0x00_A7u)
class ClientCanAddFriendPacket(
val bot: Long,
val qq: Long,
val bot: UInt,
val qq: UInt,
val sessionKey: ByteArray
) : ClientPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
......@@ -85,8 +90,8 @@ class ServerCanAddFriendResponsePacket(input: ByteReadPacket) : ServerPacket(inp
*/
@PacketId(0x00_AEu)
class ClientAddFriendPacket(
val bot: Long,
val qq: Long,
val bot: UInt,
val qq: UInt,
val sessionKey: ByteArray
) : ClientPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
......
......@@ -10,55 +10,82 @@ import net.mamoe.mirai.message.toMessage
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.utils.*
fun main() {
println("牛逼".toMessage().toChain().toPacket().readBytes().toUHexString())
println("牛逼".toMessage().toChain().toPacket(true).readBytes().toUHexString())
}
@PacketId(0x00_CDu)
class ClientSendFriendMessagePacket(
private val botQQ: Long,
private val targetQQ: Long,
private val botQQ: UInt,
private val targetQQ: UInt,
private val sessionKey: ByteArray,
private val message: MessageChain
) : ClientPacket() {
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2.21173")
override fun encode(builder: BytePacketBuilder) = with(builder) {
writeQQ(botQQ)
writeHex(TIMProtocol.versionNewest)
encryptAndWrite(sessionKey) {
//TIM最新
//3E 03 3F A2
//76 E4 B8 DD
//00 00 00 08 00 01 00 04 00 00 00 00
//38 03
//3E 03 3F A2
//76 E4 B8 DD
//C6 FB 06 30 0C 69 0C AD C6 AD 14 BF 0B C6 38 EA
//00 0B
//3D 7F
//5D AA A8 E2
//01 1D
// TIM最新, 消息内容 "牛逼"
// 3E 03 3F A2
// 76 E4 B8 DD
// 00 00 00 [08] 00 01 00 04 00 00 00 00
// 38 03
// 3E 03 3F A2
// 76 E4 B8 DD
// C6 FB 06 30 0C 69 0C AD C6 AD 14 BF 0B C6 38 EA
// 00 0B
// 3D 7F
// 5D AA A8 E2
// 01 1D
// 00 00 00 00
// 01
// 00
// 00
// 00 01 4D 53 47 00 00 00 00 00
// 5D AA A8 E2
// E2 AE 94 2D
// 00 00 00 00 0C 00 86
// 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91
// 00 00
//
// 01 00 09 01 00 06 E7 89 9B E9 80 BC
// TIM最新, 消息内容 "发图片群"
// 3E 03 3F A2
// 76 E4 B8 DD
// 00 00 00 [0D] 00 01 00 04 00 00 00 00 00 03 00 01 01
// 38 03
// 3E 03 3F A2
// 76 E4 B8 DD
// C6 FB 06 30 0C 69 0C AD C6 AD 14 BF 0B C6 38 EA
// 00 0B
// 3D 88
// 5D AA AE 33
// 01 1D
// 00 00 00 00
//01
// 00
// 00
//00 01 4D 53 47 00 00 00 00 00
//5D AA A8 E2
//E2 AE 94 2D
//00 00 00 00 0C 00 86
//22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91
//00 00
// 01 00 00
// 00 01 4D 53 47 00 00 00 00 00
// 5D AA AE 33
// 7E 51 1D AA
// 00 00 00 00 0C 00 86
// 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91
// 00 00
//
//01 00 09 01 00 06 E7 89 9B E9 80 BC
// 01 00 0F 01 00 0C E5 8F 91 E5 9B BE E7 89 87 E7 BE A4
writeQQ(botQQ)
writeQQ(targetQQ)
writeHex("00 00 00 08 00 01 00 04 00 00 00 00")
writeHex("38 03")//TIM最新: 38 03
writeHex("38 03")
writeQQ(botQQ)
writeQQ(targetQQ)
writeFully(md5(buildPacket { writeQQ(targetQQ); writeFully(sessionKey) }.readBytes()))
......@@ -81,12 +108,11 @@ class ClientSendFriendMessagePacket(
writeHex("00 01 4D 53 47 00 00 00 00 00")
writeTime()
writeRandom(4)
writeHex("00 00 00 00 0C 00 86")//TIM最新 0C 00 86
writeHex(TIMProtocol.messageConstNewest)//... 85 E9 BB 91
writeHex("00 00 00 00 0C 00 86")
writeHex(TIMProtocol.messageConstNewest)
writeZero(2)
message.toPacket().debugPrint("CHAIN")
writePacket(message.toPacket())
writePacket(message.toPacket(false))
/*
//Plain text
......
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.network.protocol.tim.packet.action
......@@ -15,8 +15,8 @@ import net.mamoe.mirai.utils.*
@PacketId(0x00_02u)
class ClientSendGroupMessagePacket(
private val botQQ: Long,
private val groupId: Long,//不是 number
private val botQQ: UInt,
private val groupId: UInt,//不是 number
private val sessionKey: ByteArray,
private val message: MessageChain
) : ClientPacket() {
......@@ -28,7 +28,7 @@ class ClientSendGroupMessagePacket(
writeByte(0x2A)
writeGroup(groupId)
writeLVPacket {
writeShortLVPacket {
writeHex("00 01 01")
writeHex("00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00")
......@@ -38,7 +38,7 @@ class ClientSendGroupMessagePacket(
writeHex(TIMProtocol.messageConst1)
writeZero(2)
writePacket(message.toPacket())
writePacket(message.toPacket(true))
}
/*it.writeByte(0x01)
it.writeShort(bytes.size + 3)
......
......@@ -63,7 +63,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
0x0052u -> ServerGroupMessageEventPacket(input, eventIdentity)
0x00A6u -> ServerFriendMessageEventPacket(input.debugColorizedPrint("好友消息事件", ignoreUntilFirstConst = true), eventIdentity)
0x00A6u -> ServerFriendMessageEventPacket(input, eventIdentity)
//0210: 00 00 00 0E 00 08 00 02 00 01 00 0A 00 04 01 00 00 00 00 00 00 06 00 00 00 26 08 02 1A 02 08 49 0A 08 08 00 10 B2 DE 8C ED 05 0A 0C 08 A2 FF 8C F0 03 10 E4 A1 A7 ED 05 0A 0C 08 DD F1 92 B7 07 10 B1 DE 8C ED 05
// 00 00 00 08 00 0A 00 04 01 00 00 00 00 00 00 16 00 00 00 37 08 02 1A 12 08 95 02 10 90 04 40 98 E1 8C ED 05 48 AF 96 C3 A4 03 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 1A 29 08 00 10 05 18 98 E1 8C ED 05 20 01 28 FF FF FF FF 0F 32 15 E5 AF B9 E6 96 B9 E6 AD A3 E5 9C A8 E8 BE 93 E5 85 A5 2E 2E 2E
......@@ -101,7 +101,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
}
inner class ResponsePacket(
val bot: Long,
val bot: UInt,
val sessionKey: ByteArray
) : ClientPacket() {
override val id: UShort get() = this@ServerEventPacket.id
......
......@@ -17,7 +17,7 @@ import net.mamoe.mirai.utils.writeQQ
*/
@PacketId(0x00_ECu)
class ClientChangeOnlineStatusPacket(
private val bot: Long,
private val bot: UInt,
private val sessionKey: ByteArray,
private val loginStatus: OnlineStatus
) : ClientPacket() {
......
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.network.protocol.tim.packet.login
......@@ -16,7 +16,7 @@ import net.mamoe.mirai.utils.*
*/
@PacketId(0x08_36u)
class ClientPasswordSubmissionPacket constructor(
private val bot: Long,
private val bot: UInt,
private val password: String,
private val loginTime: Int,
private val loginIP: String,
......@@ -57,7 +57,7 @@ class ClientPasswordSubmissionPacket constructor(
private fun BytePacketBuilder.writePart1(
qq: Long,
qq: UInt,
password: String,
loginTime: Int,
loginIP: String,
......
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.network.protocol.tim.packet.login
......@@ -14,7 +14,7 @@ import net.mamoe.mirai.utils.*
*/
@PacketId(0x00_1Du)
class ClientSKeyRequestPacket(
private val qq: Long,
private val qq: UInt,
private val sessionKey: ByteArray
) : ClientPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
......@@ -28,7 +28,7 @@ class ClientSKeyRequestPacket(
@PacketId(0x00_1Du)
class ClientSKeyRefreshmentRequestPacket(
private val qq: Long,
private val qq: UInt,
private val sessionKey: ByteArray
) : ClientPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
......@@ -46,7 +46,7 @@ class ServerSKeyResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
override fun decode() = with(input) {
discardExact(4)
//debugDiscardExact(2)
sKey = this.readString(10)
sKey = this.readString(10)//16??
DebugLogger.logPurple("SKey=$sKey")
DebugLogger.logPurple("Skey包后面${this.readRemainingBytes().toUHexString()}")
}
......
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.network.protocol.tim.packet.login
......@@ -9,7 +9,7 @@ import net.mamoe.mirai.utils.*
@PacketId(0x08_28u)
class ClientSessionRequestPacket(
private val bot: Long,
private val bot: UInt,
private val serverIp: String,
private val token38: IoBuffer,
private val token88: IoBuffer,
......
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.network.protocol.tim.packet.login
......@@ -60,7 +60,7 @@ class ServerTouchResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
* @author Him188moe
*/
@PacketId(0x08_25u)
class ClientTouchPacket(private val bot: Long, private val serverIp: String) : ClientPacket() {
class ClientTouchPacket(private val bot: UInt, private val serverIp: String) : ClientPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(bot)
this.writeHex(TIMProtocol.fixVer)
......@@ -84,7 +84,7 @@ class ClientTouchPacket(private val bot: Long, private val serverIp: String) : C
* @author Him188moe
*/
@PacketId(0x08_25u)
class ClientTouchRedirectionPacket(private val serverIP: String, private val qq: Long) : ClientPacket() {
class ClientTouchRedirectionPacket(private val serverIP: String, private val qq: UInt) : ClientPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(qq)
this.writeHex(TIMProtocol.fixVer)
......
......@@ -12,7 +12,7 @@ import net.mamoe.mirai.utils.*
*/
@PacketId(0x00_BAu)
class ClientCaptchaTransmissionRequestPacket(
private val qq: Long,
private val qq: UInt,
private val token0825: ByteArray,
private val verificationSequence: Int,
private val token00BA: ByteArray
......@@ -44,7 +44,7 @@ class ClientCaptchaTransmissionRequestPacket(
*/
@PacketId(0x00_BAu)
class ClientCaptchaSubmitPacket(
private val qq: Long,
private val qq: UInt,
private val token0825: ByteArray,
private val captcha: String,
private val verificationToken: IoBuffer
......@@ -83,7 +83,7 @@ class ClientCaptchaSubmitPacket(
*/
@PacketId(0x00_BAu)
class ClientCaptchaRefreshPacket(
private val qq: Long,
private val qq: UInt,
private val token0825: ByteArray
) : ClientPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.utils
data class BotAccount(
val account: Long,//实际上是 UInt
val account: UInt,
val password: String//todo 不保存 password?
)
\ No newline at end of file
......@@ -80,7 +80,8 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {
0x00_CD_u -> ServerSendFriendMessageResponsePacket(this)
0x00_02_u -> ServerSendGroupMessageResponsePacket(this)
0x00_A7_u -> ServerCanAddFriendResponsePacket(this)
0x03_88_u -> ServerTryGetImageIDResponsePacket.Encrypted(this)
0x03_52_u -> ServerTryGetImageIDResponsePacket.Encrypted(this)
0x01_BDu -> ServerSubmitImageFilenameResponsePacket.Encrypted(this)
else -> UnknownServerPacket.Encrypted(this, id, sequenceId)
}.applySequence(sequenceId)
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.utils
import net.mamoe.mirai.contact.Contact
class ContactList<C : Contact> : MutableMap<Long, C> by mutableMapOf()
\ No newline at end of file
class ContactList<C : Contact> : MutableMap<UInt, C> by mutableMapOf()
\ No newline at end of file
package net.mamoe.mirai.utils
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input
import kotlinx.io.core.IoBuffer
import kotlinx.io.core.readBytes
import kotlinx.io.core.*
internal object DebugLogger : MiraiLogger by PlatformLogger("Packet Debug")
......@@ -15,7 +12,7 @@ internal fun ByteArray.debugPrint(name: String): ByteArray {
return this
}
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith(""))
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this"))
internal fun IoBuffer.debugPrint(name: String): IoBuffer {
val readBytes = this.readBytes()
DebugLogger.logPurple(name + "=" + readBytes.toUHexString())
......@@ -27,20 +24,33 @@ internal fun Input.debugDiscardExact(n: Number, name: String = "") {
DebugLogger.logPurple("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString())
}
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith(""))
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this"))
internal fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket {
val bytes = this.readBytes()
DebugLogger.logPurple("ByteReadPacket $name=" + bytes.toUHexString())
return bytes.toReadPacket()
}
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith(""))
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this"))
internal fun ByteReadPacket.debugColorizedPrint(name: String = "", ignoreUntilFirstConst: Boolean = false): ByteReadPacket {
val bytes = this.readBytes()
bytes.printColorizedHex(name, ignoreUntilFirstConst)
return bytes.toReadPacket()
}
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith(" "))
internal fun BytePacketBuilder.debugColorizedPrintThis(name: String = "") {
val data = this.build().readBytes()
data.printColorizedHex(name)
this.writeFully(data)
}
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith(" "))
internal fun BytePacketBuilder.debugPrintThis(name: String = "") {
val data = this.build().readBytes()
data.debugPrint(name)
this.writeFully(data)
}
internal fun String.printStringFromHex() {
println(this.hexToBytes().stringOfWitch())
......@@ -54,4 +64,10 @@ internal fun ByteArray.printColorizedHex(name: String = "", ignoreUntilFirstCons
}
expect fun compareHex(hex1s: String, hex2s: String): String
expect fun String.colorize(ignoreUntilFirstConst: Boolean = false): String
\ No newline at end of file
expect fun String.colorize(ignoreUntilFirstConst: Boolean = false): String
fun main() {
"00 02 3E 03 3F A2 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 39 00 00 00 0B 00 00 00 2E 51 51 E7 A9 BA E9 97 B4 20 0A 20 20 E6 9C 89 E6 96 B0 E8 AE BF E5 AE A2 20 0A 20 20 E6 9C 89 E6 96 B0 E5 A5 BD E5 8F 8B E5 8A A8 E6 80 81 00 00 01 2C 00 00 00 00"
.printStringFromHex()
}
\ No newline at end of file
......@@ -13,24 +13,34 @@ fun BytePacketBuilder.writeZero(count: Int) = repeat(count) { this.writeByte(0)
fun BytePacketBuilder.writeRandom(length: Int) = repeat(length) { this.writeByte(Random.Default.nextInt(255).toByte()) }
fun BytePacketBuilder.writeQQ(qq: Long) = this.writeUInt(qq.toUInt())
fun BytePacketBuilder.writeQQ(qq: UInt) = this.writeUInt(qq)
fun BytePacketBuilder.writeGroup(groupIdOrGroupNumber: Long) = this.writeFully(groupIdOrGroupNumber.toUInt().toByteArray())
fun BytePacketBuilder.writeGroup(groupIdOrGroupNumber: Long) = this.writeUInt(groupIdOrGroupNumber.toUInt())
fun BytePacketBuilder.writeGroup(groupIdOrGroupNumber: UInt) = this.writeUInt(groupIdOrGroupNumber)
fun BytePacketBuilder.writeLVByteArray(byteArray: ByteArray) {
this.writeShort(byteArray.size.toShort())
this.writeFully(byteArray)
}
fun BytePacketBuilder.writeLVPacket(packet: ByteReadPacket) {
fun BytePacketBuilder.writeShortLVPacket(packet: ByteReadPacket) {
this.writeShort(packet.remaining.toShort())
this.writePacket(packet)
packet.release()
}
fun BytePacketBuilder.writeLVPacket(builder: BytePacketBuilder.() -> Unit) = this.writeLVPacket(BytePacketBuilder().apply(builder).build())
fun BytePacketBuilder.writeUVarintLVPacket(packet: ByteReadPacket) {
this.writeUVarLong(packet.remaining)
this.writePacket(packet)
packet.release()
}
fun BytePacketBuilder.writeShortLVPacket(builder: BytePacketBuilder.() -> Unit) = this.writeShortLVPacket(BytePacketBuilder().apply(builder).build())
fun BytePacketBuilder.writeUVarintLVPacket(builder: BytePacketBuilder.() -> Unit) = this.writeUVarintLVPacket(BytePacketBuilder().apply(builder).build())
@Suppress("DEPRECATION")
fun BytePacketBuilder.writeLVString(str: String) = this.writeLVByteArray(str.toByteArray())
fun BytePacketBuilder.writeShortLVString(str: String) = this.writeLVByteArray(str.toByteArray())
@Suppress("DEPRECATION")
fun BytePacketBuilder.writeLVHex(hex: String) = this.writeLVByteArray(hex.hexToBytes())
......@@ -45,7 +55,7 @@ fun BytePacketBuilder.encryptAndWrite(key: IoBuffer, encoder: BytePacketBuilder.
fun BytePacketBuilder.encryptAndWrite(key: ByteArray, encoder: BytePacketBuilder.() -> Unit) = writeFully(TEA.encrypt(BytePacketBuilder().apply(encoder).use { it.build().readBytes() }, key))
fun BytePacketBuilder.encryptAndWrite(keyHex: String, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(keyHex.hexToBytes(), encoder)
fun BytePacketBuilder.writeTLV0006(qq: Long, password: String, loginTime: Int, loginIP: String, privateKey: ByteArray) {
fun BytePacketBuilder.writeTLV0006(qq: UInt, password: String, loginTime: Int, loginIP: String, privateKey: ByteArray) {
val firstMD5 = md5(password)
val secondMD5 = md5(firstMD5 + byteArrayOf(0, 0, 0, 0) + qq.toUInt().toByteArray())
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.utils
import com.soywiz.klock.DateTime
......@@ -38,10 +40,14 @@ expect fun localIpAddress(): String
/**
* 上传群图片
*/
expect suspend fun httpPostGroupImage(
expect suspend fun httpPostFriendImage(
uKeyHex: String,
fileSize: Int,
botNumber: Long,
groupCode: Long,
botNumber: UInt,
qq: UInt,
imageData: ByteArray
): Boolean
\ No newline at end of file
): Boolean
fun main() {
"46 52 25 46 60 30 59 4F 4A 5A 51".printStringFromHex()
}
\ No newline at end of file
......@@ -23,7 +23,7 @@ fun ByteArray.encryptBy(keyHex: String): ByteArray = TEA.encrypt(checkLength(),
private fun ByteArray.checkLength(): ByteArray {
size.let {
require(it % 8 == 0 && it >= 16) { "data must len % 8 == 0 && len >= 16 but given $it" }
require(it % 8 == 0 && it >= 16) { "data must len % 8 == 0 && len >= 16 but given (length=$it) ${this.toUHexString()}" }
}
return this
}
\ No newline at end of file
......@@ -49,6 +49,7 @@ fun String.hexToUBytes(): UByteArray = HexCache.hexToUBytes(this)
fun String.hexToInt(): Int = hexToBytes().toUInt().toInt()
fun getRandomByteArray(length: Int): ByteArray = ByteArray(length) { Random.nextInt(0..255).toByte() }
fun getRandomString(length: Int, charRange: CharRange): String = String(CharArray(length) { charRange.random() })
fun ByteArray.toUInt(): UInt = this[0].toUInt().and(255u).shl(24) + this[1].toUInt().and(255u).shl(16) + this[2].toUInt().and(255u).shl(8) + this[3].toUInt().and(255u).shl(0)
fun ByteArray.toIoBuffer(): IoBuffer = IoBuffer.Pool.borrow().let { it.writeFully(this); it }
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.contact
import kotlinx.coroutines.launch
......@@ -18,7 +20,7 @@ import net.mamoe.mirai.utils.ContactList
* @author Him188moe
*/
@Suppress("unused")
actual sealed class Contact actual constructor(bot: Bot, number: Long) : PlatformContactBase(bot, number) {
actual sealed class Contact actual constructor(bot: Bot, number: UInt) : PlatformContactBase(bot, number) {
abstract override suspend fun sendMessage(message: MessageChain)
......@@ -71,7 +73,7 @@ actual sealed class Contact actual constructor(bot: Bot, number: Long) : Platfor
* Java 调用 [groupNumberToId] : `Group.groupNumberToId(number)`
* @author Him188moe
*/
actual class Group actual constructor(bot: Bot, number: Long) : Contact(bot, number) {
actual class Group actual constructor(bot: Bot, number: UInt) : Contact(bot, number) {
actual val groupId = groupNumberToId(number)
actual val members: ContactList<QQ>
//todo members
......@@ -100,7 +102,7 @@ actual class Group actual constructor(bot: Bot, number: Long) : Contact(bot, num
*
* @author Him188moe
*/
actual class QQ actual constructor(bot: Bot, number: Long) : Contact(bot, number) {
actual class QQ actual constructor(bot: Bot, number: UInt) : Contact(bot, number) {
actual override suspend fun sendMessage(message: MessageChain) {
bot.network[EventPacketHandler].sendFriendMessage(this, message)
}
......
......@@ -2,97 +2,186 @@
package net.mamoe.mirai.network.protocol.tim.packet
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.writeFully
import kotlinx.io.core.*
import net.mamoe.mirai.utils.*
import java.io.File
import javax.imageio.ImageIO
actual typealias ClientTryGetImageIDPacket = ClientTryGetImageIDPacketJvm
fun main() {
val packet = ClientTryGetImageIDPacketJvm(1040400290u,
"99 82 67 D4 62 20 CA 5D 81 F8 6F 83 EE 8A F7 68".hexToBytes(),
2978594313u,
ImageIO.read(File(("C:\\Users\\Him18\\Desktop\\哈哈哈操.jpg"))))
println(packet.packet.readBytes().toUHexString())
"89 FC A6 8C 0B".hexToBytes().read {
println(readUnsignedVarInt())
}
}
/**
* 请求上传图片. 将发送图片的 md5, size.
* 请求上传图片. 将发送图片的 md5, size, width, height.
* 服务器返回以下之一:
* - 服务器已经存有这个图片 [ServerTryGetImageIDFailedPacket]
* - 服务器未存有, 返回一个 key 用于客户端上传 [ServerTryGetImageIDSuccessPacket]
*
* @author Him188moe
*/
@PacketId(0x03_88u)
@PacketId(0x03_52u)
class ClientTryGetImageIDPacketJvm(
private val botNumber: Long,
private val botNumber: UInt,
private val sessionKey: ByteArray,
private val groupNumberOrAccount: Long,
private val target: UInt,
private val image: PlatformImage
) : ClientPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeRandom(2)
//00 00 00 07 00 00 00 4B 08 01 12 03 98 01 01 08 01 12 47 08 A2 FF 8C F0 03 10 89 FC A6 8C 0B 18 00 22 10 2B 23 D7 05 CA D1 F2 CF 37 10 FE 58 26 92 FC C4 28 FD 08 32 1A 7B 00 47 00 47 00 42 00 7E 00 49 00 31 00 5A 00 4D 00 43 00 28 00 25 00 49 00 38 01 48 00 70 42 78 42
//一次 body
//00 00 00 00 00 00 00 00 3C 61 3C 48 85 91 81 B9 DF 27 D9 C3 20 43 F7 1C 73 DA 2A 84 74 AC 78 AC CC 38 54 8F AE 06 8C 22 AA AF 2E C1 E4 70 8C 31 63 52 95 F2 6F C3 9A 2D 77 4B F7 7B 4F C4 1A 6D 7A 3F 22 D8 9D B3 48 99 F3 E7 4F D0 2D 31 94 40 ED A7 5C D9 CE 70 B1 F7 B8 1B 3D CA B3 0E BE 86 33 56 B4 E4 30 AD 66 30 C1 C7 15 6A 71 B6 49 DC DC 0E 74 4B CE 12 3F ED
this.writeQQ(botNumber)
this.writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00")
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2.21173")
override fun encode(builder: BytePacketBuilder) = with(builder) {
writeQQ(botNumber)
//04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00
writeHex("04 00 00 00 01 2E 01 00 00 69 35 00 00 00 00 00 00 00 00")
val imageData = image.toByteArray()
encryptAndWrite(sessionKey) {
//好友图片
// 00 00 00
// 07 00
// 00 00
// proto
// [4D 08]后文长度
// 01 12
// 03 98
// 01 01
// 08 01
// 12 49
// 08 [A2 FF 8C F0 03](1040400290 varint)
// 10 [DD F1 92 B7 07](1994701021 varint)
// 18 00
// 22 [10](=16) [E9 BA 47 2E 36 ED D4 BF 8C 4F E5 6A CB A0 2D 5E](md5)
// 28 [CE 0E](1870 varint)
// 32 1A
// 39 00
// 51 00
// 24 00
// 32 00
// 4A 00
// 53 00
// 25 00
// 4C 00
// 56 00
// 42 00
// 33 00
// 44 00
// 44 00
// 38 01
// 48 00
// 70 [92 03](402 varint)
// 78 [E3 01](227 varint)
//好友图片
/*
* 00 00 00 07 00 00 00
* [4E 08]后文长度
* 01 12
* 03 98
* 01 01
* 08 01
* 12 4A
* 08 [A2 FF 8C F0 03](varint)
* 10 [DD F1 92 B7 07](varint)
* 18 00//24
* 22 10 72 02 57 44 84 1D 83 FC C0 85 A1 E9 10 AA 9C 2C
* 28 [BD D9 19](421053 varint)
* 32 1A//48
* 49 00
* 49 00
* 25 00
* 45 00
* 5D 00
* 50 00
* 41 00
* 7D 00
* 4F 00
* 56 00
* 46 00
* 4B 00
* 5D 00
* 38 01
* 48 00//78
*
*
* 70 [80 14]
* 78 [A0 0B]//84
*/
val byteArray = image.toByteArray()
this.encryptAndWrite(sessionKey) {
writeZero(3)
writeHex("07 00")
writeZero(2)
writeHex("5B")//原5E
writeHex("08")
writeHex("01 12 03 98 01 01 10 01")
writeHex("1A")
writeHex("57")//原5A
writeHex("08")
writeUVarInt(groupNumberOrAccount)//FB D2 D8 94
writeByte(0x02)
writeHex("10")
writeUVarInt(botNumber)//A2 FF 8C F0
writeHex("18 00")
writeHex("22")
writeHex("10")
writeFully(md5(byteArray))
writeHex("28")
writeUVarInt(byteArray.size.toUInt())//E2 0D
writeHex("32")
writeHex("1A")
//28 00 5A 00 53 00 41 00 58 00 40 00 57 00 4B 00 52 00 4A 00 5A 00 31 00 7E 00 38 01 48 01 50 38 58 34 60 04 6A 05 32 36 39 33 33 70 00 78 03 80 01 00
writeHex("37 00 4D 00 32 00 25 00 4C 00 31 00 56 00 32 00 7B 00 39 00 30 00 29 00 52 00")
writeHex("38 01")
writeHex("48 01")
writeHex("50")
writeUVarInt(image.width.toUInt())
writeHex("58")
writeUVarInt(image.height.toUInt())
writeHex("60 04")
writeHex("6A")
writeHex("05")
writeHex("32 36 36 35 36")
writeHex("70 00")
writeHex("78 03")
writeHex("80 01")
writeHex("00")
writeUShort(0x07_00u)
writeZero(1)
//proto
val packet = buildPacket {
writeUByte(0x08u)
writeUShort(0x01_12u)
writeUShort(0x03_98u)
writeUShort(0x01_01u)
writeUShort(0x08_01u)
writeUShort(0x12_47u)//?似乎会变
writeUByte(0x08u)
writeUVarInt(target)//todo 这两qq号反过来放也tm可以成功
writeUByte(0x10u)
writeUVarInt(botNumber)
writeUShort(0x18_00u)
writeUByte(0x22u)
writeUByte(0x10u)
writeFully(md5(imageData))
writeUByte(0x28u)
writeUVarInt(imageData.size.toUInt())
writeUByte(0x32u)
//长度应为1A
writeUVarintLVPacket {
writeUShort(0x28_00u)
writeUShort(0x46_00u)
writeUShort(0x51_00u)
writeUShort(0x56_00u)
writeUShort(0x4B_00u)
writeUShort(0x41_00u)
writeUShort(0x49_00u)
writeUShort(0x25_00u)
writeUShort(0x4B_00u)
writeUShort(0x24_00u)
writeUShort(0x55_00u)
writeUShort(0x30_00u)
writeUShort(0x24_00u)
}
writeUShort(0x38_01u)
writeUShort(0x48_00u)
writeUByte(0x70u)
writeUVarInt(image.width.toUInt())
writeUByte(0x78u)
writeUVarInt(image.height.toUInt())
}
writeShort((packet.remaining - 7).toShort())//why?
writePacket(packet)
//println(this.build().readBytes().toUHexString())
}
}
}
\ No newline at end of file
......@@ -7,4 +7,4 @@ import javax.imageio.ImageIO
actual typealias PlatformImage = BufferedImage
@JvmOverloads
actual fun BufferedImage.toByteArray(formatName: String): ByteArray = ByteArrayOutputStream().use { ImageIO.write(this, "JPG", it); it.toByteArray() }
\ No newline at end of file
actual fun BufferedImage.toByteArray(formatName: String): ByteArray = ByteArrayOutputStream().use { ImageIO.write(this, "PNG", it); it.toByteArray() }
\ No newline at end of file
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jsoup.Connection
import org.jsoup.Jsoup
import java.net.HttpURLConnection
import java.net.InetAddress
import java.net.URL
import java.security.MessageDigest
import java.util.zip.CRC32
......@@ -19,36 +19,62 @@ actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(host
actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
actual suspend fun httpPostGroupImage(
fun main() {
"00 00 00 08 00 00 01 0D 12 06 98 01 01 A0 01 00 08 01 12 86 02 08 00 10 AB A7 89 D8 02 18 00 28 00 38 B4 C7 E6 B0 02 38 F1 C0 A1 BF 05 38 FB AE FA 95 0A 38 E5 C6 BF EC 06 40 B0 6D 40 90 3F 40 50 40 BB 03 4A 80 01 B5 29 1A 1B 0E 63 79 8B 34 B1 4E 2A 2A 9E 69 09 A7 69 F5 C6 4F 95 DA 96 A9 1B E3 CD 6F 3D 30 EE 59 C0 30 22 BF F0 2D 88 2D A7 6C B2 09 AD D6 CE E1 46 84 FC 7D 19 AF 1A 37 91 98 AD 2C 45 25 AA 17 2F 81 DC 5A 7F 30 F4 2D 73 E5 1C 8B 8A 23 85 42 9D 8D 5C 18 15 32 D1 CA A3 4D 01 7C 59 11 73 DA B6 09 C2 6D 58 35 EF 48 88 44 0F 2D 17 09 52 DF D4 EA A7 85 2F 27 CE DF A8 F5 9B CD C9 84 C2 52 25 2F 30 31 65 65 36 34 32 36 2D 35 66 66 31 2D 34 63 66 30 2D 38 32 37 38 2D 65 38 36 33 34 64 32 39 30 39 65 66 5A 25 2F 30 31 65 65 36 34 32 36 2D 35 66 66 31 2D 34 63 66 30 2D 38 32 37 38 2D 65 38 36 33 34 64 32 39 30 39 65 66 60 00 68 80 80 08 20 01"
.printStringFromHex()
println(md5("00 00 00 08 00 00 01 0D 12 06 98 01 01 A0 01 00 08 01 12 86 02 08 00 10 AB A7 89 D8 02 18 00 28 00 38 B4 C7 E6 B0 02 38 F1 C0 A1 BF 05 38 FB AE FA 95 0A 38 E5 C6 BF EC 06 40 B0 6D 40 90 3F 40 50 40 BB 03 4A 80 01 B5 29 1A 1B 0E 63 79 8B 34 B1 4E 2A 2A 9E 69 09 A7 69 F5 C6 4F 95 DA 96 A9 1B E3 CD 6F 3D 30 EE 59 C0 30 22 BF F0 2D 88 2D A7 6C B2 09 AD D6 CE E1 46 84 FC 7D 19 AF 1A 37 91 98 AD 2C 45 25 AA 17 2F 81 DC 5A 7F 30 F4 2D 73 E5 1C 8B 8A 23 85 42 9D 8D 5C 18 15 32 D1 CA A3 4D 01 7C 59 11 73 DA B6 09 C2 6D 58 35 EF 48 88 44 0F 2D 17 09 52 DF D4 EA A7 85 2F 27 CE DF A8 F5 9B CD C9 84 C2 52 25 2F 30 31 65 65 36 34 32 36 2D 35 66 66 31 2D 34 63 66 30 2D 38 32 37 38 2D 65 38 36 33 34 64 32 39 30 39 65 66 5A 25 2F 30 31 65 65 36 34 32 36 2D 35 66 66 31 2D 34 63 66 30 2D 38 32 37 38 2D 65 38 36 33 34 64 32 39 30 39 65 66 60 00 68 80 80 08 20 01").toUHexString())
}
actual suspend fun httpPostFriendImage(
uKeyHex: String,
fileSize: Int,
botNumber: Long,
groupCode: Long,
botNumber: UInt,
qq: UInt,
imageData: ByteArray
): Boolean = Jsoup
.connect("http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc" +
): Boolean {/*Jsoup
//htdata2.qq.com
// 101.227.143.109/cgi-bin/httpconn
// ?htcmd=0x6ff0070
// &ver=5603
// &ukey=3B121C959B85035F12497519221FB9E09740E477D3A440D28253E96C95BD72EA1D11B25189894F0256F3E0F3D553FB92925A8834F85583C78D0D9639A3F35C730783D45C065FF9E9E74765183A11492D50750C6BB5DCAD9F171285B68F6A11061CDDA740AD2DCD28D5B2DB2D6440143FA53F1B6F14584DB49E926FDDC4F49907
// &filesize=137791
// &range=0
// &uin=1040400290
.connect("http://101.227.143.109/cgi-bin/httpconn" +
"?htcmd=0x6ff0070" +
"&ver=5603" +
"&ukey=" + uKeyHex.replace(" ", "") +
"&filezise=" + fileSize +
"&range=" + "0" +
"&uin=" + botNumber +
"&groupcode=" + groupCode)
.userAgent("QQClient")
"&uin=" + botNumber.toLong())
//.userAgent("QQClient")
.header("Content-Length", fileSize.toString())
.requestBody(String(imageData))
.requestBody(String(imageData, Charset.forName("ASCII")))
.method(Connection.Method.POST)
.ignoreContentType(true)
.let {
withContext(Dispatchers.IO) {
it.execute()
}
}
/*
val conn = URL(builder).openConnection() as HttpURLConnection
conn.setRequestProperty("User-Agent", "QQClient")
conn.setRequestProperty("Content-Length", "" + fileSize)
conn.requestMethod = "POST"
conn.doOutput = true
conn.outputStream.write(img)
conn.connect()*/
.statusCode() == 200
\ No newline at end of file
};*/
val conn = URL("http://101.227.143.109/cgi-bin/httpconn" +
"?htcmd=0x6ff0070" +
"&ver=5603" +
"&ukey=" + uKeyHex.replace(" ", "") +
"&filezise=" + fileSize +
"&range=" + "0" +
"&uin=" + botNumber.toLong()).openConnection() as HttpURLConnection
conn.setRequestProperty("User-Agent", "QQClient")
conn.setRequestProperty("Content-Length", "" + fileSize)
conn.requestMethod = "POST"
conn.doOutput = true
conn.outputStream.buffered().write(imageData)
conn.connect()
println(conn.responseMessage)
println(conn.responseCode)
return conn.responseCode == 200
}
\ No newline at end of file
@file:Suppress("EXPERIMENTAL_API_USAGE")
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.runBlocking
......@@ -44,7 +46,7 @@ suspend fun main() {
runBlocking {
val bot = Bot(
BotAccount(
qq,
qq.toUInt(),
if (password.endsWith(".")) password.substring(0, password.length - 1) else password
),
Console()
......
......@@ -8,6 +8,8 @@ dependencies {
implementation rootProject.ext.coroutineCommon
implementation rootProject.ext.kotlinJvm
implementation rootProject.ext.kotlinxIOJvm
compile "org.jetbrains.kotlin:kotlin-reflect:1.3.50"
implementation 'org.jsoup:jsoup:1.12.1'
}
tasks.withType(JavaCompile) {
......
import net.mamoe.mirai.utils.toUHexString
import org.jsoup.Connection
import org.jsoup.Jsoup
fun main() {
println(Jsoup.connect("http://61.183.164.34/gchatpic_new/B814D8D6A55D6DB423469E38D2A17AAA23836D74E7A656A4A8288C6078950A33B4A49E854E59B6D2314EFC47D6475902EDE8CADAFAF7F2A7670CAC05EA8314A1241128102F0A3AAF9C07284B1AE35E52F6D0A265235AFA6B/0?vuin=1040400290&term=255&srvver=26933&rf=n")
.cookie("ST", "00015DAA9C030040F3B82971FCBC718AA35573100B9CDBA2CB0DE38AF8710CA22E2986246345FC96B82BA0C211FDA700C397DF99FCC0989D67FD75F00B2FFB9CE0D032C3DCAC5A77")
.method(Connection.Method.GET)
.ignoreContentType(true)
.execute()
.bodyAsBytes().toUHexString())
}
\ No newline at end of file
@file:Suppress("EXPERIMENTAL_API_USAGE", "MemberVisibilityCanBePrivate")
@file:Suppress("EXPERIMENTAL_API_USAGE", "MemberVisibilityCanBePrivate", "EXPERIMENTAL_UNSIGNED_LITERALS")
import jpcap.JpcapCaptor
import jpcap.packet.IPPacket
......@@ -31,7 +31,7 @@ object Main {
jpcap = JpcapCaptor.openDevice(devices[0], caplen, promiscCheck, 50)
while (true) {
assert(jpcap != null)
val pk = jpcap!!.packet
val pk = jpcap!!.packet ?: continue
if (pk is IPPacket && pk.version.toInt() == 4) {
if (pk is UDPPacket) {
......@@ -47,6 +47,7 @@ object Main {
}
} else {
try {
//println("发出包全部=${pk.data.toUHexString()}")
dataSent(pk.data)
println()
} catch (e: Throwable) {
......@@ -73,20 +74,36 @@ object Main {
* 7. 查看内存, 从 `eax` 开始的 16 bytes 便是 `sessionKey`
*/
val sessionKey: ByteArray = "99 82 67 D4 62 20 CA 5D 81 F8 6F 83 EE 8A F7 68".hexToBytes()
val qq: UInt = 1040400290u
fun dataReceived(data: ByteArray) {
println("--------------")
println("接收数据包")
//println("raw = " + data.toUHexString())
data.read {
discardExact(3)
val idHex = readInt().toUHexString(" ")
discardExact(7)//4 for qq number, 3 for 0x00 0x00 0x00. 但更可能是应该 discard 8
if (idHex.startsWith("00 81")) {
return@read
}
if (readUInt() != qq) {
return@read
}
println("--------------")
println("接收数据包")
discardExact(3)//0x00 0x00 0x00. 但更可能是应该 discard 8
println("id=$idHex")
println("解密body=${this.readRemainingBytes().cutTail(1).decryptBy(sessionKey).toUHexString()}")
}
val remaining = this.readRemainingBytes().cutTail(1)
try {
val decrypted = remaining.decryptBy(sessionKey)
println("解密body=${decrypted.toUHexString()}")
packetReceived(data.read { parseServerPacket(data.size) })
} catch (e: DecryptionFailedException) {
println("密文body=" + remaining.toUHexString())
println("解密body=解密失败")
}
packetReceived(data.read { this.parseServerPacket(data.size) })
}
}
fun packetReceived(packet: ServerPacket) {
......@@ -118,15 +135,38 @@ object Main {
}
fun dataSent(rawPacket: ByteArray) = rawPacket.cutTail(1).read {
// 02 38 03
// 03 52 78 9F
// 3E 03 3F A2 04 00 00 00 01 2E 01 00 00 69 35 00 00 00 00 00 00 00 00
// 02 38 03
// 01 BD 63 D6
// 3E 03 3F A2 02 00 00 00 01 2E 01 00 00 69 35
println("---------------------------")
discardExact(3)//head
val idHex = readBytes(4).toUHexString()
println("发出包ID = $idHex")
readUInt()//客户端登录的qq
println("TIM的fixVer2=" + readBytes(TIMProtocol.fixVer2.hexToBytes().size).toUHexString())
if (readUInt() != qq) {
return@read
}
println("fixVer2=" + when (val flag = readByte().toInt()) {
2 -> byteArrayOf(2) + readBytes(TIMProtocol.fixVer2.hexToBytes().size - 1)
4 -> byteArrayOf(4) + readBytes(TIMProtocol.fixVer2.hexToBytes().size - 1 + 8)//8个0
else -> error("unknown fixVer2 flag=$flag")
}.toUHexString())
//39 27 DC E2 04 00 00 00 00 00 00 00 1E 0E 89 00 00 01 05 0F 05 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 3E 03 3F A2 00 00 00 00 00 00 00 00 00 00 00
val encryptedBody = readRemainingBytes()
println("解密body = ${encryptedBody.decryptBy(sessionKey).toUHexString()}")
try {
println("解密body=${encryptedBody.decryptBy(sessionKey).toUHexString()}")
} catch (e: DecryptionFailedException) {
println("密文=" + encryptedBody.toUHexString())
println("解密body=解密失败")
}
encryptedBody.read {
......@@ -139,13 +179,18 @@ object Main {
val messageData = raw.decryptBy(sessionKey)
println("解密结果: " + messageData.toUHexString())
println("尝试解消息")
messageData.read {
discardExact(
4 + 4 + 12 + 2 + 4 + 4 + 16 + 2 + 2 + 4 + 2 + 16 + 4 + 4 + 7 + 15 + 2
+ 1
)
val chain = readMessageChain()
println(chain)
try {
messageData.read {
discardExact(
4 + 4 + 12 + 2 + 4 + 4 + 16 + 2 + 2 + 4 + 2 + 16 + 4 + 4 + 7 + 15 + 2
+ 1
)
val chain = readMessageChain()
println(chain)
}
} catch (e: Exception) {
println("失败")
}
}
......
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE")
package demo1
import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeoutOrNull
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.events.FriendMessageEvent
import net.mamoe.mirai.event.events.GroupMessageEvent
import net.mamoe.mirai.event.subscribeAll
......@@ -10,12 +14,16 @@ import net.mamoe.mirai.event.subscribeAlways
import net.mamoe.mirai.event.subscribeUntilFalse
import net.mamoe.mirai.login
import net.mamoe.mirai.message.Image
import net.mamoe.mirai.message.ImageId
import net.mamoe.mirai.message.PlainText
import net.mamoe.mirai.network.protocol.tim.packet.ClientRawPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.utils.BotAccount
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.PlatformLogger
import net.mamoe.mirai.network.protocol.tim.packet.uploadImage
import net.mamoe.mirai.network.session
import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.*
import java.io.File
import javax.imageio.ImageIO
private fun readTestAccount(): BotAccount? {
val file = File("testAccount.txt")
......@@ -25,7 +33,7 @@ private fun readTestAccount(): BotAccount? {
val lines = file.readLines()
return try {
BotAccount(lines[0].toLong(), lines[1])
BotAccount(lines[0].toUInt(), lines[1])
} catch (e: IndexOutOfBoundsException) {
null
}
......@@ -34,7 +42,7 @@ private fun readTestAccount(): BotAccount? {
@Suppress("UNUSED_VARIABLE")
suspend fun main() {
val bot = Bot(readTestAccount() ?: BotAccount(//填写你的账号
account = 1994701121,
account = 1994701121u,
password = "123456"
), PlatformLogger())
......@@ -61,20 +69,31 @@ suspend fun main() {
"复读" in it.message -> it.sender.sendMessage(it.message)
"发群" in it.message -> {
"发群" in it.message -> Group(bot, 580266363u).sendMessage("h")
"直接发送包" in it.message -> {
val d = ("01 " + 1994701021u.toByteArray().toUHexString() + " 3E 03 3F A2 00 00 02 BB 00 0A 00 01 00 01 00 5E 4F 53 52 6F 6F 74 3A 43 3A 5C 55 73 65 72 73 5C 48 69 6D 31 38 5C 44 6F 63 75 6D 65 6E 74 73 5C 54 65 6E 63 65 6E 74 20 46 69 6C 65 73 5C 31 30 34 30 34 30 30 32 39 30 5C 49 6D 61 67 65 5C 43 32 43 5C 7B 47 47 42 7E 49 31 5A 4D 43 28 25 49 4D 5A 5F 47 55 51 36 35 5D 51 2E 6A 70 67 00 00 04 7D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 35 02")
.hexToBytes()
it.bot.network.socket.sendPacket(ClientRawPacket(0x01_BDu, it.bot.qqAccount, "00 00 00 01 2E 01 00 00 69 35".hexToBytes(), it.bot.network.session.sessionKey, d))
}
"上传好友图片" in it.message -> withTimeoutOrNull(3000) {
val id = QQ(bot, 1040400290u).uploadImage(ImageIO.read(File("C:\\Users\\Him18\\Desktop\\lemon.png").readBytes().inputStream()))
it.reply(id.value)
delay(1000)
it.reply(Image(id))
}
/*it.event eq "发图片群" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, Group(session.bot, 580266363)).of()
})*/
it.message eq "发图片群2" -> Group(bot, 580266363).sendMessage(Image("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg"))
it.message eq "发图片群2" -> Group(bot, 580266363u).sendMessage(Image(ImageId("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg")))
/* it.event eq "发图片" -> sendFriendMessage(it.sender, PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, it.sender).of()
})*/
it.message eq "发图片2" -> it.reply(PlainText("test") + Image("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg"))
it.message eq "发图片2" -> it.reply(PlainText("test") + Image(ImageId("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg")))
}
}
......
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