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
ff160c20
Commit
ff160c20
authored
Oct 27, 2019
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Graceful ImageUploading
parent
de86041d
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
200 additions
and
30 deletions
+200
-30
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt
.../src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt
+74
-15
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/UploadFriendImage.kt
...i/network/protocol/tim/packet/action/UploadFriendImage.kt
+3
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/UploadGroupImage.kt
...ai/network/protocol/tim/packet/action/UploadGroupImage.kt
+3
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
.../commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
+16
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/NumberUtils.kt
...Main/kotlin/net.mamoe.mirai/utils/internal/NumberUtils.kt
+20
-0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/MiraiEnvironmentJvm.kt
...src/jvmMain/kotlin/net/mamoe/mirai/MiraiEnvironmentJvm.kt
+1
-8
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
...mMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
+83
-5
No files found.
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt
View file @
ff160c20
...
...
@@ -4,15 +4,15 @@ package net.mamoe.mirai.message
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.contact.sendMessage
import
net.mamoe.mirai.network.protocol.tim.packet.action.FriendImageIdRequestPacket
import
net.mamoe.mirai.utils.ExternalImage
// region Message Base
/**
* 可发送的或从服务器接收的消息.
* 采用这样的消息模式是因为 QQ 的消息多元化, 一条消息中可包含 [纯文本][PlainText], [图片][Image] 等.
*
*
#### 在 Kotlin 使用 [Message]
*
**在 Kotlin 使用 [Message]**
* 这与使用 [String] 的使用非常类似.
*
* 比较 [Message] 与 [String] (使用 infix [Message.eq]):
...
...
@@ -44,6 +44,15 @@ interface Message {
*/
val
stringValue
:
String
/**
* 类型 Key.
* 除 [MessageChain] 外, 每个 [Message] 类型都拥有一个`伴生对象`(companion object) 来持有一个 Key
* 在 [MessageChain.get] 时将会使用到这个 Key 进行判断类型.
*
* @param M 指代持有它的消息类型
*/
interface
Key
<
M
>
infix
fun
eq
(
other
:
Message
):
Boolean
=
this
==
other
/**
...
...
@@ -76,20 +85,26 @@ interface Message {
infix
operator
fun
plus
(
another
:
Message
):
MessageChain
=
this
.
concat
(
another
)
infix
operator
fun
plus
(
another
:
String
):
MessageChain
=
this
.
concat
(
another
.
toMessage
())
infix
operator
fun
plus
(
another
:
Number
):
MessageChain
=
this
.
concat
(
another
.
toString
().
toMessage
())
}
/**
* 将 [this] 发送给指定联系人
*/
suspend
fun
Message
.
sendTo
(
contact
:
Contact
)
=
contact
.
sendMessage
(
this
)
// endregion
// region PlainText
// ==================================== PlainText ====================================
inline
class
PlainText
(
override
val
stringValue
:
String
)
:
Message
{
override
operator
fun
contains
(
sub
:
String
):
Boolean
=
sub
in
stringValue
override
fun
toString
():
String
=
stringValue
companion
object
Key
:
Message
.
Key
<
PlainText
>
}
// endregion
// region Image
// ==================================== Image ====================================
/**
...
...
@@ -100,6 +115,9 @@ inline class PlainText(override val stringValue: String) : Message {
*/
inline
class
Image
(
val
id
:
ImageId
)
:
Message
{
override
val
stringValue
:
String
get
()
=
"[${id.value}]"
override
fun
toString
():
String
=
stringValue
companion
object
Key
:
Message
.
Key
<
Image
>
}
/**
...
...
@@ -116,6 +134,10 @@ fun ImageId.image(): Image = Image(this)
suspend
fun
ImageId
.
sendTo
(
contact
:
Contact
)
=
contact
.
sendMessage
(
this
.
image
())
// endregion
// region At
// ==================================== At ====================================
/**
...
...
@@ -125,8 +147,14 @@ inline class At(val targetQQ: UInt) : Message {
constructor
(
target
:
QQ
)
:
this
(
target
.
id
)
override
val
stringValue
:
String
get
()
=
"[@$targetQQ]"
override
fun
toString
():
String
=
stringValue
companion
object
Key
:
Message
.
Key
<
At
>
}
// endregion
// region Face
// ==================================== Face ====================================
/**
...
...
@@ -134,10 +162,17 @@ inline class At(val targetQQ: UInt) : Message {
*/
inline
class
Face
(
val
id
:
FaceID
)
:
Message
{
override
val
stringValue
:
String
get
()
=
"[face${id.value}]"
override
fun
toString
():
String
=
stringValue
companion
object
Key
:
Message
.
Key
<
Face
>
}
// endregion Face
// region MessageChain
// ==================================== MessageChain ====================================
// region constructors
/**
* 构造无初始元素的可修改的 [MessageChain]. 初始大小将会被设定为 8
*/
...
...
@@ -178,6 +213,27 @@ fun SingleMessageChain(delegate: Message): MessageChain {
require
(
delegate
!
is
MessageChain
)
{
"delegate for SingleMessageChain should not be any instance of MessageChain"
}
return
SingleMessageChainImpl
(
delegate
)
}
// endregion
// region extensions
/**
* 获取第一个 [M] 类型的 [Message] 实例
*/
inline
fun
<
reified
M
:
Message
>
MessageChain
.
firstOrNull
():
Message
?
=
this
.
firstOrNull
{
M
::
class
.
isInstance
(
it
)
}
/**
* 获取第一个 [M] 类型的 [Message] 实例
* @throws [NoSuchElementException] 如果找不到该类型的实例
*/
inline
fun
<
reified
M
:
Message
>
MessageChain
.
first
():
Message
=
this
.
first
{
M
::
class
.
isInstance
(
it
)
}
/**
* 获取第一个 [M] 类型的 [Message] 实例
*/
inline
fun
<
reified
M
:
Message
>
MessageChain
.
any
():
Boolean
=
this
.
firstOrNull
{
M
::
class
.
isInstance
(
it
)
}
!==
null
// endregion
/**
* 消息链. 即 MutableList<Message>.
...
...
@@ -204,6 +260,20 @@ interface MessageChain : Message, MutableList<Message> {
operator
fun
plusAssign
(
plain
:
String
)
{
this
.
concat
(
plain
.
toMessage
())
}
/**
* 获取第一个类型为 [key] 的 [Message] 实例
*
* @param key 由各个类型消息的伴生对象持有. 如 [PlainText.Key]
*/
@Suppress
(
"UNCHECKED_CAST"
)
operator
fun
<
M
>
get
(
key
:
Message
.
Key
<
M
>):
M
=
when
(
key
)
{
At
->
first
<
At
>()
PlainText
->
first
<
PlainText
>()
Image
->
first
<
Image
>()
Face
->
first
<
Face
>()
else
->
error
(
"unknown key: $key"
)
}
as
M
}
/**
...
...
@@ -403,15 +473,4 @@ internal inline class SingleMessageChainImpl(
override
operator
fun
contains
(
element
:
Message
):
Boolean
=
element
===
delegate
override
val
size
:
Int
get
()
=
1
// endregion
}
/**
* 获取第一个 [M] 类型的实例
*/
inline
fun
<
reified
M
:
Message
>
MessageChain
.
firstOrNull
():
Message
?
=
this
.
firstOrNull
{
M
::
class
.
isInstance
(
it
)
}
/**
* 获取第一个 [M] 类型的实例
* @throws [NoSuchElementException] 如果找不到该类型的实例
*/
inline
fun
<
reified
M
:
Message
>
MessageChain
.
first
():
Message
=
this
.
first
{
M
::
class
.
isInstance
(
it
)
}
\ No newline at end of file
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/UploadFriendImage.kt
View file @
ff160c20
...
...
@@ -23,6 +23,9 @@ import net.mamoe.mirai.withSession
/**
* 上传图片
* 挂起直到上传完成或失败
*
* 在 JVM 下, `SendImageUtilsJvm.kt` 内有多个捷径函数
*
* @throws OverFileSizeMaxException 如果文件过大, 服务器拒绝接收时
*/
suspend
fun
QQ
.
uploadImage
(
image
:
ExternalImage
):
ImageId
=
bot
.
withSession
{
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/UploadGroupImage.kt
View file @
ff160c20
...
...
@@ -26,6 +26,9 @@ class OverFileSizeMaxException : IllegalStateException()
/**
* 上传群图片
* 挂起直到上传完成或失败
*
* 在 JVM 下, `SendImageUtilsJvm.kt` 内有多个捷径函数
*
* @throws OverFileSizeMaxException 如果文件过大, 服务器拒绝接收时
*/
suspend
fun
Group
.
uploadImage
(
image
:
ExternalImage
):
ImageId
=
withSession
{
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
View file @
ff160c20
...
...
@@ -7,7 +7,9 @@ import kotlinx.io.core.Input
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.message.Image
import
net.mamoe.mirai.message.ImageId
import
net.mamoe.mirai.message.image
import
net.mamoe.mirai.message.sendTo
import
net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage
...
...
@@ -22,7 +24,8 @@ fun ExternalImage(
/**
* 外部图片. 图片数据还没有读取到内存.
* @see ExternalImage.sendTo
* @see ExternalImage.sendTo 上传图片并以纯图片消息发送给联系人
* @See ExternalImage.upload 上传图片并得到 [Image] 消息
*/
class
ExternalImage
(
val
width
:
Int
,
...
...
@@ -58,10 +61,21 @@ suspend fun ExternalImage.sendTo(contact: Contact) = when (contact) {
is
QQ
->
contact
.
uploadImage
(
this
).
sendTo
(
contact
)
}
/**
* 上传图片并通过图片 ID 构造 [Image]
* 这个函数可能需消耗一段时间
*
* @see contact 图片上传对象. 由于好友图片与群图片不通用, 上传时必须提供目标联系人
*/
suspend
fun
ExternalImage
.
upload
(
contact
:
Contact
):
Image
=
when
(
contact
)
{
is
Group
->
contact
.
uploadImage
(
this
).
image
()
is
QQ
->
contact
.
uploadImage
(
this
).
image
()
}
/**
* 将图片发送给 [this]
*/
suspend
fun
Contact
.
send
Mess
age
(
image
:
ExternalImage
)
=
image
.
sendTo
(
this
)
suspend
fun
Contact
.
send
Im
age
(
image
:
ExternalImage
)
=
image
.
sendTo
(
this
)
private
operator
fun
ByteArray
.
get
(
range
:
IntRange
):
String
=
buildString
{
range
.
forEach
{
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/NumberUtils.kt
0 → 100644
View file @
ff160c20
package
net.mamoe.mirai.utils.internal
@PublishedApi
internal
fun
Int
.
coerceAtLeastOrFail
(
value
:
Int
):
Int
{
require
(
this
>
value
)
return
this
}
@PublishedApi
internal
fun
Long
.
coerceAtLeastOrFail
(
value
:
Long
):
Long
{
require
(
this
>
value
)
return
this
}
/**
* 表示这个参数必须为正数
*/
@Retention
(
AnnotationRetention
.
SOURCE
)
@Target
(
AnnotationTarget
.
VALUE_PARAMETER
)
internal
annotation
class
PositiveNumbers
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/MiraiEnvironmentJvm.kt
View file @
ff160c20
...
...
@@ -2,13 +2,6 @@
package
net.mamoe.mirai
import
java.io.File
actual
typealias
MiraiEnvironment
=
MiraiEnvironmentJvm
object
MiraiEnvironmentJvm
{
/**
* JVM only, 临时文件夹
*/
val
TEMP_DIR
:
File
=
createTempDir
().
apply
{
deleteOnExit
()
}
}
\ No newline at end of file
object
MiraiEnvironmentJvm
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
View file @
ff160c20
...
...
@@ -9,6 +9,7 @@ import net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.network.protocol.tim.packet.action.OverFileSizeMaxException
import
net.mamoe.mirai.utils.sendTo
import
net.mamoe.mirai.utils.toExternalImage
import
net.mamoe.mirai.utils.upload
import
java.awt.image.BufferedImage
import
java.io.File
import
java.io.InputStream
...
...
@@ -18,14 +19,14 @@ import java.net.URL
* 发送图片的一些扩展函数.
*/
// region
Type extensions
// region
IMAGE.sendAsImageTo(Contact)
/**
* 将图片发送到指定联系人. 不会
保存
临时文件
* 将图片发送到指定联系人. 不会
创建
临时文件
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
BufferedImage
.
send
AsImage
To
(
contact
:
Contact
)
=
withContext
(
Dispatchers
.
IO
)
{
toExternalImage
()
}.
sendTo
(
contact
)
suspend
fun
BufferedImage
.
sendTo
(
contact
:
Contact
)
=
withContext
(
Dispatchers
.
IO
)
{
toExternalImage
()
}.
sendTo
(
contact
)
/**
* 下载 [URL] 到临时文件并将其作为图片发送到指定联系人
...
...
@@ -57,15 +58,53 @@ suspend fun File.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) {
// endregion
// region IMAGE.Upload(Contact): Image
/**
* 将图片上传后构造 [Image]. 不会创建临时文件
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
BufferedImage
.
upload
(
contact
:
Contact
):
Image
=
withContext
(
Dispatchers
.
IO
)
{
toExternalImage
()
}.
upload
(
contact
)
/**
* 下载 [URL] 到临时文件并将其作为图片上传后构造 [Image]
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
URL
.
upload
(
contact
:
Contact
):
Image
=
withContext
(
Dispatchers
.
IO
)
{
toExternalImage
()
}.
upload
(
contact
)
// region Contact extensions
/**
* 读取 [Input] 到临时文件并将其作为图片上传后构造 [Image]
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
Input
.
upload
(
contact
:
Contact
):
Image
=
withContext
(
Dispatchers
.
IO
)
{
toExternalImage
()
}.
upload
(
contact
)
/**
* 读取 [InputStream] 到临时文件并将其作为图片上传后构造 [Image]
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
InputStream
.
upload
(
contact
:
Contact
):
Image
=
withContext
(
Dispatchers
.
IO
)
{
toExternalImage
()
}.
upload
(
contact
)
/**
* 将文件作为图片上传后构造 [Image]
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
File
.
upload
(
contact
:
Contact
):
Image
=
withContext
(
Dispatchers
.
IO
)
{
toExternalImage
()
}.
upload
(
contact
)
// endregion
// region Contact.sendImage(IMAGE)
/**
* 将图片发送到指定联系人. 不会保存临时文件
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
Contact
.
sendImage
(
bufferedImage
:
BufferedImage
)
=
bufferedImage
.
send
AsImage
To
(
this
)
suspend
fun
Contact
.
sendImage
(
bufferedImage
:
BufferedImage
)
=
bufferedImage
.
sendTo
(
this
)
/**
* 下载 [URL] 到临时文件并将其作为图片发送到指定联系人
...
...
@@ -95,4 +134,43 @@ suspend fun Contact.sendImage(imageStream: InputStream) = imageStream.sendAsImag
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
Contact
.
sendImage
(
file
:
File
)
=
file
.
sendAsImageTo
(
this
)
// endregion
// region Contact.uploadImage(IMAGE)
/**
* 将图片发送到指定联系人. 不会保存临时文件
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
Contact
.
uploadImage
(
bufferedImage
:
BufferedImage
):
Image
=
bufferedImage
.
upload
(
this
)
/**
* 下载 [URL] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
Contact
.
uploadImage
(
imageUrl
:
URL
):
Image
=
imageUrl
.
upload
(
this
)
/**
* 读取 [Input] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
Contact
.
uploadImage
(
imageInput
:
Input
):
Image
=
imageInput
.
upload
(
this
)
/**
* 读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
Contact
.
uploadImage
(
imageStream
:
InputStream
):
Image
=
imageStream
.
upload
(
this
)
/**
* 将文件作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws
(
OverFileSizeMaxException
::
class
)
suspend
fun
Contact
.
uploadImage
(
file
:
File
):
Image
=
file
.
upload
(
this
)
// endregion
\ No newline at end of file
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