Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
d87c7f62
Commit
d87c7f62
authored
Nov 17, 2019
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ProtoBuf implementation
parent
d4bb6f15
Changes
11
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
614 additions
and
382 deletions
+614
-382
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/FriendImage.kt
...e.mirai/network/protocol/tim/packet/action/FriendImage.kt
+3
-357
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/GroupImage.kt
...oe.mirai/network/protocol/tim/packet/action/GroupImage.kt
+224
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/HttpAPIs.kt
...amoe.mirai/network/protocol/tim/packet/action/HttpAPIs.kt
+48
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ImageUploadInfo.kt
...rai/network/protocol/tim/packet/action/ImageUploadInfo.kt
+62
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Proto.kt
...core/src/commonMain/kotlin/net.mamoe.mirai/utils/Proto.kt
+140
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConvertion.kt
...monMain/kotlin/net.mamoe.mirai/utils/io/TypeConvertion.kt
+12
-5
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
.../src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
+35
-1
mirai-debug/build.gradle.kts
mirai-debug/build.gradle.kts
+27
-15
mirai-debug/src/main/kotlin/test/ProtoTest.kt
mirai-debug/src/main/kotlin/test/ProtoTest.kt
+57
-0
mirai-demos/mirai-demo-gentleman/build.gradle
mirai-demos/mirai-demo-gentleman/build.gradle
+3
-3
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
...rai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
+3
-1
No files found.
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/
Uploa
dImage.kt
→
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/
Frien
dImage.kt
View file @
d87c7f62
This diff is collapsed.
Click to expand it.
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/GroupImage.kt
0 → 100644
View file @
d87c7f62
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
,
"RUNTIME_ANNOTATION_NOT_SUPPORTED"
)
package
net.mamoe.mirai.network.protocol.tim.packet.action
import
kotlinx.coroutines.withContext
import
kotlinx.io.charsets.Charsets
import
kotlinx.io.core.*
import
kotlinx.serialization.SerialId
import
kotlinx.serialization.Serializable
import
kotlinx.serialization.protobuf.ProtoBuf
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.GroupId
import
net.mamoe.mirai.contact.GroupInternalId
import
net.mamoe.mirai.contact.withSession
import
net.mamoe.mirai.message.ImageId
import
net.mamoe.mirai.message.requireLength
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
net.mamoe.mirai.network.protocol.tim.packet.event.EventPacket
import
net.mamoe.mirai.qqAccount
import
net.mamoe.mirai.utils.ExternalImage
import
net.mamoe.mirai.utils.Http
import
net.mamoe.mirai.utils.assertUnreachable
import
net.mamoe.mirai.utils.io.*
import
kotlin.coroutines.coroutineContext
/**
* 上传群图片
* 挂起直到上传完成或失败
*
* 在 JVM 下, `SendImageUtilsJvm.kt` 内有多个捷径函数
*
* @throws OverFileSizeMaxException 如果文件过大, 服务器拒绝接收时
*/
suspend
fun
Group
.
uploadImage
(
image
:
ExternalImage
):
ImageId
=
withSession
{
val
userContext
=
coroutineContext
val
response
=
GroupImagePacket
.
RequestImageId
(
bot
.
qqAccount
,
internalId
,
image
,
sessionKey
).
sendAndExpect
<
GroupImageResponse
>()
withContext
(
userContext
)
{
when
(
response
)
{
is
ImageUploadInfo
->
response
.
uKey
?.
let
{
Http
.
postImage
(
htcmd
=
"0x6ff0071"
,
uin
=
bot
.
qqAccount
,
groupId
=
GroupId
(
id
),
imageInput
=
image
.
input
,
inputSize
=
image
.
inputSize
,
uKeyHex
=
it
.
toUHexString
(
""
)
)
}
// TODO: 2019/11/17 超过大小的情况
//is Overfile -> throw OverFileSizeMaxException()
else
->
assertUnreachable
()
}
}
return
image
.
groupImageId
}
interface
GroupImageResponse
:
EventPacket
// endregion
@Serializable
data class
ImageDownloadInfo
(
@SerialId
(
11
)
val
host
:
String
,
@SerialId
(
12
)
val
thumbnail
:
String
,
@SerialId
(
13
)
val
original
:
String
,
@SerialId
(
14
)
val
compressed
:
String
)
:
GroupImageResponse
@Serializable
class
ImageUploadInfo
(
@SerialId
(
8
)
val
uKey
:
ByteArray
?
=
null
)
:
GroupImageResponse
{
override
fun
toString
():
String
=
"ImageUploadInfo(uKey=${uKey?.toUHexString()})"
}
/**
* 获取 Image Id 和上传用的一个 uKey
*/
@AnnotatedId
(
KnownPacketId
.
GROUP_IMAGE_ID
)
@PacketVersion
(
date
=
"2019.10.26"
,
timVersion
=
"2.3.2 (21173)"
)
object
GroupImagePacket
:
SessionPacketFactory
<
GroupImageResponse
>()
{
@Suppress
(
"FunctionName"
)
fun
RequestImageId
(
bot
:
UInt
,
groupInternalId
:
GroupInternalId
,
image
:
ExternalImage
,
sessionKey
:
SessionKey
):
OutgoingPacket
=
buildSessionPacket
(
bot
,
sessionKey
,
version
=
TIMProtocol
.
version0x04
)
{
writeHex
(
"00 00 00 07 00 00"
)
writeShortLVPacket
(
lengthOffset
=
{
it
-
7
})
{
writeByte
(
0
x08
)
writeHex
(
"01 12 03 98 01 01 10 01 1A"
)
// 02 10 02 22
writeUVarIntLVPacket
(
lengthOffset
=
{
it
})
{
writeTUVarint
(
0
x08u
,
groupInternalId
.
value
)
writeTUVarint
(
0
x10u
,
bot
)
writeTV
(
0
x1800u
)
writeUByte
(
0
x22u
)
writeUByte
(
0
x10u
)
writeFully
(
image
.
md5
)
writeTUVarint
(
0
x28u
,
image
.
inputSize
.
toUInt
())
writeUVarIntLVPacket
(
tag
=
0
x32u
)
{
writeTV
(
0
x5B_00u
)
writeTV
(
0
x40_00u
)
writeTV
(
0
x33_00u
)
writeTV
(
0
x48_00u
)
writeTV
(
0
x5F_00u
)
writeTV
(
0
x58_00u
)
writeTV
(
0
x46_00u
)
writeTV
(
0
x51_00u
)
writeTV
(
0
x45_00u
)
writeTV
(
0
x51_00u
)
writeTV
(
0
x40_00u
)
writeTV
(
0
x24_00u
)
writeTV
(
0
x4F_00u
)
}
writeTV
(
0
x38_01u
)
writeTV
(
0
x48_01u
)
writeTUVarint
(
0
x50u
,
image
.
width
.
toUInt
())
writeTUVarint
(
0
x58u
,
image
.
height
.
toUInt
())
writeTV
(
0
x60_04u
)
//这个似乎会变 有时候是02, 有时候是03
writeTByteArray
(
0
x6Au
,
value0x6A
)
writeTV
(
0
x70_00u
)
writeTV
(
0
x78_03u
)
writeTV
(
0
x80_01u
)
writeUByte
(
0
u
)
}
}
}
@Suppress
(
"FunctionName"
)
fun
RequestImageLink
(
bot
:
UInt
,
sessionKey
:
SessionKey
,
imageId
:
ImageId
):
OutgoingPacket
{
imageId
.
requireLength
()
require
(
imageId
.
value
.
length
==
37
)
{
"ImageId.value.length must == 37"
}
// 00 00 00 07 00 00 00
// [4B]
// 08
// 01 12
// 03 98
// 01 02
// 08 02
//
// 1A [47]
// 08 [A2 FF 8C F0 03] UVarInt
// 10 [DD F1 92 B7 07] UVarInt
// 1A [25] 2F 38 65 32 63 32 38 62 64 2D 35 38 61 31 2D 34 66 37 30 2D 38 39 61 31 2D 65 37 31 39 66 63 33 30 37 65 65 66
// 20 02 30 04 38 20 40 FF 01 50 00 6A 05 32 36 39 33 33 78 01
// 00 00 00 07 00 00 00
// [4B]
// 08 01
// 12 03
// 98 01 02
// 08 02
//
// 1A
// [47]
// 08 [A2 FF 8C F0 03]
// 10 [A6 A7 F1 EA 02]
// 1A [25] 2F 39 61 31 66 37 31 36 32 2D 38 37 30 38 2D 34 39 30 38 2D 38 31 63 30 2D 66 34 63 64 66 33 35 63 38 64 37 65
// 20 02 30 04 38 20 40 FF 01 50 00 6A 05 32 36 39 33 33 78 01
return
buildSessionPacket
(
bot
,
sessionKey
,
version
=
TIMProtocol
.
version0x04
)
{
writeHex
(
"00 00 00 07 00 00"
)
writeUShort
(
0
x004Bu
)
writeUByte
(
0
x08u
)
writeTV
(
0
x01_12u
)
writeTV
(
0
x03_98u
)
writeTV
(
0
x01_02u
)
writeTV
(
0
x08_02u
)
writeUByte
(
0
x1Au
)
writeUByte
(
0
x47u
)
writeTUVarint
(
0
x08u
,
bot
)
writeTUVarint
(
0
x10u
,
bot
)
writeTLV
(
0
x1Au
,
imageId
.
value
.
toByteArray
(
Charsets
.
ISO_8859_1
))
writeHex
(
"20 02 30 04 38 20 40 FF 01 50 00 6A 05 32 36 39 33 33 78 01"
)
}
}
private
val
value0x6A
:
UByteArray
=
ubyteArrayOf
(
0
x05u
,
0
x32u
,
0
x36u
,
0
x36u
,
0
x35u
,
0
x36u
)
override
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
GroupImageResponse
{
discardExact
(
6
)
//00 00 00 05 00 00
discardExact
(
2
)
// 是 protobuf 的长度, 但是是错的
val
bytes
=
readBytes
()
// println(ByteReadPacket(bytes).readProtoMap())
@Serializable
data class
GroupImageResponseProto
(
@SerialId
(
3
)
val
imageUploadInfoPacket
:
ImageUploadInfo
?
=
null
,
@SerialId
(
4
)
val
imageDownloadInfo
:
ImageDownloadInfo
?
=
null
)
val
proto
=
ProtoBuf
.
load
(
GroupImageResponseProto
.
serializer
(),
bytes
)
return
when
{
proto
.
imageUploadInfoPacket
!=
null
->
proto
.
imageUploadInfoPacket
proto
.
imageDownloadInfo
!=
null
->
proto
.
imageDownloadInfo
else
->
assertUnreachable
()
}
}
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/HttpAPIs.kt
0 → 100644
View file @
d87c7f62
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet.action
import
io.ktor.client.HttpClient
import
io.ktor.client.request.post
import
io.ktor.http.HttpStatusCode
import
io.ktor.http.URLProtocol
import
io.ktor.http.userAgent
import
kotlinx.io.core.Input
import
net.mamoe.mirai.contact.GroupId
import
net.mamoe.mirai.utils.configureBody
@Suppress
(
"SpellCheckingInspection"
)
internal
suspend
inline
fun
HttpClient
.
postImage
(
htcmd
:
String
,
uin
:
UInt
,
groupId
:
GroupId
?,
imageInput
:
Input
,
inputSize
:
Long
,
uKeyHex
:
String
):
Boolean
=
try
{
post
<
HttpStatusCode
>
{
url
{
protocol
=
URLProtocol
.
HTTP
host
=
"htdata2.qq.com"
path
(
"cgi-bin/httpconn"
)
parameters
[
"htcmd"
]
=
htcmd
parameters
[
"uin"
]
=
uin
.
toLong
().
toString
()
if
(
groupId
!=
null
)
parameters
[
"groupcode"
]
=
groupId
.
value
.
toLong
().
toString
()
parameters
[
"term"
]
=
"pc"
parameters
[
"ver"
]
=
"5603"
parameters
[
"filesize"
]
=
inputSize
.
toString
()
parameters
[
"range"
]
=
0
.
toString
()
parameters
[
"ukey"
]
=
uKeyHex
userAgent
(
"QQClient"
)
}
configureBody
(
inputSize
,
imageInput
)
}
==
HttpStatusCode
.
OK
}
finally
{
imageInput
.
close
()
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ImageUploadInfo.kt
0 → 100644
View file @
d87c7f62
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
,
"unused"
,
"NO_REFLECTION_IN_CLASS_PATH"
)
package
net.mamoe.mirai.network.protocol.tim.packet.action
/**
* 图片文件过大
*/
class
OverFileSizeMaxException
:
IllegalStateException
()
/*
/**
* 似乎没有必要. 服务器的返回永远都是 01 00 00 00 02 00 00
*/
@Deprecated("Useless packet")
@AnnotatedId(KnownPacketId.SUBMIT_IMAGE_FILE_NAME)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2 (21173)")
object SubmitImageFilenamePacket : PacketFactory {
operator fun invoke(
bot: UInt,
target: UInt,
filename: String,
sessionKey: SessionKey
): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot)
writeFully(TIMProtocol.fixVer2)//?
//writeHex("04 00 00 00 01 2E 01 00 00 69 35")
encryptAndWrite(sessionKey) {
writeByte(0x01)
writeQQ(bot)
writeQQ(target)
writeZero(2)
writeUByte(0x02u)
writeRandom(1)
writeHex("00 0A 00 01 00 01")
val name = "UserDataImage:$filename"
writeShort(name.length.toShort())
writeStringUtf8(name)
writeHex("00 00")
writeRandom(2)//这个也与是哪个好友有关?
writeHex("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2E 01")//35 02? 最后这个值是与是哪个好友有关
//this.debugPrintThis("SubmitImageFilenamePacket")
}
//解密body=01 3E 03 3F A2 7C BC D3 C1 00 00 27 1A 00 0A 00 01 00 01 00 30 55 73 65 72 44 61 74 61 43 75 73 74 6F 6D 46 61 63 65 3A 31 5C 28 5A 53 41 58 40 57 4B 52 4A 5A 31 7E 33 59 4F 53 53 4C 4D 32 4B 49 2E 6A 70 67 00 00 06 E2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2F 02
//解密body=01 3E 03 3F A2 7C BC D3 C1 00 00 27 1B 00 0A 00 01 00 01 00 30 55 73 65 72 44 61 74 61 43 75 73 74 6F 6D 46 61 63 65 3A 31 5C 28 5A 53 41 58 40 57 4B 52 4A 5A 31 7E 33 59 4F 53 53 4C 4D 32 4B 49 2E 6A 70 67 00 00 06 E2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2F 02
//解密body=01 3E 03 3F A2 7C BC D3 C1 00 00 27 1C 00 0A 00 01 00 01 00 30 55 73 65 72 44 61 74 61 43 75 73 74 6F 6D 46 61 63 65 3A 31 5C 29 37 42 53 4B 48 32 44 35 54 51 28 5A 35 7D 35 24 56 5D 32 35 49 4E 2E 6A 70 67 00 00 03 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2F 02
}
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2 (21173)")
class Response {
override fun decode() = with(input) {
require(readBytes().contentEquals(expecting))
}
companion object {
private val expecting = byteArrayOf(0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00)
}
}
}*/
// regiion GroupImageResponse
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Proto.kt
0 → 100644
View file @
d87c7f62
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.utils
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.readBytes
import
kotlinx.io.core.readUInt
import
kotlinx.io.core.readULong
import
net.mamoe.mirai.utils.io.UVarInt
import
net.mamoe.mirai.utils.io.readUVarInt
import
net.mamoe.mirai.utils.io.toUHexString
// ProtoBuf utilities
/*
* Type Meaning Used For
* 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
* 1 64-bit fixed64, sfixed64, double
* 2 Length-delimi string, bytes, embedded messages, packed repeated fields
* 3 Start group Groups (deprecated)
* 4 End group Groups (deprecated)
* 5 32-bit fixed32, sfixed32, float
*
* https://www.jianshu.com/p/f888907adaeb
*/
@Suppress
(
"FunctionName"
)
fun
ProtoFieldId
(
serializedId
:
UInt
):
ProtoFieldId
=
ProtoFieldId
(
protoFieldNumber
(
serializedId
),
protoType
(
serializedId
))
data class
ProtoFieldId
(
val
fieldNumber
:
Int
,
val
type
:
ProtoType
)
{
override
fun
toString
():
String
=
"$type $fieldNumber"
}
enum
class
ProtoType
(
val
value
:
Byte
,
val
typeName
:
String
)
{
/**
* int32, int64, uint32, uint64, sint32, sint64, bool, enum
*/
VAR_INT
(
0
x00
,
"varint"
),
/**
* fixed64, sfixed64, double
*/
BIT_64
(
0
x01
,
" 64bit"
),
/**
* string, bytes, embedded messages, packed repeated fields
*/
LENGTH_DELIMI
(
0
x02
,
"delimi"
),
/**
* Groups (deprecated)
*/
START_GROUP
(
0
x03
,
"startg"
),
/**
* Groups (deprecated)
*/
END_GROUP
(
0
x04
,
" endg"
),
/**
* fixed32, sfixed32, float
*/
BIT_32
(
0
x05
,
" 32bit"
),
;
override
fun
toString
():
String
=
this
.
typeName
companion
object
{
fun
valueOf
(
value
:
Byte
):
ProtoType
=
values
().
firstOrNull
{
it
.
value
==
value
}
?:
error
(
"Unknown ProtoId $value"
)
}
}
/**
* 由 ProtoBuf 序列化后的 id 得到类型
*
* serializedId = (fieldNumber << 3) | wireType
*/
fun
protoType
(
number
:
UInt
):
ProtoType
=
ProtoType
.
valueOf
(
number
.
toInt
().
shl
(
29
).
ushr
(
29
).
toByte
())
/**
* ProtoBuf 序列化后的 id 转为序列前标记的 id
*
* serializedId = (fieldNumber << 3) | wireType
*/
fun
protoFieldNumber
(
number
:
UInt
):
Int
=
number
.
toInt
().
ushr
(
3
)
class
ProtoMap
(
map
:
MutableMap
<
ProtoFieldId
,
Any
>)
:
MutableMap
<
ProtoFieldId
,
Any
>
by
map
{
override
fun
toString
():
String
{
return
this
.
entries
.
joinToString
(
prefix
=
"ProtoMap(\n "
,
postfix
=
"\n)"
,
separator
=
"\n "
)
{
"${it.key}="
+
it
.
value
.
contentToString
().
replace
(
"\n"
,
"""\n"""
)
}
}
/*
override fun put(key: ProtoFieldId, value: Any): Any? {
println("${key}=" + value.contentToString())
return null
}*/
}
fun
Any
.
contentToString
():
String
=
when
(
this
)
{
is
UInt
->
"0x"
+
this
.
toUHexString
(
""
)
+
"($this)"
is
UByte
->
"0x"
+
this
.
toUHexString
()
+
"($this)"
is
UShort
->
"0x"
+
this
.
toUHexString
(
""
)
+
"($this)"
is
ULong
->
"0x"
+
this
.
toUHexString
(
""
)
+
"($this)"
is
Int
->
"0x"
+
this
.
toUHexString
(
""
)
+
"($this)"
is
Byte
->
"0x"
+
this
.
toUHexString
()
+
"($this)"
is
Short
->
"0x"
+
this
.
toUHexString
(
""
)
+
"($this)"
is
Long
->
"0x"
+
this
.
toUHexString
(
""
)
+
"($this)"
is
UVarInt
->
"0x"
+
this
.
toUHexString
(
""
)
+
"($this)"
is
Boolean
->
if
(
this
)
"true"
else
"false"
is
ByteArray
->
this
.
toUHexString
()
// + " (${this.encodeToString()})"
else
->
this
.
toString
()
}
fun
ByteReadPacket
.
readProtoMap
(
length
:
Long
=
this
.
remaining
):
ProtoMap
{
val
map
=
ProtoMap
(
mutableMapOf
())
val
expectingRemaining
=
this
.
remaining
-
length
while
(
this
.
remaining
!=
expectingRemaining
)
{
val
id
=
ProtoFieldId
(
readUVarInt
())
map
[
id
]
=
when
(
id
.
type
)
{
ProtoType
.
VAR_INT
->
UVarInt
(
readUVarInt
())
ProtoType
.
BIT_32
->
readUInt
()
ProtoType
.
BIT_64
->
readULong
()
ProtoType
.
LENGTH_DELIMI
->
readBytes
(
readUVarInt
().
toInt
())
ProtoType
.
START_GROUP
->
error
(
"unsupported"
)
ProtoType
.
END_GROUP
->
error
(
"unsupported"
)
}
}
return
map
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConvertion.kt
View file @
d87c7f62
...
@@ -52,7 +52,7 @@ fun Long.toUHexString(separator: String = " "): String =
...
@@ -52,7 +52,7 @@ fun Long.toUHexString(separator: String = " "): String =
*/
*/
fun
UByte
.
toByteArray
():
ByteArray
=
byteArrayOf
((
this
and
255
u
).
toByte
())
fun
UByte
.
toByteArray
():
ByteArray
=
byteArrayOf
((
this
and
255
u
).
toByte
())
fun
UByte
.
toUHexString
():
String
=
(
this
and
255
u
)
.
toByte
().
toUHexString
()
fun
UByte
.
toUHexString
():
String
=
this
.
toByte
().
toUHexString
()
/**
/**
* 255u -> 00 00 00 FF
* 255u -> 00 00 00 FF
...
@@ -73,10 +73,17 @@ fun UInt.toUHexString(separator: String = " "): String = this.toByteArray().toUH
...
@@ -73,10 +73,17 @@ fun UInt.toUHexString(separator: String = " "): String = this.toByteArray().toUH
* 转无符号十六进制表示, 并补充首位 `0`.
* 转无符号十六进制表示, 并补充首位 `0`.
* 转换结果示例: `FF`, `0E`
* 转换结果示例: `FF`, `0E`
*/
*/
fun
Byte
.
toUHexString
():
String
=
this
.
toUByte
().
toString
(
16
).
toUpperCase
().
let
{
fun
Byte
.
toUHexString
():
String
=
this
.
toUByte
().
fixToUHex
()
if
(
it
.
length
==
1
)
"0$it"
else
it
/**
}
* 转无符号十六进制表示, 并补充首位 `0`.
*/
fun
Byte
.
fixToUHex
():
String
=
this
.
toUByte
().
fixToUHex
()
/**
* 转无符号十六进制表示, 并补充首位 `0`.
*/
fun
UByte
.
fixToUHex
():
String
=
if
(
this
.
toInt
()
in
0
..
9
)
"0${this.toString(16).toUpperCase()}"
else
this
.
toString
(
16
).
toUpperCase
()
/**
/**
* 将无符号 Hex 转为 [ByteArray], 有根据 hex 的 [hashCode] 建立的缓存.
* 将无符号 Hex 转为 [ByteArray], 有根据 hex 的 [hashCode] 建立的缓存.
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
View file @
d87c7f62
...
@@ -44,6 +44,9 @@ fun Input.readVarInt(): Int {
...
@@ -44,6 +44,9 @@ fun Input.readVarInt(): Int {
return
decodeZigZag32
(
this
.
readUVarInt
())
return
decodeZigZag32
(
this
.
readUVarInt
())
}
}
inline
class
UVarInt
(
val
data
:
UInt
)
@JvmSynthetic
@JvmSynthetic
fun
Input
.
readUVarInt
():
UInt
{
fun
Input
.
readUVarInt
():
UInt
{
...
@@ -82,6 +85,37 @@ fun Output.writeUVarLong(ulong: Long) {
...
@@ -82,6 +85,37 @@ fun Output.writeUVarLong(ulong: Long) {
this
.
write0
(
ulong
)
this
.
write0
(
ulong
)
}
}
fun
UVarInt
.
toByteArray
():
ByteArray
{
val
list
=
mutableListOf
<
Byte
>()
var
value
=
this
.
data
.
toLong
()
do
{
var
temp
=
(
value
and
127
).
toByte
()
value
=
value
ushr
7
if
(
value
!=
0L
)
{
temp
=
temp
or
128
.
toByte
()
}
list
+=
temp
}
while
(
value
!=
0L
)
return
list
.
toByteArray
()
}
fun
UVarInt
.
toUHexString
(
separator
:
String
=
" "
):
String
=
buildString
{
var
value
=
data
.
toLong
()
var
isFirst
=
true
do
{
if
(!
isFirst
)
{
append
(
separator
)
}
var
temp
=
(
value
and
127
).
toByte
()
value
=
value
ushr
7
if
(
value
!=
0L
)
{
temp
=
temp
or
128
.
toByte
()
}
append
(
temp
.
toUByte
().
fixToUHex
())
isFirst
=
false
}
while
(
value
!=
0L
)
}
private
fun
Output
.
write0
(
long
:
Long
)
{
private
fun
Output
.
write0
(
long
:
Long
)
{
var
value
=
long
var
value
=
long
...
@@ -101,7 +135,7 @@ private fun read(stream: Input, maxSize: Int): Long {
...
@@ -101,7 +135,7 @@ private fun read(stream: Input, maxSize: Int): Long {
var
b
=
stream
.
readByte
().
toInt
()
var
b
=
stream
.
readByte
().
toInt
()
while
(
b
and
0
x80
==
0
x80
)
{
while
(
b
and
0
x80
==
0
x80
)
{
value
=
value
or
((
b
and
0
x7F
).
toLong
()
shl
size
++
*
7
)
value
=
value
or
((
b
and
0
x7F
).
toLong
()
shl
size
++
*
7
)
require
(
size
<
maxSize
)
{
"VarLong too big
ger
(expecting maxSize=$maxSize)"
}
require
(
size
<
maxSize
)
{
"VarLong too big(expecting maxSize=$maxSize)"
}
b
=
stream
.
readByte
().
toInt
()
b
=
stream
.
readByte
().
toInt
()
}
}
...
...
mirai-debug/build.gradle.kts
View file @
d87c7f62
...
@@ -3,6 +3,7 @@ plugins {
...
@@ -3,6 +3,7 @@ plugins {
id
(
"org.openjfx.javafxplugin"
)
version
"0.0.8"
id
(
"org.openjfx.javafxplugin"
)
version
"0.0.8"
kotlin
(
"jvm"
)
kotlin
(
"jvm"
)
java
java
id
(
"kotlinx-serialization"
)
}
}
javafx
{
javafx
{
...
@@ -15,14 +16,15 @@ application {
...
@@ -15,14 +16,15 @@ application {
mainClassName
=
"Application"
mainClassName
=
"Application"
}
}
val
kotlinVersion
=
rootProject
.
ext
[
"kotlinVersion"
].
toString
()
val
kotlinVersion
:
String
by
rootProject
.
ext
val
atomicFuVersion
=
rootProject
.
ext
[
"atomicFuVersion"
].
toString
()
val
atomicFuVersion
:
String
by
rootProject
.
ext
val
coroutinesVersion
=
rootProject
.
ext
[
"coroutinesVersion"
].
toString
()
val
coroutinesVersion
:
String
by
rootProject
.
ext
val
kotlinXIoVersion
=
rootProject
.
ext
[
"kotlinXIoVersion"
].
toString
()
val
kotlinXIoVersion
:
String
by
rootProject
.
ext
val
coroutinesIoVersion
=
rootProject
.
ext
[
"coroutinesIoVersion"
].
toString
()
val
coroutinesIoVersion
:
String
by
rootProject
.
ext
val
serializationVersion
:
String
by
rootProject
.
ext
val
klockVersion
=
rootProject
.
ext
[
"klockVersion"
].
toString
()
val
klockVersion
:
String
by
rootProject
.
ext
val
ktorVersion
=
rootProject
.
ext
[
"ktorVersion"
].
toString
()
val
ktorVersion
:
String
by
rootProject
.
ext
kotlin
{
kotlin
{
sourceSets
{
sourceSets
{
...
@@ -32,20 +34,30 @@ kotlin {
...
@@ -32,20 +34,30 @@ kotlin {
}
}
}
}
fun
DependencyHandlerScope
.
kotlinx
(
id
:
String
,
version
:
String
)
{
implementation
(
"org.jetbrains.kotlinx:$id:$version"
)
}
fun
DependencyHandlerScope
.
ktor
(
id
:
String
,
version
:
String
)
{
implementation
(
"io.ktor:$id:$version"
)
}
dependencies
{
dependencies
{
api
(
project
(
":mirai-core"
))
implementation
(
project
(
":mirai-core"
))
runtimeOnly
(
files
(
"../mirai-core/build/classes/kotlin/jvm/main"
))
// mpp targeting android limitation
runtimeOnly
(
files
(
"../mirai-core/build/classes/kotlin/jvm/main"
))
// mpp targeting android limitation
api
(
"org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
)
implementation
(
"org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
)
implementation
(
"org.pcap4j:pcap4j-distribution:1.8.2"
)
implementation
(
"org.pcap4j:pcap4j-distribution:1.8.2"
)
implementation
(
"no.tornado:tornadofx:1.7.17"
)
implementation
(
"no.tornado:tornadofx:1.7.17"
)
compile
(
group
=
"org.jetbrains.kotlinx"
,
name
=
"kotlinx-coroutines-javafx"
,
version
=
"1.3.2"
)
compile
(
group
=
"org.jetbrains.kotlinx"
,
name
=
"kotlinx-coroutines-javafx"
,
version
=
"1.3.2"
)
implementation
(
"org.jetbrains.kotlin:kotlin-stdlib"
)
kotlin
(
"kotlin-stdlib"
,
kotlinVersion
)
implementation
(
"org.jetbrains.kotlinx:atomicfu:$atomicFuVersion"
)
kotlinx
(
"atomicfu"
,
atomicFuVersion
)
implementation
(
"org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinXIoVersion"
)
kotlinx
(
"kotlinx-io-jvm"
,
kotlinXIoVersion
)
implementation
(
"org.jetbrains.kotlinx:kotlinx-io:$kotlinXIoVersion"
)
kotlinx
(
"kotlinx-io"
,
kotlinXIoVersion
)
implementation
(
"org.jetbrains.kotlinx:kotlinx-coroutines-io:$coroutinesIoVersion"
)
kotlinx
(
"kotlinx-coroutines-io"
,
coroutinesIoVersion
)
implementation
(
"org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
)
kotlinx
(
"kotlinx-coroutines-core"
,
coroutinesVersion
)
kotlinx
(
"kotlinx-serialization-runtime"
,
serializationVersion
)
}
}
\ No newline at end of file
mirai-debug/src/main/kotlin/test/ProtoTest.kt
0 → 100644
View file @
d87c7f62
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
test
import
kotlinx.serialization.ImplicitReflectionSerializer
import
kotlinx.serialization.SerialId
import
kotlinx.serialization.Serializable
import
kotlinx.serialization.protobuf.ProtoBuf
import
kotlinx.serialization.protobuf.ProtoNumberType
import
kotlinx.serialization.protobuf.ProtoType
import
kotlinx.serialization.serializer
import
net.mamoe.mirai.network.protocol.tim.packet.action.ImageUploadInfo
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.ProtoFieldId
import
net.mamoe.mirai.utils.io.hexToBytes
import
net.mamoe.mirai.utils.io.toUHexString
import
kotlin.reflect.KClass
@Serializable
data class
ProtoTest
(
@SerialId
(
1
)
val
string
:
String
,
@SerialId
(
1
)
val
int
:
Int
,
@SerialId
(
1
)
val
boolean
:
Boolean
,
@SerialId
(
1
)
val
short
:
Short
,
@SerialId
(
1
)
val
byte
:
Byte
,
@SerialId
(
1
)
@ProtoType
(
ProtoNumberType
.
FIXED
)
val
fixedByte
:
Byte
)
@UseExperimental
(
MiraiInternalAPI
::
class
)
fun
main
()
{
deserializeTest
()
return
println
(
ProtoFieldId
(
0
x12u
))
intArrayOf
(
0
x5A
,
0
X62
,
0
X6A
,
0
X72
).
forEach
{
println
(
it
.
toUShort
().
toUHexString
()
+
" => "
+
ProtoFieldId
(
it
.
toUInt
()))
}
println
(
ProtoBuf
.
dump
(
ProtoTest
.
serializer
(),
ProtoTest
(
"ss"
,
1
,
false
,
1
,
1
,
1
)).
toUHexString
())
}
fun
deserializeTest
()
{
val
bytes
=
(
"08 00 10 00 20 01 2A 1E 0A 10 BF 83 4C 2B 67 47 41 8C 9F DD 6D 8C 8E 95 53 D6 10 04 18 E4 E0 54 20 B0 09 28 9E 0D 30 FB AE A6 F4 07 38 50 48 D8 92 9E CD 0A"
)
.
hexToBytes
()
println
(
ImageUploadInfo
::
class
.
loadFrom
(
bytes
))
}
@UseExperimental
(
ImplicitReflectionSerializer
::
class
)
fun
<
T
:
Any
>
KClass
<
T
>.
loadFrom
(
protoBuf
:
ByteArray
):
T
=
ProtoBuf
.
load
(
this
.
serializer
(),
protoBuf
)
\ No newline at end of file
mirai-demos/mirai-demo-gentleman/build.gradle
View file @
d87c7f62
...
@@ -6,12 +6,12 @@ dependencies {
...
@@ -6,12 +6,12 @@ dependencies {
api
project
(
":mirai-core"
)
api
project
(
":mirai-core"
)
runtime
files
(
"../../mirai-core/build/classes/kotlin/jvm/main"
)
// mpp targeting android limitation
runtime
files
(
"../../mirai-core/build/classes/kotlin/jvm/main"
)
// mpp targeting android limitation
//runtime files("../../mirai-core/build/classes/atomicfu/jvm/main") // mpp targeting android limitation
//runtime files("../../mirai-core/build/classes/atomicfu/jvm/main") // mpp targeting android limitation
api
group:
'org.jetbrains.kotlin'
,
name:
'kotlin-stdlib-jdk8'
,
version:
kotlinVersion
implementation
group:
'org.jetbrains.kotlin'
,
name:
'kotlin-stdlib-jdk8'
,
version:
kotlinVersion
api
group:
'org.jetbrains.kotlinx'
,
name:
'kotlinx-coroutines-core'
,
version:
coroutinesVersion
implementation
group:
'org.jetbrains.kotlinx'
,
name:
'kotlinx-coroutines-core'
,
version:
coroutinesVersion
implementation
(
"org.jetbrains.kotlinx:kotlinx-io:$kotlinXIoVersion"
)
implementation
(
"org.jetbrains.kotlinx:kotlinx-io:$kotlinXIoVersion"
)
implementation
(
"org.jetbrains.kotlinx:kotlinx-coroutines-io:$coroutinesIoVersion"
)
implementation
(
"org.jetbrains.kotlinx:kotlinx-coroutines-io:$coroutinesIoVersion"
)
compile
group:
'com.alibaba'
,
name:
'fastjson'
,
version:
'1.2.62'
implementation
group:
'com.alibaba'
,
name:
'fastjson'
,
version:
'1.2.62'
implementation
'org.jsoup:jsoup:1.12.1'
implementation
'org.jsoup:jsoup:1.12.1'
}
}
...
...
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
View file @
d87c7f62
...
@@ -8,6 +8,7 @@ import kotlinx.coroutines.delay
...
@@ -8,6 +8,7 @@ import kotlinx.coroutines.delay
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
import
kotlinx.coroutines.withContext
import
net.mamoe.mirai.*
import
net.mamoe.mirai.*
import
net.mamoe.mirai.contact.MemberPermission
import
net.mamoe.mirai.event.Subscribable
import
net.mamoe.mirai.event.Subscribable
import
net.mamoe.mirai.event.subscribeAlways
import
net.mamoe.mirai.event.subscribeAlways
import
net.mamoe.mirai.event.subscribeMessages
import
net.mamoe.mirai.event.subscribeMessages
...
@@ -15,6 +16,7 @@ import net.mamoe.mirai.message.Image
...
@@ -15,6 +16,7 @@ import net.mamoe.mirai.message.Image
import
net.mamoe.mirai.message.getValue
import
net.mamoe.mirai.message.getValue
import
net.mamoe.mirai.message.sendAsImageTo
import
net.mamoe.mirai.message.sendAsImageTo
import
net.mamoe.mirai.network.protocol.tim.packet.event.FriendMessage
import
net.mamoe.mirai.network.protocol.tim.packet.event.FriendMessage
import
net.mamoe.mirai.network.protocol.tim.packet.event.GroupMessage
import
net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess
import
net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess
import
java.io.File
import
java.io.File
import
java.util.*
import
java.util.*
...
@@ -65,7 +67,7 @@ suspend fun main() {
...
@@ -65,7 +67,7 @@ suspend fun main() {
}
}
has
<
Image
>
{
has
<
Image
>
{
if
(
this
is
FriendMessage
)
{
if
(
this
is
FriendMessage
||
(
this
is
GroupMessage
&&
this
.
permission
==
MemberPermission
.
OPERATOR
)
)
{
withContext
(
IO
)
{
withContext
(
IO
)
{
val
image
:
Image
by
message
val
image
:
Image
by
message
// 等同于 val image = message[Image]
// 等同于 val image = message[Image]
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment